mirror of
https://github.com/Savapitech/42sh.git
synced 2026-01-18 16:57:28 +01:00
Finish job control
This commit is contained in:
3
TODO.md
3
TODO.md
@@ -1,6 +1,5 @@
|
||||
# TODO
|
||||
- [ ] Add job control in main branch
|
||||
- [ ] Refactor all the unstable code
|
||||
- [x] Add job control in main branch
|
||||
- [ ] Add auto complete
|
||||
- [ ] Finish line editing
|
||||
- [ ] Syntax highlighting
|
||||
|
||||
@@ -80,6 +80,10 @@ bool process_args(ast_t *node, args_t *args, size_t *toks_i, ef_t *ef)
|
||||
tok.str[tok.sz] = '\0';
|
||||
return (process_globbing(tok.str, args, toks_i));
|
||||
}
|
||||
if (tok.type == T_JOB) {
|
||||
ef->bg = true;
|
||||
return true;
|
||||
}
|
||||
if (tok.type == T_TILDE)
|
||||
return handle_tilde(ef, &tok, args);
|
||||
handle_var_case(node, ef->exec_ctx, toks_i, args);
|
||||
|
||||
@@ -40,9 +40,10 @@ typedef enum size_t {
|
||||
T_BACKSLASH = 1 << 19,
|
||||
T_NEWLINE = 1 << 20, // \n
|
||||
T_TILDE = 1 << 21, // ~
|
||||
T_EOF = 1 << 22, // \0
|
||||
T_ARG = 1 << 23,
|
||||
T_INVALID = 1 << 24
|
||||
T_JOB = 1 << 22, // &
|
||||
T_EOF = 1 << 23, // \0
|
||||
T_ARG = 1 << 24,
|
||||
T_INVALID = 1 << 25
|
||||
} token_type_t;
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -39,7 +39,7 @@ ast_t *parse_arg(ast_ctx_t *ctx, ast_t *node)
|
||||
ctx->act_tok.type = T_ARG;
|
||||
}
|
||||
if (ctx->act_tok.type & (T_ARG | T_REDIRECT | T_APPEND |
|
||||
T_IN_REDIRECT | T_HEREDOC | T_VAR | T_STAR | T_TILDE)) {
|
||||
T_IN_REDIRECT | T_HEREDOC | T_VAR | T_STAR | T_TILDE | T_JOB)) {
|
||||
if (!ensure_node_cap(node))
|
||||
return nullptr;
|
||||
node->vector.tokens[node->vector.sz] = ctx->act_tok;
|
||||
|
||||
@@ -17,6 +17,7 @@ const tokens_list_t TOKENS_LIST[] = {
|
||||
{ T_QUOTES, "\'", 1, "T_QUOTES"},
|
||||
{ T_DQUOTES, "\"", 1, "T_DQUOTES"},
|
||||
{ T_AND, "&&", 2, "T_AND" },
|
||||
{ T_JOB, "&", 1, "T_JOB" },
|
||||
{ T_OR, "||", 2, "T_OR" },
|
||||
{ T_PIPE, "|", 1, "T_PIPE" },
|
||||
{ T_BACKTICK, "`", 1, "T_BACKTICK" },
|
||||
|
||||
@@ -57,4 +57,5 @@ int builtins_astprint(ef_t *, char **args);
|
||||
int builtins_termname(ef_t *ef, char **);
|
||||
int builtins_fg(ef_t *ef, char **);
|
||||
int builtins_bg(ef_t *ef, char **);
|
||||
int builtins_jobs(ef_t *ef, char **args);
|
||||
#endif /* BUILTIND_H */
|
||||
|
||||
@@ -8,20 +8,30 @@
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "exec.h"
|
||||
|
||||
int builtins_bg(ef_t *ef, char **)
|
||||
int builtins_bg(ef_t *ef, char **args)
|
||||
{
|
||||
int last_job_index = ef->exec_ctx->jobs.sz - 1;
|
||||
size_t job_idx = ef->exec_ctx->jobs.sz - 1;
|
||||
char *end_ptr;
|
||||
|
||||
if (!ef->exec_ctx->isatty) {
|
||||
fprintf(stderr, "No job control in this shell.\n");
|
||||
return RETURN_FAILURE;
|
||||
}
|
||||
kill(-ef->exec_ctx->jobs.jobs[last_job_index].pgid, SIGCONT);
|
||||
if (args[1] != nullptr) {
|
||||
job_idx = strtol(args[1], &end_ptr, 10);
|
||||
if (*end_ptr != '\0' || !job_idx
|
||||
|| job_idx > ef->exec_ctx->jobs.sz - 1)
|
||||
return fprintf(stderr, "%s: No such job.\n", args[0]),
|
||||
RETURN_FAILURE;
|
||||
}
|
||||
ef->exec_ctx->jobs.jobs[job_idx].running = true;
|
||||
kill(-ef->exec_ctx->jobs.jobs[job_idx].pgid, SIGCONT);
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -4,27 +4,45 @@
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "exec.h"
|
||||
#include "job.h"
|
||||
|
||||
int builtins_fg(ef_t *ef, char **)
|
||||
static
|
||||
bool get_job_idx(ef_t *ef, char **args, size_t *job_idx)
|
||||
{
|
||||
int status = 0;
|
||||
int last_job_index = ef->exec_ctx->jobs.sz - 1;
|
||||
char *end_ptr;
|
||||
|
||||
if (args[1] != nullptr) {
|
||||
*job_idx = strtol(args[1], &end_ptr, 10);
|
||||
if (*end_ptr != '\0' || !*job_idx
|
||||
|| *job_idx > ef->exec_ctx->jobs.sz - 1)
|
||||
return fprintf(stderr, "%s: No such job.\n", args[0]), false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int builtins_fg(ef_t *ef, char **args)
|
||||
{
|
||||
size_t job_idx = ef->exec_ctx->jobs.sz - 1;
|
||||
int status;
|
||||
|
||||
if (!ef->exec_ctx->isatty) {
|
||||
fprintf(stderr, "No job control in this shell.\n");
|
||||
return RETURN_FAILURE;
|
||||
}
|
||||
if (!set_child_term(ef->exec_ctx, last_job_index))
|
||||
if (!get_job_idx(ef, args, &job_idx))
|
||||
return RETURN_FAILURE;
|
||||
kill(-ef->exec_ctx->jobs.jobs[last_job_index].pgid, SIGCONT);
|
||||
waitpid(-ef->exec_ctx->jobs.jobs[last_job_index].pgid, &status, WUNTRACED);
|
||||
if (!set_child_term(ef->exec_ctx, job_idx))
|
||||
return RETURN_FAILURE;
|
||||
kill(-ef->exec_ctx->jobs.jobs[job_idx].pgid, SIGCONT);
|
||||
waitpid(-ef->exec_ctx->jobs.jobs[job_idx].pgid, &status, WUNTRACED);
|
||||
if (WIFEXITED(status)) {
|
||||
ef->exec_ctx->history->last_exit_code =
|
||||
ef->exec_ctx->history->last_exit_code ?:
|
||||
|
||||
23
src/builtins/jobs.c
Normal file
23
src/builtins/jobs.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
** EPITECH PROJECT, 2025
|
||||
** __
|
||||
** File description:
|
||||
** _
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "exec.h"
|
||||
|
||||
int builtins_jobs(ef_t *ef, char **)
|
||||
{
|
||||
if (!ef->exec_ctx->isatty)
|
||||
return RETURN_SUCCESS;
|
||||
for (size_t i = 1; i < ef->exec_ctx->jobs.sz; i++)
|
||||
dprintf(ef->out_fd, "[%lu] + %s\t %s.\n", i,
|
||||
ef->exec_ctx->jobs.jobs[i].running ? "Running" : "Suspended",
|
||||
ef->exec_ctx->jobs.jobs[i].bin_name);
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
@@ -20,6 +20,7 @@ typedef struct {
|
||||
pid_t pgid;
|
||||
bool running;
|
||||
bool foreground;
|
||||
char *bin_name;
|
||||
} job_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
40
src/exec.c
40
src/exec.c
@@ -55,7 +55,8 @@ const builtins_funcs_t BUILTINS[] = {
|
||||
{ "termname", &builtins_termname },
|
||||
{ "echo", &builtins_echo },
|
||||
{ "fg", &builtins_fg },
|
||||
{ "bg", &builtins_bg }
|
||||
{ "bg", &builtins_bg },
|
||||
{ "jobs", &builtins_jobs }
|
||||
};
|
||||
|
||||
const size_t BUILTINS_SZ = sizeof BUILTINS / sizeof *BUILTINS;
|
||||
@@ -136,35 +137,40 @@ void set_fd(ef_t *ef)
|
||||
}
|
||||
|
||||
static
|
||||
int exec(char *full_bin_path, char **args, ef_t *ef)
|
||||
int wait_child(ef_t *ef, int status, pid_t pid)
|
||||
{
|
||||
execve(full_bin_path, args, ef->env->env);
|
||||
return command_error(full_bin_path, args, errno);
|
||||
bool untraced = (!(ef->flags & F_PIPE) || ef->p_i == ef->p_sz - 1);
|
||||
|
||||
if (!init_child_job(ef->exec_ctx, pid, ef))
|
||||
return RETURN_FAILURE;
|
||||
if (ef->bg) {
|
||||
printf("[%lu] %d\n", ef->exec_ctx->jobs.sz, pid);
|
||||
return RETURN_SUCCESS;
|
||||
}
|
||||
waitpid(pid, &status, untraced ? WUNTRACED : WNOHANG);
|
||||
if (WIFSTOPPED(status)) {
|
||||
ef->exec_ctx->jobs.jobs[ef->exec_ctx->jobs.sz - 1].running = false;
|
||||
ef->exec_ctx->jobs.jobs[ef->exec_ctx->jobs.sz - 1].foreground = false;
|
||||
printf("\nSuspended\n");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static
|
||||
int launch_bin(char *full_bin_path, char **args, ef_t *ef)
|
||||
{
|
||||
int status;
|
||||
int status = 0;
|
||||
pid_t pid = fork();
|
||||
bool untraced = (!(ef->flags & F_PIPE) || ef->p_i == ef->p_sz - 1);
|
||||
|
||||
if (pid == 0) {
|
||||
set_fd(ef);
|
||||
restore_term_flags(ef->exec_ctx);
|
||||
init_child_job(ef->exec_ctx, pid);
|
||||
status = exec(full_bin_path, args, ef);
|
||||
init_child_job(ef->exec_ctx, pid, ef);
|
||||
execve(full_bin_path, args, ef->env->env);
|
||||
status = command_error(full_bin_path, args, errno);
|
||||
exit(RETURN_FAILURE);
|
||||
}
|
||||
if (!init_child_job(ef->exec_ctx, pid))
|
||||
return RETURN_FAILURE;
|
||||
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("\nSuspended\n");
|
||||
}
|
||||
return status;
|
||||
return wait_child(ef, status, pid);
|
||||
}
|
||||
|
||||
static
|
||||
|
||||
@@ -44,6 +44,7 @@ typedef struct {
|
||||
int in_fd;
|
||||
int out_fd;
|
||||
exec_ctx_t *exec_ctx;
|
||||
bool bg;
|
||||
} ef_t;
|
||||
|
||||
__attribute__((nonnull))
|
||||
|
||||
36
src/job.c
36
src/job.c
@@ -9,6 +9,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "builtins_handler.h"
|
||||
#include "exec.h"
|
||||
#include "job.h"
|
||||
|
||||
void set_ignored_signals(int child)
|
||||
@@ -36,32 +37,39 @@ bool ensure_jobs_capacity(jobs_t *jobs)
|
||||
|
||||
bool set_child_term(exec_ctx_t *ec, size_t idx)
|
||||
{
|
||||
if (tcsetpgrp(ec->read_fd, ec->jobs.jobs[idx].pgid) < 0)
|
||||
return tcsetpgrp(ec->read_fd, ec->jobs.jobs[idx].pgid) < 0 ? false : true;
|
||||
}
|
||||
|
||||
static
|
||||
bool init_child_parent(exec_ctx_t *ec, pid_t pid, ef_t *ef)
|
||||
{
|
||||
if (!ef->bg) {
|
||||
setpgid(pid, pid);
|
||||
tcsetpgrp(ec->read_fd, pid);
|
||||
}
|
||||
if (!ensure_jobs_capacity(&ec->jobs))
|
||||
return false;
|
||||
ec->jobs.jobs[ec->jobs.sz].pgid = pid;
|
||||
ec->jobs.jobs[ec->jobs.sz].running = true;
|
||||
ec->jobs.jobs[ec->jobs.sz].foreground = !ef->bg;
|
||||
ec->jobs.jobs[ec->jobs.sz].bin_name =
|
||||
ec->history_command[ec->history_command->sz - 1].command;
|
||||
ec->jobs.sz++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool init_child_job(exec_ctx_t *ec, pid_t pid)
|
||||
bool init_child_job(exec_ctx_t *ec, pid_t pid, ef_t *ef)
|
||||
{
|
||||
if (!ec->isatty)
|
||||
return true;
|
||||
if (pid == 0) {
|
||||
setpgid(0, 0);
|
||||
if (tcsetpgrp(ec->read_fd, getpid()) < 0)
|
||||
if (!ef->bg && tcsetpgrp(ec->read_fd, getpid()) < 0)
|
||||
return false;
|
||||
set_ignored_signals(1);
|
||||
} else {
|
||||
setpgid(pid, pid);
|
||||
if (tcsetpgrp(ec->read_fd, pid) < 0)
|
||||
return false;
|
||||
if (!ensure_jobs_capacity(&ec->jobs))
|
||||
return false;
|
||||
ec->jobs.jobs[ec->jobs.sz].pgid = pid;
|
||||
ec->jobs.jobs[ec->jobs.sz].running = true;
|
||||
ec->jobs.jobs[ec->jobs.sz].foreground = true;
|
||||
ec->jobs.sz++;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return init_child_parent(ec, pid, ef);
|
||||
}
|
||||
|
||||
bool init_jobs(exec_ctx_t *ec)
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "builtins_handler.h"
|
||||
#include "exec.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 init_child_job(exec_ctx_t *ec, pid_t pid, ef_t *ef);
|
||||
bool set_child_term(exec_ctx_t *ec, size_t idx);
|
||||
void set_ignored_signals(int child);
|
||||
#endif /* JOB_H */
|
||||
|
||||
@@ -241,10 +241,8 @@ TESTS = [
|
||||
cmds=[
|
||||
"expr 1 + 2",
|
||||
"expr 4 - 2",
|
||||
#"expr 3 \\* 5",
|
||||
#"expr 5 = 5",
|
||||
#"expr 5 \\< 10",
|
||||
#"expr 5 \\> 3",
|
||||
"expr 5 = 5",
|
||||
"expr 1 != 5",
|
||||
],
|
||||
depends_on=("ARGS",)
|
||||
),
|
||||
@@ -307,7 +305,6 @@ TESTS = [
|
||||
"where cd\n",
|
||||
"where echo\n",
|
||||
"alias ll ls\nwhere ll\n",
|
||||
#"alias ll ls\nwhich ll\n",
|
||||
],
|
||||
depends_on=("ALIAS",)
|
||||
),
|
||||
@@ -342,4 +339,15 @@ TESTS = [
|
||||
],
|
||||
depends_on=()
|
||||
),
|
||||
|
||||
Test(
|
||||
key="JOBS_ERROR",
|
||||
name="jobs error",
|
||||
cmds=[
|
||||
"fg\n",
|
||||
"bg\n",
|
||||
"jobs\n",
|
||||
],
|
||||
depends_on=()
|
||||
),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user