2 Run scripts in a directory with specific event arguments
4 Copyright (C) Amitay Isaacs 2017
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/glob.h"
24 #include "system/wait.h"
29 #include "lib/util/tevent_unix.h"
30 #include "lib/util/debug.h"
32 #include "common/logging.h"
33 #include "common/run_proc.h"
34 #include "common/event_script.h"
36 #include "common/run_event.h"
42 static int get_script_list(TALLOC_CTX *mem_ctx,
43 const char *script_dir,
44 struct run_event_script_list **out)
46 struct event_script_list *s_list;
47 struct run_event_script_list *script_list;
51 ret = event_script_get_list(mem_ctx, script_dir, &s_list);
54 D_WARNING("event script dir %s removed\n", script_dir);
56 D_WARNING("failed to get script list for %s, ret=%d\n",
62 if (s_list->num_scripts == 0) {
68 script_list = talloc_zero(mem_ctx, struct run_event_script_list);
69 if (script_list == NULL) {
74 script_list->num_scripts = s_list->num_scripts;
75 script_list->script = talloc_zero_array(script_list,
76 struct run_event_script,
77 script_list->num_scripts);
78 if (script_list->script == NULL) {
80 talloc_free(script_list);
84 for (i = 0; i < s_list->num_scripts; i++) {
85 struct event_script *s = s_list->script[i];
86 struct run_event_script *script = &script_list->script[i];
88 script->name = talloc_steal(script_list->script, s->name);
91 script->summary = -ENOEXEC;
100 static int script_args(TALLOC_CTX *mem_ctx, const char *event_str,
101 const char *arg_str, const char ***out)
107 /* Preallocate argv array to avoid reallocation. */
109 argv = talloc_array(mem_ctx, const char *, len);
114 argv[0] = NULL; /* script name */
118 if (arg_str != NULL) {
121 str = talloc_strdup(argv, arg_str);
127 while ((tok = strtok(t, " ")) != NULL) {
128 argv[argc] = talloc_strdup(argv, tok);
129 if (argv[argc] == NULL) {
135 argv = talloc_realloc(mem_ctx, argv,
136 const char *, len + 8);
155 struct run_event_context {
156 struct run_proc_context *run_proc_ctx;
157 const char *script_dir;
158 const char *debug_prog;
161 struct tevent_queue *queue;
162 struct tevent_req *current_req;
163 bool monitor_running;
167 int run_event_init(TALLOC_CTX *mem_ctx, struct run_proc_context *run_proc_ctx,
168 const char *script_dir, const char *debug_prog,
169 struct run_event_context **out)
171 struct run_event_context *run_ctx;
175 run_ctx = talloc_zero(mem_ctx, struct run_event_context);
176 if (run_ctx == NULL) {
180 run_ctx->run_proc_ctx = run_proc_ctx;
182 ret = stat(script_dir, &st);
185 talloc_free(run_ctx);
189 if (! S_ISDIR(st.st_mode)) {
190 talloc_free(run_ctx);
194 run_ctx->script_dir = talloc_strdup(run_ctx, script_dir);
195 if (run_ctx->script_dir == NULL) {
196 talloc_free(run_ctx);
200 if (debug_prog != NULL) {
201 run_ctx->debug_prog = talloc_strdup(run_ctx, debug_prog);
202 if (run_ctx->debug_prog == NULL) {
203 talloc_free(run_ctx);
208 run_ctx->debug_running = false;
210 run_ctx->queue = tevent_queue_create(run_ctx, "run event queue");
211 if (run_ctx->queue == NULL) {
212 talloc_free(run_ctx);
216 run_ctx->monitor_running = false;
222 static struct run_proc_context *
223 run_event_run_proc_context(struct run_event_context *run_ctx)
225 return run_ctx->run_proc_ctx;
228 static const char *run_event_script_dir(struct run_event_context *run_ctx)
230 return run_ctx->script_dir;
233 static const char *run_event_debug_prog(struct run_event_context *run_ctx)
235 return run_ctx->debug_prog;
238 static struct tevent_queue *run_event_queue(struct run_event_context *run_ctx)
240 return run_ctx->queue;
243 static void run_event_start_running(struct run_event_context *run_ctx,
244 struct tevent_req *req, bool is_monitor)
246 run_ctx->current_req = req;
247 run_ctx->monitor_running = is_monitor;
250 static void run_event_stop_running(struct run_event_context *run_ctx)
252 run_ctx->current_req = NULL;
253 run_ctx->monitor_running = false;
256 static struct tevent_req *run_event_get_running(
257 struct run_event_context *run_ctx,
260 *is_monitor = run_ctx->monitor_running;
261 return run_ctx->current_req;
264 static int run_event_script_status(struct run_event_script *script)
268 if (script->result.sig > 0) {
270 } else if (script->result.err > 0) {
271 if (script->result.err == EACCES) {
272 /* Map EACCESS to ENOEXEC */
275 ret = -script->result.err;
278 ret = script->result.status;
284 int run_event_list(struct run_event_context *run_ctx,
286 struct run_event_script_list **output)
288 struct event_script_list *s_list = NULL;
289 struct run_event_script_list *script_list = NULL;
293 ret = event_script_get_list(mem_ctx,
294 run_event_script_dir(run_ctx),
300 if (s_list->num_scripts == 0) {
306 script_list = talloc_zero(mem_ctx, struct run_event_script_list);
307 if (script_list == NULL) {
311 script_list->num_scripts = s_list->num_scripts;
312 script_list->script = talloc_zero_array(script_list,
313 struct run_event_script,
314 script_list->num_scripts);
315 if (script_list->script == NULL) {
317 talloc_free(script_list);
321 for (i=0; i < s_list->num_scripts; i++) {
322 struct event_script *s = s_list->script[i];
323 struct run_event_script *script = &script_list->script[i];
325 script->name = talloc_steal(script_list->script, s->name);
328 script->summary = -ENOEXEC;
334 *output = script_list;
338 int run_event_script_enable(struct run_event_context *run_ctx,
339 const char *script_name)
341 return event_script_chmod(run_event_script_dir(run_ctx),
346 int run_event_script_disable(struct run_event_context *run_ctx,
347 const char *script_name)
349 return event_script_chmod(run_event_script_dir(run_ctx),
355 * Run debug program to diagnose hung scripts
358 static int debug_args(TALLOC_CTX *mem_ctx, const char *path,
359 const char *event_str, pid_t pid, const char ***out)
363 argv = talloc_array(mem_ctx, const char *, 4);
369 argv[1] = talloc_asprintf(argv, "%d", pid);
371 if (argv[1] == NULL) {
381 static void debug_log(int loglevel, const char *output, const char *log_prefix)
387 DEBUG(loglevel, ("%s: %s\n", log_prefix, output));
391 line = strtok(s, "\n");
392 while (line != NULL) {
393 DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
394 line = strtok(NULL, "\n");
399 struct run_debug_state {
400 struct run_event_context *run_ctx;
404 static void run_debug_done(struct tevent_req *subreq);
406 static struct tevent_req *run_debug_send(TALLOC_CTX *mem_ctx,
407 struct tevent_context *ev,
408 struct run_event_context *run_ctx,
409 const char *event_str, pid_t pid)
411 struct tevent_req *req, *subreq;
412 struct run_debug_state *state;
414 const char *debug_prog;
417 req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
422 state->run_ctx = run_ctx;
425 debug_prog = run_event_debug_prog(run_ctx);
426 if (debug_prog == NULL) {
427 tevent_req_done(req);
428 return tevent_req_post(req, ev);
431 if (run_ctx->debug_running) {
432 tevent_req_done(req);
433 return tevent_req_post(req, ev);
437 D_DEBUG("Event script terminated, nothing to debug\n");
438 tevent_req_done(req);
439 return tevent_req_post(req, ev);
442 ret = debug_args(state, debug_prog, event_str, pid, &argv);
444 D_ERR("debug_args() failed\n");
445 tevent_req_error(req, ret);
446 return tevent_req_post(req, ev);
449 D_DEBUG("Running debug %s with args \"%s %s\"\n",
450 debug_prog, argv[1], argv[2]);
452 subreq = run_proc_send(state, ev, run_event_run_proc_context(run_ctx),
453 debug_prog, argv, -1, tevent_timeval_zero());
454 if (tevent_req_nomem(subreq, req)) {
455 return tevent_req_post(req, ev);
457 tevent_req_set_callback(subreq, run_debug_done, req);
459 run_ctx->debug_running = true;
465 static void run_debug_done(struct tevent_req *subreq)
467 struct tevent_req *req = tevent_req_callback_data(
468 subreq, struct tevent_req);
469 struct run_debug_state *state = tevent_req_data(
470 req, struct run_debug_state);
475 state->run_ctx->debug_running = false;
477 status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output);
480 D_ERR("Running debug failed, ret=%d\n", ret);
484 if (output != NULL) {
485 debug_log(DEBUG_ERR, output, "event_debug");
489 kill(-state->pid, SIGTERM);
490 tevent_req_done(req);
493 static bool run_debug_recv(struct tevent_req *req, int *perr)
497 if (tevent_req_is_unix_error(req, &ret)) {
511 struct run_event_state {
512 struct tevent_context *ev;
513 struct run_event_context *run_ctx;
514 const char *event_str;
516 struct timeval timeout;
517 bool continue_on_failure;
519 struct run_event_script_list *script_list;
521 struct tevent_req *script_subreq;
526 static void run_event_cancel(struct tevent_req *req);
527 static void run_event_trigger(struct tevent_req *req, void *private_data);
528 static struct tevent_req *run_event_run_script(struct tevent_req *req);
529 static void run_event_next_script(struct tevent_req *subreq);
530 static void run_event_debug(struct tevent_req *req, pid_t pid);
531 static void run_event_debug_done(struct tevent_req *subreq);
533 struct tevent_req *run_event_send(TALLOC_CTX *mem_ctx,
534 struct tevent_context *ev,
535 struct run_event_context *run_ctx,
536 const char *event_str,
538 struct timeval timeout,
539 bool continue_on_failure)
541 struct tevent_req *req, *current_req;
542 struct run_event_state *state;
543 bool monitor_running, status;
545 req = tevent_req_create(mem_ctx, &state, struct run_event_state);
551 state->run_ctx = run_ctx;
552 state->event_str = talloc_strdup(state, event_str);
553 if (tevent_req_nomem(state->event_str, req)) {
554 return tevent_req_post(req, ev);
556 if (arg_str != NULL) {
557 state->arg_str = talloc_strdup(state, arg_str);
558 if (tevent_req_nomem(state->arg_str, req)) {
559 return tevent_req_post(req, ev);
562 state->timeout = timeout;
563 state->continue_on_failure = continue_on_failure;
564 state->cancelled = false;
566 state->script_list = talloc_zero(state, struct run_event_script_list);
567 if (tevent_req_nomem(state->script_list, req)) {
568 return tevent_req_post(req, ev);
572 * If monitor event is running,
573 * cancel the running monitor event and run new event
575 * If any other event is running,
576 * if new event is monitor, cancel that event
577 * else add new event to the queue
580 current_req = run_event_get_running(run_ctx, &monitor_running);
581 if (current_req != NULL) {
582 if (monitor_running) {
583 run_event_cancel(current_req);
584 } else if (strcmp(event_str, "monitor") == 0) {
585 state->script_list->summary = -ECANCELED;
586 tevent_req_done(req);
587 return tevent_req_post(req, ev);
591 status = tevent_queue_add(run_event_queue(run_ctx), ev, req,
592 run_event_trigger, NULL);
594 tevent_req_error(req, ENOMEM);
595 return tevent_req_post(req, ev);
601 static void run_event_cancel(struct tevent_req *req)
603 struct run_event_state *state = tevent_req_data(
604 req, struct run_event_state);
606 run_event_stop_running(state->run_ctx);
608 state->script_list->summary = -ECANCELED;
609 state->cancelled = true;
611 TALLOC_FREE(state->script_subreq);
613 tevent_req_done(req);
616 static void run_event_trigger(struct tevent_req *req, void *private_data)
618 struct tevent_req *subreq;
619 struct run_event_state *state = tevent_req_data(
620 req, struct run_event_state);
621 struct run_event_script_list *script_list;
623 bool is_monitor = false;
625 D_DEBUG("Running event %s with args \"%s\"\n", state->event_str,
626 state->arg_str == NULL ? "(null)" : state->arg_str);
628 ret = get_script_list(state,
629 run_event_script_dir(state->run_ctx),
632 D_ERR("get_script_list() failed, ret=%d\n", ret);
633 tevent_req_error(req, ret);
638 if (script_list == NULL || script_list->num_scripts == 0) {
639 tevent_req_done(req);
643 talloc_free(state->script_list);
644 state->script_list = script_list;
646 ret = script_args(state, state->event_str, state->arg_str,
649 D_ERR("script_args() failed, ret=%d\n", ret);
650 tevent_req_error(req, ret);
656 subreq = run_event_run_script(req);
657 if (tevent_req_nomem(subreq, req)) {
660 tevent_req_set_callback(subreq, run_event_next_script, req);
662 state->script_subreq = subreq;
664 if (strcmp(state->event_str, "monitor") == 0) {
667 run_event_start_running(state->run_ctx, req, is_monitor);
670 static struct tevent_req *run_event_run_script(struct tevent_req *req)
672 struct run_event_state *state = tevent_req_data(
673 req, struct run_event_state);
674 struct run_event_script *script;
675 struct tevent_req *subreq;
678 script = &state->script_list->script[state->index];
680 path = talloc_asprintf(state, "%s/%s.script",
681 run_event_script_dir(state->run_ctx),
687 state->argv[0] = script->name;
688 script->begin = tevent_timeval_current();
690 D_DEBUG("Running %s with args \"%s %s\"\n",
691 path, state->argv[0], state->argv[1]);
693 subreq = run_proc_send(state, state->ev,
694 run_event_run_proc_context(state->run_ctx),
695 path, state->argv, -1, state->timeout);
702 static void run_event_next_script(struct tevent_req *subreq)
704 struct tevent_req *req = tevent_req_callback_data(
705 subreq, struct tevent_req);
706 struct run_event_state *state = tevent_req_data(
707 req, struct run_event_state);
708 struct run_event_script *script;
713 script = &state->script_list->script[state->index];
714 script->end = tevent_timeval_current();
716 status = run_proc_recv(subreq, &ret, &script->result, &pid,
717 state->script_list, &script->output);
719 state->script_subreq = NULL;
721 D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret);
722 run_event_stop_running(state->run_ctx);
723 tevent_req_error(req, ret);
727 if (state->cancelled) {
732 if (script->output != NULL) {
733 debug_log(DEBUG_ERR, script->output, script->name);
736 D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n",
737 script->name, script->result.sig, script->result.err,
738 script->result.status);
741 /* If a script fails, stop running */
742 script->summary = run_event_script_status(script);
743 if (script->summary != 0 && script->summary != -ENOEXEC) {
744 state->script_list->summary = script->summary;
746 if (! state->continue_on_failure) {
747 state->script_list->num_scripts = state->index + 1;
749 if (script->summary == -ETIMEDOUT && pid != -1) {
750 run_event_debug(req, pid);
752 D_NOTICE("%s event %s\n", state->event_str,
753 (script->summary == -ETIMEDOUT) ?
756 run_event_stop_running(state->run_ctx);
757 tevent_req_done(req);
764 /* All scripts executed */
765 if (state->index >= state->script_list->num_scripts) {
766 run_event_stop_running(state->run_ctx);
767 tevent_req_done(req);
771 subreq = run_event_run_script(req);
772 if (tevent_req_nomem(subreq, req)) {
775 tevent_req_set_callback(subreq, run_event_next_script, req);
777 state->script_subreq = subreq;
780 static void run_event_debug(struct tevent_req *req, pid_t pid)
782 struct run_event_state *state = tevent_req_data(
783 req, struct run_event_state);
784 struct tevent_req *subreq;
786 /* Debug script is run with ectx as the memory context */
787 subreq = run_debug_send(state->run_ctx, state->ev, state->run_ctx,
788 state->event_str, pid);
789 if (subreq == NULL) {
790 /* If run debug fails, it's not an error */
791 D_NOTICE("Failed to run event debug\n");
794 tevent_req_set_callback(subreq, run_event_debug_done, NULL);
797 static void run_event_debug_done(struct tevent_req *subreq)
802 status = run_debug_recv(subreq, &ret);
805 D_NOTICE("run_debug() failed, ret=%d\n", ret);
809 bool run_event_recv(struct tevent_req *req, int *perr,
811 struct run_event_script_list **script_list)
813 struct run_event_state *state = tevent_req_data(
814 req, struct run_event_state);
817 if (tevent_req_is_unix_error(req, &ret)) {
824 if (script_list != NULL) {
825 *script_list = talloc_steal(mem_ctx, state->script_list);