home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff319.lzh / CNewsSrc / cnews.orig.lzh / nntpdiffs / src.allnew / batch.c
C/C++ Source or Header  |  1989-06-27  |  12KB  |  510 lines

  1. /*
  2.  * rnews - setuid-news fake rnews for nntp: appends article to a batch with
  3.  *    a fixed name under in.coming.  if batch is too big or old, rename
  4.  *    batch to be an input candidate and kick off a newsrun to process
  5.  *    the batch.  the batch file is locked during appending.
  6.  * Cooperates with C news input subsystem.
  7.  *    newsboot must be told to run partial batches left at a crash.
  8.  */
  9. #include "../common/conf.h"
  10. #include "common.h"
  11. #include <signal.h>
  12.  
  13. #define TOOBIG 300000L        /* batch > TOOBIG bytes, kick rnews */
  14. #define TOOOLD (5*60)        /* batch > TOOOLD seconds old, kick rnews */
  15. #define COPYSIZE 8192        /* bytes to copy at one time */
  16. #define MAXDIGITS 25        /* lg(maxlongint) + epsilon */
  17. #define MAXSTR 1024
  18.  
  19. #define INDIR        artfile("in.coming")
  20. #define BATCHFILE    artfile("in.coming/nntp.XXXXXX")
  21. #define NEWSRUN        binfile("input/newsrun")
  22.  
  23. #define YES 1
  24. #define NO 0
  25.  
  26. /* imports */
  27. extern time_t time();
  28. extern char *malloc(), *mktemp(), *index(), *rindex();
  29.  
  30. /* forwards */
  31. static char *artfile(), *binfile(), *strsave();
  32. static void error(), warning();
  33. static int xfer_timeout();
  34.  
  35. /* private data */
  36. static char tempfile[256];
  37. static int xfer_lines, old_xfer_lines;
  38.  
  39. static char art[COPYSIZE];        /* entire article, if it fits */
  40. static char *endart = art;        /* points just past end of article */
  41. static int incore = YES;
  42.  
  43. static struct batch_file {
  44.     char *name;
  45.     FILE *file;
  46.     char isopen;
  47.     time_t start;            /* time of creation */
  48.     off_t size;            /* current size */
  49. } btch = { NULL, NULL, NO, 0, 0 };
  50.  
  51. char *progname = "nntpd";
  52.  
  53. #ifndef lint
  54. static    char    *sccsid = "@(#)batch.c    1.5    (Toronto) 31/4/89";
  55. #endif
  56.  
  57. /*
  58.  * stash stdin (up to ".") on the end of the batch input file.
  59.  * kick newsrun if the batch is non-empty and too big or too old.
  60.  *
  61.  * Parameters:
  62.  *    "cont_code" is the response code to transmit on successful startup.
  63.  *    "err_code" is the response code to transmit when something goes wrong.
  64.  *
  65.  * Returns: -1 on non-zero return from child, 0 on error before fork/exec, 1 el
  66. e.
  67.  * Side effects: Creates and removes temporary file; accepts input from client.
  68.  *        Can time out if XFER_TIMEOUT is defined.
  69.  */
  70. int
  71. batch_input_article(cont_code, err_code, errbuf)
  72. int cont_code, err_code;
  73. char *errbuf;
  74. {
  75.     int status = 1;            /* okay status */
  76.  
  77.     /* protect locking */
  78.     signal(SIGINT, SIG_IGN);
  79.     signal(SIGQUIT, SIG_IGN);
  80.     signal(SIGHUP, SIG_IGN);
  81.  
  82.     if (btch.name == NULL) {
  83.         /* BATCHFILE may trigger unprivileged() */
  84.         btch.name = mktemp(strsave(BATCHFILE));
  85.     }
  86.     if (btch.name == NULL)
  87.         return 0;
  88. #ifdef notdef
  89.     (void) setgid(getegid());
  90.     (void) setuid(geteuid());
  91. #endif
  92.     tempfile[0] = '\0';
  93.     if (!cpstdin(cont_code, err_code, errbuf))    /* may create tempfile */
  94.         return 0;
  95. #ifdef POSTER
  96.     (void) chown(tempfile, uid_poster, gid_poster);
  97. #endif
  98.     status = appbatch();
  99.     if (tempfile[0] != '\0')
  100.         (void) unlink(tempfile);
  101.     if (status == 1 && oktorunbatch())
  102.         status = enqueue(cont_code, err_code, errbuf);
  103.     return status;
  104. }
  105.  
  106. int                        /* boolean */
  107. oktorunbatch()
  108. {
  109.     struct stat stbuf;
  110.  
  111.     if (!btch.isopen || fstat(fileno(btch.file), &stbuf) < 0)
  112.         return NO;
  113.     btch.size = stbuf.st_size;
  114.     return btch.size > TOOBIG ||
  115.         btch.size > 0 && time((time_t *)NULL) - btch.start > TOOOLD;
  116. }
  117.  
  118. /*
  119.  * Copy standard input (up to a "." line) to art, if it fits,
  120.  * else to a temporary file.
  121.  */
  122. /* ARGSUSED errbuf */
  123. static int                    /* boolean: got article ok? */
  124. cpstdin(cont_code, err_code, errbuf)
  125. int cont_code, err_code;
  126. char *errbuf;
  127. {
  128.     register FILE *tfp = NULL;
  129.     register char *cp, *realline;
  130.     char line[NNTP_STRLEN];
  131.     int toobig = NO;
  132.     int (*otimeout)();
  133.  
  134.     /* TODO: is this right?  used to open here, with errors here */
  135.     printf("%d Ok\r\n", cont_code);
  136.     (void) fflush(stdout);
  137.  
  138.     xfer_lines = old_xfer_lines = 0;
  139.     incore = YES;
  140.     art[0] = '\0';
  141.     endart = art;
  142. #ifdef XFER_TIMEOUT
  143.     otimeout = signal(SIGALRM, xfer_timeout);
  144.     (void) alarm(XFER_TIMEOUT);
  145. #endif
  146.     while (fgets(line, sizeof line, stdin) != NULL) {
  147.         xfer_lines++;
  148.         if ((cp = rindex(line, '\r')) != NULL ||
  149.             (cp = rindex(line, '\n')) != NULL)
  150.             *cp = '\0';            /* nuke CRLF */
  151.         if (line[0] == '.' && line[1] == '\0')
  152.             break;                /* article end: exit */
  153.         /* remove hidden dot if present */
  154.         realline = (line[0] == '.'? line+1: line);
  155.         if (toobig) {                /* straight to disk */
  156.             (void) fputs(realline, tfp);
  157.             (void) putc('\n', tfp);
  158.         } else {
  159.             int len = strlen(realline);
  160.  
  161.             /*
  162.              * Does art have room to append realline + \n\0?
  163.              * If not, open temp file and dump art & realline there.
  164.              */
  165.             if (sizeof art - (endart - art) < len + 1 + 1) {
  166.                 (void) strcpy(tempfile, "/tmp/rpostXXXXXX");
  167.                 (void) mktemp(tempfile);
  168.                 tfp = fopen(tempfile, "w");
  169.                 if (tfp == NULL) {
  170.                     printf("%d Cannot create temporary file.\r\n",
  171.                         err_code);
  172.                     (void) fflush(stdout);
  173.                     return 0;
  174.                 }
  175. #ifdef OK_IN_MIDDLE_OKAY
  176.                 else {
  177.                     printf("%d Ok\r\n", cont_code);
  178.                     (void) fflush(stdout);
  179.                 }
  180. #endif
  181.                 (void) fwrite(art, 1, endart - art, tfp);
  182.                 toobig = YES;
  183.                 incore = NO;
  184.                 art[0] = '\0';
  185.                 endart = art;
  186.                 (void) fputs(realline, tfp);
  187.                 (void) putc('\n', tfp);
  188.             } else {
  189.                 /* fits: append realline\n to art at endart */
  190.                 (void) strcpy(endart, realline);
  191.                 endart += len;
  192.                 *endart++ = '\n';
  193.                 *endart = '\0';
  194.             }
  195.         }
  196.     }
  197.     if (tfp != NULL)
  198.         (void) fclose(tfp);
  199. #ifdef XFER_TIMEOUT
  200.     (void) alarm(0);
  201.     (void) signal(SIGALRM, otimeout);
  202. #endif
  203.  
  204.     /* See if the connection got closed somehow... */
  205.     if (line[0] != '.' && line[1] != '\0') {
  206.         if (tempfile[0] != '\0')
  207.             (void) unlink(tempfile);
  208. #ifdef LOG
  209.         syslog(LOG_ERR, "%s spawn: EOF before period on line by itself",
  210.             hostname);
  211. #else
  212.         syslog(LOG_ERR, "spawn: EOF before period on line by itself");
  213. #endif
  214.         return 0;
  215.     }
  216.     return 1;
  217. }
  218.  
  219. static int
  220. xfer_timeout()
  221. {
  222. #ifdef XFER_TIMEOUT
  223.     if (old_xfer_lines < xfer_lines) {
  224.         old_xfer_lines = xfer_lines;
  225.         (void) alarm(XFER_TIMEOUT);
  226.         return;
  227.     }
  228.     /* Timed out. */
  229.     printf("%d timeout after %d seconds, closing connection.\r\n",
  230.         ERR_FAULT, XFER_TIMEOUT);
  231.     fflush(stdout);
  232. #ifdef LOG
  233.     syslog(LOG_ERR, "%s transfer_timeout", hostname);
  234. #endif LOG
  235.     (void) unlink(tempfile);
  236.     exit(1);
  237. #endif XFER_TIMEOUT
  238. }
  239.  
  240. /*
  241.  * Append "#! rnews count" and art (or tempfile) to batch file, locking assumed
  242.  
  243.  * If batch file is too big or too old (but not empty), feed it to newsrun.
  244.  */
  245. static int                    /* same as batch_input_article */
  246. appbatch()
  247. {
  248.     register FILE *tfp = NULL;
  249.     register int bytes = 0;
  250.     int status = 1;                /* okay status */
  251.     long size = 0;
  252.     char artbuf[COPYSIZE];
  253.     struct stat stbuf;
  254.  
  255.     if (btch.file == NULL) {
  256.         btch.file = fopen(btch.name, "a");
  257.         if (btch.file == NULL)
  258.             return 0;
  259.         btch.isopen = YES;
  260.         btch.size = 0;
  261.         btch.start = time(&btch.start);
  262.     }
  263.  
  264.     /* find article size and write the article */
  265.     if (incore)
  266.         size = endart - art;
  267.     else {
  268.         tfp = fopen(tempfile, "r");
  269.         if (tfp == NULL)
  270.             return 0;
  271.         if (fstat(fileno(tfp), &stbuf) >= 0)
  272.             size = stbuf.st_size;
  273.     }
  274.     (void) fprintf(btch.file, "#! rnews %ld\n", size);
  275.  
  276.     /* copy the article to the batch file */
  277.     if (incore)
  278.         (void) fwrite(art, 1, endart - art, btch.file);
  279.     else {
  280.         while ((bytes = fread(artbuf, 1, sizeof artbuf, tfp)) > 0)
  281.             if (fwrite(artbuf, 1, bytes, btch.file) != bytes) {
  282.                 warning("can't write %s", btch.name);
  283.                 status = 0;    /* hmm, #! count is off */
  284.                 break;
  285.             }
  286.         (void) fclose(tfp);
  287.     }
  288.     if (fflush(btch.file) == EOF) {
  289.         warning("can't write %s", btch.name);
  290.         status = 0;
  291.     }
  292.     return status;
  293. }
  294.  
  295. /*
  296.  * Enqueue any partial batch.  Called before exit.
  297.  */
  298. enqpartbatch(cont_code, err_code, errbuf)
  299. int cont_code, err_code;
  300. char *errbuf;
  301. {
  302.     struct stat stbuf;
  303.  
  304.     if (btch.isopen && fstat(fileno(btch.file), &stbuf) >= 0) {
  305.         if (btch.size > 0)
  306.             enqueue(cont_code, err_code, errbuf);
  307.         else {
  308.             (void) fclose(btch.file);
  309.             btch.file = NULL;
  310.             btch.isopen = NO;
  311.             (void) unlink(btch.name);    /* remove empty batch */
  312.         }
  313.     }
  314. }
  315.  
  316. /* 
  317.  * insert the batch file into the input subsystem queue by renaming
  318.  * it to an all-numeric name, then kick newsrun to process it.
  319.  * locks btch.name as appropriate.
  320.  */
  321. static int                    /* same as batch_input_article */
  322. enqueue(cont_code, err_code, errbuf)
  323. int cont_code, err_code;
  324. char *errbuf;
  325. {
  326.     time_t now;
  327.     int pid, wpid, status, fd, exitstat;
  328.     char permname[MAXDIGITS], *number = permname, *newsrun;
  329.     struct stat stbuf;
  330.  
  331.     (void) fclose(btch.file);
  332.     btch.file = NULL;
  333.     btch.isopen = NO;
  334.     btch.start = 0;
  335.     btch.size = 0;
  336.  
  337.     (void) fflush(stdout);
  338.     (void) fflush(stderr);
  339.     pid = fork();
  340.     if (pid == -1) {
  341.         warning("can't fork", "");
  342.         return 0;
  343.     } else if (pid != 0) {            /* parent */
  344.         while ((wpid = wait(&status)) != -1 && wpid != pid)
  345.             ;
  346.         exitstat = (status>>8)&0377;
  347.         if (exitstat != 0) {
  348.             syslog(LOG_ERR, "%s: enqueue returned exit status 0%o",
  349.                 progname, exitstat);
  350.             strcpy(errbuf, "enqueue failed to run newsrun\n");
  351.         }
  352.         return exitstat != 0? -1 :1;
  353.     }
  354.  
  355.     /* child: must exit */
  356.     for (fd = 3; fd < 20; fd++)
  357.         (void) close(fd);
  358.     if (chdir(INDIR) < 0) {
  359.         syslog(LOG_ERR, "%s: chdir(%s) failed", progname, INDIR);
  360.         nerror("can't change directory to %s", INDIR);
  361.     }
  362.  
  363.     /* rename btch.name to a number so newsrun will see it */
  364.     sprintf(number, "%ld", (long)time(&now));
  365.     while (link(btch.name, permname) < 0) {
  366.         if (stat(btch.name, &stbuf) < 0)
  367.             break;
  368.         sleep(2);
  369.         sprintf(number, "%ld", (long)time(&now));
  370.     }
  371.     if (unlink(btch.name) < 0)
  372.         vanished(btch.name);
  373.  
  374.     signal(SIGINT, SIG_IGN);
  375.     signal(SIGQUIT, SIG_IGN);
  376.     signal(SIGHUP, SIG_IGN);
  377.     (void) fflush(stdout);
  378.     (void) fflush(stderr);
  379.     newsrun = strsave(NEWSRUN);
  380.     if (newsrun == NULL)
  381.         newsrun = "/usr/lib/newsbin/input/newsrun";
  382.     execl(newsrun, newsrun, (char *)NULL);
  383.     syslog(LOG_ERR, "%s: can't run %s", progname, newsrun);
  384.     error("attempt to run %s failed!", newsrun);
  385.     exit(1);
  386.     /* NOTREACHED */
  387. }
  388.  
  389. vanished(s)                    /* grieve for s, nerror [exit] */
  390. char *s;
  391. {
  392.     syslog(LOG_ERR, "%s: %s vanished underfoot!", progname, s);
  393.     nerror("%s vanished underfoot!", s);    /* unlocks, exits */
  394. }
  395.  
  396. static
  397. nerror(fmt, s)                /* error, unused to be with unlock */
  398. char *fmt, *s;
  399. {
  400.     error(fmt, s);
  401. }
  402.  
  403. /* C news library starts here */
  404.  
  405. /*
  406.  * error - print best error message possible and exit
  407.  */
  408. static void warning();
  409.  
  410. static void
  411. error(s1, s2)
  412. char *s1;
  413. char *s2;
  414. {
  415.     warning(s1, s2);
  416.     exit(1);
  417. }
  418.  
  419. /*
  420.  * warning - print best error message possible and clear errno
  421.  */
  422. extern int errno, sys_nerr;
  423. extern char *sys_errlist[];
  424. extern char *progname;
  425. extern char *getenv();
  426.  
  427. static void
  428. warning(s1, s2)
  429. char *s1;
  430. char *s2;
  431. {
  432.     char *cmdname;
  433.  
  434.     (void) fflush(stdout);                /* hack */
  435.     cmdname = getenv("CMDNAME");
  436.     if (cmdname != NULL && *cmdname != '\0')
  437.         fprintf(stderr, "%s:", cmdname);    /* No space after :. */
  438.     if (progname != NULL)
  439.         fprintf(stderr, "%s: ", progname);
  440.     fprintf(stderr, s1, s2);
  441.     if (errno > 0 && errno < sys_nerr)
  442.         fprintf(stderr, " (%s)", sys_errlist[errno]);
  443.     fprintf(stderr, "\n");
  444.     errno = 0;
  445. }
  446.  
  447. void
  448. unprivileged()
  449. {
  450.     (void) setgid(getgid());
  451.     (void) setuid(getuid());
  452. }
  453.  
  454. static char *
  455. artfile(s)
  456. char *s;
  457. {
  458.     static char name[MAXSTR];
  459.  
  460.     strcpy(name, "/usr/spool/news/");
  461.     strcat(name, s);
  462.     return name;
  463. }
  464.  
  465. static char *
  466. binfile(s)
  467. char *s;
  468. {
  469.     static char name[MAXSTR];
  470.  
  471.     strcpy(name, "/usr/lib/newsbin/");
  472.     strcat(name, s);
  473.     return name;
  474. }
  475.  
  476. #ifdef notdef
  477. static char *
  478. ctlfile(s)
  479. char *s;
  480. {
  481.     static char name[MAXSTR];
  482.  
  483.     strcpy(name, "/usr/lib/news/");
  484.     strcat(name, s);
  485.     return name;
  486. }
  487. #endif
  488.  
  489. static char *
  490. strsave(s)
  491. register char *s;
  492. {
  493.     register char *news = malloc((unsigned)(strlen(s) + 1));
  494.  
  495.     if (news != NULL)
  496.         strcpy(news, s);
  497.     return news;
  498. }
  499.  
  500. #ifndef SYSLOG
  501. /* VARARGS 2 */
  502. static
  503. syslog(level, fmt)
  504. int level;
  505. char *fmt;
  506. {
  507. }
  508. #endif
  509.  
  510.