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