comsat for System V with FIFO
David MacKenzie
mackenzi at thor.acc.stolaf.edu
Tue Aug 15 11:27:00 AEST 1989
As a long-time BSD user I got attached to the biff and comsat programs;
when I started using a System V machine it became harder to know when I had
new mail. So I decided to convert the freed BSD comsat program (from
uunet) to System V. We don't have sockets or other networking stuff on
our systems so I changed it to use a named pipe (FIFO) instead.
I didn't find the source code to biff on uunet so I wrote my own public
domain version.
I haven't yet adapted smail or any other mail delivery program to actually
send a message to comsat yet; right now I'm just experimenting with it, and
"echo dave at 0 > /etc/comsat.fifo" is sufficient.
In case anyone else is interested, here are comsat and biff for System V.
Comments welcome.
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: comsat.c biff.c
# Wrapped by mackenzi at thor on Mon Aug 14 20:13:03 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'comsat.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'comsat.c'\"
else
echo shar: Extracting \"'comsat.c'\" \(10120 characters\)
sed "s/^X//" >'comsat.c' <<'END_OF_FILE'
X/*
X * Copyright (c) 1980 Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms are permitted
X * provided that the above copyright notice and this paragraph are
X * duplicated in all such forms and that any documentation,
X * advertising materials, and other materials related to such
X * distribution and use acknowledge that the software was developed
X * by the University of California, Berkeley. The name of the
X * University may not be used to endorse or promote products derived
X * from this software without specific prior written permission.
X * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
X * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
X */
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif /* not lint */
X
X/* comsat - daemon to notify registered users of new mail
X Usage: comsat > /dev/null 2> errorlog &
X
X Receives one line messages of the form
X user at mailbox-offset\n
X on a named pipe, and if the user is logged on and "biff y" (owner
X execute bit of tty is turned on), prints a message summarizing the new
X mail on the user's screen.
X
X Converted for System V with FIFO by David MacKenzie
X Latest revision: 08/14/89 */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <utmp.h>
X#include <signal.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <termio.h>
X#include <string.h>
X#include <varargs.h>
X#include <sys/utsname.h>
X
X/* BSD-compatible constants for lseek. */
X#define L_SET 0
X#define L_INCR 1
X#define L_XTND 2
X
X/* The directory where system mailboxes are located. */
X#define SYSMAILDIR "/usr/mail"
X
X/* Path of the named pipe used to send messages to this program. */
X#define FIFO "/etc/comsat.fifo"
X
X/* The number of seconds between checks of the utmp. */
X#define ALARM_INTERVAL 15
X
Xchar *malloc ();
Xchar *realloc ();
Xoff_t atol ();
Xoff_t fseek ();
Xoff_t lseek ();
Xtime_t time ();
X
Xchar *xmalloc ();
Xchar *xrealloc ();
Xint reap_children ();
Xint read_utmp ();
Xvoid mail_for ();
Xvoid msg_perror_fatal ();
Xvoid notify ();
Xvoid summarize_new_mail ();
X
X/* This machine's host name, used in the notification message. */
Xchar hostname[10];
X
X/* Contents of the utmp. */
Xstruct utmp *utmp;
X
X/* The last time an alarm was set. */
Xtime_t alarm_set;
X
X/* Number of entries in `utmp'. */
Xint nutmp;
X
X/* File descriptor for reading the utmp. */
Xint utfd;
X
X/* The name this program was run with, for error messages. */
Xchar *program_name;
X
X/* ARGSUSED */
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X int fifd;
X FILE *fifp;
X char msgbuf[100];
X
X program_name = argv[0];
X
X /* Open the fifo with O_NDELAY so open won't block waiting for another
X process to open the fifo for writing. */
X fifd = open (FIFO, O_RDONLY | O_NDELAY);
X if (fifd == -1)
X msg_perror_fatal ("Cannot open %s for reading", FIFO);
X /* Turn off O_NDELAY on the fifo for more efficient cpu usage. */
X if (fcntl (fifd, F_SETFL, 0) == -1)
X msg_perror_fatal ("Cannot fcntl %s", FIFO);
X fifp = fdopen (fifd, "r");
X if (fifp == NULL)
X msg_perror_fatal ("Cannot fdopen %s", FIFO);
X
X utfd = open (UTMP_FILE, O_RDONLY);
X if (utfd == -1)
X msg_perror_fatal ("Cannot read %s", UTMP_FILE);
X
X if (chdir (SYSMAILDIR) == -1)
X msg_perror_fatal ("Cannot chdir to %s", SYSMAILDIR);
X
X utmp = NULL;
X nutmp = 0;
X gethostname (hostname, sizeof (hostname));
X signal (SIGCLD, reap_children);
X
X read_utmp ();
X signal (SIGALRM, read_utmp);
X
X while (1)
X {
X while (fgets (msgbuf, sizeof msgbuf, fifp) == NULL)
X sleep (1);
X if (nutmp == 0)
X continue; /* No one has logged in yet. */
X /* Don't let automatic utmp updating corrupt the in-core copy while
X we're using it. */
X signal (SIGALRM, SIG_IGN);
X mail_for (msgbuf);
X /* If we missed a utmp update while ignoring the signal, do it
X manually. */
X if (time ((time_t *) 0) - alarm_set >= ALARM_INTERVAL)
X read_utmp ();
X else
X signal (SIGALRM, read_utmp);
X }
X /* NOTREACHED */
X}
X
X/* SIGCLD handler. Called when a child process dies.
X Is this necessary under System V? */
X
Xint
Xreap_children ()
X{
X wait ((int *) 0);
X}
X
X/* SIGALRM handler. Every ALARM_INTERVAL seconds, read a current copy
X of the utmp into `utmp'. */
X
Xint
Xread_utmp ()
X{
X static unsigned utmp_size = 0; /* Bytes allocated for `utmp'. */
X static unsigned utmp_mtime = 0; /* Last modification time of utmp. */
X struct stat stats;
X
X if (fstat (utfd, &stats) == -1)
X msg_perror_fatal ("Cannot fstat utmp");
X if (stats.st_mtime > utmp_mtime)
X {
X utmp_mtime = stats.st_mtime;
X if (stats.st_size > utmp_size)
X {
X utmp_size = stats.st_size + 10 * sizeof (struct utmp);
X utmp = (struct utmp *) xrealloc ((char *) utmp, utmp_size);
X }
X if (lseek (utfd, 0L, L_SET) < 0)
X msg_perror_fatal ("Cannot seek to beginning of utmp");
X nutmp = read (utfd, utmp, (int) stats.st_size);
X if (nutmp == -1)
X msg_perror_fatal ("Cannot read utmp");
X nutmp /= sizeof (struct utmp);
X }
X time (&alarm_set);
X alarm ((unsigned) ALARM_INTERVAL);
X signal (SIGALRM, read_utmp);
X}
X
X/* `name' has the form "user at mailbox-offset\n". Check whether "user" is
X logged on; if so, try to notify them of the new mail. */
X
Xvoid
Xmail_for (name)
X char *name;
X{
X struct utmp *utp;
X char *cp;
X off_t offset;
X
X cp = strchr (name, '@');
X if (cp == NULL)
X {
X fprintf (stderr, "%s: Invalid message: %s\n", program_name, name);
X return;
X }
X *cp++ = '\0';
X offset = atol (cp);
X utp = &utmp[nutmp];
X while (--utp >= utmp)
X {
X if (!strncmp (utp->ut_user, name, sizeof (utmp[0].ut_user)))
X notify (utp, offset);
X }
X}
X
X/* The carriage return character needed for the terminal being notified;
X it will be the null string if the terminal driver or the terminal
X is supplying a carriage return automatically with each newline. */
Xstatic char *cr;
X
X/* If the user described in `utp' is logged on and "biff y", notify them
X of the new mail in their system mailbox at offset `offset'. */
X
Xvoid
Xnotify (utp, offset)
X struct utmp *utp;
X off_t offset;
X{
X static char tty[20] = "/dev/";
X struct termio termio;
X FILE *tp;
X char name[sizeof (utmp[0].ut_user) + 1];
X struct stat stats;
X
X strncpy (tty + 5, utp->ut_line, sizeof (utp->ut_line));
X if (stat (tty, &stats) || !(stats.st_mode & S_IEXEC))
X return;
X switch (fork ())
X {
X case -1:
X msg_perror_fatal ("Cannot fork");
X case 0:
X break;
X default:
X return;
X }
X signal (SIGALRM, SIG_DFL);
X alarm ((unsigned) 30);
X tp = fopen (tty, "w");
X if (tp == NULL)
X _exit (1);
X ioctl (fileno (tp), TCGETA, &termio);
X cr = (termio.c_oflag & OPOST) && (termio.c_oflag & ONLCR)
X || (termio.c_oflag & ONLRET) ? "" : "\r";
X strncpy (name, utp->ut_user, sizeof (utp->ut_user));
X name[sizeof (name) - 1] = '\0';
X fprintf (tp, "%s\n\007New mail for %s@%.*s\007 has arrived:%s\n----%s\n",
X cr, name, sizeof (hostname), hostname, cr, cr);
X summarize_new_mail (tp, name, offset);
X fclose (tp);
X _exit (0);
X}
X
X/* Print the first 7 lines or 560 characters (whichever comes first) of
X the new mail message that starts at byte `offset' in file `name' to
X stream `tp'. Skip header lines other than From, Subject, [To, and Date].
X `name' is just the user's name, since the system mailboxes are in the
X current directory. */
X
Xvoid
Xsummarize_new_mail (tp, name, offset)
X FILE *tp;
X char *name;
X off_t offset;
X{
X char *cp;
X FILE *fi;
X int linecnt;
X int charcnt;
X int inheader;
X char line[BUFSIZ];
X
X fi = fopen (name, "r");
X if (fi == NULL)
X return;
X if (fseek (fi, offset, L_SET))
X return;
X linecnt = 7;
X charcnt = 560;
X inheader = 1;
X while (fgets (line, sizeof (line), fi) != NULL)
X {
X if (inheader)
X {
X if (line[0] == '\n')
X {
X fprintf (tp, "%s\n", cr);
X inheader = 0;
X continue;
X }
X /* Skip header continuation lines and non-essential header lines. */
X if (line[0] == ' ' || line[0] == '\t' ||
X strncmp (line, "From:", 5) &&
X strncmp (line, "Subject:", 8))
X continue;
X }
X if (linecnt <= 0 || charcnt <= 0)
X {
X fprintf (tp, "...more...%s\n", cr);
X return;
X }
X cp = strchr (line, '\n');
X if (cp)
X *cp = '\0';
X fprintf (tp, "%s%s\n", line, cr);
X charcnt -= strlen (line);
X linecnt--;
X }
X fprintf (tp, "----%s\n", cr);
X}
X
X/* Simulate the BSD gethostname(2) system call on System V. */
X
Xint
Xgethostname (name, length)
X char *name;
X int length;
X{
X struct utsname uts;
X
X if (uname (&uts) < 0)
X return -1;
X strncpy (name, uts.nodename, length);
X return 0;
X}
X
Xstatic void
Xmemory_out ()
X{
X fprintf (stderr, "%s: Virtual memory exhausted\n", program_name);
X exit (1);
X}
X
X/* Allocate `n' bytes of memory dynamically, with error checking. */
X
Xchar *
Xxmalloc (n)
X unsigned n;
X{
X char *p;
X
X p = malloc (n);
X if (p == 0)
X memory_out ();
X return p;
X}
X
X/* Change the size of an allocated block of memory `p' to `n' bytes,
X with error checking.
X If `p' is NULL, run xmalloc.
X If `n' is 0, run free and return NULL. */
X
Xchar *
Xxrealloc (p, n)
X char *p;
X unsigned n;
X{
X if (p == 0)
X return xmalloc (n);
X if (n == 0)
X {
X free (p);
X return 0;
X }
X p = realloc (p, n);
X if (p == 0)
X memory_out ();
X return p;
X}
X
X/* ANSI C function. */
X
Xchar *
Xstrerror (n)
X int n;
X{
X extern char *sys_errlist[];
X extern int sys_nerr;
X
X return n >= 0 && n < sys_nerr ? sys_errlist[n] : "Unknown error";
X}
X
X/* Print "program_name: str_and_optional_args: perror_message" on stderr,
X then exit with error status. */
X/* VARARGS */
Xvoid
Xmsg_perror_fatal (str, va_alist)
X char *str;
X va_dcl
X{
X va_list args;
X extern int errno;
X int save_errno;
X
X save_errno = errno;
X fprintf (stderr, "%s: ", program_name);
X va_start (args);
X vfprintf (stderr, str, args);
X va_end (args);
X fprintf (stderr, ": %s\n", strerror (save_errno));
X exit (1);
X}
END_OF_FILE
if test 10120 -ne `wc -c <'comsat.c'`; then
echo shar: \"'comsat.c'\" unpacked with wrong size!
fi
# end of 'comsat.c'
fi
if test -f 'biff.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'biff.c'\"
else
echo shar: Extracting \"'biff.c'\" \(2106 characters\)
sed "s/^X//" >'biff.c' <<'END_OF_FILE'
X/* biff - accept or refuse new mail messages from comsat
X
X Usage: biff [yn]
X
X David MacKenzie
X public domain
X Latest revision: 08/14/89 */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <varargs.h>
X
Xchar *ttyname ();
X
Xchar *any_ttyname ();
Xvoid msg_perror_fatal ();
Xvoid usage ();
X
Xchar *program_name;
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X struct stat stats;
X char *tty;
X
X program_name = argv[0];
X tty = any_ttyname ();
X if (tty == NULL)
X {
X fprintf (stderr, "%s: Not connected to a terminal\n", argv[0]);
X exit (1);
X }
X if (stat (tty, &stats) == -1)
X msg_perror_fatal ("Cannot stat %s", tty);
X switch (argc)
X {
X case 1:
X printf ("is %c\n", (stats.st_mode & S_IEXEC) ? 'y' : 'n');
X break;
X case 2:
X switch (*argv[1])
X {
X case 'y':
X if (chmod (tty, stats.st_mode | S_IEXEC) == -1)
X msg_perror_fatal ("Cannot change mode of %s", tty);
X break;
X case 'n':
X if (chmod (tty, stats.st_mode & ~S_IEXEC) == -1)
X msg_perror_fatal ("Cannot change mode of %s", tty);
X break;
X default:
X usage ();
X }
X break;
X default:
X usage ();
X }
X exit (0);
X /* NOTREACHED */
X}
X
Xchar *
Xany_ttyname ()
X{
X char *tty;
X
X tty = ttyname (2);
X if (tty)
X return tty;
X tty = ttyname (1);
X if (tty)
X return tty;
X tty = ttyname (0);
X if (tty)
X return tty;
X return NULL;
X}
X
X/* ANSI C function. */
X
Xchar *
Xstrerror (n)
X int n;
X{
X extern char *sys_errlist[];
X extern int sys_nerr;
X
X return n >= 0 && n < sys_nerr ? sys_errlist[n] : "Unknown error";
X}
X
X/* Print "program_name: str_and_optional_args: perror_message" on stderr,
X then exit with error status. */
X/* VARARGS */
Xvoid
Xmsg_perror_fatal (str, va_alist)
X char *str;
X va_dcl
X{
X va_list args;
X extern int errno;
X int save_errno;
X
X save_errno = errno;
X fprintf (stderr, "%s: ", program_name);
X va_start (args);
X vfprintf (stderr, str, args);
X va_end (args);
X fprintf (stderr, ": %s\n", strerror (save_errno));
X exit (1);
X}
X
Xvoid
Xusage ()
X{
X fprintf (stderr, "Usage: %s [yn]\n", program_name);
X exit (1);
X}
END_OF_FILE
if test 2106 -ne `wc -c <'biff.c'`; then
echo shar: \"'biff.c'\" unpacked with wrong size!
fi
# end of 'biff.c'
fi
echo shar: End of shell archive.
exit 0
--
David MacKenzie
mackenzi at thor.stolaf.edu or edf at rocky2.rockefeller.edu
More information about the Alt.sources
mailing list