diff options
| author | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2021-02-11 18:11:46 +0100 |
| commit | 80ec1decc49c72efec2a8b87c06245c92c0ab807 (patch) | |
| tree | e3b229aa94e8dcf0590f2317664176e7b8f7607b /src/core/main.c | |
| parent | 65f86e8f56267192d42f2b629fc6b0c99fb9cd0c (diff) | |
New upstream version 1.29.90 upstream/1.29.90
Diffstat (limited to 'src/core/main.c')
| -rw-r--r-- | src/core/main.c | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 00000000..9b79bcbb --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,546 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2004 - 2017 Red Hat, Inc. + * Copyright (C) 2005 - 2008 Novell, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include <getopt.h> +#include <locale.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/resource.h> + +#include "main-utils.h" +#include "nm-dbus-interface.h" +#include "NetworkManagerUtils.h" +#include "nm-manager.h" +#include "platform/nm-linux-platform.h" +#include "nm-dbus-manager.h" +#include "devices/nm-device.h" +#include "dhcp/nm-dhcp-manager.h" +#include "nm-config.h" +#include "nm-session-monitor.h" +#include "nm-dispatcher.h" +#include "settings/nm-settings.h" +#include "nm-auth-manager.h" +#include "nm-core-internal.h" +#include "nm-dbus-object.h" +#include "nm-connectivity.h" +#include "dns/nm-dns-manager.h" +#include "systemd/nm-sd.h" +#include "nm-netns.h" + +#if !defined(NM_DIST_VERSION) + #define NM_DIST_VERSION VERSION +#endif + +#define NM_DEFAULT_PID_FILE NMRUNDIR "/NetworkManager.pid" + +#define CONFIG_ATOMIC_SECTION_PREFIXES ((char **) NULL) + +static GMainLoop *main_loop = NULL; +static gboolean configure_and_quit = FALSE; + +static struct { + gboolean show_version; + gboolean print_config; + 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, +}; + +static void +_set_g_fatal_warnings(void) +{ + 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); +} + +static void +_init_nm_debug(NMConfig *config) +{ + gs_free char *debug = NULL; + enum { + D_RLIMIT_CORE = (1 << 0), + D_FATAL_WARNINGS = (1 << 1), + }; + GDebugKey keys[] = { + {"RLIMIT_CORE", D_RLIMIT_CORE}, + {"fatal-warnings", D_FATAL_WARNINGS}, + }; + guint flags; + const char *env = getenv("NM_DEBUG"); + + debug = nm_config_data_get_value(nm_config_get_data_orig(config), + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG, + NM_CONFIG_GET_VALUE_NONE); + + flags = nm_utils_parse_debug_string(env, keys, G_N_ELEMENTS(keys)); + flags |= nm_utils_parse_debug_string(debug, keys, G_N_ELEMENTS(keys)); + +#if !defined(__SANITIZE_ADDRESS__) + if (NM_FLAGS_HAS(flags, D_RLIMIT_CORE)) { + /* 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); + } +#endif + + if (NM_FLAGS_HAS(flags, D_FATAL_WARNINGS)) + _set_g_fatal_warnings(); +} + +void +nm_main_config_reload(int signal) +{ + 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(); + } + + nm_log_info(LOGD_CORE, "reload configuration (signal %s)...", strsignal(signal)); + + /* 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. */ + nm_config_reload(nm_config_get(), reload_flags, TRUE); +} + +static void +manager_configure_quit(NMManager *manager, gpointer user_data) +{ + nm_log_info(LOGD_CORE, "quitting now that startup is complete"); + g_main_loop_quit(main_loop); + configure_and_quit = TRUE; +} + +static int +print_config(NMConfigCmdLineOptions *config_cli) +{ + gs_unref_object NMConfig *config = NULL; + gs_free_error GError *error = NULL; + NMConfigData * config_data; + const char *const * warnings; + + nm_logging_setup("OFF", "ALL", NULL, NULL); + + config = nm_config_new(config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error); + if (config == NULL) { + fprintf(stderr, _("Failed to read configuration: %s\n"), error->message); + 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, "", "", nm_config_get_no_auto_default_file(config), stdout); + + warnings = nm_config_get_warnings(config); + if (warnings && warnings[0]) + fprintf(stdout, "\n"); + for (; warnings && warnings[0]; warnings++) + fprintf(stdout, "# WARNING: %s\n", warnings[0]); + + return 0; +} + +static void +do_early_setup(int *argc, char **argv[], NMConfigCmdLineOptions *config_cli) +{ + GOptionEntry options[] = {{"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, + N_("Log domains separated by ',': any combination of [%s]"), + "PLATFORM,RFKILL,WIFI"}, + {"g-fatal-warnings", + 0, + 0, + G_OPTION_ARG_NONE, + &global_opt.g_fatal_warnings, + N_("Make all warnings fatal"), + NULL}, + {"pid-file", + 'p', + 0, + G_OPTION_ARG_FILENAME, + &global_opt.pidfile, + N_("Specify the location of a PID file"), + NM_DEFAULT_PID_FILE}, + {"run-from-build-dir", + 0, + 0, + G_OPTION_ARG_NONE, + &global_opt.run_from_build_dir, + "Run from build directory", + NULL}, + {"print-config", + 0, + 0, + G_OPTION_ARG_NONE, + &global_opt.print_config, + N_("Print NetworkManager configuration and exit"), + NULL}, + {NULL}}; + + if (!nm_main_utils_early_setup( + "NetworkManager", + argc, + argv, + options, + (void (*)(gpointer, GOptionContext *)) nm_config_cmd_line_options_add_to_entries, + config_cli, + _("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."))) + exit(1); + + global_opt.pidfile = global_opt.pidfile ?: g_strdup(NM_DEFAULT_PID_FILE); +} + +static gboolean +_dbus_manager_init(NMConfig *config) +{ + NMDBusManager * busmgr; + NMConfigConfigureAndQuitType c_a_q_type; + + busmgr = nm_dbus_manager_get(); + + c_a_q_type = nm_config_get_configure_and_quit(config); + + if (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_DISABLED) + return nm_dbus_manager_acquire_bus(busmgr, TRUE); + + if (c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_ENABLED) { + /* 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. + * + * But we still create a nm_dbus_manager_get_dbus_connection() D-Bus connection + * so that we can talk to other services like firewalld. */ + return nm_dbus_manager_acquire_bus(busmgr, FALSE); + } + + nm_assert(c_a_q_type == NM_CONFIG_CONFIGURE_AND_QUIT_INITRD); + /* in initrd we don't have D-Bus at all. Don't even try to get the G_BUS_TYPE_SYSTEM + * connection. And of course don't claim the D-Bus name. */ + return TRUE; +} + +/* + * main + * + */ +int +main(int argc, char *argv[]) +{ + gboolean success = FALSE; + NMManager * manager = NULL; + NMConfig * config; + gs_free_error GError * error = NULL; + gboolean wrote_pidfile = FALSE; + char * bad_domains = NULL; + NMConfigCmdLineOptions *config_cli; + guint sd_id = 0; + GError * error_invalid_logging_config = NULL; + const char *const * warnings; + int errsv; + + /* Known to cause a possible deadlock upon GDBus initialization: + * 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_DBUS_MANAGER); + + _nm_utils_is_manager_process = TRUE; + + main_loop = g_main_loop_new(NULL, FALSE); + + /* 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)); + + do_early_setup(&argc, &argv, config_cli); + + if (global_opt.g_fatal_warnings) + _set_g_fatal_warnings(); + + if (global_opt.show_version) { + fprintf(stdout, NM_DIST_VERSION "\n"); + exit(0); + } + + if (global_opt.print_config) { + int result; + + result = print_config(config_cli); + nm_config_cmd_line_options_free(config_cli); + exit(result); + } + + nm_main_utils_ensure_root(); + + nm_main_utils_ensure_not_running_pidfile(global_opt.pidfile); + + nm_main_utils_ensure_statedir(); + nm_main_utils_ensure_rundir(); + + /* When running from the build directory, determine our build directory + * base and set helper paths in the build tree */ + if (global_opt.run_from_build_dir) { + char *path, *slash; + int g; + + /* exe is <basedir>/src/.libs/lt-NetworkManager, so chop off + * the last three components */ + path = realpath("/proc/self/exe", NULL); + g_assert(path != NULL); + for (g = 0; g < 3; ++g) { + slash = strrchr(path, '/'); + g_assert(slash != NULL); + *slash = '\0'; + } + + /* don't free these strings, we need them for the entire + * process lifetime */ + nm_dhcp_helper_path = g_strdup_printf("%s/src/dhcp/nm-dhcp-helper", path); + + g_free(path); + } + + 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); + } + + /* Read the config file and CLI overrides */ + config = nm_config_setup(config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error); + nm_config_cmd_line_options_free(config_cli); + config_cli = NULL; + if (config == NULL) { + fprintf(stderr, _("Failed to read configuration: %s\n"), error->message); + exit(1); + } + + _init_nm_debug(config); + + /* Initialize logging from config file *only* if not explicitly + * specified by commandline. + */ + if (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL) { + if (!nm_logging_setup(nm_config_get_log_level(config), + nm_config_get_log_domains(config), + &bad_domains, + &error_invalid_logging_config)) { + /* ignore error, and print the failure reason below. + * Likewise, print about bad_domains below. */ + } + } + + if (global_opt.become_daemon && !nm_config_get_is_debug(config)) { + if (daemon(0, 0) < 0) { + errsv = errno; + fprintf(stderr, + _("Could not daemonize: %s [error %u]\n"), + nm_strerror_native(errsv), + errsv); + exit(1); + } + wrote_pidfile = nm_main_utils_write_pidfile(global_opt.pidfile); + } + + /* Set up unix signal handling - before creating threads, but after daemonizing! */ + nm_main_utils_setup_signals(main_loop); + + { + 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_init(v, nm_config_get_is_debug(config)); + } + + nm_log_info(LOGD_CORE, + "NetworkManager (version " NM_DIST_VERSION ") is starting... (%s%s)", + nm_config_get_first_start(config) ? "for the first time" : "after a restart", + NM_MORE_ASSERTS != 0 ? ", asserts:" G_STRINGIFY(NM_MORE_ASSERTS) : ""); + + nm_log_info(LOGD_CORE, + "Read config: %s", + nm_config_data_get_config_description(nm_config_get_data(config))); + nm_config_data_log(nm_config_get_data(config), + "CONFIG: ", + " ", + nm_config_get_no_auto_default_file(config), + NULL); + + 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); + } + 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); + } + + warnings = nm_config_get_warnings(config); + for (; warnings && *warnings; warnings++) + nm_log_warn(LOGD_CORE, "config: %s", *warnings); + nm_config_clear_warnings(config); + + /* the first access to State causes the file to be read (and possibly print a warning) */ + nm_config_state_get(config); + + nm_log_dbg(LOGD_CORE, + "WEXT support is %s", +#if HAVE_WEXT + "enabled" +#else + "disabled" +#endif + ); + + if (!_dbus_manager_init(config)) + goto done_no_manager; + + nm_linux_platform_setup(); + + NM_UTILS_KEEP_ALIVE(config, nm_netns_get(), "NMConfig-depends-on-NMNetns"); + + nm_auth_manager_setup(nm_config_data_get_main_auth_polkit(nm_config_get_data_orig(config))); + + manager = nm_manager_setup(); + + nm_dbus_manager_start(nm_dbus_manager_get(), nm_manager_dbus_set_property_handle, manager); + + g_signal_connect(manager, + NM_MANAGER_CONFIGURE_QUIT, + G_CALLBACK(manager_configure_quit), + config); + + if (!nm_manager_start(manager, &error)) { + nm_log_err(LOGD_CORE, "failed to initialize: %s", error->message); + goto done; + } + + nm_platform_process_events(NM_PLATFORM_GET); + + /* 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"); + nm_platform_link_set_up(NM_PLATFORM_GET, 1, NULL); + + success = TRUE; + + if (configure_and_quit == FALSE) { + sd_id = nm_sd_event_attach_default(); + + g_main_loop_run(main_loop); + } + +done: + + /* 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_all(manager); + + nm_manager_stop(manager); + + nm_config_state_set(config, TRUE, TRUE); + + nm_dns_manager_stop(nm_dns_manager_get()); + + nm_settings_kf_db_write(NM_SETTINGS_GET); + +done_no_manager: + if (global_opt.pidfile && wrote_pidfile) + unlink(global_opt.pidfile); + + nm_log_info(LOGD_CORE, "exiting (%s)", success ? "success" : "error"); + + nm_clear_g_source(&sd_id); + + exit(success ? 0 : 1); +} |