/* * gcc -o wrap_cmd wrap_cmd.c -Wall -lpthread */ #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int grantpt_safe(int ptm) { int ret, errno_backup; void (*handler)(int); handler = signal(SIGCHLD, SIG_DFL); ret = grantpt(ptm); errno_backup = errno; if (handler != SIG_ERR) signal(SIGCHLD, handler); errno = errno_backup; return ret; } int fork_over_pty(int *ptm_fd) { int cid = -1; #if 0 /* defined(__linux__) */ /* libutil : linux, BSD, MacOS ... */ cid = forkpty(ptm_fd, NULL, NULL, NULL); #else /* POSIX */ int ret, fdptm, fdpts; struct termios child_term; *ptm_fd = -1; fdptm = posix_openpt(O_RDWR | O_NOCTTY); if (fdptm < 0) return -1; ret = grantpt_safe(fdptm); if (ret < 0) { close(fdptm); return -1; } ret = unlockpt(fdptm); if (ret < 0) { close(fdptm); return -1; } fdpts = open(ptsname(fdptm), O_RDWR); if (ret < 0) { close(fdptm); return -1; } ret = tcgetattr(fdpts, &child_term); if (ret == 0) { child_term.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); child_term.c_oflag &= ~OPOST; child_term.c_lflag &= ~(ECHONL | ISIG | IEXTEN); child_term.c_lflag |= ECHO; tcsetattr(fdpts, TCSANOW, &child_term); } *ptm_fd = fdptm; cid = fork(); switch (cid) { case -1: *ptm_fd = -1; close(fdptm); close(fdpts); break; case 0: /* child */ close(fdptm); close(0); close(1); close(2); dup(fdpts); dup(fdpts); dup(fdpts); close(fdpts); setsid(); ioctl(0, TIOCSCTTY, 1); /* controltty */ break; default: /* parent */ close(fdpts); break; } #endif return cid; } static void thread_died(void *arg) { free(arg); printf("(thread died)\n"); } static void parent_died(void) { printf("(parent died)\n"); } static void child_died(void) { printf("(child died)\n"); } static int __poll_rd_timeout(int fd, unsigned int ms) { struct pollfd fds; int ret; do { fds.fd = fd; fds.events = POLLIN; fds.revents = 0; ret = poll(&fds, 1, ms); if (ret <= 0) break; if (fds.revents & POLLIN) break; } while (1); return ret; } static int __read_timeout(int fd, unsigned int ms, char *buf, size_t sz) { int ret = -1; while ((sz > 1) && (ret = __poll_rd_timeout(fd, ms)) == 1) { char c; if ((ret = read(fd, &c, 1)) < 0) return ret; if (ret == 0) continue; *buf++ = c; sz--; if (c == '\n') break; } *buf = '\0'; return ret; } static int scanf_from_child(int fd, char *buf, size_t sz, const char *fmt, ...) { int ret = __read_timeout(fd, 10, buf, sz); if (ret < 0) return ret; va_list ap; va_start(ap, fmt); ret = vsscanf(buf, fmt, ap); va_end(ap); return ret; } /* receive from child */ static void *recv_from_child(void *arg) { const int ptm_fd = *(const int*)arg; FILE *ptm_fp = fdopen(ptm_fd, "w"); char *buf = malloc(256); int ret; pthread_cleanup_push(thread_died, buf); do { int child_int; ret = scanf_from_child(ptm_fd, buf, 256, "%d", &child_int); if (ret < 0) { perror(__func__); break; } if (ret == 1) fprintf(ptm_fp, "%d\n", child_int); else { fprintf(stderr, "child sent '%s'\n", buf); break; } } while (1); pthread_cleanup_pop(1); return (void*)NULL; } static int parent(const int *ptm_fd, const pid_t cid) { int cstatus, ret; pid_t wid; pthread_t tid; atexit(parent_died); ret = pthread_create(&tid, NULL, recv_from_child, (void*)ptm_fd); if (ret) { fprintf(stderr, "pthread_create: %s\n", strerror(ret)); return -ret; } pthread_detach(tid); wid = waitpid(cid, &cstatus, 0); if (wid < 0) { perror("wait(cid)"); } pthread_cancel(tid); /* FIXME */ return cstatus; } #include static void child(const int ptm_fd) { char **envp; char *argv[] = { "./A", NULL,}; int ret; int nr_envp; /* won't be called except exceptions */ atexit(child_died); setbuf(stdout, NULL); setbuf(stderr, NULL); { /* FIXME */ extern char **environ; for (nr_envp = 0; environ[nr_envp] != NULL; nr_envp++) do {} while (0); envp = (char**)calloc(nr_envp + 1, sizeof(char *)); for (nr_envp = 0; environ[nr_envp] != NULL; nr_envp++) envp[nr_envp] = environ[nr_envp]; envp[nr_envp++] = "LANG=C"; envp[nr_envp] = NULL; } ret = execve(argv[0], argv, envp); /* never reached except exceptions */ perror("exec*"); exit(ret); } int main(int argc, char **argv) { int ret = 0; int ptm_fd = -1; int cid; cid = fork_over_pty(&ptm_fd); switch (cid) { case -1: perror("fork_over_pty()"); return -3; case 0: child(ptm_fd); break; default: ret = parent(&ptm_fd, cid); break; } close(ptm_fd); return -ret; }