*
* 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
#include <config.h>
#endif
-#include <stdio.h>
-#include <errno.h>
#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-
-#include <glib.h>
-#include <gdbus.h>
+#include <sys/wait.h>
+#include <glib/gstdio.h>
+#define CONNMAN_API_SUBJECT_TO_CHANGE
#include <connman/plugin.h>
-#include <connman/dhcp.h>
+#include <connman/driver.h>
+#include <connman/inet.h>
+#include <connman/dbus.h>
+#include <connman/log.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_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)
{
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->ifindex == index)
static void kill_task(struct dhclient_task *task)
{
- char pathname[PATH_MAX];
+ DBG("task %p name %s pid %d", task, task->ifname, task->pid);
- kill(task->pid, SIGTERM);
+ if (task->pid > 0)
+ kill(task->pid, SIGTERM);
+}
- snprintf(pathname, sizeof(pathname) - 1,
- "%s/dhclient.%s.pid", STATEDIR, task->ifname);
- unlink(pathname);
+static void unlink_task(struct dhclient_task *task)
+{
+ gchar *pathname;
- snprintf(pathname, sizeof(pathname) - 1,
- "%s/dhclient.%s.leases", STATEDIR, task->ifname);
- unlink(pathname);
+ DBG("task %p name %s pid %d", task, task->ifname, task->pid);
- free(task->ifname);
+ pathname = g_strdup_printf("%s/dhclient.%s.pid",
+ STATEDIR, task->ifname);
+ g_unlink(pathname);
+ g_free(pathname);
- g_free(task);
+ pathname = g_strdup_printf("%s/dhclient.%s.leases",
+ STATEDIR, task->ifname);
+ g_unlink(pathname);
+ g_free(pathname);
}
-static int dhclient_request(struct connman_iface *iface)
+static void task_died(GPid pid, gint status, gpointer data)
{
- 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;
+ 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);
- sk = socket(PF_INET, SOCK_DGRAM, 0);
- if (sk < 0)
- return -EIO;
+ g_spawn_close_pid(pid);
+ task->pid = 0;
- memset(&ifr, 0, sizeof(ifr));
- ifr.ifr_ifindex = iface->index;
+ task_list = g_slist_remove(task_list, task);
- err = ioctl(sk, SIOCGIFNAME, &ifr);
+ unlink_task(task);
+
+ 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];
- close(sk);
+ DBG("element %p name %s", element, element->name);
- if (err < 0)
- return -EIO;
+ 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 = connman_inet_ifname(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,
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";
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);
+
+ g_child_watch_add(task->pid, task_died, task);
- printf("[DHCP] executed with pid %d\n", task->pid);
+ 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)
{
struct dhclient_task *task;
- task = find_task_by_index(iface->index);
- if (task == NULL)
- return NULL;
+ 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);
- printf("[DHCP] release %s\n", task->ifname);
+ if (task == NULL)
+ return;
- tasks = g_slist_remove(tasks, task);
+ DBG("release %s", task->ifname);
kill_task(task);
+}
- return 0;
+static void dhclient_change(struct connman_element *element)
+{
+ DBG("element %p name %s", element, element->name);
+
+ if (element->state == CONNMAN_ELEMENT_STATE_ERROR)
+ connman_element_set_error(element->parent,
+ CONNMAN_ELEMENT_ERROR_DHCP_FAILED);
}
-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,
+ .change = dhclient_change,
};
-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);
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);
+
if (task == NULL)
- return NULL;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
dbus_message_iter_recurse(&iter, &dict);
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 if (g_ascii_strcasecmp(text, "FAIL") == 0) {
+ connman_element_set_error(task->element,
+ CONNMAN_ELEMENT_ERROR_FAILED);
+ } 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;
- printf("[DHCP] killing process %d\n", task->pid);
+ DBG("killing process %d", task->pid);
kill_task(task);
+ unlink_task(task);
}
- g_slist_free(tasks);
+ g_slist_free(task_list);
+
+ connman_driver_unregister(&dhclient_driver);
+
+ 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,
+ CONNMAN_PLUGIN_PRIORITY_DEFAULT, dhclient_init, dhclient_exit)