Linux

Table of Contents

Internals

Processes

Here (archive.org) is an excellent explanation.

Sessions contain process groups, which contain processes, which contain threads.

Recall Keist Zenon’s video on process hacking, where they show how a “PID”, as typically used by POSIX command line tools, is actually a thread ID.

Section 10.4 of the above link:

An ordinary process has a single thread with TID [thread ID] equal to PID.

Processes in background process groups, by default, are paused when they try to read stdin. User can fg them and input normally.

That relies on a shell that supports job control. How does this work in Emacs?

General installation

Agnostic of distribution.

  1. On the old machine, package up the home directory, and update in git everything not packaged. Make sure Syncthing is fully up to date. Package the stow directory. Use tar -t to validate the archive.
  2. On the new machine, install git, syncthing, stow, and udisks.
  3. Use udisks to remount my USB drive and pull the home package onto the new machine. Alternatively, on the old machine, store the home package in ~/sync. Then use syncthing to pull it down.
  4. Stow my gitconfig.
  5. Git clone “password-store”
  6. Start Syncthing, and add all folders to the new machine. Enable autostarting Syncthing.
  7. Install Flatpak. Install what you want of:
    1. com.discordapp.Discord
    2. com.valvesoftware.Steam
    3. com.spotify.Client
    4. org.zotero.Zotero
    5. com.google.Chrome
    6. net.lutris.Lutris
    7. net.ankiweb.Anki
    8. org.prismlauncher.PrismLauncher
    9. Thunderbird
    10. Firefox

Printing

Use CUPS. Administer and add printers at http://localhost:631.

Alpine

Run the following as root:

apk add cups cups-filter
# lpadmin is required to administer CUPS.
# I don't know whether lp is required.
adduser "$user" lp lpadmin # no relog required
rc-service start cupsd
# lp and lpr are now usable with either -d <dst> or a default destination

At first (2025-06-19), I didn’t realize I had to install cups-filter. I was unable to print a test page.

Emacs default destination

To make things quick, set a default destination. Then, for example, you can position point on a file in Dired and press P RET. Invoke the following with a suitable printer-name to set the default destination within Emacs for the duration of the session.

(setenv "LPDEST" printer-name)

Reading Partners Canon printer

Canon isn’t a make explicitly recognized by CUPS. I added a Canon printer as a “Generic/PCL Laser Printer” and everything was fine.

Run on boot

OpenRC (Alpine)

The builtin local service runs scripts in /etc/local.d/ when it starts and stops.

On Alpine, see file:///etc/local.d/README.

Default browser

I don’t know the proper major/minor media (MIME) type to use to set a default URI handler. So xdg-mime can’t help.

Instead:

unset BROWSER
xdg-settings set default-web-browser firefox.desktop
# set $BROWSER again if long-lived shell

Fonts

Defaults with fontconfig

Thanks to Chuan Ji’s post about the fontconfig configuration. This worked immediately with PGTK Emacs on Wayland, which is promising.

Simply don’t set any default families in Emacs. I set only a size, and Emacs loaded the config-specified default font at the proper size.

Networking

Listing MAC and IP addresses on the local network

Nmap does this if it has root permissions:

doas nmap -sn 10.0.0.0/24

Extract the IP for a given MAC using this shell invocation:

doas nmap -sn 10.0.0.0/24 | \
    grep -B 2 $mac | \
    sed -n '/report for/ s/^.*\(\([0-9]+\.\)\{3\}[0-9]+\)$/\1/p'

TTY/console

Setting fonts

helpful article: datafix: BASHing

Setting colors

The console accepts escape sequences for this purpose. Use printf(1) with the %b specifier, which interprets escape codes in its argument.

Here’s an example with the ’everforest dark medium’ color palette:

