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