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
;
289 struct run_event_script_list
*script_list
;
292 ret
= event_script_get_list(mem_ctx
,
293 run_event_script_dir(run_ctx
),
299 if (s_list
->num_scripts
== 0) {
305 script_list
= talloc_zero(mem_ctx
, struct run_event_script_list
);
306 if (script_list
== NULL
) {
310 script_list
->num_scripts
= s_list
->num_scripts
;
311 script_list
->script
= talloc_zero_array(script_list
,
312 struct run_event_script
,
313 script_list
->num_scripts
);
314 if (script_list
->script
== NULL
) {
316 talloc_free(script_list
);
320 for (i
=0; i
< s_list
->num_scripts
; i
++) {
321 struct event_script
*s
= s_list
->script
[i
];
322 struct run_event_script
*script
= &script_list
->script
[i
];
324 script
->name
= talloc_steal(script_list
->script
, s
->name
);
327 script
->summary
= -ENOEXEC
;
333 *output
= script_list
;
337 int run_event_script_enable(struct run_event_context
*run_ctx
,
338 const char *script_name
)
340 return event_script_chmod(run_event_script_dir(run_ctx
),
345 int run_event_script_disable(struct run_event_context
*run_ctx
,
346 const char *script_name
)
348 return event_script_chmod(run_event_script_dir(run_ctx
),
354 * Run debug program to diagnose hung scripts
357 static int debug_args(TALLOC_CTX
*mem_ctx
, const char *path
,
358 const char *event_str
, pid_t pid
, const char ***out
)
362 argv
= talloc_array(mem_ctx
, const char *, 4);
368 argv
[1] = talloc_asprintf(argv
, "%d", pid
);
370 if (argv
[1] == NULL
) {
380 static void debug_log(int loglevel
, const char *output
, const char *log_prefix
)
386 DEBUG(loglevel
, ("%s: %s\n", log_prefix
, output
));
390 line
= strtok(s
, "\n");
391 while (line
!= NULL
) {
392 DEBUG(loglevel
, ("%s: %s\n", log_prefix
, line
));
393 line
= strtok(NULL
, "\n");
398 struct run_debug_state
{
399 struct run_event_context
*run_ctx
;
403 static void run_debug_done(struct tevent_req
*subreq
);
405 static struct tevent_req
*run_debug_send(TALLOC_CTX
*mem_ctx
,
406 struct tevent_context
*ev
,
407 struct run_event_context
*run_ctx
,
408 const char *event_str
, pid_t pid
)
410 struct tevent_req
*req
, *subreq
;
411 struct run_debug_state
*state
;
413 const char *debug_prog
;
416 req
= tevent_req_create(mem_ctx
, &state
, struct run_debug_state
);
421 state
->run_ctx
= run_ctx
;
424 debug_prog
= run_event_debug_prog(run_ctx
);
425 if (debug_prog
== NULL
) {
426 tevent_req_done(req
);
427 return tevent_req_post(req
, ev
);
430 if (run_ctx
->debug_running
) {
431 tevent_req_done(req
);
432 return tevent_req_post(req
, ev
);
436 D_DEBUG("Event script terminated, nothing to debug\n");
437 tevent_req_done(req
);
438 return tevent_req_post(req
, ev
);
441 ret
= debug_args(state
, debug_prog
, event_str
, pid
, &argv
);
443 D_ERR("debug_args() failed\n");
444 tevent_req_error(req
, ret
);
445 return tevent_req_post(req
, ev
);
448 D_DEBUG("Running debug %s with args \"%s %s\"\n",
449 debug_prog
, argv
[1], argv
[2]);
451 subreq
= run_proc_send(state
, ev
, run_event_run_proc_context(run_ctx
),
452 debug_prog
, argv
, -1, tevent_timeval_zero());
453 if (tevent_req_nomem(subreq
, req
)) {
454 return tevent_req_post(req
, ev
);
456 tevent_req_set_callback(subreq
, run_debug_done
, req
);
458 run_ctx
->debug_running
= true;
464 static void run_debug_done(struct tevent_req
*subreq
)
466 struct tevent_req
*req
= tevent_req_callback_data(
467 subreq
, struct tevent_req
);
468 struct run_debug_state
*state
= tevent_req_data(
469 req
, struct run_debug_state
);
474 state
->run_ctx
->debug_running
= false;
476 status
= run_proc_recv(subreq
, &ret
, NULL
, NULL
, state
, &output
);
479 D_ERR("Running debug failed, ret=%d\n", ret
);
483 if (output
!= NULL
) {
484 debug_log(DEBUG_ERR
, output
, "event_debug");
488 kill(-state
->pid
, SIGTERM
);
489 tevent_req_done(req
);
492 static bool run_debug_recv(struct tevent_req
*req
, int *perr
)
496 if (tevent_req_is_unix_error(req
, &ret
)) {
510 struct run_event_state
{
511 struct tevent_context
*ev
;
512 struct run_event_context
*run_ctx
;
513 const char *event_str
;
515 struct timeval timeout
;
516 bool continue_on_failure
;
518 struct run_event_script_list
*script_list
;
520 struct tevent_req
*script_subreq
;
525 static void run_event_cancel(struct tevent_req
*req
);
526 static void run_event_trigger(struct tevent_req
*req
, void *private_data
);
527 static struct tevent_req
*run_event_run_script(struct tevent_req
*req
);
528 static void run_event_next_script(struct tevent_req
*subreq
);
529 static void run_event_debug(struct tevent_req
*req
, pid_t pid
);
530 static void run_event_debug_done(struct tevent_req
*subreq
);
532 struct tevent_req
*run_event_send(TALLOC_CTX
*mem_ctx
,
533 struct tevent_context
*ev
,
534 struct run_event_context
*run_ctx
,
535 const char *event_str
,
537 struct timeval timeout
,
538 bool continue_on_failure
)
540 struct tevent_req
*req
, *current_req
;
541 struct run_event_state
*state
;
542 bool monitor_running
, status
;
544 req
= tevent_req_create(mem_ctx
, &state
, struct run_event_state
);
550 state
->run_ctx
= run_ctx
;
551 state
->event_str
= talloc_strdup(state
, event_str
);
552 if (tevent_req_nomem(state
->event_str
, req
)) {
553 return tevent_req_post(req
, ev
);
555 if (arg_str
!= NULL
) {
556 state
->arg_str
= talloc_strdup(state
, arg_str
);
557 if (tevent_req_nomem(state
->arg_str
, req
)) {
558 return tevent_req_post(req
, ev
);
561 state
->timeout
= timeout
;
562 state
->continue_on_failure
= continue_on_failure
;
563 state
->cancelled
= false;
565 state
->script_list
= talloc_zero(state
, struct run_event_script_list
);
566 if (tevent_req_nomem(state
->script_list
, req
)) {
567 return tevent_req_post(req
, ev
);
571 * If monitor event is running,
572 * cancel the running monitor event and run new event
574 * If any other event is running,
575 * if new event is monitor, cancel that event
576 * else add new event to the queue
579 current_req
= run_event_get_running(run_ctx
, &monitor_running
);
580 if (current_req
!= NULL
) {
581 if (monitor_running
) {
582 run_event_cancel(current_req
);
583 } else if (strcmp(event_str
, "monitor") == 0) {
584 state
->script_list
->summary
= -ECANCELED
;
585 tevent_req_done(req
);
586 return tevent_req_post(req
, ev
);
590 status
= tevent_queue_add(run_event_queue(run_ctx
), ev
, req
,
591 run_event_trigger
, NULL
);
593 tevent_req_error(req
, ENOMEM
);
594 return tevent_req_post(req
, ev
);
600 static void run_event_cancel(struct tevent_req
*req
)
602 struct run_event_state
*state
= tevent_req_data(
603 req
, struct run_event_state
);
605 run_event_stop_running(state
->run_ctx
);
607 state
->script_list
->summary
= -ECANCELED
;
608 state
->cancelled
= true;
610 TALLOC_FREE(state
->script_subreq
);
612 tevent_req_done(req
);
615 static void run_event_trigger(struct tevent_req
*req
, void *private_data
)
617 struct tevent_req
*subreq
;
618 struct run_event_state
*state
= tevent_req_data(
619 req
, struct run_event_state
);
620 struct run_event_script_list
*script_list
;
622 bool is_monitor
= false;
624 D_DEBUG("Running event %s with args \"%s\"\n", state
->event_str
,
625 state
->arg_str
== NULL
? "(null)" : state
->arg_str
);
627 ret
= get_script_list(state
,
628 run_event_script_dir(state
->run_ctx
),
631 D_ERR("get_script_list() failed, ret=%d\n", ret
);
632 tevent_req_error(req
, ret
);
637 if (script_list
== NULL
|| script_list
->num_scripts
== 0) {
638 tevent_req_done(req
);
642 talloc_free(state
->script_list
);
643 state
->script_list
= script_list
;
645 ret
= script_args(state
, state
->event_str
, state
->arg_str
,
648 D_ERR("script_args() failed, ret=%d\n", ret
);
649 tevent_req_error(req
, ret
);
655 subreq
= run_event_run_script(req
);
656 if (tevent_req_nomem(subreq
, req
)) {
659 tevent_req_set_callback(subreq
, run_event_next_script
, req
);
661 state
->script_subreq
= subreq
;
663 if (strcmp(state
->event_str
, "monitor") == 0) {
666 run_event_start_running(state
->run_ctx
, req
, is_monitor
);
669 static struct tevent_req
*run_event_run_script(struct tevent_req
*req
)
671 struct run_event_state
*state
= tevent_req_data(
672 req
, struct run_event_state
);
673 struct run_event_script
*script
;
674 struct tevent_req
*subreq
;
677 script
= &state
->script_list
->script
[state
->index
];
679 path
= talloc_asprintf(state
, "%s/%s.script",
680 run_event_script_dir(state
->run_ctx
),
686 state
->argv
[0] = script
->name
;
687 script
->begin
= tevent_timeval_current();
689 D_DEBUG("Running %s with args \"%s %s\"\n",
690 path
, state
->argv
[0], state
->argv
[1]);
692 subreq
= run_proc_send(state
, state
->ev
,
693 run_event_run_proc_context(state
->run_ctx
),
694 path
, state
->argv
, -1, state
->timeout
);
701 static void run_event_next_script(struct tevent_req
*subreq
)
703 struct tevent_req
*req
= tevent_req_callback_data(
704 subreq
, struct tevent_req
);
705 struct run_event_state
*state
= tevent_req_data(
706 req
, struct run_event_state
);
707 struct run_event_script
*script
;
712 script
= &state
->script_list
->script
[state
->index
];
713 script
->end
= tevent_timeval_current();
715 status
= run_proc_recv(subreq
, &ret
, &script
->result
, &pid
,
716 state
->script_list
, &script
->output
);
718 state
->script_subreq
= NULL
;
720 D_ERR("run_proc failed for %s, ret=%d\n", script
->name
, ret
);
721 run_event_stop_running(state
->run_ctx
);
722 tevent_req_error(req
, ret
);
726 if (state
->cancelled
) {
731 if (script
->output
!= NULL
) {
732 debug_log(DEBUG_ERR
, script
->output
, script
->name
);
735 D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n",
736 script
->name
, script
->result
.sig
, script
->result
.err
,
737 script
->result
.status
);
740 /* If a script fails, stop running */
741 script
->summary
= run_event_script_status(script
);
742 if (script
->summary
!= 0 && script
->summary
!= -ENOEXEC
) {
743 state
->script_list
->summary
= script
->summary
;
745 if (! state
->continue_on_failure
) {
746 state
->script_list
->num_scripts
= state
->index
+ 1;
748 if (script
->summary
== -ETIMEDOUT
&& pid
!= -1) {
749 run_event_debug(req
, pid
);
751 D_NOTICE("%s event %s\n", state
->event_str
,
752 (script
->summary
== -ETIMEDOUT
) ?
755 run_event_stop_running(state
->run_ctx
);
756 tevent_req_done(req
);
763 /* All scripts executed */
764 if (state
->index
>= state
->script_list
->num_scripts
) {
765 run_event_stop_running(state
->run_ctx
);
766 tevent_req_done(req
);
770 subreq
= run_event_run_script(req
);
771 if (tevent_req_nomem(subreq
, req
)) {
774 tevent_req_set_callback(subreq
, run_event_next_script
, req
);
776 state
->script_subreq
= subreq
;
779 static void run_event_debug(struct tevent_req
*req
, pid_t pid
)
781 struct run_event_state
*state
= tevent_req_data(
782 req
, struct run_event_state
);
783 struct tevent_req
*subreq
;
785 /* Debug script is run with ectx as the memory context */
786 subreq
= run_debug_send(state
->run_ctx
, state
->ev
, state
->run_ctx
,
787 state
->event_str
, pid
);
788 if (subreq
== NULL
) {
789 /* If run debug fails, it's not an error */
790 D_NOTICE("Failed to run event debug\n");
793 tevent_req_set_callback(subreq
, run_event_debug_done
, NULL
);
796 static void run_event_debug_done(struct tevent_req
*subreq
)
801 status
= run_debug_recv(subreq
, &ret
);
804 D_NOTICE("run_debug() failed, ret=%d\n", ret
);
808 bool run_event_recv(struct tevent_req
*req
, int *perr
,
810 struct run_event_script_list
**script_list
)
812 struct run_event_state
*state
= tevent_req_data(
813 req
, struct run_event_state
);
816 if (tevent_req_is_unix_error(req
, &ret
)) {
823 if (script_list
!= NULL
) {
824 *script_list
= talloc_steal(mem_ctx
, state
->script_list
);