rewrite to handle large uids and make dev naming self contained
[unix-history] / usr / src / usr.bin / lastcomm / lastcomm.c
index 2c7ac7a..ed30c86 100644 (file)
-static char *sccsid = "@(#)lastcomm.c  4.3 (Berkeley) %G%";
-#
+#ifndef lint
+static char *sccsid = "@(#)lastcomm.c  4.6 (Berkeley) %G%";
+#endif
 
 /*
  * last command
  */
 
 /*
  * last command
  */
+#include <sys/param.h>
+#include <sys/acct.h>
+#include <sys/file.h>
 
 
-# include <stdio.h>
-# include <sys/types.h>
-# include <sys/acct.h>
-# include <signal.h>
-# include <pwd.h>
-# include <stat.h>
+#include <stdio.h>
+#include <pwd.h>
+#include <stat.h>
+#include <utmp.h>
+#include <struct.h>
+#include <ctype.h>
 
 
-# define N_USER                1000
+struct acct buf[DEV_BSIZE / sizeof (struct acct)];
 
 
-struct acct    acct_buff [BUFSIZ / sizeof (struct acct)];
+time_t expand();
+char   *flagbits();
+char   *getname();
+char   *getdev();
 
 
-char   yes   = 1,
-       no    = 0,
-
-       user_list [1000][9];
-
-time_t expand ();
-
-struct passwd
-       *passwd,
-       *getpwent ();
-
-struct stat stat_buff;
-
-main (argc, argv)
-char **argv;
+main(argc, argv)
+       char *argv[];
 {
 {
-       char    acct_desc,
-               *p;
-
-       long    i,
-               j,
-               i_block,
-               n_blocks,
-               n_byte,
-               n_entry;
-
-       float   x;
+       register int bn, cc;
+       register struct acct *acp;
+       int fd;
+       struct stat sb;
 
 
-/*
- * set up user names
- */
-       while (passwd = getpwent ())
-       {
-               if (user_list[passwd->pw_uid][0]==0)
-                       move (passwd->pw_name, user_list [passwd->pw_uid]);
+       fd = open("/usr/adm/acct", O_RDONLY);
+       if (fd < 0) {
+               perror("/usr/adm/acct");
+               exit(1);
        }
        }
+       fstat(fd, &sb);
+       for (bn = btodb(sb.st_size) - 1; bn >= 0; bn--) {
+               lseek(fd, bn * DEV_BSIZE, L_SET);
+               cc = read(fd, buf, DEV_BSIZE);
+               if (cc < 0) {
+                       perror("read");
+                       break;
+               }
+               acp = buf + (cc / sizeof (buf[0])) - 1;
+               for (; acp >= buf; acp--) {
+                       register char *cp;
+                       time_t x =
+                           expand(acp->ac_utime) + expand(acp->ac_stime);
 
 
-       acct_desc = open ("/usr/adm/acct", 0);
-       if (acct_desc < 0)
-       {
-               perror ("/usr/adm/acct");
-               return;
-       }
-       fstat (acct_desc, &stat_buff);
-       n_blocks = (stat_buff.st_size + BUFSIZ - 1) / BUFSIZ;
-
-       /*
-        * read one block's worth
-        */
-       for (i_block = n_blocks - 1; i_block >= 0; i_block--)
-       {
-               lseek (acct_desc, i_block * BUFSIZ, 0);
-               n_byte = read (acct_desc, acct_buff, BUFSIZ);
-               n_entry = n_byte / sizeof acct_buff [0];
-               for (i = n_entry - 1; i >= 0; i--)
-               {
-                       if (!*user_list [acct_buff [i].ac_uid]) continue;
-                       /*
-                        * get the times
-                        */
-                       x =     expand (acct_buff [i].ac_utime)
-                               +
-                               expand (acct_buff [i].ac_stime);
-                       /*
-                        * null terminate the command name
-                        */
-                       acct_buff [i].ac_comm [10] = 0;
-                       /*
-                        * replace missing command names with question marks
-                        */
-                       if (!*acct_buff [i].ac_comm)
-                       {
-                               move ("?", acct_buff [i].ac_comm);
-                       }
-                       /*
-                        * replace control characters with question marks
-                        */
-                       for (p = acct_buff [i].ac_comm; *p; p++)
-                       {
-                               if (*p < '!' || '~' < *p) *p = '?';
-                       }
-                       for (j = 1; j < argc; j++)
-                       {
-                               if
-                               (
-                                       equal (acct_buff [i].ac_comm, argv [j])
-                                       ||
-                                       equal
-                                       (
-                                       user_list [acct_buff [i].ac_uid],
-                                               argv [j]
-                                       )
-                               )
-                               {
-                                       break;
-                               }
-                       }
-                       if (argc == 1 || j != argc)
-                       {
-                               printf
-                               (
-                                       "%-10s %-8s %6.2f     %.16s\n",
-                                       acct_buff [i].ac_comm,
-                                       user_list [acct_buff [i].ac_uid],
-                                       x / 60.0,
-                                       ctime (&acct_buff [i].ac_btime)
-                               );
-                       }
+                       acp->ac_comm[10] = '\0';
+                       if (*acp->ac_comm == '\0')
+                               strcpy(acp->ac_comm, "?");
+                       for (cp = acp->ac_comm; *cp; cp++)
+                               if (iscntrl(*cp))
+                                       *cp = '?';
+                       if (!ok(argc, argv, acp) && argc != 1)
+                               continue;
+                       printf("%-*s %s %-*s %-*s %4d sec%s %.16s\n",
+                               fldsiz(acct, ac_comm), acp->ac_comm,
+                               flagbits(acp->ac_flag),
+                               fldsiz(utmp, ut_name), getname(acp->ac_uid),
+                               fldsiz(utmp, ut_line), getdev(acp->ac_tty),
+                               x, x > 1 || x == 0 ? "s" : " ",
+                               ctime(&acp->ac_btime));
                }
        }
 }
 
 time_t
 expand (t)
                }
        }
 }
 
 time_t
 expand (t)
