home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume8 / servercomm2 / servercomm.c < prev   
Encoding:
C/C++ Source or Header  |  1989-08-23  |  16.0 KB  |  734 lines

  1. /*
  2. **    S E R V E R C O M M
  3. **
  4. **    Title:            servercomm (formerly annexf)
  5. **    Original Author:    John Sloan (internet: jsloan@SPOTS.Wright.Edu)
  6. **    System:            SunOS 
  7. **    Language:        C
  8. **    Date:            April 1988
  9. **    Abstract
  10. **
  11. **    Servercomm is a filter for the Berkeley spooler daemon that
  12. **    establishes a bidirectional TCP/IP/TELNET connection to
  13. **    a serial port on an terminal server made by popular makers
  14. **    so that printouts can be spooled to a serial printer connected
  15. **    to the server. It is intended to be used with an Apple
  16. **    Laserwriter and the Adobe Transcript package, but could
  17. **    be used for other things as well. It can also be configured
  18. **    at run time to talk to a /dev device instead of the terminal server
  19. ++
  20. ++    Modified by Jim Warner to work with the service listener
  21. ++    port on Bridge Communications terminal servers.  The dialog
  22. ++    in handshake() is unnecessary.  The "port" number was the physical
  23. ++    connector on the back of the annex.  For Bridge (and Cisco, I bet)
  24. ++    this is the tcp-port number to use instead of telnet=23.  For
  25. ++    Bridge, we're going to need an IP address per printer.
  26. ++                        Jan 2 1989
  27. ==
  28. ==    Modified by Dinah Anderson <dinah@bcm.tmc.edu> to use the 
  29. ==    sys/termios.h data structure instead of the sys/ioctl.h.
  30. ==    Also set the ptys characteristics explicitly since most postscript
  31. ==    files would not print. We tested this using a Bridge server.
  32. ==    8/23/89.
  33. **
  34. **    Disclaimer
  35. **
  36. **    This software is supplied as is, and no warranty is expressed or
  37. **    implied as to its correctness, functionality, or suitability
  38. **    for any application, anywhere, at anytime, by anyone.
  39. **
  40. **    Acknowledgements
  41. **
  42. **    Much of this code was inspired by telnet.c from UCB and aprint.c
  43. **    from Encore Computer. No code borrowed, but a lot of questions
  44. **    were answered by perusing these examples. Also, many thanks to
  45. **    Ken Yap <ken@cs.rochester.edu>, who suggested using a pty/tty
  46. **    pair to communicate with pscomm. I was working on that when I
  47. **    received his email, and it was encouraging to hear someone else
  48. **    that thought it should work.
  49. **
  50. **    Compile Time Flags
  51. **
  52. **    DEBUG        Compiles in debugging code to make
  53. **            useful remarks on stderr. Use the
  54. **            -v run time flag to activate.
  55. **
  56. **    DUMP        Compiles in code to dump data to stderr
  57. **            as it's being processed. Very verbose,
  58. **            but useful in debugging. Use the -x run
  59. **            time flag to activate.
  60. **
  61. **    BRIDGE        Compiles in code to support CISCO or BRIDGE
  62. **            communications servers.
  63. **
  64. */
  65. /*
  66. **    To do:
  67. **        The pseudottys should not have to be specified. The program
  68. **        should find the first available ones and use them.
  69. **
  70. */
  71. #include <stdio.h>
  72. #include <sys/types.h>
  73. #include <sys/file.h>
  74. #include <sys/termios.h>
  75. #include <ctype.h>
  76. #include <errno.h>
  77. #include <fcntl.h>
  78. #include <setjmp.h>
  79. #include <signal.h>
  80. #include <netdb.h>
  81. #include <netinet/in.h>
  82. #define    TELOPTS
  83. #include <arpa/telnet.h>
  84. #include <arpa/inet.h>
  85. #include <sys/socket.h>
  86. #include <sys/wait.h>
  87. #include <strings.h>
  88.  
  89. #define LPD_OK 0        /* lpd filter exit codes */
  90. #define LPD_RETRY 1
  91. #define LPD_ABORT 2
  92. #define BUFFER 512        /* spooler buffer size in bytes */
  93. #define CR '\015'
  94. #define PROMPT ':'
  95.  
  96. #ifdef DEBUG
  97. #define DPRINTF(s)    if (debugon) fprintf s
  98. #else
  99. #define DPRINTF(s)
  100. #endif
  101.  
  102. /*
  103. **    Identification
  104. */
  105. static char id[]="$Header: /home/crick/vpit/sob/src/servercomm/RCS/servercomm.c,v 1.1 89/08/06 00:10:36 sob Exp Locker: sob $";
  106.  
  107. /*
  108. **    Must be static (used by network code).
  109. */
  110. struct servent *srvc;
  111. struct hostent *host;
  112. struct sockaddr_in sock;
  113. struct sockaddr dev;
  114. struct termios ttycb;
  115. jmp_buf context;
  116.  
  117. /*
  118. **    Must be global (used by interrupt service routines).
  119. */
  120. #ifndef errno
  121. extern int errno;    /* warner: not needed for Sun OS */
  122. #endif
  123. char *pgmname;
  124. int filterpid,prodpid,conspid;
  125. int netfd,ptyfd,ttyfd;
  126. int debugon,dumpon;
  127.  
  128. void setup(),cleanup(),handshake(),trigger(),rendezvous();
  129. void spool(),transmit(),usage(),bailout(),fatal();
  130. void lost(),interrupted(),finished();
  131. #ifdef DEBUG
  132. void pargs();
  133. #endif
  134.  
  135. main(argc,argv)
  136. int argc;
  137. char *argv[];
  138.     {
  139.     int nargc,rc;
  140.     char *host,*port,*device,*filter,*pty,*tty,**nargv;
  141.  
  142.     pgmname=rindex(argv[0],'/');
  143.     pgmname=pgmname?pgmname+1:argv[0];
  144.     setup(argc,argv,&debugon,&dumpon,&host,&port,&device,&pty,&tty,&filter,&nargc,&nargv);
  145.     filterpid=0;
  146.     prodpid=0;
  147.     conspid=0;
  148.     if (pty!=NULL)
  149.         {
  150.         ptyfd=acquire(pty);
  151.         ttyfd=acquire(tty);
  152.         if ((rc=ioctl(ttyfd,TCGETS,&ttycb))<0)
  153.             fatal(rc,"main ioctl getp failed",LPD_ABORT);
  154.         ttycb.c_cflag = (B9600 | CS7 | CREAD );
  155.         ttycb.c_oflag = (OPOST | OCRNL );
  156.         ttycb.c_iflag = (ISTRIP );
  157.         ttycb.c_lflag = (0 );
  158.         if ((rc=ioctl(ttyfd,TCSETS,&ttycb))<0)
  159.             fatal(rc,"main ioctl setp failed",LPD_ABORT);
  160.         }
  161.     else
  162.         ptyfd=0;
  163.     if (host!=NULL)
  164.         netfd=establish(host,port);
  165.     else if (device!=NULL)
  166.         netfd=acquire(device);
  167.     else
  168.         usage(0);
  169.     if (rc=setjmp(context))
  170.         bailout(rc);
  171.     if (port!=NULL)
  172.         handshake(netfd,port);
  173.     if (filter!=NULL)
  174.         {
  175.         /*
  176.         ** Fork off producer and consumer processes
  177.         ** ahead of the filter so that they are ready
  178.         ** to handle the filter's I/O immediately. This
  179.         ** scheme is process intensive (makes several
  180.         ** processes) but is simpler than using non-blocking
  181.         ** I/O, which was the original implementation.
  182.         */
  183.         spool(netfd,ptyfd,&prodpid,&conspid);
  184.         filterpid=attach(filter,nargc,nargv,ttyfd);
  185.         /*
  186.         ** Don't let children inherit our interrupt
  187.         ** service routines. This naively assumes that
  188.         ** nothing interesting will happen prior to now.
  189.         */
  190.         signal(SIGPIPE,lost);
  191.         signal(SIGINT,interrupted);
  192.         /*
  193.         ** Wait for something interesting to happen.
  194.         */
  195.         rendezvous(filterpid,prodpid,conspid);
  196.         DPRINTF((stderr,"%s: main complete\n",pgmname));
  197.         exit(LPD_OK);
  198.         }
  199.     }
  200.  
  201. /*
  202. **    Setup: parses argument list.
  203. */
  204. void
  205. setup(argc,argv,dbgon,dmpon,host,port,device,pty,tty,filter,nargc,nargv)
  206. int argc,*dbgon,*dmpon,*nargc;
  207. char *argv[],**host,**port,**device,**pty,**tty,**filter,***nargv;
  208.     {
  209.  
  210.     *dbgon=0;
  211.     *dmpon=0;
  212.     *host=NULL;
  213.     *port=NULL;
  214.     *device=NULL;
  215.     *pty=NULL;
  216.     *tty=NULL;
  217.     *filter=NULL;
  218.     *nargc=0;
  219.     *nargv=NULL;
  220.     while (argc>0)
  221.         {
  222.         if (argv[0][0]=='-')
  223.             {
  224.             switch (argv[0][1])
  225.                 {
  226.             case 'v':
  227.                 *dbgon=1;
  228.                 DPRINTF((stderr,"%s: debug on\n",pgmname));
  229.                 break;
  230.             case 'x':
  231.                 *dmpon=1;
  232.                 DPRINTF((stderr,"%s: dump on\n",pgmname));
  233.                 break;
  234.             case 'h':
  235.                 usage(--argc);
  236.                 *host=(*++argv);
  237.                 DPRINTF((stderr,"%s: setup host=%s\n",pgmname,*host));
  238.                 break;
  239.             case 'p':
  240.                 usage(--argc);
  241.                 *port=(*++argv);
  242.                 DPRINTF((stderr,"%s: setup port=%s\n",pgmname,*port));
  243.                 break;
  244.             case 'd':
  245.                 usage(--argc);
  246.                 *device=(*++argv);
  247.                 DPRINTF((stderr,"%s: setup device=%s\n",pgmname,*device));
  248.                 break;
  249.             case 't':
  250.                 usage(--argc);
  251.                 *pty=(*++argv);
  252.                 usage(--argc);
  253.                 *tty=(*++argv);
  254.                 DPRINTF((stderr,"%s: setup pty=%s, tty=%s\n",pgmname,*pty,*tty));
  255.                 break;
  256.             case 'f':
  257.                 usage(--argc);
  258.                 *filter=(*++argv);
  259.                 *nargv=argv;
  260.                 *nargc=argc;
  261.                 argc=0;
  262.                 DPRINTF((stderr,"%s: setup filter=%s, argc=%d\n",pgmname,*filter,*nargc));
  263.                 break;
  264.             default:
  265.                 usage(0);
  266.                 }
  267.             }
  268.         argv++;
  269.         argc--;
  270.         }
  271.     }
  272.  
  273. /*
  274. **    Cleanup: Shutdown child processes, close files, do anything else
  275. **    necessary to gracefully complete in the event of an error.
  276. */
  277. void
  278. cleanup()
  279.     {
  280.     /*
  281.     ** Filter may not have been started up yet.
  282.     */
  283.     if (filterpid)
  284.         {
  285.         kill(filterpid,SIGTERM);
  286.         DPRINTF((stderr,"%s: cleanup killed filter %d\n",pgmname,filterpid));
  287.         }
  288.     /*
  289.     ** Producer may not have been started up yet.
  290.     */
  291.     if (prodpid)
  292.         {
  293.         kill(prodpid,SIGTERM);
  294.         DPRINTF((stderr,"%s: cleanup killed spooler %d\n",pgmname,prodpid));
  295.         }
  296.     /*
  297.     ** Consumer may not have been started up yet.
  298.     */
  299.     if (conspid)
  300.         {
  301.         kill(conspid,SIGTERM);
  302.         DPRINTF((stderr,"%s: cleanup killed spooler %d\n",pgmname,conspid));
  303.         }
  304.     /*
  305.     ** These may be already closed, in which case close returns an
  306.     ** error we can safely ignore.
  307.     */
  308.     close(ptyfd);
  309.     close(ttyfd);
  310.     close(netfd);
  311.     }
  312.  
  313. /*
  314. **    Establish: Given a host name or an IP address, establish a real
  315. **    time TCP/IP/TELNET connection to the specified Annex.
  316. */
  317. int
  318. establish(hostname,port)
  319. char *hostname, *port;
  320.     {
  321.     char *name;
  322.     int rc,netfd;
  323.  
  324.     if ((srvc=getservbyname("telnet","tcp"))==0)
  325.         fatal(srvc,"establish getservbyname failed",LPD_ABORT);
  326.     if (host=gethostbyname(hostname))
  327.         {
  328.         /*
  329.         ** hostname is an alphanumeric name in /etc/hosts
  330.         */
  331.         DPRINTF((stderr,"%s: got hostname\n",pgmname));
  332.         sock.sin_family=host->h_addrtype;
  333.         bcopy(host->h_addr,(caddr_t)&sock.sin_addr,host->h_length);
  334.         name=host->h_name;
  335.         }
  336.     else if ((sock.sin_addr.s_addr=inet_addr(hostname))!=-1)
  337.         {
  338.         /*
  339.         ** hostname is a numeric IP address
  340.         */
  341.         DPRINTF((stderr,"%s: got IP address\n",pgmname));
  342.         sock.sin_family=AF_INET;
  343.         name=hostname;
  344.         }
  345.     else
  346.         /*
  347.         ** hostname is not anything useful
  348.         */
  349.         fatal(sock.sin_addr.s_addr,"establish hostname failed",LPD_ABORT);
  350. #ifndef BRIDGE
  351.     sock.sin_port=srvc->s_port;
  352. #else
  353.     sock.sin_port= htons ( atoi (port));
  354.     DPRINTF ((stderr,"%s:  tcp port %d\n",pgmname, atoi (port) ));  
  355. #endif
  356.     if ((netfd=socket(AF_INET,SOCK_STREAM,0,0))<0)
  357.         fatal(netfd,"establish socket failed",LPD_ABORT);
  358.     if ((rc=connect(netfd,(caddr_t)&sock,sizeof(sock),0))<0)
  359.         fatal(rc,"establish connect failed",LPD_ABORT);
  360.     DPRINTF((stderr,"%s: establish host=%s, socket=%d\n",pgmname,name,netfd));
  361.     return(netfd);
  362.     }
  363.  
  364. /*
  365. **    Handshake: Given a TCP/IP/TELNET connection to an Annex,
  366. **    carry out handshaking necessary to connect to the specified
  367. **    serial port. This is clearly kludgy, and should be replaced
  368. **    with Encore's Annex R4.0.
  369. **
  370. */
  371. void
  372. handshake(netfd,port)
  373. int netfd;
  374. char *port;
  375.     {
  376.     int rc;
  377.  
  378.     DPRINTF((stderr,"%s: handshake fd=%d, port=%s\n",pgmname,netfd,port));
  379. #ifndef BRIDGE
  380.     transmit(netfd,"\r",1);
  381.     trigger(netfd,':');
  382.     trigger(netfd,':');
  383.     transmit(netfd,port,strlen(port));
  384.     transmit(netfd,"\r",1);
  385.     trigger(netfd,'\n');
  386.     trigger(netfd,'\n');
  387. #endif
  388.     }
  389.  
  390. /*
  391. **    Trigger: Scan the data from a specified file descriptor until
  392. **    the given trigger character is encountered. Hey, is this kludgey
  393. **    or what? But it keeps handshake and the comm server synchronized
  394. **    (providing no characters are lost), and keeps pscomm from seeing
  395. **    any of the prompts from the annex. Both trigger and handshake
  396. **    will be obsolesced by Encore's Annex R4.0, which has a better
  397. **    reverse telnet mechanism.
  398. */
  399. void
  400. trigger(fd,ch)
  401. int fd;
  402. char ch;
  403.     {
  404.     int count;
  405.     char buf;
  406.  
  407.     do
  408.         {
  409.         if ((count=receive(fd,&buf,1))!=1)
  410.             fatal(count,"trigger receive would block",LPD_ABORT);
  411. #ifdef DEBUG
  412.         if (debugon) putc(buf,stderr);
  413. #endif
  414.         }
  415.     while (buf!=ch);
  416.     }
  417.  
  418. /*
  419. **    Attach: Given a connection to a particular serial port on an
  420. **    terminal server, invoke an appropriate printer filter to handle the
  421. **    input/output.
  422. */
  423. int
  424. attach(filter,argc,argv,prtfd)
  425. int argc,prtfd;
  426. char *filter,**argv;
  427.     {
  428.     int rc,pid;
  429.  
  430.     if ((pid=fork())<0)
  431.         fatal(pid,"attach fork failed",LPD_ABORT);
  432.     else
  433.         if (pid)
  434.             {
  435.             /*
  436.             ** Child owns the prtfd now.
  437.             */
  438.             close(prtfd);
  439.             DPRINTF((stderr,"%s: attach filter=%s, prtfd=%d, pid=%d\n",pgmname,filter,prtfd,pid));
  440. #ifdef DEBUG
  441.             if (debugon) pargs(argc,argv);
  442. #endif
  443.             return(pid);
  444.             }
  445.         else
  446.             {
  447.             /*
  448.             ** stdout gets connected to the tty end of pty
  449.             ** so pscomm sees a tty devices as its output.
  450.             ** stdin remains connected to lpd output.
  451.             */
  452.             close(1);
  453.             if ((rc=dup2(prtfd,1))==-1)
  454.                 fatal(rc,"attach dup2 failed",LPD_ABORT);
  455.             if ((rc=execvp(filter,argv))==-1)
  456.                 fatal(rc,"attach execvp failed",LPD_ABORT);
  457.             /*
  458.             ** Never reached.
  459.             */
  460.             }
  461.     }
  462.  
  463. /*
  464. **    Rendezvous: waits for the child process (the true printer
  465. **    filter) to complete.
  466. */
  467. void
  468. rendezvous(filterpid,prodpid,conspid)
  469. int filterpid,prodpid,conspid;
  470.     {
  471.     int wpid,rc;
  472.     union wait status;
  473.  
  474.     do
  475.         wpid=wait(&status);
  476.     while ((wpid!=filterpid)&&(wpid!=-1));
  477.     if (wpid==-1)
  478.         fatal(wpid,"rendezvous wait failed",LPD_ABORT);
  479.     rc=status.w_retcode;
  480.     if (rc!=LPD_OK)
  481.         fatal(rc,"rendezvous filter failed",rc);
  482.     DPRINTF((stderr,"%s: rendezvous wpid=%d, retcode=%d\n",pgmname,wpid,rc));
  483.     /*
  484.     ** These may have already returned, in which case the kill
  485.     ** returns an error we can safely ignore.
  486.     */
  487.     kill(prodpid,SIGTERM);
  488.     kill(conspid,SIGTERM);
  489.     }
  490.  
  491. /*
  492. **    Acquire: open a device and return the file descriptor.
  493. */
  494. int
  495. acquire(dev)
  496. char *dev;
  497.     {
  498.     int fd;
  499.  
  500.     if ((fd=open(dev,O_RDWR))==-1)
  501.         fatal(fd,"acquire open failed",LPD_ABORT);
  502.     DPRINTF((stderr,"%s: acquire dev=%s, fd=%d\n",pgmname,dev,fd));
  503.     return(fd);
  504.     }
  505.  
  506. /*
  507. **    Spool: set up the net and pty connections and fork off
  508. **    children to do the actual work.
  509. */
  510. void
  511. spool(netfd,ptyfd,prodpid,conspid)
  512. int netfd,ptyfd,*prodpid,*conspid;
  513.     {
  514.     int rc;
  515.  
  516.     if ((*conspid=fork())<0)
  517.         fatal(*conspid,"spool consumer fork failed",LPD_ABORT);
  518.     if (!*conspid)
  519.         {
  520.         rc=process(netfd,ptyfd);
  521.         /*
  522.         ** Release fds now that consumer is complete.
  523.         */
  524.         close(netfd);
  525.         close(ptyfd);
  526.         exit(rc);
  527.         }
  528.     DPRINTF((stderr,"%s: spool %d->%d=%d\n",pgmname,netfd,ptyfd,*conspid));
  529.     if ((*prodpid=fork())<0)
  530.         fatal(*prodpid,"spool producer fork failed",LPD_ABORT);
  531.     if (!*prodpid)
  532.         {
  533.         rc=process(ptyfd,netfd);
  534.         /*
  535.         ** Release fds now that producer is complete.
  536.         */
  537.         close(ptyfd);
  538.         close(netfd);
  539.         exit(rc);
  540.         }
  541.     /*
  542.     ** Producer and consumer children own the fds now.
  543.     */
  544.     close(ptyfd);
  545.     close(netfd);
  546.     DPRINTF((stderr,"%s: spool %d->%d=%d\n",pgmname,ptyfd,netfd,*prodpid));
  547.     }
  548.  
  549. /*
  550. **    Process: copy from input fd to output fd until end of file.
  551. */
  552. int
  553. process(infd,outfd)
  554.     {
  555.     int len;
  556.     char buf[BUFFER+1];
  557.  
  558.     do
  559.         if ((len=receive(infd,buf,sizeof(buf)))>0)
  560.             {
  561.             transmit(outfd,buf,len);
  562. #ifdef DUMP
  563.             if (dumpon)
  564.                 {
  565.                 buf[len]='\0';
  566.                 fputs(buf,stderr);
  567.                 }
  568. #endif
  569.             }
  570.     while (len>0);
  571.     DPRINTF((stderr,"%s: process %d->%d=%d\n",pgmname,infd,outfd,len));
  572.     return(len);
  573.     }
  574.  
  575. /*
  576. **    Transmit: send a buffer of a given length to a
  577. **    specified file descriptor.
  578. */
  579. void
  580. transmit(fd,buf,len)
  581. int fd,len;
  582. char *buf;
  583.     {
  584.     int count;
  585.  
  586.     while (len>0)
  587.         if ((count=write(fd,buf,len))<0)
  588.             {
  589.             if (errno!=EWOULDBLOCK)
  590.                 fatal(count,"transmit write failed",LPD_ABORT);
  591.             }
  592.         else
  593.             {
  594.             buf+=count;
  595.             len-=count;
  596.             }
  597.     }
  598.  
  599. /*
  600. **    Receive: Receive a specified amount of data from a given
  601. **    file descriptor and place it in the specied buffer.
  602. */
  603. int
  604. receive(fd,buf,len)
  605. int fd,len;
  606. char *buf;
  607.     {
  608.     int count;
  609.  
  610.     if (len<=0)
  611.         return(len);
  612.     if ((count=read(fd,buf,len))<0)
  613.         {
  614.         if (errno!=EWOULDBLOCK)
  615.             fatal(count,"receive read failed",LPD_ABORT);
  616.         }
  617.     else
  618.         if (count>len)
  619.             fatal(count,"receive read overflow",LPD_ABORT);
  620.     return(count);
  621.     }
  622.  
  623. /*
  624. **    Bailout: handle interrupts while attempting to exit
  625. **    as gracefully and politely as possible.
  626. */
  627. void
  628. bailout(rc)
  629. int rc;
  630.     {
  631.     switch(rc)
  632.         {
  633.     case 1:
  634.         signal(SIGPIPE,SIG_DFL);
  635.         fatal(rc,"broken pipe",LPD_ABORT);
  636.         break;
  637.     case 2:
  638.         signal(SIGINT,SIG_DFL);
  639.         fatal(rc,"aborted",LPD_ABORT);
  640.         break;
  641.     default:
  642.         fatal(rc,"unknown signal",LPD_ABORT);
  643.         }
  644.     }
  645.  
  646. /*
  647. **    Fatal: Prints the system error message, prints a user
  648. **    error message, and exits with the specified code.
  649. */
  650. void
  651. fatal(rc,msg,xit)
  652. int rc,xit;
  653. char *msg;
  654.     {
  655.     /*
  656.     ** Naively assume that if rc<=0, it's a system call
  657.     ** return, and perror is valid; otherwise, perror is
  658.     ** misleading.
  659.     */
  660.     if (rc<=0)
  661.         perror("annexf");
  662.     fprintf(stderr,"%s: %s (%d)\n",pgmname,msg,rc);
  663.     cleanup();
  664.     exit(xit);
  665.     }
  666.  
  667. /*
  668. **    Usage: check argc, and report usage error.
  669. */
  670. void
  671. usage(argc)
  672. int argc;
  673.     {
  674.     if (argc <= 0)
  675.         {
  676.         fprintf(stderr,"usage: %s [-v] [-x] [[-h host [-p port]]|[-d device]] [-t pty tty] [-f filter [args]]\n",pgmname);
  677.         cleanup();
  678.         exit(LPD_ABORT);
  679.         }
  680.     }
  681.  
  682. /*
  683. **    Lost: Interrupt service routine which is invoked when
  684. **    a pipe breaks and a connection is lost.
  685. */
  686. void
  687. lost()
  688.     {
  689.     longjmp(context,1);
  690.     }
  691.  
  692. /*
  693. **    Interrupted: Interrupt service routine which is invoked
  694. **    when a interrupt signal is received from another process
  695. **    (typically the line printer spooler daemon).
  696. */
  697. void
  698. interrupted()
  699.     {
  700.     longjmp(context,2);
  701.     }
  702.  
  703. /*
  704. **    Finished: Interrupt service routine which is invoked
  705. **    when the parent receives end of file and signals the
  706. **    child process to terminate.
  707. */
  708. void
  709. finished()
  710.     {
  711.     longjmp(context,3);
  712.     }
  713.  
  714. #ifdef DEBUG
  715. /*
  716. **    Pargs: prints argc and argv argument lists for debugging.
  717. */
  718. void
  719. pargs(argc,argv)
  720. int argc;
  721. char **argv;
  722.     {
  723.     int nargc;
  724.  
  725.     fprintf(stderr,"%s:",pgmname);
  726.     for (nargc=0;argc>0;argc--,nargc++,argv++)
  727.         {
  728.         putc(' ',stderr);
  729.         fputs(*argv,stderr);
  730.         }
  731.     putc('\n',stderr);
  732.     }
  733. #endif
  734.