Add experimental DHCP plugin
authorMarcel Holtmann <marcel@holtmann.org>
Mon, 24 Dec 2007 02:04:10 +0000 (03:04 +0100)
committerMarcel Holtmann <marcel@holtmann.org>
Mon, 24 Dec 2007 02:04:10 +0000 (03:04 +0100)
include/dhcp.h
plugins/Makefile.am
plugins/dhclient.c [new file with mode: 0644]
src/dhcp.c

index 337746b..f817e0a 100644 (file)
@@ -28,6 +28,14 @@ extern "C" {
 
 #include <connman/iface.h>
 
+enum connman_dhcp_state {
+       CONNMAN_DHCP_STATE_UNKNOWN = 0,
+       CONNMAN_DHCP_STATE_INIT    = 1,
+       CONNMAN_DHCP_STATE_BOUND   = 2,
+       CONNMAN_DHCP_STATE_RENEW   = 3,
+       CONNMAN_DHCP_STATE_FAILED  = 4,
+};
+
 struct connman_dhcp_driver {
        const char *name;
        int (*request) (struct connman_iface *iface);
@@ -37,6 +45,10 @@ struct connman_dhcp_driver {
 extern int connman_dhcp_register(struct connman_dhcp_driver *driver);
 extern void connman_dhcp_unregister(struct connman_dhcp_driver *driver);
 
+extern int connman_dhcp_update(struct connman_iface *iface,
+                               enum connman_dhcp_state state,
+                                       struct connman_ipv4 *ipv4);
+
 #ifdef __cplusplus
 }
 #endif
index 2d7d5df..a0a387d 100644 (file)
@@ -1,14 +1,29 @@
 
 plugindir = $(libdir)/connman/plugins
 
-plugin_LTLIBRARIES = libconnman-80203.la libconnman-80211.la
+plugin_LTLIBRARIES = libconnman-80203.la libconnman-80211.la \
+                       libconnman-dhclient.la
 
 libconnman_80203_la_SOURCES = 80203.c net.h net.c
 
 libconnman_80211_la_SOURCES = 80211.c net.h net.c
 
+libconnman_dhclient_la_SOURCES = dhclient.c net.h net.c
+libconnman_dhclient_la_LIBADD = @GDBUS_LIBS@
+
 AM_LDFLAGS = -module -avoid-version -export-symbols-regex connman_plugin_desc
 
+statedir = $(localstatedir)/run/connman
+
+if MAINTAINER_MODE
+scriptdir = $(abs_top_srcdir)/scripts
+else
+scriptdir = $(libdir)/connman/scripts
+endif
+
+AM_CFLAGS = @GDBUS_CFLAGS@ \
+               -DSTATEDIR=\""$(statedir)"\" -DSCRIPTDIR=\""$(scriptdir)"\"
+
 INCLUDES = -I$(top_builddir)/include
 
 MAINTAINERCLEANFILES = Makefile.in
diff --git a/plugins/dhclient.c b/plugins/dhclient.c
new file mode 100644 (file)
index 0000000..dbf6e6d
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007  Intel Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gdbus.h>
+
+#include <connman/plugin.h>
+#include <connman/dhcp.h>
+
+#include "net.h"
+
+static const char *busname;
+
+struct dhclient_task {
+       GPid pid;
+       char *ifname;
+       struct connman_iface *iface;
+};
+
+static GSList *tasks = NULL;
+
+static struct dhclient_task *find_task(GPid pid)
+{
+       GSList *list;
+
+       for (list = tasks; list; list = list->next) {
+               struct dhclient_task *task = list->data;
+
+               if (task->pid == pid)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static int dhclient_request(struct connman_iface *iface)
+{
+       struct dhclient_task *task;
+       char *ifname, *argv[16], address[128], pidfile[PATH_MAX];
+       char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX];
+
+       ifname = __net_ifname(iface->sysfs);
+       if (ifname == NULL)
+               return -1;
+
+       task = g_try_new0(struct dhclient_task, 1);
+       if (task == NULL)
+               return -1;
+
+       task->ifname = ifname;
+       task->iface = iface;
+
+       printf("[DHCP] request for %s\n", ifname);
+
+       snprintf(address, sizeof(address) - 1, "BUSNAME=%s", busname);
+       snprintf(pidfile, sizeof(pidfile) - 1,
+                               "%s/dhclient.%s.pid", STATEDIR, ifname);
+       snprintf(leases, sizeof(leases) - 1,
+                               "%s/dhclient.%s.leases", STATEDIR, ifname);
+       snprintf(config, sizeof(config) - 1, "%s/dhclient.conf", SCRIPTDIR);
+       snprintf(script, sizeof(script) - 1, "%s/dhclient-script", SCRIPTDIR);
+
+       argv[0] = "/sbin/dhclient";
+       argv[1] = "-d";
+       argv[2] = "-q";
+       argv[3] = "-n";
+       argv[4] = "-e";
+       argv[5] = address;
+       argv[6] = "-pf";
+       argv[7] = pidfile;
+       argv[8] = "-lf";
+       argv[9] = leases;
+       argv[10] = "-cf";
+       argv[11] = config;
+       argv[12] = "-sf";
+       argv[13] = script;
+       argv[14] = ifname;
+       argv[15] = NULL;
+
+       if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
+                               NULL, NULL, &task->pid, NULL) == FALSE) {
+               printf("Failed to spawn dhclient\n");
+               return -1;
+       }
+
+       tasks = g_slist_append(tasks, task);
+
+       printf("[DHCP] executed with pid %d\n", task->pid);
+
+       return 0;
+}
+
+static int dhclient_release(struct connman_iface *iface)
+{
+       char *ifname;
+
+       ifname = __net_ifname(iface->sysfs);
+       if (ifname == NULL)
+               return -1;
+
+       printf("[DHCP] release for %s\n", ifname);
+
+       __net_free(ifname);
+
+       return 0;
+}
+
+static struct connman_dhcp_driver dhclient_driver = {
+       .name           = "dhclient",
+       .request        = dhclient_request,
+       .release        = dhclient_release,
+};
+
+static DBusMessage *notify_method(DBusConnection *conn,
+                                       DBusMessage *msg, void *data)
+{
+       DBusMessageIter iter, dict;
+       dbus_uint32_t pid;
+       struct dhclient_task *task;
+       struct connman_ipv4 ipv4;
+       const char *text, *key, *value;
+
+       memset(&ipv4, 0, sizeof(ipv4));
+
+       dbus_message_iter_init(msg, &iter);
+
+       dbus_message_iter_get_basic(&iter, &pid);
+       dbus_message_iter_next(&iter);
+
+       dbus_message_iter_get_basic(&iter, &text);
+       dbus_message_iter_next(&iter);
+
+       printf("[DHCP] change %d to %s\n", pid, text);
+
+       task = find_task(pid);
+       if (task == NULL)
+               return NULL;
+
+       dbus_message_iter_recurse(&iter, &dict);
+
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               dbus_message_iter_get_basic(&entry, &key);
+               dbus_message_iter_next(&entry);
+               dbus_message_iter_get_basic(&entry, &value);
+
+               printf("[DHCP] %s = %s\n", key, value);
+
+               if (strcmp(key, "new_ip_address") == 0)
+                       inet_aton(value, &ipv4.address);
+
+               if (strcmp(key, "new_subnet_mask") == 0)
+                       inet_aton(value, &ipv4.netmask);
+
+               if (strcmp(key, "new_routers") == 0)
+                       inet_aton(value, &ipv4.gateway);
+
+               if (strcmp(key, "new_network_number") == 0)
+                       inet_aton(value, &ipv4.network);
+
+               if (strcmp(key, "new_broadcast_address") == 0)
+                       inet_aton(value, &ipv4.broadcast);
+
+               if (strcmp(key, "new_domain_name_servers") == 0)
+                       inet_aton(value, &ipv4.nameserver);
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (strcmp(text, "PREINIT") == 0)
+               connman_dhcp_update(task->iface,
+                                       CONNMAN_DHCP_STATE_INIT, &ipv4);
+       else if (strcmp(text, "BOUND") == 0 || strcmp(text, "REBOOT") == 0)
+               connman_dhcp_update(task->iface,
+                                       CONNMAN_DHCP_STATE_BOUND, &ipv4);
+       else if (strcmp(text, "RENEW") == 0 || strcmp(text, "REBIND") == 0)
+               connman_dhcp_update(task->iface,
+                                       CONNMAN_DHCP_STATE_RENEW, &ipv4);
+       else
+               connman_dhcp_update(task->iface,
+                                       CONNMAN_DHCP_STATE_FAILED, NULL);
+
+       return NULL;
+}
+
+static GDBusMethodTable dhclient_methods[] = {
+       { "notify", "usa{ss}", "", notify_method, G_DBUS_METHOD_FLAG_NOREPLY },
+       { },
+};
+
+static DBusConnection *connection;
+
+static int plugin_init(void)
+{
+       connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL);
+
+       busname = dbus_bus_get_unique_name(connection);
+
+       g_dbus_register_object(connection, "/org/isc/dhclient", NULL, NULL);
+
+       g_dbus_register_interface(connection, "/org/isc/dhclient",
+                                       "org.isc.dhclient",
+                                       dhclient_methods, NULL, NULL);
+
+       connman_dhcp_register(&dhclient_driver);
+
+       return 0;
+}
+
+static void plugin_exit(void)
+{
+       GSList *list;
+
+       for (list = tasks; list; list = list->next) {
+               struct dhclient_task *task = list->data;
+               char pathname[PATH_MAX];
+
+               printf("[DHCP] killing process %d\n", task->pid);
+
+               kill(task->pid, SIGTERM);
+
+               snprintf(pathname, sizeof(pathname) - 1,
+                               "%s/dhclient.%s.pid", STATEDIR, task->ifname);
+               unlink(pathname);
+
+               snprintf(pathname, sizeof(pathname) - 1,
+                               "%s/dhclient.%s.leases", STATEDIR, task->ifname);
+               unlink(pathname);
+
+               __net_free(task->ifname);
+
+               g_free(task);
+       }
+
+       g_slist_free(tasks);
+
+       connman_dhcp_unregister(&dhclient_driver);
+
+       g_dbus_cleanup_connection(connection);
+}
+
+CONNMAN_PLUGIN_DEFINE("dhclient", "ISC DHCP client plugin", VERSION,
+                                               plugin_init, plugin_exit)
index 5a6e5c9..8af6dab 100644 (file)
@@ -23,6 +23,8 @@
 #include <config.h>
 #endif
 
