2016-04-29 02:19:50 +00:00
|
|
|
// Functions for handling event triggers.
|
2016-05-18 22:30:21 +00:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
#include <signal.h>
|
2017-02-13 04:24:22 +00:00
|
|
|
#include <stddef.h>
|
2016-04-21 06:00:54 +00:00
|
|
|
#include <unistd.h>
|
2017-02-11 02:47:02 +00:00
|
|
|
|
2016-04-21 06:00:54 +00:00
|
|
|
#include <algorithm>
|
2019-02-21 03:36:29 +00:00
|
|
|
#include <atomic>
|
2017-02-11 02:47:02 +00:00
|
|
|
#include <functional>
|
2016-04-29 02:19:50 +00:00
|
|
|
#include <memory>
|
2015-07-25 15:14:25 +00:00
|
|
|
#include <string>
|
2017-02-11 02:47:02 +00:00
|
|
|
#include <type_traits>
|
2005-10-05 22:37:08 +00:00
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "event.h"
|
2016-04-29 02:19:50 +00:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
|
|
|
#include "input_common.h"
|
2015-07-25 15:14:25 +00:00
|
|
|
#include "io.h"
|
2016-04-29 02:19:50 +00:00
|
|
|
#include "parser.h"
|
|
|
|
#include "proc.h"
|
|
|
|
#include "signal.h"
|
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2006-07-19 22:55:49 +00:00
|
|
|
|
2019-02-21 03:36:29 +00:00
|
|
|
class pending_signals_t {
|
|
|
|
static constexpr size_t SIGNAL_COUNT = NSIG;
|
2005-10-05 22:37:08 +00:00
|
|
|
|
2019-02-21 03:36:29 +00:00
|
|
|
/// A counter that is incremented each time a pending signal is received.
|
|
|
|
std::atomic<uint32_t> counter_{0};
|
2005-10-05 22:37:08 +00:00
|
|
|
|
2019-02-21 03:36:29 +00:00
|
|
|
/// List of pending signals.
|
|
|
|
std::array<std::atomic<bool>, SIGNAL_COUNT> received_{};
|
2005-10-05 22:37:08 +00:00
|
|
|
|
2019-02-21 03:36:29 +00:00
|
|
|
/// The last counter visible in acquire_pending().
|
|
|
|
/// This is not accessed from a signal handler.
|
|
|
|
owning_lock<uint32_t> last_counter_{0};
|
|
|
|
|
|
|
|
public:
|
|
|
|
pending_signals_t() = default;
|
|
|
|
|
|
|
|
/// No copying.
|
|
|
|
pending_signals_t(const pending_signals_t &);
|
|
|
|
void operator=(const pending_signals_t &);
|
|
|
|
|
|
|
|
/// Mark a signal as pending. This may be called from a signal handler.
|
|
|
|
/// We expect only one signal handler to execute at once.
|
|
|
|
/// Also note that these may be coalesced.
|
|
|
|
void mark(int which) {
|
|
|
|
if (which >= 0 && static_cast<size_t>(which) < received_.size()) {
|
|
|
|
// Must mark our received first, then pending.
|
|
|
|
received_[which].store(true, std::memory_order_relaxed);
|
|
|
|
uint32_t count = counter_.load(std::memory_order_relaxed);
|
|
|
|
counter_.store(1 + count, std::memory_order_release);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// \return the list of signals that were set, clearing them.
|
|
|
|
std::bitset<SIGNAL_COUNT> acquire_pending() {
|
|
|
|
auto current = last_counter_.acquire();
|
|
|
|
|
|
|
|
// Check the counter first. If it hasn't changed, no signals have been received.
|
|
|
|
uint32_t count = counter_.load(std::memory_order_acquire);
|
|
|
|
if (count == *current) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// The signal count has changed. Store the new counter and fetch all the signals that are set.
|
|
|
|
*current = count;
|
|
|
|
std::bitset<SIGNAL_COUNT> result{};
|
|
|
|
uint32_t bit = 0;
|
|
|
|
for (auto &signal : received_) {
|
|
|
|
bool val = signal.load(std::memory_order_relaxed);
|
|
|
|
if (val) {
|
|
|
|
result.set(bit);
|
|
|
|
signal.store(false, std::memory_order_relaxed);
|
|
|
|
}
|
|
|
|
bit++;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static pending_signals_t s_pending_signals;
|
2005-10-05 22:37:08 +00:00
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
typedef std::vector<shared_ptr<event_t>> event_list_t;
|
2011-12-27 06:27:58 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// List of event handlers.
|
2014-12-29 08:34:36 +00:00
|
|
|
static event_list_t s_event_handlers;
|
2005-10-05 22:37:08 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// List of events that have been sent but have not yet been delivered because they are blocked.
|
2011-12-27 06:27:58 +00:00
|
|
|
static event_list_t blocked;
|
2005-12-11 22:21:01 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// Variables (one per signal) set when a signal is observed. This is inspected by a signal handler.
|
2014-12-29 08:34:36 +00:00
|
|
|
static volatile bool s_observed_signals[NSIG] = {};
|
2016-04-29 02:19:50 +00:00
|
|
|
static void set_signal_observed(int sig, bool val) {
|
2014-12-29 08:34:36 +00:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2016-04-29 02:19:50 +00:00
|
|
|
if (sig >= 0 && (size_t)sig < sizeof s_observed_signals / sizeof *s_observed_signals) {
|
2014-12-29 08:34:36 +00:00
|
|
|
s_observed_signals[sig] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// Tests if one event instance matches the definition of a event class. If both the class and the
|
|
|
|
/// instance name a function, they must name the same function.
|
|
|
|
static int event_match(const event_t &classv, const event_t &instance) {
|
|
|
|
// If the function names are both non-empty and different, then it's not a match.
|
|
|
|
if (!classv.function_name.empty() && !instance.function_name.empty() &&
|
|
|
|
classv.function_name != instance.function_name) {
|
2012-03-05 22:18:16 +00:00
|
|
|
return 0;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2019-02-21 06:42:58 +00:00
|
|
|
if (classv.type == event_type_t::any) return 1;
|
2016-04-29 02:19:50 +00:00
|
|
|
if (classv.type != instance.type) return 0;
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
switch (classv.type) {
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::signal: {
|
2012-12-20 10:48:36 +00:00
|
|
|
return classv.param1.signal == instance.param1.signal;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::variable: {
|
2012-12-20 10:48:36 +00:00
|
|
|
return instance.str_param1 == classv.str_param1;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::exit: {
|
2016-04-29 02:19:50 +00:00
|
|
|
if (classv.param1.pid == EVENT_ANY_PID) return 1;
|
2012-12-20 10:48:36 +00:00
|
|
|
return classv.param1.pid == instance.param1.pid;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::job_exit: {
|
2012-12-20 10:48:36 +00:00
|
|
|
return classv.param1.job_id == instance.param1.job_id;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::generic: {
|
2012-12-20 10:48:36 +00:00
|
|
|
return instance.str_param1 == classv.str_param1;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2016-10-30 00:25:48 +00:00
|
|
|
default: {
|
|
|
|
DIE("unexpected classv.type");
|
|
|
|
break;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
// This should never be reached.
|
2012-12-19 14:56:19 +00:00
|
|
|
debug(0, "Warning: Unreachable code reached in event_match in event.cpp\n");
|
2012-11-19 00:30:30 +00:00
|
|
|
return 0;
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// Test if specified event is blocked.
|
|
|
|
static int event_is_blocked(const event_t &e) {
|
2013-12-21 01:41:21 +00:00
|
|
|
const block_t *block;
|
2012-11-19 00:30:30 +00:00
|
|
|
parser_t &parser = parser_t::principal_parser();
|
2014-01-15 09:40:40 +00:00
|
|
|
|
2013-12-21 01:41:21 +00:00
|
|
|
size_t idx = 0;
|
2016-04-29 02:19:50 +00:00
|
|
|
while ((block = parser.block_at_index(idx++))) {
|
2019-02-21 06:42:58 +00:00
|
|
|
if (event_block_list_blocks_type(block->event_blocks)) return true;
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
return event_block_list_blocks_type(parser.global_event_blocks);
|
2005-12-11 22:21:01 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
wcstring event_get_desc(const event_t &e) {
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring result;
|
2016-04-29 02:19:50 +00:00
|
|
|
switch (e.type) {
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::signal: {
|
2016-04-29 02:19:50 +00:00
|
|
|
result = format_string(_(L"signal handler for %ls (%ls)"), sig2wcs(e.param1.signal),
|
|
|
|
signal_get_desc(e.param1.signal));
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::variable: {
|
2012-12-20 10:48:36 +00:00
|
|
|
result = format_string(_(L"handler for variable '%ls'"), e.str_param1.c_str());
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::exit: {
|
2016-04-29 02:19:50 +00:00
|
|
|
if (e.param1.pid > 0) {
|
2012-12-20 10:48:36 +00:00
|
|
|
result = format_string(_(L"exit handler for process %d"), e.param1.pid);
|
2016-04-29 02:19:50 +00:00
|
|
|
} else {
|
2017-04-23 13:58:26 +00:00
|
|
|
// In events, PGIDs are stored as negative PIDs
|
2018-10-02 17:30:23 +00:00
|
|
|
job_t *j = job_t::from_pid(-e.param1.pid);
|
2012-11-19 08:31:03 +00:00
|
|
|
if (j)
|
2016-04-29 02:19:50 +00:00
|
|
|
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id,
|
|
|
|
j->command_wcstr());
|
2012-11-19 08:31:03 +00:00
|
|
|
else
|
2016-04-29 02:19:50 +00:00
|
|
|
result = format_string(_(L"exit handler for job with process group %d"),
|
|
|
|
-e.param1.pid);
|
2012-11-19 08:31:03 +00:00
|
|
|
}
|
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::job_exit: {
|
2018-10-02 17:30:23 +00:00
|
|
|
job_t *j = job_t::from_job_id(e.param1.job_id);
|
2016-04-29 02:19:50 +00:00
|
|
|
if (j) {
|
|
|
|
result = format_string(_(L"exit handler for job %d, '%ls'"), j->job_id,
|
|
|
|
j->command_wcstr());
|
|
|
|
} else {
|
2012-12-20 10:48:36 +00:00
|
|
|
result = format_string(_(L"exit handler for job with job id %d"), e.param1.job_id);
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::generic: {
|
2012-12-20 10:48:36 +00:00
|
|
|
result = format_string(_(L"handler for generic event '%ls'"), e.str_param1.c_str());
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
|
|
|
default: {
|
2012-12-22 20:21:31 +00:00
|
|
|
result = format_string(_(L"Unknown event type '0x%x'"), e.type);
|
2012-11-19 08:31:03 +00:00
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2012-11-19 00:30:30 +00:00
|
|
|
return result;
|
2006-02-01 15:49:11 +00:00
|
|
|
}
|
|
|
|
|
2012-02-17 23:58:02 +00:00
|
|
|
#if 0
|
2016-04-29 02:19:50 +00:00
|
|
|
static void show_all_handlers(void) {
|
2017-01-14 04:34:15 +00:00
|
|
|
fwprintf(stdout, L"event handlers:\n");
|
2016-04-29 02:19:50 +00:00
|
|
|
for (event_list_t::const_iterator iter = events.begin(); iter != events.end(); ++iter) {
|
2012-02-17 19:36:49 +00:00
|
|
|
const event_t *foo = *iter;
|
|
|
|
wcstring tmp = event_get_desc(foo);
|
2017-01-14 04:34:15 +00:00
|
|
|
fwprintf(stdout, L" handler now %ls\n", tmp.c_str());
|
2012-02-17 19:36:49 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-17 23:58:02 +00:00
|
|
|
#endif
|
2005-12-11 22:21:01 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// Give a more condensed description of \c event compared to \c event_get_desc. It includes what
|
|
|
|
/// function will fire if the \c event is an event handler.
|
|
|
|
static wcstring event_desc_compact(const event_t &event) {
|
2012-12-22 20:21:31 +00:00
|
|
|
wcstring res;
|
2016-04-29 02:19:50 +00:00
|
|
|
switch (event.type) {
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::any: {
|
2012-12-22 20:21:31 +00:00
|
|
|
res = L"EVENT_ANY";
|
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::variable: {
|
2016-04-29 02:19:50 +00:00
|
|
|
if (event.str_param1.c_str()) {
|
2012-12-22 20:21:31 +00:00
|
|
|
res = format_string(L"EVENT_VARIABLE($%ls)", event.str_param1.c_str());
|
2016-04-29 02:19:50 +00:00
|
|
|
} else {
|
2012-12-22 20:21:31 +00:00
|
|
|
res = L"EVENT_VARIABLE([any])";
|
|
|
|
}
|
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::signal: {
|
2019-02-23 20:18:15 +00:00
|
|
|
res = format_string(L"EVENT_SIGNAL(%ls)", sig2wcs(event.param1.signal));
|
2012-12-22 20:21:31 +00:00
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::exit: {
|
2016-04-29 02:19:50 +00:00
|
|
|
if (event.param1.pid == EVENT_ANY_PID) {
|
2012-12-22 20:21:31 +00:00
|
|
|
res = wcstring(L"EVENT_EXIT([all child processes])");
|
2016-04-29 02:19:50 +00:00
|
|
|
} else if (event.param1.pid > 0) {
|
2012-12-22 20:21:31 +00:00
|
|
|
res = format_string(L"EVENT_EXIT(pid %d)", event.param1.pid);
|
2016-04-29 02:19:50 +00:00
|
|
|
} else {
|
2017-04-23 13:58:26 +00:00
|
|
|
// In events, PGIDs are stored as negative PIDs
|
2018-10-02 17:30:23 +00:00
|
|
|
job_t *j = job_t::from_pid(-event.param1.pid);
|
2012-12-22 20:21:31 +00:00
|
|
|
if (j)
|
2016-04-29 02:19:50 +00:00
|
|
|
res = format_string(L"EVENT_EXIT(jobid %d: \"%ls\")", j->job_id,
|
|
|
|
j->command_wcstr());
|
2012-12-22 20:21:31 +00:00
|
|
|
else
|
|
|
|
res = format_string(L"EVENT_EXIT(pgid %d)", -event.param1.pid);
|
|
|
|
}
|
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::job_exit: {
|
2018-10-02 17:30:23 +00:00
|
|
|
job_t *j = job_t::from_job_id(event.param1.job_id);
|
2012-12-22 20:21:31 +00:00
|
|
|
if (j)
|
2016-04-29 02:19:50 +00:00
|
|
|
res =
|
|
|
|
format_string(L"EVENT_JOB_ID(job %d: \"%ls\")", j->job_id, j->command_wcstr());
|
2012-12-22 20:21:31 +00:00
|
|
|
else
|
|
|
|
res = format_string(L"EVENT_JOB_ID(jobid %d)", event.param1.job_id);
|
|
|
|
break;
|
|
|
|
}
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::generic: {
|
2012-12-22 20:21:31 +00:00
|
|
|
res = format_string(L"EVENT_GENERIC(%ls)", event.str_param1.c_str());
|
|
|
|
break;
|
2016-04-29 02:19:50 +00:00
|
|
|
}
|
2016-11-02 05:36:30 +00:00
|
|
|
default: {
|
|
|
|
res = format_string(L"unknown/illegal event(%x)", event.type);
|
|
|
|
break;
|
|
|
|
}
|
2012-12-22 20:21:31 +00:00
|
|
|
}
|
2016-04-29 02:19:50 +00:00
|
|
|
if (event.function_name.size()) {
|
2012-12-22 20:21:31 +00:00
|
|
|
return format_string(L"%ls: \"%ls\"", res.c_str(), event.function_name.c_str());
|
|
|
|
}
|
2016-05-04 22:19:47 +00:00
|
|
|
return res;
|
2012-12-18 23:32:03 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
void event_add_handler(const event_t &event) {
|
|
|
|
if (debug_level >= 3) {
|
2012-12-22 17:38:28 +00:00
|
|
|
wcstring desc = event_desc_compact(event);
|
2017-06-19 05:07:48 +00:00
|
|
|
debug(3, "register: %ls", desc.c_str());
|
2012-12-22 17:38:28 +00:00
|
|
|
}
|
2012-12-20 09:52:44 +00:00
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
shared_ptr<event_t> e = std::make_shared<event_t>(event);
|
2019-02-21 06:42:58 +00:00
|
|
|
if (e->type == event_type_t::signal) {
|
2012-11-19 00:30:30 +00:00
|
|
|
signal_handle(e->param1.signal, 1);
|
2014-12-29 08:34:36 +00:00
|
|
|
set_signal_observed(e->param1.signal, true);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2005-10-05 22:37:08 +00:00
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
s_event_handlers.push_back(std::move(e));
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
void event_remove(const event_t &criterion) {
|
|
|
|
if (debug_level >= 3) {
|
2012-12-22 17:38:28 +00:00
|
|
|
wcstring desc = event_desc_compact(criterion);
|
2017-06-19 05:07:48 +00:00
|
|
|
debug(3, "unregister: %ls", desc.c_str());
|
2012-12-22 17:38:28 +00:00
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
event_list_t::iterator iter = s_event_handlers.begin();
|
|
|
|
while (iter != s_event_handlers.end()) {
|
|
|
|
const event_t *n = iter->get();
|
2017-01-27 04:00:43 +00:00
|
|
|
if (!event_match(criterion, *n)) {
|
2017-01-22 00:48:07 +00:00
|
|
|
++iter;
|
|
|
|
continue;
|
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
// If this event was a signal handler and no other handler handles the specified signal
|
|
|
|
// type, do not handle that type of signal any more.
|
2019-02-21 06:42:58 +00:00
|
|
|
if (n->type == event_type_t::signal) {
|
2017-01-22 00:48:07 +00:00
|
|
|
event_t e = event_t::signal_event(n->param1.signal);
|
|
|
|
if (event_get(e, NULL) == 1) {
|
|
|
|
signal_handle(e.param1.signal, 0);
|
|
|
|
set_signal_observed(e.param1.signal, 0);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-22 00:48:07 +00:00
|
|
|
iter = s_event_handlers.erase(iter);
|
2012-11-18 10:23:22 +00:00
|
|
|
}
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
int event_get(const event_t &criterion, event_list_t *out) {
|
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2012-11-19 00:30:30 +00:00
|
|
|
int found = 0;
|
2017-01-22 00:48:07 +00:00
|
|
|
for (const shared_ptr<event_t> &n : s_event_handlers) {
|
2016-04-29 02:19:50 +00:00
|
|
|
if (event_match(criterion, *n)) {
|
2012-11-19 00:30:30 +00:00
|
|
|
found++;
|
2016-04-29 02:19:50 +00:00
|
|
|
if (out) out->push_back(n);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
return found;
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
bool event_is_signal_observed(int sig) {
|
|
|
|
// We are in a signal handler! Don't allocate memory, etc.
|
2014-12-29 08:34:36 +00:00
|
|
|
bool result = false;
|
2016-10-09 21:36:08 +00:00
|
|
|
if (sig >= 0 && (unsigned long)sig < sizeof(s_observed_signals) / sizeof(*s_observed_signals)) {
|
2014-12-29 08:34:36 +00:00
|
|
|
result = s_observed_signals[sig];
|
2012-06-04 21:20:01 +00:00
|
|
|
}
|
2014-12-29 08:34:36 +00:00
|
|
|
return result;
|
2012-06-04 21:20:01 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// Perform the specified event. Since almost all event firings will not be matched by even a single
|
|
|
|
/// event handler, we make sure to optimize the 'no matches' path. This means that nothing is
|
|
|
|
/// allocated/initialized unless needed.
|
|
|
|
static void event_fire_internal(const event_t &event) {
|
2019-02-21 03:36:29 +00:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
|
|
|
assert(is_event >= 0 && "is_event should not be negative");
|
|
|
|
scoped_push<decltype(is_event)> inc_event{&is_event, is_event + 1};
|
|
|
|
|
2017-01-22 00:48:07 +00:00
|
|
|
// Iterate over all events, adding events that should be fired to a second list. We need
|
2016-04-29 02:19:50 +00:00
|
|
|
// to do this in a separate step since an event handler might call event_remove or
|
|
|
|
// event_add_handler, which will change the contents of the \c events list.
|
2017-01-22 00:48:07 +00:00
|
|
|
event_list_t fire;
|
|
|
|
for (shared_ptr<event_t> &criterion : s_event_handlers) {
|
2016-04-29 02:19:50 +00:00
|
|
|
// Check if this event is a match.
|
|
|
|
if (event_match(*criterion, event)) {
|
2011-12-27 06:51:34 +00:00
|
|
|
fire.push_back(criterion);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
// No matches. Time to return.
|
|
|
|
if (fire.empty()) return;
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
if (signal_is_blocked()) {
|
|
|
|
// Fix for https://github.com/fish-shell/fish-shell/issues/608. Don't run event handlers
|
|
|
|
// while signals are blocked.
|
2017-01-27 04:00:43 +00:00
|
|
|
input_common_add_callback([event]() {
|
2017-01-22 01:15:45 +00:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
|
|
|
event_fire(&event);
|
|
|
|
});
|
2013-04-04 00:26:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
// Iterate over our list of matching events.
|
2017-01-22 00:48:07 +00:00
|
|
|
for (shared_ptr<event_t> &criterion : fire) {
|
|
|
|
// Only fire if this event is still present
|
2018-08-12 02:55:06 +00:00
|
|
|
if (!contains(s_event_handlers, criterion)) {
|
2017-01-22 00:48:07 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-11-19 00:30:30 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
// Fire event.
|
2012-11-19 00:30:30 +00:00
|
|
|
wcstring buffer = criterion->function_name;
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
for (size_t j = 0; j < event.arguments.size(); j++) {
|
2012-12-22 20:40:34 +00:00
|
|
|
wcstring arg_esc = escape_string(event.arguments.at(j), 1);
|
|
|
|
buffer += L" ";
|
|
|
|
buffer += arg_esc;
|
2011-12-27 08:06:07 +00:00
|
|
|
}
|
2005-10-11 19:23:43 +00:00
|
|
|
|
2012-12-22 20:21:31 +00:00
|
|
|
// debug( 1, L"Event handler fires command '%ls'", buffer.c_str() );
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
// Event handlers are not part of the main flow of code, so they are marked as
|
|
|
|
// non-interactive.
|
2012-11-19 00:30:30 +00:00
|
|
|
proc_push_interactive(0);
|
2017-01-22 00:48:07 +00:00
|
|
|
int prev_status = proc_get_last_status();
|
2012-01-23 05:40:08 +00:00
|
|
|
parser_t &parser = parser_t::principal_parser();
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2017-01-21 23:35:35 +00:00
|
|
|
event_block_t *b = parser.push_block<event_block_t>(event);
|
2012-11-19 00:30:30 +00:00
|
|
|
parser.eval(buffer, io_chain_t(), TOP);
|
2017-01-21 23:35:35 +00:00
|
|
|
parser.pop_block(b);
|
2012-11-19 00:30:30 +00:00
|
|
|
proc_pop_interactive();
|
|
|
|
proc_set_last_status(prev_status);
|
|
|
|
}
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
/// Handle all pending signal events.
|
|
|
|
static void event_fire_delayed() {
|
2019-02-21 03:36:29 +00:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
|
|
|
// Do not invoke new event handlers from within event handlers.
|
|
|
|
if (is_event)
|
|
|
|
return;
|
|
|
|
|
|
|
|
event_list_t to_send;
|
|
|
|
to_send.swap(blocked);
|
|
|
|
assert(blocked.empty());
|
|
|
|
|
|
|
|
// Append all signal events to to_send.
|
|
|
|
auto signals = s_pending_signals.acquire_pending();
|
|
|
|
if (signals.any()) {
|
|
|
|
for (uint32_t sig=0; sig < signals.size(); sig++) {
|
|
|
|
if (signals.test(sig)) {
|
2019-02-21 06:42:58 +00:00
|
|
|
auto e = std::make_shared<event_t>(event_type_t::signal);
|
2019-02-21 03:36:29 +00:00
|
|
|
e->param1.signal = sig;
|
|
|
|
e->arguments.push_back(sig2wcs(sig));
|
|
|
|
to_send.push_back(std::move(e));
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-18 10:23:22 +00:00
|
|
|
|
2019-02-21 03:36:29 +00:00
|
|
|
// Fire or re-block all events.
|
|
|
|
for (const auto &evt : to_send) {
|
|
|
|
if (event_is_blocked(*evt)) {
|
|
|
|
blocked.push_back(evt);
|
|
|
|
} else {
|
|
|
|
event_fire_internal(*evt);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 03:36:29 +00:00
|
|
|
void event_enqueue_signal(int signal) {
|
|
|
|
// Beware, we are in a signal handler
|
|
|
|
s_pending_signals.mark(signal);
|
2012-06-04 21:20:01 +00:00
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
void event_fire(const event_t *event) {
|
2019-02-21 03:36:29 +00:00
|
|
|
// Fire events triggered by signals.
|
|
|
|
event_fire_delayed();
|
|
|
|
|
|
|
|
if (event) {
|
|
|
|
if (event_is_blocked(*event)) {
|
|
|
|
blocked.push_back(std::make_shared<event_t>(*event));
|
|
|
|
} else {
|
|
|
|
event_fire_internal(*event);
|
2012-11-19 00:30:30 +00:00
|
|
|
}
|
|
|
|
}
|
2005-10-05 22:37:08 +00:00
|
|
|
}
|
|
|
|
|
2018-03-10 10:08:33 +00:00
|
|
|
/// Mapping between event type to name.
|
|
|
|
/// Note we don't bother to sort this.
|
|
|
|
struct event_type_name_t {
|
|
|
|
event_type_t type;
|
|
|
|
const wchar_t *name;
|
|
|
|
};
|
2018-01-31 17:47:23 +00:00
|
|
|
|
2019-02-21 06:42:58 +00:00
|
|
|
static const event_type_name_t events_mapping[] = {{event_type_t::signal, L"signal"},
|
|
|
|
{event_type_t::variable, L"variable"},
|
|
|
|
{event_type_t::exit, L"exit"},
|
|
|
|
{event_type_t::job_exit, L"job-id"},
|
|
|
|
{event_type_t::generic, L"generic"}};
|
2018-03-10 10:08:33 +00:00
|
|
|
|
|
|
|
maybe_t<event_type_t> event_type_for_name(const wcstring &name) {
|
|
|
|
for (const auto &em : events_mapping) {
|
|
|
|
if (name == em.name) {
|
|
|
|
return em.type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return none();
|
2018-01-31 17:47:23 +00:00
|
|
|
}
|
|
|
|
|
2018-03-10 10:08:33 +00:00
|
|
|
static const wchar_t *event_name_for_type(event_type_t type) {
|
|
|
|
for (const auto &em : events_mapping) {
|
|
|
|
if (type == em.type) {
|
|
|
|
return em.name;
|
|
|
|
}
|
2018-01-31 17:47:23 +00:00
|
|
|
}
|
2018-03-10 10:08:33 +00:00
|
|
|
return L"";
|
2018-01-31 17:47:23 +00:00
|
|
|
}
|
|
|
|
|
2018-03-10 10:08:33 +00:00
|
|
|
|
|
|
|
void event_print(io_streams_t &streams, maybe_t<event_type_t> type_filter) {
|
2018-01-31 17:47:23 +00:00
|
|
|
std::vector<shared_ptr<event_t>> tmp = s_event_handlers;
|
|
|
|
std::sort(tmp.begin(), tmp.end(),
|
|
|
|
[](const shared_ptr<event_t> &e1, const shared_ptr<event_t> &e2) {
|
|
|
|
if (e1->type == e2->type) {
|
|
|
|
switch (e1->type) {
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::signal:
|
2018-01-31 17:47:23 +00:00
|
|
|
return e1->param1.signal < e2->param1.signal;
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::exit:
|
|
|
|
return e1->param1.pid < e2->param1.pid;
|
|
|
|
case event_type_t::job_exit:
|
2018-01-31 17:47:23 +00:00
|
|
|
return e1->param1.job_id < e2->param1.job_id;
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::variable:
|
|
|
|
case event_type_t::any:
|
|
|
|
case event_type_t::generic:
|
2018-01-31 17:47:23 +00:00
|
|
|
return e1->str_param1 < e2->str_param1;
|
|
|
|
}
|
|
|
|
}
|
2018-03-10 10:08:33 +00:00
|
|
|
return e1->type < e2->type;
|
2018-01-31 17:47:23 +00:00
|
|
|
});
|
|
|
|
|
2018-03-10 10:08:33 +00:00
|
|
|
maybe_t<event_type_t> last_type{};
|
2018-01-31 17:47:23 +00:00
|
|
|
for (const shared_ptr<event_t> &evt : tmp) {
|
2018-03-10 10:08:33 +00:00
|
|
|
// If we have a filter, skip events that don't match.
|
|
|
|
if (type_filter && *type_filter != evt->type) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-01-31 17:47:23 +00:00
|
|
|
|
2018-03-10 10:08:33 +00:00
|
|
|
if (!last_type || *last_type != evt->type) {
|
|
|
|
if (last_type)
|
|
|
|
streams.out.append(L"\n");
|
|
|
|
last_type = static_cast<event_type_t>(evt->type);
|
|
|
|
streams.out.append_format(L"Event %ls\n", event_name_for_type(*last_type));
|
|
|
|
}
|
|
|
|
switch (evt->type) {
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::signal:
|
2018-03-10 10:08:33 +00:00
|
|
|
streams.out.append_format(L"%ls %ls\n", sig2wcs(evt->param1.signal),
|
|
|
|
evt->function_name.c_str());
|
|
|
|
break;
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::job_exit:
|
2018-03-10 10:08:33 +00:00
|
|
|
streams.out.append_format(L"%d %ls\n", evt->param1,
|
|
|
|
evt->function_name.c_str());
|
|
|
|
break;
|
2019-02-21 06:42:58 +00:00
|
|
|
case event_type_t::variable:
|
|
|
|
case event_type_t::generic:
|
2018-03-10 10:08:33 +00:00
|
|
|
streams.out.append_format(L"%ls %ls\n", evt->str_param1.c_str(),
|
|
|
|
evt->function_name.c_str());
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
streams.out.append_format(L"%ls\n", evt->function_name.c_str());
|
|
|
|
break;
|
2018-01-31 17:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
void event_fire_generic(const wchar_t *name, wcstring_list_t *args) {
|
|
|
|
CHECK(name, );
|
2012-02-09 03:02:25 +00:00
|
|
|
|
2019-02-21 06:42:58 +00:00
|
|
|
event_t ev(event_type_t::generic);
|
2012-11-19 00:30:30 +00:00
|
|
|
ev.str_param1 = name;
|
2016-04-29 02:19:50 +00:00
|
|
|
if (args) ev.arguments = *args;
|
2012-11-19 00:30:30 +00:00
|
|
|
event_fire(&ev);
|
2007-08-19 16:42:30 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 06:42:58 +00:00
|
|
|
event_t::event_t(event_type_t t) : type(t), param1(), str_param1(), function_name(), arguments() {}
|
2013-04-13 08:32:07 +00:00
|
|
|
|
2018-02-19 02:44:58 +00:00
|
|
|
event_t::~event_t() = default;
|
2013-04-13 08:32:07 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
event_t event_t::signal_event(int sig) {
|
2019-02-21 06:42:58 +00:00
|
|
|
event_t event(event_type_t::signal);
|
2012-02-09 03:02:25 +00:00
|
|
|
event.param1.signal = sig;
|
|
|
|
return event;
|
|
|
|
}
|
2007-08-19 16:42:30 +00:00
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
event_t event_t::variable_event(const wcstring &str) {
|
2019-02-21 06:42:58 +00:00
|
|
|
event_t event(event_type_t::variable);
|
2012-02-09 03:02:25 +00:00
|
|
|
event.str_param1 = str;
|
|
|
|
return event;
|
|
|
|
}
|
|
|
|
|
2016-04-29 02:19:50 +00:00
|
|
|
event_t event_t::generic_event(const wcstring &str) {
|
2019-02-21 06:42:58 +00:00
|
|
|
event_t event(event_type_t::generic);
|
2012-02-09 03:02:25 +00:00
|
|
|
event.str_param1 = str;
|
|
|
|
return event;
|
|
|
|
}
|