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