Command Line PHP: Part 2

Posted by Nessa | Posted in ,,, | Posted on May 21, 2010

3

This post is continuing on my three-part series on command line PHP programming. Missed part one? It’s right behind you. This part will go over command execution and processes.

Command Execution

Let’s start with the basics – you need to execute an arbitrary command from your PHP script. The exec, shell_exec, passthru, and system functions will take care of this for you:

exec("/usr/bin/perl /scripts/myscript.pl");

will do about the same thing as:

system("/usr/bin/perl /scripts/myscript.pl");

and

shell_exec("/usr/bin/perl /scripts/myscript.pl");

So what’s the difference between the three? A little, but not much. The system function will place the output of the command in an output stream, and display the results whether you tell it to or not. However, it’s mostly useful to return a yes or no output (in the form of 1, 0, or -1) based on the exit code of the command it was executing. For example, the below script views the /proc/cpuinfo file and outputs a boolean value on whether the command executed:

$cpuinfo = system("cat /proc/cpuinfo",$exitcode);
echo $exitcode;

I could then use the boolean value to write an if statement, which is more accurate because I’m basing the condition on the output of the command itself rather than PHP returning output based on the fact that the command ran, ignoring errors the command may have posted:

if($exitcode == 1){
echo "Command executed, but finished with errors";
}

The annoying part with the system function though is that it doesn’t suppress the output of the command itself, but rather sends it directly to the output stream. What if you want to format the output of the command? Then you would use exec. Below is an example of using the exec command to list the contents of a folder and only output .tar files (using substr to split the string)

$dir = exec("ls .",$output);
foreach ($output as $file)
{
if (strtolower(substr($file, -4)) == '.tar')
echo "$file\n";
}

The shell_exec function allows you to emulate a shell environment. For example, in Linux you have a choice of shells like sh, bash, and ksh that, allow you to do things such as while loops. Using shell_exec is about the same as using the backtick operator, which is generally how you’re run commands within a Perl script:

$output = shell_exec('ls nonexistentfile 2>&1 1> /dev/null');

// Does the same as

$output = `ls nonexistent file 2>&1 1> /dev/null`l

Finally, there’s the passthru function which, when invoked, immediately displays raw ouput of the command being executed. This is intended for browsers, to display binary data such as images, and not necessarily something you’re use on the command line:

header('Content-Type: image/jpg');
passthru('cat /home/user/public_html/images/image.png');

A Word on Security

A lot of hosts block command execution functions with the disable_functions setting in php.ini or by using safe mode, and for good reason. A lot of malicious scripts contain code with executable functions, and especially when the value of command or argument is passed as a variable, the results can be destructive. Here’s an example of a simple script that lists files, that accepts a folder name as a argument:

$folder = $argv[1];
exec("ls $folder");

We’d have a security problem if someone passed this as the first argument for $folder:

myfolder && cat /etc/shadow

To prevent this, PHP has built-in functions to escape shell arguments and commands, in order to prevent things that could be dangerous:

escapeshellarg: Encloses command arguments in single quotes so the argument is passed as one string:

exec('ls '.escapeshellarg($folder));

escapeshellcmd: Escapes characters that can be used to trick a shell command (like the && I used to execute cat)

Process Management & POSIX

PHP has internal  functions that allow you to manage processes, such as proc_open, proc_close, etc. However, I admit that these tend to be difficult to learn an understand, while posix functions tend to do the job with less hassle.  Posix functions are basically system-level functions. In the below script, I’m going to change the user that the script runs as, view simple information about the process, then start a counter to 1 million but kill it when it reaches 50:

$user = posix_getpwnam($vnessa5);
posix_setuid($user['uid']);
posix_setgid($user['gid']);
$pid = getmypid();

print_r(posix_times());
echo "Script owner: ". get_current_user() . "(" . getmyuid() . ")\n";
$i=0;
while( $i < 10000000 ){
$i++;
if($i == 50){
echo "Reached 50 - killing!";
posix_kill($pid,1);
}
}

Similarly, posix_getpid and getmypid do the same thing. If the script is a child of another process, you can get the parent process’s ID with posix_getppid. The getrusage function can also be used to get more informative information than posix_times. To kill a process, you can see I used posix_kill, with the process ID and exit code as arguments. This is what happened when I ran the script:

Array
(
[ticks] => 1279790432
[utime] => 0
[stime] => 0
[cutime] => 0
[cstime] => 0
)
Script owner: root(0)
Reached 50 - killing!Hangup

Keep in mind that in most situations, using die() to bail out of a script is more appropriate that using kill. I could rewrite that part of the code to say this instead:

die("Reached 50 - Bailing out!);

I also found a nice post on running background processes, that you might want to check out here.

Environmental Variables

Environmental variables tend to be a major part of command line scripting, and is something that is part of PHP as well. Let’s start with defining an environmental variable with putenv and retrieving it with getenv:

putenv("PATH=/fakebin");

if(!exec("test testdir")){
echo "Could not execute test - path is " . getenv('PATH') ;
}

In the above script, I set the $PATH environmental variable to /fakebin, so when I went to execute the ‘test’ command (which is a valid Linux command located in /usr/bin/test), the scirpt could not find it since the command was not in /fakebin. This is about the same as setting the $PATH environment in a user’s .bashrc file. Keep in mind that the variable change lasts as long as the script does – after the script exits, the environmental variable is returned to its normal state. You can use getenv to pull any environmental variable, similar to the $_SERVER superglobal.

A nice function for getting information about the current server is php_uname. You use it about the same way as the uname command in Linux:

echo php_uname();

Will return:

Linux server.v-nessa.net 2.6.18-028stab064.7 #1 SMP Wed Aug 26 13:11:07 MSD 2009 x86_64

You can further filter the output using the same arguments as uname, for instance, if I only need the server name:

echo php_uname(n);

PHP 5.3 also introduces a void function called gethostname which will get the hostname of your machine as well.

The Server Superglobal

The $_SERVER superglobal is used to get environmental variables pursuant to the server and environment for the script you are running.  While it’s normally used in web applications, some of the values can be used for command line scripts as well:

echo "This file's name is " . $_SERVER['PHP_SELF'];

So you’ve made it part two of the series. Stay tuned for part three, which will go over specific system-level tasks and functions, with useful code snippets.

Next: Command Line PHP: Part 3

Be Sociable, Share!

Comments (3)

[…] Command Line PHP: Part 2 Share and […]

[…] Command Line PHP: Part 2 […]

[…] largement ignorée par les développeurs PHP. Pour remédier à cela Vanessa Vasile a publié une série d'articles montrant comment utiliser PHP pour créer des scripts exécutables en ligne de […]

Post a comment