s4/script/rodcdns: str type doesn't need decoding
[Samba.git] / ctdb / server / ctdb_cluster_mutex.c
blob2e3cb8112ad6cea6b22a356c59c8fe9a6c976552
1 /*
2 CTDB cluster mutex handling
4 Copyright (C) Andrew Tridgell 2007
5 Copyright (C) Ronnie Sahlberg 2007
6 Copyright (C) Martin Schwenke 2016
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "replace.h"
23 #include "system/network.h"
25 #include <tevent.h>
27 #include "lib/util/debug.h"
28 #include "lib/util/time.h"
29 #include "lib/util/strv.h"
30 #include "lib/util/strv_util.h"
31 #include "lib/util/sys_rw.h"
32 #include "lib/util/blocking.h"
34 #include "ctdb_private.h"
35 #include "common/common.h"
36 #include "common/logging.h"
38 #include "ctdb_cluster_mutex.h"
40 struct ctdb_cluster_mutex_handle {
41 struct ctdb_context *ctdb;
42 cluster_mutex_handler_t handler;
43 void *private_data;
44 cluster_mutex_lost_handler_t lost_handler;
45 void *lost_data;
46 int fd[2];
47 struct tevent_timer *te;
48 struct tevent_fd *fde;
49 pid_t child;
50 struct timeval start_time;
51 bool have_response;
54 static void cluster_mutex_timeout(struct tevent_context *ev,
55 struct tevent_timer *te,
56 struct timeval t, void *private_data)
58 struct ctdb_cluster_mutex_handle *h =
59 talloc_get_type(private_data, struct ctdb_cluster_mutex_handle);
60 double latency = timeval_elapsed(&h->start_time);
62 if (h->handler != NULL) {
63 h->handler('2', latency, h->private_data);
68 /* When the handle is freed it causes any child holding the mutex to
69 * be killed, thus freeing the mutex */
70 static int cluster_mutex_destructor(struct ctdb_cluster_mutex_handle *h)
72 if (h->fd[0] != -1) {
73 h->fd[0] = -1;
75 ctdb_kill(h->ctdb, h->child, SIGTERM);
76 return 0;
79 /* this is called when the client process has completed ctdb_recovery_lock()
80 and has written data back to us through the pipe.
82 static void cluster_mutex_handler(struct tevent_context *ev,
83 struct tevent_fd *fde,
84 uint16_t flags, void *private_data)
86 struct ctdb_cluster_mutex_handle *h=
87 talloc_get_type(private_data, struct ctdb_cluster_mutex_handle);
88 double latency = timeval_elapsed(&h->start_time);
89 char c = '0';
90 int ret;
92 /* Got response from child process so abort timeout */
93 TALLOC_FREE(h->te);
95 ret = sys_read(h->fd[0], &c, 1);
97 /* Don't call the handler more than once. It only exists to
98 * process the initial response from the helper. */
99 if (h->have_response) {
100 /* Only deal with EOF due to process exit. Silently
101 * ignore any other output. */
102 if (ret == 0) {
103 if (h->lost_handler != NULL) {
104 h->lost_handler(h->lost_data);
107 return;
109 h->have_response = true;
111 /* If the child wrote status then just pass it to the handler.
112 * If no status was written then this is an unexpected error
113 * so pass generic error code to handler. */
114 if (h->handler != NULL) {
115 h->handler(ret == 1 ? c : '3', latency, h->private_data);
119 static char cluster_mutex_helper[PATH_MAX+1] = "";
121 static bool cluster_mutex_helper_args_file(TALLOC_CTX *mem_ctx,
122 const char *argstring,
123 char ***argv)
125 bool ok;
126 char **args = NULL;
128 ok = ctdb_set_helper("cluster mutex helper",
129 cluster_mutex_helper,
130 sizeof(cluster_mutex_helper),
131 "CTDB_CLUSTER_MUTEX_HELPER",
132 CTDB_HELPER_BINDIR,
133 "ctdb_mutex_fcntl_helper");
134 if (! ok) {
135 DBG_ERR("ctdb exiting with error: "
136 "Unable to set cluster mutex helper\n");
137 exit(1);
141 /* Array includes default helper, file and NULL */
142 args = talloc_array(mem_ctx, char *, 3);
143 if (args == NULL) {
144 DBG_ERR("Memory allocation error\n");
145 return false;
148 args[0] = cluster_mutex_helper;
150 args[1] = talloc_strdup(args, argstring);
151 if (args[1] == NULL) {
152 DBG_ERR("Memory allocation error\n");
153 return false;
156 args[2] = NULL;
158 *argv = args;
159 return true;
162 static bool cluster_mutex_helper_args_cmd(TALLOC_CTX *mem_ctx,
163 const char *argstring,
164 char ***argv)
166 int i, ret, n;
167 char **args = NULL;
168 char *strv = NULL;
169 char *t = NULL;
171 ret = strv_split(mem_ctx, &strv, argstring, " \t");
172 if (ret != 0) {
173 D_ERR("Unable to parse mutex helper command \"%s\" (%s)\n",
174 argstring,
175 strerror(ret));
176 return false;
178 n = strv_count(strv);
180 /* Extra slot for NULL */
181 args = talloc_array(mem_ctx, char *, n + 1);
182 if (args == NULL) {
183 DBG_ERR("Memory allocation error\n");
184 return false;
187 talloc_steal(args, strv);
189 t = NULL;
190 for (i = 0 ; i < n; i++) {
191 t = strv_next(strv, t);
192 args[i] = t;
195 args[n] = NULL;
197 *argv = args;
198 return true;
201 static bool cluster_mutex_helper_args(TALLOC_CTX *mem_ctx,
202 const char *argstring,
203 char ***argv)
205 bool ok;
207 if (argstring != NULL && argstring[0] == '!') {
208 ok = cluster_mutex_helper_args_cmd(mem_ctx, &argstring[1], argv);
209 } else {
210 ok = cluster_mutex_helper_args_file(mem_ctx, argstring, argv);
213 return ok;
216 struct ctdb_cluster_mutex_handle *
217 ctdb_cluster_mutex(TALLOC_CTX *mem_ctx,
218 struct ctdb_context *ctdb,
219 const char *argstring,
220 int timeout,
221 cluster_mutex_handler_t handler,
222 void *private_data,
223 cluster_mutex_lost_handler_t lost_handler,
224 void *lost_data)
226 struct ctdb_cluster_mutex_handle *h;
227 char **args;
228 sigset_t sigset_term;
229 int ret;
231 h = talloc(mem_ctx, struct ctdb_cluster_mutex_handle);
232 if (h == NULL) {
233 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
234 return NULL;
237 h->start_time = timeval_current();
238 h->fd[0] = -1;
239 h->fd[1] = -1;
240 h->have_response = false;
242 ret = pipe(h->fd);
243 if (ret != 0) {
244 talloc_free(h);
245 DEBUG(DEBUG_ERR, (__location__ " Failed to open pipe\n"));
246 return NULL;
248 set_close_on_exec(h->fd[0]);
250 /* Create arguments for lock helper */
251 if (!cluster_mutex_helper_args(h, argstring, &args)) {
252 close(h->fd[0]);
253 close(h->fd[1]);
254 talloc_free(h);
255 return NULL;
258 sigemptyset(&sigset_term);
259 sigaddset(&sigset_term, SIGTERM);
260 ret = sigprocmask(SIG_BLOCK, &sigset_term, NULL);
261 if (ret != 0) {
262 DBG_WARNING("Failed to block SIGTERM (%d)\n", errno);
265 h->child = ctdb_fork(ctdb);
266 if (h->child == (pid_t)-1) {
267 close(h->fd[0]);
268 close(h->fd[1]);
269 talloc_free(h);
270 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
271 if (ret != 0) {
272 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
274 return NULL;
277 if (h->child == 0) {
278 struct sigaction sa = {
279 .sa_handler = SIG_DFL,
282 ret = sigaction(SIGTERM, &sa, NULL);
283 if (ret != 0) {
284 DBG_WARNING("Failed to reset signal handler (%d)\n",
285 errno);
288 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
289 if (ret != 0) {
290 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
293 /* Make stdout point to the pipe */
294 close(STDOUT_FILENO);
295 dup2(h->fd[1], STDOUT_FILENO);
296 close(h->fd[1]);
298 execv(args[0], args);
300 /* Only happens on error */
301 DEBUG(DEBUG_ERR, (__location__ "execv() failed\n"));
302 _exit(1);
305 /* Parent */
307 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
308 if (ret != 0) {
309 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
312 DEBUG(DEBUG_DEBUG, (__location__ " Created PIPE FD:%d\n", h->fd[0]));
313 set_close_on_exec(h->fd[0]);
315 close(h->fd[1]);
316 h->fd[1] = -1;
318 talloc_set_destructor(h, cluster_mutex_destructor);
320 if (timeout != 0) {
321 h->te = tevent_add_timer(ctdb->ev, h,
322 timeval_current_ofs(timeout, 0),
323 cluster_mutex_timeout, h);
324 } else {
325 h->te = NULL;
328 h->fde = tevent_add_fd(ctdb->ev, h, h->fd[0], TEVENT_FD_READ,
329 cluster_mutex_handler, (void *)h);
331 if (h->fde == NULL) {
332 talloc_free(h);
333 return NULL;
335 tevent_fd_set_auto_close(h->fde);
337 h->ctdb = ctdb;
338 h->handler = handler;
339 h->private_data = private_data;
340 h->lost_handler = lost_handler;
341 h->lost_data = lost_data;
343 return h;