Softpanorama

Home Switchboard Unix Administration Red Hat TCP/IP Networks Neoliberalism Toxic Managers
May the source be with you, but remember the KISS principle ;-)
Skepticism and critical thinking is not panacea, but can help to understand the world better

Scheduling tasks using at and cron


Introduction

On a Linux system, some tasks have to be automated on a regular basis. It would be one option to configure each process with a process-specific solution to handle recurring tasks, but that would not be efficient to deal with. That is why on Linux the cron service is used as a generic service to run processes automatically at specific times.

The cron service consists of two major components. First, there is the cron daemon crond.  and then crontab command which manages the list of task  is specified in cron configuration file. They can be listed with crontab -l command The latter is re-read automatically each time you change it with crontab -e command.

This daemon is invoked every minute and  reads and analyzes its configuration  file to determine  is any job needs to be run

Cron configuration file has a unique  structure with five fields preceding each command. Those five fields  constitutes time specification and define  minute, hour, day_of_the_month, month, day_of_the_week that the job  will be executed. In this particular order.

In Linux cron configuration, like configuration on many other services became quite Byzantium and completely un-transparent and  the task can be specified in  three different places, two of which are never listed by crontab -l command which supposedly should list all jobs  for the particular user.  It is important to understand that crontab -l lists only tasks specified via crontab -e command.

So each RHEL sysadmin needs to write a script that lists all relevant sources to understand how cron  service on his particular server behaves. In the simplest form it should be

crontab -1
ls /etc/cron.d /etc/cron.hourly cron.daily cron.weekly cron.monthly

Yes another complication is that cron is now is managed by  systemd, but  in comparison with  other daemons manual intervention is seldom required and  this is a minor problem.

Although I once did encountered a server with RHEL6 in which it periodically died and that created strange and difficult to troubleshoot effects on monitoring

There is also another  daemon called atd which provides "one time" execution of tasks. It allows you to specify execution of scripts of binaries at any desired time.  This daemon configuration is controlled by three command at, atq and atrm.

Another face of the  same daemon is represented by the batch command which allow to specify multiple jobs in sequential queues. Around 20 queues are available. 

Managing the cron service using systemctl

The cron service is started by default on every RHEL system. The service is needed because some system tasks are running through cron as well. An example of these is logrotate, a service that cleans up log files and runs on a regular basis, but other important maintenance processes are started automatically through cron also.

Usually the cron service does not need any management. Where other services need to be reloaded or restarted to activate changes to their configuration, this is not needed by cron. The cron daemon wakes up every minute and checks its configuration to see whether anything needs to be started.

To monitor the current status of the cron service, you can use the systemctl status crond -l command.

[0]d620@ROOT:/etc/cron.d # systemctl status crond -l
? crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2019-03-22 14:26:50 EDT; 3 weeks 6 days ago
 Main PID: 3416 (crond)
   CGroup: /system.slice/crond.service
           mq3416 /usr/sbin/crond -n

Mar 22 14:26:50 d620 systemd[1]: Started Command Scheduler.
Mar 22 14:26:50 d620 crond[3416]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 54% if used.)
Mar 22 14:26:51 d620 crond[3416]: (CRON) INFO (running with inotify support)

The most significant part of the output of the systemctl status crond command is in the beginning: It mentions that the crond service is loaded and that it is enabled as well.

The fact that the service is enabled means that it will automatically be started whenever this service is restarting.

The last part of the command shows current status information. Through the journald service, the systemctl command you can find out what is actually happening to the crond service.

Understanding crontab table

NOTE:

When scheduling services through cron, you need to specify when exactly the services need to be started. In the crontab configuration (which is explained more in depth in the next section), you use a time string to indicate when tasks should be started.

The sequence of fields in crontab table is listed in the file /etc/crontab

[0]d620@ROOT:~ # cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed
  * 11  *  *  *    logger -p INFO `date`  # Each minute between 11:00 and 11:59 (probably not what you want)
  0 23  *  * 1-5   logger -p INFO `date`  # Every day at 11 a.m. on weekdays only
  0 7-18 * * 1-5   logger -p INFO `date`  # Every hour between 7 a.m. and 6 p.m. on weekdays on the hour
  0 */2  2 12 5    logger -p INFO `date`  # Every 2 hours on the hour on December second and every Friday in December

The command  logger -p INFO  `date` is very convenient for debugging as it allow to verify that you specified sequence of invocation is correct.

In any of these fields, you can use an * to refer to any value. Ranges of numbers are allowed, as are lists and patterns. Some examples are listed next:


There are thee major ways to add a script to cron for root:

  1. Using the command crontab -e
  2. Adding files to /etc/cron.d in the format described above  with the six field after fifth that specifies the user under which the job  will be running.  For example:
    [0]d620@ROOT:/etc/cron.d # cat rear
    30 1 * * * root /usr/sbin/rear checklayout || /usr/sbin/rear mkrescue
  3. Adding scripts to the directories /etc/cron.hourly, cron.daily, cron.weekly, and cron.monthly

NOTE

Anacron directories

The last way to schedule cron jobs is through the following directories:

