/* * whodo - who is doing what * * also source for "w" and "uptime" commands * * Gunnar Ritter, Freiburg i. Br., Germany, December 2000. */ /* * Copyright (c) 2003 Gunnar Ritter * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute * it freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. */ #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4 #define USED __attribute__ ((used)) #elif defined __GNUC__ #define USED __attribute__ ((unused)) #else #define USED #endif static const char sccsid[] USED = "@(#)whodo.sl 1.42 (gritter) 1/12/07"; #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined (__FreeBSD__) || defined (__DragonFly__) #include #endif #if defined (__NetBSD__) || defined (__OpenBSD__) || defined (__APPLE__) #if defined (__APPLE__) #include #include #else /* !__APPLE__ */ #include #endif /* !__APPLE__ */ #include #include #endif /* __NetBSD__, __NetBSD__, __APPLE__ */ #ifdef __hpux #include #include #endif #ifdef _AIX #include #endif #if !defined (__linux__) && !defined (__FreeBSD__) && !defined (__hpux) && \ !defined (_AIX) && !defined (__NetBSD__) && !defined (__OpenBSD__) && \ !defined (__DragonFly__) && !defined (__APPLE__) #ifdef sun #include #define _STRUCTURED_PROC 1 #endif #include #endif /* !__linux__, !__FreeBSD__, !__hpux, !_AIX, !__NetBSD__, !__OpenBSD__, !__DragonFly__, !__APPLE__ */ #ifndef PRNODEV #define PRNODEV 0 #endif #define next(wc, s, n) (mb_cur_max > 1 && *(s) & 0200 ? \ ((n) = mbtowc(&(wc), (s), mb_cur_max), \ (n) = ((n) > 0 ? (n) : (n) < 0 ? (wc=WEOF, 1) : 1)) :\ ((wc) = *(s) & 0377, (n) = 1)) enum { WHODO, W, UPTIME }; enum valtype { VT_CHAR, VT_INT, VT_UINT, VT_LONG, VT_ULONG }; union value { char v_char; int v_int; unsigned int v_uint; long v_long; unsigned long v_ulong; }; static int cmd = WHODO; /* command personality */ static int errcnt; /* count of errors */ static int hflag; /* omit header */ static int lflag; /* w-like display format */ static int sflag; /* short form of output */ static int uflag; /* "uptime" format */ #undef hz static int hz; /* clock ticks per second */ static char *user; /* look for one user only */ #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \ defined (__hpux) || defined (__NetBSD__) || defined (__OpenBSD__) || \ defined (__DragonFly__) || defined (__APPLE__) static char loadavg[64]; /* load average */ #endif /* __linux__ || __sun || __FreeBSD__ || __hpux || __NetBSD__ || __OpenBSD__ || __DragonFly__ || __APPLE__ */ static char *progname; /* argv0 to main */ static const char *unknown = " unknown"; /* unknown */ static time_t now; /* time at program start */ static int mb_cur_max; /* MB_CUR_MAX acceleration */ #undef UGLY_XDM_HACK /* * Structure to represent a single process. */ struct pslot { struct pslot *p_next; /* next slot */ long p_time; /* cpu time of process */ long p_ctime; /* cpu time of children */ char p_name[16]; /* name of the executable file */ pid_t p_pid; /* process id */ dev_t p_termid; /* device id of the controlling tty */ char p_cmdline[30]; /* command line */ }; /* * Structure to represent a user login. */ struct tslot { struct pslot *t_pslot; /* start of process table */ struct tslot *t_next; /* next slot */ time_t t_time; /* login timestamp */ char t_line[sizeof (((struct utmpx *)0)->ut_line)]; /*tty line name*/ char t_user[sizeof (((struct utmpx *)0)->ut_user)]; /* user name */ dev_t t_termid; /* device id of the tty */ }; /* * perror()-alike. */ static void pnerror(int eno, const char *string) { fprintf(stderr, "%s: %s: %s\n", progname, string, strerror(eno)); errcnt |= 1; } /* * Memory allocation with check. */ static void * srealloc(void *vp, size_t sz) { void *p; if ((p = realloc(vp, sz)) == NULL) { write(2, "no memory\n", 10); exit(077); } return p; } static void * smalloc(size_t sz) { return srealloc(NULL, sz); } static union value * getval(char **listp, enum valtype type, int separator, int sep2) { char *buf; static union value v; const char *cp, *op; char *cq, *x; if (**listp == '\0') return NULL; op = *listp; while (**listp != '\0') { if ((separator == ' ' ? isspace(**listp) : **listp == separator) || **listp == sep2) break; (*listp)++; } buf = alloca(*listp - op + 1); for (cp = op, cq = buf; cp < *listp; cp++, cq++) *cq = *cp; *cq = '\0'; if (**listp) { while ((separator == ' ' ? isspace(**listp) : **listp == separator) || **listp == sep2) (*listp)++; } switch (type) { case VT_CHAR: if (buf[1] != '\0') return NULL; v.v_char = buf[0]; break; case VT_INT: v.v_int = strtol(buf, &x, 10); if (*x != '\0') return NULL; break; case VT_UINT: v.v_uint = strtoul(buf, &x, 10); if (*x != '\0') return NULL; break; case VT_LONG: v.v_long = strtol(buf, &x, 10); if (*x != '\0') return NULL; break; case VT_ULONG: v.v_ulong = strtoul(buf, &x, 10); if (*x != '\0') return NULL; break; } return &v; } /* * Print time in am/pm format. */ static char * time12(time_t t) { static char buf[8]; struct tm *tp; int hour12; tp = localtime(&t); if (tp->tm_hour == 0) hour12 = 0; else if (tp->tm_hour < 13) hour12 = tp->tm_hour; else hour12 = tp->tm_hour - 12; snprintf(buf, sizeof buf, "%2u:%02u%s", hour12, tp->tm_min, tp->tm_hour < 12 ? "am" : "pm"); return buf; } /* * Print time in 24 hour format. */ static char * time24(time_t t) { static char buf[8]; struct tm *tp; tp = localtime(&t); snprintf(buf, sizeof buf, "%u:%02u", tp->tm_hour, tp->tm_min); return buf; } /* * Return the system's uptime. */ static const char * uptime(void) { static char buf[80]; long upsec = -1; unsigned upday = 0, uphr = 0, upmin = 0; char *cp; #if defined (__linux__) FILE *fp; union value *v; const char upfile[] = "/proc/uptime"; if ((fp = fopen(upfile, "r")) == NULL) { pnerror(errno, upfile); return unknown; } errno = 0; if (fread(buf, 1, sizeof buf, fp) > 0) { cp = buf; if ((v = getval(&cp, VT_ULONG, '.', 0)) != NULL) upsec = v->v_ulong; } if (upsec == -1) { if (errno) pnerror(errno, upfile); else { fprintf(stderr, "%s: invalid uptime file\n", upfile); errcnt |= 1; } fclose(fp); return unknown; } fclose(fp); #elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \ || defined (__DragonFly__) || defined (__APPLE__) int name[2] = { CTL_KERN, KERN_BOOTTIME }; struct timeval old; size_t oldlen = sizeof old; if (sysctl(name, 2, &old, &oldlen, NULL, 0) < 0) return unknown; upsec = now - old.tv_sec; #elif defined (__hpux) struct pst_static pst; pstat_getstatic(&pst, sizeof pst, 1, 0); upsec = now - pst.boot_time; #elif defined (_AIX) struct procentry64 pi; pid_t idx = 0; getprocs64(&pi, sizeof pi, NULL, 0, &idx, 1); upsec = now - pi.pi_start; #else /* !__linux__, !__FreeBSD__, !__hpux, !_AIX, !__NetBSD__, !__OpenBSD__, !__DragonFly__, !__APPLE__ */ FILE *fp; struct psinfo pi; if ((fp = fopen("/proc/0/psinfo", "r")) == NULL) return unknown; if (fread(&pi, 1, sizeof pi, fp) != sizeof pi) { fclose(fp); return unknown; } fclose(fp); upsec = now - pi.pr_start.tv_sec; #endif /* !__linux__, !__FreeBSD__, !__hpux, !_AIX, !__NetBSD__, !__OpenBSD__, !__DragonFly__, !__APPLE__ */ if (upsec > 59) { upmin = upsec / 60; if (upmin > 59) { uphr = upmin / 60; upmin %= 60; if (uphr > 23) { upday = uphr / 24; uphr %= 24; } } } if (cmd == WHODO) snprintf(buf, sizeof buf, " %u day(s), %u hr(s), %u min(s)", upday, uphr, upmin); else { cp = buf; if (upday) { sprintf(cp, " %u day(s),", upday); cp += strlen(cp); } if (uphr && upmin) sprintf(cp, " %2u:%02u,", uphr, upmin); else { if (uphr) { sprintf(cp, " %u hr(s),", uphr); cp += strlen(cp); } if (upmin) sprintf(cp, " %u min(s),", upmin); } } return buf; } /* * Return the number of users currently logged on. */ static unsigned userno(struct tslot *t) { unsigned uno; for (uno = 0; t; t = t->t_next, uno++); return uno; } /* * Print the heading. */ static void printhead(struct tslot *t0) { time_t t; unsigned users; struct utsname un; time(&t); if (lflag) { users = userno(t0); printf(" %s up%s ", time12(t), uptime()); if (cmd != WHODO) { printf("%u %s", users, users > 1 ? "users" : "user"); #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \ defined (__hpux) || defined (__NetBSD__) || defined (__OpenBSD__) || \ defined (__DragonFly__) || defined (__APPLE__) printf(", load average: %s", loadavg); #endif /* __linux__ || __sun || __FreeBSD__ || __hpux || __NetBSD__ || __OpenBSD__ || __DragonFly__ || __APPLE__ */ printf("\n"); } else printf("%u user(s)\n", users); if (!uflag) { if (sflag) printf("User tty idle what\n"); else printf( "User tty login@ idle JCPU PCPU what\n"); } } else { if (uname(&un) < 0) strcpy(un.nodename, unknown); printf("%s%s\n", ctime(&t), un.nodename); } } /* * Convert a login stamp to text. */ static char * logtime(time_t t, int hform) { static char b[64]; struct tm tl, tn; size_t sz; char *form; memcpy(&tl, localtime(&t), sizeof tl); memcpy(&tn, localtime(&now), sizeof tn); t = mktime(&tl); if (hform == 12) { snprintf(b, sizeof b, "%s", time12(t)); } else { if (tn.tm_year != tl.tm_year) form = "%a %b %d %Y "; else if (tn.tm_mday != tl.tm_mday) form = "%a %b %d "; else form = NULL; if (form) sz = strftime(b, 63, form, &tl); else { *b = '\0'; sz = 0; } snprintf(b + sz, sizeof b - sz, "%s", time24(t)); } return b; } /* * Convert a time value given in jiffies (on Linux) to text. */ static char * jifftime(long jiff, char *buf, size_t buflen, int colon, int width) { static char _b[63]; char *b = buf ? buf : _b; long t; #ifdef __linux__ t = jiff / hz; #else t = jiff; #endif if (t >= 60 || colon) snprintf(b, 63, "%*lu:%02lu", width - 3, t / 60, t % 60); else if (t > 0) snprintf(b, 63, "%*lu", width, t); else snprintf(b, 64, "%*s", width, ""); return b; } /* * Print a tty's idle time. */ static char * idletime(char *line) { struct stat st; char fn[_POSIX_PATH_MAX]; static char buf[8]; time_t t; snprintf(fn, sizeof fn, "/dev/%s", line); if (stat(fn, &st) < 0) return ""; time(&t); t -= st.st_atime; t /= 60; if (t >= 60) snprintf(buf, sizeof buf, "%2lu:%02lu", (long)t / 60, (long)t % 60); else if (t > 0) snprintf(buf, sizeof buf, " %4lu", (long)t); else strcpy(buf, " "); return buf; } /* * Print a process table's accumulated time (+children). */ static char * jcpu(struct pslot *p0, char *buf, size_t buflen) { struct pslot *p; long acc = 0; for (p = p0; p; p = p->p_next) acc += p->p_time + p->p_ctime; return jifftime(acc, buf, buflen, 0, 6); } /* * Print a process table's accumulated time. */ static char * pcpu(struct pslot *p0, char *buf, size_t buflen) { struct pslot *p; long acc = 0; for (p = p0; p; p = p->p_next) acc += p->p_time; return jifftime(acc, buf, buflen, 0, 6); } /* * Output the user/process table. */ static void printout(struct tslot *t0) { struct tslot *t; struct pslot *p; char jbuf[8], pbuf[8]; if (hflag == 0) printhead(t0); if (uflag) return; if (lflag) { for (t = t0; t != NULL; t = t->t_next) { #ifndef UGLY_XDM_HACK if (t->t_termid == PRNODEV) continue; #endif if (t->t_pslot == NULL) continue; for (p = t->t_pslot; p->p_next != NULL; p = p->p_next); if (sflag) printf("%-8s %-12s %s %s\n", t->t_user, t->t_line, idletime(t->t_line), p->p_name); else printf("%-8s %-12s %s %s %s %s %s\n", t->t_user, t->t_line, logtime(t->t_time, 12), idletime(t->t_line), jcpu(t->t_pslot, jbuf, sizeof jbuf), pcpu(t->t_pslot, pbuf, sizeof pbuf), p->p_cmdline); } } else { for (t = t0; t != NULL; t = t->t_next) { #ifndef UGLY_XDM_HACK if (t->t_termid == PRNODEV) continue; #endif printf("\n%-10s %-9s %s\n", t->t_line, t->t_user, logtime(t->t_time, 24)); if (t->t_pslot == NULL) continue; for (p = t->t_pslot; p != NULL; p = p->p_next) printf(" %-10s %-6lu %s %s\n", p->p_termid != PRNODEV ? t->t_line : "fd/0", (long)p->p_pid, jifftime(p->p_time, NULL, 0, 1, 6), p->p_name); } } } /* * Insert a process slot into the terminal line table. */ static void queueproc(struct tslot *t0, struct pslot *ps) { struct tslot *t; struct pslot *p; for (t = t0; t != NULL; t = t->t_next) { #ifdef UGLY_XDM_HACK if (t->t_termid == PRNODEV && ps->p_termid == PRNODEV) { /* * Hack for XFree86 xdm: The process uses -$DISPLAY on * its command line. */ if (strcmp(ps->p_name, "xdm") == 0) { size_t sz = strlen(t->t_line); if (ps->p_cmdline[0] == '-' && strncmp(&ps->p_cmdline[1], t->t_line, sz) == 0 && (ps->p_cmdline[sz+1] == ' ' || ps->p_cmdline[sz+1] == '\0')) { strcpy(ps->p_cmdline, t->t_line); strcpy(t->t_line, "X,"); strcat(t->t_line, ps->p_cmdline); ps->p_name[0] = '\0'; strcpy(ps->p_cmdline, "/usr/X/bin/xdm"); } else continue; } else continue; } else #endif /* UGLY_XDM_HACK */ if (t->t_termid != ps->p_termid) continue; if (t->t_pslot != NULL) { for (p = t->t_pslot; p->p_next != NULL; p = p->p_next); p->p_next = ps; } else t->t_pslot = ps; break; } } /* * Read an entry from /proc into a process slot. */ #if !defined (__hpux) && !defined (_AIX) && !defined (__NetBSD__) && \ !defined (__OpenBSD__) && !defined (__APPLE__) #if defined (__linux__) || defined (__FreeBSD__) || defined (__DragonFly__) #define GETVAL(a) if ((v = getval(&cp, (a), ' ', 0)) == NULL) \ goto error #define GETVAL_COMMA(a) if ((v = getval(&cp, (a), ' ', ',')) == NULL) \ goto error #endif /* __linux__ || __FreeBSD__ || __DragonFly__ */ #if defined (__linux__) static struct pslot * readproc(char *pdir) { static char *buf; static size_t buflen; char fn[_POSIX_PATH_MAX]; FILE *fp; union value *v; struct pslot p, *pp; char *cp, *cq, *ce; int c; size_t sz, sc; memset(&p, 0, sizeof p); snprintf(fn, sizeof fn, "%s/stat", pdir); if ((fp = fopen(fn, "r")) == NULL) { pnerror(errno, fn); return NULL; } for (cp = buf; ;) { const unsigned chunk = 32; if (buflen < (sz = cp - buf + chunk)) { sc = cp - buf; buf = srealloc(buf, buflen = sz); cp = &buf[sc]; } if ((sz = fread(cp, 1, chunk, fp)) < chunk) { ce = &cp[sz - 1]; break; } cp += chunk; } fclose(fp); if (*ce != '\n') goto error; *ce-- = '\0'; cp = buf; GETVAL(VT_INT); p.p_pid = v->v_int; if (*cp++ != '(') goto error; for (cq = ce; cq >= cp && *cq != ')'; cq--); if (cq < cp) goto error; *cq = '\0'; strncpy(p.p_name, cp, sizeof p.p_name); p.p_name[sizeof p.p_name - 1] = '\0'; cp = &cq[1]; while (isspace(*cp)) cp++; GETVAL(VT_CHAR); if (v->v_char == 'Z') return NULL; GETVAL(VT_INT); /* ppid unused */ GETVAL(VT_INT); /* pgrp unused */ GETVAL(VT_INT); /* session unused */ GETVAL(VT_INT); p.p_termid = v->v_int; if (p.p_termid == PRNODEV #ifdef UGLY_XDM_HACK && strcmp(p.p_name, "(xdm)") #endif ) return NULL; GETVAL(VT_INT); /* tty_pgrp unused */ GETVAL(VT_ULONG); /* flags unused */ GETVAL(VT_ULONG); /* min_flt unused */ GETVAL(VT_ULONG); /* cmin_flt unused */ GETVAL(VT_ULONG); /* maj_flt unused */ GETVAL(VT_ULONG); /* cmaj_flt unused */ GETVAL(VT_ULONG); p.p_time = v->v_ulong; GETVAL(VT_ULONG); p.p_time += v->v_ulong; GETVAL(VT_ULONG); p.p_ctime = v->v_ulong; GETVAL(VT_ULONG); p.p_ctime += v->v_ulong; snprintf(fn, sizeof fn, "%s/cmdline", pdir); if ((fp = fopen(fn, "r")) != NULL) { int hadzero = 0; cp = p.p_cmdline; ce = cp + sizeof p.p_cmdline - 1; while (cp < ce && (c = getc(fp)) != EOF) { if (c != '\0') { if (hadzero) { *cp++ = ' '; if (cp == ce) break; hadzero = 0; } *cp++ = c; } else hadzero = 1; } *cp = '\0'; fclose(fp); } if (*p.p_cmdline == '\0') snprintf(p.p_cmdline, sizeof p.p_cmdline, "[ %s ]", p.p_name); pp = smalloc(sizeof *pp); memcpy(pp, &p, sizeof *pp); return pp; error: fprintf(stderr, "%s: invalid entry\n", pdir); errcnt |= 1; return NULL; } #elif defined (__FreeBSD__) || defined (__DragonFly__) enum okay { OKAY, STOP }; static enum okay getproc_status(char *pdir, struct pslot *p) { static char *buf; static size_t buflen; char fn[_POSIX_PATH_MAX]; union value *v; FILE *fp; char *cp, *ce, *cq; size_t sz, sc; int mj, mi; snprintf(fn, sizeof fn, "%s/status", pdir); if ((fp = fopen(fn, "r")) == NULL) return STOP; for (cp = buf; ;) { const unsigned chunk = 32; if (buflen < (sz = cp - buf + chunk)) { sc = cp - buf; buf = srealloc(buf, buflen = sz); cp = &buf[sc]; } if ((sz = fread(cp, 1, chunk, fp)) < chunk) { ce = &cp[sz - 1]; break; } cp += chunk; } fclose(fp); if (*ce != '\n') return STOP; *ce-- = '\0'; cp = buf; while (*cp != ' ') { if (cp - buf < sizeof p->p_name - 2) p->p_name[cp-buf] = *cp; cp++; } if (cp - buf < sizeof p->p_name - 1) p->p_name[cp-buf] = '\0'; else p->p_name[sizeof p->p_name - 1] = '\0'; while (*cp == ' ') cp++; GETVAL(VT_INT); p->p_pid = v->v_int; GETVAL(VT_INT); /* ppid unused */ GETVAL(VT_INT); /* pgid unused */ GETVAL(VT_INT); /* sid unused */ if (isdigit(*cp)) { GETVAL_COMMA(VT_INT); mj = v->v_int; GETVAL(VT_INT); mi = v->v_int; if (mj != -1 || mi != -1) p->p_termid = makedev(mj, mi); } else { struct stat st; char *dev; cq = cp; while (*cp != ' ') cp++; *cp = '\0'; dev = smalloc(cp - cq + 8); strcpy(dev, "/dev/"); strcpy(&dev[5], cq); if (stat(dev, &st) < 0) p->p_termid = PRNODEV; else p->p_termid = st.st_rdev; free(dev); *cp = ' '; while (*cp == ' ') cp++; } while (*cp != ' ') cp++; while (*cp == ' ') cp++; /* skip flags */ GETVAL_COMMA(VT_LONG); /* start unused */ GETVAL(VT_LONG); /* skip microseconds */ GETVAL_COMMA(VT_LONG); p->p_time = v->v_long; GETVAL(VT_LONG); /* skip microseconds */ GETVAL_COMMA(VT_LONG); p->p_time += v->v_long; return OKAY; error: fprintf(stderr, "%s: invalid entry\n", pdir); errcnt |= 1; return STOP; } static enum okay getproc_cmdline(char *pdir, struct pslot *p) { char fn[_POSIX_PATH_MAX]; FILE *fp; char *cp, *ce; int hadzero = 0, c; snprintf(fn, sizeof fn, "%s/cmdline", pdir); if ((fp = fopen(fn, "r")) != NULL) { cp = p->p_cmdline; ce = cp + sizeof p->p_cmdline - 1; while (cp < ce && (c = getc(fp)) != EOF) { if (c != '\0') { if (hadzero) { *cp++ = ' '; if (cp == ce) break; hadzero = 0; } *cp++ = c; } else { hadzero = 1; } } *cp = '\0'; fclose(fp); } if (*p->p_cmdline == '\0') snprintf(p->p_cmdline,sizeof p->p_cmdline, "[ %s ]", p->p_name); return OKAY; } static struct pslot * readproc(char *pdir) { struct pslot *p; enum okay result; p = calloc(1, sizeof *p); if ((result = getproc_status(pdir, p)) == OKAY) result = getproc_cmdline(pdir, p); if (result == STOP) { free(p); return NULL; } return p; } #else /* !__linux__, !__FreeBSD__, !__DragonFly__ */ static struct pslot * readproc(char *pdir) { char fn[_POSIX_PATH_MAX]; FILE *fp; struct psinfo pi; struct pslot *p; snprintf(fn, sizeof fn, "%s/psinfo", pdir); if ((fp = fopen(fn, "r")) == NULL) return NULL; if (fread(&pi, 1, sizeof pi, fp) != sizeof pi) { fclose(fp); return NULL; } fclose(fp); p = smalloc(sizeof *p); p->p_next = NULL; p->p_time = pi.pr_time.tv_sec; #ifdef __sun p->p_ctime = pi.pr_ctime.tv_sec; #endif /* __sun */ strncpy(p->p_name, pi.pr_fname, sizeof p->p_name); p->p_name[sizeof p->p_name - 1] = '\0'; p->p_pid = pi.pr_pid; p->p_termid = pi.pr_ttydev; strncpy(p->p_cmdline, pi.pr_psargs, sizeof p->p_cmdline); p->p_cmdline[sizeof p->p_cmdline - 1] = '\0'; #ifndef __sun { struct pstatus ps; snprintf(fn, sizeof fn, "%s/status", pdir); if ((fp = fopen(fn, "r")) != NULL) { if (fread(&ps, 1, sizeof ps, fp) == sizeof ps) p->p_ctime = ps.pr_cutime.tv_sec + ps.pr_cstime.tv_sec; fclose(fp); } } #endif /* !__sun */ return p; } #endif /* !__linux__, !__FreeBSD__, !__DragonFly__ */ /* * Convert an entry in /proc to a slot entry, if advisable. */ static struct pslot * getproc(char *pname) { struct stat st; char *ep; char fn[_POSIX_PATH_MAX]; struct pslot *p; wchar_t wc; int n; strcpy(fn, "/proc/"); strcat(fn, pname); if (lstat(fn, &st) < 0) { pnerror(errno, fn); return NULL; } if (!S_ISDIR(st.st_mode)) return NULL; strtol(pname, &ep, 10); if (*ep != '\0') return NULL; if ((p = readproc(fn)) != NULL) { ep = p->p_cmdline; while (next(wc, ep, n), wc != '\0') { ep += n; if (mb_cur_max > 1 ? !iswprint(wc) : !isprint(wc)) do ep[-n] = '?'; while (--n); } } return p; } /* * Find all relevant processes. */ static void findprocs(struct tslot *t0) { DIR *dir; struct dirent *dent; struct pslot *p; if ((dir = opendir("/proc")) == NULL) { pnerror(errno, "/proc"); return; } while ((dent = readdir(dir)) != NULL) if ((p = getproc(dent->d_name)) != NULL) queueproc(t0, p); closedir(dir); } #elif defined __hpux static struct pslot * readproc(struct pst_status *pst) { struct pslot *p; p = smalloc(sizeof *p); p->p_next = NULL; p->p_time = pst->pst_utime + pst->pst_stime; p->p_ctime = pst->pst_child_utime.pst_sec + (pst->pst_child_utime.pst_usec > + 500000) + pst->pst_child_stime.pst_sec + (pst->pst_child_stime.pst_usec > + 500000); strncpy(p->p_name, pst->pst_ucomm, sizeof p->p_name); p->p_name[sizeof p->p_name - 1] = '\0'; p->p_pid = pst->pst_pid; if (pst->pst_term.psd_major != -1 || pst->pst_term.psd_minor != -1) p->p_termid = makedev(pst->pst_term.psd_major, pst->pst_term.psd_minor); else p->p_termid = PRNODEV; strncpy(p->p_cmdline, pst->pst_cmd, sizeof p->p_cmdline); p->p_cmdline[sizeof p->p_cmdline - 1] = '\0'; return p; } static void findprocs(struct tslot *t0) { #define burst ((size_t)10) struct pslot *p; struct pst_status pst[burst]; int i, count; int idx = 0; while ((count = pstat_getproc(pst, sizeof *pst, burst, idx)) > 0) { for (i = 0; i < count; i++) { p = readproc(&pst[i]); queueproc(t0, p); } idx = pst[count-1].pst_idx + 1; } } #elif defined (_AIX) static struct pslot * readproc(struct procentry64 *pi) { struct pslot *p; char args[100], *ap, *cp; p = smalloc(sizeof *p); p->p_next = NULL; p->p_time = pi->pi_utime + pi->pi_stime; p->p_ctime = p->p_time; strncpy(p->p_name, pi->pi_comm, sizeof p->p_name); p->p_name[sizeof p->p_name - 1] = '\0'; p->p_pid = pi->pi_pid; p->p_termid = pi->pi_ttyp ? pi->pi_ttyd : PRNODEV; if (getargs(pi, sizeof *pi, args, sizeof args) == 0) { ap = args; cp = p->p_cmdline; while (cp < &p->p_cmdline[sizeof p->p_cmdline - 1]) { if (ap[0] == '\0') { if (ap[1] == '\0') break; *cp++ = ' '; } else *cp++ = *ap; ap++; } *cp = '\0'; } return p; } static void findprocs(struct tslot *t0) { #define burst ((size_t)10) struct pslot *p; struct procentry64 pi[burst]; int i, count; pid_t idx = 0; while ((count = getprocs64(pi, sizeof *pi, NULL, 0, &idx, burst)) > 0) { for (i = 0; i < count; i++) { p = readproc(&pi[i]); queueproc(t0, p); } if (count < burst) break; } } #elif defined (__OpenBSD__) static struct pslot * readproc(struct kinfo_proc *kp, kvm_t *kt) { struct pslot *p; char **args; char *ap, *pp; p = smalloc(sizeof *p); p->p_next = 0; p->p_time = kp->kp_eproc.e_pstats.p_ru.ru_utime.tv_sec + kp->kp_eproc.e_pstats.p_ru.ru_stime.tv_sec; p->p_ctime = kp->kp_eproc.e_pstats.p_cru.ru_utime.tv_sec + kp->kp_eproc.e_pstats.p_cru.ru_stime.tv_sec; strncpy(p->p_name, kp->kp_proc.p_comm, sizeof p->p_name); p->p_name[sizeof p->p_name - 1] = '\0'; p->p_pid = kp->kp_proc.p_pid; if (kp->kp_proc.p_flag & P_CONTROLT) p->p_termid = kp->kp_eproc.e_tdev; else p->p_termid = PRNODEV; if ((args = kvm_getargv(kt, kp, sizeof p->p_cmdline)) == NULL) { strncpy(p->p_cmdline, p->p_name, sizeof p->p_name); p->p_cmdline[sizeof p->p_cmdline - 1] = '\0'; } else { ap = args[0]; for (pp = p->p_cmdline; pp < &p->p_cmdline[sizeof p->p_cmdline-1]; pp++) { if (*ap == '\0') { *pp = ' '; ap = *++args; if (ap == NULL) break; } else *pp = *ap++; } } return p; } static void findprocs(struct tslot *t0) { struct pslot *p; kvm_t *kt; struct kinfo_proc *kp; int i, cnt; if ((kt = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open")) == NULL) exit(1); kp = kvm_getprocs(kt, KERN_PROC_ALL, 0, &cnt); i = cnt; while (--i > 0) { p = readproc(&kp[i], kt); queueproc(t0, p); } kvm_close(kt); } #elif defined (__NetBSD__) static struct pslot * readproc(struct kinfo_proc2 *kp, kvm_t *kt) { struct pslot *p; char **args; char *ap, *pp; p = smalloc(sizeof *p); p->p_next = 0; p->p_time = kp->p_uutime_sec + kp->p_ustime_sec; p->p_ctime = kp->p_uctime_sec; strncpy(p->p_name, kp->p_comm, sizeof p->p_name); p->p_name[sizeof p->p_name - 1] = '\0'; p->p_pid = kp->p_pid; if (kp->p_flag & P_CONTROLT) p->p_termid = kp->p_tdev; else p->p_termid = PRNODEV; if ((args = kvm_getargv2(kt, kp, sizeof p->p_cmdline)) == NULL) { strncpy(p->p_cmdline, p->p_name, sizeof p->p_name); p->p_cmdline[sizeof p->p_cmdline - 1] = '\0'; } else { ap = args[0]; for (pp = p->p_cmdline; pp < &p->p_cmdline[sizeof p->p_cmdline-1]; pp++) { if (*ap == '\0') { *pp = ' '; ap = *++args; if (ap == NULL) break; } else *pp = *ap++; } } return p; } static void findprocs(struct tslot *t0) { struct pslot *p; kvm_t *kt; struct kinfo_proc2 *kp; int i, cnt; if ((kt = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open")) == NULL) exit(1); kp = kvm_getproc2(kt, KERN_PROC_ALL, 0, sizeof *kp, &cnt); i = cnt; while (--i > 0) { p = readproc(&kp[i], kt); queueproc(t0, p); } kvm_close(kt); } #elif defined (__APPLE__) static int GetBSDProcessList(pid_t thepid, struct kinfo_proc **procList, size_t *procCount) /* derived from http://developer.apple.com/qa/qa2001/qa1123.html */ /* Returns a list of all BSD processes on the system. This routine allocates the list and puts it in *procList and a count of the number of entries in *procCount. You are responsible for freeing this list (use "free" from System framework). all classic apps run in one process On success, the function returns 0. On error, the function returns a BSD errno value. Preconditions: assert( procList != NULL); assert(*procList == NULL); assert(procCount != NULL); Postconditions: assert( (err == 0) == (*procList != NULL) ); */ { int err; struct kinfo_proc *result; int mib[4]; size_t length; mib[0] = CTL_KERN; mib[1] = KERN_PROC; if (thepid == 0) { mib[2] = KERN_PROC_ALL; mib[3] = 0; } else { mib[2] = KERN_PROC_PID; mib[3] = thepid; } /* We start by calling sysctl with result == NULL and length == 0. That will succeed, and set length to the appropriate length. We then allocate a buffer of that size and call sysctl again with that buffer. */ length = 0; err = sysctl(mib, 4, NULL, &length, NULL, 0); if (err == -1) err = errno; if (err == 0) { result = smalloc(length); err = sysctl(mib, 4, result, &length, NULL, 0); if (err == -1) err = errno; if (err == ENOMEM) { free(result); /* clean up */ result = NULL; } } *procList = result; *procCount = err == 0 ? length / sizeof **procList : 0; return err; } static int getargv(struct pslot *p, struct kinfo_proc *kp) { size_t size, argsz; char *argbuf; int mib[3]; long nargs; char *ap, *pp; /* allocate a procargs space per process */ mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; size = sizeof argsz; if (sysctl(mib, 2, &argsz, &size, NULL, 0) == -1) return 0; argbuf = smalloc(argsz); /* fetch the process arguments */ mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = kp->kp_proc.p_pid; if (sysctl(mib, 3, argbuf, &argsz, NULL, 0) == -1) goto DONE; /* process already left the system */ /* the number of args is at offset 0, this works for 32 and 64bit */ memcpy(&nargs, argbuf, sizeof nargs); ap = argbuf + sizeof nargs; pp = NULL; /* skip the exec_path */ while (ap < &argbuf[argsz] && *ap != '\0') ap++; if (ap == &argbuf[argsz]) goto DONE; /* no args to show */ /* skip trailing '\0' chars */ while (ap < &argbuf[argsz] && *ap == '\0') ap++; if (ap == &argbuf[argsz]) goto DONE; /* no args to show */ /* now concat copy the arguments */ for (pp = p->p_cmdline; pp < &p->p_cmdline[sizeof p->p_cmdline-1]; pp++) { if (*ap == '\0') { if (--nargs == 0) break; *pp = ' '; ++ap; } else { *pp = *ap++; } } *pp = '\0'; DONE: free(argbuf); return pp != NULL; } static time_t tv2sec(time_value_t *tv, int mult) { return tv->seconds*mult + (tv->microseconds >= 500000/mult); } extern kern_return_t task_for_pid(task_port_t task, pid_t pid, task_port_t *target); static struct pslot * readproc(struct kinfo_proc *kp) { kern_return_t error; unsigned int info_count = TASK_BASIC_INFO_COUNT; unsigned int thread_info_count = THREAD_BASIC_INFO_COUNT; pid_t pid; task_port_t task; struct task_basic_info task_binfo; struct task_thread_times_info task_times; time_value_t total_time; struct pslot *p; char **args; char *ap, *pp; p = smalloc(sizeof *p); p->p_next = NULL; strncpy(p->p_name, kp->kp_proc.p_comm, sizeof p->p_name); p->p_name[sizeof p->p_name - 1] = '\0'; p->p_pid = kp->kp_proc.p_pid; p->p_time = 0; p->p_ctime = 0; if (kp->kp_proc.p_flag & P_CONTROLT) p->p_termid = kp->kp_eproc.e_tdev; else p->p_termid = PRNODEV; if (kp->kp_proc.p_stat == SZOMB || !getargv(p, kp)) { /* fallback to p_comm */ strncpy(p->p_cmdline, p->p_name, sizeof p->p_name); p->p_cmdline[sizeof p->p_cmdline - 1] = '\0'; } /* now try to fetch the times out of mach structures */ pid = kp->kp_proc.p_pid; error = task_for_pid(mach_task_self(), pid, &task); if (error != KERN_SUCCESS) goto DONE; /* process already left the system */ info_count = TASK_BASIC_INFO_COUNT; error = task_info(task, TASK_BASIC_INFO, &task_binfo, &info_count); if (error != KERN_SUCCESS) goto DONE; info_count = TASK_THREAD_TIMES_INFO_COUNT; error = task_info(task, TASK_THREAD_TIMES_INFO, &task_times, &info_count); if (error != KERN_SUCCESS) goto DONE; total_time = task_times.user_time; time_value_add(&total_time, &task_times.system_time); p->p_time = tv2sec(&total_time, 1); time_value_add(&total_time, &task_binfo.user_time); time_value_add(&total_time, &task_binfo.system_time); p->p_ctime = tv2sec(&total_time, 1); DONE: mach_port_deallocate(mach_task_self(), task); return p; } static void findprocs(struct tslot *t0) { struct pslot *p; struct kinfo_proc *kp = NULL; size_t cnt; int err; if ((err = GetBSDProcessList(0, &kp, &cnt)) != 0) { fprintf(stderr, "error getting proc list: %s\n", strerror(err)); exit(3); } while (--cnt > 0) { p = readproc(&kp[cnt]); queueproc(t0, p); } /* free the memory allocated by GetBSDProcessList */ free(kp); } #endif /* all */ /* * Return the device id that correspondends to the given file name. */ static dev_t lineno(char *line) { struct stat st; char fn[_POSIX_PATH_MAX]; strcpy(fn, "/dev/"); strcat(fn, line); if (stat(fn, &st) < 0) return 0; return st.st_rdev; } /* * Get load average. */ #if defined (__linux__) static void getload(void) { FILE *fp; char *sp1, *sp2, *sp3; char tmp[64]; if ((fp = fopen("/proc/loadavg", "r")) == NULL) { strcpy(loadavg, unknown); errcnt |= 1; return; } fread(tmp, sizeof(char), sizeof tmp, fp); fclose(fp); if ((sp1 = strchr(tmp, ' ')) != NULL && (sp2 = strchr(&sp1[1], ' ')) != NULL && (sp3 = strchr(&sp2[1], ' ')) != NULL) { sp1[0] = sp2[0] = sp3[0] = '\0'; snprintf(loadavg, sizeof loadavg, "%s, %s, %s", tmp, &sp1[1], &sp2[1]); } else { strcpy(loadavg, unknown); errcnt |= 1; } } #elif defined (__sun) || defined (__FreeBSD__) || defined (__NetBSD__) || \ defined (__OpenBSD__) || defined (__DragonFly__) || defined (__APPLE__) #ifndef LOADAVG_NSTATS #define LOADAVG_NSTATS 3 #endif #ifndef LOADAVG_1MIN #define LOADAVG_1MIN 0 #endif #ifndef LOADAVG_5MIN #define LOADAVG_5MIN 1 #endif #ifndef LOADAVG_15MIN #define LOADAVG_15MIN 2 #endif static void getload(void) { double val[LOADAVG_NSTATS]; if (getloadavg(val, LOADAVG_NSTATS) == LOADAVG_NSTATS) snprintf(loadavg, sizeof loadavg, "%.2f, %.2f, %.2f", val[LOADAVG_1MIN], val[LOADAVG_5MIN], val[LOADAVG_15MIN]); else strcpy(loadavg, unknown); } #elif defined (__hpux) static void getload(void) { struct pst_dynamic pst; pstat_getdynamic(&pst, sizeof pst, 1, 0); snprintf(loadavg, sizeof loadavg, "%.2f, %.2f, %.2f", pst.psd_avg_1_min, pst.psd_avg_5_min, pst.psd_avg_15_min); } #endif /* __hpux */ /* * Get the list of user logins. */ static void getlogins(void) { struct utmpx *ut; struct tslot *t, *t0 = NULL, *tprev = NULL; #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \ defined (__hpux) || defined (__NetBSD__) || defined (__OpenBSD__) || \ defined (__DragonFly__) || defined (__APPLE__) if (cmd != WHODO) getload(); #endif /* __linux__ || __sun || __FreeBSD__ || __hpux || __NetBSD__ || __OpenBSD__ || __DragonFly__ || __APPLE__ */ setutxent(); while ((ut = getutxent()) != NULL) { if (ut->ut_type == USER_PROCESS) { if (user) if (ut->ut_user == NULL || strcmp(user, ut->ut_user)) continue; t = (struct tslot *)smalloc(sizeof *t); if (t0 == NULL) t0 = t; else tprev->t_next = t; tprev = t; if (ut->ut_line) { strcpy(t->t_line, ut->ut_line); t->t_termid = lineno(ut->ut_line); } else { strcpy(t->t_line, unknown); t->t_termid = PRNODEV; } if (ut->ut_user) strcpy(t->t_user, ut->ut_user); else strcpy(t->t_user, unknown); t->t_time = ut->ut_tv.tv_sec; if (ut->ut_tv.tv_usec >= 500000) t->t_time++; t->t_pslot = NULL; t->t_next = NULL; } } findprocs(t0); printout(t0); endutxent(); } static void usage(void) { switch (cmd) { case W: case UPTIME: break; default: fprintf(stderr, "usage: %s [ -hl ] [ user ]\n", progname); } exit(2); } int main(int argc, char **argv) { int i; const char *opts = ":hl"; time(&now); #ifdef __GLIBC__ putenv("POSIXLY_CORRECT=1"); #endif setlocale(LC_CTYPE, ""); mb_cur_max = MB_CUR_MAX; progname = basename(argv[0]); if (progname[0] == 'w' && progname[1] == '\0') { cmd = W; lflag = 1; opts = ":hlsuw"; } else if (strcmp(progname, "uptime") == 0) { cmd = UPTIME; lflag = 1; uflag = 1; opts = ":"; } hz = sysconf(_SC_CLK_TCK); while ((i = getopt(argc, argv, opts)) != EOF) { switch (i) { case 'h': hflag = 1; break; case 'l': case 'w': lflag = 1; break; case 's': sflag = 1; break; case 'u': uflag = 1; break; default: if (cmd == W || cmd == UPTIME) fprintf(stderr, "%s: bad flag -%c\n", progname, optopt); usage(); } } if (argv[optind]) { if (cmd == UPTIME) usage(); user = argv[optind]; if (argv[++optind]) usage(); } getlogins(); return errcnt; }