aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-10-09 23:04:16 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-10-09 23:04:16 +0200
commit3720a61daf2e03e42e34902c2636ce3e3d6b8485 (patch)
tree6208f33c94578dbfaf39b5b108f7745190bb2c3b
parentec1ea16337623824e3e71bb5dc0e011259664d7e (diff)
downloadbusybox-3720a61daf2e03e42e34902c2636ce3e3d6b8485.tar.gz
ifupdown: rewrite state file atomically
By user's request. Decided to not use fcntl(F_SETLKW) in lieu of problems with locking on networked filesystems. The existence of /var/run/ifstate.new is treated as a write lock. rename() provides atomicity. function old new delta ifupdown_main 1019 1122 +103 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--networking/ifupdown.c43
1 files changed, 38 insertions, 5 deletions
diff --git a/networking/ifupdown.c b/networking/ifupdown.c
index b0bc0d70f..1d0fc53cf 100644
--- a/networking/ifupdown.c
+++ b/networking/ifupdown.c
@@ -56,6 +56,7 @@
#endif
#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
+#define IFSTATE_FILE_PATH CONFIG_IFUPDOWN_IFSTATE_PATH
#define debug_noise(args...) /*fprintf(stderr, args)*/
@@ -1200,7 +1201,7 @@ static llist_t *find_iface_state(llist_t *state_list, const char *iface)
static llist_t *read_iface_state(void)
{
llist_t *state_list = NULL;
- FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH);
+ FILE *state_fp = fopen_for_read(IFSTATE_FILE_PATH);
if (state_fp) {
char *start, *end_ptr;
@@ -1215,6 +1216,38 @@ static llist_t *read_iface_state(void)
return state_list;
}
+/* read the previous state from the state file */
+static FILE *open_new_state_file(void)
+{
+ int fd, flags, cnt;
+
+ cnt = 0;
+ flags = (O_WRONLY | O_CREAT | O_EXCL);
+ for (;;) {
+ fd = open(IFSTATE_FILE_PATH".new", flags, 0666);
+ if (fd >= 0)
+ break;
+ if (errno != EEXIST
+ || flags == (O_WRONLY | O_CREAT | O_TRUNC)
+ ) {
+ bb_perror_msg_and_die("can't open '%s'",
+ IFSTATE_FILE_PATH".new");
+ }
+ /* Someone else created the .new file */
+ if (cnt > 30 * 1000) {
+ /* Waited for 30*30/2 = 450 milliseconds, still EEXIST.
+ * Assuming a stale file, rewriting it.
+ */
+ flags = (O_WRONLY | O_CREAT | O_TRUNC);
+ continue;
+ }
+ usleep(cnt);
+ cnt += 1000;
+ }
+
+ return xfdopen_for_write(fd);
+}
+
int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ifupdown_main(int argc UNUSED_PARAM, char **argv)
@@ -1348,7 +1381,7 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
any_failures = 1;
} else if (!NO_ACT) {
/* update the state file */
- FILE *state_fp;
+ FILE *new_state_fp = open_new_state_file();
llist_t *state;
llist_t *state_list = read_iface_state();
llist_t *iface_state = find_iface_state(state_list, iface);
@@ -1368,15 +1401,15 @@ int ifupdown_main(int argc UNUSED_PARAM, char **argv)
}
/* Actually write the new state */
- state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
state = state_list;
while (state) {
if (state->data) {
- fprintf(state_fp, "%s\n", state->data);
+ fprintf(new_state_fp, "%s\n", state->data);
}
state = state->link;
}
- fclose(state_fp);
+ fclose(new_state_fp);
+ xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH);
llist_free(state_list, free);
}
next: