Commit 93396b8d authored by Thomas Haller's avatar Thomas Haller

config: store and load device runtime state to file

The data is still unused, the actual fields might change.

Note that the actual state we store is subject to change,
according to which data we need. The file format is non stable,
as the files don't survive reboot. So there is no backward
compatibility to maintain and the format can be changed later.
parent 1eca446c
......@@ -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 ());
......
......@@ -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)
{
......
......@@ -189,6 +189,29 @@ 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__ */
......
......@@ -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)
{
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment