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