xref: /plan9/sys/src/cmd/lp/lpdaemon.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /* these includes are needed for plan 9 ape */
2 #include <sys/types.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <sys/wait.h>
6 #include <fcntl.h>
7 
8 #include <stdio.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <time.h>
12 #include <string.h>
13 
14 /* for Plan 9 */
15 #ifdef PLAN9
16 #define LP	"/bin/lp"
17 #define TMPDIR "/sys/lib/lp/tmp"
18 #define LPDAEMONLOG	"/sys/lib/lp/log/lpdaemonl"
19 #endif
20 /* for Tenth Edition systems */
21 #ifdef V10
22 #define LP	"/usr/bin/lp"
23 #define TMPDIR "/tmp"
24 #define LPDAEMONLOG	"/tmp/lpdaemonl"
25 #endif
26 /* for System V or BSD systems */
27 #if defined(SYSV) || defined(BSD)
28 #define LP	"/v/bin/lp"
29 #define TMPDIR "/tmp"
30 #define LPDAEMONLOG	"/tmp/lpdaemonl"
31 #endif
32 
33 #define ARGSIZ 4096
34 #define NAMELEN 30
35 
36 char argvstr[ARGSIZ];		/* arguments after parsing */
37 char *argvals[ARGSIZ/2+1];	/* pointers to arguments after parsing */
38 int ascnt = 0, argcnt = 0;	/* number of arguments parsed */
39 /* for 'stuff' gleened from lpr cntrl file */
40 struct jobinfo {
41 	char user[NAMELEN+1];
42 	char host[NAMELEN+1];
43 } *getjobinfo();
44 
45 #define MIN(a,b)	((a<b)?a:b)
46 
47 #define	CPYFIELD(src, dst)	{ while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; }
48 
49 #define	ACK()	write(1, "", 1)
50 #define NAK()	write(1, "\001", 1)
51 
52 #define LNBFSZ	4096
53 char lnbuf[LNBFSZ];
54 int readline();
55 
56 #define	RDSIZE 512
57 char jobbuf[RDSIZE];
58 
59 int datafd[400], cntrlfd;
60 
61 int dbgstate = 0;
62 char *dbgstrings[] = {
63 	"",
64 	"sendack1",
65 	"send",
66 	"rcvack",
67 	"sendack2",
68 	"done"
69 };
70 
71 void
72 error(char *s1, ...)
73 {
74 	FILE *fp;
75 	long thetime;
76 	char *chartime;
77 	va_list ap;
78 	char *args[8];
79 	int argno = 0;
80 
81 	if((fp=fopen(LPDAEMONLOG, "a"))==NULL) {
82 fprintf(stderr, "fopen of %s failed\n", LPDAEMONLOG);
83 		return;
84 	}
85 
86 	time(&thetime);
87 	chartime = ctime(&thetime);
88 	fprintf(fp, "%.15s ", &(chartime[4]));
89 	va_start(ap, s1);
90 	while((args[argno++] = va_arg(ap, char*)) && argno<8);
91 	va_end(ap);
92 	fprintf(fp, s1, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
93 	fclose(fp);
94 	return;
95 }
96 
97 void
98 forklp(int inputfd)
99 {
100 	int i, cpid;
101 	char *bp, *cp;
102 	char logent[LNBFSZ];
103 
104 	/* log this call to lp */
105 	cp = logent;
106 	for (i=1; i<argcnt; i++) {
107 		bp = argvals[i];
108 		if (cp+strlen(bp)+1 < logent+LNBFSZ-1) {
109 			CPYFIELD(bp, cp);
110 			*cp++ = ' ';
111 		}
112 	}
113 	*--cp = '\n';
114 	*++cp = '\0';
115 	error(logent);
116 	switch((cpid=fork())){
117 	case -1:
118 		error("fork error\n");
119 		exit(2);
120 	case 0:
121 		if (inputfd != 0)
122 			dup2(inputfd, 0);
123 		dup2(1, 2);
124 		lseek(0, 0L, 0);
125 		execvp(LP, argvals);
126 		error("exec failed\n");
127 		exit(3);
128 	default:
129 		while(wait((int *)0) != cpid);
130 	}
131 }
132 
133 int
134 tempfile(void)
135 {
136 	static tindx = 0;
137 	char tmpf[20];
138 	int crtfd, tmpfd;
139 
140 	sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++);
141 	if((crtfd=creat(tmpf, 0666)) < 0) {
142 		error("cannot create temp file %s\n", tmpf);
143 		NAK();
144 		exit(3);
145 	}
146 	if((tmpfd=open(tmpf, 2)) < 0) {
147 		error("cannot open temp file %s\n", tmpf);
148 		NAK();
149 		exit(3);
150 	}
151 	close(crtfd);
152 	unlink(tmpf);	/* comment out for debugging */
153 	return(tmpfd);
154 }
155 
156 int
157 readfile(int outfd, int bsize)
158 {
159 	int rv;
160 
161 	dbgstate = 1;
162 	alarm(60);
163 	ACK();
164 	dbgstate = 2;
165 	for(; bsize > 0; bsize -= rv) {
166 		alarm(60);
167 		if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) {
168 			error("error reading input, %d unread\n", bsize);
169 			exit(4);
170 		} else if((write(outfd, jobbuf, rv)) != rv) {
171 			error("error writing temp file, %d unread\n", bsize);
172 			exit(5);
173 		}
174 	}
175 	dbgstate = 3;
176 	alarm(60);
177 	if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) {
178 		alarm(60);
179 		ACK();
180 		dbgstate = 4;
181 		alarm(0);
182 		return(outfd);
183 	}
184 	alarm(0);
185 	error("received bad status <%d> from sender\n", *jobbuf);
186 	error("rv=%d\n", rv);
187 	NAK();
188 	return(-1);
189 }
190 
191 /* reads a line from the input into lnbuf
192  * if there is no error, it returns
193  *   the number of characters in the buffer
194  * if there is an error and there where characters
195  *   read, it returns the negative value of the
196  *   number of characters read
197  * if there is an error and no characters were read,
198  *   it returns the negative value of 1 greater than
199  *   the size of the line buffer
200  */
201 int
202 readline(int inpfd)
203 {
204 	char *ap;
205 	int i, rv;
206 
207 	ap = lnbuf;
208 	i = 0;
209 	do {
210 		rv = read(inpfd, ap, 1);
211 	} while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2));
212 	if (i != 0 && *ap != '\n') {
213 		*++ap = '\n';
214 		i++;
215 	}
216 	*++ap = '\0';
217 	if (rv < 0) {
218 		error("read error; lost connection\n");
219 		if (i==0) i = -(LNBFSZ+1);
220 		else i = -i;
221 	}
222 	return(i);
223 }
224 
225 int
226 getfiles(void)
227 {
228 	char *ap;
229 	int filecnt, bsize, rv;
230 
231 	filecnt = 0;
232 	/* get a line, hopefully containing a ctrl char, size, and name */
233 	for(;;) {
234 		ap = lnbuf;
235 		if ((rv=readline(0)) < 0) NAK();
236 		if (rv <= 0) return(filecnt);
237 		switch(*ap++) {
238 		case '\1':		/* cleanup - data sent was bad (whatever that means) */
239 			break;
240 		case '\2':		/* read control file */
241 			bsize = atoi(ap);
242 			cntrlfd = tempfile();
243 			if (readfile(cntrlfd, bsize) < 0) {
244 				close(cntrlfd);
245 				NAK();
246 				return(0);
247 			}
248 			break;
249 		case '\3':		/* read data file */
250 			bsize = atoi(ap);
251 			datafd[filecnt] = tempfile();
252 			if (readfile(datafd[filecnt], bsize) < 0) {
253 				close(datafd[filecnt]);
254 				NAK();
255 				return(0);
256 			}
257 			filecnt++;
258 			break;
259 		default:
260 			error("protocol error <%d>\n", *(ap-1));
261 			NAK();
262 		}
263 	}
264 	return(filecnt);
265 }
266 
267 struct jobinfo *
268 getjobinfo(int fd)
269 {
270 	register char *ap;
271 	int rv;
272 	static struct jobinfo info;
273 
274 	if (lseek(fd, 0L, 0) < 0) {
275 		error("error seeking in temp file\n");
276 		exit(7);
277 	}
278 	/* the following strings should be < NAMELEN or else they will not
279 	 * be null terminated.
280 	 */
281 	strncpy(info.user, "daemon", NAMELEN);
282 	strncpy(info.host, "nowhere", NAMELEN);
283 	/* there may be a space after the name and host.  It will be filtered out
284 	 * by CPYFIELD.
285 	 */
286 	while ((rv=readline(fd)) > 0) {
287 		ap = lnbuf;
288 		ap[rv-1] = '\0';	/* remove newline from string */
289 		switch (*ap) {
290 		case 'H':
291 			if (ap[1] == '\0')
292 				strncpy(info.host, "unknown", NAMELEN);
293 			else
294 				strncpy(info.host, &ap[1], NAMELEN);
295 			info.host[strlen(info.host)] = '\0';
296 			break;
297 		case 'P':
298 			if (ap[1] == '\0')
299 				strncpy(info.user, "unknown", NAMELEN);
300 			else
301 				strncpy(info.user, &ap[1], NAMELEN);
302 			info.user[strlen(info.user)] = '\0';
303 			break;
304 		}
305 	}
306 	return(&info);
307 }
308 
309 void
310 alarmhandler(int sig) {
311 	signal(sig, alarmhandler);
312 	error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]);
313 }
314 
315 main()
316 {
317 	char *ap, *bp, *cp, *savbufpnt;
318 	int i, blen, rv, saveflg, savargcnt;
319 	struct jobinfo *jinfop;
320 
321 	signal(1, SIG_IGN);		/* SIGHUP not in lcc */
322 	signal(14, alarmhandler);	/* SIGALRM not in lcc */
323 	cp = argvstr;
324 	/* setup argv[0] for exec */
325 	argvals[argcnt++] = cp;
326 	for (bp = LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++);
327 	*cp++ = '\0';
328 	/* get the first line sent and parse it as arguments for lp */
329 	if ((rv=readline(0)) < 0)
330 		exit(1);
331 	ap = lnbuf;
332 	bp = ap;
333 	/* setup the remaining arguments */
334 	/* check for BSD style request */
335 	/* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */
336 	switch (*bp) {
337 	case '\001':
338 	case '\003':
339 	case '\004':
340 		bp++;	/* drop the ctrl character from the input */
341 		argvals[argcnt++] = cp;
342 		*cp++ = '-'; *cp++ = 'q'; *cp++ = '\0';		/* -q */
343 		argvals[argcnt++] = cp;
344 		*cp++ = '-'; *cp++ = 'd'; 			/* -d */
345 		CPYFIELD(bp, cp);				/* printer */
346 		*cp++ = '\0';
347 		break;
348 	case '\002':
349 		bp++;	/* drop the ctrl character from the input */
350 		argvals[argcnt++] = cp;
351 		*cp++ = '-'; *cp++ = 'd'; 			/* -d */
352 		CPYFIELD(bp, cp);				/* printer */
353 		*cp++ = '\0';
354 		ACK();
355 		savargcnt = argcnt;
356 		savbufpnt = cp;
357 		while ((rv=getfiles())) {
358 			jinfop = getjobinfo(cntrlfd);
359 			close(cntrlfd);
360 			argcnt = savargcnt;
361 			cp = savbufpnt;
362 			argvals[argcnt++] = cp;
363 			*cp++ = '-'; *cp++ = 'M'; 			/* -M */
364 			bp = jinfop->host;
365 			CPYFIELD(bp, cp);				/* host name */
366 			*cp++ = '\0';
367 			argvals[argcnt++] = cp;
368 			*cp++ = '-'; *cp++ = 'u'; 			/* -u */
369 			bp = jinfop->user;
370 			CPYFIELD(bp, cp);				/* user name */
371 			*cp++ = '\0';
372 			for(i=0;i<rv;i++)
373 				forklp(datafd[i]);
374 		}
375 		exit(0);
376 	case '\005':
377 		bp++;	/* drop the ctrl character from the input */
378 		argvals[argcnt++] = cp;
379 		*cp++ = '-'; *cp++ = 'k'; *cp++ = '\0';		/* -k */
380 		argvals[argcnt++] = cp;
381 		*cp++ = '-'; *cp++ = 'd'; 			/* -d */
382 		CPYFIELD(bp, cp);				/* printer */
383 		*cp++ = '\0';
384 		argvals[argcnt++] = cp;
385 		*cp++ = '-'; *cp++ = 'u'; 			/* -u */
386 		CPYFIELD(bp, cp);				/* username */
387 		*cp++ = '\0';
388 		datafd[0] = tempfile();
389 		blen = strlen(bp);
390 		if (write(datafd[0], bp, blen) != blen) {
391 			error("write error\n");
392 			exit(6);
393 		}
394 		if (write(datafd[0], "\n", 1) != 1) {
395 			error("write error\n");
396 			exit(6);
397 		}
398 		break;
399 	default:
400 		/* otherwise get my lp arguments */
401 		do {
402 			/* move to next non-white space */
403 			while (*bp==' '||*bp=='\t')
404 				++bp;
405 			if (*bp=='\n') continue;
406 			/* only accept arguments beginning with -
407 			 * this is done to prevent the printing of
408 			 * local files from the destination host
409 			 */
410 			if (*bp=='-') {
411 				argvals[argcnt++] = cp;
412 				saveflg = 1;
413 			} else
414 				saveflg = 0;
415 			/* move to next white space copying text to argument buffer */
416 			while (*bp!=' ' && *bp!='\t' && *bp!='\n'
417 			    && *bp!='\0') {
418 				*cp = *bp++;
419 				cp += saveflg;
420 			}
421 			*cp = '\0';
422 			cp += saveflg;
423 		} while (*bp!='\n');
424 		if (readline(0) == 0) exit(7);
425 		datafd[0] = tempfile();
426 		if(readfile(datafd[0], atoi(lnbuf)) < 0) {
427 			error("readfile failed\n");
428 			exit(8);
429 		}
430 	}
431 	forklp(datafd[0]);
432 	exit(0);
433 }
434