1818#include < cloysterhpc/services/options.h>
1919#include < cloysterhpc/services/scriptbuilder.h>
2020
21+ /* *
22+ * @brief Hold functions to execute commands inside shell. This is
23+ * usefull to have pipes, file descriptor, execute commands that
24+ * depends on /etc/profile.d/ files, etc.
25+ *
26+ * WARNING: THESE FUNCTIONS ARE VULNERABLE TO SHEL INJECTION
27+ *
28+ * While this is a concern in genenral, in this case (this project) the user
29+ * need root access to run the project so ... but do not COPY/PASTE this to
30+ * projects that run behind a network or you'll introduce an attack vector.
31+ */
2132namespace cloyster ::services::runner::shell {
2233
34+ /* *
35+ * @brief Hold "unsafe" shell functions. Unsafe here means that
36+ * the caller is responsible for checking the exit code, the
37+ * execution is not aborted if the command fails.
38+ */
2339namespace unsafe {
40+ /* *
41+ * @brief Executes a formatted shell command and captures its output.
42+ *
43+ * This function runs a shell command constructed from a format string
44+ * and arguments. The command is executed in a new `/bin/bash` process
45+ * with `-xelc` flags enabled:
46+ * - `-x`: print commands as they are executed (debugging).
47+ * - `-l`: load `/etc/profile.d/*` environment files.
48+ * - `-e`: exit on the first error.
49+ *
50+ * Behavior depends on runtime options:
51+ * - If `--dry-run` is enabled, the command is not executed, only logged.
52+ * - If `--debug` is enabled, both the executed command and its output
53+ * are logged line-by-line for troubleshooting.
54+ *
55+ * @tparam Args Variadic template parameter pack for formatting arguments.
56+ * @param output A vector to store each line of command output.
57+ * @param format The fmtlib format string for constructing the command.
58+ * @param args Arguments to format into the command string.
59+ * @return The exit code of the executed command, or 0 in dry-run mode.
60+ */
2461 template <typename ... Args>
2562 [[nodiscard]]
2663 int fmt (std::vector<std::string>& output,
@@ -32,7 +69,11 @@ namespace unsafe {
3269 if (!opts->dryRun ) {
3370 LOG_DEBUG (" Running shell command: {}" , command);
3471 boost::process::ipstream pipe_stream;
35- boost::process::child child (" /bin/bash" , " -xc" , command,
72+ // -x for debug
73+ // -l for loading /etc/profile.d/* files
74+ // -e for stopping in the first error, use (|| :) to ignore
75+ const std::string_view flags = opts->debug ? " -xelc" : " -elc" ;
76+ boost::process::child child (" /bin/bash" , flags, command,
3677 boost::process::std_out > pipe_stream);
3778
3879 std::string line;
@@ -50,6 +91,22 @@ namespace unsafe {
5091 }
5192 }
5293
94+ /* *
95+ * @brief Executes a formatted shell command without capturing output.
96+ *
97+ * Same as the overload above, but does not store the output lines. Output
98+ * is only streamed to logs.
99+ *
100+ * Behavior depends on runtime options:
101+ * - If `--dry-run` is enabled, the command is not executed, only logged.
102+ * - If `--debug` is enabled, both the executed command and its output
103+ * are logged line-by-line for troubleshooting.
104+ *
105+ * @tparam Args Variadic template parameter pack for formatting arguments.
106+ * @param format The fmtlib format string for constructing the command.
107+ * @param args Arguments to format into the command string.
108+ * @return The exit code of the executed command, or 0 in dry-run mode.
109+ */
53110 template <typename ... Args>
54111 [[nodiscard]]
55112 int fmt (fmt::format_string<Args...> format, Args&&... args)
@@ -59,7 +116,11 @@ namespace unsafe {
59116 if (!opts->dryRun ) {
60117 LOG_DEBUG (" Running shell command: {}" , command);
61118 boost::process::ipstream pipe_stream;
62- boost::process::child child (" /bin/bash" , " -xc" , command,
119+ // -x for debug
120+ // -l for loading /etc/profile.d/* files
121+ // -e for stopping in the first error, use (|| :) to ignore
122+ const std::string_view flags = opts->debug ? " -xelc" : " -elc" ;
123+ boost::process::child child (" /bin/bash" , flags, command,
63124 boost::process::std_out > pipe_stream);
64125
65126 std::string line;
@@ -77,6 +138,21 @@ namespace unsafe {
77138 }
78139}
79140
141+ /* *
142+ * @brief Executes a shell command and throws if it fails.
143+ *
144+ * A safer wrapper around `unsafe::fmt()`. Throws std::runtime_error if the
145+ * command returns a non-zero exit code.
146+ *
147+ * Behavior depends on runtime options:
148+ * - If `--dry-run` is enabled, the command is not executed, only logged.
149+ * - If `--debug` is enabled, the command and its output are logged.
150+ *
151+ * @tparam Args Variadic template parameter pack for formatting arguments.
152+ * @param format The fmtlib format string for constructing the command.
153+ * @param args Arguments to format into the command string.
154+ * @throws std::runtime_error if the command exits with a non-zero code.
155+ */
80156template <typename ... Args>
81157void fmt (fmt::format_string<Args...> format, Args&&... args)
82158{
@@ -89,8 +165,36 @@ void fmt(fmt::format_string<Args...> format, Args&&... args)
89165 }
90166}
91167
168+ /* *
169+ * @brief Executes a raw shell command string.
170+ *
171+ * Runs the provided command string directly without formatting. Throws if
172+ * execution fails.
173+ *
174+ * Behavior depends on runtime options:
175+ * - If `--dry-run` is enabled, the command is not executed, only logged.
176+ * - If `--debug` is enabled, the command and its output are logged.
177+ *
178+ * @param cmd The raw shell command string.
179+ */
92180void cmd (std::string_view cmd);
93181
182+ /* *
183+ * @brief Executes a shell command and returns its combined output.
184+ *
185+ * Runs a command and returns its stdout as a single string, with lines
186+ * joined by newlines. Throws if the command fails.
187+ *
188+ * Behavior depends on runtime options:
189+ * - If `--dry-run` is enabled, the command is not executed, only logged.
190+ * - If `--debug` is enabled, the command and its output are logged.
191+ *
192+ * @tparam Args Variadic template parameter pack for formatting arguments.
193+ * @param format The fmtlib format string for constructing the command.
194+ * @param args Arguments to format into the command string.
195+ * @return A string containing the full command output.
196+ * @throws std::runtime_error if the command exits with a non-zero code.
197+ */
94198template <typename ... Args>
95199[[nodiscard]]
96200std::string output (fmt::format_string<Args...> format, Args&&... args)
0 commit comments