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>
This commit is contained in:
parent
ec1ea16337
commit
3720a61daf
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user