function ssh_open($remote, $password) { global $php_version; global $debug; // Open a child process with the 'proc_open' function. // // Some tricks: we must open the connection using '-x' to disable // X11 forwarding, and use '-t -t' to avoid SSH generating an error // because we are not connected to any terminal. // NOTE: // We require users to have an account and password on // the UI and provide their user/password through the web or // otherwise (e.g. using myproxy) // // NOTE: if the web server is trusted remotely (i.e. it's SSH public // key is accepted in ~user@host:.ssh/authorized_keys) then any // password will do. if ($php_version < 5) { // Prepare I/O $descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w")); // prepare password putenv("DISPLAY=none:0."); putenv("SSH_ASKPASS={$tmpfname}"); umask(077); $tmpfname = tempnam("/tmp", "egTinker"); chmod($tmpfname, 0700); $fp = fopen($tmpfname, "w"); fputs($fp, "#!/bin/sh\necho {$password}\n"); fputs($fp, "rm {$tmpfname}\n"); fclose($fp); $process = proc_open("ssh -x -t -t {$remote}", $descriptorspec, $pipes); if ($debug) { echo "ssh -x -t -t {$remote}<br />\n"; } // check status if (!is_resource($process)) { letal("SSH::connect", "cannot connect to the remote host"); return; } if ($debug) { echo "proc_open<br />\n"; } } else { /* php5 -- we can use PTYs */ /* XXX -- untested -- probably unneeded */ $descriptorspec = array(0 => array("pty"), 1 => array("pty"), 2 => array("pty")); // prepare password putenv("DISPLAY=none:0."); putenv("SSH_ASKPASS={$tmpfname}"); umask(077); $tmpfname = tempnam("/tmp", "egTinker"); chmod($tmpfname, 0700); $fp = fopen($tmpfname, "w"); fputs($fp, "#!/bin/sh\necho {$password}\n"); fputs($fp, "rm {$tmpfname}\n"); fclose($fp); $process = proc_open("ssh -x -t -t {$remote}", $descriptorspec, $pipes); // check status if (!is_resource($process)) { letal("SSH::connect", "cannot connect to the remote host"); return; } $status = proc_get_status($process); if ($status->running == FALSE) { fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); letal("SSH::connect", "connection exited " . $status->exitcode); return; } if ($status->signaled) { fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); letal("SSH::connect", "connection terminated by " . $status->termsig); return; } if ($status->stopped) { // Tell the user and hope for the best warning("SSH::connect stopped by " . $status->stopsig . " it may still have a chance though"); } } // $pipes now looks like this: // 0 => writeable handle connected to child stdin // 1 => readable handle connected to child stdout // We now have a connection to the remote Grid User Interface // Server which we may use to send commands/receive output return array('stdin' => $pipes[0], 'stdout' => $pipes[1], 'stderr' => $pipes[2]); }
/** * Open an SSH connection to run an interactive command on a remote * site * * Connects to a remote host and runs an interactive command * with NO controlling terminal. * * This routine creates communication streams with the remote shell, * and stores all output (standard and error) of the connection into * two separate local log files (one for stdout and one for stderr). * * Returns a process_control array which contains the process resource * ID and an the standard file descriptors which the caller may use to * interact with the remote shell. * * The process control array contains: * * 'process' -- the process resource for the newly created connection * * 'std_in' -- handle to the standard input of the new connection * * 'std_out' -- handle to standard output of the new connection * * 'std_err' -- handle to standard error of the new connection * * 'stdout_file' -- actual filename of the local log file for the * new connection standard output * * 'stderr_file' -- actual filename of the local log file for the * new connection standard error * * @param string command to be executed interactively on the remote end * * @return mixed|false a process control associative array or FALSE * on failure. * * @access public * @since Method available since Release 1.0 */ function ssh_open_command($command) { global $debug_sexec; // Open a child process with the 'proc_open' function. // // Some tricks: we must open the connection using '-x' to disable // X11 forwarding, and use '-t -t' to avoid SSH generating an error // because we are not connected to any terminal. // // NOTE: if the web server is trusted remotely (i.e. it's SSH public // key is accepted in ~user@host:.ssh/authorized_keys) then any // password will do. // Prepare I/O umask(077); if ($debug_sexec) { $child_stdout = tempnam($this->workdir, "open_cmd-" . getmypid() . "-1-"); $child_stderr = tempnam($this->workdir, "open_cmd-" . getmypid() . "-2-"); } else { $child_stdout = tempnam($this->workdir, "open_cmd-"); $child_stderr = tempnam($this->workdir, "open_cmd-"); } $descriptorspec = array(0 => array("pipe", "r"), 1 => array("file", $child_stdout, "a"), 2 => array("file", $child_stderr, "a")); if ($debug_sexec) { echo "{$this->ssh} -x -t -t -S {$this->mplex_socket} {$this->remote} {$command}<br />\n"; } $process = proc_open("{$this->ssh} -x -t -t -S {$this->mplex_socket} {$this->remote} \"{$command}\"", $descriptorspec, $pipes); // check status if (!is_resource($process) || $process == FALSE) { letal("SSH::connect", "cannot connect to the remote host"); return FALSE; } if ($debug_sexec) { echo "proc_open done<br />\n"; } // $pipes now looks like this: // 0 => writeable handle connected to child stdin // Open child's stdin and stdout $pipes[1] = fopen($child_stdout, "r"); $pipes[2] = fopen($child_stderr, "r"); // Should we leave this to the user? // set to non-blocking and avoid having to call fflush #stream_set_blocking($pipes[0], FALSE); #stream_set_blocking($pipes[1], FALSE); #stream_set_blocking($pipes[2], FALSE); stream_set_write_buffer($pipes[0], 0); stream_set_write_buffer($pipes[1], 0); stream_set_write_buffer($pipes[2], 0); // We now have a connection to the remote SSH // Server which we may use to send commands/receive output $p = array('process' => $process, 'std_in' => $pipes[0], 'std_out' => $pipes[1], 'std_err' => $pipes[2], 'stdout_file' => $child_stdout, 'stderr_file' => $child_stderr); if ($debug_sexec) { echo "process descriptor array is \n"; print_r($p); } return $p; }