This page permanently redirects to gemini://shit.cx/tech/server/2022-07-01-making-a-daemon-from-a-shell-script-on-openbsd-why-is-it-so-hard-/.
        .     *  ⠈       +                ⠈
                   +  ──╕   ○    .   ⠈           ⠐
   ●     .           ╒═╕╞═╕ ╕ ╪═        *               .
                     ╘═╕│ │ │ │  .cx            +
           .     ....╘═╛╘ ╘ ╘ ╘═ ....:      ⠐        .
                 .               *                ⠐        .

Making a daemon from a shell script on OpenBSD: Why is it so hard?

2022-07-01T02:25

I've been becoming familiar with OpenBSD over the past few weeks. So far I've been liking it a lot. I'm getting so absorbed in it that it's getting into my dreams. That's not something that's happened since I was a kid getting into tech (or maybe it has something to do with the fever I've been fighting off). But today I ran into my first ordeal: running shell scripts through rcctl. It was way more fiddly than I was expecting.

I needed to publish the number of Gemini requests my vger daemon was serving over StatsD to InfluxDB. I looked for something I could take to do the job and only found statsdlog¹. A python app was more complicated than I wanted. I didn't want to deal with python and its dependencies.

I'd been thinking about setting up syslogd to send logs to netcat on localhost which would pipe it to awk to either drop or convert the log into a statsd message. Simple, right?

After a little mucking around, I had something working. It wasn't pretty and it has some unhandled edge-cases, but it's good enough for what I need.

#!/usr/bin/env bash
nc -k -l -u 50123 \
  | while read -d "<" LINE; do
      echo ${LINE} | grep -E "^\<[0-9]+\>" | sed 's/^/

The first netcat listens to a stream of syslog messages. It's then delimited on < which is dodgy but good enough. Awk finds the interesting logs and increments a counter by 1 when found. It also runs fflush without which it buffers the output for some reason. The final netcat sends the statsd messages to the local statsd port.

Next, I needed to write an rc script to manage it and that's when the trouble began.

The first issue was making it kill the correct process. That was sorted out with pexp, but after doing that it wasn't killing the child processes. Netcat was still running even though the parent, bash, was gone. Netcat's ppid switched from bash's pid to 1. Not at all what I was expecting.

So I added some code to handle that. A trap that kills all processes with a ppid of itself.

But that still didn't work. It was like the pipeline wasn't leaving room bash to run its trap. So I ran the pipeline in the background and added a wait to prevent the script from completing.

That fixed it. I'm very surprised at how complicated this is. OpenBSD doesn't come with daemon like FreeBSD has so I avoided using that. Maybe there is a better way, I'm very new to OpenBSD so it's highly likely.

The script that I ended up with is this:

#!/usr/bin/env bash
PID=$$

_cleanup() {
  echo "cleaning up"
  kill $(pgrep -P ${PID})
}

trap '_cleanup' SIGINT SIGQUIT SIGKILL

nc -k -l -u 50123 \
  | while read -d "<" LINE; do
      echo ${LINE} | grep -E "^\<[0-9]+\>" | sed 's/^/

And the rc script looks like this:

#!/bin/ksh

daemon="/usr/local/bin/log2statsd"

. /etc/rc.d/rc.subr

pexp='bash /usr/local/bin/log2statsd'
rc_stop_signal=QUIT

rc_start() {
        ${rcexec} "nohup ${daemon} ${daemon_flags} &"
}

rc_cmd $1

=>

  1. statsdlog


=> More Posts Like This | Return to Homepage

The content for this site is CC-BY-SA-4.0.

Proxy Information
Original URL
gemini://shit.cx/tech/server/2022-07-01-making-a-daemon-from-a-shell-script-on-openbsd-why-is-it-so-hard-
Status Code
Success (20)
Meta
text/gemini;
Capsule Response Time
2938.322981 milliseconds
Gemini-to-HTML Time
0.90866 milliseconds

This content has been proxied by September (3851b).