Category Archives: Too Good To

Too good to #0004

systemd, the good parts: monotonic timers

When systemd makes you suffer because “run job every 10 minutes” is infinitely harder to specify than in crontab, remember there are monotonic timers in systemd that aren’t derived from wallclock time, or “Calendar Events” as they call it.

Run timer once 60 seconds after system startup, then every 10 minutes after the job finished:

# /etc/systemd/system/demo.timer
[Unit]
Description=demo monotonic timer

[Timer]
OnStartupSec=60
OnUnitInactiveSec=600

[Install]
WantedBy=timers.target    # When in a system session
# WantedBy=default.target # When in a user session (~/.config/systemd, systemctl --user etc.)

Python: Use tabulate to format output in columns

#!/usr/bin/env python3
from tabulate import tabulate

data = [
	[ 'foo', 'bar', 'baz' ],
	[ 'spam', 'eggs', 'bacon' ]
]

headers = ['Eine', '2', 'Whatever']

print(tabulate(data, headers=headers, tablefmt='simple'))

Output:

$ ./tab.py 
Eine    2     Whatever
------  ----  ----------
foo     bar   baz
spam    eggs  bacon

virsh/libvirt, automate key presses

(* Updated to sleep 5 seconds after each keypress.)

for key in R E I S U B; do virsh send-key "${domain}" KEY_LEFTALT KEY_SYSRQ KEY_${key}; sleep 5; done

Too good to #0003

Linux uptime in seconds, once and for all

awk '{printf "%i\n", $1}' /proc/uptime

“Sudo on demand”, re-exec shell script with sudo if not running as root

#!/usr/bin/env bash
printf "Running as: %s\n" "$(id -un)"
[[ ${EUID} -ne 0 ]] && printf "Re-executing with sudo.\n" && exec sudo "${0}"

See also TGT0006, this is just as useful for downgrading privileges on the fly.


“When was the last time apt-get on that Debian/Ubuntu machine installed package upgrades?”

  • Reliably answering this is a lot harder than it looks, subject of countless discussions and really does need to parse /var/log/apt/history.log, which is painful.
  • The script below maintains a file /var/log/apt/lastupgrade with the last upgrade’s time stamp, for further processing.
  • Does NOT track invocations of apt-get upgrade that did not lead to package upgrades.
  • Does NOT look behind logfile rotations, which should not be a problem because it’s closely hooked to dpkg.

/usr/sbin/apt-lastupgrade:

#!/bin/bash

while IFS=: read -r key value
do
	if [[ "${key}" == 'Start-Date' ]]
	then
		upgraded=0
	elif [[ "${key}" == 'Upgrade' ]]
	then
		upgraded=1
	elif [[ "${key}" == 'End-Date' ]]
	then
		if [[ ${upgraded} -eq 1 ]]
		then
			printf -v lastupgrade "%s" "${value}"
		fi
		upgraded=0
	fi
done < /var/log/apt/history.log

if [[ -v lastupgrade ]]
then
	tee /var/log/apt/lastupgrade <<-Here
	# Timestamp of last upgrade: ${lastupgrade}
	Here
	touch -d "${lastupgrade}" /var/log/apt/lastupgrade
fi

/etc/apt/apt.conf.d/90lastupgrade:

DPkg::Post-Invoke {"/usr/bin/systemd-run --on-active=60 /usr/sbin/apt-lastupgrade || /bin/true"};

Path of running shell script, dirname for locating config files, includes etc.

me_path="$(readlink -f "${0}")"
me_dir="$(dirname "${me_path}")"
me_base="$(basename "${me_path}")"

Too good to #0002

Show and update the comment in an SSH private key

ssh-keygen -l -f <keyfile> # show
ssh-keygen -c -f <keyfile> # change interactively
ssh-keygen -c -C <newcomment> -f <keyfile> # change and provide new

ssh-agent in Gitlab CI/CD

  • Define SSH_KEY as a “file” in Gitlab CI/CD variables and SSH_PASSPHRASE as a regular variable
  • If libcrypto errors on execution, make sure SSH_KEY has an additional line ending at the end
before_script:
  - DEBIAN_FRONTEND=noninteractive apt-get update -y
  - DEBIAN_FRONTEND=noninteractive apt-get install -y openssh-client

sftp_to_somewhere:
  stage: deploy
  script:
    - chmod 600 "$SSH_KEY"
    - eval $(ssh-agent)
    - ssh-add "$SSH_KEY" <<< "$SSH_PASSPHRASE"
    - ssh-add -l

Urlwatch, but with a local pop-up window on Linux, no additional infrastructure

Systemd user service:

# ~/.config/systemd/user/urlwatch-to-zenity.service
[Unit]
Description=urlwatch to zenity (service)

[Service]
Type=oneshot
ExecCondition=sh -c 'nmcli net co | grep full'
ExecCondition=sh -c 'urlwatch > %h/.cache/urlwatch.out'
ExecCondition=sh -c 'test -s %h/.cache/urlwatch.out'
ExecStart=sh -c 'zenity --title Urlwatch --text-info --width=600 --height=400 --filename=%h/.cache/urlwatch.out'

[Install]
WantedBy=default.target

Systemd timer:

# ~/.config/systemd/user/urlwatch-to-zenity.timer
[Unit]
Description=urlwatch to zenity (timer)

[Timer]
OnStartupSec=1h
OnUnitInactiveSec=2h

[Install]
WantedBy=default.target

cookiecount – Load a page and show the cookies it sets

$ ./cookiecount https://example.com
0 cookies received.

ps1_anon.bash – anonymize bash prompt, for screenshots and pastes

# Anonymize bash prompt for screenshots and pastes
# Source from or add to ~/.bashrc
function ps1_anon (){
	if [[ -v SAVED_PS1 ]] 
	then
		PS1="${SAVED_PS1}" 
		unset SAVED_PS1 
	else
		SAVED_PS1="${PS1}" 
		PS1='\$ '
	fi
}

Example:

[martin@idefix ~/devel/cookiecount(main=)]$ ps1_anon
$ ./cookiecount https://reddit.com --json | jq .cookiecount
7
$ ps1_anon
[martin@idefix ~/devel/cookiecount(main=)]$

Too good to #0001

Set X11 keyboard layout manually and temporarily (e.g. in i3 if I need to test exotic window managers for my users)

setxkbmap -layout us,us -variant altgr-intl -option caps:none

Speaking of which, set keyboard layout permanently in Gnome

gsettings set org.gnome.desktop.input-sources sources "[('xkb', 'us+altgr-intl')]"

Check if Gnome Screensaver is active (I use this for automated time tracking)

busctl --user call org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver GetActive


Urlwatch for a new package version in an APT repository

After a long wait, I was finally able to find another use for my awful Perl one-liner from 2019 that pivots a Debian Packages.gz into a space-separated table!

---
name: "Pop!OS Firefox package"
command: curl -s http://apt.pop-os.org/release/dists/jammy/main/binary-amd64/Packages.gz |
  gzip -dc | perl -ane 'if($F[0]=~/^Package:/){$p=$F[1]};if($F[0]=~/^Version:/){$v=$F[1];print"$p $v\n";}' |
  grep '^firefox '
diff_filter:
  - grep: '^[+-][^+-]'
---