blob: eb368749f970e5e17faba3531b6dd15147551a07 [file] [log] [blame]
James Kuszmaul3224b8e2022-01-07 19:00:39 -08001#ifndef AOS_STARTER_SUBPROCESS_H_
2#define AOS_STARTER_SUBPROCESS_H_
3
James Kuszmaul37a56af2023-07-29 15:15:16 -07004#include <filesystem>
James Kuszmauld42edb42022-01-07 18:00:16 -08005#include <memory>
James Kuszmaul3224b8e2022-01-07 19:00:39 -08006#include <string>
James Kuszmauld42edb42022-01-07 18:00:16 -08007#include <tuple>
James Kuszmaul3224b8e2022-01-07 19:00:39 -08008#include <vector>
9
10#include "aos/events/event_loop.h"
11#include "aos/events/shm_event_loop.h"
12#include "aos/starter/starter_generated.h"
13#include "aos/starter/starter_rpc_generated.h"
14#include "aos/util/scoped_pipe.h"
James Kuszmaul6295a642022-03-22 15:23:59 -070015#include "aos/util/top.h"
James Kuszmaul3224b8e2022-01-07 19:00:39 -080016
17namespace aos::starter {
18
James Kuszmaul37a56af2023-07-29 15:15:16 -070019// Replicates the path resolution that will be attempted by the shell or
20// commands like execvp. Doing this manually allows us to conveniently know what
21// is actually being executed (rather than, e.g., querying /proc/$pid/exe after
22// the execvp() call is executed).
23// This is also useful when using the below class with sudo or bash scripts,
24// because in those circumstances /proc/$pid/exe contains sudo and /bin/bash (or
25// similar binary), rather than the actual thing being executed.
26std::filesystem::path ResolvePath(std::string_view command);
27
James Kuszmaul3224b8e2022-01-07 19:00:39 -080028// Registers a signalfd listener with the given event loop and calls callback
29// whenever a signal is received.
30class SignalListener {
31 public:
32 SignalListener(aos::ShmEventLoop *loop,
33 std::function<void(signalfd_siginfo)> callback);
Austin Schuh1cea9032023-07-10 11:56:40 -070034 SignalListener(aos::internal::EPoll *epoll,
35 std::function<void(signalfd_siginfo)> callback);
James Kuszmaul3224b8e2022-01-07 19:00:39 -080036 SignalListener(aos::ShmEventLoop *loop,
37 std::function<void(signalfd_siginfo)> callback,
38 std::initializer_list<unsigned int> signals);
Austin Schuh1cea9032023-07-10 11:56:40 -070039 SignalListener(aos::internal::EPoll *epoll,
40 std::function<void(signalfd_siginfo)> callback,
41 std::initializer_list<unsigned int> signals);
James Kuszmaul3224b8e2022-01-07 19:00:39 -080042
43 ~SignalListener();
44
45 private:
Austin Schuh1cea9032023-07-10 11:56:40 -070046 aos::internal::EPoll *epoll_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -080047 std::function<void(signalfd_siginfo)> callback_;
48 aos::ipc_lib::SignalFd signalfd_;
49
50 DISALLOW_COPY_AND_ASSIGN(SignalListener);
51};
52
Austin Schuhbbeb37e2022-08-17 16:19:27 -070053// Class to use the V1 cgroup API to limit memory usage.
54class MemoryCGroup {
55 public:
Austin Schuh77e20a32023-08-01 12:25:03 -070056 // Enum to control if MemoryCGroup should create the cgroup and remove it on
57 // its own, or if it should assume it already exists and just use it.
58 enum class Create {
59 kDoCreate,
60 kDoNotCreate,
61 };
62
63 MemoryCGroup(std::string_view name, Create should_create = Create::kDoCreate);
Austin Schuhbbeb37e2022-08-17 16:19:27 -070064 ~MemoryCGroup();
65
66 // Adds a thread ID to be managed by the cgroup.
67 void AddTid(pid_t pid = 0);
68
69 // Sets the provided limit to the provided value.
70 void SetLimit(std::string_view limit_name, uint64_t limit_value);
71
72 private:
73 std::string cgroup_;
Austin Schuh77e20a32023-08-01 12:25:03 -070074 Create should_create_;
Austin Schuhbbeb37e2022-08-17 16:19:27 -070075};
76
James Kuszmaul3224b8e2022-01-07 19:00:39 -080077// Manages a running process, allowing starting and stopping, and restarting
78// automatically.
79class Application {
80 public:
Philipp Schrader595979d2023-09-13 11:31:48 -070081 enum class QuietLogging {
82 kYes,
83 kNo,
84 // For debugging child processes not behaving as expected. When a child
85 // experiences an event such as exiting with an error code or dying to due a
86 // signal, this option will cause a log statement to be printed.
87 kNotForDebugging,
88 };
James Kuszmauld42edb42022-01-07 18:00:16 -080089 Application(const aos::Application *application, aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -070090 std::function<void()> on_change,
91 QuietLogging quiet_flag = QuietLogging::kNo);
James Kuszmauld42edb42022-01-07 18:00:16 -080092
Sarah Newman2c1b1212022-08-10 10:05:48 -070093 // executable_name is the actual executable path.
94 // When sudo is not used, name is used as argv[0] when exec'ing
95 // executable_name. When sudo is used it's not possible to pass in a
96 // distinct argv[0].
James Kuszmauld42edb42022-01-07 18:00:16 -080097 Application(std::string_view name, std::string_view executable_name,
payton.rehl2841b1c2023-05-25 17:23:55 -070098 aos::EventLoop *event_loop, std::function<void()> on_change,
99 QuietLogging quiet_flag = QuietLogging::kNo);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800100
Adam Snaider70deaf22023-08-11 13:58:34 -0700101 ~Application();
102
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800103 flatbuffers::Offset<aos::starter::ApplicationStatus> PopulateStatus(
James Kuszmaul6295a642022-03-22 15:23:59 -0700104 flatbuffers::FlatBufferBuilder *builder, util::Top *top);
James Kuszmauld42edb42022-01-07 18:00:16 -0800105 aos::starter::State status() const { return status_; };
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800106
107 // Returns the last pid of this process. -1 if not started yet.
108 pid_t get_pid() const { return pid_; }
109
110 // Handles a SIGCHLD signal received by the parent. Does nothing if this
111 // process was not the target. Returns true if this Application should be
112 // removed.
113 bool MaybeHandleSignal();
James Kuszmauld42edb42022-01-07 18:00:16 -0800114 void DisableChildDeathPolling() { child_status_handler_->Disable(); }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800115
116 // Handles a command. May do nothing if application is already in the desired
117 // state.
118 void HandleCommand(aos::starter::Command cmd);
119
120 void Start() { HandleCommand(aos::starter::Command::START); }
121
Sanjay Narayanan92fdc3d2023-08-25 14:42:56 -0700122 // Stops the command by sending a SIGINT first, followed by a SIGKILL if it's
123 // still alive in 1s.
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800124 void Stop() { HandleCommand(aos::starter::Command::STOP); }
125
Sanjay Narayanan92fdc3d2023-08-25 14:42:56 -0700126 // Stops the command the same way as Stop() does, but updates internal state
127 // to reflect that the application was terminated.
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800128 void Terminate();
129
Austin Schuh1cea9032023-07-10 11:56:40 -0700130 // Adds a callback which gets notified when the application changes state.
131 // This is in addition to any existing callbacks and doesn't replace any of
132 // them.
133 void AddOnChange(std::function<void()> fn) {
134 on_change_.emplace_back(std::move(fn));
135 }
136
James Kuszmauld42edb42022-01-07 18:00:16 -0800137 void set_args(std::vector<std::string> args);
138 void set_capture_stdout(bool capture);
139 void set_capture_stderr(bool capture);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700140 void set_run_as_sudo(bool value) { run_as_sudo_ = value; }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800141
Philipp Schraderc8e779e2024-01-25 16:32:39 -0800142 // Sets the time for a process to stop gracefully. If an application is asked
143 // to stop, but doesn't stop within the specified time limit, then it is
144 // forcefully killed. Defaults to 1 second unless overridden by the
145 // aos::Application instance in the constructor.
146 void set_stop_grace_period(std::chrono::nanoseconds stop_grace_period) {
147 stop_grace_period_ = stop_grace_period;
148 }
149
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800150 bool autostart() const { return autostart_; }
151
152 bool autorestart() const { return autorestart_; }
Adam Snaider70deaf22023-08-11 13:58:34 -0700153 void set_autorestart(bool autorestart) { autorestart_ = autorestart; }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800154
Philipp Schrader595979d2023-09-13 11:31:48 -0700155 LastStopReason stop_reason() const { return stop_reason_; }
156
James Kuszmauld42edb42022-01-07 18:00:16 -0800157 const std::string &GetStdout();
158 const std::string &GetStderr();
159 std::optional<int> exit_code() const { return exit_code_; }
160
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700161 // Sets the memory limit for the application to the provided limit.
162 void SetMemoryLimit(size_t limit) {
163 if (!memory_cgroup_) {
164 memory_cgroup_ = std::make_unique<MemoryCGroup>(name_);
165 }
166 memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
167 }
168
Austin Schuh77e20a32023-08-01 12:25:03 -0700169 // Sets the cgroup and memory limit to a pre-existing cgroup which is
170 // externally managed. This lets us configure the cgroup of an application
171 // without root access.
172 void SetExistingCgroupMemoryLimit(std::string_view name, size_t limit) {
173 if (!memory_cgroup_) {
174 memory_cgroup_ = std::make_unique<MemoryCGroup>(
175 name, MemoryCGroup::Create::kDoNotCreate);
176 }
177 memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
178 }
179
James Kuszmaul8544c492023-07-31 15:00:38 -0700180 // Observe a timing report message, and save it if it is relevant to us.
181 // It is the responsibility of the caller to manage this, because the lifetime
182 // of the Application itself is such that it cannot own Fetchers readily.
183 void ObserveTimingReport(const aos::monotonic_clock::time_point send_time,
184 const aos::timing::Report *msg);
185
James Kuszmaul37a56af2023-07-29 15:15:16 -0700186 FileState UpdateFileState();
187
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800188 private:
James Kuszmauld42edb42022-01-07 18:00:16 -0800189 typedef aos::util::ScopedPipe::PipePair PipePair;
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700190
Philipp Schrader790cb542023-07-05 21:06:52 -0700191 static constexpr const char *const kSudo{"sudo"};
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700192
James Kuszmauld42edb42022-01-07 18:00:16 -0800193 void set_args(
194 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
195 &args);
196
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800197 void DoStart();
198
199 void DoStop(bool restart);
200
201 void QueueStart();
202
Austin Schuh1cea9032023-07-10 11:56:40 -0700203 void OnChange();
204
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800205 // Copy flatbuffer vector of strings to vector of std::string.
206 static std::vector<std::string> FbsVectorToVector(
207 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v);
208
209 static std::optional<uid_t> FindUid(const char *name);
210 static std::optional<gid_t> FindPrimaryGidForUser(const char *name);
211
James Kuszmauld42edb42022-01-07 18:00:16 -0800212 void FetchOutputs();
213
214 // Provides an std::vector of the args (such that CArgs().data() ends up being
215 // suitable to pass to execve()).
216 // The points are invalidated when args_ changes (e.g., due to a set_args
217 // call).
218 std::vector<char *> CArgs();
219
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800220 // Next unique id for all applications
221 static inline uint64_t next_id_ = 0;
222
223 std::string name_;
James Kuszmaul37a56af2023-07-29 15:15:16 -0700224 std::filesystem::path path_;
225 // Inode of path_ immediately prior to the most recent fork() call.
226 ino_t pre_fork_inode_;
227 FileState file_state_ = FileState::NOT_RUNNING;
James Kuszmauld42edb42022-01-07 18:00:16 -0800228 std::vector<std::string> args_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800229 std::string user_name_;
230 std::optional<uid_t> user_;
231 std::optional<gid_t> group_;
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700232 bool run_as_sudo_ = false;
Philipp Schraderc8e779e2024-01-25 16:32:39 -0800233 std::chrono::nanoseconds stop_grace_period_ = std::chrono::seconds(1);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800234
James Kuszmauld42edb42022-01-07 18:00:16 -0800235 bool capture_stdout_ = false;
236 PipePair stdout_pipes_;
237 std::string stdout_;
238 bool capture_stderr_ = false;
239 PipePair stderr_pipes_;
240 std::string stderr_;
241
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800242 pid_t pid_ = -1;
James Kuszmauld42edb42022-01-07 18:00:16 -0800243 PipePair status_pipes_;
244 uint64_t id_ = 0;
245 std::optional<int> exit_code_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800246 aos::monotonic_clock::time_point start_time_, exit_time_;
247 bool queue_restart_ = false;
248 bool terminating_ = false;
James Kuszmauld42edb42022-01-07 18:00:16 -0800249 bool autostart_ = false;
250 bool autorestart_ = false;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800251
252 aos::starter::State status_ = aos::starter::State::STOPPED;
253 aos::starter::LastStopReason stop_reason_ =
254 aos::starter::LastStopReason::STOP_REQUESTED;
255
256 aos::EventLoop *event_loop_;
James Kuszmauld42edb42022-01-07 18:00:16 -0800257 aos::TimerHandler *start_timer_, *restart_timer_, *stop_timer_, *pipe_timer_,
258 *child_status_handler_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800259
James Kuszmaul8544c492023-07-31 15:00:38 -0700260 // Version string from the most recent valid timing report for this
261 // application. Cleared when the application restarts.
262 std::optional<std::string> latest_timing_report_version_;
263 aos::monotonic_clock::time_point last_timing_report_ =
264 aos::monotonic_clock::min_time;
265
Austin Schuh1cea9032023-07-10 11:56:40 -0700266 std::vector<std::function<void()>> on_change_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800267
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700268 std::unique_ptr<MemoryCGroup> memory_cgroup_;
269
payton.rehl2841b1c2023-05-25 17:23:55 -0700270 QuietLogging quiet_flag_ = QuietLogging::kNo;
271
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800272 DISALLOW_COPY_AND_ASSIGN(Application);
273};
274
275} // namespace aos::starter
James Kuszmauld42edb42022-01-07 18:00:16 -0800276#endif // AOS_STARTER_SUBPROCESS_H_