printf %b '\e]P02f383e' \
       '\e]P1e67e80' \
       '\e]P2a7c080' \
       '\e]P3dbbc7f' \
       '\e]P47fbbb3' \
       '\e]P5d699b6' \
       '\e]P683c092' \
       '\e]P7d3c6aa' \
       '\e]P84b565c' \
       '\e]P9e67e80' \
       '\e]Paa7c080' \
       '\e]Pbdbbc7f' \
       '\e]Pc7fbbb3' \
       '\e]Pdd699b6' \
       '\e]Pe83c092' \
       '\e]Pfd3c6aa'

Nice fonts

Enki

  • Terminus 20 bold

Watching videos in another TTY

[2025-10-11 Sat]

Phew, this one took a while to figure out.

  1. Pick a TTY with no process attached, not even getty. Call it /dev/ttyN.
  2. Change its permissions so audio can come through. I think it uses the XDG runtime directory to do this, but I’m really not sure:

    # chown <user> /dev/ttyN
    

    This is the only step requiring root privileges. You’ll probably have to do it after every boot.

    Or, if you’re on a single-user system and will do this often, invoke chown as a local.d script.

  3. Start the process. MPV can run in a framebuffer because it’s based on mplayer2, which also can. I believe VLC has similar functionality. ffplay seems like the kind of thing that would also, but without good user interface. (Gotta draw the line somewhere.)

    $ openvt -s -c N mpv --vo=drm path/to/video.mp4
    
    • -s switches to /dev/ttyN. MPV displays on the screen regardless of which tty is focused. Therefore, switch to it with -s so input immediately applies to MPV. Otherwise, input will keep going to the focused (now obscured) tty and doing nothing to MPV.
    • -c N specifies /dev/ttyN as the tty to run the process on. In step (1), we chose /dev/ttyN because it was not the controlling tty for any process. If it had been, there would now be two processes listening for input on the same tty, and results would be unpredictable and probably not what you want.

The video should be running and audible! Use the familiar Alt+Fn, Alt+Left, Alt+Right to switch between ttys.

TODO what happens when you suspend (^Z) an instance of MPV started by this method?

See also

  • chvt
  • deallocvt

Emacs

See Emacs.

Alpine Linux

Firmware packages

[2024-05-25 Sat] In an effort to get displays working through USB-C hubs on Enki, I’m installing the linux-firmware meta package, which depends on all firmware. That’s pretty large, apparently, but oh well. I’m removing from /etc/apk/world explicit dependencies on:

  • linux-firmware-i915
  • linux-firmware-intel
  • linux-firmware-mediatek
  • linux-firmware-other
  • linux-firmware-rtl_bt

This is in response to a dmesg warning that some firmware failed to load. I don’t have the knowledge right now to discern anything else.

Sure enough, after rebooting, following dmesg, and plugging in the hub, I saw some unfamiliar interface drivers load. Still, though, the displays aren’t being detected through the USB-C hub.

Swapping escape and caps lock

[2025-12-28 Sun]

  1. Install caps2esc. It pulls its required dependencies, including interception-tools, which it’s a module for.
  2. Start the udevmon OpenRC service. This is provided by interception-tools.
  3. Add the following snippet to /etc/interception/udevmon.yaml:

     - JOB: intercept -g $DEVNODE | caps2esc -m 1 | uinput -d $DEVNODE
    DEVICE:
      EVENTS:
        EV_KEY: [KEY_CAPSLOCK, KEY_ESC]
    

    See documentation of caps2esc for the meaning of the mode flag -m.

User groups

[2025-06-01 Sun 12:42] <- Groups: See

group purpose
audio alsa
input required for Xorg input
plugdev Pipewire. necessary?
pipewire (see section)
seat Wayland or greetd
video brightnessctl; webcam
lpadmin CUPS administration

On the Alpine wiki (2024-01-08) the Sway page discourages adding users to the video group, it being unnecessary. I don’t know the reasoning.

Group pipewire

Combined with a PAM snippet that ships with the pipewire package, being in this group enables realtime scheduling. This, I think, allows Pipewire to take a higher priority in scheduling and, therefore, decreases audio crackle under high CPU load.

Wayland

Minimal Wayland setup

