bool XSERDPort::open_pty(void) { // Talk to a process via a pty char slave[128]; int slavefd; protocol = pty; if (!pty_allocate(&fd, &slavefd, slave, sizeof(slave))) return false; fflush(stdout); fflush(stderr); switch (pid = fork()) { case -1: // error return false; break; case 0: // child ::close(fd); /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&slavefd, slave); ::close(0); dup(slavefd); // Use the slave fd for stdin, ::close(1); dup(slavefd); // stdout, ::close(2); dup(slavefd); // and stderr. // <should we be more paranoid about closing unused fds?> // <should we drop privileges if running setuid?> // Let the shell do the dirty work execlp("/bin/sh", "/bin/sh", "-c", ++device_name, (char *)NULL); // exec failed! printf("serial_open: could not exec %s: %s\n", "/bin/sh", strerror(errno)); exit(1); break; default: // parent // Pid was stored above break; } return true; }
/* Execute a command or shell within a pty environment, and set up * redirection as appropriate. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { pid_t pid; struct logininfo *li; TRACE(("enter ptycommand")); /* we already have a pty allocated */ assert(chansess->master != -1 && chansess->tty != NULL); pid = fork(); if (pid < 0) return DROPBEAR_FAILURE; if (pid == 0) { /* child */ /* redirect stdin/stdout/stderr */ close(chansess->master); pty_make_controlling_tty(&chansess->slave, chansess->tty); if ((dup2(chansess->slave, STDIN_FILENO) < 0) || (dup2(chansess->slave, STDERR_FILENO) < 0) || (dup2(chansess->slave, STDOUT_FILENO) < 0)) { TRACE(("leave ptycommand: error redirecting filedesc")); return DROPBEAR_FAILURE; } close(chansess->slave); /* write the utmp/wtmp login record - must be after changing the * terminal used for stdout with the dup2 above */ li= login_alloc_entry(getpid(), ses.authstate.username, ses.hostname, chansess->tty); login_login(li); login_free_entry(li); m_free(chansess->tty); execchild(chansess); /* not reached */ } else { /* parent */ TRACE(("continue ptycommand: parent")); chansess->pid = pid; /* add a child pid */ addchildpid(chansess, pid); close(chansess->slave); channel->infd = chansess->master; channel->outfd = chansess->master; /* don't need to set stderr here */ ses.maxfd = MAX(ses.maxfd, chansess->master); if (fcntl(chansess->master, F_SETFL, O_NONBLOCK) < 0) { dropbear_exit("Couldn't set nonblocking"); } } TRACE(("leave ptycommand")); return DROPBEAR_SUCCESS; }
/* pty_fork: Creates a pseudo terminal and then calls fork. In the parent * process, the slave side of the pseudo terminal is closed. In the child * process, the master side of the pseudo terminal is closed and the slave * side is made the controlling terminal. It is duplicated onto standard * input, output and error and then closed. The master side of the pseudo * terminal is stored in masterfd for the parent process. The device name * of the slave side of the pseudo terminal is stored in the buffer pointed * to by slavename which must be able to hold at least 64 bytes. * slavenamesize is the size of the buffer pointed to by slavename. No * more than slavenamesize bytes will be written to slavename, including * the terminating nul byte. If slave_termios is not null, it is passed to * tcsetattr with the command TCSANOW to set the terminal attributes of the * slave device. If slave_winsize is not null, it is passed to ioctl with * the command TIOCSWINSZ to set the window size of the slave device. * On success, returns 0 to the child process and returns the process * id of the child process to the parent process. On error, returns -1 with * errno set appropriately. */ pid_t pty_fork(int *masterfd, char *slavename, size_t slavenamesize, const struct termios * slave_termios, const struct winsize * slave_winsize) { int slavefd = 0; pid_t pid = 0; /* ** Note: we don't use forkpty() because it closes the master in the ** child process before making the slave the controlling terminal of the ** child proces and this can prevent the slave from becoming the ** controlling terminal (but I have no idea why). */ if (pty_open(masterfd, &slavefd, slavename, slavenamesize, slave_termios, slave_winsize) == -1) return -1; switch (pid = fork()) { case -1: pty_release(slavename); close(slavefd); close(*masterfd); return -1; case 0: { /* Make the slave our controlling tty */ if (pty_make_controlling_tty(&slavefd, slavename) == -1) _exit(EXIT_FAILURE); /* Redirect stdin, stdout and stderr from the pseudo tty */ if (slavefd != STDIN_FILENO && dup2(slavefd, STDIN_FILENO) == -1) _exit(EXIT_FAILURE); if (slavefd != STDOUT_FILENO && dup2(slavefd, STDOUT_FILENO) == -1) _exit(EXIT_FAILURE); if (slavefd != STDERR_FILENO && dup2(slavefd, STDERR_FILENO) == -1) _exit(EXIT_FAILURE); /* Close the extra descriptor for the pseudo tty */ if (slavefd != STDIN_FILENO && slavefd != STDOUT_FILENO && slavefd != STDERR_FILENO) close(slavefd); /* Close the master side of the pseudo tty in the child */ close(*masterfd); return 0; } default: { /* Close the slave side of the pseudo tty in the parent */ close(slavefd); return pid; } } }