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