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 SHELL 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 `-lc` flags enabled:
46+ * - `-l`: load `/etc/profile.d/` environment files.
47+ *
48+ * Behavior depends on runtime options:
49+ * - If `--dry-run` is enabled, the command is not executed, only logged.
50+ * are logged line-by-line for troubleshooting.
51+ *
52+ * @tparam Args Variadic template parameter pack for formatting arguments.
53+ * @param output A vector to store each line of command output.
54+ * @param format The fmtlib format string for constructing the command.
55+ * @param args Arguments to format into the command string.
56+ * @return The exit code of the executed command, or 0 in dry-run mode.
57+ */
2458 template <typename ... Args>
2559 [[nodiscard]]
2660 int fmt (std::vector<std::string>& output,
@@ -32,7 +66,8 @@ namespace unsafe {
3266 if (!opts->dryRun ) {
3367 LOG_DEBUG (" Running shell command: {}" , command);
3468 boost::process::ipstream pipe_stream;
35- boost::process::child child (" /bin/bash" , " -xc" , command,
69+ // -l for loading /etc/profile.d/* files
70+ boost::process::child child (" /bin/bash" , " -lc" , command,
3671 boost::process::std_out > pipe_stream);
3772
3873 std::string line;
@@ -50,6 +85,21 @@ namespace unsafe {
5085 }
5186 }
5287
88+ /* *
89+ * @brief Executes a formatted shell command without capturing output.
90+ *
91+ * Same as the overload above, but does not store the output lines. Output
92+ * is only streamed to logs.
93+ *
94+ * Behavior depends on runtime options:
95+ * - If `--dry-run` is enabled, the command is not executed, only logged.
96+ * are logged line-by-line for troubleshooting.
97+ *
98+ * @tparam Args Variadic template parameter pack for formatting arguments.
99+ * @param format The fmtlib format string for constructing the command.
100+ * @param args Arguments to format into the command string.
101+ * @return The exit code of the executed command, or 0 in dry-run mode.
102+ */
53103 template <typename ... Args>
54104 [[nodiscard]]
55105 int fmt (fmt::format_string<Args...> format, Args&&... args)
@@ -59,7 +109,9 @@ namespace unsafe {
59109 if (!opts->dryRun ) {
60110 LOG_DEBUG (" Running shell command: {}" , command);
61111 boost::process::ipstream pipe_stream;
62- boost::process::child child (" /bin/bash" , " -xc" , command,
112+ // -x for debug
113+ // -l for loading /etc/profile.d/* files
114+ boost::process::child child (" /bin/bash" , " -lc" , command,
63115 boost::process::std_out > pipe_stream);
64116
65117 std::string line;
@@ -77,6 +129,20 @@ namespace unsafe {
77129 }
78130}
79131
132+ /* *
133+ * @brief Executes a shell command and throws if it fails.
134+ *
135+ * A safer wrapper around `unsafe::fmt()`. Throws std::runtime_error if the
136+ * command returns a non-zero exit code.
137+ *
138+ * Behavior depends on runtime options:
139+ * - If `--dry-run` is enabled, the command is not executed, only logged.
140+ *
141+ * @tparam Args Variadic template parameter pack for formatting arguments.
142+ * @param format The fmtlib format string for constructing the command.
143+ * @param args Arguments to format into the command string.
144+ * @throws std::runtime_error if the command exits with a non-zero code.
145+ */
80146template <typename ... Args>
81147void fmt (fmt::format_string<Args...> format, Args&&... args)
82148{
@@ -89,8 +155,34 @@ void fmt(fmt::format_string<Args...> format, Args&&... args)
89155 }
90156}
91157
158+ /* *
159+ * @brief Executes a raw shell command string.
160+ *
161+ * Runs the provided command string directly without formatting. Throws if
162+ * execution fails.
163+ *
164+ * Behavior depends on runtime options:
165+ * - If `--dry-run` is enabled, the command is not executed, only logged.
166+ *
167+ * @param cmd The raw shell command string.
168+ */
92169void cmd (std::string_view cmd);
93170
171+ /* *
172+ * @brief Executes a shell command and returns its combined output.
173+ *
174+ * Runs a command and returns its stdout as a single string, with lines
175+ * joined by newlines. Throws if the command fails.
176+ *
177+ * Behavior depends on runtime options:
178+ * - If `--dry-run` is enabled, the command is not executed, only logged.
179+ *
180+ * @tparam Args Variadic template parameter pack for formatting arguments.
181+ * @param format The fmtlib format string for constructing the command.
182+ * @param args Arguments to format into the command string.
183+ * @return A string containing the full command output.
184+ * @throws std::runtime_error if the command exits with a non-zero code.
185+ */
94186template <typename ... Args>
95187[[nodiscard]]
96188std::string output (fmt::format_string<Args...> format, Args&&... args)
0 commit comments