fix device reload with no previous config
[project/netifd.git] / device.c
index 8bc8b53..ab8ff91 100644 (file)
--- a/device.c
+++ b/device.c
@@ -6,7 +6,10 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <net/ethernet.h>
+
+#ifdef linux
 #include <netinet/ether.h>
+#endif
 
 #include "netifd.h"
 #include "system.h"
@@ -294,3 +297,85 @@ device_free_unused(struct device *dev)
        avl_for_each_element_safe(&devices, dev, avl, tmp)
                __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));
+       if (dev->config)
+               blobmsg_parse(dev_attrs, __DEV_ATTR_MAX, tb1,
+                       blob_data(dev->config), blob_len(dev->config));
+       else
+               memset(tb1, 0, sizeof(tb1));
+
+       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 *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 = type->create(config);
+       dev->config = config_memdup(config);
+       if (odev)
+               device_replace(dev, odev);
+
+       return dev;
+}