1 /* $NetBSD: w.c,v 1.18 1996/06/07 01:36:39 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 static char copyright[] = 38 "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 42 #ifndef lint 43 #if 0 44 static char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94"; 45 #else 46 static char rcsid[] = "$NetBSD: w.c,v 1.18 1996/06/07 01:36:39 thorpej Exp $"; 47 #endif 48 #endif /* not lint */ 49 50 /* 51 * w - print system status (who and what) 52 * 53 * This program is similar to the systat command on Tenex/Tops 10/20 54 * 55 */ 56 #include <sys/param.h> 57 #include <sys/time.h> 58 #include <sys/stat.h> 59 #include <sys/sysctl.h> 60 #include <sys/proc.h> 61 #include <sys/user.h> 62 #include <sys/ioctl.h> 63 #include <sys/socket.h> 64 #include <sys/tty.h> 65 66 #include <machine/cpu.h> 67 #include <netinet/in.h> 68 #include <arpa/inet.h> 69 70 #include <ctype.h> 71 #include <err.h> 72 #include <errno.h> 73 #include <fcntl.h> 74 #include <kvm.h> 75 #include <netdb.h> 76 #include <nlist.h> 77 #include <paths.h> 78 #include <stdio.h> 79 #include <stdlib.h> 80 #include <string.h> 81 #include <tzfile.h> 82 #include <unistd.h> 83 #include <utmp.h> 84 #include <vis.h> 85 86 #include "extern.h" 87 88 struct timeval boottime; 89 struct utmp utmp; 90 struct winsize ws; 91 kvm_t *kd; 92 time_t now; /* the current time of day */ 93 time_t uptime; /* time of last reboot & elapsed time since */ 94 int ttywidth; /* width of tty */ 95 int argwidth; /* width of tty */ 96 int header = 1; /* true if -h flag: don't print heading */ 97 int nflag; /* true if -n flag: don't convert addrs */ 98 int sortidle; /* sort bu idle time */ 99 char *sel_user; /* login of particular user selected */ 100 char domain[MAXHOSTNAMELEN]; 101 102 /* 103 * One of these per active utmp entry. 104 */ 105 struct entry { 106 struct entry *next; 107 struct utmp 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 } *ep, *ehead = NULL, **nextp = &ehead; 112 113 static void pr_args __P((struct kinfo_proc *)); 114 static void pr_header __P((time_t *, int)); 115 static struct stat 116 *ttystat __P((char *)); 117 static void usage __P((int)); 118 119 int 120 main(argc, argv) 121 int argc; 122 char **argv; 123 { 124 extern char *__progname; 125 struct kinfo_proc *kp; 126 struct hostent *hp; 127 struct stat *stp; 128 FILE *ut; 129 u_long l; 130 int ch, i, nentries, nusers, wcmd; 131 char *memf, *nlistf, *p, *x; 132 char buf[MAXHOSTNAMELEN], errbuf[256]; 133 134 /* Are we w(1) or uptime(1)? */ 135 p = __progname; 136 if (*p == '-') 137 p++; 138 if (*p == 'u') { 139 wcmd = 0; 140 p = ""; 141 } else { 142 wcmd = 1; 143 p = "hiflM:N:nsuw"; 144 } 145 146 memf = nlistf = NULL; 147 while ((ch = getopt(argc, argv, p)) != EOF) 148 switch (ch) { 149 case 'h': 150 header = 0; 151 break; 152 case 'i': 153 sortidle = 1; 154 break; 155 case 'M': 156 header = 0; 157 memf = optarg; 158 break; 159 case 'N': 160 nlistf = optarg; 161 break; 162 case 'n': 163 nflag = 1; 164 break; 165 case 'f': case 'l': case 's': case 'u': case 'w': 166 warnx("[-flsuw] no longer supported"); 167 /* FALLTHROUGH */ 168 case '?': 169 default: 170 usage(wcmd); 171 } 172 argc -= optind; 173 argv += optind; 174 175 /* 176 * Discard setgid privelidges if not the running kernel so that 177 * bad guys can't print interesting stuff from kernel memory. 178 */ 179 if (nlistf != NULL || memf != NULL) 180 setgid(getgid()); 181 182 if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) 183 errx(1, "%s", errbuf); 184 185 (void)time(&now); 186 if ((ut = fopen(_PATH_UTMP, "r")) == NULL) 187 err(1, "%s", _PATH_UTMP); 188 189 if (*argv) 190 sel_user = *argv; 191 192 for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { 193 if (utmp.ut_name[0] == '\0') 194 continue; 195 ++nusers; 196 if (wcmd == 0 || (sel_user && 197 strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0)) 198 continue; 199 if ((ep = calloc(1, sizeof(struct entry))) == NULL) 200 err(1, NULL); 201 *nextp = ep; 202 nextp = &(ep->next); 203 memmove(&(ep->utmp), &utmp, sizeof(struct utmp)); 204 if (!(stp = ttystat(ep->utmp.ut_line))) 205 continue; 206 ep->tdev = stp->st_rdev; 207 #ifdef CPU_CONSDEV 208 /* 209 * If this is the console device, attempt to ascertain 210 * the true console device dev_t. 211 */ 212 if (ep->tdev == 0) { 213 int mib[2]; 214 size_t size; 215 216 mib[0] = CTL_MACHDEP; 217 mib[1] = CPU_CONSDEV; 218 size = sizeof(dev_t); 219 (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0); 220 } 221 #endif 222 if ((ep->idle = now - stp->st_atime) < 0) 223 ep->idle = 0; 224 } 225 (void)fclose(ut); 226 227 if (header || wcmd == 0) { 228 pr_header(&now, nusers); 229 if (wcmd == 0) 230 exit (0); 231 } 232 233 #define HEADER "USER TTY FROM LOGIN@ IDLE WHAT\n" 234 #define WUSED (sizeof (HEADER) - sizeof ("WHAT\n")) 235 (void)printf(HEADER); 236 237 if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) 238 errx(1, "%s", kvm_geterr(kd)); 239 for (i = 0; i < nentries; i++, kp++) { 240 struct proc *p = &kp->kp_proc; 241 struct eproc *e; 242 243 if (p->p_stat == SIDL || p->p_stat == SZOMB) 244 continue; 245 e = &kp->kp_eproc; 246 for (ep = ehead; ep != NULL; ep = ep->next) { 247 if (ep->tdev == e->e_tdev && e->e_pgid == e->e_tpgid) { 248 /* 249 * Proc is in foreground of this terminal 250 */ 251 if (proc_compare(&ep->kp->kp_proc, p)) 252 ep->kp = kp; 253 break; 254 } 255 } 256 } 257 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 258 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 259 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 260 ttywidth = 79; 261 else 262 ttywidth = ws.ws_col - 1; 263 argwidth = ttywidth - WUSED; 264 if (argwidth < 4) 265 argwidth = 8; 266 /* sort by idle time */ 267 if (sortidle && ehead != NULL) { 268 struct entry *from = ehead, *save; 269 270 ehead = NULL; 271 while (from != NULL) { 272 for (nextp = &ehead; 273 (*nextp) && from->idle >= (*nextp)->idle; 274 nextp = &(*nextp)->next) 275 continue; 276 save = from; 277 from = from->next; 278 save->next = *nextp; 279 *nextp = save; 280 } 281 } 282 283 if (!nflag) 284 if (gethostname(domain, sizeof(domain) - 1) < 0 || 285 (p = strchr(domain, '.')) == 0) 286 domain[0] = '\0'; 287 else { 288 domain[sizeof(domain) - 1] = '\0'; 289 memmove(domain, p, strlen(p) + 1); 290 } 291 292 for (ep = ehead; ep != NULL; ep = ep->next) { 293 p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; 294 if ((x = strchr(p, ':')) != NULL) 295 *x++ = '\0'; 296 if (!nflag && isdigit(*p) && 297 (long)(l = inet_addr(p)) != -1 && 298 (hp = gethostbyaddr((char *)&l, sizeof(l), AF_INET))) { 299 if (domain[0] != '\0') { 300 p = hp->h_name; 301 p += strlen(hp->h_name); 302 p -= strlen(domain); 303 if (p > hp->h_name && strcmp(p, domain) == 0) 304 *p = '\0'; 305 } 306 p = hp->h_name; 307 } 308 if (x) { 309 (void)snprintf(buf, sizeof(buf), "%s:%.*s", p, 310 ep->utmp.ut_host + UT_HOSTSIZE - x, x); 311 p = buf; 312 } 313 (void)printf("%-*.*s %-2.2s %-*.*s ", 314 UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, 315 strncmp(ep->utmp.ut_line, "tty", 3) ? 316 ep->utmp.ut_line : ep->utmp.ut_line + 3, 317 UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-"); 318 pr_attime(&ep->utmp.ut_time, &now); 319 pr_idle(ep->idle); 320 pr_args(ep->kp); 321 printf("\n"); 322 } 323 exit(0); 324 } 325 326 static void 327 pr_args(kp) 328 struct kinfo_proc *kp; 329 { 330 char **argv; 331 int left; 332 333 if (kp == 0) 334 goto nothing; 335 left = argwidth; 336 argv = kvm_getargv(kd, kp, argwidth); 337 if (argv == 0) 338 goto nothing; 339 while (*argv) { 340 fmt_puts(*argv, &left); 341 argv++; 342 fmt_putc(' ', &left); 343 } 344 return; 345 nothing: 346 putchar('-'); 347 } 348 349 static void 350 pr_header(nowp, nusers) 351 time_t *nowp; 352 int nusers; 353 { 354 double avenrun[3]; 355 time_t uptime; 356 int days, hrs, i, mins; 357 int mib[2]; 358 size_t size; 359 char buf[256]; 360 361 /* 362 * Print time of day. 363 * 364 * SCCS forces the string manipulation below, as it replaces 365 * %, M, and % in a character string with the file name. 366 */ 367 (void)strftime(buf, sizeof(buf), 368 __CONCAT("%l:%","M%p"), localtime(nowp)); 369 (void)printf("%s ", buf); 370 371 /* 372 * Print how long system has been up. 373 * (Found by looking getting "boottime" from the kernel) 374 */ 375 mib[0] = CTL_KERN; 376 mib[1] = KERN_BOOTTIME; 377 size = sizeof(boottime); 378 if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && 379 boottime.tv_sec != 0) { 380 uptime = now - boottime.tv_sec; 381 uptime += 30; 382 days = uptime / SECSPERDAY; 383 uptime %= SECSPERDAY; 384 hrs = uptime / SECSPERHOUR; 385 uptime %= SECSPERHOUR; 386 mins = uptime / SECSPERMIN; 387 (void)printf(" up"); 388 if (days > 0) 389 (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); 390 if (hrs > 0 && mins > 0) 391 (void)printf(" %2d:%02d,", hrs, mins); 392 else { 393 if (hrs > 0) 394 (void)printf(" %d hr%s,", 395 hrs, hrs > 1 ? "s" : ""); 396 if (mins > 0) 397 (void)printf(" %d min%s,", 398 mins, mins > 1 ? "s" : ""); 399 } 400 } 401 402 /* Print number of users logged in to system */ 403 (void)printf(" %d user%s", nusers, nusers != 1 ? "s" : ""); 404 405 /* 406 * Print 1, 5, and 15 minute load averages. 407 */ 408 if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) 409 (void)printf(", no load average information available\n"); 410 else { 411 (void)printf(", load averages:"); 412 for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) { 413 if (i > 0) 414 (void)printf(","); 415 (void)printf(" %.2f", avenrun[i]); 416 } 417 (void)printf("\n"); 418 } 419 } 420 421 static struct stat * 422 ttystat(line) 423 char *line; 424 { 425 static struct stat sb; 426 char ttybuf[MAXPATHLEN]; 427 428 (void)snprintf(ttybuf, sizeof(ttybuf), "%s/%s", _PATH_DEV, line); 429 if (stat(ttybuf, &sb)) 430 return (NULL); 431 return (&sb); 432 } 433 434 static void 435 usage(wcmd) 436 int wcmd; 437 { 438 if (wcmd) 439 (void)fprintf(stderr, 440 "usage: w: [-hin] [-M core] [-N system] [user]\n"); 441 else 442 (void)fprintf(stderr, "uptime\n"); 443 exit (1); 444 } 445