X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=plugins%2Fdhclient.c;h=cb0e8753d4c85c00ac1aa6fd4aa91d34ce8b137a;hb=ff0c50794b063e961775fc8fbe4fe7277bd7102d;hp=8506c633a492f41f3a7162984fbf3678622d2f63;hpb=921f79feea506fd3e44ea980c6aad04c9aac7955;p=connman diff --git a/plugins/dhclient.c b/plugins/dhclient.c index 8506c63..cb0e875 100644 --- a/plugins/dhclient.c +++ b/plugins/dhclient.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007 Intel Corporation. All rights reserved. + * Copyright (C) 2007-2009 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 @@ -23,39 +23,37 @@ #include #endif -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include +#include +#define CONNMAN_API_SUBJECT_TO_CHANGE #include -#include +#include +#include +#include + +#include "inet.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; + gchar *ifname; + struct connman_element *element; }; -static GSList *tasks = NULL; +static GSList *task_list = NULL; -static struct dhclient_task *find_task(GPid pid) +static struct dhclient_task *find_task_by_pid(GPid pid) { GSList *list; - for (list = tasks; list; list = list->next) { + for (list = task_list; list; list = list->next) { struct dhclient_task *task = list->data; if (task->pid == pid) @@ -65,42 +63,97 @@ static struct dhclient_task *find_task(GPid pid) return NULL; } -static int dhclient_request(struct connman_iface *iface) +static struct dhclient_task *find_task_by_index(int index) { - struct ifreq ifr; - struct dhclient_task *task; - char *argv[16], address[128], pidfile[PATH_MAX]; - char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX]; - int sk, err; + GSList *list; + + for (list = task_list; 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) +{ + DBG("task %p name %s pid %d", task, task->ifname, task->pid); - sk = socket(PF_INET, SOCK_DGRAM, 0); - if (sk < 0) - return -EIO; + if (task->pid > 0) + kill(task->pid, SIGTERM); +} + +static void unlink_task(struct dhclient_task *task) +{ + gchar *pathname; + + DBG("task %p name %s pid %d", task, task->ifname, task->pid); + + pathname = g_strdup_printf("%s/dhclient.%s.pid", + STATEDIR, task->ifname); + g_unlink(pathname); + g_free(pathname); + + pathname = g_strdup_printf("%s/dhclient.%s.leases", + STATEDIR, task->ifname); + g_unlink(pathname); + g_free(pathname); +} - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_ifindex = iface->index; +static void task_died(GPid pid, gint status, gpointer data) +{ + struct dhclient_task *task = data; + + if (WIFEXITED(status)) + DBG("exit status %d for %s", WEXITSTATUS(status), task->ifname); + else + DBG("signal %d killed %s", WTERMSIG(status), task->ifname); + + g_spawn_close_pid(pid); + task->pid = 0; - err = ioctl(sk, SIOCGIFNAME, &ifr); + task_list = g_slist_remove(task_list, task); - close(sk); + unlink_task(task); - if (err < 0) - return -EIO; + g_free(task->ifname); + g_free(task); +} + +static void task_setup(gpointer data) +{ + struct dhclient_task *task = data; + + DBG("task %p name %s", task, task->ifname); +} + +static int dhclient_probe(struct connman_element *element) +{ + struct dhclient_task *task; + char *argv[16], *envp[1], address[128], pidfile[PATH_MAX]; + char leases[PATH_MAX], config[PATH_MAX], script[PATH_MAX]; + + DBG("element %p name %s", element, element->name); + + if (access(DHCLIENT, X_OK) < 0) + return -errno; task = g_try_new0(struct dhclient_task, 1); if (task == NULL) return -ENOMEM; - task->ifindex = iface->index; - task->ifname = strdup(ifr.ifr_name); - task->iface = iface; + task->ifindex = element->index; + task->ifname = inet_index2name(element->index); + task->element = element; if (task->ifname == NULL) { g_free(task); return -ENOMEM; } - printf("[DHCP] request %s\n", task->ifname); + DBG("request %s", task->ifname); snprintf(address, sizeof(address) - 1, "BUSNAME=%s", busname); snprintf(pidfile, sizeof(pidfile) - 1, @@ -110,7 +163,7 @@ static int dhclient_request(struct connman_iface *iface) 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"; @@ -127,42 +180,58 @@ static int dhclient_request(struct connman_iface *iface) 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) { - printf("Failed to spawn dhclient\n"); + envp[0] = NULL; + + if (g_spawn_async(NULL, argv, envp, G_SPAWN_DO_NOT_REAP_CHILD, + task_setup, task, &task->pid, NULL) == FALSE) { + connman_error("Failed to spawn dhclient"); return -1; } - tasks = g_slist_append(tasks, task); + task_list = g_slist_append(task_list, task); - printf("[DHCP] executed with pid %d\n", task->pid); + g_child_watch_add(task->pid, task_died, task); + + DBG("executed %s with pid %d", DHCLIENT, task->pid); return 0; } -static int dhclient_release(struct connman_iface *iface) +static void dhclient_remove(struct connman_element *element) { - printf("[DHCP] release\n"); + struct dhclient_task *task; - return 0; + DBG("element %p name %s", element, element->name); + + task = find_task_by_index(element->index); + if (task != NULL) + task_list = g_slist_remove(task_list, task); + + if (task == NULL) + return; + + DBG("release %s", task->ifname); + + kill_task(task); } -static struct connman_dhcp_driver dhclient_driver = { +static struct connman_driver dhclient_driver = { .name = "dhclient", - .request = dhclient_request, - .release = dhclient_release, + .type = CONNMAN_ELEMENT_TYPE_DHCP, + .probe = dhclient_probe, + .remove = dhclient_remove, }; -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; struct dhclient_task *task; - struct connman_ipv4 ipv4; const char *text, *key, *value; - memset(&ipv4, 0, sizeof(ipv4)); + if (dbus_message_is_method_call(msg, DHCLIENT_INTF, "notify") == FALSE) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; dbus_message_iter_init(msg, &iter); @@ -172,11 +241,12 @@ static DBusMessage *notify_method(DBusConnection *conn, dbus_message_iter_get_basic(&iter, &text); dbus_message_iter_next(&iter); - printf("[DHCP] change %d to %s\n", pid, text); + DBG("change %d to %s", pid, text); + + task = find_task_by_pid(pid); - task = find_task(pid); if (task == NULL) - return NULL; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; dbus_message_iter_recurse(&iter, &dict); @@ -188,100 +258,110 @@ static DBusMessage *notify_method(DBusConnection *conn, dbus_message_iter_next(&entry); dbus_message_iter_get_basic(&entry, &value); - printf("[DHCP] %s = %s\n", key, value); + DBG("%s = %s", key, value); - if (strcmp(key, "new_ip_address") == 0) - inet_aton(value, &ipv4.address); + if (g_ascii_strcasecmp(key, "new_ip_address") == 0) { + g_free(task->element->ipv4.address); + task->element->ipv4.address = g_strdup(value); + } - if (strcmp(key, "new_subnet_mask") == 0) - inet_aton(value, &ipv4.netmask); + if (g_ascii_strcasecmp(key, "new_subnet_mask") == 0) { + g_free(task->element->ipv4.netmask); + task->element->ipv4.netmask = g_strdup(value); + } - if (strcmp(key, "new_routers") == 0) - inet_aton(value, &ipv4.gateway); + if (g_ascii_strcasecmp(key, "new_routers") == 0) { + g_free(task->element->ipv4.gateway); + task->element->ipv4.gateway = g_strdup(value); + } - if (strcmp(key, "new_network_number") == 0) - inet_aton(value, &ipv4.network); + if (g_ascii_strcasecmp(key, "new_network_number") == 0) { + g_free(task->element->ipv4.network); + task->element->ipv4.network = g_strdup(value); + } - if (strcmp(key, "new_broadcast_address") == 0) - inet_aton(value, &ipv4.broadcast); + if (g_ascii_strcasecmp(key, "new_broadcast_address") == 0) { + g_free(task->element->ipv4.broadcast); + task->element->ipv4.broadcast = g_strdup(value); + } - if (strcmp(key, "new_domain_name_servers") == 0) - inet_aton(value, &ipv4.nameserver); + if (g_ascii_strcasecmp(key, "new_domain_name_servers") == 0) { + g_free(task->element->ipv4.nameserver); + task->element->ipv4.nameserver = g_strdup(value); + } 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); + if (g_ascii_strcasecmp(text, "PREINIT") == 0) { + } else if (g_ascii_strcasecmp(text, "BOUND") == 0 || + g_ascii_strcasecmp(text, "REBOOT") == 0) { + struct connman_element *element; + element = connman_element_create(NULL); + element->type = CONNMAN_ELEMENT_TYPE_IPV4; + element->index = task->ifindex; + connman_element_update(task->element); + if (connman_element_register(element, task->element) < 0) + connman_element_unref(element); + } else if (g_ascii_strcasecmp(text, "RENEW") == 0 || + g_ascii_strcasecmp(text, "REBIND") == 0) { + connman_element_update(task->element); + } else { + } - 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) +static const char *dhclient_rule = "path=" DHCLIENT_PATH + ",interface=" DHCLIENT_INTF; + +static int dhclient_init(void) { - connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL); + int err; + + connection = connman_dbus_get_connection(); busname = dbus_bus_get_unique_name(connection); + busname = CONNMAN_SERVICE; - g_dbus_register_object(connection, "/org/isc/dhclient", NULL, NULL); + dbus_connection_add_filter(connection, dhclient_filter, NULL, NULL); - g_dbus_register_interface(connection, "/org/isc/dhclient", - "org.isc.dhclient", - dhclient_methods, NULL, NULL); + dbus_bus_add_match(connection, dhclient_rule, NULL); - connman_dhcp_register(&dhclient_driver); + err = connman_driver_register(&dhclient_driver); + if (err < 0) { + dbus_connection_unref(connection); + return err; + } return 0; } -static void plugin_exit(void) +static void dhclient_exit(void) { GSList *list; - for (list = tasks; list; list = list->next) { + for (list = task_list; 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); + DBG("killing process %d", task->pid); - snprintf(pathname, sizeof(pathname) - 1, - "%s/dhclient.%s.leases", STATEDIR, task->ifname); - unlink(pathname); + kill_task(task); + unlink_task(task); + } - free(task->ifname); + g_slist_free(task_list); - g_free(task); - } + connman_driver_unregister(&dhclient_driver); - g_slist_free(tasks); + dbus_bus_remove_match(connection, dhclient_rule, NULL); - connman_dhcp_unregister(&dhclient_driver); + dbus_connection_remove_filter(connection, dhclient_filter, NULL); - g_dbus_cleanup_connection(connection); + dbus_connection_unref(connection); } -CONNMAN_PLUGIN_DEFINE("dhclient", "ISC DHCP client plugin", VERSION, - plugin_init, plugin_exit) +CONNMAN_PLUGIN_DEFINE(dhclient, "ISC DHCP client plugin", VERSION, + dhclient_init, dhclient_exit)