Files

127 lines
3.9 KiB
C

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