Systemd timers replacement for cron jobs, plus Weechat LetsEncrypt certificate renewal and distribution.


Systemd is here.  Like it or not, we’re being given a new tool and it’s time to learn how to use it.  Today, we address the systemd timers feature and it’s ability to implement the functions of cron.

This isn’t a beginner topic, this blog assumes you know the command line well and have at least some idea of how Linux boots up.  In addition to covering how to implement a timer, an example script will implement renewal and distribution of an updated LetsEncrypt certificate to multiple Weechat users running on a remote host or VM.

Digging right in: Systemd keeps it’s local configuration in /etc/systemd/system, so we’re going to work from that folder.

cd /etc/systemd/system

The first file we’ll create is the .timer file.  In my case, semidaily-timer.timer because I need to trigger twice a day.  If your timer might be used by multiple tasks, a generic name describing the period is appropriate.   My semidaily-timer.timer file contains:

Description=Semi Daily Timer (runs twice a day at 4:44am and 4:44pm)

OnCalendar=*-*-* 04,16:44:00


Hopefully the Description field is self explanatory.

The  [Timer] OnCalendar= field is a bit like crontab but simpler.  It’s made up of date fields separated by – and time fields separated by :.  There is a space between the date and time blocks.  The overall format is <year>-<month>-<day> <hour>:<minute>:<second>.

There can be an optional day of the week before the date, and seconds can include a decimal point to handle smaller time units.  The comma separated values in the hour field of the time block are indicating that our timer will trigger at both of the listed hours, 4am and 4pm.  The odd minute was chosen by a random number generator that rolled a “44”. A .. (dot dot) allows entering a range in a single field, and a / can be used to indicate a repeat interval.

Like cron, * is the wildcard and matches anything in that field as shown in the year, month, and day of my file. In addition, you can trigger based on day of the week.  For example OnCalendar=Mon *-*-* 01:00:00 would trigger at 1AM every Monday.  As usual, more details are in the man page. [1]

[Timer] links in our .target file (described next.)

Something to note is that AccuracySec= defaults to 1 minute so the timing is only accurate to the minute by default.[2]

The [Install] field indicates that this timer can be started only when the has been reached.  The man page has a lengthy list of possible targets [3] and your distro may create more as they desire. doesn’t guarantee networking is functioning so ideally we’d verify any connectivity needed in our script at run time.

Up next is the Target file.  Mine is called and contains:

Description=Semi Daily Timer Target

Again, the Description field is self explanatory.  the [Unit] StopWhenUnneeded= field simply says that systemd can not run this target if nothing uses it.  This file is just a bit of glue and we’re not going to use anything else that isn’t defaults so it’s short and sweet[4].

The final file is our service file.  Since mine is going to be used to run the LetsEncrypt certificate update, I call mine letsencrypt.service.  The contents are:

Description=checks letsencrypt cert and updates if needed.



[Unit] Wants= and [Install] WantedBy= are your .timer and .target files from above. [Service] Type=simple means the ExecStart= is the main process of the service and that there is no need to delay after launching it before running other systemd processes. ExecStart=/usr/local/sbin/ specifies our script to be run[5].

Lastly, we need to enable and start everything.

systemctl enable semidaily-timer.timer
systemctl enable letsencrypt.service
systemctl start semidaily-timer.timer

You can verify the setup started successfully with:

systemctl status semidaily-timer.timer

The output should be similar to:

● semidaily-timer.timer - Semi Daily Timer (runs twice a day at 4:44am and 4:44pm)
 Loaded: loaded (/etc/systemd/system/semidaily-timer.timer; enabled; vendor preset: enabled)
 Active: active (waiting) since Tue 2016-10-04 18:26:36 CDT; 43min ago

Oct 04 18:26:36 elm systemd[1]: Started Semi Daily Timer (runs twice a day at 4:44am and 4:44pm).

You can monitor that the timer triggers with:

journalctl -f -u letsencrypt.service

Active timers can be listed with:

systemctl list-timers

