|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
Copyright (C) 2010 ABRT team
|
|
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 |
#include "problem_api.h"
|
|
Packit |
8ea169 |
#include "abrt_glib.h"
|
|
Packit |
8ea169 |
#include "libabrt.h"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Maximal length of backtrace. */
|
|
Packit |
8ea169 |
#define MAX_BACKTRACE_SIZE (1024*1024)
|
|
Packit |
8ea169 |
/* Amount of data received from one client for a message before reporting error. */
|
|
Packit |
8ea169 |
#define MAX_MESSAGE_SIZE (4*MAX_BACKTRACE_SIZE)
|
|
Packit |
8ea169 |
/* Maximal number of characters read from socket at once. */
|
|
Packit |
8ea169 |
#define INPUT_BUFFER_SIZE (8*1024)
|
|
Packit |
8ea169 |
/* We exit after this many seconds */
|
|
Packit |
8ea169 |
#define TIMEOUT 10
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define ABRT_SERVER_EVENT_ENV "ABRT_SERVER_PID"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
Unix socket in ABRT daemon for creating new dump directories.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
Why to use socket for creating dump dirs? Security. When a Python
|
|
Packit |
8ea169 |
script throws unexpected exception, ABRT handler catches it, running
|
|
Packit |
8ea169 |
as a part of that broken Python application. The application is running
|
|
Packit |
8ea169 |
with certain SELinux privileges, for example it can not execute other
|
|
Packit |
8ea169 |
programs, or to create files in /var/cache or anything else required
|
|
Packit |
8ea169 |
to properly fill a problem directory. Adding these privileges to every
|
|
Packit |
8ea169 |
application would weaken the security.
|
|
Packit |
8ea169 |
The most suitable solution is for the Python application
|
|
Packit |
8ea169 |
to open a socket where ABRT daemon is listening, write all relevant
|
|
Packit |
8ea169 |
data to that socket, and close it. ABRT daemon handles the rest.
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
** Protocol
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
Initializing new dump:
|
|
Packit |
8ea169 |
open /var/run/abrt.socket
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
Providing dump data (hook writes to the socket):
|
|
Packit |
8ea169 |
MANDATORY ITEMS:
|
|
Packit |
8ea169 |
-> "PID="
|
|
Packit |
8ea169 |
number 0 - PID_MAX (/proc/sys/kernel/pid_max)
|
|
Packit |
8ea169 |
\0
|
|
Packit |
8ea169 |
-> "EXECUTABLE="
|
|
Packit |
8ea169 |
string
|
|
Packit |
8ea169 |
\0
|
|
Packit |
8ea169 |
-> "BACKTRACE="
|
|
Packit |
8ea169 |
string
|
|
Packit |
8ea169 |
\0
|
|
Packit |
8ea169 |
-> "ANALYZER="
|
|
Packit |
8ea169 |
string
|
|
Packit |
8ea169 |
\0
|
|
Packit |
8ea169 |
-> "BASENAME="
|
|
Packit |
8ea169 |
string (no slashes)
|
|
Packit |
8ea169 |
\0
|
|
Packit |
8ea169 |
-> "REASON="
|
|
Packit |
8ea169 |
string
|
|
Packit |
8ea169 |
\0
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
You can send more messages using the same KEY=value format.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int g_signal_pipe[2];
|
|
Packit |
8ea169 |
static struct ns_ids g_ns_ids;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct waiting_context
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
GMainLoop *main_loop;
|
|
Packit |
8ea169 |
const char *dirname;
|
|
Packit |
8ea169 |
int retcode;
|
|
Packit |
8ea169 |
enum abrt_daemon_reply
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
ABRT_CONTINUE,
|
|
Packit |
8ea169 |
ABRT_INTERRUPT,
|
|
Packit |
8ea169 |
} reply;
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static unsigned total_bytes_read = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static pid_t client_pid = (pid_t)-1L;
|
|
Packit |
8ea169 |
static uid_t client_uid = (uid_t)-1L;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void
|
|
Packit |
8ea169 |
handle_signal(int signo)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
int save_errno = errno;
|
|
Packit |
8ea169 |
uint8_t sig_caught = signo;
|
|
Packit |
8ea169 |
if (write(g_signal_pipe[1], &sig_caught, 1))
|
|
Packit |
8ea169 |
/* we ignore result, if () shuts up stupid compiler */;
|
|
Packit |
8ea169 |
errno = save_errno;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static gboolean
|
|
Packit |
8ea169 |
handle_signal_pipe_cb(GIOChannel *gio, GIOCondition condition, gpointer user_data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
gsize len = 0;
|
|
Packit |
8ea169 |
uint8_t signals[2];
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
for (;;)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
GError *error = NULL;
|
|
Packit |
8ea169 |
GIOStatus stat = g_io_channel_read_chars(gio, (void *)signals, sizeof(signals), &len, NULL);
|
|
Packit |
8ea169 |
if (stat == G_IO_STATUS_ERROR)
|
|
Packit |
8ea169 |
error_msg_and_die(_("Can't read from gio channel: '%s'"), error ? error->message : "");
|
|
Packit |
8ea169 |
if (stat == G_IO_STATUS_EOF)
|
|
Packit |
8ea169 |
return FALSE; /* Remove this GLib source */
|
|
Packit |
8ea169 |
if (stat == G_IO_STATUS_AGAIN)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* G_IO_STATUS_NORMAL */
|
|
Packit |
8ea169 |
for (unsigned signo = 0; signo < len; ++signo)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* we did receive a signal */
|
|
Packit |
8ea169 |
struct waiting_context *context = (struct waiting_context *)user_data;
|
|
Packit |
8ea169 |
log_debug("Got signal %d through signal pipe", signals[signo]);
|
|
Packit |
8ea169 |
switch (signals[signo])
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
case SIGUSR1: context->reply = ABRT_CONTINUE; break;
|
|
Packit |
8ea169 |
case SIGINT: context->reply = ABRT_INTERRUPT; break;
|
|
Packit |
8ea169 |
default:
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Bug - aborting - unsupported signal: %d", signals[signo]);
|
|
Packit |
8ea169 |
abort();
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_main_loop_quit(context->main_loop);
|
|
Packit |
8ea169 |
return FALSE; /* remove this event */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return TRUE; /* "please don't remove this event" */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Remove dump dir */
|
|
Packit |
8ea169 |
static int delete_path(const char *dump_dir_name)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* If doesn't start with "g_settings_dump_location/"... */
|
|
Packit |
8ea169 |
if (!dir_is_in_dump_location(dump_dir_name))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Then refuse to operate on it (someone is attacking us??) */
|
|
Packit |
8ea169 |
error_msg("Bad problem directory name '%s', should start with: '%s'", dump_dir_name, g_settings_dump_location);
|
|
Packit |
8ea169 |
return 400; /* Bad Request */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (!dir_has_correct_permissions(dump_dir_name, DD_PERM_DAEMONS))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Problem directory '%s' has wrong owner or group", dump_dir_name);
|
|
Packit |
8ea169 |
return 400; /* */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_FD_ONLY);
|
|
Packit |
8ea169 |
if (dd == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
perror_msg("Can't open problem directory '%s'", dump_dir_name);
|
|
Packit |
8ea169 |
return 400;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (!dd_accessible_by_uid(dd, client_uid))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
if (errno == ENOTDIR)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Path '%s' isn't problem directory", dump_dir_name);
|
|
Packit |
8ea169 |
return 404; /* Not Found */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
error_msg("Problem directory '%s' can't be accessed by user with uid %ld", dump_dir_name, (long)client_uid);
|
|
Packit |
8ea169 |
return 403; /* Forbidden */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd = dd_fdopendir(dd, /*flags:*/ 0);
|
|
Packit |
8ea169 |
if (dd)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (dd_delete(dd) != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Failed to delete problem directory '%s'", dump_dir_name);
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
return 400;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return 0; /* success */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *event_name, int *fdp)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *args[9];
|
|
Packit |
8ea169 |
args[0] = (char *) LIBEXEC_DIR"/abrt-handle-event";
|
|
Packit |
8ea169 |
/* Do not forward ASK_* messages to parent*/
|
|
Packit |
8ea169 |
args[1] = (char *) "-i";
|
|
Packit |
8ea169 |
args[2] = (char *) "--nice";
|
|
Packit |
8ea169 |
args[3] = (char *) "10";
|
|
Packit |
8ea169 |
args[4] = (char *) "-e";
|
|
Packit |
8ea169 |
args[5] = (char *) event_name;
|
|
Packit |
8ea169 |
args[6] = (char *) "--";
|
|
Packit |
8ea169 |
args[7] = (char *) dump_dir_name;
|
|
Packit |
8ea169 |
args[8] = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int pipeout[2];
|
|
Packit |
8ea169 |
int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | EXECFLG_ERR2OUT;
|
|
Packit |
8ea169 |
VERB1 flags &= ~EXECFLG_QUIET;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *env_vec[3];
|
|
Packit |
8ea169 |
/* Intercept ASK_* messages in Client API -> don't wait for user response */
|
|
Packit |
8ea169 |
env_vec[0] = xstrdup("REPORT_CLIENT_NONINTERACTIVE=1");
|
|
Packit |
8ea169 |
env_vec[1] = xasprintf("%s=%d", ABRT_SERVER_EVENT_ENV, getpid());
|
|
Packit |
8ea169 |
env_vec[2] = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
pid_t child = fork_execv_on_steroids(flags, args, pipeout,
|
|
Packit |
8ea169 |
env_vec, /*dir:*/ NULL,
|
|
Packit |
8ea169 |
/*uid(unused):*/ 0);
|
|
Packit |
8ea169 |
if (fdp)
|
|
Packit |
8ea169 |
*fdp = pipeout[0];
|
|
Packit |
8ea169 |
return child;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int problem_dump_dir_was_provoked_by_abrt_event(struct dump_dir *dd, char **provoker)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *env_var = NULL;
|
|
Packit |
8ea169 |
const int r = dd_get_env_variable(dd, ABRT_SERVER_EVENT_ENV, &env_var);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Dump directory doesn't contain the environ file */
|
|
Packit |
8ea169 |
if (r == -ENOENT)
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (provoker != NULL)
|
|
Packit |
8ea169 |
*provoker = env_var;
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
free(env_var);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return env_var != NULL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int
|
|
Packit |
8ea169 |
emit_new_problem_signal(gpointer data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct waiting_context *context = (struct waiting_context *)data;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const size_t wrote = fprintf(stderr, "NEW_PROBLEM_DETECTED: %s\n", context->dirname);
|
|
Packit |
8ea169 |
fflush(stderr);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (wrote <= 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Failed to communicate with the daemon");
|
|
Packit |
8ea169 |
context->retcode = 503;
|
|
Packit |
8ea169 |
g_main_loop_quit(context->main_loop);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_notice("Emitted new problem signal, waiting for SIGUSR1|SIGINT");
|
|
Packit |
8ea169 |
return FALSE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct response
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
int code;
|
|
Packit |
8ea169 |
char *message;
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define RESPONSE_SETTER(r, c, m) \
|
|
Packit |
8ea169 |
do { if (r != NULL) { r->message = m; r->code = c; } else { free(m); }} while (0)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define RESPONSE_RETURN(r, c ,m) \
|
|
Packit |
8ea169 |
do { RESPONSE_SETTER(r, c, m); \
|
|
Packit |
8ea169 |
return c; } while (0)
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int run_post_create(const char *dirname, struct response *resp)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* If doesn't start with "g_settings_dump_location/"... */
|
|
Packit |
8ea169 |
if (!dir_is_in_dump_location(dirname))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Then refuse to operate on it (someone is attacking us??) */
|
|
Packit |
8ea169 |
error_msg("Bad problem directory name '%s', should start with: '%s'", dirname, g_settings_dump_location);
|
|
Packit |
8ea169 |
RESPONSE_RETURN(resp, 400, NULL);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (!dir_has_correct_permissions(dirname, DD_PERM_EVENTS))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Problem directory '%s' has wrong owner or group", dirname);
|
|
Packit |
8ea169 |
RESPONSE_RETURN(resp, 400, NULL);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
/* Check completness */
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *provoker = NULL;
|
|
Packit |
8ea169 |
const bool event_dir = dd && problem_dump_dir_was_provoked_by_abrt_event(dd, &provoker);
|
|
Packit |
8ea169 |
if (event_dir)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (g_settings_debug_level == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Removing problem provoked by ABRT(pid:%s): '%s'", provoker, dirname);
|
|
Packit |
8ea169 |
dd_delete(dd);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *dumpdir = NULL;
|
|
Packit |
8ea169 |
char *event = NULL;
|
|
Packit |
8ea169 |
char *reason = NULL;
|
|
Packit |
8ea169 |
char *cmdline = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Ignore errors */
|
|
Packit |
8ea169 |
dd_get_env_variable(dd, "DUMP_DIR", &dumpdir);
|
|
Packit |
8ea169 |
dd_get_env_variable(dd, "EVENT", &event);
|
|
Packit |
8ea169 |
reason = dd_load_text(dd, FILENAME_REASON);
|
|
Packit |
8ea169 |
cmdline = dd_load_text(dd, FILENAME_CMDLINE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
error_msg("ABRT_SERVER_PID=%s;DUMP_DIR='%s';EVENT='%s';REASON='%s';CMDLINE='%s'",
|
|
Packit |
8ea169 |
provoker, dumpdir, event, reason, cmdline);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(provoker);
|
|
Packit |
8ea169 |
return 400;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const bool complete = dd && problem_dump_dir_is_complete(dd);
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
if (complete)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Problem directory '%s' has already been processed", dirname);
|
|
Packit |
8ea169 |
RESPONSE_RETURN(resp, 403, NULL);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* The post-create event cannot be run concurrently for more problem
|
|
Packit |
8ea169 |
* directories. The problem is in searching for duplicates process
|
|
Packit |
8ea169 |
* in case when two concurrently processed directories are duplicates
|
|
Packit |
8ea169 |
* of each other. Both of the directories are marked as duplicates
|
|
Packit |
8ea169 |
* of each other and are deleted.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
log_debug("Creating glib main loop");
|
|
Packit |
8ea169 |
struct waiting_context context = {0};
|
|
Packit |
8ea169 |
context.main_loop = g_main_loop_new(NULL, FALSE);
|
|
Packit |
8ea169 |
context.dirname = strrchr(dirname, '/') + 1;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_debug("Setting up a signal handler");
|
|
Packit |
8ea169 |
/* Set up signal pipe */
|
|
Packit |
8ea169 |
xpipe(g_signal_pipe);
|
|
Packit |
8ea169 |
close_on_exec_on(g_signal_pipe[0]);
|
|
Packit |
8ea169 |
close_on_exec_on(g_signal_pipe[1]);
|
|
Packit |
8ea169 |
ndelay_on(g_signal_pipe[0]);
|
|
Packit |
8ea169 |
ndelay_on(g_signal_pipe[1]);
|
|
Packit |
8ea169 |
signal(SIGUSR1, handle_signal);
|
|
Packit |
8ea169 |
signal(SIGINT, handle_signal);
|
|
Packit |
8ea169 |
GIOChannel *channel_signal = abrt_gio_channel_unix_new(g_signal_pipe[0]);
|
|
Packit |
8ea169 |
g_io_add_watch(channel_signal, G_IO_IN | G_IO_PRI, handle_signal_pipe_cb, &context);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_idle_add(emit_new_problem_signal, &context);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_main_loop_run(context.main_loop);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_main_loop_unref(context.main_loop);
|
|
Packit |
8ea169 |
g_io_channel_unref(channel_signal);
|
|
Packit |
8ea169 |
close(g_signal_pipe[1]);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_notice("Waiting finished");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (context.retcode != 0)
|
|
Packit |
8ea169 |
RESPONSE_RETURN(resp, context.retcode, NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (context.reply != ABRT_CONTINUE)
|
|
Packit |
8ea169 |
/* The only reason for the interruption is removed problem directory */
|
|
Packit |
8ea169 |
RESPONSE_RETURN(resp, 413, NULL);
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* The post-create event synchronization done.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int child_stdout_fd;
|
|
Packit |
8ea169 |
int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *dup_of_dir = NULL;
|
|
Packit |
8ea169 |
struct strbuf *cmd_output = strbuf_new();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
bool child_is_post_create = 1; /* else it is a notify child */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
read_child_output:
|
|
Packit |
8ea169 |
//log_warning("Reading from event fd %d", child_stdout_fd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Read streamed data and split lines */
|
|
Packit |
8ea169 |
for (;;)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char buf[250]; /* usually we get one line, no need to have big buf */
|
|
Packit |
8ea169 |
errno = 0;
|
|
Packit |
8ea169 |
int r = safe_read(child_stdout_fd, buf, sizeof(buf) - 1);
|
|
Packit |
8ea169 |
if (r <= 0)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
buf[r] = '\0';
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* split lines in the current buffer */
|
|
Packit |
8ea169 |
char *raw = buf;
|
|
Packit |
8ea169 |
char *newline;
|
|
Packit |
8ea169 |
while ((newline = strchr(raw, '\n')) != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
*newline = '\0';
|
|
Packit |
8ea169 |
strbuf_append_str(cmd_output, raw);
|
|
Packit |
8ea169 |
char *msg = cmd_output->buf;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (child_is_post_create
|
|
Packit |
8ea169 |
&& prefixcmp(msg, "DUP_OF_DIR: ") == 0
|
|
Packit |
8ea169 |
) {
|
|
Packit |
8ea169 |
free(dup_of_dir);
|
|
Packit |
8ea169 |
dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: "));
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
log_warning("%s", msg);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
strbuf_clear(cmd_output);
|
|
Packit |
8ea169 |
/* jump to next line */
|
|
Packit |
8ea169 |
raw = newline + 1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* beginning of next line. the line continues by next read */
|
|
Packit |
8ea169 |
strbuf_append_str(cmd_output, raw);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* EOF/error */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Wait for child to actually exit, collect status */
|
|
Packit |
8ea169 |
int status = 0;
|
|
Packit |
8ea169 |
if (safe_waitpid(child_pid, &status, 0) <= 0)
|
|
Packit |
8ea169 |
/* should not happen */
|
|
Packit |
8ea169 |
perror_msg("waitpid(%d)", child_pid);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* If it was a "notify[-dup]" event, then we're done */
|
|
Packit |
8ea169 |
if (!child_is_post_create)
|
|
Packit |
8ea169 |
goto ret;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* exit 0 means "this is a good, non-dup dir" */
|
|
Packit |
8ea169 |
/* exit with 1 + "DUP_OF_DIR: dir" string => dup */
|
|
Packit |
8ea169 |
if (status != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (WIFSIGNALED(status))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("'post-create' on '%s' killed by signal %d",
|
|
Packit |
8ea169 |
dirname, WTERMSIG(status));
|
|
Packit |
8ea169 |
goto delete_bad_dir;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
/* else: it is WIFEXITED(status) */
|
|
Packit |
8ea169 |
if (!dup_of_dir)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("'post-create' on '%s' exited with %d",
|
|
Packit |
8ea169 |
dirname, WEXITSTATUS(status));
|
|
Packit |
8ea169 |
goto delete_bad_dir;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *work_dir = (dup_of_dir ? dup_of_dir : dirname);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Load problem_data (from the *first dir* if this one is a dup) */
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_opendir(work_dir, /*flags:*/ 0);
|
|
Packit |
8ea169 |
if (!dd)
|
|
Packit |
8ea169 |
/* dd_opendir already emitted error msg */
|
|
Packit |
8ea169 |
goto delete_bad_dir;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Update count */
|
|
Packit |
8ea169 |
char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT);
|
|
Packit |
8ea169 |
unsigned long count = strtoul(count_str, NULL, 10);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Don't increase crash count if we are working with newly uploaded
|
|
Packit |
8ea169 |
* directory (remote crash) which already has its crash count set.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
if ((status != 0 && dup_of_dir) || count == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
count++;
|
|
Packit |
8ea169 |
char new_count_str[sizeof(long)*3 + 2];
|
|
Packit |
8ea169 |
sprintf(new_count_str, "%lu", count);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_COUNT, new_count_str);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* This condition can be simplified to either
|
|
Packit |
8ea169 |
* (status * != 0 && * dup_of_dir) or (count == 1). But the
|
|
Packit |
8ea169 |
* chosen form is much more reliable and safe. We must not call
|
|
Packit |
8ea169 |
* dd_opendir() to locked dd otherwise we go into a deadlock.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
if (strcmp(dd->dd_dirname, dirname) != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Update the last occurrence file by the time file of the new problem */
|
|
Packit |
8ea169 |
struct dump_dir *new_dd = dd_opendir(dirname, DD_OPEN_READONLY);
|
|
Packit |
8ea169 |
char *last_ocr = NULL;
|
|
Packit |
8ea169 |
if (new_dd)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* TIME must exists in a valid dump directory but we don't want to die
|
|
Packit |
8ea169 |
* due to broken duplicated dump directory */
|
|
Packit |
8ea169 |
last_ocr = dd_load_text_ext(new_dd, FILENAME_TIME,
|
|
Packit |
8ea169 |
DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT);
|
|
Packit |
8ea169 |
dd_close(new_dd);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{ /* dd_opendir() already produced a message with good information about failure */
|
|
Packit |
8ea169 |
error_msg("Can't read the last occurrence file from the new dump directory.");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (!last_ocr)
|
|
Packit |
8ea169 |
{ /* the new dump directory may lie in the dump location for some time */
|
|
Packit |
8ea169 |
log_warning("Using current time for the last occurrence file which may be incorrect.");
|
|
Packit |
8ea169 |
time_t t = time(NULL);
|
|
Packit |
8ea169 |
last_ocr = xasprintf("%lu", (long)t);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_LAST_OCCURRENCE, last_ocr);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free(last_ocr);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Reset mode/uig/gid to correct values for all files created by event run */
|
|
Packit |
8ea169 |
dd_sanitize_mode_and_owner(dd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (!dup_of_dir)
|
|
Packit |
8ea169 |
log_notice("New problem directory %s, processing", work_dir);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Deleting problem directory %s (dup of %s)",
|
|
Packit |
8ea169 |
strrchr(dirname, '/') + 1,
|
|
Packit |
8ea169 |
strrchr(dup_of_dir, '/') + 1);
|
|
Packit |
8ea169 |
delete_dump_dir(dirname);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Run "notify[-dup]" event */
|
|
Packit |
8ea169 |
int fd;
|
|
Packit |
8ea169 |
child_pid = spawn_event_handler_child(
|
|
Packit |
8ea169 |
work_dir,
|
|
Packit |
8ea169 |
(dup_of_dir ? "notify-dup" : "notify"),
|
|
Packit |
8ea169 |
&fd
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
//log_warning("Started notify, fd %d -> %d", fd, child_stdout_fd);
|
|
Packit |
8ea169 |
xmove_fd(fd, child_stdout_fd);
|
|
Packit |
8ea169 |
child_is_post_create = 0;
|
|
Packit |
8ea169 |
if (dup_of_dir)
|
|
Packit |
8ea169 |
RESPONSE_SETTER(resp, 303, dup_of_dir);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
RESPONSE_SETTER(resp, 200, NULL);
|
|
Packit |
8ea169 |
free(dup_of_dir);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
dup_of_dir = NULL;
|
|
Packit |
8ea169 |
strbuf_clear(cmd_output);
|
|
Packit |
8ea169 |
goto read_child_output;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
delete_bad_dir:
|
|
Packit |
8ea169 |
log_warning("Deleting problem directory '%s'", dirname);
|
|
Packit |
8ea169 |
delete_dump_dir(dirname);
|
|
Packit |
8ea169 |
/* TODO - better code to allow detection on client's side */
|
|
Packit |
8ea169 |
RESPONSE_SETTER(resp, 403, NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
ret:
|
|
Packit |
8ea169 |
strbuf_free(cmd_output);
|
|
Packit |
8ea169 |
free(dup_of_dir);
|
|
Packit |
8ea169 |
close(child_stdout_fd);
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Create a new problem directory from client session.
|
|
Packit |
8ea169 |
* Caller must ensure that all fields in struct client
|
|
Packit |
8ea169 |
* are properly filled.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static int create_problem_dir(GHashTable *problem_info, unsigned pid)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Exit if free space is less than 1/4 of MaxCrashReportsSize */
|
|
Packit |
8ea169 |
if (g_settings_nMaxCrashReportsSize > 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (low_free_space(g_settings_nMaxCrashReportsSize, g_settings_dump_location))
|
|
Packit |
8ea169 |
exit(1);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Create temp directory with the problem data.
|
|
Packit |
8ea169 |
* This directory is renamed to final directory name after
|
|
Packit |
8ea169 |
* all files have been stored into it.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
gchar *dir_basename = g_hash_table_lookup(problem_info, "basename");
|
|
Packit |
8ea169 |
if (!dir_basename)
|
|
Packit |
8ea169 |
dir_basename = g_hash_table_lookup(problem_info, FILENAME_TYPE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *path = xasprintf("%s/%s-%s-%u.new",
|
|
Packit |
8ea169 |
g_settings_dump_location,
|
|
Packit |
8ea169 |
dir_basename,
|
|
Packit |
8ea169 |
iso_date_string(NULL),
|
|
Packit |
8ea169 |
pid);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* This item is useless, don't save it */
|
|
Packit |
8ea169 |
g_hash_table_remove(problem_info, "basename");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* No need to check the path length, as all variables used are limited,
|
|
Packit |
8ea169 |
* and dd_create() fails if the path is too long.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
struct dump_dir *dd = dd_create(path, /*fs owner*/0, DEFAULT_DUMP_DIR_MODE);
|
|
Packit |
8ea169 |
if (!dd)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg_and_die("Error creating problem directory '%s'", path);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const int proc_dir_fd = open_proc_pid_dir(pid);
|
|
Packit |
8ea169 |
char *rootdir = NULL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (proc_dir_fd < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
pwarn_msg("Cannot open /proc/%d:", pid);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else if (process_has_own_root_at(proc_dir_fd))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Obtain the root directory path only if process' root directory is
|
|
Packit |
8ea169 |
* not the same as the init's root directory
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
rootdir = get_rootdir_at(proc_dir_fd);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Reading data from an arbitrary root directory is not secure. */
|
|
Packit |
8ea169 |
if (proc_dir_fd >= 0 && g_settings_explorechroots)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char proc_pid_root[sizeof("/proc/[pid]/root") + sizeof(pid_t) * 3];
|
|
Packit |
8ea169 |
const size_t w = snprintf(proc_pid_root, sizeof(proc_pid_root), "/proc/%d/root", pid);
|
|
Packit |
8ea169 |
assert(sizeof(proc_pid_root) > w);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Yes, test 'rootdir' but use 'source_filename' because 'rootdir' can
|
|
Packit |
8ea169 |
* be '/' for a process with own namespace. 'source_filename' is /proc/[pid]/root. */
|
|
Packit |
8ea169 |
dd_create_basic_files(dd, client_uid, (rootdir != NULL) ? proc_pid_root : NULL);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_create_basic_files(dd, client_uid, NULL);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (proc_dir_fd >= 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Obtain and save the command line. */
|
|
Packit |
8ea169 |
char *cmdline = get_cmdline_at(proc_dir_fd);
|
|
Packit |
8ea169 |
if (cmdline)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CMDLINE, cmdline);
|
|
Packit |
8ea169 |
free(cmdline);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Obtain and save the environment variables. */
|
|
Packit |
8ea169 |
char *environ = get_environ_at(proc_dir_fd);
|
|
Packit |
8ea169 |
if (environ)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ENVIRON, environ);
|
|
Packit |
8ea169 |
free(environ);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_copy_file_at(dd, FILENAME_CGROUP, proc_dir_fd, "cgroup");
|
|
Packit |
8ea169 |
dd_copy_file_at(dd, FILENAME_MOUNTINFO, proc_dir_fd, "mountinfo");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
FILE *open_fds = dd_open_item_file(dd, FILENAME_OPEN_FDS, O_RDWR);
|
|
Packit |
8ea169 |
if (open_fds)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (dump_fd_info_at(proc_dir_fd, open_fds) < 0)
|
|
Packit |
8ea169 |
dd_delete_item(dd, FILENAME_OPEN_FDS);
|
|
Packit |
8ea169 |
fclose(open_fds);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const int init_proc_dir_fd = open_proc_pid_dir(1);
|
|
Packit |
8ea169 |
FILE *namespaces = dd_open_item_file(dd, FILENAME_NAMESPACES, O_RDWR);
|
|
Packit |
8ea169 |
if (namespaces && init_proc_dir_fd >= 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (dump_namespace_diff_at(init_proc_dir_fd, proc_dir_fd, namespaces) < 0)
|
|
Packit |
8ea169 |
dd_delete_item(dd, FILENAME_NAMESPACES);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (init_proc_dir_fd >= 0)
|
|
Packit |
8ea169 |
close(init_proc_dir_fd);
|
|
Packit |
8ea169 |
if (namespaces)
|
|
Packit |
8ea169 |
fclose(namespaces);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* The process's root directory isn't the same as the init's root
|
|
Packit |
8ea169 |
* directory. */
|
|
Packit |
8ea169 |
if (rootdir)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (strcmp(rootdir, "/") == 0)
|
|
Packit |
8ea169 |
{ /* We are dealing containerized process because root's
|
|
Packit |
8ea169 |
* directory path is '/' and that means that the process
|
|
Packit |
8ea169 |
* has mounted its own root.
|
|
Packit |
8ea169 |
* Seriously, it is possible if the process is running in its
|
|
Packit |
8ea169 |
* own MOUNT namespaces.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
log_debug("Process %d is considered to be containerized", pid);
|
|
Packit |
8ea169 |
pid_t container_pid;
|
|
Packit |
8ea169 |
if (get_pid_of_container_at(proc_dir_fd, &container_pid) == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *container_cmdline = get_cmdline(container_pid);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_CONTAINER_CMDLINE, container_cmdline);
|
|
Packit |
8ea169 |
free(container_cmdline);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{ /* We are dealing chrooted process. */
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ROOTDIR, rootdir);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
close(proc_dir_fd);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
free(rootdir);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Store id of the user whose application crashed. */
|
|
Packit |
8ea169 |
char uid_str[sizeof(long) * 3 + 2];
|
|
Packit |
8ea169 |
sprintf(uid_str, "%lu", (long)client_uid);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_UID, uid_str);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
GHashTableIter iter;
|
|
Packit |
8ea169 |
gpointer gpkey;
|
|
Packit |
8ea169 |
gpointer gpvalue;
|
|
Packit |
8ea169 |
g_hash_table_iter_init(&iter, problem_info);
|
|
Packit |
8ea169 |
while (g_hash_table_iter_next(&iter, &gpkey, &gpvalue))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_save_text(dd, (gchar *) gpkey, (gchar *) gpvalue);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Not needing it anymore */
|
|
Packit |
8ea169 |
g_hash_table_destroy(problem_info);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Move the completely created problem directory
|
|
Packit |
8ea169 |
* to final directory.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
char *newpath = xstrndup(path, strlen(path) - strlen(".new"));
|
|
Packit |
8ea169 |
if (rename(path, newpath) == 0)
|
|
Packit |
8ea169 |
strcpy(path, newpath);
|
|
Packit |
8ea169 |
free(newpath);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_notice("Saved problem directory of pid %u to '%s'", pid, path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* We let the peer know that problem dir was created successfully
|
|
Packit |
8ea169 |
* _before_ we run potentially long-running post-create.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
printf("HTTP/1.1 201 Created\r\n\r\n");
|
|
Packit |
8ea169 |
fflush(NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Closing STDIN_FILENO (abrtd duped the socket to stdin and stdout) and
|
|
Packit |
8ea169 |
* not-replacing it with something else to let abrt-server die on reading
|
|
Packit |
8ea169 |
* from invalid stdin - to catch bugs. */
|
|
Packit |
8ea169 |
close(STDIN_FILENO);
|
|
Packit |
8ea169 |
close(STDOUT_FILENO);
|
|
Packit |
8ea169 |
xdup2(STDERR_FILENO, STDOUT_FILENO); /* paranoia: don't leave stdout fd closed */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Trim old problem directories if necessary */
|
|
Packit |
8ea169 |
if (g_settings_nMaxCrashReportsSize > 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
trim_problem_dirs(g_settings_dump_location, g_settings_nMaxCrashReportsSize * (double)(1024*1024), path);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
run_post_create(path, NULL);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* free(path); */
|
|
Packit |
8ea169 |
exit(0);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static gboolean key_value_ok(gchar *key, gchar *value)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *i;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* check key, it has to be valid filename and will end up in the
|
|
Packit |
8ea169 |
* bugzilla */
|
|
Packit |
8ea169 |
for (i = key; *i != 0; i++)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!isalpha(*i) && (*i != '-') && (*i != '_') && (*i != ' '))
|
|
Packit |
8ea169 |
return FALSE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* check value of 'basename', it has to be valid non-hidden directory
|
|
Packit |
8ea169 |
* name */
|
|
Packit |
8ea169 |
if (strcmp(key, "basename") == 0
|
|
Packit |
8ea169 |
|| strcmp(key, FILENAME_TYPE) == 0
|
|
Packit |
8ea169 |
)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!str_is_correct_filename(value))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Value of '%s' ('%s') is not a valid directory name",
|
|
Packit |
8ea169 |
key, value);
|
|
Packit |
8ea169 |
return FALSE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return allowed_new_user_problem_entry(client_uid, key, value);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Handles a message received from client over socket. */
|
|
Packit |
8ea169 |
static void process_message(GHashTable *problem_info, char *message)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
gchar *key, *value;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
value = strchr(message, '=');
|
|
Packit |
8ea169 |
if (value)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
key = g_ascii_strdown(message, value - message); /* result is malloced */
|
|
Packit |
8ea169 |
//TODO: is it ok? it uses g_malloc, not malloc!
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
value++;
|
|
Packit |
8ea169 |
if (key_value_ok(key, value))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (strcmp(key, FILENAME_UID) == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Ignoring value of %s, will be determined later",
|
|
Packit |
8ea169 |
FILENAME_UID);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
g_hash_table_insert(problem_info, key, xstrdup(value));
|
|
Packit |
8ea169 |
/* Prevent freeing key later: */
|
|
Packit |
8ea169 |
key = NULL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* should use error_msg_and_die() here? */
|
|
Packit |
8ea169 |
error_msg("Invalid key or value format: %s", message);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
free(key);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* should use error_msg_and_die() here? */
|
|
Packit |
8ea169 |
error_msg("Invalid message format: '%s'", message);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void die_if_data_is_missing(GHashTable *problem_info)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
gboolean missing_data = FALSE;
|
|
Packit |
8ea169 |
gchar **pstring;
|
|
Packit |
8ea169 |
static const gchar *const needed[] = {
|
|
Packit |
8ea169 |
FILENAME_TYPE,
|
|
Packit |
8ea169 |
FILENAME_REASON,
|
|
Packit |
8ea169 |
/* FILENAME_BACKTRACE, - ECC errors have no such elements */
|
|
Packit |
8ea169 |
/* FILENAME_EXECUTABLE, */
|
|
Packit |
8ea169 |
NULL
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
for (pstring = (gchar**) needed; *pstring; pstring++)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!g_hash_table_lookup(problem_info, *pstring))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Element '%s' is missing", *pstring);
|
|
Packit |
8ea169 |
missing_data = TRUE;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (missing_data)
|
|
Packit |
8ea169 |
error_msg_and_die("Some data is missing, aborting");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Takes hash table, looks for key FILENAME_PID and tries to convert its value
|
|
Packit |
8ea169 |
* to int.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
unsigned convert_pid(GHashTable *problem_info)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
long ret;
|
|
Packit |
8ea169 |
gchar *pid_str = (gchar *) g_hash_table_lookup(problem_info, FILENAME_PID);
|
|
Packit |
8ea169 |
char *err_pos;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (!pid_str)
|
|
Packit |
8ea169 |
error_msg_and_die("PID data is missing, aborting");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
errno = 0;
|
|
Packit |
8ea169 |
ret = strtol(pid_str, &err_pos, 10);
|
|
Packit |
8ea169 |
if (errno || pid_str == err_pos || *err_pos != '\0'
|
|
Packit |
8ea169 |
|| ret > UINT_MAX || ret < 1)
|
|
Packit |
8ea169 |
error_msg_and_die("Malformed or out-of-range PID number: '%s'", pid_str);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return (unsigned) ret;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int perform_http_xact(struct response *rsp)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* use free instead of g_free so that we can use xstr* functions from
|
|
Packit |
8ea169 |
* libreport/lib/xfuncs.c
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
GHashTable *problem_info = g_hash_table_new_full(g_str_hash, g_str_equal,
|
|
Packit |
8ea169 |
free, free);
|
|
Packit |
8ea169 |
/* Read header */
|
|
Packit |
8ea169 |
char *body_start = NULL;
|
|
Packit |
8ea169 |
char *messagebuf_data = NULL;
|
|
Packit |
8ea169 |
unsigned messagebuf_len = 0;
|
|
Packit |
8ea169 |
/* Loop until EOF/error/timeout/end_of_header */
|
|
Packit |
8ea169 |
while (1)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
|
|
Packit |
8ea169 |
char *p = messagebuf_data + messagebuf_len;
|
|
Packit |
8ea169 |
int rd = read(STDIN_FILENO, p, INPUT_BUFFER_SIZE);
|
|
Packit |
8ea169 |
if (rd < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (errno == EINTR) /* SIGALRM? */
|
|
Packit |
8ea169 |
error_msg_and_die("Timed out");
|
|
Packit |
8ea169 |
perror_msg_and_die("read");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (rd == 0)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_debug("Received %u bytes of data", rd);
|
|
Packit |
8ea169 |
messagebuf_len += rd;
|
|
Packit |
8ea169 |
total_bytes_read += rd;
|
|
Packit |
8ea169 |
if (total_bytes_read > MAX_MESSAGE_SIZE)
|
|
Packit |
8ea169 |
error_msg_and_die("Message is too long, aborting");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Check whether we see end of header */
|
|
Packit |
8ea169 |
/* Note: we support both [\r]\n\r\n and \n\n */
|
|
Packit |
8ea169 |
char *past_end = messagebuf_data + messagebuf_len;
|
|
Packit |
8ea169 |
if (p > messagebuf_data+1)
|
|
Packit |
8ea169 |
p -= 2; /* start search from two last bytes in last read - they might be '\n\r' */
|
|
Packit |
8ea169 |
while (p < past_end)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
p = memchr(p, '\n', past_end - p);
|
|
Packit |
8ea169 |
if (!p)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
p++;
|
|
Packit |
8ea169 |
if (p >= past_end)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
if (*p == '\n'
|
|
Packit |
8ea169 |
|| (*p == '\r' && p+1 < past_end && p[1] == '\n')
|
|
Packit |
8ea169 |
) {
|
|
Packit |
8ea169 |
body_start = p + 1 + (*p == '\r');
|
|
Packit |
8ea169 |
*p = '\0';
|
|
Packit |
8ea169 |
goto found_end_of_header;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
} /* while (read) */
|
|
Packit |
8ea169 |
found_end_of_header: ;
|
|
Packit |
8ea169 |
log_debug("Request: %s", messagebuf_data);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Sanitize and analyze header.
|
|
Packit |
8ea169 |
* Header now is in messagebuf_data, NUL terminated string,
|
|
Packit |
8ea169 |
* with last empty line deleted (by placement of NUL).
|
|
Packit |
8ea169 |
* \r\n are not (yet) converted to \n, multi-line headers also
|
|
Packit |
8ea169 |
* not converted.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
/* First line must be "op<space>[http://host]/path<space>HTTP/n.n".
|
|
Packit |
8ea169 |
* <space> is exactly one space char.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
if (prefixcmp(messagebuf_data, "DELETE ") == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
messagebuf_data += strlen("DELETE ");
|
|
Packit |
8ea169 |
char *space = strchr(messagebuf_data, ' ');
|
|
Packit |
8ea169 |
if (!space || prefixcmp(space+1, "HTTP/") != 0)
|
|
Packit |
8ea169 |
return 400; /* Bad Request */
|
|
Packit |
8ea169 |
*space = '\0';
|
|
Packit |
8ea169 |
//decode_url(messagebuf_data); %20 => ' '
|
|
Packit |
8ea169 |
alarm(0);
|
|
Packit |
8ea169 |
return delete_path(messagebuf_data);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* We erroneously used "PUT /" to create new problems.
|
|
Packit |
8ea169 |
* POST is the correct request in this case:
|
|
Packit |
8ea169 |
* "PUT /" implies creation or replace of resource named "/"!
|
|
Packit |
8ea169 |
* Delete PUT in 2014.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
if (prefixcmp(messagebuf_data, "PUT ") != 0
|
|
Packit |
8ea169 |
&& prefixcmp(messagebuf_data, "POST ") != 0
|
|
Packit |
8ea169 |
) {
|
|
Packit |
8ea169 |
return 400; /* Bad Request */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
enum {
|
|
Packit |
8ea169 |
CREATION_NOTIFICATION,
|
|
Packit |
8ea169 |
CREATION_REQUEST,
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
int url_type;
|
|
Packit |
8ea169 |
char *url = skip_non_whitespace(messagebuf_data) + 1; /* skip "POST " */
|
|
Packit |
8ea169 |
if (prefixcmp(url, "/creation_notification ") == 0)
|
|
Packit |
8ea169 |
url_type = CREATION_NOTIFICATION;
|
|
Packit |
8ea169 |
else if (prefixcmp(url, "/ ") == 0)
|
|
Packit |
8ea169 |
url_type = CREATION_REQUEST;
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
return 400; /* Bad Request */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Read body */
|
|
Packit |
8ea169 |
if (!body_start)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_warning("Premature EOF detected, exiting");
|
|
Packit |
8ea169 |
return 400; /* Bad Request */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
messagebuf_len -= (body_start - messagebuf_data);
|
|
Packit |
8ea169 |
memmove(messagebuf_data, body_start, messagebuf_len);
|
|
Packit |
8ea169 |
log_debug("Body so far: %u bytes, '%s'", messagebuf_len, messagebuf_data);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Loop until EOF/error/timeout */
|
|
Packit |
8ea169 |
while (1)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (url_type == CREATION_REQUEST)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
while (1)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
unsigned len = strnlen(messagebuf_data, messagebuf_len);
|
|
Packit |
8ea169 |
if (len >= messagebuf_len)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
/* messagebuf has at least one NUL - process the line */
|
|
Packit |
8ea169 |
process_message(problem_info, messagebuf_data);
|
|
Packit |
8ea169 |
messagebuf_len -= (len + 1);
|
|
Packit |
8ea169 |
memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE + 1);
|
|
Packit |
8ea169 |
int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE);
|
|
Packit |
8ea169 |
if (rd < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (errno == EINTR) /* SIGALRM? */
|
|
Packit |
8ea169 |
error_msg_and_die("Timed out");
|
|
Packit |
8ea169 |
perror_msg_and_die("read");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
if (rd == 0)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
log_debug("Received %u bytes of data", rd);
|
|
Packit |
8ea169 |
messagebuf_len += rd;
|
|
Packit |
8ea169 |
total_bytes_read += rd;
|
|
Packit |
8ea169 |
if (total_bytes_read > MAX_MESSAGE_SIZE)
|
|
Packit |
8ea169 |
error_msg_and_die("Message is too long, aborting");
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Body received, EOF was seen. Don't let alarm to interrupt after this. */
|
|
Packit |
8ea169 |
alarm(0);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int ret = 0;
|
|
Packit |
8ea169 |
if (url_type == CREATION_NOTIFICATION)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (client_uid != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("UID=%ld is not authorized to trigger post-create processing", (long)client_uid);
|
|
Packit |
8ea169 |
ret = 403; /* Forbidden */
|
|
Packit |
8ea169 |
goto out;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
messagebuf_data[messagebuf_len] = '\0';
|
|
Packit |
8ea169 |
return run_post_create(messagebuf_data, rsp);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
die_if_data_is_missing(problem_info);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Save problem dir */
|
|
Packit |
8ea169 |
char *executable = g_hash_table_lookup(problem_info, FILENAME_EXECUTABLE);
|
|
Packit |
8ea169 |
if (executable)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *last_file = concat_path_file(g_settings_dump_location, "last-via-server");
|
|
Packit |
8ea169 |
int repeating_crash = check_recent_crash_file(last_file, executable);
|
|
Packit |
8ea169 |
free(last_file);
|
|
Packit |
8ea169 |
if (repeating_crash) /* Only pretend that we saved it */
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Not saving repeating crash in '%s'", executable);
|
|
Packit |
8ea169 |
goto out; /* ret is 0: "success" */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#if 0
|
|
Packit |
8ea169 |
//TODO:
|
|
Packit |
8ea169 |
/* At least it should generate local problem identifier UUID */
|
|
Packit |
8ea169 |
problem_data_add_basics(problem_info);
|
|
Packit |
8ea169 |
//...the problem being that problem_info here is not a problem_data_t!
|
|
Packit |
8ea169 |
#endif
|
|
Packit |
8ea169 |
unsigned pid = convert_pid(problem_info);
|
|
Packit |
8ea169 |
struct ns_ids client_ids;
|
|
Packit |
8ea169 |
if (get_ns_ids(client_pid, &client_ids) < 0)
|
|
Packit |
8ea169 |
error_msg_and_die("Cannot get peer's Namespaces from /proc/%d/ns", client_pid);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (client_ids.nsi_ids[PROC_NS_ID_PID] != g_ns_ids.nsi_ids[PROC_NS_ID_PID])
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_notice("Client is running in own PID Namespace, using PID %d instead of %d", client_pid, pid);
|
|
Packit |
8ea169 |
pid = client_pid;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
create_problem_dir(problem_info, pid);
|
|
Packit |
8ea169 |
/* does not return */
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
out:
|
|
Packit |
8ea169 |
g_hash_table_destroy(problem_info);
|
|
Packit |
8ea169 |
return ret; /* Used as HTTP response code */
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void dummy_handler(int sig_unused) {}
|
|
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 |
/* Can't keep these strings/structs static: _() doesn't support that */
|
|
Packit |
8ea169 |
const char *program_usage_string = _(
|
|
Packit |
8ea169 |
"& [options]"
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
enum {
|
|
Packit |
8ea169 |
OPT_v = 1 << 0,
|
|
Packit |
8ea169 |
OPT_u = 1 << 1,
|
|
Packit |
8ea169 |
OPT_s = 1 << 2,
|
|
Packit |
8ea169 |
OPT_p = 1 << 3,
|
|
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_INTEGER('u', NULL, &client_uid, _("Use NUM as client uid")),
|
|
Packit |
8ea169 |
OPT_BOOL( 's', NULL, NULL , _("Log to syslog")),
|
|
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 |
msg_prefix = xasprintf("%s[%u]", g_progname, getpid());
|
|
Packit |
8ea169 |
if (opts & OPT_s)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
logmode = LOGMODE_JOURNAL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Set up timeout handling */
|
|
Packit |
8ea169 |
/* Part 1 - need this to make SIGALRM interrupt syscalls
|
|
Packit |
8ea169 |
* (as opposed to restarting them): I want read syscall to be interrupted
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
struct sigaction sa;
|
|
Packit |
8ea169 |
/* sa.sa_flags.SA_RESTART bit is clear: make signal interrupt syscalls */
|
|
Packit |
8ea169 |
memset(&sa, 0, sizeof(sa));
|
|
Packit |
8ea169 |
sa.sa_handler = dummy_handler; /* pity, SIG_DFL won't do */
|
|
Packit |
8ea169 |
sigaction(SIGALRM, &sa, NULL);
|
|
Packit |
8ea169 |
/* Part 2 - set the timeout per se */
|
|
Packit |
8ea169 |
alarm(TIMEOUT);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Get uid of the connected client */
|
|
Packit |
8ea169 |
struct ucred cr;
|
|
Packit |
8ea169 |
socklen_t crlen = sizeof(cr);
|
|
Packit |
8ea169 |
if (0 != getsockopt(STDIN_FILENO, SOL_SOCKET, SO_PEERCRED, &cr, &crlen))
|
|
Packit |
8ea169 |
perror_msg_and_die("getsockopt(SO_PEERCRED)");
|
|
Packit |
8ea169 |
if (crlen != sizeof(cr))
|
|
Packit |
8ea169 |
error_msg_and_die("%s: bad crlen %d", "getsockopt(SO_PEERCRED)", (int)crlen);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (client_uid == (uid_t)-1L)
|
|
Packit |
8ea169 |
client_uid = cr.uid;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
client_pid = cr.pid;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
pid_t pid = getpid();
|
|
Packit |
8ea169 |
if (get_ns_ids(getpid(), &g_ns_ids) < 0)
|
|
Packit |
8ea169 |
error_msg_and_die("Cannot get own Namespaces from /proc/%d/ns", pid);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
load_abrt_conf();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct response rsp = { 0 };
|
|
Packit |
8ea169 |
int r = perform_http_xact(&rsp;;
|
|
Packit |
8ea169 |
if (r == 0)
|
|
Packit |
8ea169 |
r = 200;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (rsp.code == 0)
|
|
Packit |
8ea169 |
rsp.code = r;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free_abrt_conf_data();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
printf("HTTP/1.1 %u \r\n\r\n", rsp.code);
|
|
Packit |
8ea169 |
if (rsp.message != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
printf("%s", rsp.message);
|
|
Packit |
8ea169 |
fflush(stdout);
|
|
Packit |
8ea169 |
free(rsp.message);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return (r >= 400); /* Error if 400+ */
|
|
Packit |
8ea169 |
}
|