LinkedIn Sourceforge Twitter

Vincent's Blog

Pleasure in the job puts perfection in the work (Aristote)

A cron for laptops

Posted on 2018-05-21 12:17:00 from Vincent in OpenBSD Desktop vdcron

I was looking for a simple cron like tool that help me to perform tasks on one specific day on my laptop and desktops.

Unfortunately the embedded cron in OpenBSD is not foreseen for workstations or laptop that we shutdown (or hibernate) regularly.


I was looking for a tool that help me to execute tasks on a specific day, in some cases with some recurrences principles. With some specific care, a cron can restart failed jobs, but cron does not trigger tasks for which the triggered time was during a shutdown or an hibernate.

Since my laptop is not working 24x7 I missed several executions simply because the machine was not running at the specified timing.

Moreover, my laptop is not always connected to internet. So, some tasks which required internet were falling. I will it restart it until successfulness.

Should I mention that the goal is to run on OpenBSD ? (If you follow my blog here, you have understood that all my machines are OpenBSD).

Investigation on existing tools

As often in such case I check on internet and search for existing tools offering the requested features.

Anacron and fcron were 2 of the most promising tools.

Unfortunately Anacron is no more updated since several years. Moreover it seems to only work for root account (to verify).

fcron is much regularly updated. But the number of features offered are amazing high. My needs are must simpler. For example, I don't want to replace my cron. Implementing fcron, is like a big machinery for a such simple task (in my case).

My own script

Since my needs are limited and simple, why not writing a small shell script ?

After 1 hour effort I had a ksh script which was perfect for my needs:

  • execute a script once per day until it is successful
  • a simple user can use it. No need to be root.
  • I have a simple log describing what has been made and when.

You can download it from my source forge repository: vdcron

To facilitate my task, the script is triggered by apmd after each resume. I describe this here bellow.

Evolution of this simple script

Finally, days after days, the script becomes more and more efficient with exiting new features.

I've taken some interesting feature existing in cron, like:

  • possibility to define global variables
  • use the flag "-q" to avoid logging
  • use the "/d" (d is a digit) to repeat the execution. For example in cron you can repeat a tasks every 5 minutes by putting */5 in the minutes column. Similarly you can repeat a tasks every 5 hours if you put */5 in the adequate column.
  • Since I'm triggering this script at each resume (cfr apmd), it's important that the script wait a little bit before triggering the commands. For example, we first be sure that network connections are correctly re-established. A waiting time of 20 seconds is thus defined by default.

My proposed solution: vdcron

vdcron is first of all a cron. The main reasons I've created this one are the following:
- Having a very lite cron routine, in generic code like shell
- Be able to run it with root account, but also with any other account
- Designed to run on non-permanent machine like laptops and desktops. In other words, be able to run commands scheduled in a moment where the machine what not running.
- Be able to re-run commands having failed, until they are successful.
- We keep a trace of all execution, and the output of it, in the associated log files.
- No dependencies on other tools than what we can find in a standard OpenBSD install

Like many cron, you can use it to run commands at a specific moment: day, hour, minute. But you can also use it to run a command every Monday, or every 10 days, every odd days, every hour, every 5 hour, each Monday at 10AM, every months, every 2 months, … Nothing different than usual cron.

What makes vdcron special? :
- vdcron is not planned to run permanently as a daemon, but an external program will trigger Vdcron. It can be you normal cron, it can be your .profile script, it can be your .login script. In my case vdcron is triggered by the resume process of apmd. Thus each time I open the lid of my laptop, vdcron is triggered and execute commands that he must run.
- Since vdcron is not a daemon, the config file and the schedule are a little bit different from what we are used to use in cron programs.


vdcron accept the following optional parameters.

Options are:

-h : this help

-d : debug

-s /value/: force an initial sleep different than the default: 10 seconds

-f /file/ : use a specific input file


Vdcron relies on 1 config file. This config file is /etc/vdcron.conf if you run it with a root account, or it will be ~/.vdcron/vdcron.conf for each other users.

