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