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