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