w.c revision 1.10 1 /*-
2 * Copyright (c) 1980, 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 char copyright[] =
36 "@(#) Copyright (c) 1980, 1991 The Regents of the University of California.\n\
37 All rights reserved.\n";
38 #endif /* not lint */
39
40 #ifndef lint
41 /*static char sccsid[] = "from: @(#)w.c 5.29 (Berkeley) 4/23/91";*/
42 static char rcsid[] = "$Id: w.c,v 1.10 1994/05/05 02:08:58 cgd Exp $";
43 #endif /* not lint */
44
45 /*
46 * w - print system status (who and what)
47 *
48 * This program is similar to the systat command on Tenex/Tops 10/20
49 *
50 */
51 #include <sys/param.h>
52 #include <utmp.h>
53 #include <sys/time.h>
54 #include <sys/stat.h>
55 #include <sys/proc.h>
56 #include <sys/user.h>
57 #include <sys/ioctl.h>
58 #include <sys/tty.h>
59 #include <nlist.h>
60 #include <kvm.h>
61 #include <ctype.h>
62 #include <paths.h>
63 #include <string.h>
64 #include <stdio.h>
65 #include <vis.h>
66
67 #ifdef P_PPWAIT
68 #define NEWVM
69 #endif
70 #ifndef NEWVM
71 #include <machine/pte.h>
72 #include <sys/vm.h>
73 #endif
74
75 char *program;
76 int ttywidth; /* width of tty */
77 int argwidth; /* width of tty */
78 int header = 1; /* true if -h flag: don't print heading */
79 int wcmd = 1; /* true if this is w(1), and not uptime(1) */
80 int nusers; /* number of users logged in now */
81 char * sel_user; /* login of particular user selected */
82 time_t now; /* the current time of day */
83 struct timeval boottime;
84 time_t uptime; /* time of last reboot & elapsed time since */
85 struct utmp utmp;
86 struct winsize ws;
87 int sortidle; /* sort bu idle time */
88
89
90 /*
91 * One of these per active utmp entry.
92 */
93 struct entry {
94 struct entry *next;
95 struct utmp utmp;
96 dev_t tdev; /* dev_t of terminal */
97 int idle; /* idle time of terminal in minutes */
98 struct proc *proc; /* list of procs in foreground */
99 char *args; /* arg list of interesting process */
100 } *ep, *ehead = NULL, **nextp = &ehead;
101
102 struct nlist nl[] = {
103 { "_boottime" },
104 #define X_BOOTTIME 0
105 { "" },
106 };
107
108 #define USAGE "[ -hi ] [ user ]"
109 #define usage() fprintf(stderr, "usage: %s: %s\n", program, USAGE)
110
111 main(argc, argv)
112 int argc;
113 char **argv;
114 {
115 register int i;
116 struct winsize win;
117 register struct proc *p;
118 struct eproc *e;
119 struct stat *stp, *ttystat();
120 FILE *ut;
121 char *cp;
122 char *vis_args;
123 int ch;
124 extern char *optarg;
125 extern int optind;
126 char *attime();
127
128 program = argv[0];
129 /*
130 * are we w(1) or uptime(1)
131 */
132 if ((cp = rindex(program, '/')) || *(cp = program) == '-')
133 cp++;
134 if (*cp == 'u')
135 wcmd = 0;
136
137 while ((ch = getopt(argc, argv, "hiflsuw")) != EOF)
138 switch((char)ch) {
139 case 'h':
140 header = 0;
141 break;
142 case 'i':
143 sortidle++;
144 break;
145 case 'f': case 'l': case 's': case 'u': case 'w':
146 error("[-flsuw] no longer supported");
147 usage();
148 exit(1);
149 case '?':
150 default:
151 usage();
152 exit(1);
153 }
154 argc -= optind;
155 argv += optind;
156
157 if (*argv)
158 sel_user = *argv;
159
160 if (header && kvm_nlist(nl) != 0) {
161 error("can't get namelist");
162 exit (1);
163 }
164 time(&now);
165 ut = fopen(_PATH_UTMP, "r");
166 while (fread(&utmp, sizeof(utmp), 1, ut)) {
167 if (utmp.ut_name[0] == '\0')
168 continue;
169 nusers++;
170 if (wcmd == 0 || (sel_user &&
171 strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0))
172 continue;
173 if ((ep = (struct entry *)
174 calloc(1, sizeof (struct entry))) == NULL) {
175 error("out of memory");
176 exit(1);
177 }
178 *nextp = ep;
179 nextp = &(ep->next);
180 bcopy(&utmp, &(ep->utmp), sizeof (struct utmp));
181 stp = ttystat(ep->utmp.ut_line);
182 ep->tdev = stp->st_rdev;
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((void *)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 & P_CONTROLT) == 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 if (!ep->proc) {
270 ep->args = NULL;
271 continue;
272 }
273 ep->args = strdup(kvm_getargs(ep->proc, kvm_getu(ep->proc)));
274 if (ep->args == NULL) {
275 error("out of memory");
276 exit(1);
277 }
278 }
279 /* sort by idle time */
280 if (sortidle && ehead != NULL) {
281 struct entry *from = ehead, *save;
282
283 ehead = NULL;
284 while (from != NULL) {
285 for (nextp = &ehead;
286 (*nextp) && from->idle >= (*nextp)->idle;
287 nextp = &(*nextp)->next)
288 ;
289 save = from;
290 from = from->next;
291 save->next = *nextp;
292 *nextp = save;
293 }
294 }
295
296 if ((vis_args = (char *)malloc(argwidth * 4 + 1)) == NULL) {
297 error("out of memory");
298 exit(1);
299 }
300 for (ep = ehead; ep != NULL; ep = ep->next) {
301 printf("%-*.*s %-2.2s %-*.*s %s",
302 UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
303 strncmp(ep->utmp.ut_line, "tty", 3) == 0 ?
304 ep->utmp.ut_line+3 : ep->utmp.ut_line,
305 UT_HOSTSIZE, UT_HOSTSIZE, *ep->utmp.ut_host ?
306 ep->utmp.ut_host : "-",
307 attime(&ep->utmp.ut_time));
308 if (ep->idle >= 36 * 60)
309 printf(" %ddays ", (ep->idle + 12 * 60) / (24 * 60));
310 else if (ep->idle == 0)
311 printf(" - ");
312 else
313 prttime(ep->idle, " ");
314 if (ep->args)
315 strvisx(vis_args, ep->args,
316 (strlen(ep->args) > argwidth) ?
317 argwidth : strlen(ep->args),
318 VIS_TAB|VIS_NL|VIS_NOSLASH);
319 printf("%.*s\n", argwidth, ep->args ? vis_args : "-");
320 }
321 free(vis_args);
322
323 exit(0);
324 }
325
326 struct stat *
327 ttystat(line)
328 {
329 static struct stat statbuf;
330 char ttybuf[sizeof (_PATH_DEV) + UT_LINESIZE + 1];
331
332 sprintf(ttybuf, "%s/%.*s", _PATH_DEV, UT_LINESIZE, line);
333 (void) stat(ttybuf, &statbuf);
334
335 return (&statbuf);
336 }
337
338 /*
339 * prttime prints a time in hours and minutes or minutes and seconds.
340 * The character string tail is printed at the end, obvious
341 * strings to pass are "", " ", or "am".
342 */
343 prttime(tim, tail)
344 time_t tim;
345 char *tail;
346 {
347
348 if (tim >= 60)
349 printf(" %2d:%02d", tim/60, tim%60);
350 else if (tim >= 0)
351 printf(" %2d", tim);
352 printf("%s", tail);
353 }
354
355 #include <varargs.h>
356
357 error(va_alist)
358 va_dcl
359 {
360 char *fmt;
361 va_list ap;
362
363 fprintf(stderr, "%s: ", program);
364 va_start(ap);
365 fmt = va_arg(ap, char *);
366 (void) vfprintf(stderr, fmt, ap);
367 va_end(ap);
368 fprintf(stderr, "\n");
369 }
370