main.c 14.7 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-bus-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-exported-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 218 219 220 221 222 223 224 225 226
	global_opt.pidfile = global_opt.pidfile ? global_opt.pidfile : g_strdup (NM_DEFAULT_PID_FILE);
}

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

235
	/* Known to cause a possible deadlock upon GDBus initialization:
236 237 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);
	g_type_ensure (NM_TYPE_BUS_MANAGER);

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 308 309 310 311 312
	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);
	} else if (bad_domains) {
		fprintf (stderr,
		         _("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
		         bad_domains);
		g_clear_pointer (&bad_domains, g_free);
	}

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

323
	_init_nm_debug (config);
324

325 326 327
	/* Initialize logging from config file *only* if not explicitly
	 * specified by commandline.
	 */
328
	if (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL) {
329 330
		if (!nm_logging_setup (nm_config_get_log_level (config),
		                       nm_config_get_log_domains (config),
331
		                       &bad_domains,
332
		                       &error)) {
333
			fprintf (stderr, _("Error in configuration file: %s.\n"),
334 335
			         error->message);
			exit (1);
336 337 338 339 340
		} else if (bad_domains) {
			fprintf (stderr,
			         _("Ignoring unrecognized log domain(s) '%s' from config files.\n"),
			         bad_domains);
			g_clear_pointer (&bad_domains, g_free);
341
		}
342 343
	}

344
	if (global_opt.become_daemon && !nm_config_get_is_debug (config)) {
345
		if (daemon (0, 0) < 0) {
346
			int saved_errno;
347

348
			saved_errno = errno;
349
			fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
350 351
			         g_strerror (saved_errno),
			         saved_errno);
352
			exit (1);
353
		}
354
		wrote_pidfile = nm_main_utils_write_pidfile (global_opt.pidfile);
355 356
	}

357
	/* Set up unix signal handling - before creating threads, but after daemonizing! */
358
	nm_main_utils_setup_signals (main_loop);
359

360 361 362 363 364 365 366 367 368
	{
		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));
	}
369

370 371
	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");
372

373
	nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
374
	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", NULL);
375

376 377
	/* the first access to State causes the file to be read (and possibly print a warning) */
	nm_config_state_get (config);
378

379
	nm_log_dbg (LOGD_CORE, "WEXT support is %s",
380 381 382 383 384 385
#if HAVE_WEXT
	             "enabled"
#else
	             "disabled"
#endif
	             );
386

387 388 389 390 391
	/* Set up platform interaction layer */
	nm_linux_platform_setup ();

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

392 393 394 395
	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));
396

397
	nm_manager_setup ();
398

399
	if (!nm_bus_manager_get_connection (nm_bus_manager_get ())) {
400 401 402
		nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available");
	} else {
		/* Start our DBus service */
403
		if (!nm_bus_manager_start_service (nm_bus_manager_get ())) {
404 405 406 407 408
			nm_log_err (LOGD_CORE, "failed to start the dbus service.");
			goto done;
		}
	}

409 410 411
#if WITH_CONCHECK
	NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity");
#endif
412

413 414
	nm_dispatcher_init ();

415
	g_signal_connect (nm_manager_get (), NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
416

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

422 423
	nm_platform_process_events (NM_PLATFORM_GET);

424 425 426 427 428 429 430 431 432 433 434
	/* 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");
435
	nm_platform_link_set_up (NM_PLATFORM_GET, 1, NULL);
436

Dan Williams's avatar
Dan Williams committed
437 438
	success = TRUE;

439 440 441
	if (configure_and_quit == FALSE) {
		sd_id = nm_sd_event_attach_default ();

442
		g_main_loop_run (main_loop);
443
	}
444

445
done:
446 447 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. */
	nm_manager_write_device_state (nm_manager_get ());

453 454
	nm_exported_object_class_set_quitting ();

455
	nm_manager_stop (nm_manager_get ());
456

457
	nm_config_state_set (config, TRUE, TRUE);
458

459 460
	nm_dns_manager_stop (nm_dns_manager_get ());

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

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

	nm_clear_g_source (&sd_id);

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