core: add monitor-connection-files=false and ReloadConnections
authorDan Winship <danw@gnome.org>
Thu, 23 May 2013 22:05:40 +0000 (19:05 -0300)
committerDan Winship <danw@gnome.org>
Fri, 14 Jun 2013 15:57:47 +0000 (12:57 -0300)
Add a "monitor-connection-files" config option, which can be set to
"false" to disable automatic reloading of connections on file change.

To go with this, add a new ReloadConnections method on
o.fd.NM.Settings that can be used to manually reload connections, and
add an nm-cli command to call it.

21 files changed:
cli/src/connections.c
data/server.conf.in
introspection/nm-settings-connection.xml
introspection/nm-settings.xml
libnm-glib/libnm-glib.ver
libnm-glib/nm-remote-settings.c
libnm-glib/nm-remote-settings.h
man/NetworkManager.conf.xml
man/nmcli.1.in
src/config/nm-config.c
src/config/nm-config.h
src/settings/nm-settings.c
src/settings/nm-system-config-interface.c
src/settings/nm-system-config-interface.h
src/settings/plugins/example/plugin.c
src/settings/plugins/ifcfg-rh/Makefile.am
src/settings/plugins/ifcfg-rh/plugin.c
src/settings/plugins/ifcfg-rh/reader.c
src/settings/plugins/ifcfg-rh/reader.h
src/settings/plugins/ifnet/plugin.c
src/settings/plugins/keyfile/plugin.c

index 9db9e7a..63518f8 100644 (file)
@@ -205,7 +205,8 @@ usage (void)
 #endif
                 "  down [ id | uuid | path | apath ] <ID>\n\n"
                 "  add COMMON_OPTIONS TYPE_SPECIFIC_OPTIONS IP_OPTIONS\n\n"
-                "  delete [ id | uuid | path ] <ID>\n\n\n"
+                "  delete [ id | uuid | path ] <ID>\n\n"
+                "  reload\n\n\n"
                 ));
 }
 
@@ -284,6 +285,7 @@ static const char *real_con_commands[] = {
        "down",
        "add",
        "delete",
+       "reload",
        NULL
 };
 
@@ -3221,6 +3223,32 @@ finish:
        return nmc->return_value;
 }
 
