1 /*
2 * Copyright (c) 2007, XenSource Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of XenSource Inc. nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * This module implements a "dot locking" style advisory file locking algorithm.
31 */
32
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <time.h>
42 #include <dirent.h>
43 #include <limits.h>
44 #include "lock.h"
45
46 #define unlikely(x) __builtin_expect(!!(x), 0)
47
48 /* format: xenlk.hostname.uuid.<xf><rw>*/
49 #define LF_POSTFIX ".xenlk"
50 #define LFXL_FORMAT LF_POSTFIX ".%s.%s.x%s"
51 #define LFFL_FORMAT LF_POSTFIX ".%s.%s.f%s"
52 #define RETRY_MAX 16
53
54 #if defined(LOGS)
55 #define LOG(format, args...) printf("%d: ", __LINE__); printf(format, ## args)
56 #else
57 #define LOG(format, args...)
58 #endif
59
60 /* random wait - up to .5 seconds */
61 #define XSLEEP usleep(random() & 0x7ffff)
62
63 typedef int (*eval_func)(char *name, int readonly);
64
create_lockfn(char * fn_to_lock)65 static char *create_lockfn(char *fn_to_lock)
66 {
67 char *lockfn;
68
69 /* allocate string to hold constructed lock file */
70 lockfn = malloc(strlen(fn_to_lock) + strlen(LF_POSTFIX) + 1);
71 if (unlikely(!lockfn)) {
72 return 0;
73 }
74
75 /* append postfix to file to lock */
76 strcpy(lockfn, fn_to_lock);
77 strcat(lockfn, LF_POSTFIX);
78
79 return lockfn;
80 }
81
create_lockfn_link(char * fn_to_lock,char * format,char * uuid,int readonly)82 static char *create_lockfn_link(char *fn_to_lock, char *format,
83 char *uuid, int readonly)
84 {
85 char hostname[128];
86 char *lockfn_link;
87 char *ptr;
88
89 /* get hostname */
90 if (unlikely(gethostname(hostname, sizeof(hostname)) == -1)) {
91 return 0;
92 }
93
94 /* allocate string to hold constructed lock file link */
95 lockfn_link = malloc(strlen(fn_to_lock) + strlen(LF_POSTFIX) +
96 strlen(hostname) + strlen(uuid) + 8);
97 if (unlikely(!lockfn_link)) {
98 return 0;
99 }
100
101 /* construct lock file link with specific format */
102 strcpy(lockfn_link, fn_to_lock);
103 ptr = lockfn_link + strlen(lockfn_link);
104 sprintf(ptr, format, hostname, uuid, readonly ? "r" : "w");
105
106 return lockfn_link;
107 }
108
NFSnormalizedStatTime(char * fn,struct stat * statnow,int * reterrno)109 static int NFSnormalizedStatTime(char *fn, struct stat *statnow, int *reterrno)
110 {
111 int result = LOCK_OK;
112 int uniq;
113 char *buf;
114 int fd;
115 int pid = (int)getpid();
116 int clstat;
117
118 *reterrno = 0;
119
120 /* create file to normalize time */
121 srandom((int)time(0) ^ pid);
122 uniq = random() % 0xffffff;
123 buf = malloc(strlen(fn) + 24);
124 if (unlikely(!buf)) { result = LOCK_ENOMEM; goto finish; }
125
126 strcpy(buf, fn);
127 sprintf(buf + strlen(buf), ".xen%08d.tmp", uniq);
128
129 fd = open(buf, O_WRONLY | O_CREAT, 0644);
130 if (fd == -1) { *reterrno = errno; result = LOCK_EOPEN; goto finish; }
131 clstat = close(fd);
132 if (unlikely(clstat == -1)) {
133 LOG("fail on close\n");
134 }
135 if (lstat(buf, statnow) == -1) {
136 unlink(buf);
137 *reterrno = errno;
138 result = LOCK_ESTAT;
139 goto finish;
140 }
141 unlink(buf);
142
143 finish:
144 return result;
145 }
146
writer_eval(char * name,int readonly)147 static int writer_eval(char *name, int readonly)
148 {
149 return name[strlen(name)-1] == 'w';
150 }
151
reader_eval(char * name,int readonly)152 static int reader_eval(char *name, int readonly)
153 {
154 return name[strlen(name)-1] == 'r' && !readonly;
155 }
156
lock_holder(char * fn,char * lockfn,char * lockfn_link,int force,int readonly,int * stole,eval_func eval,int * elt,int * ioerror)157 static int lock_holder(char *fn, char *lockfn, char *lockfn_link,
158 int force, int readonly, int *stole, eval_func eval,
159 int *elt, int *ioerror)
160 {
161 int status = 0;
162 int ustat;
163 DIR *pd = 0;
164 struct dirent *dptr;
165 char *ptr;
166 char *dirname = malloc(strlen(lockfn));
167 char *uname = malloc(strlen(lockfn_link) + 8);
168 int elt_established = 0;
169 int fd;
170 char tmpbuf[4096];
171
172 *stole = 0;
173 *ioerror = 0;
174 *elt = 0;
175
176 if (!dirname) goto finish;
177 if (!uname) goto finish;
178
179 /* get directory */
180 ptr = strrchr(lockfn, '/');
181 if (!ptr) {
182 strcpy(dirname, ".");
183 } else {
184 int numbytes = ptr - lockfn;
185 strncpy(dirname, lockfn, numbytes);
186 dirname[numbytes] = '\0';
187 }
188 pd = opendir(dirname);
189 if (!pd) {
190 *ioerror = errno ? errno : EIO;
191 goto finish;
192 }
193
194 /*
195 * scan through directory entries and use eval function
196 * if we have a match (i.e. reader or writer lock) but
197 * note that if we are forcing, we will remove any and
198 * all locks that appear for target of our lock, regardless
199 * if it a reader/writer owns the lock.
200 */
201 errno = 0;
202 dptr = readdir(pd);
203 if (!dptr) {
204 *ioerror = EIO;
205 }
206 while (dptr) {
207 char *p1 = strrchr(fn, '/');
208 char *p2 = strrchr(lockfn, '/');
209 char *p3 = strrchr(lockfn_link, '/');
210 if (p1) p1+=1;
211 if (p2) p2+=1;
212 if (p3) p3+=1;
213 if (strcmp(dptr->d_name, p1 ? p1 : fn) &&
214 strcmp(dptr->d_name, p2 ? p2 : lockfn) &&
215 strcmp(dptr->d_name, p3 ? p3 : lockfn_link) &&
216 !strncmp(dptr->d_name, p1 ? p1 : fn, strlen(p1?p1:fn))) {
217 strcpy(uname, dirname);
218 strcat(uname, "/");
219 strcat(uname, dptr->d_name);
220 if (!elt_established) {
221 /* read final lock file and extract lease time */
222 fd = open(uname, O_RDONLY, 0644);
223 memset(tmpbuf, 0, sizeof(tmpbuf));
224 if (read(fd, tmpbuf, sizeof(tmpbuf)) < 0) {
225 *ioerror = errno;
226 status = 1;
227 close(fd);
228 goto finish;
229 }
230 close(fd);
231 ptr = strrchr(tmpbuf, '.');
232 if (ptr) {
233 *elt = atoi(ptr+1);
234 elt_established = 1;
235 }
236 }
237 if (force) {
238 ustat = unlink(uname);
239 if (ustat == -1) {
240 LOG("failed to unlink %s\n", uname);
241 }
242 *stole = 1;
243 *elt = 0;
244 } else {
245 if ((*eval)(dptr->d_name, readonly)) {
246 closedir(pd);
247 status = 1;
248 goto finish;
249 }
250 }
251 }
252 dptr = readdir(pd);
253 if (!dptr && errno) {
254 *ioerror = EIO;
255 }
256 }
257
258 closedir(pd);
259
260 finish:
261 free(dirname);
262 free(uname);
263
264 /* if IO error, force a taken status */
265 return (*ioerror) ? 1 : status;
266 }
267
lock(char * fn_to_lock,char * uuid,int force,int readonly,int * lease_time,int * retstatus)268 int lock(char *fn_to_lock, char *uuid, int force, int readonly, int *lease_time, int *retstatus)
269 {
270 char *lockfn = 0;
271 char *lockfn_xlink = 0;
272 char *lockfn_flink = 0;
273 char *buf = 0;
274 int fd;
275 int status = 0;
276 struct stat stat1, stat2;
277 int retry_attempts = 0;
278 int clstat;
279 int tmpstat;
280 int stealx = 0;
281 int stealw = 0;
282 int stealr = 0;
283 int established_lease_time = 0;
284 char tmpbuf[4096];
285 int ioerr;
286
287 if (!fn_to_lock || !uuid) {
288 *retstatus = LOCK_EBADPARM;
289 return EINVAL;
290 }
291
292 *retstatus = 0;
293
294 /* seed random with time/pid combo */
295 srandom((int)time(0) ^ getpid());
296
297 /* build lock file strings */
298 lockfn = create_lockfn(fn_to_lock);
299 if (unlikely(!lockfn)) { status = ENOMEM; *retstatus = LOCK_ENOMEM; goto finish; }
300
301 lockfn_xlink = create_lockfn_link(fn_to_lock, LFXL_FORMAT,
302 uuid, readonly);
303 if (unlikely(!lockfn_xlink)) { status = ENOMEM; *retstatus = LOCK_ENOMEM; goto finish; }
304
305 lockfn_flink = create_lockfn_link(fn_to_lock, LFFL_FORMAT, uuid,
306 readonly);
307 if (unlikely(!lockfn_flink)) { status = ENOMEM; *retstatus = LOCK_ENOMEM; goto finish; }
308
309 try_again:
310 if (retry_attempts++ > RETRY_MAX) {
311 if (*retstatus == LOCK_EXLOCK_OPEN) {
312 struct stat statnow, stat_exlock;
313 int diff;
314
315 if (lstat(lockfn, &stat_exlock) == -1) {
316 goto finish;
317 }
318
319 if (NFSnormalizedStatTime(fn_to_lock, &statnow, &ioerr)) {
320 goto finish;
321 }
322
323 diff = (int)statnow.st_mtime - (int)stat_exlock.st_mtime;
324 if (diff > DEFAULT_LEASE_TIME_SECS) {
325 unlink(lockfn);
326 retry_attempts = 0;
327 goto try_again;
328 }
329 }
330 goto finish;
331 }
332
333 /* try to open exlusive lockfile */
334 fd = open(lockfn, O_WRONLY | O_CREAT | O_EXCL, 0644);
335 if (fd == -1) {
336 LOG("Initial lockfile creation failed %s force=%d, errno=%d\n",
337 lockfn, force, errno);
338 if (errno == EIO) {
339 *retstatus = LOCK_EXLOCK_OPEN;
340 status = EIO;
341 goto finish;
342 }
343 /* already owned? (hostname & uuid match, skip time bits) */
344 errno = 0;
345 fd = open(lockfn, O_RDWR, 0644);
346 if (fd != -1) {
347 buf = malloc(strlen(lockfn_xlink)+1);
348 if (!buf) {
349 clstat = close(fd);
350 if (unlikely(clstat == -1)) {
351 LOG("fail on close\n");
352 }
353 *retstatus = LOCK_ENOMEM;
354 status = ENOMEM;
355 goto finish;
356 }
357 if (read(fd, buf, strlen(lockfn_xlink)) !=
358 (strlen(lockfn_xlink))) {
359 clstat = close(fd);
360 if (unlikely(clstat == -1)) {
361 LOG("fail on close\n");
362 }
363 free(buf);
364 goto force_lock;
365 }
366 if (!strncmp(buf, lockfn_xlink, strlen(lockfn_xlink)-1)) {
367 LOG("lock owned by us, reasserting\n");
368 /* our lock, reassert by rewriting below */
369 if (lseek(fd, 0, SEEK_SET) == -1) {
370 clstat = close(fd);
371 if (unlikely(clstat == -1)) {
372 LOG("fail on close\n");
373 }
374 goto force_lock;
375 }
376 free(buf);
377 goto skip;
378 }
379 free(buf);
380 clstat = close(fd);
381 if (unlikely(clstat == -1)) {
382 LOG("fail on close\n");
383 }
384 }
385 force_lock:
386 if (errno == EIO) {
387 *retstatus = LOCK_EXLOCK_OPEN;
388 status = EIO;
389 goto finish;
390 }
391 if (force) {
392 /* remove lock file, we are forcing lock, try again */
393 status = unlink(lockfn);
394 if (unlikely(status == -1)) {
395 if (errno == EIO) {
396 *retstatus = LOCK_EXLOCK_OPEN;
397 status = EIO;
398 goto finish;
399 }
400 LOG("force removal of %s lockfile failed, "
401 "errno=%d, trying again\n", lockfn, errno);
402 }
403 stealx = 1;
404 }
405 XSLEEP;
406 *retstatus = LOCK_EXLOCK_OPEN;
407 goto try_again;
408 }
409
410 LOG("lockfile created %s\n", lockfn);
411
412 skip:
413 /*
414 * write into the temporary xlock
415 */
416 if (write(fd, lockfn_xlink, strlen(lockfn_xlink)) !=
417 strlen(lockfn_xlink)) {
418 if (errno == EIO) {
419 *retstatus = LOCK_EXLOCK_WRITE;
420 status = EIO;
421 goto finish;
422 }
423 status = errno;
424 clstat = close(fd);
425 if (unlikely(clstat == -1)) {
426 LOG("fail on close\n");
427 }
428 XSLEEP;
429 *retstatus = LOCK_EXLOCK_WRITE;
430 if (unlink(lockfn) == -1) {
431 LOG("removal of %s lockfile failed, "
432 "errno=%d, trying again\n", lockfn, errno);
433 }
434 goto try_again;
435 }
436 clstat = close(fd);
437 if (unlikely(clstat == -1)) {
438 LOG("fail on close\n");
439 }
440
441 while (retry_attempts++ < RETRY_MAX) {
442 tmpstat = link(lockfn, lockfn_xlink);
443 LOG("linking %s and %s\n", lockfn, lockfn_xlink);
444 if ((tmpstat == -1) && (errno != EEXIST)) {
445 LOG("link status is %d, errno=%d\n", tmpstat, errno);
446 }
447
448 if ((lstat(lockfn, &stat1) == -1) ||
449 (lstat(lockfn_xlink, &stat2) == -1)) {
450 /* try again, cleanup first */
451 tmpstat = unlink(lockfn);
452 if (unlikely(tmpstat == -1)) {
453 LOG("error removing lock file %s", lockfn);
454 }
455 tmpstat = unlink(lockfn_xlink);
456 if (unlikely(tmpstat == -1)) {
457 LOG("error removing linked lock file %s",
458 lockfn_xlink);
459 }
460 XSLEEP;
461 status = LOCK_ESTAT;
462 goto finish;
463 }
464
465 /* compare inodes */
466 if (stat1.st_ino == stat2.st_ino) {
467 /* success, inodes are the same */
468 /* should we check that st_nlink's are also 2?? */
469 *retstatus = LOCK_OK;
470 status = 0;
471 tmpstat = unlink(lockfn_xlink);
472 if (unlikely(tmpstat == -1)) {
473 LOG("error removing linked lock file %s",
474 lockfn_xlink);
475 }
476 goto finish;
477 } else {
478 status = errno;
479 /* try again, cleanup first */
480 tmpstat = unlink(lockfn);
481 if (unlikely(tmpstat == -1)) {
482 LOG("error removing lock file %s", lockfn);
483 }
484 tmpstat = unlink(lockfn_xlink);
485 if (unlikely(tmpstat == -1)) {
486 LOG("error removing linked lock file %s",
487 lockfn_xlink);
488 }
489 XSLEEP;
490 *retstatus = LOCK_EINODE;
491 goto try_again;
492 }
493 }
494
495 finish:
496 if (!*retstatus) {
497
498 /* we have exclusive lock */
499
500 status = 0;
501
502 /* fast check, see if we own a final lock and are reasserting */
503 if (!lstat(lockfn_flink, &stat1)) {
504 char *ptr;
505
506 /* set the return value to notice this is a reassert */
507 *retstatus = 1;
508
509 /* read existing lock file and extract
510 established lease time */
511 fd = open(lockfn_flink, O_RDONLY, 0644);
512 memset(tmpbuf, 0, sizeof(tmpbuf));
513 if (read(fd, tmpbuf, sizeof(tmpbuf)) < 0) {
514 if (errno == EIO) {
515 close(fd);
516 *retstatus = LOCK_EINODE;
517 status = EIO;
518 goto skip_scan;
519 }
520 }
521 close(fd);
522 ptr = strrchr(tmpbuf, '.');
523 if (ptr) {
524 *lease_time = atoi(ptr+1);
525 } else {
526 *lease_time = 10; /* wkchack */
527 }
528 goto skip_scan;
529 } else {
530 if (errno == EIO) {
531 *retstatus = LOCK_EINODE;
532 status = EIO;
533 goto skip_scan;
534 }
535 }
536
537 /* we allow exclusive writer, or multiple readers */
538 if (lock_holder(fn_to_lock, lockfn, lockfn_flink, force,
539 readonly, &stealw, writer_eval,
540 &established_lease_time, &ioerr)) {
541 if (ioerr) {
542 *retstatus = LOCK_EREAD;
543 status = ioerr;
544 goto skip_scan;
545 }
546 *retstatus = LOCK_EHELD_WR;
547 } else if (lock_holder(fn_to_lock, lockfn, lockfn_flink, force,
548 readonly, &stealr, reader_eval,
549 &established_lease_time, &ioerr)) {
550 if (ioerr) {
551 *retstatus = LOCK_EREAD;
552 status = ioerr;
553 goto skip_scan;
554 }
555 *retstatus = LOCK_EHELD_RD;
556 }
557 if (established_lease_time) *lease_time =
558 established_lease_time;
559 }
560
561 skip_scan:
562 if (*retstatus >= 0) {
563 /* update file, changes last modify time */
564 fd = open(lockfn_flink, O_WRONLY | O_CREAT, 0644);
565 if (fd == -1) {
566 *retstatus = LOCK_EOPEN;
567 status = errno;
568 } else {
569 char tmpbuf[32];
570 int failed_write;
571 memset(tmpbuf, 0, sizeof(tmpbuf));
572 sprintf(tmpbuf, ".%d", *lease_time);
573 failed_write = write(fd, lockfn_flink,
574 strlen(lockfn_flink)) !=
575 strlen(lockfn_flink);
576 if (failed_write) status = errno;
577 failed_write |= write(fd, tmpbuf, strlen(tmpbuf)) !=
578 strlen(tmpbuf);
579 if (failed_write) status = errno;
580 if (failed_write) {
581 clstat = close(fd);
582 if (unlikely(clstat == -1)) {
583 LOG("fail on close\n");
584 }
585 XSLEEP;
586 *retstatus = LOCK_EUPDATE;
587 goto try_again;
588 }
589 }
590 clstat = close(fd);
591 if (unlikely(clstat == -1)) {
592 LOG("fail on close\n");
593 }
594 }
595
596 if (!*retstatus && force && (stealx || stealw || stealr)) {
597 struct timeval timeout;
598
599 /* enforce quiet time on steal */
600 timeout.tv_sec = *lease_time;
601 timeout.tv_usec = 0;
602 select(0, 0, 0, 0, &timeout);
603 }
604
605 /* remove exclusive lock, final read/write locks will hold */
606 tmpstat = unlink(lockfn);
607 if (unlikely(tmpstat == -1)) {
608 LOG("error removing exclusive lock file %s",
609 lockfn);
610 }
611
612 free(lockfn);
613 free(lockfn_xlink);
614 free(lockfn_flink);
615
616 /* set lease time to -1 if error, so no one is apt to use it */
617 if (*retstatus < 0) *lease_time = -1;
618
619 LOG("returning status %d, errno=%d\n", status, errno);
620 return status;
621 }
622
623
unlock(char * fn_to_unlock,char * uuid,int readonly,int * status)624 int unlock(char *fn_to_unlock, char *uuid, int readonly, int *status)
625 {
626 char *lockfn_link = 0;
627 int reterrno = 0;
628
629 if (!fn_to_unlock || !uuid) {
630 *status = LOCK_EBADPARM;
631 return 0;
632 }
633
634 lockfn_link = create_lockfn_link(fn_to_unlock, LFFL_FORMAT, uuid,
635 readonly);
636 if (unlikely(!lockfn_link)) { *status = LOCK_ENOMEM; goto finish; }
637
638 if (unlink(lockfn_link) == -1) {
639 LOG("error removing linked lock file %s", lockfn_link);
640 reterrno = errno;
641 *status = LOCK_ENOLOCK;
642 goto finish;
643 }
644
645 *status = LOCK_OK;
646
647 finish:
648 free(lockfn_link);
649 return reterrno;
650 }
651
lock_delta(char * fn,int * ret_lease,int * max_lease)652 int lock_delta(char *fn, int *ret_lease, int *max_lease)
653 {
654 int reterrno = 0;
655 DIR *pd = 0;
656 struct dirent *dptr;
657 char *ptr;
658 int result = INT_MAX;
659 struct stat statbuf, statnow;
660 char *dirname = malloc(strlen(fn));
661 char *uname = malloc(strlen(fn) + 8);
662 int elt_established = 0;
663 char *dotptr;
664 char tmpbuf[4096];
665 int fd;
666
667 if (!fn || !dirname || !uname) {
668 *ret_lease = LOCK_EBADPARM;
669 *max_lease = -1;
670 return 0;
671 }
672
673 if (NFSnormalizedStatTime(fn, &statnow, &reterrno)) {
674 result = LOCK_ESTAT;
675 goto finish;
676 }
677
678 /* get directory */
679 ptr = strrchr(fn, '/');
680 if (!ptr) {
681 strcpy(dirname, ".");
682 ptr = fn;
683 } else {
684 int numbytes = ptr - fn;
685 strncpy(dirname, fn, numbytes);
686 ptr += 1;
687 }
688 pd = opendir(dirname);
689 if (!pd) { reterrno = errno; goto finish; }
690
691 dptr = readdir(pd);
692 while (dptr) {
693 if (strcmp(dptr->d_name, ptr) &&
694 !strncmp(dptr->d_name, ptr, strlen(ptr))) {
695 char *fpath = malloc(strlen(dptr->d_name) +
696 strlen(dirname) + 2);
697 if (!fpath) {
698 closedir(pd);
699 result = LOCK_ENOMEM;
700 goto finish;
701 }
702 strcpy(fpath, dirname);
703 strcat(fpath, "/");
704 strcat(fpath, dptr->d_name);
705 if (lstat(fpath, &statbuf) != -1) {
706 int diff = (int)statnow.st_mtime -
707 (int)statbuf.st_mtime;
708 /* adjust diff if someone updated the lock
709 between now and when we created the "now"
710 file
711 */
712 diff = (diff < 0) ? 0 : diff;
713 result = diff < result ? diff : result;
714 } else {
715 closedir(pd);
716 reterrno = errno;
717 goto finish;
718 }
719
720 if (!elt_established) {
721 /* read final lock file and extract lease time */
722 fd = open(fpath, O_RDONLY, 0644);
723 memset(tmpbuf, 0, sizeof(tmpbuf));
724 if (read(fd, tmpbuf, sizeof(tmpbuf)) < 0) {
725 /* error on read? */
726 }
727 close(fd);
728 dotptr = strrchr(tmpbuf, '.');
729 if (dotptr) {
730 *max_lease = atoi(dotptr+1);
731 elt_established = 1;
732 }
733 }
734
735 free(fpath);
736 }
737 dptr = readdir(pd);
738 }
739
740 closedir(pd);
741
742 finish:
743 free(dirname);
744 free(uname);
745
746 /* returns smallest lock time, or error */
747 if (result == INT_MAX) result = LOCK_ENOLOCK;
748
749 /* set lease time to -1 if error, so no one is apt to use it */
750 if ((result < 0) || reterrno) *max_lease = -1;
751 *ret_lease = result;
752 return reterrno;
753 }
754
755 #if defined(TEST)
756 /*
757 * the following is for sanity testing.
758 */
759
usage(char * prg)760 static void usage(char *prg)
761 {
762 printf("usage %s\n"
763 " dtr <filename>]\n"
764 " p <filename> [num iterations]\n"
765 " u <filename> [0|1] [<uniqid>]\n"
766 " l <filename> [0|1] [0|1] [<uniqid>] [<leasetime>]\n", prg);
767 printf(" p : perf test lock take and reassert\n");
768 printf(" d : delta lock time\n");
769 printf(" t : test the file (after random locks)\n");
770 printf(" r : random lock tests (must ^C)\n");
771 printf(" u : unlock, readonly? uniqID (default is PID)\n");
772 printf(" l : lock, readonly? force?, uniqID (default is PID), lease time\n");
773 }
774
test_file(char * fn)775 static void test_file(char *fn)
776 {
777 FILE *fptr;
778 int prev_count = 0;
779 int count, pid, time;
780
781 fptr = fopen(fn, "r");
782 if (!fptr) {
783 LOG("ERROR on file %s open, errno=%d\n", fn, errno);
784 return;
785 }
786
787 while (!feof(fptr)) {
788 fscanf(fptr, "%d %d %d\n", &count, &pid, &time);
789 if (prev_count != count) {
790 LOG("ERROR: prev_count=%d, count=%d, pid=%d, time=%d\n",
791 prev_count, count, pid, time);
792 }
793 prev_count = count + 1;
794 }
795 }
796
random_locks(char * fn)797 static void random_locks(char *fn)
798 {
799 int pid = getpid();
800 int status;
801 char *filebuf = malloc(256);
802 int count = 0;
803 int dummy;
804 int clstat;
805 char uuid[12];
806 int readonly;
807 int lease = DEFAULT_LEASE_TIME_SECS;
808 int err;
809
810 /* this will never return, kill to exit */
811
812 srandom((int)time(0) ^ pid);
813
814 LOG("pid: %d using file %s\n", pid, fn);
815 sprintf(uuid, "%08d", pid);
816
817 while (1) {
818 XSLEEP;
819 readonly = random() & 1;
820 sysstatus = lock(fn, uuid, 0, readonly, &lease, status);
821 if (status == LOCK_OK) {
822 /* got lock, open, read, modify write close file */
823 int fd = open(fn, O_RDWR, 0644);
824 if (fd == -1) {
825 LOG("pid: %d ERROR on file %s open, errno=%d\n",
826 pid, fn, errno);
827 } else {
828 if (!readonly) {
829 /* ugly code to read data in test format */
830 /* format is "%d %d %d" 'count pid time' */
831 struct stat statbuf;
832 int bytes;
833 status = stat(fn, &statbuf);
834 if (status != -1) {
835 if (statbuf.st_size > 256) {
836 lseek(fd, -256, SEEK_END);
837 }
838 memset(filebuf, 0, 256);
839 bytes = read(fd, filebuf, 256);
840 if (bytes) {
841 int bw = bytes-2;
842 while (bw && filebuf[bw]!='\n')
843 bw--;
844 if (!bw) bw = -1;
845 sscanf(&filebuf[bw+1],
846 "%d %d %d",
847 &count, &dummy, &dummy);
848 count += 1;
849 }
850 lseek(fd, 0, SEEK_END);
851 sprintf(filebuf, "%d %d %d\n",
852 count, pid, (int)time(0));
853 write(fd, filebuf, strlen(filebuf));
854 } else {
855 LOG("pid: %d ERROR on file %s stat, "
856 "errno=%d\n", pid, fn, errno);
857 }
858 }
859 clstat = close(fd);
860 if (unlikely(clstat == -1)) {
861 LOG("fail on close\n");
862 }
863 }
864 XSLEEP;
865 err = unlock(fn, uuid, readonly, &status);
866 LOG("unlock status is %d (err=%d)\n", status, err);
867 }
868 }
869 }
870
perf_lock(char * fn,int loops)871 static void perf_lock(char *fn, int loops)
872 {
873 int sysstatus;
874 char buf[9];
875 int start = loops;
876 int lease = DEFAULT_LEASE_TIME_SECS;
877
878 sprintf(buf, "%08d", getpid());
879
880 while (loops--) {
881 sysstatus = lock(fn, buf, 0, 0, &lease, &status);
882 if (status < 0) {
883 printf("failed to get lock at iteration %d errno=%d\n",
884 start - loops, errno);
885 return;
886 }
887 }
888 unlock(fn, buf, 0, &status);
889 }
890
main(int argc,char * argv[])891 int main(int argc, char *argv[])
892 {
893 int status;
894 char *ptr;
895 char uuid[12];
896 int force;
897 int readonly;
898 int max_lease, cur_lease;
899 int intstatus;
900 int lease = DEFAULT_LEASE_TIME_SECS;
901
902 if (argc < 3) {
903 usage(argv[0]);
904 return 0;
905 }
906
907 sprintf(uuid, "%08d", getpid());
908 ptr = uuid;
909
910 if (!strcmp(argv[1],"d")) {
911 status = lock_delta(argv[2], &cur_lease, &max_lease);
912
913 printf("lock delta for %s is %d seconds, max lease is %d\n",
914 argv[2], cur_lease, max_lease);
915 } else if (!strcmp(argv[1],"t")) {
916 test_file(argv[2]);
917 } else if (!strcmp(argv[1],"r")) {
918 random_locks(argv[2]);
919 } else if (!strcmp(argv[1],"p")) {
920 perf_lock(argv[2], argc < 3 ? 100000 : atoi(argv[3]));
921 } else if (!strcmp(argv[1],"l")) {
922 if (argc < 4) force = 0; else force = atoi(argv[3]);
923 if (argc < 5) readonly = 0; else readonly = atoi(argv[4]);
924 if (argc >= 6) ptr = argv[5];
925 if (argc == 7) lease = atoi(argv[6]);
926 status = lock(argv[2], ptr, readonly, force, &lease, &intstatus);
927 printf("lock status = %d\n", status);
928 } else if (!strcmp(argv[1],"u") ) {
929 if (argc < 5) readonly = 0; else readonly = atoi(argv[3]);
930 if (argc == 5) ptr = argv[4];
931 status = unlock(argv[2], ptr, readonly, &intstatus);
932 printf("unlock status = %d\n", intstatus);
933 } else {
934 usage(argv[0]);
935 }
936
937 return status;
938 }
939 #elif defined(UTIL)
940 /*
941 * the following is used for non-libary, standalone
942 * program utility as a shell program
943 */
944
usage(char * prg)945 static void usage(char *prg)
946 {
947 printf("usage %s\n"
948 " delta <filename>\n"
949 " unlock <filename> <r|w> <uniqid>\n"
950 " lock <filename> <r|w> <0|1> <uniqid> <leasetime>\n", prg);
951 printf(" delta : get time since lock last refreshed\n");
952 printf(" returns delta time and max lease time in seconds\n");
953 printf(" unlock: unlock request filename, r|w, uniqID\n");
954 printf(" returns status (success is 0)\n");
955 printf(" lock : lock request filename, r|w, force?, uniqID, lease time request\n");
956 printf(" returns status (success is 0) and established lease time in seconds\n");
957 }
958
main(int argc,char * argv[])959 int main(int argc, char *argv[])
960 {
961 int status = 0;
962 int dlock;
963 char *ptr;
964 int force;
965 int readonly;
966 int cur_lease, max_lease, intstatus;
967 int lease = DEFAULT_LEASE_TIME_SECS;
968
969 if (argc < 3) {
970 if (argc == 2 && !strcmp(argv[1], "-h")) {
971 usage(argv[0]);
972 } else {
973 printf("%d\n", LOCK_EUSAGE);
974 }
975 return 0;
976 }
977
978 if (!strcmp(argv[1],"delta") && (argc == 3)) {
979 status = lock_delta(argv[2], &cur_lease, &max_lease);
980 printf("%d %d\n", cur_lease, max_lease);
981 } else if (!strcmp(argv[1],"lock") && (argc == 7)) {
982 readonly = (strcmp(argv[3], "r") == 0) ? 1 : 0;
983 force = atoi(argv[4]);
984 ptr = argv[5];
985 lease = atoi(argv[6]);
986 status = lock(argv[2], ptr, force, readonly, &lease, &intstatus);
987 printf("%d %d\n", intstatus, lease);
988 } else if (!strcmp(argv[1],"unlock") && (argc == 5)) {
989 readonly = (strcmp(argv[3], "r") == 0) ? 1 : 0;
990 ptr = argv[4];
991 status = unlock(argv[2], ptr, readonly, &intstatus);
992 printf("%d\n", intstatus);
993 } else {
994 printf("%d\n", LOCK_EUSAGE);
995 }
996
997 /* this is either 0 or a system defined errno */
998 return status;
999 }
1000 #endif
1001