Systemd for Simple Backup

Hi,

Nice to get back, Recently switched to F15. Wow!! my userland changed heavily. Previously, its very simple things like,

SysVinit for booting
Udev for devices
pppconfig for Network
Xfce for GUI
ALSA for sound
ffmpeg & mplayer for video

Now,

Systemd for booting, daemons
Udev, DBus, UDisks for devices
ModemManager+NetworkManager for Network
Gnome3 for GUI
PulseAudio+ALSA for sound
GStreamer+totem for video

These Technologies are very interesting to learn. I’m not going to explain about each. But, I went through systemd and came up with a solution for my backup problem.

Backup Problem:

While in its last phase, my previous laptop teached me the importance of external backup disks. So, I bought an external 512GB Segate, and backed-up /home/${HOME} tree. It helped me to quickly get back my files to F15. However, one problem is, maintaining my backup. I thought If I plug my external drive, someone should automatically copy all the new files resides in my current /home/${HOME} in F15 to that drive. There are lot of ways to do it. I can think of two main ways,

* Write an udev rule to call a script which will mount that external backup partition and rsync /home/${HOME} in F15 to that external partition. Pros: Fairly straight forward, udevrulefile+rsyncscript will do it. Cons: No control, this will copy every time udev detects that external drive. If there is lot of files to copy, then this will make a mess.

* Use systemd to call a script whenever that external backup partition gets mounted. Pros: You have full control, create a backup.service for systemd, create backup.bash to rsync /home/${HOME} to backup disk. Enable that service in systemd to automatically do rsync, or just load that service to systemd and only start that service If you want to backup your files. Cons: Need to pass one more layer to run the actual rsync script.

So, I took-up systemd

There are lot of ways you can trigger your service in systemd. Also, you can depend on another systemd unit to trigger your service. In this case, I depend on a mount unit to trigger my backup.service. This service, in-turn, will trigger backup.sh script.

To define a systemd unit, you need to know what type of unit you want to create. Currently there are 10 types of units systemd can understand [read systemd.unit(5)]. For backup job, I used two type units, one is systemd.mount(5) amd systemd.service(5). If you go through systemd.mount(5) you will understand that systemd will automatically load this units whenever systemd saw a block device. So, systemd will automatically provide ‘media-ExternalBackupPartition.mount’ whenever I insert my external hard disk . I only need to define the next unit, backup.service for systemd.

Note: There is a story behind the name ‘media-ExternalBackupPartition’. I’ll tell you at the end of this post. Lets just continue with systemd for now.

To define a unit for systemd, you need to create a file as /etc/systemd/system/name.unit; (for my backup problem, unit file is /etc/systemd/system/backup.service). Here ‘name’, may be anything relevent to your job and ‘unit’ must be one of ‘service’, ‘socket’, ‘device’, ‘mount’, ‘automount’, ‘swap’, ‘target’, ‘path’, ‘timer’ and ‘snapshot’. Read systemd.unit(5) man pages for more precise information.

Unit Class:
Unit definition files contains information in .ini format. One of the class ‘[Unit]’ must exist in every unit file. Here is the [Unit] class for backup.service

[Unit]
Description='sync my files with external backup drive'
Requires=media-ExternalBackupPartition.mount
After=media-ExternalBackupPartition.mount

Here ‘Description’ is a general description for your service. We need to put the required unit which will trigger this new unit in ‘Requires’ field. Systemd will run this unit once all the units in ‘Requires’ fields satisfied. We can call these ‘Requires’ units as parent units, and your unit as child. However, systemd will not wait for parent units to complete to run child unit. We need to explicitly ask systemd to wait for parent units to complete, For this purpose we have ‘After’ field. If you define which parent unit needs to be completed before your child unit could run, you need to mention it in ‘After’ field.

For my backup.service child unit, ‘media-ExternalBackupPartition.mount’ unit must be satisfied in systemd. That means, my external HD partition must be mounted inside ‘/media/ExternalBackupPartition’ path. Also, Using ‘After=’ field, I instructed systemd, not to start this child unit before ‘mount-ExternalBackupPartition.mount’ finishes.

Service Class:

Now we need to define what to do once the requirements satisfies. For that purpose, we need to define ‘[Service]’ class, Here is the service class for backup.service,

[Service]
Type=simple
ExecStart=/home/mohan/Development/scripts/backup.sh

