1 /*
2 CTDB cluster mutex handling
3
4 Copyright (C) Andrew Tridgell 2007
5 Copyright (C) Ronnie Sahlberg 2007
6 Copyright (C) Martin Schwenke 2016
7
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.
12
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.
17
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/>.
20 */
21
22 #include "replace.h"
23 #include "system/network.h"
24 #include "system/filesys.h"
25
26 #include <tevent.h>
27
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"
34
35 #include "ctdb_private.h"
36
37 #include "ctdb_cluster_mutex.h"
38
39 struct ctdb_cluster_mutex_handle {
40 struct ctdb_context *ctdb;
41 cluster_mutex_handler_t handler;
42 void *private_data;
43 cluster_mutex_lost_handler_t lost_handler;
44 void *lost_data;
45 int fd[2];
46 struct tevent_timer *te;
47 struct tevent_fd *fde;
48 pid_t child;
49 struct timeval start_time;
50 bool have_response;
51 };
52
cluster_mutex_timeout(struct tevent_context * ev,struct tevent_timer * te,struct timeval t,void * private_data)53 static void cluster_mutex_timeout(struct tevent_context *ev,
54 struct tevent_timer *te,
55 struct timeval t, void *private_data)
56 {
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);
60
61 if (h->handler != NULL) {
62 h->handler('2', latency, h->private_data);
63 }
64 }
65
66
67 /* When the handle is freed it causes any child holding the mutex to
68 * be killed, thus freeing the mutex */
cluster_mutex_destructor(struct ctdb_cluster_mutex_handle * h)69 static int cluster_mutex_destructor(struct ctdb_cluster_mutex_handle *h)
70 {
71 if (h->fd[0] != -1) {
72 h->fd[0] = -1;
73 }
74 ctdb_kill(h->ctdb, h->child, SIGTERM);
75 return 0;
76 }
77
78 /* this is called when the client process has completed ctdb_recovery_lock()
79 and has written data back to us through the pipe.
80 */
cluster_mutex_handler(struct tevent_context * ev,struct tevent_fd * fde,uint16_t flags,void * private_data)81 static void cluster_mutex_handler(struct tevent_context *ev,
82 struct tevent_fd *fde,
83 uint16_t flags, void *private_data)
84 {
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);
88 char c = '0';
89 int ret;
90
91 /* Got response from child process so abort timeout */
92 TALLOC_FREE(h->te);
93
94 ret = sys_read(h->fd[0], &c, 1);
95
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. */
101 if (ret == 0) {
102 if (h->lost_handler != NULL) {
103 h->lost_handler(h->lost_data);
104 }
105 }
106 return;
107 }
108 h->have_response = true;
109
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);
115 }
116 }
117
118 static char cluster_mutex_helper[PATH_MAX+1] = "";
119
cluster_mutex_helper_args_file(TALLOC_CTX * mem_ctx,const char * argstring,char *** argv)120 static bool cluster_mutex_helper_args_file(TALLOC_CTX *mem_ctx,
121 const char *argstring,
122 char ***argv)
123 {
124 struct stat st;
125 size_t size = sizeof(cluster_mutex_helper);
126 const char *t;
127 char **args = NULL;
128 int ret;
129
130 if (cluster_mutex_helper[0] != '\0') {
131 goto helper_done;
132 }
133
134 t = getenv("CTDB_CLUSTER_MUTEX_HELPER");
135 if (t != NULL) {
136 size_t len;
137
138 len = strlcpy(cluster_mutex_helper, t, size);
139 if (len >= size) {
140 DBG_ERR("error: CTDB_CLUSTER_MUTEX_HELPER too long\n");
141 exit(1);
142 }
143 } else {
144 ret = snprintf(cluster_mutex_helper,
145 size,
146 "%s/%s",
147 CTDB_HELPER_BINDIR,
148 "ctdb_mutex_fcntl_helper");
149 if (ret < 0 || (size_t)ret >= size) {
150 D_ERR("Unable to set cluster mutex helper - "
151 "path too long\n");
152 exit(1);
153 }
154 }
155
156 ret = stat(cluster_mutex_helper, &st);
157 if (ret != 0) {
158 D_ERR("Unable to set cluster mutex helper \"%s\" - %s\n",
159 cluster_mutex_helper,
160 strerror(errno));
161 exit(1);
162 }
163
164 if ((st.st_mode & S_IXUSR) == 0) {
165 D_ERR("Unable to set cluster_mutex helper \"%s\" - "
166 "not executable\n",
167 cluster_mutex_helper);
168 exit(1);
169 }
170
171 D_NOTICE("Set cluster mutex helper to \"%s\"\n", cluster_mutex_helper);
172
173 helper_done:
174
175 /* Array includes default helper, file and NULL */
176 args = talloc_array(mem_ctx, char *, 3);
177 if (args == NULL) {
178 DBG_ERR("Memory allocation error\n");
179 return false;
180 }
181
182 args[0] = cluster_mutex_helper;
183
184 args[1] = talloc_strdup(args, argstring);
185 if (args[1] == NULL) {
186 DBG_ERR("Memory allocation error\n");
187 return false;
188 }
189
190 args[2] = NULL;
191
192 *argv = args;
193 return true;
194 }
195
cluster_mutex_helper_args_cmd(TALLOC_CTX * mem_ctx,const char * argstring,char *** argv)196 static bool cluster_mutex_helper_args_cmd(TALLOC_CTX *mem_ctx,
197 const char *argstring,
198 char ***argv)
199 {
200 int i, ret, n;
201 char **args = NULL;
202 char *strv = NULL;
203 char *t = NULL;
204
205 ret = strv_split(mem_ctx, &strv, argstring, " \t");
206 if (ret != 0) {
207 D_ERR("Unable to parse mutex helper command \"%s\" (%s)\n",
208 argstring,
209 strerror(ret));
210 return false;
211 }
212 n = strv_count(strv);
213 if (n == 0) {
214 D_ERR("Mutex helper command is empty \"%s\"\n", argstring);
215 return false;
216 }
217
218 /* Extra slot for NULL */
219 args = talloc_array(mem_ctx, char *, n + 1);
220 if (args == NULL) {
221 DBG_ERR("Memory allocation error\n");
222 return false;
223 }
224
225 talloc_steal(args, strv);
226
227 t = NULL;
228 for (i = 0 ; i < n; i++) {
229 t = strv_next(strv, t);
230 args[i] = t;
231 }
232
233 args[n] = NULL;
234
235 *argv = args;
236 return true;
237 }
238
cluster_mutex_helper_args(TALLOC_CTX * mem_ctx,const char * argstring,char *** argv)239 static bool cluster_mutex_helper_args(TALLOC_CTX *mem_ctx,
240 const char *argstring,
241 char ***argv)
242 {
243 bool ok;
244
245 if (argstring != NULL && argstring[0] == '!') {
246 ok = cluster_mutex_helper_args_cmd(mem_ctx, &argstring[1], argv);
247 } else {
248 ok = cluster_mutex_helper_args_file(mem_ctx, argstring, argv);
249 }
250
251 return ok;
252 }
253
254 struct ctdb_cluster_mutex_handle *
ctdb_cluster_mutex(TALLOC_CTX * mem_ctx,struct ctdb_context * ctdb,const char * argstring,int timeout,cluster_mutex_handler_t handler,void * private_data,cluster_mutex_lost_handler_t lost_handler,void * lost_data)255 ctdb_cluster_mutex(TALLOC_CTX *mem_ctx,
256 struct ctdb_context *ctdb,
257 const char *argstring,
258 int timeout,
259 cluster_mutex_handler_t handler,
260 void *private_data,
261 cluster_mutex_lost_handler_t lost_handler,
262 void *lost_data)
263 {
264 struct ctdb_cluster_mutex_handle *h;
265 char **args;
266 sigset_t sigset_term;
267 int ret;
268
269 h = talloc(mem_ctx, struct ctdb_cluster_mutex_handle);
270 if (h == NULL) {
271 DBG_ERR("out of memory\n");
272 return NULL;
273 }
274
275 h->start_time = timeval_current();
276 h->fd[0] = -1;
277 h->fd[1] = -1;
278 h->have_response = false;
279
280 ret = pipe(h->fd);
281 if (ret != 0) {
282 talloc_free(h);
283 DBG_ERR("Failed to open pipe\n");
284 return NULL;
285 }
286 set_close_on_exec(h->fd[0]);
287
288 /* Create arguments for lock helper */
289 if (!cluster_mutex_helper_args(h, argstring, &args)) {
290 close(h->fd[0]);
291 close(h->fd[1]);
292 talloc_free(h);
293 return NULL;
294 }
295
296 sigemptyset(&sigset_term);
297 sigaddset(&sigset_term, SIGTERM);
298 ret = sigprocmask(SIG_BLOCK, &sigset_term, NULL);
299 if (ret != 0) {
300 DBG_WARNING("Failed to block SIGTERM (%d)\n", errno);
301 }
302
303 h->child = ctdb_fork(ctdb);
304 if (h->child == (pid_t)-1) {
305 close(h->fd[0]);
306 close(h->fd[1]);
307 talloc_free(h);
308 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
309 if (ret != 0) {
310 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
311 }
312 return NULL;
313 }
314
315 if (h->child == 0) {
316 struct sigaction sa = {
317 .sa_handler = SIG_DFL,
318 };
319
320 ret = sigaction(SIGTERM, &sa, NULL);
321 if (ret != 0) {
322 DBG_WARNING("Failed to reset signal handler (%d)\n",
323 errno);
324 }
325
326 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
327 if (ret != 0) {
328 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
329 }
330
331 /* Make stdout point to the pipe */
332 close(STDOUT_FILENO);
333 dup2(h->fd[1], STDOUT_FILENO);
334 close(h->fd[1]);
335
336 execv(args[0], args);
337
338 /* Only happens on error */
339 DBG_ERR("execv() failed\n");
340 _exit(1);
341 }
342
343 /* Parent */
344
345 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
346 if (ret != 0) {
347 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
348 }
349
350 DBG_DEBUG("Created PIPE FD:%d\n", h->fd[0]);
351 set_close_on_exec(h->fd[0]);
352
353 close(h->fd[1]);
354 h->fd[1] = -1;
355
356 talloc_set_destructor(h, cluster_mutex_destructor);
357
358 if (timeout != 0) {
359 h->te = tevent_add_timer(ctdb->ev, h,
360 timeval_current_ofs(timeout, 0),
361 cluster_mutex_timeout, h);
362 } else {
363 h->te = NULL;
364 }
365
366 h->fde = tevent_add_fd(ctdb->ev, h, h->fd[0], TEVENT_FD_READ,
367 cluster_mutex_handler, (void *)h);
368
369 if (h->fde == NULL) {
370 talloc_free(h);
371 return NULL;
372 }
373 tevent_fd_set_auto_close(h->fde);
374
375 h->ctdb = ctdb;
376 h->handler = handler;
377 h->private_data = private_data;
378 h->lost_handler = lost_handler;
379 h->lost_data = lost_data;
380
381 return h;
382 }
383