xref: /original-bsd/usr.bin/w/w.c (revision 68d9582f)
1 /*-
2  * Copyright (c) 1980, 1991 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1980, 1991 The Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)w.c	5.33 (Berkeley) 05/20/92";
16 #endif /* not lint */
17 
18 #define ADDRHACK
19 
20 /*
21  * w - print system status (who and what)
22  *
23  * This program is similar to the systat command on Tenex/Tops 10/20
24  *
25  */
26 #include <sys/param.h>
27 #include <sys/file.h>
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <sys/kinfo.h>
31 #include <sys/proc.h>
32 #include <sys/user.h>
33 #include <sys/ioctl.h>
34 #include <sys/tty.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <kvm.h>
38 #include <nlist.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <utmp.h>
43 #include <paths.h>
44 
45 #ifdef ADDRHACK
46 #include <sys/socket.h>
47 #include <netdb.h>
48 #endif
49 
50 char	*program;
51 int	ttywidth;		/* width of tty */
52 int	argwidth;		/* width of tty */
53 int	header = 1;		/* true if -h flag: don't print heading */
54 int	wcmd = 1;		/* true if this is w(1), and not uptime(1) */
55 #ifdef ADDRHACK
56 int	nflag = 0;		/* true if -n flag: don't convert addrs */
57 #endif
58 int	nusers;			/* number of users logged in now */
59 char *	sel_user;		/* login of particular user selected */
60 time_t	now;			/* the current time of day */
61 struct	timeval boottime;
62 time_t	uptime;			/* time of last reboot & elapsed time since */
63 struct	utmp utmp;
64 struct	winsize ws;
65 int	sortidle;		/* sort bu idle time */
66 #ifdef ADDRHACK
67 char	domain[64];
68 #endif
69 kvm_t *kd;
70 
71 #if __STDC__
72 void error(const char *fmt, ...);
73 #else
74 void error();
75 #endif
76 
77 void prttime();
78 
79 /*
80  * One of these per active utmp entry.
81  */
82 struct	entry {
83 	struct	entry *next;
84 	struct	utmp utmp;
85 	dev_t	tdev;		/* dev_t of terminal */
86 	int	idle;		/* idle time of terminal in minutes */
87 	struct	kinfo_proc *kp;	/* `most interesting' proc */
88 	char	*args;		/* arg list of interesting process */
89 } *ep, *ehead = NULL, **nextp = &ehead;
90 
91 struct nlist nl[] = {
92 	{ "_boottime" },
93 #define X_BOOTTIME	0
94 #if defined(hp300) || defined(i386)
95 	{ "_cn_tty" },
96 #define X_CNTTY		1
97 #endif
98 	{ "" },
99 };
100 
101 #define USAGE "[ -hi ] [ user ]"
102 #define usage()	fprintf(stderr, "usage: %s: %s\n", program, USAGE)
103 
104 main(argc, argv)
105 	int argc;
106 	char **argv;
107 {
108 	register int i;
109 	register struct kinfo_proc *kp;
110 	struct stat *stp, *ttystat();
111 	FILE *ut;
112 	char *cp;
113 	int nentries;
114 	int ch;
115 	extern char *optarg;
116 	extern int optind;
117 	char *attime();
118 	char errbuf[80];
119 
120 	program = argv[0];
121 	/*
122 	 * are we w(1) or uptime(1)
123 	 */
124 	if ((cp = rindex(program, '/')) || *(cp = program) == '-')
125 		cp++;
126 	if (*cp == 'u')
127 		wcmd = 0;
128 
129 	while ((ch = getopt(argc, argv, "hiflnsuw")) != EOF)
130 		switch((char)ch) {
131 		case 'h':
132 			header = 0;
133 			break;
134 		case 'i':
135 			sortidle++;
136 			break;
137 		case 'n':
138 #ifdef ADDRHACK
139 			++nflag;
140 #endif
141 			break;
142 		case 'f': case 'l': case 's': case 'u': case 'w':
143 			error("[-flsuw] no longer supported");
144 			usage();
145 			exit(1);
146 		case '?':
147 		default:
148 			usage();
149 			exit(1);
150 		}
151 	argc -= optind;
152 	argv += optind;
153 
154 	if (*argv)
155 		sel_user = *argv;
156 
157 	kd = kvm_openfiles((char *)0, (char *)0, (char *)0, O_RDONLY, errbuf);
158 	if (kd == NULL) {
159 		error("%s", errbuf);
160 		exit(1);
161 	}
162 	if (header && kvm_nlist(kd, nl) != 0) {
163 		error("can't get namelist");
164 		exit(1);
165 	}
166 #ifdef ADDRHACK
167 	if (!nflag) {
168 		if (gethostname(domain, sizeof(domain) - 1) < 0 ||
169 		    (cp = index(domain, '.')) == 0)
170 			domain[0] = '\0';
171 		else {
172 			domain[sizeof(domain) - 1] = '\0';
173 			bcopy(cp, domain, strlen(cp) + 1);
174 		}
175 	}
176 #endif
177 	time(&now);
178 	ut = fopen(_PATH_UTMP, "r");
179 	if (ut == NULL) {
180 		error("%s: %s", _PATH_UTMP, strerror(errno));
181 		exit(1);
182 	}
183 	while (fread(&utmp, sizeof(utmp), 1, ut)) {
184 		if (utmp.ut_name[0] == '\0')
185 			continue;
186 		nusers++;
187 		if (wcmd == 0 || (sel_user &&
188 		    strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0))
189 			continue;
190 		if ((ep = (struct entry *)
191 		     calloc(1, sizeof (struct entry))) == NULL) {
192 			error("out of memory");
193 			exit(1);
194 		}
195 		*nextp = ep;
196 		nextp = &(ep->next);
197 		bcopy(&utmp, &(ep->utmp), sizeof (struct utmp));
198 		stp = ttystat(ep->utmp.ut_line);
199 		ep->tdev = stp->st_rdev;
200 #if defined(hp300) || defined(i386)
201 		/*
202 		 * XXX  If this is the console device, attempt to ascertain
203 		 * the true console device dev_t.
204 		 */
205 		if (ep->tdev == 0) {
206 			static dev_t cn_dev;
207 
208 			if (nl[X_CNTTY].n_value) {
209 				struct tty cn_tty, *cn_ttyp;
210 
211 				if (kvm_read(kd, (u_long)nl[X_CNTTY].n_value,
212 				    (char *)&cn_ttyp, sizeof(cn_ttyp)) > 0) {
213 					(void)kvm_read(kd, (u_long)cn_ttyp,
214 					    (char *)&cn_tty, sizeof (cn_tty));
215 					cn_dev = cn_tty.t_dev;
216 				}
217 				nl[X_CNTTY].n_value = 0;
218 			}
219 			ep->tdev = cn_dev;
220 		}
221 #endif
222 		ep->idle = ((now - stp->st_atime) + 30) / 60; /* secs->mins */
223 		if (ep->idle < 0)
224 			ep->idle = 0;
225 	}
226 	fclose(ut);
227 
228 	if (header || wcmd == 0) {
229 		double	avenrun[3];
230 		int days, hrs, mins;
231 
232 		/*
233 		 * Print time of day
234 		 */
235 		fputs(attime(&now), stdout);
236 		/*
237 		 * Print how long system has been up.
238 		 * (Found by looking for "boottime" in kernel)
239 		 */
240 		(void)kvm_read(kd, (u_long)nl[X_BOOTTIME].n_value,
241 		    (char *)&boottime, sizeof (boottime));
242 		uptime = now - boottime.tv_sec;
243 		uptime += 30;
244 		days = uptime / (60*60*24);
245 		uptime %= (60*60*24);
246 		hrs = uptime / (60*60);
247 		uptime %= (60*60);
248 		mins = uptime / 60;
249 
250 		printf("  up");
251 		if (days > 0)
252 			printf(" %d day%s,", days, days>1?"s":"");
253 		if (hrs > 0 && mins > 0) {
254 			printf(" %2d:%02d,", hrs, mins);
255 		} else {
256 			if (hrs > 0)
257 				printf(" %d hr%s,", hrs, hrs>1?"s":"");
258 			if (mins > 0)
259 				printf(" %d min%s,", mins, mins>1?"s":"");
260 		}
261 
262 		/* Print number of users logged in to system */
263 		printf("  %d user%s", nusers, nusers>1?"s":"");
264 
265 		/*
266 		 * Print 1, 5, and 15 minute load averages.
267 		 */
268 		printf(",  load average:");
269 		(void)getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
270 		for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) {
271 			if (i > 0)
272 				printf(",");
273 			printf(" %.2f", avenrun[i]);
274 		}
275 		printf("\n");
276 		if (wcmd == 0)		/* if uptime(1) then done */
277 			exit(0);
278 #define HEADER	"USER    TTY FROM              LOGIN@  IDLE WHAT\n"
279 #define WUSED	(sizeof (HEADER) - sizeof ("WHAT\n"))
280 		printf(HEADER);
281 	}
282 
283 	if ((kp = kvm_getprocs(kd, KINFO_PROC_ALL, 0, &nentries)) == NULL)
284 		error("%s", kvm_geterr(kd));
285 	for (i = 0; i < nentries; i++, kp++) {
286 		register struct proc *p = &kp->kp_proc;
287 		register struct eproc *e;
288 
289 		if (p->p_stat == SIDL || p->p_stat == SZOMB)
290 			continue;
291 		e = &kp->kp_eproc;
292 		for (ep = ehead; ep != NULL; ep = ep->next) {
293 			if (ep->tdev == e->e_tdev && e->e_pgid == e->e_tpgid) {
294 				/*
295 				 * Proc is in foreground of this terminal
296 				 */
297 				if (proc_compare(&ep->kp->kp_proc, p))
298 					ep->kp = kp;
299 				break;
300 			}
301 		}
302 	}
303 	if ((ioctl(1, TIOCGWINSZ, &ws) == -1 &&
304 	     ioctl(2, TIOCGWINSZ, &ws) == -1 &&
305 	     ioctl(0, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
306 	       ttywidth = 79;
307         else
308 	       ttywidth = ws.ws_col - 1;
309 	argwidth = ttywidth - WUSED;
310 	if (argwidth < 4)
311 		argwidth = 8;
312 	for (ep = ehead; ep != NULL; ep = ep->next) {
313 		char *fmt_argv();
314 
315 		if (ep->kp == NULL) {
316 			ep->args = "-";
317 			continue;
318 		}
319 		ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
320 		    ep->kp->kp_proc.p_comm, MAXCOMLEN);
321 		if (ep->args == NULL) {
322 			error("out of memory");
323 			exit(1);
324 		}
325 	}
326 	/* sort by idle time */
327 	if (sortidle && ehead != NULL) {
328 		struct entry *from = ehead, *save;
329 
330 		ehead = NULL;
331 		while (from != NULL) {
332 			for (nextp = &ehead;
333 			    (*nextp) && from->idle >= (*nextp)->idle;
334 			    nextp = &(*nextp)->next)
335 				continue;
336 			save = from;
337 			from = from->next;
338 			save->next = *nextp;
339 			*nextp = save;
340 		}
341 	}
342 
343 	for (ep = ehead; ep != NULL; ep = ep->next) {
344 #ifdef ADDRHACK
345 		register char *x;
346 		register struct hostent *hp;
347 		unsigned long l;
348 		char buf[64];
349 #endif
350 
351 		cp = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
352 #ifdef ADDRHACK
353 		if (x = index(cp, ':'))
354 			*x++ = '\0';
355 		if (!nflag && isdigit(*cp) &&
356 		    (long)(l = inet_addr(cp)) != -1 &&
357 		    (hp = gethostbyaddr((char *)&l, sizeof(l),
358 		    AF_INET))) {
359 			    if (domain[0] != '\0') {
360 				    cp = hp->h_name;
361 				    cp += strlen(hp->h_name);
362 				    cp -= strlen(domain);
363 				    if (cp > hp->h_name &&
364 					strcmp(cp, domain) == 0)
365 					*cp = '\0';
366 			    }
367 			    cp = hp->h_name;
368 		}
369 		if (x) {
370 			sprintf(buf, "%s:%s", cp, x);
371 			cp = buf;
372 		}
373 #endif
374 		printf("%-*.*s %-2.2s %-*.*s %s",
375 			UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
376 			strncmp(ep->utmp.ut_line, "tty", 3) == 0 ?
377 				ep->utmp.ut_line+3 : ep->utmp.ut_line,
378 			UT_HOSTSIZE, UT_HOSTSIZE, *cp ? cp : "-",
379 			attime(&ep->utmp.ut_time));
380 		if (ep->idle >= 36 * 60)
381 			printf(" %ddays ", (ep->idle + 12 * 60) / (24 * 60));
382 		else
383 			prttime(ep->idle, " ");
384 		printf("%.*s\n", argwidth, ep->args);
385 	}
386 	exit(0);
387 }
388 
389 struct stat *
390 ttystat(line)
391 	char *line;
392 {
393 	static struct stat statbuf;
394 	char ttybuf[sizeof (_PATH_DEV) + UT_LINESIZE + 1];
395 
396 	sprintf(ttybuf, "%s/%.*s", _PATH_DEV, UT_LINESIZE, line);
397 	(void) stat(ttybuf, &statbuf);
398 
399 	return (&statbuf);
400 }
401 
402 /*
403  * prttime prints a time in hours and minutes or minutes and seconds.
404  * The character string tail is printed at the end, obvious
405  * strings to pass are "", " ", or "am".
406  */
407 void
408 prttime(tim, tail)
409 	time_t tim;
410 	char *tail;
411 {
412 
413 	if (tim >= 60) {
414 		printf(" %2d:", tim/60);
415 		tim %= 60;
416 		printf("%02d", tim);
417 	} else if (tim >= 0)
418 		printf("    %2d", tim);
419 	printf("%s", tail);
420 }
421 
422 #if __STDC__
423 #include <stdarg.h>
424 #else
425 #include <varargs.h>
426 #endif
427 
428 void
429 #if __STDC__
430 error(const char *fmt, ...)
431 #else
432 error(va_alist)
433 	va_dcl
434 #endif
435 {
436 #if !__STDC__
437 	char *fmt;
438 #endif
439 	va_list ap;
440 
441 	fprintf(stderr, "%s: ", program);
442 #if __STDC__
443 	va_start(ap, fmt);
444 #else
445 	va_start(ap);
446 	fmt = va_arg(ap, char *);
447 #endif
448 	(void) vfprintf(stderr, fmt, ap);
449 	va_end(ap);
450 	fprintf(stderr, "\n");
451 }
452