blob: b1320f4932590be9849528532fd76dc8721f03c3 [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)
Austin Schuh1cea9032023-07-10 11:56:40 -070080 : SignalListener(loop->epoll(), std::move(callback)) {}
81
82SignalListener::SignalListener(aos::internal::EPoll *epoll,
83 std::function<void(signalfd_siginfo)> callback)
84 : SignalListener(epoll, callback,
James Kuszmaul3224b8e2022-01-07 19:00:39 -080085 {SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGFPE, SIGSEGV,
86 SIGPIPE, SIGTERM, SIGBUS, SIGXCPU, SIGCHLD}) {}
87
88SignalListener::SignalListener(aos::ShmEventLoop *loop,
89 std::function<void(signalfd_siginfo)> callback,
90 std::initializer_list<unsigned int> signals)
Austin Schuh1cea9032023-07-10 11:56:40 -070091 : SignalListener(loop->epoll(), std::move(callback), std::move(signals)) {}
92
93SignalListener::SignalListener(aos::internal::EPoll *epoll,
94 std::function<void(signalfd_siginfo)> callback,
95 std::initializer_list<unsigned int> signals)
96 : epoll_(epoll), callback_(std::move(callback)), signalfd_(signals) {
97 epoll_->OnReadable(signalfd_.fd(), [this] {
James Kuszmaul3224b8e2022-01-07 19:00:39 -080098 signalfd_siginfo info = signalfd_.Read();
99
100 if (info.ssi_signo == 0) {
101 LOG(WARNING) << "Could not read " << sizeof(signalfd_siginfo) << " bytes";
102 return;
103 }
104
105 callback_(info);
106 });
107}
108
Austin Schuh1cea9032023-07-10 11:56:40 -0700109SignalListener::~SignalListener() { epoll_->DeleteFd(signalfd_.fd()); }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800110
James Kuszmauld42edb42022-01-07 18:00:16 -0800111Application::Application(std::string_view name,
112 std::string_view executable_name,
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800113 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700114 std::function<void()> on_change,
115 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800116 : name_(name),
117 path_(executable_name),
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800118 event_loop_(event_loop),
119 start_timer_(event_loop_->AddTimer([this] {
120 status_ = aos::starter::State::RUNNING;
payton.rehl2841b1c2023-05-25 17:23:55 -0700121 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
122 << "Started '" << name_ << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800123 })),
124 restart_timer_(event_loop_->AddTimer([this] { DoStart(); })),
125 stop_timer_(event_loop_->AddTimer([this] {
126 if (kill(pid_, SIGKILL) == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700127 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
128 << "Failed to stop, sending SIGKILL to '" << name_
129 << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800130 }
131 })),
James Kuszmauld42edb42022-01-07 18:00:16 -0800132 pipe_timer_(event_loop_->AddTimer([this]() { FetchOutputs(); })),
133 child_status_handler_(
134 event_loop_->AddTimer([this]() { MaybeHandleSignal(); })),
Austin Schuh1cea9032023-07-10 11:56:40 -0700135 on_change_({on_change}),
payton.rehl2841b1c2023-05-25 17:23:55 -0700136 quiet_flag_(quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800137 event_loop_->OnRun([this]() {
138 // Every second poll to check if the child is dead. This is used as a
139 // default for the case where the user is not directly catching SIGCHLD and
140 // calling MaybeHandleSignal for us.
Philipp Schradera6712522023-07-05 20:25:11 -0700141 child_status_handler_->Schedule(event_loop_->monotonic_now(),
142 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800143 });
144}
145
146Application::Application(const aos::Application *application,
147 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700148 std::function<void()> on_change,
149 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800150 : Application(application->name()->string_view(),
151 application->has_executable_name()
152 ? application->executable_name()->string_view()
153 : application->name()->string_view(),
payton.rehl2841b1c2023-05-25 17:23:55 -0700154 event_loop, on_change, quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800155 user_name_ = application->has_user() ? application->user()->str() : "";
156 user_ = application->has_user() ? FindUid(user_name_.c_str()) : std::nullopt;
157 group_ = application->has_user() ? FindPrimaryGidForUser(user_name_.c_str())
158 : std::nullopt;
159 autostart_ = application->autostart();
160 autorestart_ = application->autorestart();
161 if (application->has_args()) {
162 set_args(*application->args());
163 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700164
165 if (application->has_memory_limit() && application->memory_limit() > 0) {
166 SetMemoryLimit(application->memory_limit());
167 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800168}
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800169
170void Application::DoStart() {
171 if (status_ != aos::starter::State::WAITING) {
172 return;
173 }
174
175 start_timer_->Disable();
176 restart_timer_->Disable();
177
James Kuszmauld42edb42022-01-07 18:00:16 -0800178 status_pipes_ = util::ScopedPipe::MakePipe();
179
180 if (capture_stdout_) {
181 stdout_pipes_ = util::ScopedPipe::MakePipe();
182 stdout_.clear();
183 }
184 if (capture_stderr_) {
185 stderr_pipes_ = util::ScopedPipe::MakePipe();
186 stderr_.clear();
187 }
188
Philipp Schradera6712522023-07-05 20:25:11 -0700189 pipe_timer_->Schedule(event_loop_->monotonic_now(),
190 std::chrono::milliseconds(100));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800191
192 const pid_t pid = fork();
193
194 if (pid != 0) {
195 if (pid == -1) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700196 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
197 << "Failed to fork '" << name_ << "'";
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800198 stop_reason_ = aos::starter::LastStopReason::FORK_ERR;
199 status_ = aos::starter::State::STOPPED;
200 } else {
201 pid_ = pid;
202 id_ = next_id_++;
203 start_time_ = event_loop_->monotonic_now();
204 status_ = aos::starter::State::STARTING;
payton.rehl2841b1c2023-05-25 17:23:55 -0700205 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
206 << "Starting '" << name_ << "' pid " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800207
Philipp Schradera6712522023-07-05 20:25:11 -0700208 // Set up timer which moves application to RUNNING state if it is still
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800209 // alive in 1 second.
Philipp Schradera6712522023-07-05 20:25:11 -0700210 start_timer_->Schedule(event_loop_->monotonic_now() +
211 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800212 // Since we are the parent process, clear our write-side of all the pipes.
213 status_pipes_.write.reset();
214 stdout_pipes_.write.reset();
215 stderr_pipes_.write.reset();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800216 }
Austin Schuh1cea9032023-07-10 11:56:40 -0700217 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800218 return;
219 }
220
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700221 if (memory_cgroup_) {
222 memory_cgroup_->AddTid();
223 }
224
James Kuszmauld42edb42022-01-07 18:00:16 -0800225 // Since we are the child process, clear our read-side of all the pipes.
226 status_pipes_.read.reset();
227 stdout_pipes_.read.reset();
228 stderr_pipes_.read.reset();
229
230 // The status pipe will not be needed if the execve succeeds.
231 status_pipes_.write->SetCloexec();
232
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800233 // Clear out signal mask of parent so forked process receives all signals
234 // normally.
235 sigset_t empty_mask;
236 sigemptyset(&empty_mask);
237 sigprocmask(SIG_SETMASK, &empty_mask, nullptr);
238
239 // Cleanup children if starter dies in a way that is not handled gracefully.
240 if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800241 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800242 static_cast<uint32_t>(aos::starter::LastStopReason::SET_PRCTL_ERR));
243 PLOG(FATAL) << "Could not set PR_SET_PDEATHSIG to SIGKILL";
244 }
245
246 if (group_) {
247 CHECK(!user_name_.empty());
248 // The manpage for setgroups says we just need CAP_SETGID, but empirically
249 // we also need the effective UID to be 0 to make it work. user_ must also
250 // be set so we change this effective UID back later.
251 CHECK(user_);
252 if (seteuid(0) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800253 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800254 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
255 PLOG(FATAL) << "Could not seteuid(0) for " << name_
256 << " in preparation for setting groups";
257 }
258 if (initgroups(user_name_.c_str(), *group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800259 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800260 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
261 PLOG(FATAL) << "Could not initialize normal groups for " << name_
262 << " as " << user_name_ << " with " << *group_;
263 }
264 if (setgid(*group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800265 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800266 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
267 PLOG(FATAL) << "Could not set group for " << name_ << " to " << *group_;
268 }
269 }
270
271 if (user_) {
272 if (setuid(*user_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800273 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800274 static_cast<uint32_t>(aos::starter::LastStopReason::SET_USR_ERR));
275 PLOG(FATAL) << "Could not set user for " << name_ << " to " << *user_;
276 }
277 }
278
James Kuszmauld42edb42022-01-07 18:00:16 -0800279 if (capture_stdout_) {
280 PCHECK(STDOUT_FILENO == dup2(stdout_pipes_.write->fd(), STDOUT_FILENO));
281 stdout_pipes_.write.reset();
282 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800283
James Kuszmauld42edb42022-01-07 18:00:16 -0800284 if (capture_stderr_) {
285 PCHECK(STDERR_FILENO == dup2(stderr_pipes_.write->fd(), STDERR_FILENO));
286 stderr_pipes_.write.reset();
287 }
288
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700289 if (run_as_sudo_) {
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700290 // For sudo we must supply the actual path
291 args_.insert(args_.begin(), path_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700292 args_.insert(args_.begin(), kSudo);
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700293 } else {
294 // argv[0] should be the program name
295 args_.insert(args_.begin(), name_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700296 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800297
298 std::vector<char *> cargs = CArgs();
Philipp Schrader790cb542023-07-05 21:06:52 -0700299 const char *path = run_as_sudo_ ? kSudo : path_.c_str();
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700300 execvp(path, cargs.data());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800301
302 // If we got here, something went wrong
James Kuszmauld42edb42022-01-07 18:00:16 -0800303 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800304 static_cast<uint32_t>(aos::starter::LastStopReason::EXECV_ERR));
payton.rehl2841b1c2023-05-25 17:23:55 -0700305 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
306 << "Could not execute " << name_ << " (" << path_ << ')';
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800307
308 _exit(EXIT_FAILURE);
309}
310
James Kuszmauld42edb42022-01-07 18:00:16 -0800311void Application::FetchOutputs() {
312 if (capture_stdout_) {
313 stdout_pipes_.read->Read(&stdout_);
314 }
315 if (capture_stderr_) {
316 stderr_pipes_.read->Read(&stderr_);
317 }
318}
319
320const std::string &Application::GetStdout() {
321 CHECK(capture_stdout_);
322 FetchOutputs();
323 return stdout_;
324}
325
326const std::string &Application::GetStderr() {
327 CHECK(capture_stderr_);
328 FetchOutputs();
329 return stderr_;
330}
331
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800332void Application::DoStop(bool restart) {
333 // If stop or restart received, the old state of these is no longer applicable
334 // so cancel both.
335 restart_timer_->Disable();
336 start_timer_->Disable();
337
James Kuszmauld42edb42022-01-07 18:00:16 -0800338 FetchOutputs();
339
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800340 switch (status_) {
341 case aos::starter::State::STARTING:
342 case aos::starter::State::RUNNING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700343 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
344 << "Stopping '" << name_ << "' pid: " << pid_ << " with signal "
345 << SIGINT;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800346 status_ = aos::starter::State::STOPPING;
347
348 kill(pid_, SIGINT);
349
350 // Watchdog timer to SIGKILL application if it is still running 1 second
351 // after SIGINT
Philipp Schradera6712522023-07-05 20:25:11 -0700352 stop_timer_->Schedule(event_loop_->monotonic_now() +
353 std::chrono::seconds(1));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800354 queue_restart_ = restart;
Austin Schuh1cea9032023-07-10 11:56:40 -0700355 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800356 break;
357 }
358 case aos::starter::State::WAITING: {
359 // If waiting to restart, and receives restart, skip the waiting period
360 // and restart immediately. If stop received, all we have to do is move
361 // to the STOPPED state.
362 if (restart) {
363 DoStart();
364 } else {
365 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700366 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800367 }
368 break;
369 }
370 case aos::starter::State::STOPPING: {
371 // If the application is already stopping, then we just need to update the
372 // restart flag to the most recent status.
373 queue_restart_ = restart;
374 break;
375 }
376 case aos::starter::State::STOPPED: {
377 // Restart immediately if the application is already stopped
378 if (restart) {
379 status_ = aos::starter::State::WAITING;
380 DoStart();
381 }
382 break;
383 }
384 }
385}
386
387void Application::QueueStart() {
388 status_ = aos::starter::State::WAITING;
389
payton.rehl2841b1c2023-05-25 17:23:55 -0700390 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
391 << "Restarting " << name_ << " in 3 seconds";
Philipp Schradera6712522023-07-05 20:25:11 -0700392 restart_timer_->Schedule(event_loop_->monotonic_now() +
393 std::chrono::seconds(3));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800394 start_timer_->Disable();
395 stop_timer_->Disable();
Austin Schuh1cea9032023-07-10 11:56:40 -0700396 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800397}
398
James Kuszmauld42edb42022-01-07 18:00:16 -0800399std::vector<char *> Application::CArgs() {
400 std::vector<char *> cargs;
401 std::transform(args_.begin(), args_.end(), std::back_inserter(cargs),
402 [](std::string &str) { return str.data(); });
403 cargs.push_back(nullptr);
404 return cargs;
405}
406
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800407void Application::set_args(
408 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v) {
409 args_.clear();
410 std::transform(v.begin(), v.end(), std::back_inserter(args_),
James Kuszmauld42edb42022-01-07 18:00:16 -0800411 [](const flatbuffers::String *str) { return str->str(); });
412}
413
414void Application::set_args(std::vector<std::string> args) {
415 args_ = std::move(args);
416}
417
418void Application::set_capture_stdout(bool capture) {
419 capture_stdout_ = capture;
420}
421
422void Application::set_capture_stderr(bool capture) {
423 capture_stderr_ = capture;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800424}
425
426std::optional<uid_t> Application::FindUid(const char *name) {
427 // TODO(austin): Use the reentrant version. This should be safe.
428 struct passwd *user_data = getpwnam(name);
429 if (user_data != nullptr) {
430 return user_data->pw_uid;
431 } else {
432 LOG(FATAL) << "Could not find user " << name;
433 return std::nullopt;
434 }
435}
436
437std::optional<gid_t> Application::FindPrimaryGidForUser(const char *name) {
438 // TODO(austin): Use the reentrant version. This should be safe.
439 struct passwd *user_data = getpwnam(name);
440 if (user_data != nullptr) {
441 return user_data->pw_gid;
442 } else {
443 LOG(FATAL) << "Could not find user " << name;
444 return std::nullopt;
445 }
446}
447
448flatbuffers::Offset<aos::starter::ApplicationStatus>
James Kuszmaul6295a642022-03-22 15:23:59 -0700449Application::PopulateStatus(flatbuffers::FlatBufferBuilder *builder,
450 util::Top *top) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800451 CHECK_NOTNULL(builder);
452 auto name_fbs = builder->CreateString(name_);
453
James Kuszmaul6295a642022-03-22 15:23:59 -0700454 const bool valid_pid = pid_ > 0 && status_ != aos::starter::State::STOPPED;
455 const flatbuffers::Offset<util::ProcessInfo> process_info =
456 valid_pid ? top->InfoForProcess(builder, pid_)
457 : flatbuffers::Offset<util::ProcessInfo>();
458
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800459 aos::starter::ApplicationStatus::Builder status_builder(*builder);
460 status_builder.add_name(name_fbs);
461 status_builder.add_state(status_);
James Kuszmauld42edb42022-01-07 18:00:16 -0800462 if (exit_code_.has_value()) {
463 status_builder.add_last_exit_code(exit_code_.value());
464 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800465 status_builder.add_last_stop_reason(stop_reason_);
466 if (pid_ != -1) {
467 status_builder.add_pid(pid_);
468 status_builder.add_id(id_);
469 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700470 // Note that even if process_info is null, calling add_process_info is fine.
471 status_builder.add_process_info(process_info);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800472 status_builder.add_last_start_time(start_time_.time_since_epoch().count());
473 return status_builder.Finish();
474}
475
476void Application::Terminate() {
477 stop_reason_ = aos::starter::LastStopReason::TERMINATE;
478 DoStop(false);
479 terminating_ = true;
480}
481
482void Application::HandleCommand(aos::starter::Command cmd) {
483 switch (cmd) {
484 case aos::starter::Command::START: {
485 switch (status_) {
486 case aos::starter::State::WAITING: {
487 restart_timer_->Disable();
488 DoStart();
489 break;
490 }
491 case aos::starter::State::STARTING: {
492 break;
493 }
494 case aos::starter::State::RUNNING: {
495 break;
496 }
497 case aos::starter::State::STOPPING: {
498 queue_restart_ = true;
499 break;
500 }
501 case aos::starter::State::STOPPED: {
502 status_ = aos::starter::State::WAITING;
503 DoStart();
504 break;
505 }
506 }
507 break;
508 }
509 case aos::starter::Command::STOP: {
510 stop_reason_ = aos::starter::LastStopReason::STOP_REQUESTED;
511 DoStop(false);
512 break;
513 }
514 case aos::starter::Command::RESTART: {
515 stop_reason_ = aos::starter::LastStopReason::RESTART_REQUESTED;
516 DoStop(true);
517 break;
518 }
519 }
520}
521
522bool Application::MaybeHandleSignal() {
523 int status;
524
Sarah Newman21c59202022-06-16 12:36:33 -0700525 if (status_ == aos::starter::State::WAITING ||
526 status_ == aos::starter::State::STOPPED) {
527 // We can't possibly have received a signal meant for this process.
528 return false;
529 }
530
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800531 // Check if the status of this process has changed
Sarah Newman21c59202022-06-16 12:36:33 -0700532 // The PID won't be -1 if this application has ever been run successfully
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800533 if (pid_ == -1 || waitpid(pid_, &status, WNOHANG) != pid_) {
534 return false;
535 }
536
537 // Check that the event was the process exiting
538 if (!WIFEXITED(status) && !WIFSIGNALED(status)) {
539 return false;
540 }
541
James Kuszmauld42edb42022-01-07 18:00:16 -0800542 start_timer_->Disable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800543 exit_time_ = event_loop_->monotonic_now();
544 exit_code_ = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status);
545
James Kuszmauld42edb42022-01-07 18:00:16 -0800546 if (auto read_result = status_pipes_.read->Read()) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800547 stop_reason_ = static_cast<aos::starter::LastStopReason>(*read_result);
548 }
549
550 switch (status_) {
551 case aos::starter::State::STARTING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800552 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700553 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
554 << "Application '" << name_ << "' pid " << pid_
555 << " exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800556 } else {
payton.rehl2841b1c2023-05-25 17:23:55 -0700557 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
558 << "Failed to start '" << name_ << "' on pid " << pid_
559 << " : Exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800560 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800561 if (autorestart()) {
562 QueueStart();
563 } else {
564 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700565 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800566 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800567 break;
568 }
569 case aos::starter::State::RUNNING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800570 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700571 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
572 << "Application '" << name_ << "' pid " << pid_
573 << " exited with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800574 } else {
payton.rehl2841b1c2023-05-25 17:23:55 -0700575 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
576 << "Application '" << name_ << "' pid " << pid_
577 << " exited unexpectedly with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800578 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800579 if (autorestart()) {
580 QueueStart();
581 } else {
582 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700583 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800584 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800585 break;
586 }
587 case aos::starter::State::STOPPING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700588 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
589 << "Successfully stopped '" << name_ << "' pid: " << pid_
590 << " with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800591 status_ = aos::starter::State::STOPPED;
592
593 // Disable force stop timer since the process already died
594 stop_timer_->Disable();
595
Austin Schuh1cea9032023-07-10 11:56:40 -0700596 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800597 if (terminating_) {
598 return true;
599 }
600
601 if (queue_restart_) {
602 queue_restart_ = false;
603 status_ = aos::starter::State::WAITING;
604 DoStart();
605 }
606 break;
607 }
608 case aos::starter::State::WAITING:
609 case aos::starter::State::STOPPED: {
Sarah Newman21c59202022-06-16 12:36:33 -0700610 __builtin_unreachable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800611 break;
612 }
613 }
614
615 return false;
616}
617
Austin Schuh1cea9032023-07-10 11:56:40 -0700618void Application::OnChange() {
619 for (auto &fn : on_change_) {
620 fn();
621 }
622}
623
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800624} // namespace aos::starter