+static NMCResultCode
+do_connection_reload (NmCli *nmc, int argc, char **argv)
+{
+       GError *error = NULL;
+
+       nmc->return_value = NMC_RESULT_SUCCESS;
+       nmc->should_wait = FALSE;
+
+       if (!nm_client_get_manager_running (nmc->client)) {
+               g_string_printf (nmc->return_text, _("Error: NetworkManager is not running."));
+               nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
+               return nmc->return_value;
+       }
+
+       if (!nm_remote_settings_reload_connections (nmc->system_settings, &error)) {
+               g_string_printf (nmc->return_text, _("Error: %s."), error->message);
+               if (error->code == NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE)
+                       nmc->return_value = NMC_RESULT_ERROR_NM_NOT_RUNNING;
+               else
+                       nmc->return_value = NMC_RESULT_ERROR_UNKNOWN;
+               g_clear_error (&error);
+       }
+
+       return nmc->return_value;
+}
+
 static NMCResultCode
 parse_cmd (NmCli *nmc, int argc, char **argv)
 {
@@ -3263,6 +3291,9 @@ parse_cmd (NmCli *nmc, int argc, char **argv)
                else if (matches(*argv, "delete") == 0) {
                        nmc->return_value = do_connection_delete (nmc, argc-1, argv+1);
                }
+               else if (matches(*argv, "reload") == 0) {
+                       nmc->return_value = do_connection_reload (nmc, argc-1, argv+1);
+               }
                else if (nmc_arg_is_help (*argv)) {
                        usage ();
                        nmc->should_wait = FALSE;
index 7576363..031fbeb 100644 (file)
@@ -4,6 +4,15 @@
 
 [main]
 
+# Normally, NetworkManager reloads connection files on disk any time
+# they are changed. Setting "monitor-connection-files=false" will
+# disable this behavior, and NetworkManager will then only read
+# connection files at startup, and when explicitly requested via
+# D-Bus.
+
+#monitor-connection-files=false
+
+
 # Normally, if there is an ethernet device that is not matched by any
 # existing configured connection, NetworkManager will create a
 # "default" connection for that device, using automatic (DHCP/SLAAC)
index b1f5879..0937380 100644 (file)
             the connection to disk.  Secrets may be part of the update request
             and may sent to a Secret Agent for storage, depending on the the
             flags associated with each secret.
+
+            Use the 'Save' method to save these changes to disk. Note
+            that unsaved changes will be lost if the connection is
+            reloaded from disk (either automatically on file change or
+            due to an explicit ReloadConnections call).
           </tp:docstring>
           <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_connection_update_unsaved"/>
           <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
index 794e302..0043ec7 100644 (file)
         operation does not start the network connection unless (1) device is
         idle and able to connect to the network described by the new connection,
         and (2) the connection is allowed to be started automatically.
+
+        Use the 'Save' method on the connection to save these changes
+        to disk. Note that unsaved changes will be lost if the
+        connection is reloaded from disk (either automatically on file
+        change or due to an explicit ReloadConnections call).
       </tp:docstring>
       <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_add_connection_unsaved"/>
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       </arg>
     </method>
 
+    <method name="ReloadConnections">
+      <tp:docstring>
+        Tells NetworkManager to reload all connection files from disk,
+        including noticing any added or deleted connection files. By
+        default, connections are re-read automatically any time they
+        change, so you only need to use this command if you have set
+        "monitor-connection-files=false" in NetworkManager.conf.
+      </tp:docstring>
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_settings_reload_connections"/>
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="status" type="b" direction="out">
+        <tp:docstring>
+          Success or failure.
+        </tp:docstring>
+      </arg>
+    </method>
+
     <method name="SaveHostname">
       <tp:docstring>
         Save the hostname to persistent configuration.
index 4b5fc90..668fbac 100644 (file)
@@ -231,6 +231,7 @@ global:
        nm_remote_settings_new;
        nm_remote_settings_new_async;
        nm_remote_settings_new_finish;
+       nm_remote_settings_reload_connections;
        nm_remote_settings_save_hostname;
        nm_secret_agent_delete_secrets;
        nm_secret_agent_error_get_type;
index 9dc74ef..589d755 100644 (file)
@@ -629,6 +629,47 @@ nm_remote_settings_add_connection_unsaved (NMRemoteSettings *settings,
        return TRUE;
 }
 
+/**
+ * nm_remote_settings_reload_connections:
+ * @settings: the #NMRemoteSettings
+ * @error: return location for #GError
+ *
+ * Requests that the remote settings service reload all connection
+ * files from disk, adding, updating, and removing connections until
+ * the in-memory state matches the on-disk state.
+ *
+ * Return value: %TRUE on success, %FALSE on failure
+ *
+ * Since: 0.9.10
+ **/
+gboolean
+nm_remote_settings_reload_connections (NMRemoteSettings *settings,
+                                       GError **error)
+{
+       NMRemoteSettingsPrivate *priv;
+       gboolean success;
+
+       g_return_val_if_fail (NM_IS_REMOTE_SETTINGS (settings), FALSE);
+
+       priv = NM_REMOTE_SETTINGS_GET_PRIVATE (settings);
+
+       _nm_remote_settings_ensure_inited (settings);
+
+       if (!priv->service_running) {
+               g_set_error_literal (error, NM_REMOTE_SETTINGS_ERROR,
+                                    NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE,
+                                    "NetworkManager is not running.");
+               return FALSE;
+       }
+
+       if (!dbus_g_proxy_call (priv->proxy, "ReloadConnections", error,
+                               G_TYPE_INVALID,
+                               G_TYPE_BOOLEAN, &success,
+                               G_TYPE_INVALID))
+               return FALSE;
+       return success;
+}
+
 static void
 clear_one_hash (GHashTable *table)
 {
index c8d1b3e..1bef4a7 100644 (file)
@@ -45,6 +45,8 @@ G_BEGIN_DECLS
  *   was removed before it was completely initialized
  * @NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE: the #NMRemoteConnection object
  *   is not visible or otherwise unreadable
+ * @NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE: NetworkManager is not running.
+ *   (Since 0.9.10)
  *
  * Describes errors that may result from operations involving a #NMRemoteSettings.
  *
@@ -53,6 +55,7 @@ typedef enum {
        NM_REMOTE_SETTINGS_ERROR_UNKNOWN = 0,            /*< nick=UnknownError >*/
        NM_REMOTE_SETTINGS_ERROR_CONNECTION_REMOVED,     /*< nick=ConnectionRemoved >*/
        NM_REMOTE_SETTINGS_ERROR_CONNECTION_UNAVAILABLE, /*< nick=ConnectionUnavailable >*/
+       NM_REMOTE_SETTINGS_ERROR_SERVICE_UNAVAILABLE,    /*< nick=ServiceUnavailable >*/
 } NMRemoteSettingsError;
 
 #define NM_REMOTE_SETTINGS_ERROR nm_remote_settings_error_quark ()
@@ -132,6 +135,9 @@ gboolean nm_remote_settings_add_connection_unsaved (NMRemoteSettings *settings,
                                                     NMRemoteSettingsAddConnectionFunc callback,
                                                     gpointer user_data);
 
+gboolean nm_remote_settings_reload_connections (NMRemoteSettings *settings,
+                                                GError **error);
+
 gboolean nm_remote_settings_save_hostname (NMRemoteSettings *settings,
                                            const char *hostname,
                                            NMRemoteSettingsSaveHostnameFunc callback,
index 6b73c82..7ee5dd9 100644 (file)
@@ -96,6 +96,16 @@ Copyright (C) 2010 - 2013 Red Hat, Inc.
        connection, the error is returned to the user.  See below
        for available plugins.</para></listitem>
       </varlistentry>
+      <varlistentry>
+       <term><varname>monitor-connection-files</varname></term>
+       <listitem><para>Whether the configured settings plugin(s)
+       should set up file monitors and immediately pick up changes
+       made to connection files while NetworkManager is running. This
+       is enabled by default; if this key is set to
+       '<literal>false</literal>', then NetworkManager will only read
+       the connection files at startup, and when explicitly requested
+       via the ReloadConnections D-Bus call.</para></listitem>
+      </varlistentry>
       <varlistentry>
        <term><varname>dhcp</varname></term>
        <listitem><para>This key sets up what DHCP client
index 77675d3..754f80c 100644 (file)
@@ -238,7 +238,7 @@ NetworkManager connections
 .br
 Get information about \fINetworkManager\fP connections and manage them.
 .TP
-.SS \fICOMMAND\fP := { show | up | down | add | delete }
+.SS \fICOMMAND\fP := { show | up | down | add | delete  | reload }
 .sp
 .RS
 .TP
@@ -501,6 +501,13 @@ its name, UUID or D-Bus path. If <ID> is ambiguous, a keyword \fIid\fP,
 \fIuuid\fP or \fIpath\fP can be used.
 .br
 See \fBshow active\fP above for the description of the <ID>-specifying keywords.
+.TP
+.B reload
+.br
+Reload all connection files from disk. By default, connections are re-read
+automatically any time they change, so you only need to use this command when
+the auto-loading feature is disabled ("monitor-connection-files=false"
+in NetworkManager.conf).
 .RE
 
 .TP
index 5a7e2ec..8eb40ef 100644 (file)
@@ -43,6 +43,7 @@ typedef struct {
        GKeyFile *keyfile;
 
        char **plugins;
+       gboolean monitor_connection_files;
        char *dhcp_client;
        char *dns_mode;
 
@@ -81,6 +82,14 @@ nm_config_get_plugins (NMConfig *config)
        return (const char **) NM_CONFIG_GET_PRIVATE (config)->plugins;
 }
 
+gboolean
+nm_config_get_monitor_connection_files (NMConfig *config)
+{
+       g_return_val_if_fail (config != NULL, FALSE);
+
+       return NM_CONFIG_GET_PRIVATE (config)->monitor_connection_files;
+}
+
 const char *
 nm_config_get_dhcp_client (NMConfig *config)
 {
@@ -434,6 +443,7 @@ nm_config_new (GError **error)
        GFileInfo *info;
        GPtrArray *confs;
        const char *name;
+       char *value;
        int i;
 
        g_assert (!singleton);
@@ -492,6 +502,20 @@ nm_config_new (GError **error)
                g_key_file_set_value (priv->keyfile, "main", "plugins", cli_plugins);
        priv->plugins = g_key_file_get_string_list (priv->keyfile, "main", "plugins", NULL, NULL);
 
+       value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
+       if (value) {
+               if (!strcmp (value, "true") || !strcmp (value, "yes") || !strcmp (value, "on"))
+                       priv->monitor_connection_files = TRUE;
+               else if (!strcmp (value, "false") || !strcmp (value, "no") || !strcmp (value, "off"))
+                       priv->monitor_connection_files = FALSE;
+               else {
+                       g_warning ("Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
+                       priv->monitor_connection_files = FALSE;
+               }
+               g_free (value);
+       } else
+               priv->monitor_connection_files = TRUE;
+
        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);
 
index 82cf8c5..6518fb3 100644 (file)
@@ -50,6 +50,7 @@ NMConfig *nm_config_get (void);
 
 const char *nm_config_get_path (NMConfig *config);
 const char **nm_config_get_plugins (NMConfig *config);
+gboolean nm_config_get_monitor_connection_files (NMConfig *config);
 const char *nm_config_get_dhcp_client (NMConfig *config);
 const char *nm_config_get_dns_mode (NMConfig *config);
 const char *nm_config_get_log_level (NMConfig *config);
index 578ca45..7950a8c 100644 (file)
@@ -104,6 +104,9 @@ static void impl_settings_add_connection_unsaved (NMSettings *self,
                                                   GHashTable *settings,
                                                   DBusGMethodInvocation *context);
 
+static void impl_settings_reload_connections (NMSettings *self,
+                                              DBusGMethodInvocation *context);
+
 static void impl_settings_save_hostname (NMSettings *self,
                                          const char *hostname,
                                          DBusGMethodInvocation *context);
@@ -1213,6 +1216,46 @@ impl_settings_add_connection_unsaved (NMSettings *self,
        impl_settings_add_connection_helper (self, settings, FALSE, context);
 }
 
+static void
+impl_settings_reload_connections (NMSettings *self,
+                                  DBusGMethodInvocation *context)
+{
+       NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+       GSList *iter;
+       gulong caller_uid;
+       GError *error = NULL;
+
+       if (!nm_dbus_manager_get_caller_info (priv->dbus_mgr, context, NULL, &caller_uid)) {
+               error = g_error_new_literal (NM_SETTINGS_ERROR,
+                                            NM_SETTINGS_ERROR_PERMISSION_DENIED,
+                                            "Unable to determine request UID.");
+               dbus_g_method_return_error (context, error);
+               g_error_free (error);
+               return;
+       }
+       if (caller_uid != 0) {
+               nm_log_warn (LOGD_SETTINGS, "ReloadConnections: permission denied to %lu", caller_uid);
+               error = g_error_new_literal (NM_SETTINGS_ERROR,
+                                            NM_SETTINGS_ERROR_PERMISSION_DENIED,
+                                            "Permission denied");
+               dbus_g_method_return_error (context, error);
+               g_error_free (error);
+               return;
+       }
+
+       if (!priv->connections_loaded) {
+               load_connections (self);
+       } else {
+               for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+                       NMSystemConfigInterface *plugin = NM_SYSTEM_CONFIG_INTERFACE (iter->data);
+
+                       nm_system_config_interface_reload_connections (plugin);
+               }
+       }
+
+       dbus_g_method_return (context, TRUE);
+}
+
 static void
 pk_hostname_cb (NMAuthChain *chain,
                 GError *chain_error,
index 1a9d5d5..0d9426d 100644 (file)
@@ -137,6 +137,15 @@ nm_system_config_interface_get_connections (NMSystemConfigInterface *config)
        return NULL;
 }
 
+void
+nm_system_config_interface_reload_connections (NMSystemConfigInterface *config)
+{
+       g_return_if_fail (config != NULL);
+
+       if (NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->reload_connections)
+               NM_SYSTEM_CONFIG_INTERFACE_GET_INTERFACE (config)->reload_connections (config);
+}
+
 GSList *
 nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config)
 {
index 9648fdb..cd65a6a 100644 (file)
@@ -89,6 +89,11 @@ struct _NMSystemConfigInterface {
         */
        GSList * (*get_connections) (NMSystemConfigInterface *config);
 
+       /* Requests that the plugin reload all connection files from disk,
+        * and emit signals reflecting new, changed, and removed connections.
+        */
+       void (*reload_connections) (NMSystemConfigInterface *config);
+
        /*
         * Return a string list of specifications of devices which NetworkManager
         * should not manage.  Returned list will be freed by the system settings
@@ -137,6 +142,8 @@ void nm_system_config_interface_init (NMSystemConfigInterface *config,
 
 GSList *nm_system_config_interface_get_connections (NMSystemConfigInterface *config);
 
+void nm_system_config_interface_reload_connections (NMSystemConfigInterface *config);
+
 GSList *nm_system_config_interface_get_unmanaged_specs (NMSystemConfigInterface *config);
 
 NMSettingsConnection *nm_system_config_interface_add_connection (NMSystemConfigInterface *config,
index c8c1bff..710ac75 100644 (file)
@@ -421,10 +421,9 @@ conf_file_changed (GFileMonitor *monitor,
 }
 
 /* This function starts the inotify monitors that watch the plugin's config
- * file directory for new connections and changes to existing connections.
- * At this time all plugins are expected to make NM aware of changes on-the-fly
- * instead of requiring a SIGHUP or SIGUSR1 or some D-Bus method to say
- * "reload".
+ * file directory for new connections and changes to existing connections
+ * (if not disabled by NetworkManager.conf), and for changes to the plugin's
+ * non-connection config files.
  */
 static void
 setup_monitoring (NMSystemConfigInterface *config)
@@ -438,16 +437,18 @@ setup_monitoring (NMSystemConfigInterface *config)
         */
        priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
 
-       /* Set up the watch for our config directory */
-       file = g_file_new_for_path (EXAMPLE_DIR);
-       monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
-       g_object_unref (file);
-       if (monitor) {
-               /* This registers the dir_changed() function to be called whenever
-                * the GFileMonitor object notices a change in the directory.
-                */
-               priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config);
-               priv->monitor = monitor;
+       if (nm_config_get_monitor_connection_files (nm_config_get ())) {
+               /* Set up the watch for our config directory */
+               file = g_file_new_for_path (EXAMPLE_DIR);
+               monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+               g_object_unref (file);
+               if (monitor) {
+                       /* This registers the dir_changed() function to be called whenever
+                        * the GFileMonitor object notices a change in the directory.
+                        */
+                       priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config);
+                       priv->monitor = monitor;
+               }
        }
 
        /* Set up a watch on our configuration file, basically just for watching
index 829378d..49b115c 100644 (file)
@@ -28,6 +28,7 @@ INCLUDES = \
        -I$(top_srcdir)/src/wifi \
        -I$(top_srcdir)/src/settings \
        -I$(top_srcdir)/src/posix-signals \
+       -I$(top_srcdir)/src/config \
        -I$(top_srcdir)/include \
        -I$(top_builddir)/include \
        -I$(top_srcdir)/libnm-glib \
index fac70c4..034737c 100644 (file)
 #include "plugin.h"
 #include "nm-system-config-interface.h"
 #include "nm-settings-error.h"
+#include "nm-config.h"
 
 #include "nm-ifcfg-connection.h"
 #include "nm-inotify-helper.h"
 #include "shvar.h"
+#include "reader.h"
 #include "writer.h"
 #include "utils.h"
 
@@ -64,7 +66,8 @@ static gboolean impl_ifcfgrh_get_ifcfg_details (SCPluginIfcfg *plugin,
 
 static void connection_new_or_changed (SCPluginIfcfg *plugin,
                                        const char *path,
-                                       NMIfcfgConnection *existing);
+                                       NMIfcfgConnection *existing,
+                                       char **out_old_path);
 
 static void system_config_interface_init (NMSystemConfigInterface *system_config_interface_class);
 
@@ -109,7 +112,7 @@ connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data)
        path = nm_ifcfg_connection_get_path (connection);
        g_return_if_fail (path != NULL);
 
-       connection_new_or_changed (plugin, path, connection);
+       connection_new_or_changed (plugin, path, connection, NULL);
 }
 
 static NMIfcfgConnection *
@@ -173,35 +176,6 @@ _internal_new_connection (SCPluginIfcfg *self,
        return connection;
 }
 
-static void
-read_connections (SCPluginIfcfg *plugin)
-{
-       GDir *dir;
-       GError *err = NULL;
-
-       dir = g_dir_open (IFCFG_DIR, 0, &err);
-       if (dir) {
-               const char *item;
-
-               while ((item = g_dir_read_name (dir))) {
-                       char *full_path;
-
-                       if (utils_should_ignore_file (item, TRUE))
-                               continue;
-
-                       full_path = g_build_filename (IFCFG_DIR, item, NULL);
-                       if (utils_get_ifcfg_name (full_path, TRUE))
-                               _internal_new_connection (plugin, full_path, NULL, NULL);
-                       g_free (full_path);
-               }
-
-               g_dir_close (dir);
-       } else {
-               PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Can not read directory '%s': %s", IFCFG_DIR, err->message);
-               g_error_free (err);
-       }
-}
-
 /* Monitoring */
 
 static void
@@ -242,10 +216,26 @@ find_by_path (SCPluginIfcfg *self, const char *path)
        return NULL;
 }
 
+static NMIfcfgConnection *
+find_by_uuid_from_path (SCPluginIfcfg *self, const char *path)
+{
+       SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (self);
+       char *uuid;
+
+       g_return_val_if_fail (path != NULL, NULL);
+
+       uuid = uuid_from_file (path);
+       if (uuid)
+               return g_hash_table_lookup (priv->connections, uuid);
+       else
+               return NULL;
+}
+
 static void
 connection_new_or_changed (SCPluginIfcfg *self,
                            const char *path,
-                           NMIfcfgConnection *existing)
+                           NMIfcfgConnection *existing,
+                           char **out_old_path)
 {
        NMIfcfgConnection *new;
        GError *error = NULL;
@@ -255,6 +245,21 @@ connection_new_or_changed (SCPluginIfcfg *self,
        g_return_if_fail (self != NULL);
        g_return_if_fail (path != NULL);
 
+       if (out_old_path)
+               *out_old_path = NULL;
+
+       if (!existing) {
+               /* See if it's a rename */
+               existing = find_by_uuid_from_path (self, path);
+               if (existing) {
+                       const char *old_path = nm_ifcfg_connection_get_path (existing);
+                       PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "renaming %s -> %s", old_path, path);
+                       if (out_old_path)
+                               *out_old_path = g_strdup (old_path);
+                       nm_ifcfg_connection_set_path (existing, path);
+               }
+       }
+
        if (!existing) {
                /* New connection */
                new = _internal_new_connection (self, path, NULL, NULL);
@@ -371,7 +376,7 @@ ifcfg_dir_changed (GFileMonitor *monitor,
                case G_FILE_MONITOR_EVENT_CREATED:
                case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
                        /* Update or new */
-                       connection_new_or_changed (plugin, ifcfg_path, connection);
+                       connection_new_or_changed (plugin, ifcfg_path, connection, NULL);
                        break;
                default:
                        break;
@@ -399,6 +404,65 @@ setup_ifcfg_monitoring (SCPluginIfcfg *plugin)
        }
 }
 
+static void
+read_connections (SCPluginIfcfg *plugin)
+{
+       SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
+       GDir *dir;
+       GError *err = NULL;
+       const char *item;
+       GHashTable *oldconns;
+       GHashTableIter iter;
+       gpointer key, value;
+       NMIfcfgConnection *connection;
+
+       dir = g_dir_open (IFCFG_DIR, 0, &err);
+       if (!dir) {
+               PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Can not read directory '%s': %s", IFCFG_DIR, err->message);
+               g_error_free (err);
+               return;
+       }
+
+       oldconns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+       g_hash_table_iter_init (&iter, priv->connections);
+       while (g_hash_table_iter_next (&iter, NULL, &value))
+               g_hash_table_insert (oldconns, g_strdup (nm_ifcfg_connection_get_path (value)), value);
+
+       while ((item = g_dir_read_name (dir))) {
+               char *full_path, *old_path;
+
+               if (utils_should_ignore_file (item, TRUE))
+                       continue;
+
+               full_path = g_build_filename (IFCFG_DIR, item, NULL);
+               if (!utils_get_ifcfg_name (full_path, TRUE))
+                       goto next;
+
+               connection = g_hash_table_lookup (oldconns, full_path);
+               g_hash_table_remove (oldconns, full_path);
+               connection_new_or_changed (plugin, full_path, connection, &old_path);
+
+               if (old_path) {
+                       g_hash_table_remove (oldconns, old_path);
+                       g_free (old_path);
+               }
+
+       next:
+               g_free (full_path);
+       }
+
+       g_dir_close (dir);
+
+       g_hash_table_iter_init (&iter, oldconns);
+       while (g_hash_table_iter_next (&iter, &key, &value)) {
+               PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", (char *)key);
+               g_hash_table_iter_remove (&iter);
+               remove_connection (plugin, value);
+       }
+
+       g_hash_table_destroy (oldconns);
+}
+
 static GSList *
 get_connections (NMSystemConfigInterface *config)
 {
@@ -409,7 +473,8 @@ get_connections (NMSystemConfigInterface *config)
        NMIfcfgConnection *connection;
 
        if (!priv->initialized) {
-               setup_ifcfg_monitoring (plugin);
+               if (nm_config_get_monitor_connection_files (nm_config_get ()))
+                       setup_ifcfg_monitoring (plugin);
                read_connections (plugin);
                priv->initialized = TRUE;
        }
@@ -423,6 +488,14 @@ get_connections (NMSystemConfigInterface *config)
        return list;
 }
 
+static void
+reload_connections (NMSystemConfigInterface *config)
+{
+       SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config);
+
+       read_connections (plugin);
+}
+
 static GSList *
 get_unmanaged_specs (NMSystemConfigInterface *config)
 {
@@ -854,6 +927,7 @@ system_config_interface_init (NMSystemConfigInterface *system_config_interface_c
        /* interface implementation */
        system_config_interface_class->get_connections = get_connections;
        system_config_interface_class->add_connection = add_connection;
+       system_config_interface_class->reload_connections = reload_connections;
        system_config_interface_class->get_unmanaged_specs = get_unmanaged_specs;
        system_config_interface_class->init = init;
 }
index 34769f3..e99cbe6 100644 (file)
@@ -4251,6 +4251,34 @@ ensure_unmanaged (shvarFile *ifcfg,
        PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    warning: NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
 }
 
+char *
+uuid_from_file (const char *filename)
+{
+       const char *ifcfg_name = NULL;
+       shvarFile *ifcfg;
+       char *uuid;
+
+       g_return_val_if_fail (filename != NULL, NULL);
+
+       ifcfg_name = utils_get_ifcfg_name (filename, TRUE);
+       if (!ifcfg_name)
+               return NULL;
+
+       ifcfg = svNewFile (filename);
+       if (!ifcfg)
+               return NULL;
+
+       /* Try for a UUID key before falling back to hashing the file name */
+       uuid = svGetValue (ifcfg, "UUID", FALSE);
+       if (!uuid || !strlen (uuid)) {
+               g_free (uuid);
+               uuid = nm_utils_uuid_generate_from_string (ifcfg->fileName);
+       }
+
+       svCloseFile (ifcfg);
+       return uuid;
+}
+
 NMConnection *
 connection_from_file (const char *filename,
                       const char *network_file,  /* for unit tests only */
index 97c727c..5121bb8 100644 (file)
@@ -37,4 +37,6 @@ NMConnection *connection_from_file (const char *filename,
                                     GError **error,
                                     gboolean *ignore_error);
 
+char *uuid_from_file (const char *filename);
+
 #endif  /* __READER_H__ */
index 8b056a6..1691106 100644 (file)
@@ -66,7 +66,7 @@ typedef struct {
 
 static void system_config_interface_init (NMSystemConfigInterface *class);
 
-static void reload_connections (gpointer config);
+static void reload_connections (NMSystemConfigInterface *config);
 
 G_DEFINE_TYPE_EXTENDED (SCPluginIfnet, sc_plugin_ifnet, G_TYPE_OBJECT, 0,
                         G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE, system_config_interface_init))
@@ -191,12 +191,15 @@ setup_monitors (NMIfnetConnection * connection, gpointer user_data)
 
        priv->hostname_monitor =
            monitor_file_changes (IFNET_SYSTEM_HOSTNAME_FILE,
-                                 update_system_hostname, user_data);
-       priv->net_monitor =
-           monitor_file_changes (CONF_NET_FILE, reload_connections, user_data);
-       priv->wpa_monitor =
-           monitor_file_changes (WPA_SUPPLICANT_CONF, reload_connections,
-                                 user_data);
+                                 update_system_hostname, user_data);
+       if (nm_config_get_monitor_connection_files (nm_config_get ())) {
+               priv->net_monitor =
+                       monitor_file_changes (CONF_NET_FILE, (FileChangedFn) reload_connections,
+                                             user_data);
+               priv->wpa_monitor =
+                       monitor_file_changes (WPA_SUPPLICANT_CONF, (FileChangedFn) reload_connections,
+                                             user_data);
+       }
 }
 
 static void
@@ -220,7 +223,7 @@ cancel_monitors (NMIfnetConnection * connection, gpointer user_data)
 }
 
 static void
-reload_connections (gpointer config)
+reload_connections (NMSystemConfigInterface *config)
 {
        SCPluginIfnet *self = SC_PLUGIN_IFNET (config);
        SCPluginIfnetPrivate *priv = SC_PLUGIN_IFNET_GET_PRIVATE (self);
@@ -460,6 +463,7 @@ system_config_interface_init (NMSystemConfigInterface *class)
        class->get_connections = get_connections;
        class->get_unmanaged_specs = get_unmanaged_specs;
        class->add_connection = add_connection;
+       class->reload_connections = reload_connections;
 }
 
 static void
index 52cb36d..9b54fe7 100644 (file)
@@ -88,48 +88,6 @@ _internal_new_connection (SCPluginKeyfile *self,
        return (NMSettingsConnection *) connection;
 }
 
-static void
-read_connections (NMSystemConfigInterface *config)
-{
-       SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
-       GDir *dir;
-       GError *error = NULL;
-       const char *item;
-
-       dir = g_dir_open (KEYFILE_DIR, 0, &error);
-       if (!dir) {
-               PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "Cannot read directory '%s': (%d) %s",
-                            KEYFILE_DIR,
-                            error ? error->code : -1,
-                            error && error->message ? error->message : "(unknown)");
-               g_clear_error (&error);
-               return;
-       }
-
-       while ((item = g_dir_read_name (dir))) {
-               NMSettingsConnection *connection;
-               char *full_path;
-
-               if (nm_keyfile_plugin_utils_should_ignore_file (item))
-                       continue;
-
-               full_path = g_build_filename (KEYFILE_DIR, item, NULL);
-               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "parsing %s ... ", item);
-
-               connection = _internal_new_connection (self, full_path, NULL, &error);
-               if (connection) {
-                       PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    read connection '%s'",
-                                     nm_connection_get_id (NM_CONNECTION (connection)));
-               } else {
-                       PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
-                                         (error && error->message) ? error->message : "(unknown)");
-               }
-               g_clear_error (&error);
-               g_free (full_path);
-       }
-       g_dir_close (dir);
-}
-
 /* Monitoring */
 
 static void
