]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.bin/w/w.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.bin / w / w.c
1 /*-
2  * Copyright (c) 1980, 1991, 1993, 1994
3  *      The Regents of the University of California.  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  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31
32 __FBSDID("$FreeBSD$");
33
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
37         The Regents of the University of California.  All rights reserved.\n";
38 #endif
39
40 #ifndef lint
41 static const char sccsid[] = "@(#)w.c   8.4 (Berkeley) 4/16/94";
42 #endif
43
44 /*
45  * w - print system status (who and what)
46  *
47  * This program is similar to the systat command on Tenex/Tops 10/20
48  *
49  */
50 #include <sys/param.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 #include <sys/sysctl.h>
54 #include <sys/proc.h>
55 #include <sys/user.h>
56 #include <sys/ioctl.h>
57 #include <sys/socket.h>
58 #include <sys/tty.h>
59
60 #include <machine/cpu.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #include <arpa/nameser.h>
64
65 #include <ctype.h>
66 #include <err.h>
67 #include <errno.h>
68 #include <fcntl.h>
69 #include <kvm.h>
70 #include <langinfo.h>
71 #include <libutil.h>
72 #include <limits.h>
73 #include <locale.h>
74 #include <netdb.h>
75 #include <nlist.h>
76 #include <paths.h>
77 #include <resolv.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <timeconv.h>
82 #include <unistd.h>
83 #include <utmpx.h>
84 #include <vis.h>
85
86 #include "extern.h"
87
88 struct timeval  boottime;
89 struct utmpx   *utmp;
90 struct winsize  ws;
91 kvm_t          *kd;
92 time_t          now;            /* the current time of day */
93 int             ttywidth;       /* width of tty */
94 int             argwidth;       /* width of tty */
95 int             header = 1;     /* true if -h flag: don't print heading */
96 int             nflag;          /* true if -n flag: don't convert addrs */
97 int             dflag;          /* true if -d flag: output debug info */
98 int             sortidle;       /* sort by idle time */
99 int             use_ampm;       /* use AM/PM time */
100 int             use_comma;      /* use comma as floats separator */
101 char          **sel_users;      /* login array of particular users selected */
102
103 /*
104  * One of these per active utmp entry.
105  */
106 struct  entry {
107         struct  entry *next;
108         struct  utmpx utmp;
109         dev_t   tdev;                   /* dev_t of terminal */
110         time_t  idle;                   /* idle time of terminal in seconds */
111         struct  kinfo_proc *kp;         /* `most interesting' proc */
112         char    *args;                  /* arg list of interesting process */
113         struct  kinfo_proc *dkp;        /* debug option proc list */
114 } *ep, *ehead = NULL, **nextp = &ehead;
115
116 #define debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata)
117
118 #define W_DISPUSERSIZE  10
119 #define W_DISPLINESIZE  8
120 #define W_DISPHOSTSIZE  24
121
122 static void              pr_header(time_t *, int);
123 static struct stat      *ttystat(char *);
124 static void              usage(int);
125 static int               this_is_uptime(const char *s);
126
127 char *fmt_argv(char **, char *, char *, size_t);        /* ../../bin/ps/fmt.c */
128
129 int
130 main(int argc, char *argv[])
131 {
132         struct kinfo_proc *kp;
133         struct kinfo_proc *dkp;
134         struct stat *stp;
135         time_t touched;
136         int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid;
137         const char *memf, *nlistf, *p;
138         char *x_suffix;
139         char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
140         char fn[MAXHOSTNAMELEN];
141         char *dot;
142
143         (void)setlocale(LC_ALL, "");
144         use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
145         use_comma = (*nl_langinfo(RADIXCHAR) != ',');
146
147         /* Are we w(1) or uptime(1)? */
148         if (this_is_uptime(argv[0]) == 0) {
149                 wcmd = 0;
150                 p = "";
151         } else {
152                 wcmd = 1;
153                 p = "dhiflM:N:nsuw";
154         }
155
156         dropgid = 0;
157         memf = _PATH_DEVNULL;
158         nlistf = NULL;
159         while ((ch = getopt(argc, argv, p)) != -1)
160                 switch (ch) {
161                 case 'd':
162                         dflag = 1;
163                         break;
164                 case 'h':
165                         header = 0;
166                         break;
167                 case 'i':
168                         sortidle = 1;
169                         break;
170                 case 'M':
171                         header = 0;
172                         memf = optarg;
173                         dropgid = 1;
174                         break;
175                 case 'N':
176                         nlistf = optarg;
177                         dropgid = 1;
178                         break;
179                 case 'n':
180                         nflag = 1;
181                         break;
182                 case 'f': case 'l': case 's': case 'u': case 'w':
183                         warnx("[-flsuw] no longer supported");
184                         /* FALLTHROUGH */
185                 case '?':
186                 default:
187                         usage(wcmd);
188                 }
189         argc -= optind;
190         argv += optind;
191
192         if (!(_res.options & RES_INIT))
193                 res_init();
194         _res.retrans = 2;       /* resolver timeout to 2 seconds per try */
195         _res.retry = 1;         /* only try once.. */
196
197         /*
198          * Discard setgid privileges if not the running kernel so that bad
199          * guys can't print interesting stuff from kernel memory.
200          */
201         if (dropgid)
202                 setgid(getgid());
203
204         if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
205                 errx(1, "%s", errbuf);
206
207         (void)time(&now);
208
209         if (*argv)
210                 sel_users = argv;
211
212         setutxent();
213         for (nusers = 0; (utmp = getutxent()) != NULL;) {
214                 if (utmp->ut_type != USER_PROCESS)
215                         continue;
216                 if (!(stp = ttystat(utmp->ut_line)))
217                         continue;       /* corrupted record */
218                 ++nusers;
219                 if (wcmd == 0)
220                         continue;
221                 if (sel_users) {
222                         int usermatch;
223                         char **user;
224
225                         usermatch = 0;
226                         for (user = sel_users; !usermatch && *user; user++)
227                                 if (!strcmp(utmp->ut_user, *user))
228                                         usermatch = 1;
229                         if (!usermatch)
230                                 continue;
231                 }
232                 if ((ep = calloc(1, sizeof(struct entry))) == NULL)
233                         errx(1, "calloc");
234                 *nextp = ep;
235                 nextp = &ep->next;
236                 memmove(&ep->utmp, utmp, sizeof *utmp);
237                 ep->tdev = stp->st_rdev;
238                 /*
239                  * If this is the console device, attempt to ascertain
240                  * the true console device dev_t.
241                  */
242                 if (ep->tdev == 0) {
243                         size_t size;
244
245                         size = sizeof(dev_t);
246                         (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0);
247                 }
248                 touched = stp->st_atime;
249                 if (touched < ep->utmp.ut_tv.tv_sec) {
250                         /* tty untouched since before login */
251                         touched = ep->utmp.ut_tv.tv_sec;
252                 }
253                 if ((ep->idle = now - touched) < 0)
254                         ep->idle = 0;
255         }
256         endutxent();
257
258         if (header || wcmd == 0) {
259                 pr_header(&now, nusers);
260                 if (wcmd == 0) {
261                         (void)kvm_close(kd);
262                         exit(0);
263                 }
264
265 #define HEADER_USER             "USER"
266 #define HEADER_TTY              "TTY"
267 #define HEADER_FROM             "FROM"
268 #define HEADER_LOGIN_IDLE       "LOGIN@  IDLE "
269 #define HEADER_WHAT             "WHAT\n"
270 #define WUSED  (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \
271                 sizeof(HEADER_LOGIN_IDLE) + 3)  /* header width incl. spaces */ 
272                 (void)printf("%-*.*s %-*.*s %-*.*s  %s", 
273                                 W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER,
274                                 W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY,
275                                 W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM,
276                                 HEADER_LOGIN_IDLE HEADER_WHAT);
277         }
278
279         if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL)
280                 err(1, "%s", kvm_geterr(kd));
281         for (i = 0; i < nentries; i++, kp++) {
282                 if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB ||
283                     kp->ki_tdev == NODEV)
284                         continue;
285                 for (ep = ehead; ep != NULL; ep = ep->next) {
286                         if (ep->tdev == kp->ki_tdev) {
287                                 /*
288                                  * proc is associated with this terminal
289                                  */
290                                 if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) {
291                                         /*
292                                          * Proc is 'most interesting'
293                                          */
294                                         if (proc_compare(ep->kp, kp))
295                                                 ep->kp = kp;
296                                 }
297                                 /*
298                                  * Proc debug option info; add to debug
299                                  * list using kinfo_proc ki_spare[0]
300                                  * as next pointer; ptr to ptr avoids the
301                                  * ptr = long assumption.
302                                  */
303                                 dkp = ep->dkp;
304                                 ep->dkp = kp;
305                                 debugproc(kp) = dkp;
306                         }
307                 }
308         }
309         if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
310              ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
311              ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
312                ttywidth = 79;
313         else
314                ttywidth = ws.ws_col - 1;
315         argwidth = ttywidth - WUSED;
316         if (argwidth < 4)
317                 argwidth = 8;
318         for (ep = ehead; ep != NULL; ep = ep->next) {
319                 if (ep->kp == NULL) {
320                         ep->args = strdup("-");
321                         continue;
322                 }
323                 ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
324                     ep->kp->ki_comm, NULL, MAXCOMLEN);
325                 if (ep->args == NULL)
326                         err(1, NULL);
327         }
328         /* sort by idle time */
329         if (sortidle && ehead != NULL) {
330                 struct entry *from, *save;
331
332                 from = ehead;
333                 ehead = NULL;
334                 while (from != NULL) {
335                         for (nextp = &ehead;
336                             (*nextp) && from->idle >= (*nextp)->idle;
337                             nextp = &(*nextp)->next)
338                                 continue;
339                         save = from;
340                         from = from->next;
341                         save->next = *nextp;
342                         *nextp = save;
343                 }
344         }
345
346         for (ep = ehead; ep != NULL; ep = ep->next) {
347                 struct addrinfo hints, *res;
348                 struct sockaddr_storage ss;
349                 struct sockaddr *sa = (struct sockaddr *)&ss;
350                 struct sockaddr_in *lsin = (struct sockaddr_in *)&ss;
351                 struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss;
352                 time_t t;
353                 int isaddr;
354
355                 p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
356                 if ((x_suffix = strrchr(p, ':')) != NULL) {
357                         if ((dot = strchr(x_suffix, '.')) != NULL &&
358                             strchr(dot+1, '.') == NULL)
359                                 *x_suffix++ = '\0';
360                         else
361                                 x_suffix = NULL;
362                 }
363
364                 isaddr = 0;
365                 memset(&ss, '\0', sizeof(ss));
366                 if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) {
367                         lsin6->sin6_len = sizeof(*lsin6);
368                         lsin6->sin6_family = AF_INET6;
369                         isaddr = 1;
370                 } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) {
371                         lsin->sin_len = sizeof(*lsin);
372                         lsin->sin_family = AF_INET;
373                         isaddr = 1;
374                 }
375                 if (!nflag) {
376                         /* Attempt to change an IP address into a name */
377                         if (isaddr && realhostname_sa(fn, sizeof(fn), sa,
378                             sa->sa_len) == HOSTNAME_FOUND)
379                                 p = fn;
380                 } else if (!isaddr) {
381                         /*
382                          * If a host has only one A/AAAA RR, change a
383                          * name into an IP address
384                          */
385                         memset(&hints, 0, sizeof(hints));
386                         hints.ai_flags = AI_PASSIVE;
387                         hints.ai_family = AF_UNSPEC;
388                         hints.ai_socktype = SOCK_STREAM;
389                         if (getaddrinfo(p, NULL, &hints, &res) == 0) {
390                                 if (res->ai_next == NULL &&
391                                     getnameinfo(res->ai_addr, res->ai_addrlen,
392                                         fn, sizeof(fn), NULL, 0,
393                                         NI_NUMERICHOST) == 0)
394                                         p = fn;
395                                 freeaddrinfo(res);
396                         }
397                 }
398
399                 if (x_suffix) {
400                         (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix);
401                         p = buf;
402                 }
403                 if (dflag) {
404                         for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) {
405                                 const char *ptr;
406
407                                 ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth),
408                                     dkp->ki_comm, NULL, MAXCOMLEN);
409                                 if (ptr == NULL)
410                                         ptr = "-";
411                                 (void)printf("\t\t%-9d %s\n",
412                                     dkp->ki_pid, ptr);
413                         }
414                 }
415                 (void)printf("%-*.*s %-*.*s %-*.*s ",
416                     W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user,
417                     W_DISPLINESIZE, W_DISPLINESIZE,
418                     *ep->utmp.ut_line ?
419                     (strncmp(ep->utmp.ut_line, "tty", 3) &&
420                     strncmp(ep->utmp.ut_line, "cua", 3) ?
421                     ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-",
422                     W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-");
423                 t = ep->utmp.ut_tv.tv_sec;
424                 longattime = pr_attime(&t, &now);
425                 longidle = pr_idle(ep->idle);
426                 (void)printf("%.*s\n", argwidth - longidle - longattime,
427                     ep->args);
428         }
429         (void)kvm_close(kd);
430         exit(0);
431 }
432
433 static void
434 pr_header(time_t *nowp, int nusers)
435 {
436         double avenrun[3];
437         time_t uptime;
438         struct timespec tp;
439         int days, hrs, i, mins, secs;
440         char buf[256];
441
442         /*
443          * Print time of day.
444          */
445         if (strftime(buf, sizeof(buf),
446             use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0)
447                 (void)printf("%s ", buf);
448         /*
449          * Print how long system has been up.
450          */
451         if (clock_gettime(CLOCK_UPTIME, &tp) != -1) {
452                 uptime = tp.tv_sec;
453                 if (uptime > 60)
454                         uptime += 30;
455                 days = uptime / 86400;
456                 uptime %= 86400;
457                 hrs = uptime / 3600;
458                 uptime %= 3600;
459                 mins = uptime / 60;
460                 secs = uptime % 60;
461                 (void)printf(" up");
462                 if (days > 0)
463                         (void)printf(" %d day%s,", days, days > 1 ? "s" : "");
464                 if (hrs > 0 && mins > 0)
465                         (void)printf(" %2d:%02d,", hrs, mins);
466                 else if (hrs > 0)
467                         (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : "");
468                 else if (mins > 0)
469                         (void)printf(" %d min%s,", mins, mins > 1 ? "s" : "");
470                 else
471                         (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : "");
472         }
473
474         /* Print number of users logged in to system */
475         (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s");
476
477         /*
478          * Print 1, 5, and 15 minute load averages.
479          */
480         if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
481                 (void)printf(", no load average information available\n");
482         else {
483                 (void)printf(", load averages:");
484                 for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) {
485                         if (use_comma && i > 0)
486                                 (void)printf(",");
487                         (void)printf(" %.2f", avenrun[i]);
488                 }
489                 (void)printf("\n");
490         }
491 }
492
493 static struct stat *
494 ttystat(char *line)
495 {
496         static struct stat sb;
497         char ttybuf[MAXPATHLEN];
498
499         (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
500         if (stat(ttybuf, &sb) == 0 && S_ISCHR(sb.st_mode)) {
501                 return (&sb);
502         } else
503                 return (NULL);
504 }
505
506 static void
507 usage(int wcmd)
508 {
509         if (wcmd)
510                 (void)fprintf(stderr,
511                     "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
512         else
513                 (void)fprintf(stderr, "usage: uptime\n");
514         exit(1);
515 }
516
517 static int 
518 this_is_uptime(const char *s)
519 {
520         const char *u;
521
522         if ((u = strrchr(s, '/')) != NULL)
523                 ++u;
524         else
525                 u = s;
526         if (strcmp(u, "uptime") == 0)
527                 return (0);
528         return (-1);
529 }