1 /*
   2  * Copyright (c) 2014, 2015, 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 
  41 #include <assert.h>
  42 #include <stdbool.h>
  43 #include <sys/types.h>
  44 #include <unistd.h>
  45 #include <sys/sysctl.h>
  46 #include <iostream>
  47 #include <dlfcn.h>
  48 #include <signal.h>
  49 
  50 
  51 PosixPlatform::PosixPlatform(void) {
  52 }
  53 
  54 PosixPlatform::~PosixPlatform(void) {
  55 }
  56 
  57 MessageResponse PosixPlatform::ShowResponseMessage(TString title, TString description) {
  58     MessageResponse result = mrCancel;
  59 
  60     printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(), PlatformString(description).toPlatformString());
  61     fflush(stdout);
  62 
  63     std::string input;
  64     std::cin >> input;
  65 
  66     if (input == "Y") {
  67         result = mrOK;
  68     }
  69 
  70     return result;
  71 }
  72 
  73 //MessageResponse PosixPlatform::ShowResponseMessageB(TString description) {
  74 //    TString appname = GetModuleFileName();
  75 //    appname = FilePath::ExtractFileName(appname);
  76 //    return ShowResponseMessage(appname, description);
  77 //}
  78 
  79 void PosixPlatform::SetCurrentDirectory(TString Value) {
  80     chdir(StringToFileSystemString(Value));
  81 }
  82 
  83 Module PosixPlatform::LoadLibrary(TString FileName) {
  84     return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
  85 }
  86 
  87 void PosixPlatform::FreeLibrary(Module AModule) {
  88     dlclose(AModule);
  89 }
  90 
  91 Procedure PosixPlatform::GetProcAddress(Module AModule, std::string MethodName) {
  92     return dlsym(AModule, PlatformString(MethodName));
  93 }
  94 
  95 std::vector<std::string> PosixPlatform::GetLibraryImports(const TString FileName) {
  96  std::vector<TString> result;
  97  return result;
  98 }
  99 
 100 std::vector<TString> PosixPlatform::FilterOutRuntimeDependenciesForPlatform(std::vector<TString> Imports) {
 101  std::vector<TString> result;
 102  return result;
 103 }
 104 
 105 Process* PosixPlatform::CreateProcess() {
 106     return new PosixProcess();
 107 }
 108 
 109 //--------------------------------------------------------------------------------------------------
 110 
 111 
 112 PosixProcess::PosixProcess() : Process() {
 113     FChildPID = 0;
 114     FRunning = false;
 115 }
 116 
 117 PosixProcess::~PosixProcess() {
 118     Terminate();
 119 }
 120 
 121 void PosixProcess::Cleanup() {
 122 #ifdef MAC
 123     sigaction(SIGINT, &savintr, (struct sigaction *)0);
 124     sigaction(SIGQUIT, &savequit, (struct sigaction *)0);
 125     sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0);
 126 #endif //MAC
 127 }
 128 
 129 bool PosixProcess::IsRunning() {
 130     bool result = false;
 131 
 132     if (kill(FChildPID, 0) == 0) {
 133         result = true;
 134     }
 135 
 136     return result;
 137 }
 138 
 139 bool PosixProcess::Terminate() {
 140     bool result = false;
 141 
 142     if (IsRunning() == true && FRunning == true) {
 143         FRunning = false;
 144         Cleanup();
 145         int status = kill(FChildPID, SIGTERM);
 146 
 147         if (status == 0) {
 148             result = true;
 149         }
 150         else {
 151 #ifdef DEBUG
 152             if (errno == EINVAL)
 153                 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number.");
 154             else if (errno == EPERM)
 155                 printf("Kill error: The process does not have permission to send the signal to any receiving process.");
 156             else if (errno == ESRCH)
 157                 printf("Kill error: No process or process group can be found corresponding to that specified by pid.");
 158 #endif //DEBUG
 159             if (IsRunning() == true) {
 160                 status = kill(FChildPID, SIGKILL);
 161 
 162                 if (status == 0) {
 163                     result = true;
 164                 }
 165             }
 166         }
 167     }
 168 
 169     return result;
 170 }
 171 
 172 bool PosixProcess::Execute(const TString Application, const std::vector<TString> Arguments, bool AWait) {
 173     bool result = false;
 174 
 175     if (FRunning == false) {
 176         FRunning = true;
 177 
 178         struct sigaction sa;
 179         sa.sa_handler = SIG_IGN;
 180         sigemptyset(&sa.sa_mask);
 181         sa.sa_flags = 0;
 182 #ifdef MAC
 183         sigemptyset(&savintr.sa_mask);
 184         sigemptyset(&savequit.sa_mask);
 185         sigaction(SIGINT, &sa, &savintr);
 186         sigaction(SIGQUIT, &sa, &savequit);
 187         sigaddset(&sa.sa_mask, SIGCHLD);
 188         sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock);
 189 #endif //MAC
 190         FChildPID = fork();
 191 
 192         // PID returned by vfork is 0 for the child process and the PID of the child
 193         // process for the parent.
 194         if (FChildPID == -1) {
 195             // Error
 196             TString message = PlatformString::Format(_T("Error: Unable to create process %s"), Application.data());
 197             throw Exception(message);
 198         }
 199         else if (FChildPID == 0) {
 200             Cleanup();
 201             TString command = Application;
 202 
 203             for (std::vector<TString>::const_iterator iterator = Arguments.begin(); iterator != Arguments.end(); iterator++) {
 204                 command += TString(_T(" ")) + *iterator;
 205             }
 206 #ifdef DEBUG
 207             printf("%s\n", command.data());
 208 #endif //DEBUG
 209             execl("/bin/sh", "sh", "-c", command.data(), (char *)0);
 210             _exit(127);
 211         } else {
 212             if (AWait == true) {
 213                 Wait();
 214                 Cleanup();
 215                 FRunning = false;
 216                 result = true;
 217             }
 218             else {
 219                 result = true;
 220             }
 221         }
 222     }
 223 
 224     return result;
 225 }
 226 
 227 bool PosixProcess::Wait() {
 228     bool result = false;
 229 
 230     int status = 0;
 231     pid_t wpid;
 232 
 233     //TODO Use waitpid instead of wait
 234 #ifdef LINUX
 235     wait();
 236 #endif
 237 #ifdef MAC
 238     wpid = wait(&status);
 239 #endif
 240 
 241     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 242         if (errno != EINTR){
 243             status = -1;
 244         }
 245     }
 246 
 247 #ifdef DEBUG
 248     if (WIFEXITED(status)) {
 249         printf("child exited, status=%d\n", WEXITSTATUS(status));
 250     } else if (WIFSIGNALED(status)) {
 251         printf("child killed (signal %d)\n", WTERMSIG(status));
 252     } else if (WIFSTOPPED(status)) {
 253         printf("child stopped (signal %d)\n", WSTOPSIG(status));
 254 #ifdef WIFCONTINUED // Not all implementations support this
 255     } else if (WIFCONTINUED(status)) {
 256         printf("child continued\n");
 257 #endif //WIFCONTINUED
 258     } else { // Non-standard case -- may never happen
 259         printf("Unexpected status (0x%x)\n", status);
 260     }
 261 #endif //DEBUG
 262 
 263     if (wpid != -1) {
 264         result = true;
 265     }
 266 
 267     return result;
 268 }
 269 
 270 TProcessID PosixProcess::GetProcessID() {
 271     return FChildPID;
 272 }
 273 
 274 #endif //POSIX