@@ -137,6 +95,8 @@ remove_connection (SCPluginKeyfile *self, NMKeyfileConnection *connection)
 {
        g_return_if_fail (connection != NULL);
 
+       PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "removed %s.", nm_keyfile_connection_get_path (connection));
+
        /* Removing from the hash table should drop the last reference */
        g_object_ref (connection);
        g_hash_table_remove (SC_PLUGIN_KEYFILE_GET_PRIVATE (self)->connections,
@@ -145,6 +105,40 @@ remove_connection (SCPluginKeyfile *self, NMKeyfileConnection *connection)
        g_object_unref (connection);
 }
 
+static void
+update_connection (SCPluginKeyfile *self,
+                   NMKeyfileConnection *connection,
+                   const char *name)
+{
+       NMKeyfileConnection *tmp;
+       GError *error = NULL;
+
+       tmp = nm_keyfile_connection_new (NULL, name, &error);
+       if (!tmp) {
+               /* Error; remove the connection */
+               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
+                             (error && error->message) ? error->message : "(unknown)");
+               g_clear_error (&error);
+               remove_connection (self, connection);
+               return;
+       }
+
+       if (!nm_connection_compare (NM_CONNECTION (connection),
+                                   NM_CONNECTION (tmp),
+                                   NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+                                         NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
+               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", name);
+               if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection),
+                                                             NM_CONNECTION (tmp),
+                                                             FALSE,  /* don't set Unsaved */
+                                                             &error)) {
+                       /* Shouldn't ever get here as 'new' was verified by the reader already */
+                       g_assert_no_error (error);
+               }
+       }
+       g_object_unref (tmp);
+}
+
 static NMKeyfileConnection *
 find_by_path (SCPluginKeyfile *self, const char *path)
 {
@@ -162,6 +156,51 @@ find_by_path (SCPluginKeyfile *self, const char *path)
        return NULL;
 }
 
