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