From 1ab41dec5fcc9d9411a7d318523f647ded40b37e Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 20 Apr 2011 14:01:32 +0200 Subject: rename Daemon.cpp to abrtd.c This loses libstdc++, libm and libgcc_s libraries from abrtd Signed-off-by: Denys Vlasenko --- src/daemon/CommLayerServerDBus.c | 2 +- src/daemon/Daemon.cpp | 709 ----------------------------- src/daemon/Makefile.am | 6 +- src/daemon/MiddleWare.c | 2 +- src/daemon/Settings.cpp | 167 ------- src/daemon/Settings.h | 43 -- src/daemon/abrt-action-save-package-data.c | 2 +- src/daemon/abrtd.c | 709 +++++++++++++++++++++++++++++ 8 files changed, 715 insertions(+), 925 deletions(-) delete mode 100644 src/daemon/Daemon.cpp delete mode 100644 src/daemon/Settings.cpp delete mode 100644 src/daemon/Settings.h create mode 100644 src/daemon/abrtd.c (limited to 'src/daemon') diff --git a/src/daemon/CommLayerServerDBus.c b/src/daemon/CommLayerServerDBus.c index 9d6b6a1f..dd03d989 100644 --- a/src/daemon/CommLayerServerDBus.c +++ b/src/daemon/CommLayerServerDBus.c @@ -21,7 +21,7 @@ #include "abrt_dbus.h" #include "comm_layer_inner.h" #include "MiddleWare.h" -#include "Settings.h" +#include "abrt_conf.h" #include "CommLayerServerDBus.h" /* diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp deleted file mode 100644 index 6b894f01..00000000 --- a/src/daemon/Daemon.cpp +++ /dev/null @@ -1,709 +0,0 @@ -/* - 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 -#endif -#include -#include -#include -#include /* ioctl(FIONREAD) */ -#include "abrtlib.h" -#include "comm_layer_inner.h" -#include "Settings.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; -} diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 97e6fb5f..f57f85ff 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -12,9 +12,9 @@ sbin_PROGRAMS = \ abrtd_SOURCES = \ MiddleWare.h MiddleWare.c \ CommLayerServerDBus.h CommLayerServerDBus.c \ - Settings.h Settings.cpp \ + abrt_conf.h abrt_conf.c \ comm_layer_inner.h comm_layer_inner.c \ - Daemon.cpp + abrtd.c abrtd_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ -I$(srcdir)/../lib \ @@ -75,7 +75,7 @@ abrt_handle_crashdump_LDADD = \ abrt_action_save_package_data_SOURCES = \ rpm.h rpm.c \ - Settings.h Settings.cpp \ + abrt_conf.h abrt_conf.c \ abrt-action-save-package-data.c abrt_action_save_package_data_CPPFLAGS = \ -I$(srcdir)/../include/report -I$(srcdir)/../include \ diff --git a/src/daemon/MiddleWare.c b/src/daemon/MiddleWare.c index cff5d785..d337668f 100644 --- a/src/daemon/MiddleWare.c +++ b/src/daemon/MiddleWare.c @@ -19,7 +19,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "abrtlib.h" -#include "Settings.h" +#include "abrt_conf.h" #include "comm_layer_inner.h" #include "CommLayerServerDBus.h" #include "MiddleWare.h" diff --git a/src/daemon/Settings.cpp b/src/daemon/Settings.cpp deleted file mode 100644 index 90efd199..00000000 --- a/src/daemon/Settings.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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. -*/ -#include "abrtlib.h" -#include "Settings.h" - -bool g_settings_bOpenGPGCheck = false; -GList * g_settings_setOpenGPGPublicKeys = NULL; -GList * g_settings_setBlackListedPkgs = NULL; -GList * g_settings_setBlackListedPaths = NULL; -char * g_settings_sWatchCrashdumpArchiveDir = NULL; -unsigned int g_settings_nMaxCrashReportsSize = 1000; -bool g_settings_bProcessUnpackaged = false; - - -void free_settings() -{ - list_free_with_free(g_settings_setOpenGPGPublicKeys); - g_settings_setOpenGPGPublicKeys = NULL; - - list_free_with_free(g_settings_setBlackListedPkgs); - g_settings_setBlackListedPkgs = NULL; - - list_free_with_free(g_settings_setBlackListedPaths); - g_settings_setBlackListedPaths = NULL; - - free(g_settings_sWatchCrashdumpArchiveDir); - g_settings_sWatchCrashdumpArchiveDir = NULL; -} - -static GList *parse_list(const char* list) -{ - struct strbuf *item = strbuf_new(); - GList *l = NULL; - - char *trim_item = NULL; - - for (unsigned ii = 0; list[ii]; ii++) - { - if (list[ii] == ',') - { - trim_item = strtrim(item->buf); - l = g_list_append(l, xstrdup(trim_item)); - strbuf_clear(item); - } - else - strbuf_append_char(item, list[ii]); - } - - if (item->len > 0) - { - trim_item = strtrim(item->buf); - l = g_list_append(l, xstrdup(trim_item)); - } - - strbuf_free(item); - - return l; -} - -static void ParseCommon(map_string_h *settings, const char *conf_filename) -{ - char *value; - - value = g_hash_table_lookup(settings, "OpenGPGCheck"); - if (value) - { - g_settings_bOpenGPGCheck = string_to_bool(value); - g_hash_table_remove(settings, "OpenGPGCheck"); - } - - value = g_hash_table_lookup(settings, "BlackList"); - if (value) - { - g_settings_setBlackListedPkgs = parse_list(value); - g_hash_table_remove(settings, "BlackList"); - } - - value = g_hash_table_lookup(settings, "BlackListedPaths"); - if (value) - { - g_settings_setBlackListedPaths = parse_list(value); - g_hash_table_remove(settings, "BlackListedPaths"); - } - - value = g_hash_table_lookup(settings, "WatchCrashdumpArchiveDir"); - if (value) - { - g_settings_sWatchCrashdumpArchiveDir = xstrdup(value); - g_hash_table_remove(settings, "WatchCrashdumpArchiveDir"); - } - - value = g_hash_table_lookup(settings, "MaxCrashReportsSize"); - if (value) - { -//fixme: dont die - g_settings_nMaxCrashReportsSize = xatoi_positive(value); - g_hash_table_remove(settings, "MaxCrashReportsSize"); - } - - value = g_hash_table_lookup(settings, "ProcessUnpackaged"); - if (value) - { - g_settings_bProcessUnpackaged = string_to_bool(value); - g_hash_table_remove(settings, "ProcessUnpackaged"); - } - - GHashTableIter iter; - char *name; - /*char *value; - already declared */ - g_hash_table_iter_init(&iter, settings); - while (g_hash_table_iter_next(&iter, (void**)&name, (void**)&value)) - { - error_msg("Unrecognized variable '%s' in '%s'", name, conf_filename); - } -} - -static void LoadGPGKeys() -{ - FILE *fp = fopen(CONF_DIR"/gpg_keys", "r"); - if (fp) - { - /* every line is one key - * FIXME: make it more robust, it doesn't handle comments - */ - char *line; - while ((line = xmalloc_fgetline(fp)) != NULL) - { - if (line[0] == '/') // probably the beginning of a path, so let's handle it as a key - g_settings_setOpenGPGPublicKeys = g_list_append(g_settings_setOpenGPGPublicKeys, line); - else - free(line); - } - fclose(fp); - } -} - -int load_settings() -{ - free_settings(); - - map_string_h *settings = new_map_string(); - if (!load_conf_file(CONF_DIR"/abrt.conf", settings, /*skip key w/o values:*/ false)) - error_msg("Can't open '%s'", CONF_DIR"/abrt.conf"); - - ParseCommon(settings, CONF_DIR"/abrt.conf"); - free_map_string(settings); - - LoadGPGKeys(); - - return 0; -} diff --git a/src/daemon/Settings.h b/src/daemon/Settings.h deleted file mode 100644 index c01f8c4a..00000000 --- a/src/daemon/Settings.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (C) 2010 ABRT team - Copyright (C) 2010 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. -*/ -#ifndef SETTINGS_H_ -#define SETTINGS_H_ - -#include "abrt_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern GList * g_settings_setOpenGPGPublicKeys; -extern GList * g_settings_setBlackListedPkgs; -extern GList * g_settings_setBlackListedPaths; -extern unsigned int g_settings_nMaxCrashReportsSize; -extern bool g_settings_bOpenGPGCheck; -extern bool g_settings_bProcessUnpackaged; -extern char * g_settings_sWatchCrashdumpArchiveDir; - -int load_settings(); -void free_settings(); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/daemon/abrt-action-save-package-data.c b/src/daemon/abrt-action-save-package-data.c index 7249551d..90b16ee3 100644 --- a/src/daemon/abrt-action-save-package-data.c +++ b/src/daemon/abrt-action-save-package-data.c @@ -18,7 +18,7 @@ */ #include #include "abrtlib.h" -#include "Settings.h" +#include "abrt_conf.h" #include "rpm.h" #include "parse_options.h" 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 +#endif +#include +#include +#include +#include /* 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; +} -- cgit