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: }

Defined functions

chkhost defined in line 398; used 1 times
doit defined in line 265; used 1 times
main defined in line 52; never used
mcleanup defined in line 232; used 9 times
reapchild defined in line 224; used 2 times
startup defined in line 364; used 2 times
unpause defined in line 240; used 2 times

Defined variables

cbuf defined in line 255; used 5 times
cmdnames defined in line 256; used 1 times
copyright defined in line 8; never used
fromb defined in line 254; used 4 times
lflag defined in line 46; used 3 times
person defined in line 252; used 1 times
requ defined in line 250; used 2 times
requests defined in line 251; used 4 times
sccsid defined in line 12; never used
unblock defined in line 50; used 3 times
user defined in line 248; used 2 times
users defined in line 249; used 4 times

Defined macros

DUMMY defined in line 393; used 2 times
  • in line 437(2)
Last modified: 1994-01-11
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 4487
Valid CSS Valid XHTML 1.0 Strict