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