forked from canonical/lxd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main_forkexec.go
122 lines (99 loc) · 2.57 KB
/
main_forkexec.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
110
111
112
113
114
115
116
117
118
119
120
121
122
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"syscall"
"gopkg.in/lxc/go-lxc.v2"
"github.com/lxc/lxd/shared"
)
/*
* This is called by lxd when called as "lxd forkexec <container>"
*/
func cmdForkExec(args []string) (int, error) {
if len(args) < 6 {
return -1, fmt.Errorf("Bad arguments: %q", args)
}
name := args[1]
lxcpath := args[2]
configPath := args[3]
c, err := lxc.NewContainer(name, lxcpath)
if err != nil {
return -1, fmt.Errorf("Error initializing container for start: %q", err)
}
err = c.LoadConfigFile(configPath)
if err != nil {
return -1, fmt.Errorf("Error opening startup config file: %q", err)
}
syscall.Dup3(int(os.Stdin.Fd()), 200, 0)
syscall.Dup3(int(os.Stdout.Fd()), 201, 0)
syscall.Dup3(int(os.Stderr.Fd()), 202, 0)
syscall.Close(int(os.Stdin.Fd()))
syscall.Close(int(os.Stdout.Fd()))
syscall.Close(int(os.Stderr.Fd()))
opts := lxc.DefaultAttachOptions
opts.ClearEnv = true
opts.StdinFd = 200
opts.StdoutFd = 201
opts.StderrFd = 202
logPath := shared.LogPath(name, "forkexec.log")
if shared.PathExists(logPath) {
os.Remove(logPath)
}
logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_SYNC, 0644)
if err == nil {
syscall.Dup3(int(logFile.Fd()), 1, 0)
syscall.Dup3(int(logFile.Fd()), 2, 0)
}
env := []string{}
cmd := []string{}
section := ""
for _, arg := range args[5:] {
// The "cmd" section must come last as it may contain a --
if arg == "--" && section != "cmd" {
section = ""
continue
}
if section == "" {
section = arg
continue
}
if section == "env" {
fields := strings.SplitN(arg, "=", 2)
if len(fields) == 2 && fields[0] == "HOME" {
opts.Cwd = fields[1]
}
env = append(env, arg)
} else if section == "cmd" {
cmd = append(cmd, arg)
} else {
return -1, fmt.Errorf("Invalid exec section: %s", section)
}
}
opts.Env = env
status, err := c.RunCommandNoWait(cmd, opts)
if err != nil {
return -1, fmt.Errorf("Failed running command: %q", err)
}
// Send the PID of the executing process.
w := os.NewFile(uintptr(3), "attachedPid")
defer w.Close()
err = json.NewEncoder(w).Encode(status)
if err != nil {
return -1, fmt.Errorf("Failed sending PID of executing command: %q", err)
}
var ws syscall.WaitStatus
wpid, err := syscall.Wait4(status, &ws, 0, nil)
if err != nil || wpid != status {
return -1, fmt.Errorf("Failed finding process: %q", err)
}
if ws.Exited() {
return ws.ExitStatus(), nil
}
if ws.Signaled() {
// 128 + n == Fatal error signal "n"
return 128 + int(ws.Signal()), nil
}
return -1, fmt.Errorf("Command failed")
}