blob: faa1873fb2080f5d6a93660a820c4e3931edd86d [file] [log] [blame]
James Kuszmaul3224b8e2022-01-07 19:00:39 -08001#include "aos/starter/subprocess.h"
2
3#include <grp.h>
4#include <pwd.h>
5#include <sys/prctl.h>
6#include <sys/types.h>
7#include <sys/wait.h>
8
9#include "glog/logging.h"
10
James Kuszmaul8544c492023-07-31 15:00:38 -070011#include "aos/flatbuffer_merge.h"
12
James Kuszmaul3224b8e2022-01-07 19:00:39 -080013namespace aos::starter {
14
Austin Schuhbbeb37e2022-08-17 16:19:27 -070015// RAII class to become root and restore back to the original user and group
16// afterwards.
17class Sudo {
18 public:
19 Sudo() {
20 // Save what we were.
21 PCHECK(getresuid(&ruid_, &euid_, &suid_) == 0);
22 PCHECK(getresgid(&rgid_, &egid_, &sgid_) == 0);
23
24 // Become root.
25 PCHECK(setresuid(/* ruid */ 0 /* root */, /* euid */ 0, /* suid */ 0) == 0)
26 << ": Failed to become root";
27 PCHECK(setresgid(/* ruid */ 0 /* root */, /* euid */ 0, /* suid */ 0) == 0)
28 << ": Failed to become root";
29 }
30
31 ~Sudo() {
32 // And recover.
33 PCHECK(setresgid(rgid_, egid_, sgid_) == 0);
34 PCHECK(setresuid(ruid_, euid_, suid_) == 0);
35 }
36
37 uid_t ruid_, euid_, suid_;
38 gid_t rgid_, egid_, sgid_;
39};
40
Austin Schuh77e20a32023-08-01 12:25:03 -070041MemoryCGroup::MemoryCGroup(std::string_view name, Create should_create)
42 : cgroup_(absl::StrCat("/sys/fs/cgroup/memory/aos_", name)),
43 should_create_(should_create) {
44 if (should_create_ == Create::kDoCreate) {
45 Sudo sudo;
46 int ret = mkdir(cgroup_.c_str(), 0755);
Austin Schuhbbeb37e2022-08-17 16:19:27 -070047
Austin Schuh77e20a32023-08-01 12:25:03 -070048 if (ret != 0) {
49 if (errno == EEXIST) {
50 PCHECK(rmdir(cgroup_.c_str()) == 0)
51 << ": Failed to remove previous cgroup " << cgroup_;
52 ret = mkdir(cgroup_.c_str(), 0755);
53 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -070054 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -070055
Austin Schuh77e20a32023-08-01 12:25:03 -070056 if (ret != 0) {
57 PLOG(FATAL) << ": Failed to create cgroup aos_" << cgroup_
58 << ", do you have permission?";
59 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -070060 }
61}
62
63void MemoryCGroup::AddTid(pid_t pid) {
64 if (pid == 0) {
65 pid = getpid();
66 }
Austin Schuh77e20a32023-08-01 12:25:03 -070067 if (should_create_ == Create::kDoCreate) {
68 Sudo sudo;
69 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
70 std::to_string(pid));
71 } else {
72 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
73 std::to_string(pid));
74 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -070075}
76
77void MemoryCGroup::SetLimit(std::string_view limit_name, uint64_t limit_value) {
Austin Schuh77e20a32023-08-01 12:25:03 -070078 if (should_create_ == Create::kDoCreate) {
79 Sudo sudo;
80 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
81 std::to_string(limit_value));
82 } else {
83 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
84 std::to_string(limit_value));
85 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -070086}
87
88MemoryCGroup::~MemoryCGroup() {
Austin Schuh77e20a32023-08-01 12:25:03 -070089 if (should_create_ == Create::kDoCreate) {
90 Sudo sudo;
91 PCHECK(rmdir(absl::StrCat(cgroup_).c_str()) == 0);
92 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -070093}
94
James Kuszmaul3224b8e2022-01-07 19:00:39 -080095SignalListener::SignalListener(aos::ShmEventLoop *loop,
96 std::function<void(signalfd_siginfo)> callback)
Austin Schuh1cea9032023-07-10 11:56:40 -070097 : SignalListener(loop->epoll(), std::move(callback)) {}
98
99SignalListener::SignalListener(aos::internal::EPoll *epoll,
100 std::function<void(signalfd_siginfo)> callback)
101 : SignalListener(epoll, callback,
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800102 {SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGFPE, SIGSEGV,
103 SIGPIPE, SIGTERM, SIGBUS, SIGXCPU, SIGCHLD}) {}
104
105SignalListener::SignalListener(aos::ShmEventLoop *loop,
106 std::function<void(signalfd_siginfo)> callback,
107 std::initializer_list<unsigned int> signals)
Austin Schuh1cea9032023-07-10 11:56:40 -0700108 : SignalListener(loop->epoll(), std::move(callback), std::move(signals)) {}
109
110SignalListener::SignalListener(aos::internal::EPoll *epoll,
111 std::function<void(signalfd_siginfo)> callback,
112 std::initializer_list<unsigned int> signals)
113 : epoll_(epoll), callback_(std::move(callback)), signalfd_(signals) {
114 epoll_->OnReadable(signalfd_.fd(), [this] {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800115 signalfd_siginfo info = signalfd_.Read();
116
117 if (info.ssi_signo == 0) {
118 LOG(WARNING) << "Could not read " << sizeof(signalfd_siginfo) << " bytes";
119 return;
120 }
121
122 callback_(info);
123 });
124}
125
Austin Schuh1cea9032023-07-10 11:56:40 -0700126SignalListener::~SignalListener() { epoll_->DeleteFd(signalfd_.fd()); }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800127
James Kuszmauld42edb42022-01-07 18:00:16 -0800128Application::Application(std::string_view name,
129 std::string_view executable_name,
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800130 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700131 std::function<void()> on_change,
132 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800133 : name_(name),
134 path_(executable_name),
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800135 event_loop_(event_loop),
136 start_timer_(event_loop_->AddTimer([this] {
137 status_ = aos::starter::State::RUNNING;
payton.rehl2841b1c2023-05-25 17:23:55 -0700138 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
139 << "Started '" << name_ << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800140 })),
141 restart_timer_(event_loop_->AddTimer([this] { DoStart(); })),
142 stop_timer_(event_loop_->AddTimer([this] {
143 if (kill(pid_, SIGKILL) == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700144 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
145 << "Failed to stop, sending SIGKILL to '" << name_
146 << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800147 }
148 })),
James Kuszmauld42edb42022-01-07 18:00:16 -0800149 pipe_timer_(event_loop_->AddTimer([this]() { FetchOutputs(); })),
150 child_status_handler_(
151 event_loop_->AddTimer([this]() { MaybeHandleSignal(); })),
Austin Schuh1cea9032023-07-10 11:56:40 -0700152 on_change_({on_change}),
payton.rehl2841b1c2023-05-25 17:23:55 -0700153 quiet_flag_(quiet_flag) {
Sanjay Narayanan92fdc3d2023-08-25 14:42:56 -0700154 // Every second poll to check if the child is dead. This is used as a
155 // default for the case where the user is not directly catching SIGCHLD and
156 // calling MaybeHandleSignal for us.
157 child_status_handler_->Schedule(event_loop_->monotonic_now(),
158 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800159}
160
161Application::Application(const aos::Application *application,
162 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700163 std::function<void()> on_change,
164 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800165 : Application(application->name()->string_view(),
166 application->has_executable_name()
167 ? application->executable_name()->string_view()
168 : application->name()->string_view(),
payton.rehl2841b1c2023-05-25 17:23:55 -0700169 event_loop, on_change, quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800170 user_name_ = application->has_user() ? application->user()->str() : "";
171 user_ = application->has_user() ? FindUid(user_name_.c_str()) : std::nullopt;
172 group_ = application->has_user() ? FindPrimaryGidForUser(user_name_.c_str())
173 : std::nullopt;
174 autostart_ = application->autostart();
175 autorestart_ = application->autorestart();
176 if (application->has_args()) {
177 set_args(*application->args());
178 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700179
180 if (application->has_memory_limit() && application->memory_limit() > 0) {
181 SetMemoryLimit(application->memory_limit());
182 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800183}
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800184
185void Application::DoStart() {
186 if (status_ != aos::starter::State::WAITING) {
187 return;
188 }
189
190 start_timer_->Disable();
191 restart_timer_->Disable();
192
James Kuszmauld42edb42022-01-07 18:00:16 -0800193 status_pipes_ = util::ScopedPipe::MakePipe();
194
195 if (capture_stdout_) {
196 stdout_pipes_ = util::ScopedPipe::MakePipe();
197 stdout_.clear();
198 }
199 if (capture_stderr_) {
200 stderr_pipes_ = util::ScopedPipe::MakePipe();
201 stderr_.clear();
202 }
203
Philipp Schradera6712522023-07-05 20:25:11 -0700204 pipe_timer_->Schedule(event_loop_->monotonic_now(),
205 std::chrono::milliseconds(100));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800206
207 const pid_t pid = fork();
208
209 if (pid != 0) {
210 if (pid == -1) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700211 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
212 << "Failed to fork '" << name_ << "'";
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800213 stop_reason_ = aos::starter::LastStopReason::FORK_ERR;
214 status_ = aos::starter::State::STOPPED;
215 } else {
216 pid_ = pid;
217 id_ = next_id_++;
218 start_time_ = event_loop_->monotonic_now();
219 status_ = aos::starter::State::STARTING;
James Kuszmaul8544c492023-07-31 15:00:38 -0700220 latest_timing_report_version_.reset();
payton.rehl2841b1c2023-05-25 17:23:55 -0700221 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
222 << "Starting '" << name_ << "' pid " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800223
Philipp Schradera6712522023-07-05 20:25:11 -0700224 // Set up timer which moves application to RUNNING state if it is still
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800225 // alive in 1 second.
Philipp Schradera6712522023-07-05 20:25:11 -0700226 start_timer_->Schedule(event_loop_->monotonic_now() +
227 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800228 // Since we are the parent process, clear our write-side of all the pipes.
229 status_pipes_.write.reset();
230 stdout_pipes_.write.reset();
231 stderr_pipes_.write.reset();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800232 }
Austin Schuh1cea9032023-07-10 11:56:40 -0700233 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800234 return;
235 }
236
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700237 if (memory_cgroup_) {
238 memory_cgroup_->AddTid();
239 }
240
James Kuszmauld42edb42022-01-07 18:00:16 -0800241 // Since we are the child process, clear our read-side of all the pipes.
242 status_pipes_.read.reset();
243 stdout_pipes_.read.reset();
244 stderr_pipes_.read.reset();
245
246 // The status pipe will not be needed if the execve succeeds.
247 status_pipes_.write->SetCloexec();
248
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800249 // Clear out signal mask of parent so forked process receives all signals
250 // normally.
251 sigset_t empty_mask;
252 sigemptyset(&empty_mask);
253 sigprocmask(SIG_SETMASK, &empty_mask, nullptr);
254
255 // Cleanup children if starter dies in a way that is not handled gracefully.
256 if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800257 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800258 static_cast<uint32_t>(aos::starter::LastStopReason::SET_PRCTL_ERR));
259 PLOG(FATAL) << "Could not set PR_SET_PDEATHSIG to SIGKILL";
260 }
261
262 if (group_) {
263 CHECK(!user_name_.empty());
264 // The manpage for setgroups says we just need CAP_SETGID, but empirically
265 // we also need the effective UID to be 0 to make it work. user_ must also
266 // be set so we change this effective UID back later.
267 CHECK(user_);
268 if (seteuid(0) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800269 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800270 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
271 PLOG(FATAL) << "Could not seteuid(0) for " << name_
272 << " in preparation for setting groups";
273 }
274 if (initgroups(user_name_.c_str(), *group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800275 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800276 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
277 PLOG(FATAL) << "Could not initialize normal groups for " << name_
278 << " as " << user_name_ << " with " << *group_;
279 }
280 if (setgid(*group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800281 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800282 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
283 PLOG(FATAL) << "Could not set group for " << name_ << " to " << *group_;
284 }
285 }
286
287 if (user_) {
288 if (setuid(*user_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800289 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800290 static_cast<uint32_t>(aos::starter::LastStopReason::SET_USR_ERR));
291 PLOG(FATAL) << "Could not set user for " << name_ << " to " << *user_;
292 }
293 }
294
James Kuszmauld42edb42022-01-07 18:00:16 -0800295 if (capture_stdout_) {
296 PCHECK(STDOUT_FILENO == dup2(stdout_pipes_.write->fd(), STDOUT_FILENO));
297 stdout_pipes_.write.reset();
298 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800299
James Kuszmauld42edb42022-01-07 18:00:16 -0800300 if (capture_stderr_) {
301 PCHECK(STDERR_FILENO == dup2(stderr_pipes_.write->fd(), STDERR_FILENO));
302 stderr_pipes_.write.reset();
303 }
304
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700305 if (run_as_sudo_) {
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700306 // For sudo we must supply the actual path
307 args_.insert(args_.begin(), path_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700308 args_.insert(args_.begin(), kSudo);
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700309 } else {
310 // argv[0] should be the program name
311 args_.insert(args_.begin(), name_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700312 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800313
314 std::vector<char *> cargs = CArgs();
Philipp Schrader790cb542023-07-05 21:06:52 -0700315 const char *path = run_as_sudo_ ? kSudo : path_.c_str();
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700316 execvp(path, cargs.data());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800317
318 // If we got here, something went wrong
James Kuszmauld42edb42022-01-07 18:00:16 -0800319 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800320 static_cast<uint32_t>(aos::starter::LastStopReason::EXECV_ERR));
payton.rehl2841b1c2023-05-25 17:23:55 -0700321 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
322 << "Could not execute " << name_ << " (" << path_ << ')';
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800323
324 _exit(EXIT_FAILURE);
325}
326
James Kuszmaul8544c492023-07-31 15:00:38 -0700327void Application::ObserveTimingReport(
328 const aos::monotonic_clock::time_point send_time,
329 const aos::timing::Report *msg) {
330 if (msg->name()->string_view() == name_ && msg->pid() == pid_ &&
331 msg->has_version()) {
332 latest_timing_report_version_ = msg->version()->str();
333 last_timing_report_ = send_time;
334 }
335}
336
James Kuszmauld42edb42022-01-07 18:00:16 -0800337void Application::FetchOutputs() {
338 if (capture_stdout_) {
339 stdout_pipes_.read->Read(&stdout_);
340 }
341 if (capture_stderr_) {
342 stderr_pipes_.read->Read(&stderr_);
343 }
344}
345
346const std::string &Application::GetStdout() {
347 CHECK(capture_stdout_);
348 FetchOutputs();
349 return stdout_;
350}
351
352const std::string &Application::GetStderr() {
353 CHECK(capture_stderr_);
354 FetchOutputs();
355 return stderr_;
356}
357
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800358void Application::DoStop(bool restart) {
359 // If stop or restart received, the old state of these is no longer applicable
360 // so cancel both.
361 restart_timer_->Disable();
362 start_timer_->Disable();
363
James Kuszmauld42edb42022-01-07 18:00:16 -0800364 FetchOutputs();
365
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800366 switch (status_) {
367 case aos::starter::State::STARTING:
368 case aos::starter::State::RUNNING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700369 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
370 << "Stopping '" << name_ << "' pid: " << pid_ << " with signal "
371 << SIGINT;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800372 status_ = aos::starter::State::STOPPING;
373
374 kill(pid_, SIGINT);
375
376 // Watchdog timer to SIGKILL application if it is still running 1 second
377 // after SIGINT
Philipp Schradera6712522023-07-05 20:25:11 -0700378 stop_timer_->Schedule(event_loop_->monotonic_now() +
379 std::chrono::seconds(1));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800380 queue_restart_ = restart;
Austin Schuh1cea9032023-07-10 11:56:40 -0700381 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800382 break;
383 }
384 case aos::starter::State::WAITING: {
385 // If waiting to restart, and receives restart, skip the waiting period
386 // and restart immediately. If stop received, all we have to do is move
387 // to the STOPPED state.
388 if (restart) {
389 DoStart();
390 } else {
391 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700392 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800393 }
394 break;
395 }
396 case aos::starter::State::STOPPING: {
397 // If the application is already stopping, then we just need to update the
398 // restart flag to the most recent status.
399 queue_restart_ = restart;
400 break;
401 }
402 case aos::starter::State::STOPPED: {
403 // Restart immediately if the application is already stopped
404 if (restart) {
405 status_ = aos::starter::State::WAITING;
406 DoStart();
407 }
408 break;
409 }
410 }
411}
412
413void Application::QueueStart() {
414 status_ = aos::starter::State::WAITING;
415
payton.rehl2841b1c2023-05-25 17:23:55 -0700416 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
417 << "Restarting " << name_ << " in 3 seconds";
Philipp Schradera6712522023-07-05 20:25:11 -0700418 restart_timer_->Schedule(event_loop_->monotonic_now() +
419 std::chrono::seconds(3));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800420 start_timer_->Disable();
421 stop_timer_->Disable();
Austin Schuh1cea9032023-07-10 11:56:40 -0700422 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800423}
424
James Kuszmauld42edb42022-01-07 18:00:16 -0800425std::vector<char *> Application::CArgs() {
426 std::vector<char *> cargs;
427 std::transform(args_.begin(), args_.end(), std::back_inserter(cargs),
428 [](std::string &str) { return str.data(); });
429 cargs.push_back(nullptr);
430 return cargs;
431}
432
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800433void Application::set_args(
434 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v) {
435 args_.clear();
436 std::transform(v.begin(), v.end(), std::back_inserter(args_),
James Kuszmauld42edb42022-01-07 18:00:16 -0800437 [](const flatbuffers::String *str) { return str->str(); });
438}
439
440void Application::set_args(std::vector<std::string> args) {
441 args_ = std::move(args);
442}
443
444void Application::set_capture_stdout(bool capture) {
445 capture_stdout_ = capture;
446}
447
448void Application::set_capture_stderr(bool capture) {
449 capture_stderr_ = capture;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800450}
451
452std::optional<uid_t> Application::FindUid(const char *name) {
453 // TODO(austin): Use the reentrant version. This should be safe.
454 struct passwd *user_data = getpwnam(name);
455 if (user_data != nullptr) {
456 return user_data->pw_uid;
457 } else {
458 LOG(FATAL) << "Could not find user " << name;
459 return std::nullopt;
460 }
461}
462
463std::optional<gid_t> Application::FindPrimaryGidForUser(const char *name) {
464 // TODO(austin): Use the reentrant version. This should be safe.
465 struct passwd *user_data = getpwnam(name);
466 if (user_data != nullptr) {
467 return user_data->pw_gid;
468 } else {
469 LOG(FATAL) << "Could not find user " << name;
470 return std::nullopt;
471 }
472}
473
474flatbuffers::Offset<aos::starter::ApplicationStatus>
James Kuszmaul6295a642022-03-22 15:23:59 -0700475Application::PopulateStatus(flatbuffers::FlatBufferBuilder *builder,
476 util::Top *top) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800477 CHECK_NOTNULL(builder);
478 auto name_fbs = builder->CreateString(name_);
479
James Kuszmaul6295a642022-03-22 15:23:59 -0700480 const bool valid_pid = pid_ > 0 && status_ != aos::starter::State::STOPPED;
481 const flatbuffers::Offset<util::ProcessInfo> process_info =
482 valid_pid ? top->InfoForProcess(builder, pid_)
483 : flatbuffers::Offset<util::ProcessInfo>();
484
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800485 aos::starter::ApplicationStatus::Builder status_builder(*builder);
486 status_builder.add_name(name_fbs);
487 status_builder.add_state(status_);
James Kuszmauld42edb42022-01-07 18:00:16 -0800488 if (exit_code_.has_value()) {
489 status_builder.add_last_exit_code(exit_code_.value());
490 }
James Kuszmaul8544c492023-07-31 15:00:38 -0700491 status_builder.add_has_active_timing_report(
492 last_timing_report_ +
493 // Leave a bit of margin on the timing report receipt time, to allow
494 // for timing errors.
495 3 * std::chrono::milliseconds(FLAGS_timing_report_ms) >
496 event_loop_->monotonic_now());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800497 status_builder.add_last_stop_reason(stop_reason_);
498 if (pid_ != -1) {
499 status_builder.add_pid(pid_);
500 status_builder.add_id(id_);
501 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700502 // Note that even if process_info is null, calling add_process_info is fine.
503 status_builder.add_process_info(process_info);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800504 status_builder.add_last_start_time(start_time_.time_since_epoch().count());
505 return status_builder.Finish();
506}
507
508void Application::Terminate() {
509 stop_reason_ = aos::starter::LastStopReason::TERMINATE;
510 DoStop(false);
511 terminating_ = true;
512}
513
514void Application::HandleCommand(aos::starter::Command cmd) {
515 switch (cmd) {
516 case aos::starter::Command::START: {
517 switch (status_) {
518 case aos::starter::State::WAITING: {
519 restart_timer_->Disable();
520 DoStart();
521 break;
522 }
523 case aos::starter::State::STARTING: {
524 break;
525 }
526 case aos::starter::State::RUNNING: {
527 break;
528 }
529 case aos::starter::State::STOPPING: {
530 queue_restart_ = true;
531 break;
532 }
533 case aos::starter::State::STOPPED: {
534 status_ = aos::starter::State::WAITING;
535 DoStart();
536 break;
537 }
538 }
539 break;
540 }
541 case aos::starter::Command::STOP: {
542 stop_reason_ = aos::starter::LastStopReason::STOP_REQUESTED;
543 DoStop(false);
544 break;
545 }
546 case aos::starter::Command::RESTART: {
547 stop_reason_ = aos::starter::LastStopReason::RESTART_REQUESTED;
548 DoStop(true);
549 break;
550 }
551 }
552}
553
554bool Application::MaybeHandleSignal() {
555 int status;
556
Sarah Newman21c59202022-06-16 12:36:33 -0700557 if (status_ == aos::starter::State::WAITING ||
558 status_ == aos::starter::State::STOPPED) {
559 // We can't possibly have received a signal meant for this process.
560 return false;
561 }
562
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800563 // Check if the status of this process has changed
Sarah Newman21c59202022-06-16 12:36:33 -0700564 // The PID won't be -1 if this application has ever been run successfully
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800565 if (pid_ == -1 || waitpid(pid_, &status, WNOHANG) != pid_) {
566 return false;
567 }
568
569 // Check that the event was the process exiting
570 if (!WIFEXITED(status) && !WIFSIGNALED(status)) {
571 return false;
572 }
573
James Kuszmauld42edb42022-01-07 18:00:16 -0800574 start_timer_->Disable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800575 exit_time_ = event_loop_->monotonic_now();
576 exit_code_ = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status);
577
James Kuszmauld42edb42022-01-07 18:00:16 -0800578 if (auto read_result = status_pipes_.read->Read()) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800579 stop_reason_ = static_cast<aos::starter::LastStopReason>(*read_result);
580 }
581
582 switch (status_) {
583 case aos::starter::State::STARTING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800584 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700585 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
586 << "Application '" << name_ << "' pid " << pid_
587 << " exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800588 } else {
payton.rehl2841b1c2023-05-25 17:23:55 -0700589 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
590 << "Failed to start '" << name_ << "' on pid " << pid_
591 << " : Exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800592 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800593 if (autorestart()) {
594 QueueStart();
595 } else {
596 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700597 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800598 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800599 break;
600 }
601 case aos::starter::State::RUNNING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800602 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700603 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
604 << "Application '" << name_ << "' pid " << pid_
605 << " exited with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800606 } else {
James Kuszmaul8544c492023-07-31 15:00:38 -0700607 if (quiet_flag_ == QuietLogging::kNo) {
608 std::string version_string =
609 latest_timing_report_version_.has_value()
610 ? absl::StrCat("'", latest_timing_report_version_.value(),
611 "'")
612 : "unknown";
613 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
614 << "Application '" << name_ << "' pid " << pid_ << " version "
615 << version_string << " exited unexpectedly with status "
616 << exit_code_.value();
617 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800618 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800619 if (autorestart()) {
620 QueueStart();
621 } else {
622 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700623 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800624 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800625 break;
626 }
627 case aos::starter::State::STOPPING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700628 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
629 << "Successfully stopped '" << name_ << "' pid: " << pid_
630 << " with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800631 status_ = aos::starter::State::STOPPED;
632
633 // Disable force stop timer since the process already died
634 stop_timer_->Disable();
635
Austin Schuh1cea9032023-07-10 11:56:40 -0700636 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800637 if (terminating_) {
638 return true;
639 }
640
641 if (queue_restart_) {
642 queue_restart_ = false;
643 status_ = aos::starter::State::WAITING;
644 DoStart();
645 }
646 break;
647 }
648 case aos::starter::State::WAITING:
649 case aos::starter::State::STOPPED: {
Sarah Newman21c59202022-06-16 12:36:33 -0700650 __builtin_unreachable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800651 break;
652 }
653 }
654
655 return false;
656}
657
Austin Schuh1cea9032023-07-10 11:56:40 -0700658void Application::OnChange() {
659 for (auto &fn : on_change_) {
660 fn();
661 }
662}
663
Adam Snaider70deaf22023-08-11 13:58:34 -0700664Application::~Application() {
665 start_timer_->Disable();
666 restart_timer_->Disable();
667 stop_timer_->Disable();
668 pipe_timer_->Disable();
669 child_status_handler_->Disable();
670}
671
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800672} // namespace aos::starter