diff --git a/src/builtins_handler.h b/src/builtins_handler.h index f8fa00d..0a337ff 100644 --- a/src/builtins_handler.h +++ b/src/builtins_handler.h @@ -16,6 +16,18 @@ #include "local.h" #include "shell.h" +typedef struct { + pid_t pgid; + bool running; + bool foreground; +} job_t; + +typedef struct { + job_t *jobs; + size_t sz; + size_t cap; +} jobs_t; + typedef struct { env_t *env; history_t *history; @@ -31,6 +43,7 @@ typedef struct { char *precmd; char *cwdcmd; bool ignoreof; + jobs_t jobs; } exec_ctx_t; size_t update_command(char **buffer, diff --git a/src/exec.c b/src/exec.c index 729b871..01c7bbf 100644 --- a/src/exec.c +++ b/src/exec.c @@ -20,6 +20,7 @@ #include "debug.h" #include "env.h" #include "exec.h" +#include "job.h" #include "path.h" #include "repl.h" #include "u_mem.h" @@ -52,7 +53,8 @@ const builtins_funcs_t BUILTINS[] = { { "break", &builtins_break }, { "astprint", &builtins_astprint }, { "termname", &builtins_termname }, - { "echo", &builtins_echo } + { "echo", &builtins_echo }, + { "fg", &builtins_fg } }; const size_t BUILTINS_SZ = sizeof BUILTINS / sizeof *BUILTINS; @@ -132,28 +134,38 @@ void set_fd(ef_t *ef) } } +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(); + bool untraced = (!(ef->flags & F_PIPE) || ef->p_i == ef->p_sz - 1); if (pid == 0) { - restore_term_flags(ef->exec_ctx); set_fd(ef); - if (execve(full_bin_path, args, ef->env->env) < 0) { - status = command_error(full_bin_path, args, errno); - free_env(ef->env); - exit(((free_args(args)), status)); - } + restore_term_flags(ef->exec_ctx); + init_child_job(ef->exec_ctx, pid); + status = exec(full_bin_path, args, ef); + exit(RETURN_FAILURE); + } + setpgid(pid, pid); + tcsetpgrp(ef->exec_ctx->read_fd, pid); + waitpid(pid, &status, untraced ? 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("[%lu]+ Continued &\n", ef->exec_ctx->jobs.sz); } - if (!(ef->flags & F_PIPE) || ef->p_i == ef->p_sz - 1) - waitpid(pid, &status, 0); - else - waitpid(pid, &status, WNOHANG); if (WIFEXITED(status)) ef->exec_ctx->history->last_exit_code = - ef->exec_ctx->history->last_exit_code ?: WEXITSTATUS(status); + ef->exec_ctx->history->last_exit_code ?: WEXITSTATUS(status); return status; } @@ -223,6 +235,9 @@ int execute(ef_t *ef) exec_the_args(ef, args); free_args(args); init_shell_repl(ef->exec_ctx); + if (ef->exec_ctx->isatty && + tcsetpgrp(ef->exec_ctx->read_fd, ef->exec_ctx->jobs.jobs[0].pgid) < 0) + return RETURN_FAILURE; return ef->exec_ctx->history->last_exit_code != 0 ? RETURN_FAILURE : RETURN_SUCCESS; } diff --git a/src/job.c b/src/job.c new file mode 100644 index 0000000..49cdc75 --- /dev/null +++ b/src/job.c @@ -0,0 +1,79 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#include +#include + +#include "builtins_handler.h" +#include "job.h" + +void set_ignored_signals(int child) +{ + signal(SIGINT, child ? SIG_DFL : SIG_IGN); + signal(SIGQUIT, child ? SIG_DFL : SIG_IGN); + signal(SIGTSTP, child ? SIG_DFL : SIG_IGN); + signal(SIGTTIN, child ? SIG_DFL : SIG_IGN); + signal(SIGTTOU, child ? SIG_DFL : SIG_IGN); +} + +bool ensure_jobs_capacity(jobs_t *jobs) +{ + job_t *tmp; + + if (jobs->sz + 1 < jobs->cap) + return true; + tmp = realloc(jobs->jobs, sizeof *jobs->jobs * jobs->cap << 1); + if (tmp == nullptr) + return false; + jobs->jobs = tmp; + jobs->cap <<= 1; + return true; +} + +bool set_child_fg(exec_ctx_t *ec, size_t idx) +{ + if (tcsetpgrp(ec->read_fd, ec->jobs.jobs[idx].pgid) < 0) + return false; + kill(-ec->jobs.jobs[idx].pgid, SIGCONT); + return true; +} + +bool init_child_job(exec_ctx_t *ec, pid_t) +{ + if (!ec->isatty) + return true; + setpgid(0, 0); + tcsetpgrp(ec->read_fd, getpid()); + set_ignored_signals(1); + if (!ensure_jobs_capacity(&ec->jobs)) + return false; + ec->jobs.jobs[ec->jobs.sz].pgid = getpid(); + ec->jobs.jobs[ec->jobs.sz].running = true; + ec->jobs.jobs[ec->jobs.sz].foreground = true; + ec->jobs.sz++; + return true; +} + +bool init_jobs(exec_ctx_t *ec) +{ + ec->jobs.jobs = nullptr; + if (!isatty(ec->read_fd)) + return true; + ec->jobs.cap = DEFAULT_JOBS_CAP; + ec->jobs.jobs = malloc(sizeof *ec->jobs.jobs * ec->jobs.cap); + if (ec->jobs.jobs == nullptr) + return false; + ec->jobs.jobs[0].pgid = getpid(); + ec->jobs.jobs[0].running = true; + ec->jobs.sz = 1; + if (setpgid(ec->jobs.jobs[0].pgid, ec->jobs.jobs[0].pgid) < 0) + return false; + if (tcsetpgrp(ec->read_fd, ec->jobs.jobs[0].pgid) < 0) + return false; + set_ignored_signals(0); + return true; +} diff --git a/src/job.h b/src/job.h new file mode 100644 index 0000000..b4a0901 --- /dev/null +++ b/src/job.h @@ -0,0 +1,20 @@ +/* +** EPITECH PROJECT, 2025 +** __ +** File description: +** _ +*/ + +#ifndef JOB_H + #define JOB_H + #include + + #include "builtins_handler.h" + #define DEFAULT_JOBS_CAP 4 + +bool ensure_jobs_capacity(jobs_t *jobs); +bool init_jobs(exec_ctx_t *ec); +bool init_child_job(exec_ctx_t *ec, pid_t pid); +bool set_child_fg(exec_ctx_t *ec, size_t idx); +void set_ignored_signals(int child); +#endif /* JOB_H */ diff --git a/src/magic_quotes.c b/src/magic_quotes.c index a56cedd..fa10809 100644 --- a/src/magic_quotes.c +++ b/src/magic_quotes.c @@ -56,20 +56,20 @@ char *add_to_args(char *to_return, args_t *args, size_t *sz, size_t i) { to_return = realloc_buffer(to_return, sz, &i); if (to_return == NULL) - return NULL; + return nullptr; to_return[i] = '\0'; if (!ensure_args_capacity(args)) return (free(to_return), NULL); args->args[args->sz] = to_return; args->sz++; - return NULL; + return nullptr; } char *handle_buffer(char *to_return, size_t *sz, size_t *i, char buf) { to_return = realloc_buffer(to_return, sz, i); if (to_return == NULL) - return NULL; + return nullptr; to_return[*i] = buf; return to_return; } diff --git a/src/repl.h b/src/repl.h index 7d74bc2..1c808fc 100644 --- a/src/repl.h +++ b/src/repl.h @@ -14,7 +14,7 @@ #include "readline.h" #include "u_str.h" -void init_shell_repl(exec_ctx_t *exec_ctx); +void init_shell_repl(exec_ctx_t *ec); void restore_term_flags(exec_ctx_t *exec_ctx); diff --git a/src/shell.c b/src/shell.c index a2bafcb..ceba597 100644 --- a/src/shell.c +++ b/src/shell.c @@ -16,6 +16,7 @@ #include "debug.h" #include "env.h" #include "history.h" +#include "job.h" #include "local.h" #include "readline.h" #include "repl.h" @@ -147,8 +148,7 @@ int shell(opt_t *opt, char **env_ptr) { alias_t alias = init_alias(); env_t env = parse_env(env_ptr); - history_t history = { .cmd_history = nullptr, .last_exit_code = 0, - .last_chdir = nullptr}; + history_t history = { nullptr, 0, nullptr}; his_command_t *cmd_history = init_cmd_history(); local_t local = create_local(); exec_ctx_t exec_ctx = {.env = &env, .local = &local, .opt = opt, @@ -157,7 +157,8 @@ int shell(opt_t *opt, char **env_ptr) .ignoreof = false }; int shell_result; - if (exec_ctx.read_fd == -1 || (int)error_in_init(&exec_ctx)) + if (exec_ctx.read_fd == -1 || (int)error_in_init(&exec_ctx) + || !init_jobs(&exec_ctx)) return RETURN_FAILURE; shell_result = shell_loop(isatty(exec_ctx.read_fd), &exec_ctx); if (opt->cmd == NULL && isatty(exec_ctx.read_fd)) {