BSD 4_2 release
[unix-history] / usr / src / usr.lib / lpr / lpd.c
CommitLineData
3a7f30af 1#ifndef lint
0f4556f1 2static 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
42static int lflag; /* log requests flag */
43static char *logfile = DEFLOGF;
54266d1f 44
7245937a 45int reapchild();
8c4b7b45 46int cleanup();
7245937a
RC
47
48main(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 213static
7245937a
RC
214reapchild()
215{
216 union wait status;
217
218 while (wait3(&status, WNOHANG, 0) > 0)
219 ;
220}
221
8c4b7b45
SL
222static
223cleanup()
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 */
234char *user[MAXUSERS]; /* users to process */
235int users; /* # of users in user array */
236int requ[MAXREQUESTS]; /* job number of spool entries */
237int requests; /* # of spool requests */
54266d1f 238char *person; /* name of person doing lprm */
7245937a 239
54266d1f
RC
240static char fromb[32]; /* buffer for client's machine name */
241static char cbuf[BUFSIZ]; /* command line buffer */
242static char *cmdnames[] = {
4d4caa50
RC
243 "null",
244 "printjob",
245 "recvjob",
246 "displayq short",
247 "displayq long",
248 "rmjob"
249};
7245937a 250
54266d1f 251static
8c4b7b45 252doit()
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 347static
4d4caa50
RC
348startup()
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 379static
8c4b7b45
SL
380chkhost(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*/
416log(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 431static
8c4b7b45 432logerr(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}