1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /* NetworkManager -- Network link manager
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Copyright (C) 2011 Red Hat, Inc.
19 * Copyright (C) 2013 Thomas Bechtold <thomasbechtold@jpberlin.de>
20 */
21
22 #include <config.h>
23 #include <string.h>
24 #include <stdio.h>
25
26 #include "nm-config.h"
27 #include "nm-logging.h"
28 #include "nm-utils.h"
29 #include "nm-glib-compat.h"
30
31 #include <gio/gio.h>
32 #include <glib/gi18n.h>
33
34 #define NM_DEFAULT_SYSTEM_CONF_FILE NMCONFDIR "/NetworkManager.conf"
35 #define NM_DEFAULT_SYSTEM_CONF_DIR NMCONFDIR "/conf.d"
36 #define NM_OLD_SYSTEM_CONF_FILE NMCONFDIR "/nm-system-settings.conf"
37 #define NM_NO_AUTO_DEFAULT_STATE_FILE NMSTATEDIR "/no-auto-default.state"
38
39 typedef struct {
40 char *nm_conf_path;
41 char *config_dir;
42 char *config_description;
43 char *no_auto_default_file;
44 GKeyFile *keyfile;
45
46 char **plugins;
47 gboolean monitor_connection_files;
48 char *dhcp_client;
49 char *dns_mode;
50
51 char *log_level;
52 char *log_domains;
53
54 char *connectivity_uri;
55 gint connectivity_interval;
56 char *connectivity_response;
57
58 char **no_auto_default;
59 char **ignore_carrier;
60 } NMConfigPrivate;
61
62 static NMConfig *singleton = NULL;
63
64 G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
65
66 #define NM_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_CONFIG, NMConfigPrivate))
67
68 /************************************************************************/
69
70 const char *
71 nm_config_get_path (NMConfig *config)
72 {
73 g_return_val_if_fail (config != NULL, NULL);
74
75 return NM_CONFIG_GET_PRIVATE (config)->nm_conf_path;
76 }
77
78 const char *
79 nm_config_get_description (NMConfig *config)
80 {
81 g_return_val_if_fail (config != NULL, NULL);
82
83 return NM_CONFIG_GET_PRIVATE (config)->config_description;
84 }
85
86 const char **
87 nm_config_get_plugins (NMConfig *config)
88 {
89 g_return_val_if_fail (config != NULL, NULL);
90
91 return (const char **) NM_CONFIG_GET_PRIVATE (config)->plugins;
92 }
93
94 gboolean
95 nm_config_get_monitor_connection_files (NMConfig *config)
96 {
97 g_return_val_if_fail (config != NULL, FALSE);
98
99 return NM_CONFIG_GET_PRIVATE (config)->monitor_connection_files;
100 }
101
102 const char *
103 nm_config_get_dhcp_client (NMConfig *config)
104 {
105 g_return_val_if_fail (config != NULL, NULL);
106
107 return NM_CONFIG_GET_PRIVATE (config)->dhcp_client;
108 }
109
110 const char *
111 nm_config_get_dns_mode (NMConfig *config)
112 {
113 g_return_val_if_fail (config != NULL, NULL);
114
115 return NM_CONFIG_GET_PRIVATE (config)->dns_mode;
116 }
117
118 const char *
119 nm_config_get_log_level (NMConfig *config)
120 {
121 g_return_val_if_fail (config != NULL, NULL);
122
123 return NM_CONFIG_GET_PRIVATE (config)->log_level;
124 }
125
126 const char *
127 nm_config_get_log_domains (NMConfig *config)
128 {
129 g_return_val_if_fail (config != NULL, NULL);
130
131 return NM_CONFIG_GET_PRIVATE (config)->log_domains;
132 }
133
134 const char *
135 nm_config_get_connectivity_uri (NMConfig *config)
136 {
137 g_return_val_if_fail (config != NULL, NULL);
138
139 return NM_CONFIG_GET_PRIVATE (config)->connectivity_uri;
140 }
141
142 const guint
143 nm_config_get_connectivity_interval (NMConfig *config)
144 {
145 g_return_val_if_fail (config != NULL, 0);
146
147 /* We store interval as signed internally to track whether it's
148 * set or not, but report as unsigned to callers.
149 */
(1) Event result_independent_of_operands: |
"((NMConfigPrivate *)g_type_instance_get_private((GTypeInstance *)config, nm_config_get_type()))->connectivity_interval > 2147483647 /* (gint32)2147483647 */" is always false regardless of the values of its operands. This occurs as the logical first operand of '?:'. |
150 return CLAMP (NM_CONFIG_GET_PRIVATE (config)->connectivity_interval, 0, G_MAXINT32);
151 }
152
153 const char *
154 nm_config_get_connectivity_response (NMConfig *config)
155 {
156 g_return_val_if_fail (config != NULL, NULL);
157
158 return NM_CONFIG_GET_PRIVATE (config)->connectivity_response;
159 }
160
161 char *
162 nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error)
163 {
164 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
165
166 return g_key_file_get_string (priv->keyfile, group, key, error);
167 }
168
169 gboolean
170 nm_config_get_ignore_carrier (NMConfig *config, NMConfigDevice *device)
171 {
172 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
173
174 return nm_config_device_spec_match_list (device, (const char **) priv->ignore_carrier);
175 }
176
177 /************************************************************************/
178
179 static void
180 merge_no_auto_default_state (NMConfig *config)
181 {
182 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
183 GPtrArray *updated;
184 char **list;
185 int i, j;
186 char *data;
187
188 /* If the config already matches everything, we don't need to do anything else. */
189 if (priv->no_auto_default && !g_strcmp0 (priv->no_auto_default[0], "*"))
190 return;
191
192 updated = g_ptr_array_new ();
193 if (priv->no_auto_default) {
194 for (i = 0; priv->no_auto_default[i]; i++)
195 g_ptr_array_add (updated, priv->no_auto_default[i]);
196 g_free (priv->no_auto_default);
197 }
198
199 if (g_file_get_contents (priv->no_auto_default_file, &data, NULL, NULL)) {
200 list = g_strsplit (data, "\n", -1);
201 for (i = 0; list[i]; i++) {
202 if (!*list[i])
203 continue;
204 for (j = 0; j < updated->len; j++) {
205 if (!strcmp (list[i], updated->pdata[j]))
206 break;
207 }
208 if (j == updated->len)
209 g_ptr_array_add (updated, list[i]);
210 }
211 g_free (list);
212 g_free (data);
213 }
214
215 g_ptr_array_add (updated, NULL);
216 priv->no_auto_default = (char **) g_ptr_array_free (updated, FALSE);
217 }
218
219 gboolean
220 nm_config_get_ethernet_can_auto_default (NMConfig *config, NMConfigDevice *device)
221 {
222 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
223
224 return !nm_config_device_spec_match_list (device, (const char **) priv->no_auto_default);
225 }
226
227 void
228 nm_config_set_ethernet_no_auto_default (NMConfig *config, NMConfigDevice *device)
229 {
230 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
231 char *current;
232 GString *updated;
233 GError *error = NULL;
234
235 if (!nm_config_get_ethernet_can_auto_default (config, device))
236 return;
237
238 updated = g_string_new (NULL);
239 if (g_file_get_contents (priv->no_auto_default_file, ¤t, NULL, NULL)) {
240 g_string_append (updated, current);
241 g_free (current);
242 if (updated->str[updated->len - 1] != '\n')
243 g_string_append_c (updated, '\n');
244 }
245
246 g_string_append (updated, nm_config_device_get_hwaddr (device));
247 g_string_append_c (updated, '\n');
248
249 if (!g_file_set_contents (priv->no_auto_default_file, updated->str, updated->len, &error)) {
250 nm_log_warn (LOGD_SETTINGS, "Could not update no-auto-default.state file: %s",
251 error->message);
252 g_error_free (error);
253 }
254
255 g_string_free (updated, TRUE);
256
257 merge_no_auto_default_state (config);
258 }
259
260 /************************************************************************/
261
262 static char *cli_config_path;
263 static char *cli_config_dir;
264 static char *cli_no_auto_default_file;
265 static char *cli_plugins;
266 static char *cli_connectivity_uri;
267 static int cli_connectivity_interval = -1;
268 static char *cli_connectivity_response;
269
270 static GOptionEntry config_options[] = {
271 { "config", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_path, N_("Config file location"), N_("/path/to/config.file") },
272 { "config-dir", 0, 0, G_OPTION_ARG_FILENAME, &cli_config_dir, N_("Config directory location"), N_("/path/to/config/dir") },
273 { "no-auto-default", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &cli_no_auto_default_file, "no-auto-default.state location", NULL },
274 { "plugins", 0, 0, G_OPTION_ARG_STRING, &cli_plugins, N_("List of plugins separated by ','"), N_("plugin1,plugin2") },
275
276 /* These three are hidden for now, and should eventually just go away. */
277 { "connectivity-uri", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_uri, N_("An http(s) address for checking internet connectivity"), "http://example.com" },
278 { "connectivity-interval", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_INT, &cli_connectivity_interval, N_("The interval between connectivity checks (in seconds)"), "60" },
279 { "connectivity-response", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &cli_connectivity_response, N_("The expected start of the response"), N_("Bingo!") },
280 {NULL}
281 };
282 GOptionEntry *
283 nm_config_get_options (void)
284 {
285 return config_options;
286 }
287
288 /************************************************************************/
289
290 static gboolean
291 read_config (NMConfig *config, const char *path, GError **error)
292 {
293 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
294 GKeyFile *kf;
295 char **groups, **keys;
296 gsize ngroups, nkeys;
297 int g, k;
298
299 if (g_file_test (path, G_FILE_TEST_EXISTS) == FALSE) {
300 g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND, "file %s not found", path);
301 return FALSE;
302 }
303
304 nm_log_dbg (LOGD_SETTINGS, "Reading config file '%s'", path);
305
306 kf = g_key_file_new ();
307 g_key_file_set_list_separator (kf, ',');
308 if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, error)) {
309 g_key_file_free (kf);
310 return FALSE;
311 }
312
313 /* Override the current settings with the new ones */
314 groups = g_key_file_get_groups (kf, &ngroups);
315 for (g = 0; groups[g]; g++) {
316 keys = g_key_file_get_keys (kf, groups[g], &nkeys, NULL);
317 if (!keys)
318 continue;
319 for (k = 0; keys[k]; k++) {
320 int len = strlen (keys[k]);
321 if (keys[k][len - 1] == '+') {
322 char *base_key = g_strndup (keys[k], len - 1);
323 const char *old_val = g_key_file_get_value (priv->keyfile, groups[g], base_key, NULL);
324 const char *new_val = g_key_file_get_value (kf, groups[g], keys[k], NULL);
325
326 if (old_val && *old_val) {
327 char *combined = g_strconcat (old_val, ",", new_val, NULL);
328
329 g_key_file_set_value (priv->keyfile, groups[g], base_key, combined);
330 g_free (combined);
331 } else
332 g_key_file_set_value (priv->keyfile, groups[g], base_key, new_val);
333
334 g_free (base_key);
335 continue;
336 }
337
338 g_key_file_set_value (priv->keyfile, groups[g], keys[k],
339 g_key_file_get_value (kf, groups[g], keys[k], NULL));
340 }
341 }
342 g_key_file_free (kf);
343
344 return TRUE;
345 }
346
347 static gboolean
348 find_base_config (NMConfig *config, GError **error)
349 {
350 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
351 GError *my_error = NULL;
352
353 /* Try a user-specified config file first */
354 if (cli_config_path) {
355 /* Bad user-specific config file path is a hard error */
356 if (read_config (config, cli_config_path, error)) {
357 priv->nm_conf_path = g_strdup (cli_config_path);
358 return TRUE;
359 } else
360 return FALSE;
361 }
362
363 /* Even though we prefer NetworkManager.conf, we need to check the
364 * old nm-system-settings.conf first to preserve compat with older
365 * setups. In package managed systems dropping a NetworkManager.conf
366 * onto the system would make NM use it instead of nm-system-settings.conf,
367 * changing behavior during an upgrade. We don't want that.
368 */
369
370 /* Try deprecated nm-system-settings.conf first */
371 if (read_config (config, NM_OLD_SYSTEM_CONF_FILE, &my_error)) {
372 priv->nm_conf_path = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
373 return TRUE;
374 }
375
376 if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
377 g_warning ("Default config file %s invalid: %s\n",
378 NM_OLD_SYSTEM_CONF_FILE,
379 my_error->message);
380 }
381 g_clear_error (&my_error);
382
383 /* Try the standard config file location next */
384 if (read_config (config, NM_DEFAULT_SYSTEM_CONF_FILE, &my_error)) {
385 priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
386 return TRUE;
387 }
388
389 if (!g_error_matches (my_error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_NOT_FOUND)) {
390 g_warning ("Default config file %s invalid: %s\n",
391 NM_DEFAULT_SYSTEM_CONF_FILE,
392 my_error->message);
393 g_propagate_error (error, my_error);
394 return FALSE;
395 }
396
397 /* If for some reason no config file exists, use the default
398 * config file path.
399 */
400 priv->nm_conf_path = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
401 g_warning ("No config file found or given; using %s\n",
402 NM_DEFAULT_SYSTEM_CONF_FILE);
403 return TRUE;
404 }
405
406 /************************************************************************/
407
408 NMConfig *
409 nm_config_get (void)
410 {
411 g_assert (singleton);
412 return singleton;
413 }
414
415 static int
416 sort_asciibetically (gconstpointer a, gconstpointer b)
417 {
418 const char *s1 = *(const char **)a;
419 const char *s2 = *(const char **)b;
420
421 return strcmp (s1, s2);
422 }
423
424 /* call this function only once! */
425 NMConfig *
426 nm_config_new (GError **error)
427 {
428 NMConfigPrivate *priv = NULL;
429 GFile *dir;
430 GFileEnumerator *direnum;
431 GFileInfo *info;
432 GPtrArray *confs;
433 const char *name;
434 char *value;
435 int i;
436 GString *config_description;
437
438 g_assert (!singleton);
439 singleton = NM_CONFIG (g_object_new (NM_TYPE_CONFIG, NULL));
440 priv = NM_CONFIG_GET_PRIVATE (singleton);
441
442 /* First read the base config file */
443 if (!find_base_config (singleton, error)) {
444 g_object_unref (singleton);
445 singleton = NULL;
446 return NULL;
447 }
448
449 /* Now read the overrides in the config dir */
450 if (cli_config_dir)
451 priv->config_dir = g_strdup (cli_config_dir);
452 else
453 priv->config_dir = g_strdup (NM_DEFAULT_SYSTEM_CONF_DIR);
454
455 confs = g_ptr_array_new_with_free_func (g_free);
456 config_description = g_string_new (priv->nm_conf_path);
457 dir = g_file_new_for_path (priv->config_dir);
458 direnum = g_file_enumerate_children (dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, NULL);
459 if (direnum) {
460 while ((info = g_file_enumerator_next_file (direnum, NULL, NULL))) {
461 name = g_file_info_get_name (info);
462 if (g_str_has_suffix (name, ".conf")) {
463 g_ptr_array_add (confs, g_build_filename (priv->config_dir, name, NULL));
464 if (confs->len == 1)
465 g_string_append (config_description, " and conf.d: ");
466 else
467 g_string_append (config_description, ", ");
468 g_string_append (config_description, name);
469 }
470 g_object_unref (info);
471 }
472 g_object_unref (direnum);
473 }
474 g_object_unref (dir);
475
476 g_ptr_array_sort (confs, sort_asciibetically);
477 priv->config_description = g_string_free (config_description, FALSE);
478 for (i = 0; i < confs->len; i++) {
479 if (!read_config (singleton, confs->pdata[i], error)) {
480 g_object_unref (singleton);
481 singleton = NULL;
482 break;
483 }
484 }
485 g_ptr_array_unref (confs);
486 if (!singleton)
487 return FALSE;
488
489 /* Handle no-auto-default key and state file */
490 priv->no_auto_default = g_key_file_get_string_list (priv->keyfile, "main", "no-auto-default", NULL, NULL);
491 if (cli_no_auto_default_file)
492 priv->no_auto_default_file = g_strdup (cli_no_auto_default_file);
493 else
494 priv->no_auto_default_file = g_strdup (NM_NO_AUTO_DEFAULT_STATE_FILE);
495 merge_no_auto_default_state (singleton);
496
497 /* Now let command-line options override the config files, and fill in priv. */
498 if (cli_plugins && cli_plugins[0])
499 g_key_file_set_value (priv->keyfile, "main", "plugins", cli_plugins);
500 priv->plugins = g_key_file_get_string_list (priv->keyfile, "main", "plugins", NULL, NULL);
501
502 value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
503 if (value) {
504 if (!strcmp (value, "true") || !strcmp (value, "yes") || !strcmp (value, "on"))
505 priv->monitor_connection_files = TRUE;
506 else if (!strcmp (value, "false") || !strcmp (value, "no") || !strcmp (value, "off"))
507 priv->monitor_connection_files = FALSE;
508 else {
509 g_warning ("Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
510 priv->monitor_connection_files = FALSE;
511 }
512 g_free (value);
513 } else
514 priv->monitor_connection_files = FALSE;
515
516 priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
517 priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
518
519 priv->log_level = g_key_file_get_value (priv->keyfile, "logging", "level", NULL);
520 priv->log_domains = g_key_file_get_value (priv->keyfile, "logging", "domains", NULL);
521
522 if (cli_connectivity_uri && cli_connectivity_uri[0])
523 g_key_file_set_value (priv->keyfile, "connectivity", "uri", cli_connectivity_uri);
524 priv->connectivity_uri = g_key_file_get_value (priv->keyfile, "connectivity", "uri", NULL);
525
526 if (cli_connectivity_interval >= 0)
527 g_key_file_set_integer (priv->keyfile, "connectivity", "interval", cli_connectivity_interval);
528 priv->connectivity_interval = g_key_file_get_integer (priv->keyfile, "connectivity", "interval", NULL);
529
530 if (cli_connectivity_response && cli_connectivity_response[0])
531 g_key_file_set_value (priv->keyfile, "connectivity", "response", cli_connectivity_response);
532 priv->connectivity_response = g_key_file_get_value (priv->keyfile, "connectivity", "response", NULL);
533
534 priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
535
536 return singleton;
537 }
538
539 static void
540 nm_config_init (NMConfig *config)
541 {
542 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (config);
543
544 priv->keyfile = g_key_file_new ();
545 g_key_file_set_list_separator (priv->keyfile, ',');
546
547 priv->connectivity_interval = -1;
548 }
549
550 static void
551 finalize (GObject *gobject)
552 {
553 NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (gobject);
554
555 g_free (priv->nm_conf_path);
556 g_free (priv->config_dir);
557 g_free (priv->config_description);
558 g_free (priv->no_auto_default_file);
559 g_clear_pointer (&priv->keyfile, g_key_file_unref);
560 g_strfreev (priv->plugins);
561 g_free (priv->dhcp_client);
562 g_free (priv->dns_mode);
563 g_free (priv->log_level);
564 g_free (priv->log_domains);
565 g_free (priv->connectivity_uri);
566 g_free (priv->connectivity_response);
567 g_strfreev (priv->no_auto_default);
568 g_strfreev (priv->ignore_carrier);
569
570 singleton = NULL;
571
572 g_clear_pointer (&cli_config_path, g_free);
573 g_clear_pointer (&cli_config_dir, g_free);
574 g_clear_pointer (&cli_no_auto_default_file, g_free);
575 g_clear_pointer (&cli_plugins, g_free);
576 g_clear_pointer (&cli_connectivity_uri, g_free);
577 g_clear_pointer (&cli_connectivity_response, g_free);
578
579 G_OBJECT_CLASS (nm_config_parent_class)->finalize (gobject);
580 }
581
582
583 static void
584 nm_config_class_init (NMConfigClass *config_class)
585 {
586 GObjectClass *object_class = G_OBJECT_CLASS (config_class);
587
588 g_type_class_add_private (config_class, sizeof (NMConfigPrivate));
589 object_class->finalize = finalize;
590 }
591
592