home *** CD-ROM | disk | FTP | other *** search
- /* Public domain. */
-
- #include <stdio.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/file.h>
- #include <sys/wait.h>
- #include <sys/time.h>
- #include "dupdup.h"
- #include "malloc.h"
- #include "errno.h"
- #include "username.h"
- #include "env.h"
- #include "getopt.h"
- #include "numfiles.h"
-
- #ifndef O_RDONLY
- #define O_RDONLY 0
- #endif
- #ifndef O_WRONLY
- #define O_WRONLY 1
- #endif
-
- static int flagprintlocal = 0;
- static int flagverbose = 1;
- static char fnserv[200];
- static char fnlock[200];
- static char fnexcl[200];
- static int fdserv;
- static int fdlock;
- static int fdexcl;
- static int flagbindok = 0;
-
- unlinkem()
- {
- if (flagbindok)
- {
- unlink(fnserv);
- unlink(fnlock);
- unlink(fnexcl);
- }
- }
-
- SIGRET sigterm()
- {
- unlinkem();
- exit(0);
- }
-
- SIGRET sigchld()
- {
- wait((int *) 0); /*XXX*/
- }
-
- static char errbuf[500];
- void die(n,s) int n; char *s;
- { if (flagverbose) fprintf(stderr,"npipeserver: fatal: %s\n",s); exit(n); }
- void dies(n,s,t) int n; char *s; char *t;
- { if (flagverbose) fprintf(stderr,"npipeserver: fatal: %s%s\n",s,t); exit(n); }
- void diep(n,s) int n; char *s;
- { if (flagverbose) sprintf(errbuf,"npipeserver: fatal: %s",s); perror(errbuf); exit(n); }
- void warnp(s) char *s;
- { if (flagverbose) sprintf(errbuf,"npipeserver: warning: %s",s); perror(errbuf); }
- void warnsp(s,t) char *s; char *t;
- { if (flagverbose) sprintf(errbuf,"npipeserver: warning: %s%s",s,t); perror(errbuf); }
-
- main(argc,argv,envp)
- int argc;
- char *argv[];
- char *envp[];
- {
- int *d;
- int numfiles;
- int uid;
- int arguid;
- int i;
- int r;
- int n;
- char fns2c[200];
- char fnc2s[200];
- char buf[100];
- int pid;
- int fds2c;
- int fdc2s;
- char npipelocal[200]; /*XXX*/
- char npiperemote[300]; /*XXX*/
- int opt;
-
- while ((opt = getopt(argc,argv,"qQ4")) != EOF)
- switch(opt)
- {
- case 'q':
- flagverbose = 0;
- break;
- case 'Q':
- flagverbose = 1;
- break;
- case '4':
- flagprintlocal = 1;
- break;
- case '?':
- break; /*XXX*/
- default:
- break; /*XXX*/
- }
- argc -= optind; argv += optind;
-
- if (argc < 3)
- die(1,"need at least three arguments");
-
- uid = getuid();
- if (username2uid(argv[0],&arguid) == -1)
- dies(2,"cannot figure out username ",argv[0]);
- if (uid && (uid != arguid))
- dies(3,"permission denied for username ",argv[0]);
-
- for (i = 0;argv[0][i];++i)
- if ((argv[0][i] == '/') || (argv[0][i] == ':') || (argv[0][i] == '.') || (argv[0][i] == '\n'))
- dies(4,"illegal characters in username ",argv[0]);
-
- for (i = 0;argv[1][i];++i)
- if ((argv[1][i] == '/') || (argv[1][i] == '.') || (argv[0][i] == '\n'))
- dies(5,"illegal characters in service ",argv[1]);
-
- close(6);
- close(7); /* XXX: these have implications that should be documented */
-
- numfiles = NUMFILES;
- d = (int *) malloc(numfiles * sizeof(int));
- if (!d)
- die(6,"cannot malloc");
- for (i = 0;i < numfiles;++i)
- d[i] = -1;
-
- if (strlen(NPIPEDIR) + strlen(argv[0]) + strlen(argv[1]) + 1 > 90) /*XXX*/
- die(8,"server filenames too long");
- sprintf(fnserv,"%s/%ds%s",NPIPEDIR,arguid,argv[1]);
- sprintf(fnlock,"%s/%dl%s",NPIPEDIR,arguid,argv[1]);
- sprintf(fnexcl,"%s/%de%s",NPIPEDIR,arguid,argv[1]);
- /* XXX: Even if arguid has 5 digits, this leaves 8 characters for
- the service in System V filesystems. It might be worth switching
- to a fixed-length or hex format... I dunno. */
-
- signal(SIGTERM,sigterm);
- signal(SIGINT,sigterm);
- signal(SIGCHLD,sigchld);
- signal(SIGPIPE,SIG_IGN);
-
- if (env_init() == -1)
- die(11,"cannot init environment");
- if (env_put("PROTO=NPIPE") == -1)
- die(11,"out of memory putting PROTO into environment");
- sprintf(npipelocal,"NPIPELOCAL=%s:%s",argv[0],argv[1]);
- if (env_put(npipelocal) == -1)
- die(11,"out of memory putting NPIPELOCAL into environment");
-
- if (flagprintlocal)
- {
- FILE *fd4;
- fd4 = fdopen(4,"w");
- if (!fd4)
- die(1,"cannot print NPIPELOCAL on fd 4");
- fprintf(fd4,"%s:%s\n",argv[0],argv[1]);
- fclose(fd4);
- close(4); /* just in case */
- }
-
- /* Action! */
-
- fdexcl = creat(fnexcl,0);
- if (fdexcl == -1) /* somebody else got here before us */
- die(1,"service already in use");
- /* XXX: there's a race right here */
- flagbindok = 1;
- fdlock = creat(fnlock,0); /* refuse connections until we're ready */
- if (fdlock == -1)
- diep(1,"cannot create lock file");
- if (write(fdlock,buf,1) < 1)
- diep(1,"cannot write to lock file");
- if (mknod(fnserv,010600,0) == -1)
- diep(1,"cannot create server's named pipe");
- if (chmod(fnlock,0600) == -1) /* gaargh. fchmod(fdlock,0600)! */
- diep(1,"cannot set modes on lock file");
-
- /* I hate programming like this. In a real OS I'd just create the named
- pipe---outside the directory hierarchy!---then try to link it in.
- None of this stupid testing and locking and so on. Gaargh. */
-
- for (;;)
- {
- /* XXX: should we really die on simple I/O errors? */
- for (;;)
- {
- fdserv = open(fnserv,O_RDONLY,0); /* this blocks */
- /* XXX: don't accept too many requests at once? */
- if (fdserv == -1) /* down the drain */
- {
- if (errno == EINTR)
- continue;
- unlinkem();
- diep(1,"cannot open server's named pipe");
- }
- break;
- }
- n = 0;
- r = 0;
- while ((n < sizeof(buf)) && ((r = read(fdserv,buf + n,sizeof(buf) - n)) > 0))
- n += r;
- if (r > 0)
- ; /* overflowed buffer---shouldn't happen, we'll just truncate it */
- if (r == -1)
- {
- unlinkem();
- diep(1,"cannot read client's pid and username");
- }
- /* XXX: do something for killer? */
- close(fdserv);
- unlink(fnserv); /*XXX: check for errors?*/
- for (i = 0;i < n;++i)
- if (!buf[i])
- break;
- if (i == n)
- {
- unlinkem();
- die(1,"cannot read remote username");
- }
- /* XXX: if i is n - 1, we have a null username */
- ++i; /* now buf + i is the username */
- if (sscanf(buf,"%d",&pid) < 1)
- {
- unlinkem();
- die(1,"cannot read remote pid");
- }
- if (!pid) /* npipekiller */
- sigterm(); /*XXX*/
-
- switch(fork())
- {
- case -1:
- warnp("cannot fork");
- /* XXX: say anything to other side? */
- break;
-
- case 0:
-
- close(fdlock);
- close(fdexcl);
-
- sprintf(fns2c,"%s/s2c.%d",NPIPEDIR,pid);
- sprintf(fnc2s,"%s/c2s.%d",NPIPEDIR,pid);
-
- fds2c = open(fns2c,O_WRONLY,0); /* blocks */
- if (fds2c == -1)
- diep(1,"cannot open server-to-client pipe");
- fdc2s = open(fnc2s,O_RDONLY,0); /* blocks */
- if (fdc2s == -1)
- diep(1,"cannot open client-to-server pipe");
-
- if (write(fds2c,buf,1) < 1)
- diep(1,"cannot write on server-to-client pipe");
-
- sprintf(npiperemote,"NPIPEREMOTE=%s",buf + i);
- if (env_put(npiperemote) == -1)
- die(1,"out of memory putting NPIPEREMOTE into environment");
-
- /* Now put fdc2s into descriptor 6 and fds2c into descriptor 7. */
- /* XXX: should check that fdc2s and fds2c are small enough */
- d[fdc2s] = 6;
- d[fds2c] = 7;
- if (dupdup(d,numfiles) == -1)
- diep(1,"cannot dup connection to client");
-
- if (setreuid(uid,uid) == -1)
- diep(1,"cannot setreuid"); /* will never happen */
- signal(SIGPIPE,SIG_DFL);
- execvp(argv[2],argv + 2);
- warnsp("cannot exec ",argv[2]);
- exit(1);
-
- default:
- ;
- }
- /* the cycle begins anew */
- if (mknod(fnserv,010600,0) == -1)
- {
- unlinkem();
- diep(1,"cannot create server's named pipe");
- }
- }
- }
-