61

I tried to set up a root cron job to run a Bash script as root, to run at minute 7,37, every hour, every day of month, every month. This script is located in /usr/bin and named tunlrupdate.sh. It updates the DNS of Tunlr.

$ ls -l /usr/bin/tunlrupdate.sh 
-rwxr-xr-x 1 root root 2133 Sep 24 15:42 /usr/bin/tunlrupdate.sh

This Bash script is available here.

When invoked the script writes what's happening in a log located in /var/log/tunlr.log

To add this root cron job I used the standard for root's crontab

sudo crontab -e

And inserted these 2 lines at the end. I expect cron to run the script as root.

# check for updated Tunlr DNS every 30 minutes at the hour + 7 mn and hour + 37 mn
07,37 * * * * root /usr/bin/tunlrupdate.sh

A later command sudo crontab -l confirmed that the cron job has been inserted.

I did reboot Ubuntu and was checking in the log file if the cron job was launched properly. However there is nothing in the logfile /var/log/tunlr.log meaning the job was never successfully launched.

I did check that if I run the script from the command line

sudo /usr/bin/tunlrupdate.sh

then the logfile is updated accordingly.

Why is this cron job not running as planned in my system?

UPDATE 1 : All the proposed solutions so far do not work. I thank Olli for a CLI to list the system log sudo grep CRON /var/log/syslog. However I did get a CRON error

CRON[13092]: (root) CMD (  [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ]
&& find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php
/maxlifetime) ! -execdir fuser -s {} 2>/dev/null \; -delete)

with the suggested PATH= insertion & use of absolute path from root for functions in the script or without this suggested solutions here. I still get this error.

After some searching I pinpointed the error in the file /usr/lib/php5/maxlifetime as is explained here: Change #!/bin/sh -e --> #!/bin/sh -x

Then listing the CRON error log in my system

sudo grep CRON /var/log/syslog
Feb 11 18:07:01 Marius-PC CRON[14067]: (root) CMD (root /usr/bin/tunlrupdate.sh)
Feb 11 18:07:01 Marius-PC CRON[14066]: (root) MAIL (mailed 1 byte of output; but got
status 0x00ff, #012)

I still don't get the bash script executing. This time no error is shown in the log. To get assurance this was not the content of the script I reduced the script to the following 3 lines:

#!/bin/bash
LOGFILE=/var/log/tunlr.log
echo $LOGFILE >> $LOGFILE

I still don't get the cron job through. Nothing is written in the log file. So even may be an empty script will not run in cron ? I don't get it. I am know trying a script reduced to these 2 lines:

#!/bin/bash
exit 0

And still the same error log. The cron script does not go through ...

Zanna
  • 70,465
Antonio
  • 1,594
  • If you want it to bee a "root" cronjob you have to be root then you type crontab -e. Also you just have to login as root (console type "su root") and then crontab -e (sudo is not needed in this case). – Wolfgang Feb 11 '14 at 18:05
  • Well. I don't see the point of your answer ? Typing $ sudo crontab -e did the job as reported by $ sudo crontab -l , i.e. the line describing the new job has been added to the root's cron. As per se the job is not present in the user cron e.g. $ crontab -l do not show any cron job was added here. – Antonio Feb 11 '14 at 18:44
  • @WolfgangVogl he used "sudo" which works as expected. – Alexis Wilke Feb 11 '14 at 18:45

6 Answers6

106

If you want to run a script as a normal user:

crontab -e

And add the line:

07,37 * * * * /usr/bin/tunlrupdate.sh

If you want to run your script as root:

sudo crontab -e

And add the same line:

07,37 * * * * /usr/bin/tunlrupdate.sh
Zanna
  • 70,465
Guillaume
  • 2,101
  • @NineCattoRules If you don't drop the output, what do you see? – Angelo Fuchs May 15 '17 at 07:47
  • @AngeloFuchs old comment...surely I tried that command as root (sudo crontab -e instead of crontab -e). Or something else, anyway it works – NineCattoRules May 15 '17 at 16:53
  • But the crontab file will be in a temporary file? Or I am missing an automatic copy? Because the file edited with sudo crontab -e is clearly in /tmp – Sandburg Dec 15 '22 at 11:06
13

Well, at last the working solution. In the syslog I saw the repetitive and intriguing:

CRON[18770]: (root) CMD (root /usr/bin/tunlrupdate.sh)

That sounds like root was not recognized as a cmd. As I already used the root's cron by using $ sudo /usr/bin/tunlrupdate.sh. Then I tried with the original script (corrected for a mistake in the date UNIX cmd : %m which is month was used for minutes which is %M) the following (which removes the root from the cron line):

$ sudo crontab -e
# check for updated Tunlr DNS every 30 minutes at the hour + 7 mn and hour + 37 mn
07,37 * * * * /usr/bin/tunlrupdate.sh

This turned out to be the final solution. [Although I found scores of literature stating the erroneous line with root in the cron line. That was a mistake].

Antonio
  • 1,594
  • Good point Olli. I agree with you on that. For reference the user crontab is stored in /var/spool/cron/crontabs/user-name or for root /var/spool/cron/crontabs/root . See this page http://askubuntu.com/questions/216692/where-is-the-user-crontab-stored The containing folder already is and gives the name of the user. – Antonio Feb 12 '14 at 17:52
  • Well, you shouldn't need that information, as you only should edit crontab files with crontab command (except crontab files under /etc). – Olli Feb 13 '14 at 17:16
  • 1
    @Antonio the scores with the username field are only used in /etc/crontab (the system-wide crontab). Using sudo crontab -e you're working with root's crontab which usually can be found in /var/spool/cron/crontabs – Matijs Jul 19 '16 at 21:55
5

One "problem" with cron is that lack of environment variables (for obvious security reasons). You are probably missing PATH and HOME. You can define those in the script directly or in the crontab file.

# check for updated Tunlr DNS every 30 minutes at the hour + 7 mn and hour + 37 mn
PATH=/usr/bin
07,37 * * * * root /usr/bin/tunlrupdate.sh

You'll have to test until all the necessary variables are defined as required by the script.

Alexis Wilke
  • 2,707
  • 1
    This is the only answer here that actually worked for me. I copied the SHELL and PATH statements from the /etc/crontab file and pasted them into the sudo crontab -e and the command ran as root without a problem. Thank you! – Terrance Apr 21 '17 at 15:23
  • 1
    yes! That was most likely the issue with my own cron issues :) thanks a lot! – David 天宇 Wong Oct 04 '20 at 16:21
  • What are these obvious security reasons? What risks do I take by adding the PATH variable to my root crontab? – Joooeey Jun 30 '22 at 14:49
  • @Joooeey When you define your own variables, it is considered safe. By default, CRON offers no environment variables to make it safer. Many people think that they would get the same variable as found in their usual shell. There could be passwords or keys or some other secret in those variables and CRON doesn't want to leak them to your CRON jobs. – Alexis Wilke Jun 30 '22 at 15:01
