blob: 33b0dc9c5430223e8547eb774e8c5f593d8e1d00 [file] [log] [blame]
James Kuszmaul3224b8e2022-01-07 19:00:39 -08001#ifndef AOS_STARTER_SUBPROCESS_H_
2#define AOS_STARTER_SUBPROCESS_H_
3
Stephan Pleinesf581a072024-05-23 20:59:27 -07004#include <stdint.h>
5#include <sys/signalfd.h>
6#include <sys/types.h>
7
8#include <algorithm>
9#include <chrono>
10#include <filesystem> // IWYU pragma: keep
11#include <functional>
12#include <initializer_list>
James Kuszmauld42edb42022-01-07 18:00:16 -080013#include <memory>
Stephan Pleinesf581a072024-05-23 20:59:27 -070014#include <optional>
James Kuszmaul3224b8e2022-01-07 19:00:39 -080015#include <string>
Stephan Pleinesf581a072024-05-23 20:59:27 -070016#include <string_view>
17#include <utility>
James Kuszmaul3224b8e2022-01-07 19:00:39 -080018#include <vector>
19
Stephan Pleinesf581a072024-05-23 20:59:27 -070020#include "flatbuffers/buffer.h"
21#include "flatbuffers/flatbuffer_builder.h"
22#include "flatbuffers/string.h"
23#include "flatbuffers/vector.h"
24
25#include "aos/configuration_generated.h"
26#include "aos/events/epoll.h"
James Kuszmaul3224b8e2022-01-07 19:00:39 -080027#include "aos/events/event_loop.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070028#include "aos/events/event_loop_generated.h"
James Kuszmaul3224b8e2022-01-07 19:00:39 -080029#include "aos/events/shm_event_loop.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070030#include "aos/ipc_lib/signalfd.h"
31#include "aos/macros.h"
James Kuszmaul3224b8e2022-01-07 19:00:39 -080032#include "aos/starter/starter_generated.h"
33#include "aos/starter/starter_rpc_generated.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070034#include "aos/time/time.h"
James Kuszmaul3224b8e2022-01-07 19:00:39 -080035#include "aos/util/scoped_pipe.h"
James Kuszmaul6295a642022-03-22 15:23:59 -070036#include "aos/util/top.h"
James Kuszmaul3224b8e2022-01-07 19:00:39 -080037
38namespace aos::starter {
39
James Kuszmaul37a56af2023-07-29 15:15:16 -070040// Replicates the path resolution that will be attempted by the shell or
41// commands like execvp. Doing this manually allows us to conveniently know what
42// is actually being executed (rather than, e.g., querying /proc/$pid/exe after
43// the execvp() call is executed).
44// This is also useful when using the below class with sudo or bash scripts,
45// because in those circumstances /proc/$pid/exe contains sudo and /bin/bash (or
46// similar binary), rather than the actual thing being executed.
47std::filesystem::path ResolvePath(std::string_view command);
48
James Kuszmaul3224b8e2022-01-07 19:00:39 -080049// Registers a signalfd listener with the given event loop and calls callback
50// whenever a signal is received.
51class SignalListener {
52 public:
53 SignalListener(aos::ShmEventLoop *loop,
54 std::function<void(signalfd_siginfo)> callback);
Austin Schuh1cea9032023-07-10 11:56:40 -070055 SignalListener(aos::internal::EPoll *epoll,
56 std::function<void(signalfd_siginfo)> callback);
James Kuszmaul3224b8e2022-01-07 19:00:39 -080057 SignalListener(aos::ShmEventLoop *loop,
58 std::function<void(signalfd_siginfo)> callback,
59 std::initializer_list<unsigned int> signals);
Austin Schuh1cea9032023-07-10 11:56:40 -070060 SignalListener(aos::internal::EPoll *epoll,
61 std::function<void(signalfd_siginfo)> callback,
62 std::initializer_list<unsigned int> signals);
James Kuszmaul3224b8e2022-01-07 19:00:39 -080063
64 ~SignalListener();
65
66 private:
Austin Schuh1cea9032023-07-10 11:56:40 -070067 aos::internal::EPoll *epoll_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -080068 std::function<void(signalfd_siginfo)> callback_;
69 aos::ipc_lib::SignalFd signalfd_;
70
71 DISALLOW_COPY_AND_ASSIGN(SignalListener);
72};
73
Austin Schuhbbeb37e2022-08-17 16:19:27 -070074// Class to use the V1 cgroup API to limit memory usage.
75class MemoryCGroup {
76 public:
Austin Schuh77e20a32023-08-01 12:25:03 -070077 // Enum to control if MemoryCGroup should create the cgroup and remove it on
78 // its own, or if it should assume it already exists and just use it.
79 enum class Create {
80 kDoCreate,
81 kDoNotCreate,
82 };
83
84 MemoryCGroup(std::string_view name, Create should_create = Create::kDoCreate);
Austin Schuhbbeb37e2022-08-17 16:19:27 -070085 ~MemoryCGroup();
86
87 // Adds a thread ID to be managed by the cgroup.
88 void AddTid(pid_t pid = 0);
89
90 // Sets the provided limit to the provided value.
91 void SetLimit(std::string_view limit_name, uint64_t limit_value);
92
93 private:
94 std::string cgroup_;
Austin Schuh77e20a32023-08-01 12:25:03 -070095 Create should_create_;
Austin Schuhbbeb37e2022-08-17 16:19:27 -070096};
97
James Kuszmaul3224b8e2022-01-07 19:00:39 -080098// Manages a running process, allowing starting and stopping, and restarting
99// automatically.
100class Application {
101 public:
Philipp Schrader595979d2023-09-13 11:31:48 -0700102 enum class QuietLogging {
103 kYes,
104 kNo,
105 // For debugging child processes not behaving as expected. When a child
106 // experiences an event such as exiting with an error code or dying to due a
107 // signal, this option will cause a log statement to be printed.
108 kNotForDebugging,
109 };
James Kuszmauld42edb42022-01-07 18:00:16 -0800110 Application(const aos::Application *application, aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700111 std::function<void()> on_change,
112 QuietLogging quiet_flag = QuietLogging::kNo);
James Kuszmauld42edb42022-01-07 18:00:16 -0800113
Sarah Newman2c1b1212022-08-10 10:05:48 -0700114 // executable_name is the actual executable path.
115 // When sudo is not used, name is used as argv[0] when exec'ing
116 // executable_name. When sudo is used it's not possible to pass in a
117 // distinct argv[0].
James Kuszmauld42edb42022-01-07 18:00:16 -0800118 Application(std::string_view name, std::string_view executable_name,
payton.rehl2841b1c2023-05-25 17:23:55 -0700119 aos::EventLoop *event_loop, std::function<void()> on_change,
120 QuietLogging quiet_flag = QuietLogging::kNo);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800121
Adam Snaider70deaf22023-08-11 13:58:34 -0700122 ~Application();
123
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800124 flatbuffers::Offset<aos::starter::ApplicationStatus> PopulateStatus(
James Kuszmaul6295a642022-03-22 15:23:59 -0700125 flatbuffers::FlatBufferBuilder *builder, util::Top *top);
James Kuszmauld42edb42022-01-07 18:00:16 -0800126 aos::starter::State status() const { return status_; };
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800127
128 // Returns the last pid of this process. -1 if not started yet.
129 pid_t get_pid() const { return pid_; }
130
131 // Handles a SIGCHLD signal received by the parent. Does nothing if this
132 // process was not the target. Returns true if this Application should be
133 // removed.
134 bool MaybeHandleSignal();
James Kuszmauld42edb42022-01-07 18:00:16 -0800135 void DisableChildDeathPolling() { child_status_handler_->Disable(); }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800136
137 // Handles a command. May do nothing if application is already in the desired
138 // state.
139 void HandleCommand(aos::starter::Command cmd);
140
141 void Start() { HandleCommand(aos::starter::Command::START); }
142
Sanjay Narayanan92fdc3d2023-08-25 14:42:56 -0700143 // Stops the command by sending a SIGINT first, followed by a SIGKILL if it's
144 // still alive in 1s.
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800145 void Stop() { HandleCommand(aos::starter::Command::STOP); }
146
Sanjay Narayanan92fdc3d2023-08-25 14:42:56 -0700147 // Stops the command the same way as Stop() does, but updates internal state
148 // to reflect that the application was terminated.
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800149 void Terminate();
150
Austin Schuh1cea9032023-07-10 11:56:40 -0700151 // Adds a callback which gets notified when the application changes state.
152 // This is in addition to any existing callbacks and doesn't replace any of
153 // them.
154 void AddOnChange(std::function<void()> fn) {
155 on_change_.emplace_back(std::move(fn));
156 }
157
James Kuszmauld42edb42022-01-07 18:00:16 -0800158 void set_args(std::vector<std::string> args);
159 void set_capture_stdout(bool capture);
160 void set_capture_stderr(bool capture);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700161 void set_run_as_sudo(bool value) { run_as_sudo_ = value; }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800162
Philipp Schraderc8e779e2024-01-25 16:32:39 -0800163 // Sets the time for a process to stop gracefully. If an application is asked
164 // to stop, but doesn't stop within the specified time limit, then it is
165 // forcefully killed. Defaults to 1 second unless overridden by the
166 // aos::Application instance in the constructor.
167 void set_stop_grace_period(std::chrono::nanoseconds stop_grace_period) {
168 stop_grace_period_ = stop_grace_period;
169 }
170
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800171 bool autostart() const { return autostart_; }
172
173 bool autorestart() const { return autorestart_; }
Adam Snaider70deaf22023-08-11 13:58:34 -0700174 void set_autorestart(bool autorestart) { autorestart_ = autorestart; }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800175
Philipp Schrader595979d2023-09-13 11:31:48 -0700176 LastStopReason stop_reason() const { return stop_reason_; }
177
James Kuszmauld42edb42022-01-07 18:00:16 -0800178 const std::string &GetStdout();
179 const std::string &GetStderr();
180 std::optional<int> exit_code() const { return exit_code_; }
181
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700182 // Sets the memory limit for the application to the provided limit.
183 void SetMemoryLimit(size_t limit) {
184 if (!memory_cgroup_) {
185 memory_cgroup_ = std::make_unique<MemoryCGroup>(name_);
186 }
187 memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
188 }
189
Austin Schuh77e20a32023-08-01 12:25:03 -0700190 // Sets the cgroup and memory limit to a pre-existing cgroup which is
191 // externally managed. This lets us configure the cgroup of an application
192 // without root access.
193 void SetExistingCgroupMemoryLimit(std::string_view name, size_t limit) {
194 if (!memory_cgroup_) {
195 memory_cgroup_ = std::make_unique<MemoryCGroup>(
196 name, MemoryCGroup::Create::kDoNotCreate);
197 }
198 memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
199 }
200
James Kuszmaul8544c492023-07-31 15:00:38 -0700201 // Observe a timing report message, and save it if it is relevant to us.
202 // It is the responsibility of the caller to manage this, because the lifetime
203 // of the Application itself is such that it cannot own Fetchers readily.
204 void ObserveTimingReport(const aos::monotonic_clock::time_point send_time,
205 const aos::timing::Report *msg);
206
James Kuszmaul37a56af2023-07-29 15:15:16 -0700207 FileState UpdateFileState();
208
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800209 private:
James Kuszmauld42edb42022-01-07 18:00:16 -0800210 typedef aos::util::ScopedPipe::PipePair PipePair;
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700211
Philipp Schrader790cb542023-07-05 21:06:52 -0700212 static constexpr const char *const kSudo{"sudo"};
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700213
James Kuszmauld42edb42022-01-07 18:00:16 -0800214 void set_args(
215 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
216 &args);
217
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800218 void DoStart();
219
220 void DoStop(bool restart);
221
222 void QueueStart();
223
Austin Schuh1cea9032023-07-10 11:56:40 -0700224 void OnChange();
225
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800226 // Copy flatbuffer vector of strings to vector of std::string.
227 static std::vector<std::string> FbsVectorToVector(
228 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v);
229
230 static std::optional<uid_t> FindUid(const char *name);
231 static std::optional<gid_t> FindPrimaryGidForUser(const char *name);
232
James Kuszmauld42edb42022-01-07 18:00:16 -0800233 void FetchOutputs();
234
235 // Provides an std::vector of the args (such that CArgs().data() ends up being
236 // suitable to pass to execve()).
237 // The points are invalidated when args_ changes (e.g., due to a set_args
238 // call).
239 std::vector<char *> CArgs();
240
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800241 // Next unique id for all applications
242 static inline uint64_t next_id_ = 0;
243
244 std::string name_;
James Kuszmaul37a56af2023-07-29 15:15:16 -0700245 std::filesystem::path path_;
246 // Inode of path_ immediately prior to the most recent fork() call.
247 ino_t pre_fork_inode_;
248 FileState file_state_ = FileState::NOT_RUNNING;
James Kuszmauld42edb42022-01-07 18:00:16 -0800249 std::vector<std::string> args_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800250 std::string user_name_;
251 std::optional<uid_t> user_;
252 std::optional<gid_t> group_;
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700253 bool run_as_sudo_ = false;
Philipp Schraderc8e779e2024-01-25 16:32:39 -0800254 std::chrono::nanoseconds stop_grace_period_ = std::chrono::seconds(1);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800255
James Kuszmauld42edb42022-01-07 18:00:16 -0800256 bool capture_stdout_ = false;
257 PipePair stdout_pipes_;
258 std::string stdout_;
259 bool capture_stderr_ = false;
260 PipePair stderr_pipes_;
261 std::string stderr_;
262
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800263 pid_t pid_ = -1;
James Kuszmauld42edb42022-01-07 18:00:16 -0800264 PipePair status_pipes_;
265 uint64_t id_ = 0;
266 std::optional<int> exit_code_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800267 aos::monotonic_clock::time_point start_time_, exit_time_;
268 bool queue_restart_ = false;
269 bool terminating_ = false;
James Kuszmauld42edb42022-01-07 18:00:16 -0800270 bool autostart_ = false;
271 bool autorestart_ = false;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800272
273 aos::starter::State status_ = aos::starter::State::STOPPED;
274 aos::starter::LastStopReason stop_reason_ =
275 aos::starter::LastStopReason::STOP_REQUESTED;
276
277 aos::EventLoop *event_loop_;
James Kuszmauld42edb42022-01-07 18:00:16 -0800278 aos::TimerHandler *start_timer_, *restart_timer_, *stop_timer_, *pipe_timer_,
279 *child_status_handler_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800280
James Kuszmaul8544c492023-07-31 15:00:38 -0700281 // Version string from the most recent valid timing report for this
282 // application. Cleared when the application restarts.
283 std::optional<std::string> latest_timing_report_version_;
284 aos::monotonic_clock::time_point last_timing_report_ =
285 aos::monotonic_clock::min_time;
286
Austin Schuh1cea9032023-07-10 11:56:40 -0700287 std::vector<std::function<void()>> on_change_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800288
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700289 std::unique_ptr<MemoryCGroup> memory_cgroup_;
290
payton.rehl2841b1c2023-05-25 17:23:55 -0700291 QuietLogging quiet_flag_ = QuietLogging::kNo;
292
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800293 DISALLOW_COPY_AND_ASSIGN(Application);
294};
295
296} // namespace aos::starter
James Kuszmauld42edb42022-01-07 18:00:16 -0800297#endif // AOS_STARTER_SUBPROCESS_H_