1 /*- 2 * Copyright (c) 1980, 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1980, 1991 The Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)w.c 5.33 (Berkeley) 05/20/92"; 16 #endif /* not lint */ 17 18 #define ADDRHACK 19 20 /* 21 * w - print system status (who and what) 22 * 23 * This program is similar to the systat command on Tenex/Tops 10/20 24 * 25 */ 26 #include <sys/param.h> 27 #include <sys/file.h> 28 #include <sys/time.h> 29 #include <sys/stat.h> 30 #include <sys/kinfo.h> 31 #include <sys/proc.h> 32 #include <sys/user.h> 33 #include <sys/ioctl.h> 34 #include <sys/tty.h> 35 #include <ctype.h> 36 #include <errno.h> 37 #include <kvm.h> 38 #include <nlist.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <utmp.h> 43 #include <paths.h> 44 45 #ifdef ADDRHACK 46 #include <sys/socket.h> 47 #include <netdb.h> 48 #endif 49 50 char *program; 51 int ttywidth; /* width of tty */ 52 int argwidth; /* width of tty */ 53 int header = 1; /* true if -h flag: don't print heading */ 54 int wcmd = 1; /* true if this is w(1), and not uptime(1) */ 55 #ifdef ADDRHACK 56 int nflag = 0; /* true if -n flag: don't convert addrs */ 57 #endif 58 int nusers; /* number of users logged in now */ 59 char * sel_user; /* login of particular user selected */ 60 time_t now; /* the current time of day */ 61 struct timeval boottime; 62 time_t uptime; /* time of last reboot & elapsed time since */ 63 struct utmp utmp; 64 struct winsize ws; 65 int sortidle; /* sort bu idle time */ 66 #ifdef ADDRHACK 67 char domain[64]; 68 #endif 69 kvm_t *kd; 70 71 #if __STDC__ 72 void error(const char *fmt, ...); 73 #else 74 void error(); 75 #endif 76 77 void prttime(); 78 79 /* 80 * One of these per active utmp entry. 81 */ 82 struct entry { 83 struct entry *next; 84 struct utmp utmp; 85 dev_t tdev; /* dev_t of terminal */ 86 int idle; /* idle time of terminal in minutes */ 87 struct kinfo_proc *kp; /* `most interesting' proc */ 88 char *args; /* arg list of interesting process */ 89 } *ep, *ehead = NULL, **nextp = &ehead; 90 91 struct nlist nl[] = { 92 { "_boottime" }, 93 #define X_BOOTTIME 0 94 #if defined(hp300) || defined(i386) 95 { "_cn_tty" }, 96 #define X_CNTTY 1 97 #endif 98 { "" }, 99 }; 100 101 #define USAGE "[ -hi ] [ user ]" 102 #define usage() fprintf(stderr, "usage: %s: %s\n", program, USAGE) 103 104 main(argc, argv) 105 int argc; 106 char **argv; 107 { 108 register int i; 109 register struct kinfo_proc *kp; 110 struct stat *stp, *ttystat(); 111 FILE *ut; 112 char *cp; 113 int nentries; 114 int ch; 115 extern char *optarg; 116 extern int optind; 117 char *attime(); 118 char errbuf[80]; 119 120 program = argv[0]; 121 /* 122 * are we w(1) or uptime(1) 123 */ 124 if ((cp = rindex(program, '/')) || *(cp = program) == '-') 125 cp++; 126 if (*cp == 'u') 127 wcmd = 0; 128 129 while ((ch = getopt(argc, argv, "hiflnsuw")) != EOF) 130 switch((char)ch) { 131 case 'h': 132 header = 0; 133 break; 134 case 'i': 135 sortidle++; 136 break; 137 case 'n': 138 #ifdef ADDRHACK 139 ++nflag; 140 #endif 141 break; 142 case 'f': case 'l': case 's': case 'u': case 'w': 143 error("[-flsuw] no longer supported"); 144 usage(); 145 exit(1); 146 case '?': 147 default: 148 usage(); 149 exit(1); 150 } 151 argc -= optind; 152 argv += optind; 153 154 if (*argv) 155 sel_user = *argv; 156 157 kd = kvm_openfiles((char *)0, (char *)0, (char *)0, O_RDONLY, errbuf); 158 if (kd == NULL) { 159 error("%s", errbuf); 160 exit(1); 161 } 162 if (header && kvm_nlist(kd, nl) != 0) { 163 error("can't get namelist"); 164 exit(1); 165 } 166 #ifdef ADDRHACK 167 if (!nflag) { 168 if (gethostname(domain, sizeof(domain) - 1) < 0 || 169 (cp = index(domain, '.')) == 0) 170 domain[0] = '\0'; 171 else { 172 domain[sizeof(domain) - 1] = '\0'; 173 bcopy(cp, domain, strlen(cp) + 1); 174 } 175 } 176 #endif 177 time(&now); 178 ut = fopen(_PATH_UTMP, "r"); 179 if (ut == NULL) { 180 error("%s: %s", _PATH_UTMP, strerror(errno)); 181 exit(1); 182 } 183 while (fread(&utmp, sizeof(utmp), 1, ut)) { 184 if (utmp.ut_name[0] == '\0') 185 continue; 186 nusers++; 187 if (wcmd == 0 || (sel_user && 188 strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0)) 189 continue; 190 if ((ep = (struct entry *) 191 calloc(1, sizeof (struct entry))) == NULL) { 192 error("out of memory"); 193 exit(1); 194 } 195 *nextp = ep; 196 nextp = &(ep->next); 197 bcopy(&utmp, &(ep->utmp), sizeof (struct utmp)); 198 stp = ttystat(ep->utmp.ut_line); 199 ep->tdev = stp->st_rdev; 200 #if defined(hp300) || defined(i386) 201 /* 202 * XXX If this is the console device, attempt to ascertain 203 * the true console device dev_t. 204 */ 205 if (ep->tdev == 0) { 206 static dev_t cn_dev; 207 208 if (nl[X_CNTTY].n_value) { 209 struct tty cn_tty, *cn_ttyp; 210 211 if (kvm_read(kd, (u_long)nl[X_CNTTY].n_value, 212 (char *)&cn_ttyp, sizeof(cn_ttyp)) > 0) { 213 (void)kvm_read(kd, (u_long)cn_ttyp, 214 (char *)&cn_tty, sizeof (cn_tty)); 215 cn_dev = cn_tty.t_dev; 216 } 217 nl[X_CNTTY].n_value = 0; 218 } 219 ep->tdev = cn_dev; 220 } 221 #endif 222 ep->idle = ((now - stp->st_atime) + 30) / 60; /* secs->mins */ 223 if (ep->idle < 0) 224 ep->idle = 0; 225 } 226 fclose(ut); 227 228 if (header || wcmd == 0) { 229 double avenrun[3]; 230 int days, hrs, mins; 231 232 /* 233 * Print time of day 234 */ 235 fputs(attime(&now), stdout); 236 /* 237 * Print how long system has been up. 238 * (Found by looking for "boottime" in kernel) 239 */ 240 (void)kvm_read(kd, (u_long)nl[X_BOOTTIME].n_value, 241 (char *)&boottime, sizeof (boottime)); 242 uptime = now - boottime.tv_sec; 243 uptime += 30; 244 days = uptime / (60*60*24); 245 uptime %= (60*60*24); 246 hrs = uptime / (60*60); 247 uptime %= (60*60); 248 mins = uptime / 60; 249 250 printf(" up"); 251 if (days > 0) 252 printf(" %d day%s,", days, days>1?"s":""); 253 if (hrs > 0 && mins > 0) { 254 printf(" %2d:%02d,", hrs, mins); 255 } else { 256 if (hrs > 0) 257 printf(" %d hr%s,", hrs, hrs>1?"s":""); 258 if (mins > 0) 259 printf(" %d min%s,", mins, mins>1?"s":""); 260 } 261 262 /* Print number of users logged in to system */ 263 printf(" %d user%s", nusers, nusers>1?"s":""); 264 265 /* 266 * Print 1, 5, and 15 minute load averages. 267 */ 268 printf(", load average:"); 269 (void)getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])); 270 for (i = 0; i < (sizeof(avenrun)/sizeof(avenrun[0])); i++) { 271 if (i > 0) 272 printf(","); 273 printf(" %.2f", avenrun[i]); 274 } 275 printf("\n"); 276 if (wcmd == 0) /* if uptime(1) then done */ 277 exit(0); 278 #define HEADER "USER TTY FROM LOGIN@ IDLE WHAT\n" 279 #define WUSED (sizeof (HEADER) - sizeof ("WHAT\n")) 280 printf(HEADER); 281 } 282 283 if ((kp = kvm_getprocs(kd, KINFO_PROC_ALL, 0, &nentries)) == NULL) 284 error("%s", kvm_geterr(kd)); 285 for (i = 0; i < nentries; i++, kp++) { 286 register struct proc *p = &kp->kp_proc; 287 register struct eproc *e; 288 289 if (p->p_stat == SIDL || p->p_stat == SZOMB) 290 continue; 291 e = &kp->kp_eproc; 292 for (ep = ehead; ep != NULL; ep = ep->next) { 293 if (ep->tdev == e->e_tdev && e->e_pgid == e->e_tpgid) { 294 /* 295 * Proc is in foreground of this terminal 296 */ 297 if (proc_compare(&ep->kp->kp_proc, p)) 298 ep->kp = kp; 299 break; 300 } 301 } 302 } 303 if ((ioctl(1, TIOCGWINSZ, &ws) == -1 && 304 ioctl(2, TIOCGWINSZ, &ws) == -1 && 305 ioctl(0, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) 306 ttywidth = 79; 307 else 308 ttywidth = ws.ws_col - 1; 309 argwidth = ttywidth - WUSED; 310 if (argwidth < 4) 311 argwidth = 8; 312 for (ep = ehead; ep != NULL; ep = ep->next) { 313 char *fmt_argv(); 314 315 if (ep->kp == NULL) { 316 ep->args = "-"; 317 continue; 318 } 319 ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), 320 ep->kp->kp_proc.p_comm, MAXCOMLEN); 321 if (ep->args == NULL) { 322 error("out of memory"); 323 exit(1); 324 } 325 } 326 /* sort by idle time */ 327 if (sortidle && ehead != NULL) { 328 struct entry *from = ehead, *save; 329 330 ehead = NULL; 331 while (from != NULL) { 332 for (nextp = &ehead; 333 (*nextp) && from->idle >= (*nextp)->idle; 334 nextp = &(*nextp)->next) 335 continue; 336 save = from; 337 from = from->next; 338 save->next = *nextp; 339 *nextp = save; 340 } 341 } 342 343 for (ep = ehead; ep != NULL; ep = ep->next) { 344 #ifdef ADDRHACK 345 register char *x; 346 register struct hostent *hp; 347 unsigned long l; 348 char buf[64]; 349 #endif 350 351 cp = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; 352 #ifdef ADDRHACK 353 if (x = index(cp, ':')) 354 *x++ = '\0'; 355 if (!nflag && isdigit(*cp) && 356 (long)(l = inet_addr(cp)) != -1 && 357 (hp = gethostbyaddr((char *)&l, sizeof(l), 358 AF_INET))) { 359 if (domain[0] != '\0') { 360 cp = hp->h_name; 361 cp += strlen(hp->h_name); 362 cp -= strlen(domain); 363 if (cp > hp->h_name && 364 strcmp(cp, domain) == 0) 365 *cp = '\0'; 366 } 367 cp = hp->h_name; 368 } 369 if (x) { 370 sprintf(buf, "%s:%s", cp, x); 371 cp = buf; 372 } 373 #endif 374 printf("%-*.*s %-2.2s %-*.*s %s", 375 UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, 376 strncmp(ep->utmp.ut_line, "tty", 3) == 0 ? 377 ep->utmp.ut_line+3 : ep->utmp.ut_line, 378 UT_HOSTSIZE, UT_HOSTSIZE, *cp ? cp : "-", 379 attime(&ep->utmp.ut_time)); 380 if (ep->idle >= 36 * 60) 381 printf(" %ddays ", (ep->idle + 12 * 60) / (24 * 60)); 382 else 383 prttime(ep->idle, " "); 384 printf("%.*s\n", argwidth, ep->args); 385 } 386 exit(0); 387 } 388 389 struct stat * 390 ttystat(line) 391 char *line; 392 { 393 static struct stat statbuf; 394 char ttybuf[sizeof (_PATH_DEV) + UT_LINESIZE + 1]; 395 396 sprintf(ttybuf, "%s/%.*s", _PATH_DEV, UT_LINESIZE, line); 397 (void) stat(ttybuf, &statbuf); 398 399 return (&statbuf); 400 } 401 402 /* 403 * prttime prints a time in hours and minutes or minutes and seconds. 404 * The character string tail is printed at the end, obvious 405 * strings to pass are "", " ", or "am". 406 */ 407 void 408 prttime(tim, tail) 409 time_t tim; 410 char *tail; 411 { 412 413 if (tim >= 60) { 414 printf(" %2d:", tim/60); 415 tim %= 60; 416 printf("%02d", tim); 417 } else if (tim >= 0) 418 printf(" %2d", tim); 419 printf("%s", tail); 420 } 421 422 #if __STDC__ 423 #include <stdarg.h> 424 #else 425 #include <varargs.h> 426 #endif 427 428 void 429 #if __STDC__ 430 error(const char *fmt, ...) 431 #else 432 error(va_alist) 433 va_dcl 434 #endif 435 { 436 #if !__STDC__ 437 char *fmt; 438 #endif 439 va_list ap; 440 441 fprintf(stderr, "%s: ", program); 442 #if __STDC__ 443 va_start(ap, fmt); 444 #else 445 va_start(ap); 446 fmt = va_arg(ap, char *); 447 #endif 448 (void) vfprintf(stderr, fmt, ap); 449 va_end(ap); 450 fprintf(stderr, "\n"); 451 } 452