Exemplo n.º 1
0
char *run_in_shell_and_save_output(int flags,
		const char *cmd,
		const char *dir,
		size_t *size_p)
{
	flags |= EXECFLG_OUTPUT;
	flags &= ~EXECFLG_INPUT;

	const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
	int pipeout[2];
	pid_t child = fork_execv_on_steroids(flags, (char **)argv, pipeout,
		/*env_vec:*/ NULL, dir, /*uid (unused):*/ 0);

	size_t pos = 0;
	char *result = NULL;
	while (1) {
		result = (char*) xrealloc(result, pos + 4*1024 + 1);
		size_t sz = safe_read(pipeout[0], result + pos, 4*1024);
		if (sz <= 0) {
			break;
		}
		pos += sz;
	}
	result[pos] = '\0';
	if (size_p)
		*size_p = pos;
	close(pipeout[0]);
	safe_waitpid(child, NULL, 0);

	return result;
}
Exemplo n.º 2
0
/* Signal pipe handler */
static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr_unused)
{
    uint8_t signo;
    gsize len = 0;
    g_io_channel_read_chars(gio, (void*) &signo, 1, &len, NULL);
    if (len == 1)
    {
        /* we did receive a signal */
        log_debug("Got signal %d through signal pipe", signo);
        if (signo != SIGCHLD)
            g_main_loop_quit(s_main_loop);
        else
        {
            pid_t cpid;
            int status;
            while ((cpid = safe_waitpid(-1, &status, WNOHANG)) > 0)
            {
                if (WIFSIGNALED(status))
                    log_debug("abrt-server(%d) signaled with %d", cpid, WTERMSIG(status));
                else if (WIFEXITED(status))
                    log_debug("abrt-server(%d) exited with %d", cpid, WEXITSTATUS(status));
                else
                {
                    log_debug("abrt-server(%d) is being debugged", cpid);
                    continue;
                }

                remove_abrt_server_proc(cpid, status);
            }
        }
    }
    start_idle_timeout();
    return TRUE; /* "please don't remove this event" */
}
Exemplo n.º 3
0
/// Blocks to wait for completion of a subprocess.
///
/// \param pid Identifier of the process to wait for.
///
/// \return The termination status of the child process that terminated.
///
/// \throw process::system_error If the call to wait(2) fails.
process::status
process::wait(const int pid)
{
    const process::status status = safe_waitpid(pid);
    {
        signals::interrupts_inhibiter inhibiter;
        signals::remove_pid_to_kill(pid);
    }
    return status;
}
Exemplo n.º 4
0
static int
decompress_using_fork_execvp(const char** cmd, int fdi, int fdo)
{
    pid_t child = fork();
    if (child < 0)
    {
        VERB1 perror_msg("fork() for decompression");
        return -1;
    }

    if (child == 0)
    {
        close(STDIN_FILENO);
        if (dup2(fdi, STDIN_FILENO) < 0)
        {
            VERB1 perror_msg("Decompression failed: dup2(fdi, STDIN_FILENO)");
            exit(EXIT_FAILURE);
        }

        close(STDOUT_FILENO);
        if (dup2(fdo, STDOUT_FILENO) < 0)
        {
            VERB1 perror_msg("Decompression failed: dup2(fdo, STDOUT_FILENO)");
            exit(EXIT_FAILURE);
        }

        execvp(cmd[0], (char **)cmd);

        VERB1 perror_msg("Decompression failed: execlp('%s')", cmd[0]);
        exit(EXIT_FAILURE);
    }

    int status = 0;
    int r = safe_waitpid(child, &status, 0);
    if (r < 0)
    {
        VERB1 perror_msg("Decompression failed: waitpid($1) failed");
        return -2;
    }

    if (!WIFEXITED(status))
    {
        log_info("Decompression process returned abnormally");
        return -3;
    }

    if (WEXITSTATUS(status) != 0)
    {
        log_info("Decompression process exited with %d", WEXITSTATUS(r));
        return -4;
    }

    return 0;
}
Exemplo n.º 5
0
// generic signal handler
static void signal_handler(int signo)
{
#define err signo
	if (SIGALRM == signo) {
		bb_error_msg_and_die("timed out");
	}

	// SIGCHLD. reap zombies
	if (safe_waitpid(G.helper_pid, &err, WNOHANG) > 0) {
		if (WIFSIGNALED(err))
			bb_error_msg_and_die("helper killed by signal %u", WTERMSIG(err));
		if (WIFEXITED(err)) {
			G.helper_pid = 0;
			if (WEXITSTATUS(err))
				bb_error_msg_and_die("helper exited (%u)", WEXITSTATUS(err));
		}
	}
#undef err
}
Exemplo n.º 6
0
static unsigned custom(struct svdir *s, char c)
{
	int pid;
	int w;
	char a[10];
	struct stat st;
	char *prog[2];

	if (s->islog) return 0;
	strcpy(a, "control/?");
	a[8] = c; /* replace '?' */
	if (stat(a, &st) == 0) {
		if (st.st_mode & S_IXUSR) {
			pid = vfork();
			if (pid == -1) {
				warn_cannot("vfork for control/?");
				return 0;
			}
			if (!pid) {
				/* child */
				if (haslog && dup2(logpipe.wr, 1) == -1)
					warn_cannot("setup stdout for control/?");
				prog[0] = a;
				prog[1] = NULL;
				execv(a, prog);
				fatal_cannot("run control/?");
			}
			/* parent */
			while (safe_waitpid(pid, &w, 0) == -1) {
				warn_cannot("wait for child control/?");
				return 0;
			}
			return !wait_exitcode(w);
		}
	} else {
		if (errno != ENOENT)
			warn_cannot("stat control/?");
	}
	return 0;
}
/* This does a fork/exec in one call, using vfork().  Returns PID of new child,
 * -1 for failure.  Runs argv[0], searching path if that has no / in it. */
