122429Sdist /* 222429Sdist * Copyright (c) 1983 Regents of the University of California. 3*34203Sbostic * All rights reserved. 4*34203Sbostic * 5*34203Sbostic * Redistribution and use in source and binary forms are permitted 6*34203Sbostic * provided that this notice is preserved and that due credit is given 7*34203Sbostic * to the University of California at Berkeley. The name of the University 8*34203Sbostic * may not be used to endorse or promote products derived from this 9*34203Sbostic * software without specific prior written permission. This software 10*34203Sbostic * is provided ``as is'' without express or implied warranty. 1122429Sdist */ 1222429Sdist 1313957Ssam #ifndef lint 1422429Sdist char copyright[] = 1522429Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1622429Sdist All rights reserved.\n"; 17*34203Sbostic #endif /* not lint */ 1813957Ssam 1922429Sdist #ifndef lint 20*34203Sbostic static char sccsid[] = "@(#)lpd.c 5.5 (Berkeley) 05/05/88"; 21*34203Sbostic #endif /* not lint */ 2222429Sdist 2312106Sralph /* 2412106Sralph * lpd -- line printer daemon. 2512106Sralph * 2612106Sralph * Listen for a connection and perform the requested operation. 2712106Sralph * Operations are: 2812106Sralph * \1printer\n 2912106Sralph * check the queue for jobs and print any found. 3012106Sralph * \2printer\n 3112106Sralph * receive a job from another machine and queue it. 3212106Sralph * \3printer [users ...] [jobs ...]\n 3312106Sralph * return the current state of the queue (short form). 3412106Sralph * \4printer [users ...] [jobs ...]\n 3512106Sralph * return the current state of the queue (long form). 3612106Sralph * \5printer person [users ...] [jobs ...]\n 3712106Sralph * remove jobs from the queue. 3812106Sralph * 3912106Sralph * Strategy to maintain protected spooling area: 4012106Sralph * 1. Spooling area is writable only by daemon and spooling group 4112106Sralph * 2. lpr runs setuid root and setgrp spooling group; it uses 4212106Sralph * root to access any file it wants (verifying things before 4312106Sralph * with an access call) and group id to know how it should 4412106Sralph * set up ownership of files in the spooling area. 4512430Sralph * 3. Files in spooling area are owned by root, group spooling 4612106Sralph * group, with mode 660. 4712106Sralph * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 4812106Sralph * access files and printer. Users can't get to anything 4912106Sralph * w/o help of lpq and lprm programs. 5012106Sralph */ 5112106Sralph 5212106Sralph #include "lp.h" 5312106Sralph 5416761Sralph int lflag; /* log requests flag */ 5512875Sralph 5612106Sralph int reapchild(); 5716761Sralph int mcleanup(); 5812106Sralph 5912106Sralph main(argc, argv) 6012106Sralph int argc; 6112106Sralph char **argv; 6212106Sralph { 6313816Ssam int f, funix, finet, options, defreadfds, fromlen; 6413816Ssam struct sockaddr_un sun, fromunix; 6513816Ssam struct sockaddr_in sin, frominet; 6614837Sralph int omask, lfd; 6712106Sralph 6812106Sralph gethostname(host, sizeof(host)); 6912106Sralph name = argv[0]; 7012106Sralph 7112106Sralph while (--argc > 0) { 7212106Sralph argv++; 7312106Sralph if (argv[0][0] == '-') 7412106Sralph switch (argv[0][1]) { 7512106Sralph case 'd': 7612106Sralph options |= SO_DEBUG; 7712106Sralph break; 7812106Sralph case 'l': 7912430Sralph lflag++; 8012430Sralph break; 8112106Sralph } 8212106Sralph } 8314837Sralph 8412106Sralph #ifndef DEBUG 8512106Sralph /* 8612106Sralph * Set up standard environment by detaching from the parent. 8712106Sralph */ 8812106Sralph if (fork()) 8912106Sralph exit(0); 9016761Sralph for (f = 0; f < 5; f++) 9112106Sralph (void) close(f); 9213147Ssam (void) open("/dev/null", O_RDONLY); 9313147Ssam (void) open("/dev/null", O_WRONLY); 9416761Sralph (void) dup(1); 9513147Ssam f = open("/dev/tty", O_RDWR); 9612106Sralph if (f > 0) { 9712106Sralph ioctl(f, TIOCNOTTY, 0); 9812106Sralph (void) close(f); 9912106Sralph } 10012106Sralph #endif 10114837Sralph 10225494Seric openlog("lpd", LOG_PID, LOG_LPR); 10312106Sralph (void) umask(0); 10414837Sralph lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644); 10514837Sralph if (lfd < 0) { 10616761Sralph syslog(LOG_ERR, "%s: %m", MASTERLOCK); 10714837Sralph exit(1); 10814837Sralph } 10914837Sralph if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { 11014837Sralph if (errno == EWOULDBLOCK) /* active deamon present */ 11114837Sralph exit(0); 11216761Sralph syslog(LOG_ERR, "%s: %m", MASTERLOCK); 11314837Sralph exit(1); 11414837Sralph } 11514837Sralph ftruncate(lfd, 0); 11612875Sralph /* 11714837Sralph * write process id for others to know 11814837Sralph */ 11914837Sralph sprintf(line, "%u\n", getpid()); 12014837Sralph f = strlen(line); 12114837Sralph if (write(lfd, line, f) != f) { 12216761Sralph syslog(LOG_ERR, "%s: %m", MASTERLOCK); 12314837Sralph exit(1); 12414837Sralph } 12514837Sralph signal(SIGCHLD, reapchild); 12614837Sralph /* 12712875Sralph * Restart all the printers. 12812875Sralph */ 12912875Sralph startup(); 13014837Sralph (void) unlink(SOCKETNAME); 13113816Ssam funix = socket(AF_UNIX, SOCK_STREAM, 0); 13213816Ssam if (funix < 0) { 13316761Sralph syslog(LOG_ERR, "socket: %m"); 13412106Sralph exit(1); 13512106Sralph } 13614149Sralph #define mask(s) (1 << ((s) - 1)) 13714149Sralph omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); 13816761Sralph signal(SIGHUP, mcleanup); 13916761Sralph signal(SIGINT, mcleanup); 14016761Sralph signal(SIGQUIT, mcleanup); 14116761Sralph signal(SIGTERM, mcleanup); 14213816Ssam sun.sun_family = AF_UNIX; 14313816Ssam strcpy(sun.sun_path, SOCKETNAME); 14413816Ssam if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) { 14516761Sralph syslog(LOG_ERR, "ubind: %m"); 14612106Sralph exit(1); 14712106Sralph } 14813816Ssam sigsetmask(omask); 14914149Sralph defreadfds = 1 << funix; 15014149Sralph listen(funix, 5); 15113816Ssam finet = socket(AF_INET, SOCK_STREAM, 0); 15213816Ssam if (finet >= 0) { 15313816Ssam struct servent *sp; 15413816Ssam 15513816Ssam if (options & SO_DEBUG) 15613816Ssam if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 15716761Sralph syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 15816761Sralph mcleanup(); 15913816Ssam } 16013816Ssam sp = getservbyname("printer", "tcp"); 16113816Ssam if (sp == NULL) { 16216761Sralph syslog(LOG_ERR, "printer/tcp: unknown service"); 16316761Sralph mcleanup(); 16413816Ssam } 16513816Ssam sin.sin_family = AF_INET; 16613816Ssam sin.sin_port = sp->s_port; 16713816Ssam if (bind(finet, &sin, sizeof(sin), 0) < 0) { 16816761Sralph syslog(LOG_ERR, "bind: %m"); 16916761Sralph mcleanup(); 17013816Ssam } 17114149Sralph defreadfds |= 1 << finet; 17214149Sralph listen(finet, 5); 17313816Ssam } 17412106Sralph /* 17514149Sralph * Main loop: accept, do a request, continue. 17612106Sralph */ 17712106Sralph for (;;) { 17813957Ssam int domain, nfds, s, readfds = defreadfds; 17912106Sralph 18013957Ssam nfds = select(20, &readfds, 0, 0, 0); 18113957Ssam if (nfds <= 0) { 18216761Sralph if (nfds < 0 && errno != EINTR) 18316761Sralph syslog(LOG_WARNING, "select: %m"); 18413957Ssam continue; 18513957Ssam } 18613816Ssam if (readfds & (1 << funix)) { 18713957Ssam domain = AF_UNIX, fromlen = sizeof(fromunix); 18813816Ssam s = accept(funix, &fromunix, &fromlen); 18913957Ssam } else if (readfds & (1 << finet)) { 19013957Ssam domain = AF_INET, fromlen = sizeof(frominet); 19113816Ssam s = accept(finet, &frominet, &fromlen); 19213816Ssam } 19312106Sralph if (s < 0) { 19416761Sralph if (errno != EINTR) 19516761Sralph syslog(LOG_WARNING, "accept: %m"); 19616761Sralph continue; 19712106Sralph } 19812106Sralph if (fork() == 0) { 19913147Ssam signal(SIGCHLD, SIG_IGN); 20014708Sralph signal(SIGHUP, SIG_IGN); 20114708Sralph signal(SIGINT, SIG_IGN); 20214708Sralph signal(SIGQUIT, SIG_IGN); 20314708Sralph signal(SIGTERM, SIG_IGN); 20413816Ssam (void) close(funix); 20513816Ssam (void) close(finet); 20613816Ssam dup2(s, 1); 20713816Ssam (void) close(s); 20813816Ssam if (domain == AF_INET) 20913816Ssam chkhost(&frominet); 21013816Ssam doit(); 21112106Sralph exit(0); 21212106Sralph } 21312106Sralph (void) close(s); 21412106Sralph } 21512106Sralph } 21612106Sralph 21712106Sralph reapchild() 21812106Sralph { 21912106Sralph union wait status; 22012106Sralph 22112106Sralph while (wait3(&status, WNOHANG, 0) > 0) 22212106Sralph ; 22312106Sralph } 22412106Sralph 22516761Sralph mcleanup() 22613816Ssam { 22714149Sralph if (lflag) 22816761Sralph syslog(LOG_INFO, "exiting"); 22913816Ssam unlink(SOCKETNAME); 23013816Ssam exit(0); 23113816Ssam } 23213816Ssam 23312106Sralph /* 23412106Sralph * Stuff for handling job specifications 23512106Sralph */ 23612106Sralph char *user[MAXUSERS]; /* users to process */ 23712106Sralph int users; /* # of users in user array */ 23812106Sralph int requ[MAXREQUESTS]; /* job number of spool entries */ 23912106Sralph int requests; /* # of spool requests */ 24012875Sralph char *person; /* name of person doing lprm */ 24112106Sralph 24216761Sralph char fromb[32]; /* buffer for client's machine name */ 24316761Sralph char cbuf[BUFSIZ]; /* command line buffer */ 24416761Sralph char *cmdnames[] = { 24512430Sralph "null", 24612430Sralph "printjob", 24712430Sralph "recvjob", 24812430Sralph "displayq short", 24912430Sralph "displayq long", 25012430Sralph "rmjob" 25112430Sralph }; 25212106Sralph 25313816Ssam doit() 25412106Sralph { 25512106Sralph register char *cp; 25612106Sralph register int n; 25712106Sralph 25812106Sralph for (;;) { 25912106Sralph cp = cbuf; 26012106Sralph do { 26113816Ssam if (cp >= &cbuf[sizeof(cbuf) - 1]) 26213816Ssam fatal("Command line too long"); 26313816Ssam if ((n = read(1, cp, 1)) != 1) { 26412106Sralph if (n < 0) 26512106Sralph fatal("Lost connection"); 26612106Sralph return; 26712106Sralph } 26813816Ssam } while (*cp++ != '\n'); 26912106Sralph *--cp = '\0'; 27012106Sralph cp = cbuf; 27116761Sralph if (lflag) { 27216761Sralph if (*cp >= '\1' && *cp <= '\5') 27316761Sralph syslog(LOG_INFO, "%s requests %s %s", 27416761Sralph from, cmdnames[*cp], cp+1); 27516761Sralph else 27616761Sralph syslog(LOG_INFO, "bad request (%d) from %s", 27716761Sralph *cp, from); 27812430Sralph } 27912106Sralph switch (*cp++) { 28012106Sralph case '\1': /* check the queue and print any jobs there */ 28112106Sralph printer = cp; 28212106Sralph printjob(); 28312106Sralph break; 28412106Sralph case '\2': /* receive files to be queued */ 28512106Sralph printer = cp; 28612106Sralph recvjob(); 28712106Sralph break; 28812430Sralph case '\3': /* display the queue (short form) */ 28912430Sralph case '\4': /* display the queue (long form) */ 29012106Sralph printer = cp; 29112106Sralph while (*cp) { 29212106Sralph if (*cp != ' ') { 29312106Sralph cp++; 29412106Sralph continue; 29512106Sralph } 29612106Sralph *cp++ = '\0'; 29712106Sralph while (isspace(*cp)) 29812106Sralph cp++; 29912106Sralph if (*cp == '\0') 30012106Sralph break; 30112106Sralph if (isdigit(*cp)) { 30212106Sralph if (requests >= MAXREQUESTS) 30312106Sralph fatal("Too many requests"); 30412106Sralph requ[requests++] = atoi(cp); 30512106Sralph } else { 30612106Sralph if (users >= MAXUSERS) 30712106Sralph fatal("Too many users"); 30812106Sralph user[users++] = cp; 30912106Sralph } 31012106Sralph } 31112106Sralph displayq(cbuf[0] - '\3'); 31212106Sralph exit(0); 31312106Sralph case '\5': /* remove a job from the queue */ 31412106Sralph printer = cp; 31512106Sralph while (*cp && *cp != ' ') 31612106Sralph cp++; 31712106Sralph if (!*cp) 31812106Sralph break; 31912106Sralph *cp++ = '\0'; 32012106Sralph person = cp; 32112106Sralph while (*cp) { 32212106Sralph if (*cp != ' ') { 32312106Sralph cp++; 32412106Sralph continue; 32512106Sralph } 32612106Sralph *cp++ = '\0'; 32712106Sralph while (isspace(*cp)) 32812106Sralph cp++; 32912106Sralph if (*cp == '\0') 33012106Sralph break; 33112106Sralph if (isdigit(*cp)) { 33212106Sralph if (requests >= MAXREQUESTS) 33312106Sralph fatal("Too many requests"); 33412106Sralph requ[requests++] = atoi(cp); 33512106Sralph } else { 33612106Sralph if (users >= MAXUSERS) 33712106Sralph fatal("Too many users"); 33812106Sralph user[users++] = cp; 33912106Sralph } 34012106Sralph } 34112106Sralph rmjob(); 34212106Sralph break; 34312106Sralph } 34412106Sralph fatal("Illegal service request"); 34512106Sralph } 34612106Sralph } 34712106Sralph 34812106Sralph /* 34912430Sralph * Make a pass through the printcap database and start printing any 35012430Sralph * files left from the last time the machine went down. 35112430Sralph */ 35212430Sralph startup() 35312430Sralph { 35412430Sralph char buf[BUFSIZ]; 35512430Sralph register char *cp; 35612430Sralph int pid; 35712430Sralph 35812430Sralph printer = buf; 35912430Sralph 36012430Sralph /* 36112430Sralph * Restart the daemons. 36212430Sralph */ 36312430Sralph while (getprent(buf) > 0) { 36412430Sralph for (cp = buf; *cp; cp++) 36512430Sralph if (*cp == '|' || *cp == ':') { 36612430Sralph *cp = '\0'; 36712430Sralph break; 36812430Sralph } 36912430Sralph if ((pid = fork()) < 0) { 37016761Sralph syslog(LOG_WARNING, "startup: cannot fork"); 37116761Sralph mcleanup(); 37212430Sralph } 37312430Sralph if (!pid) { 37412430Sralph endprent(); 37512430Sralph printjob(); 37612430Sralph } 37712430Sralph } 37812430Sralph } 37912430Sralph 38027746Sbloom #define DUMMY ":nobody::" 38127746Sbloom 38212430Sralph /* 38312430Sralph * Check to see if the from host has access to the line printer. 38412430Sralph */ 38513816Ssam chkhost(f) 38613816Ssam struct sockaddr_in *f; 38712430Sralph { 38813816Ssam register struct hostent *hp; 38912430Sralph register FILE *hostf; 39027746Sbloom register char *cp, *sp; 39112430Sralph char ahost[50]; 39215551Sralph int first = 1; 39313816Ssam extern char *inet_ntoa(); 39427746Sbloom int baselen = -1; 39512430Sralph 39613816Ssam f->sin_port = ntohs(f->sin_port); 39713816Ssam if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 39813816Ssam fatal("Malformed from address"); 39913816Ssam hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family); 40013816Ssam if (hp == 0) 40113816Ssam fatal("Host name for your address (%s) unknown", 40213816Ssam inet_ntoa(f->sin_addr)); 40313816Ssam 40413816Ssam strcpy(fromb, hp->h_name); 40513816Ssam from = fromb; 40612734Sralph if (!strcmp(from, host)) 40713816Ssam return; 40812734Sralph 40927746Sbloom sp = fromb; 41027746Sbloom cp = ahost; 41127746Sbloom while (*sp) { 41227746Sbloom if (*sp == '.') { 41327746Sbloom if (baselen == -1) 41427746Sbloom baselen = sp - fromb; 41527746Sbloom *cp++ = *sp++; 41627746Sbloom } else { 41727746Sbloom *cp++ = isupper(*sp) ? tolower(*sp++) : *sp++; 41827746Sbloom } 41927746Sbloom } 42027746Sbloom *cp = '\0'; 42112430Sralph hostf = fopen("/etc/hosts.equiv", "r"); 42215551Sralph again: 42315551Sralph if (hostf) { 42427746Sbloom if (!_validuser(hostf, ahost, DUMMY, DUMMY, baselen)) { 42527746Sbloom (void) fclose(hostf); 42627746Sbloom return; 42712430Sralph } 42815551Sralph (void) fclose(hostf); 42912430Sralph } 43015551Sralph if (first == 1) { 43115551Sralph first = 0; 43215551Sralph hostf = fopen("/etc/hosts.lpd", "r"); 43315551Sralph goto again; 43415551Sralph } 43513816Ssam fatal("Your host does not have line printer access"); 43612430Sralph } 437