diff options
author | Jiri Moskovcak <jmoskovc@redhat.com> | 2010-11-10 17:01:31 +0100 |
---|---|---|
committer | Jiri Moskovcak <jmoskovc@redhat.com> | 2010-11-10 18:27:05 +0100 |
commit | 3fce116492052267e5e1a634e5404b1b518f7ef3 (patch) | |
tree | 3160a2d429987573004fb8166d40542b76a7315b /src/plugins/abrt-action-analyze-c.c | |
parent | 9d2cb4518c3a8a72ccc714ddbc131aaa84506092 (diff) | |
download | abrt-3fce116492052267e5e1a634e5404b1b518f7ef3.tar.gz abrt-3fce116492052267e5e1a634e5404b1b518f7ef3.tar.xz abrt-3fce116492052267e5e1a634e5404b1b518f7ef3.zip |
move files from lib/plugins to src/plugins
Diffstat (limited to 'src/plugins/abrt-action-analyze-c.c')
-rw-r--r-- | src/plugins/abrt-action-analyze-c.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/plugins/abrt-action-analyze-c.c b/src/plugins/abrt-action-analyze-c.c new file mode 100644 index 00000000..60e08372 --- /dev/null +++ b/src/plugins/abrt-action-analyze-c.c @@ -0,0 +1,238 @@ +/* + Copyright (C) 2009 Zdenek Prikryl (zprikryl@redhat.com) + Copyright (C) 2009 RedHat inc. + + 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 "parse_options.h" + +#define PROGNAME "abrt-action-analyze-c" + +static void create_hash(char hash_str[SHA1_RESULT_LEN*2 + 1], const char *pInput) +{ + unsigned len; + unsigned char hash2[SHA1_RESULT_LEN]; + sha1_ctx_t sha1ctx; + + sha1_begin(&sha1ctx); + sha1_hash(pInput, strlen(pInput), &sha1ctx); + sha1_end(hash2, &sha1ctx); + len = SHA1_RESULT_LEN; + + char *d = hash_str; + unsigned char *s = hash2; + while (len) + { + *d++ = "0123456789abcdef"[*s >> 4]; + *d++ = "0123456789abcdef"[*s & 0xf]; + s++; + len--; + } + *d = '\0'; + //log("hash2:%s str:'%s'", hash_str, pInput); +} + +static char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec) +{ + struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + if (!dd) + return NULL; + char *uid_str = dd_load_text(dd, CD_UID); + dd_close(dd); + unsigned uid = xatoi_u(uid_str); + free(uid_str); + + int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETGUID | EXECFLG_SETSID | EXECFLG_QUIET; + VERB1 flags &= ~EXECFLG_QUIET; + int pipeout[2]; + char* args[4]; + args[0] = (char*)"eu-unstrip"; + args[1] = xasprintf("--core=%s/"FILENAME_COREDUMP, dump_dir_name); + args[2] = (char*)"-n"; + args[3] = NULL; + pid_t child = fork_execv_on_steroids(flags, args, pipeout, /*unsetenv_vec:*/ NULL, /*dir:*/ NULL, uid); + free(args[1]); + + /* Bugs in unstrip or corrupted coredumps can cause it to enter infinite loop. + * Therefore we have a (largish) timeout, after which we kill the child. + */ + int t = time(NULL); /* int is enough, no need to use time_t */ + int endtime = t + timeout_sec; + struct strbuf *buf_out = strbuf_new(); + while (1) + { + int timeout = endtime - t; + if (timeout < 0) + { + kill(child, SIGKILL); + strbuf_append_strf(buf_out, "\nTimeout exceeded: %u seconds, killing %s\n", timeout_sec, args[0]); + break; + } + + /* We don't check poll result - checking read result is enough */ + struct pollfd pfd; + pfd.fd = pipeout[0]; + pfd.events = POLLIN; + poll(&pfd, 1, timeout * 1000); + + char buff[1024]; + int r = read(pipeout[0], buff, sizeof(buff) - 1); + if (r <= 0) + break; + buff[r] = '\0'; + strbuf_append_str(buf_out, buff); + t = time(NULL); + } + close(pipeout[0]); + + /* Prevent having zombie child process */ + int status; + waitpid(child, &status, 0); + + if (status != 0) + { + /* unstrip didnt exit with exitcode 0 */ + strbuf_free(buf_out); + return NULL; + } + + return strbuf_free_nobuf(buf_out); +} + +static void trim_unstrip_output(char *result, const char *unstrip_n_output) +{ + // lines look like this: + // 0x400000+0x209000 23c77451cf6adff77fc1f5ee2a01d75de6511dda@0x40024c - - [exe] + // 0x400000+0x209000 ab3c8286aac6c043fd1bb1cc2a0b88ec29517d3e@0x40024c /bin/sleep /usr/lib/debug/bin/sleep.debug [exe] + // 0x7fff313ff000+0x1000 389c7475e3d5401c55953a425a2042ef62c4c7df@0x7fff313ff2f8 . - linux-vdso.so.1 + // ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + // we drop everything except the marked part ^ + + char *dst = result; + const char *line = unstrip_n_output; + while (*line) + { + const char *eol = strchrnul(line, '\n'); + const char *plus = (char*)memchr(line, '+', eol - line); + if (plus) + { + while (++plus < eol && *plus != '@') + { + if (!isspace(*plus)) + { + *dst++ = *plus; + } + } + } + if (*eol != '\n') break; + line = eol + 1; + } + *dst = '\0'; +} + +int main(int argc, char **argv) +{ + char *env_verbose = getenv("ABRT_VERBOSE"); + if (env_verbose) + g_verbose = atoi(env_verbose); + + /* Can't keep these strings/structs static: _() doesn't support that */ + const char *program_usage_string = _( + PROGNAME" [-vs] -d DIR\n\n" + "Calculates and saves UUID of coredumps" + ); + const char *dump_dir_name = "."; + enum { + OPT_v = 1 << 0, + OPT_d = 1 << 1, + OPT_s = 1 << 2, + }; + /* Keep enum above and order of options below in sync! */ + struct options program_options[] = { + OPT__VERBOSE(&g_verbose), + OPT_STRING('d', NULL, &dump_dir_name, "DIR", _("Crash dump directory")), + OPT_BOOL( 's', NULL, NULL, _("Log to syslog" )), + OPT_END() + }; + /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); + + putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose)); + + msg_prefix = PROGNAME; +//Maybe we will want this... later +// if (opts & OPT_s) +// { +// openlog(msg_prefix, 0, LOG_DAEMON); +// logmode = LOGMODE_SYSLOG; +// } + + /* Run unstrip -n and trim its output, leaving only sizes and build ids */ + + char *unstrip_n_output = run_unstrip_n(dump_dir_name, /*timeout_sec:*/ 30); + if (!unstrip_n_output) + return 1; /* bad dump_dir_name, can't run unstrip, etc... */ + /* modifies unstrip_n_output in-place: */ + trim_unstrip_output(unstrip_n_output, unstrip_n_output); + + /* Hash package + executable + unstrip_n_output and save it as UUID */ + + struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); + if (!dd) + return 1; + + char *executable = dd_load_text(dd, FILENAME_EXECUTABLE); + char *package = dd_load_text(dd, FILENAME_PACKAGE); + /* Package variable has "firefox-3.5.6-1.fc11[.1]" format */ + /* Remove distro suffix and maybe least significant version number */ + char *p = package; + while (*p) + { + if (*p == '.' && (p[1] < '0' || p[1] > '9')) + { + /* We found "XXXX.nondigitXXXX", trim this part */ + *p = '\0'; + break; + } + p++; + } + char *first_dot = strchr(package, '.'); + if (first_dot) + { + char *last_dot = strrchr(first_dot, '.'); + if (last_dot != first_dot) + { + /* There are more than one dot: "1.2.3" + * Strip last part, we don't want to distinquish crashes + * in packages which differ only by minor release number. + */ + *last_dot = '\0'; + } + } + + char *string_to_hash = xasprintf("%s%s%s", package, executable, unstrip_n_output); + /*free(package);*/ + /*free(executable);*/ + /*free(unstrip_n_output);*/ + + char hash_str[SHA1_RESULT_LEN*2 + 1]; + create_hash(hash_str, string_to_hash); + /*free(hash_str);*/ + + dd_save_text(dd, CD_UUID, hash_str); + dd_close(dd); + + return 0; +} |