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