17090975b101d1df775995851a0b994ac96fcddd
[tridge/ctdb.git] / server / eventscript.c
1 /* 
2    event script handling
3
4    Copyright (C) Andrew Tridgell  2007
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 "system/wait.h"
23 #include "system/dir.h"
24 #include "system/locale.h"
25 #include "../include/ctdb_private.h"
26 #include "lib/events/events.h"
27 #include "../common/rb_tree.h"
28
29 static struct {
30         struct timeval start;
31         const char *script_running;
32 } child_state;
33
34 /*
35   ctdbd sends us a SIGTERM when we should time out the current script
36  */
37 static void sigterm(int sig)
38 {
39         DEBUG(0,("Timed out running script '%s' after %.1f seconds\n", 
40                  child_state.script_running, timeval_elapsed(&child_state.start)));
41         /* all the child processes will be running in the same process group */
42         kill(-getpgrp(), SIGKILL);
43         exit(1);
44 }
45
46 /*
47   run the event script - varargs version
48   this function is called and run in the context of a forked child
49   which allows it to do blocking calls such as system()
50  */
51 static int ctdb_event_script_v(struct ctdb_context *ctdb, const char *fmt, va_list ap)
52 {
53         char *options, *cmdstr;
54         int ret;
55         va_list ap2;
56         struct stat st;
57         TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
58         trbt_tree_t *tree;
59         DIR *dir;
60         struct dirent *de;
61         char *script;
62
63         if (setpgid(0,0) != 0) {
64                 DEBUG(0,("Failed to create process group for event scripts - %s\n",
65                          strerror(errno)));
66                 talloc_free(tmp_ctx);
67                 return -1;              
68         }
69
70         signal(SIGTERM, sigterm);
71
72         child_state.start = timeval_current();
73         child_state.script_running = "startup";
74
75         /*
76           the service specific event scripts 
77         */
78         if (stat(ctdb->event_script_dir, &st) != 0 && 
79             errno == ENOENT) {
80                 DEBUG(0,("No event script directory found at '%s'\n", ctdb->event_script_dir));
81                 talloc_free(tmp_ctx);
82                 return -1;
83         }
84
85         /* create a tree to store all the script names in */
86         tree = trbt_create(tmp_ctx, 0);
87
88         /* scan all directory entries and insert all valid scripts into the 
89            tree
90         */
91         dir = opendir(ctdb->event_script_dir);
92         if (dir == NULL) {
93                 DEBUG(0,("Failed to open event script directory '%s'\n", ctdb->event_script_dir));
94                 talloc_free(tmp_ctx);
95                 return -1;
96         }
97
98         while ((de=readdir(dir)) != NULL) {
99                 int namlen;
100                 unsigned num;
101                 char *str;
102
103                 namlen = strlen(de->d_name);
104
105                 if (namlen < 3) {
106                         continue;
107                 }
108
109                 if (de->d_name[namlen-1] == '~') {
110                         /* skip files emacs left behind */
111                         continue;
112                 }
113
114                 if (de->d_name[2] != '.') {
115                         continue;
116                 }
117
118                 if (sscanf(de->d_name, "%02u.", &num) != 1) {
119                         continue;
120                 }
121
122                 /* Make sure the event script is executable */
123                 str = talloc_asprintf(tree, "%s/%s", ctdb->event_script_dir, de->d_name);
124                 if (stat(str, &st) != 0) {
125                         DEBUG(0,("Could not stat event script %s. Ignoring this event script\n", str));
126                         continue;
127                 }
128                 if (!(st.st_mode & S_IXUSR)) {
129                         DEBUG(0,("Event script %s is not executable. Ignoring this event script\n", str));
130                         continue;
131                 }
132                 
133                 
134                 /* store the event script in the tree */                
135                 script = trbt_insert32(tree, num, talloc_strdup(tree, de->d_name));
136                 if (script != NULL) {
137                         DEBUG(0,("CONFIG ERROR: Multiple event scripts with the same prefix : '%s' and '%s'. Each event script MUST have a unique prefix\n", script, de->d_name));
138                         talloc_free(tmp_ctx);
139                         closedir(dir);
140                         return -1;
141                 }
142         }
143         closedir(dir);
144
145         /* fetch the scripts from the tree one by one and execute
146            them
147          */
148         while ((script=trbt_findfirstarray32(tree, 1)) != NULL) {
149                 va_copy(ap2, ap);
150                 options  = talloc_vasprintf(tmp_ctx, fmt, ap2);
151                 va_end(ap2);
152                 CTDB_NO_MEMORY(ctdb, options);
153
154                 cmdstr = talloc_asprintf(tmp_ctx, "%s/%s %s", 
155                                 ctdb->event_script_dir,
156                                 script, options);
157                 CTDB_NO_MEMORY(ctdb, cmdstr);
158
159                 DEBUG(DEBUG_INFO,("Executing event script %s\n",cmdstr));
160
161                 child_state.start = timeval_current();
162                 child_state.script_running = cmdstr;
163
164                 ret = system(cmdstr);
165                 /* if the system() call was successful, translate ret into the
166                    return code from the command
167                 */
168                 if (ret != -1) {
169                         ret = WEXITSTATUS(ret);
170                 }
171                 /* return an error if the script failed */
172                 if (ret != 0) {
173                         DEBUG(0,("Event script %s failed with error %d\n", cmdstr, ret));
174                         talloc_free(tmp_ctx);
175                         return ret;
176                 }
177
178                 /* remove this script from the tree */
179                 talloc_free(script);
180         }
181
182         child_state.start = timeval_current();
183         child_state.script_running = "finished";
184         
185         talloc_free(tmp_ctx);
186         return 0;
187 }
188
189 struct ctdb_event_script_state {
190         struct ctdb_context *ctdb;
191         pid_t child;
192         void (*callback)(struct ctdb_context *, int, void *);
193         int fd[2];
194         void *private_data;
195 };
196
197 /* called when child is finished */
198 static void ctdb_event_script_handler(struct event_context *ev, struct fd_event *fde, 
199                                       uint16_t flags, void *p)
200 {
201         struct ctdb_event_script_state *state = 
202                 talloc_get_type(p, struct ctdb_event_script_state);
203         int status = -1;
204         void (*callback)(struct ctdb_context *, int, void *) = state->callback;
205         void *private_data = state->private_data;
206         struct ctdb_context *ctdb = state->ctdb;
207
208         waitpid(state->child, &status, 0);
209         if (status != -1) {
210                 status = WEXITSTATUS(status);
211         }
212         talloc_set_destructor(state, NULL);
213         talloc_free(state);
214         callback(ctdb, status, private_data);
215 }
216
217
218 /* called when child times out */
219 static void ctdb_event_script_timeout(struct event_context *ev, struct timed_event *te, 
220                                       struct timeval t, void *p)
221 {
222         struct ctdb_event_script_state *state = talloc_get_type(p, struct ctdb_event_script_state);
223         void (*callback)(struct ctdb_context *, int, void *) = state->callback;
224         void *private_data = state->private_data;
225         struct ctdb_context *ctdb = state->ctdb;
226
227         DEBUG(0,("event script timed out\n"));
228         talloc_free(state);
229         callback(ctdb, -1, private_data);
230 }
231
232 /*
233   destroy a running event script
234  */
235 static int event_script_destructor(struct ctdb_event_script_state *state)
236 {
237         DEBUG(0,(__location__ " Sending SIGTERM to child pid:%d\n", state->child));
238         kill(state->child, SIGTERM);
239         waitpid(state->child, NULL, 0);
240         return 0;
241 }
242
243 /*
244   run the event script in the background, calling the callback when 
245   finished
246  */
247 static int ctdb_event_script_callback_v(struct ctdb_context *ctdb, 
248                                         struct timeval timeout,
249                                         TALLOC_CTX *mem_ctx,
250                                         void (*callback)(struct ctdb_context *, int, void *),
251                                         void *private_data,
252                                         const char *fmt, va_list ap)
253 {
254         struct ctdb_event_script_state *state;
255         int ret;
256
257         state = talloc(mem_ctx, struct ctdb_event_script_state);
258         CTDB_NO_MEMORY(ctdb, state);
259
260         state->ctdb = ctdb;
261         state->callback = callback;
262         state->private_data = private_data;
263         
264         ret = pipe(state->fd);
265         if (ret != 0) {
266                 talloc_free(state);
267                 return -1;
268         }
269
270         state->child = fork();
271
272         if (state->child == (pid_t)-1) {
273                 close(state->fd[0]);
274                 close(state->fd[1]);
275                 talloc_free(state);
276                 return -1;
277         }
278
279         if (state->child == 0) {
280                 close(state->fd[0]);
281                 if (ctdb->do_setsched) {
282                         ctdb_restore_scheduler(ctdb);
283                 }
284                 set_close_on_exec(state->fd[1]);
285                 ret = ctdb_event_script_v(ctdb, fmt, ap);
286                 _exit(ret);
287         }
288
289         talloc_set_destructor(state, event_script_destructor);
290
291         close(state->fd[1]);
292
293         event_add_fd(ctdb->ev, state, state->fd[0], EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
294                      ctdb_event_script_handler, state);
295
296         if (!timeval_is_zero(&timeout)) {
297                 event_add_timed(ctdb->ev, state, timeout, ctdb_event_script_timeout, state);
298         }
299
300         return 0;
301 }
302
303
304 /*
305   run the event script in the background, calling the callback when 
306   finished
307  */
308 int ctdb_event_script_callback(struct ctdb_context *ctdb, 
309                                struct timeval timeout,
310                                TALLOC_CTX *mem_ctx,
311                                void (*callback)(struct ctdb_context *, int, void *),
312                                void *private_data,
313                                const char *fmt, ...)
314 {
315         va_list ap;
316         int ret;
317
318         va_start(ap, fmt);
319         ret = ctdb_event_script_callback_v(ctdb, timeout, mem_ctx, callback, private_data, fmt, ap);
320         va_end(ap);
321
322         return ret;
323 }
324
325
326 struct callback_status {
327         bool done;
328         int status;
329 };
330
331 /*
332   called when ctdb_event_script() finishes
333  */
334 static void event_script_callback(struct ctdb_context *ctdb, int status, void *private_data)
335 {
336         struct callback_status *s = (struct callback_status *)private_data;
337         s->done = true;
338         s->status = status;
339 }
340
341 /*
342   run the event script, waiting for it to complete. Used when the caller doesn't want to 
343   continue till the event script has finished.
344  */
345 int ctdb_event_script(struct ctdb_context *ctdb, const char *fmt, ...)
346 {
347         va_list ap;
348         int ret;
349         TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
350         struct callback_status status;
351
352         va_start(ap, fmt);
353         ret = ctdb_event_script_callback_v(ctdb, timeval_zero(), tmp_ctx, event_script_callback, &status, fmt, ap);
354         va_end(ap);
355
356         if (ret != 0) {
357                 talloc_free(tmp_ctx);
358                 return ret;
359         }
360
361         status.status = -1;
362         status.done = false;
363
364         while (status.done == false && event_loop_once(ctdb->ev) == 0) /* noop */;
365
366         talloc_free(tmp_ctx);
367
368         return status.status;
369 }