330d5fd1d909c2275eba5af7c857dbf3ff911463
[gd/samba-autobuild/.git] / ctdb / server / ctdb_cluster_mutex.c
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
25 #include <tevent.h>
26
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"
33
34 #include "ctdb_private.h"
35 #include "common/common.h"
36 #include "common/logging.h"
37
38 #include "ctdb_cluster_mutex.h"
39
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;
52 };
53
54 static void cluster_mutex_timeout(struct tevent_context *ev,
55                                   struct tevent_timer *te,
56                                   struct timeval t, void *private_data)
57 {
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);
61
62         if (h->handler != NULL) {
63                 h->handler('2', latency, h->private_data);
64         }
65 }
66
67
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)
71 {
72         if (h->fd[0] != -1) {
73                 h->fd[0] = -1;
74         }
75         ctdb_kill(h->ctdb, h->child, SIGTERM);
76         return 0;
77 }
78
79 /* this is called when the client process has completed ctdb_recovery_lock()
80    and has written data back to us through the pipe.
81 */
82 static void cluster_mutex_handler(struct tevent_context *ev,
83                                   struct tevent_fd *fde,
84                                   uint16_t flags, void *private_data)
85 {
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;
91
92         /* Got response from child process so abort timeout */
93         TALLOC_FREE(h->te);
94
95         ret = sys_read(h->fd[0], &c, 1);
96
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);
105                         }
106                 }
107                 return;
108         }
109         h->have_response = true;
110
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);
116         }
117 }
118
119 static char cluster_mutex_helper[PATH_MAX+1] = "";
120
121 static bool cluster_mutex_helper_args(TALLOC_CTX *mem_ctx,
122                                       const char *argstring, char ***argv)
123 {
124         int nargs, i, ret, n;
125         bool is_command = false;
126         char **args = NULL;
127         char *strv = NULL;
128         char *t = NULL;
129
130         if (argstring != NULL && argstring[0] == '!') {
131                 /* This is actually a full command */
132                 is_command = true;
133                 t = discard_const(&argstring[1]);
134         } else {
135                 is_command = false;
136                 t = discard_const(argstring);
137         }
138
139         ret = strv_split(mem_ctx, &strv, t, " \t");
140         if (ret != 0) {
141                 DEBUG(DEBUG_ERR,
142                       ("Unable to parse mutex helper string \"%s\" (%s)\n",
143                        argstring, strerror(ret)));
144                 return false;
145         }
146         n = strv_count(strv);
147
148         args = talloc_array(mem_ctx, char *, n + (is_command ? 1 : 2));
149
150         if (args == NULL) {
151                 DEBUG(DEBUG_ERR,(__location__ " out of memory\n"));
152                 return false;
153         }
154
155         nargs = 0;
156
157         if (! is_command) {
158                 if (!ctdb_set_helper("cluster mutex helper",
159                                      cluster_mutex_helper,
160                                      sizeof(cluster_mutex_helper),
161                                      "CTDB_CLUSTER_MUTEX_HELPER",
162                                      CTDB_HELPER_BINDIR,
163                                      "ctdb_mutex_fcntl_helper")) {
164                         DEBUG(DEBUG_ERR,("ctdb exiting with error: %s\n",
165                                          __location__
166                                          " Unable to set cluster mutex helper\n"));
167                         exit(1);
168                 }
169
170                 args[nargs++] = cluster_mutex_helper;
171         }
172
173         t = NULL;
174         for (i = 0; i < n; i++) {
175                 /* Don't copy, just keep cmd_args around */
176                 t = strv_next(strv, t);
177                 args[nargs++] = t;
178         }
179
180         /* Make sure last argument is NULL */
181         args[nargs] = NULL;
182
183         *argv = args;
184         return true;
185 }
186
187 struct ctdb_cluster_mutex_handle *
188 ctdb_cluster_mutex(TALLOC_CTX *mem_ctx,
189                    struct ctdb_context *ctdb,
190                    const char *argstring,
191                    int timeout,
192                    cluster_mutex_handler_t handler,
193                    void *private_data,
194                    cluster_mutex_lost_handler_t lost_handler,
195                    void *lost_data)
196 {
197         struct ctdb_cluster_mutex_handle *h;
198         char **args;
199         sigset_t sigset_term;
200         int ret;
201
202         h = talloc(mem_ctx, struct ctdb_cluster_mutex_handle);
203         if (h == NULL) {
204                 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
205                 return NULL;
206         }
207
208         h->start_time = timeval_current();
209         h->fd[0] = -1;
210         h->fd[1] = -1;
211         h->have_response = false;
212
213         ret = pipe(h->fd);
214         if (ret != 0) {
215                 talloc_free(h);
216                 DEBUG(DEBUG_ERR, (__location__ " Failed to open pipe\n"));
217                 return NULL;
218         }
219         set_close_on_exec(h->fd[0]);
220
221         /* Create arguments for lock helper */
222         if (!cluster_mutex_helper_args(h, argstring, &args)) {
223                 close(h->fd[0]);
224                 close(h->fd[1]);
225                 talloc_free(h);
226                 return NULL;
227         }
228
229         sigemptyset(&sigset_term);
230         sigaddset(&sigset_term, SIGTERM);
231         ret = sigprocmask(SIG_BLOCK, &sigset_term, NULL);
232         if (ret != 0) {
233                 DBG_WARNING("Failed to block SIGTERM (%d)\n", errno);
234         }
235
236         h->child = ctdb_fork(ctdb);
237         if (h->child == (pid_t)-1) {
238                 close(h->fd[0]);
239                 close(h->fd[1]);
240                 talloc_free(h);
241                 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
242                 if (ret != 0) {
243                         DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
244                 }
245                 return NULL;
246         }
247
248         if (h->child == 0) {
249                 struct sigaction sa = {
250                         .sa_handler = SIG_DFL,
251                 };
252
253                 ret = sigaction(SIGTERM, &sa, NULL);
254                 if (ret != 0) {
255                         DBG_WARNING("Failed to reset signal handler (%d)\n",
256                                     errno);
257                 }
258
259                 ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
260                 if (ret != 0) {
261                         DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
262                 }
263
264                 /* Make stdout point to the pipe */
265                 close(STDOUT_FILENO);
266                 dup2(h->fd[1], STDOUT_FILENO);
267                 close(h->fd[1]);
268
269                 execv(args[0], args);
270
271                 /* Only happens on error */
272                 DEBUG(DEBUG_ERR, (__location__ "execv() failed\n"));
273                 _exit(1);
274         }
275
276         /* Parent */
277
278         ret = sigprocmask(SIG_UNBLOCK, &sigset_term, NULL);
279         if (ret != 0) {
280                 DBG_WARNING("Failed to unblock SIGTERM (%d)\n", errno);
281         }
282
283         DEBUG(DEBUG_DEBUG, (__location__ " Created PIPE FD:%d\n", h->fd[0]));
284         set_close_on_exec(h->fd[0]);
285
286         close(h->fd[1]);
287         h->fd[1] = -1;
288
289         talloc_set_destructor(h, cluster_mutex_destructor);
290
291         if (timeout != 0) {
292                 h->te = tevent_add_timer(ctdb->ev, h,
293                                          timeval_current_ofs(timeout, 0),
294                                          cluster_mutex_timeout, h);
295         } else {
296                 h->te = NULL;
297         }
298
299         h->fde = tevent_add_fd(ctdb->ev, h, h->fd[0], TEVENT_FD_READ,
300                                cluster_mutex_handler, (void *)h);
301
302         if (h->fde == NULL) {
303                 talloc_free(h);
304                 return NULL;
305         }
306         tevent_fd_set_auto_close(h->fde);
307
308         h->ctdb = ctdb;
309         h->handler = handler;
310         h->private_data = private_data;
311         h->lost_handler = lost_handler;
312         h->lost_data = lost_data;
313
314         return h;
315 }