pid_t FAST_FUNC spawn(char **argv)
{
	/* Compiler should not optimize stores here */
	volatile int failed;
	pid_t pid;

	fflush_all();

	/* Be nice to nommu machines. */
	failed = 0;
	pid = vfork();
	if (pid < 0) /* error */
		return pid;
	if (!pid) { /* child */
		/* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
		BB_EXECVP(argv[0], argv);

		/* We are (maybe) sharing a stack with blocked parent,
		 * let parent know we failed and then exit to unblock parent
		 * (but don't run atexit() stuff, which would screw up parent.)
		 */
		failed = errno;
		/* mount, for example, does not want the message */
		/*bb_perror_msg("can't execute '%s'", argv[0]);*/
		_exit(111);
	}
	/* parent */
	/* Unfortunately, this is not reliable: according to standards
	 * vfork() can be equivalent to fork() and we won't see value
	 * of 'failed'.
	 * Interested party can wait on pid and learn exit code.
	 * If 111 - then it (most probably) failed to exec */
	if (failed) {
		safe_waitpid(pid, NULL, 0); /* prevent zombie */
		errno = failed;
		return -1;
	}
	return pid;
}
Exemplo n.º 8
0
static unsigned custom(struct svdir *s, char c)
{
	pid_t pid;
	int w;
	char a[10];
	struct stat st;

	if (s->islog)
		return 0;
	strcpy(a, "control/?");
	a[8] = c; /* replace '?' */
	if (stat(a, &st) == 0) {
		if (st.st_mode & S_IXUSR) {
			pid = vfork();
			if (pid == -1) {
				warn_cannot("vfork for control/?");
				return 0;
			}
			if (pid == 0) {
				/* child */
				if (haslog && dup2(logpipe.wr, 1) == -1)
					warn_cannot("setup stdout for control/?");
				execl(a, a, (char *) NULL);
				fatal_cannot("run control/?");
			}
			/* parent */
			if (safe_waitpid(pid, &w, 0) == -1) {
				warn_cannot("wait for child control/?");
				return 0;
			}
			return WEXITSTATUS(w) == 0;
		}
	} else {
		if (errno != ENOENT)
			warn_cannot("stat control/?");
	}
	return 0;
}
Exemplo n.º 9
0
static int run_post_create(const char *dirname)
{
    /* If doesn't start with "g_settings_dump_location/"... */
    if (!dir_is_in_dump_location(dirname))
    {
        /* Then refuse to operate on it (someone is attacking us??) */
        error_msg("Bad problem directory name '%s', should start with: '%s'", dirname, g_settings_dump_location);
        return 400; /* Bad Request */
    }
    if (!dir_has_correct_permissions(dirname, DD_PERM_EVENTS))
    {
        error_msg("Problem directory '%s' has wrong owner or group", dirname);
        return 400; /*  */
    }
    /* Check completness */
    {
        struct dump_dir *dd = dd_opendir(dirname, DD_OPEN_READONLY);

        char *provoker = NULL;
        const bool event_dir = dd && problem_dump_dir_was_provoked_by_abrt_event(dd, &provoker);
        if (event_dir)
        {
            if (g_settings_debug_level == 0)
            {
                error_msg("Removing problem provoked by ABRT(pid:%s): '%s'", provoker, dirname);
                dd_delete(dd);
            }
            else
            {
                char *dumpdir = NULL;
                char *event   = NULL;
                char *reason  = NULL;
                char *cmdline = NULL;

                /* Ignore errors */
                dd_get_env_variable(dd, "DUMP_DIR", &dumpdir);
                dd_get_env_variable(dd, "EVENT",    &event);
                reason  = dd_load_text(dd, FILENAME_REASON);
                cmdline = dd_load_text(dd, FILENAME_CMDLINE);

                error_msg("ABRT_SERVER_PID=%s;DUMP_DIR='%s';EVENT='%s';REASON='%s';CMDLINE='%s'",
                           provoker, dumpdir, event, reason, cmdline);

            }

            free(provoker);
            return 400;
        }

        const bool complete = dd && problem_dump_dir_is_complete(dd);
        dd_close(dd);
        if (complete)
        {
            error_msg("Problem directory '%s' has already been processed", dirname);
            return 403;
        }
    }

    int child_stdout_fd;
    int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd);

    char *dup_of_dir = NULL;
    struct strbuf *cmd_output = strbuf_new();

    bool child_is_post_create = 1; /* else it is a notify child */

 read_child_output:
    //log("Reading from event fd %d", child_stdout_fd);

    /* Read streamed data and split lines */
    for (;;)
    {
        char buf[250]; /* usually we get one line, no need to have big buf */
        errno = 0;
        int r = safe_read(child_stdout_fd, buf, sizeof(buf) - 1);
        if (r <= 0)
            break;
        buf[r] = '\0';

        /* split lines in the current buffer */
        char *raw = buf;
        char *newline;
        while ((newline = strchr(raw, '\n')) != NULL)
        {
            *newline = '\0';
            strbuf_append_str(cmd_output, raw);
            char *msg = cmd_output->buf;

            if (child_is_post_create
             && prefixcmp(msg, "DUP_OF_DIR: ") == 0
            ) {
                free(dup_of_dir);
                dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: "));
            }
            else
                log("%s", msg);

            strbuf_clear(cmd_output);
            /* jump to next line */
            raw = newline + 1;
        }

        /* beginning of next line. the line continues by next read */
        strbuf_append_str(cmd_output, raw);
    }

    /* EOF/error */

    /* Wait for child to actually exit, collect status */
    int status = 0;
    if (safe_waitpid(child_pid, &status, 0) <= 0)
    /* should not happen */
        perror_msg("waitpid(%d)", child_pid);

    /* If it was a "notify[-dup]" event, then we're done */
    if (!child_is_post_create)
        goto ret;

    /* exit 0 means "this is a good, non-dup dir" */
    /* exit with 1 + "DUP_OF_DIR: dir" string => dup */
    if (status != 0)
    {
        if (WIFSIGNALED(status))
        {
            log("'post-create' on '%s' killed by signal %d",
                            dirname, WTERMSIG(status));
            goto delete_bad_dir;
        }
        /* else: it is WIFEXITED(status) */
        if (!dup_of_dir)
        {
            log("'post-create' on '%s' exited with %d",
                            dirname, WEXITSTATUS(status));
            goto delete_bad_dir;
        }
    }

    const char *work_dir = (dup_of_dir ? dup_of_dir : dirname);

    /* Load problem_data (from the *first dir* if this one is a dup) */
    struct dump_dir *dd = dd_opendir(work_dir, /*flags:*/ 0);
    if (!dd)
        /* dd_opendir already emitted error msg */
        goto delete_bad_dir;

    /* Update count */
    char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT);
    unsigned long count = strtoul(count_str, NULL, 10);

    /* Don't increase crash count if we are working with newly uploaded
     * directory (remote crash) which already has its crash count set.
     */
    if ((status != 0 && dup_of_dir) || count == 0)
    {
        count++;
        char new_count_str[sizeof(long)*3 + 2];
        sprintf(new_count_str, "%lu", count);
        dd_save_text(dd, FILENAME_COUNT, new_count_str);

        /* This condition can be simplified to either
         * (status * != 0 && * dup_of_dir) or (count == 1). But the
         * chosen form is much more reliable and safe. We must not call
         * dd_opendir() to locked dd otherwise we go into a deadlock.
         */
        if (strcmp(dd->dd_dirname, dirname) != 0)
        {
            /* Update the last occurrence file by the time file of the new problem */
            struct dump_dir *new_dd = dd_opendir(dirname, DD_OPEN_READONLY);
            char *last_ocr = NULL;
            if (new_dd)
            {
                /* TIME must exists in a valid dump directory but we don't want to die
                 * due to broken duplicated dump directory */
                last_ocr = dd_load_text_ext(new_dd, FILENAME_TIME,
                            DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT);
                dd_close(new_dd);
            }
            else
            {   /* dd_opendir() already produced a message with good information about failure */
                error_msg("Can't read the last occurrence file from the new dump directory.");
            }

            if (!last_ocr)
            {   /* the new dump directory may lie in the dump location for some time */
                log("Using current time for the last occurrence file which may be incorrect.");
                time_t t = time(NULL);
                last_ocr = xasprintf("%lu", (long)t);
            }

            dd_save_text(dd, FILENAME_LAST_OCCURRENCE, last_ocr);

            free(last_ocr);
        }
    }

    /* Reset mode/uig/gid to correct values for all files created by event run */
    dd_sanitize_mode_and_owner(dd);

    dd_close(dd);

    if (!dup_of_dir)
        log_notice("New problem directory %s, processing", work_dir);
    else
    {
        log_warning("Deleting problem directory %s (dup of %s)",
                    strrchr(dirname, '/') + 1,
                    strrchr(dup_of_dir, '/') + 1);
        delete_dump_dir(dirname);
    }

    /* Run "notify[-dup]" event */
    int fd;
    child_pid = spawn_event_handler_child(
                work_dir,
                (dup_of_dir ? "notify-dup" : "notify"),
                &fd
    );
    //log("Started notify, fd %d -> %d", fd, child_stdout_fd);
    xmove_fd(fd, child_stdout_fd);
    child_is_post_create = 0;
    strbuf_clear(cmd_output);
    free(dup_of_dir);
    dup_of_dir = NULL;
    goto read_child_output;

 delete_bad_dir:
    log_warning("Deleting problem directory '%s'", dirname);
    delete_dump_dir(dirname);

 ret:
    strbuf_free(cmd_output);
    free(dup_of_dir);
    close(child_stdout_fd);
    return 0;
}
Exemplo n.º 10
0
static void run_scanner_prog(int fd, struct stat *statbuf, GList *match_list, char **prog)
{
    /* fstat(fd, &statbuf) was just done by caller */

    off_t cur_pos = lseek(fd, 0, SEEK_CUR);
    if (statbuf->st_size <= cur_pos)
    {
        /* If file was truncated, treat it as a new file.
         * (changing inode# causes caller to think that file was closed or renamed)
         */
        if (statbuf->st_size < cur_pos)
            statbuf->st_ino++;
        return; /* we are at EOF, nothing to do */
    }

    log_info("File grew by %llu bytes, from %llu to %llu",
        (long long)(statbuf->st_size - cur_pos),
        (long long)(cur_pos),
        (long long)(statbuf->st_size));

    if (match_list && (statbuf->st_size - cur_pos) < MAX_SCAN_BLOCK)
    {
        size_t length = statbuf->st_size - cur_pos;

        off_t mapofs = cur_pos & ~(off_t)(page_size - 1);
        size_t maplen = statbuf->st_size - mapofs;
        void *map = mmap(NULL, maplen, PROT_READ, MAP_SHARED, fd, mapofs);

        if (map != MAP_FAILED)
        {
            char *start = (char*)map + (cur_pos & (page_size - 1));
            for (GList *l = match_list; l; l = l->next)
            {
                log_debug("Searching for '%s' in '%.*s'",
                                (char*)l->data,
                                length > 20 ? 20 : (int)length, start
                );
                if (memstr(start, length, (char*)l->data))
                {
                    log_debug("FOUND:'%s'", (char*)l->data);
                    goto found;
                }
            }
            /* None of the strings are found */
            log_debug("NOT FOUND");
            munmap(map, maplen);
            lseek(fd, statbuf->st_size, SEEK_SET);
            return;
 found: ;
            munmap(map, maplen);
        }
    }

    fflush(NULL); /* paranoia */
    pid_t pid = vfork();
    if (pid < 0)
        perror_msg_and_die("vfork");
    if (pid == 0)
    {
        xmove_fd(fd, STDIN_FILENO);
        log_debug("Execing '%s'", prog[0]);
        execvp(prog[0], prog);
        perror_msg_and_die("Can't execute '%s'", prog[0]);
    }

    safe_waitpid(pid, NULL, 0);

    /* Check fd's position, and move to end if it wasn't advanced.
     * This means that child failed to read its stdin.
     * This is not supposed to happen, so warn about it.
     */
    if (lseek(fd, 0, SEEK_CUR) <= cur_pos)
    {
        log("Warning, '%s' did not process its input", prog[0]);
        lseek(fd, statbuf->st_size, SEEK_SET);
    }
}
/* A binary wrapper is needed around python scripts if we want
 * to run them in sgid/suid mode.
 *
 * This is such a wrapper.
 */
