Update copyright information
[framework/connectivity/connman.git] / plugins / dhclient.c
index dbf6e6d..612d02a 100644 (file)
@@ -2,7 +2,7 @@
  *
  *  Connection Manager
  *
- *  Copyright (C) 2007  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2008  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
 #endif
 
 #include <stdio.h>
+#include <errno.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include <signal.h>
 #include <string.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
 #include <arpa/inet.h>
+#include <net/if.h>
 
 #include <glib.h>
 #include <gdbus.h>
 #include <connman/plugin.h>
 #include <connman/dhcp.h>
 
-#include "net.h"
+#define DHCLIENT_INTF "org.isc.dhclient"
+#define DHCLIENT_PATH "/org/isc/dhclient"
 
 static const char *busname;
 
 struct dhclient_task {
        GPid pid;
+       int ifindex;
        char *ifname;
        struct connman_iface *iface;
 };
 
 static GSList *tasks = NULL;
 
-static struct dhclient_task *find_task(GPid pid)
+static struct dhclient_task *find_task_by_pid(GPid pid)
 {
        GSList *list;
 
@@ -61,34 +69,109 @@ static struct dhclient_task *find_task(GPid pid)
        return NULL;
 }
 
+static struct dhclient_task *find_task_by_index(int index)
+{
+       GSList *list;
+
+       for (list = tasks; list; list = list->next) {
+               struct dhclient_task *task = list->data;
+
+               if (task->ifindex == index)
+                       return task;
+       }
+
+       return NULL;
+}
+
+static void kill_task(struct dhclient_task *task)
+{
+       if (task->pid > 0)
+               kill(task->pid, SIGTERM);
+}
+
+static void task_died(GPid pid, gint status, gpointer data)
+{
+       struct dhclient_task *task = data;
+       char pathname[PATH_MAX];
+
+       if (WIFEXITED(status))
+               printf("[DHCP] exit status %d for %s\n",
+                                       WEXITSTATUS(status), task->ifname);
+       else
+               printf("[DHCP] signal %d killed %s\n",
+                                       WTERMSIG(status), task->ifname);
+
+       g_spawn_close_pid(pid);
+       task->pid = 0;
+
+       tasks = g_slist_remove(tasks, task);
+
+       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);
+
+       free(task->ifname);
+
+       g_free(task);
+}
+
+static void task_setup(gpointer data)
+{
+       struct dhclient_task *task = data;
+
+       printf("[DHCP] setup %s\n", task->ifname);
+}
+
 static int dhclient_request(struct connman_iface *iface)
 {
+       struct ifreq ifr;
        struct dhclient_task *task;
-       char *ifname, *argv[16], address[128], pidfile[PATH_MAX];
+       char *argv[16], *envp[1], address[128], pidfile[PATH_MAX];
        char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX];
+       int sk, err;
 
-       ifname = __net_ifname(iface->sysfs);
-       if (ifname == NULL)
-               return -1;
+       sk = socket(PF_INET, SOCK_DGRAM, 0);
+       if (sk < 0)
+               return -EIO;
+
+       memset(&ifr, 0, sizeof(ifr));
+       ifr.ifr_ifindex = iface->index;
+
+       err = ioctl(sk, SIOCGIFNAME, &ifr);
+
+       close(sk);
+
+       if (err < 0)
+               return -EIO;
 
        task = g_try_new0(struct dhclient_task, 1);
        if (task == NULL)
-               return -1;
+               return -ENOMEM;
 
-       task->ifname = ifname;
+       task->ifindex = iface->index;
+       task->ifname = strdup(ifr.ifr_name);
        task->iface = iface;
 
-       printf("[DHCP] request for %s\n", ifname);
+       if (task->ifname == NULL) {
+               g_free(task);
+               return -ENOMEM;
+       }
+
+       printf("[DHCP] request %s\n", task->ifname);
 
        snprintf(address, sizeof(address) - 1, "BUSNAME=%s", busname);
        snprintf(pidfile, sizeof(pidfile) - 1,
-                               "%s/dhclient.%s.pid", STATEDIR, ifname);
+                       "%s/dhclient.%s.pid", STATEDIR, task->ifname);
        snprintf(leases, sizeof(leases) - 1,
-                               "%s/dhclient.%s.leases", STATEDIR, ifname);
+                       "%s/dhclient.%s.leases", STATEDIR, task->ifname);
        snprintf(config, sizeof(config) - 1, "%s/dhclient.conf", SCRIPTDIR);
        snprintf(script, sizeof(script) - 1, "%s/dhclient-script", SCRIPTDIR);
 
