7a2377f945de10771e4ee47fc91edfdac5a8a18e
[vlendec/samba-autobuild/.git] / ctdb / 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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "system/wait.h"
24 #include "../include/ctdb_private.h"
25 #include "lib/events/events.h"
26
27 /*
28   run the event script - varargs version
29  */
30 static int ctdb_event_script_v(struct ctdb_context *ctdb, const char *fmt, va_list ap)
31 {
32         char *options, *cmdstr;
33         int ret;
34         va_list ap2;
35         struct stat st;
36
37         if (stat(ctdb->takeover.event_script, &st) != 0 && 
38             errno == ENOENT) {
39                 DEBUG(0,("No event script found at '%s'\n", ctdb->takeover.event_script));
40                 return 0;
41         }
42
43         va_copy(ap2, ap);
44         options  = talloc_vasprintf(ctdb, fmt, ap2);
45         va_end(ap2);
46         CTDB_NO_MEMORY(ctdb, options);
47
48         cmdstr = talloc_asprintf(ctdb, "%s %s", ctdb->takeover.event_script, options);
49         CTDB_NO_MEMORY(ctdb, cmdstr);
50
51         ret = system(cmdstr);
52         if (ret != -1) {
53                 ret = WEXITSTATUS(ret);
54         }
55
56         talloc_free(cmdstr);
57         talloc_free(options);
58
59         return ret;
60 }
61
62 /*
63   run the event script
64  */
65 int ctdb_event_script(struct ctdb_context *ctdb, const char *fmt, ...)
66 {
67         va_list ap;
68         int ret;
69
70         va_start(ap, fmt);
71         ret = ctdb_event_script_v(ctdb, fmt, ap);
72         va_end(ap);
73
74         return ret;
75 }
76
77
78 struct ctdb_event_script_state {
79         struct ctdb_context *ctdb;
80         pid_t child;
81         void (*callback)(struct ctdb_context *, int, void *);
82         int fd[2];
83         void *private_data;
84 };
85
86 /* called when child is finished */
87 static void ctdb_event_script_handler(struct event_context *ev, struct fd_event *fde, 
88                                       uint16_t flags, void *p)
89 {
90         struct ctdb_event_script_state *state = 
91                 talloc_get_type(p, struct ctdb_event_script_state);
92         int status = -1;
93         void (*callback)(struct ctdb_context *, int, void *) = state->callback;
94         void *private_data = state->private_data;
95         struct ctdb_context *ctdb = state->ctdb;
96
97         waitpid(state->child, &status, 0);
98         if (status != -1) {
99                 status = WEXITSTATUS(status);
100         }
101         talloc_set_destructor(state, NULL);
102         talloc_free(state);
103         callback(ctdb, status, private_data);
104 }
105
106
107 /* called when child times out */
108 static void ctdb_event_script_timeout(struct event_context *ev, struct timed_event *te, 
109                                       struct timeval t, void *p)
110 {
111         struct ctdb_event_script_state *state = talloc_get_type(p, struct ctdb_event_script_state);
112         void (*callback)(struct ctdb_context *, int, void *) = state->callback;
113         void *private_data = state->private_data;
114         struct ctdb_context *ctdb = state->ctdb;
115
116         DEBUG(0,("event script timed out\n"));
117         talloc_free(state);
118         callback(ctdb, -1, private_data);
119 }
120
121 /*
122   destroy a running event script
123  */
124 static int event_script_destructor(struct ctdb_event_script_state *state)
125 {
126         kill(state->child, SIGKILL);
127         waitpid(state->child, NULL, 0);
128         return 0;
129 }
130
131 /*
132   run the event script in the background, calling the callback when 
133   finished
134  */
135 int ctdb_event_script_callback(struct ctdb_context *ctdb, 
136                                struct timeval timeout,
137                                TALLOC_CTX *mem_ctx,
138                                void (*callback)(struct ctdb_context *, int, void *),
139                                void *private_data,
140                                const char *fmt, ...)
141 {
142         struct ctdb_event_script_state *state;
143         va_list ap;
144         int ret;
145
146         state = talloc(mem_ctx, struct ctdb_event_script_state);
147         CTDB_NO_MEMORY(ctdb, state);
148
149         state->ctdb = ctdb;
150         state->callback = callback;
151         state->private_data = private_data;
152         
153         ret = pipe(state->fd);
154         if (ret != 0) {
155                 talloc_free(state);
156                 return -1;
157         }
158
159         state->child = fork();
160
161         if (state->child == (pid_t)-1) {
162                 close(state->fd[0]);
163                 close(state->fd[1]);
164                 talloc_free(state);
165                 return -1;
166         }
167
168         if (state->child == 0) {
169                 close(state->fd[0]);
170                 ctdb_set_realtime(false);
171                 set_close_on_exec(state->fd[1]);
172                 va_start(ap, fmt);
173                 ret = ctdb_event_script_v(ctdb, fmt, ap);
174                 va_end(ap);
175                 _exit(ret);
176         }
177
178         talloc_set_destructor(state, event_script_destructor);
179
180         close(state->fd[1]);
181
182         event_add_fd(ctdb->ev, state, state->fd[0], EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
183                      ctdb_event_script_handler, state);
184
185         if (!timeval_is_zero(&timeout)) {
186                 event_add_timed(ctdb->ev, state, timeout, ctdb_event_script_timeout, state);
187         }
188
189         return 0;
190 }
191
192