gp: Skip site GP list if no site is found
[Samba.git] / ctdb / server / ctdb_mutex_fcntl_helper.c
blobaac98eaeeadf8ebc02e9550161bb3a4c5aa079a1
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"
27 #include "system/wait.h"
28 #include "system/dir.h"
30 #include <tevent.h>
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/tevent_unix.h"
34 #include "lib/util/util.h"
35 #include "lib/util/smb_strtox.h"
37 /* protocol.h is just needed for ctdb_sock_addr, which is used in system.h */
38 #include "protocol/protocol.h"
39 #include "common/system.h"
40 #include "common/tmon.h"
42 static char progpath[PATH_MAX];
43 static char *progname = NULL;
45 static int fcntl_lock_fd(int fd, bool block, off_t start)
47 static struct flock lock = {
48 .l_type = F_WRLCK,
49 .l_whence = SEEK_SET,
50 .l_len = 1,
51 .l_pid = 0,
53 int cmd = block ? F_SETLKW : F_SETLK;
55 lock.l_start = start;
56 if (fcntl(fd, cmd, &lock) != 0) {
57 return errno;
60 return 0;
63 static char fcntl_lock(const char *file, int *outfd)
65 int fd;
66 int ret;
68 fd = open(file, O_RDWR|O_CREAT, 0600);
69 if (fd == -1) {
70 fprintf(stderr, "%s: Unable to open %s - (%s)\n",
71 progname, file, strerror(errno));
72 return '3';
75 ret = fcntl_lock_fd(fd, false, 0);
76 if (ret != 0) {
77 close(fd);
78 if (ret == EACCES || ret == EAGAIN) {
79 /* Lock contention, fail silently */
80 return '1';
83 /* Log an error for any other failure */
84 fprintf(stderr,
85 "%s: Failed to get lock on '%s' - (%s)\n",
86 progname,
87 file,
88 strerror(ret));
89 return '3';
92 *outfd = fd;
94 return '0';
98 * Wait and see if the parent exits
101 struct wait_for_parent_state {
102 struct tevent_context *ev;
103 pid_t ppid;
106 static void wait_for_parent_check(struct tevent_req *subreq);
108 static struct tevent_req *wait_for_parent_send(TALLOC_CTX *mem_ctx,
109 struct tevent_context *ev,
110 pid_t ppid)
112 struct tevent_req *req, *subreq;
113 struct wait_for_parent_state *state;
115 req = tevent_req_create(mem_ctx, &state, struct wait_for_parent_state);
116 if (req == NULL) {
117 return NULL;
120 state->ev = ev;
121 state->ppid = ppid;
123 if (ppid == 1) {
124 fprintf(stderr, "parent == 1\n");
125 tevent_req_done(req);
126 return tevent_req_post(req, ev);
129 subreq = tevent_wakeup_send(state, ev,
130 tevent_timeval_current_ofs(5,0));
131 if (tevent_req_nomem(subreq, req)) {
132 return tevent_req_post(req, ev);
134 tevent_req_set_callback(subreq, wait_for_parent_check, req);
136 return req;
139 static void wait_for_parent_check(struct tevent_req *subreq)
141 struct tevent_req *req = tevent_req_callback_data(
142 subreq, struct tevent_req);
143 struct wait_for_parent_state *state = tevent_req_data(
144 req, struct wait_for_parent_state);
145 bool status;
147 status = tevent_wakeup_recv(subreq);
148 TALLOC_FREE(subreq);
149 if (! status) {
150 /* Ignore error */
151 fprintf(stderr, "%s: tevent_wakeup_recv() failed\n", progname);
154 if (kill(state->ppid, 0) == -1 && errno == ESRCH) {
155 fprintf(stderr, "parent gone\n");
156 tevent_req_done(req);
157 return;
160 subreq = tevent_wakeup_send(state, state->ev,
161 tevent_timeval_current_ofs(5,0));
162 if (tevent_req_nomem(subreq, req)) {
163 return;
165 tevent_req_set_callback(subreq, wait_for_parent_check, req);
168 static bool wait_for_parent_recv(struct tevent_req *req, int *perr)
170 if (tevent_req_is_unix_error(req, perr)) {
171 return false;
174 return true;
178 * Perform I/O on lock in a loop - complete when file removed or replaced
181 struct lock_io_check_state {
182 struct tevent_context *ev;
183 const char *lock_file;
184 ino_t inode;
185 unsigned long recheck_interval;
188 static void lock_io_check_loop(struct tevent_req *subreq);
190 static struct tevent_req *lock_io_check_send(TALLOC_CTX *mem_ctx,
191 struct tevent_context *ev,
192 const char *lock_file,
193 ino_t inode,
194 unsigned long recheck_interval)
196 struct tevent_req *req, *subreq;
197 struct lock_io_check_state *state;
199 req = tevent_req_create(mem_ctx, &state, struct lock_io_check_state);
200 if (req == NULL) {
201 return NULL;
204 state->ev = ev;
205 state->lock_file = lock_file;
206 state->inode = inode;
207 state->recheck_interval = recheck_interval;
209 subreq = tevent_wakeup_send(
210 state,
212 tevent_timeval_current_ofs(state->recheck_interval, 0));
213 if (tevent_req_nomem(subreq, req)) {
214 return tevent_req_post(req, ev);
216 tevent_req_set_callback(subreq, lock_io_check_loop, req);
218 return req;
221 static void lock_io_check_loop(struct tevent_req *subreq)
223 struct tevent_req *req = tevent_req_callback_data(
224 subreq, struct tevent_req);
225 struct lock_io_check_state *state = tevent_req_data(
226 req, struct lock_io_check_state);
227 bool status;
228 struct stat sb;
229 int fd = -1;
230 int ret;
232 status = tevent_wakeup_recv(subreq);
233 TALLOC_FREE(subreq);
234 if (! status) {
235 /* Ignore error */
236 fprintf(stderr, "%s: tevent_wakeup_recv() failed\n", progname);
239 fd = open(state->lock_file, O_RDWR);
240 if (fd == -1) {
241 fprintf(stderr,
242 "%s: "
243 "lock lost - lock file \"%s\" open failed (ret=%d)\n",
244 progname,
245 state->lock_file,
246 errno);
247 goto done;
250 ret = fstat(fd, &sb);
251 if (ret != 0) {
252 fprintf(stderr,
253 "%s: "
254 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
255 progname,
256 state->lock_file,
257 errno);
258 goto done;
261 if (sb.st_ino != state->inode) {
262 fprintf(stderr,
263 "%s: lock lost - lock file \"%s\" inode changed\n",
264 progname,
265 state->lock_file);
266 goto done;
270 * Attempt to lock a 2nd byte range. Using a blocking lock
271 * encourages ping timeouts if the cluster filesystem is in a
272 * bad state. It also makes testing easier.
274 ret = fcntl_lock_fd(fd, true, 1);
275 if (ret != 0) {
276 fprintf(stderr,
277 "%s: "
278 "lock fail - lock file \"%s\" test lock error (%d)\n",
279 progname,
280 state->lock_file,
281 ret);
282 goto done;
285 /* Unlock occurs on close */
286 close(fd);
288 subreq = tevent_wakeup_send(
289 state,
290 state->ev,
291 tevent_timeval_current_ofs(state->recheck_interval, 0));
292 if (tevent_req_nomem(subreq, req)) {
293 return;
295 tevent_req_set_callback(subreq, lock_io_check_loop, req);
297 return;
299 done:
300 if (fd != -1) {
301 close(fd);
303 tevent_req_done(req);
306 static bool lock_io_check_recv(struct tevent_req *req, int *perr)
308 if (tevent_req_is_unix_error(req, perr)) {
309 return false;
312 return true;
315 struct lock_test_child_state {
318 static void lock_test_child_ping_done(struct tevent_req *subreq);
319 static void lock_test_child_io_check_done(struct tevent_req *subreq);
321 static struct tevent_req *lock_test_child_send(TALLOC_CTX *mem_ctx,
322 struct tevent_context *ev,
323 const char *lock_file,
324 int fd,
325 ino_t inode,
326 unsigned long recheck_interval,
327 bool send_pings)
329 struct tevent_req *req, *subreq;
330 struct lock_test_child_state *state;
331 unsigned int interval = send_pings ? 1 : 0;
333 req = tevent_req_create(mem_ctx, &state, struct lock_test_child_state);
334 if (req == NULL) {
335 return NULL;
338 subreq = tmon_ping_send(state, ev, fd, TMON_FD_BOTH, 0, interval);
339 if (tevent_req_nomem(subreq, req)) {
340 return tevent_req_post(req, ev);
342 tevent_req_set_callback(subreq, lock_test_child_ping_done, req);
344 subreq = lock_io_check_send(state,
346 lock_file,
347 inode,
348 recheck_interval);
349 if (tevent_req_nomem(subreq, req)) {
350 return tevent_req_post(req, ev);
352 tevent_req_set_callback(subreq, lock_test_child_io_check_done, req);
354 return req;
357 static void lock_test_child_ping_done(struct tevent_req *subreq)
359 struct tevent_req *req = tevent_req_callback_data(
360 subreq, struct tevent_req);
361 bool status;
362 int err;
364 status = tmon_ping_recv(subreq, &err);
365 TALLOC_FREE(subreq);
366 if (!status) {
367 tevent_req_error(req, err);
368 return;
371 tevent_req_done(req);
374 static void lock_test_child_io_check_done(struct tevent_req *subreq)
376 struct tevent_req *req = tevent_req_callback_data(
377 subreq, struct tevent_req);
378 bool status;
379 int err;
381 status = lock_io_check_recv(subreq, &err);
382 TALLOC_FREE(subreq);
383 if (!status) {
384 tevent_req_error(req, err);
385 return;
388 tevent_req_done(req);
391 static bool lock_test_child_recv(struct tevent_req *req, int *perr)
393 if (tevent_req_is_unix_error(req, perr)) {
394 /* Parent exit is expected */
395 if (*perr == EPIPE) {
396 return true;
398 return false;
401 return true;
404 static void lock_test_child(const char *lock_file,
405 int lock_fd,
406 int pipe_fd,
407 unsigned long recheck_interval,
408 bool send_pings)
410 struct tevent_context *ev;
411 struct tevent_req *req;
412 struct stat sb;
413 ino_t inode;
414 bool status;
415 int ret;
417 ret = fstat(lock_fd, &sb);
418 if (ret != 0) {
419 fprintf(stderr,
420 "%s: lock lost - "
421 "lock file \"%s\" stat failed (ret=%d)\n",
422 progname,
423 lock_file,
424 errno);
425 _exit(1);
427 inode = sb.st_ino;
428 close(lock_fd);
430 ev = tevent_context_init(NULL);
431 if (ev == NULL) {
432 fprintf(stderr, "%s: tevent_context_init() failed\n", progname);
433 _exit(1);
436 req = lock_test_child_send(ev,
438 lock_file,
439 pipe_fd,
440 inode,
441 recheck_interval,
442 send_pings);
443 if (req == NULL) {
444 fprintf(stderr,
445 "%s: lock_test_child_send() failed\n",
446 progname);
447 _exit(1);
450 tevent_req_poll(req, ev);
452 status = lock_test_child_recv(req, &ret);
453 if (! status) {
454 fprintf(stderr,
455 "%s: lock_test_child_recv() failed (%d)\n",
456 progname,
457 ret);
458 _exit(1);
461 _exit(0);
464 struct lock_test_state {
465 int *lock_fdp;
466 int pipe_fd;
467 pid_t child_pid;
470 static void lock_test_ping_done(struct tevent_req *subreq);
472 static struct tevent_req *lock_test_send(TALLOC_CTX *mem_ctx,
473 struct tevent_context *ev,
474 const char *lock_file,
475 int *fdp,
476 unsigned long recheck_interval,
477 unsigned long ping_timeout)
479 struct tevent_req *req, *subreq;
480 struct lock_test_state *state;
481 pid_t pid;
482 int sv[2];
483 int ret;
485 req = tevent_req_create(mem_ctx, &state, struct lock_test_state);
486 if (req == NULL) {
487 return NULL;
490 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
491 if (ret != 0) {
492 fprintf(stderr,
493 "%s: socketpair() failed (errno=%d)\n",
494 progname,
495 errno);
496 tevent_req_error(req, errno);
497 return tevent_req_post(req, ev);
500 pid = fork();
501 if (pid == -1) {
503 int err = errno;
504 fprintf(stderr, "%s: fork() failed (errno=%d)\n", progname, err);
505 close(sv[0]);
506 close(sv[1]);
507 tevent_req_error(req, err);
508 return tevent_req_post(req, ev);
510 if (pid == 0) {
511 /* Child */
512 close(sv[0]);
513 TALLOC_FREE(ev);
515 lock_test_child(lock_file,
516 *fdp,
517 sv[1],
518 recheck_interval,
519 ping_timeout != 0);
520 /* Above does not return */
523 /* Parent */
524 close(sv[1]);
526 state->lock_fdp = fdp;
527 state->pipe_fd = sv[0];
528 state->child_pid = pid;
530 subreq = tmon_ping_send(state, ev, sv[0], TMON_FD_BOTH, ping_timeout, 0);
531 if (tevent_req_nomem(subreq, req)) {
532 close(sv[0]);
533 return tevent_req_post(req, ev);
535 tevent_req_set_callback(subreq, lock_test_ping_done, req);
537 return req;
540 static void lock_test_ping_done(struct tevent_req *subreq)
542 struct tevent_req *req = tevent_req_callback_data(
543 subreq, struct tevent_req);
544 struct lock_test_state *state = tevent_req_data(
545 req, struct lock_test_state);
546 int wstatus;
547 bool status;
548 int err;
550 status = tmon_ping_recv(subreq, &err);
551 TALLOC_FREE(subreq);
552 if (! status) {
553 switch (err) {
554 case EPIPE:
555 /* Child exit, child already printed message */
556 break;
557 case ETIMEDOUT:
558 fprintf(stderr,
559 "%s: ping timeout from lock test child\n",
560 progname);
561 break;
562 default:
563 fprintf(stderr,
564 "%s: tmon_ping_recv() failed (%d)\n",
565 progname,
566 err);
568 /* Ignore error */
572 * Lock checking child is gone or not sending pings. Release
573 * the lock, close this end of pipe, send SIGKILL to the child
574 * process and wait for the child to exit.
576 close(*state->lock_fdp);
577 *state->lock_fdp = -1;
578 close(state->pipe_fd);
579 kill(state->child_pid, SIGKILL);
580 waitpid(state->child_pid, &wstatus, 0);
582 tevent_req_done(req);
585 static bool lock_test_recv(struct tevent_req *req, int *perr)
587 if (tevent_req_is_unix_error(req, perr)) {
588 return false;
591 return true;
595 * Wait for a reason to exit, indicating that parent has exited or I/O
596 * on lock failed
599 struct wait_for_exit_state {
602 static void wait_for_exit_parent_done(struct tevent_req *subreq);
603 static void wait_for_exit_lock_test_done(struct tevent_req *subreq);
605 static struct tevent_req *wait_for_exit_send(TALLOC_CTX *mem_ctx,
606 struct tevent_context *ev,
607 pid_t ppid,
608 const char *lock_file,
609 int *fdp,
610 unsigned long recheck_interval,
611 unsigned long ping_timeout)
613 struct tevent_req *req, *subreq;
614 struct wait_for_exit_state *state;
616 req = tevent_req_create(mem_ctx, &state, struct wait_for_exit_state);
617 if (req == NULL) {
618 return NULL;
621 subreq = wait_for_parent_send(state, ev, ppid);
622 if (tevent_req_nomem(subreq, req)) {
623 return tevent_req_post(req, ev);
625 tevent_req_set_callback(subreq, wait_for_exit_parent_done, req);
627 if (recheck_interval > 0) {
628 subreq = lock_test_send(state,
630 lock_file,
631 fdp,
632 recheck_interval,
633 ping_timeout);
634 if (tevent_req_nomem(subreq, req)) {
635 return tevent_req_post(req, ev);
637 tevent_req_set_callback(subreq,
638 wait_for_exit_lock_test_done,
639 req);
642 return req;
645 static void wait_for_exit_parent_done(struct tevent_req *subreq)
647 struct tevent_req *req = tevent_req_callback_data(
648 subreq, struct tevent_req);
649 bool status;
650 int err;
652 status = wait_for_parent_recv(subreq, &err);
653 TALLOC_FREE(subreq);
654 if (! status) {
655 /* Ignore error */
656 fprintf(stderr,
657 "%s: "
658 "wait_for_parent_recv() failed (%d)\n",
659 progname,
660 err);
663 tevent_req_done(req);
666 static void wait_for_exit_lock_test_done(struct tevent_req *subreq)
668 struct tevent_req *req = tevent_req_callback_data(
669 subreq, struct tevent_req);
670 bool status;
671 int err;
673 status = lock_test_recv(subreq, &err);
674 TALLOC_FREE(subreq);
675 if (! status) {
676 fprintf(stderr,
677 "%s: "
678 "lock_test_recv() failed (%d)\n",
679 progname,
680 err);
681 /* Ignore error, fall through to done */
684 tevent_req_done(req);
687 static bool wait_for_exit_recv(struct tevent_req *req, int *perr)
689 if (tevent_req_is_unix_error(req, perr)) {
690 return false;
693 return true;
696 static void usage(void)
698 fprintf(stderr,
699 "Usage: %s <file> [recheck_interval [ping_timeout]]\n",
700 progname);
703 int main(int argc, char *argv[])
705 struct tevent_context *ev;
706 char result;
707 int ppid;
708 const char *file = NULL;
709 unsigned long recheck_interval;
710 unsigned long ping_timeout;
711 int ret;
712 int fd = -1;
713 struct tevent_req *req;
714 bool status;
716 strlcpy(progpath, argv[0], sizeof(progpath));
717 progname = basename(progpath);
719 if (argc < 2 || argc > 4) {
720 usage();
721 exit(1);
724 ev = tevent_context_init(NULL);
725 if (ev == NULL) {
726 fprintf(stderr, "locking: tevent_context_init() failed\n");
727 exit(1);
730 ppid = getppid();
732 file = argv[1];
734 recheck_interval = 5;
735 ping_timeout = 0;
736 if (argc >= 3) {
737 recheck_interval = smb_strtoul(argv[2],
738 NULL,
740 &ret,
741 SMB_STR_STANDARD);
742 if (ret != 0) {
743 usage();
744 exit(1);
747 if (argc >= 4) {
748 ping_timeout = smb_strtoul(argv[3],
749 NULL,
751 &ret,
752 SMB_STR_STANDARD);
753 if (ret != 0) {
754 usage();
755 exit(1);
759 result = fcntl_lock(file, &fd);
760 sys_write(STDOUT_FILENO, &result, 1);
762 if (result != '0') {
763 return 0;
766 req = wait_for_exit_send(ev,
768 ppid,
769 file,
770 &fd,
771 recheck_interval,
772 ping_timeout);
773 if (req == NULL) {
774 fprintf(stderr,
775 "%s: wait_for_exit_send() failed\n",
776 progname);
777 exit(1);
780 tevent_req_poll(req, ev);
782 status = wait_for_exit_recv(req, &ret);
783 if (! status) {
784 fprintf(stderr,
785 "%s: wait_for_exit_recv() failed (%d)\n",
786 progname,
787 ret);
790 if (fd != -1) {
791 close(fd);
794 return 0;