This ‘[Service]’ class is specific to ‘systemd.service’ units. Here, ‘ExecStart=/home/mohan/Development/scripts/backup.sh’ asks systemd to run ‘backup.sh’ whenever ‘backup.service’ satisfies. You can do lot of customization to setup the execution environment before start running any commands, such as log redirection, demonizing etc., there are lot of fields to use in ‘[Service]’ class, but I simply used ‘Type=simple’ to tell systemd, that no need to do any change in execution environment. The script ‘backup.sh’ will take care of all the redirection within itself.

Install Class:
In SysV init system, we use ‘chkconfig (redhat/fedora)’ or ‘update-rc.d (debian)’ to enable or disable a service. In systemd, we use this ‘[Install]’ class to enable or disable our ‘backup.service’ unit so that it will work even after a restart. Here is install class for backup.service,

[Install]
WantedBy=media-ExternalBackupPartition.mount

In systemd, enabling a service means, adding a symlink to ‘/etc/systemd/system/name.service.wants/’ directory. disabling a service means, removing that symlink. ‘systemctl’ command can do this add/remove symlink automatically when we call it with ‘systemctl enable backup.service’ or ‘systemctl disable backup.service’, but we need to say the parent unit name, thats why we have this ‘[Install]’ class. Simply, we need to mention that parent unit in ‘WantedBy=’ field.

Execution:

Once the unit files are ready, we need to enable them into systemd. For backup.service, I executed following commands to setup the service

$ sudo cp ~/backup.service /etc/systemd/system/backup.service
$ sudo systemctl daemon-reload

I just copied backup.service unit file to systemd’s location and asked systemd to reload unit definitions. Now we can check if things loaded properly or not using following command,

$ sudo systemctl status backup.service
backup.service - 'sync my files with external backup drive'
          Loaded: loaded (/etc/systemd/system/backup.service)
          Active: inactive (dead)
          CGroup: name=systemd:/system/backup.service
$

systemd will say ‘Loaded : error’ if it can’t understand any defnintion in backup.service or if it can’t satisfy the definitions. Otherwise we can start this service using following command, we need to make sure the final rsync script ‘backup.sh’ exists in the location pointed by ‘ExecStart=’ field.

$ sudo systemctl start backup.service

This will start syncing new files to External backup HD drive, only when It is plugged-in and mounted. Otherwise, the service will fail. you can check the status again using ‘systemctl status’. Once you checked that the service is working as intended, we can enable this service (I mean, creating symlinks) using following command,

$ sudo systemctl enable backup.service

We can verify the symlink as below to make sure parent-child linking is done correctly.

$ ls -l /etc/systemd/system/media-ExternalBackupPartition.mount.wants/baskup.service
lrwxrwxrwx 1 root root 34 Nov  9 02:08 /etc/systemd/system/media-ExternalBackupPartition.mount.wants/backup.service -> /etc/systemd/system/backup.service

If we don’t want to copy automatically, we can disable it using below command,

$ sudo systemctl disable backup.service

Even If the service is disabled, you can start/stop the service. Systemd will recognize the backup.service, check it’s dependencies and execute ‘backup.sh’ correctly.

media-ExternalBackupPartition.mount:

As I already said, systemd will automatically create units using udev, so when my external HD plugs-in, udev will tell to udisks that a new partition is available, then udisks will mount that partition inside ‘/media/uuid’ location, then systemd will create a unit as ‘media-uuid.mount'(systemd uses ‘-‘ instead of ‘/’ for path seperation). But specifying ‘Requires=media-uuid.mount’ inside backup.service file is not working. Thus, I used a simple udev rule to rename udisk’s mount path, here is the rule file,

$ cat 99-rename-udisk-mountpoint.rules
ENV{ID_FS_UUID}=="251c683d-bce0-489c-aab5-f684a9a1f3b2",ENV{ID_FS_UUID}="ExternalBackupPartition"
$ sudo cp ~/99-rename-udisk-mountpoint.rules /etc/udev/rules.d

This above two commands, will modify udisk’s mount path to ‘/media/ExternalBackupPartition’ instead of ‘/media/251c683d-bce0-489c-aab5-f684a9a1f3b2′, thus systemd will automatically create ‘media-ExternalBackupPartition.mount’ instead of ‘media-251c683d-bce0-489c-aab5-f684a9a1f3b2.mount’

Finally, Thanks for reaching this line. I hope this long boring article will help you to understand something about systemd.

Advertisements