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
). Herename
, may be anything relevent to your job andunit
must be one ofservice
,socket
,device
,mount
,automount
,swap
,target
,path
,timer
andsnapshot
. 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.mountHere
Description
is a general description for your service. We need to put the required unit which will trigger this new unit inRequires
field. Systemd will run this unit once all the units inRequires
fields satisfied. We can call theseRequires
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 haveAfter
field. If you define which parent unit needs to be completed before your child unit could run, you need to mention it inAfter
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, UsingAfter=
field, I instructed systemd, not to start this child unit beforemount-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.shThis
[Service]
class is specific tosystemd.service
units. Here,ExecStart=/home/mohan/Development/scripts/backup.sh
asks systemd to runbackup.sh
wheneverbackup.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 usedType=simple
to tell systemd, that no need to do any change in execution environment. The scriptbackup.sh
will take care of all the redirection within itself.
Install Class
In SysV init system, we use
chkconfig (redhat/fedora)
orupdate-rc.d (debian)
to enable or disable a service. In systemd, we use this[Install]
class to enable or disable ourbackup.service
unit so that it will work even after a restart. Here is install class for backup.service,[Install] WantedBy=media-ExternalBackupPartition.mountIn 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 withsystemctl enable backup.service
orsystemctl 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 inWantedBy=
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-reloadI 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 scriptbackup.sh
exists in the location pointed byExecStart=
field.$ sudo systemctl start backup.serviceThis 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.serviceWe 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.serviceIf we don’t want to copy automatically, we can disable it using below command,
$ sudo systemctl disable backup.serviceEven 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 asmedia-uuid.mount
(systemd uses-
instead of/
for path seperation). But specifyingRequires=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.dThis above two commands, will modify udisk’s mount path to
/media/ExternalBackupPartition
instead of/media/251c683d-bce0-489c-aab5-f684a9a1f3b2
, thus systemd will automatically createmedia-ExternalBackupPartition.mount
instead ofmedia-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.