Files
literate-dotfiles/systemd.org

13 KiB

systemd services and timers

https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units

braingit - Automatic commit and push new brain pages

Service

[Unit]
Description=Sync Brain Website everyday
RefuseManualStart=no
RefuseManualStop=yes
OnFailure=notify-via-gotify@%i.service

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
Type=oneshot
ExecStart=%h/.local/bin/brain_git_push

Timer

[Unit]
Description=Sync Brain Website everyday
RefuseManualStart=no
RefuseManualStop=no
Wants=network-online.target
After=network-online.target

[Timer]
Persistent=false
OnBootSec=30min
OnUnitActiveSec=1d
Unit=braingit.service

[Install]
WantedBy=default.target

Script

cd /home/thomas/Cloud/programming/brain-website/

if [[ ! -z $(git status -s content/) ]]
then
    git add content static && \
        git commit -m "Update Content - $(date +%F)" && \
        git push
    exit
fi

notify-via-gotify - Notify

Service

[Unit]
Description=%i failure notification over gotify

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
Type=oneshot
ExecStart=/bin/bash -c "systemctl status -n10 %i | ~/.local/bin/create-gotify-notification-for-systemd --unit %i --host %H | curl -XPOST \"https://gotify.tdehaeze.xyz/message?token=$(pass nas/gotify_notif_token | sed -n 1p)\" -H \"Content-Type: application/json\" --data-binary @-"

Script

import json
import argparse
import sys


TITLE_TEMPLATE = "Systemd unit failed"

MESSAGE_TEMPLATE = "Systemd unit {unit} failed on host {host}."


def main():
    arguments = parse_arguments()
    status = "\n".join("    " + line.rstrip() for line in sys.stdin.readlines())
    message = MESSAGE_TEMPLATE.format(unit=arguments.unit, host=arguments.host, status=status)
    title = TITLE_TEMPLATE.format(unit=arguments.unit, host=arguments.host)
    output = create_gotify_json(title, message, arguments.priority)
    print(output)


def parse_arguments():
    parser = argparse.ArgumentParser(
        description="Create Gotify notification for failing systemd unit",
    )
    parser.add_argument(
        "--unit",
        default="unknown",
        help="the failing systemd unit name",
    )
    parser.add_argument(
        "--host",
        default="unknown",
        help="the failing systemd unit host",
    )
    parser.add_argument(
        "--priority",
        default="5",
        type=int,
        help="the notification priority",
    )

    return parser.parse_args()


def create_gotify_json(title, message, priority):
    data = {
        "title": title,
        "message": message,
        "priority": priority,
        "extras": {
            "client::display": {
                "contentType": "text/markdown"
            },
        },
    }

    return json.dumps(data)


if __name__ == '__main__':
    main()

checkmail - Check new mails

Service

[Unit]
Description=Check new mails
RefuseManualStart=no
RefuseManualStop=yes
OnFailure=notify-via-gotify@%i.service

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
Type=oneshot
ExecStart=%h/.local/bin/checkmail -q

Timer

[Unit]
Description=Check Mail every x minutes
RefuseManualStart=no
RefuseManualStop=no
Wants=network-online.target
After=network-online.target

[Timer]
Persistent=false
OnBootSec=2min
OnUnitActiveSec=5min
AccuracySec=2min
Unit=checkmail.service

[Install]
WantedBy=default.target

Script

while [ -n "$1" ]; do # while loop starts
    case "$1" in
        -a) opt_all='--all' ;; # Check All inboxes
        -v) opt_verbose='--verbose' ;; # Verbose
        -q) opt_quiet='--quiet' ;; # Quiet
        ,*) echo "Option $1 not recognized" ;; # In case you typed a different option
    esac
    shift
done

# Count number of new mails before retreiving mails
gmail_old="$(ls ~/.local/share/mails/gmail/Inbox/new | wc -l)"
esrf_old="$(ls ~/.local/share/mails/esrf/Inbox/new | wc -l)"

# Retreive mails
mbsync -c /home/thomas/.config/isync/mbsyncrc $opt_all $opt_verbose gmail-Home esrf-Home 2>/tmp/mbsync.log

# Count number of new mails after retreiving mails
gmail_new="$(ls ~/.local/share/mails/gmail/Inbox/new | wc -l)"
esrf_new="$(ls ~/.local/share/mails/esrf/Inbox/new | wc -l)"

# Notification if there are new retreive mails
if [ "$(($esrf_new+$gmail_new))" -gt "$(($esrf_old+$gmail_old))" ]; then
    dunstify --replace=98465 'Mails ' "$(($gmail_new+$esrf_new)) new mail(s)"
fi

# Indexation and Tags
mu index $opt_verbose $opt_quiet

syncmail - Synchronize all mails

Service

[Unit]
Description=Sync all mails
RefuseManualStart=no
RefuseManualStop=yes
OnFailure=notify-via-gotify@%i.service

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
Type=oneshot
ExecStart=%h/.local/bin/checkmail -a -q

