blob: b7a1cf6dfd06500e0a67e6b9a17731d092719cb8 [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
11namespace aos::starter {
12
Austin Schuhbbeb37e2022-08-17 16:19:27 -070013// RAII class to become root and restore back to the original user and group
14// afterwards.
15class Sudo {
16 public:
17 Sudo() {
18 // Save what we were.
19 PCHECK(getresuid(&ruid_, &euid_, &suid_) == 0);
20 PCHECK(getresgid(&rgid_, &egid_, &sgid_) == 0);
21
22 // Become root.
23 PCHECK(setresuid(/* ruid */ 0 /* root */, /* euid */ 0, /* suid */ 0) == 0)
24 << ": Failed to become root";
25 PCHECK(setresgid(/* ruid */ 0 /* root */, /* euid */ 0, /* suid */ 0) == 0)
26 << ": Failed to become root";
27 }
28
29 ~Sudo() {
30 // And recover.
31 PCHECK(setresgid(rgid_, egid_, sgid_) == 0);
32 PCHECK(setresuid(ruid_, euid_, suid_) == 0);
33 }
34
35 uid_t ruid_, euid_, suid_;
36 gid_t rgid_, egid_, sgid_;
37};
38
39MemoryCGroup::MemoryCGroup(std::string_view name)
40 : cgroup_(absl::StrCat("/sys/fs/cgroup/memory/aos_", name)) {
41 Sudo sudo;
42 int ret = mkdir(cgroup_.c_str(), 0755);
43
44 if (ret != 0) {
45 if (errno == EEXIST) {
Austin Schuh4d8a46e2022-10-07 19:20:20 -070046 PCHECK(rmdir(cgroup_.c_str()) == 0)
Austin Schuhbbeb37e2022-08-17 16:19:27 -070047 << ": Failed to remove previous cgroup " << cgroup_;
48 ret = mkdir(cgroup_.c_str(), 0755);
49 }
50 }
51
52 if (ret != 0) {
53 PLOG(FATAL) << ": Failed to create cgroup aos_" << cgroup_
54 << ", do you have permission?";
55 }
56}
57
58void MemoryCGroup::AddTid(pid_t pid) {
59 if (pid == 0) {
60 pid = getpid();
61 }
62 Sudo sudo;
63 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
64 std::to_string(pid));
65}
66
67void MemoryCGroup::SetLimit(std::string_view limit_name, uint64_t limit_value) {
68 Sudo sudo;
69 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
70 std::to_string(limit_value));
71}
72
73MemoryCGroup::~MemoryCGroup() {
74 Sudo sudo;
75 PCHECK(rmdir(absl::StrCat(cgroup_).c_str()) == 0);
76}
77
James Kuszmaul3224b8e2022-01-07 19:00:39 -080078SignalListener::SignalListener(aos::ShmEventLoop *loop,
79 std::function<void(signalfd_siginfo)> callback)
80 : SignalListener(loop, callback,
81 {SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGFPE, SIGSEGV,
82 SIGPIPE, SIGTERM, SIGBUS, SIGXCPU, SIGCHLD}) {}
83
84SignalListener::SignalListener(aos::ShmEventLoop *loop,
85 std::function<void(signalfd_siginfo)> callback,
86 std::initializer_list<unsigned int> signals)
87 : loop_(loop), callback_(std::move(callback)), signalfd_(signals) {
88 loop->epoll()->OnReadable(signalfd_.fd(), [this] {
89 signalfd_siginfo info = signalfd_.Read();
90
91 if (info.ssi_signo == 0) {
92 LOG(WARNING) << "Could not read " << sizeof(signalfd_siginfo) << " bytes";
93 return;
94 }
95
96 callback_(info);
97 });
98}
99
100SignalListener::~SignalListener() { loop_->epoll()->DeleteFd(signalfd_.fd()); }
101
James Kuszmauld42edb42022-01-07 18:00:16 -0800102Application::Application(std::string_view name,
103 std::string_view executable_name,
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800104 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700105 std::function<void()> on_change,
106 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800107 : name_(name),
108 path_(executable_name),
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800109 event_loop_(event_loop),
110 start_timer_(event_loop_->AddTimer([this] {
111 status_ = aos::starter::State::RUNNING;
payton.rehl2841b1c2023-05-25 17:23:55 -0700112 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
113 << "Started '" << name_ << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800114 })),
115 restart_timer_(event_loop_->AddTimer([this] { DoStart(); })),
116 stop_timer_(event_loop_->AddTimer([this] {
117 if (kill(pid_, SIGKILL) == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700118 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
119 << "Failed to stop, sending SIGKILL to '" << name_
120 << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800121 }
122 })),
James Kuszmauld42edb42022-01-07 18:00:16 -0800123 pipe_timer_(event_loop_->AddTimer([this]() { FetchOutputs(); })),
124 child_status_handler_(
125 event_loop_->AddTimer([this]() { MaybeHandleSignal(); })),
payton.rehl2841b1c2023-05-25 17:23:55 -0700126 on_change_(on_change),
127 quiet_flag_(quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800128 event_loop_->OnRun([this]() {
129 // Every second poll to check if the child is dead. This is used as a
130 // default for the case where the user is not directly catching SIGCHLD and
131 // calling MaybeHandleSignal for us.
Philipp Schradera6712522023-07-05 20:25:11 -0700132 child_status_handler_->Schedule(event_loop_->monotonic_now(),
133 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800134 });
135}
136
137Application::Application(const aos::Application *application,
138 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700139 std::function<void()> on_change,
140 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800141 : Application(application->name()->string_view(),
142 application->has_executable_name()
143 ? application->executable_name()->string_view()
144 : application->name()->string_view(),
payton.rehl2841b1c2023-05-25 17:23:55 -0700145 event_loop, on_change, quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800146 user_name_ = application->has_user() ? application->user()->str() : "";
147 user_ = application->has_user() ? FindUid(user_name_.c_str()) : std::nullopt;
148 group_ = application->has_user() ? FindPrimaryGidForUser(user_name_.c_str())
149 : std::nullopt;
150 autostart_ = application->autostart();
151 autorestart_ = application->autorestart();
152 if (application->has_args()) {
153 set_args(*application->args());
154 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700155
156 if (application->has_memory_limit() && application->memory_limit() > 0) {
157 SetMemoryLimit(application->memory_limit());
158 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800159}
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800160
161void Application::DoStart() {
162 if (status_ != aos::starter::State::WAITING) {
163 return;
164 }
165
166 start_timer_->Disable();
167 restart_timer_->Disable();
168
James Kuszmauld42edb42022-01-07 18:00:16 -0800169 status_pipes_ = util::ScopedPipe::MakePipe();
170
171 if (capture_stdout_) {
172 stdout_pipes_ = util::ScopedPipe::MakePipe();
173 stdout_.clear();
174 }
175 if (capture_stderr_) {
176 stderr_pipes_ = util::ScopedPipe::MakePipe();
177 stderr_.clear();
178 }
179
Philipp Schradera6712522023-07-05 20:25:11 -0700180 pipe_timer_->Schedule(event_loop_->monotonic_now(),
181 std::chrono::milliseconds(100));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800182
183 const pid_t pid = fork();
184
185 if (pid != 0) {
186 if (pid == -1) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700187 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
188 << "Failed to fork '" << name_ << "'";
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800189 stop_reason_ = aos::starter::LastStopReason::FORK_ERR;
190 status_ = aos::starter::State::STOPPED;
191 } else {
192 pid_ = pid;
193 id_ = next_id_++;
194 start_time_ = event_loop_->monotonic_now();
195 status_ = aos::starter::State::STARTING;
payton.rehl2841b1c2023-05-25 17:23:55 -0700196 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
197 << "Starting '" << name_ << "' pid " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800198
Philipp Schradera6712522023-07-05 20:25:11 -0700199 // Set up timer which moves application to RUNNING state if it is still
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800200 // alive in 1 second.
Philipp Schradera6712522023-07-05 20:25:11 -0700201 start_timer_->Schedule(event_loop_->monotonic_now() +
202 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800203 // Since we are the parent process, clear our write-side of all the pipes.
204 status_pipes_.write.reset();
205 stdout_pipes_.write.reset();
206 stderr_pipes_.write.reset();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800207 }
208 on_change_();
209 return;
210 }
211
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700212 if (memory_cgroup_) {
213 memory_cgroup_->AddTid();
214 }
215
James Kuszmauld42edb42022-01-07 18:00:16 -0800216 // Since we are the child process, clear our read-side of all the pipes.
217 status_pipes_.read.reset();
218 stdout_pipes_.read.reset();
219 stderr_pipes_.read.reset();
220
221 // The status pipe will not be needed if the execve succeeds.
222 status_pipes_.write->SetCloexec();
223
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800224 // Clear out signal mask of parent so forked process receives all signals
225 // normally.
226 sigset_t empty_mask;
227 sigemptyset(&empty_mask);
228 sigprocmask(SIG_SETMASK, &empty_mask, nullptr);
229
230 // Cleanup children if starter dies in a way that is not handled gracefully.
231 if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800232 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800233 static_cast<uint32_t>(aos::starter::LastStopReason::SET_PRCTL_ERR));
234 PLOG(FATAL) << "Could not set PR_SET_PDEATHSIG to SIGKILL";
235 }
236
237 if (group_) {
238 CHECK(!user_name_.empty());
239 // The manpage for setgroups says we just need CAP_SETGID, but empirically
240 // we also need the effective UID to be 0 to make it work. user_ must also
241 // be set so we change this effective UID back later.
242 CHECK(user_);
243 if (seteuid(0) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800244 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800245 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
246 PLOG(FATAL) << "Could not seteuid(0) for " << name_
247 << " in preparation for setting groups";
248 }
249 if (initgroups(user_name_.c_str(), *group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800250 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800251 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
252 PLOG(FATAL) << "Could not initialize normal groups for " << name_
253 << " as " << user_name_ << " with " << *group_;
254 }
255 if (setgid(*group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800256 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800257 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
258 PLOG(FATAL) << "Could not set group for " << name_ << " to " << *group_;
259 }
260 }
261
262 if (user_) {
263 if (setuid(*user_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800264 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800265 static_cast<uint32_t>(aos::starter::LastStopReason::SET_USR_ERR));
266 PLOG(FATAL) << "Could not set user for " << name_ << " to " << *user_;
267 }
268 }
269
James Kuszmauld42edb42022-01-07 18:00:16 -0800270 if (capture_stdout_) {
271 PCHECK(STDOUT_FILENO == dup2(stdout_pipes_.write->fd(), STDOUT_FILENO));
272 stdout_pipes_.write.reset();
273 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800274
James Kuszmauld42edb42022-01-07 18:00:16 -0800275 if (capture_stderr_) {
276 PCHECK(STDERR_FILENO == dup2(stderr_pipes_.write->fd(), STDERR_FILENO));
277 stderr_pipes_.write.reset();
278 }
279
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700280 if (run_as_sudo_) {
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700281 // For sudo we must supply the actual path
282 args_.insert(args_.begin(), path_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700283 args_.insert(args_.begin(), kSudo);
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700284 } else {
285 // argv[0] should be the program name
286 args_.insert(args_.begin(), name_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700287 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800288
289 std::vector<char *> cargs = CArgs();
Philipp Schrader790cb542023-07-05 21:06:52 -0700290 const char *path = run_as_sudo_ ? kSudo : path_.c_str();
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700291 execvp(path, cargs.data());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800292
293 // If we got here, something went wrong
James Kuszmauld42edb42022-01-07 18:00:16 -0800294 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800295 static_cast<uint32_t>(aos::starter::LastStopReason::EXECV_ERR));
payton.rehl2841b1c2023-05-25 17:23:55 -0700296 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
297 << "Could not execute " << name_ << " (" << path_ << ')';
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800298
299 _exit(EXIT_FAILURE);
300}
301
James Kuszmauld42edb42022-01-07 18:00:16 -0800302void Application::FetchOutputs() {
303 if (capture_stdout_) {
304 stdout_pipes_.read->Read(&stdout_);
305 }
306 if (capture_stderr_) {
307 stderr_pipes_.read->Read(&stderr_);
308 }
309}
310
311const std::string &Application::GetStdout() {
312 CHECK(capture_stdout_);
313 FetchOutputs();
314 return stdout_;
315}
316
317const std::string &Application::GetStderr() {
318 CHECK(capture_stderr_);
319 FetchOutputs();
320 return stderr_;
321}
322
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800323void Application::DoStop(bool restart) {
324 // If stop or restart received, the old state of these is no longer applicable
325 // so cancel both.
326 restart_timer_->Disable();
327 start_timer_->Disable();
328
James Kuszmauld42edb42022-01-07 18:00:16 -0800329 FetchOutputs();
330
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800331 switch (status_) {
332 case aos::starter::State::STARTING:
333 case aos::starter::State::RUNNING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700334 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
335 << "Stopping '" << name_ << "' pid: " << pid_ << " with signal "
336 << SIGINT;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800337 status_ = aos::starter::State::STOPPING;
338
339 kill(pid_, SIGINT);
340
341 // Watchdog timer to SIGKILL application if it is still running 1 second
342 // after SIGINT
Philipp Schradera6712522023-07-05 20:25:11 -0700343 stop_timer_->Schedule(event_loop_->monotonic_now() +
344 std::chrono::seconds(1));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800345 queue_restart_ = restart;
346 on_change_();
347 break;
348 }
349 case aos::starter::State::WAITING: {
350 // If waiting to restart, and receives restart, skip the waiting period
351 // and restart immediately. If stop received, all we have to do is move
352 // to the STOPPED state.
353 if (restart) {
354 DoStart();
355 } else {
356 status_ = aos::starter::State::STOPPED;
357 on_change_();
358 }
359 break;
360 }
361 case aos::starter::State::STOPPING: {
362 // If the application is already stopping, then we just need to update the
363 // restart flag to the most recent status.
364 queue_restart_ = restart;
365 break;
366 }
367 case aos::starter::State::STOPPED: {
368 // Restart immediately if the application is already stopped
369 if (restart) {
370 status_ = aos::starter::State::WAITING;
371 DoStart();
372 }
373 break;
374 }
375 }
376}
377
378void Application::QueueStart() {
379 status_ = aos::starter::State::WAITING;
380
payton.rehl2841b1c2023-05-25 17:23:55 -0700381 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
382 << "Restarting " << name_ << " in 3 seconds";
Philipp Schradera6712522023-07-05 20:25:11 -0700383 restart_timer_->Schedule(event_loop_->monotonic_now() +
384 std::chrono::seconds(3));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800385 start_timer_->Disable();
386 stop_timer_->Disable();
387 on_change_();
388}
389
James Kuszmauld42edb42022-01-07 18:00:16 -0800390std::vector<char *> Application::CArgs() {
391 std::vector<char *> cargs;
392 std::transform(args_.begin(), args_.end(), std::back_inserter(cargs),
393 [](std::string &str) { return str.data(); });
394 cargs.push_back(nullptr);
395 return cargs;
396}
397
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800398void Application::set_args(
399 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v) {
400 args_.clear();
401 std::transform(v.begin(), v.end(), std::back_inserter(args_),
James Kuszmauld42edb42022-01-07 18:00:16 -0800402 [](const flatbuffers::String *str) { return str->str(); });
403}
404
405void Application::set_args(std::vector<std::string> args) {
406 args_ = std::move(args);
407}
408
409void Application::set_capture_stdout(bool capture) {
410 capture_stdout_ = capture;
411}
412
413void Application::set_capture_stderr(bool capture) {
414 capture_stderr_ = capture;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800415}
416
417std::optional<uid_t> Application::FindUid(const char *name) {
418 // TODO(austin): Use the reentrant version. This should be safe.
419 struct passwd *user_data = getpwnam(name);
420 if (user_data != nullptr) {
421 return user_data->pw_uid;
422 } else {
423 LOG(FATAL) << "Could not find user " << name;
424 return std::nullopt;
425 }
426}
427
428std::optional<gid_t> Application::FindPrimaryGidForUser(const char *name) {
429 // TODO(austin): Use the reentrant version. This should be safe.
430 struct passwd *user_data = getpwnam(name);
431 if (user_data != nullptr) {
432 return user_data->pw_gid;
433 } else {
434 LOG(FATAL) << "Could not find user " << name;
435 return std::nullopt;
436 }
437}
438
439flatbuffers::Offset<aos::starter::ApplicationStatus>
James Kuszmaul6295a642022-03-22 15:23:59 -0700440Application::PopulateStatus(flatbuffers::FlatBufferBuilder *builder,
441 util::Top *top) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800442 CHECK_NOTNULL(builder);
443 auto name_fbs = builder->CreateString(name_);
444
James Kuszmaul6295a642022-03-22 15:23:59 -0700445 const bool valid_pid = pid_ > 0 && status_ != aos::starter::State::STOPPED;
446 const flatbuffers::Offset<util::ProcessInfo> process_info =
447 valid_pid ? top->InfoForProcess(builder, pid_)
448 : flatbuffers::Offset<util::ProcessInfo>();
449
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800450 aos::starter::ApplicationStatus::Builder status_builder(*builder);
451 status_builder.add_name(name_fbs);
452 status_builder.add_state(status_);
James Kuszmauld42edb42022-01-07 18:00:16 -0800453 if (exit_code_.has_value()) {
454 status_builder.add_last_exit_code(exit_code_.value());
455 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800456 status_builder.add_last_stop_reason(stop_reason_);
457 if (pid_ != -1) {
458 status_builder.add_pid(pid_);
459 status_builder.add_id(id_);
460 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700461 // Note that even if process_info is null, calling add_process_info is fine.
462 status_builder.add_process_info(process_info);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800463 status_builder.add_last_start_time(start_time_.time_since_epoch().count());
464 return status_builder.Finish();
465}
466
467void Application::Terminate() {
468 stop_reason_ = aos::starter::LastStopReason::TERMINATE;
469 DoStop(false);
470 terminating_ = true;
471}
472
473void Application::HandleCommand(aos::starter::Command cmd) {
474 switch (cmd) {
475 case aos::starter::Command::START: {
476 switch (status_) {
477 case aos::starter::State::WAITING: {
478 restart_timer_->Disable();
479 DoStart();
480 break;
481 }
482 case aos::starter::State::STARTING: {
483 break;
484 }
485 case aos::starter::State::RUNNING: {
486 break;
487 }
488 case aos::starter::State::STOPPING: {
489 queue_restart_ = true;
490 break;
491 }
492 case aos::starter::State::STOPPED: {
493 status_ = aos::starter::State::WAITING;
494 DoStart();
495 break;
496 }
497 }
498 break;
499 }
500 case aos::starter::Command::STOP: {
501 stop_reason_ = aos::starter::LastStopReason::STOP_REQUESTED;
502 DoStop(false);
503 break;
504 }
505 case aos::starter::Command::RESTART: {
506 stop_reason_ = aos::starter::LastStopReason::RESTART_REQUESTED;
507 DoStop(true);
508 break;
509 }
510 }
511}
512
513bool Application::MaybeHandleSignal() {
514 int status;
515
Sarah Newman21c59202022-06-16 12:36:33 -0700516 if (status_ == aos::starter::State::WAITING ||
517 status_ == aos::starter::State::STOPPED) {
518 // We can't possibly have received a signal meant for this process.
519 return false;
520 }
521
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800522 // Check if the status of this process has changed
Sarah Newman21c59202022-06-16 12:36:33 -0700523 // The PID won't be -1 if this application has ever been run successfully
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800524 if (pid_ == -1 || waitpid(pid_, &status, WNOHANG) != pid_) {
525 return false;
526 }
527
528 // Check that the event was the process exiting
529 if (!WIFEXITED(status) && !WIFSIGNALED(status)) {
530 return false;
531 }
532
James Kuszmauld42edb42022-01-07 18:00:16 -0800533 start_timer_->Disable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800534 exit_time_ = event_loop_->monotonic_now();
535 exit_code_ = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status);
536
James Kuszmauld42edb42022-01-07 18:00:16 -0800537 if (auto read_result = status_pipes_.read->Read()) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800538 stop_reason_ = static_cast<aos::starter::LastStopReason>(*read_result);
539 }
540
541 switch (status_) {
542 case aos::starter::State::STARTING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800543 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700544 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
545 << "Application '" << name_ << "' pid " << pid_
546 << " exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800547 } else {
payton.rehl2841b1c2023-05-25 17:23:55 -0700548 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
549 << "Failed to start '" << name_ << "' on pid " << pid_
550 << " : Exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800551 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800552 if (autorestart()) {
553 QueueStart();
554 } else {
555 status_ = aos::starter::State::STOPPED;
556 on_change_();
557 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800558 break;
559 }
560 case aos::starter::State::RUNNING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800561 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700562 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
563 << "Application '" << name_ << "' pid " << pid_
564 << " exited with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800565 } else {
payton.rehl2841b1c2023-05-25 17:23:55 -0700566 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
567 << "Application '" << name_ << "' pid " << pid_
568 << " exited unexpectedly with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800569 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800570 if (autorestart()) {
571 QueueStart();
572 } else {
573 status_ = aos::starter::State::STOPPED;
574 on_change_();
575 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800576 break;
577 }
578 case aos::starter::State::STOPPING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700579 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
580 << "Successfully stopped '" << name_ << "' pid: " << pid_
581 << " with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800582 status_ = aos::starter::State::STOPPED;
583
584 // Disable force stop timer since the process already died
585 stop_timer_->Disable();
586
587 on_change_();
588 if (terminating_) {
589 return true;
590 }
591
592 if (queue_restart_) {
593 queue_restart_ = false;
594 status_ = aos::starter::State::WAITING;
595 DoStart();
596 }
597 break;
598 }
599 case aos::starter::State::WAITING:
600 case aos::starter::State::STOPPED: {
Sarah Newman21c59202022-06-16 12:36:33 -0700601 __builtin_unreachable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800602 break;
603 }
604 }
605
606 return false;
607}
608
609} // namespace aos::starter