/* sysstat - a program to monitor system statistics (very Linuxish) * Invoke as: sysstat[-color] [-s[howusers]] [delay] * delay is the number of seconds to wait between updates; defaults to * DEFAULT_DELAY. Note that a very small number (zero) may cause much CPU * usage! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include _syscall1(int, sysinfo, struct sysinfo *, info); #define DEFAULT_DELAY 5 static const char months[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* This is how we do delays: At the beginning of each loop, we * set an alarm() for the delay length. When the alarm expires, * got_alarm will be set nonzero by alarm_handler(). If we don't * get an alarm by the end of the loop, we wait in read() for * either a character on the input stream or the alarm (whose * handler will cause a SIGINT and cause read() to return -1). */ int got_alarm; /* Routine called on SIGALRM. */ void alarm_handler(int sig_unused) { got_alarm = 1; kill(0, SIGINT); } static const char boldon[] = "\033[1m", boldoff[] = "\033[m"; static const char red[] = "\033[31;1m", white[] = "\033[39;1m"; int main(int ac, char **av) { int delay = DEFAULT_DELAY, left, c, hour, nusers, t1, t2, t3, t4, i; char ampm, s[256], cmd[256]; FILE *f, *g; struct utmp ut; struct sysinfo si; time_t t; struct tm tm; long memtot, memused, memfree, memshr, membuf, swaptot, swapused, swapfree; char disks[64][256]; long disktot[64], diskused[64], ino_tot[64], ino_used[64]; int ndisks; int showusers = 0; char ch; int use_color = (strcmp(av[0]+strlen(av[0])-6, "-color") == 0); struct mntent *me; if (use_color) fputs(white, stdout); if (ac > 1 && strncmp(av[1], "-showusers", strlen(av[1]))) showusers = 1; if (ac > 1 && atoi(av[1])) delay = atoi(av[1]); /* Don't let SIGINT terminate us - just use it to cancel a * getchar(). */ signal(SIGINT, SIG_IGN); /* Clear screen in preparation for printing stuff. Note that * everything has a fixed width, so we can just print over the last * set of output without having to clear again. */ printf("\033[2J"); /* Main loop */ for (;;) { /* Start the timer ticking. Remember that we need to signal() each * time through the loop because getting a signal resets the * handler to the default (terminating the process). */ got_alarm = 0; signal(SIGALRM, alarm_handler); alarm(delay); /* Get the current time. */ time(&t); tm = *localtime(&t); /* Get some system info, and calculate load fractions. */ sysinfo(&si); si.loads[0] = (si.loads[0] & 0xFFFF0000) | (((si.loads[0] & 0xFFFF) * 100 + 0x8000) >> 16); if ((si.loads[0] & 0xFFFF) >= 100) si.loads[0] += (0x10000-100); si.loads[1] = (si.loads[1] & 0xFFFF0000) | (((si.loads[1] & 0xFFFF) * 100 + 0x8000) >> 16); if ((si.loads[1] & 0xFFFF) >= 100) si.loads[1] += (0x10000-100); si.loads[2] = (si.loads[2] & 0xFFFF0000) | (((si.loads[2] & 0xFFFF) * 100 + 0x8000) >> 16); if ((si.loads[2] & 0xFFFF) >= 100) si.loads[2] += (0x10000-100); /* Get memory info. */ f = fopen("/proc/meminfo", "r"); if (f) { while ((c = fgetc(f)) != EOF && c != '\n') ; fscanf(f, "%*s %d %d %d %d %d ", &memtot, &memused, &memfree, &memshr, &membuf); fscanf(f, "%*s %d %d %d", &swaptot, &swapused, &swapfree); fclose(f); memtot /= 1024; memused /= 1024; memfree /= 1024; memshr /= 1024; membuf /= 1024; swaptot /= 1024; swapused /= 1024; swapfree /= 1024; } else { memtot = -1; /* flag: don't print memory info */ } /* Count users. (If -showusers, we'll print them out later.) */ f = fopen("/etc/utmp", "r"); if (f) { nusers = 0; while (fread(&ut, sizeof(ut), 1, f) == 1) { if (ut.ut_type == USER_PROCESS && *ut.ut_user) ++nusers; } fclose(f); } else { perror("/etc/utmp"); return 1; } /* Get disk space info. */ f = setmntent("/etc/mtab", "r"); if (f) { ndisks = 0; while (me = getmntent(f)) { struct statfs fs; if (strcmp(me->mnt_type, "nfs") == 0 || strcmp(me->mnt_type, "proc") == 0) continue; strcpy(disks[ndisks], me->mnt_dir); if (statfs(me->mnt_dir, &fs) < 0) { disktot[ndisks] = diskused[ndisks] = 0; } else { disktot[ndisks] = fs.f_blocks - (fs.f_bfree - fs.f_bavail); diskused[ndisks] = disktot[ndisks] - fs.f_bavail; ino_tot[ndisks] = fs.f_files; ino_used[ndisks] = ino_tot[ndisks] - fs.f_ffree; } ++ndisks; } endmntent(f); } else { perror("/etc/mtab"); return 1; } /* Go to top of screen and print stuff out. */ printf("\033[H"); if (tm.tm_hour > 11) ampm = 'p'; else ampm = 'a'; if ((hour = tm.tm_hour % 12) == 0) hour = 12; printf("%2d:%02d%cm up ", hour, tm.tm_min, ampm); if (si.uptime > 86400) printf("%d days, ", si.uptime / 86400); printf( "%2d:%02d, %d user%s load: %d.%02d, %d.%02d, %d.%02d, %d procs\033[K\r\n", (si.uptime/3600) % 24, (si.uptime/60) % 60, nusers, nusers==1 ? ", " : "s,", si.loads[0] >> 16, si.loads[0] & 0xFFFF, si.loads[1] >> 16, si.loads[1] & 0xFFFF, si.loads[2] >> 16, si.loads[2] & 0xFFFF, si.procs); if (memtot > 0) { printf("Memory use: %6d/%d real + %6d buffers %6d/%d swap\r\n", memused-membuf, memtot, membuf, swapused, swaptot); } printf("Disks:\r\n"); for (i = 0; i < ndisks; ++i) { const char *hilite = use_color ? red : boldon; const char *normal = use_color ? white : boldoff; if (disktot[i]) { int pct = (diskused[i] + disktot[i]/200) / (disktot[i]/100); int ino_pct = (ino_used[i] + ino_tot[i]/200) / (ino_tot[i]/100); printf(" %s%-16s%s %s%3d %7d/%7d%s %s%6d/%6d %3d%s\r\n", pct>=90 || ino_pct>=75 ? hilite : "", disks[i], pct>=90 || ino_pct>=75 ? normal : "", pct>=90 ? hilite : "", pct, diskused[i], disktot[i], pct>=90 ? normal : "", ino_pct>=75 ? hilite : "", ino_used[i], ino_tot[i], ino_pct, ino_pct>=75 ? normal : ""); } else printf(" %-16s ------------------\r\n", disks[i]); } /* Print out all users (only if -showusers). */ if (showusers) { struct stat st; f = fopen("/etc/utmp", "r"); if (f) { printf( "\r\nUser tty from login@ idle JCPU PCPU what\r\n"); while (fread(&ut, sizeof(ut), 1, f) == 1) { /* Grab all and only the current logins. */ if (ut.ut_type == USER_PROCESS && *ut.ut_user) { /* Print username, tty, host, and login time. */ tm = *localtime(&ut.ut_time); printf("%-8s %-8s %-16s", ut.ut_user, ut.ut_line, ut.ut_host); if (time(NULL) - ut.ut_time >= 24*60*60) { printf("%2d%s%02d", tm.tm_mday, months[tm.tm_mon], tm.tm_year % 100); } else { if (tm.tm_hour > 11) ampm = 'p'; else ampm = 'a'; if ((hour = tm.tm_hour % 12) == 0) hour = 12; printf("%2d:%02d%cm", hour, tm.tm_min, ampm); } /* Get process info. */ sprintf(s, "/proc/%d/stat", ut.ut_pid); g = fopen(s, "r"); if (g) { fgets(s, sizeof(s), g); fclose(g); sscanf(s, "%*d %s %*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d %d %d", cmd, &t1, &t2, &t3, &t4); t2 = (t1+t2) / HZ; t3 = (t3+t4) / HZ; } else { t2 = t3 = 0; } /* Get tty idle time. (Not the best way...) */ sprintf(s, "/dev/%s", ut.ut_line); if (stat(s, &st) < 0) t1 = 0; else t1 = (time(NULL) - st.st_mtime) / 60; /* Get current command line. */ sprintf(s, "/proc/%d/cmdline", ut.ut_pid); g = fopen(s, "r"); if (g) { i = fread(s, 1, sizeof(s)-1, g); fclose(g); } else { i = 0; } if (i) { s[i] = 0; if (strchr(s, 0)-s < i) *strchr(s, 0) = ' '; strcpy(cmd, s); } /* Write all the time stuff out. */ if (t1 == 0) printf(" "); else if (t1 < 60) printf(" %2d", t1); else if (t1 < 24*60) printf(" %2d:%02d", t1/60, t1%60); else printf(" %4dd", t1/(24*60)); if (t2 == 0) printf(" "); else if (t2 < 60) printf(" %2d", t2); else printf(" %3d:%02d", t2/60, t2%60); if (t3 == 0) printf(" "); else if (t3 < 60) printf(" %2d", t3); else printf(" %3d:%02d", t3/60, t3%60); printf(" %s\r\n", cmd); } /* if (*ut.user) */ } /* while (fread() == 1) */ fclose(f); } else { /* !fopen(/etc/utmp) */ perror("/etc/utmp"); return 1; } /* fopen(/etc/utmp) */ } /* if (showusers) */ printf("\033[J"); fflush(stdout); /* Look for stuff on input stream if we have any time left. */ while (!got_alarm) { read(0, &ch, 1); if (ch == '\n') { break; } else if (ch == 'q') { #if 0 /* Why doesn't this work? */ fcntl(0, F_SETFD, O_NONBLOCK); while (read(0, &ch, 1) == 1) ; #endif return 0; } } } /* End of main loop */ }