123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- #define _BSD_EXTENSION
- #define _NET_EXTENSION
- #define _POSIX_SOURCE
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <string.h>
- #include <ctype.h>
- #include <signal.h>
- #include <errno.h>
- #include <select.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <sys/wait.h>
- extern int dial_debug;
- extern int dial(char*, char*, char*, int*);
- /* debug = 0 for no debugging */
- /* debug = 1 for readprinter debugging */
- /* debug = 2 for sendprinter debugging */
- /* debug = 3 for full debugging, its hard to read the messages */
- int debug = 0;
- #define READTIMEOUT 300
- #define RCVSELTIMEOUT 30
- #define SNDSELTIMEOUT 300
- void
- rdtmout(void) {
- fprintf(stderr, "read timeout occurred, check printer\n");
- }
- int
- getline(int fd, char *buf, int len) {
- char *bp, c;
- int i = 0, n;
- bp = buf;
- while (alarm(READTIMEOUT),(n=read(fd, bp, 1)) == 1) {
- alarm(0);
- if (*bp == '\r') continue;
- i += n;
- c = *bp++;
- if (c == '\n' || c == '\004' || i >= len-1)
- break;
- }
- alarm(0);
- if (n < 0)
- return(n);
- *bp = '\0';
- return(i);
- }
- typedef struct {
- char *state; /* printer's current status */
- int val; /* value returned by getstatus() */
- } Status;
- /* printer states */
- #define INITIALIZING 0
- #define IDLE 1
- #define BUSY 2
- #define WAITING 3
- #define PRINTING 4
- #define PRINTERERROR 5
- #define ERROR 6
- #define FLUSHING 7
- #define UNKNOWN 8
- /* protocol requests and program states */
- #define START 'S'
- unsigned char Start[] = { START };
- #define ID_LE 'L'
- unsigned char Id_le[] = { ID_LE };
- #define REQ_STAT 'T'
- unsigned char Req_stat[] = { REQ_STAT };
- #define SEND_DATA 'D'
- unsigned char Send_data[] = { SEND_DATA };
- #define SENT_DATA 'A'
- unsigned char Sent_data[] = { SENT_DATA };
- #define WAIT_FOR_EOJ 'W'
- unsigned char Wait_for_eoj[] = { WAIT_FOR_EOJ };
- #define END_OF_JOB 'E'
- unsigned char End_of_job[] = { END_OF_JOB };
- #define FATAL_ERROR 'F'
- unsigned char Fatal_error[] = { FATAL_ERROR };
- #define WAIT_FOR_IDLE 'I'
- unsigned char Wait_for_idle[] = { WAIT_FOR_IDLE };
- #define OVER_AND_OUT 'O'
- unsigned char Over_and_out[] = { OVER_AND_OUT };
- Status statuslist[] = {
- "initializing", INITIALIZING,
- "idle", IDLE,
- "busy", BUSY,
- "waiting", WAITING,
- "printing", PRINTING,
- "printererror", PRINTERERROR,
- "Error", ERROR,
- "flushing", FLUSHING,
- NULL, UNKNOWN
- };
- /* find returns a pointer to the location of string str2 in string str1,
- * if it exists. Otherwise, it points to the end of str1.
- */
- char *
- find(char *str1, char *str2) {
- char *s1, *s2;
- for (; *str1!='\0'; str1++) {
- for (s1=str1,s2=str2; *s2!='\0'&&*s1==*s2; s1++,s2++) ;
- if ( *s2 == '\0' )
- break;
- }
- return(str1);
- }
- #define MESGSIZE 16384
- int blocksize = 1920; /* 19200/10, with 1 sec delay between transfers
- * this keeps the queues from building up.
- */
- char mesg[MESGSIZE]; /* exactly what came back on ttyi */
- int
- parsmesg(char *buf) {
- static char sbuf[MESGSIZE];
- char *s; /* start of printer messsage in mesg[] */
- char *e; /* end of printer message in mesg[] */
- char *key, *val; /* keyword/value strings in sbuf[] */
- char *p; /* for converting to lower case etc. */
- int i; /* where *key was found in statuslist[] */
- if (*(s=find(buf, "%[ "))!='\0' && *(e=find(s, " ]%"))!='\0') {
- strcpy(sbuf, s+3); /* don't change mesg[] */
- sbuf[e-(s+3)] = '\0'; /* ignore the trailing " ]%" */
- for (key=strtok(sbuf, " :"); key != NULL; key=strtok(NULL, " :")) {
- if (strcmp(key, "Error") == 0)
- return(ERROR);
- if ((val=strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0)
- key = val;
- for (; *key == ' '; key++) ; /* skip any leading spaces */
- for (p = key; *p; p++) /* convert to lower case */
- if (*p == ':') {
- *p = '\0';
- break;
- } else if (isupper(*p)) *p = tolower(*p);
- for (i=0; statuslist[i].state != NULL; i++) {
- if (strcmp(statuslist[i].state, key) == 0)
- return(statuslist[i].val);
- }
- }
- }
- return(UNKNOWN);
- }
- char buf[MESGSIZE];
- fd_set readfds, writefds, exceptfds;
- struct timeval rcvtimeout = { RCVSELTIMEOUT, 0 };
- struct timeval sndtimeout = { SNDSELTIMEOUT, 0 };
- int
- readprinter(int printerfd, int pipefd)
- {
- unsigned char proto;
- int progstate = START;
- int print_wait_msg = 0;
- int tocount = 0;
- int c, printstat, lastprintstat, n, nfds;
- nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
- printstat = 0;
- signal(SIGALRM, rdtmout);
- do {
- reselect:
- /* ask sending process to request printer status */
- if (write(pipefd, Req_stat, 1) != 1) {
- fprintf(stderr, "request status failed\n");
- progstate = FATAL_ERROR;
- continue;
- }
- FD_ZERO(&readfds); /* lets be anal */
- FD_SET(printerfd, &readfds);
- FD_SET(pipefd, &readfds);
- FD_ZERO(&exceptfds);
- FD_SET(printerfd, &exceptfds);
- FD_SET(pipefd, &exceptfds);
- n = select(nfds, &readfds, (fd_set *)0, &exceptfds, &rcvtimeout);
- if (debug&0x1) fprintf(stderr, "readprinter select returned %d\n", n);
- if (n == 0) {
- /* a timeout occurred */
- if (++tocount > 4) {
- fprintf(stderr, "printer appears to be offline.\nHP4m printers may be out of paper.\n");
- tocount = 0;
- }
- goto reselect;
- }
- if (n > 0 && FD_ISSET(printerfd, &exceptfds)) {
- /* printer problem */
- fprintf(stderr, "printer exception\n");
- if (write(pipefd, Fatal_error, 1) != 1) {
- fprintf(stderr, "'fatal error' write to pipe failed\n");
- }
- progstate = FATAL_ERROR;
- continue;
- }
- if (n > 0 && FD_ISSET(pipefd, &exceptfds)) {
- /* pipe problem */
- fprintf(stderr, "pipe exception\n");
- progstate = FATAL_ERROR;
- continue;
- }
- if (n > 0 && FD_ISSET(pipefd, &readfds)) {
- /* protocol pipe wants to be read */
- if (debug&0x1) fprintf(stderr, "pipe wants to be read\n");
- if (read(pipefd, &proto, 1) != 1) {
- fprintf(stderr, "read protocol pipe failed\n");
- progstate = FATAL_ERROR;
- continue;
- }
- if (debug&0x1) fprintf(stderr, "readprinter: proto=%c\n", proto);
- /* change state? */
- switch (proto) {
- case SENT_DATA:
- break;
- case WAIT_FOR_EOJ:
- if (!print_wait_msg) {
- print_wait_msg = 1;
- fprintf(stderr, "waiting for end of job\n");
- }
- progstate = proto;
- break;
- default:
- fprintf(stderr, "received unknown protocol request <%c> from sendfile\n", proto);
- break;
- }
- n--;
- }
- if (n > 0 && FD_ISSET(printerfd, &readfds)) {
- /* printer wants to be read */
- if (debug&0x1) fprintf(stderr, "printer wants to be read\n");
- if ((c=getline(printerfd, buf, MESGSIZE)) < 0) {
- fprintf(stderr, "read printer failed\n");
- progstate = FATAL_ERROR;
- continue;
- }
- if (debug&0x1) fprintf(stderr, "%s\n", buf);
- if (c==1 && *buf == '\004') {
- if (progstate == WAIT_FOR_EOJ) {
- if (debug&0x1) fprintf(stderr, "progstate=%c, ", progstate);
- fprintf(stderr, "%%[ status: endofjob ]%%\n");
- /* progstate = WAIT_FOR_IDLE; */
- progstate = OVER_AND_OUT;
- if (write(pipefd, Over_and_out, 1) != 1) {
- fprintf(stderr, "'fatal error' write to pipe failed\n");
- }
- continue;
- } else {
- if (printstat == ERROR) {
- progstate = FATAL_ERROR;
- continue;
- }
- if (progstate != START && progstate != WAIT_FOR_IDLE)
- fprintf(stderr, "warning: EOF received; program status is '%c'\n", progstate);
- }
- continue;
- }
- /* figure out if it was a status line */
- lastprintstat = printstat;
- printstat = parsmesg(buf);
- if (printstat == UNKNOWN || printstat == ERROR
- || lastprintstat != printstat) {
- /* print whatever it is that was read */
- fprintf(stderr, buf);
- fflush(stderr);
- if (printstat == UNKNOWN) {
- printstat = lastprintstat;
- continue;
- }
- }
- switch (printstat) {
- case UNKNOWN:
- continue; /* shouldn't get here */
- case FLUSHING:
- case ERROR:
- progstate = FATAL_ERROR;
- /* ask sending process to die */
- if (write(pipefd, Fatal_error, 1) != 1) {
- fprintf(stderr, "Fatal_error mesg write to pipe failed\n");
- }
- continue;
- case INITIALIZING:
- case PRINTERERROR:
- sleep(1);
- break;
- case IDLE:
- if (progstate == WAIT_FOR_IDLE) {
- progstate = OVER_AND_OUT;
- if (write(pipefd, Over_and_out, 1) != 1) {
- fprintf(stderr, "'fatal error' write to pipe failed\n");
- }
- continue;
- }
- progstate = SEND_DATA;
- goto dowait;
- case BUSY:
- case WAITING:
- default:
- sleep(1);
- dowait:
- switch (progstate) {
- case WAIT_FOR_IDLE:
- case WAIT_FOR_EOJ:
- case START:
- sleep(5);
- break;
- case SEND_DATA:
- if (write(pipefd, Send_data, 1) != 1) {
- fprintf(stderr, "send data write to pipe failed\n");
- progstate = FATAL_ERROR;
- continue;
- }
- break;
- default:
- fprintf(stderr, "unexpected program state %c\n", progstate);
- exit(1);
- }
- break;
- }
- n--;
- }
- if (n > 0) {
- fprintf(stderr, "more fds selected than requested!\n");
- exit(1);
- };
- } while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
- if (progstate == FATAL_ERROR)
- return(1);
- else
- return(0);
- }
- int
- sendfile(int infd, int printerfd, int pipefd)
- {
- unsigned char proto;
- int progstate = START;
- int i, n, nfds;
- int bytesread, bytesent = 0;
- nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
- if (write(printerfd, "\004", 1)!=1) {
- perror("sendfile:write:");
- progstate = FATAL_ERROR;
- }
- do {
- FD_ZERO(&readfds); /* lets be anal */
- FD_SET(pipefd, &readfds);
- n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &sndtimeout);
- if (debug&02) fprintf(stderr, "sendfile select returned %d\n", n);
- if (n > 0 && FD_ISSET(pipefd, &readfds)) {
- /* protocol pipe wants to be read */
- if (read(pipefd, &proto, 1) != 1) {
- fprintf(stderr, "read protocol pipe failed\n");
- return(1);
- }
- /* change state? */
- if (debug&02) fprintf(stderr, "sendfile command - <%c>\n", proto);
- switch (proto) {
- case OVER_AND_OUT:
- case END_OF_JOB:
- progstate = proto;
- break;
- case SEND_DATA:
- bytesread = 0;
- do {
- i = read(infd, &buf[bytesread], blocksize-bytesread);
- if (debug&02) fprintf(stderr, "read %d bytes\n", i);
- if (i > 0)
- bytesread += i;
- } while((i > 0) && (bytesread < blocksize));
- if (i < 0) {
- fprintf(stderr, "input file read error\n");
- progstate = FATAL_ERROR;
- break; /* from switch */
- }
- if (bytesread > 0) {
- if (debug&02) fprintf(stderr, "writing %d bytes\n", bytesread);
- if (write(printerfd, buf, bytesread)!=bytesread) {
- perror("sendfile:write:");
- progstate = FATAL_ERROR;
- } else if (write(pipefd, Sent_data, 1)!=1) {
- perror("sendfile:write:");
- progstate = FATAL_ERROR;
- } else {
- bytesent += bytesread;
- }
- fprintf(stderr, "%d sent\n", bytesent);
- fflush(stderr);
- /* we have reached the end of the input file */
- }
- if (i == 0) {
- if (progstate != WAIT_FOR_EOJ) {
- if (write(printerfd, "\004", 1)!=1) {
- perror("sendfile:write:");
- progstate = FATAL_ERROR;
- } else if (write(pipefd, Wait_for_eoj, 1)!=1) {
- perror("sendfile:write:");
- progstate = FATAL_ERROR;
- } else {
- progstate = WAIT_FOR_EOJ;
- }
- }
- }
- break;
- case REQ_STAT:
- if (write(printerfd, "\024", 1)!=1) {
- fprintf(stderr, "write to printer failed\n");
- progstate = FATAL_ERROR;
- }
- if (debug&02) fprintf(stderr, "^T");
- break;
- case FATAL_ERROR:
- progstate = FATAL_ERROR;
- }
- } else if (n < 0) {
- perror("sendfile:select:");
- progstate = FATAL_ERROR;
- } else if (n == 0) {
- sleep(1);
- fprintf(stderr, "sendfile timeout\n");
- progstate = FATAL_ERROR;
- }
- } while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
- if (write(printerfd, "\004", 1)!=1) {
- perror("sendfile:write:");
- progstate = FATAL_ERROR;
- }
- if (debug&02) fprintf(stderr, "%d bytes sent\n", bytesent);
- if (progstate == FATAL_ERROR)
- return(1);
- else
- return(0);
- }
- void main(int argc, char *argv[]) {
- int c, usgflg=0, infd, printerfd;
- int cpid, sprv;
- int pipefd[2];
- char *dialstr;
- unsigned long rprv;
- dialstr = 0;
- while ((c = getopt(argc, argv, "b:d:")) != -1)
- switch (c) {
- case 'b':
- blocksize = atoi(optarg)/10;
- if (blocksize > MESGSIZE || blocksize < 1)
- blocksize = MESGSIZE;
- break;
- case 'd':
- debug = atoi(optarg);
- dial_debug = debug;
- break;
- case '?':
- fprintf(stderr, "unknown option %c\n", c);
- usgflg++;
- break;
- }
- if (optind < argc)
- dialstr = argv[optind++];
- else
- usgflg++;
- if (usgflg) {
- fprintf(stderr, "usage: %s [-b baudrate] net!host!service [infile]\n",
- argv[0]);
- exit (2);
- }
- if (optind < argc) {
- infd = open(argv[optind], 0);
- if (infd < 0) {
- fprintf(stderr, "cannot open %s\n", argv[optind]);
- exit(1);
- }
- optind++;
- } else
- infd = 0;
- if (debug & 02)
- fprintf(stderr, "blocksize=%d\n", blocksize);
- if (debug)
- fprintf(stderr, "dialing address=%s\n", dialstr);
- printerfd = dial(dialstr, 0, 0, 0);
- if (printerfd < 0)
- exit(1);
- fprintf(stderr, "printer startup\n");
- if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipefd) < 0) {
- perror("socketpair");
- exit(1);
- }
- switch(cpid = fork()){
- case -1:
- perror("fork error");
- exit(1);
- case 0: /* child - to printer */
- close(pipefd[1]);
- sprv = sendfile(infd, printerfd, pipefd[0]);
- if (debug)
- fprintf(stderr, "to remote - exiting\n");
- exit(sprv);
- default: /* parent - from printer */
- close(pipefd[0]);
- rprv = readprinter(printerfd, pipefd[1]);
- if (debug)
- fprintf(stderr, "from remote - exiting\n");
- while(wait(&sprv) != cpid)
- ;
- exit(rprv|sprv);
- }
- }
|