summaryrefslogtreecommitdiffstats
path: root/src/plugins/abrt-action-analyze-c.c
diff options
context:
space:
mode:
authorJiri Moskovcak <jmoskovc@redhat.com>2010-11-10 17:01:31 +0100
committerJiri Moskovcak <jmoskovc@redhat.com>2010-11-10 18:27:05 +0100
commit3fce116492052267e5e1a634e5404b1b518f7ef3 (patch)
tree3160a2d429987573004fb8166d40542b76a7315b /src/plugins/abrt-action-analyze-c.c
parent9d2cb4518c3a8a72ccc714ddbc131aaa84506092 (diff)
downloadabrt-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.c238
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;
+}