diff options
Diffstat (limited to 'dispatcher')
| -rw-r--r-- | dispatcher/nm-dispatcher-utils.c | 978 | ||||
| -rw-r--r-- | dispatcher/nm-dispatcher-utils.h | 38 | ||||
| -rw-r--r-- | dispatcher/nm-dispatcher.c | 1768 | ||||
| -rw-r--r-- | dispatcher/tests/test-dispatcher-envp.c | 1087 |
4 files changed, 1915 insertions, 1956 deletions
diff --git a/dispatcher/nm-dispatcher-utils.c b/dispatcher/nm-dispatcher-utils.c index 13a7f789..57a7864c 100644 --- a/dispatcher/nm-dispatcher-utils.c +++ b/dispatcher/nm-dispatcher-utils.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2008 - 2011 Red Hat, Inc. */ @@ -19,579 +19,563 @@ /*****************************************************************************/ static gboolean -_is_valid_key (const char *line, gssize len) +_is_valid_key(const char *line, gssize len) { - gsize i, l; - char ch; + gsize i, l; + char ch; - if (!line) - return FALSE; + if (!line) + return FALSE; - if (len < 0) - len = strlen (line); + if (len < 0) + len = strlen(line); - if (len == 0) - return FALSE; + if (len == 0) + return FALSE; - ch = line[0]; - if ( !(ch >= 'A' && ch <= 'Z') - && !NM_IN_SET (ch, '_')) - return FALSE; + ch = line[0]; + if (!(ch >= 'A' && ch <= 'Z') && !NM_IN_SET(ch, '_')) + return FALSE; - l = (gsize) len; + l = (gsize) len; - for (i = 1; i < l; i++) { - ch = line[i]; + for (i = 1; i < l; i++) { + ch = line[i]; - if ( !(ch >= 'A' && ch <= 'Z') - && !(ch >= '0' && ch <= '9') - && !NM_IN_SET (ch, '_')) - return FALSE; - } + if (!(ch >= 'A' && ch <= 'Z') && !(ch >= '0' && ch <= '9') && !NM_IN_SET(ch, '_')) + return FALSE; + } - return TRUE; + return TRUE; } static gboolean -_is_valid_line (const char *line) +_is_valid_line(const char *line) { - const char *d; + const char *d; - if (!line) - return FALSE; + if (!line) + return FALSE; - d = strchr (line, '='); - if (!d || d == line) - return FALSE; + d = strchr(line, '='); + if (!d || d == line) + return FALSE; - return _is_valid_key (line, d - line); + return _is_valid_key(line, d - line); } static char * -_sanitize_var_name (const char *key) +_sanitize_var_name(const char *key) { - char *sanitized; + char *sanitized; - nm_assert (key); + nm_assert(key); - if (!key[0]) - return NULL; + if (!key[0]) + return NULL; - sanitized = g_ascii_strup (key, -1); - if (!NM_STRCHAR_ALL (sanitized, ch, (ch >= 'A' && ch <= 'Z') - || (ch >= '0' && ch <= '9') - || NM_IN_SET (ch, '_'))) { - g_free (sanitized); - return NULL; - } + sanitized = g_ascii_strup(key, -1); + if (!NM_STRCHAR_ALL(sanitized, + ch, + (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') + || NM_IN_SET(ch, '_'))) { + g_free(sanitized); + return NULL; + } - nm_assert (_is_valid_key (sanitized, -1)); - return sanitized; + nm_assert(_is_valid_key(sanitized, -1)); + return sanitized; } static void -_items_add_str_take (GPtrArray *items, char *line) +_items_add_str_take(GPtrArray *items, char *line) { - nm_assert (items); - nm_assert (_is_valid_line (line)); + nm_assert(items); + nm_assert(_is_valid_line(line)); - g_ptr_array_add (items, line); + g_ptr_array_add(items, line); } static void -_items_add_str (GPtrArray *items, const char *line) +_items_add_str(GPtrArray *items, const char *line) { - _items_add_str_take (items, g_strdup (line)); + _items_add_str_take(items, g_strdup(line)); } static void -_items_add_key (GPtrArray *items, const char *prefix, const char *key, const char *value) +_items_add_key(GPtrArray *items, const char *prefix, const char *key, const char *value) { - nm_assert (items); - nm_assert (_is_valid_key (key, -1)); - nm_assert (value); + nm_assert(items); + nm_assert(_is_valid_key(key, -1)); + nm_assert(value); - _items_add_str_take (items, g_strconcat (prefix ?: "", key, "=", value, NULL)); + _items_add_str_take(items, g_strconcat(prefix ?: "", key, "=", value, NULL)); } static void -_items_add_key0 (GPtrArray *items, const char *prefix, const char *key, const char *value) +_items_add_key0(GPtrArray *items, const char *prefix, const char *key, const char *value) { - nm_assert (items); - nm_assert (_is_valid_key (key, -1)); + nm_assert(items); + nm_assert(_is_valid_key(key, -1)); - if (!value) { - /* for convenience, allow NULL values to indicate to skip the line. */ - return; - } + if (!value) { + /* for convenience, allow NULL values to indicate to skip the line. */ + return; + } - _items_add_str_take (items, g_strconcat (prefix ?: "", key, "=", value, NULL)); + _items_add_str_take(items, g_strconcat(prefix ?: "", key, "=", value, NULL)); } -G_GNUC_PRINTF (2, 3) +G_GNUC_PRINTF(2, 3) static void -_items_add_printf (GPtrArray *items, const char *fmt, ...) +_items_add_printf(GPtrArray *items, const char *fmt, ...) { - va_list ap; - char *line; + va_list ap; + char * line; - nm_assert (items); - nm_assert (fmt); + nm_assert(items); + nm_assert(fmt); - va_start (ap, fmt); - line = g_strdup_vprintf (fmt, ap); - va_end (ap); - _items_add_str_take (items, line); + va_start(ap, fmt); + line = g_strdup_vprintf(fmt, ap); + va_end(ap); + _items_add_str_take(items, line); } static void -_items_add_strv (GPtrArray *items, const char *prefix, const char *key, const char *const*values) +_items_add_strv(GPtrArray *items, const char *prefix, const char *key, const char *const *values) { - gboolean has; - guint i; - GString *str; - - nm_assert (items); - nm_assert (_is_valid_key (key, -1)); - - if (!values || !values[0]) { - /* Only add an item if the list of @values is not empty */ - return; - } - - str = g_string_new (NULL); - - if (prefix) - g_string_append (str, prefix); - g_string_append (str, key); - g_string_append_c (str, '='); - - has = FALSE; - for (i = 0; values[i]; i++) { - if (!values[i][0]) - continue; - if (has) - g_string_append_c (str, ' '); - else - has = TRUE; - g_string_append (str, values[i]); - } - - _items_add_str_take (items, g_string_free (str, FALSE)); + gboolean has; + guint i; + GString *str; + + nm_assert(items); + nm_assert(_is_valid_key(key, -1)); + + if (!values || !values[0]) { + /* Only add an item if the list of @values is not empty */ + return; + } + + str = g_string_new(NULL); + + if (prefix) + g_string_append(str, prefix); + g_string_append(str, key); + g_string_append_c(str, '='); + + has = FALSE; + for (i = 0; values[i]; i++) { + if (!values[i][0]) + continue; + if (has) + g_string_append_c(str, ' '); + else + has = TRUE; + g_string_append(str, values[i]); + } + + _items_add_str_take(items, g_string_free(str, FALSE)); } /*****************************************************************************/ static void -construct_proxy_items (GPtrArray *items, GVariant *proxy_config, const char *prefix) +construct_proxy_items(GPtrArray *items, GVariant *proxy_config, const char *prefix) { - GVariant *variant; - - nm_assert (items); - - if (!proxy_config) - return; - - variant = g_variant_lookup_value (proxy_config, "pac-url", G_VARIANT_TYPE_STRING); - if (variant) { - _items_add_key (items, prefix, "PROXY_PAC_URL", - g_variant_get_string (variant, NULL)); - g_variant_unref (variant); - } - - variant = g_variant_lookup_value (proxy_config, "pac-script", G_VARIANT_TYPE_STRING); - if (variant) { - _items_add_key (items, prefix, "PROXY_PAC_SCRIPT", - g_variant_get_string (variant, NULL)); - g_variant_unref (variant); - } + GVariant *variant; + + nm_assert(items); + + if (!proxy_config) + return; + + variant = g_variant_lookup_value(proxy_config, "pac-url", G_VARIANT_TYPE_STRING); + if (variant) { + _items_add_key(items, prefix, "PROXY_PAC_URL", g_variant_get_string(variant, NULL)); + g_variant_unref(variant); + } + + variant = g_variant_lookup_value(proxy_config, "pac-script", G_VARIANT_TYPE_STRING); + if (variant) { + _items_add_key(items, prefix, "PROXY_PAC_SCRIPT", g_variant_get_string(variant, NULL)); + g_variant_unref(variant); + } } static void -construct_ip_items (GPtrArray *items, int addr_family, GVariant *ip_config, const char *prefix) +construct_ip_items(GPtrArray *items, int addr_family, GVariant *ip_config, const char *prefix) { - GVariant *val; - guint i; - guint nroutes = 0; - char four_or_six; - - if (!ip_config) - return; - - if (!prefix) - prefix = ""; - - four_or_six = nm_utils_addr_family_to_char (addr_family); - - val = g_variant_lookup_value (ip_config, - "addresses", - addr_family == AF_INET - ? G_VARIANT_TYPE ("aau") - : G_VARIANT_TYPE ("a(ayuay)")); - if (val) { - gs_unref_ptrarray GPtrArray *addresses = NULL; - gs_free char *gateway_free = NULL; - const char *gateway; - - if (addr_family == AF_INET) - addresses = nm_utils_ip4_addresses_from_variant (val, &gateway_free); - else - addresses = nm_utils_ip6_addresses_from_variant (val, &gateway_free); - - gateway = gateway_free ?: "0.0.0.0"; - - if (addresses && addresses->len) { - for (i = 0; i < addresses->len; i++) { - NMIPAddress *addr = addresses->pdata[i]; - - _items_add_printf (items, - "%sIP%c_ADDRESS_%d=%s/%d %s", - prefix, - four_or_six, - i, - nm_ip_address_get_address (addr), - nm_ip_address_get_prefix (addr), - gateway); - } - - _items_add_printf (items, - "%sIP%c_NUM_ADDRESSES=%u", - prefix, - four_or_six, - addresses->len); - } - - _items_add_key (items, - prefix, - addr_family == AF_INET - ? "IP4_GATEWAY" - : "IP6_GATEWAY", - gateway); - - g_variant_unref (val); - } - - val = g_variant_lookup_value (ip_config, - "nameservers", - addr_family == AF_INET - ? G_VARIANT_TYPE ("au") - : G_VARIANT_TYPE ("aay")); - if (val) { - gs_strfreev char **v = NULL; - - if (addr_family == AF_INET) - v = nm_utils_ip4_dns_from_variant (val); - else - v = nm_utils_ip6_dns_from_variant (val); - _items_add_strv (items, - prefix, - addr_family == AF_INET - ? "IP4_NAMESERVERS" - : "IP6_NAMESERVERS", - NM_CAST_STRV_CC (v)); - g_variant_unref (val); - } - - val = g_variant_lookup_value (ip_config, "domains", G_VARIANT_TYPE_STRING_ARRAY); - if (val) { - gs_free const char **v = NULL; - - v = g_variant_get_strv (val, NULL); - _items_add_strv (items, prefix, - addr_family == AF_INET - ? "IP4_DOMAINS" - : "IP6_DOMAINS", - v); - g_variant_unref (val); - } - - - if (addr_family == AF_INET) { - val = g_variant_lookup_value (ip_config, "wins-servers", G_VARIANT_TYPE ("au")); - if (val) { - gs_strfreev char **v = NULL; - - v = nm_utils_ip4_dns_from_variant (val); - _items_add_strv (items, prefix, "IP4_WINS_SERVERS", NM_CAST_STRV_CC (v)); - g_variant_unref (val); - } - } - - val = g_variant_lookup_value (ip_config, - "routes", - addr_family == AF_INET - ? G_VARIANT_TYPE ("aau") - : G_VARIANT_TYPE ("a(ayuayu)")); - if (val) { - gs_unref_ptrarray GPtrArray *routes = NULL; - - if (addr_family == AF_INET) - routes = nm_utils_ip4_routes_from_variant (val); - else - routes = nm_utils_ip6_routes_from_variant (val); - - if ( routes - && routes->len > 0) { - const char *const DEFAULT_GW = addr_family == AF_INET ? "0.0.0.0" : "::"; - - nroutes = routes->len; - - for (i = 0; i < routes->len; i++) { - NMIPRoute *route = routes->pdata[i]; - - _items_add_printf (items, - "%sIP%c_ROUTE_%u=%s/%d %s %u", - prefix, - four_or_six, - i, - nm_ip_route_get_dest (route), - nm_ip_route_get_prefix (route), - nm_ip_route_get_next_hop (route) ?: DEFAULT_GW, - (guint) NM_MAX ((gint64) 0, nm_ip_route_get_metric (route))); - } - } - - g_variant_unref (val); - } - if (nroutes > 0 || addr_family == AF_INET) { - /* we also set IP4_NUM_ROUTES=0, but don't do so for addresses and IPv6 routes. - * Historic reasons. */ - _items_add_printf (items, "%sIP%c_NUM_ROUTES=%u", prefix, four_or_six, nroutes); - } + GVariant *val; + guint i; + guint nroutes = 0; + char four_or_six; + + if (!ip_config) + return; + + if (!prefix) + prefix = ""; + + four_or_six = nm_utils_addr_family_to_char(addr_family); + + val = g_variant_lookup_value(ip_config, + "addresses", + addr_family == AF_INET ? G_VARIANT_TYPE("aau") + : G_VARIANT_TYPE("a(ayuay)")); + if (val) { + gs_unref_ptrarray GPtrArray *addresses = NULL; + gs_free char * gateway_free = NULL; + const char * gateway; + + if (addr_family == AF_INET) + addresses = nm_utils_ip4_addresses_from_variant(val, &gateway_free); + else + addresses = nm_utils_ip6_addresses_from_variant(val, &gateway_free); + + gateway = gateway_free ?: "0.0.0.0"; + + if (addresses && addresses->len) { + for (i = 0; i < addresses->len; i++) { + NMIPAddress *addr = addresses->pdata[i]; + + _items_add_printf(items, + "%sIP%c_ADDRESS_%d=%s/%d %s", + prefix, + four_or_six, + i, + nm_ip_address_get_address(addr), + nm_ip_address_get_prefix(addr), + gateway); + } + + _items_add_printf(items, + "%sIP%c_NUM_ADDRESSES=%u", + prefix, + four_or_six, + addresses->len); + } + + _items_add_key(items, + prefix, + addr_family == AF_INET ? "IP4_GATEWAY" : "IP6_GATEWAY", + gateway); + + g_variant_unref(val); + } + + val = g_variant_lookup_value(ip_config, + "nameservers", + addr_family == AF_INET ? G_VARIANT_TYPE("au") + : G_VARIANT_TYPE("aay")); + if (val) { + gs_strfreev char **v = NULL; + + if (addr_family == AF_INET) + v = nm_utils_ip4_dns_from_variant(val); + else + v = nm_utils_ip6_dns_from_variant(val); + _items_add_strv(items, + prefix, + addr_family == AF_INET ? "IP4_NAMESERVERS" : "IP6_NAMESERVERS", + NM_CAST_STRV_CC(v)); + g_variant_unref(val); + } + + val = g_variant_lookup_value(ip_config, "domains", G_VARIANT_TYPE_STRING_ARRAY); + if (val) { + gs_free const char **v = NULL; + + v = g_variant_get_strv(val, NULL); + _items_add_strv(items, prefix, addr_family == AF_INET ? "IP4_DOMAINS" : "IP6_DOMAINS", v); + g_variant_unref(val); + } + + if (addr_family == AF_INET) { + val = g_variant_lookup_value(ip_config, "wins-servers", G_VARIANT_TYPE("au")); + if (val) { + gs_strfreev char **v = NULL; + + v = nm_utils_ip4_dns_from_variant(val); + _items_add_strv(items, prefix, "IP4_WINS_SERVERS", NM_CAST_STRV_CC(v)); + g_variant_unref(val); + } + } + + val = g_variant_lookup_value(ip_config, + "routes", + addr_family == AF_INET ? G_VARIANT_TYPE("aau") + : G_VARIANT_TYPE("a(ayuayu)")); + if (val) { + gs_unref_ptrarray GPtrArray *routes = NULL; + + if (addr_family == AF_INET) + routes = nm_utils_ip4_routes_from_variant(val); + else + routes = nm_utils_ip6_routes_from_variant(val); + + if (routes && routes->len > 0) { + const char *const DEFAULT_GW = addr_family == AF_INET ? "0.0.0.0" : "::"; + + nroutes = routes->len; + + for (i = 0; i < routes->len; i++) { + NMIPRoute *route = routes->pdata[i]; + + _items_add_printf(items, + "%sIP%c_ROUTE_%u=%s/%d %s %u", + prefix, + four_or_six, + i, + nm_ip_route_get_dest(route), + nm_ip_route_get_prefix(route), + nm_ip_route_get_next_hop(route) ?: DEFAULT_GW, + (guint) NM_MAX((gint64) 0, nm_ip_route_get_metric(route))); + } + } + + g_variant_unref(val); + } + if (nroutes > 0 || addr_family == AF_INET) { + /* we also set IP4_NUM_ROUTES=0, but don't do so for addresses and IPv6 routes. + * Historic reasons. */ + _items_add_printf(items, "%sIP%c_NUM_ROUTES=%u", prefix, four_or_six, nroutes); + } } static void -construct_device_dhcp_items (GPtrArray *items, int addr_family, GVariant *dhcp_config) +construct_device_dhcp_items(GPtrArray *items, int addr_family, GVariant *dhcp_config) { - GVariantIter iter; - const char *key; - GVariant *val; - char four_or_six; - gboolean found_unknown_245 = FALSE; - gs_unref_variant GVariant *private_245_val = NULL; - - if (!dhcp_config) - return; - - if (!g_variant_is_of_type (dhcp_config, G_VARIANT_TYPE_VARDICT)) - return; - - four_or_six = nm_utils_addr_family_to_char (addr_family); - - g_variant_iter_init (&iter, dhcp_config); - while (g_variant_iter_next (&iter, "{&sv}", &key, &val)) { - if (g_variant_is_of_type (val, G_VARIANT_TYPE_STRING)) { - gs_free char *ucased = NULL; - - ucased = _sanitize_var_name (key); - if (ucased) { - _items_add_printf (items, - "DHCP%c_%s=%s", - four_or_six, - ucased, - g_variant_get_string (val, NULL)); - - /* MS Azure sends the server endpoint in the dhcp private - * option 245. cloud-init searches the Azure server endpoint - * value looking for the standard dhclient label used for - * that option, which is "unknown_245". - * The 11-dhclient script shipped with Fedora and RHEL dhcp - * package converts our dispatcher environment vars to the - * dhclient ones (new_<some_option>) and calls dhclient hook - * scripts. - * Let's make cloud-init happy and let's duplicate the dhcp - * option 245 with the legacy name of the default dhclient - * label also when using the internal client. - * Note however that the dhclient plugin will have unknown_ - * labels represented as ascii string when possible, falling - * back to hex string otherwise. - * private_ labels instead are always in hex string format. - * This shouldn't affect the MS Azure server endpoint value, - * as it usually belongs to the 240.0.0.0/4 network and so - * is always represented as an hex string. Moreover, cloudinit - * code checks just for an hex value in unknown_245. - */ - if (addr_family == AF_INET) { - if (nm_streq (key, "private_245")) - private_245_val = g_variant_ref (val); - else if (nm_streq (key, "unknown_245")) - found_unknown_245 = true; - } - } - } - g_variant_unref (val); - } - - if (private_245_val != NULL && !found_unknown_245) { - _items_add_printf (items, - "DHCP4_UNKNOWN_245=%s", - g_variant_get_string (private_245_val, NULL)); - } + GVariantIter iter; + const char * key; + GVariant * val; + char four_or_six; + gboolean found_unknown_245 = FALSE; + gs_unref_variant GVariant *private_245_val = NULL; + + if (!dhcp_config) + return; + + if (!g_variant_is_of_type(dhcp_config, G_VARIANT_TYPE_VARDICT)) + return; + + four_or_six = nm_utils_addr_family_to_char(addr_family); + + g_variant_iter_init(&iter, dhcp_config); + while (g_variant_iter_next(&iter, "{&sv}", &key, &val)) { + if (g_variant_is_of_type(val, G_VARIANT_TYPE_STRING)) { + gs_free char *ucased = NULL; + + ucased = _sanitize_var_name(key); + if (ucased) { + _items_add_printf(items, + "DHCP%c_%s=%s", + four_or_six, + ucased, + g_variant_get_string(val, NULL)); + + /* MS Azure sends the server endpoint in the dhcp private + * option 245. cloud-init searches the Azure server endpoint + * value looking for the standard dhclient label used for + * that option, which is "unknown_245". + * The 11-dhclient script shipped with Fedora and RHEL dhcp + * package converts our dispatcher environment vars to the + * dhclient ones (new_<some_option>) and calls dhclient hook + * scripts. + * Let's make cloud-init happy and let's duplicate the dhcp + * option 245 with the legacy name of the default dhclient + * label also when using the internal client. + * Note however that the dhclient plugin will have unknown_ + * labels represented as ascii string when possible, falling + * back to hex string otherwise. + * private_ labels instead are always in hex string format. + * This shouldn't affect the MS Azure server endpoint value, + * as it usually belongs to the 240.0.0.0/4 network and so + * is always represented as an hex string. Moreover, cloudinit + * code checks just for an hex value in unknown_245. + */ + if (addr_family == AF_INET) { + if (nm_streq(key, "private_245")) + private_245_val = g_variant_ref(val); + else if (nm_streq(key, "unknown_245")) + found_unknown_245 = true; + } + } + } + g_variant_unref(val); + } + + if (private_245_val != NULL && !found_unknown_245) { + _items_add_printf(items, + "DHCP4_UNKNOWN_245=%s", + g_variant_get_string(private_245_val, NULL)); + } } /*****************************************************************************/ char ** -nm_dispatcher_utils_construct_envp (const char *action, - GVariant *connection_dict, - GVariant *connection_props, - GVariant *device_props, - GVariant *device_proxy_props, - GVariant *device_ip4_props, - GVariant *device_ip6_props, - GVariant *device_dhcp4_props, - GVariant *device_dhcp6_props, - const char *connectivity_state, - const char *vpn_ip_iface, - GVariant *vpn_proxy_props, - GVariant *vpn_ip4_props, - GVariant *vpn_ip6_props, - char **out_iface, - const char **out_error_message) +nm_dispatcher_utils_construct_envp(const char * action, + GVariant * connection_dict, + GVariant * connection_props, + GVariant * device_props, + GVariant * device_proxy_props, + GVariant * device_ip4_props, + GVariant * device_ip6_props, + GVariant * device_dhcp4_props, + GVariant * device_dhcp6_props, + const char * connectivity_state, + const char * vpn_ip_iface, + GVariant * vpn_proxy_props, + GVariant * vpn_ip4_props, + GVariant * vpn_ip6_props, + char ** out_iface, + const char **out_error_message) { - const char *iface = NULL; - const char *ip_iface = NULL; - const char *uuid = NULL; - const char *id = NULL; - const char *path = NULL; - const char *filename = NULL; - gboolean external; - NMDeviceState dev_state = NM_DEVICE_STATE_UNKNOWN; - GVariant *variant; - gs_unref_ptrarray GPtrArray *items = NULL; - const char *error_message_backup; - - if (!out_error_message) - out_error_message = &error_message_backup; - - g_return_val_if_fail (action != NULL, NULL); - g_return_val_if_fail (out_iface != NULL, NULL); - g_return_val_if_fail (*out_iface == NULL, NULL); - - items = g_ptr_array_new_with_free_func (g_free); - - /* Hostname and connectivity changes don't require a device nor contain a connection */ - if (NM_IN_STRSET (action, NMD_ACTION_HOSTNAME, - NMD_ACTION_CONNECTIVITY_CHANGE)) - goto done; - - /* Connection properties */ - if (g_variant_lookup (connection_props, NMD_CONNECTION_PROPS_PATH, "&o", &path)) - _items_add_key (items, NULL, "CONNECTION_DBUS_PATH", path); - - if (g_variant_lookup (connection_props, NMD_CONNECTION_PROPS_EXTERNAL, "b", &external) && external) - _items_add_str (items, "CONNECTION_EXTERNAL=1"); - - if (g_variant_lookup (connection_props, NMD_CONNECTION_PROPS_FILENAME, "&s", &filename)) - _items_add_key (items, NULL, "CONNECTION_FILENAME", filename); - - /* Canonicalize the VPN interface name; "" is used when passing it through - * D-Bus so make sure that's fixed up here. - */ - if (vpn_ip_iface && !vpn_ip_iface[0]) - vpn_ip_iface = NULL; - - if (!g_variant_lookup (device_props, NMD_DEVICE_PROPS_INTERFACE, "&s", &iface)) { - *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_INTERFACE "!"; - return NULL; - } - if (!*iface) - iface = NULL; - - variant = g_variant_lookup_value (device_props, NMD_DEVICE_PROPS_IP_INTERFACE, NULL); - if (variant) { - if (!g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { - *out_error_message = "Invalid value " NMD_DEVICE_PROPS_IP_INTERFACE "!"; - return NULL; - } - g_variant_unref (variant); - (void) g_variant_lookup (device_props, NMD_DEVICE_PROPS_IP_INTERFACE, "&s", &ip_iface); - } - - if (!g_variant_lookup (device_props, NMD_DEVICE_PROPS_TYPE, "u", NULL)) { - *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_TYPE "!"; - return NULL; - } - - variant = g_variant_lookup_value (device_props, NMD_DEVICE_PROPS_STATE, G_VARIANT_TYPE_UINT32); - if (!variant) { - *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_STATE "!"; - return NULL; - } - dev_state = g_variant_get_uint32 (variant); - g_variant_unref (variant); - - if (!g_variant_lookup (device_props, NMD_DEVICE_PROPS_PATH, "o", NULL)) { - *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_PATH "!"; - return NULL; - } - - { - gs_unref_variant GVariant *con_setting = NULL; - - con_setting = g_variant_lookup_value (connection_dict, NM_SETTING_CONNECTION_SETTING_NAME, NM_VARIANT_TYPE_SETTING); - if (!con_setting) { - *out_error_message = "Failed to read connection setting"; - return NULL; - } - - if (!g_variant_lookup (con_setting, NM_SETTING_CONNECTION_UUID, "&s", &uuid)) { - *out_error_message = "Connection hash did not contain the UUID"; - return NULL; - } - - if (!g_variant_lookup (con_setting, NM_SETTING_CONNECTION_ID, "&s", &id)) { - *out_error_message = "Connection hash did not contain the ID"; - return NULL; - } - - _items_add_key0 (items, NULL, "CONNECTION_UUID", uuid); - _items_add_key0 (items, NULL, "CONNECTION_ID", id); - _items_add_key0 (items, NULL, "DEVICE_IFACE", iface); - _items_add_key0 (items, NULL, "DEVICE_IP_IFACE", ip_iface); - } - - /* Device items aren't valid if the device isn't activated */ - if ( iface - && dev_state == NM_DEVICE_STATE_ACTIVATED) { - construct_proxy_items (items, device_proxy_props, NULL); - construct_ip_items (items, AF_INET, device_ip4_props, NULL); - construct_ip_items (items, AF_INET6, device_ip6_props, NULL); - construct_device_dhcp_items (items, AF_INET, device_dhcp4_props); - construct_device_dhcp_items (items, AF_INET6, device_dhcp6_props); - } - - if (vpn_ip_iface) { - _items_add_key (items, NULL, "VPN_IP_IFACE", vpn_ip_iface); - construct_proxy_items (items, vpn_proxy_props, "VPN_"); - construct_ip_items (items, AF_INET, vpn_ip4_props, "VPN_"); - construct_ip_items (items, AF_INET6, vpn_ip6_props, "VPN_"); - } - - /* Backwards compat: 'iface' is set in this order: - * 1) VPN interface name - * 2) Device IP interface name - * 3) Device interface anme - */ - if (vpn_ip_iface) - *out_iface = g_strdup (vpn_ip_iface); - else if (ip_iface) - *out_iface = g_strdup (ip_iface); - else - *out_iface = g_strdup (iface); + const char * iface = NULL; + const char * ip_iface = NULL; + const char * uuid = NULL; + const char * id = NULL; + const char * path = NULL; + const char * filename = NULL; + gboolean external; + NMDeviceState dev_state = NM_DEVICE_STATE_UNKNOWN; + GVariant * variant; + gs_unref_ptrarray GPtrArray *items = NULL; + const char * error_message_backup; + + if (!out_error_message) + out_error_message = &error_message_backup; + + g_return_val_if_fail(action != NULL, NULL); + g_return_val_if_fail(out_iface != NULL, NULL); + g_return_val_if_fail(*out_iface == NULL, NULL); + + items = g_ptr_array_new_with_free_func(g_free); + + /* Hostname and connectivity changes don't require a device nor contain a connection */ + if (NM_IN_STRSET(action, NMD_ACTION_HOSTNAME, NMD_ACTION_CONNECTIVITY_CHANGE)) + goto done; + + /* Connection properties */ + if (g_variant_lookup(connection_props, NMD_CONNECTION_PROPS_PATH, "&o", &path)) + _items_add_key(items, NULL, "CONNECTION_DBUS_PATH", path); + + if (g_variant_lookup(connection_props, NMD_CONNECTION_PROPS_EXTERNAL, "b", &external) + && external) + _items_add_str(items, "CONNECTION_EXTERNAL=1"); + + if (g_variant_lookup(connection_props, NMD_CONNECTION_PROPS_FILENAME, "&s", &filename)) + _items_add_key(items, NULL, "CONNECTION_FILENAME", filename); + + /* Canonicalize the VPN interface name; "" is used when passing it through + * D-Bus so make sure that's fixed up here. + */ + if (vpn_ip_iface && !vpn_ip_iface[0]) + vpn_ip_iface = NULL; + + if (!g_variant_lookup(device_props, NMD_DEVICE_PROPS_INTERFACE, "&s", &iface)) { + *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_INTERFACE "!"; + return NULL; + } + if (!*iface) + iface = NULL; + + variant = g_variant_lookup_value(device_props, NMD_DEVICE_PROPS_IP_INTERFACE, NULL); + if (variant) { + if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_STRING)) { + *out_error_message = "Invalid value " NMD_DEVICE_PROPS_IP_INTERFACE "!"; + return NULL; + } + g_variant_unref(variant); + (void) g_variant_lookup(device_props, NMD_DEVICE_PROPS_IP_INTERFACE, "&s", &ip_iface); + } + + if (!g_variant_lookup(device_props, NMD_DEVICE_PROPS_TYPE, "u", NULL)) { + *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_TYPE "!"; + return NULL; + } + + variant = g_variant_lookup_value(device_props, NMD_DEVICE_PROPS_STATE, G_VARIANT_TYPE_UINT32); + if (!variant) { + *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_STATE "!"; + return NULL; + } + dev_state = g_variant_get_uint32(variant); + g_variant_unref(variant); + + if (!g_variant_lookup(device_props, NMD_DEVICE_PROPS_PATH, "o", NULL)) { + *out_error_message = "Missing or invalid required value " NMD_DEVICE_PROPS_PATH "!"; + return NULL; + } + + { + gs_unref_variant GVariant *con_setting = NULL; + + con_setting = g_variant_lookup_value(connection_dict, + NM_SETTING_CONNECTION_SETTING_NAME, + NM_VARIANT_TYPE_SETTING); + if (!con_setting) { + *out_error_message = "Failed to read connection setting"; + return NULL; + } + + if (!g_variant_lookup(con_setting, NM_SETTING_CONNECTION_UUID, "&s", &uuid)) { + *out_error_message = "Connection hash did not contain the UUID"; + return NULL; + } + + if (!g_variant_lookup(con_setting, NM_SETTING_CONNECTION_ID, "&s", &id)) { + *out_error_message = "Connection hash did not contain the ID"; + return NULL; + } + + _items_add_key0(items, NULL, "CONNECTION_UUID", uuid); + _items_add_key0(items, NULL, "CONNECTION_ID", id); + _items_add_key0(items, NULL, "DEVICE_IFACE", iface); + _items_add_key0(items, NULL, "DEVICE_IP_IFACE", ip_iface); + } + + /* Device items aren't valid if the device isn't activated */ + if (iface && dev_state == NM_DEVICE_STATE_ACTIVATED) { + construct_proxy_items(items, device_proxy_props, NULL); + construct_ip_items(items, AF_INET, device_ip4_props, NULL); + construct_ip_items(items, AF_INET6, device_ip6_props, NULL); + construct_device_dhcp_items(items, AF_INET, device_dhcp4_props); + construct_device_dhcp_items(items, AF_INET6, device_dhcp6_props); + } + + if (vpn_ip_iface) { + _items_add_key(items, NULL, "VPN_IP_IFACE", vpn_ip_iface); + construct_proxy_items(items, vpn_proxy_props, "VPN_"); + construct_ip_items(items, AF_INET, vpn_ip4_props, "VPN_"); + construct_ip_items(items, AF_INET6, vpn_ip6_props, "VPN_"); + } + + /* Backwards compat: 'iface' is set in this order: + * 1) VPN interface name + * 2) Device IP interface name + * 3) Device interface anme + */ + if (vpn_ip_iface) + *out_iface = g_strdup(vpn_ip_iface); + else if (ip_iface) + *out_iface = g_strdup(ip_iface); + else + *out_iface = g_strdup(iface); done: - /* The connectivity_state value will only be meaningful for 'connectivity-change' events - * (otherwise it will be "UNKNOWN"), so we only set the environment variable in those cases. - */ - if (!NM_IN_STRSET (connectivity_state, NULL, "UNKNOWN")) - _items_add_key (items, NULL, "CONNECTIVITY_STATE", connectivity_state); + /* The connectivity_state value will only be meaningful for 'connectivity-change' events + * (otherwise it will be "UNKNOWN"), so we only set the environment variable in those cases. + */ + if (!NM_IN_STRSET(connectivity_state, NULL, "UNKNOWN")) + _items_add_key(items, NULL, "CONNECTIVITY_STATE", connectivity_state); - _items_add_key0 (items, NULL, "PATH", g_getenv ("PATH")); + _items_add_key0(items, NULL, "PATH", g_getenv("PATH")); - _items_add_key (items, NULL, "NM_DISPATCHER_ACTION", action); + _items_add_key(items, NULL, "NM_DISPATCHER_ACTION", action); - *out_error_message = NULL; - g_ptr_array_add (items, NULL); - return (char **) g_ptr_array_free (g_steal_pointer (&items), FALSE); + *out_error_message = NULL; + g_ptr_array_add(items, NULL); + return (char **) g_ptr_array_free(g_steal_pointer(&items), FALSE); } diff --git a/dispatcher/nm-dispatcher-utils.h b/dispatcher/nm-dispatcher-utils.h index ba355f31..106a1357 100644 --- a/dispatcher/nm-dispatcher-utils.h +++ b/dispatcher/nm-dispatcher-utils.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2008 - 2011 Red Hat, Inc. */ @@ -6,23 +6,21 @@ #ifndef __NETWORKMANAGER_DISPATCHER_UTILS_H__ #define __NETWORKMANAGER_DISPATCHER_UTILS_H__ -char ** -nm_dispatcher_utils_construct_envp (const char *action, - GVariant *connection_dict, - GVariant *connection_props, - GVariant *device_props, - GVariant *device_proxy_props, - GVariant *device_ip4_props, - GVariant *device_ip6_props, - GVariant *device_dhcp4_props, - GVariant *device_dhcp6_props, - const char *connectivity_state, - const char *vpn_ip_iface, - GVariant *vpn_proxy_props, - GVariant *vpn_ip4_props, - GVariant *vpn_ip6_props, - char **out_iface, - const char **out_error_message); - -#endif /* __NETWORKMANAGER_DISPATCHER_UTILS_H__ */ +char **nm_dispatcher_utils_construct_envp(const char * action, + GVariant * connection_dict, + GVariant * connection_props, + GVariant * device_props, + GVariant * device_proxy_props, + GVariant * device_ip4_props, + GVariant * device_ip6_props, + GVariant * device_dhcp4_props, + GVariant * device_dhcp6_props, + const char * connectivity_state, + const char * vpn_ip_iface, + GVariant * vpn_proxy_props, + GVariant * vpn_ip4_props, + GVariant * vpn_ip6_props, + char ** out_iface, + const char **out_error_message); +#endif /* __NETWORKMANAGER_DISPATCHER_UTILS_H__ */ diff --git a/dispatcher/nm-dispatcher.c b/dispatcher/nm-dispatcher.c index e1737d85..e14cc40b 100644 --- a/dispatcher/nm-dispatcher.c +++ b/dispatcher/nm-dispatcher.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2008 - 2012 Red Hat, Inc. */ @@ -24,172 +24,182 @@ typedef struct Request Request; static struct { - GDBusConnection *dbus_connection; - GMainLoop *loop; - gboolean debug; - gboolean persist; - guint quit_id; - guint request_id_counter; - gboolean ever_acquired_name; - bool exit_with_failure; - - Request *current_request; - GQueue *requests_waiting; - int num_requests_pending; + GDBusConnection *dbus_connection; + GMainLoop * loop; + gboolean debug; + gboolean persist; + guint quit_id; + guint request_id_counter; + gboolean ever_acquired_name; + bool exit_with_failure; + + Request *current_request; + GQueue * requests_waiting; + int num_requests_pending; } gl; typedef struct { - Request *request; - - char *script; - GPid pid; - DispatchResult result; - char *error; - gboolean wait; - gboolean dispatched; - guint watch_id; - guint timeout_id; + Request *request; + + char * script; + GPid pid; + DispatchResult result; + char * error; + gboolean wait; + gboolean dispatched; + guint watch_id; + guint timeout_id; } ScriptInfo; struct Request { - guint request_id; - - GDBusMethodInvocation *context; - char *action; - char *iface; - char **envp; - gboolean debug; - - GPtrArray *scripts; /* list of ScriptInfo */ - guint idx; - int num_scripts_done; - int num_scripts_nowait; + guint request_id; + + GDBusMethodInvocation *context; + char * action; + char * iface; + char ** envp; + gboolean debug; + + GPtrArray *scripts; /* list of ScriptInfo */ + guint idx; + int num_scripts_done; + int num_scripts_nowait; }; /*****************************************************************************/ -#define __LOG_print(print_cmd, ...) \ - G_STMT_START { \ - if (FALSE) { \ - /* g_message() alone does not warn about invalid format. Add a dummy printf() statement to - * get a compiler warning about wrong format. */ \ - printf (__VA_ARGS__); \ - } \ - print_cmd (__VA_ARGS__); \ - } G_STMT_END - -#define __LOG_print_R(print_cmd, _request, ...) \ - G_STMT_START { \ - __LOG_print (print_cmd, \ - "req:%u '%s'%s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - (_request)->request_id, \ - (_request)->action, \ - (_request)->iface ? " [" : "", \ - (_request)->iface ?: "", \ - (_request)->iface ? "]" : "" \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } G_STMT_END - -#define __LOG_print_S(print_cmd, _request, _script, ...) \ - G_STMT_START { \ - __LOG_print_R (print_cmd, \ - (_request), \ - "%s%s%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - (_script) ? ", \"" : "", \ - (_script) ? (_script)->script : "", \ - (_script) ? "\"" : "" \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } G_STMT_END - -#define _LOG_X_(enabled_cmd, print_cmd, ...) \ - G_STMT_START { \ - if (enabled_cmd) \ - __LOG_print (print_cmd, __VA_ARGS__); \ - } G_STMT_END - -#define _LOG_R_(enabled_cmd, x_request, print_cmd, ...) \ - G_STMT_START { \ - const Request *const _request = (x_request); \ - \ - nm_assert (_request); \ - if (enabled_cmd) \ - __LOG_print_R (print_cmd, _request, ": "__VA_ARGS__); \ - } G_STMT_END - -#define _LOG_S_(enabled_cmd, x_script, print_cmd, ...) \ - G_STMT_START { \ - const ScriptInfo *const _script = (x_script); \ - const Request *const _request = _script ? _script->request : NULL; \ - \ - nm_assert (_script && _request); \ - if (enabled_cmd) \ - __LOG_print_S (print_cmd, _request, _script, ": "__VA_ARGS__); \ - } G_STMT_END +#define __LOG_print(print_cmd, ...) \ + G_STMT_START \ + { \ + if (FALSE) { \ + /* g_message() alone does not warn about invalid format. Add a dummy printf() statement to + * get a compiler warning about wrong format. */ \ + printf(__VA_ARGS__); \ + } \ + print_cmd(__VA_ARGS__); \ + } \ + G_STMT_END + +#define __LOG_print_R(print_cmd, _request, ...) \ + G_STMT_START \ + { \ + __LOG_print(print_cmd, \ + "req:%u '%s'%s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + (_request)->request_id, \ + (_request)->action, \ + (_request)->iface ? " [" : "", \ + (_request)->iface ?: "", \ + (_request)->iface ? "]" : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +#define __LOG_print_S(print_cmd, _request, _script, ...) \ + G_STMT_START \ + { \ + __LOG_print_R(print_cmd, \ + (_request), \ + "%s%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + (_script) ? ", \"" : "", \ + (_script) ? (_script)->script : "", \ + (_script) ? "\"" : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +#define _LOG_X_(enabled_cmd, print_cmd, ...) \ + G_STMT_START \ + { \ + if (enabled_cmd) \ + __LOG_print(print_cmd, __VA_ARGS__); \ + } \ + G_STMT_END + +#define _LOG_R_(enabled_cmd, x_request, print_cmd, ...) \ + G_STMT_START \ + { \ + const Request *const _request = (x_request); \ + \ + nm_assert(_request); \ + if (enabled_cmd) \ + __LOG_print_R(print_cmd, _request, ": "__VA_ARGS__); \ + } \ + G_STMT_END + +#define _LOG_S_(enabled_cmd, x_script, print_cmd, ...) \ + G_STMT_START \ + { \ + const ScriptInfo *const _script = (x_script); \ + const Request *const _request = _script ? _script->request : NULL; \ + \ + nm_assert(_script &&_request); \ + if (enabled_cmd) \ + __LOG_print_S(print_cmd, _request, _script, ": "__VA_ARGS__); \ + } \ + G_STMT_END #define _LOG_X_D_enabled() (gl.debug) -#define _LOG_X_T_enabled() _LOG_X_D_enabled () +#define _LOG_X_T_enabled() _LOG_X_D_enabled() -#define _LOG_R_D_enabled(request) (_NM_ENSURE_TYPE_CONST (Request *, request)->debug) -#define _LOG_R_T_enabled(request) _LOG_R_D_enabled (request) +#define _LOG_R_D_enabled(request) (_NM_ENSURE_TYPE_CONST(Request *, request)->debug) +#define _LOG_R_T_enabled(request) _LOG_R_D_enabled(request) -#define _LOG_X_T(...) _LOG_X_ (_LOG_X_T_enabled (), g_debug, __VA_ARGS__) -#define _LOG_X_D(...) _LOG_X_ (_LOG_X_D_enabled (), g_info, __VA_ARGS__) -#define _LOG_X_I(...) _LOG_X_ (TRUE, g_message, __VA_ARGS__) -#define _LOG_X_W(...) _LOG_X_ (TRUE, g_warning, __VA_ARGS__) +#define _LOG_X_T(...) _LOG_X_(_LOG_X_T_enabled(), g_debug, __VA_ARGS__) +#define _LOG_X_D(...) _LOG_X_(_LOG_X_D_enabled(), g_info, __VA_ARGS__) +#define _LOG_X_I(...) _LOG_X_(TRUE, g_message, __VA_ARGS__) +#define _LOG_X_W(...) _LOG_X_(TRUE, g_warning, __VA_ARGS__) -#define _LOG_R_T(request, ...) _LOG_R_ (_LOG_R_T_enabled (_request), request, g_debug, __VA_ARGS__) -#define _LOG_R_D(request, ...) _LOG_R_ (_LOG_R_D_enabled (_request), request, g_info, __VA_ARGS__) -#define _LOG_R_W(request, ...) _LOG_R_ (TRUE, request, g_warning, __VA_ARGS__) +#define _LOG_R_T(request, ...) _LOG_R_(_LOG_R_T_enabled(_request), request, g_debug, __VA_ARGS__) +#define _LOG_R_D(request, ...) _LOG_R_(_LOG_R_D_enabled(_request), request, g_info, __VA_ARGS__) +#define _LOG_R_W(request, ...) _LOG_R_(TRUE, request, g_warning, __VA_ARGS__) -#define _LOG_S_T(script, ...) _LOG_S_ (_LOG_R_T_enabled (_request), script, g_debug, __VA_ARGS__) -#define _LOG_S_D(script, ...) _LOG_S_ (_LOG_R_D_enabled (_request), script, g_info, __VA_ARGS__) -#define _LOG_S_W(script, ...) _LOG_S_ (TRUE, script, g_warning, __VA_ARGS__) +#define _LOG_S_T(script, ...) _LOG_S_(_LOG_R_T_enabled(_request), script, g_debug, __VA_ARGS__) +#define _LOG_S_D(script, ...) _LOG_S_(_LOG_R_D_enabled(_request), script, g_info, __VA_ARGS__) +#define _LOG_S_W(script, ...) _LOG_S_(TRUE, script, g_warning, __VA_ARGS__) /*****************************************************************************/ -static gboolean dispatch_one_script (Request *request); +static gboolean dispatch_one_script(Request *request); /*****************************************************************************/ static void -script_info_free (gpointer ptr) +script_info_free(gpointer ptr) { - ScriptInfo *info = ptr; + ScriptInfo *info = ptr; - g_free (info->script); - g_free (info->error); - g_slice_free (ScriptInfo, info); + g_free(info->script); + g_free(info->error); + g_slice_free(ScriptInfo, info); } static void -request_free (Request *request) +request_free(Request *request) { - g_assert_cmpuint (request->num_scripts_done, ==, request->scripts->len); - g_assert_cmpuint (request->num_scripts_nowait, ==, 0); + g_assert_cmpuint(request->num_scripts_done, ==, request->scripts->len); + g_assert_cmpuint(request->num_scripts_nowait, ==, 0); - g_free (request->action); - g_free (request->iface); - g_strfreev (request->envp); - g_ptr_array_free (request->scripts, TRUE); + g_free(request->action); + g_free(request->iface); + g_strfreev(request->envp); + g_ptr_array_free(request->scripts, TRUE); - g_slice_free (Request, request); + g_slice_free(Request, request); } static gboolean -quit_timeout_cb (gpointer user_data) +quit_timeout_cb(gpointer user_data) { - gl.quit_id = 0; - g_main_loop_quit (gl.loop); - return G_SOURCE_REMOVE; + gl.quit_id = 0; + g_main_loop_quit(gl.loop); + return G_SOURCE_REMOVE; } static void -quit_timeout_reschedule (void) +quit_timeout_reschedule(void) { - if (!gl.persist) { - nm_clear_g_source (&gl.quit_id); - gl.quit_id = g_timeout_add_seconds (10, quit_timeout_cb, NULL); - } + if (!gl.persist) { + nm_clear_g_source(&gl.quit_id); + gl.quit_id = g_timeout_add_seconds(10, quit_timeout_cb, NULL); + } } /** @@ -207,29 +217,29 @@ quit_timeout_reschedule (void) * a new request as current. */ static gboolean -next_request (Request *request) +next_request(Request *request) { - if (request) { - if (gl.current_request) { - g_queue_push_tail (gl.requests_waiting, request); - return FALSE; - } - } else { - /* when calling next_request() without explicit @request, we always - * forcefully clear @current_request. That one is certainly - * handled already. */ - gl.current_request = NULL; - - request = g_queue_pop_head (gl.requests_waiting); - if (!request) - return FALSE; - } - - _LOG_R_D (request, "start running ordered scripts..."); - - gl.current_request = request; - - return TRUE; + if (request) { + if (gl.current_request) { + g_queue_push_tail(gl.requests_waiting, request); + return FALSE; + } + } else { + /* when calling next_request() without explicit @request, we always + * forcefully clear @current_request. That one is certainly + * handled already. */ + gl.current_request = NULL; + + request = g_queue_pop_head(gl.requests_waiting); + if (!request) + return FALSE; + } + + _LOG_R_D(request, "start running ordered scripts..."); + + gl.current_request = request; + + return TRUE; } /** @@ -242,862 +252,850 @@ next_request (Request *request) * It also decreases @num_requests_pending and possibly does quit_timeout_reschedule(). */ static void -complete_request (Request *request) +complete_request(Request *request) { - GVariantBuilder results; - GVariant *ret; - guint i; + GVariantBuilder results; + GVariant * ret; + guint i; - nm_assert (request); + nm_assert(request); - /* Are there still pending scripts? Then do nothing (for now). */ - if (request->num_scripts_done < request->scripts->len) - return; + /* Are there still pending scripts? Then do nothing (for now). */ + if (request->num_scripts_done < request->scripts->len) + return; - g_variant_builder_init (&results, G_VARIANT_TYPE ("a(sus)")); - for (i = 0; i < request->scripts->len; i++) { - ScriptInfo *script = g_ptr_array_index (request->scripts, i); + g_variant_builder_init(&results, G_VARIANT_TYPE("a(sus)")); + for (i = 0; i < request->scripts->len; i++) { + ScriptInfo *script = g_ptr_array_index(request->scripts, i); - g_variant_builder_add (&results, "(sus)", - script->script, - script->result, - script->error ?: ""); - } + g_variant_builder_add(&results, + "(sus)", + script->script, + script->result, + script->error ?: ""); + } - ret = g_variant_new ("(a(sus))", &results); - g_dbus_method_invocation_return_value (request->context, ret); + ret = g_variant_new("(a(sus))", &results); + g_dbus_method_invocation_return_value(request->context, ret); - _LOG_R_T (request, "completed (%u scripts)", request->scripts->len); + _LOG_R_T(request, "completed (%u scripts)", request->scripts->len); - if (gl.current_request == request) - gl.current_request = NULL; + if (gl.current_request == request) + gl.current_request = NULL; - request_free (request); + request_free(request); - g_assert_cmpuint (gl.num_requests_pending, >, 0); - if (--gl.num_requests_pending <= 0) { - nm_assert (!gl.current_request && !g_queue_peek_head (gl.requests_waiting)); - quit_timeout_reschedule (); - } + g_assert_cmpuint(gl.num_requests_pending, >, 0); + if (--gl.num_requests_pending <= 0) { + nm_assert(!gl.current_request && !g_queue_peek_head(gl.requests_waiting)); + quit_timeout_reschedule(); + } } static void -complete_script (ScriptInfo *script) +complete_script(ScriptInfo *script) { - Request *request; - gboolean wait = script->wait; - - request = script->request; - - if (wait) { - /* for "wait" scripts, try to schedule the next blocking script. - * If that is successful, return (as we must wait for its completion). */ - if (dispatch_one_script (request)) - return; - } - - nm_assert (!wait || gl.current_request == request); - - /* Try to complete the request. @request will be possibly free'd, - * making @script and @request a dangling pointer. */ - complete_request (request); - - if (!wait) { - /* this was a "no-wait" script. We either completed the request, - * or there is nothing to do. Especially, there is no need to - * queue the next_request() -- because no-wait scripts don't block - * requests. However, if this was the last "no-wait" script and - * there are "wait" scripts ready to run, launch them. - */ - if ( gl.current_request == request - && gl.current_request->num_scripts_nowait == 0) { - - if (dispatch_one_script (gl.current_request)) - return; - - complete_request (gl.current_request); - } else - return; - } else { - /* if the script is a "wait" script, we already tried above to - * dispatch the next script. As we didn't do that, it means we - * just completed the last script of @request and we can continue - * with the next request... - * - * Also, it cannot be that there is another request currently being - * processed because only requests with "wait" scripts can become - * @current_request. As there can only be one "wait" script running - * at any time, it means complete_request() above completed @request. */ - nm_assert (!gl.current_request); - } - - while (next_request (NULL)) { - request = gl.current_request; - - if (dispatch_one_script (request)) - return; - - /* Try to complete the request. It will be either completed - * now, or when all pending "no-wait" scripts return. */ - complete_request (request); - - /* We can immediately start next_request(), because our current - * @request has obviously no more "wait" scripts either. - * Repeat... */ - } + Request *request; + gboolean wait = script->wait; + + request = script->request; + + if (wait) { + /* for "wait" scripts, try to schedule the next blocking script. + * If that is successful, return (as we must wait for its completion). */ + if (dispatch_one_script(request)) + return; + } + + nm_assert(!wait || gl.current_request == request); + + /* Try to complete the request. @request will be possibly free'd, + * making @script and @request a dangling pointer. */ + complete_request(request); + + if (!wait) { + /* this was a "no-wait" script. We either completed the request, + * or there is nothing to do. Especially, there is no need to + * queue the next_request() -- because no-wait scripts don't block + * requests. However, if this was the last "no-wait" script and + * there are "wait" scripts ready to run, launch them. + */ + if (gl.current_request == request && gl.current_request->num_scripts_nowait == 0) { + if (dispatch_one_script(gl.current_request)) + return; + + complete_request(gl.current_request); + } else + return; + } else { + /* if the script is a "wait" script, we already tried above to + * dispatch the next script. As we didn't do that, it means we + * just completed the last script of @request and we can continue + * with the next request... + * + * Also, it cannot be that there is another request currently being + * processed because only requests with "wait" scripts can become + * @current_request. As there can only be one "wait" script running + * at any time, it means complete_request() above completed @request. */ + nm_assert(!gl.current_request); + } + + while (next_request(NULL)) { + request = gl.current_request; + + if (dispatch_one_script(request)) + return; + + /* Try to complete the request. It will be either completed + * now, or when all pending "no-wait" scripts return. */ + complete_request(request); + + /* We can immediately start next_request(), because our current + * @request has obviously no more "wait" scripts either. + * Repeat... */ + } } static void -script_watch_cb (GPid pid, int status, gpointer user_data) +script_watch_cb(GPid pid, int status, gpointer user_data) { - ScriptInfo *script = user_data; - guint err; - - g_assert (pid == script->pid); - - script->watch_id = 0; - nm_clear_g_source (&script->timeout_id); - script->request->num_scripts_done++; - if (!script->wait) - script->request->num_scripts_nowait--; - - if (WIFEXITED (status)) { - err = WEXITSTATUS (status); - if (err == 0) - script->result = DISPATCH_RESULT_SUCCESS; - else { - script->error = g_strdup_printf ("Script '%s' exited with error status %d.", - script->script, err); - } - } else if (WIFSTOPPED (status)) { - script->error = g_strdup_printf ("Script '%s' stopped unexpectedly with signal %d.", - script->script, WSTOPSIG (status)); - } else if (WIFSIGNALED (status)) { - script->error = g_strdup_printf ("Script '%s' died with signal %d", - script->script, WTERMSIG (status)); - } else { - script->error = g_strdup_printf ("Script '%s' died from an unknown cause", - script->script); - } - - if (script->result == DISPATCH_RESULT_SUCCESS) { - _LOG_S_T (script, "complete"); - } else { - script->result = DISPATCH_RESULT_FAILED; - _LOG_S_W (script, "complete: failed with %s", script->error); - } - - g_spawn_close_pid (script->pid); - - complete_script (script); + ScriptInfo *script = user_data; + guint err; + + g_assert(pid == script->pid); + + script->watch_id = 0; + nm_clear_g_source(&script->timeout_id); + script->request->num_scripts_done++; + if (!script->wait) + script->request->num_scripts_nowait--; + + if (WIFEXITED(status)) { + err = WEXITSTATUS(status); + if (err == 0) + script->result = DISPATCH_RESULT_SUCCESS; + else { + script->error = + g_strdup_printf("Script '%s' exited with error status %d.", script->script, err); + } + } else if (WIFSTOPPED(status)) { + script->error = g_strdup_printf("Script '%s' stopped unexpectedly with signal %d.", + script->script, + WSTOPSIG(status)); + } else if (WIFSIGNALED(status)) { + script->error = + g_strdup_printf("Script '%s' died with signal %d", script->script, WTERMSIG(status)); + } else { + script->error = g_strdup_printf("Script '%s' died from an unknown cause", script->script); + } + + if (script->result == DISPATCH_RESULT_SUCCESS) { + _LOG_S_T(script, "complete"); + } else { + script->result = DISPATCH_RESULT_FAILED; + _LOG_S_W(script, "complete: failed with %s", script->error); + } + + g_spawn_close_pid(script->pid); + + complete_script(script); } static gboolean -script_timeout_cb (gpointer user_data) +script_timeout_cb(gpointer user_data) { - ScriptInfo *script = user_data; + ScriptInfo *script = user_data; - script->timeout_id = 0; - nm_clear_g_source (&script->watch_id); - script->request->num_scripts_done++; - if (!script->wait) - script->request->num_scripts_nowait--; + script->timeout_id = 0; + nm_clear_g_source(&script->watch_id); + script->request->num_scripts_done++; + if (!script->wait) + script->request->num_scripts_nowait--; - _LOG_S_W (script, "complete: timeout (kill script)"); + _LOG_S_W(script, "complete: timeout (kill script)"); - kill (script->pid, SIGKILL); + kill(script->pid, SIGKILL); again: - if (waitpid (script->pid, NULL, 0) == -1) { - if (errno == EINTR) - goto again; - } + if (waitpid(script->pid, NULL, 0) == -1) { + if (errno == EINTR) + goto again; + } - script->error = g_strdup_printf ("Script '%s' timed out.", script->script); - script->result = DISPATCH_RESULT_TIMEOUT; + script->error = g_strdup_printf("Script '%s' timed out.", script->script); + script->result = DISPATCH_RESULT_TIMEOUT; - g_spawn_close_pid (script->pid); + g_spawn_close_pid(script->pid); - complete_script (script); + complete_script(script); - return FALSE; + return FALSE; } static gboolean -check_permissions (struct stat *s, const char **out_error_msg) +check_permissions(struct stat *s, const char **out_error_msg) { - g_return_val_if_fail (s != NULL, FALSE); - g_return_val_if_fail (out_error_msg != NULL, FALSE); - g_return_val_if_fail (*out_error_msg == NULL, FALSE); - - /* Only accept files owned by root */ - if (s->st_uid != 0) { - *out_error_msg = "not owned by root."; - return FALSE; - } - - /* Only accept files not writable by group or other, and not SUID */ - if (s->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - *out_error_msg = "writable by group or other, or set-UID."; - return FALSE; - } - - /* Only accept files executable by the owner */ - if (!(s->st_mode & S_IXUSR)) { - *out_error_msg = "not executable by owner."; - return FALSE; - } - - return TRUE; + g_return_val_if_fail(s != NULL, FALSE); + g_return_val_if_fail(out_error_msg != NULL, FALSE); + g_return_val_if_fail(*out_error_msg == NULL, FALSE); + + /* Only accept files owned by root */ + if (s->st_uid != 0) { + *out_error_msg = "not owned by root."; + return FALSE; + } + + /* Only accept files not writable by group or other, and not SUID */ + if (s->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { + *out_error_msg = "writable by group or other, or set-UID."; + return FALSE; + } + + /* Only accept files executable by the owner */ + if (!(s->st_mode & S_IXUSR)) { + *out_error_msg = "not executable by owner."; + return FALSE; + } + + return TRUE; } static gboolean -check_filename (const char *file_name) +check_filename(const char *file_name) { - static const char *bad_suffixes[] = { - "~", - ".rpmsave", - ".rpmorig", - ".rpmnew", - ".swp", - }; - char *tmp; - guint i; - - /* File must not be a backup file, package management file, or start with '.' */ - - if (file_name[0] == '.') - return FALSE; - for (i = 0; i < G_N_ELEMENTS (bad_suffixes); i++) { - if (g_str_has_suffix (file_name, bad_suffixes[i])) - return FALSE; - } - tmp = g_strrstr (file_name, ".dpkg-"); - if (tmp && !strchr (&tmp[1], '.')) - return FALSE; - return TRUE; + static const char *bad_suffixes[] = { + "~", + ".rpmsave", + ".rpmorig", + ".rpmnew", + ".swp", + }; + char *tmp; + guint i; + + /* File must not be a backup file, package management file, or start with '.' */ + + if (file_name[0] == '.') + return FALSE; + for (i = 0; i < G_N_ELEMENTS(bad_suffixes); i++) { + if (g_str_has_suffix(file_name, bad_suffixes[i])) + return FALSE; + } + tmp = g_strrstr(file_name, ".dpkg-"); + if (tmp && !strchr(&tmp[1], '.')) + return FALSE; + return TRUE; } -#define SCRIPT_TIMEOUT 600 /* 10 minutes */ +#define SCRIPT_TIMEOUT 600 /* 10 minutes */ static gboolean -script_dispatch (ScriptInfo *script) +script_dispatch(ScriptInfo *script) { - gs_free_error GError *error = NULL; - char *argv[4]; - Request *request = script->request; - - if (script->dispatched) - return FALSE; - - script->dispatched = TRUE; - - /* Only for "hostname" action we coerce the interface name to "none". We don't - * do so for "connectivity-check" action. */ - - argv[0] = script->script; - argv[1] = request->iface - ?: (nm_streq (request->action, NMD_ACTION_HOSTNAME) ? "none" : ""); - argv[2] = request->action; - argv[3] = NULL; - - _LOG_S_T (script, "run script%s", script->wait ? "" : " (no-wait)"); - - if (!g_spawn_async ("/", argv, request->envp, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &script->pid, &error)) { - _LOG_S_W (script, "complete: failed to execute script: %s", error->message); - script->result = DISPATCH_RESULT_EXEC_FAILED; - script->error = g_strdup (error->message); - request->num_scripts_done++; - return FALSE; - } - - script->watch_id = g_child_watch_add (script->pid, (GChildWatchFunc) script_watch_cb, script); - script->timeout_id = g_timeout_add_seconds (SCRIPT_TIMEOUT, script_timeout_cb, script); - if (!script->wait) - request->num_scripts_nowait++; - return TRUE; + gs_free_error GError *error = NULL; + char * argv[4]; + Request * request = script->request; + + if (script->dispatched) + return FALSE; + + script->dispatched = TRUE; + + /* Only for "hostname" action we coerce the interface name to "none". We don't + * do so for "connectivity-check" action. */ + + argv[0] = script->script; + argv[1] = request->iface ?: (nm_streq(request->action, NMD_ACTION_HOSTNAME) ? "none" : ""); + argv[2] = request->action; + argv[3] = NULL; + + _LOG_S_T(script, "run script%s", script->wait ? "" : " (no-wait)"); + + if (!g_spawn_async("/", + argv, + request->envp, + G_SPAWN_DO_NOT_REAP_CHILD, + NULL, + NULL, + &script->pid, + &error)) { + _LOG_S_W(script, "complete: failed to execute script: %s", error->message); + script->result = DISPATCH_RESULT_EXEC_FAILED; + script->error = g_strdup(error->message); + request->num_scripts_done++; + return FALSE; + } + + script->watch_id = g_child_watch_add(script->pid, (GChildWatchFunc) script_watch_cb, script); + script->timeout_id = g_timeout_add_seconds(SCRIPT_TIMEOUT, script_timeout_cb, script); + if (!script->wait) + request->num_scripts_nowait++; + return TRUE; } static gboolean -dispatch_one_script (Request *request) +dispatch_one_script(Request *request) { - if (request->num_scripts_nowait > 0) - return TRUE; + if (request->num_scripts_nowait > 0) + return TRUE; - while (request->idx < request->scripts->len) { - ScriptInfo *script; + while (request->idx < request->scripts->len) { + ScriptInfo *script; - script = g_ptr_array_index (request->scripts, request->idx++); - if (script_dispatch (script)) - return TRUE; - } - return FALSE; + script = g_ptr_array_index(request->scripts, request->idx++); + if (script_dispatch(script)) + return TRUE; + } + return FALSE; } static int -_compare_basenames (gconstpointer a, gconstpointer b) +_compare_basenames(gconstpointer a, gconstpointer b) { - const char *basename_a = strrchr (a, '/'); - const char *basename_b = strrchr (b, '/'); - int ret; + const char *basename_a = strrchr(a, '/'); + const char *basename_b = strrchr(b, '/'); + int ret; - nm_assert (basename_a); - nm_assert (basename_b); + nm_assert(basename_a); + nm_assert(basename_b); - ret = strcmp (++basename_a, ++basename_b); - if (ret) - return ret; + ret = strcmp(++basename_a, ++basename_b); + if (ret) + return ret; - nm_assert_not_reached (); - return 0; + nm_assert_not_reached(); + return 0; } static void -_find_scripts (Request *request, GHashTable *scripts, const char *base, const char *subdir) +_find_scripts(Request *request, GHashTable *scripts, const char *base, const char *subdir) { - const char *filename; - gs_free char *dirname = NULL; - GError *error = NULL; - GDir *dir; - - dirname = g_build_filename (base, "dispatcher.d", subdir, NULL); - - if (!(dir = g_dir_open (dirname, 0, &error))) { - if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - _LOG_R_W (request, "find-scripts: Failed to open dispatcher directory '%s': %s", - dirname, error->message); - } - g_error_free (error); - return; - } - - while ((filename = g_dir_read_name (dir))) { - if (!check_filename (filename)) - continue; - - g_hash_table_insert (scripts, - g_strdup (filename), - g_build_filename (dirname, filename, NULL)); - } - - g_dir_close (dir); + const char * filename; + gs_free char *dirname = NULL; + GError * error = NULL; + GDir * dir; + + dirname = g_build_filename(base, "dispatcher.d", subdir, NULL); + + if (!(dir = g_dir_open(dirname, 0, &error))) { + if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + _LOG_R_W(request, + "find-scripts: Failed to open dispatcher directory '%s': %s", + dirname, + error->message); + } + g_error_free(error); + return; + } + + while ((filename = g_dir_read_name(dir))) { + if (!check_filename(filename)) + continue; + + g_hash_table_insert(scripts, g_strdup(filename), g_build_filename(dirname, filename, NULL)); + } + + g_dir_close(dir); } static GSList * -find_scripts (Request *request) +find_scripts(Request *request) { - gs_unref_hashtable GHashTable *scripts = NULL; - GSList *script_list = NULL; - GHashTableIter iter; - const char *subdir; - char *path; - char *filename; - - if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_UP, - NMD_ACTION_VPN_PRE_UP)) - subdir = "pre-up.d"; - else if (NM_IN_STRSET (request->action, NMD_ACTION_PRE_DOWN, - NMD_ACTION_VPN_PRE_DOWN)) - subdir = "pre-down.d"; - else - subdir = NULL; - - scripts = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free); - - _find_scripts (request, scripts, NMLIBDIR, subdir); - _find_scripts (request, scripts, NMCONFDIR, subdir); - - g_hash_table_iter_init (&iter, scripts); - while (g_hash_table_iter_next (&iter, (gpointer *) &filename, (gpointer *) &path)) { - gs_free char *link_target = NULL; - const char *err_msg = NULL; - struct stat st; - int err; - - link_target = g_file_read_link (path, NULL); - if (nm_streq0 (link_target, "/dev/null")) - continue; - - err = stat (path, &st); - if (err) - _LOG_R_W (request, "find-scripts: Failed to stat '%s': %d", path, err); - else if ( !S_ISREG (st.st_mode) - || st.st_size == 0) { - /* silently skip. */ - } else if (!check_permissions (&st, &err_msg)) - _LOG_R_W (request, "find-scripts: Cannot execute '%s': %s", path, err_msg); - else { - /* success */ - script_list = g_slist_prepend (script_list, g_strdup (path)); - continue; - } - } - - return g_slist_sort (script_list, _compare_basenames); + gs_unref_hashtable GHashTable *scripts = NULL; + GSList * script_list = NULL; + GHashTableIter iter; + const char * subdir; + char * path; + char * filename; + + if (NM_IN_STRSET(request->action, NMD_ACTION_PRE_UP, NMD_ACTION_VPN_PRE_UP)) + subdir = "pre-up.d"; + else if (NM_IN_STRSET(request->action, NMD_ACTION_PRE_DOWN, NMD_ACTION_VPN_PRE_DOWN)) + subdir = "pre-down.d"; + else + subdir = NULL; + + scripts = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free); + + _find_scripts(request, scripts, NMLIBDIR, subdir); + _find_scripts(request, scripts, NMCONFDIR, subdir); + + g_hash_table_iter_init(&iter, scripts); + while (g_hash_table_iter_next(&iter, (gpointer *) &filename, (gpointer *) &path)) { + gs_free char *link_target = NULL; + const char * err_msg = NULL; + struct stat st; + int err; + + link_target = g_file_read_link(path, NULL); + if (nm_streq0(link_target, "/dev/null")) + continue; + + err = stat(path, &st); + if (err) + _LOG_R_W(request, "find-scripts: Failed to stat '%s': %d", path, err); + else if (!S_ISREG(st.st_mode) || st.st_size == 0) { + /* silently skip. */ + } else if (!check_permissions(&st, &err_msg)) + _LOG_R_W(request, "find-scripts: Cannot execute '%s': %s", path, err_msg); + else { + /* success */ + script_list = g_slist_prepend(script_list, g_strdup(path)); + continue; + } + } + + return g_slist_sort(script_list, _compare_basenames); } static gboolean -script_must_wait (const char *path) +script_must_wait(const char *path) { - gs_free char *link = NULL; - - link = g_file_read_link (path, NULL); - if (link) { - gs_free char *dir = NULL; - nm_auto_free char *real = NULL; - - if (!g_path_is_absolute (link)) { - char *tmp; - - dir = g_path_get_dirname (path); - tmp = g_build_path ("/", dir, link, NULL); - g_free (link); - g_free (dir); - link = tmp; - } - - dir = g_path_get_dirname (link); - real = realpath (dir, NULL); - if (NM_STR_HAS_SUFFIX (real, "/no-wait.d")) - return FALSE; - } - - return TRUE; + gs_free char *link = NULL; + + link = g_file_read_link(path, NULL); + if (link) { + gs_free char * dir = NULL; + nm_auto_free char *real = NULL; + + if (!g_path_is_absolute(link)) { + char *tmp; + + dir = g_path_get_dirname(path); + tmp = g_build_path("/", dir, link, NULL); + g_free(link); + g_free(dir); + link = tmp; + } + + dir = g_path_get_dirname(link); + real = realpath(dir, NULL); + if (NM_STR_HAS_SUFFIX(real, "/no-wait.d")) + return FALSE; + } + + return TRUE; } static void -_method_call_action (GDBusMethodInvocation *invocation, - GVariant *parameters) +_method_call_action(GDBusMethodInvocation *invocation, GVariant *parameters) { - const char *action; - gs_unref_variant GVariant *connection = NULL; - gs_unref_variant GVariant *connection_properties = NULL; - gs_unref_variant GVariant *device_properties = NULL; - gs_unref_variant GVariant *device_proxy_properties = NULL; - gs_unref_variant GVariant *device_ip4_config = NULL; - gs_unref_variant GVariant *device_ip6_config = NULL; - gs_unref_variant GVariant *device_dhcp4_config = NULL; - gs_unref_variant GVariant *device_dhcp6_config = NULL; - const char *connectivity_state; - const char *vpn_ip_iface; - gs_unref_variant GVariant *vpn_proxy_properties = NULL; - gs_unref_variant GVariant *vpn_ip4_config = NULL; - gs_unref_variant GVariant *vpn_ip6_config = NULL; - gboolean debug; - GSList *sorted_scripts = NULL; - GSList *iter; - Request *request; - char **p; - guint i, num_nowait = 0; - const char *error_message = NULL; - - g_variant_get (parameters, "(" - "&s" /* action */ - "@a{sa{sv}}" /* connection */ - "@a{sv}" /* connection_properties */ - "@a{sv}" /* device_properties */ - "@a{sv}" /* device_proxy_properties */ - "@a{sv}" /* device_ip4_config */ - "@a{sv}" /* device_ip6_config */ - "@a{sv}" /* device_dhcp4_config */ - "@a{sv}" /* device_dhcp6_config */ - "&s" /* connectivity_state */ - "&s" /* vpn_ip_iface */ - "@a{sv}" /* vpn_proxy_properties */ - "@a{sv}" /* vpn_ip4_config */ - "@a{sv}" /* vpn_ip6_config */ - "b" /* debug */ - ")", - &action, - &connection, - &connection_properties, - &device_properties, - &device_proxy_properties, - &device_ip4_config, - &device_ip6_config, - &device_dhcp4_config, - &device_dhcp6_config, - &connectivity_state, - &vpn_ip_iface, - &vpn_proxy_properties, - &vpn_ip4_config, - &vpn_ip6_config, - &debug); - - request = g_slice_new0 (Request); - request->request_id = ++gl.request_id_counter; - request->debug = debug || gl.debug; - request->context = invocation; - request->action = g_strdup (action); - - request->envp = nm_dispatcher_utils_construct_envp (action, - connection, - connection_properties, - device_properties, - device_proxy_properties, - device_ip4_config, - device_ip6_config, - device_dhcp4_config, - device_dhcp6_config, - connectivity_state, - vpn_ip_iface, - vpn_proxy_properties, - vpn_ip4_config, - vpn_ip6_config, - &request->iface, - &error_message); - - request->scripts = g_ptr_array_new_full (5, script_info_free); - - sorted_scripts = find_scripts (request); - for (iter = sorted_scripts; iter; iter = g_slist_next (iter)) { - ScriptInfo *s; - - s = g_slice_new0 (ScriptInfo); - s->request = request; - s->script = iter->data; - s->wait = script_must_wait (s->script); - g_ptr_array_add (request->scripts, s); - } - g_slist_free (sorted_scripts); - - _LOG_R_D (request, "new request (%u scripts)", request->scripts->len); - if ( _LOG_R_T_enabled (request) - && request->envp) { - for (p = request->envp; *p; p++) - _LOG_R_T (request, "environment: %s", *p); - } - - if (error_message || request->scripts->len == 0) { - GVariant *results; - - if (error_message) - _LOG_R_W (request, "completed: invalid request: %s", error_message); - else - _LOG_R_D (request, "completed: no scripts"); - - results = g_variant_new_array (G_VARIANT_TYPE ("(sus)"), NULL, 0); - g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@a(sus))", results)); - request->num_scripts_done = request->scripts->len; - request_free (request); - return; - } - - nm_clear_g_source (&gl.quit_id); - - gl.num_requests_pending++; - - for (i = 0; i < request->scripts->len; i++) { - ScriptInfo *s = g_ptr_array_index (request->scripts, i); - - if (!s->wait) { - script_dispatch (s); - num_nowait++; - } - } - - if (num_nowait < request->scripts->len) { - /* The request has at least one wait script. - * Try next_request() to schedule the request for - * execution. This either enqueues the request or - * sets it as gl.current_request. */ - if (next_request (request)) { - /* @request is now @current_request. Go ahead and - * schedule the first wait script. */ - if (!dispatch_one_script (request)) { - /* If that fails, we might be already finished with the - * request. Try complete_request(). */ - complete_request (request); - - if (next_request (NULL)) { - /* As @request was successfully scheduled as next_request(), there is no - * other request in queue that can be scheduled afterwards. Assert against - * that, but call next_request() to clear current_request. */ - g_assert_not_reached (); - } - } - } - } else { - /* The request contains only no-wait scripts. Try to complete - * the request right away (we might have failed to schedule any - * of the scripts). It will be either completed now, or later - * when the pending scripts return. - * We don't enqueue it to gl.requests_waiting. - * There is no need to handle next_request(), because @request is - * not the current request anyway and does not interfere with requests - * that have any "wait" scripts. */ - complete_request (request); - } + const char * action; + gs_unref_variant GVariant *connection = NULL; + gs_unref_variant GVariant *connection_properties = NULL; + gs_unref_variant GVariant *device_properties = NULL; + gs_unref_variant GVariant *device_proxy_properties = NULL; + gs_unref_variant GVariant *device_ip4_config = NULL; + gs_unref_variant GVariant *device_ip6_config = NULL; + gs_unref_variant GVariant *device_dhcp4_config = NULL; + gs_unref_variant GVariant *device_dhcp6_config = NULL; + const char * connectivity_state; + const char * vpn_ip_iface; + gs_unref_variant GVariant *vpn_proxy_properties = NULL; + gs_unref_variant GVariant *vpn_ip4_config = NULL; + gs_unref_variant GVariant *vpn_ip6_config = NULL; + gboolean debug; + GSList * sorted_scripts = NULL; + GSList * iter; + Request * request; + char ** p; + guint i, num_nowait = 0; + const char * error_message = NULL; + + g_variant_get(parameters, + "(" + "&s" /* action */ + "@a{sa{sv}}" /* connection */ + "@a{sv}" /* connection_properties */ + "@a{sv}" /* device_properties */ + "@a{sv}" /* device_proxy_properties */ + "@a{sv}" /* device_ip4_config */ + "@a{sv}" /* device_ip6_config */ + "@a{sv}" /* device_dhcp4_config */ + "@a{sv}" /* device_dhcp6_config */ + "&s" /* connectivity_state */ + "&s" /* vpn_ip_iface */ + "@a{sv}" /* vpn_proxy_properties */ + "@a{sv}" /* vpn_ip4_config */ + "@a{sv}" /* vpn_ip6_config */ + "b" /* debug */ + ")", + &action, + &connection, + &connection_properties, + &device_properties, + &device_proxy_properties, + &device_ip4_config, + &device_ip6_config, + &device_dhcp4_config, + &device_dhcp6_config, + &connectivity_state, + &vpn_ip_iface, + &vpn_proxy_properties, + &vpn_ip4_config, + &vpn_ip6_config, + &debug); + + request = g_slice_new0(Request); + request->request_id = ++gl.request_id_counter; + request->debug = debug || gl.debug; + request->context = invocation; + request->action = g_strdup(action); + + request->envp = nm_dispatcher_utils_construct_envp(action, + connection, + connection_properties, + device_properties, + device_proxy_properties, + device_ip4_config, + device_ip6_config, + device_dhcp4_config, + device_dhcp6_config, + connectivity_state, + vpn_ip_iface, + vpn_proxy_properties, + vpn_ip4_config, + vpn_ip6_config, + &request->iface, + &error_message); + + request->scripts = g_ptr_array_new_full(5, script_info_free); + + sorted_scripts = find_scripts(request); + for (iter = sorted_scripts; iter; iter = g_slist_next(iter)) { + ScriptInfo *s; + + s = g_slice_new0(ScriptInfo); + s->request = request; + s->script = iter->data; + s->wait = script_must_wait(s->script); + g_ptr_array_add(request->scripts, s); + } + g_slist_free(sorted_scripts); + + _LOG_R_D(request, "new request (%u scripts)", request->scripts->len); + if (_LOG_R_T_enabled(request) && request->envp) { + for (p = request->envp; *p; p++) + _LOG_R_T(request, "environment: %s", *p); + } + + if (error_message || request->scripts->len == 0) { + GVariant *results; + + if (error_message) + _LOG_R_W(request, "completed: invalid request: %s", error_message); + else + _LOG_R_D(request, "completed: no scripts"); + + results = g_variant_new_array(G_VARIANT_TYPE("(sus)"), NULL, 0); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(@a(sus))", results)); + request->num_scripts_done = request->scripts->len; + request_free(request); + return; + } + + nm_clear_g_source(&gl.quit_id); + + gl.num_requests_pending++; + + for (i = 0; i < request->scripts->len; i++) { + ScriptInfo *s = g_ptr_array_index(request->scripts, i); + + if (!s->wait) { + script_dispatch(s); + num_nowait++; + } + } + + if (num_nowait < request->scripts->len) { + /* The request has at least one wait script. + * Try next_request() to schedule the request for + * execution. This either enqueues the request or + * sets it as gl.current_request. */ + if (next_request(request)) { + /* @request is now @current_request. Go ahead and + * schedule the first wait script. */ + if (!dispatch_one_script(request)) { + /* If that fails, we might be already finished with the + * request. Try complete_request(). */ + complete_request(request); + + if (next_request(NULL)) { + /* As @request was successfully scheduled as next_request(), there is no + * other request in queue that can be scheduled afterwards. Assert against + * that, but call next_request() to clear current_request. */ + g_assert_not_reached(); + } + } + } + } else { + /* The request contains only no-wait scripts. Try to complete + * the request right away (we might have failed to schedule any + * of the scripts). It will be either completed now, or later + * when the pending scripts return. + * We don't enqueue it to gl.requests_waiting. + * There is no need to handle next_request(), because @request is + * not the current request anyway and does not interfere with requests + * that have any "wait" scripts. */ + complete_request(request); + } } static void -on_name_acquired (GDBusConnection *connection, - const char *name, - gpointer user_data) +on_name_acquired(GDBusConnection *connection, const char *name, gpointer user_data) { - gl.ever_acquired_name = TRUE; + gl.ever_acquired_name = TRUE; } static void -on_name_lost (GDBusConnection *connection, - const char *name, - gpointer user_data) +on_name_lost(GDBusConnection *connection, const char *name, gpointer user_data) { - if (!connection) { - if (!gl.ever_acquired_name) { - _LOG_X_W ("Could not get the system bus. Make sure the message bus daemon is running!"); - gl.exit_with_failure = TRUE; - } else { - _LOG_X_I ("System bus stopped. Exiting"); - } - } else if (!gl.ever_acquired_name) { - _LOG_X_W ("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service."); - gl.exit_with_failure = TRUE; - } else - _LOG_X_I ("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting"); - - g_main_loop_quit (gl.loop); + if (!connection) { + if (!gl.ever_acquired_name) { + _LOG_X_W("Could not get the system bus. Make sure the message bus daemon is running!"); + gl.exit_with_failure = TRUE; + } else { + _LOG_X_I("System bus stopped. Exiting"); + } + } else if (!gl.ever_acquired_name) { + _LOG_X_W("Could not acquire the " NM_DISPATCHER_DBUS_SERVICE " service."); + gl.exit_with_failure = TRUE; + } else + _LOG_X_I("Lost the " NM_DISPATCHER_DBUS_SERVICE " name. Exiting"); + + g_main_loop_quit(gl.loop); } static void -_method_call (GDBusConnection *connection, - const char *sender, - const char *object_path, - const char *interface_name, - const char *method_name, - GVariant *parameters, - GDBusMethodInvocation *invocation, - gpointer user_data) +_method_call(GDBusConnection * connection, + const char * sender, + const char * object_path, + const char * interface_name, + const char * method_name, + GVariant * parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) { - if (nm_streq (interface_name, NM_DISPATCHER_DBUS_INTERFACE)) { - if (nm_streq (method_name, "Action")) { - _method_call_action (invocation, parameters); - return; - } - } - g_dbus_method_invocation_return_error (invocation, - G_DBUS_ERROR, - G_DBUS_ERROR_UNKNOWN_METHOD, - "Unknown method %s", - method_name); + if (nm_streq(interface_name, NM_DISPATCHER_DBUS_INTERFACE)) { + if (nm_streq(method_name, "Action")) { + _method_call_action(invocation, parameters); + return; + } + } + g_dbus_method_invocation_return_error(invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_UNKNOWN_METHOD, + "Unknown method %s", + method_name); } -static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO ( - NM_DISPATCHER_DBUS_INTERFACE, - .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( - NM_DEFINE_GDBUS_METHOD_INFO ( - "Action", - .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( - NM_DEFINE_GDBUS_ARG_INFO ("action", "s"), - NM_DEFINE_GDBUS_ARG_INFO ("connection", "a{sa{sv}}"), - NM_DEFINE_GDBUS_ARG_INFO ("connection_properties", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("device_properties", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("device_proxy_properties", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("device_ip4_config", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("device_ip6_config", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("device_dhcp4_config", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("device_dhcp6_config", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("connectivity_state", "s"), - NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip_iface", "s"), - NM_DEFINE_GDBUS_ARG_INFO ("vpn_proxy_properties", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip4_config", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("vpn_ip6_config", "a{sv}"), - NM_DEFINE_GDBUS_ARG_INFO ("debug", "b"), - ), - .out_args = NM_DEFINE_GDBUS_ARG_INFOS ( - NM_DEFINE_GDBUS_ARG_INFO ("results", "a(sus)"), - ), - ), - ), -); +static GDBusInterfaceInfo *const interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO( + NM_DISPATCHER_DBUS_INTERFACE, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS( + NM_DEFINE_GDBUS_METHOD_INFO( + "Action", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS( + NM_DEFINE_GDBUS_ARG_INFO("action", "s"), + NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), + NM_DEFINE_GDBUS_ARG_INFO("connection_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("device_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("device_proxy_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("device_ip4_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("device_ip6_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("device_dhcp4_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("device_dhcp6_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("connectivity_state", "s"), + NM_DEFINE_GDBUS_ARG_INFO("vpn_ip_iface", "s"), + NM_DEFINE_GDBUS_ARG_INFO("vpn_proxy_properties", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("vpn_ip4_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("vpn_ip6_config", "a{sv}"), + NM_DEFINE_GDBUS_ARG_INFO("debug", "b"), ), + .out_args = + NM_DEFINE_GDBUS_ARG_INFOS(NM_DEFINE_GDBUS_ARG_INFO("results", "a(sus)"), ), ), ), ); static const GDBusInterfaceVTable interface_vtable = { - .method_call = _method_call, + .method_call = _method_call, }; /*****************************************************************************/ static void -log_handler (const char *log_domain, - GLogLevelFlags log_level, - const char *message, - gpointer ignored) +log_handler(const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer ignored) { - int syslog_priority; - - switch (log_level) { - case G_LOG_LEVEL_ERROR: - syslog_priority = LOG_CRIT; - break; - case G_LOG_LEVEL_CRITICAL: - syslog_priority = LOG_ERR; - break; - case G_LOG_LEVEL_WARNING: - syslog_priority = LOG_WARNING; - break; - case G_LOG_LEVEL_MESSAGE: - syslog_priority = LOG_NOTICE; - break; - case G_LOG_LEVEL_DEBUG: - syslog_priority = LOG_DEBUG; - break; - case G_LOG_LEVEL_INFO: - default: - syslog_priority = LOG_INFO; - break; - } - - syslog (syslog_priority, "%s", message); + int syslog_priority; + + switch (log_level) { + case G_LOG_LEVEL_ERROR: + syslog_priority = LOG_CRIT; + break; + case G_LOG_LEVEL_CRITICAL: + syslog_priority = LOG_ERR; + break; + case G_LOG_LEVEL_WARNING: + syslog_priority = LOG_WARNING; + break; + case G_LOG_LEVEL_MESSAGE: + syslog_priority = LOG_NOTICE; + break; + case G_LOG_LEVEL_DEBUG: + syslog_priority = LOG_DEBUG; + break; + case G_LOG_LEVEL_INFO: + default: + syslog_priority = LOG_INFO; + break; + } + + syslog(syslog_priority, "%s", message); } static void -logging_setup (void) +logging_setup(void) { - openlog (G_LOG_DOMAIN, LOG_CONS, LOG_DAEMON); - g_log_set_handler (G_LOG_DOMAIN, - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, - log_handler, - NULL); + openlog(G_LOG_DOMAIN, LOG_CONS, LOG_DAEMON); + g_log_set_handler(G_LOG_DOMAIN, + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, + log_handler, + NULL); } static void -logging_shutdown (void) +logging_shutdown(void) { - closelog (); + closelog(); } static gboolean -signal_handler (gpointer user_data) +signal_handler(gpointer user_data) { - int signo = GPOINTER_TO_INT (user_data); + int signo = GPOINTER_TO_INT(user_data); - _LOG_X_I ("Caught signal %d, shutting down...", signo); - g_main_loop_quit (gl.loop); + _LOG_X_I("Caught signal %d, shutting down...", signo); + g_main_loop_quit(gl.loop); - return G_SOURCE_CONTINUE; + return G_SOURCE_CONTINUE; } static gboolean -parse_command_line (int *p_argc, - char ***p_argv, - GError **error) +parse_command_line(int *p_argc, char ***p_argv, GError **error) { - GOptionContext *opt_ctx; - GOptionEntry entries[] = { - { "debug", 0, 0, G_OPTION_ARG_NONE, &gl.debug, "Output to console rather than syslog", NULL }, - { "persist", 0, 0, G_OPTION_ARG_NONE, &gl.persist, "Don't quit after a short timeout", NULL }, - { NULL } - }; - gboolean success; + GOptionContext *opt_ctx; + GOptionEntry entries[] = { + {"debug", 0, 0, G_OPTION_ARG_NONE, &gl.debug, "Output to console rather than syslog", NULL}, + {"persist", 0, 0, G_OPTION_ARG_NONE, &gl.persist, "Don't quit after a short timeout", NULL}, + {NULL}}; + gboolean success; - opt_ctx = g_option_context_new (NULL); - g_option_context_set_summary (opt_ctx, "Executes scripts upon actions by NetworkManager."); - g_option_context_add_main_entries (opt_ctx, entries, NULL); + opt_ctx = g_option_context_new(NULL); + g_option_context_set_summary(opt_ctx, "Executes scripts upon actions by NetworkManager."); + g_option_context_add_main_entries(opt_ctx, entries, NULL); - success = g_option_context_parse (opt_ctx, p_argc, p_argv, error); + success = g_option_context_parse(opt_ctx, p_argc, p_argv, error); - g_option_context_free (opt_ctx); + g_option_context_free(opt_ctx); - return success; + return success; } int -main (int argc, char **argv) +main(int argc, char **argv) { - gs_free_error GError *error = NULL; - guint signal_id_term = 0; - guint signal_id_int = 0; - guint dbus_regist_id = 0; - guint dbus_own_name_id = 0; - - if (!parse_command_line (&argc, &argv, &error)) { - _LOG_X_W ("Error parsing command line arguments: %s", error->message); - gl.exit_with_failure = TRUE; - goto done; - } - - signal_id_term = g_unix_signal_add (SIGTERM, signal_handler, GINT_TO_POINTER (SIGTERM)); - signal_id_int = g_unix_signal_add (SIGINT, signal_handler, GINT_TO_POINTER (SIGINT)); - - if (gl.debug) { - if (!g_getenv ("G_MESSAGES_DEBUG")) { - /* we log our regular messages using g_debug() and g_info(). - * When we redirect glib logging to syslog, there is no problem. - * But in "debug" mode, glib will no print these messages unless - * we set G_MESSAGES_DEBUG. */ - g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); - } - } else - logging_setup (); - - gl.loop = g_main_loop_new (NULL, FALSE); - - gl.dbus_connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error); - if (!gl.dbus_connection) { - _LOG_X_W ("Could not get the system bus (%s). Make sure the message bus daemon is running!", - error->message); - gl.exit_with_failure = TRUE; - goto done; - } - - gl.requests_waiting = g_queue_new (); - - dbus_regist_id = g_dbus_connection_register_object (gl.dbus_connection, - NM_DISPATCHER_DBUS_PATH, - interface_info, - NM_UNCONST_PTR (GDBusInterfaceVTable, &interface_vtable), - NULL, - NULL, - &error); - if (dbus_regist_id == 0) { - _LOG_X_W ("Could not export Dispatcher D-Bus interface: %s", error->message); - gl.exit_with_failure = 1; - goto done; - } - - dbus_own_name_id = g_bus_own_name_on_connection (gl.dbus_connection, - NM_DISPATCHER_DBUS_SERVICE, - G_BUS_NAME_OWNER_FLAGS_NONE, - on_name_acquired, - on_name_lost, - NULL, NULL); - - quit_timeout_reschedule (); - - g_main_loop_run (gl.loop); + gs_free_error GError *error = NULL; + guint signal_id_term = 0; + guint signal_id_int = 0; + guint dbus_regist_id = 0; + guint dbus_own_name_id = 0; + + if (!parse_command_line(&argc, &argv, &error)) { + _LOG_X_W("Error parsing command line arguments: %s", error->message); + gl.exit_with_failure = TRUE; + goto done; + } + + signal_id_term = g_unix_signal_add(SIGTERM, signal_handler, GINT_TO_POINTER(SIGTERM)); + signal_id_int = g_unix_signal_add(SIGINT, signal_handler, GINT_TO_POINTER(SIGINT)); + + if (gl.debug) { + if (!g_getenv("G_MESSAGES_DEBUG")) { + /* we log our regular messages using g_debug() and g_info(). + * When we redirect glib logging to syslog, there is no problem. + * But in "debug" mode, glib will no print these messages unless + * we set G_MESSAGES_DEBUG. */ + g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + } + } else + logging_setup(); + + gl.loop = g_main_loop_new(NULL, FALSE); + + gl.dbus_connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!gl.dbus_connection) { + _LOG_X_W("Could not get the system bus (%s). Make sure the message bus daemon is running!", + error->message); + gl.exit_with_failure = TRUE; + goto done; + } + + gl.requests_waiting = g_queue_new(); + + dbus_regist_id = + g_dbus_connection_register_object(gl.dbus_connection, + NM_DISPATCHER_DBUS_PATH, + interface_info, + NM_UNCONST_PTR(GDBusInterfaceVTable, &interface_vtable), + NULL, + NULL, + &error); + if (dbus_regist_id == 0) { + _LOG_X_W("Could not export Dispatcher D-Bus interface: %s", error->message); + gl.exit_with_failure = 1; + goto done; + } + + dbus_own_name_id = g_bus_own_name_on_connection(gl.dbus_connection, + NM_DISPATCHER_DBUS_SERVICE, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_name_acquired, + on_name_lost, + NULL, + NULL); + + quit_timeout_reschedule(); + + g_main_loop_run(gl.loop); done: - if (gl.num_requests_pending > 0) { - /* this only happens when we quit due to SIGTERM (not due to the idle timer). - * - * Log a warning about pending scripts. - * - * Maybe we should notify NetworkManager that these scripts are left in an unknown state. - * But this is either a bug of a dispatcher script (not terminating in time). - * - * FIXME(shutdown): Also, currently NetworkManager behaves wrongly on shutdown. - * Note that systemd would not terminate NetworkManager-dispatcher before NetworkManager. - * It's NetworkManager's responsibility to keep running long enough so that all requests - * can complete (with a watchdog timer, and a warning that user provided scripts hang). */ - _LOG_X_W ("exiting but there are still %u requests pending", gl.num_requests_pending); - } - - if (dbus_own_name_id != 0) - g_bus_unown_name (nm_steal_int (&dbus_own_name_id)); - - if (dbus_regist_id != 0) - g_dbus_connection_unregister_object (gl.dbus_connection, nm_steal_int (&dbus_regist_id)); - - nm_clear_pointer (&gl.requests_waiting, g_queue_free); - - nm_clear_g_source (&signal_id_term); - nm_clear_g_source (&signal_id_int); - nm_clear_g_source (&gl.quit_id); - nm_clear_pointer (&gl.loop, g_main_loop_unref); - g_clear_object (&gl.dbus_connection); - - if (!gl.debug) - logging_shutdown (); - - return gl.exit_with_failure ? 1 : 0; + if (gl.num_requests_pending > 0) { + /* this only happens when we quit due to SIGTERM (not due to the idle timer). + * + * Log a warning about pending scripts. + * + * Maybe we should notify NetworkManager that these scripts are left in an unknown state. + * But this is either a bug of a dispatcher script (not terminating in time). + * + * FIXME(shutdown): Also, currently NetworkManager behaves wrongly on shutdown. + * Note that systemd would not terminate NetworkManager-dispatcher before NetworkManager. + * It's NetworkManager's responsibility to keep running long enough so that all requests + * can complete (with a watchdog timer, and a warning that user provided scripts hang). */ + _LOG_X_W("exiting but there are still %u requests pending", gl.num_requests_pending); + } + + if (dbus_own_name_id != 0) + g_bus_unown_name(nm_steal_int(&dbus_own_name_id)); + + if (dbus_regist_id != 0) + g_dbus_connection_unregister_object(gl.dbus_connection, nm_steal_int(&dbus_regist_id)); + + nm_clear_pointer(&gl.requests_waiting, g_queue_free); + + nm_clear_g_source(&signal_id_term); + nm_clear_g_source(&signal_id_int); + nm_clear_g_source(&gl.quit_id); + nm_clear_pointer(&gl.loop, g_main_loop_unref); + g_clear_object(&gl.dbus_connection); + + if (!gl.debug) + logging_shutdown(); + + return gl.exit_with_failure ? 1 : 0; } diff --git a/dispatcher/tests/test-dispatcher-envp.c b/dispatcher/tests/test-dispatcher-envp.c index 240b8951..2e64dc58 100644 --- a/dispatcher/tests/test-dispatcher-envp.c +++ b/dispatcher/tests/test-dispatcher-envp.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2011 Red Hat, Inc. */ @@ -15,644 +15,623 @@ #include "nmdbus-dispatcher.h" -#define TEST_DIR NM_BUILD_SRCDIR"/dispatcher/tests" +#define TEST_DIR NM_BUILD_SRCDIR "/dispatcher/tests" /*****************************************************************************/ static void -_print_env (const char *const*denv, GHashTable *expected_env) +_print_env(const char *const *denv, GHashTable *expected_env) { - const char *const*iter; - GHashTableIter k; - const char *key; - - g_print ("\n******* Generated environment:\n"); - for (iter = denv; iter && *iter; iter++) - g_print (" %s\n", *iter); - - g_print ("\n******* Expected environment:\n"); - g_hash_table_iter_init (&k, expected_env); - while (g_hash_table_iter_next (&k, (gpointer) &key, NULL)) - g_print (" %s\n", key); + const char *const *iter; + GHashTableIter k; + const char * key; + + g_print("\n******* Generated environment:\n"); + for (iter = denv; iter && *iter; iter++) + g_print(" %s\n", *iter); + + g_print("\n******* Expected environment:\n"); + g_hash_table_iter_init(&k, expected_env); + while (g_hash_table_iter_next(&k, (gpointer) &key, NULL)) + g_print(" %s\n", key); } static gboolean -parse_main (GKeyFile *kf, - const char *filename, - GVariant **out_con_dict, - GVariant **out_con_props, - char **out_expected_iface, - char **out_action, - char **out_connectivity_state, - char **out_vpn_ip_iface, - GError **error) +parse_main(GKeyFile * kf, + const char *filename, + GVariant ** out_con_dict, + GVariant ** out_con_props, + char ** out_expected_iface, + char ** out_action, + char ** out_connectivity_state, + char ** out_vpn_ip_iface, + GError ** error) { - nm_auto_clear_variant_builder GVariantBuilder props = { }; - gs_free char *uuid = NULL; - gs_free char *id = NULL; - gs_unref_object NMConnection *connection = NULL; - NMSettingConnection *s_con; - - *out_expected_iface = g_key_file_get_string (kf, "main", "expected-iface", NULL); - - *out_connectivity_state = g_key_file_get_string (kf, "main", "connectivity-state", NULL); - *out_vpn_ip_iface = g_key_file_get_string (kf, "main", "vpn-ip-iface", NULL); - - *out_action = g_key_file_get_string (kf, "main", "action", error); - if (*out_action == NULL) - return FALSE; - - uuid = g_key_file_get_string (kf, "main", "uuid", error); - if (uuid == NULL) - return FALSE; - id = g_key_file_get_string (kf, "main", "id", error); - if (id == NULL) - return FALSE; - - connection = nm_simple_connection_new (); - s_con = (NMSettingConnection *) nm_setting_connection_new (); - g_object_set (s_con, - NM_SETTING_CONNECTION_UUID, uuid, - NM_SETTING_CONNECTION_ID, id, - NULL); - nm_connection_add_setting (connection, NM_SETTING (s_con)); - - *out_con_dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL); - - g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - g_variant_builder_add (&props, "{sv}", - NMD_CONNECTION_PROPS_PATH, - g_variant_new_object_path ("/org/freedesktop/NetworkManager/Connections/5")); - - /* Strip out the non-fixed portion of the filename */ - filename = strstr (filename, "/dispatcher"); - g_variant_builder_add (&props, "{sv}", - "filename", - g_variant_new_string (filename)); - - if (g_key_file_get_boolean (kf, "main", "external", NULL)) { - g_variant_builder_add (&props, "{sv}", - "external", - g_variant_new_boolean (TRUE)); - } - - *out_con_props = g_variant_builder_end (&props); - - return TRUE; + nm_auto_clear_variant_builder GVariantBuilder props = {}; + gs_free char * uuid = NULL; + gs_free char * id = NULL; + gs_unref_object NMConnection *connection = NULL; + NMSettingConnection * s_con; + + *out_expected_iface = g_key_file_get_string(kf, "main", "expected-iface", NULL); + + *out_connectivity_state = g_key_file_get_string(kf, "main", "connectivity-state", NULL); + *out_vpn_ip_iface = g_key_file_get_string(kf, "main", "vpn-ip-iface", NULL); + + *out_action = g_key_file_get_string(kf, "main", "action", error); + if (*out_action == NULL) + return FALSE; + + uuid = g_key_file_get_string(kf, "main", "uuid", error); + if (uuid == NULL) + return FALSE; + id = g_key_file_get_string(kf, "main", "id", error); + if (id == NULL) + return FALSE; + + connection = nm_simple_connection_new(); + s_con = (NMSettingConnection *) nm_setting_connection_new(); + g_object_set(s_con, NM_SETTING_CONNECTION_UUID, uuid, NM_SETTING_CONNECTION_ID, id, NULL); + nm_connection_add_setting(connection, NM_SETTING(s_con)); + + *out_con_dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + + g_variant_builder_init(&props, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add( + &props, + "{sv}", + NMD_CONNECTION_PROPS_PATH, + g_variant_new_object_path("/org/freedesktop/NetworkManager/Connections/5")); + + /* Strip out the non-fixed portion of the filename */ + filename = strstr(filename, "/dispatcher"); + g_variant_builder_add(&props, "{sv}", "filename", g_variant_new_string(filename)); + + if (g_key_file_get_boolean(kf, "main", "external", NULL)) { + g_variant_builder_add(&props, "{sv}", "external", g_variant_new_boolean(TRUE)); + } + + *out_con_props = g_variant_builder_end(&props); + + return TRUE; } static gboolean -parse_device (GKeyFile *kf, GVariant **out_device_props, GError **error) +parse_device(GKeyFile *kf, GVariant **out_device_props, GError **error) { - nm_auto_clear_variant_builder GVariantBuilder props = { }; - gs_free char *tmp = NULL; - int i; - - g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - - i = g_key_file_get_integer (kf, "device", "state", error); - if (i == 0) - return FALSE; - g_variant_builder_add (&props, "{sv}", - NMD_DEVICE_PROPS_STATE, - g_variant_new_uint32 (i)); - - i = g_key_file_get_integer (kf, "device", "type", error); - if (i == 0) - return FALSE; - g_variant_builder_add (&props, "{sv}", - NMD_DEVICE_PROPS_TYPE, - g_variant_new_uint32 (i)); - - tmp = g_key_file_get_string (kf, "device", "interface", error); - if (tmp == NULL) - return FALSE; - g_variant_builder_add (&props, "{sv}", - NMD_DEVICE_PROPS_INTERFACE, - g_variant_new_string (tmp)); - - nm_clear_g_free (&tmp); - tmp = g_key_file_get_string (kf, "device", "ip-interface", error); - if (tmp == NULL) - return FALSE; - g_variant_builder_add (&props, "{sv}", - NMD_DEVICE_PROPS_IP_INTERFACE, - g_variant_new_string (tmp)); - - nm_clear_g_free (&tmp); - tmp = g_key_file_get_string (kf, "device", "path", error); - if (tmp == NULL) - return FALSE; - g_variant_builder_add (&props, "{sv}", - NMD_DEVICE_PROPS_PATH, - g_variant_new_object_path (tmp)); - - *out_device_props = g_variant_builder_end (&props); - return TRUE; + nm_auto_clear_variant_builder GVariantBuilder props = {}; + gs_free char * tmp = NULL; + int i; + + g_variant_builder_init(&props, G_VARIANT_TYPE("a{sv}")); + + i = g_key_file_get_integer(kf, "device", "state", error); + if (i == 0) + return FALSE; + g_variant_builder_add(&props, "{sv}", NMD_DEVICE_PROPS_STATE, g_variant_new_uint32(i)); + + i = g_key_file_get_integer(kf, "device", "type", error); + if (i == 0) + return FALSE; + g_variant_builder_add(&props, "{sv}", NMD_DEVICE_PROPS_TYPE, g_variant_new_uint32(i)); + + tmp = g_key_file_get_string(kf, "device", "interface", error); + if (tmp == NULL) + return FALSE; + g_variant_builder_add(&props, "{sv}", NMD_DEVICE_PROPS_INTERFACE, g_variant_new_string(tmp)); + + nm_clear_g_free(&tmp); + tmp = g_key_file_get_string(kf, "device", "ip-interface", error); + if (tmp == NULL) + return FALSE; + g_variant_builder_add(&props, "{sv}", NMD_DEVICE_PROPS_IP_INTERFACE, g_variant_new_string(tmp)); + + nm_clear_g_free(&tmp); + tmp = g_key_file_get_string(kf, "device", "path", error); + if (tmp == NULL) + return FALSE; + g_variant_builder_add(&props, "{sv}", NMD_DEVICE_PROPS_PATH, g_variant_new_object_path(tmp)); + + *out_device_props = g_variant_builder_end(&props); + return TRUE; } static gboolean -add_uint_array (GKeyFile *kf, - GVariantBuilder *props, - const char *section, - const char *key, - GError **error) +add_uint_array(GKeyFile * kf, + GVariantBuilder *props, + const char * section, + const char * key, + GError ** error) { - gs_free char *tmp = NULL; - gs_free const char **split = NULL; - gsize i; - - tmp = g_key_file_get_string (kf, section, key, NULL); - if (tmp == NULL) - return TRUE; - - split = nm_utils_strsplit_set_with_empty (tmp, " "); - if (split) { - gs_unref_array GArray *items = NULL; - - items = g_array_sized_new (FALSE, TRUE, sizeof (guint32), NM_PTRARRAY_LEN (split)); - for (i = 0; split[i]; i++) { - const char *s; - - s = split[i]; - g_strstrip ((char *) s); - if (s[0]) { - guint32 addr; - - if (inet_pton (AF_INET, s, &addr) != 1) - g_assert_not_reached (); - g_array_append_val (items, addr); - } - } - g_variant_builder_add (props, "{sv}", key, - g_variant_new_fixed_array (G_VARIANT_TYPE_UINT32, - items->data, items->len, - sizeof (guint32))); - } - - return TRUE; + gs_free char * tmp = NULL; + gs_free const char **split = NULL; + gsize i; + + tmp = g_key_file_get_string(kf, section, key, NULL); + if (tmp == NULL) + return TRUE; + + split = nm_utils_strsplit_set_with_empty(tmp, " "); + if (split) { + gs_unref_array GArray *items = NULL; + + items = g_array_sized_new(FALSE, TRUE, sizeof(guint32), NM_PTRARRAY_LEN(split)); + for (i = 0; split[i]; i++) { + const char *s; + + s = split[i]; + g_strstrip((char *) s); + if (s[0]) { + guint32 addr; + + if (inet_pton(AF_INET, s, &addr) != 1) + g_assert_not_reached(); + g_array_append_val(items, addr); + } + } + g_variant_builder_add(props, + "{sv}", + key, + g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + items->data, + items->len, + sizeof(guint32))); + } + + return TRUE; } static gboolean -parse_proxy (GKeyFile *kf, GVariant **out_props, const char *section, GError **error) +parse_proxy(GKeyFile *kf, GVariant **out_props, const char *section, GError **error) { - nm_auto_clear_variant_builder GVariantBuilder props = { }; - gs_free char *tmp = NULL; - - g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - - tmp = g_key_file_get_string (kf, section, "pac-url", error); - if (tmp == NULL) - return FALSE; - g_variant_builder_add (&props, "{sv}", - "pac-url", - g_variant_new_string (tmp)); - - nm_clear_g_free (&tmp); - tmp = g_key_file_get_string (kf, section, "pac-script", error); - if (tmp == NULL) - return FALSE; - g_variant_builder_add (&props, "{sv}", - "pac-script", - g_variant_new_string (tmp)); - - *out_props = g_variant_builder_end (&props); - return TRUE; + nm_auto_clear_variant_builder GVariantBuilder props = {}; + gs_free char * tmp = NULL; + + g_variant_builder_init(&props, G_VARIANT_TYPE("a{sv}")); + + tmp = g_key_file_get_string(kf, section, "pac-url", error); + if (tmp == NULL) + return FALSE; + g_variant_builder_add(&props, "{sv}", "pac-url", g_variant_new_string(tmp)); + + nm_clear_g_free(&tmp); + tmp = g_key_file_get_string(kf, section, "pac-script", error); + if (tmp == NULL) + return FALSE; + g_variant_builder_add(&props, "{sv}", "pac-script", g_variant_new_string(tmp)); + + *out_props = g_variant_builder_end(&props); + return TRUE; } static gboolean -parse_ip4 (GKeyFile *kf, GVariant **out_props, const char *section, GError **error) +parse_ip4(GKeyFile *kf, GVariant **out_props, const char *section, GError **error) { - nm_auto_clear_variant_builder GVariantBuilder props = { }; - gs_free char *tmp = NULL; - gs_free const char **split = NULL; - const char **iter; - - g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - - /* search domains */ - /* Use char** for domains. (DBUS_TYPE_G_ARRAY_OF_STRING of NMIP4Config - * becomes G_TYPE_STRV when sending the value over D-Bus) - */ - tmp = g_key_file_get_string (kf, section, "domains", error); - if (tmp == NULL) - return FALSE; - split = nm_utils_strsplit_set_with_empty (tmp, " "); - if (split) { - for (iter = split; *iter; iter++) - g_strstrip ((char *) *iter); - g_variant_builder_add (&props, "{sv}", "domains", g_variant_new_strv ((gpointer) split, -1)); - } - nm_clear_g_free (&split); - - if (!add_uint_array (kf, &props, "ip4", "nameservers", error)) - return FALSE; - - if (!add_uint_array (kf, &props, "ip4", "wins-servers", error)) - return FALSE; - - nm_clear_g_free (&tmp); - tmp = g_key_file_get_string (kf, section, "addresses", error); - if (tmp == NULL) - return FALSE; - split = nm_utils_strsplit_set_with_empty (tmp, ","); - if (split) { - gs_unref_ptrarray GPtrArray *addresses = NULL; - const char *gateway = NULL; - - addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref); - for (iter = split; *iter; iter++) { - const char *s = *iter; - NMIPAddress *addr; - const char *ip; - const char *prefix; - - g_strstrip ((char *) s); - if (s[0] == '\0') - continue; - - ip = *iter; - - prefix = strchr (ip, '/'); - g_assert (prefix); - ((char *) (prefix++))[0] = '\0'; - - if (addresses->len == 0) { - gateway = strchr (prefix, ' '); - g_assert (gateway); - gateway++; - } - - addr = nm_ip_address_new (AF_INET, ip, (guint) atoi (prefix), error); - if (!addr) - return FALSE; - - g_ptr_array_add (addresses, addr); - } - - g_variant_builder_add (&props, "{sv}", "addresses", - nm_utils_ip4_addresses_to_variant (addresses, gateway)); - } - nm_clear_g_free (&split); - - nm_clear_g_free (&tmp); - tmp = g_key_file_get_string (kf, section, "routes", NULL); - split = nm_utils_strsplit_set_with_empty (tmp, ","); - if (split) { - gs_unref_ptrarray GPtrArray *routes = NULL; - - routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref); - for (iter = split; *iter; iter++) { - const char *s = *iter; - NMIPRoute *route; - const char *dest; - const char *prefix; - const char *next_hop; - const char *metric; - - g_strstrip ((char *) s); - if (s[0] == '\0') - continue; - - dest = s; - - prefix = strchr (dest, '/'); - g_assert (prefix); - ((char *) (prefix++))[0] = '\0'; - - next_hop = strchr (prefix, ' '); - g_assert (next_hop); - ((char *) (next_hop++))[0] = '\0'; - - metric = strchr (next_hop, ' '); - g_assert (metric); - ((char *) (metric++))[0] = '\0'; - - route = nm_ip_route_new (AF_INET, - dest, - _nm_utils_ascii_str_to_int64 (prefix, 10, 0, 32, 255), - next_hop, - (guint) atoi (metric), - error); - if (!route) - return FALSE; - g_ptr_array_add (routes, route); - } - - g_variant_builder_add (&props, "{sv}", "routes", - nm_utils_ip4_routes_to_variant (routes)); - } - - *out_props = g_variant_builder_end (&props); - return TRUE; + nm_auto_clear_variant_builder GVariantBuilder props = {}; + gs_free char * tmp = NULL; + gs_free const char ** split = NULL; + const char ** iter; + + g_variant_builder_init(&props, G_VARIANT_TYPE("a{sv}")); + + /* search domains */ + /* Use char** for domains. (DBUS_TYPE_G_ARRAY_OF_STRING of NMIP4Config + * becomes G_TYPE_STRV when sending the value over D-Bus) + */ + tmp = g_key_file_get_string(kf, section, "domains", error); + if (tmp == NULL) + return FALSE; + split = nm_utils_strsplit_set_with_empty(tmp, " "); + if (split) { + for (iter = split; *iter; iter++) + g_strstrip((char *) *iter); + g_variant_builder_add(&props, "{sv}", "domains", g_variant_new_strv((gpointer) split, -1)); + } + nm_clear_g_free(&split); + + if (!add_uint_array(kf, &props, "ip4", "nameservers", error)) + return FALSE; + + if (!add_uint_array(kf, &props, "ip4", "wins-servers", error)) + return FALSE; + + nm_clear_g_free(&tmp); + tmp = g_key_file_get_string(kf, section, "addresses", error); + if (tmp == NULL) + return FALSE; + split = nm_utils_strsplit_set_with_empty(tmp, ","); + if (split) { + gs_unref_ptrarray GPtrArray *addresses = NULL; + const char * gateway = NULL; + + addresses = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_address_unref); + for (iter = split; *iter; iter++) { + const char * s = *iter; + NMIPAddress *addr; + const char * ip; + const char * prefix; + + g_strstrip((char *) s); + if (s[0] == '\0') + continue; + + ip = *iter; + + prefix = strchr(ip, '/'); + g_assert(prefix); + ((char *) (prefix++))[0] = '\0'; + + if (addresses->len == 0) { + gateway = strchr(prefix, ' '); + g_assert(gateway); + gateway++; + } + + addr = nm_ip_address_new(AF_INET, ip, (guint) atoi(prefix), error); + if (!addr) + return FALSE; + + g_ptr_array_add(addresses, addr); + } + + g_variant_builder_add(&props, + "{sv}", + "addresses", + nm_utils_ip4_addresses_to_variant(addresses, gateway)); + } + nm_clear_g_free(&split); + + nm_clear_g_free(&tmp); + tmp = g_key_file_get_string(kf, section, "routes", NULL); + split = nm_utils_strsplit_set_with_empty(tmp, ","); + if (split) { + gs_unref_ptrarray GPtrArray *routes = NULL; + + routes = g_ptr_array_new_with_free_func((GDestroyNotify) nm_ip_route_unref); + for (iter = split; *iter; iter++) { + const char *s = *iter; + NMIPRoute * route; + const char *dest; + const char *prefix; + const char *next_hop; + const char *metric; + + g_strstrip((char *) s); + if (s[0] == '\0') + continue; + + dest = s; + + prefix = strchr(dest, '/'); + g_assert(prefix); + ((char *) (prefix++))[0] = '\0'; + + next_hop = strchr(prefix, ' '); + g_assert(next_hop); + ((char *) (next_hop++))[0] = '\0'; + + metric = strchr(next_hop, ' '); + g_assert(metric); + ((char *) (metric++))[0] = '\0'; + + route = nm_ip_route_new(AF_INET, + dest, + _nm_utils_ascii_str_to_int64(prefix, 10, 0, 32, 255), + next_hop, + (guint) atoi(metric), + error); + if (!route) + return FALSE; + g_ptr_array_add(routes, route); + } + + g_variant_builder_add(&props, "{sv}", "routes", nm_utils_ip4_routes_to_variant(routes)); + } + + *out_props = g_variant_builder_end(&props); + return TRUE; } static gboolean -parse_dhcp (GKeyFile *kf, - const char *group_name, - GVariant **out_props, - GError **error) +parse_dhcp(GKeyFile *kf, const char *group_name, GVariant **out_props, GError **error) { - nm_auto_clear_variant_builder GVariantBuilder props = { }; - gs_strfreev char **keys = NULL; - char **iter; - - keys = g_key_file_get_keys (kf, group_name, NULL, error); - if (!keys) - return FALSE; - - g_variant_builder_init (&props, G_VARIANT_TYPE ("a{sv}")); - for (iter = keys; iter && *iter; iter++) { - gs_free char *val = NULL; - - val = g_key_file_get_string (kf, group_name, *iter, error); - if (!val) - return FALSE; - g_variant_builder_add (&props, "{sv}", *iter, g_variant_new_string (val)); - } - - *out_props = g_variant_builder_end (&props); - return TRUE; + nm_auto_clear_variant_builder GVariantBuilder props = {}; + gs_strfreev char ** keys = NULL; + char ** iter; + + keys = g_key_file_get_keys(kf, group_name, NULL, error); + if (!keys) + return FALSE; + + g_variant_builder_init(&props, G_VARIANT_TYPE("a{sv}")); + for (iter = keys; iter && *iter; iter++) { + gs_free char *val = NULL; + + val = g_key_file_get_string(kf, group_name, *iter, error); + if (!val) + return FALSE; + g_variant_builder_add(&props, "{sv}", *iter, g_variant_new_string(val)); + } + + *out_props = g_variant_builder_end(&props); + return TRUE; } static gboolean -get_dispatcher_file (const char *file, - GVariant **out_con_dict, - GVariant **out_con_props, - GVariant **out_device_props, - GVariant **out_device_proxy_props, - GVariant **out_device_ip4_props, - GVariant **out_device_ip6_props, - GVariant **out_device_dhcp4_props, - GVariant **out_device_dhcp6_props, - char **out_connectivity_state, - char **out_vpn_ip_iface, - GVariant **out_vpn_proxy_props, - GVariant **out_vpn_ip4_props, - GVariant **out_vpn_ip6_props, - char **out_expected_iface, - char **out_action, - GHashTable **out_env, - GError **error) +get_dispatcher_file(const char * file, + GVariant ** out_con_dict, + GVariant ** out_con_props, + GVariant ** out_device_props, + GVariant ** out_device_proxy_props, + GVariant ** out_device_ip4_props, + GVariant ** out_device_ip6_props, + GVariant ** out_device_dhcp4_props, + GVariant ** out_device_dhcp6_props, + char ** out_connectivity_state, + char ** out_vpn_ip_iface, + GVariant ** out_vpn_proxy_props, + GVariant ** out_vpn_ip4_props, + GVariant ** out_vpn_ip6_props, + char ** out_expected_iface, + char ** out_action, + GHashTable **out_env, + GError ** error) { - gs_unref_keyfile GKeyFile *kf = NULL; - gs_strfreev char **keys = NULL; - char **iter; - - g_assert (!error || !*error); - g_assert (out_con_dict && !*out_con_dict); - g_assert (out_con_props && !*out_con_props); - g_assert (out_device_props && !*out_device_props); - g_assert (out_device_proxy_props && !*out_device_proxy_props); - g_assert (out_device_ip4_props && !*out_device_ip4_props); - g_assert (out_device_ip6_props && !*out_device_ip6_props); - g_assert (out_device_dhcp4_props && !*out_device_dhcp4_props); - g_assert (out_device_dhcp6_props && !*out_device_dhcp6_props); - g_assert (out_connectivity_state && !*out_connectivity_state); - g_assert (out_vpn_ip_iface && !*out_vpn_ip_iface); - g_assert (out_vpn_proxy_props && !*out_vpn_proxy_props); - g_assert (out_vpn_ip4_props && !*out_vpn_ip4_props); - g_assert (out_vpn_ip6_props && !*out_vpn_ip6_props); - g_assert (out_expected_iface && !*out_expected_iface); - g_assert (out_action && !*out_action); - g_assert (out_env && !*out_env); - - kf = g_key_file_new (); - if (!g_key_file_load_from_file (kf, file, G_KEY_FILE_NONE, error)) - return FALSE; - - if (!parse_main (kf, - file, - out_con_dict, - out_con_props, - out_expected_iface, - out_action, - out_connectivity_state, - out_vpn_ip_iface, - error)) - return FALSE; - - if (!parse_device (kf, out_device_props, error)) - return FALSE; - - if (g_key_file_has_group (kf, "proxy")) { - if (!parse_proxy (kf, out_device_proxy_props, "proxy", error)) - return FALSE; - } - - if (g_key_file_has_group (kf, "ip4")) { - if (!parse_ip4 (kf, out_device_ip4_props, "ip4", error)) - return FALSE; - } - - if (g_key_file_has_group (kf, "dhcp4")) { - if (!parse_dhcp (kf, "dhcp4", out_device_dhcp4_props, error)) - return FALSE; - } - - if (g_key_file_has_group (kf, "dhcp6")) { - if (!parse_dhcp (kf, "dhcp6", out_device_dhcp6_props, error)) - return FALSE; - } - - g_assert (g_key_file_has_group (kf, "env")); - keys = g_key_file_get_keys (kf, "env", NULL, error); - *out_env = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - for (iter = keys; iter && *iter; iter++) { - gs_free char *val = NULL; - - val = g_key_file_get_string (kf, "env", *iter, error); - if (!val) - return FALSE; - g_hash_table_insert (*out_env, - g_strdup_printf ("%s=%s", *iter, val), - GUINT_TO_POINTER (1)); - } - - return TRUE; + nm_auto_unref_keyfile GKeyFile *kf = NULL; + gs_strfreev char ** keys = NULL; + char ** iter; + + g_assert(!error || !*error); + g_assert(out_con_dict && !*out_con_dict); + g_assert(out_con_props && !*out_con_props); + g_assert(out_device_props && !*out_device_props); + g_assert(out_device_proxy_props && !*out_device_proxy_props); + g_assert(out_device_ip4_props && !*out_device_ip4_props); + g_assert(out_device_ip6_props && !*out_device_ip6_props); + g_assert(out_device_dhcp4_props && !*out_device_dhcp4_props); + g_assert(out_device_dhcp6_props && !*out_device_dhcp6_props); + g_assert(out_connectivity_state && !*out_connectivity_state); + g_assert(out_vpn_ip_iface && !*out_vpn_ip_iface); + g_assert(out_vpn_proxy_props && !*out_vpn_proxy_props); + g_assert(out_vpn_ip4_props && !*out_vpn_ip4_props); + g_assert(out_vpn_ip6_props && !*out_vpn_ip6_props); + g_assert(out_expected_iface && !*out_expected_iface); + g_assert(out_action && !*out_action); + g_assert(out_env && !*out_env); + + kf = g_key_file_new(); + if (!g_key_file_load_from_file(kf, file, G_KEY_FILE_NONE, error)) + return FALSE; + + if (!parse_main(kf, + file, + out_con_dict, + out_con_props, + out_expected_iface, + out_action, + out_connectivity_state, + out_vpn_ip_iface, + error)) + return FALSE; + + if (!parse_device(kf, out_device_props, error)) + return FALSE; + + if (g_key_file_has_group(kf, "proxy")) { + if (!parse_proxy(kf, out_device_proxy_props, "proxy", error)) + return FALSE; + } + + if (g_key_file_has_group(kf, "ip4")) { + if (!parse_ip4(kf, out_device_ip4_props, "ip4", error)) + return FALSE; + } + + if (g_key_file_has_group(kf, "dhcp4")) { + if (!parse_dhcp(kf, "dhcp4", out_device_dhcp4_props, error)) + return FALSE; + } + + if (g_key_file_has_group(kf, "dhcp6")) { + if (!parse_dhcp(kf, "dhcp6", out_device_dhcp6_props, error)) + return FALSE; + } + + g_assert(g_key_file_has_group(kf, "env")); + keys = g_key_file_get_keys(kf, "env", NULL, error); + *out_env = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + for (iter = keys; iter && *iter; iter++) { + gs_free char *val = NULL; + + val = g_key_file_get_string(kf, "env", *iter, error); + if (!val) + return FALSE; + g_hash_table_insert(*out_env, g_strdup_printf("%s=%s", *iter, val), GUINT_TO_POINTER(1)); + } + + return TRUE; } /*****************************************************************************/ static void -test_generic (const char *file, const char *override_vpn_ip_iface) +test_generic(const char *file, const char *override_vpn_ip_iface) { - gs_unref_variant GVariant *con_dict = NULL; - gs_unref_variant GVariant *con_props = NULL; - gs_unref_variant GVariant *device_props = NULL; - gs_unref_variant GVariant *device_proxy_props = NULL; - gs_unref_variant GVariant *device_ip4_props = NULL; - gs_unref_variant GVariant *device_ip6_props = NULL; - gs_unref_variant GVariant *device_dhcp4_props = NULL; - gs_unref_variant GVariant *device_dhcp6_props = NULL; - gs_free char *connectivity_change = NULL; - gs_free char *vpn_ip_iface = NULL; - gs_unref_variant GVariant *vpn_proxy_props = NULL; - gs_unref_variant GVariant *vpn_ip4_props = NULL; - gs_unref_variant GVariant *vpn_ip6_props = NULL; - gs_free char *expected_iface = NULL; - gs_free char *action = NULL; - gs_free char *out_iface = NULL; - const char *error_message = NULL; - gs_unref_hashtable GHashTable *expected_env = NULL; - GError *error = NULL; - gboolean success; - gs_free char *filename = NULL; - gs_strfreev char **denv = NULL; - char **iter; - - filename = g_build_filename (TEST_DIR, file, NULL); - success = get_dispatcher_file (filename, - &con_dict, - &con_props, - &device_props, - &device_proxy_props, - &device_ip4_props, - &device_ip6_props, - &device_dhcp4_props, - &device_dhcp6_props, - &connectivity_change, - &vpn_ip_iface, - &vpn_proxy_props, - &vpn_ip4_props, - &vpn_ip6_props, - &expected_iface, - &action, - &expected_env, - &error); - nmtst_assert_success (success, error); - - /* Get the environment from the dispatcher code */ - denv = nm_dispatcher_utils_construct_envp (action, - con_dict, - con_props, - device_props, - device_proxy_props, - device_ip4_props, - device_ip6_props, - device_dhcp4_props, - device_dhcp6_props, - connectivity_change, - override_vpn_ip_iface ?: vpn_ip_iface, - vpn_proxy_props, - vpn_ip4_props, - vpn_ip6_props, - &out_iface, - &error_message); - - g_assert ((!denv && error_message) || (denv && !error_message)); - - if (error_message) - g_error ("FAILED: %s", error_message); - - if (g_strv_length (denv) != g_hash_table_size (expected_env)) { - _print_env (NM_CAST_STRV_CC (denv), expected_env); - g_assert_cmpint (g_strv_length (denv), ==, g_hash_table_size (expected_env)); - } - - /* Compare dispatcher generated env and expected env */ - for (iter = denv; iter && *iter; iter++) { - gpointer foo; - const char *i_value = *iter; - - if (strstr (i_value, "PATH=") == i_value) { - g_assert_cmpstr (&i_value[strlen("PATH=")], ==, g_getenv ("PATH")); - - /* The path is constructed dynamically. Ignore the actual value. */ - i_value = "PATH="; - } - - foo = g_hash_table_lookup (expected_env, i_value); - if (!foo) { - _print_env (NM_CAST_STRV_CC (denv), expected_env); - g_error ("Failed to find %s in environment", i_value); - } - } - - g_assert_cmpstr (expected_iface, ==, out_iface); + gs_unref_variant GVariant *con_dict = NULL; + gs_unref_variant GVariant *con_props = NULL; + gs_unref_variant GVariant *device_props = NULL; + gs_unref_variant GVariant *device_proxy_props = NULL; + gs_unref_variant GVariant *device_ip4_props = NULL; + gs_unref_variant GVariant *device_ip6_props = NULL; + gs_unref_variant GVariant *device_dhcp4_props = NULL; + gs_unref_variant GVariant *device_dhcp6_props = NULL; + gs_free char * connectivity_change = NULL; + gs_free char * vpn_ip_iface = NULL; + gs_unref_variant GVariant *vpn_proxy_props = NULL; + gs_unref_variant GVariant *vpn_ip4_props = NULL; + gs_unref_variant GVariant *vpn_ip6_props = NULL; + gs_free char * expected_iface = NULL; + gs_free char * action = NULL; + gs_free char * out_iface = NULL; + const char * error_message = NULL; + gs_unref_hashtable GHashTable *expected_env = NULL; + GError * error = NULL; + gboolean success; + gs_free char * filename = NULL; + gs_strfreev char ** denv = NULL; + char ** iter; + + filename = g_build_filename(TEST_DIR, file, NULL); + success = get_dispatcher_file(filename, + &con_dict, + &con_props, + &device_props, + &device_proxy_props, + &device_ip4_props, + &device_ip6_props, + &device_dhcp4_props, + &device_dhcp6_props, + &connectivity_change, + &vpn_ip_iface, + &vpn_proxy_props, + &vpn_ip4_props, + &vpn_ip6_props, + &expected_iface, + &action, + &expected_env, + &error); + nmtst_assert_success(success, error); + + /* Get the environment from the dispatcher code */ + denv = nm_dispatcher_utils_construct_envp(action, + con_dict, + con_props, + device_props, + device_proxy_props, + device_ip4_props, + device_ip6_props, + device_dhcp4_props, + device_dhcp6_props, + connectivity_change, + override_vpn_ip_iface ?: vpn_ip_iface, + vpn_proxy_props, + vpn_ip4_props, + vpn_ip6_props, + &out_iface, + &error_message); + + g_assert((!denv && error_message) || (denv && !error_message)); + + if (error_message) + g_error("FAILED: %s", error_message); + + if (g_strv_length(denv) != g_hash_table_size(expected_env)) { + _print_env(NM_CAST_STRV_CC(denv), expected_env); + g_assert_cmpint(g_strv_length(denv), ==, g_hash_table_size(expected_env)); + } + + /* Compare dispatcher generated env and expected env */ + for (iter = denv; iter && *iter; iter++) { + gpointer foo; + const char *i_value = *iter; + + if (strstr(i_value, "PATH=") == i_value) { + g_assert_cmpstr(&i_value[strlen("PATH=")], ==, g_getenv("PATH")); + + /* The path is constructed dynamically. Ignore the actual value. */ + i_value = "PATH="; + } + + foo = g_hash_table_lookup(expected_env, i_value); + if (!foo) { + _print_env(NM_CAST_STRV_CC(denv), expected_env); + g_error("Failed to find %s in environment", i_value); + } + } + + g_assert_cmpstr(expected_iface, ==, out_iface); } /*****************************************************************************/ static void -test_up (void) +test_up(void) { - test_generic ("dispatcher-up", NULL); + test_generic("dispatcher-up", NULL); } static void -test_down (void) +test_down(void) { - test_generic ("dispatcher-down", NULL); + test_generic("dispatcher-down", NULL); } static void -test_vpn_up (void) +test_vpn_up(void) { - test_generic ("dispatcher-vpn-up", NULL); + test_generic("dispatcher-vpn-up", NULL); } static void -test_vpn_down (void) +test_vpn_down(void) { - test_generic ("dispatcher-vpn-down", NULL); + test_generic("dispatcher-vpn-down", NULL); } static void -test_external (void) +test_external(void) { - test_generic ("dispatcher-external", NULL); + test_generic("dispatcher-external", NULL); } static void -test_connectivity_changed (void) +test_connectivity_changed(void) { - /* These tests will check that the CONNECTIVITY_STATE environment - * variable is only defined for known states, such as 'full'. */ - test_generic ("dispatcher-connectivity-unknown", NULL); - test_generic ("dispatcher-connectivity-full", NULL); + /* These tests will check that the CONNECTIVITY_STATE environment + * variable is only defined for known states, such as 'full'. */ + test_generic("dispatcher-connectivity-unknown", NULL); + test_generic("dispatcher-connectivity-full", NULL); } static void -test_up_empty_vpn_iface (void) +test_up_empty_vpn_iface(void) { - /* Test that an empty VPN iface variable, like is passed through D-Bus - * from NM, is ignored by the dispatcher environment construction code. - */ - test_generic ("dispatcher-up", ""); + /* Test that an empty VPN iface variable, like is passed through D-Bus + * from NM, is ignored by the dispatcher environment construction code. + */ + test_generic("dispatcher-up", ""); } /*****************************************************************************/ static void -test_gdbus_codegen (void) +test_gdbus_codegen(void) { - gs_unref_object NMDBusDispatcher *dbus_dispatcher = NULL; + gs_unref_object NMDBusDispatcher *dbus_dispatcher = NULL; - dbus_dispatcher = nmdbus_dispatcher_skeleton_new (); - g_assert (NMDBUS_IS_DISPATCHER_SKELETON (dbus_dispatcher)); + dbus_dispatcher = nmdbus_dispatcher_skeleton_new(); + g_assert(NMDBUS_IS_DISPATCHER_SKELETON(dbus_dispatcher)); } /*****************************************************************************/ -NMTST_DEFINE (); +NMTST_DEFINE(); int -main (int argc, char **argv) +main(int argc, char **argv) { - nmtst_init (&argc, &argv, TRUE); + nmtst_init(&argc, &argv, TRUE); - g_test_add_func ("/dispatcher/up", test_up); - g_test_add_func ("/dispatcher/down", test_down); - g_test_add_func ("/dispatcher/vpn_up", test_vpn_up); - g_test_add_func ("/dispatcher/vpn_down", test_vpn_down); - g_test_add_func ("/dispatcher/external", test_external); - g_test_add_func ("/dispatcher/connectivity_changed", test_connectivity_changed); + g_test_add_func("/dispatcher/up", test_up); + g_test_add_func("/dispatcher/down", test_down); + g_test_add_func("/dispatcher/vpn_up", test_vpn_up); + g_test_add_func("/dispatcher/vpn_down", test_vpn_down); + g_test_add_func("/dispatcher/external", test_external); + g_test_add_func("/dispatcher/connectivity_changed", test_connectivity_changed); - g_test_add_func ("/dispatcher/up_empty_vpn_iface", test_up_empty_vpn_iface); + g_test_add_func("/dispatcher/up_empty_vpn_iface", test_up_empty_vpn_iface); - g_test_add_func ("/dispatcher/gdbus-codegen", test_gdbus_codegen); + g_test_add_func("/dispatcher/gdbus-codegen", test_gdbus_codegen); - return g_test_run (); + return g_test_run(); } - |