memoryleak: remove element when not needed any more
[framework/connectivity/connman.git] / src / dhcp.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27
28 #include "connman.h"
29
30 struct connman_dhcp {
31         gint refcount;
32         int index;
33         enum connman_dhcp_state state;
34
35         struct connman_element *element;
36
37         struct connman_dhcp_driver *driver;
38         void *driver_data;
39 };
40
41 /**
42  * connman_dhcp_ref:
43  * @dhcp: DHCP structure
44  *
45  * Increase reference counter of DHCP
46  */
47 struct connman_dhcp *connman_dhcp_ref(struct connman_dhcp *dhcp)
48 {
49         g_atomic_int_inc(&dhcp->refcount);
50
51         return dhcp;
52 }
53
54 /**
55  * connman_dhcp_unref:
56  * @dhcp: DHCP structure
57  *
58  * Decrease reference counter of DHCP
59  */
60 void connman_dhcp_unref(struct connman_dhcp *dhcp)
61 {
62         if (g_atomic_int_dec_and_test(&dhcp->refcount) == TRUE)
63                 g_free(dhcp);
64 }
65
66 /**
67  * connman_dhcp_get_index:
68  * @dhcp: DHCP structure
69  *
70  * Get network index of DHCP
71  */
72 int connman_dhcp_get_index(struct connman_dhcp *dhcp)
73 {
74         return dhcp->index;
75 }
76
77 /**
78  * connman_dhcp_get_interface:
79  * @dhcp: DHCP structure
80  *
81  * Get network interface of DHCP
82  */
83 char *connman_dhcp_get_interface(struct connman_dhcp *dhcp)
84 {
85         return connman_inet_ifname(dhcp->index);
86 }
87
88 /**
89  * connman_dhcp_set_value:
90  * @dhcp: DHCP structure
91  * @key: unique identifier
92  * @value: string value
93  *
94  * Set string value for specific key
95  */
96 void connman_dhcp_set_value(struct connman_dhcp *dhcp,
97                                         const char *key, const char *value)
98 {
99         char **nameservers;
100
101         if (g_strcmp0(key, "Address") == 0) {
102                 g_free(dhcp->element->ipv4.address);
103                 dhcp->element->ipv4.address = g_strdup(value);
104         } else if (g_strcmp0(key, "Netmask") == 0) {
105                 g_free(dhcp->element->ipv4.netmask);
106                 dhcp->element->ipv4.netmask = g_strdup(value);
107         } else if (g_strcmp0(key, "Gateway") == 0) {
108                 g_free(dhcp->element->ipv4.gateway);
109                 dhcp->element->ipv4.gateway = g_strdup(value);
110         } else if (g_strcmp0(key, "Network") == 0) {
111                 g_free(dhcp->element->ipv4.network);
112                 dhcp->element->ipv4.network = g_strdup(value);
113         } else if (g_strcmp0(key, "Broadcast") == 0) {
114                 g_free(dhcp->element->ipv4.broadcast);
115                 dhcp->element->ipv4.broadcast = g_strdup(value);
116         } else if (g_strcmp0(key, "Nameserver") == 0) {
117                 g_free(dhcp->element->ipv4.nameserver);
118                 nameservers = g_strsplit_set(value, " ", 0);
119                 /* FIXME: The ipv4 structure can only hold one nameserver, so
120                  * we are only able to pass along the first nameserver sent by
121                  * the DHCP server.  If this situation changes, we should
122                  * retain all of them.
123                  */
124                 dhcp->element->ipv4.nameserver = g_strdup(nameservers[0]);
125                 g_strfreev(nameservers);
126         } else if (g_strcmp0(key, "Domainname") == 0) {
127                 g_free(dhcp->element->domainname);
128                 dhcp->element->domainname = g_strdup(value);
129
130                 __connman_utsname_set_domainname(value);
131         } else if (g_strcmp0(key, "Hostname") == 0) {
132                 g_free(dhcp->element->hostname);
133                 dhcp->element->hostname = g_strdup(value);
134
135                 __connman_utsname_set_hostname(value);
136         } else if (g_strcmp0(key, "Timeserver") == 0) {
137                 connman_info("Timeserver %s", value);
138
139                 g_free(dhcp->element->ipv4.timeserver);
140                 dhcp->element->ipv4.timeserver = g_strdup(value);
141         } else if (g_strcmp0(key, "MTU") == 0) {
142         } else if (g_strcmp0(key, "PAC") == 0) {
143                 connman_info("PAC configuration %s", value);
144
145                 g_free(dhcp->element->ipv4.pac);
146                 dhcp->element->ipv4.pac = g_strdup(value);
147         }
148 }
149
150 /**
151  * connman_dhcp_bound:
152  * @dhcp: DHCP structure
153  *
154  * Report successful bound of the interface
155  */
156 void connman_dhcp_bound(struct connman_dhcp *dhcp)
157 {
158         struct connman_element *element;
159
160         DBG("dhcp %p", dhcp);
161
162         element = connman_element_create(NULL);
163         if (element == NULL)
164                 return;
165
166         element->type = CONNMAN_ELEMENT_TYPE_IPV4;
167         element->index = dhcp->index;
168
169         connman_element_update(dhcp->element);
170
171         if (connman_element_register(element, dhcp->element) < 0)
172                 connman_element_unref(element);
173 }
174
175 /**
176  * connman_dhcp_renew:
177  * @dhcp: DHCP structure
178  *
179  * Report successful renew of the interface
180  */
181 void connman_dhcp_renew(struct connman_dhcp *dhcp)
182 {
183         DBG("dhcp %p", dhcp);
184
185         connman_element_update(dhcp->element);
186 }
187
188 /**
189  * connman_dhcp_release:
190  * @dhcp: DHCP structure
191  *
192  * Report DHCP release of the interface
193  */
194 void connman_dhcp_release(struct connman_dhcp *dhcp)
195 {
196         DBG("dhcp %p", dhcp);
197
198         connman_element_unregister_children(dhcp->element);
199 }
200
201 /**
202  * connman_dhcp_fail:
203  * @dhcp: DHCP structure
204  *
205  * Report DHCP failure of the interface
206  */
207 void connman_dhcp_fail(struct connman_dhcp *dhcp)
208 {
209         DBG("dhcp %p", dhcp);
210
211         connman_element_set_error(dhcp->element,
212                                         CONNMAN_ELEMENT_ERROR_FAILED);
213 }
214
215 /**
216  * connman_dhcp_get_data:
217  * @dhcp: DHCP structure
218  *
219  * Get private DHCP data pointer
220  */
221 void *connman_dhcp_get_data(struct connman_dhcp *dhcp)
222 {
223         return dhcp->driver_data;
224 }
225
226 /**
227  * connman_dhcp_set_data:
228  * @dhcp: DHCP structure
229  * @data: data pointer
230  *
231  * Set private DHCP data pointer
232  */
233 void connman_dhcp_set_data(struct connman_dhcp *dhcp, void *data)
234 {
235         dhcp->driver_data = data;
236 }
237
238 static GSList *driver_list = NULL;
239
240 static gint compare_priority(gconstpointer a, gconstpointer b)
241 {
242         const struct connman_dhcp_driver *driver1 = a;
243         const struct connman_dhcp_driver *driver2 = b;
244
245         return driver2->priority - driver1->priority;
246 }
247
248 /**
249  * connman_dhcp_driver_register:
250  * @driver: DHCP driver definition
251  *
252  * Register a new DHCP driver
253  *
254  * Returns: %0 on success
255  */
256 int connman_dhcp_driver_register(struct connman_dhcp_driver *driver)
257 {
258         DBG("driver %p name %s", driver, driver->name);
259
260         driver_list = g_slist_insert_sorted(driver_list, driver,
261                                                         compare_priority);
262
263         return 0;
264 }
265
266 /**
267  * connman_dhcp_driver_unregister:
268  * @driver: DHCP driver definition
269  *
270  * Remove a previously registered DHCP driver
271  */
272 void connman_dhcp_driver_unregister(struct connman_dhcp_driver *driver)
273 {
274         DBG("driver %p name %s", driver, driver->name);
275
276         driver_list = g_slist_remove(driver_list, driver);
277 }
278
279 static int dhcp_probe(struct connman_element *element)
280 {
281         struct connman_dhcp *dhcp;
282         GSList *list;
283
284         DBG("element %p name %s", element, element->name);
285
286         dhcp = g_try_new0(struct connman_dhcp, 1);
287         if (dhcp == NULL)
288                 return -ENOMEM;
289
290         dhcp->refcount = 1;
291         dhcp->index = element->index;
292         dhcp->state = CONNMAN_DHCP_STATE_IDLE;
293
294         dhcp->element = element;
295
296         connman_element_set_data(element, dhcp);
297
298         for (list = driver_list; list; list = list->next) {
299                 struct connman_dhcp_driver *driver = list->data;
300
301                 DBG("driver %p name %s", driver, driver->name);
302
303                 if (driver->request(dhcp) == 0) {
304                         dhcp->driver = driver;
305                         break;
306                 }
307         }
308
309         if (dhcp->driver == NULL) {
310                 connman_dhcp_unref(dhcp);
311                 return -ENOENT;
312         }
313
314         return 0;
315 }
316
317 static void dhcp_remove(struct connman_element *element)
318 {
319         struct connman_dhcp *dhcp = connman_element_get_data(element);
320
321         DBG("element %p name %s", element, element->name);
322
323         connman_element_set_data(element, NULL);
324
325         if (dhcp->driver) {
326                 dhcp->driver->release(dhcp);
327                 dhcp->driver = NULL;
328         }
329
330         connman_dhcp_unref(dhcp);
331         connman_element_unref(element);
332 }
333
334 static void dhcp_change(struct connman_element *element)
335 {
336         DBG("element %p name %s", element, element->name);
337
338         if (element->state == CONNMAN_ELEMENT_STATE_ERROR)
339                 connman_element_set_error(element->parent,
340                                         CONNMAN_ELEMENT_ERROR_DHCP_FAILED);
341 }
342
343 static struct connman_driver dhcp_driver = {
344         .name           = "dhcp",
345         .type           = CONNMAN_ELEMENT_TYPE_DHCP,
346         .priority       = CONNMAN_DRIVER_PRIORITY_LOW,
347         .probe          = dhcp_probe,
348         .remove         = dhcp_remove,
349         .change         = dhcp_change,
350 };
351
352 int __connman_dhcp_init(void)
353 {
354         return connman_driver_register(&dhcp_driver);
355 }
356
357 void __connman_dhcp_cleanup(void)
358 {
359         connman_driver_unregister(&dhcp_driver);
360 }