In these directories, you typically find scripts that are put in there from RPM package files. When opening these scripts, notice that no information is included about the time when the command should be executed. That is because the exact time of execution does not really matter. The only thing that does matter is that the job is launched once an hour, day, week, or month.

To ensure regular execution of the job, cron uses the Anacron extension. This extension takes care of starting the hourly, daily, weekly, and monthly cron jobs, no matter at which exact time. To determine how this should be done, Anacron uses the /etc/anacrontab file.

[1] d620@ROOT:~ # cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command
1                 5                  cron.daily       nice run-parts /etc/cron.daily
7                 25                 cron.weekly      nice run-parts /etc/cron.weekly
@monthly          45                 cron.monthly     nice run-parts /etc/cron.monthly

In /etc/anacrontab, the jobs to be executed are specified in lines that contain three fields. The first field specifies the frequency of job execution, expressed in days. The second column specifies how long anacron waits before executing the job, and in the last part is the command that should be executed.

User specific cron jobs

Cron jobs can be started not only for root but  for any users. To create a user-specific cron job, type crontab -e after logging in as that user, or as root type crontab -e -u username

NOTE:

Example of a scheduling  a task via cron

In this exercise, you apply some of the cron basics. You schedule cron jobs using different mechanisms.

  1. Open a root shell. Type cat /etc/crontab to get an impression of the contents of the /etc/crontab configuration file.
  2. Type crontab -e. This opens an editor interface that by default uses vi as its editor. Add the following line (here we assume that the current time is 11:20 and you schedule execution three minutes into the future):
    23 11 * * 1-5 logger -INFO I am alive
  3. Use the vi command :wq! to close the editing session and write changes.
  4. Use cd /etc/cron.hourly. In this directory, create a script file with the name chronoline that contains the following line:
    logger -p INFO "This message is coming from the script chronoline cron.hourlys"
  5. Use chmod +x chronoline to make the script executable.
  6. Now enter the directory /etc/crond.d and in this directory create a file with the name  chronoline  Put the following line (here 11 is assumed to the hour you are doing this exercise):
    */5 11 * * * root logger -p INFO Five minutes timebeat
  7. Save the modifications
  8. In 5-10 minutes or longer , type grep timebeat /var/log/messages and read the messages that have been written, which verifies correct cron operations.

One time jobs scheduling: configuring atd daemon

Whereas cron is used to schedule jobs that need to be executed on a regular basis, the atd service is available for services that need to be executed only once. On RHEL 7, the atd service is available by default, so all that needs to be done is scheduling jobs.

To run a job through the atd service, you would use the at command, followed by the time the job needs to be executed. This can be a specific time, as in at 14:00, but it can also be a time indication like at now or  at noon.

You can also specify offset from now, like

 at now + 30 minutes

to submit command to at commend you can use 3 methods.

After scheduling jobs with at, you can use the atq command (q for queue) to get an overview of all jobs currently scheduled. Without any parameteers it lists all the jobs. With a numeric parameter (representing job ID) option -V instructs atq command to list time the job will be executed before reading the job on the console.

[1] d620@ROOT:/etc/cron.d # atq -V 1
at version 3.1.13
1       Thu Apr 18 16:43:00 2019 a root

To list the content of the job you should use at -c <job number> command, for example 

at -c 1

you will see that job includes setting the environment that exist at themoment of submission  of the job. The likes you entered are at the very bootom of this listing.

