|
Packit |
4f15d5 |
/*
|
|
Packit |
4f15d5 |
Copyright (C) 2011 ABRT Team
|
|
Packit |
4f15d5 |
Copyright (C) 2011 RedHat inc.
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
4f15d5 |
it under the terms of the GNU General Public License as published by
|
|
Packit |
4f15d5 |
the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
4f15d5 |
(at your option) any later version.
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
This program is distributed in the hope that it will be useful,
|
|
Packit |
4f15d5 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
4f15d5 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
4f15d5 |
GNU General Public License for more details.
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
You should have received a copy of the GNU General Public License along
|
|
Packit |
4f15d5 |
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
4f15d5 |
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
#include "report.h"
|
|
Packit |
4f15d5 |
#include "internal_libreport.h"
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
int report_problem_in_dir(const char *dirname, int flags)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
/* Prepare it before fork, to avoid thread-unsafe setenv there */
|
|
Packit |
4f15d5 |
char *prgname = (char*) g_get_prgname();
|
|
Packit |
4f15d5 |
if (prgname)
|
|
Packit |
4f15d5 |
prgname = xasprintf("LIBREPORT_PRGNAME=%s", prgname);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_IGNORE_NOT_REPORTABLE)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
load_global_configuration();
|
|
Packit |
4f15d5 |
set_global_stop_on_not_reportable(false, 0);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
fflush(NULL);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
pid_t pid = fork();
|
|
Packit |
4f15d5 |
if (pid < 0) /* error */
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
perror_msg("fork");
|
|
Packit |
4f15d5 |
return -1;
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (pid == 0) /* child */
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
const char *event_name;
|
|
Packit |
4f15d5 |
const char *path, *path1, *path2;
|
|
Packit |
4f15d5 |
char *args[7], **pp;
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
/* Graphical tool */
|
|
Packit |
4f15d5 |
event_name = "report-gui";
|
|
Packit |
4f15d5 |
path1 = BIN_DIR"/report-gtk";
|
|
Packit |
4f15d5 |
path2 = "report-gtk";
|
|
Packit |
4f15d5 |
pp = args;
|
|
Packit |
4f15d5 |
*pp++ = (char *)"report-gtk";
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_DEL_DIR)
|
|
Packit |
4f15d5 |
*pp++ = (char *)"--delete";
|
|
Packit |
4f15d5 |
*pp++ = (char *)"--";
|
|
Packit |
4f15d5 |
*pp++ = (char *)dirname;
|
|
Packit |
4f15d5 |
*pp = NULL;
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (prgname)
|
|
Packit |
4f15d5 |
putenv(prgname);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_RUN_NEWT)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
/* we want to run newt first */
|
|
Packit |
4f15d5 |
event_name = "report-cli";
|
|
Packit |
4f15d5 |
path1 = BIN_DIR"/report-newt";
|
|
Packit |
4f15d5 |
path2 = "report-newt";
|
|
Packit |
4f15d5 |
pp = args;
|
|
Packit |
4f15d5 |
*pp++ = (char *)"report-newt";
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_DEL_DIR)
|
|
Packit |
4f15d5 |
*pp++ = (char *)"--delete";
|
|
Packit |
4f15d5 |
*pp++ = (char *)"--";
|
|
Packit |
4f15d5 |
*pp++ = (char *)dirname;
|
|
Packit |
4f15d5 |
*pp = NULL;
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
else if (!getenv("DISPLAY") || (flags & LIBREPORT_RUN_CLI))
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
/* GUI won't work, use command line tool instead */
|
|
Packit |
4f15d5 |
event_name = "report-cli";
|
|
Packit |
4f15d5 |
path1 = BIN_DIR"/report-cli";
|
|
Packit |
4f15d5 |
path2 = "report-cli";
|
|
Packit |
4f15d5 |
pp = args;
|
|
Packit |
4f15d5 |
*pp++ = (char *)"report-cli";
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_DEL_DIR)
|
|
Packit |
4f15d5 |
*pp++ = (char *)"--delete";
|
|
Packit |
4f15d5 |
*pp++ = (char *)"-e";
|
|
Packit |
4f15d5 |
*pp++ = (char *)"report-cli";
|
|
Packit |
4f15d5 |
*pp++ = (char *)"--";
|
|
Packit |
4f15d5 |
*pp++ = (char *)dirname;
|
|
Packit |
4f15d5 |
*pp = NULL;
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
/* Some callers set SIGCHLD to SIG_IGN.
|
|
Packit |
4f15d5 |
* However, reporting spawns child processes.
|
|
Packit |
4f15d5 |
* Suppressing child death notification terribly confuses some of them.
|
|
Packit |
4f15d5 |
* Just in case, undo it.
|
|
Packit |
4f15d5 |
* Note that we do it in the child, so the parent is never affected.
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
signal(SIGCHLD, SIG_DFL);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (!(flags & (LIBREPORT_WAIT | LIBREPORT_GETPID)))
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
/* Caller doesn't want to wait for completion (!LIBREPORT_WAIT),
|
|
Packit |
4f15d5 |
* and doesn't want to have pid returned (!LIBREPORT_GETPID).
|
|
Packit |
4f15d5 |
* Create a grandchild, and then exit.
|
|
Packit |
4f15d5 |
* This reparents grandchild to init, and makes waitpid
|
|
Packit |
4f15d5 |
* in parent detect our exit and return almost immediately.
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
pid_t pid = fork();
|
|
Packit |
4f15d5 |
if (pid < 0) /* error */
|
|
Packit |
4f15d5 |
perror_msg_and_die("fork");
|
|
Packit |
4f15d5 |
if (pid != 0) /* not grandchild */
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
/* And now we exit: */
|
|
Packit |
4f15d5 |
_exit(0);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
/* There's an alternative approach to achieve this,
|
|
Packit |
4f15d5 |
* instead of using --delete.
|
|
Packit |
4f15d5 |
* We can create yet another intermediate process which
|
|
Packit |
4f15d5 |
* waits for reporting child to finish, and then
|
|
Packit |
4f15d5 |
* removes temporary dump dir.
|
|
Packit |
4f15d5 |
* Pros: deletion becomes more robust.
|
|
Packit |
4f15d5 |
* Even if child crashes, dir will be deleted.
|
|
Packit |
4f15d5 |
* Cons: having another process would use some resources,
|
|
Packit |
4f15d5 |
* and we'll need to at least close all open file descriptors,
|
|
Packit |
4f15d5 |
* and reopen stdio to /dev/null. We also might keep
|
|
Packit |
4f15d5 |
* a lot of libraries loaded:
|
|
Packit |
4f15d5 |
* who knows what parent process links against.
|
|
Packit |
4f15d5 |
* (can be worked around by exec'ing a "wait & delete" helper)
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
struct run_event_state *run_state = new_run_event_state();
|
|
Packit |
4f15d5 |
int r = run_event_on_dir_name(run_state, dirname, event_name);
|
|
Packit |
4f15d5 |
int no_such_event = (r == 0 && run_state->children_count == 0);
|
|
Packit |
4f15d5 |
free_run_event_state(run_state);
|
|
Packit |
4f15d5 |
if (!no_such_event)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_DEL_DIR)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
struct dump_dir *dd = dd_opendir(dirname, 0);
|
|
Packit |
4f15d5 |
if (dd)
|
|
Packit |
4f15d5 |
dd_delete(dd);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
_exit(r);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
/* No "report-cli/gui" event found, do it old-style */
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
path = path1;
|
|
Packit |
4f15d5 |
log_info("Executing: %s", path);
|
|
Packit |
4f15d5 |
execv(path, args);
|
|
Packit |
4f15d5 |
/* Did not find the desired executable in the installation directory.
|
|
Packit |
4f15d5 |
* Trying to find it in PATH.
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
path = path2;
|
|
Packit |
4f15d5 |
execvp(path, args);
|
|
Packit |
4f15d5 |
perror_msg_and_die("Can't execute %s", path);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
/* parent */
|
|
Packit |
4f15d5 |
free(prgname);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (!(flags & LIBREPORT_WAIT) && (flags & LIBREPORT_GETPID))
|
|
Packit |
4f15d5 |
return pid;
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
/* we are here either if LIBREPORT_WAIT (caller wants exitcode)
|
|
Packit |
4f15d5 |
* or !LIBREPORT_GETPID (caller doesn't want to have a child).
|
|
Packit |
4f15d5 |
* In both cases, we need to wait for child:
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
int status;
|
|
Packit |
4f15d5 |
pid = safe_waitpid(pid, &status, 0);
|
|
Packit |
4f15d5 |
if (pid <= 0)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
perror_msg("waitpid");
|
|
Packit |
4f15d5 |
return -1;
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (WIFEXITED(status))
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
log_info("reporting finished with exitcode %d", WEXITSTATUS(status));
|
|
Packit |
4f15d5 |
return WEXITSTATUS(status);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
/* child died from a signal: WIFSIGNALED(status) should be true */
|
|
Packit |
4f15d5 |
log_info("reporting killed by signal %d", WTERMSIG(status));
|
|
Packit |
4f15d5 |
return WTERMSIG(status) + 128;
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
int report_problem_in_memory(problem_data_t *pd, int flags)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
int result = 0;
|
|
Packit |
4f15d5 |
struct dump_dir *dd = create_dump_dir_from_problem_data(pd, LARGE_DATA_TMP_DIR);
|
|
Packit |
4f15d5 |
if (!dd)
|
|
Packit |
4f15d5 |
return -1;
|
|
Packit |
4f15d5 |
char *dir_name = xstrdup(dd->dd_dirname);
|
|
Packit |
4f15d5 |
dd_close(dd);
|
|
Packit |
4f15d5 |
log_info("Temp problem dir: '%s'", dir_name);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
if (!(flags & LIBREPORT_WAIT))
|
|
Packit |
4f15d5 |
flags |= LIBREPORT_DEL_DIR;
|
|
Packit |
4f15d5 |
result = report_problem_in_dir(dir_name, flags);
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
/* If we waited for reporter to finish, we should clean up the tmp dir
|
|
Packit |
4f15d5 |
* (if we didn't, cleaning up will be done by reporting child process later).
|
|
Packit |
4f15d5 |
* We can also reload the problem data if requested.
|
|
Packit |
4f15d5 |
*/
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_WAIT)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_RELOAD_DATA)
|
|
Packit |
4f15d5 |
g_hash_table_remove_all(pd);
|
|
Packit |
4f15d5 |
dd = dd_opendir(dir_name, 0);
|
|
Packit |
4f15d5 |
if (dd)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
if (flags & LIBREPORT_RELOAD_DATA)
|
|
Packit |
4f15d5 |
problem_data_load_from_dump_dir(pd, dd, NULL);
|
|
Packit |
4f15d5 |
dd_delete(dd);
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
free(dir_name);
|
|
Packit |
4f15d5 |
return result;
|
|
Packit |
4f15d5 |
}
|
|
Packit |
4f15d5 |
|
|
Packit |
4f15d5 |
int report_problem(problem_data_t *pd)
|
|
Packit |
4f15d5 |
{
|
|
Packit |
4f15d5 |
return report_problem_in_memory(pd, LIBREPORT_NOWAIT);
|
|
Packit |
4f15d5 |
}
|