The below is one of the smallest ways I’ve found to enter a Wayland environment. Excludes display management and Dbus.

  • Packages
    • shadow-login (enables PAM)
    • pam-rundir (Alpine wiki says no config required)
    • seatd
    • eudev (+ deps: see setup on wiki)
    • (compositor)
    • (video driver: mesa-dri-gallium)

    elogind obviates seatd and pam-rundir.

  • OpenRC

    Enable:

    • seatd (default)
    • udev, udev-trigger, udev-settle (sysinit)
    • udev-postmount (default)

    Disable:

    • mdev
  • Groups

    Only seat.

Fixing missing cursors

Fix missing cursors by installing a cursor package and setting the Xcursor theme.

The core of the problem is that (version?) Alpine hicolor-icon-theme doesn’t provide cursors.

I fixed it like this:

  1. Install capitaine-cursors (or other)
  2. In River (compositor) config, issue:

    riverctl xcursor-theme <cursor-theme>
    

    where <cursor-theme> matches exactly the directory in /usr/share/icons/ created by capitaine-cursors.

Installation

Sections after “Steps” are in alphabet, not chronological, order.

Steps

  • Live environment

    I’ve often found it difficult to establish a network connection in the live Alpine image. I established this sequence while trying to reinstall a corrupted bootloader:

    1. Write a WPA Supplicant configuration file
    2. Write `/etc/network/interfaces`
    3. If applicable, disable IPv6 with the following /etc/sysctl.d/local.conf:

      net.ipv6.conf.all.disable_ipv6 = 1
      net.ipv6.conf.default.disable_ipv6 = 1
      net.ipv6.conf.lo.disable_ipv6 = 1
      net.ipv6.conf.wlan0.disable_ipv6 = 1
      

      It dictates behavior across reboots. Use

      sysctl -p $file
      

      (-p from BusyBox) to load it without rebooting.

    4. Launch WPA Supplicant
    5. Launch a DHCP client

Audio

Alsa plays well with Pipewire.

Note: Add alsa to the default OpenRC runelevel manually. Alpine doesn’t do it automatically.

Btrfs

[2025-04-19 Sat] mkfs.btrfs was saying it couldn’t register with /dev/btrfs-control.

Then mount was complaining about an invalid argument.

I did this:

echo btrfs >> /etc/modules
modprobe btrfs

And mount worked afterward. I’m guessing the modprobe is what fixed it.

Flatpak and xdg-desktop-portal-*

As far as I can tell, the package xdg-desktop-portal is an interface, and xdg-desktop-portal-* are implementations.

Flatpaks fail to open URIs if no available portal implements OpenURI.

I had success with this:

  1. Add package xdg-desktop-portal-gtk
  2. Reboot

File picking dialogs work (or fail to work) similarly.

Groups: See User groups

Networking

  • IPv4 and IPv6

    BusyBox’s udhcpd doesn’t support IPv6. Use dhcpcd if that’s necessary.

  • WPA supplicant

    Bring a wpa_supplicant.conf with you to the live environment. The installation is much smoother with Internet connection. Use wpa_cli or simply copy over the config file. Then start wpa_supplicant as the first thing. This might require setting up wlan0 in /etc/network/interfaces. I’m not 100% sure.

    Remember to add WPA Supplicant to the boot runlevel. Otherwise, the DHCP daemon will always timeout at boot.

River and seatd

River, the Wayland compositor, requires seatd. Install it and enable it via OpenRC. Add relevant users to the seat group.

UDisks2

Install udisks2 early. Make sure all packages surrounding it are updated (i.e., if you’re installing a version newer than the install image). This might require some udev setup. Busybox mdev might be able to handle it as long as you use the Alpine script setup-devd. I think my running world includes eudev anyway.

Handling power events

ACPId conflict: Busybox vs acpid

[2024-10-16 Wed] I don’t really know what was happening here. The lid was not triggering sleep. Something like: Busybox was taking over as OpenRC acpid service, even though the acpid package was controlling /etc/acpid. So, the scripts didn’t match up with the daemon itself and how it reported power events. Uninstalled the acpid package, and everything was fine.

