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"
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
)
46 fd
= open(file
, O_RDWR
|O_CREAT
, 0600);
48 fprintf(stderr
, "%s: Unable to open %s - (%s)\n",
49 progname
, file
, strerror(errno
));
53 lock
.l_type
= F_WRLCK
;
54 lock
.l_whence
= SEEK_SET
;
59 if (fcntl(fd
, F_SETLK
, &lock
) != 0) {
60 int saved_errno
= errno
;
62 if (saved_errno
== EACCES
||
63 saved_errno
== EAGAIN
) {
64 /* Lock contention, fail silently */
68 /* Log an error for any other failure */
70 "%s: Failed to get lock on '%s' - (%s)\n",
71 progname
, file
, strerror(saved_errno
));
81 * Wait and see if the parent exits
84 struct wait_for_parent_state
{
85 struct tevent_context
*ev
;
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
,
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
);
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
);
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
);
130 status
= tevent_wakeup_recv(subreq
);
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
);
145 subreq
= tevent_wakeup_send(state
, state
->ev
,
146 tevent_timeval_current_ofs(5,0));
147 if (tevent_req_nomem(subreq
, req
)) {
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
)) {
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
;
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
,
179 unsigned long recheck_time
)
181 struct tevent_req
*req
, *subreq
;
182 struct wait_for_lost_state
*state
;
186 req
= tevent_req_create(mem_ctx
, &state
, struct wait_for_lost_state
);
192 state
->lock_file
= lock_file
;
193 state
->recheck_time
= recheck_time
;
195 ret
= fstat(fd
, &sb
);
198 "ctdb_mutex_fcntl_helper: "
199 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
202 tevent_req_done(req
);
203 return tevent_req_post(req
, ev
);
205 state
->inode
= sb
.st_ino
;
207 subreq
= tevent_wakeup_send(
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
);
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
);
229 status
= tevent_wakeup_recv(subreq
);
234 "ctdb_mutex_fcntl_helper: "
235 "tevent_wakeup_recv() failed\n");
238 ret
= stat(state
->lock_file
, &sb
);
241 "ctdb_mutex_fcntl_helper: "
242 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
245 tevent_req_done(req
);
249 if (sb
.st_ino
!= state
->inode
) {
251 "ctdb_mutex_fcntl_helper: "
252 "lock lost - lock file \"%s\" inode changed\n",
254 tevent_req_done(req
);
258 subreq
= tevent_wakeup_send(
261 tevent_timeval_current_ofs(state
->recheck_time
, 0));
262 if (tevent_req_nomem(subreq
, req
)) {
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
)) {
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
,
290 const char *lock_file
,
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
);
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
,
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
);
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
);
329 status
= wait_for_parent_recv(subreq
);
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
);
347 status
= wait_for_lost_recv(subreq
);
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
)) {
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
;
378 const char *file
= NULL
;
379 unsigned long recheck_time
;
382 struct tevent_req
*req
;
387 if (argc
< 2 || argc
> 3) {
392 ev
= tevent_context_init(NULL
);
394 fprintf(stderr
, "locking: tevent_context_init() failed\n");
404 recheck_time
= smb_strtoul(argv
[2],
415 result
= fcntl_lock(file
, &fd
);
416 sys_write(STDOUT_FILENO
, &result
, 1);
422 req
= wait_for_exit_send(ev
, ev
, ppid
, file
, fd
, recheck_time
);
425 "%s: wait_for_exit_send() failed\n",
430 tevent_req_poll(req
, ev
);
432 status
= wait_for_exit_recv(req
);
435 "%s: wait_for_exit_recv() failed\n",