Home | History | Annotate | Download | only in dbus
      1 /*
      2  * dhcpcd - DHCP client daemon
      3  * Copyright (c) 2006-2015 Roy Marples <roy (at) marples.name>
      4  * All rights reserved
      5 
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 #include <errno.h>
     29 #include <poll.h>
     30 #include <signal.h>
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <syslog.h>
     35 #include <unistd.h>
     36 
     37 #include <dbus/dbus.h>
     38 
     39 #include "../config.h"
     40 #include "../eloop.h"
     41 #include "../dhcp.h"
     42 #ifdef INET6
     43 #include "../dhcp6.h"
     44 #endif
     45 #include "../rpc-interface.h"
     46 #include "dbus-dict.h"
     47 
     48 #define SERVICE_NAME 	"org.chromium.dhcpcd"
     49 #define SERVICE_PATH    "/org/chromium/dhcpcd"
     50 #define S_EINVAL	SERVICE_NAME ".InvalidArgument"
     51 #define S_ARGS		"Not enough arguments"
     52 
     53 static DBusConnection *connection;
     54 static struct dhcpcd_ctx *dhcpcd_ctx;
     55 
     56 static const char dhcpcd_introspection_xml[] =
     57     "    <method name=\"GetVersion\">\n"
     58     "      <arg name=\"version\" direction=\"out\" type=\"s\"/>\n"
     59     "    </method>\n"
     60     "    <method name=\"Rebind\">\n"
     61     "      <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"
     62     "    </method>\n"
     63     "    <method name=\"Release\">\n"
     64     "      <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"
     65     "    </method>\n"
     66     "    <method name=\"Stop\">\n"
     67     "      <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"
     68     "    </method>\n"
     69     "    <signal name=\"Event\">\n"
     70     "      <arg name=\"configuration\" type=\"usa{sv}\"/>\n"
     71     "    </signal>\n"
     72     "    <signal name=\"StatusChanged\">\n"
     73     "      <arg name=\"status\" type=\"us\"/>\n"
     74     "    </signal>\n";
     75 
     76 static const char service_watch_rule[] = "interface=" DBUS_INTERFACE_DBUS
     77 	",type=signal,member=NameOwnerChanged";
     78 
     79 static const char introspection_header_xml[] =
     80     "<!DOCTYPE node PUBLIC \"-//freedesktop//"
     81     "DTD D-BUS Object Introspection 1.0//EN\"\n"
     82     "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
     83     "<node name=\"" SERVICE_PATH "\">\n"
     84     "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
     85     "    <method name=\"Introspect\">\n"
     86     "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
     87     "    </method>\n"
     88     "  </interface>\n"
     89     "  <interface name=\"" SERVICE_NAME "\">\n";
     90 
     91 static const char introspection_footer_xml[] =
     92     "  </interface>\n"
     93     "</node>\n";
     94 
     95 static const struct o_dbus dhos[] = {
     96 	{ "ip_address=", DBUS_TYPE_UINT32, 0, "IPAddress" },
     97 	{ "server_name=", DBUS_TYPE_STRING, 0, "ServerName"},
     98 	{ "subnet_mask=", DBUS_TYPE_UINT32, 0, "SubnetMask" },
     99 	{ "subnet_cidr=", DBUS_TYPE_BYTE, 0, "SubnetCIDR" },
    100 	{ "network_number=", DBUS_TYPE_UINT32, 0, "NetworkNumber" },
    101 	{ "classless_static_routes=", DBUS_TYPE_STRING, 0,
    102 	  "ClasslessStaticRoutes" },
    103 	{ "ms_classless_static_routes=", DBUS_TYPE_STRING, 0,
    104 	  "MSClasslessStaticRoutes" },
    105 	{ "static_routes=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    106 	  "StaticRoutes"} ,
    107 	{ "routers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "Routers" },
    108 	{ "time_offset=", DBUS_TYPE_UINT32, 0, "TimeOffset" },
    109 	{ "time_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "TimeServers" },
    110 	{ "ien116_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    111 	  "IEN116NameServers" },
    112 	{ "domain_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    113 	  "DomainNameServers" },
    114 	{ "log_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LogServers" },
    115 	{ "cookie_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    116 	  "CookieServers" },
    117 	{ "lpr_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LPRServers" },
    118 	{ "impress_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    119 	  "ImpressServers" },
    120 	{ "resource_location_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    121 	  "ResourceLocationServers" },
    122 	{ "host_name=", DBUS_TYPE_STRING, 0, "Hostname" },
    123 	{ "boot_size=", DBUS_TYPE_UINT16, 0, "BootSize" },
    124 	{ "merit_dump=", DBUS_TYPE_STRING, 0, "MeritDump" },
    125 	{ "domain_name=", DBUS_TYPE_STRING, 0, "DomainName" },
    126 	{ "swap_server=", DBUS_TYPE_UINT32, 0, "SwapServer" },
    127 	{ "root_path=", DBUS_TYPE_STRING, 0, "RootPath" },
    128 	{ "extensions_path=", DBUS_TYPE_STRING, 0, "ExtensionsPath" },
    129 	{ "ip_forwarding=", DBUS_TYPE_BOOLEAN, 0, "IPForwarding" },
    130 	{ "non_local_source_routing=", DBUS_TYPE_BOOLEAN, 0,
    131 	  "NonLocalSourceRouting" },
    132 	{ "policy_filter=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    133 	  "PolicyFilter" },
    134 	{ "max_dgram_reassembly=", DBUS_TYPE_INT16, 0,
    135 	  "MaxDatagramReassembly" },
    136 	{ "default_ip_ttl=", DBUS_TYPE_UINT16, 0, "DefaultIPTTL" },
    137 	{ "path_mtu_aging_timeout=", DBUS_TYPE_UINT32, 0,
    138 	  "PathMTUAgingTimeout" },
    139 	{ "path_mtu_plateau_table=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT16,
    140 	  "PolicyFilter"} ,
    141 	{ "interface_mtu=", DBUS_TYPE_UINT16, 0, "InterfaceMTU" },
    142 	{ "all_subnets_local=", DBUS_TYPE_BOOLEAN, 0, "AllSubnetsLocal" },
    143 	{ "broadcast_address=", DBUS_TYPE_UINT32, 0, "BroadcastAddress" },
    144 	{ "perform_mask_discovery=", DBUS_TYPE_BOOLEAN, 0,
    145 	  "PerformMaskDiscovery" },
    146 	{ "mask_supplier=", DBUS_TYPE_BOOLEAN, 0, "MaskSupplier" },
    147 	{ "router_discovery=", DBUS_TYPE_BOOLEAN, 0, "RouterDiscovery" },
    148 	{ "router_solicitiation_address=", DBUS_TYPE_UINT32, 0,
    149 	  "RouterSolicationAddress" },
    150 	{ "trailer_encapsulation=", DBUS_TYPE_BOOLEAN, 0,
    151 	  "TrailerEncapsulation" },
    152 	{ "arp_cache_timeout=", DBUS_TYPE_UINT32, 0, "ARPCacheTimeout" },
    153 	{ "ieee802_3_encapsulation=", DBUS_TYPE_UINT16, 0,
    154 	  "IEEE8023Encapsulation" },
    155 	{ "default_tcp_ttl=", DBUS_TYPE_BYTE, 0, "DefaultTCPTTL" },
    156 	{ "tcp_keepalive_interval=", DBUS_TYPE_UINT32, 0,
    157 	  "TCPKeepAliveInterval" },
    158 	{ "tcp_keepalive_garbage=", DBUS_TYPE_BOOLEAN, 0,
    159 	  "TCPKeepAliveGarbage" },
    160 	{ "nis_domain=", DBUS_TYPE_STRING, 0, "NISDomain" },
    161 	{ "nis_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NISServers" },
    162 	{ "ntp_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NTPServers" },
    163 	{ "vendor_encapsulated_options=", DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
    164 	  "VendorEncapsulatedOptions" },
    165 	{ "netbios_name_servers=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    166 	  "NetBIOSNameServers" },
    167 	{ "netbios_dd_server=", DBUS_TYPE_UINT32, 0, "NetBIOSDDServer" },
    168 	{ "netbios_node_type=", DBUS_TYPE_BYTE, 0, "NetBIOSNodeType" },
    169 	{ "netbios_scope=", DBUS_TYPE_STRING, 0, "NetBIOSScope" },
    170 	{ "font_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "FontServers" },
    171 	{ "x_display_manager=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    172 	  "XDisplayManager" },
    173 	{ "dhcp_requested_address=", DBUS_TYPE_UINT32, 0,
    174 	  "DHCPRequestedAddress" },
    175 	{ "dhcp_lease_time=", DBUS_TYPE_UINT32, 0, "DHCPLeaseTime" },
    176 	{ "dhcp_option_overload=", DBUS_TYPE_BOOLEAN, 0,
    177 	  "DHCPOptionOverload" },
    178 	{ "dhcp_message_type=", DBUS_TYPE_BYTE, 0, "DHCPMessageType" },
    179 	{ "dhcp_server_identifier=", DBUS_TYPE_UINT32, 0,
    180 	  "DHCPServerIdentifier" },
    181 	{ "dhcp_message=", DBUS_TYPE_STRING, 0, "DHCPMessage" },
    182 	{ "dhcp_max_message_size=", DBUS_TYPE_UINT16, 0,
    183 	  "DHCPMaxMessageSize" },
    184 	{ "dhcp_renewal_time=", DBUS_TYPE_UINT32, 0, "DHCPRenewalTime" },
    185 	{ "dhcp_rebinding_time=", DBUS_TYPE_UINT32, 0, "DHCPRebindingTime" },
    186 	{ "nisplus_domain=", DBUS_TYPE_STRING, 0, "NISPlusDomain" },
    187 	{ "nisplus_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    188 	  "NISPlusServers" },
    189 	{ "tftp_server_name=", DBUS_TYPE_STRING, 0, "TFTPServerName" },
    190 	{ "bootfile_name=", DBUS_TYPE_STRING, 0, "BootFileName" },
    191 	{ "mobile_ip_home_agent=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    192 	  "MobileIPHomeAgent" },
    193 	{ "smtp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "SMTPServer" },
    194 	{ "pop_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "POPServer" },
    195 	{ "nntp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NNTPServer" },
    196 	{ "www_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "WWWServer" },
    197 	{ "finger_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    198 	  "FingerServer" },
    199 	{ "irc_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "IRCServer" },
    200 	{ "streettalk_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    201 	  "StreetTalkServer" },
    202 	{ "streettalk_directory_assistance_server=", DBUS_TYPE_ARRAY,
    203 	  DBUS_TYPE_UINT32, "StreetTalkDirectoryAssistanceServer" },
    204 	{ "user_class=", DBUS_TYPE_STRING, 0, "UserClass" },
    205 	{ "new_fqdn_name=", DBUS_TYPE_STRING, 0, "FQDNName" },
    206 	{ "nds_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NDSServers" },
    207 	{ "nds_tree_name=", DBUS_TYPE_STRING, 0, "NDSTreeName" },
    208 	{ "nds_context=", DBUS_TYPE_STRING, 0, "NDSContext" },
    209 	{ "bcms_controller_names=", DBUS_TYPE_STRING, 0,
    210 	  "BCMSControllerNames" },
    211 	{ "client_last_transaction_time=", DBUS_TYPE_UINT32, 0,
    212 	  "ClientLastTransactionTime" },
    213 	{ "associated_ip=", DBUS_TYPE_UINT32, 0, "AssociatedIP" },
    214 	{ "uap_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "UAPServers" },
    215 	{ "netinfo_server_address=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
    216 	  "NetinfoServerAddress" },
    217 	{ "netinfo_server_tag=", DBUS_TYPE_STRING, 0, "NetinfoServerTag" },
    218 	{ "default_url=", DBUS_TYPE_STRING, 0, "DefaultURL" },
    219 	{ "subnet_selection=", DBUS_TYPE_UINT32, 0, "SubnetSelection" },
    220 	{ "domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
    221 	  "DomainSearch" },
    222 	{ "wpad_url=", DBUS_TYPE_STRING, 0, "WebProxyAutoDiscoveryUrl" },
    223 #ifdef INET6
    224 	{ "dhcp6_server_id=", DBUS_TYPE_STRING, 0,
    225 	  "DHCPv6ServerIdentifier" },
    226 	{ "dhcp6_ia_na1_ia_addr1=", DBUS_TYPE_STRING, 0, "DHCPv6Address" },
    227 	{ "dhcp6_ia_na1_ia_addr1_vltime=", DBUS_TYPE_UINT32, 0,
    228 	  "DHCPv6AddressLeaseTime" },
    229 	{ "dhcp6_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
    230 	  "DHCPv6NameServers" },
    231 	{ "dhcp6_domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
    232 	  "DHCPv6DomainSearch" },
    233 	{ "dhcp6_ia_pd1_prefix1=", DBUS_TYPE_STRING, 0,
    234 	  "DHCPv6DelegatedPrefix" },
    235 	{ "dhcp6_ia_pd1_prefix1_length=", DBUS_TYPE_UINT32, 0,
    236 	  "DHCPv6DelegatedPrefixLength" },
    237 	{ "dhcp6_ia_pd1_prefix1_vltime=", DBUS_TYPE_UINT32, 0,
    238 	  "DHCPv6DelegatedPrefixLeaseTime" },
    239 #endif
    240 	{ NULL, 0, 0, NULL }
    241 };
    242 
    243 static int
    244 append_config(DBusMessageIter *iter,
    245     const char *prefix, char **env, ssize_t elen)
    246 {
    247 	char **eenv, *p;
    248 	const struct o_dbus *dhop;
    249 	size_t l, lp;
    250 	int retval;
    251 
    252 	retval = 0;
    253 	lp = strlen(prefix);
    254 	for (eenv = env + elen; env < eenv; env++) {
    255 		p = env[0];
    256 		for (dhop = dhos; dhop->var; dhop++) {
    257 			l = strlen(dhop->var);
    258 			if (strncmp(p, dhop->var, l) == 0) {
    259 				retval = dict_append_config_item(iter,
    260 				    dhop, p + l);
    261 				break;
    262 			}
    263 			if (strncmp(p, prefix, lp) == 0 &&
    264 			    strncmp(p + lp, dhop->var, l) == 0)
    265 			{
    266 				retval = dict_append_config_item(iter,
    267 				    dhop, p + l + lp);
    268 				break;
    269 			}
    270 		}
    271 		if (retval == -1)
    272 			break;
    273 	}
    274 	return retval;
    275 }
    276 
    277 static DBusHandlerResult
    278 get_dbus_error(DBusConnection *con, DBusMessage *msg,
    279 		  const char *name, const char *fmt, ...)
    280 {
    281 	char buffer[1024];
    282 	DBusMessage *reply;
    283 	va_list args;
    284 
    285 	va_start(args, fmt);
    286 	vsnprintf(buffer, sizeof(buffer), fmt, args);
    287 	va_end(args);
    288 	reply = dbus_message_new_error(msg, name, buffer);
    289 	dbus_connection_send(con, reply, NULL);
    290 	dbus_message_unref(reply);
    291 	return DBUS_HANDLER_RESULT_HANDLED;
    292 }
    293 
    294 static dbus_bool_t
    295 dbus_send_message(const struct interface *ifp, const char *reason,
    296     const char *prefix, struct dhcp_message *message)
    297 {
    298 	const struct if_options *ifo = ifp->options;
    299 	DBusMessage* msg;
    300 	DBusMessageIter args, dict;
    301 	int pid = getpid();
    302 	char **env = NULL;
    303 	ssize_t e, elen;
    304 	int retval;
    305 	int success = FALSE;
    306 
    307 	syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name);
    308 
    309 	msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event");
    310 	if (msg == NULL) {
    311 		syslog(LOG_ERR, "failed to make a configure message");
    312 		return FALSE;
    313 	}
    314 	dbus_message_iter_init_append(msg, &args);
    315 	dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid);
    316 	dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason);
    317 	dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
    318 	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
    319 	    DBUS_TYPE_STRING_AS_STRING
    320 	    DBUS_TYPE_VARIANT_AS_STRING
    321 	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
    322 	    &dict);
    323 	if (prefix == NULL || message == NULL)
    324 		retval = 0;
    325 	else {
    326 		e = dhcp_env(NULL, NULL, message, ifp);
    327 		if (e > 0) {
    328 			char *config_prefix = strdup(prefix);
    329 			if (config_prefix == NULL) {
    330 				logger(dhcpcd_ctx, LOG_ERR,
    331 				       "Memory exhausted (strdup)");
    332 				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
    333 			}
    334 			char *p = config_prefix + strlen(config_prefix) - 1;
    335 			if (p >= config_prefix && *p == '_')
    336 				*p = '\0';
    337 			env = calloc(e + 1, sizeof(char *));
    338 			if (env == NULL) {
    339 				logger(dhcpcd_ctx, LOG_ERR,
    340 				       "Memory exhausted (calloc)");
    341 				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
    342 			}
    343 			elen = dhcp_env(env, config_prefix, message, ifp);
    344 			free(config_prefix);
    345 		}
    346 		retval = append_config(&dict, prefix, env, elen);
    347 	}
    348 
    349 	/* Release memory allocated for env. */
    350 	if (env) {
    351 		char **current = env;
    352 		while (*current)
    353 			free(*current++);
    354 		free(env);
    355 	}
    356 
    357 	dbus_message_iter_close_container(&args, &dict);
    358 	if (retval == 0) {
    359 		success = dbus_connection_send(connection, msg, NULL);
    360 		if (!success)
    361 			syslog(LOG_ERR, "failed to send dhcp to dbus");
    362 	} else
    363 		syslog(LOG_ERR, "failed to construct dbus message");
    364 	dbus_message_unref(msg);
    365 
    366 	return success;
    367 }
    368 
    369 #ifdef INET6
    370 static dbus_bool_t
    371 dbus_send_dhcpv6_message(const struct interface *ifp, const char *reason,
    372     const char *prefix, struct dhcp6_message *message, size_t length)
    373 {
    374 	const struct if_options *ifo = ifp->options;
    375 	DBusMessage* msg;
    376 	DBusMessageIter args, dict;
    377 	int pid = getpid();
    378 	char **env = NULL;
    379 	ssize_t e, elen;
    380 	int retval;
    381 	int success = FALSE;
    382 
    383 	syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name);
    384 
    385 	msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event");
    386 	if (msg == NULL) {
    387 		syslog(LOG_ERR, "failed to make a configure message");
    388 		return FALSE;
    389 	}
    390 	dbus_message_iter_init_append(msg, &args);
    391 	dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid);
    392 	dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason);
    393 	dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
    394 	    DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
    395 	    DBUS_TYPE_STRING_AS_STRING
    396 	    DBUS_TYPE_VARIANT_AS_STRING
    397 	    DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
    398 	    &dict);
    399 	if (prefix == NULL || message == NULL)
    400 		retval = 0;
    401 	else {
    402 		e = dhcp6_env(NULL, NULL, ifp, message, length);
    403 		if (e > 0) {
    404 			char *config_prefix = strdup(prefix);
    405 			if (config_prefix == NULL) {
    406 				logger(dhcpcd_ctx, LOG_ERR,
    407 				       "Memory exhausted (strdup)");
    408 				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
    409 			}
    410 			char *p = config_prefix + strlen(config_prefix) - 1;
    411 			if (p >= config_prefix && *p == '_')
    412 				*p = '\0';
    413 			env = calloc(e + 1, sizeof(char *));
    414 			if (env == NULL) {
    415 				logger(dhcpcd_ctx, LOG_ERR,
    416 				       "Memory exhausted (calloc)");
    417 				eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE);
    418 			}
    419 			elen = dhcp6_env(env, "new", ifp, message, length);
    420 			free(config_prefix);
    421 		}
    422 		retval = append_config(&dict, prefix, env, elen);
    423 	}
    424 
    425 	/* Release memory allocated for env. */
    426 	if (env) {
    427 		char **current = env;
    428 		while (*current)
    429 			free(*current++);
    430 		free(env);
    431 	}
    432 
    433 	dbus_message_iter_close_container(&args, &dict);
    434 	if (retval == 0) {
    435 		success = dbus_connection_send(connection, msg, NULL);
    436 		if (!success)
    437 			syslog(LOG_ERR, "failed to send dhcpv6 to dbus");
    438 	} else
    439 		syslog(LOG_ERR, "failed to construct dbus message");
    440 	dbus_message_unref(msg);
    441 
    442 	return success;
    443 }
    444 #endif
    445 
    446 static DBusHandlerResult
    447 introspect(DBusConnection *con, DBusMessage *msg)
    448 {
    449 	DBusMessage *reply;
    450 	char *xml;
    451 	size_t len;
    452 
    453 	len = sizeof(introspection_header_xml) - 1
    454 	    + sizeof(dhcpcd_introspection_xml) - 1
    455 	    + sizeof(introspection_footer_xml) - 1
    456 	    + 1; /* terminal \0 */
    457 	xml = malloc(len);
    458 	if (xml == NULL)
    459 		return DBUS_HANDLER_RESULT_HANDLED;
    460 	snprintf(xml, len, "%s%s%s",
    461 	    introspection_header_xml,
    462 	    dhcpcd_introspection_xml,
    463 	    introspection_footer_xml);
    464 	reply = dbus_message_new_method_return(msg);
    465 	dbus_message_append_args(reply,
    466 	    DBUS_TYPE_STRING, &xml,
    467 	    DBUS_TYPE_INVALID);
    468 	dbus_connection_send(con, reply, NULL);
    469 	dbus_message_unref(reply);
    470 	free(xml);
    471 	return DBUS_HANDLER_RESULT_HANDLED;
    472 }
    473 
    474 static DBusHandlerResult
    475 version(DBusConnection *con, DBusMessage *msg, const char *ver)
    476 {
    477 	DBusMessage *reply;
    478 
    479 	reply = dbus_message_new_method_return(msg);
    480 	dbus_message_append_args(reply,
    481 	    DBUS_TYPE_STRING, &ver,
    482 	    DBUS_TYPE_INVALID);
    483 	dbus_connection_send(con, reply, NULL);
    484 	dbus_message_unref(reply);
    485 	return DBUS_HANDLER_RESULT_HANDLED;
    486 }
    487 
    488 static DBusHandlerResult
    489 dbus_ack(DBusConnection *con, DBusMessage *msg)
    490 {
    491 	DBusMessage *reply;
    492 
    493 	reply = dbus_message_new_method_return(msg);
    494 	dbus_connection_send(con, reply, NULL);
    495 	dbus_message_unref(reply);
    496 	return DBUS_HANDLER_RESULT_HANDLED;
    497 }
    498 
    499 static DBusHandlerResult
    500 msg_handler(DBusConnection *con, DBusMessage *msg, __unused void *data)
    501 {
    502 #define	IsMethod(msg, method) \
    503 	dbus_message_is_method_call(msg, SERVICE_NAME, method)
    504 
    505 	if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE,
    506 					"Introspect")) {
    507 		return introspect(con, msg);
    508 	} else if (IsMethod(msg, "GetVersion")) {
    509 		return version(con, msg, VERSION);
    510 	} else if (IsMethod(msg, "Rebind")) {
    511 		const char *iface_name;
    512 		if (!dbus_message_get_args(msg, NULL,
    513 				   DBUS_TYPE_STRING, &iface_name,
    514 				   DBUS_TYPE_INVALID)) {
    515 			logger(dhcpcd_ctx, LOG_ERR,
    516 			       "Invalid arguments for Rebind");
    517 			return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
    518 		}
    519 		dhcpcd_start_interface(dhcpcd_ctx, iface_name);
    520 		return dbus_ack(con, msg);
    521 	} else if (IsMethod(msg, "Release")) {
    522 		const char *iface_name;
    523 		if (!dbus_message_get_args(msg, NULL,
    524 				   DBUS_TYPE_STRING, &iface_name,
    525 				   DBUS_TYPE_INVALID)) {
    526 			logger(dhcpcd_ctx, LOG_ERR,
    527 			       "Invalid arguments for Release");
    528 			return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
    529 		}
    530 		dhcpcd_release_ipv4(dhcpcd_ctx, iface_name);
    531 		return dbus_ack(con, msg);
    532 	} else if (IsMethod(msg, "Stop")) {
    533 		const char *iface_name;
    534 		if (!dbus_message_get_args(msg, NULL,
    535 				   DBUS_TYPE_STRING, &iface_name,
    536 				   DBUS_TYPE_INVALID)) {
    537 			logger(dhcpcd_ctx, LOG_ERR,
    538 			       "Invalid arguments for Stop");
    539 			return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
    540 		}
    541 		dhcpcd_stop_interface(dhcpcd_ctx, iface_name);
    542 		(void) dbus_ack(con, msg);
    543 		exit(EXIT_FAILURE);
    544 	} else if (dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL,
    545 					  "Disconnected")) {
    546 		dhcpcd_stop_interfaces(dhcpcd_ctx);
    547 		exit(EXIT_FAILURE);
    548 	}
    549 	return get_dbus_error(con, msg, S_EINVAL, S_ARGS);
    550 #undef IsMethod
    551 }
    552 
    553 static void
    554 dbus_handle_event(DBusWatch *watch, int flags)
    555 {
    556 	dbus_watch_handle((DBusWatch *)watch, flags);
    557 
    558 	if (connection != NULL) {
    559 		dbus_connection_ref(connection);
    560 		while (dbus_connection_dispatch(connection) ==
    561 				DBUS_DISPATCH_DATA_REMAINS)
    562 				;
    563 		dbus_connection_unref(connection);
    564 	}
    565 }
    566 
    567 static void
    568 dbus_read_event(void *watch)
    569 {
    570 	dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_READABLE);
    571 }
    572 
    573 static void
    574 dbus_write_event(void *watch)
    575 {
    576 	dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_WRITABLE);
    577 }
    578 
    579 static dbus_bool_t
    580 add_watch(DBusWatch *watch, __unused void *data)
    581 {
    582 	int fd, flags;
    583 	void (*read_event)(void *) = NULL;
    584 	void *read_arg = NULL;
    585 	void (*write_event)(void *) = NULL;
    586 	void *write_arg = NULL;
    587 
    588 	fd = dbus_watch_get_unix_fd(watch);
    589 	flags = dbus_watch_get_flags(watch);
    590 	if (flags & DBUS_WATCH_READABLE) {
    591 		read_event = dbus_read_event;
    592 		read_arg = watch;
    593 	}
    594 	if (flags & DBUS_WATCH_WRITABLE) {
    595 		write_event = dbus_write_event;
    596 		write_arg = watch;
    597 	}
    598 
    599 	if (eloop_event_add(dhcpcd_ctx->eloop, fd, read_event, read_arg,
    600 			    write_event, write_arg) == 0)
    601 		return TRUE;
    602 	return FALSE;
    603 }
    604 
    605 static void
    606 remove_watch(DBusWatch *watch, __unused void *data)
    607 {
    608 	int fd, flags;
    609 	int write_only = 0;
    610 	fd = dbus_watch_get_unix_fd(watch);
    611 	flags = dbus_watch_get_flags(watch);
    612 	if (!(flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE))
    613 		write_only = 1;
    614 	eloop_event_delete(dhcpcd_ctx->eloop, fd, write_only);
    615 }
    616 
    617 static DBusHandlerResult
    618 dhcpcd_dbus_filter(DBusConnection *conn, DBusMessage *msg, void *user_data)
    619 {
    620 	const char *service = NULL;
    621 	const char *old_owner = NULL;
    622 	const char *new_owner = NULL;
    623 
    624 	if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS,
    625 				    "NameOwnerChanged"))
    626 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    627 
    628 	if (!dbus_message_get_args(msg, NULL,
    629 				   DBUS_TYPE_STRING, &service,
    630 				   DBUS_TYPE_STRING, &old_owner,
    631 				   DBUS_TYPE_STRING, &new_owner,
    632 				   DBUS_TYPE_INVALID)) {
    633 		syslog(LOG_ERR,
    634 		       "Invalid arguments for NameOwnerChanged signal");
    635 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    636 	}
    637 	if (strcmp(service, "org.chromium.flimflam") == 0 &&
    638 	    strlen(new_owner) == 0) {
    639 		syslog(LOG_INFO, "exiting because flimflamd has died");
    640 		dhcpcd_stop_interfaces(dhcpcd_ctx);
    641 		exit(EXIT_FAILURE);
    642 	}
    643 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    644 }
    645 
    646 int
    647 rpc_init(struct dhcpcd_ctx *ctx)
    648 {
    649 	DBusObjectPathVTable vt = {
    650 		NULL, &msg_handler, NULL, NULL, NULL, NULL
    651 	};
    652 	DBusError err;
    653 	int ret;
    654 
    655 	dhcpcd_ctx = ctx;
    656 
    657 	dbus_error_init(&err);
    658 	connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    659 	if (connection == NULL) {
    660 		if (dbus_error_is_set(&err))
    661 			syslog(LOG_ERR, "%s", err.message);
    662 		else
    663 			syslog(LOG_ERR, "failed to get a dbus connection");
    664 		return -1;
    665 	}
    666 	atexit(rpc_close);
    667 
    668 	if (!dbus_connection_set_watch_functions(connection,
    669 		add_watch, remove_watch, NULL, NULL, NULL))
    670 	{
    671 		syslog(LOG_ERR, "dbus: failed to set watch functions");
    672 		return -1;
    673 	}
    674 	if (!dbus_connection_register_object_path(connection,
    675 		SERVICE_PATH, &vt, NULL))
    676 	{
    677 		syslog(LOG_ERR, "dbus: failed to register object path");
    678 		return -1;
    679 	}
    680 	dbus_connection_add_filter(connection, dhcpcd_dbus_filter, NULL, NULL);
    681 	dbus_bus_add_match(connection, service_watch_rule, &err);
    682 	if (dbus_error_is_set(&err)) {
    683 		syslog(LOG_ERR, "Cannot add rule: %s", err.message);
    684 		return -1;
    685 	}
    686 	return 0;
    687 }
    688 
    689 void
    690 rpc_close(void)
    691 {
    692 	if (connection) {
    693 		dbus_bus_remove_match(connection, service_watch_rule, NULL);
    694 		dbus_connection_remove_filter(connection,
    695 					      dhcpcd_dbus_filter,
    696 					      NULL);
    697 		dbus_connection_unref(connection);
    698 		connection = NULL;
    699 	}
    700 }
    701 
    702 void
    703 rpc_signal_status(const char *status)
    704 {
    705 	DBusMessage *msg;
    706 	DBusMessageIter args;
    707 	int pid = getpid();
    708 
    709 	syslog(LOG_INFO, "status changed to %s", status);
    710 
    711 	msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME,
    712 	    "StatusChanged");
    713 	if (msg == NULL) {
    714 		syslog(LOG_ERR, "failed to make a status changed message");
    715 		return;
    716 	}
    717 	dbus_message_iter_init_append(msg, &args);
    718 	dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid);
    719 	dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &status);
    720 	if (!dbus_connection_send(connection, msg, NULL))
    721 		syslog(LOG_ERR, "failed to send status to dbus");
    722 	dbus_message_unref(msg);
    723 }
    724 
    725 
    726 int
    727 rpc_update_ipv4(struct interface *ifp)
    728 {
    729 	struct dhcp_state *state = D_STATE(ifp);
    730 	if (state->new != NULL) {
    731 		/* push state over d-bus */
    732 		dbus_send_message(ifp, state->reason, "new_", state->new);
    733 		rpc_signal_status("Bound");
    734 	} else {
    735 		rpc_signal_status("Release");
    736 	}
    737 	return 0;
    738 }
    739 
    740 #ifdef INET6
    741 int
    742 rpc_update_ipv6(struct interface *ifp)
    743 {
    744 	struct dhcp6_state *state = D6_STATE(ifp);
    745 	if (state->new != NULL) {
    746 		/* push state over d-bus */
    747 		dbus_send_dhcpv6_message(ifp, state->reason, "new_",
    748 					 state->new, state->new_len);
    749 		rpc_signal_status("Bound6");
    750 	} else {
    751 		rpc_signal_status("Release6");
    752 	}
    753 	return 0;
    754 }
    755 #endif
    756 
    757 int
    758 rpc_notify_unicast_arp(struct interface *ifp) {
    759 	struct dhcp_state *state = D_STATE(ifp);
    760 	return dbus_send_message(ifp, "GATEWAY-ARP", "saved_", state->offer);
    761 }
    762