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