home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-06-29 | 32.9 KB | 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
-
-
-
-