-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexec.go
135 lines (106 loc) · 3.39 KB
/
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main
import (
"strconv"
"path"
"fmt"
"os"
"io/ioutil"
"encoding/json"
"strings"
"os/exec"
"qsrdocker/container"
_ "qsrdocker/nsenter"
// 导入并不使用
// 当设置环境变量后,触发其 __attribute__ 函数
log "github.com/sirupsen/logrus"
)
// ENVEXECPID 环境变量 Pid
const ENVEXECPID = "QSRDOCKER_PID"
// ENVEXECCMD 环境变量 cmd
const ENVEXECCMD = "QSRDOCKER_CMD"
// ExecContainer 登陆到已经创建好的 qsrdocker
func ExecContainer(tty bool, containerName string, cmdList []string) {
// 获得目标容器状态
statusInfo, err := GetContainerStatusByName(containerName)
if err != nil {
log.Errorf("Exec container GetContainerStatusByName %s error %v", containerName, err)
return
}
// 判断进程状态
if !statusInfo.Running {
log.Errorf("Exec container fail, Status is Dead and pid %v is not exist", statusInfo.Pid)
return
}
// 获取进程PID
pid := strconv.Itoa(statusInfo.Pid)
// 字符切片拼接
cmdStr := strings.Join(cmdList, " ")
log.Debugf("Container pid %s", pid)
log.Debugf("Command %s", cmdStr)
// 重新 fork/exec 执行自己
// 触发__attribute__函数
cmd := exec.Command("/proc/self/exe", "exec")
// 标准输出输入错误
if tty {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
// 获取进程环境变量
// 在 run 创建 container 时,可能设置了环境变量
containerEnvSlice := getEnvSliceByPid(pid)
// 设置环境变量
envPid := strings.Join([]string{ENVEXECPID, pid}, "=")
envCmd := strings.Join([]string{ENVEXECCMD, cmdStr}, "=")
// 将上面获取到的环境变量通过加入到 cmd 的环境变量中
// 便于后面再一次调用时触发 nsenter
// containerEnvSlice == 容器创建时设置的
cmd.Env = append([]string{envPid, envCmd}, containerEnvSlice...)
log.Debugf("Set exec container %s env %v", containerName, cmd.Env)
if err := cmd.Run(); err != nil {
log.Errorf("Exec container %s error %v", containerName, err)
}
}
// GetContainerStatusByName 通过容器名获取容器状态信息
func GetContainerStatusByName(containerName string) (*container.StatusInfo, error) {
// 获取 container ID
containerID, err := container.GetContainerIDByName(containerName)
if strings.Replace(containerID, " ", "", -1) == "" || err != nil {
return nil, fmt.Errorf("Get containerID fail : %v", err)
}
containerConfigFile := path.Join(container.ContainerDir, containerID, container.ConfigName)
// 获取容器配置信息
configBytes, err := ioutil.ReadFile(containerConfigFile)
if err != nil {
return nil, err
}
// 反序列化
var containerInfo container.ContainerInfo
if err := json.Unmarshal(configBytes, &containerInfo); err != nil {
return nil, err
}
// 检测当前状态
containerInfo.Status.StatusCheck()
// 持久化当前状态
container.RecordContainerInfo(&containerInfo, containerID)
return containerInfo.Status, nil
}
// getEnvSliceByPid 获取进程环境变量
func getEnvSliceByPid(pid string) []string {
envPath := path.Join("/proc", pid, "environ")
exist, _ := container.PathExists(envPath)
envSlice := []string{}
// 读取环境变量
// 确定设置成功
if exist{
contentBytes, err := ioutil.ReadFile(envPath)
if err != nil {
log.Errorf("Read file %s error %v", envPath, err)
return nil
}
// env split by \u0000
// 默认格式
envSlice = strings.Split(string(contentBytes), "\u0000")
}
return envSlice
}