Steven Grimm <Steven.Grimm@Eng.Sun.COM> (06/01/91)
Submitted-by: Steven Grimm <Steven.Grimm@Eng.Sun.COM> Posting-number: Volume 1, Issue 3 Archive-name: supersrv/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 2 (of 2)." # Contents: supersrv.c # Wrapped by csr@calvin.doc.ca on Fri May 31 23:28:09 1991 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'supersrv.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'supersrv.c'\" else echo shar: Extracting \"'supersrv.c'\" \(8732 characters\) sed "s/^X//" >'supersrv.c' <<'END_OF_FILE' X#include "common.h" X#include <netinet/in.h> X#include <arpa/inet.h> X Xextern int errno; X X/* X * SuperServer. X * X * 1.2: REGISTER handler sends QUIT to all old servers running with the same X * username. X * 1.3: Fix infinite loop bug X * Pass client IP address to server X * 1.4: Services can have descriptions X */ X X#define WELCOME "SuperServer -- enter service desired.\n" X#define NOSERV "Service not offered.\n" X Xint thirty; X X/* X * This structure is used to keep the database of available services. X */ Xstruct service { X char name[20]; /* Service name */ X char desc[50]; /* Service description */ X int fd; /* File descriptor that offers it */ X struct service *next; /* Next service in list */ X} *list = (struct service *)0; X Xchar users[NOFILE][12]; /* user connected to each fd */ X X/* X * The peer of the most recent connection. X */ Xstruct sockaddr_in rcon; X Xmain() X{ X int fd_so, /* Socket() file descriptor */ X fd_co; /* Connected file descriptor */ X short portno; /* Port number to listen on */ X char request[80]; X extern int sigchld(); X X/* X * First things first: put ourselves in the background. X */ X/* if (fork()) X exit(0);*/ X X portno = SUPERPORT; X thirty = 30; X X/* X * Set up the server socket on the appropriate port number and listen on it. X * If we get an address-already-in-use error, silently exit, since we're X * presumably waiting for the TCP port to be cleared up, and will be executed X * again by server. X */ X fd_so = serversock(portno); X if (fd_so < 0) X { X if (errno != EADDRINUSE) X perror("serversock"); X exit(errno); X } X X (void)listen(fd_so, 5); X setsockopt(fd_so, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty)); X fcntl(fd_so, F_SETOWN, getpid()); X X/* X * And we'll need to accomodate child processes. X */ X signal(SIGCHLD, sigchld); X/* X * We will catch broken pipes in our main loop, so don't fuss with signals. X */ X signal(SIGPIPE, SIG_IGN); X X/* X * Now keep accepting connections and interpreting them. X */ X while (1) X { X fd_co = getcon(fd_so); X X if (fd_co < 0) X { X perror("accept"); X exit(0); X } X X fcntl(fd_co, F_SETOWN, getpid()); X setsockopt(fd_co,SOL_SOCKET,SO_LINGER,&thirty, sizeof(thirty)); X X write(fd_co, WELCOME, sizeof(WELCOME)-1); X if (getline(fd_co, request, sizeof(request)-1)) X { X if (handle(fd_co, request)) X close(fd_co); X } X else X close(fd_co); X } X} X X/* X * Get a connection, or handle a disconnected server. X */ Xgetcon(old) Xint old; X{ X struct service *cur; X fd_set reed, tread, other; X int fd, size; X X FD_ZERO(&reed); X FD_ZERO(&other); X X for (cur = list; cur; cur = cur->next) X FD_SET(cur->fd, &reed); X FD_SET(old, &reed); X X while (1) X { X tread = reed; X select(NOFILE, &tread, NULL, NULL, NULL); X if (FD_ISSET(old, &tread)) X break; X for (fd = 0; fd < NOFILE; fd++) X if (FD_ISSET(fd, &tread)) X { X killfd(fd); X close(fd); X FD_CLR(fd, &reed); X } X } X size = sizeof(rcon); X return( accept(old, &rcon, &size) ); X} X X X/* X * Get an input line from a file descriptor. This is probably very slow. X * Since it's only called once, though... X */ Xgetline(fd, buf, len) Xint fd, len; Xchar *buf; X{ X int index = 0; X char c; X X while (read(fd, &c, 1) == 1) X { X if (c == '\n') X break; X X if (c == '\r') X continue; X X if (index < len) X buf[index++] = c; X } X X buf[index] = 0; X return index; X} X X X/* X * Handle a user request. This will either be "REGISTER" or some X * user-defined function. X */ Xhandle(fd, string) Xint fd; Xchar *string; X{ X struct service *cur; X char user[16], hisip[16]; X X if (string[0] == '\0') X return 1; X/* X * If a subserver wants to register itself, grab service X * names from it until it outputs an empty line. X */ X if (!strcmp(string, "REGISTER")) X { X char name[70]; X int i; X X if (! getline(fd, users[fd], 11)) X return 1; X X/* X * If this user already has servers connected, kill them off. X */ X for (i = 0; i < NOFILE; i++) X if (i != fd && ! strcmp(users[i], users[fd])) X { X killfd(i); X write(i, "QUIT\0xxxxxxxxxxxxxxx", 20); X close(i); X users[i][0] = '\0'; X } X X/* X * Get service names. If there are descriptions, use those as well. X */ X while (getline(fd, name, 69)) X { X char *desc, *index(); X X#ifdef DEBUG X printf("supersrv got input '%s'\n", name); X#endif X desc = index(name, '\t'); X if (desc != NULL) X *desc++ = '\0'; X X cur = (struct service *)malloc(sizeof(*cur)); X strcpy(cur->name, name); X if (desc != NULL) X strcpy(cur->desc, desc); X else X cur->desc[0] = '\0'; X X cur->fd = fd; X cur->next = list; X list = cur; X } X return 0; /* Keep file descriptor open */ X } X X getline(fd, user, 15); X X/* X * Print a nicely-formatted list of services. If there aren't any services, X * print a message to that effect. X */ X if (!strcmp(string, "LIST")) X { X char buf[100], format[20]; X int maxlen = 10; X#define LISTHEAD "Username Service\n-------- -------\n" X#define NOSERVS "No services available.\n" X X if (list) X { X write(fd, LISTHEAD, sizeof(LISTHEAD)-1); X X for (cur = list; cur; cur = cur->next) X if (maxlen < strlen(cur->name)) X maxlen = strlen(cur->name); X sprintf(format, "%%-12s %%-%ds%%s\n", maxlen + 2); X X for (cur = list; cur; cur = cur->next) X { X if (user[0] && strcmp(user, users[cur->fd])) X continue; X sprintf(buf, format, users[cur->fd], cur->name, X cur->desc); X write(fd, buf, strlen(buf)); X } X } X else X write(fd, NOSERVS, sizeof(NOSERVS)-1); X return 1; X X } X X for (cur = list; cur; cur=cur->next) X if (! strcmp(string, cur->name)) X if ((! user[0]) || (! strcmp(user, users[cur->fd]))) X break; X X if (! cur) X { X write(fd, NOSERV, sizeof(NOSERV)); X return 1; X } X X write(cur->fd, string, 20); X/* X * 1.3: Write out the client's IP address (which we know will be in rcon.) X */ X sprintf(hisip, "%s\n", inet_ntoa(rcon.sin_addr)); X write(cur->fd, hisip, strlen(hisip)); X X shuffle(cur->fd, fd); X X return 1; X} X Xsigchld() X{ X wait(0); X} X X/* X * Kill all entries in the linked list with a certain file X * descriptor. X */ Xkillfd(fd) Xint fd; X{ X struct service *cur, *temp; X X while (list && list->fd == fd) X { X temp = list->next; X free(list); X list = temp; X } X X if (list) X for (cur = list; cur; cur = cur->next) X while (cur->next && cur->next->fd == fd) X { X temp = cur->next; X cur->next = cur->next->next; X free(temp); X } X} X X X/* X * This is the kludgy part. We want to effectively connect the X * client and the appropriate subserver. Since there's no way to X * connect two sockets together, we have to fork off a child and X * sit there shuffling bytes back and forth between the two file X * descriptors. When one of them shuts down, we shut the other one X * down and die. X * X * For now, since only one client can be talking to each subserver X * at a given time, we erase all the subserver's services from the X * service list. It will reconnect when it's done forking off the X * service. X */ X#ifndef MIN X#define MIN(x,y) (((x)>(y))?(y):(x)) X#endif X Xshuffle(subsrv, client) Xint subsrv, client; X{ X int fd; X fd_set reed, rite, except; X extern void quit(); X X killfd(subsrv); X X if (fork()) X { X close(subsrv); X return; X } X X for (fd = 0; fd < NOFILE; fd++) X if (fd != client && fd != subsrv) X close(fd); X X FD_ZERO(&reed); X FD_SET(client, &reed); X FD_SET(subsrv, &reed); X FD_ZERO(&rite); X except = reed; X X fcntl(client, F_SETOWN, getpid()); X fcntl(subsrv, F_SETOWN, getpid()); X/* fcntl(client, F_SETFL, FNDELAY); X fcntl(subsrv, F_SETFL, FNDELAY); X*/ X X signal(SIGURG, quit); X signal(SIGPIPE, quit); X X while (1) X { X fd_set tread, twrite, texcept; X int numbytes, bsize, numread; X char buf[4096]; X X tread = reed; X twrite = rite; X texcept = except; X X select(NOFILE, &tread, &twrite, &texcept, (void *)0); X X if (FD_ISSET(subsrv, &tread)) X { X ioctl(subsrv, FIONREAD, &numbytes); X bsize = MIN(numbytes, sizeof(buf)); X numread = read(subsrv, buf, bsize); X if (numread < 0 && errno != EWOULDBLOCK) X { X perror("subsrv"); X exit(0); X } X if (! numread) X { X shutdown(client, 1); X shutdown(subsrv, 0); X FD_CLR(subsrv, &reed); X } X else X write(client, buf, numread); X } X X if (FD_ISSET(client, &tread)) X { X ioctl(client, FIONREAD, &numbytes); X bsize = MIN(numbytes, sizeof(buf)); X numread = read(client, buf, bsize); X if (numread < 0 && errno != EWOULDBLOCK) X { X perror("client"); X exit(0); X } X if (! numread) X { X shutdown(client, 0); X shutdown(subsrv, 1); X FD_CLR(client, &reed); X } X else X write(subsrv, buf, numread); X } X X/* If both sides were shut down, leave. */ X if (! (FD_ISSET(client, &reed) || FD_ISSET(subsrv, &reed))) X { X close(client); X close(subsrv); X exit(0); X } X X if (FD_ISSET(client, &texcept) || FD_ISSET(subsrv, &texcept)) X { X close(client); X close(subsrv); X exit(0); X } X } X} X Xvoid quit() X{ X exit(0); X} X END_OF_FILE if test 8732 -ne `wc -c <'supersrv.c'`; then echo shar: \"'supersrv.c'\" unpacked with wrong size! fi # end of 'supersrv.c' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 exit 0 # Just in case... -- Andrew Patrick acting as Comp.Sources.Reviewed Moderator Department of Communications, Ottawa, CANADA csr@calvin.doc.CA