Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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