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