** Read batchfiles on standard input and spew out batches.
#if defined(DO_NEED_TIME)
#endif /* defined(DO_NEED_TIME) */
STATIC
char *InitialString
;
STATIC
long BytesInBatch
= 60 * 1024;
STATIC
long BytesWritten
;
STATIC SIGVAR GotInterrupt
;
STATIC STRING Separator
= "#! rnews %ld";
** Start a batch process.
if (Processor
&& *Processor
) {
(void)sprintf(buff
, Processor
, Host
);
** Close a batch, return exit status.
return fflush(stdout
) == EOF
? 1 : 0;
** Update the batch file and exit.
RequeueAndExit(Cookie
, line
, BytesInArt
)
static char LINE1
[] = "batcher %s times user %.3f system %.3f elapsed %.3f";
static char LINE2
[] ="batcher %s stats batches %d articles %d bytes %ld";
static char NOWRITE
[] = "batcher %s cant write spool %s\n";
static char BATCHDIR
[] = _PATH_BATCHDIR
;
STATend
= TIMEINFOasDOUBLE(Now
);
if (GetResourceUsage(&usertime
, &systime
) < 0) {
(void)printf(LINE1
, Host
, usertime
, systime
, STATend
- STATbegin
);
(void)printf(LINE2
, Host
, BATCHcount
, ArtsWritten
, BytesWritten
);
(void)openlog("batcher", L_OPENLOG_FLAGS
| LOG_PID
, LOG_INN_PROG
);
syslog(L_NOTICE
, LINE1
, Host
, usertime
, systime
, STATend
- STATbegin
);
syslog(L_NOTICE
, LINE2
, Host
, BATCHcount
, ArtsWritten
, BytesWritten
);
/* Last batch exit okay? */
/* Yes, and we're all done -- remove input and exit. */
/* Don't seek back -- batch was fine. */
/* Make an appropriate spool file. */
(void)sprintf(temp
, "%s/%s", BATCHDIR
, Host
);
(void)sprintf(temp
, "%s.bch", Input
);
if ((F
= xfopena(temp
)) == NULL
) {
(void)fprintf(stderr
, "batcher %s cant open %s %s\n",
Host
, temp
, strerror(errno
));
/* If we can back up to where the batch started, do so. */
if (Cookie
!= -1 && fseek(stdin
, Cookie
, SEEK_SET
) == -1) {
(void)fprintf(stderr
, "batcher %s cant seek %s\n",
/* Write the line we had; if the fseek worked, this will be an
* extra line, but that's okay. */
if (line
&& fprintf(F
, "%s %ld\n", line
, BytesInArt
) == EOF
) {
(void)fprintf(stderr
, NOWRITE
, Host
, strerror(errno
));
/* Write rest of stdin to spool. */
while (fgets(buff
, sizeof buff
, stdin
) != NULL
)
if (fputs(buff
, F
) == EOF
) {
(void)fprintf(stderr
, NOWRITE
, Host
, strerror(errno
));
(void)fprintf(stderr
, "batcher %s cant close spool %s\n",
/* If we had a named input file, try to rename the spool. */
if (Input
!= NULL
&& rename(temp
, Input
) < 0) {
(void)fprintf(stderr
, "batcher %s cant rename spool %s\n",
** Mark that we got interrupted.
/* Let two interrupts kill us. */
(void)signal(s
, SIG_DFL
);
** Print a usage message and exit.
(void)fprintf(stderr
, "batcher usage_error.\n");
static char SKIPPING
[] = "batcher %s skipping \"%.40s...\" %s\n";
static char SPOOL
[] = _PATH_SPOOL
;
static char BATCHDIR
[] = _PATH_BATCHDIR
;
while ((i
= getopt(ac
, av
, "a:A:b:B:i:N:p:rs:S:v")) != EOF
)
ArtsInBatch
= atoi(optarg
);
BytesInBatch
= atol(optarg
);
MaxBatches
= atoi(optarg
);
if (MaxArts
&& ArtsInBatch
== 0)
if (MaxBytes
&& BytesInBatch
== 0)
if ((Input
= av
[1]) != NULL
) {
Input
= NEW(char, STRLEN(BATCHDIR
) + 1 + strlen(av
[1]) + 1);
(void)sprintf(Input
, "%s/%s", BATCHDIR
, av
[1]);
if (freopen(Input
, "r", stdin
) == NULL
) {
(void)fprintf(stderr
, "batcher %s cant open %s %s\n",
Host
, Input
, strerror(errno
));
(void)freopen(_PATH_ERRLOG
, "a", stderr
);
/* Go to where the articles are. */
(void)fprintf(stderr
, "batcher %s cant cd %s %s\n",
Host
, SPOOL
, strerror(errno
));
/* Set initial counters, etc. */
data
= NEW(char, datasize
);
(void)signal(SIGHUP
, CATCHinterrupt
);
(void)signal(SIGINT
, CATCHinterrupt
);
(void)signal(SIGTERM
, CATCHinterrupt
);
/* (void)signal(SIGPIPE, CATCHinterrupt); */
STATbegin
= TIMEINFOasDOUBLE(Now
);
while (fgets(line
, sizeof line
, stdin
) != NULL
) {
/* Record line length in case we do an ftell. Not portable to
* systems with non-Unix file formats. */
/* Get lines like "name size" */
if ((p
= strchr(line
, '\n')) == NULL
) {
(void)fprintf(stderr
, SKIPPING
, Host
, line
, "too long");
if (line
[0] == '\0' || line
[0] == COMMENT_CHAR
)
if ((p
= strchr(line
, ' ')) != NULL
) {
/* Strip of leading spool pathname. */
&& line
[STRLEN(SPOOL
)] == '/'
&& EQn(line
, SPOOL
, STRLEN(SPOOL
)))
p
= line
+ STRLEN(SPOOL
) + 1;
if ((artfd
= open(p
, O_RDONLY
)) < 0) {
(void)fprintf(stderr
, SKIPPING
, Host
, p
, strerror(errno
));
(void)sprintf(buff
, "%s/%s", AltSpool
, p
);
if ((artfd
= open(buff
, O_RDONLY
)) < 0) {
(void)fprintf(stderr
, SKIPPING
,
Host
, buff
, strerror(errno
));
/* If we need to, get its size. */
if (fstat(artfd
, &Sb
) < 0) {
(void)fprintf(stderr
, SKIPPING
, Host
, line
, strerror(errno
));
if (!S_ISREG(Sb
.st_mode
)) {
(void)fprintf(stderr
, SKIPPING
, Host
, line
, "not a file");
/* Have an open article, do we need to open a batch? This code
* is here (rather then up before the while loop) so that we
* can avoid sending an empty batch. The goto makes the code
RequeueAndExit(Cookie
, (char *)NULL
, 0L);
if ((F
= BATCHstart()) == NULL
) {
(void)fprintf(stderr
, "batcher %s cant startbatch %d %s\n",
Host
, BATCHcount
, strerror(errno
));
if (InitialString
&& *InitialString
) {
(void)fprintf(F
, "%s\n", InitialString
);
BytesInCB
+= strlen(InitialString
) + 1;
BytesWritten
+= strlen(InitialString
) + 1;
Cookie
= ftell(stdin
) - length
;
/* We're writing a batch, see if adding the current article
* would exceed the limits. */
if ((ArtsInBatch
> 0 && ArtsInCB
+ 1 >= ArtsInBatch
)
|| (BytesInBatch
> 0 && BytesInCB
+ BytesInArt
>= BytesInBatch
)) {
if ((BATCHstatus
= BATCHclose(F
)) != 0) {
(void)fprintf(stderr
, "batcher %s cant closebatch %d %s\n",
Host
, BATCHcount
, strerror(errno
));
(void)fprintf(stderr
, "batcher %s batch %d exit %d\n",
Host
, BATCHcount
, BATCHstatus
);
/* See if we can start a new batch. */
if ((MaxBatches
> 0 && BATCHcount
>= MaxBatches
)
|| (MaxBytes
> 0 && BytesWritten
+ BytesInArt
>= MaxBytes
)
|| (MaxArts
> 0 && ArtsWritten
+ 1 >= MaxArts
)) {
RequeueAndExit(Cookie
, line
, BytesInArt
);
if ((F
= BATCHstart()) == NULL
) {
(void)fprintf(stderr
, "batcher %s cant startbatch %d %s\n",
Host
, BATCHcount
, strerror(errno
));
Cookie
= ftell(stdin
) - length
;
/* Now we can start to send the article! */
if (Separator
&& *Separator
) {
(void)sprintf(buff
, Separator
, BytesInArt
);
BytesInCB
+= strlen(buff
) + 1;
BytesWritten
+= strlen(buff
) + 1;
if (fprintf(F
, "%s\n", buff
) == EOF
|| ferror(F
)) {
(void)fprintf(stderr
, "batcher %s cant write separator %s\n",
/* Write the article. In case of interrupts, retry the read but
* not the fwrite because we can't check that reliably and
while ((i
= read(artfd
, (POINTER
)data
, datasize
)) > 0 || errno
== EINTR
)
if (fwrite((POINTER
)data
, (SIZE_T
)1, (SIZE_T
)i
, F
) != i
)
(void)fprintf(stderr
, "batcher %s cant write article %s\n",
BytesWritten
+= BytesInArt
;
BATCHstatus
= BATCHclose(F
);
RequeueAndExit(Cookie
, line
, BytesInArt
);
BATCHstatus
= BATCHclose(F
);
RequeueAndExit(Cookie
, (char *)NULL
, 0L);