[1] d620@ROOT:/etc/cron.d # at -c 2
#!/bin/sh
# atrun uid=0 gid=0
# mail root 0
umask 22
XDG_SESSION_ID=677; export XDG_SESSION_ID
HOSTNAME=d620; export HOSTNAME
PREVPWD=/etc/cron.d; export PREVPWD
SELINUX_ROLE_REQUESTED=; export SELINUX_ROLE_REQUESTED
SHELL=/bin/bash; export SHELL
HISTSIZE=1000; export HISTSIZE
SSH_CLIENT=192.168.5.14\ 56167\ 22; export SSH_CLIENT
SELINUX_USE_CURRENT_RANGE=; export SELINUX_USE_CURRENT_RANGE
OLDPWD=/root; export OLDPWD
PROMPT_DIRTRIM=3; export PROMPT_DIRTRIM
SSH_TTY=/dev/pts/0; export SSH_TTY
USER=root; export USER
LS_COLORS=rs=0:di=01\;34:ln=01\;36:mh=00:pi=40\;33:so=01\;35:do=01\;35:bd=40\;33\;01:cd=40\;33\;01:or=40\;31\;01:mi=01\;05\;37\;41:su=37\;41:sg=30\;43:ca=30\;41:tw=30\;42:ow=34\;42:st=37\;44:ex=01\;32:\*.tar=01\;31:\*.tgz=01\;31:\*.arc=01\;31:\*.arj=01\;31:\*.taz=01\;31:\*.lha=01\;31:\*.lz4=01\;31:\*.lzh=01\;31:\*.lzma=01\;31:\*.tlz=01\;31:\*.txz=01\;31:\*.tzo=01\;31:\*.t7z=01\;31:\*.zip=01\;31:\*.z=01\;31:\*.Z=01\;31:\*.dz=01\;31:\*.gz=01\;31:\*.lrz=01\;31:\*.lz=01\;31:\*.lzo=01\;31:\*.xz=01\;31:\*.bz2=01\;31:\*.bz=01\;31:\*.tbz=01\;31:\*.tbz2=01\;31:\*.tz=01\;31:\*.deb=01\;31:\*.rpm=01\;31:\*.jar=01\;31:\*.war=01\;31:\*.ear=01\;31:\*.sar=01\;31:\*.rar=01\;31:\*.alz=01\;31:\*.ace=01\;31:\*.zoo=01\;31:\*.cpio=01\;31:\*.7z=01\;31:\*.rz=01\;31:\*.cab=01\;31:\*.jpg=01\;35:\*.jpeg=01\;35:\*.gif=01\;35:\*.bmp=01\;35:\*.pbm=01\;35:\*.pgm=01\;35:\*.ppm=01\;35:\*.tga=01\;35:\*.xbm=01\;35:\*.xpm=01\;35:\*.tif=01\;35:\*.tiff=01\;35:\*.png=01\;35:\*.svg=01\;35:\*.svgz=01\;35:\*.mng=01\;35:\*.pcx=01\;35:\*.mov=01\;35:\*.mpg=01\;35:\*.mpeg=01\;35:\*.m2v=01\;35:\*.mkv=01\;35:\*.webm=01\;35:\*.ogm=01\;35:\*.mp4=01\;35:\*.m4v=01\;35:\*.mp4v=01\;35:\*.vob=01\;35:\*.qt=01\;35:\*.nuv=01\;35:\*.wmv=01\;35:\*.asf=01\;35:\*.rm=01\;35:\*.rmvb=01\;35:\*.flc=01\;35:\*.avi=01\;35:\*.fli=01\;35:\*.flv=01\;35:\*.gl=01\;35:\*.dl=01\;35:\*.xcf=01\;35:\*.xwd=01\;35:\*.yuv=01\;35:\*.cgm=01\;35:\*.emf=01\;35:\*.axv=01\;35:\*.anx=01\;35:\*.ogv=01\;35:\*.ogx=01\;35:\*.aac=01\;36:\*.au=01\;36:\*.flac=01\;36:\*.mid=01\;36:\*.midi=01\;36:\*.mka=01\;36:\*.mp3=01\;36:\*.mpc=01\;36:\*.ogg=01\;36:\*.ra=01\;36:\*.wav=01\;36:\*.axa=01\;36:\*.oga=01\;36:\*.spx=01\;36:\*.xspf=01\;36:; export LS_COLORS
MAIL=/var/spool/mail/root; export MAIL
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin; export PATH
PWD=/etc/cron.d; export PWD
LANG=en_US.UTF-8; export LANG
SELINUX_LEVEL_REQUESTED=; export SELINUX_LEVEL_REQUESTED
HISTCONTROL=ignoredups; export HISTCONTROL
SHLVL=1; export SHLVL
HOME=/root; export HOME
LOGNAME=root; export LOGNAME
SSH_CONNECTION=192.168.5.14\ 56167\ 192.168.5.23\ 22; export SSH_CONNECTION
LESSOPEN=\|\|/usr/bin/lesspipe.sh\ %s; export LESSOPEN
PROMPT_COMMAND=root_prompt; export PROMPT_COMMAND
XDG_RUNTIME_DIR=/run/user/0; export XDG_RUNTIME_DIR
cd /etc/cron\.d || {
         echo 'Execution directory inaccessible' >&2
         exit 1
}
${SHELL:-/bin/sh} << 'marcinDELIMITER379b732b'
echo timebeat

marcinDELIMITER379b732b

It is also possible to remove any scheduled at job using the atrm command followed by the number of the at job that you want to remove, for example

atrm 2

.

The batch command

The batch command works like at, but is more sophisticated. When using batch, you can specify multiple jobs that will will be executed sequentially one after another.  Of also can serve multiple queues.  The default queue for batch is b queue.  Each queue is denoted with a single letter and can be specified using option -q:

-q queue

uses the specified queue. A queue designation consists of a single letter; valid queue designations range from a to z. and A to Z. The a queue is the default for at and the b queue for batch. Queues with higher letters run with increased niceness. The special queue "=" is reserved for jobs which are currently running.

You also can specify the level of system load at which batch jobs are allowed to run. The default is when system load is lower than 0.8, which in most cases is a reasonable default value. 

If in your environment this value is too high or too low you can be specified manually when starting atd, using the -l command-line option.  If atd was already started you need to restart it via systemctl.

Managing access to cron daemon /etc/cron.allow and /etc/cron.deny configuration files

By default, all users can enter cron jobs. It is possible to limit which user is allowed to schedule cron jobs by using the /etc/cron.allow and /etc/cron.deny configuration files. If the cron.allow file exists, a user must be listed in it to be allowed to use cron. If the /etc/cron.deny file exists, a user must not be listed in it to be allowed to set up cron jobs.