int main(int argc, char **argv)
{
    /* I18n */
    setlocale(LC_ALL, "");
#if ENABLE_NLS
    bindtextdomain(PACKAGE, LOCALEDIR);
    textdomain(PACKAGE);
#endif

    abrt_init(argv);

    /* Can't keep these strings/structs static: _() doesn't support that */
    const char *program_usage_string = _(
                                           "& [-y] [-i BUILD_IDS_FILE|-i -] [-e PATH[:PATH]...]\n"
                                           "\t[-r REPO]\n"
                                           "\n"
                                           "Installs debuginfo packages for all build-ids listed in BUILD_IDS_FILE to\n"
                                           "ABRT system cache."
                                       );

    enum {
        OPT_v = 1 << 0,
        OPT_y = 1 << 1,
        OPT_i = 1 << 2,
        OPT_e = 1 << 3,
        OPT_r = 1 << 4,
        OPT_s = 1 << 5,
    };

    const char *build_ids = "build_ids";
    const char *exact = NULL;
    const char *repo = NULL;
    const char *size_mb = NULL;

    struct options program_options[] = {
        OPT__VERBOSE(&g_verbose),
        OPT_BOOL  ('y', "yes",         NULL,                   _("Noninteractive, assume 'Yes' to all questions")),
        OPT_STRING('i', "ids",   &build_ids, "BUILD_IDS_FILE", _("- means STDIN, default: build_ids")),
        OPT_STRING('e', "exact",     &exact, "EXACT",          _("Download only specified files")),
        OPT_STRING('r', "repo",       &repo, "REPO",           _("Pattern to use when searching for repos, default: *debug*")),
        OPT_STRING('s', "size_mb", &size_mb, "SIZE_MB",        _("Ignored option")),
        OPT_END()
    };
    const unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);

    const gid_t egid = getegid();
    const gid_t rgid = getgid();
    const uid_t euid = geteuid();
    const gid_t ruid = getuid();

    /* We need to open the build ids file under the caller's UID/GID to avoid
     * information disclosures when reading files with changed UID.
     * Unfortunately, we cannot replace STDIN with the new fd because ABRT uses
     * STDIN to communicate with the caller. So, the following code opens a
     * dummy file descriptor to the build ids file and passes the new fd's proc
     * path to the wrapped program in the ids argument.
     * The new fd remains opened, the OS will close it for us. */
    char *build_ids_self_fd = NULL;
    if (strcmp("-", build_ids) != 0)
    {
        if (setregid(egid, rgid) < 0)
            perror_msg_and_die("setregid(egid, rgid)");

        if (setreuid(euid, ruid) < 0)
            perror_msg_and_die("setreuid(euid, ruid)");

        const int build_ids_fd = open(build_ids, O_RDONLY);

        if (setregid(rgid, egid) < 0)
            perror_msg_and_die("setregid(rgid, egid)");

        if (setreuid(ruid, euid) < 0 )
            perror_msg_and_die("setreuid(ruid, euid)");

        if (build_ids_fd < 0)
            perror_msg_and_die("Failed to open file '%s'", build_ids);

        /* We are not going to free this memory. There is no place to do so. */
        build_ids_self_fd = xasprintf("/proc/self/fd/%d", build_ids_fd);
    }

    char tmp_directory[] = LARGE_DATA_TMP_DIR"/abrt-tmp-debuginfo.XXXXXX";
    if (mkdtemp(tmp_directory) == NULL)
        perror_msg_and_die("Failed to create working directory");

    log_info("Created working directory: %s", tmp_directory);

    /* name, -v, --ids, -, -y, -e, EXACT, -r, REPO, -t, PATH, --, NULL */
    const char *args[13];
    {
        const char *verbs[] = { "", "-v", "-vv", "-vvv" };
        unsigned i = 0;
        args[i++] = EXECUTABLE;
        args[i++] = "--ids";
        args[i++] = (build_ids_self_fd != NULL) ? build_ids_self_fd : "-";
        if (g_verbose > 0)
            args[i++] = verbs[g_verbose <= 3 ? g_verbose : 3];
        if ((opts & OPT_y))
            args[i++] = "-y";
        if ((opts & OPT_e))
        {
            args[i++] = "--exact";
            args[i++] = exact;
        }
        if ((opts & OPT_r))
        {
            args[i++] = "--repo";
            args[i++] = repo;
        }
        args[i++] = "--tmpdir";
        args[i++] = tmp_directory;
        args[i++] = "--";
        args[i] = NULL;
    }

    /* Switch real user/group to effective ones.
     * Otherwise yum library gets confused - gets EPERM (why??).
     */
    /* do setregid only if we have to, to not upset selinux needlessly */
    if (egid != rgid)
        IGNORE_RESULT(setregid(egid, egid));
    if (euid != ruid)
    {
        IGNORE_RESULT(setreuid(euid, euid));
        /* We are suid'ed! */
        /* Prevent malicious user from messing up with suid'ed process: */
#if 1
// We forgot to sanitize PYTHONPATH. And who knows what else we forgot
// (especially considering *future* new variables of this kind).
// We switched to clearing entire environment instead:

        // However since we communicate through environment variables
        // we have to keep a whitelist of variables to keep.
        static const char *whitelist[] = {
            "REPORT_CLIENT_SLAVE", //  Check if the app is being run as a slave
            "LANG",
        };
        const size_t wlsize = sizeof(whitelist)/sizeof(char*);
        char *setlist[sizeof(whitelist)/sizeof(char*)] = { 0 };
        char *p = NULL;
        for (size_t i = 0; i < wlsize; i++)
            if ((p = getenv(whitelist[i])) != NULL)
                setlist[i] = xstrdup(p);

        // Now we can clear the environment
        clearenv();

        // And once again set whitelisted variables
        for (size_t i = 0; i < wlsize; i++)
            if (setlist[i] != NULL)
            {
                xsetenv(whitelist[i], setlist[i]);
                free(setlist[i]);
            }
#else
        /* Clear dangerous stuff from env */
        static const char forbid[] =
            "LD_LIBRARY_PATH" "\0"
            "LD_PRELOAD" "\0"
            "LD_TRACE_LOADED_OBJECTS" "\0"
            "LD_BIND_NOW" "\0"
            "LD_AOUT_LIBRARY_PATH" "\0"
            "LD_AOUT_PRELOAD" "\0"
            "LD_NOWARN" "\0"
            "LD_KEEPDIR" "\0"
            ;
        const char *p = forbid;
        do {
            unsetenv(p);
            p += strlen(p) + 1;
        } while (*p);
#endif
        /* Set safe PATH */
        // Adding configure --bindir and --sbindir to the PATH so that
        // abrt-action-install-debuginfo doesn't fail when spawning
        // abrt-action-trim-files
        char path_env[] = "PATH=/usr/sbin:/sbin:/usr/bin:/bin:"BIN_DIR":"SBIN_DIR;
        if (euid != 0)
            strcpy(path_env, "PATH=/usr/bin:/bin:"BIN_DIR);
        putenv(path_env);

        /* Use safe umask */
        umask(0022);
    }

    pid_t pid = fork();
    if (pid < 0)
        perror_msg_and_die("fork");

    if (pid == 0)
    {
        execvp(EXECUTABLE, (char **)args);
        error_msg_and_die("Can't execute %s", EXECUTABLE);
    }

    int status;
    if (safe_waitpid(pid, &status, 0) < 0)
        perror_msg_and_die("waitpid");

    if (rmdir(tmp_directory) >= 0)
        log_info("Removed working directory: %s", tmp_directory);
    else if (errno != ENOENT)
        perror_msg("Failed to remove working directory");

    /* Normal execution should exit here. */
    if (WIFEXITED(status))
        return WEXITSTATUS(status);

    if (WIFSIGNALED(status))
        error_msg_and_die("Child terminated with signal %d", WTERMSIG(status));

    error_msg_and_die("Child exit failed");
}
Exemplo n.º 12
0
/**
 *
 * @param[out] status See `man 2 wait` for status information.
 * @return Malloc'ed string
 */
