sonar security issue fix- Make sure that environment variables are used safely here
[cli.git] / framework / src / main / java / org / onap / cli / fw / utils / ProcessRunner.java
1 /*
2  * Copyright 2018 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.cli.fw.utils;
18
19 import java.io.BufferedReader;
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.OutputStream;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.concurrent.TimeUnit;
30
31 import org.apache.commons.io.IOUtils;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 public class ProcessRunner {
36     private static Logger log = LoggerFactory.getLogger(ProcessRunner.class);
37     public static final String WIN_SHELL = "cmd.exe /c ";
38     public static final String UNIX_SHELL = "";
39     private String []cmd = null;
40     private String shell = System.getProperty("os.name").toLowerCase().startsWith("windows") ? WIN_SHELL : UNIX_SHELL;
41     private String cwd = System.getProperty("user.home");
42     private String []env = null;
43     private int exitCode = -1;
44     private String output = "";
45     private String error = "";
46     private long timeout = 0;
47     private OutputStream stdout;
48     private OutputStream stderr;
49     public ProcessRunner(String []cmd, String []env, String cwd) {
50         this.cmd = cmd;
51
52         if (cwd != null && !cwd.isEmpty()) {
53             this.cwd = cwd;
54         }
55
56         this.env = env;
57     }
58
59     public void setTimeout(long timeout) {
60         this.timeout = timeout;
61     }
62
63     public long getTimeout() {
64         return this.timeout;
65     }
66
67     public void overrideToUnix() {
68         this.shell = UNIX_SHELL;
69     }
70
71     public ProcessRunner(String []cmd, String cwd) {
72         this(cmd, null, cwd);
73     }
74
75     public ProcessRunner(String []cmd) {
76         this(cmd, null, null);
77     }
78
79     public ProcessRunner(String cmd, String []env, String cwd) {
80         this(new String []{cmd}, env, cwd);
81     }
82
83     public ProcessRunner(String cmd, String cwd) {
84         this(new String []{cmd}, null, cwd);
85     }
86
87     public ProcessRunner(String cmd) {
88         this(new String []{cmd}, null, null);
89     }
90
91     @SuppressWarnings("unchecked")
92     public void run() throws InterruptedException, IOException {
93         Process p = null;
94
95         File workingDirectory = null;
96         if (cwd != null) {
97            workingDirectory = new File(cwd);
98         }
99         if (this.cmd.length == 1) {
100             p = Runtime.getRuntime().exec(this.shell + this.cmd[0], this.env, workingDirectory); //NOSONAR
101         } else {
102             List list = new ArrayList(Arrays.asList(this.shell.split(" ")));
103             list.addAll(Arrays.asList(this.cmd));
104             String []cmds = Arrays.copyOf(list.toArray(), list.size(), String[].class);
105             p = Runtime.getRuntime().exec(cmds, this.env, workingDirectory); //NOSONAR
106         }
107
108         boolean readOutput = false;
109         if (this.getStdout() == null) {
110             this.setStdout(new ByteArrayOutputStream());
111             readOutput = true;
112         }
113
114         boolean readError = false;
115         if (this.getStderr() == null) {
116             this.setStderr(new ByteArrayOutputStream());
117             readError = true;
118         }
119
120         final OutputStream stdout = this.getStdout();
121         final OutputStream stderr = this.getStderr();
122
123         final InputStream stdoutP = p.getInputStream();
124         final InputStream stderrP = p.getErrorStream();
125
126         Thread outThread = new Thread(new Runnable() {
127             public void run() {
128                 try {
129                     IOUtils.copy(stdoutP, stdout);
130                 } catch (IOException e) {
131                 }
132             }
133         });
134
135         Thread errThread = new Thread(new Runnable() {
136             public void run() {
137                 try {
138                     IOUtils.copy(stderrP, stderr);
139                 } catch (IOException e) {
140                 }
141             }
142         });
143
144         outThread.start();
145         errThread.start();
146
147         boolean completed = p.waitFor(this.getTimeout(), TimeUnit.MILLISECONDS);
148         outThread.join();
149         errThread.join();
150
151         if (completed) {
152             this.exitCode = p.exitValue();
153         }
154
155         if (readOutput)
156             this.output = new String(((ByteArrayOutputStream)this.getStdout()).toByteArray(), "UTF-8");
157
158         if (readError)
159             this.error = new String(((ByteArrayOutputStream)this.getStderr()).toByteArray(), "UTF-8");;
160
161         p.destroy();
162
163         log.debug("CMD: " + Arrays.asList(this.cmd).toString() +
164                 "\nWORKING_DIR: " + this.cwd +
165                 "\nENV: " + ((this.env == null) ? this.env : Arrays.asList(this.env).toString()) +
166                 "\nOUTPUT: " + this.output +
167                 "\nERROR: " + this.error +
168                 "\nEXIT_CODE: " + this.exitCode);
169
170         if (!completed) {
171             throw new RuntimeException("TIMEOUT:: cmd:" + Arrays.asList(this.cmd).toString());
172         }
173     }
174
175     public String streamToString(InputStream stream) throws IOException {
176         StringBuilder sb = new StringBuilder();
177         try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
178             String line = null;
179             while ((line = br.readLine()) != null) {
180                 sb.append(line + System.getProperty("line.separator"));
181             }
182         }
183         return sb.toString();
184     }
185
186     public int getExitCode() {
187         return this.exitCode;
188     }
189
190     public String getOutput() {
191         return this.output;
192     }
193
194     public String getError() {
195         return this.error;
196     }
197
198     public OutputStream getStdout() {
199         return stdout;
200     }
201
202     public void setStdout(OutputStream stdout) {
203         this.stdout = stdout;
204     }
205
206     public OutputStream getStderr() {
207         return stderr;
208     }
209
210     public void setStderr(OutputStream stderr) {
211         this.stderr = stderr;
212     }
213
214     public String toString() {
215         StringBuilder sb = new StringBuilder();
216
217         sb.append("COMMAND: " + this.shell + " " + Arrays.asList(this.cmd));
218         sb.append("\nCWD: " + new File(this.cwd).getAbsolutePath());
219         sb.append("\nTIMEOUT: " + this.timeout);
220         sb.append("\nEXIT-CODE: " + this.getExitCode());
221         sb.append("\nENVIRONMENTS: " + Arrays.asList(this.env));
222
223         return sb.toString();
224     }
225 }