4 Copyright (C) Andrew Tridgell 2007
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.
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.
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/>.
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"
31 const char *script_running;
35 ctdbd sends us a SIGTERM when we should time out the current script
37 static void sigterm(int sig)
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);
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()
51 static int ctdb_event_script_v(struct ctdb_context *ctdb, const char *fmt, va_list ap)
53 char *options, *cmdstr;
57 TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
63 if (setpgid(0,0) != 0) {
64 DEBUG(0,("Failed to create process group for event scripts - %s\n",
70 signal(SIGTERM, sigterm);
72 child_state.start = timeval_current();
73 child_state.script_running = "startup";
76 the service specific event scripts
78 if (stat(ctdb->event_script_dir, &st) != 0 &&
80 DEBUG(0,("No event script directory found at '%s'\n", ctdb->event_script_dir));
85 /* create a tree to store all the script names in */
86 tree = trbt_create(tmp_ctx, 0);
88 /* scan all directory entries and insert all valid scripts into the
91 dir = opendir(ctdb->event_script_dir);
93 DEBUG(0,("Failed to open event script directory '%s'\n", ctdb->event_script_dir));
98 while ((de=readdir(dir)) != NULL) {
103 namlen = strlen(de->d_name);
109 if (de->d_name[namlen-1] == '~') {
110 /* skip files emacs left behind */
114 if (de->d_name[2] != '.') {
118 if (sscanf(de->d_name, "%02u.", &num) != 1) {
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));
128 if (!(st.st_mode & S_IXUSR)) {
129 DEBUG(0,("Event script %s is not executable. Ignoring this event script\n", str));
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);
145 /* fetch the scripts from the tree one by one and execute
148 while ((script=trbt_findfirstarray32(tree, 1)) != NULL) {
150 options = talloc_vasprintf(tmp_ctx, fmt, ap2);
152 CTDB_NO_MEMORY(ctdb, options);
154 cmdstr = talloc_asprintf(tmp_ctx, "%s/%s %s",
155 ctdb->event_script_dir,
157 CTDB_NO_MEMORY(ctdb, cmdstr);
159 DEBUG(DEBUG_INFO,("Executing event script %s\n",cmdstr));
161 child_state.start = timeval_current();
162 child_state.script_running = cmdstr;
164 ret = system(cmdstr);
165 /* if the system() call was successful, translate ret into the
166 return code from the command
169 ret = WEXITSTATUS(ret);
171 /* return an error if the script failed */
173 DEBUG(0,("Event script %s failed with error %d\n", cmdstr, ret));
174 talloc_free(tmp_ctx);
178 /* remove this script from the tree */
182 child_state.start = timeval_current();
183 child_state.script_running = "finished";
185 talloc_free(tmp_ctx);
189 struct ctdb_event_script_state {
190 struct ctdb_context *ctdb;
192 void (*callback)(struct ctdb_context *, int, void *);
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)
201 struct ctdb_event_script_state *state =
202 talloc_get_type(p, struct ctdb_event_script_state);
204 void (*callback)(struct ctdb_context *, int, void *) = state->callback;
205 void *private_data = state->private_data;
206 struct ctdb_context *ctdb = state->ctdb;
208 waitpid(state->child, &status, 0);
210 status = WEXITSTATUS(status);
212 talloc_set_destructor(state, NULL);
214 callback(ctdb, status, private_data);
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)
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;
227 DEBUG(0,("event script timed out\n"));
229 callback(ctdb, -1, private_data);
233 destroy a running event script
235 static int event_script_destructor(struct ctdb_event_script_state *state)
237 DEBUG(0,(__location__ " Sending SIGTERM to child pid:%d\n", state->child));
238 kill(state->child, SIGTERM);
239 waitpid(state->child, NULL, 0);
244 run the event script in the background, calling the callback when
247 static int ctdb_event_script_callback_v(struct ctdb_context *ctdb,
248 struct timeval timeout,
250 void (*callback)(struct ctdb_context *, int, void *),
252 const char *fmt, va_list ap)
254 struct ctdb_event_script_state *state;
257 state = talloc(mem_ctx, struct ctdb_event_script_state);
258 CTDB_NO_MEMORY(ctdb, state);
261 state->callback = callback;
262 state->private_data = private_data;
264 ret = pipe(state->fd);
270 state->child = fork();
272 if (state->child == (pid_t)-1) {
279 if (state->child == 0) {
281 if (ctdb->do_setsched) {
282 ctdb_restore_scheduler(ctdb);
284 set_close_on_exec(state->fd[1]);
285 ret = ctdb_event_script_v(ctdb, fmt, ap);
289 talloc_set_destructor(state, event_script_destructor);
293 event_add_fd(ctdb->ev, state, state->fd[0], EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
294 ctdb_event_script_handler, state);
296 if (!timeval_is_zero(&timeout)) {
297 event_add_timed(ctdb->ev, state, timeout, ctdb_event_script_timeout, state);
305 run the event script in the background, calling the callback when
308 int ctdb_event_script_callback(struct ctdb_context *ctdb,
309 struct timeval timeout,
311 void (*callback)(struct ctdb_context *, int, void *),
313 const char *fmt, ...)
319 ret = ctdb_event_script_callback_v(ctdb, timeout, mem_ctx, callback, private_data, fmt, ap);
326 struct callback_status {
332 called when ctdb_event_script() finishes
334 static void event_script_callback(struct ctdb_context *ctdb, int status, void *private_data)
336 struct callback_status *s = (struct callback_status *)private_data;
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.
345 int ctdb_event_script(struct ctdb_context *ctdb, const char *fmt, ...)
349 TALLOC_CTX *tmp_ctx = talloc_new(ctdb);
350 struct callback_status status;
353 ret = ctdb_event_script_callback_v(ctdb, timeval_zero(), tmp_ctx, event_script_callback, &status, fmt, ap);
357 talloc_free(tmp_ctx);
364 while (status.done == false && event_loop_once(ctdb->ev) == 0) /* noop */;
366 talloc_free(tmp_ctx);
368 return status.status;