static char* exec_vp(char **args, int redirect_stderr, int exec_timeout_sec, int *status)
{
    /* Nuke everything which may make setlocale() switch to non-POSIX locale:
     * we need to avoid having gdb output in some obscure language.
     */
    static const char *const env_vec[] = {
        "LANG",
        "LC_ALL",
        "LC_COLLATE",
        "LC_CTYPE",
        "LC_MESSAGES",
        "LC_MONETARY",
        "LC_NUMERIC",
        "LC_TIME",
        /* Workaround for
         * http://sourceware.org/bugzilla/show_bug.cgi?id=9622
         * (gdb emitting ESC sequences even with -batch)
         */
        "TERM",
        NULL
    };

    int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_SETSID | EXECFLG_QUIET;
    if (redirect_stderr)
        flags |= EXECFLG_ERR2OUT;
    VERB1 flags &= ~EXECFLG_QUIET;

    int pipeout[2];
    pid_t child = fork_execv_on_steroids(flags, args, pipeout, (char**)env_vec, /*dir:*/ NULL, /*uid(unused):*/ 0);

    /* We use this function to run gdb and unstrip. Bugs in gdb or corrupted
     * coredumps were observed to cause gdb to enter infinite loop.
     * Therefore we have a (largish) timeout, after which we kill the child.
     */
    ndelay_on(pipeout[0]);
    int t = time(NULL); /* int is enough, no need to use time_t */
    int endtime = t + exec_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, "\n"
                        "Timeout exceeded: %u seconds, killing %s.\n"
                        "Looks like gdb hung while generating backtrace.\n"
                        "This may be a bug in gdb. Consider submitting a bug report to gdb developers.\n"
                        "Please attach coredump from this crash to the bug report if you do.\n",
                        exec_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)
        {
            /* I did see EAGAIN happening here */
            if (r < 0 && errno == EAGAIN)
                goto next;
            break;
        }
        buff[r] = '\0';
        strbuf_append_str(buf_out, buff);
 next:
        t = time(NULL);
    }
    close(pipeout[0]);

    /* Prevent having zombie child process, and maybe collect status
     * (note that status == NULL is ok too) */
    safe_waitpid(child, status, 0);

    return strbuf_free_nobuf(buf_out);
}
Exemplo n.º 13
0
Arquivo: tar.c Projeto: ph4r05/boinc
static int writeTarFile(const int tar_fd, const int verboseFlag,
	const unsigned long dereferenceFlag, const llist_t *include,
	const llist_t *exclude, const int gzip)
{
	pid_t gzipPid = 0;
	int errorFlag = FALSE;
	struct TarBallInfo tbInfo;

	tbInfo.hlInfoHead = NULL;

	fchmod(tar_fd, 0644);
	tbInfo.tarFd = tar_fd;
	tbInfo.verboseFlag = verboseFlag;

	/* Store the stat info for the tarball's file, so
	 * can avoid including the tarball into itself....  */
	if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
		bb_perror_msg_and_die("cannot stat tar file");

#if ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2
	if (gzip) {
#if ENABLE_FEATURE_TAR_GZIP && ENABLE_FEATURE_TAR_BZIP2
		const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
#elif ENABLE_FEATURE_TAR_GZIP
		const char *zip_exec = "gzip";
#else /* only ENABLE_FEATURE_TAR_BZIP2 */
		const char *zip_exec = "bzip2";
#endif
	// On Linux, vfork never unpauses parent early, although standard
	// allows for that. Do we want to waste bytes checking for it?
#define WAIT_FOR_CHILD 0
		volatile int vfork_exec_errno = 0;
#if WAIT_FOR_CHILD
		struct fd_pair gzipStatusPipe;
#endif
		struct fd_pair gzipDataPipe;
		xpiped_pair(gzipDataPipe);
#if WAIT_FOR_CHILD
		xpiped_pair(gzipStatusPipe);
#endif

		signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */

#if defined(__GNUC__) && __GNUC__
		/* Avoid vfork clobbering */
		(void) &include;
		(void) &errorFlag;
		(void) &zip_exec;
#endif

		gzipPid = vfork();
		if (gzipPid < 0)
			bb_perror_msg_and_die("vfork gzip");

		if (gzipPid == 0) {
			/* child */
			/* NB: close _first_, then move fds! */
			close(gzipDataPipe.wr);
#if WAIT_FOR_CHILD
			close(gzipStatusPipe.rd);
			/* gzipStatusPipe.wr will close only on exec -
			 * parent waits for this close to happen */
			fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC);
#endif
			xmove_fd(gzipDataPipe.rd, 0);
			xmove_fd(tbInfo.tarFd, 1);
			/* exec gzip/bzip2 program/applet */
			BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
			vfork_exec_errno = errno;
			_exit(1);
		}

		/* parent */
		xmove_fd(gzipDataPipe.wr, tbInfo.tarFd);
		close(gzipDataPipe.rd);
#if WAIT_FOR_CHILD
		close(gzipStatusPipe.wr);
		while (1) {
			char buf;
			int n;

			/* Wait until child execs (or fails to) */
			n = full_read(gzipStatusPipe.rd, &buf, 1);
			if (n < 0 /* && errno == EAGAIN */)
				continue;	/* try it again */

		}
		close(gzipStatusPipe.rd);
#endif
		if (vfork_exec_errno) {
			errno = vfork_exec_errno;
			bb_perror_msg_and_die("cannot exec %s", zip_exec);
		}
	}
