rsalz@bbn.com (Rich Salz) (03/02/88)
Submitted-by: Steven Grimm <koreth@ssyx.UCSC.EDU> Posting-number: Volume 13, Issue 77 Archive-name: budpak The "buddy system." This is a collection of utilities for monitoring other users on the system. It runs on 4.3BSD on a VAX, and 4.2BSD on a Sun 3/160 and an ISI Optimum V. I have no idea whether it works (or even compiles) on SysV or not. +New! Improved! Now 100% Artificial-+-+-----------------------------------+ |# # @@@ **** &&&&& $$$$$ % %| |Steven Grimm | |# # @ @ * * & $ % %+-+ ARPA: koreth@ssyx.ucsc.edu | |### @ @ **** &&&& $ %%%%%| | UUCP: ...!ucbvax!ucscc!ssyx!koreth| |# # @ @ * * & $ % %+-+ ______________________________| |# # @@@ * ** &&&&& $ % %| | |"Let's see what's out there."| +-----with NutraSour(TM)! No natural colors or preservatives!------------+ ------------ (chop here) ------------ # This is a shell archive. Remove anything before this line # then unpack it by saving it in a file and typing "sh file" # (Files unpacked will be owned by you and have default permissions). # This archive contains the following files: # ./MANIFEST # ./Makefile # ./aliases # ./budpak.1 # ./buds.1 # ./buds.c # ./cbuds.1 # ./cbuds.c # ./mbuds.1 # ./mbuds.c # ./monitor.1 # ./monitor.c # ./rbuds.1 # ./rbuds.c # ./wbuds.1 # ./wbuds.c # if `test ! -s ./MANIFEST` then echo "writing ./MANIFEST" cat > ./MANIFEST << '\Rogue\Monster\' This shar should contain the following files: Makefile - A makefile for BudPak. budpak.1 buds.1 cbuds.1 mbuds.1 monitor.1 rbuds.1 wbuds.1 - Documentation for the BudPak utilities. Use [nt]roff -man. buds.c cbuds.c mbuds.c monitor.c rbuds.c wbuds.c - BudPak source code. aliases - Aliases for setting "single" and "horny" flags (see buds.6) Read budpak.1 for an overview of the utilities provided. \Rogue\Monster\ else echo "will not over write ./MANIFEST" fi if `test ! -s ./Makefile` then echo "writing ./Makefile" cat > ./Makefile << '\Rogue\Monster\' # # BUDPAK 1.0 # # Written by Jon Luini (niteowl@ssyx.ucsc.edu) # ...!uunet!ucbvax!ucscc!ssyx!niteowl # and Steven Grimm (koreth@ssyx.ucsc.edu) # ...!uunet!ucbvax!ucscc!ssyx!koreth # CFLAGS= -g BINDIR= /usr/local BINARIES= buds cbuds mbuds monitor rbuds wbuds BudPak: $(BINARIES) buds : buds.c cc $(CFLAGS) buds.c -o buds cbuds: cbuds.c cc $(CFLAGS) cbuds.c -o cbuds mbuds: mbuds.c cc $(CFLAGS) mbuds.c -o mbuds monitor: monitor.c cc $(CFLAGS) monitor.c -o monitor rbuds: rbuds.c cc $(CFLAGS) rbuds.c -o rbuds wbuds: wbuds.c cc $(CFLAGS) wbuds.c -o wbuds \Rogue\Monster\ else echo "will not over write ./Makefile" fi if `test ! -s ./aliases` then echo "writing ./aliases" cat > ./aliases << '\Rogue\Monster\' alias single chmod g+x \`tty\` alias horny chmod o+x \`tty\` \Rogue\Monster\ else echo "will not over write ./aliases" fi if `test ! -s ./budpak.1` then echo "writing ./budpak.1" cat > ./budpak.1 << '\Rogue\Monster\' .TH BUDPAK 1 BUDPAK .UC 4 .SH NAME buds, wbuds, mbuds, monitor, rbuds, cbuds \- The Buddy System .SH SYNOPSIS .B buds [ .I options ] .PP .B wbuds .PP .B mbuds [ user1 user2 user3 ... ] .PP .B monitor [ .I options ] [ user1 user2 user3 ... ] .PP .B rbuds .PP .B cbuds .SH DESCRIPTION .I BudPak is a collection of silly utilities to monitor the activities of other users on the system. It consists of six programs of varying uselessness: .PP .TP .I buds This is the program that started it all. It was suggested by Mark Axelrod (deckard@ucscb.ucsc.edu), who was displeased with the .I whom utility's inability to handle large alternate usernames (our .I whom reads in a file called .B .whom in the user's home directory, so that short real names can be substituted for account names). .I buds and its friends allow tremendous (well, 37 characters) aliases. The .I buds program itself simply lists the people who are online and in a file called .B .buddy in the your home directory. .TP .I wbuds This command looks at your buddies and tells you what they're up to. .TP .I mbuds .I mbuds checks /usr/spool/mail and tells you which buddies have mail, and how much of it they have. .TP .I monitor This is perhaps the most interesting program of the bunch, which isn't saying a whole lot. It constantly monitors /etc/utmp and tells you when buddies log on or off. .TP .I rbuds .I rbuds is a handy utility that allows several aliases to be kept for each buddy. It randomly selects aliases from a file called .B .rbuds in your home directory. .TP .I cbuds This is a silly hack that was necessary when we wrote .I rbuds\fR. It converts a .B .buddy file to a .B .rbuds file, so that you don't have to retype the whole thing. If you use .B .rbuds from the start, you'll never need to use .I cbuds. .PP .PP The \fB.buddy\fR file in your home directory contains a list of account names and a \fIbuddy alias\fR for each name. A buddy alias can be up to 37 characters, and is typically a user's real name, or a nickname or some other information. The format of the \fB.buddy\fR file is simply <account> <space> <alias> <newline> for each buddy. For instance: .PP .PP root Mister SuperUser zooker Donuts! Feed me donuts! harris David Harris, accountant at large .PP .PP Other utilities can affect your \fB.buddy\fR file; see rbuds(1) for the most useful one. .SH "SEE ALSO" buds(1), wbuds(1), mbuds(1), monitor(1), rbuds(1), cbuds(1) .SH AUTHORS Jon Luini, niteowl@ssyx.ucsc.edu (\fIbuds\fR and the original \fImonitor\fR) .PP Steven Grimm, koreth@ssyx.ucsc.edu (everything else) .SH BUGS .I BudPak is far too simple to have any bugs. \Rogue\Monster\ else echo "will not over write ./budpak.1" fi if `test ! -s ./buds.1` then echo "writing ./buds.1" cat > ./buds.1 << '\Rogue\Monster\' .TH BUDS 1 BUDPAK .UC 4 .SH NAME buds \- list of other users .SH SYNOPSIS .B buds [ .I options ] .SH DESCRIPTION .I buds tells you who's online at the moment, and prints some statistics about them. Foremost is the \fIbuddy alias\fR, which is a long (up to 37 characters) string containing a user's real name, nickname, or other information. See budpak(1) for more information. A typical line of \fIbuds\fR output is: .PP .PP Mister SuperUser wb 13 hs p3 (root) 0:47 [ucbvax] .PP .PP "Mister SuperUser" is the buddy alias for "root" (whose real username is in the third to last column of the listing). "w" and "b" indicates that he's writable (see mesg(1)) and biffable (see biff(1)). He has been idle for 13 minutes. "h" is the "horny" flag -- it indicates that root wants it, and wants it bad. "s" means that he's single ("h" without "s" can mean trouble, so watch it!) "p3" is the name of the tty that root is on; in this example, it's rlogin port 3. 0:47 is the amount of time he's been logged on. The last field only appears when the user is logged in from a remote host, and contains the name of the remote host (ucbvax, in this example.) .SH OPTIONS .TP .I \-w Only lists writable users. .TP .I \-b Only lists biffable users. .TP .I \-i Only lists users who have been idle for one minute or more. .TP .I \-h Only lists horny users. .TP .I \-s Only lists single users. .TP .I \-d Doesn't substitute buddy aliases for usernames. This flag is actually pretty useless, since real usernames are printed anyway. .TP .I \-a List all users who are online, whether they're in \fB.buddy\fR or not. .TP .I \-S Doesn't print the load average at the end of the buddy listing. The load average is only printed on systems which support the rwho(1C) service. .SH AUTHOR Jon Luini, niteowl@ssyx.ucsc.edu .SH FILES .TP /etc/utmp .TP $HOME/.buddy \- Buddy alias file. .SH "SEE ALSO" monitor(1), rbuds(1), budpak(1) \Rogue\Monster\ else echo "will not over write ./buds.1" fi if `test ! -s ./buds.c` then echo "writing ./buds.c" cat > ./buds.c << '\Rogue\Monster\' /** *** buds.c --> the ORIGINAL (completely rewritten) budpak utility *** this (and all) versions *** by Jon Luini, niteowl@ssyx.ucsc.edu *** *** A couple of modifications by Steven Grimm, koreth@ssyx.ucsc.edu *** *** 1:32:11 A.M. Saturday, November 14th 1987 **/ #include <stdio.h> #include <utmp.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> /* ** Comment out the following #define statement if your machine isn't listed ** in its own /etc/rwhod section. The /etc/rwhod section is used for a fast ** load average estimate. */ #define RWHOD /* ** The following define is a prefix that will be removed from the remote ** hostnames of people who are rlogged-in. For instance, at UCSC we have ** machines called "ucsca", "ucscb", "ucscc", and so on. Our prefix is ** "ucsc", so that the aforementioned machine names will show up as "a", ** "b", and "c", respectively, in the remote host part of the buddy listing. ** If you don't want a prefix, set it to something silly like "zzzzzzzzz" ** that will never occur. */ #define PREFIX "ucsc" #define TRUE 1 #define FALSE 0 #define UTMP "/etc/utmp" /* the location of utmp */ #define MAX_USERS 80 /* Maximum number of users possible */ #define IDLE 002 /* is user idle for longer than 60 seconds? */ #define RITABLE 020 /* Is each user's tty ritable? */ #define BIFF 0100 /* User have biff set? */ #define SINGLE 0010 /* User have single set? */ #define HORNY 0001 /* User have HoRnY set? */ #define C_IDLE 0000001 /* list only if user is idle */ #define C_RITEABLE 0000010 /* list only if writeable */ #define C_BIFF 0000100 /* list only if biffable */ #define C_SINGLE 0001000 /* list only if user is single */ #define C_HORNY 0010000 /* list only if HoRnY */ #define DONT_SUB 0100000 /* Don't substitute the login with the alias */ #define LIST_ALL 01000000 /* List all users, not just buddies */ struct BUD { char login[8]; /* login name */ char alias[50]; /* alias of login */ char line[8]; /* tty name */ char host[16]; /* host name, if remote */ long time; /* time on */ int idle; /* time idle */ int buddy; /* is the user a buddy? */ unsigned long perm; /* perm of the buds tty */ } buddy[MAX_USERS]; char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; char *space(), /* function for printing spaces */ *lentime(), /* funtion to print time buddy has been on */ hostname[10]; /* the hostname of this machine */ unsigned long mode; /* the mode of the command line params */ int num_usrs = 0, /* total number of users online */ num_buds = 0, /* total number of buddies online */ compare(), /* algorithm used for qsort */ shortf; /* flag to tell buds to be in short mode */ main(argc, argv) int argc; char **argv; { int bflag = FALSE; while (*argv) { if (**argv == '-') { ++*argv; while (**argv) { switch (**argv) { case 'w': mode |= C_RITEABLE; break; case 'b': mode |= C_BIFF; break; case 'i': mode |= C_IDLE; break; case 'h': mode |= C_HORNY; break; case 's': mode |= C_SINGLE; break; case 'd': mode |= DONT_SUB; break; case 'a': mode |= LIST_ALL; break; case 'S': shortf = TRUE; break; default : printf("'%c': bad flag\n", **argv); bflag = TRUE; } ++*argv; } } ++argv; } if (bflag) { /** print usage screen **/ puts("Usage: buds [-wbihsdaS]"); puts("\tw \tlist only writeable users"); puts("\tb \tlist only biffable users"); puts("\ti \tlist only idle users"); puts("\th \tlist only horny users"); puts("\ts \tlist only single users"); puts("\td \tdon't substitute buddy aliases"); puts("\ta \tlist all users on"); #ifdef RWHOD puts("\tS \trun buds in Short mode.. dont print the load (faster)"); #endif exit(-1); } init(); get_usrs(); if (!(mode & DONT_SUB)) get_buds(); qsort((char *) &buddy[0], num_usrs, sizeof(struct BUD), compare); print_out(); } compare(name1, name2) /** the compare function for qsort **/ struct BUD *name1, *name2; { return(strncmp(name1->alias, name2->alias, 40)); } init() { /** zero the buddy struct array **/ register int i; for (i = 0; i < MAX_USERS; i++) bzero(&buddy[i], sizeof(buddy[i])); } get_usrs() { /** get the user online in putthem in buddy struct array **/ struct utmp butane; int fd, i = 0; if ((fd = open(UTMP, 0)) == -1) { perror("reading users"); exit(-1); } while (read(fd, &butane, sizeof(struct utmp))) { if (butane.ut_name[0]) { strncpy(buddy[i].login, butane.ut_name, 8); strncpy(buddy[i].alias, butane.ut_name, 8); strncpy(buddy[i].line, butane.ut_line, 8); strcpy(buddy[i].host, butane.ut_host); buddy[i].time = butane.ut_time; status(&buddy[i]); i++; } } num_usrs = i; close(fd); } status(buddy) /** get the ttystatus of each buddy **/ struct BUD *buddy; { struct stat sbuf; char tty[14]; long now; sprintf(tty, "/dev/%s", buddy->line); if (stat(tty, &sbuf) == -1) { perror("getting tty status"); buddy->perm = 0; return(-1); } time(&now); buddy->perm = 0; if ((now - sbuf.st_atime) >= 60) { buddy->perm |= IDLE; buddy->idle = now - sbuf.st_atime; } if (sbuf.st_mode & RITABLE) buddy->perm |= RITABLE; if (sbuf.st_mode & BIFF) buddy->perm |= BIFF; if (sbuf.st_mode & HORNY) buddy->perm |= HORNY; if (sbuf.st_mode & SINGLE) buddy->perm |= SINGLE; } int get_buds() { /** check the ~/.buddy for valid buddies **/ FILE *fp; char budpath[100], login[8], alias[40]; register int i; sprintf(budpath, "%s/.buddy", getenv("HOME")); if ((fp = fopen(budpath, "r")) == NULL) { perror("getting buddies"); return(-1); } while (!feof(fp)) { fscanf(fp, "%s %[^\n]\n", login, alias); for (i = 0; i < num_usrs; i++) if (!strcmp(buddy[i].login, login)) { strncpy(buddy[i].alias, alias, 40); buddy[i].buddy = TRUE; } } return(1); } print_out() { /** print the final output to the screen **/ struct tm *t; char idle[2], buf[14], host[20]; register int i; long now; int valid[MAX_USERS], lav[2]; num_buds = 0; gethostname(buf, 14); strncpy(hostname, buf, 5); hostname[5] = '\0'; /** check to see whether buddy should be printed **/ for (i = 0; i < num_usrs; i++) { valid[i] = TRUE; if (mode & LIST_ALL) valid[i] = TRUE; else if (!buddy[i].buddy && (!(mode & DONT_SUB))) valid[i] = FALSE; else valid[i] = TRUE; if (mode & C_RITEABLE) if (!(buddy[i].perm & RITABLE)) valid[i] = FALSE; if (mode & C_BIFF) if (!(buddy[i].perm & BIFF)) valid[i] = FALSE; if (mode & C_IDLE) if (!(buddy[i].perm & IDLE)) valid[i] = FALSE; if (mode & C_HORNY) if (!(buddy[i].perm & HORNY)) valid[i] = FALSE; if (mode & C_SINGLE) if (!(buddy[i].perm & SINGLE)) valid[i] = FALSE; if (valid[i] && buddy[i].buddy) num_buds++; } time(&now); t = localtime(&now); if (!shortf) printf("%d buddies out of %d users on %s : %s %d at %d:%02d%s\n", num_buds, num_usrs, hostname, month[t->tm_mon], t->tm_mday, t->tm_hour > 12 ? t->tm_hour-12 : t->tm_hour == 0 ? 12 : t->tm_hour, t->tm_min, t->tm_hour > 11 ? "pm" : "am"); puts("==================================================================="); for (i = 0; i < num_usrs; i++) { if (buddy[i].line[strlen(buddy[i].line)-2] == 'p') sprintf(host, "[%s]", !strncmp(buddy[i].host, PREFIX, sizeof(PREFIX)-1) ? buddy[i].host+sizeof(PREFIX)-1 : buddy[i].host); else strcpy(host, ""); if (valid[i]) { if (buddy[i].idle >= 60) sprintf(idle, "%02d", buddy[i].idle / 60); else strcpy(idle, "--"); printf("%-40.40s %c%c %s %c%c %2.2s (%s)%s%s %s\n", buddy[i].alias, buddy[i].perm & RITABLE ? 'w' : '-', buddy[i].perm & BIFF ? 'b' : '-', idle, buddy[i].perm & HORNY ? 'h' : '-', buddy[i].perm & SINGLE ? 's' : '-', strcmp(buddy[i].line, "console") ? &buddy[i].line[3] : "co", buddy[i].login, space(7-strlen(buddy[i].login)), lentime(now - buddy[i].time), host); } } #ifdef RWHOD if (!shortf) { load(lav); printf("================== Load: %2.2f %2.2f %2.2f ===========================\n", lav[0]/100.0, lav[1]/100.0, lav[2]/100.0); } else #endif /* RWHOD */ puts("==================================================================="); } char *space(len) /** returns a blank string of length 'len' **/ int len; { register int i; static char tmp[100]; strcpy(tmp, ""); for (i = 0; i < len; i++) strcat(tmp, " "); return(tmp); } char *lentime (time) /** returns the time on in readable format **/ long time; { static char buffer[32], /* A buffer for formatted time */ *s; /* A pointer to the formatted time */ int minutes, /* Number of minutes past the hour */ hours; /* The number of hours past midnight */ hours = time / 3600; minutes = (time % 3600) / 60; s = &buffer[31]; *s-- = '\0'; *s-- = minutes % 10 + '0'; *s-- = minutes / 10 + '0'; if (hours > 23) { hours %= 24; *s-- = '?'; } else *s-- = ':'; *s-- = hours % 10 + '0'; *s = (hours >= 10) ? hours / 10 + '0' : ' '; return(s); } #ifdef RWHOD #include <protocols/rwhod.h> load(load_buf) int *load_buf; { struct whod wbuf; char whod_path[80]; int fd; sprintf(whod_path, "/usr/spool/rwho/whod\.%s", hostname); if ((fd = open(whod_path, 0)) == -1) { perror("getting load"); load_buf[0] = 0; load_buf[1] = 0; load_buf[2] = 0; return(-1); } read(fd, (char *) &wbuf, sizeof(struct whod)); load_buf[0] = wbuf.wd_loadav[0]; load_buf[1] = wbuf.wd_loadav[1]; load_buf[2] = wbuf.wd_loadav[2]; close(fd); } #endif /* RWHOD */ \Rogue\Monster\ else echo "will not over write ./buds.c" fi if `test ! -s ./cbuds.1` then echo "writing ./cbuds.1" cat > ./cbuds.1 << '\Rogue\Monster\' .TH CBUDS 1 BUDPAK .UC 4 .SH NAME cbuds \- Convert a .buddy file to a .rbuds file. .SH SYNOPSIS .B cbuds .SH DESCRIPTION .I Cbuds reads in a .B .buddy file (from the user's home directory) and creates a .B .rbuds file (also in the user's home directory). See rbuds(1) for more information about the format of the .rbuds file. .SH AUTHOR Steven Grimm, koreth@ssyx.ucsc.edu .SH "SEE ALSO" rbuds(1) \Rogue\Monster\ else echo "will not over write ./cbuds.1" fi if `test ! -s ./cbuds.c` then echo "writing ./cbuds.c" cat > ./cbuds.c << '\Rogue\Monster\' #include <stdio.h> #include <ctype.h> char *getenv(); main() { FILE *bud, *rbud; char f1[99], f2[99]; strcpy(f1, getenv("HOME")); strcpy(f2, f1); strcat(f1, "/.buddy"); strcat(f2, "/.rbuds"); if ((bud = fopen(f1, "r")) == NULL) { printf("Couldn't open %s for read.\n", f1); exit(-1); } if (access(f2, 0) == 0) { char x; printf("%s already exists. Overwrite? ", f2); fflush(stdout); x = getchar(); if (toupper(x) != 'Y') { fclose(bud); exit(-1); } } if ((rbud = fopen(f2, "w+")) == NULL) { printf("Couldn't open %s for write.\n", f2); exit(-1); } while (! feof(bud)) { char login[8], budname[99]; fscanf(bud, "%s %[^\n]\n", login, budname); fprintf(rbud, "*%s\n%s\n", login, budname); } fclose(bud); fclose(rbud); } \Rogue\Monster\ else echo "will not over write ./cbuds.c" fi if `test ! -s ./mbuds.1` then echo "writing ./mbuds.1" cat > ./mbuds.1 << '\Rogue\Monster\' .TH MBUDS 1 BUDPAK .UC 4 .SH NAME mbuds \- Check buddies for mail .SH SYNOPSIS .B mbuds [ .I user1 .I user2 .I user3 \&... ] .SH DESCRIPTION .I Mbuds scans the file \fB.buddy\fR in the user's home directory (see budpak(1)) and checks the users listed therein for mail. If there are some users whose mailboxes (which are assumed to be located in /usr/spool/mail) aren't empty, their names, their aliases in the .buddy file, and the length of their mailbox files are printed. .PP If login names are specified, .I mbuds scans those users' mailboxes and lists them (using the above format) whether they have mail or not. .SH AUTHOR Steven Grimm, koreth@ssyx.ucsc.edu .SH "SEE ALSO" buds(1), wbuds(1), monitor(1), rbuds(1), budpak(1) \Rogue\Monster\ else echo "will not over write ./mbuds.1" fi if `test ! -s ./mbuds.c` then echo "writing ./mbuds.c" cat > ./mbuds.c << '\Rogue\Monster\' #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> char *getlogin(), *getenv(), *malloc(); /* ** MBUDS - Check for buddies' mail ** ** Usage: mbuds [login1 login2 login3...] ** ** If no logins are specified, mbuds prints everyone in the user's .buddy ** file who has mail. Otherwise, only the specified logins are selected ** from the .buddy file, and are printed whether they have mail or not. */ struct bud { char login[8]; char name[40]; struct bud *next; }; struct bud *start; /* Read in a .buddy file and make a linked list out of it. */ readbuds() { struct bud *end; FILE *fp; char budfile[80]; strcpy(budfile, getenv("HOME")); strcat(budfile, "/.buddy"); if ((fp=fopen(budfile, "r"))==NULL) { printf("No .buddy file.\n"); return 0; } start = end = (struct bud *)NULL; while (! feof(fp)) { struct bud *newnode; newnode = (struct bud *)malloc(sizeof(struct bud)); newnode->login[0] = 0; newnode->next = (struct bud *)NULL; fscanf(fp, "%s %39[^\n]\n", newnode->login, newnode->name); if (strlen(newnode->login)) { if (end) end->next = newnode; else start = newnode; end = newnode; } } return 1; } /* Scan the linked list for entries that aren't in the command line arguments and chop them out. */ scanlist(argc, argv) int argc; char **argv; { struct bud *now, *then, *temp; if (argc < 2) return; argv++; argc--; then = (struct bud *)NULL; now = start; while (now) { int i; for (i = 0; i < argc; ++i) if (! strcmp(argv[i], now->login)) break; if (i >= argc) /* Loop finished = name not found */ { if (then) { temp = now; then->next = now->next; now = now->next; free(temp); } else { temp = now; start = now->next; now = start; free(temp); } } else { then = now; now = now->next; argv[i] = argv[argc]; /* Trim the list so it's */ argc--; /* faster to search */ } } } checkmail(bud) struct bud *bud; { struct stat buf; if (stat(bud->login, &buf) < 0) return 0; return (int)buf.st_size; } main(argc, argv) int argc; char **argv; { struct bud *now; int maillen; chdir("/usr/spool/mail"); if (! readbuds()) exit(-1); scanlist(argc, argv); now = start; while (now) { maillen = checkmail(now); if (argc > 1 || maillen) { printf("%7s %-40s %6d bytes\n", now->login, now->name, maillen); } now = now->next; } } \Rogue\Monster\ else echo "will not over write ./mbuds.c" fi if `test ! -s ./monitor.1` then echo "writing ./monitor.1" cat > ./monitor.1 << '\Rogue\Monster\' .TH MONITOR 1 BUDPAK .UC 4 .SH NAME monitor \- Notify about logins and logouts .SH SYNOPSIS .B monitor [ .I options ] [ user1 user2 user3 ... ] .SH DESCRIPTION .I Monitor is a utility that notifies the user when certain other people log on and off. Pass the usernames of the people to monitor on the command line. When one of the monitored people logs on, a short message is printed containing his username and alias in the user's .B .buddy file (see budpak(1)), if he has an alias there. If the \-o option (see below) is used, a short message is also printed when one of the monitored users logs off. The messages can be changed using the \-m option. .SH OPTIONS .TP .I \-o Users specified after the \-o option are monitored for both logins and logouts. Only logins are monitored for users specified before the \-o option. .TP .I \-c The \-c option prevents the program from printing its messages when "cbreak" mode is on. Cbreak is active in programs such as vi, which can easily be messed up by messages appearing at random on the screen. The messages are printed as soon as cbreak mode is deactivated (e.g., when you leave vi). .TP .I \-n The \-n option prevents \fImonitor\fR from printing users' real account names if there are aliases in the \fB.buddy\fR file. .TP .I \-N The \-N option prevents \fImonitor\fR from printing aliases from a \fB.buddy\fR file. .TP .I \-q The \-q (quiet) option suppresses \fImonitor\fR's startup message. .TP .I \-b The \-b flag stops \fImonitor\fR from beeping when it prints messages. .TP .I \-t The \-t flag causes \fImonitor\fR to print the name of the tty a monitoree is on, as well as his name. .TP .I \-d The \-d option causes \fImonitor\fR to terminate when any of the specified users logs on. This is useful if you're waiting for someone, and don't want to have to search for \fImonitor\fR's PID to kill it. .TP .I \-m The \-m option takes login names from a file called .B .monitor in the user's home directory, rather than from the command line. The format of the .B .monitor file is as follows: The first line is a message that's printed out when one of the users logs on (with a "%s" where the login name and/or buddy alias is to be placed). The second line is the logout message (again, with a "%s" where the username should go). The following lines are the login names that the user wants to watch for logins only (i.e., logouts aren't printed). Then, optionally, a line containing only a pound sign ("#") character can be followed by the login names of users to monitor for both logins and logouts. Note that it's possible to have the login/logout messages, then a pound sign and a list of users; that will monitor both logins and logouts of everyone listed in the .B .monitor file. A .B .monitor file might look like: Wow! %s is here! Drat. There goes %s. jones martin # root zooker harris .SH AUTHOR Steven Grimm, koreth@ssyx.ucsc.edu .SH FILES .TP /etc/utmp .TP $HOME/.monitor \- The file of login names to monitor, and the alert strings for logins and logouts. .TP $HOME/.buddy \- Buddy alias file. .SH "SEE ALSO" buds(1), rbuds(1), budpak(1) .SH BUGS .I Monitor doesn't allow different login and logout messages for individual buddies. \Rogue\Monster\ else echo "will not over write ./monitor.1" fi if `test ! -s ./monitor.c` then echo "writing ./monitor.c" cat > ./monitor.c << '\Rogue\Monster\' #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <utmp.h> #include <signal.h> #include <sgtty.h> /* ** MONITOR - Watch people log on and off ** ** Original program by Jon Luini ** niteowl@ssyx.ucsc.edu ** This version by Steven Grimm ** koreth@ssyx.ucsc.edu ** ** Operation: ** ** Monitor scans the file /etc/utmp and checks each entry to see if its ** tty is in the binary tree of ttys belonging to buddies (actually, the ** tree is a structure with lots of information about the buddy). If the ** tty is used by a buddy, Monitor compares the login names of the buddy and ** the person using the tty. If they're not equal, the buddy who used to ** be on the tty has logged off and a message to that effect may be printed. ** The buddy is deleted from the tty tree and added to the second tree, ** the buddy tree. If the tty isn't in the tty tree, the login name of the ** buddy is searched for in the second binary tree, which is sorted by login ** names. If the name is found, the buddy is added to the tty tree and a ** logon message is printed out. ** The /etc/utmp file isn't scanned if it hasn't been modified since the ** last time Monitor passed through it. */ #define UTMP "/etc/utmp" #define WAIT 15 /* Seconds between status checks */ char *ttyname(), *getenv(), *rindex(); struct buddy { char tty[8]; /* Tty name */ char login[8]; /* Login name */ char budname[40]; /* .buddy file name, if any */ int logoff; /* Flag: Monitor logoffs? */ struct buddy *lchild, /* Binary search tree pointers */ *rchild, *parent; }; struct buddy *buddy, *tty; char logonmsg[80], /* Gets printed when someone logs on */ logoffmsg[80], /* Gets printed when someone logs off */ whichtty[8], /* Which tty this was started on */ login[8]; /* Name of the user who started this */ int check_off = 0, /* Flag: Check logoffs */ die = 0, /* Flag: Die when someone comes on */ quiet = 0, /* Flag: Start quietly */ nobeep = 0, /* Flag: Don't beep */ noreal = 0, /* Flag: Don't print account names */ nofake = 0, /* Flag: Don't print buddy aliases */ dotty = 0, /* Flag: Print a user's tty */ cblock = 0; /* Flag: Don't interrupt cbreak */ /* Search for a buddy. Pass a 0 in "type" to search for a login name, or a 1 to search for a tty. */ struct buddy *bst_search(tree, key, type) struct buddy *tree; char *key; int type; { int comp; while (tree) { if (type) { if (! (comp=strcmp(key, tree->tty))) return tree; } else if (! (comp=strcmp(key, tree->login))) return tree; if (comp < 0) tree = tree->lchild; else tree = tree->rchild; } return tree; } /* Add a node to a tree (the node can have children). Pass the tree type, as in bst_search above. */ bst_add(tree, node, type) struct buddy *tree, *node; int type; { int comp; if (! node) return 1; while (tree) { if (type) { if (! (comp=strcmp(node->tty, tree->tty))) return 0; } else if (! (comp=strcmp(node->login, tree->login))) return 0; if (comp < 0) { if (tree->lchild) tree = tree->lchild; else { tree->lchild = node; node->parent = tree; break; } } else { if (tree->rchild) tree = tree->rchild; else { tree->rchild = node; node->parent = tree; break; } } } if (tree) return 1; else return 0; /* This should never happen */ } /* Delete a node from a tree. Returns the node that has taken the deleted node's place in the tree. */ struct buddy *bst_delete(node, type) struct buddy *node; int type; { if (node->parent) { if (node == node->parent->lchild) { node->parent->lchild = node->rchild; if (node->rchild) node->rchild->parent = node->parent; if (node->lchild) bst_add(node->parent, node->lchild, type); return node->rchild; } else if (node == node->parent->rchild) { node->parent->rchild = node->lchild; if (node->lchild) node->lchild->parent = node->parent; if (node->rchild) bst_add(node->parent, node->rchild, type); return node->lchild; } else fprintf(stderr, "\r\nBinary tree error!\r\n"); } else if (node->lchild) { node->lchild->parent = 0; if (node->rchild) bst_add(node->lchild, node->rchild, type); return node->lchild; } else if (node->rchild) { node->rchild->parent = 0; if (node->lchild) bst_add(node->rchild, node->lchild, type); return node->rchild; } else return (struct buddy *)0; } /* Handle one entry from the utmp file. */ doutmp(entry) struct utmp *entry; { struct buddy *bud, *newbud; if (tty) /* Only check logoffs if there are buddies on */ { bud = bst_search(tty, entry->ut_line, 1); if (bud) { if (strcmp(bud->login, entry->ut_name)) { if (bud->logoff) domsg(logoffmsg, bud); newbud = bst_delete(bud); if (bud == tty) tty = newbud; free(bud); } return; } } if (buddy) { bud = bst_search(buddy, entry->ut_name, 0); if (bud) { newbud = (struct buddy *) malloc(sizeof(struct buddy)); *newbud = *bud; strcpy(newbud->tty, entry->ut_line); newbud->parent = newbud->lchild = newbud->rchild = (struct buddy *) 0; if (tty) bst_add(tty, newbud, 1); else tty = newbud; domsg(logonmsg, newbud); if (die) exit(0); } } } /* Process the utmp file. Returns 0 if the user has logged off. */ process() { int hand, status=1; struct utmp entry; hand = open(UTMP, 0); while (read(hand, &entry, sizeof(entry)) > 0) { if (! strcmp(entry.ut_line, whichtty)) if (strcmp(entry.ut_name, login)) status = 0; doutmp(&entry); } close(hand); return status; } /* Print the logoff message for a buddy. */ domsg(message, bud) char *message; struct buddy *bud; { char budname[60], text[256]; if ((! bud->budname[0]) || nofake) strcpy(budname, bud->login); else if (bud->budname[0] && !noreal) sprintf(budname, "%s (%s)", bud->budname, bud->login); else if (bud->budname[0]) sprintf(budname, "%s", bud->budname); if (dotty) { strcat(budname, " on "); strcat(budname, bud->tty); } sprintf(text, message, budname); fprintf(stderr, "\r\n%s\r\n", text); if (! nobeep) putc(7, stderr); } /* Read in the .buddy file and assign aliases to the buddies in the buddy tree. */ readbuddy() { char fname[256], login[8], budname[40]; struct buddy *bud; FILE *fp; strcpy(fname, getenv("HOME")); strcat(fname, "/.buddy"); if (!(fp = fopen(fname, "r"))) return 1; while (! feof(fp)) { fscanf(fp, "%s %[^\n]\n", login, budname); bud = bst_search(buddy, login, 0); if (bud) strcpy(bud->budname, budname); } fclose(fp); } /* Initialize the login and whichtty variables. */ initvars() { char ttypath[256], *ri; strcpy(login, getenv("USER")); strcpy(ttypath, ttyname(2)); ri = rindex(ttypath, '/'); if (! ri) ri = ttypath; else ++ri; strcpy(whichtty, ri); buddy = tty = (struct buddy *)0; } /* Add a user to the buddy tree. */ adduser(name) char *name; { struct buddy *newbud; newbud = (struct buddy *) malloc(sizeof(struct buddy)); newbud->parent = newbud->lchild = newbud->rchild = (struct buddy *) 0; newbud->tty[0] = newbud->budname[0] = 0; newbud->logoff = check_off; strcpy(newbud->login, name); if (buddy) bst_add(buddy, newbud, 0); else buddy = newbud; } /* Read in a .monitor file. */ readmonitor() { FILE *fp; char fname[256], user[80]; strcpy(fname, getenv("HOME")); strcat(fname, "/.monitor"); if (!(fp=fopen(fname, "r"))) { printf("Monitor: Error opening %s.\n", fname); return 0; } fscanf(fp, "%[^\n]\n%[^\n]\n", logonmsg, logoffmsg); while (! feof(fp)) { fscanf(fp, "%s\n", user); if (strcmp(user, "#")) adduser(user); else check_off = 1; } fclose(fp); return 1; } /* Check the last modify date of a file. */ long lastmod(file) char *file; { struct stat buf; stat(file, &buf); return buf.st_mtime; } /* Check the tty for cbreak mode. */ cbreak() { struct sgttyb tty; gtty(0, &tty); return tty.sg_flags & CBREAK; } /* Main program. */ main(argc, argv) int argc; char **argv; { long lastutmp, lu; initvars(); strcpy(logonmsg, "%s has logged on."); strcpy(logoffmsg, "%s has logged off."); while (*++argv) { if (argv[0][0] == '-') switch (argv[0][1]) { case 'o': check_off = 1; break; case 'm': readmonitor(); break; case 'c': cblock = 1; break; case 'd': die = 1; break; case 'q': quiet = 1; break; case 'n': noreal = 1; break; case 'N': nofake = 1; break; case 'b': nobeep = 1; break; case 't': dotty = 1; break; default: printf("%c: unknown flag\n", argv[0][1]); break; } else adduser(argv[0]); } if (! quiet) printf("Monitor: running on %s.\n", whichtty); if (fork()) /* Shove myself in the background */ exit(0); signal(SIGHUP, SIG_IGN); /* Ignore some signals so that */ signal(SIGINT, SIG_IGN); /* w won't think we're the user's */ signal(SIGQUIT, SIG_IGN); /* current process. */ if (! buddy) { printf("Monitor: No users selected (using default)\n"); adduser("root"); strcpy(buddy->budname, "God"); } readbuddy(); lastutmp = 0; while (1) { if ((!cblock)||(!cbreak())) { lu = lastmod(UTMP); if (lu != lastutmp) { lastutmp = lu; if (! process()) exit(0); } } sleep(WAIT); } } \Rogue\Monster\ else echo "will not over write ./monitor.c" fi if `test ! -s ./rbuds.1` then echo "writing ./rbuds.1" cat > ./rbuds.1 << '\Rogue\Monster\' .TH RBUDS 1 BUDPAK .UC 4 .SH NAME rbuds \- Construct a random .buddy file .SH SYNOPSIS .B rbuds [ .I \-b ] .SH DESCRIPTION .I Rbuds allows users to keep lists of aliases for buddies on the system, and select random names from those lists to put in a .buddy file. The input to .I rbuds is the file .B .rbuds in the user's home directory, which contains data in the following format: .PP *login1 Buddy Alias 1 for login 1 Buddy Alias 2 for login 1 Buddy Alias 3 for login 1 *login2 Buddy Alias for login 2 *login3 Buddy Alias 1 for login 3 Buddy Alias 2 for login 3 . . . .PP Up to 128 aliases per buddy are allowed. When .I rbuds is run, it reads in all the buddy aliases for a login name and selects a random one to output to the .B .buddy file in the user's home directory. .SH OPTIONS .TP .I \-b The \-b option causes .I rbuds to put itself in the background as soon as it begins executing. This is primarily intended for users with large .rbuds files who want to put the .I rbuds command in their .login files without having to wait longer to log in. .SH AUTHOR Steven Grimm, koreth@ssyx.ucsc.edu .SH FILES $HOME/.buddy \- The output .br $HOME/.rbuds \- The input .SH "SEE ALSO" buds(1), mbuds(1), wbuds(1), monitor(1), budpak(1), cbuds(1) .SH BUGS Buddy aliases can't begin with asterisks. \Rogue\Monster\ else echo "will not over write ./rbuds.1" fi if `test ! -s ./rbuds.c` then echo "writing ./rbuds.c" cat > ./rbuds.c << '\Rogue\Monster\' #include <stdio.h> /* ** RBUDS - make a .buddy file from a .rbuds file. ** ** .rbuds is in the format ** ** *login1 ** Buddy Alias 0 ** *login2 ** Buddy Alias 1 ** Buddy Alias 2 ** Buddy Alias 3 ** ** Buddy aliases can't begin with asterisks. Up to 128 choices per login ** name are allowed. ** ** If the -b option is used, rbuds puts itself in the background. */ char *getenv(); char oldlogin[8], curlogin[8]; char budnames[40][128]; FILE *rbuds, *buddy; /* Read in the names for a person and return an index into budnames[] corresponding to the name that'll be used. */ getname() { int i=0; budnames[i][0] = ' '; while (! feof(rbuds)) { fscanf(rbuds, "%[^\n]\n", budnames[i]); if (budnames[i][0] == '*') break; i++; } if (budnames[i][0] == '*') strcpy(curlogin, budnames[i]+1); return (random() % i); } main(argc, argv) char **argv; { char fname[128], bname[128]; if (argc == 2 && !strcmp(argv[1], "-b")) if (fork()) exit(0); srandom(getpid()); strcpy(fname, getenv("HOME")); strcpy(bname, fname); strcat(fname, "/.rbuds"); strcat(bname, "/.buddy"); if (! (rbuds = fopen(fname, "r"))) { fprintf(stderr, "rbuds: No .rbuds file!\n"); exit(-1); } if (! (buddy = fopen(bname, "w+"))) { fprintf(stderr, "rbuds: Couldn't open .buddy!\n"); exit(-1); } fscanf(rbuds, "*%s\n", curlogin); while (! feof(rbuds)) { strcpy(oldlogin, curlogin); fprintf(buddy, "%s %s\n", oldlogin, budnames[getname()]); } fclose(buddy); fclose(rbuds); } \Rogue\Monster\ else echo "will not over write ./rbuds.c" fi if `test ! -s ./wbuds.1` then echo "writing ./wbuds.1" cat > ./wbuds.1 << '\Rogue\Monster\' .TH WBUDS 1 BUDPAK .UC 4 .SH NAME wbuds \- See what buddies are doing .SH SYNOPSIS .B wbuds .SH DESCRIPTION .I Wbuds executes the "w" command, but only selects those users who are in the caller's ".buddy" file. It replaces the statistics reported by "w" with the aliases in the .buddy file. .SH AUTHOR Steven Grimm, koreth@ssyx.ucsc.edu .SH FILES /usr/ucb/w - the "w" command .SH "SEE ALSO" w(1), buds(1), budpak(1) .SH BUGS .I Wbuds assumes that the output of /usr/ucb/w is in a specific format. .PP .I Wbuds doesn't support the -s option to /usr/ucb/w, and can't restrict its output to certain users. \Rogue\Monster\ else echo "will not over write ./wbuds.1" fi if `test ! -s ./wbuds.c` then echo "writing ./wbuds.c" cat > ./wbuds.c << '\Rogue\Monster\' #include <stdio.h> /* ** wbuds ** ** Tells you what everyone in your .buddy file is doing. */ #define W "/usr/ucb/w" extern char *getenv(); extern FILE *popen(); main() { FILE *buddy, *w; char budfile[128], budname[16], buddud[128]; char buds[128][16], rbuds[128][60]; int numbuds=0, i; strcpy(budfile, getenv("HOME")); strcat(budfile, "/.buddy"); if ((buddy = fopen(budfile, "r")) == NULL) { printf("No .buddy file!\n"); exit(-1); } while (! feof(buddy)) { budname[0] = 0; fscanf(buddy, "%s", budname); if (! budname[0]) continue; fscanf(buddy, "%[^\n]", buddud); getc(buddy); buddud[38] = 0; while (buddud[0] == 10) /* Strip tabs. */ strcpy(buddud, buddud+1); strcpy(rbuds[numbuds], buddud); strcpy(buds[numbuds++], budname); } fclose(buddy); if ((w = popen(W, "r")) == NULL) { printf("%s isn't working.\n", W); exit(-1); } fscanf(w, "%[^\n]", buddud); /* Get the load, etc. */ getc(w); printf("%s\n", buddud); printf("%-8s%-39s%s\n", "Login", "Name", "What"); fscanf(w, "%[^\n]", buddud); /* Get the column headers. */ getc(w); while (! feof(w)) { buddud[0]=0; fscanf(w, "%[^\n]", buddud); buddud[8]=0; while (buddud[strlen(buddud)-1] == ' ') buddud[strlen(buddud)-1]=0; getc(w); for (i=0; i<numbuds; ++i) if (! strcmp(buddud, buds[i])) printf("%-7s%-40s%s\n", buddud, rbuds[i], &buddud[47]); } pclose(w); } \Rogue\Monster\ else echo "will not over write ./wbuds.c" fi echo "Finished archive 1 of 1" # if you want to concatenate archives, remove anything after this line exit -- For comp.sources.unix stuff, mail to rsalz@uunet.uu.net.