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