]> mj.ucw.cz Git - ywho.git/blob - ywho.c
See changelogs...
[ywho.git] / ywho.c
1 /*
2  *      Extended `who' command, version 1.9.
3  *
4  *      (c) 1996--2001 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
5  *
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.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <utmp.h>
16 #include <time.h>
17 #include <dirent.h>
18 #include <pwd.h>
19 #include <sys/stat.h>
20 #include <asm/param.h>
21 #include <sys/ioctl.h>
22 #include <ctype.h>
23
24 #undef DEBUG
25 #define MAXHOSTSIZE 24
26
27 struct procrec {
28   struct procrec *next;
29   int pid, ppid, pgrp, sess;
30   int usr, state;
31   time_t ctim, tim;
32   char cmd_line[1];
33 };
34
35 static struct procrec *first_procrec;
36
37 struct utrec {
38   struct utrec *next;
39   pid_t login_pid;
40   char line[UT_LINESIZE];
41   time_t login_time, click_time, total_time;
42   char user_name[UT_NAMESIZE+1];
43   int usr;
44   char host_name[MAXHOSTSIZE+1];
45   int mesg, detach;
46   struct procrec *proc;
47 };
48
49 static struct utrec *first_utrec;
50 static int usercnt, zombies, maxhost = 10;
51 static time_t now;
52
53 static void *
54 xmalloc(int i)
55 {
56   void *z;
57   z = malloc(i);
58   if (!z)
59     {
60       fprintf(stderr, "ywho: out of memory!\n");
61       exit(1);
62     }
63   return z;
64 }
65
66 static void
67 strcpy_padded(char *to, char *from, int size)
68 {
69   memcpy(to, from, size);
70   to[size] = 0;
71 }
72
73 static void
74 readutmp(void)
75 {
76   static struct utmp *ut;
77   static struct passwd *pw;
78   char *z;
79   utmpname(UTMP_FILE);
80   while (ut = getutent())
81     if (ut->ut_type == USER_PROCESS && ut->ut_user[0])
82       {
83         struct utrec *u = xmalloc(sizeof(struct utrec));
84         struct stat st;
85         char device[32];
86         size_t hlen;
87         u->next = first_utrec;
88         first_utrec = u;
89         u->login_pid = ut->ut_pid;
90         z = ut->ut_line;
91         if (!strncmp(z, "tty", 3))
92           {
93             if (z[3] >= '0' && z[3] <= '9')
94               sprintf(u->line, "c%s", z+3);
95             else
96               strcpy(u->line, z+3);
97           }
98         else if (!strncmp(z, "pts/", 4))
99           strcpy(u->line, z+4);
100         u->login_time = ut->ut_time;
101         strcpy_padded(u->user_name, ut->ut_user, UT_NAMESIZE);
102         pw = getpwnam(u->user_name);
103         if (pw)
104           u->usr = pw->pw_uid;
105         else
106           u->usr = -1;
107         strcpy_padded(u->host_name, ut->ut_host,
108           (MAXHOSTSIZE > UT_HOSTSIZE ? UT_HOSTSIZE : MAXHOSTSIZE));
109         if ((hlen = strlen(u->host_name)) > maxhost)
110           maxhost = hlen;
111         strcpy(u->line, ut->ut_line);
112         if (*u->line)
113           {
114             char *z = u->line;
115             if (!strncmp(z, "tty", 3))
116               {
117                 if (isdigit(z[0]))
118                   sprintf(u->line, "c%s", z+3);
119                 else
120                   strcpy(u->line, z+3);
121               }
122             else if (!strncmp(z, "pts/", 4))
123               strcpy(u->line, z+4);
124             else
125               strcpy(u->line, z);
126           }
127         strcpy(device, "/dev/");
128         strcat(device, ut->ut_line);
129         if (stat(device, &st))
130           u->mesg = 2;
131         else
132           {
133             u->mesg = !!(S_IWGRP & st.st_mode);
134             u->click_time = st.st_atime;
135 #if 0
136             if (u->click_time < st.st_mtime)
137               u->click_time = st.st_mtime;
138             if (u->click_time < st.st_ctime)
139               u->click_time = st.st_ctime;
140 #endif
141           }
142         u->detach = 0;
143         usercnt++;
144 #ifdef DEBUG
145         printf("UT %s %s %d %d\n", u->user_name, u->host_name,
146                u->usr, u->login_pid);
147 #endif
148       }
149   endutent();
150 }
151
152 static void
153 puttime(int s)
154 {
155   int d, h, m;
156   if (s < 100)
157     printf("%4ds", s);
158   else
159     {
160       m = s/60;
161       s %= 60;
162       h = m/60;
163       m %= 60;
164       if (h < 100)
165         printf("%02d.%02d", h, m);
166       else
167         {
168           d = h/24;
169           h %= 24;
170           if (d < 100)
171             printf("%2dd%02d", d, h);
172           else
173             printf("%4dd", d);
174         }
175     }
176 }
177
178 static void
179 memory(unsigned int i)
180 {
181   if (i < 10240)
182     printf("%dB", i);
183   else
184     {
185       i = (i+511U)/1024U;
186       if (i < 2048)
187         printf("%dK", i);
188       else
189         {
190           i = (i+511)/1024;
191           if (i < 2048)
192             printf("%dM", i);
193           else
194             printf("%dG", (i+511U)/1024U);
195         }
196     }
197 }
198
199 static void
200 dispsys(void)
201 {
202   FILE *f;
203   char line[256];
204   char *z, *w;
205   w = NULL;
206   if (f = fopen("/proc/version", "r"))
207     {
208       fgets(line, 256, f);
209       z = strchr(line, '(');
210       if (z)
211         {
212           while (z != line && z[-1] == ' ')
213             z--;
214         }
215       else
216         z = line + strlen(line) - 1;
217       *z = 0;
218       z = strstr(line, " version");
219       if (z)
220         memmove(z, z+8, strlen(z+8)+1);
221       printf(line);
222       fclose(f);
223     }
224   if (f = fopen("/proc/cpuinfo", "r"))
225     {
226       char a[32], b[32], c[32];
227       strcpy(a, "???");
228       b[0] = 0;
229       c[0] = 0;
230       for(;;)
231         {
232           fgets(line, 256, f);
233           if (feof(f))
234             break;
235           if (!strncmp(line, "cpu\t\t: ", 7))
236             {
237               z = line+7;
238               w = a;
239             }
240           else if (!strncmp(line, "cpu family\t: ",13))
241             {
242               z = line+13; strcpy(z + 1, "86\n");
243               w = a;
244             }
245           else if (!strncmp(line, "model\t\t: ", 9))
246             {
247               z = line+9;
248               w = b;
249             }
250           else if (!strncmp(line, "model name\t: ", 13))
251             {
252               z = line+13;
253               if (!strncmp(z, "AMD-K6", 6))
254                 {
255                   z[3] = ' ';
256                   if ((w = strstr(z, "tm w/ multimedia")))
257                     strcpy(w, "-1\n");
258                   else if ((w = strstr(z, "(tm) 3D")))
259                     strcpy(w, "-2\n");
260                 }
261               w = b;
262             }
263           else if (!strncmp(line, "mask\t\t: ", 8))
264             {
265               z = line+8;
266               w = c;
267             }
268           else
269             z = NULL;
270           if (z)
271             {
272               line[strlen(line)-1] = 0;
273               strcpy(w, z);
274               if (!strcmp(w, "Unknown"))
275                 w[0] = 0;
276             }
277         }
278       printf(" on %s", *b ? b : a);
279       if (*c)
280         printf("/%s", c);
281       fclose(f);
282     }
283   if (f = fopen("/proc/uptime", "r"))
284     {
285       int p,q,p1,q1;
286       fgets(line, 256, f);
287       sscanf(line, "%d.%d%d.%d", &p, &p1, &q, &q1);
288       printf(" up ");
289       puttime(p);
290       printf(" run ");
291       puttime(p-q);
292       fclose(f);
293     }
294   {
295     struct tm *tm;
296     char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
297     now = time(NULL);
298     tm = localtime(&now);
299     printf(" on %s %02d-%02d-%02d %02d.%02d:%02d", days[tm->tm_wday], tm->tm_mday, tm->tm_mon+1,
300            tm->tm_year%100, tm->tm_hour, tm->tm_min, tm->tm_sec);
301     printf(" (%u UE)\n", (unsigned int) now);
302   }
303   if (f = fopen("/proc/loadavg", "r"))
304     {
305       int i = 3;
306       fgets(line, 256, f);
307       *strchr(line, '\n') = 0;
308       z = line-1;
309       while (i-- && z)
310         {
311           *z = '/';
312           z = strchr(z, ' ');
313         }
314       if (z)
315         {
316           char *k;
317           *z++ = 0;
318           k = strchr(z, ' ');
319           if (k)
320             *k = 0;
321           printf("R/T=%s, ", z);
322         }
323       else
324         z = line;
325       printf("LAV=%s", line);
326       fclose(f);
327     }
328   if (f = fopen("/proc/meminfo", "r"))
329     {
330       int free, buffers, stotal, sfree;
331       free = buffers = stotal = sfree = 0;
332       while (fgets(line, 256, f))
333         {
334           if (!strncmp(line, "MemFree:", 8))
335             sscanf(line+8, "%d", &free);
336           else if (!strncmp(line, "Buffers:", 8))
337             sscanf(line+8, "%d", &buffers);
338           else if (!strncmp(line, "SwapTotal:", 10))
339             sscanf(line+10, "%d", &stotal);
340           else if (!strncmp(line, "SwapFree:", 9))
341             sscanf(line+9, "%d", &sfree);
342         }
343       printf(", free ");
344       memory(1024*(free+buffers));
345       printf(" of RAM");
346       if (stotal != sfree)
347         {
348           printf(", used ");
349           memory(1024*(stotal - sfree));
350           printf(" of swap");
351         }
352     }
353   printf(", %d user%s.\n", usercnt, usercnt == 1 ? "" : "s");
354 }
355
356 static void
357 readproc(void)
358 {
359   DIR *d;
360   struct dirent *e;
361   struct winsize win;
362   unsigned cmdcols = 0;
363 #define LEFTSIZE 29
364   
365   if (!ioctl(1, TIOCGWINSZ, &win) && win.ws_col >= LEFTSIZE + maxhost + 10 && win.ws_col < 1024)
366     cmdcols = win.ws_col;
367   else cmdcols = 80;
368     cmdcols -= LEFTSIZE + maxhost;
369   if (d = opendir("/proc"))
370     {
371       while (e = readdir(d))
372         {
373           int i;
374           if (sscanf(e->d_name, "%d", &i)==1)
375             {
376               struct procrec *p = xmalloc(sizeof(struct procrec) + cmdcols);
377               struct stat st;
378               char name[256], eman[1024];
379               int fil;
380               p->pid = i;
381               strcpy(name, "/proc/");
382               strcat(name, e->d_name);
383               if (!stat(name, &st))
384                 {
385                   p->next = first_procrec;
386                   first_procrec = p;
387                   p->usr = st.st_uid;
388                   strcpy(eman, name);
389                   strcat(eman, "/cmdline");
390                   if ((fil = open(eman, O_RDONLY)) >= 0)
391                     {
392                       int z = read(fil, p->cmd_line, cmdcols);
393                       if (z < 0)
394                         z = 0;
395                       p->cmd_line[z] = 0x91;
396                       close(fil);
397                     }
398                   strcpy(eman, name);
399                   strcat(eman, "/stat");
400                   if ((fil = open(eman, O_RDONLY)) >= 0)
401                     {
402                       int z = read(fil, eman, 1023);
403                       char *k;
404                       int trash, utim, stim, cutim, cstim;
405                       if (z < 0)
406                         z = 0;
407                       eman[z] = 0;
408                       k = strchr(eman, ')');
409                       if (k)
410                         {
411                           p->state = k[2];
412                           if (p->state == 'Z')
413                             zombies++;
414                           sscanf(k+4, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
415                                  &p->ppid, &p->pgrp, &p->sess, &trash, &trash,
416                                  &trash, &trash, &trash, &trash, &trash, &utim,
417                                  &stim, &cutim, &cstim);
418                         }
419                       p->tim = utim + stim;
420                       p->ctim = cutim + cstim;
421                       close(fil);
422                     }
423                 }
424             }
425         }
426       closedir(d);
427     }
428   else
429     {
430       perror("unable to scan /proc");
431       exit(1);
432     }
433 }
434
435 static void
436 solve(void)
437 {
438   struct utrec *u = first_utrec;
439   struct procrec *m, *n, *o, *q;
440   struct passwd *pw;
441   while (u)
442     {
443       m = first_procrec;
444       while (m && m->pid != u->login_pid)
445         m = m->next;
446       o = m;
447       if (m)
448         {
449         retry:
450           n = first_procrec;
451           q = o;
452           while (n)
453             {
454               if (q->sess == n->sess)
455                 n->usr = -1;
456               if (q->pid == n->ppid && n->usr == -1)
457                 {
458                   o = n;
459                   goto retry;
460                 }
461               n = n->next;
462             }
463         }
464       u->proc = o;
465       if (m)
466         u->total_time = m->ctim;
467 #ifdef DEBUG
468       if (o)
469         {
470           printf("%s %s\n", u->user_name, o->cmd_line);
471         }
472 #endif
473       u = u->next;
474     }
475   for(u=first_utrec; u; u=u->next)
476     {
477       for(m=first_procrec; m; m=m->next)
478         if (u->usr == m->usr)
479           {
480             m->usr = -1;
481             u->detach++;
482 #ifdef DEBUG
483             printf("** %s %s\n", u->user_name, m->cmd_line);
484 #endif
485           }
486     }
487   for(m=first_procrec; m; m=m->next)
488     if (m->usr >= 2 && m->ppid == 1) /* Exclude no_user,root,bin */
489       {
490         for(u=first_utrec; u; u=u->next)
491           if (u->usr == m->usr)
492             break;
493         if (!u)
494           {
495             u = xmalloc(sizeof(struct utrec));
496             u->next = first_utrec;
497             first_utrec = u;
498             u->login_pid = 0;
499             u->line[0] = 0;
500             pw = getpwuid(u->usr = m->usr);
501             if (pw)
502               strcpy(u->user_name, pw->pw_name);
503             else
504               sprintf(u->user_name, "<%d>", m->usr);
505             u->host_name[0] = 0;
506             u->mesg = 3;
507             u->detach = 0;
508             u->proc = NULL;
509             u->total_time = 0;
510           }
511         if (!u->proc)
512           u->proc = m;
513         u->total_time += m->ctim;
514         u->detach ++;
515 #ifdef DEBUG
516         printf("// %s %s\n", u->user_name, m->cmd_line);
517 #endif
518       }
519 }
520
521 static inline int
522 comp(struct utrec *a, struct utrec *b)
523 {
524   int k;
525   if (b->mesg == 3)
526     return -1;
527   k = strcmp(a->user_name, b->user_name);
528   if (k)
529     return k;
530   if (!a->host_name[0] && b->host_name[0])
531     return -1;
532   return strcmp(a->line, b->line);
533 }
534
535 static void
536 sort(void)
537 {
538   struct utrec *fi = NULL;
539   struct utrec *z, **y, **m, **l;
540   l = &fi;
541   while (first_utrec)
542     {
543       y = &first_utrec;
544       m = y;
545       while (z = *y)
546         {
547           if (comp(z, *m) < 0)
548             m = y;
549           y = &z->next;
550         }
551       z = *m;
552       *m = z->next;
553       *l = z;
554       z->next = NULL;
555       l = &z->next;
556     }
557   first_utrec = fi;
558 }
559
560 static void
561 show(void)
562 {
563   struct utrec *u;
564   unsigned char *c;
565   printf("Name     Li  MD %-*s LogT  IdleT RunT  Command\n", maxhost, "From");
566   for(u=first_utrec; u; u=u->next)
567     {
568       if (!u->line[0])
569         strcpy(u->line, "--");
570       printf("%-8.8s %-3s %c%c %-*.*s ",
571              u->user_name,
572              u->line,
573              u->mesg ? ' ' : '-',
574              u->detach ? 'D' : ' ',
575              maxhost,
576              maxhost,
577              u->host_name);
578       if (u->mesg <= 1)
579         {
580           if (u->login_time > now)
581             u->login_time = now;
582           puttime(now - u->login_time);
583           putchar(' ');
584           if (u->click_time > now)
585             u->click_time = now;
586           if (now - u->click_time >= 60)
587             puttime(now - u->click_time);
588           else
589             printf("     ");
590         }
591       else
592         printf("?????      ");
593       if (u->proc)
594         {
595           putchar(' ');
596           puttime((unsigned)u->total_time/(unsigned)HZ);
597           putchar(' ');
598           for (c = u->proc->cmd_line; *c != 0x91; c++)
599             {
600               if (*c < 0x20 || (*c >= 0x7f && *c < 0xa0))
601                 putchar(' ');
602               else
603                 putchar(*c);
604             }
605         }
606       else
607         printf(" <nothing>");
608       putchar('\n');
609     }
610 }
611
612 int main(void)
613 {
614   readutmp();
615   dispsys();
616   readproc();
617   solve();
618   sort();
619   show();
620   if (zombies)
621     {
622       printf("!!! There %s %d zombie%s walking around.\n",
623              zombies == 1 ? "is" : "are",
624              zombies,
625              zombies == 1 ? "" : "s" );
626     }
627   return 0;
628 }