Improve supervisor signal handler safety

Now should only be making async-signal-safe calls
This commit is contained in:
Jon Siwek 2020-01-14 18:56:34 -08:00
parent 3e1a9ebec3
commit f5b3673890
4 changed files with 38 additions and 19 deletions

View file

@ -13,8 +13,11 @@ Flare::Flare()
{ {
} }
static void bad_pipe_op(const char* which) static void bad_pipe_op(const char* which, bool signal_safe)
{ {
if ( signal_safe )
abort();
char buf[256]; char buf[256];
bro_strerror_r(errno, buf, sizeof(buf)); bro_strerror_r(errno, buf, sizeof(buf));
@ -27,7 +30,7 @@ static void bad_pipe_op(const char* which)
} }
} }
void Flare::Fire() void Flare::Fire(bool signal_safe)
{ {
char tmp = 0; char tmp = 0;
@ -49,14 +52,14 @@ void Flare::Fire()
// Interrupted: try again. // Interrupted: try again.
continue; continue;
bad_pipe_op("write"); bad_pipe_op("write", signal_safe);
} }
// No error, but didn't write a byte: try again. // No error, but didn't write a byte: try again.
} }
} }
int Flare::Extinguish() int Flare::Extinguish(bool signal_safe)
{ {
int rval = 0; int rval = 0;
char tmp[256]; char tmp[256];
@ -80,7 +83,7 @@ int Flare::Extinguish()
// Interrupted: try again. // Interrupted: try again.
continue; continue;
bad_pipe_op("read"); bad_pipe_op("read", signal_safe);
} }
return rval; return rval;

View file

@ -26,15 +26,19 @@ public:
/** /**
* Put the object in the "ready" state. * Put the object in the "ready" state.
* @param signal_safe whether to skip error-reporting functionality that
* is not async-signal-safe
*/ */
void Fire(); void Fire(bool signal_safe = false);
/** /**
* Take the object out of the "ready" state. * Take the object out of the "ready" state.
* @param signal_safe whether to skip error-reporting functionality that
* is not async-signal-safe
* @return number of bytes read from the pipe, corresponds to the number * @return number of bytes read from the pipe, corresponds to the number
* of times Fire() was called. * of times Fire() was called.
*/ */
int Extinguish(); int Extinguish(bool signal_safe = false);
private: private:
Pipe pipe; Pipe pipe;

View file

@ -65,6 +65,7 @@ struct Stem {
void LogError(const char* format, ...) const __attribute__((format(printf, 2, 3))); void LogError(const char* format, ...) const __attribute__((format(printf, 2, 3)));
int last_signal = -1;
std::unique_ptr<bro::Flare> signal_flare; std::unique_ptr<bro::Flare> signal_flare;
std::unique_ptr<bro::PipePair> pipe; std::unique_ptr<bro::PipePair> pipe;
std::map<std::string, Supervisor::Node> nodes; std::map<std::string, Supervisor::Node> nodes;
@ -77,13 +78,12 @@ static Stem* stem = nullptr;
static RETSIGTYPE stem_sig_handler(int signo) static RETSIGTYPE stem_sig_handler(int signo)
{ {
// TODO: signal safety stem->last_signal = signo;
DBG_STEM("Stem received signal: %d", signo);
if ( stem->shutting_down ) if ( stem->shutting_down )
return RETSIGVAL; return RETSIGVAL;
stem->signal_flare->Fire(); stem->signal_flare->Fire(true);
if ( signo == SIGTERM ) if ( signo == SIGTERM )
stem->shutting_down = true; stem->shutting_down = true;
@ -93,9 +93,7 @@ static RETSIGTYPE stem_sig_handler(int signo)
static RETSIGTYPE supervisor_sig_handler(int signo) static RETSIGTYPE supervisor_sig_handler(int signo)
{ {
// TODO: signal safety supervisor->ObserveChildSignal(signo);
DBG_LOG(DBG_SUPERVISOR, "received signal: %d", signo);
supervisor->ObserveChildSignal();
return RETSIGVAL; return RETSIGVAL;
} }
@ -126,8 +124,8 @@ static std::string make_create_message(const Supervisor::NodeConfig& node)
} }
Supervisor::Supervisor(Supervisor::Config cfg, Supervisor::Supervisor(Supervisor::Config cfg,
std::unique_ptr<bro::PipePair> pipe, std::unique_ptr<bro::PipePair> pipe,
pid_t arg_stem_pid) pid_t arg_stem_pid)
: config(std::move(cfg)), stem_pid(arg_stem_pid), stem_pipe(std::move(pipe)) : config(std::move(cfg)), stem_pid(arg_stem_pid), stem_pipe(std::move(pipe))
{ {
DBG_LOG(DBG_SUPERVISOR, "forked stem process %d", stem_pid); DBG_LOG(DBG_SUPERVISOR, "forked stem process %d", stem_pid);
@ -171,9 +169,10 @@ Supervisor::~Supervisor()
while ( ProcessMessages() != 0 ); while ( ProcessMessages() != 0 );
} }
void Supervisor::ObserveChildSignal() void Supervisor::ObserveChildSignal(int signo)
{ {
signal_flare.Fire(); last_signal = signo;
signal_flare.Fire(true);
} }
void Supervisor::ReapStem() void Supervisor::ReapStem()
@ -216,13 +215,19 @@ void Supervisor::ReapStem()
void Supervisor::HandleChildSignal() void Supervisor::HandleChildSignal()
{ {
if ( last_signal >= 0 )
{
DBG_LOG(DBG_SUPERVISOR, "Supervisor received signal %d", last_signal);
last_signal = -1;
}
bool had_child_signal = signal_flare.Extinguish(); bool had_child_signal = signal_flare.Extinguish();
if ( had_child_signal ) if ( had_child_signal )
{ {
ReapStem(); ReapStem();
DBG_LOG(DBG_SUPERVISOR, "processed SIGCHLD %s", DBG_LOG(DBG_SUPERVISOR, "Supervisor processed child signal %s",
stem_pid ? "(spurious)" : ""); stem_pid ? "(spurious)" : "");
} }
@ -657,6 +662,12 @@ std::optional<Supervisor::NodeConfig> Stem::Poll()
} }
} }
if ( last_signal >= 0 )
{
DBG_STEM("Stem received signal: %d", last_signal);
last_signal = -1;
}
if ( getppid() == 1 ) if ( getppid() == 1 )
{ {
// TODO: better way to detect loss of parent than polling ? // TODO: better way to detect loss of parent than polling ?

View file

@ -80,7 +80,7 @@ public:
pid_t StemPID() const pid_t StemPID() const
{ return stem_pid; } { return stem_pid; }
void ObserveChildSignal(); void ObserveChildSignal(int signo);
RecordVal* Status(std::string_view node_name); RecordVal* Status(std::string_view node_name);
std::string Create(const RecordVal* node); std::string Create(const RecordVal* node);
@ -113,6 +113,7 @@ private:
Config config; Config config;
pid_t stem_pid; pid_t stem_pid;
std::unique_ptr<bro::PipePair> stem_pipe; std::unique_ptr<bro::PipePair> stem_pipe;
int last_signal = -1;
bro::Flare signal_flare; bro::Flare signal_flare;
NodeMap nodes; NodeMap nodes;
std::string msg_buffer; std::string msg_buffer;