add error reporting support
[project/netifd.git] / interface.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4
5 #include "netifd.h"
6 #include "ubus.h"
7
8 LIST_HEAD(interfaces);
9
10 static void
11 clear_interface_errors(struct interface *iface)
12 {
13 struct interface_error *error, *tmp;
14
15 list_for_each_entry_safe(error, tmp, &iface->errors, list) {
16 list_del(&error->list);
17 free(error);
18 }
19 }
20
21 void interface_add_error(struct interface *iface, const char *subsystem,
22 const char *code, const char **data, int n_data)
23 {
24 struct interface_error *error;
25 int i, len = 0;
26 int *datalen;
27 char *dest;
28
29 if (n_data) {
30 len = n_data * sizeof(char *);
31 datalen = alloca(len);
32 for (i = 0; i < n_data; i++) {
33 datalen[i] = strlen(data[i]) + 1;
34 len += datalen[i];
35 }
36 }
37
38 error = calloc(1, sizeof(*error) + sizeof(char *) + len);
39 if (!error)
40 return;
41
42 list_add_tail(&error->list, &iface->errors);
43 error->subsystem = subsystem;
44 error->code = code;
45
46 dest = (char *) &error->data[n_data + 1];
47 for (i = 0; i < n_data; i++) {
48 error->data[i] = dest;
49 memcpy(dest, data[i], datalen[i]);
50 dest += datalen[i];
51 }
52 error->data[n_data] = NULL;
53 }
54
55 static int
56 interface_event(struct interface *iface, enum interface_event ev)
57 {
58 if (!iface->state || !iface->state->event)
59 return 0;
60
61 return iface->state->event(iface, iface->state, ev);
62 }
63
64 static void
65 __set_interface_up(struct interface *iface)
66 {
67 if (iface->up)
68 return;
69
70 if (claim_device(iface->main_dev.dev) < 0)
71 return;
72
73 if (interface_event(iface, IFEV_UP) < 0) {
74 release_device(iface->main_dev.dev);
75 return;
76 }
77
78 iface->up = true;
79 }
80
81 static void
82 __set_interface_down(struct interface *iface)
83 {
84 clear_interface_errors(iface);
85
86 if (!iface->up)
87 return;
88
89 iface->up = false;
90 interface_event(iface, IFEV_DOWN);
91 release_device(iface->main_dev.dev);
92 }
93
94 static void
95 interface_cb(struct device_user *dep, enum device_event ev)
96 {
97 struct interface *iface;
98 bool new_state;
99
100 iface = container_of(dep, struct interface, main_dev);
101 switch (ev) {
102 case DEV_EVENT_ADD:
103 new_state = true;
104 break;
105 case DEV_EVENT_REMOVE:
106 new_state = false;
107 break;
108 default:
109 return;
110 }
111
112 if (iface->active == new_state)
113 return;
114
115 iface->active = new_state;
116
117 if (new_state) {
118 if (iface->autostart)
119 __set_interface_up(iface);
120 } else
121 __set_interface_down(iface);
122 }
123
124 struct interface *
125 alloc_interface(const char *name)
126 {
127 struct interface *iface;
128
129 iface = get_interface(name);
130 if (iface)
131 return iface;
132
133 iface = calloc(1, sizeof(*iface));
134 iface->main_dev.cb = interface_cb;
135 iface->l3_iface = &iface->main_dev;
136 strncpy(iface->name, name, sizeof(iface->name) - 1);
137 list_add(&iface->list, &interfaces);
138 INIT_LIST_HEAD(&iface->errors);
139
140 netifd_ubus_add_interface(iface);
141
142 return iface;
143 }
144
145 void
146 free_interface(struct interface *iface)
147 {
148 netifd_ubus_remove_interface(iface);
149 list_del(&iface->list);
150 if (iface->state && iface->state->free)
151 iface->state->free(iface, iface->state);
152 free(iface);
153 }
154
155 struct interface *
156 get_interface(const char *name)
157 {
158 struct interface *iface;
159
160 list_for_each_entry(iface, &interfaces, list) {
161 if (!strcmp(iface->name, name))
162 return iface;
163 }
164 return NULL;
165 }
166
167 void
168 interface_remove_link(struct interface *iface, struct device *llif)
169 {
170 struct device *dev = iface->main_dev.dev;
171
172 if (dev && dev->hotplug_ops) {
173 dev->hotplug_ops->del(dev, llif);
174 return;
175 }
176
177 remove_device_user(&iface->main_dev);
178 }
179
180 int
181 interface_add_link(struct interface *iface, struct device *llif)
182 {
183 struct device *dev = iface->main_dev.dev;
184
185 if (dev && dev->hotplug_ops)
186 return dev->hotplug_ops->add(dev, llif);
187
188 if (iface->main_dev.dev)
189 interface_remove_link(iface, NULL);
190
191 add_device_user(&iface->main_dev, llif);
192
193 return 0;
194 }
195
196 int
197 set_interface_up(struct interface *iface)
198 {
199 iface->autostart = true;
200
201 if (!iface->active) {
202 interface_add_error(iface, "interface", "NO_DEVICE", NULL, 0);
203 return -1;
204 }
205
206 if (iface->up || !iface->active)
207 return -1;
208
209 __set_interface_up(iface);
210 return 0;
211 }
212
213 int
214 set_interface_down(struct interface *iface)
215 {
216 iface->autostart = false;
217 __set_interface_down(iface);
218
219 return 0;
220 }