diff options
-rw-r--r-- | src/CLI/CLI.cpp | 226 | ||||
-rw-r--r-- | src/CLI/Makefile.am | 9 | ||||
-rw-r--r-- | src/CLI/dbus.cpp | 130 | ||||
-rw-r--r-- | src/CLI/dbus.h | 34 | ||||
-rw-r--r-- | src/CLI/report.cpp | 344 | ||||
-rw-r--r-- | src/CLI/report.h | 24 | ||||
-rw-r--r-- | src/CLI/run-command.cpp | 87 | ||||
-rw-r--r-- | src/CLI/run-command.h | 23 |
8 files changed, 657 insertions, 220 deletions
diff --git a/src/CLI/CLI.cpp b/src/CLI/CLI.cpp index d4e2bb4b..a782a59a 100644 --- a/src/CLI/CLI.cpp +++ b/src/CLI/CLI.cpp @@ -21,17 +21,19 @@ #include "abrtlib.h" #include "abrt_dbus.h" #include "DBusCommon.h" +#include "report.h" +#include "dbus.h" #if HAVE_CONFIG_H - #include <config.h> +#include <config.h> #endif #if HAVE_LOCALE_H - #include <locale.h> +#include <locale.h> #endif #if ENABLE_NLS - #include <libintl.h> - #define _(S) gettext(S) +#include <libintl.h> +#define _(S) gettext(S) #else - #define _(S) (S) +#define _(S) (S) #endif /* Program options */ @@ -46,8 +48,6 @@ enum OPT_DELETE }; -static DBusConnection* s_dbus_conn; - static void print_crash_infos(vector_crash_infos_t& pCrashInfos, int pMode) { unsigned int ii; @@ -85,218 +85,6 @@ static void print_crash_infos(vector_crash_infos_t& pCrashInfos, int pMode) } } -/* Saves the crash report to a file. - * Fp must be opened before write_crash_report is called. - * Returned Value: - * If the report is successfully stored to the file, a zero value is returned. - * On failure, nonzero value is returned. - */ -static int write_crash_report(const map_crash_report_t& report, FILE *fp) -{ - for (map_crash_report_t::const_iterator it = report.begin(); it != report.end(); it++) - { - if (it->second[CD_TYPE] == CD_SYS) - continue; - - fprintf(fp, "\n%s\n-----\n%s\n", it->first.c_str(), it->second[CD_CONTENT].c_str()); - } - - return 0; -} - -static void print_crash_report(const map_crash_report_t& pCrashReport) -{ - map_crash_report_t::const_iterator it = pCrashReport.begin(); - for (; it != pCrashReport.end(); it++) - { - if (it->second[CD_TYPE] != CD_SYS) - { - printf("\n%s\n" - "-----\n" - "%s\n", it->first.c_str(), it->second[CD_CONTENT].c_str()); - } - } -} - -/* - * DBus member calls - */ - -/* helpers */ -static DBusMessage* new_call_msg(const char* method) -{ - DBusMessage* msg = dbus_message_new_method_call(CC_DBUS_NAME, CC_DBUS_PATH, CC_DBUS_IFACE, method); - if (!msg) - die_out_of_memory(); - return msg; -} - -static DBusMessage* send_get_reply_and_unref(DBusMessage* msg) -{ - DBusError err; - dbus_error_init(&err); - DBusMessage *reply = dbus_connection_send_with_reply_and_block(s_dbus_conn, msg, /*timeout*/ -1, &err); - if (reply == NULL) - { - //TODO: analyse err - error_msg_and_die("Error sending DBus message"); - } - dbus_message_unref(msg); - return reply; -} - -static vector_crash_infos_t call_GetCrashInfos() -{ - DBusMessage* msg = new_call_msg("GetCrashInfos"); - DBusMessage *reply = send_get_reply_and_unref(msg); - - vector_crash_infos_t argout; - DBusMessageIter in_iter; - dbus_message_iter_init(reply, &in_iter); - int r = load_val(&in_iter, argout); - if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ - error_msg_and_die("dbus call %s: return type mismatch", "GetCrashInfos"); - dbus_message_unref(reply); - return argout; -} - -static map_crash_report_t call_CreateReport(const char* uuid) -{ - /* Yes, call name is not "CreateReport" but "GetJobResult". - * We need to clean up the names one day. */ - DBusMessage* msg = new_call_msg("GetJobResult"); - dbus_message_append_args(msg, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - DBusMessage *reply = send_get_reply_and_unref(msg); - - map_crash_report_t argout; - DBusMessageIter in_iter; - dbus_message_iter_init(reply, &in_iter); - int r = load_val(&in_iter, argout); - if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ - error_msg_and_die("dbus call %s: return type mismatch", "GetJobResult"); - dbus_message_unref(reply); - return argout; -} - -static void call_Report(const map_crash_report_t& report) -{ - DBusMessage* msg = new_call_msg("Report"); - DBusMessageIter out_iter; - dbus_message_iter_init_append(msg, &out_iter); - store_val(&out_iter, report); - - DBusMessage *reply = send_get_reply_and_unref(msg); - //it returns a single value of report_status_t type, - //but we don't use it (yet?) - - dbus_message_unref(reply); - return; -} - -static void call_DeleteDebugDump(const char* uuid) -{ - DBusMessage* msg = new_call_msg("DeleteDebugDump"); - dbus_message_append_args(msg, - DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); - - DBusMessage *reply = send_get_reply_and_unref(msg); - //it returns a single boolean value, - //but we don't use it (yet?) - - dbus_message_unref(reply); - return; -} - -static void handle_dbus_err(bool error_flag, DBusError *err) -{ - if (dbus_error_is_set(err)) - { - error_msg("dbus error: %s", err->message); - /* dbus_error_free(&err); */ - error_flag = true; - } - if (!error_flag) - return; - error_msg_and_die( - "error requesting DBus name %s, possible reasons: " - "abrt run by non-root; dbus config is incorrect", - CC_DBUS_NAME); -} - -int launch_editor(const char *path, const char *report, char **output) -{ - const char *editor, *terminal; - - editor = getenv("VISUAL"); - if (!editor) - editor = getenv("EDITOR"); - - terminal = getenv("TERM"); - if (!editor && (!terminal || !strcmp(terminal, "dumb"))) - { - error_msg(_("Terminal is dumb but no VISUAL nor EDITOR defined.")); - return 1; - } - - if (!editor) - editor = "vi"; - - return 0; -} - -/* Reports the crash with corresponding uuid over DBus. */ -int report(const char *uuid, bool always) -{ - map_crash_report_t cr = call_CreateReport(uuid); - - if (always) - { - call_Report(cr); - return 0; - } - - print_crash_report(cr); - - /* Open a temporary file and write the crash report to it. */ - char filename[] = "/tmp/abrt-report.XXXXXX"; - int fd = mkstemp(filename); - if (fd == -1) - { - error_msg("could not generate temporary file name"); - return 1; - } - - FILE *fp = fdopen(fd, "w"); - if (!fp) - { - error_msg("could not open '%s' to save the crash report", filename); - return 1; - } - - write_crash_report(cr, fp); - - if (fclose(fp)) - { - error_msg("could not close '%s'", filename); - return 2; - } - - printf(_("\nDo you want to send the report? [y/n]: ")); - fflush(NULL); - char answer[16] = "n"; - fgets(answer, sizeof(answer), stdin); - if (answer[0] == 'Y' || answer[0] == 'y') - { - call_Report(cr); - } - - return 0; -} - static const struct option longopts[] = { /* name, has_arg, flag, val */ diff --git a/src/CLI/Makefile.am b/src/CLI/Makefile.am index 68109534..7b10bfb8 100644 --- a/src/CLI/Makefile.am +++ b/src/CLI/Makefile.am @@ -2,7 +2,14 @@ bin_PROGRAMS = abrt-cli abrt_cli_SOURCES = \ CLI.cpp \ - ABRTSocket.h ABRTSocket.cpp + ABRTSocket.h \ + ABRTSocket.cpp \ + run-command.h \ + run-command.cpp \ + report.h \ + report.cpp \ + dbus.h \ + dbus.cpp abrt_cli_CPPFLAGS = \ -I$(srcdir)/../../inc \ diff --git a/src/CLI/dbus.cpp b/src/CLI/dbus.cpp new file mode 100644 index 00000000..3a2999e2 --- /dev/null +++ b/src/CLI/dbus.cpp @@ -0,0 +1,130 @@ +/* + 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 "dbus.h" +#include "DBusCommon.h" + +DBusConnection* s_dbus_conn; + +/* + * DBus member calls + */ + +/* helpers */ +DBusMessage* new_call_msg(const char* method) +{ + DBusMessage* msg = dbus_message_new_method_call(CC_DBUS_NAME, CC_DBUS_PATH, CC_DBUS_IFACE, method); + if (!msg) + die_out_of_memory(); + return msg; +} + +DBusMessage* send_get_reply_and_unref(DBusMessage* msg) +{ + DBusError err; + dbus_error_init(&err); + DBusMessage *reply; + reply = dbus_connection_send_with_reply_and_block(s_dbus_conn, + msg, /*timeout*/ -1, &err); + if (reply == NULL) + { + //TODO: analyse err + error_msg_and_die("Error sending DBus message"); + } + dbus_message_unref(msg); + return reply; +} + +vector_crash_infos_t call_GetCrashInfos() +{ + DBusMessage* msg = new_call_msg("GetCrashInfos"); + DBusMessage *reply = send_get_reply_and_unref(msg); + + vector_crash_infos_t argout; + DBusMessageIter in_iter; + dbus_message_iter_init(reply, &in_iter); + int r = load_val(&in_iter, argout); + if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ + error_msg_and_die("dbus call %s: return type mismatch", "GetCrashInfos"); + dbus_message_unref(reply); + return argout; +} + +map_crash_report_t call_CreateReport(const char* uuid) +{ + /* Yes, call name is not "CreateReport" but "GetJobResult". + * We need to clean up the names one day. */ + DBusMessage* msg = new_call_msg("GetJobResult"); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + DBusMessage *reply = send_get_reply_and_unref(msg); + + map_crash_report_t argout; + DBusMessageIter in_iter; + dbus_message_iter_init(reply, &in_iter); + int r = load_val(&in_iter, argout); + if (r != ABRT_DBUS_LAST_FIELD) /* more values present, or bad type */ + error_msg_and_die("dbus call %s: return type mismatch", "GetJobResult"); + dbus_message_unref(reply); + return argout; +} + +void call_Report(const map_crash_report_t& report) +{ + DBusMessage* msg = new_call_msg("Report"); + DBusMessageIter out_iter; + dbus_message_iter_init_append(msg, &out_iter); + store_val(&out_iter, report); + + DBusMessage *reply = send_get_reply_and_unref(msg); + //it returns a single value of report_status_t type, + //but we don't use it (yet?) + + dbus_message_unref(reply); +} + +void call_DeleteDebugDump(const char* uuid) +{ + DBusMessage* msg = new_call_msg("DeleteDebugDump"); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + DBusMessage *reply = send_get_reply_and_unref(msg); + //it returns a single boolean value, + //but we don't use it (yet?) + + dbus_message_unref(reply); +} + +void handle_dbus_err(bool error_flag, DBusError *err) +{ + if (dbus_error_is_set(err)) + { + error_msg("dbus error: %s", err->message); + /* dbus_error_free(&err); */ + error_flag = true; + } + if (!error_flag) + return; + error_msg_and_die( + "error requesting DBus name %s, possible reasons: " + "abrt run by non-root; dbus config is incorrect", + CC_DBUS_NAME); +} diff --git a/src/CLI/dbus.h b/src/CLI/dbus.h new file mode 100644 index 00000000..febcb942 --- /dev/null +++ b/src/CLI/dbus.h @@ -0,0 +1,34 @@ +/* + 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. +*/ +#ifndef ABRT_CLI_DBUS_H +#define ABRT_CLI_DBUS_H + +#include "abrt_dbus.h" +#include "CrashTypes.h" + +extern DBusConnection* s_dbus_conn; + +extern DBusMessage* new_call_msg(const char* method); +extern DBusMessage* send_get_reply_and_unref(DBusMessage* msg); +extern vector_crash_infos_t call_GetCrashInfos(); +extern map_crash_report_t call_CreateReport(const char* uuid); +extern void call_Report(const map_crash_report_t& report); +extern void call_DeleteDebugDump(const char* uuid); +extern void handle_dbus_err(bool error_flag, DBusError *err); + +#endif diff --git a/src/CLI/report.cpp b/src/CLI/report.cpp new file mode 100644 index 00000000..6f2ba6a2 --- /dev/null +++ b/src/CLI/report.cpp @@ -0,0 +1,344 @@ +/* + 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 "report.h" +#include "run-command.h" +#include "dbus.h" +#include "abrtlib.h" +#if HAVE_CONFIG_H +#include <config.h> +#endif +#if ENABLE_NLS +#include <libintl.h> +#define _(S) gettext(S) +#else +#define _(S) (S) +#endif + +#define FIELD_SEP "%----" + +/* Escapes the field content string to avoid confusion with file comments. + Returned field must be free()d by caller. */ +static char *escape(const char *str) +{ + // Determine the size of resultant string. + // Count the required number of escape characters. + // 1. NEWLINE followed by # + // 2. NEWLINE followed by \# (escaped version) + const char *ptr = str; + bool newline = true; + int count = 0; + while (*ptr) + { + if (newline) + { + if (*ptr == '#') + ++count; + if (*ptr == '\\' && *(ptr + 1) == '#') + ++count; + } + + newline = (*ptr == '\n'); + ++ptr; + } + + // Copy the input string to the resultant string, and escape all + // occurences of \# and #. + char *result = (char*)malloc(strlen(str) + 1 + count); + if (!result) + error_msg_and_die("Memory error while escaping a field."); + + const char *src = str; + char *dest = result; + newline = true; + while (*src) + { + if (newline) + { + if (*src == '#') + *dest++ = '\\'; + else if (*src == '\\' && *(src + 1) == '#') + *dest++ = '\\'; + } + + newline = (*src == '\n'); + *dest++ = *src++; + } + *dest = '\0'; + return result; +} + +/* Removes all comment lines, and unescapes the string previously escaped + by escape(). Works in-place. */ +static char *remove_comments_and_unescape(char *str) +{ + char *src = str, *dest = str; + bool newline = true; + while (*src) + { + if (newline) + { + if (*src == '#') + { // Skip the comment line! + while (*src && *src != '\n') + ++src; + + if (*src == '\0') + break; + + ++src; + continue; + } + else if (*src == '\\' && + (*(src + 1) == '#' || + (*(src + 1) == '\\' && *(src + 2) == '#'))) + { + ++src; // Unescape escaped char. + } + } + + newline = (*src == '\n'); + *dest++ = *src++; + } + *dest = '\0'; +} + +/* Writes a field of crash report to a file. + * Field must be writable. + */ +static void write_crash_report_field(FILE *fp, const map_crash_report_t &report, + const char *field, const char *description) +{ + const map_crash_report_t::const_iterator it = report.find(field); + if (it == report.end()) + { + error_msg("Field %s not found.\n", field); + return; + } + + if (it->second[CD_TYPE] == CD_SYS) + { + error_msg("Cannot write field %s because it is a system value\n", field); + return; + } + + fprintf(fp, "%s%s\n", FIELD_SEP, it->first.c_str()); + + bool readonly = (it->second[CD_EDITABLE] != CD_ISEDITABLE); + fprintf(fp, readonly ? _("# %s (read only)\n") : "# %s\n", description); + + char *escaped_content = escape(it->second[CD_CONTENT].c_str()); + fprintf(fp, "%s\n", escaped_content); + free(escaped_content); +} + +/* Saves the crash report to a file. + * Fp must be opened before write_crash_report is called. + * Returned Value: + * If the report is successfully stored to the file, a zero value is returned. + * On failure, nonzero value is returned. + */ +static int write_crash_report(const map_crash_report_t &report, FILE *fp) +{ + fprintf(fp, "# Please check this report. Lines starting with '#' will be ignored.\n" + "# Lines starting with '%%----' separate fields, please do not delete them.\n\n"); + + write_crash_report_field(fp, report, "Comment", + _("Describe the circumstances of this crash below.")); + write_crash_report_field(fp, report, "How to reproduce", + _("How to reproduce the crash?")); + write_crash_report_field(fp, report, "backtrace", + _("Backtrace. Check that it does not contain any sensitive data such as passwords.")); + write_crash_report_field(fp, report, "UUID", "UUID"); + write_crash_report_field(fp, report, "architecture", "Architecture"); + write_crash_report_field(fp, report, "cmdline", "Command line"); + write_crash_report_field(fp, report, "component", "Component"); + write_crash_report_field(fp, report, "coredump", "Core dump"); + write_crash_report_field(fp, report, "executable", "Executable"); + write_crash_report_field(fp, report, "kernel", "Kernel"); + write_crash_report_field(fp, report, "package", "Package"); + write_crash_report_field(fp, report, "reason", "Reason"); + write_crash_report_field(fp, report, "release", "Release"); + + return 0; +} + +/* + * Updates appropriate field in the report from the text. The text can + * contain multiple fields. + */ +static void read_crash_report_field(const char *text, map_crash_report_t &report, + const char *field) +{ + char separator[strlen("\n" FIELD_SEP) + strlen(field) + 2]; // 2 = '\n\0' + sprintf(separator, "\n%s%s\n", FIELD_SEP, field); + const char *textfield = strstr(text, separator); + if (!textfield) + return; + + textfield += strlen(separator); + int length = 0; + const char *end = strstr(textfield, "\n" FIELD_SEP); + if (!end) + length = strlen(textfield); + else + length = end - textfield; + + const map_crash_report_t::iterator it = report.find(field); + if (it == report.end()) + { + error_msg("Field %s not found.\n", field); + return; + } + + if (it->second[CD_TYPE] == CD_SYS) + { + error_msg("Cannot update field %s because it is a system value.\n", field); + return; + } + + if (it->second[CD_EDITABLE] == CD_ISEDITABLE) + it->second[CD_CONTENT].assign(textfield, length); +} + +/* + * Updates the crash report 'report' from the text. The text must not contain + * any comments. + */ +static int read_crash_report(map_crash_report_t &report, const char *text) +{ + read_crash_report_field(text, report, "Comment"); + read_crash_report_field(text, report, "How to reproduce"); + read_crash_report_field(text, report, "backtrace"); + read_crash_report_field(text, report, "UUID"); + read_crash_report_field(text, report, "architecture"); + read_crash_report_field(text, report, "cmdline"); + read_crash_report_field(text, report, "component"); + read_crash_report_field(text, report, "coredump"); + read_crash_report_field(text, report, "executable"); + read_crash_report_field(text, report, "kernel"); + read_crash_report_field(text, report, "package"); + read_crash_report_field(text, report, "reason"); + read_crash_report_field(text, report, "release"); + + return 0; +} + +/* Runs external editor. */ +int launch_editor(const char *path) +{ + const char *editor, *terminal; + + editor = getenv("ABRT_EDITOR"); + if (!editor) + editor = getenv("VISUAL"); + if (!editor) + editor = getenv("EDITOR"); + + terminal = getenv("TERM"); + if (!editor && (!terminal || !strcmp(terminal, "dumb"))) + { + error_msg(_("Terminal is dumb but no VISUAL nor EDITOR defined.")); + return 1; + } + + if (!editor) + editor = "vi"; + + const char *args[6]; + args[0] = editor; + args[1] = path; + run_command(args); + + return 0; +} + +/* Reports the crash with corresponding uuid over DBus. */ +int report(const char *uuid, bool always) +{ + map_crash_report_t cr = call_CreateReport(uuid); + + if (always) + { + call_Report(cr); + return 0; + } + + /* Open a temporary file and write the crash report to it. */ + char filename[] = "/tmp/abrt-report.XXXXXX"; + int fd = mkstemp(filename); + if (fd == -1) + { + error_msg("could not generate temporary file name"); + return 1; + } + + FILE *fp = fdopen(fd, "w"); + if (!fp) + { + error_msg("could not open '%s' to save the crash report", filename); + return 1; + } + + write_crash_report(cr, fp); + + if (fclose(fp)) + { + error_msg("could not close '%s'", filename); + return 2; + } + + launch_editor(filename); + + fp = fopen(filename, "r"); + if (!fp) + { + error_msg("could not open '%s' to read the crash report", filename); + return 1; + } + + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + printf("%d", size); + fseek(fp, 0, SEEK_SET); + + char *text = (char*)malloc(size + 1); + if (fread(text, 1, size, fp) != size) + { + error_msg("could not read '%s'", filename); + return 1; + } + text[size] = '\0'; + fclose(fp); + + remove_comments_and_unescape(text); + read_crash_report(cr, text); + free(text); + + /*int result = */unlink(filename); + + printf(_("\nReport has been updated.\nDo you want to send the report? [y/n]: ")); + fflush(NULL); + char answer[16] = "n"; + fgets(answer, sizeof(answer), stdin); + if (answer[0] == 'Y' || answer[0] == 'y') + { + call_Report(cr); + } + + return 0; +} diff --git a/src/CLI/report.h b/src/CLI/report.h new file mode 100644 index 00000000..888babf3 --- /dev/null +++ b/src/CLI/report.h @@ -0,0 +1,24 @@ +/* + 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. +*/ +#ifndef ABRT_CLI_REPORT_H +#define ABRT_CLI_REPORT_H + +/* Reports the crash with corresponding uuid over DBus. */ +extern int report(const char *uuid, bool always); + +#endif diff --git a/src/CLI/run-command.cpp b/src/CLI/run-command.cpp new file mode 100644 index 00000000..df29b0e1 --- /dev/null +++ b/src/CLI/run-command.cpp @@ -0,0 +1,87 @@ +/* + 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 "run-command.h" +#include "abrtlib.h" + +/* + Inspired by git code. + http://git.kernel.org/?p=git/git.git;a=blob;f=run-command.c;hb=HEAD +*/ + +struct child_process +{ + const char **argv; + pid_t pid; +}; + +static int start_command(struct child_process *cmd) +{ + cmd->pid = fork(); + if (cmd->pid == 0) + { // new process + execvp(cmd->argv[0], (char *const*)cmd->argv); + exit(127); + } + if (cmd->pid < 0) + { + error_msg_and_die("Unable to fork for %s: %s", cmd->argv[0], strerror(errno)); + return -1; + } + return 0; +} + +static int finish_command(struct child_process *cmd) +{ + pid_t waiting; + int status, code = -1; + while ((waiting = waitpid(cmd->pid, &status, 0)) < 0 && errno == EINTR) + ; /* nothing */ + + if (waiting < 0) + error_msg_and_die("waitpid for %s failed: %s", cmd->argv[0], strerror(errno)); + else if (waiting != cmd->pid) + error_msg_and_die("waitpid is confused (%s)", cmd->argv[0]); + else if (WIFSIGNALED(status)) + { + code = WTERMSIG(status); + error_msg("%s died of signal %d", cmd->argv[0], code); + } + else if (WIFEXITED(status)) + { + code = WEXITSTATUS(status); + if (code == 127) + { + code = -1; + error_msg_and_die("cannot run %s: %s", cmd->argv[0], strerror(ENOENT)); + } + } + else + error_msg_and_die("waitpid is confused (%s)", cmd->argv[0]); + + return code; +} + +int run_command(const char **argv) +{ + struct child_process cmd; + cmd.argv = argv; + int code = start_command(&cmd); + if (code) + return code; + return finish_command(&cmd); +} diff --git a/src/CLI/run-command.h b/src/CLI/run-command.h new file mode 100644 index 00000000..45a85af5 --- /dev/null +++ b/src/CLI/run-command.h @@ -0,0 +1,23 @@ +/* + 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. +*/ +#ifndef ABRT_CLI_RUN_COMMAND_H +#define ABRT_CLI_RUN_COMMAND_H + +int run_command(const char **argv); + +#endif |