summaryrefslogtreecommitdiff
path: root/utils/utils.go
diff options
context:
space:
mode:
Diffstat (limited to 'utils/utils.go')
-rw-r--r--utils/utils.go140
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
+}