1 /* 2 * Copyright (c) 2014, 2019, 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 } 58 59 TString PosixPlatform::GetTempDirectory() { 60 struct passwd* pw = getpwuid(getuid()); 61 TString homedir(pw->pw_dir); 62 homedir += getTmpDirString(); 63 if (!FilePath::DirectoryExists(homedir)) { 64 if (!FilePath::CreateDirectory(homedir, false)) { 65 homedir.clear(); 66 } 67 } 68 69 return homedir; 70 } 71 72 TString PosixPlatform::fixName(const TString& name) { 73 TString fixedName(name); 74 const TString chars("?:*<>/\\"); 75 for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) { 76 fixedName.erase(std::remove(fixedName.begin(), 77 fixedName.end(), *it), fixedName.end()); 78 } 79 return fixedName; 80 } 81 82 MessageResponse PosixPlatform::ShowResponseMessage(TString title, 83 TString description) { 84 MessageResponse result = mrCancel; 85 86 printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), 87 PlatformString(description).toPlatformString()); 88 fflush(stdout); 89 90 std::string input; 91 std::cin >> input; 92 93 if (input == "Y") { 94 result = mrOK; 95 } 96 97 return result; 98 } 99 100 void PosixPlatform::SetCurrentDirectory(TString Value) { 101 chdir(StringToFileSystemString(Value)); 102 } 103 104 Module PosixPlatform::LoadLibrary(TString FileName) { 105 return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); 106 } 107 108 void PosixPlatform::FreeLibrary(Module AModule) { 109 dlclose(AModule); 110 } 111 112 Procedure PosixPlatform::GetProcAddress(Module AModule, 113 std::string MethodName) { 114 return dlsym(AModule, PlatformString(MethodName)); 115 } 116 117 std::vector<std::string> PosixPlatform::GetLibraryImports( 118 const TString FileName) { 119 std::vector<TString> result; 120 return result; 121 } 122 123 std::vector<TString> PosixPlatform::FilterOutRuntimeDependenciesForPlatform( 124 std::vector<TString> Imports) { 125 std::vector<TString> result; 126 return result; 127 } 128 129 Process* PosixPlatform::CreateProcess() { 130 return new PosixProcess(); 131 } 132 133 PosixProcess::PosixProcess() : Process() { 134 FChildPID = 0; 135 FRunning = false; 136 FOutputHandle = 0; 137 FInputHandle = 0; 138 } 139 140 PosixProcess::~PosixProcess() { 141 Terminate(); 142 } 143 144 void PosixProcess::Cleanup() { 145 if (FOutputHandle != 0) { 146 close(FOutputHandle); 147 FOutputHandle = 0; 148 } 149 150 if (FInputHandle != 0) { 151 close(FInputHandle); 152 FInputHandle = 0; 153 } 154 155 #ifdef MAC 156 sigaction(SIGINT, &savintr, (struct sigaction *)0); 157 sigaction(SIGQUIT, &savequit, (struct sigaction *)0); 158 sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0); 159 #endif //MAC 160 } 161 162 bool PosixProcess::ReadOutput() { 163 bool result = false; 164 165 if (FOutputHandle != 0 && IsRunning() == true) { 166 char buffer[4096] = {0}; 167 168 ssize_t count = read(FOutputHandle, buffer, sizeof(buffer)); 169 170 if (count == -1) { 171 if (errno == EINTR) { 172 // continue; 173 } else { 174 perror("read"); 175 exit(1); 176 } 177 } else if (count == 0) { 178 // break; 179 } else { 180 if (buffer[count - 1] == EOF) { 181 buffer[count - 1] = '\0'; 182 } 183 184 std::list<TString> output = Helpers::StringToArray(buffer); 185 FOutput.splice(FOutput.end(), output, output.begin(), output.end()); 186 result = true; 187 } 188 } 189 190 return false; 191 } 192 193 bool PosixProcess::IsRunning() { 194 bool result = false; 195 196 if (kill(FChildPID, 0) == 0) { 197 result = true; 198 } 199 200 return result; 201 } 202 203 bool PosixProcess::Terminate() { 204 bool result = false; 205 206 if (IsRunning() == true && FRunning == true) { 207 FRunning = false; 208 Cleanup(); 209 int status = kill(FChildPID, SIGTERM); 210 211 if (status == 0) { 212 result = true; 213 } else { 214 #ifdef DEBUG 215 if (errno == EINVAL) { 216 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number."); 217 } else if (errno == EPERM) { 218 printf("Kill error: The process does not have permission to send the signal to any receiving process."); 219 } else if (errno == ESRCH) { 220 printf("Kill error: No process or process group can be found corresponding to that specified by pid."); 221 } 222 #endif // DEBUG 223 if (IsRunning() == true) { 224 status = kill(FChildPID, SIGKILL); 225 226 if (status == 0) { 227 result = true; 228 } 229 } 230 } 231 } 232 233 return result; 234 } 235 236 #define PIPE_READ 0 237 #define PIPE_WRITE 1 238 239 bool PosixProcess::Execute(const TString Application, 240 const std::vector<TString> Arguments, bool AWait) { 241 bool result = false; 242 243 if (FRunning == false) { 244 FRunning = true; 245 246 int handles[2]; 247 248 if (pipe(handles) == -1) { 249 return false; 250 } 251 252 struct sigaction sa; 253 sa.sa_handler = SIG_IGN; 254 sigemptyset(&sa.sa_mask); 255 sa.sa_flags = 0; 256 #ifdef MAC 257 sigemptyset(&savintr.sa_mask); 258 sigemptyset(&savequit.sa_mask); 259 sigaction(SIGINT, &sa, &savintr); 260 sigaction(SIGQUIT, &sa, &savequit); 261 sigaddset(&sa.sa_mask, SIGCHLD); 262 sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); 263 #endif // MAC 264 FChildPID = fork(); 265 266 // PID returned by vfork is 0 for the child process and the 267 // PID of the child process for the parent. 268 if (FChildPID == -1) { 269 // Error 270 TString message = PlatformString::Format( 271 _T("Error: Unable to create process %s"), 272 Application.data()); 273 throw Exception(message); 274 } 275 else if (FChildPID == 0) { 276 Cleanup(); 277 TString command = Application; 278 279 for (std::vector<TString>::const_iterator iterator = 280 Arguments.begin(); iterator != Arguments.end(); 281 iterator++) { 282 command += TString(_T(" ")) + *iterator; 283 } 284 #ifdef DEBUG 285 printf("%s\n", command.data()); 286 #endif // DEBUG 287 288 dup2(handles[PIPE_READ], STDIN_FILENO); 289 dup2(handles[PIPE_WRITE], STDOUT_FILENO); 290 291 close(handles[PIPE_READ]); 292 close(handles[PIPE_WRITE]); 293 294 execl("/bin/sh", "sh", "-c", command.data(), (char *)0); 295 296 _exit(127); 297 } else { 298 FOutputHandle = handles[PIPE_READ]; 299 FInputHandle = handles[PIPE_WRITE]; 300 301 if (AWait == true) { 302 ReadOutput(); 303 Wait(); 304 Cleanup(); 305 FRunning = false; 306 result = true; 307 } 308 else { 309 result = true; 310 } 311 } 312 } 313 314 return result; 315 } 316 317 bool PosixProcess::Wait() { 318 bool result = false; 319 320 int status = 0; 321 pid_t wpid = 0; 322 323 #ifdef LINUX 324 wpid = wait(&status); 325 #endif 326 #ifdef MAC 327 wpid = wait(&status); 328 #endif 329 330 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 331 if (errno != EINTR){ 332 status = -1; 333 } 334 } 335 336 #ifdef DEBUG 337 if (WIFEXITED(status)) { 338 printf("child exited, status=%d\n", WEXITSTATUS(status)); 339 } else if (WIFSIGNALED(status)) { 340 printf("child killed (signal %d)\n", WTERMSIG(status)); 341 } else if (WIFSTOPPED(status)) { 342 printf("child stopped (signal %d)\n", WSTOPSIG(status)); 343 #ifdef WIFCONTINUED // Not all implementations support this 344 } else if (WIFCONTINUED(status)) { 345 printf("child continued\n"); 346 #endif // WIFCONTINUED 347 } else { // Non-standard case -- may never happen 348 printf("Unexpected status (0x%x)\n", status); 349 } 350 #endif // DEBUG 351 352 if (wpid != -1) { 353 result = true; 354 } 355 356 return result; 357 } 358 359 TProcessID PosixProcess::GetProcessID() { 360 return FChildPID; 361 } 362 363 void PosixProcess::SetInput(TString Value) { 364 if (FInputHandle != 0) { 365 write(FInputHandle, Value.data(), Value.size()); 366 } 367 } 368 369 std::list<TString> PosixProcess::GetOutput() { 370 ReadOutput(); 371 return Process::GetOutput(); 372 } 373 374 #endif // POSIX