summaryrefslogtreecommitdiffstats
path: root/src/daemon/dumpsocket.cpp
diff options
context:
space:
mode:
authorDenys Vlasenko <dvlasenk@redhat.com>2010-09-14 19:23:24 +0200
committerDenys Vlasenko <dvlasenk@redhat.com>2010-09-14 19:23:24 +0200
commitfe808e80b2ea5a894a213e38ce7068e6001a932a (patch)
treeacce4a49d0b56eef32759dd2c9d3099151302843 /src/daemon/dumpsocket.cpp
parentbfdf82e770def64e204d1becf9640034bcdc19bb (diff)
downloadabrt-fe808e80b2ea5a894a213e38ce7068e6001a932a.tar.gz
abrt-fe808e80b2ea5a894a213e38ce7068e6001a932a.tar.xz
abrt-fe808e80b2ea5a894a213e38ce7068e6001a932a.zip
Pure file rename without changes
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src/daemon/dumpsocket.cpp')
-rw-r--r--src/daemon/dumpsocket.cpp382
1 files changed, 0 insertions, 382 deletions
diff --git a/src/daemon/dumpsocket.cpp b/src/daemon/dumpsocket.cpp
deleted file mode 100644
index 44955351..00000000
--- a/src/daemon/dumpsocket.cpp
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- Copyright (C) 2010 ABRT team
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-#include "abrtlib.h"
-#include "dump_dir.h"
-#include "crash_types.h" /* FILENAME_foo */
-#include "hooklib.h"
-
-
-/* Maximal length of backtrace. */
-#define MAX_BACKTRACE_SIZE (1024*1024)
-/* Amount of data received from one client for a message before reporting error. */
-#define MAX_MESSAGE_SIZE (4*MAX_BACKTRACE_SIZE)
-/* Maximal number of characters read from socket at once. */
-#define INPUT_BUFFER_SIZE (8*1024)
-/* We exit after this many seconds */
-#define TIMEOUT 10
-
-
-/*
-Unix socket in ABRT daemon for creating new dump directories.
-
-Why to use socket for creating dump dirs? Security. When a Python
-script throws unexpected exception, ABRT handler catches it, running
-as a part of that broken Python application. The application is running
-with certain SELinux privileges, for example it can not execute other
-programs, or to create files in /var/cache or anything else required
-to properly fill a dump directory. Adding these privileges to every
-application would weaken the security.
-The most suitable solution is for the Python application
-to open a socket where ABRT daemon is listening, write all relevant
-data to that socket, and close it. ABRT daemon handles the rest.
-
-** Protocol
-
-Initializing new dump:
-open /var/run/abrt.socket
-
-Providing dump data (hook writes to the socket):
--> "PID="
- number 0 - PID_MAX (/proc/sys/kernel/pid_max)
- \0
--> "EXECUTABLE="
- string (maximum length ~MAX_PATH)
- \0
--> "BACKTRACE="
- string (maximum length 1 MB)
- \0
--> "ANALYZER="
- string (maximum length 100 bytes)
- \0
--> "BASENAME="
- string (maximum length 100 bytes, no slashes)
- \0
--> "REASON="
- string (maximum length 512 bytes)
- \0
-
-Finalizing dump creation:
--> "DONE"
- \0
-*/
-
-
-/* Buffer for incomplete incoming messages. */
-static char *messagebuf_data = NULL;
-static unsigned messagebuf_len = 0;
-
-static unsigned total_bytes_read = 0;
-
-static uid_t client_uid = (uid_t)-1L;
-
-static int pid;
-static char *executable;
-static char *backtrace;
-/* "python", "ruby" etc. */
-static char *analyzer;
-/* Directory base name: "pyhook", "ruby" etc. */
-static char *dir_basename;
-/* Crash reason.
- * Python example:
- * "CCMainWindow.py:1:<module>:ZeroDivisionError: integer division or modulo by zero"
- */
-static char *reason;
-
-
-/* Create a new debug dump from client session.
- * Caller must ensure that all fields in struct client
- * are properly filled.
- */
-static void create_debug_dump()
-{
- /* Create temp directory with the debug dump.
- This directory is renamed to final directory name after
- all files have been stored into it.
- */
- char *path = xasprintf(DEBUG_DUMPS_DIR"/%s-%ld-%u.new",
- dir_basename,
- (long)time(NULL),
- pid);
- /* No need to check the path length, as all variables used are limited, and dd_create()
- fails if the path is too long. */
-
- dump_dir_t *dd = dd_init();
- if (!dd_create(dd, path, client_uid))
- {
- dd_delete(dd);
- dd_close(dd);
- error_msg_and_die("Error creating crash dump %s", path);
- }
-
- dd_save_text(dd, FILENAME_ANALYZER, analyzer);
- dd_save_text(dd, FILENAME_EXECUTABLE, executable);
- dd_save_text(dd, FILENAME_BACKTRACE, backtrace);
- dd_save_text(dd, FILENAME_REASON, reason);
-
- /* Obtain and save the command line. */
- char *cmdline = get_cmdline(pid); // never NULL
- dd_save_text(dd, FILENAME_CMDLINE, cmdline);
- free(cmdline);
-
- /* Store id of the user whose application crashed. */
- char uid_str[sizeof(long) * 3 + 2];
- sprintf(uid_str, "%lu", (long)client_uid);
- dd_save_text(dd, CD_UID, uid_str);
-
- dd_close(dd);
-
- /* Move the completely created debug dump to
- final directory. */
- char *newpath = xstrndup(path, strlen(path) - strlen(".new"));
- if (rename(path, newpath) == 0)
- strcpy(path, newpath);
- free(newpath);
-
- log("Saved %s crash dump of pid %u to %s", analyzer, pid, path);
-
- /* Trim old crash dumps if necessary */
- unsigned maxCrashReportsSize = 0;
- parse_conf(NULL, &maxCrashReportsSize, NULL, NULL);
- if (maxCrashReportsSize > 0)
- {
- check_free_space(maxCrashReportsSize);
- trim_debug_dumps(maxCrashReportsSize, path);
- }
-
- free(path);
-}
-
-/* Checks if a string contains only printable characters. */
-static bool printable_str(const char *str)
-{
- do {
- if ((unsigned char)(*str) < ' ' || *str == 0x7f)
- return false;
- str++;
- } while (*str);
- return true;
-}
-
-/* Checks if a string has certain prefix. */
-static bool starts_with(const char *str, const char *start)
-{
- return strncmp(str, start, strlen(start)) == 0;
-}
-
-/* @returns
- * Caller is responsible to call free() on the returned
- * pointer.
- * If NULL is returned, string extraction failed.
- */
-static char *try_to_get_string(const char *message,
- const char *tag,
- size_t max_len,
- bool printable,
- bool allow_slashes)
-{
- if (!starts_with(message, tag))
- return NULL;
-
- const char *contents = message + strlen(tag);
- if ((printable && !printable_str(contents))
- || (!allow_slashes && strchr(contents, '/'))
- ) {
- error_msg("Received %s contains invalid characters, skipping", tag);
- return NULL;
- }
-
- if (strlen(contents) > max_len)
- {
- error_msg("Received %s too long, trimming to %lu", tag, (long)max_len);
- }
-
- return xstrndup(contents, max_len);
-}
-
-/* Handles a message received from client over socket. */
-static void process_message(const char *message)
-{
-/* @param tag
- * The message identifier. Message starting with it
- * is handled by this macro.
- * @param field
- * Member in struct client, which should be filled by
- * the field contents.
- * @param max_len
- * Maximum length of the field in bytes.
- * Exceeding bytes are trimmed.
- * @param printable
- * Whether to limit the field contents to ASCII only.
- * @param allow_slashes
- * Whether to allow slashes to be a part of input.
- */
-#define HANDLE_INCOMING_STRING(tag, field, max_len, printable, allow_slashes) \
-{ \
- char *s = try_to_get_string(message, tag, max_len, printable, allow_slashes); \
- if (s) \
- { \
- free(field); \
- field = s; \
- VERB3 log("Saved %s%s", tag, s); \
- return; \
- } \
-}
-
- HANDLE_INCOMING_STRING("EXECUTABLE=", executable, PATH_MAX, true, true);
- HANDLE_INCOMING_STRING("BACKTRACE=", backtrace, MAX_BACKTRACE_SIZE, false, true);
- HANDLE_INCOMING_STRING("BASENAME=", dir_basename, 100, true, false);
- HANDLE_INCOMING_STRING("ANALYZER=", analyzer, 100, true, true);
- HANDLE_INCOMING_STRING("REASON=", reason, 512, false, true);
-
-#undef HANDLE_INCOMING_STRING
-
- /* PID is not handled as a string, we convert it to pid_t. */
- if (starts_with(message, "PID="))
- {
- pid = xatou(message + strlen("PID="));
- if (pid < 1)
- /* pid == 0 is error, the lowest PID is 1. */
- error_msg_and_die("Malformed or out-of-range number: '%s'", message + strlen("PID="));
- VERB3 log("Saved PID %u", pid);
- return;
- }
-
- /* Creates debug dump if all fields were already provided. */
- if (starts_with(message, "DONE"))
- {
- if (!pid || !backtrace || !executable
- || !analyzer || !dir_basename || !reason
- ) {
- error_msg_and_die("Got DONE, but some data are missing. Aborting");
- }
-
- /* Write out the crash dump. Don't let alarm to interrupt here */
- alarm(0);
- create_debug_dump();
-
- /* Reset alarm and the counter which detects oversized dumps */
- alarm(TIMEOUT);
- total_bytes_read = 0;
- }
-}
-
-static void dummy_handler(int sig_unused) {}
-
-int main(int argc, char **argv)
-{
- char *env_verbose = getenv("ABRT_VERBOSE");
- if (env_verbose)
- g_verbose = atoi(env_verbose);
-
- enum {
- OPT_s = (1 << 0),
- };
- int optflags = 0;
- int opt;
- while ((opt = getopt(argc, argv, "u:vs")) != -1)
- {
- switch (opt)
- {
- case 'u':
- client_uid = xatoi_u(optarg);
- break;
- case 'v':
- g_verbose++;
- break;
- case 's':
- optflags |= OPT_s;
- break;
- default:
- error_msg_and_die(
- "Usage: abrt-server [-v] [-u UID]\n"
- "\n(So far only) creates crash dumps"
- "\nOptions:"
- "\n\t-v\tVerbose"
- "\n\t-s\tLog to syslog"
- "\n\t-u UID\tUse UID as client uid"
- );
- }
- }
-
- putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
- msg_prefix = xasprintf("abrt-server[%u]", getpid());
- if (optflags & OPT_s)
- {
- openlog(msg_prefix, 0, LOG_DAEMON);
- logmode = LOGMODE_SYSLOG;
- }
-
- /* Set up timeout handling */
- /* Part 1 - need this to make SIGALRM interrupt syscalls
- * (as opposed to restarting them): I want read syscall to be interrupted
- */
- struct sigaction sa;
- /* sa.sa_flags.SA_RESTART bit is clear: make signal interrupt syscalls */
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = dummy_handler; /* pity, SIG_DFL won't do */
- sigaction(SIGALRM, &sa, NULL);
- /* Part 2 - set the timeout per se */
- alarm(TIMEOUT);
-
- if (client_uid == (uid_t)-1L)
- {
- /* Get uid of the connected client */
- struct ucred cr;
- socklen_t crlen = sizeof(cr);
- if (0 != getsockopt(STDIN_FILENO, SOL_SOCKET, SO_PEERCRED, &cr, &crlen))
- perror_msg_and_die("getsockopt(SO_PEERCRED)");
- if (crlen != sizeof(cr))
- error_msg_and_die("%s: bad crlen %d", "getsockopt(SO_PEERCRED)", (int)crlen);
- client_uid = cr.uid;
- }
-
- /* Loop until EOF/error/timeout */
- while (1)
- {
- messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
- int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE);
- if (rd < 0)
- {
- if (errno == EINTR) /* SIGALRM? */
- error_msg_and_die("Timed out");
- perror_msg_and_die("read");
- }
- if (rd == 0)
- break;
-
- VERB3 log("Received %u bytes of data", rd);
- messagebuf_len += rd;
- total_bytes_read += rd;
- if (total_bytes_read > MAX_MESSAGE_SIZE)
- error_msg_and_die("Message is too long, aborting");
-
- while (1)
- {
- unsigned len = strnlen(messagebuf_data, messagebuf_len);
- if (len >= messagebuf_len)
- break;
- /* messagebuf has at least one NUL - process the line */
- process_message(messagebuf_data);
- messagebuf_len -= (len + 1);
- memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
- }
- }
-
- VERB1 log("EOF detected, exiting");
- return 0;
-}