# Lab 7: Scheduling tasks

### Task 1: Create cron jobs

- Let’s create an example script `job.sh`. We can check `log.txt` at any time to see whether our scheduled job has run. Create this script in your home directory

      #!/bin/bash
      echo `date +"%Y-%M-%d %T"`" - Hello world" >> /var/log/log.txt

Adding the job to the user crontab

- To understand the user crontab, let’s add the script to it manually

      $ crontab -e

- This command will open an editor to edit the existing user crontab. Let’s append our cron expression:

  > Replace the `<username>` with your username

      30 0 * * * /home/<username>/job.sh

  This schedules the script to run every day, 30 minutes after midnight.
- We also need to be sure that the current user has execute permissions for this script. So, let’s use the chmod command to add them:

      $ chmod u+x /home/<username>/job.sh

  Now, `job.sh` is scheduled and will run every day. We can test this by inspecting the `log.txt` file

Adding the job to the system crontab
To understand the system crontab, let’s also add this script to it manually.

- The system crontab file is kept in `/etc/crontab`. Let’s append the following line:

      30 0 * * * root /home/<username>/job.sh

  We should note that we need to specify the root username. This is because jobs in system cron are system jobs and will be run by the root user.

Script for adding the job to the user crontab
Now let’s try automating the process to add to the user crontab. Install a new file to crontab.

- Let’s first create a new script file:

      $ touch /home/<username>/myScript.sh

- The first thing our script will do is take a copy of all the current jobs. Do not forget to add executable rights for the script to work

      #!/bin/bash
      crontab -l > crontab_new

  We now have all the previous jobs in the `crontab_new` file. This means we can append our new job to it and then rewrite the crontab by using the edited file as an input argument to the crontab command:

      $ echo "30 0 * * * /home/<username>/job.sh" >> crontab_new
      $ crontab crontab_new

- Since the `crontab_new` file is temporary, we can remove it:

      $ rm crontab_new

  This method works well, though it does require the use of a temporary file. The main idea here is to add multiple tasks to the existing user jobs. Let’s see if we can optimize it further.

Optimize the previous script by using a pipe
Our previous script relied on a temporary file and had to tidy it up. It also didn’t check whether the cron entry was already installed, and thus, it could install a duplicate entry if executed multiple times.

- We can address both of these by using a pipe-based script. If crontab command has a dash `-`, the crontab data is read from standard the input

      #!/bin/bash
      (crontab -l; echo "30 0 * * * /home/<username>/job.sh") | sort -u | crontab -

  As before, the `crontab -l` and `echo` commands write out the previous lines of the crontab as well as the new entry. These are piped through the sort command to remove duplicate lines. The `-u` option in `sort` is for keeping only unique lines.
  The result of this is piped into the `crontab` command, which rewrites the crontab file with the new entries.
  We should be aware, though, that using sort will completely reorder the file, including any comments. `sort -u` is pretty easy to understand in a script, but we can achieve a less destructive de-duplication with awk:

    #!/bin/bash
    (crontab -l; echo "30 0 * * * /home/<username>/job.sh")|awk '!x[$0]++'|crontab -

This will remove all duplicates from the crontab without sorting it.

- The syntax of the awk command used is explained below:
  - `a[$0]` - uses the current line `$0` as key to the array, taking the value stored there. If this particular key was never referenced before, `a[$0]` evaluates to the empty string.
  - The `!` negates the value from before. If it was empty or zero (false), we now have a true result. If it was non-zero (true), we have a false result. If the whole expression evaluated to true, the whole line is printed as the default action print `$0`
  - `++` increment the value of `a[$0]`

Using system crontab

- First, let’s create a new script:

      $ touch /home/$USER/myScript2.sh

  The syntax of the system schedule line is similar to the user schedule. We just need to specify the root username in the schedule line

      #!/bin/bash
      sudo /bin/bash -c 'echo "30 0 * * * root /home/<username>/job.sh" >> /etc/crontab'

  We’re using `sudo /bin/bash` before `echo` because the user needs root access to both echo and redirect as the root user. Otherwise, we’ll get a permission denied error because just echo will run as root and the redirection will be made with the current user’s permission. The `-c` option tells bash to get the command in single quotes as a string and run it in a shell.
  Note that this is plain file manipulation, compared with the `crontab` command used earlier. We can add similar filters like sort or awk if we want to avoid duplicate entries.

