main.c 14.8 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
	const guint D_RLIMIT_CORE = 1;
97
	const guint D_FATAL_WARNINGS = 2;
98 99
	GDebugKey keys[] = {
		{ "RLIMIT_CORE", D_RLIMIT_CORE },
100
		{ "fatal-warnings", D_FATAL_WARNINGS },
101
	};
102
	guint flags;
103
	const char *env = getenv ("NM_DEBUG");
104 105 106 107 108

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

110 111
	flags  = nm_utils_parse_debug_string (env, keys, G_N_ELEMENTS (keys));
	flags |= nm_utils_parse_debug_string (debug, keys, G_N_ELEMENTS (keys));
112

113
	if (NM_FLAGS_HAS (flags, D_RLIMIT_CORE)) {
114 115 116 117 118 119 120 121 122
		/* 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);
	}
123 124
	if (NM_FLAGS_HAS (flags, D_FATAL_WARNINGS))
		_set_g_fatal_warnings ();
125 126
}

127
void
128
nm_main_config_reload (int signal)
129
{
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	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 ();
	}

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

148 149 150 151 152 153
	/* 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. */
154
	nm_config_reload (nm_config_get (), reload_flags);
155 156
}

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

165 166 167 168 169 170 171 172 173
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);

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

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

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

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

/*
 * main
 *
 */
int
main (int argc, char *argv[])
{
222
	gboolean success = FALSE;
223 224 225 226 227
	NMConfig *config;
	GError *error = NULL;
	gboolean wrote_pidfile = FALSE;
	char *bad_domains = NULL;
	NMConfigCmdLineOptions *config_cli;
228
	guint sd_id = 0;
229

230
	nm_g_type_init ();
231

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

238 239 240 241
	_nm_utils_is_manager_process = TRUE;

	main_loop = g_main_loop_new (NULL, FALSE);

242 243 244 245 246
	/* 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));

247 248
	do_early_setup (&argc, &argv, config_cli);

249 250
	if (global_opt.g_fatal_warnings)
		_set_g_fatal_warnings ();
251

252
	if (global_opt.show_version) {
253 254 255 256
		fprintf (stdout, NM_DIST_VERSION "\n");
		exit (0);
	}

257 258 259 260 261 262 263 264
	if (global_opt.print_config) {
		int result;

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

265 266
	nm_main_utils_ensure_root ();

267 268
	nm_main_utils_ensure_not_running_pidfile (global_opt.pidfile);

269
	nm_main_utils_ensure_statedir ();
270 271
	nm_main_utils_ensure_rundir ();

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

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

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

		g_free (path);
	}

295 296 297 298 299 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);
	} 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);
	}

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)) {
330
			fprintf (stderr, _("Error in configuration file: %s.\n"),
331 332
			         error->message);
			exit (1);
333 334 335 336 337
		} 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);
338
		}
339 340
	}

341
	if (global_opt.become_daemon && !nm_config_get_is_debug (config)) {
342
		if (daemon (0, 0) < 0) {
343
			int saved_errno;
344

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

354
	/* Set up unix signal handling - before creating threads, but after daemonizing! */
355
	nm_main_utils_setup_signals (main_loop);
356

357 358 359 360 361
	nm_logging_syslog_openlog (nm_config_data_get_value_cached (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_config_get_is_debug (config));
362

363 364
	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");
365

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

369 370
	/* the first access to State causes the file to be read (and possibly print a warning) */
	nm_config_state_get (config);
371

372
	nm_log_dbg (LOGD_CORE, "WEXT support is %s",
373 374 375 376 377 378
#if HAVE_WEXT
	             "enabled"
#else
	             "disabled"
#endif
	             );
379

380 381 382 383 384
	/* Set up platform interaction layer */
	nm_linux_platform_setup ();

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

385 386 387 388
	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));
389

390
	nm_manager_setup ();
391

392
	if (!nm_bus_manager_get_connection (nm_bus_manager_get ())) {
393 394 395
		nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available");
	} else {
		/* Start our DBus service */
396
		if (!nm_bus_manager_start_service (nm_bus_manager_get ())) {
397 398 399 400 401
			nm_log_err (LOGD_CORE, "failed to start the dbus service.");
			goto done;
		}
	}

402 403 404
#if WITH_CONCHECK
	NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity");
#endif
405

406 407
	nm_dispatcher_init ();

408
	g_signal_connect (nm_manager_get (), NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
409

410
	if (!nm_manager_start (nm_manager_get (), &error)) {
411 412 413
		nm_log_err (LOGD_CORE, "failed to initialize: %s", error->message);
		goto done;
	}
414

415 416
	nm_platform_process_events (NM_PLATFORM_GET);

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

Dan Williams's avatar
Dan Williams committed
430 431
	success = TRUE;

432 433 434
	if (configure_and_quit == FALSE) {
		sd_id = nm_sd_event_attach_default ();

435
		g_main_loop_run (main_loop);
436
	}
437

438
done:
439 440 441 442 443 444 445

	/* 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 ());

446 447
	nm_exported_object_class_set_quitting ();

448
	nm_manager_stop (nm_manager_get ());
449

450
	nm_config_state_set (config, TRUE, TRUE);
451

452 453
	nm_dns_manager_stop (nm_dns_manager_get ());

454 455
	if (global_opt.pidfile && wrote_pidfile)
		unlink (global_opt.pidfile);
456

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

	nm_clear_g_source (&sd_id);

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