I use Net_SSH (phpseclib) to execute SSH commands on an external server. I simply cannot figure out how to have real-time output from the command. I know how to make it run in the background so it's not dependant on the Apache process, but it's unclear how I'd go about showing the external output in real-time instead of having to wait for the command to finish.
My current code is as simple as $ssh->exec('command').
The PHP version used is:
[admin# ~]$ php -v
PHP 7.1.9 (cli) (built: Sep 10 2017 11:31:06) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2017 Zend Technologies
I managed to get it working with libssh2 and output buffering, see example below:
$session = ssh2_connect("server.local", 22, array('hostkey'=> 'ssh-rsa' )) or die("Couldn't connect to the SSH Server.");
ssh2_auth_pubkey_file($session, "root", "/path/to/public/key.pub", "/path/to/private/key") or die("couldn't authenticate to server"); // Authenticating to the server with a ssh-key for security purposes
while (ob_end_flush()); // end all output buffers if any
$proc = ssh2_exec($session, "ping -c 40 google.nl");
echo '<pre class="scroll">';
echo "[root#server ~]# ping -c 5 google.nl\n"; // Command you will execute
while (!feof($proc))
{
echo fread($proc, 4096); // Read the output from the command
# flush(); // Flushes the whole php buffer so we can output new data
}
echo "\nDone";
echo '</pre>';
Don't forget that you need php 5.6 or lower for ssh2, you can replace the command in the variable $proc by $ssh->exec('command') as you use it.
I was able to get it working using this:
$ssh->exec('ping 127.0.0.1', function($output) {
echo $output;
});
To eliminate the variability in how your system is configured versus mine I'll use Vagrant to establish a common configuration. To that end, here's my Vagrantfile:
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
end
My full phpseclib code (using 1.0.7):
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('127.0.0.1', 2222);
$ssh->login('vagrant', 'vagrant');
$ssh->exec('ping 127.0.0.1', function($output) {
echo $output;
});
A youtube video of the output:
https://youtu.be/j9-q3024eEk
If it's not working then several possibilities exist.
Maybe the "command" you're running simply doesn't dump output real time. Or maybe it requires a PTY or something. It's hard to comment since you haven't said what the command you're trying to run is. As my post demonstrates there are commands that my solution does work with.
Maybe it works with Vagrant but not with your system. Maybe your system has been configured in some funky way or something. In this scenario I guess what'd help is if you provided the SSH logs. You can get them by doing define('NET_SSH2_LOGGING', 2); and then echo $ssh->getLog();. Post the results in pastebin.com and then post the link.
edit: if you're running this in a webserver vs in the CLI you may encounter issues with how the webserver is setup - issues that go past phpseclib. For example, does this output real time or does it lock up?:
while (true) {
echo "test\n";
sleep(1);
}
flush() / ob_flush() might help but ultimately this would depend on the webserver you're using (Apache, nginx, etc), the SAPI you're using (CGI, Apache module, etc), etc.
I would consider this to be a "funky configuration".
Related
I am trying to run a PowerShell command from inside a php file on my webserver.
Here is my php code:
<?php
$query = shell_exec('C:\\Windows\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe -command Get-Host');
echo $query;
?>
The result I get is HTTP Error 500 - C:\PHP\php-cgi.exe - The FastCGI process exceeded configured activity timeout - Error Code 0x80070102
To perform a test to verify shell_exec and php were functioning (that I hadn't screwed something up when setting up the environment). I also tried this php:
<?php
$query = shell_exec('dir c:\\');
echo $query;
?>
This worked, the page source looked exactly like you'd expect if you had run the dir c:\ command from a command prompt in windows.
I've looked at several other articles here on stackoverflow, as well as on technet, and can't seem to identify what is causing this to time out. When I run the same command from the command prompt the result is very quick.
Here are the results from executing the same command manually at the command prompt (again what you'd expect):
C:\>C:\Windows\syswow64\WindowsPowerShell\v1.0\powershell.exe -command Get-Host
Name : ConsoleHost
Version : 2.0
InstanceId : 9a24a372-f0a3-4a79-b9fd-1e180e9a8069
UI : System.Management.Automation.Internal.Host.InternalHostUserI
nterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
IsRunspacePushed : False
Runspace : System.Management.Automation.Runspaces.LocalRunspace
A little about my environment:
I'm running IIS 7.5 on a Windows 2008 R2 SP1 Standard Server.
The server is standalone (not part of a domain).
I have PHP 5.5.9 installed, and working. phpinfo() returns the expected page without any noticeable errors.
Any help in getting me on track with executing powershell commands from php is much appreciated!
Try this :
$query = shell_exec('C:\\Windows\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe -command Get-Host < NUL');
I've write a php code to call a Python script much like this:
<?php
system("tmn", $return_value);
echo $return_value;
?>
Below is the Python script.
#!/usr/bin/env python
import os
from subprocess import Popen
devnull = open(os.devnull, 'wb')
p = [] # ip -> process
for n in range(1, 20): # start ping processes
ip = "172.28.83.%d" % n
p.append((ip, Popen(['ping', '-c', '1', '-w', '1', ip], stdout=devnull)))
#NOTE: you could set stderr=subprocess.STDOUT to ignore stderr also
while p:
for i, (ip, proc) in enumerate(p[:]):
if proc.poll() is not None: # ping finished
p.remove((ip, proc)) # this makes it O(n**2)
if proc.returncode == 0:
print('%s active' % ip)
elif proc.returncode == 2:
print('%s no response' % ip)
else:
print('%s error' % ip)
devnull.close()
But when I load the php page using my broswer, the page will loading forever, it seems that PHP is stuck at the system or exec call.
I tried using different Python script, but as long as the script is parallel(using either Multiproccessing or Multithreading), this problem will definitely happen.
The weirdest thing is that this issue only happens on one of my linux server(CentOS 6.5).
$php -v
PHP 5.5.7 (cli) (built: Jan 3 2014 11:19:10)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2013 Zend Technologies
python --version
Python 2.7.6
I've squeeze my head all day for this. It would be a huge help if you give any suggestion.
This is probably widely off mark, but the rule of thumb solution for solving "weird problems which only happen on centos" is "have you tried disabling selinux?"
Maybe you should try disabling it (http://www.cyberciti.biz/faq/howto-turn-off-selinux/), rebooting and trying your code again. If it works, you will either learn to keep selinux disabled on all your systems or you will have an excellent adventure in trying to understand how selinux works, in which case, good luck and bring a lot of aspirin.
I made a php script to start Tomcat Server (catalina.sh start) thats works well from command line in Linux. It was run as ROOT with a different set of ENV.
However, I would like to do the same from the Web, Please note due to security issues, this script would only be accesible via localhost (for administration only), so we ironed out the security issue.
Problem is, Tomcat is not starting (we could confirm that from the logs, nothing was initiated). We coded lots of Echo around catalina.sh and its providing the right echo output.
Im sure theres an easier way to do this. Any Ideas?
The PHP script does the following:
Changes Dir to th Tomcat Bin
Starts Catalina.sh Script vis startup.sh
OUTPUT FROM COMMAND LINE PRINT ENV:
ORBIT_SOCKETDIR=/tmp/orbit-root
HOSTNAME=localhost.localdomain
SHELL=/bin/bash
TERM=xterm
HISTSIZE=1000
XDG_SESSION_COOKIE=c111d8f81ff89709b78252c50000000d-1338310776.433586-1797694621
GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2
WINDOWID=73400323
QTDIR=/usr/lib/qt-3.3
QTINC=/usr/lib/qt-3.3/include
USER=root
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.tbz=01;31:*.tbz2=01;31:*.bz=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
SSH_AUTH_SOCK=/tmp/keyring-bsFAQz/socket.ssh
GNOME_KEYRING_SOCKET=/tmp/keyring-bsFAQz/socket
USERNAME=root
SESSION_MANAGER=local/unix:#/tmp/.ICE-unix/1788,unix/unix:/tmp/.ICE-unix/1788
PATH=/usr/lib/qt-3.3/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin:/root/bin
MAIL=/var/spool/mail/root
DESKTOP_SESSION=gnome
PWD=/programs/tomcat/bin
GDM_KEYBOARD_LAYOUT=us
GNOME_KEYRING_PID=1779
LANG=en_US.UTF-8
GDM_LANG=en_US.UTF-8
GDMSESSION=gnome
HISTCONTROL=ignoredups
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SHLVL=3
HOME=/root
GNOME_DESKTOP_SESSION_ID=this-is-deprecated
LOGNAME=root
QTLIB=/usr/lib/qt-3.3/lib
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-9HtQtRhCr9,guid=e1755bf07ea8a916f0a4e3f70000001c
LESSOPEN=|/usr/bin/lesspipe.sh %s
WINDOWPATH=1
DISPLAY=:0.0
G_BROKEN_FILENAMES=1
COLORTERM=gnome-terminal
XAUTHORITY=/var/run/gdm/auth-for-root-IT2DiY/database
_=/usr/bin/printenv
OUTPUT FROM APACHE PRINT ENV:
TERM=xterm
PATH=/sbin:/usr/sbin:/bin:/usr/bin
PWD=/programs/tomcat/bin
LANG=C
SHLVL=4
_=/usr/bin/printenv
try using exec()
exec('full path to your script')
I'm using Ubuntu Natty with PHP 5.3.5 and PECL Gearman 0.8.0. Here's the version info:
PHP 5.3.5-1ubuntu7.3 with Suhosin-Patch (cli) (built: Oct 13 2011 22:20:48)
Copyright (c) 1997-2009 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
with the ionCube PHP Loader v4.0.10, Copyright (c) 2002-2011, by ionCube Ltd., and
with Zend Guard Loader v3.3, Copyright (c) 1998-2010, by Zend Technologies
with Suhosin v0.9.32.1, Copyright (c) 2007-2010, by SektionEins GmbH
I'm getting a segmentation fault when trying to run a Gearman Client via the command line (I already have my worker running).
Here's what I get on the command line:
root#Local:~/sandbox# php php_gearman_client.php
Sending job
Segmentation fault
Here is my worker code:
<?php
echo "Starting\n";
# Create our worker object.
$gmworker= new GearmanWorker();
# Add default server (localhost).
$gmworker->addServer();
# Register function "reverse" with the server. Change the worker function to
# "reverse_fn_fast" for a faster worker with no output.
$gmworker->addFunction("reverse", "reverse_fn");
print "Waiting for job...\n";
while($gmworker->work())
{
if ($gmworker->returnCode() != GEARMAN_SUCCESS)
{
echo "return_code: " . $gmworker->returnCode() . "\n";
break;
}
}
function reverse_fn($job)
{
echo "Received job: " . $job->handle() . "\n";
$workload = $job->workload();
$workload_size = $job->workloadSize();
echo "Workload: $workload ($workload_size)\n";
# This status loop is not needed, just showing how it works
for ($x= 0; $x < $workload_size; $x++)
{
echo "Sending status: " . ($x + 1) . "/$workload_size complete\n";
$job->sendStatus($x, $workload_size);
sleep(1);
}
$result= strrev($workload);
echo "Result: $result\n";
# Return what we want to send back to the client.
return $result;
}
# A much simpler and less verbose version of the above function would be:
function reverse_fn_fast($job)
{
return strrev($job->workload());
}
?>
And here is my client code:
<?php
# Create our client object.
$gmclient= new GearmanClient();
# Add default server (localhost).
$gmclient->addServer();
echo "Sending job\n";
# Send reverse job
do
{
$result = $gmclient->do("reverse", "Hello!");
# Check for various return packets and errors.
switch($gmclient->returnCode())
{
case GEARMAN_WORK_DATA:
echo "Data: $result\n";
break;
case GEARMAN_WORK_STATUS:
list($numerator, $denominator)= $gmclient->doStatus();
echo "Status: $numerator/$denominator complete\n";
break;
case GEARMAN_WORK_FAIL:
echo "Failed\n";
exit;
case GEARMAN_SUCCESS:
break;
default:
echo "RET: " . $gmclient->returnCode() . "\n";
exit;
}
}
while($gmclient->returnCode() != GEARMAN_SUCCESS);
?>
EDIT
It appears the segmentation fault was being caused by Imagick. So I did the following to deal with the issue.
Remove imagick dpkg --purge --force-all php5-imagick. I had installed this when I was setting up PHP
Restart PHP (This could vary depending on how you installed php)
Restart Gearman Job Server /etc/init.d/gearman-job-server stop && /etc/init.d/gearman-job-server
Everything seems to be working ok now.
since it is segmentation fault that means something is wrong with your installation. Run dmesg to see more details there may be problem with some php extension that could be disabled.
I just also had this problem.
This is the soution I took (to fully resolving it), and steps to observing the error.
Step 1.
Running the Gearman Worker resulted in a:
Segmentation fault
Step 2.
Run dmesg from shell, I observed that this was showing up.
[2423402.716386] php[21232]: segfault at 30 ip b66d0321 sp bfc704c0
error 6 in libuuid.so.1.3.0[b66cf000+3000]
Step 3.
Googled for libuuid.so and it became apparent that Imagick uses uuid_create() for Image UUIDs.
http://usrportage.de/archives/922-PHP-segfaulting-with-pecluuid-and-peclimagick.html
Step 4. (The solution)
I ensured that Imagick extension gets initialized last out of all of my PHP extensions.
So previously I had in the first line of my
/etc/php5/conf.d/extensions.ini file imagick.so
What I did instead was created a file called imagick.ini and the contents of that were
imagick.so
Because the extensions are loaded in alphabetical order (and if you are running your Worker through command line), loading imagick last ensures that all the dependant extensions are loaded first. In this case libuuid.
I want to execute less and similar programs from the command-line in PHP.
I have tried the usual suspects (exec, shell_exec, passthru, etc), and while many of them can dump the file to the screen, the process is terminated before I can make use of it. If I wanted cat, I'd use it.
How do I execute a program in this fashion?
You could use proc_open to feed input to and get output back from a process via pipes. However, it doesn't seem like less allows for user interaction via pipes as it basically degrades to a cat command. Here's my first (failed) approach:
<?php
$dspec = array(
0 = array('pipe', 'r'), // pipe to child process's stdin
1 = array('pipe', 'w'), // pipe from child process's stdout
2 = array('file', 'error_log', 'a'), // stderr dumped to file
);
// run the external command
$proc = proc_open('less name_of_file_here', $dspec, $pipes, null, null);
if (is_resource($proc)) {
while (($cmd = readline('')) != 'q') {
// if the external command expects input, it will get it from us here
fwrite($pipes[0], $cmd);
fflush($pipes[0]);
// we can get the response from the external command here
echo fread($pipes[1], 1024);
}
fclose($pipes[0]);
fclose($pipes[1]);
echo proc_close($proc);
I guess for some commands this approach might actually work - and there are some examples in the php manpage for proc_open that might be helpful to look over - but for less, you get the whole file back and no possibility for interaction, maybe for reasons mentioned by Viper_Sb's answer.
...But it seems easy enough to simulate less if that's all you need. For example, you could read the output of the command into an array of lines and feed it in bite-sized chunks:
<?php
$pid = popen('cat name_of_file_here', 'r');
$buf = array();
while ($s = fgets($pid, 1024))
$buf[] = $s;
pclose($pid);
for ($i = 0; $i < count($buf)/25 && readline('more') != 'q'; $i++) {
for ($j = 0; $j < 25; $j++) {
echo array_shift($buf);
}
}
I don't believe this is possible. PHP is not a VM/shell environment, the commands it has to access other programs all return control to it, and normally there is no interaction while PHP is running.
One last thing, try with the backtick operators, if that doesn't work then I'm pretty sure you can't do this without writing up something yourself that will sleep and allow user input etc... by default no
`nano file.txt`
Adding exec('stty cbreak'); to the PHP script also fixes the issue.
I put the following in a file defined by the auto_prepend_file setting in php.ini
So, I would do something like edit php.ini to the following:
auto_prepend_file = /path/to/prepend.php
Then in, /path/to/prepend.php, I would add the following line:
if (php_sapi_name() == 'cli') exec('stty cbreak');
I'm not exactly sure of the cause. I've read bug reports for PHP. I'm not sure about versions though. I noticed the problem with the following setup:
$ php -v
PHP 5.3.3 (cli) (built: Jul 12 2013 20:35:47)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
However, the following did not show the issue:
# php -v
PHP 5.3.26 (cli) (built: Oct 21 2013 16:50:03)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2013 Zend Technologies
with the ionCube PHP Loader v4.4.1, Copyright (c) 2002-2013, by ionCube Ltd.
It is worth noting that the version without the issue was using cPanel and the other was using the default CentOS 6 install via yum.