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