libgocryptfs/daemonize.go
Jakob Unterwurzacher 496968e06c main: redirectStdFds: keep logger from holding stdout open
We passed our stdout and stderr to the new logger instance,
which makes sense to see any error message, but also means that
the fd is kept open even when we close it.

Fixes the new TestMountBackground test and
https://github.com/rfjakob/gocryptfs/issues/130 .
2017-07-24 00:05:39 +02:00

100 lines
2.7 KiB
Go

package main
import (
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"github.com/rfjakob/gocryptfs/internal/exitcodes"
"github.com/rfjakob/gocryptfs/internal/syscallcompat"
"github.com/rfjakob/gocryptfs/internal/tlog"
)
// The child sends us USR1 if the mount was successful. Exit with error code
// 0 if we get it.
func exitOnUsr1() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGUSR1)
go func() {
<-c
os.Exit(0)
}()
}
// forkChild - execute ourselves once again, this time with the "-fg" flag, and
// wait for SIGUSR1 or child exit.
// This is a workaround for the missing true fork function in Go.
func forkChild() int {
name := os.Args[0]
newArgs := []string{"-fg", fmt.Sprintf("-notifypid=%d", os.Getpid())}
newArgs = append(newArgs, os.Args[1:]...)
c := exec.Command(name, newArgs...)
c.Stdout = os.Stdout
c.Stderr = os.Stderr
c.Stdin = os.Stdin
exitOnUsr1()
err := c.Start()
if err != nil {
tlog.Fatal.Printf("forkChild: starting %s failed: %v\n", name, err)
return exitcodes.ForkChild
}
err = c.Wait()
if err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
if waitstat, ok := exiterr.Sys().(syscall.WaitStatus); ok {
os.Exit(waitstat.ExitStatus())
}
}
tlog.Fatal.Printf("forkChild: wait returned an unknown error: %v\n", err)
return exitcodes.ForkChild
}
// The child exited with 0 - let's do the same.
return 0
}
// redirectStdFds redirects stderr and stdout to syslog; stdin to /dev/null
func redirectStdFds() {
// Create a pipe pair "pw" -> "pr" and start logger reading from "pr".
// We do it ourselves instead of using StdinPipe() because we need access
// to the fd numbers.
pr, pw, err := os.Pipe()
if err != nil {
tlog.Warn.Printf("redirectStdFds: could not create pipe: %v\n", err)
return
}
tag := fmt.Sprintf("gocryptfs-%d-logger", os.Getpid())
cmd := exec.Command("logger", "-t", tag)
cmd.Stdin = pr
err = cmd.Start()
if err != nil {
tlog.Warn.Printf("redirectStdFds: could not start logger: %v\n", err)
return
}
// The logger now reads on "pr". We can close it.
pr.Close()
// Redirect stout and stderr to "pw".
err = syscallcompat.Dup3(int(pw.Fd()), 1, 0)
if err != nil {
tlog.Warn.Printf("redirectStdFds: stdout dup error: %v\n", err)
}
syscallcompat.Dup3(int(pw.Fd()), 2, 0)
if err != nil {
tlog.Warn.Printf("redirectStdFds: stderr dup error: %v\n", err)
}
// Our stout and stderr point to "pw". We can close the extra copy.
pw.Close()
// Redirect stdin to /dev/null
nullFd, err := os.Open("/dev/null")
if err != nil {
tlog.Warn.Printf("redirectStdFds: could not open /dev/null: %v\n", err)
return
}
err = syscallcompat.Dup3(int(nullFd.Fd()), 0, 0)
if err != nil {
tlog.Warn.Printf("redirectStdFds: stdin dup error: %v\n", err)
}
nullFd.Close()
}