home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char *rcsid = "@(#)$Header: batch.c,v 1.15 91/01/13 03:27:26 sob Exp $";
- #endif
- /*
- * Batch subroutine for Cnews.
- * Cooperates with C news input subsystem.
- * newsboot must be told to run partial batches left at a crash.
- *
- ***************************************************************************
- This work in its current form is Copyright 1989 Stan Barber
- and is based on the work of Henry Spencer and Geoff Collyer at the University
- of Toronto. This software may be distributed freely as long as no profit is
- made from such distribution and this notice is reproducted in whole.
- ***************************************************************************
- This software is provided on an "as is" basis with no guarantee of
- usefulness or correctness of operation for any purpose, intended or
- otherwise. The author is in no way liable for this software's performance
- or any damage it may cause to any data of any kind anywhere.
- ***************************************************************************
- */
- #include "common.h"
- #include <signal.h>
- #ifdef sparc
- #include <vfork.h>
- #endif
- #ifdef BATCHED_INPUT
- #define YES 1
- #define NO 0
-
- /* imports */
- extern time_t time();
- extern char *malloc(), *mktemp(), *index(), *rindex();
- /* forwards */
- static char *strsave();
- #ifdef XFER_TIMEOUT
- static int xfer_timeout();
- #endif
- static int cpstdin();
- static int appbatch();
- static int enqueue();
-
- /* private data */
- static char tempfile[256];
- static int xfer_lines, old_xfer_lines;
-
- static char art[COPYSIZE]; /* entire article, if it fits */
- static char *endart = art; /* points just past end of article */
- static int incore = YES;
-
- #ifdef NONEWSRUN
- static int uniq = 0; /* unique counter for this process */
- static int in_batchdir = NO;
-
- #endif /* NONEWSRUN */
- static struct batch_file {
- char *name;
- FILE *file;
- char isopen;
- time_t start; /* time of creation */
- off_t size; /* current size */
- int arts; /* number of articles */
- } btch = { NULL, NULL, NO, 0, 0 };
-
- /*
- * stash stdin (up to ".") on the end of the batch input file.
- * kick newsrun if the batch is non-empty and too big or too old.
- *
- * Parameters:
- * "cont_code" is the response code to transmit on successful startup.
- * "err_code" is the response code to transmit when something goes wrong.
- *
- * Returns: -1 on non-zero return from child, 0 on error before fork/exec, 1 else.
- * Side effects: Creates and removes temporary file; accepts input from client.
- * Can time out if XFER_TIMEOUT is defined.
- */
- int
- batch_input_article(cont_code, err_code, errbuf, msg_id)
- int cont_code, err_code;
- char *errbuf;
- char *msg_id;
- {
- int status = 1; /* okay status */
-
- /* protect locking */
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
-
- #ifdef NONEWSRUN
- /* This really should be done in main.c so that we don't have to
- check each time we get a new article - sigh */
-
- if (!in_batchdir)
- if (chdir(INDIR) < 0)
- syslog(LOG_ERR, "chdir(%s) failed", INDIR);
- else
- in_batchdir = YES;
-
- #endif /* NONEWSRUN */
- if (btch.name == NULL) {
- /* BATCH_FILE may trigger unprivileged() */
- btch.name = mktemp(strsave(BATCH_FILE));
- }
- if (btch.name == NULL)
- return 0;
- tempfile[0] = '\0';
- #ifdef UMASK
- (void) umask(UMASK);
- #endif
- /* may create tempfile */
- if (!cpstdin(cont_code, err_code, errbuf, msg_id))
- return 0;
- #ifdef POSTER
- if (tempfile[0])
- (void) chown(tempfile, uid_poster, gid_poster);
- #endif
- status = appbatch();
- btch.arts++;
- if (tempfile[0] != '\0')
- (void) unlink(tempfile);
- if (status == 1 && oktorunbatch())
- status = enqueue(cont_code, err_code, errbuf);
- return status;
- }
-
- int /* boolean */
- oktorunbatch()
- {
- struct stat stbuf;
-
- if (!btch.isopen || fstat(fileno(btch.file), &stbuf) < 0)
- return NO;
- btch.size = stbuf.st_size;
- return(btch.arts >= TOOMANY || btch.size > TOOBIG ||
- btch.size > 0 && time((time_t *)NULL) - btch.start > TOOOLD);
- }
-
- /*
- * Copy standard input (up to a "." line) to art, if it fits,
- * else to a temporary file.
- */
- /* ARGSUSED errbuf */
- static int /* boolean: got article ok? */
- cpstdin(cont_code, err_code, errbuf, msg_id)
- int cont_code, err_code;
- char *errbuf, *msg_id;
- {
- register FILE *tfp = NULL;
- register char *cp, *realline;
- char line[NNTP_STRLEN];
- int toobig = NO;
-
- /* TODO: is this right? used to open here, with errors here */
- printf("%d Ok\r\n", cont_code);
- (void) fflush(stdout);
-
- xfer_lines = old_xfer_lines = 0;
- incore = YES;
- art[0] = '\0';
- endart = art;
- #ifdef XFER_TIMEOUT
- signal(SIGALRM, xfer_timeout);
- (void) alarm(XFER_TIMEOUT);
- #endif
- while (fgets(line, sizeof line, stdin) != NULL) {
- xfer_lines++;
- if ((cp = rindex(line, '\r')) != NULL ||
- (cp = rindex(line, '\n')) != NULL)
- *cp = '\0'; /* nuke CRLF */
- if (line[0] == '.' && line[1] == '\0')
- break; /* article end: exit */
- /* remove hidden dot if present */
- realline = (line[0] == '.'? line+1: line);
- if (toobig) { /* straight to disk */
- (void) fputs(realline, tfp);
- (void) putc('\n', tfp);
- } else {
- int len = strlen(realline);
-
- /*
- * Does art have room to append realline + \n\0?
- * If not, open temp file and dump art & realline there.
- */
- if (sizeof art - (endart - art) < len + 1 + 1) {
- (void) strcpy(tempfile, "/tmp/rpostXXXXXX");
- (void) mktemp(tempfile);
- tfp = fopen(tempfile, "w");
- if (tfp == NULL) {
- printf("%d Cannot create temporary file.\r\n",
- err_code);
- (void) fflush(stdout);
- return 0;
- }
- #ifdef OK_IN_MIDDLE_OKAY
- else {
- printf("%d Ok\r\n", cont_code);
- (void) fflush(stdout);
- }
- #endif
- (void) fwrite(art, 1, endart - art, tfp);
- toobig = YES;
- incore = NO;
- art[0] = '\0';
- endart = art;
- (void) fputs(realline, tfp);
- (void) putc('\n', tfp);
- } else {
- /* fits: append realline\n to art at endart */
- (void) strcpy(endart, realline);
- endart += len;
- *endart++ = '\n';
- *endart = '\0';
- }
- }
- }
- if (tfp != NULL)
- (void) fclose(tfp);
- #ifdef XFER_TIMEOUT
- (void) alarm(0);
- (void) signal(SIGALRM, SIG_DFL);
- #endif
-
- /* See if the connection got closed somehow... */
- if (line[0] != '.' && line[1] != '\0') {
- if (tempfile[0] != '\0')
- (void) unlink(tempfile);
- #ifdef SYSLOG
- #ifdef LOG
- syslog(LOG_ERR,
- "%s cpstdin: EOF before period on line by itself %s",
- hostname, msg_id);
- #else
- syslog(LOG_ERR,
- "cpstdin: EOF before period on line by itself %s", msg_id);
- #endif
- #endif
- return 0;
- }
- return 1;
- }
-
- static int
- xfer_timeout()
- {
- #ifdef XFER_TIMEOUT
- if (old_xfer_lines < xfer_lines) {
- old_xfer_lines = xfer_lines;
- (void) alarm(XFER_TIMEOUT);
- return;
- }
- /* Timed out. */
- printf("%d timeout after %d seconds, closing connection.\r\n",
- ERR_FAULT, XFER_TIMEOUT);
- fflush(stdout);
- #ifdef SYSLOG
- #ifdef LOG
- syslog(LOG_ERR, "%s transfer_timeout", hostname);
- #endif LOG
- #endif
- (void) unlink(tempfile);
- exit(1);
- #endif XFER_TIMEOUT
- }
-
- /*
- * Append "#! rnews count" and art (or tempfile) to batch file, locking
- * assumed. If batch file is too big or too old (but not empty), feed it
- * to newsrun.
- */
- static int /* same as batch_input_article */
- appbatch()
- {
- register FILE *tfp = NULL;
- register int bytes = 0;
- int status = 1; /* okay status */
- long size = 0;
- char artbuf[COPYSIZE];
- struct stat stbuf;
-
- if (btch.file == NULL) {
- btch.file = fopen(btch.name, "a");
- if (btch.file == NULL) {
- #ifdef SYSLOG
- syslog(LOG_ERR,"appbatch: fopen: %s: %m", btch.name);
- #endif
- return 0;
- }
- btch.isopen = YES;
- btch.start = time(&btch.start);
- btch.size = 0;
- btch.arts = 0;
- }
-
- /* find article size and write the article */
- if (incore)
- size = endart - art;
- else {
- tfp = fopen(tempfile, "r");
- if (tfp == NULL)
- return 0;
- if (fstat(fileno(tfp), &stbuf) >= 0)
- size = stbuf.st_size;
- }
- (void) fprintf(btch.file, "#! rnews %ld %s\n", size, hostname);
-
- /* copy the article to the batch file */
- if (incore)
- (void) fwrite(art, 1, endart - art, btch.file);
- else {
- while ((bytes = fread(artbuf, 1, sizeof artbuf, tfp)) > 0)
- if (fwrite(artbuf, 1, bytes, btch.file) != bytes) {
- #ifdef SYSLOG
- syslog(LOG_ERR,
- "enqueue: fwrite can't write %s",
- btch.name);
- #endif
- status = 0; /* hmm, #! count is off */
- break;
- }
- (void) fclose(tfp);
- }
- if (fflush(btch.file) == EOF) {
- #ifdef SYSLOG
- syslog(LOG_ERR, "enqueue: fflush: %s", btch.name);
- #endif
- status = 0;
- }
- return status;
- }
-
- /*
- * Enqueue any partial batch. Called before exit.
- */
- enqpartbatch(cont_code, err_code, errbuf)
- int cont_code, err_code;
- char *errbuf;
- {
- struct stat stbuf;
-
- if (btch.isopen && fstat(fileno(btch.file), &stbuf) >= 0) {
- if (btch.size > 0)
- enqueue(cont_code, err_code, errbuf);
- else {
- (void) fclose(btch.file);
- btch.file = NULL;
- btch.isopen = NO;
- (void) unlink(btch.name); /* remove empty batch */
- }
- }
- }
-
- /*
- * insert the batch file into the input subsystem queue by renaming
- * it to an all-numeric name, then kick newsrun to process it.
- * locks btch.name as appropriate.
- */
- static int /* same as batch_input_article */
- enqueue(cont_code, err_code, errbuf)
- int cont_code, err_code;
- char *errbuf;
- {
- time_t now;
- int pid, wpid, status, fd, exitstat;
- char permname[MAXDIGITS], *number = permname, *newsrun;
- struct stat stbuf;
- #ifdef POSTER
- char *envp[4], user[sizeof(POSTER) + 5], logname[sizeof(POSTER) + 8];
- char *home;
- #else
- char *envp[1];
- #endif
-
- if (fclose(btch.file) != 0) {
- #ifdef SYSLOG
- syslog(LOG_ERR, "enqueue: fclose: %m");
- #endif
- }
- btch.file = NULL;
- btch.isopen = NO;
- btch.start = 0;
- btch.size = 0;
- btch.arts = 0;
-
- (void) fflush(stdout);
- (void) fflush(stderr);
- #ifndef NONEWSRUN
- pid = fork();
- if (pid == -1) {
- #ifdef SYSLOG
- syslog(LOG_ERR, "enqueue: fork: %m");
- #endif
- return 0;
- } else if (pid != 0) { /* parent */
- while ((wpid = wait(&status)) != -1 && wpid != pid)
- ;
- exitstat = (status>>8)&0377;
- #ifdef SYSLOG
- if (exitstat != 0) {
- syslog(LOG_ERR, " enqueue returned exit status 0%o",
- exitstat);
- }
- #endif
- return exitstat != 0? -1 :1;
- }
- #endif /* NONEWSRUN */
- #ifdef POSTER
- #ifndef USG
- if (getuid() == 0) initgroups(POSTER,gid_poster);
- #endif
- (void) setgid(gid_poster);
- (void) setuid(uid_poster);
- #endif
- #ifndef NONEWSRUN
- /* child: must exit */
- #ifdef SYSLOG
- /* Close in such a way that syslog() will know to reopen */
- closelog();
- #endif
- for (fd = 3; fd < 20; fd++)
- (void) close(fd);
- if (chdir(INDIR) < 0) {
- #ifdef SYSLOG
- syslog(LOG_ERR, "enqueue: chdir(%s): %m", INDIR);
- #else
- ;
- #endif
- }
-
- /* rename btch.name to a number so newsrun will see it */
- sprintf(number, "%ld", (long)time(&now));
- #else
- sprintf(number, "%d.%d", getpid(), uniq++);
- #endif /* NONEWSRUN */
- while (link(btch.name, permname) < 0) {
- if (stat(btch.name, &stbuf) < 0)
- break;
- sleep(2);
- #ifndef NONEWSRUN
- sprintf(number, "%ld", (long)time(&now));
- #else
- sprintf(number, "%d.%d", getpid(), uniq++);
- #endif /* NONEWSRUN */
- }
- if (unlink(btch.name) < 0) {
- #ifdef SYSLOG
- syslog(LOG_ERR, "enqueue: cannot unlink %s: %m", btch.name);
- #endif
- }
- #ifndef NONEWSRUN
-
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
- (void) fflush(stdout);
- (void) fflush(stderr);
- newsrun = strsave(NEWSRUN);
- if (newsrun == NULL)
- newsrun = "/usr/lib/newsbin/input/newsrun";
-
- /* Empty environment keeps cnews inews from telling lies */
- #ifdef POSTER
- sprintf(user, "USER=%s", POSTER);
- sprintf(logname, "LOGNAME=%s", POSTER);
- if ((home = (char *)malloc(strlen(home_poster)+5)) != NULL)
- sprintf(home, "HOME=%s", home_poster);
- envp[0] = user;
- envp[1] = logname;
- envp[2] = home;
- envp[3] = 0;
- #else
- envp[0] = 0;
- #endif
- #ifdef USG
- /* execle() fails because newsrun is a shell procedure */
- execle("/bin/sh", "sh", newsrun, (char *)NULL, envp);
- #else
- execle(newsrun, newsrun, (char *)NULL, envp);
- #endif
- #ifdef SYSLOG
- syslog(LOG_ERR, "enqueue: execle(%s): %m", newsrun);
- #endif
- exit(1);
- /* NOTREACHED */
- #else
- return(1);
- #endif /* NONEWSRUN */
- }
-
- static char *
- strsave(s)
- register char *s;
- {
- register char *news = malloc((unsigned)(strlen(s) + 1));
-
- if (news != NULL)
- strcpy(news, s);
- return news;
- }
- #endif
-
-
-