Elogind overriding busybox acpid

Elogind handles power events before acpid can process them. That led to some double event handling, although I might be remembering them wrong. It did lead to behavior I didn’t expect.

I like acpid to handle power events in a simple way that gives me lots of control:

  • When the laptop lid closes, turn off the screen but don’t suspend. Turning off the screen is a hardware service, I think, so the lid switch can be ignored.
  • When I press the power button, suspend, don’t turn off.

A default busybox acpid can present this behavior easily. This should be the filetree of file:///etc/acpi/:

/etc/acpi/
|
+- PWRF
   |
   +- 00000080

file:///etc/acpi/PWRF/00000080 contents:

#!/bin/sh
echo mem > /sys/power/state

Nice and simple.

Elogind wants to do much more. We can prevent by adding a drop-in configuration file that ignores all (I hope) power events. For information about drop-in files, see the comment atop file:///etc/elogind/logind.conf. I’m not entirely sure what file:///etc/elogind/sleep.conf does.

file:///etc/elogind/logind.conf.d/disable-power.conf contents:

[Login]
HandlePowerKey=ignore
HandlePowerKeyLongPress=ignore
HandleRebootKey=ignore
HandleRebootKeyLongPress=ignore
HandleSuspendKey=ignore
HandleSuspendKeyLongPress=ignore
HandleHibernateKey=ignore
HandleHibernateKeyLongPress=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore

[2025-05-21 Wed] Rebooted, and this worked.

Setting up an airgapped storage machine

Updating /etc/network/interfaces to read:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
	address 192.168.1.150/24

The address given appears on the Alpine Wiki. I’ve opted for no gateway, since I don’t quite know what that would mean. The intent is to simply ssh into the system after connecting the computers by ethernet cord.

It won’t be able to update itself, but that’s a problem for another time. I just updated it to Alpine 3.20, so it should be solid awhile.

Enabling sshd

I added enki’s public ed25519 to ty@thoth’s ~/.ssh/authorized_keys. I’m suspicious I’ll have to do the same in root’s home directory, not in ty’s, but we’ll see. One variable at a time.

On second thought, that makes sense. You ssh as a user. If I ssh as “ty”, “ty” gets to decide which keys are authorized. Indeed, over wifi, ssh was working just fine a moment ago.

Disabling OpenRC services

  • ntpd
  • wpasupplicant

Scratch that.

I don’t have an ethernet crossover cable or a network switch here, so I can’t connect these two machines. Back to WiFi!

Fragile but good enough for now

I might just have to check on the address manually until I can get my hands on a crossover cable. The router seems to be consistently assigning it 10.0.0.51, so I added that to my config. Very brittle.

[2024-06-11 Tue 14:32] progress

I have something working. Picked up a crossover cable today and have Enki and Thoth talking. This is working with the cable in Enki directly as well as in the dock.

Both have wlan working as well; we’ve started using Thoth as a minecraft server.

  • Thoth’s interface file
    auto lo
    iface lo inet loopback
    
    auto eth0
    iface eth0 inet static
            address <IP/subnet>
    
    auto wlan0
    iface wlan0 inet dhcp
    
  • Enki’s interface file

    This is untested. I think I actually used

    # ip link set eth0 up
    # ip ad add <IP/subnet> dev eth0
    

    to hot-setup Enki for this test. I’m assuming the interfaces file below will do the same. A restart will validate.

    File:

    auto lo
    iface lo inet loopback
    
    auto eth0
    iface eth0 inet static
          address <IP/subnet>
    
    auto wlan0
    iface wlan0 inet dhcp
    

Disabling IPv6

This forum thread suggests adding a line to /etc/sysctl.conf:

net.ipv6.conf.all.disable_ipv6 = 1

I don’t know much about that file. Are these kernel parameters?

That seemed to work.

The same forum thread suggests that the net..._ipv6 key above will be unrecognizable unless the ipv6 module is loaded at init time. Fortunately, on both Enki and Thoth, /etc/modules contains an ipv6 line by default.

