Skip to content

Commit dfd2711

Browse files
authored
Revert "Revert "[flang] Fix execute_command_line cmdstat is not set when error occurs" (#96365)" (#96774)
The fix broke llvm-test-suite, so it was reverted previously. With test fixes added in llvm/llvm-test-suite#137, it should now pass the tests This reverts commit 4356356.
1 parent 8467cc6 commit dfd2711

File tree

3 files changed

+171
-51
lines changed

3 files changed

+171
-51
lines changed

flang/docs/Intrinsics.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -893,16 +893,17 @@ used in constant expressions have currently no folding support at all.
893893
##### `CMDSTAT`:
894894

895895
- Synchronous execution:
896-
- -2: No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
897-
- -1: The processor does not support command line execution.
896+
- -2: `ASYNC_NO_SUPPORT_ERR` - No error condition occurs, but `WAIT` is present with the value `false`, and the processor does not support asynchronous execution.
897+
- -1: `NO_SUPPORT_ERR` - The processor does not support command line execution. (system returns -1 with errno `ENOENT`)
898+
- 0: `CMD_EXECUTED` - Command executed with no error.
898899
- \+ (positive value): An error condition occurs.
899-
- 1: Fork Error (occurs only on POSIX-compatible systems).
900-
- 2: Execution Error (command exits with status -1).
901-
- 3: Invalid Command Error (determined by the exit code depending on the system).
902-
- On Windows: exit code is 1.
903-
- On POSIX-compatible systems: exit code is 127 or 126.
904-
- 4: Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
905-
- 0: Otherwise.
900+
- 1: `FORK_ERR` - Fork Error (occurs only on POSIX-compatible systems).
901+
- 2: `EXECL_ERR` - Execution Error (system returns -1 with other errno).
902+
- 3: `COMMAND_EXECUTION_ERR` - Invalid Command Error (exit code 1).
903+
- 4: `COMMAND_CANNOT_EXECUTE_ERR` - Command Cannot Execute Error (Linux exit code 126).
904+
- 5: `COMMAND_NOT_FOUND_ERR` - Command Not Found Error (Linux exit code 127).
905+
- 6: `INVALID_CL_ERR` - Invalid Command Line Error (covers all other non-zero exit codes).
906+
- 7: `SIGNAL_ERR` - Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
906907
- Asynchronous execution:
907908
- 0 will always be assigned.
908909

flang/runtime/execute.cpp

Lines changed: 96 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#include "tools.h"
1414
#include "flang/Runtime/descriptor.h"
1515
#include <cstdlib>
16+
#include <errno.h>
1617
#include <future>
1718
#include <limits>
19+
1820
#ifdef _WIN32
1921
#include "flang/Common/windows-include.h"
2022
#else
@@ -32,13 +34,16 @@ namespace Fortran::runtime {
3234
// and the processor does not support asynchronous execution. Otherwise it is
3335
// assigned the value 0
3436
enum CMD_STAT {
35-
ASYNC_NO_SUPPORT_ERR = -2,
36-
NO_SUPPORT_ERR = -1,
37-
CMD_EXECUTED = 0,
38-
FORK_ERR = 1,
39-
EXECL_ERR = 2,
40-
INVALID_CL_ERR = 3,
41-
SIGNAL_ERR = 4
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
4247
};
4348

4449
// Override CopyCharsToDescriptor in tools.h, pass string directly
@@ -62,24 +67,86 @@ void CheckAndStoreIntToDescriptor(
6267

6368
// If a condition occurs that would assign a nonzero value to CMDSTAT but
6469
// the CMDSTAT variable is not present, error termination is initiated.
65-
int TerminationCheck(int status, const Descriptor *cmdstat,
70+
std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
6671
const Descriptor *cmdmsg, Terminator &terminator) {
72+
// On both Windows and Linux, errno is set when system returns -1.
6773
if (status == -1) {
68-
if (!cmdstat) {
69-
terminator.Crash("Execution error with system status code: %d", status);
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+
}
7087
} else {
71-
StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
72-
CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
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);
73107
}
74108
}
109+
75110
#ifdef _WIN32
76111
// On WIN32 API std::system returns exit status directly
77-
int exitStatusVal{status};
78-
if (exitStatusVal == 1) {
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+
}
79122
#else
80-
int exitStatusVal{WEXITSTATUS(status)};
81-
if (exitStatusVal == 127 || exitStatusVal == 126) {
82-
#endif
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) {
83150
if (!cmdstat) {
84151
terminator.Crash(
85152
"Invalid command quit with exit status code: %d", exitStatusVal);
@@ -88,23 +155,26 @@ int TerminationCheck(int status, const Descriptor *cmdstat,
88155
CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
89156
}
90157
}
158+
#endif
159+
91160
#if defined(WIFSIGNALED) && defined(WTERMSIG)
92161
if (WIFSIGNALED(status)) {
93162
if (!cmdstat) {
94-
terminator.Crash("killed by signal: %d", WTERMSIG(status));
163+
terminator.Crash("Killed by signal: %d", WTERMSIG(status));
95164
} else {
96165
StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
97-
CheckAndCopyCharsToDescriptor(cmdmsg, "killed by signal");
166+
CheckAndCopyCharsToDescriptor(cmdmsg, "Killed by signal");
98167
}
99168
}
100169
#endif
170+
101171
#if defined(WIFSTOPPED) && defined(WSTOPSIG)
102172
if (WIFSTOPPED(status)) {
103173
if (!cmdstat) {
104-
terminator.Crash("stopped by signal: %d", WSTOPSIG(status));
174+
terminator.Crash("Stopped by signal: %d", WSTOPSIG(status));
105175
} else {
106176
StoreIntToDescriptor(cmdstat, SIGNAL_ERR, terminator);
107-
CheckAndCopyCharsToDescriptor(cmdmsg, "stopped by signal");
177+
CheckAndCopyCharsToDescriptor(cmdmsg, "Stopped by signal");
108178
}
109179
}
110180
#endif
@@ -134,8 +204,9 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
134204

135205
if (wait) {
136206
// either wait is not specified or wait is true: synchronous mode
137-
int status{std::system(newCmd)};
138-
int exitStatusVal{TerminationCheck(status, cmdstat, cmdmsg, terminator)};
207+
std::int64_t status{std::system(newCmd)};
208+
std::int64_t exitStatusVal{
209+
TerminationCheck(status, cmdstat, cmdmsg, terminator)};
139210
// If sync, assigned processor-dependent exit status. Otherwise unchanged
140211
CheckAndStoreIntToDescriptor(exitstat, exitStatusVal, terminator);
141212
} else {
@@ -173,7 +244,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
173244
terminator.Crash(
174245
"CreateProcess failed with error code: %lu.", GetLastError());
175246
} else {
176-
StoreIntToDescriptor(cmdstat, (uint32_t)GetLastError(), terminator);
247+
StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);
177248
CheckAndCopyCharsToDescriptor(cmdmsg, "CreateProcess failed.");
178249
}
179250
}
@@ -201,7 +272,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
201272
}
202273
exit(EXIT_FAILURE);
203274
}
204-
int status{std::system(newCmd)};
275+
std::int64_t status{std::system(newCmd)};
205276
TerminationCheck(status, cmdstat, cmdmsg, terminator);
206277
exit(status);
207278
}