Using the /etc/cron.d directory
Besides the `/etc/crontab` path, cron considers all the files in the `/etc/cron.d` directory as system jobs too. So, we can also put the schedule line in a new file in the `/etc/cron.d` directory.

- Let’s now make another script for adding a job to the `cron.d` directory, as an alternative to the `/etc/crontab` file:

      $ touch /home/<username>/myScript3.sh

  We need to put the schedule line in a new file in the cron.d directory — we’ll call our file schedule. Note that in `/etc/cron.d`, some filenames are considered invalid. For example, if we choose `schedule.sh` for the filename, it will be skipped because the filename should not have any extension:

      #!/bin/bash
      sudo touch /etc/cron.d/schedule

  The `cron.d` directory and its sub-directories are usually used by system services, and only the root user can have access to these directories. Also, the files in `/etc/cron.d` must be owned by root. So, we need to use sudo.

- Let’s now add our schedule line to the schedule file and change the permissions.

      $ sudo /bin/bash -c 'echo "30 0 * * * root /home/<username>/job.sh" > /etc/cron.d/schedule'
      $ sudo chmod 600 /etc/cron.d/schedule

  Note that we change the file’s permissions to a minimum `600`. This is because files in `/etc/cron.d` must not be writable by group or other. Otherwise, they will be ignored. Also, the schedule files under `/etc/cron.d` do not need to be executable. So, we don’t need permission `700`.

# Task 2: `at` command

`at` is a command-line utility that allows you to schedule commands to be executed at a particular time. Jobs created with `at` are executed only once.

- Install `at` from the repository

      $ sudo apt install at

- Once the program is installed make sure `atd`, the scheduling daemon is running and set to start on boot:

      $ sudo systemctl enable --now atd

- The simplified syntax for the at command is as follows:

      $ at [OPTION...] runtime

- Let’s create a job that will be executed at 9:00 am:

      $ at 09:00

  Once you hit Enter, you’ll be presented with the `at` command prompt that most often starts with `at>`. You also see a warning that tells you the shell in which the command will run:

      warning: commands will be executed using /bin/sh
      at>

  Enter one or more command you want to execute:

      at> tar -xf $HOME/file.tar.gz

  When you’re done entering the commands, press `CTRL+D` to exit the prompt and save the job:

      at> <EOT>
      job 1 at Mon Oct 17 09:00:00 2022

- There are also other ways to pass the command you want to run, besides entering the command in the at prompt. One way is to use echo and pipe the command to at:

      $ echo "command_to_be_run" | at 09:00

- Another option is to use the redirect

      $ at 09:00 <<END
      command_to_be_run
      END

- To read the commands from a file instead of the standard input, invoke the command with -f option following by the path to the file. For example, to create a job that will run the script `$HOME/script.sh`:

      $ at 09:00 -f $HOME/script.sh

## Questions to answer

> Upload the scripts you create to moodle.
> Show test results of all implementation in your report.

1. Create backup for any directory with files inside.

   - Create a cron job which backs up the directories at the 5th day of every month.
   - Create an anacron job that backs up the directories daily. The anacron job should delete old backups.

2. Install nginx and create a cron job that backs up the directory that contains `index.html`.

   - The backup should occur at midnight every Sunday.
   - The job should delete old or previous backups.

3. Create cron jobs that appends the current time and a descriptive information about the job to the log file at `/var/log/sna_cron.log`. You should meet the following requirements:

   - Use `/bin/bash` to run commands instead of the default `/bin/sh`
   - Schedule the following jobs:
     - Run five minutes after midnight, everyday
     - Run at 10:00 on weekdays
     - Run at 04:00 every Monday
     - Run on the second saturday of every month.

   > Example output written to the log file when the first job executes is shown below

   >     14-10-22 00:05:00 Run five minutes after midnight

### Bonus

4. How can cron jobs be abused?
   - Give one specific real life example where cron job was abused.
   - Provide details about the job that was scheduled which led to the abuse. Details should include job execution frequency, command/script scheduled, and the objective(s) of the job.
   - Show the job you are describing. For example `* * * * * /var/tmp/.ICE-unix/-l/sh >/dev/null 2>&1`

     > Be brief in your explanations. Answer this question with a maximum of six sentences.