/* * asl_capture.c * * Deterministic execution capture with optional PTY support. * * PIPE mode: strict stdin/stdout/stderr separation * PTY mode: interactive, single combined stream */ #include "asl_capture.h" #include #include #include #include #include #include #include #include #include /* PTY support is optional and explicitly enabled */ #ifdef ASL_ENABLE_PTY #define _GNU_SOURCE #include #endif /* ------------------------------------------------------------------------- */ /* Utilities */ /* ------------------------------------------------------------------------- */ static void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL, 0); if (flags >= 0) fcntl(fd, F_SETFL, flags | O_NONBLOCK); } /* ------------------------------------------------------------------------- */ /* PIPE mode implementation */ /* ------------------------------------------------------------------------- */ static pid_t spawn_pipe( char **argv, int *child_stdin, int *child_stdout, int *child_stderr ) { int in_p[2], out_p[2], err_p[2]; if (pipe(in_p) < 0) return -1; if (pipe(out_p) < 0) return -1; if (pipe(err_p) < 0) return -1; pid_t pid = fork(); if (pid < 0) return -1; if (pid == 0) { /* child */ dup2(in_p[0], STDIN_FILENO); dup2(out_p[1], STDOUT_FILENO); dup2(err_p[1], STDERR_FILENO); close(in_p[1]); close(out_p[0]); close(err_p[0]); execvp(argv[0], argv); perror("execvp"); _exit(127); } /* parent */ close(in_p[0]); close(out_p[1]); close(err_p[1]); *child_stdin = in_p[1]; *child_stdout = out_p[0]; *child_stderr = err_p[0]; set_nonblocking(*child_stdout); set_nonblocking(*child_stderr); return pid; } static void pump_pipe( int child_stdin, int child_stdout, int child_stderr ) { char buf[8192]; int in_open = 1, out_open = 1, err_open = 1; while (in_open || out_open || err_open) { fd_set rfds; FD_ZERO(&rfds); if (in_open) FD_SET(STDIN_FILENO, &rfds); if (out_open) FD_SET(child_stdout, &rfds); if (err_open) FD_SET(child_stderr, &rfds); int maxfd = child_stdout > child_stderr ? child_stdout : child_stderr; if (select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0) { if (errno == EINTR) continue; break; } /* stdin -> child stdin */ if (in_open && FD_ISSET(STDIN_FILENO, &rfds)) { ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); if (n <= 0) { close(child_stdin); in_open = 0; } else { write(child_stdin, buf, n); } } /* child stdout */ if (out_open && FD_ISSET(child_stdout, &rfds)) { ssize_t n = read(child_stdout, buf, sizeof(buf)); if (n <= 0) { close(child_stdout); out_open = 0; } else { /* placeholder for ASL stdout artifact */ write(STDOUT_FILENO, buf, n); } } /* child stderr */ if (err_open && FD_ISSET(child_stderr, &rfds)) { ssize_t n = read(child_stderr, buf, sizeof(buf)); if (n <= 0) { close(child_stderr); err_open = 0; } else { /* placeholder for ASL stderr artifact */ write(STDERR_FILENO, buf, n); } } } } /* ------------------------------------------------------------------------- */ /* PTY mode implementation */ /* ------------------------------------------------------------------------- */ #ifdef ASL_ENABLE_PTY static pid_t spawn_pty( char **argv, int *pty_master_fd ) { int master_fd; pid_t pid = forkpty(&master_fd, NULL, NULL, NULL); if (pid < 0) return -1; if (pid == 0) { execvp(argv[0], argv); perror("execvp"); _exit(127); } set_nonblocking(master_fd); *pty_master_fd = master_fd; return pid; } static void pump_pty(int pty_master) { char buf[8192]; int open = 1; while (open) { fd_set rfds; FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); FD_SET(pty_master, &rfds); int maxfd = pty_master; if (select(maxfd + 1, &rfds, NULL, NULL, NULL) < 0) { if (errno == EINTR) continue; break; } /* stdin -> PTY */ if (FD_ISSET(STDIN_FILENO, &rfds)) { ssize_t n = read(STDIN_FILENO, buf, sizeof(buf)); if (n > 0) { write(pty_master, buf, n); } } /* PTY -> stdout (combined stream) */ if (FD_ISSET(pty_master, &rfds)) { ssize_t n = read(pty_master, buf, sizeof(buf)); if (n <= 0) { close(pty_master); open = 0; } else { /* placeholder for ASL combined output artifact */ write(STDOUT_FILENO, buf, n); } } } } #endif /* ASL_ENABLE_PTY */ /* ------------------------------------------------------------------------- */ /* Public entry point */ /* ------------------------------------------------------------------------- */ int asl_capture_run( asl_capture_mode_t mode, char **argv, asl_capture_result_t *result ) { pid_t pid; int status; if (!argv || !argv[0] || !result) return -1; if (mode == ASL_CAPTURE_PTY) { #ifndef ASL_ENABLE_PTY fprintf(stderr, "asl-capture: PTY support not enabled at build time\n"); return -1; #else int pty_master; pid = spawn_pty(argv, &pty_master); if (pid < 0) return -1; pump_pty(pty_master); #endif } else { int in_fd, out_fd, err_fd; pid = spawn_pipe(argv, &in_fd, &out_fd, &err_fd); if (pid < 0) return -1; pump_pipe(in_fd, out_fd, err_fd); } waitpid(pid, &status, 0); if (WIFEXITED(status)) { result->exit_code = WEXITSTATUS(status); result->term_signal = 0; } else if (WIFSIGNALED(status)) { result->exit_code = 128; result->term_signal = WTERMSIG(status); } else { result->exit_code = 128; result->term_signal = 0; } return 0; }