#endif

	tbInfo.excludeList = exclude;

	/* Read the directory/files and iterate over them one at a time */
	while (include) {
		if (!recursive_action(include->data, ACTION_RECURSE |
				(dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
				writeFileToTarball, writeFileToTarball, &tbInfo, 0))
		{
			errorFlag = TRUE;
		}
		include = include->link;
	}
	/* Write two empty blocks to the end of the archive */
	memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
	xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);

	/* To be pedantically correct, we would check if the tarball
	 * is smaller than 20 tar blocks, and pad it if it was smaller,
	 * but that isn't necessary for GNU tar interoperability, and
	 * so is considered a waste of space */

	/* Close so the child process (if any) will exit */
	close(tbInfo.tarFd);

	/* Hang up the tools, close up shop, head home */
	if (ENABLE_FEATURE_CLEAN_UP)
		freeHardLinkInfo(&tbInfo.hlInfoHead);

	if (errorFlag)
		bb_error_msg("error exit delayed from previous errors");

	if (gzipPid) {
#if ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2
		int status;
		if (safe_waitpid(gzipPid, &status, 0) == -1)
			bb_perror_msg("waitpid");
		else if (!WIFEXITED(status) || WEXITSTATUS(status))
			/* gzip was killed or has exited with nonzero! */
			errorFlag = TRUE;
#endif
	}
	return errorFlag;
}
Exemplo n.º 14
0
int openvt_main(int argc UNUSED_PARAM, char **argv)
{
	char vtname[sizeof(VC_FORMAT) + sizeof(int)*3];
	struct vt_stat vtstat;
	char *str_c;
	int vtno;
	int flags;
	enum {
		OPT_c = (1 << 0),
		OPT_w = (1 << 1),
		OPT_s = (1 << 2),
		OPT_l = (1 << 3),
		OPT_f = (1 << 4),
		OPT_v = (1 << 5),
	};

	/* "+" - stop on first non-option */
	flags = getopt32(argv, "+c:wslfv", &str_c);
	argv += optind;

	if (flags & OPT_c) {
		/* Check for illegal vt number: < 1 or > 63 */
		vtno = xatou_range(str_c, 1, 63);
	} else {
		vtno = find_free_vtno();
	}

	/* Grab new VT */
	sprintf(vtname, VC_FORMAT, vtno);
	/* (Try to) clean up stray open fds above fd 2 */
	bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL);
	close(STDIN_FILENO);
	/*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
	xopen(vtname, O_RDWR);
	xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat);

	if (flags & OPT_s) {
		console_make_active(STDIN_FILENO, vtno);
	}

	if (!argv[0]) {
		argv--;
		argv[0] = getenv("SHELL");
		if (!argv[0])
			argv[0] = (char *) DEFAULT_SHELL;
		/*argv[1] = NULL; - already is */
	}

	xdup2(STDIN_FILENO, STDOUT_FILENO);
	xdup2(STDIN_FILENO, STDERR_FILENO);

#ifdef BLOAT
	{
	/* Handle -l (login shell) option */
	const char *prog = argv[0];
	if (flags & OPT_l)
		argv[0] = xasprintf("-%s", argv[0]);
	}
