|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
Copyright (C) 2009 Jiri Moskovcak (jmoskovc@redhat.com)
|
|
Packit |
8ea169 |
Copyright (C) 2009 RedHat inc.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
8ea169 |
it under the terms of the GNU General Public License as published by
|
|
Packit |
8ea169 |
the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
8ea169 |
(at your option) any later version.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
This program is distributed in the hope that it will be useful,
|
|
Packit |
8ea169 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8ea169 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8ea169 |
GNU General Public License for more details.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
You should have received a copy of the GNU General Public License along
|
|
Packit |
8ea169 |
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
8ea169 |
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
#if HAVE_LOCALE_H
|
|
Packit |
8ea169 |
# include <locale.h>
|
|
Packit |
8ea169 |
#endif
|
|
Packit |
8ea169 |
#include <sys/un.h>
|
|
Packit |
8ea169 |
#include <glib-unix.h>
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#include "abrt_glib.h"
|
|
Packit |
8ea169 |
#include "abrt-inotify.h"
|
|
Packit |
8ea169 |
#include "libabrt.h"
|
|
Packit |
8ea169 |
#include "problem_api.h"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* I want to use -Werror, but gcc-4.4 throws a curveball:
|
|
Packit |
8ea169 |
* "warning: ignoring return value of 'ftruncate', declared with attribute warn_unused_result"
|
|
Packit |
8ea169 |
* and (void) cast is not enough to shut it up! Oh God...
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
#define IGNORE_RESULT(func_call) do { if (func_call) /* nothing */; } while (0)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define VAR_RUN_PIDFILE VAR_RUN"/abrt/abrtd.pid"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define SOCKET_FILE VAR_RUN"/abrt/abrt.socket"
|
|
Packit |
8ea169 |
#define SOCKET_PERMISSION 0666
|
|
Packit |
8ea169 |
/* Maximum number of simultaneously opened client connections. */
|
|
Packit |
8ea169 |
#define MAX_CLIENT_COUNT 10
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define IN_DUMP_LOCATION_FLAGS (IN_DELETE_SELF | IN_MOVE_SELF)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define ABRTD_DBUS_NAME ABRT_DBUS_NAME".daemon"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Daemon initializes, then sits in glib main loop, waiting for events.
|
|
Packit |
8ea169 |
* Events can be:
|
|
Packit |
8ea169 |
* - inotify: something new appeared under /var/tmp/abrt or /var/spool/abrt-upload
|
|
Packit |
8ea169 |
* - signal: we got SIGTERM, SIGINT, SIGALRM or SIGCHLD
|
|
Packit |
8ea169 |
* - new socket connection
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static volatile sig_atomic_t s_sig_caught;
|
|
Packit |
8ea169 |
static int s_signal_pipe[2];
|
|
Packit |
8ea169 |
static int s_signal_pipe_write = -1;
|
|
Packit |
8ea169 |
static unsigned s_timeout;
|
|
Packit |
8ea169 |
static int s_timeout_src;
|
|
Packit |
8ea169 |
static GMainLoop *s_main_loop;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
GList *s_processes;
|
|
Packit |
8ea169 |
GList *s_dir_queue;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static GIOChannel *channel_socket = NULL;
|
|
Packit |
8ea169 |
static guint channel_id_socket = 0;
|
|
Packit |
8ea169 |
static int child_count = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct abrt_server_proc
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
pid_t pid;
|
|
Packit |
8ea169 |
int fdout;
|
|
Packit |
8ea169 |
char *dirname;
|
|
Packit |
8ea169 |
GIOChannel *channel;
|
|
Packit |
8ea169 |
guint watch_id;
|
|
Packit |
8ea169 |
enum {
|
|
Packit |
8ea169 |
AS_UKNOWN,
|
|
Packit |
8ea169 |
AS_POST_CREATE,
|
|
Packit |
8ea169 |
} type;
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Returns 0 if proc's pid equals the the given pid */
|
|
Packit |
8ea169 |
static gint abrt_server_compare_pid(struct abrt_server_proc *proc, pid_t *pid)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
return proc->pid != *pid;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Returns 0 if proc's fdout equals the the given fdout */
|
|
Packit |
8ea169 |
static gint abrt_server_compare_fdout(struct abrt_server_proc *proc, int *fdout)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
return proc->fdout != *fdout;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Returns 0 if proc's dirname equals the the given dirname */
|
|
Packit |
8ea169 |
static gint abrt_server_compare_dirname(struct abrt_server_proc *proc, const char *dirname)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
return g_strcmp0(proc->dirname, dirname);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Helpers */
|
|
Packit |
8ea169 |
static guint add_watch_or_die(GIOChannel *channel, unsigned condition, GIOFunc func)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
errno = 0;
|
|
Packit |
8ea169 |
guint r = g_io_add_watch(channel, (GIOCondition)condition, func, NULL);
|
|
Packit |
8ea169 |
if (!r)
|
|
Packit |
8ea169 |
perror_msg_and_die("g_io_add_watch failed");
|
|
Packit |
8ea169 |
return r;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void stop_abrt_server(struct abrt_server_proc *proc)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
kill(proc->pid, SIGINT);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void dispose_abrt_server(struct abrt_server_proc *proc)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
free(proc->dirname);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (proc->watch_id > 0)
|
|
Packit |
8ea169 |
g_source_remove(proc->watch_id);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (proc->channel != NULL)
|
|
Packit |
8ea169 |
g_io_channel_unref(proc->channel);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void notify_next_post_create_process(struct abrt_server_proc *finished)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (finished != NULL)
|
|
Packit |
8ea169 |
s_dir_queue = g_list_remove(s_dir_queue, finished);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
while (s_dir_queue != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct abrt_server_proc *n = (struct abrt_server_proc *)s_dir_queue->data;
|
|
Packit |
8ea169 |
if (n->type == AS_POST_CREATE)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (kill(n->pid, SIGUSR1) >= 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
n->type = AS_POST_CREATE;
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* This could happen only if the notified process disappeared - crashed?
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
perror_msg("Failed to send SIGUSR1 to %d", n->pid);
|
|
Packit |
8ea169 |
log_warning("Directory '%s' will not be processed", n->dirname);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Remove the problematic process from the post-crate directory queue
|
|
Packit |
8ea169 |
* and go to try to notify another process.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
s_dir_queue = g_list_delete_link(s_dir_queue, s_dir_queue);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Queueing the process will also lead to cleaning up the dump location.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static void queue_post_craete_process(struct abrt_server_proc *proc)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
load_abrt_conf();
|
|
Packit |
8ea169 |
struct abrt_server_proc *running = s_dir_queue == NULL ? NULL
|
|
Packit |
8ea169 |
: (struct abrt_server_proc *)s_dir_queue->data;
|
|
Packit |
8ea169 |
if (g_settings_nMaxCrashReportsSize == 0)
|
|
Packit |
8ea169 |
goto consider_processing;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *full_path_ignored = running != NULL ? running->dirname
|
|
Packit |
8ea169 |
: proc->dirname;
|
|
Packit |
8ea169 |
const char *ignored = strrchr(full_path_ignored, '/');
|
|
Packit |
8ea169 |
if (NULL == ignored)
|
|
Packit |
8ea169 |
/* Paranoia, this should not happen. */
|
|
Packit |
8ea169 |
ignored = full_path_ignored;
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
/* Move behind '/' */
|
|
Packit |
8ea169 |
++ignored;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *worst_dir = NULL;
|
|
Packit |
8ea169 |
const double max_size = 1024 * 1024 * g_settings_nMaxCrashReportsSize;
|
|
Packit |
8ea169 |
while (get_dirsize_find_largest_dir(g_settings_dump_location, &worst_dir, ignored) >= max_size
|
|
Packit |
8ea169 |
&& worst_dir)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const char *kind = "old";
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
GList *proc_of_deleted_item = NULL;
|
|
Packit |
8ea169 |
if (proc != NULL && strcmp(worst_dir, proc->dirname) == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
kind = "new";
|
|
Packit |
8ea169 |
stop_abrt_server(proc);
|
|
Packit |
8ea169 |
proc = NULL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else if ((proc_of_deleted_item = g_list_find_custom(s_dir_queue, worst_dir, (GCompareFunc)abrt_server_compare_dirname)))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
kind = "unprocessed";
|
|
Packit |
8ea169 |
struct abrt_server_proc *removed_proc = (struct abrt_server_proc *)proc_of_deleted_item->data;
|
|
Packit |
8ea169 |
s_dir_queue = g_list_delete_link(s_dir_queue, proc_of_deleted_item);
|
|
Packit |
8ea169 |
stop_abrt_server(removed_proc);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_warning("Size of '%s' >= %u MB (MaxCrashReportsSize), deleting %s directory '%s'",
|
|
Packit |
8ea169 |
g_settings_dump_location, g_settings_nMaxCrashReportsSize,
|
|
Packit |
8ea169 |
kind, worst_dir);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *deleted = concat_path_file(g_settings_dump_location, worst_dir);
|
|
Packit |
8ea169 |
free(worst_dir);
|
|
Packit |
8ea169 |
worst_dir = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_opendir(deleted, DD_FAIL_QUIETLY_ENOENT);
|
|
Packit |
8ea169 |
if (dd != NULL)
|
|
Packit |
8ea169 |
dd_delete(dd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(deleted);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
consider_processing:
|
|
Packit |
8ea169 |
/* If the process survived cleaning up the dump location, append it to the
|
|
Packit |
8ea169 |
* post-create queue.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
if (proc != NULL)
|
|
Packit |
8ea169 |
s_dir_queue = g_list_append(s_dir_queue, proc);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* If there were no running post-crate process before we added the
|
|
Packit |
8ea169 |
* currently handled process to the post-create queue, start processing of
|
|
Packit |
8ea169 |
* the currently handled process.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
if (running == NULL)
|
|
Packit |
8ea169 |
notify_next_post_create_process(NULL/*finished*/);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static gboolean abrt_server_output_cb(GIOChannel *channel, GIOCondition condition, gpointer user_data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
int fdout = g_io_channel_unix_get_fd(channel);
|
|
Packit |
8ea169 |
GList *item = g_list_find_custom(s_processes, &fdout, (GCompareFunc)abrt_server_compare_fdout);
|
|
Packit |
8ea169 |
if (item == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Removing an input channel fd (%d) without a process assigned", fdout);
|
|
Packit |
8ea169 |
return FALSE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct abrt_server_proc *proc = (struct abrt_server_proc *)item->data;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (condition & G_IO_HUP)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("abrt-server(%d) closed its pipe", proc->pid);
|
|
Packit |
8ea169 |
proc->watch_id = 0;
|
|
Packit |
8ea169 |
return FALSE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
for (;;)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
gchar *line;
|
|
Packit |
8ea169 |
gsize len = 0;
|
|
Packit |
8ea169 |
gsize pos = 0;
|
|
Packit |
8ea169 |
GError *error = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* We use buffered channel so we do not need to read from the channel in a
|
|
Packit |
8ea169 |
* loop */
|
|
Packit |
8ea169 |
GIOStatus stat = g_io_channel_read_line(channel, &line, &len, &pos, &error);
|
|
Packit |
8ea169 |
if (stat == G_IO_STATUS_ERROR)
|
|
Packit |
8ea169 |
error_msg_and_die("Can't read from pipe of abrt-server(%d): '%s'", proc->pid, error ? error->message : "");
|
|
Packit |
8ea169 |
if (stat == G_IO_STATUS_EOF)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("abrt-server(%d)'s output read till end", proc->pid);
|
|
Packit |
8ea169 |
proc->watch_id = 0;
|
|
Packit |
8ea169 |
return FALSE; /* Remove this event */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (stat == G_IO_STATUS_AGAIN)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* G_IO_STATUS_NORMAL) */
|
|
Packit |
8ea169 |
line[pos] = '\0';
|
|
Packit |
8ea169 |
if (g_str_has_prefix(line, "NEW_PROBLEM_DETECTED: "))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (proc->dirname != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("abrt-server(%d): already handling: %s", proc->pid, proc->dirname);
|
|
Packit |
8ea169 |
free(proc->dirname);
|
|
Packit |
8ea169 |
/* Because process can be only once in the dir queue */
|
|
Packit |
8ea169 |
s_dir_queue = g_list_remove(s_dir_queue, proc);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
proc->dirname = xstrdup(line + strlen("NEW_PROBLEM_DETECTED: "));
|
|
Packit |
8ea169 |
log_notice("abrt-server(%d): handling new problem: %s", proc->pid, proc->dirname);
|
|
Packit |
8ea169 |
queue_post_craete_process(proc);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
log_warning("abrt-server(%d): not recognized message: '%s'", proc->pid, line);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_free(line);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return TRUE; /* Keep this event */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void add_abrt_server_proc(const pid_t pid, int fdout)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct abrt_server_proc *proc = xmalloc(sizeof(*proc));
|
|
Packit |
8ea169 |
proc->pid = pid;
|
|
Packit |
8ea169 |
proc->fdout = fdout;
|
|
Packit |
8ea169 |
proc->dirname = NULL;
|
|
Packit |
8ea169 |
proc->type = AS_UKNOWN;
|
|
Packit |
8ea169 |
proc->channel = abrt_gio_channel_unix_new(proc->fdout);
|
|
Packit |
8ea169 |
proc->watch_id = g_io_add_watch(proc->channel,
|
|
Packit |
8ea169 |
G_IO_IN | G_IO_HUP,
|
|
Packit |
8ea169 |
abrt_server_output_cb,
|
|
Packit |
8ea169 |
proc);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
GError *error = NULL;
|
|
Packit |
8ea169 |
g_io_channel_set_flags(proc->channel, G_IO_FLAG_NONBLOCK, &error);
|
|
Packit |
8ea169 |
if (error != NULL)
|
|
Packit |
8ea169 |
error_msg_and_die("g_io_channel_set_flags failed: '%s'", error->message);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_io_channel_set_buffered(proc->channel, TRUE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
s_processes = g_list_append(s_processes, proc);
|
|
Packit |
8ea169 |
if (g_list_length(s_processes) >= MAX_CLIENT_COUNT)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Too many clients, refusing connections to '%s'", SOCKET_FILE);
|
|
Packit |
8ea169 |
/* To avoid infinite loop caused by the descriptor in "ready" state,
|
|
Packit |
8ea169 |
* the callback must be disabled.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
g_source_remove(channel_id_socket);
|
|
Packit |
8ea169 |
channel_id_socket = 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void start_idle_timeout(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (s_timeout == 0 || child_count > 0)
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
s_timeout_src = g_timeout_add_seconds(s_timeout, (GSourceFunc)g_main_loop_quit, s_main_loop);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void kill_idle_timeout(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (s_timeout == 0)
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (s_timeout_src != 0)
|
|
Packit |
8ea169 |
g_source_remove(s_timeout_src);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
s_timeout_src = 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpointer ptr_unused);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void remove_abrt_server_proc(pid_t pid, int status)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
GList *item = g_list_find_custom(s_processes, &pid, (GCompareFunc)abrt_server_compare_pid);
|
|
Packit |
8ea169 |
if (item == NULL)
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct abrt_server_proc *proc = (struct abrt_server_proc *)item->data;
|
|
Packit |
8ea169 |
item->data = NULL;
|
|
Packit |
8ea169 |
s_processes = g_list_delete_link(s_processes, item);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (proc->type == AS_POST_CREATE)
|
|
Packit |
8ea169 |
notify_next_post_create_process(proc);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{ /* Make sure out-of-order exited abrt-server post-create processes do
|
|
Packit |
8ea169 |
* not stay in the post-create queue.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
s_dir_queue = g_list_remove(s_dir_queue, proc);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dispose_abrt_server(proc);
|
|
Packit |
8ea169 |
free(proc);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (g_list_length(s_processes) < MAX_CLIENT_COUNT && !channel_id_socket)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("Accepting connections on '%s'", SOCKET_FILE);
|
|
Packit |
8ea169 |
channel_id_socket = add_watch_or_die(channel_socket, G_IO_IN | G_IO_PRI | G_IO_HUP, server_socket_cb);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Callback called by glib main loop when a client connects to ABRT's socket. */
|
|
Packit |
8ea169 |
static gboolean server_socket_cb(GIOChannel *source, GIOCondition condition, gpointer ptr_unused)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
kill_idle_timeout();
|
|
Packit |
8ea169 |
load_abrt_conf();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int socket = accept(g_io_channel_unix_get_fd(source), NULL, NULL);
|
|
Packit |
8ea169 |
if (socket == -1)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg("accept");
|
|
Packit |
8ea169 |
goto server_socket_finitio;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_notice("New client connected");
|
|
Packit |
8ea169 |
fflush(NULL); /* paranoia */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int pipefd[2];
|
|
Packit |
8ea169 |
xpipe(pipefd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
pid_t pid = fork();
|
|
Packit |
8ea169 |
if (pid < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg("fork");
|
|
Packit |
8ea169 |
close(socket);
|
|
Packit |
8ea169 |
close(pipefd[0]);
|
|
Packit |
8ea169 |
close(pipefd[1]);
|
|
Packit |
8ea169 |
goto server_socket_finitio;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (pid == 0) /* child */
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
xdup2(socket, STDIN_FILENO);
|
|
Packit |
8ea169 |
xdup2(socket, STDOUT_FILENO);
|
|
Packit |
8ea169 |
close(socket);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
close(pipefd[0]);
|
|
Packit |
8ea169 |
xmove_fd(pipefd[1], STDERR_FILENO);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *argv[3]; /* abrt-server [-s] NULL */
|
|
Packit |
8ea169 |
char **pp = argv;
|
|
Packit |
8ea169 |
*pp++ = (char*)"abrt-server";
|
|
Packit |
8ea169 |
if (logmode & LOGMODE_JOURNAL)
|
|
Packit |
8ea169 |
*pp++ = (char*)"-s";
|
|
Packit |
8ea169 |
*pp = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
execvp(argv[0], argv);
|
|
Packit |
8ea169 |
perror_msg_and_die("Can't execute '%s'", argv[0]);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* parent */
|
|
Packit |
8ea169 |
close(socket);
|
|
Packit |
8ea169 |
close(pipefd[1]);
|
|
Packit |
8ea169 |
add_abrt_server_proc(pid, pipefd[0]);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
server_socket_finitio:
|
|
Packit |
8ea169 |
start_idle_timeout();
|
|
Packit |
8ea169 |
return TRUE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Signal pipe handler */
|
|
Packit |
8ea169 |
static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr_unused)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
uint8_t signo;
|
|
Packit |
8ea169 |
gsize len = 0;
|
|
Packit |
8ea169 |
g_io_channel_read_chars(gio, (void*) &signo, 1, &len, NULL);
|
|
Packit |
8ea169 |
if (len == 1)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* we did receive a signal */
|
|
Packit |
8ea169 |
log_debug("Got signal %d through signal pipe", signo);
|
|
Packit |
8ea169 |
if (signo != SIGCHLD)
|
|
Packit |
8ea169 |
g_main_loop_quit(s_main_loop);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
pid_t cpid;
|
|
Packit |
8ea169 |
int status;
|
|
Packit |
8ea169 |
while ((cpid = safe_waitpid(-1, &status, WNOHANG)) > 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (WIFSIGNALED(status))
|
|
Packit |
8ea169 |
log_debug("abrt-server(%d) signaled with %d", cpid, WTERMSIG(status));
|
|
Packit |
8ea169 |
else if (WIFEXITED(status))
|
|
Packit |
8ea169 |
log_debug("abrt-server(%d) exited with %d", cpid, WEXITSTATUS(status));
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("abrt-server(%d) is being debugged", cpid);
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
remove_abrt_server_proc(cpid, status);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
start_idle_timeout();
|
|
Packit |
8ea169 |
return TRUE; /* "please don't remove this event" */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void sanitize_dump_dir_rights(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* We can't allow everyone to create dumps: otherwise users can flood
|
|
Packit |
8ea169 |
* us with thousands of bogus or malicious dumps */
|
|
Packit |
8ea169 |
/* 07000 bits are setuid, setgit, and sticky, and they must be unset */
|
|
Packit |
8ea169 |
/* 00777 bits are usual "rwxrwxrwx" access rights */
|
|
Packit |
8ea169 |
ensure_writable_dir_group(g_settings_dump_location, DEFAULT_DUMP_LOCATION_MODE, "root", "abrt");
|
|
Packit |
8ea169 |
/* temp dir */
|
|
Packit |
8ea169 |
ensure_writable_dir(VAR_RUN"/abrt", 0755, "root");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Inotify handler */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void handle_inotify_cb(struct abrt_inotify_watch *watch, struct inotify_event *event, gpointer ptr_unused)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
kill_idle_timeout();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Recreating deleted dump location '%s'", g_settings_dump_location);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
load_abrt_conf();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
sanitize_dump_dir_rights();
|
|
Packit |
8ea169 |
abrt_inotify_watch_reset(watch, g_settings_dump_location, IN_DUMP_LOCATION_FLAGS);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
start_idle_timeout();
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Initializes the dump socket, usually in /var/run directory
|
|
Packit |
8ea169 |
* (the path depends on compile-time configuration).
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static void dumpsocket_init(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
unlink(SOCKET_FILE); /* not caring about the result */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int socketfd = xsocket(AF_UNIX, SOCK_STREAM, 0);
|
|
Packit |
8ea169 |
close_on_exec_on(socketfd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct sockaddr_un local;
|
|
Packit |
8ea169 |
memset(&local, 0, sizeof(local));
|
|
Packit |
8ea169 |
local.sun_family = AF_UNIX;
|
|
Packit |
8ea169 |
strcpy(local.sun_path, SOCKET_FILE);
|
|
Packit |
8ea169 |
xbind(socketfd, (struct sockaddr*)&local, sizeof(local));
|
|
Packit |
8ea169 |
xlisten(socketfd, MAX_CLIENT_COUNT);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (chmod(SOCKET_FILE, SOCKET_PERMISSION) != 0)
|
|
Packit |
8ea169 |
perror_msg_and_die("chmod '%s'", SOCKET_FILE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
channel_socket = abrt_gio_channel_unix_new(socketfd);
|
|
Packit |
8ea169 |
g_io_channel_set_buffered(channel_socket, FALSE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
channel_id_socket = add_watch_or_die(channel_socket, G_IO_IN | G_IO_PRI | G_IO_HUP, server_socket_cb);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Releases all resources used by dumpsocket. */
|
|
Packit |
8ea169 |
static void dumpsocket_shutdown(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Set everything to pre-initialization state. */
|
|
Packit |
8ea169 |
if (channel_socket)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Undo add_watch_or_die */
|
|
Packit |
8ea169 |
g_source_remove(channel_id_socket);
|
|
Packit |
8ea169 |
/* Undo g_io_channel_unix_new */
|
|
Packit |
8ea169 |
g_io_channel_unref(channel_socket);
|
|
Packit |
8ea169 |
channel_socket = NULL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int create_pidfile(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Note:
|
|
Packit |
8ea169 |
* No O_EXCL: we would happily overwrite stale pidfile from previous boot.
|
|
Packit |
8ea169 |
* No O_TRUNC: we must first try to lock the file, and if lock fails,
|
|
Packit |
8ea169 |
* there is another live abrtd. O_TRUNCing the file in this case
|
|
Packit |
8ea169 |
* would be wrong - it'll erase the pid to empty string!
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
int fd = open(VAR_RUN_PIDFILE, O_RDWR|O_CREAT, 0644);
|
|
Packit |
8ea169 |
if (fd >= 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (lockf(fd, F_TLOCK, 0) < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg("Can't lock file '%s'", VAR_RUN_PIDFILE);
|
|
Packit |
8ea169 |
/* should help with problems like rhbz#859724 */
|
|
Packit |
8ea169 |
char pid_str[sizeof(long)*3 + 4];
|
|
Packit |
8ea169 |
int r = full_read(fd, pid_str, sizeof(pid_str));
|
|
Packit |
8ea169 |
close(fd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* File can contain garbage. Be careful interpreting it as PID */
|
|
Packit |
8ea169 |
if (r > 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
pid_str[r] = '\0';
|
|
Packit |
8ea169 |
errno = 0;
|
|
Packit |
8ea169 |
long locking_pid = strtol(pid_str, NULL, 10);
|
|
Packit |
8ea169 |
if (!errno && locking_pid > 0 && locking_pid <= INT_MAX)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *cmdline = get_cmdline(locking_pid);
|
|
Packit |
8ea169 |
if (cmdline)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Process %lu '%s' is holding the lock", locking_pid, cmdline);
|
|
Packit |
8ea169 |
free(cmdline);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return -1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
close_on_exec_on(fd);
|
|
Packit |
8ea169 |
/* write our pid to it */
|
|
Packit |
8ea169 |
char buf[sizeof(long)*3 + 2];
|
|
Packit |
8ea169 |
int len = sprintf(buf, "%lu\n", (long)getpid());
|
|
Packit |
8ea169 |
IGNORE_RESULT(write(fd, buf, len));
|
|
Packit |
8ea169 |
IGNORE_RESULT(ftruncate(fd, len));
|
|
Packit |
8ea169 |
/* we leak opened+locked fd intentionally */
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
perror_msg("Can't open '%s'", VAR_RUN_PIDFILE);
|
|
Packit |
8ea169 |
return -1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void handle_signal(int signo)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
int save_errno = errno;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
// Enable for debugging only, malloc/printf are unsafe in signal handlers
|
|
Packit |
8ea169 |
//log_debug("Got signal %d", signo);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
uint8_t sig_caught;
|
|
Packit |
8ea169 |
s_sig_caught = sig_caught = signo;
|
|
Packit |
8ea169 |
/* Using local copy of s_sig_caught so that concurrent signal
|
|
Packit |
8ea169 |
* won't change it under us */
|
|
Packit |
8ea169 |
if (s_signal_pipe_write >= 0)
|
|
Packit |
8ea169 |
IGNORE_RESULT(write(s_signal_pipe_write, &sig_caught, 1));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
errno = save_errno;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void start_logging(void)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Open stdin to /dev/null */
|
|
Packit |
8ea169 |
xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
|
|
Packit |
8ea169 |
/* We must not leave fds 0,1,2 closed.
|
|
Packit |
8ea169 |
* Otherwise fprintf(stderr) dumps messages into random fds, etc. */
|
|
Packit |
8ea169 |
xdup2(STDIN_FILENO, STDOUT_FILENO);
|
|
Packit |
8ea169 |
xdup2(STDIN_FILENO, STDERR_FILENO);
|
|
Packit |
8ea169 |
logmode = LOGMODE_JOURNAL;
|
|
Packit |
8ea169 |
putenv((char*)"ABRT_SYSLOG=1");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* The function expects that FILENAME_COUNT dump dir element is created by
|
|
Packit |
8ea169 |
* abrtd after all post-create events are successfully done. Thus if
|
|
Packit |
8ea169 |
* FILENAME_COUNT element doesn't exist abrtd can consider the dump directory
|
|
Packit |
8ea169 |
* as unprocessed.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* Relying on content of dump directory has one problem. If a hook provides
|
|
Packit |
8ea169 |
* FILENAME_COUNT abrtd will consider the dump directory as processed.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static void mark_unprocessed_dump_dirs_not_reportable(const char *path)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_notice("Searching for unprocessed dump directories");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
DIR *dp = opendir(path);
|
|
Packit |
8ea169 |
if (!dp)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg("Can't open directory '%s'", path);
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct dirent *dent;
|
|
Packit |
8ea169 |
while ((dent = readdir(dp)) != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (dot_or_dotdot(dent->d_name))
|
|
Packit |
8ea169 |
continue; /* skip "." and ".." */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *full_name = concat_path_file(path, dent->d_name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct stat stat_buf;
|
|
Packit |
8ea169 |
if (stat(full_name, &stat_buf) != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg("Can't access path '%s'", full_name);
|
|
Packit |
8ea169 |
goto next_dd;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (S_ISDIR(stat_buf.st_mode) == 0)
|
|
Packit |
8ea169 |
/* This is expected. The dump location contains some aux files */
|
|
Packit |
8ea169 |
goto next_dd;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_opendir(full_name, /*flags*/0);
|
|
Packit |
8ea169 |
if (dd)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!problem_dump_dir_is_complete(dd) && !dd_exist(dd, FILENAME_NOT_REPORTABLE))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Marking '%s' not reportable (no '"FILENAME_COUNT"' item)", full_name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_NOT_REPORTABLE, _("The problem data are "
|
|
Packit |
8ea169 |
"incomplete. This usually happens when a problem "
|
|
Packit |
8ea169 |
"is detected while computer is shutting down or "
|
|
Packit |
8ea169 |
"user is logging out. In order to provide "
|
|
Packit |
8ea169 |
"valuable problem reports, ABRT will not allow "
|
|
Packit |
8ea169 |
"you to submit this problem. If you have time and "
|
|
Packit |
8ea169 |
"want to help the developers in their effort to "
|
|
Packit |
8ea169 |
"sort out this problem, please contact them directly."));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
next_dd:
|
|
Packit |
8ea169 |
free(full_name);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
closedir(dp);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void on_bus_acquired(GDBusConnection *connection,
|
|
Packit |
8ea169 |
const gchar *name,
|
|
Packit |
8ea169 |
gpointer user_data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Going to own bus '%s'", name);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void on_name_acquired (GDBusConnection *connection,
|
|
Packit |
8ea169 |
const gchar *name,
|
|
Packit |
8ea169 |
gpointer user_data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_debug("Acquired the name '%s' on the system bus", name);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void on_name_lost(GDBusConnection *connection,
|
|
Packit |
8ea169 |
const gchar *name,
|
|
Packit |
8ea169 |
gpointer user_data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg_and_die(_("The name '%s' has been lost, please check if other "
|
|
Packit |
8ea169 |
"service owning the name is not running.\n"), name);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int main(int argc, char** argv)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* I18n */
|
|
Packit |
8ea169 |
setlocale(LC_ALL, "");
|
|
Packit |
8ea169 |
#if ENABLE_NLS
|
|
Packit |
8ea169 |
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
Packit |
8ea169 |
textdomain(PACKAGE);
|
|
Packit |
8ea169 |
#endif
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_init(argv);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int parent_pid = getpid();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *program_usage_string = _(
|
|
Packit |
8ea169 |
"& [options]"
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
enum {
|
|
Packit |
8ea169 |
OPT_v = 1 << 0,
|
|
Packit |
8ea169 |
OPT_d = 1 << 1,
|
|
Packit |
8ea169 |
OPT_s = 1 << 2,
|
|
Packit |
8ea169 |
// TODO: get rid of -t NUM, it is no longer useful since dbus is moved to a separate tool
|
|
Packit |
8ea169 |
OPT_t = 1 << 3,
|
|
Packit |
8ea169 |
OPT_p = 1 << 4,
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
/* Keep enum above and order of options below in sync! */
|
|
Packit |
8ea169 |
struct options program_options[] = {
|
|
Packit |
8ea169 |
OPT__VERBOSE(&g_verbose),
|
|
Packit |
8ea169 |
OPT_BOOL( 'd', NULL, NULL , _("Do not daemonize")),
|
|
Packit |
8ea169 |
OPT_BOOL( 's', NULL, NULL , _("Log to syslog even with -d")),
|
|
Packit |
8ea169 |
OPT_INTEGER('t', NULL, &s_timeout, _("Exit after NUM seconds of inactivity")),
|
|
Packit |
8ea169 |
OPT_BOOL( 'p', NULL, NULL , _("Add program names to log")),
|
|
Packit |
8ea169 |
OPT_END()
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
export_abrt_envvars(opts & OPT_p);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#if 0 /* We no longer use dbus */
|
|
Packit |
8ea169 |
/* When dbus daemon starts us, it doesn't set PATH
|
|
Packit |
8ea169 |
* (I saw it set only DBUS_STARTER_ADDRESS and DBUS_STARTER_BUS_TYPE).
|
|
Packit |
8ea169 |
* In this case, set something sane:
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
const char *env_path = getenv("PATH");
|
|
Packit |
8ea169 |
if (!env_path || !env_path[0])
|
|
Packit |
8ea169 |
putenv((char*)"PATH=/usr/sbin:/usr/bin:/sbin:/bin");
|
|
Packit |
8ea169 |
#endif
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
unsetenv("ABRT_SYSLOG");
|
|
Packit |
8ea169 |
msg_prefix = g_progname; /* for log_warning(), error_msg() and such */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (getuid() != 0)
|
|
Packit |
8ea169 |
error_msg_and_die("Must be run as root");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (opts & OPT_s)
|
|
Packit |
8ea169 |
start_logging();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
xpipe(s_signal_pipe);
|
|
Packit |
8ea169 |
close_on_exec_on(s_signal_pipe[0]);
|
|
Packit |
8ea169 |
close_on_exec_on(s_signal_pipe[1]);
|
|
Packit |
8ea169 |
ndelay_on(s_signal_pipe[0]); /* I/O should not block - */
|
|
Packit |
8ea169 |
ndelay_on(s_signal_pipe[1]); /* especially writes! they happen in signal handler! */
|
|
Packit |
8ea169 |
signal(SIGTERM, handle_signal);
|
|
Packit |
8ea169 |
signal(SIGINT, handle_signal);
|
|
Packit |
8ea169 |
signal(SIGCHLD, handle_signal);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
GIOChannel* channel_signal = NULL;
|
|
Packit |
8ea169 |
guint channel_id_signal_event = 0;
|
|
Packit |
8ea169 |
bool pidfile_created = false;
|
|
Packit |
8ea169 |
struct abrt_inotify_watch *aiw = NULL;
|
|
Packit |
8ea169 |
int ret = 1;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Initialization */
|
|
Packit |
8ea169 |
log_notice("Loading settings");
|
|
Packit |
8ea169 |
if (load_abrt_conf() != 0)
|
|
Packit |
8ea169 |
goto init_error;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Moved before daemonization because parent waits for signal from daemon
|
|
Packit |
8ea169 |
* only for short period and time consumed by
|
|
Packit |
8ea169 |
* mark_unprocessed_dump_dirs_not_reportable() is slightly unpredictable.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
sanitize_dump_dir_rights();
|
|
Packit |
8ea169 |
mark_unprocessed_dump_dirs_not_reportable(g_settings_dump_location);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Daemonize unless -d */
|
|
Packit |
8ea169 |
if (!(opts & OPT_d))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* forking to background */
|
|
Packit |
8ea169 |
fflush(NULL); /* paranoia */
|
|
Packit |
8ea169 |
pid_t pid = fork();
|
|
Packit |
8ea169 |
if (pid < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg_and_die("fork");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (pid > 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Parent */
|
|
Packit |
8ea169 |
/* Wait for child to notify us via SIGTERM that it feels ok */
|
|
Packit |
8ea169 |
int i = 20; /* 2 sec */
|
|
Packit |
8ea169 |
while (s_sig_caught == 0 && --i)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
usleep(100 * 1000);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (s_sig_caught == SIGTERM)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
exit(0);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (s_sig_caught)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg_and_die("Failed to start: got sig %d", s_sig_caught);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
error_msg_and_die("Failed to start: timeout waiting for child");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
/* Child (daemon) continues */
|
|
Packit |
8ea169 |
if (setsid() < 0)
|
|
Packit |
8ea169 |
perror_msg_and_die("setsid");
|
|
Packit |
8ea169 |
if (g_verbose == 0 && logmode != LOGMODE_JOURNAL)
|
|
Packit |
8ea169 |
start_logging();
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_notice("Creating glib main loop");
|
|
Packit |
8ea169 |
s_main_loop = g_main_loop_new(NULL, FALSE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Watching 'g_settings_dump_location' for delete self
|
|
Packit |
8ea169 |
* because hooks expects that the dump location exists if abrtd is running
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
aiw = abrt_inotify_watch_init(g_settings_dump_location,
|
|
Packit |
8ea169 |
IN_DUMP_LOCATION_FLAGS, handle_inotify_cb, /*user data*/NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Add an event source which waits for INT/TERM signal */
|
|
Packit |
8ea169 |
log_notice("Adding signal pipe watch to glib main loop");
|
|
Packit |
8ea169 |
channel_signal = abrt_gio_channel_unix_new(s_signal_pipe[0]);
|
|
Packit |
8ea169 |
channel_id_signal_event = add_watch_or_die(channel_signal,
|
|
Packit |
8ea169 |
G_IO_IN | G_IO_PRI | G_IO_HUP,
|
|
Packit |
8ea169 |
handle_signal_cb);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
guint name_id = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Mark the territory */
|
|
Packit |
8ea169 |
log_notice("Creating pid file");
|
|
Packit |
8ea169 |
if (create_pidfile() != 0)
|
|
Packit |
8ea169 |
goto init_error;
|
|
Packit |
8ea169 |
pidfile_created = true;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Open socket to receive new problem data (from python etc). */
|
|
Packit |
8ea169 |
dumpsocket_init();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Inform parent that we initialized ok */
|
|
Packit |
8ea169 |
if (!(opts & OPT_d))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_notice("Signalling parent");
|
|
Packit |
8ea169 |
kill(parent_pid, SIGTERM);
|
|
Packit |
8ea169 |
if (logmode != LOGMODE_JOURNAL)
|
|
Packit |
8ea169 |
start_logging();
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Only now we want signal pipe to work */
|
|
Packit |
8ea169 |
s_signal_pipe_write = s_signal_pipe[1];
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Own a name on D-Bus */
|
|
Packit |
8ea169 |
name_id = g_bus_own_name(G_BUS_TYPE_SYSTEM,
|
|
Packit |
8ea169 |
ABRTD_DBUS_NAME,
|
|
Packit |
8ea169 |
G_BUS_NAME_OWNER_FLAGS_NONE,
|
|
Packit |
8ea169 |
on_bus_acquired,
|
|
Packit |
8ea169 |
on_name_acquired,
|
|
Packit |
8ea169 |
on_name_lost,
|
|
Packit |
8ea169 |
NULL, NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
start_idle_timeout();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Enter the event loop */
|
|
Packit |
8ea169 |
log_debug("Init complete, entering main loop");
|
|
Packit |
8ea169 |
g_main_loop_run(s_main_loop);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
ret = 0;
|
|
Packit |
8ea169 |
/* Jump to exit */
|
|
Packit |
8ea169 |
goto cleanup;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
init_error:
|
|
Packit |
8ea169 |
/* Initialization error */
|
|
Packit |
8ea169 |
error_msg("Error while initializing daemon");
|
|
Packit |
8ea169 |
/* Inform parent that initialization failed */
|
|
Packit |
8ea169 |
if (!(opts & OPT_d))
|
|
Packit |
8ea169 |
kill(parent_pid, SIGINT);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
cleanup:
|
|
Packit |
8ea169 |
if (name_id > 0)
|
|
Packit |
8ea169 |
g_bus_unown_name (name_id);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Error or INT/TERM. Clean up, in reverse order.
|
|
Packit |
8ea169 |
* Take care to not undo things we did not do.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
dumpsocket_shutdown();
|
|
Packit |
8ea169 |
if (pidfile_created)
|
|
Packit |
8ea169 |
unlink(VAR_RUN_PIDFILE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (channel_id_signal_event > 0)
|
|
Packit |
8ea169 |
g_source_remove(channel_id_signal_event);
|
|
Packit |
8ea169 |
if (channel_signal)
|
|
Packit |
8ea169 |
g_io_channel_unref(channel_signal);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_inotify_watch_destroy(aiw);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (s_main_loop)
|
|
Packit |
8ea169 |
g_main_loop_unref(s_main_loop);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free_abrt_conf_data();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (s_sig_caught && s_sig_caught != SIGCHLD)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* We use TERM to stop abrtd, so not printing out error message. */
|
|
Packit |
8ea169 |
if (s_sig_caught != SIGTERM)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Got signal %d, exiting", s_sig_caught);
|
|
Packit |
8ea169 |
signal(s_sig_caught, SIG_DFL);
|
|
Packit |
8ea169 |
raise(s_sig_caught);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Exiting */
|
|
Packit |
8ea169 |
log_notice("Exiting");
|
|
Packit |
8ea169 |
return ret;
|
|
Packit |
8ea169 |
}
|