llvm-project

Форк
0
/
execute.cpp 
287 строк · 9.9 Кб
1
//===-- runtime/execute.cpp -----------------------------------------------===//
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-exception
6
//
7
//===----------------------------------------------------------------------===//
8

9
#include "flang/Runtime/execute.h"
10
#include "environment.h"
11
#include "stat.h"
12
#include "terminator.h"
13
#include "tools.h"
14
#include "flang/Runtime/descriptor.h"
15
#include <cstdlib>
16
#include <errno.h>
17
#include <future>
18
#include <limits>
19

20
#ifdef _WIN32
21
#include "flang/Common/windows-include.h"
22
#else
23
#include <signal.h>
24
#include <sys/wait.h>
25
#include <unistd.h>
26
#endif
27

28
namespace Fortran::runtime {
29

30
// cmdstat specified in 16.9.73
31
// −1 if the processor does not support command line execution,
32
// a processor-dependent positive value if an error condition occurs
33
// −2 if no error condition occurs but WAIT is present with the value false
34
// and the processor does not support asynchronous execution. Otherwise it is
35
// assigned the value 0
36
enum CMD_STAT {
37
  ASYNC_NO_SUPPORT_ERR = -2, // system returns -1 with ENOENT
38
  NO_SUPPORT_ERR = -1, // Linux setsid() returns -1
39
  CMD_EXECUTED = 0, // command executed with no error
40
  FORK_ERR = 1, // Linux fork() returns < 0
41
  EXECL_ERR = 2, // system returns -1 with other errno
42
  COMMAND_EXECUTION_ERR = 3, // exit code 1
43
  COMMAND_CANNOT_EXECUTE_ERR = 4, // Linux exit code 126
44
  COMMAND_NOT_FOUND_ERR = 5, // Linux exit code 127
45
  INVALID_CL_ERR = 6, // cover all other non-zero exit code
46
  SIGNAL_ERR = 7
47
};
48

49
// Override CopyCharsToDescriptor in tools.h, pass string directly
50
void CopyCharsToDescriptor(const Descriptor &value, const char *rawValue) {
51
  CopyCharsToDescriptor(value, rawValue, std::strlen(rawValue));
52
}
53

54
void CheckAndCopyCharsToDescriptor(
55
    const Descriptor *value, const char *rawValue) {
56
  if (value) {
57
    CopyCharsToDescriptor(*value, rawValue);
58
  }
59
}
60

61
void 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 but
69
// the CMDSTAT variable is not present, error termination is initiated.
70
std::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 the
76
    // 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 _WIN32
91
      if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)
92
#else
93
      if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)
94
#endif
95
        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 _WIN32
111
  // On WIN32 API std::system returns exit status directly
112
  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
#else
123
  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 code
149
  } 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
#endif
159

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
#endif
170

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
#endif
181
  return exitStatusVal;
182
}
183

184
void 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 overwrite
198
    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 mode
207
    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 unchanged
211
    CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);
212
  } else {
213
// Asynchronous mode
214
#ifdef _WIN32
215
    STARTUPINFO 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 command
222
    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 char
229
    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 (CreateProcess(nullptr, wcmd, nullptr, nullptr, FALSE, 0, nullptr,
238
            nullptr, &si, &pi)) {
239
      // Close handles so it will be removed when terminated
240
      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
#else
253
    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 child
263
      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
#endif
280
  }
281
  // Deallocate memory if EnsureNullTerminated dynamically allocated memory
282
  if (newCmd != command.OffsetElement()) {
283
    FreeMemory(newCmd);
284
  }
285
}
286

287
} // namespace Fortran::runtime
288

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.