#include "execute_cmd.h"
#include "redir.h"
#include "print_cmd.h"
#include "command.h"
#include "lex.h"
#include "builtins/builtins.h"
#include "jobs.h"
#include "safe_malloc.h"
#include "error.h"
#include "vars.h"
#include <getopt.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#define ES_NOT_FOUND 127
#define ES_NOT_EXEC 126
struct executable_cmd_struct;
struct executable_cmd_operations_struct {
unsigned subshell : 1;
int (*execute)(struct executable_cmd_struct *, char *[]);
void (*destroy)(struct executable_cmd_struct *);
};
struct executable_cmd_struct {
const struct executable_cmd_operations_struct *operations;
union {
const struct builtin_struct *builtin;
char *path;
};
};
struct exec_env_struct {
unsigned subshell : 1;
int saved_line_number;
unsigned builtin : 1;
struct undo_redirect_stack_struct undo_redirect_stack;
};
static inline int file_exists(const char *path)
{
struct stat statbuf;
return stat(path, &statbuf) == 0;
}
void redirect_list_list_init(struct redirect_list_list_struct *redirect_list_list)
{
redirect_list_list->first = NULL;
redirect_list_list->last = NULL;
}
void redirect_list_list_append(struct redirect_list_list_struct *redirect_list_list, struct redirect_list_struct *redirect_list)
{
redirect_list->next = NULL;
if (redirect_list_list->last)
redirect_list_list->last->next = redirect_list;
else
redirect_list_list->first = redirect_list;
redirect_list_list->last = redirect_list;
}
static char **make_argv(const char *command_name, const struct argument_struct *first_argument)
{
int argc;
const struct argument_struct *argument;
char **argv, **arg;
if (command_name) {
argc = 1;
for (argument = first_argument; argument; argument = argument->next)
argc++;
}
else
argc = 0;
argv = safe_malloc((argc + 1) * sizeof (char *));
arg = argv;
if (command_name) {
*arg++ = safe_strdup(command_name);
for (argument = first_argument; argument; argument = argument->next)
*arg++ = remove_quotes(argument->word);
}
*arg = NULL;
return argv;
}
static int count_argv(char *const argv[])
{
int argc;
argc = 0;
while (*argv)
argc++, argv++;
return argc;
}
static void free_argv(char **argv)
{
char **arg;
for (arg = argv; *arg; arg++)
free(*arg);
free(argv);
}
static const struct builtin_struct *lookup_builtin(const char *command_name)
{
const struct builtin_struct **builtin;
for (builtin = &builtin_table[0]; *builtin; builtin++)
if (!strcmp(command_name, (*builtin)->name))
return *builtin;
return NULL;
}
static int execute_builtin(const struct builtin_struct *builtin, char **argv)
{
int status;
opterr = 0;
optind = 0;
current_builtin = builtin;
status = builtin->func(count_argv(argv), argv);
current_builtin = NULL;
return status;
}
static char *search_program(const char *command_name)
{
const char *path_prefixes;
char *path;
const char *p1;
char *p2;
if (!(path_prefixes = getenv("PATH")))
return NULL;
path = safe_malloc(strlen(path_prefixes) + strlen(command_name) + 2);
if (*path_prefixes) {
p1 = path_prefixes;
while (1) {
p2 = path;
while (*p1 && *p1 != ':')
*p2++ = *p1++;
if (p2 != path)
*p2++ = '/';
strcpy(p2, command_name);
if (file_exists(path))
return path;
if (!*p1)
break;
if (*p1 == ':')
p1++;
}
}
free(path);
return NULL;
}
static int execute_program(const char *path, char *const argv[])
{
int errnum;
execve(path, argv, environ);
errnum = errno;
file_error(errnum, path);
return errnum == ENOENT ? ES_NOT_FOUND : ES_NOT_EXEC;
}
static int builtin_execute(struct executable_cmd_struct *executable_cmd, char *argv[])
{
return execute_builtin(executable_cmd->builtin, argv);
}
static void builtin_destroy(struct executable_cmd_struct *executable_cmd) {}
static const struct executable_cmd_operations_struct builtin_operations = {
.subshell = 0,
.execute = builtin_execute,
.destroy = builtin_destroy
};
static void init_builtin_executable_cmd(struct executable_cmd_struct *executable_cmd, const struct builtin_struct *builtin)
{
executable_cmd->operations = &builtin_operations;
executable_cmd->builtin = builtin;
}
static int program_execute(struct executable_cmd_struct *executable_cmd, char *argv[])
{
return execute_program(executable_cmd->path, argv);
}
static void program_destroy(struct executable_cmd_struct *executable_cmd)
{
free(executable_cmd->path);
}
static const struct executable_cmd_operations_struct program_operations = {
.subshell = 1,
.execute = program_execute,
.destroy = program_destroy
};
static void init_program_executable_cmd(struct executable_cmd_struct *executable_cmd, char *path)
{
executable_cmd->operations = &program_operations;
executable_cmd->path = path;
}
static int not_found_execute(struct executable_cmd_struct *executable_cmd, char *argv[])
{
generic_error("%s: command not found", argv[0]);
return ES_NOT_FOUND;
}
static void not_found_destroy(struct executable_cmd_struct *executable_cmd) {}
static const struct executable_cmd_operations_struct not_found_operations = {
.subshell = 1,
.execute = not_found_execute,
.destroy = not_found_destroy
};
static void init_not_found_executable_cmd(struct executable_cmd_struct *executable_cmd)
{
executable_cmd->operations = ¬_found_operations;
}
static int no_command_execute(struct executable_cmd_struct *executable_cmd, char *argv[])
{
return EXIT_SUCCESS;
}
static void no_command_destroy(struct executable_cmd_struct *executable_cmd) {}
static const struct executable_cmd_operations_struct no_command_operations = {
.subshell = 0,
.execute = no_command_execute,
.destroy = no_command_destroy
};
static void init_no_command_executable_cmd(struct executable_cmd_struct *executable_cmd)
{
executable_cmd->operations = &no_command_operations;
}
static int reassign_stdin()
{
int fd;
if ((fd = open("/dev/null", O_RDONLY)) == -1) {
sys_error(errno, "cannot open /dev/null");
return -1;
}
if (dup2(fd, 0) == -1) {
sys_error(errno, "cannot duplicate /dev/null");
close(fd);
return -1;
}
close(fd);
return 0;
}
static int reassign_fd(int orig_fd, int dest_fd)
{
if (dest_fd == orig_fd)
return 0;
if (dup2(dest_fd, orig_fd) == -1) {
sys_error(errno, "cannot duplicate fd");
close(dest_fd);
return -1;
}
close(dest_fd);
return 0;
}
static int do_redirections(struct redirect_list_list_struct *redirect_list_list, struct undo_redirect_stack_struct *undo_redirect_stack)
{
const struct redirect_list_struct *redirect_list;
const struct redirect_struct *redirect;
for (redirect_list = redirect_list_list->first; redirect_list; redirect_list = redirect_list->next)
for (redirect = redirect_list->first; redirect; redirect = redirect->next)
if (do_redirection(redirect, undo_redirect_stack) == -1)
goto error_undo_redirections;
return 0;
error_undo_redirections:
if (undo_redirect_stack)
undo_redirections(undo_redirect_stack);
return -1;
}
static int setup_shell_execution_environment(struct exec_env_desc_struct *exec_env_desc, struct exec_env_struct *exec_env)
{
exec_env->subshell = 0;
exec_env->saved_line_number = line_number;
line_number = exec_env_desc->line_number;
free(exec_env_desc->command);
exec_env->builtin = exec_env_desc->builtin;
init_undo_redirect_stack(&exec_env->undo_redirect_stack);
if (do_redirections(&exec_env_desc->redirect_list_list, &exec_env->undo_redirect_stack) == -1) {
last_command_status = EXIT_FAILURE;
goto error;
}
return 1;
error:
if (exec_env->builtin)
exit_pseudo_job(last_command_status);
line_number = exec_env->saved_line_number;
return 0;
}
static int setup_subshell_execution_environment(struct exec_env_desc_struct *exec_env_desc, struct exec_env_struct *exec_env)
{
pid_t pid;
int must_reassign_stdin;
if ((pid = make_child(exec_env_desc->running_pipeline, exec_env_desc->command)) == -1) {
last_command_status = EXIT_FAILURE;
return 0;
}
if (pid) {
last_command_status = EXIT_SUCCESS;
return 0;
}
exec_env->subshell = 1;
must_reassign_stdin = !interactive && !exec_env_desc->running_pipeline->foreground && exec_env_desc->pipe_in_fd == -1;
interactive = 0;
line_number = exec_env_desc->line_number;
if (must_reassign_stdin)
if (reassign_stdin() == -1) {
last_command_status = EXIT_FAILURE;
goto error_exit;
}
if (exec_env_desc->pipe_in_fd != -1)
if (reassign_fd(0, exec_env_desc->pipe_in_fd) == -1) {
last_command_status = EXIT_FAILURE;
goto error_exit;
}
if (exec_env_desc->fd_to_close != -1)
close(exec_env_desc->fd_to_close);
if (exec_env_desc->pipe_out_fd != -1)
if (reassign_fd(1, exec_env_desc->pipe_out_fd) == -1) {
last_command_status = EXIT_FAILURE;
goto error_exit;
}
if (do_redirections(&exec_env_desc->redirect_list_list, NULL) == -1) {
last_command_status = EXIT_FAILURE;
goto error_exit;
}
return 1;
error_exit:
exit(last_command_status);
}
static int setup_execution_environment(struct exec_env_desc_struct *exec_env_desc, struct exec_env_struct *exec_env)
{
struct running_pipeline_struct running_pipeline;
int must_start_job;
if (exec_env_desc->subshell) {
if ((must_start_job = !exec_env_desc->running_pipeline)) {
init_running_pipeline(&running_pipeline, 1);
exec_env_desc->running_pipeline = &running_pipeline;
exec_env_desc->pipe_in_fd = -1;
exec_env_desc->fd_to_close = -1;
exec_env_desc->pipe_out_fd = -1;
}
if (!setup_subshell_execution_environment(exec_env_desc, exec_env)) {
if (must_start_job) {
if (!last_command_status)
last_command_status = start_job(&running_pipeline);
else
kill_running_pipeline(&running_pipeline);
}
return 0;
}
return 1;
}
else
return setup_shell_execution_environment(exec_env_desc, exec_env);
}
static void exit_shell_execution_environment(struct exec_env_struct *exec_env)
{
fflush(stdout);
fflush(stderr);
if (ferror(stdout))
clearerr(stdout);
undo_redirections(&exec_env->undo_redirect_stack);
if (!done && exec_env->builtin)
exit_pseudo_job(last_command_status);
line_number = exec_env->saved_line_number;
}
static void exit_subshell_execution_environment(struct exec_env_struct *exec_env)
{
exit(last_command_status);
}
static void exit_execution_environment(struct exec_env_struct *exec_env)
{
return exec_env->subshell ? exit_subshell_execution_environment(exec_env) : exit_shell_execution_environment(exec_env);
}
static void search_command(const char *command_name, struct executable_cmd_struct *executable_cmd)
{
const struct builtin_struct *builtin;
char *path;
if (command_name) {
if (!strchr(command_name, '/')) {
if ((builtin = lookup_builtin(command_name)))
init_builtin_executable_cmd(executable_cmd, builtin);
else if ((path = search_program(command_name)))
init_program_executable_cmd(executable_cmd, path);
else
init_not_found_executable_cmd(executable_cmd);
}
else
init_program_executable_cmd(executable_cmd, safe_strdup(command_name));
}
else
init_no_command_executable_cmd(executable_cmd);
}
static void do_execute_command(struct executable_cmd_struct *executable_cmd, const char *command_name, const struct argument_struct *first_argument)
{
char **argv;
argv = make_argv(command_name, first_argument);
last_command_status = executable_cmd->operations->execute(executable_cmd, argv);
free_argv(argv);
}
static void execute_simple_command(const struct simple_command_struct *simple_command, struct exec_env_desc_struct *exec_env_desc)
{
char *command_name;
struct executable_cmd_struct executable_cmd;
struct redirect_list_struct redirect_list;
struct exec_env_struct exec_env;
command_name = simple_command->command_name ? remove_quotes(simple_command->command_name) : NULL;
search_command(command_name, &executable_cmd);
if (executable_cmd.operations->subshell) {
if (!exec_env_desc->subshell && !exec_env_desc->no_fork) {
exec_env_desc->subshell = 1;
exec_env_desc->running_pipeline = NULL;
}
}
else
if (!exec_env_desc->subshell)
exec_env_desc->builtin = 1;
redirect_list.first = simple_command->first_redirect;
redirect_list_list_append(&exec_env_desc->redirect_list_list, &redirect_list);
if (setup_execution_environment(exec_env_desc, &exec_env)) {
do_execute_command(&executable_cmd, command_name, simple_command->first_argument);
executable_cmd.operations->destroy(&executable_cmd);
if (command_name)
free(command_name);
exit_execution_environment(&exec_env);
}
else {
executable_cmd.operations->destroy(&executable_cmd);
if (command_name)
free(command_name);
}
}
static void execute_compound_command(const struct compound_command_struct *compound_command, struct exec_env_desc_struct *exec_env_desc)
{
struct redirect_list_struct redirect_list;
switch (compound_command->type) {
case CC_SUBSHELL:
if (!exec_env_desc->subshell) {
exec_env_desc->subshell = 1;
exec_env_desc->running_pipeline = NULL;
}
redirect_list.first = compound_command->first_redirect;
redirect_list_list_append(&exec_env_desc->redirect_list_list, &redirect_list);
execute_list(compound_command->list, exec_env_desc);
break;
}
}
void execute_command(const struct command_struct *command, struct exec_env_desc_struct *exec_env_desc)
{
switch (command->type) {
case CT_SIMPLE:
execute_simple_command(&command->simple_command, exec_env_desc);
break;
case CT_COMPOUND:
execute_compound_command(&command->compound_command, exec_env_desc);
break;
}
}
static void do_execute_pipeline(const struct pipeline_struct *pipeline, int foreground)
{
struct running_pipeline_struct running_pipeline;
int prev_fd;
struct string_buffer_struct string_buffer;
const struct command_struct *command;
int pipe_fd[2];
struct exec_env_desc_struct exec_env_desc;
init_running_pipeline(&running_pipeline, foreground);
prev_fd = -1;
string_buffer_init(&string_buffer);
for (command = pipeline->first_command; command; command = command->next) {
if (command->next)
if (pipe(pipe_fd) == -1) {
sys_error(errno, "pipe error");
last_command_status = EXIT_FAILURE;
goto error_close_prev_fd;
}
print_command(command, &exec_env_desc.line_number, &string_buffer);
exec_env_desc.command = string_buffer_release(&string_buffer);
exec_env_desc.subshell = 1;
exec_env_desc.running_pipeline = &running_pipeline;
exec_env_desc.pipe_in_fd = prev_fd;
if (command->next) {
exec_env_desc.fd_to_close = pipe_fd[0];
exec_env_desc.pipe_out_fd = pipe_fd[1];
}
else {
exec_env_desc.fd_to_close = -1;
exec_env_desc.pipe_out_fd = -1;
}
redirect_list_list_init(&exec_env_desc.redirect_list_list);
execute_command(command, &exec_env_desc);
if (last_command_status)
goto error_close_pipe_fd;
if (prev_fd != -1)
close(prev_fd);
if (command->next) {
prev_fd = pipe_fd[0];
close(pipe_fd[1]);
}
}
if (prev_fd != -1)
close(prev_fd);
last_command_status = start_job(&running_pipeline);
return;
error_close_pipe_fd:
close(pipe_fd[0]);
close(pipe_fd[1]);
error_close_prev_fd:
if (prev_fd != -1)
close(prev_fd);
kill_running_pipeline(&running_pipeline);
}
void execute_pipeline(const struct pipeline_struct *pipeline, int asynchronous, struct exec_env_desc_struct *exec_env_desc)
{
struct exec_env_struct exec_env;
if (asynchronous)
do_execute_pipeline(pipeline, 0);
else {
if (!pipeline->first_command->next) {
execute_command(pipeline->first_command, exec_env_desc);
return;
}
if (!setup_execution_environment(exec_env_desc, &exec_env))
return;
do_execute_pipeline(pipeline, 1);
exit_execution_environment(&exec_env);
}
}
static void do_execute_and_or_list(const struct and_or_list_struct *and_or_list)
{
int execute_next_command;
struct string_buffer_struct string_buffer;
const struct pipeline_struct *pipeline;
struct exec_env_desc_struct exec_env_desc;
execute_next_command = 1;
string_buffer_init(&string_buffer);
for (pipeline = and_or_list->first_pipeline; pipeline; pipeline = pipeline->next) {
if (execute_next_command) {
print_pipeline(pipeline, &exec_env_desc.line_number, &string_buffer);
exec_env_desc.command = string_buffer_release(&string_buffer);
exec_env_desc.subshell = 0;
exec_env_desc.no_fork = 0;
exec_env_desc.builtin = 0;
redirect_list_list_init(&exec_env_desc.redirect_list_list);
execute_pipeline(pipeline, 0, &exec_env_desc);
if (done)
return;
}
if (pipeline->next)
switch (pipeline->operator) {
case AO_AND:
execute_next_command = !last_command_status;
break;
case AO_OR:
execute_next_command = last_command_status;
break;
}
}
}
void execute_and_or_list(const struct and_or_list_struct *and_or_list, struct exec_env_desc_struct *exec_env_desc)
{
struct running_pipeline_struct running_pipeline;
struct exec_env_struct exec_env1, exec_env2;
struct string_buffer_struct string_buffer;
if (!and_or_list->first_pipeline->next) {
if (!and_or_list->asynchronous)
execute_pipeline(and_or_list->first_pipeline, 0, exec_env_desc);
else {
if (!setup_execution_environment(exec_env_desc, &exec_env1))
return;
execute_pipeline(and_or_list->first_pipeline, 1, NULL);
exit_execution_environment(&exec_env1);
}
return;
}
if (!setup_execution_environment(exec_env_desc, &exec_env1))
return;
if (!and_or_list->asynchronous)
do_execute_and_or_list(and_or_list);
else {
init_running_pipeline(&running_pipeline, 0);
string_buffer_init(&string_buffer);
print_and_or_list(and_or_list, &exec_env_desc->line_number, &string_buffer);
exec_env_desc->command = string_buffer_release(&string_buffer);
exec_env_desc->subshell = 1;
exec_env_desc->running_pipeline = &running_pipeline;
exec_env_desc->pipe_in_fd = -1;
exec_env_desc->fd_to_close = -1;
exec_env_desc->pipe_out_fd = -1;
redirect_list_list_init(&exec_env_desc->redirect_list_list);
if (setup_execution_environment(exec_env_desc, &exec_env2)) {
do_execute_and_or_list(and_or_list);
exit_execution_environment(&exec_env2);
}
else {
if (!last_command_status)
last_command_status = start_job(&running_pipeline);
else
kill_running_pipeline(&running_pipeline);
}
}
exit_execution_environment(&exec_env1);
}
static void do_execute_list(const struct list_struct *list)
{
struct string_buffer_struct string_buffer;
const struct and_or_list_struct *and_or_list;
struct exec_env_desc_struct exec_env_desc;
string_buffer_init(&string_buffer);
for (and_or_list = list->first_and_or_list; !done && and_or_list; and_or_list = and_or_list->next) {
print_and_or_list(and_or_list, &exec_env_desc.line_number, &string_buffer);
exec_env_desc.command = string_buffer_release(&string_buffer);
exec_env_desc.subshell = 0;
exec_env_desc.no_fork = 0;
exec_env_desc.builtin = 0;
redirect_list_list_init(&exec_env_desc.redirect_list_list);
execute_and_or_list(and_or_list, &exec_env_desc);
}
}
void execute_list(const struct list_struct *list, struct exec_env_desc_struct *exec_env_desc)
{
struct exec_env_struct exec_env;
if (!list->first_and_or_list->next) {
execute_and_or_list(list->first_and_or_list, exec_env_desc);
return;
}
if (!setup_execution_environment(exec_env_desc, &exec_env))
return;
do_execute_list(list);
exit_execution_environment(&exec_env);
}