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