From 87758403be31772f94ed43caa62750ae6fa3dc5e Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Thu, 23 Oct 2025 09:36:46 -0500 Subject: [PATCH 1/2] Allow handlers directory to be set at compile time Signed-off-by: Andy Doan --- internal/app.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/app.go b/internal/app.go index fbc1718..926cac8 100644 --- a/internal/app.go +++ b/internal/app.go @@ -20,6 +20,7 @@ import ( const onChangedForceExit = 123 var NotModifiedError = errors.New("Config unchanged on server") +var HandlersDir = "/usr/share/fioconfig/handlers/" type CryptoHandler interface { Decrypt(value string) ([]byte, error) @@ -169,7 +170,7 @@ func (a *App) runOnChanged(fname string, fullpath string, onChanged []string) { } if len(onChanged) > 0 { binary := filepath.Clean(onChanged[0]) - if a.unsafeHandlers || strings.HasPrefix(binary, "/usr/share/fioconfig/handlers/") { + if a.unsafeHandlers || strings.HasPrefix(binary, HandlersDir) { slog.Info("Running on-change command", "file", fname, "args", onChanged) cmd := exec.Command(onChanged[0], onChanged[1:]...) cmd.Env = append(os.Environ(), "CONFIG_FILE="+fullpath) From 901521afa2fbd25c0f08180facf0fada3ede54dd Mon Sep 17 00:00:00 2001 From: Andy Doan Date: Thu, 23 Oct 2025 11:34:32 -0500 Subject: [PATCH 2/2] Make handler output easier to identify Prefix all the change handler output with "| " so that our logs are easier to follow Signed-off-by: Andy Doan --- internal/app.go | 5 ++--- internal/exec.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 internal/exec.go diff --git a/internal/app.go b/internal/app.go index 926cac8..6aad8b9 100644 --- a/internal/app.go +++ b/internal/app.go @@ -177,9 +177,8 @@ func (a *App) runOnChanged(fname string, fullpath string, onChanged []string) { cmd.Env = append(cmd.Env, "STORAGE_DIR="+a.StorageDir) cmd.Env = append(cmd.Env, "SOTA_DIR="+strings.Join(a.sota.SearchPaths(), ",")) cmd.Env = append(cmd.Env, "FIOCONFIG_BIN="+path) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { + + if err := ExecIndented(cmd, "| "); err != nil { slog.Error("Unable to run command", "command", onChanged, "error", err) if exitError, ok := err.(*exec.ExitError); ok { if exitError.ExitCode() == onChangedForceExit { diff --git a/internal/exec.go b/internal/exec.go new file mode 100644 index 0000000..487c6ae --- /dev/null +++ b/internal/exec.go @@ -0,0 +1,48 @@ +package internal + +import ( + "bufio" + "fmt" + "io" + "log/slog" + "os" + "os/exec" + "sync" +) + +func ExecIndented(cmd *exec.Cmd, indentChars string) error { + stdout, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("failed to get stdout: %w", err) + } + + stderr, err := cmd.StderrPipe() + if err != nil { + return fmt.Errorf("failed to get stderr: %w", err) + } + + if err = cmd.Start(); err != nil { + return fmt.Errorf("failed to start command: %w", err) + } + + var wg sync.WaitGroup + wg.Add(2) + + go prefixAndCopy(indentChars, stdout, os.Stdout, &wg) + go prefixAndCopy(indentChars, stderr, os.Stderr, &wg) + + wg.Wait() + return cmd.Wait() +} + +// prefixAndCopy reads from r line by line and writes to w with "| " prefix. +func prefixAndCopy(prefix string, r io.Reader, w io.Writer, wg *sync.WaitGroup) { + defer wg.Done() + scanner := bufio.NewScanner(r) + for scanner.Scan() { + fmt.Fprint(w, prefix, scanner.Text(), "\n") + } + if err := scanner.Err(); err != nil { + slog.Error("Error reading command output", "error", err) + } +}