1 /*
2 * whodo - who is doing what
3 *
4 * also source for "w" and "uptime" commands
5 *
6 * Gunnar Ritter, Freiburg i. Br., Germany, December 2000.
7 */
8 /*
9 * Copyright (c) 2003 Gunnar Ritter
10 *
11 * This software is provided 'as-is', without any express or implied
12 * warranty. In no event will the authors be held liable for any damages
13 * arising from the use of this software.
14 *
15 * Permission is granted to anyone to use this software for any purpose,
16 * including commercial applications, and to alter it and redistribute
17 * it freely, subject to the following restrictions:
18 *
19 * 1. The origin of this software must not be misrepresented; you must not
20 * claim that you wrote the original software. If you use this software
21 * in a product, an acknowledgment in the product documentation would be
22 * appreciated but is not required.
23 *
24 * 2. Altered source versions must be plainly marked as such, and must not be
25 * misrepresented as being the original software.
26 *
27 * 3. This notice may not be removed or altered from any source distribution.
28 */
29
30 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
31 #define USED __attribute__ ((used))
32 #elif defined __GNUC__
33 #define USED __attribute__ ((unused))
34 #else
35 #define USED
36 #endif
37 static const char sccsid[] USED = "@(#)whodo.sl 1.42 (gritter) 1/12/07";
38
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/utsname.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <dirent.h>
45 #include <limits.h>
46 #include <stdio.h>
47 #include <string.h>
48 #include <stdlib.h>
49 #include <errno.h>
50 #include <time.h>
51 #include <utmpx.h>
52 #include <libgen.h>
53 #include <ctype.h>
54 #include <locale.h>
55 #include <wchar.h>
56 #include <wctype.h>
57
58 #if defined (__FreeBSD__) || defined (__DragonFly__)
59 #include <sys/sysctl.h>
60 #endif
61
62 #if defined (__NetBSD__) || defined (__OpenBSD__) || defined (__APPLE__)
63 #if defined (__APPLE__)
64 #include <mach/mach_types.h>
65 #include <mach/task_info.h>
66 #else /* !__APPLE__ */
67 #include <kvm.h>
68 #endif /* !__APPLE__ */
69 #include <sys/param.h>
70 #include <sys/sysctl.h>
71 #endif /* __NetBSD__, __NetBSD__, __APPLE__ */
72
73 #ifdef __hpux
74 #include <sys/param.h>
75 #include <sys/pstat.h>
76 #endif
77
78 #ifdef _AIX
79 #include <procinfo.h>
80 #endif
81
82 #if !defined (__linux__) && !defined (__FreeBSD__) && !defined (__hpux) && \
83 !defined (_AIX) && !defined (__NetBSD__) && !defined (__OpenBSD__) && \
84 !defined (__DragonFly__) && !defined (__APPLE__)
85 #ifdef sun
86 #include <sys/loadavg.h>
87 #define _STRUCTURED_PROC 1
88 #endif
89 #include <sys/procfs.h>
90 #endif /* !__linux__, !__FreeBSD__, !__hpux, !_AIX, !__NetBSD__, !__OpenBSD__,
91 !__DragonFly__, !__APPLE__ */
92
93 #ifndef PRNODEV
94 #define PRNODEV 0
95 #endif
96
97 #define next(wc, s, n) (mb_cur_max > 1 && *(s) & 0200 ? \
98 ((n) = mbtowc(&(wc), (s), mb_cur_max), \
99 (n) = ((n) > 0 ? (n) : (n) < 0 ? (wc=WEOF, 1) : 1)) :\
100 ((wc) = *(s) & 0377, (n) = 1))
101
102 enum {
103 WHODO,
104 W,
105 UPTIME
106 };
107
108 enum valtype {
109 VT_CHAR,
110 VT_INT,
111 VT_UINT,
112 VT_LONG,
113 VT_ULONG
114 };
115
116 union value {
117 char v_char;
118 int v_int;
119 unsigned int v_uint;
120 long v_long;
121 unsigned long v_ulong;
122 };
123
124 static int cmd = WHODO; /* command personality */
125 static int errcnt; /* count of errors */
126 static int hflag; /* omit header */
127 static int lflag; /* w-like display format */
128 static int sflag; /* short form of output */
129 static int uflag; /* "uptime" format */
130 #undef hz
131 static int hz; /* clock ticks per second */
132 static char *user; /* look for one user only */
133 #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \
134 defined (__hpux) || defined (__NetBSD__) || defined (__OpenBSD__) || \
135 defined (__DragonFly__) || defined (__APPLE__)
136 static char loadavg[64]; /* load average */
137 #endif /* __linux__ || __sun || __FreeBSD__ || __hpux || __NetBSD__ ||
138 __OpenBSD__ || __DragonFly__ || __APPLE__ */
139 static char *progname; /* argv0 to main */
140 static const char *unknown = " unknown"; /* unknown */
141 static time_t now; /* time at program start */
142 static int mb_cur_max; /* MB_CUR_MAX acceleration */
143
144 #undef UGLY_XDM_HACK
145
146 /*
147 * Structure to represent a single process.
148 */
149 struct pslot {
150 struct pslot *p_next; /* next slot */
151 long p_time; /* cpu time of process */
152 long p_ctime; /* cpu time of children */
153 char p_name[16]; /* name of the executable file */
154 pid_t p_pid; /* process id */
155 dev_t p_termid; /* device id of the controlling tty */
156 char p_cmdline[30]; /* command line */
157 };
158
159 /*
160 * Structure to represent a user login.
161 */
162 struct tslot {
163 struct pslot *t_pslot; /* start of process table */
164 struct tslot *t_next; /* next slot */
165 time_t t_time; /* login timestamp */
166 char t_line[sizeof (((struct utmpx *)0)->ut_line)]; /*tty line name*/
167 char t_user[sizeof (((struct utmpx *)0)->ut_user)]; /* user name */
168 dev_t t_termid; /* device id of the tty */
169 };
170
171 /*
172 * perror()-alike.
173 */
174 static void
pnerror(int eno,const char * string)175 pnerror(int eno, const char *string)
176 {
177 fprintf(stderr, "%s: %s: %s\n", progname, string, strerror(eno));
178 errcnt |= 1;
179 }
180
181 /*
182 * Memory allocation with check.
183 */
184 static void *
srealloc(void * vp,size_t sz)185 srealloc(void *vp, size_t sz)
186 {
187 void *p;
188
189 if ((p = realloc(vp, sz)) == NULL) {
190 write(2, "no memory\n", 10);
191 exit(077);
192 }
193 return p;
194 }
195
196 static void *
smalloc(size_t sz)197 smalloc(size_t sz)
198 {
199 return srealloc(NULL, sz);
200 }
201
202 static union value *
getval(char ** listp,enum valtype type,int separator,int sep2)203 getval(char **listp, enum valtype type, int separator, int sep2)
204 {
205 char *buf;
206 static union value v;
207 const char *cp, *op;
208 char *cq, *x;
209
210 if (**listp == '\0')
211 return NULL;
212 op = *listp;
213 while (**listp != '\0') {
214 if ((separator == ' ' ?
215 isspace(**listp) : **listp == separator) ||
216 **listp == sep2)
217 break;
218 (*listp)++;
219 }
220 buf = alloca(*listp - op + 1);
221 for (cp = op, cq = buf; cp < *listp; cp++, cq++)
222 *cq = *cp;
223 *cq = '\0';
224 if (**listp) {
225 while ((separator == ' ' ?
226 isspace(**listp) : **listp == separator) ||
227 **listp == sep2)
228 (*listp)++;
229 }
230 switch (type) {
231 case VT_CHAR:
232 if (buf[1] != '\0')
233 return NULL;
234 v.v_char = buf[0];
235 break;
236 case VT_INT:
237 v.v_int = strtol(buf, &x, 10);
238 if (*x != '\0')
239 return NULL;
240 break;
241 case VT_UINT:
242 v.v_uint = strtoul(buf, &x, 10);
243 if (*x != '\0')
244 return NULL;
245 break;
246 case VT_LONG:
247 v.v_long = strtol(buf, &x, 10);
248 if (*x != '\0')
249 return NULL;
250 break;
251 case VT_ULONG:
252 v.v_ulong = strtoul(buf, &x, 10);
253 if (*x != '\0')
254 return NULL;
255 break;
256 }
257 return &v;
258 }
259
260 /*
261 * Print time in am/pm format.
262 */
263 static char *
time12(time_t t)264 time12(time_t t)
265 {
266 static char buf[8];
267 struct tm *tp;
268 int hour12;
269
270 tp = localtime(&t);
271 if (tp->tm_hour == 0)
272 hour12 = 0;
273 else if (tp->tm_hour < 13)
274 hour12 = tp->tm_hour;
275 else
276 hour12 = tp->tm_hour - 12;
277 snprintf(buf, sizeof buf, "%2u:%02u%s", hour12, tp->tm_min,
278 tp->tm_hour < 12 ? "am" : "pm");
279 return buf;
280 }
281
282 /*
283 * Print time in 24 hour format.
284 */
285 static char *
time24(time_t t)286 time24(time_t t)
287 {
288 static char buf[8];
289 struct tm *tp;
290
291 tp = localtime(&t);
292 snprintf(buf, sizeof buf, "%u:%02u", tp->tm_hour, tp->tm_min);
293 return buf;
294 }
295
296 /*
297 * Return the system's uptime.
298 */
299 static const char *
uptime(void)300 uptime(void)
301 {
302 static char buf[80];
303 long upsec = -1;
304 unsigned upday = 0, uphr = 0, upmin = 0;
305 char *cp;
306 #if defined (__linux__)
307 FILE *fp;
308 union value *v;
309 const char upfile[] = "/proc/uptime";
310
311 if ((fp = fopen(upfile, "r")) == NULL) {
312 pnerror(errno, upfile);
313 return unknown;
314 }
315 errno = 0;
316 if (fread(buf, 1, sizeof buf, fp) > 0) {
317 cp = buf;
318 if ((v = getval(&cp, VT_ULONG, '.', 0)) != NULL)
319 upsec = v->v_ulong;
320 }
321 if (upsec == -1) {
322 if (errno)
323 pnerror(errno, upfile);
324 else {
325 fprintf(stderr, "%s: invalid uptime file\n", upfile);
326 errcnt |= 1;
327 }
328 fclose(fp);
329 return unknown;
330 }
331 fclose(fp);
332 #elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) \
333 || defined (__DragonFly__) || defined (__APPLE__)
334 int name[2] = { CTL_KERN, KERN_BOOTTIME };
335 struct timeval old;
336 size_t oldlen = sizeof old;
337
338 if (sysctl(name, 2, &old, &oldlen, NULL, 0) < 0)
339 return unknown;
340 upsec = now - old.tv_sec;
341 #elif defined (__hpux)
342 struct pst_static pst;
343
344 pstat_getstatic(&pst, sizeof pst, 1, 0);
345 upsec = now - pst.boot_time;
346 #elif defined (_AIX)
347 struct procentry64 pi;
348 pid_t idx = 0;
349
350 getprocs64(&pi, sizeof pi, NULL, 0, &idx, 1);
351 upsec = now - pi.pi_start;
352 #else /* !__linux__, !__FreeBSD__, !__hpux, !_AIX, !__NetBSD__, !__OpenBSD__,
353 !__DragonFly__, !__APPLE__ */
354 FILE *fp;
355 struct psinfo pi;
356
357 if ((fp = fopen("/proc/0/psinfo", "r")) == NULL)
358 return unknown;
359 if (fread(&pi, 1, sizeof pi, fp) != sizeof pi) {
360 fclose(fp);
361 return unknown;
362 }
363 fclose(fp);
364 upsec = now - pi.pr_start.tv_sec;
365 #endif /* !__linux__, !__FreeBSD__, !__hpux, !_AIX, !__NetBSD__,
366 !__OpenBSD__, !__DragonFly__, !__APPLE__ */
367 if (upsec > 59) {
368 upmin = upsec / 60;
369 if (upmin > 59) {
370 uphr = upmin / 60;
371 upmin %= 60;
372 if (uphr > 23) {
373 upday = uphr / 24;
374 uphr %= 24;
375 }
376 }
377 }
378 if (cmd == WHODO)
379 snprintf(buf, sizeof buf, " %u day(s), %u hr(s), %u min(s)",
380 upday, uphr, upmin);
381 else {
382 cp = buf;
383 if (upday) {
384 sprintf(cp, " %u day(s),", upday);
385 cp += strlen(cp);
386 }
387 if (uphr && upmin)
388 sprintf(cp, " %2u:%02u,", uphr, upmin);
389 else {
390 if (uphr) {
391 sprintf(cp, " %u hr(s),", uphr);
392 cp += strlen(cp);
393 }
394 if (upmin)
395 sprintf(cp, " %u min(s),", upmin);
396 }
397 }
398 return buf;
399 }
400
401 /*
402 * Return the number of users currently logged on.
403 */
404 static unsigned
userno(struct tslot * t)405 userno(struct tslot *t)
406 {
407 unsigned uno;
408
409 for (uno = 0; t; t = t->t_next, uno++);
410 return uno;
411 }
412
413 /*
414 * Print the heading.
415 */
416 static void
printhead(struct tslot * t0)417 printhead(struct tslot *t0)
418 {
419 time_t t;
420 unsigned users;
421 struct utsname un;
422
423 time(&t);
424 if (lflag) {
425 users = userno(t0);
426 printf(" %s up%s ", time12(t), uptime());
427 if (cmd != WHODO) {
428 printf("%u %s", users, users > 1 ? "users" : "user");
429 #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \
430 defined (__hpux) || defined (__NetBSD__) || defined (__OpenBSD__) || \
431 defined (__DragonFly__) || defined (__APPLE__)
432 printf(", load average: %s", loadavg);
433 #endif /* __linux__ || __sun || __FreeBSD__ || __hpux || __NetBSD__ ||
434 __OpenBSD__ || __DragonFly__ || __APPLE__ */
435 printf("\n");
436 } else
437 printf("%u user(s)\n", users);
438 if (!uflag) {
439 if (sflag)
440 printf("User tty idle what\n");
441 else
442 printf(
443 "User tty login@ idle JCPU PCPU what\n");
444 }
445 } else {
446 if (uname(&un) < 0)
447 strcpy(un.nodename, unknown);
448 printf("%s%s\n", ctime(&t), un.nodename);
449 }
450 }
451
452 /*
453 * Convert a login stamp to text.
454 */
455 static char *
logtime(time_t t,int hform)456 logtime(time_t t, int hform)
457 {
458 static char b[64];
459 struct tm tl, tn;
460 size_t sz;
461 char *form;
462
463 memcpy(&tl, localtime(&t), sizeof tl);
464 memcpy(&tn, localtime(&now), sizeof tn);
465 t = mktime(&tl);
466 if (hform == 12) {
467 snprintf(b, sizeof b, "%s", time12(t));
468 } else {
469 if (tn.tm_year != tl.tm_year)
470 form = "%a %b %d %Y ";
471 else if (tn.tm_mday != tl.tm_mday)
472 form = "%a %b %d ";
473 else
474 form = NULL;
475 if (form)
476 sz = strftime(b, 63, form, &tl);
477 else {
478 *b = '\0';
479 sz = 0;
480 }
481 snprintf(b + sz, sizeof b - sz, "%s", time24(t));
482 }
483 return b;
484 }
485
486 /*
487 * Convert a time value given in jiffies (on Linux) to text.
488 */
489 static char *
jifftime(long jiff,char * buf,size_t buflen,int colon,int width)490 jifftime(long jiff, char *buf, size_t buflen, int colon, int width)
491 {
492 static char _b[63];
493 char *b = buf ? buf : _b;
494 long t;
495
496 #ifdef __linux__
497 t = jiff / hz;
498 #else
499 t = jiff;
500 #endif
501 if (t >= 60 || colon)
502 snprintf(b, 63, "%*lu:%02lu", width - 3, t / 60, t % 60);
503 else if (t > 0)
504 snprintf(b, 63, "%*lu", width, t);
505 else
506 snprintf(b, 64, "%*s", width, "");
507 return b;
508 }
509
510 /*
511 * Print a tty's idle time.
512 */
513 static char *
idletime(char * line)514 idletime(char *line)
515 {
516 struct stat st;
517 char fn[_POSIX_PATH_MAX];
518 static char buf[8];
519 time_t t;
520
521 snprintf(fn, sizeof fn, "/dev/%s", line);
522 if (stat(fn, &st) < 0)
523 return "";
524 time(&t);
525 t -= st.st_atime;
526 t /= 60;
527 if (t >= 60)
528 snprintf(buf, sizeof buf,
529 "%2lu:%02lu", (long)t / 60, (long)t % 60);
530 else if (t > 0)
531 snprintf(buf, sizeof buf, " %4lu", (long)t);
532 else
533 strcpy(buf, " ");
534 return buf;
535 }
536
537 /*
538 * Print a process table's accumulated time (+children).
539 */
540 static char *
jcpu(struct pslot * p0,char * buf,size_t buflen)541 jcpu(struct pslot *p0, char *buf, size_t buflen)
542 {
543 struct pslot *p;
544 long acc = 0;
545
546 for (p = p0; p; p = p->p_next)
547 acc += p->p_time + p->p_ctime;
548 return jifftime(acc, buf, buflen, 0, 6);
549 }
550
551 /*
552 * Print a process table's accumulated time.
553 */
554 static char *
pcpu(struct pslot * p0,char * buf,size_t buflen)555 pcpu(struct pslot *p0, char *buf, size_t buflen)
556 {
557 struct pslot *p;
558 long acc = 0;
559
560 for (p = p0; p; p = p->p_next)
561 acc += p->p_time;
562 return jifftime(acc, buf, buflen, 0, 6);
563 }
564
565 /*
566 * Output the user/process table.
567 */
568 static void
printout(struct tslot * t0)569 printout(struct tslot *t0)
570 {
571 struct tslot *t;
572 struct pslot *p;
573 char jbuf[8], pbuf[8];
574
575 if (hflag == 0)
576 printhead(t0);
577 if (uflag)
578 return;
579 if (lflag) {
580 for (t = t0; t != NULL; t = t->t_next) {
581 #ifndef UGLY_XDM_HACK
582 if (t->t_termid == PRNODEV)
583 continue;
584 #endif
585 if (t->t_pslot == NULL)
586 continue;
587 for (p = t->t_pslot; p->p_next != NULL; p = p->p_next);
588 if (sflag)
589 printf("%-8s %-12s %s %s\n",
590 t->t_user, t->t_line,
591 idletime(t->t_line), p->p_name);
592 else
593 printf("%-8s %-12s %s %s %s %s %s\n",
594 t->t_user, t->t_line,
595 logtime(t->t_time, 12),
596 idletime(t->t_line),
597 jcpu(t->t_pslot, jbuf, sizeof jbuf),
598 pcpu(t->t_pslot, pbuf, sizeof pbuf),
599 p->p_cmdline);
600 }
601 } else {
602 for (t = t0; t != NULL; t = t->t_next) {
603 #ifndef UGLY_XDM_HACK
604 if (t->t_termid == PRNODEV)
605 continue;
606 #endif
607 printf("\n%-10s %-9s %s\n", t->t_line, t->t_user,
608 logtime(t->t_time, 24));
609 if (t->t_pslot == NULL)
610 continue;
611 for (p = t->t_pslot; p != NULL; p = p->p_next)
612 printf(" %-10s %-6lu %s %s\n",
613 p->p_termid != PRNODEV ?
614 t->t_line : "fd/0",
615 (long)p->p_pid,
616 jifftime(p->p_time, NULL, 0, 1, 6),
617 p->p_name);
618 }
619 }
620 }
621
622 /*
623 * Insert a process slot into the terminal line table.
624 */
625 static void
queueproc(struct tslot * t0,struct pslot * ps)626 queueproc(struct tslot *t0, struct pslot *ps)
627 {
628 struct tslot *t;
629 struct pslot *p;
630
631 for (t = t0; t != NULL; t = t->t_next) {
632 #ifdef UGLY_XDM_HACK
633 if (t->t_termid == PRNODEV && ps->p_termid == PRNODEV) {
634 /*
635 * Hack for XFree86 xdm: The process uses -$DISPLAY on
636 * its command line.
637 */
638 if (strcmp(ps->p_name, "xdm") == 0) {
639 size_t sz = strlen(t->t_line);
640
641 if (ps->p_cmdline[0] == '-'
642 && strncmp(&ps->p_cmdline[1],
643 t->t_line, sz) == 0
644 && (ps->p_cmdline[sz+1] == ' '
645 || ps->p_cmdline[sz+1] == '\0')) {
646 strcpy(ps->p_cmdline, t->t_line);
647 strcpy(t->t_line, "X,");
648 strcat(t->t_line, ps->p_cmdline);
649 ps->p_name[0] = '\0';
650 strcpy(ps->p_cmdline, "/usr/X/bin/xdm");
651 } else
652 continue;
653 } else
654 continue;
655 } else
656 #endif /* UGLY_XDM_HACK */
657 if (t->t_termid != ps->p_termid)
658 continue;
659 if (t->t_pslot != NULL) {
660 for (p = t->t_pslot; p->p_next != NULL; p = p->p_next);
661 p->p_next = ps;
662 } else
663 t->t_pslot = ps;
664 break;
665 }
666 }
667
668 /*
669 * Read an entry from /proc into a process slot.
670 */
671
672 #if !defined (__hpux) && !defined (_AIX) && !defined (__NetBSD__) && \
673 !defined (__OpenBSD__) && !defined (__APPLE__)
674
675 #if defined (__linux__) || defined (__FreeBSD__) || defined (__DragonFly__)
676
677 #define GETVAL(a) if ((v = getval(&cp, (a), ' ', 0)) == NULL) \
678 goto error
679
680 #define GETVAL_COMMA(a) if ((v = getval(&cp, (a), ' ', ',')) == NULL) \
681 goto error
682 #endif /* __linux__ || __FreeBSD__ || __DragonFly__ */
683
684 #if defined (__linux__)
685 static struct pslot *
readproc(char * pdir)686 readproc(char *pdir)
687 {
688 static char *buf;
689 static size_t buflen;
690 char fn[_POSIX_PATH_MAX];
691 FILE *fp;
692 union value *v;
693 struct pslot p, *pp;
694 char *cp, *cq, *ce;
695 int c;
696 size_t sz, sc;
697
698 memset(&p, 0, sizeof p);
699 snprintf(fn, sizeof fn, "%s/stat", pdir);
700 if ((fp = fopen(fn, "r")) == NULL) {
701 pnerror(errno, fn);
702 return NULL;
703 }
704 for (cp = buf; ;) {
705 const unsigned chunk = 32;
706
707 if (buflen < (sz = cp - buf + chunk)) {
708 sc = cp - buf;
709 buf = srealloc(buf, buflen = sz);
710 cp = &buf[sc];
711 }
712 if ((sz = fread(cp, 1, chunk, fp)) < chunk) {
713 ce = &cp[sz - 1];
714 break;
715 }
716 cp += chunk;
717 }
718 fclose(fp);
719 if (*ce != '\n')
720 goto error;
721 *ce-- = '\0';
722 cp = buf;
723 GETVAL(VT_INT);
724 p.p_pid = v->v_int;
725 if (*cp++ != '(')
726 goto error;
727 for (cq = ce; cq >= cp && *cq != ')'; cq--);
728 if (cq < cp)
729 goto error;
730 *cq = '\0';
731 strncpy(p.p_name, cp, sizeof p.p_name);
732 p.p_name[sizeof p.p_name - 1] = '\0';
733 cp = &cq[1];
734 while (isspace(*cp))
735 cp++;
736 GETVAL(VT_CHAR);
737 if (v->v_char == 'Z')
738 return NULL;
739 GETVAL(VT_INT);
740 /* ppid unused */
741 GETVAL(VT_INT);
742 /* pgrp unused */
743 GETVAL(VT_INT);
744 /* session unused */
745 GETVAL(VT_INT);
746 p.p_termid = v->v_int;
747 if (p.p_termid == PRNODEV
748 #ifdef UGLY_XDM_HACK
749 && strcmp(p.p_name, "(xdm)")
750 #endif
751 )
752 return NULL;
753 GETVAL(VT_INT);
754 /* tty_pgrp unused */
755 GETVAL(VT_ULONG);
756 /* flags unused */
757 GETVAL(VT_ULONG);
758 /* min_flt unused */
759 GETVAL(VT_ULONG);
760 /* cmin_flt unused */
761 GETVAL(VT_ULONG);
762 /* maj_flt unused */
763 GETVAL(VT_ULONG);
764 /* cmaj_flt unused */
765 GETVAL(VT_ULONG);
766 p.p_time = v->v_ulong;
767 GETVAL(VT_ULONG);
768 p.p_time += v->v_ulong;
769 GETVAL(VT_ULONG);
770 p.p_ctime = v->v_ulong;
771 GETVAL(VT_ULONG);
772 p.p_ctime += v->v_ulong;
773 snprintf(fn, sizeof fn, "%s/cmdline", pdir);
774 if ((fp = fopen(fn, "r")) != NULL) {
775 int hadzero = 0;
776
777 cp = p.p_cmdline;
778 ce = cp + sizeof p.p_cmdline - 1;
779 while (cp < ce && (c = getc(fp)) != EOF) {
780 if (c != '\0') {
781 if (hadzero) {
782 *cp++ = ' ';
783 if (cp == ce)
784 break;
785 hadzero = 0;
786 }
787 *cp++ = c;
788 } else
789 hadzero = 1;
790 }
791 *cp = '\0';
792 fclose(fp);
793 }
794 if (*p.p_cmdline == '\0')
795 snprintf(p.p_cmdline, sizeof p.p_cmdline, "[ %s ]", p.p_name);
796 pp = smalloc(sizeof *pp);
797 memcpy(pp, &p, sizeof *pp);
798 return pp;
799 error:
800 fprintf(stderr, "%s: invalid entry\n", pdir);
801 errcnt |= 1;
802 return NULL;
803 }
804
805 #elif defined (__FreeBSD__) || defined (__DragonFly__)
806
807 enum okay {
808 OKAY,
809 STOP
810 };
811
812 static enum okay
getproc_status(char * pdir,struct pslot * p)813 getproc_status(char *pdir, struct pslot *p)
814 {
815 static char *buf;
816 static size_t buflen;
817 char fn[_POSIX_PATH_MAX];
818 union value *v;
819 FILE *fp;
820 char *cp, *ce, *cq;
821 size_t sz, sc;
822 int mj, mi;
823
824 snprintf(fn, sizeof fn, "%s/status", pdir);
825 if ((fp = fopen(fn, "r")) == NULL)
826 return STOP;
827 for (cp = buf; ;) {
828 const unsigned chunk = 32;
829
830 if (buflen < (sz = cp - buf + chunk)) {
831 sc = cp - buf;
832 buf = srealloc(buf, buflen = sz);
833 cp = &buf[sc];
834 }
835 if ((sz = fread(cp, 1, chunk, fp)) < chunk) {
836 ce = &cp[sz - 1];
837 break;
838 }
839 cp += chunk;
840 }
841 fclose(fp);
842 if (*ce != '\n')
843 return STOP;
844 *ce-- = '\0';
845 cp = buf;
846 while (*cp != ' ') {
847 if (cp - buf < sizeof p->p_name - 2)
848 p->p_name[cp-buf] = *cp;
849 cp++;
850 }
851 if (cp - buf < sizeof p->p_name - 1)
852 p->p_name[cp-buf] = '\0';
853 else
854 p->p_name[sizeof p->p_name - 1] = '\0';
855 while (*cp == ' ')
856 cp++;
857 GETVAL(VT_INT);
858 p->p_pid = v->v_int;
859 GETVAL(VT_INT);
860 /* ppid unused */
861 GETVAL(VT_INT);
862 /* pgid unused */
863 GETVAL(VT_INT);
864 /* sid unused */
865 if (isdigit(*cp)) {
866 GETVAL_COMMA(VT_INT);
867 mj = v->v_int;
868 GETVAL(VT_INT);
869 mi = v->v_int;
870 if (mj != -1 || mi != -1)
871 p->p_termid = makedev(mj, mi);
872 } else {
873 struct stat st;
874 char *dev;
875 cq = cp;
876 while (*cp != ' ') cp++;
877 *cp = '\0';
878 dev = smalloc(cp - cq + 8);
879 strcpy(dev, "/dev/");
880 strcpy(&dev[5], cq);
881 if (stat(dev, &st) < 0)
882 p->p_termid = PRNODEV;
883 else
884 p->p_termid = st.st_rdev;
885 free(dev);
886 *cp = ' ';
887 while (*cp == ' ') cp++;
888 }
889 while (*cp != ' ') cp++; while (*cp == ' ') cp++;
890 /* skip flags */
891 GETVAL_COMMA(VT_LONG);
892 /* start unused */
893 GETVAL(VT_LONG);
894 /* skip microseconds */
895 GETVAL_COMMA(VT_LONG);
896 p->p_time = v->v_long;
897 GETVAL(VT_LONG);
898 /* skip microseconds */
899 GETVAL_COMMA(VT_LONG);
900 p->p_time += v->v_long;
901 return OKAY;
902 error:
903 fprintf(stderr, "%s: invalid entry\n", pdir);
904 errcnt |= 1;
905 return STOP;
906 }
907
908 static enum okay
getproc_cmdline(char * pdir,struct pslot * p)909 getproc_cmdline(char *pdir, struct pslot *p)
910 {
911 char fn[_POSIX_PATH_MAX];
912 FILE *fp;
913 char *cp, *ce;
914 int hadzero = 0, c;
915
916 snprintf(fn, sizeof fn, "%s/cmdline", pdir);
917 if ((fp = fopen(fn, "r")) != NULL) {
918 cp = p->p_cmdline;
919 ce = cp + sizeof p->p_cmdline - 1;
920 while (cp < ce && (c = getc(fp)) != EOF) {
921 if (c != '\0') {
922 if (hadzero) {
923 *cp++ = ' ';
924 if (cp == ce)
925 break;
926 hadzero = 0;
927 }
928 *cp++ = c;
929 } else {
930 hadzero = 1;
931 }
932 }
933 *cp = '\0';
934 fclose(fp);
935 }
936 if (*p->p_cmdline == '\0')
937 snprintf(p->p_cmdline,sizeof p->p_cmdline, "[ %s ]", p->p_name);
938 return OKAY;
939 }
940
941 static struct pslot *
readproc(char * pdir)942 readproc(char *pdir)
943 {
944 struct pslot *p;
945 enum okay result;
946
947 p = calloc(1, sizeof *p);
948 if ((result = getproc_status(pdir, p)) == OKAY)
949 result = getproc_cmdline(pdir, p);
950 if (result == STOP) {
951 free(p);
952 return NULL;
953 }
954 return p;
955 }
956
957 #else /* !__linux__, !__FreeBSD__, !__DragonFly__ */
958 static struct pslot *
readproc(char * pdir)959 readproc(char *pdir)
960 {
961 char fn[_POSIX_PATH_MAX];
962 FILE *fp;
963 struct psinfo pi;
964 struct pslot *p;
965
966 snprintf(fn, sizeof fn, "%s/psinfo", pdir);
967 if ((fp = fopen(fn, "r")) == NULL)
968 return NULL;
969 if (fread(&pi, 1, sizeof pi, fp) != sizeof pi) {
970 fclose(fp);
971 return NULL;
972 }
973 fclose(fp);
974 p = smalloc(sizeof *p);
975 p->p_next = NULL;
976 p->p_time = pi.pr_time.tv_sec;
977 #ifdef __sun
978 p->p_ctime = pi.pr_ctime.tv_sec;
979 #endif /* __sun */
980 strncpy(p->p_name, pi.pr_fname, sizeof p->p_name);
981 p->p_name[sizeof p->p_name - 1] = '\0';
982 p->p_pid = pi.pr_pid;
983 p->p_termid = pi.pr_ttydev;
984 strncpy(p->p_cmdline, pi.pr_psargs, sizeof p->p_cmdline);
985 p->p_cmdline[sizeof p->p_cmdline - 1] = '\0';
986 #ifndef __sun
987 {
988 struct pstatus ps;
989 snprintf(fn, sizeof fn, "%s/status", pdir);
990 if ((fp = fopen(fn, "r")) != NULL) {
991 if (fread(&ps, 1, sizeof ps, fp) == sizeof ps)
992 p->p_ctime = ps.pr_cutime.tv_sec +
993 ps.pr_cstime.tv_sec;
994 fclose(fp);
995 }
996 }
997 #endif /* !__sun */
998 return p;
999 }
1000
1001 #endif /* !__linux__, !__FreeBSD__, !__DragonFly__ */
1002
1003 /*
1004 * Convert an entry in /proc to a slot entry, if advisable.
1005 */
1006 static struct pslot *
getproc(char * pname)1007 getproc(char *pname)
1008 {
1009 struct stat st;
1010 char *ep;
1011 char fn[_POSIX_PATH_MAX];
1012 struct pslot *p;
1013 wchar_t wc;
1014 int n;
1015
1016 strcpy(fn, "/proc/");
1017 strcat(fn, pname);
1018 if (lstat(fn, &st) < 0) {
1019 pnerror(errno, fn);
1020 return NULL;
1021 }
1022 if (!S_ISDIR(st.st_mode))
1023 return NULL;
1024 strtol(pname, &ep, 10);
1025 if (*ep != '\0')
1026 return NULL;
1027 if ((p = readproc(fn)) != NULL) {
1028 ep = p->p_cmdline;
1029 while (next(wc, ep, n), wc != '\0') {
1030 ep += n;
1031 if (mb_cur_max > 1 ? !iswprint(wc) : !isprint(wc))
1032 do
1033 ep[-n] = '?';
1034 while (--n);
1035 }
1036 }
1037 return p;
1038 }
1039
1040 /*
1041 * Find all relevant processes.
1042 */
1043 static void
findprocs(struct tslot * t0)1044 findprocs(struct tslot *t0)
1045 {
1046 DIR *dir;
1047 struct dirent *dent;
1048 struct pslot *p;
1049
1050 if ((dir = opendir("/proc")) == NULL) {
1051 pnerror(errno, "/proc");
1052 return;
1053 }
1054 while ((dent = readdir(dir)) != NULL)
1055 if ((p = getproc(dent->d_name)) != NULL)
1056 queueproc(t0, p);
1057 closedir(dir);
1058 }
1059
1060 #elif defined __hpux
1061 static struct pslot *
readproc(struct pst_status * pst)1062 readproc(struct pst_status *pst)
1063 {
1064 struct pslot *p;
1065
1066 p = smalloc(sizeof *p);
1067 p->p_next = NULL;
1068 p->p_time = pst->pst_utime + pst->pst_stime;
1069 p->p_ctime = pst->pst_child_utime.pst_sec +
1070 (pst->pst_child_utime.pst_usec > + 500000) +
1071 pst->pst_child_stime.pst_sec +
1072 (pst->pst_child_stime.pst_usec > + 500000);
1073 strncpy(p->p_name, pst->pst_ucomm, sizeof p->p_name);
1074 p->p_name[sizeof p->p_name - 1] = '\0';
1075 p->p_pid = pst->pst_pid;
1076 if (pst->pst_term.psd_major != -1 || pst->pst_term.psd_minor != -1)
1077 p->p_termid = makedev(pst->pst_term.psd_major,
1078 pst->pst_term.psd_minor);
1079 else
1080 p->p_termid = PRNODEV;
1081 strncpy(p->p_cmdline, pst->pst_cmd, sizeof p->p_cmdline);
1082 p->p_cmdline[sizeof p->p_cmdline - 1] = '\0';
1083 return p;
1084 }
1085
1086 static void
findprocs(struct tslot * t0)1087 findprocs(struct tslot *t0) {
1088 #define burst ((size_t)10)
1089 struct pslot *p;
1090 struct pst_status pst[burst];
1091 int i, count;
1092 int idx = 0;
1093
1094 while ((count = pstat_getproc(pst, sizeof *pst, burst, idx)) > 0) {
1095 for (i = 0; i < count; i++) {
1096 p = readproc(&pst[i]);
1097 queueproc(t0, p);
1098 }
1099 idx = pst[count-1].pst_idx + 1;
1100 }
1101 }
1102 #elif defined (_AIX)
1103 static struct pslot *
readproc(struct procentry64 * pi)1104 readproc(struct procentry64 *pi)
1105 {
1106 struct pslot *p;
1107 char args[100], *ap, *cp;
1108
1109 p = smalloc(sizeof *p);
1110 p->p_next = NULL;
1111 p->p_time = pi->pi_utime + pi->pi_stime;
1112 p->p_ctime = p->p_time;
1113 strncpy(p->p_name, pi->pi_comm, sizeof p->p_name);
1114 p->p_name[sizeof p->p_name - 1] = '\0';
1115 p->p_pid = pi->pi_pid;
1116 p->p_termid = pi->pi_ttyp ? pi->pi_ttyd : PRNODEV;
1117 if (getargs(pi, sizeof *pi, args, sizeof args) == 0) {
1118 ap = args;
1119 cp = p->p_cmdline;
1120 while (cp < &p->p_cmdline[sizeof p->p_cmdline - 1]) {
1121 if (ap[0] == '\0') {
1122 if (ap[1] == '\0')
1123 break;
1124 *cp++ = ' ';
1125 } else
1126 *cp++ = *ap;
1127 ap++;
1128 }
1129 *cp = '\0';
1130 }
1131 return p;
1132 }
1133
1134 static void
findprocs(struct tslot * t0)1135 findprocs(struct tslot *t0) {
1136 #define burst ((size_t)10)
1137 struct pslot *p;
1138 struct procentry64 pi[burst];
1139 int i, count;
1140 pid_t idx = 0;
1141
1142 while ((count = getprocs64(pi, sizeof *pi, NULL, 0, &idx, burst)) > 0) {
1143 for (i = 0; i < count; i++) {
1144 p = readproc(&pi[i]);
1145 queueproc(t0, p);
1146 }
1147 if (count < burst)
1148 break;
1149 }
1150 }
1151 #elif defined (__OpenBSD__)
1152 static struct pslot *
readproc(struct kinfo_proc * kp,kvm_t * kt)1153 readproc(struct kinfo_proc *kp, kvm_t *kt)
1154 {
1155 struct pslot *p;
1156 char **args;
1157 char *ap, *pp;
1158
1159 p = smalloc(sizeof *p);
1160 p->p_next = 0;
1161 p->p_time = kp->kp_eproc.e_pstats.p_ru.ru_utime.tv_sec +
1162 kp->kp_eproc.e_pstats.p_ru.ru_stime.tv_sec;
1163 p->p_ctime = kp->kp_eproc.e_pstats.p_cru.ru_utime.tv_sec +
1164 kp->kp_eproc.e_pstats.p_cru.ru_stime.tv_sec;
1165 strncpy(p->p_name, kp->kp_proc.p_comm, sizeof p->p_name);
1166 p->p_name[sizeof p->p_name - 1] = '\0';
1167 p->p_pid = kp->kp_proc.p_pid;
1168 if (kp->kp_proc.p_flag & P_CONTROLT)
1169 p->p_termid = kp->kp_eproc.e_tdev;
1170 else
1171 p->p_termid = PRNODEV;
1172 if ((args = kvm_getargv(kt, kp, sizeof p->p_cmdline)) == NULL) {
1173 strncpy(p->p_cmdline, p->p_name, sizeof p->p_name);
1174 p->p_cmdline[sizeof p->p_cmdline - 1] = '\0';
1175 } else {
1176 ap = args[0];
1177 for (pp = p->p_cmdline; pp <
1178 &p->p_cmdline[sizeof p->p_cmdline-1]; pp++) {
1179 if (*ap == '\0') {
1180 *pp = ' ';
1181 ap = *++args;
1182 if (ap == NULL)
1183 break;
1184 } else
1185 *pp = *ap++;
1186 }
1187 }
1188 return p;
1189 }
1190
1191 static void
findprocs(struct tslot * t0)1192 findprocs(struct tslot *t0) {
1193 struct pslot *p;
1194 kvm_t *kt;
1195 struct kinfo_proc *kp;
1196 int i, cnt;
1197
1198 if ((kt = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open")) == NULL)
1199 exit(1);
1200 kp = kvm_getprocs(kt, KERN_PROC_ALL, 0, &cnt);
1201 i = cnt;
1202 while (--i > 0) {
1203 p = readproc(&kp[i], kt);
1204 queueproc(t0, p);
1205 }
1206 kvm_close(kt);
1207 }
1208 #elif defined (__NetBSD__)
1209 static struct pslot *
readproc(struct kinfo_proc2 * kp,kvm_t * kt)1210 readproc(struct kinfo_proc2 *kp, kvm_t *kt)
1211 {
1212 struct pslot *p;
1213 char **args;
1214 char *ap, *pp;
1215
1216 p = smalloc(sizeof *p);
1217 p->p_next = 0;
1218 p->p_time = kp->p_uutime_sec + kp->p_ustime_sec;
1219 p->p_ctime = kp->p_uctime_sec;
1220 strncpy(p->p_name, kp->p_comm, sizeof p->p_name);
1221 p->p_name[sizeof p->p_name - 1] = '\0';
1222 p->p_pid = kp->p_pid;
1223 if (kp->p_flag & P_CONTROLT)
1224 p->p_termid = kp->p_tdev;
1225 else
1226 p->p_termid = PRNODEV;
1227 if ((args = kvm_getargv2(kt, kp, sizeof p->p_cmdline)) == NULL) {
1228 strncpy(p->p_cmdline, p->p_name, sizeof p->p_name);
1229 p->p_cmdline[sizeof p->p_cmdline - 1] = '\0';
1230 } else {
1231 ap = args[0];
1232 for (pp = p->p_cmdline; pp <
1233 &p->p_cmdline[sizeof p->p_cmdline-1]; pp++) {
1234 if (*ap == '\0') {
1235 *pp = ' ';
1236 ap = *++args;
1237 if (ap == NULL)
1238 break;
1239 } else
1240 *pp = *ap++;
1241 }
1242 }
1243 return p;
1244 }
1245
1246 static void
findprocs(struct tslot * t0)1247 findprocs(struct tslot *t0) {
1248 struct pslot *p;
1249 kvm_t *kt;
1250 struct kinfo_proc2 *kp;
1251 int i, cnt;
1252
1253 if ((kt = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open")) == NULL)
1254 exit(1);
1255 kp = kvm_getproc2(kt, KERN_PROC_ALL, 0, sizeof *kp, &cnt);
1256 i = cnt;
1257 while (--i > 0) {
1258 p = readproc(&kp[i], kt);
1259 queueproc(t0, p);
1260 }
1261 kvm_close(kt);
1262 }
1263 #elif defined (__APPLE__)
1264
1265 static int
GetBSDProcessList(pid_t thepid,struct kinfo_proc ** procList,size_t * procCount)1266 GetBSDProcessList(pid_t thepid, struct kinfo_proc **procList, size_t *procCount)
1267 /* derived from http://developer.apple.com/qa/qa2001/qa1123.html */
1268 /* Returns a list of all BSD processes on the system. This routine
1269 allocates the list and puts it in *procList and a count of the
1270 number of entries in *procCount. You are responsible for freeing
1271 this list (use "free" from System framework).
1272 all classic apps run in one process
1273 On success, the function returns 0.
1274 On error, the function returns a BSD errno value.
1275 Preconditions:
1276 assert( procList != NULL);
1277 assert(*procList == NULL);
1278 assert(procCount != NULL);
1279 Postconditions:
1280 assert( (err == 0) == (*procList != NULL) );
1281 */
1282 {
1283 int err;
1284 struct kinfo_proc *result;
1285 int mib[4];
1286 size_t length;
1287
1288 mib[0] = CTL_KERN;
1289 mib[1] = KERN_PROC;
1290 if (thepid == 0) {
1291 mib[2] = KERN_PROC_ALL;
1292 mib[3] = 0;
1293 } else {
1294 mib[2] = KERN_PROC_PID;
1295 mib[3] = thepid;
1296 }
1297 /* We start by calling sysctl with result == NULL and length == 0.
1298 That will succeed, and set length to the appropriate length.
1299 We then allocate a buffer of that size and call sysctl again
1300 with that buffer.
1301 */
1302 length = 0;
1303 err = sysctl(mib, 4, NULL, &length, NULL, 0);
1304 if (err == -1)
1305 err = errno;
1306 if (err == 0) {
1307 result = smalloc(length);
1308 err = sysctl(mib, 4, result, &length, NULL, 0);
1309 if (err == -1)
1310 err = errno;
1311 if (err == ENOMEM) {
1312 free(result); /* clean up */
1313 result = NULL;
1314 }
1315 }
1316 *procList = result;
1317 *procCount = err == 0 ? length / sizeof **procList : 0;
1318 return err;
1319 }
1320
1321 static int
getargv(struct pslot * p,struct kinfo_proc * kp)1322 getargv(struct pslot *p, struct kinfo_proc *kp)
1323 {
1324 size_t size, argsz;
1325 char *argbuf;
1326 int mib[3];
1327 long nargs;
1328 char *ap, *pp;
1329
1330 /* allocate a procargs space per process */
1331 mib[0] = CTL_KERN;
1332 mib[1] = KERN_ARGMAX;
1333 size = sizeof argsz;
1334 if (sysctl(mib, 2, &argsz, &size, NULL, 0) == -1)
1335 return 0;
1336 argbuf = smalloc(argsz);
1337
1338 /* fetch the process arguments */
1339 mib[0] = CTL_KERN;
1340 mib[1] = KERN_PROCARGS2;
1341 mib[2] = kp->kp_proc.p_pid;
1342 if (sysctl(mib, 3, argbuf, &argsz, NULL, 0) == -1)
1343 goto DONE; /* process already left the system */
1344
1345 /* the number of args is at offset 0, this works for 32 and 64bit */
1346 memcpy(&nargs, argbuf, sizeof nargs);
1347 ap = argbuf + sizeof nargs;
1348 pp = NULL;
1349
1350 /* skip the exec_path */
1351 while (ap < &argbuf[argsz] && *ap != '\0')
1352 ap++;
1353 if (ap == &argbuf[argsz])
1354 goto DONE; /* no args to show */
1355 /* skip trailing '\0' chars */
1356 while (ap < &argbuf[argsz] && *ap == '\0')
1357 ap++;
1358 if (ap == &argbuf[argsz])
1359 goto DONE; /* no args to show */
1360
1361 /* now concat copy the arguments */
1362 for (pp = p->p_cmdline; pp < &p->p_cmdline[sizeof p->p_cmdline-1]; pp++) {
1363 if (*ap == '\0') {
1364 if (--nargs == 0)
1365 break;
1366 *pp = ' ';
1367 ++ap;
1368 } else {
1369 *pp = *ap++;
1370 }
1371 }
1372 *pp = '\0';
1373
1374 DONE: free(argbuf);
1375 return pp != NULL;
1376 }
1377
1378 static time_t
tv2sec(time_value_t * tv,int mult)1379 tv2sec(time_value_t *tv, int mult)
1380 {
1381 return tv->seconds*mult + (tv->microseconds >= 500000/mult);
1382 }
1383
1384 extern kern_return_t task_for_pid(task_port_t task, pid_t pid, task_port_t *target);
1385
1386 static struct pslot *
readproc(struct kinfo_proc * kp)1387 readproc(struct kinfo_proc *kp)
1388 {
1389 kern_return_t error;
1390 unsigned int info_count = TASK_BASIC_INFO_COUNT;
1391 unsigned int thread_info_count = THREAD_BASIC_INFO_COUNT;
1392 pid_t pid;
1393 task_port_t task;
1394 struct task_basic_info task_binfo;
1395 struct task_thread_times_info task_times;
1396 time_value_t total_time;
1397 struct pslot *p;
1398 char **args;
1399 char *ap, *pp;
1400
1401 p = smalloc(sizeof *p);
1402 p->p_next = NULL;
1403 strncpy(p->p_name, kp->kp_proc.p_comm, sizeof p->p_name);
1404 p->p_name[sizeof p->p_name - 1] = '\0';
1405 p->p_pid = kp->kp_proc.p_pid;
1406 p->p_time = 0;
1407 p->p_ctime = 0;
1408 if (kp->kp_proc.p_flag & P_CONTROLT)
1409 p->p_termid = kp->kp_eproc.e_tdev;
1410 else
1411 p->p_termid = PRNODEV;
1412
1413 if (kp->kp_proc.p_stat == SZOMB || !getargv(p, kp)) {
1414 /* fallback to p_comm */
1415 strncpy(p->p_cmdline, p->p_name, sizeof p->p_name);
1416 p->p_cmdline[sizeof p->p_cmdline - 1] = '\0';
1417 }
1418
1419 /* now try to fetch the times out of mach structures */
1420 pid = kp->kp_proc.p_pid;
1421 error = task_for_pid(mach_task_self(), pid, &task);
1422 if (error != KERN_SUCCESS)
1423 goto DONE; /* process already left the system */
1424 info_count = TASK_BASIC_INFO_COUNT;
1425 error = task_info(task, TASK_BASIC_INFO, &task_binfo, &info_count);
1426 if (error != KERN_SUCCESS)
1427 goto DONE;
1428 info_count = TASK_THREAD_TIMES_INFO_COUNT;
1429 error = task_info(task, TASK_THREAD_TIMES_INFO, &task_times, &info_count);
1430 if (error != KERN_SUCCESS)
1431 goto DONE;
1432
1433 total_time = task_times.user_time;
1434 time_value_add(&total_time, &task_times.system_time);
1435 p->p_time = tv2sec(&total_time, 1);
1436
1437 time_value_add(&total_time, &task_binfo.user_time);
1438 time_value_add(&total_time, &task_binfo.system_time);
1439 p->p_ctime = tv2sec(&total_time, 1);
1440
1441 DONE: mach_port_deallocate(mach_task_self(), task);
1442 return p;
1443 }
1444
1445 static void
findprocs(struct tslot * t0)1446 findprocs(struct tslot *t0) {
1447 struct pslot *p;
1448 struct kinfo_proc *kp = NULL;
1449 size_t cnt;
1450 int err;
1451
1452 if ((err = GetBSDProcessList(0, &kp, &cnt)) != 0) {
1453 fprintf(stderr, "error getting proc list: %s\n", strerror(err));
1454 exit(3);
1455 }
1456 while (--cnt > 0) {
1457 p = readproc(&kp[cnt]);
1458 queueproc(t0, p);
1459 }
1460 /* free the memory allocated by GetBSDProcessList */
1461 free(kp);
1462 }
1463
1464 #endif /* all */
1465
1466 /*
1467 * Return the device id that correspondends to the given file name.
1468 */
1469 static dev_t
lineno(char * line)1470 lineno(char *line)
1471 {
1472 struct stat st;
1473 char fn[_POSIX_PATH_MAX];
1474
1475 strcpy(fn, "/dev/");
1476 strcat(fn, line);
1477 if (stat(fn, &st) < 0)
1478 return 0;
1479 return st.st_rdev;
1480 }
1481
1482 /*
1483 * Get load average.
1484 */
1485 #if defined (__linux__)
1486 static void
getload(void)1487 getload(void)
1488 {
1489 FILE *fp;
1490 char *sp1, *sp2, *sp3;
1491 char tmp[64];
1492
1493 if ((fp = fopen("/proc/loadavg", "r")) == NULL) {
1494 strcpy(loadavg, unknown);
1495 errcnt |= 1;
1496 return;
1497 }
1498 fread(tmp, sizeof(char), sizeof tmp, fp);
1499 fclose(fp);
1500 if ((sp1 = strchr(tmp, ' ')) != NULL &&
1501 (sp2 = strchr(&sp1[1], ' ')) != NULL &&
1502 (sp3 = strchr(&sp2[1], ' ')) != NULL) {
1503 sp1[0] = sp2[0] = sp3[0] = '\0';
1504 snprintf(loadavg, sizeof loadavg, "%s, %s, %s",
1505 tmp, &sp1[1], &sp2[1]);
1506 } else {
1507 strcpy(loadavg, unknown);
1508 errcnt |= 1;
1509 }
1510 }
1511 #elif defined (__sun) || defined (__FreeBSD__) || defined (__NetBSD__) || \
1512 defined (__OpenBSD__) || defined (__DragonFly__) || defined (__APPLE__)
1513
1514 #ifndef LOADAVG_NSTATS
1515 #define LOADAVG_NSTATS 3
1516 #endif
1517 #ifndef LOADAVG_1MIN
1518 #define LOADAVG_1MIN 0
1519 #endif
1520 #ifndef LOADAVG_5MIN
1521 #define LOADAVG_5MIN 1
1522 #endif
1523 #ifndef LOADAVG_15MIN
1524 #define LOADAVG_15MIN 2
1525 #endif
1526
1527 static void
getload(void)1528 getload(void)
1529 {
1530 double val[LOADAVG_NSTATS];
1531
1532 if (getloadavg(val, LOADAVG_NSTATS) == LOADAVG_NSTATS)
1533 snprintf(loadavg, sizeof loadavg, "%.2f, %.2f, %.2f",
1534 val[LOADAVG_1MIN],
1535 val[LOADAVG_5MIN],
1536 val[LOADAVG_15MIN]);
1537 else
1538 strcpy(loadavg, unknown);
1539 }
1540 #elif defined (__hpux)
1541 static void
getload(void)1542 getload(void)
1543 {
1544 struct pst_dynamic pst;
1545
1546 pstat_getdynamic(&pst, sizeof pst, 1, 0);
1547 snprintf(loadavg, sizeof loadavg, "%.2f, %.2f, %.2f",
1548 pst.psd_avg_1_min,
1549 pst.psd_avg_5_min,
1550 pst.psd_avg_15_min);
1551 }
1552 #endif /* __hpux */
1553
1554 /*
1555 * Get the list of user logins.
1556 */
1557 static void
getlogins(void)1558 getlogins(void)
1559 {
1560 struct utmpx *ut;
1561 struct tslot *t, *t0 = NULL, *tprev = NULL;
1562
1563 #if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \
1564 defined (__hpux) || defined (__NetBSD__) || defined (__OpenBSD__) || \
1565 defined (__DragonFly__) || defined (__APPLE__)
1566 if (cmd != WHODO)
1567 getload();
1568 #endif /* __linux__ || __sun || __FreeBSD__ || __hpux || __NetBSD__ ||
1569 __OpenBSD__ || __DragonFly__ || __APPLE__ */
1570 setutxent();
1571 while ((ut = getutxent()) != NULL) {
1572 if (ut->ut_type == USER_PROCESS) {
1573 if (user)
1574 if (ut->ut_user == NULL
1575 || strcmp(user, ut->ut_user))
1576 continue;
1577 t = (struct tslot *)smalloc(sizeof *t);
1578 if (t0 == NULL)
1579 t0 = t;
1580 else
1581 tprev->t_next = t;
1582 tprev = t;
1583 if (ut->ut_line) {
1584 strcpy(t->t_line, ut->ut_line);
1585 t->t_termid = lineno(ut->ut_line);
1586 } else {
1587 strcpy(t->t_line, unknown);
1588 t->t_termid = PRNODEV;
1589 }
1590 if (ut->ut_user)
1591 strcpy(t->t_user, ut->ut_user);
1592 else
1593 strcpy(t->t_user, unknown);
1594 t->t_time = ut->ut_tv.tv_sec;
1595 if (ut->ut_tv.tv_usec >= 500000)
1596 t->t_time++;
1597 t->t_pslot = NULL;
1598 t->t_next = NULL;
1599 }
1600 }
1601 findprocs(t0);
1602 printout(t0);
1603 endutxent();
1604 }
1605
1606 static void
usage(void)1607 usage(void)
1608 {
1609 switch (cmd) {
1610 case W:
1611 case UPTIME:
1612 break;
1613 default:
1614 fprintf(stderr, "usage: %s [ -hl ] [ user ]\n", progname);
1615 }
1616 exit(2);
1617 }
1618
1619 int
main(int argc,char ** argv)1620 main(int argc, char **argv)
1621 {
1622 int i;
1623 const char *opts = ":hl";
1624
1625 time(&now);
1626 #ifdef __GLIBC__
1627 putenv("POSIXLY_CORRECT=1");
1628 #endif
1629 setlocale(LC_CTYPE, "");
1630 mb_cur_max = MB_CUR_MAX;
1631 progname = basename(argv[0]);
1632 if (progname[0] == 'w' && progname[1] == '\0') {
1633 cmd = W;
1634 lflag = 1;
1635 opts = ":hlsuw";
1636 } else if (strcmp(progname, "uptime") == 0) {
1637 cmd = UPTIME;
1638 lflag = 1;
1639 uflag = 1;
1640 opts = ":";
1641 }
1642 hz = sysconf(_SC_CLK_TCK);
1643 while ((i = getopt(argc, argv, opts)) != EOF) {
1644 switch (i) {
1645 case 'h':
1646 hflag = 1;
1647 break;
1648 case 'l':
1649 case 'w':
1650 lflag = 1;
1651 break;
1652 case 's':
1653 sflag = 1;
1654 break;
1655 case 'u':
1656 uflag = 1;
1657 break;
1658 default:
1659 if (cmd == W || cmd == UPTIME)
1660 fprintf(stderr, "%s: bad flag -%c\n",
1661 progname, optopt);
1662 usage();
1663 }
1664 }
1665 if (argv[optind]) {
1666 if (cmd == UPTIME)
1667 usage();
1668 user = argv[optind];
1669 if (argv[++optind])
1670 usage();
1671 }
1672 getlogins();
1673 return errcnt;
1674 }
1675