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