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