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/locale.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
)
42 size_t namelen
= strlen(de
->d_name
);
50 /* Skip filenames with ~ */
51 ptr
= strchr(de
->d_name
, '~');
56 /* Filename should start with [0-9][0-9]. */
57 if ((! isdigit(de
->d_name
[0])) ||
58 (! isdigit(de
->d_name
[1])) ||
59 (de
->d_name
[2] != '.')) {
66 static int get_script_list(TALLOC_CTX
*mem_ctx
,
67 const char *script_dir
,
68 struct run_event_script_list
**out
)
70 struct dirent
**namelist
= NULL
;
71 struct run_event_script_list
*script_list
;
75 count
= scandir(script_dir
, &namelist
, script_filter
, alphasort
);
79 D_WARNING("event script dir %s removed\n", script_dir
);
81 D_WARNING("scandir() failed on %s, ret=%d\n",
95 script_list
= talloc_zero(mem_ctx
, struct run_event_script_list
);
96 if (script_list
== NULL
) {
100 script_list
->num_scripts
= count
;
101 script_list
->script
= talloc_zero_array(script_list
,
102 struct run_event_script
,
104 if (script_list
->script
== NULL
) {
106 talloc_free(script_list
);
110 for (i
=0; i
<count
; i
++) {
111 struct run_event_script
*s
= &script_list
->script
[i
];
113 s
->name
= talloc_strdup(script_list
, namelist
[i
]->d_name
);
114 if (s
->name
== NULL
) {
116 talloc_free(script_list
);
125 if (namelist
!= NULL
&& count
!= -1) {
126 for (i
=0; i
<count
; i
++) {
134 static int script_chmod(TALLOC_CTX
*mem_ctx
, const char *script_dir
,
135 const char *script_name
, bool enable
)
145 dirp
= opendir(script_dir
);
151 while ((de
= readdir(dirp
)) != NULL
) {
152 if (strcmp(de
->d_name
, script_name
) == 0) {
154 /* check for valid script names */
155 ret
= script_filter(de
);
171 filename
= talloc_asprintf(mem_ctx
, "%s/%s", script_dir
, script_name
);
172 if (filename
== NULL
) {
176 fd
= open(filename
, O_RDWR
);
182 ret
= fstat(fd
, &st
);
189 new_mode
= st
.st_mode
| (S_IXUSR
| S_IXGRP
| S_IXOTH
);
191 new_mode
= st
.st_mode
& ~(S_IXUSR
| S_IXGRP
| S_IXOTH
);
194 ret
= fchmod(fd
, new_mode
);
204 talloc_free(filename
);
208 static int script_args(TALLOC_CTX
*mem_ctx
, const char *event_str
,
209 const char *arg_str
, const char ***out
)
215 /* Preallocate argv array to avoid reallocation. */
217 argv
= talloc_array(mem_ctx
, const char *, len
);
222 argv
[0] = NULL
; /* script name */
226 if (arg_str
!= NULL
) {
229 str
= talloc_strdup(argv
, arg_str
);
235 while ((tok
= strtok(t
, " ")) != NULL
) {
236 argv
[argc
] = talloc_strdup(argv
, tok
);
237 if (argv
[argc
] == NULL
) {
243 argv
= talloc_realloc(mem_ctx
, argv
,
244 const char *, len
+ 8);
263 struct run_event_context
{
264 struct run_proc_context
*run_proc_ctx
;
265 const char *script_dir
;
266 const char *debug_prog
;
271 int run_event_init(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
272 const char *script_dir
, const char *debug_prog
,
273 struct run_event_context
**out
)
275 struct run_event_context
*run_ctx
;
279 run_ctx
= talloc_zero(mem_ctx
, struct run_event_context
);
280 if (run_ctx
== NULL
) {
284 ret
= run_proc_init(run_ctx
, ev
, &run_ctx
->run_proc_ctx
);
286 talloc_free(run_ctx
);
290 ret
= stat(script_dir
, &st
);
293 talloc_free(run_ctx
);
297 if (! S_ISDIR(st
.st_mode
)) {
298 talloc_free(run_ctx
);
302 run_ctx
->script_dir
= talloc_strdup(run_ctx
, script_dir
);
303 if (run_ctx
->script_dir
== NULL
) {
304 talloc_free(run_ctx
);
308 if (debug_prog
!= NULL
) {
309 run_ctx
->debug_prog
= talloc_strdup(run_ctx
, debug_prog
);
310 if (run_ctx
->debug_prog
== NULL
) {
311 talloc_free(run_ctx
);
316 run_ctx
->debug_running
= false;
322 static struct run_proc_context
*
323 run_event_run_proc_context(struct run_event_context
*run_ctx
)
325 return run_ctx
->run_proc_ctx
;
328 static const char *run_event_script_dir(struct run_event_context
*run_ctx
)
330 return run_ctx
->script_dir
;
333 static const char *run_event_debug_prog(struct run_event_context
*run_ctx
)
335 return run_ctx
->debug_prog
;
338 static int run_event_script_status(struct run_event_script
*script
)
342 if (script
->result
.sig
> 0) {
344 } else if (script
->result
.err
> 0) {
345 if (script
->result
.err
== EACCES
) {
346 /* Map EACCESS to ENOEXEC */
349 ret
= -script
->result
.err
;
352 ret
= script
->result
.status
;
358 int run_event_script_list(struct run_event_context
*run_ctx
,
360 struct run_event_script_list
**output
)
362 struct run_event_script_list
*script_list
;
365 ret
= get_script_list(mem_ctx
, run_event_script_dir(run_ctx
),
371 if (script_list
== NULL
) {
376 for (i
=0; i
<script_list
->num_scripts
; i
++) {
377 struct run_event_script
*script
= &script_list
->script
[i
];
381 path
= talloc_asprintf(mem_ctx
, "%s/%s",
382 run_event_script_dir(run_ctx
),
388 ret
= stat(path
, &st
);
394 if (! (st
.st_mode
& S_IXUSR
)) {
395 script
->summary
= -ENOEXEC
;
401 *output
= script_list
;
405 int run_event_script_enable(struct run_event_context
*run_ctx
,
406 const char *script_name
)
408 return script_chmod(run_ctx
, run_event_script_dir(run_ctx
),
412 int run_event_script_disable(struct run_event_context
*run_ctx
,
413 const char *script_name
)
415 return script_chmod(run_ctx
, run_event_script_dir(run_ctx
),
420 * Run debug program to diagnose hung scripts
423 static int debug_args(TALLOC_CTX
*mem_ctx
, const char *path
,
424 const char *event_str
, pid_t pid
, const char ***out
)
428 argv
= talloc_array(mem_ctx
, const char *, 4);
434 argv
[1] = talloc_asprintf(argv
, "%d", pid
);
436 if (argv
[1] == NULL
) {
446 static void debug_log(int loglevel
, const char *output
, const char *log_prefix
)
452 DEBUG(loglevel
, ("%s: %s\n", log_prefix
, output
));
456 line
= strtok(s
, "\n");
457 while (line
!= NULL
) {
458 DEBUG(loglevel
, ("%s: %s\n", log_prefix
, line
));
459 line
= strtok(NULL
, "\n");
464 struct run_debug_state
{
465 struct run_event_context
*run_ctx
;
469 static void run_debug_done(struct tevent_req
*subreq
);
471 static struct tevent_req
*run_debug_send(TALLOC_CTX
*mem_ctx
,
472 struct tevent_context
*ev
,
473 struct run_event_context
*run_ctx
,
474 const char *event_str
, pid_t pid
)
476 struct tevent_req
*req
, *subreq
;
477 struct run_debug_state
*state
;
479 const char *debug_prog
;
482 req
= tevent_req_create(mem_ctx
, &state
, struct run_debug_state
);
487 state
->run_ctx
= run_ctx
;
490 debug_prog
= run_event_debug_prog(run_ctx
);
491 if (debug_prog
== NULL
) {
492 tevent_req_done(req
);
493 return tevent_req_post(req
, ev
);
496 if (run_ctx
->debug_running
) {
497 tevent_req_done(req
);
498 return tevent_req_post(req
, ev
);
502 D_DEBUG("Event script terminated, nothing to debug\n");
503 tevent_req_done(req
);
504 return tevent_req_post(req
, ev
);
507 ret
= debug_args(state
, debug_prog
, event_str
, pid
, &argv
);
509 D_ERR("debug_args() failed\n");
510 tevent_req_error(req
, ret
);
511 return tevent_req_post(req
, ev
);
514 D_DEBUG("Running debug %s with args \"%s %s\"\n",
515 debug_prog
, argv
[1], argv
[2]);
517 subreq
= run_proc_send(state
, ev
, run_event_run_proc_context(run_ctx
),
518 debug_prog
, argv
, -1, tevent_timeval_zero());
519 if (tevent_req_nomem(subreq
, req
)) {
520 return tevent_req_post(req
, ev
);
522 tevent_req_set_callback(subreq
, run_debug_done
, req
);
524 run_ctx
->debug_running
= true;
530 static void run_debug_done(struct tevent_req
*subreq
)
532 struct tevent_req
*req
= tevent_req_callback_data(
533 subreq
, struct tevent_req
);
534 struct run_debug_state
*state
= tevent_req_data(
535 req
, struct run_debug_state
);
540 state
->run_ctx
->debug_running
= false;
542 status
= run_proc_recv(subreq
, &ret
, NULL
, NULL
, state
, &output
);
545 D_ERR("Running debug failed, ret=%d\n", ret
);
549 if (output
!= NULL
) {
550 debug_log(DEBUG_ERR
, output
, "event_debug");
554 kill(-state
->pid
, SIGTERM
);
555 tevent_req_done(req
);
558 static bool run_debug_recv(struct tevent_req
*req
, int *perr
)
562 if (tevent_req_is_unix_error(req
, &ret
)) {
576 struct run_event_state
{
577 struct tevent_context
*ev
;
578 struct run_event_context
*run_ctx
;
579 const char *event_str
;
580 struct timeval timeout
;
582 struct run_event_script_list
*script_list
;
588 static struct tevent_req
*run_event_run_script(struct tevent_req
*req
);
589 static void run_event_next_script(struct tevent_req
*subreq
);
590 static void run_event_debug(struct tevent_req
*req
, pid_t pid
);
591 static void run_event_debug_done(struct tevent_req
*subreq
);
593 struct tevent_req
*run_event_send(TALLOC_CTX
*mem_ctx
,
594 struct tevent_context
*ev
,
595 struct run_event_context
*run_ctx
,
596 const char *event_str
,
598 struct timeval timeout
)
600 struct tevent_req
*req
, *subreq
;
601 struct run_event_state
*state
;
604 req
= tevent_req_create(mem_ctx
, &state
, struct run_event_state
);
610 state
->run_ctx
= run_ctx
;
611 state
->event_str
= talloc_strdup(state
, event_str
);
612 if (tevent_req_nomem(state
->event_str
, req
)) {
613 return tevent_req_post(req
, ev
);
615 state
->timeout
= timeout
;
617 ret
= get_script_list(state
, run_event_script_dir(run_ctx
),
618 &state
->script_list
);
620 D_ERR("get_script_list() failed, ret=%d\n", ret
);
621 tevent_req_error(req
, ret
);
622 return tevent_req_post(req
, ev
);
626 if (state
->script_list
== NULL
||
627 state
->script_list
->num_scripts
== 0) {
628 tevent_req_done(req
);
629 return tevent_req_post(req
, ev
);
632 ret
= script_args(state
, event_str
, arg_str
, &state
->argv
);
634 D_ERR("script_args() failed, ret=%d\n", ret
);
635 tevent_req_error(req
, ret
);
636 return tevent_req_post(req
, ev
);
641 subreq
= run_event_run_script(req
);
642 if (tevent_req_nomem(subreq
, req
)) {
643 return tevent_req_post(req
, ev
);
645 tevent_req_set_callback(subreq
, run_event_next_script
, req
);
650 static struct tevent_req
*run_event_run_script(struct tevent_req
*req
)
652 struct run_event_state
*state
= tevent_req_data(
653 req
, struct run_event_state
);
654 struct run_event_script
*script
;
655 struct tevent_req
*subreq
;
658 script
= &state
->script_list
->script
[state
->index
];
660 path
= talloc_asprintf(state
, "%s/%s",
661 run_event_script_dir(state
->run_ctx
),
667 state
->argv
[0] = script
->name
;
668 script
->begin
= tevent_timeval_current();
670 D_DEBUG("Running %s with args \"%s %s\"\n",
671 path
, state
->argv
[0], state
->argv
[1]);
673 subreq
= run_proc_send(state
, state
->ev
,
674 run_event_run_proc_context(state
->run_ctx
),
675 path
, state
->argv
, -1, state
->timeout
);
682 static void run_event_next_script(struct tevent_req
*subreq
)
684 struct tevent_req
*req
= tevent_req_callback_data(
685 subreq
, struct tevent_req
);
686 struct run_event_state
*state
= tevent_req_data(
687 req
, struct run_event_state
);
688 struct run_event_script
*script
;
693 script
= &state
->script_list
->script
[state
->index
];
694 script
->end
= tevent_timeval_current();
696 status
= run_proc_recv(subreq
, &ret
, &script
->result
, &pid
,
697 state
->script_list
, &script
->output
);
700 D_ERR("run_proc failed for %s, ret=%d\n", script
->name
, ret
);
701 tevent_req_error(req
, ret
);
706 if (script
->output
!= NULL
) {
707 debug_log(DEBUG_ERR
, script
->output
, script
->name
);
710 D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n",
711 script
->name
, script
->result
.sig
, script
->result
.err
,
712 script
->result
.status
);
715 /* If a script fails, stop running */
716 script
->summary
= run_event_script_status(script
);
717 if (script
->summary
!= 0 && script
->summary
!= -ENOEXEC
) {
718 state
->script_list
->num_scripts
= state
->index
+ 1;
720 if (script
->summary
== -ETIME
&& pid
!= -1) {
721 run_event_debug(req
, pid
);
724 state
->script_list
->summary
= script
->summary
;
725 D_NOTICE("%s event %s\n", state
->event_str
,
726 (script
->summary
== -ETIME
) ? "timed out" : "failed");
728 tevent_req_done(req
);
734 /* All scripts executed */
735 if (state
->index
>= state
->script_list
->num_scripts
) {
736 tevent_req_done(req
);
740 subreq
= run_event_run_script(req
);
741 if (tevent_req_nomem(subreq
, req
)) {
744 tevent_req_set_callback(subreq
, run_event_next_script
, req
);
747 static void run_event_debug(struct tevent_req
*req
, pid_t pid
)
749 struct run_event_state
*state
= tevent_req_data(
750 req
, struct run_event_state
);
751 struct tevent_req
*subreq
;
753 /* Debug script is run with ectx as the memory context */
754 subreq
= run_debug_send(state
->run_ctx
, state
->ev
, state
->run_ctx
,
755 state
->event_str
, pid
);
756 if (subreq
== NULL
) {
757 /* If run debug fails, it's not an error */
758 D_NOTICE("Failed to run event debug\n");
761 tevent_req_set_callback(subreq
, run_event_debug_done
, NULL
);
764 static void run_event_debug_done(struct tevent_req
*subreq
)
769 status
= run_debug_recv(subreq
, &ret
);
772 D_NOTICE("run_debug() failed, ret=%d\n", ret
);
776 bool run_event_recv(struct tevent_req
*req
, int *perr
,
778 struct run_event_script_list
**script_list
)
780 struct run_event_state
*state
= tevent_req_data(
781 req
, struct run_event_state
);
784 if (tevent_req_is_unix_error(req
, &ret
)) {
791 if (script_list
!= NULL
) {
792 *script_list
= talloc_steal(mem_ctx
, state
->script_list
);