Skip to content

Commit c6d4ac0

Browse files
committed
add cmdstat and cmdmsg for common linux exit code 1 126 127
1 parent 5344dc5 commit c6d4ac0

File tree

3 files changed

+98
-54
lines changed

3 files changed

+98
-54
lines changed

flang/docs/Intrinsics.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -893,14 +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-
- 4: Signal error (either stopped or killed by signal, occurs only on POSIX-compatible systems).
903-
- 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).
904907
- Asynchronous execution:
905908
- 0 will always be assigned.
906909

flang/runtime/execute.cpp

Lines changed: 70 additions & 29 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
@@ -67,37 +72,43 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
6772
#ifdef _WIN32
6873
// On WIN32 API std::system returns exit status directly
6974
std::int64_t exitStatusVal{status};
75+
if (exitStatusVal != 0) {
76+
if (!cmdstat) {
77+
terminator.Crash(
78+
"Invalid command quit with exit status code: %d", exitStatusVal);
79+
} else {
80+
StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
81+
CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
82+
}
83+
}
7084
#else
7185
std::int64_t exitStatusVal{WEXITSTATUS(status)};
7286
if (exitStatusVal == 1) {
7387
if (!cmdstat) {
74-
terminator.Crash("General Error with exit status code: 1");
88+
terminator.Crash("Command line execution failed with exit code: 1.");
7589
} else {
76-
StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
77-
CheckAndCopyCharsToDescriptor(cmdmsg,
78-
"General Error: a catch-all exit code for a variety of general "
79-
"errors.");
90+
StoreIntToDescriptor(cmdstat, COMMAND_EXECUTION_ERR, terminator);
91+
CheckAndCopyCharsToDescriptor(
92+
cmdmsg, "Command line execution failed with exit code: 1.");
8093
}
8194
} else if (exitStatusVal == 126) {
8295
if (!cmdstat) {
83-
terminator.Crash("Command cannot execute with exit status code: 126");
96+
terminator.Crash("Command cannot be executed with exit code: 126.");
8497
} else {
85-
StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
86-
CheckAndCopyCharsToDescriptor(cmdmsg,
87-
"Command cannot execute: command was found, but it could not be "
88-
"executed.");
98+
StoreIntToDescriptor(cmdstat, COMMAND_CANNOT_EXECUTE_ERR, terminator);
99+
CheckAndCopyCharsToDescriptor(
100+
cmdmsg, "Command cannot be executed with exit code: 126.");
89101
}
90102
} else if (exitStatusVal == 127) {
91103
if (!cmdstat) {
92-
terminator.Crash("Command not found with exit status code: 127");
104+
terminator.Crash("Command not found with exit code: 127.");
93105
} else {
94-
StoreIntToDescriptor(cmdstat, INVALID_CL_ERR, terminator);
95-
CheckAndCopyCharsToDescriptor(cmdmsg,
96-
"Command not found: command was not found in the system's PATH");
106+
StoreIntToDescriptor(cmdstat, COMMAND_NOT_FOUND_ERR, terminator);
107+
CheckAndCopyCharsToDescriptor(
108+
cmdmsg, "Command not found with exit code: 127.");
97109
}
98-
} else
99-
#endif
100-
if (exitStatusVal != 0) {
110+
// capture all other nonzero exit code
111+
} else if (exitStatusVal != 0) {
101112
if (!cmdstat) {
102113
terminator.Crash(
103114
"Invalid command quit with exit status code: %d", exitStatusVal);
@@ -106,13 +117,43 @@ std::int64_t TerminationCheck(std::int64_t status, const Descriptor *cmdstat,
106117
CheckAndCopyCharsToDescriptor(cmdmsg, "Invalid command line");
107118
}
108119
}
120+
#endif
109121

122+
// On both Windows and Linux, errno is set when system returns -1.
110123
if (status == -1) {
111-
if (!cmdstat) {
112-
terminator.Crash("Execution error with system status code: %d", status);
124+
// On Windows, ENOENT means the command interpreter can't be found.
125+
// On Linux, system calls execl with filepath "/bin/sh", ENOENT means the
126+
// file pathname does not exist.
127+
if (errno = ENOENT) {
128+
if (!cmdstat) {
129+
terminator.Crash("Command line execution is not supported, system "
130+
"returns -1 with errno ENOENT.");
131+
} else {
132+
StoreIntToDescriptor(cmdstat, NO_SUPPORT_ERR, terminator);
133+
CheckAndCopyCharsToDescriptor(cmdmsg,
134+
"Command line execution is not supported, system returns -1 with "
135+
"errno ENOENT.");
136+
}
113137
} else {
114-
StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
115-
CheckAndCopyCharsToDescriptor(cmdmsg, "Execution error");
138+
char err_buffer[30];
139+
char msg[]{"Execution error with system status code: -1, errno: "};
140+
#ifdef _WIN32
141+
if (strerror_s(err_buffer, sizeof(err_buffer), errno) != 0)
142+
#else
143+
if (strerror_r(errno, err_buffer, sizeof(err_buffer)) != 0)
144+
#endif
145+
terminator.Crash("errno to char errno failed.");
146+
char *newMsg{static_cast<char *>(AllocateMemoryOrCrash(
147+
terminator, std::strlen(msg) + std::strlen(err_buffer) + 1))};
148+
std::strcat(newMsg, err_buffer);
149+
150+
if (!cmdstat) {
151+
terminator.Crash(newMsg);
152+
} else {
153+
StoreIntToDescriptor(cmdstat, EXECL_ERR, terminator);
154+
CheckAndCopyCharsToDescriptor(cmdmsg, newMsg);
155+
}
156+
FreeMemory(newMsg);
116157
}
117158
}
118159

@@ -203,7 +244,7 @@ void RTNAME(ExecuteCommandLine)(const Descriptor &command, bool wait,
203244
terminator.Crash(
204245
"CreateProcess failed with error code: %lu.", GetLastError());
205246
} else {
206-
StoreIntToDescriptor(cmdstat, (std::int64_t)GetLastError(), terminator);
247+
StoreIntToDescriptor(cmdstat, ASYNC_NO_SUPPORT_ERR, terminator);
207248
CheckAndCopyCharsToDescriptor(cmdmsg, "CreateProcess failed.");
208249
}
209250
}

flang/unittests/Runtime/CommandTest.cpp

Lines changed: 18 additions & 18 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)
@@ -344,18 +344,19 @@ TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {
344344
bool wait{true};
345345
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
346346
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
347-
OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};
347+
OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXXXXXXXX")};
348348

