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-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
#define NM_DEFAULT_PID_FILE          NMRUNDIR "/NetworkManager.pid"
62

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

242 243 244 245
	_nm_utils_is_manager_process = TRUE;

	main_loop = g_main_loop_new (NULL, FALSE);

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

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

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

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

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

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

269 270
	nm_main_utils_ensure_root ();

271 272
	nm_main_utils_ensure_not_running_pidfile (global_opt.pidfile);

273
	nm_main_utils_ensure_statedir ();
274 275
	nm_main_utils_ensure_rundir ();

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

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

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

		g_free (path);
	}

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

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

319
	_init_nm_debug (config);
320

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

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

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

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

350 351 352 353 354 355 356 357 358
	{
		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));
	}
359

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

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

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

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

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

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

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

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

400 401 402 403 404 405 406
	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;
	}
407

408 409 410 411
	manager = nm_manager_setup ();
	nm_dbus_manager_start (nm_dbus_manager_get(),
	                       nm_manager_dbus_set_property_handle,
	                       manager);
412

413 414
	nm_dispatcher_init ();

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

417
	if (!nm_manager_start (manager, &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

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

453
	nm_manager_stop (manager);
454

455
	nm_config_state_set (config, TRUE, TRUE);
456

457 458
	nm_dns_manager_stop (nm_dns_manager_get ());

459
done_no_manager:
460 461
	if (global_opt.pidfile && wrote_pidfile)
		unlink (global_opt.pidfile);
462

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

	nm_clear_g_source (&sd_id);

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