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