Skip to content

Commit ac718c9

Browse files
committed
Implement library for lockfiles and use it in cfg code.
This is useful because the upcoming configuration database also needs a lockfile implementation. Also adds tests.
1 parent 8ca79da commit ac718c9

10 files changed

Lines changed: 627 additions & 124 deletions

File tree

lib/automake.mk

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ lib_libopenvswitch_a_SOURCES = \
5555
lib/learning-switch.h \
5656
lib/list.c \
5757
lib/list.h \
58+
lib/lockfile.c \
59+
lib/lockfile.h \
5860
lib/mac-learning.c \
5961
lib/mac-learning.h \
6062
lib/netdev-linux.c \
@@ -175,9 +177,9 @@ install-data-local:
175177

176178
# All the source files that have coverage counters.
177179
COVERAGE_FILES = \
178-
lib/cfg.c \
179180
lib/dpif.c \
180181
lib/flow.c \
182+
lib/lockfile.c \
181183
lib/hmap.c \
182184
lib/mac-learning.c \
183185
lib/netdev.c \

lib/cfg.c

Lines changed: 17 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@
2525
#include <netinet/in.h>
2626
#include <stdlib.h>
2727
#include <string.h>
28-
#include <sys/stat.h>
29-
#include <unistd.h>
3028
#include "coverage.h"
3129
#include "dynamic-string.h"
30+
#include "lockfile.h"
3231
#include "ofpbuf.h"
3332
#include "packets.h"
3433
#include "svec.h"
@@ -52,8 +51,7 @@ static char *cfg_name;
5251
static char *tmp_name;
5352

5453
/* Lock information. */
55-
static char *lock_name;
56-
static int lock_fd = -1;
54+
static struct lockfile *lockfile;
5755

5856
/* Flag to indicate whether local modifications have been made. */
5957
static bool dirty;
@@ -106,15 +104,14 @@ cfg_init(void)
106104
int
107105
cfg_set_file(const char *file_name)
108106
{
109-
const char *slash;
107+
char *lock_name;
110108
int fd;
111109

112110
if (cfg_name) {
113-
assert(lock_fd < 0);
111+
assert(!lockfile);
114112
free(cfg_name);
115-
free(lock_name);
116113
free(tmp_name);
117-
cfg_name = lock_name = tmp_name = NULL;
114+
cfg_name = tmp_name = NULL;
118115
}
119116

120117
/* Make sure that we can open this file for reading. */
@@ -131,18 +128,11 @@ cfg_set_file(const char *file_name)
131128
* rename(tmp_name, cfg_name) will work. */
132129
tmp_name = xasprintf("%s.~tmp~", file_name);
133130

134-
/* Put the lock file in the same directory as cfg_name, but prefixed by
135-
* a dot so as not to garner administrator interest. */
136-
slash = strrchr(file_name, '/');
137-
if (slash) {
138-
lock_name = xasprintf("%.*s/.%s.~lock~",
139-
slash - file_name, file_name, slash + 1);
140-
} else {
141-
lock_name = xasprintf(".%s.~lock~", file_name);
142-
}
143-
131+
lock_name = lockfile_name(file_name);
144132
VLOG_INFO("using \"%s\" as configuration file, \"%s\" as lock file",
145133
file_name, lock_name);
134+
free(lock_name);
135+
146136
return 0;
147137
}
148138

