progress: make gmsh great again
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# userprog/gmsh/Makefile
|
# userprog/gmsh/Makefile
|
||||||
|
|
||||||
TARGET = gmsh
|
TARGET = gmsh
|
||||||
SRCS = gmsh.c builtin.c
|
SRCS = gmsh.c job.c parser.c exec.c builtin.c
|
||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -static -Wall
|
CFLAGS = -static -Wall
|
||||||
|
|
||||||
|
|||||||
+1
-56
@@ -114,59 +114,4 @@ void cmd_resize(char* row, char* col) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_help() { printf("Built-ins: ls, cd, pwd, cat, sz, poweroff, reboot\n"); }
|
void cmd_help() { printf("Built-ins: ls, cd, pwd, cat, sz, poweroff, reboot\n"); }
|
||||||
|
|
||||||
void handle_redirection(const char *argv[]) {
|
|
||||||
int i = 0;
|
|
||||||
while (argv[i] != NULL) {
|
|
||||||
int fd = -1;
|
|
||||||
int is_redirect = 0;
|
|
||||||
|
|
||||||
// 1. Output Redirection (Overwrite): >
|
|
||||||
if (strcmp(argv[i], ">") == 0) {
|
|
||||||
if (argv[i+1] == NULL) {
|
|
||||||
fprintf(stderr, "gmsh: syntax error near unexpected token 'newline'\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fd = open(argv[i+1], O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
|
||||||
if (fd < 0) { perror("open"); exit(1); }
|
|
||||||
|
|
||||||
dup2(fd, STDOUT_FILENO); // stdout(1) -> fd
|
|
||||||
close(fd);
|
|
||||||
is_redirect = 1;
|
|
||||||
}
|
|
||||||
// 2. Output Redirection (Append): >>
|
|
||||||
else if (strcmp(argv[i], ">>") == 0) {
|
|
||||||
if (argv[i+1] == NULL) {
|
|
||||||
fprintf(stderr, "gmsh: syntax error\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
// O_APPEND: 뒤에 추가
|
|
||||||
fd = open(argv[i+1], O_WRONLY | O_CREAT | O_APPEND, 0644);
|
|
||||||
if (fd < 0) { perror("open"); exit(1); }
|
|
||||||
|
|
||||||
dup2(fd, STDOUT_FILENO);
|
|
||||||
close(fd);
|
|
||||||
is_redirect = 1;
|
|
||||||
}
|
|
||||||
// 3. Input Redirection: <
|
|
||||||
else if (strcmp(argv[i], "<") == 0) {
|
|
||||||
if (argv[i+1] == NULL) {
|
|
||||||
fprintf(stderr, "gmsh: syntax error\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
fd = open(argv[i+1], O_RDONLY);
|
|
||||||
if (fd < 0) { perror("open"); exit(1); }
|
|
||||||
|
|
||||||
dup2(fd, STDIN_FILENO); // stdin(0) -> fd
|
|
||||||
close(fd);
|
|
||||||
is_redirect = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_redirect) {
|
|
||||||
argv[i] = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
// 리다이렉션 처리 헬퍼
|
||||||
|
static void setup_redirection(command_t *cmd) {
|
||||||
|
if (cmd->input_path) {
|
||||||
|
int fd = open(cmd->input_path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("gmsh: input error");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
dup2(fd, STDIN_FILENO);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->output_path) {
|
||||||
|
int flags = O_WRONLY | O_CREAT;
|
||||||
|
if (cmd->append_mode) flags |= O_APPEND;
|
||||||
|
else flags |= O_TRUNC;
|
||||||
|
|
||||||
|
int fd = open(cmd->output_path, flags, 0644);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("gmsh: output error");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
dup2(fd, STDOUT_FILENO);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job 실행 (핵심 함수)
|
||||||
|
void launch_job(job_t *job) {
|
||||||
|
command_t *cmd;
|
||||||
|
int next_in_fd = STDIN_FILENO; // 파이프라인의 이전 단계 출력 (현재 단계의 입력)
|
||||||
|
int pipe_fds[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
// 1. 명령어 리스트 순회
|
||||||
|
for (cmd = job->head; cmd != NULL; cmd = cmd->next) {
|
||||||
|
// 다음 명령어가 있으면 파이프 생성
|
||||||
|
if (cmd->next) {
|
||||||
|
if (pipe(pipe_fds) < 0) {
|
||||||
|
perror("pipe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 프로세스 생성
|
||||||
|
pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
perror("fork");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else if (pid == 0) {
|
||||||
|
// [자식 프로세스]
|
||||||
|
|
||||||
|
// A. 프로세스 그룹 설정 (Job Control)
|
||||||
|
// 첫 번째 프로세스의 PID를 그룹 ID로 삼음
|
||||||
|
if (job->pgid == 0) job->pgid = getpid();
|
||||||
|
setpgid(0, job->pgid);
|
||||||
|
|
||||||
|
// B. 시그널 핸들러 복구 (쉘의 보호막 해제)
|
||||||
|
signal(SIGINT, SIG_DFL);
|
||||||
|
signal(SIGCHLD, SIG_DFL);
|
||||||
|
signal(SIGTTOU, SIG_DFL);
|
||||||
|
|
||||||
|
// C. 입력 연결 (이전 파이프 -> 내 입력)
|
||||||
|
if (next_in_fd != STDIN_FILENO) {
|
||||||
|
dup2(next_in_fd, STDIN_FILENO);
|
||||||
|
close(next_in_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// D. 출력 연결 (내 출력 -> 다음 파이프)
|
||||||
|
if (cmd->next) {
|
||||||
|
dup2(pipe_fds[1], STDOUT_FILENO);
|
||||||
|
close(pipe_fds[1]);
|
||||||
|
close(pipe_fds[0]); // 읽기 쪽은 안 씀
|
||||||
|
}
|
||||||
|
|
||||||
|
// E. 리다이렉션 (파이프보다 우선순위 높음)
|
||||||
|
setup_redirection(cmd);
|
||||||
|
|
||||||
|
// F. 실행
|
||||||
|
execvp(cmd->argv[0], cmd->argv);
|
||||||
|
fprintf(stderr, "gmsh: command not found: %s\n", cmd->argv[0]);
|
||||||
|
exit(127);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// [부모 프로세스]
|
||||||
|
|
||||||
|
// A. Job PGID 설정 (첫 번째 자식의 PID로)
|
||||||
|
if (job->pgid == 0) job->pgid = pid;
|
||||||
|
setpgid(pid, job->pgid); // 자식과 부모 양쪽에서 해줘야 경쟁상태 방지
|
||||||
|
|
||||||
|
// B. 파이프 정리
|
||||||
|
// 이전에 썼던 입력 파이프 닫기
|
||||||
|
if (next_in_fd != STDIN_FILENO) close(next_in_fd);
|
||||||
|
|
||||||
|
// 현재 만든 파이프의 읽기 쪽을 다음 루프로 넘김
|
||||||
|
if (cmd->next) {
|
||||||
|
next_in_fd = pipe_fds[0];
|
||||||
|
close(pipe_fds[1]); // 쓰기 쪽은 자식이 가져갔으니 닫음
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 대기 (Foreground인 경우)
|
||||||
|
if (!job->is_bg) {
|
||||||
|
// 이 Job에 속한 모든 프로세스가 끝날 때까지 대기
|
||||||
|
int status;
|
||||||
|
while (waitpid(-job->pgid, &status, WNOHANG) == 0) {
|
||||||
|
// 아직 안 끝난 자식이 있으면 잠시 대기 (CPU 절약)
|
||||||
|
// 실제로는 sigsuspend 등을 쓰는 게 정석이지만 간단히 sleep/pause
|
||||||
|
// 여기선 blocking wait로 단순화
|
||||||
|
waitpid(-job->pgid, &status, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Background
|
||||||
|
printf("[%d] %d\n", job->id, job->pgid);
|
||||||
|
add_job(job); // Job 리스트에 등록 (나중에 확인용)
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
-174
@@ -9,122 +9,62 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "para.h"
|
|
||||||
#include "builtin.h"
|
#include "builtin.h"
|
||||||
|
#include "para.h"
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
job_t *parse_line(char *line);
|
||||||
|
void launch_job(job_t *job);
|
||||||
|
|
||||||
static bool verbose = false;
|
static bool verbose = false;
|
||||||
|
volatile pid_t fg_pgid = 0;
|
||||||
|
|
||||||
int parse_cmd(char *cmd, char *argv[]) {
|
void handle_sigint(int sig) {
|
||||||
int argc = 0;
|
printf("\n");
|
||||||
char *ptr = cmd;
|
|
||||||
char *token_start;
|
|
||||||
char quote_char = 0;
|
|
||||||
|
|
||||||
while (*ptr) {
|
|
||||||
while (*ptr == ' ' && quote_char == 0) ptr++;
|
|
||||||
if (*ptr == '\0') break;
|
|
||||||
|
|
||||||
token_start = ptr;
|
|
||||||
char *write_ptr = ptr;
|
|
||||||
|
|
||||||
while (*ptr) {
|
|
||||||
if (quote_char == 0 && *ptr == ' ') {
|
|
||||||
ptr++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*ptr == '"' || *ptr == '\'') {
|
|
||||||
if (quote_char == *ptr) quote_char = 0;
|
|
||||||
else if (quote_char == 0) quote_char = *ptr;
|
|
||||||
else *write_ptr++ = *ptr;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*write_ptr++ = *ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr++;
|
|
||||||
}
|
|
||||||
|
|
||||||
*write_ptr = '\0';
|
|
||||||
argv[argc++] = token_start;
|
|
||||||
|
|
||||||
if (argc >= MAX_ARGS - 1) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
argv[argc] = NULL;
|
|
||||||
return argc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 반환값: 1(처리함), 0(파이프 없음)
|
void handle_sigchld(int sig) {
|
||||||
int handle_pipe(char *argv[]) {
|
while (waitpid(-1, NULL, WNOHANG) > 0);
|
||||||
int pipe_idx = -1;
|
|
||||||
|
|
||||||
for (int i = 0; argv[i] != NULL; i++) {
|
|
||||||
if (strcmp(argv[i], "|") == 0) {
|
|
||||||
pipe_idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pipe_idx == -1) return 0;
|
|
||||||
|
|
||||||
argv[pipe_idx] = NULL;
|
|
||||||
char **left_cmd = &argv[0];
|
|
||||||
char **right_cmd = &argv[pipe_idx + 1];
|
|
||||||
|
|
||||||
if (right_cmd[0] == NULL) {
|
|
||||||
printf("gmsh: syntax error near unexpected token `|'\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fds[2];
|
|
||||||
if (pipe(fds) == -1) {
|
|
||||||
perror("pipe");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t pid1 = fork();
|
|
||||||
if (pid1 == 0) {
|
|
||||||
close(fds[0]);
|
|
||||||
dup2(fds[1], STDOUT_FILENO);
|
|
||||||
close(fds[1]);
|
|
||||||
|
|
||||||
handle_redirection((const char**) left_cmd);
|
|
||||||
execvp(left_cmd[0], left_cmd);
|
|
||||||
perror("execvp left");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t pid2 = fork();
|
|
||||||
if (pid2 == 0) {
|
|
||||||
close(fds[1]);
|
|
||||||
dup2(fds[0], STDIN_FILENO);
|
|
||||||
close(fds[0]);
|
|
||||||
|
|
||||||
if (handle_pipe(right_cmd) == 1) {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
handle_redirection((const char**) right_cmd);
|
|
||||||
execvp(right_cmd[0], right_cmd);
|
|
||||||
perror("execvp right");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fds[0]);
|
|
||||||
close(fds[1]);
|
|
||||||
|
|
||||||
waitpid(pid1, NULL, 0);
|
|
||||||
waitpid(pid2, NULL, 0);
|
|
||||||
|
|
||||||
return 1; // 처리 완료
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int builtin_cmd(job_t *job) {
|
||||||
|
command_t *cmd = job->head;
|
||||||
|
if (!cmd || !cmd->argv[0]) return 0;
|
||||||
|
|
||||||
|
// if (strcmp(cmd->argv[0], "exit") == 0) exit(0);
|
||||||
|
// if (strcmp(cmd->argv[0], "cd") == 0) {
|
||||||
|
// if (cmd->argc < 2) fprintf(stderr, "cd: missing argument\n");
|
||||||
|
// else if (chdir(cmd->argv[1]) != 0) perror("cd");
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
bool ret = 0;
|
||||||
|
if (strcmp(cmd->argv[0], "exit") == 0) exit(0), ret = 1;
|
||||||
|
else if (strcmp(cmd->argv[0], "ls") == 0) cmd_ls(cmd->argv[1]), ret = 1;
|
||||||
|
else if (strcmp(cmd->argv[0], "cd") == 0) cmd_cd(cmd->argv[1]), ret = 1;
|
||||||
|
else if (strcmp(cmd->argv[0], "pwd") == 0) cmd_pwd(), ret = 1;
|
||||||
|
// else if (strcmp(cmd->argv[0], "cat") == 0) cmd_cat(argc, (const char**) argv);
|
||||||
|
// else if (strcmp(cmd->argv[0], "proc") == 0) cmd_proc();
|
||||||
|
// else if (strcmp(cmd->argv[0], "echo") == 0) cmd_echo(argc, (const char**) argv);
|
||||||
|
|
||||||
|
else if (strcmp(cmd->argv[0], "size") == 0) cmd_size(), ret = 1;
|
||||||
|
else if (strcmp(cmd->argv[0], "resize") == 0){
|
||||||
|
if(cmd->argc != 3){
|
||||||
|
printf("Usage: resize <rows> <cols>\n");
|
||||||
|
}
|
||||||
|
else cmd_resize(cmd->argv[1], cmd->argv[2]);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strcmp(cmd->argv[0], "poweroff") == 0) cmd_poweroff(), ret = 1;
|
||||||
|
else if (strcmp(cmd->argv[0], "reboot") == 0) cmd_reboot(), ret = 1;
|
||||||
|
else if (strcmp(cmd->argv[0], "help") == 0) cmd_help(), ret = 1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void run_shell() {
|
void run_shell() {
|
||||||
char cmd_buf[MAX_CMD_LEN];
|
char cmd_buf[MAX_CMD_LEN];
|
||||||
char *args[MAX_ARGS];
|
|
||||||
char cwd[1024];
|
char cwd[1024];
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -136,82 +76,23 @@ void run_shell() {
|
|||||||
cmd_buf[strcspn(cmd_buf, "\n")] = 0;
|
cmd_buf[strcspn(cmd_buf, "\n")] = 0;
|
||||||
if (strlen(cmd_buf) == 0) continue;
|
if (strlen(cmd_buf) == 0) continue;
|
||||||
|
|
||||||
// int argc = 0;
|
job_t *job = parse_line(cmd_buf);
|
||||||
// args[argc] = strtok(cmd_buf, " ");
|
if (job == NULL) continue;
|
||||||
// while (args[argc] != NULL && argc < MAX_ARGS - 1) args[++argc] = strtok(NULL, " ");
|
|
||||||
|
|
||||||
int argc = parse_cmd(cmd_buf, args);
|
if (builtin_cmd(job)) {
|
||||||
|
free_job(job);
|
||||||
if (argc == 0) continue;
|
|
||||||
|
|
||||||
if (handle_pipe(args)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
launch_job(job);
|
||||||
printf("[debug] argc: %d\n", argc);
|
if (!job->is_bg) free_job(job);
|
||||||
for(int i=0; i<argc; i++) printf("[debug] argv[%d]: '%s'\n", i, args[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(args[0], "exit") == 0) exit(0);
|
|
||||||
else if (strcmp(args[0], "ls") == 0) cmd_ls(args[1]);
|
|
||||||
else if (strcmp(args[0], "cd") == 0) cmd_cd(args[1]);
|
|
||||||
else if (strcmp(args[0], "pwd") == 0) cmd_pwd();
|
|
||||||
// else if (strcmp(args[0], "cat") == 0) cmd_cat(argc, (const char**) args);
|
|
||||||
// else if (strcmp(args[0], "proc") == 0) cmd_proc();
|
|
||||||
// else if (strcmp(args[0], "echo") == 0) cmd_echo(argc, (const char**) args);
|
|
||||||
|
|
||||||
else if (strcmp(args[0], "size") == 0) cmd_size();
|
|
||||||
else if (strcmp(args[0], "resize") == 0){
|
|
||||||
if(argc != 3){
|
|
||||||
printf("Usage: resize <rows> <cols>\n");
|
|
||||||
}
|
|
||||||
else cmd_resize(args[1], args[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (strcmp(args[0], "poweroff") == 0) cmd_poweroff();
|
|
||||||
else if (strcmp(args[0], "reboot") == 0) cmd_reboot();
|
|
||||||
else if (strcmp(args[0], "help") == 0) cmd_help();
|
|
||||||
else {
|
|
||||||
pid_t pid = fork();
|
|
||||||
|
|
||||||
// if (pid == 0) {
|
|
||||||
// execvp(args[0], args);
|
|
||||||
// printf("%s: command not found\n", args[0]);
|
|
||||||
// exit(1);
|
|
||||||
// } else wait(NULL);
|
|
||||||
|
|
||||||
if (pid == 0) {
|
|
||||||
handle_redirection((const char**) args);
|
|
||||||
execvp(args[0], args);
|
|
||||||
|
|
||||||
fprintf(stderr, "%s: command not found\n", args[0]);
|
|
||||||
exit(127);
|
|
||||||
} else if (pid > 0) {
|
|
||||||
int status;
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
printf("[debug] Spawned child PID: %d\n", pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
waitpid(pid, &status, 0);
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
printf("[debug] Process %d exited with code %d\n", pid, WEXITSTATUS(status));
|
|
||||||
} else if (WIFSIGNALED(status)) {
|
|
||||||
printf("[debug] Process %d killed by signal %d\n", pid, WTERMSIG(status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
perror("fork");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
printf("GMSH (GMS Minimal SHell) v1.0 initialized.\n");
|
signal(SIGINT, handle_sigint);
|
||||||
|
signal(SIGCHLD, handle_sigchld);
|
||||||
|
signal(SIGTTOU, SIG_IGN);
|
||||||
|
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "v")) != -1) {
|
while ((opt = getopt(argc, argv, "v")) != -1) {
|
||||||
@@ -225,7 +106,9 @@ int main(int argc, char** argv) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(verbose) printf("GMSH (GMS Minimal SHell) v1.0 initialized.\n");
|
||||||
|
|
||||||
run_shell();
|
run_shell();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
job_t *job_list_head = NULL;
|
||||||
|
static int next_job_id = 1;
|
||||||
|
|
||||||
|
// 초기화
|
||||||
|
void init_job_list() {
|
||||||
|
job_list_head = NULL;
|
||||||
|
next_job_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 새로운 Job 생성 (껍데기)
|
||||||
|
job_t *create_job(char *line) {
|
||||||
|
job_t *job = (job_t *)malloc(sizeof(job_t));
|
||||||
|
memset(job, 0, sizeof(job_t));
|
||||||
|
|
||||||
|
job->id = next_job_id++; // ID 자동 할당
|
||||||
|
job->command_line = strdup(line); // 원본 보존
|
||||||
|
job->state = JOB_RUNNING;
|
||||||
|
job->next = NULL;
|
||||||
|
job->head = NULL;
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 명령어 추가 (파이프라인 연결)
|
||||||
|
void append_command(job_t *job, command_t *cmd) {
|
||||||
|
if (job->head == NULL) {
|
||||||
|
job->head = cmd;
|
||||||
|
} else {
|
||||||
|
// 리스트 끝을 찾아서 연결
|
||||||
|
command_t *curr = job->head;
|
||||||
|
while (curr->next != NULL) {
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
curr->next = cmd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job 리스트에 등록
|
||||||
|
void add_job(job_t *job) {
|
||||||
|
if (job_list_head == NULL) {
|
||||||
|
job_list_head = job;
|
||||||
|
} else {
|
||||||
|
job_t *curr = job_list_head;
|
||||||
|
while (curr->next != NULL) {
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
curr->next = job;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Job 삭제 (PGID로 찾아서)
|
||||||
|
int delete_job(pid_t pgid) {
|
||||||
|
job_t *curr = job_list_head;
|
||||||
|
job_t *prev = NULL;
|
||||||
|
|
||||||
|
while (curr != NULL) {
|
||||||
|
if (curr->pgid == pgid) {
|
||||||
|
// 연결 끊기
|
||||||
|
if (prev == NULL) job_list_head = curr->next;
|
||||||
|
else prev->next = curr->next;
|
||||||
|
|
||||||
|
free_job(curr);
|
||||||
|
return 1; // 성공
|
||||||
|
}
|
||||||
|
prev = curr;
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
return 0; // 못 찾음
|
||||||
|
}
|
||||||
|
|
||||||
|
// PGID로 Job 찾기
|
||||||
|
job_t *find_job(pid_t pgid) {
|
||||||
|
job_t *curr = job_list_head;
|
||||||
|
while (curr != NULL) {
|
||||||
|
if (curr->pgid == pgid) return curr;
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID로 Job 찾기 (fg %1, bg %2 처리용)
|
||||||
|
job_t *find_job_by_id(int id) {
|
||||||
|
job_t *curr = job_list_head;
|
||||||
|
while (curr != NULL) {
|
||||||
|
if (curr->id == id) return curr;
|
||||||
|
curr = curr->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 메모리 해제 (재귀적으로 Command도 해제)
|
||||||
|
void free_command(command_t *cmd) {
|
||||||
|
if (cmd == NULL) return;
|
||||||
|
free_command(cmd->next); // 재귀 호출로 다음꺼 먼저 삭제
|
||||||
|
|
||||||
|
// 인자 해제
|
||||||
|
// (주의: argv 문자열들이 한 덩어리 buffer를 가리키는지, 각각 malloc인지에 따라 다름)
|
||||||
|
// 여기서는 파서 구현에 따라 달라지므로 일단 구조체만 해제
|
||||||
|
if (cmd->input_path) free(cmd->input_path);
|
||||||
|
if (cmd->output_path) free(cmd->output_path);
|
||||||
|
free(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_job(job_t *job) {
|
||||||
|
if (job == NULL) return;
|
||||||
|
free_command(job->head); // 연결된 명령어들 해제
|
||||||
|
if (job->command_line) free(job->command_line);
|
||||||
|
free(job);
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef _JOB_H_
|
||||||
|
#define _JOB_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define MAX_ARGS 64
|
||||||
|
|
||||||
|
// [1단계] 단일 명령어 (Simple Command)
|
||||||
|
// 예: "grep error > log.txt"
|
||||||
|
typedef struct command_t {
|
||||||
|
char *argv[MAX_ARGS]; // 인자 리스트 (예: {"grep", "error", NULL})
|
||||||
|
int argc; // 인자 개수
|
||||||
|
|
||||||
|
char *input_path; // 리다이렉션 < (없으면 NULL)
|
||||||
|
char *output_path; // 리다이렉션 > (없으면 NULL)
|
||||||
|
int append_mode; // >> 모드 여부 (0 or 1)
|
||||||
|
|
||||||
|
struct command_t *next; // 파이프라인의 다음 명령어 (Linked List)
|
||||||
|
} command_t;
|
||||||
|
|
||||||
|
// [2단계] 작업 상태 (Job State)
|
||||||
|
typedef enum {
|
||||||
|
JOB_RUNNING, // 실행 중
|
||||||
|
JOB_STOPPED, // 정지됨 (Ctrl+Z)
|
||||||
|
JOB_DONE // 완료됨
|
||||||
|
} job_state_t;
|
||||||
|
|
||||||
|
// [3단계] 작업 (Job) - 파이프라인 전체를 하나의 작업으로 봄
|
||||||
|
// 예: "ls | grep c &"
|
||||||
|
typedef struct job_t {
|
||||||
|
int id; // Job ID (쉘이 관리하는 번호: 1, 2, 3...)
|
||||||
|
pid_t pgid; // Process Group ID (이 작업의 대표 PID)
|
||||||
|
job_state_t state; // 현재 상태
|
||||||
|
int is_bg; // 백그라운드 실행 여부 (1=BG, 0=FG)
|
||||||
|
char *command_line; // 원본 명령어 문자열 (jobs 출력용)
|
||||||
|
|
||||||
|
command_t *head; // 명령어 리스트의 첫 번째 (ls)
|
||||||
|
|
||||||
|
struct job_t *next; // 다음 작업 (Job List용)
|
||||||
|
} job_t;
|
||||||
|
|
||||||
|
// --- 함수 원형 ---
|
||||||
|
|
||||||
|
// Job 관리
|
||||||
|
void init_job_list();
|
||||||
|
job_t *create_job(char *line);
|
||||||
|
void add_job(job_t *job);
|
||||||
|
int delete_job(pid_t pgid);
|
||||||
|
job_t *find_job(pid_t pgid);
|
||||||
|
job_t *find_job_by_id(int id);
|
||||||
|
void free_job(job_t *job);
|
||||||
|
|
||||||
|
// Command 관리
|
||||||
|
void append_command(job_t *job, command_t *cmd);
|
||||||
|
void free_command(command_t *cmd);
|
||||||
|
|
||||||
|
// 전역 변수 (외부에서 접근 가능)
|
||||||
|
extern job_t *job_list_head;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "job.h"
|
||||||
|
|
||||||
|
// 문자열을 공백 기준으로 쪼개서 argv에 넣기
|
||||||
|
static void parse_args(char *cmd_str, command_t *cmd) {
|
||||||
|
char *token = strtok(cmd_str, " \t\n");
|
||||||
|
while (token != NULL && cmd->argc < MAX_ARGS - 1) {
|
||||||
|
|
||||||
|
// 리다이렉션 처리 (간이 버전)
|
||||||
|
if (strcmp(token, "<") == 0) {
|
||||||
|
token = strtok(NULL, " \t\n");
|
||||||
|
if (token) cmd->input_path = strdup(token);
|
||||||
|
}
|
||||||
|
else if (strcmp(token, ">") == 0) {
|
||||||
|
token = strtok(NULL, " \t\n");
|
||||||
|
if (token) cmd->output_path = strdup(token);
|
||||||
|
cmd->append_mode = 0;
|
||||||
|
}
|
||||||
|
else if (strcmp(token, ">>") == 0) {
|
||||||
|
token = strtok(NULL, " \t\n");
|
||||||
|
if (token) cmd->output_path = strdup(token);
|
||||||
|
cmd->append_mode = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// 일반 인자
|
||||||
|
cmd->argv[cmd->argc++] = strdup(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, " \t\n");
|
||||||
|
}
|
||||||
|
cmd->argv[cmd->argc] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 메인 파서 함수
|
||||||
|
job_t *parse_line(char *line) {
|
||||||
|
if (line == NULL || strlen(line) == 0) return NULL;
|
||||||
|
|
||||||
|
// 1. Job 껍데기 생성
|
||||||
|
job_t *job = create_job(line);
|
||||||
|
|
||||||
|
// 2. 백그라운드(&) 확인
|
||||||
|
// 문자열 끝에서부터 역탐색하거나, 토큰화 과정에서 체크해야 함.
|
||||||
|
// 편의상 strrchr 사용 (단, 따옴표 안에 있는 &는 무시 못함. 일단 간단히 구현)
|
||||||
|
char *bg_ptr = strrchr(line, '&');
|
||||||
|
if (bg_ptr != NULL) {
|
||||||
|
job->is_bg = 1;
|
||||||
|
*bg_ptr = ' '; // &를 공백으로 치환해서 명령어 파싱 방해 안 되게 함
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 파이프(|) 단위로 자르기
|
||||||
|
// strtok는 원본을 훼손하므로 복사본 사용
|
||||||
|
char *line_copy = strdup(line);
|
||||||
|
char *cmd_str = strtok(line_copy, "|");
|
||||||
|
|
||||||
|
while (cmd_str != NULL) {
|
||||||
|
command_t *cmd = (command_t *)malloc(sizeof(command_t));
|
||||||
|
memset(cmd, 0, sizeof(command_t));
|
||||||
|
|
||||||
|
// 내부 인자 및 리다이렉션 파싱
|
||||||
|
parse_args(cmd_str, cmd);
|
||||||
|
|
||||||
|
if (cmd->argc > 0) {
|
||||||
|
append_command(job, cmd);
|
||||||
|
} else {
|
||||||
|
free(cmd); // 빈 명령어는 버림
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_str = strtok(NULL, "|");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line_copy);
|
||||||
|
|
||||||
|
if (job->head == NULL) { // 명령어가 하나도 없으면
|
||||||
|
free_job(job);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user