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