blob: a518c0be457af62b1a8894979bf594bc48811b11 [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
41MemoryCGroup::MemoryCGroup(std::string_view name)
42 : cgroup_(absl::StrCat("/sys/fs/cgroup/memory/aos_", name)) {
43 Sudo sudo;
44 int ret = mkdir(cgroup_.c_str(), 0755);
45
46 if (ret != 0) {
47 if (errno == EEXIST) {
Austin Schuh4d8a46e2022-10-07 19:20:20 -070048 PCHECK(rmdir(cgroup_.c_str()) == 0)
Austin Schuhbbeb37e2022-08-17 16:19:27 -070049 << ": Failed to remove previous cgroup " << cgroup_;
50 ret = mkdir(cgroup_.c_str(), 0755);
51 }
52 }
53
54 if (ret != 0) {
55 PLOG(FATAL) << ": Failed to create cgroup aos_" << cgroup_
56 << ", do you have permission?";
57 }
58}
59
60void MemoryCGroup::AddTid(pid_t pid) {
61 if (pid == 0) {
62 pid = getpid();
63 }
64 Sudo sudo;
65 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
66 std::to_string(pid));
67}
68
69void MemoryCGroup::SetLimit(std::string_view limit_name, uint64_t limit_value) {
70 Sudo sudo;
71 util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
72 std::to_string(limit_value));
73}
74
75MemoryCGroup::~MemoryCGroup() {
76 Sudo sudo;
77 PCHECK(rmdir(absl::StrCat(cgroup_).c_str()) == 0);
78}
79
James Kuszmaul3224b8e2022-01-07 19:00:39 -080080SignalListener::SignalListener(aos::ShmEventLoop *loop,
81 std::function<void(signalfd_siginfo)> callback)
Austin Schuh1cea9032023-07-10 11:56:40 -070082 : SignalListener(loop->epoll(), std::move(callback)) {}
83
84SignalListener::SignalListener(aos::internal::EPoll *epoll,
85 std::function<void(signalfd_siginfo)> callback)
86 : SignalListener(epoll, callback,
James Kuszmaul3224b8e2022-01-07 19:00:39 -080087 {SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGFPE, SIGSEGV,
88 SIGPIPE, SIGTERM, SIGBUS, SIGXCPU, SIGCHLD}) {}
89
90SignalListener::SignalListener(aos::ShmEventLoop *loop,
91 std::function<void(signalfd_siginfo)> callback,
92 std::initializer_list<unsigned int> signals)
Austin Schuh1cea9032023-07-10 11:56:40 -070093 : SignalListener(loop->epoll(), std::move(callback), std::move(signals)) {}
94
95SignalListener::SignalListener(aos::internal::EPoll *epoll,
96 std::function<void(signalfd_siginfo)> callback,
97 std::initializer_list<unsigned int> signals)
98 : epoll_(epoll), callback_(std::move(callback)), signalfd_(signals) {
99 epoll_->OnReadable(signalfd_.fd(), [this] {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800100 signalfd_siginfo info = signalfd_.Read();
101
102 if (info.ssi_signo == 0) {
103 LOG(WARNING) << "Could not read " << sizeof(signalfd_siginfo) << " bytes";
104 return;
105 }
106
107 callback_(info);
108 });
109}
110
Austin Schuh1cea9032023-07-10 11:56:40 -0700111SignalListener::~SignalListener() { epoll_->DeleteFd(signalfd_.fd()); }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800112
James Kuszmauld42edb42022-01-07 18:00:16 -0800113Application::Application(std::string_view name,
114 std::string_view executable_name,
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800115 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700116 std::function<void()> on_change,
117 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800118 : name_(name),
119 path_(executable_name),
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800120 event_loop_(event_loop),
121 start_timer_(event_loop_->AddTimer([this] {
122 status_ = aos::starter::State::RUNNING;
payton.rehl2841b1c2023-05-25 17:23:55 -0700123 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
124 << "Started '" << name_ << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800125 })),
126 restart_timer_(event_loop_->AddTimer([this] { DoStart(); })),
127 stop_timer_(event_loop_->AddTimer([this] {
128 if (kill(pid_, SIGKILL) == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700129 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
130 << "Failed to stop, sending SIGKILL to '" << name_
131 << "' pid: " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800132 }
133 })),
James Kuszmauld42edb42022-01-07 18:00:16 -0800134 pipe_timer_(event_loop_->AddTimer([this]() { FetchOutputs(); })),
135 child_status_handler_(
136 event_loop_->AddTimer([this]() { MaybeHandleSignal(); })),
Austin Schuh1cea9032023-07-10 11:56:40 -0700137 on_change_({on_change}),
payton.rehl2841b1c2023-05-25 17:23:55 -0700138 quiet_flag_(quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800139 event_loop_->OnRun([this]() {
140 // Every second poll to check if the child is dead. This is used as a
141 // default for the case where the user is not directly catching SIGCHLD and
142 // calling MaybeHandleSignal for us.
Philipp Schradera6712522023-07-05 20:25:11 -0700143 child_status_handler_->Schedule(event_loop_->monotonic_now(),
144 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800145 });
146}
147
148Application::Application(const aos::Application *application,
149 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700150 std::function<void()> on_change,
151 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800152 : Application(application->name()->string_view(),
153 application->has_executable_name()
154 ? application->executable_name()->string_view()
155 : application->name()->string_view(),
payton.rehl2841b1c2023-05-25 17:23:55 -0700156 event_loop, on_change, quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800157 user_name_ = application->has_user() ? application->user()->str() : "";
158 user_ = application->has_user() ? FindUid(user_name_.c_str()) : std::nullopt;
159 group_ = application->has_user() ? FindPrimaryGidForUser(user_name_.c_str())
160 : std::nullopt;
161 autostart_ = application->autostart();
162 autorestart_ = application->autorestart();
163 if (application->has_args()) {
164 set_args(*application->args());
165 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700166
167 if (application->has_memory_limit() && application->memory_limit() > 0) {
168 SetMemoryLimit(application->memory_limit());
169 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800170}
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800171
172void Application::DoStart() {
173 if (status_ != aos::starter::State::WAITING) {
174 return;
175 }
176
177 start_timer_->Disable();
178 restart_timer_->Disable();
179
James Kuszmauld42edb42022-01-07 18:00:16 -0800180 status_pipes_ = util::ScopedPipe::MakePipe();
181
182 if (capture_stdout_) {
183 stdout_pipes_ = util::ScopedPipe::MakePipe();
184 stdout_.clear();
185 }
186 if (capture_stderr_) {
187 stderr_pipes_ = util::ScopedPipe::MakePipe();
188 stderr_.clear();
189 }
190
Philipp Schradera6712522023-07-05 20:25:11 -0700191 pipe_timer_->Schedule(event_loop_->monotonic_now(),
192 std::chrono::milliseconds(100));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800193
194 const pid_t pid = fork();
195
196 if (pid != 0) {
197 if (pid == -1) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700198 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
199 << "Failed to fork '" << name_ << "'";
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800200 stop_reason_ = aos::starter::LastStopReason::FORK_ERR;
201 status_ = aos::starter::State::STOPPED;
202 } else {
203 pid_ = pid;
204 id_ = next_id_++;
205 start_time_ = event_loop_->monotonic_now();
206 status_ = aos::starter::State::STARTING;
James Kuszmaul8544c492023-07-31 15:00:38 -0700207 latest_timing_report_version_.reset();
payton.rehl2841b1c2023-05-25 17:23:55 -0700208 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
209 << "Starting '" << name_ << "' pid " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800210
Philipp Schradera6712522023-07-05 20:25:11 -0700211 // Set up timer which moves application to RUNNING state if it is still
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800212 // alive in 1 second.
Philipp Schradera6712522023-07-05 20:25:11 -0700213 start_timer_->Schedule(event_loop_->monotonic_now() +
214 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800215 // Since we are the parent process, clear our write-side of all the pipes.
216 status_pipes_.write.reset();
217 stdout_pipes_.write.reset();
218 stderr_pipes_.write.reset();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800219 }
Austin Schuh1cea9032023-07-10 11:56:40 -0700220 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800221 return;
222 }
223
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700224 if (memory_cgroup_) {
225 memory_cgroup_->AddTid();
226 }
227
James Kuszmauld42edb42022-01-07 18:00:16 -0800228 // Since we are the child process, clear our read-side of all the pipes.
229 status_pipes_.read.reset();
230 stdout_pipes_.read.reset();
231 stderr_pipes_.read.reset();
232
233 // The status pipe will not be needed if the execve succeeds.
234 status_pipes_.write->SetCloexec();
235
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800236 // Clear out signal mask of parent so forked process receives all signals
237 // normally.
238 sigset_t empty_mask;
239 sigemptyset(&empty_mask);
240 sigprocmask(SIG_SETMASK, &empty_mask, nullptr);
241
242 // Cleanup children if starter dies in a way that is not handled gracefully.
243 if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -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_PRCTL_ERR));
246 PLOG(FATAL) << "Could not set PR_SET_PDEATHSIG to SIGKILL";
247 }
248
249 if (group_) {
250 CHECK(!user_name_.empty());
251 // The manpage for setgroups says we just need CAP_SETGID, but empirically
252 // we also need the effective UID to be 0 to make it work. user_ must also
253 // be set so we change this effective UID back later.
254 CHECK(user_);
255 if (seteuid(0) == -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 seteuid(0) for " << name_
259 << " in preparation for setting groups";
260 }
261 if (initgroups(user_name_.c_str(), *group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800262 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800263 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
264 PLOG(FATAL) << "Could not initialize normal groups for " << name_
265 << " as " << user_name_ << " with " << *group_;
266 }
267 if (setgid(*group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800268 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800269 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
270 PLOG(FATAL) << "Could not set group for " << name_ << " to " << *group_;
271 }
272 }
273
274 if (user_) {
275 if (setuid(*user_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800276 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800277 static_cast<uint32_t>(aos::starter::LastStopReason::SET_USR_ERR));
278 PLOG(FATAL) << "Could not set user for " << name_ << " to " << *user_;
279 }
280 }
281
James Kuszmauld42edb42022-01-07 18:00:16 -0800282 if (capture_stdout_) {
283 PCHECK(STDOUT_FILENO == dup2(stdout_pipes_.write->fd(), STDOUT_FILENO));
284 stdout_pipes_.write.reset();
285 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800286
James Kuszmauld42edb42022-01-07 18:00:16 -0800287 if (capture_stderr_) {
288 PCHECK(STDERR_FILENO == dup2(stderr_pipes_.write->fd(), STDERR_FILENO));
289 stderr_pipes_.write.reset();
290 }
291
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700292 if (run_as_sudo_) {
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700293 // For sudo we must supply the actual path
294 args_.insert(args_.begin(), path_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700295 args_.insert(args_.begin(), kSudo);
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700296 } else {
297 // argv[0] should be the program name
298 args_.insert(args_.begin(), name_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700299 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800300
301 std::vector<char *> cargs = CArgs();
Philipp Schrader790cb542023-07-05 21:06:52 -0700302 const char *path = run_as_sudo_ ? kSudo : path_.c_str();
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700303 execvp(path, cargs.data());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800304
305 // If we got here, something went wrong
James Kuszmauld42edb42022-01-07 18:00:16 -0800306 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800307 static_cast<uint32_t>(aos::starter::LastStopReason::EXECV_ERR));
payton.rehl2841b1c2023-05-25 17:23:55 -0700308 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
309 << "Could not execute " << name_ << " (" << path_ << ')';
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800310
311 _exit(EXIT_FAILURE);
312}
313
James Kuszmaul8544c492023-07-31 15:00:38 -0700314void Application::ObserveTimingReport(
315 const aos::monotonic_clock::time_point send_time,
316 const aos::timing::Report *msg) {
317 if (msg->name()->string_view() == name_ && msg->pid() == pid_ &&
318 msg->has_version()) {
319 latest_timing_report_version_ = msg->version()->str();
320 last_timing_report_ = send_time;
321 }
322}
323
James Kuszmauld42edb42022-01-07 18:00:16 -0800324void Application::FetchOutputs() {
325 if (capture_stdout_) {
326 stdout_pipes_.read->Read(&stdout_);
327 }
328 if (capture_stderr_) {
329 stderr_pipes_.read->Read(&stderr_);
330 }
331}
332
333const std::string &Application::GetStdout() {
334 CHECK(capture_stdout_);
335 FetchOutputs();
336 return stdout_;
337}
338
339const std::string &Application::GetStderr() {
340 CHECK(capture_stderr_);
341 FetchOutputs();
342 return stderr_;
343}
344
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800345void Application::DoStop(bool restart) {
346 // If stop or restart received, the old state of these is no longer applicable
347 // so cancel both.
348 restart_timer_->Disable();
349 start_timer_->Disable();
350
James Kuszmauld42edb42022-01-07 18:00:16 -0800351 FetchOutputs();
352
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800353 switch (status_) {
354 case aos::starter::State::STARTING:
355 case aos::starter::State::RUNNING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700356 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
357 << "Stopping '" << name_ << "' pid: " << pid_ << " with signal "
358 << SIGINT;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800359 status_ = aos::starter::State::STOPPING;
360
361 kill(pid_, SIGINT);
362
363 // Watchdog timer to SIGKILL application if it is still running 1 second
364 // after SIGINT
Philipp Schradera6712522023-07-05 20:25:11 -0700365 stop_timer_->Schedule(event_loop_->monotonic_now() +
366 std::chrono::seconds(1));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800367 queue_restart_ = restart;
Austin Schuh1cea9032023-07-10 11:56:40 -0700368 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800369 break;
370 }
371 case aos::starter::State::WAITING: {
372 // If waiting to restart, and receives restart, skip the waiting period
373 // and restart immediately. If stop received, all we have to do is move
374 // to the STOPPED state.
375 if (restart) {
376 DoStart();
377 } else {
378 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700379 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800380 }
381 break;
382 }
383 case aos::starter::State::STOPPING: {
384 // If the application is already stopping, then we just need to update the
385 // restart flag to the most recent status.
386 queue_restart_ = restart;
387 break;
388 }
389 case aos::starter::State::STOPPED: {
390 // Restart immediately if the application is already stopped
391 if (restart) {
392 status_ = aos::starter::State::WAITING;
393 DoStart();
394 }
395 break;
396 }
397 }
398}
399
400void Application::QueueStart() {
401 status_ = aos::starter::State::WAITING;
402
payton.rehl2841b1c2023-05-25 17:23:55 -0700403 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
404 << "Restarting " << name_ << " in 3 seconds";
Philipp Schradera6712522023-07-05 20:25:11 -0700405 restart_timer_->Schedule(event_loop_->monotonic_now() +
406 std::chrono::seconds(3));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800407 start_timer_->Disable();
408 stop_timer_->Disable();
Austin Schuh1cea9032023-07-10 11:56:40 -0700409 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800410}
411
James Kuszmauld42edb42022-01-07 18:00:16 -0800412std::vector<char *> Application::CArgs() {
413 std::vector<char *> cargs;
414 std::transform(args_.begin(), args_.end(), std::back_inserter(cargs),
415 [](std::string &str) { return str.data(); });
416 cargs.push_back(nullptr);
417 return cargs;
418}
419
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800420void Application::set_args(
421 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v) {
422 args_.clear();
423 std::transform(v.begin(), v.end(), std::back_inserter(args_),
James Kuszmauld42edb42022-01-07 18:00:16 -0800424 [](const flatbuffers::String *str) { return str->str(); });
425}
426
427void Application::set_args(std::vector<std::string> args) {
428 args_ = std::move(args);
429}
430
431void Application::set_capture_stdout(bool capture) {
432 capture_stdout_ = capture;
433}
434
435void Application::set_capture_stderr(bool capture) {
436 capture_stderr_ = capture;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800437}
438
439std::optional<uid_t> Application::FindUid(const char *name) {
440 // TODO(austin): Use the reentrant version. This should be safe.
441 struct passwd *user_data = getpwnam(name);
442 if (user_data != nullptr) {
443 return user_data->pw_uid;
444 } else {
445 LOG(FATAL) << "Could not find user " << name;
446 return std::nullopt;
447 }
448}
449
450std::optional<gid_t> Application::FindPrimaryGidForUser(const char *name) {
451 // TODO(austin): Use the reentrant version. This should be safe.
452 struct passwd *user_data = getpwnam(name);
453 if (user_data != nullptr) {
454 return user_data->pw_gid;
455 } else {
456 LOG(FATAL) << "Could not find user " << name;
457 return std::nullopt;
458 }
459}
460
461flatbuffers::Offset<aos::starter::ApplicationStatus>
James Kuszmaul6295a642022-03-22 15:23:59 -0700462Application::PopulateStatus(flatbuffers::FlatBufferBuilder *builder,
463 util::Top *top) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800464 CHECK_NOTNULL(builder);
465 auto name_fbs = builder->CreateString(name_);
466
James Kuszmaul6295a642022-03-22 15:23:59 -0700467 const bool valid_pid = pid_ > 0 && status_ != aos::starter::State::STOPPED;
468 const flatbuffers::Offset<util::ProcessInfo> process_info =
469 valid_pid ? top->InfoForProcess(builder, pid_)
470 : flatbuffers::Offset<util::ProcessInfo>();
471
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800472 aos::starter::ApplicationStatus::Builder status_builder(*builder);
473 status_builder.add_name(name_fbs);
474 status_builder.add_state(status_);
James Kuszmauld42edb42022-01-07 18:00:16 -0800475 if (exit_code_.has_value()) {
476 status_builder.add_last_exit_code(exit_code_.value());
477 }
James Kuszmaul8544c492023-07-31 15:00:38 -0700478 status_builder.add_has_active_timing_report(
479 last_timing_report_ +
480 // Leave a bit of margin on the timing report receipt time, to allow
481 // for timing errors.
482 3 * std::chrono::milliseconds(FLAGS_timing_report_ms) >
483 event_loop_->monotonic_now());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800484 status_builder.add_last_stop_reason(stop_reason_);
485 if (pid_ != -1) {
486 status_builder.add_pid(pid_);
487 status_builder.add_id(id_);
488 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700489 // Note that even if process_info is null, calling add_process_info is fine.
490 status_builder.add_process_info(process_info);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800491 status_builder.add_last_start_time(start_time_.time_since_epoch().count());
492 return status_builder.Finish();
493}
494
495void Application::Terminate() {
496 stop_reason_ = aos::starter::LastStopReason::TERMINATE;
497 DoStop(false);
498 terminating_ = true;
499}
500
501void Application::HandleCommand(aos::starter::Command cmd) {
502 switch (cmd) {
503 case aos::starter::Command::START: {
504 switch (status_) {
505 case aos::starter::State::WAITING: {
506 restart_timer_->Disable();
507 DoStart();
508 break;
509 }
510 case aos::starter::State::STARTING: {
511 break;
512 }
513 case aos::starter::State::RUNNING: {
514 break;
515 }
516 case aos::starter::State::STOPPING: {
517 queue_restart_ = true;
518 break;
519 }
520 case aos::starter::State::STOPPED: {
521 status_ = aos::starter::State::WAITING;
522 DoStart();
523 break;
524 }
525 }
526 break;
527 }
528 case aos::starter::Command::STOP: {
529 stop_reason_ = aos::starter::LastStopReason::STOP_REQUESTED;
530 DoStop(false);
531 break;
532 }
533 case aos::starter::Command::RESTART: {
534 stop_reason_ = aos::starter::LastStopReason::RESTART_REQUESTED;
535 DoStop(true);
536 break;
537 }
538 }
539}
540
541bool Application::MaybeHandleSignal() {
542 int status;
543
Sarah Newman21c59202022-06-16 12:36:33 -0700544 if (status_ == aos::starter::State::WAITING ||
545 status_ == aos::starter::State::STOPPED) {
546 // We can't possibly have received a signal meant for this process.
547 return false;
548 }
549
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800550 // Check if the status of this process has changed
Sarah Newman21c59202022-06-16 12:36:33 -0700551 // The PID won't be -1 if this application has ever been run successfully
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800552 if (pid_ == -1 || waitpid(pid_, &status, WNOHANG) != pid_) {
553 return false;
554 }
555
556 // Check that the event was the process exiting
557 if (!WIFEXITED(status) && !WIFSIGNALED(status)) {
558 return false;
559 }
560
James Kuszmauld42edb42022-01-07 18:00:16 -0800561 start_timer_->Disable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800562 exit_time_ = event_loop_->monotonic_now();
563 exit_code_ = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status);
564
James Kuszmauld42edb42022-01-07 18:00:16 -0800565 if (auto read_result = status_pipes_.read->Read()) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800566 stop_reason_ = static_cast<aos::starter::LastStopReason>(*read_result);
567 }
568
569 switch (status_) {
570 case aos::starter::State::STARTING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800571 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700572 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
573 << "Application '" << name_ << "' pid " << pid_
574 << " exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800575 } else {
payton.rehl2841b1c2023-05-25 17:23:55 -0700576 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
577 << "Failed to start '" << name_ << "' on pid " << pid_
578 << " : Exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800579 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800580 if (autorestart()) {
581 QueueStart();
582 } else {
583 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700584 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800585 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800586 break;
587 }
588 case aos::starter::State::RUNNING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800589 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700590 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
591 << "Application '" << name_ << "' pid " << pid_
592 << " exited with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800593 } else {
James Kuszmaul8544c492023-07-31 15:00:38 -0700594 if (quiet_flag_ == QuietLogging::kNo) {
595 std::string version_string =
596 latest_timing_report_version_.has_value()
597 ? absl::StrCat("'", latest_timing_report_version_.value(),
598 "'")
599 : "unknown";
600 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
601 << "Application '" << name_ << "' pid " << pid_ << " version "
602 << version_string << " exited unexpectedly with status "
603 << exit_code_.value();
604 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800605 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800606 if (autorestart()) {
607 QueueStart();
608 } else {
609 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700610 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800611 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800612 break;
613 }
614 case aos::starter::State::STOPPING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700615 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
616 << "Successfully stopped '" << name_ << "' pid: " << pid_
617 << " with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800618 status_ = aos::starter::State::STOPPED;
619
620 // Disable force stop timer since the process already died
621 stop_timer_->Disable();
622
Austin Schuh1cea9032023-07-10 11:56:40 -0700623 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800624 if (terminating_) {
625 return true;
626 }
627
628 if (queue_restart_) {
629 queue_restart_ = false;
630 status_ = aos::starter::State::WAITING;
631 DoStart();
632 }
633 break;
634 }
635 case aos::starter::State::WAITING:
636 case aos::starter::State::STOPPED: {
Sarah Newman21c59202022-06-16 12:36:33 -0700637 __builtin_unreachable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800638 break;
639 }
640 }
641
642 return false;
643}
644
Austin Schuh1cea9032023-07-10 11:56:40 -0700645void Application::OnChange() {
646 for (auto &fn : on_change_) {
647 fn();
648 }
649}
650
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800651} // namespace aos::starter