merge from tridge
[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
26 /*
27   run the event script - varargs version
28  */
29 static int ctdb_event_script_v(struct ctdb_context *ctdb, const char *fmt, va_list ap)
30 {
31         char *options, *cmdstr;
32         int ret;
33         va_list ap2;
34         struct stat st;
35
36         if (stat(ctdb->takeover.event_script, &st) != 0 && 
37             errno == ENOENT) {
38                 DEBUG(0,("No event script found at '%s'\n", ctdb->takeover.event_script));
39                 return 0;
40         }
41
42         va_copy(ap2, ap);
43         options  = talloc_vasprintf(ctdb, fmt, ap2);
44         va_end(ap2);
45         CTDB_NO_MEMORY(ctdb, options);
46
47         cmdstr = talloc_asprintf(ctdb, "%s %s", ctdb->takeover.event_script, options);
48         CTDB_NO_MEMORY(ctdb, cmdstr);
49
50         ret = system(cmdstr);
51         if (ret != -1) {
52                 ret = WEXITSTATUS(ret);
53         }
54
55         talloc_free(cmdstr);
56         talloc_free(options);
57
58         return ret;
59 }
60
61 struct ctdb_event_script_state {
62         struct ctdb_context *ctdb;
63         pid_t child;
64         void (*callback)(struct ctdb_context *, int, void *);
65         int fd[2];
66         void *private_data;
67 };
68
69 /* called when child is finished */
70 static void ctdb_event_script_handler(struct event_context *ev, struct fd_event *fde, 
71                                       uint16_t flags, void *p)
72 {
73         struct ctdb_event_script_state *state = 
74                 talloc_get_type(p, struct ctdb_event_script_state);
75         int status = -1;
76         void (*callback)(struct ctdb_context *, int, void *) = state->callback;
77         void *private_data = state->private_data;
78         struct ctdb_context *ctdb = state->ctdb;
79
80         waitpid(state->child, &status, 0);
81         if (status != -1) {
82                 status = WEXITSTATUS(status);
83         }
84         talloc_set_destructor(state, NULL);
85         talloc_free(state);
86         callback(ctdb, status, private_data);
87 }
88
89
90 /* called when child times out */
91 static void ctdb_event_script_timeout(struct event_context *ev, struct timed_event *te, 
92                                       struct timeval t, void *p)
93 {
94         struct ctdb_event_script_state *state = talloc_get_type(p, struct ctdb_event_script_state);
95         void (*callback)(struct ctdb_context *, int, void *) = state->callback;
96         void *private_data = state->private_data;
97         struct ctdb_context *ctdb = state->ctdb;
98
99         DEBUG(0,("event script timed out\n"));
100         talloc_free(state);
101         callback(ctdb, -1, private_data);
102 }
103
104 /*
105   destroy a running event script
106  */
107 static int event_script_destructor(struct ctdb_event_script_state *state)
108 {
109         kill(state->child, SIGKILL);
110         waitpid(state->child, NULL, 0);
111         return 0;
112 }
113
114 /*
115   run the event script in the background, calling the callback when 
116   finished
117  */
118 static int ctdb_event_script_callback_v(struct ctdb_context *ctdb, 
119                                         struct timeval timeout,
120                                         TALLOC_CTX *mem_ctx,
121                                         void (*callback)(struct ctdb_context *, int, void *),
122                                         void *private_data,
123                                         const char *fmt, va_list ap)
124 {
125         struct ctdb_event_script_state *state;
126         int ret;
127
128         state = talloc(mem_ctx, struct ctdb_event_script_state);
129         CTDB_NO_MEMORY(ctdb, state);
130
131         state->ctdb = ctdb;
132         state->callback = callback;
133         state->private_data = private_data;
134         
135         ret = pipe(state->fd);
136         if (ret != 0) {
137                 talloc_free(state);
138                 return -1;
139         }
140
141         state->child = fork();
142
143         if (state->child == (pid_t)-1) {
144                 close(state->fd[0]);
145                 close(state->fd[1]);
146                 talloc_free(state);
147                 return -1;
148         }
149
150         if (state->child == 0) {
151                 close(state->fd[0]);
152                 if (ctdb->do_setsched) {
153                         ctdb_restore_scheduler(ctdb);
154                 }
155                 set_close_on_exec(state->fd[1]);
156                 ret = ctdb_event_script_v(ctdb, fmt, ap);
157                 _exit(ret);
158         }
159
160         talloc_set_destructor(state, event_script_destructor);
161
162         close(state->fd[1]);
163
164         event_add_fd(ctdb->ev, state, state->fd[0], EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
165                      ctdb_event_script_handler, state);
166
167         if (!timeval_is_zero(&timeout)) {
168                 event_add_timed(ctdb->ev, state, timeout, ctdb_event_script_timeout, state);
169         }
170
171         return 0;
172 }
173
174
175 /*
176   run the event script in the background, calling the callback when 
177   finished
178  */
179 int ctdb_event_script_callback(struct ctdb_context *ctdb, 
180                                struct timeval timeout,
181                                TALLOC_CTX *mem_ctx,
182                                void (*callback)(struct ctdb_context *, int, void *),
183                                void *private_data,
184                                const char *fmt, ...)
185 {
186         va_list ap;
187         int ret;
188
189         va_start(ap, fmt);
190         ret = ctdb_event_script_callback_v(ctdb, timeout, mem_ctx, callback, private_data, fmt, ap);
191         va_end(ap);
192
193         return ret;
194 }
195
196
197 struct callback_status {
198         bool done;
199         int status;
200 };
201
202 /*
203   called when ctdb_event_script() finishes
204  */
205 static void event_script_callback(struct ctdb_context *ctdb, int status, void *private_data)
206 {
207         struct callback_status *s = (struct callback_status *)private_data;
208         s->done = true;
209         s->status = status;
210 }
211
212 /*
213   run the event script, waiting for it to complete. Used when the caller doesn't want to 
214   continue till the event script has finished.
215  */
216 int ctdb_event_script(struct ctdb_context *ctdb, const char *fmt, ...)
217 {
218         va_list ap;
219         int ret;
220         TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
221         struct callback_status status;
222
223         va_start(ap, fmt);
224         ret = ctdb_event_script_callback_v(ctdb, timeval_zero(), tmp_ctx, event_script_callback, &status, fmt, ap);
225         va_end(ap);
226
227         if (ret != 0) {
228                 talloc_free(tmp_ctx);
229                 return ret;
230         }
231
232         status.status = -1;
233         status.done = false;
234
235         while (status.done == false && event_loop_once(ctdb->ev) == 0) /* noop */;
236
237         talloc_free(tmp_ctx);
238
239         return status.status;
240 }