diff options
author | rsc <devnull@localhost> | 2005-08-31 02:18:29 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2005-08-31 02:18:29 +0000 |
commit | 262ecfed9f7e39811f34517d82e848b8ec20f863 (patch) | |
tree | b9947441be3e7b6a3d7c5e944854591e1b027bfb /src/cmd/lp | |
parent | 2863ba101f0c9fec34756948e263cd534a3634ee (diff) | |
download | plan9port-262ecfed9f7e39811f34517d82e848b8ec20f863.tar.gz plan9port-262ecfed9f7e39811f34517d82e848b8ec20f863.tar.bz2 plan9port-262ecfed9f7e39811f34517d82e848b8ec20f863.zip |
Initial lp.
Diffstat (limited to 'src/cmd/lp')
-rw-r--r-- | src/cmd/lp/LOCK.c | 57 | ||||
-rw-r--r-- | src/cmd/lp/ipcopen.c | 92 | ||||
-rwxr-xr-x | src/cmd/lp/lp.rc | 224 | ||||
-rw-r--r-- | src/cmd/lp/lpdaemon.c | 457 | ||||
-rw-r--r-- | src/cmd/lp/lpdsend.c | 428 | ||||
-rw-r--r-- | src/cmd/lp/lpsend.c | 341 | ||||
-rwxr-xr-x | src/cmd/lp/lpsend.rc | 18 | ||||
-rw-r--r-- | src/cmd/lp/mkfile | 30 |
8 files changed, 1647 insertions, 0 deletions
diff --git a/src/cmd/lp/LOCK.c b/src/cmd/lp/LOCK.c new file mode 100644 index 00000000..4ca1ec3a --- /dev/null +++ b/src/cmd/lp/LOCK.c @@ -0,0 +1,57 @@ +#include <u.h> +#include <libc.h> + +/* MAXHOSTNAMELEN is in sys/param.h */ +#define MAXHOSTNAMELEN 64 + +char lockstring[MAXHOSTNAMELEN+8]; + +void +main(int argc, char *argv[]) { + char *lockfile; + int fd, ppid, ssize; + struct Dir *statbuf; + + if (argc != 4) { + fprint(2, "usage: LOCK lockfile hostname ppid\n"); + exits("lock failed on usage"); + } + lockfile = argv[1]; + if ((fd=create(lockfile, OLOCK|ORDWR, 0666)) < 0) { + exits("lock failed on create"); + } + ppid = atoi(argv[3]); + ssize = sprint(lockstring, "%s %s\n", argv[2], argv[3]); + if (write(fd, lockstring, ssize) != ssize) { + fprint(2, "LOCK:write(): %r\n"); + exits("lock failed on write to lockfile"); + } + + switch(fork()) { + default: + exits(""); + case 0: + break; + case -1: + fprint(2, "LOCK:fork(): %r\n"); + exits("lock failed on fork"); + } + + for(;;) { + statbuf = dirfstat(fd); + if(statbuf == nil) + break; + if (statbuf->length == 0){ + free(statbuf); + break; + } + free(statbuf); + if (write(fd, "", 0) < 0) + break; + sleep(3000); + } + + close(fd); + postnote(PNGROUP, ppid, "kill"); + exits(""); +} diff --git a/src/cmd/lp/ipcopen.c b/src/cmd/lp/ipcopen.c new file mode 100644 index 00000000..666f64c4 --- /dev/null +++ b/src/cmd/lp/ipcopen.c @@ -0,0 +1,92 @@ +#include <u.h> +#include <libc.h> + +int ppid; + +/* + * predefined + */ +void pass(int from, int to); + + +/* + * Connect to given datakit port + */ +main(int argc, char *argv[]) +{ + int fd0, fd1; + int cpid; + char c; + char *cp, *devdir, *buf; + + if (argc != 4) { + fprint(2, "usage: %s destination network service\n", argv[0]); + exits("incorrect number of arguments"); + } + if(!(cp = malloc((long)(strlen(argv[1])+strlen(argv[2])+strlen(argv[3])+8)))) { + perror("malloc"); + exits("malloc failed"); + } + sprint(cp, "%s!%s!%s", argv[2], argv[1], argv[3]); + if (dial(cp, &devdir, 0) < 0) { + fprint(2, "dialing %s\n", cp); + perror("dial"); + exits("can't dial"); + } + + /* + * Initialize the input fd, and copy bytes. + */ + + if(!(buf = malloc((long)(strlen(devdir)+6)))) { + perror("malloc"); + exits("malloc failed"); + } + sprint(buf, "%s/data", devdir); + fd0=open(buf, OREAD); + fd1=open(buf, OWRITE); + if(fd0<0 || fd1<0) { + print("can't open", buf); + exits("can't open port"); + } + ppid = getpid(); + switch(cpid = fork()){ + case -1: + perror("fork failed"); + exits("fork failed"); + case 0: + close(0); + close(fd1); + pass(fd0, 1); /* from remote */ + hangup(fd0); + close(1); + close(fd0); + exits(""); + default: + close(1); + close(fd0); + pass(0, fd1); /* to remote */ + hangup(fd1); + close(0); + close(fd1); + exits(""); + } +} + +void +pass(int from, int to) +{ + char buf[1024]; + int ppid, cpid; + int n, tot = 0; + + while ((n=read(from, buf, sizeof(buf))) > 0) { + if (n==1 && tot==0 && *buf=='\0') + break; + tot += n; + if (write(to, buf, n)!=n) { + perror("pass write error"); + exits("pass write error"); + } + } +} diff --git a/src/cmd/lp/lp.rc b/src/cmd/lp/lp.rc new file mode 100755 index 00000000..fca6f068 --- /dev/null +++ b/src/cmd/lp/lp.rc @@ -0,0 +1,224 @@ +#!/usr/local/plan9/bin/rc +# This program enqueues the file to be printed and starts the daemon, when necessary. +# Make changes to /sys/src/cmd/lp/lp.rc. Changes made directly to /rc/bin/lp will be lost. + +# rfork en # so that environment and name space are not polluted +# +# put 'fn sigexit { rm /tmp/lpcrap; exit interrupted }' into processes that create /tmp/lpcrap. + +ifs=' +' # set ifs in case it is munged in user's environment + +LPLIB=$PLAN9/lp # lp scripts directories and configuration file are here +LPBIN=$PLAN9/bin/lpbin # lp specific binaries are here +LPSPOOL=$LPLIB/queue # lp queues +LPLOGDIR=$LPLIB/log # lp logs + +# $LPLIB/bin/lpscratch || exit $status +LPTMP=/var/tmp + +path=($PLAN9/bin /usr/local/bin /usr/bin /bin $LPLIB/bin $LPBIN) + +USAGE='usage: lp [-d printer] [-p process] [options] [files] + lp [-d printer] -q + lp [-d printer] -k jobnos + + options include: + -H no header + -L landscape mode + -c<n> make <n> copies + -f<font.size> specify font and size + -i<src> take media from <src> input bin + -l<n> print <n> lines per logical page + -m<n> magnify <n> times + -n<n> print <n> logical pages per physical page + -o<i-j,k> print only pages i-j and k + -r reverse pages + -x<n> x page offset in inches + -y<n> y page offset in inches' + +umask 000 # this doesn't work in plan 9 +THIS_HOST=$sysname +if(~ $#THIS_HOST 0) + THIS_HOST=`{hostname | sed 's/\..*//'} +if(~ $#THIS_HOST 0) + THIS_HOST=gnot + +# Helpers for scripts + +# Run a program from a /sys/lib/lp subdirectory. +fn lpsub { + _LPSUB=$1 + shift + _LPCMD=$1 + shift + @{path=($LPLIB/$_LPSUB $path); $LPLIB/$_LPSUB/$_LPCMD $*} +} + +# Run a command with standard input from file $1. +# If $1 is '', use the current standard input. +fn lpinput { + _LPFILE=$1 + shift + if(~ $_LPFILE '') $* + if not $* < $_LPFILE +} + +LPMACHID=$THIS_HOST +THIS_USERID=$user +LPUSERID=$THIS_USERID +LPLOC='' + +# Set default printer to be output device +if (~ $#LPDEST 0 && test -f $LPLIB/default) + LPDEST=`{cat $LPLIB/default} + +# Parse option parameters + +XOFF='' +YOFF='' +POINT='' +FONT='' +LINES='' +LAND='' +COPIES='' +MAG='' +NPAG='' +OLIST='' +IBIN='' +REVERSE='' +NOHEAD='' +TRAY='' +# remove FLAGS from environment +FLAGD=();FLAGH=();FLAGL=();FLAGM=();FLAGQ=();FLAGc=();FLAGd=();FLAGf=() +FLAGi=();FLAGk=();FLAGl=();FLAGm=();FLAGn=();FLAGo=();FLAGp=();FLAGq=() +FLAGr=();FLAGt=();FLAGu=();FLAGx=();FLAGy=() +# Process options +eval `{getflags DHLM:1QRc:1d:1f:1i:1kl:1m:1n:1o:1p:1qrt:1u:1x:1y:1 $*} +if (! ~ $status '') exit $status +if (! ~ $#FLAGD 0) { DEBUG=1; flag x + }; if not { DEBUG=''; flag x - } +if (! ~ $#FLAGH 0) NOHEAD=1; if not NOHEAD='' +if (! ~ $#FLAGL 0) LAND=1; if not LAND='' +# originating machine id (for information only) +if (! ~ $#FLAGM 0 && ~ $LPUSERID daemon) LPMACHID=$FLAGM +if (! ~ $#FLAGQ 0) QONLY=1; if not QONLY='' +if (! ~ $#FLAGR 0) RESET=1; if not RESET='' +if (! ~ $#FLAGc 0) COPIES=$FLAGc; if not COPIES=1 +if (! ~ $#FLAGd 0) { + switch ($FLAGd) { + case '?'; exec awk 'BEGIN{printf "device location host class\n"} + /^[^#]/ {printf "%-12s %-9s %-20s %s\n", $1, $2, $3, $6 }' $LPLIB/devices + case *; LPDEST=$FLAGd + } +} +if (! ~ $#FLAGf 0) eval `{echo $FLAGf | sed -e 's/([^.]*)\.([0-9.]*)/FONT=\1;POINT=\2;/'} +if (! ~ $#FLAGi 0) IBIN=$FLAGi +if (! ~ $#FLAGk 0) KILLFLAG=1; if not KILLFLAG=0 +if (! ~ $#FLAGl 0) LINES=$FLAGl +if (! ~ $#FLAGm 0) MAG=$FLAGm +if (! ~ $#FLAGn 0) NPAG=$FLAGn +if (! ~ $#FLAGo 0) OLIST=-o$FLAGo +if (! ~ $#FLAGp 0) { + switch (FLAGp) { + case '?';exec ls $LPLIB/process + case *; LPPROC=$FLAGp + } +} +if (! ~ $#FLAGq 0) LPQ=1; if not LPQ=0 +if (! ~ $#FLAGr 0) { + switch ($REVERSE) { + case '';REVERSE=1 + case 1; REVERSE='' + } +} +if (! ~ $#FLAGt 0) TRAY=$FLAGt +# originating user id +if (! ~ $#FLAGu 0) LPUSERID=$FLAGu +if (! ~ $#FLAGx 0) XOFF=$FLAGx +if (! ~ $#FLAGy 0) YOFF=$FLAGy + +if (~ $#LPDEST 0) { + echo 'Set environment variable LPDEST or use the +''-d printer'' option to set the destination.' >[1=2] + exit 'LPDEST not set' +} +if (~ $LPDEST */*) { # handles MHCC destinations like mh/lino + LPLOC=`{echo $LPDEST|sed 's/^(.*)\/(.*)/\1/'} + LPDEST=`{echo $LPDEST|sed 's/^(.*)\/(.*)/\2/'} +} + +# Fetch device info from devices file. + +LPDLINE=`{grep '^'$LPDEST'[ ]' $LPLIB/devices} +if (! ~ $status '') { + echo 'device '$LPDEST' is not in '$LPLIB'/devices' >[1=2] + exit 'LPDEST is bad' +} +LOC=$LPDLINE(2) +DEST_HOST=$LPDLINE(3) +OUTDEV=$LPDLINE(4) +SPEED=$LPDLINE(5) +LPCLASS=$LPDLINE(6) +if (~ $#LPPROC 0) LPPROC=$LPDLINE(7) +SPOOLER=$LPDLINE(8) +STAT=$LPDLINE(9) +KILL=$LPDLINE(10) +DAEMON=$LPDLINE(11) +SCHED=$LPDLINE(12) + +# On to the actual command-line processing. + +# lp -k +if (~ $KILLFLAG 1) + switch ($KILL) { + case - + echo kill option not available on $LPDEST >[1=2] + exit 'kill n/a' + case * + lpsub kill $KILL $* + exit $status + } + +# lp -q +if (~ $LPQ 1) + switch ($STAT) { + case - + echo queue status option not available on $LPDEST >[1=2] + exit 'stat option not available' + case * + lpsub stat $STAT $* </dev/null + exit $status + } + +# lp +DATE=`{date} +LPLOG=$LPLOGDIR/$LPDEST +if (! test -e $LPLOG) { + >$LPLOG + chmod +rwa $LPLOG >[2]/dev/null +} + +if (~ $RESET '') { # lp file + switch ($SPOOLER) { + case -; echo spooler does not exist for $LPDEST >[1=2] + exit 'no spooler' + case *; path=($LPLIB/spooler $path) + if (~ $#* 0) $SPOOLER + if not $SPOOLER $* + } +} + +if not { # lp -R + echo restarting daemon for printer $LPDEST >[1=2] + echo `{date} restarting daemon >>$LPLOG + UNLOCK $LPSPOOL/$LPDEST + sleep 5 +} + +# run daemon +if (~ $QONLY '') { # not lp -Q + if (! ~ $DAEMON -) { + lpsub daemon $DAEMON $* >>$LPLOG >[2=1] & + } +} +exit '' diff --git a/src/cmd/lp/lpdaemon.c b/src/cmd/lp/lpdaemon.c new file mode 100644 index 00000000..10b0aa3d --- /dev/null +++ b/src/cmd/lp/lpdaemon.c @@ -0,0 +1,457 @@ +#include <u.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <errno.h> +#include <time.h> +#include <string.h> +#include <stdarg.h> +#include <libc.h> + +#undef ctime +#undef wait + +/* for Plan 9 */ +#ifdef PLAN9 +#define LP "/bin/lp" +#define TMPDIR "/var/tmp" +#define LPDAEMONLOG unsharp("#9/lp/log/lpdaemonl") +#endif +/* for Tenth Edition systems */ +#ifdef V10 +#define LP "/usr/bin/lp" +#define TMPDIR "/tmp" +#define LPDAEMONLOG "/tmp/lpdaemonl" +#endif +/* for System V or BSD systems */ +#if defined(SYSV) || defined(BSD) +#define LP "/v/bin/lp" +#define TMPDIR "/tmp" +#define LPDAEMONLOG "/tmp/lpdaemonl" +#endif + +#define ARGSIZ 4096 +#define NAMELEN 30 + +unsigned char argvstr[ARGSIZ]; /* arguments after parsing */ +unsigned char *argvals[ARGSIZ/2+1]; /* pointers to arguments after parsing */ +int ascnt = 0, argcnt = 0; /* number of arguments parsed */ +/* for 'stuff' gleened from lpr cntrl file */ +struct jobinfo { + char user[NAMELEN+1]; + char host[NAMELEN+1]; +} *getjobinfo(); + +#define MIN(a,b) ((a<b)?a:b) + +#define CPYFIELD(src, dst) { while (*(src)!=' ' && *(src)!='\t' && *(src)!='\r' && *(src)!='\n' && *(src)!='\0') *(dst)++ = *(src)++; } + +#define ACK() write(1, "", 1) +#define NAK() write(1, "\001", 1) + +#define LNBFSZ 4096 +unsigned char lnbuf[LNBFSZ]; + +#define RDSIZE 512 +unsigned char jobbuf[RDSIZE]; + +int datafd[400], cntrlfd = -1; + +int dbgstate = 0; +char *dbgstrings[] = { + "", + "sendack1", + "send", + "rcvack", + "sendack2", + "done" +}; + +void +error(char *s1, ...) +{ + FILE *fp; + long thetime; + char *chartime; + va_list ap; + char *args[8]; + int argno = 0; + + if((fp=fopen(LPDAEMONLOG, "a"))==NULL) { + fprintf(stderr, "cannot open %s in append mode\n", LPDAEMONLOG); + return; + } + time(&thetime); + chartime = ctime(&thetime); + fprintf(fp, "%.15s [%5.5d] ", &(chartime[4]), getpid()); + va_start(ap, s1); + while((args[argno++] = va_arg(ap, char*)) && argno<8); + va_end(ap); + fprintf(fp, s1, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + fflush(fp); + fclose(fp); + return; +} + +void +forklp(int inputfd) +{ + int i, cpid; + unsigned char *bp, *cp; + unsigned char logent[LNBFSZ]; + + /* log this call to lp */ + cp = logent; + for (i=1; i<argcnt; i++) { + bp = argvals[i]; + if (cp+strlen((const char *)bp)+1 < logent+LNBFSZ-1) { + CPYFIELD(bp, cp); + *cp++ = ' '; + } + } + *--cp = '\n'; + *++cp = '\0'; + error(logent); + switch((cpid=fork())){ + case -1: + error("fork error\n"); + exit(2); + case 0: + if (inputfd != 0) + dup2(inputfd, 0); + dup2(1, 2); + lseek(0, 0L, 0); + execvp(LP, (char**)argvals); + error("exec failed\n"); + exit(3); + default: + while(wait((int *)0) != cpid); + } +} + +int +tempfile(void) +{ + int tindx = 0; + char tmpf[sizeof(TMPDIR)+64]; + int crtfd, tmpfd; + + sprintf(tmpf, "%s/lp%d.%d", TMPDIR, getpid(), tindx++); + if((crtfd=creat(tmpf, 0666)) < 0) { + error("cannot create temp file %s\n", tmpf); + NAK(); + exit(3); + } + if((tmpfd=open(tmpf, 2)) < 0) { + error("cannot open temp file %s\n", tmpf); + NAK(); + exit(3); + } + close(crtfd); + unlink(tmpf); /* comment out for debugging */ + return(tmpfd); +} + +int +readfile(int outfd, int bsize) +{ + int rv; + + dbgstate = 1; + alarm(60); + ACK(); + dbgstate = 2; + for(; bsize > 0; bsize -= rv) { + alarm(60); + if((rv=read(0, jobbuf, MIN(bsize,RDSIZE))) < 0) { + error("error reading input, %d unread\n", bsize); + exit(4); + } else if (rv == 0) { + error("connection closed prematurely\n"); + exit(4); + } else if((write(outfd, jobbuf, rv)) != rv) { + error("error writing temp file, %d unread\n", bsize); + exit(5); + } + } + dbgstate = 3; + alarm(60); + if (((rv=read(0, jobbuf, 1))==1) && (*jobbuf=='\0')) { + alarm(60); + ACK(); + dbgstate = 4; + alarm(0); + return(outfd); + } + alarm(0); + error("received bad status <%d> from sender\n", *jobbuf); + error("rv=%d\n", rv); + NAK(); + return(-1); +} + +/* reads a line from the input into lnbuf + * if there is no error, it returns + * the number of characters in the buffer + * if there is an error and there where characters + * read, it returns the negative value of the + * number of characters read + * if there is an error and no characters were read, + * it returns the negative value of 1 greater than + * the size of the line buffer + */ +int +readline(int inpfd) +{ + unsigned char *ap; + int i, rv; + + ap = lnbuf; + lnbuf[0] = '\0'; + i = 0; + alarm(60); + do { + rv = read(inpfd, ap, 1); + } while (rv==1 && ++i && *ap != '\n' && ap++ && (i < LNBFSZ - 2)); + alarm(0); + if (i != 0 && *ap != '\n') { + *++ap = '\n'; + i++; + } + *++ap = '\0'; + if (rv < 0) { + error("read error; lost connection\n"); + if (i==0) i = -(LNBFSZ+1); + else i = -i; + } + return(i); +} + +int +getfiles(void) +{ + unsigned char *ap; + int filecnt, bsize, rv; + + filecnt = 0; + /* get a line, hopefully containing a ctrl char, size, and name */ + for(;;) { + ap = lnbuf; + if ((rv=readline(0)) < 0) NAK(); + if (rv <= 0) { + return(filecnt); + } + switch(*ap++) { + case '\1': /* cleanup - data sent was bad (whatever that means) */ + break; + case '\2': /* read control file */ + bsize = atoi(ap); + cntrlfd = tempfile(); + if (readfile(cntrlfd, bsize) < 0) { + close(cntrlfd); + NAK(); + return(0); + } + break; + case '\3': /* read data file */ + bsize = atoi(ap); + datafd[filecnt] = tempfile(); + if (readfile(datafd[filecnt], bsize) < 0) { + close(datafd[filecnt]); + NAK(); + return(0); + } + filecnt++; + break; + default: + error("protocol error <%d>\n", *(ap-1)); + NAK(); + } + } + return(filecnt); +} + +struct jobinfo * +getjobinfo(int fd) +{ + unsigned char *ap; + int rv; + static struct jobinfo info; + + if (fd < 0) error("getjobinfo: bad file descriptor\n"); + if (lseek(fd, 0L, 0) < 0) { + error("error seeking in temp file\n"); + exit(7); + } + /* the following strings should be < NAMELEN or else they will not + * be null terminated. + */ + strncpy(info.user, "daemon", NAMELEN); + strncpy(info.host, "nowhere", NAMELEN); + /* there may be a space after the name and host. It will be filtered out + * by CPYFIELD. + */ + while ((rv=readline(fd)) > 0) { + ap = lnbuf; + ap[rv-1] = '\0'; /* remove newline from string */ + switch (*ap) { + case 'H': + if (ap[1] == '\0') + strncpy(info.host, "unknown", NAMELEN); + else + strncpy(info.host, (const char *)&ap[1], NAMELEN); + info.host[strlen(info.host)] = '\0'; + break; + case 'P': + if (ap[1] == '\0') + strncpy(info.user, "unknown", NAMELEN); + else + strncpy(info.user, (const char *)&ap[1], NAMELEN); + info.user[strlen(info.user)] = '\0'; + break; + } + } + return(&info); +} + +void +alarmhandler(int sig) { + signal(sig, alarmhandler); + error("alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); +} + +void +main(int argc, char **argv) +{ + unsigned char *ap, *bp, *cp, *savbufpnt; + int i, blen, rv, saveflg, savargcnt; + struct jobinfo *jinfop; + + USED(argc); + USED(argv); + + signal(SIGHUP, SIG_IGN); + signal(SIGALRM, alarmhandler); + cp = argvstr; + /* setup argv[0] for exec */ + argvals[argcnt++] = cp; + for (bp = (unsigned char *)LP, i = 0; (*bp != '\0') && (i < ARGSIZ-1); *cp++ = *bp++, i++); + *cp++ = '\0'; + /* get the first line sent and parse it as arguments for lp */ + if ((rv=readline(0)) < 0) + exit(1); + bp = lnbuf; + /* setup the remaining arguments */ + /* check for BSD style request */ + /* ^A, ^B, ^C, ^D, ^E (for BSD lpr) */ + switch (*bp) { + case '\001': + case '\003': + case '\004': + bp++; /* drop the ctrl character from the input */ + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'q'; *cp++ = '\0'; /* -q */ + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'd'; /* -d */ + CPYFIELD(bp, cp); /* printer */ + *cp++ = '\0'; + break; + case '\002': + bp++; /* drop the ctrl character from the input */ + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'd'; /* -d */ + CPYFIELD(bp, cp); /* printer */ + *cp++ = '\0'; + ACK(); + savargcnt = argcnt; + savbufpnt = cp; + while ((rv=getfiles())) { + jinfop = getjobinfo(cntrlfd); + close(cntrlfd); + argcnt = savargcnt; + cp = savbufpnt; + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'M'; /* -M */ + bp = (unsigned char *)jinfop->host; + CPYFIELD(bp, cp); /* host name */ + *cp++ = '\0'; + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'u'; /* -u */ + bp = (unsigned char *)jinfop->user; + CPYFIELD(bp, cp); /* user name */ + *cp++ = '\0'; + for(i=0;i<rv;i++) + forklp(datafd[i]); + } + exit(0); + case '\005': + bp++; /* drop the ctrl character from the input */ + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'k'; *cp++ = '\0'; /* -k */ + argvals[argcnt++] = cp; + *cp++ = '-'; *cp++ = 'd'; /* -d */ + CPYFIELD(bp, cp); /* printer */ + *cp++ = '\0'; + argvals[argcnt++] = cp; + *cp++ = '-'; ap = cp; *cp++ = 'u'; /* -u */ + CPYFIELD(bp, cp); /* username */ + + /* deal with bug in lprng where the username is not supplied + */ + if (ap == (cp-1)) { + ap = (unsigned char *)"none"; + CPYFIELD(ap, cp); + } + + *cp++ = '\0'; + datafd[0] = tempfile(); + blen = strlen((const char *)bp); + if (write(datafd[0], bp, blen) != blen) { + error("write error\n"); + exit(6); + } + if (write(datafd[0], "\n", 1) != 1) { + error("write error\n"); + exit(6); + } + break; + default: + /* otherwise get my lp arguments */ + do { + /* move to next non-white space */ + while (*bp==' '||*bp=='\t') + ++bp; + if (*bp=='\n') continue; + /* only accept arguments beginning with - + * this is done to prevent the printing of + * local files from the destination host + */ + if (*bp=='-') { + argvals[argcnt++] = cp; + saveflg = 1; + } else + saveflg = 0; + /* move to next white space copying text to argument buffer */ + while (*bp!=' ' && *bp!='\t' && *bp!='\n' + && *bp!='\0') { + *cp = *bp++; + cp += saveflg; + } + *cp = '\0'; + cp += saveflg; + } while (*bp!='\n' && *bp!='\0'); + if (readline(0) < 0) exit(7); + datafd[0] = tempfile(); + if(readfile(datafd[0], atoi((char *)lnbuf)) < 0) { + error("readfile failed\n"); + exit(8); + } + } + forklp(datafd[0]); + exit(0); +} diff --git a/src/cmd/lp/lpdsend.c b/src/cmd/lp/lpdsend.c new file mode 100644 index 00000000..0dc70259 --- /dev/null +++ b/src/cmd/lp/lpdsend.c @@ -0,0 +1,428 @@ +#include <u.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <libc.h> + +#define REDIALTIMEOUT 15 +#define TIMEOUT 600 + +char tmpfilename[L_tmpnam+1]; +int alarmstate = 0; +int debugflag = 0; +int killflag = 0; +int statflag = 0; + +void +cleanup(void) { + unlink(tmpfilename); +} + +#define SBSIZE 8192 +unsigned char sendbuf[SBSIZE]; + +void +debug(char *str) { + if (debugflag) + fprintf(stderr, "%s", str); +} + +void +alarmhandler(int sig) { + fprintf(stderr, "timeout occurred, check printer.\n"); + exit(2); +} + +/* send a message after each WARNPC percent of data sent */ +#define WARNPC 5 + +int +copyfile(int in, int out, long tosend) { + int n; + int sent = 0; + int percent = 0; + + if (debugflag) + fprintf(stderr, "lpdsend: copyfile(%d,%d,%ld)\n", + in, out, tosend); + while ((n=read(in, sendbuf, SBSIZE)) > 0) { + if (debugflag) + fprintf(stderr, "lpdsend: copyfile read %d bytes from %d\n", + n, in); + alarm(TIMEOUT); alarmstate = 1; + if (write(out, sendbuf, n) != n) { + alarm(0); + fprintf(stderr, "write to fd %d failed\n", out); + return(0); + } + alarm(0); + if (debugflag) + fprintf(stderr, "lpdsend: copyfile wrote %d bytes to %d\n", + n, out); + sent += n; + if (tosend && ((sent*100/tosend)>=(percent+WARNPC))) { + percent += WARNPC; + fprintf(stderr, ": %5.2f%% sent\n", sent*100.0/tosend); + } + } + if (debugflag) + fprintf(stderr, "lpdsend: copyfile read %d bytes from %d\n", + n, in); + return(!n); +} + +char strbuf[120]; +char hostname[MAXHOSTNAMELEN], *username, *printername, *killarg; +char *inputname; +char filetype = 'o'; /* 'o' is for PostScript */ +int seqno = 0; +char *seqfilename; + +void +killjob(int printerfd) { + int strlength; + if (printername==0) { + fprintf(stderr, "no printer name\n"); + exit(1); + } + if (username==0) { + fprintf(stderr, "no user name given\n"); + exit(1); + } + if (killarg==0) { + fprintf(stderr, "no job to kill\n"); + exit(1); + } + sprintf(strbuf, "%c%s %s %s\n", '\5', printername, username, killarg); + strlength = strlen(strbuf); + if (write(printerfd, strbuf, strlength) != strlength) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + copyfile(printerfd, 2, 0L); +} + +void +checkqueue(int printerfd) { + int strlength; + + sprintf(strbuf, "%c%s\n", '\4', printername); + strlength = strlen(strbuf); + if (write(printerfd, strbuf, strlength) != strlength) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + copyfile(printerfd, 2, 0L); +/* +{ int n; + unsigned char sendbuf[1]; + while ((n=read(printerfd, sendbuf, 1)) > 0) { + write(2, sendbuf, n); + } +} +*/ +} + +void +getack(int printerfd, int as) { + char resp; + int rv; + + alarm(TIMEOUT); alarmstate = as; + if ((rv=read(printerfd, &resp, 1)) != 1 || resp != '\0') { + fprintf(stderr, "getack failed: read returned %d, read value (if any) %d, alarmstate=%d\n", + rv, resp, alarmstate); + exit(1); + } + alarm(0); +} + +/* send control file */ +void +sendctrl(int printerfd) { + char cntrlstrbuf[256]; + int strlength, cntrlen; + + sprintf(cntrlstrbuf, "H%s\nP%s\n%cdfA%3.3d%s\n", hostname, username, filetype, seqno, hostname); + cntrlen = strlen(cntrlstrbuf); + sprintf(strbuf, "%c%d cfA%3.3d%s\n", '\2', cntrlen, seqno, hostname); + strlength = strlen(strbuf); + if (write(printerfd, strbuf, strlength) != strlength) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + getack(printerfd, 3); + if (write(printerfd, cntrlstrbuf, cntrlen) != cntrlen) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + if (write(printerfd, "\0", 1) != 1) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + getack(printerfd, 4); +} + +/* send data file */ +void +senddata(int inputfd, int printerfd, long size) { + int strlength; + + sprintf(strbuf, "%c%ld dfA%3.3d%s\n", '\3', size, seqno, hostname); + strlength = strlen(strbuf); + if (write(printerfd, strbuf, strlength) != strlength) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + getack(printerfd, 5); + if (!copyfile(inputfd, printerfd, size)) { + fprintf(stderr, "failed to send file to printer\n"); + exit(1); + } + if (write(printerfd, "\0", 1) != 1) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + fprintf(stderr, "%ld bytes sent, status: waiting for end of job\n", size); + getack(printerfd, 6); +} + +void +sendjob(int inputfd, int printerfd) { + struct stat statbuf; + int strlength; + + if (fstat(inputfd, &statbuf) < 0) { + fprintf(stderr, "fstat(%s) failed\n", inputname); + exit(1); + } + sprintf(strbuf, "%c%s\n", '\2', printername); + strlength = strlen(strbuf); + if (write(printerfd, strbuf, strlength) != strlength) { + fprintf(stderr, "write(printer) error\n"); + exit(1); + } + getack(printerfd, 2); + debug("send data\n"); + senddata(inputfd, printerfd, statbuf.st_size); + debug("send control info\n"); + sendctrl(printerfd); + fprintf(stderr, "%ld bytes sent, status: end of job\n", (long)statbuf.st_size); +} + +/* + * make an address, add the defaults + */ +char * +netmkaddr(char *linear, char *defnet, char *defsrv) +{ + static char addr[512]; + char *cp; + + /* + * dump network name + */ + cp = strchr(linear, '!'); + if(cp == 0){ + if(defnet==0){ + if(defsrv) + sprintf(addr, "net!%s!%s", linear, defsrv); + else + sprintf(addr, "net!%s", linear); + } + else { + if(defsrv) + sprintf(addr, "%s!%s!%s", defnet, linear, defsrv); + else + sprintf(addr, "%s!%s", defnet, linear); + } + return addr; + } + + /* + * if there is already a service, use it + */ + cp = strchr(cp+1, '!'); + if(cp) + return linear; + + /* + * add default service + */ + if(defsrv == 0) + return linear; + sprintf(addr, "%s!%s", linear, defsrv); + + return addr; +} + +void +main(int argc, char *argv[]) { + int c, usgflg = 0; + char *desthostname; + int printerfd; + int inputfd; + int sendport; + char portstr[4]; + + desthostname = nil; + if (signal(SIGALRM, alarmhandler) == SIG_ERR) { + fprintf(stderr, "failed to set alarm handler\n"); + exit(1); + } + while ((c = getopt(argc, argv, "Dd:k:qs:t:H:P:")) != -1) + switch (c) { + case 'D': + debugflag = 1; + debug("debugging on\n"); + break; + case 'd': + printername = optarg; + break; + case 'k': + if (statflag) { + fprintf(stderr, "cannot have both -k and -q flags\n"); + exit(1); + } + killflag = 1; + killarg = optarg; + break; + case 'q': + if (killflag) { + fprintf(stderr, "cannot have both -q and -k flags\n"); + exit(1); + } + statflag = 1; + break; + case 's': + seqno = strtol(optarg, NULL, 10); + if (seqno < 0 || seqno > 999) + seqno = 0; + break; + case 't': + switch (filetype) { + case 'c': + case 'd': + case 'f': + case 'g': + case 'l': + case 'n': + case 'o': + case 'p': + case 'r': + case 't': + case 'v': + case 'z': + filetype = optarg[0]; + break; + default: + usgflg++; + break; + } + break; + case 'H': + strncpy(hostname, optarg, MAXHOSTNAMELEN); + break; + case 'P': + username = optarg; + break; + default: + case '?': + fprintf(stderr, "unknown option %c\n", c); + usgflg++; + } + if (argc < 2) usgflg++; + if (optind < argc) { + desthostname = argv[optind++]; + } else + usgflg++; + if (usgflg) { + fprintf(stderr, "usage: to send a job - %s -d printer -H hostname -P username [-s seqno] [-t[cdfgklnoprtvz]] desthost [filename]\n", argv[0]); + fprintf(stderr, " to check status - %s -d printer -q desthost\n", argv[0]); + fprintf(stderr, " to kill a job - %s -d printer -P username -k jobname desthost\n", argv[0]); + exit(1); + } + +/* make sure the file to send is here and ready + * otherwise the TCP connection times out. + */ + inputfd = -1; + if (!statflag && !killflag) { + if (optind < argc) { + inputname = argv[optind++]; + debug("open("); debug(inputname); debug(")\n"); + inputfd = open(inputname, O_RDONLY); + if (inputfd < 0) { + fprintf(stderr, "open(%s) failed\n", inputname); + exit(1); + } + } else { + inputname = "stdin"; + tmpnam(tmpfilename); + debug("using stdin\n"); + if ((inputfd = create(tmpfilename, ORDWR, 0600)) < 0) { + fprintf(stderr, "open(%s) failed\n", tmpfilename); + exit(1); + } + atexit(cleanup); + debug("copy input to temp file "); + debug(tmpfilename); + debug("\n"); + if (!copyfile(0, inputfd, 0L)) { + fprintf(stderr, "failed to copy file to temporary file\n"); + exit(1); + } + if (lseek(inputfd, 0L, 0) < 0) { + fprintf(stderr, "failed to seek back to the beginning of the temporary file\n"); + exit(1); + } + } + } + + sprintf(strbuf, "%s", netmkaddr(desthostname, "tcp", "printer")); + fprintf(stderr, "connecting to %s\n", strbuf); + for (sendport=721; sendport<=731; sendport++) { + sprintf(portstr, "%3.3d", sendport); + fprintf(stderr, " trying from port %s...", portstr); + debug(" dial("); debug(strbuf); debug(", "); debug(portstr); debug(", 0, 0) ..."); + printerfd = dial(strbuf, portstr, 0, 0); + if (printerfd >= 0) { + fprintf(stderr, "connected\n"); + break; + } + fprintf(stderr, "failed\n"); + sleep(REDIALTIMEOUT); + } + if (printerfd < 0) { + fprintf(stderr, "Cannot open a valid port!\n"); + fprintf(stderr, "- All source ports [721-731] may be busy.\n"); + fprintf(stderr, "- Is recipient ready and online?\n"); + fprintf(stderr, "- If all else fails, cycle the power!\n"); + exit(1); + } +/* hostname[8] = '\0'; */ +#ifndef PLAN9 + if (gethostname(hostname, sizeof(hostname)) < 0) { + perror("gethostname"); + exit(1); + } +#endif +/* char *hnend; + if ((hnend = strchr(hostname, '.')) != NULL) + *hnend = '\0'; + */ + if (statflag) { + checkqueue(printerfd); + } else if (killflag) { + killjob(printerfd); + } else { + sendjob(inputfd, printerfd); + } + exit(0); +} diff --git a/src/cmd/lp/lpsend.c b/src/cmd/lp/lpsend.c new file mode 100644 index 00000000..4893f452 --- /dev/null +++ b/src/cmd/lp/lpsend.c @@ -0,0 +1,341 @@ +#ifdef plan9 + +#include <u.h> +#include <libc.h> +#define stderr 2 + +#define RDNETIMEOUT 60000 +#define WRNETIMEOUT 60000 + +#else + +/* not for plan 9 */ +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <signal.h> + +#define create creat +#define seek lseek +#define fprint fprintf +#define sprint sprintf +#define exits exit + +#define ORDWR O_RDWR +#define OTRUNC O_TRUNC +#define ORCLOSE 0 + +#define RDNETIMEOUT 60 +#define WRNETIMEOUT 60 + +#endif + +#define MIN(a,b) ((a<b)?a:b) + +#define ACK(a) write(a, "", 1) +#define NAK(a) write(a, "\001", 1) + +#define LPDAEMONLOG "/tmp/lpdaemonl" + +#define LNBFSZ 4096 +char lnbuf[LNBFSZ]; +int dbgstate = 0; +char *dbgstrings[] = { + "", + "rcvack1", + "send", + "rcvack2", + "response", + "done" +}; + +#ifdef plan9 + +void +error(int level, char *s1, ...) +{ + va_list ap; + long thetime; + char *chartime; + char *args[8]; + int argno = 0; + + if (level == 0) { + time(&thetime); + chartime = ctime(thetime); + fprint(stderr, "%.15s ", &(chartime[4])); + } + va_start(ap, s1); + while(args[argno++] = va_arg(ap, char*)); + va_end(ap); + fprint(stderr, s1, *args); + return; +} + +int +alarmhandler(void *foo, char *note) { + USED(foo); + if(strcmp(note, "alarm")==0) { + fprint(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); + return(1); + } else return(0); +} + +#else + +void +error(int level, char *s1, ...) +{ + time_t thetime; + char *chartime; + + if (level == 0) { + time(&thetime); + chartime = ctime(&thetime); + fprintf(stderr, "%.15s ", &(chartime[4])); + } + fprintf(stderr, s1, (&s1+1)); + return; +} + +void +alarmhandler() { + fprintf(stderr, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); +} + +#endif + +/* get a line from inpfd using nonbuffered input. The line is truncated if it is too + * long for the buffer. The result is left in lnbuf and the number of characters + * read in is returned. + */ +int +readline(int inpfd) +{ + register char *ap; + register int i; + + ap = lnbuf; + i = 0; + do { + if (read(inpfd, ap, 1) != 1) { + error(0, "read error in readline, fd=%d\n", inpfd); + break; + } + } while ((++i < LNBFSZ - 2) && *ap++ != '\n'); + if (i == LNBFSZ - 2) { + *ap = '\n'; + i++; + } + *ap = '\0'; + return(i); +} + +#define RDSIZE 512 +char jobbuf[RDSIZE]; + +int +pass(int inpfd, int outfd, int bsize) +{ + int bcnt = 0; + int rv = 0; + + for(bcnt=bsize; bcnt > 0; bcnt -= rv) { + alarm(WRNETIMEOUT); /* to break hanging */ + if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) { + error(0, "read error during pass, %d remaining\n", bcnt); + break; + } else if((write(outfd, jobbuf, rv)) != rv) { + error(0, "write error during pass, %d remaining\n", bcnt); + break; + } + } + alarm(0); + return(bcnt); +} + +/* get whatever stdin has and put it into the temporary file. + * return the file size. + */ +int +prereadfile(int inpfd) +{ + int rv, bsize; + + bsize = 0; + do { + if((rv=read(0, jobbuf, RDSIZE))<0) { + error(0, "read error while making temp file\n"); + exits("read error while making temp file"); + } else if((write(inpfd, jobbuf, rv)) != rv) { + error(0, "write error while making temp file\n"); + exits("write error while making temp file"); + } + bsize += rv; + } while (rv!=0); + return(bsize); +} + +int +tempfile(void) +{ + static int tindx = 0; + char tmpf[20]; + int tmpfd; + + sprint(tmpf, "/var/tmp/lp%d.%d", getpid(), tindx++); + if((tmpfd=create(tmpf, + +#ifdef plan9 + + ORDWR|OTRUNC, + +#endif + + 0666)) < 0) { + error(0, "cannot create temp file %s\n", tmpf); + exits("cannot create temp file"); + } + close(tmpfd); + if((tmpfd=open(tmpf, ORDWR + +#ifdef plan9 + + |ORCLOSE|OTRUNC + +#endif + + )) < 0) { + error(0, "cannot open temp file %s\n", tmpf); + exits("cannot open temp file"); + } + return(tmpfd); +} + +int +recvACK(int netfd) +{ + int rv; + + *jobbuf = '\0'; + alarm(RDNETIMEOUT); + if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') { + error(0, "failed to receive ACK, "); + if (*jobbuf == '\0') + error(1, "read failed\n"); + else + error(1, "received <0x%x> instead\n", *jobbuf); + rv = 0; + } else rv = 1; + alarm(0); + return(rv); +} + +void +main(int argc, char *argv[]) +{ + char *devdir; + int i, rv, netfd, bsize; + int datafd; + +#ifndef plan9 + + void (*oldhandler)(); + +#endif + + devdir = nil; + /* make connection */ + if (argc != 2) { + fprint(stderr, "usage: %s network!destination!service\n", argv[0]); + exits("incorrect number of arguments"); + } + + /* read options line from stdin into lnbuf */ + i = readline(0); + + /* read stdin into tempfile to get size */ + datafd = tempfile(); + bsize = prereadfile(datafd); + + /* network connection is opened after data is in to avoid timeout */ + if ((netfd=dial(argv[1], 0, 0, 0)) < 0) { + fprint(stderr, "dialing %s\n", devdir); + perror("dial"); + exits("can't dial"); + } + + /* write out the options we read above */ + if (write(netfd, lnbuf, i) != i) { + error(0, "write error while sending options\n"); + exits("write error while sending options"); + } + + /* send the size of the file to be sent */ + sprint(lnbuf, "%d\n", bsize); + i = strlen(lnbuf); + if ((rv=write(netfd, lnbuf, i)) != i) { + perror("write error while sending size"); + error(0, "write returned %d\n", rv); + exits("write error while sending size"); + } + + if (seek(datafd, 0L, 0) < 0) { + error(0, "error seeking temp file\n"); + exits("seek error"); + } + /* mirror performance in readfile() in lpdaemon */ + +#ifdef plan9 + + atnotify(alarmhandler, 1); + +#else + + oldhandler = signal(SIGALRM, alarmhandler); + +#endif + + dbgstate = 1; + if(!recvACK(netfd)) { + error(0, "failed to receive ACK before sending data\n"); + exits("recv ack1 failed"); + } + dbgstate = 2; + if ((i=pass(datafd, netfd, bsize)) != 0) { + NAK(netfd); + error(0, "failed to send %d bytes\n", i); + exits("send data failed"); + } + ACK(netfd); + dbgstate = 3; + if(!recvACK(netfd)) { + error(0, "failed to receive ACK after sending data\n"); + exits("recv ack2 failed"); + } + + /* get response, as from lp -q */ + dbgstate = 4; + while((rv=read(netfd, jobbuf, RDSIZE)) > 0) { + if((write(1, jobbuf, rv)) != rv) { + error(0, "write error while sending to stdout\n"); + exits("write error while sending to stdout"); + } + } + dbgstate = 5; + +#ifdef plan9 + + atnotify(alarmhandler, 0); + /* close down network connections and go away */ + exits(""); + +#else + + signal(SIGALRM, oldhandler); + exit(0); + +#endif + +} diff --git a/src/cmd/lp/lpsend.rc b/src/cmd/lp/lpsend.rc new file mode 100755 index 00000000..74806cde --- /dev/null +++ b/src/cmd/lp/lpsend.rc @@ -0,0 +1,18 @@ +#!/bin/rc +if (! ~ $DEBUG '') { flag x + } +if (test -e /net/tcp/clone) { + dialstring=`{ndb/query sys $1 dom} + network=tcp + if (~ $#dialstring 0 || ! ~ $dialstring '') { + dialstring=$1 + } + if(lpsend $network^!^$dialstring^!printer) exit '' + rv='tcp failed' +} +if not rv='no tcp' + + +if (! ~ $dialstring '') + exit 'lpsend: no dialstring' +if not + exit 'lpsend: '^$rv diff --git a/src/cmd/lp/mkfile b/src/cmd/lp/mkfile new file mode 100644 index 00000000..7400e8d5 --- /dev/null +++ b/src/cmd/lp/mkfile @@ -0,0 +1,30 @@ +<$PLAN9/src/mkhdr + +TARG=\ + lpdsend \ + lpsend \ + LOCK \ + lpdaemon + +OFILES= + +HFILES= + +BIN=$PLAN9/lp/bin +<$PLAN9/src/mkmany +CFLAGS=-Dplan9 + +install:V: $PLAN9/lp/bin/lpsend.rc $PLAN9/bin/lp + +$PLAN9/lp/bin/lpsend.rc: lpsend.rc + cp $prereq $target + +lpdsend.$O: lpdsend.c + $CC $CFLAGS -D_POSIX_SOURCE -D_BSD_EXTENSION -D_NET_EXTENSION -DPLAN9 -'DMAXHOSTNAMELEN=64' lpdsend.c + +lpdaemon.$O: lpdaemon.c + $CC $CFLAGS -D_POSIX_SOURCE -DPLAN9 lpdaemon.c + +$PLAN9/bin/lp: lp.rc + cp $prereq $target + |