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