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