source: trunk/third/transcript/src/qmscomm.bsd @ 9090

Revision 9090, 55.8 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9089, which included commits to RCS files with non-trunk default branches.
Line 
1#ifndef lint
2#define _NOTICE static char
3_NOTICE N1[] = "Copyright (c) 1985,1986,1987,1990,1991,1992 Adobe Systems Incorporated";
4_NOTICE N2[] = "GOVERNMENT END USERS: See Notice file in TranScript library directory";
5_NOTICE N3[] = "-- probably /usr/lib/ps/Notice";
6_NOTICE RCSID[] = "$Header: /afs/dev.mit.edu/source/repository/third/transcript/src/qmscomm.bsd,v 1.1.1.1 1996-10-07 20:25:52 ghudson Exp $";
7#endif
8/* pscomm.c, modified to qmscomm.c
9 *
10 * Copyright (C) 1985,1986,1987,1990,1991,1992 Adobe Systems Incorporated.
11 * All rights reserved. GOVERNMENT END USERS: See Notice file in TranScript
12 * library directory -- probably /usr/lib/ps/Notice
13 *
14 * Copyright (C) 1991 QMS, Inc
15 *
16 * 4.2BSD lpr/lpd communications filter for PostScript printers (formerly
17 * "psif" in TranScript release 1.0)
18 *
19 * pscomm is the general communications filter for sending files to a
20 *  PostScript printer via RS232 lines.  It does page accounting, error
21 *  handling/reporting, job logging, banner page printing, etc. It observes
22 *  (parts of) the PostScript file structuring conventions. In particular,
23 *  it distinguishes between PostScript files (beginning with the "%!"
24 *  magic number) -- which are shipped to the printer -- and text files (no
25 *  magic number) which are formatted and listed on the printer.  Files
26 *  which begin with "%!PS-Adobe-" may be page-reversed if the target
27 *  printer has that option specified.
28 *
29 * depending on the values of BANNERFIRST and BANNERLAST, pscomm looks for a
30 * file named ".banner", (created by the "of" filter) in the current
31 * working directory and ships it to the printer also.
32 *
33 * pscomm gets called with:
34 *    stdin     == the file to print (may be a pipe!)
35 *    stdout    == the printer
36 *    stderr    == the printer log file
37 *    cwd       == the spool directory
38 *    argv      == set up by interface shell script:
39 *   filtername -P printer -p filtername [-r] (don't ever reverse)
40 *              -n login -h host [accntfile]
41 *
42 * environ  == various environment variable effect behavior
43 *    VERBOSELOG - do verbose log file output
44 *    BANNERFIRST - print .banner before job
45 *    BANNERLAST - print .banner after job
46 *    REVERSE - page reversal filter program (no reversal if null or missing)
47 *    PSLIBDIR - transcript library directory
48 *    JOBOUTPUT - file for actual printer stream output (if defined)
49 *
50 * General flow of control for communications: 1) "Sync" with printer: Send
51 * small accounting job, and make sure reply matches what we sent.  If not
52 * a match, sleep a little and try again. 2) Send user job: Start listener
53 * process, and write user job to printer. Banner may get sent first.
54 * CTRL-T gets sent occasionally. Listener get ALL output from printer. If
55 * listener sees "idle" status message it aborts, which flushes the job
56 * (printer rebooted). End-of-job character is written to printer after
57 * job has been sent. 3) Wait for user job to complete: Listener process
58 * exits when end-of-job character is received from printer.  "idle"
59 * status will make job abort as in (2) above. 4) If doing accounting or
60 * "bannerlast", use accounting job to "sync" printer again.  A banner may
61 * be sent before the accounting job. Note that only output from the user
62 * job is read by the listener process. Output from the banner, for
63 * instance, is ignored (there shouldn't be any). Also, note that jobs
64 * containing or printing end-of-job characters will may print correctly
65 * -- the "sync" process guarantees the next job will be executed
66 * correctly, however.
67 *
68 * pscomm depends on certain additional features of the 4.2BSD spooling
69 * architecture.  In particular it assumes that the printer status file
70 * has the default name (./status) and it uses this file to communicate
71 * printer error status information to the user -- the contents of the
72 * status file gets incorporated in "lpq" and "lpc status" messages.
73 *
74 * RCSLOG:
75 * $Log: not supported by cvs2svn $
76# Revision 1.8  1992/08/21  16:26:32  snichols
77# Release 4.0
78#
79# Revision 1.7  1992/08/19  20:39:24  snichols
80# Neglected to send banner pages, if desired.
81#
82# Revision 1.6  1992/07/14  22:10:28  snichols
83# Updated copyrights.
84#
85# Revision 1.5  1992/06/30  00:16:04  snichols
86# include file messes.
87#
88# Revision 1.4  1992/06/04  18:26:19  snichols
89# added missing parameter to fprintf, and added void declaration for
90# croak_udp.
91#
92# Revision 1.3  1992/06/01  22:16:41  snichols
93# ifdef'ed out some unnecessary code when building qmscomm.
94#
95# Revision 1.2  1992/05/26  21:56:00  snichols
96# comments after endif
97#
98# Revision 1.1  1992/05/19  23:09:13  snichols
99# Initial revision
100#
101 * Revision 3.0  1991/06/17  16:50:45  snichols
102 * Release 3.0
103 *
104 * Revision 2.9  1991/06/11  23:34:42  snichols
105 * cleaned up some include file stuff - don't need sys/resource.h, should
106 * include time.h, rather than sys/time.h
107 *
108 * Revision 2.8  1990/12/12  10:20:54  snichols
109 * missed a spot in the cleanup.
110 *
111 * Revision 2.7  90/12/12  10:19:03  snichols
112 * new configuration stuff
113 *
114 * Revision 2.6  90/12/11  09:52:25  snichols
115 * cleaned up after emacs went wild reformatting comments.
116 *
117 * Revision 2.5  90/11/21  10:15:22  snichols
118 * handle status messages that don't end with \r\n.
119 *
120 * Revision 2.4  90/11/16  14:12:09  snichols
121 * use str(r)chr instead of (r)index (XPG3).  Added pipelining.  Fixed
122 * various error messages with missing parameters.
123 *
124 * Revision 2.3  90/10/11  15:03:15  snichols
125 * Now only deals with communication.  Page reversal, text2PS
126 * conversion, and other features moved into psdman.
127 *
128 * Revision 2.1  90/07/10  14:49:37  snichols
129 * Re-sync versions and releases
130 *
131 * Revision 2.2  87/11/17  16:51:17  byron Release 2.1
132 *
133 * Revision 2.1.1.19  87/11/12  13:41:02  byron
134 * Changed Government user's notice.
135 *
136 * Revision 2.1.1.18  87/09/30  16:52:36  byron
137 * 1. Added some info to the "printer sync" error message.
138 * 2. Changed the timeout parsing not to recognize "PrinterError: timeout".
139 *
140 * Revision 2.1.1.17  87/09/22  11:04:37  byron
141 * Added check for "printer:" in status, which is like "PrinterError:".
142 *
143 * Revision 2.1.1.16  87/09/16  11:16:25  byron
144 * 1. Added check for printer timeout, which aborts job and tries
145 * again. 2. Added warning if CTRL-D is found while job is still being sent.
146 *
147 *
148 * Revision 2.1.1.15  87/09/09  10:10:22  byron
149 * Added blank-skipping to status close string, so that %%[ ... ] %% works.
150 *
151 * Revision 2.1.1.14  87/07/24  16:08:25  byron
152 * Added some error checking that was developed while debugging pscomm.sysv.
153 *
154 * Revision 2.1.1.13  87/07/15  16:27:33  byron
155 * Changed "unexpected EOF" message when child gets EOF from printer.
156 *
157 * Revision 2.1.1.12  87/07/01  11:41:04  byron
158 * Changed Bridge code to flush I/O buffers in resetprt().
159 * Also, added more code to give up when job is aborted and
160 * things look confused.  Added errno declaration for portability.
161 *
162 * Revision 2.1.1.11  87/06/25  15:58:45  byron
163 * Bug in page reverser process critical region.  Would have caused
164 * pscomm to hang if reverser got done before parent continued
165 * (and redid stdin).
166 *
167 * Revision 2.1.1.10  87/06/24  11:48:49  byron
168 * Added back some RestoreStatus calls.  This makes status correct when
169 * an error happens in one file in a multi-file user job.
170 *
171 * Revision 2.1.1.9  87/06/22  10:15:37  byron
172 * 1. Changed when status message is restored -- don't do it so often.
173 * 2. Fixed bugs where original value of errno was getting lost before it was
174 * printed out.  Now save and restore errno around some system calls.
175 *
176 * Revision 2.1.1.8  87/06/18  16:07:37  byron
177 * Complete rearrangement of code (most algorithms the same). Goals:
178 * 1. Make listener process simpler, and able to communicate better w/ parent.
179 * 2. Make sure printer is replying to the messages we are sending.
180 *    syncprinter() accomplishes this using accounting info.
181 * 3. Eliminate race conditions, especially involving signal processing.
182 *    Had multiple signal() calls -- now has single signal() call for each
183 *    signal and state variable (intstate). Also added STARTCRIT and ENDCRIT.
184 * 4. Explicitly kill existing children, and make sure they go away.
185 * 5. Make goto's and setjmp/longjmp's more straightforward (if possible).
186 *
187 * Revision 2.1.1.7  87/05/14  16:52:43  byron
188 * Added support for BANNERLAST=2, which unlinks the .banner file after use.
189 * This breaks multiple-copy operation, but make -h jobs work correctly.
190 *
191 * Revision 2.1.1.6  87/04/23  10:26:10  byron
192 * Copyright notice.
193 *
194 * Revision 2.1.1.5  87/03/24  16:44:31  shore
195 * fixed parsing of %%X messages back from printer, X was getting dropped
196 *
197 * Revision 2.1.1.4  86/11/02  14:40:56  shore
198 * fixed race around page reversal
199 * (thanks to Ron Stanonik @ nprdc)
200 *
201 * Revision 2.1.1.3  86/07/15  15:05:45  shore
202 * fixed accounting/BannerLast end-of-job race
203 *
204 * Revision 2.1.1.2  86/06/08  11:12:50  shore
205 * fixed 11 character minimum magic restriction
206 *
207 * Revision 2.1.1.1  86/03/25  13:33:13  shore
208 * fixed lpr -p bug (when text input is a pipe)
209 * fixed lprm bug when printer is off-line
210 *
211 * Revision 2.1  85/11/24  11:50:16  shore
212 * Product Release 2.0
213 *
214 * Revision 1.1  85/11/20  00:35:21  shore
215 * Initial revision
216 *
217 * Revision 1.2  85/05/14  11:25:29  shore
218 * better support for BANNERLAST, still buggy though
219 *
220 *
221 */
222
223
224#include <ctype.h>
225#include <setjmp.h>
226#include <sgtty.h>
227#include <signal.h>
228#include <stdio.h>
229#include <string.h>
230#include <errno.h>
231
232#include <sys/file.h>
233#include <sys/ioctl.h>
234#ifdef XPG3
235#include <time.h>
236#else
237#include <sys/time.h>
238#endif
239#include <sys/wait.h>
240#include <sys/types.h>
241#include <sys/stat.h>
242#include <fcntl.h>
243
244#ifdef BRIDGE                   /* Unsupported code for Bridge
245                                   communications boxes */
246#include <sys/socket.h>
247#include <netdb.h>
248#include <netinet/in.h>
249#endif /* BRIDGE */
250
251#ifdef QMS
252#include <netdb.h>
253#include <sys/socket.h>
254#include <netinet/in.h>
255#include <sys/time.h>
256#include "qef.h"
257#endif /* QMS */
258
259#include "transcript.h"
260#include "psspool.h"
261#include "config.h"
262
263#ifdef BDEBUG
264#define debugp(x) {fprintf x ; (void) fflush(stderr);}
265#else
266#define debugp(x)
267#endif /* BDEBUG */
268
269/* the following string is sent to the printer when we want it to report
270   its current pagecount (for accounting) */
271
272private char *getpages =
273"\n(%%%%[ pagecount: )print statusdict/pagecount get exec(                )cvs \
274print(, %d %d ]%%%%)= flush\n%s";
275
276private jmp_buf initlabel, synclabel, sendlabel, croaklabel;
277
278private char *prog;             /* invoking program name */
279private char *name;             /* user login name */
280private char *host;             /* host name */
281private char *pname;            /* printer name */
282private char *accountingfile;   /* file for printer accounting */
283private int doactng;            /* true if we can do accounting */
284private int progress, oldprogress;      /* finite progress counts */
285private int getstatus = FALSE;  /* TRUE = Query printer for status */
286private int newstatmsg = FALSE; /* TRUE = We changed status message */
287private int childdone = FALSE;  /* TRUE = Listener process finished */
288private int jobaborted = FALSE; /* TRUE = Aborting current job */
289private long startpagecount;    /* Page count at start of job */
290private long endpagecount;      /* Page count at end of job */
291private long starttime;         /* Timer start. For status warnings */
292private int saveerror;          /* Place to save errno when exiting */
293
294private char *bannerfirst;
295private char *bannerlast;
296private char *verboselog;
297private int BannerFirst;
298private int BannerLast;
299private int UnlinkBannerLast;
300private int VerboseLog;
301private int pipeline = FALSE;
302
303/* Interrupt state variables */
304typedef enum {                  /* Values for "die" interrupts, like
305                                   SIGINT */
306    init,                       /* Initialization */
307    syncstart,                  /* Synchronize communications with printer */
308    sending,                    /* Send info to printer */
309    waiting,                    /* Waiting for listener to get EOF from
310                                   prntr */
311    lastpart,                   /* Final processing following user job */
312    synclast,                   /* Syncronize communications at the end */
313    ending,                     /* Cleaning up */
314    croaking,                   /* Abnormal exit, waiting for children to
315                                   die */
316    child                       /* Used ONLY for the child (listener)
317                                   process */
318}    dievals;
319private dievals intstate;       /* State of interrupts */
320private flagsig;                /* TRUE = On signal receipt, just set flag */
321#define DIE_INT    1            /* Got a "die" interrupt. like SIGINT */
322#define ALARM_INT  2            /* Got an alarm */
323#define EMT_INT    4            /* Got a SIGEMT signal (child-parent
324                                   comm.) */
325private int gotsig;             /* Mask that may take any of the above
326                                   values */
327
328#define STARTCRIT() {gotsig=0; flagsig=TRUE;}   /* Start a critical region */
329#define ENDCRIT()   {flagsig=FALSE;}    /* End a critical region */
330
331/* WARNING: Make sure reapchildren() routine kills all processes we
332   started */
333private int cpid = 0;           /* listener pid */
334private int mpid = 0;           /* current process pid */
335private int wpid;               /* Temp pid */
336private union wait status;      /* Return value from wait() */
337
338private char abortbuf[] = "\003";       /* ^C abort */
339private char statusbuf[] = "\024";      /* ^T status */
340private char eofbuf[] = "\004"; /* ^D end of file */
341
342/* global file descriptors (avoid stdio buffering!) */
343private int fdsend;             /* to printer (from stdout) */
344private int fdlisten;           /* from printer (same tty line) */
345private int fdinput;            /* file to print (from stdin) */
346
347private FILE *psin = NULL;      /* Buffered printer input */
348private FILE *jobout;           /* special printer output log */
349
350#ifdef BRIDGE
351/* socket numbers here are for SUN with ntohs() to convert */
352#define GENERIC_SOCKET  ntohs((u_short) 0x07D0)
353#define NMUI_SOCKET     ntohs((u_short) 0x004D)
354private int socklen;
355struct sockaddr_in dest;
356struct hostent *hp;
357#endif /* BRIDGE */
358
359#ifdef QMS
360struct sockaddr_in printaddr;
361struct command_opt copts;
362struct sockaddr_in claddr;
363struct hostent *printhost;
364int tpfd, ufd;
365#endif /* QMS */
366
367/* Return values from the NextCh() routine */
368typedef enum {
369    ChOk,                       /* Everything is fine */
370    ChIdle,                     /* We got status="idle" from the printer */
371    ChTimeout                   /* The printer timed out */
372}    ChStat;
373
374
375extern char *getenv();
376extern int errno;
377
378private VOID GotDieSig();
379private VOID GotAlarmSig();
380private VOID GotEmtSig();
381private VOID syncprinter();
382private VOID listenexit();
383private VOID closedown();
384private VOID myexit1(), myexit2();
385private VOID croak();
386private VOID acctentry();
387private char *FindPattern();
388private ChStat NextCh();
389private SendBanner();
390private NextChInit();
391private BackupStatus();
392private RestoreStatus();
393private Status();
394private int resetprt();
395
396/* The following are alarms settings for various states of this program */
397#define SENDALARM 90            /* Status check while sending job to
398                                   printer */
399#define WAITALARM 90            /* Status check while waiting for job to
400                                   end */
401#define CROAKALARM 10           /* Waiting for child processes to die */
402#define CHILDWAIT 60            /* Child waiting to die -- checking on
403                                   parent */
404#define ABORTALARM 90           /* Waiting for printer to respond to job
405                                   abort */
406#define SYNCALARM 30            /* Waiting for response to communications
407                                   sync */
408
409#define MAXSLEEP  15            /* Max time to sleep while sync'ing
410                                   printer */
411
412/* Exit values from the listener process */
413#define LIS_NORMAL 0            /* No problems */
414#define LIS_EOF    1            /* Listener got EOF on printer */
415#define LIS_IDLE   2            /* Heard a "status: idle" from the printer */
416#define LIS_DIE    3            /* Parent told listener to kill itself */
417#define LIS_NOPARENT  4         /* Parent is no longer present */
418#define LIS_ERROR  5            /* Unrecoverable error */
419#define LIS_TIMEOUT 6           /* Printer timed out */
420
421
422
423main(argc, argv)                /* MAIN ROUTINE */
424    int argc;
425    char *argv[];
426{
427    register char  *cp;
428    register int    cnt, wc;
429    register char  *mbp;
430
431    char  **av;
432    FILE * streamin;
433
434    char   *q;
435
436    int     tmp;                /* fd for temporary when input is text
437                                   pipe */
438    struct stat sbuf;
439
440    char    mybuf[BUFSIZ];
441    int     fdpipe[2];
442    int     i;
443#ifdef QMS
444    int     sleeptime, pstatus;
445    int     bannerfd;
446#endif /* QMS */
447
448    mpid = getpid();/* Save the current process ID for later */
449
450    /* initialize signal processing */
451    flagsig = FALSE;/* Process the signals */
452    intstate = init;/* We are initializing things now */
453    VOIDC signal(SIGINT, GotDieSig);
454    VOIDC signal(SIGHUP, GotDieSig);
455    VOIDC signal(SIGTERM, GotDieSig);
456    VOIDC signal(SIGALRM, GotAlarmSig);
457    VOIDC signal(SIGEMT, GotEmtSig);
458
459    /* parse command-line arguments */
460    /* the argv (see header comments) comes from the spooler daemon */
461    /* itself, so it should be canonical, but at least one 4.2-based */
462    /* system uses -nlogin -hhost (insead of -n login -h host) so I */
463    /* check for both */
464
465    av = argv;
466    prog = *av;
467
468    while (--argc) {
469        if (*(cp = *++av) == '-') {
470            switch (*(cp + 1)) {
471                case 'P': /* printer name */
472                    argc--;
473                    pname = *(++av);
474                    break;
475
476                case 'n': /* user name */
477                    argc--;
478                    name = *(++av);
479                    break;
480
481                case 'h': /* host */
482                    argc--;
483                    host = *(++av);
484                    break;
485
486                case 'p': /* prog */
487                    argc--;
488                    prog = *(++av);
489                    break;
490
491                default: /* unknown */
492                    fprintf(stderr, "%s: unknown option: %s\n", prog, cp);
493                    break;
494            }
495        }
496        else
497            accountingfile = cp;
498    }
499
500    debugp((stderr, "args: %s %s %s %s\n", prog, host, name, accountingfile));
501
502    /* do printer-specific options processing */
503
504    VerboseLog = 1;
505    BannerFirst = BannerLast = 0;
506    UnlinkBannerLast = 0;
507    if (bannerfirst = envget("BANNERFIRST"))
508        BannerFirst = atoi(bannerfirst);
509    if (bannerlast = envget("BANNERLAST")) {
510        switch (atoi(bannerlast)) {
511            case 0:
512            default:
513                BannerLast = UnlinkBannerLast = 0;
514                break;
515            case 1:
516                BannerLast = 1;
517                UnlinkBannerLast = 0;/* No unlink banner */
518                break;
519            case 2:
520                BannerLast = UnlinkBannerLast = 1;
521 /* Unlink banner file */
522                break;
523        }
524    }
525    if (verboselog = envget("VERBOSELOG")) {
526        VerboseLog = atoi(verboselog);
527    }
528    if (VerboseLog) {
529        VOIDC time(&starttime);
530        fprintf(stderr, "%s: %s:%s %s start - %s", prog, host, name, pname,
531                ctime(&starttime));
532        VOIDC fflush(stderr);
533    }
534    if (q = envget("PIPELINE"))
535        pipeline = atoi(q);
536    debugp((stderr, "%s: pid %d ppid %d\n", prog, getpid(), getppid()));
537    debugp((stderr, "%s: options BF=%d BL=%d VL=%d PL=%d \n", prog, BannerFirst,
538                BannerLast, VerboseLog, pipeline));
539
540
541    streamin = stdin;
542    fdinput = fileno(streamin);/* the file to print */
543
544    /* get control of the "status" message file. we copy the current one
545       to ".status" so we can restore it on exit (to be clean). Our
546       ability to use this is publicized nowhere in the 4.2 lpr
547       documentation, so things might go bad for us. We will use it to
548       report that printer errors condition has been detected, and the
549       printer should be checked. Unfortunately, this notice may persist
550       through the end of the print job, but this is no big deal. */
551    BackupStatus(".status", "status");
552
553#ifdef BRIDGE
554    /* open network connection to the printer */
555    if (envget("NETNAME"))
556        pname = envget("NETNAME");
557
558    if ((hp = gethostbyname(pname)) == NULL) {
559        myexit2(prog, "badhost", TRYAGAIN);
560    }
561    bcopy(hp -> h_addr, &dest.sin_addr.s_addr, hp -> h_length);
562    dest.sin_family = AF_INET;
563    dest.sin_port = GENERIC_SOCKET;
564
565    if ((fdsend = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
566        myexit2(prog, "opening socket", TRYAGAIN);
567    }
568    socklen = sizeof(dest);
569    if (connect(fdsend, &dest, socklen)) {
570        saveerror = errno;
571        sleep(30);
572        errno = saveerror;
573        myexit2(prog, "connecting", TRYAGAIN);
574    }
575    if (getsockname(fdsend, &dest, &socklen)) {
576        saveerror = errno;
577        VOIDC close(fdsend);
578        errno = saveerror;
579        myexit2(prog, "getting socket name", TRYAGAIN);
580    }
581    /* socket options processing */
582    if (setsockopt(fdsend, SOL_SOCKET, SO_DONTLINGER, 0, 0) < 0) {
583        perror("sockopt dontlinger");
584    }
585#else
586#ifdef QMS
587    init_data(&copts);
588
589    Status("Starting job");
590
591    if ((printhost = gethostbyname(pname)) == (struct hostent *) NULL) {
592        fprintf(stderr, "gethostbyname failed\n");
593        exit(1);
594    }
595
596    debugp((stderr,
597            "printer hostent fields:\n h_name= %s h_addr= %s h_length=0x%x\n",
598            printhost -> h_name,
599            inet_ntoa((struct in_addr *) (printhost -> h_addr)),
600            printhost -> h_length));
601
602    /*
603     * Set up the address of the peer process with whom we want to
604     * communicate. The same address (printaddr) can be used for both
605     * TCP and UDP connections since both tcp port and udp port numbers
606     * are the same (PRINTER_PORT).
607     */
608
609    bzero((char *) & printaddr, sizeof(printaddr));
610    printaddr.sin_family = AF_INET;
611    printaddr.sin_port = htons(PRINTER_PORT);
612    bcopy((char *) printhost->h_addr,
613            (char *) &printaddr.sin_addr, printhost->h_length);
614
615    copts.cpid = spawn_statproc();
616
617    /*
618     * Open a tcp socket.
619     */
620    if ((tpfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
621        fprintf(stderr, "%s: failed to open a socket\n", prog);
622        perror("");
623        croak(TRYAGAIN);
624    }
625    debugp((stderr, "TCP file descriptor = %d\n", tpfd));
626
627    /*
628     * Set up UDP socket.
629     */
630    if ((ufd = setup_udp()) < 0) {
631        fprintf(stderr, "Failed to open UDP connection\n");
632        perror("");
633        croak(TRYAGAIN);
634    }
635    debugp((stderr, "UDP file descriptor = %d\n", tpfd));
636#else
637    fdsend = fileno(stdout);/* the printer (write) */
638#endif                          /* QMS */
639#endif /* BRIDGE */
640
641    doactng = name && accountingfile && (access(accountingfile, W_OK) == 0);
642
643
644    intstate = syncstart;/* start communications */
645
646#ifdef QMS
647    /* Get printer status using UDP based STATUS1 protocol. */
648    pstatus = get_printstat(ufd, 0, (char *) 0);
649    sleeptime = SLEEPTIME;
650    while(pstatus == 0) {
651        sleeptime = sleeptime * 2;
652        sleep(sleeptime);
653        pstatus = get_printstat(ufd, 0, (char *) 0);
654    }
655#else   
656    debugp((stderr, "%s: sync printer and get initial page count\n", prog));
657    syncprinter(&startpagecount);/* Make sure printer is listening */
658#endif /* QMS */
659
660    if (newstatmsg)
661        RestoreStatus();/* Put back old message -- we're OK now */
662
663    STARTCRIT();/* Child state change and setjmp() */
664    intstate = sending;
665
666#ifdef QMS
667    /*
668     * Connect (TCP) to the peer on the printer.
669     */
670    if (connect(tpfd, &printaddr, sizeof(printaddr)) < 0) {
671        fprintf(stderr, "Failed to connect to printer\n");
672        perror("");
673        croak(TRYAGAIN);
674    }
675
676    /*
677     * Printer is accepting data; Transfer it (over TCP).
678     */
679    if (BannerFirst) {
680        if ((bannerfd = open(".banner", O_RDONLY|O_NDELAY, 0)) < 0) {
681            debugp((stderr, "%s: No banner file\n", prog));
682        }
683        else {
684            if (xfer_data(tpfd, &copts, bannerfd) < 0) {
685                printf("Data Transfer to the printer failed\n");
686                Status("communications ended abnormally (banner)");
687                croak(TRYAGAIN);
688            }
689            close(bannerfd);
690        }
691        if (!BannerLast)
692            unlink(".banner");
693    }
694    if (xfer_data(tpfd, &copts, fdinput) < 0) {
695        printf("Data Transfer to the printer failed\n");
696        Status("communications ended abnormally");
697        croak(TRYAGAIN);
698    }
699    if (BannerLast) {
700        if ((bannerfd = open(".banner", O_RDONLY|O_NDELAY, 0)) < 0) {
701            debugp((stderr, "%s: No banner file\n", prog));
702        }
703        else {
704            if (xfer_data(tpfd, &copts, bannerfd) < 0) {
705                printf("Data Transfer to the printer failed\n");
706                Status("communications ended abnormally (banner)");
707                croak(TRYAGAIN);
708            }
709            close(bannerfd);
710        }
711        if (UnlinkBannerLast)
712            unlink(".banner");
713    }
714
715    if (close(tpfd) < 0) {
716        fprintf(stderr, "Failed to close tcp socket\n");
717        perror("");
718        croak(TRYAGAIN);
719    }
720
721    if (close(ufd) < 0) {
722        fprintf(stderr, "Failed to close udp socket\n");
723        perror("");
724        croak(TRYAGAIN);
725    }
726#else
727    if ((cpid = fork()) < 0)
728        myexit1(prog, THROW_AWAY);
729    else
730        if (cpid) {/* START PARENT -- SENDER */
731
732            if (setjmp(sendlabel))
733                goto donefile;
734            ENDCRIT();/* Child state change and setjmp() */
735            if (gotsig & DIE_INT)
736                closedown();
737
738            debugp((stderr, "%s: printer responding\n", prog));
739            progress = oldprogress = 0;/* finite progress on sender */
740
741            /* initial break page ? */
742            if (BannerFirst) {
743                SendBanner();
744                progress++;
745                if (!BannerLast)
746                    VOIDC unlink(".banner");
747            }
748
749            /* now ship the rest of the file */
750
751            VOIDC alarm(SENDALARM);/* schedule an alarm */
752
753            while ((cnt = read(fdinput, mybuf, sizeof mybuf)) > 0) {
754                if (getstatus) {/* Get printer status sometimes */
755                    VOIDC write(fdsend, statusbuf, 1);
756                    getstatus = FALSE;
757                    progress++;
758                }
759                mbp = mybuf;
760                while ((cnt > 0) && ((wc = write(fdsend, mbp, cnt)) != cnt)) {
761                    if (wc < 0) {
762                        fprintf(stderr, "%s: error writing to printer", prog);
763                        perror("");
764                        sleep(10);
765                        croak(TRYAGAIN);
766                    }
767                    mbp += wc;
768                    cnt -= wc;
769                    progress++;
770                }
771                progress++;
772            }
773            if (cnt < 0) {
774                fprintf(stderr, "%s: error reading from stdin", prog);
775                perror("");
776                sleep(10);
777                croak(TRYAGAIN);
778            }
779
780    donefile: /* Done sending the user's file */
781
782            /* Send the PostScript end-of-job character */
783            debugp((stderr, "%s: done sending\n", prog));
784            STARTCRIT();/* Only do end-of-job char once */
785            VOIDC write(fdsend, eofbuf, 1);
786            intstate = waiting;/* Waiting for end of user job */
787            ENDCRIT();/* Only do end-of-job char once */
788            if (gotsig & DIE_INT)
789                VOIDC kill(getpid(), SIGINT);
790
791            if (!pipeline) {
792                VOIDC alarm(WAITALARM);
793                while (!childdone)
794                    pause();/* Wait for listener to finish */
795                VOIDC alarm(0);
796            }
797
798            intstate = lastpart;
799            if (BannerLast || doactng) {
800                if (BannerLast) {/* final banner page */
801                    SendBanner();
802                    if (UnlinkBannerLast)
803                        VOIDC unlink(".banner");
804                }
805                if (!pipeline) {
806                    intstate = synclast;
807                    syncprinter(&endpagecount);
808 /* Get communications in sync again */
809                    intstate = ending;
810                    if (doactng)
811                        VOIDC acctentry(startpagecount, endpagecount);
812                }
813            }
814            intstate = ending;
815
816            if (VerboseLog) {
817                VOIDC time(&starttime);
818                fprintf(stderr, "%s: end - %s", prog, ctime(&starttime));
819                VOIDC fflush(stderr);
820            }
821            RestoreStatus();
822            exit(0);
823
824        }/* END PARENT -- SENDER */
825        else {/* START CHILD -- LISTENER */
826            /* This process listens while the user job is sent. It
827               communicates with the parent by signalling a SIGEMT, then
828               exiting.  The exit code is the only form of communication
829               implemented. The user job is aborted: A) If we get a status
830               of "idle" back from the printer (see NextCh() routine), or
831               B) If we get EOF back while reading the printer (somebody
832               unplugged the comm line?), or C) If we receive a SIGEMT
833               signal (parent wants us to die). This process ignores most
834               signals except SIGEMT. A signal is used before exiting to
835               make it easy for the parent to use wait() to get the child
836               status, and for portability to System V. */
837            register int    r;
838            register    ChStat i;
839            char   *outname;    /* file name for job output */
840            int     havejobout = FALSE;/* flag if jobout != stderr */
841
842            intstate = child;
843            ENDCRIT();/* Child state change and setjmp() */
844            prog = "pslisten";/* Change our name */
845
846            /* get jobout from environment if there, otherwise use stderr
847            */
848            if (((outname = envget("JOBOUTPUT")) == NULL)
849                    || ((jobout = fopen(outname, "w")) == NULL)) {
850                jobout = stderr;
851            }
852            else
853                havejobout = TRUE;
854
855            /* listen for the user job */
856            NextChInit();
857            while (TRUE) {
858                r = getc(psin);
859                if ((r & 0377) == 004)
860                    break;/* Printer says end of job */
861                else
862                    if (r == EOF) {/* Printer comm line died? */
863                        if (feof(psin)) {
864                            VOIDC kill(getppid(), SIGEMT);
865 /* Tell parent we exit */
866                            exit(LIS_EOF);
867                        }
868                        else {
869                            if (errno != EINTR) {/* Got a random error */
870                                fprintf(stderr, "%s: Error", prog);
871                                perror("");
872                                fflush(stderr);
873                                exit(LIS_ERROR);
874                            }
875                        }
876                        clearerr(psin);
877 /* Make it so we can read again */
878                        continue;
879                    }
880                if ((i = NextCh(r)) != ChOk) {
881                    switch ((int) i) {
882                        case ChIdle:
883                            VOIDC kill(getppid(), SIGEMT);
884                            exit(LIS_IDLE);
885                        case ChTimeout:
886                            VOIDC kill(getppid(), SIGEMT);
887                            exit(LIS_TIMEOUT);
888                    }
889                }
890            }
891
892            debugp((stderr, "%s: listener saw eof, done listening\n", prog));
893            if (havejobout)
894                VOIDC fclose(jobout);
895            VOIDC fclose(psin);
896            VOIDC kill(getppid(), SIGEMT);
897 /* Tell parent we are exiting */
898            exit(LIS_NORMAL);
899        }/* END CHILD -- LISTENER */
900    /* Can't get here */
901#endif                          /* QMS */
902#ifdef QMS
903    kill_statproc();
904    if (VerboseLog) {
905        VOIDC time(&starttime);
906        fprintf(stderr, "%s: end - %s", prog, ctime(&starttime));
907        VOIDC fflush(stderr);
908    }
909    RestoreStatus();
910    exit(0);
911#endif   
912}
913
914/* send the file ".banner" */
915private SendBanner()
916{
917    register int banner;
918    int cnt;
919    char buf[BUFSIZ];
920
921    if ((banner = open(".banner",O_RDONLY|O_NDELAY,0)) < 0) {
922        debugp((stderr,"%s: No banner file\n",prog));
923        return;
924    }
925    while ((cnt = read(banner,buf,sizeof buf)) > 0) {
926        VOIDC write(fdsend,buf,cnt);
927    }
928    VOIDC close(banner);
929}
930
931/* search backwards from p in start for patt */
932private char *FindPattern(p, start, patt)
933register char *p;
934         char *start;
935         char *patt;
936{
937    int patlen;
938    register char c;
939
940    patlen = strlen(patt);
941    c = *patt;
942   
943    p -= patlen;
944    for (; p >= start; p--) {
945        if (c == *p && strncmp(p, patt, patlen) == 0) return(p);
946    }
947    return ((char *)NULL);
948}
949
950/* Static variables for NextCh() routine */
951static char linebuf[BUFSIZ];
952static char *cp;
953static enum {normal, onep, twop, inmessage,
954             close1, close2, close3, close4} st;
955static int level;
956
957/* Initialize the NextCh routine */
958private NextChInit() {
959    cp = linebuf;
960    st = normal;
961    level = 0;
962}
963
964/* Overflowed the NextCh() buffer */
965private NextChErr()
966{
967    *cp = '\0';
968    fprintf(stderr,"%s: Status message too long: (%s)\n",prog,linebuf);
969    VOIDC fflush(stderr);
970    st = normal;
971    cp = linebuf;
972}
973
974/* Put one character in the status line buffer. Called by NextCh() */
975#define NextChChar(c) if (cp <= linebuf+BUFSIZ-2) *cp++ = c; else NextChErr();
976
977/* Process a char from the printer.  This picks out and processes status
978 * and PrinterError messages.  The printer status file is handled IN THIS
979 * ROUTINE when status messages are encountered -- there is no way to
980 * control how these are handled outside this routine.
981 */
982private ChStat NextCh(c)
983register int c;
984{
985    char *match, *last;
986
987    switch ((int) st) {
988        case normal:
989            if (c == '%') {
990                st = onep;
991                cp = linebuf;
992                NextChChar(c);
993                break;
994            }
995            putc(c, jobout);
996            VOIDC fflush(jobout);
997            break;
998        case onep:
999            if (c == '%') {
1000                st = twop;
1001                NextChChar(c);
1002                break;
1003            }
1004            putc('%', jobout);
1005            putc(c, jobout);
1006            VOIDC fflush(jobout);
1007            st = normal;
1008            break;
1009        case twop:
1010            if (c == '\[') {
1011                st = inmessage;
1012                level++;
1013                NextChChar(c);
1014                break;
1015            }
1016            if (c == '\%') {
1017                putc('%', jobout);
1018                VOIDC fflush(jobout);
1019                /* don't do anything to cp */
1020                break;
1021            }
1022            putc('%', jobout);
1023            putc('%', jobout);
1024            putc(c, jobout);
1025            VOIDC fflush(jobout);
1026            st = normal;
1027            break;
1028        case inmessage:
1029            NextChChar(c);
1030            switch (c) {
1031                case '\]':
1032                    st = close1;
1033                    level--;
1034                    break;
1035                case '\[':
1036                    st = inmessage;
1037                    level++;
1038                    break;
1039                }
1040            break;
1041        case close1:
1042            NextChChar(c);
1043            switch (c) {
1044                case '%':
1045                    st = close2;
1046                    break;
1047                case '\]':
1048                    st = close1;
1049                    level--;
1050                    break;
1051                case ' ':
1052                    break;
1053                default:
1054                    st = inmessage;
1055                    break;
1056            }
1057            break;
1058        case close2:
1059            NextChChar(c);
1060            switch (c) {
1061                case '%':
1062                    st = close3;
1063                    break;
1064                case '\]':
1065                    st = close1;
1066                    level--;
1067                    break;
1068                default:
1069                    st = inmessage;
1070                    break;
1071            }
1072            break;
1073        case close3:
1074            switch (c) {
1075                case '\r':
1076                    st = close4;
1077                    NextChChar(c);
1078                    break;
1079                case '\]':
1080                    st = close1;
1081                    NextChChar(c);
1082                    level--;
1083                    break;
1084                case '\n':
1085                    st = normal;
1086                    NextChChar(c);
1087                    break;
1088                default:
1089                    if (level > 0) {
1090                        NextChChar(c);
1091                        st = inmessage;
1092                        break;
1093                    }
1094                    st = normal;
1095                    putc(c, jobout);
1096                    fflush(jobout);
1097                    break;
1098            }
1099            break;
1100        case close4:
1101            switch (c) {
1102                case '\n':
1103                    st = normal;
1104                    NextChChar(c);
1105                    break;
1106                case '\]':
1107                    st = close1;
1108                    level--;
1109                    NextChChar(c);
1110                    break;
1111                default:
1112                    if (level > 0) {
1113                        st = inmessage;
1114                        NextChChar(c);
1115                        break;
1116                    }
1117                    st = normal;
1118                    putc(c,jobout);
1119                    fflush(jobout);
1120                    break;
1121            }
1122            if (st == normal) {
1123                /* parse complete message */
1124                last = cp;
1125                *cp = '\0';
1126                debugp((stderr, ">>%s", linebuf));
1127                if (match = FindPattern(cp, linebuf, " pagecount: ")) {
1128                    /* Do nothing */
1129                }
1130                else if (match = FindPattern(cp, linebuf, " PrinterError: ")) {
1131                    if (*(match - 1) != ':') {
1132                        fprintf(stderr, "%s", linebuf);
1133                        VOIDC fflush(stderr);
1134                        *(last - 6) = 0;
1135                        Status(match + 15);
1136                    }
1137                    else {
1138                        last = strchr(match, ';');
1139                        *last = '\0';
1140                        Status(match + 15);
1141                    }
1142                }
1143                /* PrinterError's for certain (rare) printers */
1144                else if (match = FindPattern(cp, linebuf, " printer: ")) {
1145                    if (*(match - 1) != ':') {
1146                        fprintf(stderr, "%s", linebuf);
1147                        VOIDC fflush(stderr);
1148                        *(last - 6) = 0;
1149                        Status(match + 10);
1150                    }
1151                    else {
1152                        last = strchr(match, ';');
1153                        *last = '\0';
1154                        Status(match + 10);
1155                    }
1156                }
1157                else if (match = FindPattern(cp, linebuf, " status: ")) {
1158                    match += 9;
1159                    if (strncmp(match, "idle", 4) == 0) {       /* Printer is idle */
1160                        return (ChIdle);
1161                    }
1162                    else {
1163                        /* one of: busy, waiting, printing, initializing */
1164                        /* clear status message */
1165                        RestoreStatus();
1166                    }
1167                }
1168                /* WARNING: Must NOT match "PrinterError: timeout" */
1169                else if (match = FindPattern(cp, linebuf, " Error: timeout")) {
1170                    return (ChTimeout);
1171                }
1172                else {
1173                    /* message not for us */
1174                    fprintf(jobout, "%s", linebuf);
1175                    VOIDC fflush(jobout);
1176                    st = normal;
1177                    break;
1178                }
1179            }
1180            break;
1181        default:
1182            fprintf(stderr, "bad case;\n");
1183    }
1184    return (ChOk);
1185}
1186
1187/* backup "status" message file in ".status", in case there is a PrinterError */
1188private BackupStatus(file1, file2)
1189char *file1, *file2;
1190{
1191    register int fd1, fd2;
1192    char buf[BUFSIZ];
1193    int cnt;
1194
1195    VOIDC umask(0);
1196    fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
1197    if ((fd1 < 0) || (flock(fd1,LOCK_EX) < 0)) {
1198        VOIDC unlink(file1);
1199        VOIDC flock(fd1,LOCK_UN);
1200        VOIDC close(fd1);
1201        fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
1202    }
1203    if ((fd1 < 0) || (flock(fd1,LOCK_EX) <0)) {
1204        fprintf(stderr, "%s: writing %s; ",prog,file1);
1205        perror("");
1206        VOIDC close(fd1);
1207        return;
1208    }
1209    VOIDC ftruncate(fd1,0);
1210    if ((fd2 = open(file2, O_RDONLY,0)) < 0) {
1211        fprintf(stderr, "%s: error reading %s; ", prog, file2);
1212        perror("");
1213        VOIDC close(fd1);
1214        return;
1215    }
1216    cnt = read(fd2,buf,BUFSIZ);
1217    VOIDC write(fd1,buf,cnt);
1218    VOIDC flock(fd1,LOCK_UN);
1219    VOIDC close(fd1);
1220    VOIDC close(fd2);
1221}
1222
1223/* restore the "status" message from the backed-up ".status" copy */
1224private RestoreStatus() {
1225    BackupStatus("status",".status");
1226    newstatmsg = FALSE;         /* Say we went back to the old message */
1227}
1228
1229/* report PrinterError via "status" message file */
1230private Status(msg)
1231register char *msg;
1232{
1233    register int fd;
1234    char msgbuf[100];
1235
1236    if ((fd = open("status",O_WRONLY|O_CREAT,0664)) < 0) return;
1237    VOIDC ftruncate(fd,0);
1238    sprintf(msgbuf,"Printer Error: may need attention! (%s)\n\0",msg);
1239    VOIDC write(fd,msgbuf,strlen(msgbuf));
1240    VOIDC close(fd);
1241    newstatmsg = TRUE;          /* Say we changed the status message */
1242}
1243
1244/* Child has exited.  If there is a problem, this routine causes the program
1245 * to abort.  Otherwise, the routine just returns.
1246 */
1247private VOID listenexit(exitstatus)
1248union wait exitstatus;     /* Status returned by the child */
1249{
1250    debugp((stderr, "%s: Listener return status: 0x%x\n", prog, exitstatus));
1251    if (exitstatus.w_termsig != 0) {    /* Some signal got the child */
1252        fprintf(stderr, "%s: Error: Listener process killed using signal=%d\n",
1253          prog, exitstatus.w_termsig);
1254        VOIDC fflush(stderr);
1255        croak(TRYAGAIN);
1256    }
1257    else {
1258        switch (exitstatus.w_retcode) { /* Depends on child's exit status */
1259            case LIS_IDLE:      /* Printer went idle during job. Probably
1260                                   rebooted. */
1261                fprintf(stderr, "%s: ERROR: printer is idle. Giving up!\n", prog);
1262                VOIDC fflush(stderr);
1263                croak(THROW_AWAY);
1264            case LIS_TIMEOUT:   /* Printer timed out during job. System
1265                                   loaded? */
1266                fprintf(stderr, "%s: ERROR: printer timed out. Trying again.\n", prog);
1267                VOIDC fflush(stderr);
1268                croak(TRYAGAIN);
1269            case LIS_EOF:       /* Comm line down.  Somebody unplugged the
1270                                   printer? */
1271                fprintf(stderr,
1272                  "%s: unexpected EOF from printer (listening)!\n", prog);
1273                VOIDC fflush(stderr);
1274                sleep(10);
1275                croak(TRYAGAIN);
1276            case LIS_ERROR:     /* Listener died */
1277                fprintf(stderr,
1278                  "%s: unrecoverable error from printer (listening)!\n", prog);
1279                VOIDC fflush(stderr);
1280                sleep(30);
1281                croak(TRYAGAIN);
1282            case LIS_NORMAL:    /* Normal exit. Keep going */
1283            case LIS_DIE:       /* Parent said to die. */
1284            default:
1285                break;
1286        }
1287    }
1288}
1289
1290/* Reap the children.  This returns when all children are dead.
1291 * This routine ASSUMES we are dying.
1292 */
1293private VOID reapchildren() {
1294    intstate = croaking;    /* OK -- we are dying */
1295    VOIDC setjmp(croaklabel);    /* Get back here when we get an alarm */
1296    VOIDC unlink(".banner");         /* get rid of banner file */
1297    VOIDC alarm(CROAKALARM);
1298    if (cpid != 0) VOIDC kill(cpid,SIGEMT);  /* This kills listener */
1299    while (wait((union wait *) 0) > 0);
1300    VOIDC alarm(0);         /* No more alarms */
1301    if (VerboseLog) {
1302        VOIDC time(&starttime);
1303        fprintf(stderr,"%s: end - %s",prog,ctime(&starttime));
1304        VOIDC fflush(stderr);
1305    }
1306}
1307
1308/* Reap our children and die */
1309private VOID croak(exitcode)
1310int exitcode;
1311{
1312#ifdef QMS
1313    if (copts.cpid != getpid()) {
1314        kill_statproc();
1315    }
1316#else   
1317    VOIDC reapchildren();
1318#endif
1319    RestoreStatus();
1320    exit(exitcode);
1321}
1322
1323/* Exit and printer system error message with perror() */
1324private VOID myexit1(progname,exitcode)
1325char  *progname;
1326int    exitcode;
1327{
1328    saveerror = errno;
1329    VOIDC reapchildren();
1330    RestoreStatus();
1331    errno = saveerror;
1332    pexit(progname,exitcode);
1333}
1334
1335/* Exit and print system error message with perror() and printer a reason */
1336private VOID myexit2(progname,reason,exitcode)
1337char  *progname;
1338char  *reason;
1339int    exitcode;
1340{
1341    saveerror = errno;
1342    VOIDC reapchildren();
1343    RestoreStatus();
1344    errno = saveerror;
1345    pexit2(progname, reason, exitcode);
1346}
1347
1348/* Close down without having done anything much */
1349private VOID closedown() {
1350    fprintf(stderr,"%s: abort (during startup)\n",prog);
1351    VOIDC fflush(stderr);
1352    croak(THROW_AWAY);
1353}
1354
1355/* On receipt of a job abort, we normally get the printer to abort the job
1356 * and still do the normal final banner page and accounting entry.  This
1357 * requires the printer to respond correctly.  If the printer is busted, it
1358 * will not respond.  In order to prevent this process from sitting around
1359 * until a working printer is connected to the printer port, this routine
1360 * gets called went it is deemed that the printer will not respond...
1361 */
1362private VOID dieanyway() {
1363    fprintf(stderr,"%s: No response from printer after abort.  Giving up!\n",
1364        prog);
1365    croak(THROW_AWAY);
1366}
1367
1368/* Abort the current job running on the printer */
1369private VOID abortjob() {
1370    if (jobaborted) return;             /* Don't repeat work */
1371    alarm(ABORTALARM);               /* Limit time for printer to respond */
1372    jobaborted = TRUE;
1373
1374    if (resetprt() || write(fdsend,abortbuf,1) != 1) {
1375        fprintf(stderr, "%s: ioctl error (abort job); ", prog);
1376        perror("");
1377    }
1378}
1379
1380/* Got an EMT signal */
1381private VOID GotEmtSig() {
1382
1383    debugp((stderr,"%s: Got SIGEMT signal, instate is %d\n",prog,(int) intstate));
1384
1385    /* This signal does not need to obey the critical region rules */
1386
1387    switch ((int)intstate) {
1388        case sending:
1389        case waiting:
1390            while ((wpid=wait(&status)) > 0) {
1391                if (wpid==cpid) {
1392                    cpid = 0;
1393                    listenexit(status);
1394                    break;
1395                }
1396            }
1397            if (intstate == sending) {
1398                fprintf(stderr,"WARNING: Check spooled PostScript for control characters.\n");
1399                fflush(stderr);
1400            }
1401            childdone = TRUE;    /* Child exited somehow */
1402            break;
1403        case child:
1404            VOIDC kill(getppid(),SIGEMT);   /* Tell parent we are exiting */
1405            exit(LIS_DIE);    /* Parent says die -- we exit early */
1406        default:
1407            break;              /* Ignore it */
1408    }
1409}
1410
1411/* Got an alarm signal. */
1412private VOID GotAlarmSig()
1413{
1414    char mybuf[BUFSIZ];
1415
1416    debugp((stderr,"%s: Got alarm signal %d %d %d %d\n",
1417        prog,intstate,oldprogress,progress,getstatus));
1418    if (flagsig) {
1419        gotsig |= ALARM_INT;  /* We just say we got one and return */
1420        return;
1421    }
1422    switch ((int)intstate) {
1423        case syncstart:
1424        case synclast:
1425            if (jobaborted) dieanyway(); /* If already aborted, just croak */
1426            sprintf(mybuf, "Not Responding for %ld minutes",
1427                (time((long*)0)-starttime+30)/60);
1428            Status(mybuf);
1429            longjmp(synclabel,1);
1430        case sending:
1431            if (progress == oldprogress) { /* Nothing written since last time */
1432                getstatus = TRUE;
1433            }
1434            else {
1435                oldprogress = progress;
1436                getstatus = FALSE;
1437            }
1438            VOIDC alarm(SENDALARM); /* reset the alarm and return */
1439            break;
1440        case waiting:
1441            if (jobaborted) dieanyway();   /* If already aborted, just croak */
1442            VOIDC write(fdsend, statusbuf, 1);
1443            if (kill(cpid,0) < 0) childdone = TRUE;  /* Missed exit somehow */
1444            VOIDC alarm(WAITALARM); /* reset the alarm and return */
1445            break;
1446        case lastpart:
1447            if (jobaborted) dieanyway();   /* If already aborted, just croak */
1448            break;
1449        case croaking:
1450            longjmp(croaklabel,1);
1451        case child:
1452            if (kill(getppid(),0) < 0) {  /* Missed death signal from parent */
1453                fprintf(stderr,
1454                    "%s: Error: Parent exited without signalling child\n",prog);
1455                VOIDC fflush(stderr);
1456                exit(LIS_NOPARENT);
1457            }
1458            alarm(CHILDWAIT);
1459            break;
1460        default:
1461            break; /* Ignore it */
1462        }
1463}
1464
1465/* Got a "die" signal, like SIGINT */
1466private VOID GotDieSig(sig)
1467    int sig;
1468{
1469    debugp((stderr,"%s: Got 'die' signal=%d\n",prog,sig));
1470    if (flagsig) {
1471        gotsig |= DIE_INT;  /* We just say we got one and return */
1472        return;
1473    }
1474    switch ((int)intstate) {
1475        case init:
1476            VOIDC closedown();
1477        case syncstart:
1478            fprintf(stderr,"%s: abort (start communications)\n",prog);
1479            VOIDC fflush(stderr);
1480            abortjob();
1481            VOIDC write(fdsend, eofbuf, 1);
1482            croak(THROW_AWAY);
1483        case sending:
1484            fprintf(stderr,"%s: abort (sending job)\n",prog);
1485            VOIDC fflush(stderr);
1486            abortjob();
1487            longjmp(sendlabel,1);
1488        case waiting:
1489            if (!jobaborted) {
1490                fprintf(stderr,"%s: abort (waiting for job end)\n",prog);
1491                VOIDC fflush(stderr);
1492            }
1493            abortjob();
1494            break;
1495        case lastpart:
1496        case synclast:
1497            if (jobaborted) dieanyway();   /* If already aborted, just croak */
1498            fprintf(stderr,"%s: abort (post-job processing)\n",prog);
1499            VOIDC fflush(stderr);
1500            alarm(ABORTALARM);
1501            jobaborted = TRUE;
1502            break;
1503        case child:
1504            alarm(CHILDWAIT);   /* Wait a while, then see if parent is alive */
1505            break;
1506        default:
1507            break; /* Ignore it */
1508        }
1509}
1510
1511/* Open the printer for listening.
1512 * This fcloses the old file as well, so it may be used to throw away
1513 * any buffered input.
1514 * NOTE: The printcap entry specifies "rw" and we get invoked with
1515 * stdout == the device, so we dup stdout, and reopen it for reading;
1516 * this seems to work fine...
1517 */
1518private VOID openprtread()
1519{
1520#ifdef BRIDGE
1521    if (psin == NULL && (psin = fdopen(fdsend, "r")) == NULL) {
1522        myexit1(prog, THROW_AWAY);
1523    }
1524#else
1525    if (psin != NULL)
1526        VOIDC fclose(psin);     /* Close old one */
1527    if ((fdlisten = dup(fdsend)) < 0)   /* the printer (read) */
1528        myexit1(prog, THROW_AWAY);
1529    if ((psin = fdopen(fdlisten, "r")) == NULL) {
1530        myexit1(prog, THROW_AWAY);
1531    }
1532#endif /* BRIDGE */
1533}
1534
1535/* Flush the I/O queues for the printer, and restart output to the
1536  printer if it has been XOFF'ed.
1537  Returns correct error stuff for perror() if a system call fails.
1538  NOTE: If this routine does nothing, one can get into a state where
1539  syncprinter() loops forever reading OLD responses to the acct message. */
1540
1541private int resetprt()
1542{
1543#ifdef BRIDGE
1544    static struct timeval t = {0, 0};   /* Zero time */
1545    int rmask;                  /* Hope fdsend bit fits in here... */
1546    int c;
1547
1548    rmask = 1 << fdsend;
1549    debugp((stderr, "%s: Start reset\n", prog));
1550    while (select(sizeof(int) * 8, &rmask, (int *) 0, (int *) 0, &t) == 1) {
1551        debugp((stderr, "%s: Read another buffer cnt=%d rmask=0x%x\n", prog, psin->_cnt, rmask));
1552        while (psin->_cnt > 0)
1553            c = getc(psin);     /* Empty the buffer */
1554        c = getc(psin);         /* Fill the buffer */
1555        if (c == EOF) {
1556            if (feof(psin)) {
1557                flagsig = 1;
1558                fprintf(stderr, "%s: unexpected EOF from printer (resetprinter)!\n",
1559                  prog);
1560                VOIDC fflush(stderr);
1561                sleep(10);
1562                croak(TRYAGAIN);
1563            }
1564            else {
1565                if (errno != EINTR) {   /* Got a random error */
1566                    fprintf(stderr, "%s: Error; ", prog);
1567                    perror("");
1568                    fflush(stderr);
1569                    sleep(30);
1570                    croak(TRYAGAIN);
1571                }
1572            }
1573            clearerr(psin);     /* Make it so we can read again */
1574            continue;
1575        }
1576    }
1577    debugp((stderr, "%s: Read last buffer cnt=%d rmask=0x%x\n", prog, psin->_cnt, rmask));
1578    while (psin->_cnt > 0)
1579        c = getc(psin);         /* Empty the buffer */
1580#else
1581#ifndef QMS
1582    int flg = FREAD | FWRITE;   /* ioctl FLUSH arg */
1583
1584    VOIDC openprtread();        /* Re-open the printer */
1585    if (ioctl(fdsend, TIOCFLUSH, &flg) || ioctl(fdsend, TIOCSTART, &flg))
1586        return (-1);
1587#endif /* QMS */
1588#endif /* BRIDGE */
1589
1590    return (0);
1591}
1592
1593/* Synchronize the input and output of the printer.   We use the
1594   accounting message, and include our process ID and
1595   a sequence number.  If the output doesn't match what we expect, we
1596   sleep a bit, flush the terminal buffers, and try again.
1597   WARNING: Make sure there are no pending alarms before calling this
1598   routine. */
1599
1600private VOID syncprinter(pagecount)
1601    long *pagecount;            /* The current page count in the printer */
1602{
1603    static int synccount = 0;   /* Unique number for acct output.  This is
1604                                   static so ALL calls will produce
1605                                   different output */
1606    unsigned int sleeptime;     /* Number of seconds to sleep */
1607    char *mp;                   /* Current pointer into mybuf */
1608    int gotpid;                 /* The process ID returned from the
1609                                   printer */
1610    int gotnum;                 /* The synccount returned from the printer */
1611    register int r;             /* Place to put the chars we get */
1612    int sc;                     /* Results from sscanf() */
1613    int errcnt;                 /* Number of errors we have output */
1614    char mybuf[BUFSIZ];
1615
1616    VOIDC time(&starttime);     /* Get current time for status warnings */
1617    errcnt = 0;
1618    sleeptime = 2;              /* Initial sleep interval */
1619    jobout = stderr;            /* Write extra stuff to the log file */
1620    NextChInit();
1621    openprtread();              /* Open the printer for reading */
1622    while (TRUE) {
1623        synccount++;
1624        if (setjmp(synclabel))
1625            goto tryagain;      /* Got an alarm */
1626        VOIDC alarm(SYNCALARM); /* schedule an alarm/timeout */
1627        VOIDC sprintf(mybuf, getpages, mpid, synccount, "\004");
1628        VOIDC write(fdsend, mybuf, strlen(mybuf));      /* Send program */
1629        mp = mybuf;
1630        *mp = '\0';
1631        while (TRUE) {          /* Listen for return string */
1632            r = getc(psin);
1633            if (r == EOF) {
1634                if (feof(psin)) {
1635                    flagsig = 1;
1636                    fprintf(stderr,
1637                      "%s: unexpected EOF from printer (sync)!\n", prog);
1638                    VOIDC fflush(stderr);
1639                    sleep(10);
1640                    croak(TRYAGAIN);
1641                }
1642                else {
1643                    if (errno != EINTR) {       /* Got a random error */
1644                        fprintf(stderr, "%s: Error; ", prog);
1645                        perror("");
1646                        fflush(stderr);
1647                        sleep(10);
1648                        croak(TRYAGAIN);
1649                    }
1650                }
1651                clearerr(psin); /* Make it so we can read again */
1652                continue;
1653            }
1654            if ((r & 0377) == 004)
1655                break;          /* Echoed back end of job */
1656            VOIDC NextCh(r);    /* Ignore return values */
1657            if (mp >= mybuf + sizeof(mybuf) - 1) {      /* Overflow? */
1658                mp = '\0';
1659                strcpy(mybuf, mybuf + sizeof(mybuf) / 2);
1660                mp = mybuf + sizeof(mybuf) / 2;
1661            }
1662            *mp++ = r;
1663        }
1664        *mp = '\0';
1665        debugp((stderr, "%s: sync reply (%s)\n", prog, mybuf));
1666        if (mp = FindPattern(mp, mybuf, "%%[ pagecount: ")) {
1667            sc = sscanf(mp, "%%%%[ pagecount: %ld, %d %d ]%%%%\r",
1668              pagecount, &gotpid, &gotnum);
1669        }
1670        if (mp != NULL && sc == 3 && gotpid == mpid && gotnum == synccount)
1671            break;
1672        errcnt++;
1673        if (errcnt <= 3 || errcnt % 10 == 0) {  /* Only give a few errors */
1674            fprintf(stderr, "%s: printer sync problem [%d] (%s)\n",
1675              prog, synccount, mybuf);
1676            VOIDC fflush(stderr);
1677        }
1678        VOIDC alarm(0);         /* Don't want alarms anymore */
1679        sleep(sleeptime);       /* Sleep for a while */
1680        sleeptime *= 2;         /* Wait for longer next time */
1681        if (sleeptime > MAXSLEEP) {
1682            if (jobaborted)
1683                dieanyway();    /* If already aborted, just croak */
1684            sleeptime = MAXSLEEP;
1685        }
1686tryagain:
1687        if (resetprt()) {       /* Reset the printer I/O queues */
1688            fprintf(stderr, "%s: ioctl error (sync); ", prog);
1689            perror("");
1690        }
1691    }
1692    VOIDC alarm(0);             /* Make sure we don't longjmp() after
1693                                   exiting */
1694}
1695
1696/* Make an entry in the accounting file */
1697private VOID acctentry(start,end)
1698long start,end;         /* Starting and ending page counts for job */
1699{
1700    debugp((stderr, "%s: Make acct entry s=%ld, e=%ld\n", prog, start, end));
1701    if (start > end || start < 0 || end < 0) {
1702        fprintf(stderr, "%s: accounting error 3, %ld %ld\n", prog, start, end);
1703        fflush(stderr);
1704    }
1705    else if (freopen(accountingfile, "a", stdout) != NULL) {
1706        printf("%7.2f\t%s:%s\n", (float) (end - start), host, name);
1707        VOIDC fclose(stdout);
1708    }
1709}
1710
1711#ifdef QMS
1712
1713/*
1714 * Get printer status. This is an infinite loop; if no replies
1715 * are being received from the printer, the procedure keeps on sending
1716 * requests in the hope that a reply will be received. It would be trivial,
1717 * however, to add logic to terminate the procedure if no replies are
1718 * received after a long timeout, say 5 minutes.
1719 * Use of select() enables low level timeout mechanism to be enforced and so,
1720 * in theory, a network read, i.e. recvfrom(), should never block.
1721 * A reply packet from the printer indicates if the printer is busy or not.
1722 * If it is, the procedure, after RETRY counts, sleeps for SLEEPTIME before
1723 * sending the request packet again. The priming select() outside the
1724 * inner while loop (RETRY loop), checks to see if any old packets have
1725 * finally found their destination and discards them.
1726 */
1727get_printstat(ufd, statflag, statbuf)
1728    int ufd;
1729    int statflag;
1730    char *statbuf;
1731{
1732    int     nfound, rcnt, rflags, pstatus, recvlen, age;
1733    char   *strptr;
1734    char    preply[256];
1735    struct sockaddr_in  recvaddr;
1736    struct printer_pkt  preq, *ps;
1737    char    reply[MAX_REPLY_SIZE];
1738    fd_set readfds;
1739    struct timeval  timeout;
1740
1741    /*
1742     * Set the time out value.
1743     */
1744    timeout.tv_sec = 0;
1745    timeout.tv_usec = UDP_TIMEOUT * 10000;
1746
1747
1748    /* Flush out past reply packets, if any */
1749    FD_ZERO(&readfds);
1750    FD_SET(ufd, &readfds);
1751
1752    while (TRUE) {
1753        nfound = select(ufd + 1, &readfds, (fd_set *) 0,
1754                        (fd_set *) 0, &timeout);
1755        if ((nfound < 0) || (nfound > 1)) {
1756            fprintf(stderr, "select failed\n");
1757            perror("");
1758            croak(TRYAGAIN);
1759        }
1760        else
1761            if (nfound == 1) {
1762                debugp((stderr, "Found an old reply packet\n"));
1763                FD_ZERO(&readfds);
1764                FD_SET(ufd, &readfds);
1765                if (recvfrom (ufd, &preply[0], sizeof (preply), 0,
1766                            (struct sockaddr   *) & recvaddr, &recvlen) < 0) {
1767                    fprintf(stderr, "Recvfrom:Failed to receive udp packet\n");
1768                    perror("");
1769                    croak(TRYAGAIN);
1770
1771                }
1772            }
1773            else {
1774                debugp((stderr, "Timed out: No[more] past reply packets\n"));
1775                break;
1776            }
1777    }
1778
1779    pstatus = 0;
1780    rcnt = NUMRETRY;
1781    while (rcnt--) {
1782        bzero ((char *) & preq, sizeof (preq));
1783        if (sendto (ufd, (char *) & preq, sizeof (preq), 0, &printaddr,
1784                    sizeof (printaddr)) != sizeof (preq)) {
1785            fprintf(stderr, "Sendto:Failed to send udp data \n");
1786            perror("");
1787            croak(TRYAGAIN);
1788        }
1789
1790        /*
1791         * Issue a select and see if you get a packet within the timeout interval.
1792         */
1793
1794        FD_ZERO(&readfds);
1795        FD_SET(ufd, &readfds);
1796        nfound = select (ufd + 1, &readfds, (fd_set *) 0, (fd_set *) 0,
1797                         &timeout);
1798        if ((nfound < 0) || (nfound > 1)) {
1799            fprintf(stderr, "Select failed\n");
1800            perror("");
1801            croak(TRYAGAIN);
1802        }
1803        else
1804            if (nfound == 0) {
1805                debugp((stderr, "Timed out: Reply packet not received from printer\n"));
1806                continue;
1807            }
1808            else {
1809                debugp((stderr, "Reply packet received from printer\n"));
1810            }
1811
1812        /*
1813         * Read in the reply packet.
1814         */
1815        recvlen = sizeof (recvaddr);
1816        if (recvfrom (ufd, &preply[0], sizeof (preply), 0,
1817                    (struct sockaddr   *) & recvaddr, &recvlen) < 0) {
1818            fprintf(stderr, "Recvfrom:Failed to receive udp packet\n");
1819            perror("");
1820            croak(TRYAGAIN);
1821        }
1822
1823
1824        ps = (struct printer_pkt   *) preply;
1825
1826        debugp((stderr, "Printer Status data:\n\t pr_status:0x%x\n\t pr_acceptmask:0x%x\n",
1827                    ps->pr_status, ntohl (ps->pr_acceptmask)));
1828
1829        pstatus = (!(ps->pr_status & 0x0f)) & (ntohl (ps->pr_acceptmask) & 0x01);
1830        debugp((stderr, "print_status=%x\n", pstatus));
1831
1832
1833#ifdef BDEBUG
1834        if (ps->pr_status & 0x02)
1835            debugp((stderr, "Printer lacks paper\n"));
1836
1837        if (ps->pr_status & 0x04)
1838            debugp((stderr, "Printer has paper jam\n"));
1839
1840        if (ps->pr_status & 0x08)
1841            debugp((stderr, "Printer lacks consumables (other than paper)\n"));
1842
1843        if (ps->pr_status & 0x10)
1844            debugp((stderr, "Job in progress at the printer\n"));
1845
1846        if (pstatus != 0) {
1847            debugp((stderr, "...printer ready to accept data\n"));
1848        }
1849        else {
1850            debugp((stderr, "....printer busy or has problems\n"));
1851        }
1852#endif
1853
1854        age = ntohl (ps->pr_age);
1855
1856        strptr = (&preply[0] + ntohs (ps->pr_stroffset));
1857        /* Make sure string is null terminated */
1858        *(strptr + ntohs (ps->pr_strlength)) = 0x00;
1859
1860        if (statflag) {
1861            sprintf (statbuf, "%s (for %02d:%02d:%02d)",
1862                    strptr, age / 3600, (age / 60) % 60, age % 60);
1863        }
1864
1865        return (pstatus);
1866
1867    }/* end rcnt while */
1868
1869    debugp((stderr, "....printer not responding \n"));
1870
1871    if (statflag) {
1872        sprintf (statbuf, "Printer not responding to status request\n");
1873    }
1874    return (0);
1875}
1876
1877xfer_data(tpfd, copts, rfd)
1878    int tpfd;
1879    struct command_opt *copts;
1880    int rfd;
1881{
1882    int     nw, nr, w, tobewritten;
1883    char    buf[BUFSIZE], *bufp;
1884
1885    Status ("Sending file");
1886
1887    while ((nr = read (rfd, buf, BUFSIZE)) != 0) {
1888        if (nr < 0) {
1889            fprintf(stderr, "read of input file failed\n");
1890            perror("");
1891            return -1;
1892        }
1893        debugp((stderr, "Numbytes Read from the Input file:: %d\n", nr));
1894
1895        nw = 0;
1896        bufp = buf;
1897        tobewritten = nr;
1898
1899        while (nw != nr) {
1900            if ((w = send (tpfd, bufp, tobewritten, 0)) < 0) {
1901                fprintf(stderr,"send across tcp socket failed\n");
1902                perror("");
1903                return -1;
1904            }
1905            nw += w;
1906            bufp += w;
1907            tobewritten -= w;
1908            debugp ((stderr,
1909                        "Numbytes sent to the printer:: %d\n", nw));
1910        }
1911    }
1912}
1913
1914setup_udp()
1915{
1916    int     ufd;
1917    struct sockaddr_in  claddr;
1918
1919    if ((ufd = socket (PF_INET, SOCK_DGRAM, 0)) < 0) {
1920        fprintf(stderr, "Failed to open UDP socket\n");
1921        perror("");
1922        croak (TRYAGAIN);
1923    }
1924
1925    /*
1926     * Bind any local address for us.
1927     */
1928
1929    bzero ((char *) & claddr, sizeof (claddr));
1930    claddr.sin_family = AF_INET;
1931    claddr.sin_port = htons (0);
1932    claddr.sin_addr.s_addr = htonl (INADDR_ANY);
1933
1934
1935    if (bind (ufd, (struct sockaddr_in *) & claddr, sizeof (claddr)) < 0) {
1936        fprintf(stderr, "UDP bind failed\n");
1937        perror("");
1938        croak (TRYAGAIN);
1939    }
1940
1941    return (ufd);
1942}
1943       
1944
1945
1946init_data(copts)
1947    struct command_opt *copts;
1948{
1949    copts->input_stdin = 1;
1950}
1951
1952/*
1953 * Catch the kill signal for the child and exit gracefully.
1954 */
1955
1956void croak_udp()
1957{
1958    exit(0);
1959}
1960
1961/*
1962 * Fork a child process to gather printer status while a job is active.
1963 */
1964
1965spawn_statproc()
1966{
1967    int     pid, ufd;
1968
1969    if ((pid = fork ()) < 0) {
1970        fprintf(stderr, "Failed to spawn status gathering process\n");
1971        perror("");
1972        exit (1);
1973    }
1974    if (pid == 0) {/* child */
1975        /*
1976         * Set up for being killed by parent.
1977         */
1978        signal(SIGTERM, croak_udp);
1979        /*
1980         * Set up UDP socket.
1981         */
1982        if ((ufd = setup_udp ()) < 0) {
1983            printf ("Failed to open UDP connection for child (status) process\n");
1984            exit (1);
1985        }
1986        update_status (ufd);
1987    }
1988    else {
1989        return (pid);
1990    }
1991}
1992
1993/*
1994 * Terminate the child  --- status gathering process.
1995 */
1996kill_statproc()
1997{
1998    if (kill(copts.cpid, SIGTERM) < 0) {
1999        if (errno != ESRCH) {
2000            fprintf(stderr, "Failed to terminate the child (status gathering) process\n");
2001            perror("");
2002            exit (1);
2003        }
2004    }
2005    while (wait (0) != copts.cpid);
2006}
2007
2008update_status(ufd)
2009    int ufd;
2010{
2011    int     i;
2012    char    statstring[650];
2013    char    statbuf[512];
2014
2015    sleep (STATUSINTERVAL);
2016
2017    i = 100;
2018    while (TRUE) {
2019        get_printstat (ufd, 1, statbuf);
2020        sprintf (statstring, "Job in progress--- Status: %s", statbuf);
2021        Status (statstring);
2022        sleep (STATUSINTERVAL);
2023    }
2024}
2025
2026
2027
2028#endif /* QMS */
2029
2030
2031
2032
Note: See TracBrowser for help on using the repository browser.