+/*
+ * todo
+ * - actually do something with readable on control master
+ * - option for realsockdir
+ * - manpage: document control master stuff
+ * - manpage: innconf is used for communicating with innd
+ * - debug this:
+ * build-lfs/backends/innduct --no-daemon -f `pwd`/fee sit dom
+ */
+
/*
* Newsfeeds file entries should look like this:
* host.name.of.site[/exclude,exclude,...]\
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
+#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
#include <stdlib.h>
+#include <stddef.h>
#include <glob.h>
#include <time.h>
#include <math.h>
/* when changing defaults, remember to update the manpage */
static const char *sitename, *remote_host;
-static const char *feedfile;
+static const char *feedfile, *realsockdir="/tmp/innduct.control";
static int quiet_multiple=0;
static int become_daemon=1;
static int try_stream=1;
static oop_source *loop;
static ConnList conns;
static ArticleList queue;
-static char *path_lock, *path_flushing, *path_defer, *globpat_backlog;
+static char *path_lock, *path_flushing, *path_defer, *path_control;
+static char *globpat_backlog;
+static pid_t self_pid;
/* statemc_init initialises */
static StateMachineState sms;
static InputFile *main_input_file, *flushing_input_file, *backlog_input_file;
static int sm_period_counter;
+/* control_init initialises */
+static int control_master;
+
/* initialisation to 0 is good */
static int until_connect, until_backlog_nextscan;
static double accept_proportion;
if (become_daemon) {
vsyslog(sysloglevel,fmt,al);
} else {
+ if (self_pid) fprintf(stderr,"[%lu] ",(unsigned long)self_pid);
vfprintf(stderr,fmt,al);
putc('\n',stderr);
}
logwrap(syswarn, " warning", LOG_WARNING, errno);
logwrap(warn, " warning", LOG_WARNING, -1);
-logwrap(notice, "", LOG_NOTICE, -1);
+logwrap(notice, " notice", LOG_NOTICE, -1);
logwrap(info, " info", LOG_INFO, -1);
logwrap(debug, " debug", LOG_DEBUG, -1);
return errnoval==EWOULDBLOCK || errnoval==EAGAIN;
}
+
+/*========== command and control connections ==========*/
+
+#define NOCONTROL(...) do{ \
+ syswarn("no control socket, because failed to " __VA_ARGS__); \
+ goto nocontrol; \
+ }while(0)
+
+static void control_init(void) {
+ char *real=0;
+
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+
+ memset(&sa,0,sizeof(sa));
+ int maxlen= sizeof(sa.un.sun_path);
+
+ int reallen= readlink(path_control, sa.un.sun_path, maxlen);
+ if (reallen<0) {
+ if (errno != ENOENT)
+ NOCONTROL("readlink control socket symlink path %s", path_control);
+ }
+ if (reallen >= maxlen) {
+ debug("control socket symlink path too long (r=%d)",reallen);
+ xunlink(path_control, "old (overlong) control socket symlink");
+ reallen= -1;
+ }
+
+ if (reallen<0) {
+ struct stat stab;
+ int r= lstat(realsockdir,&stab);
+ if (r) {
+ if (errno != ENOENT) NOCONTROL("lstat real socket dir %s", realsockdir);
+
+ r= mkdir(realsockdir, 0700);
+ if (r) NOCONTROL("mkdir real socket dir %s", realsockdir);
+
+ } else {
+ uid_t self= geteuid();
+ if (!S_ISDIR(stab.st_mode) ||
+ stab.st_uid != self ||
+ stab.st_mode & 0077) {
+ warn("no control socket, because real socket directory"
+ " is somehow wrong (ISDIR=%d, uid=%lu (exp.%lu), mode %lo)",
+ !!S_ISDIR(stab.st_mode),
+ (unsigned long)stab.st_uid, (unsigned long)self,
+ (unsigned long)stab.st_mode & 0777UL);
+ goto nocontrol;
+ }
+ }
+
+ real= xasprintf("%s/s%lx.%lx", realsockdir,
+ (unsigned long)xtime(), (unsigned long)self_pid);
+ int reallen= strlen(real);
+
+ if (reallen >= maxlen) {
+ warn("no control socket, because tmpnam gave overly-long path"
+ " %s", real);
+ goto nocontrol;
+ }
+ r= symlink(real, path_control);
+ if (r) NOCONTROL("make control socket path %s a symlink to real"
+ " socket path %s", path_control, real);
+ memcpy(sa.un.sun_path, real, reallen);
+ }
+
+ int r= unlink(sa.un.sun_path);
+ if (r && errno!=ENOENT)
+ NOCONTROL("remove old real socket %s", sa.un.sun_path);
+
+ control_master= socket(PF_UNIX, SOCK_STREAM, 0);
+ if (control_master<0) NOCONTROL("create new control socket");
+
+ sa.un.sun_family= AF_UNIX;
+ int sl= strlen(sa.un.sun_path) + offsetof(struct sockaddr_un, sun_path);
+ r= bind(control_master, &sa.sa, sl);
+ if (r) NOCONTROL("bind to real socket path %s", sa.un.sun_path);
+
+ r= listen(control_master, 5);
+ if (r) NOCONTROL("listen");
+
+ //loop->on_fd(loop, control_master, OOP_READ, control_master_readable, 0);
+ info("control socket ok, real path %s", sa.un.sun_path);
+
+ return;
+
+ nocontrol:
+ free(real);
+ xclose_perhaps(&control_master, "control master",0);
+ return;
+}
+
/*========== management of connections ==========*/
static void conn_closefd(Conn *conn, const char *msgprefix) {
inputfile_reading_start(f);
}
-static void statemc_init(void) {
- struct stat stab, stabf;
-
- path_lock= xasprintf("%s_lock", feedfile);
- path_flushing= xasprintf("%s_flushing", feedfile);
- path_defer= xasprintf("%s_defer", feedfile);
- globpat_backlog= xasprintf("%s_backlog*", feedfile);
-
+static void statemc_lock(void) {
int lockfd;
+ struct stat stab, stabf;
for (;;) {
lockfd= open(path_lock, O_CREAT|O_RDWR, 0600);
xclose(lockfd, "stale lockfile ", path_lock);
}
- pid_t self= getpid();
- if (self==-1) sysdie("getpid");
FILE *lockfile= fdopen(lockfd, "w");
if (!lockfile) sysdie("fdopen lockfile");
if (r) sysdie("truncate lockfile to write new info");
if (fprintf(lockfile, "pid %ld\nsite %s\nfeedfile %s\nfqdn %s\n",
- (unsigned long)self, sitename, feedfile, remote_host) == EOF ||
+ (unsigned long)self_pid,
+ sitename, feedfile, remote_host) == EOF ||
fflush(lockfile))
sysfatal("write info to lockfile %s", path_lock);
debug("startup: locked");
+}
+
+static void statemc_init(void) {
+ struct stat stabdefer;
search_backlog_file();
int defer_noent;
- xlstat_isreg(path_defer, &stab, &defer_noent, "defer file");
+ xlstat_isreg(path_defer, &stabdefer, &defer_noent, "defer file");
if (defer_noent) {
debug("startup: ductdefer ENOENT");
} else {
- debug("startup: ductdefer nlink=%ld", (long)stab.st_nlink);
- switch (stab.st_nlink==1) {
+ debug("startup: ductdefer nlink=%ld", (long)stabdefer.st_nlink);
+ switch (stabdefer.st_nlink==1) {
case 1:
open_defer(); /* so that we will later close it and rename it */
break;
break;
default:
die("defer file %s has unexpected link count %d",
- path_defer, stab.st_nlink);
+ path_defer, stabdefer.st_nlink);
}
}
}));
static char *debug_report_ipf(InputFile *ipf) {
- if (!ipf) return xasprintf("-");
+ if (!ipf) return xasprintf("none");
const char *slash= strrchr(ipf->path,'/');
const char *path= slash ? slash+1 : ipf->path;
debug("PERIOD"
" sms=%s[%d] conns=%d queue=%d until_connect=%d"
" input_files main:%s old:%s flushing:%s"
- " children connecting=%ld inndcomm_child=%ld"
+ " children connecting=%ld inndcomm=%ld"
,
sms_names[sms], sm_period_counter,
conns.count, queue.count, until_connect,
/* set things up */
+ path_lock= xasprintf("%s_lock", feedfile);
+ path_flushing= xasprintf("%s_flushing", feedfile);
+ path_defer= xasprintf("%s_defer", feedfile);
+ path_control= xasprintf("%s_control", feedfile);
+ globpat_backlog= xasprintf("%s_backlog*", feedfile);
+
oop_source_sys *sysloop= oop_sys_new();
if (!sysloop) sysdie("could not create liboop event loop");
loop= (oop_source*)sysloop;
if (child2) _exit(0);
}
+ self_pid= getpid();
+ if (self_pid==-1) sysdie("getpid");
+
+ statemc_lock();
+
notice("starting");
+ control_init();
+
if (!filemon_method_init()) {
warn("no file monitoring available, polling");
filepoll_schedule();