/****** uti/pty/fork_pty() ***************************************************** * NAME * fork_pty() -- Opens a pty, forks and redirects the std handles * * SYNOPSIS * pid_t fork_pty(int *ptrfdm, int *fd_pipe_err, dstring *err_msg) * * FUNCTION * Opens a pty, forks and redirects stdin, stdout and stderr of the child * to the pty. * * INPUTS * int *ptrfdm - Receives the file descriptor of the master side of * the pty. * int *fd_pipe_err - A int[2] array that receives the file descriptors * of a pipe to separately redirect stderr. * To achieve the same behaviour like rlogin/rsh, this * is normally disabled, compile with * -DUSE_PTY_AND_PIPE_ERR to enable this feature. * dstring *err_msg - Receives an error string in case of error. * * RESULT * pid_t - -1 in case of error, * 0 in the child process, * or the pid of the child process in the parent process. * * NOTES * MT-NOTE: fork_pty() is not MT safe * * SEE ALSO * pty/fork_no_pty *******************************************************************************/ pid_t fork_pty(int *ptrfdm, int *fd_pipe_err, dstring *err_msg) { pid_t pid; int fdm, fds; char pts_name[20]; int old_euid; /* * We run this either as root with euid="sge admin user" or as an unprivileged * user. If we are root with euid="sge admin user", we must change our * euid back to root for this function. */ old_euid = geteuid(); if (getuid() == SGE_SUPERUSER_UID) { seteuid(SGE_SUPERUSER_UID); } if ((fdm = ptym_open(pts_name)) < 0) { sge_dstring_sprintf(err_msg, "can't open master pty \"%s\": %d, %s", pts_name, errno, strerror(errno)); return -1; } #if defined(USE_PTY_AND_PIPE_ERR) if (pipe(fd_pipe_err) == -1) { sge_dstring_sprintf(err_msg, "can't create pipe for stderr: %d, %s", errno, strerror(errno)); return -1; } #endif if ((pid = fork()) < 0) { return -1; } else if (pid == 0) { /* child */ if ((g_newpgrp = setsid()) < 0) { sge_dstring_sprintf(err_msg, "setsid() error: %d, %s", errno, strerror(errno)); return -1; } /* Open pty slave */ if ((fds = ptys_open(fdm, pts_name)) < 0) { seteuid(old_euid); sge_dstring_sprintf(err_msg, "can't open slave pty: %d", fds); return -1; } seteuid(old_euid); close(fdm); fdm = -1; /* all done with master in child */ #if defined(TIOCSCTTY) && !defined(CIBAUD) /* 44BSD way to acquire controlling terminal */ /* !CIBAUD to avoid doing this under SunOS */ if (ioctl(fds, TIOCSCTTY, (char *) 0) < 0) { sge_dstring_sprintf(err_msg, "TIOCSCTTY error: %d, %s", errno, strerror(errno)); return -1; } #endif /* slave becomes stdin/stdout/stderr of child */ if ((dup2(fds, STDIN_FILENO)) != STDIN_FILENO) { sge_dstring_sprintf(err_msg, "dup2 to stdin error: %d, %s", errno, strerror(errno)); return -1; } if ((dup2(fds, STDOUT_FILENO)) != STDOUT_FILENO) { sge_dstring_sprintf(err_msg, "dup2 to stdout error: %d, %s", errno, strerror(errno)); return -1; } #if defined(USE_PTY_AND_PIPE_ERR) close(fd_pipe_err[0]); fd_pipe_err[0] = -1; if ((dup2(fd_pipe_err[1], STDERR_FILENO)) != STDERR_FILENO) { sge_dstring_sprintf(err_msg, "dup2 to stderr error: %d, %s", errno, strerror(errno)); return -1; } close(fd_pipe_err[1]); fd_pipe_err[1] = -1; #else if ((dup2(fds, STDERR_FILENO)) != STDERR_FILENO) { sge_dstring_sprintf(err_msg, "dup2 to stderr error: %d, %s", errno, strerror(errno)); return -1; } #endif if (fds > STDERR_FILENO) { close(fds); fds = -1; } return 0; /* child returns 0 just like fork() */ } else { /* parent */ *ptrfdm = fdm; /* return fd of master */ close(fd_pipe_err[1]); fd_pipe_err[1] = -1; seteuid(old_euid); return pid; /* parent returns pid of child */ } }
pid_t pty_fork(int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize) { int fdm, fds; pid_t pid; char pts_name[20]; if ((fdm = ptym_open(pts_name, sizeof(pts_name))) < 0) err_sys("can't open master pty: %s, error %d", pts_name, fdm); if (slave_name != NULL) { /* * Return name of slave. Null terminate to handle case * where strlen(pts_name) > slave_namesz. */ strncpy(slave_name, pts_name, slave_namesz); slave_name[slave_namesz - 1] = '\0'; } if ((pid = fork()) < 0) { return -1; } else if (pid == 0) { /* child */ if (setsid() < 0) err_sys("setsid error"); /* * System V acquires controlling terminal on open(). */ if ((fds = ptys_open(pts_name)) < 0) err_sys("can't open slave pty"); close(fdm); /* all done with master in child */ #if defined(TIOCSCTTY) /* * TIOCSCTTY is the BSD way to acquire a controlling terminal. */ if (ioctl(fds, TIOCSCTTY, (char *)0) < 0) err_sys("TIOCSCTTY error"); #endif /* * Set slave's termios and window size. */ if (slave_termios != NULL) { if (tcsetattr(fds, TCSANOW, slave_termios) < 0) err_sys("tcsetattr error on slave pty"); } if (slave_winsize != NULL) { if (ioctl(fds, TIOCSWINSZ, slave_winsize) < 0) err_sys("TIOCSWINSZ error on slave pty"); } /* * Slave becomes stdin/stdout/stderr of child. */ if (dup2(fds, STDIN_FILENO) != STDIN_FILENO) err_sys("dup2 error to stdin"); if (dup2(fds, STDOUT_FILENO) != STDOUT_FILENO) err_sys("dup2 error to stdout"); if (dup2(fds, STDERR_FILENO) != STDERR_FILENO) err_sys("dup2 error to stderr"); if (fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO) close(fds); return 0; /* child return 0 just like fork() */ } else { /* parent */ *ptrfdm = fdm; /* return fd of master */ return pid; /* parent returns pid of child */ } }
pid_t exec_pty(const char *path, char *const argv[], char *const envp[], const char *dirpath, int channels[3], const char *pts_name, int fdm) { int pipe2[2]; pid_t childpid; char *full_path; /* * We use pfind() to check that the program exists and is an executable. * If not pass the error up. Also execve() wants a full path. */ full_path = pfind(path); if (full_path == NULL) { fprintf(stderr, "Unable to find full path for \"%s\"\n", (path) ? path : ""); return -1; } /* * Make sure we can create our pipes before forking. */ if (channels != NULL) { if (pipe(pipe2) < 0) { fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno)); free(full_path); return -1; } } childpid = fork(); if (childpid < 0) { fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno)); free(full_path); return -1; } else if (childpid == 0) { /* child */ chdir(dirpath); if (channels != NULL) { int fds; fds = ptys_open(fdm, pts_name); if (fds < 0) { fprintf(stderr, "%s(%d): returning due to error: %s\n", __FUNCTION__, __LINE__, strerror(errno)); return -1; } /* Close the read end of pipe2 */ if (close(pipe2[0]) == -1) perror("close(pipe2[0]))"); /* close the master, no need in the child */ close(fdm); set_noecho(fds); /* redirections */ dup2(fds, STDIN_FILENO); /* dup stdin */ dup2(fds, STDOUT_FILENO); /* dup stdout */ dup2(pipe2[1], STDERR_FILENO); /* dup stderr */ close(fds); /* done with fds. */ } /* Close all the fd's in the child */ { int fdlimit = sysconf(_SC_OPEN_MAX); int fd = 3; while (fd < fdlimit) close(fd++); } if (envp[0] == NULL) { execv(full_path, argv); } else { execve(full_path, argv, envp); } _exit(127); } else if (childpid != 0) { /* parent */ set_noecho(fdm); if (channels != NULL) { /* close the write end of pipe1 */ if (close(pipe2[1]) == -1) perror("close(pipe2[1])"); channels[0] = fdm; /* Input Stream. */ channels[1] = fdm; /* Output Stream. */ channels[2] = pipe2[0]; /* stderr Stream. */ /*channels[2] = fdm; Input Stream. */ } free(full_path); return childpid; } free(full_path); return -1; /*NOT REACHED */ }
pid_t pty_fork2(int *ptrfdm, char *slave_name) { int fdm, fds; pid_t pid; char pts_name[20]; if ( (fdm = ptym_open(pts_name)) < 0) { msg("can't open master pty"); return ERRO; } strcpy(slave_name, pts_name); /* return name of slave */ /*/printf("\npty_fork2: %d %s", fdm, pts_name); */ if ( (pid = fork()) < 0) { msg("Can not fork"); return ERRO; } else if (pid == 0) { /* child */ /*/ sleep(30); */ if (setsid() < 0) { msg("setsid error"); exit(1); } /* SVR4 acquires controlling terminal on open() */ if ( (fds = ptys_open(fdm, pts_name)) < 0) { msg("can't open slave pty"); exit(1); } close(fdm); /* all done with master in child */ #ifdef LINUX /* 44BSD way to acquire controlling terminal */ if (ioctl(fds, TIOCSCTTY, (char *) 0) < 0) { msg("TIOCSCTTY error"); return ERRO; } #endif if (tty_copy(fds) == ERRO) exit(1); /* slave becomes stdin/stdout/stderr of child */ if (dup2(fds, STDOUT_FILENO) != STDOUT_FILENO || dup2(fds, STDERR_FILENO) != STDERR_FILENO || dup2(fds, STDIN_FILENO) != STDIN_FILENO ) { msg("dup2 error to slave"); exit(1); } if (fds > STDERR_FILENO) close(fds); return 0; /* child returns 0 just like fork() */ } else { /* parent */ *ptrfdm = fdm; /* return fd of master */ return(pid); /* parent returns pid of child */ } }