I am trying to build a simple deployer in Laravel, that will receive a webhook, perform a deployment and store some information (from the hook) about said deployment.
I am using Laravel 5.2 jobs dispatcher to perform the following:
public function deploy()
{
$result = 0;
$output = array();
exec('cd ' . $this->deploymentMapping->full_file_path . '; git pull', $output, $result);
return array(
'result' => $result,
'output' => $this->outputToString($output)
);
}
My understanding of the exec() function is that this should not dump any errors directly, but will store them in $output.
However, when I run php artisan queue:work on my server in the command line, to test the job dispatcher, I just immediately get fatal: Not a git repository (or any of the parent directories): .git dumped into the command line output. This git error is correct, but it is making the job "fail" as though exec() threw an error. Is this correct? My job should be reporting the error in its own way, as follows:
public function handle()
{
$deploymentAttempt = new DeploymentAttempt();
$deploymentAttempt->deployment_id = $this->deploymentID;
$gitResponse = $this->deployer->deploy(); //the above function
$deploymentAttempt->success = !($gitResponse['result'] > 0);
$deploymentAttempt->message = $gitResponse['output'];
$deploymentAttempt->save();
}
This is because PHP's exec doesn't provide a simple way to capture the stderr output separately.
You need to capture the stderr too.
Redirecting stderr to stdout should do the trick. Append 2>&1 to the end of your command.
exec('cd ' . $this->deploymentMapping->full_file_path . '; git pull 2>&1', $output, $result);
It will fill the array $output with the expected output, one line per array key.
To know more about how 2>&1 works you can follow this thread.
Related
For monitoring purposes, I want to forward the amount of open files of the current process to our monitoring tool. In shell, the following command can be executed to get the desired information: ls /proc/PROCES_ID/fd | wc -l where PROCES_ID is the current process id. Is there a way to get this information natively in PHP?
To run any shell commands from within php script and get the output:
From PHP manual on exec() command
exec(string $command, array &$output = null, int &$result_code = null): string|false
For your command:
$output = []; // this array will hold whatever the output from the bash command
$result_code = null; // this will give you the result code returned, if needed
$command = '/proc/PROCES_ID/fd | wc -l'; // command to be run in the bash
exec($command, &$output, &$result_code);
//now you can look into $output array variable for the values returned from the above command
print_r($output);
But as mentioned in the comment, using bash script over php should be preferred if feasible.
I have following function:
public function update($id){
$data = $this->client_model->get_client_by_id($id);
$sshport = $data['ssh_port'];
$sshcommand = '/var/script/config.sh';
$this->sshcommand($sshport, $sshcommand);
$this->session->set_flashdata('msg', 'Config has been sent');
redirect(base_url('admin/edit/'.$id)) }
The sshcommand function looks like this:
private function sshcommand($port, $command) {
$remotecommand = 'ssh -q root#localhost -o "StrictHostKeyChecking=no" -p'.$port.' "'.$command.'" 2> /dev/null';
$connection = ssh2_connect('controller.server.example.org', 22);
ssh2_auth_pubkey_file($connection, 'root','/root/.ssh/id_rsa.pub','/root/.ssh/id_rsa');
ssh2_exec($connection, $remotecommand); }
My problem is that the first update function wait till /var/script/config.sh has finished.
But in some of my cases it takes very long, so I just want to sent the command and let it work in the background.
I tried to change it to /var/script/config.sh | at now + 1 minute but its the same result..
Any Ideas?
Try using & with your command:
$sshcommand = '/var/script/config.sh &';
bash man page says:
If a command is terminated by the control operator &, the shell executes the command in the background in a subshell. The shell does not wait for the command to finish, and the return status is 0.
Ensure shell you are using for user that is being used by ssh, supports that.
Alternatively you can try with nohup:
$sshcommand = 'nohup /var/script/config.sh';
This may also be shell dependant. bash works. Not sure about i.e. plain sh though.
I need to start php process from shell on remove server with some arguments, so i thought that it should be a nice idea to make REST API, that executes some function when user performs GET request.
I wrote a simple bash script for testing and figured out that command-line argument is not being specified, when calling this script from website:
shell_exec('/var/www/test.sh 123')
Bash script source:
#!/bin/sh
echo $1;
When calling this bash script from root (or other existing user) it correctly shows argument it has received. When i call this script from website (that is running under user www-data under apache2), it returns nothing:
Also, if i execute this bash script in my console under www-data user, it also returns nothing:
su -c '/var/www/test.sh 123' www-data
Also i've tried to start process from different user from php (is supposed that this will not work for security reasons, but just in case):
$result = system("su -c '/var/www/test.sh 123' sexyuser", $data);
// var_dump($result): string(0) ""
// var_dump($data): int(1)
So, what privileges should i give to www-data user to run process under php?
You should let php run the script and handle the results
check php.net on exec for example http://www.php.net/manual/en/function.exec.php
//called by example.com/myshell.php?day=today&k=y&whatever=youwant
$arguments = implode(" ", $_GET);
$lastline_of_exec_result = exec ( "/your/command.sh ".$arguments); //sh called with today y youwant
echo $lastline_of_exec;
Where $arguments are the stringified list of ALL information your script got from GET arguments
if you want a ore precise in and output, try this:
//called by example.com/myshell.php?day=today&k=y&whatever=youwant
$argument = $_GET['whatever'];
$output = array();
$last_line = exec("your/command.sh ".$argument, &$output); //sh called with youwant
foreach($output as $line)
echo $line."<br/>".PHP_EOL;
or of course (with shell_exec)
$argument = $_GET['whatever'];
$output = shell_exec("your/command.sh ".$argument);
echo "<pre>".$output."</pre>";
make sure (shell_)exec is not listed under disable_functions in your php.ini
I am writing a program that will at some point call a shell script. I need this shell script (bash, or if necessary PHP 4+ will work) to be called by the program, and return an exit status that I can relay before the 1 minute is reached and the system reboots.
Here's an idea of what I mean, best as I can describe:
Program calls 'reboot' script
Reboot script runs 'shutdown -r 1' and then exits with a status of 0
Program echo's out the exit status
Server reboots
I can get everything to work except the exit status - no matter what I try the program never exits its loop waiting for an exit status, so it never returns anything but the reboot still occurs. This program runs other scripts that return exit statuses, so I need this one to as well to maintain functionality and all that...
Any help is appreciated!
EDIT- The program that calls the reboot script is a PHP script that runs in a loop. When certain events happen, the program runs certain scripts and echos out the exit status. All of them work but this - it never returns an exit status.
Scripts are being called using system($cmd) where $cmd is './scriptname.sh'
Assuming you're opening the process using proc_open, then calling proc_get_status should return an array that has the exit code in it.
You could create a bash script that backgrounds the shutdown process:
#!/bin/bash
shutdown -r 1 &
exit 0
This returns control to the parent shell, which receives "0" as the exit code.
Unfortunately, you can't rely on PHP's system() and exec() functions to retrieve the proper return value, but with a nice little workaround in BASH, it's possible to parse exit code really effectively:
function runthis($command) {
$output = array();
$retcode = -1;
$command .= " &2>1; echo $?";
exec($command, $output, $retcode);
$retcode = intval(array_pop($output));
return $retcode;
}
if (runthis("shutdown -r 1") !== 0) echo "Command failed!\n";
Let me break down what does the code doing:
$command .= " &2>1; echo $?"; - expand the command so we pipe the stderr into stdout, then run echo $?
echo $? - this special bash parameter which expands to the last executed command's exit code.
exec($command, $output, $retcode); - execute the command. ($retcode is just a placeholder here since the returned data isn't trustworthy. We'll overwrite it later.) The command's output will be written in $output as an array. Every element will represent an individual row.
$retcode = intval(array_pop($output)); - parse the last row as an integer. (since the last command will be echo $?, it will be always the actual exitcode.
And that's all you need! Although it's a really crude code, and prone to errors if not used correctly, it's perfect for executing simpler tasks, and it will always give you the proper exit code.
For more professional (and programmatic) approach, you have to dig yourself into PHP's pnctl, posix, stream functions, and also Linux pipe handling.
Using PHP on Linux, I'd like to determine whether a shell command run using exec() was successfully executed. I'm using the return_var parameter to check for a successful return value of 0. This works fine until I need to do the same thing for a process that has to run in the background. For example, in the following command $result returns 0:
exec('badcommand > /dev/null 2>&1 &', $output, $result);
I have put the redirect in there on purpose, I do not want to capture any output. I just want to know that the command was executed successfully. Is that possible to do?
Thanks, Brian
My guess is that what you are trying to do is not directly possible. By backgrounding the process, you are letting your PHP script continue (and potentially exit) before a result exists.
A work around is to have a second PHP (or Bash/etc) script that just does the command execution and writes the result to a temp file.
The main script would be something like:
$resultFile = '/tmp/result001';
touch($resultFile);
exec('php command_runner.php '.escapeshellarg($resultFile).' > /dev/null 2>&1 &');
// do other stuff...
// Sometime later when you want to check the result...
while (!strlen(file_get_contents($resultFile))) {
sleep(5);
}
$result = intval(file_get_contents($resultFile));
unlink($resultFile);
And the command_runner.php would look like:
$outputFile = $argv[0];
exec('badcommand > /dev/null 2>&1', $output, $result);
file_put_contents($outputFile, $result);
Its not pretty, and there is certainly room for adding robustness and handling concurrent executions, but the general idea should work.
Not using the exec() method. When you send a process to the background, it will return 0 to the exec call and php will continue execution, there's no way to retrieve the final result.
pcntl_fork() however will fork your application, so you can run exec() in the child process and leave it waiting until it finishes. Then exit() with the status the exec call returned.
In the parent process you can access that return code with pcntl_waitpid()
Just my 2 cents, how about using the || or && bash operator?
exec('ls && touch /tmp/res_ok || touch /tmp/res_bad');
And then check for file existence.