The LetsEncrypt certificate renewal script needs the various parameters at the top set, and to be saved in  /usr/local/sbin/  It assumes your weechat users are members of the group weechat on the remote host and that you’re running nginx locally on your Ubuntu Xenial computer and have followed the EFF certbot instructions linked in the references [6] to get your certificate. This script assumes you have passwordless key based ssh login on the remote host/VM while running as the unprivileged user. Weechat does not add users to a group by default, any mechanism that gets a list of users could be substituted for the group member list.

Of course the above systemd timer instructions work for any other task that you wish to automate on a schedule as well.

Update: Weechat 1.7 changes the fifo name from weechat_fifo_<pid> to just weechat_fifo.  Script revised.

The script is:

##run twice daily as root


#renew cert
letsencrypt renew

#test cert newer than flag?  yes, process, no, exit
if [[ /etc/letsencrypt/live/${DOMIAN}/flag -ot /etc/letsencrypt/live/${DOMIAN}/cert.pem ]]; then

  echo "Certificate updated."
  #update flag
  touch -r /etc/letsencrypt/live/${DOMIAN}/cert.pem /etc/letsencrypt/live/${DOMIAN}/flag

  #restart nginx
  systemctl restart nginx

  #copy keys to a place unprivileged user can read them
  cp /etc/letsencrypt/live/${DOMIAN}/fullchain.pem /home/${UNPRIVILEGEDUSER}/
  cp /etc/letsencrypt/live/${DOMIAN}/privkey.pem /home/${UNPRIVILEGEDUSER}/

  #temporarily drop privileges to ${UNPRIVILEGEDUSER}

  #mount ${REMOTEHOST} as ${UNPRIVILEGEDUSER} if needed (for sshkey)
  if [ -d ${REMOTEMOUNT}/home ]; then

  #for each user
  for u in $(ssh ${REMOTEUSER}@${REMOTEHOST} "getent group ${WEECHATGROUP}" | cut -f4 -d:| tr ',' ' '); do

    echo "Processing $u..."

    #concatenate the key into the file, overwriting the old file.
    cat /home/${UNPRIVILEGEDUSER}/fullchain.pem /home/${UNPRIVILEGEDUSER}/privkey.pem > ~/${REMOTEUSER}/home/$u/.weechat/ssl/relay.pem

    #use fifo to set to a dummy value then back to relay.pem to force a re-read
    ssh ${REMOTEUSER}@${REMOTEHOST} "sudo bash -c \"echo \\\"*/set \\\"%h/ssl/relay.self.pem\\\"\\\" > /home/$u/.weechat/weechat_fifo\""
    ssh ${REMOTEUSER}@${REMOTEHOST} "sudo bash -c \"echo \\\"*/set \\\"%h/ssl/relay.pem\\\"\\\" > /home/$u/.weechat/weechat_fifo\""


  #dismount ${REMOTEHOST}i if needed
  if [ "$mounted" = "false ]; then
    fusermount -u ${REMOTEMOUNT}

  #return to root (EOF must be at character 1)

  #cleanup temp files
  rm /home/${UNPRIVILEGEDUSER}/fullchain.pem
  rm /home/${UNPRIVILEGEDUSER}/privkey.pem



man systemd.unit(5)
^[1] man systemd.time(5)
^[2] man systemd.timer(7)
^[3] man systemd.special(5)
^[4] man
^[5] man systemd.service(5)

The above directions have been tested on Ubuntu 16.04 with systemd 229.


OpenOCD for programming nRF51822 via nothing but wires and a Raspberry Pi

nRF51822 BoardThe Hardware here is simple and cheap, it can be done with any Pi, though I used a Pi Zero.  For me, it was free because I had the Pi Zero and the wire.

The default pins are:

Ground                        to Ground
3.3V power              to 3.3V power
Pi header pin 22  to swclk
Pi header pin 18   to swdio

At the time this was written, OpenOCD didn’t have bcm2835gpio in the main release so we git clone it.
The debugger/programmer install is:

cd ~/Downloads
sudo bash
apt-get install libtool libusb-dev libusb-1.0 autoconf automake texinfo
git clone git:// openocd-code
cd openocd-code/
./configure --enable-sysfsgpio --enable-bcm2835gpio
make install

The raspberrypi-native.cfg didn’t have SWD support, so add them from raspberrypi2-native.cfg.  I needed to comment out:

#bcm2835gpio_jtag_nums 11 25 10 9

and to add:

# Each of the SWD lines need a gpio number set: swclk swdio
# Header pin numbers: 22 18
bcm2835gpio_swd_nums 25 24

The peripheral base address is different on some Pi models.  You can check yours by grabbing the middle 8 digits from:

hexdump -s4 -n4 -e '"0x" 4/1 "%02X""\n"" "' /proc/device-tree/soc/ranges

Edit it with:

vi /usr/local/share/openocd/scripts/interface/raspberrypi-native.cfg

Check to be sure your default speed is 700Mhz, or adjust bcm2835gpio_speed_coeffs:

cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq

To run openocd:

openocd -f interface/raspberrypi-native.cfg -c "transport select swd; set WORKAREASIZE 0" -f target/nrf51.cfg

If it worked correctly, the last line should read (nothing before this indicates you successfully connected):

Info : nrf51.cpu: hardware has 4 breakpoints, 2 watchpoints

To connect from your development platform:

telnet pi0 4444

To burn the code, we need two files, in my case both are on a different machine (host) so I use sshfs to mount that.

mkdir ~/host
sshfs user@host:/ ~/host

Since the path is rather long, I break it in two and store into variables. This is the proximity example from the SDK:

set BASEPATH ~/host/home/user/Downloads/nRF5_SDK_11.0.0_89a8197
set SOFTDEV $BASEPATH/components/softdevice/s130/hex/s130_nrf51_2.0.0_softdevice.hex
set HEXFILE $BASEPATH/examples/ble_peripheral/ble_app_proximity/pca10028/s130/armgcc/_build/nrf51422_xxac_s130.hex
#halt, erase, write softdevice and binary, verify both, run it.
halt; nrf51 mass_erase; sleep 500; flash write_image $SOFTDEV 0; flash write_image $HEXFILE 0; verify_image $SOFTDEV 0; verify_image $HEXFILE 0; reset run

At this point, your sniffer or phone or whatever should see the Nordic_Prox device.

Fedora 23 on Raspberry Pi 2 or 3

Raspberry Pi 2B

Installing Fedora 23 on the Pi2 or 3 is pretty reasonable with a little effort and worth the trouble.  A real distro may not be exactly snappy on the Pi3 but the advantage of a familiar environment with a real browser makes up for a little slowness. Something to note, activating WiFi is not included here, so have a wired connection please.

If you came in from Redit or StackExchange or similar, please upvote my posting there so that others will see this page too.

pi 2B Terminal I’m going to assume you have the ability to write a Raspberry image to SD card with some confidence, to issue basic commands in a terminal session and that you have root access on a Linux machine to work with. My apologies to those with more skills but I’m targeting as wide an audience as possible. These instructions were tested using copy and paste from each of the code blocks below. If you’re unable to copy and paste, please pay careful attention to word-wrap because some of the lines are rather long.

I started with Jon Archer’s directions along with the steps in the comments plus a missing instruction that was critical to my install; with his directions the USB mouse and keyboard don’t work until you upgrade the Pi’s firmware images. Here I avoid the issue by downloading the firmware .zip file rather than cloning the firmware repository.

Download your interface of choice from

In my case, I used Mate in hopes of a reasonable trade off between resources and capabilities. My choice would be:


Download errors happen so before wasting a ton of time on a bad image, compare the checksum in to the output of:

sha256sum Fedora-Images-armhfp-23-CHECKSUM

The mirrors I used were all throttled and the only torrent I found had only 2 seeders, so I recommend doing the following download in parallel in an additional terminal session:

wget -O ~/Downloads/

SD Card Before issuing the commands below, make sure you know exactly where your sdcard is and that your device options match the device.  A good way to find it is to insert the card and run:

dmesg | tail

Some likely names include /dev/sda, /dev/sdb, and /dev/mmcblk0.

I didn’t have a desktop with Windows, IOS or Linux, just the Pi2 running Raspbian, so I used a SD to USB adapter which appears as /dev/sda.  Make sure all the partitions on your SD card are not mounted and unmount them if needed.  Mine had one partition auto-mounted so I unmounted that with the following command:

umount /dev/sda1

This command, like just about everything below, requires root.

The next step was to copy the image to SD with the following command (again edit the version number if needed):

xzcat Fedora-Mate-armhfp-23-10-sda.raw.xz |dd of=/dev/sda bs=1M;sync

The image write took 16 minutes for me, so this might be a good time to get a fresh cup of coffee.

Change the partition type of partition 1 from 0x83 to 0x0B and expand the root partition to fill the disk with your preferred tools or with something like:

echo -e "p\nt\n1\nb\nd\n3\nn\np\n3\n`fdisk -l /dev/sda|tail -1|tr -s ' '|cut -d' ' -f2`\n\np\nw\n"|fdisk /dev/sda; sync; partprobe

Replace the ext3 filesystem with a fat32 one using:

mkfs.vfat /dev/sda1

Expand the root partition to fill the disk with:

e2fsck -f /dev/sda3
resize2fs /dev/sda3

Mount the partitions with:

mkdir /mnt/sdcard
mount /dev/sda3 /mnt/sdcard
mount /dev/sda1 /mnt/sdcard/boot

Extract the boot and module files from the raspberry firmware archive downloaded earlier:

unzip ~/Downloads/ firmware-master/boot/* -d /mnt/sdcard/boot/
mv /mnt/sdcard/boot/firmware-master/boot/* /mnt/sdcard/boot/
rm -rf /mnt/sdcard/boot/firmware-master
unzip ~/Downloads/ firmware-master/modules/*-v7+/* -d /mnt/sdcard/lib/
mv /mnt/sdcard/lib/firmware-master/modules/*-v7+/ /mnt/sdcard/lib/modules/
rm -rf /mnt/sdcard/lib/firmware-master/

Edit /mnt/sdcard/etc/fstab, setting the boot partition type to vfat and replacing the UUID of the boot partition with the UUID from:

blkid -s UUID /dev/sda1

Note, vfat UUIDs really are much shorter.  It will be 8 hex digits with a dash in the middle.

Create the cmdline.txt file with:

echo "dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 \
root=/dev/mmcblk0p3 rootfstype=ext4 elevator=deadline rootwait"\

Create config.txt with something like:

cat </mnt/sdcard/boot/config.txt

The above settings force full HD over HDMI with no overscan, even if you boot while the monitor is off or disconnected.  Adjust as needed per the Raspberry PI docs, or pull the config.txt from your existing Pi setup.

Install rpi-update with:

wget -O /mnt/sdcard/usr/bin/rpi-update
chmod +x /mnt/sdcard/usr/bin/rpi-update

Adjust the root password by copying from an existing /etc/shadow file to /mnt/sdcard/etc/shadow.  The password is the long string between the 1st and 2nd : characters, or set it using the following commands:

read -s -p "Enter password: " pass
echo -n root:$pass | chpasswd -c SHA512 -R /mnt/sdcard

Finish up this phase of the install with:

umount /mnt/sdcard/boot
umount /mnt/sdcard

Install the SD card in your Pi2 and boot.

At this point, your Pi2 pi might come up to the Fedora first boot screen but the mouse and keyboard don’t work.  The problem will probably be resolved by running rpi-update.

Get the DHCP address from your router or DHCP server and ssh into the Pi2 as root.  (if you’re doing all this from your Pi2 like I did, you can reasonably guess that the address won’t change.)  In my case, this was the only “desktop” I had so I used ConnectBot from my phone.

Install binutils so that rpi-update can run:

dnf install binutils

Update your Fedora install and Pi2 firmware with:

dnf update

Reboot with the new firmware and the mouse/keyboard should work:

init 6

Complete the Fedora firstboot menus.

At this point, you have a basic functioning Fedora desktop.

If sound is not working in 23, try this:
cat </etc/modules-load.d/snd_bcm2835.conf
#load Raspberry Pi sound module
modprobe snd_bcm2835

See my next post about what to do after install to make a more complete desktop Fedora system with multimedia.