/* $Id: ttyd.C,v 1.15 2002/11/01 20:47:38 max Exp $ */ /* * * Copyright (C) 2001 Eric Peterson (ericp@lcs.mit.edu) * Copyright (C) 2001 Michael Kaminsky (kaminsky@lcs.mit.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "sfsmisc.h" #include "crypt.h" #include "aios.h" #include "rex_prot.h" #include "sfstty.h" ptr paios; /* from openssh */ /* Changes the window size associated with the pty. */ static void pty_change_window_size(int ptyfd, int row, int col, int xpixel, int ypixel) { struct winsize w; w.ws_row = row; w.ws_col = col; w.ws_xpixel = xpixel; w.ws_ypixel = ypixel; (void) ioctl(ptyfd, TIOCSWINSZ, &w); } void windowresize (int mfd, const str data, int err) { static int x = 0; static int a[4]; if (!data.len ()) return; //todo: use something better than atoi a[x++] = atoi(data.cstr()); if (x == 4) { //do resize pty_change_window_size(mfd, a[0], a[1], a[2], a[3]); //warn ("changing window size to col:%d row:%d xpix:%d ypix:%d\n", // a[1], a[0], a[2], a[3]); x = 0; } paios->readline (wrap (windowresize, mfd)); } static void slaveexit (int status) { /* Linux has problems flushing data, so sleep for a tiny bit of time. */ #ifdef __linux__ struct timeval t; t.tv_sec = 0; t.tv_usec = 100; select (0, NULL, NULL, NULL, &t); #endif /* __linux */ exit (WIFEXITED (status) ? WEXITSTATUS (status) : -WTERMSIG (status)); } static void postforkcb (str path) { setsid (); int ttyfd = open (path, O_RDWR); if (ttyfd < 0) fatal << "could not open slave tty\n"; dup2 (ttyfd, 0); dup2 (ttyfd, 1); dup2 (ttyfd, 2); if (ttyfd > 2) close (ttyfd); #ifdef TIOCSCTTY //make us controlling terminal ioctl (0, TIOCSCTTY); #endif /* TIOCSCTTY */ //todo: set terminal modes here } int main (int argc, char **argv) { setprogname (argv[0]); sfsconst_init (); warn ("ttyd called for utmp host: %s\n", argv[1]); /* Set up the command that we want to run. */ str command; if (!strcmp (argv[2], ".")) { char *default_shell = getenv ("SHELL"); if (default_shell) command = default_shell; else { warn ("SHELL not set, reverting to sh\n"); command = "sh"; } } else command = argv[2]; command = find_program (command); if (!command) fatal << "Could not locate program: " << argv[2] << "\n"; char *name = strdup (strrchr (command.cstr (), '/')); assert (name); /* If the shell is interactive, make it a login shell */ if (argc == 3) *name = '-'; else name++; /* Set up tty/pty. */ int fd = suidgetfd_required ("ptyd"); if (fd < 0) fatal << "connection to ptyd failed\n"; ref ux = axprt_unix::alloc (fd); ref ac = aclnt::alloc (ux, ptyd_prog_1); utmphost host = argv[1]; pty_alloc_res res; if (ac->scall (PTYD_PTY_ALLOC, &host, &res) || res.err) { fatal << "could not allocate pty via ptyd\n"; } ttypath path = *res.path; /* path to slave side of pseudo-tty */ int mfd = ux->recvfd (); /* fd connect to master side of pseudo-tty */ if (mfd < 0) fatal << "could not receive master fd from ptyd\n"; close_on_exec (mfd); close_on_exec (fd); /* Note that on *BSD, many things cannot be done to the pty file * descriptor until the tty has been opened. For example, the pty * cannot even be put into non-blocking mode. We therefore want to * make sure the shell has been executed before passing the pty file * descriptor back to proxy. */ argv[2] = name; pid_t pid = spawn (command, &argv[2], 0, 1, 2, wrap (postforkcb, path)); if (pid == -1) fatal << "could not fork\n"; if (writefd (0, "", 1, mfd) < 0) fatal << "could not pass master fd to proxy\n"; //keep mfd around for window resizing paios = aios::alloc (0); paios->readline (wrap (windowresize, mfd)); chldcb (pid, wrap (slaveexit)); #if 0 //is this necessary, will ptyd clean then up when master/slave fd closes ?? //this has to be written async and put in slaveexit if it's necessary int err; if(ac->scall (PTYD_PTY_FREE, &path, &err) || err) warn("failed to free pty via ptyd\n"); #endif amain (); }