ctdb-daemon: Improve error handling for running event scripts
[obnox/samba/samba-obnox.git] / ctdb / server / ctdb_event_helper.c
1 /*
2    ctdb event script helper
3
4    Copyright (C) Amitay Isaacs  2013
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 "includes.h"
21 #include "system/filesys.h"
22 #include "ctdb_private.h"
23
24 static char *progname = NULL;
25
26
27 /* CTDB sends SIGTERM, when process must die */
28 static void sigterm(int sig)
29 {
30         pid_t pid;
31
32         /* all the child processes are running in the same process group */
33         pid = getpgrp();
34         if (pid == -1) {
35                 kill(-getpid(), SIGKILL);
36         } else {
37                 kill(-pid, SIGKILL);
38         }
39         _exit(0);
40 }
41
42 static int check_executable(const char *path)
43 {
44         struct stat st;
45
46         if (stat(path, &st) != 0) {
47                 fprintf(stderr, "Failed to access '%s' - %s\n",
48                         path, strerror(errno));
49                 return errno;
50         }
51
52         if (!(st.st_mode & S_IXUSR)) {
53                 return ENOEXEC;
54         }
55
56         return 0;
57 }
58
59 static void usage(void)
60 {
61         fprintf(stderr, "\n");
62         fprintf(stderr, "Usage: %s <log-fd> <output-fd> <script_path> <event> [<args>]\n",
63                 progname);
64 }
65
66 int main(int argc, char *argv[])
67 {
68         int log_fd, write_fd;
69         pid_t pid;
70         int status, output, ret;
71
72         progname = argv[0];
73
74         if (argc < 5) {
75                 usage();
76                 exit(1);
77         }
78
79         reset_scheduler();
80
81         log_fd = atoi(argv[1]);
82         write_fd = atoi(argv[2]);
83
84         set_close_on_exec(write_fd);
85
86         close(STDOUT_FILENO);
87         close(STDERR_FILENO);
88         dup2(log_fd, STDOUT_FILENO);
89         dup2(log_fd, STDERR_FILENO);
90         close(log_fd);
91
92         if (setpgid(0, 0) != 0) {
93                 fprintf(stderr, "Failed to create process group for event script - %s\n",
94                         strerror(errno));
95                 exit(1);
96         }
97
98         signal(SIGTERM, sigterm);
99
100         pid = fork();
101         if (pid < 0) {
102                 int save_errno = errno;
103                 fprintf(stderr, "Failed to fork - %s\n", strerror(errno));
104                 sys_write(write_fd, &save_errno, sizeof(save_errno));
105                 exit(1);
106         }
107
108         if (pid == 0) {
109                 ret = check_executable(argv[3]);
110                 if (ret != 0) {
111                         _exit(ret);
112                 }
113                 ret = execv(argv[3], &argv[3]);
114                 if (ret != 0) {
115                         int save_errno = errno;
116                         fprintf(stderr, "Error executing '%s' - %s\n",
117                                 argv[3], strerror(save_errno));
118                 }
119                 /* This should never happen */
120                 _exit(ENOEXEC);
121         }
122
123         ret = waitpid(pid, &status, 0);
124         if (ret == -1) {
125                 output = -errno;
126                 fprintf(stderr, "waitpid() failed - %s\n", strerror(errno));
127                 sys_write(write_fd, &output, sizeof(output));
128                 exit(1);
129         }
130         if (WIFEXITED(status)) {
131                 output = -WEXITSTATUS(status);
132                 sys_write(write_fd, &output, sizeof(output));
133                 exit(0);
134         }
135         if (WIFSIGNALED(status)) {
136                 output = -EINTR;
137                 fprintf(stderr, "Process terminated with signal - %d\n",
138                         WTERMSIG(status));
139                 sys_write(write_fd, &output, sizeof(output));
140                 exit(0);
141         }
142
143         fprintf(stderr, "waitpid() status=%d\n", status);
144         exit(1);
145 }