From 5aff4e6a0661927776d85ce6d8843a90494b174c Mon Sep 17 00:00:00 2001 From: Akemi Izuko Date: Sat, 23 Dec 2023 20:14:09 -0700 Subject: [PATCH] Update: systemd-timers notes and examples --- notes/linux/systemd_timers.md | 13 +-- .../timer-scripts/template.set_a_records.sh | 81 +++++++++++++++++++ systemd/system/update_a_record.service | 12 +++ systemd/system/update_a_record.timer | 11 +++ systemd/system/ydotoold.service | 9 +++ 5 files changed, 121 insertions(+), 5 deletions(-) create mode 100755 systemd/system/timer-scripts/template.set_a_records.sh create mode 100644 systemd/system/update_a_record.service create mode 100644 systemd/system/update_a_record.timer create mode 100644 systemd/system/ydotoold.service diff --git a/notes/linux/systemd_timers.md b/notes/linux/systemd_timers.md index 0e0976b..232323d 100644 --- a/notes/linux/systemd_timers.md +++ b/notes/linux/systemd_timers.md @@ -13,6 +13,12 @@ Generally there are 3 pieces to a timer systemd-analyze calendar *-*-* *:10,20:10 ``` +Time is in the format: + +``` +DayOfWeek Year-Month-Day Hour:Minute:Second +``` + To find suitable targets for things like `Wants` and `After`, see `systemd.special(7)`. If you want to check the current status of your targets, use: @@ -28,8 +34,8 @@ Scripts should be put in `/usr/local/bin` if they can be run by anyone or `/usr/local/sbin` if they should only be run by root Define a `xxx.service` file in `/etc/systemd/system`. Set the timer as one of -its `Wants`. `network.target` may be more appropriate in some cases. -`multi-user.target` is a good default for `WantedBy` +its `Wants`. `network.target` may be more appropriate in some cases. Do not add +an `[Install]` section, as your timer already handles that ```systemd [Unit] @@ -41,9 +47,6 @@ After = network.target [Service] Type = oneshot ExecStart = /usr/local/bin/broadcast_ip.sh - -[Install] -WantedBy = multi-user.target ``` Now you'll need a timer file. It's easiest to make it have the same name as the diff --git a/systemd/system/timer-scripts/template.set_a_records.sh b/systemd/system/timer-scripts/template.set_a_records.sh new file mode 100755 index 0000000..d88eb4a --- /dev/null +++ b/systemd/system/timer-scripts/template.set_a_records.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +declare wan_ip_record wan_ip cf_records host_record cf_host_ip cf_rec_id + +declare -r HOST= +declare -r DOMAIN= +declare -r TOKEN= +declare -r ZONE_ID= + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Gετ WΛN IP | +#╚─────────────────────────────────────────────────────────────────────────────╝ +if ! wan_ip_record="$(host -W2 myip.opendns.com resolver1.opendns.com)"; then + echo "Hosts timed out" >&2 + exit 1 +fi + +wan_ip="$(echo "$wan_ip_record" | tail -n1 | awk '{ split($0, a, " "); print a[4] }')" + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Gετ Λ rεcδrd δη Clδμdflαrε | +#╚─────────────────────────────────────────────────────────────────────────────╝ +if ! cf_records="$(curl -s --request GET \ + --url https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records \ + --header 'Content-Type: application/json' \ + --header "Authorization: Bearer $TOKEN")" +then + echo "Failed to retrive cloudflare zone records" >&2 + exit 1 +fi + +for i in {0..4000}; do # Assuming 4000 is enough + record="$(echo "$cf_records" | jq --raw-output ".result[${i}].name")" + + if [[ "$record" == "${HOST}.${DOMAIN}" ]]; then + host_record="$(echo "$cf_records" | jq -r ".result[${i}]")" + break + elif [[ "$record" == null ]]; then + echo "No record found for ${HOST}.${DOMAIN}" >&2 + exit 1 + fi +done + +#╔─────────────────────────────────────────────────────────────────────────────╗ +#│ Sετ Λ rεcδrd τδ cμrrεητ WΛN | +#╚─────────────────────────────────────────────────────────────────────────────╝ +cf_host_ip="$(echo "$host_record" | jq -r '.content')" +cf_rec_id="$(echo "$host_record" | jq -r '.id')" + +if [[ -z "$cf_host_ip" || "$cf_host_ip" == null ]]; then + echo "Failed to find content of A record for ${HOST}.${DOMAIN}" >&2 + exit 1 +elif [[ -z "$cf_rec_id" || "$cf_rec_id" == null ]]; then + echo "Failed to find A record ID for ${HOST}.${DOMAIN}" >&2 + exit 1 +fi + +if [[ "$cf_host_ip" == "$wan_ip" ]]; then + echo "Cloudflare is up to date @ $(date)" >&2 +else + echo "Updating Cloudflare's A record from $cf_host_ip to $wan_ip" >&2 + + patch_response="$(curl -s --request PATCH \ + --url "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${cf_rec_id}" \ + --header 'Content-Type: application/json' \ + --header "Authorization: Bearer $TOKEN" \ + --data '{ + "comment": "'"${HOST} @ $(date)"'", + "content": "'"$wan_ip"'", + "name": "'"${HOST}.${DOMAIN}"'", + "proxied": false, + "ttl": 1 + }')" + + if [[ "$(echo "$patch_response" | jq -r '.success')" == true ]]; then + echo "Update to $wan_ip succeeded @ $(date)" >&2 + else + echo "Failed to update A record. DUMP:" + echo "$patch_response" + exit 1 + fi +fi diff --git a/systemd/system/update_a_record.service b/systemd/system/update_a_record.service new file mode 100644 index 0000000..c5ef026 --- /dev/null +++ b/systemd/system/update_a_record.service @@ -0,0 +1,12 @@ +[Unit] +Description = Sends current ip address to uni servers +Wants = update_a_record.timer +Wants = network-online.target +After = network-online.target + +[Service] +Type = oneshot +ExecStart = /usr/local/bin/set_a_records.sh + +[Install] +WantedBy = multi-user.target diff --git a/systemd/system/update_a_record.timer b/systemd/system/update_a_record.timer new file mode 100644 index 0000000..fdea809 --- /dev/null +++ b/systemd/system/update_a_record.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Updates Cloudflare's A record for mirrorside.mami2.moe +Requires=update_a_record.service + +[Timer] +Unit=update_a_record.service +OnCalendar=*-*-* *:00,15,30,45:00 +RandomizedDelaySec=15min + +[Install] +WantedBy=timers.target diff --git a/systemd/system/ydotoold.service b/systemd/system/ydotoold.service new file mode 100644 index 0000000..ae299cf --- /dev/null +++ b/systemd/system/ydotoold.service @@ -0,0 +1,9 @@ +[Unit] +Description=ydotool daemon + +[Service] +Type=simple +ExecStart=/usr/bin/ydotoold + +[Install] +WantedBy=default.target