-unsigned t;
+       unsigned t;
 {
        register time_t nt;
 
        nt = t & 017777;
        t >>= 13;
 {
        register time_t nt;
 
        nt = t & 017777;
        t >>= 13;
-       while (t)
-       {
+       while (t) {
                t--;
                nt <<= 3;
        }
        return (nt);
 }
 
                t--;
                nt <<= 3;
        }
        return (nt);
 }
 
-move (a, b)
-char *a, *b;
+char *
+flagbits(f)
+       register int f;
+{
+       register int i = 0;
+       static char flags[20];
+
+#define BIT(flag, ch)  flags[i++] = (f & flag) ? ch : ' '
+       BIT(ASU, 'S');
+       BIT(AFORK, 'F');
+       BIT(ACOMPAT, 'C');
+       BIT(ACORE, 'D');
+       BIT(AXSIG, 'X');
+       flags[i] = '\0';
+       return (flags);
+}
+
+ok(argc, argv, acp)
+       register int argc;
+       register char *argv[];
+       register struct acct *acp;
+{
+       register int j;
+
+       for (j = 1; j < argc; j++)
+               if (strcmp(getname(acp->ac_uid), argv[j]) &&
+                   strcmp(getdev(acp->ac_tty), argv[j]) &&
+                   strcmp(acp->ac_comm, argv[j]))
+                       break;
+       return (j == argc);
+}
+
+/* should be done with nameserver or database */
+
+struct utmp utmp;
+
+#define NUID   2048
+#define        NMAX    (sizeof (utmp.ut_name))
+
+char   names[NUID][NMAX+1];
+char   outrangename[NMAX+1];
+int    outrangeuid = -1;
+
+char *
+getname(uid)
+{
+       register struct passwd *pw;
+       static init;
+       struct passwd *getpwent();
+
+       if (uid >= 0 && uid < NUID && names[uid][0])
+               return (&names[uid][0]);
+       if (uid >= 0 && uid == outrangeuid)
+               return (outrangename);
+       if (init == 2) {
+               if (uid < NUID)
+                       return (0);
+               setpwent();
+               while (pw = getpwent()) {
+                       if (pw->pw_uid != uid)
+                               continue;
+                       outrangeuid = pw->pw_uid;
+                       strncpy(outrangename, pw->pw_name, NUID);
+                       endpwent();
+                       return (outrangename);
+               }
+               endpwent();
+               return (0);
+       }
+       if (init == 0)
+               setpwent(), init = 1;
+       while (pw = getpwent()) {
+               if (pw->pw_uid < 0 || pw->pw_uid >= NUID) {
+                       if (pw->pw_uid == uid) {
+                               outrangeuid = pw->pw_uid;
+                               strncpy(outrangename, pw->pw_name, NUID);
+                               return (outrangename);
+                       }
+                       continue;
+               }
+               if (names[pw->pw_uid][0])
+                       continue;
+               strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
+               if (pw->pw_uid == uid)
+                       return (&names[uid][0]);
+       }
+       init = 2;
+       endpwent();
+       return (0);
+}
+
+#include <sys/dir.h>
+
+#define N_DEVS         43              /* hash value for device names */
+#define NDEVS          500             /* max number of file names in /dev */
+
+struct devhash {
+       dev_t   dev_dev;
+       char    dev_name [fldsiz(utmp, ut_line) + 1];
+       struct  devhash * dev_nxt;
+};
+struct devhash *dev_hash[N_DEVS];
+struct devhash *dev_chain;
+#define HASH(d)        (((int) d) % N_DEVS)
+
+setupdevs()
 {
 {
-       while (*b++ = *a++);
+       register DIR * fd;
+       register struct devhash * hashtab;
+       register ndevs = NDEVS;
+       struct direct * dp;
+
+       if ((fd = opendir("/dev")) == NULL) {
+               perror("/dev");
+               return;
+       }
+       hashtab = (struct devhash *)malloc(NDEVS * sizeof(struct devhash));
+       if (hashtab == (struct devhash *)0) {
+               fprintf(stderr, "No mem for dev table\n");
+               return;
+       }
+       while (dp = readdir(fd)) {
+               if (dp->d_ino == 0)
+                       continue;
+               if (dp->d_name[0] != 't' && strcmp(dp->d_name, "console"))
+                       continue;
+               strncpy(hashtab->dev_name, dp->d_name, fldsiz(utmp, ut_line));
+               hashtab->dev_name[fldsiz(utmp, ut_line)] = 0;
+               hashtab->dev_nxt = dev_chain;
+               dev_chain = hashtab;
+               hashtab++;
+               if (--ndevs <= 0)
+                       break;
+       }
+       closedir(fd);
 }
 
 }
 
