1*13816Ssam /* lpd.c 4.6 83/07/06 */ 212106Sralph /* 312106Sralph * lpd -- line printer daemon. 412106Sralph * 512106Sralph * Listen for a connection and perform the requested operation. 612106Sralph * Operations are: 712106Sralph * \1printer\n 812106Sralph * check the queue for jobs and print any found. 912106Sralph * \2printer\n 1012106Sralph * receive a job from another machine and queue it. 1112106Sralph * \3printer [users ...] [jobs ...]\n 1212106Sralph * return the current state of the queue (short form). 1312106Sralph * \4printer [users ...] [jobs ...]\n 1412106Sralph * return the current state of the queue (long form). 1512106Sralph * \5printer person [users ...] [jobs ...]\n 1612106Sralph * remove jobs from the queue. 1712430Sralph * \6printer\n 1812430Sralph * enable queuing on the specified printer queue. 1912430Sralph * \7printer\n 2012430Sralph * disable queuing on the specified printer queue. 2112430Sralph * \8printer\n 2212430Sralph * return the queue status (queuing enabled or disabled). 2312106Sralph * 2412106Sralph * Strategy to maintain protected spooling area: 2512106Sralph * 1. Spooling area is writable only by daemon and spooling group 2612106Sralph * 2. lpr runs setuid root and setgrp spooling group; it uses 2712106Sralph * root to access any file it wants (verifying things before 2812106Sralph * with an access call) and group id to know how it should 2912106Sralph * set up ownership of files in the spooling area. 3012430Sralph * 3. Files in spooling area are owned by root, group spooling 3112106Sralph * group, with mode 660. 3212106Sralph * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 3312106Sralph * access files and printer. Users can't get to anything 3412106Sralph * w/o help of lpq and lprm programs. 3512106Sralph */ 3612106Sralph 3712106Sralph #include "lp.h" 3812106Sralph 3912875Sralph static int lflag; /* log requests flag */ 4012875Sralph static char *logfile = DEFLOGF; 4112875Sralph 4212106Sralph int reapchild(); 43*13816Ssam int cleanup(); 4412106Sralph 4512106Sralph main(argc, argv) 4612106Sralph int argc; 4712106Sralph char **argv; 4812106Sralph { 49*13816Ssam int f, funix, finet, options, defreadfds, fromlen; 50*13816Ssam struct sockaddr_un sun, fromunix; 51*13816Ssam struct sockaddr_in sin, frominet; 52*13816Ssam int omask; 5312106Sralph 5412106Sralph gethostname(host, sizeof(host)); 5512106Sralph name = argv[0]; 5612106Sralph 5712106Sralph while (--argc > 0) { 5812106Sralph argv++; 5912106Sralph if (argv[0][0] == '-') 6012106Sralph switch (argv[0][1]) { 6112106Sralph case 'd': 6212106Sralph options |= SO_DEBUG; 6312106Sralph break; 6412106Sralph case 'l': 6512430Sralph lflag++; 6612430Sralph break; 6712430Sralph case 'L': 6812106Sralph argc--; 6912106Sralph logfile = *++argv; 7012106Sralph break; 7112106Sralph } 7212106Sralph } 7312106Sralph #ifndef DEBUG 7412106Sralph /* 7512106Sralph * Set up standard environment by detaching from the parent. 7612106Sralph */ 7712106Sralph if (fork()) 7812106Sralph exit(0); 7912106Sralph for (f = 0; f < 3; f++) 8012106Sralph (void) close(f); 8113147Ssam (void) open("/dev/null", O_RDONLY); 8213147Ssam (void) open("/dev/null", O_WRONLY); 8313147Ssam (void) open(logfile, O_WRONLY|O_APPEND); 8413147Ssam f = open("/dev/tty", O_RDWR); 8512106Sralph if (f > 0) { 8612106Sralph ioctl(f, TIOCNOTTY, 0); 8712106Sralph (void) close(f); 8812106Sralph } 8912106Sralph #endif 90*13816Ssam #define mask(s) (1 << ((s) - 1)) 91*13816Ssam #define ALLINTS mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM) 92*13816Ssam omask = sigblock(ALLINTS); 93*13816Ssam signal(SIGHUP, cleanup); 94*13816Ssam signal(SIGINT, cleanup); 95*13816Ssam signal(SIGQUIT, cleanup); 96*13816Ssam signal(SIGTERM, cleanup); 97*13816Ssam signal(SIGCHLD, reapchild); 9812106Sralph (void) umask(0); 9912875Sralph /* 10012875Sralph * Restart all the printers. 10112875Sralph */ 10212875Sralph startup(); 103*13816Ssam funix = socket(AF_UNIX, SOCK_STREAM, 0); 104*13816Ssam if (funix < 0) { 105*13816Ssam logerr("socket"); 10612106Sralph exit(1); 10712106Sralph } 108*13816Ssam sun.sun_family = AF_UNIX; 109*13816Ssam strcpy(sun.sun_path, SOCKETNAME); 110*13816Ssam if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) { 111*13816Ssam logerr("unix domain bind"); 11212106Sralph exit(1); 11312106Sralph } 114*13816Ssam sigsetmask(omask); 115*13816Ssam finet = socket(AF_INET, SOCK_STREAM, 0); 116*13816Ssam if (finet >= 0) { 117*13816Ssam struct servent *sp; 118*13816Ssam 119*13816Ssam if (options & SO_DEBUG) 120*13816Ssam if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 121*13816Ssam logerr("setsockopt (SO_DEBUG)"); 122*13816Ssam cleanup(); 123*13816Ssam } 124*13816Ssam sp = getservbyname("printer", "tcp"); 125*13816Ssam if (sp == NULL) { 126*13816Ssam log("printer/tcp: unknown service"); 127*13816Ssam cleanup(); 128*13816Ssam } 129*13816Ssam sin.sin_family = AF_INET; 130*13816Ssam sin.sin_port = sp->s_port; 131*13816Ssam if (bind(finet, &sin, sizeof(sin), 0) < 0) { 132*13816Ssam logerr("internet domain bind"); 133*13816Ssam cleanup(); 134*13816Ssam } 135*13816Ssam } 13612106Sralph /* 13712106Sralph * Main loop: listen, accept, do a request, continue. 13812106Sralph */ 139*13816Ssam defreadfds = 1 << funix; 140*13816Ssam listen(funix, 5); 141*13816Ssam if (finet >= 0) { 142*13816Ssam defreadfds |= 1 << finet; 143*13816Ssam listen(finet, 5); 144*13816Ssam } 14512106Sralph for (;;) { 146*13816Ssam int domain, s, readfds = defreadfds; 14712106Sralph 148*13816Ssam (void) select(&readfds, 0, 0, 0); 149*13816Ssam if (readfds & (1 << funix)) { 150*13816Ssam domain = AF_UNIX; 151*13816Ssam fromlen = sizeof(fromunix); 152*13816Ssam s = accept(funix, &fromunix, &fromlen); 153*13816Ssam } else { 154*13816Ssam domain = AF_INET; 155*13816Ssam fromlen = sizeof(frominet); 156*13816Ssam s = accept(finet, &frominet, &fromlen); 157*13816Ssam } 15812106Sralph if (s < 0) { 15912106Sralph if (errno == EINTR) 16012106Sralph continue; 161*13816Ssam logerr("accept"); 162*13816Ssam cleanup(); 16312106Sralph } 16412106Sralph if (fork() == 0) { 16513147Ssam signal(SIGCHLD, SIG_IGN); 166*13816Ssam (void) close(funix); 167*13816Ssam (void) close(finet); 168*13816Ssam dup2(s, 1); 169*13816Ssam (void) close(s); 170*13816Ssam if (domain == AF_INET) 171*13816Ssam chkhost(&frominet); 172*13816Ssam doit(); 17312106Sralph exit(0); 17412106Sralph } 17512106Sralph (void) close(s); 17612106Sralph } 17712106Sralph } 17812106Sralph 17912875Sralph static 18012106Sralph reapchild() 18112106Sralph { 18212106Sralph union wait status; 18312106Sralph 18412106Sralph while (wait3(&status, WNOHANG, 0) > 0) 18512106Sralph ; 18612106Sralph } 18712106Sralph 188*13816Ssam static 189*13816Ssam cleanup() 190*13816Ssam { 191*13816Ssam unlink(SOCKETNAME); 192*13816Ssam exit(0); 193*13816Ssam } 194*13816Ssam 19512106Sralph /* 19612106Sralph * Stuff for handling job specifications 19712106Sralph */ 19812106Sralph char *user[MAXUSERS]; /* users to process */ 19912106Sralph int users; /* # of users in user array */ 20012106Sralph int requ[MAXREQUESTS]; /* job number of spool entries */ 20112106Sralph int requests; /* # of spool requests */ 20212875Sralph char *person; /* name of person doing lprm */ 20312106Sralph 20412875Sralph static char fromb[32]; /* buffer for client's machine name */ 20512875Sralph static char cbuf[BUFSIZ]; /* command line buffer */ 20612875Sralph static char *cmdnames[] = { 20712430Sralph "null", 20812430Sralph "printjob", 20912430Sralph "recvjob", 21012430Sralph "displayq short", 21112430Sralph "displayq long", 21212430Sralph "rmjob" 21312430Sralph }; 21412106Sralph 21512875Sralph static 216*13816Ssam doit() 21712106Sralph { 21812106Sralph register char *cp; 21912106Sralph register int n; 22012106Sralph 22112106Sralph for (;;) { 22212106Sralph cp = cbuf; 22312106Sralph do { 224*13816Ssam if (cp >= &cbuf[sizeof(cbuf) - 1]) 225*13816Ssam fatal("Command line too long"); 226*13816Ssam if ((n = read(1, cp, 1)) != 1) { 22712106Sralph if (n < 0) 22812106Sralph fatal("Lost connection"); 22912106Sralph return; 23012106Sralph } 231*13816Ssam } while (*cp++ != '\n'); 23212106Sralph *--cp = '\0'; 23312106Sralph cp = cbuf; 23412430Sralph if (lflag && *cp >= '\1' && *cp <= '\5') { 23512430Sralph printer = NULL; 23612430Sralph log("%s requests %s %s", from, cmdnames[*cp], cp+1); 23712430Sralph } 23812106Sralph switch (*cp++) { 23912106Sralph case '\1': /* check the queue and print any jobs there */ 24012106Sralph printer = cp; 24112106Sralph printjob(); 24212106Sralph break; 24312106Sralph case '\2': /* receive files to be queued */ 24412106Sralph printer = cp; 24512106Sralph recvjob(); 24612106Sralph break; 24712430Sralph case '\3': /* display the queue (short form) */ 24812430Sralph case '\4': /* display the queue (long form) */ 24912106Sralph printer = cp; 25012106Sralph while (*cp) { 25112106Sralph if (*cp != ' ') { 25212106Sralph cp++; 25312106Sralph continue; 25412106Sralph } 25512106Sralph *cp++ = '\0'; 25612106Sralph while (isspace(*cp)) 25712106Sralph cp++; 25812106Sralph if (*cp == '\0') 25912106Sralph break; 26012106Sralph if (isdigit(*cp)) { 26112106Sralph if (requests >= MAXREQUESTS) 26212106Sralph fatal("Too many requests"); 26312106Sralph requ[requests++] = atoi(cp); 26412106Sralph } else { 26512106Sralph if (users >= MAXUSERS) 26612106Sralph fatal("Too many users"); 26712106Sralph user[users++] = cp; 26812106Sralph } 26912106Sralph } 27012106Sralph displayq(cbuf[0] - '\3'); 27112106Sralph exit(0); 27212106Sralph case '\5': /* remove a job from the queue */ 27312106Sralph printer = cp; 27412106Sralph while (*cp && *cp != ' ') 27512106Sralph cp++; 27612106Sralph if (!*cp) 27712106Sralph break; 27812106Sralph *cp++ = '\0'; 27912106Sralph person = cp; 28012106Sralph while (*cp) { 28112106Sralph if (*cp != ' ') { 28212106Sralph cp++; 28312106Sralph continue; 28412106Sralph } 28512106Sralph *cp++ = '\0'; 28612106Sralph while (isspace(*cp)) 28712106Sralph cp++; 28812106Sralph if (*cp == '\0') 28912106Sralph break; 29012106Sralph if (isdigit(*cp)) { 29112106Sralph if (requests >= MAXREQUESTS) 29212106Sralph fatal("Too many requests"); 29312106Sralph requ[requests++] = atoi(cp); 29412106Sralph } else { 29512106Sralph if (users >= MAXUSERS) 29612106Sralph fatal("Too many users"); 29712106Sralph user[users++] = cp; 29812106Sralph } 29912106Sralph } 30012106Sralph rmjob(); 30112106Sralph break; 30212106Sralph } 30312106Sralph fatal("Illegal service request"); 30412106Sralph } 30512106Sralph } 30612106Sralph 30712106Sralph /* 30812430Sralph * Make a pass through the printcap database and start printing any 30912430Sralph * files left from the last time the machine went down. 31012430Sralph */ 31112875Sralph static 31212430Sralph startup() 31312430Sralph { 31412430Sralph char buf[BUFSIZ]; 31512430Sralph register char *cp; 31612430Sralph int pid; 31712430Sralph 31812430Sralph printer = buf; 31912430Sralph 32012430Sralph /* 32112430Sralph * Restart the daemons. 32212430Sralph */ 32312430Sralph while (getprent(buf) > 0) { 32412430Sralph for (cp = buf; *cp; cp++) 32512430Sralph if (*cp == '|' || *cp == ':') { 32612430Sralph *cp = '\0'; 32712430Sralph break; 32812430Sralph } 32912430Sralph if ((pid = fork()) < 0) { 33012430Sralph log("startup: cannot fork"); 331*13816Ssam cleanup(); 33212430Sralph } 33312430Sralph if (!pid) { 33412430Sralph endprent(); 33512430Sralph printjob(); 33612430Sralph } 33712430Sralph } 33812430Sralph } 33912430Sralph 34012430Sralph /* 34112430Sralph * Check to see if the from host has access to the line printer. 34212430Sralph */ 34312875Sralph static 344*13816Ssam chkhost(f) 345*13816Ssam struct sockaddr_in *f; 34612430Sralph { 347*13816Ssam register struct hostent *hp; 34812430Sralph register FILE *hostf; 34912430Sralph register char *cp; 35012430Sralph char ahost[50]; 351*13816Ssam extern char *inet_ntoa(); 35212430Sralph 353*13816Ssam f->sin_port = ntohs(f->sin_port); 354*13816Ssam if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 355*13816Ssam fatal("Malformed from address"); 356*13816Ssam hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family); 357*13816Ssam if (hp == 0) 358*13816Ssam fatal("Host name for your address (%s) unknown", 359*13816Ssam inet_ntoa(f->sin_addr)); 360*13816Ssam 361*13816Ssam strcpy(fromb, hp->h_name); 362*13816Ssam from = fromb; 36312734Sralph if (!strcmp(from, host)) 364*13816Ssam return; 36512734Sralph 36612430Sralph hostf = fopen("/etc/hosts.equiv", "r"); 36712430Sralph while (fgets(ahost, sizeof(ahost), hostf)) { 36812430Sralph if (cp = index(ahost, '\n')) 36912430Sralph *cp = '\0'; 37012430Sralph cp = index(ahost, ' '); 37112430Sralph if (!strcmp(from, ahost) && cp == NULL) { 37212430Sralph (void) fclose(hostf); 373*13816Ssam return; 37412430Sralph } 37512430Sralph } 376*13816Ssam fatal("Your host does not have line printer access"); 37712430Sralph } 37812430Sralph 37912106Sralph /*VARARGS1*/ 38012106Sralph log(msg, a1, a2, a3) 38112106Sralph char *msg; 38212106Sralph { 38312106Sralph short console = isatty(fileno(stderr)); 38412106Sralph 38512106Sralph fprintf(stderr, console ? "\r\n%s: " : "%s: ", name); 38612106Sralph if (printer) 38712106Sralph fprintf(stderr, "%s: ", printer); 38812106Sralph fprintf(stderr, msg, a1, a2, a3); 38912106Sralph if (console) 39012106Sralph putc('\r', stderr); 39112106Sralph putc('\n', stderr); 39212106Sralph fflush(stderr); 39312106Sralph } 39412106Sralph 39512875Sralph static 396*13816Ssam logerr(msg) 39712106Sralph char *msg; 39812106Sralph { 39912106Sralph register int err = errno; 40012106Sralph short console = isatty(fileno(stderr)); 40112106Sralph extern int sys_nerr; 40212106Sralph extern char *sys_errlist[]; 40312106Sralph 40412106Sralph fprintf(stderr, console ? "\r\n%s: " : "%s: ", name); 40512430Sralph if (msg) 40612106Sralph fprintf(stderr, "%s: ", msg); 40712106Sralph fputs(err < sys_nerr ? sys_errlist[err] : "Unknown error" , stderr); 40812106Sralph if (console) 40912106Sralph putc('\r', stderr); 41012106Sralph putc('\n', stderr); 41112106Sralph fflush(stderr); 41212106Sralph } 413