[ Index ] |
PHP Cross Reference of phpSSH |
[Summary view] [Print]
1 <?php 2 /** 3 * Implementation of SExec using pipes. 4 * 5 * @package SExec 6 * @author José R. Valverde <jrvalverde@acm.org> 7 * @version 1.0 8 * @copyright José R. Valverde <jrvalverde@es.embnet.org> 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 * 24 */ 25 26 /** 27 * NOTE: this is version 1.0! It has not been updated yet to exploit 28 * SSH shared channel facilities. 29 */ 30 class SExec { 31 32 var $version="1.0"; 33 var remote; 34 var password; 35 36 var $ssh = "/usr/bin/ssh"; 37 var $scp = "/usr/bin/scp"; 38 39 function SExec($this->remote="localhost", $this->password="xxyzzy") 40 { 41 $this->remote = $this->remote; 42 $this->password = "$this->password"; 43 } 44 45 function set_remote_end($this->remote, $this->password="xxyzzy") 46 { 47 $this->remote = $this->remote; 48 $this->password = $this->password; 49 } 50 51 52 /** 53 * Execute a single command remotely using ssh and 54 * display its output, optionally returning its exit 55 * status (like passthru) 56 * 57 * This function is intended to be used as a one-time 58 * all-at-once non-interactive execution mechanism which 59 * will run the command remotely and display its output. 60 * 61 * If you try to issue an interactive command using this 62 * function, all you will get is unneccessary trouble. So 63 * don't! 64 * 65 * This might be done as well using a pipe on /tmp and 66 * making the command 'cat' the pipe: when ssh runs, it 67 * runs the command 'cat' on the pipe and hangs on read. 68 * Then we just need a thread to open the pipe, put the 69 * password and close the pipe. 70 * This other way the password is never wirtten down. 71 * But, OTOH, the file life is so ephemeral that most 72 * of the time it will only exist in the internal system 73 * cache, so this approach is not that bad either. 74 * 75 * @param remote The remote end to run the command, in 76 * the form 'user@host:port' (you may 77 * omit the 'user@' or ':port' parts 78 * if the default values [i.e. same user 79 * or standard port] are OK). 80 * @param password The remote password. Note that if direct 81 * RSA/DSA/.shosts/.rhosts login is enabled 82 * then the password should be ignored as 83 * SSH should not run the ASKPASS command). 84 * @param command The command to execute on the remote end 85 * NOTE: if you want to use redirection, the 86 * entire remote command line should be 87 * enclosed in additional quotes! 88 * @param status Optional, this will hold the termination 89 * status of SSH after invocation, which 90 * should be the exit status of the remote 91 * command or 255 if an error occurred 92 * @return void 93 */ 94 function ssh_passthru($command, &$status) 95 { 96 global $debug; 97 98 // Setup environment 99 umask(0077); 100 $tmpfname = tempnam('/tmp', 'phpSsh-'); 101 chmod($tmpfname, 0700); 102 if ($debug) echo $tmpfname."\n"; 103 104 putenv("DISPLAY=none:0."); 105 putenv("SSH_ASKPASS=$tmpfname"); 106 107 // make askpass command 108 $fp = fopen($tmpfname, "w"); 109 fputs($fp, "#!/bin/sh\necho $this->password\n"); 110 fputs($fp, "rm -f $tmpfname\n"); 111 fclose($fp); 112 // go 113 if (isset($status)) { 114 if ($debug) echo "$this->ssh -x -t -t $this->remote \"$command\"\n"; 115 passthru("$this->ssh -x -t -t $this->remote \"$command\"", $status); 116 } 117 else { 118 if ($debug) echo "$this->ssh -x -t -t $this->remote \"$command\"\n"; 119 passthru("$this->ssh -x -t -t $this->remote \"$command\""); 120 } 121 } 122 123 124 /** 125 * Execute a remote command using SSH 126 * 127 * This function sort of mimics rexec(3) using SSH as the transport 128 * protocol. 129 * 130 * The function returns the exit status of the remote command, and 131 * appends the remote job output to an optional argument. 132 * 133 * This function is intended to be used as a one-time 134 * all-at-once non-interactive execution mechanism which 135 * will run the command remotely and return its output. 136 * 137 * If you try to issue an interactive command using this 138 * function, all you will get is unneccessary trouble. So 139 * don't! 140 * 141 * @param remote The remote end to run the command, in 142 * the form 'user@host:port' (you may 143 * omit the 'user@' or ':port' parts 144 * if the default values [i.e. same user 145 * or standard port] are OK). 146 * @param password The remote password. Note that if direct 147 * RSA/DSA/.shosts/.rhosts login is enabled 148 * then the password should be ignored as 149 * SSH should not run the ASKPASS command). 150 * @param command The command to execute on the remote end 151 * NOTE: if you want to use redirection, the 152 * entire remote command line should be 153 * enclosed in additional quotes! 154 * @param output Optional, the collated (stdout+stderr) output 155 * of the remote command. 156 * @return status will hold the termination 157 * status of SSH after invocation, which 158 * should be the exit status of the remote 159 * command or 255 if an error occurred 160 */ 161 function ssh_exec($command, &$out) 162 { 163 global $debug; 164 165 umask(0077); 166 $tmpfname = tempnam('/tmp', 'phpSsh'); 167 chmod($tmpfname, 0700); 168 if ($debug) echo $tmpfname . "\n"; 169 170 putenv('DISPLAY=none:0.'); 171 putenv("SSH_ASKPASS=$tmpfname"); 172 $fp = fopen($tmpfname, "w"); 173 fputs($fp, "#!/bin/sh\necho $this->password\n"); 174 fputs($fp, "rm -f $tmpfname\n"); 175 fclose($fp); 176 exec("$this->ssh -x -t -t $this->remote \"$command\"", $out, $retval); 177 return $retval; 178 179 } 180 181 /** 182 * Copy a file or directory from one source to a destination 183 * 184 * This function copies source to dest, where one of them is a 185 * local filespec and the other a remote filespec of the form 186 * [user@]host:path 187 * 188 * If the original source is a directory, it will be copied 189 * recursively to destination (hence easing file transfers). 190 * 191 * The function returns TRUE on success or FALSE on failure. 192 * 193 * @param origin The origin path, of the form 194 * [user@][host][:port]path 195 * You may omit the optional sections if 196 * the default values (local username, local 197 * host, standard SSH port) are OK 198 * 199 * @param destination The destination path, of the form 200 * [user@][host][:port:]path 201 * You may omit the optional sections if 202 * the default values (local username, local 203 * host, standard SSH port) are OK 204 * 205 * @param password The password to use to connect to the remote 206 * end of the copy (be it the origin or the 207 * destination, it's all the same). If connection 208 * is automatic by some means (.shosts or RSA/DSA 209 * authentication) then it should be ignored and 210 * any password should do. 211 * 212 * @return status TRUE if all went well, or FALSE on failure. 213 */ 214 function ssh_copy($origin, $destination, $this->password) 215 { 216 global $debug; 217 218 umask(0077); 219 $tmpfname = tempnam("/tmp", "phpSsh"); 220 chmod($tmpfname, 0700); 221 putenv("DISPLAY=none:0."); 222 putenv("SSH_ASKPASS=$tmpfname"); 223 $fp = fopen($tmpfname, "w"); 224 fputs($fp, "#!/bin/sh\necho $this->password\n"); 225 fputs($fp, "rm $tmpfname\n"); 226 fclose($fp); 227 exec("$this->scp -pqrC $origin $destination", $out, $status); 228 if ($status == 0) 229 return TRUE; 230 else 231 return FALSE; 232 } 233 234 /** 235 * Open an SSH connection to a remote site with a shell to run 236 * interactive commands 237 * 238 * Connects to a remote host and opens an interactive shell session 239 * with NO controlling terminal. 240 * 241 * Returns a process_control array which contains the process resource 242 * ID and an the standard file descriptors which the caller may use to 243 * interact with the remote shell. 244 * 245 * @param remote The remote end to run the shell, in 246 * the form 'user@host:port' (you may 247 * omit the 'user@' or ':port' parts 248 * if the default values [i.e. same user 249 * or standard port] are OK). 250 * @param password The remote password. Note that if direct 251 * RSA/DSA/.shosts/.rhosts login is enabled 252 * then the password should be ignored as 253 * SSH should not run the ASKPASS command). 254 */ 255 function ssh_open_shell() 256 { 257 global $debug; 258 259 // Open a child process with the 'proc_open' function. 260 // 261 // Some tricks: we must open the connection using '-x' to disable 262 // X11 forwarding, and use '-t -t' to avoid SSH generating an error 263 // because we are not connected to any terminal. 264 // 265 // NOTE: if the web server is trusted remotely (i.e. it's SSH public 266 // key is accepted in ~user@host:.ssh/authorized_keys) then any 267 // password will do. 268 269 // Prepare I/O 270 $descriptorspec = array( 271 0 => array("pipe", "r"), // connect child's stdin to the read end of a pipe 272 1 => array("pipe", "a"), // connect child's stdout to the write end of a pipe 273 2 => array("pipe", "a") // stderr is a pipe to read from 274 ); 275 276 // prepare password 277 umask(0077); 278 $tmpfname = tempnam("/tmp", "phpSsh-"); 279 chmod($tmpfname, 0700); 280 if ($debug) echo $tmpfname . "\n"; 281 282 putenv("DISPLAY=none:0."); 283 putenv("SSH_ASKPASS=$tmpfname"); 284 $fp = fopen($tmpfname, "w"); 285 fputs($fp, "#!/bin/sh\necho $this->password\n"); 286 fputs($fp, "rm $tmpfname\n"); 287 fclose($fp); 288 289 if ($debug) echo "$this->ssh -x -t -t $this->remote<br />\n"; 290 $process = proc_open("$this->ssh -x -t -t $this->remote", 291 $descriptorspec, 292 $pipes); 293 294 // check status 295 if (!is_resource($process)) 296 { 297 letal("SSH::connect", "cannot connect to the remote host"); 298 return; 299 } 300 if ($debug) echo "proc_open done<br />\n"; 301 302 // $pipes now looks like this: 303 // 0 => writeable handle connected to child stdin 304 // 1 => readable handle connected to child stdout 305 // 2 => readable handle connected to child stderr 306 307 // Should we leave this to the user? 308 // set to non-blocking and avoid having to call fflush 309 stream_set_blocking($pipes[0], FALSE); 310 stream_set_blocking($pipes[1], FALSE); 311 stream_set_blocking($pipes[2], FALSE); 312 stream_set_write_buffer($pipes[0], 0); 313 stream_set_write_buffer($pipes[1], 0); 314 stream_set_write_buffer($pipes[2], 0); 315 316 // We now have a connection to the remote SSH 317 // Server which we may use to send commands/receive output 318 $p = array('process' => $process 319 ,'std_in' => $pipes[0] 320 ,'std_out' => $pipes[1] 321 ,'std_err' => $pipes[2] 322 ); 323 if ($debug) { 324 echo "process descriptor array is \n"; 325 print_r($p); 326 /* 327 fwrite($p['std_in'], "\n"); 328 fwrite($p['std_in'], "touch touche\n"); 329 fwrite($p['std_in'], "logout\n"); 330 fflush($p['std_in']); 331 fclose($p['std_in']); fclose($p['std_out']); fclose($p['std_err']); 332 echo "pipes closed\n"; 333 proc_close($p['process']); 334 echo "process closed\n"; 335 } 336 if ($debug == "CHANGE ME") { 337 echo "process "; print_r($process); echo "\n"; 338 echo "p->process "; print_r($p['process']); echo "\n"; 339 fwrite($pipes[0], "touch touche\n"); 340 fwrite($pipes[0], "logout\n"); 341 fflush($pipes[0]); 342 fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); 343 proc_close($process); 344 */ 345 } 346 return $p; 347 } 348 349 /** 350 * Open an SSH connection to run an interactive command on a remote 351 * site 352 * 353 * Connects to a remote host and runs an interactive command 354 * with NO controlling terminal. 355 * 356 * Returns a process_control array which contains the process resource 357 * ID and an the standard file descriptors which the caller may use to 358 * interact with the remote shell. 359 */ 360 function ssh_open_command($command) 361 { 362 global $debug; 363 364 // Open a child process with the 'proc_open' function. 365 // 366 // Some tricks: we must open the connection using '-x' to disable 367 // X11 forwarding, and use '-t -t' to avoid SSH generating an error 368 // because we are not connected to any terminal. 369 // 370 // NOTE: if the web server is trusted remotely (i.e. it's SSH public 371 // key is accepted in ~user@host:.ssh/authorized_keys) then any 372 // password will do. 373 374 // Prepare I/O 375 umask(0077); 376 $descriptorspec = array( 377 0 => array("pipe", "r"), // connect child's stdin to the read end of a pipe 378 1 => array("pipe", "a"), // connect child's stdout to the write end of a pipe 379 2 => array("pipe", "a") // stderr is a pipe to read from 380 ); 381 382 // prepare password 383 umask(0077); 384 $tmpfname = tempnam("/tmp", "phpSsh-"); 385 chmod($tmpfname, 0700); 386 if ($debug) echo $tmpfname . "\n"; 387 388 putenv("DISPLAY=none:0."); 389 putenv("SSH_ASKPASS=$tmpfname"); 390 $fp = fopen($tmpfname, "w"); 391 fputs($fp, "#!/bin/sh\necho $this->password\n"); 392 fputs($fp, "rm $tmpfname\n"); 393 fclose($fp); 394 395 if ($debug) echo "$this->ssh -x -t -t $this->remote $command<br />\n"; 396 $process = proc_open("$this->ssh -x -t -t $this->remote \"$command\"", 397 $descriptorspec, 398 $pipes); 399 400 // check status 401 if (!is_resource($process)) 402 { 403 letal("SSH::connect", "cannot connect to the remote host"); 404 return; 405 } 406 if ($debug) echo "proc_open done<br />\n"; 407 408 // $pipes now looks like this: 409 // 0 => writeable handle connected to child stdin 410 // 1 => readable handle connected to child stdout 411 // 2 => readable handle connected to child stderr 412 413 // Should we leave this to the user? 414 // set to non-blocking and avoid having to call fflush 415 stream_set_blocking($pipes[0], FALSE); 416 stream_set_blocking($pipes[1], FALSE); 417 stream_set_blocking($pipes[2], FALSE); 418 stream_set_write_buffer($pipes[0], 0); 419 stream_set_write_buffer($pipes[1], 0); 420 stream_set_write_buffer($pipes[2], 0); 421 422 // We now have a connection to the remote SSH 423 // Server which we may use to send commands/receive output 424 $p = array('process' => $process 425 ,'std_in' => $pipes[0] 426 ,'std_out' => $pipes[1] 427 ,'std_err' => $pipes[2] 428 ); 429 if ($debug) { 430 echo "process descriptor array is \n"; 431 print_r($p); 432 } 433 return $p; 434 } 435 436 /** 437 * Get output until we reach a given regular expression 438 */ 439 function ssh_out_expect($p, $expr="^# ") 440 { 441 flush(); 442 fseek($p["std_out"], $last); 443 do { 444 $line = fgets($p["std_out"], 1024); 445 #echo ">> ".$line; 446 } while ((! feof($p["std_out"]) ) && (! ereg($expr, $line))); 447 $last = ftell($p["std_out"]); 448 } 449 450 /** 451 * Close an SSH interactive session 452 */ 453 function ssh_close($p) 454 { 455 global $debug; 456 457 fwrite($p['std_in'], "\n"); 458 fwrite($p['std_in'], "logout\n"); 459 fflush($p['std_in']); 460 fclose($p['std_in']); fclose($p['std_out']); fclose($p['std_err']); 461 if ($debug) echo "pipes closed\n"; 462 return proc_close($p['process']); 463 } 464 465 # if ($php_version >= 5) 466 # { 467 # /** 468 # * send a signal to a running ssh_open_* process 469 # */ 470 # function ssh_signal($p, $signal) 471 # { 472 # return proc_terminate($p['process'], $signal); 473 # } 474 # /** 475 # * get info about a running ssh_open_* process 476 # */ 477 # function ssh_get_status($p) 478 # { 479 # return proc_get_status($p['process']); 480 # } 481 # } 482 483 /** 484 * Execute a remote command and keep an unidirectional stream 485 * contact with it. 486 * 487 * This routine mimics 'popen()' but uses ssh to connect to 488 * a remote host and run the requested command. 489 */ 490 function ssh_popen($command, $mode) 491 { 492 global $debug; 493 494 // Setup environment 495 umask(0077); 496 $tmpfname = tempnam('/tmp', 'phpSsh-'); 497 chmod($tmpfname, 0700); 498 if ($debug) echo $tmpfname."\n"; 499 500 putenv("DISPLAY=none:0."); 501 putenv("SSH_ASKPASS=$tmpfname"); 502 503 // make askpass command 504 $fp = fopen($tmpfname, "w"); 505 fputs($fp, "#!/bin/sh\necho $this->password\n"); 506 fputs($fp, "rm -f $tmpfname\n"); 507 fclose($fp); 508 // go 509 return popen("$this->ssh -x -t -t $this->remote \"$command\"", $mode); 510 } 511 512 function ssh_pclose($f) 513 { 514 return pclose($f); 515 } 516 517 } 518 519 ?>
title
Description
Body
title
Description
Body
title
Body
Generated: Wed May 25 19:30:43 2005 | Cross-referenced by PHPXref 0.4.1 |