0961d657a743fabfd31b4e629a4fd70e2081fdca
[samba.git] / ctdb / common / run_event.c
1 /*
2    Run scripts in a directory with specific event arguments
3
4    Copyright (C) Amitay Isaacs  2017
5
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.
10
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.
15
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/>.
18 */
19
20 #include "replace.h"
21 #include "system/filesys.h"
22 #include "system/dir.h"
23 #include "system/locale.h"
24 #include "system/wait.h"
25
26 #include <talloc.h>
27 #include <tevent.h>
28
29 #include "lib/util/tevent_unix.h"
30 #include "lib/util/debug.h"
31
32 #include "common/logging.h"
33 #include "common/run_proc.h"
34 #include "common/run_event.h"
35
36 /*
37  * Utility functions
38  */
39
40 static int script_filter(const struct dirent *de)
41 {
42         size_t namelen = strlen(de->d_name);
43         char *ptr;
44
45         /* Ignore . and .. */
46         if (namelen < 3) {
47                 return 0;
48         }
49
50         /* Skip filenames with ~ */
51         ptr = strchr(de->d_name, '~');
52         if (ptr != NULL) {
53                 return 0;
54         }
55
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;
61         }
62
63         /* Ignore filenames with multiple '.'s */
64         ptr = index(&de->d_name[3], '.');
65         if (ptr != NULL) {
66                 return 0;
67         }
68
69         return 1;
70 }
71
72 static int get_script_list(TALLOC_CTX *mem_ctx,
73                            const char *script_dir,
74                            struct run_event_script_list **out)
75 {
76         struct dirent **namelist = NULL;
77         struct run_event_script_list *script_list;
78         int count, ret;
79         int i;
80
81         count = scandir(script_dir, &namelist, script_filter, alphasort);
82         if (count == -1) {
83                 ret = errno;
84                 if (ret == ENOENT) {
85                         D_WARNING("event script dir %s removed\n", script_dir);
86                 } else {
87                         D_WARNING("scandir() failed on %s, ret=%d\n",
88                                   script_dir, ret);
89                 }
90                 *out = NULL;
91                 ret = 0;
92                 goto done;
93         }
94
95         if (count == 0) {
96                 *out = NULL;
97                 ret = 0;
98                 goto done;
99         }
100
101         script_list = talloc_zero(mem_ctx, struct run_event_script_list);
102         if (script_list == NULL) {
103                 return ENOMEM;
104         }
105
106         script_list->num_scripts = count;
107         script_list->script = talloc_zero_array(script_list,
108                                                 struct run_event_script,
109                                                 count);
110         if (script_list->script == NULL) {
111                 ret = ENOMEM;
112                 talloc_free(script_list);
113                 goto done;
114         }
115
116         for (i=0; i<count; i++) {
117                 struct run_event_script *s = &script_list->script[i];
118
119                 s->name = talloc_strdup(script_list, namelist[i]->d_name);
120                 if (s->name == NULL) {
121                         ret = ENOMEM;
122                         talloc_free(script_list);
123                         goto done;
124                 }
125         }
126
127         *out = script_list;
128         ret = 0;
129
130 done:
131         if (namelist != NULL && count != -1) {
132                 for (i=0; i<count; i++) {
133                         free(namelist[i]);
134                 }
135                 free(namelist);
136         }
137         return ret;
138 }
139
140 static int script_chmod(TALLOC_CTX *mem_ctx, const char *script_dir,
141                         const char *script_name, bool enable)
142 {
143         DIR *dirp;
144         struct dirent *de;
145         int ret, new_mode;
146         char *filename;
147         struct stat st;
148         bool found;
149         int fd = -1;
150
151         dirp = opendir(script_dir);
152         if (dirp == NULL) {
153                 return errno;
154         }
155
156         found = false;
157         while ((de = readdir(dirp)) != NULL) {
158                 if (strcmp(de->d_name, script_name) == 0) {
159
160                         /* check for valid script names */
161                         ret = script_filter(de);
162                         if (ret == 0) {
163                                 closedir(dirp);
164                                 return EINVAL;
165                         }
166
167                         found = true;
168                         break;
169                 }
170         }
171         closedir(dirp);
172
173         if (! found) {
174                 return ENOENT;
175         }
176
177         filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_name);
178         if (filename == NULL) {
179                 return ENOMEM;
180         }
181
182         fd = open(filename, O_RDWR);
183         if (fd == -1) {
184                 ret = errno;
185                 goto done;
186         }
187
188         ret = fstat(fd, &st);
189         if (ret != 0) {
190                 ret = errno;
191                 goto done;
192         }
193
194         if (enable) {
195                 new_mode = st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH);
196         } else {
197                 new_mode = st.st_mode & ~(S_IXUSR | S_IXGRP | S_IXOTH);
198         }
199
200         ret = fchmod(fd, new_mode);
201         if (ret != 0) {
202                 ret = errno;
203                 goto done;
204         }
205
206 done:
207         if (fd != -1) {
208                 close(fd);
209         }
210         talloc_free(filename);
211         return ret;
212 }
213
214 static int script_args(TALLOC_CTX *mem_ctx, const char *event_str,
215                        const char *arg_str, const char ***out)
216 {
217         const char **argv;
218         int argc;
219         size_t len;
220
221         /* Preallocate argv array to avoid reallocation. */
222         len = 8;
223         argv = talloc_array(mem_ctx, const char *, len);
224         if (argv == NULL) {
225                 return ENOMEM;
226         }
227
228         argv[0] = NULL; /* script name */
229         argv[1] = event_str;
230         argc = 2;
231
232         if (arg_str != NULL) {
233                 char *str, *t, *tok;
234
235                 str = talloc_strdup(argv, arg_str);
236                 if (str == NULL) {
237                         return ENOMEM;
238                 }
239
240                 t = str;
241                 while ((tok = strtok(t, " ")) != NULL) {
242                         argv[argc] = talloc_strdup(argv, tok);
243                         if (argv[argc] == NULL) {
244                                 talloc_free(argv);
245                                 return ENOMEM;
246                         }
247                         argc += 1;
248                         if (argc >= len) {
249                                 argv = talloc_realloc(mem_ctx, argv,
250                                                       const char *, len + 8);
251                                 if (argv == NULL) {
252                                         return ENOMEM;
253                                 }
254                                 len += 8;
255                         }
256                         t = NULL;
257                 }
258
259                 talloc_free(str);
260         }
261
262         argv[argc] = NULL;
263         argc += 1;
264
265         *out = argv;
266         return 0;
267 }
268
269 struct run_event_context {
270         struct run_proc_context *run_proc_ctx;
271         const char *script_dir;
272         const char *debug_prog;
273         bool debug_running;
274 };
275
276
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)
280 {
281         struct run_event_context *run_ctx;
282         struct stat st;
283         int ret;
284
285         run_ctx = talloc_zero(mem_ctx, struct run_event_context);
286         if (run_ctx == NULL) {
287                 return ENOMEM;
288         }
289
290         ret = run_proc_init(run_ctx, ev, &run_ctx->run_proc_ctx);
291         if (ret != 0) {
292                 talloc_free(run_ctx);
293                 return ret;
294         }
295
296         ret = stat(script_dir, &st);
297         if (ret != 0) {
298                 ret = errno;
299                 talloc_free(run_ctx);
300                 return ret;
301         }
302
303         if (! S_ISDIR(st.st_mode)) {
304                 talloc_free(run_ctx);
305                 return EINVAL;
306         }
307
308         run_ctx->script_dir = talloc_strdup(run_ctx, script_dir);
309         if (run_ctx->script_dir == NULL) {
310                 talloc_free(run_ctx);
311                 return ENOMEM;
312         }
313
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);
318                         return ENOMEM;
319                 }
320         }
321
322         run_ctx->debug_running = false;
323
324         *out = run_ctx;
325         return 0;
326 }
327
328 static struct run_proc_context *
329 run_event_run_proc_context(struct run_event_context *run_ctx)
330 {
331         return run_ctx->run_proc_ctx;
332 }
333
334 static const char *run_event_script_dir(struct run_event_context *run_ctx)
335 {
336         return run_ctx->script_dir;
337 }
338
339 static const char *run_event_debug_prog(struct run_event_context *run_ctx)
340 {
341         return run_ctx->debug_prog;
342 }
343
344 static int run_event_script_status(struct run_event_script *script)
345 {
346         int ret;
347
348         if (script->result.sig > 0) {
349                 ret = -EINTR;
350         } else if (script->result.err > 0) {
351                 if (script->result.err == EACCES) {
352                         /* Map EACCESS to ENOEXEC */
353                         ret = -ENOEXEC;
354                 } else {
355                         ret = -script->result.err;
356                 }
357         } else {
358                 ret = script->result.status;
359         }
360
361         return ret;
362 }
363
364 int run_event_script_list(struct run_event_context *run_ctx,
365                           TALLOC_CTX *mem_ctx,
366                           struct run_event_script_list **output)
367 {
368         struct run_event_script_list *script_list;
369         int ret, i;
370
371         ret = get_script_list(mem_ctx, run_event_script_dir(run_ctx),
372                               &script_list);
373         if (ret != 0) {
374                 return ret;
375         }
376
377         if (script_list == NULL) {
378                 *output = NULL;
379                 return 0;
380         }
381
382         for (i=0; i<script_list->num_scripts; i++) {
383                 struct run_event_script *script = &script_list->script[i];
384                 struct stat st;
385                 char *path = NULL;
386
387                 path = talloc_asprintf(mem_ctx, "%s/%s",
388                                        run_event_script_dir(run_ctx),
389                                        script->name);
390                 if (path == NULL) {
391                         continue;
392                 }
393
394                 ret = stat(path, &st);
395                 if (ret != 0) {
396                         TALLOC_FREE(path);
397                         continue;
398                 }
399
400                 if (! (st.st_mode & S_IXUSR)) {
401                         script->summary = -ENOEXEC;
402                 }
403
404                 TALLOC_FREE(path);
405         }
406
407         *output = script_list;
408         return 0;
409 }
410
411 int run_event_script_enable(struct run_event_context *run_ctx,
412                             const char *script_name)
413 {
414         return script_chmod(run_ctx, run_event_script_dir(run_ctx),
415                             script_name, true);
416 }
417
418 int run_event_script_disable(struct run_event_context *run_ctx,
419                              const char *script_name)
420 {
421         return script_chmod(run_ctx, run_event_script_dir(run_ctx),
422                             script_name, false);
423 }
424
425 /*
426  * Run debug program to diagnose hung scripts
427  */
428
429 static int debug_args(TALLOC_CTX *mem_ctx, const char *path,
430                       const char *event_str, pid_t pid, const char ***out)
431 {
432         const char **argv;
433
434         argv = talloc_array(mem_ctx, const char *, 4);
435         if (argv == NULL) {
436                 return ENOMEM;
437         }
438
439         argv[0] = path;
440         argv[1] = talloc_asprintf(argv, "%d", pid);
441         argv[2] = event_str;
442         if (argv[1] == NULL) {
443                 talloc_free(argv);
444                 return ENOMEM;
445         }
446         argv[3] = NULL;
447
448         *out = argv;
449         return 0;
450 }
451
452 static void debug_log(int loglevel, const char *output, const char *log_prefix)
453 {
454         char *line, *s;
455
456         s = strdup(output);
457         if (s == NULL) {
458                 DEBUG(loglevel, ("%s: %s\n", log_prefix, output));
459                 return;
460         }
461
462         line = strtok(s, "\n");
463         while (line != NULL) {
464                 DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
465                 line = strtok(NULL, "\n");
466         }
467         free(s);
468 }
469
470 struct run_debug_state {
471         struct run_event_context *run_ctx;
472         pid_t pid;
473 };
474
475 static void run_debug_done(struct tevent_req *subreq);
476
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)
481 {
482         struct tevent_req *req, *subreq;
483         struct run_debug_state *state;
484         const char **argv;
485         const char *debug_prog;
486         int ret;
487
488         req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
489         if (req == NULL) {
490                 return NULL;
491         }
492
493         state->run_ctx = run_ctx;
494         state->pid = pid;
495
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);
500         }
501
502         if (run_ctx->debug_running) {
503                 tevent_req_done(req);
504                 return tevent_req_post(req, ev);
505         }
506
507         if (pid == -1) {
508                 D_DEBUG("Event script terminated, nothing to debug\n");
509                 tevent_req_done(req);
510                 return tevent_req_post(req, ev);
511         }
512
513         ret = debug_args(state, debug_prog, event_str, pid, &argv);
514         if (ret != 0) {
515                 D_ERR("debug_args() failed\n");
516                 tevent_req_error(req, ret);
517                 return tevent_req_post(req, ev);
518         }
519
520         D_DEBUG("Running debug %s with args \"%s %s\"\n",
521                 debug_prog, argv[1], argv[2]);
522
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);
527         }
528         tevent_req_set_callback(subreq, run_debug_done, req);
529
530         run_ctx->debug_running = true;
531
532         talloc_free(argv);
533         return req;
534 }
535
536 static void run_debug_done(struct tevent_req *subreq)
537 {
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);
542         char *output;
543         int ret;
544         bool status;
545
546         state->run_ctx->debug_running = false;
547
548         status = run_proc_recv(subreq, &ret, NULL, NULL, state, &output);
549         TALLOC_FREE(subreq);
550         if (! status) {
551                 D_ERR("Running debug failed, ret=%d\n", ret);
552         }
553
554         /* Log output */
555         if (output != NULL) {
556                 debug_log(DEBUG_ERR, output, "event_debug");
557                 talloc_free(output);
558         }
559
560         kill(-state->pid, SIGTERM);
561         tevent_req_done(req);
562 }
563
564 static bool run_debug_recv(struct tevent_req *req, int *perr)
565 {
566         int ret;
567
568         if (tevent_req_is_unix_error(req, &ret)) {
569                 if (perr != NULL) {
570                         *perr = ret;
571                 }
572                 return false;
573         }
574
575         return true;
576 }
577
578 /*
579  * Run a single event
580  */
581
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;
587
588         struct run_event_script_list *script_list;
589         const char **argv;
590         int index;
591         int status;
592 };
593
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);
598
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,
603                                   const char *arg_str,
604                                   struct timeval timeout)
605 {
606         struct tevent_req *req, *subreq;
607         struct run_event_state *state;
608         int ret;
609
610         req = tevent_req_create(mem_ctx, &state, struct run_event_state);
611         if (req == NULL) {
612                 return NULL;
613         }
614
615         state->ev = ev;
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);
620         }
621         state->timeout = timeout;
622
623         ret = get_script_list(state, run_event_script_dir(run_ctx),
624                               &state->script_list);
625         if (ret != 0) {
626                 D_ERR("get_script_list() failed, ret=%d\n", ret);
627                 tevent_req_error(req, ret);
628                 return tevent_req_post(req, ev);
629         }
630
631         /* No scripts */
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);
636         }
637
638         ret = script_args(state, event_str, arg_str, &state->argv);
639         if (ret != 0) {
640                 D_ERR("script_args() failed, ret=%d\n", ret);
641                 tevent_req_error(req, ret);
642                 return tevent_req_post(req, ev);
643         }
644
645         state->index = 0;
646
647         subreq = run_event_run_script(req);
648         if (tevent_req_nomem(subreq, req)) {
649                 return tevent_req_post(req, ev);
650         }
651         tevent_req_set_callback(subreq, run_event_next_script, req);
652
653         return req;
654 }
655
656 static struct tevent_req *run_event_run_script(struct tevent_req *req)
657 {
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;
662         char *path;
663
664         script = &state->script_list->script[state->index];
665
666         path = talloc_asprintf(state, "%s/%s",
667                                run_event_script_dir(state->run_ctx),
668                                script->name);
669         if (path == NULL) {
670                 return NULL;
671         }
672
673         state->argv[0] = script->name;
674         script->begin = tevent_timeval_current();
675
676         D_DEBUG("Running %s with args \"%s %s\"\n",
677                 path, state->argv[0], state->argv[1]);
678
679         subreq = run_proc_send(state, state->ev,
680                                run_event_run_proc_context(state->run_ctx),
681                                path, state->argv, -1, state->timeout);
682
683         talloc_free(path);
684
685         return subreq;
686 }
687
688 static void run_event_next_script(struct tevent_req *subreq)
689 {
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;
695         pid_t pid;
696         int ret;
697         bool status;
698
699         script = &state->script_list->script[state->index];
700         script->end = tevent_timeval_current();
701
702         status = run_proc_recv(subreq, &ret, &script->result, &pid,
703                                state->script_list, &script->output);
704         TALLOC_FREE(subreq);
705         if (! status) {
706                 D_ERR("run_proc failed for %s, ret=%d\n", script->name, ret);
707                 tevent_req_error(req, ret);
708                 return;
709         }
710
711         /* Log output */
712         if (script->output != NULL) {
713                 debug_log(DEBUG_ERR, script->output, script->name);
714         }
715
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);
719
720
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;
725
726                 if (script->summary == -ETIME && pid != -1) {
727                         run_event_debug(req, pid);
728                 }
729
730                 state->script_list->summary = script->summary;
731                 D_NOTICE("%s event %s\n", state->event_str,
732                          (script->summary == -ETIME) ? "timed out" : "failed");
733
734                 tevent_req_done(req);
735                 return;
736         }
737
738         state->index += 1;
739
740         /* All scripts executed */
741         if (state->index >= state->script_list->num_scripts) {
742                 tevent_req_done(req);
743                 return;
744         }
745
746         subreq = run_event_run_script(req);
747         if (tevent_req_nomem(subreq, req)) {
748                 return;
749         }
750         tevent_req_set_callback(subreq, run_event_next_script, req);
751 }
752
753 static void run_event_debug(struct tevent_req *req, pid_t pid)
754 {
755         struct run_event_state *state = tevent_req_data(
756                 req, struct run_event_state);
757         struct tevent_req *subreq;
758
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");
765                 return;
766         }
767         tevent_req_set_callback(subreq, run_event_debug_done, NULL);
768 }
769
770 static void run_event_debug_done(struct tevent_req *subreq)
771 {
772         int ret = 0;
773         bool status;
774
775         status = run_debug_recv(subreq, &ret);
776         TALLOC_FREE(subreq);
777         if (! status) {
778                 D_NOTICE("run_debug() failed, ret=%d\n", ret);
779         }
780 }
781
782 bool run_event_recv(struct tevent_req *req, int *perr,
783                     TALLOC_CTX *mem_ctx,
784                     struct run_event_script_list **script_list)
785 {
786         struct run_event_state *state = tevent_req_data(
787                 req, struct run_event_state);
788         int ret;
789
790         if (tevent_req_is_unix_error(req, &ret)) {
791                 if (perr != NULL) {
792                         *perr = ret;
793                 }
794                 return false;
795         }
796
797         if (script_list != NULL) {
798                 *script_list = talloc_steal(mem_ctx, state->script_list);
799         }
800         return true;
801 }
802