This repository has been archived by the owner on Mar 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
prog.exec.go
109 lines (89 loc) · 2.88 KB
/
prog.exec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright © 2020 Hedzr Yeh.
package dex
import (
"bytes"
"fmt"
"gopkg.in/hedzr/errors.v2"
"io"
"io/ioutil"
"os/exec"
"syscall"
)
func shell(command string, arguments ...string) error {
_, _, err := runCommand(command, false, arguments...)
return err
}
func shellWithOutput(command string, arguments ...string) (int, string, error) {
return runCommand(command, true, arguments...)
}
func sudo(command string, arguments ...string) (int, string, error) {
sudocmd, err := exec.LookPath("sudo")
if err != nil {
return -1, "'sudo' not found", shell(command, arguments...)
}
rc, output, err1 := runCommand(sudocmd, true, append([]string{command}, arguments...)...)
return rc, output, err1
}
func runCommand(command string, readStdout bool, arguments ...string) (int, string, error) {
cmd := exec.Command(command, arguments...)
var output string
var stdout io.ReadCloser
var err error
if readStdout {
// Connect pipe to read Stdout
stdout, err = cmd.StdoutPipe()
if err != nil {
// Failed to connect pipe
return 0, "", fmt.Errorf("%q failed to connect stdout pipe: %v", command, err)
}
}
// Connect pipe to read Stderr
stderr, err := cmd.StderrPipe()
if err != nil {
// Failed to connect pipe
return 0, "", fmt.Errorf("%q failed to connect stderr pipe: %v", command, err)
}
// Do not use cmd.Run()
if err := cmd.Start(); err != nil {
// Problem while copying stdin, stdout, or stderr
return 0, "", fmt.Errorf("%q failed: %v", command, err)
}
// Zero exit status
// Darwin: launchctl can fail with a zero exit status,
// so check for emtpy stderr
if command == "launchctl" {
slurp, _ := ioutil.ReadAll(stderr)
if len(slurp) > 0 && !bytes.HasSuffix(slurp, []byte("Operation now in progress\n")) {
return 0, "", fmt.Errorf("%q failed with stderr: %s", command, slurp)
}
}
if readStdout {
out, err := ioutil.ReadAll(stdout)
if err != nil {
return 0, "", fmt.Errorf("%q failed while attempting to read stdout: %v", command, err)
} else if len(out) > 0 {
output = string(out)
}
}
if err := cmd.Wait(); err != nil {
exitStatus, ok := isExitError(err)
slurp, _ := ioutil.ReadAll(stderr)
if ok {
// Command didn't exit with a zero exit status.
// return exitStatus, output, fmt.Errorf("%q failed: %w |\n stderr: %s", command, err, slurp)
return exitStatus, output, errors.New("%q failed: %s |\n stderr: %s", command, err.Error(), slurp).Attach(err)
}
// An error occurred and there is no exit status.
// return 0, output, fmt.Errorf("%q failed: %w |\n stderr: %s", command, err, slurp)
return 0, output, errors.New("%q failed: %s |\n stderr: %s", command, err.Error(), slurp).Attach(err)
}
return 0, output, nil
}
func isExitError(err error) (int, bool) {
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
return status.ExitStatus(), true
}
}
return 0, false
}