Inject input into process connected to standard input (interactive console) - php

We have a tool which executes the PHP interactive shell like this:
$descriptorSpec = array(
0 => STDIN,
1 => STDOUT,
2 => STDERR
);
$prependFile = __DIR__ . '/../../../../../res/dev/console_auto_prepend.php';
$exec = 'php -a -d auto_prepend_file=' . escapeshellarg($prependFile);
$pipes = array();
proc_open($exec, $descriptorSpec, $pipes);
The trick with auto_prepend_file unfortunately causes issues with autoloading on PHP 5.3. We found out that everyting works well when we include the file inside the interactive shell:
$ php -a
Interactive shell
php > include "myproject/res/dev/console_auto_prepend.php";
Autoloader initialized.
What we want to do is the following:
execute php interactive shell via proc_open
send the include line to the interactive shell
hand over the controll to the user input
Is there any way to do this?

Untested idea:
Created a new input pipe
Open the PHP process (php -a) with that input pipe, stdout and stderr could be connected to the system pipes
Inject the include command (write to the input pipe)
In a loop read from the STDIN and write to our new input pipe (which is connected to the php -a)

The project ended up in using a custom PHP shell (psysh)

Related

Why update nohup.out when running nohup in exec php

I'm running a php socket. I run the program through nohup. Run this program properly through root. But my problem is running the program via the exec () function in php. When I run the command this way the program runs correctly but the program output is not printed in nohup.out.
my command in ssh:
nohup php my_path/example.php & #is working
my command in user php:
exec('nohup php my_path/example.php >/dev/null 2>&1 &', $output); #not update nohup.out
please guide me...
From PHP docs on exec:
If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
From man nohup:
If standard input is a terminal, redirect it from /dev/null. If standard output is a terminal, append output to 'nohup.out' if possible, '$HOME/nohup.out' otherwise. If standard error is a terminal, redirect it to standard output. To save output to FILE, use 'nohup COMMAND > FILE'.
To satisfy both - redirect manually to nohup.out:
exec('nohup php my_path/example.php >>nohup.out 2>&1 &', $output);

ROS catkin_init_workspace not found when spawned as process by PHP

Let me elaborate: I am trying to spawn the catkin_init_workspace from PHP, using the proc_open like so:
touch( "$dir/stderr.txt" );
chmod( "$dir/stderr.txt", 0755 );
$fp = fopen("$dir/stderr.txt", "w");
fclose($fp);
$descr = array(
0 => array("pipe", 'r'), // stdin
1 => array("pipe", 'w'), // stdout
2 => array("file", "$dir/stderr.txt", "w")to file
);
$pid = proc_open( "catkin_init_workspace", $descr, $pipes, $dir );
if (!is_resource( $pid) )
throw new Exception ( "`catkin_init_workspace` exec failed");
else if ( is_resource( $pid ) )
{
fclose( $pipes[1] );
$retval = proc_close( $pid );
}
The above code has worked with CMake, with GCC and other applications.
However, when I try this with catkin_init_workspace, I get:
sh: 1: catkin_init_workspace: not found
Now, as far as I understand, catkin_init_workspace is a python script at:
/opt/ros/indigo/bin/catkin_init_workspace
I tried invoking it directly by using the absolute path, but that didn't work.
As a user, everything works fine. But not when I am executing via www-data, the user/group setup for Apache2.
ROS tutorial explain that I need to setup my environment variables, by running
source /opt/ros/indigo/setup.bash
Which I also tried doing via PHP, right before I invoke the proc_open, but to no avail.
My understanding is that I need to setup the environment variables correctly.
Doing
export | grep ROS
shows:
declare -x ROSLISP_PACKAGE_DIRECTORIES="/home/alex/Projects/ros_ws/devel/share/common-lisp"
declare -x ROS_DISTRO="indigo"
declare -x ROS_ETC_DIR="/opt/ros/indigo/etc/ros"
declare -x ROS_MASTER_URI="http://localhost:11311"
declare -x ROS_PACKAGE_PATH="/home/alex/Projects/ros_ws/src:/opt/ros/indigo/share:/opt/ros/indigo/stacks"
declare -x ROS_ROOT="/opt/ros/indigo/share/ros"
declare -x ROS_TEST_RESULTS_DIR="/home/alex/Projects/ros_ws/build/test_results"
Are those the environment variables I need to setup for www-data to correctly invoke catkin?
If so, how do I pass as an env array to PHP's proc_open, those variables?
As you already figured out, source /opt/ros/indigo/setup.bash has to be called in advance, otherwise your environment is not set up to find the ROS commands.
When you did this in PHP, I guess you used something like an additional proc_open or exec call or something similar before you call proc_open("catkin_init_workspace", ...)?
By doing this, the environment is probably only set up for this single call and it is not kept until you run catkin_init_workspace in another proc_open-call.
Possible Solution
I cannot test this here right now (no PHP installed), but the following should work:
Create a simple bash script with the following content:
#!/bin/bash
source /opt/ros/indigo/setup.bash
catkin_init_workspace
In PHP, call this script instead of catkin_init_workspace

