summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-10-29 09:12:18 -0500
committerDan Williams <dcbw@redhat.com>2014-11-07 10:53:40 -0600
commita770b00f70435f7ed922b912c7e38289235e90f5 (patch)
treef31d2cfadb87fe7a0af2d154d2c7092153f990e9
parent48288ff9d15366db3642d01f0ad8b177d7a710f2 (diff)
downloadNetworkManager-dcbw/rh863515-rh1083683-config-then-quit.tar.gz
iface-helper: add nm-iface-helper for dynamic configure-then-quit supportdcbw/rh863515-rh1083683-config-then-quit
When quitting, the Manager asks each device to spawn the interface helper, which persists and manages dynamic address on the interface after NetworkManager is gone. If the dynamic address cannot be maintaned, the helper quits and the interface's address may be removed when their lifetime runs out. To keep the helper as simple as possible, NetworkManager passes most of the configuration on the command-line, including some properties of the device's current state, which are necessary for the helper to maintain DHCP leases or IPv6 SLAAC addresses.
-rw-r--r--.gitignore1
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/Makefile.am85
-rw-r--r--src/devices/nm-device.c167
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c9
-rw-r--r--src/main.c24
-rw-r--r--src/nm-config.c80
-rw-r--r--src/nm-iface-helper.c529
-rw-r--r--src/nm-ip4-config.c4
-rw-r--r--src/nm-ip6-config.c4
-rw-r--r--src/nm-manager.c28
-rw-r--r--src/nm-manager.h2
13 files changed, 864 insertions, 72 deletions
diff --git a/.gitignore b/.gitignore
index d7c47e3899..8aa06cad5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -232,6 +232,7 @@ valgrind-*.log
/src/dhcp-manager/tests/test-dhcp-options
/src/dhcp-manager/tests/test-dhcp-utils
/src/dnsmasq-manager/tests/test-dnsmasq-utils
+/src/nm-iface-helper
/src/settings/plugins/ibft/tests/test-ibft
/src/settings/plugins/ifcfg-rh/tests/network-scripts/*-Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/network-scripts/Test_Write_*
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b964b93f0e..0571f0e238 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -147,6 +147,7 @@ src/devices/wifi/nm-wifi-ap-utils.c
src/devices/wimax/nm-device-wimax.c
src/devices/wwan/nm-modem-broadband.c
src/nm-config.c
+src/nm-iface-helper.c
src/nm-logging.c
src/nm-manager.c
src/nm-sleep-monitor-systemd.c
diff --git a/src/Makefile.am b/src/Makefile.am
index e186632548..eb5c64c089 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -48,7 +48,10 @@ AM_CPPFLAGS = \
# primarily for its side effect of removing duplicates.
AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
-noinst_LTLIBRARIES = libNetworkManager.la libsystemd-dhcp.la
+noinst_LTLIBRARIES = \
+ libNetworkManager.la \
+ libnm-iface-helper.la \
+ libsystemd-dhcp.la
######################
# libsystemd-dhcp
@@ -458,6 +461,86 @@ NetworkManager_LDFLAGS = -rdynamic
######################
+libnm_iface_helper_la_SOURCES = \
+ dhcp-manager/nm-dhcp-client.c \
+ dhcp-manager/nm-dhcp-client.h \
+ dhcp-manager/nm-dhcp-utils.c \
+ dhcp-manager/nm-dhcp-utils.h \
+ dhcp-manager/nm-dhcp-manager.c \
+ dhcp-manager/nm-dhcp-manager.h \
+ \
+ platform/nm-linux-platform.c \
+ platform/nm-linux-platform.h \
+ platform/nm-platform.c \
+ platform/nm-platform.h \
+ platform/wifi/wifi-utils-nl80211.c \
+ platform/wifi/wifi-utils-nl80211.h \
+ platform/wifi/wifi-utils-private.h \
+ platform/wifi/wifi-utils.c \
+ platform/wifi/wifi-utils.h \
+ \
+ rdisc/nm-fake-rdisc.c \
+ rdisc/nm-fake-rdisc.h \
+ rdisc/nm-lndp-rdisc.c \
+ rdisc/nm-lndp-rdisc.h \
+ rdisc/nm-rdisc.c \
+ rdisc/nm-rdisc.h \
+ \
+ nm-ip4-config.c \
+ nm-ip4-config.h \
+ nm-ip6-config.c \
+ nm-ip6-config.h \
+ \
+ nm-enum-types.c \
+ nm-enum-types.h \
+ nm-logging.c \
+ nm-logging.h \
+ nm-posix-signals.c \
+ nm-posix-signals.h \
+ NetworkManagerUtils.c \
+ NetworkManagerUtils.h
+
+if WITH_WEXT
+libnm_iface_helper_la_SOURCES += \
+ platform/wifi/wifi-utils-wext.c \
+ platform/wifi/wifi-utils-wext.h
+endif
+
+libnm_iface_helper_la_LIBADD = \
+ $(top_builddir)/libnm-core/libnm-core.la \
+ libsystemd-dhcp.la \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS) \
+ $(GUDEV_LIBS) \
+ $(LIBNL_LIBS) \
+ $(LIBNDP_LIBS) \
+ $(LIBDL) \
+ $(LIBM)
+
+libexec_PROGRAMS = nm-iface-helper
+
+nm_iface_helper_SOURCES = \
+ dhcp-manager/nm-dhcp-systemd.h \
+ dhcp-manager/nm-dhcp-systemd.c \
+ nm-iface-helper.c \
+ main-utils.c \
+ main-utils.h
+
+nm_iface_helper_LDADD = \
+ $(top_builddir)/libnm-core/libnm-core.la \
+ libsystemd-dhcp.la \
+ libnm-iface-helper.la \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS) \
+ $(GUDEV_LIBS) \
+ $(LIBNL_LIBS) \
+ $(LIBNDP_LIBS) \
+ $(LIBM)
+
+nm_iface_helper_LDFLAGS = -rdynamic
+
+######################
+
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.NetworkManager.conf
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9d8932f18a..3390bf765e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -6990,6 +6990,173 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
_cleanup_generic_post (self, TRUE);
}
+static char *
+bin2hexstr (const char *bytes, gsize len)
+{
+ GString *str;
+ int i;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (len > 0, NULL);
+
+ str = g_string_sized_new (len * 2 + 1);
+ for (i = 0; i < len; i++) {
+ if (str->len)
+ g_string_append_c (str, ':');
+ g_string_append_printf (str, "%02x", (guint8) bytes[i]);
+ }
+ return g_string_free (str, FALSE);
+}
+
+static char *
+find_dhcp4_address (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ guint i, n;
+
+ if (!priv->ip4_config)
+ return NULL;
+
+ n = nm_ip4_config_get_num_addresses (priv->ip4_config);
+ for (i = 0; i < n; i++) {
+ const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i);
+
+ if (a->source == NM_IP_CONFIG_SOURCE_DHCP)
+ return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
+ }
+ return NULL;
+}
+
+void
+nm_device_spawn_iface_helper (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gboolean priority_set = FALSE, configured = FALSE;
+ NMConnection *connection;
+ GError *error = NULL;
+ const char *method;
+ GPtrArray *argv;
+ gs_free char *dhcp4_address = NULL;
+
+ if (priv->state != NM_DEVICE_STATE_ACTIVATED)
+ return;
+ if (!nm_device_can_assume_connections (self))
+ return;
+
+ connection = nm_device_get_connection (self);
+ g_assert (connection);
+
+ argv = g_ptr_array_sized_new (10);
+ g_ptr_array_set_free_func (argv, g_free);
+
+ g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/nm-iface-helper"));
+ g_ptr_array_add (argv, g_strdup ("--ifname"));
+ g_ptr_array_add (argv, g_strdup (nm_device_get_ip_iface (self)));
+ g_ptr_array_add (argv, g_strdup ("--uuid"));
+ g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
+
+ dhcp4_address = find_dhcp4_address (self);
+
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+ if ( priv->ip4_config
+ && priv->ip4_state == IP_DONE
+ && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
+ && priv->dhcp4_client
+ && dhcp4_address) {
+ NMSettingIPConfig *s_ip4;
+ GBytes *client_id;
+ char *hex_client_id;
+ const char *hostname;
+
+ s_ip4 = nm_connection_get_setting_ip4_config (connection);
+ g_assert (s_ip4);
+
+ g_ptr_array_add (argv, g_strdup ("--priority"));
+ g_ptr_array_add (argv, g_strdup_printf ("%u", nm_dhcp_client_get_priority (priv->dhcp4_client)));
+ priority_set = TRUE;
+
+ g_ptr_array_add (argv, g_strdup ("--dhcp4"));
+ g_ptr_array_add (argv, g_strdup (dhcp4_address));
+ if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE)
+ g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
+
+ client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
+ if (client_id) {
+ g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid"));
+ hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL),
+ g_bytes_get_size (client_id));
+ g_ptr_array_add (argv, hex_client_id);
+ }
+
+ hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
+ if (client_id) {
+ g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
+ g_ptr_array_add (argv, g_strdup (hostname));
+ }
+
+ configured = TRUE;
+ }
+
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+ if ( priv->ip6_config
+ && priv->ip6_state == IP_DONE
+ && g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
+ && priv->rdisc
+ && priv->ac_ip6_config) {
+ NMSettingIPConfig *s_ip6;
+ char *hex_iid;
+ NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
+
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ g_assert (s_ip6);
+
+ g_ptr_array_add (argv, g_strdup ("--slaac"));
+
+ if (nm_setting_ip_config_get_may_fail (s_ip6) == FALSE)
+ g_ptr_array_add (argv, g_strdup ("--slaac-required"));
+
+ g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr"));
+ g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr));
+
+ if (nm_device_get_ip_iface_identifier (self, &iid)) {
+ g_ptr_array_add (argv, g_strdup ("--iid"));
+ hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId));
+ g_ptr_array_add (argv, hex_iid);
+ }
+
+ configured = TRUE;
+ }
+
+ if (configured) {
+ GPid pid;
+
+ if (!priority_set) {
+ g_ptr_array_add (argv, g_strdup ("--priority"));
+ g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_priority (self)));
+ }
+
+ g_ptr_array_add (argv, NULL);
+
+ if (nm_logging_enabled (LOGL_DEBUG, LOGD_DEVICE)) {
+ char *tmp;
+
+ tmp = g_strjoinv (" ", (char **) argv->pdata);
+ _LOGD (LOGD_DEVICE, "running '%s'", tmp);
+ g_free (tmp);
+ }
+
+ if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) {
+ _LOGI (LOGD_DEVICE, "spawned helper PID %u", (guint) pid);
+ } else {
+ _LOGW (LOGD_DEVICE, "failed to spawn helper: %s", error->message);
+ g_error_free (error);
+ }
+ }
+
+ g_ptr_array_unref (argv);
+}
+
/***********************************************************/
static gboolean
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index f7b91e00cc..840a19c08b 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -365,6 +365,8 @@ NMConnection *nm_device_new_default_connection (NMDevice *self);
const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self);
const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self);
+void nm_device_spawn_iface_helper (NMDevice *self);
+
G_END_DECLS
/* For testing only */
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 563b7ac91a..3147980b4b 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -386,11 +386,18 @@ static void
nm_dhcp_manager_init (NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ NMConfig *config = nm_config_get ();
const char *client;
GError *error = NULL;
/* Client-specific setup */
- client = nm_config_get_dhcp_client (nm_config_get ());
+ client = nm_config_get_dhcp_client (config);
+ if (nm_config_get_configure_and_quit (config)) {
+ if (g_strcmp0 (client, "internal") != 0)
+ nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
+ client = "internal";
+ }
+
priv->client_type = get_client_type (client, &error);
if (priv->client_type == G_TYPE_INVALID) {
nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
diff --git a/src/main.c b/src/main.c
index 708545fd4c..476d2a7711 100644
--- a/src/main.c
+++ b/src/main.c
@@ -178,17 +178,10 @@ _init_nm_debug (const char *debug)
}
static void
-manager_startup_complete (NMManager *manager, GParamSpec *pspec, gpointer user_data)
+manager_configure_quit (NMManager *manager, gpointer user_data)
{
- NMConfig *config = NM_CONFIG (user_data);
- gboolean startup = FALSE;
-
- g_object_get (G_OBJECT (manager), NM_MANAGER_STARTUP, &startup, NULL);
-
- if (!startup && nm_config_get_configure_and_quit (config)) {
- nm_log_info (LOGD_CORE, "quitting now that startup is complete");
- g_main_loop_quit (main_loop);
- }
+ nm_log_info (LOGD_CORE, "quitting now that startup is complete");
+ g_main_loop_quit (main_loop);
}
/*
@@ -462,10 +455,7 @@ main (int argc, char *argv[])
}
}
- g_signal_connect (manager,
- "notify::" NM_MANAGER_STARTUP,
- G_CALLBACK (manager_startup_complete),
- config);
+ g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
nm_manager_start (manager);
@@ -485,10 +475,10 @@ main (int argc, char *argv[])
success = TRUE;
/* Told to quit before getting to the mainloop by the signal handler */
- if (quit_early == TRUE)
- goto done;
+ if (!quit_early)
+ g_main_loop_run (main_loop);
- g_main_loop_run (main_loop);
+ nm_manager_stop (manager);
done:
g_clear_object (&manager);
diff --git a/src/nm-config.c b/src/nm-config.c
index 26d5cb437a..aba4fa1a1b 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -74,41 +74,36 @@ G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
/************************************************************************/
static gboolean
-_parse_bool_str (const char *str, gboolean *out_value)
+_get_bool_value (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ gboolean default_value)
{
- gboolean value;
- gsize len;
- char *s = NULL;
-
- g_return_val_if_fail (str, FALSE);
-
- while (g_ascii_isspace (*str))
- str++;
-
- if (!*str)
- return FALSE;
-
- len = strlen (str);
-
- if (g_ascii_isspace (str[len-1])) {
- str = s = g_strdup (str);
- g_strchomp (s);
- }
-
- if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
- value = TRUE;
- else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
- value = FALSE;
- else {
- g_free (s);
- return FALSE;
+ gboolean value = default_value;
+ char *str;
+
+ g_return_val_if_fail (keyfile != NULL, default_value);
+ g_return_val_if_fail (section != NULL, default_value);
+ g_return_val_if_fail (key != NULL, default_value);
+
+ str = g_key_file_get_value (keyfile, section, key, NULL);
+ if (!str)
+ return default_value;
+
+ g_strstrip (str);
+ if (str[0]) {
+ if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
+ value = TRUE;
+ else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
+ value = FALSE;
+ else {
+ nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'",
+ section, key, str, default_value ? "true" : "false");
+ }
}
- if (out_value)
- *out_value = value;
-
- g_free (s);
- return TRUE;
+ g_free (str);
+ return value;
}
/************************************************************************/
@@ -521,7 +516,6 @@ nm_config_new (GError **error)
GFileInfo *info;
GPtrArray *confs;
const char *name;
- char *value;
int i;
GString *config_description;
@@ -591,23 +585,9 @@ nm_config_new (GError **error)
if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1);
- value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
- priv->monitor_connection_files = FALSE;
- if (value) {
- if (!_parse_bool_str (value, &priv->monitor_connection_files))
- nm_log_warn (LOGD_CORE, "Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
- g_free (value);
- }
+ priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE);
- value = g_key_file_get_value (priv->keyfile, "main", "auth-polkit", NULL);
- priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT;
- if (value) {
- if (!_parse_bool_str (value, &priv->auth_polkit)) {
- nm_log_warn (LOGD_CORE, "Unrecognized value for main.auth-polkit: %s. Assuming '%s'", value,
- NM_CONFIG_DEFAULT_AUTH_POLKIT ? "true" : "false");
- }
- g_free (value);
- }
+ priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
@@ -631,7 +611,7 @@ nm_config_new (GError **error)
priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
- priv->configure_and_quit = g_key_file_get_boolean (priv->keyfile, "main", "configure-and-quit", NULL);
+ priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE);
return singleton;
}
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
new file mode 100644
index 0000000000..62f1b2aba4
--- /dev/null
+++ b/src/nm-iface-helper.c
@@ -0,0 +1,529 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2014 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib-unix.h>
+#include <getopt.h>
+#include <locale.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "gsystem-local-alloc.h"
+#include "NetworkManagerUtils.h"
+#include "nm-linux-platform.h"
+#include "nm-dhcp-manager.h"
+#include "nm-logging.h"
+#include "main-utils.h"
+#include "nm-rdisc.h"
+#include "nm-lndp-rdisc.h"
+#include "nm-utils.h"
+
+#if !defined(NM_DIST_VERSION)
+# define NM_DIST_VERSION VERSION
+#endif
+
+#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid"
+
+static GMainLoop *main_loop = NULL;
+static char *ifname = NULL;
+static int ifindex = -1;
+static gboolean slaac_required = FALSE;
+static gboolean dhcp4_required = FALSE;
+static int tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+static int priority = -1;
+
+static void
+dhcp4_state_changed (NMDhcpClient *client,
+ NMDhcpState state,
+ NMIP4Config *ip4_config,
+ GHashTable *options,
+ gpointer user_data)
+{
+ static NMIP4Config *last_config = NULL;
+ NMIP4Config *existing;
+
+ g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
+
+ nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", ifname, state);
+
+ switch (state) {
+ case NM_DHCP_STATE_BOUND:
+ g_assert (ip4_config);
+ existing = nm_ip4_config_capture (ifindex, FALSE);
+ if (last_config)
+ nm_ip4_config_subtract (existing, last_config);
+
+ nm_ip4_config_merge (existing, ip4_config);
+ if (!nm_ip4_config_commit (existing, ifindex))
+ nm_log_warn (LOGD_DHCP4, "(%s): failed to apply DHCPv4 config", ifname);
+
+ if (last_config) {
+ g_object_unref (last_config);
+ last_config = nm_ip4_config_new ();
+ nm_ip4_config_replace (last_config, ip4_config, NULL);
+ }
+ break;
+ case NM_DHCP_STATE_TIMEOUT:
+ case NM_DHCP_STATE_DONE:
+ case NM_DHCP_STATE_FAIL:
+ if (dhcp4_required) {
+ nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed, quitting...", ifname);
+ g_main_loop_quit (main_loop);
+ } else
+ nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed", ifname);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
+{
+ static NMIP6Config *last_config = NULL;
+ NMIP6Config *existing;
+ NMIP6Config *ip6_config;
+ static int system_support = -1;
+ guint ifa_flags = 0x00;
+ int i;
+
+ if (system_support == -1) {
+ /*
+ * Check, if both libnl and the kernel are recent enough,
+ * to help user space handling RA. If it's not supported,
+ * we have no ipv6-privacy and must add autoconf addresses
+ * as /128. The reason for the /128 is to prevent the kernel
+ * from adding a prefix route for this address.
+ **/
+ system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
+ nm_platform_check_support_kernel_extended_ifa_flags ();
+ }
+
+ if (system_support)
+ ifa_flags = IFA_F_NOPREFIXROUTE;
+ if (tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
+ || tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
+ {
+ /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
+ ifa_flags |= IFA_F_MANAGETEMPADDR;
+ }
+
+ ip6_config = nm_ip6_config_new ();
+
+ if (changed & NM_RDISC_CONFIG_GATEWAYS) {
+ /* Use the first gateway as ordered in router discovery cache. */
+ if (rdisc->gateways->len) {
+ NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
+
+ nm_ip6_config_set_gateway (ip6_config, &gateway->address);
+ } else
+ nm_ip6_config_set_gateway (ip6_config, NULL);
+ }
+
+ if (changed & NM_RDISC_CONFIG_ADDRESSES) {
+ /* Rebuild address list from router discovery cache. */
+ nm_ip6_config_reset_addresses (ip6_config);
+
+ /* rdisc->addresses contains at most max_addresses entries.
+ * This is different from what the kernel does, which
+ * also counts static and temporary addresses when checking
+ * max_addresses.
+ **/
+ for (i = 0; i < rdisc->addresses->len; i++) {
+ NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+ NMPlatformIP6Address address;
+
+ memset (&address, 0, sizeof (address));
+ address.address = discovered_address->address;
+ address.plen = system_support ? 64 : 128;
+ address.timestamp = discovered_address->timestamp;
+ address.lifetime = discovered_address->lifetime;
+ address.preferred = discovered_address->preferred;
+ if (address.preferred > address.lifetime)
+ address.preferred = address.lifetime;
+ address.source = NM_IP_CONFIG_SOURCE_RDISC;
+ address.flags = ifa_flags;
+
+ nm_ip6_config_add_address (ip6_config, &address);
+ }
+ }
+
+ if (changed & NM_RDISC_CONFIG_ROUTES) {
+ /* Rebuild route list from router discovery cache. */
+ nm_ip6_config_reset_routes (ip6_config);
+
+ for (i = 0; i < rdisc->routes->len; i++) {
+ NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
+ NMPlatformIP6Route route;
+
+ /* Only accept non-default routes. The router has no idea what the
+ * local configuration or user preferences are, so sending routes
+ * with a prefix length of 0 is quite rude and thus ignored.
+ */
+ if (discovered_route->plen > 0) {
+ memset (&route, 0, sizeof (route));
+ route.network = discovered_route->network;
+ route.plen = discovered_route->plen;
+ route.gateway = discovered_route->gateway;
+ route.source = NM_IP_CONFIG_SOURCE_RDISC;
+ route.metric = priority;
+
+ nm_ip6_config_add_route (ip6_config, &route);
+ }
+ }
+ }
+
+ if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
+ /* Unsupported until systemd DHCPv6 is ready */
+ }
+
+ /* hop_limit == 0 is a special value "unspecified", so do not touch
+ * in this case */
+ if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) {
+ char val[16];
+
+ g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit);
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "hop_limit"), val);
+ }
+
+ if (changed & NM_RDISC_CONFIG_MTU) {
+ char val[16];
+
+ g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "mtu"), val);
+ }
+
+ existing = nm_ip6_config_capture (ifindex, FALSE, tempaddr);
+ if (last_config)
+ nm_ip6_config_subtract (existing, last_config);
+
+ nm_ip6_config_merge (existing, ip6_config);
+ if (!nm_ip6_config_commit (existing, ifindex))
+ nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", ifname);
+
+ if (last_config) {
+ g_object_unref (last_config);
+ last_config = nm_ip6_config_new ();
+ nm_ip6_config_replace (last_config, ip6_config, NULL);
+ }
+}
+
+static void
+rdisc_ra_timeout (NMRDisc *rdisc, gpointer user_data)
+{
+ if (slaac_required) {
+ nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed, quitting...", ifname);
+ g_main_loop_quit (main_loop);
+ } else
+ nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed", ifname);
+}
+
+static gboolean
+quit_handler (gpointer user_data)
+{
+ gboolean *quit_early_ptr = user_data;
+
+ *quit_early_ptr = TRUE;
+ g_main_loop_quit (main_loop);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+setup_signals (gboolean *quit_early_ptr)
+{
+ sigset_t sigmask;
+
+ sigemptyset (&sigmask);
+ pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
+
+ signal (SIGPIPE, SIG_IGN);
+ g_unix_signal_add (SIGINT, quit_handler, quit_early_ptr);
+ g_unix_signal_add (SIGTERM, quit_handler, quit_early_ptr);
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *opt_log_level = NULL;
+ char *opt_log_domains = NULL;
+ gboolean debug = FALSE, g_fatal_warnings = FALSE, become_daemon = FALSE;
+ gboolean show_version = FALSE, slaac = FALSE;
+ char *bad_domains = NULL, *dhcp4_hostname = NULL, *uuid = NULL;
+ char *iid_str = NULL, *dhcp4_clientid = NULL, *dhcp4_address = NULL;
+ gs_unref_object NMDhcpManager *dhcp_mgr = NULL;
+ GError *error = NULL;
+ gboolean wrote_pidfile = FALSE;
+ gs_free char *pidfile = NULL;
+ gboolean quit_early = FALSE;
+ gs_unref_object NMDhcpClient *dhcp4_client = NULL;
+ gs_unref_object NMRDisc *rdisc = NULL;
+ GByteArray *hwaddr = NULL;
+ size_t hwaddr_len = 0;
+ gconstpointer tmp;
+ gs_free NMUtilsIPv6IfaceId *iid = NULL;
+
+ GOptionEntry options[] = {
+ /* Interface/IP config */
+ { "ifname", 'i', 0, G_OPTION_ARG_STRING, &ifname, N_("The interface to manage"), N_("eth0") },
+ { "uuid", 'u', 0, G_OPTION_ARG_STRING, &uuid, N_("Connection UUID"), N_("661e8cd0-b618-46b8-9dc9-31a52baaa16b") },
+ { "slaac", 's', 0, G_OPTION_ARG_NONE, &slaac, N_("Whether to manage IPv6 SLAAC"), NULL },
+ { "slaac-required", '6', 0, G_OPTION_ARG_NONE, &slaac_required, N_("Whether SLAAC must be successful"), NULL },
+ { "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &tempaddr, N_("Use an IPv6 temporary privacy address"), NULL },
+ { "dhcp4", 'd', 0, G_OPTION_ARG_STRING, &dhcp4_address, N_("Current DHCPv4 address"), NULL },
+ { "dhcp4-required", '4', 0, G_OPTION_ARG_NONE, &dhcp4_required, N_("Whether DHCPv4 must be successful"), NULL },
+ { "dhcp4-clientid", 'c', 0, G_OPTION_ARG_STRING, &dhcp4_clientid, N_("Hex-encoded DHCPv4 client ID"), NULL },
+ { "dhcp4-hostname", 'h', 0, G_OPTION_ARG_STRING, &dhcp4_hostname, N_("Hostname to send to DHCP server"), N_("barbar") },
+ { "priority", 'p', 0, G_OPTION_ARG_INT, &priority, N_("Route priority"), N_("10") },
+ { "iid", 'e', 0, G_OPTION_ARG_STRING, &iid_str, N_("Hex-encoded Interface Identifier"), N_("") },
+
+ /* Logging/debugging */
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
+ { "no-daemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &become_daemon, N_("Don't become a daemon"), NULL },
+ { "debug", 'b', 0, G_OPTION_ARG_NONE, &debug, N_("Don't become a daemon, and log to stderr"), NULL },
+ { "log-level", 0, 0, G_OPTION_ARG_STRING, &opt_log_level, N_("Log level: one of [%s]"), "INFO" },
+ { "log-domains", 0, 0, G_OPTION_ARG_STRING, &opt_log_domains,
+ N_("Log domains separated by ',': any combination of [%s]"),
+ "PLATFORM,RFKILL,WIFI" },
+ { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, N_("Make all warnings fatal"), NULL },
+ {NULL}
+ };
+
+ setpgid (getpid (), getpid ());
+
+ if (!nm_main_utils_early_setup ("nm-iface-helper",
+ &argv,
+ &argc,
+ options,
+ NULL,
+ _("nm-iface-helper is a small, standalone process that manages a single network interface.")))
+ exit (1);
+
+ if (show_version) {
+ fprintf (stdout, NM_DIST_VERSION "\n");
+ exit (0);
+ }
+
+ if (!ifname || !uuid) {
+ fprintf (stderr, _("An interface name and UUID are required\n"));
+ exit (1);
+ }
+
+ if (!nm_logging_setup (opt_log_level,
+ opt_log_domains,
+ &bad_domains,
+ &error)) {
+ fprintf (stderr,
+ _("%s. Please use --help to see a list of valid options.\n"),
+ error->message);
+ exit (1);
+ } else if (bad_domains) {
+ fprintf (stderr,
+ _("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
+ bad_domains);
+ g_clear_pointer (&bad_domains, g_free);
+ }
+
+ pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, ifindex);
+ g_assert (pidfile);
+
+ /* check pid file */
+ if (nm_main_utils_check_pidfile (pidfile, "nm-iface-helper"))
+ exit (1);
+
+ if (become_daemon && !debug) {
+ if (daemon (0, 0) < 0) {
+ int saved_errno;
+
+ saved_errno = errno;
+ fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
+ g_strerror (saved_errno),
+ saved_errno);
+ exit (1);
+ }
+ if (nm_main_utils_write_pidfile (pidfile))
+ wrote_pidfile = TRUE;
+ }
+
+ /* Set up unix signal handling - before creating threads, but after daemonizing! */
+ main_loop = g_main_loop_new (NULL, FALSE);
+ setup_signals (&quit_early);
+
+ if (g_fatal_warnings) {
+ GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);
+ }
+
+ nm_logging_syslog_openlog (debug);
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ nm_log_info (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
+
+ /* Set up platform interaction layer */
+ nm_linux_platform_setup ();
+
+ ifindex = nm_platform_link_get_ifindex (ifname);
+ if (ifindex <= 0) {
+ fprintf (stderr, _("Failed to find interface index for %s\n"), ifname);
+ exit (1);
+ }
+
+ tmp = nm_platform_link_get_address (ifindex, &hwaddr_len);
+ if (tmp) {
+ hwaddr = g_byte_array_sized_new (hwaddr_len);
+ g_byte_array_append (hwaddr, tmp, hwaddr_len);
+ }
+
+ if (iid_str) {
+ GBytes *bytes;
+ gsize ignored = 0;
+
+ bytes = nm_utils_hexstr2bin (iid_str);
+ if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) {
+ fprintf (stderr, _("(%s): Invalid IID %s\n"), ifname, iid_str);
+ exit (1);
+ }
+ iid = g_bytes_unref_to_data (bytes, &ignored);
+ }
+
+ priority = MAX (0, priority);
+
+ if (dhcp4_address) {
+ nm_platform_sysctl_set (nm_utils_ip4_property_path (ifname, "promote_secondaries"), "1");
+
+ /* Initialize DHCP manager */
+ dhcp_mgr = nm_dhcp_manager_get ();
+ g_assert (dhcp_mgr != NULL);
+
+ dhcp4_client = nm_dhcp_manager_start_ip4 (dhcp_mgr,
+ ifname,
+ ifindex,
+ hwaddr,
+ uuid,
+ priority,
+ !!dhcp4_hostname,
+ dhcp4_hostname,
+ dhcp4_clientid,
+ 45,
+ NULL,
+ dhcp4_address);
+ g_assert (dhcp4_client);
+ g_signal_connect (dhcp4_client,
+ NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
+ G_CALLBACK (dhcp4_state_changed),
+ NULL);
+ }
+
+ if (slaac) {
+ nm_platform_link_set_user_ipv6ll_enabled (ifindex, TRUE);
+
+ rdisc = nm_lndp_rdisc_new (ifindex, ifname);
+ g_assert (rdisc);
+
+ if (iid)
+ nm_rdisc_set_iid (rdisc, *iid);
+
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra"), "1");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_defrtr"), "0");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_pinfo"), "0");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_rtr_pref"), "0");
+
+ g_signal_connect (rdisc,
+ NM_RDISC_CONFIG_CHANGED,
+ G_CALLBACK (rdisc_config_changed),
+ NULL);
+ g_signal_connect (rdisc,
+ NM_RDISC_RA_TIMEOUT,
+ G_CALLBACK (rdisc_ra_timeout),
+ NULL);
+ nm_rdisc_start (rdisc);
+ }
+
+ if (!quit_early)
+ g_main_loop_run (main_loop);
+
+ g_clear_pointer (&hwaddr, g_byte_array_unref);
+
+ nm_logging_syslog_closelog ();
+
+ if (pidfile && wrote_pidfile)
+ unlink (pidfile);
+
+ nm_log_info (LOGD_CORE, "exiting");
+ exit (0);
+}
+
+/*******************************************************/
+/* Stub functions */
+
+gconstpointer nm_config_get (void);
+const char *nm_config_get_dhcp_client (gpointer unused);
+gboolean nm_config_get_configure_and_quit (gpointer unused);
+gconstpointer nm_dbus_manager_get (void);
+void nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2);
+void nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object);
+
+gconstpointer
+nm_config_get (void)
+{
+ return GUINT_TO_POINTER (1);
+}
+
+const char *
+nm_config_get_dhcp_client (gpointer unused)
+{
+ return "internal";
+}
+
+gboolean
+nm_config_get_configure_and_quit (gpointer unused)
+{
+ return TRUE;
+}
+
+gconstpointer
+nm_dbus_manager_get (void)
+{
+ return GUINT_TO_POINTER (1);
+}
+
+void
+nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2)
+{
+}
+
+void
+nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object)
+{
+}
+
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 72c1b0cd75..7548daded0 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -83,6 +83,7 @@ nm_ip4_config_new (void)
}
+#ifndef NM_IFACE_HELPER
void
nm_ip4_config_export (NMIP4Config *config)
{
@@ -94,6 +95,7 @@ nm_ip4_config_export (NMIP4Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
+#endif
const char *
nm_ip4_config_get_dbus_path (const NMIP4Config *config)
@@ -1952,7 +1954,9 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip4_config_object_info);
+#endif
}
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index fe45f824d8..f0930b7453 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -73,6 +73,7 @@ nm_ip6_config_new (void)
return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL);
}
+#ifndef NM_IFACE_HELPER
void
nm_ip6_config_export (NMIP6Config *config)
{
@@ -84,6 +85,7 @@ nm_ip6_config_export (NMIP6Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
+#endif
const char *
nm_ip6_config_get_dbus_path (const NMIP6Config *config)
@@ -1863,7 +1865,9 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip6_config_object_info);
+#endif
}
diff --git a/src/nm-manager.c b/src/nm-manager.c
index f936235c78..d8973b7337 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -59,6 +59,7 @@
#include "nm-session-monitor.h"
#include "nm-activation-request.h"
#include "nm-core-internal.h"
+#include "nm-config.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
@@ -214,6 +215,7 @@ enum {
USER_PERMISSIONS_CHANGED,
ACTIVE_CONNECTION_ADDED,
ACTIVE_CONNECTION_REMOVED,
+ CONFIGURE_QUIT,
LAST_SIGNAL
};
@@ -706,6 +708,9 @@ check_if_startup_complete (NMManager *self)
g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
}
+
+ if (nm_config_get_configure_and_quit (nm_config_get ()))
+ g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
}
static void
@@ -745,6 +750,8 @@ remove_device (NMManager *manager,
nm_device_set_unmanaged_quitting (device);
else
nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
+ } else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
+ nm_device_spawn_iface_helper (device);
}
}
@@ -4167,6 +4174,16 @@ nm_manager_start (NMManager *self)
check_if_startup_complete (self);
}
+void
+nm_manager_stop (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ /* Remove all devices */
+ while (priv->devices)
+ remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
+}
+
static gboolean
handle_firmware_changed (gpointer user_data)
{
@@ -4990,9 +5007,7 @@ dispose (GObject *object)
G_CALLBACK (authority_changed_cb),
manager);
- /* Remove all devices */
- while (priv->devices)
- remove_device (manager, NM_DEVICE (priv->devices->data), TRUE, TRUE);
+ g_assert (priv->devices == NULL);
if (priv->ac_cleanup_id) {
g_source_remove (priv->ac_cleanup_id);
@@ -5258,6 +5273,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ signals[CONFIGURE_QUIT] =
+ g_signal_new (NM_MANAGER_CONFIGURE_QUIT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (manager_class),
&dbus_glib_nm_manager_object_info);
diff --git a/src/nm-manager.h b/src/nm-manager.h
index f623516159..3b00e8053a 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -59,6 +59,7 @@
/* Internal signals */
#define NM_MANAGER_ACTIVE_CONNECTION_ADDED "active-connection-added"
#define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed"
+#define NM_MANAGER_CONFIGURE_QUIT "configure-quit"
struct _NMManager {
@@ -88,6 +89,7 @@ NMManager * nm_manager_new (NMSettings *settings,
NMManager * nm_manager_get (void);
void nm_manager_start (NMManager *manager);
+void nm_manager_stop (NMManager *manager);
NMState nm_manager_get_state (NMManager *manager);
const GSList *nm_manager_get_active_connections (NMManager *manager);
GSList * nm_manager_get_activatable_connections (NMManager *manager);