func main() { eng := engine.New() c, err := net.Dial("unix", "beam.sock") if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return } defer c.Close() f, err := c.(*net.UnixConn).File() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return } child, err := beam.FileConn(f) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) return } defer child.Close() sender := engine.NewSender(child) sender.Install(eng) cmd := eng.Job(os.Args[1], os.Args[2:]...) cmd.Stdout.Add(os.Stdout) cmd.Stderr.Add(os.Stderr) if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) os.Exit(1) } }
// Spawn starts a new Engine in a child process and returns // a proxy Engine through which it can be controlled. // // The commands available on the child engine are determined // by an earlier call to Init. It is important that Init be // called at the very beginning of the current program - this // allows it to be called as a re-execution hook in the child // process. // // Long story short, if you want to expose `myservice` in a child // process, do this: // // func main() { // spawn.Init(myservice) // [..] // child, err := spawn.Spawn() // [..] // child.Job("dosomething").Run() // } func Spawn() (*engine.Engine, error) { if !initCalled { return nil, fmt.Errorf("spawn.Init must be called at the top of the main() function") } cmd := exec.Command(utils.SelfPath()) cmd.Env = append(cmd.Env, "ENGINESPAWN=1") local, remote, err := beam.SocketPair() if err != nil { return nil, err } child, err := beam.FileConn(local) if err != nil { local.Close() remote.Close() return nil, err } local.Close() cmd.ExtraFiles = append(cmd.ExtraFiles, remote) // FIXME: the beam/engine glue has no way to inform the caller // of the child's termination. The next call will simply return // an error. if err := cmd.Start(); err != nil { child.Close() return nil, err } eng := engine.New() if err := engine.NewSender(child).Install(eng); err != nil { child.Close() return nil, err } return eng, nil }