1*22429Sdist /* 2*22429Sdist * Copyright (c) 1983 Regents of the University of California. 3*22429Sdist * All rights reserved. The Berkeley software License Agreement 4*22429Sdist * specifies the terms and conditions for redistribution. 5*22429Sdist */ 6*22429Sdist 713957Ssam #ifndef lint 8*22429Sdist char copyright[] = 9*22429Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 10*22429Sdist All rights reserved.\n"; 11*22429Sdist #endif not lint 1213957Ssam 13*22429Sdist #ifndef lint 14*22429Sdist static char sccsid[] = "@(#)lpd.c 5.1 (Berkeley) 06/06/85"; 15*22429Sdist #endif not lint 16*22429Sdist 1712106Sralph /* 1812106Sralph * lpd -- line printer daemon. 1912106Sralph * 2012106Sralph * Listen for a connection and perform the requested operation. 2112106Sralph * Operations are: 2212106Sralph * \1printer\n 2312106Sralph * check the queue for jobs and print any found. 2412106Sralph * \2printer\n 2512106Sralph * receive a job from another machine and queue it. 2612106Sralph * \3printer [users ...] [jobs ...]\n 2712106Sralph * return the current state of the queue (short form). 2812106Sralph * \4printer [users ...] [jobs ...]\n 2912106Sralph * return the current state of the queue (long form). 3012106Sralph * \5printer person [users ...] [jobs ...]\n 3112106Sralph * remove jobs from the queue. 3212106Sralph * 3312106Sralph * Strategy to maintain protected spooling area: 3412106Sralph * 1. Spooling area is writable only by daemon and spooling group 3512106Sralph * 2. lpr runs setuid root and setgrp spooling group; it uses 3612106Sralph * root to access any file it wants (verifying things before 3712106Sralph * with an access call) and group id to know how it should 3812106Sralph * set up ownership of files in the spooling area. 3912430Sralph * 3. Files in spooling area are owned by root, group spooling 4012106Sralph * group, with mode 660. 4112106Sralph * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 4212106Sralph * access files and printer. Users can't get to anything 4312106Sralph * w/o help of lpq and lprm programs. 4412106Sralph */ 4512106Sralph 4612106Sralph #include "lp.h" 4712106Sralph 4816761Sralph int lflag; /* log requests flag */ 4912875Sralph 5012106Sralph int reapchild(); 5116761Sralph int mcleanup(); 5212106Sralph 5312106Sralph main(argc, argv) 5412106Sralph int argc; 5512106Sralph char **argv; 5612106Sralph { 5713816Ssam int f, funix, finet, options, defreadfds, fromlen; 5813816Ssam struct sockaddr_un sun, fromunix; 5913816Ssam struct sockaddr_in sin, frominet; 6014837Sralph int omask, lfd; 6112106Sralph 6212106Sralph gethostname(host, sizeof(host)); 6312106Sralph name = argv[0]; 6412106Sralph 6512106Sralph while (--argc > 0) { 6612106Sralph argv++; 6712106Sralph if (argv[0][0] == '-') 6812106Sralph switch (argv[0][1]) { 6912106Sralph case 'd': 7012106Sralph options |= SO_DEBUG; 7112106Sralph break; 7212106Sralph case 'l': 7312430Sralph lflag++; 7412430Sralph break; 7512106Sralph } 7612106Sralph } 7714837Sralph 7812106Sralph #ifndef DEBUG 7912106Sralph /* 8012106Sralph * Set up standard environment by detaching from the parent. 8112106Sralph */ 8212106Sralph if (fork()) 8312106Sralph exit(0); 8416761Sralph for (f = 0; f < 5; f++) 8512106Sralph (void) close(f); 8613147Ssam (void) open("/dev/null", O_RDONLY); 8713147Ssam (void) open("/dev/null", O_WRONLY); 8816761Sralph (void) dup(1); 8913147Ssam f = open("/dev/tty", O_RDWR); 9012106Sralph if (f > 0) { 9112106Sralph ioctl(f, TIOCNOTTY, 0); 9212106Sralph (void) close(f); 9312106Sralph } 9412106Sralph #endif 9514837Sralph 9616761Sralph openlog("lpd", LOG_PID, 0); 9712106Sralph (void) umask(0); 9814837Sralph lfd = open(MASTERLOCK, O_WRONLY|O_CREAT, 0644); 9914837Sralph if (lfd < 0) { 10016761Sralph syslog(LOG_ERR, "%s: %m", MASTERLOCK); 10114837Sralph exit(1); 10214837Sralph } 10314837Sralph if (flock(lfd, LOCK_EX|LOCK_NB) < 0) { 10414837Sralph if (errno == EWOULDBLOCK) /* active deamon present */ 10514837Sralph exit(0); 10616761Sralph syslog(LOG_ERR, "%s: %m", MASTERLOCK); 10714837Sralph exit(1); 10814837Sralph } 10914837Sralph ftruncate(lfd, 0); 11012875Sralph /* 11114837Sralph * write process id for others to know 11214837Sralph */ 11314837Sralph sprintf(line, "%u\n", getpid()); 11414837Sralph f = strlen(line); 11514837Sralph if (write(lfd, line, f) != f) { 11616761Sralph syslog(LOG_ERR, "%s: %m", MASTERLOCK); 11714837Sralph exit(1); 11814837Sralph } 11914837Sralph signal(SIGCHLD, reapchild); 12014837Sralph /* 12112875Sralph * Restart all the printers. 12212875Sralph */ 12312875Sralph startup(); 12414837Sralph (void) unlink(SOCKETNAME); 12513816Ssam funix = socket(AF_UNIX, SOCK_STREAM, 0); 12613816Ssam if (funix < 0) { 12716761Sralph syslog(LOG_ERR, "socket: %m"); 12812106Sralph exit(1); 12912106Sralph } 13014149Sralph #define mask(s) (1 << ((s) - 1)) 13114149Sralph omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); 13216761Sralph signal(SIGHUP, mcleanup); 13316761Sralph signal(SIGINT, mcleanup); 13416761Sralph signal(SIGQUIT, mcleanup); 13516761Sralph signal(SIGTERM, mcleanup); 13613816Ssam sun.sun_family = AF_UNIX; 13713816Ssam strcpy(sun.sun_path, SOCKETNAME); 13813816Ssam if (bind(funix, &sun, strlen(sun.sun_path) + 2) < 0) { 13916761Sralph syslog(LOG_ERR, "ubind: %m"); 14012106Sralph exit(1); 14112106Sralph } 14213816Ssam sigsetmask(omask); 14314149Sralph defreadfds = 1 << funix; 14414149Sralph listen(funix, 5); 14513816Ssam finet = socket(AF_INET, SOCK_STREAM, 0); 14613816Ssam if (finet >= 0) { 14713816Ssam struct servent *sp; 14813816Ssam 14913816Ssam if (options & SO_DEBUG) 15013816Ssam if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 15116761Sralph syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 15216761Sralph mcleanup(); 15313816Ssam } 15413816Ssam sp = getservbyname("printer", "tcp"); 15513816Ssam if (sp == NULL) { 15616761Sralph syslog(LOG_ERR, "printer/tcp: unknown service"); 15716761Sralph mcleanup(); 15813816Ssam } 15913816Ssam sin.sin_family = AF_INET; 16013816Ssam sin.sin_port = sp->s_port; 16113816Ssam if (bind(finet, &sin, sizeof(sin), 0) < 0) { 16216761Sralph syslog(LOG_ERR, "bind: %m"); 16316761Sralph mcleanup(); 16413816Ssam } 16514149Sralph defreadfds |= 1 << finet; 16614149Sralph listen(finet, 5); 16713816Ssam } 16812106Sralph /* 16914149Sralph * Main loop: accept, do a request, continue. 17012106Sralph */ 17112106Sralph for (;;) { 17213957Ssam int domain, nfds, s, readfds = defreadfds; 17312106Sralph 17413957Ssam nfds = select(20, &readfds, 0, 0, 0); 17513957Ssam if (nfds <= 0) { 17616761Sralph if (nfds < 0 && errno != EINTR) 17716761Sralph syslog(LOG_WARNING, "select: %m"); 17813957Ssam continue; 17913957Ssam } 18013816Ssam if (readfds & (1 << funix)) { 18113957Ssam domain = AF_UNIX, fromlen = sizeof(fromunix); 18213816Ssam s = accept(funix, &fromunix, &fromlen); 18313957Ssam } else if (readfds & (1 << finet)) { 18413957Ssam domain = AF_INET, fromlen = sizeof(frominet); 18513816Ssam s = accept(finet, &frominet, &fromlen); 18613816Ssam } 18712106Sralph if (s < 0) { 18816761Sralph if (errno != EINTR) 18916761Sralph syslog(LOG_WARNING, "accept: %m"); 19016761Sralph continue; 19112106Sralph } 19212106Sralph if (fork() == 0) { 19313147Ssam signal(SIGCHLD, SIG_IGN); 19414708Sralph signal(SIGHUP, SIG_IGN); 19514708Sralph signal(SIGINT, SIG_IGN); 19614708Sralph signal(SIGQUIT, SIG_IGN); 19714708Sralph signal(SIGTERM, SIG_IGN); 19813816Ssam (void) close(funix); 19913816Ssam (void) close(finet); 20013816Ssam dup2(s, 1); 20113816Ssam (void) close(s); 20213816Ssam if (domain == AF_INET) 20313816Ssam chkhost(&frominet); 20413816Ssam doit(); 20512106Sralph exit(0); 20612106Sralph } 20712106Sralph (void) close(s); 20812106Sralph } 20912106Sralph } 21012106Sralph 21112106Sralph reapchild() 21212106Sralph { 21312106Sralph union wait status; 21412106Sralph 21512106Sralph while (wait3(&status, WNOHANG, 0) > 0) 21612106Sralph ; 21712106Sralph } 21812106Sralph 21916761Sralph mcleanup() 22013816Ssam { 22114149Sralph if (lflag) 22216761Sralph syslog(LOG_INFO, "exiting"); 22313816Ssam unlink(SOCKETNAME); 22413816Ssam exit(0); 22513816Ssam } 22613816Ssam 22712106Sralph /* 22812106Sralph * Stuff for handling job specifications 22912106Sralph */ 23012106Sralph char *user[MAXUSERS]; /* users to process */ 23112106Sralph int users; /* # of users in user array */ 23212106Sralph int requ[MAXREQUESTS]; /* job number of spool entries */ 23312106Sralph int requests; /* # of spool requests */ 23412875Sralph char *person; /* name of person doing lprm */ 23512106Sralph 23616761Sralph char fromb[32]; /* buffer for client's machine name */ 23716761Sralph char cbuf[BUFSIZ]; /* command line buffer */ 23816761Sralph char *cmdnames[] = { 23912430Sralph "null", 24012430Sralph "printjob", 24112430Sralph "recvjob", 24212430Sralph "displayq short", 24312430Sralph "displayq long", 24412430Sralph "rmjob" 24512430Sralph }; 24612106Sralph 24713816Ssam doit() 24812106Sralph { 24912106Sralph register char *cp; 25012106Sralph register int n; 25112106Sralph 25212106Sralph for (;;) { 25312106Sralph cp = cbuf; 25412106Sralph do { 25513816Ssam if (cp >= &cbuf[sizeof(cbuf) - 1]) 25613816Ssam fatal("Command line too long"); 25713816Ssam if ((n = read(1, cp, 1)) != 1) { 25812106Sralph if (n < 0) 25912106Sralph fatal("Lost connection"); 26012106Sralph return; 26112106Sralph } 26213816Ssam } while (*cp++ != '\n'); 26312106Sralph *--cp = '\0'; 26412106Sralph cp = cbuf; 26516761Sralph if (lflag) { 26616761Sralph if (*cp >= '\1' && *cp <= '\5') 26716761Sralph syslog(LOG_INFO, "%s requests %s %s", 26816761Sralph from, cmdnames[*cp], cp+1); 26916761Sralph else 27016761Sralph syslog(LOG_INFO, "bad request (%d) from %s", 27116761Sralph *cp, from); 27212430Sralph } 27312106Sralph switch (*cp++) { 27412106Sralph case '\1': /* check the queue and print any jobs there */ 27512106Sralph printer = cp; 27612106Sralph printjob(); 27712106Sralph break; 27812106Sralph case '\2': /* receive files to be queued */ 27912106Sralph printer = cp; 28012106Sralph recvjob(); 28112106Sralph break; 28212430Sralph case '\3': /* display the queue (short form) */ 28312430Sralph case '\4': /* display the queue (long form) */ 28412106Sralph printer = cp; 28512106Sralph while (*cp) { 28612106Sralph if (*cp != ' ') { 28712106Sralph cp++; 28812106Sralph continue; 28912106Sralph } 29012106Sralph *cp++ = '\0'; 29112106Sralph while (isspace(*cp)) 29212106Sralph cp++; 29312106Sralph if (*cp == '\0') 29412106Sralph break; 29512106Sralph if (isdigit(*cp)) { 29612106Sralph if (requests >= MAXREQUESTS) 29712106Sralph fatal("Too many requests"); 29812106Sralph requ[requests++] = atoi(cp); 29912106Sralph } else { 30012106Sralph if (users >= MAXUSERS) 30112106Sralph fatal("Too many users"); 30212106Sralph user[users++] = cp; 30312106Sralph } 30412106Sralph } 30512106Sralph displayq(cbuf[0] - '\3'); 30612106Sralph exit(0); 30712106Sralph case '\5': /* remove a job from the queue */ 30812106Sralph printer = cp; 30912106Sralph while (*cp && *cp != ' ') 31012106Sralph cp++; 31112106Sralph if (!*cp) 31212106Sralph break; 31312106Sralph *cp++ = '\0'; 31412106Sralph person = cp; 31512106Sralph while (*cp) { 31612106Sralph if (*cp != ' ') { 31712106Sralph cp++; 31812106Sralph continue; 31912106Sralph } 32012106Sralph *cp++ = '\0'; 32112106Sralph while (isspace(*cp)) 32212106Sralph cp++; 32312106Sralph if (*cp == '\0') 32412106Sralph break; 32512106Sralph if (isdigit(*cp)) { 32612106Sralph if (requests >= MAXREQUESTS) 32712106Sralph fatal("Too many requests"); 32812106Sralph requ[requests++] = atoi(cp); 32912106Sralph } else { 33012106Sralph if (users >= MAXUSERS) 33112106Sralph fatal("Too many users"); 33212106Sralph user[users++] = cp; 33312106Sralph } 33412106Sralph } 33512106Sralph rmjob(); 33612106Sralph break; 33712106Sralph } 33812106Sralph fatal("Illegal service request"); 33912106Sralph } 34012106Sralph } 34112106Sralph 34212106Sralph /* 34312430Sralph * Make a pass through the printcap database and start printing any 34412430Sralph * files left from the last time the machine went down. 34512430Sralph */ 34612430Sralph startup() 34712430Sralph { 34812430Sralph char buf[BUFSIZ]; 34912430Sralph register char *cp; 35012430Sralph int pid; 35112430Sralph 35212430Sralph printer = buf; 35312430Sralph 35412430Sralph /* 35512430Sralph * Restart the daemons. 35612430Sralph */ 35712430Sralph while (getprent(buf) > 0) { 35812430Sralph for (cp = buf; *cp; cp++) 35912430Sralph if (*cp == '|' || *cp == ':') { 36012430Sralph *cp = '\0'; 36112430Sralph break; 36212430Sralph } 36312430Sralph if ((pid = fork()) < 0) { 36416761Sralph syslog(LOG_WARNING, "startup: cannot fork"); 36516761Sralph mcleanup(); 36612430Sralph } 36712430Sralph if (!pid) { 36812430Sralph endprent(); 36912430Sralph printjob(); 37012430Sralph } 37112430Sralph } 37212430Sralph } 37312430Sralph 37412430Sralph /* 37512430Sralph * Check to see if the from host has access to the line printer. 37612430Sralph */ 37713816Ssam chkhost(f) 37813816Ssam struct sockaddr_in *f; 37912430Sralph { 38013816Ssam register struct hostent *hp; 38112430Sralph register FILE *hostf; 38212430Sralph register char *cp; 38312430Sralph char ahost[50]; 38415551Sralph int first = 1; 38513816Ssam extern char *inet_ntoa(); 38612430Sralph 38713816Ssam f->sin_port = ntohs(f->sin_port); 38813816Ssam if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 38913816Ssam fatal("Malformed from address"); 39013816Ssam hp = gethostbyaddr(&f->sin_addr, sizeof(struct in_addr), f->sin_family); 39113816Ssam if (hp == 0) 39213816Ssam fatal("Host name for your address (%s) unknown", 39313816Ssam inet_ntoa(f->sin_addr)); 39413816Ssam 39513816Ssam strcpy(fromb, hp->h_name); 39613816Ssam from = fromb; 39712734Sralph if (!strcmp(from, host)) 39813816Ssam return; 39912734Sralph 40012430Sralph hostf = fopen("/etc/hosts.equiv", "r"); 40115551Sralph again: 40215551Sralph if (hostf) { 40315551Sralph while (fgets(ahost, sizeof (ahost), hostf)) { 40415551Sralph if (cp = index(ahost, '\n')) 40515551Sralph *cp = '\0'; 40615551Sralph cp = index(ahost, ' '); 40715551Sralph if (!strcmp(from, ahost) && cp == NULL) { 40815551Sralph (void) fclose(hostf); 40915551Sralph return; 41015551Sralph } 41112430Sralph } 41215551Sralph (void) fclose(hostf); 41312430Sralph } 41415551Sralph if (first == 1) { 41515551Sralph first = 0; 41615551Sralph hostf = fopen("/etc/hosts.lpd", "r"); 41715551Sralph goto again; 41815551Sralph } 41913816Ssam fatal("Your host does not have line printer access"); 42012430Sralph } 421