349349
RTNAME(ExecuteCommandLine)
350350
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
351351
#ifdef _WIN32
352352
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
353-
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
353+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
354+
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX");
354355
#else
355356
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
356-
CheckDescriptorEqStr(cmdMsg.get(), "General Error: a catch-");
357-
#endif
358357
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
358+
CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed");
359+
#endif
359360
}
360361

361362
TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
@@ -371,12 +372,13 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
371372
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
372373
#ifdef _WIN32
373374
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
375+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
374376
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
375377
#else
376378
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
377-
CheckDescriptorEqStr(cmdMsg.get(), "Command cannot execute:");
379+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
380+
CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");
378381
#endif
379-
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
380382

381383
// removing the file should have no error on Linux, have error on Windows
382384
OwningPtr<Descriptor> commandClean{
@@ -387,7 +389,7 @@ TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
387389
#ifdef _WIN32
388390
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
389391
CheckDescriptorEqStr(cmdMsgNoErr.get(), "Invalid ");
390-
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
392+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
391393
#else
392394
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
393395
CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
@@ -400,21 +402,19 @@ TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
400402
bool wait{true};
401403
OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
402404
OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
403-
OwningPtr<Descriptor> cmdMsg{CharDescriptor(
404-
"cmdmsg will not modify the remaining buffer XXXXXXXXXXXXXXXXXXXX")};
405+
OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")};
405406

406407
RTNAME(ExecuteCommandLine)
407408
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
408409
#ifdef _WIN32
409410
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
410-
CheckDescriptorEqStr(cmdMsg.get(),
411-
"Invalid command linefy the remaining buffer XXXXXXXXXXXXXXXXXXXX");
411+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
412+
CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX");
412413
#else
413414
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
414-
CheckDescriptorEqStr(cmdMsg.get(),
415-
"Command not found: command was not found in the system's PATHXXX");
415+
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
416+
CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit");
416417
#endif
417-
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
418418
}
419419

420420
TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
@@ -429,7 +429,7 @@ TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
429429
#else
430430
EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
431431
*command.get(), wait, nullptr, nullptr, cmdMsg.get()),
432-
"Command not found with exit status code: 127");
432+
"Command not found with exit code: 127.");
433433
#endif
434434
CheckDescriptorEqStr(cmdMsg.get(), "No Change");
435435
}
@@ -444,7 +444,7 @@ TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
444444
RTNAME(ExecuteCommandLine)
445445
(*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
446446

447-
CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 404);
447+
CheckDescriptorEqInt(exitStat.get(), 404);
448448
CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
449449
CheckDescriptorEqStr(cmdMsg.get(), "No change");
450450
}

0 commit comments

Comments
 (0)