Timer

[Unit]
Description=Sync All Mails every x hours
RefuseManualStart=no
RefuseManualStop=no
Wants=network-online.target
After=network-online.target

[Timer]
Persistent=false
OnBootSec=30min
OnUnitActiveSec=300min
AccuracySec=10min
Unit=syncmail.service

[Install]
WantedBy=default.target

vdirsyncer - Synchronize calendar and contacts

Service

[Unit]
Description=Synchronize calendars and contacts
Documentation=https://vdirsyncer.readthedocs.org/
Wants=network-online.target
After=network-online.target
OnFailure=notify-via-gotify@%i.service

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
ExecStart=/usr/bin/vdirsyncer --verbosity "ERROR" sync
Type=oneshot

Timer

[Unit]
Description=Synchronize vdirs

[Timer]
OnBootSec=5m
OnUnitActiveSec=15m
AccuracySec=5m

[Install]
WantedBy=timers.target

syncthing - Synchronize Cloud directory

Service

[Unit]
Description=Syncthing - Open Source Continuous File Synchronization for %I
Documentation=man:syncthing(1)
After=network.target

[Service]
Environment="all_proxy=socks5://localhost:8080"
ExecStart=/usr/bin/syncthing --no-browser --gui-address="0.0.0.0:8384" --no-restart --logflags=0
Restart=on-failure
SuccessExitStatus=3 4
RestartForceExitStatus=3 4

[Install]
WantedBy=default.target

restic-backup - Backup Home Directory

Backup

Service

[Unit]
Description=Backup Home Directory
RefuseManualStart=no
RefuseManualStop=no
OnFailure=notify-via-gotify@%i.service

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
Type=oneshot
ExecStart=%h/.local/bin/restic-backup
ExecStartPost=/bin/sleep 30
ExecStartPost=%h/.local/bin/restic-forget

Timer

[Unit]
Description=Backup Home Directory Everyday
RefuseManualStart=no
RefuseManualStop=no
Wants=network-online.target

[Timer]
Persistent=true
OnCalendar=daily
Unit=restic-backup.service

[Install]
WantedBy=default.target

Script - Backup

restic backup \
-r sftp:thomas@homelab:/srv/storage/Backups/esrf-laptop \
    --password-command "pass show restic" \
    --verbose --one-file-system --tag systemd.timer \
    --exclude "/home/thomas/.cache" \
    --exclude "/home/thomas/.local/data/docker" \
    --exclude "/home/thomas/.local/soft" \
    --exclude "/home/thomas/Cloud" \
    --exclude "/home/thomas/mnt" \
    /home/thomas

Script - Forget

restic unlock \
    -r sftp:thomas@homelab:/srv/storage/Backups/esrf-laptop \
    --password-command "pass show restic" && \
    restic forget \
    -r sftp:thomas@homelab:/srv/storage/Backups/esrf-laptop \
    --password-command "pass show restic" \
    --verbose --tag systemd.timer \
    --group-by "paths,tags" \
    --keep-daily 7 \
    --keep-weekly 4 \
    --keep-monthly 1 \
    --keep-yearly 1

Prune

Service

[Unit]
Description=Prune restic backup
RefuseManualStart=no
RefuseManualStop=no
OnFailure=notify-via-gotify@%i.service

[Service]
Environment="PASSWORD_STORE_DIR=/home/thomas/.local/share/pass"
Environment="GNUPGHOME=/home/thomas/.local/share/gnupg"
Type=oneshot
ExecStart=%h/.local/bin/restic-prune

Timer

[Unit]
Description=Prune Restic Backup
RefuseManualStart=no
RefuseManualStop=no
Wants=network-online.target

[Timer]
Persistent=true
OnCalendar=monthly
Unit=restic-prune.service

[Install]
WantedBy=default.target

Script

restic prune \
    -r sftp:thomas@homelab:/srv/storage/Backups/esrf-laptop \
    --password-command "pass show restic" \

homelab-tunnel - SSH Tunnel

Useful to bypass firewalls. This can we used on the browser:

  • for qutebrowser, use :set content.proxy socks5://localhost:8080 (can setup a shortcut for that)

This is also used for Syncthing.

Service

[Unit]
Description=Setup a secure tunnel with homelab
After=network.target

[Service]
ExecStart=/usr/bin/ssh -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -D 8080 -q -N -T homelab

# Restart every >2 seconds to avoid StartLimitInterval failure
RestartSec=5
Restart=always

[Install]
WantedBy=default.target

esrf-tunnel - SSH Tunnel

Service

[Unit]
Description=Setup a secure tunnel with ESRF
After=network.target

[Service]
ExecStart=/usr/bin/ssh -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -D 8081 -q -N -T firewall.esrf.fr

# Restart every >2 seconds to avoid StartLimitInterval failure
RestartSec=5
Restart=always

[Install]
WantedBy=default.target