main.c 14.9 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
Dan Williams's avatar
Dan Williams committed
2 3 4 5 6 7 8 9 10 11 12 13
/* NetworkManager -- Network link manager
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
14 15 16
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Dan Williams's avatar
Dan Williams committed
17
 *
18
 * Copyright (C) 2004 - 2017 Red Hat, Inc.
19
 * Copyright (C) 2005 - 2008 Novell, Inc.
Dan Williams's avatar
Dan Williams committed
20 21
 */

22
#include "nm-default.h"
23

Dan Williams's avatar
Dan Williams committed
24
#include <getopt.h>
25
#include <locale.h>
Dan Williams's avatar
Dan Williams committed
26
#include <stdlib.h>
27
#include <signal.h>
Dan Williams's avatar
Dan Williams committed
28 29 30
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
31
#include <sys/types.h>
32
#include <sys/resource.h>
Dan Williams's avatar
Dan Williams committed
33

34
#include "main-utils.h"
35
#include "nm-dbus-interface.h"
Dan Williams's avatar
Dan Williams committed
36
#include "NetworkManagerUtils.h"
37
#include "nm-manager.h"
38
#include "platform/nm-linux-platform.h"
39
#include "nm-dbus-manager.h"
40 41
#include "devices/nm-device.h"
#include "dhcp/nm-dhcp-manager.h"
42
#include "nm-config.h"
43
#include "nm-session-monitor.h"
44
#include "nm-dispatcher.h"
45
#include "settings/nm-settings.h"
46
#include "nm-auth-manager.h"
47
#include "nm-core-internal.h"
48
#include "nm-dbus-object.h"
49
#include "nm-connectivity.h"
50
#include "dns/nm-dns-manager.h"
51
#include "systemd/nm-sd.h"
52
#include "nm-netns.h"
Dan Williams's avatar
Dan Williams committed
53

54 55 56 57
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
#endif

58
#define NM_DEFAULT_PID_FILE          NMRUNDIR "/NetworkManager.pid"
59

60 61
#define CONFIG_ATOMIC_SECTION_PREFIXES ((char **) NULL)

62
static GMainLoop *main_loop = NULL;
63
static gboolean configure_and_quit = FALSE;
64

65 66
static struct {
	gboolean show_version;
67
	gboolean print_config;
68 69 70 71 72 73 74 75 76 77
	gboolean become_daemon;
	gboolean g_fatal_warnings;
	gboolean run_from_build_dir;
	char *opt_log_level;
	char *opt_log_domains;
	char *pidfile;
} global_opt = {
	.become_daemon = TRUE,
};

78
static void
79
_set_g_fatal_warnings (void)
80 81 82 83 84 85 86 87
{
	GLogLevelFlags fatal_mask;

	fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
	fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
	g_log_set_always_fatal (fatal_mask);
}

88
static void
89
_init_nm_debug (NMConfig *config)
90
{
91
	gs_free char *debug = NULL;
92 93 94 95
	enum {
		D_RLIMIT_CORE =    (1 << 0),
		D_FATAL_WARNINGS = (1 << 1),
	};
96 97
	GDebugKey keys[] = {
		{ "RLIMIT_CORE", D_RLIMIT_CORE },
98
		{ "fatal-warnings", D_FATAL_WARNINGS },
99
	};
100
	guint flags;
101
	const char *env = getenv ("NM_DEBUG");
102 103 104 105 106

	debug = nm_config_data_get_value (nm_config_get_data_orig (config),
	                                  NM_CONFIG_KEYFILE_GROUP_MAIN,
	                                  NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG,
	                                  NM_MANAGER_RELOAD_FLAGS_NONE);
107

108 109
	flags  = nm_utils_parse_debug_string (env, keys, G_N_ELEMENTS (keys));
	flags |= nm_utils_parse_debug_string (debug, keys, G_N_ELEMENTS (keys));
110

111
#if ! defined (__SANITIZE_ADDRESS__)
112
	if (NM_FLAGS_HAS (flags, D_RLIMIT_CORE)) {
113 114 115 116 117 118 119 120 121
		/* only enable this, if explicitly requested, because it might
		 * expose sensitive data. */

		struct rlimit limit = {
			.rlim_cur = RLIM_INFINITY,
			.rlim_max = RLIM_INFINITY,
		};
		setrlimit (RLIMIT_CORE, &limit);
	}
122 123
#endif

124 125
	if (NM_FLAGS_HAS (flags, D_FATAL_WARNINGS))
		_set_g_fatal_warnings ();
126 127
}

