home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.sbin / lpr / lpd / lpd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-19  |  11.1 KB  |  465 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. char copyright[] =
  36. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  37.  All rights reserved.\n";
  38. #endif /* not lint */
  39.  
  40. #ifndef lint
  41. static char sccsid[] = "@(#)lpd.c    5.12 (Berkeley) 3/7/91";
  42. #endif /* not lint */
  43.  
  44. /*
  45.  * lpd -- line printer daemon.
  46.  *
  47.  * Listen for a connection and perform the requested operation.
  48.  * Operations are:
  49.  *    \1printer\n
  50.  *        check the queue for jobs and print any found.
  51.  *    \2printer\n
  52.  *        receive a job from another machine and queue it.
  53.  *    \3printer [users ...] [jobs ...]\n
  54.  *        return the current state of the queue (short form).
  55.  *    \4printer [users ...] [jobs ...]\n
  56.  *        return the current state of the queue (long form).
  57.  *    \5printer person [users ...] [jobs ...]\n
  58.  *        remove jobs from the queue.
  59.  *
  60.  * Strategy to maintain protected spooling area:
  61.  *    1. Spooling area is writable only by daemon and spooling group
  62.  *    2. lpr runs setuid root and setgrp spooling group; it uses
  63.  *       root to access any file it wants (verifying things before
  64.  *       with an access call) and group id to know how it should
  65.  *       set up ownership of files in the spooling area.
  66.  *    3. Files in spooling area are owned by root, group spooling
  67.  *       group, with mode 660.
  68.  *    4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
  69.  *       access files and printer.  Users can't get to anything
  70.  *       w/o help of lpq and lprm programs.
  71.  */
  72.  
  73. #include "lp.h"
  74. #include "pathnames.h"
  75.  
  76. int    lflag;                /* log requests flag */
  77. int    from_remote;            /* from remote socket */
  78.  
  79. void mcleanup(), reapchild();
  80.  
  81. main(argc, argv)
  82.     int argc;
  83.     char **argv;
  84. {
  85.     int f, funix, finet, options = 0, defreadfds, fromlen;
  86.     struct sockaddr_un sun, fromunix;
  87.     struct sockaddr_in sin, frominet;
  88.     int omask, lfd;
  89.  
  90.     gethostname(host, sizeof(host));
  91.     name = argv[0];
  92.  
  93.     while (--argc > 0) {
  94.         argv++;
  95.         if (argv[0][0] == '-')
  96.             switch (argv[0][1]) {
  97.             case 'd':
  98.                 options |= SO_DEBUG;
  99.                 break;
  100.             case 'l':
  101.                 lflag++;
  102.                 break;
  103.             }
  104.     }
  105.  
  106. #ifndef DEBUG
  107.     /*
  108.      * Set up standard environment by detaching from the parent.
  109.      */
  110.     daemon(0, 0);
  111. #endif
  112.  
  113.     openlog("lpd", LOG_PID, LOG_LPR);
  114.     (void) umask(0);
  115.     lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
  116.     if (lfd < 0) {
  117.         syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
  118.         exit(1);
  119.     }
  120.     if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
  121.         if (errno == EWOULDBLOCK)    /* active deamon present */
  122.             exit(0);
  123.         syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
  124.         exit(1);
  125.     }
  126.     ftruncate(lfd, 0);
  127.     /*
  128.      * write process id for others to know
  129.      */
  130.     sprintf(line, "%u\n", getpid());
  131.     f = strlen(line);
  132.     if (write(lfd, line, f) != f) {
  133.         syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
  134.         exit(1);
  135.     }
  136.     signal(SIGCHLD, reapchild);
  137.     /*
  138.      * Restart all the printers.
  139.      */
  140.     startup();
  141.     (void) unlink(_PATH_SOCKETNAME);
  142.     funix = socket(AF_UNIX, SOCK_STREAM, 0);
  143.     if (funix < 0) {
  144.         syslog(LOG_ERR, "socket: %m");
  145.         exit(1);
  146.     }
  147. #define    mask(s)    (1 << ((s) - 1))
  148.     omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
  149.     signal(SIGHUP, mcleanup);
  150.     signal(SIGINT, mcleanup);
  151.     signal(SIGQUIT, mcleanup);
  152.     signal(SIGTERM, mcleanup);
  153.     sun.sun_family = AF_UNIX;
  154.     strcpy(sun.sun_path, _PATH_SOCKETNAME);
  155.     if (bind(funix,
  156.          (struct sockaddr *)&sun, strlen(sun.sun_path) + 2) < 0) {
  157.         syslog(LOG_ERR, "ubind: %m");
  158.         exit(1);
  159.     }
  160.     sigsetmask(omask);
  161.     defreadfds = 1 << funix;
  162.     listen(funix, 5);
  163.     finet = socket(AF_INET, SOCK_STREAM, 0);
  164.     if (finet >= 0) {
  165.         struct servent *sp;
  166.  
  167.         if (options & SO_DEBUG)
  168.             if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
  169.                 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
  170.                 mcleanup();
  171.             }
  172.         sp = getservbyname("printer", "tcp");
  173.         if (sp == NULL) {
  174.             syslog(LOG_ERR, "printer/tcp: unknown service");
  175.             mcleanup();
  176.         }
  177.         sin.sin_family = AF_INET;
  178.         sin.sin_port = sp->s_port;
  179.         if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
  180.             syslog(LOG_ERR, "bind: %m");
  181.             mcleanup();
  182.         }
  183.         defreadfds |= 1 << finet;
  184.         listen(finet, 5);
  185.     }
  186.     /*
  187.      * Main loop: accept, do a request, continue.
  188.      */
  189.     for (;;) {
  190.         int domain, nfds, s, readfds = defreadfds;
  191.  
  192.         nfds = select(20, &readfds, 0, 0, 0);
  193.         if (nfds <= 0) {
  194.             if (nfds < 0 && errno != EINTR)
  195.                 syslog(LOG_WARNING, "select: %m");
  196.             continue;
  197.         }
  198.         if (readfds & (1 << funix)) {
  199.             domain = AF_UNIX, fromlen = sizeof(fromunix);
  200.             s = accept(funix,
  201.                 (struct sockaddr *)&fromunix, &fromlen);
  202.         } else if (readfds & (1 << finet)) {
  203.             domain = AF_INET, fromlen = sizeof(frominet);
  204.             s = accept(finet,
  205.                 (struct sockaddr *)&frominet, &fromlen);
  206.         }
  207.         if (s < 0) {
  208.             if (errno != EINTR)
  209.                 syslog(LOG_WARNING, "accept: %m");
  210.             continue;
  211.         }
  212.         if (fork() == 0) {
  213.             signal(SIGCHLD, SIG_IGN);
  214.             signal(SIGHUP, SIG_IGN);
  215.             signal(SIGINT, SIG_IGN);
  216.             signal(SIGQUIT, SIG_IGN);
  217.             signal(SIGTERM, SIG_IGN);
  218.             (void) close(funix);
  219.             (void) close(finet);
  220.             dup2(s, 1);
  221.             (void) close(s);
  222.             if (domain == AF_INET) {
  223.                 from_remote = 1;
  224.                 chkhost(&frominet);
  225.             } else
  226.                 from_remote = 0;
  227.             doit();
  228.             exit(0);
  229.         }
  230.         (void) close(s);
  231.     }
  232. }
  233.  
  234. void
  235. reapchild()
  236. {
  237.     union wait status;
  238.  
  239.     while (wait3((int *)&status, WNOHANG, 0) > 0)
  240.         ;
  241. }
  242.  
  243. void
  244. mcleanup()
  245. {
  246.     if (lflag)
  247.         syslog(LOG_INFO, "exiting");
  248.     unlink(_PATH_SOCKETNAME);
  249.     exit(0);
  250. }
  251.  
  252. /*
  253.  * Stuff for handling job specifications
  254.  */
  255. char    *user[MAXUSERS];    /* users to process */
  256. int    users;            /* # of users in user array */
  257. int    requ[MAXREQUESTS];    /* job number of spool entries */
  258. int    requests;        /* # of spool requests */
  259. char    *person;        /* name of person doing lprm */
  260.  
  261. char    fromb[32];    /* buffer for client's machine name */
  262. char    cbuf[BUFSIZ];    /* command line buffer */
  263. char    *cmdnames[] = {
  264.     "null",
  265.     "printjob",
  266.     "recvjob",
  267.     "displayq short",
  268.     "displayq long",
  269.     "rmjob"
  270. };
  271.  
  272. doit()
  273. {
  274.     register char *cp;
  275.     register int n;
  276.  
  277.     for (;;) {
  278.         cp = cbuf;
  279.         do {
  280.             if (cp >= &cbuf[sizeof(cbuf) - 1])
  281.                 fatal("Command line too long");
  282.             if ((n = read(1, cp, 1)) != 1) {
  283.                 if (n < 0)
  284.                     fatal("Lost connection");
  285.                 return;
  286.             }
  287.         } while (*cp++ != '\n');
  288.         *--cp = '\0';
  289.         cp = cbuf;
  290.         if (lflag) {
  291.             if (*cp >= '\1' && *cp <= '\5')
  292.                 syslog(LOG_INFO, "%s requests %s %s",
  293.                     from, cmdnames[*cp], cp+1);
  294.             else
  295.                 syslog(LOG_INFO, "bad request (%d) from %s",
  296.                     *cp, from);
  297.         }
  298.         switch (*cp++) {
  299.         case '\1':    /* check the queue and print any jobs there */
  300.             printer = cp;
  301.             printjob();
  302.             break;
  303.         case '\2':    /* receive files to be queued */
  304.             if (!from_remote) {
  305.                 syslog(LOG_INFO, "illegal request (%d)", *cp);
  306.                 exit(1);
  307.             }
  308.             printer = cp;
  309.             recvjob();
  310.             break;
  311.         case '\3':    /* display the queue (short form) */
  312.         case '\4':    /* display the queue (long form) */
  313.             printer = cp;
  314.             while (*cp) {
  315.                 if (*cp != ' ') {
  316.                     cp++;
  317.                     continue;
  318.                 }
  319.                 *cp++ = '\0';
  320.                 while (isspace(*cp))
  321.                     cp++;
  322.                 if (*cp == '\0')
  323.                     break;
  324.                 if (isdigit(*cp)) {
  325.                     if (requests >= MAXREQUESTS)
  326.                         fatal("Too many requests");
  327.                     requ[requests++] = atoi(cp);
  328.                 } else {
  329.                     if (users >= MAXUSERS)
  330.                         fatal("Too many users");
  331.                     user[users++] = cp;
  332.                 }
  333.             }
  334.             displayq(cbuf[0] - '\3');
  335.             exit(0);
  336.         case '\5':    /* remove a job from the queue */
  337.             if (!from_remote) {
  338.                 syslog(LOG_INFO, "illegal request (%d)", *cp);
  339.                 exit(1);
  340.             }
  341.             printer = cp;
  342.             while (*cp && *cp != ' ')
  343.                 cp++;
  344.             if (!*cp)
  345.                 break;
  346.             *cp++ = '\0';
  347.             person = cp;
  348.             while (*cp) {
  349.                 if (*cp != ' ') {
  350.                     cp++;
  351.                     continue;
  352.                 }
  353.                 *cp++ = '\0';
  354.                 while (isspace(*cp))
  355.                     cp++;
  356.                 if (*cp == '\0')
  357.                     break;
  358.                 if (isdigit(*cp)) {
  359.                     if (requests >= MAXREQUESTS)
  360.                         fatal("Too many requests");
  361.                     requ[requests++] = atoi(cp);
  362.                 } else {
  363.                     if (users >= MAXUSERS)
  364.                         fatal("Too many users");
  365.                     user[users++] = cp;
  366.                 }
  367.             }
  368.             rmjob();
  369.             break;
  370.         }
  371.         fatal("Illegal service request");
  372.     }
  373. }
  374.  
  375. /*
  376.  * Make a pass through the printcap database and start printing any
  377.  * files left from the last time the machine went down.
  378.  */
  379. startup()
  380. {
  381.     char buf[BUFSIZ];
  382.     register char *cp;
  383.     int pid;
  384.  
  385.     printer = buf;
  386.  
  387.     /*
  388.      * Restart the daemons.
  389.      */
  390.     while (getprent(buf) > 0) {
  391.         for (cp = buf; *cp; cp++)
  392.             if (*cp == '|' || *cp == ':') {
  393.                 *cp = '\0';
  394.                 break;
  395.             }
  396.         if ((pid = fork()) < 0) {
  397.             syslog(LOG_WARNING, "startup: cannot fork");
  398.             mcleanup();
  399.         }
  400.         if (!pid) {
  401.             endprent();
  402.             printjob();
  403.         }
  404.     }
  405. }
  406.  
  407. #define DUMMY ":nobody::"
  408.  
  409. /*
  410.  * Check to see if the from host has access to the line printer.
  411.  */
  412. chkhost(f)
  413.     struct sockaddr_in *f;
  414. {
  415.     register struct hostent *hp;
  416.     register FILE *hostf;
  417.     register char *cp, *sp;
  418.     char ahost[50];
  419.     int first = 1;
  420.     extern char *inet_ntoa();
  421.     int baselen = -1;
  422.  
  423.     f->sin_port = ntohs(f->sin_port);
  424.     if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
  425.         fatal("Malformed from address");
  426.     hp = gethostbyaddr((char *)&f->sin_addr,
  427.         sizeof(struct in_addr), f->sin_family);
  428.     if (hp == 0)
  429.         fatal("Host name for your address (%s) unknown",
  430.             inet_ntoa(f->sin_addr));
  431.  
  432.     strcpy(fromb, hp->h_name);
  433.     from = fromb;
  434.     if (!strcmp(from, host))
  435.         return;
  436.  
  437.     sp = fromb;
  438.     cp = ahost;
  439.     while (*sp) {
  440.         if (*sp == '.') {
  441.             if (baselen == -1)
  442.                 baselen = sp - fromb;
  443.             *cp++ = *sp++;
  444.         } else {
  445.             *cp++ = isupper(*sp) ? tolower(*sp++) : *sp++;
  446.         }
  447.     }
  448.     *cp = '\0';
  449.     hostf = fopen(_PATH_HOSTSEQUIV, "r");
  450. again:
  451.     if (hostf) {
  452.         if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) {
  453.             (void) fclose(hostf);
  454.             return;
  455.         }
  456.         (void) fclose(hostf);
  457.     }
  458.     if (first == 1) {
  459.         first = 0;
  460.         hostf = fopen(_PATH_HOSTSLPD, "r");
  461.         goto again;
  462.     }
  463.     fatal("Your host does not have line printer access");
  464. }
  465.