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/>.
23 #include "system/network.h"
24 #include "system/filesys.h"
28 #include "lib/util/debug.h"
29 #include "lib/util/time.h"
30 #include "lib/util/strv.h"
31 #include "lib/util/strv_util.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/blocking.h"
35 #include "ctdb_private.h"
37 #include "ctdb_cluster_mutex.h"
39 struct ctdb_cluster_mutex_handle
{
40 struct ctdb_context
*ctdb
;
41 cluster_mutex_handler_t handler
;
43 cluster_mutex_lost_handler_t lost_handler
;
46 struct tevent_timer
*te
;
47 struct tevent_fd
*fde
;
49 struct timeval start_time
;
53 static void cluster_mutex_timeout(struct tevent_context
*ev
,
54 struct tevent_timer
*te
,
55 struct timeval t
, void *private_data
)
57 struct ctdb_cluster_mutex_handle
*h
=
58 talloc_get_type(private_data
, struct ctdb_cluster_mutex_handle
);
59 double latency
= timeval_elapsed(&h
->start_time
);
61 if (h
->handler
!= NULL
) {
62 h
->handler('2', latency
, h
->private_data
);
67 /* When the handle is freed it causes any child holding the mutex to
68 * be killed, thus freeing the mutex */
69 static int cluster_mutex_destructor(struct ctdb_cluster_mutex_handle
*h
)
74 ctdb_kill(h
->ctdb
, h
->child
, SIGTERM
);
78 /* this is called when the client process has completed ctdb_recovery_lock()
79 and has written data back to us through the pipe.
81 static void cluster_mutex_handler(struct tevent_context
*ev
,
82 struct tevent_fd
*fde
,
83 uint16_t flags
, void *private_data
)
85 struct ctdb_cluster_mutex_handle
*h
=
86 talloc_get_type(private_data
, struct ctdb_cluster_mutex_handle
);
87 double latency
= timeval_elapsed(&h
->start_time
);
91 /* Got response from child process so abort timeout */
94 ret
= sys_read(h
->fd
[0], &c
, 1);
96 /* Don't call the handler more than once. It only exists to
97 * process the initial response from the helper. */
98 if (h
->have_response
) {
99 /* Only deal with EOF due to process exit. Silently
100 * ignore any other output. */
102 if (h
->lost_handler
!= NULL
) {
103 h
->lost_handler(h
->lost_data
);
108 h
->have_response
= true;
110 /* If the child wrote status then just pass it to the handler.
111 * If no status was written then this is an unexpected error
112 * so pass generic error code to handler. */
113 if (h
->handler
!= NULL
) {
114 h
->handler(ret
== 1 ? c
: '3', latency
, h
->private_data
);
118 static char cluster_mutex_helper
[PATH_MAX
+1] = "";
120 static bool cluster_mutex_helper_args_file(TALLOC_CTX
*mem_ctx
,
121 const char *argstring
,
125 size_t size
= sizeof(cluster_mutex_helper
);
130 if (cluster_mutex_helper
[0] != '\0') {
134 t
= getenv("CTDB_CLUSTER_MUTEX_HELPER");
138 len
= strlcpy(cluster_mutex_helper
, t
, size
);
140 DBG_ERR("error: CTDB_CLUSTER_MUTEX_HELPER too long\n");
144 ret
= snprintf(cluster_mutex_helper
,
148 "ctdb_mutex_fcntl_helper");
149 if (ret
< 0 || (size_t)ret
>= size
) {
150 D_ERR("Unable to set cluster mutex helper - "
156 ret
= stat(cluster_mutex_helper
, &st
);
158 D_ERR("Unable to set cluster mutex helper \"%s\" - %s\n",
159 cluster_mutex_helper
,
164 if ((st
.st_mode
& S_IXUSR
) == 0) {
165 D_ERR("Unable to set cluster_mutex helper \"%s\" - "
167 cluster_mutex_helper
);
171 D_NOTICE("Set cluster mutex helper to \"%s\"\n", cluster_mutex_helper
);
175 /* Array includes default helper, file and NULL */
176 args
= talloc_array(mem_ctx
, char *, 3);
178 DBG_ERR("Memory allocation error\n");
182 args
[0] = cluster_mutex_helper
;
184 args
[1] = talloc_strdup(args
, argstring
);
185 if (args
[1] == NULL
) {
186 DBG_ERR("Memory allocation error\n");
196 static bool cluster_mutex_helper_args_cmd(TALLOC_CTX
*mem_ctx
,
197 const char *argstring
,
205 ret
= strv_split(mem_ctx
, &strv
, argstring
, " \t");
207 D_ERR("Unable to parse mutex helper command \"%s\" (%s)\n",
212 n
= strv_count(strv
);
214 D_ERR("Mutex helper command is empty \"%s\"\n", argstring
);
218 /* Extra slot for NULL */
219 args
= talloc_array(mem_ctx
, char *, n
+ 1);
221 DBG_ERR("Memory allocation error\n");
225 talloc_steal(args
, strv
);
228 for (i
= 0 ; i
< n
; i
++) {
229 t
= strv_next(strv
, t
);
239 static bool cluster_mutex_helper_args(TALLOC_CTX
*mem_ctx
,
240 const char *argstring
,
245 if (argstring
!= NULL
&& argstring
[0] == '!') {
246 ok
= cluster_mutex_helper_args_cmd(mem_ctx
, &argstring
[1], argv
);
248 ok
= cluster_mutex_helper_args_file(mem_ctx
, argstring
, argv
);
254 struct ctdb_cluster_mutex_handle
*
255 ctdb_cluster_mutex(TALLOC_CTX
*mem_ctx
,
256 struct ctdb_context
*ctdb
,
257 const char *argstring
,
259 cluster_mutex_handler_t handler
,
261 cluster_mutex_lost_handler_t lost_handler
,
264 struct ctdb_cluster_mutex_handle
*h
;
266 sigset_t sigset_term
;
269 h
= talloc(mem_ctx
, struct ctdb_cluster_mutex_handle
);
271 DBG_ERR("out of memory\n");
275 h
->start_time
= timeval_current();
278 h
->have_response
= false;
283 DBG_ERR("Failed to open pipe\n");
286 set_close_on_exec(h
->fd
[0]);
288 /* Create arguments for lock helper */
289 if (!cluster_mutex_helper_args(h
, argstring
, &args
)) {
296 sigemptyset(&sigset_term
);
297 sigaddset(&sigset_term
, SIGTERM
);
298 ret
= sigprocmask(SIG_BLOCK
, &sigset_term
, NULL
);
300 DBG_WARNING("Failed to block SIGTERM (%d)\n", errno
);
303 h
->child
= ctdb_fork(ctdb
);
304 if (h
->child
== (pid_t
)-1) {
308 ret
= sigprocmask(SIG_UNBLOCK
, &sigset_term
, NULL
);
310 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno
);
316 struct sigaction sa
= {
317 .sa_handler
= SIG_DFL
,
320 ret
= sigaction(SIGTERM
, &sa
, NULL
);
322 DBG_WARNING("Failed to reset signal handler (%d)\n",
326 ret
= sigprocmask(SIG_UNBLOCK
, &sigset_term
, NULL
);
328 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno
);
331 /* Make stdout point to the pipe */
332 close(STDOUT_FILENO
);
333 dup2(h
->fd
[1], STDOUT_FILENO
);
336 execv(args
[0], args
);
338 /* Only happens on error */
339 DBG_ERR("execv() failed\n");
345 ret
= sigprocmask(SIG_UNBLOCK
, &sigset_term
, NULL
);
347 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno
);
350 DBG_DEBUG("Created PIPE FD:%d\n", h
->fd
[0]);
351 set_close_on_exec(h
->fd
[0]);
356 talloc_set_destructor(h
, cluster_mutex_destructor
);
359 h
->te
= tevent_add_timer(ctdb
->ev
, h
,
360 timeval_current_ofs(timeout
, 0),
361 cluster_mutex_timeout
, h
);
366 h
->fde
= tevent_add_fd(ctdb
->ev
, h
, h
->fd
[0], TEVENT_FD_READ
,
367 cluster_mutex_handler
, (void *)h
);
369 if (h
->fde
== NULL
) {
373 tevent_fd_set_auto_close(h
->fde
);
376 h
->handler
= handler
;
377 h
->private_data
= private_data
;
378 h
->lost_handler
= lost_handler
;
379 h
->lost_data
= lost_data
;