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