summaryrefslogtreecommitdiffstats
path: root/src/daemon/abrtd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/daemon/abrtd.c')
-rw-r--r--src/daemon/abrtd.c709
1 files changed, 709 insertions, 0 deletions
diff --git a/src/daemon/abrtd.c b/src/daemon/abrtd.c
new file mode 100644
index 00000000..efc8d056
--- /dev/null
+++ b/src/daemon/abrtd.c
@@ -0,0 +1,709 @@
+/*
+ Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
+ Copyright (C) 2009 RedHat inc.
+
+ 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.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#include <sys/un.h>
+#include <syslog.h>
+#include <sys/inotify.h>
+#include <sys/ioctl.h> /* ioctl(FIONREAD) */
+#include "abrtlib.h"
+#include "comm_layer_inner.h"
+#include "abrt_conf.h"
+#include "CommLayerServerDBus.h"
+#include "MiddleWare.h"
+#include "parse_options.h"
+
+#define PROGNAME "abrtd"
+
+#define VAR_RUN_PIDFILE VAR_RUN"/abrtd.pid"
+
+#define SOCKET_FILE VAR_RUN"/abrt/abrt.socket"
+#define SOCKET_PERMISSION 0666
+/* Maximum number of simultaneously opened client connections. */
+#define MAX_CLIENT_COUNT 10
+
+
+/* Daemon initializes, then sits in glib main loop, waiting for events.
+ * Events can be:
+ * - inotify: something new appeared under /var/spool/abrt
+ * - DBus: dbus message arrived
+ * - signal: we got SIGTERM or SIGINT
+ *
+ * DBus methods we have:
+ * - DeleteDebugDump(crash_id): delete it from DB and delete corresponding /var/spool/abrt/DIR
+ *
+ * DBus signals we emit:
+ * - Crash(progname, crash_id, dir, uid) - a new crash occurred (new /var/spool/abrt/DIR is found)
+ * - Warning(msg)
+ * - Update(msg)
+ * Both are sent as unicast to last client set by set_client_name(name).
+ * If set_client_name(NULL) was done, they are not sent.
+ */
+static volatile sig_atomic_t s_sig_caught;
+static int s_signal_pipe[2];
+static int s_signal_pipe_write = -1;
+static int s_upload_watch = -1;
+static unsigned s_timeout;
+static bool s_exiting;
+
+static GIOChannel *socket_channel = NULL;
+static guint socket_channel_cb_id = 0;
+static int socket_client_count = 0;
+
+
+/* Helpers */
+
+static guint add_watch_or_die(GIOChannel *channel, unsigned condition, GIOFunc func)
+{
+ errno = 0;
+ guint r = g_io_add_watch(channel, (GIOCondition)condition, func, NULL);
+ if (!r)
+ perror_msg_and_die("g_io_add_watch failed");
+ return r;
+}
+
+
+/* Socket handling */
+
+/* Callback called by glib main loop when a client connects to ABRT's socket. */
+static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpointer ptr_unused)
+{
+ /* Check the limit for number of simultaneously attached clients. */
+ if (socket_client_count >= MAX_CLIENT_COUNT)
+ {
+ error_msg("Too many clients, refusing connections to '%s'", SOCKET_FILE);
+ /* To avoid infinite loop caused by the descriptor in "ready" state,
+ * the callback must be disabled.
+ * It is added back in client_free(). */
+ g_source_remove(socket_channel_cb_id);
+ socket_channel_cb_id = 0;
+ return TRUE;
+ }
+
+ int socket = accept(g_io_channel_unix_get_fd(source), NULL, NULL);
+ if (socket == -1)
+ {
+ perror_msg("accept");
+ return TRUE;
+ }
+
+ log("New client connected");
+ pid_t pid = fork();
+ if (pid < 0)
+ {
+ perror_msg("fork");
+ close(socket);
+ return TRUE;
+ }
+ if (pid == 0) /* child */
+ {
+ xmove_fd(socket, 0);
+ xdup2(0, 1);
+
+ char *argv[3]; /* abrt-server [-s] NULL */
+ char **pp = argv;
+ *pp++ = (char*)"abrt-server";
+ if (logmode & LOGMODE_SYSLOG)
+ *pp++ = (char*)"-s";
+ *pp = NULL;
+
+ execvp(argv[0], argv);
+ perror_msg_and_die("Can't execute '%s'", argv[0]);
+ }
+ /* parent */
+ socket_client_count++;
+ close(socket);
+ return TRUE;
+}
+
+/* Initializes the dump socket, usually in /var/run directory
+ * (the path depends on compile-time configuration).
+ */
+static void dumpsocket_init()
+{
+ unlink(SOCKET_FILE); /* not caring about the result */
+
+ int socketfd = xsocket(AF_UNIX, SOCK_STREAM, 0);
+ close_on_exec_on(socketfd);
+
+ struct sockaddr_un local;
+ memset(&local, 0, sizeof(local));
+ local.sun_family = AF_UNIX;
+ strcpy(local.sun_path, SOCKET_FILE);
+ xbind(socketfd, (struct sockaddr*)&local, sizeof(local));
+ xlisten(socketfd, MAX_CLIENT_COUNT);
+
+ if (chmod(SOCKET_FILE, SOCKET_PERMISSION) != 0)
+ perror_msg_and_die("chmod '%s'", SOCKET_FILE);
+
+ socket_channel = g_io_channel_unix_new(socketfd);
+ g_io_channel_set_close_on_unref(socket_channel, TRUE);
+ socket_channel_cb_id = add_watch_or_die(socket_channel, G_IO_IN | G_IO_PRI, server_socket_cb);
+}
+
+/* Releases all resources used by dumpsocket. */
+static void dumpsocket_shutdown()
+{
+ /* Set everything to pre-initialization state. */
+ if (socket_channel)
+ {
+ /* Undo add_watch_or_die */
+ g_source_remove(socket_channel_cb_id);
+ /* Undo g_io_channel_unix_new */
+ g_io_channel_unref(socket_channel);
+ socket_channel = NULL;
+ }
+}
+
+static int create_pidfile()
+{
+ /* Note:
+ * No O_EXCL: we would happily overwrite stale pidfile from previous boot.
+ * No O_TRUNC: we must first try to lock the file, and if lock fails,
+ * there is another live abrtd. O_TRUNCing the file in this case
+ * would be wrong - it'll erase the pid to empty string!
+ */
+ int fd = open(VAR_RUN_PIDFILE, O_WRONLY|O_CREAT, 0644);
+ if (fd >= 0)
+ {
+ if (lockf(fd, F_TLOCK, 0) < 0)
+ {
+ perror_msg("Can't lock file '%s'", VAR_RUN_PIDFILE);
+ return -1;
+ }
+ close_on_exec_on(fd);
+ /* write our pid to it */
+ char buf[sizeof(long)*3 + 2];
+ int len = sprintf(buf, "%lu\n", (long)getpid());
+ write(fd, buf, len);
+ ftruncate(fd, len);
+ /* we leak opened+locked fd intentionally */
+ return 0;
+ }
+
+ perror_msg("Can't open '%s'", VAR_RUN_PIDFILE);
+ return -1;
+}
+
+static void handle_signal(int signo)
+{
+ int save_errno = errno;
+
+ // Enable for debugging only, malloc/printf are unsafe in signal handlers
+ //VERB3 log("Got signal %d", signo);
+
+ uint8_t l_sig_caught;
+ s_sig_caught = l_sig_caught = signo;
+ /* Using local copy of s_sig_caught so that concurrent signal
+ * won't change it under us */
+ if (s_signal_pipe_write >= 0)
+ write(s_signal_pipe_write, &l_sig_caught, 1);
+
+ errno = save_errno;
+}
+
+/* Signal pipe handler */
+static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr_unused)
+{
+ char signo;
+ gsize len = 0;
+ g_io_channel_read(gio, &signo, 1, &len);
+ if (len == 1)
+ {
+ /* we did receive a signal */
+ VERB3 log("Got signal %d through signal pipe", signo);
+ if (signo != SIGCHLD)
+ s_exiting = 1;
+ else
+ {
+ pid_t pid;
+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0)
+ {
+ if (socket_client_count)
+ socket_client_count--;
+ if (!socket_channel_cb_id)
+ {
+ log("Accepting connections on '%s'", SOCKET_FILE);
+ socket_channel_cb_id = add_watch_or_die(socket_channel, G_IO_IN | G_IO_PRI, server_socket_cb);
+ }
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Inotify handler */
+static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr_unused)
+{
+ /* Default size: 128 simultaneous actions (about 1/2 meg) */
+#define INOTIFY_BUF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX)*128)
+ /* Determine how much to read (it usually is much smaller) */
+ /* NB: this variable _must_ be int-sized, ioctl expects that! */
+ int inotify_bytes = INOTIFY_BUF_SIZE;
+ if (ioctl(g_io_channel_unix_get_fd(gio), FIONREAD, &inotify_bytes) != 0
+ || inotify_bytes < sizeof(struct inotify_event)
+ || inotify_bytes > INOTIFY_BUF_SIZE
+ ) {
+ inotify_bytes = INOTIFY_BUF_SIZE;
+ }
+ VERB3 log("FIONREAD:%d", inotify_bytes);
+
+ char *buf = (char*)xmalloc(inotify_bytes);
+ errno = 0;
+ gsize len;
+ GIOError err = g_io_channel_read(gio, buf, inotify_bytes, &len);
+ if (err != G_IO_ERROR_NONE)
+ {
+ perror_msg("Error reading inotify fd");
+ free(buf);
+ return FALSE;
+ }
+
+ /* Reconstruct each event and send message to the dbus */
+ gsize i = 0;
+ while (i < len)
+ {
+ struct inotify_event *event = (struct inotify_event *) &buf[i];
+ const char *name = NULL;
+ if (event->len)
+ name = event->name;
+ //log("i:%d len:%d event->mask:%x IN_ISDIR:%x IN_CLOSE_WRITE:%x event->len:%d",
+ // i, len, event->mask, IN_ISDIR, IN_CLOSE_WRITE, event->len);
+ i += sizeof(*event) + event->len;
+
+ if (event->wd == s_upload_watch)
+ {
+ /* Was the (presumable newly created) file closed in upload dir,
+ * or a file moved to upload dir? */
+ if (!(event->mask & IN_ISDIR)
+ && event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO)
+ && name
+ ) {
+ const char *ext = strrchr(name, '.');
+ if (ext && strcmp(ext + 1, "working") == 0)
+ continue;
+
+ const char *dir = g_settings_sWatchCrashdumpArchiveDir;
+ log("Detected creation of file '%s' in upload directory '%s'", name, dir);
+ if (fork() == 0)
+ {
+ xchdir(dir);
+ execlp("abrt-handle-upload", "abrt-handle-upload", DEBUG_DUMPS_DIR, dir, name, (char*)NULL);
+ error_msg_and_die("Can't execute '%s'", "abrt-handle-upload");
+ }
+ }
+ continue;
+ }
+
+ if (!(event->mask & IN_ISDIR) || !name)
+ {
+ /* ignore lock files and such */
+ // Happens all the time during normal run
+ //VERB3 log("File '%s' creation detected, ignoring", name);
+ continue;
+ }
+ if (strcmp(strchrnul(name, '.'), ".new") == 0)
+ {
+ //VERB3 log("Directory '%s' creation detected, ignoring", name);
+ continue;
+ }
+ log("Directory '%s' creation detected", name);
+
+ if (g_settings_nMaxCrashReportsSize > 0)
+ {
+ char *worst_dir = NULL;
+ while (g_settings_nMaxCrashReportsSize > 0
+ && get_dirsize_find_largest_dir(DEBUG_DUMPS_DIR, &worst_dir, name) / (1024*1024) >= g_settings_nMaxCrashReportsSize
+ && worst_dir
+ ) {
+ log("Size of '%s' >= %u MB, deleting '%s'", DEBUG_DUMPS_DIR, g_settings_nMaxCrashReportsSize, worst_dir);
+ send_dbus_sig_QuotaExceeded(_("The size of the report exceeded the quota. Please check system's MaxCrashReportsSize value in abrt.conf."));
+ /* deletes both directory and DB record */
+ char *d = concat_path_file(DEBUG_DUMPS_DIR, worst_dir);
+ free(worst_dir);
+ worst_dir = NULL;
+ delete_dump_dir(d);
+ free(d);
+ }
+ }
+
+ char *fullname = NULL;
+ crash_data_t *crash_data = NULL;
+ fullname = concat_path_file(DEBUG_DUMPS_DIR, name);
+ mw_result_t res = LoadDebugDump(fullname, &crash_data);
+ const char *first = crash_data ? get_crash_item_content_or_NULL(crash_data, CD_DUMPDIR) : NULL;
+ switch (res)
+ {
+ case MW_OK:
+ log("New dump directory %s, processing", fullname);
+ /* Fall through */
+
+ case MW_OCCURRED: /* dup */
+ {
+ if (res != MW_OK)
+ {
+ log("Deleting dump directory %s (dup of %s), sending dbus signal",
+ strrchr(fullname, '/') + 1,
+ strrchr(first, '/') + 1);
+ delete_dump_dir(fullname);
+ }
+
+ const char *uid_str = get_crash_item_content_or_NULL(crash_data, FILENAME_UID);
+ /* When dup occurs we need to return first occurence,
+ * not the one which is deleted
+ */
+ send_dbus_sig_Crash(get_crash_item_content_or_NULL(crash_data, FILENAME_PACKAGE),
+ (first) ? first : fullname,
+ uid_str
+ );
+ break;
+ }
+ case MW_CORRUPTED:
+ case MW_GPG_ERROR:
+ default:
+ log("Corrupted or bad dump %s (res:%d), deleting", fullname, (int)res);
+ delete_dump_dir(fullname);
+ break;
+ }
+ free(fullname);
+ free_crash_data(crash_data);
+ } /* while */
+
+ free(buf);
+ return TRUE;
+}
+
+/* Run main loop with idle timeout.
+ * Basically, almost like glib's g_main_run(loop)
+ */
+static void run_main_loop(GMainLoop* loop)
+{
+ GMainContext *context = g_main_loop_get_context(loop);
+ int fds_size = 0;
+ GPollFD *fds = NULL;
+
+ while (!s_exiting)
+ {
+ gboolean some_ready;
+ gint max_priority;
+ gint timeout;
+ gint nfds;
+
+ some_ready = g_main_context_prepare(context, &max_priority);
+ if (some_ready)
+ g_main_context_dispatch(context);
+
+ while (1)
+ {
+ nfds = g_main_context_query(context, max_priority, &timeout, fds, fds_size);
+ if (nfds <= fds_size)
+ break;
+ fds_size = nfds + 16; /* +16: optimizing realloc frequency */
+ fds = (GPollFD *)xrealloc(fds, fds_size * sizeof(fds[0]));
+ }
+
+ if (s_timeout != 0)
+ alarm(s_timeout);
+ g_poll(fds, nfds, timeout);
+ if (s_timeout != 0)
+ alarm(0);
+
+ some_ready = g_main_context_check(context, max_priority, fds, nfds);
+ if (some_ready)
+ g_main_context_dispatch(context);
+ }
+
+ free(fds);
+ g_main_context_unref(context);
+}
+
+static void start_syslog_logging()
+{
+ /* Open stdin to /dev/null */
+ xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
+ /* We must not leave fds 0,1,2 closed.
+ * Otherwise fprintf(stderr) dumps messages into random fds, etc. */
+ xdup2(STDIN_FILENO, STDOUT_FILENO);
+ xdup2(STDIN_FILENO, STDERR_FILENO);
+ openlog(PROGNAME, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ putenv((char*)"ABRT_SYSLOG=1");
+}
+
+static void ensure_writable_dir(const char *dir, mode_t mode, const char *user)
+{
+ struct stat sb;
+
+ if (mkdir(dir, mode) != 0 && errno != EEXIST)
+ perror_msg_and_die("Can't create '%s'", dir);
+ if (stat(dir, &sb) != 0 || !S_ISDIR(sb.st_mode))
+ error_msg_and_die("'%s' is not a directory", dir);
+
+ struct passwd *pw = getpwnam(user);
+ if (!pw)
+ perror_msg_and_die("Can't find user '%s'", user);
+
+ if ((sb.st_uid != pw->pw_uid || sb.st_gid != pw->pw_gid) && chown(dir, pw->pw_uid, pw->pw_gid) != 0)
+ perror_msg_and_die("Can't set owner %u:%u on '%s'", (unsigned int)pw->pw_uid, (unsigned int)pw->pw_gid, dir);
+ if ((sb.st_mode & 07777) != mode && chmod(dir, mode) != 0)
+ perror_msg_and_die("Can't set mode %o on '%s'", mode, dir);
+}
+
+static void sanitize_dump_dir_rights()
+{
+ /* We can't allow everyone to create dumps: otherwise users can flood
+ * us with thousands of bogus or malicious dumps */
+ /* 07000 bits are setuid, setgit, and sticky, and they must be unset */
+ /* 00777 bits are usual "rwxrwxrwx" access rights */
+ ensure_writable_dir(DEBUG_DUMPS_DIR, 0755, "abrt");
+ /* debuginfo cache */
+ ensure_writable_dir(DEBUG_INFO_DIR, 0775, "abrt");
+ /* temp dir */
+ ensure_writable_dir(VAR_RUN"/abrt", 0755, "root");
+}
+
+int main(int argc, char** argv)
+{
+ int parent_pid = getpid();
+
+ setlocale(LC_ALL, "");
+
+#if ENABLE_NLS
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ if (getuid() != 0)
+ error_msg_and_die("ABRT daemon must be run as root");
+
+ char *env_verbose = getenv("ABRT_VERBOSE");
+ if (env_verbose)
+ g_verbose = atoi(env_verbose);
+
+ const char *program_usage_string = _(
+ PROGNAME" [options]"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_d = 1 << 1,
+ OPT_s = 1 << 2,
+ OPT_t = 1 << 3,
+ OPT_p = 1 << 4,
+ };
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL( 'd', NULL, NULL , _("Do not daemonize")),
+ OPT_BOOL( 's', NULL, NULL , _("Log to syslog even with -d")),
+ OPT_INTEGER('t', NULL, &s_timeout, _("Exit after SEC seconds of inactivity")),
+ OPT_BOOL( 'p', NULL, NULL , _("Add program names to log")),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+
+ /* When dbus daemon starts us, it doesn't set PATH
+ * (I saw it set only DBUS_STARTER_ADDRESS and DBUS_STARTER_BUS_TYPE).
+ * In this case, set something sane:
+ */
+ const char *env_path = getenv("PATH");
+ if (!env_path || !env_path[0])
+ putenv((char*)"PATH=/usr/sbin:/usr/bin:/sbin:/bin");
+
+ unsetenv("ABRT_SYSLOG");
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+ msg_prefix = PROGNAME; /* for log(), error_msg() and such */
+ if (opts & OPT_p)
+ putenv((char*)"ABRT_PROG_PREFIX=1");
+ if (opts & OPT_s)
+ start_syslog_logging();
+
+ xpipe(s_signal_pipe);
+ close_on_exec_on(s_signal_pipe[0]);
+ close_on_exec_on(s_signal_pipe[1]);
+ signal(SIGTERM, handle_signal);
+ signal(SIGINT, handle_signal);
+ signal(SIGCHLD, handle_signal);
+ if (s_timeout != 0)
+ signal(SIGALRM, handle_signal);
+
+ /* Daemonize unless -d */
+ if (!(opts & OPT_d))
+ {
+ /* forking to background */
+ pid_t pid = fork();
+ if (pid < 0)
+ {
+ perror_msg_and_die("fork");
+ }
+ if (pid > 0)
+ {
+ /* Parent */
+ /* Wait for child to notify us via SIGTERM that it feels ok */
+ int i = 20; /* 2 sec */
+ while (s_sig_caught == 0 && --i)
+ {
+ usleep(100 * 1000);
+ }
+ if (s_sig_caught == SIGTERM)
+ {
+ exit(0);
+ }
+ if (s_sig_caught)
+ {
+ error_msg_and_die("Failed to start: got sig %d", s_sig_caught);
+ }
+ error_msg_and_die("Failed to start: timeout waiting for child");
+ }
+ /* Child (daemon) continues */
+ setsid(); /* never fails */
+ if (g_verbose == 0 && logmode != LOGMODE_SYSLOG)
+ start_syslog_logging();
+ }
+
+ GMainLoop* pMainloop = NULL;
+ GIOChannel* channel_inotify = NULL;
+ guint channel_inotify_event_id = 0;
+ GIOChannel* channel_signal = NULL;
+ guint channel_signal_event_id = 0;
+ bool pidfile_created = false;
+
+ /* Initialization */
+ init_daemon_logging();
+
+ VERB1 log("Loading settings");
+ if (load_settings() != 0)
+ goto init_error;
+
+ sanitize_dump_dir_rights();
+
+ VERB1 log("Creating glib main loop");
+ pMainloop = g_main_loop_new(NULL, FALSE);
+
+ VERB1 log("Initializing inotify");
+ errno = 0;
+ int inotify_fd = inotify_init();
+ if (inotify_fd == -1)
+ perror_msg_and_die("inotify_init failed");
+ close_on_exec_on(inotify_fd);
+
+ /* Watching DEBUG_DUMPS_DIR for new files... */
+ if (inotify_add_watch(inotify_fd, DEBUG_DUMPS_DIR, IN_CREATE | IN_MOVED_TO) < 0)
+ {
+ perror_msg("inotify_add_watch failed on '%s'", DEBUG_DUMPS_DIR);
+ goto init_error;
+ }
+ if (g_settings_sWatchCrashdumpArchiveDir)
+ {
+ s_upload_watch = inotify_add_watch(inotify_fd, g_settings_sWatchCrashdumpArchiveDir, IN_CLOSE_WRITE|IN_MOVED_TO);
+ if (s_upload_watch < 0)
+ {
+ perror_msg("inotify_add_watch failed on '%s'", g_settings_sWatchCrashdumpArchiveDir);
+ goto init_error;
+ }
+ }
+ VERB1 log("Adding inotify watch to glib main loop");
+ channel_inotify = g_io_channel_unix_new(inotify_fd);
+ channel_inotify_event_id = g_io_add_watch(channel_inotify,
+ G_IO_IN,
+ handle_inotify_cb,
+ NULL);
+
+ /* Add an event source which waits for INT/TERM signal */
+ VERB1 log("Adding signal pipe watch to glib main loop");
+ channel_signal = g_io_channel_unix_new(s_signal_pipe[0]);
+ channel_signal_event_id = g_io_add_watch(channel_signal,
+ G_IO_IN,
+ handle_signal_cb,
+ NULL);
+
+ /* Mark the territory */
+ VERB1 log("Creating pid file");
+ if (create_pidfile() != 0)
+ goto init_error;
+ pidfile_created = true;
+
+ /* Open socket to receive new crashes. */
+ dumpsocket_init();
+
+ /* Note: this already may process a few dbus messages,
+ * therefore it should be the last thing to initialize.
+ */
+ VERB1 log("Initializing dbus");
+ if (init_dbus() != 0)
+ goto init_error;
+
+ /* Inform parent that we initialized ok */
+ if (!(opts & OPT_d))
+ {
+ VERB1 log("Signalling parent");
+ kill(parent_pid, SIGTERM);
+ if (logmode != LOGMODE_SYSLOG)
+ start_syslog_logging();
+ }
+
+ /* Only now we want signal pipe to work */
+ s_signal_pipe_write = s_signal_pipe[1];
+
+ /* Enter the event loop */
+ log("Init complete, entering main loop");
+ run_main_loop(pMainloop);
+
+ cleanup:
+ /* Error or INT/TERM. Clean up, in reverse order.
+ * Take care to not undo things we did not do.
+ */
+ dumpsocket_shutdown();
+ if (pidfile_created)
+ unlink(VAR_RUN_PIDFILE);
+
+ if (channel_signal_event_id > 0)
+ g_source_remove(channel_signal_event_id);
+ if (channel_signal)
+ g_io_channel_unref(channel_signal);
+ if (channel_inotify_event_id > 0)
+ g_source_remove(channel_inotify_event_id);
+ if (channel_inotify)
+ g_io_channel_unref(channel_inotify);
+
+ deinit_dbus();
+
+ if (pMainloop)
+ g_main_loop_unref(pMainloop);
+
+ free_settings();
+
+ /* Exiting */
+ if (s_sig_caught && s_sig_caught != SIGALRM && s_sig_caught != SIGCHLD)
+ {
+ error_msg("Got signal %d, exiting", s_sig_caught);
+ signal(s_sig_caught, SIG_DFL);
+ raise(s_sig_caught);
+ }
+ error_msg_and_die("Exiting");
+
+ init_error:
+ /* Initialization error */
+ error_msg("Error while initializing daemon");
+ /* Inform parent that initialization failed */
+ if (!(opts & OPT_d))
+ kill(parent_pid, SIGINT);
+ goto cleanup;
+}