blob: 039f3d8bb7815c49eaca206b6a926170c36e6e79 [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) {
Philipp Schrader595979d2023-09-13 11:31:48 -0700144 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo ||
145 quiet_flag_ == QuietLogging::kNotForDebugging)
payton.rehl2841b1c2023-05-25 17:23:55 -0700146 << "Failed to stop, sending SIGKILL to '" << name_
147 << "' pid: " << pid_;
Sarah Newman9687e062023-09-08 12:22:27 -0700148 } else {
Philipp Schrader595979d2023-09-13 11:31:48 -0700149 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo ||
150 quiet_flag_ == QuietLogging::kNotForDebugging)
Sarah Newman9687e062023-09-08 12:22:27 -0700151 << "Failed to send SIGKILL to '" << name_ << "' pid: " << pid_;
152 stop_timer_->Schedule(event_loop_->monotonic_now() +
153 std::chrono::seconds(1));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800154 }
155 })),
James Kuszmauld42edb42022-01-07 18:00:16 -0800156 pipe_timer_(event_loop_->AddTimer([this]() { FetchOutputs(); })),
157 child_status_handler_(
158 event_loop_->AddTimer([this]() { MaybeHandleSignal(); })),
Austin Schuh1cea9032023-07-10 11:56:40 -0700159 on_change_({on_change}),
payton.rehl2841b1c2023-05-25 17:23:55 -0700160 quiet_flag_(quiet_flag) {
Sanjay Narayanan92fdc3d2023-08-25 14:42:56 -0700161 // Every second poll to check if the child is dead. This is used as a
162 // default for the case where the user is not directly catching SIGCHLD and
163 // calling MaybeHandleSignal for us.
164 child_status_handler_->Schedule(event_loop_->monotonic_now(),
165 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800166}
167
168Application::Application(const aos::Application *application,
169 aos::EventLoop *event_loop,
payton.rehl2841b1c2023-05-25 17:23:55 -0700170 std::function<void()> on_change,
171 QuietLogging quiet_flag)
James Kuszmauld42edb42022-01-07 18:00:16 -0800172 : Application(application->name()->string_view(),
173 application->has_executable_name()
174 ? application->executable_name()->string_view()
175 : application->name()->string_view(),
payton.rehl2841b1c2023-05-25 17:23:55 -0700176 event_loop, on_change, quiet_flag) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800177 user_name_ = application->has_user() ? application->user()->str() : "";
178 user_ = application->has_user() ? FindUid(user_name_.c_str()) : std::nullopt;
179 group_ = application->has_user() ? FindPrimaryGidForUser(user_name_.c_str())
180 : std::nullopt;
181 autostart_ = application->autostart();
182 autorestart_ = application->autorestart();
183 if (application->has_args()) {
184 set_args(*application->args());
185 }
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700186
187 if (application->has_memory_limit() && application->memory_limit() > 0) {
188 SetMemoryLimit(application->memory_limit());
189 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800190}
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800191
192void Application::DoStart() {
193 if (status_ != aos::starter::State::WAITING) {
194 return;
195 }
196
197 start_timer_->Disable();
198 restart_timer_->Disable();
199
James Kuszmauld42edb42022-01-07 18:00:16 -0800200 status_pipes_ = util::ScopedPipe::MakePipe();
201
202 if (capture_stdout_) {
203 stdout_pipes_ = util::ScopedPipe::MakePipe();
204 stdout_.clear();
205 }
206 if (capture_stderr_) {
207 stderr_pipes_ = util::ScopedPipe::MakePipe();
208 stderr_.clear();
209 }
210
Philipp Schradera6712522023-07-05 20:25:11 -0700211 pipe_timer_->Schedule(event_loop_->monotonic_now(),
212 std::chrono::milliseconds(100));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800213
214 const pid_t pid = fork();
215
216 if (pid != 0) {
217 if (pid == -1) {
Philipp Schrader595979d2023-09-13 11:31:48 -0700218 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo ||
219 quiet_flag_ == QuietLogging::kNotForDebugging)
payton.rehl2841b1c2023-05-25 17:23:55 -0700220 << "Failed to fork '" << name_ << "'";
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800221 stop_reason_ = aos::starter::LastStopReason::FORK_ERR;
222 status_ = aos::starter::State::STOPPED;
223 } else {
224 pid_ = pid;
225 id_ = next_id_++;
226 start_time_ = event_loop_->monotonic_now();
227 status_ = aos::starter::State::STARTING;
James Kuszmaul8544c492023-07-31 15:00:38 -0700228 latest_timing_report_version_.reset();
payton.rehl2841b1c2023-05-25 17:23:55 -0700229 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
230 << "Starting '" << name_ << "' pid " << pid_;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800231
Philipp Schradera6712522023-07-05 20:25:11 -0700232 // Set up timer which moves application to RUNNING state if it is still
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800233 // alive in 1 second.
Philipp Schradera6712522023-07-05 20:25:11 -0700234 start_timer_->Schedule(event_loop_->monotonic_now() +
235 std::chrono::seconds(1));
James Kuszmauld42edb42022-01-07 18:00:16 -0800236 // Since we are the parent process, clear our write-side of all the pipes.
237 status_pipes_.write.reset();
238 stdout_pipes_.write.reset();
239 stderr_pipes_.write.reset();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800240 }
Austin Schuh1cea9032023-07-10 11:56:40 -0700241 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800242 return;
243 }
244
Austin Schuhbbeb37e2022-08-17 16:19:27 -0700245 if (memory_cgroup_) {
246 memory_cgroup_->AddTid();
247 }
248
James Kuszmauld42edb42022-01-07 18:00:16 -0800249 // Since we are the child process, clear our read-side of all the pipes.
250 status_pipes_.read.reset();
251 stdout_pipes_.read.reset();
252 stderr_pipes_.read.reset();
253
254 // The status pipe will not be needed if the execve succeeds.
255 status_pipes_.write->SetCloexec();
256
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800257 // Clear out signal mask of parent so forked process receives all signals
258 // normally.
259 sigset_t empty_mask;
260 sigemptyset(&empty_mask);
261 sigprocmask(SIG_SETMASK, &empty_mask, nullptr);
262
263 // Cleanup children if starter dies in a way that is not handled gracefully.
264 if (prctl(PR_SET_PDEATHSIG, SIGKILL) == -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_PRCTL_ERR));
267 PLOG(FATAL) << "Could not set PR_SET_PDEATHSIG to SIGKILL";
268 }
269
270 if (group_) {
271 CHECK(!user_name_.empty());
272 // The manpage for setgroups says we just need CAP_SETGID, but empirically
273 // we also need the effective UID to be 0 to make it work. user_ must also
274 // be set so we change this effective UID back later.
275 CHECK(user_);
276 if (seteuid(0) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800277 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800278 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
279 PLOG(FATAL) << "Could not seteuid(0) for " << name_
280 << " in preparation for setting groups";
281 }
282 if (initgroups(user_name_.c_str(), *group_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800283 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800284 static_cast<uint32_t>(aos::starter::LastStopReason::SET_GRP_ERR));
285 PLOG(FATAL) << "Could not initialize normal groups for " << name_
286 << " as " << user_name_ << " with " << *group_;
287 }
288 if (setgid(*group_) == -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_GRP_ERR));
291 PLOG(FATAL) << "Could not set group for " << name_ << " to " << *group_;
292 }
293 }
294
295 if (user_) {
296 if (setuid(*user_) == -1) {
James Kuszmauld42edb42022-01-07 18:00:16 -0800297 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800298 static_cast<uint32_t>(aos::starter::LastStopReason::SET_USR_ERR));
299 PLOG(FATAL) << "Could not set user for " << name_ << " to " << *user_;
300 }
301 }
302
James Kuszmauld42edb42022-01-07 18:00:16 -0800303 if (capture_stdout_) {
304 PCHECK(STDOUT_FILENO == dup2(stdout_pipes_.write->fd(), STDOUT_FILENO));
305 stdout_pipes_.write.reset();
306 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800307
James Kuszmauld42edb42022-01-07 18:00:16 -0800308 if (capture_stderr_) {
309 PCHECK(STDERR_FILENO == dup2(stderr_pipes_.write->fd(), STDERR_FILENO));
310 stderr_pipes_.write.reset();
311 }
312
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700313 if (run_as_sudo_) {
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700314 // For sudo we must supply the actual path
315 args_.insert(args_.begin(), path_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700316 args_.insert(args_.begin(), kSudo);
Sarah Newman6d1e53b2022-08-09 14:38:08 -0700317 } else {
318 // argv[0] should be the program name
319 args_.insert(args_.begin(), name_);
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700320 }
James Kuszmauld42edb42022-01-07 18:00:16 -0800321
322 std::vector<char *> cargs = CArgs();
Philipp Schrader790cb542023-07-05 21:06:52 -0700323 const char *path = run_as_sudo_ ? kSudo : path_.c_str();
Sanjay Narayanan01a228f2022-04-26 14:19:30 -0700324 execvp(path, cargs.data());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800325
326 // If we got here, something went wrong
James Kuszmauld42edb42022-01-07 18:00:16 -0800327 status_pipes_.write->Write(
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800328 static_cast<uint32_t>(aos::starter::LastStopReason::EXECV_ERR));
Philipp Schrader595979d2023-09-13 11:31:48 -0700329 PLOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo ||
330 quiet_flag_ == QuietLogging::kNotForDebugging)
payton.rehl2841b1c2023-05-25 17:23:55 -0700331 << "Could not execute " << name_ << " (" << path_ << ')';
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800332
333 _exit(EXIT_FAILURE);
334}
335
James Kuszmaul8544c492023-07-31 15:00:38 -0700336void Application::ObserveTimingReport(
337 const aos::monotonic_clock::time_point send_time,
338 const aos::timing::Report *msg) {
339 if (msg->name()->string_view() == name_ && msg->pid() == pid_ &&
340 msg->has_version()) {
341 latest_timing_report_version_ = msg->version()->str();
342 last_timing_report_ = send_time;
343 }
344}
345
James Kuszmauld42edb42022-01-07 18:00:16 -0800346void Application::FetchOutputs() {
347 if (capture_stdout_) {
348 stdout_pipes_.read->Read(&stdout_);
349 }
350 if (capture_stderr_) {
351 stderr_pipes_.read->Read(&stderr_);
352 }
353}
354
355const std::string &Application::GetStdout() {
356 CHECK(capture_stdout_);
357 FetchOutputs();
358 return stdout_;
359}
360
361const std::string &Application::GetStderr() {
362 CHECK(capture_stderr_);
363 FetchOutputs();
364 return stderr_;
365}
366
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800367void Application::DoStop(bool restart) {
368 // If stop or restart received, the old state of these is no longer applicable
369 // so cancel both.
370 restart_timer_->Disable();
371 start_timer_->Disable();
372
James Kuszmauld42edb42022-01-07 18:00:16 -0800373 FetchOutputs();
374
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800375 switch (status_) {
376 case aos::starter::State::STARTING:
377 case aos::starter::State::RUNNING: {
Philipp Schrader595979d2023-09-13 11:31:48 -0700378 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo ||
379 quiet_flag_ == QuietLogging::kNotForDebugging)
payton.rehl2841b1c2023-05-25 17:23:55 -0700380 << "Stopping '" << name_ << "' pid: " << pid_ << " with signal "
381 << SIGINT;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800382 status_ = aos::starter::State::STOPPING;
383
Philipp Schrader595979d2023-09-13 11:31:48 -0700384 if (kill(pid_, SIGINT) != 0) {
385 PLOG_IF(INFO, quiet_flag_ == QuietLogging::kNo ||
386 quiet_flag_ == QuietLogging::kNotForDebugging)
387 << "Failed to send signal " << SIGINT << " to '" << name_
388 << "' pid: " << pid_;
389 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800390
391 // Watchdog timer to SIGKILL application if it is still running 1 second
392 // after SIGINT
Philipp Schradera6712522023-07-05 20:25:11 -0700393 stop_timer_->Schedule(event_loop_->monotonic_now() +
394 std::chrono::seconds(1));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800395 queue_restart_ = restart;
Austin Schuh1cea9032023-07-10 11:56:40 -0700396 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800397 break;
398 }
399 case aos::starter::State::WAITING: {
400 // If waiting to restart, and receives restart, skip the waiting period
401 // and restart immediately. If stop received, all we have to do is move
402 // to the STOPPED state.
403 if (restart) {
404 DoStart();
405 } else {
406 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700407 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800408 }
409 break;
410 }
411 case aos::starter::State::STOPPING: {
412 // If the application is already stopping, then we just need to update the
413 // restart flag to the most recent status.
414 queue_restart_ = restart;
415 break;
416 }
417 case aos::starter::State::STOPPED: {
418 // Restart immediately if the application is already stopped
419 if (restart) {
420 status_ = aos::starter::State::WAITING;
421 DoStart();
422 }
423 break;
424 }
425 }
426}
427
428void Application::QueueStart() {
429 status_ = aos::starter::State::WAITING;
430
payton.rehl2841b1c2023-05-25 17:23:55 -0700431 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
432 << "Restarting " << name_ << " in 3 seconds";
Philipp Schradera6712522023-07-05 20:25:11 -0700433 restart_timer_->Schedule(event_loop_->monotonic_now() +
434 std::chrono::seconds(3));
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800435 start_timer_->Disable();
436 stop_timer_->Disable();
Austin Schuh1cea9032023-07-10 11:56:40 -0700437 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800438}
439
James Kuszmauld42edb42022-01-07 18:00:16 -0800440std::vector<char *> Application::CArgs() {
441 std::vector<char *> cargs;
442 std::transform(args_.begin(), args_.end(), std::back_inserter(cargs),
443 [](std::string &str) { return str.data(); });
444 cargs.push_back(nullptr);
445 return cargs;
446}
447
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800448void Application::set_args(
449 const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v) {
450 args_.clear();
451 std::transform(v.begin(), v.end(), std::back_inserter(args_),
James Kuszmauld42edb42022-01-07 18:00:16 -0800452 [](const flatbuffers::String *str) { return str->str(); });
453}
454
455void Application::set_args(std::vector<std::string> args) {
456 args_ = std::move(args);
457}
458
459void Application::set_capture_stdout(bool capture) {
460 capture_stdout_ = capture;
461}
462
463void Application::set_capture_stderr(bool capture) {
464 capture_stderr_ = capture;
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800465}
466
467std::optional<uid_t> Application::FindUid(const char *name) {
468 // TODO(austin): Use the reentrant version. This should be safe.
469 struct passwd *user_data = getpwnam(name);
470 if (user_data != nullptr) {
471 return user_data->pw_uid;
472 } else {
473 LOG(FATAL) << "Could not find user " << name;
474 return std::nullopt;
475 }
476}
477
478std::optional<gid_t> Application::FindPrimaryGidForUser(const char *name) {
479 // TODO(austin): Use the reentrant version. This should be safe.
480 struct passwd *user_data = getpwnam(name);
481 if (user_data != nullptr) {
482 return user_data->pw_gid;
483 } else {
484 LOG(FATAL) << "Could not find user " << name;
485 return std::nullopt;
486 }
487}
488
489flatbuffers::Offset<aos::starter::ApplicationStatus>
James Kuszmaul6295a642022-03-22 15:23:59 -0700490Application::PopulateStatus(flatbuffers::FlatBufferBuilder *builder,
491 util::Top *top) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800492 CHECK_NOTNULL(builder);
493 auto name_fbs = builder->CreateString(name_);
494
James Kuszmaul6295a642022-03-22 15:23:59 -0700495 const bool valid_pid = pid_ > 0 && status_ != aos::starter::State::STOPPED;
496 const flatbuffers::Offset<util::ProcessInfo> process_info =
497 valid_pid ? top->InfoForProcess(builder, pid_)
498 : flatbuffers::Offset<util::ProcessInfo>();
499
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800500 aos::starter::ApplicationStatus::Builder status_builder(*builder);
501 status_builder.add_name(name_fbs);
502 status_builder.add_state(status_);
James Kuszmauld42edb42022-01-07 18:00:16 -0800503 if (exit_code_.has_value()) {
504 status_builder.add_last_exit_code(exit_code_.value());
505 }
James Kuszmaul8544c492023-07-31 15:00:38 -0700506 status_builder.add_has_active_timing_report(
507 last_timing_report_ +
508 // Leave a bit of margin on the timing report receipt time, to allow
509 // for timing errors.
510 3 * std::chrono::milliseconds(FLAGS_timing_report_ms) >
511 event_loop_->monotonic_now());
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800512 status_builder.add_last_stop_reason(stop_reason_);
513 if (pid_ != -1) {
514 status_builder.add_pid(pid_);
515 status_builder.add_id(id_);
516 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700517 // Note that even if process_info is null, calling add_process_info is fine.
518 status_builder.add_process_info(process_info);
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800519 status_builder.add_last_start_time(start_time_.time_since_epoch().count());
520 return status_builder.Finish();
521}
522
523void Application::Terminate() {
524 stop_reason_ = aos::starter::LastStopReason::TERMINATE;
525 DoStop(false);
526 terminating_ = true;
527}
528
529void Application::HandleCommand(aos::starter::Command cmd) {
530 switch (cmd) {
531 case aos::starter::Command::START: {
532 switch (status_) {
533 case aos::starter::State::WAITING: {
534 restart_timer_->Disable();
535 DoStart();
536 break;
537 }
538 case aos::starter::State::STARTING: {
539 break;
540 }
541 case aos::starter::State::RUNNING: {
542 break;
543 }
544 case aos::starter::State::STOPPING: {
545 queue_restart_ = true;
546 break;
547 }
548 case aos::starter::State::STOPPED: {
549 status_ = aos::starter::State::WAITING;
550 DoStart();
551 break;
552 }
553 }
554 break;
555 }
556 case aos::starter::Command::STOP: {
557 stop_reason_ = aos::starter::LastStopReason::STOP_REQUESTED;
558 DoStop(false);
559 break;
560 }
561 case aos::starter::Command::RESTART: {
562 stop_reason_ = aos::starter::LastStopReason::RESTART_REQUESTED;
563 DoStop(true);
564 break;
565 }
566 }
567}
568
569bool Application::MaybeHandleSignal() {
570 int status;
571
Sarah Newman21c59202022-06-16 12:36:33 -0700572 if (status_ == aos::starter::State::WAITING ||
573 status_ == aos::starter::State::STOPPED) {
574 // We can't possibly have received a signal meant for this process.
575 return false;
576 }
577
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800578 // Check if the status of this process has changed
Sarah Newman21c59202022-06-16 12:36:33 -0700579 // The PID won't be -1 if this application has ever been run successfully
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800580 if (pid_ == -1 || waitpid(pid_, &status, WNOHANG) != pid_) {
581 return false;
582 }
583
584 // Check that the event was the process exiting
585 if (!WIFEXITED(status) && !WIFSIGNALED(status)) {
586 return false;
587 }
588
James Kuszmauld42edb42022-01-07 18:00:16 -0800589 start_timer_->Disable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800590 exit_time_ = event_loop_->monotonic_now();
591 exit_code_ = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status);
592
James Kuszmauld42edb42022-01-07 18:00:16 -0800593 if (auto read_result = status_pipes_.read->Read()) {
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800594 stop_reason_ = static_cast<aos::starter::LastStopReason>(*read_result);
595 }
596
597 switch (status_) {
598 case aos::starter::State::STARTING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800599 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700600 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
601 << "Application '" << name_ << "' pid " << pid_
602 << " exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800603 } else {
Philipp Schrader595979d2023-09-13 11:31:48 -0700604 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo ||
605 quiet_flag_ == QuietLogging::kNotForDebugging)
payton.rehl2841b1c2023-05-25 17:23:55 -0700606 << "Failed to start '" << name_ << "' on pid " << pid_
607 << " : Exited with status " << exit_code_.value();
James Kuszmauld42edb42022-01-07 18:00:16 -0800608 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800609 if (autorestart()) {
610 QueueStart();
611 } else {
612 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700613 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800614 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800615 break;
616 }
617 case aos::starter::State::RUNNING: {
James Kuszmauld42edb42022-01-07 18:00:16 -0800618 if (exit_code_.value() == 0) {
payton.rehl2841b1c2023-05-25 17:23:55 -0700619 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
620 << "Application '" << name_ << "' pid " << pid_
621 << " exited with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800622 } else {
Philipp Schrader595979d2023-09-13 11:31:48 -0700623 if (quiet_flag_ == QuietLogging::kNo ||
624 quiet_flag_ == QuietLogging::kNotForDebugging) {
James Kuszmaul8544c492023-07-31 15:00:38 -0700625 std::string version_string =
626 latest_timing_report_version_.has_value()
627 ? absl::StrCat("'", latest_timing_report_version_.value(),
628 "'")
629 : "unknown";
630 LOG_IF(WARNING, quiet_flag_ == QuietLogging::kNo)
631 << "Application '" << name_ << "' pid " << pid_ << " version "
632 << version_string << " exited unexpectedly with status "
633 << exit_code_.value();
634 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800635 }
James Kuszmaul6f10b382022-03-11 22:31:38 -0800636 if (autorestart()) {
637 QueueStart();
638 } else {
639 status_ = aos::starter::State::STOPPED;
Austin Schuh1cea9032023-07-10 11:56:40 -0700640 OnChange();
James Kuszmaul6f10b382022-03-11 22:31:38 -0800641 }
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800642 break;
643 }
644 case aos::starter::State::STOPPING: {
payton.rehl2841b1c2023-05-25 17:23:55 -0700645 LOG_IF(INFO, quiet_flag_ == QuietLogging::kNo)
646 << "Successfully stopped '" << name_ << "' pid: " << pid_
647 << " with status " << exit_code_.value();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800648 status_ = aos::starter::State::STOPPED;
649
650 // Disable force stop timer since the process already died
651 stop_timer_->Disable();
652
Austin Schuh1cea9032023-07-10 11:56:40 -0700653 OnChange();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800654 if (terminating_) {
655 return true;
656 }
657
658 if (queue_restart_) {
659 queue_restart_ = false;
660 status_ = aos::starter::State::WAITING;
661 DoStart();
662 }
663 break;
664 }
665 case aos::starter::State::WAITING:
666 case aos::starter::State::STOPPED: {
Sarah Newman21c59202022-06-16 12:36:33 -0700667 __builtin_unreachable();
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800668 break;
669 }
670 }
671
672 return false;
673}
674
Austin Schuh1cea9032023-07-10 11:56:40 -0700675void Application::OnChange() {
676 for (auto &fn : on_change_) {
677 fn();
678 }
679}
680
Adam Snaider70deaf22023-08-11 13:58:34 -0700681Application::~Application() {
682 start_timer_->Disable();
683 restart_timer_->Disable();
684 stop_timer_->Disable();
685 pipe_timer_->Disable();
686 child_status_handler_->Disable();
687}
688
James Kuszmaul3224b8e2022-01-07 19:00:39 -0800689} // namespace aos::starter