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