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