1 /*
2    CTDB event daemon utility code
3 
4    Copyright (C) Amitay Isaacs  2018
5 
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.
10 
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.
15 
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/>.
18 */
19 
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/time.h"
23 
24 #include <popt.h>
25 #include <talloc.h>
26 #include <tevent.h>
27 
28 #include "lib/util/debug.h"
29 
30 #include "common/cmdline.h"
31 #include "common/logging.h"
32 #include "common/path.h"
33 #include "common/event_script.h"
34 
35 #include "event/event_protocol_api.h"
36 #include "event/event.h"
37 #include "event/event_tool.h"
38 
39 struct event_tool_context {
40 	struct cmdline_context *cmdline;
41 	struct tevent_context *ev;
42 	struct ctdb_event_context *eclient;
43 };
44 
compact_args(TALLOC_CTX * mem_ctx,const char ** argv,int argc,int from,const char ** result)45 static int compact_args(TALLOC_CTX *mem_ctx,
46 			const char **argv,
47 			int argc,
48 			int from,
49 			const char **result)
50 {
51 	char *arg_str;
52 	int i;
53 
54 	if (argc <= from) {
55 		*result = NULL;
56 		return 0;
57 	}
58 
59 	arg_str = talloc_strdup(mem_ctx, argv[from]);
60 	if (arg_str == NULL) {
61 		return ENOMEM;
62 	}
63 
64 	for (i = from+1; i < argc; i++) {
65 		arg_str = talloc_asprintf_append(arg_str, " %s", argv[i]);
66 		if (arg_str == NULL) {
67 			return ENOMEM;
68 		}
69 	}
70 
71 	*result = arg_str;
72 	return 0;
73 }
74 
event_command_run(TALLOC_CTX * mem_ctx,int argc,const char ** argv,void * private_data)75 static int event_command_run(TALLOC_CTX *mem_ctx,
76 			     int argc,
77 			     const char **argv,
78 			     void *private_data)
79 {
80 	struct event_tool_context *ctx = talloc_get_type_abort(
81 		private_data, struct event_tool_context);
82 	struct tevent_req *req;
83 	struct ctdb_event_request_run request_run;
84 	const char *arg_str = NULL;
85 	const char *t;
86 	int timeout, ret = 0, result = 0;
87 	bool ok;
88 
89 	if (argc < 3) {
90 		cmdline_usage(ctx->cmdline, "run");
91 		return 1;
92 	}
93 
94 	ret = ctdb_event_init(ctx, ctx->ev, &ctx->eclient);
95 	if (ret != 0) {
96 		D_ERR("Failed to initialize event client, ret=%d\n", ret);
97 		return ret;
98 	}
99 
100 	timeout = atoi(argv[0]);
101 	if (timeout < 0) {
102 		timeout = 0;
103 	}
104 
105 	ret = compact_args(mem_ctx, argv, argc, 3, &arg_str);
106 	if (ret != 0) {
107 		D_ERR("Memory allocation error\n");
108 		return 1;
109 	}
110 
111 	request_run.component = argv[1];
112 	request_run.event = argv[2];
113 	request_run.args = arg_str;
114 	request_run.timeout = timeout;
115 	request_run.flags = 0;
116 
117 	t = getenv("CTDB_TEST_MODE");
118 	if (t != NULL) {
119 		t = getenv("CTDB_EVENT_RUN_ALL");
120 		if (t != NULL) {
121 			request_run.flags = CTDB_EVENT_RUN_ALL;
122 		}
123 	}
124 
125 	req = ctdb_event_run_send(mem_ctx,
126 				  ctx->ev,
127 				  ctx->eclient,
128 				  &request_run);
129 	if (req == NULL) {
130 		D_ERR("Memory allocation error\n");
131 		return 1;
132 	}
133 
134 	tevent_req_poll(req, ctx->ev);
135 
136 	ok = ctdb_event_run_recv(req, &ret, &result);
137 	if (!ok) {
138 		D_ERR("Failed to run event %s in %s, ret=%d\n",
139 		      argv[2],
140 		      argv[1],
141 		      ret);
142 		return 1;
143 	}
144 
145 	D_NOTICE("Command run finished with result=%d\n", result);
146 
147 	if (result == ENOENT) {
148 		printf("Event dir for %s does not exist\n", argv[1]);
149 	} else if (result == ETIMEDOUT) {
150 		printf("Event %s in %s timed out\n", argv[2], argv[1]);
151 	} else if (result == ECANCELED) {
152 		printf("Event %s in %s got cancelled\n", argv[2], argv[1]);
153 	} else if (result == ENOEXEC) {
154 		printf("Event %s in %s failed\n", argv[2], argv[1]);
155 	} else if (result != 0) {
156 		printf("Failed to run event %s in %s, result=%d\n",
157 		       argv[2],
158 		       argv[1],
159 		       result);
160 	}
161 
162 	ret = (result < 0) ? -result : result;
163 	return ret;
164 }
165 
timeval_delta(struct timeval * tv2,struct timeval * tv)166 static double timeval_delta(struct timeval *tv2, struct timeval *tv)
167 {
168         return (tv2->tv_sec - tv->tv_sec) +
169                (tv2->tv_usec - tv->tv_usec) * 1.0e-6;
170 }
171 
print_status_one(struct ctdb_event_script * script)172 static void print_status_one(struct ctdb_event_script *script)
173 {
174 	if (script->result == -ETIMEDOUT) {
175 		printf("%-20s %-10s %s",
176 		       script->name,
177 		       "TIMEDOUT",
178 		       ctime(&script->begin.tv_sec));
179 	} else if (script->result == -ENOEXEC) {
180 		printf("%-20s %-10s\n", script->name, "DISABLED");
181 	} else if (script->result < 0) {
182 		printf("%-20s %-10s (%s)\n",
183 		       script->name,
184 		       "CANNOT RUN",
185 		       strerror(-script->result));
186 	} else if (script->result == 0) {
187 		printf("%-20s %-10s %.3lf %s",
188 		       script->name,
189 		       "OK",
190 		       timeval_delta(&script->end, &script->begin),
191 		       ctime(&script->begin.tv_sec));
192 	} else {
193 		printf("%-20s %-10s %.3lf %s",
194 		       script->name,
195 		       "ERROR",
196 		       timeval_delta(&script->end, &script->begin),
197 		       ctime(&script->begin.tv_sec));
198 	}
199 
200 	if (script->result != 0 && script->result != -ENOEXEC) {
201 		printf("  OUTPUT: %s\n",
202 		       script->output == NULL ? "" : script->output);
203 	}
204 }
205 
print_status(const char * component,const char * event,int result,struct ctdb_event_reply_status * status)206 static void print_status(const char *component,
207 			 const char *event,
208 			 int result,
209 			 struct ctdb_event_reply_status *status)
210 {
211 	int i;
212 
213 	if (result != 0) {
214 		if (result == ENOENT) {
215 			printf("Event dir for %s does not exist\n", component);
216 		} else if (result == EINVAL) {
217 			printf("Event %s has never run in %s\n",
218 			       event,
219 			       component);
220 		} else {
221 			printf("Unknown error (%d) for event %s in %s\n",
222 			       result,
223 			       event,
224 			       component);
225 		}
226 		return;
227 	}
228 
229 	for (i=0; i<status->script_list->num_scripts; i++) {
230 		print_status_one(&status->script_list->script[i]);
231 	}
232 }
233 
event_command_status(TALLOC_CTX * mem_ctx,int argc,const char ** argv,void * private_data)234 static int event_command_status(TALLOC_CTX *mem_ctx,
235 				int argc,
236 				const char **argv,
237 				void *private_data)
238 {
239 	struct event_tool_context *ctx = talloc_get_type_abort(
240 		private_data, struct event_tool_context);
241 	struct tevent_req *req;
242 	struct ctdb_event_request_status request_status;
243 	struct ctdb_event_reply_status *reply_status;
244 	int ret = 0, result = 0;
245 	bool ok;
246 
247 	if (argc != 2) {
248 		cmdline_usage(ctx->cmdline, "status");
249 		return 1;
250 	}
251 
252 	ret = ctdb_event_init(ctx, ctx->ev, &ctx->eclient);
253 	if (ret != 0) {
254 		D_ERR("Failed to initialize event client, ret=%d\n", ret);
255 		return ret;
256 	}
257 
258 	request_status.component = argv[0];
259 	request_status.event = argv[1];
260 
261 	req = ctdb_event_status_send(mem_ctx,
262 				     ctx->ev,
263 				     ctx->eclient,
264 				     &request_status);
265 	if (req == NULL) {
266 		D_ERR("Memory allocation error\n");
267 		return 1;
268 	}
269 
270 	tevent_req_poll(req, ctx->ev);
271 
272 	ok = ctdb_event_status_recv(req,
273 				    &ret,
274 				    &result,
275 				    mem_ctx,
276 				    &reply_status);
277 	if (!ok) {
278 		D_ERR("Failed to get status for event %s in %s, ret=%d\n",
279 		      argv[1],
280 		      argv[0],
281 		      ret);
282 		return 1;
283 	}
284 
285 	D_NOTICE("Command status finished with result=%d\n", result);
286 
287 	print_status(argv[0], argv[1], result, reply_status);
288 
289 	if (reply_status == NULL) {
290 		ret = result;
291 	} else {
292 		ret = reply_status->summary;
293 		ret = (ret < 0) ? -ret : ret;
294 	}
295 	return ret;
296 }
297 
298 #define EVENT_SCRIPT_DISABLED      ' '
299 #define EVENT_SCRIPT_ENABLED       '*'
300 
event_command_script_list(TALLOC_CTX * mem_ctx,int argc,const char ** argv,void * private_data)301 static int event_command_script_list(TALLOC_CTX *mem_ctx,
302 				     int argc,
303 				     const char **argv,
304 				     void *private_data)
305 {
306 	struct event_tool_context *ctx = talloc_get_type_abort(
307 		private_data, struct event_tool_context);
308 	char *subdir = NULL;
309 	char *data_dir = NULL;
310 	char *etc_dir = NULL;
311 	char *t = NULL;
312 	struct event_script_list *data_list = NULL;
313 	struct event_script_list *etc_list = NULL;
314 	unsigned int i, j, matched;
315 	int ret = 0;
316 
317 	if (argc != 1) {
318 		cmdline_usage(ctx->cmdline, "script list");
319 		return 1;
320 	}
321 
322 	subdir = talloc_asprintf(mem_ctx, "events/%s", argv[0]);
323 	if (subdir == NULL) {
324 		return ENOMEM;
325 	}
326 
327 	data_dir = path_datadir_append(mem_ctx, subdir);
328 	if (data_dir == NULL) {
329 		return ENOMEM;
330 	}
331 
332 	t = talloc_size(mem_ctx, PATH_MAX);
333 	if (t == NULL) {
334 		return ENOMEM;
335 	}
336 
337 	data_dir = realpath(data_dir, t);
338 	if (data_dir == NULL) {
339 		if (errno != ENOENT) {
340 			return errno;
341 		}
342 		D_ERR("Command script list finished with result=%d\n", ENOENT);
343 		return ENOENT;
344 	}
345 
346 	etc_dir = path_etcdir_append(mem_ctx, subdir);
347 	if (etc_dir == NULL) {
348 		return ENOMEM;
349 	}
350 
351 	/*
352 	 * Ignore error on ENOENT for cut down (e.g. fixed/embedded)
353 	 * installs that don't use symlinks but just populate etc_dir
354 	 * directly
355 	 */
356 	ret = event_script_get_list(mem_ctx, data_dir, &data_list);
357 	if (ret != 0 && ret != ENOENT) {
358 		D_ERR("Command script list finished with result=%d\n", ret);
359 		goto done;
360 	}
361 
362 	ret = event_script_get_list(mem_ctx, etc_dir, &etc_list);
363 	if (ret != 0) {
364 		D_ERR("Command script list finished with result=%d\n", ret);
365 		goto done;
366 	}
367 
368 	D_NOTICE("Command script list finished with result=%d\n", ret);
369 
370 	if (data_list == NULL) {
371 		goto list_enabled_only;
372 	}
373 
374 	/*
375 	 * First list scripts provided by CTDB.  Flag those that are
376 	 * enabled via a symlink and arrange for them to be excluded
377 	 * from the subsequent list of local scripts.
378 	 *
379 	 * Both lists are sorted, so walk the list of enabled scripts
380 	 * only once in this pass.
381 	 */
382 	j = 0;
383 	matched = 0;
384 	for (i = 0; i < data_list->num_scripts; i++) {
385 		struct event_script *d = data_list->script[i];
386 		char flag = EVENT_SCRIPT_DISABLED;
387 		char buf[PATH_MAX];
388 		ssize_t len;
389 
390 		/* Check to see if this script is enabled */
391 		while (j < etc_list->num_scripts) {
392 			struct event_script *e = etc_list->script[j];
393 
394 			ret = strcmp(e->name, d->name);
395 
396 			if (ret > 0) {
397 				/*
398 				 * Enabled name is greater, so needs
399 				 * to be considered later: done
400 				 */
401 				break;
402 			}
403 
404 			if (ret < 0) {
405 				/* Enabled name is less: next */
406 				j++;
407 				continue;
408 			}
409 
410 			len = readlink(e->path, buf, sizeof(buf));
411 			if (len == -1 || (size_t)len >= sizeof(buf)) {
412 				/*
413 				 * Not a link?  Disappeared?  Invalid
414 				 * link target?  Something else?
415 				 *
416 				 * Doesn't match provided script: next, done
417 				 */
418 				j++;
419 				break;
420 			}
421 
422 			/* readlink() does not NUL-terminate */
423 			buf[len] = '\0';
424 
425 			ret = strcmp(buf, d->path);
426 			if (ret != 0) {
427 				/* Enabled link doesn't match: next, done */
428 				j++;
429 				break;
430 			}
431 
432 			/*
433 			 * Enabled script's symlink matches our
434 			 * script: flag our script as enabled
435 			 *
436 			 * Also clear the enabled script so it can be
437 			 * trivially skipped in the next pass
438 			 */
439 			flag = EVENT_SCRIPT_ENABLED;
440 			TALLOC_FREE(etc_list->script[j]);
441 			j++;
442 			matched++;
443 			break;
444 		}
445 
446 		printf("%c %s\n", flag, d->name);
447 	}
448 
449 	/* Print blank line if both provided and local lists are being printed */
450 	if (data_list->num_scripts > 0 && matched != etc_list->num_scripts) {
451 		printf("\n");
452 	}
453 
454 list_enabled_only:
455 
456 	/* Now print details of local scripts, after a blank line */
457 	for (j = 0; j < etc_list->num_scripts; j++) {
458 		struct event_script *e = etc_list->script[j];
459 		char flag = EVENT_SCRIPT_DISABLED;
460 
461 		if (e == NULL) {
462 			/* Matched in previous pass: next */
463 			continue;
464 		}
465 
466 		/* Script is local: if executable then flag as enabled */
467 		if (e->enabled) {
468 			flag = EVENT_SCRIPT_ENABLED;
469 		}
470 
471 		printf("%c %s\n", flag, e->name);
472 	}
473 
474 	ret = 0;
475 
476 done:
477 	talloc_free(subdir);
478 	talloc_free(data_dir);
479 	talloc_free(etc_dir);
480 	talloc_free(data_list);
481 	talloc_free(etc_list);
482 
483 	return ret;
484 }
485 
event_command_script(TALLOC_CTX * mem_ctx,struct event_tool_context * ctx,const char * component,const char * script,bool enable)486 static int event_command_script(TALLOC_CTX *mem_ctx,
487 				struct event_tool_context *ctx,
488 				const char *component,
489 				const char *script,
490 				bool enable)
491 {
492 	char *subdir, *etc_dir;
493 	int result = 0;
494 
495 	subdir = talloc_asprintf(mem_ctx, "events/%s", component);
496 	if (subdir == NULL) {
497 		return ENOMEM;
498 	}
499 
500 	etc_dir = path_etcdir_append(mem_ctx, subdir);
501 	if (etc_dir == NULL) {
502 		return ENOMEM;
503 	}
504 
505 	if (enable) {
506 		result = event_script_chmod(etc_dir, script, true);
507 	} else {
508 		result = event_script_chmod(etc_dir, script, false);
509 	}
510 
511 	talloc_free(subdir);
512 	talloc_free(etc_dir);
513 
514 	D_NOTICE("Command script finished with result=%d\n", result);
515 
516 	if (result == EINVAL) {
517 		printf("Script %s is invalid in %s\n", script, component);
518 	} else if (result == ENOENT) {
519 		printf("Script %s does not exist in %s\n", script, component);
520 	}
521 
522 	return result;
523 }
524 
event_command_script_enable(TALLOC_CTX * mem_ctx,int argc,const char ** argv,void * private_data)525 static int event_command_script_enable(TALLOC_CTX *mem_ctx,
526 				       int argc,
527 				       const char **argv,
528 				       void *private_data)
529 {
530 	struct event_tool_context *ctx = talloc_get_type_abort(
531 		private_data, struct event_tool_context);
532 	struct stat statbuf;
533 	char *script, *etc_script;
534 	int ret;
535 
536 	if (argc != 2) {
537 		cmdline_usage(ctx->cmdline, "script enable");
538 		return 1;
539 	}
540 
541 	script = talloc_asprintf(mem_ctx, "events/%s/%s.script", argv[0], argv[1]);
542 	if (script == NULL) {
543 		return ENOMEM;
544 	}
545 
546 	etc_script = path_etcdir_append(mem_ctx, script);
547 	if (etc_script == NULL) {
548 		return ENOMEM;
549 	}
550 
551 	ret = lstat(etc_script, &statbuf);
552 	if (ret == 0) {
553 		if (S_ISLNK(statbuf.st_mode)) {
554 			/* Link already exists */
555 			return 0;
556 		} else if (S_ISREG(statbuf.st_mode)) {
557 			return event_command_script(mem_ctx,
558 						    ctx,
559 						    argv[0],
560 						    argv[1],
561 						    true);
562 		}
563 
564 		printf("Script %s is not a file or a link\n", etc_script);
565 		return EINVAL;
566 	} else {
567 		if (errno == ENOENT) {
568 			char *t;
569 			char *data_script;
570 
571 			data_script = path_datadir_append(mem_ctx, script);
572 			if (data_script == NULL) {
573 				return ENOMEM;
574 			}
575 
576 			t = talloc_size(mem_ctx, PATH_MAX);
577 			if (t == NULL) {
578 				return ENOMEM;
579 			}
580 
581 			data_script = realpath(data_script, t);
582 			if (data_script == NULL) {
583 				if (errno != ENOENT) {
584 					return errno;
585 				}
586 				printf("Script %s does not exist in %s\n",
587 				       argv[1],
588 				       argv[0]);
589 				return ENOENT;
590 			}
591 
592 			ret = stat(data_script, &statbuf);
593 			if (ret != 0) {
594 				printf("Script %s does not exist in %s\n",
595 				       argv[1], argv[0]);
596 				return ENOENT;
597 			}
598 
599 			ret = symlink(data_script, etc_script);
600 			if (ret != 0) {
601 				printf("Failed to create symlink %s\n",
602 				      etc_script);
603 				return EIO;
604 			}
605 
606 			return 0;
607 		}
608 
609 		printf("Script %s does not exist\n", etc_script);
610 		return EINVAL;
611 	}
612 }
613 
event_command_script_disable(TALLOC_CTX * mem_ctx,int argc,const char ** argv,void * private_data)614 static int event_command_script_disable(TALLOC_CTX *mem_ctx,
615 					int argc,
616 					const char **argv,
617 					void *private_data)
618 {
619 	struct event_tool_context *ctx = talloc_get_type_abort(
620 		private_data, struct event_tool_context);
621 	struct stat statbuf;
622 	char *script, *etc_script;
623 	int ret;
624 
625 
626 	if (argc != 2) {
627 		cmdline_usage(ctx->cmdline, "script disable");
628 		return 1;
629 	}
630 
631 	script = talloc_asprintf(mem_ctx, "events/%s/%s.script", argv[0], argv[1]);
632 	if (script == NULL) {
633 		return ENOMEM;
634 	}
635 
636 	etc_script = path_etcdir_append(mem_ctx, script);
637 	if (etc_script == NULL) {
638 		return ENOMEM;
639 	}
640 
641 	ret = lstat(etc_script, &statbuf);
642 	if (ret == 0) {
643 		if (S_ISLNK(statbuf.st_mode)) {
644 			/* Link exists */
645 			ret = unlink(etc_script);
646 			if (ret != 0) {
647 				printf("Failed to remove symlink %s\n",
648 				       etc_script);
649 				return EIO;
650 			}
651 
652 			return 0;
653 		} else if (S_ISREG(statbuf.st_mode)) {
654 			return event_command_script(mem_ctx,
655 						    ctx,
656 						    argv[0],
657 						    argv[1],
658 						    false);
659 		}
660 
661 		printf("Script %s is not a file or a link\n", etc_script);
662 		return EINVAL;
663 	}
664 
665 	return 0;
666 }
667 
668 struct cmdline_command event_commands[] = {
669 	{ "run", event_command_run,
670 		"Run an event", "<timeout> <component> <event> <args>" },
671 	{ "status", event_command_status,
672 		"Get status of an event", "<component> <event>" },
673 	{ "script list", event_command_script_list,
674 		"List event scripts", "<component>" },
675 	{ "script enable", event_command_script_enable,
676 		"Enable an event script", "<component> <script>" },
677 	{ "script disable", event_command_script_disable,
678 		"Disable an event script", "<component> <script>" },
679 	CMDLINE_TABLEEND
680 };
681 
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)682 int event_tool_init(TALLOC_CTX *mem_ctx,
683 		    const char *prog,
684 		    struct poptOption *options,
685 		    int argc,
686 		    const char **argv,
687 		    bool parse_options,
688 		    struct event_tool_context **result)
689 {
690 	struct event_tool_context *ctx;
691 	int ret;
692 
693 	ctx = talloc_zero(mem_ctx, struct event_tool_context);
694 	if (ctx == NULL) {
695 		D_ERR("Memory allocation error\n");
696 		return ENOMEM;
697 	}
698 
699 	ret = cmdline_init(mem_ctx,
700 			   prog,
701 			   options,
702 			   NULL,
703 			   event_commands,
704 			   &ctx->cmdline);
705 	if (ret != 0) {
706 		D_ERR("Failed to initialize cmdline, ret=%d\n", ret);
707 		talloc_free(ctx);
708 		return ret;
709 	}
710 
711 	ret = cmdline_parse(ctx->cmdline, argc, argv, parse_options);
712 	if (ret != 0) {
713 		cmdline_usage(ctx->cmdline, NULL);
714 		talloc_free(ctx);
715 		return ret;
716 	}
717 
718 	*result = ctx;
719 	return 0;
720 }
721 
event_tool_run(struct event_tool_context * ctx,int * result)722 int event_tool_run(struct event_tool_context *ctx, int *result)
723 {
724 	int ret;
725 
726 	ctx->ev = tevent_context_init(ctx);
727 	if (ctx->ev == NULL) {
728 		D_ERR("Failed to initialize tevent\n");
729 		return ENOMEM;
730 	}
731 
732 	ret = cmdline_run(ctx->cmdline, ctx, result);
733 	return ret;
734 }
735 
736 #ifdef CTDB_EVENT_TOOL
737 
738 static struct {
739 	const char *debug;
740 } event_data = {
741 	.debug = "ERROR",
742 };
743 
744 struct poptOption event_options[] = {
745 	{ "debug", 'd', POPT_ARG_STRING, &event_data.debug, 0,
746 		"debug level", "ERROR|WARNING|NOTICE|INFO|DEBUG" },
747 	POPT_TABLEEND
748 };
749 
main(int argc,const char ** argv)750 int main(int argc, const char **argv)
751 {
752 	TALLOC_CTX *mem_ctx;
753 	struct event_tool_context *ctx;
754 	int ret, result = 0;
755 	int level;
756 	bool ok;
757 
758 	mem_ctx = talloc_new(NULL);
759 	if (mem_ctx == NULL) {
760 		fprintf(stderr, "Memory allocation error\n");
761 		exit(1);
762 	}
763 
764 	ret = event_tool_init(mem_ctx,
765 			      "ctdb-event",
766 			      event_options,
767 			      argc,
768 			      argv,
769 			      true,
770 			      &ctx);
771 	if (ret != 0) {
772 		talloc_free(mem_ctx);
773 		exit(1);
774 	}
775 
776 	setup_logging("ctdb-event", DEBUG_STDERR);
777 	ok = debug_level_parse(event_data.debug, &level);
778 	if (!ok) {
779 		level = DEBUG_ERR;
780 	}
781 	debuglevel_set(level);
782 
783 	ret = event_tool_run(ctx, &result);
784 	if (ret != 0) {
785 		exit(1);
786 	}
787 
788 	talloc_free(mem_ctx);
789 	exit(result);
790 }
791 
792 #endif /* CTDB_EVENT_TOOL */
793