diff --git a/ctdb/event/event_cmd.c b/ctdb/event/event_cmd.c new file mode 100644 index 00000000000..c1163416687 --- /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 . +*/ + +#include "replace.h" + +#include +#include + +#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 == -ETIME) { + state->reply->result = ETIME; + } 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_config.c b/ctdb/event/event_config.c new file mode 100644 index 00000000000..d2826225c80 --- /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 . +*/ + +#include "replace.h" + +#include + +#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 00000000000..79bcd834576 --- /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 . +*/ + +#include "replace.h" +#include "system/dir.h" + +#include +#include + +#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; iscript[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 00000000000..63c4dad2866 --- /dev/null +++ b/ctdb/event/event_daemon.c @@ -0,0 +1,359 @@ +/* + 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 . +*/ + +#include "replace.h" +#include "system/filesys.h" + +#include +#include +#include + +#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 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; +} options = { + .pid = -1, +}; + +struct poptOption cmdline_options[] = { + POPT_AUTOHELP + { "pid", 'P', POPT_ARG_INT, &options.pid, 0, + "pid to wait for", "PID" }, + 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; + + 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 initalize event config\n"); + goto fail; + } + + e_state->ev = tevent_context_init(e_state->mem_ctx); + if (e_state->ev == NULL) { + D_ERR("Failed to initalize tevent\n"); + ret = 1; + goto fail; + } + + daemon_funcs = (struct sock_daemon_funcs) { + .startup = event_daemon_startup, + .reconfigure = event_daemon_reconfigure, + .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; + } + + ret = sock_daemon_run(e_state->ev, + e_state->sockd, + e_state->pidfile, + (interactive == 1), + false, + options.pid); + if (ret == EINTR) { + ret = 0; + } + + if (getenv("CTDB_TEST_MODE") != 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 00000000000..0cc8d803f5b --- /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 . +*/ + +#ifndef __CTDB_EVENT_PRIVATE_H__ +#define __CTDB_EVENT_PRIVATE_H__ + +#include +#include + +#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_request.c b/ctdb/event/event_request.c new file mode 100644 index 00000000000..303e73524bb --- /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 . +*/ + +#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/wscript b/ctdb/wscript index 90cb0a6b2b3..cdf7a945355 100644 --- a/ctdb/wscript +++ b/ctdb/wscript @@ -504,6 +504,20 @@ def build(bld): '''), deps='ctdb-protocol-basic') + bld.SAMBA_BINARY('ctdb-eventd', + source=bld.SUBDIR('event', + '''event_cmd.c + event_config.c + event_context.c + event_daemon.c + event_request.c + '''), + deps='''ctdb-event-protocol + ctdb-event-conf ctdb-logging-conf + ctdb-server-util samba-util ctdb-util + talloc tevent replace popt''', + install_path='${CTDB_HELPER_BINDIR}') + bld.SAMBA_BINARY('ctdbd', source='server/ctdbd.c ' + bld.SUBDIR('server',