Beispiel #1
0
// 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
}
Beispiel #2
0
func CmdExec(args []string, stdout, stderr io.Writer, in beam.Receiver, out beam.Sender) {
	cmd := exec.Command(args[1], args[2:]...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr
	//cmd.Stdin = os.Stdin
	local, remote, err := beam.SocketPair()
	if err != nil {
		fmt.Fprintf(stderr, "%v\n", err)
		return
	}
	child, err := beam.FileConn(local)
	if err != nil {
		local.Close()
		remote.Close()
		fmt.Fprintf(stderr, "%v\n", err)
		return
	}
	local.Close()
	cmd.ExtraFiles = append(cmd.ExtraFiles, remote)

	var tasks sync.WaitGroup
	tasks.Add(1)
	go func() {
		defer Debugf("done copying to child\n")
		defer tasks.Done()
		defer child.CloseWrite()
		beam.Copy(child, in)
	}()

	tasks.Add(1)
	go func() {
		defer Debugf("done copying from child %d\n")
		defer tasks.Done()
		r := beam.NewRouter(out)
		r.NewRoute().All().Handler(func(p []byte, a *os.File) error {
			return out.Send(data.Message(p).Set("pid", fmt.Sprintf("%d", cmd.Process.Pid)).Bytes(), a)
		})
		beam.Copy(r, child)
	}()
	execErr := cmd.Run()
	// We can close both ends of the socket without worrying about data stuck in the buffer,
	// because unix socket writes are fully synchronous.
	child.Close()
	tasks.Wait()
	var status string
	if execErr != nil {
		status = execErr.Error()
	} else {
		status = "ok"
	}
	out.Send(data.Empty().Set("status", status).Set("cmd", args...).Bytes(), nil)
}
Beispiel #3
0
func ReceiveFromConn(connections chan net.Conn, dst beam.Sender) error {
	for conn := range connections {
		err := func() error {
			Logf("parsing message from network...\n")
			defer Logf("done parsing message from network\n")
			buf := make([]byte, 4098)
			n, err := conn.Read(buf)
			if n == 0 {
				conn.Close()
				if err == io.EOF {
					return nil
				} else {
					return err
				}
			}
			Logf("decoding message from '%s'\n", buf[:n])
			header, skip, err := data.DecodeString(string(buf[:n]))
			if err != nil {
				conn.Close()
				return err
			}
			pub, priv, err := beam.SocketPair()
			if err != nil {
				return err
			}
			Logf("decoded message: %s\n", data.Message(header).Pretty())
			go func(skipped []byte, conn net.Conn, f *os.File) {
				// this closes both conn and f
				if len(skipped) > 0 {
					if _, err := f.Write(skipped); err != nil {
						Logf("ERROR: %v\n", err)
						f.Close()
						conn.Close()
						return
					}
				}
				bicopy(conn, f)
			}(buf[skip:n], conn, pub)
			if err := dst.Send([]byte(header), priv); err != nil {
				return err
			}
			return nil
		}()
		if err != nil {
			Logf("Error reading from connection: %v\n", err)
		}
	}
	return nil
}