Context.Reborn fails with "write |1: broken pipe" due to a race condition in writing to Context.wpipe #86
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:
- parent writes json into d.wpipe with
encoder.Encode(d)
; - child reads json with corresponding call to
decoder.Decode(d)
; - child executes some code, finishes and close the pipe;
- 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.