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