ctdb: Fix build on AIX
[vlendec/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         int ret;
200
201         h = talloc(mem_ctx, struct ctdb_cluster_mutex_handle);
202         if (h == NULL) {
203                 DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
204                 return NULL;
205         }
206
207         h->start_time = timeval_current();
208         h->fd[0] = -1;
209         h->fd[1] = -1;
210         h->have_response = false;
211
212         ret = pipe(h->fd);
213         if (ret != 0) {
214                 talloc_free(h);
215                 DEBUG(DEBUG_ERR, (__location__ " Failed to open pipe\n"));
216                 return NULL;
217         }
218         set_close_on_exec(h->fd[0]);
219
220         /* Create arguments for lock helper */
221         if (!cluster_mutex_helper_args(h, argstring, &args)) {
222                 close(h->fd[0]);
223                 close(h->fd[1]);
224                 talloc_free(h);
225                 return NULL;
226         }
227
228         h->child = ctdb_fork(ctdb);
229         if (h->child == (pid_t)-1) {
230                 close(h->fd[0]);
231                 close(h->fd[1]);
232                 talloc_free(h);
233                 return NULL;
234         }
235
236         if (h->child == 0) {
237                 /* Make stdout point to the pipe */
238                 close(STDOUT_FILENO);
239                 dup2(h->fd[1], STDOUT_FILENO);
240                 close(h->fd[1]);
241
242                 execv(args[0], args);
243
244                 /* Only happens on error */
245                 DEBUG(DEBUG_ERR, (__location__ "execv() failed\n"));
246                 _exit(1);
247         }
248
249         /* Parent */
250
251         DEBUG(DEBUG_DEBUG, (__location__ " Created PIPE FD:%d\n", h->fd[0]));
252         set_close_on_exec(h->fd[0]);
253
254         close(h->fd[1]);
255         h->fd[1] = -1;
256
257         talloc_set_destructor(h, cluster_mutex_destructor);
258
259         if (timeout != 0) {
260                 h->te = tevent_add_timer(ctdb->ev, h,
261                                          timeval_current_ofs(timeout, 0),
262                                          cluster_mutex_timeout, h);
263         } else {
264                 h->te = NULL;
265         }
266
267         h->fde = tevent_add_fd(ctdb->ev, h, h->fd[0], TEVENT_FD_READ,
268                                cluster_mutex_handler, (void *)h);
269
270         if (h->fde == NULL) {
271                 talloc_free(h);
272                 return NULL;
273         }
274         tevent_fd_set_auto_close(h->fde);
275
276         h->ctdb = ctdb;
277         h->handler = handler;
278         h->private_data = private_data;
279         h->lost_handler = lost_handler;
280         h->lost_data = lost_data;
281
282         return h;
283 }