summaryrefslogtreecommitdiffstats
path: root/src/daemon/Daemon.cpp
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-14 19:22:05 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-14 19:22:05 +0200
commitbfdf82e770def64e204d1becf9640034bcdc19bb (patch)
treee97b51dbcbab89ad8f1baa5f48754465350065a9 /src/daemon/Daemon.cpp
parentd34e82eb33d3f058f6600cbeccf5f2d01dcf9ff6 (diff)
downloadabrt-bfdf82e770def64e204d1becf9640034bcdc19bb.tar.gz
abrt-bfdf82e770def64e204d1becf9640034bcdc19bb.tar.xz
abrt-bfdf82e770def64e204d1becf9640034bcdc19bb.zip
This patch makes abrtd spawn a new process, abrt-server,
for every socket client. This allows for simpler timeout handling using SIGALRM, and makes timers for tracking client timeouts unnecessary. This also allows for debugging and regression testing of abrt-server separately - it can be simply run from command-line and fed commands on stdin. Also it provides a better fault isolation - crash in abrt-server does not take down abrtd. The code is based on dumpsocket.{cpp,h}. Most of dumpsocket.cpp goes to abrt-server.c, a small part goes to Daemon.cpp (i.e. to abrtd). This change will not compile - it does not have dumpsocket.cpp renamed to abrt-server.c, which makes the change easier to read. The next change, which I'll commit after this one, contains only the renaming. Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src/daemon/Daemon.cpp')
-rw-r--r--src/daemon/Daemon.cpp176
1 files changed, 154 insertions, 22 deletions
diff --git a/src/daemon/Daemon.cpp b/src/daemon/Daemon.cpp
index a0875279..1237e7c4 100644
--- a/src/daemon/Daemon.cpp
+++ b/src/daemon/Daemon.cpp
@@ -19,6 +19,7 @@
#if HAVE_LOCALE_H
# include <locale.h>
#endif
+#include <sys/un.h>
#include <syslog.h>
#include <pthread.h>
#include <resolv.h> /* res_init */
@@ -32,12 +33,20 @@
#include "abrt_exception.h"
#include "CrashWatcher.h"
#include "Daemon.h"
-#include "dumpsocket.h"
#include "rpm.h"
using namespace std;
+#define VAR_RUN_LOCK_FILE VAR_RUN"/abrt/abrtd.lock"
+#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
@@ -77,12 +86,129 @@ using namespace std;
* If set_client_name(NULL) was done, they are not sent.
*/
+CCommLayerServer* g_pCommLayer;
-#define VAR_RUN_LOCK_FILE VAR_RUN"/abrt/abrtd.lock"
-#define VAR_RUN_PIDFILE VAR_RUN"/abrtd.pid"
+static bool daemonize = true;
+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)
+ {
+ close(socket);
+ perror_msg("fork");
+ 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;
+ }
+}
+
+
+/* Cron handling */
-//FIXME: add some struct to be able to join all threads!
typedef struct cron_callback_data_t
{
std::string m_sPluginName;
@@ -99,17 +225,6 @@ typedef struct cron_callback_data_t
{}
} cron_callback_data_t;
-
-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;
-
-CCommLayerServer* g_pCommLayer;
-
-
static void cron_delete_callback_data_cb(gpointer data)
{
cron_callback_data_t* cronDeleteCallbackData = static_cast<cron_callback_data_t*>(data);
@@ -392,7 +507,7 @@ static int Lock()
/* we leak opened lfd intentionally */
}
-static void handle_fatal_signal(int signo)
+static void handle_signal(int signo)
{
// Enable for debugging only, malloc/printf are unsafe in signal handlers
//VERB3 log("Got signal %d", signo);
@@ -415,7 +530,18 @@ static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpoint
{
/* we did receive a signal */
VERB3 log("Got signal %d through signal pipe", signo);
- s_exiting = 1;
+ if (signo != SIGCHLD)
+ s_exiting = 1;
+ else
+ {
+ 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;
@@ -739,7 +865,6 @@ static void sanitize_dump_dir_rights()
int main(int argc, char** argv)
{
- bool daemonize = true;
int opt;
int parent_pid = getpid();
@@ -753,6 +878,10 @@ int main(int argc, char** argv)
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);
+
while ((opt = getopt(argc, argv, "dsvt:")) != -1)
{
unsigned long ul;
@@ -787,15 +916,18 @@ int main(int argc, char** argv)
}
}
+ putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
+
msg_prefix = "abrtd"; /* for log(), error_msg() and such */
xpipe(s_signal_pipe);
close_on_exec_on(s_signal_pipe[0]);
close_on_exec_on(s_signal_pipe[1]);
- signal(SIGTERM, handle_fatal_signal);
- signal(SIGINT, handle_fatal_signal);
+ signal(SIGTERM, handle_signal);
+ signal(SIGINT, handle_signal);
+ signal(SIGCHLD, handle_signal);
if (s_timeout)
- signal(SIGALRM, handle_fatal_signal);
+ signal(SIGALRM, handle_signal);
/* Daemonize unless -d */
if (daemonize)
@@ -995,7 +1127,7 @@ int main(int argc, char** argv)
g_main_loop_unref(pMainloop);
/* Exiting */
- if (s_sig_caught && s_sig_caught != SIGALRM)
+ if (s_sig_caught && s_sig_caught != SIGALRM && s_sig_caught != SIGCHLD)
{
error_msg_and_die("Got signal %d, exiting", s_sig_caught);
signal(s_sig_caught, SIG_DFL);