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