s4-torture: add test to check for max. number of channels per session.
[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         return 1;
64 }
65
66 static int get_script_list(TALLOC_CTX *mem_ctx,
67                            const char *script_dir,
68                            struct run_event_script_list **out)
69 {
70         struct dirent **namelist = NULL;
71         struct run_event_script_list *script_list;
72         int count, ret;
73         int i;
74
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);
83                 }
84                 *out = NULL;
85                 ret = 0;
86                 goto done;
87         }
88
89         if (count == 0) {
90                 *out = NULL;
91                 ret = 0;
92                 goto done;
93         }
94
95         script_list = talloc_zero(mem_ctx, struct run_event_script_list);
96         if (script_list == NULL) {
97                 return ENOMEM;
98         }
99
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;
108         }
109
110         for (i=0; i<count; i++) {
111                 struct run_event_script *s = &script_list->script[i];
112
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;
118                 }
119         }
120
121         *out = script_list;
122         ret = 0;
123
124 done:
125         if (namelist != NULL && count != -1) {
126                 for (i=0; i<count; i++) {
127                         free(namelist[i]);
128                 }
129                 free(namelist);
130         }
131         return ret;
132 }
133
134 static int script_chmod(TALLOC_CTX *mem_ctx, const char *script_dir,
135                         const char *script_name, bool enable)
136 {
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;
144
145         dirp = opendir(script_dir);
146         if (dirp == NULL) {
147                 return errno;
148         }
149
150         found = false;
151         while ((de = readdir(dirp)) != NULL) {
152                 if (strcmp(de->d_name, script_name) == 0) {
153
154                         /* check for valid script names */
155                         ret = script_filter(de);
156                         if (ret == 0) {
157                                 closedir(dirp);
158                                 return EINVAL;
159                         }
160
161                         found = true;
162                         break;
163                 }
164         }
165         closedir(dirp);
166
167         if (! found) {
168                 return ENOENT;
169         }
170
171         filename = talloc_asprintf(mem_ctx, "%s/%s", script_dir, script_name);
172         if (filename == NULL) {
173                 return ENOMEM;
174         }
175
176         fd = open(filename, O_RDWR);
177         if (fd == -1) {
178                 ret = errno;
179                 goto done;
180         }
181
182         ret = fstat(fd, &st);
183         if (ret != 0) {
184                 ret = errno;
185                 goto done;
186         }
187
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);
192         }
193
194         ret = fchmod(fd, new_mode);
195         if (ret != 0) {
196                 ret = errno;
197                 goto done;
198         }
199
200 done:
201         if (fd != -1) {
202                 close(fd);
203         }
204         talloc_free(filename);
205         return ret;
206 }
207
208 static int script_args(TALLOC_CTX *mem_ctx, const char *event_str,
209                        const char *arg_str, const char ***out)
210 {
211         const char **argv;
212         int argc;
213         size_t len;
214
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;
220         }
221
222         argv[0] = NULL; /* script name */
223         argv[1] = event_str;
224         argc = 2;
225
226         if (arg_str != NULL) {
227                 char *str, *t, *tok;
228
229                 str = talloc_strdup(argv, arg_str);
230                 if (str == NULL) {
231                         return ENOMEM;
232                 }
233
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;
240                         }
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;
247                                 }
248                                 len += 8;
249                         }
250                         t = NULL;
251                 }
252
253                 talloc_free(str);
254         }
255
256         argv[argc] = NULL;
257         argc += 1;
258
259         *out = argv;
260         return 0;
261 }
262
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;
268 };
269
270
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)
274 {
275         struct run_event_context *run_ctx;
276         struct stat st;
277         int ret;
278
279         run_ctx = talloc_zero(mem_ctx, struct run_event_context);
280         if (run_ctx == NULL) {
281                 return ENOMEM;
282         }
283
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;
288         }
289
290         ret = stat(script_dir, &st);
291         if (ret != 0) {
292                 ret = errno;
293                 talloc_free(run_ctx);
294                 return ret;
295         }
296
297         if (! S_ISDIR(st.st_mode)) {
298                 talloc_free(run_ctx);
299                 return EINVAL;
300         }
301
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;
306         }
307
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;
313                 }
314         }
315
316         run_ctx->debug_running = false;
317
318         *out = run_ctx;
319         return 0;
320 }
321
322 static struct run_proc_context *
323 run_event_run_proc_context(struct run_event_context *run_ctx)
324 {
325         return run_ctx->run_proc_ctx;
326 }
327
328 static const char *run_event_script_dir(struct run_event_context *run_ctx)
329 {
330         return run_ctx->script_dir;
331 }
332
333 static const char *run_event_debug_prog(struct run_event_context *run_ctx)
334 {
335         return run_ctx->debug_prog;
336 }
337
338 static int run_event_script_status(struct run_event_script *script)
339 {
340         int ret;
341
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;
350                 }
351         } else {
352                 ret = script->result.status;
353         }
354
355         return ret;
356 }
357
358 int run_event_script_list(struct run_event_context *run_ctx,
359                           TALLOC_CTX *mem_ctx,
360                           struct run_event_script_list **output)
361 {
362         struct run_event_script_list *script_list;
363         int ret, i;
364
365         ret = get_script_list(mem_ctx, run_event_script_dir(run_ctx),
366                               &script_list);
367         if (ret != 0) {
368                 return ret;
369         }
370
371         if (script_list == NULL) {
372                 *output = NULL;
373                 return 0;
374         }
375
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;
380
381                 path = talloc_asprintf(mem_ctx, "%s/%s",
382                                        run_event_script_dir(run_ctx),
383                                        script->name);
384                 if (path == NULL) {
385                         continue;
386                 }
387
388                 ret = stat(path, &st);
389                 if (ret != 0) {
390                         TALLOC_FREE(path);
391                         continue;
392                 }
393
394                 if (! (st.st_mode & S_IXUSR)) {
395                         script->summary = -ENOEXEC;
396                 }
397
398                 TALLOC_FREE(path);
399         }
400
401         *output = script_list;
402         return 0;
403 }
404
405 int run_event_script_enable(struct run_event_context *run_ctx,
406                             const char *script_name)
407 {
408         return script_chmod(run_ctx, run_event_script_dir(run_ctx),
409                             script_name, true);
410 }
411
412 int run_event_script_disable(struct run_event_context *run_ctx,
413                              const char *script_name)
414 {
415         return script_chmod(run_ctx, run_event_script_dir(run_ctx),
416                             script_name, false);
417 }
418
419 /*
420  * Run debug program to diagnose hung scripts
421  */
422
423 static int debug_args(TALLOC_CTX *mem_ctx, const char *path,
424                       const char *event_str, pid_t pid, const char ***out)
425 {
426         const char **argv;
427
428         argv = talloc_array(mem_ctx, const char *, 4);
429         if (argv == NULL) {
430                 return ENOMEM;
431         }
432
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;
439         }
440         argv[3] = NULL;
441
442         *out = argv;
443         return 0;
444 }
445
446 static void debug_log(int loglevel, const char *output, const char *log_prefix)
447 {
448         char *line, *s;
449
450         s = strdup(output);
451         if (s == NULL) {
452                 DEBUG(loglevel, ("%s: %s\n", log_prefix, output));
453                 return;
454         }
455
456         line = strtok(s, "\n");
457         while (line != NULL) {
458                 DEBUG(loglevel, ("%s: %s\n", log_prefix, line));
459                 line = strtok(NULL, "\n");
460         }
461         free(s);
462 }
463
464 struct run_debug_state {
465         struct run_event_context *run_ctx;
466         pid_t pid;
467 };
468
469 static void run_debug_done(struct tevent_req *subreq);
470
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)
475 {
476         struct tevent_req *req, *subreq;
477         struct run_debug_state *state;
478         const char **argv;
479         const char *debug_prog;
480         int ret;
481
482         req = tevent_req_create(mem_ctx, &state, struct run_debug_state);
483         if (req == NULL) {
484                 return NULL;
485         }
486
487         state->run_ctx = run_ctx;
488         state->pid = pid;
489
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);
494         }
495
496         if (run_ctx->debug_running) {
497                 tevent_req_done(req);
498                 return tevent_req_post(req, ev);
499         }
500
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);
505         }
506
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);
512         }
513
514         D_DEBUG("Running debug %s with args \"%s %s\"\n",
515                 debug_prog, argv[1], argv[2]);
516
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);
521         }
522         tevent_req_set_callback(subreq, run_debug_done, req);
523
524         run_ctx->debug_running = true;
525
526         talloc_free(argv);
527         return req;
528 }
529
530 static void run_debug_done(struct tevent_req *subreq)
531 {
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;
539
540         state->run_ctx->debug_running = false;
541
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);
546         }
547
548         /* Log output */
549         if (output != NULL) {
550                 debug_log(DEBUG_ERR, output, "event_debug");
551                 talloc_free(output);
552         }
553
554         kill(-state->pid, SIGTERM);
555         tevent_req_done(req);
556 }
557
558 static bool run_debug_recv(struct tevent_req *req, int *perr)
559 {
560         int ret;
561
562         if (tevent_req_is_unix_error(req, &ret)) {
563                 if (perr != NULL) {
564                         *perr = ret;
565                 }
566                 return false;
567         }
568
569         return true;
570 }
571
572 /*
573  * Run a single event
574  */
575
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;
581
582         struct run_event_script_list *script_list;
583         const char **argv;
584         int index;
585         int status;
586 };
587
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);
592
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)
599 {
600         struct tevent_req *req, *subreq;
601         struct run_event_state *state;
602         int ret;
603
604         req = tevent_req_create(mem_ctx, &state, struct run_event_state);
605         if (req == NULL) {
606                 return NULL;
607         }
608
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);
614         }
615         state->timeout = timeout;
616
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);
623         }
624
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);
630         }
631
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);
637         }
638
639         state->index = 0;
640
641         subreq = run_event_run_script(req);
642         if (tevent_req_nomem(subreq, req)) {
643                 return tevent_req_post(req, ev);
644         }
645         tevent_req_set_callback(subreq, run_event_next_script, req);
646
647         return req;
648 }
649
650 static struct tevent_req *run_event_run_script(struct tevent_req *req)
651 {
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;
657
658         script = &state->script_list->script[state->index];
659
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;
665         }
666
667         state->argv[0] = script->name;
668         script->begin = tevent_timeval_current();
669
670         D_DEBUG("Running %s with args \"%s %s\"\n",
671                 path, state->argv[0], state->argv[1]);
672
673         subreq = run_proc_send(state, state->ev,
674                                run_event_run_proc_context(state->run_ctx),
675                                path, state->argv, -1, state->timeout);
676
677         talloc_free(path);
678
679         return subreq;
680 }
681
682 static void run_event_next_script(struct tevent_req *subreq)
683 {
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;
692
693         script = &state->script_list->script[state->index];
694         script->end = tevent_timeval_current();
695
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;
703         }
704
705         /* Log output */
706         if (script->output != NULL) {
707                 debug_log(DEBUG_ERR, script->output, script->name);
708         }
709
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);
713
714
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;
719
720                 if (script->summary == -ETIME && pid != -1) {
721                         run_event_debug(req, pid);
722                 }
723
724                 state->script_list->summary = script->summary;
725                 D_NOTICE("%s event %s\n", state->event_str,
726                          (script->summary == -ETIME) ? "timed out" : "failed");
727
728                 tevent_req_done(req);
729                 return;
730         }
731
732         state->index += 1;
733
734         /* All scripts executed */
735         if (state->index >= state->script_list->num_scripts) {
736                 tevent_req_done(req);
737                 return;
738         }
739
740         subreq = run_event_run_script(req);
741         if (tevent_req_nomem(subreq, req)) {
742                 return;
743         }
744         tevent_req_set_callback(subreq, run_event_next_script, req);
745 }
746
747 static void run_event_debug(struct tevent_req *req, pid_t pid)
748 {
749         struct run_event_state *state = tevent_req_data(
750                 req, struct run_event_state);
751         struct tevent_req *subreq;
752
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;
760         }
761         tevent_req_set_callback(subreq, run_event_debug_done, NULL);
762 }
763
764 static void run_event_debug_done(struct tevent_req *subreq)
765 {
766         int ret = 0;
767         bool status;
768
769         status = run_debug_recv(subreq, &ret);
770         TALLOC_FREE(subreq);
771         if (! status) {
772                 D_NOTICE("run_debug() failed, ret=%d\n", ret);
773         }
774 }
775
776 bool run_event_recv(struct tevent_req *req, int *perr,
777                     TALLOC_CTX *mem_ctx,
778                     struct run_event_script_list **script_list)
779 {
780         struct run_event_state *state = tevent_req_data(
781                 req, struct run_event_state);
782         int ret;
783
784         if (tevent_req_is_unix_error(req, &ret)) {
785                 if (perr != NULL) {
786                         *perr = ret;
787                 }
788                 return false;
789         }
790
791         if (script_list != NULL) {
792                 *script_list = talloc_steal(mem_ctx, state->script_list);
793         }
794         return true;
795 }
796