1

Bit of a hard one to debug as I'm working with both a new version of PHP and a new OS on a new server.

I have a cron management system in PHP that allows me too add / remove or enable / disable cronjobs.

On another current Deb 8 server with PHP 7.2 it works flawlessly using the following function to create a job...

public static function createCronjob($job)
{
    exec('echo -e "`crontab -l`\n'. $job .'" | crontab -', $output);
    return $output;
}

Where $job would be something like :

10 0 * * * wget -O - https://website.com/cxs?job=jobTitle >/dev/null 2>&1

And I can also list the contents of the crontab with this, which also spits out two separate arrays, one for active jobs and one for inactive ones...

public static function getCronjobs()
{
    exec('crontab -l', $data);
    
    $active = [];
    $inactive = [];
    foreach ($data as $j) {
        if (!empty($j)) {
            if (substr($j,0,1) == '#') {
                array_push($inactive, $j);
            } else {
                array_push($active, $j);
            }
        }
    }

    $arr = [
        'active' => $active,
        'inactive' => $inactive
    ];

    return $arr;
}

But I've just set up a new Debian 10 server with PHP 7.4 and none of this seems to do anything anymore? It doesn't add jobs and crontab -l always returns an empty array (which I assume is because there are no jobs to be found).

I've checked that exec() is working and also tried adding the www-data user to /etc/cron.allow as suggested by a few articles I found, although I never had to do that on the Deb 8 server, but I'm getting no joy at all.

Were there new security measures or code changes introduced in Deb 10 or PHP 7.4 that I'm unaware of that would be preventing this from working or is there something obvious I'm missing here?

-- Edit to include the solution kindly provided by @summea below --

I'm sure others are going to stumble upon this so here is the answer :

a) PHP 7.4 (and 7.3 it seems) require the full path to echo in exec()

public static function createCronjob($job)
{
    exec('/usr/bin/echo -e "`crontab -l`\n'. $job .'" | crontab -', $output);
    return $output;
}

But strangely the full path to crontab is NOT required (but probably good practice) as this still works :

exec('crontab -l', $data);

b) Creating /etc/cron.allow was a mistake for me. It was refusing access to the crontab to every other user that wasn't declared in this file. After I deleted it and restarted the cron service /etc/init.d/cron restart everything works as it should.

Really hopes this saves somebody else some time as it's a bugger!

spice
  • 1,442
  • 19
  • 35
  • 2
    First thing always to try is the full path to your executable `/usr/bin/crontab` because you can't ever assume www-data is running with the `$PATH` you expect – Michael Berkowski Feb 27 '21 at 23:49
  • Great shout Michael, just tried that and it's still not doing anything though. Is there anything else you can think of that I could try? I did attempt to add a job manually via the cli to the www-data crontab which added the job just fine, but still shows that I have no jobs when I try to list them with exec(). – spice Feb 28 '21 at 00:02
  • Would personally recommend jenkins instead of crontab. With that said, you need to look at the error exec is getting. Redirect stderr to stdout and see what it says: Add `2>&1` to the end of your command. Some other options for that https://stackoverflow.com/q/2320608/1301076 – rjdown Feb 28 '21 at 03:46

1 Answers1

1

I have a Debian 10 VM on my local computer and have PHP 7.3.19 on it. After lots of time trying different approaches to the issue you're facing, on my side the issue turned out to relate to needing to include the full path to the echo program. I don't know if there is some other echo program on my machine that PHP was finding in a different path or something (and this might indirectly relate to what Michael was saying in the comment from earlier). At one point I was wondering if the backticks in the command were causing a problem with PHP, because the shell_exec() is evidently like the PHP backtick operator.

Here is what I did to get your createCronjob() to run successfully from the PHP side of things:

// ref: q21.php
function createCronjob($job)
{
    exec('/usr/bin/echo -e "`crontab -l`\n'. $job .'" | crontab -', $output);
    return $output;
}

When I added the full path to the echo command, it began to work. Your echo path might be different, but it might be the same because we should be using similar systems.

I found out the echo path by using this command:

which echo

And it returned this location:

/usr/bin/echo

Also, for future reference, I don't know if this is a case where using the escapeshellcmd() might be a good idea in the future? I'm not very familiar with it, so I'm not sure if this is a situation where you would want to include it somewhere on your shell command (and it might make the command not run as intended anyway), but wanted to mention it!

summea
  • 7,390
  • 4
  • 32
  • 48
  • 1
    What will it return if you run `which echo` using `exec` in php? I guess this issue must have something to do with [this](https://askubuntu.com/questions/960822/why-is-there-a-bin-echo-and-why-would-i-want-to-use-it) – Ali Tou Feb 28 '21 at 06:27
  • Hi @AliTou, Hmm, I tried running `` to check about that, but ended up getting `/usr/bin/echo` as the result on my side. Thanks for letting me know about the bash related echo difference in [that other thread](https://askubuntu.com/questions/960822/why-is-there-a-bin-echo-and-why-would-i-want-to-use-it)! Maybe there is something like that happening here, but I'm not sure at the moment. – summea Mar 02 '21 at 03:00
  • Hey @summea thanks for your detailed answer. Unfortunately it still doesn't work for me. I also tried `which echo` and got the same result as you `/usr/bin/echo` but I just can't seem to get it to work. I'm not seeing any errors at all, just nothing happening. No jobs being added and a perpetually empty `crontab -l` when trying to list the jobs. Also checked server side and there are definiely no jobs being added with the code you supplied. Could this be a user issue maybe? `www-data` had no problem listing or creating crons before. – spice Mar 03 '21 at 19:44
  • 1
    Okay so you were perfectly correct. Adding the full path to `echo` DID resolve my issue. The problem I had was that I had created the `/etc/cron.allow` file and added the `www-data` user which as it turns out was preventing any other user from being able to access crontab. PHP must use a different user even though the script is being run via Apache/Nginx? Thanks again man, doubt I would have ever thought about adding the full path to echo as a resolution to this. – spice Mar 03 '21 at 22:10
  • 1
    @spice Glad to hear that it's working on your side! Also, it looks like there might be a way to check which user is related to the PHP process or script being run using [this code example](https://www.php.net/manual/en/function.get-current-user.php#57624). Maybe that would shed more light on the user question if you're curious! – summea Mar 04 '21 at 02:54
  • 1
    @summea thanks again, that's a super snippet of code to have at hand for the future. – spice Mar 15 '21 at 00:09