ANSI
[unix-history] / usr / src / usr.bin / w / w.c
CommitLineData
f42904bc
DF
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
8char copyright[] =
9"@(#) Copyright (c) 1980 Regents of the University of California.\n\
10 All rights reserved.\n";
11#endif not lint
12
68d13ee3 13#ifndef lint
12bed5d4 14static char sccsid[] = "@(#)w.c 5.25 (Berkeley) %G%";
f42904bc
DF
15#endif not lint
16
76238d88
BJ
17/*
18 * w - print system status (who and what)
19 *
20 * This program is similar to the systat command on Tenex/Tops 10/20
134b296f 21 *
76238d88
BJ
22 */
23#include <sys/param.h>
76238d88 24#include <utmp.h>
76238d88 25#include <sys/stat.h>
76238d88
BJ
26#include <sys/user.h>
27#include <sys/proc.h>
3b8dff21 28#include <sys/ioctl.h>
57ee57ff 29#include <machine/pte.h>
76238d88 30#include <sys/vm.h>
af08d75a 31#include <sys/tty.h>
134b296f
MT
32#include <nlist.h>
33#include <kvm.h>
34#include <ctype.h>
57bb6ffa 35#include <paths.h>
38dde0cd 36#include <string.h>
134b296f 37#include <stdio.h>
76238d88 38
134b296f
MT
39char *program;
40int ttywidth; /* width of tty */
41int argwidth; /* width of tty */
76238d88 42int header = 1; /* true if -h flag: don't print heading */
134b296f 43int wcmd = 1; /* true if this is w(1), and not uptime(1) */
76238d88
BJ
44int nusers; /* number of users logged in now */
45char * sel_user; /* login of particular user selected */
76238d88 46time_t now; /* the current time of day */
a43be079
SL
47struct timeval boottime;
48time_t uptime; /* time of last reboot & elapsed time since */
76238d88 49struct utmp utmp;
134b296f
MT
50struct winsize ws;
51int sortidle; /* sort bu idle time */
52
53
54/*
55 * One of these per active utmp entry.
56 */
57struct 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
66struct nlist nl[] = {
67 { "_boottime" },
68#define X_BOOTTIME 0
958d0e58
KB
69#if defined(hp300)
70 { "_cn_tty" },
71#define X_CNTTY 1
72#endif
134b296f
MT
73 { "" },
74};
75
ee8a3c9d 76#define USAGE "[ -hi ] [ user ]"
134b296f 77#define usage() fprintf(stderr, "usage: %s: %s\n", program, USAGE)
76238d88
BJ
78
79main(argc, argv)
80 char **argv;
81{
134b296f 82 register int i;
3b8dff21 83 struct winsize win;
134b296f
MT
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;
12bed5d4 92 char *attime();
76238d88 93
134b296f
MT
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
ee8a3c9d 103 while ((ch = getopt(argc, argv, "hiflsuw")) != EOF)
134b296f
MT
104 switch((char)ch) {
105 case 'h':
106 header = 0;
107 break;
ee8a3c9d 108 case 'i':
134b296f
MT
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);
76238d88 119 }
134b296f
MT
120 argc -= optind;
121 argv += optind;
122 if (argc == 1) {
123 sel_user = argv[0];
124 argv++, argc--;
76238d88 125 }
134b296f
MT
126 if (argc) {
127 usage();
76238d88
BJ
128 exit(1);
129 }
130
134b296f
MT
131 if (header && kvm_nlist(nl) != 0) {
132 error("can't get namelist");
133 exit (1);
be7adcb6 134 }
80b96391 135 time(&now);
134b296f
MT
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;
958d0e58
KB
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
134b296f
MT
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;
76238d88 185
134b296f
MT
186 /*
187 * Print time of day
188 */
189 fputs(attime(&now), stdout);
76238d88
BJ
190 /*
191 * Print how long system has been up.
a43be079 192 * (Found by looking for "boottime" in kernel)
76238d88 193 */
134b296f
MT
194 (void)kvm_read((off_t)nl[X_BOOTTIME].n_value, &boottime,
195 sizeof (boottime));
a43be079 196 uptime = now - boottime.tv_sec;
baaa0078 197 uptime += 30;
76238d88
BJ
198 days = uptime / (60*60*24);
199 uptime %= (60*60*24);
200 hrs = uptime / (60*60);
201 uptime %= (60*60);
baaa0078 202 mins = uptime / 60;
76238d88
BJ
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 */
fddf7972 217 printf(" %d user%s", nusers, nusers>1?"s":"");
76238d88
BJ
218
219 /*
220 * Print 1, 5, and 15 minute load averages.
76238d88
BJ
221 */
222 printf(", load average:");
07c23b0d 223 (void)getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0]));
76238d88
BJ
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");
134b296f 230 if (wcmd == 0) /* if uptime(1) then done */
76238d88 231 exit(0);
134b296f
MT
232#define HEADER "USER TTY FROM LOGIN@ IDLE WHAT\n"
233#define WUSED (sizeof (HEADER) - sizeof ("WHAT\n"))
234 printf(HEADER);
76238d88
BJ
235 }
236
7a5744ec 237 while ((p = kvm_nextproc()) != NULL) {
134b296f
MT
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;
76238d88
BJ
249 }
250 }
76238d88 251 }
134b296f
MT
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) {
12bed5d4 262 ep->args = strdup(kvm_getargs(ep->proc, kvm_getu(ep->proc)));
134b296f
MT
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 }
76238d88
BJ
299}
300
134b296f
MT
301struct stat *
302ttystat(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);
76238d88 309
134b296f 310 return (&statbuf);
76238d88
BJ
311}
312
76238d88 313/*
80b96391 314 * prttime prints a time in hours and minutes or minutes and seconds.
76238d88
BJ
315 * The character string tail is printed at the end, obvious
316 * strings to pass are "", " ", or "am".
317 */
318prttime(tim, tail)
319 time_t tim;
320 char *tail;
321{
76238d88
BJ
322
323 if (tim >= 60) {
891d0483 324 printf(" %2d:", tim/60);
80b96391
MK
325 tim %= 60;
326 printf("%02d", tim);
891d0483 327 } else if (tim >= 0)
80b96391 328 printf(" %2d", tim);
76238d88
BJ
329 printf("%s", tail);
330}
331
134b296f 332#include <varargs.h>
76238d88 333
134b296f
MT
334warning(va_alist)
335 va_dcl
76238d88 336{
134b296f
MT
337 char *fmt;
338 va_list ap;
339
340 fprintf(stderr, "%s: warning: ", program);
341 va_start(ap);
342 fmt = va_arg(ap, char *);
343 (void) vfprintf(stderr, fmt, ap);
344 va_end(ap);
345 fprintf(stderr, "\n");
76238d88
BJ
346}
347
134b296f
MT
348error(va_alist)
349 va_dcl
76238d88 350{
134b296f
MT
351 char *fmt;
352 va_list ap;
353
354 fprintf(stderr, "%s: ", program);
355 va_start(ap);
356 fmt = va_arg(ap, char *);
357 (void) vfprintf(stderr, fmt, ap);
358 va_end(ap);
359 fprintf(stderr, "\n");
76238d88
BJ
360}
361
134b296f
MT
362syserror(va_alist)
363 va_dcl
655ebcd5 364{
134b296f
MT
365 char *fmt;
366 va_list ap;
367 extern errno;
368
369 fprintf(stderr, "%s: ", program);
370 va_start(ap);
371 fmt = va_arg(ap, char *);
372 (void) vfprintf(stderr, fmt, ap);
373 va_end(ap);
374 fprintf(stderr, ": %s\n", strerror(errno));
655ebcd5 375}