-       argv[0] = "/sbin/dhclient";
+       argv[0] = DHCLIENT;
        argv[1] = "-d";
        argv[2] = "-q";
        argv[3] = "-n";
@@ -102,33 +185,39 @@ static int dhclient_request(struct connman_iface *iface)
        argv[11] = config;
        argv[12] = "-sf";
        argv[13] = script;
-       argv[14] = ifname;
+       argv[14] = task->ifname;
        argv[15] = NULL;
 
-       if (g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
-                               NULL, NULL, &task->pid, NULL) == FALSE) {
+       envp[0] = NULL;
+
+       if (g_spawn_async(NULL, argv, envp, G_SPAWN_DO_NOT_REAP_CHILD,
+                               task_setup, task, &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);
+       g_child_watch_add(task->pid, task_died, task);
+
+       printf("[DHCP] executed %s with pid %d\n", DHCLIENT, task->pid);
 
        return 0;
 }
 
 static int dhclient_release(struct connman_iface *iface)
 {
-       char *ifname;
+       struct dhclient_task *task;
 
-       ifname = __net_ifname(iface->sysfs);
-       if (ifname == NULL)
-               return -1;
+       task = find_task_by_index(iface->index);
+       if (task == NULL)
+               return -ENODEV;
 
-       printf("[DHCP] release for %s\n", ifname);
+       printf("[DHCP] release %s\n", task->ifname);
 
-       __net_free(ifname);
+       tasks = g_slist_remove(tasks, task);
+
+       kill_task(task);
 
        return 0;
 }
@@ -139,8 +228,8 @@ static struct connman_dhcp_driver dhclient_driver = {
        .release        = dhclient_release,
 };
 
-static DBusMessage *notify_method(DBusConnection *conn,
-                                       DBusMessage *msg, void *data)
+static DBusHandlerResult dhclient_filter(DBusConnection *conn,
+                                               DBusMessage *msg, void *data)
 {
        DBusMessageIter iter, dict;
        dbus_uint32_t pid;
@@ -148,6 +237,9 @@ static DBusMessage *notify_method(DBusConnection *conn,
        struct connman_ipv4 ipv4;
        const char *text, *key, *value;
 
+       if (dbus_message_is_method_call(msg, DHCLIENT_INTF, "notify") == FALSE)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
        memset(&ipv4, 0, sizeof(ipv4));
 
        dbus_message_iter_init(msg, &iter);
@@ -160,9 +252,9 @@ static DBusMessage *notify_method(DBusConnection *conn,
 
        printf("[DHCP] change %d to %s\n", pid, text);
 
-       task = find_task(pid);
+       task = find_task_by_pid(pid);
        if (task == NULL)
-               return NULL;
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
        dbus_message_iter_recurse(&iter, &dict);
 
@@ -210,27 +302,29 @@ static DBusMessage *notify_method(DBusConnection *conn,
                connman_dhcp_update(task->iface,
                                        CONNMAN_DHCP_STATE_FAILED, NULL);
 
-       return NULL;
+       return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-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);
+       gchar *filter;
+
+       connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
 
        busname = dbus_bus_get_unique_name(connection);
 
-       g_dbus_register_object(connection, "/org/isc/dhclient", NULL, NULL);
+       busname = "org.freedesktop.connman";
+
+       dbus_connection_add_filter(connection, dhclient_filter, NULL, NULL);
+
+       filter = g_strdup_printf("interface=%s,path=%s",
+                                               DHCLIENT_INTF, DHCLIENT_PATH);
 
-       g_dbus_register_interface(connection, "/org/isc/dhclient",
-                                       "org.isc.dhclient",
-                                       dhclient_methods, NULL, NULL);
+       dbus_bus_add_match(connection, filter, NULL);
+
+       g_free(filter);
 
        connman_dhcp_register(&dhclient_driver);
 
@@ -243,30 +337,17 @@ static void plugin_exit(void)
 
        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);
+               kill_task(task);
        }
 
        g_slist_free(tasks);
 
        connman_dhcp_unregister(&dhclient_driver);
 
-       g_dbus_cleanup_connection(connection);
+       dbus_connection_unref(connection);
 }
 
 CONNMAN_PLUGIN_DEFINE("dhclient", "ISC DHCP client plugin", VERSION,