127 lines
3.9 KiB
C
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 리스트에 등록 (나중에 확인용)
|
|
}
|
|
} |