#endif

	vfork_child(argv);
	if (flags & OPT_w) {
		/* We have only one child, wait for it */
		safe_waitpid(-1, NULL, 0); /* loops on EINTR */
		if (flags & OPT_s) {
			console_make_active(STDIN_FILENO, vtstat.v_active);
			// Compat: even with -c N (try to) disallocate:
			// # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5
			// openvt: could not deallocate console 9
			xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno);
		}
	}
	return EXIT_SUCCESS;
}
Exemplo n.º 15
0
int consume_event_command_output(struct run_event_state *state, const char *dump_dir_name)
{
    int r = 0;
    char buf[256];
    errno = 0;
    struct strbuf *cmd_output = state->command_output;
    while ((r = safe_read(state->command_out_fd, buf, sizeof(buf) - 1)) > 0)
    {
        char *newline;
        char *raw;
        buf[r] = '\0';
        raw = buf;

        while ((newline = strchr(raw, '\n')) != NULL)
        {
            *newline = '\0';
            strbuf_append_str(cmd_output, raw);
            char *msg = cmd_output->buf;

            char *response = NULL;

            /* just cut off prefix, no waiting */
            if (prefixcmp(msg, REPORT_PREFIX_ALERT) == 0)
            {
                state->alert_callback(msg + sizeof(REPORT_PREFIX_ALERT) - 1 , state->interaction_param);
            }
            /* wait for y/N/f response on the same line */
            else if (prefixcmp(msg, REPORT_PREFIX_ASK_YES_NO_YESFOREVER) == 0)
            {
                /* example:
                 *   ASK_YES_NO_YESFOREVER ask_before_delete Do you want to delete selected files?
                 */
                char *key = msg + sizeof(REPORT_PREFIX_ASK_YES_NO_YESFOREVER) - 1;
                char *key_end = strchr(key, ' ');

                bool ans = false;

                if (!key_end)
                {   /* example:
                     *  ASK_YES_NO_YESFOREVER Continue?
                     *
                     * Print a wraning only and do not scary users with error messages.
                     */
                    log_warning("invalid input format (missing option name), using simple ask yes/no");

                    /* can't simply use 'goto ask_yes_no' because of different lenght of prefixes */
                    ans = state->ask_yes_no_callback(key, state->interaction_param);
                }
                else
                {
                    key_end[0] = '\0'; /* split 'key msg' to 'key' and 'msg' */
                    ans = state->ask_yes_no_yesforever_callback(key, key + strlen(key) + 1, state->interaction_param);
                    key_end[0] = ' '; /* restore original message, not sure if it is necessary */
                }

                response = xstrdup(ans ? "y" : "N");
            }
            /* wait for y/N response on the same line */
            else if (prefixcmp(msg, REPORT_PREFIX_ASK_YES_NO) == 0)
            {
                const bool ans = state->ask_yes_no_callback(msg + sizeof(REPORT_PREFIX_ASK_YES_NO) - 1, state->interaction_param);
                response = xstrdup(ans ? "y" : "N");
            }
            /* wait for the string on the same line */
            else if (prefixcmp(msg, REPORT_PREFIX_ASK) == 0)
            {
                response = state->ask_callback(msg + sizeof(REPORT_PREFIX_ASK) - 1, state->interaction_param);
            }
            /* set echo off and wait for password on the same line */
            else if (prefixcmp(msg, REPORT_PREFIX_ASK_PASSWORD) == 0)
            {
                response = state->ask_password_callback(msg + sizeof(REPORT_PREFIX_ASK_PASSWORD) - 1, state->interaction_param);
            }
            /* no special prefix -> forward to log if applicable
             * note that callback may take ownership of buf by returning NULL */
            else if (state->logging_callback)
            {
                char *logged = state->logging_callback(xstrdup(msg), state->logging_param);
                free(logged);
            }

            if (response)
            {
                size_t len = strlen(response);
                response[len++] = '\n';

                if (full_write(state->command_in_fd, response, len) != len)
                {
                    if (state->error_callback)
                        state->error_callback("<WRITE ERROR>", state->error_param);
                    else
                        perror_msg_and_die("Can't write %zu bytes to child's stdin", len);
                }

                free(response);
            }

            strbuf_clear(cmd_output);

            /* jump to next line */
            raw = newline + 1;
        }

        /* beginning of next line. the line continues by next read() */
        strbuf_append_str(cmd_output, raw);
    }

    /* Hope that child's stdout fd was set to O_NONBLOCK */
    if (r == -1 && errno == EAGAIN)
        return -1;

    strbuf_clear(cmd_output);

    /* Wait for child to actually exit, collect status */
    safe_waitpid(state->command_pid, &(state->process_status), 0);

    int retval = WEXITSTATUS(state->process_status);
    if (WIFSIGNALED(state->process_status))
        retval = WTERMSIG(state->process_status) + 128;

    if (retval == 0 && state->post_run_callback)
        retval = state->post_run_callback(dump_dir_name, state->post_run_param);

    return retval;
}
Exemplo n.º 16
0
static int create_and_upload_archive(
                const char *dump_dir_name,
                map_string_t *settings)
{
    int result = 1; /* error */

    pid_t child;
    TAR* tar = NULL;
    const char* errmsg = NULL;
    char* tempfile = NULL;

    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
    if (!dd)
        xfunc_die(); /* error msg is already logged by dd_opendir */

    /* Gzipping e.g. 0.5gig coredump takes a while. Let client know what we are doing */
    log(_("Compressing data"));

//TODO:
//Encrypt = yes
//ArchiveType = .tar.bz2
//ExcludeFiles = foo,bar*,b*z
    const char* opt = getenv("Upload_URL");
    if (!opt)
        opt = get_map_string_item_or_empty(settings, "URL");
    char *url = opt[0] != '\0' ? xstrdup(opt) : ask_url(_("Please enter a URL (scp, ftp, etc.) where the problem data is to be exported:"));

    /* Create a child gzip which will compress the data */
    /* SELinux guys are not happy with /tmp, using /var/run/abrt */
    /* Reverted back to /tmp for ABRT2 */
    /* Changed again to /var/tmp because of Fedora feature tmp-on-tmpfs */
    tempfile = concat_path_basename(LARGE_DATA_TMP_DIR, dump_dir_name);
    tempfile = append_to_malloced_string(tempfile, ".tar.gz");

    int pipe_from_parent_to_child[2];
    xpipe(pipe_from_parent_to_child);
    child = vfork();
    if (child == 0)
    {
        /* child */
        close(pipe_from_parent_to_child[1]);
        xmove_fd(pipe_from_parent_to_child[0], 0);
        xmove_fd(xopen3(tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600), 1);
        execlp("gzip", "gzip", NULL);
        perror_msg_and_die("Can't execute '%s'", "gzip");
    }
    close(pipe_from_parent_to_child[0]);

    /* If child died (say, in xopen), then parent might get SIGPIPE.
     * We want to properly unlock dd, therefore we must not die on SIGPIPE:
     */
    signal(SIGPIPE, SIG_IGN);

    /* Create tar writer object */
    if (tar_fdopen(&tar, pipe_from_parent_to_child[1], tempfile,
                /*fileops:(standard)*/ NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0)
    {
        errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR;
        goto ret;
    }

    /* Write data to the tarball */
    {
        string_vector_ptr_t exclude_from_report = get_global_always_excluded_elements();
        dd_init_next_file(dd);
        char *short_name, *full_name;
        while (dd_get_next_file(dd, &short_name, &full_name))
        {
            if (exclude_from_report && is_in_string_list(short_name, (const_string_vector_const_ptr_t)exclude_from_report))
                goto next;

            // dd_get_next_file guarantees that it's a REG:
            //struct stat stbuf;
            //if (stat(full_name, &stbuf) != 0)
            // || !S_ISREG(stbuf.st_mode)
            //) {
            //     goto next;
            //}
            if (tar_append_file(tar, full_name, short_name) != 0)
            {
                errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR;
                free(short_name);
                free(full_name);
                goto ret;
            }
 next:
            free(short_name);
            free(full_name);
        }
    }
    dd_close(dd);
    dd = NULL;

    /* Close tar writer... */
    if (tar_append_eof(tar) != 0 || tar_close(tar) != 0)
    {
        errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR;
        goto ret;
    }
    tar = NULL;
    /* ...and check that gzip child finished successfully */
    int status;
    safe_waitpid(child, &status, 0);
    child = -1;
    if (status != 0)
    {
        /* We assume the error was out-of-disk-space or out-of-quota */
        errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR;
        goto ret;
    }

    /* Upload the tarball */
    /* Upload from /tmp to /tmp + deletion -> BAD, exclude this possibility */
    if (url && url[0] && strcmp(url, "file://"LARGE_DATA_TMP_DIR"/") != 0)
    {
        post_state_t *state = new_post_state(POST_WANT_ERROR_MSG);
        state->username = getenv("Upload_Username");
        char *password_inp = NULL;
        if (state->username != NULL && state->username[0] != '\0')
        {
            /* Load Password only if Username is configured, it doesn't make */
            /* much sense to load Password without Username. */
            state->password = getenv("Upload_Password");
            if (state->password == NULL)
            {
                /* Be permissive and nice, ask only once and don't check */
                /* the result. User can dismiss this prompt but the upload */
                /* may work somehow??? */
                char *msg = xasprintf(_("Please enter password for uploading:"), state->username);
                state->password = password_inp = ask_password(msg);
                free(msg);
            }
        }

        char *remote_name = upload_file_ext(state, url, tempfile, UPLOAD_FILE_HANDLE_ACCESS_DENIALS);

        result = (remote_name == NULL); /* error if NULL */
        free(remote_name);
        free(password_inp);
        free_post_state(state);
        /* cleanup code will delete tempfile */
    }
    else
    {
        result = 0; /* success */
        log(_("Archive is created: '%s'"), tempfile);
        free(tempfile);
        tempfile = NULL;
    }

 ret:
    free(url);
    dd_close(dd);
    if (tar)
        tar_close(tar);
    /* close(pipe_from_parent_to_child[1]); - tar_close() does it itself */
    if (child > 0)
        safe_waitpid(child, NULL, 0);
    if (tempfile)
    {
        unlink(tempfile);
        free(tempfile);
    }
    if (errmsg)
        error_msg_and_die("%s", errmsg);

    return result;
}
void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle)
{
	file_header_t *file_header = archive_handle->file_header;

#if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */
	char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE];
	if (!sctx)
		sctx = archive_handle->tar__sctx[PAX_GLOBAL];
	if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
		setfscreatecon(sctx);
		free(archive_handle->tar__sctx[PAX_NEXT_FILE]);
		archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL;
	}
