Files
42sh/bonus/exec.c
2025-05-09 16:51:51 +02:00

237 lines
6.5 KiB
C

/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include "args.h"
#include "ast.h"
#include "builtins.h"
#include "common.h"
#include "debug.h"
#include "env.h"
#include "exec.h"
#include "job.h"
#include "path.h"
#include "repl.h"
#include "u_mem.h"
#include "u_str.h"
const builtins_funcs_t BUILTINS[] = {
{ "builtins", &builtins_builtins },
{ "cd", &builtins_cd },
{ "chdir", &builtins_cd },
{ "env", &builtins_env },
{ "printenv", &builtins_env },
{ "setenv", &builtins_setenv },
{ "unsetenv", &builtins_unsetenv },
{ ":", &builtins_funny_double_dot },
{ "repeat", &builtins_repeat },
{ "exit", &builtins_exit },
{ "history", &builtins_history},
{ "alias", &builtins_alias},
{ "yes", &builtins_yes },
{ "foreach", &builtins_foreach },
{ "while", &builtins_while },
{ "set", &builtins_set },
{ "unset", &builtins_unset },
{ "where", &builtins_where },
{ "which", &builtins_which },
{ "expr", &builtins_expr },
{ "if", &builtins_if },
{ "fg", &builtins_fg },
{ "break", &builtins_break }
};
const size_t BUILTINS_SZ = sizeof BUILTINS / sizeof *BUILTINS;
__attribute__((nonnull))
bool ensure_args_capacity(args_t *args)
{
char **new_ptr;
if (args->sz + 1 < args->cap)
return true;
new_ptr = (char **)u_realloc((void *)args->args, sizeof *args->args *
args->sz, sizeof *args->args * args->cap << 1);
if (!new_ptr)
return false;
args->cap <<= 1;
args->args = new_ptr;
return true;
}
static
char **parse_args(ef_t *ef, ast_t *node)
{
args_t args = { .args = (char **)malloc(sizeof *args.args
* DEFAULT_ARGS_CAP), .sz = 0, .cap = DEFAULT_ARGS_CAP };
if (!args.args)
return nullptr;
for (size_t i = 0; i < node->vector.sz; i++) {
if (ef->skip_sz > 0 && i >= ef->skip_i && i < ef->skip_i + ef->skip_sz)
continue;
if (!process_args(node, &args, &i, ef))
return free((void *)args.args), NULL;
}
if (!ensure_args_capacity(&args))
return nullptr;
args.args[args.sz] = nullptr;
return args.args;
}
static
int command_error(char *cmd, char **args, int error)
{
struct stat st;
if (access(cmd, F_OK) == -1) {
write(STDERR_FILENO, args[0], u_strlen(args[0]));
WRITE_CONST(STDERR_FILENO, ": Command not found.\n");
return 84;
}
stat(cmd, &st);
if (S_ISDIR(st.st_mode) || access(cmd, X_OK)) {
write(STDERR_FILENO, args[0], u_strlen(args[0]));
WRITE_CONST(STDERR_FILENO, ": Permission denied.\n");
return 84;
}
if (error == ENOEXEC) {
write(STDERR_FILENO, args[0], u_strlen(args[0]));
WRITE_CONST(STDERR_FILENO, ": Exec format error."
" Binary file not executable.\n");
return 0;
}
return 84;
}
static
void set_fd(ef_t *ef)
{
U_DEBUG("In fd [%d] out fd [%d]\n", ef->in_fd, ef->out_fd);
if (ef->in_fd != STDIN_FILENO) {
dup2(ef->in_fd, STDIN_FILENO);
close(ef->in_fd);
}
if (ef->out_fd != STDOUT_FILENO) {
dup2(ef->out_fd, STDOUT_FILENO);
close(ef->out_fd);
}
}
static
int exec(char *full_bin_path, char **args, ef_t *ef)
{
execve(full_bin_path, args, ef->env->env);
return command_error(full_bin_path, args, errno);
}
static
int launch_bin(char *full_bin_path, char **args, ef_t *ef)
{
int status;
pid_t pid = fork();
int nohang;
if (pid == 0) {
restore_term_flags(ef->exec_ctx);
set_fd(ef);
init_child_job(ef->exec_ctx, pid);
status = exec(full_bin_path, args, ef);
exit(RETURN_FAILURE);
}
init_child_job(ef->exec_ctx, pid);
nohang = ef->flags & F_PIPE || ef->p_i != ef->p_sz - 1;
waitpid(pid, &status, nohang ? WUNTRACED : WNOHANG);
if (WIFSTOPPED(status)) {
ef->exec_ctx->jobs.jobs[ef->exec_ctx->jobs.sz - 1].running = true;
ef->exec_ctx->jobs.jobs[ef->exec_ctx->jobs.sz - 1].foreground = false;
printf("\n[%lu]+ Continued &\n", ef->exec_ctx->jobs.sz);
}
if (WIFEXITED(status))
ef->exec_ctx->history->last_exit_code =
ef->exec_ctx->history->last_exit_code ?: WEXITSTATUS(status);
tcsetpgrp(ef->exec_ctx->read_fd, ef->exec_ctx->jobs.jobs[0].pgid);
return status;
}
static
void status_handler(int status)
{
if (!WIFEXITED(status) && WIFSIGNALED(status) && WTERMSIG(status)) {
if (WTERMSIG(status) == SIGSEGV)
WRITE_CONST(STDERR_FILENO, "Segmentation fault");
if (WTERMSIG(status) == SIGTRAP)
WRITE_CONST(STDERR_FILENO, "Trace/BPT trap");
if (WTERMSIG(status) == SIGFPE)
WRITE_CONST(STDERR_FILENO, "Floating exception");
if (WCOREDUMP(status) && (WTERMSIG(status) == SIGSEGV ||
WTERMSIG(status) == SIGFPE || WTERMSIG(status) == SIGTRAP))
WRITE_CONST(STDERR_FILENO, " (core dumped)");
if (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGFPE ||
WTERMSIG(status) == SIGTRAP)
WRITE_CONST(STDERR_FILENO, "\n");
}
}
static
bool builtins_launcher(ef_t *ef, char **args)
{
int bin_l = u_strlen(args[0]);
U_DEBUG("In fd [%d] out fd [%d]\n", ef->in_fd, ef->out_fd);
for (size_t i = 0; i < BUILTINS_SZ; i++) {
if (u_strlen(BUILTINS[i].name) != bin_l)
continue;
if (u_strcmp(BUILTINS[i].name, args[0]) == 0) {
restore_term_flags(ef->exec_ctx);
ef->exec_ctx->history->last_exit_code =
BUILTINS[i].ptr(ef, args);
return true;
}
}
return false;
}
int exec_the_args(ef_t *ef, char **args)
{
char *full_bin_path;
int status;
if (builtins_launcher(ef, args))
return RETURN_SUCCESS;
full_bin_path = parse_full_bin_path(ef->env, args[0]);
if (full_bin_path == NULL)
return (free((void *)args), RETURN_FAILURE);
U_DEBUG("Found bin [%s]\n", full_bin_path);
status = launch_bin(full_bin_path, args, ef);
status_handler(status);
U_DEBUG("Exit code [%d]\n", ef->history->last_exit_code);
free(full_bin_path);
return status;
}
int execute(ef_t *ef)
{
char **args;
args = parse_args(ef, ef->act_node);
if (!args)
return RETURN_FAILURE;
exec_the_args(ef, args);
free((void *)args);
init_shell_repl(ef->exec_ctx);
return ef->exec_ctx->history->last_exit_code
!= 0 ? RETURN_FAILURE : RETURN_SUCCESS;
}