1 /* for plan 9 */ 2 #include <u.h> 3 #include <libc.h> 4 #include <stdarg.h> 5 6 /* not for plan 9 */ 7 /* #include <errno.h> */ 8 /* #include <time.h> */ 9 /* #include <ipc.h> */ 10 11 #define MIN(a,b) ((a<b)?a:b) 12 13 #define ACK(a) write(a, "", 1) 14 #define NAK(a) write(a, "\001", 1) 15 16 #define RDNETIMEOUT 60000 17 #define WRNETIMEOUT 60000 18 #define LPDAEMONLOG "/tmp/lpdaemonl" 19 20 #define LNBFSZ 4096 21 char lnbuf[LNBFSZ]; 22 int dbgstate = 0; 23 char *dbgstrings[] = { 24 "", 25 "rcvack1", 26 "send", 27 "rcvack2", 28 "response", 29 "done" 30 }; 31 void 32 error(int level, char *s1, ...) 33 { 34 long thetime; 35 char *chartime; 36 va_list ap; 37 char *args[8]; 38 int argno = 0; 39 40 if (level == 0) { 41 time(&thetime); 42 chartime = ctime(thetime); 43 fprint(2, "%.15s ", &(chartime[4])); 44 } 45 va_start(ap, s1); 46 while(args[argno++] = va_arg(ap, char*)); 47 va_end(ap); 48 fprint(2, s1, *args); 49 return; 50 } 51 52 int 53 alarmhandler(void *foo, char *note) { 54 USED(foo); 55 if(strncmp(note, "alarm", ERRLEN)==0) { 56 fprint(2, "alarm at %d - %s\n", dbgstate, dbgstrings[dbgstate]); 57 return(1); 58 } else return(0); 59 } 60 61 /* get a line from inpfd using nonbuffered input. The line is truncated if it is too 62 * long for the buffer. The result is left in lnbuf and the number of characters 63 * read in is returned. 64 */ 65 int 66 readline(int inpfd) 67 { 68 register char *ap; 69 register int i; 70 71 ap = lnbuf; 72 i = 0; 73 do { 74 if (read(inpfd, ap, 1) != 1) { 75 error(0, "read error in readline, fd=%d\n", inpfd); 76 break; 77 } 78 } while ((++i < LNBFSZ - 2) && *ap++ != '\n'); 79 if (i == LNBFSZ - 2) { 80 *ap = '\n'; 81 i++; 82 } 83 *ap = '\0'; 84 return(i); 85 } 86 87 #define RDSIZE 512 88 char jobbuf[RDSIZE]; 89 90 int 91 pass(int inpfd, int outfd, int bsize) 92 { 93 int bcnt, rv; 94 95 for(bcnt=bsize; bcnt > 0; bcnt -= rv) { 96 alarm(WRNETIMEOUT); /* to break hanging */ 97 if((rv=read(inpfd, jobbuf, MIN(bcnt,RDSIZE))) < 0) { 98 error(0, "read error during pass, %d remaining\n", bcnt); 99 break; 100 } else if((write(outfd, jobbuf, rv)) != rv) { 101 error(0, "write error during pass, %d remaining\n", bcnt); 102 break; 103 } 104 } 105 alarm(0); 106 return(bcnt); 107 } 108 109 /* get whatever stdin has and put it into the temporary file. 110 * return the file size. 111 */ 112 int 113 prereadfile(int inpfd) 114 { 115 int rv, bsize; 116 117 bsize = 0; 118 do { 119 if((rv=read(0, jobbuf, RDSIZE))<0) { 120 error(0, "read error while making temp file\n"); 121 exits("read error while making temp file"); 122 } else if((write(inpfd, jobbuf, rv)) != rv) { 123 error(0, "write error while making temp file\n"); 124 exits("write error while making temp file"); 125 } 126 bsize += rv; 127 } while (rv!=0); 128 return(bsize); 129 } 130 131 int 132 tmpfile(void) 133 { 134 static tindx = 0; 135 char tmpf[20]; 136 int tmpfd; 137 138 sprint(tmpf, "/tmp/lp%d.%d", getpid(), tindx++); 139 if((tmpfd=create(tmpf, ORDWR|ORCLOSE|OTRUNC, 0666)) < 0) { 140 error(0, "cannot create temp file %s\n", tmpf); 141 exits("cannot create temp file"); 142 } 143 return(tmpfd); 144 } 145 146 int 147 recvACK(int netfd) 148 { 149 int rv; 150 151 *jobbuf = '\0'; 152 alarm(RDNETIMEOUT); 153 if (read(netfd, jobbuf, 1)!=1 || *jobbuf!='\0') { 154 error(0, "failed to receive ACK, "); 155 if (*jobbuf == '\0') 156 error(1, "read failed\n"); 157 else 158 error(1, "received <0x%x> instead\n", *jobbuf); 159 rv = 0; 160 } else rv = 1; 161 alarm(0); 162 return(rv); 163 } 164 165 void 166 main(int argc, char *argv[]) 167 { 168 char *devdir; 169 int i, rv, netfd, bsize, datafd; 170 171 /* make connection */ 172 if (argc != 4) { 173 fprint(2, "usage: %s destination network service\n", argv[0]); 174 exits("incorrect number of arguments"); 175 } 176 if ((netfd=dial((devdir=netmkaddr(argv[1], argv[2], argv[3])), 0, 0, 0)) < 0) { 177 fprint(2, "dialing %s\n", devdir); 178 perror("dial"); 179 exits("can't dial"); 180 } 181 182 /* read options line from stdin and send it */ 183 i = readline(0); 184 if (write(netfd, lnbuf, i) != i) { 185 error(0, "write error while sending options\n"); 186 exits("write error while sending options"); 187 } 188 189 /* read stdin into tmpfile to get size */ 190 datafd = tmpfile(); 191 bsize = prereadfile(datafd); 192 193 /* send the size of the file to be sent */ 194 i = sprint(lnbuf, "%d\n", bsize); 195 if ((rv=write(netfd, lnbuf, i)) != i) { 196 perror("write error while sending size"); 197 error(0, "write returned %d\n", rv); 198 exits("write error while sending size"); 199 } 200 201 if (seek(datafd, 0L, 0) < 0) { 202 error(0, "error seeking temp file\n"); 203 exits("seek error"); 204 } 205 /* mirror performance in readfile() in lpdaemon */ 206 atnotify(alarmhandler, 1); 207 dbgstate = 1; 208 if(!recvACK(netfd)) { 209 error(0, "failed to receive ACK before sending data\n"); 210 exits("recv ack1 failed"); 211 } 212 dbgstate = 2; 213 if ((i=pass(datafd, netfd, bsize)) != 0) { 214 NAK(netfd); 215 error(0, "failed to send %d bytes\n", i); 216 exits("send data failed"); 217 } 218 ACK(netfd); 219 dbgstate = 3; 220 if(!recvACK(netfd)) { 221 error(0, "failed to receive ACK after sending data\n"); 222 exits("recv ack2 failed"); 223 } 224 225 /* get response, as from lp -q */ 226 dbgstate = 4; 227 while((rv=read(netfd, jobbuf, RDSIZE)) > 0) { 228 if((write(1, jobbuf, rv)) != rv) { 229 error(0, "write error while sending to stdout\n"); 230 exits("write error while sending to stdout"); 231 } 232 } 233 dbgstate = 5; 234 atnotify(alarmhandler, 0); 235 /* close down network connections and go away */ 236 exits(""); 237 } 238