summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2011-10-03 01:56:37 +0200
committerFelix Fietkau <nbd@openwrt.org>2011-10-03 01:56:37 +0200
commitf6b38cb7c9ad0e7feba7cb7d2a1678af5e032000 (patch)
treebdd8833e10c25ebfc7d017e12d505619aac3ff6a
parent39f5ec857a31fe65e14cdf9b75927297e78015bb (diff)
more changes for config reload handling
-rw-r--r--config.c52
-rw-r--r--config.h2
-rw-r--r--device.c76
-rw-r--r--device.h10
-rw-r--r--interface.c3
5 files changed, 127 insertions, 16 deletions
diff --git a/config.c b/config.c
index ff0410d..4f3a3cd 100644
--- a/config.c
+++ b/config.c
@@ -274,22 +274,56 @@ config_check_equal(struct blob_attr *c1, struct blob_attr *c2,
return true;
}
-void
-config_init_interfaces(const char *name)
+struct blob_attr *
+config_memdup(struct blob_attr *attr)
{
- struct uci_context *ctx;
+ struct blob_attr *ret;
+ int size = blob_pad_len(attr);
+
+ ret = malloc(size);
+ if (!ret)
+ return NULL;
+
+ memcpy(ret, attr, size);
+ return ret;
+}
+
+static struct uci_package *
+config_init_package(const char *config)
+{
+ struct uci_context *ctx = uci_ctx;
struct uci_package *p = NULL;
- struct uci_element *e;
- ctx = uci_alloc_context();
- uci_ctx = ctx;
+ if (!ctx) {
+ ctx = uci_alloc_context();
+ uci_ctx = ctx;
#ifdef DUMMY_MODE
- uci_set_confdir(ctx, "./config");
- uci_set_savedir(ctx, "./tmp");
+ uci_set_confdir(ctx, "./config");
+ uci_set_savedir(ctx, "./tmp");
#endif
+ } else {
+ p = uci_lookup_package(ctx, config);
+ if (p)
+ uci_unload(ctx, p);
+ }
+
+ if (uci_load(ctx, "network", &p))
+ return NULL;
+
+ return p;
+}
+
+void
+config_init_interfaces(const char *name)
+{
+ struct uci_context *ctx;
+ struct uci_package *p = NULL;
+ struct uci_element *e;
- if (uci_load(ctx, "network", &p)) {
+ p = config_init_package("network");
+ ctx = uci_ctx;
+ if (!p) {
fprintf(stderr, "Failed to load network config\n");
return;
}
diff --git a/config.h b/config.h
index 6dfb236..c4953c6 100644
--- a/config.h
+++ b/config.h
@@ -45,4 +45,6 @@ bool config_check_equal(struct blob_attr *c1, struct blob_attr *c2,
bool config_diff(struct blob_attr **tb1, struct blob_attr **tb2,
const struct config_param_list *config, unsigned long *diff);
+struct blob_attr *config_memdup(struct blob_attr *attr);
+
#endif
diff --git a/device.c b/device.c
index 77594be..caadbc3 100644
--- a/device.c
+++ b/device.c
@@ -298,15 +298,81 @@ device_free_unused(struct device *dev)
__device_free_unused(dev);
}
+enum dev_change_type
+device_reload_config(struct device *dev, struct blob_attr *attr)
+{
+ struct blob_attr *tb[__DEV_ATTR_MAX], *tb1[__DEV_ATTR_MAX];
+
+ blobmsg_parse(dev_attrs, __DEV_ATTR_MAX, tb,
+ blob_data(attr), blob_len(attr));
+ blobmsg_parse(dev_attrs, __DEV_ATTR_MAX, tb1,
+ blob_data(dev->config), blob_len(dev->config));
+
+ if (!config_diff(tb, tb1, &device_attr_list, NULL))
+ return DEV_CONFIG_NO_CHANGE;
+
+ device_init_settings(dev, tb);
+ return DEV_CONFIG_APPLIED;
+}
+
+static enum dev_change_type
+device_check_config(struct device *dev, struct blob_attr *attr)
+{
+ if (dev->type->reload)
+ return dev->type->reload(dev, attr);
+
+ return device_reload_config(dev, attr);
+}
+
+static void
+device_replace(struct device *dev, struct device *odev)
+{
+ struct device_user *dep, *tmp;
+ bool present = odev->present;
+
+ if (present)
+ device_set_present(odev, false);
+
+ list_for_each_entry_safe(dep, tmp, &odev->users, list) {
+ list_move_tail(&dep->list, &dev->users);
+ dep->dev = dev;
+ }
+ device_free(odev);
+
+ if (present)
+ device_set_present(dev, true);
+}
+
struct device *
device_create(const char *name, const struct device_type *type,
struct blob_attr *config)
{
- struct device *dev;
+ struct device *odev = NULL, *dev;
+ enum dev_change_type change;
+
+ odev = device_get(name, false);
+ if (odev) {
+ change = device_check_config(odev, config);
+ switch (change) {
+ case DEV_CONFIG_APPLIED:
+ free(odev->config);
+ odev->config = config_memdup(config);
+ if (odev->present) {
+ device_set_present(odev, false);
+ device_set_present(odev, true);
+ }
+ /* fall through */
+ case DEV_CONFIG_NO_CHANGE:
+ return odev;
+ case DEV_CONFIG_RECREATE:
+ break;
+ }
+ }
- dev = device_get(name, false);
- if (dev)
- return dev;
+ dev = type->create(config);
+ dev->config = config_memdup(config);
+ if (odev)
+ device_replace(dev, odev);
- return type->create(config);
+ return dev;
}
diff --git a/device.h b/device.h
index f1cdda0..907b9c6 100644
--- a/device.h
+++ b/device.h
@@ -19,6 +19,12 @@ enum {
__DEV_ATTR_MAX,
};
+enum dev_change_type {
+ DEV_CONFIG_NO_CHANGE,
+ DEV_CONFIG_APPLIED,
+ DEV_CONFIG_RECREATE,
+};
+
struct device_type {
struct list_head list;
const char *name;
@@ -26,6 +32,7 @@ struct device_type {
const struct config_param_list *config_params;
struct device *(*create)(struct blob_attr *attr);
+ enum dev_change_type (*reload)(struct device *, struct blob_attr *);
void (*dump_status)(struct device *, struct blob_buf *buf);
int (*check_state)(struct device *);
void (*free)(struct device *);
@@ -50,6 +57,7 @@ struct device {
char ifname[IFNAMSIZ + 1];
int ifindex;
+ struct blob_attr *config;
bool present;
int active;
@@ -64,8 +72,6 @@ struct device {
unsigned int mtu;
unsigned int txqueuelen;
uint8_t macaddr[6];
-
- uint32_t config_hash;
};
/* events broadcasted to all users of a device */
diff --git a/interface.c b/interface.c
index 5b50314..435b485 100644
--- a/interface.c
+++ b/interface.c
@@ -384,6 +384,7 @@ interface_change_config(struct interface *if_old, struct interface *if_new)
set_config_state(if_old, IFC_RELOAD);
free(old_config);
+ free(if_new);
}
static void
@@ -410,4 +411,6 @@ interface_init_list(void)
{
vlist_init(&interfaces, avl_strcmp, interface_update,
struct interface, node, name);
+ interfaces.keep_old = true;
+ interfaces.no_delete = true;
}