BSD 4_3_Net_2 release
[unix-history] / usr / src / usr.sbin / lpr / lpd / lpd.c
index 39aa7df..edfda93 100644 (file)
@@ -1,4 +1,46 @@
-/*     lpd.c   4.4     83/06/02        */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)lpd.c      5.12 (Berkeley) 3/7/91";
+#endif /* not lint */
+
 /*
  * lpd -- line printer daemon.
  *
 /*
  * lpd -- line printer daemon.
  *
  *             return the current state of the queue (long form).
  *     \5printer person [users ...] [jobs ...]\n
  *             remove jobs from the queue.
  *             return the current state of the queue (long form).
  *     \5printer person [users ...] [jobs ...]\n
  *             remove jobs from the queue.
- *     \6printer\n
- *             enable queuing on the specified printer queue.
- *     \7printer\n
- *             disable queuing on the specified printer queue.
- *     \8printer\n
- *             return the queue status (queuing enabled or disabled).
  *
  * Strategy to maintain protected spooling area:
  *     1. Spooling area is writable only by daemon and spooling group
  *
  * Strategy to maintain protected spooling area:
  *     1. Spooling area is writable only by daemon and spooling group
  */
 
 #include "lp.h"
  */
 
 #include "lp.h"
+#include "pathnames.h"
 
 
-static int     lflag;                          /* log requests flag */
-static char    *logfile = DEFLOGF;
-static struct  sockaddr_in sin = { AF_INET };
+int    lflag;                          /* log requests flag */
+int    from_remote;                    /* from remote socket */
 
 
-int    reapchild();
-char   *ntoa();
+void mcleanup(), reapchild();
 
 main(argc, argv)
        int argc;
        char **argv;
 {
 
 main(argc, argv)
        int argc;
        char **argv;
 {
-       int f, options;
-       struct sockaddr_in fromaddr;
-       struct servent *sp;
+       int f, funix, finet, options = 0, defreadfds, fromlen;
+       struct sockaddr_un sun, fromunix;
+       struct sockaddr_in sin, frominet;
+       int omask, lfd;
 
        gethostname(host, sizeof(host));
        name = argv[0];
 
 
        gethostname(host, sizeof(host));
        name = argv[0];
 
-       sp = getservbyname("printer", "tcp");
-       if (sp == NULL) {
-               fprintf(stderr, "printer/tcp: unknown service");
-               exit(1);
-       }
-       sin.sin_port = sp->s_port;
-
        while (--argc > 0) {
                argv++;
                if (argv[0][0] == '-')
        while (--argc > 0) {
                argv++;
                if (argv[0][0] == '-')
@@ -71,92 +100,155 @@ main(argc, argv)
                        case 'l':
                                lflag++;
                                break;
                        case 'l':
                                lflag++;
                                break;
-                       case 'L':
-                               argc--;
-                               logfile = *++argv;
-                               break;
                        }
                        }
-               else {
-                       int port = atoi(argv[0]);
-                       int c = argv[0][0];
-
-                       if (c < '0' || c > '9' || port < 0) {
-                               fprintf(stderr, "lpd: %s: bad port number\n", argv[0]);
-                               exit(1);
-                       }
-                       sin.sin_port = htons((u_short) port);
-               }
        }
        }
+
 #ifndef DEBUG
        /*
         * Set up standard environment by detaching from the parent.
         */
 #ifndef DEBUG
        /*
         * Set up standard environment by detaching from the parent.
         */
-       if (fork())
-               exit(0);
-       for (f = 0; f < 3; f++)
-               (void) close(f);
-       (void) open("/dev/null", FRDONLY, 0);
-       (void) open("/dev/null", FWRONLY, 0);
-       (void) open(logfile, FWRONLY|FAPPEND, 0);
-       f = open("/dev/tty", FRDWR, 0);
-       if (f > 0) {
-               ioctl(f, TIOCNOTTY, 0);
-               (void) close(f);
-       }
+       daemon(0, 0);
 #endif
 #endif
+
+       openlog("lpd", LOG_PID, LOG_LPR);
        (void) umask(0);
        (void) umask(0);
+       lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
+       if (lfd < 0) {
+               syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+               exit(1);
+       }
+       if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
+               if (errno == EWOULDBLOCK)       /* active deamon present */
+                       exit(0);
+               syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+               exit(1);
+       }
+       ftruncate(lfd, 0);
+       /*
+        * write process id for others to know
+        */
+       sprintf(line, "%u\n", getpid());
+       f = strlen(line);
+       if (write(lfd, line, f) != f) {
+               syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+               exit(1);
+       }
+       signal(SIGCHLD, reapchild);
        /*
         * Restart all the printers.
         */
        startup();
        /*
         * Restart all the printers.
         */
        startup();
-       f = socket(AF_INET, SOCK_STREAM, 0);
-       if (f < 0) {
-               logerror("socket");
+       (void) unlink(_PATH_SOCKETNAME);
+       funix = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (funix < 0) {
+               syslog(LOG_ERR, "socket: %m");
                exit(1);
        }
                exit(1);
        }
-       if (options & SO_DEBUG)
-               if (setsockopt(f, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
-                       logerror("setsockopt (SO_DEBUG)");
-                       exit(1);
-               }
-       if (bind(f, &sin, sizeof(sin), 0) < 0) {
-               logerror("bind");
+#define        mask(s) (1 << ((s) - 1))
+       omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
+       signal(SIGHUP, mcleanup);
+       signal(SIGINT, mcleanup);
+       signal(SIGQUIT, mcleanup);
+       signal(SIGTERM, mcleanup);
+       sun.sun_family = AF_UNIX;
+       strcpy(sun.sun_path, _PATH_SOCKETNAME);
+       if (bind(funix,
+            (struct sockaddr *)&sun, strlen(sun.sun_path) + 2) < 0) {
+               syslog(LOG_ERR, "ubind: %m");
                exit(1);
        }
                exit(1);
        }
+       sigsetmask(omask);
+       defreadfds = 1 << funix;
+       listen(funix, 5);
+       finet = socket(AF_INET, SOCK_STREAM, 0);
+       if (finet >= 0) {
+               struct servent *sp;
+
+               if (options & SO_DEBUG)
+                       if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
+                               syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+                               mcleanup();
+                       }
+               sp = getservbyname("printer", "tcp");
+               if (sp == NULL) {
+                       syslog(LOG_ERR, "printer/tcp: unknown service");
+                       mcleanup();
+               }
+               sin.sin_family = AF_INET;
+               sin.sin_port = sp->s_port;
+               if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+                       syslog(LOG_ERR, "bind: %m");
+                       mcleanup();
+               }
+               defreadfds |= 1 << finet;
+               listen(finet, 5);
+       }
        /*
        /*
-        * Main loop: listen, accept, do a request, continue.
+        * Main loop: accept, do a request, continue.
         */
         */
-       sigset(SIGCHLD, reapchild);
-       listen(f, 10);
        for (;;) {
        for (;;) {
-               int s, len = sizeof(fromaddr);
+               int domain, nfds, s, readfds = defreadfds;
 
 
-               s = accept(f, &fromaddr, &len, 0);
+               nfds = select(20, &readfds, 0, 0, 0);
+               if (nfds <= 0) {
+                       if (nfds < 0 && errno != EINTR)
+                               syslog(LOG_WARNING, "select: %m");
+                       continue;
+               }
+               if (readfds & (1 << funix)) {
+                       domain = AF_UNIX, fromlen = sizeof(fromunix);
+                       s = accept(funix,
+                           (struct sockaddr *)&fromunix, &fromlen);
+               } else if (readfds & (1 << finet)) {
+                       domain = AF_INET, fromlen = sizeof(frominet);
+                       s = accept(finet,
+                           (struct sockaddr *)&frominet, &fromlen);
+               }
                if (s < 0) {
                if (s < 0) {
-                       if (errno == EINTR)
-                               continue;
-                       logerror("accept");
-                       exit(1);
+                       if (errno != EINTR)
+                               syslog(LOG_WARNING, "accept: %m");
+                       continue;
                }
                if (fork() == 0) {
                }
                if (fork() == 0) {
-                       sigset(SIGCHLD, SIG_IGN);
-                       (void) close(f);
-                       doit(s, &fromaddr);
+                       signal(SIGCHLD, SIG_IGN);
+                       signal(SIGHUP, SIG_IGN);
+                       signal(SIGINT, SIG_IGN);
+                       signal(SIGQUIT, SIG_IGN);
+                       signal(SIGTERM, SIG_IGN);
+                       (void) close(funix);
+                       (void) close(finet);
+                       dup2(s, 1);
+                       (void) close(s);
+                       if (domain == AF_INET) {
+                               from_remote = 1;
+                               chkhost(&frominet);
+                       } else
+                               from_remote = 0;
+                       doit();
                        exit(0);
                }
                (void) close(s);
        }
 }
 
                        exit(0);
                }
                (void) close(s);
        }
 }
 
-static
+void
 reapchild()
 {
        union wait status;
 
 reapchild()
 {
        union wait status;
 
-       while (wait3(&status, WNOHANG, 0) > 0)
+       while (wait3((int *)&status, WNOHANG, 0) > 0)
                ;
 }
 
                ;
 }
 
