#include #include #include #include #include #include #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 리스트에 등록 (나중에 확인용) } }