blob: 1809326d2b49708c959b962c628e602b94ad7bcb [file] [log] [blame]
#ifndef AOS_STARTER_STARTERD_LIB_H_
#define AOS_STARTER_STARTERD_LIB_H_
#include <sys/signalfd.h>
#include <sys/wait.h>
#include <csignal>
#include <cstdio>
#include <string>
#include <unordered_map>
#include <vector>
#include "aos/configuration.h"
#include "aos/events/shm_event_loop.h"
#include "aos/ipc_lib/signalfd.h"
#include "aos/macros.h"
#include "aos/starter/starter_generated.h"
#include "aos/starter/starter_rpc_generated.h"
namespace aos {
namespace starter {
// RAII Pipe for sending individual ints between reader and writer.
class ScopedPipe {
public:
class ScopedReadPipe;
class ScopedWritePipe;
static std::tuple<ScopedReadPipe, ScopedWritePipe> MakePipe();
virtual ~ScopedPipe();
int fd() const { return fd_; }
private:
ScopedPipe(int fd = -1);
int fd_;
ScopedPipe(const ScopedPipe &) = delete;
ScopedPipe &operator=(const ScopedPipe &) = delete;
ScopedPipe(ScopedPipe &&);
ScopedPipe &operator=(ScopedPipe &&);
};
class ScopedPipe::ScopedReadPipe : public ScopedPipe {
public:
std::optional<uint32_t> Read();
private:
using ScopedPipe::ScopedPipe;
friend class ScopedPipe;
};
class ScopedPipe::ScopedWritePipe : public ScopedPipe {
public:
void Write(uint32_t data);
private:
using ScopedPipe::ScopedPipe;
friend class ScopedPipe;
};
// Manages a running process, allowing starting and stopping, and restarting
// automatically.
class Application {
public:
Application(const aos::Application *application,
aos::ShmEventLoop *event_loop, std::function<void()> on_change);
flatbuffers::Offset<aos::starter::ApplicationStatus> PopulateStatus(
flatbuffers::FlatBufferBuilder *builder);
// Returns the last pid of this process. -1 if not started yet.
pid_t get_pid() const { return pid_; }
// Handles a SIGCHLD signal received by the parent. Does nothing if this
// process was not the target. Returns true if this Application should be
// removed.
bool MaybeHandleSignal();
// Handles a command. May do nothing if application is already in the desired
// state.
void HandleCommand(aos::starter::Command cmd);
void Start() { HandleCommand(aos::starter::Command::START); }
void Stop() { HandleCommand(aos::starter::Command::STOP); }
void Terminate();
void set_args(
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>>
&args);
bool autostart() const { return autostart_; }
private:
void DoStart();
void DoStop(bool restart);
void QueueStart();
// Copy flatbuffer vector of strings to vector of std::string.
static std::vector<std::string> FbsVectorToVector(
const flatbuffers::Vector<flatbuffers::Offset<flatbuffers::String>> &v);
static std::optional<uid_t> FindUid(const char *name);
static std::optional<gid_t> FindPrimaryGidForUser(const char *name);
// Next unique id for all applications
static inline uint64_t next_id_ = 0;
std::string name_;
std::string path_;
std::vector<char *> args_;
std::optional<uid_t> user_;
std::optional<gid_t> group_;
pid_t pid_ = -1;
ScopedPipe::ScopedReadPipe read_pipe_;
ScopedPipe::ScopedWritePipe write_pipe_;
uint64_t id_;
int exit_code_ = 0;
aos::monotonic_clock::time_point start_time_, exit_time_;
bool queue_restart_ = false;
bool terminating_ = false;
bool autostart_ = true;
aos::starter::State status_ = aos::starter::State::STOPPED;
aos::starter::LastStopReason stop_reason_ =
aos::starter::LastStopReason::STOP_REQUESTED;
aos::ShmEventLoop *event_loop_;
aos::TimerHandler *start_timer_, *restart_timer_, *stop_timer_;
std::function<void()> on_change_;
DISALLOW_COPY_AND_ASSIGN(Application);
};
// Registers a signalfd listener with the given event loop and calls callback
// whenever a signal is received.
class SignalListener {
public:
SignalListener(aos::ShmEventLoop *loop,
std::function<void(signalfd_siginfo)> callback);
~SignalListener();
private:
aos::ShmEventLoop *loop_;
std::function<void(signalfd_siginfo)> callback_;
aos::ipc_lib::SignalFd signalfd_;
DISALLOW_COPY_AND_ASSIGN(SignalListener);
};
const aos::Channel *StatusChannelForNode(const aos::Configuration *config,
const aos::Node *node);
const aos::Channel *StarterRpcChannelForNode(const aos::Configuration *config,
const aos::Node *node);
class Starter {
public:
Starter(const aos::Configuration *event_loop_config);
// Inserts a new application from config. Returns the inserted application if
// it was successful, otherwise nullptr if an application already exists
// with the given name.
Application *AddApplication(const aos::Application *application);
// Runs the event loop and starts all applications
void Run();
void Cleanup();
private:
// Signals which indicate starter has died
static const inline std::vector<int> kStarterDeath = {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE,
SIGSEGV, SIGPIPE, SIGTERM, SIGBUS, SIGXCPU};
void OnSignal(signalfd_siginfo signal);
void HandleStarterRpc(const StarterRpc &command);
// Sends the Status message if it wouldn't exceed the rate limit.
void MaybeSendStatus();
void SendStatus();
const std::string config_path_;
const aos::Configuration *config_msg_;
aos::ShmEventLoop event_loop_;
aos::Sender<aos::starter::Status> status_sender_;
aos::TimerHandler *status_timer_;
aos::TimerHandler *cleanup_timer_;
int status_count_ = 0;
const int max_status_count_;
std::unordered_map<std::string, Application> applications_;
// Set to true on cleanup to block rpc commands and ensure cleanup only
// happens once.
bool exiting_ = false;
SignalListener listener_;
DISALLOW_COPY_AND_ASSIGN(Starter);
};
} // namespace starter
} // namespace aos
#endif // AOS_STARTER_STARTERD_LIB_H_