Add experimental DHCP plugin
[framework/connectivity/connman.git] / plugins / dhclient.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007  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 <stdio.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <string.h>
30 #include <arpa/inet.h>
31
32 #include <glib.h>
33 #include <gdbus.h>
34
35 #include <connman/plugin.h>
36 #include <connman/dhcp.h>
37
38 #include "net.h"
39
40 static const char *busname;
41
42 struct dhclient_task {
43         GPid pid;
44         char *ifname;
45         struct connman_iface *iface;
46 };
47
48 static GSList *tasks = NULL;
49
50 static struct dhclient_task *find_task(GPid pid)
51 {
52         GSList *list;
53
54         for (list = tasks; list; list = list->next) {
55                 struct dhclient_task *task = list->data;
56
57                 if (task->pid == pid)
58                         return task;
59         }
60
61         return NULL;
62 }
63
64 static int dhclient_request(struct connman_iface *iface)
65 {
66         struct dhclient_task *task;
67         char *ifname, *argv[16], address[128], pidfile[PATH_MAX];
68         char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX];
69
70         ifname = __net_ifname(iface->sysfs);
71         if (ifname == NULL)
72                 return -1;
73
74         task = g_try_new0(struct dhclient_task, 1);
75         if (task == NULL)
76                 return -1;
77
78         task->ifname = ifname;
79         task->iface = iface;
80
81         printf("[DHCP] request for %s\n", ifname);
82
83         snprintf(address, sizeof(address) - 1, "BUSNAME=%s", busname);
84         snprintf(pidfile, sizeof(pidfile) - 1,
85                                 "%s/dhclient.%s.pid", STATEDIR, ifname);
86         snprintf(leases, sizeof(leases) - 1,
87                                 "%s/dhclient.%s.leases", STATEDIR, ifname);
88         snprintf(config, sizeof(config) - 1, "%s/dhclient.conf", SCRIPTDIR);
89         snprintf(script, sizeof(script) - 1, "%s/dhclient-script", SCRIPTDIR);
90
91         argv[0] = "/sbin/dhclient";
92         argv[1] = "-d";
93         argv[2] = "-q";
94         argv[3] = "-n";
95         argv[4] = "-e";
96         argv[5] = address;
97         argv[6] = "-pf";
98         argv[7] = pidfile;
99         argv[8] = "-lf";
100         argv[9] = leases;
101         argv[10] = "-cf";
102         argv[11] = config;
103         argv[12] = "-sf";
104         argv[13] = script;
105         argv[14] = ifname;
106         argv[15] = NULL;
107
108         if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
109                                 NULL, NULL, &task->pid, NULL) == FALSE) {
110                 printf("Failed to spawn dhclient\n");
111                 return -1;
112         }
113
114         tasks = g_slist_append(tasks, task);
115
116         printf("[DHCP] executed with pid %d\n", task->pid);
117
118         return 0;
119 }
120
121 static int dhclient_release(struct connman_iface *iface)
122 {
123         char *ifname;
124
125         ifname = __net_ifname(iface->sysfs);
126         if (ifname == NULL)
127                 return -1;
128
129         printf("[DHCP] release for %s\n", ifname);
130
131         __net_free(ifname);
132
133         return 0;
134 }
135
136 static struct connman_dhcp_driver dhclient_driver = {
137         .name           = "dhclient",
138         .request        = dhclient_request,
139         .release        = dhclient_release,
140 };
141
142 static DBusMessage *notify_method(DBusConnection *conn,
143                                         DBusMessage *msg, void *data)
144 {
145         DBusMessageIter iter, dict;
146         dbus_uint32_t pid;
147         struct dhclient_task *task;
148         struct connman_ipv4 ipv4;
149         const char *text, *key, *value;
150
151         memset(&ipv4, 0, sizeof(ipv4));
152
153         dbus_message_iter_init(msg, &iter);
154
155         dbus_message_iter_get_basic(&iter, &pid);
156         dbus_message_iter_next(&iter);
157
158         dbus_message_iter_get_basic(&iter, &text);
159         dbus_message_iter_next(&iter);
160
161         printf("[DHCP] change %d to %s\n", pid, text);
162
163         task = find_task(pid);
164         if (task == NULL)
165                 return NULL;
166
167         dbus_message_iter_recurse(&iter, &dict);
168
169         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
170                 DBusMessageIter entry;
171
172                 dbus_message_iter_recurse(&dict, &entry);
173                 dbus_message_iter_get_basic(&entry, &key);
174                 dbus_message_iter_next(&entry);
175                 dbus_message_iter_get_basic(&entry, &value);
176
177                 printf("[DHCP] %s = %s\n", key, value);
178
179                 if (strcmp(key, "new_ip_address") == 0)
180                         inet_aton(value, &ipv4.address);
181
182                 if (strcmp(key, "new_subnet_mask") == 0)
183                         inet_aton(value, &ipv4.netmask);
184
185                 if (strcmp(key, "new_routers") == 0)
186                         inet_aton(value, &ipv4.gateway);
187
188                 if (strcmp(key, "new_network_number") == 0)
189                         inet_aton(value, &ipv4.network);
190
191                 if (strcmp(key, "new_broadcast_address") == 0)
192                         inet_aton(value, &ipv4.broadcast);
193
194                 if (strcmp(key, "new_domain_name_servers") == 0)
195                         inet_aton(value, &ipv4.nameserver);
196
197                 dbus_message_iter_next(&dict);
198         }
199
200         if (strcmp(text, "PREINIT") == 0)
201                 connman_dhcp_update(task->iface,
202                                         CONNMAN_DHCP_STATE_INIT, &ipv4);
203         else if (strcmp(text, "BOUND") == 0 || strcmp(text, "REBOOT") == 0)
204                 connman_dhcp_update(task->iface,
205                                         CONNMAN_DHCP_STATE_BOUND, &ipv4);
206         else if (strcmp(text, "RENEW") == 0 || strcmp(text, "REBIND") == 0)
207                 connman_dhcp_update(task->iface,
208                                         CONNMAN_DHCP_STATE_RENEW, &ipv4);
209         else
210                 connman_dhcp_update(task->iface,
211                                         CONNMAN_DHCP_STATE_FAILED, NULL);
212
213         return NULL;
214 }
215
216 static GDBusMethodTable dhclient_methods[] = {
217         { "notify", "usa{ss}", "", notify_method, G_DBUS_METHOD_FLAG_NOREPLY },
218         { },
219 };
220
221 static DBusConnection *connection;
222
223 static int plugin_init(void)
224 {
225         connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL);
226
227         busname = dbus_bus_get_unique_name(connection);
228
229         g_dbus_register_object(connection, "/org/isc/dhclient", NULL, NULL);
230
231         g_dbus_register_interface(connection, "/org/isc/dhclient",
232                                         "org.isc.dhclient",
233                                         dhclient_methods, NULL, NULL);
234
235         connman_dhcp_register(&dhclient_driver);
236
237         return 0;
238 }
239
240 static void plugin_exit(void)
241 {
242         GSList *list;
243
244         for (list = tasks; list; list = list->next) {
245                 struct dhclient_task *task = list->data;
246                 char pathname[PATH_MAX];
247
248                 printf("[DHCP] killing process %d\n", task->pid);
249
250                 kill(task->pid, SIGTERM);
251
252                 snprintf(pathname, sizeof(pathname) - 1,
253                                 "%s/dhclient.%s.pid", STATEDIR, task->ifname);
254                 unlink(pathname);
255
256                 snprintf(pathname, sizeof(pathname) - 1,
257                                 "%s/dhclient.%s.leases", STATEDIR, task->ifname);
258                 unlink(pathname);
259
260                 __net_free(task->ifname);
261
262                 g_free(task);
263         }
264
265         g_slist_free(tasks);
266
267         connman_dhcp_unregister(&dhclient_driver);
268
269         g_dbus_cleanup_connection(connection);
270 }
271
272 CONNMAN_PLUGIN_DEFINE("dhclient", "ISC DHCP client plugin", VERSION,
273                                                 plugin_init, plugin_exit)