Commit 1b43c880 authored by Thomas Haller's avatar Thomas Haller

config: let NMConfig handle "NetworkManager.state" file (bgo#764474)

Move reading and writing of the state file to NMConfig
("/var/lib/NetworkManager/NetworkManager.state" file).

Originally, I intended to persist more state, thus it made
sense to cleanup handling of the state file and move it all
at one place. Now, it's not clear that will happen anytime soon.

Still, the change is a worthy cleanup, so do it anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=764474
parent 350d96a9
......@@ -71,81 +71,10 @@ static struct {
char *opt_log_level;
char *opt_log_domains;
char *pidfile;
char *state_file;
} global_opt = {
.become_daemon = TRUE,
};
static gboolean
parse_state_file (const char *filename,
gboolean *net_enabled,
gboolean *wifi_enabled,
gboolean *wwan_enabled,
GError **error)
{
GKeyFile *state_file;
GError *tmp_error = NULL;
gboolean wifi, net, wwan;
g_return_val_if_fail (net_enabled != NULL, FALSE);
g_return_val_if_fail (wifi_enabled != NULL, FALSE);
g_return_val_if_fail (wwan_enabled != NULL, FALSE);
state_file = g_key_file_new ();
g_key_file_set_list_separator (state_file, ',');
if (!g_key_file_load_from_file (state_file, filename, G_KEY_FILE_KEEP_COMMENTS, &tmp_error)) {
gboolean ret = FALSE;
/* This is kinda ugly; create the file and directory if it doesn't
* exist yet. We can't rely on distros necessarily creating the
* /var/lib/NetworkManager for us since we have to ensure that
* users upgrading NM get this working too.
*/
if (g_error_matches (tmp_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
char *data;
gsize len = 0;
g_clear_error (&tmp_error);
/* Write out the initial state to the state file */
g_key_file_set_boolean (state_file, "main", "NetworkingEnabled", *net_enabled);
g_key_file_set_boolean (state_file, "main", "WirelessEnabled", *wifi_enabled);
g_key_file_set_boolean (state_file, "main", "WWANEnabled", *wwan_enabled);
data = g_key_file_to_data (state_file, &len, NULL);
if (data)
ret = g_file_set_contents (filename, data, len, error);
g_free (data);
} else {
/* the error is not "No such file or directory" - propagate the error */
g_propagate_error (error, tmp_error);
}
return ret;
}
/* Reading state bits of NetworkManager; an error leaves the passed-in state
* value unchanged.
*/
net = g_key_file_get_boolean (state_file, "main", "NetworkingEnabled", &tmp_error);
if (tmp_error == NULL)
*net_enabled = net;
g_clear_error (&tmp_error);
wifi = g_key_file_get_boolean (state_file, "main", "WirelessEnabled", &tmp_error);
if (tmp_error == NULL)
*wifi_enabled = wifi;
g_clear_error (&tmp_error);
wwan = g_key_file_get_boolean (state_file, "main", "WWANEnabled", &tmp_error);
if (tmp_error == NULL)
*wwan_enabled = wwan;
g_clear_error (&tmp_error);
g_key_file_free (state_file);
return TRUE;
}
static void
_set_g_fatal_warnings (void)
{
......@@ -239,7 +168,6 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
"PLATFORM,RFKILL,WIFI" },
{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
{ "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), N_(NM_DEFAULT_PID_FILE) },
{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &global_opt.state_file, N_("State file location"), N_(NM_DEFAULT_SYSTEM_STATE_FILE) },
{ "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL },
{ "print-config", 0, 0, G_OPTION_ARG_NONE, &global_opt.print_config, N_("Print NetworkManager configuration and exit"), NULL },
{NULL}
......@@ -255,7 +183,6 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
exit (1);
global_opt.pidfile = global_opt.pidfile ? global_opt.pidfile : g_strdup (NM_DEFAULT_PID_FILE);
global_opt.state_file = global_opt.state_file ? global_opt.state_file : g_strdup (NM_DEFAULT_SYSTEM_STATE_FILE);
}
/*
......@@ -265,7 +192,6 @@ do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
int
main (int argc, char *argv[])
{
gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE;
gboolean success = FALSE;
NMConfig *config;
GError *error = NULL;
......@@ -406,17 +332,12 @@ main (int argc, char *argv[])
nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting...");
/* Parse the state file */
if (!parse_state_file (global_opt.state_file, &net_enabled, &wifi_enabled, &wwan_enabled, &error)) {
nm_log_err (LOGD_CORE, "State file %s parsing failed: %s",
global_opt.state_file,
error->message);
/* Not a hard failure */
}
g_clear_error (&error);
nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
nm_config_data_log (nm_config_get_data (config), "CONFIG: ", " ", NULL);
/* the first access to RunState causes the file to be read (and possibly print a warning) */
nm_config_run_state_get (config);
nm_log_dbg (LOGD_CORE, "WEXT support is %s",
#if HAVE_WEXT
"enabled"
......@@ -427,10 +348,7 @@ main (int argc, char *argv[])
nm_auth_manager_setup (nm_config_get_auth_polkit (config));
nm_manager_setup (global_opt.state_file,
net_enabled,
wifi_enabled,
wwan_enabled);
nm_manager_setup ();
if (!nm_bus_manager_get_connection (nm_bus_manager_get ())) {
nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available");
......@@ -482,6 +400,8 @@ done:
nm_manager_stop (nm_manager_get ());
nm_config_run_state_set (config, TRUE, TRUE);
if (global_opt.pidfile && wrote_pidfile)
unlink (global_opt.pidfile);
......
......@@ -38,12 +38,27 @@
#define DEFAULT_SYSTEM_CONFIG_DIR NMLIBDIR "/conf.d"
#define DEFAULT_NO_AUTO_DEFAULT_FILE NMSTATEDIR "/no-auto-default.state"
#define DEFAULT_INTERN_CONFIG_FILE NMSTATEDIR "/NetworkManager-intern.conf"
#define DEFAULT_STATE_FILE NMSTATEDIR "/NetworkManager.state"
/*****************************************************************************/
#define _NMLOG_PREFIX_NAME "config"
#define _NMLOG_DOMAIN LOGD_CORE
#define _NMLOG(level, ...) \
nm_log (level, _NMLOG_DOMAIN, \
"%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
_NMLOG_PREFIX_NAME \
_NM_UTILS_MACRO_REST(__VA_ARGS__))
/*****************************************************************************/
struct NMConfigCmdLineOptions {
char *config_main_file;
char *intern_config_file;
char *config_dir;
char *system_config_dir;
char *state_file;
char *no_auto_default_file;
char *plugins;
gboolean configure_and_quit;
......@@ -57,6 +72,10 @@ struct NMConfigCmdLineOptions {
char *connectivity_response;
};
typedef struct {
NMConfigRunState p;
} RunState;
typedef struct {
NMConfigCmdLineOptions cli;
......@@ -81,6 +100,16 @@ typedef struct {
gboolean configure_and_quit;
char **atomic_section_prefixes;
/* The run-state. This is actually a mutable data member and it makes sense:
* The regular config is immutable (NMConfigData) which allows atomic updates
* which is handy during reload. Also, we invoke a config-changed signal when
* the config changes.
*
* For run-state, there are no events. You can query it and set it.
* It only gets read once at startup, and later is cached and only written
* out to disk. Hence, no need for the immutable dance here. */
RunState *run_state;
} NMConfigPrivate;
enum {
......@@ -406,6 +435,7 @@ _nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli)
g_clear_pointer (&cli->system_config_dir, g_free);
g_clear_pointer (&cli->no_auto_default_file, g_free);
g_clear_pointer (&cli->intern_config_file, g_free);
g_clear_pointer (&cli->state_file, g_free);
g_clear_pointer (&cli->plugins, g_free);
cli->configure_and_quit = FALSE;
cli->is_debug = FALSE;
......@@ -427,6 +457,7 @@ _nm_config_cmd_line_options_copy (const NMConfigCmdLineOptions *cli, NMConfigCmd
dst->config_main_file = g_strdup (cli->config_main_file);
dst->no_auto_default_file = g_strdup (cli->no_auto_default_file);
dst->intern_config_file = g_strdup (cli->intern_config_file);
dst->state_file = g_strdup (cli->state_file);
dst->plugins = g_strdup (cli->plugins);
dst->configure_and_quit = cli->configure_and_quit;
dst->is_debug = cli->is_debug;
......@@ -466,6 +497,7 @@ nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli,
{ "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->config_dir, N_("Config directory location"), N_(DEFAULT_CONFIG_DIR) },
{ "system-config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli->system_config_dir, N_("System config directory location"), N_(DEFAULT_SYSTEM_CONFIG_DIR) },
{ "intern-config", 0, 0, G_OPTION_ARG_FILENAME, &cli->intern_config_file, N_("Internal config file location"), N_(DEFAULT_INTERN_CONFIG_FILE) },
{ "state-file", 0, 0, G_OPTION_ARG_FILENAME, &cli->state_file, N_("State file location"), N_(DEFAULT_STATE_FILE) },
{ "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli->no_auto_default_file, N_("State file for no-auto-default devices"), N_(DEFAULT_NO_AUTO_DEFAULT_FILE) },
{ "plugins", 0, 0, G_OPTION_ARG_STRING, &cli->plugins, N_("List of plugins separated by ','"), N_(CONFIG_PLUGINS_DEFAULT) },
{ "configure-and-quit", 0, 0, G_OPTION_ARG_NONE, &cli->configure_and_quit, N_("Quit after initial configuration"), NULL },
......@@ -1645,7 +1677,191 @@ nm_config_set_values (NMConfig *self,
g_key_file_unref (keyfile_new);
}
/************************************************************************/
/******************************************************************************
* RunState
******************************************************************************/
static const char *
run_state_get_filename (const NMConfigCmdLineOptions *cli)
{
/* For an empty filename, we assume the user wants to disable
* persistent run-state. NMConfig will not try to read it nor
* write it out. */
if (!cli->state_file)
return DEFAULT_STATE_FILE;
return cli->state_file[0] ? cli->state_file : NULL;
}
static RunState *
run_state_new (void)
{
RunState *run_state;
run_state = g_slice_new0 (RunState);
run_state->p.net_enabled = TRUE;
run_state->p.wifi_enabled = TRUE;
run_state->p.wwan_enabled = TRUE;
return run_state;
}
static void
run_state_free (RunState *run_state)
{
if (!run_state)
return;
g_slice_free (RunState, run_state);
}
static RunState *
run_state_new_from_file (const char *filename)
{
GKeyFile *keyfile;
gs_free_error GError *error = NULL;
RunState *run_state;
run_state = run_state_new ();
if (!filename)
return run_state;
keyfile = g_key_file_new ();
g_key_file_set_list_separator (keyfile, ',');
if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error)) {
if (g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
_LOGD ("run-state: missing state file \"%s\": %s", filename, error->message);
else
_LOGW ("run-state: error reading state file \"%s\": %s", filename, error->message);
goto out;
}
_LOGD ("run-state: successfully read state file \"%s\"", filename);
run_state->p.net_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "NetworkingEnabled", run_state->p.net_enabled);
run_state->p.wifi_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "WirelessEnabled", run_state->p.wifi_enabled);
run_state->p.wwan_enabled = nm_config_keyfile_get_boolean (keyfile, "main", "WWANEnabled", run_state->p.wwan_enabled);
out:
g_key_file_unref (keyfile);
return run_state;
}
const NMConfigRunState *
nm_config_run_state_get (NMConfig *self)
{
NMConfigPrivate *priv;
g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
priv = NM_CONFIG_GET_PRIVATE (self);
if (G_UNLIKELY (!priv->run_state)) {
/* read the runstate from file lazy on first access. The reason is that
* we want to log a failure to read the file via nm-logging. But during
* construction of NMConfig, nm-logging is not yet configured.
*/
priv->run_state = run_state_new_from_file (run_state_get_filename (&priv->cli));
}
return &priv->run_state->p;
}
static void
run_state_write (NMConfig *self)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
const char *filename;
GString *str;
GError *error = NULL;
filename = run_state_get_filename (&priv->cli);
if (!filename) {
priv->run_state->p.dirty = FALSE;
return;
}
str = g_string_sized_new (256);
/* Let's construct the keyfile data by hand. */
g_string_append (str, "[main]\n");
g_string_append_printf (str, "NetworkingEnabled=%s\n", priv->run_state->p.net_enabled ? "true" : "false");
g_string_append_printf (str, "WirelessEnabled=%s\n", priv->run_state->p.wifi_enabled ? "true" : "false");
g_string_append_printf (str, "WWANEnabled=%s\n", priv->run_state->p.wwan_enabled ? "true" : "false");
if (!g_file_set_contents (filename,
str->str, str->len,
&error)) {
_LOGD ("run-state: error writing state file \"%s\": %s", filename, error->message);
g_clear_error (&error);
/* we leave the state dirty. That potentally means, that we try to
* write the file over and over again, although it isn't possible. */
priv->run_state->p.dirty = TRUE;
} else
priv->run_state->p.dirty = FALSE;
_LOGT ("run-state: success writing state file \"%s\"", filename);
g_string_free (str, TRUE);
}
void
_nm_config_run_state_set (NMConfig *self,
gboolean allow_persist,
gboolean force_persist,
...)
{
NMConfigPrivate *priv;
va_list ap;
NMConfigRunStatePropertyType property_type;
g_return_if_fail (NM_IS_CONFIG (self));
priv = NM_CONFIG_GET_PRIVATE (self);
va_start (ap, force_persist);
/* We expect that the NMConfigRunStatePropertyType is an integer type <= sizeof (int).
* Smaller would be fine, since the variadic arguments get promoted to int.
* Larger would be a problem, also, because we want that "0" is a valid sentinel. */
G_STATIC_ASSERT_EXPR (sizeof (NMConfigRunStatePropertyType) <= sizeof (int));
while ((property_type = va_arg (ap, int)) != NM_CONFIG_RUN_STATE_PROPERTY_NONE) {
bool *p_bool, v_bool;
switch (property_type) {
case NM_CONFIG_RUN_STATE_PROPERTY_NETWORKING_ENABLED:
p_bool = &priv->run_state->p.net_enabled;
goto handle_p_bool;
case NM_CONFIG_RUN_STATE_PROPERTY_WIFI_ENABLED:
p_bool = &priv->run_state->p.wifi_enabled;
goto handle_p_bool;
case NM_CONFIG_RUN_STATE_PROPERTY_WWAN_ENABLED:
p_bool = &priv->run_state->p.wwan_enabled;
goto handle_p_bool;
default:
break;
}
g_return_if_reached ();
handle_p_bool:
v_bool = va_arg (ap, gboolean);
if (*p_bool == v_bool)
continue;
*p_bool = v_bool;
priv->run_state->p.dirty = TRUE;
}
va_end (ap);
if ( allow_persist
&& (force_persist || priv->run_state->p.dirty))
run_state_write (self);
}
/*****************************************************************************/
void
nm_config_reload (NMConfig *self, int signal)
......@@ -1918,6 +2134,8 @@ finalize (GObject *gobject)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
run_state_free (priv->run_state);
g_free (priv->config_dir);
g_free (priv->system_config_dir);
g_free (priv->no_auto_default_file);
......
......@@ -83,6 +83,24 @@ G_BEGIN_DECLS
typedef struct NMConfigCmdLineOptions NMConfigCmdLineOptions;
typedef enum {
NM_CONFIG_RUN_STATE_PROPERTY_NONE,
/* 1 set-argument: (gboolean enabled) */
NM_CONFIG_RUN_STATE_PROPERTY_NETWORKING_ENABLED,
NM_CONFIG_RUN_STATE_PROPERTY_WIFI_ENABLED,
NM_CONFIG_RUN_STATE_PROPERTY_WWAN_ENABLED,
} NMConfigRunStatePropertyType;
typedef struct {
bool net_enabled;
bool wifi_enabled;
bool wwan_enabled;
/* Whether the runstate is modified and not saved to disk. */
bool dirty;
} NMConfigRunState;
struct _NMConfig {
GObject parent;
};
......@@ -131,6 +149,15 @@ NMConfig *nm_config_new (const NMConfigCmdLineOptions *cli, char **atomic_sectio
NMConfig *nm_config_setup (const NMConfigCmdLineOptions *cli, char **atomic_section_prefixes, GError **error);
void nm_config_reload (NMConfig *config, int signal);
const NMConfigRunState *nm_config_run_state_get (NMConfig *config);
void _nm_config_run_state_set (NMConfig *config,
gboolean allow_persist,
gboolean force_persist,
...);
#define nm_config_run_state_set(config, allow_persist, force_persist, ...) \
_nm_config_run_state_set (config, allow_persist, force_persist, ##__VA_ARGS__, 0)
gint nm_config_parse_boolean (const char *str, gint default_value);
GKeyFile *nm_config_create_keyfile (void);
......
This diff is collapsed.
......@@ -55,7 +55,6 @@
/* Not exported */
#define NM_MANAGER_HOSTNAME "hostname"
#define NM_MANAGER_SLEEPING "sleeping"
#define NM_MANAGER_STATE_FILE "state-file"
/* signals */
#define NM_MANAGER_CHECK_PERMISSIONS "check-permissions"
......@@ -88,10 +87,7 @@ typedef struct {
GType nm_manager_get_type (void);
/* nm_manager_setup() should only be used by main.c */
NMManager * nm_manager_setup (const char *state_file,
gboolean initial_net_enabled,
gboolean initial_wifi_enabled,
gboolean initial_wwan_enabled);
NMManager * nm_manager_setup (void);
NMManager * nm_manager_get (void);
......
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