Guilgamos
Guilgamos

Reputation: 3961

Result form crontab is not same as manually execute?

I have a PHP script that execute some shell commands.

Once I manually executed it. It worked very fine.

But after I used crontab to execute, the result from some shell commands are missing.

These are 2 commands.

and here is a sample PHP code.

#!/usr/bin/php -q
<?
    $route = exec('ip route');
    $iptable = exec('iptables -t nat -L | grep 8800');
    echo $route;
    echo $iptables;
?>

The above code worked well with manually execute but not crontab.

I found that some commands worked well with both. For example

Any ideas for this problem, and thank you in advance.

Upvotes: 1

Views: 489

Answers (2)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76395

A couple of things worth noting:

The exec function's signature is

string exec ( string $command [, array &$output [, int &$return_var ]] )

To get the full output of a command you'll have to call it like so:

$output = array();//declare in advance is best because:
$lastLine = exec('ip route', $status, $output);//signature shows reference expected
print_r($output);//full output, array, each index === new line
echo PHP_EOL, 'The command finished ', $status === 0 ? 'well' : 'With errors: '.$status;

You're using the exec command as though it were passthru, which might be worth a look in your case:

The last line from the result of the command. If you need to execute a command and have all the data from the command passed directly back without any interference, use the passthru() function.

It's important to not that, when you run the command manually, it has access to all your environment variables, loaded when you log in (and any additional vars set in your .profile or .basrc file). This is not the case when running a command using crontab or over ssh.
Perhaps, the $PATH environment variable is set so, that you don't have access to ip and other commands. There are easy fixes for this:

exech('/sbin/ip route');//if that's the correct path

Or, have a second script at the ready, and change your PHP script to:

if (!exec('which ip'))
{//ip command not found
    return exec('helper.sh '.implode(' ', $argv));//helper script
}

With the helper script looking like this:

#/bin/bash
source /home/user/.bashrc
$*

Where $* merely calls the original PHP script again, only this time, the profile has been loaded. You could replace the source call with export PATH=$PATH:/sbin or something, and just set the environment variables up the way you need them.

The third, and final part uses pipes, and proc_open. It's a PHP-only way of loading the profile and calling the script recursively:

if(!$_SERVER['FOO'])
{//where FOO is an environment variable that is loaded in the .profile/.bashrc file
    $d = array(array('pipe','r'),array('pipe','w'));
    $p = proc_open('ksh',$d,$pipes);
    if(!is_resource($p) || end($argv) === 'CalledFromSelf')
    {//if the last argument is calledFromSelf, we've been here before, and it didn't work
        die('FFS');//this is to prevent deadlock due to infinite recursive self-calling
    }
    fwrite($pipes[0],'source /home/user/.profile'."\n");
    fwrite($pipes[0],implode(' ',$argv).' CalledFromSelf'."\n");
    fclose($pipes[0]);
    usleep(15);//as long as you need
    echo stream_get_contents($pipes[1]);
    fclose($pipes[1]);
    proc_close($p);
    exit();
}
echo $_SERVER['FOO'].PHP_EOL;//actual script goes here, we can use the FOO env var

This is how I solved an old question of mine where I encountered difficulties with environment variables not being set

Last thing to note is: Does the user running crontab have the permissions required to do what needs to be done?

Upvotes: 4

Konstantin Yaniv
Konstantin Yaniv

Reputation: 455

Pay attention to what user runs the script. May be the user (which you add crontab task) haven't got enough permissions.

Upvotes: 0

Related Questions