Fortnightly cron jobs
#351 Henry, Tuesday, 01 August 2017 3:22 PM (Category: Linux)
(Tags: linux cron)

In the League Website that I run, the players requested a fortnightly league - start and stop on Mondays and run for two weeks.

Up till now, I've only every had weekly events or monthly events. I stop and start the events by running scripts from cron. Weekly and monthly are very easy to set up in cron. Turns out fortnightly is not so easy.

Weekly events are easily started with a cron job like this:

59 16 * * 1 $HOME/wgl/bin/weekly_start.py -l15 >> $HOME/wgl/bin/weekly.log 2>&1

My first approach was to think about it and try a few things myself. I failed.

So I searched online for ideas from others. StackOverflow and ServerFault had plenty of ideas about running test in cron. I experimented with it, but found it hard to specify which Monday to start on. It kept starting on the Monday after the one I wanted to start on. The logic was difficult to understand, it was hard to restart and manually fiddle with it. So I kept looking for a simpler solution.

Eventually, I found the SysTutorials website that had a couple of methods of doing it. First, using test in the crontab again. The second way immediately interested me.

They called a shell script every week, and that script would test if it was the first or second week by the existence of a small dotfile. If it was not there, run the rest of the script and create the dotfile. If it was there, delete the dotfile and exit. Very neat, very elegant, and you could set which week to start it on by either changing the logic slightly or touching the dotfile. So I set up cron to do this:

59 16 * * 1 $HOME/wgl/bin/skyfall_start.sh

And the start script looked like this:

#!/bin/bash

# a file marking the state on disk
mark_file=$HOME/wgl/bin/.skyfall-start

# check whether the job run last time it is invoked
if [ -e $mark_file ] ; then
    rm -f $mark_file
else
    touch $mark_file
    exit 0
fi

# Run the league start
$HOME/wgl/bin/fortnight_start.py -l34 >> $HOME/wgl/bin/skyfall.log 2>&1

That worked and got me started. But I thought about it some more and decided I could simplify this a lot. I don't really need a shell script to go in-between cron and the Python fortnight_start.py script. I could build that logic directly into the Python script.

So I simplified cron to look like this:

59 16 * * 1 $HOME/wgl/bin/fortnight_start.py -l34 >> $HOME/wgl/bin/skyfall.log 2>&1

and added this code to the start of the fortnight_start.py Python script:

#!/usr/bin/env python

import sys
import os

home = os.getenv("HOME")
markfile = "%s/.skyfall_start" % (home)

if os.path.isfile(markfile):
    os.remove(markfile)
else:
    fh = open(markfile, 'a')
    fh.close()
    sys.exit(0)

# Rest of the Python to start the league event

That's the simple version without any error checking or exceptions. And for my purposes, I probably won't use any as I control the environment and it works well enough, and if anything goes wrong, my players will rapidly alert me to it.

Having done that, now I can see how to easily create three-week events or two-month events or five-day events or any sort of crazy event. This opens up a lot of possibilities.

I like how the control is a dotfile that I can touch to create, or rm to delete for manual fiddling. That lets me easily restart or switch around events. For non-binary events (example one week on, two weeks off), I could extend it by considering the contents of the dotfile.

0 comments