+static void
+new_connection (SCPluginKeyfile *self,
+                const char *name,
+                char **out_old_path)
+{
+       SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
+       NMKeyfileConnection *tmp, *connection;
+       GError *error = NULL;
+
+       if (out_old_path)
+               *out_old_path = NULL;
+
+       tmp = nm_keyfile_connection_new (NULL, name, &error);
+       if (!tmp) {
+               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error in connection %s: %s", name,
+                             (error && error->message) ? error->message : "(unknown)");
+               g_clear_error (&error);
+               return;
+       }
+
+       /* Connection renames will show as different paths but same UUID */
+       connection = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (NM_CONNECTION (tmp)));
+       if (connection) {
+               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "rename %s -> %s",
+                             nm_keyfile_connection_get_path (connection), name);
+               if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection),
+                                                             NM_CONNECTION (tmp),
+                                                             FALSE,  /* don't set Unsaved */
+                                                             &error)) {
+                       /* Shouldn't ever get here as 'tmp' was verified by the reader already */
+                       g_assert_no_error (error);
+               }
+               g_object_unref (tmp);
+               if (out_old_path)
+                       *out_old_path = g_strdup (nm_keyfile_connection_get_path (connection));
+               nm_keyfile_connection_set_path (connection, name);
+       } else {
+               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "new connection %s", name);
+               g_hash_table_insert (priv->connections,
+                                    (gpointer) nm_connection_get_uuid (NM_CONNECTION (tmp)),
+                                    tmp);
+               g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, tmp);
+       }
+}
+
 static void
 dir_changed (GFileMonitor *monitor,
              GFile *file,
@@ -171,10 +210,8 @@ dir_changed (GFileMonitor *monitor,
 {
        NMSystemConfigInterface *config = NM_SYSTEM_CONFIG_INTERFACE (user_data);
        SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
-       SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
+       NMKeyfileConnection *connection;
        char *full_path;
-       NMKeyfileConnection *connection, *tmp;
-       GError *error = NULL;
 
        full_path = g_file_get_path (file);
        if (nm_keyfile_plugin_utils_should_ignore_file (full_path)) {
@@ -186,67 +223,15 @@ dir_changed (GFileMonitor *monitor,
 
        switch (event_type) {
        case G_FILE_MONITOR_EVENT_DELETED:
-               if (connection) {
-                       PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "removed %s.", full_path);
+               if (connection)
                        remove_connection (SC_PLUGIN_KEYFILE (config), connection);
-               }
                break;
        case G_FILE_MONITOR_EVENT_CREATED:
        case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-               if (connection) {
-                       /* Update */
-                       tmp = nm_keyfile_connection_new (NULL, full_path, &error);
-                       if (tmp) {
-                               if (!nm_connection_compare (NM_CONNECTION (connection),
-                                                           NM_CONNECTION (tmp),
-                                                           NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
-                                                             NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
-                                       PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", full_path);
-                                       if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection),
-                                                                                     NM_CONNECTION (tmp),
-                                                                                     FALSE,  /* don't set Unsaved */
-                                                                                     &error)) {
-                                               /* Shouldn't ever get here as 'new' was verified by the reader already */
-                                               g_assert_no_error (error);
-                                       }
-                               }
-                               g_object_unref (tmp);
-                       } else {
-                               /* Error; remove the connection */
-                               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
-                                                     (error && error->message) ? error->message : "(unknown)");
-                               g_clear_error (&error);
-                               remove_connection (SC_PLUGIN_KEYFILE (config), connection);
-                       }
-               } else {
-                       /* New */
-                       PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", full_path);
-                       tmp = nm_keyfile_connection_new (NULL, full_path, &error);
-                       if (tmp) {
-                               /* Connection renames will show as different paths but same UUID */
-                               connection = g_hash_table_lookup (priv->connections, nm_connection_get_uuid (NM_CONNECTION (tmp)));
-                               if (connection) {
-                                       if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection),
-                                                                                     NM_CONNECTION (tmp),
-                                                                                     FALSE,  /* don't set Unsaved */
-                                                                                     &error)) {
-                                               /* Shouldn't ever get here as 'tmp' was verified by the reader already */
-                                               g_assert_no_error (error);
-                                       }
-                                       g_object_unref (tmp);
-                                       nm_keyfile_connection_set_path (connection, full_path);
-                               } else {
-                                       g_hash_table_insert (priv->connections,
-                                                            (gpointer) nm_connection_get_uuid (NM_CONNECTION (tmp)),
-                                                            tmp);
-                                       g_signal_emit_by_name (config, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, tmp);
-                               }
-                       } else {
-                               PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
-                                                     (error && error->message) ? error->message : "(unknown)");
-                               g_clear_error (&error);
-                       }
-               }
+               if (connection)
+                       update_connection (SC_PLUGIN_KEYFILE (config), connection, full_path);
+               else
+                       new_connection (SC_PLUGIN_KEYFILE (config), full_path, NULL);
                break;
        default:
                break;
