progress: make gmsh great again
This commit is contained in:
@@ -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 리스트에 등록 (나중에 확인용)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user