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.14 (Berkeley) 06/29/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 "lp.h" 48 #include "pathnames.h" 49 50 int lflag; /* log requests flag */ 51 int from_remote; /* from remote socket */ 52 53 void mcleanup(), reapchild(); 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 un, 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 un.sun_family = AF_UNIX; 128 strcpy(un.sun_path, _PATH_SOCKETNAME); 129 if (bind(funix, 130 (struct sockaddr *)&un, strlen(un.sun_path) + 2) < 0) { 131 syslog(LOG_ERR, "ubind: %m"); 132 exit(1); 133 } 134 sigsetmask(omask); 135 defreadfds = 1 << funix; 136 listen(funix, 5); 137 finet = socket(AF_INET, SOCK_STREAM, 0); 138 if (finet >= 0) { 139 struct servent *sp; 140 141 if (options & SO_DEBUG) 142 if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) { 143 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 144 mcleanup(); 145 } 146 sp = getservbyname("printer", "tcp"); 147 if (sp == NULL) { 148 syslog(LOG_ERR, "printer/tcp: unknown service"); 149 mcleanup(); 150 } 151 sin.sin_family = AF_INET; 152 sin.sin_port = sp->s_port; 153 if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 154 syslog(LOG_ERR, "bind: %m"); 155 mcleanup(); 156 } 157 defreadfds |= 1 << finet; 158 listen(finet, 5); 159 } 160 /* 161 * Main loop: accept, do a request, continue. 162 */ 163 for (;;) { 164 int domain, nfds, s, readfds = defreadfds; 165 166 nfds = select(20, &readfds, 0, 0, 0); 167 if (nfds <= 0) { 168 if (nfds < 0 && errno != EINTR) 169 syslog(LOG_WARNING, "select: %m"); 170 continue; 171 } 172 if (readfds & (1 << funix)) { 173 domain = AF_UNIX, fromlen = sizeof(fromunix); 174 s = accept(funix, 175 (struct sockaddr *)&fromunix, &fromlen); 176 } else if (readfds & (1 << finet)) { 177 domain = AF_INET, fromlen = sizeof(frominet); 178 s = accept(finet, 179 (struct sockaddr *)&frominet, &fromlen); 180 } 181 if (s < 0) { 182 if (errno != EINTR) 183 syslog(LOG_WARNING, "accept: %m"); 184 continue; 185 } 186 if (fork() == 0) { 187 signal(SIGCHLD, SIG_IGN); 188 signal(SIGHUP, SIG_IGN); 189 signal(SIGINT, SIG_IGN); 190 signal(SIGQUIT, SIG_IGN); 191 signal(SIGTERM, SIG_IGN); 192 (void) close(funix); 193 (void) close(finet); 194 dup2(s, 1); 195 (void) close(s); 196 if (domain == AF_INET) { 197 from_remote = 1; 198 chkhost(&frominet); 199 } else 200 from_remote = 0; 201 doit(); 202 exit(0); 203 } 204 (void) close(s); 205 } 206 } 207 208 void 209 reapchild() 210 { 211 union wait status; 212 213 while (wait3((int *)&status, WNOHANG, 0) > 0) 214 ; 215 } 216 217 void 218 mcleanup() 219 { 220 if (lflag) 221 syslog(LOG_INFO, "exiting"); 222 unlink(_PATH_SOCKETNAME); 223 exit(0); 224 } 225 226 /* 227 * Stuff for handling job specifications 228 */ 229 char *user[MAXUSERS]; /* users to process */ 230 int users; /* # of users in user array */ 231 int requ[MAXREQUESTS]; /* job number of spool entries */ 232 int requests; /* # of spool requests */ 233 char *person; /* name of person doing lprm */ 234 235 char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */ 236 char cbuf[BUFSIZ]; /* command line buffer */ 237 char *cmdnames[] = { 238 "null", 239 "printjob", 240 "recvjob", 241 "displayq short", 242 "displayq long", 243 "rmjob" 244 }; 245 246 doit() 247 { 248 register char *cp; 249 register int n; 250 251 for (;;) { 252 cp = cbuf; 253 do { 254 if (cp >= &cbuf[sizeof(cbuf) - 1]) 255 fatal("Command line too long"); 256 if ((n = read(1, cp, 1)) != 1) { 257 if (n < 0) 258 fatal("Lost connection"); 259 return; 260 } 261 } while (*cp++ != '\n'); 262 *--cp = '\0'; 263 cp = cbuf; 264 if (lflag) { 265 if (*cp >= '\1' && *cp <= '\5') 266 syslog(LOG_INFO, "%s requests %s %s", 267 from, cmdnames[*cp], cp+1); 268 else 269 syslog(LOG_INFO, "bad request (%d) from %s", 270 *cp, from); 271 } 272 switch (*cp++) { 273 case '\1': /* check the queue and print any jobs there */ 274 printer = cp; 275 printjob(); 276 break; 277 case '\2': /* receive files to be queued */ 278 if (!from_remote) { 279 syslog(LOG_INFO, "illegal request (%d)", *cp); 280 exit(1); 281 } 282 printer = cp; 283 recvjob(); 284 break; 285 case '\3': /* display the queue (short form) */ 286 case '\4': /* display the queue (long form) */ 287 printer = cp; 288 while (*cp) { 289 if (*cp != ' ') { 290 cp++; 291 continue; 292 } 293 *cp++ = '\0'; 294 while (isspace(*cp)) 295 cp++; 296 if (*cp == '\0') 297 break; 298 if (isdigit(*cp)) { 299 if (requests >= MAXREQUESTS) 300 fatal("Too many requests"); 301 requ[requests++] = atoi(cp); 302 } else { 303 if (users >= MAXUSERS) 304 fatal("Too many users"); 305 user[users++] = cp; 306 } 307 } 308 displayq(cbuf[0] - '\3'); 309 exit(0); 310 case '\5': /* remove a job from the queue */ 311 if (!from_remote) { 312 syslog(LOG_INFO, "illegal request (%d)", *cp); 313 exit(1); 314 } 315 printer = cp; 316 while (*cp && *cp != ' ') 317 cp++; 318 if (!*cp) 319 break; 320 *cp++ = '\0'; 321 person = cp; 322 while (*cp) { 323 if (*cp != ' ') { 324 cp++; 325 continue; 326 } 327 *cp++ = '\0'; 328 while (isspace(*cp)) 329 cp++; 330 if (*cp == '\0') 331 break; 332 if (isdigit(*cp)) { 333 if (requests >= MAXREQUESTS) 334 fatal("Too many requests"); 335 requ[requests++] = atoi(cp); 336 } else { 337 if (users >= MAXUSERS) 338 fatal("Too many users"); 339 user[users++] = cp; 340 } 341 } 342 rmjob(); 343 break; 344 } 345 fatal("Illegal service request"); 346 } 347 } 348 349 /* 350 * Make a pass through the printcap database and start printing any 351 * files left from the last time the machine went down. 352 */ 353 startup() 354 { 355 char buf[BUFSIZ]; 356 register char *cp; 357 int pid; 358 359 printer = buf; 360 361 /* 362 * Restart the daemons. 363 */ 364 while (getprent(buf) > 0) { 365 for (cp = buf; *cp; cp++) 366 if (*cp == '|' || *cp == ':') { 367 *cp = '\0'; 368 break; 369 } 370 if ((pid = fork()) < 0) { 371 syslog(LOG_WARNING, "startup: cannot fork"); 372 mcleanup(); 373 } 374 if (!pid) { 375 endprent(); 376 printjob(); 377 } 378 } 379 } 380 381 #define DUMMY ":nobody::" 382 383 /* 384 * Check to see if the from host has access to the line printer. 385 */ 386 chkhost(f) 387 struct sockaddr_in *f; 388 { 389 register struct hostent *hp; 390 register FILE *hostf; 391 register char *cp, *sp; 392 int first = 1; 393 extern char *inet_ntoa(); 394 395 f->sin_port = ntohs(f->sin_port); 396 if (f->sin_family != AF_INET || f->sin_port >= IPPORT_RESERVED) 397 fatal("Malformed from address"); 398 399 /* Need real hostname for temporary filenames */ 400 hp = gethostbyaddr((char *)&f->sin_addr, 401 sizeof(struct in_addr), f->sin_family); 402 if (hp == NULL) 403 fatal("Host name for your address (%s) unknown", 404 inet_ntoa(f->sin_addr)); 405 406 (void) strncpy(fromb, hp->h_name, sizeof(fromb)); 407 from[sizeof(fromb) - 1] = '\0'; 408 from = fromb; 409 410 hostf = fopen(_PATH_HOSTSEQUIV, "r"); 411 again: 412 if (hostf) { 413 if (__ivaliduser(hostf, f->sin_addr.s_addr, 414 DUMMY, DUMMY) == 0) { 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 /*NOTREACHED*/ 427 } 428