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

Defined functions

chkhost defined in line 379; used 1 times
doit defined in line 247; used 1 times
main defined in line 53; never used
mcleanup defined in line 219; used 9 times
reapchild defined in line 211; used 2 times
startup defined in line 346; used 2 times

Defined variables

cbuf defined in line 237; used 7 times
cmdnames defined in line 238; used 1 times
copyright defined in line 8; never used
fromb defined in line 236; used 4 times
lflag defined in line 48; used 3 times
person defined in line 234; used 1 times
requ defined in line 232; used 2 times
requests defined in line 233; used 4 times
sccsid defined in line 14; never used
user defined in line 230; used 2 times
users defined in line 231; used 4 times

Defined macros

DUMMY defined in line 374; used 2 times
  • in line 418(2)
mask defined in line 130; used 4 times
  • in line 131(4)
Last modified: 1986-05-07
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 1899
Valid CSS Valid XHTML 1.0 Strict