Understanding a bourne shell script - php

I came across a wrapper script to run php as fastcgi, could someone explain what is going on in the script?
#!/bin/sh
exec /usr/bin/php5-cgi -c /etc/php5/cgi/php-fcgi.ini

The first line (sha bang or hash bang) set the interpreter, in this case /bin/sh the standard shell, this is not necessarily a bourne shell.
The script is probably used to start a php-cgi session with a custom config file.
The -c flag is used to select a configuration file . .
try /usr/bin/php5-cgi --help for more information on available flags.

Not much. It executes /usr/bin/php5-cgi with the parameter -c /etc/php5/cgi/php-fcgi.ini, i.e. it specifies a special configuration file.

The script replaces itself (exec), keeping the pid, by executing usr/bin/php5-cgi -c /etc/php5/cgi/php-fcgi.ini
If it did not use exec, whoever called it would not get the process ID of the php5-cgi process. Likely this script is intended as a drop-in replacement for executing php directly, and whatever uses this script needs the process ID of the php process.

Related

PHP system() or shell_exec(): execute another shell

I'm using system() and shell_exec() but it seems that this funcions use /bin/sh always. I want to use another shell when I call this functions: bash or zsh. Is it possible?
What's the point? If you use system to start another shell script, that shell script itself can decide (via its #! line) which shell it is supposed to be processed by. Aside from this, you can always explicitly call the shell. For instance, if you want to take advantage of globbing operators specific to zsh or specific zsh builtins, you can do a
# Produce list of files including hidden files. Use
# the zsh-builtin command instead of /usr/bin/echo :
system('zsh -c "echo *(DN)"')
One solution can be:
<?php
\system('bash -c "here your command"');
or
<?php
\system('zsh -c "here your command"');

Use php exec to launch a linux command with brace expansion

How can I force the php exec() to interpret the linux brace expansion?
I am encountering a strange behavior, and did not find a way to fix it the way I want.
I want to execute a linux command containing brace expression to select a batch of files,
from php
I am using php to generate a "random" number of files, and want then to execute a shell script which will make something with the files.
Here is my bash version:
"$ echo $BASH_VERSION"
4.1.5(1)-release
To give a simple example, let's assume I create the following files:
touch /tmp/file_{1..12}.xml
shell.sh
#!/bin/sh
FILES=$*
echo "\n\nFILES: $FILES"
for f in $FILES; do
echo Posting file $f
done
test.php
<?php
$cmd = "./shell.sh /tmp/file_{1..12}.xml";
echo"\n\nCOMMAND:\n".$cmd."\n\n";
var_dump(shell_exec($cmd));
The output of "php test.php" is:
COMMAND:
./shell.sh /tmp/file_{1..12}.xml
string(66) "
FILES: /tmp/file_{1..12}.xml
Posting file /tmp/file_{1..12}.xml
"
I expect to have the same as if I run "./shell.sh /tmp/file_{1..12}.xml" from linux terminal:
$ ./shell.sh /tmp/file_{1..12}.xml
FILES: /tmp/file_1.xml /tmp/file_2.xml /tmp/file_3.xml /tmp/file_4.xml /tmp/file_5.xml /tmp/file_6.xml /tmp/file_7.xml /tmp/file_8.xml /tmp/file_9.xml /tmp/file_10.xml /tmp/file_11.xml /tmp/file_12.xml
Posting file /tmp/file_1.xml
Posting file /tmp/file_2.xml
Posting file /tmp/file_3.xml
Posting file /tmp/file_4.xml
Posting file /tmp/file_5.xml
Posting file /tmp/file_6.xml
Posting file /tmp/file_7.xml
Posting file /tmp/file_8.xml
Posting file /tmp/file_9.xml
Posting file /tmp/file_10.xml
Posting file /tmp/file_11.xml
Posting file /tmp/file_12.xml
But I also tried with or without escapeshellcmd()
with exec($cmd) AND other functions like system() or eval()...
None of them did the job...
I know that I could do the foreach loop in php, but I am sure there is a way to have this command interpreted as if it was launched from command line.
As #Josh Trii Johnston has pointed out, the 'outer' shell you are implicitly using to call your shell script using shell_exec() is probably not Bash in your case. This way, brace expansion never takes place because there is no shell capable of expanding the expression before calling your program (as it would be in an interactive Bash session).
You could
try to change the shell invoked by PHP's shell_exec(), but this may not be possible
call /bin/bash with your program and the brace expression instead of only the brace expression: $cmd = "/bin/bash -c './shell.sh /tmp/file_{1..12}.xml'";
use eval on the argument inside your script to expand the brace expression.
From the bash(1) man page:
If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well. [...] When invoked as sh, bash enters posix mode after the startup files are read.
Try changing
#!/bin/sh
to
#!/bin/bash
if you expect Bash behavior (there is no brace expansion in POSIX).
If all of the above does not help, you should make sure that brace expansion is activated by executing set -o (while calling your program from the PHP script). If it is off, you can turn it on using:
set -o braceexpand
I used your exact example on my OS X machine and it works as expected. What user are you executing php as? Is that user's shell (/bin/sh) set to a non-bash shell?
$ php test.php
COMMAND:
./shell.sh /tmp/file_{1..12}.xml
string(555) "\n\nFILES: /tmp/file_1.xml /tmp/file_2.xml /tmp/file_3.xml /tmp/file_4.xml /tmp/file_5.xml /tmp/file_6.xml /tmp/file_7.xml /tmp/file_8.xml /tmp/file_9.xml /tmp/file_10.xml /tmp/file_11.xml /tmp/file_12.xml\nPosting file /tmp/file_1.xml\nPosting file /tmp/file_2.xml\nPosting file /tmp/file_3.xml\nPosting file /tmp/file_4.xml\nPosting file /tmp/file_5.xml\nPosting file /tmp/file_6.xml\nPosting file /tmp/file_7.xml\nPosting file /tmp/file_8.xml\nPosting file /tmp/file_9.xml\nPosting file /tmp/file_10.xml\nPost"...

