1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package chaosdaemon
17
18 import (
19 "context"
20 "fmt"
21 "os"
22 "strings"
23
24 "github.com/golang/protobuf/ptypes/empty"
25 "github.com/pkg/errors"
26
27 "github.com/chaos-mesh/chaos-mesh/pkg/bpm"
28 pb "github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/pb"
29 "github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/util"
30 )
31
32 const (
33 bmInstallCommand = "bminstall.sh -b -Dorg.jboss.byteman.transform.all -Dorg.jboss.byteman.verbose -Dorg.jboss.byteman.compileToBytecode -p %d %d"
34 bmSubmitCommand = "bmsubmit.sh -p %d -%s %s"
35 )
36
37 func (s *DaemonServer) InstallJVMRules(ctx context.Context,
38 req *pb.InstallJVMRulesRequest) (*empty.Empty, error) {
39 log := s.getLoggerFromContext(ctx)
40 log.Info("InstallJVMRules", "request", req)
41 pid, err := s.crClient.GetPidFromContainerID(ctx, req.ContainerId)
42 if err != nil {
43 log.Error(err, "GetPidFromContainerID")
44 return nil, err
45 }
46
47 containerPids := []uint32{pid}
48 childPids, err := util.GetChildProcesses(pid, log)
49 if err != nil {
50 log.Error(err, "GetChildProcesses")
51 }
52 containerPids = append(containerPids, childPids...)
53 for _, containerPid := range containerPids {
54 name, err := util.ReadCommName(int(containerPid))
55 if err != nil {
56 log.Error(err, "ReadCommName")
57 continue
58 }
59 if name == "java\n" {
60 pid = containerPid
61 break
62 }
63 }
64
65 bytemanHome := os.Getenv("BYTEMAN_HOME")
66 if len(bytemanHome) == 0 {
67 return nil, errors.New("environment variable BYTEMAN_HOME not set")
68 }
69
70
71 if req.EnterNS {
72 processBuilder := bpm.DefaultProcessBuilder("sh", "-c", fmt.Sprintf("mkdir -p %s/lib/", bytemanHome)).SetContext(ctx).SetNS(pid, bpm.MountNS)
73 output, err := processBuilder.Build(ctx).CombinedOutput()
74 if err != nil {
75 return nil, err
76 }
77 if len(output) > 0 {
78 log.Info("mkdir", "output", string(output))
79 }
80
81 jars := []string{"byteman.jar", "byteman-helper.jar", "chaos-agent.jar"}
82
83 for _, jar := range jars {
84 source := fmt.Sprintf("%s/lib/%s", bytemanHome, jar)
85 dest := fmt.Sprintf("/usr/local/byteman/lib/%s", jar)
86
87 output, err = copyFileAcrossNS(ctx, source, dest, pid)
88 if err != nil {
89 return nil, err
90 }
91
92 log.Info("copy", "jar name", jar, "from source", source, "to destination", dest, "output", string(output))
93 }
94 }
95
96 bmInstallCmd := fmt.Sprintf(bmInstallCommand, req.Port, pid)
97 processBuilder := bpm.DefaultProcessBuilder("sh", "-c", bmInstallCmd).SetContext(ctx)
98 if req.EnterNS {
99 processBuilder = processBuilder.EnableLocalMnt()
100 }
101
102 cmd := processBuilder.Build(ctx)
103 output, err := cmd.CombinedOutput()
104 if err != nil {
105
106 errMsg1 := "Agent JAR loaded but agent failed to initialize"
107
108
109
110
111 errMsg2 := "Provider sun.tools.attach.LinuxAttachProvider not found"
112 errMsg3 := "install java.io.IOException: Non-numeric value found"
113
114
115
116 errMsg4 := "com.sun.tools.attach.AgentLoadException"
117 if !strings.Contains(string(output), errMsg1) && !strings.Contains(string(output), errMsg2) &&
118 !strings.Contains(string(output), errMsg3) && !strings.Contains(string(output), errMsg4) {
119 log.Error(err, string(output))
120 return nil, errors.Wrap(err, string(output))
121 }
122 log.Info("exec comamnd", "cmd", cmd.String(), "output", string(output), "error", err.Error())
123 }
124
125
126 bmSubmitCmd := fmt.Sprintf(bmSubmitCommand, req.Port, "b", fmt.Sprintf("%s/lib/byteman-helper.jar", os.Getenv("BYTEMAN_HOME")))
127 processBuilder = bpm.DefaultProcessBuilder("sh", "-c", bmSubmitCmd).SetContext(ctx)
128 if req.EnterNS {
129 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
130 }
131 output, err = processBuilder.Build(ctx).CombinedOutput()
132 if err != nil {
133 log.Error(err, string(output))
134 return nil, err
135 }
136 if len(output) > 0 {
137 log.Info("submit helper jar", "output", string(output))
138 }
139
140
141 filename, err := writeDataIntoFile(req.Rule, "rule.btm")
142 if err != nil {
143 return nil, err
144 }
145
146 bmSubmitCmd = fmt.Sprintf(bmSubmitCommand, req.Port, "l", filename)
147 processBuilder = bpm.DefaultProcessBuilder("sh", "-c", bmSubmitCmd).SetContext(ctx)
148 if req.EnterNS {
149 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
150 }
151 output, err = processBuilder.Build(ctx).CombinedOutput()
152 if err != nil {
153 log.Error(err, string(output))
154 return nil, errors.Wrap(err, string(output))
155 }
156 if len(output) > 0 {
157 log.Info("submit rules", "output", string(output))
158 }
159
160 return &empty.Empty{}, nil
161 }
162
163 func (s *DaemonServer) UninstallJVMRules(ctx context.Context,
164 req *pb.UninstallJVMRulesRequest) (*empty.Empty, error) {
165 log := s.getLoggerFromContext(ctx)
166 log.Info("InstallJVMRules", "request", req)
167 pid, err := s.crClient.GetPidFromContainerID(ctx, req.ContainerId)
168 if err != nil {
169 log.Error(err, "GetPidFromContainerID")
170 return nil, err
171 }
172
173 filename, err := writeDataIntoFile(req.Rule, "rule.btm")
174 if err != nil {
175 return nil, err
176 }
177 log.Info("create btm file", "file", filename)
178
179 bmSubmitCmd := fmt.Sprintf(bmSubmitCommand, req.Port, "u", filename)
180 processBuilder := bpm.DefaultProcessBuilder("sh", "-c", bmSubmitCmd).SetContext(ctx)
181 if req.EnterNS {
182 processBuilder = processBuilder.SetNS(pid, bpm.NetNS)
183 }
184 output, err := processBuilder.Build(ctx).CombinedOutput()
185 if err != nil {
186 log.Error(err, string(output))
187 if strings.Contains(string(output), "No rule scripts to remove") {
188 return &empty.Empty{}, nil
189 }
190 return nil, errors.Wrap(err, string(output))
191 }
192
193 if len(output) > 0 {
194 log.Info(string(output))
195 }
196
197 return &empty.Empty{}, nil
198 }
199
200 func writeDataIntoFile(data string, filename string) (string, error) {
201 tmpfile, err := os.CreateTemp("", filename)
202 if err != nil {
203 return "", err
204 }
205
206 if _, err := tmpfile.WriteString(data); err != nil {
207 return "", err
208 }
209
210 if err := tmpfile.Close(); err != nil {
211 return "", err
212 }
213
214 return tmpfile.Name(), err
215 }
216
217 func copyFileAcrossNS(ctx context.Context, source string, dest string, pid uint32) ([]byte, error) {
218 sourceFile, err := os.Open(source)
219 if err != nil {
220 return nil, err
221 }
222 defer sourceFile.Close()
223
224 processBuilder := bpm.DefaultProcessBuilder("sh", "-c", fmt.Sprintf("cat > %s", dest)).SetContext(ctx)
225 processBuilder = processBuilder.SetNS(pid, bpm.MountNS).SetStdin(sourceFile)
226 output, err := processBuilder.Build(ctx).CombinedOutput()
227 if err != nil {
228 return nil, err
229 }
230
231 return output, nil
232 }
233