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