Bing Bing - 6 months ago 27
Bash Question

Shell via PHP: Trouble adding new cron job to crontab

I have several scripts scheduled to run in my crontab, but can only see them as root (or using sudo). I need to have a PHP script (which is being run as nginx) be able to add a new line to the crontab file. To do this I created a shell script (owned by root) and gave the

nginx
user permission to
sudo
it via the
/etc/sudoers
file.

The last line of the
/etc/sudoers
file:

nginx ALL=NOPASSWD: /etc/nginx/addcron.sh


The PHP call to execute the script:

chdir("/etc/nginx/");
echo exec("2>&1 ./addcron.sh $custname", $output);
echo "<pre>".print_r($output, true)."</pre>";


My current crontab:

[ec2-user@ip-172-31-xx-xxx nginx]$ sudo crontab -l
* * * * * env > /tmp/env.output
* * * * * /usr/bin/php -f /var/www/html/example/cron/cron.php
* * * * * /usr/bin/php -f /var/www/html/demo/cron/cron.php
0 23 * * * rm /tmp/cachegrind.out.*


Meta information about my
addcron.sh
file:

[ec2-user@ip-172-31-xx-xxx nginx]$ pwd
/etc/nginx
[ec2-user@ip-172-31-xx-xxx nginx]$ ls -al addcron.sh
-rwxr-xr-x 1 root root 129 Nov 24 22:16 addcron.sh


The contents of
addcron.sh
:

#!/bin/bash
custname="$1"
(crontab -l; echo \"* * * * * /usr/bin/php -f /var/www/html/$custname/cron/cron.php\" ) | crontab -


When I try to run this though, I get the following error:

errors in crontab file, can't install.
Array
(
[0] => "-":102: bad minute
[1] => errors in crontab file, can't install.
)


It seems like it doesn't like the
-
mark in
addcron.sh
, but my Google searches suggest this is correct. Also, I have tried adding
sudo
to the PHP's
exec
command, but then I just get the following error:

sorry, you must have a tty to run sudo


What am I doing wrong or missing, and why?

Answer

Side note: running cron jobs unrelated to the system admin under the root's account is a BAD IDEA from the security prospective. Install your crons under the nginx user.

The issue is caused by the escaping of the quotes in your bash script (which you can check directly in bash, piece by piece, btw):

> (crontab -l; echo \"* * * * * /usr/bin/php -f /var/www/html/$custname/cron/cron.php\" )
no crontab for username
"* ... <all kinds of filenames in here> ... /usr/bin/php -f /var/www/html/the_customer/cron/cron.php"

Without the quote escaping things work a bit better:

> (crontab -l; echo "* * * * * /usr/bin/php -f /var/www/html/$custname/cron/cron.php" )
no crontab for username
* * * * * /usr/bin/php -f /var/www/html/the_customer/cron/cron.php

You might want to add some protection against duplicating the entries, here's how crontab looks after a few invocations:

> crontab -l
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Tue Nov 24 20:54:10 2015)
# (Cronie version 4.2)
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Tue Nov 24 20:54:09 2015)
# (Cronie version 4.2)
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Tue Nov 24 20:54:09 2015)
# (Cronie version 4.2)
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Tue Nov 24 20:54:08 2015)
# (Cronie version 4.2)
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (- installed on Tue Nov 24 20:54:07 2015)
# (Cronie version 4.2)
* * * * * /usr/bin/php -f /var/www/html/the_customer/cron/cron.php
* * * * * /usr/bin/php -f /var/www/html/the_customer/cron/cron.php
* * * * * /usr/bin/php -f /var/www/html/the_customer/cron/cron.php
* * * * * /usr/bin/php -f /var/www/html/the_customer/cron/cron.php
* * * * * /usr/bin/php -f /var/www/html/the_customer/cron/cron.php
Comments