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:
Denys Vlasenko 2016-10-09 23:04:16 +02:00
parent ec1ea16337
commit 3720a61daf

View File

@ -56,6 +56,7 @@
#endif #endif
#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS #define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
#define IFSTATE_FILE_PATH CONFIG_IFUPDOWN_IFSTATE_PATH
#define debug_noise(args...) /*fprintf(stderr, args)*/ #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) static llist_t *read_iface_state(void)
{ {
llist_t *state_list = NULL; 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) { if (state_fp) {
char *start, *end_ptr; char *start, *end_ptr;
@ -1215,6 +1216,38 @@ static llist_t *read_iface_state(void)
return state_list; 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, char **argv) MAIN_EXTERNALLY_VISIBLE;
int ifupdown_main(int argc UNUSED_PARAM, char **argv) 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; any_failures = 1;
} else if (!NO_ACT) { } else if (!NO_ACT) {
/* update the state file */ /* update the state file */
FILE *state_fp; FILE *new_state_fp = open_new_state_file();
llist_t *state; llist_t *state;
llist_t *state_list = read_iface_state(); llist_t *state_list = read_iface_state();
llist_t *iface_state = find_iface_state(state_list, iface); 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 */ /* Actually write the new state */
state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
state = state_list; state = state_list;
while (state) { while (state) {
if (state->data) { if (state->data) {
fprintf(state_fp, "%s\n", state->data); fprintf(new_state_fp, "%s\n", state->data);
} }
state = state->link; state = state->link;
} }
fclose(state_fp); fclose(new_state_fp);
xrename(IFSTATE_FILE_PATH".new", IFSTATE_FILE_PATH);
llist_free(state_list, free); llist_free(state_list, free);
} }
next: next: