source to "phone" system (part 3 of 4)

Jonathan C. Broome broome at ucbvax.BERKELEY.EDU
Sun Dec 29 18:42:27 AEST 1985


#-----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	defs.h
#	../common.h
#	child.c
#	daemon.c
#	dopage.c
#	forward_program.c
#	inquire.c
#	list.c
#	main.c
#	page.c
#	pagetty.c
#	reinvite.c
#	strsave.c
#	utmp.c
# This archive created: Sat Dec 28 01:11:09 1985
export PATH; PATH=/bin:$PATH
mkdir master
cd master
echo shar: extracting "'Makefile'" '(4014 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \!Funky!Stuff! > 'Makefile'
# 
#   Makefile for phoned            20 December 1985
#


# What the flags mean:
#  INETD    - set this if the phoned is to run a a single-threaded service
#             under /etc/inetd. It expects for fd 0 to be a datagram socket
#             bound to the service address that it wil receive from.
#  FORK     - set this when *not* under inetd if you want to server to
#             fork upon startup, with the parent exiting. This is usually
#             set, and does nothing if INETD is also defined.
#  DPATH    - the full pathanme of the conversation daemon. If phoned
#             cannot find it here, it will try to find "convd" in 
#             /usr/local/lib, /usr/lib, and /etc.
#  SERVICES - set this if phone is listed as a datagram service in 
#             /etc/services. This has no effect under the inetd.
#  PORT     - if INETD and SERVICES are not defined, this is the port 
#             number to listen on (overriding the default in ../common.h)
#  NO_WHO	- Define this if you want your site to be "secure" and not allow
#             outside users to use the "who" command to see who's on ...

#CFLAGS = -O -DDPATH=\"/usr/local/lib/convd\"
CFLAGS  = -O -DINETD -DDPATH=\"/usr/local/lib/convd\"

LPR     = lpr -Pvax

HDRS	= defs.h ../common.h
SRCS	= child.c daemon.c dopage.c forward_program.c\
			inquire.c list.c main.c page.c pagetty.c\
			reinvite.c strsave.c utmp.c

OBJS	= child.o daemon.o dopage.o forward_program.c\
			inquire.o list.o main.o page.o pagetty.o\
			reinvite.o strsave.o utmp.o

DEST	= phoned
RDEST	= /etc/phoned


.DEFAULT:
	co $<

all:	${DEST}

${DEST}: ${OBJS}
	/bin/rm -f ${DEST}
	cc ${CFLAGS} -o ${DEST} ${OBJS}

${OBJS}: ${HDRS}

install:	${DEST}
	/bin/rm -f ${RDEST}
	cp ${DEST} ${RDEST}

clean:
	/bin/rm -f ${DEST} core *.o

print:	${HDRS} ${SRCS}
	pr -f ${HDRS} ${SRCS} | expand -4 | ${LPR}

tags: /dev/null
	ctags -w ${HDRS} ${SRCS}

lint: ${HDRS} ${SRCS}
	lint ${SRCS} > lint.out

shar:	Makefile ${HDRS} ${SRCS}
	shar -v Makefile ${HDRS} ${SRCS} > ../shar.master

dist:	${DEST}
	-rcp ${DEST} buddy:${RDEST}
	-rcp ${DEST} franny:${RDEST}
	-rcp ${DEST} holden:${RDEST}
	-rcp ${DEST} seymour:${RDEST}
	-rcp ${DEST} zooey:${RDEST}
	-rcp ${DEST} cory:${RDEST}
	-rcp ${DEST} miro:${RDEST}


depend: ${SRCS}
	mv Makefile makefile.old
	sed '/^# Dependencies follow/,$$d' makefile.old > Makefile
	echo '# Dependencies follow' >> Makefile
	includes -so ${SRCS} >> Makefile
	echo ' ' >> Makefile
	echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile
	echo '# see depend: above' >> Makefile

# DO NOT DELETE THE FOLLOWING LINE
# Dependencies follow

utmp.o: /usr/include/utmp.h

utmp.o pagetty.o: /usr/include/sys/file.h

page.o main.o: /usr/include/netdb.h

main.o list.o: /usr/include/signal.h

pagetty.o page.o list.o: /usr/include/time.h

pagetty.o page.o list.o: /usr/include/sys/time.h

page.o forward_program.o: /usr/include/pwd.h

utmp.o dopage.o: /usr/include/sys/stat.h

pagetty.o main.o daemon.o: /usr/include/syslog.h

child.o: /usr/include/sys/wait.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/errno.h

utmp.o reinvite.o pagetty.o pagetty.o page.o main.o main.o list.o list.o \
inquire.o forward_program.o dopage.o daemon.o daemon.o child.o: \
/usr/include/stdio.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/netinet/in.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/sys/socket.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: /usr/include/sys/types.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o forward_program.o \
dopage.o daemon.o child.o: ./defs.h

utmp.o reinvite.o pagetty.o page.o main.o list.o inquire.o dopage.o daemon.o \
child.o: ./../common.h
 
# IF YOU PUT STUFF HERE IT WILL GO AWAY
# see depend: above
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'defs.h'" '(2842 characters)'
if test -f 'defs.h'
then
	echo shar: will not over-write existing file "'defs.h'"
else
cat << \!Funky!Stuff! > 'defs.h'
/*
 * $Header: defs.h,v 1.1 85/10/28 17:38:15 broome Exp $
 */

/*
 * $Log:	defs.h,v $
 * Revision 1.1  85/10/28  17:38:15  broome
 * Initial revision
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <errno.h>

#define SOCKADDR	struct sockaddr_in    /* shorter to type */

/*
 *   One of these structures is used for each pending invitation.
 */

