home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / N / TCPIP / NETKIT-B.05 / NETKIT-B / NetKit-B-0.05 / lpr / lpd / printjob.c,v < prev    next >
Encoding:
Text File  |  1994-05-23  |  29.6 KB  |  1,318 lines

  1. head    1.1;
  2. access;
  3. symbols;
  4. locks
  5.     rzsfl:1.1; strict;
  6. comment    @ * @;
  7.  
  8.  
  9. 1.1
  10. date    94.05.23.09.05.00;    author rzsfl;    state Exp;
  11. branches;
  12. next    ;
  13.  
  14.  
  15. desc
  16. @Original
  17. @
  18.  
  19.  
  20. 1.1
  21. log
  22. @Initial revision
  23. @
  24. text
  25. @/*
  26.  * Copyright (c) 1983 Regents of the University of California.
  27.  * All rights reserved.
  28.  *
  29.  * Redistribution and use in source and binary forms, with or without
  30.  * modification, are permitted provided that the following conditions
  31.  * are met:
  32.  * 1. Redistributions of source code must retain the above copyright
  33.  *    notice, this list of conditions and the following disclaimer.
  34.  * 2. Redistributions in binary form must reproduce the above copyright
  35.  *    notice, this list of conditions and the following disclaimer in the
  36.  *    documentation and/or other materials provided with the distribution.
  37.  * 3. All advertising materials mentioning features or use of this software
  38.  *    must display the following acknowledgement:
  39.  *    This product includes software developed by the University of
  40.  *    California, Berkeley and its contributors.
  41.  * 4. Neither the name of the University nor the names of its contributors
  42.  *    may be used to endorse or promote products derived from this software
  43.  *    without specific prior written permission.
  44.  *
  45.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  46.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  47.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  48.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  49.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  50.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  51.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  52.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  53.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  54.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  55.  * SUCH DAMAGE.
  56.  */
  57.  
  58. #ifndef lint
  59. /*static char sccsid[] = "from: @@(#)printjob.c    5.13 (Berkeley) 3/2/91";*/
  60. static char rcsid[] = "$Id: printjob.c,v 1.2 1993/08/01 17:58:51 mycroft Exp $";
  61. #endif /* not lint */
  62.  
  63. /*
  64.  * printjob -- print jobs in the queue.
  65.  *
  66.  *    NOTE: the lock file is used to pass information to lpq and lprm.
  67.  *    it does not need to be removed because file locks are dynamic.
  68.  */
  69.  
  70. #include "lp.h"
  71. #include "pathnames.h"
  72.  
  73. #define DORETURN    0    /* absorb fork error */
  74. #define DOABORT        1    /* abort if dofork fails */
  75.  
  76. /*
  77.  * Error tokens
  78.  */
  79. #define REPRINT        -2
  80. #define ERROR        -1
  81. #define    OK        0
  82. #define    FATALERR    1
  83. #define    NOACCT        2
  84. #define    FILTERERR    3
  85. #define    ACCESS        4
  86.  
  87. char    title[80];        /* ``pr'' title */
  88. FILE    *cfp;            /* control file */
  89. int    pfd;            /* printer file descriptor */
  90. int    ofd;            /* output filter file descriptor */
  91. int    lfd;            /* lock file descriptor */
  92. int    pid;            /* pid of lpd process */
  93. int    prchild;        /* id of pr process */
  94. int    child;            /* id of any filters */
  95. int    ofilter;        /* id of output filter, if any */
  96. int    tof;            /* true if at top of form */
  97. int    remote;            /* true if sending files to remote */
  98. dev_t    fdev;            /* device of file pointed to by symlink */
  99. ino_t    fino;            /* inode of file pointed to by symlink */
  100.  
  101. char    fromhost[32];        /* user's host machine */
  102. char    logname[32];        /* user's login name */
  103. char    jobname[100];        /* job or file name */
  104. char    class[32];        /* classification field */
  105. char    width[10] = "-w";    /* page width in characters */
  106. char    length[10] = "-l";    /* page length in lines */
  107. char    pxwidth[10] = "-x";    /* page width in pixels */
  108. char    pxlength[10] = "-y";    /* page length in pixels */
  109. char    indent[10] = "-i0";    /* indentation size in characters */
  110. char    tempfile[] = "errsXXXXXX"; /* file name for filter output */
  111.  
  112. printjob()
  113. {
  114.     struct stat stb;
  115.     register struct queue *q, **qp;
  116.     struct queue **queue;
  117.     register int i, nitems;
  118.     long pidoff;
  119.     int count = 0;
  120.     void abortpr();
  121.  
  122.     init();                    /* set up capabilities */
  123.     (void) write(1, "", 1);            /* ack that daemon is started */
  124.     (void) close(2);            /* set up log file */
  125.     if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
  126.         syslog(LOG_ERR, "%s: %m", LF);
  127.         (void) open(_PATH_DEVNULL, O_WRONLY);
  128.     }
  129.     setgid(getegid());
  130.     pid = getpid();                /* for use with lprm */
  131.     setpgrp(0, pid);
  132.     signal(SIGHUP, abortpr);
  133.     signal(SIGINT, abortpr);
  134.     signal(SIGQUIT, abortpr);
  135.     signal(SIGTERM, abortpr);
  136.  
  137.     (void) mktemp(tempfile);
  138.  
  139.     /*
  140.      * uses short form file names
  141.      */
  142.     if (chdir(SD) < 0) {
  143.         syslog(LOG_ERR, "%s: %m", SD);
  144.         exit(1);
  145.     }
  146.     if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
  147.         exit(0);        /* printing disabled */
  148.     lfd = open(LO, O_WRONLY|O_CREAT, 0644);
  149.     if (lfd < 0) {
  150.         syslog(LOG_ERR, "%s: %s: %m", printer, LO);
  151.         exit(1);
  152.     }
  153.     if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
  154.         if (errno == EWOULDBLOCK)    /* active deamon present */
  155.             exit(0);
  156.         syslog(LOG_ERR, "%s: %s: %m", printer, LO);
  157.         exit(1);
  158.     }
  159.     ftruncate(lfd, 0);
  160.     /*
  161.      * write process id for others to know
  162.      */
  163.     sprintf(line, "%u\n", pid);
  164.     pidoff = i = strlen(line);
  165.     if (write(lfd, line, i) != i) {
  166.         syslog(LOG_ERR, "%s: %s: %m", printer, LO);
  167.         exit(1);
  168.     }
  169.     /*
  170.      * search the spool directory for work and sort by queue order.
  171.      */
  172.     if ((nitems = getq(&queue)) < 0) {
  173.         syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
  174.         exit(1);
  175.     }
  176.     if (nitems == 0)        /* no work to do */
  177.         exit(0);
  178.     if (stb.st_mode & 01) {        /* reset queue flag */
  179.         if (fchmod(lfd, stb.st_mode & 0776) < 0)
  180.             syslog(LOG_ERR, "%s: %s: %m", printer, LO);
  181.     }
  182.     openpr();            /* open printer or remote */
  183. again:
  184.     /*
  185.      * we found something to do now do it --
  186.      *    write the name of the current control file into the lock file
  187.      *    so the spool queue program can tell what we're working on
  188.      */
  189.     for (qp = queue; nitems--; free((char *) q)) {
  190.         q = *qp++;
  191.         if (stat(q->q_name, &stb) < 0)
  192.             continue;
  193.     restart:
  194.         (void) lseek(lfd, pidoff, 0);
  195.         (void) sprintf(line, "%s\n", q->q_name);
  196.         i = strlen(line);
  197.         if (write(lfd, line, i) != i)
  198.             syslog(LOG_ERR, "%s: %s: %m", printer, LO);
  199.         if (!remote)
  200.             i = printit(q->q_name);
  201.         else
  202.             i = sendit(q->q_name);
  203.         /*
  204.          * Check to see if we are supposed to stop printing or
  205.          * if we are to rebuild the queue.
  206.          */
  207.         if (fstat(lfd, &stb) == 0) {
  208.             /* stop printing before starting next job? */
  209.             if (stb.st_mode & 0100)
  210.                 goto done;
  211.             /* rebuild queue (after lpc topq) */
  212.             if (stb.st_mode & 01) {
  213.                 for (free((char *) q); nitems--; free((char *) q))
  214.                     q = *qp++;
  215.                 if (fchmod(lfd, stb.st_mode & 0776) < 0)
  216.                     syslog(LOG_WARNING, "%s: %s: %m",
  217.                         printer, LO);
  218.                 break;
  219.             }
  220.         }
  221.         if (i == OK)        /* file ok and printed */
  222.             count++;
  223.         else if (i == REPRINT) { /* try reprinting the job */
  224.             syslog(LOG_INFO, "restarting %s", printer);
  225.             if (ofilter > 0) {
  226.                 kill(ofilter, SIGCONT);    /* to be sure */
  227.                 (void) close(ofd);
  228.                 while ((i = wait(0)) > 0 && i != ofilter)
  229.                     ;
  230.                 ofilter = 0;
  231.             }
  232.             (void) close(pfd);    /* close printer */
  233.             if (ftruncate(lfd, pidoff) < 0)
  234.                 syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
  235.             openpr();        /* try to reopen printer */
  236.             goto restart;
  237.         }
  238.     }
  239.     free((char *) queue);
  240.     /*
  241.      * search the spool directory for more work.
  242.      */
  243.     if ((nitems = getq(&queue)) < 0) {
  244.         syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
  245.         exit(1);
  246.     }
  247.     if (nitems == 0) {        /* no more work to do */
  248.     done:
  249.         if (count > 0) {    /* Files actually printed */
  250.             if (!SF && !tof)
  251.                 (void) write(ofd, FF, strlen(FF));
  252.             if (TR != NULL)        /* output trailer */
  253.                 (void) write(ofd, TR, strlen(TR));
  254.         }
  255.         (void) unlink(tempfile);
  256.         exit(0);
  257.     }
  258.     goto again;
  259. }
  260.  
  261. char    fonts[4][50];    /* fonts for troff */
  262.  
  263. char ifonts[4][40] = {
  264.     _PATH_VFONTR,
  265.     _PATH_VFONTI,
  266.     _PATH_VFONTB,
  267.     _PATH_VFONTS,
  268. };
  269.  
  270. /*
  271.  * The remaining part is the reading of the control file (cf)
  272.  * and performing the various actions.
  273.  */
  274. printit(file)
  275.     char *file;
  276. {
  277.     register int i;
  278.     char *cp;
  279.     int bombed = OK;
  280.  
  281.     /*
  282.      * open control file; ignore if no longer there.
  283.      */
  284.     if ((cfp = fopen(file, "r")) == NULL) {
  285.         syslog(LOG_INFO, "%s: %s: %m", printer, file);
  286.         return(OK);
  287.     }
  288.     /*
  289.      * Reset troff fonts.
  290.      */
  291.     for (i = 0; i < 4; i++)
  292.         strcpy(fonts[i], ifonts[i]);
  293.     strcpy(width+2, "0");
  294.     strcpy(indent+2, "0");
  295.  
  296.     /*
  297.      *      read the control file for work to do
  298.      *
  299.      *      file format -- first character in the line is a command
  300.      *      rest of the line is the argument.
  301.      *      valid commands are:
  302.      *
  303.      *        S -- "stat info" for symbolic link protection
  304.      *        J -- "job name" on banner page
  305.      *        C -- "class name" on banner page
  306.      *              L -- "literal" user's name to print on banner
  307.      *        T -- "title" for pr
  308.      *        H -- "host name" of machine where lpr was done
  309.      *              P -- "person" user's login name
  310.      *              I -- "indent" amount to indent output
  311.      *              f -- "file name" name of text file to print
  312.      *        l -- "file name" text file with control chars
  313.      *        p -- "file name" text file to print with pr(1)
  314.      *        t -- "file name" troff(1) file to print
  315.      *        n -- "file name" ditroff(1) file to print
  316.      *        d -- "file name" dvi file to print
  317.      *        g -- "file name" plot(1G) file to print
  318.      *        v -- "file name" plain raster file to print
  319.      *        c -- "file name" cifplot file to print
  320.      *        1 -- "R font file" for troff
  321.      *        2 -- "I font file" for troff
  322.      *        3 -- "B font file" for troff
  323.      *        4 -- "S font file" for troff
  324.      *        N -- "name" of file (used by lpq)
  325.      *              U -- "unlink" name of file to remove
  326.      *                    (after we print it. (Pass 2 only)).
  327.      *        M -- "mail" to user when done printing
  328.      *
  329.      *      getline reads a line and expands tabs to blanks
  330.      */
  331.  
  332.     /* pass 1 */
  333.  
  334.     while (getline(cfp))
  335.         switch (line[0]) {
  336.         case 'H':
  337.             strcpy(fromhost, line+1);
  338.             if (class[0] == '\0')
  339.                 strncpy(class, line+1, sizeof(class)-1);
  340.             continue;
  341.  
  342.         case 'P':
  343.             strncpy(logname, line+1, sizeof(logname)-1);
  344.             if (RS) {            /* restricted */
  345.                 if (getpwnam(logname) == (struct passwd *)0) {
  346.                     bombed = NOACCT;
  347.                     sendmail(line+1, bombed);
  348.                     goto pass2;
  349.                 }
  350.             }
  351.             continue;
  352.  
  353.         case 'S':
  354.             cp = line+1;
  355.             i = 0;
  356.             while (*cp >= '0' && *cp <= '9')
  357.                 i = i * 10 + (*cp++ - '0');
  358.             fdev = i;
  359.             cp++;
  360.             i = 0;
  361.             while (*cp >= '0' && *cp <= '9')
  362.                 i = i * 10 + (*cp++ - '0');
  363.             fino = i;
  364.             continue;
  365.  
  366.         case 'J':
  367.             if (line[1] != '\0')
  368.                 strncpy(jobname, line+1, sizeof(jobname)-1);
  369.             else
  370.                 strcpy(jobname, " ");
  371.             continue;
  372.  
  373.         case 'C':
  374.             if (line[1] != '\0')
  375.                 strncpy(class, line+1, sizeof(class)-1);
  376.             else if (class[0] == '\0')
  377.                 gethostname(class, sizeof(class));
  378.             continue;
  379.  
  380.         case 'T':    /* header title for pr */
  381.             strncpy(title, line+1, sizeof(title)-1);
  382.             continue;
  383.  
  384.         case 'L':    /* identification line */
  385.             if (!SH && !HL)
  386.                 banner(line+1, jobname);
  387.             continue;
  388.  
  389.         case '1':    /* troff fonts */
  390.         case '2':
  391.         case '3':
  392.         case '4':
  393.             if (line[1] != '\0')
  394.                 strcpy(fonts[line[0]-'1'], line+1);
  395.             continue;
  396.  
  397.         case 'W':    /* page width */
  398.             strncpy(width+2, line+1, sizeof(width)-3);
  399.             continue;
  400.  
  401.         case 'I':    /* indent amount */
  402.             strncpy(indent+2, line+1, sizeof(indent)-3);
  403.             continue;
  404.  
  405.         default:    /* some file to print */
  406.             switch (i = print(line[0], line+1)) {
  407.             case ERROR:
  408.                 if (bombed == OK)
  409.                     bombed = FATALERR;
  410.                 break;
  411.             case REPRINT:
  412.                 (void) fclose(cfp);
  413.                 return(REPRINT);
  414.             case FILTERERR:
  415.             case ACCESS:
  416.                 bombed = i;
  417.                 sendmail(logname, bombed);
  418.             }
  419.             title[0] = '\0';
  420.             continue;
  421.  
  422.         case 'N':
  423.         case 'U':
  424.         case 'M':
  425.             continue;
  426.         }
  427.  
  428.     /* pass 2 */
  429.  
  430. pass2:
  431.     fseek(cfp, 0L, 0);
  432.     while (getline(cfp))
  433.         switch (line[0]) {
  434.         case 'L':    /* identification line */
  435.             if (!SH && HL)
  436.                 banner(line+1, jobname);
  437.             continue;
  438.  
  439.         case 'M':
  440.             if (bombed < NOACCT)    /* already sent if >= NOACCT */
  441.                 sendmail(line+1, bombed);
  442.             continue;
  443.  
  444.         case 'U':
  445.             (void) unlink(line+1);
  446.         }
  447.     /*
  448.      * clean-up in case another control file exists
  449.      */
  450.     (void) fclose(cfp);
  451.     (void) unlink(file);
  452.     return(bombed == OK ? OK : ERROR);
  453. }
  454.  
  455. /*
  456.  * Print a file.
  457.  * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
  458.  * Return -1 if a non-recoverable error occured,
  459.  * 2 if the filter detected some errors (but printed the job anyway),
  460.  * 1 if we should try to reprint this job and
  461.  * 0 if all is well.
  462.  * Note: all filters take stdin as the file, stdout as the printer,
  463.  * stderr as the log file, and must not ignore SIGINT.
  464.  */
  465. print(format, file)
  466.     int format;
  467.     char *file;
  468. {
  469.     register int n;
  470.     register char *prog;
  471.     int fi, fo;
  472.     FILE *fp;
  473.     char *av[15], buf[BUFSIZ];
  474.     int pid, p[2], stopped = 0;
  475.     union wait status;
  476.     struct stat stb;
  477.  
  478.     if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
  479.         return(ERROR);
  480.     /*
  481.      * Check to see if data file is a symbolic link. If so, it should
  482.      * still point to the same file or someone is trying to print
  483.      * something he shouldn't.
  484.      */
  485.     if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
  486.         (stb.st_dev != fdev || stb.st_ino != fino))
  487.         return(ACCESS);
  488.     if (!SF && !tof) {        /* start on a fresh page */
  489.         (void) write(ofd, FF, strlen(FF));
  490.         tof = 1;
  491.     }
  492.     if (IF == NULL && (format == 'f' || format == 'l')) {
  493.         tof = 0;
  494.         while ((n = read(fi, buf, BUFSIZ)) > 0)
  495.             if (write(ofd, buf, n) != n) {
  496.                 (void) close(fi);
  497.                 return(REPRINT);
  498.             }
  499.         (void) close(fi);
  500.         return(OK);
  501.     }
  502.     switch (format) {
  503.     case 'p':    /* print file using 'pr' */
  504.         if (IF == NULL) {    /* use output filter */
  505.             prog = _PATH_PR;
  506.             av[0] = "pr";
  507.             av[1] = width;
  508.             av[2] = length;
  509.             av[3] = "-h";
  510.             av[4] = *title ? title : " ";
  511.             av[5] = 0;
  512.             fo = ofd;
  513.             goto start;
  514.         }
  515.         pipe(p);
  516.         if ((prchild = dofork(DORETURN)) == 0) {    /* child */
  517.             dup2(fi, 0);        /* file is stdin */
  518.             dup2(p[1], 1);        /* pipe is stdout */
  519.             for (n = 3; n < NOFILE; n++)
  520.                 (void) close(n);
  521.             execl(_PATH_PR, "pr", width, length,
  522.                 "-h", *title ? title : " ", 0);
  523.             syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
  524.             exit(2);
  525.         }
  526.         (void) close(p[1]);        /* close output side */
  527.         (void) close(fi);
  528.         if (prchild < 0) {
  529.             prchild = 0;
  530.             (void) close(p[0]);
  531.             return(ERROR);
  532.         }
  533.         fi = p[0];            /* use pipe for input */
  534.     case 'f':    /* print plain text file */
  535.         prog = IF;
  536.         av[1] = width;
  537.         av[2] = length;
  538.         av[3] = indent;
  539.         n = 4;
  540.         break;
  541.     case 'l':    /* like 'f' but pass control characters */
  542.         prog = IF;
  543.         av[1] = "-c";
  544.         av[2] = width;
  545.         av[3] = length;
  546.         av[4] = indent;
  547.         n = 5;
  548.         break;
  549.     case 'r':    /* print a fortran text file */
  550.         prog = RF;
  551.         av[1] = width;
  552.         av[2] = length;
  553.         n = 3;
  554.         break;
  555.     case 't':    /* print troff output */
  556.     case 'n':    /* print ditroff output */
  557.     case 'd':    /* print tex output */
  558.         (void) unlink(".railmag");
  559.         if ((fo = creat(".railmag", FILMOD)) < 0) {
  560.             syslog(LOG_ERR, "%s: cannot create .railmag", printer);
  561.             (void) unlink(".railmag");
  562.         } else {
  563.             for (n = 0; n < 4; n++) {
  564.                 if (fonts[n][0] != '/')
  565.                     (void) write(fo, _PATH_VFONT, 15);
  566.                 (void) write(fo, fonts[n], strlen(fonts[n]));
  567.                 (void) write(fo, "\n", 1);
  568.             }
  569.             (void) close(fo);
  570.         }
  571.         prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
  572.         av[1] = pxwidth;
  573.         av[2] = pxlength;
  574.         n = 3;
  575.         break;
  576.     case 'c':    /* print cifplot output */
  577.         prog = CF;
  578.         av[1] = pxwidth;
  579.         av[2] = pxlength;
  580.         n = 3;
  581.         break;
  582.     case 'g':    /* print plot(1G) output */
  583.         prog = GF;
  584.         av[1] = pxwidth;
  585.         av[2] = pxlength;
  586.         n = 3;
  587.         break;
  588.     case 'v':    /* print raster output */
  589.         prog = VF;
  590.         av[1] = pxwidth;
  591.         av[2] = pxlength;
  592.         n = 3;
  593.         break;
  594.     default:
  595.         (void) close(fi);
  596.         syslog(LOG_ERR, "%s: illegal format character '%c'",
  597.             printer, format);
  598.         return(ERROR);
  599.     }
  600.     if ((av[0] = rindex(prog, '/')) != NULL)
  601.         av[0]++;
  602.     else
  603.         av[0] = prog;
  604.     av[n++] = "-n";
  605.     av[n++] = logname;
  606.     av[n++] = "-h";
  607.     av[n++] = fromhost;
  608.     av[n++] = AF;
  609.     av[n] = 0;
  610.     fo = pfd;
  611.     if (ofilter > 0) {        /* stop output filter */
  612.         write(ofd, "\031\1", 2);
  613.         while ((pid =
  614.             wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
  615.             ;
  616.         if (status.w_stopval != WSTOPPED) {
  617.             (void) close(fi);
  618.             syslog(LOG_WARNING, "%s: output filter died (%d)",
  619.                 printer, status.w_retcode);
  620.             return(REPRINT);
  621.         }
  622.         stopped++;
  623.     }
  624. start:
  625.     if ((child = dofork(DORETURN)) == 0) {    /* child */
  626.         dup2(fi, 0);
  627.         dup2(fo, 1);
  628.         n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
  629.         if (n >= 0)
  630.             dup2(n, 2);
  631.         for (n = 3; n < NOFILE; n++)
  632.             (void) close(n);
  633.         execv(prog, av);
  634.         syslog(LOG_ERR, "cannot execv %s", prog);
  635.         exit(2);
  636.     }
  637.     (void) close(fi);
  638.     if (child < 0)
  639.         status.w_retcode = 100;
  640.     else
  641.         while ((pid = wait((int *)&status)) > 0 && pid != child)
  642.             ;
  643.     child = 0;
  644.     prchild = 0;
  645.     if (stopped) {        /* restart output filter */
  646.         if (kill(ofilter, SIGCONT) < 0) {
  647.             syslog(LOG_ERR, "cannot restart output filter");
  648.             exit(1);
  649.         }
  650.     }
  651.     tof = 0;
  652.  
  653.     /* Copy filter output to "lf" logfile */
  654.     if (fp = fopen(tempfile, "r")) {
  655.         char tbuf[512];
  656.  
  657.         while (fgets(buf, sizeof(buf), fp))
  658.             fputs(buf, stderr);
  659.         close(fp);
  660.     }
  661.  
  662.     if (!WIFEXITED(status)) {
  663.         syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
  664.             printer, format, status.w_termsig);
  665.         return(ERROR);
  666.     }
  667.     switch (status.w_retcode) {
  668.     case 0:
  669.         tof = 1;
  670.         return(OK);
  671.     case 1:
  672.         return(REPRINT);
  673.     default:
  674.         syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
  675.             printer, format, status.w_retcode);
  676.     case 2:
  677.         return(ERROR);
  678.     }
  679. }
  680.  
  681. /*
  682.  * Send the daemon control file (cf) and any data files.
  683.  * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
  684.  * 0 if all is well.
  685.  */
  686. sendit(file)
  687.     char *file;
  688. {
  689.     register int i, err = OK;
  690.     char *cp, last[BUFSIZ];
  691.  
  692.     /*
  693.      * open control file
  694.      */
  695.     if ((cfp = fopen(file, "r")) == NULL)
  696.         return(OK);
  697.     /*
  698.      *      read the control file for work to do
  699.      *
  700.      *      file format -- first character in the line is a command
  701.      *      rest of the line is the argument.
  702.      *      commands of interest are:
  703.      *
  704.      *            a-z -- "file name" name of file to print
  705.      *              U -- "unlink" name of file to remove
  706.      *                    (after we print it. (Pass 2 only)).
  707.      */
  708.  
  709.     /*
  710.      * pass 1
  711.      */
  712.     while (getline(cfp)) {
  713.     again:
  714.         if (line[0] == 'S') {
  715.             cp = line+1;
  716.             i = 0;
  717.             while (*cp >= '0' && *cp <= '9')
  718.                 i = i * 10 + (*cp++ - '0');
  719.             fdev = i;
  720.             cp++;
  721.             i = 0;
  722.             while (*cp >= '0' && *cp <= '9')
  723.                 i = i * 10 + (*cp++ - '0');
  724.             fino = i;
  725.             continue;
  726.         }
  727.         if (line[0] >= 'a' && line[0] <= 'z') {
  728.             strcpy(last, line);
  729.             while (i = getline(cfp))
  730.                 if (strcmp(last, line))
  731.                     break;
  732.             switch (sendfile('\3', last+1)) {
  733.             case OK:
  734.                 if (i)
  735.                     goto again;
  736.                 break;
  737.             case REPRINT:
  738.                 (void) fclose(cfp);
  739.                 return(REPRINT);
  740.             case ACCESS:
  741.                 sendmail(logname, ACCESS);
  742.             case ERROR:
  743.                 err = ERROR;
  744.             }
  745.             break;
  746.         }
  747.     }
  748.     if (err == OK && sendfile('\2', file) > 0) {
  749.         (void) fclose(cfp);
  750.         return(REPRINT);
  751.     }
  752.     /*
  753.      * pass 2
  754.      */
  755.     fseek(cfp, 0L, 0);
  756.     while (getline(cfp))
  757.         if (line[0] == 'U')
  758.             (void) unlink(line+1);
  759.     /*
  760.      * clean-up in case another control file exists
  761.      */
  762.     (void) fclose(cfp);
  763.     (void) unlink(file);
  764.     return(err);
  765. }
  766.  
  767. /*
  768.  * Send a data file to the remote machine and spool it.
  769.  * Return positive if we should try resending.
  770.  */
  771. sendfile(type, file)
  772.     char type, *file;
  773. {
  774.     register int f, i, amt;
  775.     struct stat stb;
  776.     char buf[BUFSIZ];
  777.     int sizerr, resp;
  778.  
  779.     if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
  780.         return(ERROR);
  781.     /*
  782.      * Check to see if data file is a symbolic link. If so, it should
  783.      * still point to the same file or someone is trying to print something
  784.      * he shouldn't.
  785.      */
  786.     if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
  787.         (stb.st_dev != fdev || stb.st_ino != fino))
  788.         return(ACCESS);
  789.     (void) sprintf(buf, "%c%d %s\n", type, stb.st_size, file);
  790.     amt = strlen(buf);
  791.     for (i = 0;  ; i++) {
  792.         if (write(pfd, buf, amt) != amt ||
  793.             (resp = response()) < 0 || resp == '\1') {
  794.             (void) close(f);
  795.             return(REPRINT);
  796.         } else if (resp == '\0')
  797.             break;
  798.         if (i == 0)
  799.             status("no space on remote; waiting for queue to drain");
  800.         if (i == 10)
  801.             syslog(LOG_ALERT, "%s: can't send to %s; queue full",
  802.                 printer, RM);
  803.         sleep(5 * 60);
  804.     }
  805.     if (i)
  806.         status("sending to %s", RM);
  807.     sizerr = 0;
  808.     for (i = 0; i < stb.st_size; i += BUFSIZ) {
  809.         amt = BUFSIZ;
  810.         if (i + amt > stb.st_size)
  811.             amt = stb.st_size - i;
  812.         if (sizerr == 0 && read(f, buf, amt) != amt)
  813.             sizerr = 1;
  814.         if (write(pfd, buf, amt) != amt) {
  815.             (void) close(f);
  816.             return(REPRINT);
  817.         }
  818.     }
  819.     (void) close(f);
  820.     if (sizerr) {
  821.         syslog(LOG_INFO, "%s: %s: changed size", printer, file);
  822.         /* tell recvjob to ignore this file */
  823.         (void) write(pfd, "\1", 1);
  824.         return(ERROR);
  825.     }
  826.     if (write(pfd, "", 1) != 1 || response())
  827.         return(REPRINT);
  828.     return(OK);
  829. }
  830.  
  831. /*
  832.  * Check to make sure there have been no errors and that both programs
  833.  * are in sync with eachother.
  834.  * Return non-zero if the connection was lost.
  835.  */
  836. response()
  837. {
  838.     char resp;
  839.  
  840.     if (read(pfd, &resp, 1) != 1) {
  841.         syslog(LOG_INFO, "%s: lost connection", printer);
  842.         return(-1);
  843.     }
  844.     return(resp);
  845. }
  846.  
  847. /*
  848.  * Banner printing stuff
  849.  */
  850. banner(name1, name2)
  851.     char *name1, *name2;
  852. {
  853.     time_t tvec;
  854.     extern char *ctime();
  855.  
  856.     time(&tvec);
  857.     if (!SF && !tof)
  858.         (void) write(ofd, FF, strlen(FF));
  859.     if (SB) {    /* short banner only */
  860.         if (class[0]) {
  861.             (void) write(ofd, class, strlen(class));
  862.             (void) write(ofd, ":", 1);
  863.         }
  864.         (void) write(ofd, name1, strlen(name1));
  865.         (void) write(ofd, "  Job: ", 7);
  866.         (void) write(ofd, name2, strlen(name2));
  867.         (void) write(ofd, "  Date: ", 8);
  868.         (void) write(ofd, ctime(&tvec), 24);
  869.         (void) write(ofd, "\n", 1);
  870.     } else {    /* normal banner */
  871.         (void) write(ofd, "\n\n\n", 3);
  872.         scan_out(ofd, name1, '\0');
  873.         (void) write(ofd, "\n\n", 2);
  874.         scan_out(ofd, name2, '\0');
  875.         if (class[0]) {
  876.             (void) write(ofd,"\n\n\n",3);
  877.             scan_out(ofd, class, '\0');
  878.         }
  879.         (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
  880.         (void) write(ofd, name2, strlen(name2));
  881.         (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
  882.         (void) write(ofd, ctime(&tvec), 24);
  883.         (void) write(ofd, "\n", 1);
  884.     }
  885.     if (!SF)
  886.         (void) write(ofd, FF, strlen(FF));
  887.     tof = 1;
  888. }
  889.  
  890. char *
  891. scnline(key, p, c)
  892.     register char key, *p;
  893.     char c;
  894. {
  895.     register scnwidth;
  896.  
  897.     for (scnwidth = WIDTH; --scnwidth;) {
  898.         key <<= 1;
  899.         *p++ = key & 0200 ? c : BACKGND;
  900.     }
  901.     return (p);
  902. }
  903.  
  904. #define TRC(q)    (((q)-' ')&0177)
  905.  
  906. scan_out(scfd, scsp, dlm)
  907.     int scfd;
  908.     char *scsp, dlm;
  909. {
  910.     register char *strp;
  911.     register nchrs, j;
  912.     char outbuf[LINELEN+1], *sp, c, cc;
  913.     int d, scnhgt;
  914.     extern char scnkey[][HEIGHT];    /* in lpdchar.c */
  915.  
  916.     for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
  917.         strp = &outbuf[0];
  918.         sp = scsp;
  919.         for (nchrs = 0; ; ) {
  920.             d = dropit(c = TRC(cc = *sp++));
  921.             if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
  922.                 for (j = WIDTH; --j;)
  923.                     *strp++ = BACKGND;
  924.             else
  925.                 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
  926.             if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
  927.                 break;
  928.             *strp++ = BACKGND;
  929.             *strp++ = BACKGND;
  930.         }
  931.         while (*--strp == BACKGND && strp >= outbuf)
  932.             ;
  933.         strp++;
  934.         *strp++ = '\n';    
  935.         (void) write(scfd, outbuf, strp-outbuf);
  936.     }
  937. }
  938.  
  939. dropit(c)
  940.     char c;
  941. {
  942.     switch(c) {
  943.  
  944.     case TRC('_'):
  945.     case TRC(';'):
  946.     case TRC(','):
  947.     case TRC('g'):
  948.     case TRC('j'):
  949.     case TRC('p'):
  950.     case TRC('q'):
  951.     case TRC('y'):
  952.         return (DROP);
  953.  
  954.     default:
  955.         return (0);
  956.     }
  957. }
  958.  
  959. /*
  960.  * sendmail ---
  961.  *   tell people about job completion
  962.  */
  963. sendmail(user, bombed)
  964.     char *user;
  965.     int bombed;
  966. {
  967.     register int i;
  968.     int p[2], s;
  969.     register char *cp;
  970.     char buf[100];
  971.     struct stat stb;
  972.     FILE *fp;
  973.  
  974.     pipe(p);
  975.     if ((s = dofork(DORETURN)) == 0) {        /* child */
  976.         dup2(p[0], 0);
  977.         for (i = 3; i < NOFILE; i++)
  978.             (void) close(i);
  979.         if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
  980.             cp++;
  981.         else
  982.             cp = _PATH_SENDMAIL;
  983.         sprintf(buf, "%s@@%s", user, fromhost);
  984.         execl(_PATH_SENDMAIL, cp, buf, 0);
  985.         exit(0);
  986.     } else if (s > 0) {                /* parent */
  987.         dup2(p[1], 1);
  988.         printf("To: %s@@%s\n", user, fromhost);
  989.         printf("Subject: printer job\n\n");
  990.         printf("Your printer job ");
  991.         if (*jobname)
  992.             printf("(%s) ", jobname);
  993.         switch (bombed) {
  994.         case OK:
  995.             printf("\ncompleted successfully\n");
  996.             break;
  997.         default:
  998.         case FATALERR:
  999.             printf("\ncould not be printed\n");
  1000.             break;
  1001.         case NOACCT:
  1002.             printf("\ncould not be printed without an account on %s\n", host);
  1003.             break;
  1004.         case FILTERERR:
  1005.             if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
  1006.                 (fp = fopen(tempfile, "r")) == NULL) {
  1007.                 printf("\nwas printed but had some errors\n");
  1008.                 break;
  1009.             }
  1010.             printf("\nwas printed but had the following errors:\n");
  1011.             while ((i = getc(fp)) != EOF)
  1012.                 putchar(i);
  1013.             (void) fclose(fp);
  1014.             break;
  1015.         case ACCESS:
  1016.             printf("\nwas not printed because it was not linked to the original file\n");
  1017.         }
  1018.         fflush(stdout);
  1019.         (void) close(1);
  1020.     }
  1021.     (void) close(p[0]);
  1022.     (void) close(p[1]);
  1023.     wait(&s);
  1024. }
  1025.  
  1026. /*
  1027.  * dofork - fork with retries on failure
  1028.  */
  1029. dofork(action)
  1030.     int action;
  1031. {
  1032.     register int i, pid;
  1033.  
  1034.     for (i = 0; i < 20; i++) {
  1035.         if ((pid = fork()) < 0) {
  1036.             sleep((unsigned)(i*i));
  1037.             continue;
  1038.         }
  1039.         /*
  1040.          * Child should run as daemon instead of root
  1041.          */
  1042.         if (pid == 0)
  1043.             setuid(DU);
  1044.         return(pid);
  1045.     }
  1046.     syslog(LOG_ERR, "can't fork");
  1047.  
  1048.     switch (action) {
  1049.     case DORETURN:
  1050.         return (-1);
  1051.     default:
  1052.         syslog(LOG_ERR, "bad action (%d) to dofork", action);
  1053.         /*FALL THRU*/
  1054.     case DOABORT:
  1055.         exit(1);
  1056.     }
  1057.     /*NOTREACHED*/
  1058. }
  1059.  
  1060. /*
  1061.  * Kill child processes to abort current job.
  1062.  */
  1063. void
  1064. abortpr()
  1065. {
  1066.     (void) unlink(tempfile);
  1067.     kill(0, SIGINT);
  1068.     if (ofilter > 0)
  1069.         kill(ofilter, SIGCONT);
  1070.     while (wait(NULL) > 0)
  1071.         ;
  1072.     exit(0);
  1073. }
  1074.  
  1075. init()
  1076. {
  1077.     int status;
  1078.     char *s;
  1079.  
  1080.     if ((status = pgetent(line, printer)) < 0) {
  1081.         syslog(LOG_ERR, "can't open printer description file");
  1082.         exit(1);
  1083.     } else if (status == 0) {
  1084.         syslog(LOG_ERR, "unknown printer: %s", printer);
  1085.         exit(1);
  1086.     }
  1087.     if ((LP = pgetstr("lp", &bp)) == NULL)
  1088.         LP = _PATH_DEFDEVLP;
  1089.     if ((RP = pgetstr("rp", &bp)) == NULL)
  1090.         RP = DEFLP;
  1091.     if ((LO = pgetstr("lo", &bp)) == NULL)
  1092.         LO = DEFLOCK;
  1093.     if ((ST = pgetstr("st", &bp)) == NULL)
  1094.         ST = DEFSTAT;
  1095.     if ((LF = pgetstr("lf", &bp)) == NULL)
  1096.         LF = _PATH_CONSOLE;
  1097.     if ((SD = pgetstr("sd", &bp)) == NULL)
  1098.         SD = _PATH_DEFSPOOL;
  1099.     if ((DU = pgetnum("du")) < 0)
  1100.         DU = DEFUID;
  1101.     if ((FF = pgetstr("ff", &bp)) == NULL)
  1102.         FF = DEFFF;
  1103.     if ((PW = pgetnum("pw")) < 0)
  1104.         PW = DEFWIDTH;
  1105.     sprintf(&width[2], "%d", PW);
  1106.     if ((PL = pgetnum("pl")) < 0)
  1107.         PL = DEFLENGTH;
  1108.     sprintf(&length[2], "%d", PL);
  1109.     if ((PX = pgetnum("px")) < 0)
  1110.         PX = 0;
  1111.     sprintf(&pxwidth[2], "%d", PX);
  1112.     if ((PY = pgetnum("py")) < 0)
  1113.         PY = 0;
  1114.     sprintf(&pxlength[2], "%d", PY);
  1115.     RM = pgetstr("rm", &bp);
  1116.     if (s = checkremote())
  1117.         syslog(LOG_WARNING, s);
  1118.  
  1119.     AF = pgetstr("af", &bp);
  1120.     OF = pgetstr("of", &bp);
  1121.     IF = pgetstr("if", &bp);
  1122.     RF = pgetstr("rf", &bp);
  1123.     TF = pgetstr("tf", &bp);
  1124.     NF = pgetstr("nf", &bp);
  1125.     DF = pgetstr("df", &bp);
  1126.     GF = pgetstr("gf", &bp);
  1127.     VF = pgetstr("vf", &bp);
  1128.     CF = pgetstr("cf", &bp);
  1129.     TR = pgetstr("tr", &bp);
  1130.     RS = pgetflag("rs");
  1131.     SF = pgetflag("sf");
  1132.     SH = pgetflag("sh");
  1133.     SB = pgetflag("sb");
  1134.     HL = pgetflag("hl");
  1135.     RW = pgetflag("rw");
  1136.     BR = pgetnum("br");
  1137.     if ((FC = pgetnum("fc")) < 0)
  1138.         FC = 0;
  1139.     if ((FS = pgetnum("fs")) < 0)
  1140.         FS = 0;
  1141.     if ((XC = pgetnum("xc")) < 0)
  1142.         XC = 0;
  1143.     if ((XS = pgetnum("xs")) < 0)
  1144.         XS = 0;
  1145.     tof = !pgetflag("fo");
  1146. }
  1147.  
  1148. /*
  1149.  * Acquire line printer or remote connection.
  1150.  */
  1151. openpr()
  1152. {
  1153.     register int i, n;
  1154.     int resp;
  1155.  
  1156.     if (!sendtorem && *LP) {
  1157.         for (i = 1; ; i = i < 32 ? i << 1 : i) {
  1158.             pfd = open(LP, RW ? O_RDWR : O_WRONLY);
  1159.             if (pfd >= 0)
  1160.                 break;
  1161.             if (errno == ENOENT) {
  1162.                 syslog(LOG_ERR, "%s: %m", LP);
  1163.                 exit(1);
  1164.             }
  1165.             if (i == 1)
  1166.                 status("waiting for %s to become ready (offline ?)", printer);
  1167.             sleep(i);
  1168.         }
  1169.         if (isatty(pfd))
  1170.             setty();
  1171.         status("%s is ready and printing", printer);
  1172.     } else if (RM != NULL) {
  1173.         for (i = 1; ; i = i < 256 ? i << 1 : i) {
  1174.             resp = -1;
  1175.             pfd = getport(RM);
  1176.             if (pfd >= 0) {
  1177.                 (void) sprintf(line, "\2%s\n", RP);
  1178.                 n = strlen(line);
  1179.                 if (write(pfd, line, n) == n &&
  1180.                     (resp = response()) == '\0')
  1181.                     break;
  1182.                 (void) close(pfd);
  1183.             }
  1184.             if (i == 1) {
  1185.                 if (resp < 0)
  1186.                     status("waiting for %s to come up", RM);
  1187.                 else {
  1188.                     status("waiting for queue to be enabled on %s", RM);
  1189.                     i = 256;
  1190.                 }
  1191.             }
  1192.             sleep(i);
  1193.         }
  1194.         status("sending to %s", RM);
  1195.         remote = 1;
  1196.     } else {
  1197.         syslog(LOG_ERR, "%s: no line printer device or host name",
  1198.             printer);
  1199.         exit(1);
  1200.     }
  1201.     /*
  1202.      * Start up an output filter, if needed.
  1203.      */
  1204.     if (!remote && OF) {
  1205.         int p[2];
  1206.         char *cp;
  1207.  
  1208.         pipe(p);
  1209.         if ((ofilter = dofork(DOABORT)) == 0) {    /* child */
  1210.             dup2(p[0], 0);        /* pipe is std in */
  1211.             dup2(pfd, 1);        /* printer is std out */
  1212.             for (i = 3; i < NOFILE; i++)
  1213.                 (void) close(i);
  1214.             if ((cp = rindex(OF, '/')) == NULL)
  1215.                 cp = OF;
  1216.             else
  1217.                 cp++;
  1218.             execl(OF, cp, width, length, 0);
  1219.             syslog(LOG_ERR, "%s: %s: %m", printer, OF);
  1220.             exit(1);
  1221.         }
  1222.         (void) close(p[0]);        /* close input side */
  1223.         ofd = p[1];            /* use pipe for output */
  1224.     } else {
  1225.         ofd = pfd;
  1226.         ofilter = 0;
  1227.     }
  1228. }
  1229.  
  1230. struct bauds {
  1231.     int    baud;
  1232.     int    speed;
  1233. } bauds[] = {
  1234.     50,    B50,
  1235.     75,    B75,
  1236.     110,    B110,
  1237.     134,    B134,
  1238.     150,    B150,
  1239.     200,    B200,
  1240.     300,    B300,
  1241.     600,    B600,
  1242.     1200,    B1200,
  1243.     1800,    B1800,
  1244.     2400,    B2400,
  1245.     4800,    B4800,
  1246.     9600,    B9600,
  1247.     19200,    EXTA,
  1248.     38400,    EXTB,
  1249.     0,    0
  1250. };
  1251.  
  1252. /*
  1253.  * setup tty lines.
  1254.  */
  1255. setty()
  1256. {
  1257.     struct sgttyb ttybuf;
  1258.     register struct bauds *bp;
  1259.  
  1260.     if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
  1261.         syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
  1262.         exit(1);
  1263.     }
  1264.     if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
  1265.         syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
  1266.         exit(1);
  1267.     }
  1268.     if (BR > 0) {
  1269.         for (bp = bauds; bp->baud; bp++)
  1270.             if (BR == bp->baud)
  1271.                 break;
  1272.         if (!bp->baud) {
  1273.             syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
  1274.             exit(1);
  1275.         }
  1276.         ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
  1277.     }
  1278.     ttybuf.sg_flags &= ~FC;
  1279.     ttybuf.sg_flags |= FS;
  1280.     if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
  1281.         syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
  1282.         exit(1);
  1283.     }
  1284.     if (XC) {
  1285.         if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
  1286.             syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
  1287.             exit(1);
  1288.         }
  1289.     }
  1290.     if (XS) {
  1291.         if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
  1292.             syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
  1293.             exit(1);
  1294.         }
  1295.     }
  1296. }
  1297.  
  1298. /*VARARGS1*/
  1299. status(msg, a1, a2, a3)
  1300.     char *msg;
  1301. {
  1302.     register int fd;
  1303.     char buf[BUFSIZ];
  1304.  
  1305.     umask(0);
  1306.     fd = open(ST, O_WRONLY|O_CREAT, 0664);
  1307.     if (fd < 0 || flock(fd, LOCK_EX) < 0) {
  1308.         syslog(LOG_ERR, "%s: %s: %m", printer, ST);
  1309.         exit(1);
  1310.     }
  1311.     ftruncate(fd, 0);
  1312.     sprintf(buf, msg, a1, a2, a3);
  1313.     strcat(buf, "\n");
  1314.     (void) write(fd, buf, strlen(buf));
  1315.     (void) close(fd);
  1316. }
  1317. @
  1318.