Skip to content

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

Open
@avoronkov

Description

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.

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions