s4-kdc: Adapt to move from HDB auditing to KDC auditing constants
[Samba.git] / ctdb / server / ctdb_mutex_fcntl_helper.c
blob87f5dc22c41de24f8907c29a8b056f31036e812a
1 /*
2 CTDB mutex fcntl lock file helper
4 Copyright (C) Martin Schwenke 2015
6 wait_for_parent() code from ctdb_lock_helper.c:
8 Copyright (C) Amitay Isaacs 2013
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
24 #include "replace.h"
25 #include "system/filesys.h"
26 #include "system/network.h"
28 #include <tevent.h>
30 #include "lib/util/sys_rw.h"
31 #include "lib/util/tevent_unix.h"
32 #include "lib/util/util.h"
33 #include "lib/util/smb_strtox.h"
35 /* protocol.h is just needed for ctdb_sock_addr, which is used in system.h */
36 #include "protocol/protocol.h"
37 #include "common/system.h"
39 static char *progname = NULL;
41 static char fcntl_lock(const char *file, int *outfd)
43 int fd;
44 struct flock lock;
46 fd = open(file, O_RDWR|O_CREAT, 0600);
47 if (fd == -1) {
48 fprintf(stderr, "%s: Unable to open %s - (%s)\n",
49 progname, file, strerror(errno));
50 return '3';
53 lock.l_type = F_WRLCK;
54 lock.l_whence = SEEK_SET;
55 lock.l_start = 0;
56 lock.l_len = 1;
57 lock.l_pid = 0;
59 if (fcntl(fd, F_SETLK, &lock) != 0) {
60 int saved_errno = errno;
61 close(fd);
62 if (saved_errno == EACCES ||
63 saved_errno == EAGAIN) {
64 /* Lock contention, fail silently */
65 return '1';
68 /* Log an error for any other failure */
69 fprintf(stderr,
70 "%s: Failed to get lock on '%s' - (%s)\n",
71 progname, file, strerror(saved_errno));
72 return '3';
75 *outfd = fd;
77 return '0';
81 * Wait and see if the parent exits
84 struct wait_for_parent_state {
85 struct tevent_context *ev;
86 pid_t ppid;
89 static void wait_for_parent_check(struct tevent_req *subreq);
91 static struct tevent_req *wait_for_parent_send(TALLOC_CTX *mem_ctx,
92 struct tevent_context *ev,
93 pid_t ppid)
95 struct tevent_req *req, *subreq;
96 struct wait_for_parent_state *state;
98 req = tevent_req_create(mem_ctx, &state, struct wait_for_parent_state);
99 if (req == NULL) {
100 return NULL;
103 state->ev = ev;
104 state->ppid = ppid;
106 if (ppid == 1) {
107 fprintf(stderr, "parent == 1\n");
108 tevent_req_done(req);
109 return tevent_req_post(req, ev);
112 subreq = tevent_wakeup_send(state, ev,
113 tevent_timeval_current_ofs(5,0));
114 if (tevent_req_nomem(subreq, req)) {
115 return tevent_req_post(req, ev);
117 tevent_req_set_callback(subreq, wait_for_parent_check, req);
119 return req;
122 static void wait_for_parent_check(struct tevent_req *subreq)
124 struct tevent_req *req = tevent_req_callback_data(
125 subreq, struct tevent_req);
126 struct wait_for_parent_state *state = tevent_req_data(
127 req, struct wait_for_parent_state);
128 bool status;
130 status = tevent_wakeup_recv(subreq);
131 TALLOC_FREE(subreq);
132 if (! status) {
133 /* Ignore error */
134 fprintf(stderr,
135 "ctdb_mutex_fcntl_helper: "
136 "tevent_wakeup_recv() failed\n");
139 if (kill(state->ppid, 0) == -1 && errno == ESRCH) {
140 fprintf(stderr, "parent gone\n");
141 tevent_req_done(req);
142 return;
145 subreq = tevent_wakeup_send(state, state->ev,
146 tevent_timeval_current_ofs(5,0));
147 if (tevent_req_nomem(subreq, req)) {
148 return;
150 tevent_req_set_callback(subreq, wait_for_parent_check, req);
153 static bool wait_for_parent_recv(struct tevent_req *req)
155 if (tevent_req_is_unix_error(req, NULL)) {
156 return false;
159 return true;
163 * Wait and check for lost lock - file removed or replaced
166 struct wait_for_lost_state {
167 struct tevent_context *ev;
168 const char *lock_file;
169 ino_t inode;
170 unsigned long recheck_time;
173 static void wait_for_lost_check(struct tevent_req *subreq);
175 static struct tevent_req *wait_for_lost_send(TALLOC_CTX *mem_ctx,
176 struct tevent_context *ev,
177 const char *lock_file,
178 int fd,
179 unsigned long recheck_time)
181 struct tevent_req *req, *subreq;
182 struct wait_for_lost_state *state;
183 struct stat sb;
184 int ret;
186 req = tevent_req_create(mem_ctx, &state, struct wait_for_lost_state);
187 if (req == NULL) {
188 return NULL;
191 state->ev = ev;
192 state->lock_file = lock_file;
193 state->recheck_time = recheck_time;
195 ret = fstat(fd, &sb);
196 if (ret != 0) {
197 fprintf(stderr,
198 "ctdb_mutex_fcntl_helper: "
199 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
200 state->lock_file,
201 errno);
202 tevent_req_done(req);
203 return tevent_req_post(req, ev);
205 state->inode = sb.st_ino;
207 subreq = tevent_wakeup_send(
208 state,
210 tevent_timeval_current_ofs(state->recheck_time, 0));
211 if (tevent_req_nomem(subreq, req)) {
212 return tevent_req_post(req, ev);
214 tevent_req_set_callback(subreq, wait_for_lost_check, req);
216 return req;
219 static void wait_for_lost_check(struct tevent_req *subreq)
221 struct tevent_req *req = tevent_req_callback_data(
222 subreq, struct tevent_req);
223 struct wait_for_lost_state *state = tevent_req_data(
224 req, struct wait_for_lost_state);
225 bool status;
226 struct stat sb;
227 int ret;
229 status = tevent_wakeup_recv(subreq);
230 TALLOC_FREE(subreq);
231 if (! status) {
232 /* Ignore error */
233 fprintf(stderr,
234 "ctdb_mutex_fcntl_helper: "
235 "tevent_wakeup_recv() failed\n");
238 ret = stat(state->lock_file, &sb);
239 if (ret != 0) {
240 fprintf(stderr,
241 "ctdb_mutex_fcntl_helper: "
242 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
243 state->lock_file,
244 errno);
245 tevent_req_done(req);
246 return;
249 if (sb.st_ino != state->inode) {
250 fprintf(stderr,
251 "ctdb_mutex_fcntl_helper: "
252 "lock lost - lock file \"%s\" inode changed\n",
253 state->lock_file);
254 tevent_req_done(req);
255 return;
258 subreq = tevent_wakeup_send(
259 state,
260 state->ev,
261 tevent_timeval_current_ofs(state->recheck_time, 0));
262 if (tevent_req_nomem(subreq, req)) {
263 return;
265 tevent_req_set_callback(subreq, wait_for_lost_check, req);
268 static bool wait_for_lost_recv(struct tevent_req *req)
270 if (tevent_req_is_unix_error(req, NULL)) {
271 return false;
274 return true;
278 * Wait for a reason to exit, indicating that the lock is lost
281 struct wait_for_exit_state {
284 static void wait_for_exit_parent_done(struct tevent_req *subreq);
285 static void wait_for_exit_lost_done(struct tevent_req *subreq);
287 static struct tevent_req *wait_for_exit_send(TALLOC_CTX *mem_ctx,
288 struct tevent_context *ev,
289 pid_t ppid,
290 const char *lock_file,
291 int fd,
292 unsigned long recheck_time)
294 struct tevent_req *req, *subreq;
295 struct wait_for_exit_state *state;
297 req = tevent_req_create(mem_ctx, &state, struct wait_for_exit_state);
298 if (req == NULL) {
299 return NULL;
302 subreq = wait_for_parent_send(state, ev, ppid);
303 if (tevent_req_nomem(subreq, req)) {
304 return tevent_req_post(req, ev);
306 tevent_req_set_callback(subreq, wait_for_exit_parent_done, req);
308 if (recheck_time > 0) {
309 subreq = wait_for_lost_send(state,
311 lock_file,
313 recheck_time);
314 if (tevent_req_nomem(subreq, req)) {
315 return tevent_req_post(req, ev);
317 tevent_req_set_callback(subreq, wait_for_exit_lost_done, req);
320 return req;
323 static void wait_for_exit_parent_done(struct tevent_req *subreq)
325 struct tevent_req *req = tevent_req_callback_data(
326 subreq, struct tevent_req);
327 bool status;
329 status = wait_for_parent_recv(subreq);
330 TALLOC_FREE(subreq);
331 if (! status) {
332 /* Ignore error */
333 fprintf(stderr,
334 "ctdb_mutex_fcntl_helper: "
335 "wait_for_parent_recv() failed\n");
338 tevent_req_done(req);
341 static void wait_for_exit_lost_done(struct tevent_req *subreq)
343 struct tevent_req *req = tevent_req_callback_data(
344 subreq, struct tevent_req);
345 bool status;
347 status = wait_for_lost_recv(subreq);
348 TALLOC_FREE(subreq);
349 if (! status) {
350 /* Ignore error */
351 fprintf(stderr,
352 "ctdb_mutex_fcntl_helper: "
353 "wait_for_lost_recv() failed\n");
356 tevent_req_done(req);
359 static bool wait_for_exit_recv(struct tevent_req *req)
361 if (tevent_req_is_unix_error(req, NULL)) {
362 return false;
365 return true;
368 static void usage(void)
370 fprintf(stderr, "Usage: %s <file> [recheck_time]\n", progname);
373 int main(int argc, char *argv[])
375 struct tevent_context *ev;
376 char result;
377 int ppid;
378 const char *file = NULL;
379 unsigned long recheck_time;
380 int ret;
381 int fd = -1;
382 struct tevent_req *req;
383 bool status;
385 progname = argv[0];
387 if (argc < 2 || argc > 3) {
388 usage();
389 exit(1);
392 ev = tevent_context_init(NULL);
393 if (ev == NULL) {
394 fprintf(stderr, "locking: tevent_context_init() failed\n");
395 exit(1);
398 ppid = getppid();
400 file = argv[1];
402 recheck_time = 5;
403 if (argc == 3) {
404 recheck_time = smb_strtoul(argv[2],
405 NULL,
407 &ret,
408 SMB_STR_STANDARD);
409 if (ret != 0) {
410 usage();
411 exit(1);
415 result = fcntl_lock(file, &fd);
416 sys_write(STDOUT_FILENO, &result, 1);
418 if (result != '0') {
419 return 0;
422 req = wait_for_exit_send(ev, ev, ppid, file, fd, recheck_time);
423 if (req == NULL) {
424 fprintf(stderr,
425 "%s: wait_for_exit_send() failed\n",
426 progname);
427 exit(1);
430 tevent_req_poll(req, ev);
432 status = wait_for_exit_recv(req);
433 if (! status) {
434 fprintf(stderr,
435 "%s: wait_for_exit_recv() failed\n",
436 progname);
439 if (fd != -1) {
440 close(fd);
443 return 0;