+#include <arpa/inet.h>
+
 #include <glib.h>
 
 #include "connman.h"
@@ -45,6 +47,29 @@ void connman_dhcp_unregister(struct connman_dhcp_driver *driver)
        drivers = g_slist_remove(drivers, driver);
 }
 
+int connman_dhcp_update(struct connman_iface *iface,
+                               enum connman_dhcp_state state,
+                                       struct connman_ipv4 *ipv4)
+{
+       DBG("iface %p state %d", iface, state);
+
+       if (state == CONNMAN_DHCP_STATE_BOUND) {
+               DBG("address %s", inet_ntoa(ipv4->address));
+               DBG("netmask %s", inet_ntoa(ipv4->netmask));
+               DBG("gateway %s", inet_ntoa(ipv4->gateway));
+               DBG("network %s", inet_ntoa(ipv4->network));
+               DBG("broadcast %s", inet_ntoa(ipv4->broadcast));
+               DBG("nameserver %s", inet_ntoa(ipv4->nameserver));
+
+               if (iface->driver->set_ipv4) {
+                       iface->driver->set_ipv4(iface, ipv4);
+                       iface->ipv4 = *ipv4;
+               }
+       }
+
+       return 0;
+}
+
 int __connman_dhcp_request(struct connman_iface *iface)
 {
        struct connman_dhcp_driver *driver = g_slist_nth_data(drivers, 0);