]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/w/w.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 static struct utmpx *utmp;
89 static struct winsize ws;
90 static kvm_t   *kd;
91 static time_t   now;            /* the current time of day */
92 static int      ttywidth;       /* width of tty */
93 static int      argwidth;       /* width of tty */
94 static int      header = 1;     /* true if -h flag: don't print heading */
95 static int      nflag;          /* true if -n flag: don't convert addrs */
96 static int      dflag;          /* true if -d flag: output debug info */
97 static int      sortidle;       /* sort by idle time */
98 int             use_ampm;       /* use AM/PM time */
99 static int      use_comma;      /* use comma as floats separator */
100 static char   **sel_users;      /* login array of particular users selected */
101
102 /*
103  * One of these per active utmp entry.
104  */
105 static struct entry {
106         struct  entry *next;
107         struct  utmpx utmp;
108         dev_t   tdev;                   /* dev_t of terminal */
109         time_t  idle;                   /* idle time of terminal in seconds */
110         struct  kinfo_proc *kp;         /* `most interesting' proc */
111         char    *args;                  /* arg list of interesting process */
112         struct  kinfo_proc *dkp;        /* debug option proc list */
113 } *ep, *ehead = NULL, **nextp = &ehead;
114
115 #define debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata)
116
117 #define W_DISPUSERSIZE  10
118 #define W_DISPLINESIZE  8
119 #define W_DISPHOSTSIZE  24
120
121 static void              pr_header(time_t *, int);
122 static struct stat      *ttystat(char *);
123 static void              usage(int);
124 static int               this_is_uptime(const char *s);
125
126 char *fmt_argv(char **, char *, char *, size_t);        /* ../../bin/ps/fmt.c */
127
128 int
129 main(int argc, char *argv[])
130 {
131         struct kinfo_proc *kp;
132         struct kinfo_proc *dkp;
133         struct stat *stp;
134         time_t touched;
135         int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid;
136         const char *memf, *nlistf, *p;
137         char *x_suffix;
138         char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
139         char fn[MAXHOSTNAMELEN];
140         char *dot;
141
142         (void)setlocale(LC_ALL, "");
143         use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
144         use_comma = (*nl_langinfo(RADIXCHAR) != ',');
145
146         /* Are we w(1) or uptime(1)? */
147         if (this_is_uptime(argv[0]) == 0) {
148                 wcmd = 0;
149                 p = "";
150         } else {
151                 wcmd = 1;
152                 p = "dhiflM:N:nsuw";
153         }
154
155         dropgid = 0;
156         memf = _PATH_DEVNULL;
157         nlistf = NULL;
158         while ((ch = getopt(argc, argv, p)) != -1)
159                 switch (ch) {
160                 case 'd':
161                         dflag = 1;
162                         break;
163                 case 'h':
164                         header = 0;
165                         break;
166                 case 'i':
167                         sortidle = 1;
168                         break;
169                 case 'M':
170                         header = 0;
171                         memf = optarg;
172                         dropgid = 1;
173                         break;
174                 case 'N':
175                         nlistf = optarg;
176                         dropgid = 1;
177                         break;
178                 case 'n':
179                         nflag = 1;
180                         break;
181                 case 'f': case 'l': case 's': case 'u': case 'w':
182                         warnx("[-flsuw] no longer supported");
183                         /* FALLTHROUGH */
184                 case '?':
185                 default:
186                         usage(wcmd);
187                 }
188         argc -= optind;
189         argv += optind;
190
191         if (!(_res.options & RES_INIT))
192                 res_init();
193         _res.retrans = 2;       /* resolver timeout to 2 seconds per try */
194         _res.retry = 1;         /* only try once.. */
195
196         /*
197          * Discard setgid privileges if not the running kernel so that bad
198          * guys can't print interesting stuff from kernel memory.
199          */
200         if (dropgid)
201                 setgid(getgid());
202
203         if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
204                 errx(1, "%s", errbuf);
205
206         (void)time(&now);
207
208         if (*argv)
209                 sel_users = argv;
210
211         setutxent();
212         for (nusers = 0; (utmp = getutxent()) != NULL;) {
213                 if (utmp->ut_type != USER_PROCESS)
214                         continue;
215                 if (!(stp = ttystat(utmp->ut_line)))
216                         continue;       /* corrupted record */
217                 ++nusers;
218                 if (wcmd == 0)
219                         continue;
220                 if (sel_users) {
221                         int usermatch;
222                         char **user;
223
224                         usermatch = 0;
225                         for (user = sel_users; !usermatch && *user; user++)
226                                 if (!strcmp(utmp->ut_user, *user))
227                                         usermatch = 1;
228                         if (!usermatch)
229                                 continue;
230                 }
231                 if ((ep = calloc(1, sizeof(struct entry))) == NULL)
232                         errx(1, "calloc");
233                 *nextp = ep;
234                 nextp = &ep->next;
235                 memmove(&ep->utmp, utmp, sizeof *utmp);
236                 ep->tdev = stp->st_rdev;
237                 /*
238                  * If this is the console device, attempt to ascertain
239                  * the true console device dev_t.
240                  */
241                 if (ep->tdev == 0) {
242                         size_t size;
243
244                         size = sizeof(dev_t);
245                         (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0);
246                 }
247                 touched = stp->st_atime;
248                 if (touched < ep->utmp.ut_tv.tv_sec) {
249                         /* tty untouched since before login */
250                         touched = ep->utmp.ut_tv.tv_sec;
251                 }
252                 if ((ep->idle = now - touched) < 0)
253                         ep->idle = 0;
254         }
255         endutxent();
256
257         if (header || wcmd == 0) {
258                 pr_header(&now, nusers);
259                 if (wcmd == 0) {
260                         (void)kvm_close(kd);
261                         exit(0);
262                 }
263
264 #define HEADER_USER             "USER"
265 #define HEADER_TTY              "TTY"
266 #define HEADER_FROM             "FROM"
267 #define HEADER_LOGIN_IDLE       "LOGIN@  IDLE "
268 #define HEADER_WHAT             "WHAT\n"
269 #define WUSED  (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \
270                 sizeof(HEADER_LOGIN_IDLE) + 3)  /* header width incl. spaces */ 
271                 (void)printf("%-*.*s %-*.*s %-*.*s  %s", 
272                                 W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER,
273                                 W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY,
274                                 W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM,
275                                 HEADER_LOGIN_IDLE HEADER_WHAT);
276         }
277
278         if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL)
279                 err(1, "%s", kvm_geterr(kd));
280         for (i = 0; i < nentries; i++, kp++) {
281                 if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB ||
282                     kp->ki_tdev == NODEV)
283                         continue;
284                 for (ep = ehead; ep != NULL; ep = ep->next) {
285                         if (ep->tdev == kp->ki_tdev) {
286                                 /*
287                                  * proc is associated with this terminal
288                                  */
289                                 if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) {
290                                         /*
291                                          * Proc is 'most interesting'
292                                          */
293                                         if (proc_compare(ep->kp, kp))
294                                                 ep->kp = kp;
295                                 }
296                                 /*
297                                  * Proc debug option info; add to debug
298                                  * list using kinfo_proc ki_spare[0]
299                                  * as next pointer; ptr to ptr avoids the
300                                  * ptr = long assumption.
301                                  */
302                                 dkp = ep->dkp;
303                                 ep->dkp = kp;
304                                 debugproc(kp) = dkp;
305                         }
306                 }
307         }
308         if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
309              ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
310              ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
311                ttywidth = 79;
312         else
313                ttywidth = ws.ws_col - 1;
314         argwidth = ttywidth - WUSED;
315         if (argwidth < 4)
316                 argwidth = 8;
317         for (ep = ehead; ep != NULL; ep = ep->next) {
318                 if (ep->kp == NULL) {
319                         ep->args = strdup("-");
320                         continue;
321                 }
322                 ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
323                     ep->kp->ki_comm, NULL, MAXCOMLEN);
324                 if (ep->args == NULL)
325                         err(1, NULL);
326         }
327         /* sort by idle time */
328         if (sortidle && ehead != NULL) {
329                 struct entry *from, *save;
330
331                 from = ehead;
332                 ehead = NULL;
333                 while (from != NULL) {
334                         for (nextp = &ehead;
335                             (*nextp) && from->idle >= (*nextp)->idle;
336                             nextp = &(*nextp)->next)
337                                 continue;
338                         save = from;
339                         from = from->next;
340                         save->next = *nextp;
341                         *nextp = save;
342                 }
343         }
344
345         for (ep = ehead; ep != NULL; ep = ep->next) {
346                 struct addrinfo hints, *res;
347                 struct sockaddr_storage ss;
348                 struct sockaddr *sa = (struct sockaddr *)&ss;
349                 struct sockaddr_in *lsin = (struct sockaddr_in *)&ss;
350                 struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss;
351                 time_t t;
352                 int isaddr;
353
354                 p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
355                 if ((x_suffix = strrchr(p, ':')) != NULL) {
356                         if ((dot = strchr(x_suffix, '.')) != NULL &&
357                             strchr(dot+1, '.') == NULL)
358                                 *x_suffix++ = '\0';
359                         else
360                                 x_suffix = NULL;
361                 }
362
363                 isaddr = 0;
364                 memset(&ss, '\0', sizeof(ss));
365                 if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) {
366                         lsin6->sin6_len = sizeof(*lsin6);
367                         lsin6->sin6_family = AF_INET6;
368                         isaddr = 1;
369                 } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) {
370                         lsin->sin_len = sizeof(*lsin);
371                         lsin->sin_family = AF_INET;
372                         isaddr = 1;
373                 }
374                 if (!nflag) {
375                         /* Attempt to change an IP address into a name */
376                         if (isaddr && realhostname_sa(fn, sizeof(fn), sa,
377                             sa->sa_len) == HOSTNAME_FOUND)
378                                 p = fn;
379                 } else if (!isaddr) {
380                         /*
381                          * If a host has only one A/AAAA RR, change a
382                          * name into an IP address
383                          */
384                         memset(&hints, 0, sizeof(hints));
385                         hints.ai_flags = AI_PASSIVE;
386                         hints.ai_family = AF_UNSPEC;
387                         hints.ai_socktype = SOCK_STREAM;
388                         if (getaddrinfo(p, NULL, &hints, &res) == 0) {
389                                 if (res->ai_next == NULL &&
390                                     getnameinfo(res->ai_addr, res->ai_addrlen,
391                                         fn, sizeof(fn), NULL, 0,
392                                         NI_NUMERICHOST) == 0)
393                                         p = fn;
394                                 freeaddrinfo(res);
395                         }
396                 }
397
398                 if (x_suffix) {
399                         (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix);
400                         p = buf;
401                 }
402                 if (dflag) {
403                         for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) {
404                                 const char *ptr;
405
406                                 ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth),
407                                     dkp->ki_comm, NULL, MAXCOMLEN);
408                                 if (ptr == NULL)
409                                         ptr = "-";
410                                 (void)printf("\t\t%-9d %s\n",
411                                     dkp->ki_pid, ptr);
412                         }
413                 }
414                 (void)printf("%-*.*s %-*.*s %-*.*s ",
415                     W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user,
416                     W_DISPLINESIZE, W_DISPLINESIZE,
417                     *ep->utmp.ut_line ?
418                     (strncmp(ep->utmp.ut_line, "tty", 3) &&
419                     strncmp(ep->utmp.ut_line, "cua", 3) ?
420                     ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-",
421                     W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-");
422                 t = ep->utmp.ut_tv.tv_sec;
423                 longattime = pr_attime(&t, &now);
424                 longidle = pr_idle(ep->idle);
425                 (void)printf("%.*s\n", argwidth - longidle - longattime,
426                     ep->args);
427         }
428         (void)kvm_close(kd);
429         exit(0);
430 }
431
432 static void
433 pr_header(time_t *nowp, int nusers)
434 {
435         double avenrun[3];
436         time_t uptime;
437         struct timespec tp;
438         int days, hrs, i, mins, secs;
439         char buf[256];
440
441         /*
442          * Print time of day.
443          */
444         if (strftime(buf, sizeof(buf),
445             use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0)
446                 (void)printf("%s ", buf);
447         /*
448          * Print how long system has been up.
449          */
450         if (clock_gettime(CLOCK_UPTIME, &tp) != -1) {
451                 uptime = tp.tv_sec;
452                 if (uptime > 60)
453                         uptime += 30;
454                 days = uptime / 86400;
455                 uptime %= 86400;
456                 hrs = uptime / 3600;
457                 uptime %= 3600;
458                 mins = uptime / 60;
459                 secs = uptime % 60;
460                 (void)printf(" up");
461                 if (days > 0)
462                         (void)printf(" %d day%s,", days, days > 1 ? "s" : "");
463                 if (hrs > 0 && mins > 0)
464                         (void)printf(" %2d:%02d,", hrs, mins);
465                 else if (hrs > 0)
466                         (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : "");
467                 else if (mins > 0)
468                         (void)printf(" %d min%s,", mins, mins > 1 ? "s" : "");
469                 else
470                         (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : "");
471         }
472
473         /* Print number of users logged in to system */
474         (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s");
475
476         /*
477          * Print 1, 5, and 15 minute load averages.
478          */
479         if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
480                 (void)printf(", no load average information available\n");
481         else {
482                 (void)printf(", load averages:");
483                 for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) {
484                         if (use_comma && i > 0)
485                                 (void)printf(",");
486                         (void)printf(" %.2f", avenrun[i]);
487                 }
488                 (void)printf("\n");
489         }
490 }
491
492 static struct stat *
493 ttystat(char *line)
494 {
495         static struct stat sb;
496         char ttybuf[MAXPATHLEN];
497
498         (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
499         if (stat(ttybuf, &sb) == 0 && S_ISCHR(sb.st_mode)) {
500                 return (&sb);
501         } else
502                 return (NULL);
503 }
504
505 static void
506 usage(int wcmd)
507 {
508         if (wcmd)
509                 (void)fprintf(stderr,
510                     "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
511         else
512                 (void)fprintf(stderr, "usage: uptime\n");
513         exit(1);
514 }
515
516 static int 
517 this_is_uptime(const char *s)
518 {
519         const char *u;
520
521         if ((u = strrchr(s, '/')) != NULL)
522                 ++u;
523         else
524                 u = s;
525         if (strcmp(u, "uptime") == 0)
526                 return (0);
527         return (-1);
528 }