]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libmount/src/lock.c
libmount: fix memory overflow [AddressSanitizer]
[thirdparty/util-linux.git] / libmount / src / lock.c
CommitLineData
27c6d415
KZ
1/*
2 * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
3 *
4 * This file may be redistributed under the terms of the
5 * GNU Lesser General Public License.
6 */
192c6aad
KZ
7
8/**
9 * SECTION: lock
63de90d4 10 * @title: Locking
5eb00eb4 11 * @short_description: locking methods for /etc/mtab or another libmount files
192c6aad 12 *
d58b3157 13 * The mtab lock is backwards compatible with the standard linux /etc/mtab
5eb00eb4 14 * locking. Note, it's necessary to use the same locking schema in all
d58b3157 15 * applications that access the file.
192c6aad 16 */
27c6d415
KZ
17#include <sys/time.h>
18#include <time.h>
19#include <signal.h>
20#include <fcntl.h>
21#include <limits.h>
5eb00eb4 22#include <sys/file.h>
27c6d415 23
60f25dea 24#include "strutils.h"
69f1cad4 25#include "closestream.h"
27c6d415 26#include "pathnames.h"
27c6d415
KZ
27#include "mountP.h"
28
29/*
30 * lock handler
31 */
68164f6c 32struct libmnt_lock {
27c6d415
KZ
33 char *lockfile; /* path to lock file (e.g. /etc/mtab~) */
34 char *linkfile; /* path to link file (e.g. /etc/mtab~.<id>) */
35 int lockfile_fd; /* lock file descriptor */
5976114f 36
88d7d36f
KZ
37 unsigned int locked :1, /* do we own the lock? */
38 sigblock :1, /* block signals when locked */
39 simplelock :1; /* use flock rather than normal mtab lock */
5976114f
KZ
40
41 sigset_t oldsigmask;
27c6d415
KZ
42};
43
44
45/**
46 * mnt_new_lock:
0f32f1e2 47 * @datafile: the file that should be covered by the lock
27c6d415
KZ
48 * @id: unique linkfile identifier or 0 (default is getpid())
49 *
192c6aad 50 * Returns: newly allocated lock handler or NULL on case of error.
27c6d415 51 */
68164f6c 52struct libmnt_lock *mnt_new_lock(const char *datafile, pid_t id)
27c6d415 53{
68164f6c 54 struct libmnt_lock *ml = NULL;
66c33aab 55 char *lo = NULL, *ln = NULL;
5eb00eb4 56 size_t losz;
27c6d415 57
4569bbea
KZ
58 assert(datafile);
59
5eb00eb4
KZ
60 /* for flock we use "foo.lock, for mtab "foo~"
61 */
62 losz = strlen(datafile) + sizeof(".lock");
63 lo = malloc(losz);
64 if (!lo)
66c33aab 65 goto err;
5eb00eb4
KZ
66
67 /* default is mtab~ lock */
68 snprintf(lo, losz, "%s~", datafile);
69
66c33aab
KZ
70 if (asprintf(&ln, "%s~.%d", datafile, id ? : getpid()) == -1) {
71 ln = NULL;
72 goto err;
27c6d415 73 }
68164f6c 74 ml = calloc(1, sizeof(*ml) );
66c33aab
KZ
75 if (!ml)
76 goto err;
77
78 ml->lockfile_fd = -1;
79 ml->linkfile = ln;
80 ml->lockfile = lo;
3f31a959 81
83a78332 82 DBG(LOCKS, ul_debugobj(ml, "alloc: default linkfile=%s, lockfile=%s", ln, lo));
27c6d415 83 return ml;
66c33aab
KZ
84err:
85 free(lo);
86 free(ln);
87 free(ml);
88 return NULL;
27c6d415
KZ
89}
90
66c33aab 91
27c6d415
KZ
92/**
93 * mnt_free_lock:
68164f6c 94 * @ml: struct libmnt_lock handler
27c6d415
KZ
95 *
96 * Deallocates mnt_lock.
97 */
68164f6c 98void mnt_free_lock(struct libmnt_lock *ml)
27c6d415
KZ
99{
100 if (!ml)
101 return;
83a78332 102 DBG(LOCKS, ul_debugobj(ml, "free%s", ml->locked ? " !!! LOCKED !!!" : ""));
27c6d415
KZ
103 free(ml->lockfile);
104 free(ml->linkfile);
105 free(ml);
106}
107
5976114f
KZ
108/**
109 * mnt_lock_block_signals:
110 * @ml: struct libmnt_lock handler
111 * @enable: TRUE/FALSE
112 *
113 * Block/unblock signals when the lock is locked, the signals are not blocked
114 * by default.
115 *
116 * Returns: <0 on error, 0 on success.
117 */
118int mnt_lock_block_signals(struct libmnt_lock *ml, int enable)
119{
120 if (!ml)
121 return -EINVAL;
83a78332 122 DBG(LOCKS, ul_debugobj(ml, "signals: %s", enable ? "BLOCKED" : "UNBLOCKED"));
6f5788c5 123 ml->sigblock = enable ? 1 : 0;
5976114f
KZ
124 return 0;
125}
126
5eb00eb4
KZ
127/* don't export this to API
128 */
129int mnt_lock_use_simplelock(struct libmnt_lock *ml, int enable)
130{
131 size_t sz;
132
133 if (!ml)
134 return -EINVAL;
135
136 assert(ml->lockfile);
137
83a78332 138 DBG(LOCKS, ul_debugobj(ml, "flock: %s", enable ? "ENABLED" : "DISABLED"));
5eb00eb4
KZ
139 ml->simplelock = enable ? 1 : 0;
140
141 sz = strlen(ml->lockfile);
9d670a2a
KZ
142 assert(sz);
143
144 if (sz < 1)
145 return -EINVAL;
5eb00eb4
KZ
146
147 /* Change lock name:
148 *
149 * flock: "<name>.lock"
150 * mtab lock: "<name>~"
151 */
152 if (ml->simplelock && endswith(ml->lockfile, "~"))
153 memcpy(ml->lockfile + sz - 1, ".lock", 6);
154
155 else if (!ml->simplelock && endswith(ml->lockfile, ".lock"))
156 memcpy(ml->lockfile + sz - 5, "~", 2);
157
83a78332 158 DBG(LOCKS, ul_debugobj(ml, "new lock filename: '%s'", ml->lockfile));
5eb00eb4
KZ
159 return 0;
160}
161
0f32f1e2
KZ
162/*
163 * Returns path to lockfile.
27c6d415 164 */
68164f6c 165static const char *mnt_lock_get_lockfile(struct libmnt_lock *ml)
27c6d415 166{
66c33aab 167 return ml ? ml->lockfile : NULL;
27c6d415
KZ
168}
169
0f32f1e2 170/*
66c33aab
KZ
171 * Note that the filename is generated by mnt_new_lock() and depends on
172 * getpid() or 'id' argument of the mnt_new_lock() function.
173 *
174 * Returns: unique (per process/thread) path to linkfile.
27c6d415 175 */
68164f6c 176static const char *mnt_lock_get_linkfile(struct libmnt_lock *ml)
27c6d415 177{
66c33aab 178 return ml ? ml->linkfile : NULL;
27c6d415
KZ
179}
180
5eb00eb4
KZ
181/*
182 * Simple flocking
183 */
184static void unlock_simplelock(struct libmnt_lock *ml)
185{
186 assert(ml);
187 assert(ml->simplelock);
188
189 if (ml->lockfile_fd >= 0) {
83a78332 190 DBG(LOCKS, ul_debugobj(ml, "%s: unflocking",
5eb00eb4
KZ
191 mnt_lock_get_lockfile(ml)));
192 close(ml->lockfile_fd);
193 }
194}
195
196static int lock_simplelock(struct libmnt_lock *ml)
197{
198 const char *lfile;
199 int rc;
200
201 assert(ml);
202 assert(ml->simplelock);
203
204 lfile = mnt_lock_get_lockfile(ml);
205
83a78332 206 DBG(LOCKS, ul_debugobj(ml, "%s: locking", lfile));
5eb00eb4
KZ
207
208 if (ml->sigblock) {
209 sigset_t sigs;
210 sigemptyset(&ml->oldsigmask);
211 sigfillset(&sigs);
212 sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
213 }
214
215 ml->lockfile_fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC,
216 S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
217 if (ml->lockfile_fd < 0) {
218 rc = -errno;
219 goto err;
220 }
221
222 while (flock(ml->lockfile_fd, LOCK_EX) < 0) {
223 int errsv;
224 if ((errno == EAGAIN) || (errno == EINTR))
225 continue;
226 errsv = errno;
227 close(ml->lockfile_fd);
228 ml->lockfile_fd = -1;
229 rc = -errsv;
230 goto err;
231 }
86cd5870 232 ml->locked = 1;
5eb00eb4
KZ
233 return 0;
234err:
235 if (ml->sigblock)
236 sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
237 return rc;
238}
239
240/*
241 * traditional mtab locking
242 */
243
7fc6d2b8 244static void mnt_lockalrm_handler(int sig __attribute__((__unused__)))
27c6d415
KZ
245{
246 /* do nothing, say nothing, be nothing */
247}
248
249/*
250 * Waits for F_SETLKW, unfortunately we have to use SIGALRM here to interrupt
d58b3157 251 * fcntl() to avoid neverending waiting.
27c6d415 252 *
192c6aad 253 * Returns: 0 on success, 1 on timeout, -errno on error.
27c6d415 254 */
5eb00eb4 255static int mnt_wait_mtab_lock(struct libmnt_lock *ml, struct flock *fl, time_t maxtime)
27c6d415
KZ
256{
257 struct timeval now;
258 struct sigaction sa, osa;
259 int ret = 0;
260
261 gettimeofday(&now, NULL);
262
263 if (now.tv_sec >= maxtime)
264 return 1; /* timeout */
265
266 /* setup ALARM handler -- we don't want to wait forever */
267 sa.sa_flags = 0;
268 sa.sa_handler = mnt_lockalrm_handler;
269 sigfillset (&sa.sa_mask);
270
271 sigaction(SIGALRM, &sa, &osa);
272
83a78332 273 DBG(LOCKS, ul_debugobj(ml, "(%d) waiting for F_SETLKW", getpid()));
27c6d415
KZ
274
275 alarm(maxtime - now.tv_sec);
276 if (fcntl(ml->lockfile_fd, F_SETLKW, fl) == -1)
277 ret = errno == EINTR ? 1 : -errno;
278 alarm(0);
279
280 /* restore old sigaction */
281 sigaction(SIGALRM, &osa, NULL);
282
83a78332 283 DBG(LOCKS, ul_debugobj(ml, "(%d) leaving mnt_wait_setlkw(), rc=%d",
3f31a959 284 getpid(), ret));
27c6d415
KZ
285 return ret;
286}
287
288/*
5eb00eb4 289 * Create the mtab lock file.
27c6d415
KZ
290 *
291 * The old code here used flock on a lock file /etc/mtab~ and deleted
292 * this lock file afterwards. However, as rgooch remarks, that has a
293 * race: a second mount may be waiting on the lock and proceed as
294 * soon as the lock file is deleted by the first mount, and immediately
295 * afterwards a third mount comes, creates a new /etc/mtab~, applies
296 * flock to that, and also proceeds, so that the second and third mount
d58b3157 297 * are now both scribbling in /etc/mtab.
27c6d415
KZ
298 *
299 * The new code uses a link() instead of a creat(), where we proceed
300 * only if it was us that created the lock, and hence we always have
301 * to delete the lock afterwards. Now the use of flock() is in principle
302 * superfluous, but avoids an arbitrary sleep().
303 *
304 * Where does the link point to? Obvious choices are mtab and mtab~~.
305 * HJLu points out that the latter leads to races. Right now we use
ee314075 306 * mtab~.pid instead.
27c6d415
KZ
307 *
308 *
309 * The original mount locking code has used sleep(1) between attempts and
310 * maximal number of attempts has been 5.
311 *
d58b3157 312 * There was a very small number of attempts and extremely long waiting (1s)
27c6d415
KZ
313 * that is useless on machines with large number of mount processes.
314 *
d58b3157
OO
315 * Now we wait for a few thousand microseconds between attempts and we have a global
316 * time limit (30s) rather than a limit for the number of attempts. The advantage
27c6d415 317 * is that this method also counts time which we spend in fcntl(F_SETLKW) and
d58b3157 318 * the number of attempts is not restricted.
27c6d415
KZ
319 * -- kzak@redhat.com [Mar-2007]
320 *
321 *
322 * This mtab locking code has been refactored and moved to libmount. The mtab
323 * locking is really not perfect (e.g. SIGALRM), but it's stable, reliable and
66c33aab
KZ
324 * backwardly compatible code.
325 *
326 * Don't forget that this code has to be compatible with 3rd party mounts
ee314075 327 * (/sbin/mount.foo) and has to work with NFS.
27c6d415
KZ
328 * -- kzak@redhat.com [May-2009]
329 */
330
d58b3157 331/* maximum seconds between the first and the last attempt */
27c6d415
KZ
332#define MOUNTLOCK_MAXTIME 30
333
334/* sleep time (in microseconds, max=999999) between attempts */
335#define MOUNTLOCK_WAITTIME 5000
336
5eb00eb4 337static void unlock_mtab(struct libmnt_lock *ml)
27c6d415
KZ
338{
339 if (!ml)
340 return;
341
6f5788c5 342 if (!ml->locked && ml->lockfile && ml->linkfile)
27c6d415 343 {
d58b3157 344 /* We (probably) have all the files, but we don't own the lock,
27c6d415 345 * Really? Check it! Maybe ml->locked wasn't set properly
d58b3157 346 * because the code was interrupted by a signal. Paranoia? Yes.
27c6d415
KZ
347 *
348 * We own the lock when linkfile == lockfile.
349 */
350 struct stat lo, li;
351
352 if (!stat(ml->lockfile, &lo) && !stat(ml->linkfile, &li) &&
353 lo.st_dev == li.st_dev && lo.st_ino == li.st_ino)
354 ml->locked = 1;
355 }
3a5b1b1d 356
27c6d415
KZ
357 if (ml->linkfile)
358 unlink(ml->linkfile);
359 if (ml->lockfile_fd >= 0)
360 close(ml->lockfile_fd);
6f5788c5 361 if (ml->locked && ml->lockfile) {
27c6d415 362 unlink(ml->lockfile);
83a78332 363 DBG(LOCKS, ul_debugobj(ml, "unlink %s", ml->lockfile));
6f5788c5 364 }
27c6d415
KZ
365}
366
5eb00eb4 367static int lock_mtab(struct libmnt_lock *ml)
27c6d415 368{
03a8029b 369 int i, rc = -1;
27c6d415
KZ
370 struct timespec waittime;
371 struct timeval maxtime;
372 const char *lockfile, *linkfile;
373
374 if (!ml)
03a8029b 375 return -EINVAL;
27c6d415
KZ
376 if (ml->locked)
377 return 0;
378
379 lockfile = mnt_lock_get_lockfile(ml);
380 if (!lockfile)
03a8029b 381 return -EINVAL;
27c6d415
KZ
382 linkfile = mnt_lock_get_linkfile(ml);
383 if (!linkfile)
03a8029b 384 return -EINVAL;
27c6d415 385
5976114f
KZ
386 if (ml->sigblock) {
387 /*
388 * Block all signals when locked, mnt_unlock_file() will
389 * restore the old mask.
390 */
391 sigset_t sigs;
392
393 sigemptyset(&ml->oldsigmask);
394 sigfillset(&sigs);
395 sigdelset(&sigs, SIGTRAP);
396 sigdelset(&sigs, SIGALRM);
397 sigprocmask(SIG_BLOCK, &sigs, &ml->oldsigmask);
398 }
399
d39c2b43 400 i = open(linkfile, O_WRONLY|O_CREAT|O_CLOEXEC, S_IRUSR|S_IWUSR);
03a8029b
KZ
401 if (i < 0) {
402 /* linkfile does not exist (as a file) and we cannot create it.
403 * Read-only or full filesystem? Too many files open in the system?
404 */
405 if (errno > 0)
406 rc = -errno;
27c6d415 407 goto failed;
03a8029b 408 }
27c6d415
KZ
409 close(i);
410
411 gettimeofday(&maxtime, NULL);
412 maxtime.tv_sec += MOUNTLOCK_MAXTIME;
413
414 waittime.tv_sec = 0;
415 waittime.tv_nsec = (1000 * MOUNTLOCK_WAITTIME);
416
417 /* Repeat until it was us who made the link */
6f5788c5 418 while (!ml->locked) {
27c6d415
KZ
419 struct timeval now;
420 struct flock flock;
421 int j;
422
423 j = link(linkfile, lockfile);
424 if (j == 0)
425 ml->locked = 1;
426
03a8029b
KZ
427 if (j < 0 && errno != EEXIST) {
428 if (errno > 0)
429 rc = -errno;
27c6d415 430 goto failed;
03a8029b 431 }
d39c2b43 432 ml->lockfile_fd = open(lockfile, O_WRONLY|O_CLOEXEC);
27c6d415
KZ
433
434 if (ml->lockfile_fd < 0) {
435 /* Strange... Maybe the file was just deleted? */
436 int errsv = errno;
437 gettimeofday(&now, NULL);
438 if (errsv == ENOENT && now.tv_sec < maxtime.tv_sec) {
439 ml->locked = 0;
440 continue;
441 }
03a8029b
KZ
442 if (errsv > 0)
443 rc = -errsv;
27c6d415
KZ
444 goto failed;
445 }
446
447 flock.l_type = F_WRLCK;
448 flock.l_whence = SEEK_SET;
449 flock.l_start = 0;
450 flock.l_len = 0;
451
452 if (ml->locked) {
453 /* We made the link. Now claim the lock. */
454 if (fcntl (ml->lockfile_fd, F_SETLK, &flock) == -1) {
83a78332 455 DBG(LOCKS, ul_debugobj(ml,
27c6d415
KZ
456 "%s: can't F_SETLK lockfile, errno=%d\n",
457 lockfile, errno));
458 /* proceed, since it was us who created the lockfile anyway */
459 }
460 break;
461 } else {
462 /* Someone else made the link. Wait. */
5eb00eb4 463 int err = mnt_wait_mtab_lock(ml, &flock, maxtime.tv_sec);
27c6d415
KZ
464
465 if (err == 1) {
83a78332 466 DBG(LOCKS, ul_debugobj(ml,
27c6d415
KZ
467 "%s: can't create link: time out (perhaps "
468 "there is a stale lock file?)", lockfile));
03a8029b 469 rc = -ETIMEDOUT;
27c6d415
KZ
470 goto failed;
471
03a8029b
KZ
472 } else if (err < 0) {
473 rc = err;
27c6d415 474 goto failed;
03a8029b 475 }
27c6d415
KZ
476 nanosleep(&waittime, NULL);
477 close(ml->lockfile_fd);
478 ml->lockfile_fd = -1;
479 }
480 }
83a78332 481 DBG(LOCKS, ul_debugobj(ml, "%s: (%d) successfully locked",
6f5788c5 482 lockfile, getpid()));
27c6d415
KZ
483 unlink(linkfile);
484 return 0;
485
486failed:
487 mnt_unlock_file(ml);
03a8029b 488 return rc;
27c6d415
KZ
489}
490
5eb00eb4
KZ
491
492/**
493 * mnt_lock_file
494 * @ml: pointer to struct libmnt_lock instance
495 *
d58b3157 496 * Creates a lock file (e.g. /etc/mtab~). Note that this function may
5eb00eb4
KZ
497 * use alarm().
498 *
d58b3157 499 * Your application always has to call mnt_unlock_file() before exit.
5eb00eb4
KZ
500 *
501 * Traditional mtab locking scheme:
502 *
503 * 1. create linkfile (e.g. /etc/mtab~.$PID)
504 * 2. link linkfile --> lockfile (e.g. /etc/mtab~.$PID --> /etc/mtab~)
505 * 3. a) link() success: setups F_SETLK lock (see fcnlt(2))
506 * b) link() failed: wait (max 30s) on F_SETLKW lock, goto 2.
507 *
508 * Returns: 0 on success or negative number in case of error (-ETIMEOUT is case
509 * of stale lock file).
510 */
511int mnt_lock_file(struct libmnt_lock *ml)
512{
513 if (!ml)
514 return -EINVAL;
515
516 if (ml->simplelock)
517 return lock_simplelock(ml);
518
519 return lock_mtab(ml);
520}
521
522/**
523 * mnt_unlock_file:
524 * @ml: lock struct
525 *
d58b3157 526 * Unlocks the file. The function could be called independently of the
5eb00eb4
KZ
527 * lock status (for example from exit(3)).
528 */
529void mnt_unlock_file(struct libmnt_lock *ml)
530{
531 if (!ml)
532 return;
533
83a78332 534 DBG(LOCKS, ul_debugobj(ml, "(%d) %s", getpid(),
5eb00eb4
KZ
535 ml->locked ? "unlocking" : "cleaning"));
536
537 if (ml->simplelock)
538 unlock_simplelock(ml);
539 else
540 unlock_mtab(ml);
541
542 ml->locked = 0;
543 ml->lockfile_fd = -1;
544
545 if (ml->sigblock) {
83a78332 546 DBG(LOCKS, ul_debugobj(ml, "restoring sigmask"));
5eb00eb4
KZ
547 sigprocmask(SIG_SETMASK, &ml->oldsigmask, NULL);
548 }
549}
550
27c6d415 551#ifdef TEST_PROGRAM
27c6d415 552
68164f6c 553struct libmnt_lock *lock;
27c6d415
KZ
554
555/*
556 * read number from @filename, increment the number and
557 * write the number back to the file
558 */
559void increment_data(const char *filename, int verbose, int loopno)
560{
561 long num;
562 FILE *f;
563 char buf[256];
564
1eb8539d 565 if (!(f = fopen(filename, "r" UL_CLOEXECSTR)))
27c6d415
KZ
566 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
567
568 if (!fgets(buf, sizeof(buf), f))
569 err(EXIT_FAILURE, "%d failed read: %s", getpid(), filename);
570
571 fclose(f);
572 num = atol(buf) + 1;
573
1eb8539d 574 if (!(f = fopen(filename, "w" UL_CLOEXECSTR)))
27c6d415
KZ
575 err(EXIT_FAILURE, "%d: failed to open: %s", getpid(), filename);
576
577 fprintf(f, "%ld", num);
69f1cad4
SK
578
579 if (close_stream(f) != 0)
580 err(EXIT_FAILURE, "write failed: %s", filename);
27c6d415
KZ
581
582 if (verbose)
583 fprintf(stderr, "%d: %s: %ld --> %ld (loop=%d)\n", getpid(),
584 filename, num - 1, num, loopno);
585}
586
587void clean_lock(void)
588{
27c6d415
KZ
589 if (!lock)
590 return;
591 mnt_unlock_file(lock);
592 mnt_free_lock(lock);
593}
594
61d97775 595void __attribute__((__noreturn__)) sig_handler(int sig)
27c6d415
KZ
596{
597 errx(EXIT_FAILURE, "\n%d: catch signal: %s\n", getpid(), strsignal(sig));
598}
599
68164f6c 600int test_lock(struct libmnt_test *ts, int argc, char *argv[])
27c6d415 601{
9e10ad3d
KZ
602 time_t synctime = 0;
603 unsigned int usecs;
604 struct timeval tv;
605 const char *datafile = NULL;
606 int verbose = 0, loops = 0, l, idx = 1;
27c6d415 607
66c33aab 608 if (argc < 3)
9e10ad3d 609 return -EINVAL;
27c6d415 610
9e10ad3d
KZ
611 if (strcmp(argv[idx], "--synctime") == 0) {
612 synctime = (time_t) atol(argv[idx + 1]);
613 idx += 2;
614 }
615 if (idx < argc && strcmp(argv[idx], "--verbose") == 0) {
27c6d415 616 verbose = 1;
9e10ad3d
KZ
617 idx++;
618 }
619
620 if (idx < argc)
621 datafile = argv[idx++];
622 if (idx < argc)
623 loops = atoi(argv[idx++]);
624
625 if (!datafile || !loops)
626 return -EINVAL;
627
3a5b1b1d
KZ
628 if (verbose)
629 fprintf(stderr, "%d: start: synctime=%u, datafile=%s, loops=%d\n",
630 getpid(), (int) synctime, datafile, loops);
27c6d415
KZ
631
632 atexit(clean_lock);
633
634 /* be paranoid and call exit() (=clean_lock()) for all signals */
635 {
636 int sig = 0;
637 struct sigaction sa;
638
639 sa.sa_handler = sig_handler;
640 sa.sa_flags = 0;
641 sigfillset(&sa.sa_mask);
642
643 while (sigismember(&sa.sa_mask, ++sig) != -1 && sig != SIGCHLD)
644 sigaction (sig, &sa, (struct sigaction *) 0);
645 }
646
9e10ad3d
KZ
647 /* start the test in exactly defined time */
648 if (synctime) {
649 gettimeofday(&tv, NULL);
650 if (synctime && synctime - tv.tv_sec > 1) {
651 usecs = ((synctime - tv.tv_sec) * 1000000UL) -
652 (1000000UL - tv.tv_usec);
a5bd7939 653 xusleep(usecs);
9e10ad3d
KZ
654 }
655 }
656
27c6d415 657 for (l = 0; l < loops; l++) {
66c33aab
KZ
658 lock = mnt_new_lock(datafile, 0);
659 if (!lock)
660 return -1;
27c6d415 661
abc3d154 662 if (mnt_lock_file(lock) != 0) {
66c33aab
KZ
663 fprintf(stderr, "%d: failed to lock %s file\n",
664 getpid(), datafile);
27c6d415
KZ
665 return -1;
666 }
667
668 increment_data(datafile, verbose, l);
669
670 mnt_unlock_file(lock);
671 mnt_free_lock(lock);
672 lock = NULL;
9e10ad3d 673
d58b3157 674 /* The mount command usually finishes after a mtab update. We
9e10ad3d
KZ
675 * simulate this via short sleep -- it's also enough to make
676 * concurrent processes happy.
677 */
678 if (synctime)
a5bd7939 679 xusleep(25000);
27c6d415
KZ
680 }
681
682 return 0;
683}
684
192c6aad
KZ
685/*
686 * Note that this test should be executed from a script that creates many
687 * parallel processes, otherwise this test does not make sense.
688 */
27c6d415
KZ
689int main(int argc, char *argv[])
690{
68164f6c 691 struct libmnt_test tss[] = {
9e10ad3d
KZ
692 { "--lock", test_lock, " [--synctime <time_t>] [--verbose] <datafile> <loops> "
693 "increment a number in datafile" },
27c6d415
KZ
694 { NULL }
695 };
696
697 return mnt_run_test(tss, argc, argv);
698}
699
700#endif /* TEST_PROGRAM */