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"
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
;
44 cluster_mutex_lost_handler_t lost_handler
;
47 struct tevent_timer
*te
;
48 struct tevent_fd
*fde
;
50 struct timeval start_time
;
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
)
75 ctdb_kill(h
->ctdb
, h
->child
, SIGTERM
);
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
);
92 /* Got response from child process so abort timeout */
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. */
103 if (h
->lost_handler
!= NULL
) {
104 h
->lost_handler(h
->lost_data
);
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(TALLOC_CTX
*mem_ctx
,
122 const char *argstring
, char ***argv
)
124 int nargs
, i
, ret
, n
;
125 bool is_command
= false;
130 if (argstring
!= NULL
&& argstring
[0] == '!') {
131 /* This is actually a full command */
133 t
= discard_const(&argstring
[1]);
136 t
= discard_const(argstring
);
139 ret
= strv_split(mem_ctx
, &strv
, t
, " \t");
142 ("Unable to parse mutex helper string \"%s\" (%s)\n",
143 argstring
, strerror(ret
)));
146 n
= strv_count(strv
);
148 args
= talloc_array(mem_ctx
, char *, n
+ (is_command
? 1 : 2));
151 DEBUG(DEBUG_ERR
,(__location__
" out of memory\n"));
158 if (!ctdb_set_helper("cluster mutex helper",
159 cluster_mutex_helper
,
160 sizeof(cluster_mutex_helper
),
161 "CTDB_CLUSTER_MUTEX_HELPER",
163 "ctdb_mutex_fcntl_helper")) {
164 DEBUG(DEBUG_ERR
,("ctdb exiting with error: %s\n",
166 " Unable to set cluster mutex helper\n"));
170 args
[nargs
++] = cluster_mutex_helper
;
174 for (i
= 0; i
< n
; i
++) {
175 /* Don't copy, just keep cmd_args around */
176 t
= strv_next(strv
, t
);
180 /* Make sure last argument is NULL */
187 struct ctdb_cluster_mutex_handle
*
188 ctdb_cluster_mutex(TALLOC_CTX
*mem_ctx
,
189 struct ctdb_context
*ctdb
,
190 const char *argstring
,
192 cluster_mutex_handler_t handler
,
194 cluster_mutex_lost_handler_t lost_handler
,
197 struct ctdb_cluster_mutex_handle
*h
;
199 sigset_t sigset_term
;
202 h
= talloc(mem_ctx
, struct ctdb_cluster_mutex_handle
);
204 DEBUG(DEBUG_ERR
, (__location__
" out of memory\n"));
208 h
->start_time
= timeval_current();
211 h
->have_response
= false;
216 DEBUG(DEBUG_ERR
, (__location__
" Failed to open pipe\n"));
219 set_close_on_exec(h
->fd
[0]);
221 /* Create arguments for lock helper */
222 if (!cluster_mutex_helper_args(h
, argstring
, &args
)) {
229 sigemptyset(&sigset_term
);
230 sigaddset(&sigset_term
, SIGTERM
);
231 ret
= sigprocmask(SIG_BLOCK
, &sigset_term
, NULL
);
233 DBG_WARNING("Failed to block SIGTERM (%d)\n", errno
);
236 h
->child
= ctdb_fork(ctdb
);
237 if (h
->child
== (pid_t
)-1) {
241 ret
= sigprocmask(SIG_UNBLOCK
, &sigset_term
, NULL
);
243 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno
);
249 struct sigaction sa
= {
250 .sa_handler
= SIG_DFL
,
253 ret
= sigaction(SIGTERM
, &sa
, NULL
);
255 DBG_WARNING("Failed to reset signal handler (%d)\n",
259 ret
= sigprocmask(SIG_UNBLOCK
, &sigset_term
, NULL
);
261 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno
);
264 /* Make stdout point to the pipe */
265 close(STDOUT_FILENO
);
266 dup2(h
->fd
[1], STDOUT_FILENO
);
269 execv(args
[0], args
);
271 /* Only happens on error */
272 DEBUG(DEBUG_ERR
, (__location__
"execv() failed\n"));
278 ret
= sigprocmask(SIG_UNBLOCK
, &sigset_term
, NULL
);
280 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno
);
283 DEBUG(DEBUG_DEBUG
, (__location__
" Created PIPE FD:%d\n", h
->fd
[0]));
284 set_close_on_exec(h
->fd
[0]);
289 talloc_set_destructor(h
, cluster_mutex_destructor
);
292 h
->te
= tevent_add_timer(ctdb
->ev
, h
,
293 timeval_current_ofs(timeout
, 0),
294 cluster_mutex_timeout
, h
);
299 h
->fde
= tevent_add_fd(ctdb
->ev
, h
, h
->fd
[0], TEVENT_FD_READ
,
300 cluster_mutex_handler
, (void *)h
);
302 if (h
->fde
== NULL
) {
306 tevent_fd_set_auto_close(h
->fde
);
309 h
->handler
= handler
;
310 h
->private_data
= private_data
;
311 h
->lost_handler
= lost_handler
;
312 h
->lost_data
= lost_data
;