sevlyar / go-daemon

A library for writing system daemons in golang.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Context.Reborn fails with "write |1: broken pipe" due to a race condition in writing to Context.wpipe

avoronkov opened this issue · comments

Platform, version

  • Almalinux 9, x86_64 on Virtuozzo 7 virtualization.

  • go-daemon v0.1.6

  • go 1.19.2

Problem statement

Method Context.parent() in daemon_unix.go contains the following code:

        encoder := json.NewEncoder(d.wpipe)
        if err = encoder.Encode(d); err != nil {
                return
        }
        _, err = fmt.Fprint(d.wpipe, "\n\n")

In some cases the following happens:

  1. parent writes json into d.wpipe with encoder.Encode(d);
  2. child reads json with corresponding call to decoder.Decode(d);
  3. child executes some code, finishes and close the pipe;
  4. parent tries to performs fmt.Fprint(d.wpipe, "\n\n").

As a result Context.Reborn() fails with write |1: broken pipe though the child process is successfully started.

Impact

In some cases Context.Reborn() can fail with write |1: broken pipe though the child process is successfully started.

Steps to reproduce

Compile and run the following program on Almalinux 9 x86_64, Virtuozzo 7:

package main
import (
        "log"
        daemon "github.com/avoronkov/go-daemon"
)
func main() {
        cntxt := &daemon.Context{
                LogFileName: "sample.log",
                LogFilePerm: 0640,
        }
        d, err := cntxt.Reborn()
        if err != nil {
                log.Fatal("Unable to run: ", err)
        }
        if d != nil {
                // Parent process
                log.Printf("Parent: OK")
                return
        }
        defer cntxt.Release()
        log.Print("Child: OK")
}

Actual result

The program fails with the following error:

$ ./prog 
2022/10/17 20:29:30 Unable to run: write |1: broken pipe

Expected result

  • Program finishes successfully.

  • Parent writes message Parent: OK

  • Child writes message Child: OK into "sample.log"

Additional information

The problem was found when I tried to run existing application which uses go-daemon on Almalinux 9 (which is quite new RedHat-based OS).

Possible fix

Apply the following patch:

--- a/daemon_unix.go
+++ b/daemon_unix.go
@@ -113,7 +113,6 @@ func (d *Context) parent() (child *os.Process, err error) {
        if err = encoder.Encode(d); err != nil {
                return
        }
-       _, err = fmt.Fprint(d.wpipe, "\n\n")
        return
 }
 

Looks like writing "\n\n" into a pipe after encoding json is redundant.
At least I was not able to find the problem it fixes.