The basic concept of the config file is that, like in a queue, each line represent a command that we have to trigger if the schedule timing is compliant with the current date and time. For example, if you ask vdcron to run a command on Monday, vdcron will not run it until we are a Monday. Once successfully executed the command will be removed from the file. This is why I’m talking about the queuing principle. This aspect is not what we are used to have in “normal” cron systems.
If I take a specific case, you can ask vdcron to run a command at Noon on May 1st. In such case, vdcron will execute it if we are between 12:00:00 and 12:00:59. Before it’s too soon, after it’s too late.
Since vdcron is foresee to run on non-permanent machines, such concept, with a very strict timing, (with hour and minute) is not the usual case for vdcron. We prefer situations where we specify the day. We expect to have the command executed on that day, and we are less demanding for the timing.

The command part can be a command with his parameters, but is can also be a list of commands (split by “;”) or a command that you want to launch in background (add “&” at the end). In the last case, the return code of the command will not be used, vdcron will estimate that the job is successful. Thus, background commands will never be restarted if they fails.

The config file must respect some conventions:

/date/ /command and parameters/ the 2 elements must be separated by one space

/date/ can be :

  • A date with the form [+]YYYYmmdd[/d] or [+]HHMM[/d] or [+]YYYYmmddHHMM[/d]
    In case you put the "+" sign in front this means that the command will run even we are after this specific date
    In case you append with a "/" and digit(s), the command will be re-scheduled automatically for later.
    The digits represent the number of time unit you want to add to the current moment.
    This unit is always the last one used by the timing associated. Thus for a schedule with HHMM and YYYYmmddHHMM, the digits you have specified will add minutes.
    For YYYYmmdd, you will add days.

  • A locale's full weekday name with optionally re-schedule digits. For example: Monday/7.
    As explained above, in this case, the rescheduling unit for will be in days.

  • A locale's full month name with optionally re-schedule digits. For example: April/1.
    As explained above, in this case, the rescheduling unit will be in months

  • Always. With this keyword your command will always been executed when you trigger vdcron

/command/ can be preceded with "-q ". In such case there will be no logging at all. Without this parameter the results of the command (and some other info) are stored in the log file: ~/.vdcron/vdcron.log for standard users and /var/log/vdcron.log for root.
If the exit code is not zero "0", the line will not be removed from the config file. In other words, vdcron will retry until successful execution. This is useful for script requiring network connections, which you could not always have (surely with laptops).
As consequence is you always finalize your command by the command “false”, the command will always remain in the vdcron.conf file. This is like with the “Always” keyword.

You can also define some environment variable by respecting the following structure:

“var” should be alpha characters. “value” can be alpha, numeric or alphanumeric.
Such settings is exactly like you do in your usual shell scripts. Please do not put spaces before or after the sign “=” or before the “var”.

All lines, in the config file, which do not respect those formats will be treated as a comment. By looking at the log files, you can identified them easily.

Thus, since, commands been successfully executed will be removed from the config file, adding some comments or keeping a copy of your line preceded by a “#” character will allow you to easily remember it.

Config file examples

20180429 echo "Last weekend of April"

As explained, the 1st word is the schedule requirement, all the rest is your command.
In this case, we ask to execute on 2018, April the 29th the command that will echo the funny text “Last weekend of April”. All of this will be present in the log file.

+20180429/1 sh /etc/daily

In this case we ask vdcron to execute on 2018 April 29th, or after, the /etc/daily script. Moreover, if the return code of “/etc/daily” is zero (successful), we ask vdcron to reschedule it 1 day after.
If the command “/etc/daily” return a non-zero return code, the line will remain in the config file untouched.

+20180429/1 sh /etc/daily; true

With this line in the config file, we enforce the fact that command will run once, and only once, per day because the return code will always be zero. As said, above, if the scheduled date is in the past (in case the machine was down more than 1 day), the command will be triggered anyhow.

Sunday sh /etc/weekly_once
+20180429/7 sh /etc/weekly
Sunday/7 sh /etc/weekly

Those are 3 “weekly” command. Except that the 1st one will run only once; the 2 others will be rescheduled.
The 2 others will run every 7 days. Since Sunday + 7 days is also Sunday, vdcron will add a “_” at the beginning of the word. If we are Sunday, this sign allow vdcron differentiate a task to execute from a task to no more execute. Vdcron will remoce this special character when needed. You don’t have to remove it your self.
The difference between the last 2 entries, is that the 2nd one will be executed even if we trigger vdcron after the specified date. With the 3rd line, if you never trigger vdcron on Sunday, you will have to wait next Sunday for an execution.

