add code for cleaning up unused devices mentioned in the config
[project/netifd.git] / device.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <assert.h>
5
6 #include <libubox/uapi.h>
7
8 #include "netifd.h"
9 #include "system.h"
10
11 static struct avl_tree devices;
12
13 static int avl_strcmp(const void *k1, const void *k2, void *ptr)
14 {
15         return strcmp(k1, k2);
16 }
17
18 static void API_CTOR dev_init(void)
19 {
20         avl_init(&devices, avl_strcmp, false, NULL);
21 }
22
23 static void free_simple_device(struct device *dev)
24 {
25         cleanup_device(dev);
26         free(dev);
27 }
28
29 static void broadcast_device_event(struct device *dev, enum device_event ev)
30 {
31         struct device_user *dep, *tmp;
32
33         list_for_each_entry_safe(dep, tmp, &dev->users, list) {
34                 if (!dep->cb)
35                         continue;
36
37                 dep->cb(dep, ev);
38         }
39 }
40
41 static int set_device_state(struct device *dev, bool state)
42 {
43         if (state)
44                 system_if_up(dev);
45         else
46                 system_if_down(dev);
47
48         return 0;
49 }
50
51 int claim_device(struct device *dev)
52 {
53         int ret;
54
55         DPRINTF("claim device %s, new refcount: %d\n", dev->ifname, dev->active + 1);
56         if (++dev->active != 1)
57                 return 0;
58
59         broadcast_device_event(dev, DEV_EVENT_SETUP);
60         ret = dev->set_state(dev, true);
61         if (ret == 0)
62                 broadcast_device_event(dev, DEV_EVENT_UP);
63         else
64                 dev->active = 0;
65
66         return ret;
67 }
68
69 void release_device(struct device *dev)
70 {
71         dev->active--;
72         DPRINTF("release device %s, new refcount: %d\n", dev->ifname, dev->active);
73         assert(dev->active >= 0);
74
75         if (dev->active)
76                 return;
77
78         broadcast_device_event(dev, DEV_EVENT_TEARDOWN);
79         dev->set_state(dev, false);
80         broadcast_device_event(dev, DEV_EVENT_DOWN);
81 }
82
83 int check_device_state(struct device *dev)
84 {
85         if (!dev->type->check_state)
86                 return 0;
87
88         return dev->type->check_state(dev);
89 }
90
91 void init_virtual_device(struct device *dev, const struct device_type *type, const char *name)
92 {
93         assert(dev);
94         assert(type);
95
96         if (name)
97                 strncpy(dev->ifname, name, IFNAMSIZ);
98
99         fprintf(stderr, "Initialize device '%s'\n", dev->ifname);
100         INIT_LIST_HEAD(&dev->users);
101         dev->type = type;
102 }
103
104 int init_device(struct device *dev, const struct device_type *type, const char *ifname)
105 {
106         int ret;
107
108         init_virtual_device(dev, type, ifname);
109
110         if (!dev->set_state)
111                 dev->set_state = set_device_state;
112
113         dev->avl.key = dev->ifname;
114
115         ret = avl_insert(&devices, &dev->avl);
116         if (ret < 0)
117                 return ret;
118
119         check_device_state(dev);
120
121         return 0;
122 }
123
124 struct device *get_device(const char *name, bool create)
125 {
126         static const struct device_type simple_type = {
127                 .name = "Device",
128                 .check_state = system_if_check,
129                 .free = free_simple_device,
130         };
131         struct device *dev;
132
133
134         if (strchr(name, '.'))
135                 return get_vlan_device_chain(name, create);
136
137         dev = avl_find_element(&devices, name, dev, avl);
138         if (dev)
139                 return dev;
140
141         if (!create)
142                 return NULL;
143
144         dev = calloc(1, sizeof(*dev));
145         init_device(dev, &simple_type, name);
146
147         return dev;
148 }
149
150 void cleanup_device(struct device *dev)
151 {
152         struct device_user *dep, *tmp;
153
154         fprintf(stderr, "Clean up device '%s'\n", dev->ifname);
155         list_for_each_entry_safe(dep, tmp, &dev->users, list) {
156                 if (!dep->cb)
157                         continue;
158
159                 dep->cb(dep, DEV_EVENT_REMOVE);
160         }
161
162         if (dev->avl.key)
163                 avl_delete(&devices, &dev->avl);
164 }
165
166 void set_device_present(struct device *dev, bool state)
167 {
168         if (dev->present == state)
169                 return;
170
171         DPRINTF("Device '%s' %s present\n", dev->ifname, state ? "is now" : "is no longer" );
172         dev->present = state;
173         broadcast_device_event(dev, state ? DEV_EVENT_ADD : DEV_EVENT_REMOVE);
174 }
175
176 void add_device_user(struct device_user *dep, struct device *dev)
177 {
178         dep->dev = dev;
179         list_add(&dep->list, &dev->users);
180         if (dep->cb && dev->present) {
181                 dep->cb(dep, DEV_EVENT_ADD);
182                 if (dev->active)
183                         dep->cb(dep, DEV_EVENT_UP);
184         }
185 }
186
187 void remove_device_user(struct device_user *dep)
188 {
189         struct device *dev = dep->dev;
190
191         list_del(&dep->list);
192
193         if (list_empty(&dev->users)) {
194                 /* all references have gone away, remove this device */
195                 free_device(dev);
196         }
197
198         dep->dev = NULL;
199 }
200
201 void
202 cleanup_devices(void)
203 {
204         struct device *dev, *tmp;
205
206         avl_for_each_element_safe(&devices, dev, avl, tmp) {
207                 if (!list_empty(&dev->users))
208                         continue;
209
210                 free_device(dev);
211         }
212 }