-equal (a, b)
-char *a, *b;
+char *
+getdev(dev)
+       dev_t dev;
 {
 {
-       for (;; a++, b++)
-       {
-               if (*a != *b) return no;
-               if (!*a)      return yes;
+       register struct devhash *hp, *nhp;
+       struct stat statb;
+       char name[fldsiz(devhash, dev_name) + 6];
+       static dev_t lastdev = (dev_t) -1;
+       static char *lastname;
+       int init = 0;
+
+       if (dev == NODEV)
+               return ("__");
+       if (dev == lastdev)
+               return (lastname);
+       if (!init) {
+               setupdevs();
+               init++;
+       }
+       for (hp = dev_hash[HASH(dev)]; hp; hp = hp->dev_nxt)
+               if (hp->dev_dev == dev) {
+                       lastdev = dev;
+                       return (lastname = hp->dev_name);
+               }
+       for (hp = dev_chain; hp; hp = nhp) {
+               nhp = hp->dev_nxt;
+               strcpy(name, "/dev/");
+               strcat(name, hp->dev_name);
+               if (stat(name, &statb) < 0)     /* name truncated usually */
+                       continue;
+               if ((statb.st_mode & S_IFMT) != S_IFCHR)
+                       continue;
+               hp->dev_dev = statb.st_rdev;
+               hp->dev_nxt = dev_hash[HASH(hp->dev_dev)];
+               dev_hash[HASH(hp->dev_dev)] = hp;
+               if (hp->dev_dev == dev) {
+                       dev_chain = nhp;
+                       lastdev = dev;
+                       return (lastname = hp->dev_name);
+               }
        }
        }
+       dev_chain = (struct devhash *) 0;
+       return ("??");
 }
 }