@@ -299,13 +284,15 @@ setup_monitoring (NMSystemConfigInterface *config)
        GFile *file;
        GFileMonitor *monitor;
 
-       file = g_file_new_for_path (KEYFILE_DIR);
-       monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
-       g_object_unref (file);
+       if (nm_config_get_monitor_connection_files (nm_config_get ())) {
+               file = g_file_new_for_path (KEYFILE_DIR);
+               monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+               g_object_unref (file);
 
-       if (monitor) {
-               priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config);
-               priv->monitor = monitor;
+               if (monitor) {
+                       priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config);
+                       priv->monitor = monitor;
+               }
        }
 
        if (priv->conf_file) {
@@ -320,6 +307,66 @@ setup_monitoring (NMSystemConfigInterface *config)
        }
 }
 
+static void
+read_connections (NMSystemConfigInterface *config)
+{
+       SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
+       SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
+       GDir *dir;
+       GError *error = NULL;
+       const char *item;
+       GHashTable *oldconns;
+       GHashTableIter iter;
+       gpointer data;
+
+       dir = g_dir_open (KEYFILE_DIR, 0, &error);
+       if (!dir) {
+               PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "Cannot read directory '%s': (%d) %s",
+                            KEYFILE_DIR,
+                            error ? error->code : -1,
+                            error && error->message ? error->message : "(unknown)");
+               g_clear_error (&error);
+               return;
+       }
+
+       oldconns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+       g_hash_table_iter_init (&iter, priv->connections);
+       while (g_hash_table_iter_next (&iter, NULL, &data))
+               g_hash_table_insert (oldconns, g_strdup (nm_keyfile_connection_get_path (data)), data);
+
+       while ((item = g_dir_read_name (dir))) {
+               NMKeyfileConnection *connection;
+               char *full_path, *old_path;
+
+               if (nm_keyfile_plugin_utils_should_ignore_file (item))
+                       continue;
+
+               full_path = g_build_filename (KEYFILE_DIR, item, NULL);
+
+               connection = g_hash_table_lookup (oldconns, full_path);
+               if (connection) {
+                       g_hash_table_remove (oldconns, full_path);
+                       update_connection (self, connection, full_path);
+               } else {
+                       new_connection (self, full_path, &old_path);
+                       if (old_path) {
+                               g_hash_table_remove (oldconns, old_path);
+                               g_free (old_path);
+                       }
+               }
+
+               g_free (full_path);
+       }
+       g_dir_close (dir);
+
+       g_hash_table_iter_init (&iter, oldconns);
+       while (g_hash_table_iter_next (&iter, NULL, &data)) {
+               g_hash_table_iter_remove (&iter);
+               remove_connection (self, data);
+       }
+       g_hash_table_destroy (oldconns);
+}
+
 /* Plugin */
 
 static GSList *
@@ -342,6 +389,12 @@ get_connections (NMSystemConfigInterface *config)
        return list;
 }
 
+static void
+reload_connections (NMSystemConfigInterface *config)
+{
+       read_connections (config);
+}
+
 static NMSettingsConnection *
 add_connection (NMSystemConfigInterface *config,
                 NMConnection *connection,
@@ -634,6 +687,7 @@ system_config_interface_init (NMSystemConfigInterface *system_config_interface_c
 {
        /* interface implementation */
        system_config_interface_class->get_connections = get_connections;
+       system_config_interface_class->reload_connections = reload_connections;
        system_config_interface_class->add_connection = add_connection;
        system_config_interface_class->get_unmanaged_specs = get_unmanaged_specs;
 }