1 /* $NetBSD: w.c,v 1.37 2000/06/25 13:41:12 simonb 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 #include <sys/cdefs.h> 37 #ifndef lint 38 __COPYRIGHT("@(#) 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.6 (Berkeley) 6/30/94"; 45 #else 46 __RCSID("$NetBSD: w.c,v 1.37 2000/06/25 13:41:12 simonb 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 65 #include <netinet/in.h> 66 #include <arpa/inet.h> 67 68 #include <ctype.h> 69 #include <err.h> 70 #include <errno.h> 71 #include <fcntl.h> 72 #include <kvm.h> 73 #include <limits.h> 74 #include <netdb.h> 75 #include <nlist.h> 76 #include <paths.h> 77 #include <stdio.h> 78 #include <stdlib.h> 79 #include <string.h> 80 #include <time.h> 81 #include <tzfile.h> 82 #include <unistd.h> 83 #include <utmp.h> 84 #include <vis.h> 85 86 #include "extern.h" 87 88 #define max(a,b) (((a)>(b))?(a):(b)) 89 90 struct timeval boottime; 91 struct utmp utmp; 92 struct winsize ws; 93 kvm_t *kd; 94 time_t now; /* the current time of day */ 95 time_t uptime; /* time of last reboot & elapsed time since */ 96 int ttywidth; /* width of tty */ 97 int argwidth; /* width of tty */ 98 int header = 1; /* true if -h flag: don't print heading */ 99 int nflag; /* true if -n flag: don't convert addrs */ 100 int sortidle; /* sort bu idle time */ 101 char *sel_user; /* login of particular user selected */ 102 char domain[MAXHOSTNAMELEN + 1]; 103 104 /* 105 * One of these per active utmp entry. 106 */ 107 struct entry { 108 struct entry *next; 109 struct utmp utmp; 110 dev_t tdev; /* dev_t of terminal */ 111 time_t idle; /* idle time of terminal in seconds */ 112 struct kinfo_proc2 *kp; /* `most interesting' proc */ 113 } *ep, *ehead = NULL, **nextp = &ehead; 114 115 static void pr_args __P((struct kinfo_proc2 *)); 116 static void pr_header __P((time_t *, int)); 117 static struct stat 118 *ttystat __P((char *)); 119 static void usage __P((int)); 120 int main __P((int, char **)); 121 122 int 123 main(argc, argv) 124 int argc; 125 char **argv; 126 { 127 extern char *__progname; 128 struct kinfo_proc2 *kp; 129 struct hostent *hp; 130 struct stat *stp; 131 FILE *ut; 132 struct in_addr l; 133 int ch, i, nentries, nusers, wcmd, lognamelen; 134 char *memf, *nlistf, *p, *x; 135 char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; 136 137 /* Are we w(1) or uptime(1)? */ 138 p = __progname; 139 if (*p == '-') 140 p++; 141 if (*p == 'u') { 142 wcmd = 0; 143 p = ""; 144 } else { 145 wcmd = 1; 146 p = "hiflM:N:nsuw"; 147 } 148 149 memf = nlistf = NULL; 150 while ((ch = getopt(argc, argv, p)) != -1) 151 switch (ch) { 152 case 'h': 153 header = 0; 154 break; 155 case 'i': 156 sortidle = 1; 157 break; 158 case 'M': 159 header = 0; 160 memf = optarg; 161 break; 162 case 'N': 163 nlistf = optarg; 164 break; 165 case 'n': 166 nflag = 1; 167 break; 168 case 'f': case 'l': case 's': case 'u': case 'w': 169 warnx("[-flsuw] no longer supported"); 170 /* FALLTHROUGH */ 171 case '?': 172 default: 173 usage(wcmd); 174 } 175 argc -= optind; 176 argv += optind; 177 178 if ((kd = kvm_openfiles(nlistf, memf, NULL, 179 memf == NULL ? KVM_NO_FILES : O_RDONLY, errbuf)) == NULL) 180 errx(1, "%s", errbuf); 181 182 (void)time(&now); 183 if ((ut = fopen(_PATH_UTMP, "r")) == NULL) 184 err(1, "%s", _PATH_UTMP); 185 186 if (*argv) 187 sel_user = *argv; 188 189 for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { 190 if (utmp.ut_name[0] == '\0') 191 continue; 192 ++nusers; 193 if (wcmd == 0 || (sel_user && 194 strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0)) 195 continue; 196 if ((ep = calloc(1, sizeof(struct entry))) == NULL) 197 err(1, NULL); 198 *nextp = ep; 199 nextp = &(ep->next); 200 memmove(&(ep->utmp), &utmp, sizeof(struct utmp)); 201 if (!(stp = ttystat(ep->utmp.ut_line))) 202 continue; 203 ep->tdev = stp->st_rdev; 204 #ifdef CPU_CONSDEV 205 /* 206 * If this is the console device, attempt to ascertain 207 * the true console device dev_t. 208 */ 209 if (ep->tdev == 0) { 210 int mib[2]; 211 size_t size; 212 213 mib[0] = CTL_MACHDEP; 214 mib[1] = CPU_CONSDEV; 215 size = sizeof(dev_t); 216 (void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0); 217 } 218 #endif 219 if ((ep->idle = now - stp->st_atime) < 0) 220 ep->idle = 0; 221 } 222 (void)fclose(ut); 223 224 if (header || wcmd == 0) { 225 pr_header(&now, nusers); 226 if (wcmd == 0) 227 exit (0); 228 } 229 230 if ((kp = kvm_getproc2(kd, KERN_PROC_ALL, 0, 231 sizeof(struct kinfo_proc2), &nentries)) == NULL) 232 errx(1, "%s", kvm_geterr(kd)); 233 lognamelen = 0; 234 for (i = 0; i < nentries; i++, kp++) { 235 236 if (kp->p_stat == SIDL || kp->p_stat == SZOMB) 237 continue; 238 for (ep = ehead; ep != NULL; ep = ep->next) { 239 if (ep->tdev == kp->p_tdev && 240 kp->p__pgid == kp->p_tpgid) { 241 /* 242 * Proc is in foreground of this terminal 243 */ 244 if (proc_compare(ep->kp, kp)) { 245 ep->kp = kp; 246 lognamelen = max(lognamelen, 247 strlen(kp->p_login)); 248 } 249 break; 250 } 251 } 252 } 253 254 argwidth = printf("%-*sTTY %-*s %*s IDLE WHAT\n", 255 lognamelen, "USER", UT_HOSTSIZE, "FROM", 256 7 /* "dddhhXm" */, "LOGIN@"); 257 argwidth -= sizeof("WHAT\n"); 258 259 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && 260 ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && 261 ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 262 ttywidth = 79; 263 else 264 ttywidth = ws.ws_col - 1; 265 argwidth = ttywidth - argwidth; 266 if (argwidth < 4) 267 argwidth = 8; 268 /* sort by idle time */ 269 if (sortidle && ehead != NULL) { 270 struct entry *from = ehead, *save; 271 272 ehead = NULL; 273 while (from != NULL) { 274 for (nextp = &ehead; 275 (*nextp) && from->idle >= (*nextp)->idle; 276 nextp = &(*nextp)->next) 277 continue; 278 save = from; 279 from = from->next; 280 save->next = *nextp; 281 *nextp = save; 282 } 283 } 284 285 if (!nflag) { 286 int rv; 287 288 rv = gethostname(domain, sizeof(domain)); 289 domain[sizeof(domain) - 1] = '\0'; 290 if (rv < 0 || (p = strchr(domain, '.')) == 0) 291 domain[0] = '\0'; 292 else 293 memmove(domain, p, strlen(p) + 1); 294 } 295 296 for (ep = ehead; ep != NULL; ep = ep->next) { 297 p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; 298 for (x = p; x < p + UT_HOSTSIZE; x++) 299 if (*x == '\0' || *x == ':') 300 break; 301 if (x == p + UT_HOSTSIZE || *x != ':') 302 x = NULL; 303 else 304 *x++ = '\0'; 305 306 if (!nflag && inet_aton(p, &l) && 307 (hp = gethostbyaddr((char *)&l, sizeof(l), AF_INET))) { 308 if (domain[0] != '\0') { 309 p = hp->h_name; 310 p += strlen(hp->h_name); 311 p -= strlen(domain); 312 if (p > hp->h_name && strcmp(p, domain) == 0) 313 *p = '\0'; 314 } 315 p = hp->h_name; 316 } 317 if (x) { 318 (void)snprintf(buf, sizeof(buf), "%s:%.*s", p, 319 (int)(ep->utmp.ut_host + UT_HOSTSIZE - x), x); 320 p = buf; 321 } 322 (void)printf("%-*s %-2.2s %-*.*s ", 323 lognamelen, ep->kp->p_login, 324 (strncmp(ep->utmp.ut_line, "tty", 3) && 325 strncmp(ep->utmp.ut_line, "dty", 3)) ? 326 ep->utmp.ut_line : ep->utmp.ut_line + 3, 327 UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-"); 328 pr_attime(&ep->utmp.ut_time, &now); 329 pr_idle(ep->idle); 330 pr_args(ep->kp); 331 (void)printf("\n"); 332 } 333 exit(0); 334 } 335 336 static void 337 pr_args(kp) 338 struct kinfo_proc2 *kp; 339 { 340 char **argv; 341 int left; 342 343 if (kp == 0) 344 goto nothing; 345 left = argwidth; 346 argv = kvm_getargv2(kd, kp, argwidth); 347 if (argv == 0) 348 goto nothing; 349 while (*argv) { 350 fmt_puts(*argv, &left); 351 argv++; 352 fmt_putc(' ', &left); 353 } 354 return; 355 nothing: 356 putchar('-'); 357 } 358 359 static void 360 pr_header(nowp, nusers) 361 time_t *nowp; 362 int nusers; 363 { 364 double avenrun[3]; 365 time_t uptime; 366 int days, hrs, i, mins; 367 int mib[2]; 368 size_t size; 369 char buf[256]; 370 371 /* 372 * Print time of day. 373 * 374 * SCCS forces the string manipulation below, as it replaces 375 * %, M, and % in a character string with the file name. 376 */ 377 (void)strftime(buf, sizeof(buf), 378 __CONCAT("%l:%","M%p"), localtime(nowp)); 379 buf[sizeof(buf) - 1] = '\0'; 380 (void)printf("%s ", buf); 381 382 /* 383 * Print how long system has been up. 384 * (Found by looking getting "boottime" from the kernel) 385 */ 386 mib[0] = CTL_KERN; 387 mib[1] = KERN_BOOTTIME; 388 size = sizeof(boottime); 389 if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && 390 boottime.tv_sec != 0) { 391 uptime = now - boottime.tv_sec; 392 uptime += 30; 393 if (uptime > SECSPERMIN) { 394 days = uptime / SECSPERDAY; 395 uptime %= SECSPERDAY; 396 hrs = uptime / SECSPERHOUR; 397 uptime %= SECSPERHOUR; 398 mins = uptime / SECSPERMIN; 399 (void)printf(" up"); 400 if (days > 0) 401 (void)printf(" %d day%s,", days, 402 days > 1 ? "s" : ""); 403 if (hrs > 0 && mins > 0) 404 (void)printf(" %2d:%02d,", hrs, mins); 405 else { 406 if (hrs > 0) 407 (void)printf(" %d hr%s,", 408 hrs, hrs > 1 ? "s" : ""); 409 if (mins > 0) 410 (void)printf(" %d min%s,", 411 mins, mins > 1 ? "s" : ""); 412 } 413 } 414 } 415 416 /* Print number of users logged in to system */ 417 (void)printf(" %d user%s", nusers, nusers != 1 ? "s" : ""); 418 419 /* 420 * Print 1, 5, and 15 minute load averages. 421 */ 422 if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) 423 (void)printf(", no load average information available\n"); 424 else { 425 (void)printf(", load averages:"); 426 for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) { 427 if (i > 0) 428 (void)printf(","); 429 (void)printf(" %.2f", avenrun[i]); 430 } 431 (void)printf("\n"); 432 } 433 } 434 435 static struct stat * 436 ttystat(line) 437 char *line; 438 { 439 static struct stat sb; 440 char ttybuf[MAXPATHLEN]; 441 442 (void)snprintf(ttybuf, sizeof(ttybuf), "%s/%s", _PATH_DEV, line); 443 if (stat(ttybuf, &sb)) 444 return (NULL); 445 return (&sb); 446 } 447 448 static void 449 usage(wcmd) 450 int wcmd; 451 { 452 if (wcmd) 453 (void)fprintf(stderr, 454 "usage: w: [-hin] [-M core] [-N system] [user]\n"); 455 else 456 (void)fprintf(stderr, "uptime\n"); 457 exit(1); 458 } 459