| /* |
| * |
| * Connection Manager |
| * |
| * 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 |
| * 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 <unistd.h> |
| #include <sys/wait.h> |
| #include <glib/gstdio.h> |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <arpa/inet.h> |
| #include <net/if.h> |
| #include <net/route.h> |
| |
| #define CONNMAN_API_SUBJECT_TO_CHANGE |
| #include <connman/plugin.h> |
| #include <connman/element.h> |
| #include <connman/ipconfig.h> |
| #include <connman/inet.h> |
| #include <connman/dbus.h> |
| #include <connman/log.h> |
| #include <connman/profile.h> |
| #include <connman/resolver.h> |
| |
| #define _DBG_DHCPCD(fmt, arg...) DBG(DBG_DHCLIENT, fmt, ## arg) |
| |
| #define DHCPCD_INTF "org.chromium.dhcpcd" |
| #define DHCPCD_PATH "/org/chromium/dhcpcd" |
| |
| #define MAXDNS 10 /* max DNS servers per lease */ |
| #define MAXDOMAINS 10 /* max DNS search domains per lease */ |
| #define ADDR_SIZE sizeof("255.255.255.255") /* NB: includes \0 */ |
| |
| /* TODO(sleffler) rewrite using task support */ |
| struct dhcpcd_task { |
| GPid pid; |
| char *dbus_address; |
| gboolean killed; |
| struct connman_ipconfig *ipconfig; |
| }; |
| |
| static GSList *task_list = NULL; |
| |
| static DBusConnection *connection; |
| |
| static int dhcpcd_renew(struct connman_ipconfig *ipconfig); |
| |
| static struct dhcpcd_task *find_task_by_pid(GPid pid) |
| { |
| GSList *list; |
| |
| for (list = task_list; list; list = list->next) { |
| struct dhcpcd_task *task = list->data; |
| if (task->pid == pid) |
| return task; |
| } |
| return NULL; |
| } |
| |
| static struct dhcpcd_task *find_task_by_ipconfig( |
| const struct connman_ipconfig *ipconfig) |
| { |
| GSList *list; |
| |
| for (list = task_list; list; list = list->next) { |
| struct dhcpcd_task *task = list->data; |
| if (task->ipconfig == ipconfig && task->killed == FALSE) |
| return task; |
| } |
| return NULL; |
| } |
| |
| static void kill_task(struct dhcpcd_task *task) |
| { |
| const char *ifname = connman_ipconfig_get_ifname(task->ipconfig); |
| |
| _DBG_DHCPCD("task %p name %s pid %d", task, ifname, task->pid); |
| |
| if (task->killed == FALSE && task->pid > 0) { |
| if (kill(task->pid, SIGTERM) < 0 && errno != ESRCH) { |
| connman_error("dhcpcd(%s): kill pid %d: %s", |
| ifname, task->pid, strerror(errno)); |
| /* TODO(sleffler) send SIGKILL? */ |
| } |
| task->killed = TRUE; |
| } |
| } |
| |
| static void unlink_task(struct dhcpcd_task *task) |
| { |
| const char *ifname = connman_ipconfig_get_ifname(task->ipconfig); |
| gchar *pathname; |
| |
| _DBG_DHCPCD("task %p name %s pid %d", task, ifname, task->pid); |
| |
| pathname = g_strdup_printf("/var/run/dhcpcd/dhcpcd-%s.pid", ifname); |
| g_unlink(pathname); |
| g_free(pathname); |
| |
| pathname = g_strdup_printf("/var/lib/dhcpcd/dhcpcd-%s.lease", ifname); |
| g_unlink(pathname); |
| g_free(pathname); |
| } |
| |
| static int start_dhcpcd(struct dhcpcd_task *task); |
| |
| static void task_died(GPid pid, gint status, gpointer data) |
| { |
| struct dhcpcd_task *task = data; |
| |
| if (WIFEXITED(status)) |
| _DBG_DHCPCD("exit status %d for %s", WEXITSTATUS(status), |
| connman_ipconfig_get_ifname(task->ipconfig)); |
| else |
| _DBG_DHCPCD("signal %d killed %s", WTERMSIG(status), |
| connman_ipconfig_get_ifname(task->ipconfig)); |
| |
| g_spawn_close_pid(pid); |
| task->pid = 0; |
| |
| task_list = g_slist_remove(task_list, task); |
| |
| unlink_task(task); |
| g_free(task->dbus_address); |
| |
| g_free(task); |
| } |
| |
| static void task_setup(gpointer data) |
| { |
| struct dhcpcd_task *task = data; |
| |
| _DBG_DHCPCD("task %p name %s", task, |
| connman_ipconfig_get_ifname(task->ipconfig)); |
| |
| task->killed = FALSE; |
| } |
| |
| static int start_dhcpcd(struct dhcpcd_task *task) |
| { |
| const char *ifname = connman_ipconfig_get_ifname(task->ipconfig); |
| char *argv[5], *envp[1]; |
| |
| argv[0] = DHCPCD; |
| argv[1] = "-B"; /* foreground */ |
| argv[2] = (char *) ifname; /* TODO(sleffler) __DECONST */ |
| if (connman_profile_get_arpgateway()) { |
| argv[3] = "-R"; /* ARP for default gateway */ |
| argv[4] = NULL; |
| } else |
| argv[3] = NULL; |
| |
| 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("%s: failed to spawn %s for %s", |
| __func__, DHCPCD, ifname); |
| return -1; |
| } |
| |
| task_list = g_slist_append(task_list, task); |
| |
| g_child_watch_add(task->pid, task_died, task); |
| |
| _DBG_DHCPCD("spawn %s with pid %d", DHCPCD, task->pid); |
| |
| return 0; |
| } |
| |
| static int dhcpcd_request(struct connman_ipconfig *ipconfig) |
| { |
| struct dhcpcd_task *task; |
| const char *ifname; |
| |
| ifname = connman_ipconfig_get_ifname(ipconfig); |
| _DBG_DHCPCD("request %s ipconfig %p", ifname, ipconfig); |
| |
| task = find_task_by_ipconfig(ipconfig); |
| if (task != NULL) { |
| if (task->dbus_address != NULL) { |
| /* |
| * Just ask the existing process to renew it's current |
| * lease. If that fails it will fallback quicky to |
| * requesting a new lease. |
| */ |
| return dhcpcd_renew(ipconfig); |
| } else { |
| /* |
| * We are in an uncomfortable situation where |
| * we have a dhcpcd task running, but we don't |
| * (yet) have a dbus handle for it. Kill this |
| * (since we do know its PID), and start a new |
| * one. |
| */ |
| kill_task(task); |
| } |
| } |
| |
| if (ifname == NULL) |
| return -EINVAL; |
| |
| task = g_try_new0(struct dhcpcd_task, 1); |
| if (task == NULL) |
| return -ENOMEM; |
| task->ipconfig = ipconfig; |
| |
| return start_dhcpcd(task); |
| } |
| |
| static int dhcpcd_renew(struct connman_ipconfig *ipconfig) |
| { |
| DBusMessage *message, *reply; |
| DBusError error; |
| const char *ifname = connman_ipconfig_get_ifname(ipconfig); |
| struct dhcpcd_task *task; |
| |
| _DBG_DHCPCD("renew %s", ifname); |
| |
| if (ifname == NULL) |
| return -EINVAL; |
| |
| task = find_task_by_ipconfig(ipconfig); |
| if (task == NULL) |
| return -EINVAL; /* XXX */ |
| |
| if (task->dbus_address == NULL) |
| return -EINVAL; |
| |
| message = dbus_message_new_method_call(task->dbus_address, DHCPCD_PATH, |
| DHCPCD_INTF, "Rebind"); |
| if (message == NULL) |
| return -ENOMEM; |
| |
| dbus_error_init(&error); |
| |
| /* TODO(njw): If dhcpcd changes to synchronously perform the |
| operation before returning, this will hang until it's done, |
| so it might be better to use the nonblocking call here. */ |
| reply = dbus_connection_send_with_reply_and_block(connection, message, |
| -1, &error); |
| if (reply == NULL) { |
| if (dbus_error_is_set(&error) == TRUE) { |
| connman_error("%s: %s", __func__, error.message); |
| dbus_error_free(&error); |
| } else |
| connman_error("%s: failed", __func__); |
| dbus_message_unref(message); |
| return -EIO; |
| } |
| |
| dbus_message_unref(message); |
| |
| return 0; |
| } |
| |
| static int dhcpcd_release(struct connman_ipconfig *ipconfig) |
| { |
| DBusMessage *message, *reply; |
| DBusError error; |
| const char *ifname = connman_ipconfig_get_ifname(ipconfig); |
| struct dhcpcd_task *task; |
| |
| _DBG_DHCPCD("ipconfig %p ifname %s", ipconfig, ifname); |
| |
| task = find_task_by_ipconfig(ipconfig); |
| if (task == NULL) |
| return -EINVAL; |
| |
| if (task->dbus_address == NULL) { |
| kill_task(task); |
| return -EINVAL; |
| } |
| |
| message = dbus_message_new_method_call(task->dbus_address, DHCPCD_PATH, |
| DHCPCD_INTF, "Release"); |
| if (message == NULL) |
| return -ENOMEM; |
| |
| dbus_error_init(&error); |
| |
| /* TODO(njw): See dhcpcd_renew() */ |
| reply = dbus_connection_send_with_reply_and_block(connection, message, |
| -1, &error); |
| if (reply == NULL) { |
| if (dbus_error_is_set(&error) == TRUE) { |
| connman_error("%s: %s", __func__, error.message); |
| dbus_error_free(&error); |
| } else |
| connman_error("%s: failed", __func__); |
| dbus_message_unref(message); |
| return -EIO; |
| } |
| |
| dbus_message_unref(message); |
| |
| kill_task(task); |
| |
| return 0; |
| } |
| |
| static struct connman_ipconfig_driver dhcpcd_driver = { |
| .name = "dhcpcd", |
| .type = CONNMAN_IPCONFIG_TYPE_DHCP, |
| .priority = CONNMAN_IPCONFIG_PRIORITY_DEFAULT, |
| .request = dhcpcd_request, |
| .release = dhcpcd_release, |
| .renew = dhcpcd_renew, |
| }; |
| |
| static DBusHandlerResult bad_type(const char *key, int type_expect, int type_got) |
| { |
| connman_error("dhcpcd_filter: bad d-bus type for key %s: " |
| "got 0x%x, expected 0x%x", key, type_got, type_expect); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| static DBusHandlerResult bad_array_size(const char *key, int count, int max) |
| { |
| connman_error("dhcpcd_filter: bad d-bus array size for key %s: " |
| "got %d elements, max %d", key, count, max); |
| return DBUS_HANDLER_RESULT_HANDLED; |
| } |
| |
| /* |
| * D-Bus helper functions for parsing basic values enclosed in a variant. |
| */ |
| |
| static int _get_basic(DBusMessageIter *iter, const char *key, |
| int basic_type, void *value) |
| { |
| DBusMessageIter entry; |
| int type; |
| |
| dbus_message_iter_recurse(iter, &entry); |
| type = dbus_message_iter_get_arg_type(&entry); |
| if (type != basic_type) { |
| (void) bad_type(key, basic_type, type); |
| return FALSE; |
| } else { |
| dbus_message_iter_get_basic(&entry, value); |
| return TRUE; |
| } |
| } |
| static int get_byte(DBusMessageIter *iter, const char *key, uint8_t *value) |
| { |
| int status = _get_basic(iter, key, DBUS_TYPE_BYTE, value); |
| _DBG_DHCPCD("%s = 0x%x (byte)", key, *value); |
| return status; |
| } |
| static int get_uint16(DBusMessageIter *iter, const char *key, uint16_t *value) |
| { |
| int status = _get_basic(iter, key, DBUS_TYPE_UINT16, value); |
| _DBG_DHCPCD("%s = 0x%x (uint16)", key, *value); |
| return status; |
| } |
| #if 0 |
| static int get_uint32(DBusMessageIter *iter, const char *key, uint32_t *value) |
| { |
| int status = _get_basic(iter, key, DBUS_TYPE_UINT32, value); |
| _DBG_DHCPCD("%s = 0x%x (uint32)", key, *value); |
| return status; |
| } |
| #endif |
| static int get_string(DBusMessageIter *iter, const char *key, char **value) |
| { |
| int status = _get_basic(iter, key, DBUS_TYPE_STRING, value); |
| _DBG_DHCPCD("%s = %s (string)", key, *value); |
| return status; |
| } |
| |
| /* |
| * Convert an IPv4 address in network byte order to dot'd quad string. |
| * Return 1 if successful, 0 if there is a problem in the conversion. |
| */ |
| static int _cvt_ipv4(char str[ADDR_SIZE], uint32_t addr) |
| { |
| return (inet_ntop(AF_INET, &addr, str, ADDR_SIZE) != NULL); |
| } |
| |
| /* |
| * Get an IPv4 address and return as a string. Return TRUE on |
| * success, FALSE if a protocol or conversion botch. |
| */ |
| static int get_addr(DBusMessageIter *iter, const char *key, |
| char local_addr[ADDR_SIZE]) |
| { |
| uint32_t value; |
| |
| if (_get_basic(iter, key, DBUS_TYPE_UINT32, &value) == TRUE && |
| _cvt_ipv4(local_addr, value) == TRUE) { |
| _DBG_DHCPCD("%s = %s (0x%x)", key, local_addr, value); |
| return TRUE; |
| } else |
| return FALSE; |
| } |
| |
| /* |
| * D-Bus helper functions for parsing an array of basic values. |
| */ |
| |
| static int _get_array_basic(DBusMessageIter *iter, const char *key, |
| int basic_type, void *value, int *nelems, int max_elems) |
| { |
| DBusMessageIter array, entry; |
| int type; |
| |
| *nelems = 0; |
| dbus_message_iter_recurse(iter, &array); |
| type = dbus_message_iter_get_arg_type(&array); |
| if (type != DBUS_TYPE_ARRAY) { |
| (void) bad_type(key, DBUS_TYPE_ARRAY, type); |
| return FALSE; |
| } |
| dbus_message_iter_recurse(&array, &entry); |
| /* TODO(sleffler) verify dbus_type_is_fixed */ |
| type = dbus_message_iter_get_arg_type(&entry); |
| if (type != basic_type) { |
| (void) bad_type(key, basic_type, type); |
| return FALSE; |
| } else { |
| dbus_message_iter_get_fixed_array(&entry, value, nelems); |
| if ((*nelems) > max_elems) |
| return bad_array_size(key, *nelems, max_elems); |
| return TRUE; |
| } |
| } |
| static int get_array_uint32(DBusMessageIter *iter, const char *key, |
| uint32_t **value, int *nelems, int max_elems) |
| { |
| int status = _get_array_basic(iter, key, DBUS_TYPE_UINT32, |
| value, nelems, max_elems); |
| _DBG_DHCPCD("%s = [%d]{ 0x%x, 0x%x, 0x%x, 0x%x, 0x%x }", key, *nelems |
| , (*nelems > 0) ? (*value)[0] : -1 |
| , (*nelems > 1) ? (*value)[1] : -1 |
| , (*nelems > 2) ? (*value)[2] : -1 |
| , (*nelems > 3) ? (*value)[3] : -1 |
| , (*nelems > 4) ? (*value)[4] : -1 |
| ); |
| return status; |
| } |
| |
| /* |
| * Parse and return an array of strings. The returned data are references |
| * to the d-bus data and most be copied. The array size is returned in nelems. |
| * The max_elems parameter specifies the maximum number of elements to accept; |
| * if more elements are present in the d-bus data then FALSE is returned. |
| * Otherwise TRUE is returned on success or FALSE if the wire format is |
| * malformed. |
| */ |
| static int get_array_string(DBusMessageIter *iter, const char *key, |
| char **value, int *nelems, int max_elems) |
| { |
| DBusMessageIter array, entry; |
| int type; |
| |
| *nelems = 0; |
| dbus_message_iter_recurse(iter, &array); |
| type = dbus_message_iter_get_arg_type(&array); |
| if (type != DBUS_TYPE_ARRAY) { |
| (void) bad_type(key, DBUS_TYPE_ARRAY, type); |
| return FALSE; |
| } |
| type = dbus_message_iter_get_element_type(&array); |
| if (type != DBUS_TYPE_STRING) |
| return bad_type(key, DBUS_TYPE_STRING, type); |
| dbus_message_iter_recurse(&array, &entry); |
| while (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_INVALID) { |
| if ((*nelems) >= max_elems) |
| return bad_array_size(key, (*nelems), max_elems); |
| |
| dbus_message_iter_get_basic(&entry, &value[*nelems]); |
| _DBG_DHCPCD("%s = [%d]{ %s }", key, *nelems, value[*nelems]); |
| (*nelems)++; |
| |
| dbus_message_iter_next(&entry); |
| } |
| return TRUE; |
| } |
| |
| static DBusHandlerResult dhcpcd_filter(DBusConnection *conn, |
| DBusMessage *msg, void *data) |
| { |
| #define iseq(_a, _b) (g_ascii_strcasecmp((_a), (_b)) == 0) |
| DBusMessageIter iter, dict; |
| dbus_uint32_t pid; |
| struct dhcpcd_task *task; |
| const char *reason, *key; |
| char *value; |
| struct connman_ipaddress ipaddr; |
| char local_addr[ADDR_SIZE]; |
| char bcast_addr[ADDR_SIZE]; |
| char gateway_addr[ADDR_SIZE]; |
| char name_servers[MAXDNS][ADDR_SIZE], *dns_servers[MAXDNS+1]; |
| char *search_domains[MAXDOMAINS+1]; |
| uint32_t *uint32_array; |
| int i, nelems; |
| dbus_bool_t is_event, is_status; |
| |
| is_event = dbus_message_is_signal(msg, DHCPCD_INTF, "Event"); |
| is_status = dbus_message_is_signal(msg, DHCPCD_INTF, "StatusChanged"); |
| |
| /* Accept StatusChanged events long enough that we can get dbus addr */ |
| if (is_event == FALSE && is_status == FALSE) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| dbus_message_iter_init(msg, &iter); |
| |
| /* PID of dhcpcd process */ |
| dbus_message_iter_get_basic(&iter, &pid); |
| task = find_task_by_pid(pid); |
| if (task == NULL) { |
| connman_error("%s: no task for pid %d", __func__, pid); |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| } |
| |
| /* Save the DBus address of the sender if we don't have it yet */ |
| if (task->dbus_address == NULL) { |
| const char *message_dbus_address; |
| message_dbus_address = dbus_message_get_sender(msg); |
| task->dbus_address = g_strdup(message_dbus_address); |
| _DBG_DHCPCD("pid %d has DBus address %s", pid, task->dbus_address); |
| } |
| |
| /* Past here, only events are handled */ |
| if (is_event == FALSE) |
| return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; |
| |
| dbus_message_iter_next(&iter); |
| |
| /* reason for message */ |
| dbus_message_iter_get_basic(&iter, &reason); |
| dbus_message_iter_next(&iter); |
| |
| _DBG_DHCPCD("change %s state to %s", |
| connman_ipconfig_get_ifname(task->ipconfig), reason); |
| |
| memset(&ipaddr, 0, sizeof(ipaddr)); |
| ipaddr.af = AF_INET; |
| ipaddr.mask |= CONNMAN_IPCONFIG_AF; |
| |
| dbus_message_iter_recurse(&iter, &dict); |
| while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { |
| DBusMessageIter entry; |
| int type; |
| |
| dbus_message_iter_recurse(&dict, &entry); |
| dbus_message_iter_get_basic(&entry, &key); |
| |
| dbus_message_iter_next(&entry); |
| type = dbus_message_iter_get_arg_type(&entry); |
| if (type != DBUS_TYPE_VARIANT) |
| return bad_type(key, DBUS_TYPE_VARIANT, type); |
| |
| if (iseq(key, "IPAddress")) { |
| if (get_addr(&entry, key, local_addr) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| ipaddr.local = local_addr; |
| ipaddr.mask |= CONNMAN_IPCONFIG_LOCAL; |
| } else if (iseq(key, "SubnetCIDR")) { |
| uint8_t byte_value; |
| |
| if (get_byte(&entry, key, &byte_value) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| ipaddr.prefixlen = byte_value; |
| ipaddr.mask |= CONNMAN_IPCONFIG_PREFIX; |
| } else if (iseq(key, "BroadcastAddress")) { |
| if (get_addr(&entry, key, bcast_addr) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| ipaddr.broadcast = bcast_addr; |
| ipaddr.mask |= CONNMAN_IPCONFIG_BCAST; |
| } else if (iseq(key, "Routers")) { |
| if (get_array_uint32(&entry, key, |
| /* NB: 1000 is just "big" */ |
| &uint32_array, &nelems, 1000) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| /* NB: silently discard extra routers */ |
| if (nelems < 1) |
| return bad_array_size(key, nelems, 1); |
| /* TODO(sleffler) check return */ |
| (void) _cvt_ipv4(gateway_addr, uint32_array[0]); |
| ipaddr.gateway = gateway_addr; |
| ipaddr.mask |= CONNMAN_IPCONFIG_GW; |
| } else if (iseq(key, "DomainNameServers")) { |
| if (get_array_uint32(&entry, key, |
| &uint32_array, &nelems, 10) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| for (i = 0; i < nelems; i++) { |
| dns_servers[i] = &name_servers[i][0]; |
| _cvt_ipv4(dns_servers[i], uint32_array[i]); |
| } |
| dns_servers[i] = NULL; /* NB: for g_strdupv */ |
| ipaddr.dns_servers = dns_servers; |
| ipaddr.mask |= CONNMAN_IPCONFIG_DNS; |
| } else if (iseq(key, "DomainName")) { |
| if (get_string(&entry, key, &value) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| ipaddr.domain_name = value; |
| ipaddr.mask |= CONNMAN_IPCONFIG_DOMAIN; |
| } else if (iseq(key, "DomainSearch")) { |
| if (get_array_string(&entry, key, |
| search_domains, &nelems, 10) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| search_domains[nelems] = NULL; /* NB: for g_strdupv */ |
| ipaddr.search_domains = search_domains; |
| ipaddr.mask |= CONNMAN_IPCONFIG_SEARCH; |
| } else if (iseq(key, "Hostname")) { |
| if (get_string(&entry, key, &value) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| /* TODO(sleffler) add support */ |
| } else if (iseq(key, "InterfaceMTU")) { |
| uint16_t uint16_value; |
| |
| if (get_uint16(&entry, key, &uint16_value) == FALSE) |
| return DBUS_HANDLER_RESULT_HANDLED; |
| /* NB: restrict min MTU per dhcpcd-script */ |
| if (576 <= uint16_value) { |
| ipaddr.mtu = uint16_value; |
| ipaddr.mask |= CONNMAN_IPCONFIG_MTU; |
| } else |
| _DBG_DHCPCD("%s = %u (skipped)", key, |
| uint16_value); |
| } else |
| _DBG_DHCPCD("ignore key %s", key); |
| |
| dbus_message_iter_next(&dict); |
| } |
| |
| if (g_ascii_strcasecmp(reason, "BOUND") == 0 |
| || g_ascii_strcasecmp(reason, "REBOOT") == 0 |
| /* |
| * Even if IP state hasn't changed, this is the path to |
| * bringing the service from CONFIGURATION state to READY. |
| */ |
| || g_ascii_strcasecmp(reason, "RENEW") == 0 |
| || g_ascii_strcasecmp(reason, "REBIND") == 0 |
| ) { |
| connman_ipconfig_bind(task->ipconfig, &ipaddr); |
| } else if (g_ascii_strcasecmp(reason, "FAIL") == 0) { |
| connman_ipconfig_set_error(task->ipconfig, |
| CONNMAN_ELEMENT_ERROR_DHCP_FAILED); |
| } |
| return DBUS_HANDLER_RESULT_HANDLED; |
| #undef iseq |
| } |
| |
| static const char *dhcpcd_rule = "path=" DHCPCD_PATH |
| ",interface=" DHCPCD_INTF; |
| |
| static int dhcpcd_init(void) |
| { |
| int err; |
| |
| /* TODO(sleffler) check return values */ |
| connection = connman_dbus_get_connection(); |
| dbus_connection_add_filter(connection, dhcpcd_filter, NULL, NULL); |
| dbus_bus_add_match(connection, dhcpcd_rule, NULL); |
| |
| err = connman_ipconfig_driver_register(&dhcpcd_driver); |
| if (err < 0) |
| dbus_connection_unref(connection); |
| return err; |
| } |
| |
| static void dhcpcd_exit(void) |
| { |
| GSList *list; |
| |
| for (list = task_list; list; list = list->next) { |
| struct dhcpcd_task *task = list->data; |
| |
| _DBG_DHCPCD("kill process %d", task->pid); |
| |
| kill_task(task); |
| unlink_task(task); |
| } |
| g_slist_free(task_list); |
| |
| connman_ipconfig_driver_unregister(&dhcpcd_driver); |
| |
| dbus_bus_remove_match(connection, dhcpcd_rule, NULL); |
| dbus_connection_remove_filter(connection, dhcpcd_filter, NULL); |
| dbus_connection_unref(connection); |
| } |
| |
| CONNMAN_PLUGIN_DEFINE(dhcpcd, "Chrome OS DHCP client", VERSION, |
| CONNMAN_PLUGIN_PRIORITY_DEFAULT, dhcpcd_init, dhcpcd_exit) |