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