flang/unittests/Runtime/CommandTest.cpp

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
311311
TEST_F(ZeroArguments, ECLValidCommandAndPadSync) {
312312
OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
313313
bool wait{true};
314-
OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
315-
OwningPtr<Descriptor> cmdStat{EmptyIntDescriptor()};
314+
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
315+
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
316316
OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
317317

318318
RTNAME(ExecuteCommandLine)
@@ -339,40 +339,91 @@ TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) {
339339
CheckDescriptorEqStr(cmdMsg.get(), "No change");
340340
}
341341

342-
TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
343-
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
342+
TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
343+
OwningPtr<Descriptor> command{CharDescriptor("cat GeneralErrorCommand")};
344+
bool wait{true};
345+
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
346+
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
347+
OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXXXXXXXX")};
348+
349+
RTNAME(ExecuteCommandLine)
350+
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
351+
#ifdef _WIN32
352+
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
353+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
354+
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX");
355+
#else
356+
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
357+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
358+
CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed");
359+
#endif
360+
}
361+
362+
TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
363+
OwningPtr<Descriptor> command{CharDescriptor(
364+
"touch NotExecutedCommandFile && chmod -x NotExecutedCommandFile && "
365+
"./NotExecutedCommandFile")};
366+
bool wait{true};
367+
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
368+
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
369+
OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};
370+
371+
RTNAME(ExecuteCommandLine)
372+
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
373+
#ifdef _WIN32
374+
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
375+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
376+
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
377+
#else
378+
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
379+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
380+
CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");
381+
// removing the file only on Linux (file is not created on Win)
382+
OwningPtr<Descriptor> commandClean{
383+
CharDescriptor("rm -f NotExecutedCommandFile")};
384+
OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};
385+
RTNAME(ExecuteCommandLine)
386+
(*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());
387+
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
388+
CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
389+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
390+
#endif
391+
}
392+
393+
TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
394+
OwningPtr<Descriptor> command{CharDescriptor("NotFoundCommand")};
344395
bool wait{true};
345396
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
346397
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
347-
OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
398+
OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")};
348399

349400
RTNAME(ExecuteCommandLine)
350401
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
351402
#ifdef _WIN32
352-
CheckDescriptorEqInt(exitStat.get(), 1);
403+
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
404+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
405+
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX");
353406
#else
354407
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
408+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
409+
CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit");
355410
#endif
356-
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
357-
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
358411
}
359412

360413
TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
361414
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
362415
bool wait{true};
363-
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
364416
OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};
365417

366418
#ifdef _WIN32
367419
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
368-
*command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
420+
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
369421
"Invalid command quit with exit status code: 1");
370422
#else
371423
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
372-
*command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
373-
"Invalid command quit with exit status code: 127");
424+
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
425+
"Command not found with exit code: 127.");
374426
#endif
375-
CheckDescriptorEqInt(exitStat.get(), 404);
376427
CheckDescriptorEqStr(cmdMsg.get(), "No Change");
377428
}
378429

@@ -394,13 +445,10 @@ TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
394445
TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
395446
OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
396447
bool wait{false};
397-
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
398448
OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
399449

400450
EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
401-
*command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()));
402-
403-
CheckDescriptorEqInt(exitStat.get(), 404);
451+
*command.get(), wait, nullptr, nullptr, cmdMsg.get()));
404452
CheckDescriptorEqStr(cmdMsg.get(), "No change");
405453
}
406454

0 commit comments

Comments
 (0)