1 /*
   2  * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "PosixPlatform.h"
  27 
  28 #ifdef POSIX
  29 
  30 #include "PlatformString.h"
  31 #include "FilePath.h"
  32 #include "Helpers.h"
  33 
  34 #include <assert.h>
  35 #include <stdbool.h>
  36 #include <sys/types.h>
  37 #include <unistd.h>
  38 #include <sys/sysctl.h>
  39 #include <sys/file.h>
  40 #include <sys/stat.h>
  41 #ifdef LINUX
  42 #include <sys/wait.h>
  43 #endif
  44 #include <errno.h>
  45 #include <limits.h>
  46 #include <pwd.h>
  47 #include <iostream>
  48 #include <algorithm>
  49 #include <dlfcn.h>
  50 #include <signal.h>
  51 
  52 
  53 PosixPlatform::PosixPlatform(void) {
  54 }
  55 
  56 PosixPlatform::~PosixPlatform(void) {
  57 }
  58 
  59 TString PosixPlatform::GetTempDirectory() {
  60     struct passwd* pw = getpwuid(getuid());
  61     TString homedir(pw->pw_dir);
  62     homedir += getTmpDirString();
  63     if (!FilePath::DirectoryExists(homedir)) {
  64         if (!FilePath::CreateDirectory(homedir, false)) {
  65             homedir.clear();
  66         }
  67     }
  68 
  69     return homedir;
  70 }
  71 
  72 TString PosixPlatform::fixName(const TString& name) {
  73     TString fixedName(name);
  74     const TString chars("?:*<>/\\");
  75     for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) {
  76         fixedName.erase(std::remove(fixedName.begin(),
  77                 fixedName.end(), *it), fixedName.end());
  78     }
  79     return fixedName;
  80 }
  81 
  82 MessageResponse PosixPlatform::ShowResponseMessage(TString title,
  83         TString description) {
  84     MessageResponse result = mrCancel;
  85 
  86     printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(),
  87             PlatformString(description).toPlatformString());
  88     fflush(stdout);
  89 
  90     std::string input;
  91     std::cin >> input;
  92 
  93     if (input == "Y") {
  94         result = mrOK;
  95     }
  96 
  97     return result;
  98 }
  99 
 100 void PosixPlatform::SetCurrentDirectory(TString Value) {
 101     chdir(StringToFileSystemString(Value));
 102 }
 103 
 104 Module PosixPlatform::LoadLibrary(TString FileName) {
 105     return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
 106 }
 107 
 108 void PosixPlatform::FreeLibrary(Module AModule) {
 109     dlclose(AModule);
 110 }
 111 
 112 Procedure PosixPlatform::GetProcAddress(Module AModule,
 113         std::string MethodName) {
 114     return dlsym(AModule, PlatformString(MethodName));
 115 }
 116 
 117 std::vector<std::string> PosixPlatform::GetLibraryImports(
 118        const TString FileName) {
 119  std::vector<TString> result;
 120  return result;
 121 }
 122 
 123 std::vector<TString> PosixPlatform::FilterOutRuntimeDependenciesForPlatform(
 124        std::vector<TString> Imports) {
 125  std::vector<TString> result;
 126  return result;
 127 }
 128 
 129 Process* PosixPlatform::CreateProcess() {
 130     return new PosixProcess();
 131 }
 132 
 133 PosixProcess::PosixProcess() : Process() {
 134     FChildPID = 0;
 135     FRunning = false;
 136     FOutputHandle = 0;
 137     FInputHandle = 0;
 138 }
 139 
 140 PosixProcess::~PosixProcess() {
 141     Terminate();
 142 }
 143 
 144 void PosixProcess::Cleanup() {
 145     if (FOutputHandle != 0) {
 146         close(FOutputHandle);
 147         FOutputHandle = 0;
 148     }
 149 
 150     if (FInputHandle != 0) {
 151         close(FInputHandle);
 152         FInputHandle = 0;
 153     }
 154 
 155 #ifdef MAC
 156     sigaction(SIGINT, &savintr, (struct sigaction *)0);
 157     sigaction(SIGQUIT, &savequit, (struct sigaction *)0);
 158     sigprocmask(SIG_SETMASK, &saveblock, (sigset_t *)0);
 159 #endif //MAC
 160 }
 161 
 162 bool PosixProcess::ReadOutput() {
 163     bool result = false;
 164 
 165     if (FOutputHandle != 0 && IsRunning() == true) {
 166         char buffer[4096] = {0};
 167 
 168         ssize_t count = read(FOutputHandle, buffer, sizeof(buffer));
 169 
 170         if (count == -1) {
 171             if (errno == EINTR) {
 172                 // continue;
 173             } else {
 174                 perror("read");
 175                 exit(1);
 176             }
 177         } else if (count == 0) {
 178             // break;
 179         } else {
 180             if (buffer[count - 1] == EOF) {
 181                 buffer[count - 1] = '\0';
 182             }
 183 
 184             std::list<TString> output = Helpers::StringToArray(buffer);
 185             FOutput.splice(FOutput.end(), output, output.begin(), output.end());
 186             result = true;
 187         }
 188     }
 189 
 190     return false;
 191 }
 192 
 193 bool PosixProcess::IsRunning() {
 194     bool result = false;
 195 
 196     if (kill(FChildPID, 0) == 0) {
 197         result = true;
 198     }
 199 
 200     return result;
 201 }
 202 
 203 bool PosixProcess::Terminate() {
 204     bool result = false;
 205 
 206     if (IsRunning() == true && FRunning == true) {
 207         FRunning = false;
 208         Cleanup();
 209         int status = kill(FChildPID, SIGTERM);
 210 
 211         if (status == 0) {
 212             result = true;
 213         } else {
 214 #ifdef DEBUG
 215             if (errno == EINVAL) {
 216                 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number.");
 217             } else if (errno == EPERM) {
 218                 printf("Kill error: The process does not have permission to send the signal to any receiving process.");
 219             } else if (errno == ESRCH) {
 220                 printf("Kill error: No process or process group can be found corresponding to that specified by pid.");
 221             }
 222 #endif // DEBUG
 223             if (IsRunning() == true) {
 224                 status = kill(FChildPID, SIGKILL);
 225 
 226                 if (status == 0) {
 227                     result = true;
 228                 }
 229             }
 230         }
 231     }
 232 
 233     return result;
 234 }
 235 
 236 #define PIPE_READ 0
 237 #define PIPE_WRITE 1
 238 
 239 bool PosixProcess::Execute(const TString Application,
 240         const std::vector<TString> Arguments, bool AWait) {
 241     bool result = false;
 242 
 243     if (FRunning == false) {
 244         FRunning = true;
 245 
 246         int handles[2];
 247 
 248         if (pipe(handles) == -1) {
 249             return false;
 250         }
 251 
 252         struct sigaction sa;
 253         sa.sa_handler = SIG_IGN;
 254         sigemptyset(&sa.sa_mask);
 255         sa.sa_flags = 0;
 256 #ifdef MAC
 257         sigemptyset(&savintr.sa_mask);
 258         sigemptyset(&savequit.sa_mask);
 259         sigaction(SIGINT, &sa, &savintr);
 260         sigaction(SIGQUIT, &sa, &savequit);
 261         sigaddset(&sa.sa_mask, SIGCHLD);
 262         sigprocmask(SIG_BLOCK, &sa.sa_mask, &saveblock);
 263 #endif // MAC
 264         FChildPID = fork();
 265 
 266         // PID returned by vfork is 0 for the child process and the
 267         // PID of the child process for the parent.
 268         if (FChildPID == -1) {
 269             // Error
 270             TString message = PlatformString::Format(
 271                     _T("Error: Unable to create process %s"),
 272                     Application.data());
 273             throw Exception(message);
 274         }
 275         else if (FChildPID == 0) {
 276             Cleanup();
 277             TString command = Application;
 278 
 279             for (std::vector<TString>::const_iterator iterator =
 280                     Arguments.begin(); iterator != Arguments.end();
 281                     iterator++) {
 282                 command += TString(_T(" ")) + *iterator;
 283             }
 284 #ifdef DEBUG
 285             printf("%s\n", command.data());
 286 #endif // DEBUG
 287 
 288             dup2(handles[PIPE_READ], STDIN_FILENO);
 289             dup2(handles[PIPE_WRITE], STDOUT_FILENO);
 290 
 291             close(handles[PIPE_READ]);
 292             close(handles[PIPE_WRITE]);
 293 
 294             execl("/bin/sh", "sh", "-c", command.data(), (char *)0);
 295 
 296             _exit(127);
 297         } else {
 298             FOutputHandle = handles[PIPE_READ];
 299             FInputHandle = handles[PIPE_WRITE];
 300 
 301             if (AWait == true) {
 302                 ReadOutput();
 303                 Wait();
 304                 Cleanup();
 305                 FRunning = false;
 306                 result = true;
 307             }
 308             else {
 309                 result = true;
 310             }
 311         }
 312     }
 313 
 314     return result;
 315 }
 316 
 317 bool PosixProcess::Wait() {
 318     bool result = false;
 319 
 320     int status = 0;
 321     pid_t wpid = 0;
 322 
 323 #ifdef LINUX
 324     wpid = wait(&status);
 325 #endif
 326 #ifdef MAC
 327     wpid = wait(&status);
 328 #endif
 329 
 330     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 331         if (errno != EINTR){
 332             status = -1;
 333         }
 334     }
 335 
 336 #ifdef DEBUG
 337     if (WIFEXITED(status)) {
 338         printf("child exited, status=%d\n", WEXITSTATUS(status));
 339     } else if (WIFSIGNALED(status)) {
 340         printf("child killed (signal %d)\n", WTERMSIG(status));
 341     } else if (WIFSTOPPED(status)) {
 342         printf("child stopped (signal %d)\n", WSTOPSIG(status));
 343 #ifdef WIFCONTINUED // Not all implementations support this
 344     } else if (WIFCONTINUED(status)) {
 345         printf("child continued\n");
 346 #endif // WIFCONTINUED
 347     } else { // Non-standard case -- may never happen
 348         printf("Unexpected status (0x%x)\n", status);
 349     }
 350 #endif // DEBUG
 351 
 352     if (wpid != -1) {
 353         result = true;
 354     }
 355 
 356     return result;
 357 }
 358 
 359 TProcessID PosixProcess::GetProcessID() {
 360     return FChildPID;
 361 }
 362 
 363 void PosixProcess::SetInput(TString Value) {
 364     if (FInputHandle != 0) {
 365         write(FInputHandle, Value.data(), Value.size());
 366     }
 367 }
 368 
 369 std::list<TString> PosixProcess::GetOutput() {
 370     ReadOutput();
 371     return Process::GetOutput();
 372 }
 373 
 374 #endif // POSIX