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