diff --git a/src/main-utils.c b/src/main-utils.c index c2ed9d1cbb..bad3141abf 100644 --- a/src/main-utils.c +++ b/src/main-utils.c @@ -34,6 +34,7 @@ #include "main-utils.h" #include "NetworkManagerUtils.h" +#include "nm-config.h" static gboolean sighup_handler (gpointer user_data) @@ -139,11 +140,23 @@ nm_main_utils_ensure_statedir () void nm_main_utils_ensure_rundir () { + int errsv; + /* Setup runtime directory */ if (g_mkdir_with_parents (NMRUNDIR, 0755) != 0) { - fprintf (stderr, _("Cannot create '%s': %s"), NMRUNDIR, strerror (errno)); + errsv = errno; + fprintf (stderr, _("Cannot create '%s': %s"), NMRUNDIR, g_strerror (errsv)); exit (1); } + + nm_assert (g_str_has_prefix (NM_CONFIG_DEVICE_STATE_DIR, NMRUNDIR"/")); + if (g_mkdir (NM_CONFIG_DEVICE_STATE_DIR, 0755) != 0) { + errsv = errno; + if (errsv != EEXIST) { + fprintf (stderr, _("Cannot create '%s': %s"), NM_CONFIG_DEVICE_STATE_DIR, g_strerror (errsv)); + exit (1); + } + } } /** diff --git a/src/main.c b/src/main.c index 490dcfe97c..0dca1afad8 100644 --- a/src/main.c +++ b/src/main.c @@ -412,6 +412,13 @@ main (int argc, char *argv[]) } done: + + /* write the device-state to file. Note that we only persist the + * state here. We don't bother updating the state as devices + * change during regular operation. If NM is killed with SIGKILL, + * it misses to update the state. */ + nm_manager_write_device_state (nm_manager_get ()); + nm_exported_object_class_set_quitting (); nm_manager_stop (nm_manager_get ()); diff --git a/src/nm-config.c b/src/nm-config.c index 4bc4f48340..8536a77fd5 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -418,8 +418,8 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device) g_ptr_array_add (no_auto_default_new, NULL); if (!no_auto_default_to_file (priv->no_auto_default_file, (const char *const*) no_auto_default_new->pdata, &error)) { - nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s", - error->message); + _LOGW ("Could not update no-auto-default.state file: %s", + error->message); g_error_free (error); } @@ -680,7 +680,7 @@ read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, co return FALSE; } - nm_log_dbg (LOGD_SETTINGS, "Reading config file '%s'", path); + _LOGD ("Reading config file '%s'", path); kf = nm_config_create_keyfile (); if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) { @@ -866,8 +866,8 @@ read_base_config (GKeyFile *keyfile, } if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { - nm_log_warn (LOGD_CORE, "Old default config file invalid: %s\n", - my_error->message); + _LOGW ("Old default config file invalid: %s\n", + my_error->message); } g_clear_error (&my_error); @@ -878,8 +878,8 @@ read_base_config (GKeyFile *keyfile, } if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) { - nm_log_warn (LOGD_CORE, "Default config file invalid: %s\n", - my_error->message); + _LOGW ("Default config file invalid: %s\n", + my_error->message); g_propagate_error (error, my_error); return FALSE; } @@ -889,8 +889,8 @@ read_base_config (GKeyFile *keyfile, * config file path. */ *out_config_main_file = g_strdup (DEFAULT_CONFIG_MAIN_FILE); - nm_log_info (LOGD_CORE, "No config file found or given; using %s\n", - DEFAULT_CONFIG_MAIN_FILE); + _LOGI ("No config file found or given; using %s\n", + DEFAULT_CONFIG_MAIN_FILE); return TRUE; } @@ -1303,7 +1303,7 @@ out: if (out_needs_rewrite) *out_needs_rewrite = needs_rewrite; - nm_log_dbg (LOGD_CORE, "intern config file \"%s\"", filename); + _LOGD ("intern config file \"%s\"", filename); if (!has_intern) { g_key_file_unref (keyfile_intern); @@ -1502,7 +1502,7 @@ intern_config_write (const char *filename, success = g_key_file_save_to_file (keyfile, filename, &local); - nm_log_dbg (LOGD_CORE, "write intern config file \"%s\"%s%s", filename, success ? "" : ": ", success ? "" : local->message); + _LOGD ("write intern config file \"%s\"%s%s", filename, success ? "" : ": ", success ? "" : local->message); g_key_file_unref (keyfile); if (!success) g_propagate_error (error, local); @@ -1658,7 +1658,7 @@ nm_config_set_values (NMConfig *self, if (!_nm_keyfile_equals (keyfile_intern_current, keyfile_new, TRUE)) new_data = nm_config_data_new_update_keyfile_intern (priv->config_data, keyfile_new); - nm_log_dbg (LOGD_CORE, "set values(): %s", new_data ? "has changes" : "no changes"); + _LOGD ("set values(): %s", new_data ? "has changes" : "no changes"); if (allow_write && (new_data || force_rewrite)) { @@ -1673,11 +1673,11 @@ nm_config_set_values (NMConfig *self, keyfile_user = _nm_config_data_get_keyfile_user (priv->config_data); if (!intern_config_write (priv->intern_config_file, keyfile_new, keyfile_user, (const char *const*) priv->atomic_section_prefixes, &local)) { - nm_log_warn (LOGD_CORE, "error saving internal configuration \"%s\": %s", priv->intern_config_file, local->message); + _LOGW ("error saving internal configuration \"%s\": %s", priv->intern_config_file, local->message); g_clear_error (&local); } } else - nm_log_dbg (LOGD_CORE, "don't persistate internal configuration (no file set, use --intern-config?)"); + _LOGD ("don't persist internal configuration (no file set, use --intern-config?)"); } if (new_data) _set_config_data (self, new_data, NM_CONFIG_CHANGE_CAUSE_SET_VALUES); @@ -1870,6 +1870,184 @@ _nm_config_state_set (NMConfig *self, /*****************************************************************************/ +#define DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE "device" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED "managed" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID "connection-uuid" + +static NMConfigDeviceStateData * +_config_device_state_data_new (int ifindex, GKeyFile *kf) +{ + NMConfigDeviceStateData *device_state; + NMConfigDeviceStateManagedType managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN; + gs_free char *connection_uuid = NULL; + gsize len_plus_1; + + nm_assert (ifindex > 0); + + if (kf) { + gboolean managed; + + managed = nm_config_keyfile_get_boolean (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, + FALSE); + managed_type = managed + ? NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED + : NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED; + + if (managed) { + connection_uuid = nm_config_keyfile_get_value (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + } + } + + len_plus_1 = connection_uuid ? strlen (connection_uuid) + 1 : 0; + + device_state = g_malloc (sizeof (NMConfigDeviceStateData) + len_plus_1); + + device_state->ifindex = ifindex; + device_state->managed = managed_type; + device_state->connection_uuid = NULL; + if (connection_uuid) { + char *device_state_data; + + device_state_data = (char *) (&device_state[1]); + memcpy (device_state_data, connection_uuid, len_plus_1); + device_state->connection_uuid = device_state_data; + } + + return device_state; +} + +/** + * nm_config_device_state_load: + * @self: the NMConfig instance + * @ifindex: the ifindex for which the state is to load + * + * Returns: (transfer full): a run state object. + * Must be freed with g_free(). + */ +NMConfigDeviceStateData * +nm_config_device_state_load (NMConfig *self, + int ifindex) +{ + NMConfigDeviceStateData *device_state; + char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60]; + gs_unref_keyfile GKeyFile *kf = NULL; + gs_free_error GError *error = NULL; + + g_return_val_if_fail (ifindex > 0, NULL); + + nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); + + kf = nm_config_create_keyfile (); + if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, &error)) + g_clear_pointer (&kf, g_key_file_unref); + + device_state = _config_device_state_data_new (ifindex, kf); + + if (kf) { + _LOGT ("device-state: read #%d (%s); managed=%d, connection-uuid=%s%s%s", + ifindex, path, + device_state->managed, + NM_PRINT_FMT_QUOTE_STRING (device_state->connection_uuid)); + } else { + _LOGT ("device-state: read #%d (%s); no persistent state", + ifindex, path); + } + + return device_state; +} + +gboolean +nm_config_device_state_write (NMConfig *self, + int ifindex, + gboolean managed, + const char *connection_uuid) +{ + char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60]; + GError *local = NULL; + gs_unref_keyfile GKeyFile *kf = NULL; + + g_return_val_if_fail (NM_IS_CONFIG (self), FALSE); + g_return_val_if_fail (ifindex > 0, FALSE); + g_return_val_if_fail (!connection_uuid || *connection_uuid, FALSE); + g_return_val_if_fail (managed || !connection_uuid, FALSE); + + nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex); + + kf = nm_config_create_keyfile (); + g_key_file_set_boolean (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED, + !!managed); + if (connection_uuid) { + g_key_file_set_string (kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID, + connection_uuid); + } + + if (!g_key_file_save_to_file (kf, path, &local)) { + _LOGW ("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); + g_error_free (local); + return FALSE; + } + _LOGT ("device-state: write #%d (%s); managed=%d, connection-uuid=%s%s%s", + ifindex, path, + (bool) managed, + NM_PRINT_FMT_QUOTE_STRING (connection_uuid)); + return TRUE; +} + +void +nm_config_device_state_prune_unseen (NMConfig *self, + GHashTable *seen_ifindexes) +{ + GDir *dir; + const char *fn; + int ifindex; + gsize fn_len; + gsize i; + char buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + 30 + 3] = NM_CONFIG_DEVICE_STATE_DIR"/"; + char *buf_p = &buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/")]; + + g_return_if_fail (seen_ifindexes); + + dir = g_dir_open (NM_CONFIG_DEVICE_STATE_DIR, 0, NULL); + if (!dir) + return; + + while ((fn = g_dir_read_name (dir))) { + fn_len = strlen (fn); + + /* skip over file names that are not plain integers. */ + for (i = 0; i < fn_len; i++) { + if (!g_ascii_isdigit (fn[i])) + break; + } + if (fn_len == 0 || i != fn_len) + continue; + + ifindex = _nm_utils_ascii_str_to_int64 (fn, 10, 1, G_MAXINT, 0); + if (!ifindex) + continue; + + if (g_hash_table_contains (seen_ifindexes, GINT_TO_POINTER (ifindex))) + continue; + + memcpy (buf_p, fn, fn_len + 1); + _LOGT ("device-state: prune #%d (%s)", ifindex, buf); + (void) unlink (buf); + } + + g_dir_close (dir); +} + +/*****************************************************************************/ + void nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags) { @@ -1907,7 +2085,7 @@ nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags) &config_description, &error); if (!keyfile) { - nm_log_err (LOGD_CORE, "Failed to reload the configuration: %s", error->message); + _LOGE ("Failed to reload the configuration: %s", error->message); g_clear_error (&error); _set_config_data (self, NULL, reload_flags); return; @@ -1989,15 +2167,15 @@ _set_config_data (NMConfig *self, NMConfigData *new_data, NMConfigChangeFlags re } if (new_data) { - nm_log_info (LOGD_CORE, "config: signal %s (%s)", - nm_config_change_flags_to_string (changes, NULL, 0), - nm_config_data_get_config_description (new_data)); + _LOGI ("config: signal %s (%s)", + nm_config_change_flags_to_string (changes, NULL, 0), + nm_config_data_get_config_description (new_data)); nm_config_data_log (new_data, "CONFIG: ", " ", NULL); priv->config_data = new_data; } else if (had_new_data) - nm_log_info (LOGD_CORE, "config: signal %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0)); + _LOGI ("config: signal %s (no changes from disk)", nm_config_change_flags_to_string (changes, NULL, 0)); else - nm_log_info (LOGD_CORE, "config: signal %s", nm_config_change_flags_to_string (changes, NULL, 0)); + _LOGI ("config: signal %s", nm_config_change_flags_to_string (changes, NULL, 0)); g_signal_emit (self, signals[SIGNAL_CONFIG_CHANGED], 0, new_data ? new_data : old_data, changes, old_data); diff --git a/src/nm-config.h b/src/nm-config.h index a75e393523..6ada27672d 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -185,5 +185,34 @@ gboolean nm_config_set_global_dns (NMConfig *self, NMGlobalDnsConfig *global_dns extern guint _nm_config_match_nm_version; extern char *_nm_config_match_env; +/*****************************************************************************/ + +#define NM_CONFIG_DEVICE_STATE_DIR ""NMRUNDIR"/devices" + +typedef enum { + NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN = -1, + NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED = 0, + NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED = 1, +} NMConfigDeviceStateManagedType; + +typedef struct { + int ifindex; + NMConfigDeviceStateManagedType managed; + + /* the UUID of the last settings-connection active + * on the device. */ + const char *connection_uuid; +} NMConfigDeviceStateData; + +NMConfigDeviceStateData *nm_config_device_state_load (NMConfig *self, + int ifindex); +gboolean nm_config_device_state_write (NMConfig *self, + int ifindex, + gboolean managed, + const char *connection_uuid); +void nm_config_device_state_prune_unseen (NMConfig *self, GHashTable *seen_ifindexes); + +/*****************************************************************************/ + #endif /* __NETWORKMANAGER_CONFIG_H__ */ diff --git a/src/nm-manager.c b/src/nm-manager.c index 2335a731bf..efc21c9459 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -2083,7 +2083,8 @@ _register_device_factory (NMDeviceFactory *factory, gpointer user_data) static void platform_link_added (NMManager *self, int ifindex, - const NMPlatformLink *plink) + const NMPlatformLink *plink, + const NMConfigDeviceStateData *dev_state) { NMDeviceFactory *factory; NMDevice *device = NULL; @@ -2194,7 +2195,7 @@ _platform_link_cb_idle (PlatformLinkCbData *data) NMPlatformLink pllink; pllink = *l; /* make a copy of the link instance */ - platform_link_added (self, data->ifindex, &pllink); + platform_link_added (self, data->ifindex, &pllink, NULL); } else { NMDevice *device; GError *error = NULL; @@ -2249,14 +2250,24 @@ platform_link_cb (NMPlatform *platform, static void platform_query_devices (NMManager *self) { + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); GArray *links_array; NMPlatformLink *links; int i; links_array = nm_platform_link_get_all (NM_PLATFORM_GET); links = (NMPlatformLink *) links_array->data; - for (i = 0; i < links_array->len; i++) - platform_link_added (self, links[i].ifindex, &links[i]); + for (i = 0; i < links_array->len; i++) { + gs_free NMConfigDeviceStateData *dev_state = NULL; + + dev_state = nm_config_device_state_load (priv->config, + links[i].ifindex); + + platform_link_added (self, + links[i].ifindex, + &links[i], + dev_state); + } g_array_unref (links_array); } @@ -4586,6 +4597,51 @@ start_factory (NMDeviceFactory *factory, gpointer user_data) nm_device_factory_start (factory); } +void +nm_manager_write_device_state (NMManager *self) +{ + const GSList *devices; + NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self); + gs_unref_hashtable GHashTable *seen_ifindexes = NULL; + + seen_ifindexes = g_hash_table_new (NULL, NULL); + + for (devices = priv->devices; devices; devices = devices->next) { + NMDevice *device = NM_DEVICE (devices->data); + int ifindex; + gboolean managed; + NMConnection *settings_connection; + const char *uuid = NULL; + + ifindex = nm_device_get_ip_ifindex (device); + if (ifindex <= 0) + continue; + if (ifindex == 1) { + /* ignore loopback */ + continue; + } + + if (!nm_platform_link_get (NM_PLATFORM_GET, ifindex)) + continue; + + managed = nm_device_get_managed (device, FALSE); + if (managed) { + settings_connection = NM_CONNECTION (nm_device_get_settings_connection (device)); + if (settings_connection) + uuid = nm_connection_get_uuid (settings_connection); + } + + if (nm_config_device_state_write (priv->config, + ifindex, + managed, + uuid)) + g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex)); + } + + nm_config_device_state_prune_unseen (priv->config, + seen_ifindexes); +} + gboolean nm_manager_start (NMManager *self, GError **error) { diff --git a/src/nm-manager.h b/src/nm-manager.h index 11c4eb4a18..fb97951ce3 100644 --- a/src/nm-manager.h +++ b/src/nm-manager.h @@ -86,6 +86,8 @@ NMState nm_manager_get_state (NMManager *manager); const GSList *nm_manager_get_active_connections (NMManager *manager); GSList * nm_manager_get_activatable_connections (NMManager *manager); +void nm_manager_write_device_state (NMManager *manager); + /* Device handling */ const GSList * nm_manager_get_devices (NMManager *manager);