main.c 14.6 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 27
#include <errno.h>
#include <stdlib.h>
28
#include <signal.h>
29
#include <pthread.h>
Dan Williams's avatar
Dan Williams committed
30 31 32
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
33
#include <sys/types.h>
Dan Williams's avatar
Dan Williams committed
34
#include <string.h>
35
#include <sys/resource.h>
Dan Williams's avatar
Dan Williams committed
36

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

57 58 59 60
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
#endif

61 62
#define NM_DEFAULT_PID_FILE          NMRUNDIR "/NetworkManager.pid"
#define NM_DEFAULT_SYSTEM_STATE_FILE NMSTATEDIR "/NetworkManager.state"
63

64 65
#define CONFIG_ATOMIC_SECTION_PREFIXES ((char **) NULL)

66
static GMainLoop *main_loop = NULL;
67
static gboolean configure_and_quit = FALSE;
68

69 70
static struct {
	gboolean show_version;
71
	gboolean print_config;
72 73 74 75 76 77 78 79 80 81
	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,
};

82
static void
83
_set_g_fatal_warnings (void)
84 85 86 87 88 89 90 91
{
	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);
}

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

	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);
111

112 113
	flags  = nm_utils_parse_debug_string (env, keys, G_N_ELEMENTS (keys));
	flags |= nm_utils_parse_debug_string (debug, keys, G_N_ELEMENTS (keys));
114

115
#if ! defined (__SANITIZE_ADDRESS__)
116
	if (NM_FLAGS_HAS (flags, D_RLIMIT_CORE)) {
117 118 119 120 121 122 123 124 125
		/* 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);
	}
126 127
#endif

128 129
	if (NM_FLAGS_HAS (flags, D_FATAL_WARNINGS))
		_set_g_fatal_warnings ();
130 131
}

132
void
133
nm_main_config_reload (int signal)
134
{
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
	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 ();
	}

151
	nm_log_info (LOGD_CORE, "reload configuration (signal %s)...", strsignal (signal));
152

153 154 155 156 157 158
	/* 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. */
159
	nm_config_reload (nm_config_get (), reload_flags);
160 161
}

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

170 171 172 173 174 175 176 177 178
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);

179
	config = nm_config_new (config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error);
180
	if (config == NULL) {
Thomas Haller's avatar
Thomas Haller committed
181
		fprintf (stderr, _("Failed to read configuration: %s\n"), error->message);
182 183 184 185 186 187 188 189 190
		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;
}

191 192
static void
do_early_setup (int *argc, char **argv[], NMConfigCmdLineOptions *config_cli)
Dan Williams's avatar
Dan Williams committed
193
{
194
	GOptionEntry options[] = {
195 196 197 198
		{ "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,
199 200
		  N_("Log domains separated by ',': any combination of [%s]"),
		  "PLATFORM,RFKILL,WIFI" },
201
		{ "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &global_opt.g_fatal_warnings, N_("Make all warnings fatal"), NULL },
202
		{ "pid-file", 'p', 0, G_OPTION_ARG_FILENAME, &global_opt.pidfile, N_("Specify the location of a PID file"), NM_DEFAULT_PID_FILE },
203
		{ "run-from-build-dir", 0, 0, G_OPTION_ARG_NONE, &global_opt.run_from_build_dir, "Run from build directory", NULL },
204
		{ "print-config", 0, 0, G_OPTION_ARG_NONE, &global_opt.print_config, N_("Print NetworkManager configuration and exit"), NULL },
205 206 207
		{NULL}
	};

208
	if (!nm_main_utils_early_setup ("NetworkManager",
209 210
	                                argc,
	                                argv,
211
	                                options,
212 213
	                                (void (*)(gpointer, GOptionContext *)) nm_config_cmd_line_options_add_to_entries,
	                                config_cli,
214
	                                _("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.")))
215
		exit (1);
Dan Williams's avatar
Dan Williams committed
216

217
	global_opt.pidfile = global_opt.pidfile ?: g_strdup(NM_DEFAULT_PID_FILE);
218 219 220 221 222 223 224 225 226
}

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

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

243 244 245 246
	_nm_utils_is_manager_process = TRUE;

	main_loop = g_main_loop_new (NULL, FALSE);

247 248 249 250 251
	/* 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));

252 253
	do_early_setup (&argc, &argv, config_cli);

254 255
	if (global_opt.g_fatal_warnings)
		_set_g_fatal_warnings ();
256

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

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

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

270 271
	nm_main_utils_ensure_root ();

272 273
	nm_main_utils_ensure_not_running_pidfile (global_opt.pidfile);

274
	nm_main_utils_ensure_statedir ();
275 276
	nm_main_utils_ensure_rundir ();

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

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

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

		g_free (path);
	}

300 301 302 303 304 305 306 307 308 309
	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);
	}

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

320
	_init_nm_debug (config);
321

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

335
	if (global_opt.become_daemon && !nm_config_get_is_debug (config)) {
336
		if (daemon (0, 0) < 0) {
337
			int saved_errno;
338

339
			saved_errno = errno;
340
			fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
341 342
			         g_strerror (saved_errno),
			         saved_errno);
343
			exit (1);
344
		}
345
		wrote_pidfile = nm_main_utils_write_pidfile (global_opt.pidfile);
346 347
	}

348
	/* Set up unix signal handling - before creating threads, but after daemonizing! */
349
	nm_main_utils_setup_signals (main_loop);
350

351 352 353 354 355 356 357 358 359
	{
		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);
		nm_logging_syslog_openlog (v, nm_config_get_is_debug (config));
	}
360

361 362
	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");
363

364
	nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
365
	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", NULL);
366

367 368 369 370
	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);
	}
371 372 373 374 375 376 377 378
	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);
	}
379

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

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

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

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

396 397 398 399
	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));
400

401 402
	if (!nm_dbus_manager_acquire_bus (nm_dbus_manager_get ()))
		goto done_no_manager;
403

404 405 406 407
	manager = nm_manager_setup ();
	nm_dbus_manager_start (nm_dbus_manager_get(),
	                       nm_manager_dbus_set_property_handle,
	                       manager);
408

409 410
	nm_dispatcher_init ();

411
	g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
412

413
	if (!nm_manager_start (manager, &error)) {
414 415 416
		nm_log_err (LOGD_CORE, "failed to initialize: %s", error->message);
		goto done;
	}
417

418 419
	nm_platform_process_events (NM_PLATFORM_GET);

420 421 422 423 424 425 426 427 428 429 430
	/* 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");
431
	nm_platform_link_set_up (NM_PLATFORM_GET, 1, NULL);
432

Dan Williams's avatar
Dan Williams committed
433 434
	success = TRUE;

435 436 437
	if (configure_and_quit == FALSE) {
		sd_id = nm_sd_event_attach_default ();

438
		g_main_loop_run (main_loop);
439
	}
440

441
done:
442 443 444 445 446

	/* 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. */
447
	nm_manager_write_device_state (manager);
448

449
	nm_manager_stop (manager);
450

451
	nm_config_state_set (config, TRUE, TRUE);
452

453 454
	nm_dns_manager_stop (nm_dns_manager_get ());

455
done_no_manager:
456 457
	if (global_opt.pidfile && wrote_pidfile)
		unlink (global_opt.pidfile);
458

459
	nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
460 461 462

	nm_clear_g_source (&sd_id);

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