1
0

Compare commits

...

11 Commits

Author SHA1 Message Date
DTB
e90d844ff4 2025-04-24 2025-04-24 19:58:09 -06:00
DTB
cba16ff731 convert neovim config to lua 2025-04-18 09:54:56 -06:00
DTB
d3cc5416ab convert nvim config to lua 2025-04-18 09:54:10 -06:00
DTB
f14330ee0f 2025-04-18 2025-04-18 09:53:43 -06:00
DTB
12bd97447b 2025-04-05 2025-04-05 18:18:35 -06:00
DTB
5ca407f1d8 2025-04-01 2025-04-01 19:01:22 -06:00
DTB
6d8d0d8c2d just some comments 2025-03-27 16:32:38 -06:00
DTB
24cb04160a make maintainable in a week 2025-03-27 02:48:51 -06:00
DTB
adbaf11d71 move io and keyval to libio and libkeyval 2025-03-27 02:39:23 -06:00
DTB
db13c085c8 better separation of modules 2025-03-27 02:34:39 -06:00
DTB
591f58d70a status mvp 2025-03-27 02:08:07 -06:00
15 changed files with 629 additions and 21 deletions

View File

@@ -1,6 +1,6 @@
/.ignore verbatim
/test ignore
#llllmmmm11234567892123456789312345678941234567895123456789612345678971234567890
#llllmmmm1123456789212345678931234567894123456789512345678961234567897123456789
# vim: syntax=:ts=8
@@ -1050,6 +1050,312 @@ pre { /* DRY who? */
}
/blah/2025-04-24.html
: XDG_RUNTIME_DIR=/tmp/$(id -u)-runtime-dir causes pipewire to crash
So PipeWire wasn't working for inscrutable reasons. Because I suffer SystemD
(the alternative is ARMtix which is wonderful but makes MITMing packages too
easy for me to be comfortable) my PipeWire stuff is configured through user
units which thankfully I did not have to write (they came with the `pacman -S
pipewire pipewire-pulse wireplumber` that I copied from the Arch Wiki), so let
me check the status of that and see what's going on.
$ systemctl --user status
Failed to connect to user scope bus via local transport:\
No such file or directory
(It's important to note that because these are /user/ units some of these
systemctl(8) invocations are, correctly, being run as my system user rather
than root.)
I'd like to explain UNIX error handling. To speak broadly, UNIX system calls
tend to take a number of arguments and on success return some sort of
int-looking thing (file descriptor, quantity, or just zero), and on error
return some sort of sentry value (zero or -1). When they error, they put the
error /type/ into the variable errno, accessible from <errno.h>. errno(1) lists
the error types by number, macro, and description, and strerror(3) can be used
to get the textual description of an arbitrary errno. An errno of 0 means no
error has occurred.
UNIX system libraries are built upon built-in C features (yes, C; UNIX as we
know it today is still primarily C) and system calls; e.g. <stdio.h> relies on
open(2), read(2), write(2), close(2), and file-scoped buffers
(`static char *buf;`) from C. Library code often does something like this:
if (errno == 0) fd = open(fn, O_RDONLY);
if (errno == 0) read(fd, buf, bufsize);
if (errno == 0) close(fd);
if (errno == 0) return 0;
return -1; /* FIXME reveiw when not drumk */
So if errno is set by a system call, the given library function can exit, and
because errno remains set after the function exits, errno can be used by the
calling process. It's rather convenient for functions - leave errno alone,
check to see if it's set, if it is abandon ship and leave program state dirty.
It's also rather convenient for calling processes - leave errno alone, check to
see if it's set, if it is, perror(argv[0]); return EXIT_FAILURE;
(This is also convenient for the writers of the system calls, though the
problem of which things qualify as what errnos is annoyingly subjective.)
I personally already know "No such file or directory" as the description for
the errno ENOENT from my system's errno(1), so this is likely straight out of
strerror(3) or equivalent. But what system call caused this error? I'll use
strace(1).
$ alias p
alias p='bat' # in my .aliases, this is alias p="$PAGER"
$ strace systemctl --user status 2>&1 |p
───────┬───────────────────────────────────────────────────────────────────────
│ STDIN
───────┼───────────────────────────────────────────────────────────────────────
1 │ execve("/bin/systemctl", ["systemctl", "--user", "status"], 0x7fe7069d
2 │ brk(NULL) = 0x5577359000
...
292 │ connect(3, {sa_family=AF_UNIX, sun_path="/tmp/1000-runtime-dir/systemd
/private"}, 37) = -1 ENOENT (No such file or directory)
293 │ close(3) = 0
294 │ ioctl(1, TCGETS, 0x7fc8f59ea0) = -1 ENOTTY (Inappropriate ioc
295 │ newfstatat(AT_FDCWD, "/dev/null", {st_mode=S_IFCHR|0666, st_rdev=maked
296 │ fstat(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
297 │ fstat(2, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
298 │ ioctl(2, TCGETS, 0x7fc8f59f40) = -1 ENOTTY (Inappropriate ioc
299 │ writev(2, [{iov_base="", iov_len=0}, {iov_base="Failed to connect to u
│ en=0}, {iov_base="\n", iov_len=1}], 4Failed to connect to user scope b
│ y
300 │ ) = 83
301 │ exit_group(1) = ?
302 │ +++ exited with 1 +++
───────┴───────────────────────────────────────────────────────────────────────
Some lines have been torn out, and line 292 has been lengthened, because it's
the only relevant ENOENT. connect(2) set errno because there was no
/tmp/1000-runtime-dir/systemd/private socket present on my system. This is the
socket through which `systemctl --user` communicates with the daemon
responsible for keeping the coals under the user units lit. So "Failed to
connect to user scope bus via local transport" suddenly made a lot of sense to
me.
/tmp/1000-runtime-dir was my $XDG_RUNTIME_DIR, set in my $HOME/.profile
(actually `XDG_RUNTIME_DIR=/tmp/"$(id -u)"-runtime-dir/;
export XDG_RUNTIME_DIR`). This didn't stick out to me or anything because why
would it? I'd been running UNIXes for years and it had worked fine on all of
them.
A conversation with Perplexity.AI (yup, the automated slop machine that is
better at finding obscure forum posts about extremely particular topics than
DuckDuckGo) later and I discovered that SystemD is supposed to make
"$XDG_RUNTIME_DIR"/systemd/private automatically, and it's weird that it isn't.
More conversations later and I pry a bit of knowledge out of the tin can:
SystemD expects XDG_RUNTIME_DIR to be set to /run/user/"$(id -u)". It won't
create "$XDG_RUNTIME_DIR"/systemd/private otherwise.
Wait, what?
I changed my $HOME/.profile and rebooted.
All of a sudden `systemctl --user status` just worked. Audio did as well.
/blah/2025-04-18.html
: X without the WM
I use a netbook for /only/ SShing into this CM4 uConsole. The netbook runs
NetBSD/i386 and my usual behavior is something like
sanichi$ ssh laika.lan
laika$ tmux
which works well for me, except for fancy UTF-8 TUI programs, which look ugly,
because the NetBSD tty(4) supports ASCII only (as far as I know, though I did
check the manual).
I don't want windows or decorations, I don't want clocks or status bars, I just
want tmux in a terminal window. I'm going to achieve this using xinit(1) (which
comes with the full NetBSD system) and xdotool(1).
# pkgin install xdotool
This command assumes the use of pkgsrc; adapt it to other package managers if
need be. I don't know much about modular-xorg so you may need to futz a bit to
find xinit(1).
$ cd; vi .xinitrc
And here's the entire .xinitrc:
xdotool search --sync --class xterm windowsize 100% 100% & exec xterm
Not bad for 80B, eh? Let's chart it out:
xdotool ; xdotool(1) - command-line X11 automation tool
search ; Find a certain window.
--sync ; Wait until it's found.
--class xterm ; It calls itself "xterm". When you find it,
windowsize ; resize it.
100% ; Make it fill the view horizontally.
100% ; Make it fill the view vertically.
& ; Don't wait for this command to finish before
; starting the next one.
exec ; Start this next program in the X server, and
; when it finishes, close the X server.
xterm ; xterm(1) - terminal emulator for X
I can exit X by either sending an EOF (that's ^D) from xterm(1) or, if it
freezes, switching to ttyE0 and sending a ^C to the running X server.
I initially passed xterm(1) `-e tmux` to start tmux, but I've found I still
prefer to SSh into laika before starting tmux, so all my terminals are remote
(one for mail, one for writing, one for debugging, one for man pages).
I did add `xrdb -load ~/.Xresources` before the xdotool(1)/xterm(1) line, so
xterm(1) would load with my preferred color scheme which I've already
configured (also see X(7)). I thought this might be automatic, but on here it's
not.
I'm finishing this blah post on my netbook running NetBSD/i386 and an X11
server, in which xterm(1) is running, in which ssh(1) is running, connected to
my uConsole across the room, on which tmux(1) is running, in which nvim(1) is
running. The UTF-8 is beautiful and bat(1) (a guilty pleasure of mine) looks
good.
If I was going to use this computer for anything more than terminals, I'd just
use a window manager. I thought about using sxhkd(1) for this task but it
ultimately proved to be unnecessary.
I added the following to my $ENV (see sh(1)):
# Don't start X if I'm on a framebuffer kick or am in tmux(1) (or screen(1)).
if test -z "$NO_WM" && ! test "$TERM" = screen
then case "$0" in
-*) startx ;; # Do start X if I'm in a login shell
# (see bash(1)).
esac
fi
Which seems to work well. If it failed dreadfully I could reboot and login as
root.
I don't have much more to say. Don't be scared of X - in conjunction with
xinit(1) and startx(1) it's quite easy to use. Don't be scared of Wayland
either - it may not yet run well on NetBSD, but if you get a chance to try it,
you might really like it.
/blah/2025-04-05.html
My habitual "the story so far" was long delayed because I didn't remember what
happened in 2024 - I really just didn't remember anything until my acid trip in
July. I don't know if it's out of trauma or shame or a combination of both.
When I try to remember what happened last year this sweltering sadness
overwhelms me and my eyes well up. Otherwise it seems like just another
forgotten nightmare.
: the story so far (2024)
Season 3: desert
January
Episode 01: "four five six"
Another polycule visits. Trinity finds it hard to balance work and
life.
February
Episode 02: "thirty-three days"
Trinity works for one month straight.
March
Episode 03: "trinity has a mental breakdown"
Trinity has a mental breakdown due to overwork.
April
Episode 04: "toki pona"
The gang goes to a toki pona meetup.
Episode 05: "bicycle day"
Trinity takes two days off to try acid but [...] and [...] worry it'll
have a bad trip.
May
Episode 06: "three's a crowd"
[...] leaves to get [...]. [...] comes back with [...] and Trinity
can't watch a movie without them making out near it.
June
Episode 07: "a match made in hell"
Trinity trains a new coworker.
July
Episode 08: "moxie"
[...] and [...] find a place that sells Moxie.
Episode 09: "suicide is painless"
Trinity contemplates suicide.
Episode 10: "nuture"
Trinity takes 100ug of lysergic acid diethylamine. [...] quits.
Episode 11: "a day off"
Trinity enjoys a day off from work.
Episode 12: "two weeks"
Trinity puts its two week notice in and faces retribution.
August
Episode 13: "a softer world"
Trinity starts a new job.
September
Episode 14: "prog rock"
Trinity goes to a concert with a friend.
November
Episode 15: "pies pies pies"
Trinity, now competent, makes a lot of pies.
Episode 16: "thanksgiving"
Trinity and [...] survive Thanksgiving at [...].
December
Episode 17: "noted"
Trinity gets a notebook.
Episode 18: "yule"
The gang goes to Yule at [...]'s parents' and gets drunk.
Episode 19: "the birds"
Trinity's lost notebook falls out of the sky. Trinity struggles to
think of Christmas presents for everyone.
/blah/2025-04-01.html
: openbsd server
i'm using caddy instead of relayd,httpd,acme because i like an easy config for
web shit and i loathe tls stuff
/etc/caddy/Caddyfile
|
| # lightly modified default
| {
| http_port 8080
| https_port 8443
| admin unix//var/caddy/admin.sock|0220
| }
|
| trinity.moe {
| root * /srv/trinity.moe
| file_server
| }
|
| www.trinity.moe {
| redir https://trinity.moe{uri}
| }
cool and all, right? except caddy can't bind to low ports on openbsd, because
caddy isn't running as root (which is a security issue) and openbsd can't let
non-root processes bind to low ports like linux can. so we've bound to high
ports. let's fix this in pf(4)
/etc/pf.conf
| # [ defaults included, but i'm not copying them over here ]
| pass in on any proto tcp from any to any port 80 rdr-to 127.0.0.1 port 8080
| pass in on any proto tcp from any to any port 443 rdr-to 127.0.0.1 port 8443
okay cool
for a while trinity.moe was hosted on the same machine as feeling.murderu.us,
as of today that is no longer the case (i still own murderu.us and everything,
i just wanted a personal vps for other things too)
maybe there will be more blah posts but probably not
/blah/2024-12-01.html
: vaporware i looked forward to

4
status/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.o
order.c
settings.c
status

28
status/Makefile Normal file
View File

@@ -0,0 +1,28 @@
CFLAGS += -g
.PHONY: run
run: status
./status
.PHONY: cleanall
cleanall: clean
rm -f order.c settings.c
.PHONY: clean
clean:
rm -f status *.o
status: status.c order.c libio.o libkeyval.o
time $(CC) $(CFLAGS) -o $@ $@.c libio.o libkeyval.o
order.c: order.def.c
- cp -v order.def.c $@
libio.o: libio.c libio.h
$(CC) $(CFLAGS) -c -o $@ libio.c
libkeyval.o: libkeyval.c libkeyval.h settings.c
$(CC) $(CFLAGS) -c -o $@ libkeyval.c
settings.c: settings.def.c
- cp -v settings.def.c $@

13
status/README Normal file
View File

@@ -0,0 +1,13 @@
__ ___ __ ___ _ _ __
/ _// // // // // // _/ STATUS
_'. / // _ // / / // /_'. how's your day going?
/__/ /_//_//_//_/ /__/ /__/ 2025 DTB. Public Domain.
build: $ make
libio.c, libio.h: libio, a wrapper around I/O functions for status(1)
libkeyval.c, libkeyval.h: libkeyval, a key/value system for status(1)
*.def.c: the default for foo.c, a configuration file #included
directly in a source-file
mod_*.c: modules to be #included directly in status.h
status.c: main() and boilerplate

19
status/libio.c Normal file
View File

@@ -0,0 +1,19 @@
/* 2025 DTB. Public Domain. */
#undef NDEBUG /* LOAD-BEARING ASSERTS! */
#include <assert.h> /* assert(3) */
#include <errno.h> /* errno */
#include <stdio.h> /* fputs(3), stdout, NULL */
#include "libio.h"
/* output */
static char *hold = NULL;
void
drop(void) { hold = NULL; }
void
out(char *s) {
if (hold != NULL)
{ errno = 0; fputs(hold, stdout); assert(errno == 0); }
hold = s;
}

8
status/libio.h Normal file
View File

@@ -0,0 +1,8 @@
/* 2025 DTB. Public Domain. */
/* libio is a microscopic library that exists to separate less relevant code
* from status(1). It should not be used in critical applications for lack of
* recoverability (as opposed to <stdio.h> on its own). */
void drop(void); /* Set the buffer to NULL. */
void out(char *s); /* Print the buffer if not NULL and replace it with s. */

68
status/libkeyval.c Normal file
View File

@@ -0,0 +1,68 @@
/* 2025 DTB. Public domain. */
#undef NDEBUG /* LOAD-BEARING ASSERTS! */
#include <assert.h> /* assert(3) */
#include <stdlib.h> /* atoi(3) */
#include <string.h> /* strcmp(3) */
#include "libkeyval.h"
static struct State state = { 0 };
void
cpy(char *key) {
state.settings[state.setting_last].key = key;
state.settings[state.setting_last].val = get(key);
assert(++state.setting_last < state_settings_size);
}
void
del(char *key) {
size_t i;
for (i = idx(key); i < state.setting_last; ++i) {
state.settings[i].key = state.settings[i + 1].key;
state.settings[i].val = state.settings[i + 1].val;
}
--state.setting_last;
}
char *
get(char *key) { return state.settings[idx(key)].val; }
int
getd(char *key) { return atoi(get(key)); }
size_t
idx(char *key) {
size_t r;
for (r = 0; state.settings[r].key != NULL
&& strcmp(state.settings[r].key, key) != 0;
++r);
return (state.settings[r].key == NULL) ? state.setting_last : r;
}
void
keyval_init(void) {
/* Unnecessary, but means a wrong state.setting_last won't blow up. */
(void)memset(&state.settings[state.setting_last],
state_settings_size - state.setting_last, '\0');
#include "settings.c"
}
size_t
ren(char *key, char *new) {
size_t i;
if ((i = idx(key)) != state.setting_last)
{ state.settings[i].key = new; }
return i;
}
size_t
set(char *key, char *val) {
size_t i;
i = idx(key);
state.settings[i].key = key;
state.settings[i].val = val;
if (i == state.setting_last)
{ assert(++state.setting_last < state_settings_size); }
return i;
}

29
status/libkeyval.h Normal file
View File

@@ -0,0 +1,29 @@
/* 2025 DTB. Public domain. */
/* libkeyval is a key/value store library to be used for nonessential
* applications due to lack of recoverability (errors fail assert(3)).
* libkeyval deals with pointers and pointers only, and an effort has been made
* to reflect the simplicity of the procedure in the code itself. */
/* keyval store */
#ifndef state_settings_size
# define state_settings_size 100
#endif
struct State {
size_t setting_last;
struct Setting { char *key; char *val; } settings[state_settings_size];
};
void cpy(char *key); /* Duplicate the setting key. */
void del(char *key); /* Delete the first instance of key in settings. */
char * get(char *key); /* Get the corresponding val to key in settings. */
int getd(char *key); /* get() that returns a decimal number if key was a
* number, or 0. */
void keyval_init(void); /* Sanitize the settings store. */
size_t idx(char *key); /* Returns the index of key if in settings, or
* state.setting_last. */
size_t ren(char *key, char *dst); /* Rename the first instance of key in
* settings. */
size_t set(char *key, char *val); /* Set the first instance of key in
* settings. */

5
status/mod_static.c Normal file
View File

@@ -0,0 +1,5 @@
// #include "io.h" /* out(3) */
// #include "keyval.h" /* get(3) */
void
mod_static(char *s) { out(s); out(get("separator")); }

35
status/mod_time.c Normal file
View File

@@ -0,0 +1,35 @@
/* 2025 DTB. Public Domain. */
// #undef NDEBUG /* LOAD BEARING ASSERTS */
// #include <assert.h> /* assert(3) */
// #include <stdlib.h> /* setenv(3), unsetenv(3), NULL */
// #include <time.h> /* localtime(3), strftime(3), time(2), tzset(3), time_t,
// * struct tm */
// #include "io.h" /* out(3) */
// #include "keyval.h" /* get(3) */
#ifndef mod_time_bufsize
# define mod_time_bufsize 100
#endif
void
set_timezone(char *zone) {
if (zone == NULL) { assert(unsetenv("TZ") == 0); }
else { assert(setenv("TZ", zone, 1) == 0); }
tzset();
}
void
mod_time(char *fmt) {
static char buf[mod_time_bufsize];
struct tm *tm;
static time_t t = 0;
static tick_t last_tick = 0;
if (ticker == 0) { t = time(NULL); }
else if (ticker != last_tick) { t += slept; }
last_tick = ticker;
assert((tm = localtime(&t)) != NULL);
assert(strftime(buf, sizeof(buf) / sizeof(*buf), fmt, tm) != 0);
out(buf);
out(get("separator"));
}

52
status/order.def.c Normal file
View File

@@ -0,0 +1,52 @@
/* This is a C source file #included at global scope in status.c. There are a
* number of useful functions defined in status.c and #included headers to try
* to make iteration a little more convenient. */
/* Environment
* getenv(key) gets the value of an environment variable
* setenv(key, val, overwrite) sets key to val in the environment, overwriting
* val if key was already defined and overwrite is 0
* set_timezone(val) sets TZ to val in the environment, then calls tzset(3) to
* set appropriate timezone values. if val is NULL, it clears the TZ val,
* which restores the timezone to the system local time
* get(key) gets the val corresponding to key in settings
* set(key, val) sets the val corresponding to key in settings
* idx(key) returns the index of key in settings
* del(key) deletes the first instance of key in settings
* cpy(key) creates an identical instance to key in settings
* ren(key, dst) renames key to dst in settings
* keyval_init() cleans the settings in case of a glitch */
/* I/O
* out(s) holds s and prints what it was previously holding, if it wasn't NULL
* drop() sets out()'s held value to NULL */
/* Modules
* mod_static(s) outputs s and then the separator val, unless mod_static() is
* called as the last function in order
* mod_time(fmt) outputs the time for the current time zone in the strftime(3)
* fmt */
void
order() {
/* there are a couple good ways to print without the separator. the
* best is to use out() to write directly to the output buffer */
out("utc"); /* => "utc" */
/* but another way is to use status_static to write a message and
* separator, then use drop() to recall the last thing written (always
* the separator in status_* functions) */
mod_static(" "); drop(); /* => " " */
set_timezone("");
mod_time("%Y-%m-%dT%H:%M"); /* => "2025-03-27T00:00 // " */
/* and another way is to use the weird key-val system */
cpy("separator"); /* duplicate setting */
set("separator", " "); /* overwrite first setting */
set_timezone(NULL);
mod_static("local"); /* use first setting */
del("separator"); /* delete the first, restoring the second */
mod_time("%Y-%m-%dT%H:%M");
drop();
/* => "local 2025-03-26T18:00" */
/* all => "utc 2025-03-27T00:00 // local 2025-03-26T18:00" */
}

2
status/settings.def.c Normal file
View File

@@ -0,0 +1,2 @@
set("interval_seconds", "1");
set("separator", " // ");

32
status/status.c Normal file
View File

@@ -0,0 +1,32 @@
#undef NDEBUG /* LOAD-BEARING ASSERTS! */
#include <assert.h> /* "mod_time.c" */
#include <limits.h> /* UCHAR_MAX */
#include <stdlib.h> /* "mod_time.c" */
#include <time.h> /* "mod_time.c" */
#include <unistd.h> /* sleep(3) */
#include "libio.h" /* drop(3), out(3), "mod_static.c" */
#include "libkeyval.h" /* cpy(3), del(3), get(3), keyval_init(3), set(3),
* "mod_static.c" */
#define ever (;;)
unsigned int slept = 0;
typedef unsigned char tick_t;
#define TICK_MAX UCHAR_MAX
tick_t ticker = 0;
#include "mod_static.c"
#include "mod_time.c"
#include "order.c"
int main(int argc, char *argv) {
keyval_init();
for ever {
order(); drop(); out("\n");
slept = sleep(getd("interval_seconds"));
ticker = ++ticker % TICK_MAX;
}
}

View File

@@ -0,0 +1,26 @@
vim.cmd.colorscheme("torte");
vim.cmd.syntax("on");
vim.cmd([[
set mouse=
set noautoindent
set nocp
set noexpandtab
set noim
set nois
set nowrap
call plug#begin('~/.vim/plugged')
Plug 'Olical/conjure'
Plug 'atweiden/vim-fennel'
call plug#end()
]]);
vim.opt.bg = "dark";
vim.opt.colorcolumn = "80";
vim.opt.nu = true;
vim.opt.relativenumber = true;
vim.opt.rnu = true;
vim.opt.ruler = true;
vim.opt.tabstop = 8;
vim.opt.tf = true;
vim.opt.title = true;
vim.opt.vb = true;

View File

@@ -1,19 +0,0 @@
colorscheme torte
set bg=dark
set colorcolumn=80
set mouse=
set noautoindent
set nocp
set noexpandtab
set noim
set nois
set nu
set relativenumber
set rnu
set ruler
set tabstop=8
set tf
set title
set vb
set nowrap
syntax on