| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "pty_fork.h" |
| #include "error.h" |
| |
| #include <unistd.h> |
| #include <termios.h> |
| #include <stdarg.h> |
| #ifdef SOLARIS |
| #include <stropts.h> |
| #else |
| #include <sys/ioctl.h> |
| #endif |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <signal.h> |
| |
| #ifdef __CYGWIN__ |
| //added for compatibility with cygwin 1.5 |
| |
| int posix_openpt(int flags) { |
| return open("/dev/ptmx", flags); |
| } |
| |
| #endif |
| extern int grantpt(int); |
| extern int unlockpt(int); |
| extern char *ptsname(int); |
| |
| static void dup_fd(int pty_fd); |
| static int ptm_open(void); |
| static int pts_open(int masterfd); |
| |
| int ptm_open(void) { |
| int masterfd; |
| |
| /* |
| * O_NOCTTY (?) we'll be a group leader in any case (?) |
| * So will get a controlling terminal.. O_NOCTTY - do we need it? |
| * |
| */ |
| if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) == -1) { |
| return -1; |
| } |
| |
| if (grantpt(masterfd) == -1 || unlockpt(masterfd) == -1) { |
| close(masterfd); |
| return -1; |
| } |
| |
| return masterfd; |
| } |
| |
| int pts_open(int masterfd) { |
| int slavefd; |
| char* name; |
| |
| if ((name = ptsname(masterfd)) == NULL) { |
| close(masterfd); |
| return -1; |
| } |
| |
| if ((slavefd = open(name, O_RDWR)) == -1) { |
| close(masterfd); |
| return -1; |
| } |
| |
| #if defined (__SVR4) && defined (__sun) |
| if (ioctl(slavefd, I_PUSH, "ptem") == -1) { |
| close(masterfd); |
| close(slavefd); |
| return -1; |
| } |
| |
| if (ioctl(slavefd, I_PUSH, "ldterm") == -1) { |
| close(masterfd); |
| close(slavefd); |
| return -1; |
| } |
| |
| if (ioctl(slavefd, I_PUSH, "ttcompat") == -1) { |
| close(masterfd); |
| close(slavefd); |
| return -1; |
| } |
| #endif |
| |
| return slavefd; |
| } |
| |
| pid_t pty_fork(int *ptrfdm) { |
| pid_t pid; |
| char* name; |
| int master_fd, pty_fd; |
| struct termios termios; |
| struct termios* ptermios = NULL; |
| struct winsize wsize; |
| struct winsize* pwinsize = NULL; |
| |
| // If we are in a terminal - get it's params and set them to |
| // a newly allocated one... |
| |
| if (isatty(STDIN_FILENO)) { |
| ptermios = &termios; |
| if (tcgetattr(STDIN_FILENO, ptermios) == -1) { |
| err_sys("tcgetattr failed"); |
| } |
| |
| pwinsize = &wsize; |
| if (ioctl(STDIN_FILENO, TIOCGWINSZ, pwinsize) == -1) { |
| err_sys("ioctl(TIOCGWINSZ) failed"); |
| } |
| } |
| |
| if ((master_fd = ptm_open()) < 0) { |
| err_sys("ERROR: ptm_open() failed [%d]\n", master_fd); |
| } |
| |
| if ((name = ptsname(master_fd)) == NULL) { |
| close(master_fd); |
| return -1; |
| } |
| |
| // Put values to the output params |
| *ptrfdm = master_fd; |
| |
| if ((pid = fork()) < 0) { |
| err_sys("fork failed"); |
| return (-1); |
| } |
| |
| if (pid == 0) { /* child */ |
| if (setsid() < 0) { |
| err_sys("setsid error"); |
| } |
| |
| if ((pty_fd = pts_open(master_fd)) < 0) { |
| err_sys("can't open slave pty"); |
| } |
| |
| if (ptermios != NULL) { |
| if (tcsetattr(pty_fd, TCSANOW, ptermios) == -1) { |
| err_sys("tcsetattr(TCSANOW) failed"); |
| } |
| } |
| |
| if (pwinsize != NULL) { |
| if (ioctl(pty_fd, TIOCSWINSZ, pwinsize) == -1) { |
| err_sys("ioctl(TIOCSWINSZ) failed"); |
| } |
| } |
| |
| close(master_fd); |
| dup_fd(pty_fd); |
| |
| return (0); /* child returns 0 just like fork() */ |
| } else { /* parent */ |
| return (pid); /* parent returns pid of child */ |
| } |
| |
| } |
| |
| pid_t pty_fork1(const char *pty) { |
| pid_t pid; |
| int pty_fd; |
| |
| if ((pid = fork()) < 0) { |
| printf("FAILED"); |
| return (-1); |
| } |
| |
| if (pid == 0) { /* child */ |
| /* |
| * Create a new process session for this child. |
| */ |
| if (setsid() < 0) { |
| err_sys("setsid error"); |
| } |
| |
| /* |
| * Open a terminal descriptor... |
| */ |
| if ((pty_fd = open(pty, O_RDWR)) == -1) { |
| err_sys("ERROR cannot open pty \"%s\" -- %s\n", |
| pty, strerror(errno)); |
| } |
| |
| /* |
| * Associate pty_fd with I/O and close it |
| */ |
| dup_fd(pty_fd); |
| return (0); |
| } else { |
| /* |
| * parent just returns a pid of the child |
| */ |
| return (pid); |
| } |
| } |
| |
| static void dup_fd(int pty_fd) { |
| // Ensure SIGINT isn't being ignored |
| struct sigaction act; |
| sigaction(SIGINT, NULL, &act); |
| act.sa_handler = SIG_DFL; |
| sigaction(SIGINT, &act, NULL); |
| |
| |
| #if defined(TIOCSCTTY) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__APPLE__) |
| // If this terminal is already the controlling |
| // terminal of a different session group then the |
| // ioctl fails with EPERM, unless the caller is root |
| if (ioctl(pty_fd, TIOCSCTTY, 0) == -1) { |
| printf("ERROR ioctl(TIOCSCTTY) failed on \"pty %d\" -- %s\n", |
| pty_fd, strerror(errno)); |
| exit(-1); |
| } |
| #endif |
| |
| /* |
| * Slave becomes stdin/stdout/stderr of child. |
| */ |
| if (dup2(pty_fd, STDIN_FILENO) != STDIN_FILENO) { |
| err_sys("dup2 error to stdin"); |
| } |
| |
| if (dup2(pty_fd, STDOUT_FILENO) != STDOUT_FILENO) { |
| err_sys("dup2 error to stdout"); |
| } |
| |
| if (dup2(pty_fd, STDERR_FILENO) != STDERR_FILENO) { |
| err_sys("dup2 error to stderr"); |
| } |
| |
| close(pty_fd); |
| } |