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