struct invite {
	/*  info about the person requesting a call  */
	char      *caller;           /* login name of person making invitation */
	char      *host;             /* figured out from control port address  */
	SOCKADDR  ctladdr;           /* inviter's control port address         */
	char      *convaddr;         /* inviter's conversation port address    */
	char      *callno;			 /* unique per-user message id from caller */

	/*  info about the person being requested  */
	char      *callee;           /* login name of person being requested   */
	char      *tty;              /* user's tty, if any                     */
	char      *ptty;             /* tty we are actually paging             */
	char      *home;             /* his home directory                     */
	int       uid, gid;          /* used for forwarding programs           */

	/*  and bookkeeping information about the invitation itself  */
	int       type;              /* normal page or being forwarded?        */
	int       rings;             /* send a new ring when rings == 0        */
	int       pid;               /* child notification pid                 */
	int       flags;             /* various stuff about status             */
	char      id[10];			 /* identification for this request        */
	struct    invite *prev;      /* previous in doubly-linked list         */
	struct    invite *next;      /* next most recent invitation            */
};

typedef struct invite INV;
#define NIL       ((INV *) 0)
#define eq(a,b)   (strcmp(a,b) == 0)

/*  often-used functions  */
char    *malloc();
char    *strsave();
char    *findtty();
INV     *lookup();

#define SIZ 512
char    host[32];               /* name of this host */
char	buf[SIZ];				/* general-purpose buffer */
extern	int errno;
int		misc;					/* socket used to send out */

/*  Error return values from paging routines  */
#define NOT_HERE   1
#define ERR        3
#define THRESHOLD  (60*10)    /* ten minutes */

#define	PROG		(1<<0)		/* was forwarded to a program        */
#define FORWARD		(1<<1)		/* forwarded to another user/host    */
#define	DONTFORWARD	(1<<2)		/* forwarding failed - don't forward */
#define	NOT_ON		(1<<3)		/* user is not logged on */
#define	MESG_OFF	(1<<4)		/* user is refusing messages */

#define	BUSYFILE	"/.busy"	/* name of forwarding file */

INV		*invitations;			/* list of pending invitations */
INV		*freelist;				/* list of free invite structs */

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'../common.h'" '(1266 characters)'
if test -f '../common.h'
then
	echo shar: will not over-write existing file "'../common.h'"
else
cat << \!Funky!Stuff! > '../common.h'
/*
 *   Defines common to all the parts of the phone system.
 */


#ifndef		ESC
#define		ESC			'\033'		/* precedes all commands */
#endif

#define		ACK			'y'			/* good response code */
#define		NAK			'n'			/* not-so-good code   */

/*
 *   Commands sent from conversation daemon to client.
 */
 
#define		META		0200			/* high bit for command characters */
#define		ADDUSER		(01<<5)			/* add a user to the conversation  */
#define		DELUSER		(02<<5)			/* delete a user from conversation */
#define		UPDATE		(03<<5)			/* set screen update mode */

/*
 *   Commands sent from or master daemon to client.
 */

#define		MESSAGE		'M'				/* following is message text       */

/*
 *   Commands sent by client to conversation or master daemons.
 */

#define		ANSWER		'A'			/* he got the invite, will answer  */
#define		CALLING		'C'			/* daemon is calling the user      */
#define		PAGE		'P'			/* page a user                     */
#define		INQUIRE		'I'			/* inquire as to whether invited   */
#define		REINVITE	'R'			/* renew a request for paging      */
#define		DAEMON		'D'			/* create a daemon for me          */
#define		WHO			'W'			/* tell me who's on ...            */
#define		KILL		'K'			/* cause the daemon to exit        */

#ifndef PORT
#define		PORT	1167
#endif
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'child.c'" '(1702 characters)'
if test -f 'child.c'
then
	echo shar: will not over-write existing file "'child.c'"
else
cat << \!Funky!Stuff! > 'child.c'
#ifndef lint
static char RCSid[] = "$Header: child.c,v 1.1 85/10/28 17:38:11 broome Exp $";
#endif

/*
 * $Log:	child.c,v $
 * Revision 1.1  85/10/28  17:38:11  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"
#include <sys/wait.h>


/*
 *   A more complicated sigchld handler -
 *   looks for the pid in the list of invitations
 *   and sends appropriate status messages to the callers.
 */

