2 Event script running daemon
4 Copyright (C) Amitay Isaacs 2016
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include "system/filesys.h"
22 #include "system/dir.h"
23 #include "system/wait.h"
24 #include "system/locale.h"
30 #include "lib/util/debug.h"
31 #include "lib/util/tevent_unix.h"
32 #include "lib/util/blocking.h"
33 #include "lib/util/sys_rw.h"
34 #include "lib/util/dlinklist.h"
35 #include "lib/async_req/async_sock.h"
37 #include "protocol/protocol_api.h"
38 #include "protocol/protocol_util.h"
40 #include "common/comm.h"
41 #include "common/logging.h"
42 #include "common/run_event.h"
43 #include "common/sock_daemon.h"
45 struct eventd_client {
46 struct eventd_client *prev, *next;
48 struct sock_client_context *client_ctx;
49 struct pending_event *pending_list;
52 struct eventd_context {
53 struct run_proc_context *run_proc_ctx;
54 struct run_event_context *run_ctx;
56 /* result of last execution */
57 struct run_event_script_list *status_run[CTDB_EVENT_MAX];
58 struct run_event_script_list *status_pass[CTDB_EVENT_MAX];
59 struct run_event_script_list *status_fail[CTDB_EVENT_MAX];
61 struct eventd_client *client_list;
65 * Global state manipulation functions
68 static int eventd_context_init(TALLOC_CTX *mem_ctx,
69 struct tevent_context *ev,
70 const char *script_dir,
71 const char *debug_script,
72 struct eventd_context **result)
74 struct eventd_context *ectx;
77 ectx = talloc_zero(mem_ctx, struct eventd_context);
82 ret = run_proc_init(ectx, ev, &ectx->run_proc_ctx);
88 ret = run_event_init(ectx,
102 static struct run_event_context *eventd_run_context(struct eventd_context *ectx)
104 return ectx->run_ctx;
107 static struct run_event_script_list *script_list_copy(
109 struct run_event_script_list *s)
111 struct run_event_script_list *s2;
113 s2 = talloc_zero(mem_ctx, struct run_event_script_list);
118 s2->num_scripts = s->num_scripts;
119 s2->script = talloc_memdup(s2, s->script,
121 sizeof(struct run_event_script));
122 if (s2->script == NULL) {
126 s2->summary = s->summary;
131 static struct ctdb_script_list *script_list_to_ctdb_script_list(
133 struct run_event_script_list *s)
135 struct ctdb_script_list *sl;
138 sl = talloc_zero(mem_ctx, struct ctdb_script_list);
143 sl->script = talloc_zero_array(sl, struct ctdb_script, s->num_scripts);
144 if (sl->script == NULL) {
149 sl->num_scripts = s->num_scripts;
151 for (i=0; i<s->num_scripts; i++) {
152 struct run_event_script *escript = &s->script[i];
153 struct ctdb_script *script = &sl->script[i];
155 strlcpy(script->name, escript->name, MAX_SCRIPT_NAME+1);
156 script->start = escript->begin;
157 script->finished = escript->end;
158 script->status = escript->summary;
159 if (escript->output != NULL) {
160 strlcpy(script->output, escript->output,
161 MAX_SCRIPT_OUTPUT+1);
168 static void eventd_set_result(struct eventd_context *ectx,
169 enum ctdb_event event,
170 struct run_event_script_list *script_list)
172 struct run_event_script_list *s;
174 if (script_list == NULL) {
178 /* Do not update status if event was cancelled */
179 if (script_list->summary == -ECANCELED) {
183 TALLOC_FREE(ectx->status_run[event]);
184 ectx->status_run[event] = talloc_steal(ectx, script_list);
186 s = script_list_copy(ectx, script_list);
191 if (s->summary == 0) {
192 TALLOC_FREE(ectx->status_pass[event]);
193 ectx->status_pass[event] = s;
195 TALLOC_FREE(ectx->status_fail[event]);
196 ectx->status_fail[event] = s;
200 static int eventd_get_result(struct eventd_context *ectx,
201 enum ctdb_event event,
202 enum ctdb_event_status_state state,
204 struct ctdb_script_list **out)
206 struct run_event_script_list *s = NULL;
209 case CTDB_EVENT_LAST_RUN:
210 s = ectx->status_run[event];
213 case CTDB_EVENT_LAST_PASS:
214 s = ectx->status_pass[event];
217 case CTDB_EVENT_LAST_FAIL:
218 s = ectx->status_fail[event];
227 *out = script_list_to_ctdb_script_list(mem_ctx, s);
232 * Process RUN command
235 struct command_run_state {
236 struct eventd_context *ectx;
238 enum ctdb_event event;
239 struct ctdb_event_reply *reply;
242 static void command_run_done(struct tevent_req *subreq);
244 static struct tevent_req *command_run_send(TALLOC_CTX *mem_ctx,
245 struct tevent_context *ev,
246 struct eventd_context *ectx,
247 struct eventd_client *client,
248 struct ctdb_event_request *request)
250 struct tevent_req *req, *subreq;
251 struct command_run_state *state;
254 req = tevent_req_create(mem_ctx, &state, struct command_run_state);
260 state->event = request->rdata.data.run->event;
262 state->reply = talloc_zero(state, struct ctdb_event_reply);
263 if (tevent_req_nomem(state->reply, req)) {
264 return tevent_req_post(req, ev);
267 state->reply->rdata.command = request->rdata.command;
269 timeout = request->rdata.data.run->timeout;
270 subreq = run_event_send(state, ev,
271 eventd_run_context(state->ectx),
272 ctdb_event_to_string(state->event),
273 request->rdata.data.run->arg_str,
274 tevent_timeval_current_ofs(timeout, 0),
276 if (tevent_req_nomem(subreq, req)) {
277 return tevent_req_post(req, ev);
279 tevent_req_set_callback(subreq, command_run_done, req);
284 static void command_run_done(struct tevent_req *subreq)
286 struct tevent_req *req = tevent_req_callback_data(
287 subreq, struct tevent_req);
288 struct command_run_state *state = tevent_req_data(
289 req, struct command_run_state);
290 struct run_event_script_list *script_list;
294 status = run_event_recv(subreq, &ret, state, &script_list);
297 tevent_req_error(req, ret);
301 if (script_list == NULL) {
302 eventd_set_result(state->ectx, state->event, NULL);
303 state->reply->rdata.result = 0;
305 eventd_set_result(state->ectx, state->event, script_list);
306 state->reply->rdata.result = script_list->summary;
309 tevent_req_done(req);
312 static bool command_run_recv(struct tevent_req *req, int *perr,
314 struct ctdb_event_reply **reply)
316 struct command_run_state *state = tevent_req_data(
317 req, struct command_run_state);
320 if (tevent_req_is_unix_error(req, &ret)) {
328 *reply = talloc_steal(mem_ctx, state->reply);
334 * Process STATUS command
337 struct command_status_state {
338 struct ctdb_event_reply *reply;
341 static struct tevent_req *command_status_send(
343 struct tevent_context *ev,
344 struct eventd_context *ectx,
345 struct eventd_client *client,
346 struct ctdb_event_request *request)
348 struct tevent_req *req;
349 struct command_status_state *state;
350 enum ctdb_event event;
351 enum ctdb_event_status_state estate;
353 req = tevent_req_create(mem_ctx, &state, struct command_status_state);
358 event = request->rdata.data.status->event;
359 estate = request->rdata.data.status->state;
361 state->reply = talloc_zero(state, struct ctdb_event_reply);
362 if (tevent_req_nomem(state->reply, req)) {
363 return tevent_req_post(req, ev);
366 state->reply->rdata.data.status =
367 talloc(state->reply, struct ctdb_event_reply_status);
368 if (tevent_req_nomem(state->reply->rdata.data.status, req)) {
369 return tevent_req_post(req, ev);
372 state->reply->rdata.command = request->rdata.command;
373 state->reply->rdata.result = 0;
374 state->reply->rdata.data.status->status =
375 eventd_get_result(ectx, event, estate, state->reply,
376 &state->reply->rdata.data.status->script_list);
378 tevent_req_done(req);
379 return tevent_req_post(req, ev);
382 static bool command_status_recv(struct tevent_req *req, int *perr,
384 struct ctdb_event_reply **reply)
386 struct command_status_state *state = tevent_req_data(
387 req, struct command_status_state);
390 if (tevent_req_is_unix_error(req, &ret)) {
398 *reply = talloc_steal(mem_ctx, state->reply);
404 * Process SCRIPT_LIST command
407 struct command_script_list_state {
408 struct ctdb_event_reply *reply;
411 static struct tevent_req *command_script_list_send(
413 struct tevent_context *ev,
414 struct eventd_context *ectx,
415 struct eventd_client *client,
416 struct ctdb_event_request *request)
418 struct tevent_req *req;
419 struct command_script_list_state *state;
420 struct run_event_script_list *s;
423 req = tevent_req_create(mem_ctx, &state,
424 struct command_script_list_state);
429 state->reply = talloc_zero(state, struct ctdb_event_reply);
430 if (tevent_req_nomem(state->reply, req)) {
431 return tevent_req_post(req, ev);
434 state->reply->rdata.data.script_list =
435 talloc(state->reply, struct ctdb_event_reply_script_list);
436 if (tevent_req_nomem(state->reply->rdata.data.script_list, req)) {
437 return tevent_req_post(req, ev);
440 ret = run_event_list(eventd_run_context(ectx), state->reply, &s);
442 tevent_req_error(req, ret);
443 return tevent_req_post(req, ev);
446 state->reply->rdata.command = request->rdata.command;
448 state->reply->rdata.result = 0;
449 state->reply->rdata.data.script_list->script_list = NULL;
451 state->reply->rdata.result = s->summary;
452 state->reply->rdata.data.script_list->script_list =
453 script_list_to_ctdb_script_list(state->reply, s);
456 tevent_req_done(req);
457 return tevent_req_post(req, ev);
460 static bool command_script_list_recv(struct tevent_req *req, int *perr,
462 struct ctdb_event_reply **reply)
464 struct command_script_list_state *state = tevent_req_data(
465 req, struct command_script_list_state);
468 if (tevent_req_is_unix_error(req, &ret)) {
476 *reply = talloc_steal(mem_ctx, state->reply);
482 * Process SCRIPT_ENABLE command
485 struct command_script_enable_state {
486 struct ctdb_event_reply *reply;
489 static struct tevent_req *command_script_enable_send(
491 struct tevent_context *ev,
492 struct eventd_context *ectx,
493 struct eventd_client *client,
494 struct ctdb_event_request *request)
496 struct tevent_req *req;
497 struct command_script_enable_state *state;
498 const char *script_name;
501 req = tevent_req_create(mem_ctx, &state,
502 struct command_script_enable_state);
507 script_name = request->rdata.data.script_enable->script_name;
509 state->reply = talloc_zero(state, struct ctdb_event_reply);
510 if (tevent_req_nomem(state->reply, req)) {
511 return tevent_req_post(req, ev);
514 state->reply->rdata.command = request->rdata.command;
516 ret = run_event_script_enable(eventd_run_context(ectx), script_name);
517 state->reply->rdata.result = -ret;
519 tevent_req_done(req);
520 return tevent_req_post(req, ev);
523 static bool command_script_enable_recv(struct tevent_req *req, int *perr,
525 struct ctdb_event_reply **reply)
527 struct command_script_enable_state *state = tevent_req_data(
528 req, struct command_script_enable_state);
531 if (tevent_req_is_unix_error(req, &ret)) {
539 *reply = talloc_steal(mem_ctx, state->reply);
545 * Process SCRIPT_DISABLE command
548 struct command_script_disable_state {
549 struct ctdb_event_reply *reply;
552 static struct tevent_req *command_script_disable_send(
554 struct tevent_context *ev,
555 struct eventd_context *ectx,
556 struct eventd_client *client,
557 struct ctdb_event_request *request)
559 struct tevent_req *req;
560 struct command_script_disable_state *state;
561 const char *script_name;
564 req = tevent_req_create(mem_ctx, &state,
565 struct command_script_disable_state);
570 script_name = request->rdata.data.script_disable->script_name;
572 state->reply = talloc_zero(state, struct ctdb_event_reply);
573 if (tevent_req_nomem(state->reply, req)) {
574 return tevent_req_post(req, ev);
577 state->reply->rdata.command = request->rdata.command;
579 ret = run_event_script_disable(eventd_run_context(ectx), script_name);
580 state->reply->rdata.result = -ret;
582 tevent_req_done(req);
583 return tevent_req_post(req, ev);
586 static bool command_script_disable_recv(struct tevent_req *req, int *perr,
588 struct ctdb_event_reply **reply)
590 struct command_script_disable_state *state = tevent_req_data(
591 req, struct command_script_disable_state);
594 if (tevent_req_is_unix_error(req, &ret)) {
602 *reply = talloc_steal(mem_ctx, state->reply);
611 static struct eventd_client *client_find(struct eventd_context *ectx,
612 struct sock_client_context *client_ctx)
614 struct eventd_client *client;
616 for (client = ectx->client_list;
618 client = client->next) {
619 if (client->client_ctx == client_ctx) {
627 static bool client_connect(struct sock_client_context *client_ctx,
630 struct eventd_context *ectx = talloc_get_type_abort(
631 private_data, struct eventd_context);
632 struct eventd_client *client;
634 client = talloc_zero(ectx, struct eventd_client);
635 if (client == NULL) {
639 client->client_ctx = client_ctx;
641 DLIST_ADD(ectx->client_list, client);
645 static void client_disconnect(struct sock_client_context *client_ctx,
648 struct eventd_context *ectx = talloc_get_type_abort(
649 private_data, struct eventd_context);
650 struct eventd_client *client;
652 client = client_find(ectx, client_ctx);
653 if (client == NULL) {
657 DLIST_REMOVE(ectx->client_list, client);
661 struct client_process_state {
662 struct tevent_context *ev;
664 struct eventd_client *client;
665 struct ctdb_event_request request;
668 static void client_run_done(struct tevent_req *subreq);
669 static void client_status_done(struct tevent_req *subreq);
670 static void client_script_list_done(struct tevent_req *subreq);
671 static void client_script_enable_done(struct tevent_req *subreq);
672 static void client_script_disable_done(struct tevent_req *subreq);
673 static void client_process_reply(struct tevent_req *req,
674 struct ctdb_event_reply *reply);
675 static void client_process_reply_done(struct tevent_req *subreq);
677 static struct tevent_req *client_process_send(
679 struct tevent_context *ev,
680 struct sock_client_context *client_ctx,
681 uint8_t *buf, size_t buflen,
684 struct eventd_context *ectx = talloc_get_type_abort(
685 private_data, struct eventd_context);
686 struct tevent_req *req, *subreq;
687 struct client_process_state *state;
690 req = tevent_req_create(mem_ctx, &state, struct client_process_state);
697 state->client = client_find(ectx, client_ctx);
698 if (state->client == NULL) {
699 tevent_req_error(req, EIO);
700 return tevent_req_post(req, ev);
703 ret = ctdb_event_request_pull(buf, buflen, state, &state->request);
705 tevent_req_error(req, EPROTO);
706 return tevent_req_post(req, ev);
709 switch (state->request.rdata.command) {
710 case CTDB_EVENT_COMMAND_RUN:
711 subreq = command_run_send(state, ev, ectx, state->client,
713 if (tevent_req_nomem(subreq, req)) {
714 return tevent_req_post(req, ev);
716 tevent_req_set_callback(subreq, client_run_done, req);
719 case CTDB_EVENT_COMMAND_STATUS:
720 subreq = command_status_send(state, ev, ectx, state->client,
722 if (tevent_req_nomem(subreq, req)) {
723 return tevent_req_post(req, ev);
725 tevent_req_set_callback(subreq, client_status_done, req);
728 case CTDB_EVENT_COMMAND_SCRIPT_LIST:
729 subreq = command_script_list_send(state, ev, ectx,
732 if (tevent_req_nomem(subreq, req)) {
733 return tevent_req_post(req, ev);
735 tevent_req_set_callback(subreq, client_script_list_done, req);
738 case CTDB_EVENT_COMMAND_SCRIPT_ENABLE:
739 subreq = command_script_enable_send(state, ev, ectx,
742 if (tevent_req_nomem(subreq, req)) {
743 return tevent_req_post(req, ev);
745 tevent_req_set_callback(subreq, client_script_enable_done,
749 case CTDB_EVENT_COMMAND_SCRIPT_DISABLE:
750 subreq = command_script_disable_send(state, ev, ectx,
753 if (tevent_req_nomem(subreq, req)) {
754 return tevent_req_post(req, ev);
756 tevent_req_set_callback(subreq, client_script_disable_done,
764 static void client_run_done(struct tevent_req *subreq)
766 struct tevent_req *req = tevent_req_callback_data(
767 subreq, struct tevent_req);
768 struct client_process_state *state = tevent_req_data(
769 req, struct client_process_state);
770 struct ctdb_event_reply *reply = NULL;
774 status = command_run_recv(subreq, &ret, state, &reply);
777 D_ERR("COMMAND_RUN failed\n");
778 tevent_req_error(req, ret);
782 client_process_reply(req, reply);
786 static void client_status_done(struct tevent_req *subreq)
788 struct tevent_req *req = tevent_req_callback_data(
789 subreq, struct tevent_req);
790 struct client_process_state *state = tevent_req_data(
791 req, struct client_process_state);
792 struct ctdb_event_reply *reply = NULL;
796 status = command_status_recv(subreq, &ret, state, &reply);
799 D_ERR("COMMAND_STATUS failed\n");
800 tevent_req_error(req, ret);
804 client_process_reply(req, reply);
808 static void client_script_list_done(struct tevent_req *subreq)
810 struct tevent_req *req = tevent_req_callback_data(
811 subreq, struct tevent_req);
812 struct client_process_state *state = tevent_req_data(
813 req, struct client_process_state);
814 struct ctdb_event_reply *reply = NULL;
818 status = command_script_list_recv(subreq, &ret, state, &reply);
821 D_ERR("COMMAND_SCRIPT_LIST failed\n");
822 tevent_req_error(req, ret);
826 client_process_reply(req, reply);
830 static void client_script_enable_done(struct tevent_req *subreq)
832 struct tevent_req *req = tevent_req_callback_data(
833 subreq, struct tevent_req);
834 struct client_process_state *state = tevent_req_data(
835 req, struct client_process_state);
836 struct ctdb_event_reply *reply = NULL;
840 status = command_script_enable_recv(subreq, &ret, state, &reply);
843 D_ERR("COMMAND_SCRIPT_ENABLE failed\n");
844 tevent_req_error(req, ret);
848 client_process_reply(req, reply);
852 static void client_script_disable_done(struct tevent_req *subreq)
854 struct tevent_req *req = tevent_req_callback_data(
855 subreq, struct tevent_req);
856 struct client_process_state *state = tevent_req_data(
857 req, struct client_process_state);
858 struct ctdb_event_reply *reply = NULL;
862 status = command_script_disable_recv(subreq, &ret, state, &reply);
865 D_ERR("COMMAND_SCRIPT_DISABLE failed\n");
866 tevent_req_error(req, ret);
870 client_process_reply(req, reply);
874 static void client_process_reply(struct tevent_req *req,
875 struct ctdb_event_reply *reply)
877 struct client_process_state *state = tevent_req_data(
878 req, struct client_process_state);
879 struct tevent_req *subreq;
884 sock_packet_header_set_reqid(&reply->header,
885 state->request.header.reqid);
887 buflen = ctdb_event_reply_len(reply);
888 buf = talloc_zero_size(state, buflen);
889 if (tevent_req_nomem(buf, req)) {
893 ret = ctdb_event_reply_push(reply, buf, &buflen);
896 tevent_req_error(req, ret);
900 subreq = sock_socket_write_send(state, state->ev,
901 state->client->client_ctx,
903 if (tevent_req_nomem(subreq, req)) {
906 tevent_req_set_callback(subreq, client_process_reply_done, req);
909 static void client_process_reply_done(struct tevent_req *subreq)
911 struct tevent_req *req = tevent_req_callback_data(
912 subreq, struct tevent_req);
916 status = sock_socket_write_recv(subreq, &ret);
919 D_ERR("Sending reply failed\n");
920 tevent_req_error(req, ret);
924 tevent_req_done(req);
927 static bool client_process_recv(struct tevent_req *req, int *perr)
931 if (tevent_req_is_unix_error(req, &ret)) {
945 static void eventd_shutdown(void *private_data)
947 struct eventd_context *ectx = talloc_get_type_abort(
948 private_data, struct eventd_context);
949 struct eventd_client *client;
951 while ((client = ectx->client_list) != NULL) {
952 DLIST_REMOVE(ectx->client_list, client);
958 const char *debug_script;
959 const char *script_dir;
961 const char *debug_level;
966 .debug_level = "ERR",
969 struct poptOption cmdline_options[] = {
971 { "debug_script", 'D', POPT_ARG_STRING, &options.debug_script, 0,
972 "debug script", "FILE" },
973 { "pid", 'P', POPT_ARG_INT, &options.pid, 0,
974 "pid to wait for", "PID" },
975 { "event_script_dir", 'e', POPT_ARG_STRING, &options.script_dir, 0,
976 "event script dir", "DIRECTORY" },
977 { "logging", 'l', POPT_ARG_STRING, &options.logging, 0,
978 "logging specification" },
979 { "debug", 'd', POPT_ARG_STRING, &options.debug_level, 0,
981 { "pidfile", 'p', POPT_ARG_STRING, &options.pidfile, 0,
982 "eventd pid file", "FILE" },
983 { "socket", 's', POPT_ARG_STRING, &options.socket, 0,
984 "eventd socket path", "FILE" },
988 int main(int argc, const char **argv)
992 struct tevent_context *ev;
993 struct eventd_context *ectx;
994 struct sock_daemon_context *sockd;
995 struct sock_daemon_funcs daemon_funcs;
996 struct sock_socket_funcs socket_funcs;
1000 /* Set default options */
1003 pc = poptGetContext(argv[0], argc, argv, cmdline_options,
1004 POPT_CONTEXT_KEEP_FIRST);
1005 while ((opt = poptGetNextOpt(pc)) != -1) {
1006 fprintf(stderr, "Invalid options %s: %s\n",
1007 poptBadOption(pc, 0), poptStrerror(opt));
1011 if (options.socket == NULL) {
1012 fprintf(stderr, "Please specify eventd socket (--socket)\n");
1016 if (options.script_dir == NULL) {
1018 "Please specify script dir (--event_script_dir)\n");
1022 if (options.logging == NULL) {
1024 "Please specify logging (--logging)\n");
1028 ret = stat(options.script_dir, &statbuf);
1031 fprintf(stderr, "Error reading script_dir %s, ret=%d\n",
1032 options.script_dir, ret);
1035 if (! S_ISDIR(statbuf.st_mode)) {
1036 fprintf(stderr, "script_dir %s is not a directory\n",
1037 options.script_dir);
1041 mem_ctx = talloc_new(NULL);
1042 if (mem_ctx == NULL) {
1046 ev = tevent_context_init(mem_ctx);
1052 ret = eventd_context_init(mem_ctx, ev, options.script_dir,
1053 options.debug_script, &ectx);
1058 daemon_funcs = (struct sock_daemon_funcs) {
1059 .shutdown = eventd_shutdown,
1062 ret = sock_daemon_setup(mem_ctx, "ctdb-eventd", options.logging,
1063 options.debug_level,
1064 &daemon_funcs, ectx, &sockd);
1069 socket_funcs = (struct sock_socket_funcs) {
1070 .connect = client_connect,
1071 .disconnect = client_disconnect,
1072 .read_send = client_process_send,
1073 .read_recv = client_process_recv,
1076 ret = sock_daemon_add_unix(sockd, options.socket, &socket_funcs, ectx);
1081 ret = sock_daemon_run(ev, sockd,
1082 options.pidfile, false, false, options.pid);
1088 talloc_free(mem_ctx);
1089 (void)poptFreeContext(pc);