1 /* 2 * Copyright (c) 2014, 2017, 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 <errno.h> 50 #include <limits.h> 51 #include <pwd.h> 52 #include <iostream> 53 #include <dlfcn.h> 54 #include <signal.h> 55 56 57 PosixPlatform::PosixPlatform(void) { 58 } 59 60 PosixPlatform::~PosixPlatform(void) { 61 if (!SingleInstanceFile.empty()) { 62 unlink(SingleInstanceFile.c_str()); 63 } 64 } 65 66 bool PosixPlatform::mkdirs(const char* path) { 67 char* p = NULL; 68 char tmp_path[PATH_MAX] = {0}; 69 70 if (strlen(path) > (sizeof(tmp_path) - 1)) { 71 // too long name 72 return false; 73 } 74 75 strcpy(tmp_path, path); 76 77 errno = 0; 78 for (p = tmp_path + 1; *p; ++p) { 79 if (*p == '/') { 80 *p = '\0'; 81 if (mkdir(tmp_path, S_IRWXU) != 0) { 82 if (errno != EEXIST) { 83 return false; 84 } 85 } 86 *p = '/'; 87 } 88 } 89 90 if (mkdir(tmp_path, S_IRWXU) != 0) { 91 if (errno != EEXIST) { 92 return false; 93 } 94 } 95 96 return true; 97 } 98 99 bool PosixPlatform::getTmpDir(char* path, int len) { 100 struct passwd* pw = getpwuid(getuid()); 101 const char* homedir = pw->pw_dir; 102 snprintf(path, len-1 , "%s%s", homedir, getTmpDirString()); 103 struct stat sb; 104 if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode)) { 105 // the dir doesn't exist 106 if (!mkdirs(path)) { 107 return false; 108 } 109 } 110 111 return true; 112 } 113 114 // returns true if another instance is already running. 115 // if false, we need to continue regular launch. 116 bool PosixPlatform::CheckForSingleInstance(TString appName) { 117 char tmpDir[PATH_MAX] = {0}; 118 if (!getTmpDir(tmpDir, PATH_MAX)) { 119 printf("Unable to check for single instance.\n"); 120 return false; 121 } 122 123 char lock_file[PATH_MAX] = {0}; 124 snprintf(lock_file, PATH_MAX-1, "%s/%s", tmpDir, appName.c_str()); 125 SingleInstanceFile = lock_file; 126 int pid_file = open(lock_file, O_CREAT | O_RDWR, 0666); 127 int rc = flock(pid_file, LOCK_EX | LOCK_NB); 128 129 if (rc) { 130 if (EWOULDBLOCK == errno) { 131 // another instance is running 132 pid_t pid = 0; 133 read(pid_file, (void*)&pid, sizeof(pid_t)); 134 printf("Another instance is running PID: %d\n", pid); 135 if (pid != 0) { 136 singleInstanceProcessId = pid; 137 SingleInstanceFile.clear(); 138 return true; 139 } 140 } else { 141 printf("Unable to check for single instance.\n"); 142 } 143 } else { 144 // It is the first instance. 145 pid_t pid = getpid(); 146 write(pid_file, (void*)&pid, sizeof(pid_t)); 147 } 148 149 return false; 150 } 151 152 MessageResponse PosixPlatform::ShowResponseMessage(TString title, TString description) { 153 MessageResponse result = mrCancel; 154 155 printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), PlatformString(description).toPlatformString()); 156 fflush(stdout); 157 158 std::string input; 159 std::cin >> input; 160 161 if (input == "Y") { 162 result = mrOK; 163 } 164 165 return result; 166 } 167 168 //MessageResponse PosixPlatform::ShowResponseMessageB(TString description) { 169 // TString appname = GetModuleFileName(); 170 // appname = FilePath::ExtractFileName(appname); 171 // return ShowResponseMessage(appname, description); 172 //} 173 174 void PosixPlatform::SetCurrentDirectory(TString Value) { 175 chdir(StringToFileSystemString(Value)); 176 } 177 178 Module PosixPlatform::LoadLibrary(TString FileName) { 179 return dlopen(StringToFileSystemString(FileName), RTLD_LAZY); 180 } 181 182 void PosixPlatform::FreeLibrary(Module AModule) { 183 dlclose(AModule); 184 } 185 186 Procedure PosixPlatform::GetProcAddress(Module AModule, std::string MethodName) { 187 return dlsym(AModule, PlatformString(MethodName)); 188 } 189 190 std::vector<std::string> PosixPlatform::GetLibraryImports(const TString FileName) { 191 std::vector<TString> result; 192 return result; 193 } 194 195 std::vector<TString> PosixPlatform::FilterOutRuntimeDependenciesForPlatform(std::vector<TString> Imports) { 196 std::vector<TString> result; 197 return result; 198 } 199 200 Process* PosixPlatform::CreateProcess() { 201 return new PosixProcess(); 202 } 203 204 //-------------------------------------------------------------------------------------------------- 205 206 207 PosixProcess::PosixProcess() : Process() { 208 FChildPID = 0; 209 FRunning = false; 210 FOutputHandle = 0; 211 FInputHandle = 0; 212 } 213 214 PosixProcess::~PosixProcess() { 215 Terminate(); 216 } 217 218 void PosixProcess::Cleanup() { 219 if (FOutputHandle != 0) { 220 close(FOutputHandle); 221 FOutputHandle = 0; 222 } 223 224 if (FInputHandle != 0) { 225 close(FInputHandle); 226 FInputHandle = 0; 227 } 228 229 #ifdef MAC 230 sigaction(SIGINT, &savintr, (struct sigaction *)0); 231 sigaction(SIGQUIT, &savequit, (struct sigaction *)0); 232 sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0); 233 #endif //MAC 234 } 235 236 bool PosixProcess::ReadOutput() { 237 bool result = false; 238 239 if (FOutputHandle != 0 && IsRunning() == true) { 240 char buffer[4096]; 241 //select(p[0] + 1, &rfds, NULL, NULL, NULL); 242 243 ssize_t count = read(FOutputHandle, buffer, sizeof(buffer)); 244 245 if (count == -1) { 246 if (errno == EINTR) { 247 //continue; 248 } else { 249 perror("read"); 250 exit(1); 251 } 252 } else if (count == 0) { 253 //break; 254 } else { 255 if (buffer[count] == EOF) { 256 buffer[count] = '\0'; 257 } 258 259 std::list<TString> output = Helpers::StringToArray(buffer); 260 FOutput.splice(FOutput.end(), output, output.begin(), output.end()); 261 result = true; 262 } 263 } 264 265 return false; 266 } 267 268 bool PosixProcess::IsRunning() { 269 bool result = false; 270 271 if (kill(FChildPID, 0) == 0) { 272 result = true; 273 } 274 275 return result; 276 } 277 278 bool PosixProcess::Terminate() { 279 bool result = false; 280 281 if (IsRunning() == true && FRunning == true) { 282 FRunning = false; 283 Cleanup(); 284 int status = kill(FChildPID, SIGTERM); 285 286 if (status == 0) { 287 result = true; 288 } 289 else { 290 #ifdef DEBUG 291 if (errno == EINVAL) 292 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number."); 293 else if (errno == EPERM) 294 printf("Kill error: The process does not have permission to send the signal to any receiving process."); 295 else if (errno == ESRCH) 296 printf("Kill error: No process or process group can be found corresponding to that specified by pid."); 297 #endif //DEBUG 298 if (IsRunning() == true) { 299 status = kill(FChildPID, SIGKILL); 300 301 if (status == 0) { 302 result = true; 303 } 304 } 305 } 306 } 307 308 return result; 309 } 310 311 #define PIPE_READ 0 312 #define PIPE_WRITE 1 313 314 bool PosixProcess::Execute(const TString Application, const std::vector<TString> Arguments, bool AWait) { 315 bool result = false; 316 317 if (FRunning == false) { 318 FRunning = true; 319 320 int handles[2]; 321 322 if (pipe(handles) == -1) { 323 //perror("pipe"); 324 //exit(1); 325 return false; 326 } 327 328 struct sigaction sa; 329 sa.sa_handler = SIG_IGN; 330 sigemptyset(&sa.sa_mask); 331 sa.sa_flags = 0; 332 #ifdef MAC 333 sigemptyset(&savintr.sa_mask); 334 sigemptyset(&savequit.sa_mask); 335 sigaction(SIGINT, &sa, &savintr); 336 sigaction(SIGQUIT, &sa, &savequit); 337 sigaddset(&sa.sa_mask, SIGCHLD); 338 sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock); 339 #endif //MAC 340 FChildPID = fork(); 341 342 // PID returned by vfork is 0 for the child process and the PID of the child 343 // process for the parent. 344 if (FChildPID == -1) { 345 // Error 346 TString message = PlatformString::Format(_T("Error: Unable to create process %s"), Application.data()); 347 throw Exception(message); 348 } 349 else if (FChildPID == 0) { 350 Cleanup(); 351 TString command = Application; 352 353 for (std::vector<TString>::const_iterator iterator = Arguments.begin(); iterator != Arguments.end(); iterator++) { 354 command += TString(_T(" ")) + *iterator; 355 } 356 #ifdef DEBUG 357 printf("%s\n", command.data()); 358 #endif //DEBUG 359 // dup2(FOutputHandle, STDOUT_FILENO); 360 // dup2(FInputHandle, STDIN_FILENO); 361 // close(FOutputHandle); 362 // close(FInputHandle); 363 364 dup2(handles[PIPE_READ], STDIN_FILENO); 365 dup2(handles[PIPE_WRITE], STDOUT_FILENO); 366 // setvbuf(stdout,NULL,_IONBF,0); 367 // setvbuf(stdin,NULL,_IONBF,0); 368 369 close(handles[PIPE_READ]); 370 close(handles[PIPE_WRITE]); 371 372 execl("/bin/sh", "sh", "-c", command.data(), (char *)0); 373 374 _exit(127); 375 } else { 376 // close(handles[PIPE_READ]); 377 // close(handles[PIPE_WRITE]); 378 379 // close(output[1]); 380 // int nbytes = read(output[0], foo, sizeof(foo)); 381 // printf("Output: (%.*s)\n", nbytes, foo); 382 // wait(NULL); 383 FOutputHandle = handles[PIPE_READ]; 384 FInputHandle = handles[PIPE_WRITE]; 385 386 if (AWait == true) { 387 ReadOutput(); 388 Wait(); 389 Cleanup(); 390 FRunning = false; 391 result = true; 392 } 393 else { 394 result = true; 395 } 396 } 397 } 398 399 return result; 400 } 401 402 bool PosixProcess::Wait() { 403 bool result = false; 404 405 int status = 0; 406 pid_t wpid = 0; 407 408 #ifdef LINUX 409 wait(); 410 #endif 411 #ifdef MAC 412 wpid = wait(&status); 413 #endif 414 415 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { 416 if (errno != EINTR){ 417 status = -1; 418 } 419 } 420 421 #ifdef DEBUG 422 if (WIFEXITED(status)) { 423 printf("child exited, status=%d\n", WEXITSTATUS(status)); 424 } else if (WIFSIGNALED(status)) { 425 printf("child killed (signal %d)\n", WTERMSIG(status)); 426 } else if (WIFSTOPPED(status)) { 427 printf("child stopped (signal %d)\n", WSTOPSIG(status)); 428 #ifdef WIFCONTINUED // Not all implementations support this 429 } else if (WIFCONTINUED(status)) { 430 printf("child continued\n"); 431 #endif //WIFCONTINUED 432 } else { // Non-standard case -- may never happen 433 printf("Unexpected status (0x%x)\n", status); 434 } 435 #endif //DEBUG 436 437 if (wpid != -1) { 438 result = true; 439 } 440 441 return result; 442 } 443 444 TProcessID PosixProcess::GetProcessID() { 445 return FChildPID; 446 } 447 448 void PosixProcess::SetInput(TString Value) { 449 if (FInputHandle != 0) { 450 write(FInputHandle, Value.data(), Value.size()); 451 } 452 } 453 454 std::list<TString> PosixProcess::GetOutput() { 455 ReadOutput(); 456 return Process::GetOutput(); 457 } 458 459 #endif //POSIX