auth/gensec: add some useful debugging to gensec_update_send/gensec_update_done
[Samba.git] / ctdb / common / run_event.c
blobe5d562c96b3926600efc0cf3b0e4d370c87a5c55
1 /*
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/>.
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/dir.h"
23 #include "system/locale.h"
24 #include "system/wait.h"
26 #include <talloc.h>
27 #include <tevent.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"
37 * Utility functions
40 static int script_filter(const struct dirent *de)
42 size_t namelen = strlen(de->d_name);
43 char *ptr;
45 /* Ignore . and .. */
46 if (namelen < 3) {
47 return 0;
50 /* Skip filenames with ~ */
51 ptr = strchr(de->d_name, '~');
52 if (ptr != NULL) {
53 return 0;
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] != '.')) {
60 return 0;
63 return 1;
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;
72 int count, ret;
73 int i;
75 count = scandir(script_dir, &namelist, script_filter, alphasort);
76 if (count == -1) {
77 ret = errno;
78 if (ret == ENOENT) {
79 D_WARNING("event script dir %s removed\n", script_dir);
80 } else {
81 D_WARNING("scandir() failed on %s, ret=%d\n",
82 script_dir, ret);
84 *out = NULL;
85 ret = 0;
86 goto done;
89 if (count == 0) {
90 *out = NULL;
91 ret = 0;
92 goto done;
95 script_list = talloc_zero(mem_ctx, struct run_event_script_list);
96 if (script_list == NULL) {
97 return ENOMEM;
100 script_list->num_scripts = count;
101 script_list->script = talloc_zero_array(script_list,
102 struct run_event_script,
103 count);
104 if (script_list->script == NULL) {
105 ret = ENOMEM;
106 talloc_free(script_list);
107 goto done;
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) {
115 ret = ENOMEM;
116 talloc_free(script_list);
117 goto done;
121 *out = script_list;
122 ret = 0;
124 done:
125 if (namelist != NULL && count != -1) {
126 for (i=0; i<count; i++) {
127 free(namelist[i]);
129 free(namelist);
131 return ret;
134 static int script_chmod(TALLOC_CTX *mem_ctx, const char *script_dir,
135 const char *script_name, bool enable)
137 DIR *dirp;
138 struct dirent *de;
139 int ret, new_mode;
140 char *filename;
141 struct stat st;
142 bool found;
143 int fd = -1;
145 dirp = opendir(script_dir);
146 if (dirp == NULL) {
147 return errno;
150 found = false;
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);
156 if (ret == 0) {
157 closedir(dirp);
158 return EINVAL;
161 found = true;
162 break;
165 closedir(dirp);
167 if (! found) {
168 return ENOENT;
171 filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_name);
172 if (filename == NULL) {
173 return ENOMEM;
176 fd = open(filename, O_RDWR);
177 if (fd == -1) {
178 ret = errno;
179 goto done;
182 ret = fstat(fd, &st);
183 if (ret != 0) {
184 ret = errno;
185 goto done;
188 if (enable) {
189 new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH);
190 } else {
191 new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
194 ret = fchmod(fd, new_mode);
195 if (ret != 0) {
196 ret = errno;
197 goto done;
200 done:
201 if (fd != -1) {
202 close(fd);
204 talloc_free(filename);
205 return ret;
208 static int script_args(TALLOC_CTX *mem_ctx, const char *event_str,
209 const char *arg_str, const char ***out)
211 const char **argv;
212 int argc;
213 size_t len;
215 /* Preallocate argv array to avoid reallocation. */
216 len = 8;
217 argv = talloc_array(mem_ctx, const char *, len);
218 if (argv == NULL) {
219 return ENOMEM;
222 argv[0] = NULL; /* script name */
223 argv[1] = event_str;
224 argc = 2;
226 if (arg_str != NULL) {
227 char *str, *t, *tok;
229 str = talloc_strdup(argv, arg_str);
230 if (str == NULL) {
231 return ENOMEM;
234 t = str;
235 while ((tok = strtok(t, " ")) != NULL) {
236 argv[argc] = talloc_strdup(argv, tok);
237 if (argv[argc] == NULL) {
238 talloc_free(argv);
239 return ENOMEM;
241 argc += 1;
242 if (argc >= len) {
243 argv = talloc_realloc(mem_ctx, argv,
244 const char *, len + 8);
245 if (argv == NULL) {
246 return ENOMEM;
248 len += 8;
250 t = NULL;
253 talloc_free(str);
256 argv[argc] = NULL;
257 argc += 1;
259 *out = argv;
260 return 0;
263 struct run_event_context {
264 struct run_proc_context *run_proc_ctx;
265 const char *script_dir;
266 const char *debug_prog;
267 bool debug_running;
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;
276 struct stat st;
277 int ret;
279 run_ctx = talloc_zero(mem_ctx, struct run_event_context);
280 if (run_ctx == NULL) {
281 return ENOMEM;
284 ret = run_proc_init(run_ctx, ev, &run_ctx->run_proc_ctx);
285 if (ret != 0) {
286 talloc_free(run_ctx);
287 return ret;
290 ret = stat(script_dir, &st);
291 if (ret != 0) {
292 ret = errno;
293 talloc_free(run_ctx);
294 return ret;
297 if (! S_ISDIR(st.st_mode)) {
298 talloc_free(run_ctx);
299 return EINVAL;
302 run_ctx->script_dir = talloc_strdup(run_ctx, script_dir);
303 if (run_ctx->script_dir == NULL) {
304 talloc_free(run_ctx);
305 return ENOMEM;
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);
312 return ENOMEM;
316 run_ctx->debug_running = false;
318 *out = run_ctx;
319 return 0;
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)
340 int ret;
342 if (script->result.sig > 0) {
343 ret = -EINTR;
344 } else if (script->result.err > 0) {
345 if (script->result.err == EACCES) {
346 /* Map EACCESS to ENOEXEC */
347 ret = -ENOEXEC;
348 } else {
349 ret = -script->result.err;
351 } else {
352 ret = script->result.status;
355 return ret;
358 int run_event_script_list(struct run_event_context *run_ctx,
359 TALLOC_CTX *mem_ctx,
360 struct run_event_script_list **output)
362 struct run_event_script_list *script_list;
363 int ret, i;
365 ret = get_script_list(mem_ctx, run_event_script_dir(run_ctx),
366 &script_list);
367 if (ret != 0) {
368 return ret;
371 if (script_list == NULL) {
372 *output = NULL;
373 return 0;
376 for (i=0; i<script_list->num_scripts; i++) {
377 struct run_event_script *script = &script_list->script[i];
378 struct stat st;
379 char *path = NULL;
381 path = talloc_asprintf(mem_ctx, "%s/%s",
382 run_event_script_dir(run_ctx),
383 script->name);
384 if (path == NULL) {
385 continue;
388 ret = stat(path, &st);
389 if (ret != 0) {
390 TALLOC_FREE(path);
391 continue;
394 if (! (st.st_mode & S_IXUSR)) {
395 script->summary = -ENOEXEC;
398 TALLOC_FREE(path);
401 *output = script_list;
402 return 0;
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),
409 script_name, true);
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),
416 script_name, false);
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)
426 const char **argv;
428 argv = talloc_array(mem_ctx, const char *, 4);
429 if (argv == NULL) {
430 return ENOMEM;
433 argv[0] = path;
434 argv[1] = talloc_asprintf(argv, "%d", pid);
435 argv[2] = event_str;
436 if (argv[1] == NULL) {
437 talloc_free(argv);
438 return ENOMEM;
440 argv[3] = NULL;
442 *out = argv;
443 return 0;
446 static void debug_log(int loglevel, const char *output, const char *log_prefix)
448 char *line, *s;
450 s = strdup(output);
451 if (s == NULL) {
452 DEBUG(loglevel, ("%s: %s\n", log_prefix, output));
453 return;
456 line = strtok(s, "\n");
457 while (line != NULL) {
458 DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
459 line = strtok(NULL, "\n");
461 free(s);
464 struct run_debug_state {
465 struct run_event_context *run_ctx;
466 pid_t pid;
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;
478 const char **argv;
479 const char *debug_prog;
480 int ret;
482 req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
483 if (req == NULL) {
484 return NULL;
487 state->run_ctx = run_ctx;
488 state->pid = pid;
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);
501 if (pid == -1) {
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);
508 if (ret != 0) {
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;
526 talloc_free(argv);
527 return req;
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);
536 char *output;
537 int ret;
538 bool status;
540 state->run_ctx->debug_running = false;
542 status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output);
543 TALLOC_FREE(subreq);
544 if (! status) {
545 D_ERR("Running debug failed, ret=%d\n", ret);
548 /* Log output */
549 if (output != NULL) {
550 debug_log(DEBUG_ERR, output, "event_debug");
551 talloc_free(output);
554 kill(-state->pid, SIGTERM);
555 tevent_req_done(req);
558 static bool run_debug_recv(struct tevent_req *req, int *perr)
560 int ret;
562 if (tevent_req_is_unix_error(req, &ret)) {
563 if (perr != NULL) {
564 *perr = ret;
566 return false;
569 return true;
573 * Run a single event
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;
583 const char **argv;
584 int index;
585 int status;
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,
597 const char *arg_str,
598 struct timeval timeout)
600 struct tevent_req *req, *subreq;
601 struct run_event_state *state;
602 int ret;
604 req = tevent_req_create(mem_ctx, &state, struct run_event_state);
605 if (req == NULL) {
606 return NULL;
609 state->ev = ev;
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);
619 if (ret != 0) {
620 D_ERR("get_script_list() failed, ret=%d\n", ret);
621 tevent_req_error(req, ret);
622 return tevent_req_post(req, ev);
625 /* No scripts */
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);
633 if (ret != 0) {
634 D_ERR("script_args() failed, ret=%d\n", ret);
635 tevent_req_error(req, ret);
636 return tevent_req_post(req, ev);
639 state->index = 0;
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);
647 return 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;
656 char *path;
658 script = &state->script_list->script[state->index];
660 path = talloc_asprintf(state, "%s/%s",
661 run_event_script_dir(state->run_ctx),
662 script->name);
663 if (path == NULL) {
664 return NULL;
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);
677 talloc_free(path);
679 return subreq;
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;
689 pid_t pid;
690 int ret;
691 bool status;
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);
698 TALLOC_FREE(subreq);
699 if (! status) {
700 D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret);
701 tevent_req_error(req, ret);
702 return;
705 /* Log output */
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);
729 return;
732 state->index += 1;
734 /* All scripts executed */
735 if (state->index >= state->script_list->num_scripts) {
736 tevent_req_done(req);
737 return;
740 subreq = run_event_run_script(req);
741 if (tevent_req_nomem(subreq, req)) {
742 return;
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");
759 return;
761 tevent_req_set_callback(subreq, run_event_debug_done, NULL);
764 static void run_event_debug_done(struct tevent_req *subreq)
766 int ret = 0;
767 bool status;
769 status = run_debug_recv(subreq, &ret);
770 TALLOC_FREE(subreq);
771 if (! status) {
772 D_NOTICE("run_debug() failed, ret=%d\n", ret);
776 bool run_event_recv(struct tevent_req *req, int *perr,
777 TALLOC_CTX *mem_ctx,
778 struct run_event_script_list **script_list)
780 struct run_event_state *state = tevent_req_data(
781 req, struct run_event_state);
782 int ret;
784 if (tevent_req_is_unix_error(req, &ret)) {
785 if (perr != NULL) {
786 *perr = ret;
788 return false;
791 if (script_list != NULL) {
792 *script_list = talloc_steal(mem_ctx, state->script_list);
794 return true;