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