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