brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.9 KiB · 779e040 Raw
288 lines · cpp
1//===-- lib/runtime/execute.cpp ---------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Runtime/execute.h"10#include "flang-rt/runtime/descriptor.h"11#include "flang-rt/runtime/environment.h"12#include "flang-rt/runtime/stat.h"13#include "flang-rt/runtime/terminator.h"14#include "flang-rt/runtime/tools.h"15#include <cstdlib>16#include <errno.h>17#include <future>18#include <limits>19 20#ifdef _WIN3221#include "flang/Common/windows-include.h"22#else23#include <signal.h>24#include <sys/wait.h>25#include <unistd.h>26#endif27 28namespace Fortran::runtime {29 30// cmdstat specified in 16.9.7331// −1 if the processor does not support command line execution,32// a processor-dependent positive value if an error condition occurs33// −2 if no error condition occurs but WAIT is present with the value false34// and the processor does not support asynchronous execution. Otherwise it is35// assigned the value 036enum CMD_STAT {37  ASYNC_NO_SUPPORT_ERR = -2, // system returns -1 with ENOENT38  NO_SUPPORT_ERR = -1, // Linux setsid() returns -139  CMD_EXECUTED = 0, // command executed with no error40  FORK_ERR = 1, // Linux fork() returns < 041  EXECL_ERR = 2, // system returns -1 with other errno42  COMMAND_EXECUTION_ERR = 3, // exit code 143  COMMAND_CANNOT_EXECUTE_ERR = 4, // Linux exit code 12644  COMMAND_NOT_FOUND_ERR = 5, // Linux exit code 12745  INVALID_CL_ERR = 6, // cover all other non-zero exit code46  SIGNAL_ERR = 747};48 49// Override CopyCharsToDescriptor in tools.h, pass string directly50void CopyCharsToDescriptor(const Descriptor &value, const char *rawValue) {51  CopyCharsToDescriptor(value, rawValue, std::strlen(rawValue));52}53 54void CheckAndCopyCharsToDescriptor(55    const Descriptor *value, const char *rawValue) {56  if (value) {57    CopyCharsToDescriptor(*value, rawValue);58  }59}60 61void CheckAndStoreIntToDescriptor(62    const Descriptor *intVal, std::int64_t value, Terminator &terminator) {63  if (intVal) {64    StoreIntToDescriptor(intVal, value, terminator);65  }66}67 68// If a condition occurs that would assign a nonzero value to CMDSTAT but69// the CMDSTAT variable is not present, error termination is initiated.70std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,71    const Descriptor *cmdmsg, Terminator &terminator) {72  // On both Windows and Linux, errno is set when system returns -1.73  if (status == -1) {74    // On Windows, ENOENT means the command interpreter can't be found.75    // On Linux, system calls execl with filepath "/bin/sh", ENOENT means the76    // file pathname does not exist.77    if (errno == ENOENT) {78      if (!cmdstat) {79        terminator.Crash("Command line execution is not supported, system "80                         "returns -1 with errno ENOENT.");81      } else {82        StoreIntToDescriptor(cmdstat, NO_SUPPORT_ERR, terminator);83        CheckAndCopyCharsToDescriptor(cmdmsg,84            "Command line execution is not supported, system returns -1 with "85            "errno ENOENT.");86      }87    } else {88      char err_buffer[30];89      char msg[]{"Execution error with system status code: -1, errno: "};90#ifdef _WIN3291      if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)92#else93      if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)94#endif95        terminator.Crash("errno to char msg failed.");96      char *newMsg{static_cast<char *>(AllocateMemoryOrCrash(97          terminator, std::strlen(msg) + std::strlen(err_buffer) + 1))};98      std::strcat(newMsg, err_buffer);99 100      if (!cmdstat) {101        terminator.Crash(newMsg);102      } else {103        StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);104        CheckAndCopyCharsToDescriptor(cmdmsg, newMsg);105      }106      FreeMemory(newMsg);107    }108  }109 110#ifdef _WIN32111  // On WIN32 API std::system returns exit status directly112  std::int64_t exitStatusVal{status};113  if (exitStatusVal != 0) {114    if (!cmdstat) {115      terminator.Crash(116          "Invalid command quit with exit status code: %d", exitStatusVal);117    } else {118      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);119      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");120    }121  }122#else123  std::int64_t exitStatusVal{WEXITSTATUS(status)};124  if (exitStatusVal == 1) {125    if (!cmdstat) {126      terminator.Crash("Command line execution failed with exit code: 1.");127    } else {128      StoreIntToDescriptor(cmdstat, COMMAND_EXECUTION_ERR, terminator);129      CheckAndCopyCharsToDescriptor(130          cmdmsg, "Command line execution failed with exit code: 1.");131    }132  } else if (exitStatusVal == 126) {133    if (!cmdstat) {134      terminator.Crash("Command cannot be executed with exit code: 126.");135    } else {136      StoreIntToDescriptor(cmdstat, COMMAND_CANNOT_EXECUTE_ERR, terminator);137      CheckAndCopyCharsToDescriptor(138          cmdmsg, "Command cannot be executed with exit code: 126.");139    }140  } else if (exitStatusVal == 127) {141    if (!cmdstat) {142      terminator.Crash("Command not found with exit code: 127.");143    } else {144      StoreIntToDescriptor(cmdstat, COMMAND_NOT_FOUND_ERR, terminator);145      CheckAndCopyCharsToDescriptor(146          cmdmsg, "Command not found with exit code: 127.");147    }148    // capture all other nonzero exit code149  } else if (exitStatusVal != 0) {150    if (!cmdstat) {151      terminator.Crash(152          "Invalid command quit with exit status code: %d", exitStatusVal);153    } else {154      StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);155      CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");156    }157  }158#endif159 160#if defined(WIFSIGNALED) && defined(WTERMSIG)161  if (WIFSIGNALED(status)) {162    if (!cmdstat) {163      terminator.Crash("Killed by signal: %d", WTERMSIG(status));164    } else {165      StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);166      CheckAndCopyCharsToDescriptor(cmdmsg, "Killed by signal");167    }168  }169#endif170 171#if defined(WIFSTOPPED) && defined(WSTOPSIG)172  if (WIFSTOPPED(status)) {173    if (!cmdstat) {174      terminator.Crash("Stopped by signal: %d", WSTOPSIG(status));175    } else {176      StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);177      CheckAndCopyCharsToDescriptor(cmdmsg, "Stopped by signal");178    }179  }180#endif181  return exitStatusVal;182}183 184void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,185    const Descriptor *exitstat, const Descriptor *cmdstat,186    const Descriptor *cmdmsg, const char *sourceFile, int line) {187  Terminator terminator{sourceFile, line};188  char *newCmd{EnsureNullTerminated(189      command.OffsetElement(), command.ElementBytes(), terminator)};190 191  if (exitstat) {192    RUNTIME_CHECK(terminator, IsValidIntDescriptor(exitstat));193  }194 195  if (cmdstat) {196    RUNTIME_CHECK(terminator, IsValidIntDescriptor(cmdstat));197    // Assigned 0 as specifed in standard, if error then overwrite198    StoreIntToDescriptor(cmdstat, CMD_EXECUTED, terminator);199  }200 201  if (cmdmsg) {202    RUNTIME_CHECK(terminator, IsValidCharDescriptor(cmdmsg));203  }204 205  if (wait) {206    // either wait is not specified or wait is true: synchronous mode207    std::int64_t status{std::system(newCmd)};208    std::int64_t exitStatusVal{209        TerminationCheck(status, cmdstat, cmdmsg, terminator)};210    // If sync, assigned processor-dependent exit status. Otherwise unchanged211    CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);212  } else {213// Asynchronous mode214#ifdef _WIN32215    STARTUPINFOW si;216    PROCESS_INFORMATION pi;217    ZeroMemory(&si, sizeof(si));218    si.cb = sizeof(si);219    ZeroMemory(&pi, sizeof(pi));220 221    // add "cmd.exe /c " to the beginning of command222    const char *prefix{"cmd.exe /c "};223    char *newCmdWin{static_cast<char *>(AllocateMemoryOrCrash(224        terminator, std::strlen(prefix) + std::strlen(newCmd) + 1))};225    std::strcpy(newCmdWin, prefix);226    std::strcat(newCmdWin, newCmd);227 228    // Convert the char to wide char229    const size_t sizeNeeded{mbstowcs(NULL, newCmdWin, 0) + 1};230    wchar_t *wcmd{static_cast<wchar_t *>(231        AllocateMemoryOrCrash(terminator, sizeNeeded * sizeof(wchar_t)))};232    if (std::mbstowcs(wcmd, newCmdWin, sizeNeeded) == static_cast<size_t>(-1)) {233      terminator.Crash("Char to wide char failed for newCmd");234    }235    FreeMemory(newCmdWin);236 237    if (CreateProcessW(nullptr, wcmd, nullptr, nullptr, FALSE, 0, nullptr,238            nullptr, &si, &pi)) {239      // Close handles so it will be removed when terminated240      CloseHandle(pi.hProcess);241      CloseHandle(pi.hThread);242    } else {243      if (!cmdstat) {244        terminator.Crash(245            "CreateProcess failed with error code: %lu.", GetLastError());246      } else {247        StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);248        CheckAndCopyCharsToDescriptor(cmdmsg, "CreateProcess failed.");249      }250    }251    FreeMemory(wcmd);252#else253    pid_t pid{fork()};254    if (pid < 0) {255      if (!cmdstat) {256        terminator.Crash("Fork failed with pid: %d.", pid);257      } else {258        StoreIntToDescriptor(cmdstat, FORK_ERR, terminator);259        CheckAndCopyCharsToDescriptor(cmdmsg, "Fork failed");260      }261    } else if (pid == 0) {262      // Create a new session, let init process take care of zombie child263      if (setsid() == -1) {264        if (!cmdstat) {265          terminator.Crash("setsid() failed with errno: %d, asynchronous "266                           "process initiation failed.",267              errno);268        } else {269          StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);270          CheckAndCopyCharsToDescriptor(cmdmsg,271              "setsid() failed, asynchronous process initiation failed.");272        }273        exit(EXIT_FAILURE);274      }275      std::int64_t status{std::system(newCmd)};276      TerminationCheck(status, cmdstat, cmdmsg, terminator);277      exit(status);278    }279#endif280  }281  // Deallocate memory if EnsureNullTerminated dynamically allocated memory282  if (newCmd != command.OffsetElement()) {283    FreeMemory(newCmd);284  }285}286 287} // namespace Fortran::runtime288