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