2 * Extended `who' command, version 1.11.
4 * (c) 1996--2003 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU General Public License. See file COPYING in any of the GNU packages.
20 #include <asm/param.h>
21 #include <sys/ioctl.h>
25 #define MAXHOSTSIZE 24
28 #define DBG(x,y...) printf(x,##y)
30 #define DBG(x,y...) do { } while(0)
35 int pid, ppid, pgrp, sess;
36 int uid, state, distance;
42 static struct procrec *first_procrec;
47 char line[UT_LINESIZE];
48 time_t login_time, click_time, total_time;
49 char user_name[UT_NAMESIZE+1];
51 char host_name[MAXHOSTSIZE+1];
56 static struct utrec *first_utrec;
57 static int usercnt, zombies, maxhost = 10;
67 fprintf(stderr, "ywho: out of memory!\n");
74 strcpy_padded(char *to, char *from, int size)
76 memcpy(to, from, size);
83 static struct utmp *ut;
84 static struct passwd *pw;
87 while (ut = getutent())
88 if (ut->ut_type == USER_PROCESS && ut->ut_user[0])
90 struct utrec *u = xmalloc(sizeof(struct utrec));
95 u->next = first_utrec;
97 u->login_pid = ut->ut_pid;
99 if (!strncmp(z, "tty", 3))
101 if (z[3] >= '0' && z[3] <= '9')
102 sprintf(u->line, "c%s", z+3);
104 strcpy(u->line, z+3);
106 else if (!strncmp(z, "pts/", 4))
107 strcpy(u->line, z+4);
108 u->login_time = ut->ut_time;
109 strcpy_padded(u->user_name, ut->ut_user, UT_NAMESIZE);
110 pw = getpwnam(u->user_name);
115 strcpy_padded(u->host_name, ut->ut_host,
116 (MAXHOSTSIZE > UT_HOSTSIZE ? UT_HOSTSIZE : MAXHOSTSIZE));
117 if ((hlen = strlen(u->host_name)) > maxhost)
119 strcpy(u->line, ut->ut_line);
123 if (!strncmp(z, "tty", 3))
126 sprintf(u->line, "c%s", z+3);
128 strcpy(u->line, z+3);
130 else if (!strncmp(z, "pts/", 4))
131 strcpy(u->line, z+4);
135 strcpy(device, "/dev/");
136 strcat(device, ut->ut_line);
137 if (stat(device, &st))
141 u->mesg = !!(S_IWGRP & st.st_mode);
142 u->click_time = st.st_atime;
144 if (u->click_time < st.st_mtime)
145 u->click_time = st.st_mtime;
146 if (u->click_time < st.st_ctime)
147 u->click_time = st.st_ctime;
152 DBG("UTMP: %s %s %d %d\n", u->user_name, u->host_name,
153 u->uid, u->login_pid);
171 printf("%02d.%02d", h, m);
177 printf("%2dd%02d", d, h);
185 memory(unsigned int i)
200 printf("%dG", (i+511U)/1024U);
213 if (f = fopen("/proc/version", "r"))
216 z = strchr(line, '(');
219 while (z != line && z[-1] == ' ')
223 z = line + strlen(line) - 1;
225 z = strstr(line, " version");
227 memmove(z, z+8, strlen(z+8)+1);
232 if (gethostname(line, sizeof(line)) >= 0)
233 printf(" (%s)", line);
235 if (f = fopen("/proc/cpuinfo", "r"))
237 char a[32], b[32], c[32];
246 if (!strncmp(line, "cpu\t\t: ", 7))
251 else if (!strncmp(line, "cpu family\t: ",13))
253 z = line+13; strcpy(z + 1, "86\n");
256 else if (!strncmp(line, "model\t\t: ", 9))
261 else if (!strncmp(line, "model name\t: ", 13))
264 if (!strncmp(z, "AMD-K6", 6))
267 if ((w = strstr(z, "tm w/ multimedia")))
269 else if ((w = strstr(z, "(tm) 3D")))
274 else if (!strncmp(line, "mask\t\t: ", 8))
283 line[strlen(line)-1] = 0;
285 if (!strcmp(w, "Unknown"))
289 printf(" on %s", *b ? b : a);
295 if (f = fopen("/proc/uptime", "r"))
299 sscanf(line, "%d.%d%d.%d", &p, &p1, &q, &q1);
309 char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
311 tm = localtime(&now);
312 printf(" on %s %02d-%02d-%02d %02d.%02d:%02d", days[tm->tm_wday], tm->tm_mday, tm->tm_mon+1,
313 tm->tm_year%100, tm->tm_hour, tm->tm_min, tm->tm_sec);
314 printf(" (%u UE)\n", (unsigned int) now);
317 if (f = fopen("/proc/loadavg", "r"))
321 *strchr(line, '\n') = 0;
335 printf("R/T=%s, ", z);
339 printf("LAV=%s", line);
343 if (f = fopen("/proc/meminfo", "r"))
345 int free, buffers, cached, stotal, sfree;
346 free = buffers = stotal = sfree = 0;
347 while (fgets(line, 256, f))
349 if (!strncmp(line, "MemFree:", 8))
350 sscanf(line+8, "%d", &free);
351 else if (!strncmp(line, "Buffers:", 8))
352 sscanf(line+8, "%d", &buffers);
353 else if (!strncmp(line, "Cached:", 7))
354 sscanf(line+7, "%d", &cached);
355 else if (!strncmp(line, "SwapTotal:", 10))
356 sscanf(line+10, "%d", &stotal);
357 else if (!strncmp(line, "SwapFree:", 9))
358 sscanf(line+9, "%d", &sfree);
361 memory(1024*(free+buffers+cached));
366 memory(1024*(stotal - sfree));
370 printf(", %d user%s.\n", usercnt, usercnt == 1 ? "" : "s");
382 if (!ioctl(1, TIOCGWINSZ, &win) && win.ws_col >= LEFTSIZE + maxhost + 10 && win.ws_col < 1024)
383 cmdcols = win.ws_col;
386 cmdcols -= LEFTSIZE + maxhost;
387 if (d = opendir("/proc"))
389 while (e = readdir(d))
392 if (sscanf(e->d_name, "%d", &i)==1)
394 struct procrec *p = xmalloc(sizeof(struct procrec) + cmdcols);
396 char name[256], eman[1024];
398 bzero(p, sizeof(*p));
400 strcpy(name, "/proc/");
401 strcat(name, e->d_name);
402 if (!stat(name, &st))
406 strcat(eman, "/cmdline");
407 if ((fil = open(eman, O_RDONLY)) >= 0)
409 int y, z = read(fil, p->cmd_line, cmdcols);
414 p->cmd_line[y] = ' ';
419 strcat(eman, "/stat");
420 if ((fil = open(eman, O_RDONLY)) >= 0)
422 int z = read(fil, eman, 1023);
424 int trash, utim, stim, cutim, cstim;
428 if ((j = strchr(eman, '(')) && (k = strchr(j+1, ')')))
433 strncpy(p->cmd_line, j+1, cmdcols);
434 p->cmd_line[cmdcols] = 0;
439 sscanf(k+4, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
440 &p->ppid, &p->pgrp, &p->sess, &trash, &trash,
441 &trash, &trash, &trash, &trash, &trash, &utim,
442 &stim, &cutim, &cstim);
443 DBG("PROC: pid=%d ppid=%d pgrp=%d sess=%d uid=%d\n", p->pid, p->ppid, p->pgrp, p->sess, p->uid);
444 p->next = first_procrec;
447 p->tim = utim + stim;
448 p->ctim = cutim + cstim;
458 perror("unable to scan /proc");
467 struct procrec *m, *n;
471 /* Recognize login session process trees and find leaves of these trees */
472 for (u=first_utrec; u; u=u->next)
474 for (m=first_procrec; m; m=m->next)
475 if (m->pid == u->login_pid)
478 u->total_time = m->ctim;
480 DBG("** %d is login process for %s@%s\n", m->pid, u->user_name, u->line);
486 for (m=first_procrec; m; m=m->next)
488 for (n=first_procrec; n; n=n->next)
489 if (n->pid == m->ppid && n->user)
491 u = m->user = n->user;
492 m->distance = n->distance + 1;
493 if (!u->proc || u->proc->distance < m->distance)
496 DBG("** %d at distance %d for %s@%s\n", m->pid, m->distance, u->user_name, u->line);
502 /* Process the remaining processes */
503 for(m=first_procrec; m; m=m->next)
507 for(u=first_utrec; u; u=u->next)
508 if (u->uid == m->uid)
512 if (m->uid < 2 || m->ppid != 1)
514 u = xmalloc(sizeof(struct utrec));
515 bzero(u, sizeof(*u));
516 u->next = first_utrec;
518 pw = getpwuid(u->uid = m->uid);
520 strcpy(u->user_name, pw->pw_name);
522 sprintf(u->user_name, "<%d>", m->uid);
527 u->total_time += m->ctim;
529 DBG("** daemon %s (%s)\n", m->cmd_line, u->user_name);
534 comp(struct utrec *a, struct utrec *b)
539 k = strcmp(a->user_name, b->user_name);
542 if (!a->host_name[0] && b->host_name[0])
544 return strcmp(a->line, b->line);
550 struct utrec *fi = NULL;
551 struct utrec *z, **y, **m, **l;
577 printf("Name Li MD %-*s LogT IdleT RunT Command\n", maxhost, "From");
578 for(u=first_utrec; u; u=u->next)
581 strcpy(u->line, "--");
582 printf("%-8.8s %-3s %c%c %-*.*s ",
586 u->detach ? 'D' : ' ',
592 if (u->login_time > now)
594 puttime(now - u->login_time);
596 if (u->click_time > now)
598 if (now - u->click_time >= 60)
599 puttime(now - u->click_time);
608 puttime((unsigned)u->total_time/(unsigned)HZ);
610 for (c = u->proc->cmd_line; *c; c++)
612 if (*c < 0x20 || (*c >= 0x7f && *c < 0xa0))
619 printf(" <nothing>");
634 printf("!!! There %s %d zombie%s walking around.\n",
635 zombies == 1 ? "is" : "are",
637 zombies == 1 ? "" : "s" );