Always echo "heartbeat"
+20180429/1 echo “heartbeat”;false

The “Always” keyword informs vdcron to run the command every time vdcron is started.
The 2nd line has the same effect. Indeed, each time the command will return a non zero return code, so vdcron will keep the command on the config file and will trigger it at each run.

April sh /etc/monthly_once
+20180401/31 /etc/monthly
April/1 /etc/monthly

Those are 3 methods to run a monthly command. The 1st line will be execute only once, because not rescheduled. The 2 others will be rescheduled just after correct execution of the command. If you are not sure and want to avoid that such monthly script will be triggered each time vdcron run, you can add “; true” at the end of each line.
On the 2nd line vdcron will re-schedule the command 31 days after 1st of April 2018. So, this will be rescheduled for 2nd of May. Then, 2nd of June 2018, then 3rd of July, … On the opposite, the 3rd line will always be reschedule for the next month, without any specification for the day.
Like for week day’s name, vdcron will add a “_” sign at the beginning to mark script already successfully executed.

a Real config file

I remind that in my implementation, I trigger vdcron via /etc/apm/resume (at each lid open)

My personal config file for root is the following:

$ doas more /etc/vdcron.conf                                                 
+20180509/1 sh /etc/daily &
20180512/7 sh /etc/weekly &
Always /usr/local/bin/nmctl -r -w 5 -R 5 >> /tmp/nmctl.log
Always su - vi -c "/usr/local/bin/vdcron -s3 "

With this config, I run my /etc/daily once per day in background. I run the weekly script every Saturday (or later). If I do not open my machine on a Saturday, I accept to run it on Sunday or Monday or … I always run my Network Manager script (details here ). And I always trigger vdcron for my own user: vi.

In my vi’s home directory, I have the following vdcron.conf file

$  more ~/.vdcron/vdcron.conf                                                 
+20180509/1 openboxmenuCreate &
+20180511/7 sh ~/bin/

In my case, I run once per day a python script I’ve made to recreate my openbox’s menus.
On a weekly basis (ideally on Friday), I sync my local files to my NAS. If I’m not connected to my NAS vdcron will keep it in the config file. Moreover, in this case, I accept to run it after the Friday or later (because sign “+”)

How to trigger vdcron ?

As said, to my eyes, the ideal is to trigger the execution of vdcron at each lid's open of my laptop.
To do that I’ve added the following file in /etc/apm:

cat /etc/apm/resume
/usr/local/bin/vdcron &

And I’ve activated apm:

rcctl enable apmd
rcctl start apmd

Maybe you will prefer to trigger vdcron at each reboot. In such case, just add the vdcron in your /etc/rc.local or in your normal cron with the keyword “@reboot”.
For specific cases, you could imagine to trigger vdcron from your .login script of from your .profile script.
Why not putting triggering vdcron from your cron every hour ?

It’s really up to you to decide when, how often, … you want to trigger vdcron. The ideal solution will depend on what you want to execute with vdcron.


This is a very small script, but it help me to solve several actions in my day to day.

Currently I'm using it since several weeks with lot of fun when I see the usage of it.

Do not hesitate to improve it and share your changes.

Also feel free to send me your comments about such tool. Is it useful for you too ?

32, 31
displayed: 5737

1. From Mat on Tue May 22 17:12:49 2018

Hi Vincent, haven't looked at the code yet, but does it provide duplicate detection or avoidance? Example: If you schedule "sh /etc/daily" to be run tomorrow and sleep/resume 5 times a day, does it schedules 5 "/etc/daily" jobs for tomorrow or only one? Regards

2. From Vincent on Tue May 22 19:13:14 2018

Hello Mat, if you schedule a taks for tomorrow, each time you execute vdcron it will see that today is not the correct day, so it will skip it. Tomorrow, it will see that timing is matching, so he will execute it. If the return code is "0", vdcron will remove it from the taks's list or reschedule it automatically. If the return code is not "0", the task will remain in the config file. So it will be triggered each time you launch vdcron (each time you suspend/resume).

What is the first letter of the word Python?