- Dec. 24th, 2008
- 10 comments
PHP doesn't come with native support for making SSH connections via the libssh2 libraries. You must use the PECL SSH2 extensions. Installing them can be tricky, but Kevin van Zonneveld does a great job explaining how to install them over here. So I won't go there. The new version, 0.11.0, also seems to be compiling more reliably for everyone.
Unfortunately, the library is sparsely documented, and still buggy in some places. And most of the comments posted on http://www.php.net/ssh2 are just plain wrong! Kudos to Mike Sullivan for fixing some of the issues with non-blocking I/O (And let's not forget to thank Sara Golemon for writing it to begin with!). I list the most conspicuous problems at the bottom of this blog entry for interested parties. Everyone else can use our wrappers to smooth over some of the kinks and make most common tasks trivial to perform.
Our wrapper is released under GNU license and can be used, for example, as follows:
$php_ssh2 = new SSH2('YOURHOST.COM');
$php_ssh2->loginWithPassword('YOUR_LOGIN', 'YOUR_PASSWORD');
echo $php_ssh2->execCommandBlock('find /cat/food');
All Pertinent Features:
$php_ssh2->execCommandBlockNoOutput()
allows execution of a script ignoring output. Note that without blocking execution manually, 2 ssh2_execs will execute asychronously. So it should also be used for shell scripts that have no output. Why? If you never check for data, it will never block — yes, even in blocking mode! This is subtle … until it clicks.
$php_ssh2->setLogReads($setting = true)
enables or disables read logging on the current SSH2 stream.
$php_ssh2->setLogWrites($setting = true)
enables or disables write logging on the current SSH2 stream.
$php_ssh2->getShell($set_blocking = false, $term_type = 'vt102' ... )
opens a shell for the user — generally not needed and much harder to work with.
$php_ssh2->waitPrompt($prompt_regex = '> $', &$buf = '', $timeout_secs = 0)
waits for a specified prompt $prompt_regex (expressed as a regular expression) for $timeout_secs (or 0 to block forever). Returns true or false, leaves response (whether matching or not) buffer in &$buf parameter.
$php_ssh2->writePrompt($command, $add_newline = true)
writes the specified output to stream. Returns what it was able to write.
The rest of the functions are fairly self-explanatory. Check out the library here —
http://www.seoegghead.com/software/ssh2-php-wrappers.seo
Problems/Gotchas I've Observed in PECL SSH2
1. Non-blocking mode buggy and/or coredumps in versions < 0.11.0. So make sure you upgrade.
2. FreeBSD Ports currently reports a patched "usr/ports/security/pecl-ssh2″ version 0.10.0 as 0.11.0 (Not strictly a PECL SSH2 problem, but worth noting as it caused me grief).
3. stream_set_timeout() does not work at all with SSH2 streams — silently always returns false.
4. stream_select() does not work with SSH2 streams — but prints a warning.
5. One must pass NULL to $pty — not false (like some comments on php.net claim), not "" — otherwise LFs ("\n") will get changed to CRLFs ("\r\n"). Text files will mostly survive this, but binary data will be corrupted! This took us a solid hour to debug and involved a hex-editor.
As a consequence of some of the above, there is no good way to do non-blocking I/O in versions < 0.11.0. Period. No timeouts, no polling, no selecting. If you need non-blocking I/O to work reliably, you must upgrade to 0.11.0.
Related posts:
"10 Wise Comments Banged Out Somewhere On The Internet ..."
waitPrompt() should return $buf, not $_r, otherwise execCommandBlock() cannot work.
There's a pure-PHP implementation of SSH2 that's probably going to be easier to use than PECL's ssh2 extension:
I want to execute a command, which may have output if it is correct, and may have no output if it is not correct.If I use execCommandBlocking(), which has no '@' trick, it cannot determine the end of the buffer. If I use execCommandBlockNoOutput(), which has '@' trick, I cannot get the output. Therefore I make a new function. function execCommandBlockAnyOutput($command, $not_used = true) $command = SSH2::_generateCommand($command, true, $get_stderr, $prompt_regex); while (!$_r = preg_match("#$prompt_regex#", $buf .= fread($stream, 4096))) {
To get result with '@', I improve my code above by changing the line: And change the line:
Thank you for this class! I am having a difficult time using this to interact with an interactive program on the remote side (in one such case, passwd) and am wondering if you have any suggestions for how to use this class and/or PHP's ssh2 extension to interact WITHOUT the use of PHP's expect extension?
Hi macrocode, I will not interact with passwd. Instead, I will modify /etc/shadow directly. Of course you need to encrypt the password before saving it at /etc/shadow.
Hi, I'll have some trouble with your code and, finally, feof php function does some infinity loop. So, in your code, I replace : while (!$this->feof()) by : while ($line=getStreamOutput()) Have a nice day
Guys, thanks for the useful class! In any case, the current implementation of execCommandBlocking() depends on $this-feof() to break the "while" cycle yet there is nothing in $this->feof() that alters $this->_current_stream and therefore it never actually ends which makes the whole thing run infinite loops. I can't quite understand how it was supposed to work in the first place… Anyways, a small change in execCommandBlocking() function fixes the looping issue: function execCommandBlocking($command, $not_used = true, $pty = null, (sorry I can't see if the text will format well when this comment is posted) Cheers and thanks again!
I followed the install instructions on Kevin van Zonneveld's site and verified the modules are working like they should be. I am trying to SSH into a Digi PortServer TS 16 and automate modem dialing testing to several remote locations. I'm not getting any response from the following code: include('SSH2.php'); The SSH session is successful and the "Successful login" message outputs correctly but I don't get any return message from the "conn 16″ command even though I should. See this screenshot of an SSH session with putty: http://www.flickr.com/photos/50449963@N07/5282523492/ I never get the output of "Set escape character……" like in the screenshot. It seems like the Digi doesn't return like a normal Linux or Cisco command prompt and I'm not sure if your wrapper has a way to deal with it. I can't just blindly send commands like scripting an instruction set because I need to analyze the output to see if connections to remote Digi's are successful or not. Any suggestions are welcome. Also, I tried using your wrapper for SSH to a Cisco 2811 router and got very strange output. See the screenshot:
I tried to use the seclib but got a timeout when using the default 10, However, If I increased to 20, the function does not return $ssh = new Net_SFTP($ftp_host, 22, 20); I traced into module 22h2 echo "Try Socket Open "; echo "Socket Open "; I do not get the second output.
|
