+void
+mcleanup()
+{
+       if (lflag)
+               syslog(LOG_INFO, "exiting");
+       unlink(_PATH_SOCKETNAME);
+       exit(0);
+}
+
 /*
  * Stuff for handling job specifications
  */
 /*
  * Stuff for handling job specifications
  */
@@ -166,9 +258,9 @@ int requ[MAXREQUESTS];      /* job number of spool entries */
 int    requests;               /* # of spool requests */
 char   *person;                /* name of person doing lprm */
 
 int    requests;               /* # of spool requests */
 char   *person;                /* name of person doing lprm */
 
-static char    fromb[32];      /* buffer for client's machine name */
-static char    cbuf[BUFSIZ];   /* command line buffer */
-static char    *cmdnames[] = {
+char   fromb[32];      /* buffer for client's machine name */
+char   cbuf[BUFSIZ];   /* command line buffer */
+char   *cmdnames[] = {
        "null",
        "printjob",
        "recvjob",
        "null",
        "printjob",
        "recvjob",
@@ -177,48 +269,31 @@ static char       *cmdnames[] = {
        "rmjob"
 };
 
        "rmjob"
 };
 
-static
-doit(f, fromaddr)
-       int f;
-       struct sockaddr_in *fromaddr;
+doit()
 {
        register char *cp;
 {
        register char *cp;
-       register struct hostent *hp;
        register int n;
        register int n;
-       char c;
 
 
-       dup2(f, 1);
-       (void) close(f);
-       f = 1;
-       fromaddr->sin_port = ntohs(fromaddr->sin_port);
-       if (fromaddr->sin_family != AF_INET || fromaddr->sin_port >= IPPORT_RESERVED)
-               fatal("Malformed from address");
-       hp = gethostbyaddr(&fromaddr->sin_addr, sizeof(struct in_addr),
-               fromaddr->sin_family);
-       if (hp == 0)
-               fatal("Host name for your address (%s) unknown",
-                       ntoa(fromaddr->sin_addr));
-       strcpy(fromb, hp->h_name);
-       from = fromb;
-       if (chkhost())
-               fatal("Your host does not have line printer access");
        for (;;) {
                cp = cbuf;
                do {
        for (;;) {
                cp = cbuf;
                do {
-                       if ((n = read(f, &c, 1)) != 1) {
+                       if (cp >= &cbuf[sizeof(cbuf) - 1])
+                               fatal("Command line too long");
+                       if ((n = read(1, cp, 1)) != 1) {
                                if (n < 0)
                                        fatal("Lost connection");
                                return;
                        }
                                if (n < 0)
                                        fatal("Lost connection");
                                return;
                        }
-                       if (cp >= &cbuf[sizeof(cbuf)])
-                               fatal("Command line too long");
-                       *cp++ = c;
-               } while (c != '\n');
+               } while (*cp++ != '\n');
                *--cp = '\0';
                cp = cbuf;
                *--cp = '\0';
                cp = cbuf;
-               if (lflag && *cp >= '\1' && *cp <= '\5') {
-                       printer = NULL;
-                       log("%s requests %s %s", from, cmdnames[*cp], cp+1);
+               if (lflag) {
+                       if (*cp >= '\1' && *cp <= '\5')
+                               syslog(LOG_INFO, "%s requests %s %s",
+                                       from, cmdnames[*cp], cp+1);
+                       else
+                               syslog(LOG_INFO, "bad request (%d) from %s",
+                                       *cp, from);
                }
                switch (*cp++) {
                case '\1':      /* check the queue and print any jobs there */
                }
                switch (*cp++) {
                case '\1':      /* check the queue and print any jobs there */
@@ -226,6 +301,10 @@ doit(f, fromaddr)
                        printjob();
                        break;
                case '\2':      /* receive files to be queued */
                        printjob();
                        break;
                case '\2':      /* receive files to be queued */
+                       if (!from_remote) {
+                               syslog(LOG_INFO, "illegal request (%d)", *cp);
+                               exit(1);
+                       }
                        printer = cp;
                        recvjob();
                        break;
                        printer = cp;
                        recvjob();
                        break;
@@ -255,6 +334,10 @@ doit(f, fromaddr)
                        displayq(cbuf[0] - '\3');
                        exit(0);
                case '\5':      /* remove a job from the queue */
                        displayq(cbuf[0] - '\3');
                        exit(0);
                case '\5':      /* remove a job from the queue */
+                       if (!from_remote) {
+                               syslog(LOG_INFO, "illegal request (%d)", *cp);
+                               exit(1);
+                       }
                        printer = cp;
                        while (*cp && *cp != ' ')
                                cp++;
                        printer = cp;
                        while (*cp && *cp != ' ')
                                cp++;
@@ -286,7 +369,6 @@ doit(f, fromaddr)
                        break;
                }
                fatal("Illegal service request");
                        break;
                }
                fatal("Illegal service request");
-               exit(1);
        }
 }
 
        }
 }
 
@@ -294,7 +376,6 @@ doit(f, fromaddr)
  * Make a pass through the printcap database and start printing any
  * files left from the last time the machine went down.
  */
  * Make a pass through the printcap database and start printing any
  * files left from the last time the machine went down.
  */
-static
 startup()
 {
        char buf[BUFSIZ];
 startup()
 {
        char buf[BUFSIZ];
@@ -313,8 +394,8 @@ startup()
                                break;
                        }
                if ((pid = fork()) < 0) {
                                break;
                        }
                if ((pid = fork()) < 0) {
-                       log("startup: cannot fork");
-                       exit(1);
+                       syslog(LOG_WARNING, "startup: cannot fork");
+                       mcleanup();
                }
                if (!pid) {
                        endprent();
                }
                if (!pid) {
                        endprent();
@@ -323,81 +404,61 @@ startup()
        }
 }
 
        }
 }
 
