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
);