/* $Id: crosspost.c 6135 2003-01-19 01:15:40Z rra $
**
** Parse input to add links for cross posted articles. Input format is one
** line per article. Dots '.' are changed to '/'. Commas ',' or blanks
** ' ' separate entries. Typically this is via a channel feed from innd
** though an edit of the history file can also be used for recovery
** purposes. Sample newsfeeds entry:
**
** # Create the links for cross posted articles
** crosspost:*:Tc,Ap,WR:/usr/local/newsbin/crosspost
**
** WARNING: This no longer works with the current INN; don't use it
** currently. It still exists in the source tree in case someone will
** want to clean it up and make it useable again.
*/
#include "config.h"
#include "clibrary.h"
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/stat.h>
#include "inn/qio.h"
#include "libinn.h"
#include "paths.h"
static char *Dir;
static int debug = false;
static int syncfiles = true;
static unsigned long STATTime = 0;
static unsigned long STATMissing = 0; /* Source file missing */
static unsigned long STATTooLong = 0; /* Name Too Long (src or dest) */
static unsigned long STATLink = 0; /* Link done */
static unsigned long STATLError = 0; /* Link problem */
static unsigned long STATSymlink = 0; /* Symlink done */
static unsigned long STATSLError = 0; /* Symlink problem */
static unsigned long STATMkdir = 0; /* Mkdir done */
static unsigned long STATMdError = 0; /* Mkdir problem */
static unsigned long STATOError = 0; /* Other errors */
#define MAXXPOST 256
#define STATREFRESH 10800 /* 3 hours */
/*
** Write some statistics and reset all counters.
*/
void
ProcessStats()
{
time_t Time;
Time = time (NULL);
syslog(L_NOTICE,
"seconds %lu links %lu %lu symlinks %lu %lu mkdirs %lu %lu missing %lu toolong %lu other %lu",
Time - STATTime, STATLink, STATLError, STATSymlink, STATSLError,
STATMkdir, STATMdError, STATMissing, STATTooLong, STATOError);
STATMissing = STATTooLong = STATLink = STATLError = 0;
STATSymlink = STATSLError = STATMkdir = STATMdError = STATOError = 0;
STATTime = Time;
}
/*
** Try to make one directory. Return false on error.
*/
static bool
MakeDir(Name)
char *Name;
{
struct stat Sb;
if (mkdir(Name, GROUPDIR_MODE) >= 0) {
STATMkdir++;
return true;
}
/* See if it failed because it already exists. */
return stat(Name, &Sb) >= 0 && S_ISDIR(Sb.st_mode);
}
/*
** Make spool directory. Return false on error.
*/
static bool
MakeSpoolDir(Name)
char *Name;
{
char *p;
bool made;
/* Optimize common case -- parent almost always exists. */
if (MakeDir(Name))
return true;
/* Try to make each of comp and comp/foo in turn. */
for (p = Name; *p; p++)
if (*p == '/') {
*p = '\0';
made = MakeDir(Name);
*p = '/';
if (!made) {
STATMdError++;
return false;
}
}
return MakeDir(Name);
}
/*
** Process the input. Data can come from innd:
** news/group/name/<number> [space news/group/<number>]...
** or
** news.group.name/<number>,[news.group.name/<number>]...
*/
static void
ProcessIncoming(qp)
QIOSTATE *qp;
{
char *p;
int i;
int nxp;
int fd;
int lnval ;
char *names[MAXXPOST];
for ( ; ; ) {
if (time(NULL) - STATTime > STATREFRESH)
ProcessStats();
/* Read the first line of data. */
if ((p = QIOread(qp)) == NULL) {
if (QIOtoolong(qp)) {
fprintf(stderr, "crosspost line too long\n");
STATTooLong++;
continue;
}
break;
}
for (i = 0; *p && (i < MAXXPOST); i++) { /* parse input into array */
names[i] = p;
for ( ; *p; p++) {
if (*p == '.') *p++ = '/'; /* dot to slash translation */
else if ((*p == ',') /* name separators */
|| (*p == ' ')
|| (*p == '\t')
|| (*p == '\n')) {
*p++ = '\0';
break;
}
}
}
nxp = i;
if (debug) {
for (i = 0; i < nxp; i++)
fprintf(stderr, "crosspost: debug %d %s\n",
i, names[i]);
}
if(syncfiles) fd = open(names[0], O_RDWR);
for (i = 1; i < nxp; i++) {
lnval = link(names[0], names[i]) ;
if (lnval == 0) STATLink++;
if (lnval < 0 && errno != EXDEV) { /* first try to link */
int j;
char path[SPOOLNAMEBUFF+2];
for (j = 0; (path[j] = names[i][j]) != '\0' ; j++) ;
for (j--; (j > 0) && (path[j] != '/'); j--) ;
if (path[j] == '/') {
path[j] = '\0';
/* try making parent dir */
if (MakeSpoolDir(path) == false) {
fprintf(stderr, "crosspost cant mkdir %s\n",
path);
}
else {
/* 2nd try to link */
lnval = link(names[0], names[i]) ;
if (lnval == 0) STATLink++;
if (lnval < 0 && errno == EXDEV) {
#if !defined(HAVE_SYMLINK)
fprintf(stderr, "crosspost cant link %s %s",
names[0], names[i]);
perror(" ");
#else
/* Try to make a symbolic link
** to the full pathname.
*/
for (j = 0, p = Dir; (j < SPOOLNAMEBUFF) && *p; )
path[j++] = *p++; /* copy spool dir */
if (j < SPOOLNAMEBUFF) path[j++] = '/';
for (p = names[0]; (j < SPOOLNAMEBUFF) && *p; )
path[j++] = *p++; /* append path */
path[j++] = '\0';
if (symlink(path, names[i]) < 0) {
fprintf(stderr,
"crosspost cant symlink %s %s",
path, names[i]);
perror(" ");
STATSLError++;
}
else
STATSymlink++;
#endif /* !defined(HAVE_SYMLINK) */
} else if (lnval < 0) {
if (lnval == ENOENT)
STATMissing++;
else {
fprintf(stderr, "crosspost cant link %s %s",
names[0], names[i]);
perror(" ");
STATLError++;
}
}
}
} else {
fprintf(stderr, "crosspost bad path %s\n",
names[i]);
STATOError++;
}
} else if (lnval < 0) {
int j;
char path[SPOOLNAMEBUFF+2];
#if !defined(HAVE_SYMLINK)
fprintf(stderr, "crosspost cant link %s %s",
names[0], names[i]);
perror(" ");
#else
/* Try to make a symbolic link
** to the full pathname.
*/
for (j = 0, p = Dir; (j < SPOOLNAMEBUFF) && *p; )
path[j++] = *p++; /* copy spool dir */
if (j < SPOOLNAMEBUFF) path[j++] = '/';
for (p = names[0]; (j < SPOOLNAMEBUFF) && *p; )
path[j++] = *p++; /* append path */
path[j++] = '\0';
if (symlink(path, names[i]) < 0) {
fprintf(stderr,
"crosspost cant symlink %s %s",
path, names[i]);
perror(" ");
STATSLError++;
}
else
STATSymlink++;
#endif /* !defined(HAVE_SYMLINK) */
}
}
if (syncfiles && (fd >= 0)) {
fsync(fd);
close(fd);
}
}
if (QIOerror(qp))
fprintf(stderr, "crosspost cant read %s\n", strerror(errno));
QIOclose(qp);
}
static void
Usage(void)
{
fprintf(stderr, "usage: crosspost [-D dir] [files...]\n");
exit(1);
}
int
main(ac, av)
int ac;
char *av[];
{
int i;
QIOSTATE *qp;
/* Set defaults. */
if (ReadInnConf() < 0) exit(1);
Dir = innconf->patharticles;
umask(NEWSUMASK);
/* Parse JCL. */
while ((i = getopt(ac, av, "D:ds")) != EOF)
switch (i) {
default:
Usage();
/* NOTREACHED */
case 'D':
Dir = optarg; /* specify spool path */
break;
case 'd':
debug = true;
break;
case 's':
syncfiles = false; /* do not fsync articles */
break;
}
ac -= optind;
av += optind;
if (chdir(Dir) < 0) {
fprintf(stderr, "crosspost cant chdir %s %s\n",
Dir, strerror(errno));
exit(1);
}
openlog("crosspost", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
STATTime = time (NULL);
if (ac == 0)
ProcessIncoming(QIOfdopen(STDIN_FILENO));
else {
for ( ; *av; av++)
if (strcmp(*av, "-") == 0)
ProcessIncoming(QIOfdopen(STDIN_FILENO));
else if ((qp = QIOopen(*av)) == NULL)
fprintf(stderr, "crosspost cant open %s %s\n",
*av, strerror(errno));
else
ProcessIncoming(qp);
}
ProcessStats();
exit(0);
/* NOTREACHED */
}
syntax highlighted by Code2HTML, v. 0.9.1