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/>.
25 #include "system/filesys.h"
26 #include "system/network.h"
27 #include "system/wait.h"
28 #include "system/dir.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
= {
53 int cmd
= block
? F_SETLKW
: F_SETLK
;
56 if (fcntl(fd
, cmd
, &lock
) != 0) {
63 static char fcntl_lock(const char *file
, int *outfd
)
68 fd
= open(file
, O_RDWR
|O_CREAT
, 0600);
70 fprintf(stderr
, "%s: Unable to open %s - (%s)\n",
71 progname
, file
, strerror(errno
));
75 ret
= fcntl_lock_fd(fd
, false, 0);
78 if (ret
== EACCES
|| ret
== EAGAIN
) {
79 /* Lock contention, fail silently */
83 /* Log an error for any other failure */
85 "%s: Failed to get lock on '%s' - (%s)\n",
98 * Wait and see if the parent exits
101 struct wait_for_parent_state
{
102 struct tevent_context
*ev
;
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
,
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
);
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
);
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
);
147 status
= tevent_wakeup_recv(subreq
);
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
);
160 subreq
= tevent_wakeup_send(state
, state
->ev
,
161 tevent_timeval_current_ofs(5,0));
162 if (tevent_req_nomem(subreq
, req
)) {
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
)) {
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
;
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
,
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
);
205 state
->lock_file
= lock_file
;
206 state
->inode
= inode
;
207 state
->recheck_interval
= recheck_interval
;
209 subreq
= tevent_wakeup_send(
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
);
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
);
232 status
= tevent_wakeup_recv(subreq
);
236 fprintf(stderr
, "%s: tevent_wakeup_recv() failed\n", progname
);
239 fd
= open(state
->lock_file
, O_RDWR
);
243 "lock lost - lock file \"%s\" open failed (ret=%d)\n",
250 ret
= fstat(fd
, &sb
);
254 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
261 if (sb
.st_ino
!= state
->inode
) {
263 "%s: lock lost - lock file \"%s\" inode changed\n",
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);
278 "lock fail - lock file \"%s\" test lock error (%d)\n",
285 /* Unlock occurs on close */
288 subreq
= tevent_wakeup_send(
291 tevent_timeval_current_ofs(state
->recheck_interval
, 0));
292 if (tevent_req_nomem(subreq
, req
)) {
295 tevent_req_set_callback(subreq
, lock_io_check_loop
, req
);
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
)) {
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
,
326 unsigned long recheck_interval
,
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
);
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
,
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
);
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
);
364 status
= tmon_ping_recv(subreq
, &err
);
367 tevent_req_error(req
, err
);
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
);
381 status
= lock_io_check_recv(subreq
, &err
);
384 tevent_req_error(req
, err
);
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
) {
404 static void lock_test_child(const char *lock_file
,
407 unsigned long recheck_interval
,
410 struct tevent_context
*ev
;
411 struct tevent_req
*req
;
417 ret
= fstat(lock_fd
, &sb
);
421 "lock file \"%s\" stat failed (ret=%d)\n",
430 ev
= tevent_context_init(NULL
);
432 fprintf(stderr
, "%s: tevent_context_init() failed\n", progname
);
436 req
= lock_test_child_send(ev
,
445 "%s: lock_test_child_send() failed\n",
450 tevent_req_poll(req
, ev
);
452 status
= lock_test_child_recv(req
, &ret
);
455 "%s: lock_test_child_recv() failed (%d)\n",
464 struct lock_test_state
{
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
,
476 unsigned long recheck_interval
,
477 unsigned long ping_timeout
)
479 struct tevent_req
*req
, *subreq
;
480 struct lock_test_state
*state
;
485 req
= tevent_req_create(mem_ctx
, &state
, struct lock_test_state
);
490 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, sv
);
493 "%s: socketpair() failed (errno=%d)\n",
496 tevent_req_error(req
, errno
);
497 return tevent_req_post(req
, ev
);
504 fprintf(stderr
, "%s: fork() failed (errno=%d)\n", progname
, err
);
507 tevent_req_error(req
, err
);
508 return tevent_req_post(req
, ev
);
515 lock_test_child(lock_file
,
520 /* Above does not return */
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
)) {
533 return tevent_req_post(req
, ev
);
535 tevent_req_set_callback(subreq
, lock_test_ping_done
, 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
);
550 status
= tmon_ping_recv(subreq
, &err
);
555 /* Child exit, child already printed message */
559 "%s: ping timeout from lock test child\n",
564 "%s: tmon_ping_recv() failed (%d)\n",
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
)) {
595 * Wait for a reason to exit, indicating that parent has exited or I/O
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
,
608 const char *lock_file
,
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
);
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
,
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
,
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
);
652 status
= wait_for_parent_recv(subreq
, &err
);
658 "wait_for_parent_recv() failed (%d)\n",
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
);
673 status
= lock_test_recv(subreq
, &err
);
678 "lock_test_recv() failed (%d)\n",
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
)) {
696 static void usage(void)
699 "Usage: %s <file> [recheck_interval [ping_timeout]]\n",
703 int main(int argc
, char *argv
[])
705 struct tevent_context
*ev
;
708 const char *file
= NULL
;
709 unsigned long recheck_interval
;
710 unsigned long ping_timeout
;
713 struct tevent_req
*req
;
716 strlcpy(progpath
, argv
[0], sizeof(progpath
));
717 progname
= basename(progpath
);
719 if (argc
< 2 || argc
> 4) {
724 ev
= tevent_context_init(NULL
);
726 fprintf(stderr
, "locking: tevent_context_init() failed\n");
734 recheck_interval
= 5;
737 recheck_interval
= smb_strtoul(argv
[2],
748 ping_timeout
= smb_strtoul(argv
[3],
759 result
= fcntl_lock(file
, &fd
);
760 sys_write(STDOUT_FILENO
, &result
, 1);
766 req
= wait_for_exit_send(ev
,
775 "%s: wait_for_exit_send() failed\n",
780 tevent_req_poll(req
, ev
);
782 status
= wait_for_exit_recv(req
, &ret
);
785 "%s: wait_for_exit_recv() failed (%d)\n",