home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: alt.sources
- From: 231b3679@fergvax.unl.edu (Mike Gleason)
- Subject: whom.c - another who (and whoson) replacement
- Message-ID: <231b3679.672991846@fergvax>
- Date: 30 Apr 91 06:10:46 GMT
-
- I had also been working on a 'who' program. I tried yesterday's posting,
- whoson, but it didn't work because my unix host uses fopen(file, "r") for
- both binary and text files, and the author of whoson used "rb". Anyway, you
- may want to try this out. Along with the usual stuff 'who' prints out, it
- also prints the user's full names. You could get this effect by just typing
- 'finger', but this program is much, much faster. Whom will also tell you
- which tty's are writeable (so you can phone, talk, or write them) by denoting
- them with an asterisk. Here is a sample of the output:
-
- User: TTY: Where: Time On: Idle: Name:
- 252u3693 16* 3:32 0 Brian Guenther
- tdavis p1 mod801.unomaha.e 0:47 Thomas Davis
- 252u3744 p2* tsx-wsec103.unl. 0:34 Jerry Annin
- gunnit p3 mandala.unl.edu 1:41 1:31 Gunnit S. Khurana
- 231b3630 p4* tsx-wsec104.unl. 1:50 35 Peter Fields
- chaddan p5* tsx-wsec103.unl. 0:40 Christopher Neal Haddan
-
- I have spent way too much time optimizing this silly thing, so it is quite
- quick. If you use it, please let me know, and I'll keep working on it.
-
- ---------cut here--------
- /*
- * NCEMRS whom (C) 1991 Mike Gleason, NCEMRSoft.
- * version 1.0 -- 08 Mar 91
- * version 2.0 -- 18 Apr 91
- * version 2.1 -- 29 Apr 91
- * Compile with CC for the smallest executable.
- * Until 10 May 91, I am emailable at 231b3679@fergvax.unl.edu.
- */
-
-
- #include <stdio.h>
- #include <time.h>
- #include <string.h>
-
- #ifdef THINK_C
- #include "utmp.h"
- #include <stdlib.h>
- #include "stat.h"
- #else
- #include <utmp.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #endif
-
- /* The only things you should need to configure are:
- * PASSWORD_FILE
- * DFILE
- * READ_BINARY
- * WRITE_BINARY
- */
-
- /* I developed this using a Macintosh and Think C, and it needs the
- binary mode specifier char. I think all non-unix machines will
- need that too, but I doubt this will be run on a non-unix machine
- anyway. */
- #ifdef THINK_C
- #define WRITE_BINARY "wb"
- #define READ_BINARY "rb"
- #else
- #define WRITE_BINARY "w" /* On my unix host, you don't use the b's */
- #define READ_BINARY "r"
- #endif
-
- #define ADD_SPACE *lyne++ = ' '
- #define PUBLIC_WRITE_PERM 00002 /* write permission: other */
-
-
-
- #ifndef NO_REAL_NAMES
-
- /* One of whom's best features is that it can print out the real name
- of each user along with the other stuff. There are a few minor
- disadvantages. (1), it needs to munge through the password file,
- and (2) it needs to keep it's own private data file on disk somewhere.
- If your password file does not have the real names of the users
- in it, or if you can't spare the disk space (but it's not THAT big),
- or just don't like it, then define NO_REAL_NAMES. The reason for
- the private datafile is because Whom uses an optimized version of
- the password file so it rapidly find information about any user.
- If it didn't have this file, you might as well use finger, because
- it will take forever to do this otherwise. */
-
- /* Point me to the location of your system's password file. I need that
- some stuff I need to make the data file, namely the login and real
- name of each user. */
- #define PASSWORD_FILE "/etc/passwd"
-
- /* Point me to where you want to store the data file. The size of this
- will vary on the total number of users, but my machine has about 700
- users with only a 30k data file. The data file will be sorted and
- each record will be the same size so we can use lightning fast
- searching. Programs like finger and getpwnam() probably have to
- search the entire passwd file every time through because it isn't
- sorted, and each line is of variable length. For my machine it could
- take up to 700 searches just to find one user with those programs,
- but for this little ditty, it would only take a maximum of 10
- searches. The ideal path for this file would be in the /etc direcotry
- along with the passwd file, but my system admins don't like me
- writing stuff there! */
-
- #define DFILE "/u3/231b3679/.whom"
- #define MAX_NAME_LEN 30
- #define MAX_LOGIN_LEN 8
- #define COLONS_TO_SKIP 4 /* Skip this many fields in passwd file */
- typedef struct
- {
- char login[MAX_LOGIN_LEN + 2]; /* leave room for ' \0'*/
- char name[MAX_NAME_LEN + 2]; /* leave room for ' \0' */
- } User;
-
- #endif
-
-
-
- /* These should be declared in utmp.h, but I've found an exception so... */
- #ifndef UTMP_FILE
- #define UTMP_FILE "/etc/utmp"
- #endif
-
- #ifndef WTMP_FILE
- #define WTMP_FILE "/usr/adm/wtmp"
- #endif
-
- /* Bonus Features:
- * 1. #define ANSI, and when whom does it's thang, it will first
- * clear the screen and print the header line in boldface (oooh).
- *
- * 2. When running the program, if you pass an arbitrary argument
- * (ex. whom -useWtmpDude) will try to use the wtmp file.
- */
-
- /* Prototypes */
- extern void *bsearch();
- extern void *malloc();
- char *strnpcat (/* dst, src, howMany */);
-
-
-
- /* main, finally! */
- int main (argc, argv)
- int argc;
- char **argv;
- {
- FILE *in, *dev;
- char fname[31], dname[31], str[31];
- char *lyne, line[128];
- struct utmp info;
- short wtmp, writeable;
- time_t Now;
- int result;
- struct stat stbuf;
- #ifndef NO_REAL_NAMES
- User *Users, *u;
- long numUsers;
- #endif
-
- if ((wtmp = (argc != 1)))
- strcpy (fname, WTMP_FILE);
- else
- strcpy (fname, UTMP_FILE);
-
- if (!(in = fopen (fname, READ_BINARY)))
- {
- fprintf (stderr, "%s: Could not open the file \"%s\".\n", argv[0], fname
- );
- exit (1);
- }
-
- #ifndef NO_REAL_NAMES
-
- result = OpenDataFile (&Users, &numUsers);
- if (result < 0)
- {
- Thrash ();
- result = OpenDataFile (&Users, &numUsers);
- }
-
- #ifndef ANSI
- printf ("\nUser: TTY: Where: Time On: Idle: %s\n",
- result==0 ? "Name:" : "");
- #else
- /* clear screen, home cursor, and turn bold face on. */
- printf ("\n\033[2J \033[H\033[1m");
- printf ("User: TTY: Where: Time On: Idle: %s\033[0m\n",
- result==0 ? "Name:" : "");
- #endif
-
- #else
-
- #ifndef ANSI
- printf ("\nUser: TTY: Where: Time On: Idle:\n");
- #else
- /* clear screen, home cursor, and turn bold face on. */
- printf ("\n\033[2J \033[H\033[1m");
- printf ("User: TTY: Where: Time On: Idle:\033[0m\n");
- #endif
-
- #endif
-
- (void) time (&Now);
-
- while ((fread (&info, (long) sizeof (info), 1, in)) == 1L)
- {
- if (!*info.ut_name)
- continue;
-
- lyne = (char *) line;
- *lyne = '\0';
- lyne = strnpcat (lyne, info.ut_name, 8L);
- ADD_SPACE; ADD_SPACE;
-
- writeable = 0;
-
- strcpy (dname, "/dev/");
- strcat (dname, info.ut_line); /* form the full path of tty */
- stat (dname, &stbuf);
- writeable = stbuf.st_mode | PUBLIC_WRITE_PERM;
-
- if (*info.ut_line == 't') /* is it in the form ttyxx? */
- {
- /* This assumes that after 'tty', there are only two other
- characters. */
- if (writeable)
- info.ut_line[5] = '*';
-
- lyne = strnpcat (lyne, info.ut_line+3, 3L);
- }
- else
- lyne = strnpcat (lyne, info.ut_line, 3L);
- /* else its probably 'console' */
-
- ADD_SPACE; ADD_SPACE;
-
- lyne = strnpcat (lyne, info.ut_host, 16L);
- ADD_SPACE; ADD_SPACE; ADD_SPACE;
-
- TimeOn (Now - info.ut_time, str);
- lyne = strnpcat (lyne, str, 10L);
-
- IdleTime ((Now - stbuf.st_mtime), str);
- lyne = strnpcat (lyne, str, 7L);
-
- #ifndef NO_REAL_NAMES
-
- if (result == 0)
- {
- info.ut_name[8] = '\0';
- u = (User *) bsearch (info.ut_name, Users, numUsers,
- (long) sizeof (User), strcmp);
-
- if (u)
- strcpy (str, u->name);
- else
- strcpy (str, "(Unknown)");
-
- lyne = strnpcat (lyne, str, 28L);
- }
-
- #endif
- puts (line); /* finally, dump the whole line to stdout */
- }
-
- fputc ('\n', stdout);
- fclose (in);
- } /* main */
-
-
-
-
- /* strnpcat: given two strings, this function will copy up to 'howMany'
- characters to the destination. If the source string is shorter than
- 'howMany' characters, it will pad the destinaton string with spaces
- until it's length is howMany. In addition, this will also return the
- pointer where we left off. It'd be silly to use strcat all the time,
- since every time you called strcat it would have to loop through the
- whole string just to find the end. */
-
- char *strnpcat (dst, src, howMany)
- register char *dst, *src;
- long howMany;
- {
- register int echoSpaces;
-
- for (echoSpaces = 0; howMany > 0; dst++, src++, --howMany)
- {
- if (!*src)
- echoSpaces = 1;
- if (echoSpaces)
- *dst = ' ';
- else
- *dst = *src;
- }
- *dst = '\0';
- return (dst);
- } /* strnpcat */
-
-
-
-
-
- int TimeOn (tyme, tstr)
- long tyme;
- char *tstr;
- {
- long hr, min, day;
-
- tyme /= 60L;
- day = tyme / 1440L;
- hr = (tyme - day * 1440L) / 60L;
- min = (tyme - (day * 1440L) - (hr * 60L));
-
- sprintf (tstr, "%ld:%02ld", hr, min);
- } /* TimeOn */
-
-
-
-
-
- int IdleTime (tyme, tstr)
- long tyme;
- char *tstr;
- {
- long hr, min, day;
-
- if (((tyme + 30L) / 60L) > 0L)
- {
- tyme /= 60L;
- day = tyme / 1440L;
- hr = (tyme - day * 1440L) / 60L;
- min = (tyme - (day * 1440L) - (hr * 60L));
- if (hr > 0L)
- sprintf (tstr, "%ld:%02ld", hr, min);
- else
- sprintf (tstr, "%ld", min);
- }
- else *tstr = '\0';
- } /* IdleTime */
-
-
-
-
-
-
- #ifndef NO_REAL_NAMES
-
- int OpenDataFile (Users, numUsers)
- User **Users;
- long *numUsers;
- {
- FILE *data;
-
- if (!(data = fopen (DFILE, READ_BINARY)))
- return (-1);
-
- if (fread (numUsers, (long) sizeof (*numUsers), 1L, data) != 1L)
- return (1);
-
- *Users = (User *) malloc ((long) sizeof (User) * (*numUsers));
- if (!*Users)
- return (2);
-
- if (fread (*Users, (long) sizeof (User) * (*numUsers), 1L, data) != 1L)
- return (3);
-
- fclose (data);
- return (0); /* noErr */
- } /* OpenDataFile */
-
-
-
-
-
- /* Thrash: This creates the data file, which is needed so you can print
- the real names of the users along with their logins. This is
- extremely useful on machines (like mine) whose logins are mostly
- numbers or alphanumeric garbage. */
-
- int Thrash ()
- {
- FILE *passwd, *out;
- long nUsers, i;
- char lyne[256];
- User *users;
- int skippedcolons, j;
- register char *p, *q;
-
- passwd = fopen (PASSWORD_FILE, "r");
- if (!passwd)
- return (0);
-
- nUsers = 0L;
- while (fgets (lyne, (int) sizeof (lyne), passwd))
- nUsers++;
-
- rewind (passwd);
-
- users = (User *) calloc (nUsers, (long) sizeof (User));
- if (!users)
- return (0);
-
- for (i = 0; fgets (lyne, (int) sizeof (lyne), passwd); i++)
- {
- p = lyne;
-
- q = users[i].login;
- while (*p != ':') /* get login */
- *q++ = *p++;
- *q = '\0'; /* add terminating null */
-
- /* After getting the login, skip over the fields in between the
- login and real name (encoded password, userid, groupid). */
- skippedcolons = 0;
- while (*p)
- {
- if (*p++ == ':')
- skippedcolons++;
- if (skippedcolons >= COLONS_TO_SKIP)
- break;
- }
- if (skippedcolons < COLONS_TO_SKIP)
- return (0);
-
- /* Copy stuff until we hit a colon or a comma. I only want to
- keep the names, and on our machine at least, after the real
- name there are commas followed by junk like office address
- and such ("Herbie H. Husker,104 Nebraska Union,2-3970").
- This example would only copy "Herbie H. Husker" to the name
- field. */
- for (j = 0, q = users[i].name;
- (*p && j < MAX_NAME_LEN && *p != ':' && *p != ',');
- p++, q++, j++)
- {
- *q = *p;
- }
-
- *q = '\0'; /* add null terminator */
- }
-
- fclose (passwd);
-
- qsort (users, i, (long) sizeof (User), strcmp);
-
- out = fopen (DFILE, WRITE_BINARY);
- if (!out)
- return (0);
- if (fwrite (&i, (long) sizeof (i), 1L, out) != 1L)
- return (0);
- if (fwrite (users, (long) sizeof (User) * i, 1L, out) != 1L)
- return (0);
- fclose (out);
- return (1);
- } /* Thrash */
-
- #endif
-
- /* eof */
-
-
-