#endif

	if ((file_header->mode & S_IFMT) == S_IFREG) {
		pid_t pid;
		int p[2], status;
		char *tar_env[TAR_MAX];

		memset(tar_env, 0, sizeof(tar_env));

		xpipe(p);
		pid = BB_MMU ? xfork() : xvfork();
		if (pid == 0) {
			/* Child */
			/* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */
			oct2env(tar_env, TAR_MODE, file_header->mode);
			str2env(tar_env, TAR_FILENAME, file_header->name);
			str2env(tar_env, TAR_REALNAME, file_header->name);
#if ENABLE_FEATURE_TAR_UNAME_GNAME
			str2env(tar_env, TAR_UNAME, file_header->tar__uname);
			str2env(tar_env, TAR_GNAME, file_header->tar__gname);
#endif
			dec2env(tar_env, TAR_SIZE, file_header->size);
			dec2env(tar_env, TAR_UID, file_header->uid);
			dec2env(tar_env, TAR_GID, file_header->gid);
			close(p[1]);
			xdup2(p[0], STDIN_FILENO);
			signal(SIGPIPE, SIG_DFL);
			execl(archive_handle->tar__to_command_shell,
				archive_handle->tar__to_command_shell,
				"-c",
				archive_handle->tar__to_command,
				NULL);
			bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell);
		}
		close(p[0]);
		/* Our caller is expected to do signal(SIGPIPE, SIG_IGN)
		 * so that we don't die if child don't read all the input: */
		bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size);
		close(p[1]);

		if (safe_waitpid(pid, &status, 0) == -1)
			bb_perror_msg_and_die("waitpid");
		if (WIFEXITED(status) && WEXITSTATUS(status))
			bb_error_msg_and_die("'%s' returned status %d",
				archive_handle->tar__to_command, WEXITSTATUS(status));
		if (WIFSIGNALED(status))
			bb_error_msg_and_die("'%s' terminated on signal %d",
				archive_handle->tar__to_command, WTERMSIG(status));

		if (!BB_MMU) {
			int i;
			for (i = 0; i < TAR_MAX; i++) {
				if (tar_env[i])
					bb_unsetenv_and_free(tar_env[i]);
			}
		}
	}

#if 0 /* ENABLE_FEATURE_TAR_SELINUX */
	if (sctx)
		/* reset the context after creating an entry */
		setfscreatecon(NULL);
#endif
}
Exemplo n.º 18
0
char *run_unstrip_n(const char *dump_dir_name, unsigned timeout_sec)
{
    int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | 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, /*env_vec:*/ NULL, /*dir:*/ NULL, /*uid(unused):*/ 0);
    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.
     */
    ndelay_on(pipeout[0]);
    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_free(buf_out);
            buf_out = NULL;
            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)
        {
            /* I did see EAGAIN happening here */
            if (r < 0 && errno == EAGAIN)
                goto next;
            break;
        }
        buff[r] = '\0';
        strbuf_append_str(buf_out, buff);
 next:
        t = time(NULL);
    }
    close(pipeout[0]);

    /* Prevent having zombie child process */
    int status;
    safe_waitpid(child, &status, 0);

    if (status != 0 || buf_out == NULL)
    {
        /* unstrip didnt exit with exit code 0, or we timed out */
        strbuf_free(buf_out);
        return NULL;
    }

    return strbuf_free_nobuf(buf_out);
}
Exemplo n.º 19
0
/* gcc 4.2.1 inlines it, making code bigger */
static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
	int dereferenceFlag, const llist_t *include,
	const llist_t *exclude, int gzip)
{
	int errorFlag = FALSE;
	struct TarBallInfo tbInfo;

	tbInfo.hlInfoHead = NULL;
	tbInfo.tarFd = tar_fd;
	tbInfo.verboseFlag = verboseFlag;

	/* Store the stat info for the tarball's file, so
	 * can avoid including the tarball into itself....  */
	if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0)
		bb_perror_msg_and_die("cannot stat tar file");

