Merge pull request #4892 from khaneliman/bugfix/stab-004-command-exec-failure

fix(command): return non-zero when child exec fails
This commit is contained in:
Alexis Rouillard
2026-03-04 22:39:31 +01:00
committed by GitHub
3 changed files with 65 additions and 2 deletions

View File

@@ -20,6 +20,8 @@ extern std::list<pid_t> reap;
namespace waybar::util::command { namespace waybar::util::command {
constexpr int kExecFailureExitCode = 127;
struct res { struct res {
int exit_code; int exit_code;
std::string out; std::string out;
@@ -114,7 +116,9 @@ inline FILE* open(const std::string& cmd, int& pid, const std::string& output_na
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1); setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
} }
execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); execlp("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0); const int saved_errno = errno;
spdlog::error("execlp(/bin/sh) failed in open: {}", strerror(saved_errno));
_exit(kExecFailureExitCode);
} else { } else {
::close(fd[1]); ::close(fd[1]);
} }
@@ -162,7 +166,9 @@ inline int32_t forkExec(const std::string& cmd, const std::string& output_name)
setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1); setenv("WAYBAR_OUTPUT_NAME", output_name.c_str(), 1);
} }
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0); execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
exit(0); const int saved_errno = errno;
spdlog::error("execl(/bin/sh) failed in forkExec: {}", strerror(saved_errno));
_exit(kExecFailureExitCode);
} else { } else {
reap_mtx.lock(); reap_mtx.lock();
reap.push_back(pid); reap.push_back(pid);

56
test/utils/command.cpp Normal file
View File

@@ -0,0 +1,56 @@
#if __has_include(<catch2/catch_test_macros.hpp>)
#include <catch2/catch_test_macros.hpp>
#else
#include <catch2/catch.hpp>
#endif
#include <cerrno>
#include <list>
#include <mutex>
#include <sys/wait.h>
#include <unistd.h>
std::mutex reap_mtx;
std::list<pid_t> reap;
extern "C" int waybar_test_execl(const char* path, const char* arg, ...);
extern "C" int waybar_test_execlp(const char* file, const char* arg, ...);
#define execl waybar_test_execl
#define execlp waybar_test_execlp
#include "util/command.hpp"
#undef execl
#undef execlp
extern "C" int waybar_test_execl(const char* path, const char* arg, ...) {
(void)path;
(void)arg;
errno = ENOENT;
return -1;
}
extern "C" int waybar_test_execlp(const char* file, const char* arg, ...) {
(void)file;
(void)arg;
errno = ENOENT;
return -1;
}
TEST_CASE("command::execNoRead returns 127 when shell exec fails", "[util][command]") {
const auto result = waybar::util::command::execNoRead("echo should-not-run");
REQUIRE(result.exit_code == waybar::util::command::kExecFailureExitCode);
REQUIRE(result.out.empty());
}
TEST_CASE("command::forkExec child exits 127 when shell exec fails", "[util][command]") {
const auto pid = waybar::util::command::forkExec("echo should-not-run", "test-output");
REQUIRE(pid > 0);
int status = -1;
REQUIRE(waitpid(pid, &status, 0) == pid);
REQUIRE(WIFEXITED(status));
REQUIRE(WEXITSTATUS(status) == waybar::util::command::kExecFailureExitCode);
std::scoped_lock<std::mutex> lock(reap_mtx);
reap.remove(pid);
}

View File

@@ -14,6 +14,7 @@ test_src = files(
'JsonParser.cpp', 'JsonParser.cpp',
'SafeSignal.cpp', 'SafeSignal.cpp',
'sleeper_thread.cpp', 'sleeper_thread.cpp',
'command.cpp',
'css_reload_helper.cpp', 'css_reload_helper.cpp',
'../../src/util/css_reload_helper.cpp', '../../src/util/css_reload_helper.cpp',
) )