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