/* * dhcpcd - DHCP client daemon * Copyright (c) 2006-2015 Roy Marples * All rights reserved * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include "../config.h" #include "../eloop.h" #include "../dhcp.h" #ifdef INET6 #include "../dhcp6.h" #endif #include "../rpc-interface.h" #include "dbus-dict.h" #define SERVICE_NAME "org.chromium.dhcpcd" #define SERVICE_PATH "/org/chromium/dhcpcd" #define S_EINVAL SERVICE_NAME ".InvalidArgument" #define S_ARGS "Not enough arguments" static DBusConnection *connection; static struct dhcpcd_ctx *dhcpcd_ctx; static const char dhcpcd_introspection_xml[] = " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n"; static const char service_watch_rule[] = "interface=" DBUS_INTERFACE_DBUS ",type=signal,member=NameOwnerChanged"; static const char introspection_header_xml[] = "\n" "\n" " \n" " \n" " \n" " \n" " \n" " \n"; static const char introspection_footer_xml[] = " \n" "\n"; static const struct o_dbus dhos[] = { { "ip_address=", DBUS_TYPE_UINT32, 0, "IPAddress" }, { "server_name=", DBUS_TYPE_STRING, 0, "ServerName"}, { "subnet_mask=", DBUS_TYPE_UINT32, 0, "SubnetMask" }, { "subnet_cidr=", DBUS_TYPE_BYTE, 0, "SubnetCIDR" }, { "network_number=", DBUS_TYPE_UINT32, 0, "NetworkNumber" }, { "classless_static_routes=", DBUS_TYPE_STRING, 0, "ClasslessStaticRoutes" }, { "ms_classless_static_routes=", DBUS_TYPE_STRING, 0, "MSClasslessStaticRoutes" }, { "static_routes=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "StaticRoutes"} , { "routers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "Routers" }, { "time_offset=", DBUS_TYPE_UINT32, 0, "TimeOffset" }, { "time_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "TimeServers" }, { "ien116_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "IEN116NameServers" }, { "domain_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "DomainNameServers" }, { "log_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LogServers" }, { "cookie_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "CookieServers" }, { "lpr_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "LPRServers" }, { "impress_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "ImpressServers" }, { "resource_location_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "ResourceLocationServers" }, { "host_name=", DBUS_TYPE_STRING, 0, "Hostname" }, { "boot_size=", DBUS_TYPE_UINT16, 0, "BootSize" }, { "merit_dump=", DBUS_TYPE_STRING, 0, "MeritDump" }, { "domain_name=", DBUS_TYPE_STRING, 0, "DomainName" }, { "swap_server=", DBUS_TYPE_UINT32, 0, "SwapServer" }, { "root_path=", DBUS_TYPE_STRING, 0, "RootPath" }, { "extensions_path=", DBUS_TYPE_STRING, 0, "ExtensionsPath" }, { "ip_forwarding=", DBUS_TYPE_BOOLEAN, 0, "IPForwarding" }, { "non_local_source_routing=", DBUS_TYPE_BOOLEAN, 0, "NonLocalSourceRouting" }, { "policy_filter=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "PolicyFilter" }, { "max_dgram_reassembly=", DBUS_TYPE_INT16, 0, "MaxDatagramReassembly" }, { "default_ip_ttl=", DBUS_TYPE_UINT16, 0, "DefaultIPTTL" }, { "path_mtu_aging_timeout=", DBUS_TYPE_UINT32, 0, "PathMTUAgingTimeout" }, { "path_mtu_plateau_table=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT16, "PolicyFilter"} , { "interface_mtu=", DBUS_TYPE_UINT16, 0, "InterfaceMTU" }, { "all_subnets_local=", DBUS_TYPE_BOOLEAN, 0, "AllSubnetsLocal" }, { "broadcast_address=", DBUS_TYPE_UINT32, 0, "BroadcastAddress" }, { "perform_mask_discovery=", DBUS_TYPE_BOOLEAN, 0, "PerformMaskDiscovery" }, { "mask_supplier=", DBUS_TYPE_BOOLEAN, 0, "MaskSupplier" }, { "router_discovery=", DBUS_TYPE_BOOLEAN, 0, "RouterDiscovery" }, { "router_solicitiation_address=", DBUS_TYPE_UINT32, 0, "RouterSolicationAddress" }, { "trailer_encapsulation=", DBUS_TYPE_BOOLEAN, 0, "TrailerEncapsulation" }, { "arp_cache_timeout=", DBUS_TYPE_UINT32, 0, "ARPCacheTimeout" }, { "ieee802_3_encapsulation=", DBUS_TYPE_UINT16, 0, "IEEE8023Encapsulation" }, { "default_tcp_ttl=", DBUS_TYPE_BYTE, 0, "DefaultTCPTTL" }, { "tcp_keepalive_interval=", DBUS_TYPE_UINT32, 0, "TCPKeepAliveInterval" }, { "tcp_keepalive_garbage=", DBUS_TYPE_BOOLEAN, 0, "TCPKeepAliveGarbage" }, { "nis_domain=", DBUS_TYPE_STRING, 0, "NISDomain" }, { "nis_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NISServers" }, { "ntp_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NTPServers" }, { "vendor_encapsulated_options=", DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, "VendorEncapsulatedOptions" }, { "netbios_name_servers=" ,DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NetBIOSNameServers" }, { "netbios_dd_server=", DBUS_TYPE_UINT32, 0, "NetBIOSDDServer" }, { "netbios_node_type=", DBUS_TYPE_BYTE, 0, "NetBIOSNodeType" }, { "netbios_scope=", DBUS_TYPE_STRING, 0, "NetBIOSScope" }, { "font_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "FontServers" }, { "x_display_manager=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "XDisplayManager" }, { "dhcp_requested_address=", DBUS_TYPE_UINT32, 0, "DHCPRequestedAddress" }, { "dhcp_lease_time=", DBUS_TYPE_UINT32, 0, "DHCPLeaseTime" }, { "dhcp_option_overload=", DBUS_TYPE_BOOLEAN, 0, "DHCPOptionOverload" }, { "dhcp_message_type=", DBUS_TYPE_BYTE, 0, "DHCPMessageType" }, { "dhcp_server_identifier=", DBUS_TYPE_UINT32, 0, "DHCPServerIdentifier" }, { "dhcp_message=", DBUS_TYPE_STRING, 0, "DHCPMessage" }, { "dhcp_max_message_size=", DBUS_TYPE_UINT16, 0, "DHCPMaxMessageSize" }, { "dhcp_renewal_time=", DBUS_TYPE_UINT32, 0, "DHCPRenewalTime" }, { "dhcp_rebinding_time=", DBUS_TYPE_UINT32, 0, "DHCPRebindingTime" }, { "nisplus_domain=", DBUS_TYPE_STRING, 0, "NISPlusDomain" }, { "nisplus_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NISPlusServers" }, { "tftp_server_name=", DBUS_TYPE_STRING, 0, "TFTPServerName" }, { "bootfile_name=", DBUS_TYPE_STRING, 0, "BootFileName" }, { "mobile_ip_home_agent=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "MobileIPHomeAgent" }, { "smtp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "SMTPServer" }, { "pop_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "POPServer" }, { "nntp_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NNTPServer" }, { "www_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "WWWServer" }, { "finger_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "FingerServer" }, { "irc_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "IRCServer" }, { "streettalk_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "StreetTalkServer" }, { "streettalk_directory_assistance_server=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "StreetTalkDirectoryAssistanceServer" }, { "user_class=", DBUS_TYPE_STRING, 0, "UserClass" }, { "new_fqdn_name=", DBUS_TYPE_STRING, 0, "FQDNName" }, { "nds_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NDSServers" }, { "nds_tree_name=", DBUS_TYPE_STRING, 0, "NDSTreeName" }, { "nds_context=", DBUS_TYPE_STRING, 0, "NDSContext" }, { "bcms_controller_names=", DBUS_TYPE_STRING, 0, "BCMSControllerNames" }, { "client_last_transaction_time=", DBUS_TYPE_UINT32, 0, "ClientLastTransactionTime" }, { "associated_ip=", DBUS_TYPE_UINT32, 0, "AssociatedIP" }, { "uap_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "UAPServers" }, { "netinfo_server_address=", DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, "NetinfoServerAddress" }, { "netinfo_server_tag=", DBUS_TYPE_STRING, 0, "NetinfoServerTag" }, { "default_url=", DBUS_TYPE_STRING, 0, "DefaultURL" }, { "subnet_selection=", DBUS_TYPE_UINT32, 0, "SubnetSelection" }, { "domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, "DomainSearch" }, { "wpad_url=", DBUS_TYPE_STRING, 0, "WebProxyAutoDiscoveryUrl" }, #ifdef INET6 { "dhcp6_server_id=", DBUS_TYPE_STRING, 0, "DHCPv6ServerIdentifier" }, { "dhcp6_ia_na1_ia_addr1=", DBUS_TYPE_STRING, 0, "DHCPv6Address" }, { "dhcp6_ia_na1_ia_addr1_vltime=", DBUS_TYPE_UINT32, 0, "DHCPv6AddressLeaseTime" }, { "dhcp6_name_servers=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, "DHCPv6NameServers" }, { "dhcp6_domain_search=", DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, "DHCPv6DomainSearch" }, { "dhcp6_ia_pd1_prefix1=", DBUS_TYPE_STRING, 0, "DHCPv6DelegatedPrefix" }, { "dhcp6_ia_pd1_prefix1_length=", DBUS_TYPE_UINT32, 0, "DHCPv6DelegatedPrefixLength" }, { "dhcp6_ia_pd1_prefix1_vltime=", DBUS_TYPE_UINT32, 0, "DHCPv6DelegatedPrefixLeaseTime" }, #endif { NULL, 0, 0, NULL } }; static int append_config(DBusMessageIter *iter, const char *prefix, char **env, ssize_t elen) { char **eenv, *p; const struct o_dbus *dhop; size_t l, lp; int retval; retval = 0; lp = strlen(prefix); for (eenv = env + elen; env < eenv; env++) { p = env[0]; for (dhop = dhos; dhop->var; dhop++) { l = strlen(dhop->var); if (strncmp(p, dhop->var, l) == 0) { retval = dict_append_config_item(iter, dhop, p + l); break; } if (strncmp(p, prefix, lp) == 0 && strncmp(p + lp, dhop->var, l) == 0) { retval = dict_append_config_item(iter, dhop, p + l + lp); break; } } if (retval == -1) break; } return retval; } static DBusHandlerResult get_dbus_error(DBusConnection *con, DBusMessage *msg, const char *name, const char *fmt, ...) { char buffer[1024]; DBusMessage *reply; va_list args; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); reply = dbus_message_new_error(msg, name, buffer); dbus_connection_send(con, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } static dbus_bool_t dbus_send_message(const struct interface *ifp, const char *reason, const char *prefix, struct dhcp_message *message) { const struct if_options *ifo = ifp->options; DBusMessage* msg; DBusMessageIter args, dict; int pid = getpid(); char **env = NULL; ssize_t e, elen; int retval; int success = FALSE; syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name); msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event"); if (msg == NULL) { syslog(LOG_ERR, "failed to make a configure message"); return FALSE; } dbus_message_iter_init_append(msg, &args); dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason); dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); if (prefix == NULL || message == NULL) retval = 0; else { e = dhcp_env(NULL, NULL, message, ifp); if (e > 0) { char *config_prefix = strdup(prefix); if (config_prefix == NULL) { logger(dhcpcd_ctx, LOG_ERR, "Memory exhausted (strdup)"); eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); } char *p = config_prefix + strlen(config_prefix) - 1; if (p >= config_prefix && *p == '_') *p = '\0'; env = calloc(e + 1, sizeof(char *)); if (env == NULL) { logger(dhcpcd_ctx, LOG_ERR, "Memory exhausted (calloc)"); eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); } elen = dhcp_env(env, config_prefix, message, ifp); free(config_prefix); } retval = append_config(&dict, prefix, env, elen); } /* Release memory allocated for env. */ if (env) { char **current = env; while (*current) free(*current++); free(env); } dbus_message_iter_close_container(&args, &dict); if (retval == 0) { success = dbus_connection_send(connection, msg, NULL); if (!success) syslog(LOG_ERR, "failed to send dhcp to dbus"); } else syslog(LOG_ERR, "failed to construct dbus message"); dbus_message_unref(msg); return success; } #ifdef INET6 static dbus_bool_t dbus_send_dhcpv6_message(const struct interface *ifp, const char *reason, const char *prefix, struct dhcp6_message *message, size_t length) { const struct if_options *ifo = ifp->options; DBusMessage* msg; DBusMessageIter args, dict; int pid = getpid(); char **env = NULL; ssize_t e, elen; int retval; int success = FALSE; syslog(LOG_INFO, "event %s on interface %s", reason, ifp->name); msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "Event"); if (msg == NULL) { syslog(LOG_ERR, "failed to make a configure message"); return FALSE; } dbus_message_iter_init_append(msg, &args); dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &reason); dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); if (prefix == NULL || message == NULL) retval = 0; else { e = dhcp6_env(NULL, NULL, ifp, message, length); if (e > 0) { char *config_prefix = strdup(prefix); if (config_prefix == NULL) { logger(dhcpcd_ctx, LOG_ERR, "Memory exhausted (strdup)"); eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); } char *p = config_prefix + strlen(config_prefix) - 1; if (p >= config_prefix && *p == '_') *p = '\0'; env = calloc(e + 1, sizeof(char *)); if (env == NULL) { logger(dhcpcd_ctx, LOG_ERR, "Memory exhausted (calloc)"); eloop_exit(dhcpcd_ctx->eloop, EXIT_FAILURE); } elen = dhcp6_env(env, "new", ifp, message, length); free(config_prefix); } retval = append_config(&dict, prefix, env, elen); } /* Release memory allocated for env. */ if (env) { char **current = env; while (*current) free(*current++); free(env); } dbus_message_iter_close_container(&args, &dict); if (retval == 0) { success = dbus_connection_send(connection, msg, NULL); if (!success) syslog(LOG_ERR, "failed to send dhcpv6 to dbus"); } else syslog(LOG_ERR, "failed to construct dbus message"); dbus_message_unref(msg); return success; } #endif static DBusHandlerResult introspect(DBusConnection *con, DBusMessage *msg) { DBusMessage *reply; char *xml; size_t len; len = sizeof(introspection_header_xml) - 1 + sizeof(dhcpcd_introspection_xml) - 1 + sizeof(introspection_footer_xml) - 1 + 1; /* terminal \0 */ xml = malloc(len); if (xml == NULL) return DBUS_HANDLER_RESULT_HANDLED; snprintf(xml, len, "%s%s%s", introspection_header_xml, dhcpcd_introspection_xml, introspection_footer_xml); reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID); dbus_connection_send(con, reply, NULL); dbus_message_unref(reply); free(xml); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult version(DBusConnection *con, DBusMessage *msg, const char *ver) { DBusMessage *reply; reply = dbus_message_new_method_return(msg); dbus_message_append_args(reply, DBUS_TYPE_STRING, &ver, DBUS_TYPE_INVALID); dbus_connection_send(con, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult dbus_ack(DBusConnection *con, DBusMessage *msg) { DBusMessage *reply; reply = dbus_message_new_method_return(msg); dbus_connection_send(con, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult msg_handler(DBusConnection *con, DBusMessage *msg, __unused void *data) { #define IsMethod(msg, method) \ dbus_message_is_method_call(msg, SERVICE_NAME, method) if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) { return introspect(con, msg); } else if (IsMethod(msg, "GetVersion")) { return version(con, msg, VERSION); } else if (IsMethod(msg, "Rebind")) { const char *iface_name; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &iface_name, DBUS_TYPE_INVALID)) { logger(dhcpcd_ctx, LOG_ERR, "Invalid arguments for Rebind"); return get_dbus_error(con, msg, S_EINVAL, S_ARGS); } dhcpcd_start_interface(dhcpcd_ctx, iface_name); return dbus_ack(con, msg); } else if (IsMethod(msg, "Release")) { const char *iface_name; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &iface_name, DBUS_TYPE_INVALID)) { logger(dhcpcd_ctx, LOG_ERR, "Invalid arguments for Release"); return get_dbus_error(con, msg, S_EINVAL, S_ARGS); } dhcpcd_release_ipv4(dhcpcd_ctx, iface_name); return dbus_ack(con, msg); } else if (IsMethod(msg, "Stop")) { const char *iface_name; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &iface_name, DBUS_TYPE_INVALID)) { logger(dhcpcd_ctx, LOG_ERR, "Invalid arguments for Stop"); return get_dbus_error(con, msg, S_EINVAL, S_ARGS); } dhcpcd_stop_interface(dhcpcd_ctx, iface_name); (void) dbus_ack(con, msg); exit(EXIT_FAILURE); } else if (dbus_message_is_signal(msg, DBUS_INTERFACE_LOCAL, "Disconnected")) { dhcpcd_stop_interfaces(dhcpcd_ctx); exit(EXIT_FAILURE); } return get_dbus_error(con, msg, S_EINVAL, S_ARGS); #undef IsMethod } static void dbus_handle_event(DBusWatch *watch, int flags) { dbus_watch_handle((DBusWatch *)watch, flags); if (connection != NULL) { dbus_connection_ref(connection); while (dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; dbus_connection_unref(connection); } } static void dbus_read_event(void *watch) { dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_READABLE); } static void dbus_write_event(void *watch) { dbus_handle_event((DBusWatch *)watch, DBUS_WATCH_WRITABLE); } static dbus_bool_t add_watch(DBusWatch *watch, __unused void *data) { int fd, flags; void (*read_event)(void *) = NULL; void *read_arg = NULL; void (*write_event)(void *) = NULL; void *write_arg = NULL; fd = dbus_watch_get_unix_fd(watch); flags = dbus_watch_get_flags(watch); if (flags & DBUS_WATCH_READABLE) { read_event = dbus_read_event; read_arg = watch; } if (flags & DBUS_WATCH_WRITABLE) { write_event = dbus_write_event; write_arg = watch; } if (eloop_event_add(dhcpcd_ctx->eloop, fd, read_event, read_arg, write_event, write_arg) == 0) return TRUE; return FALSE; } static void remove_watch(DBusWatch *watch, __unused void *data) { int fd, flags; int write_only = 0; fd = dbus_watch_get_unix_fd(watch); flags = dbus_watch_get_flags(watch); if (!(flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) write_only = 1; eloop_event_delete(dhcpcd_ctx->eloop, fd, write_only); } static DBusHandlerResult dhcpcd_dbus_filter(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *service = NULL; const char *old_owner = NULL; const char *new_owner = NULL; if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &service, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID)) { syslog(LOG_ERR, "Invalid arguments for NameOwnerChanged signal"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } if (strcmp(service, "org.chromium.flimflam") == 0 && strlen(new_owner) == 0) { syslog(LOG_INFO, "exiting because flimflamd has died"); dhcpcd_stop_interfaces(dhcpcd_ctx); exit(EXIT_FAILURE); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } int rpc_init(struct dhcpcd_ctx *ctx) { DBusObjectPathVTable vt = { NULL, &msg_handler, NULL, NULL, NULL, NULL }; DBusError err; int ret; dhcpcd_ctx = ctx; dbus_error_init(&err); connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err); if (connection == NULL) { if (dbus_error_is_set(&err)) syslog(LOG_ERR, "%s", err.message); else syslog(LOG_ERR, "failed to get a dbus connection"); return -1; } atexit(rpc_close); if (!dbus_connection_set_watch_functions(connection, add_watch, remove_watch, NULL, NULL, NULL)) { syslog(LOG_ERR, "dbus: failed to set watch functions"); return -1; } if (!dbus_connection_register_object_path(connection, SERVICE_PATH, &vt, NULL)) { syslog(LOG_ERR, "dbus: failed to register object path"); return -1; } dbus_connection_add_filter(connection, dhcpcd_dbus_filter, NULL, NULL); dbus_bus_add_match(connection, service_watch_rule, &err); if (dbus_error_is_set(&err)) { syslog(LOG_ERR, "Cannot add rule: %s", err.message); return -1; } return 0; } void rpc_close(void) { if (connection) { dbus_bus_remove_match(connection, service_watch_rule, NULL); dbus_connection_remove_filter(connection, dhcpcd_dbus_filter, NULL); dbus_connection_unref(connection); connection = NULL; } } void rpc_signal_status(const char *status) { DBusMessage *msg; DBusMessageIter args; int pid = getpid(); syslog(LOG_INFO, "status changed to %s", status); msg = dbus_message_new_signal(SERVICE_PATH, SERVICE_NAME, "StatusChanged"); if (msg == NULL) { syslog(LOG_ERR, "failed to make a status changed message"); return; } dbus_message_iter_init_append(msg, &args); dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid); dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &status); if (!dbus_connection_send(connection, msg, NULL)) syslog(LOG_ERR, "failed to send status to dbus"); dbus_message_unref(msg); } int rpc_update_ipv4(struct interface *ifp) { struct dhcp_state *state = D_STATE(ifp); if (state->new != NULL) { /* push state over d-bus */ dbus_send_message(ifp, state->reason, "new_", state->new); rpc_signal_status("Bound"); } else { rpc_signal_status("Release"); } return 0; } #ifdef INET6 int rpc_update_ipv6(struct interface *ifp) { struct dhcp6_state *state = D6_STATE(ifp); if (state->new != NULL) { /* push state over d-bus */ dbus_send_dhcpv6_message(ifp, state->reason, "new_", state->new, state->new_len); rpc_signal_status("Bound6"); } else { rpc_signal_status("Release6"); } return 0; } #endif int rpc_notify_unicast_arp(struct interface *ifp) { struct dhcp_state *state = D_STATE(ifp); return dbus_send_message(ifp, "GATEWAY-ARP", "saved_", state->offer); }