Add job control

This commit is contained in:
savalet
2025-05-21 00:16:13 +02:00
parent 3514782d17
commit 159624c6eb
7 changed files with 147 additions and 19 deletions

View File

@@ -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,

View File

@@ -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;
}

79
src/job.c Normal file
View File

@@ -0,0 +1,79 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#include <stdlib.h>
#include <unistd.h>
#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;
}

20
src/job.h Normal file
View File

@@ -0,0 +1,20 @@
/*
** EPITECH PROJECT, 2025
** __
** File description:
** _
*/
#ifndef JOB_H
#define JOB_H
#include <sys/wait.h>
#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 */

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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)) {