|
1 /* |
|
2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 #include "PosixPlatform.h" |
|
27 |
|
28 #ifdef POSIX |
|
29 |
|
30 #include "PlatformString.h" |
|
31 #include "FilePath.h" |
|
32 #include "Helpers.h" |
|
33 |
|
34 #include <assert.h> |
|
35 #include <stdbool.h> |
|
36 #include <sys/types.h> |
|
37 #include <unistd.h> |
|
38 #include <sys/sysctl.h> |
|
39 #include <sys/file.h> |
|
40 #include <sys/stat.h> |
|
41 #ifdef LINUX |
|
42 #include <sys/wait.h> |
|
43 #endif |
|
44 #include <errno.h> |
|
45 #include <limits.h> |
|
46 #include <pwd.h> |
|
47 #include <iostream> |
|
48 #include <algorithm> |
|
49 #include <dlfcn.h> |
|
50 #include <signal.h> |
|
51 |
|
52 |
|
53 PosixPlatform::PosixPlatform(void) { |
|
54 } |
|
55 |
|
56 PosixPlatform::~PosixPlatform(void) { |
|
57 if (!SingleInstanceFile.empty()) { |
|
58 unlink(SingleInstanceFile.c_str()); |
|
59 } |
|
60 } |
|
61 |
|
62 TString PosixPlatform::GetTempDirectory() { |
|
63 struct passwd* pw = getpwuid(getuid()); |
|
64 TString homedir(pw->pw_dir); |
|
65 homedir += getTmpDirString(); |
|
66 if (!FilePath::DirectoryExists(homedir)) { |
|
67 if (!FilePath::CreateDirectory(homedir, false)) { |
|
68 homedir.clear(); |
|
69 } |
|
70 } |
|
71 |
|
72 return homedir; |
|
73 } |
|
74 |
|
75 TString PosixPlatform::fixName(const TString& name) { |
|
76 TString fixedName(name); |
|
77 const TString chars("?:*<>/\\"); |
|
78 for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) { |
|
79 fixedName.erase(std::remove(fixedName.begin(), |
|
80 fixedName.end(), *it), fixedName.end()); |
|
81 } |
|
82 return fixedName; |
|
83 } |
|
84 |
|
85 // returns true if another instance is already running. |
|
86 // if false, we need to continue regular launch. |
|
87 bool PosixPlatform::CheckForSingleInstance(TString appName) { |
|
88 TString tmpDir = GetTempDirectory(); |
|
89 if (tmpDir.empty()) { |
|
90 printf("Unable to check for single instance.\n"); |
|
91 return false; |
|
92 } |
|
93 |
|
94 TString lockFile = tmpDir + "/" + fixName(appName); |
|
95 SingleInstanceFile = lockFile; |
|
96 int pid_file = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); |
|
97 int rc = flock(pid_file, LOCK_EX | LOCK_NB); |
|
98 |
|
99 if (rc) { |
|
100 if (EWOULDBLOCK == errno) { |
|
101 // another instance is running |
|
102 pid_t pid = 0; |
|
103 read(pid_file, (void*)&pid, sizeof(pid_t)); |
|
104 printf("Another instance is running PID: %d\n", pid); |
|
105 if (pid != 0) { |
|
106 singleInstanceProcessId = pid; |
|
107 SingleInstanceFile.clear(); |
|
108 return true; |
|
109 } |
|
110 } else { |
|
111 printf("Unable to check for single instance.\n"); |
|
112 } |
|
113 } else { |
|
114 // It is the first instance. |
|
115 pid_t pid = getpid(); |
|
116 write(pid_file, (void*)&pid, sizeof(pid_t)); |
|
117 } |
|
118 |
|
119 return false; |
|
120 } |
|
121 |
|
122 MessageResponse PosixPlatform::ShowResponseMessage(TString title, |
|
123 TString description) { |
|
124 MessageResponse result = mrCancel; |
|
125 |
|
126 printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), |
|
127 PlatformString(description).toPlatformString()); |
|
128 fflush(stdout); |
|
129 |
|
130 std::string input; |
|
131 std::cin >> input; |
|
132 |
|
133 if (input == "Y") { |
|
134 result = mrOK; |
|
135 } |
|
136 |
|
137 return result; |
|
138 } |
|
139 |
|
140 void PosixPlatform::SetCurrentDirectory(TString Value) { |
|
141 chdir(StringToFileSystemString(Value)); |
|
142 } |
|
143 |
|
144 Module PosixPlatform::LoadLibrary(TString FileName) { |
|
145 return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); |
|
146 } |
|
147 |
|
148 void PosixPlatform::FreeLibrary(Module AModule) { |
|
149 dlclose(AModule); |
|
150 } |
|
151 |
|
152 Procedure PosixPlatform::GetProcAddress(Module AModule, |
|
153 std::string MethodName) { |
|
154 return dlsym(AModule, PlatformString(MethodName)); |
|
155 } |
|
156 |
|
157 std::vector<std::string> PosixPlatform::GetLibraryImports( |
|
158 const TString FileName) { |
|
159 std::vector<TString> result; |
|
160 return result; |
|
161 } |
|
162 |
|
163 std::vector<TString> PosixPlatform::FilterOutRuntimeDependenciesForPlatform( |
|
164 std::vector<TString> Imports) { |
|
165 std::vector<TString> result; |
|
166 return result; |
|
167 } |
|
168 |
|
169 Process* PosixPlatform::CreateProcess() { |
|
170 return new PosixProcess(); |
|
171 } |
|
172 |
|
173 PosixProcess::PosixProcess() : Process() { |
|
174 FChildPID = 0; |
|
175 FRunning = false; |
|
176 FOutputHandle = 0; |
|
177 FInputHandle = 0; |
|
178 } |
|
179 |
|
180 PosixProcess::~PosixProcess() { |
|
181 Terminate(); |
|
182 } |
|
183 |
|
184 void PosixProcess::Cleanup() { |
|
185 if (FOutputHandle != 0) { |
|
186 close(FOutputHandle); |
|
187 FOutputHandle = 0; |
|
188 } |
|
189 |
|
190 if (FInputHandle != 0) { |
|
191 close(FInputHandle); |
|
192 FInputHandle = 0; |
|
193 } |
|
194 |
|
195 #ifdef MAC |
|
196 sigaction(SIGINT, &savintr, (struct sigaction *)0); |
|
197 sigaction(SIGQUIT, &savequit, (struct sigaction *)0); |
|
198 sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0); |
|
199 #endif //MAC |
|
200 } |
|
201 |
|
202 bool PosixProcess::ReadOutput() { |
|
203 bool result = false; |
|
204 |
|
205 if (FOutputHandle != 0 && IsRunning() == true) { |
|
206 char buffer[4096]; |
|
207 |
|
208 ssize_t count = read(FOutputHandle, buffer, sizeof(buffer)); |
|
209 |
|
210 if (count == -1) { |
|
211 if (errno == EINTR) { |
|
212 // continue; |
|
213 } else { |
|
214 perror("read"); |
|
215 exit(1); |
|
216 } |
|
217 } else if (count == 0) { |
|
218 // break; |
|
219 } else { |
|
220 if (buffer[count] == EOF) { |
|
221 buffer[count] = '\0'; |
|
222 } |
|
223 |
|
224 std::list<TString> output = Helpers::StringToArray(buffer); |
|
225 FOutput.splice(FOutput.end(), output, output.begin(), output.end()); |
|
226 result = true; |
|
227 } |
|
228 } |
|
229 |
|
230 return false; |
|
231 } |
|
232 |
|
233 bool PosixProcess::IsRunning() { |
|
234 bool result = false; |
|
235 |
|
236 if (kill(FChildPID, 0) == 0) { |
|
237 result = true; |
|
238 } |
|
239 |
|
240 return result; |
|
241 } |
|
242 |
|
243 bool PosixProcess::Terminate() { |
|
244 bool result = false; |
|
245 |
|
246 if (IsRunning() == true && FRunning == true) { |
|
247 FRunning = false; |
|
248 Cleanup(); |
|
249 int status = kill(FChildPID, SIGTERM); |
|
250 |
|
251 if (status == 0) { |
|
252 result = true; |
|
253 } else { |
|
254 #ifdef DEBUG |
|
255 if (errno == EINVAL) { |
|
256 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number."); |
|
257 } else if (errno == EPERM) { |
|
258 printf("Kill error: The process does not have permission to send the signal to any receiving process."); |
|
259 } else if (errno == ESRCH) { |
|
260 printf("Kill error: No process or process group can be found corresponding to that specified by pid."); |
|
261 } |
|
262 #endif // DEBUG |
|
263 if (IsRunning() == true) { |
|
264 status = kill(FChildPID, SIGKILL); |
|
265 |
|
266 if (status == 0) { |
|
267 result = true; |
|
268 } |
|
269 } |
|
270 } |
|
271 } |
|
272 |
|
273 return result; |
|
274 } |
|
275 |
|
276 #define PIPE_READ 0 |
|
277 #define PIPE_WRITE 1 |
|
278 |
|
279 bool PosixProcess::Execute(const TString Application, |
|
280 const std::vector<TString> Arguments, bool AWait) { |
|
281 bool result = false; |
|
282 |
|
283 if (FRunning == false) { |
|
284 FRunning = true; |
|
285 |
|
286 int handles[2]; |
|
287 |
|
288 if (pipe(handles) == -1) { |
|
289 return false; |
|
290 } |
|
291 |
|
292 struct sigaction sa; |
|
293 sa.sa_handler = SIG_IGN; |
|
294 sigemptyset(&sa.sa_mask); |
|
295 sa.sa_flags = 0; |
|
296 #ifdef MAC |
|
297 sigemptyset(&savintr.sa_mask); |
|
298 sigemptyset(&savequit.sa_mask); |
|
299 sigaction(SIGINT, &sa, &savintr); |
|
300 sigaction(SIGQUIT, &sa, &savequit); |
|
301 sigaddset(&sa.sa_mask, SIGCHLD); |
|
302 sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); |
|
303 #endif // MAC |
|
304 FChildPID = fork(); |
|
305 |
|
306 // PID returned by vfork is 0 for the child process and the |
|
307 // PID of the child process for the parent. |
|
308 if (FChildPID == -1) { |
|
309 // Error |
|
310 TString message = PlatformString::Format( |
|
311 _T("Error: Unable to create process %s"), |
|
312 Application.data()); |
|
313 throw Exception(message); |
|
314 } |
|
315 else if (FChildPID == 0) { |
|
316 Cleanup(); |
|
317 TString command = Application; |
|
318 |
|
319 for (std::vector<TString>::const_iterator iterator = |
|
320 Arguments.begin(); iterator != Arguments.end(); |
|
321 iterator++) { |
|
322 command += TString(_T(" ")) + *iterator; |
|
323 } |
|
324 #ifdef DEBUG |
|
325 printf("%s\n", command.data()); |
|
326 #endif // DEBUG |
|
327 |
|
328 dup2(handles[PIPE_READ], STDIN_FILENO); |
|
329 dup2(handles[PIPE_WRITE], STDOUT_FILENO); |
|
330 |
|
331 close(handles[PIPE_READ]); |
|
332 close(handles[PIPE_WRITE]); |
|
333 |
|
334 execl("/bin/sh", "sh", "-c", command.data(), (char *)0); |
|
335 |
|
336 _exit(127); |
|
337 } else { |
|
338 FOutputHandle = handles[PIPE_READ]; |
|
339 FInputHandle = handles[PIPE_WRITE]; |
|
340 |
|
341 if (AWait == true) { |
|
342 ReadOutput(); |
|
343 Wait(); |
|
344 Cleanup(); |
|
345 FRunning = false; |
|
346 result = true; |
|
347 } |
|
348 else { |
|
349 result = true; |
|
350 } |
|
351 } |
|
352 } |
|
353 |
|
354 return result; |
|
355 } |
|
356 |
|
357 bool PosixProcess::Wait() { |
|
358 bool result = false; |
|
359 |
|
360 int status = 0; |
|
361 pid_t wpid = 0; |
|
362 |
|
363 #ifdef LINUX |
|
364 wpid = wait(&status); |
|
365 #endif |
|
366 #ifdef MAC |
|
367 wpid = wait(&status); |
|
368 #endif |
|
369 |
|
370 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { |
|
371 if (errno != EINTR){ |
|
372 status = -1; |
|
373 } |
|
374 } |
|
375 |
|
376 #ifdef DEBUG |
|
377 if (WIFEXITED(status)) { |
|
378 printf("child exited, status=%d\n", WEXITSTATUS(status)); |
|
379 } else if (WIFSIGNALED(status)) { |
|
380 printf("child killed (signal %d)\n", WTERMSIG(status)); |
|
381 } else if (WIFSTOPPED(status)) { |
|
382 printf("child stopped (signal %d)\n", WSTOPSIG(status)); |
|
383 #ifdef WIFCONTINUED // Not all implementations support this |
|
384 } else if (WIFCONTINUED(status)) { |
|
385 printf("child continued\n"); |
|
386 #endif // WIFCONTINUED |
|
387 } else { // Non-standard case -- may never happen |
|
388 printf("Unexpected status (0x%x)\n", status); |
|
389 } |
|
390 #endif // DEBUG |
|
391 |
|
392 if (wpid != -1) { |
|
393 result = true; |
|
394 } |
|
395 |
|
396 return result; |
|
397 } |
|
398 |
|
399 TProcessID PosixProcess::GetProcessID() { |
|
400 return FChildPID; |
|
401 } |
|
402 |
|
403 void PosixProcess::SetInput(TString Value) { |
|
404 if (FInputHandle != 0) { |
|
405 write(FInputHandle, Value.data(), Value.size()); |
|
406 } |
|
407 } |
|
408 |
|
409 std::list<TString> PosixProcess::GetOutput() { |
|
410 ReadOutput(); |
|
411 return Process::GetOutput(); |
|
412 } |
|
413 |
|
414 #endif // POSIX |