s3: VFS: vfs_snapper: Make chflags return errno = EROFS on a shadow copy path.
[Samba.git] / ctdb / server / ctdb_mutex_fcntl_helper.c
blob1448a9062a015fb9bcf4721f0c1dd14e59710e3e
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"
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)
42 int fd;
43 struct flock lock;
45 fd = open(file, O_RDWR|O_CREAT, 0600);
46 if (fd == -1) {
47 fprintf(stderr, "%s: Unable to open %s - (%s)\n",
48 progname, file, strerror(errno));
49 return '3';
52 lock.l_type = F_WRLCK;
53 lock.l_whence = SEEK_SET;
54 lock.l_start = 0;
55 lock.l_len = 1;
56 lock.l_pid = 0;
58 if (fcntl(fd, F_SETLK, &lock) != 0) {
59 int saved_errno = errno;
60 close(fd);
61 if (saved_errno == EACCES ||
62 saved_errno == EAGAIN) {
63 /* Lock contention, fail silently */
64 return '1';
67 /* Log an error for any other failure */
68 fprintf(stderr,
69 "%s: Failed to get lock on '%s' - (%s)\n",
70 progname, file, strerror(saved_errno));
71 return '3';
74 *outfd = fd;
76 return '0';
80 * Wait and see if the parent exits
83 struct wait_for_parent_state {
84 struct tevent_context *ev;
85 pid_t ppid;
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,
92 pid_t ppid)
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);
98 if (req == NULL) {
99 return NULL;
102 state->ev = ev;
103 state->ppid = ppid;
105 if (ppid == 1) {
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);
118 return 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);
127 bool status;
129 status = tevent_wakeup_recv(subreq);
130 TALLOC_FREE(subreq);
131 if (! status) {
132 /* Ignore error */
133 fprintf(stderr,
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);
141 return;
144 subreq = tevent_wakeup_send(state, state->ev,
145 tevent_timeval_current_ofs(5,0));
146 if (tevent_req_nomem(subreq, req)) {
147 return;
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)) {
155 return false;
158 return true;
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;
168 ino_t inode;
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,
177 int fd,
178 unsigned long recheck_time)
180 struct tevent_req *req, *subreq;
181 struct wait_for_lost_state *state;
182 struct stat sb;
183 int ret;
185 req = tevent_req_create(mem_ctx, &state, struct wait_for_lost_state);
186 if (req == NULL) {
187 return NULL;
190 state->ev = ev;
191 state->lock_file = lock_file;
192 state->recheck_time = recheck_time;
194 ret = fstat(fd, &sb);
195 if (ret != 0) {
196 fprintf(stderr,
197 "ctdb_mutex_fcntl_helper: "
198 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
199 state->lock_file,
200 errno);
201 tevent_req_done(req);
202 return tevent_req_post(req, ev);
204 state->inode = sb.st_ino;
206 subreq = tevent_wakeup_send(
207 state,
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);
215 return 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);
224 bool status;
225 struct stat sb;
226 int ret;
228 status = tevent_wakeup_recv(subreq);
229 TALLOC_FREE(subreq);
230 if (! status) {
231 /* Ignore error */
232 fprintf(stderr,
233 "ctdb_mutex_fcntl_helper: "
234 "tevent_wakeup_recv() failed\n");
237 ret = stat(state->lock_file, &sb);
238 if (ret != 0) {
239 fprintf(stderr,
240 "ctdb_mutex_fcntl_helper: "
241 "lock lost - lock file \"%s\" check failed (ret=%d)\n",
242 state->lock_file,
243 errno);
244 tevent_req_done(req);
245 return;
248 if (sb.st_ino != state->inode) {
249 fprintf(stderr,
250 "ctdb_mutex_fcntl_helper: "
251 "lock lost - lock file \"%s\" inode changed\n",
252 state->lock_file);
253 tevent_req_done(req);
254 return;
257 subreq = tevent_wakeup_send(
258 state,
259 state->ev,
260 tevent_timeval_current_ofs(state->recheck_time, 0));
261 if (tevent_req_nomem(subreq, req)) {
262 return;
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)) {
270 return false;
273 return true;
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,
288 pid_t ppid,
289 const char *lock_file,
290 int fd,
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);
297 if (req == NULL) {
298 return NULL;
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,
310 lock_file,
312 recheck_time);
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);
319 return 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);
326 bool status;
328 status = wait_for_parent_recv(subreq);
329 TALLOC_FREE(subreq);
330 if (! status) {
331 /* Ignore error */
332 fprintf(stderr,
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);
344 bool status;
346 status = wait_for_lost_recv(subreq);
347 TALLOC_FREE(subreq);
348 if (! status) {
349 /* Ignore error */
350 fprintf(stderr,
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)) {
361 return false;
364 return true;
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;
375 char result;
376 int ppid;
377 const char *file = NULL;
378 unsigned long recheck_time;
379 int ret;
380 int fd = -1;
381 struct tevent_req *req;
382 bool status;
384 progname = argv[0];
386 if (argc < 2 || argc > 3) {
387 usage();
388 exit(1);
391 ev = tevent_context_init(NULL);
392 if (ev == NULL) {
393 fprintf(stderr, "locking: tevent_context_init() failed\n");
394 exit(1);
397 ppid = getppid();
399 file = argv[1];
401 recheck_time = 60;
402 if (argc == 3) {
403 recheck_time = smb_strtoul(argv[2],
404 NULL,
406 &ret,
407 SMB_STR_STANDARD);
408 if (ret != 0) {
409 usage();
410 exit(1);
414 result = fcntl_lock(file, &fd);
415 sys_write(STDOUT_FILENO, &result, 1);
417 if (result != '0') {
418 return 0;
421 req = wait_for_exit_send(ev, ev, ppid, file, fd, recheck_time);
422 if (req == NULL) {
423 fprintf(stderr,
424 "%s: wait_for_exit_send() failed\n",
425 progname);
426 exit(1);
429 tevent_req_poll(req, ev);
431 status = wait_for_exit_recv(req);
432 if (! status) {
433 fprintf(stderr,
434 "%s: wait_for_exit_recv() failed\n",
435 progname);
438 if (fd != -1) {
439 close(fd);
442 return 0;