1 /* 2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 34 #include "PosixPlatform.h" 35 36 #ifdef POSIX 37 38 #include "PlatformString.h" 39 #include "FilePath.h" 40 #include "Helpers.h" 41 42 #include <assert.h> 43 #include <stdbool.h> 44 #include <sys/types.h> 45 #include <unistd.h> 46 #include <sys/sysctl.h> 47 #include <sys/file.h> 48 #include <sys/stat.h> 49 #include <sys/wait.h> 50 #include <errno.h> 51 #include <limits.h> 52 #include <pwd.h> 53 #include <iostream> 54 #include <algorithm> 55 #include <dlfcn.h> 56 #include <signal.h> 57 58 59 PosixPlatform::PosixPlatform(void) { 60 } 61 62 PosixPlatform::~PosixPlatform(void) { 63 if (!SingleInstanceFile.empty()) { 64 unlink(SingleInstanceFile.c_str()); 65 } 66 } 67 68 TString PosixPlatform::GetTempDirectory() { 69 struct passwd* pw = getpwuid(getuid()); 70 TString homedir(pw->pw_dir); 71 homedir += getTmpDirString(); 72 if (!FilePath::DirectoryExists(homedir)) { 73 if (!FilePath::CreateDirectory(homedir, false)) { 74 homedir.clear(); 75 } 76 } 77 78 return homedir; 79 } 80 81 TString PosixPlatform::fixName(const TString& name) { 82 TString fixedName(name); 83 const TString chars("?:*<>/\\"); 84 for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) { 85 fixedName.erase(std::remove(fixedName.begin(), fixedName.end(), *it), fixedName.end()); 86 } 87 return fixedName; 88 } 89 90 // returns true if another instance is already running. 91 // if false, we need to continue regular launch. 92 bool PosixPlatform::CheckForSingleInstance(TString appName) { 93 TString tmpDir = GetTempDirectory(); 94 if (tmpDir.empty()) { 95 printf("Unable to check for single instance.\n"); 96 return false; 97 } 98 99 TString lockFile = tmpDir + "/" + fixName(appName); 100 SingleInstanceFile = lockFile; 101 int pid_file = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); 102 int rc = flock(pid_file, LOCK_EX | LOCK_NB); 103 104 if (rc) { 105 if (EWOULDBLOCK == errno) { 106 // another instance is running 107 pid_t pid = 0; 108 read(pid_file, (void*)&pid, sizeof(pid_t)); 109 printf("Another instance is running PID: %d\n", pid); 110 if (pid != 0) { 111 singleInstanceProcessId = pid; 112 SingleInstanceFile.clear(); 113 return true; 114 } 115 } else { 116 printf("Unable to check for single instance.\n"); 117 } 118 } else { 119 // It is the first instance. 120 pid_t pid = getpid(); 121 write(pid_file, (void*)&pid, sizeof(pid_t)); 122 } 123 124 return false; 125 } 126 127 MessageResponse PosixPlatform::ShowResponseMessage(TString title, TString description) { 128 MessageResponse result = mrCancel; 129 130 printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), PlatformString(description).toPlatformString()); 131 fflush(stdout); 132 133 std::string input; 134 std::cin >> input; 135 136 if (input == "Y") { 137 result = mrOK; 138 } 139 140 return result; 141 } 142 143 //MessageResponse PosixPlatform::ShowResponseMessageB(TString description) { 144 // TString appname = GetModuleFileName(); 145 // appname = FilePath::ExtractFileName(appname); 146 // return ShowResponseMessage(appname, description); 147 //} 148 149 void PosixPlatform::SetCurrentDirectory(TString Value) { 150 chdir(StringToFileSystemString(Value)); 151 } 152 153 Module PosixPlatform::LoadLibrary(TString FileName) { 154 return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); 155 } 156 157 void PosixPlatform::FreeLibrary(Module AModule) { 158 dlclose(AModule); 159 } 160 161 Procedure PosixPlatform::GetProcAddress(Module AModule, std::string MethodName) { 162 return dlsym(AModule, PlatformString(MethodName)); 163 } 164 165 std::vector<std::string> PosixPlatform::GetLibraryImports(const TString FileName) { 166 std::vector<TString> result; 167 return result; 168 } 169 170 std::vector<TString> PosixPlatform::FilterOutRuntimeDependenciesForPlatform(std::vector<TString> Imports) { 171 std::vector<TString> result; 172 return result; 173 } 174 175 Process* PosixPlatform::CreateProcess() { 176 return new PosixProcess(); 177 } 178 179 //-------------------------------------------------------------------------------------------------- 180 181 182 PosixProcess::PosixProcess() : Process() { 183 FChildPID = 0; 184 FRunning = false; 185 FOutputHandle = 0; 186 FInputHandle = 0; 187 } 188 189 PosixProcess::~PosixProcess() { 190 Terminate(); 191 } 192 193 void PosixProcess::Cleanup() { 194 if (FOutputHandle != 0) { 195 close(FOutputHandle); 196 FOutputHandle = 0; 197 } 198 199 if (FInputHandle != 0) { 200 close(FInputHandle); 201 FInputHandle = 0; 202 } 203 204 #ifdef MAC 205 sigaction(SIGINT, &savintr, (struct sigaction *)0); 206 sigaction(SIGQUIT, &savequit, (struct sigaction *)0); 207 sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0); 208 #endif //MAC 209 } 210 211 bool PosixProcess::ReadOutput() { 212 bool result = false; 213 214 if (FOutputHandle != 0 && IsRunning() == true) { 215 char buffer[4096]; 216 //select(p[0] + 1, &rfds, NULL, NULL, NULL); 217 218 ssize_t count = read(FOutputHandle, buffer, sizeof(buffer)); 219 220 if (count == -1) { 221 if (errno == EINTR) { 222 //continue; 223 } else { 224 perror("read"); 225 exit(1); 226 } 227 } else if (count == 0) { 228 //break; 229 } else { 230 if (buffer[count] == EOF) { 231 buffer[count] = '\0'; 232 } 233 234 std::list<TString> output = Helpers::StringToArray(buffer); 235 FOutput.splice(FOutput.end(), output, output.begin(), output.end()); 236 result = true; 237 } 238 } 239 240 return false; 241 } 242 243 bool PosixProcess::IsRunning() { 244 bool result = false; 245 246 if (kill(FChildPID, 0) == 0) { 247 result = true; 248 } 249 250 return result; 251 } 252 253 bool PosixProcess::Terminate() { 254 bool result = false; 255 256 if (IsRunning() == true && FRunning == true) { 257 FRunning = false; 258 Cleanup(); 259 int status = kill(FChildPID, SIGTERM); 260 261 if (status == 0) { 262 result = true; 263 } 264 else { 265 #ifdef DEBUG 266 if (errno == EINVAL) 267 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number."); 268 else if (errno == EPERM) 269 printf("Kill error: The process does not have permission to send the signal to any receiving process."); 270 else if (errno == ESRCH) 271 printf("Kill error: No process or process group can be found corresponding to that specified by pid."); 272 #endif //DEBUG 273 if (IsRunning() == true) { 274 status = kill(FChildPID, SIGKILL); 275 276 if (status == 0) { 277 result = true; 278 } 279 } 280 } 281 } 282 283 return result; 284 } 285 286 #define PIPE_READ 0 287 #define PIPE_WRITE 1 288 289 bool PosixProcess::Execute(const TString Application, const std::vector<TString> Arguments, bool AWait) { 290 bool result = false; 291 292 if (FRunning == false) { 293 FRunning = true; 294 295 int handles[2]; 296 297 if (pipe(handles) == -1) { 298 //perror("pipe"); 299 //exit(1); 300 return false; 301 } 302 303 struct sigaction sa; 304 sa.sa_handler = SIG_IGN; 305 sigemptyset(&sa.sa_mask); 306 sa.sa_flags = 0; 307 #ifdef MAC 308 sigemptyset(&savintr.sa_mask); 309 sigemptyset(&savequit.sa_mask); 310 sigaction(SIGINT, &sa, &savintr); 311 sigaction(SIGQUIT, &sa, &savequit); 312 sigaddset(&sa.sa_mask, SIGCHLD); 313 sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); 314 #endif //MAC 315 FChildPID = fork(); 316 317 // PID returned by vfork is 0 for the child process and the PID of the child 318 // process for the parent. 319 if (FChildPID == -1) { 320 // Error 321 TString message = PlatformString::Format(_T("Error: Unable to create process %s"), Application.data()); 322 throw Exception(message); 323 } 324 else if (FChildPID == 0) { 325 Cleanup(); 326 TString command = Application; 327 328 for (std::vector<TString>::const_iterator iterator = Arguments.begin(); iterator != Arguments.end(); iterator++) { 329 command += TString(_T(" ")) + *iterator; 330 } 331 #ifdef DEBUG 332 printf("%s\n", command.data()); 333 #endif //DEBUG 334 // dup2(FOutputHandle, STDOUT_FILENO); 335 // dup2(FInputHandle, STDIN_FILENO); 336 // close(FOutputHandle); 337 // close(FInputHandle); 338 339 dup2(handles[PIPE_READ], STDIN_FILENO); 340 dup2(handles[PIPE_WRITE], STDOUT_FILENO); 341 // setvbuf(stdout,NULL,_IONBF,0); 342 // setvbuf(stdin,NULL,_IONBF,0); 343 344 close(handles[PIPE_READ]); 345 close(handles[PIPE_WRITE]); 346 347 execl("/bin/sh", "sh", "-c", command.data(), (char *)0); 348 349 _exit(127); 350 } else { 351 // close(handles[PIPE_READ]); 352 // close(handles[PIPE_WRITE]); 353 354 // close(output[1]); 355 // int nbytes = read(output[0], foo, sizeof(foo)); 356 // printf("Output: (%.*s)\n", nbytes, foo); 357 // wait(NULL); 358 FOutputHandle = handles[PIPE_READ]; 359 FInputHandle = handles[PIPE_WRITE]; 360 361 if (AWait == true) { 362 ReadOutput(); 363 Wait(); 364 Cleanup(); 365 FRunning = false; 366 result = true; 367 } 368 else { 369 result = true; 370 } 371 } 372 } 373 374 return result; 375 } 376 377 bool PosixProcess::Wait() { 378 bool result = false; 379 380 int status = 0; 381 pid_t wpid = 0; 382 383 #ifdef LINUX 384 wait(&status); 385 #endif 386 #ifdef MAC 387 wpid = wait(&status); 388 #endif 389 390 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 391 if (errno != EINTR){ 392 status = -1; 393 } 394 } 395 396 #ifdef DEBUG 397 if (WIFEXITED(status)) { 398 printf("child exited, status=%d\n", WEXITSTATUS(status)); 399 } else if (WIFSIGNALED(status)) { 400 printf("child killed (signal %d)\n", WTERMSIG(status)); 401 } else if (WIFSTOPPED(status)) { 402 printf("child stopped (signal %d)\n", WSTOPSIG(status)); 403 #ifdef WIFCONTINUED // Not all implementations support this 404 } else if (WIFCONTINUED(status)) { 405 printf("child continued\n"); 406 #endif //WIFCONTINUED 407 } else { // Non-standard case -- may never happen 408 printf("Unexpected status (0x%x)\n", status); 409 } 410 #endif //DEBUG 411 412 if (wpid != -1) { 413 result = true; 414 } 415 416 return result; 417 } 418 419 TProcessID PosixProcess::GetProcessID() { 420 return FChildPID; 421 } 422 423 void PosixProcess::SetInput(TString Value) { 424 if (FInputHandle != 0) { 425 write(FInputHandle, Value.data(), Value.size()); 426 } 427 } 428 429 std::list<TString> PosixProcess::GetOutput() { 430 ReadOutput(); 431 return Process::GetOutput(); 432 } 433 434 #endif //POSIX