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