plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

lpdaemon.c (9967B)


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