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
Wrap
C/C++ Source or Header
|
1989-06-27
|
12KB
|
510 lines
/*
* rnews - setuid-news fake rnews for nntp: appends article to a batch with
* a fixed name under in.coming. if batch is too big or old, rename
* batch to be an input candidate and kick off a newsrun to process
* the batch. the batch file is locked during appending.
* Cooperates with C news input subsystem.
* newsboot must be told to run partial batches left at a crash.
*/
#include "../common/conf.h"
#include "common.h"
#include <signal.h>
#define TOOBIG 300000L /* batch > TOOBIG bytes, kick rnews */
#define TOOOLD (5*60) /* batch > TOOOLD seconds old, kick rnews */
#define COPYSIZE 8192 /* bytes to copy at one time */
#define MAXDIGITS 25 /* lg(maxlongint) + epsilon */
#define MAXSTR 1024
#define INDIR artfile("in.coming")
#define BATCHFILE artfile("in.coming/nntp.XXXXXX")
#define NEWSRUN binfile("input/newsrun")
#define YES 1
#define NO 0
/* imports */
extern time_t time();
extern char *malloc(), *mktemp(), *index(), *rindex();
/* forwards */
static char *artfile(), *binfile(), *strsave();
static void error(), warning();
static int xfer_timeout();
/* 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;
static struct batch_file {
char *name;
FILE *file;
char isopen;
time_t start; /* time of creation */
off_t size; /* current size */
} btch = { NULL, NULL, NO, 0, 0 };
char *progname = "nntpd";
#ifndef lint
static char *sccsid = "@(#)batch.c 1.5 (Toronto) 31/4/89";
#endif
/*
* 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 el
e.
* 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)
int cont_code, err_code;
char *errbuf;
{
int status = 1; /* okay status */
/* protect locking */
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
if (btch.name == NULL) {
/* BATCHFILE may trigger unprivileged() */
btch.name = mktemp(strsave(BATCHFILE));
}
if (btch.name == NULL)
return 0;
#ifdef notdef
(void) setgid(getegid());
(void) setuid(geteuid());
#endif
tempfile[0] = '\0';
if (!cpstdin(cont_code, err_code, errbuf)) /* may create tempfile */
return 0;
#ifdef POSTER
(void) chown(tempfile, uid_poster, gid_poster);
#endif
status = appbatch();
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.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)
int cont_code, err_code;
char *errbuf;
{
register FILE *tfp = NULL;
register char *cp, *realline;
char line[NNTP_STRLEN];
int toobig = NO;
int (*otimeout)();
/* 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
otimeout = 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, otimeout);
#endif
/* See if the connection got closed somehow... */
if (line[0] != '.' && line[1] != '\0') {
if (tempfile[0] != '\0')
(void) unlink(tempfile);
#ifdef LOG
syslog(LOG_ERR, "%s spawn: EOF before period on line by itself",
hostname);
#else
syslog(LOG_ERR, "spawn: EOF before period on line by itself");
#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 LOG
syslog(LOG_ERR, "%s transfer_timeout", hostname);
#endif LOG
(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)
return 0;
btch.isopen = YES;
btch.size = 0;
btch.start = time(&btch.start);
}
/* 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\n", size);
/* 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) {
warning("can't write %s", btch.name);
status = 0; /* hmm, #! count is off */
break;
}
(void) fclose(tfp);
}
if (fflush(btch.file) == EOF) {
warning("can't write %s", btch.name);
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;
(void) fclose(btch.file);
btch.file = NULL;
btch.isopen = NO;
btch.start = 0;
btch.size = 0;
(void) fflush(stdout);
(void) fflush(stderr);
pid = fork();
if (pid == -1) {
warning("can't fork", "");
return 0;
} else if (pid != 0) { /* parent */
while ((wpid = wait(&status)) != -1 && wpid != pid)
;
exitstat = (status>>8)&0377;
if (exitstat != 0) {
syslog(LOG_ERR, "%s: enqueue returned exit status 0%o",
progname, exitstat);
strcpy(errbuf, "enqueue failed to run newsrun\n");
}
return exitstat != 0? -1 :1;
}
/* child: must exit */
for (fd = 3; fd < 20; fd++)
(void) close(fd);
if (chdir(INDIR) < 0) {
syslog(LOG_ERR, "%s: chdir(%s) failed", progname, INDIR);
nerror("can't change directory to %s", INDIR);
}
/* rename btch.name to a number so newsrun will see it */
sprintf(number, "%ld", (long)time(&now));
while (link(btch.name, permname) < 0) {
if (stat(btch.name, &stbuf) < 0)
break;
sleep(2);
sprintf(number, "%ld", (long)time(&now));
}
if (unlink(btch.name) < 0)
vanished(btch.name);
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";
execl(newsrun, newsrun, (char *)NULL);
syslog(LOG_ERR, "%s: can't run %s", progname, newsrun);
error("attempt to run %s failed!", newsrun);
exit(1);
/* NOTREACHED */
}
vanished(s) /* grieve for s, nerror [exit] */
char *s;
{
syslog(LOG_ERR, "%s: %s vanished underfoot!", progname, s);
nerror("%s vanished underfoot!", s); /* unlocks, exits */
}
static
nerror(fmt, s) /* error, unused to be with unlock */
char *fmt, *s;
{
error(fmt, s);
}
/* C news library starts here */
/*
* error - print best error message possible and exit
*/
static void warning();
static void
error(s1, s2)
char *s1;
char *s2;
{
warning(s1, s2);
exit(1);
}
/*
* warning - print best error message possible and clear errno
*/
extern int errno, sys_nerr;
extern char *sys_errlist[];
extern char *progname;
extern char *getenv();
static void
warning(s1, s2)
char *s1;
char *s2;
{
char *cmdname;
(void) fflush(stdout); /* hack */
cmdname = getenv("CMDNAME");
if (cmdname != NULL && *cmdname != '\0')
fprintf(stderr, "%s:", cmdname); /* No space after :. */
if (progname != NULL)
fprintf(stderr, "%s: ", progname);
fprintf(stderr, s1, s2);
if (errno > 0 && errno < sys_nerr)
fprintf(stderr, " (%s)", sys_errlist[errno]);
fprintf(stderr, "\n");
errno = 0;
}
void
unprivileged()
{
(void) setgid(getgid());
(void) setuid(getuid());
}
static char *
artfile(s)
char *s;
{
static char name[MAXSTR];
strcpy(name, "/usr/spool/news/");
strcat(name, s);
return name;
}
static char *
binfile(s)
char *s;
{
static char name[MAXSTR];
strcpy(name, "/usr/lib/newsbin/");
strcat(name, s);
return name;
}
#ifdef notdef
static char *
ctlfile(s)
char *s;
{
static char name[MAXSTR];
strcpy(name, "/usr/lib/news/");
strcat(name, s);
return name;
}
#endif
static char *
strsave(s)
register char *s;
{
register char *news = malloc((unsigned)(strlen(s) + 1));
if (news != NULL)
strcpy(news, s);
return news;
}
#ifndef SYSLOG
/* VARARGS 2 */
static
syslog(level, fmt)
int level;
char *fmt;
{
}
#endif