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"
30 #include "lib/util/sys_rw.h"
31 #include "lib/util/tevent_unix.h"
32 #include "lib/util/util.h"
34 /* protocol.h is just needed for ctdb_sock_addr, which is used in system.h */
35 #include "protocol/protocol.h"
36 #include "common/system.h"
38 static char *progname
= NULL
;
40 static char fcntl_lock(const char *file
, int *outfd
)
45 fd
= open(file
, O_RDWR
|O_CREAT
, 0600);
47 fprintf(stderr
, "%s: Unable to open %s - (%s)\n",
48 progname
, file
, strerror(errno
));
52 lock
.l_type
= F_WRLCK
;
53 lock
.l_whence
= SEEK_SET
;
58 if (fcntl(fd
, F_SETLK
, &lock
) != 0) {
59 int saved_errno
= errno
;
61 if (saved_errno
== EACCES
||
62 saved_errno
== EAGAIN
) {
63 /* Lock contention, fail silently */
67 /* Log an error for any other failure */
69 "%s: Failed to get lock on '%s' - (%s)\n",
70 progname
, file
, strerror(saved_errno
));
80 * Wait and see if the parent exits
83 struct wait_for_parent_state
{
84 struct tevent_context
*ev
;
88 static void wait_for_parent_check(struct tevent_req
*subreq
);
90 static struct tevent_req
*wait_for_parent_send(TALLOC_CTX
*mem_ctx
,
91 struct tevent_context
*ev
,
94 struct tevent_req
*req
, *subreq
;
95 struct wait_for_parent_state
*state
;
97 req
= tevent_req_create(mem_ctx
, &state
, struct wait_for_parent_state
);
106 fprintf(stderr
, "parent == 1\n");
107 tevent_req_done(req
);
108 return tevent_req_post(req
, ev
);
111 subreq
= tevent_wakeup_send(state
, ev
,
112 tevent_timeval_current_ofs(5,0));
113 if (tevent_req_nomem(subreq
, req
)) {
114 return tevent_req_post(req
, ev
);
116 tevent_req_set_callback(subreq
, wait_for_parent_check
, req
);
121 static void wait_for_parent_check(struct tevent_req
*subreq
)
123 struct tevent_req
*req
= tevent_req_callback_data(
124 subreq
, struct tevent_req
);
125 struct wait_for_parent_state
*state
= tevent_req_data(
126 req
, struct wait_for_parent_state
);
129 status
= tevent_wakeup_recv(subreq
);
134 "ctdb_mutex_fcntl_helper: "
135 "tevent_wakeup_recv() failed\n");
138 if (kill(state
->ppid
, 0) == -1 && errno
== ESRCH
) {
139 fprintf(stderr
, "parent gone\n");
140 tevent_req_done(req
);
144 subreq
= tevent_wakeup_send(state
, state
->ev
,
145 tevent_timeval_current_ofs(5,0));
146 if (tevent_req_nomem(subreq
, req
)) {
149 tevent_req_set_callback(subreq
, wait_for_parent_check
, req
);
152 static bool wait_for_parent_recv(struct tevent_req
*req
)
154 if (tevent_req_is_unix_error(req
, NULL
)) {
162 * Wait and check for lost lock - file removed or replaced
165 struct wait_for_lost_state
{
166 struct tevent_context
*ev
;
167 const char *lock_file
;
169 unsigned long recheck_time
;
172 static void wait_for_lost_check(struct tevent_req
*subreq
);
174 static struct tevent_req
*wait_for_lost_send(TALLOC_CTX
*mem_ctx
,
175 struct tevent_context
*ev
,
176 const char *lock_file
,
178 unsigned long recheck_time
)
180 struct tevent_req
*req
, *subreq
;
181 struct wait_for_lost_state
*state
;
185 req
= tevent_req_create(mem_ctx
, &state
, struct wait_for_lost_state
);
191 state
->lock_file
= lock_file
;
192 state
->recheck_time
= recheck_time
;
194 ret
= fstat(fd
, &sb
);
197 "ctdb_mutex_fcntl_helper: "
198 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
201 tevent_req_done(req
);
202 return tevent_req_post(req
, ev
);
204 state
->inode
= sb
.st_ino
;
206 subreq
= tevent_wakeup_send(
209 tevent_timeval_current_ofs(state
->recheck_time
, 0));
210 if (tevent_req_nomem(subreq
, req
)) {
211 return tevent_req_post(req
, ev
);
213 tevent_req_set_callback(subreq
, wait_for_lost_check
, req
);
218 static void wait_for_lost_check(struct tevent_req
*subreq
)
220 struct tevent_req
*req
= tevent_req_callback_data(
221 subreq
, struct tevent_req
);
222 struct wait_for_lost_state
*state
= tevent_req_data(
223 req
, struct wait_for_lost_state
);
228 status
= tevent_wakeup_recv(subreq
);
233 "ctdb_mutex_fcntl_helper: "
234 "tevent_wakeup_recv() failed\n");
237 ret
= stat(state
->lock_file
, &sb
);
240 "ctdb_mutex_fcntl_helper: "
241 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
244 tevent_req_done(req
);
248 if (sb
.st_ino
!= state
->inode
) {
250 "ctdb_mutex_fcntl_helper: "
251 "lock lost - lock file \"%s\" inode changed\n",
253 tevent_req_done(req
);
257 subreq
= tevent_wakeup_send(
260 tevent_timeval_current_ofs(state
->recheck_time
, 0));
261 if (tevent_req_nomem(subreq
, req
)) {
264 tevent_req_set_callback(subreq
, wait_for_lost_check
, req
);
267 static bool wait_for_lost_recv(struct tevent_req
*req
)
269 if (tevent_req_is_unix_error(req
, NULL
)) {
277 * Wait for a reason to exit, indicating that the lock is lost
280 struct wait_for_exit_state
{
283 static void wait_for_exit_parent_done(struct tevent_req
*subreq
);
284 static void wait_for_exit_lost_done(struct tevent_req
*subreq
);
286 static struct tevent_req
*wait_for_exit_send(TALLOC_CTX
*mem_ctx
,
287 struct tevent_context
*ev
,
289 const char *lock_file
,
291 unsigned long recheck_time
)
293 struct tevent_req
*req
, *subreq
;
294 struct wait_for_exit_state
*state
;
296 req
= tevent_req_create(mem_ctx
, &state
, struct wait_for_exit_state
);
301 subreq
= wait_for_parent_send(state
, ev
, ppid
);
302 if (tevent_req_nomem(subreq
, req
)) {
303 return tevent_req_post(req
, ev
);
305 tevent_req_set_callback(subreq
, wait_for_exit_parent_done
, req
);
307 if (recheck_time
> 0) {
308 subreq
= wait_for_lost_send(state
,
313 if (tevent_req_nomem(subreq
, req
)) {
314 return tevent_req_post(req
, ev
);
316 tevent_req_set_callback(subreq
, wait_for_exit_lost_done
, req
);
322 static void wait_for_exit_parent_done(struct tevent_req
*subreq
)
324 struct tevent_req
*req
= tevent_req_callback_data(
325 subreq
, struct tevent_req
);
328 status
= wait_for_parent_recv(subreq
);
333 "ctdb_mutex_fcntl_helper: "
334 "wait_for_parent_recv() failed\n");
337 tevent_req_done(req
);
340 static void wait_for_exit_lost_done(struct tevent_req
*subreq
)
342 struct tevent_req
*req
= tevent_req_callback_data(
343 subreq
, struct tevent_req
);
346 status
= wait_for_lost_recv(subreq
);
351 "ctdb_mutex_fcntl_helper: "
352 "wait_for_lost_recv() failed\n");
355 tevent_req_done(req
);
358 static bool wait_for_exit_recv(struct tevent_req
*req
)
360 if (tevent_req_is_unix_error(req
, NULL
)) {
367 static void usage(void)
369 fprintf(stderr
, "Usage: %s <file> [recheck_time]\n", progname
);
372 int main(int argc
, char *argv
[])
374 struct tevent_context
*ev
;
377 const char *file
= NULL
;
378 unsigned long recheck_time
;
381 struct tevent_req
*req
;
386 if (argc
< 2 || argc
> 3) {
391 ev
= tevent_context_init(NULL
);
393 fprintf(stderr
, "locking: tevent_context_init() failed\n");
403 recheck_time
= smb_strtoul(argv
[2],
414 result
= fcntl_lock(file
, &fd
);
415 sys_write(STDOUT_FILENO
, &result
, 1);
421 req
= wait_for_exit_send(ev
, ev
, ppid
, file
, fd
, recheck_time
);
424 "%s: wait_for_exit_send() failed\n",
429 tevent_req_poll(req
, ev
);
431 status
= wait_for_exit_recv(req
);
434 "%s: wait_for_exit_recv() failed\n",