Steam Flatpak

bwrap dislikes the ~/Music symlink

Apparently flatpak wants to know my XDG setup, but a symlink plays weird with some applications’ XDG folder handling. This invocation lets them know what I’m up to:

xdg-user-dirs-update --set MUSIC ~/path/to/music-dir

Where ~/path/to/music-dir is the directory pointed to by the symlink.

The same thing happens if I accidentally, on a new machine, create .local as symlink using stow. Then .local/share and .local/state are behind symlinks, which bwrap doesn’t like.

Potentially helpful Alpine package: steam-devices

I haven’t tried it. Keep it on the radar.

[2026-04-06 Mon] Update: I think I have been using this in recent years. Makes gamepad support pretty seamless, if I recall.

Setting up KDE Plasma

OpenRC Services

  • polkit
  • elogind
  • sddm
  • bluetooth?

Also disable acpid, assuming SDDM or plasma ([2026-04-06 Mon] or, more likely, the display manager) do their own thing. That solves a double-sleep problem I’ve seen.

Packages

[2024-09-27 Fri 08:14] <- Switching from Plasma to a lightweight DM

  • all pulled by setup-desktop, which might include:
    • ark
    • bluedevil
    • breeze(-gtk)
    • discover
    • drkonqi
    • gwenview
    • kate
    • kde-cli-tools
    • kde-gtk-config
    • kde-icons
    • kdeplasma-addons
    • kgamma
    • kinfocenter
    • kio-fuse
    • kmenuedit
    • konsole
    • kscreen
    • ksshaskpass
    • kwallet-pam
    • kwayland-integration
    • plasma-browser-integration
    • plasma-desktop
    • plasma-disks
    • plasma-nm
    • plasma-pa
    • plasma-systemmonitor
    • plasma-vault
    • plasma-welcome
    • plasma-workspace-wallpapers
    • polkit-kde-agent-1
    • powerdevil
    • print-manager (might keep this to try out)
    • sddm-breeze
    • sddm-kcm
    • spectacle
    • systemsettings
  • elogind (pulled by setup-desktop)
  • polkit-elogind (pulled by something kde related?)
  • xdg-desktop-portal-kde

Can Replace Packages

  • blueman
  • gnome-screenshot?
  • explicit xorg packages (but they might be helpful to have)
  • scrot
  • thunar

NetworkManager

[2024-07-23 Tue 11:18] <- NetworkManager

See Alpine Wiki on NetworkManager.

  • Packages

    KDE might pull these in on its own.

    • plasma-nm
    • networkmanager
    • networkmanager-wifi
  • Groups
  • OpenRC Services
    • networkmanager (runlevel default)
    • DISABLE wpasupplicant
    • DISABLE networking

Switching from Plasma to a lightweight DM

See Packages for Plasma. Uninstall these.

Shadow-login and pam-rundir should be enough to get going. DWM doesn’t need Seatd. Make sure to edit .xinitrc and .profile. Re-enable acpid. Switch rofi from wayland to X11.

NetworkManager

See NetworkManager setup with KDE.

Relationship with /etc/network/interfaces

By default, NetworkManager will ignore any interface described in /etc/network/interfaces. In the NetworkManager configuration, load the ifupdown module and use it to set managed=true. The following should be a subset of the whole config:

[main]
plugins=ifupdown

[ifupdown]
managed=true

See the Alpine Wiki on NetworkManager.

Setting console fonts

[2024-05-18 Sat 09:56] <- Setting fonts/Alpine linux

Give setfont a full filename argument.

Fonts are kept, at least, in /usr/share/consolefonts. If that directory contains, for example, ter-122b.psf.gz, invoke:

setfont ter-122b.psf.gz

TODO how to change persistently across reboots?

Controller input

For Steam games, this worked for me simply by plugging a Dualshock 3 into the USB port of Enki. It doesn’t do anything in dwm, but I assume that’s dependent on the graphical environment. For example, I’d guess GNOME would auto-detect the controller and allow some basic inputs with it. I wonder if any other apps would see it the same.

