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