package libcontainer import ( "encoding/json" "fmt" "io" "github.com/opencontainers/runc/libcontainer/utils" ) type syncType string // Constants that are used for synchronisation between the parent and child // during container setup. They come in pairs (with procError being a generic // response which is followed by a &genericError). // // [ child ] <-> [ parent ] // // procHooks --> [run hooks] // <-- procResume // // procConsole --> // <-- procConsoleReq // [send(fd)] --> [recv(fd)] // <-- procConsoleAck // // procReady --> [final setup] // <-- procRun const ( procError syncType = "procError" procReady syncType = "procReady" procRun syncType = "procRun" procHooks syncType = "procHooks" procResume syncType = "procResume" ) type syncT struct { Type syncType `json:"type"` } // writeSync is used to write to a synchronisation pipe. An error is returned // if there was a problem writing the payload. func writeSync(pipe io.Writer, sync syncType) error { if err := utils.WriteJSON(pipe, syncT{sync}); err != nil { return err } return nil } // readSync is used to read from a synchronisation pipe. An error is returned // if we got a genericError, the pipe was closed, or we got an unexpected flag. func readSync(pipe io.Reader, expected syncType) error { var procSync syncT if err := json.NewDecoder(pipe).Decode(&procSync); err != nil { if err == io.EOF { return fmt.Errorf("parent closed synchronisation channel") } if procSync.Type == procError { var ierr genericError if err := json.NewDecoder(pipe).Decode(&ierr); err != nil { return fmt.Errorf("failed reading error from parent: %v", err) } return &ierr } if procSync.Type != expected { return fmt.Errorf("invalid synchronisation flag from parent") } } return nil } // parseSync runs the given callback function on each syncT received from the // child. It will return once io.EOF is returned from the given pipe. func parseSync(pipe io.Reader, fn func(*syncT) error) error { dec := json.NewDecoder(pipe) for { var sync syncT if err := dec.Decode(&sync); err != nil { if err == io.EOF { break } return err } // We handle this case outside fn for cleanliness reasons. var ierr *genericError if sync.Type == procError { if err := dec.Decode(&ierr); err != nil && err != io.EOF { return newSystemErrorWithCause(err, "decoding proc error from init") } if ierr != nil { return ierr } // Programmer error. panic("No error following JSON procError payload.") } if err := fn(&sync); err != nil { return err } } return nil }