Annotation of 42BSD/usr.lib/lpr/lpd.c, revision 1.1.1.1

1.1       root        1: #ifndef lint
                      2: static char sccsid[] = "@(#)lpd.c      4.10 (Berkeley) 8/24/83";
                      3: #endif
                      4: 
                      5: /*
                      6:  * lpd -- line printer daemon.
                      7:  *
                      8:  * Listen for a connection and perform the requested operation.
                      9:  * Operations are:
                     10:  *     \1printer\n
                     11:  *             check the queue for jobs and print any found.
                     12:  *     \2printer\n
                     13:  *             receive a job from another machine and queue it.
                     14:  *     \3printer [users ...] [jobs ...]\n
                     15:  *             return the current state of the queue (short form).
                     16:  *     \4printer [users ...] [jobs ...]\n
                     17:  *             return the current state of the queue (long form).
                     18:  *     \5printer person [users ...] [jobs ...]\n
                     19:  *             remove jobs from the queue.
                     20:  *     \6printer\n
                     21:  *             enable queuing on the specified printer queue.
                     22:  *     \7printer\n
                     23:  *             disable queuing on the specified printer queue.
                     24:  *     \8printer\n
                     25:  *             return the queue status (queuing enabled or disabled).
                     26:  *
                     27:  * Strategy to maintain protected spooling area:
                     28:  *     1. Spooling area is writable only by daemon and spooling group
                     29:  *     2. lpr runs setuid root and setgrp spooling group; it uses
                     30:  *        root to access any file it wants (verifying things before
                     31:  *        with an access call) and group id to know how it should
                     32:  *        set up ownership of files in the spooling area.
                     33:  *     3. Files in spooling area are owned by root, group spooling
                     34:  *        group, with mode 660.
                     35:  *     4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
                     36:  *        access files and printer.  Users can't get to anything
                     37:  *        w/o help of lpq and lprm programs.
                     38:  */
                     39: 
                     40: #include "lp.h"
                     41: 
                     42: static int     lflag;                          /* log requests flag */
                     43: static char    *logfile = DEFLOGF;
                     44: 
                     45: int    reapchild();
                     46: int    cleanup();
                     47: 
                     48: main(argc, argv)
                     49:        int argc;
                     50:        char **argv;
                     51: {
                     52:        int f, funix, finet, options, defreadfds, fromlen;
                     53:        struct sockaddr_un sun, fromunix;
                     54:        struct sockaddr_in sin, frominet;
                     55:        int omask, lfd;
                     56: 
                     57:        gethostname(host, sizeof(host));
                     58:        name = argv[0];
                     59: 
                     60:        while (--argc > 0) {
                     61:                argv++;
                     62:                if (argv[0][0] == '-')
                     63:                        switch (argv[0][1]) {
                     64:                        case 'd':
                     65:                                options |= SO_DEBUG;
                     66:                                break;
                     67:                        case 'l':
                     68:                                lflag++;
                     69:                                break;
                     70:                        case 'L':
                     71:                                argc--;
                     72:                                logfile = *++argv;
                     73:                                break;
                     74:                        }
                     75:        }
                     76: 
                     77: #ifndef DEBUG
                     78:        /*
                     79:         * Set up standard environment by detaching from the parent.
                     80:         */
                     81:        if (fork())
                     82:                exit(0);
                     83:        for (f = 0; f < 3; f++)
                     84:                (void) close(f);
                     85:        (void) open("/dev/null", O_RDONLY);
                     86:        (void) open("/dev/null", O_WRONLY);
                     87:        (void) open(logfile, O_WRONLY|O_APPEND);
                     88:        f = open("/dev/tty", O_RDWR);
                     89:        if (f > 0) {
                     90:                ioctl(f, TIOCNOTTY, 0);
                     91:                (void) close(f);
                     92:        }
                     93: #endif
                     94: 
                     95:        (void) umask(0);
                     96:        lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644);
                     97:        if (lfd < 0) {
                     98:                log("cannot create %s", MASTERLOCK);
                     99:                exit(1);
                    100:        }
                    101:        if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
                    102:                if (errno == EWOULDBLOCK)       /* active deamon present */
                    103:                        exit(0);
                    104:                log("cannot lock %s", MASTERLOCK);
                    105:                exit(1);
                    106:        }
                    107:        ftruncate(lfd, 0);
                    108:        /*
                    109:         * write process id for others to know
                    110:         */
                    111:        sprintf(line, "%u\n", getpid());
                    112:        f = strlen(line);
                    113:        if (write(lfd, line, f) != f) {
                    114:                log("cannot write daemon pid");
                    115:                exit(1);
                    116:        }
                    117:        signal(SIGCHLD, reapchild);
                    118:        /*
                    119:         * Restart all the printers.
                    120:         */
                    121:        startup();
                    122:        (void) unlink(SOCKETNAME);
                    123:        funix = socket(AF_UNIX, SOCK_STREAM, 0);
                    124:        if (funix < 0) {
                    125:                logerr("socket");
                    126:                exit(1);
                    127:        }
                    128: #define        mask(s) (1 << ((s) - 1))
                    129:        omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
                    130:        signal(SIGHUP, cleanup);
                    131:        signal(SIGINT, cleanup);
                    132:        signal(SIGQUIT, cleanup);
                    133:        signal(SIGTERM, cleanup);
                    134:        sun.sun_family = AF_UNIX;
                    135:        strcpy(sun.sun_path, SOCKETNAME);
                    136:        if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) {
                    137:                logerr("unix domain bind");
                    138:                exit(1);
                    139:        }
                    140:        sigsetmask(omask);
                    141:        defreadfds = 1 << funix;
                    142:        listen(funix, 5);
                    143:        finet = socket(AF_INET, SOCK_STREAM, 0);
                    144:        if (finet >= 0) {
                    145:                struct servent *sp;
                    146: 
                    147:                if (options & SO_DEBUG)
                    148:                        if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
                    149:                                logerr("setsockopt (SO_DEBUG)");
                    150:                                cleanup();
                    151:                        }
                    152:                sp = getservbyname("printer", "tcp");
                    153:                if (sp == NULL) {
                    154:                        log("printer/tcp: unknown service");
                    155:                        cleanup();
                    156:                }
                    157:                sin.sin_family = AF_INET;
                    158:                sin.sin_port = sp->s_port;
                    159:                if (bind(finet, &sin, sizeof(sin), 0) < 0) {
                    160:                        logerr("internet domain bind");
                    161:                        cleanup();
                    162:                }
                    163:                defreadfds |= 1 << finet;
                    164:                listen(finet, 5);
                    165:        }
                    166:        /*
                    167:         * Main loop: accept, do a request, continue.
                    168:         */
                    169:        for (;;) {
                    170:                int domain, nfds, s, readfds = defreadfds;
                    171: 
                    172:                nfds = select(20, &readfds, 0, 0, 0);
                    173:                if (nfds <= 0) {
                    174:                        if (nfds < 0 && errno != EINTR) {
                    175:                                logerr("select");
                    176:                                cleanup();
                    177:                                /*NOTREACHED*/
                    178:                        }
                    179:                        continue;
                    180:                }
                    181:                if (readfds & (1 << funix)) {
                    182:                        domain = AF_UNIX, fromlen = sizeof(fromunix);
                    183:                        s = accept(funix, &fromunix, &fromlen);
                    184:                } else if (readfds & (1 << finet)) {
                    185:                        domain = AF_INET, fromlen = sizeof(frominet);
                    186:                        s = accept(finet, &frominet, &fromlen);
                    187:                }
                    188:                if (s < 0) {
                    189:                        if (errno == EINTR)
                    190:                                continue;
                    191:                        logerr("accept");
                    192:                        cleanup();
                    193:                }
                    194:                if (fork() == 0) {
                    195:                        signal(SIGCHLD, SIG_IGN);
                    196:                        signal(SIGHUP, SIG_IGN);
                    197:                        signal(SIGINT, SIG_IGN);
                    198:                        signal(SIGQUIT, SIG_IGN);
                    199:                        signal(SIGTERM, SIG_IGN);
                    200:                        (void) close(funix);
                    201:                        (void) close(finet);
                    202:                        dup2(s, 1);
                    203:                        (void) close(s);
                    204:                        if (domain == AF_INET)
                    205:                                chkhost(&frominet);
                    206:                        doit();
                    207:                        exit(0);
                    208:                }
                    209:                (void) close(s);
                    210:        }
                    211: }
                    212: 
                    213: static
                    214: reapchild()
                    215: {
                    216:        union wait status;
                    217: 
                    218:        while (wait3(&status, WNOHANG, 0) > 0)
                    219:                ;
                    220: }
                    221: 
                    222: static
                    223: cleanup()
                    224: {
                    225:        if (lflag)
                    226:                log("cleanup()");
                    227:        unlink(SOCKETNAME);
                    228:        exit(0);
                    229: }
                    230: 
                    231: /*
                    232:  * Stuff for handling job specifications
                    233:  */
                    234: char   *user[MAXUSERS];        /* users to process */
                    235: int    users;                  /* # of users in user array */
                    236: int    requ[MAXREQUESTS];      /* job number of spool entries */
                    237: int    requests;               /* # of spool requests */
                    238: char   *person;                /* name of person doing lprm */
                    239: 
                    240: static char    fromb[32];      /* buffer for client's machine name */
                    241: static char    cbuf[BUFSIZ];   /* command line buffer */
                    242: static char    *cmdnames[] = {
                    243:        "null",
                    244:        "printjob",
                    245:        "recvjob",
                    246:        "displayq short",
                    247:        "displayq long",
                    248:        "rmjob"
                    249: };
                    250: 
                    251: static
                    252: doit()
                    253: {
                    254:        register char *cp;
                    255:        register int n;
                    256: 
                    257:        for (;;) {
                    258:                cp = cbuf;
                    259:                do {
                    260:                        if (cp >= &cbuf[sizeof(cbuf) - 1])
                    261:                                fatal("Command line too long");
                    262:                        if ((n = read(1, cp, 1)) != 1) {
                    263:                                if (n < 0)
                    264:                                        fatal("Lost connection");
                    265:                                return;
                    266:                        }
                    267:                } while (*cp++ != '\n');
                    268:                *--cp = '\0';
                    269:                cp = cbuf;
                    270:                if (lflag && *cp >= '\1' && *cp <= '\5') {
                    271:                        printer = NULL;
                    272:                        log("%s requests %s %s", from, cmdnames[*cp], cp+1);
                    273:                }
                    274:                switch (*cp++) {
                    275:                case '\1':      /* check the queue and print any jobs there */
                    276:                        printer = cp;
                    277:                        printjob();
                    278:                        break;
                    279:                case '\2':      /* receive files to be queued */
                    280:                        printer = cp;
                    281:                        recvjob();
                    282:                        break;
                    283:                case '\3':      /* display the queue (short form) */
                    284:                case '\4':      /* display the queue (long form) */
                    285:                        printer = cp;
                    286:                        while (*cp) {
                    287:                                if (*cp != ' ') {
                    288:                                        cp++;
                    289:                                        continue;
                    290:                                }
                    291:                                *cp++ = '\0';
                    292:                                while (isspace(*cp))
                    293:                                        cp++;
                    294:                                if (*cp == '\0')
                    295:                                        break;
                    296:                                if (isdigit(*cp)) {
                    297:                                        if (requests >= MAXREQUESTS)
                    298:                                                fatal("Too many requests");
                    299:                                        requ[requests++] = atoi(cp);
                    300:                                } else {
                    301:                                        if (users >= MAXUSERS)
                    302:                                                fatal("Too many users");
                    303:                                        user[users++] = cp;
                    304:                                }
                    305:                        }
                    306:                        displayq(cbuf[0] - '\3');
                    307:                        exit(0);
                    308:                case '\5':      /* remove a job from the queue */
                    309:                        printer = cp;
                    310:                        while (*cp && *cp != ' ')
                    311:                                cp++;
                    312:                        if (!*cp)
                    313:                                break;
                    314:                        *cp++ = '\0';
                    315:                        person = cp;
                    316:                        while (*cp) {
                    317:                                if (*cp != ' ') {
                    318:                                        cp++;
                    319:                                        continue;
                    320:                                }
                    321:                                *cp++ = '\0';
                    322:                                while (isspace(*cp))
                    323:                                        cp++;
                    324:                                if (*cp == '\0')
                    325:                                        break;
                    326:                                if (isdigit(*cp)) {
                    327:                                        if (requests >= MAXREQUESTS)
                    328:                                                fatal("Too many requests");
                    329:                                        requ[requests++] = atoi(cp);
                    330:                                } else {
                    331:                                        if (users >= MAXUSERS)
                    332:                                                fatal("Too many users");
                    333:                                        user[users++] = cp;
                    334:                                }
                    335:                        }
                    336:                        rmjob();
                    337:                        break;
                    338:                }
                    339:                fatal("Illegal service request");
                    340:        }
                    341: }
                    342: 
                    343: /*
                    344:  * Make a pass through the printcap database and start printing any
                    345:  * files left from the last time the machine went down.
                    346:  */
                    347: static
                    348: startup()
                    349: {
                    350:        char buf[BUFSIZ];
                    351:        register char *cp;
                    352:        int pid;
                    353: 
                    354:        printer = buf;
                    355: 
                    356:        /*
                    357:         * Restart the daemons.
                    358:         */
                    359:        while (getprent(buf) > 0) {
                    360:                for (cp = buf; *cp; cp++)
                    361:                        if (*cp == '|' || *cp == ':') {
                    362:                                *cp = '\0';
                    363:                                break;
                    364:                        }
                    365:                if ((pid = fork()) < 0) {
                    366:                        log("startup: cannot fork");
                    367:                        cleanup();
                    368:                }
                    369:                if (!pid) {
                    370:                        endprent();
                    371:                        printjob();
                    372:                }
                    373:        }
                    374: }
                    375: 
                    376: /*
                    377:  * Check to see if the from host has access to the line printer.
                    378:  */
                    379: static
                    380: chkhost(f)
                    381:        struct sockaddr_in *f;
                    382: {
                    383:        register struct hostent *hp;
                    384:        register FILE *hostf;
                    385:        register char *cp;
                    386:        char ahost[50];
                    387:        extern char *inet_ntoa();
                    388: 
                    389:        f->sin_port = ntohs(f->sin_port);
                    390:        if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED)
                    391:                fatal("Malformed from address");
                    392:        hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family);
                    393:        if (hp == 0)
                    394:                fatal("Host name for your address (%s) unknown",
                    395:                        inet_ntoa(f->sin_addr));
                    396: 
                    397:        strcpy(fromb, hp->h_name);
                    398:        from = fromb;
                    399:        if (!strcmp(from, host))
                    400:                return;
                    401: 
                    402:        hostf = fopen("/etc/hosts.equiv", "r");
                    403:        while (fgets(ahost, sizeof(ahost), hostf)) {
                    404:                if (cp = index(ahost, '\n'))
                    405:                        *cp = '\0';
                    406:                cp = index(ahost, ' ');
                    407:                if (!strcmp(from, ahost) && cp == NULL) {
                    408:                        (void) fclose(hostf);
                    409:                        return;
                    410:                }
                    411:        }
                    412:        fatal("Your host does not have line printer access");
                    413: }
                    414: 
                    415: /*VARARGS1*/
                    416: log(msg, a1, a2, a3)
                    417:        char *msg;
                    418: {
                    419:        short console = isatty(fileno(stderr));
                    420: 
                    421:        fprintf(stderr, console ? "\r\n%s: " : "%s: ", name);
                    422:        if (printer)
                    423:                fprintf(stderr, "%s: ", printer);
                    424:        fprintf(stderr, msg, a1, a2, a3);
                    425:        if (console)
                    426:                putc('\r', stderr);
                    427:        putc('\n', stderr);
                    428:        fflush(stderr);
                    429: }
                    430: 
                    431: static
                    432: logerr(msg)
                    433:        char *msg;
                    434: {
                    435:        register int err = errno;
                    436:        short console = isatty(fileno(stderr));
                    437:        extern int sys_nerr;
                    438:        extern char *sys_errlist[];
                    439: 
                    440:        fprintf(stderr, console ? "\r\n%s: " : "%s: ", name);
                    441:        if (msg)
                    442:                fprintf(stderr, "%s: ", msg);
                    443:        fputs(err < sys_nerr ? sys_errlist[err] : "Unknown error" , stderr);
                    444:        if (console)
                    445:                putc('\r', stderr);
                    446:        putc('\n', stderr);
                    447:        fflush(stderr);
                    448: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.