home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
mac
/
source
/
suprsrv.shr
< prev
next >
Wrap
Text File
|
1990-06-29
|
34KB
|
1,535 lines
#!/bin/sh
# shar: Shell Archiver (v1.22)
#
# Run the following text with /bin/sh to create:
# MANIFEST
# Makefile
# NOTES
# client.1
# client.c
# common.h
# server.1
# server.c
# socket.c
# supersrv.8
# supersrv.c
#
sed 's/^X//' << 'SHAR_EOF' > MANIFEST &&
XThis shar file should contain:
X
XMANIFEST This file
XMakefile To build everything
XNOTES Various implementation notes, wish list, etc.
Xclient.1 Manual page for the client program
Xclient.c The client program
Xcommon.h Definitions common to all three programs
Xserver.1 Manual page for the server program
Xserver.c The server program
Xsocket.c Various blackbox IPC routines used by everything
Xsupersrv.8 Manual page for the supersrv program
Xsupersrv.c The supersrv program
SHAR_EOF
chmod 0600 MANIFEST || echo "restore of MANIFEST fails"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X#
X# SuperServer 1.2
X#
X# by Steven Grimm, koreth@ebay.sun.com
X#
X
XCFLAGS = -O
X
XSUPERO = supersrv.o socket.o
XSERVERO = server.o socket.o
XCLIO = client.o socket.o
X
Xall: supersrv server client
X
Xsupersrv: $(SUPERO)
X $(CC) $(CFLAGS) $(SUPERO) -o $@
X
Xserver: $(SERVERO)
X $(CC) $(CFLAGS) $(SERVERO) -o $@
X
Xclient: $(CLIO)
X $(CC) $(CFLAGS) $(CLIO) -o $@
X
Xclean:
X /bin/rm -f $(SUPERO) $(SERVERO) $(CLIO) supersrv server client core
X
Xinstall: all
X install -s -c -m 755 supersrv /usr/local
X install -s -c -m 755 server /usr/local
X install -s -c -m 755 client /usr/local
SHAR_EOF
chmod 0600 Makefile || echo "restore of Makefile fails"
sed 's/^X//' << 'SHAR_EOF' > NOTES &&
XVERSION 1.2 CHANGES:
X
XServer has a builtin "QUIT" service, which causes it to disconnect from
Xsupersrv and die. When a server REGISTERs with supersrv, supersrv tells
Xall servers offering services from the same user to QUIT. This allows
Xusers to run server without worrying about multiple servers sticking
Xaround.
X
XClient no longer needs a LIST argument; if no service name is given,
XLIST is assumed.
X
XVERSION 1.1 CHANGES:
X
XMinor bug fixes of little consequence.
X
XVERSION 1.0:
X
XThis program allows individual users to set up network services without
Xhaving to worry about the intricacies of socket I/O. It is similar in
Xfunction to the "rsh" program, but restricts the commands which can be
Xexecuted by remote users. No .rhosts or password is required, since
Xthe remote user can only execute commands from a specified (presumably
Xsafe) list.
X
XThe service programs think they're talking to a pipe (because they are).
XStdin and stdout are redirected to the pipe; stderr is mapped to stdout.
XShell scripts can be offered as network services, but make sure you have
Xexecute permission on them and that the line "#!/bin/sh" (or csh, or
Xwhatever shell you're using) appears at the top of the file, or UNIX won't
Xrecognize it as an executable-format file.
X
XThe server consists of two layers. The bottom layer (subserver) is
Xexecuted by a user when he wants to advertise services. It reads a
Xconfiguration file from the user's home directory, which contains a
Xlist of service names and full path specifications. It then checks for
Xthe existence of the top layer (the super-server), and runs the
Xsuperserver program if it was not already active. In any case, the
Xserver connects to the superserver, registers its service-names, and
Xawaits a request.
X
XThe superserver accepts connections from remote locations, usually
Xinitiated with the "client" program. It reads a line of input from the
Xclient program, which specifies the name of the desired service. If
Xone of the subservers has advertised the requested service, the
Xsuperserver forks off a child process. The child writes the name of
Xthe requested service to the appropriate subserver, then acts as a
Xmailman, shuffling bytes between the remote user and the subserver
Xuntil one of them disconnects. Meanwhile, the parent superserver
Xwaits for another connection, and the whole bloody mess starts over.
X
XThe client-supersrv handshaking looks like this (all lines are newline-
Xterminated, so you could talk to supersrv with telnet, for instance):
X
Xclient supersrv
X------ --------
X welcome message
Xservice name
Xusername (or empty line)
Xarg1
Xarg2
Xarg3
X .
X .
X .
Xargn
Xblank line
X
X
XIf a superserver process is killed, all its subservers try to restart
Xit until one of them succeeds. If a server process is killed, the
Xsuperserver removes all its services from the listing. Some braindamaged
XBSD implementations can take up to five minutes to figure out that a
Xprocess has died and that its socket addresses are no longer in use, so
Xkilling a superserver might result in a short interruption of service.
X
XSending a HUP signal to a server causes it to reload its database.
X
X
XWish list:
X
X If exec-ing a program fails because of an invalid file format, we
Xshould try to run the program from sh, since it's probably a script without
Xthe #!/bin/sh at the top.
X
X Some form of logging would be nice, so people can tell who's
Xusing which services.
X
X Signal-passing and standard error support a la rsh would be neat, too.
X
X Support for interactive programs by automatically allocating a pty
Xbefore running a program would be helpful. Some form of environment passing
Xwould also need to be implemented for programs that use termcap.
X
XIf you have questions, comments, bug reports, large amounts of excess cash,
Xhorny women, etc., send a letter to koreth@ebay.sun.com.
X
SHAR_EOF
chmod 0600 NOTES || echo "restore of NOTES fails"
sed 's/^X//' << 'SHAR_EOF' > client.1 &&
X.TH CLIENT 1 "15 February 1990"
X.SH NAME
Xclient \- use a network service, version 1.2
X.SH SYNOPSIS
X.B client
X[
X.B \-u
X.I username
X]
X.I host
X[
X.I servicename
X[
X.I arguments
X] ]
X.SH DESCRIPTION
X.I Client
Xinterfaces with services offered through
X.IR server (1).
XSpecify the hostname and service; the service must be offered
Xby someone on the appropriate host. To get a list of services
Xoffered on
X.I host,
Xomit the
X.I servicename
Xand
X.I arguments.
XAny
X.I arguments
Xafter the
X.I servicename
Xare passed to the service. Be careful of passing
Xfilenames, as remote machines generally can't read files on the
Xlocal host.
X.SH OPTIONS
X.IP \-u
XIf two services of the same name are offered by different users,
Xuse the
X.I \-u
Xoption to select the desired user. If this flag is not given,
Xthe client will pick the first service whose name matches the
Xrequested one.
X.SH EXAMPLES
X.ft C
X% client ssyx.ucsc.edu fortune -l
X.ft R
X.PP
XThis connects to machine "ssyx.ucsc.edu" and prints out a long
Xfortune.
X.sp 1
X.ft C
X% client -u geek doofus.foo.bar
X.ft R
X.PP
XThis lists all services offered by user "geek" on host
X"doofus.foo.bar".
X.SH "SEE ALSO"
Xrsh(1), server(1), supersrv(8)
X.SH DIAGNOSTICS
XIf no services are offered on a host,
X.I client
Xsays "No services on \fIhostname\fR." This sometimes means that the
X.IR supersrv (8)
Xprocess on that host has died; if this is the case, then services will
Xappear again in a short time.
X.SH AUTHOR
XSteven Grimm (koreth@ebay.sun.com, sun!ebay!koreth)
SHAR_EOF
chmod 0600 client.1 || echo "restore of client.1 fails"
sed 's/^X//' << 'SHAR_EOF' > client.c &&
X#include "common.h"
X
X/*
X** Use the "server" and "supersrv" programs.
X**
X** 1.2: LIST argument is no longer necessary to list services.
X*/
X
Xmain(argc, argv)
Xchar **argv;
X{
X int thirty;
X char c, user[16];
X int fd, i;
X
X if (argc < 2)
X {
Xusage:
X fprintf(stderr, "usage: %s [-u user] host [cmd [parms]]\n",
X argv[0]);
X exit(-1);
X }
X
X user[0] = 0;
X
X if (argv[1][0] == '-')
X switch(argv[1][1])
X {
X case 'u':
X strncpy(user, argv[2], 15);
X user[15] = 0;
X argv += 2;
X argc -= 2;
X if (argc < 2)
X goto usage;
X break;
X default:
X fprintf(stderr, "-%c flag unknown\n", argv[1][1]);
X break;
X }
X
X thirty = 30;
X
X fd = clientsock(argv[1], SUPERPORT);
X if (fd < 0)
X {
X switch(fd) {
X case -9999:
X fprintf(stderr, "%s: host unknown\n", argv[1]);
X break;
X case -ECONNREFUSED:
X fprintf(stderr, "No services on %s\n", argv[1]);
X break;
X default:
X perror("clientsock");
X break;
X }
X exit(-1);
X }
X
X setsockopt(fd, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty));
X
X do
X read(fd, &c, 1);
X while (c != '\n');
X
X if (argc == 2)
X writeln(fd, "LIST");
X else
X writeln(fd, argv[2]);
X writeln(fd, user);
X
X if (argc > 3)
X for (i=3; i<argc; i++)
X writeln(fd, argv[i]);
X
X writeln(fd, "");
X
X shuffle(fd);
X
X fcntl(0, F_SETFL, 0);
X close(fd);
X}
X
Xwriteln(fd, string)
Xint fd;
Xchar *string;
X{
X write(fd, string, strlen(string));
X write(fd, "\n", 1);
X}
X
X/*
X * Shuffle bytes between stdin/out and the socket. This forks off
X * once so that one process handles dataflow in each direction (that's
X * how rsh does it, and it makes the code a lot prettier...)
X */
Xshuffle(subsrv)
Xint subsrv; /* this will probably always be 3... */
X{
X fd_set reed, tread, other;
X int pid, numread, buf[BUFSIZ];
X
X pid = fork();
X if (pid < 0)
X {
X perror("fork");
X close(subsrv);
X exit(-1);
X }
X
X FD_ZERO(&reed);
X FD_ZERO(&other);
X
X if (!pid)
X {
X close(1);
X close(2);
X
X while (1)
X {
X numread = read(0, buf, sizeof(buf));
X if (numread <= 0)
X break;
X write(subsrv, buf, numread);
X }
X shutdown(subsrv, 1);
X close(subsrv);
X exit(0);
X }
X else
X {
X close(0);
X close(2);
X
X while (1)
X {
X numread = read(subsrv, buf, sizeof(buf));
X if (numread <= 0)
X break;
X write(1, buf, numread);
X }
X
X close(subsrv);
X kill(pid, SIGKILL);
X }
X}
X
SHAR_EOF
chmod 0600 client.c || echo "restore of client.c fails"
sed 's/^X//' << 'SHAR_EOF' > common.h &&
X/*
X * Common.h
X *
X * Definitions common to both the sub- and superserver.
X */
X
X/*
X * Common include files
X */
X#include <stdio.h>
X#include <signal.h>
X#include <errno.h>
X#include <fcntl.h>
X#include <sys/param.h>
X#include <sys/ioctl.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X
X/*
X * This is the port number that the superserver listens on.
X */
X#define SUPERPORT 3502
X
X/*
X * Common external variables.
X */
Xextern int errno;
X
SHAR_EOF
chmod 0600 common.h || echo "restore of common.h fails"
sed 's/^X//' << 'SHAR_EOF' > server.1 &&
X.TH SERVER 1 "14 July 1988"
X.SH NAME
Xserver \- offer network services, version 1.2
X.SH SYNOPSIS
X.B server
X.SH DESCRIPTION
X.I Server
Xallows users to offer services to other users, possibly on remote
Xhosts. Any program (or shell script) that a user has execute permission
Xon can be offered as a service. The program
X.IR client (1)
Xis then used to run the services, optionally passing arguments or input.
X.PP
XIf a server is the first to start up on a machine, the super-server
X.IR supersrv (8)
Xis automatically started by
X.I server,
Xwhich then tells
X.I supersrv
Xwhich services the user wants to offer.
X.I Server
Xlooks for a file called
X.I .services
Xin the user's home directory, which consists of a list of service names
Xand pathnames.
X.PP
XTo change the services being offered, run
X.I server
Xagain. The old
X.I server
Xwill die, and the new one will take its place. Killing a
X.I server
Xprocess results in the services it offered being removed from
X.IR supersrv 's
Xlist of services.
X.SH EXAMPLE
X.nf
X.ft C
X% cat .services
Xfortune /usr/games/fortune
Xw /usr/ucb/w
Xwho /bin/who
Xcrash /g/f/v/foobar/kill-the-system
X% server
X%
X.ft R
X.SH DISCUSSION
XMost programs should work without any problems as services. The service
Xthinks it's talking to a pipe; interactive programs work as long as they
Xdon't attempt to set any special terminal modes or use
X.IR termcap (3)
Xor
X.IR curses (3)
Xto try to update the client's screen. These restrictions are the same as
Xthose of
X.IR rsh (1);
X.IR client (1)
Xis similar to
X.I rsh
Xexcept that security is handled differently.
X.PP
XIf
X.I supersrv
Xgoes away (usually as a result of a kill command from a user who hasn't
Xread this or doesn't know why there's an extra process running next to his
Xserver), all the
X.I server
Xprocesses on the system will keep trying to restart
X.I supersrv
Xuntil one of them succeeds. Since it can take a while for TCP ports to
Xclear after their binding process has died on some implementations of
XBSD, killing a
X.I supersrv
Xmay result in a short interruption of service.
X.SH FILES
X.TS
X$HOME/.services services list
X.TE
X.SH "SEE ALSO"
Xclient(1), supersrv(8)
X.SH BUGS
X.PP
XShell scripts offered as services must have the line "#!/bin/sh" (or csh,
Xor whatever shell is being used) at the top, or
X.I server
Xwon't be able to execute them.
X.SH AUTHOR
XSteven Grimm (koreth@ebay.sun.com, ...!sun!ebay!koreth)
SHAR_EOF
chmod 0600 server.1 || echo "restore of server.1 fails"
sed 's/^X//' << 'SHAR_EOF' > server.c &&
X#include "common.h"
X#include <sys/file.h>
X#include <pwd.h>
X
X/*
X * SubServer main module.
X *
X * 1.2: Starting up a new server kills the old one.
X * 1.1: Some small bug fixes.
X */
X
X#define RCFILE "/.services" /* File to grab services from */
X
Xchar *getenv();
X
X/*
X * This linked list structure is used to keep track of the
X * services we're offering.
X */
Xstruct service {
X char name[20]; /* Service name */
X char path[MAXPATHLEN]; /* Service path */
X struct service *next; /* Next element... */
X} *list;
X
X/*
X * Read in a services file and set up the linked list of
X * services. Test each service to be sure we can offer it.
X * Returns 0 if there are no services offered.
X */
Xgetservices()
X{
X FILE *fp;
X char filename[MAXPATHLEN], *home;
X
X home = getenv("HOME");
X if (! home)
X {
X fprintf(stderr, "no home directory\n");
X return(0);
X }
X
X strcpy(filename, home);
X strcat(filename, RCFILE);
X
X fp = fopen(filename, "r");
X if (! fp)
X {
X perror("couldn't open services file");
X return(0);
X }
X
X list = NULL;
X
X while (! feof(fp))
X {
X char servname[20], format[20];
X
X servname[0] = 0;
X sprintf(format, "%%20s\t%%%d[^\n]", MAXPATHLEN);
X fscanf(fp, format, servname, filename);
X getc(fp);
X
X if (servname[0] && filename[0])
X {
X struct service *temp;
X
X if (access(filename, X_OK))
X {
X fprintf(stderr, "warning: can't execute %s\n",
X filename);
X continue;
X }
X
X temp = (struct service *) malloc(sizeof(struct service));
X strcpy(temp->name, servname);
X strcpy(temp->path, filename);
X temp->next = list;
X list = temp;
X }
X }
X fclose(fp);
X return 1;
X}
X
X
X/*
X * Reap children.
X */
Xsigchld()
X{
X return wait(0);
X}
X
Xint fd;
X
X/*
X * Reload the database.
X * We do this by killing the old list element by element then calling
X * getservices(), and closing the socket file descriptor so the main
X * loop will have to reregister our services.
X */
Xreload()
X{
X struct service *cur, *next;
X
X cur = list;
X while (cur)
X {
X next = cur->next;
X free(cur);
X cur = next;
X }
X
X if (! getservices())
X exit(-1);
X close(fd);
X fd = -1; /* prevent another close... */
X}
X
Xmain(argc, argv, envp)
Xchar **argv, **envp;
X{
X int thirty;
X struct service *cur;
X struct passwd *pw;
X char service[80], user[16];
X
X if (! getservices())
X exit(0);
X
X if (fork())
X exit(0);
X
X fd = open("/dev/tty", O_RDWR);
X if (fd >= 0)
X {
X ioctl(fd, TIOCNOTTY, 0);
X close(fd);
X }
X else
X printf("warning: couldn't disassociate from tty\n");
X
X/*
X * Handle signals. We want to reap children, so we should handle SIGCHLDs;
X * we also want to let the user reload his services database, which we do
X * with SIGHUP.
X */
X signal(SIGCHLD, sigchld);
X signal(SIGHUP, reload);
X
X thirty = 30;
X
X pw = getpwuid(getuid());
X if (pw == NULL)
X {
X printf("warning: couldn't get UID\n");
X user[0] = '\n';
X user[1] = '\0';
X }
X else
X {
X strncpy(user, pw->pw_name, sizeof(user)-2);
X user[sizeof(user)-1] = '\0';
X strcat(user, "\n");
X }
X
X close(0);
X close(1);
X close(2);
X
X while (1)
X {
X char c;
X
X fd = clientsock("localhost", SUPERPORT);
X if (fd < 0)
X if (errno == ECONNREFUSED)
X {
X start_super(argv[0], envp);
X/*
X * Give the superserver time to fire up.
X */
X sleep(5);
X continue;
X }
X else
X {
X perror("superserver connect");
X exit(-1);
X }
X
X thirty = 30;
X setsockopt(fd, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty));
X c = 0;
X do
X if (read(fd, &c, 1) < 0 && errno != EINTR)
X break;
X while (c != '\n');
X
X if (c != '\n')
X continue;
X
X write(fd, "REGISTER\n", 9);
X write(fd, user, strlen(user));
X
X for (cur = list; cur; cur = cur->next)
X {
X write(fd, cur->name, strlen(cur->name));
X write(fd, "\n", 1);
X }
X write(fd, "\n", 1);
X
X service[0] = 0;
X if (read(fd, service, 20) < 0 && errno != EBADF && errno != EINTR)
X {
X perror("read");
X exit(-1);
X }
X
X if (service[0])
X do_service(service, fd, envp);
X if (fd >= 0)
X close(fd);
X }
X}
X
X
X/*
X * Provide the service. Fork off and keep reading parameters until
X * they are terminated by an empty line, then pass them to the program
X * specified by the service.
X */
Xdo_service(service, fd, envp)
Xchar *service;
Xint fd;
Xchar **envp;
X{
X struct service *cur;
X char *argv[256], input[256];
X int curarg = 0, index = 0, thirty = 60;
X
X/*
X * If we're told to quit by the superserver, do so -- our owner has probably
X * run another server.
X */
X if (! strcmp(service, "QUIT"))
X {
X close(fd);
X exit(0);
X }
X
X if (fork())
X return;
X
X argv[curarg++] = service;
X
X while (1) {
X read(fd, &input[index], 1);
X if (input[index] == '\r')
X continue;
X if (input[index] == '\n')
X {
X if (! index)
X break;
X
X argv[curarg] = (char *)malloc(index+1);
X bcopy(input, argv[curarg], index);
X argv[curarg][index] = 0;
X
X index = 0;
X curarg++;
X }
X else
X index++;
X }
X
X dup2(fd, 0);
X dup2(fd, 1);
X dup2(fd, 2);
X setsockopt(0, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty));
X setsockopt(1, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty));
X setsockopt(2, SOL_SOCKET, SO_LINGER, &thirty, sizeof(thirty));
X if (fd > 2)
X close(fd);
X
X argv[curarg] = NULL;
X
X for (cur = list; cur; cur = cur->next)
X if (! strcmp(cur->name, service))
X break;
X
X if (! cur) /* service not there */
X exit(0);
X
X execve(cur->path, argv, envp);
X perror("execve");
X exit(0);
X}
X
Xchar *superv[] = { "/bin/sh", "-c", "supersrv", NULL };
X
Xstart_super(argv0, envp)
Xchar *argv0, **envp;
X{
X char *lastslash, argcopy[MAXPATHLEN], *rindex();
X
X strcpy(argcopy, argv0);
X/*
X * If a path was given, try to find the superserver in the
X * same directory as the subserver...
X */
X if (lastslash = rindex(argcopy, '/'))
X {
X char path[MAXPATHLEN];
X
X *lastslash = 0;
X strcpy(path, argcopy);
X strcat(path, "/supersrv");
X if (! access(path, X_OK))
X {
X if (! fork())
X execve(path, superv+2, envp);
X return;
X }
X }
X
X/*
X * Otherwise, start up a shell to scan along the user's
X * $PATH.
X */
X if (! fork())
X execve(superv[0], superv, envp);
X}
X
SHAR_EOF
chmod 0600 server.c || echo "restore of server.c fails"
sed 's/^X//' << 'SHAR_EOF' > socket.c &&
X/*
X** SOCKET.C
X**
X** Written by Steven Grimm (koreth@ssyx.ucsc.edu) on 11-26-87 (Thanksgiving)
X** Please distribute widely, but leave my name here.
X**
X** Various black-box routines for socket manipulation, so I don't have to
X** remember all the structure elements.
X** Of course, I still have to remember how to call these routines.
X*/
X
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <stdio.h>
X
X#ifndef FD_SET /* for 4.2BSD */
X#define FD_SETSIZE (sizeof(fd_set) * 8)
X#define FD_SET(n, p) (((fd_set *) (p))->fds_bits[0] |= (1 << ((n) % 32)))
X#define FD_CLR(n, p) (((fd_set *) (p))->fds_bits[0] &= ~(1 << ((n) % 32)))
X#define FD_ISSET(n, p) (((fd_set *) (p))->fds_bits[0] & (1 << ((n) % 32)))
X#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
X#endif
X
Xextern int errno;
X
X/*
X** serversock()
X**
X** Creates an internet socket, binds it to an address, and prepares it for
X** subsequent accept() calls by calling listen().
X**
X** Input: port number desired, or 0 for a random one
X** Output: file descriptor of socket, or a negative error
X*/
Xint serversock(port)
Xint port;
X{
X int sock, x;
X struct sockaddr_in server;
X
X sock = socket(AF_INET, SOCK_STREAM, 0);
X if (sock < 0)
X return -errno;
X
X bzero(&server, sizeof(server));
X server.sin_family = AF_INET;
X server.sin_addr.s_addr = INADDR_ANY;
X server.sin_port = htons(port);
X
X x = bind(sock, &server, sizeof(server));
X if (x < 0)
X {
X close(sock);
X return -errno;
X }
X
X listen(sock, 5);
X
X return sock;
X}
X
X/*
X** portnum()
X**
X** Returns the internet port number for a socket.
X**
X** Input: file descriptor of socket
X** Output: inet port number
X*/
Xint portnum(fd)
Xint fd;
X{
X int length, err;
X struct sockaddr_in address;
X
X length = sizeof(address);
X err = getsockname(fd, &address, &length);
X if (err < 0)
X return -errno;
X
X return ntohs(address.sin_port);
X}
X
X/*
X** clientsock()
X**
X** Returns a connected client socket.
X**
X** Input: host name and port number to connect to
X** Output: file descriptor of CONNECTED socket, or a negative error (-9999
X** if the hostname was bad).
X*/
Xint clientsock(host, port)
Xchar *host;
Xint port;
X{
X int sock;
X struct sockaddr_in server;
X struct hostent *hp, *gethostbyname();
X
X hp = gethostbyname(host);
X if (hp == NULL)
X return -9999;
X
X sock = socket(AF_INET, SOCK_STREAM, 0);
X if (sock < 0)
X return -errno;
X
X bzero(&server, sizeof(server));
X server.sin_family = AF_INET;
X bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
X server.sin_port = htons(port);
X
X if (connect(sock, &server, sizeof(server)) < 0)
X {
X close(sock);
X return -errno;
X }
X
X return sock;
X}
X
X/*
X** readable()
X**
X** Poll a socket for pending input. Returns immediately. This is a front-end
X** to waitread() below.
X**
X** Input: file descriptor to poll
X** Output: 1 if data is available for reading
X*/
Xreadable(fd)
Xint fd;
X{
X return(waitread(fd, 0));
X}
X
X/*
X** waitread()
X**
X** Wait for data on a file descriptor for a little while.
X**
X** Input: file descriptor to watch
X** how long to wait, in seconds, before returning
X** Output: 1 if data was available
X** 0 if the timer expired or a signal occurred.
X*/
Xwaitread(fd, time)
Xint fd, time;
X{
X fd_set readbits, other;
X struct timeval timer;
X int ret;
X
X timerclear(&timer);
X timer.tv_sec = time;
X FD_ZERO(&readbits);
X FD_ZERO(&other);
X FD_SET(fd, &readbits);
X
X ret = select(fd+1, &readbits, &other, &other, &timer);
X if (FD_ISSET(fd, &readbits))
X return 1;
X return 0;
X}
X
SHAR_EOF
chmod 0600 socket.c || echo "restore of socket.c fails"
sed 's/^X//' << 'SHAR_EOF' > supersrv.8 &&
X.TH SUPERSRV 8 "July 14, 1988"
X.SH NAME
Xsupersrv \- manage network services, version 1.2
X.SH SYNOPSIS
X.B supersrv
X.SH DESCRIPTION
X.I Supersrv
Xkeeps track of which services (see
X.IR server (1))
Xare offered on a host. It is automatically started by
X.I server
Xif it isn't already running; users should never have to run
X.I supersrv
Xexplicitly.
X.SH DISCUSSION
XThe superserver accepts connections from remote locations, usually
Xinitiated with the "telnet" program. It reads a line of input from the
Xclient program, which specifies the name of the desired service. If
Xone of the subservers has advertised the requested service, the
Xsuperserver forks off a child process. The child writes the name of
Xthe requested service to the appropriate subserver, then acts as a
Xmailman, shuffling bytes between the remote user and the subserver
Xuntil one of them disconnects. Meanwhile, the parent superserver
Xwaits for another connection, and the whole bloody mess starts over.
X.PP
XIf a superserver process is killed, all its subservers try to restart
Xit until one of them succeeds.
X.PP
X.I Supersrv
Xlistens on port number 3502; it's possible to request services via
Xthe
X.IR telnet (1)
Xprogram, though the
X.IR client (1)
Xinterface is preferred. The fields sent by
X.I client,
Xterminated by newlines, are:
X.PP
Xservice name or LIST
X.br
Xusername to request service from (blank for any)
X.br
Xcommand line arguments, one per line (optional)
X.br
Xterminating blank line
X.PP
XThus, a session could look like this:
X.PP
X%
X.B telnet ucsco.ucsc.edu 3502
X.br
XTrying...
X.br
XConnected to UCSCO.UCSC.EDU.
X.br
XSuperServer -- enter service desired.
X.br
X.B webster <newline>
X.br
X.B <newline>
X.br
X.B topiary <newline>
X.br
X.B <newline>
X.br
X.IP 1.
Xto.pi.ary \\'to--pe--.er-e-\\ aj [L topiarius, fr. topia ornamental
Xgardening, irreg. fr. Gk topo]s place : of, relating to, or being the
Xpractice or art of training, cutting, and trimming trees or shrubs into odd
Xor ornamental shapes; also : characterized by such work
X.IP 2.
Xtopiary n : topiary art or gardening; also : a topiary garden
X.PP
XConnection closed by foreign host.
X.PP
XNote:
X.I supersrv
Xshould be placed in a directory that is in users' search paths, as
X.I server
Xneeds to find it.
X.PP
XThree special service names,
X.BR LIST ,
X.BR QUIT ,
Xand
X.BR REGISTER ,
Xare reserved by
X.IR supersrv .
X.B LIST
Xlists the services currently being offered;
X.B REGISTER
Xis used by the
X.IR server (1)
Xprogram to register new services with
X.IR supersrv .
X.B QUIT
Xis passed from
X.I supersrv
Xto
X.I server
Xwhen another server is started by the same user, to tell the old one to die.
X.SH BUGS
XIt is possible for users to kill each others'
X.I server
Xprocesses by using the
X.B REGISTER
Xservice manually. It is hoped that anyone with the skill to do this will also
Xhave the maturity to refrain from doing it. This is probably naive.
X.SH "SEE ALSO"
Xserver(1), client(1)
X.SH AUTHOR
XSteven Grimm (koreth@ebay.sun.com, ...!sun!ebay!koreth)
SHAR_EOF
chmod 0600 supersrv.8 || echo "restore of supersrv.8 fails"
sed 's/^X//' << 'SHAR_EOF' > supersrv.c &&
X#include "common.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 */
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 int fd; /* File descriptor that offers it */
X struct service *next; /* Next service in list */
X} *list = (struct service *)0;
X
Xchar users[NOFILE][16]; /* user connected to each fd */
X
Xmain(argc, argv)
Xchar **argv;
X{
X struct itimerval it; /* Alarm! */
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 */
X fd_so = serversock(portno);
X if (fd_so < 0)
X {
X perror("serversock");
X exit(-1);
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 do {
X write(fd_co, WELCOME, sizeof(WELCOME)-1);
X } while (! getline(fd_co, request, sizeof(request)-1));
X
X if (handle(fd_co, request))
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 firstfd;
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, &other, &other, 0);
X if (FD_ISSET(old, &tread))
X break;
X while (firstfd = ffs(tread))
X {
X killfd(--firstfd);
X close(firstfd);
X FD_CLR(firstfd, &tread);
X FD_CLR(firstfd, &reed);
X }
X }
X return( accept(old, 0, 0) );
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];
X
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[20];
X int i;
X
X if (! getline(fd, users[fd], 15))
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 while (getline(fd, name, 19))
X {
X cur = (struct service *)malloc(sizeof(*cur));
X strcpy(cur->name, name);
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 if (!strcmp(string, "LIST"))
X {
X char buf[80];
X
X write(fd, "Username\tService\n--------\t-------\n", 34);
X
X for (cur = list; cur; cur = cur->next)
X {
X if (user[0] && strcmp(user, users[cur->fd]))
X continue;
X sprintf(buf, "%-8s\t%s\n", users[cur->fd], cur->name);
X write(fd, buf, strlen(buf));
X }
X return 1;
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 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.
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, zero = 0;
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
SHAR_EOF
chmod 0600 supersrv.c || echo "restore of supersrv.c fails"
exit 0