1 #define _BSD_EXTENSION
2 #define _NET_EXTENSION
3 #define _POSIX_SOURCE
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <signal.h>
12 #include <errno.h>
13 #include <select.h>
14 #include <sys/types.h>
15 #include <sys/time.h>
16 #include <sys/socket.h>
17 #include <sys/wait.h>
18
19 extern int dial_debug;
20 extern int dial(char*, char*, char*, int*);
21
22
23 /* debug = 0 for no debugging */
24 /* debug = 1 for readprinter debugging */
25 /* debug = 2 for sendprinter debugging */
26 /* debug = 3 for full debugging, its hard to read the messages */
27
28 int debug = 0;
29 #define READTIMEOUT 300
30 #define RCVSELTIMEOUT 30
31 #define SNDSELTIMEOUT 300
32
33 void
rdtmout(void)34 rdtmout(void) {
35 fprintf(stderr, "read timeout occurred, check printer\n");
36 }
37
38 int
getline(int fd,char * buf,int len)39 getline(int fd, char *buf, int len) {
40 char *bp, c;
41 int i = 0, n;
42
43 bp = buf;
44 while (alarm(READTIMEOUT),(n=read(fd, bp, 1)) == 1) {
45 alarm(0);
46 if (*bp == '\r') continue;
47 i += n;
48
49 c = *bp++;
50 if (c == '\n' || c == '\004' || i >= len-1)
51 break;
52 }
53 alarm(0);
54 if (n < 0)
55 return(n);
56 *bp = '\0';
57 return(i);
58 }
59
60 typedef struct {
61 char *state; /* printer's current status */
62 int val; /* value returned by getstatus() */
63 } Status;
64
65 /* printer states */
66 #define INITIALIZING 0
67 #define IDLE 1
68 #define BUSY 2
69 #define WAITING 3
70 #define PRINTING 4
71 #define PRINTERERROR 5
72 #define ERROR 6
73 #define FLUSHING 7
74 #define UNKNOWN 8
75
76 /* protocol requests and program states */
77 #define START 'S'
78 unsigned char Start[] = { START };
79 #define ID_LE 'L'
80 unsigned char Id_le[] = { ID_LE };
81 #define REQ_STAT 'T'
82 unsigned char Req_stat[] = { REQ_STAT };
83 #define SEND_DATA 'D'
84 unsigned char Send_data[] = { SEND_DATA };
85 #define SENT_DATA 'A'
86 unsigned char Sent_data[] = { SENT_DATA };
87 #define WAIT_FOR_EOJ 'W'
88 unsigned char Wait_for_eoj[] = { WAIT_FOR_EOJ };
89 #define END_OF_JOB 'E'
90 unsigned char End_of_job[] = { END_OF_JOB };
91 #define FATAL_ERROR 'F'
92 unsigned char Fatal_error[] = { FATAL_ERROR };
93 #define WAIT_FOR_IDLE 'I'
94 unsigned char Wait_for_idle[] = { WAIT_FOR_IDLE };
95 #define OVER_AND_OUT 'O'
96 unsigned char Over_and_out[] = { OVER_AND_OUT };
97
98 Status statuslist[] = {
99 "initializing", INITIALIZING,
100 "idle", IDLE,
101 "busy", BUSY,
102 "waiting", WAITING,
103 "printing", PRINTING,
104 "printererror", PRINTERERROR,
105 "Error", ERROR,
106 "flushing", FLUSHING,
107 NULL, UNKNOWN
108 };
109
110
111 /* find returns a pointer to the location of string str2 in string str1,
112 * if it exists. Otherwise, it points to the end of str1.
113 */
114 char *
find(char * str1,char * str2)115 find(char *str1, char *str2) {
116 char *s1, *s2;
117
118 for (; *str1!='\0'; str1++) {
119 for (s1=str1,s2=str2; *s2!='\0'&&*s1==*s2; s1++,s2++) ;
120 if ( *s2 == '\0' )
121 break;
122 }
123
124 return(str1);
125 }
126
127 #define MESGSIZE 16384
128 int blocksize = 1920; /* 19200/10, with 1 sec delay between transfers
129 * this keeps the queues from building up.
130 */
131 char mesg[MESGSIZE]; /* exactly what came back on ttyi */
132
133 int
parsmesg(char * buf)134 parsmesg(char *buf) {
135 static char sbuf[MESGSIZE];
136 char *s; /* start of printer messsage in mesg[] */
137 char *e; /* end of printer message in mesg[] */
138 char *key, *val; /* keyword/value strings in sbuf[] */
139 char *p; /* for converting to lower case etc. */
140 int i; /* where *key was found in statuslist[] */
141
142 if (*(s=find(buf, "%[ "))!='\0' && *(e=find(s, " ]%"))!='\0') {
143 strcpy(sbuf, s+3); /* don't change mesg[] */
144 sbuf[e-(s+3)] = '\0'; /* ignore the trailing " ]%" */
145
146 for (key=strtok(sbuf, " :"); key != NULL; key=strtok(NULL, " :")) {
147 if (strcmp(key, "Error") == 0)
148 return(ERROR);
149 if ((val=strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0)
150 key = val;
151
152 for (; *key == ' '; key++) ; /* skip any leading spaces */
153 for (p = key; *p; p++) /* convert to lower case */
154 if (*p == ':') {
155 *p = '\0';
156 break;
157 } else if (isupper(*p)) *p = tolower(*p);
158
159 for (i=0; statuslist[i].state != NULL; i++) {
160 if (strcmp(statuslist[i].state, key) == 0)
161 return(statuslist[i].val);
162 }
163 }
164 }
165 return(UNKNOWN);
166 }
167
168 char buf[MESGSIZE];
169 fd_set readfds, writefds, exceptfds;
170 struct timeval rcvtimeout = { RCVSELTIMEOUT, 0 };
171 struct timeval sndtimeout = { SNDSELTIMEOUT, 0 };
172
173 int
readprinter(int printerfd,int pipefd)174 readprinter(int printerfd, int pipefd)
175 {
176 unsigned char proto;
177 int progstate = START;
178 int print_wait_msg = 0;
179 int tocount = 0;
180 int c, printstat, lastprintstat, n, nfds;
181
182
183 nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
184 printstat = 0;
185 signal(SIGALRM, rdtmout);
186 do {
187
188 reselect:
189 /* ask sending process to request printer status */
190 if (write(pipefd, Req_stat, 1) != 1) {
191 fprintf(stderr, "request status failed\n");
192 progstate = FATAL_ERROR;
193 continue;
194 }
195 FD_ZERO(&readfds); /* lets be anal */
196 FD_SET(printerfd, &readfds);
197 FD_SET(pipefd, &readfds);
198 FD_ZERO(&exceptfds);
199 FD_SET(printerfd, &exceptfds);
200 FD_SET(pipefd, &exceptfds);
201 n = select(nfds, &readfds, (fd_set *)0, &exceptfds, &rcvtimeout);
202 if (debug&0x1) fprintf(stderr, "readprinter select returned %d\n", n);
203 if (n == 0) {
204 /* a timeout occurred */
205 if (++tocount > 4) {
206 fprintf(stderr, "printer appears to be offline.\nHP4m printers may be out of paper.\n");
207 tocount = 0;
208 }
209 goto reselect;
210 }
211 if (n > 0 && FD_ISSET(printerfd, &exceptfds)) {
212 /* printer problem */
213 fprintf(stderr, "printer exception\n");
214 if (write(pipefd, Fatal_error, 1) != 1) {
215 fprintf(stderr, "'fatal error' write to pipe failed\n");
216 }
217 progstate = FATAL_ERROR;
218 continue;
219 }
220 if (n > 0 && FD_ISSET(pipefd, &exceptfds)) {
221 /* pipe problem */
222 fprintf(stderr, "pipe exception\n");
223 progstate = FATAL_ERROR;
224 continue;
225 }
226 if (n > 0 && FD_ISSET(pipefd, &readfds)) {
227 /* protocol pipe wants to be read */
228 if (debug&0x1) fprintf(stderr, "pipe wants to be read\n");
229 if (read(pipefd, &proto, 1) != 1) {
230 fprintf(stderr, "read protocol pipe failed\n");
231 progstate = FATAL_ERROR;
232 continue;
233 }
234 if (debug&0x1) fprintf(stderr, "readprinter: proto=%c\n", proto);
235 /* change state? */
236 switch (proto) {
237 case SENT_DATA:
238 break;
239 case WAIT_FOR_EOJ:
240 if (!print_wait_msg) {
241 print_wait_msg = 1;
242 fprintf(stderr, "waiting for end of job\n");
243 }
244 progstate = proto;
245 break;
246 default:
247 fprintf(stderr, "received unknown protocol request <%c> from sendfile\n", proto);
248 break;
249 }
250 n--;
251 }
252 if (n > 0 && FD_ISSET(printerfd, &readfds)) {
253 /* printer wants to be read */
254 if (debug&0x1) fprintf(stderr, "printer wants to be read\n");
255 if ((c=getline(printerfd, buf, MESGSIZE)) < 0) {
256 fprintf(stderr, "read printer failed\n");
257 progstate = FATAL_ERROR;
258 continue;
259 }
260 if (debug&0x1) fprintf(stderr, "%s\n", buf);
261 if (c==1 && *buf == '\004') {
262 if (progstate == WAIT_FOR_EOJ) {
263 if (debug&0x1) fprintf(stderr, "progstate=%c, ", progstate);
264 fprintf(stderr, "%%[ status: endofjob ]%%\n");
265 /* progstate = WAIT_FOR_IDLE; */
266 progstate = OVER_AND_OUT;
267 if (write(pipefd, Over_and_out, 1) != 1) {
268 fprintf(stderr, "'fatal error' write to pipe failed\n");
269 }
270 continue;
271 } else {
272 if (printstat == ERROR) {
273 progstate = FATAL_ERROR;
274 continue;
275 }
276 if (progstate != START && progstate != WAIT_FOR_IDLE)
277 fprintf(stderr, "warning: EOF received; program status is '%c'\n", progstate);
278
279 }
280 continue;
281 }
282
283 /* figure out if it was a status line */
284 lastprintstat = printstat;
285 printstat = parsmesg(buf);
286 if (printstat == UNKNOWN || printstat == ERROR
287 || lastprintstat != printstat) {
288 /* print whatever it is that was read */
289 fprintf(stderr, buf);
290 fflush(stderr);
291 if (printstat == UNKNOWN) {
292 printstat = lastprintstat;
293 continue;
294 }
295 }
296 switch (printstat) {
297 case UNKNOWN:
298 continue; /* shouldn't get here */
299 case FLUSHING:
300 case ERROR:
301 progstate = FATAL_ERROR;
302 /* ask sending process to die */
303 if (write(pipefd, Fatal_error, 1) != 1) {
304 fprintf(stderr, "Fatal_error mesg write to pipe failed\n");
305 }
306 continue;
307 case INITIALIZING:
308 case PRINTERERROR:
309 sleep(1);
310 break;
311 case IDLE:
312 if (progstate == WAIT_FOR_IDLE) {
313 progstate = OVER_AND_OUT;
314 if (write(pipefd, Over_and_out, 1) != 1) {
315 fprintf(stderr, "'fatal error' write to pipe failed\n");
316 }
317 continue;
318 }
319 progstate = SEND_DATA;
320
321 goto dowait;
322 case BUSY:
323 case WAITING:
324 default:
325 sleep(1);
326 dowait:
327 switch (progstate) {
328 case WAIT_FOR_IDLE:
329 case WAIT_FOR_EOJ:
330 case START:
331 sleep(5);
332 break;
333
334 case SEND_DATA:
335 if (write(pipefd, Send_data, 1) != 1) {
336 fprintf(stderr, "send data write to pipe failed\n");
337 progstate = FATAL_ERROR;
338 continue;
339 }
340 break;
341 default:
342 fprintf(stderr, "unexpected program state %c\n", progstate);
343 exit(1);
344 }
345 break;
346 }
347 n--;
348 }
349 if (n > 0) {
350 fprintf(stderr, "more fds selected than requested!\n");
351 exit(1);
352 };
353 } while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
354
355 if (progstate == FATAL_ERROR)
356 return(1);
357 else
358 return(0);
359 }
360
361 int
sendfile(int infd,int printerfd,int pipefd)362 sendfile(int infd, int printerfd, int pipefd)
363 {
364 unsigned char proto;
365 int progstate = START;
366 int i, n, nfds;
367 int bytesread, bytesent = 0;
368
369 nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
370
371 if (write(printerfd, "\004", 1)!=1) {
372 perror("sendfile:write:");
373 progstate = FATAL_ERROR;
374 }
375 do {
376 FD_ZERO(&readfds); /* lets be anal */
377 FD_SET(pipefd, &readfds);
378 n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &sndtimeout);
379 if (debug&02) fprintf(stderr, "sendfile select returned %d\n", n);
380 if (n > 0 && FD_ISSET(pipefd, &readfds)) {
381 /* protocol pipe wants to be read */
382 if (read(pipefd, &proto, 1) != 1) {
383 fprintf(stderr, "read protocol pipe failed\n");
384 return(1);
385 }
386 /* change state? */
387 if (debug&02) fprintf(stderr, "sendfile command - <%c>\n", proto);
388 switch (proto) {
389 case OVER_AND_OUT:
390 case END_OF_JOB:
391 progstate = proto;
392 break;
393 case SEND_DATA:
394 bytesread = 0;
395 do {
396 i = read(infd, &buf[bytesread], blocksize-bytesread);
397 if (debug&02) fprintf(stderr, "read %d bytes\n", i);
398 if (i > 0)
399 bytesread += i;
400 } while((i > 0) && (bytesread < blocksize));
401 if (i < 0) {
402 fprintf(stderr, "input file read error\n");
403 progstate = FATAL_ERROR;
404 break; /* from switch */
405 }
406 if (bytesread > 0) {
407 if (debug&02) fprintf(stderr, "writing %d bytes\n", bytesread);
408 if (write(printerfd, buf, bytesread)!=bytesread) {
409 perror("sendfile:write:");
410 progstate = FATAL_ERROR;
411 } else if (write(pipefd, Sent_data, 1)!=1) {
412 perror("sendfile:write:");
413 progstate = FATAL_ERROR;
414 } else {
415 bytesent += bytesread;
416 }
417 fprintf(stderr, "%d sent\n", bytesent);
418 fflush(stderr);
419
420 /* we have reached the end of the input file */
421 }
422 if (i == 0) {
423 if (progstate != WAIT_FOR_EOJ) {
424 if (write(printerfd, "\004", 1)!=1) {
425 perror("sendfile:write:");
426 progstate = FATAL_ERROR;
427 } else if (write(pipefd, Wait_for_eoj, 1)!=1) {
428 perror("sendfile:write:");
429 progstate = FATAL_ERROR;
430 } else {
431 progstate = WAIT_FOR_EOJ;
432 }
433 }
434 }
435 break;
436 case REQ_STAT:
437 if (write(printerfd, "\024", 1)!=1) {
438 fprintf(stderr, "write to printer failed\n");
439 progstate = FATAL_ERROR;
440 }
441 if (debug&02) fprintf(stderr, "^T");
442 break;
443 case FATAL_ERROR:
444 progstate = FATAL_ERROR;
445 }
446 } else if (n < 0) {
447 perror("sendfile:select:");
448 progstate = FATAL_ERROR;
449 } else if (n == 0) {
450 sleep(1);
451 fprintf(stderr, "sendfile timeout\n");
452 progstate = FATAL_ERROR;
453 }
454 } while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
455 if (write(printerfd, "\004", 1)!=1) {
456 perror("sendfile:write:");
457 progstate = FATAL_ERROR;
458 }
459
460 if (debug&02) fprintf(stderr, "%d bytes sent\n", bytesent);
461 if (progstate == FATAL_ERROR)
462 return(1);
463 else
464 return(0);
465 }
466
main(int argc,char * argv[])467 void main(int argc, char *argv[]) {
468 int c, usgflg=0, infd, printerfd;
469 int cpid, sprv;
470 int pipefd[2];
471 char *dialstr;
472 unsigned long rprv;
473
474 dialstr = 0;
475
476 while ((c = getopt(argc, argv, "b:d:")) != -1)
477 switch (c) {
478 case 'b':
479 blocksize = atoi(optarg)/10;
480 if (blocksize > MESGSIZE || blocksize < 1)
481 blocksize = MESGSIZE;
482 break;
483 case 'd':
484 debug = atoi(optarg);
485 dial_debug = debug;
486 break;
487 case '?':
488 fprintf(stderr, "unknown option %c\n", c);
489 usgflg++;
490 break;
491 }
492 if (optind < argc)
493 dialstr = argv[optind++];
494 else
495 usgflg++;
496 if (usgflg) {
497 fprintf(stderr, "usage: %s [-b baudrate] net!host!service [infile]\n",
498 argv[0]);
499 exit (2);
500 }
501 if (optind < argc) {
502 infd = open(argv[optind], 0);
503 if (infd < 0) {
504 fprintf(stderr, "cannot open %s\n", argv[optind]);
505 exit(1);
506 }
507 optind++;
508 } else
509 infd = 0;
510
511 if (debug & 02)
512 fprintf(stderr, "blocksize=%d\n", blocksize);
513 if (debug)
514 fprintf(stderr, "dialing address=%s\n", dialstr);
515 printerfd = dial(dialstr, 0, 0, 0);
516 if (printerfd < 0)
517 exit(1);
518
519 fprintf(stderr, "printer startup\n");
520
521 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipefd) < 0) {
522 perror("socketpair");
523 exit(1);
524 }
525 switch(cpid = fork()){
526 case -1:
527 perror("fork error");
528 exit(1);
529 case 0: /* child - to printer */
530 close(pipefd[1]);
531 sprv = sendfile(infd, printerfd, pipefd[0]);
532 if (debug)
533 fprintf(stderr, "to remote - exiting\n");
534 exit(sprv);
535 default: /* parent - from printer */
536 close(pipefd[0]);
537 rprv = readprinter(printerfd, pipefd[1]);
538 if (debug)
539 fprintf(stderr, "from remote - exiting\n");
540 while(wait(&sprv) != cpid)
541 ;
542 exit(rprv|sprv);
543 }
544 }
545