cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

run-command.c (4482B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <unistd.h>
      3#include <sys/types.h>
      4#include <sys/stat.h>
      5#include <fcntl.h>
      6#include <string.h>
      7#include <linux/string.h>
      8#include <errno.h>
      9#include <sys/wait.h>
     10#include "subcmd-util.h"
     11#include "run-command.h"
     12#include "exec-cmd.h"
     13
     14#define STRERR_BUFSIZE 128
     15
     16static inline void close_pair(int fd[2])
     17{
     18	close(fd[0]);
     19	close(fd[1]);
     20}
     21
     22static inline void dup_devnull(int to)
     23{
     24	int fd = open("/dev/null", O_RDWR);
     25	dup2(fd, to);
     26	close(fd);
     27}
     28
     29int start_command(struct child_process *cmd)
     30{
     31	int need_in, need_out, need_err;
     32	int fdin[2], fdout[2], fderr[2];
     33	char sbuf[STRERR_BUFSIZE];
     34
     35	/*
     36	 * In case of errors we must keep the promise to close FDs
     37	 * that have been passed in via ->in and ->out.
     38	 */
     39
     40	need_in = !cmd->no_stdin && cmd->in < 0;
     41	if (need_in) {
     42		if (pipe(fdin) < 0) {
     43			if (cmd->out > 0)
     44				close(cmd->out);
     45			return -ERR_RUN_COMMAND_PIPE;
     46		}
     47		cmd->in = fdin[1];
     48	}
     49
     50	need_out = !cmd->no_stdout
     51		&& !cmd->stdout_to_stderr
     52		&& cmd->out < 0;
     53	if (need_out) {
     54		if (pipe(fdout) < 0) {
     55			if (need_in)
     56				close_pair(fdin);
     57			else if (cmd->in)
     58				close(cmd->in);
     59			return -ERR_RUN_COMMAND_PIPE;
     60		}
     61		cmd->out = fdout[0];
     62	}
     63
     64	need_err = !cmd->no_stderr && cmd->err < 0;
     65	if (need_err) {
     66		if (pipe(fderr) < 0) {
     67			if (need_in)
     68				close_pair(fdin);
     69			else if (cmd->in)
     70				close(cmd->in);
     71			if (need_out)
     72				close_pair(fdout);
     73			else if (cmd->out)
     74				close(cmd->out);
     75			return -ERR_RUN_COMMAND_PIPE;
     76		}
     77		cmd->err = fderr[0];
     78	}
     79
     80	fflush(NULL);
     81	cmd->pid = fork();
     82	if (!cmd->pid) {
     83		if (cmd->no_stdin)
     84			dup_devnull(0);
     85		else if (need_in) {
     86			dup2(fdin[0], 0);
     87			close_pair(fdin);
     88		} else if (cmd->in) {
     89			dup2(cmd->in, 0);
     90			close(cmd->in);
     91		}
     92
     93		if (cmd->no_stderr)
     94			dup_devnull(2);
     95		else if (need_err) {
     96			dup2(fderr[1], 2);
     97			close_pair(fderr);
     98		}
     99
    100		if (cmd->no_stdout)
    101			dup_devnull(1);
    102		else if (cmd->stdout_to_stderr)
    103			dup2(2, 1);
    104		else if (need_out) {
    105			dup2(fdout[1], 1);
    106			close_pair(fdout);
    107		} else if (cmd->out > 1) {
    108			dup2(cmd->out, 1);
    109			close(cmd->out);
    110		}
    111
    112		if (cmd->dir && chdir(cmd->dir))
    113			die("exec %s: cd to %s failed (%s)", cmd->argv[0],
    114			    cmd->dir, str_error_r(errno, sbuf, sizeof(sbuf)));
    115		if (cmd->env) {
    116			for (; *cmd->env; cmd->env++) {
    117				if (strchr(*cmd->env, '='))
    118					putenv((char*)*cmd->env);
    119				else
    120					unsetenv(*cmd->env);
    121			}
    122		}
    123		if (cmd->preexec_cb)
    124			cmd->preexec_cb();
    125		if (cmd->exec_cmd) {
    126			execv_cmd(cmd->argv);
    127		} else {
    128			execvp(cmd->argv[0], (char *const*) cmd->argv);
    129		}
    130		exit(127);
    131	}
    132
    133	if (cmd->pid < 0) {
    134		int err = errno;
    135		if (need_in)
    136			close_pair(fdin);
    137		else if (cmd->in)
    138			close(cmd->in);
    139		if (need_out)
    140			close_pair(fdout);
    141		else if (cmd->out)
    142			close(cmd->out);
    143		if (need_err)
    144			close_pair(fderr);
    145		return err == ENOENT ?
    146			-ERR_RUN_COMMAND_EXEC :
    147			-ERR_RUN_COMMAND_FORK;
    148	}
    149
    150	if (need_in)
    151		close(fdin[0]);
    152	else if (cmd->in)
    153		close(cmd->in);
    154
    155	if (need_out)
    156		close(fdout[1]);
    157	else if (cmd->out)
    158		close(cmd->out);
    159
    160	if (need_err)
    161		close(fderr[1]);
    162
    163	return 0;
    164}
    165
    166static int wait_or_whine(pid_t pid)
    167{
    168	char sbuf[STRERR_BUFSIZE];
    169
    170	for (;;) {
    171		int status, code;
    172		pid_t waiting = waitpid(pid, &status, 0);
    173
    174		if (waiting < 0) {
    175			if (errno == EINTR)
    176				continue;
    177			fprintf(stderr, " Error: waitpid failed (%s)",
    178				str_error_r(errno, sbuf, sizeof(sbuf)));
    179			return -ERR_RUN_COMMAND_WAITPID;
    180		}
    181		if (waiting != pid)
    182			return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
    183		if (WIFSIGNALED(status))
    184			return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
    185
    186		if (!WIFEXITED(status))
    187			return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
    188		code = WEXITSTATUS(status);
    189		switch (code) {
    190		case 127:
    191			return -ERR_RUN_COMMAND_EXEC;
    192		case 0:
    193			return 0;
    194		default:
    195			return -code;
    196		}
    197	}
    198}
    199
    200int finish_command(struct child_process *cmd)
    201{
    202	return wait_or_whine(cmd->pid);
    203}
    204
    205int run_command(struct child_process *cmd)
    206{
    207	int code = start_command(cmd);
    208	if (code)
    209		return code;
    210	return finish_command(cmd);
    211}
    212
    213static void prepare_run_command_v_opt(struct child_process *cmd,
    214				      const char **argv,
    215				      int opt)
    216{
    217	memset(cmd, 0, sizeof(*cmd));
    218	cmd->argv = argv;
    219	cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
    220	cmd->exec_cmd = opt & RUN_EXEC_CMD ? 1 : 0;
    221	cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
    222}
    223
    224int run_command_v_opt(const char **argv, int opt)
    225{
    226	struct child_process cmd;
    227	prepare_run_command_v_opt(&cmd, argv, opt);
    228	return run_command(&cmd);
    229}