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] != '.')) {
63 /* Ignore filenames with multiple '.'s */
64 ptr = index(&de->d_name[3], '.');
72 static int get_script_list(TALLOC_CTX *mem_ctx,
73 const char *script_dir,
74 struct run_event_script_list **out)
76 struct dirent **namelist = NULL;
77 struct run_event_script_list *script_list;
81 count = scandir(script_dir, &namelist, script_filter, alphasort);
85 D_WARNING("event script dir %s removed\n", script_dir);
87 D_WARNING("scandir() failed on %s, ret=%d\n",
101 script_list = talloc_zero(mem_ctx, struct run_event_script_list);
102 if (script_list == NULL) {
106 script_list->num_scripts = count;
107 script_list->script = talloc_zero_array(script_list,
108 struct run_event_script,
110 if (script_list->script == NULL) {
112 talloc_free(script_list);
116 for (i=0; i<count; i++) {
117 struct run_event_script *s = &script_list->script[i];
119 s->name = talloc_strdup(script_list, namelist[i]->d_name);
120 if (s->name == NULL) {
122 talloc_free(script_list);
131 if (namelist != NULL && count != -1) {
132 for (i=0; i<count; i++) {
140 static int script_chmod(TALLOC_CTX *mem_ctx, const char *script_dir,
141 const char *script_name, bool enable)
151 dirp = opendir(script_dir);
157 while ((de = readdir(dirp)) != NULL) {
158 if (strcmp(de->d_name, script_name) == 0) {
160 /* check for valid script names */
161 ret = script_filter(de);
177 filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_name);
178 if (filename == NULL) {
182 fd = open(filename, O_RDWR);
188 ret = fstat(fd, &st);
195 new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH);
197 new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
200 ret = fchmod(fd, new_mode);
210 talloc_free(filename);
214 static int script_args(TALLOC_CTX *mem_ctx, const char *event_str,
215 const char *arg_str, const char ***out)
221 /* Preallocate argv array to avoid reallocation. */
223 argv = talloc_array(mem_ctx, const char *, len);
228 argv[0] = NULL; /* script name */
232 if (arg_str != NULL) {
235 str = talloc_strdup(argv, arg_str);
241 while ((tok = strtok(t, " ")) != NULL) {
242 argv[argc] = talloc_strdup(argv, tok);
243 if (argv[argc] == NULL) {
249 argv = talloc_realloc(mem_ctx, argv,
250 const char *, len + 8);
269 struct run_event_context {
270 struct run_proc_context *run_proc_ctx;
271 const char *script_dir;
272 const char *debug_prog;
277 int run_event_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
278 const char *script_dir, const char *debug_prog,
279 struct run_event_context **out)
281 struct run_event_context *run_ctx;
285 run_ctx = talloc_zero(mem_ctx, struct run_event_context);
286 if (run_ctx == NULL) {
290 ret = run_proc_init(run_ctx, ev, &run_ctx->run_proc_ctx);
292 talloc_free(run_ctx);
296 ret = stat(script_dir, &st);
299 talloc_free(run_ctx);
303 if (! S_ISDIR(st.st_mode)) {
304 talloc_free(run_ctx);
308 run_ctx->script_dir = talloc_strdup(run_ctx, script_dir);
309 if (run_ctx->script_dir == NULL) {
310 talloc_free(run_ctx);
314 if (debug_prog != NULL) {
315 run_ctx->debug_prog = talloc_strdup(run_ctx, debug_prog);
316 if (run_ctx->debug_prog == NULL) {
317 talloc_free(run_ctx);
322 run_ctx->debug_running = false;
328 static struct run_proc_context *
329 run_event_run_proc_context(struct run_event_context *run_ctx)
331 return run_ctx->run_proc_ctx;
334 static const char *run_event_script_dir(struct run_event_context *run_ctx)
336 return run_ctx->script_dir;
339 static const char *run_event_debug_prog(struct run_event_context *run_ctx)
341 return run_ctx->debug_prog;
344 static int run_event_script_status(struct run_event_script *script)
348 if (script->result.sig > 0) {
350 } else if (script->result.err > 0) {
351 if (script->result.err == EACCES) {
352 /* Map EACCESS to ENOEXEC */
355 ret = -script->result.err;
358 ret = script->result.status;
364 int run_event_script_list(struct run_event_context *run_ctx,
366 struct run_event_script_list **output)
368 struct run_event_script_list *script_list;
371 ret = get_script_list(mem_ctx, run_event_script_dir(run_ctx),
377 if (script_list == NULL) {
382 for (i=0; i<script_list->num_scripts; i++) {
383 struct run_event_script *script = &script_list->script[i];
387 path = talloc_asprintf(mem_ctx, "%s/%s",
388 run_event_script_dir(run_ctx),
394 ret = stat(path, &st);
400 if (! (st.st_mode & S_IXUSR)) {
401 script->summary = -ENOEXEC;
407 *output = script_list;
411 int run_event_script_enable(struct run_event_context *run_ctx,
412 const char *script_name)
414 return script_chmod(run_ctx, run_event_script_dir(run_ctx),
418 int run_event_script_disable(struct run_event_context *run_ctx,
419 const char *script_name)
421 return script_chmod(run_ctx, run_event_script_dir(run_ctx),
426 * Run debug program to diagnose hung scripts
429 static int debug_args(TALLOC_CTX *mem_ctx, const char *path,
430 const char *event_str, pid_t pid, const char ***out)
434 argv = talloc_array(mem_ctx, const char *, 4);
440 argv[1] = talloc_asprintf(argv, "%d", pid);
442 if (argv[1] == NULL) {
452 static void debug_log(int loglevel, const char *output, const char *log_prefix)
458 DEBUG(loglevel, ("%s: %s\n", log_prefix, output));
462 line = strtok(s, "\n");
463 while (line != NULL) {
464 DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
465 line = strtok(NULL, "\n");
470 struct run_debug_state {
471 struct run_event_context *run_ctx;
475 static void run_debug_done(struct tevent_req *subreq);
477 static struct tevent_req *run_debug_send(TALLOC_CTX *mem_ctx,
478 struct tevent_context *ev,
479 struct run_event_context *run_ctx,
480 const char *event_str, pid_t pid)
482 struct tevent_req *req, *subreq;
483 struct run_debug_state *state;
485 const char *debug_prog;
488 req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
493 state->run_ctx = run_ctx;
496 debug_prog = run_event_debug_prog(run_ctx);
497 if (debug_prog == NULL) {
498 tevent_req_done(req);
499 return tevent_req_post(req, ev);
502 if (run_ctx->debug_running) {
503 tevent_req_done(req);
504 return tevent_req_post(req, ev);
508 D_DEBUG("Event script terminated, nothing to debug\n");
509 tevent_req_done(req);
510 return tevent_req_post(req, ev);
513 ret = debug_args(state, debug_prog, event_str, pid, &argv);
515 D_ERR("debug_args() failed\n");
516 tevent_req_error(req, ret);
517 return tevent_req_post(req, ev);
520 D_DEBUG("Running debug %s with args \"%s %s\"\n",
521 debug_prog, argv[1], argv[2]);
523 subreq = run_proc_send(state, ev, run_event_run_proc_context(run_ctx),
524 debug_prog, argv, -1, tevent_timeval_zero());
525 if (tevent_req_nomem(subreq, req)) {
526 return tevent_req_post(req, ev);
528 tevent_req_set_callback(subreq, run_debug_done, req);
530 run_ctx->debug_running = true;
536 static void run_debug_done(struct tevent_req *subreq)
538 struct tevent_req *req = tevent_req_callback_data(
539 subreq, struct tevent_req);
540 struct run_debug_state *state = tevent_req_data(
541 req, struct run_debug_state);
546 state->run_ctx->debug_running = false;
548 status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output);
551 D_ERR("Running debug failed, ret=%d\n", ret);
555 if (output != NULL) {
556 debug_log(DEBUG_ERR, output, "event_debug");
560 kill(-state->pid, SIGTERM);
561 tevent_req_done(req);
564 static bool run_debug_recv(struct tevent_req *req, int *perr)
568 if (tevent_req_is_unix_error(req, &ret)) {
582 struct run_event_state {
583 struct tevent_context *ev;
584 struct run_event_context *run_ctx;
585 const char *event_str;
586 struct timeval timeout;
588 struct run_event_script_list *script_list;
594 static struct tevent_req *run_event_run_script(struct tevent_req *req);
595 static void run_event_next_script(struct tevent_req *subreq);
596 static void run_event_debug(struct tevent_req *req, pid_t pid);
597 static void run_event_debug_done(struct tevent_req *subreq);
599 struct tevent_req *run_event_send(TALLOC_CTX *mem_ctx,
600 struct tevent_context *ev,
601 struct run_event_context *run_ctx,
602 const char *event_str,
604 struct timeval timeout)
606 struct tevent_req *req, *subreq;
607 struct run_event_state *state;
610 req = tevent_req_create(mem_ctx, &state, struct run_event_state);
616 state->run_ctx = run_ctx;
617 state->event_str = talloc_strdup(state, event_str);
618 if (tevent_req_nomem(state->event_str, req)) {
619 return tevent_req_post(req, ev);
621 state->timeout = timeout;
623 ret = get_script_list(state, run_event_script_dir(run_ctx),
624 &state->script_list);
626 D_ERR("get_script_list() failed, ret=%d\n", ret);
627 tevent_req_error(req, ret);
628 return tevent_req_post(req, ev);
632 if (state->script_list == NULL ||
633 state->script_list->num_scripts == 0) {
634 tevent_req_done(req);
635 return tevent_req_post(req, ev);
638 ret = script_args(state, event_str, arg_str, &state->argv);
640 D_ERR("script_args() failed, ret=%d\n", ret);
641 tevent_req_error(req, ret);
642 return tevent_req_post(req, ev);
647 subreq = run_event_run_script(req);
648 if (tevent_req_nomem(subreq, req)) {
649 return tevent_req_post(req, ev);
651 tevent_req_set_callback(subreq, run_event_next_script, req);
656 static struct tevent_req *run_event_run_script(struct tevent_req *req)
658 struct run_event_state *state = tevent_req_data(
659 req, struct run_event_state);
660 struct run_event_script *script;
661 struct tevent_req *subreq;
664 script = &state->script_list->script[state->index];
666 path = talloc_asprintf(state, "%s/%s",
667 run_event_script_dir(state->run_ctx),
673 state->argv[0] = script->name;
674 script->begin = tevent_timeval_current();
676 D_DEBUG("Running %s with args \"%s %s\"\n",
677 path, state->argv[0], state->argv[1]);
679 subreq = run_proc_send(state, state->ev,
680 run_event_run_proc_context(state->run_ctx),
681 path, state->argv, -1, state->timeout);
688 static void run_event_next_script(struct tevent_req *subreq)
690 struct tevent_req *req = tevent_req_callback_data(
691 subreq, struct tevent_req);
692 struct run_event_state *state = tevent_req_data(
693 req, struct run_event_state);
694 struct run_event_script *script;
699 script = &state->script_list->script[state->index];
700 script->end = tevent_timeval_current();
702 status = run_proc_recv(subreq, &ret, &script->result, &pid,
703 state->script_list, &script->output);
706 D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret);
707 tevent_req_error(req, ret);
712 if (script->output != NULL) {
713 debug_log(DEBUG_ERR, script->output, script->name);
716 D_DEBUG("Script %s finished sig=%d, err=%d, status=%d\n",
717 script->name, script->result.sig, script->result.err,
718 script->result.status);
721 /* If a script fails, stop running */
722 script->summary = run_event_script_status(script);
723 if (script->summary != 0 && script->summary != -ENOEXEC) {
724 state->script_list->num_scripts = state->index + 1;
726 if (script->summary == -ETIME && pid != -1) {
727 run_event_debug(req, pid);
730 state->script_list->summary = script->summary;
731 D_NOTICE("%s event %s\n", state->event_str,
732 (script->summary == -ETIME) ? "timed out" : "failed");
734 tevent_req_done(req);
740 /* All scripts executed */
741 if (state->index >= state->script_list->num_scripts) {
742 tevent_req_done(req);
746 subreq = run_event_run_script(req);
747 if (tevent_req_nomem(subreq, req)) {
750 tevent_req_set_callback(subreq, run_event_next_script, req);
753 static void run_event_debug(struct tevent_req *req, pid_t pid)
755 struct run_event_state *state = tevent_req_data(
756 req, struct run_event_state);
757 struct tevent_req *subreq;
759 /* Debug script is run with ectx as the memory context */
760 subreq = run_debug_send(state->run_ctx, state->ev, state->run_ctx,
761 state->event_str, pid);
762 if (subreq == NULL) {
763 /* If run debug fails, it's not an error */
764 D_NOTICE("Failed to run event debug\n");
767 tevent_req_set_callback(subreq, run_event_debug_done, NULL);
770 static void run_event_debug_done(struct tevent_req *subreq)
775 status = run_debug_recv(subreq, &ret);
778 D_NOTICE("run_debug() failed, ret=%d\n", ret);
782 bool run_event_recv(struct tevent_req *req, int *perr,
784 struct run_event_script_list **script_list)
786 struct run_event_state *state = tevent_req_data(
787 req, struct run_event_state);
790 if (tevent_req_is_unix_error(req, &ret)) {
797 if (script_list != NULL) {
798 *script_list = talloc_steal(mem_ctx, state->script_list);