It’s especially nice that the Steam Flatpak detected the controller without any setup on my part.

Unenforced dependencies

These are packages that I need installed for some local application or another, but which don’t have any obvious dependents within APK.

Emacs PDFTools

Compiling epdfinfo causes these packages to be installed:

  • autoconf
  • automake
  • libpng-dev
  • poppler-dev
  • glib-dev
  • gcc
  • build-base

They remain in world after the build succeeds.

hidapi

Installed in attempt to fix error (

/sys/class/hidraw: No such file or directory

) when trying to run Nonguix’s Steam.

I don’t think it worked, but we’ll wait for reboot.

musl-dev: emacs-*-nativecomp

optional for offpunk

  • py3-lxml-html-clean

Common Lisp

Nodgui, when installed through Quicklisp, wants <turbojpeg.h> in the Alpine package libjpeg-turbo-dev.

Alpine Install Checklist

  • [ ] copy ssh and gpg keys
  • [ ] copy wpa supplicant conf

x11 on alpine linux: need dbus-x11

The package dbus-x11 provides the executable dbus-launch, which I use in .xinitrc.

Nextcloud

Install packages nextcloud-mysql, nextcloud-initscript.

Each of these requires nextcloud itself. I’m not entirely sure what nextcloud-initscript does.

helpful links

2024-07-21: apps not working

I installed nextcloud-default-apps after I’d already setup. I’m not sure what it does, and I can’t seem to load anything. Maybe another reinstall is in order.

Guix

[2024-09-29 Sun 07:55] <- On Alpine Linux

