func (info DeviceInfo) Load() (Device, error) { // Find the appropriate driver. driver, ok := drivers[info.Driver] if !ok { return nil, DriverUnknown(info.Driver) } // Load the driver. device, err := driver(&info) if err != nil { return nil, err } if info.Data != nil { // Scratch data. buffer := bytes.NewBuffer(nil) // Encode the original object. encoder := utils.NewEncoder(buffer) err = encoder.Encode(info.Data) if err != nil { return nil, err } // Decode a new object. // This will override all the default // settings in the initialized object. decoder := utils.NewDecoder(buffer) log.Printf("Loading %s...", device.Name()) err = decoder.Decode(device) if err != nil { return nil, err } } // Save the original device. // This will allow us to implement a // simple Save() method that serializes // the state of this device as it exists. info.Data = device // We're done. return device, nil }
func (control *Control) handle( conn_fd int, server *rpc.Server) { control_file := os.NewFile(uintptr(conn_fd), "control") defer control_file.Close() // Read single header. // Our header is exactly 9 characters, and we // expect the last character to be a newline. // This is a simple plaintext protocol. header_buf := make([]byte, 9, 9) n, err := control_file.Read(header_buf) if n != 9 || header_buf[8] != '\n' { if err != nil { control_file.Write([]byte(err.Error())) } else { control_file.Write([]byte("invalid header")) } return } header := string(header_buf) // We read a special header before diving into RPC // mode. This is because for the novmrun case, we turn // the socket into a stream of input/output events. // These are simply JSON serialized versions of the // events for the guest RPC interface. if header == "NOVM RUN\n" { decoder := utils.NewDecoder(control_file) encoder := utils.NewEncoder(control_file) var start noguest.StartCommand err := decoder.Decode(&start) if err != nil { // Poorly encoded command. encoder.Encode(err.Error()) return } // Grab our client. client, err := control.Ready() if err != nil { encoder.Encode(err.Error()) return } // Call start. result := noguest.StartResult{} err = client.Call("Server.Start", &start, &result) if err != nil { encoder.Encode(err.Error()) return } // Save our pid. pid := result.Pid inputs := make(chan error) outputs := make(chan error) exitcode := make(chan int) // This indicates we're okay. encoder.Encode(nil) // Wait for the process to exit. go func() { wait := noguest.WaitCommand{ Pid: pid, } var wait_result noguest.WaitResult err := client.Call("Server.Wait", &wait, &wait_result) if err != nil { exitcode <- 1 } else { exitcode <- wait_result.Exitcode } }() // Read from stdout & stderr. go func() { read := noguest.ReadCommand{ Pid: pid, N: 4096, } var read_result noguest.ReadResult for { err := client.Call("Server.Read", &read, &read_result) if err != nil { inputs <- err return } err = encoder.Encode(read_result.Data) if err != nil { inputs <- err return } } }() // Write to stdin. go func() { write := noguest.WriteCommand{ Pid: pid, } var write_result noguest.WriteResult for { err := decoder.Decode(&write.Data) if err != nil { outputs <- err return } err = client.Call("Server.Write", &write, &write_result) if err != nil { outputs <- err return } } }() // Wait till exit. status := <-exitcode encoder.Encode(status) // Wait till EOF. <-inputs // Send a notice and close the socket. encoder.Encode(nil) } else if header == "NOVM RPC\n" { // Run as JSON RPC connection. codec := jsonrpc.NewServerCodec(control_file) server.ServeCodec(codec) } }
func restart( model *machine.Model, vm *platform.Vm, is_tracing bool, stop bool) error { // Get our binary. bin, err := os.Readlink("/proc/self/exe") if err != nil { return err } _, err = os.Stat(bin) if err != nil { // If this is no longer the same binary, then the // kernel proc node will have "fixed" the symlink // to point to "/path (deleted)". This is mildly // annoying, as one would assume there would be a // better way of transmitting that information. if os.IsNotExist(err) && strings.HasSuffix(bin, " (deleted)") { bin = strings.TrimSuffix(bin, " (deleted)") _, err = os.Stat(bin) } if err != nil { return err } } // Create our state. state, err := control.SaveState(vm, model) if err != nil { return err } // Encode our state in a temporary file. // This is passed in to the new VMM as the statefd. // We unlink it immediately because we don't need to // access it by name, and can ensure it is cleaned up. // Note that the TempFile is normally opened CLOEXEC. // This means that need we need to perform a DUP in // order to get an FD that can pass to the child. state_file, err := ioutil.TempFile(os.TempDir(), "state") if err != nil { return err } defer state_file.Close() err = os.Remove(state_file.Name()) if err != nil { return err } encoder := utils.NewEncoder(state_file) err = encoder.Encode(&state) if err != nil { return err } _, err = state_file.Seek(0, 0) if err != nil { return err } state_fd, err := syscall.Dup(int(state_file.Fd())) if err != nil { return err } defer syscall.Close(state_fd) // Prepare to reexec. cmd := []string{ os.Args[0], fmt.Sprintf("-controlfd=%d", *control_fd), fmt.Sprintf("-statefd=%d", state_fd), fmt.Sprintf("-trace=%t", is_tracing), fmt.Sprintf("-paused=%t", *paused), fmt.Sprintf("-stop=%t", stop), } return syscall.Exec(bin, cmd, os.Environ()) }