128
void
129
nm_main_config_reload (int signal)
130
{
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
	NMConfigChangeFlags reload_flags;

	switch (signal) {
	case SIGHUP:
		reload_flags = NM_CONFIG_CHANGE_CAUSE_SIGHUP;
		break;
	case SIGUSR1:
		reload_flags = NM_CONFIG_CHANGE_CAUSE_SIGUSR1;
		break;
	case SIGUSR2:
		reload_flags = NM_CONFIG_CHANGE_CAUSE_SIGUSR2;
		break;
	default:
		g_return_if_reached ();
	}

147
	nm_log_info (LOGD_CORE, "reload configuration (signal %s)...", strsignal (signal));
148

149 150 151 152 153 154
	/* The signal handler thread is only installed after
	 * creating NMConfig instance, and on shut down we
	 * no longer run the mainloop (to reach this point).
	 *
	 * Hence, a NMConfig singleton instance must always be
	 * available. */
155
	nm_config_reload (nm_config_get (), reload_flags, TRUE);
156 157
}

158
static void
159
manager_configure_quit (NMManager *manager, gpointer user_data)
160
{
161 162
	nm_log_info (LOGD_CORE, "quitting now that startup is complete");
	g_main_loop_quit (main_loop);
163
	configure_and_quit = TRUE;
164 165
}

166 167 168 169 170 171 172 173 174
static int
print_config (NMConfigCmdLineOptions *config_cli)
{
	gs_unref_object NMConfig *config = NULL;
	gs_free_error GError *error = NULL;
	NMConfigData *config_data;

	nm_logging_setup ("OFF", "ALL", NULL, NULL);

175
	config = nm_config_new (config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error);
176
	if (config == NULL) {
Thomas Haller's avatar
Thomas Haller committed
177
		fprintf (stderr, _("Failed to read configuration: %s\n"), error->message);
178 179 180 181 182 183 184 185 186
		return 7;
	}

	config_data = nm_config_get_data (config);
	fprintf (stdout, "# NetworkManager configuration: %s\n", nm_config_data_get_config_description (config_data));
	nm_config_data_log (config_data, "", "", stdout);
	return 0;
}

