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