diff options
author | Denys Vlasenko <dvlasenk@redhat.com> | 2011-01-06 16:18:09 +0100 |
---|---|---|
committer | Denys Vlasenko <dvlasenk@redhat.com> | 2011-01-06 16:18:09 +0100 |
commit | d108d7d2fbe0b178110295fd8335c258f699a5d4 (patch) | |
tree | 3bebcc8c7f66a15383be577beb04300f6fe6e9af /src | |
parent | 6c7086f6c0086496a5a1ae9ab13fdbb310e070ba (diff) | |
download | abrt-d108d7d2fbe0b178110295fd8335c258f699a5d4.tar.gz abrt-d108d7d2fbe0b178110295fd8335c258f699a5d4.tar.xz abrt-d108d7d2fbe0b178110295fd8335c258f699a5d4.zip |
pass old pattern to ccpp hook and use it
abrtd:
instead of
"|/usr/libexec/abrt-ccpp-hook DEBUG_DUMPS_DIR %p %s %u %c",
sets coredump handler to
"|/usr/libexec/abrt-ccpp-hook DEBUG_DUMPS_DIR %s %c %p %u %g %t %h %e OLD_PATTERN"
abrt-ccpp-hook:
expands OLD_PATTERN using values of %s %c %p %u %g %t %h %e
and uses it as a name of "compat coredump".
Patch has a feature which prevents usage of kernel-truncated
OLD_PATTERN: it is passed as hex string *with terminating NUL*
(encoded as 00). If ccpp hook doesn't see 00, it refuses to use
OLD_PATTERN and uses string "core" instead.
Run tested. On a new kernel, passes up to 27 char long old pattern.
Longer patterns are still truncated.
This may be improved in future kernels.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/hooks/abrt-hook-ccpp.c | 137 | ||||
-rw-r--r-- | src/include/abrtlib.h | 5 | ||||
-rw-r--r-- | src/lib/Makefile.am | 1 | ||||
-rw-r--r-- | src/lib/binhex.c | 74 | ||||
-rw-r--r-- | src/plugins/CCpp.cpp | 55 |
5 files changed, 232 insertions, 40 deletions
diff --git a/src/hooks/abrt-hook-ccpp.c b/src/hooks/abrt-hook-ccpp.c index 430eaf6c..c9327723 100644 --- a/src/hooks/abrt-hook-ccpp.c +++ b/src/hooks/abrt-hook-ccpp.c @@ -161,9 +161,22 @@ static char* get_cwd(pid_t pid) return malloc_readlink(buf); } -static char core_basename[sizeof("core.%lu") + sizeof(long)*3] = "core"; +/* + * %s - signal number + * %c - ulimit -c value + * %p - pid + * %u - uid + * %g - gid + * %t - UNIX time of dump + * %h - hostname + * %e - executable filename + * %% - output one "%" + */ +/* Must match CORE_PATTERN order in daemon! */ +static const char percent_specifiers[] = "%scpugthe"; +static char *core_basename = "core"; -static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) +static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid, char **percent_values) { struct passwd* pw = getpwuid(uid); gid_t gid = pw ? pw->pw_gid : uid; @@ -178,20 +191,63 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) return -1; } - /* Mimic "core.PID" if requested */ - char buf[] = "0\n"; - int fd = open("/proc/sys/kernel/core_uses_pid", O_RDONLY); - if (fd >= 0) + if (strcmp(core_basename, "core") == 0) { - read(fd, buf, sizeof(buf)); - close(fd); + /* Mimic "core.PID" if requested */ + char buf[] = "0\n"; + int fd = open("/proc/sys/kernel/core_uses_pid", O_RDONLY); + if (fd >= 0) + { + read(fd, buf, sizeof(buf)); + close(fd); + } + if (strcmp(buf, "1\n") == 0) + { + core_basename = xasprintf("%s.%lu", core_basename, (long)pid); + } } - if (strcmp(buf, "1\n") == 0) + else { - sprintf(core_basename, "core.%lu", (long)pid); + /* Expand old core pattern, put expanded name in core_basename */ + core_basename = xstrdup(core_basename); + unsigned idx = 0; + while (1) + { + char c = core_basename[idx]; + if (!c) + break; + idx++; + if (c != '%') + continue; + + /* We just copied %, look at following char and expand %c */ + c = core_basename[idx]; + unsigned specifier_num = strchrnul(percent_specifiers, c) - percent_specifiers; + if (percent_specifiers[specifier_num] != '\0') /* valid %c (might be %% too) */ + { + const char *val = "%"; + if (specifier_num > 0) /* not %% */ + val = percent_values[specifier_num - 1]; + //log("c:'%c'", c); + //log("val:'%s'", val); + + /* Replace %c at core_basename[idx] by its value */ + idx--; + char *old = core_basename; + core_basename = xasprintf("%.*s%s%s", idx, core_basename, val, core_basename + idx + 2); + //log("pos:'%*s|'", idx, ""); + //log("new:'%s'", core_basename); + //log("old:'%s'", old); + free(old); + idx += strlen(val); + } + /* else: invalid %c, % is already copied verbatim, + * next loop iteration will copy c */ + } } - /* man core: + /* Open (create) compat core file. + * man core: * There are various circumstances in which a core dump file * is not produced: * @@ -209,8 +265,7 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) * * The RLIMIT_CORE or RLIMIT_FSIZE resource limits for the process * are set to zero. - * [shouldn't it be checked by kernel? 2.6.30.9-96 doesn't, still - * calls us even if "ulimit -c 0"] + * [we check RLIMIT_CORE, but how can we check RLIMIT_FSIZE?] * * The binary being executed by the process does not have * read permission enabled. [how we can check it here?] @@ -221,7 +276,6 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) * (However, see the description of the prctl(2) PR_SET_DUMPABLE operation, * and the description of the /proc/sys/fs/suid_dumpable file in proc(5).) */ - /* Do not O_TRUNC: if later checks fail, we do not want to have file already modified here */ struct stat sb; errno = 0; @@ -240,6 +294,7 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) if (ftruncate(user_core_fd, 0) != 0) { /* perror first, otherwise unlink may trash errno */ perror_msg("truncate %s/%s", user_pwd, core_basename); + unlink(core_basename); return -1; } @@ -248,23 +303,28 @@ static int open_user_core(const char *user_pwd, uid_t uid, pid_t pid) int main(int argc, char** argv) { - int i; struct stat sb; - if (argc < 5) + if (argc < 10) /* no argv[9]? */ { - error_msg_and_die("Usage: %s: DUMPDIR PID SIGNO UID CORE_SIZE_LIMIT", argv[0]); + /* percent specifier: %s %c %p %u %g %t %h %e */ + /* argv: [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] */ + error_msg_and_die("Usage: %s DUMPDIR SIGNO CORE_SIZE_LIMIT PID UID GID TIME HOSTNAME BINARY_NAME [OLD_PATTERN]", argv[0]); } /* Not needed on 2.6.30. * At least 2.6.18 has a bug where - * argv[1] = "DUMPDIR PID SIGNO UID CORE_SIZE_LIMIT" - * argv[2] = "PID SIGNO UID CORE_SIZE_LIMIT" + * argv[1] = "DUMPDIR SIGNO CORE_SIZE_LIMIT ..." + * argv[2] = "SIGNO CORE_SIZE_LIMIT ..." * and so on. Fixing it: */ - for (i = 1; argv[i]; i++) + if (strchr(argv[1], ' ')) { - strchrnul(argv[i], ' ')[0] = '\0'; + int i; + for (i = 1; argv[i]; i++) + { + strchrnul(argv[i], ' ')[0] = '\0'; + } } openlog("abrt", LOG_PID, LOG_DAEMON); @@ -272,19 +332,38 @@ int main(int argc, char** argv) errno = 0; const char* dddir = argv[1]; - pid_t pid = xatoi_positive(argv[2]); - const char* signal_str = argv[3]; - int signal_no = xatoi_positive(argv[3]); - uid_t uid = xatoi_positive(argv[4]); - off_t ulimit_c = strtoull(argv[5], NULL, 10); + const char* signal_str = argv[2]; + int signal_no = xatoi_positive(signal_str); + off_t ulimit_c = strtoull(argv[3], NULL, 10); if (ulimit_c < 0) /* unlimited? */ { /* set to max possible >0 value */ ulimit_c = ~((off_t)1 << (sizeof(off_t)*8-1)); } + pid_t pid = xatoi_positive(argv[4]); + uid_t uid = xatoi_positive(argv[5]); if (errno || pid <= 0) { - error_msg_and_die("pid '%s' or limit '%s' is bogus", argv[2], argv[5]); + perror_msg_and_die("pid '%s' or limit '%s' is bogus", argv[4], argv[3]); + } + if (argv[10]) /* OLD_PATTERN */ + { + char *buf = (char*) xzalloc(strlen(argv[10]) / 2 + 2); + char *end = hex2bin(buf, argv[10], strlen(argv[10])); + if (end && end > buf && end[-1] == '\0') + { + core_basename = buf; + //log("core_basename:'%s'", core_basename); + } + else + { + /* Until recently, kernels were truncating expanded core pattern. + * In this case, we end up here... + */ + error_msg("bad old pattern '%s', ignoring and using 'core'", argv[10]); + /* core_basename = "core"; - already is */ + free(buf); + } } int src_fd_binary; @@ -311,8 +390,8 @@ int main(int argc, char** argv) /* Open a fd to compat coredump, if requested and is possible */ int user_core_fd = -1; if (setting_MakeCompatCore && ulimit_c != 0) - /* note: checks "user_pwd == NULL" inside */ - user_core_fd = open_user_core(user_pwd, uid, pid); + /* note: checks "user_pwd == NULL" inside, updates core_basename */ + user_core_fd = open_user_core(user_pwd, uid, pid, &argv[2]); if (executable == NULL) { diff --git a/src/include/abrtlib.h b/src/include/abrtlib.h index 0eb0f2d0..2e23805a 100644 --- a/src/include/abrtlib.h +++ b/src/include/abrtlib.h @@ -233,6 +233,11 @@ double get_dirsize_find_largest_dir( const char *excluded /* can be NULL */ ); +/* Emit a string of hex representation of bytes */ +char* bin2hex(char *dst, const char *str, int count); +/* Convert "xxxxxxxx" hex string to binary, no more than COUNT bytes */ +char* hex2bin(char *dst, const char *str, int count); + /* Returns command line of running program. * Caller is responsible to free() the returned value. * If the pid is not valid or command line can not be obtained, diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index b02acb82..6d6c22bb 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -15,6 +15,7 @@ lib_LTLIBRARIES = \ libreport_la_SOURCES = \ xfuncs.c \ encbase64.c \ + binhex.c \ stdio_helpers.c \ hash_md5.c hash_md5.h \ hash_sha1.c hash_sha1.h \ diff --git a/src/lib/binhex.c b/src/lib/binhex.c new file mode 100644 index 00000000..1fcb7445 --- /dev/null +++ b/src/lib/binhex.c @@ -0,0 +1,74 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 RedHat Inc + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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" + +static const char hexdigits_locase[] = "0123456789abcdef"; + +/* Emit a string of hex representation of bytes */ +char *bin2hex(char *dst, const char *str, int count) +{ + while (count) { + unsigned char c = *str++; + /* put lowercase hex digits */ + *dst++ = hexdigits_locase[c >> 4]; + *dst++ = hexdigits_locase[c & 0xf]; + count--; + } + return dst; +} + +/* Convert "xxxxxxxx" hex string to binary, no more than COUNT bytes */ +char *hex2bin(char *dst, const char *str, int count) +{ + /* Parts commented out with // allow parsing + * of strings like "xx:x:x:xx:xx:xx:xxxxxx" + * (IPv6, ethernet addresses and the like). + */ + errno = EINVAL; + while (*str && count) { + uint8_t val; + uint8_t c; + + c = *str++; + if (isdigit(c)) + val = c - '0'; + else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') + val = (c|0x20) - ('a' - 10); + else + return NULL; + val <<= 4; + c = *str; + if (isdigit(c)) + val |= c - '0'; + else if ((c|0x20) >= 'a' && (c|0x20) <= 'f') + val |= (c|0x20) - ('a' - 10); + //else if (c == ':' || c == '\0') + // val >>= 4; + else + return NULL; + + *dst++ = val; + //if (c != '\0') + str++; + //if (*str == ':') + // str++; + count--; + } + errno = (*str ? ERANGE : 0); + return dst; +} diff --git a/src/plugins/CCpp.cpp b/src/plugins/CCpp.cpp index d88a593d..df2c4482 100644 --- a/src/plugins/CCpp.cpp +++ b/src/plugins/CCpp.cpp @@ -26,16 +26,28 @@ using namespace std; #define CORE_PATTERN_IFACE "/proc/sys/kernel/core_pattern" -#define CORE_PATTERN "|"CCPP_HOOK_PATH" "DEBUG_DUMPS_DIR" %p %s %u %c" +/* + * %s - signal number + * %c - ulimit -c value + * %p - pid + * %u - uid + * %g - gid + * %t - UNIX time of dump + * %h - hostname + * %e - executable filename + * %% - output one "%" + */ +#define CORE_PATTERN "|"CCPP_HOOK_PATH" "DEBUG_DUMPS_DIR" %s %c %p %u %g %t %h %e" + #define CORE_PIPE_LIMIT_IFACE "/proc/sys/kernel/core_pipe_limit" /* core_pipe_limit specifies how many dump_helpers might run at the same time * 0 - means unlimited, but it's not guaranteed that /proc/<pid> of crashing - * process will be available for dump_helper + * process will be available for dump_helper. * 4 - means that 4 dump_helpers can run at the same time (the rest will also - * run, but they will fail to read /proc/<pid>) - * This should be enough for ABRT, we can miss some crashes, but what are - * the odds that more processes crash at the same time? - * The value of 4 has been recommended by nhorman. + * run, but they will fail to read /proc/<pid>). + * This should be enough for ABRT, we can miss some crashes, but what are + * the odds that more processes crash at the same time? + * The value of 4 has been recommended by nhorman. */ #define CORE_PIPE_LIMIT "4" @@ -133,7 +145,7 @@ void CAnalyzerCCpp::Init() } if (m_sOldCorePattern[0] == '|') { - if (m_sOldCorePattern == CORE_PATTERN) + if (strncmp(m_sOldCorePattern.c_str(), CORE_PATTERN, strlen(CORE_PATTERN)) == 0) { log("warning: %s already contains %s, " "did abrt daemon crash recently?", @@ -157,14 +169,35 @@ void CAnalyzerCCpp::Init() fp = fopen(CORE_PATTERN_IFACE, "w"); if (fp) { - fputs(CORE_PATTERN, fp); + if (m_sOldCorePattern[0] != '|') + { + const char *old = m_sOldCorePattern.c_str(); + unsigned len = strchrnul(old, '\n') - old; + char *hex_old = (char*)xmalloc(len * 2 + 1); + bin2hex(hex_old, old, len)[0] = '\0'; + /* Trailing 00 is a sentinel. Decoder in the hook will check it. + * If it won't see it, then kernel has truncated the argument: + */ + char *pattern = xasprintf("%s %s00", CORE_PATTERN, hex_old); + //log("old:'%s'->'%s'", old, hex_old); + //log("pattern:'%s'", pattern); + + fputs(pattern, fp); + + free(pattern); + free(hex_old); + } + else + { + fputs(CORE_PATTERN, fp); + } fclose(fp); } /* read the core_pipe_limit and change it if it's == 0 - otherwise the abrt-hook-ccpp won't be able to read /proc/<pid> - of the crashing process - */ + * otherwise the abrt-hook-ccpp won't be able to read /proc/<pid> + * of the crashing process + */ fp = fopen(CORE_PIPE_LIMIT_IFACE, "r"); if (fp) { |