xref: /original-bsd/usr.sbin/lpr/lpd/lpd.c (revision 9cf5e8d3)
1 /*	lpd.c	4.1	83/04/29	*/
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.
17  *
18  * Strategy to maintain protected spooling area:
19  *	1. Spooling area is writable only by daemon and spooling group
20  *	2. lpr runs setuid root and setgrp spooling group; it uses
21  *	   root to access any file it wants (verifying things before
22  *	   with an access call) and group id to know how it should
23  *	   set up ownership of files in the spooling area.
24  *	3. Files in spooling area are owned by the owner, group spooling
25  *	   group, with mode 660.
26  *	4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
27  *	   access files and printer.  Users can't get to anything
28  *	   w/o help of lpq and lprm programs.
29  */
30 
31 #include "lp.h"
32 
33 char	*logfile = DEFLOGF;
34 struct	sockaddr_in sin = { AF_INET };
35 int	reapchild();
36 char	*ntoa();
37 
38 main(argc, argv)
39 	int argc;
40 	char **argv;
41 {
42 	int f, options;
43 	struct sockaddr_in from;
44 	struct servent *sp;
45 
46 	gethostname(host, sizeof(host));
47 	name = argv[0];
48 
49 	sp = getservbyname("printer", "tcp");
50 	if (sp == NULL) {
51 		log("printer/tcp: unknown service");
52 		exit(1);
53 	}
54 	sin.sin_port = sp->s_port;
55 
56 	while (--argc > 0) {
57 		argv++;
58 		if (argv[0][0] == '-')
59 			switch (argv[0][1]) {
60 			case 'd':
61 				options |= SO_DEBUG;
62 				break;
63 			case 'l':
64 				argc--;
65 				logfile = *++argv;
66 				break;
67 			}
68 		else {
69 			int port = atoi(argv[0]);
70 
71 			if (port < 0) {
72 				fprintf(stderr, "%s: bad port #\n", argv[0]);
73 				exit(1);
74 			}
75 			sin.sin_port = htons((u_short) port);
76 		}
77 	}
78 #ifndef DEBUG
79 	/*
80 	 * Set up standard environment by detaching from the parent.
81 	 */
82 	if (fork())
83 		exit(0);
84 	for (f = 0; f < 3; f++)
85 		(void) close(f);
86 	(void) open("/dev/null", FRDONLY, 0);
87 	(void) open("/dev/null", FWRONLY, 0);
88 	(void) open(logfile, FWRONLY|FAPPEND, 0);
89 	f = open("/dev/tty", FRDWR, 0);
90 	if (f > 0) {
91 		ioctl(f, TIOCNOTTY, 0);
92 		(void) close(f);
93 	}
94 #endif
95 	(void) umask(0);
96 	f = socket(AF_INET, SOCK_STREAM, 0);
97 	if (f < 0) {
98 		logerror("socket");
99 		exit(1);
100 	}
101 	if (options & SO_DEBUG)
102 		if (setsockopt(f, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
103 			logerror("setsockopt (SO_DEBUG)");
104 	if (bind(f, &sin, sizeof(sin), 0) < 0) {
105 		logerror("bind");
106 		exit(1);
107 	}
108 	/*
109 	 * Restart all the printers and tell everyone that we are
110 	 * up and running.
111 	 */
112 	startup();
113 	/*
114 	 * Main loop: listen, accept, do a request, continue.
115 	 */
116 	sigset(SIGCHLD, reapchild);
117 	listen(f, 10);
118 	for (;;) {
119 		int s, len = sizeof(from);
120 
121 		s = accept(f, &from, &len, 0);
122 		if (s < 0) {
123 			if (errno == EINTR)
124 				continue;
125 			logerror("accept");
126 			continue;
127 		}
128 		if (fork() == 0) {
129 			sigset(SIGCHLD, SIG_IGN);
130 			(void) close(f);
131 			doit(s, &from);
132 			exit(0);
133 		}
134 		(void) close(s);
135 	}
136 }
137 
138 reapchild()
139 {
140 	union wait status;
141 
142 	while (wait3(&status, WNOHANG, 0) > 0)
143 		;
144 }
145 
146 /*
147  * Stuff for handling job specifications
148  */
149 char	*user[MAXUSERS];	/* users to process */
150 int	users;			/* # of users in user array */
151 int	requ[MAXREQUESTS];	/* job number of spool entries */
152 int	requests;		/* # of spool requests */
153 
154 char	cbuf[BUFSIZ];		/* command line buffer */
155 char	fromb[32];		/* buffer for client's machine name */
156 
157 doit(f, fromp)
158 	int f;
159 	struct sockaddr_in *fromp;
160 {
161 	register char *cp;
162 	register struct hostent *hp;
163 	register int n;
164 	extern char *person;
165 	char c;
166 
167 	dup2(f, 1);
168 	(void) close(f);
169 	f = 1;
170 	fromp->sin_port = ntohs(fromp->sin_port);
171 	if (fromp->sin_family != AF_INET || fromp->sin_port >= IPPORT_RESERVED)
172 		fatal("Malformed from address");
173 	hp = gethostbyaddr(&fromp->sin_addr, sizeof(struct in_addr),
174 		fromp->sin_family);
175 	if (hp == 0)
176 		fatal("Host name for your address (%s) unknown",
177 			ntoa(fromp->sin_addr));
178 	strcpy(fromb, hp->h_name);
179 	from = fromb;
180 	for (;;) {
181 		cp = cbuf;
182 		do {
183 			if ((n = read(f, &c, 1)) != 1) {
184 				if (n < 0)
185 					fatal("Lost connection");
186 				return;
187 			}
188 			if (cp >= &cbuf[sizeof(cbuf)])
189 				fatal("Command line too long");
190 			*cp++ = c;
191 		} while (c != '\n');
192 		*--cp = '\0';
193 		cp = cbuf;
194 		switch (*cp++) {
195 		case '\1':	/* check the queue and print any jobs there */
196 			printer = cp;
197 			printjob();
198 			break;
199 		case '\2':	/* receive files to be queued */
200 			printer = cp;
201 			recvjob();
202 			break;
203 		case '\3':	/* send back the short form queue status */
204 		case '\4':	/* send back the long form queue status */
205 			printer = cp;
206 			while (*cp) {
207 				if (*cp != ' ') {
208 					cp++;
209 					continue;
210 				}
211 				*cp++ = '\0';
212 				while (isspace(*cp))
213 					cp++;
214 				if (*cp == '\0')
215 					break;
216 				if (isdigit(*cp)) {
217 					if (requests >= MAXREQUESTS)
218 						fatal("Too many requests");
219 					requ[requests++] = atoi(cp);
220 				} else {
221 					if (users >= MAXUSERS)
222 						fatal("Too many users");
223 					user[users++] = cp;
224 				}
225 			}
226 			displayq(cbuf[0] - '\3');
227 			exit(0);
228 		case '\5':	/* remove a job from the queue */
229 			printer = cp;
230 			while (*cp && *cp != ' ')
231 				cp++;
232 			if (!*cp)
233 				break;
234 			*cp++ = '\0';
235 			person = cp;
236 			while (*cp) {
237 				if (*cp != ' ') {
238 					cp++;
239 					continue;
240 				}
241 				*cp++ = '\0';
242 				while (isspace(*cp))
243 					cp++;
244 				if (*cp == '\0')
245 					break;
246 				if (isdigit(*cp)) {
247 					if (requests >= MAXREQUESTS)
248 						fatal("Too many requests");
249 					requ[requests++] = atoi(cp);
250 				} else {
251 					if (users >= MAXUSERS)
252 						fatal("Too many users");
253 					user[users++] = cp;
254 				}
255 			}
256 			rmjob();
257 			break;
258 		}
259 		fatal("Illegal service request");
260 		exit(1);
261 	}
262 }
263 
264 /*
265  * Convert network-format internet address
266  * to base 256 d.d.d.d representation.
267  */
268 char *
269 ntoa(in)
270 	struct in_addr in;
271 {
272 	static char b[18];
273 	register char *p;
274 
275 	p = (char *)&in;
276 #define	UC(b)	(((int)b)&0xff)
277 	sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
278 	return (b);
279 }
280 
281 /*VARARGS1*/
282 log(msg, a1, a2, a3)
283 	char *msg;
284 {
285 	short console = isatty(fileno(stderr));
286 
287 	fprintf(stderr, console ? "\r\n%s: " : "%s: ", name);
288 	if (printer)
289 		fprintf(stderr, "%s: ", printer);
290 	fprintf(stderr, msg, a1, a2, a3);
291 	if (console)
292 		putc('\r', stderr);
293 	putc('\n', stderr);
294 	fflush(stderr);
295 }
296 
297 logerror(msg)
298 	char *msg;
299 {
300 	register int err = errno;
301 	short console = isatty(fileno(stderr));
302 	extern int sys_nerr;
303 	extern char *sys_errlist[];
304 
305 	fprintf(stderr, console ? "\r\n%s: " : "%s: ", name);
306 	if (*msg)
307 		fprintf(stderr, "%s: ", msg);
308 	fputs(err < sys_nerr ? sys_errlist[err] : "Unknown error" , stderr);
309 	if (console)
310 		putc('\r', stderr);
311 	putc('\n', stderr);
312 	fflush(stderr);
313 }
314