bound notification delivery and expand placeholders once

Give the notification queue a buffer and drop events when it is full.
The channel was unbuffered, so a slow or hung notification command
blocked whichever UI or network callback happened to raise the event.

Expand the command placeholders in a single pass.
Substituting %event, then %who, then %what meant text arriving in an
earlier field could contain a later placeholder and have it expanded,
letting a remote user inject their own text into the command.

Move the command runner behind a build tag and drop the POSIX default
on Windows.
The helper script it pointed at does not exist there, so the default
was a command that could only fail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon McGinty
2026-08-20 14:42:52 -04:00
co-authored by Claude Opus 5
parent 4f41dd4ed6
commit 97ec48534e
7 changed files with 63 additions and 28 deletions
+22 -26
View File
@@ -9,7 +9,6 @@ import (
"log"
"net/http"
"os"
"os/exec"
"strings"
"time"
@@ -45,31 +44,28 @@ func do_list_devices() {
show_devs("Inputs:", idevs)
}
func setup_notify_runner(notify_command string) chan []string {
t := make(chan []string)
var do_nothing = false
var err error
if err != nil {
}
if notify_command == "" {
do_nothing = true
}
go func(events chan []string, cmd_template string, dummy bool) {
for {
event := <-events
if !dummy {
t := string(cmd_template)
t = strings.ReplaceAll(t, "%event", shellescape.Quote(event[0]))
t = strings.ReplaceAll(t, "%who", shellescape.Quote(event[1]))
t = strings.ReplaceAll(t, "%what", shellescape.Quote(event[2]))
cmd := "/bin/sh"
args := []string{"-c", t}
x := exec.Command(cmd, args...)
x.Run()
} //if we actually have a command to run
} //for
}(t, notify_command, do_nothing)
return t
const notificationQueueSize = 32
func setup_notify_runner(notifyCommand string) chan []string {
events := make(chan []string, notificationQueueSize)
go func() {
for event := range events {
if notifyCommand != "" {
runNotification(expandNotification(notifyCommand, event))
}
}
}()
return events
}
// expandNotification replaces placeholders in one pass so text supplied for
// one field cannot cause another placeholder to be expanded recursively.
func expandNotification(template string, event []string) string {
return strings.NewReplacer(
"%event", shellescape.Quote(event[0]),
"%who", shellescape.Quote(event[1]),
"%what", shellescape.Quote(event[2]),
).Replace(template)
}
func main() {