sigchld ()
{
	register INV *inv;
	union    wait status;
	char     mbuf[SIZ];
	int      pid;
	int      exitstat;

	while ((pid = wait3 (&status, WNOHANG, 0)) > 0) {	/* any children? */
		if (WIFSTOPPED (status)) {		/* shouldn't happen */
			kill (pid, 9);
			exitstat = 1;
		} else
			exitstat = status.w_retcode;

		for (inv = invitations; inv; inv = inv->next)
			if (inv->pid == pid)		/* does pid match? */
				break;
		if (inv->pid != pid)			/* didn't find child - continue */
			continue;

		if (exitstat && (inv->flags & PROG))		/* their program has problems */
			inv->flags |= DONTFORWARD;
		
		/*
		 *  Now send a message to the user.
		 *  The multiple sprintf()'s aren't very pretty ...
		 */
		
		if (exitstat == 0) {				/* good exit status - ok */
			sprintf (buf, "%s%sing user %s@%s",
						inv->id, (inv->flags & PROG) ? "Forward" : "Ring", 
						inv->callee, host);
			if ((inv->flags & PROG) == 0) {
				strcat (buf, " on ");
				strcat (buf, inv->ptty);
			}
		} else {
			sprintf (buf, "%sCannot ring %s@%s - Unknown error", 
						inv->id, inv->callee, host);
		}
		sprintf (mbuf, "%c%c%c%s", ESC, CALLING, exitstat ? NAK : ACK, buf);
		
		if (sendto (misc, mbuf, strlen (mbuf), 0, &inv->ctladdr,
					sizeof (inv->ctladdr)) < 0)
			perror ("child: sendto");
	}
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'daemon.c'" '(2810 characters)'
if test -f 'daemon.c'
then
	echo shar: will not over-write existing file "'daemon.c'"
else
cat << \!Funky!Stuff! > 'daemon.c'
#ifndef lint
static char RCSid[] = "$Header: daemon.c,v 1.1 85/10/28 17:38:13 broome Exp $";
#endif

/*
 * $Log:	daemon.c,v $
 * Revision 1.1  85/10/28  17:38:13  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <stdio.h>
#include <syslog.h>


/*
 *  The guy wants a daemon, so give him one ...
 */

daemon (addr)
struct sockaddr_in addr;
{
	struct sockaddr_in sin;		/* address of new daemon */
	extern char myaddr[];		/* address of this host */
	char   *error();
	int    sock;
	int    pid;
	int    i, len;

	if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		sprintf (buf, "%c%c%cCannot create socket: %s", 
					ESC, DAEMON, NAK, error());
		sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		return;
	}

	i = 1;
	if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof (i)))
		syslog (LOG_ERR, "daemon: setsockopt: %m");

	bzero ((char *)&sin, sizeof (sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	sin.sin_family = AF_INET;

	len = sizeof (sin);
	if (bind (sock, &sin, len) < 0) {
		sprintf (buf,"%c%c%cCannot bind socket: %s", ESC, DAEMON, NAK, error());
		sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		return;
	}

	if (pid = fork ()) {		/* parent */
		if (pid == -1) {		/* failed */
			sprintf (buf, "%c%c%cFork failed: %s", ESC, DAEMON, NAK, error());
			sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		} 
		(void) close (sock);
		return;
	}
	
	len = sizeof (sin);
	if (getsockname (sock, &sin, &len) < 0) {
		sprintf (buf, "%c%c%cCannot get socket name: %s", 
					ESC, DAEMON, NAK, error());
		(void) sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
		_exit (1);
	}

	/* life is good */
	sprintf (buf,"%c%c%c%s/%d", ESC, DAEMON, ACK, myaddr, ntohs (sin.sin_port));
	sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));

	listen (sock, 5);

	if (sock != 0)
		if (dup2 (sock, 0)) {		/* set socket to be stdin */
			perror ("dup2");
			syslog (LOG_ERR, "daemon: dup2 failed: %m");
		}

	for (i = 1; i < getdtablesize(); i++)	/* close anything else */
		(void) close (i);
	
#ifdef DPATH		/* sure hope this is it! */
	execl (DPATH, "convd", 0);
#else !DPATH
	execl ("/usr/local/lib/convd", "convd", 0);
	execl ("/usr/lib/convd", "convd", 0);
	execl ("/etc/convd", "convd", 0);
#endif DPATH

#ifdef DPATH
	syslog (LOG_ERR, "cannot execl %s: %m", DPATH);
#else
	syslog (LOG_ERR, "cannot execl convd: %m");
#endif

	sprintf (buf, "%c%c%cExecl failed: %s", ESC, DAEMON, NAK, error());
	sendto (misc, buf, strlen (buf), 0, &addr, sizeof (addr));
	_exit (-99);
}


/*
 *  Return a string with the error message.
 */

