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 #include "PlatformString.h"
  29 #include "FilePath.h"
  30 #include "Helpers.h"
  31 
  32 #include <assert.h>
  33 #include <stdbool.h>
  34 #include <sys/types.h>
  35 #include <unistd.h>
  36 #include <sys/sysctl.h>
  37 #include <sys/file.h>
  38 #include <sys/stat.h>
  39 #include <sys/wait.h>
  40 #include <errno.h>
  41 #include <limits.h>
  42 #include <pwd.h>
  43 #include <iostream>
  44 #include <algorithm>
  45 #include <dlfcn.h>
  46 #include <signal.h>
  47 
  48 using namespace std;
  49 
  50 PosixPlatform::PosixPlatform(void) {
  51 }
  52 
  53 PosixPlatform::~PosixPlatform(void) {
  54 }
  55 
  56 TString PosixPlatform::GetTempDirectory() {
  57     struct passwd* pw = getpwuid(getuid());
  58     TString homedir(pw->pw_dir);
  59     homedir += getTmpDirString();
  60     if (!FilePath::DirectoryExists(homedir)) {
  61         if (!FilePath::CreateDirectory(homedir, false)) {
  62             homedir.clear();
  63         }
  64     }
  65 
  66     return homedir;
  67 }
  68 
  69 TString PosixPlatform::fixName(const TString& name) {
  70     TString fixedName(name);
  71     const TString chars("?:*<>/\\");
  72     for (TString::const_iterator it = chars.begin(); it != chars.end(); it++) {
  73         fixedName.erase(std::remove(fixedName.begin(),
  74                 fixedName.end(), *it), fixedName.end());
  75     }
  76     return fixedName;
  77 }
  78 
  79 MessageResponse PosixPlatform::ShowResponseMessage(TString title,
  80         TString description) {
  81     MessageResponse result = mrCancel;
  82 
  83     printf("%s %s (Y/N)\n", PlatformString(title).toPlatformString(),
  84             PlatformString(description).toPlatformString());
  85     fflush(stdout);
  86 
  87     std::string input;
  88     std::cin >> input;
  89 
  90     if (input == "Y") {
  91         result = mrOK;
  92     }
  93 
  94     return result;
  95 }
  96 
  97 void PosixPlatform::SetCurrentDirectory(TString Value) {
  98     chdir(StringToFileSystemString(Value));
  99 }
 100 
 101 Module PosixPlatform::LoadLibrary(TString FileName) {
 102     return dlopen(StringToFileSystemString(FileName), RTLD_LAZY);
 103 }
 104 
 105 void PosixPlatform::FreeLibrary(Module AModule) {
 106     dlclose(AModule);
 107 }
 108 
 109 Procedure PosixPlatform::GetProcAddress(Module AModule,
 110         std::string MethodName) {
 111     return dlsym(AModule, PlatformString(MethodName));
 112 }
 113 
 114 Process* PosixPlatform::CreateProcess() {
 115     return new PosixProcess();
 116 }
 117 
 118 void PosixPlatform::addPlatformDependencies(JavaLibrary *pJavaLibrary) {
 119 }
 120 
 121 void Platform::CopyString(char *Destination,
 122         size_t NumberOfElements, const char *Source) {
 123     strncpy(Destination, Source, NumberOfElements);
 124 
 125     if (NumberOfElements > 0) {
 126         Destination[NumberOfElements - 1] = '\0';
 127     }
 128 }
 129 
 130 void Platform::CopyString(wchar_t *Destination,
 131         size_t NumberOfElements, const wchar_t *Source) {
 132     wcsncpy(Destination, Source, NumberOfElements);
 133 
 134     if (NumberOfElements > 0) {
 135         Destination[NumberOfElements - 1] = '\0';
 136     }
 137 }
 138 
 139 // Owner must free the return value.
 140 
 141 MultibyteString Platform::WideStringToMultibyteString(
 142         const wchar_t* value) {
 143     MultibyteString result;
 144     size_t count = 0;
 145 
 146     if (value == NULL) {
 147         return result;
 148     }
 149 
 150     count = wcstombs(NULL, value, 0);
 151     if (count > 0) {
 152         result.data = new char[count + 1];
 153         result.data[count] = '\0';
 154         result.length = count;
 155         wcstombs(result.data, value, count);
 156     }
 157 
 158     return result;
 159 }
 160 
 161 // Owner must free the return value.
 162 
 163 WideString Platform::MultibyteStringToWideString(const char* value) {
 164     WideString result;
 165     size_t count = 0;
 166 
 167     if (value == NULL) {
 168         return result;
 169     }
 170 
 171     count = mbstowcs(NULL, value, 0);
 172     if (count > 0) {
 173         result.data = new wchar_t[count + 1];
 174         result.data[count] = '\0';
 175         result.length = count;
 176         mbstowcs(result.data, value, count);
 177     }
 178 
 179     return result;
 180 }
 181 
 182 void PosixPlatform::InitStreamLocale(wios *stream) {
 183     // Nothing to do for POSIX platforms.
 184 }
 185 
 186 PosixProcess::PosixProcess() : Process() {
 187     FChildPID = 0;
 188     FRunning = false;
 189     FOutputHandle = 0;
 190     FInputHandle = 0;
 191 }
 192 
 193 PosixProcess::~PosixProcess() {
 194     Terminate();
 195 }
 196 
 197 bool PosixProcess::ReadOutput() {
 198     bool result = false;
 199 
 200     if (FOutputHandle != 0 && IsRunning() == true) {
 201         char buffer[4096] = {0};
 202 
 203         ssize_t count = read(FOutputHandle, buffer, sizeof (buffer));
 204 
 205         if (count == -1) {
 206             if (errno == EINTR) {
 207                 // continue;
 208             } else {
 209                 perror("read");
 210                 exit(1);
 211             }
 212         } else if (count == 0) {
 213             // break;
 214         } else {
 215             if (buffer[count - 1] == EOF) {
 216                 buffer[count - 1] = '\0';
 217             }
 218 
 219             std::list<TString> output = Helpers::StringToArray(buffer);
 220             FOutput.splice(FOutput.end(), output, output.begin(), output.end());
 221             result = true;
 222         }
 223     }
 224 
 225     return false;
 226 }
 227 
 228 bool PosixProcess::IsRunning() {
 229     bool result = false;
 230 
 231     if (kill(FChildPID, 0) == 0) {
 232         result = true;
 233     }
 234 
 235     return result;
 236 }
 237 
 238 bool PosixProcess::Terminate() {
 239     bool result = false;
 240 
 241     if (IsRunning() == true && FRunning == true) {
 242         FRunning = false;
 243         Cleanup();
 244         int status = kill(FChildPID, SIGTERM);
 245 
 246         if (status == 0) {
 247             result = true;
 248         } else {
 249 #ifdef DEBUG
 250             if (errno == EINVAL) {
 251                 printf("Kill error: The value of the sig argument is an invalid or unsupported signal number.");
 252             } else if (errno == EPERM) {
 253                 printf("Kill error: The process does not have permission to send the signal to any receiving process.");
 254             } else if (errno == ESRCH) {
 255                 printf("Kill error: No process or process group can be found corresponding to that specified by pid.");
 256             }
 257 #endif // DEBUG
 258             if (IsRunning() == true) {
 259                 status = kill(FChildPID, SIGKILL);
 260 
 261                 if (status == 0) {
 262                     result = true;
 263                 }
 264             }
 265         }
 266     }
 267 
 268     return result;
 269 }
 270 
 271 bool PosixProcess::Wait() {
 272     bool result = false;
 273 
 274     int status = 0;
 275     pid_t wpid = 0;
 276 
 277     wpid = wait(&status);
 278     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
 279         if (errno != EINTR) {
 280             status = -1;
 281         }
 282     }
 283 
 284 #ifdef DEBUG
 285     if (WIFEXITED(status)) {
 286         printf("child exited, status=%d\n", WEXITSTATUS(status));
 287     } else if (WIFSIGNALED(status)) {
 288         printf("child killed (signal %d)\n", WTERMSIG(status));
 289     } else if (WIFSTOPPED(status)) {
 290         printf("child stopped (signal %d)\n", WSTOPSIG(status));
 291 #ifdef WIFCONTINUED // Not all implementations support this
 292     } else if (WIFCONTINUED(status)) {
 293         printf("child continued\n");
 294 #endif // WIFCONTINUED
 295     } else { // Non-standard case -- may never happen
 296         printf("Unexpected status (0x%x)\n", status);
 297     }
 298 #endif // DEBUG
 299 
 300     if (wpid != -1) {
 301         result = true;
 302     }
 303 
 304     return result;
 305 }
 306 
 307 TProcessID PosixProcess::GetProcessID() {
 308     return FChildPID;
 309 }
 310 
 311 void PosixProcess::SetInput(TString Value) {
 312     if (FInputHandle != 0) {
 313         write(FInputHandle, Value.data(), Value.size());
 314     }
 315 }
 316 
 317 std::list<TString> PosixProcess::GetOutput() {
 318     ReadOutput();
 319     return Process::GetOutput();
 320 }