This document is aimed to provide an enviroment for building a bootable OpenBSD system image, using QEMU and Org. The entire process can be automated and reproducible. The result image will be partitioned with GPT and bootable on UEFI machines.
It’s possible to build an entire system automatically by “publishing” this document:
emacs --quick README.org --batch --load=init.el --funcall=org-md-export-to-markdown --kill
Supported OpenBSD versions:
- 7.2
- 7.3
The main requirements are QEMU, Expect & GNU Emacs. Pass is used to managed passwords, and can be changed to something else (i.e literals) in the definitions below.
emacs | >= 27.2 |
qemu | >= 6.0.0 |
expect | >= 1.1 |
bash | |
pass |
See shell.nix
with import <nixpkgs> {};
stdenv.mkDerivation {
name = "emacs-qemu-enviroment";
buildInputs = [
pkgs.gnutar
pkgs.wget
pkgs.qemu
pkgs.coreutils
pkgs.openssh
pkgs.rsync
pkgs.expect
pkgs.emacs-nox
pkgs.netcat-gnu
pkgs.inetutils
pkgs.pass
];
}
(emacs-version)
qemu-kvm --version
expect -version
This definitions are needed to provide dynamic values in `header-args`.
For example:
#+begin_src shell :eval (a-is-system-p 'openbsd) zzz #+end_src
(defvar a-ref-cache (make-hash-table :test 'equal))
(defun a-ref (name)
(or (gethash name a-ref-cache)
(let ((value
(save-excursion
(org-babel-execute-src-block nil
(org-babel-lob-get-info
(list
'babel-call
(list
:call
"ref-unquoted"
:arguments
(concat
"'"
name))))))))
(puthash name value a-ref-cache))))
(defun a-is-system-p (sys)
"Which is the host system-p. "
(if (eq system-type sys) "yes" "no"))
(defun a-host ()
(concat "#!" default-directory "host-shell"))
(defvar a-guest (concat "#!" default-directory "guest-shell"))
(defun a-sudo ()
"Directory for executing as root. XS is sub directories. "
(concat "/sudo::/" default-directory))
(defvar a-remote
(let ((user (a-ref "user-name"))
(port (a-ref "ssh-port")))
(concat (format "/ssh:%s@localhost#%d:" user port))))
(defvar a-remote-sudo
(let ((user (a-ref "user-name"))
(port (a-ref "ssh-port")))
(concat (format "/ssh:%s@localhost#%d|sudo:" user port))))
(defun a-contains-p? (needle haystack)
(cl-assert
(cl-search needle haystack)))
(defun a-not-contains-p? (needle haystack)
(cl-assert
(not (cl-search needle haystack))))
Clean up this document from all result blocks.
(defun a-ob-clear-all-results ()
"Clear all results in the buffer."
(interactive)
(save-excursion
(goto-char (point-min))
(while (org-babel-next-src-block)
(org-babel-remove-result))))
«log»
if [ -e "$LOG" ]
then
mv --verbose --force "$LOG" "${LOG//.log}$(date +%s).log"
fi
log_user 0
spawn telnet localhost «ref('serial-port)»
log_user 1
log_user 0
spawn telnet localhost «ref('monitor-port)»
log_user 1
expect -re {(?n)(?:[a-z]+)?[#]\s}
Executed during boot of install.img
«serial»
«timeout»
expect "boot>"
send "stty com0 115200\r"
expect "boot>"
sleep 1
send "set tty com0\r"
expect "boot>"
sleep 1
send "boot\r\r\r"
sleep 1
expect "\r"
exit
Executed during boot of install.img
«monitor»
«timeout»
set command [lindex $argv 0];
expect "(qemu)"
send "$command\r"
expect "(qemu)"
exit
set timeout -1
set timeout 10
«serial»
«notimeout»
send "\r";
expect "(I)nstall, (U)pgrade, (A)utoinstall or (S)hell?" { send "S\r" }
«expect-prompt»
Execute a shell command via COM.
set command [lindex $argv 0];
«serial»
«timeout»
set send_human {.1 .3 1 .05 2}
send -h "$command"
send -h "\r"
after 1000 set stop_wait &
vwait stop_wait
unset stop_wait
«expect-prompt»
A wrapper for the script above to use it as part of shebang.
./execute "$(sed /^#!/d "$1")"
This block is executed via Buffer settings
& init.el.
(require 'ob-shell)
(require 'ob-eshell)
(require 'cl)
(setq org-babel-eval-verbose nil)
«definitions»
(defun a-babel-ansi-color-apply ()
(when-let ((beg (org-babel-where-is-src-block-result nil nil)))
(save-excursion
(goto-char beg)
(when (looking-at org-babel-result-regexp)
(let ((end (org-babel-result-end))
(ansi-color-context-region nil))
(ansi-color-apply-on-region beg end))))))
(add-hook 'org-babel-after-execute-hook 'a-babel-ansi-color-apply)
This is useful for debugging.
All code with this shebang will log its stderr & stdout to $LOG
.
LOG=${LOG:-output.log}
«log»
if [ "$LOG" ]
then
exec 1> >(tee -a "$LOG") 2> >(tee -a "$LOG" >&2)
fi
exec "$SHELL" "$@" </dev/stdin
(let ((files
(eval
contains-p)))
(dolist (file files)
(assert (file-exists-p (expand-file-name file)))))
(format "%s" block)
(eval contains-p)
(format "%s" block)
Reference a value from Parameters.
(let ((key (if (symbolp name)
(symbol-name name) name)))
(nth 2 (assoc key table)))
echo «ref("user-name")»
The following table is to parameterize the system.
Reference | Description | Value |
---|---|---|
hostname | Hostname | puffy |
domain | Domain | cx |
volume-size | Volume size | 16005464064B |
time-zone | Timezone | America/New_York |
root-password | Root’s password | toor |
serial-port | Tty Port | 1234 |
monitor-port | Monitor Port | 1233 |
ssh-port | Ssh Port | 7922 |
arch | Architecure (only amd64) | amd64 |
system-image | Result image | /dev/sdb |
release | Release | 7.4 |
install-image | Installation image | install74.img |
image-format | Result image format | raw |
(qcow2 or raw) | ||
mirror | Mirror | https://cdn.openbsd.org/pub/OpenBSD |
user-name | Regular user name | a |
user-id | Id | 1337 |
user-group | Primary group | staff |
user-shell | Shell of user | bash |
The password for SoftRAID is generated by pass.
Show password:
(if (not (string-empty-p name))
(string-trim (shell-command-to-string (concat "pass show" " " (a-ref "hostname") "/" name))))
Generate password:
(if (not (string-empty-p name))
(string-trim (shell-command-to-string (concat "pass generate --no-symbols " (a-ref "hostname")
"/" name " " length))))
wget --continue «ref("mirror")»/«ref("release")»/«ref("arch")»/«ref("install-image")» \
«ref("mirror")»/«ref("release")»/«ref("arch")»/SHA256 \
«ref("mirror")»/«ref("release")»/«ref("arch")»/SHA256.sig
ftp «ref("mirror")»/«ref("release")»/«ref("arch")»/«ref("install-image")» \
«ref("mirror")»/«ref("release")»/«ref("arch")»/SHA256 \
«ref("mirror")»/«ref("release")»/«ref("arch")»/SHA256.sig
NOTE: The installation image is mutable, the checksum most likely won’t match after the first boot.
sha256sum --ignore-missing --check SHA256
signify -C -x SHA256.sig «ref("install-image")»
Make sure that UFS can be mounted with RW permissions. For example, on NixOS it can be enabled like this in boot.nix:
boot.kernelPatches = [
{
name = "ufs-rw-support";
patch = null;
extraConfig = "UFS_FS_WRITE y";
}
];
zgrep UFS_FS /proc/config.gz
losetup --partscan /dev/loop0 install73.img
sfdisk -l /dev/loop0
mkdir -p /tmp/install
mount -t ufs -o ufstype=44bsd,rw /dev/loop0p4 /tmp/install
echo "stty com0 115200" >> /tmp/install/etc/boot.conf
echo "set tty com0" >> /tmp/install/etc/boot.conf
umount /tmp/install
losetup --detach-all
waitport() {
while ! nc -z localhost "${1:?no argument}" ; do sleep 3; done
}
QEMU_MEM=4g
QEMU_CPU=host
QEMU_PID=.pid
QEMU_COMMAND=qemu-kvm
Ports for Monitor and Serial console:
QEMU_MON_PORT=«ref("monitor-port")»
QEMU_SER_PORT=«ref("serial-port")»
QEMU arguments: System drive:
QEMU_SYSTEM_DRIVE=(
-device scsi-hd,drive=hd0
-drive file=«ref("system-image")»,media=disk,snapshot=off,if=none,id=hd0,format=«ref("image-format")»
)
Installation drive:
QEMU_INSTALL_DRIVE=(
-drive file=«ref("install-image")»,media=disk,format=raw
)
Key-disk drive:
QEMU_KEY_DRIVE=(
-device scsi-hd,drive=hd1
-drive file=key.raw,media=disk,snapshot=off,if=none,id=hd1,format=raw
)
Monitor device:
QEMU_MONITOR=(
-monitor chardev:mon0
-chardev socket,id=mon0,server=on,wait=off,telnet=on,port=$QEMU_MON_PORT,host=localhost,ipv4=on,ipv6=off
)
Serial device:
QEMU_SERIAL=(
-serial chardev:ser0
-chardev socket,id=ser0,server=on,wait=off,telnet=on,port=$QEMU_SER_PORT,host=localhost,ipv4=on,ipv6=off
)
Network with port forwarding:
QEMU_NETWORK=(
-netdev tap,id=mn0,br=virbr0,helper=$(type -p qemu-bridge-helper)
-device virtio-net,netdev=mn0,mac=00:00:00:00:00:01
)
QEMU_SOUND=(
-audio pipewire,model=es1370,out.frequency=48000
)
QEMU_OPTS=(
-vnc :0
-smp 3
-cpu phenom
-display none
-vga virtio
-m "$QEMU_MEM"
-cpu "$QEMU_CPU"
-bios bios/ovmf-x64/OVMF-pure-efi.fd
-device virtio-scsi-pci,id=scsi
)
QEMU_OPTS+=("${QEMU_NETWORK[@]}")
QEMU_OPTS+=("${QEMU_MONITOR[@]}")
QEMU_OPTS+=("${QEMU_SERIAL[@]}")
QEMU_OPTS+=("${QEMU_SYSTEM_DRIVE[@]}")
QEMU_OPTS+=("${QEMU_SOUND[@]}")
QEMU_OPTS+=(-pidfile "$QEMU_PID" -daemonize)
__boot() {
./type-password «pass-show("bioctl")»
}
__status() {
if [ ! -e "$QEMU_PID" ]
then
>&2 echo "Not running"
exit 1
fi
PID="$(< "$QEMU_PID")"
if kill -0 "$PID" >/dev/null 2>/dev/null
then
>&2 echo "Running: $PID"
else
>&2 echo "Stopped: $PID"
exit 1
fi
}
__start() {
[ -e "$QEMU_PID" ] && >&2 echo "Already running" && exit 1
"$QEMU_COMMAND" "${QEMU_OPTS[@]}"
waitport "$QEMU_MON_PORT"
waitport "$QEMU_SER_PORT"
while :
do
PID=$(cat < "$QEMU_PID")
kill -0 $PID && echo Running && break
done
}
if [ "${USE_KEYDISK:-0}" -eq "1" ]
then
QEMU_OPTS+=("${QEMU_KEY_DRIVE[@]}")
fi
if [ "${USE_INSTALL:-1}" -eq "1" ]
then
QEMU_OPTS+=("${QEMU_INSTALL_DRIVE[@]}")
fi
case "${1:?no arg}" in
start)
__start
__boot
;;
status)
__status
;;
reset)
__status
./qemu-command system_reset
;;
poweroff)
__status
./qemu-command system_powerdown
;;
stop)
[ -e "$QEMU_PID" ] && xargs kill < "$QEMU_PID"
rm --force "$QEMU_PID"
;;
restart)
"$0" stop
"$0" start
;;
esac
Installing UEFI Bios for QEMU. This BIOS does not support CD, this is why we are using a USB image.
wget --continue https://packages.slackonly.com/pub/packages/14.2-x86_64/system/ovmf/ovmf-20171116-noarch-1_slonly.txz
tar -C ./bios -xvf ovmf*txz --strip-components=2
qemu-img create -f «ref("image-format")» «ref("system-image")» «ref("volume-size")»
Stop VM:
./vm status
./vm stop
Start VM:
env USE_GRAPHIC=1 USE_INSTALL=0 ./vm start
./set-tty
./start-shell
Print names of available disks:
sysctl hw.disknames
You should see the target image being attached as sd0
.
dmesg | grep sd[0-9]
Installation media should be available as wd0
(if installing from img file)
dmesg | grep wd[0-9]
cd /dev
sh MAKEDEV sd0
sh MAKEDEV sd1
sh MAKEDEV sd2
ls -l sd*a
dd if=/dev/zero of=/dev/rsd0c bs=1m count=100
fdisk -iy -g -b 960 sd0
The same for keydisk (Optional)
fdisk -iy -g -b 960 sd1
Create one RAID partition using entire disk space.
{
echo a a
echo
echo
echo raid
echo w
echo q
} | disklabel -E sd0
disklabel sd0
Prepare keydisk (Optional)
{
echo a a
echo
echo
echo raid
echo w
echo q
} | disklabel -E sd1
disklabel sd1
Create bioctl(8) Crypto RAID
NOTE: New line at EOF is required.
echo «pass-show("bioctl")» > /tmp/.passphrase
chmod 0600 /tmp/.passphrase
ls -l /tmp/.passphrase
Initialize RAID on sd0
bioctl -p /tmp/.passphrase -c C -l sd0a softraid0
Using keydisk (Optional)
bioctl -k sd1a -c C -l sd0a softraid0
Send ^D and press enter.
«serial»
set send_human {.1 .3 1 .05 2}
send "\x04"
send "\r"
expect "(I)nstall, (U)pgrade, (A)utoinstall or (S)hell?" { send "I\r" }
expect "Terminal type?" { send "vt220\r" }
expect "System hostname?" { send «ref("hostname")»; send "\r" }
Do not configure network interfaces.
expect "Network interface to configure?" {
send "done\r"
}
DNS Domain name.
expect "DNS domain name?" {
send «ref("domain")»;
send "\r";
}
DNS Domain name.
expect "DNS nameservers?" {
send "1.1.1.1\r";
}
Root password.
expect "Password for root account?" {
sleep 1
send -h «ref("root-password")»
send "\r"
}
expect "Password for root account? (again)" {
sleep 1
send -h «ref("root-password")»
send "\r"
}
Do not start sshd(8) by default yet. Will be enabled later.
expect "Start sshd(8) by default?" {
send "no\r"
}
Do not start xenodm(1) by default yet. Will be enabled later.
expect "Do you want the X Window System to be started by xenodm(1)?" {
send "no\r"
}
Keep COM0 available after reboot to the freshly installed system. Will be disabled after sshd(8) is enabled.
expect "Change the default console to com0?" {
send "yes\r"
expect "Which speed should com0 use?" {
send "115200\r"
}
}
No need to add a user at this step.
expect "Setup a user?" {
send "no\r"
}
expect "Allow root ssh login?" {
send "no\r"
}
expect "Which disk is the root disk?" {
send "sd1\r"
}
expect "Use (W)hole disk MBR, whole disk (G)PT" {
send "gpt\r"
}
Use custom layout, use entire volume as /.
expect "Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout?" {
send "c"
send "\r"
expect ">" { send "a a" ; send "\r" }
expect "offset:" { send "\r" }
expect "size:" { send "90%" ; send "\r" }
expect "FS type:" { send "\r" }
expect "mount point:" { send "/" ; send "\r" }
expect ">" { send "a b" ; send "\r" }
expect "offset:" { send "\r" }
expect "size:" { send "*" ; send "\r" }
expect "FS type:" { send "swap" ; send "\r" }
expect "*>" { send "w" ; send "\r" }
expect ">" { send "p" ; send "\r" }
expect ">" { send "q" ; send "\r" }
}
(Alternative) Use automatic layout, which produces different results depending on volume size.
expect "Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout?" {
send "a\r"
}
expect "Which disk do you wish to initialize?" {
send "done\r"
}
expect "Location of sets?" {
send "disk\r"
}
expect "Is the disk partition already mounted?" {
send "no\r"
}
Install from `wd0`, which is USB installation media.
expect "Which disk contains the install media?" {
send "wd0\r"
}
expect "Which wd0 partition has the install sets?" {
send "a\r"
}
expect "Pathname to the sets?" {
send "\r"
}
Install everything but games.
expect "Set name(s)?" {
send -- "-game*\r\r"
}
There is no SHA256.sig on the installation drive. This step will triger installation, thus “notimeout”.
expect "Continue without verification?" {
send "yes\r"
«notimeout»
}
expect "Location of sets? (disk http nfs or 'done')" {
send "done\r"
}
expect "What timezone are you in?" {
send «ref("time-zone")»;
send "\r";
}
Not ready to reboot yet. Go back to the shell to install UEFI.
expect "Exit to (S)hell, (H)alt or (R)eboot?" {
send "S\r"
«expect-prompt»
}
./setup-dialog
newfs_msdos /dev/sd0i
mount /dev/sd0i /mnt2
cp /mnt/usr/mdec/BOOTX64.EFI /mnt2/efi/boot/
mount
umount /dev/sd0i
echo 'swap /tmp mfs rw,nodev,nosuid,-s=300m 0 0' >> /mnt/etc/fstab
chmod 1777 /mnt/tmp
halt
sleep 5
./vm stop
Start VM without the installation media, and type cryptodisk password:
./vm stop
USE_INSTALL=0 ./vm start
./type-password «pass-show("bioctl")»
Login as root via COM
./login root «ref("root-password")»
«serial»
set password [lindex $argv 0];
«timeout»
expect "boot>" { send "set device sr0a\r" }
expect "boot>" { send "\r" }
expect "Passphrase: " { send "$password\r" }
expect "boot>" { send "machine gop 15\r" }
expect "boot>" { send "boot /bsd\r" }
expect "booting"
«notimeout»
«serial»
set user [lindex $argv 0];
set password [lindex $argv 1];
send "\r\r\r"
expect "login:"
send "$user\r"
sleep 1
expect "Password:"
send "$password\r"
«expect-prompt»
sleep 1
«serial»
send "\r"
«expect-prompt»
send "adduser\r"
If /etc/adduser.conf doesn’t exits…
expect "Couldn't find /etc/adduser.conf" {
expect "Enter your default shell:" { send "ksh\r"; }
expect "Default login class:" { send "default\r"}
expect "Enter your default HOME partition:" { send "/home\r"; }
expect "Copy dotfiles from:" { send "/etc/skel\r"; }
expect "Send welcome message?" { send "no\r"; }
expect "Prompt for passwords by default" { send "no\r"; }
expect "Default encryption method for passwords:" { send "blowfish\r" }
}
New user
expect "Enter username" { send «ref('user-name)» ; send "\r" }
expect "Enter full name" { send "\r" }
expect "Enter shell" { send "ksh\r" }
expect "Uid" { send «ref('user-id)» ; send "\r" }
expect "Login group" { send «ref('user-group)» ; send "\r" }
expect "Invite a into other groups" { send "no\r" }
expect "Login class" { send "default\r" }
expect "OK?" { send "y\r" }
expect "Add another user?" { send "n\r" }
«expect-prompt»
./adduser
Configure doas(8)
Disable password promt for staff group. See doas.conf(5)
echo permit nopass :«ref("user-group")»| tee /etc/doas.conf
Change default parameters of sshd(8)
cp /etc/ssh/sshd_config{,.orig}
rm /etc/motd
ln -s /dev/null /etc/motd
perl -i -pE 's/[#]?(Banner) \s+ \S+/$1 none/x' /etc/ssh/sshd_config
perl -i -pE 's/[#]?(PrintMotd) \s+ \S+/$1 no/x' /etc/ssh/sshd_config
perl -i -pE 's/[#]?(X11Forwarding) \s+ \S+/$1 yes/x' /etc/ssh/sshd_config
perl -i -pE 's/[#]?(PasswordAuthentication) \s+ \S+/$1 no/x' /etc/ssh/sshd_config
perl -i -pE 's/[#]?(PermitRootLogin) \s+ \S+/$1 no/x' /etc/ssh/sshd_config
Enable sshd(8)
rcctl enable sshd
rcctl restart sshd
(string-trim (shell-command-to-string "ssh-add -L"))
echo «ssh-key()» | doas -u a tee /home/$user/.ssh/authorized_keys
{
echo "-inet6"
echo "dhcp"
echo "up"
} | tee /etc/hostname.vio0
perl -i -pE 's/(nameserver) \s+ \S+/$1 8.8.8.8/x' /etc/resolv.conf
sh /etc/netstart
Add fingerprint to known_hosts
ssh-keyscan -p «ref("ssh-port")» -H localhost | tee -a ~/.ssh/known_hosts
Tramp does not support doas(8). Let’s install & configure sudo with the same permissions as doas.
echo "%$group ALL=(ALL) NOPASSWD: SETENV: ALL" | doas tee /etc/sudoers
doas fw_update -a
doas fw_update iwm
doas fw_update iwi
doas fw_update iwn
doas fw_update iwx
doas perl -i.bak -nE 'print unless /com0/' /etc/boot.conf
echo "machine gop 15" | doas tee -a /etc/boot.conf
echo 'keyboard.bell.volume=0' | doas tee /etc/wsconsctl.conf
echo 'keyboard.map+="keysym Caps_Lock = Control_L"' | doas tee -a /etc/wsconsctl.conf
echo 'display.screen_off=60000' | doas tee -a /etc/wsconsctl.conf
echo 'vm.swapencrypt.enable=1' | doas tee /etc/sysctl.conf
echo 'machdep.lidaction=2' | doas tee -a /etc/sysctl.conf
echo 'machdep.pwraction=1' | doas tee -a /etc/sysctl.conf
doas rcctl disable ntpd
doas rcctl stop ntpd
doas perl -i.bak -pE 's/(?<=rw)(?!,noatime)/,noatime/' /etc/fstab
doas perl -i.bak -pE 's/(?<=rw)(?!,softdep)/,softdep/' /etc/fstab
doas rcctl set sndiod flags -b24000
doas rcctl restart sndiod
Unlock database in case it’s locked (see pkg_check(8)).
doas pkg_check -f
Install a package (see pkg_add(8)).
[ "$NAME" ] && doas pkg_add -x -r "$NAME"
UA_FIX=https://gist.githubusercontent.com/andreoss/91a0d21dc99bd9eae8bce5d573fb5a00/raw/64d0926caf60103efab55ea4cc4150d5d86b369a/ua.js
SER_JS=https://raw.githubusercontent.com/pyllyukko/user.js/master/user.js
FIREFOX_BASE=/usr/local/lib/firefox
doas cp "$FIREFOX_BASE"/browser/defaults/preferences/all-openbsd.js{,.back}
curl "$USER_JS" | grep -v captive-portal | \
doas tee -a "$FIREFOX_BASE"/browser/defaults/preferences/all-openbsd.js >/dev/null
curl "$UA_FIX" | doas tee -a "$FIREFOX_BASE"/browser/defaults/preferences/all-openbsd.js
doas sed -i s/user_pref/pref/ "$FIREFOX_BASE"/browser/defaults/preferences/all-openbsd.js
chsh -s `which $XSHELL`
chsh -s "/bin/ksh"
-A
- enables performance adjustment mode
-Z 5
- hibernate at 5% battery life
See apmd(8).
doas mkdir /etc/apm
echo "#!/bin/sh" | doas tee /etc/apm/suspend
echo "pkill -USR1 xidle" | doas tee -a /etc/apm/suspend
doas chmod +x /etc/apm/suspend
doas rcctl enable apmd
doas rcctl set apmd flags -A -Z 5
doas rcctl start apmd
doas rcctl check apmd
$include /etc/inputrc
set bell-style visible
set blink-matching-paren on
set visible-stats on
$if mode=vi
set editing-mode vi
set keymap vi
set vi-cmd-mode-string "*"
set vi-ins-mode-string " "
set show-mode-in-prompt on
Control-l: clear-screen
set keymap vi-command
Control-l: clear-screen
set keymap vi-insert
Control-l: clear-screen
$endif
set emacs-mode-string "&"
doas rcctl enable dnscrypt_proxy
doas rcctl start dnscrypt_proxy
doas rcctl check dnscrypt_proxy
See man dhclient.conf
.
echo "supersede domain-name-servers 127.0.0.1;" | doas tee /etc/dhclient.conf
Restart network
doas sh /etc/netstart
Now resolv.conf should contains-p local DNS server
grep nameserver /etc/resolv.conf
echo "DisplayManager.*.autoLogin: «ref('user-name)»" | doas tee -a /etc/X11/xenodm/xenodm-config
doas rcctl enable xenodm
doas rcctl start xenodm
doas rcctl check xenodm
which ratpoison
echo "exec ratpoison" > ~/.xsession
https://www.quicklisp.org/beta-
curl -O https://beta.quicklisp.org/quicklisp.lisp
curl -O https://beta.quicklisp.org/quicklisp.lisp.asc
gpg --keyserver pgp.mit.edu --recv-keys 028B5FF7
gpg --verify quicklisp.lisp.asc quicklisp.lisp
sbcl --non-interactive --load quicklisp.lisp --eval "(quicklisp-quickstart:install)"
Add Quicklisp to .sbclrc
sbcl --non-interactive --load quicklisp/setup.lisp --eval "(ql-util:without-prompting (ql:add-to-init-file))"
sbcl --non-interactive --eval "(ql:quickload :bt-semaphore)"
sbcl --non-interactive --eval "(ql:quickload :external-program)"
sbcl --non-interactive --eval "(ql:quickload :swank)"
sbcl --non-interactive --eval "(ql:quickload :stumpwm)"
git clone https://github.com/andreoss/.stumpwm.d
echo "exec ~/.stumpwm.d/start.sh" > ~/.xsession
which urxvt
git clone https://github.com/andreoss/.config
ln -s ~/.config/shrc ~/.kshrc
ln -s ~/.config/shrc ~/.bashrc
ln -s ~/.config/shrc ~/.bash_profile
ln -s ~/.config/inputrc ~/.inputrc
git clone https://github.com/andreoss/.emacs.d
which rsync
rsync -Lahv -e 'ssh -p «ref('ssh-port)»' ~/.ssh localhost:
rsync -Lahv -e 'ssh -p «ref('ssh-port)»' ~/.gnupg localhost:
rsync -Lahv -e 'ssh -p «ref('ssh-port)»' "$PASSWORD_STORE_DIR" localhost:
#!/bin/sh
mkdir -p ~/src
cd ~/src
if [ ! -d emacs ]; then git clone https://github.com/emacs-mirror/emacs; fi
cd ~/src/emacs
git pull
gmake clean
AUTOCONF_VERSION=2.65
CC=egcc
MAKEINFO='/usr/local/bin/gmakeinfo'
export AUTOCONF_VERSION MAKEINFO CC
./autogen.sh
./configure --prefix=$HOME/.local --with-json --with-x=lucid
gmake bootstrap
gmake
gmake install