summaryrefslogtreecommitdiffstats
path: root/ctdb/event
diff options
context:
space:
mode:
Diffstat (limited to 'ctdb/event')
-rw-r--r--ctdb/event/event.h55
-rw-r--r--ctdb/event/event_client.c351
-rw-r--r--ctdb/event/event_cmd.c358
-rw-r--r--ctdb/event/event_conf.c85
-rw-r--r--ctdb/event/event_conf.h31
-rw-r--r--ctdb/event/event_config.c122
-rw-r--r--ctdb/event/event_context.c472
-rw-r--r--ctdb/event/event_daemon.c382
-rw-r--r--ctdb/event/event_private.h103
-rw-r--r--ctdb/event/event_protocol.c1123
-rw-r--r--ctdb/event/event_protocol.h100
-rw-r--r--ctdb/event/event_protocol_api.h61
-rw-r--r--ctdb/event/event_protocol_test.c412
-rw-r--r--ctdb/event/event_protocol_util.c46
-rw-r--r--ctdb/event/event_request.c217
-rw-r--r--ctdb/event/event_tool.c846
-rw-r--r--ctdb/event/event_tool.h38
17 files changed, 4802 insertions, 0 deletions
diff --git a/ctdb/event/event.h b/ctdb/event/event.h
new file mode 100644
index 0000000..13617c0
--- /dev/null
+++ b/ctdb/event/event.h
@@ -0,0 +1,55 @@
+/*
+ CTDB event daemon client
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_EVENT_H__
+#define __CTDB_EVENT_H__
+
+#include "event/event_protocol.h"
+
+struct ctdb_event_context;
+
+int ctdb_event_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context **result);
+
+struct tevent_req *ctdb_event_run_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request_run *run);
+bool ctdb_event_run_recv(struct tevent_req *req, int *perr, int *result);
+
+struct tevent_req *ctdb_event_status_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request_status *status);
+bool ctdb_event_status_recv(struct tevent_req *req,
+ int *perr,
+ int *result,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_status **status);
+
+struct tevent_req *ctdb_event_script_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request_script *script);
+bool ctdb_event_script_recv(struct tevent_req *req, int *perr, int *result);
+
+#endif /* __CTDB_EVENT_H__ */
diff --git a/ctdb/event/event_client.c b/ctdb/event/event_client.c
new file mode 100644
index 0000000..d0bee0d
--- /dev/null
+++ b/ctdb/event/event_client.c
@@ -0,0 +1,351 @@
+/*
+ CTDB event daemon client
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/debug.h"
+#include "lib/util/tevent_unix.h"
+
+#include "common/logging.h"
+#include "common/path.h"
+#include "common/sock_client.h"
+
+#include "event/event_protocol_api.h"
+#include "event/event.h"
+
+struct ctdb_event_context {
+ char *socket;
+ struct sock_client_context *sockc;
+};
+
+struct event_request {
+ struct ctdb_event_header header;
+ struct ctdb_event_request *request;
+};
+
+struct event_reply {
+ struct ctdb_event_header header;
+ struct ctdb_event_reply *reply;
+};
+
+static int event_request_push(void *request_data,
+ uint32_t reqid,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **buf,
+ size_t *buflen,
+ void *private_data)
+{
+ struct event_request *r = (struct event_request *)request_data;
+ int ret;
+
+ r->header.reqid = reqid;
+
+ *buflen = ctdb_event_request_len(&r->header, r->request);
+ *buf = talloc_size(mem_ctx, *buflen);
+ if (*buf == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_request_push(&r->header, r->request, *buf, buflen);
+ if (ret != 0) {
+ talloc_free(*buf);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int event_reply_pull(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ void **reply_data,
+ void *private_data)
+{
+ struct event_reply *r;
+ int ret;
+
+ r = talloc_zero(mem_ctx, struct event_reply);
+ if (r == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_reply_pull(buf, buflen, &r->header, r, &r->reply);
+ if (ret != 0) {
+ talloc_free(r);
+ return ret;
+ }
+
+ *reply_data = r;
+
+ return 0;
+}
+
+static int event_reply_reqid(uint8_t *buf,
+ size_t buflen,
+ uint32_t *reqid,
+ void *private_data)
+{
+ struct ctdb_event_header header;
+ int ret;
+
+ ret = ctdb_event_header_extract(buf, buflen, &header);
+ if (ret != 0) {
+ return ret;
+ }
+
+ *reqid = header.reqid;
+ return 0;
+}
+
+struct sock_client_proto_funcs event_proto_funcs = {
+ .request_push = event_request_push,
+ .reply_pull = event_reply_pull,
+ .reply_reqid = event_reply_reqid,
+};
+
+int ctdb_event_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context **result)
+{
+ struct ctdb_event_context *eclient;
+ int ret;
+
+ eclient = talloc_zero(mem_ctx, struct ctdb_event_context);
+ if (eclient == NULL) {
+ return ENOMEM;
+ }
+
+ eclient->socket = path_socket(eclient, "eventd");
+ if (eclient->socket == NULL) {
+ talloc_free(eclient);
+ return ENOMEM;
+ }
+
+ ret = sock_client_setup(eclient,
+ ev,
+ eclient->socket,
+ &event_proto_funcs,
+ eclient,
+ &eclient->sockc);
+ if (ret != 0) {
+ talloc_free(eclient);
+ return ret;
+ }
+
+ *result = eclient;
+ return 0;
+}
+
+/*
+ * Handle request and reply
+ */
+
+struct ctdb_event_msg_state {
+ struct event_request e_request;
+ struct event_reply *e_reply;
+};
+
+static void ctdb_event_msg_done(struct tevent_req *subreq);
+
+static struct tevent_req *ctdb_event_msg_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct ctdb_event_msg_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct ctdb_event_msg_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->e_request.request = request;
+
+ subreq = sock_client_msg_send(mem_ctx,
+ ev,
+ eclient->sockc,
+ tevent_timeval_zero(),
+ &state->e_request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, ctdb_event_msg_done, req);
+
+ return req;
+}
+
+static void ctdb_event_msg_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct ctdb_event_msg_state *state = tevent_req_data(
+ req, struct ctdb_event_msg_state);
+ int ret = 0;
+ bool ok;
+
+ ok = sock_client_msg_recv(subreq, &ret, state, &state->e_reply);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool ctdb_event_msg_recv(struct tevent_req *req,
+ int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct ctdb_event_msg_state *state = tevent_req_data(
+ req, struct ctdb_event_msg_state);
+ int ret = 0;
+
+ if (tevent_req_is_unix_error(req, &ret)) {
+ if (perr != NULL) {
+ *perr = ret;
+ }
+ return false;
+ }
+
+ *reply = talloc_steal(mem_ctx, state->e_reply->reply);
+
+ return true;
+}
+
+/*
+ * API functions
+ */
+
+struct tevent_req *ctdb_event_run_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request_run *run)
+{
+ struct ctdb_event_request request;
+
+ request.cmd = CTDB_EVENT_CMD_RUN;
+ request.data.run = run;
+
+ return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
+}
+
+bool ctdb_event_run_recv(struct tevent_req *req, int *perr, int *result)
+{
+ struct ctdb_event_reply *reply = NULL;
+ bool ok;
+
+ ok = ctdb_event_msg_recv(req, perr, req, &reply);
+ if (!ok) {
+ return false;
+ }
+
+ if (reply->cmd != CTDB_EVENT_CMD_RUN) {
+ *result = EPROTO;
+ } else {
+ *result = reply->result;
+ }
+
+ talloc_free(reply);
+ return true;
+}
+
+struct tevent_req *ctdb_event_status_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request_status *status)
+{
+ struct ctdb_event_request request;
+
+ request.cmd = CTDB_EVENT_CMD_STATUS;
+ request.data.status = status;
+
+ return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
+}
+
+bool ctdb_event_status_recv(struct tevent_req *req,
+ int *perr,
+ int *result,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_status **status)
+{
+ struct ctdb_event_reply *reply = NULL;
+ bool ok;
+
+ ok = ctdb_event_msg_recv(req, perr, req, &reply);
+ if (!ok) {
+ return false;
+ }
+
+ if (reply->cmd != CTDB_EVENT_CMD_STATUS) {
+ *result = EPROTO;
+ } else {
+ *result = reply->result;
+ }
+
+ if (reply->result == 0) {
+ *status = talloc_steal(mem_ctx, reply->data.status);
+ } else {
+ *status = NULL;
+ }
+
+ talloc_free(reply);
+ return true;
+}
+
+struct tevent_req *ctdb_event_script_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct ctdb_event_context *eclient,
+ struct ctdb_event_request_script *script)
+{
+ struct ctdb_event_request request;
+
+ request.cmd = CTDB_EVENT_CMD_SCRIPT;
+ request.data.script = script;
+
+ return ctdb_event_msg_send(mem_ctx, ev, eclient, &request);
+}
+
+bool ctdb_event_script_recv(struct tevent_req *req, int *perr, int *result)
+{
+ struct ctdb_event_reply *reply = NULL;
+ bool ok;
+
+ ok = ctdb_event_msg_recv(req, perr, req, &reply);
+ if (!ok) {
+ return false;
+ }
+
+ if (reply->cmd != CTDB_EVENT_CMD_SCRIPT) {
+ *result = EPROTO;
+ } else {
+ *result = reply->result;
+ }
+
+ talloc_free(reply);
+ return true;
+}
diff --git a/ctdb/event/event_cmd.c b/ctdb/event/event_cmd.c
new file mode 100644
index 0000000..db39e4d
--- /dev/null
+++ b/ctdb/event/event_cmd.c
@@ -0,0 +1,358 @@
+/*
+ CTDB event daemon - command handling
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/debug.h"
+#include "lib/util/tevent_unix.h"
+
+#include "common/logging.h"
+
+#include "event/event_private.h"
+
+struct event_cmd_state {
+ struct event_context *eventd;
+ struct ctdb_event_request *request;
+ struct ctdb_event_reply *reply;
+};
+
+/*
+ * CTDB_EVENT_CMD_RUN
+ */
+
+static void event_cmd_run_done(struct tevent_req *subreq);
+
+static struct tevent_req *event_cmd_run_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ struct ctdb_event_request *request,
+ struct ctdb_event_reply *reply)
+{
+ struct tevent_req *req, *subreq;
+ struct event_cmd_state *state;
+ struct run_event_context *run_ctx;
+ struct ctdb_event_request_run *rdata;
+ int ret;
+ bool continue_on_failure = false;
+
+ req = tevent_req_create(mem_ctx, &state, struct event_cmd_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->eventd = eventd;
+ state->request = request;
+ state->reply = reply;
+
+ rdata = request->data.run;
+
+ ret = eventd_run_ctx(eventd, rdata->component, &run_ctx);
+ if (ret != 0) {
+ state->reply->result = ret;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (rdata->flags & CTDB_EVENT_RUN_ALL) {
+ continue_on_failure = true;
+ }
+
+ subreq = run_event_send(state,
+ ev,
+ run_ctx,
+ rdata->event,
+ rdata->args,
+ tevent_timeval_current_ofs(rdata->timeout,0),
+ continue_on_failure);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, event_cmd_run_done, req);
+
+ return req;
+}
+
+static void event_cmd_run_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct event_cmd_state *state = tevent_req_data(
+ req, struct event_cmd_state);
+ struct run_event_script_list *script_list = NULL;
+ struct ctdb_event_request_run *rdata;
+ int ret;
+ bool ok;
+
+ ok = run_event_recv(subreq, &ret, state, &script_list);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ state->reply->result = ret;
+ goto done;
+ }
+
+ if (script_list == NULL) {
+ state->reply->result = EIO;
+ goto done;
+ }
+
+ if (script_list->summary == -ECANCELED) {
+ state->reply->result = ECANCELED;
+ goto done;
+ }
+
+ rdata = state->request->data.run;
+ ret = eventd_set_event_result(state->eventd,
+ rdata->component,
+ rdata->event,
+ script_list);
+ if (ret != 0) {
+ state->reply->result = ret;
+ goto done;
+ }
+
+ if (script_list->summary == -ETIMEDOUT) {
+ state->reply->result = ETIMEDOUT;
+ } else if (script_list->summary != 0) {
+ state->reply->result = ENOEXEC;
+ }
+
+done:
+ tevent_req_done(req);
+}
+
+/*
+ * CTDB_EVENT_CMD_STATUS
+ */
+
+static struct tevent_req *event_cmd_status_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ struct ctdb_event_request *request,
+ struct ctdb_event_reply *reply)
+{
+ struct tevent_req *req;
+ struct event_cmd_state *state;
+ struct ctdb_event_request_run *rdata;
+ struct run_event_script_list *script_list;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct event_cmd_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ reply->data.status = talloc_zero(reply,
+ struct ctdb_event_reply_status);
+ if (tevent_req_nomem(reply->data.status, req)) {
+ reply->result = ENOMEM;
+ goto done;
+ }
+
+ rdata = request->data.run;
+
+ ret = eventd_get_event_result(eventd,
+ rdata->component,
+ rdata->event,
+ &script_list);
+ if (ret != 0) {
+ reply->result = ret;
+ goto done;
+ }
+
+ reply->data.status->script_list = eventd_script_list(reply,
+ script_list);
+ if (reply->data.status->script_list == NULL) {
+ reply->result = ENOMEM;
+ goto done;
+ }
+ reply->data.status->summary = script_list->summary;
+
+ reply->result = 0;
+
+done:
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+/*
+ * CTDB_EVENT_CMD_SCRIPT
+ */
+
+static struct tevent_req *event_cmd_script_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ struct ctdb_event_request *request,
+ struct ctdb_event_reply *reply)
+{
+ struct tevent_req *req;
+ struct event_cmd_state *state;
+ struct run_event_context *run_ctx;
+ struct ctdb_event_request_script *rdata;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct event_cmd_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ rdata = request->data.script;
+
+ ret = eventd_run_ctx(eventd, rdata->component, &run_ctx);
+ if (ret != 0) {
+ reply->result = ret;
+ goto done;
+ }
+
+ if (rdata->action == CTDB_EVENT_SCRIPT_DISABLE) {
+ ret = run_event_script_disable(run_ctx, rdata->script);
+ } else if (rdata->action == CTDB_EVENT_SCRIPT_ENABLE) {
+ ret = run_event_script_enable(run_ctx, rdata->script);
+ } else {
+ D_ERR("Invalid action specified\n");
+ reply->result = EPROTO;
+ goto done;
+ }
+
+ if (ret != 0) {
+ reply->result = ret;
+ goto done;
+ }
+
+ reply->result = 0;
+
+done:
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static bool event_cmd_recv(struct tevent_req *req, int *perr)
+{
+ if (tevent_req_is_unix_error(req, perr)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+struct event_cmd_dispatch_state {
+ struct ctdb_event_reply *reply;
+};
+
+static void event_cmd_dispatch_done(struct tevent_req *subreq);
+
+struct tevent_req *event_cmd_dispatch_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct event_cmd_dispatch_state *state;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct event_cmd_dispatch_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ state->reply->cmd = request->cmd;
+
+ switch (request->cmd) {
+ case CTDB_EVENT_CMD_RUN:
+ subreq = event_cmd_run_send(state,
+ ev,
+ eventd,
+ request,
+ state->reply);
+ break;
+
+ case CTDB_EVENT_CMD_STATUS:
+ subreq = event_cmd_status_send(state,
+ ev,
+ eventd,
+ request,
+ state->reply);
+ break;
+
+ case CTDB_EVENT_CMD_SCRIPT:
+ subreq = event_cmd_script_send(state,
+ ev,
+ eventd,
+ request,
+ state->reply);
+ break;
+
+ default:
+ state->reply->result = EPROTO;
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, event_cmd_dispatch_done, req);
+
+ return req;
+}
+
+static void event_cmd_dispatch_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret;
+ bool ok;
+
+ ok = event_cmd_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+bool event_cmd_dispatch_recv(struct tevent_req *req,
+ int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct event_cmd_dispatch_state *state = tevent_req_data(
+ req, struct event_cmd_dispatch_state);
+
+ if (tevent_req_is_unix_error(req, perr)) {
+ return false;
+ }
+
+ *reply = talloc_steal(mem_ctx, state->reply);
+ return true;
+}
diff --git a/ctdb/event/event_conf.c b/ctdb/event/event_conf.c
new file mode 100644
index 0000000..61ecf8e
--- /dev/null
+++ b/ctdb/event/event_conf.c
@@ -0,0 +1,85 @@
+/*
+ CTDB event daemon
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/dir.h"
+
+#include "lib/util/debug.h"
+
+#include "common/conf.h"
+#include "common/path.h"
+
+#include "event/event_conf.h"
+
+static bool event_conf_validate_debug_script(const char *key,
+ const char *old_script,
+ const char *new_script,
+ enum conf_update_mode mode)
+{
+ char script[PATH_MAX];
+ char script_path[PATH_MAX];
+ struct stat st;
+ size_t len;
+ int ret;
+
+ len = strlcpy(script, new_script, sizeof(script));
+ if (len >= sizeof(script)) {
+ D_ERR("debug script name too long\n");
+ return false;
+ }
+
+ ret = snprintf(script_path,
+ sizeof(script_path),
+ "%s/%s",
+ path_etcdir(),
+ basename(script));
+ if (ret < 0 || (size_t)ret >= sizeof(script_path)) {
+ D_ERR("debug script path too long\n");
+ return false;
+ }
+
+ ret = stat(script_path, &st);
+ if (ret == -1) {
+ D_ERR("debug script %s does not exist\n", script_path);
+ return false;
+ }
+
+ if (! S_ISREG(st.st_mode)) {
+ D_ERR("debug script %s is not a file\n", script_path);
+ return false;
+ }
+ if (! (st.st_mode & S_IXUSR)) {
+ D_ERR("debug script %s is not executable\n", script_path);
+ return false;
+ }
+
+ return true;
+}
+
+void event_conf_init(struct conf_context *conf)
+{
+ conf_define_section(conf, EVENT_CONF_SECTION, NULL);
+
+ conf_define_string(conf,
+ EVENT_CONF_SECTION,
+ EVENT_CONF_DEBUG_SCRIPT,
+ NULL,
+ event_conf_validate_debug_script);
+}
diff --git a/ctdb/event/event_conf.h b/ctdb/event/event_conf.h
new file mode 100644
index 0000000..964a18a
--- /dev/null
+++ b/ctdb/event/event_conf.h
@@ -0,0 +1,31 @@
+/*
+ CTDB event daemon
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_EVENT_CONF_H__
+#define __CTDB_EVENT_CONF_H__
+
+#include "common/conf.h"
+
+#define EVENT_CONF_SECTION "event"
+
+#define EVENT_CONF_DEBUG_SCRIPT "debug script"
+
+void event_conf_init(struct conf_context *conf);
+
+#endif /* __CTDB_EVENT_CONF_H__ */
diff --git a/ctdb/event/event_config.c b/ctdb/event/event_config.c
new file mode 100644
index 0000000..d282622
--- /dev/null
+++ b/ctdb/event/event_config.c
@@ -0,0 +1,122 @@
+/*
+ CTDB event daemon - config handling
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include <talloc.h>
+
+#include "common/conf.h"
+#include "common/logging_conf.h"
+#include "common/path.h"
+
+#include "event/event_private.h"
+#include "event/event_conf.h"
+
+struct event_config {
+ char *config_file;
+ struct conf_context *conf;
+
+ const char *logging_location;
+ const char *logging_loglevel;
+ const char *debug_script;
+};
+
+int event_config_init(TALLOC_CTX *mem_ctx, struct event_config **result)
+{
+ struct event_config *config;
+ int ret;
+ bool ok;
+
+ config = talloc_zero(mem_ctx, struct event_config);
+ if (config == NULL) {
+ return ENOMEM;
+ }
+
+ config->config_file = path_config(config);
+ if (config->config_file == NULL) {
+ talloc_free(config);
+ return ENOMEM;
+ }
+
+ ret = conf_init(config, &config->conf);
+ if (ret != 0) {
+ talloc_free(config);
+ return ret;
+ }
+
+ logging_conf_init(config->conf, NULL);
+
+ conf_assign_string_pointer(config->conf,
+ LOGGING_CONF_SECTION,
+ LOGGING_CONF_LOCATION,
+ &config->logging_location);
+ conf_assign_string_pointer(config->conf,
+ LOGGING_CONF_SECTION,
+ LOGGING_CONF_LOG_LEVEL,
+ &config->logging_loglevel);
+
+ event_conf_init(config->conf);
+
+ conf_assign_string_pointer(config->conf,
+ EVENT_CONF_SECTION,
+ EVENT_CONF_DEBUG_SCRIPT,
+ &config->debug_script);
+
+ ok = conf_valid(config->conf);
+ if (!ok) {
+ talloc_free(config);
+ return EINVAL;
+ }
+
+ ret = conf_load(config->conf, config->config_file, true);
+ if (ret != 0 && ret != ENOENT) {
+ talloc_free(config);
+ return ret;
+ }
+
+ *result = config;
+ return 0;
+}
+
+const char *event_config_log_location(struct event_config *config)
+{
+ return config->logging_location;
+}
+
+const char *event_config_log_level(struct event_config *config)
+{
+ return config->logging_loglevel;
+}
+
+const char *event_config_debug_script(struct event_config *config)
+{
+ return config->debug_script;
+}
+
+int event_config_reload(struct event_config *config)
+{
+ int ret;
+
+ ret = conf_reload(config->conf);
+ if (ret != 0 && ret != ENOENT) {
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/ctdb/event/event_context.c b/ctdb/event/event_context.c
new file mode 100644
index 0000000..79bcd83
--- /dev/null
+++ b/ctdb/event/event_context.c
@@ -0,0 +1,472 @@
+/*
+ CTDB event daemon - daemon state
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/dir.h"
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/debug.h"
+#include "lib/util/dlinklist.h"
+
+#include "common/logging.h"
+#include "common/run_event.h"
+#include "common/path.h"
+
+#include "event/event_private.h"
+
+struct event_event {
+ struct event_event *prev, *next;
+
+ const char *name;
+ struct run_event_script_list *script_list;
+};
+
+struct event_component {
+ struct event_component *prev, *next;
+
+ /* component state */
+ const char *name;
+ const char *path;
+ struct run_event_context *run_ctx;
+
+ /* events list */
+ struct event_event *event;
+};
+
+struct event_client {
+ struct event_client *prev, *next;
+
+ struct sock_client_context *client;
+};
+
+struct event_context {
+ struct tevent_context *ev;
+ struct event_config *config;
+ struct run_proc_context *run_proc_ctx;
+
+ const char *script_dir;
+ const char *debug_script;
+
+ /* component list */
+ struct event_component *component;
+
+ /* client list */
+ struct event_client *client;
+};
+
+/*
+ * event_event functions
+ */
+
+static struct event_event *eventd_event_find(struct event_component *comp,
+ const char *event_name)
+{
+ struct event_event *event;
+
+ if (event_name == NULL) {
+ return NULL;
+ }
+
+ for (event = comp->event; event != NULL; event = event->next) {
+ if (strcmp(event->name, event_name) == 0) {
+ return event;
+ }
+ }
+
+ return NULL;
+}
+
+static int eventd_event_add(struct event_component *comp,
+ const char *event_name,
+ struct event_event **result)
+{
+ struct event_event *event;
+
+ if (event_name == NULL) {
+ return EINVAL;
+ }
+
+ event = eventd_event_find(comp, event_name);
+ if (event != NULL) {
+ goto done;
+ }
+
+ event = talloc_zero(comp, struct event_event);
+ if (event == NULL) {
+ return ENOMEM;
+ }
+
+ event->name = talloc_strdup(event, event_name);
+ if (event->name == NULL) {
+ talloc_free(event);
+ return ENOMEM;
+ }
+
+ DLIST_ADD_END(comp->event, event);
+
+done:
+ if (result != NULL) {
+ *result = event;
+ }
+ return 0;
+}
+
+static int eventd_event_set(struct event_component *comp,
+ const char *event_name,
+ struct run_event_script_list *script_list)
+{
+ struct event_event *event = NULL;
+ int ret;
+
+ ret = eventd_event_add(comp, event_name, &event);
+ if (ret != 0) {
+ return ret;
+ }
+
+ TALLOC_FREE(event->script_list);
+ if (script_list != NULL) {
+ event->script_list = talloc_steal(event, script_list);
+ }
+
+ return 0;
+}
+
+static int eventd_event_get(struct event_component *comp,
+ const char *event_name,
+ struct run_event_script_list **result)
+{
+ struct event_event *event;
+
+ event = eventd_event_find(comp, event_name);
+ if (event == NULL) {
+ return EINVAL;
+ }
+
+ *result = event->script_list;
+ return 0;
+}
+
+/*
+ * event_component functions
+ */
+
+static struct event_component *eventd_component_find(
+ struct event_context *eventd,
+ const char *comp_name)
+{
+ struct event_component *comp;
+
+ if (comp_name == NULL) {
+ return NULL;
+ }
+
+ for (comp = eventd->component; comp != NULL; comp = comp->next) {
+ if (strcmp(comp->name, comp_name) == 0) {
+ return comp;
+ }
+ }
+
+ return NULL;
+}
+
+static int eventd_component_add(struct event_context *eventd,
+ const char *comp_name,
+ struct event_component **result)
+{
+ struct event_component *comp;
+ int ret;
+
+ if (comp_name == NULL) {
+ return EINVAL;
+ }
+
+ comp = eventd_component_find(eventd, comp_name);
+ if (comp != NULL) {
+ goto done;
+ }
+
+ comp = talloc_zero(eventd, struct event_component);
+ if (comp == NULL) {
+ return ENOMEM;
+ }
+
+ comp->name = talloc_strdup(comp, comp_name);
+ if (comp->name == NULL) {
+ talloc_free(comp);
+ return ENOMEM;
+ }
+
+ comp->path = talloc_asprintf(comp,
+ "%s/%s",
+ eventd->script_dir,
+ comp_name);
+ if (comp->path == NULL) {
+ talloc_free(comp);
+ return ENOMEM;
+ }
+
+ ret = run_event_init(eventd,
+ eventd->run_proc_ctx,
+ comp->path,
+ eventd->debug_script,
+ &comp->run_ctx);
+ if (ret != 0) {
+ talloc_free(comp);
+ return ret;
+ }
+
+ DLIST_ADD_END(eventd->component, comp);
+
+done:
+ if (result != NULL) {
+ *result = comp;
+ }
+ return 0;
+}
+
+/*
+ * event_client functions
+ */
+
+static struct event_client *eventd_client_find(
+ struct event_context *eventd,
+ struct sock_client_context *client)
+{
+ struct event_client *e;
+
+ for (e = eventd->client; e != NULL; e = e->next) {
+ if (e->client == client) {
+ return e;
+ }
+ }
+
+ return NULL;
+}
+
+int eventd_client_add(struct event_context *eventd,
+ struct sock_client_context *client)
+{
+ struct event_client *e;
+
+ e = talloc_zero(eventd, struct event_client);
+ if (e == NULL) {
+ return ENOMEM;
+ }
+
+ e->client = client;
+
+ DLIST_ADD_END(eventd->client, e);
+
+ return 0;
+}
+
+void eventd_client_del(struct event_context *eventd,
+ struct sock_client_context *client)
+{
+ struct event_client *e;
+
+ e = eventd_client_find(eventd, client);
+ if (e == NULL) {
+ return;
+ }
+
+ DLIST_REMOVE(eventd->client, e);
+
+ talloc_free(e);
+}
+
+bool eventd_client_exists(struct event_context *eventd,
+ struct sock_client_context *client)
+{
+ struct event_client *e;
+
+ e = eventd_client_find(eventd, client);
+ if (e == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+/* public functions */
+
+int event_context_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_config *config,
+ struct event_context **result)
+{
+ struct event_context *eventd;
+ const char *debug_script;
+ int ret;
+
+ eventd = talloc_zero(mem_ctx, struct event_context);
+ if (eventd == NULL) {
+ return ENOMEM;
+ }
+
+ eventd->ev = ev;
+ eventd->config = config;
+
+ ret = run_proc_init(eventd, ev, &eventd->run_proc_ctx);
+ if (ret != 0) {
+ talloc_free(eventd);
+ return ret;
+ }
+
+ eventd->script_dir = path_etcdir_append(eventd, "events");
+ if (eventd->script_dir == NULL) {
+ talloc_free(eventd);
+ return ENOMEM;
+ }
+
+ /* FIXME
+ status = directory_exist(eventd->script_dir);
+ if (! status) {
+ talloc_free(eventd);
+ return EINVAL;
+ }
+ */
+
+ debug_script = event_config_debug_script(config);
+ if (debug_script != NULL) {
+ eventd->debug_script = path_etcdir_append(eventd,
+ debug_script);
+ if (eventd->debug_script == NULL) {
+ D_WARNING("Failed to set debug script to %s\n",
+ debug_script);
+ }
+ }
+
+ *result = eventd;
+ return 0;
+}
+
+struct event_config *eventd_config(struct event_context *eventd)
+{
+ return eventd->config;
+}
+
+int eventd_run_ctx(struct event_context *eventd,
+ const char *comp_name,
+ struct run_event_context **result)
+{
+ struct event_component *comp;
+ int ret;
+
+ ret = eventd_component_add(eventd, comp_name, &comp);
+ if (ret != 0) {
+ return ret;
+ }
+
+ *result = comp->run_ctx;
+ return 0;
+}
+
+int eventd_set_event_result(struct event_context *eventd,
+ const char *comp_name,
+ const char *event_name,
+ struct run_event_script_list *script_list)
+{
+ struct event_component *comp;
+
+ comp = eventd_component_find(eventd, comp_name);
+ if (comp == NULL) {
+ return ENOENT;
+ }
+
+ return eventd_event_set(comp, event_name, script_list);
+}
+
+int eventd_get_event_result(struct event_context *eventd,
+ const char *comp_name,
+ const char *event_name,
+ struct run_event_script_list **result)
+{
+ struct event_component *comp;
+ int ret;
+
+ ret = eventd_component_add(eventd, comp_name, &comp);
+ if (ret != 0) {
+ return ret;
+ }
+
+ return eventd_event_get(comp, event_name, result);
+}
+
+struct ctdb_event_script_list *eventd_script_list(
+ TALLOC_CTX *mem_ctx,
+ struct run_event_script_list *script_list)
+{
+ struct ctdb_event_script_list *value;
+ int num_scripts = 0;
+ int i;
+
+ value = talloc_zero(mem_ctx, struct ctdb_event_script_list);
+ if (value == NULL) {
+ return NULL;
+ }
+
+ if (script_list != NULL) {
+ num_scripts = script_list->num_scripts;
+ }
+
+ if (num_scripts <= 0) {
+ return value;
+ }
+
+ value->script = talloc_array(value,
+ struct ctdb_event_script,
+ num_scripts);
+ if (value->script == NULL) {
+ goto fail;
+ }
+
+ for (i=0; i<num_scripts; i++) {
+ struct run_event_script *rscript = &script_list->script[i];
+ struct ctdb_event_script *escript = &value->script[i];
+
+ escript->name = talloc_strdup(value, rscript->name);
+ if (escript->name == NULL) {
+ goto fail;
+ }
+
+ escript->begin = rscript->begin;
+ escript->end = rscript->end;
+ escript->result = rscript->summary;
+
+ if (rscript->output == NULL) {
+ escript->output = NULL;
+ continue;
+ }
+
+ escript->output = talloc_strdup(value, rscript->output);
+ if (escript->output == NULL) {
+ goto fail;
+ }
+ }
+ value->num_scripts = num_scripts;
+
+ return value;
+
+fail:
+ talloc_free(value);
+ return NULL;
+}
diff --git a/ctdb/event/event_daemon.c b/ctdb/event/event_daemon.c
new file mode 100644
index 0000000..d96ff6f
--- /dev/null
+++ b/ctdb/event/event_daemon.c
@@ -0,0 +1,382 @@
+/*
+ CTDB event daemon
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+
+#include <popt.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/tevent_unix.h"
+
+#include "common/logging.h"
+#include "common/path.h"
+#include "common/sock_daemon.h"
+
+#include "event/event_private.h"
+
+struct event_daemon_state {
+ TALLOC_CTX *mem_ctx;
+ char *socket;
+ char *pidfile;
+ struct tevent_context *ev;
+ struct event_config *config;
+ struct sock_daemon_context *sockd;
+ struct event_context *eventd;
+};
+
+static int event_daemon_startup(void *private_data)
+{
+ struct event_daemon_state *e_state = talloc_get_type_abort(
+ private_data, struct event_daemon_state);
+ int ret;
+
+ ret = event_context_init(e_state,
+ e_state->ev,
+ e_state->config,
+ &e_state->eventd);
+ if (ret != 0) {
+ D_ERR("Failed to initialize event context\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int event_daemon_reconfigure(void *private_data)
+{
+ struct event_daemon_state *e_state = talloc_get_type_abort(
+ private_data, struct event_daemon_state);
+ int ret;
+
+ ret = event_config_reload(e_state->config);
+ if (ret != 0) {
+ D_WARNING("Configuration reload failed\n");
+ }
+
+ return 0;
+}
+
+static int event_daemon_reopen_logs(void *private_data)
+{
+ bool status;
+
+ status = logging_reopen_logs();
+
+ return status ? 0 : 1;
+}
+
+static void event_daemon_shutdown(void *private_data)
+{
+ struct event_daemon_state *e_state = talloc_get_type_abort(
+ private_data, struct event_daemon_state);
+
+ TALLOC_FREE(e_state->eventd);
+}
+
+static bool event_client_connect(struct sock_client_context *client,
+ pid_t pid,
+ void *private_data)
+{
+ struct event_daemon_state *e_state = talloc_get_type_abort(
+ private_data, struct event_daemon_state);
+ int ret;
+
+ ret = eventd_client_add(e_state->eventd, client);
+ if (ret != 0) {
+ D_ERR("Failed to register client, ret=%d\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+static void event_client_disconnect(struct sock_client_context *client,
+ void *private_data)
+{
+ struct event_daemon_state *e_state = talloc_get_type_abort(
+ private_data, struct event_daemon_state);
+
+ eventd_client_del(e_state->eventd, client);
+}
+
+struct event_client_state {
+ struct tevent_context *ev;
+ struct event_context *eventd;
+ struct sock_client_context *client;
+ uint8_t *buf;
+ size_t buflen;
+};
+
+static void event_client_request_done(struct tevent_req *subreq);
+static void event_client_reply_done(struct tevent_req *subreq);
+
+static struct tevent_req *event_client_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct sock_client_context *client,
+ uint8_t *buf,
+ size_t buflen,
+ void *private_data)
+{
+ struct event_daemon_state *e_state = talloc_get_type_abort(
+ private_data, struct event_daemon_state);
+ struct tevent_req *req, *subreq;
+ struct event_client_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct event_client_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->ev = ev;
+ state->eventd = e_state->eventd;
+ state->client = client;
+
+ subreq = event_pkt_send(state, ev, e_state->eventd, buf, buflen);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, event_client_request_done, req);
+
+ return req;
+}
+
+static void event_client_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct event_client_state *state = tevent_req_data(
+ req, struct event_client_state);
+ int ret = 0;
+ bool ok;
+
+ ok = event_pkt_recv(subreq, &ret, state, &state->buf, &state->buflen);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ok = eventd_client_exists(state->eventd, state->client);
+ if (!ok) {
+ /* Client has already disconnected */
+ talloc_free(state->buf);
+ tevent_req_done(req);
+ return;
+ }
+
+ subreq = sock_socket_write_send(state,
+ state->ev,
+ state->client,
+ state->buf,
+ state->buflen);
+ if (tevent_req_nomem(subreq, req)) {
+ talloc_free(state->buf);
+ return;
+ }
+ tevent_req_set_callback(subreq, event_client_reply_done, req);
+}
+
+static void event_client_reply_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct event_client_state *state = tevent_req_data(
+ req, struct event_client_state);
+ int ret = 0;
+ bool ok;
+
+ talloc_free(state->buf);
+
+ ok = sock_socket_write_recv(subreq, &ret);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ D_ERR("Sending reply failed\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool event_client_recv(struct tevent_req *req, int *perr)
+{
+ if (tevent_req_is_unix_error(req, perr)) {
+ return false;
+ }
+
+ return true;
+}
+
+static struct {
+ int pid;
+ int startup_fd;
+} options = {
+ .pid = -1,
+ .startup_fd = -1,
+};
+
+struct poptOption cmdline_options[] = {
+ POPT_AUTOHELP
+ { "pid", 'P', POPT_ARG_INT, &options.pid, 0,
+ "pid to wait for", "PID" },
+ { "startup-fd", 'S', POPT_ARG_INT, &options.startup_fd, 0,
+ "file descriptor to notify of successful start", "FD" },
+ POPT_TABLEEND
+};
+
+int main(int argc, const char **argv)
+{
+ poptContext pc;
+ struct event_daemon_state *e_state;
+ struct sock_daemon_funcs daemon_funcs;
+ struct sock_socket_funcs socket_funcs;
+ const char *log_location = "file:";
+ const char *log_level = "NOTICE";
+ const char *t;
+ int interactive = 0;
+ int opt, ret;
+ bool ok;
+
+ pc = poptGetContext(argv[0],
+ argc,
+ argv,
+ cmdline_options,
+ 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ D_ERR("Invalid options %s: %s\n",
+ poptBadOption(pc, 0),
+ poptStrerror(opt));
+ exit(1);
+ }
+
+ t = getenv("CTDB_INTERACTIVE");
+ if (t != NULL) {
+ interactive = 1;
+ }
+
+ e_state = talloc_zero(NULL, struct event_daemon_state);
+ if (e_state == NULL) {
+ D_ERR("Memory allocation error\n");
+ ret = 1;
+ goto fail;
+ }
+
+ e_state->mem_ctx = talloc_new(e_state);
+ if (e_state->mem_ctx == NULL) {
+ D_ERR("Memory allocation error\n");
+ ret = 1;
+ goto fail;
+ }
+
+ e_state->socket = path_socket(e_state, "eventd");
+ if (e_state->socket == NULL) {
+ D_ERR("Memory allocation error\n");
+ ret = 1;
+ goto fail;
+ }
+
+ e_state->pidfile = path_pidfile(e_state, "eventd");
+ if (e_state->pidfile == NULL) {
+ D_ERR("Memory allocation error\n");
+ ret = 1;
+ goto fail;
+ }
+
+ ret = event_config_init(e_state, &e_state->config);
+ if (ret != 0) {
+ D_ERR("Failed to initialize event config\n");
+ goto fail;
+ }
+
+ e_state->ev = tevent_context_init(e_state->mem_ctx);
+ if (e_state->ev == NULL) {
+ D_ERR("Failed to initialize tevent\n");
+ ret = 1;
+ goto fail;
+ }
+
+ daemon_funcs = (struct sock_daemon_funcs) {
+ .startup = event_daemon_startup,
+ .reconfigure = event_daemon_reconfigure,
+ .reopen_logs = event_daemon_reopen_logs,
+ .shutdown = event_daemon_shutdown,
+ };
+
+ if (interactive == 0) {
+ log_location = event_config_log_location(e_state->config);
+ log_level = event_config_log_level(e_state->config);
+ }
+
+ ret = sock_daemon_setup(e_state->mem_ctx,
+ "ctdb-eventd",
+ log_location,
+ log_level,
+ &daemon_funcs,
+ e_state,
+ &e_state->sockd);
+ if (ret != 0) {
+ D_ERR("Failed to setup sock daemon\n");
+ goto fail;
+ }
+
+ socket_funcs = (struct sock_socket_funcs) {
+ .connect = event_client_connect,
+ .disconnect = event_client_disconnect,
+ .read_send = event_client_send,
+ .read_recv = event_client_recv,
+ };
+
+ ret = sock_daemon_add_unix(e_state->sockd,
+ e_state->socket,
+ &socket_funcs,
+ e_state);
+ if (ret != 0) {
+ D_ERR("Failed to setup socket %s\n", e_state->socket);
+ goto fail;
+ }
+
+ if (options.startup_fd != -1) {
+ ok = sock_daemon_set_startup_fd(e_state->sockd,
+ options.startup_fd);
+ if (!ok) {
+ goto fail;
+ }
+ }
+
+ ret = sock_daemon_run(e_state->ev,
+ e_state->sockd,
+ e_state->pidfile,
+ false,
+ false,
+ options.pid);
+ if (ret == EINTR) {
+ ret = 0;
+ }
+
+ if (t != NULL) {
+ talloc_report_full(e_state->mem_ctx, stderr);
+ }
+
+fail:
+ talloc_free(e_state);
+ (void)poptFreeContext(pc);
+ exit(ret);
+}
diff --git a/ctdb/event/event_private.h b/ctdb/event/event_private.h
new file mode 100644
index 0000000..0cc8d80
--- /dev/null
+++ b/ctdb/event/event_private.h
@@ -0,0 +1,103 @@
+/*
+ CTDB event daemon
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_EVENT_PRIVATE_H__
+#define __CTDB_EVENT_PRIVATE_H__
+
+#include <talloc.h>
+#include <tevent.h>
+
+#include "common/run_event.h"
+#include "common/sock_daemon.h"
+
+#include "event/event_protocol.h"
+
+struct event_config;
+struct event_context;
+
+/* From event/event_cmd.c */
+
+struct tevent_req *event_cmd_dispatch_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ struct ctdb_event_request *request);
+bool event_cmd_dispatch_recv(struct tevent_req *req,
+ int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply);
+
+/* From event/event_config.c */
+
+int event_config_init(TALLOC_CTX *mem_ctx, struct event_config **result);
+
+const char *event_config_log_location(struct event_config *config);
+const char *event_config_log_level(struct event_config *config);
+const char *event_config_debug_script(struct event_config *config);
+
+int event_config_reload(struct event_config *config);
+
+/* From event/event_context.c */
+
+int eventd_client_add(struct event_context *eventd,
+ struct sock_client_context *client);
+void eventd_client_del(struct event_context *eventd,
+ struct sock_client_context *client);
+bool eventd_client_exists(struct event_context *eventd,
+ struct sock_client_context *client);
+
+int event_context_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_config *config,
+ struct event_context **result);
+
+struct event_config *eventd_config(struct event_context *eventd);
+int eventd_run_ctx(struct event_context *eventd,
+ const char *comp_name,
+ struct run_event_context **result);
+
+int eventd_set_event_result(struct event_context *eventd,
+ const char *comp_name,
+ const char *event_name,
+ struct run_event_script_list *script_list);
+int eventd_get_event_result(struct event_context *eventd,
+ const char *comp_name,
+ const char *event_name,
+ struct run_event_script_list **result);
+
+struct ctdb_event_script_list *eventd_script_list(
+ TALLOC_CTX *mem_ctx,
+ struct run_event_script_list *script_list);
+
+
+/* From event/event_request.c */
+
+struct tevent_req *event_pkt_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ uint8_t *buf,
+ size_t buflen);
+
+bool event_pkt_recv(struct tevent_req *req,
+ int *perr,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **buf,
+ size_t *buflen);
+
+#endif /* __CTDB_EVENT_PRIVATE_H__ */
diff --git a/ctdb/event/event_protocol.c b/ctdb/event/event_protocol.c
new file mode 100644
index 0000000..baa9e1e
--- /dev/null
+++ b/ctdb/event/event_protocol.c
@@ -0,0 +1,1123 @@
+/*
+ CTDB event daemon protocol
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include <talloc.h>
+
+#include "protocol/protocol_basic.h"
+
+#include "event_protocol.h"
+#include "event_protocol_api.h"
+
+static size_t ctdb_event_script_action_len(enum ctdb_event_script_action in)
+{
+ uint32_t u32 = in;
+
+ return ctdb_uint32_len(&u32);
+}
+
+static void ctdb_event_script_action_push(enum ctdb_event_script_action in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ uint32_t u32 = in;
+
+ ctdb_uint32_push(&u32, buf, npush);
+}
+
+static int ctdb_event_script_action_pull(uint8_t *buf,
+ size_t buflen,
+ enum ctdb_event_script_action *out,
+ size_t *npull)
+{
+ enum ctdb_event_script_action value;
+ uint32_t u32;
+ size_t np;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf, buflen, &u32, &np);
+ if (ret != 0) {
+ return ret;
+ }
+
+ switch (u32) {
+ case 0:
+ value = CTDB_EVENT_SCRIPT_DISABLE;
+ break;
+
+ case 1:
+ value = CTDB_EVENT_SCRIPT_ENABLE;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ *out = value;
+ *npull = np;
+
+ return 0;
+}
+
+static size_t ctdb_event_command_len(enum ctdb_event_command in)
+{
+ uint32_t u32 = in;
+
+ return ctdb_uint32_len(&u32);
+}
+
+static void ctdb_event_command_push(enum ctdb_event_command in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ uint32_t u32 = in;
+
+ ctdb_uint32_push(&u32, buf, npush);
+}
+
+static int ctdb_event_command_pull(uint8_t *buf,
+ size_t buflen,
+ enum ctdb_event_command *out,
+ size_t *npull)
+{
+ enum ctdb_event_command value;
+ uint32_t u32;
+ size_t np;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf, buflen, &u32, &np);
+ if (ret != 0) {
+ return ret;
+ }
+
+ switch (u32) {
+ case 1:
+ value = CTDB_EVENT_CMD_RUN;
+ break;
+
+ case 2:
+ value = CTDB_EVENT_CMD_STATUS;
+ break;
+
+ case 3:
+ value = CTDB_EVENT_CMD_SCRIPT;
+ break;
+
+ default:
+ return EINVAL;
+ }
+
+ *out = value;
+ *npull = np;
+
+ return 0;
+}
+
+static size_t ctdb_event_script_len(struct ctdb_event_script *in)
+{
+ return ctdb_stringn_len(&in->name) +
+ ctdb_timeval_len(&in->begin) +
+ ctdb_timeval_len(&in->end) +
+ ctdb_int32_len(&in->result) +
+ ctdb_stringn_len(&in->output);
+}
+
+static void ctdb_event_script_push(struct ctdb_event_script *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_stringn_push(&in->name, buf+offset, &np);
+ offset += np;
+
+ ctdb_timeval_push(&in->begin, buf+offset, &np);
+ offset += np;
+
+ ctdb_timeval_push(&in->end, buf+offset, &np);
+ offset += np;
+
+ ctdb_int32_push(&in->result, buf+offset, &np);
+ offset += np;
+
+ ctdb_stringn_push(&in->output, buf+offset, &np);
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_script_pull_elems(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_script *value,
+ size_t *npull)
+{
+ size_t offset = 0, np;
+ int ret;
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ mem_ctx,
+ &value->name,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_timeval_pull(buf+offset,
+ buflen-offset,
+ &value->begin,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_timeval_pull(buf+offset,
+ buflen-offset,
+ &value->end,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_int32_pull(buf+offset,
+ buflen-offset,
+ &value->result,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ mem_ctx,
+ &value->output,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ *npull = offset;
+
+ return 0;
+}
+
+#ifdef EVENT_PROTOCOL_TEST
+static int ctdb_event_script_pull(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_script **out,
+ size_t *npull)
+{
+ struct ctdb_event_script *value;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_script);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_script_pull_elems(buf, buflen, value, value, npull);
+ if (ret != 0) {
+ talloc_free(value);
+ return ret;
+ }
+
+ *out = value;
+
+ return 0;
+}
+#endif
+
+static size_t ctdb_event_script_list_len(struct ctdb_event_script_list *in)
+{
+ size_t len;
+ int i;
+
+ len = ctdb_int32_len(&in->num_scripts);
+
+ for (i=0; i<in->num_scripts; i++) {
+ len += ctdb_event_script_len(&in->script[i]);
+ }
+
+ return len;
+}
+
+static void ctdb_event_script_list_push(struct ctdb_event_script_list *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+ int i;
+
+ ctdb_int32_push(&in->num_scripts, buf+offset, &np);
+ offset += np;
+
+ for (i=0; i<in->num_scripts; i++) {
+ ctdb_event_script_push(&in->script[i], buf+offset, &np);
+ offset += np;
+ }
+
+ *npush = offset;
+}
+
+static int ctdb_event_script_list_pull(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_script_list **out,
+ size_t *npull)
+{
+ struct ctdb_event_script_list *value = NULL;
+ size_t offset = 0, np;
+ int num_scripts;
+ int ret, i;
+
+ ret = ctdb_int32_pull(buf+offset, buflen-offset, &num_scripts, &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ if (num_scripts < 0) {
+ return EINVAL;
+ }
+
+ value = talloc_zero(mem_ctx, struct ctdb_event_script_list);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ value->num_scripts = num_scripts;
+ if (num_scripts == 0) {
+ goto done;
+ }
+
+ value->script = talloc_array(value, struct ctdb_event_script,
+ num_scripts);
+ if (value->script == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ for (i=0; i<num_scripts; i++) {
+ ret = ctdb_event_script_pull_elems(buf+offset,
+ buflen-offset,
+ value,
+ &value->script[i],
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+ }
+
+done:
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+static size_t ctdb_event_request_run_len(struct ctdb_event_request_run *in)
+{
+ return ctdb_stringn_len(&in->component) +
+ ctdb_stringn_len(&in->event) +
+ ctdb_stringn_len(&in->args) +
+ ctdb_uint32_len(&in->timeout) +
+ ctdb_uint32_len(&in->flags);
+}
+
+static void ctdb_event_request_run_push(struct ctdb_event_request_run *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_stringn_push(&in->component, buf+offset, &np);
+ offset += np;
+
+ ctdb_stringn_push(&in->event, buf+offset, &np);
+ offset += np;
+
+ ctdb_stringn_push(&in->args, buf+offset, &np);
+ offset += np;
+
+ ctdb_uint32_push(&in->timeout, buf+offset, &np);
+ offset += np;
+
+ ctdb_uint32_push(&in->flags, buf+offset, &np);
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_request_run_pull(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_run **out,
+ size_t *npull)
+{
+ struct ctdb_event_request_run *value;
+ size_t offset = 0, np;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_request_run);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->component,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->event,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->args,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_uint32_pull(buf+offset,
+ buflen-offset,
+ &value->timeout,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_uint32_pull(buf+offset,
+ buflen-offset,
+ &value->flags,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+static size_t ctdb_event_request_status_len(
+ struct ctdb_event_request_status *in)
+{
+ return ctdb_stringn_len(&in->component) +
+ ctdb_stringn_len(&in->event);
+}
+
+static void ctdb_event_request_status_push(
+ struct ctdb_event_request_status *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_stringn_push(&in->component, buf+offset, &np);
+ offset += np;
+
+ ctdb_stringn_push(&in->event, buf+offset, &np);
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_request_status_pull(
+ uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_status **out,
+ size_t *npull)
+{
+ struct ctdb_event_request_status *value;
+ size_t offset = 0, np;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_request_status);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->component,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->event,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+static size_t ctdb_event_request_script_len(
+ struct ctdb_event_request_script *in)
+{
+ return ctdb_stringn_len(&in->component) +
+ ctdb_stringn_len(&in->script) +
+ ctdb_event_script_action_len(in->action);
+}
+
+static void ctdb_event_request_script_push(
+ struct ctdb_event_request_script *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_stringn_push(&in->component, buf+offset, &np);
+ offset += np;
+
+ ctdb_stringn_push(&in->script, buf+offset, &np);
+ offset += np;
+
+ ctdb_event_script_action_push(in->action, buf+offset, &np);
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_request_script_pull(
+ uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_script **out,
+ size_t *npull)
+{
+ struct ctdb_event_request_script *value;
+ size_t offset = 0, np;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_request_script);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->component,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_stringn_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->script,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_event_script_action_pull(buf+offset,
+ buflen-offset,
+ &value->action,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+static size_t ctdb_event_reply_status_len(
+ struct ctdb_event_reply_status *in)
+{
+ return ctdb_int32_len(&in->summary) +
+ ctdb_event_script_list_len(in->script_list);
+}
+
+static void ctdb_event_reply_status_push(
+ struct ctdb_event_reply_status *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_int32_push(&in->summary, buf+offset, &np);
+ offset += np;
+
+ ctdb_event_script_list_push(in->script_list, buf+offset, &np);
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_reply_status_pull(
+ uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_status **out,
+ size_t *npull)
+{
+ struct ctdb_event_reply_status *value;
+ size_t offset = 0, np;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_reply_status);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_int32_pull(buf+offset, buflen-offset, &value->summary, &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_event_script_list_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->script_list,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+static size_t ctdb_event_header_len(struct ctdb_event_header *in)
+{
+ return ctdb_uint32_len(&in->length) +
+ ctdb_uint32_len(&in->version) +
+ ctdb_uint32_len(&in->reqid);
+}
+
+static void ctdb_event_header_push(struct ctdb_event_header *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_uint32_push(&in->length, buf+offset, &np);
+ offset += np;
+
+ ctdb_uint32_push(&in->version, buf+offset, &np);
+ offset += np;
+
+ ctdb_uint32_push(&in->reqid, buf+offset, &np);
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_header_pull(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *value,
+ size_t *npull)
+{
+ size_t offset = 0, np;
+ int ret;
+
+ ret = ctdb_uint32_pull(buf+offset,
+ buflen-offset,
+ &value->length,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_uint32_pull(buf+offset,
+ buflen-offset,
+ &value->version,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_uint32_pull(buf+offset,
+ buflen-offset,
+ &value->reqid,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ *npull = offset;
+
+ return 0;
+}
+
+int ctdb_event_header_extract(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *value)
+{
+ size_t np;
+
+ return ctdb_event_header_pull(buf, buflen, value, &np);
+}
+
+static size_t ctdb_event_request_data_len(struct ctdb_event_request *in)
+{
+ size_t len;
+
+ len = ctdb_event_command_len(in->cmd);
+
+ switch (in->cmd) {
+ case CTDB_EVENT_CMD_RUN:
+ len += ctdb_event_request_run_len(in->data.run);
+ break;
+
+ case CTDB_EVENT_CMD_STATUS:
+ len += ctdb_event_request_status_len(in->data.status);
+ break;
+
+ case CTDB_EVENT_CMD_SCRIPT:
+ len += ctdb_event_request_script_len(in->data.script);
+ break;
+
+ default:
+ break;
+ }
+
+ return len;
+}
+
+static void ctdb_event_request_data_push(struct ctdb_event_request *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_event_command_push(in->cmd, buf+offset, &np);
+ offset += np;
+
+ switch (in->cmd) {
+ case CTDB_EVENT_CMD_RUN:
+ ctdb_event_request_run_push(in->data.run, buf+offset, &np);
+ break;
+
+ case CTDB_EVENT_CMD_STATUS:
+ ctdb_event_request_status_push(in->data.status,
+ buf+offset,
+ &np);
+ break;
+
+ case CTDB_EVENT_CMD_SCRIPT:
+ ctdb_event_request_script_push(in->data.script,
+ buf+offset,
+ &np);
+ break;
+ default:
+ np = 0;
+ break;
+ }
+ offset += np;
+
+ *npush = offset;
+}
+
+static int ctdb_event_request_data_pull(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request **out,
+ size_t *npull)
+{
+ struct ctdb_event_request *value;
+ size_t offset = 0, np;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_request);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_command_pull(buf+offset,
+ buflen-offset,
+ &value->cmd,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ switch (value->cmd) {
+ case CTDB_EVENT_CMD_RUN:
+ ret = ctdb_event_request_run_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->data.run,
+ &np);
+ break;
+
+ case CTDB_EVENT_CMD_STATUS:
+ ret = ctdb_event_request_status_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->data.status,
+ &np);
+ break;
+
+ case CTDB_EVENT_CMD_SCRIPT:
+ ret = ctdb_event_request_script_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->data.script,
+ &np);
+ break;
+
+ default:
+ np = 0;
+ break;
+ }
+
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+static size_t ctdb_event_reply_data_len(struct ctdb_event_reply *in)
+{
+ size_t len;
+
+ len = ctdb_event_command_len(in->cmd) +
+ ctdb_int32_len(&in->result);
+
+ if (in->result != 0) {
+ goto done;
+ }
+
+ switch (in->cmd) {
+ case CTDB_EVENT_CMD_STATUS:
+ len += ctdb_event_reply_status_len(in->data.status);
+ break;
+
+ default:
+ break;
+ }
+
+done:
+ return len;
+}
+
+static void ctdb_event_reply_data_push(struct ctdb_event_reply *in,
+ uint8_t *buf,
+ size_t *npush)
+{
+ size_t offset = 0, np;
+
+ ctdb_event_command_push(in->cmd, buf+offset, &np);
+ offset += np;
+
+ ctdb_int32_push(&in->result, buf+offset, &np);
+ offset += np;
+
+ if (in->result != 0) {
+ goto done;
+ }
+
+ switch (in->cmd) {
+ case CTDB_EVENT_CMD_STATUS:
+ ctdb_event_reply_status_push(in->data.status, buf+offset, &np);
+ break;
+
+ default:
+ np = 0;
+ break;
+ }
+ offset += np;
+
+done:
+ *npush = offset;
+}
+
+static int ctdb_event_reply_data_pull(uint8_t *buf,
+ size_t buflen,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **out,
+ size_t *npull)
+{
+ struct ctdb_event_reply *value;
+ size_t offset = 0, np;
+ int ret;
+
+ value = talloc(mem_ctx, struct ctdb_event_reply);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ ret = ctdb_event_command_pull(buf+offset,
+ buflen-offset,
+ &value->cmd,
+ &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ ret = ctdb_int32_pull(buf+offset, buflen-offset, &value->result, &np);
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+ if (value->result != 0) {
+ goto done;
+ }
+
+ switch (value->cmd) {
+ case CTDB_EVENT_CMD_STATUS:
+ ret = ctdb_event_reply_status_pull(buf+offset,
+ buflen-offset,
+ value,
+ &value->data.status,
+ &np);
+ break;
+
+ default:
+ np = 0;
+ break;
+ }
+
+ if (ret != 0) {
+ goto fail;
+ }
+ offset += np;
+
+done:
+ *out = value;
+ *npull = offset;
+
+ return 0;
+
+fail:
+ talloc_free(value);
+ return ret;
+}
+
+size_t ctdb_event_request_len(struct ctdb_event_header *h,
+ struct ctdb_event_request *in)
+{
+ return ctdb_event_header_len(h) +
+ ctdb_event_request_data_len(in);
+}
+
+int ctdb_event_request_push(struct ctdb_event_header *h,
+ struct ctdb_event_request *in,
+ uint8_t *buf,
+ size_t *buflen)
+{
+ size_t len, offset = 0, np;
+
+ len = ctdb_event_request_len(h, in);
+ if (*buflen < len) {
+ *buflen = len;
+ return EMSGSIZE;
+ }
+
+ h->length = *buflen;
+
+ ctdb_event_header_push(h, buf+offset, &np);
+ offset += np;
+
+ ctdb_event_request_data_push(in, buf+offset, &np);
+ offset += np;
+
+ if (offset > *buflen) {
+ return EMSGSIZE;
+ }
+
+ return 0;
+}
+
+int ctdb_event_request_pull(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *h,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request **out)
+{
+ size_t offset = 0, np;
+ int ret;
+
+ ret = ctdb_event_header_pull(buf+offset, buflen-offset, h, &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_event_request_data_pull(buf+offset,
+ buflen-offset,
+ mem_ctx,
+ out,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ if (offset > buflen) {
+ return EMSGSIZE;
+ }
+
+ return 0;
+}
+
+size_t ctdb_event_reply_len(struct ctdb_event_header *h,
+ struct ctdb_event_reply *in)
+{
+ return ctdb_event_header_len(h) +
+ ctdb_event_reply_data_len(in);
+}
+
+int ctdb_event_reply_push(struct ctdb_event_header *h,
+ struct ctdb_event_reply *in,
+ uint8_t *buf,
+ size_t *buflen)
+{
+ size_t len, offset = 0, np;
+
+ len = ctdb_event_reply_len(h, in);
+ if (*buflen < len) {
+ *buflen = len;
+ return EMSGSIZE;
+ }
+
+ h->length = *buflen;
+
+ ctdb_event_header_push(h, buf+offset, &np);
+ offset += np;
+
+ ctdb_event_reply_data_push(in, buf+offset, &np);
+ offset += np;
+
+ if (offset > *buflen) {
+ return EMSGSIZE;
+ }
+
+ return 0;
+}
+
+int ctdb_event_reply_pull(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *h,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **out)
+{
+ size_t offset = 0, np;
+ int ret;
+
+ ret = ctdb_event_header_pull(buf+offset, buflen-offset, h, &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ ret = ctdb_event_reply_data_pull(buf+offset,
+ buflen-offset,
+ mem_ctx,
+ out,
+ &np);
+ if (ret != 0) {
+ return ret;
+ }
+ offset += np;
+
+ if (offset > buflen) {
+ return EMSGSIZE;
+ }
+
+ return 0;
+}
diff --git a/ctdb/event/event_protocol.h b/ctdb/event/event_protocol.h
new file mode 100644
index 0000000..e7680fc
--- /dev/null
+++ b/ctdb/event/event_protocol.h
@@ -0,0 +1,100 @@
+/*
+ CTDB event daemon protocol
+ Based on eventd code
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_EVENT_PROTOCOL_H__
+#define __CTDB_EVENT_PROTOCOL_H__
+
+#define CTDB_EVENT_PROTOCOL_VERSION 1
+
+enum ctdb_event_script_action {
+ CTDB_EVENT_SCRIPT_DISABLE = 0,
+ CTDB_EVENT_SCRIPT_ENABLE = 1,
+};
+
+enum ctdb_event_command {
+ CTDB_EVENT_CMD_RUN = 1,
+ CTDB_EVENT_CMD_STATUS = 2,
+ CTDB_EVENT_CMD_SCRIPT = 3,
+ CTDB_EVENT_CMD_MAX = 4,
+};
+
+struct ctdb_event_script {
+ const char *name;
+ struct timeval begin;
+ struct timeval end;
+ int result;
+ const char *output;
+};
+
+struct ctdb_event_script_list {
+ int num_scripts;
+ struct ctdb_event_script *script;
+};
+
+#define CTDB_EVENT_RUN_ALL 1
+
+struct ctdb_event_request_run {
+ const char *component;
+ const char *event;
+ const char *args;
+ uint32_t timeout;
+ uint32_t flags;
+};
+
+struct ctdb_event_request_status {
+ const char *component;
+ const char *event;
+};
+
+struct ctdb_event_request_script {
+ const char *component;
+ const char *script;
+ enum ctdb_event_script_action action;
+};
+
+struct ctdb_event_reply_status {
+ int32_t summary;
+ struct ctdb_event_script_list *script_list;
+};
+
+struct ctdb_event_header {
+ uint32_t length;
+ uint32_t version;
+ uint32_t reqid;
+};
+
+struct ctdb_event_request {
+ enum ctdb_event_command cmd;
+ union {
+ struct ctdb_event_request_run *run;
+ struct ctdb_event_request_status *status;
+ struct ctdb_event_request_script *script;
+ } data;
+};
+
+struct ctdb_event_reply {
+ enum ctdb_event_command cmd;
+ int32_t result;
+ union {
+ struct ctdb_event_reply_status *status;
+ } data;
+};
+
+#endif /* __CTDB_EVENT_PROTOCOL_H__ */
diff --git a/ctdb/event/event_protocol_api.h b/ctdb/event/event_protocol_api.h
new file mode 100644
index 0000000..e2ab439
--- /dev/null
+++ b/ctdb/event/event_protocol_api.h
@@ -0,0 +1,61 @@
+/*
+ CTDB event daemon protocol
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_EVENT_PROTOCOL_API_H__
+#define __CTDB_EVENT_PROTOCOL_API_H__
+
+#include <talloc.h>
+
+#include "event/event_protocol.h"
+
+/* From event/event_protocol.c */
+
+int ctdb_event_header_extract(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *h);
+
+size_t ctdb_event_request_len(struct ctdb_event_header *h,
+ struct ctdb_event_request *in);
+int ctdb_event_request_push(struct ctdb_event_header *h,
+ struct ctdb_event_request *in,
+ uint8_t *buf,
+ size_t *buflen);
+int ctdb_event_request_pull(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *h,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request **out);
+
+size_t ctdb_event_reply_len(struct ctdb_event_header *h,
+ struct ctdb_event_reply *in);
+int ctdb_event_reply_push(struct ctdb_event_header *h,
+ struct ctdb_event_reply *in,
+ uint8_t *buf,
+ size_t *buflen);
+int ctdb_event_reply_pull(uint8_t *buf,
+ size_t buflen,
+ struct ctdb_event_header *h,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **out);
+
+/* From event/event_protocol_util.c */
+
+const char *ctdb_event_command_to_string(enum ctdb_event_command cmd);
+
+#endif /* __CTDB_EVENT_PROTOCOL_API_H__ */
diff --git a/ctdb/event/event_protocol_test.c b/ctdb/event/event_protocol_test.c
new file mode 100644
index 0000000..8f34fa5
--- /dev/null
+++ b/ctdb/event/event_protocol_test.c
@@ -0,0 +1,412 @@
+/*
+ CTDB event daemon - protocol test
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include <talloc.h>
+#include <assert.h>
+
+#define EVENT_PROTOCOL_TEST
+#include "event/event_protocol.c"
+
+#include "tests/src/protocol_common_basic.h"
+
+/*
+ * Functions to fill and verify event protocol structures
+ */
+
+static void fill_ctdb_event_script(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_script *p)
+{
+ fill_ctdb_stringn(mem_ctx, &p->name);
+ fill_ctdb_timeval(&p->begin);
+ fill_ctdb_timeval(&p->end);
+ p->result = rand32i();
+ fill_ctdb_stringn(mem_ctx, &p->output);
+}
+
+static void verify_ctdb_event_script(struct ctdb_event_script *p1,
+ struct ctdb_event_script *p2)
+{
+ verify_ctdb_stringn(&p1->name, &p2->name);
+ verify_ctdb_timeval(&p1->begin, &p2->begin);
+ verify_ctdb_timeval(&p1->end, &p2->end);
+ assert(p1->result == p2->result);
+ verify_ctdb_stringn(&p1->output, &p2->output);
+}
+
+static void fill_ctdb_event_script_list(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_script_list *p)
+{
+ int i;
+
+ p->num_scripts = rand_int(32);
+ if (p->num_scripts > 0) {
+ p->script = talloc_array(mem_ctx,
+ struct ctdb_event_script,
+ p->num_scripts);
+ assert(p->script != NULL);
+
+ for (i=0; i<p->num_scripts; i++) {
+ fill_ctdb_event_script(mem_ctx, &p->script[i]);
+ }
+ } else {
+ p->script = NULL;
+ }
+}
+
+static void verify_ctdb_event_script_list(struct ctdb_event_script_list *p1,
+ struct ctdb_event_script_list *p2)
+{
+ int i;
+
+ assert(p1->num_scripts == p2->num_scripts);
+ for (i=0; i<p1->num_scripts; i++) {
+ verify_ctdb_event_script(&p1->script[i], &p2->script[i]);
+ }
+}
+
+static void fill_ctdb_event_request_run(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_run *p)
+{
+ fill_ctdb_stringn(mem_ctx, &p->component);
+ fill_ctdb_stringn(mem_ctx, &p->event);
+ fill_ctdb_stringn(mem_ctx, &p->args);
+ p->timeout = rand32();
+ p->flags = rand32();
+}
+
+static void verify_ctdb_event_request_run(struct ctdb_event_request_run *p1,
+ struct ctdb_event_request_run *p2)
+{
+ verify_ctdb_stringn(&p1->component, &p2->component);
+ verify_ctdb_stringn(&p1->event, &p2->event);
+ verify_ctdb_stringn(&p1->args, &p2->args);
+ assert(p1->timeout == p2->timeout);
+ assert(p1->flags == p2->flags);
+}
+
+static void fill_ctdb_event_request_status(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_status *p)
+{
+ fill_ctdb_stringn(mem_ctx, &p->component);
+ fill_ctdb_stringn(mem_ctx, &p->event);
+}
+
+static void verify_ctdb_event_request_status(
+ struct ctdb_event_request_status *p1,
+ struct ctdb_event_request_status *p2)
+{
+ verify_ctdb_stringn(&p1->component, &p2->component);
+ verify_ctdb_stringn(&p1->event, &p2->event);
+}
+
+static void fill_ctdb_event_request_script(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request_script *p)
+{
+ fill_ctdb_stringn(mem_ctx, &p->component);
+ fill_ctdb_stringn(mem_ctx, &p->script);
+ if (rand_int(1) == 0) {
+ p->action = CTDB_EVENT_SCRIPT_DISABLE;
+ } else {
+ p->action = CTDB_EVENT_SCRIPT_ENABLE;
+ }
+}
+
+static void fill_ctdb_event_reply_status(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply_status *p)
+{
+ p->summary = rand32i();
+ p->script_list = talloc(mem_ctx, struct ctdb_event_script_list);
+ assert(p->script_list != NULL);
+
+ fill_ctdb_event_script_list(mem_ctx, p->script_list);
+}
+
+static void verify_ctdb_event_reply_status(struct ctdb_event_reply_status *p1,
+ struct ctdb_event_reply_status *p2)
+{
+ assert(p1->summary == p2->summary);
+ verify_ctdb_event_script_list(p1->script_list, p2->script_list);
+}
+
+static void verify_ctdb_event_request_script(
+ struct ctdb_event_request_script *p1,
+ struct ctdb_event_request_script *p2)
+{
+ verify_ctdb_stringn(&p1->component, &p2->component);
+ verify_ctdb_stringn(&p1->script, &p2->script);
+ assert(p1->action == p2->action);
+}
+
+static void fill_ctdb_event_request_data(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request *p,
+ uint32_t cmd)
+{
+ p->cmd = cmd;
+
+ switch (cmd) {
+ case CTDB_EVENT_CMD_RUN:
+ p->data.run = talloc(mem_ctx, struct ctdb_event_request_run);
+ assert(p->data.run != NULL);
+
+ fill_ctdb_event_request_run(mem_ctx, p->data.run);
+ break;
+
+ case CTDB_EVENT_CMD_STATUS:
+ p->data.status = talloc(mem_ctx,
+ struct ctdb_event_request_status);
+ assert(p->data.status != NULL);
+
+ fill_ctdb_event_request_status(mem_ctx, p->data.status);
+ break;
+
+ case CTDB_EVENT_CMD_SCRIPT:
+ p->data.script = talloc(mem_ctx,
+ struct ctdb_event_request_script);
+ assert(p->data.script != NULL);
+
+ fill_ctdb_event_request_script(mem_ctx, p->data.script);
+ break;
+
+ default:
+ assert(cmd > 0 && cmd < CTDB_EVENT_CMD_MAX);
+ }
+}
+
+static void verify_ctdb_event_request_data(struct ctdb_event_request *p1,
+ struct ctdb_event_request *p2)
+{
+ assert(p1->cmd == p2->cmd);
+
+ switch (p1->cmd) {
+ case CTDB_EVENT_CMD_RUN:
+ verify_ctdb_event_request_run(p1->data.run, p2->data.run);
+ break;
+
+ case CTDB_EVENT_CMD_STATUS:
+ verify_ctdb_event_request_status(p1->data.status,
+ p2->data.status);
+ break;
+
+ case CTDB_EVENT_CMD_SCRIPT:
+ verify_ctdb_event_request_script(p1->data.script,
+ p2->data.script);
+ break;
+
+ default:
+ assert(p1->cmd > 0 && p1->cmd < CTDB_EVENT_CMD_MAX);
+ }
+}
+
+static void fill_ctdb_event_reply_data(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply *p,
+ uint32_t cmd)
+{
+ p->cmd = cmd;
+ p->result = rand32i();
+
+ if (p->result != 0) {
+ return;
+ }
+
+ switch (cmd) {
+ case CTDB_EVENT_CMD_STATUS:
+ p->data.status = talloc(mem_ctx,
+ struct ctdb_event_reply_status);
+ assert(p->data.status != NULL);
+
+ fill_ctdb_event_reply_status(mem_ctx, p->data.status);
+ break;
+
+ default:
+ assert(cmd > 0 && cmd < CTDB_EVENT_CMD_MAX);
+ }
+}
+
+static void verify_ctdb_event_reply_data(struct ctdb_event_reply *p1,
+ struct ctdb_event_reply *p2)
+{
+ assert(p1->cmd == p2->cmd);
+ assert(p1->result == p2->result);
+
+ if (p1->result != 0) {
+ return;
+ }
+
+ switch (p1->cmd) {
+ case CTDB_EVENT_CMD_STATUS:
+ verify_ctdb_event_reply_status(p1->data.status,
+ p2->data.status);
+ break;
+
+ default:
+ assert(p1->cmd > 0 && p1->cmd < CTDB_EVENT_CMD_MAX);
+ }
+}
+
+static void fill_ctdb_event_header(struct ctdb_event_header *p)
+{
+ p->length = 0; /* updated by push functions */
+ p->version = 0; /* updated by push functions */
+ p->reqid = rand32();
+}
+
+static void verify_ctdb_event_header(struct ctdb_event_header *p1,
+ struct ctdb_event_header *p2)
+{
+ assert(p1->length == p2->length);
+ assert(p1->version == p2->version);
+ assert(p1->reqid == p2->reqid);
+}
+
+static void fill_ctdb_event_request(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_request *p,
+ uint32_t cmd)
+{
+ fill_ctdb_event_request_data(mem_ctx, p, cmd);
+}
+
+static void verify_ctdb_event_request(struct ctdb_event_request *p1,
+ struct ctdb_event_request *p2)
+{
+ verify_ctdb_event_request_data(p1, p2);
+}
+
+static void fill_ctdb_event_reply(TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply *p,
+ uint32_t cmd)
+{
+ fill_ctdb_event_reply_data(mem_ctx, p, cmd);
+}
+
+static void verify_ctdb_event_reply(struct ctdb_event_reply *p1,
+ struct ctdb_event_reply *p2)
+{
+ verify_ctdb_event_reply_data(p1, p2);
+}
+
+#define EVENT_PROTOCOL1_TEST(TYPE, NAME) \
+static void TEST_FUNC(NAME)(uint32_t cmd) \
+{ \
+ TALLOC_CTX *mem_ctx; \
+ TYPE c1, *c2; \
+ uint8_t *buf; \
+ size_t buflen, np; \
+ int ret; \
+\
+ protocol_test_iterate_tag("%s %u\n", #NAME, cmd); \
+ mem_ctx = talloc_new(NULL); \
+ assert(mem_ctx != NULL); \
+ FILL_FUNC(NAME)(mem_ctx, &c1, cmd); \
+ buflen = LEN_FUNC(NAME)(&c1); \
+ buf = talloc_size(mem_ctx, buflen); \
+ assert(buf != NULL); \
+ np = 0; \
+ PUSH_FUNC(NAME)(&c1, buf, &np); \
+ assert(np == buflen); \
+ np = 0; \
+ ret = PULL_FUNC(NAME)(buf, buflen, mem_ctx, &c2, &np); \
+ assert(ret == 0); \
+ assert(np == buflen); \
+ VERIFY_FUNC(NAME)(&c1, c2); \
+ talloc_free(mem_ctx); \
+}
+
+#define EVENT_PROTOCOL2_TEST(TYPE, NAME) \
+static void TEST_FUNC(NAME)(uint32_t cmd) \
+{ \
+ TALLOC_CTX *mem_ctx; \
+ struct ctdb_event_header h1, h2; \
+ TYPE c1, *c2; \
+ uint8_t *buf; \
+ size_t buflen, len; \
+ int ret; \
+\
+ protocol_test_iterate_tag("%s %u\n", #NAME, cmd); \
+ mem_ctx = talloc_new(NULL); \
+ assert(mem_ctx != NULL); \
+ fill_ctdb_event_header(&h1); \
+ FILL_FUNC(NAME)(mem_ctx, &c1, cmd); \
+ buflen = LEN_FUNC(NAME)(&h1, &c1); \
+ buf = talloc_size(mem_ctx, buflen); \
+ assert(buf != NULL); \
+ len = 0; \
+ ret = PUSH_FUNC(NAME)(&h1, &c1, buf, &len); \
+ assert(ret == EMSGSIZE); \
+ assert(len == buflen); \
+ ret = PUSH_FUNC(NAME)(&h1, &c1, buf, &buflen); \
+ assert(ret == 0); \
+ ret = PULL_FUNC(NAME)(buf, buflen, &h2, mem_ctx, &c2); \
+ assert(ret == 0); \
+ verify_ctdb_event_header(&h1, &h2); \
+ VERIFY_FUNC(NAME)(&c1, c2); \
+ talloc_free(mem_ctx); \
+}
+
+PROTOCOL_TYPE3_TEST(struct ctdb_event_script, ctdb_event_script);
+PROTOCOL_TYPE3_TEST(struct ctdb_event_script_list, ctdb_event_script_list);
+
+PROTOCOL_TYPE3_TEST(struct ctdb_event_request_run, ctdb_event_request_run);
+PROTOCOL_TYPE3_TEST(struct ctdb_event_request_status,
+ ctdb_event_request_status);
+PROTOCOL_TYPE3_TEST(struct ctdb_event_request_script,
+ ctdb_event_request_script);
+
+PROTOCOL_TYPE3_TEST(struct ctdb_event_reply_status, ctdb_event_reply_status);
+
+EVENT_PROTOCOL1_TEST(struct ctdb_event_request, ctdb_event_request_data);
+EVENT_PROTOCOL1_TEST(struct ctdb_event_reply, ctdb_event_reply_data);
+
+EVENT_PROTOCOL2_TEST(struct ctdb_event_request, ctdb_event_request);
+EVENT_PROTOCOL2_TEST(struct ctdb_event_reply, ctdb_event_reply);
+
+static void event_protocol_test(void)
+{
+ uint32_t cmd;
+
+ TEST_FUNC(ctdb_event_script)();
+ TEST_FUNC(ctdb_event_script_list)();
+
+ TEST_FUNC(ctdb_event_request_run)();
+ TEST_FUNC(ctdb_event_request_status)();
+ TEST_FUNC(ctdb_event_request_script)();
+
+ TEST_FUNC(ctdb_event_reply_status)();
+
+ for (cmd=1; cmd<CTDB_EVENT_CMD_MAX; cmd++) {
+ TEST_FUNC(ctdb_event_request_data)(cmd);
+ }
+ for (cmd=1; cmd<CTDB_EVENT_CMD_MAX; cmd++) {
+ TEST_FUNC(ctdb_event_reply_data)(cmd);
+ }
+
+ for (cmd=1; cmd<CTDB_EVENT_CMD_MAX; cmd++) {
+ TEST_FUNC(ctdb_event_request)(cmd);
+ }
+ for (cmd=1; cmd<CTDB_EVENT_CMD_MAX; cmd++) {
+ TEST_FUNC(ctdb_event_reply)(cmd);
+ }
+}
+
+int main(int argc, const char **argv)
+{
+ protocol_test_iterate(argc, argv, event_protocol_test);
+ return 0;
+}
diff --git a/ctdb/event/event_protocol_util.c b/ctdb/event/event_protocol_util.c
new file mode 100644
index 0000000..cc59615
--- /dev/null
+++ b/ctdb/event/event_protocol_util.c
@@ -0,0 +1,46 @@
+/*
+ CTDB event daemon - protocol utilities
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include "event/event_protocol.h"
+#include "event/event_protocol_api.h"
+
+static struct {
+ enum ctdb_event_command command;
+ const char *label;
+} event_command_map[] = {
+ { CTDB_EVENT_CMD_RUN, "RUN" },
+ { CTDB_EVENT_CMD_STATUS, "STATUS" },
+ { CTDB_EVENT_CMD_SCRIPT, "SCRIPT" },
+ { CTDB_EVENT_CMD_MAX, NULL },
+};
+
+const char *ctdb_event_command_to_string(enum ctdb_event_command command)
+{
+ int i;
+
+ for (i=0; event_command_map[i].label != NULL; i++) {
+ if (event_command_map[i].command == command) {
+ return event_command_map[i].label;
+ }
+ }
+
+ return "UNKNOWN";
+}
diff --git a/ctdb/event/event_request.c b/ctdb/event/event_request.c
new file mode 100644
index 0000000..303e735
--- /dev/null
+++ b/ctdb/event/event_request.c
@@ -0,0 +1,217 @@
+/*
+ CTDB event daemon - handle requests
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+
+#include "lib/util/debug.h"
+#include "lib/util/tevent_unix.h"
+
+#include "common/logging.h"
+
+#include "event/event_private.h"
+#include "event/event_protocol_api.h"
+
+struct event_request_state {
+ struct ctdb_event_request *request;
+ struct ctdb_event_reply *reply;
+};
+
+static void event_request_done(struct tevent_req *subreq);
+
+static struct tevent_req *event_request_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ struct ctdb_event_header *header,
+ struct ctdb_event_request *request)
+{
+ struct tevent_req *req, *subreq;
+ struct event_request_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct event_request_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->request = request;
+
+ subreq = event_cmd_dispatch_send(state, ev, eventd, request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, event_request_done, req);
+
+ return req;
+}
+
+static void event_request_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct event_request_state *state = tevent_req_data(
+ req, struct event_request_state);
+ int ret;
+ bool ok;
+
+ ok = event_cmd_dispatch_recv(subreq, &ret, state, &state->reply);
+ TALLOC_FREE(subreq);
+ if (!ok) {
+ D_ERR("Command %s failed, ret=%d\n",
+ ctdb_event_command_to_string(state->request->cmd), ret);
+
+ state->reply = talloc_zero(state, struct ctdb_event_reply);
+ if (tevent_req_nomem(state->reply, req)) {
+ return;
+ }
+
+ state->reply->cmd = state->request->cmd;
+ state->reply->result = EIO;
+ }
+
+ tevent_req_done(req);
+}
+
+static bool event_request_recv(struct tevent_req *req,
+ int *perr,
+ TALLOC_CTX *mem_ctx,
+ struct ctdb_event_reply **reply)
+{
+ struct event_request_state *state = tevent_req_data(
+ req, struct event_request_state);
+
+ if (tevent_req_is_unix_error(req, perr)) {
+ return false;
+ }
+
+ *reply = talloc_steal(mem_ctx, state->reply);
+
+ return true;
+}
+
+struct event_pkt_state {
+ struct ctdb_event_header header;
+ struct ctdb_event_request *request;
+ uint8_t *buf;
+ size_t buflen;
+};
+
+static void event_pkt_done(struct tevent_req *subreq);
+
+struct tevent_req *event_pkt_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct event_context *eventd,
+ uint8_t *buf,
+ size_t buflen)
+{
+ struct tevent_req *req, *subreq;
+ struct event_pkt_state *state;
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct event_pkt_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ ret = ctdb_event_request_pull(buf,
+ buflen,
+ &state->header,
+ state,
+ &state->request);
+ if (ret != 0) {
+ /* Ignore invalid packets */
+ D_ERR("Invalid packet received, buflen=%zu\n", buflen);
+ tevent_req_error(req, EPROTO);
+ return tevent_req_post(req, ev);
+ }
+
+ subreq = event_request_send(state,
+ ev,
+ eventd,
+ &state->header,
+ state->request);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, event_pkt_done, req);
+
+ return req;
+}
+
+static void event_pkt_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct event_pkt_state *state = tevent_req_data(
+ req, struct event_pkt_state);
+ struct ctdb_event_header header;
+ struct ctdb_event_reply *reply;
+ int ret;
+ bool ok;
+
+ ok = event_request_recv(subreq, &ret, state, &reply);
+ TALLOC_FREE(subreq);
+ TALLOC_FREE(state->request);
+ if (!ok) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ header = (struct ctdb_event_header) {
+ .reqid = state->header.reqid,
+ };
+
+ state->buflen = ctdb_event_reply_len(&header, reply);
+ state->buf = talloc_zero_size(state, state->buflen);
+ if (tevent_req_nomem(state->buf, req)) {
+ talloc_free(reply);
+ return;
+ }
+
+ ret = ctdb_event_reply_push(&header,
+ reply,
+ state->buf,
+ &state->buflen);
+ talloc_free(reply);
+ if (ret != 0) {
+ talloc_free(state->buf);
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+bool event_pkt_recv(struct tevent_req *req,
+ int *perr,
+ TALLOC_CTX *mem_ctx,
+ uint8_t **buf,
+ size_t *buflen)
+{
+ struct event_pkt_state *state = tevent_req_data(
+ req, struct event_pkt_state);
+
+ if (tevent_req_is_unix_error(req, perr)) {
+ return false;
+ }
+
+ *buf = talloc_steal(mem_ctx, state->buf);
+ *buflen = state->buflen;
+
+ return true;
+}
diff --git a/ctdb/event/event_tool.c b/ctdb/event/event_tool.c
new file mode 100644
index 0000000..d6b7156
--- /dev/null
+++ b/ctdb/event/event_tool.c
@@ -0,0 +1,846 @@
+/*
+ CTDB event daemon utility code
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+
+#include <popt.h>
+#include <talloc.h>
+#include <tevent.h>
+
+#include "lib/util/debug.h"
+
+#include "common/cmdline.h"
+#include "common/logging.h"
+#include "common/path.h"
+#include "common/event_script.h"
+
+#include "event/event_protocol_api.h"
+#include "event/event.h"
+#include "event/event_tool.h"
+
+struct event_tool_context {
+ struct cmdline_context *cmdline;
+ struct tevent_context *ev;
+ struct ctdb_event_context *eclient;
+};
+
+static int compact_args(TALLOC_CTX *mem_ctx,
+ const char **argv,
+ int argc,
+ int from,
+ const char **result)
+{
+ char *arg_str;
+ int i;
+
+ if (argc <= from) {
+ *result = NULL;
+ return 0;
+ }
+
+ arg_str = talloc_strdup(mem_ctx, argv[from]);
+ if (arg_str == NULL) {
+ return ENOMEM;
+ }
+
+ for (i = from+1; i < argc; i++) {
+ arg_str = talloc_asprintf_append(arg_str, " %s", argv[i]);
+ if (arg_str == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ *result = arg_str;
+ return 0;
+}
+
+static int event_command_run(TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ void *private_data)
+{
+ struct event_tool_context *ctx = talloc_get_type_abort(
+ private_data, struct event_tool_context);
+ struct tevent_req *req;
+ struct ctdb_event_request_run request_run;
+ const char *arg_str = NULL;
+ const char *t;
+ int timeout, ret = 0, result = 0;
+ bool ok;
+
+ if (argc < 3) {
+ cmdline_usage(ctx->cmdline, "run");
+ return 1;
+ }
+
+ ret = ctdb_event_init(ctx, ctx->ev, &ctx->eclient);
+ if (ret != 0) {
+ D_ERR("Failed to initialize event client, ret=%d\n", ret);
+ return ret;
+ }
+
+ timeout = atoi(argv[0]);
+ if (timeout < 0) {
+ timeout = 0;
+ }
+
+ ret = compact_args(mem_ctx, argv, argc, 3, &arg_str);
+ if (ret != 0) {
+ D_ERR("Memory allocation error\n");
+ return 1;
+ }
+
+ request_run.component = argv[1];
+ request_run.event = argv[2];
+ request_run.args = arg_str;
+ request_run.timeout = timeout;
+ request_run.flags = 0;
+
+ t = getenv("CTDB_TEST_MODE");
+ if (t != NULL) {
+ t = getenv("CTDB_EVENT_RUN_ALL");
+ if (t != NULL) {
+ request_run.flags = CTDB_EVENT_RUN_ALL;
+ }
+ }
+
+ req = ctdb_event_run_send(mem_ctx,
+ ctx->ev,
+ ctx->eclient,
+ &request_run);
+ if (req == NULL) {
+ D_ERR("Memory allocation error\n");
+ return 1;
+ }
+
+ tevent_req_poll(req, ctx->ev);
+
+ ok = ctdb_event_run_recv(req, &ret, &result);
+ if (!ok) {
+ D_ERR("Failed to run event %s in %s, ret=%d\n",
+ argv[2],
+ argv[1],
+ ret);
+ return 1;
+ }
+
+ D_NOTICE("Command run finished with result=%d\n", result);
+
+ if (result == ENOENT) {
+ printf("Event dir for %s does not exist\n", argv[1]);
+ } else if (result == ETIMEDOUT) {
+ printf("Event %s in %s timed out\n", argv[2], argv[1]);
+ } else if (result == ECANCELED) {
+ printf("Event %s in %s got cancelled\n", argv[2], argv[1]);
+ } else if (result == ENOEXEC) {
+ printf("Event %s in %s failed\n", argv[2], argv[1]);
+ } else if (result != 0) {
+ printf("Failed to run event %s in %s, result=%d\n",
+ argv[2],
+ argv[1],
+ result);
+ }
+
+ ret = (result < 0) ? -result : result;
+ return ret;
+}
+
+static double timeval_delta(struct timeval *tv2, struct timeval *tv)
+{
+ return (tv2->tv_sec - tv->tv_sec) +
+ (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
+}
+
+static void print_status_one(struct ctdb_event_script *script)
+{
+ if (script->result == -ETIMEDOUT) {
+ printf("%-20s %-10s %s",
+ script->name,
+ "TIMEDOUT",
+ ctime(&script->begin.tv_sec));
+ } else if (script->result == -ENOEXEC) {
+ printf("%-20s %-10s\n", script->name, "DISABLED");
+ } else if (script->result < 0) {
+ printf("%-20s %-10s (%s)\n",
+ script->name,
+ "CANNOT RUN",
+ strerror(-script->result));
+ } else if (script->result == 0) {
+ printf("%-20s %-10s %.3lf %s",
+ script->name,
+ "OK",
+ timeval_delta(&script->end, &script->begin),
+ ctime(&script->begin.tv_sec));
+ } else {
+ printf("%-20s %-10s %.3lf %s",
+ script->name,
+ "ERROR",
+ timeval_delta(&script->end, &script->begin),
+ ctime(&script->begin.tv_sec));
+ }
+
+ if ((script->result != 0 && script->result != -ENOEXEC) ||
+ script->output != NULL) {
+ /* Empty output is informative so always print it on failure */
+ const char *t = script->output == NULL ? "" : script->output;
+ size_t len = strlen(t);
+ char output[len+1];
+ char *t1, *t2;
+
+ strlcpy(output, t, sizeof(output));
+
+ /*
+ * Strip trailing newlines, they are clutter and
+ * interfere with multi-line detection
+ */
+ t1 = output + len - 1;
+ while (t1 >= output && *t1 == '\n') {
+ *t1 = '\0';
+ t1--;
+ }
+
+ /* If the output is a single line then print it inline */
+ t2 = strchr(output, '\n');
+ if (t2 == NULL) {
+ printf(" OUTPUT: %s\n", output);
+ return;
+ }
+
+ /*
+ * More than 1 line. Print a header and then each
+ * line, with suitable indent. There are more general
+ * ways to do this, but let's maintain intermediate
+ * blank lines (e.g. strv_split() loses blank lines).
+ */
+ printf(" OUTPUT:\n");
+ t1 = output;
+ do {
+ /*
+ * Points to newline character. t2 initially
+ * set non-NULL outside loop because this loop
+ * only covers multi-line output.
+ */
+ *t2 = '\0';
+
+
+ printf(" %s\n", t1);
+ t1 = t2 + 1;
+
+ if (t1 >= output + len) {
+ break;
+ }
+
+ /* strchrnul() would be awesome, but isn't portable */
+ t2 = strchr(t1, '\n');
+ if (t2 == NULL) {
+ t2 = output + len;
+ }
+ } while (true);
+ }
+}
+
+static void print_status(const char *component,
+ const char *event,
+ int result,
+ struct ctdb_event_reply_status *status)
+{
+ int i;
+
+ if (result != 0) {
+ if (result == ENOENT) {
+ printf("Event dir for %s does not exist\n", component);
+ } else if (result == EINVAL) {
+ printf("Event %s has never run in %s\n",
+ event,
+ component);
+ } else {
+ printf("Unknown error (%d) for event %s in %s\n",
+ result,
+ event,
+ component);
+ }
+ return;
+ }
+
+ for (i=0; i<status->script_list->num_scripts; i++) {
+ print_status_one(&status->script_list->script[i]);
+ }
+}
+
+static int event_command_status(TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ void *private_data)
+{
+ struct event_tool_context *ctx = talloc_get_type_abort(
+ private_data, struct event_tool_context);
+ struct tevent_req *req;
+ struct ctdb_event_request_status request_status;
+ struct ctdb_event_reply_status *reply_status;
+ int ret = 0, result = 0;
+ bool ok;
+
+ if (argc != 2) {
+ cmdline_usage(ctx->cmdline, "status");
+ return 1;
+ }
+
+ ret = ctdb_event_init(ctx, ctx->ev, &ctx->eclient);
+ if (ret != 0) {
+ D_ERR("Failed to initialize event client, ret=%d\n", ret);
+ return ret;
+ }
+
+ request_status.component = argv[0];
+ request_status.event = argv[1];
+
+ req = ctdb_event_status_send(mem_ctx,
+ ctx->ev,
+ ctx->eclient,
+ &request_status);
+ if (req == NULL) {
+ D_ERR("Memory allocation error\n");
+ return 1;
+ }
+
+ tevent_req_poll(req, ctx->ev);
+
+ ok = ctdb_event_status_recv(req,
+ &ret,
+ &result,
+ mem_ctx,
+ &reply_status);
+ if (!ok) {
+ D_ERR("Failed to get status for event %s in %s, ret=%d\n",
+ argv[1],
+ argv[0],
+ ret);
+ return 1;
+ }
+
+ D_NOTICE("Command status finished with result=%d\n", result);
+
+ print_status(argv[0], argv[1], result, reply_status);
+
+ if (reply_status == NULL) {
+ ret = result;
+ } else {
+ ret = reply_status->summary;
+ ret = (ret < 0) ? -ret : ret;
+ }
+ return ret;
+}
+
+#define EVENT_SCRIPT_DISABLED ' '
+#define EVENT_SCRIPT_ENABLED '*'
+
+static int event_command_script_list(TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ void *private_data)
+{
+ struct event_tool_context *ctx = talloc_get_type_abort(
+ private_data, struct event_tool_context);
+ char *subdir = NULL;
+ char *data_dir = NULL;
+ char *etc_dir = NULL;
+ char *t = NULL;
+ struct event_script_list *data_list = NULL;
+ struct event_script_list *etc_list = NULL;
+ unsigned int i, j, matched;
+ int ret = 0;
+
+ if (argc != 1) {
+ cmdline_usage(ctx->cmdline, "script list");
+ return 1;
+ }
+
+ subdir = talloc_asprintf(mem_ctx, "events/%s", argv[0]);
+ if (subdir == NULL) {
+ return ENOMEM;
+ }
+
+ data_dir = path_datadir_append(mem_ctx, subdir);
+ if (data_dir == NULL) {
+ return ENOMEM;
+ }
+
+ t = talloc_size(mem_ctx, PATH_MAX);
+ if (t == NULL) {
+ return ENOMEM;
+ }
+
+ data_dir = realpath(data_dir, t);
+ if (data_dir == NULL) {
+ if (errno != ENOENT) {
+ return errno;
+ }
+ D_ERR("Command script list finished with result=%d\n", ENOENT);
+ return ENOENT;
+ }
+
+ etc_dir = path_etcdir_append(mem_ctx, subdir);
+ if (etc_dir == NULL) {
+ return ENOMEM;
+ }
+
+ /*
+ * Ignore error on ENOENT for cut down (e.g. fixed/embedded)
+ * installs that don't use symlinks but just populate etc_dir
+ * directly
+ */
+ ret = event_script_get_list(mem_ctx, data_dir, &data_list);
+ if (ret != 0 && ret != ENOENT) {
+ D_ERR("Command script list finished with result=%d\n", ret);
+ goto done;
+ }
+
+ ret = event_script_get_list(mem_ctx, etc_dir, &etc_list);
+ if (ret != 0) {
+ D_ERR("Command script list finished with result=%d\n", ret);
+ goto done;
+ }
+
+ D_NOTICE("Command script list finished with result=%d\n", ret);
+
+ if (data_list == NULL) {
+ goto list_enabled_only;
+ }
+
+ /*
+ * First list scripts provided by CTDB. Flag those that are
+ * enabled via a symlink and arrange for them to be excluded
+ * from the subsequent list of local scripts.
+ *
+ * Both lists are sorted, so walk the list of enabled scripts
+ * only once in this pass.
+ */
+ j = 0;
+ matched = 0;
+ for (i = 0; i < data_list->num_scripts; i++) {
+ struct event_script *d = data_list->script[i];
+ char flag = EVENT_SCRIPT_DISABLED;
+ char buf[PATH_MAX];
+ ssize_t len;
+
+ /* Check to see if this script is enabled */
+ while (j < etc_list->num_scripts) {
+ struct event_script *e = etc_list->script[j];
+
+ ret = strcmp(e->name, d->name);
+
+ if (ret > 0) {
+ /*
+ * Enabled name is greater, so needs
+ * to be considered later: done
+ */
+ break;
+ }
+
+ if (ret < 0) {
+ /* Enabled name is less: next */
+ j++;
+ continue;
+ }
+
+ len = readlink(e->path, buf, sizeof(buf));
+ if (len == -1 || (size_t)len >= sizeof(buf)) {
+ /*
+ * Not a link? Disappeared? Invalid
+ * link target? Something else?
+ *
+ * Doesn't match provided script: next, done
+ */
+ j++;
+ break;
+ }
+
+ /* readlink() does not NUL-terminate */
+ buf[len] = '\0';
+
+ ret = strcmp(buf, d->path);
+ if (ret != 0) {
+ /* Enabled link doesn't match: next, done */
+ j++;
+ break;
+ }
+
+ /*
+ * Enabled script's symlink matches our
+ * script: flag our script as enabled
+ *
+ * Also clear the enabled script so it can be
+ * trivially skipped in the next pass
+ */
+ flag = EVENT_SCRIPT_ENABLED;
+ TALLOC_FREE(etc_list->script[j]);
+ j++;
+ matched++;
+ break;
+ }
+
+ printf("%c %s\n", flag, d->name);
+ }
+
+ /* Print blank line if both provided and local lists are being printed */
+ if (data_list->num_scripts > 0 && matched != etc_list->num_scripts) {
+ printf("\n");
+ }
+
+list_enabled_only:
+
+ /* Now print details of local scripts, after a blank line */
+ for (j = 0; j < etc_list->num_scripts; j++) {
+ struct event_script *e = etc_list->script[j];
+ char flag = EVENT_SCRIPT_DISABLED;
+
+ if (e == NULL) {
+ /* Matched in previous pass: next */
+ continue;
+ }
+
+ /* Script is local: if executable then flag as enabled */
+ if (e->enabled) {
+ flag = EVENT_SCRIPT_ENABLED;
+ }
+
+ printf("%c %s\n", flag, e->name);
+ }
+
+ ret = 0;
+
+done:
+ talloc_free(subdir);
+ talloc_free(data_dir);
+ talloc_free(etc_dir);
+ talloc_free(data_list);
+ talloc_free(etc_list);
+
+ return ret;
+}
+
+static int event_command_script(TALLOC_CTX *mem_ctx,
+ struct event_tool_context *ctx,
+ const char *component,
+ const char *script,
+ bool enable)
+{
+ char *subdir, *etc_dir;
+ int result = 0;
+
+ subdir = talloc_asprintf(mem_ctx, "events/%s", component);
+ if (subdir == NULL) {
+ return ENOMEM;
+ }
+
+ etc_dir = path_etcdir_append(mem_ctx, subdir);
+ if (etc_dir == NULL) {
+ return ENOMEM;
+ }
+
+ if (enable) {
+ result = event_script_chmod(etc_dir, script, true);
+ } else {
+ result = event_script_chmod(etc_dir, script, false);
+ }
+
+ talloc_free(subdir);
+ talloc_free(etc_dir);
+
+ D_NOTICE("Command script finished with result=%d\n", result);
+
+ if (result == EINVAL) {
+ printf("Script %s is invalid in %s\n", script, component);
+ } else if (result == ENOENT) {
+ printf("Script %s does not exist in %s\n", script, component);
+ }
+
+ return result;
+}
+
+static int event_command_script_enable(TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ void *private_data)
+{
+ struct event_tool_context *ctx = talloc_get_type_abort(
+ private_data, struct event_tool_context);
+ struct stat statbuf;
+ char *script, *etc_script;
+ int ret;
+
+ if (argc != 2) {
+ cmdline_usage(ctx->cmdline, "script enable");
+ return 1;
+ }
+
+ script = talloc_asprintf(mem_ctx, "events/%s/%s.script", argv[0], argv[1]);
+ if (script == NULL) {
+ return ENOMEM;
+ }
+
+ etc_script = path_etcdir_append(mem_ctx, script);
+ if (etc_script == NULL) {
+ return ENOMEM;
+ }
+
+ ret = lstat(etc_script, &statbuf);
+ if (ret == 0) {
+ if (S_ISLNK(statbuf.st_mode)) {
+ /* Link already exists */
+ return 0;
+ } else if (S_ISREG(statbuf.st_mode)) {
+ return event_command_script(mem_ctx,
+ ctx,
+ argv[0],
+ argv[1],
+ true);
+ }
+
+ printf("Script %s is not a file or a link\n", etc_script);
+ return EINVAL;
+ } else {
+ if (errno == ENOENT) {
+ char *t;
+ char *data_script;
+
+ data_script = path_datadir_append(mem_ctx, script);
+ if (data_script == NULL) {
+ return ENOMEM;
+ }
+
+ t = talloc_size(mem_ctx, PATH_MAX);
+ if (t == NULL) {
+ return ENOMEM;
+ }
+
+ data_script = realpath(data_script, t);
+ if (data_script == NULL) {
+ if (errno != ENOENT) {
+ return errno;
+ }
+ printf("Script %s does not exist in %s\n",
+ argv[1],
+ argv[0]);
+ return ENOENT;
+ }
+
+ ret = stat(data_script, &statbuf);
+ if (ret != 0) {
+ printf("Script %s does not exist in %s\n",
+ argv[1], argv[0]);
+ return ENOENT;
+ }
+
+ ret = symlink(data_script, etc_script);
+ if (ret != 0) {
+ printf("Failed to create symlink %s\n",
+ etc_script);
+ return EIO;
+ }
+
+ return 0;
+ }
+
+ printf("Script %s does not exist\n", etc_script);
+ return EINVAL;
+ }
+}
+
+static int event_command_script_disable(TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ void *private_data)
+{
+ struct event_tool_context *ctx = talloc_get_type_abort(
+ private_data, struct event_tool_context);
+ struct stat statbuf;
+ char *script, *etc_script;
+ int ret;
+
+
+ if (argc != 2) {
+ cmdline_usage(ctx->cmdline, "script disable");
+ return 1;
+ }
+
+ script = talloc_asprintf(mem_ctx, "events/%s/%s.script", argv[0], argv[1]);
+ if (script == NULL) {
+ return ENOMEM;
+ }
+
+ etc_script = path_etcdir_append(mem_ctx, script);
+ if (etc_script == NULL) {
+ return ENOMEM;
+ }
+
+ ret = lstat(etc_script, &statbuf);
+ if (ret == 0) {
+ if (S_ISLNK(statbuf.st_mode)) {
+ /* Link exists */
+ ret = unlink(etc_script);
+ if (ret != 0) {
+ printf("Failed to remove symlink %s\n",
+ etc_script);
+ return EIO;
+ }
+
+ return 0;
+ } else if (S_ISREG(statbuf.st_mode)) {
+ return event_command_script(mem_ctx,
+ ctx,
+ argv[0],
+ argv[1],
+ false);
+ }
+
+ printf("Script %s is not a file or a link\n", etc_script);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+struct cmdline_command event_commands[] = {
+ { "run", event_command_run,
+ "Run an event", "<timeout> <component> <event> <args>" },
+ { "status", event_command_status,
+ "Get status of an event", "<component> <event>" },
+ { "script list", event_command_script_list,
+ "List event scripts", "<component>" },
+ { "script enable", event_command_script_enable,
+ "Enable an event script", "<component> <script>" },
+ { "script disable", event_command_script_disable,
+ "Disable an event script", "<component> <script>" },
+ CMDLINE_TABLEEND
+};
+
+int event_tool_init(TALLOC_CTX *mem_ctx,
+ const char *prog,
+ struct poptOption *options,
+ int argc,
+ const char **argv,
+ bool parse_options,
+ struct event_tool_context **result)
+{
+ struct event_tool_context *ctx;
+ int ret;
+
+ ctx = talloc_zero(mem_ctx, struct event_tool_context);
+ if (ctx == NULL) {
+ D_ERR("Memory allocation error\n");
+ return ENOMEM;
+ }
+
+ ret = cmdline_init(mem_ctx,
+ prog,
+ options,
+ NULL,
+ event_commands,
+ &ctx->cmdline);
+ if (ret != 0) {
+ D_ERR("Failed to initialize cmdline, ret=%d\n", ret);
+ talloc_free(ctx);
+ return ret;
+ }
+
+ ret = cmdline_parse(ctx->cmdline, argc, argv, parse_options);
+ if (ret != 0) {
+ cmdline_usage(ctx->cmdline, NULL);
+ talloc_free(ctx);
+ return ret;
+ }
+
+ *result = ctx;
+ return 0;
+}
+
+int event_tool_run(struct event_tool_context *ctx, int *result)
+{
+ int ret;
+
+ ctx->ev = tevent_context_init(ctx);
+ if (ctx->ev == NULL) {
+ D_ERR("Failed to initialize tevent\n");
+ return ENOMEM;
+ }
+
+ ret = cmdline_run(ctx->cmdline, ctx, result);
+ return ret;
+}
+
+#ifdef CTDB_EVENT_TOOL
+
+static struct {
+ const char *debug;
+} event_data = {
+ .debug = "ERROR",
+};
+
+struct poptOption event_options[] = {
+ { "debug", 'd', POPT_ARG_STRING, &event_data.debug, 0,
+ "debug level", "ERROR|WARNING|NOTICE|INFO|DEBUG" },
+ POPT_TABLEEND
+};
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ struct event_tool_context *ctx;
+ int ret, result = 0;
+ int level;
+ bool ok;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ fprintf(stderr, "Memory allocation error\n");
+ exit(1);
+ }
+
+ ret = event_tool_init(mem_ctx,
+ "ctdb-event",
+ event_options,
+ argc,
+ argv,
+ true,
+ &ctx);
+ if (ret != 0) {
+ talloc_free(mem_ctx);
+ exit(1);
+ }
+
+ setup_logging("ctdb-event", DEBUG_STDERR);
+ ok = debug_level_parse(event_data.debug, &level);
+ if (!ok) {
+ level = DEBUG_ERR;
+ }
+ debuglevel_set(level);
+
+ ret = event_tool_run(ctx, &result);
+ if (ret != 0) {
+ exit(1);
+ }
+
+ talloc_free(mem_ctx);
+ exit(result);
+}
+
+#endif /* CTDB_EVENT_TOOL */
diff --git a/ctdb/event/event_tool.h b/ctdb/event/event_tool.h
new file mode 100644
index 0000000..3abe716
--- /dev/null
+++ b/ctdb/event/event_tool.h
@@ -0,0 +1,38 @@
+/*
+ CTDB event daemon utility code
+
+ Copyright (C) Amitay Isaacs 2018
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __CTDB_EVENT_TOOL_H__
+#define __CTDB_EVENT_TOOL_H__
+
+#include <popt.h>
+#include <talloc.h>
+
+struct event_tool_context;
+
+int event_tool_init(TALLOC_CTX *mem_ctx,
+ const char *prog,
+ struct poptOption *options,
+ int argc,
+ const char **argv,
+ bool parse_options,
+ struct event_tool_context **result);
+
+int event_tool_run(struct event_tool_context *ctx, int *result);
+
+#endif /* __CTDB_EVENT_TOOL_H__ */