2

Cron error messages are usually - by default - sent via email. You can check whether there's email for root with sudo mail, or by just checking contents of /var/mail/root, e.g sudo less /var/mail/root.


If email messages does not help, also check /var/log/syslog:

sudo grep CRON /var/log/syslog

As Alexis Wilke already said, cron have different mechanism for setting environment variables.

Your script needs

PATH=/sbin:/bin:/usr/bin

to the crontab. HOME shouldn't be necessary. You should use absolute paths in your scripts, e.g /bin/date instead of date. You can find proper paths for each command with which command_name, e.g

$ which date
/bin/date
Olli
  • 8,971
  • Running your suggested grep CRON in /var/log/syslog I got the foolowing

    Feb 11 14:37:01 Marius-PC CRON[7826]: (root) CMD (root /usr/bin/tunlrupdate.sh)

    Feb 11 14:37:01 Marius-PC CRON[7825]: (root) MAIL (mailed 1 byte of output; but got status 0x00ff, #012)

    Feb 11 14:39:01 Marius-PC CRON[7849]: (root) CMD ( [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null ; -delete) If I add some definitiion for the PATH in cron these will not affect my system $PATH ?

    – Antonio Feb 11 '14 at 20:11
  • That output says it tried to email something but failed. That means, you are not getting that error message to /var/mail/root. You can either fix that, or try with PATH=... – Olli Feb 11 '14 at 20:14
  • @Antonio, alternatively, use this patched version – Olli Feb 11 '14 at 20:18
  • Thanks. I know I didn't configure system e-mail and was aware that didn't go through. I already had modified the script to use absolute path for any function called. The last thing was my previous question a PATH definition within crontab will not mess my system $PATH variable ? – Antonio Feb 11 '14 at 20:37
  • No it doesn't. That's completely separate thing. – Olli Feb 11 '14 at 20:39
  • 1
    In the mean time I was busy correcting a bug in the date format from the original script. It was using %m (which is for month NOT minute) instead of %M ... – Antonio Feb 11 '14 at 20:40
  • After modifying the script and adding the PATH= I still get the same error (root) CMD ( [ -x /usr/lib/php5/maxlifetime ] && [ -d /var/lib/php5 ] && find /var/lib/php5/ -depth -mindepth 1 -maxdepth 1 -type f -cmin +$(/usr/lib/php5/maxlifetime) ! -execdir fuser -s {} 2>/dev/null ; -delete) – Antonio Feb 11 '14 at 21:14
  • That's not an error. It's a different cron job, namely, php cleanup. It is not related to your issue. – Olli Feb 11 '14 at 22:00
0

You state in your question that you reduced the script to:

#!/bin/bash
LOGFILE=/var/log/tunlr.log
echo $LOGFILE >> $LOGFILE

I have had problems with this before. It turned out to be the first line. Instead of #!/bin/bash I tried #!/bin/sh and that worked (for me, at least).

Eliah Kagan
  • 117,780
  • This might have been because the bash interpreter was in a different location. Use which bash to find out where bash lives, and use that path in your shebang. – daviewales Feb 17 '22 at 00:05
0

You can add this line in your script. So after you check the cron logs and comprove your job was executed you can get the the same $PATH of crontabs had.

/bin/echo $PATH > /root/path.txt

And probably the best thing you can do to diagnose issues in cron scripts is get all the environment variables of SO with env command in your script. So just add this line to your script. Then you can analyze the output allEvnVars.txt

/usr/bin/env > /root/allEvnVars.txt

Another trick is to direct the output of the script to some place. Adding the /root/log.log. This way all the output of the script will be maintained in /root/log.log

07,37 * * * * root /usr/bin/tunlrupdate.sh  > /root/log.log

Also you can schedule the script to run each min to facilitate the tests and checks.

*/1 * * * * root /usr/bin/tunlrupdate.sh  > /root/log.log
Zanna
  • 70,465