char *
error ()
{
	extern int  errno;
	extern char *sys_errlist[];
	extern int  sys_nerr;

	if (errno > sys_nerr)
		return ("Unknown error.");
	else
		return (sys_errlist[errno]);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'dopage.c'" '(1924 characters)'
if test -f 'dopage.c'
then
	echo shar: will not over-write existing file "'dopage.c'"
else
cat << \!Funky!Stuff! > 'dopage.c'
#ifndef lint
static char RCSid[] = "$Header: dopage.c,v 1.1 85/10/28 17:38:16 broome Exp $";
#endif

/*
 * $Log:	dopage.c,v $
 * Revision 1.1  85/10/28  17:38:16  broome
 * Initial revision
 */


#include "../common.h"
#include "defs.h"
#include <sys/stat.h>

/*
 *   Handle paging one user - one invitation.
 *   Checks for ~/.busy, tries to do 
 *   the right thing.
 */

_dopage (inv)
INV    *inv;
{
	FILE     *fp;			/* forward file   */
	char     *tty;			/* tty they're on */
	int      pid;			/* child process  */
	int      mode;			/* tty status     */
	struct   stat statb;

	/*
	 *  We won't check for forwarding if it has failed already.
	 *  We also make sure that home isn't null - otherwise unknown
	 *  users might be able to take advantage of root somehow ...
	 */

	if ((inv->flags & DONTFORWARD) == 0 && inv->home) {
		strcpy (buf, inv->home);
		strcat (buf, BUSYFILE);
		if (stat (buf, &statb) == 0 && (statb.st_mode & (04<<3)) &&
		  statb.st_uid == inv->uid && (fp = fopen (buf, "r"))) {
			/* file exists */
			while (fgets (buf, SIZ, fp))		/* read a line */
				if (*buf != '\n' && *buf != '#')
					break;
			fclose (fp);
			if (*buf == '/' || *buf == '~')		/* path to program */
				return (forward_program (buf, inv));	/* so invoke it */
		}
	}

	inv->flags &= ~PROG;

	/* 
	 *  If we're here, we didn't forward it, so look for them
	 *  on a tty and send a message to their terminal.
	 */
	
	tty = findtty (inv->callee, inv->tty, &mode);
	if (mode == NOT_ON)
		return (NOT_ON);
	
	if (inv->ptty)
		free (inv->ptty);
	inv->ptty = strsave (tty+5);	/* save name of tty being paged */

	if (mode == MESG_OFF)
		return (MESG_OFF);
	
	if (pid = fork ()) {		/* parent */
		if (pid == -1) {			/* fork failed */
			perror ("fork");
			inv->rings = 0;		/* so try again next time */
		} else
			inv->pid = pid;		/* save child's id */
		return (0);
	}
	pagetty (inv, tty);			/* child - do it */
	/*NOTREACHED*/
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'forward_program.c'" '(3308 characters)'
if test -f 'forward_program.c'
then
	echo shar: will not over-write existing file "'forward_program.c'"
else
cat << \!Funky!Stuff! > 'forward_program.c'
#ifndef lint
static char RCSid[] = "$Header: forward_program.c,v 1.1 85/10/28 17:38:17 broome Exp $";
#endif

/*
 * $Log:	forward_program.c,v $
 * Revision 1.1  85/10/28  17:38:17  broome
 * Initial revision
 */

#include "defs.h"
#include <pwd.h>

/*
 *  Take the pathname of a forwarding program
 *  and start it up.
 *
 *  Features: the ability to give a printf-like
 *  format string to fill in with the caller and such.
 *
 *		"%R" - name of recipient 
 *		"%C" - name of caller
 *      "%H" - caller's host
 */

forward_program (buf, inv)
char *buf;
INV  *inv;
{
	int      pid;			/* child process               */
	int      fd;			/* fds to close                */
	int      argc;			/* count of words in command   */
	char     *argv[32];		/* command in forward file     */

	inv->flags |= PROG;				/* mark as being piped */
	if (pid = fork()) {				/* parent returns immediately */
		if (pid == -1)				/* fork failed */
			inv->rings = 0;			/* try again next time */
		else
			inv->pid = pid;			/* save process id */
		return (0);
	}

	/*
	 *  We're the child process, so clean up 
	 *  and exec the program.
	 */

	sigblock (0);					/* ignore all signals */

	if (argc = expand (buf, argv, inv)) {		/* it contains something */

		setgid (inv->gid);				/* set up permissions */
		initgroups (inv->callee, inv->gid);
		setuid (inv->uid);				/* ... fix security   */
		for (fd = 0; fd < getdtablesize(); fd++)	/* clean up */
			(void) close (fd);
		execv (argv[0], argv);			/* and do it */
		_exit (-99);					/* bad format?? */
	}
}



/*
 *  Given a line from the .phonerc file, expand ~user and also '%'
 *  substitutions (like printf) and parse into an argument vector.
 *
 *  We use a static buffer to stick the string sinto, so as to 
 *  avoid the malloc/free-in-interrupt routine problem.
 *
 *  The allowed '%' substitutions are:
 *
 *		"%R" - name of the recipient.
 *		"%C" - name of the calling party.
 *		"%H" - calling party's host.
 */

expand (inbuf, argp, inv)
char  *inbuf;
char  **argp;
INV   *inv;
{
	struct		passwd *pwd;
	static		char outbuf[10240];
	register	char *i;
	register	char *o;
	register	char *n;
	char		*start;
	char		name[32];
	int			first;
	char		**ap;

	i = inbuf;
	o = outbuf;
	ap = argp;

	while (*i) {
		start = o;		/* save front of this word */

		while (*i && *i == ' ' || *i == '\t')
			i++;
		
		for (first = 1; *i && *i != ' ' && *i != '\t'; i++) {
			if (*i == '~' && first) {		/* ~user */
				for (i++, n = name; *i && *i != '/' && *i != ' ' && *i != '\t';)
					*n++ = *i++;
				i--;
				*n = '\0';
				if (*name == '\0') {	/* use $HOME */
					n = inv->home;
				} else {				/* lookup user in passwd file */
					if (pwd = getpwnam (name))
						n = pwd->pw_dir;
					else
						n = (char *) 0;
				}
				while (n && *n)		/* copy dir over */
					*o++ = *n++;
			} else if (*i == '%') {		/* do printf-like stuff */
				switch (*++i) {
					case 'R':	n = inv->callee;	/* recipient */
								break;
					case 'C':	n = inv->caller;	/* caller */
								break;
					case 'H':	n = inv->host;		/* calling host */
								break;
					case '%':	n = "%";			/* normal percent */
								break;
				}
				while (n && *n)
					(*o++ = *n++);
			} else
				*o++ = *i;
		}
		*o++ = '\0';
		if (*start != '\0')
			*ap++ = start;
	}
	*ap = (char *) 0;
	return (ap - argp);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'inquire.c'" '(1200 characters)'
if test -f 'inquire.c'
then
	echo shar: will not over-write existing file "'inquire.c'"
else
cat << \!Funky!Stuff! > 'inquire.c'
#ifndef lint
static char RCSid[] = "$Header: inquire.c,v 1.1 85/10/28 17:38:21 broome Exp $";
#endif

/*
 * $Log:	inquire.c,v $
 * Revision 1.1  85/10/28  17:38:21  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"

/*
 *  Check to see if there are any pending calls for this user,
 *  send back the first address and delete the invite if any are found.
 */

inquire (argv, sin)
char   *argv[];
struct sockaddr_in sin;
{
	INV  *inv;

	if (inv = lookup (argv[0], argv[1])) {		/* had one pending */
		sprintf (buf, "%c%c%c%s", ESC, INQUIRE, ACK, inv->convaddr);
		delete (inv);
	} else
		sprintf (buf, "%c%c%cNo messages pending.", ESC, INQUIRE, NAK);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
}


/*
 *  They say they answered the call, so delete it from the list.
 *  This routine is for future use - not used now ...
 */

answer (argv)
char   *argv[];
{
	INV *inv;
	
	for (inv = invitations; inv; inv = inv->next)
		if (eq (argv[0], inv->callee) && eq (argv[1], inv->convaddr)) {
			sprintf (buf, "%c%c%c%s", ESC, ANSWER, ACK, inv->id);
			(void) sendto (misc, buf, strlen(buf), 0, &inv->ctladdr, 
				sizeof (inv->ctladdr));
			delete (inv);
			return;
		}
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'list.c'" '(3769 characters)'
if test -f 'list.c'
then
	echo shar: will not over-write existing file "'list.c'"
else
cat << \!Funky!Stuff! > 'list.c'
#ifndef lint
static char RCSid[] = "$Header: list.c,v 1.1 85/10/28 17:38:22 broome Exp $";
#endif

/*
 *  Routines for managing the list of pending calls, including
 *  creating and looking for invitations.
 */

/*
 * $Log:	list.c,v $
 * Revision 1.1  85/10/28  17:38:22  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>



static int  requests = 0;              /* number of pending invitations */

/*
 *  Insert a request into the pending list.
 */

insert (inv)
INV    *inv;
{
	/* insert into top of list */
	if (invitations == NIL) {
		inv->next         = NIL;
		invitations       = inv;
	} else {
		inv->next         = invitations;
		invitations->prev = inv;
		invitations       = inv;
	}

	if (requests++ == 0)    /* need to set alarm interrupt */
		alarm (1);			/* so start up alarm */
}


/*
 *  Called on SIGALRM to process pending calls ---
 *  go through the list of pending invitations, removing any old
 *  ones, trying to ring the rest.
 */

ring ()
{
	register	INV   *inv;
	register	INV   *next;

	if (!requests)   /* no pending requests **/
		return;

	readutmp ();	/* reread /etc/utmp */

	for (inv = invitations; inv; inv = next) {
		next = inv->next;                	/* save the next one          */
		if (inv->rings++ == 0)				/* time to page this one ???  */
			dopage (inv);               	/* I guess so ...             */
		else if (inv->rings > 30)			/* more than five minutes old */
			delete (inv);
	}
	if (requests > 0)                        /* any requests left ???     */
		alarm (5);                           /* set the next alarm        */
#ifdef INET		  /* if running under inetd, exit when no more work to do */
	else
		exit (0);
#endif INETD
}


/*
 *  Page the given invitation and return a status message.
 *  Notice the incredible amount of indirection going on here -
 *  up to three (or is it four) levels !! Have to clean this up 
 *  when we get forwarding working properly.
 */

dopage (inv)
INV    *inv;
{
	char  rbuf[10];
	int   ret;
	char  *name;
	char  *tty;

	name = inv->callee;
	ret = _dopage (inv);

	sprintf (rbuf, "%c%c%c%s", ESC, CALLING, NAK, inv->id);

	if (ret == 0)		/* all looks to be good */
		return;

	if (ret == MESG_OFF)
		sprintf (buf, "%s%s@%s is refusing messages", rbuf, name, host);
	else if (ret == NOT_ON)
		sprintf (buf, "%s%s@%s is not logged in", rbuf, name, host);

	if ((tty = inv->tty) && *tty) {
		strcat (buf, " on ");
		strcat (buf, tty);
	}

	delete (inv);
	sendto (misc, buf, strlen (buf), 0, &inv->ctladdr, sizeof (inv->ctladdr));
}


/*
 *  Check to see if the named user is being invited by the right person.
 *  Returns a pointer to the invitation in question.
 *
 *  A wildcard "*" is acceptable as the caller name - this is 
 *  useful for answering machine programs.
 */

INV *
lookup (callee, caller)
char   *callee, *caller;
{
	register	INV  *inv;
	register	int  all = (eq (caller, "*"));

	for (inv = invitations; inv; inv = inv->next) {
		if (all || eq (inv->caller, caller))     /* caller match */
			if (eq (callee, inv->callee))        /* callee match */
				return (inv);
	}
	return (NIL);
}


/*
 *  The usual linked-list deletion routine, with a minor
 *  difference - instead of deallocating the space, we simply
 *  place the element on a free list for future use (LIFO form)
 */

delete (ptr)
INV *ptr;
{
	requests--;                      	/* decrement number pending  */
	if (ptr->prev)
		ptr->prev->next = ptr->next;	/* set previous's next pointer */ 
	else 
		invitations = ptr->next;

	if (ptr->next)
		ptr->next->prev = ptr->prev;	/* set next's previous pointer */
	
	ptr->next = freelist;				/* add on tail of free list */
	freelist = ptr;						/* and make this the top */
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'main.c'" '(4574 characters)'
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
cat << \!Funky!Stuff! > 'main.c'
#ifndef lint
static char RCSid[] = "$Header: main.c,v 1.1 85/10/28 17:38:28 broome Exp $";
#endif

/*
 * $Log:	main.c,v $
 * Revision 1.1  85/10/28  17:38:28  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <signal.h>
#include <stdio.h>
#include <syslog.h>
#include <netdb.h>

#ifdef FORK
#include <sys/ioctl.h>
#endif



char	myaddr[20];			/* internet host address in ascii dot notation */

/*
 *   Master phone daemon, sits on known socket address and
 *   listens for requests...  Handles invitations, acts as
 *   central clearinghouse for conversations.
 */

main (argc, argv)
int   argc;
char *argv[];
{
#if defined(SERVICES) && !defined(INETD)
	struct   servent *sp, *getservbyname();
#endif
	struct   sockaddr_in  sin;
	struct   hostent *hp, *gethostbyname();
	extern   int   ring();
	extern   int   sigchld();
	int      sock;
#ifndef INETD
	int      port = PORT;
#endif
	int      tty, i;

	invitations = NIL;
	freelist    = NIL;

#ifdef INETD
	if (argc != 1) {
		fprintf (stderr, "%s takes no options!\n", argv[0]);
		exit (1);
	}
#else  !INETD
	if (argc > 2) {
		fprintf (stderr, "Usage: %s [ port# ]\n", argv[0]);
		exit (1);
	}
#endif INETD

#ifndef INETD
	if (argc == 2) {
		if ((port = atoi (argv[1])) == 0) {
			fprintf (stderr, "Bad port number: %s\n", argv[1]);
			exit (1);
		}
	}
#endif INETD
#if defined(SERVICES) && !defined(INETD)
	else {
		if ((sp = getservbyname ("phone", "udp")) == (struct servent *) 0) {
			fprintf (stderr, "phone/udp: unknown service.\n");
			exit (1);
		}
		port = sp->s_port;
	}
#endif SERVICES

#ifndef INETD
	/*  Open and initialize the socket we will take requests on  */
	if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror ("Cannot create datagram socket");
		exit (2);
	}
	bzero ((char *)&sin, sizeof (sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = htons (port);
	sin.sin_family = AF_INET;

	if (bind (sock, &sin, sizeof (sin))) {
		perror ("Cannot bind datagram socket");
		exit (3);
	}
#else INETD
	sock = 0;			/* inetd hands us the first packet on stdin */
#endif INETD

	/*  Initialize the work socket as well  */
	if ((misc = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror ("Cannot create datagram work socket");
		exit (4);
	}
	bzero ((char *)&sin, sizeof (sin));
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	sin.sin_family = AF_INET;
	if (bind (misc, &sin, sizeof (sin))) {
		perror ("Cannot bind datagram work socket");
		exit (5);
	}

	signal (SIGCHLD, sigchld);
	signal (SIGALRM, ring);
	gethostname (host, 32);

	if ((hp = gethostbyname (host)) == (struct hostent *) 0) {
		fprintf (stderr, "%s: cannot find my own address!!!\n", argv[0]);
		exit (6);
	}
	bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
	strcpy (myaddr, inet_ntoa (sin.sin_addr.s_addr));

#if defined(FORK) && !defined(INETD)
	if (fork ())
		exit (0);

	if (sock != 0)
		close (0);
	i = open ("/dev/null", 0);
	if (sock != 1)
		dup2 (i, 1);
	if (sock != 2)
		dup2 (i, 2);

	if ((tty = open ("/dev/tty", 0)) != -1) {
		ioctl (tty, TIOCNOTTY);
		close (tty);
	} else 
		setpgrp (0, getpid ());
#endif FORK && !INETD

#ifdef LOG_ODELAY
	openlog ("phoned", LOG_PID | LOG_ODELAY, 0);
#else
	openlog ("phoned", LOG_PID, 0);
#endif
	openutmp ();

	service (sock);
	exit (0);
}


/*
 *  Main service routine.
 *  Listen on the socket, process requests.
 */

service (sock)
register int sock;
{
	int 		mask;		/* can't be in a register ... darn */
	int 		len;
	register	int  omask;
	register	int  rval;
	register	char *av[10];
	register	char buf[SIZ];
	register	int  i;
	struct		sockaddr_in sin;

	omask = 1 << sock;
	for ( ;; ) {
		mask = omask;
		if (select (32, &mask, 0, 0, 0) <= 0)
			continue;
		len = sizeof (sin);
		if ((rval = recvfrom (sock, buf, SIZ, 0, &sin, &len)) <= 0) {
			syslog (LOG_ERR, "recvfrom: %m");
			continue;
		}
		if (*buf != ESC)
			continue;
		buf[rval] = '\0';
		parse (buf+2, av);
		switch (buf[1]) {		/* figure out command */
			case PAGE:		page (av, sin);		break;
			case REINVITE:	reinvite (av, sin);	break;
			case INQUIRE:	inquire (av, sin);	break;
			case ANSWER:	answer (av);		break;
			case DAEMON:	daemon (sin);		break;
			case WHO:		who (sin);			break;
			case KILL:		exit (0);			break;
		}
	}
}


/*
 *  Parse the buffer into an argument vector.
 */

parse (buf, argv)
char  *buf;
char  **argv;
{
	register	char **ap;
	register	char *b;

	ap = argv;

	for (b = buf; b && *b && *b != '\n'; ) {
		*ap++ = b;
		for ( ; b && *b && *b != ':' && *b != '\n'; b++)
			;
		*b++ = '\0';
	}
	*ap = (char *) 0;
	return (ap - argv);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'page.c'" '(3334 characters)'
if test -f 'page.c'
then
	echo shar: will not over-write existing file "'page.c'"
else
cat << \!Funky!Stuff! > 'page.c'
#ifndef lint
static char RCSid[] = "$Header: page.c,v 1.1 85/10/28 17:38:29 broome Exp $";
#endif

/*
 * $Log:	page.c,v $
 * Revision 1.1  85/10/28  17:38:29  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <netdb.h>
#include <pwd.h>
#include <sys/time.h>

/*
 *   He wants us to page someone...
 *
 *   argv points to array like this: "callno : callee:tty:caller:conv_addr"
 *
 *   If we already have a call from the same address with the same call
 *   number then we assume it's a retransmission and just resend the
 *   same message-id (the one generated by us) and hope it will make 
 *   it to them.
 *
 *   We try to get the user's password entry so that we can
 *   look for a .busy forwarding file when we actually ring him.
 */

page (argv, sin)
char   *argv[];
struct sockaddr_in sin;
{
	long     now;
	register INV    *new;
	INV		 *seenit();
	struct   passwd  *pw, *getpwnam();
	struct   hostent *hp, *gethostbyaddr();

	/* first check to see if we already have the request */
	if (new = seenit (argv[0], sin)) {
		(void) sprintf (buf, "%c%c%c%s", ESC, PAGE, ACK, new->id);
		(void) sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
		return;
	}

	if (freelist) {
		new = freelist;			/* grab one from existing list */
		freelist = new->next;
		free (new->caller);		/* deallocate space from last time */
		free (new->callee);
		free (new->host);
		free (new->home);
		free (new->tty);
		free (new->convaddr);
		if (new->ptty)
			free (new->ptty);
	} else {			/* need to malloc new space */
		if ((new = (INV *) malloc (sizeof (INV))) == (INV *) 0) {
			perror ("malloc failed...");
			exit (1);
		}
	}

	new->callno   = strsave (argv[0]);		/* caller's form of call id */
	new->callee   = strsave (argv[1]);		/* person being called */
	new->tty      = strsave (argv[2]);		/* his tty */
	new->caller   = strsave (argv[3]);		/* caller name */
	new->convaddr = strsave (argv[4]);		/* conversation address */

	time (&now);

	/*  
	 *  Acknowledge immediately. We make an id 
	 *  from the lower 4 bits of the time.  
	 */

	sprintf (new->id, "%05ld", (now & 9999L));
	sprintf (buf, "%c%c%c%s", ESC, PAGE, ACK, new->id);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));

	/* figure out host name */
	if (hp = gethostbyaddr (&sin.sin_addr, sizeof (struct in_addr), AF_INET))
		new->host = strsave (hp->h_name);
	else
		new->host = strsave (inet_ntoa (sin.sin_addr.s_addr));

	/* save control address */
	bcopy ((char *)&sin, (char *)&new->ctladdr, sizeof (struct sockaddr_in));

	/* lookup callee in password file */
	if (pw = getpwnam (argv[0])) {
		new->home = strsave (pw->pw_dir);
		new->uid  = pw->pw_uid;
		new->gid  = pw->pw_gid;
	} else
		new->home = (char *) 0;

	new->rings  = 0;		/* so they get it next time around */
	new->ptty   = (char *) 0;
	new->prev   = NIL;

	insert (new);			/* and add to the pending list */
}


/*
 *  Search through the list of invitations, looking for one
 *  from the same address as this, with the same call number as well.
 */

INV *
seenit (callno, addr)
char   *callno;
struct sockaddr_in addr;
{
	register	INV	*inv;

	for (inv = invitations; inv; inv = inv->next)
		if (strcmp (callno, inv->callno) == 0 && 
			bcmp ((char *)&addr, (char *)&inv->ctladdr, 
			sizeof (struct sockaddr_in)) == 0)
				return (inv);
	return (NIL);
}

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'pagetty.c'" '(1602 characters)'
if test -f 'pagetty.c'
then
	echo shar: will not over-write existing file "'pagetty.c'"
else
cat << \!Funky!Stuff! > 'pagetty.c'
#ifndef lint
static char RCSid[] = "$Header: pagetty.c,v 1.1 85/10/28 17:38:31 broome Exp $";
#endif

/*
 * $Log:	pagetty.c,v $
 * Revision 1.1  85/10/28  17:38:31  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"
#include <sys/time.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/file.h>


/*
 *  Page a local user to his terminal. 
 *  We compose the message, then send it 
 *  in one big block so as to minimise messing
 *  him up if he's in vi or the like.
 */

pagetty (inv, tty)
INV  *inv;
char *tty;
{
	struct  tm   *tm;
	long    now;
	char    buf[100], mesg[300];
	int     fd;		/* tty file descriptor */

	time (&now);
	tm = localtime (&now);

	/*  Now to compose the message  */
#ifdef notdef
	sprintf (buf, "\r\n\7Message from Phone_Daemon@%s at %d:%02d ...\r\n", 
				host, tm->tm_hour, tm->tm_min);
#endif
	sprintf (buf, "\r\n\7Message from the Telephone_Operator@%s at %d:%02d ...\r\n", 
				host, tm->tm_hour, tm->tm_min);
	strcpy (mesg, buf);

	sprintf (buf, "phone: connection requested by %s@%s\r\n", 
				inv->caller, inv->host);
	strcat (mesg, buf);

	sprintf (buf, "phone: respond with \"phone %s", inv->caller);
	strcat (mesg, buf);

	/* only list host if it differs from our own */
	if (strcmp (inv->host, host)) {
		strcat (mesg, "@");
		strcat (mesg, inv->host);
	}

	strcat (mesg, "\"\r\n\r\n\7");

	/*  And send it  */
	if ((fd = open (tty, O_WRONLY, 0444)) < 0) {   /* shouldn't happen */
		syslog (LOG_ERR, "phoned: can't open %s: %m", tty);
		_exit (-99);
	}
	
	(void) write (fd, mesg, strlen (mesg));
	(void) close (fd);

	_exit (0);
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'reinvite.c'" '(773 characters)'
if test -f 'reinvite.c'
then
	echo shar: will not over-write existing file "'reinvite.c'"
else
cat << \!Funky!Stuff! > 'reinvite.c'
#ifndef lint
static char RCSid[] = "$Header: reinvite.c,v 1.1 85/10/28 17:38:35 broome Exp $";
#endif

/*
 * $Log:	reinvite.c,v $
 * Revision 1.1  85/10/28  17:38:35  broome
 * Initial revision
 * 
 */

#include "../common.h"
#include "defs.h"

/*
 *  Reinvite the given invitation by resetting the `rings' field to zero.
 */

reinvite (argv, sin)
char   **argv;
struct sockaddr_in sin;
{
	register	INV *inv;
	register	int found = 0;

	for (inv = invitations; inv; inv = inv->next) {
		if (strcmp (inv->id, *argv) == 0 && 
		  bcmp ((char *)&sin, (char *)&(inv->ctladdr), sizeof (sin)) == 0) {
			inv->rings = 0;
			found = 1;
			break;
		}
	}
	sprintf (buf, "%c%c%c%s", ESC, REINVITE, found ? ACK : NAK, *argv);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
}
!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'strsave.c'" '(407 characters)'
if test -f 'strsave.c'
then
	echo shar: will not over-write existing file "'strsave.c'"
else
cat << \!Funky!Stuff! > 'strsave.c'
#ifndef lint
static char RCSid[] = "$Header: strsave.c,v 1.1 85/10/28 17:38:36 broome Exp $";
#endif

/*
 * $Log:	strsave.c,v $
 * Revision 1.1  85/10/28  17:38:36  broome
 * Initial revision
 * 
 */

/*
 *  Allocate enough space for the given string and copy it over.
 */

char *
strsave (s)
char *s;
{
	char *malloc();
	char *new;

	if (new = malloc (strlen (s) + 1))
		strcpy (new, s);
	return (new);
}

!Funky!Stuff!
fi # end of overwriting check
echo shar: extracting "'utmp.c'" '(4186 characters)'
if test -f 'utmp.c'
then
	echo shar: will not over-write existing file "'utmp.c'"
else
cat << \!Funky!Stuff! > 'utmp.c'

#ifndef lint
static char RCSid[] = "$Header: utmp.c,v 1.1 85/10/28 17:38:37 broome Exp $";
#endif

/*
 * $Log:	utmp.c,v $
 * Revision 1.1  85/10/28  17:38:37  broome
 * Initial revision
 */

#include "../common.h"
#include "defs.h"
#include <sys/stat.h>
#include <sys/file.h>
#include <utmp.h>


/*
 *   Routines for dealing with /etc/utmp.
 *
 *   We use a statically-allocated array because readutmp() is
 *   called at interrupt-level and we don't want to mess up malloc()
 *   and free as a result.
 */

#ifndef	LINELEN				/* length of line for "who()" */
#define	LINELEN		76		/* not 80 - for magic-cookie terminals */
#endif

#ifndef	MAXUSERS
#define	MAXUSERS	64
#endif

static	struct	utmp utbuf[MAXUSERS];				/* contents of file */
static	int		numents;							/* number of users on */
static	int		utfd;								/* file descriptor */


/*
 *  Initialize by opening the file and malloc'ing space.
 */

openutmp ()
{
	register	int i;
	struct		stat statb;

	if ((utfd = open ("/etc/utmp", O_RDONLY)) < 0) {
		perror ("can't open utmp");
		return;		/* exit?? */
	}
}


/*
 *  Reread /etc/utmp from the open file descriptor into the buffer.
 *  We test/set the "here" flag so we don't try to read at interrupt level
 *  if we're already doing it normally at the same time. (in "who()")
 */

readutmp ()
{
	static	int	here = 0;
	int	cc;

	if (here)
		return;
	here = 1;
	lseek (utfd, 0L, 0);	/* rewind */
	cc = read (utfd, utbuf, sizeof (utbuf));	/* and read */
	numents = cc / sizeof (struct utmp);
	here = 0;
}



/*
 *  Go through the utmp buffer, trying to find the named user,
 *  possibly with the tty specified.
 */

char *
findtty (user, tty, mode)
char *user;
char *tty;
int  *mode;
{
	static 		char   ttybuf[15];
	register	int    i;
	register	struct utmp *utmp;
	struct		stat   statb;

	*mode = NOT_ON;							/* start by assuming he's not on */
	for (i = 0; i < numents; i++) {
		utmp = &utbuf[i];
		if (*utmp->ut_name == '\0')              /* noone on this port */
			continue;
		if (strncmp (utmp->ut_name, user, 8))    /* names don't match */
			continue;
		if (tty && *tty && strncmp (tty, utmp->ut_line, 8)) /* not spec'd tty */
			continue;
		strcpy  (ttybuf, "/dev/");
		strncat (ttybuf, utmp->ut_line, 8);
		if (stat (ttybuf, &statb))	/* error on tty? */
			continue;
		if ((statb.st_mode & 02) == 0) {
			*mode = MESG_OFF;		/* refusing messages */
			if (tty && *tty)		/* they specified a particular tty */
				break;
			else
				continue;			/* see if we can find another one */
		} else {					/* all is okay, use this one */
			*mode = 0;
			return (ttybuf);
		}
	}
	return ((char *) 0);
}


/*
 *  Send a "who" message to the given address ...
 *  We go through the utmp buffer, building LINELEN-long buffers
 *  and send them on over to the user.
 */

who (sin)
struct sockaddr_in sin;
{
	register	struct	utmp *utmp;
	register	int		i;
	register	int		ulen;
	register	int		len = LINELEN + 1;
	register	int		users = 0;
	char		buf[LINELEN+5];		/* buffer for whole message */
	char		ubuf[20];			/* buffer for one user and tty */

#ifdef NO_WHO		/* not allowed here ... */
	sprintf (buf, "%c%c%cwho@%s: this site doesn't allow remote who.", 
				ESC, NAK, WHO, host);
	(void) sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
	return;
#else !NO_WHO	/* not so paranoid here */

	sprintf (buf, "%c%c%cwho info coming...", ESC, WHO, ACK);
	sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));

	readutmp ();

	buf[0] = '\0';
	for (i = 0; i < numents; i++) {
		utmp = &utbuf[i];
		if (*utmp->ut_name == '\0')		/* noone on that line */
			continue;
		sprintf (ubuf, " %.8s(%.5s)", utmp->ut_name, utmp->ut_line);
		ulen = strlen (ubuf);
		if (ulen + len + 1 < LINELEN)
			strcat (buf, ubuf);
		else {
			if (users)
				(void) sendto (misc, buf, len+4, 0, &sin, sizeof (sin));
			sprintf (buf, "%c%c%cwho@%s:", ESC, MESSAGE, ACK, host);
			len = strlen (buf) - 4;
			strcat (buf, ubuf);
		}
		len += ulen;
		users++;
	}
	if (users == 0) {
		sprintf (buf, "%c%c%c%s: Noone logged on.", 
				ESC, MESSAGE, ACK, host);
		sendto (misc, buf, strlen (buf), 0, &sin, sizeof (sin));
	} else if (len)
		(void) sendto (misc, buf, len+4, 0, &sin, sizeof (sin));

#endif NO_WHO
}
!Funky!Stuff!
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Comp.sources.unix mailing list