Install attempt starting [2024-09-29 Sun]

  • Steps taken
    • Install guix package from APK. This pulls in guix-openrc. I think it also created Guix build users.
    • Enable and start the guix-daemon OpenRC service. This creates /var/guix/, or maybe the installation already did.
    • At this point, I installed a locale from a file. Substitutes (at least, ci.guix.gnu.org and bordeaux) seem enabled already. I think the daemon itself created /gnu/ at this step. It seems to have worked. Locale:

      (use-modules (gnu packages base))
      
      (make-glibc-utf8-locales
       glibc
       #:locales (list "en_US")
       #:name "glibc-en-us-utf8-locale")
      

      Write this to a file, then invoke guix package -f FILE.

    • Next, I’ll tackle the Name service switch.
    • Seems fine. Configuring Guix Home now. I haven’t done everything in Application Setup yet, but things seem alright. In Guix Home containers, I can’t open graphical programs right now. Maybe a reboot will fix.
  • Name service switch

    [2024-09-29 Sun 08:39] <- Steps taken

    Trying installing musl-nscd through APK. Worked? idk Enabled and started nscd OpenRC service.

    Previous notes of mine said:

    Alpine’s musl-nscd doesn’t seem to satisfy Guix. Uninstall musl-nscd entirely, and let Guix fend for itself.

  • Environment variables

    All exported in ~/.profile.

    Variable Value
    GUIXLOCPATH $HOME/.guix-profile/lib/locale
  • Uninstalling, cleaning
    • Packages

      guix is all I added to world. Remove it.

    • OpenRC services

      If keeping the guix package, disable the guix-daemon OpenRC service.

    • File system
      • Remove /gnu/
      • Remove /var/guix/
      • Remove ~/.guix-profile/
    • Configuration files
      • Remove or comment Guix-related environment variables from ~/.profile

Adding a substitute server

  • Authorizing public key
    guix archive --authorize < PUBKEY_FILE
    
  • Adding URL

    Edit the OpenRC service that runs the Guix daemon.

    /etc/init.d/guix-daemon:

    - command_args="--build-users-group=guixbuild --discover=no"
    + command_args="--build-users-group=guixbuild --discover=no --substitute-urls='https://ci.guix.gnu.org https://bordeaux.guix.gnu.org https://substitutes.nonguix.org'"
    

    Nonguix specifies that the URL shouldn’t contain a trailing slash.

Home

  • Syncthing

    Add the Syncthing home service. See the Guix Networking Home Services manual. Per that, wrap the configuration in (for-home ...), otherwise guix home reconfigure will complain about a missing loopback service.

Lutris

Solution: GRIS crashing with Fontconfig errors

[2024-10-06 Sun 19:07] <- Fontconfig errors

GRIS would display two splash screens and a loading screen, then crash. Logs showed many fontconfig errors. In Lutris, configured GRIS to prefer system libraries. Then, made it to the main menu. [2024-10-06 Sun] haven’t tried to run the game proper yet. [2025-07-09 Wed] I can’t remember this problem, but we did play through Gris with no issues last fall/winter. So whatever I did, hopefully written above, must have worked.

Altering ulimit for Esync

[2025-06-10 Tue 16:16] <- [2025-06-10 Tue] Install attempt

In /etc/security/limits.conf:

+ username hard nofile 524288
 # End of file

Where username is replaced by the user to make the change for.

Game: GRIS

DWM autostart

There’s an autostart patch for DWM.

~/.config/dwm/autostart.sh:

#!/bin/sh
echo 'started' "`date -Is`" >>~/.var/log/dwm-autostart
/usr/libexec/pipewire-launcher 2>&1 >~/.var/log/pipewire-launcher &
# if [ -z "$(pgrep syncthing)" ] ; then
#     syncthing --no-browser 2>&1 >~/.var/log/syncthing &
# fi
playerctld daemon &
dunst &
~/.fehbg &

Manual networking

[2025-07-09 Wed] I’ve successfully set up something I’ve wanted for a while.

On boot, all radio interfaces are rfkill’d, and eth0 is brought up on a hotplug basis. It doesn’t block getting to login quickly.

Here are the necessary configuration files:

auto lo
iface lo inet loopback

allow-hotplug eth0
iface eth0 inet dhcp

iface wlan0 inet dhcp

Some ifupdown implementations respect allow-hotplug as an alternative to auto that will bring up eth0 whenever an ethernet device (?) is detected. ifupdown-ng seems not to support it, so we need this:

#!/bin/sh
ifup eth0 >/dev/null 2>&1 &

Finally, the rfkill:

#!/bin/sh
rfkill block all &

And remember to enable everything:

# rc-update add local
# chmod +x /etc/local.d/50-ifup-eth0.start
# chmod +x /etc/local.d/50-rfkill.start

[2025-08-01 Fri] But now how do I turn on WiFi when I want it?

Running

doas ifup wlan0

starts dhcpcd and tries to connect, but wlan0 received no carrier.

When I restart WPA Supplicant and watch the operstate closely, I get a second or two of dormant, then down again. Don’t know what that’s about.

  • Solved

    ifup wasn’t configured to communicate with the OpenRC wpa_supplicant instance.

    Here’s /etc/network/interfaces/ now:

    # loopback, eth0
    
    iface wlan0 inet dhcp
          wifi-config-path /etc/wpa_supplicant/wpa_supplicant.conf
    

    wifi-config-path is a feature of the particular implementation ifupdown-ng.

    wpa_supplicant is disabled in OpenRC; ifup starts it.

Can I replace ifupdown-ng with BusyBox’s ifupdown?

[2026-04-06 Mon] I don’t think so. Syntaxes are different, as are supporting extensions like the dhcp and wifi-config-path keywords.

OpenRC user services

Follow OpenRC documentation.

I’m getting an error trying to manually start user.ty. Maybe that’s because I’m already logged in? I added user.ty to the default runlevel. Also, I can’t enable user services right now. “Runlevel sysinit doesn’t exist’”. I’ll try rebooting and seeing if these two things are resolved.

Also recall the official service script writing guide.

Disabling screen sleep

I don’t think the ACPI daemon actually ever handles this. See Xorg’s DPMS.

Disable DPMS and turn off screen saver blanking with:

xset s off -dpms

QEMU

Minimal invocation on x8664

[2024-10-18 Fri 19:30] <- Minimal + SSH Debian

From Guix’s virtualization guide.

qemu-system-x86_64 \
    -nic user,model=virtio-net-pci \
    -enable-kvm -m 2048 \
    -device virtio-blk,drive=myhd \
    -drive if=none,file=${IMAGE},id=myhd

Debian

Minimal + SSH Debian

From Guix’s virtualization guide. Forward an address to the host. Make sure to start an ssh server on the guest.

Compared to Minimal invocation on x8664:

-	-nic user,model=virtio-net-pci
+ -nic user,model=virtio-net-pci,hostfwd=tcp::10022-:22	

Yields (with qcow2 path hardcoded):

qemu-system-x86_64 \
    -nic user,model=virtio-net-pci,hostfwd=tcp::10022-:22 \
    -enable-kvm -m 2048 \
    -device virtio-blk,drive=myhd \
    -drive if=none,file='/home/ty/virtual-machines/images/debian.qcow2',id=myhd

Flowing between console and graphical environments

My wisdom so far:

No login manager

Manage long-running processes in .profile, and launch graphical sessions directly from the command line.

One long-running Pipewire session

if [ -z "`pgrep pipewire`" ] ; then
    /usr/libexec/pipewire-launcher 1>>/tmp/ty-stdout-pipewire 2>>/tmp/ty-stderr-pipewire
fi

One long-running Syncthing session

if [ -z "`pgrep syncthing`" ] ; then
    syncthing --no-browser 1>>/tmp/ty-stdout-syncthing 2>>/tmp/ty-stderr-syncthing &
fi

Note the & asynchronicity. This differs from pipewire-launcher, which forks and exits quickly. I don’t think Syncthing provides its own forking flag or subcommand.

Steam Flatpak nuance: run a separate DBus session

The Steam Flatpak demands a DISPLAY env var properly loaded into the DBus activation environment. I couldn’t figure out how to appease it while running a DBus session that persists across and without graphical environments. The simple solution:

dbus-run-session -- flatpak run org.valvesoftware.Steam

Audio connection under Pipewire seems totally fine even with the duplicate DBus sessions.

Lean 4

On musl-libc

Installing elan and components through the Lean 4 extension seems to work well. Plus, components thus installed work even on musl-libc when installed within the VS Code Flatpak. At least, so far ([2024-10-26 Sat]).

Giving up on that. I had to restart the server constantly to see any information. Bad workflow.

On Glibc

On Void Linux now. Working fine.

Error importing Mathlib: “Couldn’t resolve HEAD to a commit”

Solved:

  1. Remove .lake/packages/mathlib.
  2. Clone mathlib4 repo to the same place.
  3. I chose the stable branch and copied the lean-toolchain file into the project root.

lake exe cache get worked after that. I haven’t tried lake update yet.

Zotero

Plugins

Better BibTex for Zotero

Citation key formula:

authorsAlpha + year

Zotfile

I used Zotfile to automatically rename stored attachments to the citation key of the record.

Zotfile is no longer maintained, and there’s no version for Zotero 7. Fortunately, Zotero 7 can rename attachments powerfully enough for me.

Rename attachments in general settings

As of Zotero 7.

Go to

Settings → General → File Renaming → Customize Filename Format...

and set the format to {{ citationKey }}. With BetterBibTex active and working, this is exactly what I want.

Minecraft

Prism Launcher Flatpak on Void Linux: /tmp with noexec

Got this error when launching the Flatpak out of the box: the /tmp directory is mounted with noexec, which may prevent some Minecraft versions from launching. Sure enough, I was getting crashes on all instances.

I added the following to the launcher’s top-level JVM arguments box:

-Djava.io.tmp="/home/ty/.local/tmp"

The error disappeared, and games launched to the menu screen. I haven’t tried playing yet.

The obvious next step is to try and mount ~/.local/tmp as an actual in-memory filesystem to keep things clean. Or, to mount /tmp as exec. I might be able to do that with /etc/fstab; a hot remount (below) didn’t work for me.

$ mount -o remount,exec /tmp