broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)
#-----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: # NOTE # READ_ME # phone.1 # Makefile # common.h # This archive created: Sat Dec 28 01:18:51 1985 export PATH; PATH=/bin:$PATH echo shar: extracting "'NOTE'" '(2690 characters)' if test -f 'NOTE' then echo shar: will not over-write existing file "'NOTE'" else cat << \!Funky!Stuff! > 'NOTE' Well, this is the first part of the source to my "phone" program, as promised. I received quite a few responses, many with weird addresses, thus the posting instead of a mailing. Be warned that this source is *not* a finished product, but more a "gamma test" version, although it has been running on a dozen UCB machines for several months and seems to work reasonably well. For those of you who may feel compelled to hack on it, I have laced the sources with quite a few comments about possible improvements. I would also welcome any suggestions/code you may come up with. Hopefully I will be able to send out a finished product sometime in the coming few months ... A few of the letters I received mentioned an interest in porting this to SysV machines and such ... Good luck! (Last I heard, AT&T had just heard of IPC!) This has only been tested on 4.[23] machines (Vax, Sun, and Perkin/Elmer), but will hopefully be easy enough to port to other hardware. Installing phone: Create an empty directory to put the sources in. Unshar the archive files by feeding to "sh" (or use "unshar"). Run "make", then "make install" (use "make -n" first if you're paranoid!) Add "phone" to /etc/services, the line should look like: phone 1167/udp # phone - conference calling (broome) A note on port numbers: there is no need for phone to use priviledged port numbers (think about it), and it would really be best for you to stick with port 1167 (what we use at Berkeley), at least if you want to be able to communicate with the outside world. I am working on an rfc-type spec in order to get a legitimately-assigned port number, but it will take a little while, so let's use 1167, okay? Back to installation: If your site runs the inetd, also add this line to /etc/services: phone dgram udp wait root /etc/phoned phoned Finally, if you do not use inetd, start up the phoned by typing "/etc/phoned", else do a "ps ax | grep inetd" to find inetd's pid, then kill -HUP pid to have it reread the config file. The manual page is presently pretty skimpy, so you may want to look at the routines in kb.c and cmd.c to get some idea as to what is going on. (Brownie points to anyone who writes a better man page!) For those of you with arpanet access, the sources will also be available in ~ftp/pub/phone.tar on ucbvax for your ftp'ing enjoyment. Good luck, and enjoy! ============================================================================== Jonathan C. Broome University of California at Berkeley ...!ucbvax!broome broome@ucb-vax.berkeley.edu ============================================================================== !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'READ_ME'" '(1102 characters)' if test -f 'READ_ME' then echo shar: will not over-write existing file "'READ_ME'" else cat << \!Funky!Stuff! > 'READ_ME' Stuff for installing phone: 22 Dec 1985 =========================== =========== Phone consists of three parts - the client ("phone"), the master daemon ("phoned"), and the conversation daemon ("convd"). "Phoned" is the master server on each machine. It receives requests from the client process and acts on them. Its main purpose is to receive, store, and process call requests. All communication to phoned is through udp/ip datagrams. The master daemon can be compiled to run standalone or under the inetd as a single-threaded datagram-oriented server. The conversation daemon is invoked by the master daemon when a user requests a conversation. Its purpose is simply to relay what each person types back to all the others in a single conversation. Each user connects to the convd over a tcp/ip stream connection, and the convd reads a buffer from each client and resends the buffer to the other users. "Phone" is the user client program, responsible for managing windows and sending control packets to request calls. !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'phone.1'" '(4210 characters)' if test -f 'phone.1' then echo shar: will not over-write existing file "'phone.1'" else cat << \!Funky!Stuff! > 'phone.1' .TH PHONE 1 .UC 5 .SH NAME phone \- communicate with other users in real-time .SH SYNOPSIS phone [ user@host [tty] ] .SH DESCRIPTION \fIPhone\fR allows for two or more people to interact in a conversation across a machine or network, providing a form of conference calling. Each participant has a window in which to type. The first line of each window is a header showing who is in the window, like: .sp 1 .nf .in +3 ---- root@cory on console (Commodore Cory) -------------- .in -3 .fi .sp 1 The login name and tty are automatically determined, and the real name is taken from the password file, which may be overridden by setting the \fINAME\fR environment variable (see \fIcsh\fR(1) for further details.) Users may join or leave a conversation at any point in time, and the windows will be automatically resized and redrawn. .SH USAGE When you are being paged by another person, a message like this will appear on your screen: .br .sp 1 .nf .in +5 Message from the Telephone_Operator@host at time ... phone: connection requested by user@host phone: respond with "phone user@host" .in -5 .sp 1 .fi You may answer the phone simply by typing "phone", which will answer the pending call, and connect you directly. .br .sp 1 \fIPhone\fR has two \fImodes\fR, much like the \fIvi\fR editor. These two modes are called \fIconversation\fR and \fIcommand\fR modes, and are toggled through the escape (<esc>) and return (<ret>) keys. .br .sp 1 When in the \fIconversation\fR mode, anything typed on the keyboard is sent to everyone in the current conversation. This is the default mode. .br .sp 1 The \fIcommand\fR mode is used to execute commands, and is entered by pressing the escape key. When in this mode, \fIphone\fR will clear the bottom line of the screen and print the prompt "Command>". At this point anything typed in is added to the command buffer, and will be executed when the return key is pressed. To exit command mode without executing the acommand, press the escape key a second time. .br .sp 1 To ivite another user to join the current conversation from within \fIphone\fP, enter command mode by pressing the escape key, then type .br .sp 1 .in +5 call \fIuser@host\fR .in -5 .sp 1 followed by the <return> key. The user will receive a message like the one shown above if he is logged in. The \fIhost\fR part of the name may be omitted if the both you and the other person are on the same machine. .br .sp 1 \fIPhone\fR also allows a user to execute shell commands inside his window with any keyboard input being fed to the process. The program's output is sent to all users in the conversation. A shell command is executed within \fIphone\fR through the use of the \fIrun\fR or \fI!\fR command. An example of this is: .br .sp 1 .in +5 run adb a.out core .in -5 .sp 1 to run the \fIadb\fR command with the arguments \fIa.out\fR and \fIcore\fR. Note that tilde expansion (ie. ~user) is done by \fIphone\fR, but wildcarding, piping, and i/o redirection are performed by the user's shell. It is unlikely that anyone actually cares, of course. Also, the use of visually-oriented programs such as \fIvi\fR and \fIrogue\fR is not recommended, as this usually results in strange and unpredictable things happening. If your terminal goes up in a puff of smoke, you were warned. .br .sp 1 To find about the other commands available with \fIphone\fR, type \fIhelp\fR or \fI?\fR in command mode. .PP You can allow or disallow \fIphone\fR messages to your terminal through the use of the \fImesg\fR command. When you first log on, messages are enabled. .SH BUGS \fICsh\fR is unhappy being fed through pipes, but it's a dumb program anyway. .br The manual page is horrendous at best. .br Please send any problems, questions, or suggestions to the author. .SH AUTHOR Jonathan C. Broome (broome@ucb-vax.berkeley.edu) .br The original user interface is borrowed from a previous program (also called \fIphone\fR) posted to the network in late 1984, author unknown. .SH FILES .ta \w'/etc/passwd 'u .br /etc/hosts to find the recipient's machine .br /etc/utmp to find the recipient's tty .br /etc/passwd to find each user's real name .SH "SEE ALSO" mail(1), mesg(1), talk(1), who(1), write(1) !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'Makefile'" '(310 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \!Funky!Stuff! > 'Makefile' # # Master makefile for the phone program 18 December 1985 # # You'll need to edit the makefile in each of the subdirectories # to change the compilation flags. # DIRS = client master conv .DEFAULT: for i in ${DIRS} ; do \ cd $$i ; make MFLAGS="${MFLAGS}" $@ ; cd .. ; \ done default: all !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 # End of shell archive exit 0
broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)
#-----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 # alias.c # calls.c # check_invite.c # connect_daemon.c # cmd.c # getdaemon.c # kb.c # main.c # message.c # misc.c # names.c # parse.c # readctl.c # readrc.c # readstream.c # runprog.c # sendit.c # set.c # sig.c # stop.c # strsave.c # tilde.c # timer.c # who.c # windows.c # This archive created: Sat Dec 28 01:11:00 1985 export PATH; PATH=/bin:$PATH mkdir client cd client echo shar: extracting "'Makefile'" '(4691 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \!Funky!Stuff! > 'Makefile' # # Makefile for phone client 20 December 1985 # # What all this stuff means:: # SERVICES - define this is you have an entry for phone in /etc/services # PORT - if SERVICES is not defined, set this to the port number that # phone should use (this overrides the default) # LOCAL_ECHO - if this is defined, what the user types will be echoed # locally, and the remote echo will be ignored. The option # is really an old relic, and should always be on. # # Also: # o Add "-lresolv" to the library if you want to use the network name # server and have the library routines. # o Add "-Dvoid=int" if your compiler doesn't know about the void type. CC = cc CFLAGS = -O -DSERVICES -DLOCAL_ECHO LIBS = -lcurses -ltermlib -lresolv LPR = lpr -Pvax RDEST = /usr/local/phone HDRS = defs.h SRCS = alias.c calls.c check_invite.c connect_daemon.c cmd.c \ getdaemon.c kb.c main.c message.c misc.c names.c \ parse.c readctl.c readrc.c readstream.c runprog.c \ sendit.c set.c sig.c stop.c strsave.c tilde.c timer.c \ who.c windows.c OBJS = alias.o calls.o check_invite.o connect_daemon.o cmd.o \ getdaemon.o kb.o main.o message.o misc.o names.o \ parse.o readctl.o readrc.o readstream.o runprog.o \ sendit.o set.o sig.o stop.o strsave.o tilde.o timer.o \ who.o windows.o DEST = phone .DEFAULT: co $< all: ${DEST} ${DEST}: ${OBJS} /bin/rm -f ${DEST} ${CC} ${CFLAGS} -o ${DEST} ${OBJS} ${LIBS} ${OBJS}: ${HDRS} install: ${DEST} /bin/rm -f ${RDEST} cp ${DEST} ${RDEST} print: ${HDRS} ${SRCS} pr -f ${HDRS} ${SRCS} | expand -4 | ${LPR} tags: /dev/null ctags -w ${HDRS} ${SRCS} tar: ${HDRS} ${SRCS} tar cf client.tar ${HDRS} ${SRCS} Makefile shar: Makefile ${HDRS} ${SRCS} shar -v Makefile ${HDRS} ${SRCS} > ../shar.client clean: /bin/rm -f ${OBJS} ${DEST} core lint: ${HDRS} ${SRCS} lint ${CFLAGS} ${SRCS} > lint.out dist: ${DEST} -rcp ${DEST} buddy:${RDEST} -rcp ${DEST} franny:${RDEST} -rcp ${DEST} holden:${RDEST} -rcp ${DEST} seymour:${RDEST} -rcp ${DEST} zooey:${RDEST} -rcp ${DEST} miro:${RDEST} -rcp ${DEST} cory:${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 who.o: /usr/include/sys/file.h who.o: /usr/include/utmp.h runprog.o: /usr/include/fcntl.h runprog.o: /usr/include/sys/resource.h runprog.o: /usr/include/sys/wait.h tilde.o names.o: /usr/include/pwd.h windows.o stop.o runprog.o readstream.o misc.o kb.o: /usr/include/sgtty.h windows.o stop.o runprog.o readstream.o misc.o kb.o: /usr/include/curses.h windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \ /usr/include/sys/ttydev.h windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \ /usr/include/sys/ttychars.h windows.o windows.o stop.o sig.o runprog.o readstream.o misc.o kb.o: \ /usr/include/sys/ioctl.h runprog.o main.o getdaemon.o: /usr/include/errno.h windows.o strsave.o stop.o sig.o message.o main.o getdaemon.o \ connect_daemon.o: /usr/include/signal.h sendit.o runprog.o getdaemon.o calls.o: /usr/include/time.h sendit.o runprog.o getdaemon.o calls.o: /usr/include/sys/time.h who.o names.o connect_daemon.o calls.o: /usr/include/netdb.h who.o runprog.o readstream.o readctl.o names.o main.o getdaemon.o \ connect_daemon.o check_invite.o calls.o: ./../common.h windows.o strsave.o stop.o runprog.o readstream.o readrc.o names.o misc.o \ message.o main.o kb.o connect_daemon.o calls.o: /usr/include/stdio.h windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \ readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \ check_invite.o calls.o alias.o: /usr/include/netinet/in.h windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \ readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \ check_invite.o calls.o alias.o: /usr/include/sys/socket.h windows.o who.o stop.o set.o sendit.o runprog.o runprog.o readstream.o \ readrc.o readctl.o names.o misc.o message.o main.o kb.o getdaemon.o \ connect_daemon.o check_invite.o calls.o alias.o: /usr/include/sys/types.h windows.o who.o stop.o set.o sendit.o runprog.o readstream.o readrc.o \ readctl.o names.o misc.o message.o main.o kb.o getdaemon.o connect_daemon.o \ check_invite.o calls.o alias.o: ./defs.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'" '(1720 characters)' if test -f 'defs.h' then echo shar: will not over-write existing file "'defs.h'" else cat << \!Funky!Stuff! > 'defs.h' #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define BUFFER 512 /* internal character buffer size */ char *sprintf(); char *index(); char *strsave(); char *malloc(); char *basename(); char *expalias(); char *login; /* user's login name */ char *tty; /* tty he's on */ char *home; /* home directory */ char *shell; /* preferred shell */ extern char host[]; /* local host name */ extern char realname[]; /* from password file */ int maxx; /* max legal x coordinate */ int maxy; /* max legal y coordinate */ int stream; /* stream socket filedes */ int ctl; /* control socket fd */ int reading; /* currently reading kb? */ extern char buf[]; /* general-use buffer */ extern int pending; /* number of pending calls */ int connected; /* number of connected people */ int touched25; /* message changed line 25 */ struct sockaddr_in locaddr; /* localhost address */ extern char convaddr[]; /* conversation address */ int port; /* phoned socket port */ #ifndef ESC #define ESC '\033' #endif #define PROMPT "Command> " #define ctrl(C) ('C'-'@') #define isdigit(c) ('0' <= c && c <= '9') #define equal(a,b) (strcmp (a,b) == 0) /* * Various oft-used flags. */ int Interval; /* seconds between messages */ int Inverse; /* allow use of inverse video */ int Stop; /* print "Stopped" to socket on ^Z */ int Debug; /* verbose tracing stuff */ int Bells; /* let ^G come through as bell */ int Hold; /* send input to child process */ int Echo; /* send input to socket when running prog */ !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'alias.c'" '(1316 characters)' if test -f 'alias.c' then echo shar: will not over-write existing file "'alias.c'" else cat << \!Funky!Stuff! > 'alias.c' #include "defs.h" /* * Routines to handle phone aliases, much like mail aliases. */ struct alias { char *name; /* name of alias */ char *real; /* and what it expands to */ struct alias *next; }; static struct alias *aliases = (struct alias *) 0; /* * Set up an alias. */ alias (argc, argv) int argc; char *argv[]; { struct alias *ap; if (argc == 0) { /* just show aliases */ message ("Showing all aliases:"); for (ap = aliases; ap; ap = ap->next) { sprintf (buf, "%s --> %s", ap->name, ap->real); message (buf); } message ("That's all!"); return; } if (argc == 1) { putmessage ("alias: arg count must be 0 or 2!"); return; } ap = (struct alias *) malloc (sizeof (struct alias)); if (ap == (struct alias *) 0) { error (0, "alias: cannot allocate memory for alias"); return; } ap->name = strsave (argv[0]); ap->real = strsave (argv[1]); ap->next = aliases; aliases = ap; } /* * Expand an alias. It copies the expansion into a buffer so * that the calling routines can munge it to their heart's content. */ char * expalias (name) char *name; { register struct alias *ap; static char buf[128]; for (ap = aliases; ap; ap = ap->next) if (strcmp (name, ap->name) == 0) { strcpy (buf, ap->real); return (buf); } return ((char *) 0); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'calls.c'" '(7403 characters)' if test -f 'calls.c' then echo shar: will not over-write existing file "'calls.c'" else cat << \!Funky!Stuff! > 'calls.c' #include <stdio.h> #include "defs.h" #include "../common.h" #include <netdb.h> #include <sys/time.h> /* client pending call structure */ struct call { char *user; /* user's name */ char *host; /* his host */ char *tty; /* his tty */ struct sockaddr_in addr; /* socket address */ char id[11]; /* used to talk to daemon */ int rings; /* ticks since last ring */ int status; /* has daemon acted on it? */ struct call *next; /* next one in the list */ }; static struct call *calls = (struct call *) 0; /* list of pending calls */ int pending = 0; /* number of pending calls */ /* ** Place a call. ** ** Parameters: ** user - the login name of the person to call ** host - his hostname, in either normal form ('ucbarpa') or ** internet dotted quad form ('128.32.149.5') ** tty - a specified tty, if any, other the null string ("") ** ** What we do: ** 1. Send a request packet to the phone daemon at his address, ** packet looks like "<esc> P callno : user : tty : myname : convaddr". ** ** Callno is a unique request identifier that the daemon uses ** to guard against that duplicate messages occurring due to ** the time delay between placing the call and getting the response ** causing the client to retransmit the request. [Got that?] ** ** If we are using the master/slave protocol, the convaddr is ** preceded by either 'y' or 'n' to indicate whether or not we ** will be willing to give up our convd and go to his. (This ** is used for joining an existing conversation.) ** [Not used yet ...] ** ** 2. We expect to get a response back from his phoned of the form ** "<ESC> P y id" where `id' is a small 5-character request id which ** is to be used when 'renewing' a page request. ** ** 3. Then save all of the info (user, host, tty, message id) in ** a 'call' structure which is added to the linked list of pending ** calls. ** ** 4. As a side effect, the global variable 'pending' is incremented. */ placecall (user, host, tty) char *user; char *host; char *tty; { struct call *new; /* new call structure */ struct hostent *hp; /* host info */ struct hostent *gethostbyname(); struct sockaddr_in sin; /* network address */ static int callno = 0; extern int users; int rval; if (isdigit (*host)) /* numeric address ??? */ sin.sin_addr.s_addr = inet_addr (host); else { if ((hp = gethostbyname (host)) == (struct hostent *) 0) { sprintf (buf, "Unknown host: %s", host); putmessage (buf); return; } bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length); } sin.sin_family = AF_INET; sin.sin_port = port; sprintf (buf, "Calling %s@%s", user, host); message (buf); #ifdef SLAVE sprintf (buf, "%c%c%03d:%s:%s:%s:%c%s", ESC, PAGE, callno++, user, tty ? tty : "", login, users > 1 ? NAK : ACK, convaddr); #else !SLAVE sprintf (buf, "%c%c%03d:%s:%s:%s:%s", ESC, PAGE, callno++, user, tty ? tty : "", login, convaddr); #endif SLAVE if ((rval = sendit (buf, strlen (buf), sin)) < 0) { message ("No response to call request."); return; } /* got a response - text is in buf. */ buf[rval] = '\0'; new = (struct call *) malloc (sizeof (struct call)); new->user = strsave (user); new->host = strsave (host); new->tty = strsave (tty); new->rings = 1; strncpy (new->id, buf+3, 10); /* save the mesg id # */ bcopy ((char *)&sin, (char *)&new->addr, sizeof (sin)); new->next = calls; /* put list together */ calls = new; pending++; /* increment the number of pending calls */ return; } /* ** Go through the list of pending calls and reinvite ** all the calls that have expired. Returns 1 if the timer ** should be reset, 0 if the timer should expire. ** ** If SLAVE is defined, we check every call to see they have ** tried to call us. If they indicate that they are *not* willing ** to submit (they are in an existing conversation), then we close ** our existing convd connection and connect to their convd; else ** we go over only if our convd address is lower than theirs. ** (Yes, this looks like a hack, but it seems to work best.) */ reinvite () { register struct call *ptr; extern int users; if (pending == 0) /* nothing to do */ return (0); for (ptr = calls; ptr; ptr = ptr->next) { /* * See if we've connected to anyone, * if not then first call our local host and see if the * intended recipient is trying to call us... */ #ifdef SLAVE if (users < 2 && pending < 2) { int r; if (Debug) putmessage ("Checking for reverse calls..."); sprintf (buf, "%c%c%s:%s", ESC, INQUIRE, login, ptr->user); if ((r = sendit (buf, strlen (buf), locaddr)) > 0) { buf[r] = '\0'; if (Debug) printf ("Received \"%s\"\r\n", buf+1); if (buf[2] == ACK && (buf[3] == NAK || strcmp (convaddr, buf+4) < 0) && connect_daemon (buf+4) == 0) { putmessage ("Switching to conversation already in progress."); delete (ptr->id); /* need to reset status so windows work */ users = 0; return; } } putmessage (""); } #endif SLAVE if (ptr->rings == 0) { /* time to reinvite */ buf[0] = ESC; buf[1] = REINVITE; strcpy (buf+2, ptr->id); if (sendit (buf, strlen (buf), ptr->addr) < 0) error (0, "reinvite: sendto"); /* should check response value!!! */ } ptr->rings = (ptr->rings + 1) % 20; /* reinvite every 20 secs. */ } return (1); } /* ** Delete the call `id' from the pending list. */ delete (id) char *id; { register struct call *curr; register struct call *prev = (struct call *) 0; register struct call *next; for (curr = calls; curr; curr = next) { next = curr->next; if (strncmp (id, curr->id, 5) == 0) { /* this is the one */ if (prev) prev->next = next; else calls = next; free (curr->user); free (curr->host); free (curr->tty); free (curr); pending--; return; } } } /* ** Front end routine --- Page a user to join in the conversation. */ page (argc, argv) int argc; char *argv[]; { char *hishost; char *tty; char *user; char *index(); if ((user = expalias (argv[0])) == (char *) 0) user = argv[0]; if (hishost = index (user, '@')) *hishost++ = '\0'; else hishost = host; tty = (argc == 2) ? argv[1] : (char *) 0; placecall (user, hishost, tty); } /* ** Cancel a call sent out by simply not bothering to "renew" it. */ cancel (argc, argv) int argc; char *argv[]; { char *user; char *hishost; char *tty; struct call *curr, *prev, *next; if ((user = expalias (argv[0])) == (char *) 0) user = argv[0]; tty = (argc == 2) ? argv[1] : (char *) 0; if (hishost = index (user, '@')) *hishost++ = '\0'; else hishost = host; prev = (struct call *) 0; for (curr = calls; curr; curr = next) { next = curr->next; if (equal (user, curr->user) && equal (hishost, curr->host)) { if (prev) prev->next = next; else calls = next; free (curr->user); free (curr->host); free (curr->tty); free (curr); pending--; sprintf (buf, "Cancelling call to %s@%s", user, hishost); if (tty) { strcat (buf, " on "); strcat (buf, tty); } message (buf); return; } } sprintf (buf, "No pending calls to %s@%s", user, hishost); message (buf); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'check_invite.c'" '(1106 characters)' if test -f 'check_invite.c' then echo shar: will not over-write existing file "'check_invite.c'" else cat << \!Funky!Stuff! > 'check_invite.c' #include "../common.h" #include "defs.h" /* * Check to see if we have any pending invitations from the named user. * Returns the string containing our given address (or NULL) */ /*ARGSUSED*/ char * check_invite (user, host) char *user; char *host; { struct sockaddr_in sin; /* address of daemon socket */ char mbuf[80]; /* one line of incoming message */ int r, tries; /* socket address "locaddr" is already initialised to the local host */ bcopy ((char *)&locaddr, (char *)&sin, sizeof (sin)); sin.sin_port = port; sprintf (mbuf, "%c%c%s:%s", ESC, INQUIRE, login, user); for (tries = 0; tries < 5; tries++) { if ((r = sendit (mbuf, strlen (mbuf), sin)) <= 0) { if (Debug) printf ("\r\ncheck_invite: sendit returned %d.\r\n", r); continue; } buf[r] = '\0'; if (Debug) printf ("check_invite: returned string is \"%s\"\r\n", buf+1); if (buf[2] == NAK) return ((char *) 0); return (strsave (buf+3)); /* skip over ESC I y */ printf ("Trying to contact phone daemon.\r\n"); } error (1, "Cannot contact local phone daemon."); return ((char *) 0); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'connect_daemon.c'" '(2397 characters)' if test -f 'connect_daemon.c' then echo shar: will not over-write existing file "'connect_daemon.c'" else cat << \!Funky!Stuff! > 'connect_daemon.c' #include "../common.h" #include "defs.h" #include <signal.h> #include <stdio.h> #include <netdb.h> /* * Connect to the conversation daemon. * Addrstr is a string containing the intended address * in the form "128.32.149.2/9535". */ connect_daemon (addrstr) char *addrstr; { struct sockaddr_in addr; char *inet_ntoa(); char *p, *index(); char str[30]; int oldval; int (*oldfunc)(); int timeout(); if (addrstr == (char *) 0 || *addrstr == '\0') error (1, "Failed to connect to conversation daemon"); oldval = alarm (0); /* turn off alarms and save old value */ #ifdef SLAVE if (*addrstr == 'y' || *addrstr == 'n') addrstr++; #endif SLAVE strcpy (convaddr, addrstr); /* save the current conversation address */ strcpy (str, addrstr); /* and use 'str' for our working copy */ if (p = index (str, '/')) /* find slash before port number */ *p++ = '\0'; if ((stream = socket (AF_INET, SOCK_STREAM, 0)) < 0) error (1, "Cannot open stream socket"); bzero ((char *)&addr, sizeof (addr)); /* connect trashes the args */ addr.sin_addr.s_addr = inet_addr (str); addr.sin_port = htons (atoi (p)); addr.sin_family = AF_INET; oldfunc = signal (SIGALRM, timeout); /* save old handler */ alarm (15); /* timeout in five seconds */ if (Debug) { printf ("Trying to connect to conversation daemon.\r\n"); fflush (stdout); } if (connect (stream, (char *)&addr, sizeof (struct sockaddr_in)) < 0) { alarm (0); close (stream); connected = 0; if (Debug) printf ("\r\n\r\nNot connected.\r\n"); } else { /* say hello */ if (Debug) { printf ("Connected to conversation daemon @ \"%s\"\r\n", convaddr); fflush (stdout); } sprintf (buf, "%s:%s:%s:%s", login, host, tty, realname); if (write (stream, buf, strlen (buf)) < 0) error (1, "getdaemon: cannot write stream socket"); connected = 1; } /* * Tell the local phoned to delete our invitaton now * that we've answered it. Not used yet. */ #ifdef notdef sprintf (buf, "%c%c%s:%s", ESC, ANSWER, login, convaddr); (void) sendto (ctl, buf, strlen (buf), 0, &locaddr, sizeof (locaddr)); #endif notdef (void) alarm (oldval); /* restore old alarm value */ (void) signal (SIGALRM, oldfunc); /* and handler routine */ return (!connected); } timeout () { putmessage ("cannot connect to conversation daemon: connection timed out."); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'cmd.c'" '(3466 characters)' if test -f 'cmd.c' then echo shar: will not over-write existing file "'cmd.c'" else cat << \!Funky!Stuff! > 'cmd.c' /* * All the (external) command functions. */ extern int alias(), cancel(), cd(), dump(), help(), page(), quit(), run(), set(), show(), longhelp(), who(), zip(); static char aliashelp[] = "Assign an alias to a user name"; static char cancelhelp[] = "Cancel a pending call to user"; static char cdhelp[] = "Change the working directory"; static char dumphelp[] = "Save a copy of the screen image into a file"; static char helphelp[] = "See help messages for any command"; static char pagehelp[] = "Invite user to join the conversation"; static char quithelp[] = "Leave the current conversation"; static char runhelp[] = "Run a program within the window"; static char sethelp[] = "Set the value of a variable"; static char showhelp[] = "See the value of a variable"; static char whohelp[] = "See who is currently logged on"; static struct cmd { char *name; /* name this command goes by */ int (*func)(); /* function to handle it */ int minargs; /* minimum number of args */ int maxargs; /* maximum number of args */ char *usage; /* usage message text */ char *help; /* help message text */ } cmds [] = { { "call", page, 2, 3, "user@host [tty]", pagehelp }, { "page", page, 2, 3, "user@host [tty]", pagehelp }, { "phone", page, 2, 3, "user@host [tty]", pagehelp }, { "cancel", cancel, 2, 3, "user@host [tty]", cancelhelp }, { "quit", quit, 1, 1, "", quithelp }, { "who", who, 1, 2, "[user]", whohelp }, { "?", help, 1, 2, "[command]", helphelp }, { "help", help, 1, 2, "[command]", helphelp }, { "set", set, 2, 3, "variable [value]", sethelp }, { "show", show, 1, 98, "[var [var]]", showhelp }, { "!", run, 2, 98, "prog [args]", runhelp }, { "run", run, 2, 98, "prog [args]", runhelp }, { "dump", dump, 1, 2, "[file]", dumphelp }, { "cd", cd, 1, 2, "[directory]", cdhelp }, { "alias", alias, 1, 3, "[alias user]", aliashelp }, { "accept", zip, 1, 99, "", "" }, { "forward",zip, 1, 99, "", "" }, { 0, 0, 0, 0, 0, 0 } }; /* * Given a buffer, parse it and execute the command. */ execute (buf) char *buf; { int len; int argc; char *argv[99]; char line[80]; struct cmd *cmd; if ((argc = parse (buf, argv)) == 0) /* null command */ return; len = strlen (argv[0]); for (cmd = cmds; cmd->name; cmd++) { if (strncmp (argv[0], cmd->name, len)) /* is it a prefix? */ continue; if (argc < cmd->minargs || argc > cmd->maxargs) { sprintf (line, "%s: bad number of arguments - usage: %s %s", cmd->name, cmd->name, cmd->usage); putmessage (line); } else (*cmd->func)(argc-1, argv+1); return; } sprintf (line, "Unknown command: \"%s\" - try \"?\" for list of commands", argv[0]); putmessage (line); } /* * Print a help message for the given command, or all commands. */ help (argc, argv) int argc; char *argv[]; { struct cmd *cmd; char buf[80]; int all = (argc == 0); int len; if (all) message ("Showing help for all commands. Commands may be abbreviated."); else len = strlen (argv[0]); for (cmd = cmds; cmd->name; cmd++) if (all || strncmp (argv[0], cmd->name, len) == 0) { if (!all || strcmp (cmd->help, (cmd+1)->help)) { sprintf (buf, "help: %s: %s.", cmd->name, cmd->help); message (buf); } } } /* * Do nothing. Used for commands in .phonerc that have * no meaning to the client process. */ zip () { ; } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'getdaemon.c'" '(972 characters)' if test -f 'getdaemon.c' then echo shar: will not over-write existing file "'getdaemon.c'" else cat << \!Funky!Stuff! > 'getdaemon.c' #include "../common.h" #include "defs.h" #include <sys/time.h> #include <signal.h> #include <errno.h> /* * Ask the master daemon to give us its first-born child ... * Returns the string that it gave us ... */ char * getdaemon () { int r; /* number of chars read */ char mbuf[80]; if (connected) { error (0, "getdaemon(): already connected!"); return; } connected = 0; if (Debug) message ("Asking for a conversation daemon ..."); mbuf[0] = ESC; mbuf[1] = DAEMON; for ( ;; ) { if ((r = sendit (mbuf, 2, locaddr)) < 0) { error (0, "could not get conversation daemon from local daemon"); return ((char *) 0); } buf[r] = '\0'; if (buf[2] == NAK) { sprintf (mbuf, "Daemon failed to fork: %s", buf+3); putmessage (mbuf); } else break; putmessage ("Trying to connect to conversation daemon."); } if (Debug) message (sprintf (mbuf, "Daemon address is %s", buf+3)); return (strsave (buf+3)); /* skip ESC D y */ } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'kb.c'" '(4489 characters)' if test -f 'kb.c' then echo shar: will not over-write existing file "'kb.c'" else cat << \!Funky!Stuff! > 'kb.c' #include <sys/ioctl.h> #include <curses.h> #undef ctrl() #include "defs.h" /* * Handle keyboard input. * * Need to handle edit character mapping --- see if char is one * of his edit characters, swap to standard character. */ static struct key { int his; /* his key */ int std; /* what we'll map it to */ } keys [] = { { ctrl(M), ctrl(J) }, /* <cr> -> newline */ { 0, ctrl(H) }, /* backspace */ { 0, ctrl(W) }, /* word erase */ { 0, ctrl(X) }, /* line kill */ { 0, 0 } }; keyboard () { static char c; /* character read in */ register int k; /* for doing key maps */ static int command = 0; /* on bottom line? */ static int cpos = 0; /* position in buffer */ static char cbuf[80]; /* and command buffer */ extern int childpid, tochild; static int y, x; if (read (0, &c, 1) != 1) return; /* * We remap the basic editing characters here to make life easier later. */ for (k = 0; k < 4; k++) { if (keys[k].his == c) { c = keys[k].std; break; } } if (c == ctrl(L) || c == ctrl(R)) { wrefresh (curscr); return; } if (c == ESC) { /* toggle command mode */ getyx (stdscr, y, x); /* save the current cursor position */ move (maxy, 0); /* jump to bottom line */ clrtoeol (); command = !command; /* toggle mode */ if (command) /* entering command mode */ addstr (PROMPT); cpos = 0; /* and reset buffer index */ move (y, x); /* restore cursor position */ refresh (); return; } if (command == 0) { /* not in command mode */ if ((c == ctrl(D)) && childpid) /* send eof to child */ close (tochild); if (childpid == 0 || Echo) { /* send char to the socket */ #ifdef LOCAL_ECHO int old = selwin (0); showch (c); selwin (old); refresh (); #endif LOCAL_ECHO (void) write (stream, &c, 1); } if (childpid && !Hold) /* and to child process */ (void) write (tochild, &c, 1); return; } else { /* * The rest is command-mode processing where * we fiddle with the command buffer. This * should move to a separate routine somewhere. */ getyx (stdscr, y, x); /* save the current cursor position */ if (c == ctrl(J)) { /* execute the buffer */ cbuf[cpos] = '\0'; /* null-terminate it */ move (maxy, 0); /* go to beginning of line */ clrtoeol (); /* clear line to end */ refresh (); /* update the screen */ if (cpos) execute (cbuf); /* and execute it !!! */ command = cpos = 0; /* turn off command mode */ move (y, x); /* restore cursor position */ refresh (); return; /* and we're done */ } /* * The message() routine modified the bottom line, * so reprint whatever should be down there. */ if (touched25) { move (maxy, 0); /* go to beginning of line */ addstr (PROMPT); /* print prompt */ cbuf[cpos] = '\0'; addstr (cbuf); /* and print buffer */ clrtoeol (); touched25 = 0; } move (maxy, strlen (PROMPT) + cpos); /* * If we're here, then we want to add the character to * the pending command buffer. */ switch (c) { case ctrl(H): /* backspace */ if (cpos) { cpos--; addstr ("\b \b"); } break; case ctrl(W): /* word erase */ while (--cpos >= 0) { if (cbuf[cpos] != ' ') { cpos++; break; } addstr ("\b \b"); } while (--cpos >= 0) { if (cbuf[cpos] == ' ') { cpos++; break; } addstr ("\b \b"); } break; case ctrl(U): /* line kill */ case ctrl(X): move (maxy, strlen (PROMPT)); clrtoeol (); cpos = 0; break; case ctrl(I): /* tab */ c = ' '; /* fall through ... */ default: cbuf[cpos++] = c; addch (c); break; } cbuf[cpos] = '\0'; move (y, x); /* restore cursor position */ refresh (); return; } } /* * Get our editing characters. Called by setup(). */ geteditchars () { struct sgttyb chars; /* standard char- and line-erase */ #ifdef TIOCGLTC struct ltchars lchars; /* local - word-erase */ #endif if (ioctl (0, TIOCGETP, &chars) < 0) { error (0, "ioctl (TIOCGETP) failed"); return; } keys[1].his = chars.sg_erase; keys[3].his = chars.sg_kill; #ifdef TIOCGLTC if (ioctl (0, TIOCGLTC, &lchars) < 0) { error (0, "ioctl (TIOCGLTC) failed"); return; } keys[2].his = lchars.t_werasc; #endif } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'main.c'" '(4570 characters)' if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else cat << \!Funky!Stuff! > 'main.c' #include "../common.h" #include "defs.h" #include <stdio.h> #include <signal.h> #include <errno.h> #define toggle(var) var = !var struct sockaddr_in locaddr; /* our own host address */ char buf[BUFFER]; /* general purpose character buffer */ char convaddr[25]; /* address of our conversation in ascii */ char host[32]; /* local host name */ char realname[100]; /* user's real name from password file */ extern int users; /* number of users connected - from windows.c */ int pending; /* number of pending calls */ int changed_size; /* caught SIGWINCH, need to resize windows */ main (argc, argv) int argc; char *argv[]; { int answer = 0; int smask; /* saved socket select mask */ extern int errno; extern int timer(); extern int quit(); extern int badsig(); extern int sigint(); extern int sigquit(); extern int sigstop(); extern int sigwinch(); extern int sigchld(); char *check_invite(); char *getdaemon(); char *index(); char *user; /* name of user we are trying to call */ char *hishost; /* and his hostname */ char *addr; /* address of conversation from getdaemon () */ struct sockaddr_in sin; argv++, argc--; while (*argv && **argv == '-') { while (*++*argv) { switch (**argv) { case 'a': if (argc > 1) { /* given an address */ addr = *++argv; argc--; printf ("Using address \"%s\"\n", addr); } break; case 'b': toggle (Bells); break; case 'd': toggle(Debug); break; case 'i': toggle(Inverse); break; } } argv++, argc--; } if (argc == 0) /* just answer incoming calls */ answer = 1; else if (argc > 2) { fprintf (stderr, "Usage: %s [-{bdi}] user@host [ tty ]\n", *argv); exit (1); } if (names ()) /* get all the various info */ exit (1); readrc (); /* read in and act on the .phonerc */ if (!answer) { /* expand any user aliases and see who we're calling */ if ((user = expalias (argv[0])) == (char *) 0) user = argv[0]; if (hishost = index (user, '@')) *hishost++ = '\0'; else hishost = host; } connected = 0; pending = 0; /* set up all the signals */ signal (SIGPIPE, SIG_IGN); if (!Debug) { signal (SIGHUP, badsig); signal (SIGBUS, badsig); signal (SIGSEGV, badsig); } signal (SIGALRM, timer); signal (SIGQUIT, sigquit); signal (SIGINT, sigint); #ifdef SIGWINCH signal (SIGWINCH, sigwinch); #endif signal (SIGCHLD, sigchld); /* * open datagram control socket. */ if ((ctl = socket (AF_INET, SOCK_DGRAM, 0)) < 0) error (1, "Can't get control socket"); sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = 0; sin.sin_family = AF_INET; if (bind (ctl, &sin, sizeof (sin)) < 0) error (1, "Can't bind control socket"); setup (); /* initialize all the screen stuff */ /* Have we been invited ??? */ if (addr || (addr = check_invite (answer ? "*" : user, hishost))) { if (Debug) printf ("\r\nCheck_invite returned \"%s\"\r\n", addr); connect_daemon (addr); if (!connected) { if (Debug) printf ("Connection failed.\r\n"); goto again; /* caller has hung up !!! */ } } else { again: if (Debug) printf ("\r\nCheck_invite returned no address.\r\n"); if (answer) { /* don't want to place a call */ fprintf (stderr, "\r\nNo pending calls for %s\r\n", login); cleanup (); exit (1); } do { putmessage (""); if ((addr = getdaemon()) == (char *) 0) error (1, "Cannot get conversation daemon address"); if (Debug) printf ("\rGet_daemon returned address of \"%s\"\r\n", addr); connect_daemon (addr); if (!connected) { putmessage ("Trying to connect to conversation daemon."); printf ("Trying to connect to conversation daemon."); fflush (stdout); } } while (!connected); placecall (user, hishost, argv[1] ? argv[1] : ""); } smask = (1 << 0) | (1 << ctl) | (1 << stream); alarm (1); /* get things rolling */ /* * Main loop - select on sockets and handle appropriately. */ for ( ;; ) { static int mask; extern int childpid, fromchild; #ifdef SIGWINCH if (changed_size) { /* caught SIGWINCH - have to resize windows */ stretch (1); changed_size = 0; } #endif mask = smask; if (childpid) mask |= 1 << fromchild; if (select (32, &mask, 0, 0, 0) <= 0) { if (errno == EINTR) continue; error (1, "select"); } errno = 0; if (mask & (1 << 0)) keyboard (); if (mask & (1 << stream)) readstream (); if (mask & (1 << ctl)) readctl (); if (childpid && (mask & (1 << fromchild))) readchild (); } /*NOTREACHED*/ } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'message.c'" '(2503 characters)' if test -f 'message.c' then echo shar: will not over-write existing file "'message.c'" else cat << \!Funky!Stuff! > 'message.c' #include "defs.h" #include <stdio.h> #include <signal.h> /* * Routines for dealing with the messages printed * down on the bottom line of the screen. */ struct mesg { char *text; /* text of this message */ struct mesg *next; /* link to next message */ }; static struct mesg *mesghead = (struct mesg *) 0; /* top of queue */ static struct mesg *last = (struct mesg *) 0; /* bottom of queue */ static struct mesg *freelist = (struct mesg *) 0; /* stuff to free up */ int msgpending = 0; /* number of pending messages */ /* * Add a message to the pending message queue. */ message (str) char *str; { struct mesg *new; int sigs; sigs = sigblock (1 << SIGALRM); if (!freelist) { /* need to malloc some up */ new = (struct mesg *) malloc (sizeof (struct mesg)); if (!new) error (1, "cannot malloc message buffer!"); } else { /* take off the top of the list */ new = freelist; freelist = freelist->next; free (new->text); } new->text = strsave (str); new->next = (struct mesg *) 0; if (last) /* add to end of currently pending list */ last->next = new; if (!mesghead) /* empty queue - add to top */ mesghead = new; last = new; /* update bottom pointer */ if (msgpending <= 0) { /* need to restart alarms */ alarm (1); msgpending = 1; } else msgpending++; (void) sigsetmask (sigs); } /* * Called by the alarm interrupt routine - print the * next pending message and remove it from the queue. */ showmessage () { static int ticks = 0; struct mesg *m; if ((ticks = (ticks + 1) % Interval) != 0) return; if (msgpending == 0) { /* just clean up after last message */ putmessage (""); msgpending = -1; return; } m = mesghead; /* save pointer to this message */ putmessage (m->text); mesghead = m->next; /* move down top of queue */ if (last == m) /* was the last item, too */ last = (struct mesg *) 0; m->next = freelist; /* add this item to the free list */ freelist = m; /* this way we don't free in an interrupt !!! */ msgpending--; /* and decrement counter */ } /* * Flush the pending message buffer. Not actually used yet. */ flush () { last->next = freelist; /* add curr freelist to end of list */ last = (struct mesg *) 0; freelist = mesghead; /* move pending list to free list */ mesghead = (struct mesg *) 0; /* zap the head */ msgpending = 0; /* and reset the counter */ } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'misc.c'" '(2640 characters)' if test -f 'misc.c' then echo shar: will not over-write existing file "'misc.c'" else cat << \!Funky!Stuff! > 'misc.c' #include "defs.h" #include <curses.h> #undef echo() #undef noecho() /* * See if char `c' is contained in string `s'. Returns 1 if true, 0 if not. */ any (c, s) char c; char *s; { while (*s) if (c == *s++) return (1); return (0); } /* * Send an error message to the user. Much like perror(). * If fatal is non-zero, we will terminate with that status. */ error (fatal, mesg) int fatal; char *mesg; { extern int errno; extern char *sys_errlist[]; extern int sys_nerr; char buf[80]; if (errno) { if (errno < sys_nerr) sprintf (buf, "%s: %s", mesg, sys_errlist[errno]); else sprintf (buf, "%s: Error %d", mesg, errno); } else strcpy (buf, mesg); if (connected) /* and have set up curses */ putmessage (buf); else fprintf (stderr, "%s\r\n", buf); if (fatal) { cleanup (); printf ("\n\n"); exit (fatal); } } /* * User interface to the screen dump routine. */ dump (argc, argv) int argc; char *argv[]; { dodump (argc ? *argv : ".dump"); } /* * Do a screen dump to the named file. */ dodump (name) char *name; { FILE *fp; int y, x; char buf[80]; extern int connected; if (!connected) /* take it as a normal interrupt */ quit (); if ((fp = fopen (name, "w")) == (FILE *) 0) { (void) sprintf (buf, "Cannot create \"%s\" file", name); error (0, buf); return; } for (y = 0; y <= maxy; y++) { for (x = 0; x < maxx; x++) (void) putc (stdscr->_y[y][x] & 0177, fp); (void) putc ('\n', fp); } (void) fclose (fp); (void) sprintf (buf, "Screen dump to \"%s\" complete.", name); putmessage (buf); } /* * Move to the bottom line and exit. */ quit () { extern int did_screen; extern int childpid; if (did_screen) cleanup (); if (connected) printf ("\nConnection closed. Exiting ..."); printf ("\n"); if (childpid) /* clean up rather forcefully */ kill (childpid, 9); exit (0); } /* * Catch SIGWINCH and come here - just set a flag and return. */ sigwinch () { extern int changed_size; changed_size = 1; } /* * Chdir to the named directory, to $HOME if no args. */ cd (argc, argv) int argc; char *argv[]; { char *getenv(); char *dir; if (argc == 0) { /* try to go home */ if ((dir = getenv ("HOME")) == (char *) 0) { putmessage ("No HOME set!"); return; } } else dir = argv[0]; if (chdir (dir)) { sprintf (buf, "Cannot chdir to \"%s\"", dir); error (0, buf); } } /* * Given a full pathname, return a pointer to the last part of the path. */ char * basename (path) char *path; { char *rindex(); char *p; if (p = rindex (path, '/')) return (p+1); return (path); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'names.c'" '(2359 characters)' if test -f 'names.c' then echo shar: will not over-write existing file "'names.c'" else cat << \!Funky!Stuff! > 'names.c' #include "../common.h" #include "defs.h" #include <netdb.h> #include <stdio.h> #include <pwd.h> #undef NULL #define NULL ((char *) 0) /* * Initialize all the various names and such that we all want to know. */ names () { struct passwd *pw, *getpwnam(); struct hostent *hp, *gethostbyname(); #ifdef SERVICES struct servent *sp, *getservbyname(); #endif extern char *getlogin(); extern char *ttyname(); extern char *getenv(); extern char realname[]; char *r, *p, *n; if ((pw = getpwuid (getuid ())) == (struct passwd *) 0) { fprintf (stderr, "Who are you?\n"); return (1); } if ((n = getlogin ()) == NULL) /* in a window? */ login = strsave (pw->pw_name); /* use our acct name */ else login = strsave (n); if ((home = getenv ("HOME")) == NULL) home = pw->pw_dir; if ((shell = getenv ("SHELL")) == NULL) shell = "/bin/sh"; if (p = getenv ("NAME")) strcpy (realname, p); else { for (p=pw->pw_gecos, r=realname; p && *p && *p!=',' && *p!=';'; p++) { if (*p == '&') { /* copy in from login name */ n = pw->pw_name; /* and grab the login name */ if ('a' <= *n && *n <= 'z') /* capitalize the first character */ *n += 'A' - 'a'; while (*n) *r++ = *n++; } else *r++ = *p; } *r = '\0'; } if ((tty = ttyname (0)) == NULL || !isatty (1)) { fprintf (stderr, "Input must be a terminal, not a file or pipe.\n"); return (1); } tty = strsave (tty); if (strncmp (tty, "/dev/", 5) == 0) tty += 5; /* skip over "/dev/" */ /* find out about our host */ gethostname (host, 32); if ((hp = gethostbyname ("localhost")) == (struct hostent *) 0) if ((hp = gethostbyname (host)) == (struct hostent *) 0) { fprintf (stderr, "Cannot find network entry for %s\n", host); return (1); } bzero ((char *)&locaddr, sizeof (locaddr)); bcopy ((char *)hp->h_addr, (char *)&locaddr.sin_addr, hp->h_length); locaddr.sin_family = hp->h_addrtype; #ifdef SERVICES if ((sp = getservbyname ("phone", "udp")) == (struct servent *) 0) { fprintf (stderr, "This machine doesn't support \"phone\"\r\n"); return (1); } locaddr.sin_port = port = sp->s_port; #else SERVICES #ifdef PORT locaddr.sin_port = port = htons (PORT); #else /* PORT */ fprintf (stderr, "Your site administrator screwed up installing this program!!!\r\n"); return (1); #endif PORT #endif SERVICES return (0); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'parse.c'" '(1159 characters)' if test -f 'parse.c' then echo shar: will not over-write existing file "'parse.c'" else cat << \!Funky!Stuff! > 'parse.c' /* * Take a line buffer and turn into an array of words, * doing the right thing with quotes. */ parse (line, argv) char *line; char **argv; { char *lp; char *wp; char **ap; char word[128]; char quotec = '\0'; char *exptilde (); ap = argv; lp = line; while (*lp) { while (any (*lp, " \t")) /* skip over leading space */ lp++; quotec = '\0'; wp = word; while (*lp) { /* get one word */ if (*lp == '!') { /* hack to allow !cmd */ *wp++ = *lp++; break; } if (*lp == '\\') { /* literal next character */ *wp++ = *++lp; lp++; continue; } if (any (*lp, " \t") && !quotec) /* wsp not in a quoted string */ break; if (any (*lp, "\"'")) { /* quote characters */ if (!quotec) { /* this must be open quote */ quotec = *lp++; continue; } /* else */ if (*lp == quotec) { /* matches opening -> close quote */ quotec = '\0'; lp++; continue; } } *wp++ = *lp++; } *wp = '\0'; if (wp > word) *ap++ = exptilde (word); } *ap = (char *) 0; if (quotec) putmessage ("Unmatched quote."); return (ap - argv); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'readctl.c'" '(1238 characters)' if test -f 'readctl.c' then echo shar: will not over-write existing file "'readctl.c'" else cat << \!Funky!Stuff! > 'readctl.c' #include "defs.h" #include "../common.h" /* * Read a message on the control socket and act upon it. */ readctl () { int ack; /* = 1 if this is a good message */ int r; /* number of chars read */ char buf[512]; /* input buffer */ int len; /* length of return address */ struct sockaddr_in from; len = sizeof (from); if ((r = recvfrom (ctl, buf, 512, 0, &from, &len)) == 0) { error (0, "read: control socket"); return; } if (*buf != ESC) { /* bad control packet */ error (0, "malformed control packet"); return; } buf[r] = '\0'; ack = buf[2] == ACK; switch (buf[1]) { /* command character */ case CALLING: if (!ack) /* can't call a user */ delete (buf+3); putmessage (buf+8); break; case PAGE: if (!ack) { /* couldn't place call */ pending--; putmessage (buf+3); } break; case INQUIRE: break; /* don't do anything */ case DAEMON: break; /* and again */ case MESSAGE: message (buf+3); /* show message */ break; case ANSWER: delete (buf+3); /* someone answered a call */ if (Debug) { char mbuf[80]; sprintf (mbuf, "Answer on id >>%s<<", buf+3); putmessage (mbuf); } break; } } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'readrc.c'" '(519 characters)' if test -f 'readrc.c' then echo shar: will not over-write existing file "'readrc.c'" else cat << \!Funky!Stuff! > 'readrc.c' #include "defs.h" #include <stdio.h> #ifndef PHONERC #define PHONERC "/.phonerc" #endif /* * Open the .phonerc and do the stuff in it. */ readrc () { char line[BUFSIZ]; char *i, *index(); FILE *rc; strcpy (buf, home); strcat (buf, PHONERC); if ((rc = fopen (buf, "r")) == (FILE *) 0) return; while (fgets (line, BUFSIZ, rc)) { if (Debug) printf ("Rc: %s", line); if (*line == '#' || *line == '\n') continue; if (i = index (line, '\n')) *i = '\0'; execute (line); } fclose (rc); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'readstream.c'" '(2629 characters)' if test -f 'readstream.c' then echo shar: will not over-write existing file "'readstream.c'" else cat << \!Funky!Stuff! > 'readstream.c' #include "../common.h" #include "defs.h" #include <curses.h> /* * Read and process some characters from the stream socket. * * Commands are passed in one byte by setting the high bit * with info encoded in the following manner: * * +---+-------+-------------------+ * Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * +---+-------+-------------------+ * | 1 | cmd | window number | * +---+-------+-------------------+ * * For the "set update" command, only the low bit of the window * number is used, to indicate whether updating should be on or off. */ #define WINMASK ((1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0)) /*037*/ /* lower 5 bits (0-4) set */ #define CMDMASK ((1<<6)|(1<<5)) /* only bits 5 and 6 set */ readstream () { static int adding = 0; /* reading an "adduser" string? */ static char combuf[512]; /* command mode buffer */ char buf[512]; /* normal input buffer */ register char *bufptr; /* pointer into normal buffer */ static char *comptr; /* pointer into command mode buffer */ register int i, r; register int c; static int addslot = 0; /* slot for adduser() */ static int dorefresh = 0; static int noupdate = 0; /* don't refresh screen until told to */ if ((r = read (stream, buf, 512)) == 0) { /* daemon hung up */ quit (); connected = 0; return; } bufptr = buf; for (i = 0; i < r; i++) { /* go through each character */ c = *bufptr++; if (c & META) { /* a command byte */ switch (c & CMDMASK) { case ADDUSER: /* add a new user */ comptr = combuf; adding = 1; addslot = c & WINMASK; break; case DELUSER: /* delete an existing user */ deluser (c & WINMASK); break; case UPDATE: /* set screen updating */ if (c & 01) /* turn updating on */ noupdate = 0; else /* turn it off */ noupdate = 1; break; default: if (adding) { /* end of adduser cmd */ *comptr = '\0'; adduser (addslot, combuf); adding = 0; dorefresh = 1; } else /* select current window */ selwin (c & WINMASK); break; } } else { /* normal character */ if (adding) /* add to adduser buffer */ *comptr++ = c; else { /* add to display */ #ifdef LOCAL_ECHO extern int current; /* defined in windows.c */ if (current != 0 || !Echo){ /* only show if it's not ours */ #endif showch (c); dorefresh = 1; #ifdef LOCAL_ECHO } #endif } } } if (dorefresh && noupdate == 0) { refresh (); dorefresh = 0; } return; } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'runprog.c'" '(3260 characters)' if test -f 'runprog.c' then echo shar: will not over-write existing file "'runprog.c'" else cat << \!Funky!Stuff! > 'runprog.c' #include "../common.h" #include "defs.h" #include <sys/types.h> #include <sys/wait.h> #include <sys/time.h> /* these two are for wait3() */ #include <sys/resource.h> #include <fcntl.h> #include <errno.h> #include <curses.h> /* * Routines to handle subprocesses inside windows. * * Future plans include putting children on ptys, * allowing multiple processes, some kind of simple * process control, setting window size (ioctl) and * termcap to properly reflect the window size, and * being able to run screen-oriented programs within * the phone window. Maybe sometime ... */ int childpid = 0; /* current child process's pid */ int tochild; /* fd to write to child */ int fromchild; /* fd to read from child */ int killedchild; /* flag set on intr - flush output */ /* * Run a program. We use the user's shell to allow piping and wildcards. */ run (argc, argv) int argc; char *argv[]; { char args[512]; int in[2], out[2]; int i; if (childpid) { putmessage ("You are already running a program!!!"); return; } /* concatenate all the arguments to pass to a shell */ for (*args = '\0', i = 0; i < argc; ) { strcat (args, argv[i]); if (++i < argc) strcat (args, " "); } if (pipe (in) < 0 || pipe (out) < 0) { error (0, "Cannot pipe to child process"); return; } killedchild = 0; switch (childpid = fork ()) { case -1: error (0, "fork"); break; case 0: (void) dup2 (in[0], 0); /* child's stdin */ (void) dup2 (out[1], 1); /* stdout */ (void) dup2 (out[1], 2); /* stderr */ (void) close (in[0]); (void) close (out[1]); execl (shell, basename (shell), "-c", args, (char *) 0); perror (argv[0]); exit (1); default: (void) close (in[0]); (void) close (out[1]); (void) fcntl (in[1], F_SETFL, FNDELAY | FASYNC); (void) fcntl (out[0], F_SETFL, FNDELAY | FASYNC); tochild = in[1]; /* write to child here */ fromchild = out[0]; /* read from child here */ return; } } /* * Read some output from the process. We make sure that there * aren't any magic characters there to hang anyone, and do * local echoing if need be. */ readchild () { int r, i; char buf[1024]; register char *c; extern int errno, stream; if ((r = read (fromchild, buf, 1024)) == -1) { if (errno != EWOULDBLOCK) perror ("read from child"); return; } else if (r == 0) { /* child is finished */ if (Debug) putmessage ("Saw EOF on child process."); childpid = 0; (void) close (tochild); (void) close (fromchild); } else { /* send to everyone */ if (!killedchild) { #ifdef LOCAL_ECHO int oldwin = selwin (0); #endif LOCAL_ECHO for (c = buf, i = 0; i < r; c++, i++) { if (*c & META) *c &= 0177; #ifdef LOCAL_ECHO showch (*c); #endif LOCAL_ECHO } (void) write (stream, buf, r); #ifdef LOCAL_ECHO refresh (); (void) selwin (oldwin); #endif LOCAL_ECHO } } } /* * SIGCHLD handler. * We don't reset the pid and fd states because we'll get those when * we see the eof on the kid's descriptors. */ sigchld () { union wait status; if (childpid == 0) return; while (wait3 (&status, WNOHANG, (struct rusage *) 0) > 0) ; if (Debug) putmessage ("Caught sigchld."); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'sendit.c'" '(1506 characters)' if test -f 'sendit.c' then echo shar: will not over-write existing file "'sendit.c'" else cat << \!Funky!Stuff! > 'sendit.c' #include "defs.h" #include <sys/time.h> #define WAIT 2 /* wait this long before re-sending */ /* * Send the given buffer to the given address, with * multiple retries. Stores the received string * in the global `buf', and returns the number of chars * read, or -1 on error or no response. */ sendit (str, len, sin) char *str; int len; struct sockaddr_in sin; { struct timeval timeout; int rval; /* number of bytes read */ int mask; /* used for select */ int tries; /* number of tries made */ #ifdef DEBUG char mbuf[100]; /* message buffer */ #endif timeout.tv_sec = WAIT; timeout.tv_usec = 0; for (tries = 0; tries < 5; tries++) { if (sendto (ctl, str, len, 0, &sin, sizeof (sin)) < 0) { error (0, "sendit: sendto"); return (-1); } if (Debug) printf ("\r\nsendit: trying #%d.\r\n", tries); #ifdef DEBUG sprintf (mbuf, "sendit: trying #%d ...", tries); putmessage (mbuf); #endif DEBUG mask = 1 << ctl; if (select (32, &mask, 0, 0, &timeout) <= 0) { /* nothing to read on */ if (Debug) printf ("\r\nsendit: no response on try #%d.\r\n", tries); continue; } rval = read (ctl, buf, BUFFER); /* read the response */ if (rval < 0) { error (0, "sendit: read response"); continue; } if (Debug) printf ("sendit: read %d chars.\r\n", rval); return (rval); } if (Debug) printf ("\r\nsendit: response timed out.\r\n"); #ifdef DEBUG putmessage ("sendit: response timed out."); #endif DEBUG return (-1); /* timed out */ } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'set.c'" '(3287 characters)' if test -f 'set.c' then echo shar: will not over-write existing file "'set.c'" else cat << \!Funky!Stuff! > 'set.c' #include "defs.h" /* * Routines to manage the various user-modifiable variables * that influence the program's behaviour. */ int Interval = 2, /* number of seconds between messages */ Inverse = 1, /* use inverse video in header lines */ Stop = 1, /* print out the "[Stopped]" mesg on ^Z */ Debug = 0, /* print out gobs of debuuging info */ Bells = 0, /* allow bells to actually beep */ Echo = 1, /* echo kb input while running program */ Hold = 0; /* hold kb input from child program */ #define VALUE (1<<1) /* variable must have a _value_ */ #define BOOLEAN (1<<2) /* is just a toggle */ static struct variable { char *name; /* name of variable as user sees it */ int *value; /* currently - stored value */ int type; /* boolean or integer-type variable */ } vars[] = { { "interval", &Interval, VALUE }, { "inverse", &Inverse, BOOLEAN }, { "stop", &Stop, BOOLEAN }, { "debug", &Debug, BOOLEAN }, { "bells", &Bells, BOOLEAN }, { "echo", &Echo, BOOLEAN }, { "hold", &Hold, BOOLEAN }, { 0, 0, 0 }, }; /* * Assign a value to a given variable ... */ set (argc, argv) int argc; char **argv; { struct variable *vp; char *name; int vlen; int toggle = 0; /* see if it's a toggle */ if (strncmp ("no", *argv, 2) == 0) { toggle = 1; *argv += 2; } /* search for variable */ vlen = strlen (*argv); for (vp = vars; vp && (name = vp->name); vp++) if (strncmp (*argv, name, vlen) == 0) break; if (!vp || !name) { /* not found */ sprintf (buf, "%s: No such variable - %s", *argv, "use 'show' to list all variable names"); putmessage (buf); return; } if (argc == 2) { if (vp->type == BOOLEAN) { sprintf (buf, "%s is a boolean toggle - use 'set %s' or 'set no%s'.", name, name, name); putmessage (buf); } else { if (!isdigit (argv[1][0]) || toggle) { sprintf (buf, "%s requires a numeric argument", name); putmessage (buf); } else *vp->value = atoi (argv[1]); } } else { /* argc == 1 */ if (vp->type != BOOLEAN) { sprintf (buf, "%s requires a numeric argument", name); putmessage (buf); } else { if (toggle) /* name was preceeded by 'no' */ *vp->value = 0; else *vp->value = 1; } } } /* * Show the value of a variable. */ show (argc, argv) int argc; char *argv[]; { struct variable *vp; char *name; int vlen; if (argc == 0) { /* list all variables known */ message ("Listing all variable names:"); for (vp = vars; vp && vp->name; vp++) showvar (vp); return; } while (*argv) { /* search for variable */ vlen = strlen (*argv); for (vp = vars; vp && (name = vp->name); vp++) if (strncmp (*argv, name, vlen) == 0) break; if (!vp || !name) { /* not found */ sprintf (buf, "%s: No such variable - %s", *argv, "use 'show' to list all variable names"); putmessage (buf); return; } /* print out the variable */ showvar (vp); argv++; } } /* * Print out one variable nicely... */ showvar (vp) struct variable *vp; { if (vp->type == BOOLEAN) (void) sprintf (buf, "%s is %s", vp->name, *vp->value ? "on" : "off"); else (void) sprintf (buf, "%s = %d", vp->name, *vp->value); message (buf); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'sig.c'" '(2331 characters)' if test -f 'sig.c' then echo shar: will not over-write existing file "'sig.c'" else cat << \!Funky!Stuff! > 'sig.c' #include <signal.h> #include <sys/ioctl.h> /* * Interrupt handler - if we have a child, kill it off, else exit. */ sigint () { extern int tochild, fromchild; extern int childpid; extern int killedchild; extern int connected; extern int did_screen; unsigned char c; if (childpid > 0) { kill (childpid, SIGHUP); /* it's sure to die sooner or later!!! */ kill (childpid, SIGINT); kill (childpid, SIGKILL); killedchild = 1; putmessage ("Child has been killed!!!!"); } else { if (connected && did_screen) { putmessage ("Really quit? "); read (0, &c, 1); c &= 0177; if (c == 'y' || c == 'Y') quit (); putmessage (" "); } else { quit (); } } } /* * Sigquit handler - save a copy of the screen into a ".dump" file. */ sigquit () { dodump (".dump"); } #ifdef vax /* * Generic bad signal handler. Very non-portable, * at least according to the documentation. */ badsig (sig) int sig; { char *s; cleanup (); switch (sig) { case SIGHUP: s = "SIGHUP"; break; case SIGINT: s = "SIGINT"; break; case SIGQUIT: s = "SIGQUIT"; break; case SIGILL: s = "SIGILL"; break; case SIGTRAP: s = "SIGTRAP"; break; case SIGIOT: s = "SIGIOT"; break; case SIGEMT: s = "SIGEMT"; break; case SIGFPE: s = "SIGFPE"; break; case SIGKILL: s = "SIGKILL"; break; case SIGBUS: s = "SIGBUS"; break; case SIGSEGV: s = "SIGSEGV"; break; case SIGSYS: s = "SIGSYS"; break; case SIGPIPE: s = "SIGPIPE"; break; case SIGALRM: s = "SIGALRM"; break; case SIGTERM: s = "SIGTERM"; break; case SIGURG: s = "SIGURG"; break; case SIGSTOP: s = "SIGSTOP"; break; case SIGTSTP: s = "SIGTSTP"; break; case SIGCONT: s = "SIGCONT"; break; case SIGCHLD: s = "SIGCHLD"; break; case SIGTTIN: s = "SIGTTIN"; break; case SIGTTOU: s = "SIGTTOU"; break; case SIGIO: s = "SIGIO"; break; case SIGXCPU: s = "SIGXCPU"; break; case SIGXFSZ: s = "SIGXFSZ"; break; case SIGVTALRM: s = "SIGVTALRM"; break; case SIGPROF: s = "SIGPROF"; break; case SIGWINCH: s = "SIGWINCH"; break; case SIGUSR1: s = "SIGUSR1"; break; case SIGUSR2: s = "SIGUSR2"; break; } printf ("\r\n\r\nCaught %s signal!!!! Exiting...\r\n\r\n", s); exit (1); } #else badsig () { cleanup (); printf ("\r\n\r\nCaught an unknown bad signal! Exiting ...\r\n\r\n"); exit (1); } #endif vax !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'stop.c'" '(501 characters)' if test -f 'stop.c' then echo shar: will not over-write existing file "'stop.c'" else cat << \!Funky!Stuff! > 'stop.c' #include <signal.h> #include <curses.h> #include "defs.h" /* * Routine to handle SIGTSTP and the like. */ sigstop () { extern int stream; extern int maxy; int (*func)(); func = signal (SIGTSTP, SIG_DFL); /* save old SIGTSTP routine */ if (Stop) (void) write (stream, "\n[ Stopped ]", 12); tstp (); /* curses routine to stop and restart */ if (Stop) (void) write (stream, "\r\f", 2); /* clear this line */ signal (SIGTSTP, func); /* and restore SIGTSTP routine */ } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'strsave.c'" '(324 characters)' if test -f 'strsave.c' then echo shar: will not over-write existing file "'strsave.c'" else cat << \!Funky!Stuff! > 'strsave.c' #include <signal.h> #include <stdio.h> /* * Allocate enough space for the given string and copy it over. */ char * strsave (s) char *s; { register char *new; char *malloc(); if (new = malloc ((unsigned) (strlen (s) + 1))) strcpy (new, s); else error (1, "strsave: cannot malloc memory"); return (new); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'tilde.c'" '(866 characters)' if test -f 'tilde.c' then echo shar: will not over-write existing file "'tilde.c'" else cat << \!Funky!Stuff! > 'tilde.c' #include <pwd.h> /* * Expand a name containing a tilde to the full pathname. */ char * exptilde (str) char *str; { char buf[512]; char name[20]; char *i, *index(); char *getenv(), *strsave(); struct passwd *pw, *getpwnam(); if (*str != '~') /* doesn't contain a tilde */ return (strsave (str)); if (i = index (str, '/')) /* find slash, if any */ *i = '\0'; strcpy (name, str+1); /* copy name after tilde */ if (*name == '\0') /* was "~/" */ strcpy (buf, getenv ("HOME")); /* so use our home */ else { if ((pw = getpwnam (name)) == (struct passwd *) 0) { sprintf (buf, "Unknown user: %s", name); putmessage (buf); return ((char *) 0); } strcpy (buf, pw->pw_dir); /* fill in that user's home */ } if (i) { *i = '/'; /* and put back trailing pathname */ strcat (buf, i); } return (strsave (buf)); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'timer.c'" '(373 characters)' if test -f 'timer.c' then echo shar: will not over-write existing file "'timer.c'" else cat << \!Funky!Stuff! > 'timer.c' /* * Timer routine. * When we come here, see if we have any pending calls or messages, * call the appropriate routines, then reset the timer. */ timer () { extern int pending, msgpending; if (pending) /* have pending calls */ reinvite (); if (msgpending >= 0) showmessage (); if (pending || msgpending >= 0) /* set the next alarm */ alarm (1); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'who.c'" '(2104 characters)' if test -f 'who.c' then echo shar: will not over-write existing file "'who.c'" else cat << \!Funky!Stuff! > 'who.c' #include "../common.h" #include "defs.h" #include <netdb.h> #include <utmp.h> #include <sys/file.h> /* * Print out who is on the machine. * If an argument is specified, only show occurrences of that user. */ who (argc, argv) int argc; char *argv[]; { extern int maxx; /* max display line length */ int found = 0; /* found who we're looking for */ int utfd; /* /etc/utmp file descriptor */ int len = 256; /* length of current line */ int ulen; /* length of buf this user */ struct utmp utmp; /* one entry in /etc/utmp */ char line[256]; /* line buffer */ char buf[20]; /* and one entry buffer */ char *host; struct hostent *hp, *gethostbyname(); struct sockaddr_in sin; if (argc == 1 && **argv == '@') { host = &argv[0][1]; if (isdigit (*host)) sin.sin_addr.s_addr = inet_addr (host); else if (hp = gethostbyname (host)) bcopy ((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length); else { sprintf (buf, "who: unknown host: %s", host); message (buf); } sin.sin_port = port; sin.sin_family = AF_INET; sprintf (buf, "%c%c", ESC, WHO); if (sendto (ctl, buf, 2, 0, &sin, sizeof (sin)) < 0) error (0, "who: sendto"); return; } if ((utfd = open ("/etc/utmp", O_RDONLY)) < 0) { message ("Can't open /etc/utmp!!!"); return; } line[0] = '\0'; while (read (utfd, (char *)&utmp, sizeof (struct utmp))) { if (utmp.ut_name[0] == '\0') continue; if (argc && strncmp (argv[0], utmp.ut_name, 8)) continue; found++; sprintf (buf, " %.8s(%.5s)", utmp.ut_name, utmp.ut_line); ulen = strlen (buf); if (len + ulen + 2 < maxx) { /* just add to current message */ strcat (line, buf); } else { /* buf is too long - print line */ message (line); len = Inverse ? 2 : 0; /* kludge for inverse glitch */ sprintf (line, "who:%s", buf); } len += ulen; /* add to total line length */ } if (len) message (line); close (utfd); if (argc && !found) message ("<not logged on>"); } !Funky!Stuff! fi # end of overwriting check echo shar: extracting "'windows.c'" '(12693 characters)' if test -f 'windows.c' then echo shar: will not over-write existing file "'windows.c'" else cat << \!Funky!Stuff! > 'windows.c' /* * Routines to manage the windows. SOme of this is pretty ugly * stuff, especially as most of the time is spent here. * * We manage to resize windows by buffering all parts of a conversation, * so we just (re)print the buffered part. The buffering also accounts * for most of the lack of speed. A future enhancement will probably * be to replace the constant buffering with a smarter routine that * will just grab each window from the curses screen buffer when we call * stretch() ... */ #include <curses.h> #include <sys/ioctl.h> #include <signal.h> #undef ctrl() /* defined in curses.h */ #include "defs.h" #ifndef cbreak() /* these are defined in *some* versions of curses.h */ #define cbreak() (_tty.sg_flags |= CBREAK, _rawmode = TRUE, stty(_tty_ch,&_tty)) #define nocbreak() (_tty.sg_flags &= ~CBREAK,_rawmode=FALSE,stty(_tty_ch,&_tty)) #endif !cbreak() #ifndef WINDOWS #define WINDOWS 32 /* we can only fit 23 on a sun !!! */ #endif struct win { int inuse; /* 1 if in use, 0 if not */ char *login; /* login name of this person */ char *host; /* their host name */ char *tty; /* tty */ char *realname; /* finger name, if any */ int upper; /* top line of this window */ int lower; /* bottom line of this window */ int y; /* y coord rel to top of wind */ int x; /* last known x coordinate */ int old; /* index of oldest ch in buf */ int new; /* where next char goes */ char *buf; /* text buffer */ }; static struct win windows[WINDOWS]; static struct win *currwin; /* pointer to current window */ int current = 0; /* currently active window */ int users; /* number of users/windows */ int upper_y; /* boundaries of current window */ int lower_y; int stretching = 0; int did_screen = 0; /* have set up curses */ static int y , x; /* current screen coordinates */ extern int sigstop(); /* ^Z handling routine */ static int ourwin; /* the window # that is _ours_ */ /* * Mark all of the windows as not being in use. */ setup () { int i; for (i = 0; i < WINDOWS; i++) { windows[i].inuse = 0; windows[i].old = 0; windows[i].new = 0; } initscr(); did_screen = 1; clear (); cbreak (); noecho (); nonl (); refresh (); signal (SIGTSTP, sigstop); geteditchars (); /* get and save his editing characters */ maxx = stdscr->_maxx - 1; /* save max screen coordinates */ maxy = stdscr->_maxy - 1; windows[0].upper = upper_y = 0; /* initial screen bounds */ windows[0].lower = lower_y = maxy - 1; windows[0].y = y = 1; /* initial window position */ windows[0].x = x = 0; currwin = &windows[0]; move (1, 0); users = 0; /* not talking to anyone yet */ } /* * Restore everything to how it was before we came... */ cleanup () { move (maxy, 0); refresh (); nocbreak (); echo (); endwin (); did_screen = 0; } /* * Select the new current window to use, set the window bounds, * and set the new cursor position ... Returns the old window number. */ selwin (win) char win; { int old = current; if (win == current && !stretching) /* no change */ return (win); if (win == ourwin) /* map our window to window 0 */ win = 0; if (windows[win].inuse == 0) return (old); if (win >= 0 && win < WINDOWS) { currwin->y = y; /* save last coordinates */ currwin->x = x; currwin = &windows[win]; /* set pointer to new window */ y = currwin->y; /* and retrieve from new win */ x = currwin->x; upper_y = currwin->upper; /* get bounds */ lower_y = currwin->lower; move (y + upper_y, x); /* restore cursor pos */ current = win; } return (old); } /* * Add a new window to the screen. * Buf looks like "user:host:tty:realname" */ adduser (slot, buf) int slot; char *buf; { char *i; /* * We rely on the fact that the first window given to us is * our own, so we remember which one that is and remap it * to the top window. This makes people happier. */ if (users == 0) { ourwin = slot; slot = 0; } if (windows[slot].inuse) /* that window is already in use */ return; windows[slot].inuse = 1; /* mark as being in use */ windows[slot].x = 0; /* and current coordinates */ windows[slot].y = 1; windows[slot].buf = malloc (BUFFER); /* and grab a buffer for it */ windows[slot].old = 0; /* nothing in the buffer yet */ windows[slot].new = 0; /* get the info out of the buffer line */ if (i = index (buf, ':')) /* name */ *i++ = '\0'; else return; windows[slot].login = strsave (buf); buf = i; i = index (buf, ':'); /* host */ *i++ = '\0'; windows[slot].host = strsave (buf); buf = i; i = index (buf, ':'); /* tty */ *i++ = '\0'; windows[slot].tty = strsave (buf); buf = i; windows[slot].realname = strsave (buf); /* real name */ if (users++ == 1) /* add another user to the count */ (void) write (1, "\07\07", 2); /* initial connection - beep the user */ stretch (0); /* and recompute all the windows */ } /* * Delete a user and his window. */ deluser (win) char win; { if (win < 0 || win >= WINDOWS) return; if (windows[win].inuse == 0) return; windows[win].inuse = 0; /* this window no longer in use */ free (windows[win].login); /* so free up any space allocated for it */ free (windows[win].host); free (windows[win].tty); free (windows[win].realname); free (windows[win].buf); if (--users > 1) { /* decrement the count */ stretch (0); /* and redraw */ refresh (); } else /* if only one user left .. */ quit (); /* then we're done */ } /* * Show the given character, performing word and line erase. */ showch (ch) char ch; { if (users == 0) return; if (!stretching) { /* save char into buffer */ if (currwin->new >= BUFFER) error (1, "New >= BUFFER in showch()"); currwin->buf[currwin->new++] = ch; if (currwin->new >= BUFFER) /* wrap around */ currwin->new = 0; if (currwin->new == currwin->old) /* tail caught up */ currwin->old++; if (currwin->old >= BUFFER) /* wrap old around */ currwin->old = 0; } switch (ch) { case '\0177': /* delete */ case ctrl(H): /* backspace */ if (x > 0) { addstr ("\b \b"); x--; } break; case ctrl(I): /* tab */ addch (' '); x++; while (x++ % 8) addch (' '); break; case ctrl(J): /* newline */ x = 0; if ((++y + upper_y) > lower_y) y = 1; move (y + upper_y, 0); clrtoeol (); break; case ctrl(L): /* clear to end of current line */ clrtoeol (); break; case ctrl(M): /* carriage return */ x = 0; move (y + upper_y, 0); break; case ctrl(N): /* clear window */ for (y = 1; (y+upper_y) <= lower_y; y++) { move (y + upper_y, 0); clrtoeol (); } y = 1; x = 0; move (y + upper_y, 0); break; case ctrl(U): /* line kill */ case ctrl(X): x = 0; move (y + upper_y, 0); clrtoeol (); break; case ctrl(W): /* word erase */ while (x >= 0) { /* nuke the spaces */ if (inch () != ' ') break; addstr (" \b\b"); if (x > 0) x--; else break; } while (x >= 0) { /* and the non-spaces */ if ((ch = inch ()) == ' ') { addch (ch); x++; break; } addstr (" \b\b"); if (x > 0) x--; else break; } break; case ctrl(^): /* home cursor */ y = 1; x = 0; move (y + upper_y, 0); break; case ctrl(G): /* bell - switchable */ if (Bells) { (void) write (1, "\007", 1); break; } /* fall through ... */ default: if (ch < ' ') { addch ('^'); ch += '@'; x++; } addch (ch); x++; break; } if (x >= maxx) { /* have wrapped around to next line */ y++; x %= maxx; if (y + upper_y > lower_y) /* wrapped back to top of window */ y = 1; move (upper_y + y, 0); clrtoeol (); } } /* * Print a message to the bottom line. */ putmessage (str) char *str; { int y, x; extern int Inverse; extern int did_screen; if (!did_screen) { printf ("%s\r\n", str); fflush (stdout); return; } touched25 = 1; /* has been changed */ getyx (stdscr, y, x); /* save current pos */ move (maxy, 0); /* go to bottom line */ clrtoeol (); /* clear it */ refresh (); /* they see it clear */ if (Inverse) standout (); while (*str) { /* print out the message, removing ctrl chars */ if (*str < ' ') { addch ('^'); addch (*str + '@'); } else addch (*str); str++; } if (Inverse) standend (); move (y, x); /* restore old pos */ refresh (); /* etc ... */ } /* * Print out a header for the given slot. */ header (num) int num; { register int i; extern int Inverse; if (windows[num].inuse == 0) { error (0, "header(): slot not in use"); return; } sprintf (buf, "---- %s@%s on %s (%s) ", windows[num].login, windows[num].host, windows[num].tty, windows[num].realname); for (i = strlen (buf); i < maxx; i++) buf[i] = '-'; buf[maxx-2] = '\0'; /* two (four?) for magic-cookie terminals */ move (windows[num].upper, 0); if (Inverse) standout (); addstr (buf); if (Inverse) standend (); } /* * Take the current screen and contort it to handle the new windows. * * Handles both the screen changing size and just the * number of windows changing. * * We set caught_sig to be non-zero if coming here on SIGWINCH, 0 otherwise. * We also only refresh the screen if we caught the signal.... */ stretch (caught_sig) int caught_sig; { register int i, w; register int extra; register int lines; int oldy, oldx; int newy, newx; int lines_win; int oldcurr = current; if (users == 0) /* nothing to recompute */ return; stretching = 1; oldy = maxy + 1; oldx = maxx + 1; if (caught_sig) { /* got SIGWINCH, have to redo screen */ #if defined(TIOCGWINSZ) || defined(TIOCGSIZE) #ifdef TIOCGWINSZ struct winsize winsize; #else TIOCGSIZE /* (older?) suns */ struct ttysize ttysize; #endif TIOGSIZE /* get new screen size */ #ifdef TIOCGWINSZ if (ioctl (0, TIOCGWINSZ, &winsize)) { error (0, "ioctl: TIOCGWINSZ"); return; } newy = winsize.ws_row; newx = winsize.ws_col; #else TIOCGSIZE if (ioctl (0, TIOCGSIZE, &ttysize)) { error (0, "ioctl: TIOCGSIZE"); return; } newy = ttysize.ts_lines; newx = ttysize.ts_cols; #endif TIOCGSIZE #else /* don't have window size ioctls */ /* * Can't get new window size, so presumably sigwinch * doesn't exist and this code will never happen !!! */ newy = oldy; newx = oldx; #endif TIOCGWINSZ maxy = newy - 1; /* max screen coordinate - zero-based */ maxx = newx - 1; } else { /* screen size hasn't changed */ newy = oldy; newx = oldx; } if (newx != oldx || newy != oldy) { /* have to shut down curses */ echo (); nocbreak (); endwin (); did_screen = 0; initscr (); /* and restart it */ did_screen = 1; cbreak (); noecho (); nonl (); signal (SIGTSTP, sigstop); } else { /* just have to clear all the lines */ for (i = 0; i < newy; i++) { move (i, 0); clrtoeol (); } } lines_win = (newy - 1) / users; /* recompute window boundaries */ extra = (newy - 1) - (users * lines_win); /* total extra lines */ lines = 0; /* * Now calculate the new boundaries and * copy the buffer back to the new window. */ x = 0; y = 1; /* go back to the beginning of the window */ for (w = 0; w < WINDOWS; w++) { /* now go copy boundaries */ if (windows[w].inuse == 0) continue; windows[w].upper = lines; /* new top boundary */ if (extra-- > 0) /* extra lines for this window */ lines++; lines += lines_win; /* add on lines */ windows[w].lower = lines-1; /* new bottom boundary */ header (w); /* redraw the window header */ windows[w].x = 0; /* reset position to top corner */ windows[w].y = 1; selwin (w); /* switch to this window */ /* and dump the buffer back into the window */ for (i = windows[w].old; i != windows[w].new; i = (i+1) % BUFFER) showch (windows[w].buf[i]); } move (y, x); selwin (oldcurr); /* reselect the right window */ if (caught_sig) refresh (); /* and make it all visible */ stretching = 0; } !Funky!Stuff! fi # end of overwriting check # End of shell archive exit 0
broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)
#-----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
broome@ucbvax.BERKELEY.EDU (Jonathan C. Broome) (12/29/85)
#-----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 # convd.c # This archive created: Sat Dec 28 01:11:14 1985 export PATH; PATH=/bin:$PATH mkdir convd cd convd echo shar: extracting "'Makefile'" '(1515 characters)' if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \!Funky!Stuff! > 'Makefile' # # Makefile for phone conversation daemon 20 December 1985 # # This one is pretty straightforward - no special flags for it. CFLAGS = -O SRCS = convd.c DEST = convd RDEST = /usr/local/lib/convd all: ${DEST} ${DEST}: ${SRCS} /bin/rm -f ${DEST} cc ${CFLAGS} -o ${DEST} ${SRCS} install: ${DEST} /bin/rm -f ${RDEST} cp ${DEST} ${RDEST} clean: /bin/rm -f ${DEST} core *.o shar: Makefile ${SRCS} shar -v Makefile ${SRCS} > ../shar.convd dist: ${DEST} -cp ${DEST} ${RDEST} -rcp ${DEST} buddy:${RDEST} -rcp ${DEST} franny:${RDEST} -rcp ${DEST} holden:${RDEST} -rcp ${DEST} seymour:${RDEST} -rcp ${DEST} zooey:${RDEST} -rcp ${DEST} miro:${RDEST} -rcp ${DEST} cory:${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 convd.o: /usr/include/errno.h convd.o: /usr/include/sys/ttydev.h convd.o: /usr/include/sys/ttychars.h convd.o: /usr/include/sys/ioctl.h convd.o: /usr/include/signal.h convd.o: /usr/include/netdb.h convd.o: /usr/include/netinet/in.h convd.o: /usr/include/sys/socket.h convd.o: /usr/include/sys/uio.h convd.o: /usr/include/sys/types.h convd.o: /usr/include/stdio.h convd.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 "'convd.c'" '(9758 characters)' if test -f 'convd.c' then echo shar: will not over-write existing file "'convd.c'" else cat << \!Funky!Stuff! > 'convd.c' #define KERNEL_BUG #ifndef lint static char RCSid[] = "$Header: convd.c,v 1.1 85/10/29 14:20:06 broome Exp $"; #endif /* * The conversation daemon --- does all the main work * for one conversation. It is invoked with stdin (fd 0) * on the service port; it listens for requests there and * does the right thing. * * See the comments in ../client/readstream.c for an * explanation of the command encoding scheme used here. * * NOTE: this code relies heavily upon the writev() system call * which provides for scatter/gather arrays of data, thus allowing * us to to write out multiple arrays of characters in a single * system call, thus avoiding having to copy data from one buffer * to another. * * Also ... note that we don't use slot #0 - the client program * wants to remap the user's window into slot zero, so we help * out by never assigning *anyone* that slot. */ /* * $Log: convd.c,v $ * Revision 1.1 85/10/29 14:20:06 broome * Initial revision */ #include "../common.h" #include <stdio.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <signal.h> #include <sys/ioctl.h> #include <errno.h> #define BUFFER 128 /* size of char buffer to use */ #define MAXSLOTS 32 /* max users/conversation */ struct slot { int inuse; /* this slot in use? */ char *info; /* user's login, host, tty, etc. */ char buffer[BUFFER]; /* text buffer */ int new; /* index of most recent character */ int old; /* index of oldest character */ int fd; /* open stream file descriptor */ int mask; /* mask set on this file descrip. */ } slots[MAXSLOTS]; /* all users in this conversation */ extern int errno; int users; /* number of users on */ int stayaround; /* still waiting for first users */ int currslot; /* current slot */ int highslot; /* highest slot number in use */ #ifdef KERNEL_BUG int warned = 0; #endif #define SIZ BUFFER /* read this many chars from clients */ char buf[SIZ]; char *strsave(); char *malloc(); int sigalrm(); main () { register struct slot *cslot; register char *c; register int i; register int sl; register int fds; register int r; register int changed; static int new, old; struct iovec iov[2]; char recvbuf[SIZ]; char sel[1]; /* for selecting current window */ int mask; /* saved mask ... */ int rmask; /* munged by select() */ int ind; #ifdef NICE (void) nice (-3); /* get a little bit of priority */ #endif users = 0; /* noone here yet */ stayaround = 1; currslot = -1; highslot = -1; changed = 0; signal (SIGPIPE, SIG_IGN); /* we'll find out soon enough */ signal (SIGALRM, sigalrm); /* to handle timeout */ alarm (60 * 60); /* go away if noone home */ initslots (); /* clean everything out first */ mask = 1 << 0; /* stdin is service socket */ iov[0].iov_base = sel; iov[0].iov_len = 1; iov[1].iov_base = recvbuf; do { rmask = mask; if ((fds = select (32, &rmask, 0, 0, 0)) <= 0) continue; if (rmask & (1 << 0)) /* service port */ service (&mask); /* let it modify mask */ for (sl = 0; sl <= highslot && fds; sl++) { /* client port */ cslot = &slots[sl]; if (cslot->inuse == 0) continue; if (rmask & cslot->mask) { /* on this slot */ fds--; /* decrement slots to check */ if ((r = read (cslot->fd, recvbuf, SIZ)) <= 0) { /* EOF */ mask &= ~(cslot->mask); /* remove from mask */ deluser (sl); if (sl == currslot) /* have to switch windows */ currslot = -1; /* just in case ... */ } else { iov[1].iov_len = r; new = cslot->new; /* index of where to add */ old = cslot->old; /* index of oldest char */ c = &cslot->buffer[new]; /* so point to newest */ for (i = 0; i < r; i++) { *c++ = recvbuf[i]; new++; if (new == BUFFER) { /* at end of buffer */ new = 0; /* so loop back around */ c = cslot->buffer; } else if (new == old) { /* full buffer */ old++; /* so advance the end */ if (old == BUFFER) /* wrapped around here */ old = 0; } } cslot->new = new; cslot->old = old; /* save pointers */ if (sl != currslot) { /* switch to this slot */ sel[0] = (META | sl); currslot = sl; ind = 0; } else ind = 1; for (i = 0; i <= highslot; i++) /* ship out to others */ if (slots[i].inuse) (void) writev (slots[i].fd, &iov[ind], 2-ind); } } } } while ((users > 1) || (stayaround == 1)); shutdown (0, 2); exit (17); } /* * Set all the slots to an unused state before starting... */ initslots () { int i; for (i = 0; i < MAXSLOTS; i++) { slots[i].inuse = 0; slots[i].fd = -1; } } /* * Handle a request on the service port. * Modifies the socket select mask appropriately. */ service (mask) int *mask; { register int new; register int j; register char *i; struct sockaddr_in addr; int len; int r; len = sizeof (addr); if ((new = accept (0, &addr, &len)) < 0) { if (errno != EINTR) #ifdef KERNEL_BUG if (warned++ == 0) #endif KERNEL_BUG fatal (errno); return; } for (j = 1; j < MAXSLOTS; j++) if (slots[j].inuse == 0) break; if (j == MAXSLOTS) { write (new, "Too many users!\n", 16); shutdown (new, 2); close (new); return; } if ((r = read (new, buf, SIZ)) == 0) { /* EOF ?? */ close (new); return; } buf[r] = '\0'; /* save name, host, tty, realname */ slots[j].info = strsave (buf); if (j > highslot) highslot = j; slots[j].inuse = 1; slots[j].fd = new; slots[j].new = 0; slots[j].mask = 1 << new; slots[j].old = 0; users++; *mask |= (1 << new); /* add new fd to mask */ r = 1; ioctl (new, FIONBIO, &r); /* mark socket as non-blocking */ sprintf (buf, "%c%s%c", META | ADDUSER | j, slots[j].info, META); sendit (buf, strlen (buf)); /* tell whole group about me */ intro (new); /* and fill me in on things */ return; } /* * Retransmit a message to all the users. */ sendit (buf, len) char *buf; int len; { register int i; for (i = 1; i <= highslot; i++) if (slots[i].inuse) if (write (slots[i].fd, buf, len) != len) perror ("sendit: write"); } /* * Delete a user from this conversation. */ deluser (i) int i; { int ch; if (--users < 2) { shutdown (0, 2); close (0); exit (0); } stayaround = 0; slots[i].inuse = 0; close (slots[i].fd); free (slots[i].info); ch = META | DELUSER | i; sendit (&ch, 1); } /* * Save a string. */ char * strsave (s) char *s; { char *new, *malloc(); if (new = malloc (strlen (s) + 1)) strcpy (new, s); return (new); } /* * Send the new filedes all the users and all the buffers. * We first send UPDATE | 0 to tell the user to delay updating * the screen until we've sent all the text buffers, at which * point we send UPDATE | 01 to signal that we're done and * the screen should be updated. */ intro (fd) int fd; { struct iovec iov[3]; /* used for multi-buffer writes */ register int old, new; register int s; /* slot number */ register int i; register char *c; register int num; /* number of buffers to write out */ char sc; /* used for slot number selection */ /* tell user not to update screen until done with intro */ sc = META | UPDATE | 00; write (fd, &sc, 1); /* first go through and add all the windows */ for (s = 0; s <= highslot; s++) { if (slots[s].inuse == 0 || slots[s].fd == fd) continue; sprintf (buf, "%c%s%c", META | ADDUSER | s, slots[s].info, META); write (fd, buf, strlen (buf)); } /* now go through and give him all the buffers */ for (s = 0; s <= highslot; s++) { if (slots[s].inuse == 0 || slots[s].fd == fd) continue; sc = META | s; /* switch to this slot */ iov[0].iov_base = ≻ iov[0].iov_len = 1; #ifdef notdef /raboof ======== foo bar baz zot blip/ ^ new ^ old ^ BUFFER |-----| |-------------------| slots[s].new BUFFER - slots[s].old #endif notdef new = slots[s].new; old = slots[s].old; iov[1].iov_base = &slots[s].buffer[old]; iov[1].iov_len = (old > new ? BUFFER - old : new - old); if (old > new) { iov[2].iov_base = slots[s].buffer; iov[2].iov_len = new; num = 3; } else num = 2; writev (fd, iov, num); /* write all at once!!! */ } /* now he can update the screen */ sc = META | UPDATE | 01; write (fd, &sc, 1); } /* * Come here on alarm signal. If less than 2 users, exit. */ sigalrm () { if (users < 2) { shutdown (0, 2); close (0); exit (0); } return; } /* * We have encountered some kind of nasty error. * Tell all the users about it and go away. */ fatal (err) int err; { extern char *sys_errlist[]; extern int sys_nerr; char buf[128]; char mesg[256]; char host[32]; int s; gethostname (host, 32); sprintf (mesg, "\nMessage from phone conversation daemon @ %s:\n", host); if (err < sys_nerr) sprintf (buf, "Fatal error: %s\n", sys_errlist[err]); else sprintf (buf, "Fatal error: %d", err); #ifdef KERNEL_BUG /* try to keep going */ strcat (mesg, buf); strcat (mesg,"Warning: no more users can join this conversation! Sorry.\n"); #endif KERNEL_BUG for (s = 0; s <= highslot; s++) if (slots[s].inuse) write (slots[s].fd, mesg, strlen (mesg)); #ifndef KERNEL_BUG chdir ("/"); abort (); exit (1); #endif KERNEL_BUG } !Funky!Stuff! fi # end of overwriting check # End of shell archive exit 0