Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.3 4.3bsd-beta 6/6/85; site ucbvax.BERKELEY.EDU Path: utzoo!linus!decvax!ucbvax!broome From: broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) Newsgroups: net.sources Subject: source to "phone" system (part 3 of 4) Message-ID: <11313@ucbvax.BERKELEY.EDU> Date: Sun, 29-Dec-85 03:42:27 EST Article-I.D.: ucbvax.11313 Posted: Sun Dec 29 03:42:27 1985 Date-Received: Sun, 29-Dec-85 20:17:59 EST Organization: University of California at Berkeley Lines: 1698 #-----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 #include #include #include #include #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 /* * 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 #include /* * 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 /* * 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 /* * 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 #include #include 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 #include #include #include #ifdef FORK #include #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 #include #include /* * 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 #include #include #include /* * 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 #include #include /* * 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