php run another script in foreground

I have a php script that leads up to running another expect script by passing it arguments.
$output = shell_exec("expect login_script.tcl '$user' '$host' '$port' '$password'");
Using shell_exec doesn't work as the script gets run in the background or 'within' the php script. I need it to run in the foreground, allowing user interactivity. Is there an elegant way to do this. Already it is getting messy by having to use different scripting languages. I tried wrapping the two scripts with a shell script that called the php script, assigned output the result as a variable (which was a command) and then ran sh on that. However I have the same problem again where the scripts are run in the background and any user interactivity creates a halt/freeze. Its ok in this situation if php 'quits' out when calling shell exec. Ie. php stops and expect gets run as if you called it. (the same as if i just copied the command that is output and pasted it into the terminal).
Update
I am having much more luck with the following command in php:
shell_exec("gnome-terminal -e 'bash -c \"expect ~/commands/login_script.tcl; exec bash\"' &");
However, can this be improved in order to not close the shell immediately after the secondary script (login_script) is finished?
Further Update
From reading the answers I think I need to clarify things as it looks like people are assuming a 'more complicated' issue.
the two processes do not need to communicate with each other, I should probably not have put the $output = shell_exec in the example and just shell_exec on its own as I believe this has led to the confusion.
The php script needs to only initiate the expect script with some cli parameters, e.g. my-script 'param1' 'param2' and can be thought of as completely 'asynchronous'. This is much like the behaviour of launcher programs like 'launchy' or 'synapse' they can launch other programs but need not affect them. Nor do they wait for the secondary program to quit/finish.
I made the mistake of saying 'shell_exec' doesn't work for me. What I should have said was that 'I have so far not succeeded with using shell_exec', but shell_exec("gnome-terminal -e 'bash -c \"expect ~/commands/login_script.tcl; exec bash\"' &"); is 'working' but still trying to find the right quote combination to allow passing arguments to the expect script.
Task managing is an interesting but difficult job.
Because your user can move during a task (and leads it to an unexpected result, such as session freezes, or an incomplete work from the process), you need to execute it in background. If you need to interact between your user and your process, you'll need to create a way to communicate.
The easiest way (I think) is to use a file, shared between your user session and the task.
If you have a lot of users simultaneously and communicates a lot between user and processes, you can mount a partition in memory to optimize the read/write operations.
In your fstab, a line like :
tmpfs /memory tmpfs defaults,uid=www-data,gid=www-data,size=128M 0 0
Or, in a script, you could do :
#!/bin/sh
mkfs -t ext2 -q /dev/ram1 65536
[ ! -d /memory ] && mkdir -p /memory
mount /dev/ram1 /memory
chmod -R 777 /memory
You'll need to take care of a lot of things :
file access (to avoid concurrency between your webapp and your processes)
time (to avoid zombies or useless long-running scripts)
security (such operations must be carefully designed)
resources management (to avoid that 10000 processes runs simuntaneouly)
...
I think what you're looking for is the proc_open() command. It gives you access to the stdin/stdout streams of the background process. You can pass your own stdin/stdout streams to the new process in the $descriptorSpec parameter, which will let your background process talk to the user.
Your 'foreground' application will have to wait around until the background process has died. I haven't actuallly done this with PHP, but I'm guessing you'll have to watch the $pipes to see when they get closed -- then you'll know the background process is finished and you can delete the process resource and continue on with whatever the foreground process needs to do.
In the end, I managed to get it working by by adding a third quotation mark type: ` (I believe it is called a 'tack'?) which allowed me to pass arguments to the next script from the first script
The command I needed in my php script was:
$command = `gnome-terminal -e 'bash -c "expect ~/commands/login_script.tcl \"$user\" \"$host\" \"$port\" \"$password\"; exec bash"' &`;
shell_exec($command);
It took a while to get all the quotes right as swapping the type of quotes around can lead to it not working.
Here is a video demonstrating the end result
Use:
pcntl_exec("command", array("parameter1", "parameter2"));
For example, I have a script that starts the mysql command using the parameters in the current php project that looks like:
pcntl_exec("/usr/bin/mysql", array(
"--user=".$params['user'],
"--password=".$params['password'],
"--host=".$params['host'],
$params['dbname']
));
This doesn't rely on gnome terminal or anything, it replaces PHP with the program you call.
You do need to know the full path of the command, which is a pain because it can vary by platform, but you can use the env command command which is available at /usr/bin/env on most systems to find the command for you. The above example above becomes:
pcntl_exec("/usr/bin/env", array(
"mysql",
"--user=".$params['user'],
"--password=".$params['password'],
"--host=".$params['host'],
$params['dbname']
));

Multi threading in PHP

In a apcahe server i want to run a PHP scripts as cron which starts a php file in background and exits just after starting of the file and doesn't wait for the script to complete as that script will take around 60 minutes to complete.how this can be done?
You should know that there is no threads in PHP.
But you can execute programs and detach them easily if you're running on Unix/linux system.
$command = "/usr/bin/php '/path/to/your/php/to/execute.php'";
exec("{$command} > /dev/null 2>&1 & echo -n \$!");
May do the job. Let's explain a bit :
exec($command);
Executes /usr/bin/php '/path/to/your/php/to/execute.php' : your script is launched but Apache will awaits the end of the execution before executing next code.
> /dev/null
will redirect standard output (ie. your echo, print etc) to a virtual file (all outputs written in it are lost).
2>&1
will redirect error output to standard output, writting in the same virtual and non-existing file. This avoids having logs into your apache2/error.log for example.
&
is the most important thing in your case : it will detach your execution of $command : so exec() will immediatly release your php code execution.
echo -n \$!
will give PID of your detached execution as response : it will be returned by exec() and makes you able to work with it (such as, put this pid into a database and kill it after some time to avoid zombies).
You need to use "&" symbol to run program as background proccess.
$ php -f file.php &
Thats will run this command in background.
You may wright sh script
#!/bin/bash
php -f file.php &
And run this script from crontab.
This may not be the best solution to your specific problem. But for the record, there is Threads in PHP.
https://github.com/krakjoe/pthreads
I'm assuming you know how to use threads, this is very young code that I wrote myself, but if you have experience with threads and mutex and the like you should be able to solve your problem using this extension.
This is clearly a shameless plug of my own project, and if the user doesn't have the access required to install extensions then it won't help him, but many people find stackoverflow and it will solve other problems no doubt ...

Integrating php in shell scripts for a cronjob?

I would like to execute a cronjob for a routine task every X hours. The cronjob basically executes a shell script which in turn uses a WGET command to download files from a remote server. However, before I run this shell script I want the cronjob to execute a php script which will check whether the update's available (there's no point in wasting BW and downloading the same file over and over again) and if it is, it should pass on the update URL to the shell script which in turn uses the WGET command.
The cronjobs are set from the hosts Admin Panel. There is no other way around it. Being a shared hosting service, I am not allowed access to other functions on PHP which might do the task for me either.
Is this possible? I am Linux illiterate. I have installed a few RPM's on Fedora but that's about it. Please bear with me. Thanks!
Just pass --timestamping to your wget command.
Alternatively if you are more familiar with PHP's ways you can check this question for a usable method.
Use a curl HEAD request to get the file's headers and parse out the Last-Modified: header.
To use a php script as a regular command line executable use this as a starting point:
#!/bin/env php
<?php
echo "Hello World\n";
Save the file without the .php and tuck it somewhere that your server won't serve it.
Next, set the executable bit so that you can execute the script like a regular program
(u+x in the following command means grant the [u]ser e[x]ecute privileges for helloworld, and chmod is the command that unix variants use to set file permissions)
Omit the $ in the following sequence, as it represents the command prompt
$ chmod u+x helloworld
now you can execute your commandline script by calling it in the bash prompt:
$ ls
helloworld
$ ./helloworld
Hello World
$
From here you can get the full path of the executable script:
$ readlink -f helloworld
/home/SPI/helloworld
And now you can install the cronjob using the path to your executable script.

Categories