187 188
static void
do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
Dan Williams's avatar
Dan Williams committed
189
{
190
	GOptionEntry options[] = {
191 192 193 194
		{ "version", 'V', 0, G_OPTION_ARG_NONE, &global_opt.show_version, N_("Print NetworkManager version and exit"), NULL },
		{ "no-daemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &global_opt.become_daemon, N_("Don't become a daemon"), NULL },
		{ "log-level", 0, 0, G_OPTION_ARG_STRING, &global_opt.opt_log_level, N_("Log level: one of [%s]"), "INFO" },
		{ "log-domains", 0, 0, G_OPTION_ARG_STRING, &global_opt.opt_log_domains,
195 196
		  N_("Log domains separated by ',': any combination of [%s]"),
		  "PLATFORM,RFKILL,WIFI" },
197
		{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
198
		{ "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), NM_DEFAULT_PID_FILE },
199
		{ "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL },
200
		{ "print-config", 0, 0, G_OPTION_ARG_NONE, &global_opt.print_config, N_("Print NetworkManager configuration and exit"), NULL },
201 202 203
		{NULL}
	};

204
	if (!nm_main_utils_early_setup ("NetworkManager",
205 206
	                                argc,
	                                argv,
207
	                                options,
208 209
	                                (void (*)(gpointer, GOptionContext *)) nm_config_cmd_line_options_add_to_entries,
	                                config_cli,
210
	                                _("NetworkManager monitors all network connections and automatically\nchooses the best connection to use.  It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with.")))
211
		exit (1);
Dan Williams's avatar
Dan Williams committed
212

213
	global_opt.pidfile = global_opt.pidfile ?: g_strdup(NM_DEFAULT_PID_FILE);
214 215 216 217 218 219 220 221 222
}

/*
 * main
 *
 */
int
main (int argc, char *argv[])
{
223
	gboolean success = FALSE;
224
	NMManager *manager = NULL;
225
	NMConfig *config;
226
	gs_free_error GError *error = NULL;
227 228 229
	gboolean wrote_pidfile = FALSE;
	char *bad_domains = NULL;
	NMConfigCmdLineOptions *config_cli;
230
	guint sd_id = 0;
231
	GError *error_invalid_logging_config = NULL;
232
	const char *const *warnings;
233
	int errsv;
234

235
	/* Known to cause a possible deadlock upon GDBus initialization:
236 237 238
	 * https://bugzilla.gnome.org/show_bug.cgi?id=674885 */
	g_type_ensure (G_TYPE_SOCKET);
	g_type_ensure (G_TYPE_DBUS_CONNECTION);
239
	g_type_ensure (NM_TYPE_DBUS_MANAGER);
240

241 242 243 244
	_nm_utils_is_manager_process = TRUE;

	main_loop = g_main_loop_new (NULL, FALSE);

245 246 247 248 249
	/* we determine a first-start (contrary to a restart during the same boot)
	 * based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */
	config_cli = nm_config_cmd_line_options_new (!g_file_test (NM_CONFIG_DEVICE_STATE_DIR,
	                                                           G_FILE_TEST_IS_DIR));

250 251
	do_early_setup (&argc, &argv, config_cli);

252 253
	if (global_opt.g_fatal_warnings)
		_set_g_fatal_warnings ();
254

255
	if (global_opt.show_version) {
256 257 258 259
		fprintf (stdout, NM_DIST_VERSION "\n");
		exit (0);
	}

260 261 262 263 264 265 266 267
	if (global_opt.print_config) {
		int result;

		result = print_config (config_cli);
		nm_config_cmd_line_options_free (config_cli);
		exit (result);
	}

268 269
	nm_main_utils_ensure_root ();

270 271
	nm_main_utils_ensure_not_running_pidfile (global_opt.pidfile);

272
	nm_main_utils_ensure_statedir ();
273 274
	nm_main_utils_ensure_rundir ();

275 276
	/* When running from the build directory, determine our build directory
	 * base and set helper paths in the build tree */
277
	if (global_opt.run_from_build_dir) {
278
		char *path, *slash;
279
		int g;
280 281 282 283 284

		/* exe is <basedir>/src/.libs/lt-NetworkManager, so chop off
		 * the last three components */
		path = realpath ("/proc/self/exe", NULL);
		g_assert (path != NULL);
285
		for (g = 0; g < 3; ++g) {
286 287 288 289 290 291 292
			slash = strrchr (path, '/');
			g_assert (slash != NULL);
			*slash = '\0';
		}

		/* don't free these strings, we need them for the entire
		 * process lifetime */
293
		nm_dhcp_helper_path = g_strdup_printf ("%s/src/dhcp/nm-dhcp-helper", path);
294 295 296 297

		g_free (path);
	}

298 299 300 301 302 303 304 305 306 307
	if (!nm_logging_setup (global_opt.opt_log_level,
	                       global_opt.opt_log_domains,
	                       &bad_domains,
	                       &error)) {
		fprintf (stderr,
		         _("%s.  Please use --help to see a list of valid options.\n"),
		         error->message);
		exit (1);
	}

308
	/* Read the config file and CLI overrides */
309
	config = nm_config_setup (config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error);
310 311
	nm_config_cmd_line_options_free (config_cli);
	config_cli = NULL;
312
	if (config == NULL) {
313
		fprintf (stderr, _("Failed to read configuration: %s\n"),
Thomas Haller's avatar
Thomas Haller committed
314
		         error->message);
315
		exit (1);
316
	}
317

318
	_init_nm_debug (config);
319

320 321 322
	/* Initialize logging from config file *only* if not explicitly
	 * specified by commandline.
	 */
323
	if (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL) {
324 325
		if (!nm_logging_setup (nm_config_get_log_level (config),
		                       nm_config_get_log_domains (config),
326
		                       &bad_domains,
327
		                       &error_invalid_logging_config)) {
328 329
			/* ignore error, and print the failure reason below.
			 * Likewise, print about bad_domains below. */
330
		}
331 332
	}

333
	if (global_opt.become_daemon && !nm_config_get_is_debug (config)) {
334
		if (daemon (0, 0) < 0) {
335
			errsv = errno;
336
			fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
337 338
			         g_strerror (errsv),
			         errsv);
339
			exit (1);
340
		}
341
		wrote_pidfile = nm_main_utils_write_pidfile (global_opt.pidfile);
342 343
	}

344
	/* Set up unix signal handling - before creating threads, but after daemonizing! */
345
	nm_main_utils_setup_signals (main_loop);
346

347 348 349 350 351 352 353
	{
		gs_free char *v = NULL;

		v = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
		                              NM_CONFIG_KEYFILE_GROUP_LOGGING,
		                              NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND,
		                              NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
354
		nm_logging_init (v, nm_config_get_is_debug (config));
355
	}
356

357 358
	nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting... (%s)",
	             nm_config_get_first_start (config) ? "for the first time" : "after a restart");
359

360
	nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
361
	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", NULL);
362

363 364 365 366
	if (error_invalid_logging_config) {
		nm_log_warn (LOGD_CORE, "config: invalid logging configuration: %s", error_invalid_logging_config->message);
		g_clear_error (&error_invalid_logging_config);
	}
367 368 369 370 371 372 373 374
	if (bad_domains) {
		nm_log_warn (LOGD_CORE, "config: invalid logging domains '%s' from %s",
		             bad_domains,
		             (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL)
		               ? "config file"
		               : "command line");
		nm_clear_g_free (&bad_domains);
	}
375

376 377 378 379 380
	warnings = nm_config_get_warnings (config);
	for ( ; warnings && *warnings; warnings++)
		nm_log_warn (LOGD_CORE, "config: %s", *warnings);
	nm_config_clear_warnings (config);

381 382
	/* the first access to State causes the file to be read (and possibly print a warning) */
	nm_config_state_get (config);
383

384
	nm_log_dbg (LOGD_CORE, "WEXT support is %s",
385 386 387 388 389 390
#if HAVE_WEXT
	             "enabled"
#else
	             "disabled"
#endif
	             );
391

392 393 394 395 396
	/* Set up platform interaction layer */
	nm_linux_platform_setup ();

	NM_UTILS_KEEP_ALIVE (config, nm_netns_get (), "NMConfig-depends-on-NMNetns");

397 398 399 400
	nm_auth_manager_setup (nm_config_data_get_value_boolean (nm_config_get_data_orig (config),
	                                                         NM_CONFIG_KEYFILE_GROUP_MAIN,
	                                                         NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
	                                                         NM_CONFIG_DEFAULT_MAIN_AUTH_POLKIT_BOOL));
401

402 403 404 405 406 407 408
	if (!nm_config_get_configure_and_quit (config)) {
		/* D-Bus is useless in configure and quit mode -- we're eventually dropping
		 * off and potential clients would have no way of knowing whether we're
		 * finished already or didn't start yet. */
		if (!nm_dbus_manager_acquire_bus (nm_dbus_manager_get ()))
			goto done_no_manager;
	}
409

410 411 412 413
	manager = nm_manager_setup ();
	nm_dbus_manager_start (nm_dbus_manager_get(),
	                       nm_manager_dbus_set_property_handle,
	                       manager);
414

415 416
	nm_dispatcher_init ();

417
	g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
418

419
	if (!nm_manager_start (manager, &error)) {
420 421 422
		nm_log_err (LOGD_CORE, "failed to initialize: %s", error->message);
		goto done;
	}
423

424 425
	nm_platform_process_events (NM_PLATFORM_GET);

426 427 428 429 430 431 432 433 434 435 436
	/* Make sure the loopback interface is up. If interface is down, we bring
	 * it up and kernel will assign it link-local IPv4 and IPv6 addresses. If
	 * it was already up, we assume is in clean state.
	 *
	 * TODO: it might be desirable to check the list of addresses and compare
	 * it with a list of expected addresses (one of the protocol families
	 * could be disabled). The 'lo' interface is sometimes used for assigning
	 * global addresses so their availability doesn't depend on the state of
	 * physical interfaces.
	 */
	nm_log_dbg (LOGD_CORE, "setting up local loopback");
437
	nm_platform_link_set_up (NM_PLATFORM_GET, 1, NULL);
438

Dan Williams's avatar
Dan Williams committed
439 440
	success = TRUE;

441 442 443
	if (configure_and_quit == FALSE) {
		sd_id = nm_sd_event_attach_default ();

444
		g_main_loop_run (main_loop);
445
	}
446

447
done:
448 449 450 451 452

	/* 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. */
453
	nm_manager_write_device_state_all (manager);
454

455
	nm_manager_stop (manager);
456

457
	nm_config_state_set (config, TRUE, TRUE);
458

459 460
	nm_dns_manager_stop (nm_dns_manager_get ());

461
done_no_manager:
462 463
	if (global_opt.pidfile && wrote_pidfile)
		unlink (global_opt.pidfile);
464

465
	nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
466 467 468

	nm_clear_g_source (&sd_id);

Dan Williams's avatar
Dan Williams committed
469
	exit (success ? 0 : 1);
Dan Williams's avatar
Dan Williams committed
470
}