-/*
- * Copyright (c) 1990,1993 Regents of The University of Michigan.
- * All Rights Reserved. See COPYRIGHT.
- */
-
+/*
+ Copyright (c) 2012 Frank Lahm <franklahm@gmail.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include <atalk/errchk.h>
#include <atalk/globals.h>
#include <atalk/netatalk_conf.h>
+#include <atalk/bstrlib.h>
+#include <atalk/bstradd.h>
#include <event2/event.h>
+/* how many seconds we wait to shutdown from SIGTERM before we send SIGKILL */
+#define KILL_GRACETIME 5
+
/* forward declaration */
static pid_t run_process(const char *path, ...);
+static void kill_childs(int sig, ...);
/* static variables */
static AFPObj obj;
-static sig_atomic_t got_chldsig;
-static pid_t afpd_pid, cnid_metad_pid;
-static uint afpd_restarts, cnid_metad_restarts;
+static pid_t afpd_pid = -1, cnid_metad_pid = -1, dbus_pid = -1, trackerd_pid = -1;
+static uint afpd_restarts, cnid_metad_restarts, dbus_restarts, trackerd_restarts;
static struct event_base *base;
+struct event *sigterm_ev, *sigquit_ev, *sigchld_ev, *timer_ev;
+static int in_shutdown;
+static const char *dbus_path;
+static char *trackerd_loglev;
-/* event functions */
+/******************************************************************
+ * Misc stuff
+ ******************************************************************/
+
+/* Set Tracker Miners to index all our volumes */
+static int set_sl_volumes(void)
+{
+ EC_INIT;
+ const struct vol *volumes, *vol;
+ struct bstrList *vollist = bstrListCreate();
+ bstring sep = bfromcstr(", ");
+ bstring volnamelist = NULL, cmd = NULL;
+
+ EC_NULL_LOG( volumes = getvolumes() );
+
+ for (vol = volumes; vol; vol = vol->v_next) {
+ bstring volnamequot = bformat("'%s'", vol->v_path);
+ bstrListPush(vollist, volnamequot);
+ }
+
+ volnamelist = bjoin(vollist, sep);
+ cmd = bformat("gsettings set org.freedesktop.Tracker.Miner.Files index-recursive-directories \"[%s]\"", bdata(volnamelist));
+ LOG(log_debug, logtype_sl, "set_sl_volumes: %s", bdata(cmd));
+ system(bdata(cmd));
+
+EC_CLEANUP:
+ if (cmd)
+ bdestroy(cmd);
+ if (sep)
+ bdestroy(sep);
+ if (vollist)
+ bstrListDestroy(vollist);
+ if (volnamelist)
+ bdestroy(volnamelist);
+ EC_EXIT;
+}
+
+/******************************************************************
+ * libevent helper functions
+ ******************************************************************/
+
+/* libevent logging callback */
static void libevent_logmsg_cb(int severity, const char *msg)
{
switch (severity) {
}
}
+/******************************************************************
+ * libevent event callbacks
+ ******************************************************************/
+
+/* SIGTERM callback */
static void sigterm_cb(evutil_socket_t fd, short what, void *arg)
{
- LOG(log_note, logtype_afpd, "Exiting on SIGTERM");
- kill(afpd_pid, SIGTERM);
- kill(cnid_metad_pid, SIGTERM);
- server_unlock(_PATH_NETATALK_LOCK);
- event_base_loopbreak(base);
+ sigset_t sigs;
+ struct timeval tv;
+
+ LOG(log_info, logtype_afpd, "Exiting on SIGTERM");
+
+ if (in_shutdown)
+ return;
+ in_shutdown = 1;
+
+ /* block any signal but SIGCHLD */
+ sigfillset(&sigs);
+ sigdelset(&sigs, SIGCHLD);
+ sigprocmask(SIG_SETMASK, &sigs, NULL);
+
+ /* add 10 sec timeout timer, remove all events but SIGCHLD */
+ tv.tv_sec = KILL_GRACETIME;
+ tv.tv_usec = 0;
+ event_base_loopexit(base, &tv);
+ event_del(sigterm_ev);
+ event_del(sigquit_ev);
+ event_del(timer_ev);
+
+#ifdef HAVE_TRACKER_SPARQL
+ system("tracker-control -t");
+#endif
+ kill_childs(SIGTERM, &afpd_pid, &cnid_metad_pid, &dbus_pid, &trackerd_pid, NULL);
}
+/* SIGQUIT callback */
static void sigquit_cb(evutil_socket_t fd, short what, void *arg)
{
LOG(log_note, logtype_afpd, "Exiting on SIGQUIT");
- kill(afpd_pid, SIGTERM);
- kill(cnid_metad_pid, SIGTERM);
- server_unlock(_PATH_NETATALK_LOCK);
- event_base_loopbreak(base);
+#ifdef HAVE_TRACKER_SPARQL
+ system("tracker-control -t");
+#endif
+ kill_childs(SIGQUIT, &afpd_pid, &cnid_metad_pid, &dbus_pid, &trackerd_pid, NULL);
+}
+
+/* SIGHUP callback */
+static void sighup_cb(evutil_socket_t fd, short what, void *arg)
+{
+ LOG(log_note, logtype_afpd, "Received SIGHUP, sending all processes signal to reload config");
+ kill_childs(SIGHUP, &afpd_pid, &cnid_metad_pid, &trackerd_pid, NULL);
}
+/* SIGCHLD callback */
static void sigchld_cb(evutil_socket_t fd, short what, void *arg)
{
- int status, i;
+ int status;
pid_t pid;
- LOG(log_note, logtype_afpd, "Got SIGCHLD event");
-
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (WIFEXITED(status)) {
if (WEXITSTATUS(status))
- LOG(log_info, logtype_afpd, "child[%d]: exited %d", pid, WEXITSTATUS(status));
+ LOG(log_info, logtype_default, "child[%d]: exited %d", pid, WEXITSTATUS(status));
else
- LOG(log_info, logtype_afpd, "child[%d]: done", pid);
+ LOG(log_info, logtype_default, "child[%d]: done", pid);
} else {
if (WIFSIGNALED(status))
- LOG(log_info, logtype_afpd, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
+ LOG(log_info, logtype_default, "child[%d]: killed by signal %d", pid, WTERMSIG(status));
else
- LOG(log_info, logtype_afpd, "child[%d]: died", pid);
+ LOG(log_info, logtype_default, "child[%d]: died", pid);
}
- if (pid == afpd_pid) {
- sleep(1);
- afpd_restarts++;
- LOG(log_note, logtype_afpd, "Restarting 'afpd' (restart: %u)", afpd_restarts);
- if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
- LOG(log_error, logtype_afpd, "Error starting 'afpd'");
- }
- } else if (pid = cnid_metad_pid) {
- sleep(1);
- cnid_metad_restarts++;
- LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restart: %u)", cnid_metad_restarts);
- if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
- LOG(log_error, logtype_afpd, "Error starting 'cnid_metad'");
- }
- } else {
+ if (pid == afpd_pid)
+ afpd_pid = -1;
+ else if (pid == cnid_metad_pid)
+ cnid_metad_pid = -1;
+ else if (pid == dbus_pid)
+ dbus_pid = -1;
+ else if (pid == trackerd_pid)
+ trackerd_pid = -1;
+ else
LOG(log_error, logtype_afpd, "Bad pid: %d", pid);
+ }
+
+ if (in_shutdown && afpd_pid == -1 && cnid_metad_pid == -1 && dbus_pid == -1 && trackerd_pid == -1)
+ event_base_loopbreak(base);
+}
+
+/* timer callback */
+static void timer_cb(evutil_socket_t fd, short what, void *arg)
+{
+ if (in_shutdown)
+ return;
+
+ if (afpd_pid == -1) {
+ afpd_restarts++;
+ LOG(log_note, logtype_afpd, "Restarting 'afpd' (restarts: %u)", afpd_restarts);
+ if ((afpd_pid = run_process(_PATH_AFPD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
+ LOG(log_error, logtype_default, "Error starting 'afpd'");
+ }
+ }
+
+ if (cnid_metad_pid == -1) {
+ cnid_metad_restarts++;
+ LOG(log_note, logtype_afpd, "Restarting 'cnid_metad' (restarts: %u)", cnid_metad_restarts);
+ if ((cnid_metad_pid = run_process(_PATH_CNID_METAD, "-d", "-F", obj.options.configfile, NULL)) == -1) {
+ LOG(log_error, logtype_default, "Error starting 'cnid_metad'");
}
}
+
+ if (dbus_pid == -1) {
+ dbus_restarts++;
+ LOG(log_note, logtype_afpd, "Restarting 'dbus' (restarts: %u)", dbus_restarts);
+ if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus.session.conf", NULL)) == -1) {
+ LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
+ }
+ }
+
+#ifdef HAVE_TRACKER_RDF
+ if (trackerd_pid == -1) {
+ trackerd_restarts++;
+ LOG(log_note, logtype_afpd, "Restarting 'trackerd' (restarts: %u)", trackerd_restarts);
+ if ((trackerd_pid = run_process(TRACKER_RDF_PREFIX "/bin/trackerd", trackerd_loglev, NULL)) == -1) {
+ LOG(log_error, logtype_default, "Error starting '%s'", "/usr/bin/trackerd");
+ }
+ }
+#endif
}
-/* other functions */
+/******************************************************************
+ * helper functions
+ ******************************************************************/
+
+/* kill processes passed as varargs of type "pid_t *", terminate list with NULL */
+static void kill_childs(int sig, ...)
+{
+ va_list args;
+ pid_t *pid;
+ va_start(args, sig);
+
+ while ((pid = va_arg(args, pid_t *)) != NULL) {
+ if (*pid == -1)
+ continue;
+ kill(*pid, sig);
+ }
+ va_end(args);
+}
+
+/* this get called when error conditions are met that require us to exit gracefully */
static void netatalk_exit(int ret)
{
- server_unlock(_PATH_NETATALK_LOCK);
+ server_unlock(PATH_NETATALK_LOCK);
exit(ret);
}
+/* this forks() and exec() "path" with varags as argc[] */
static pid_t run_process(const char *path, ...)
{
int ret, i = 0;
- char *myargv[10];
+#define MYARVSIZE 64
+ char *myargv[MYARVSIZE];
va_list args;
pid_t pid;
if (pid == 0) {
myargv[i++] = (char *)path;
va_start(args, path);
- while ((myargv[i++] = va_arg(args, char *)) != NULL)
- ;
+ while (i < MYARVSIZE) {
+ if ((myargv[i++] = va_arg(args, char *)) == NULL)
+ break;
+ }
va_end(args);
ret = execv(path, myargv);
int main(int argc, char **argv)
{
- const char *configfile = NULL;
int c, ret, debug = 0;
sigset_t blocksigs;
+ struct timeval tv;
/* Log SIGBUS/SIGSEGV SBT */
fault_setup(NULL);
}
}
- if (check_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
+ if (check_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
exit(EXITERR_SYS);
if (!debug && daemonize(0, 0) != 0)
exit(EXITERR_SYS);
- if (create_lockfile("netatalk", _PATH_NETATALK_LOCK) != 0)
+ if (create_lockfile("netatalk", PATH_NETATALK_LOCK) != 0)
exit(EXITERR_SYS);
sigfillset(&blocksigs);
sigprocmask(SIG_SETMASK, &blocksigs, NULL);
- if (afp_config_parse(&obj) != 0)
+ if (afp_config_parse(&obj, "netatalk") != 0)
netatalk_exit(EXITERR_CONF);
- set_processname("netatalk");
- setuplog(obj.options.logconfig, obj.options.logfile);
+ load_volumes(&obj);
+
event_set_log_callback(libevent_logmsg_cb);
event_set_fatal_callback(netatalk_exit);
netatalk_exit(EXITERR_CONF);
}
- struct event *sigterm_ev, *sigquit_ev, *sigchld_ev;
- sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL|EV_PERSIST, sigterm_cb, NULL);
- sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL|EV_PERSIST, sigquit_cb, NULL);
- sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL|EV_PERSIST, sigchld_cb, NULL);
+ sigterm_ev = event_new(base, SIGTERM, EV_SIGNAL, sigterm_cb, NULL);
+ sigquit_ev = event_new(base, SIGQUIT, EV_SIGNAL | EV_PERSIST, sigquit_cb, NULL);
+ sigquit_ev = event_new(base, SIGHUP, EV_SIGNAL | EV_PERSIST, sighup_cb, NULL);
+ sigchld_ev = event_new(base, SIGCHLD, EV_SIGNAL | EV_PERSIST, sigchld_cb, NULL);
+ timer_ev = event_new(base, -1, EV_PERSIST, timer_cb, NULL);
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
event_add(sigterm_ev, NULL);
event_add(sigquit_ev, NULL);
event_add(sigchld_ev, NULL);
+ event_add(timer_ev, &tv);
sigfillset(&blocksigs);
sigdelset(&blocksigs, SIGTERM);
sigdelset(&blocksigs, SIGQUIT);
sigdelset(&blocksigs, SIGCHLD);
+ sigdelset(&blocksigs, SIGHUP);
sigprocmask(SIG_SETMASK, &blocksigs, NULL);
+#ifdef HAVE_TRACKER
+ setenv("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/spotlight.ipc", 1);
+ setenv("XDG_DATA_HOME", _PATH_STATEDIR, 0);
+ setenv("XDG_CACHE_HOME", _PATH_STATEDIR, 0);
+ setenv("XDG_CONFIG_HOME", _PATH_CONFDIR, 0);
+#endif
+
+#ifdef HAVE_TRACKER_RDF
+ /* This assumes Tracker 0.6 with RDF is only used on Solaris and derived platforms */
+ dbus_path = iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon path", "/usr/lib/dbus-daemon");
+#else
+ dbus_path = iniparser_getstring(obj.iniconfig, INISEC_GLOBAL, "dbus daemon path", "/bin/dbus-daemon");
+#endif
+ LOG(log_debug, logtype_default, "DBUS: '%s'", dbus_path);
+ if ((dbus_pid = run_process(dbus_path, "--config-file=" _PATH_CONFDIR "dbus-session.conf", NULL)) == -1) {
+ LOG(log_error, logtype_default, "Error starting '%s'", dbus_path);
+ netatalk_exit(EXITERR_CONF);
+ }
+
+ /* Allow dbus some time to start up */
+ sleep(1);
+
+#ifdef HAVE_TRACKER_SPARQL
+ set_sl_volumes();
+ system(TRACKER_PREFIX "/bin/tracker-control -s");
+#endif
+#ifdef HAVE_TRACKER_RDF
+ if (asprintf(&trackerd_loglev, "--verbosity=%d", obj.options.tracker_loglevel) == -1)
+ netatalk_exit(EXITERR_CONF);
+ if ((trackerd_pid = run_process(TRACKER_RDF_PREFIX "/bin/trackerd", trackerd_loglev, NULL)) == -1) {
+ LOG(log_error, logtype_default, "Error starting '%s'", TRACKER_RDF_PREFIX "/bin/trackerd");
+ netatalk_exit(EXITERR_CONF);
+ }
+#endif
+
/* run the event loop */
ret = event_base_dispatch(base);
- return ret;
+ if (afpd_pid != -1 || cnid_metad_pid != -1 || dbus_pid != -1 || trackerd_pid != -1) {
+ if (afpd_pid != -1)
+ LOG(log_error, logtype_afpd, "AFP service did not shutdown, killing it");
+ if (cnid_metad_pid != -1)
+ LOG(log_error, logtype_afpd, "CNID database service did not shutdown, killing it");
+ if (dbus_pid != -1)
+ LOG(log_error, logtype_afpd, "DBUS session daemon still running, killing it");
+ if (trackerd_pid != -1)
+ LOG(log_error, logtype_afpd, "trackerd still running, killing it");
+ kill_childs(SIGKILL, &afpd_pid, &cnid_metad_pid, &dbus_pid, &trackerd_pid, NULL);
+ }
+
+ LOG(log_note, logtype_afpd, "Netatalk AFP server exiting");
+
+ netatalk_exit(ret);
}