@@ -280,61 +270,12 @@ cfg_get_cookie(uint8_t *cookie)
280270
void
281271
cfg_unlock(void)
282272
{
283-
if (lock_fd != -1) {
284-
COVERAGE_INC(cfg_unlock);
285-
close(lock_fd);
286-
lock_fd = -1;
287-
}
288-
}
289-
290-
static int
291-
open_lockfile(const char *name)
292-
{
293-
for (;;) {
294-
/* Try to open an existing lock file. */
295-
int fd = open(name, O_RDWR);
296-
if (fd >= 0) {
297-
return fd;
298-
} else if (errno != ENOENT) {
299-
VLOG_WARN("%s: failed to open lock file: %s",
300-
name, strerror(errno));
301-
return -errno;
302-
}
303-
304-
/* Try to create a new lock file. */
305-
VLOG_INFO("%s: lock file does not exist, creating", name);
306-
fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
307-
if (fd >= 0) {
308-
return fd;
309-
} else if (errno != EEXIST) {
310-
VLOG_WARN("%s: failed to create lock file: %s",
311-
name, strerror(errno));
312-
return -errno;
313-
}
314-
315-
/* Someone else created the lock file. Try again. */
273+
if (lockfile) {
274+
lockfile_unlock(lockfile);
275+
lockfile = NULL;
316276
}
317277
}
318278

319-
static int
320-
try_lock(int fd, bool block)
321-
{
322-
struct flock l;
323-
int error;
324-
325-
memset(&l, 0, sizeof l);
326-
l.l_type = F_WRLCK;
327-
l.l_whence = SEEK_SET;
328-
l.l_start = 0;
329-
l.l_len = 0;
330-
331-
time_disable_restart();
332-
error = fcntl(fd, block ? F_SETLKW : F_SETLK, &l) == -1 ? errno : 0;
333-
time_enable_restart();
334-
335-
return error;
336-
}
337-
338279
/* Locks the configuration file against modification by other processes and
339280
* re-reads it from disk.
340281
*
@@ -346,65 +287,18 @@ try_lock(int fd, bool block)
346287
int
347288
cfg_lock(uint8_t *cookie, int timeout)
348289
{
349-
long long int start;
350-
long long int elapsed = 0;
351-
int fd;
352-
uint8_t curr_cookie[CFG_COOKIE_LEN];
353-
354-
assert(lock_fd < 0);
355-
COVERAGE_INC(cfg_lock);
356-
357-
time_refresh();
358-
start = time_msec();
359-
for (;;) {
360-
int error;
361-
362-
/* Open lock file. */
363-
fd = open_lockfile(lock_name);
364-
if (fd < 0) {
365-
return -fd;
366-
}
367-
368-
/* Try to lock it. This will block (if 'timeout' > 0). */
369-
error = try_lock(fd, timeout > 0);
370-
time_refresh();
371-
elapsed = time_msec() - start;
372-
if (!error) {
373-
/* Success! */
374-
break;
375-
}
376-
377-
/* Lock failed. Close the lock file and reopen it on the next
378-
* iteration, just in case someone deletes it underneath us (even
379-
* though that should not happen). */
380-
close(fd);
381-
if (error != EINTR) {
382-
/* Hard error, give up. */
383-
COVERAGE_INC(cfg_lock_error);
384-
VLOG_WARN("%s: failed to lock file "
385-
"(after %lld ms, with %d-ms timeout): %s",
386-
lock_name, elapsed, timeout, strerror(error));
387-
return error;
388-
}
290+
int error;
389291

390-
/* Probably, the periodic timer set up by time_init() woke up us. Just
391-
* check whether it's time to give up. */
392-
if (timeout != INT_MAX && elapsed >= timeout) {
393-
COVERAGE_INC(cfg_lock_timeout);
394-
VLOG_WARN("%s: giving up on lock file after %lld ms",
395-
lock_name, elapsed);
396-
return ETIMEDOUT;
397-
}
398-
COVERAGE_INC(cfg_lock_retry);
399-
}
400-
if (elapsed) {
401-
VLOG_WARN("%s: waited %lld ms for lock file", lock_name, elapsed);
292+
assert(!lockfile);
293+
error = lockfile_lock(cfg_name, timeout, &lockfile);
294+
if (error) {
295+
return error;
402296
}
403-
lock_fd = fd;
404297

405298
cfg_read();
406299

407300
if (cookie) {
301+
uint8_t curr_cookie[CFG_COOKIE_LEN];
408302
cfg_get_cookie(curr_cookie);
409303

410304
if (memcmp(curr_cookie, cookie, sizeof *curr_cookie)) {

lib/daemon.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <unistd.h>
2424
#include "fatal-signal.h"
2525
#include "dirs.h"
26+
#include "lockfile.h"
2627
#include "timeval.h"
2728
#include "util.h"
2829

@@ -224,6 +225,7 @@ daemonize(void)
224225
chdir("/");
225226
}
226227
time_postfork();
228+
lockfile_postfork();
227229
break;
228230

229231
case -1:

0 commit comments

Comments
 (0)