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!
Advertisement
Answer
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!