+#define DUMMY ":nobody::"
+
 /*
  * Check to see if the from host has access to the line printer.
  */
 /*
  * Check to see if the from host has access to the line printer.
  */
-static
-chkhost()
+chkhost(f)
+       struct sockaddr_in *f;
 {
 {
+       register struct hostent *hp;
        register FILE *hostf;
        register FILE *hostf;
-       register char *cp;
+       register char *cp, *sp;
        char ahost[50];
        char ahost[50];
+       int first = 1;
+       extern char *inet_ntoa();
+       int baselen = -1;
+
+       f->sin_port = ntohs(f->sin_port);
+       if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
+               fatal("Malformed from address");
+       hp = gethostbyaddr((char *)&f->sin_addr,
+           sizeof(struct in_addr), f->sin_family);
+       if (hp == 0)
+               fatal("Host name for your address (%s) unknown",
+                       inet_ntoa(f->sin_addr));
 
 
+       strcpy(fromb, hp->h_name);
+       from = fromb;
        if (!strcmp(from, host))
        if (!strcmp(from, host))
-               return(0);
+               return;
 
 
-       hostf = fopen("/etc/hosts.equiv", "r");
-       while (fgets(ahost, sizeof(ahost), hostf)) {
-               if (cp = index(ahost, '\n'))
-                       *cp = '\0';
-               cp = index(ahost, ' ');
-               if (!strcmp(from, ahost) && cp == NULL) {
+       sp = fromb;
+       cp = ahost;
+       while (*sp) {
+               if (*sp == '.') {
+                       if (baselen == -1)
+                               baselen = sp - fromb;
+                       *cp++ = *sp++;
+               } else {
+                       *cp++ = isupper(*sp) ? tolower(*sp++) : *sp++;
+               }
+       }
+       *cp = '\0';
+       hostf = fopen(_PATH_HOSTSEQUIV, "r");
+again:
+       if (hostf) {
+               if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) {
                        (void) fclose(hostf);
                        (void) fclose(hostf);
-                       return(0);
+                       return;
                }
                }
+               (void) fclose(hostf);
        }
        }
-       (void) fclose(hostf);
-       return(-1);
-}
-
-/*
- * Convert network-format internet address
- * to base 256 d.d.d.d representation.
- */
-static char *
-ntoa(in)
-       struct in_addr in;
-{
-       static char b[18];
-       register char *p;
-
-       p = (char *)&in;
-#define        UC(b)   (((int)b)&0xff)
-       sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
-       return (b);
-}
-
-/*VARARGS1*/
-log(msg, a1, a2, a3)
-       char *msg;
-{
-       short console = isatty(fileno(stderr));
-
-       fprintf(stderr, console ? "\r\n%s: " : "%s: ", name);
-       if (printer)
-               fprintf(stderr, "%s: ", printer);
-       fprintf(stderr, msg, a1, a2, a3);
-       if (console)
-               putc('\r', stderr);
-       putc('\n', stderr);
-       fflush(stderr);
-}
-
-static
-logerror(msg)
-       char *msg;
-{
-       register int err = errno;
-       short console = isatty(fileno(stderr));
-       extern int sys_nerr;
-       extern char *sys_errlist[];
-
-       fprintf(stderr, console ? "\r\n%s: " : "%s: ", name);
-       if (msg)
-               fprintf(stderr, "%s: ", msg);
-       fputs(err < sys_nerr ? sys_errlist[err] : "Unknown error" , stderr);
-       if (console)
-               putc('\r', stderr);
-       putc('\n', stderr);
-       fflush(stderr);
+       if (first == 1) {
+               first = 0;
+               hostf = fopen(_PATH_HOSTSLPD, "r");
+               goto again;
+       }
+       fatal("Your host does not have line printer access");
 }
 }