main.c 14.2 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 - 2012 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 "dns/nm-dns-manager.h"
53
#include "systemd/nm-sd.h"
Dan Williams's avatar
Dan Williams committed
54

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

59 60
#define NM_DEFAULT_PID_FILE          NMRUNDIR "/NetworkManager.pid"
#define NM_DEFAULT_SYSTEM_STATE_FILE NMSTATEDIR "/NetworkManager.state"
61

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

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

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

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

90
static void
91
_init_nm_debug (NMConfig *config)
92
{
93
	gs_free char *debug = NULL;
94
	const guint D_RLIMIT_CORE = 1;
95
	const guint D_FATAL_WARNINGS = 2;
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 (NM_FLAGS_HAS (flags, D_RLIMIT_CORE)) {
112 113 114 115 116 117 118 119 120
		/* 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);
	}
121 122
	if (NM_FLAGS_HAS (flags, D_FATAL_WARNINGS))
		_set_g_fatal_warnings ();
123 124
}

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

144
	nm_log_info (LOGD_CORE, "reload configuration (signal %s)...", strsignal (signal));
145

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

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

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

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

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

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

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

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

228
	nm_g_type_init ();
229

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

236 237 238 239 240 241 242
	_nm_utils_is_manager_process = TRUE;

	main_loop = g_main_loop_new (NULL, FALSE);

	config_cli = nm_config_cmd_line_options_new ();
	do_early_setup (&argc, &argv, config_cli);

243 244
	if (global_opt.g_fatal_warnings)
		_set_g_fatal_warnings ();
245

246
	if (global_opt.show_version) {
247 248 249 250
		fprintf (stdout, NM_DIST_VERSION "\n");
		exit (0);
	}

251 252 253 254 255 256 257 258
	if (global_opt.print_config) {
		int result;

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

259 260
	nm_main_utils_ensure_root ();

261 262
	nm_main_utils_ensure_not_running_pidfile (global_opt.pidfile);

263
	nm_main_utils_ensure_statedir ();
264 265
	nm_main_utils_ensure_rundir ();

266 267
	/* When running from the build directory, determine our build directory
	 * base and set helper paths in the build tree */
268
	if (global_opt.run_from_build_dir) {
269
		char *path, *slash;
270
		int g;
271 272 273 274 275

		/* exe is <basedir>/src/.libs/lt-NetworkManager, so chop off
		 * the last three components */
		path = realpath ("/proc/self/exe", NULL);
		g_assert (path != NULL);
276
		for (g = 0; g < 3; ++g) {
277 278 279 280 281 282 283
			slash = strrchr (path, '/');
			g_assert (slash != NULL);
			*slash = '\0';
		}

		/* don't free these strings, we need them for the entire
		 * process lifetime */
284
		nm_dhcp_helper_path = g_strdup_printf ("%s/src/dhcp/nm-dhcp-helper", path);
285 286 287 288

		g_free (path);
	}

289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	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);
	}

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

314
	_init_nm_debug (config);
315

316 317 318
	/* Initialize logging from config file *only* if not explicitly
	 * specified by commandline.
	 */
319
	if (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL) {
320 321
		if (!nm_logging_setup (nm_config_get_log_level (config),
		                       nm_config_get_log_domains (config),
322
		                       &bad_domains,
323
		                       &error)) {
324
			fprintf (stderr, _("Error in configuration file: %s.\n"),
325 326
			         error->message);
			exit (1);
327 328 329 330 331
		} 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);
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
	nm_logging_syslog_openlog (nm_config_get_is_debug (config)
	                           ? "debug"
	                           : 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));
357

358 359
	nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting...");

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
	/* the first access to State causes the file to be read (and possibly print a warning) */
	nm_config_state_get (config);
365

366
	nm_log_dbg (LOGD_CORE, "WEXT support is %s",
367 368 369 370 371 372
#if HAVE_WEXT
	             "enabled"
#else
	             "disabled"
#endif
	             );
373

374 375 376 377
	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));
378

379
	nm_manager_setup ();
380

381
	if (!nm_bus_manager_get_connection (nm_bus_manager_get ())) {
382 383 384
		nm_log_warn (LOGD_CORE, "Failed to connect to D-Bus; only private bus is available");
	} else {
		/* Start our DBus service */
385
		if (!nm_bus_manager_start_service (nm_bus_manager_get ())) {
386 387 388 389 390
			nm_log_err (LOGD_CORE, "failed to start the dbus service.");
			goto done;
		}
	}

Pavel Šimerda's avatar
Pavel Šimerda committed
391 392 393
	/* Set up platform interaction layer */
	nm_linux_platform_setup ();

394
	NM_UTILS_KEEP_ALIVE (config, NM_PLATFORM_GET, "NMConfig-depends-on-NMPlatform");
395

396 397
	nm_dispatcher_init ();

398
	g_signal_connect (nm_manager_get (), NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
399

400
	if (!nm_manager_start (nm_manager_get (), &error)) {
401 402 403
		nm_log_err (LOGD_CORE, "failed to initialize: %s", error->message);
		goto done;
	}
404

405 406 407 408 409 410 411 412 413 414 415
	/* 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");
416
	nm_platform_link_set_up (NM_PLATFORM_GET, 1, NULL);
417

Dan Williams's avatar
Dan Williams committed
418 419
	success = TRUE;

420 421 422
	if (configure_and_quit == FALSE) {
		sd_id = nm_sd_event_attach_default ();

423
		g_main_loop_run (main_loop);
424
	}
425

426
done:
427 428 429 430 431 432 433

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

434 435
	nm_exported_object_class_set_quitting ();

436
	nm_manager_stop (nm_manager_get ());
437

438
	nm_config_state_set (config, TRUE, TRUE);
439

440 441
	nm_dns_manager_stop (nm_dns_manager_get ());

442 443
	if (global_opt.pidfile && wrote_pidfile)
		unlink (global_opt.pidfile);
444

445
	nm_log_info (LOGD_CORE, "exiting (%s)", success ? "success" : "error");
446 447 448

	nm_clear_g_source (&sd_id);

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