PHP proc_open() pipes communication

The context is the following:
I'm using a Raspberry Pi with PHP5 and Apache installed on it and it's connected to a LED display.
There are a few executables on the Pi to make this LED display works.
I want my php script to fetch datas from the web with some APIs and display the result on the LED.
So, the display works fine. I have installed a C-based program in the the /var/www/ and here is an example
on /var/www/: sudo ./text-example -f fonts/5x8.bdf -r 16
(-f for the font used, -r for the number of rows of my LED)
When you execute this, you can write some text in the shell and it will display it on the LED.
So I made a simple test.php script with the following: $res = shell_exec('sudo ./test-example -f fonts/5x8.bdf -r 16'); and it works too. I can enter messages in the STDIN.
A little further, now I need to enter the text dynamically so I did a proc_open() in a proc.php:
<?php
$desc = array(0=>array("pipe", "r"), 1=>array("pipe", "w"));
$cwd = '/var/www/';
$process = proc_open('php test.php', $desc, $pipes, $cwd, NULL);
if (is_resource($process)) {
fwrite($pipes[0], 'hello');
fclose($pipes[0]);
}
?>
So, now I run in the shell: php proc.php. As soon as I define a $desc with pipe, the programm will execute, and then stop. The LED display is not displaying anything. If I remove the $desc variable and remove the pipes things (fwrite, fclose), I can enter text in the shell and it will execute properly as if it was test.php. I don't understand why it won't let me STDIN with fwrite and why the php is interrupted with theses pipes.

Open vim from PHP like git

How do i open vim from a PHP file to edit some other file like git when you run "git commit" without the -m flag?
I've tried this solution Open Vim From PHP CLI
from-php-cli but it gets me some IO error with vim :
Vim: Warning: Output is not to a terminal
Vim: Warning: Input is not from a termin?
is there other way to do this? probably with bash or something else?
Vim is an interactive editor. The error you are getting ("Output is not to a terminal") is a precise description of the problem. You can't control Vim from a PHP script.
If you need to pass text, you should use alternative approaches like the aforementioned -m flag or STDIN.
I was able to do it using the proc_open method with one of the solutions from Open Vim From PHP CLI and a cycle that constantly checks if the process is still running. Once you finish editing it continues running the script.
$descriptors = array(
array('file', '/dev/tty', 'r'),
array('file', '/dev/tty', 'w'),
array('file', '/dev/tty', 'w')
);
$process = proc_open($_SERVER['EDITOR']. $filename, $descriptors, $pipes);
//if(is_resource($process))
while(true){
if (proc_get_status($process)['running']==FALSE){
break;
}
}
It's not very elegant but it does the job :)
PS:I'm sorry for the bad English >.<
So you want to execute a bash script with PHP?
Take a look at:
shell_exec()
exec()
http://php.net/manual/de/function.shell-exec.php
http://php.net/manual/de/function.exec.php
The first one will return data from executed command, if you do a cat file.txt you will end up with a string which contains the content of file.txt
If you use exec it will not return std output, but will return true/false if the given command was successful.
You can now write a simple bash script which does manipulate text and then execute it via PHP.

Invoke external shell script from PHP and get its process ID

How can I invoke an external shell script (Or alternatively an external PHP script) from PHP itself and get its process ID within the same script?
$command = 'yourcommand' . ' > /dev/null 2>&1 & echo $!; ';
$pid = exec($command, $output);
var_dump($pid);
If you want to do this strictly using tools PHP gives you, rather than Unix-specific wizardry, you can do so with proc_open and proc_get_status, although the need to pass a descriptor spec into proc_open makes it unpleasantly verbose to use:
<?php
$descriptorspec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w']
];
$proc = proc_open('yourcommand', $descriptorspec, $pipes);
$proc_details = proc_get_status($proc);
$pid = $proc_details['pid'];
echo $pid;
For a cross-platform solution, check out symfony/process.
use Symfony\Component\Process\Process;
$process = new Process('sleep 100');
$process->start();
var_dump($process->getPid());
After you install symfony/process with composer (composer require symfony/process), you may need to update autoloading info with composer dump-autoload and then require the autoload with require __DIR__ . '/vendor/autoload.php';.
Notice also that you can get PID of a running process only. Refer to the documentation for API details.
What ended up working for me is using pgrep to get the PID of the command (or process name) executed after calling exec() in PHP.
exec($command);
$pid = exec("pgrep $command");
This will work for launching background processes too. However, you must remember to pipe the program's output to /dev/null or else PHP will hang. Also, when calling pgrep you can't include the pipe portion of the command:
$command = "bg_process -o someOption";
exec($command + " > /dev/null &"); //Separate the pipe and '&' from the command
$pid = exec("pgrep $command");
Note that if the system has multiple processes launched with the same exact command, it will return the PIDs of all processes which match the command given to pgrep. If you only pass in a process name, it will return all PIDs with that process name.

Categories