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