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