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