#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
	if (gzip)
		vfork_compressor(tbInfo.tarFd, gzip);
#endif

	tbInfo.excludeList = exclude;

	/* Read the directory/files and iterate over them one at a time */
	while (include) {
		if (!recursive_action(include->data, ACTION_RECURSE |
				(dereferenceFlag ? ACTION_FOLLOWLINKS : 0),
				writeFileToTarball, writeFileToTarball, &tbInfo, 0))
		{
			errorFlag = TRUE;
		}
		include = include->link;
	}
	/* Write two empty blocks to the end of the archive */
	memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
	xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);

	/* To be pedantically correct, we would check if the tarball
	 * is smaller than 20 tar blocks, and pad it if it was smaller,
	 * but that isn't necessary for GNU tar interoperability, and
	 * so is considered a waste of space */

	/* Close so the child process (if any) will exit */
	close(tbInfo.tarFd);

	/* Hang up the tools, close up shop, head home */
	if (ENABLE_FEATURE_CLEAN_UP)
		freeHardLinkInfo(&tbInfo.hlInfoHead);

	if (errorFlag)
		bb_error_msg("error exit delayed from previous errors");

#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
	if (gzip) {
		int status;
		if (safe_waitpid(-1, &status, 0) == -1)
			bb_perror_msg("waitpid");
		else if (!WIFEXITED(status) || WEXITSTATUS(status))
			/* gzip was killed or has exited with nonzero! */
			errorFlag = TRUE;
	}
#endif
	return errorFlag;
}
Exemplo n.º 20
0
int report_problem_in_dir(const char *dirname, int flags)
{
    /* Prepare it before fork, to avoid thread-unsafe setenv there */
    char *prgname = (char*) g_get_prgname();
    if (prgname)
        prgname = xasprintf("LIBREPORT_PRGNAME=%s", prgname);

    fflush(NULL);

    pid_t pid = fork();
    if (pid < 0) /* error */
    {
        perror_msg("fork");
        return -1;
    }

    if (pid == 0) /* child */
    {
        const char *event_name;
        const char *path, *path1, *path2;
        char *args[7], **pp;

        /* Graphical tool */
        event_name = "report-gui";
        path1 = BIN_DIR"/report-gtk";
        path2 = "report-gtk";
        pp = args;
        *pp++ = (char *)"report-gtk";
        if (flags & LIBREPORT_DEL_DIR)
            *pp++ = (char *)"--delete";
        *pp++ = (char *)"--";
        *pp++ = (char *)dirname;
        *pp = NULL;

        if (prgname)
            putenv(prgname);

        if (flags & LIBREPORT_RUN_NEWT)
        {
            /* we want to run newt first */
            event_name = "report-cli";
            path1 = BIN_DIR"/report-newt";
            path2 = "report-newt";
            pp = args;
            *pp++ = (char *)"report-newt";
            if (flags & LIBREPORT_DEL_DIR)
                *pp++ = (char *)"--delete";
            *pp++ = (char *)"--";
            *pp++ = (char *)dirname;
            *pp = NULL;
        }
        else if (!getenv("DISPLAY") || (flags & LIBREPORT_RUN_CLI))
        {
            /* GUI won't work, use command line tool instead */
            event_name = "report-cli";
            path1 = BIN_DIR"/report-cli";
            path2 = "report-cli";
            pp = args;
            *pp++ = (char *)"report-cli";
            if (flags & LIBREPORT_DEL_DIR)
                *pp++ = (char *)"--delete";
            *pp++ = (char *)"-e";
            *pp++ = (char *)"report-cli";
            *pp++ = (char *)"--";
            *pp++ = (char *)dirname;
            *pp = NULL;
        }

        /* Some callers set SIGCHLD to SIG_IGN.
         * However, reporting spawns child processes.
         * Suppressing child death notification terribly confuses some of them.
         * Just in case, undo it.
         * Note that we do it in the child, so the parent is never affected.
         */
        signal(SIGCHLD, SIG_DFL);

        if (!(flags & (LIBREPORT_WAIT | LIBREPORT_GETPID)))
        {
            /* Caller doesn't want to wait for completion (!LIBREPORT_WAIT),
             * and doesn't want to have pid returned (!LIBREPORT_GETPID).
             * Create a grandchild, and then exit.
             * This reparents grandchild to init, and makes waitpid
             * in parent detect our exit and return almost immediately.
             */
            pid_t pid = fork();
            if (pid < 0) /* error */
                perror_msg_and_die("fork");
            if (pid != 0) /* not grandchild */
            {
                /* And now we exit: */
                _exit(0);
            }
            /* There's an alternative approach to achieve this,
             * instead of using --delete.
             * We can create yet another intermediate process which
             * waits for reporting child to finish, and then
             * removes temporary dump dir.
             * Pros: deletion becomes more robust.
             * Even if child crashes, dir will be deleted.
             * Cons: having another process would use some resources,
             * and we'll need to at least close all open file descriptors,
             * and reopen stdio to /dev/null. We also might keep
             * a lot of libraries loaded:
             * who knows what parent process links against.
             * (can be worked around by exec'ing a "wait & delete" helper)
             */
        }

        struct run_event_state *run_state = new_run_event_state();
        int r = run_event_on_dir_name(run_state, dirname, event_name);
        int no_such_event = (r == 0 && run_state->children_count == 0);
        free_run_event_state(run_state);
        if (!no_such_event)
        {
            if (flags & LIBREPORT_DEL_DIR)
            {
                struct dump_dir *dd = dd_opendir(dirname, 0);
                if (dd)
                    dd_delete(dd);
            }
            _exit(r);
        }
        /* No "report-cli/gui" event found, do it old-style */

        path = path1;
        VERB1 log("Executing: %s", path);
        execv(path, args);
        /* Did not find the desired executable in the installation directory.
         * Trying to find it in PATH.
         */
        path = path2;
        execvp(path, args);
        perror_msg_and_die("Can't execute %s", path);
    }

    /* parent */
    free(prgname);

    if (!(flags & LIBREPORT_WAIT) && (flags & LIBREPORT_GETPID))
        return pid;

    /* we are here either if LIBREPORT_WAIT (caller wants exitcode)
     * or !LIBREPORT_GETPID (caller doesn't want to have a child).
     * In both cases, we need to wait for child:
     */
    int status;
    pid = safe_waitpid(pid, &status, 0);
    if (pid <= 0)
    {
        perror_msg("waitpid");
        return -1;
    }

    if (WIFEXITED(status))
    {
        VERB2 log("reporting finished with exitcode %d", WEXITSTATUS(status));
        return WEXITSTATUS(status);
    }
    /* child died from a signal: WIFSIGNALED(status) should be true */
    VERB2 log("reporting killed by signal %d", WTERMSIG(status));
    return WTERMSIG(status) + 128;
}