--argfile behaves differently depending on the number of JSON objects in the file. If it the file contains 1 JSON object, it is returned as a JSON object. If I create a JSON file that contains 2 JSON objects, they are converted to a list of objects:
--slurpfile treats all JSON objects the same and if the file contains a single JSON object, returns a list of JSON objects containing a single entry. Hence the added [0] from the TL;DR section.
One might argue that the removal of --argfile could have come following a clear deprecation notice instead of semi-permanently (first appearance in Debian 9/Stretch) advising against its use and then removing it all of a sudden. However, as far as I am concerned, the issue was detected in earliest Debian 13 compatibility testing within days after the freeze, I immediately knew what was cooking, the migration to --slurpfile was trivial, and it can also be deployed retroactively on all older systems.
Caveat: The preview pane inside OBS requires 24 bit color depth.
The ~/.xinitrc sets a language environment, which I use for influencing the date format inside the OBS browser source. The OBS invocation also starts the stream automatically, and ignores uncontrolled shutdowns of OBS, so the stream will autostart after a reboot.
The VNC service will be bound to localhost only, but adding a password won’t hurt and will hardly be noticeable once it’s setup and saved in the VNC client.
x11vnc -storepasswd
Another systemd user-unit takes care of starting x11vnc:
Pipewire Easyeffects with RNNoise on Debian 12 / Bookworm
Gnome Night Light from Sunrise to Sunset, without Location Services
Revisiting ratarmount
Pipewire Easyeffects with RNNoise on Debian 12 / Bookworm
RNNoise for removing background sound on your microphone is not included in Debian 12, due to licensing issues around its training data. The least painful way to work around this is to install the Easyeffects Flatpak instead of the packaged easyeffects:
Ich habe einige Monate damit rumgemacht, dass das Konglomerat Telegraf/Prometheus/Grafana von ziemlich starken Abhängigkeiten bei der Startreihenfolge der beteiligten Systeme geplagt ist. Meine Server rebooten alle einmal pro Woche automatisch innerhalb eines gewissen Zeitfensters und seit Grafana musste ich so alle 2-3 Wochen manuell eingreifen und Services neustarten, da insbesondere Telegraf davon ausgeht, dass Schnittstellen, auf die es selbst zugreift, beim eigenen Start erreichbar sein müssen. (Siehe auch: Fallacies of distributed computing.)
Die Frage war also, wie sieht die einfachste Lösung aus, um einen gegebenen Service 60 Minuten nach seinem Start noch einmal automatisch durchzustarten (mit sporadischen Lücken in den Daten kann ich am frühen Sonntagmorgen leben).
Timer und dedizierte Service-Units zu verteilen schien mir zu aufwändig, und ich habe unangenehm lange gegrübelt, wie die simpelste Lösung aussehen könnte, die einfach per systemctl edit foo.service (oder per anderweitig verteiltem systemd drop-in, ymmv) in die betreffende Unit eingebracht werden kann.
Angekommen bin ich schließlich bei dieser Bandwurmzeile:
systemd-run instanziiert einen transienten timer und service, der in einer Stunde systemctl restart ausführen wird. %N wird dabei durch den Namen der übergeordneten Unit (hier also: telegraf) ersetzt.
Der Name des transienten timer und service wird über die Option --unit vorgegeben um im vorherigen Schritt (eins weiter links) vorhersagbar zu sein.
systemd-run wird nur dann ausgeführt, wenn nicht bereits eine Unit aktiv ist, die den vorgegebenen Namen hat, den die transiente Unit bekommen würde, wenn sie noch auszuführen wäre.
Das ganze ist als Shell-Aufruf gewrappt, um den || – Operator zu haben.
ExecStartPost=+ führt als root aus, obwohl die übergeordnete Unit ihre Befehle unprivilegiert ausführt.
ExecStartPost=- ignoriert den Fehler aus dem Shell-Einzeiler, wenn die Unit bereits aktiv war. Es könnte ersatzweise auch ; true ans Ende des Einzeilers geschrieben werden.
I recently got the opportunity to work on adapting the default Debian Gnome experience to a client’s corporate design, and it felt a LOT like reverse-engineering deeply into the undocumented.
I found the work to fall into a number of categories, which I will classify as “dconf policy”, “css”, “xml-manifest” and “packaging”.
GDM logo
dconf policy, packaging
GDM banner message
dconf policy
GDM background color
css, xml-manifest
GDM wallpaper
css, xml-manifest, packaging
Gnome default wallpaper
dconf policy, packaging
Gnome default theme
dconf policy
Gnome shell plugins
dconf policy, packaging
Gnome UI and plugin defaults
dconf policy
Gnome wallpapers
xml-manifest
Note I’m not familiar with any underlying Gnome/GTK philosopy aspects but come from a Linux engineering role and just need to get the job done.
Packaging
The “packaging” class really just means that required assets need to be packaged onto the system and that any shell plugins that should be enabled by default, must be installed.
GDM
The GDM-Settings workflow
For GDM customization, GDM-Settings proved immensely helpful for identifying where to make changes.
# Install via flatpak
sudo apt-get install flatpak gnome-software-plugin-flatpak
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak -y install io.github.realmazharhussain.GdmSettings
# Keep track of where we started off
touch /tmp/now
# Run gdm-settings
flatpak run io.github.realmazharhussain.GdmSettings
# See what changed
find / -type f -newer /tmp/now 2>/dev/null | egrep -v '^/(dev|run|proc|sys|home|var|tmp)'
For this post, I will stick with the default dconf policy filename used by GDM-Settings.
Logo and banner
# /etc/dconf/db/gdm.d/95-gdm-settings
[org/gnome/login-screen]
logo='/usr/share/icons/hicolor/48x48/apps/gvim.png'
banner-message-enable=true
banner-message-text='Welcome to VIMnux'
dconf needs an accompanying profile definition, /etc/dconf/profile/gdm:
user-db:user
system-db:gdm
dconf update needs to be run after modifying these files.
Background color and wallpaper
GDM background settings are hidden deep in the global gnome-shell theme CSS, which itself is hidden in /usr/share/gnome-shell/gnome-shell-theme.gresource.
GDM-Settings completely hides the tedious process of drilling down to the CSS away from the user, which is great from a user perspective, but not what I needed for my customizations. I went with the following workflow for unpacking the files. gresource list lists the file names contained in the gresource file, gresource extract extracts them one by one.
# Unpack /usr/share/gnome-shell/gnome-shell-theme.gresource
# to a temporary directory:
T=$(mktemp -d /tmp/gres.XXX); printf "Resources tempdir: %s\n" $T
cd $T
while read R
do
gresource extract /usr/share/gnome-shell/gnome-shell-theme.gresource $R > $(basename $R)
done < <(gresource list /usr/share/gnome-shell/gnome-shell-theme.gresource)
At this point, the only file I’m interested in is gnome-shell.css, where I set a black background for my application.
Reassembly of the gresource file requires an XML manifest which I generate using the following script, manifest.py:
#!/usr/bin/env python3
import os, sys, glob
import xml.etree.ElementTree as ET
from io import BytesIO
os.chdir(sys.argv[1])
gresources = ET.Element('gresources')
gresource = ET.SubElement(gresources, 'gresource', attrib = {'prefix': '/org/gnome/shell/theme'})
for resourcefile in glob.glob('*'):
file = ET.SubElement(gresource, 'file')
file.text = resourcefile
out = BytesIO()
xmldoc = ET.ElementTree(gresources)
ET.indent(xmldoc)
xmldoc.write(out, encoding='utf-8', xml_declaration=True)
print(out.getvalue().decode())
First generate the XML manifest, then compile the gresources file.
# Generate XML
./manifest.py $T > gnome-shell-theme.gresource.xml
# Compile gresources (glib-compile-resources from libglib2.0-dev-bin)
glib-compile-resources gnome-shell-theme.gresource.xml --sourcedir=$T --target=gnome-shell-theme.gresource
Someone over here decided to indirect /usr/share/gnome-shell/gnome-shell-theme.gresource via /etc/alternatives, do whatever you like.
Note that on the systems I tested this on, gdm.css and gdm3.css could be left out of the gresource file and all changes were made in gnome-shell.css.
Gnome Wallpapers
Speaking of XML, Wallpapers can be installed to someplace intuitive such as /usr/share/backgrounds/corporate but must be accompanied by another XML manifest in /usr/share/gnome-background-properties, which I generate using another XML generator, properties-xml.py:
dconf update needs to be run after modifying this file.
Other Gnome defaults
dconf watch /
dconf watch / in a terminal makes it possible to take note of what configuration options change as changes are being made. They can now be made the defaults in a policy file such as /etc/dconf/db/local.d/99-misc-defaults:
tuptime – Report historical and statistical real time of the system, keeping it between restarts. Total uptime
tuptime calculates overall uptime of the system it’s running on. It also flags shutdowns as “BAD” if it comes up without having been gracefully stopped before.
As I grew up in an age where uptime braggery was common even among professionals, my entirely unreasonable use case here is to determine uptime since the previous unclean shutdown:
function tuptime-graceful () {
local tuptime_since=1
local temp_array
while read -r line
do
if [[ "${line}" =~ ' BAD ' ]]
then
read -r -a temp_array <<< "${line}"
tuptime_since=$(( temp_array[0] + 1 ))
break
fi
done < <(tuptime --table --order e --reverse)
tuptime --since "${tuptime_since}"
}
Nothing serious, just a few notes I like to share with friends and colleagues who, like me, script around curl.
curl -f / --fail
I try to use --fail whenever I can, because why would I want to exit zero on server errors?
$ curl -L https://download.grml.org/grml64-small_2024.02.iso.NO
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.41 (Ubuntu) Server at ftp.fau.de Port 443</address>
</body></html>
$ echo $?
0
$ curl -f -L https://download.grml.org/grml64-small_2024.02.iso.NO
curl: (22) The requested URL returned error: 404
$ echo $?
22
curl --fail-with-body
I have a CI/CD situation where curl calls a webhook and it’s incredibly useful to see its error message in case of failure.
$ curl --fail https://binblog.de/xmlrpc.php
curl: (22) The requested URL returned error: 405
$ curl --fail-with-body https://binblog.de/xmlrpc.php
curl: (22) The requested URL returned error: 405
XML-RPC server accepts POST requests only.
set -o pipefail
When curl‘s output gets piped to any other command, I try to remember to set -o pipefail along with curl --fail so if curl fails, the pipe exits non-zero.
#!/usr/bin/env bash
url='https://download.grml.org/grml64-small_2024.02.iso.NONO'
if curl -s -f -L "${url}" | sha256sum
then
echo "Success."
else
echo "Failure."
fi
set -o pipefail
if curl -s -f -L "${url}" | sha256sum
then
echo "Success."
else
echo "Failure."
fi
curl --connect-timeout
Useful to get quicker response in scripts instead of waiting for the system’s default timeouts.
curl -w / --write-out
This may be over the top most of the time, but I have one situation that requires extremely detailed error handling. (The reason being a bit of a foul split DNS situation in the environment, long story.) This is where I use --write-out to analyze the server response.
Username:password authentication is a thing, no matter how much it’s discouraged. Here’s how to at least hide username and password from the process list.
$ chmod 600 ~/.netrc
$ cat ~/.netrc
machine binblog.de
login foo
password bar
$ curl -v -o /dev/null -n https://binblog.de
...
* Server auth using Basic with user 'foo'
...
To use any other file instead of ~/.netrc, use --netrc-file instead.
Here’s the bug that neccessitatesthe --no-setuptools option: “ModuleNotFoundError: No module named ‘debian'”
mirror.list entry for the Mozilla Firefox APT repository:
deb-all [signed-by=/path/to/packages-mozilla-org.gpg] https://packages.mozilla.org/apt mozilla main
deb-amd64 [signed-by=/path/to/packages-mozilla-org.gpg] https://packages.mozilla.org/apt mozilla main
How to convert Mozilla’s sloppy ASCII-armored PGP key:
$ curl -s -O https://packages.mozilla.org/apt/repo-signing-key.gpg
$ file repo-signing-key.gpg
repo-signing-key.gpg: PGP public key block Secret-Key
$ mv repo-signing-key.gpg repo-signing-key
$ gpg --dearmor repo-signing-key
$ file repo-signing-key.gpg
repo-signing-key.gpg: OpenPGP Public Key Version 4, Created Tue May 4 21:08:03 2021, RSA (Encrypt or Sign, 2048 bits); User ID; Signature; OpenPGP Certificate
forkstat (8) – a tool to show process fork/exec/exit activity
High load without a single obvious CPU consuming process (not related to the Nextcloud shenanigans above) led me to forkstat(8):
Forkstat is a program that logs process fork(), exec(), exit(), coredump and process name change activity. It is useful for monitoring system behaviour and to track down rogue processes that are spawning off processes and potentially abusing the system.
$ sudo tee /etc/apt/sources.list.d/mozillateam-ppa.list <<Here
deb https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu jammy main
deb-src https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu jammy main
Here
$ sudo tee /etc/apt/trusted.gpg.d/mozillateam.asc < <(curl 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x0ab215679c571d1c8325275b9bdb3d89ce49ec21')