diff options
Diffstat (limited to 'utils/utils.go')
-rw-r--r-- | utils/utils.go | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 000000000..9b7cebfea --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,140 @@ +package utils + +import ( + "bytes" + "fmt" + "io" + "os/exec" + "strings" + + systemdDbus "github.com/coreos/go-systemd/dbus" + "github.com/godbus/dbus" +) + +// ExecCmd executes a command with args and returns its output as a string along +// with an error, if any +func ExecCmd(name string, args ...string) (string, error) { + cmd := exec.Command(name, args...) + var stdout bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + return "", fmt.Errorf("`%v %v` failed: %v %v (%v)", name, strings.Join(args, " "), stderr.String(), stdout.String(), err) + } + + return stdout.String(), nil +} + +// ExecCmdWithStdStreams execute a command with the specified standard streams. +func ExecCmdWithStdStreams(stdin io.Reader, stdout, stderr io.Writer, name string, args ...string) error { + cmd := exec.Command(name, args...) + cmd.Stdin = stdin + cmd.Stdout = stdout + cmd.Stderr = stderr + + err := cmd.Run() + if err != nil { + return fmt.Errorf("`%v %v` failed: %v", name, strings.Join(args, " "), err) + } + + return nil +} + +// StatusToExitCode converts wait status code to an exit code +func StatusToExitCode(status int) int { + return ((status) & 0xff00) >> 8 +} + +// RunUnderSystemdScope adds the specified pid to a systemd scope +func RunUnderSystemdScope(pid int, slice string, unitName string) error { + var properties []systemdDbus.Property + conn, err := systemdDbus.New() + if err != nil { + return err + } + properties = append(properties, systemdDbus.PropSlice(slice)) + properties = append(properties, newProp("PIDs", []uint32{uint32(pid)})) + properties = append(properties, newProp("Delegate", true)) + properties = append(properties, newProp("DefaultDependencies", false)) + ch := make(chan string) + _, err = conn.StartTransientUnit(unitName, "replace", properties, ch) + if err != nil { + return err + } + defer conn.Close() + + // Block until job is started + <-ch + + return nil +} + +func newProp(name string, units interface{}) systemdDbus.Property { + return systemdDbus.Property{ + Name: name, + Value: dbus.MakeVariant(units), + } +} + +// DetachError is special error which returned in case of container detach. +type DetachError struct{} + +func (DetachError) Error() string { + return "detached from container" +} + +// CopyDetachable is similar to io.Copy but support a detach key sequence to break out. +func CopyDetachable(dst io.Writer, src io.Reader, keys []byte) (written int64, err error) { + if len(keys) == 0 { + // Default keys : ctrl-p ctrl-q + keys = []byte{16, 17} + } + + buf := make([]byte, 32*1024) + for { + nr, er := src.Read(buf) + if nr > 0 { + preservBuf := []byte{} + for i, key := range keys { + preservBuf = append(preservBuf, buf[0:nr]...) + if nr != 1 || buf[0] != key { + break + } + if i == len(keys)-1 { + // src.Close() + return 0, DetachError{} + } + nr, er = src.Read(buf) + } + var nw int + var ew error + if len(preservBuf) > 0 { + nw, ew = dst.Write(preservBuf) + nr = len(preservBuf) + } else { + nw, ew = dst.Write(buf[0:nr]) + } + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er != nil { + if er != io.EOF { + err = er + } + break + } + } + return written, err +} |