// +build !windows package bind import ( "fmt" "log" "net" "os" "strconv" "syscall" ) const tooBigErr = "bind: einhorn@%d not found (einhorn only passed %d fds)" const bindErr = "bind: could not bind einhorn@%d: not running under einhorn" const einhornErr = "bind: einhorn environment initialization error" const ackErr = "bind: error ACKing to einhorn: %v" var einhornNumFds int func envInt(val string) (int, error) { return strconv.Atoi(os.Getenv(val)) } // Unfortunately this can't be a normal init function, because their execution // order is undefined, and we need to run before the init() in bind.go. func einhornInit() { mpid, err := envInt("EINHORN_MASTER_PID") if err != nil || mpid != os.Getppid() { return } einhornNumFds, err = envInt("EINHORN_FD_COUNT") if err != nil { einhornNumFds = 0 return } // Prevent einhorn's fds from leaking to our children for i := 0; i < einhornNumFds; i++ { syscall.CloseOnExec(einhornFdMap(i)) } } func usingEinhorn() bool { return einhornNumFds > 0 } func einhornFdMap(n int) int { name := fmt.Sprintf("EINHORN_FD_%d", n) fno, err := envInt(name) if err != nil { log.Fatal(einhornErr) } return fno } func einhornBind(n int) (net.Listener, error) { if !usingEinhorn() { return nil, fmt.Errorf(bindErr, n) } if n >= einhornNumFds || n < 0 { return nil, fmt.Errorf(tooBigErr, n, einhornNumFds) } fno := einhornFdMap(n) f := os.NewFile(uintptr(fno), fmt.Sprintf("einhorn@%d", n)) defer f.Close() return net.FileListener(f) } // Fun story: this is actually YAML, not JSON. const ackMsg = `{"command": "worker:ack", "pid": %d}` + "\n" func einhornAck() { if !usingEinhorn() { return } log.Print("bind: ACKing to einhorn") ctl, err := net.Dial("unix", os.Getenv("EINHORN_SOCK_PATH")) if err != nil { log.Fatalf(ackErr, err) } defer ctl.Close() _, err = fmt.Fprintf(ctl, ackMsg, os.Getpid()) if err != nil { log.Fatalf(ackErr, err) } }