29 |
#include <string.h> |
#include <string.h> |
30 |
#include <time.h> |
#include <time.h> |
31 |
#include <unistd.h> |
#include <unistd.h> |
32 |
|
#include <fcntl.h> |
33 |
#include <utime.h> |
#include <utime.h> |
34 |
|
|
35 |
#include <sys/param.h> |
#include <sys/param.h> |
51 |
int refcnt; |
int refcnt; |
52 |
|
|
53 |
char *file; |
char *file; |
|
|
|
|
char *dotlock; |
|
|
char *nfslock; |
|
|
|
|
54 |
int flags; |
int flags; |
|
|
|
55 |
int expire_time; |
int expire_time; |
56 |
int retries; |
int retries; |
57 |
int retry_sleep; |
int retry_sleep; |
58 |
char *external; |
|
59 |
|
union lock_data { |
60 |
|
struct { |
61 |
|
char *dotlock; |
62 |
|
char *nfslock; |
63 |
|
} dot; |
64 |
|
|
65 |
|
struct { |
66 |
|
char *name; |
67 |
|
} external; |
68 |
|
|
69 |
|
struct { |
70 |
|
int fd; |
71 |
|
} kernel; |
72 |
|
} data; |
73 |
}; |
}; |
74 |
|
|
75 |
/* Assert that we're managing the refcnt and fd correctly, either |
/* Assert that we're managing the refcnt and fd correctly, either |
78 |
*/ |
*/ |
79 |
#define INVARIANT(l) assert((l)->refcnt >= 0); |
#define INVARIANT(l) assert((l)->refcnt >= 0); |
80 |
|
|
81 |
static void expire_stale_lock (locker_t lock); |
static void expire_stale_lock __P((locker_t lock)); |
82 |
static int stat_check (const char *file, int fd, int links); |
static int stat_check __P((const char *file, int fd, int links)); |
83 |
static int check_file_permissions (const char *file); |
static int check_file_permissions __P((const char *file)); |
84 |
static int lock_external(locker_t l, int lock); |
static int lock_external __P((locker_t l, int lock)); |
85 |
|
static int _locker_lock_dotlock __P((locker_t lock)); |
86 |
|
static int _locker_unlock_dotlock __P((locker_t lock)); |
87 |
|
static int _locker_lock_kernel __P((locker_t lock)); |
88 |
|
static int _locker_unlock_kernel __P((locker_t lock)); |
89 |
|
|
90 |
int |
int |
91 |
locker_create (locker_t *plocker, const char *filename, int flags) |
locker_create (locker_t *plocker, const char *filename, int flags) |
95 |
if (plocker == NULL) |
if (plocker == NULL) |
96 |
return MU_ERR_OUT_PTR_NULL; |
return MU_ERR_OUT_PTR_NULL; |
97 |
|
|
98 |
if(filename == NULL) |
if (filename == NULL) |
99 |
return EINVAL; |
return EINVAL; |
100 |
|
|
101 |
l = calloc (1, sizeof (*l)); |
l = calloc (1, sizeof (*l)); |
103 |
return ENOMEM; |
return ENOMEM; |
104 |
|
|
105 |
/* Should make l->file be the resulting of following the symlinks. */ |
/* Should make l->file be the resulting of following the symlinks. */ |
106 |
l->file = strdup(filename); |
l->file = strdup (filename); |
107 |
if (l->file == NULL) |
if (l->file == NULL) |
108 |
{ |
{ |
109 |
free (l); |
free (l); |
110 |
return ENOMEM; |
return ENOMEM; |
111 |
} |
} |
112 |
|
|
|
l->dotlock = malloc(strlen(l->file) + 5 /*strlen(".lock")*/ + 1); |
|
|
|
|
|
if(!l->dotlock) |
|
|
{ |
|
|
free (l->file); |
|
|
free (l); |
|
|
return ENOMEM; |
|
|
} |
|
|
|
|
|
sprintf(l->dotlock, "%s.lock", l->file); |
|
|
|
|
113 |
if (strcmp (filename, "/dev/null") == 0) |
if (strcmp (filename, "/dev/null") == 0) |
114 |
l->flags = MU_LOCKER_NULL; |
l->flags = MU_LOCKER_NULL; |
115 |
else if (flags) |
else if (flags) |
121 |
l->retries = MU_LOCKER_RETRIES; |
l->retries = MU_LOCKER_RETRIES; |
122 |
l->retry_sleep = MU_LOCKER_RETRY_SLEEP; |
l->retry_sleep = MU_LOCKER_RETRY_SLEEP; |
123 |
|
|
124 |
if(flags & MU_LOCKER_EXTERNAL) |
/* Initialize locker-type-specific data */ |
125 |
{ |
if (flags & MU_LOCKER_EXTERNAL) |
|
if(!(l->external = strdup(MU_LOCKER_EXTERNAL_PROGRAM))) |
|
126 |
{ |
{ |
127 |
locker_destroy(&l); |
if (!(l->data.external.name = strdup (MU_LOCKER_EXTERNAL_PROGRAM))) |
128 |
return ENOMEM; |
{ |
129 |
|
locker_destroy (&l); |
130 |
|
return ENOMEM; |
131 |
|
} |
132 |
|
} |
133 |
|
else if (!(flags & MU_LOCKER_KERNEL)) |
134 |
|
{ |
135 |
|
l->data.dot.dotlock = malloc (strlen (l->file) |
136 |
|
+ 5 /*strlen(".lock")*/ + 1); |
137 |
|
|
138 |
|
if (!l->data.dot.dotlock) |
139 |
|
{ |
140 |
|
free (l->file); |
141 |
|
free (l); |
142 |
|
return ENOMEM; |
143 |
|
} |
144 |
|
|
145 |
|
sprintf (l->data.dot.dotlock, "%s.lock", l->file); |
146 |
} |
} |
|
} |
|
147 |
|
|
148 |
INVARIANT(l); |
INVARIANT(l); |
149 |
|
|
153 |
} |
} |
154 |
|
|
155 |
void |
void |
156 |
|
_locker_destroy_private (locker_t locker) |
157 |
|
{ |
158 |
|
if (locker) |
159 |
|
{ |
160 |
|
if (locker->flags & MU_LOCKER_EXTERNAL) |
161 |
|
free (locker->data.external.name); |
162 |
|
else if (locker->flags & MU_LOCKER_KERNEL) |
163 |
|
/* nothing */; |
164 |
|
else |
165 |
|
{ |
166 |
|
free (locker->data.dot.dotlock); |
167 |
|
free (locker->data.dot.nfslock); |
168 |
|
} |
169 |
|
} |
170 |
|
} |
171 |
|
|
172 |
|
void |
173 |
locker_destroy (locker_t *plocker) |
locker_destroy (locker_t *plocker) |
174 |
{ |
{ |
175 |
if (plocker && *plocker) |
if (plocker && *plocker) |
176 |
{ |
{ |
177 |
|
_locker_destroy_private (*plocker); |
178 |
free ((*plocker)->file); |
free ((*plocker)->file); |
|
free ((*plocker)->dotlock); |
|
|
free ((*plocker)->nfslock); |
|
|
free ((*plocker)->external); |
|
179 |
free (*plocker); |
free (*plocker); |
180 |
*plocker = NULL; |
*plocker = NULL; |
181 |
} |
} |
197 |
if (!locker) |
if (!locker) |
198 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
199 |
|
|
200 |
if(etime <= 0) |
if (etime <= 0) |
201 |
return EINVAL; |
return EINVAL; |
202 |
|
|
203 |
locker->expire_time = etime; |
locker->expire_time = etime; |
211 |
if (!locker) |
if (!locker) |
212 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
213 |
|
|
214 |
if(retries <= 0) |
if (retries <= 0) |
215 |
return EINVAL; |
return EINVAL; |
216 |
|
|
217 |
locker->retries = retries; |
locker->retries = retries; |
225 |
if (!locker) |
if (!locker) |
226 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
227 |
|
|
228 |
if(retry_sleep <= 0) |
if (retry_sleep <= 0) |
229 |
return EINVAL; |
return EINVAL; |
230 |
|
|
231 |
locker->retry_sleep = retry_sleep; |
locker->retry_sleep = retry_sleep; |
240 |
|
|
241 |
if (!locker) |
if (!locker) |
242 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
243 |
|
if (!(locker->flags & MU_LOCKER_EXTERNAL)) |
244 |
|
return EINVAL; |
245 |
|
|
246 |
/* program can be NULL */ |
/* program can be NULL */ |
247 |
if(program != 0) |
if (program != 0) |
248 |
{ |
{ |
249 |
p = strdup(program); |
p = strdup (program); |
250 |
if(!p) |
if (!p) |
251 |
return ENOMEM; |
return ENOMEM; |
252 |
} |
} |
253 |
|
|
254 |
free(locker->external); |
free (locker->data.external.name); |
255 |
locker->external = p; |
locker->data.external.name = p; |
256 |
|
|
257 |
return 0; |
return 0; |
258 |
} |
} |
259 |
|
|
260 |
int locker_get_flags (locker_t locker, int *flags) |
int |
261 |
|
locker_get_flags (locker_t locker, int *flags) |
262 |
{ |
{ |
263 |
if (!locker) |
if (!locker) |
264 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
277 |
if (!locker) |
if (!locker) |
278 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
279 |
|
|
280 |
if(!ptime) |
if (!ptime) |
281 |
return EINVAL; |
return EINVAL; |
282 |
|
|
283 |
*ptime = locker->expire_time; |
*ptime = locker->expire_time; |
291 |
if (!locker) |
if (!locker) |
292 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
293 |
|
|
294 |
if(!retries) |
if (!retries) |
295 |
return EINVAL; |
return EINVAL; |
296 |
|
|
297 |
*retries = locker->retries; |
*retries = locker->retries; |
305 |
if (!locker) |
if (!locker) |
306 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
307 |
|
|
308 |
if(!retry_sleep) |
if (!retry_sleep) |
309 |
return EINVAL; |
return EINVAL; |
310 |
|
|
311 |
*retry_sleep = locker->retry_sleep; |
*retry_sleep = locker->retry_sleep; |
316 |
int |
int |
317 |
locker_lock (locker_t lock) |
locker_lock (locker_t lock) |
318 |
{ |
{ |
319 |
int err = 0; |
int rc; |
|
int fd = -1; |
|
320 |
int retries = 1; |
int retries = 1; |
321 |
|
|
322 |
if (lock == NULL) |
if (lock == NULL) |
333 |
return 0; |
return 0; |
334 |
} |
} |
335 |
|
|
336 |
|
if (access (lock->file, W_OK)) |
337 |
|
{ |
338 |
|
/* There is no use trying to lock the file if we are not |
339 |
|
allowed to write to it */ |
340 |
|
_locker_destroy_private (lock); |
341 |
|
lock->flags |= MU_LOCKER_NULL; |
342 |
|
return 0; |
343 |
|
} |
344 |
|
|
345 |
|
/* Check we are trying to lock a regular file, with a link count |
346 |
|
of 1, that we have permission to read, etc., or don't lock it. */ |
347 |
|
if ((rc = check_file_permissions(lock->file))) |
348 |
|
return rc; |
349 |
|
|
350 |
/* Do the lock with an external program, if requested. */ |
/* Do the lock with an external program, if requested. */ |
351 |
if (lock->flags & MU_LOCKER_EXTERNAL) |
if (lock->flags & MU_LOCKER_EXTERNAL) |
352 |
{ |
return lock_external (lock, 1); |
353 |
return lock_external(lock, 1); |
else if (!(lock->flags & MU_LOCKER_KERNEL)) |
354 |
} |
{ |
355 |
|
char *tmp, *p; |
356 |
|
|
357 |
/* Check we are trying to lock a regular file, with a link count |
tmp = strdup (lock->file); |
358 |
of 1, that we have permission to read, etc., or don't lock it. */ |
if (!tmp) |
359 |
if((err = check_file_permissions(lock->file))) |
return ENOMEM; |
360 |
return err; |
|
361 |
|
strcpy (tmp, lock->file); |
362 |
|
p = strrchr (tmp, '/'); |
363 |
|
/* A '/' must be present, if not we'll coredump, and that's |
364 |
|
correct! */ |
365 |
|
*p = 0; |
366 |
|
|
367 |
|
if (access (tmp, W_OK)) |
368 |
|
{ |
369 |
|
/* Fallback to kernel locking */ |
370 |
|
_locker_destroy_private (lock); |
371 |
|
lock->flags |= MU_LOCKER_KERNEL; |
372 |
|
} |
373 |
|
free (tmp); |
374 |
|
} |
375 |
|
|
376 |
if (lock->flags & MU_LOCKER_RETRY) |
if (lock->flags & MU_LOCKER_RETRY) |
377 |
{ |
{ |
380 |
|
|
381 |
while (retries--) |
while (retries--) |
382 |
{ |
{ |
383 |
/* cleanup after last loop */ |
if (lock->flags & MU_LOCKER_KERNEL) |
384 |
close (fd); |
rc = _locker_lock_kernel (lock); |
|
fd = -1; |
|
|
if (lock->nfslock) |
|
|
{ |
|
|
unlink (lock->nfslock); |
|
|
free (lock->nfslock); |
|
|
lock->nfslock = 0; |
|
|
} |
|
|
|
|
|
expire_stale_lock (lock); |
|
|
|
|
|
/* build the NFS hitching-post to the lock file */ |
|
|
{ |
|
|
char host[MAXHOSTNAMELEN + 1] = "localhost"; |
|
|
char pid[11]; /* 10 is strlen(2^32 = 4294967296) */ |
|
|
char now[11]; |
|
|
size_t sz = 0; |
|
|
|
|
|
gethostname (host, sizeof (host)); |
|
|
host[MAXHOSTNAMELEN] = 0; |
|
|
|
|
|
snprintf (pid, sizeof (pid), "%d", getpid ()); |
|
|
pid[sizeof (pid) - 1] = 0; |
|
|
|
|
|
snprintf (now, sizeof (now), "%d", time (0)); |
|
|
now[sizeof (now) - 1] = 0; |
|
|
|
|
|
sz = strlen (lock->file) + 1 /* "." */ + |
|
|
strlen (pid) + 1 /* "." */ + |
|
|
strlen (now) + 1 /* "." */ + |
|
|
strlen (host) + 1; |
|
|
|
|
|
lock->nfslock = malloc (sz); |
|
|
|
|
|
if (!lock->nfslock) |
|
|
return ENOMEM; |
|
|
|
|
|
snprintf (lock->nfslock, sz, "%s.%s.%s.%s", lock->file, pid, now, |
|
|
host); |
|
|
lock->nfslock[sz - 1] = 0; |
|
|
} |
|
|
|
|
|
fd = open (lock->nfslock, O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR); |
|
|
|
|
|
if (fd == -1) |
|
|
{ |
|
|
if (errno == EEXIST) |
|
|
continue; /* Try with another lockfile name. */ |
|
|
else |
|
|
return errno; |
|
|
} |
|
|
close (fd); |
|
|
|
|
|
fd = -1; |
|
|
|
|
|
|
|
|
/* Try to link to the lockfile. */ |
|
|
if (link (lock->nfslock, lock->dotlock) == -1) |
|
|
{ |
|
|
err = errno; |
|
|
|
|
|
if (err != EEXIST) |
|
|
break; /* fatal */ |
|
|
} |
|
385 |
else |
else |
386 |
{ |
rc = _locker_lock_dotlock (lock); |
387 |
if ((fd = open (lock->dotlock, O_RDONLY)) == -1) |
if (rc == EAGAIN && retries) |
|
{ |
|
|
err = errno; |
|
|
break; /* fatal, we just made it, we should be able to open! */ |
|
|
} |
|
|
err = stat_check (lock->nfslock, fd, 2); |
|
|
if (err == 0) |
|
|
{ |
|
|
/* We got the lock! */ |
|
|
break; |
|
|
} |
|
|
else |
|
|
{ |
|
|
unlink (lock->dotlock); |
|
|
unlink (lock->nfslock); |
|
|
|
|
|
/* If there was something invalid about the file/fd, return |
|
|
a more descriptive code. */ |
|
|
if (err == EINVAL) |
|
|
err = MU_ERR_LOCK_BAD_LOCK; |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
if (retries) |
|
388 |
sleep (lock->retry_sleep); |
sleep (lock->retry_sleep); |
389 |
|
else if (rc) |
390 |
|
return rc; |
391 |
|
else /* rc == 0 */ |
392 |
|
break; |
393 |
} |
} |
394 |
|
|
|
unlink (lock->nfslock); |
|
|
|
|
|
/* EEXIST means somebody else has the lock, anything else is an |
|
|
error. */ |
|
|
if (err == EEXIST) |
|
|
err = MU_ERR_LOCK_CONFLICT; |
|
|
|
|
|
if (err) |
|
|
return err; |
|
|
|
|
|
/* If no errors, we have the lock. */ |
|
|
assert (lock->refcnt == 0); |
|
|
|
|
|
if (lock->flags & MU_LOCKER_PID) |
|
|
{ |
|
|
char buf[16]; |
|
|
sprintf (buf, "%ld", (long) getpid ()); |
|
|
write (fd, buf, strlen (buf)); |
|
|
} |
|
|
|
|
|
close(fd); |
|
|
|
|
395 |
lock->refcnt = 1; |
lock->refcnt = 1; |
396 |
|
|
397 |
return 0; |
return 0; |
406 |
if (lock->flags == MU_LOCKER_NULL) |
if (lock->flags == MU_LOCKER_NULL) |
407 |
return 0; |
return 0; |
408 |
|
|
409 |
if(lock->refcnt > 0) |
if (lock->refcnt > 0) |
410 |
return utime (lock->dotlock, NULL); |
return utime (lock->data.dot.dotlock, NULL); |
411 |
|
|
412 |
return MU_ERR_LOCK_NOT_HELD; |
return MU_ERR_LOCK_NOT_HELD; |
413 |
} |
} |
415 |
int |
int |
416 |
locker_unlock (locker_t lock) |
locker_unlock (locker_t lock) |
417 |
{ |
{ |
418 |
int err = 0; |
int rc = 0; |
419 |
|
|
420 |
if (!lock) |
if (!lock) |
421 |
return MU_ERR_LOCKER_NULL; |
return MU_ERR_LOCKER_NULL; |
428 |
|
|
429 |
/* Do the lock with an external program, if requested. */ |
/* Do the lock with an external program, if requested. */ |
430 |
if (lock->flags & MU_LOCKER_EXTERNAL) |
if (lock->flags & MU_LOCKER_EXTERNAL) |
431 |
{ |
return lock_external (lock, 0); |
432 |
return lock_external (lock, 0); |
|
|
} |
|
|
|
|
433 |
if (lock->refcnt > 1) |
if (lock->refcnt > 1) |
434 |
{ |
{ |
435 |
lock->refcnt--; |
lock->refcnt--; |
436 |
return 0; |
return 0; |
437 |
} |
} |
438 |
|
|
439 |
if ((err = check_file_permissions (lock->file))) |
if ((rc = check_file_permissions (lock->file))) |
440 |
return err; |
return rc; |
|
|
|
|
if (unlink (lock->dotlock) == -1) |
|
|
{ |
|
|
err = errno; |
|
|
if (err == ENOENT) |
|
|
{ |
|
|
lock->refcnt = 0; |
|
|
err = MU_ERR_LOCK_NOT_HELD; |
|
|
return err; |
|
|
} |
|
441 |
|
|
442 |
return err; |
if (lock->flags & MU_LOCKER_KERNEL) |
443 |
} |
rc = _locker_unlock_kernel (lock); |
444 |
|
else |
445 |
|
rc = _locker_unlock_dotlock (lock); |
446 |
|
|
447 |
lock->refcnt = 0; |
if (rc == 0) |
448 |
|
lock->refcnt = 0; |
449 |
|
|
450 |
return 0; |
return rc; |
451 |
} |
} |
452 |
|
|
453 |
int |
int |
463 |
|
|
464 |
/* Force the reference count to 1 to unlock the file. */ |
/* Force the reference count to 1 to unlock the file. */ |
465 |
lock->refcnt = 1; |
lock->refcnt = 1; |
466 |
err = locker_unlock(lock); |
err = locker_unlock (lock); |
467 |
|
|
468 |
return err; |
return err; |
469 |
} |
} |
473 |
expire_stale_lock (locker_t lock) |
expire_stale_lock (locker_t lock) |
474 |
{ |
{ |
475 |
int stale = 0; |
int stale = 0; |
476 |
int fd = open (lock->dotlock, O_RDONLY); |
int fd = open (lock->data.dot.dotlock, O_RDONLY); |
477 |
if (fd == -1) |
if (fd == -1) |
478 |
return; |
return; |
479 |
|
|
497 |
stale = 1; /* Corrupted file, remove the lock. */ |
stale = 1; /* Corrupted file, remove the lock. */ |
498 |
} |
} |
499 |
} |
} |
500 |
|
|
501 |
/* Check to see if the lock expired. */ |
/* Check to see if the lock expired. */ |
502 |
if (lock->flags & MU_LOCKER_TIME) |
if (lock->flags & MU_LOCKER_TIME) |
503 |
{ |
{ |
511 |
|
|
512 |
close (fd); |
close (fd); |
513 |
if (stale) |
if (stale) |
514 |
unlink (lock->dotlock); |
unlink (lock->data.dot.dotlock); |
515 |
} |
} |
516 |
|
|
517 |
static int |
static int |
522 |
int err = 0; |
int err = 0; |
523 |
int localfd = -1; |
int localfd = -1; |
524 |
|
|
525 |
if(fd == -1) |
if (fd == -1) |
526 |
{ |
{ |
527 |
localfd = open(file, O_RDONLY); |
localfd = open(file, O_RDONLY); |
528 |
|
|
529 |
if(localfd == -1) |
if (localfd == -1) |
530 |
return errno; |
return errno; |
531 |
fd = localfd; |
fd = localfd; |
532 |
} |
} |
533 |
|
|
534 |
/* We should always be able to stat a valid fd, so this |
/* We should always be able to stat a valid fd, so this |
535 |
is an error condition. */ |
is an error condition. */ |
554 |
|
|
555 |
#undef CHK |
#undef CHK |
556 |
} |
} |
557 |
if(localfd != -1) |
if (localfd != -1) |
558 |
close(localfd); |
close (localfd); |
559 |
|
|
560 |
return err; |
return err; |
561 |
} |
} |
567 |
int err = 0; |
int err = 0; |
568 |
|
|
569 |
if ((fd = open (file, O_RDONLY)) == -1) |
if ((fd = open (file, O_RDONLY)) == -1) |
570 |
return errno; |
return errno == ENOENT ? 0 : errno; |
571 |
|
|
572 |
err = stat_check (file, fd, 1); |
err = stat_check (file, fd, 1); |
573 |
close (fd); |
close (fd); |
582 |
return 0; |
return 0; |
583 |
} |
} |
584 |
|
|
585 |
|
/* Locker-specific lock/unlock functions */ |
586 |
|
int |
587 |
|
_locker_lock_dotlock (locker_t lock) |
588 |
|
{ |
589 |
|
char host[MAXHOSTNAMELEN + 1] = "localhost"; |
590 |
|
char pid[11]; /* 10 is strlen(2^32 = 4294967296) */ |
591 |
|
char now[11]; |
592 |
|
size_t sz = 0; |
593 |
|
int err = 0; |
594 |
|
int fd; |
595 |
|
|
596 |
|
if (lock->data.dot.nfslock) |
597 |
|
{ |
598 |
|
unlink (lock->data.dot.nfslock); |
599 |
|
free (lock->data.dot.nfslock); |
600 |
|
lock->data.dot.nfslock = 0; |
601 |
|
} |
602 |
|
|
603 |
|
expire_stale_lock (lock); |
604 |
|
|
605 |
|
/* build the NFS hitching-post to the lock file */ |
606 |
|
|
607 |
|
gethostname (host, sizeof (host)); |
608 |
|
host[MAXHOSTNAMELEN] = 0; |
609 |
|
|
610 |
|
snprintf (now, sizeof (now), "%d", time (0)); |
611 |
|
now[sizeof (now) - 1] = 0; |
612 |
|
|
613 |
|
snprintf (pid, sizeof (pid), "%d", getpid ()); |
614 |
|
pid[sizeof (pid) - 1] = 0; |
615 |
|
|
616 |
|
sz = strlen (lock->file) + 1 /* "." */ |
617 |
|
+ strlen (pid) + 1 /* "." */ |
618 |
|
+ strlen (now) + 1 /* "." */ |
619 |
|
+ strlen (host) + 1; |
620 |
|
|
621 |
|
lock->data.dot.nfslock = malloc (sz); |
622 |
|
|
623 |
|
if (!lock->data.dot.nfslock) |
624 |
|
return ENOMEM; |
625 |
|
|
626 |
|
snprintf (lock->data.dot.nfslock, sz, "%s.%s.%s.%s", lock->file, pid, now, |
627 |
|
host); |
628 |
|
|
629 |
|
fd = open (lock->data.dot.nfslock, |
630 |
|
O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR); |
631 |
|
if (fd == -1) |
632 |
|
{ |
633 |
|
if (errno == EEXIST) |
634 |
|
return EAGAIN; |
635 |
|
else |
636 |
|
return errno; |
637 |
|
} |
638 |
|
close (fd); |
639 |
|
|
640 |
|
/* Try to link to the lockfile. */ |
641 |
|
if (link (lock->data.dot.nfslock, lock->data.dot.dotlock) == -1) |
642 |
|
{ |
643 |
|
if (errno == EEXIST) |
644 |
|
return MU_ERR_LOCK_CONFLICT; |
645 |
|
return errno; |
646 |
|
} |
647 |
|
|
648 |
|
if ((fd = open (lock->data.dot.dotlock, O_RDONLY)) == -1) |
649 |
|
return errno; |
650 |
|
err = stat_check (lock->data.dot.nfslock, fd, 2); |
651 |
|
if (err) |
652 |
|
{ |
653 |
|
if (err == EINVAL) |
654 |
|
return MU_ERR_LOCK_BAD_LOCK; |
655 |
|
return errno; |
656 |
|
} |
657 |
|
|
658 |
|
unlink (lock->data.dot.nfslock); |
659 |
|
|
660 |
|
/* If no errors, we have the lock. */ |
661 |
|
assert (lock->refcnt == 0); |
662 |
|
|
663 |
|
if (lock->flags & MU_LOCKER_PID) |
664 |
|
{ |
665 |
|
char buf[16]; |
666 |
|
sprintf (buf, "%ld", (long) getpid ()); |
667 |
|
write (fd, buf, strlen (buf)); |
668 |
|
} |
669 |
|
close(fd); |
670 |
|
return 0; |
671 |
|
} |
672 |
|
|
673 |
|
int |
674 |
|
_locker_unlock_dotlock (locker_t lock) |
675 |
|
{ |
676 |
|
if (unlink (lock->data.dot.dotlock) == -1) |
677 |
|
{ |
678 |
|
int err = errno; |
679 |
|
if (err == ENOENT) |
680 |
|
{ |
681 |
|
lock->refcnt = 0; |
682 |
|
err = MU_ERR_LOCK_NOT_HELD; |
683 |
|
return err; |
684 |
|
} |
685 |
|
return err; |
686 |
|
} |
687 |
|
return 0; |
688 |
|
} |
689 |
|
|
690 |
|
int |
691 |
|
_locker_lock_kernel (locker_t lock) |
692 |
|
{ |
693 |
|
int fd; |
694 |
|
struct flock fl; |
695 |
|
|
696 |
|
fd = open (lock->file, O_RDWR); |
697 |
|
if (fd == -1) |
698 |
|
return errno; |
699 |
|
lock->data.kernel.fd = fd; |
700 |
|
fl.l_type = F_WRLCK; |
701 |
|
fl.l_whence = SEEK_SET; |
702 |
|
fl.l_start = 0; |
703 |
|
fl.l_len = 0; /* Lock entire file */ |
704 |
|
if (fcntl (fd, F_SETLK, &fl)) |
705 |
|
{ |
706 |
|
#ifdef EACCESS |
707 |
|
if (errno == EACCESS) |
708 |
|
return EAGAIN; |
709 |
|
#endif |
710 |
|
if (errno == EAGAIN) |
711 |
|
return EAGAIN; |
712 |
|
return errno; |
713 |
|
} |
714 |
|
return 0; |
715 |
|
} |
716 |
|
|
717 |
|
int |
718 |
|
_locker_unlock_kernel (locker_t lock) |
719 |
|
{ |
720 |
|
struct flock fl; |
721 |
|
|
722 |
|
fl.l_type = F_UNLCK; |
723 |
|
fl.l_whence = SEEK_SET; |
724 |
|
fl.l_start = 0; |
725 |
|
fl.l_len = 0; /* Unlock entire file */ |
726 |
|
if (fcntl (lock->data.kernel.fd, F_SETLK, &fl)) |
727 |
|
{ |
728 |
|
#ifdef EACCESS |
729 |
|
if (errno == EACCESS) |
730 |
|
return EAGAIN; |
731 |
|
#endif |
732 |
|
if (errno == EAGAIN) |
733 |
|
return EAGAIN; |
734 |
|
return errno; |
735 |
|
} |
736 |
|
close (lock->data.kernel.fd); |
737 |
|
return 0; |
738 |
|
} |
739 |
|
|
740 |
/* |
/* |
741 |
Estimate 1 decimal digit per 3 bits, + 1 for round off. |
Estimate 1 decimal digit per 3 bits, + 1 for round off. |
742 |
*/ |
*/ |
757 |
assert (lock == !l->refcnt); |
assert (lock == !l->refcnt); |
758 |
/* lock is true, refcnt is 0 or lock is false and refcnt is 1 */ |
/* lock is true, refcnt is 0 or lock is false and refcnt is 1 */ |
759 |
|
|
760 |
av[ac++] = l->external ? l->external : MU_LOCKER_EXTERNAL_PROGRAM; |
av[ac++] = l->data.external.name ? |
761 |
|
l->data.external.name : MU_LOCKER_EXTERNAL_PROGRAM; |
762 |
|
|
763 |
if (l->flags & MU_LOCKER_TIME) |
if (l->flags & MU_LOCKER_TIME) |
764 |
{ |
{ |
766 |
aforce[sizeof (aforce) - 1] = 0; |
aforce[sizeof (aforce) - 1] = 0; |
767 |
av[ac++] = aforce; |
av[ac++] = aforce; |
768 |
} |
} |
769 |
|
|
770 |
if (l->flags & MU_LOCKER_RETRY) |
if (l->flags & MU_LOCKER_RETRY) |
771 |
{ |
{ |
772 |
snprintf (aretry, sizeof (aretry), "-r%d", l->retries); |
snprintf (aretry, sizeof (aretry), "-r%d", l->retries); |
773 |
aretry[sizeof (aretry) - 1] = 0; |
aretry[sizeof (aretry) - 1] = 0; |
774 |
av[ac++] = aretry; |
av[ac++] = aretry; |
775 |
} |
} |
776 |
|
|
777 |
if (lock == 0) |
if (lock == 0) |
778 |
{ |
{ |
779 |
av[ac++] = "-u"; |
av[ac++] = "-u"; |