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