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/run_event.h"
40 static int script_filter(const struct dirent *de)
44 /* Match a script pattern */
45 ret = fnmatch("[0-9][0-9].*.script", de->d_name, 0);
53 static int get_script_list(TALLOC_CTX *mem_ctx,
54 const char *script_dir,
55 struct run_event_script_list **out)
57 struct dirent **namelist = NULL;
58 struct run_event_script_list *script_list;
63 count = scandir(script_dir, &namelist, script_filter, alphasort);
67 D_WARNING("event script dir %s removed\n", script_dir);
69 D_WARNING("scandir() failed on %s, ret=%d\n",
82 script_list = talloc_zero(mem_ctx, struct run_event_script_list);
83 if (script_list == NULL) {
87 script_list->num_scripts = count;
88 script_list->script = talloc_zero_array(script_list,
89 struct run_event_script,
91 if (script_list->script == NULL) {
93 talloc_free(script_list);
97 ls = strlen(".script");
98 for (i=0; i<count; i++) {
99 struct run_event_script *s = &script_list->script[i];
101 s->name = talloc_strndup(script_list,
103 strlen(namelist[i]->d_name) - ls);
104 if (s->name == NULL) {
106 talloc_free(script_list);
115 if (namelist != NULL && count != -1) {
116 for (i=0; i<count; i++) {
124 static int script_chmod(TALLOC_CTX *mem_ctx, const char *script_dir,
125 const char *script_name, bool enable)
129 char script_file[PATH_MAX];
136 ret = snprintf(script_file,
140 if (ret >= sizeof(script_file)) {
144 dirp = opendir(script_dir);
150 while ((de = readdir(dirp)) != NULL) {
151 if (strcmp(de->d_name, script_file) == 0) {
153 /* check for valid script names */
154 ret = script_filter(de);
170 filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_file);
171 if (filename == NULL) {
175 fd = open(filename, O_RDWR);
181 ret = fstat(fd, &st);
188 new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH);
190 new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
193 ret = fchmod(fd, new_mode);
203 talloc_free(filename);
207 static int script_args(TALLOC_CTX *mem_ctx, const char *event_str,
208 const char *arg_str, const char ***out)
214 /* Preallocate argv array to avoid reallocation. */
216 argv = talloc_array(mem_ctx, const char *, len);
221 argv[0] = NULL; /* script name */
225 if (arg_str != NULL) {
228 str = talloc_strdup(argv, arg_str);
234 while ((tok = strtok(t, " ")) != NULL) {
235 argv[argc] = talloc_strdup(argv, tok);
236 if (argv[argc] == NULL) {
242 argv = talloc_realloc(mem_ctx, argv,
243 const char *, len + 8);
262 struct run_event_context {
263 struct run_proc_context *run_proc_ctx;
264 const char *script_dir;
265 const char *debug_prog;
268 struct tevent_queue *queue;
269 struct tevent_req *current_req;
270 bool monitor_running;
274 int run_event_init(TALLOC_CTX *mem_ctx, struct run_proc_context *run_proc_ctx,
275 const char *script_dir, const char *debug_prog,
276 struct run_event_context **out)
278 struct run_event_context *run_ctx;
282 run_ctx = talloc_zero(mem_ctx, struct run_event_context);
283 if (run_ctx == NULL) {
287 run_ctx->run_proc_ctx = run_proc_ctx;
289 ret = stat(script_dir, &st);
292 talloc_free(run_ctx);
296 if (! S_ISDIR(st.st_mode)) {
297 talloc_free(run_ctx);
301 run_ctx->script_dir = talloc_strdup(run_ctx, script_dir);
302 if (run_ctx->script_dir == NULL) {
303 talloc_free(run_ctx);
307 if (debug_prog != NULL) {
308 run_ctx->debug_prog = talloc_strdup(run_ctx, debug_prog);
309 if (run_ctx->debug_prog == NULL) {
310 talloc_free(run_ctx);
315 run_ctx->debug_running = false;
317 run_ctx->queue = tevent_queue_create(run_ctx, "run event queue");
318 if (run_ctx->queue == NULL) {
319 talloc_free(run_ctx);
323 run_ctx->monitor_running = false;
329 static struct run_proc_context *
330 run_event_run_proc_context(struct run_event_context *run_ctx)
332 return run_ctx->run_proc_ctx;
335 static const char *run_event_script_dir(struct run_event_context *run_ctx)
337 return run_ctx->script_dir;
340 static const char *run_event_debug_prog(struct run_event_context *run_ctx)
342 return run_ctx->debug_prog;
345 static struct tevent_queue *run_event_queue(struct run_event_context *run_ctx)
347 return run_ctx->queue;
350 static void run_event_start_running(struct run_event_context *run_ctx,
351 struct tevent_req *req, bool is_monitor)
353 run_ctx->current_req = req;
354 run_ctx->monitor_running = is_monitor;
357 static void run_event_stop_running(struct run_event_context *run_ctx)
359 run_ctx->current_req = NULL;
360 run_ctx->monitor_running = false;
363 static struct tevent_req *run_event_get_running(
364 struct run_event_context *run_ctx,
367 *is_monitor = run_ctx->monitor_running;
368 return run_ctx->current_req;
371 static int run_event_script_status(struct run_event_script *script)
375 if (script->result.sig > 0) {
377 } else if (script->result.err > 0) {
378 if (script->result.err == EACCES) {
379 /* Map EACCESS to ENOEXEC */
382 ret = -script->result.err;
385 ret = script->result.status;
391 int run_event_list(struct run_event_context *run_ctx,
393 struct run_event_script_list **output)
395 struct run_event_script_list *script_list;
398 ret = get_script_list(mem_ctx, run_event_script_dir(run_ctx),
404 if (script_list == NULL) {
409 for (i=0; i<script_list->num_scripts; i++) {
410 struct run_event_script *script = &script_list->script[i];
414 path = talloc_asprintf(mem_ctx, "%s/%s.script",
415 run_event_script_dir(run_ctx),
421 ret = stat(path, &st);
427 if (! (st.st_mode & S_IXUSR)) {
428 script->summary = -ENOEXEC;
434 *output = script_list;
438 int run_event_script_enable(struct run_event_context *run_ctx,
439 const char *script_name)
441 return script_chmod(run_ctx, run_event_script_dir(run_ctx),
445 int run_event_script_disable(struct run_event_context *run_ctx,
446 const char *script_name)
448 return script_chmod(run_ctx, run_event_script_dir(run_ctx),
453 * Run debug program to diagnose hung scripts
456 static int debug_args(TALLOC_CTX *mem_ctx, const char *path,
457 const char *event_str, pid_t pid, const char ***out)
461 argv = talloc_array(mem_ctx, const char *, 4);
467 argv[1] = talloc_asprintf(argv, "%d", pid);
469 if (argv[1] == NULL) {
479 static void debug_log(int loglevel, const char *output, const char *log_prefix)
485 DEBUG(loglevel, ("%s: %s\n", log_prefix, output));
489 line = strtok(s, "\n");
490 while (line != NULL) {
491 DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
492 line = strtok(NULL, "\n");
497 struct run_debug_state {
498 struct run_event_context *run_ctx;
502 static void run_debug_done(struct tevent_req *subreq);
504 static struct tevent_req *run_debug_send(TALLOC_CTX *mem_ctx,
505 struct tevent_context *ev,
506 struct run_event_context *run_ctx,
507 const char *event_str, pid_t pid)
509 struct tevent_req *req, *subreq;
510 struct run_debug_state *state;
512 const char *debug_prog;
515 req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
520 state->run_ctx = run_ctx;
523 debug_prog = run_event_debug_prog(run_ctx);
524 if (debug_prog == NULL) {
525 tevent_req_done(req);
526 return tevent_req_post(req, ev);
529 if (run_ctx->debug_running) {
530 tevent_req_done(req);
531 return tevent_req_post(req, ev);
535 D_DEBUG("Event script terminated, nothing to debug\n");
536 tevent_req_done(req);
537 return tevent_req_post(req, ev);
540 ret = debug_args(state, debug_prog, event_str, pid, &argv);
542 D_ERR("debug_args() failed\n");
543 tevent_req_error(req, ret);
544 return tevent_req_post(req, ev);
547 D_DEBUG("Running debug %s with args \"%s %s\"\n",
548 debug_prog, argv[1], argv[2]);
550 subreq = run_proc_send(state, ev, run_event_run_proc_context(run_ctx),
551 debug_prog, argv, -1, tevent_timeval_zero());
552 if (tevent_req_nomem(subreq, req)) {
553 return tevent_req_post(req, ev);
555 tevent_req_set_callback(subreq, run_debug_done, req);
557 run_ctx->debug_running = true;
563 static void run_debug_done(struct tevent_req *subreq)
565 struct tevent_req *req = tevent_req_callback_data(
566 subreq, struct tevent_req);
567 struct run_debug_state *state = tevent_req_data(
568 req, struct run_debug_state);
573 state->run_ctx->debug_running = false;
575 status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output);
578 D_ERR("Running debug failed, ret=%d\n", ret);
582 if (output != NULL) {
583 debug_log(DEBUG_ERR, output, "event_debug");
587 kill(-state->pid, SIGTERM);
588 tevent_req_done(req);
591 static bool run_debug_recv(struct tevent_req *req, int *perr)
595 if (tevent_req_is_unix_error(req, &ret)) {
609 struct run_event_state {
610 struct tevent_context *ev;
611 struct run_event_context *run_ctx;
612 const char *event_str;
614 struct timeval timeout;
615 bool continue_on_failure;
617 struct run_event_script_list *script_list;
619 struct tevent_req *script_subreq;
624 static void run_event_cancel(struct tevent_req *req);
625 static void run_event_trigger(struct tevent_req *req, void *private_data);
626 static struct tevent_req *run_event_run_script(struct tevent_req *req);
627 static void run_event_next_script(struct tevent_req *subreq);
628 static void run_event_debug(struct tevent_req *req, pid_t pid);
629 static void run_event_debug_done(struct tevent_req *subreq);
631 struct tevent_req *run_event_send(TALLOC_CTX *mem_ctx,
632 struct tevent_context *ev,
633 struct run_event_context *run_ctx,
634 const char *event_str,
636 struct timeval timeout,
637 bool continue_on_failure)
639 struct tevent_req *req, *current_req;
640 struct run_event_state *state;
641 bool monitor_running, status;
643 req = tevent_req_create(mem_ctx, &state, struct run_event_state);
649 state->run_ctx = run_ctx;
650 state->event_str = talloc_strdup(state, event_str);
651 if (tevent_req_nomem(state->event_str, req)) {
652 return tevent_req_post(req, ev);
654 if (arg_str != NULL) {
655 state->arg_str = talloc_strdup(state, arg_str);
656 if (tevent_req_nomem(state->arg_str, req)) {
657 return tevent_req_post(req, ev);
660 state->timeout = timeout;
661 state->continue_on_failure = continue_on_failure;
662 state->cancelled = false;
664 state->script_list = talloc_zero(state, struct run_event_script_list);
665 if (tevent_req_nomem(state->script_list, req)) {
666 return tevent_req_post(req, ev);
670 * If monitor event is running,
671 * cancel the running monitor event and run new event
673 * If any other event is running,
674 * if new event is monitor, cancel that event
675 * else add new event to the queue
678 current_req = run_event_get_running(run_ctx, &monitor_running);
679 if (current_req != NULL) {
680 if (monitor_running) {
681 run_event_cancel(current_req);
682 } else if (strcmp(event_str, "monitor") == 0) {
683 state->script_list->summary = -ECANCELED;
684 tevent_req_done(req);
685 return tevent_req_post(req, ev);
689 status = tevent_queue_add(run_event_queue(run_ctx), ev, req,
690 run_event_trigger, NULL);
692 tevent_req_error(req, ENOMEM);
693 return tevent_req_post(req, ev);
699 static void run_event_cancel(struct tevent_req *req)
701 struct run_event_state *state = tevent_req_data(
702 req, struct run_event_state);
704 run_event_stop_running(state->run_ctx);
706 state->script_list->summary = -ECANCELED;
707 state->cancelled = true;
709 TALLOC_FREE(state->script_subreq);
711 tevent_req_done(req);
714 static void run_event_trigger(struct tevent_req *req, void *private_data)
716 struct tevent_req *subreq;
717 struct run_event_state *state = tevent_req_data(
718 req, struct run_event_state);
719 struct run_event_script_list *script_list;
721 bool is_monitor = false;
723 D_DEBUG("Running event %s with args \"%s\"\n", state->event_str,
724 state->arg_str == NULL ? "(null)" : state->arg_str);
726 ret = get_script_list(state,
727 run_event_script_dir(state->run_ctx),
730 D_ERR("get_script_list() failed, ret=%d\n", ret);
731 tevent_req_error(req, ret);
736 if (script_list == NULL || script_list->num_scripts == 0) {
737 tevent_req_done(req);
741 talloc_free(state->script_list);
742 state->script_list = script_list;
744 ret = script_args(state, state->event_str, state->arg_str,
747 D_ERR("script_args() failed, ret=%d\n", ret);
748 tevent_req_error(req, ret);
754 subreq = run_event_run_script(req);
755 if (tevent_req_nomem(subreq, req)) {
758 tevent_req_set_callback(subreq, run_event_next_script, req);
760 state->script_subreq = subreq;
762 if (strcmp(state->event_str, "monitor") == 0) {
765 run_event_start_running(state->run_ctx, req, is_monitor);
768 static struct tevent_req *run_event_run_script(struct tevent_req *req)
770 struct run_event_state *state = tevent_req_data(
771 req, struct run_event_state);
772 struct run_event_script *script;
773 struct tevent_req *subreq;
776 script = &state->script_list->script[state->index];
778 path = talloc_asprintf(state, "%s/%s.script",
779 run_event_script_dir(state->run_ctx),
785 state->argv[0] = script->name;
786 script->begin = tevent_timeval_current();
788 D_DEBUG("Running %s with args \"%s %s\"\n",
789 path, state->argv[0], state->argv[1]);
791 subreq = run_proc_send(state, state->ev,
792 run_event_run_proc_context(state->run_ctx),
793 path, state->argv, -1, state->timeout);
800 static void run_event_next_script(struct tevent_req *subreq)
802 struct tevent_req *req = tevent_req_callback_data(
803 subreq, struct tevent_req);
804 struct run_event_state *state = tevent_req_data(
805 req, struct run_event_state);
806 struct run_event_script *script;
811 script = &state->script_list->script[state->index];
812 script->end = tevent_timeval_current();
814 status = run_proc_recv(subreq, &ret, &script->result, &pid,
815 state->script_list, &script->output);
817 state->script_subreq = NULL;
819 D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret);
820 run_event_stop_running(state->run_ctx);
821 tevent_req_error(req, ret);
825 if (state->cancelled) {
830 if (script->output != NULL) {
831 debug_log(DEBUG_ERR, script->output, script->name);
834 D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n",
835 script->name, script->result.sig, script->result.err,
836 script->result.status);
839 /* If a script fails, stop running */
840 script->summary = run_event_script_status(script);
841 if (script->summary != 0 && script->summary != -ENOEXEC) {
842 state->script_list->summary = script->summary;
844 if (! state->continue_on_failure) {
845 state->script_list->num_scripts = state->index + 1;
847 if (script->summary == -ETIME && pid != -1) {
848 run_event_debug(req, pid);
850 D_NOTICE("%s event %s\n", state->event_str,
851 (script->summary == -ETIME) ?
854 run_event_stop_running(state->run_ctx);
855 tevent_req_done(req);
862 /* All scripts executed */
863 if (state->index >= state->script_list->num_scripts) {
864 run_event_stop_running(state->run_ctx);
865 tevent_req_done(req);
869 subreq = run_event_run_script(req);
870 if (tevent_req_nomem(subreq, req)) {
873 tevent_req_set_callback(subreq, run_event_next_script, req);
875 state->script_subreq = subreq;
878 static void run_event_debug(struct tevent_req *req, pid_t pid)
880 struct run_event_state *state = tevent_req_data(
881 req, struct run_event_state);
882 struct tevent_req *subreq;
884 /* Debug script is run with ectx as the memory context */
885 subreq = run_debug_send(state->run_ctx, state->ev, state->run_ctx,
886 state->event_str, pid);
887 if (subreq == NULL) {
888 /* If run debug fails, it's not an error */
889 D_NOTICE("Failed to run event debug\n");
892 tevent_req_set_callback(subreq, run_event_debug_done, NULL);
895 static void run_event_debug_done(struct tevent_req *subreq)
900 status = run_debug_recv(subreq, &ret);
903 D_NOTICE("run_debug() failed, ret=%d\n", ret);
907 bool run_event_recv(struct tevent_req *req, int *perr,
909 struct run_event_script_list **script_list)
911 struct run_event_state *state = tevent_req_data(
912 req, struct run_event_state);
915 if (tevent_req_is_unix_error(req, &ret)) {
922 if (script_list != NULL) {
923 *script_list = talloc_steal(mem_ctx, state->script_list);