// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "services/service_manager/embedder/main.h" #include "base/allocator/buildflags.h" #include "base/at_exit.h" #include "base/base_switches.h" #include "base/command_line.h" #include "base/debug/activity_tracker.h" #include "base/debug/debugger.h" #include "base/debug/stack_trace.h" #include "base/i18n/icu_util.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/optional.h" #include "base/process/launch.h" #include "base/process/memory.h" #include "base/process/process.h" #include "base/run_loop.h" #include "base/task_scheduler/task_scheduler.h" #include "base/threading/thread.h" #include "base/trace_event/trace_config.h" #include "base/trace_event/trace_log.h" #include "build/build_config.h" #include "components/tracing/common/trace_to_console.h" #include "components/tracing/common/tracing_switches.h" #include "mojo/edk/embedder/configuration.h" #include "mojo/edk/embedder/embedder.h" #include "mojo/edk/embedder/scoped_ipc_support.h" #include "services/service_manager/embedder/main_delegate.h" #include "services/service_manager/embedder/process_type.h" #include "services/service_manager/embedder/set_process_title.h" #include "services/service_manager/embedder/shared_file_util.h" #include "services/service_manager/embedder/switches.h" #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_context.h" #include "services/service_manager/public/cpp/standalone_service/standalone_service.h" #include "services/service_manager/runner/common/client_util.h" #include "services/service_manager/runner/common/switches.h" #include "services/service_manager/runner/init.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" #if defined(OS_WIN) #include #include "base/win/process_startup_helper.h" #include "ui/base/win/atl_module.h" #endif #if defined(OS_POSIX) && !defined(OS_ANDROID) #include #include #include "base/file_descriptor_store.h" #include "base/posix/global_descriptors.h" #endif #if defined(OS_MACOSX) #include "base/mac/scoped_nsautorelease_pool.h" #include "services/service_manager/embedder/mac_init.h" #if BUILDFLAG(USE_ALLOCATOR_SHIM) #include "base/allocator/allocator_shim.h" #endif #endif // defined(OS_MACOSX) namespace service_manager { namespace { // Maximum message size allowed to be read from a Mojo message pipe in any // service manager embedder process. constexpr size_t kMaximumMojoMessageSize = 128 * 1024 * 1024; class ServiceProcessLauncherDelegateImpl : public service_manager::ServiceProcessLauncherDelegate { public: explicit ServiceProcessLauncherDelegateImpl(MainDelegate* main_delegate) : main_delegate_(main_delegate) {} ~ServiceProcessLauncherDelegateImpl() override {} private: // service_manager::ServiceProcessLauncherDelegate: void AdjustCommandLineArgumentsForTarget( const service_manager::Identity& target, base::CommandLine* command_line) override { if (main_delegate_->ShouldLaunchAsServiceProcess(target)) { command_line->AppendSwitchASCII(switches::kProcessType, switches::kProcessTypeService); #if defined(OS_WIN) command_line->AppendArg(switches::kDefaultServicePrefetchArgument); #endif } main_delegate_->AdjustServiceProcessCommandLine(target, command_line); } MainDelegate* const main_delegate_; DISALLOW_COPY_AND_ASSIGN(ServiceProcessLauncherDelegateImpl); }; #if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) // Setup signal-handling state: resanitize most signals, ignore SIGPIPE. void SetupSignalHandlers() { if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableInProcessStackTraces)) { // Don't interfere with sanitizer signal handlers. return; } // Sanitise our signal handling state. Signals that were ignored by our // parent will also be ignored by us. We also inherit our parent's sigmask. sigset_t empty_signal_set; CHECK_EQ(0, sigemptyset(&empty_signal_set)); CHECK_EQ(0, sigprocmask(SIG_SETMASK, &empty_signal_set, NULL)); struct sigaction sigact; memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = SIG_DFL; static const int signals_to_reset[] = { SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGALRM, SIGTERM, SIGCHLD, SIGBUS, SIGTRAP}; // SIGPIPE is set below. for (unsigned i = 0; i < arraysize(signals_to_reset); i++) { CHECK_EQ(0, sigaction(signals_to_reset[i], &sigact, NULL)); } // Always ignore SIGPIPE. We check the return value of write(). CHECK_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN)); } void PopulateFDsFromCommandLine() { const std::string& shared_file_param = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kSharedFiles); if (shared_file_param.empty()) return; base::Optional> shared_file_descriptors = service_manager::ParseSharedFileSwitchValue(shared_file_param); if (!shared_file_descriptors) return; for (const auto& descriptor : *shared_file_descriptors) { base::MemoryMappedFile::Region region; const std::string& key = descriptor.second; base::ScopedFD fd = base::GlobalDescriptors::GetInstance()->TakeFD( descriptor.first, ®ion); base::FileDescriptorStore::GetInstance().Set(key, std::move(fd), region); } } #endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) void CommonSubprocessInit() { #if defined(OS_WIN) // HACK: Let Windows know that we have started. This is needed to suppress // the IDC_APPSTARTING cursor from being displayed for a prolonged period // while a subprocess is starting. PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0); MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); #endif #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) // Various things break when you're using a locale where the decimal // separator isn't a period. See e.g. bugs 22782 and 39964. For // all processes except the browser process (where we call system // APIs that may rely on the correct locale for formatting numbers // when presenting them to the user), reset the locale for numeric // formatting. // Note that this is not correct for plugin processes -- they can // surface UI -- but it's likely they get this wrong too so why not. setlocale(LC_NUMERIC, "C"); #endif #if !defined(OFFICIAL_BUILD) && defined(OS_WIN) base::RouteStdioToConsole(false); LoadLibraryA("dbghelp.dll"); #endif } void NonEmbedderProcessInit() { service_manager::InitializeLogging(); #if !defined(OFFICIAL_BUILD) // Initialize stack dumping before initializing sandbox to make sure symbol // names in all loaded libraries will be cached. // NOTE: On Chrome OS, crash reporting for the root process and non-browser // service processes is handled by the OS-level crash_reporter. if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableInProcessStackTraces)) { base::debug::EnableInProcessStackDumping(); } #endif base::TaskScheduler::CreateAndStartWithDefaultParams("ServiceManagerProcess"); } void WaitForDebuggerIfNecessary() { if (!ServiceManagerIsRemote()) return; const auto& command_line = *base::CommandLine::ForCurrentProcess(); const std::string service_name = command_line.GetSwitchValueASCII(switches::kServiceName); if (service_name != command_line.GetSwitchValueASCII(::switches::kWaitForDebugger)) { return; } // Include the pid as logging may not have been initialized yet (the pid // printed out by logging is wrong). LOG(WARNING) << "waiting for debugger to attach for service " << service_name << " pid=" << base::Process::Current().Pid(); base::debug::WaitForDebugger(120, true); } int RunServiceManager(MainDelegate* delegate) { NonEmbedderProcessInit(); base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); base::Thread ipc_thread("IPC thread"); ipc_thread.StartWithOptions( base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); mojo::edk::ScopedIPCSupport ipc_support( ipc_thread.task_runner(), mojo::edk::ScopedIPCSupport::ShutdownPolicy::FAST); ServiceProcessLauncherDelegateImpl service_process_launcher_delegate( delegate); service_manager::BackgroundServiceManager background_service_manager( &service_process_launcher_delegate, delegate->CreateServiceCatalog()); base::RunLoop run_loop; delegate->OnServiceManagerInitialized(run_loop.QuitClosure(), &background_service_manager); run_loop.Run(); ipc_thread.Stop(); base::TaskScheduler::GetInstance()->Shutdown(); return 0; } void InitializeResources() { const std::string locale = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( ::switches::kLang); // This loads the embedder's common resources (e.g. chrome_100_percent.pak for // Chrome.) ui::ResourceBundle::InitSharedInstanceWithLocale( locale, nullptr, ui::ResourceBundle::LOAD_COMMON_RESOURCES); } int RunService(MainDelegate* delegate) { NonEmbedderProcessInit(); WaitForDebuggerIfNecessary(); InitializeResources(); int exit_code = 0; RunStandaloneService(base::Bind( [](MainDelegate* delegate, int* exit_code, mojom::ServiceRequest request) { // TODO(rockot): Make the default MessageLoop type overridable for // services. This is TYPE_UI because at least one service (the "ui" // service) needs it to be. base::MessageLoop message_loop(base::MessageLoop::TYPE_UI); base::RunLoop run_loop; std::string service_name = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kServiceName); if (service_name.empty()) { LOG(ERROR) << "Service process requires --service-name"; *exit_code = 1; return; } std::unique_ptr service = delegate->CreateEmbeddedService(service_name); if (!service) { LOG(ERROR) << "Failed to start embedded service: " << service_name; *exit_code = 1; return; } ServiceContext context(std::move(service), std::move(request)); context.SetQuitClosure(run_loop.QuitClosure()); run_loop.Run(); }, delegate, &exit_code)); return exit_code; } } // namespace MainParams::MainParams(MainDelegate* delegate) : delegate(delegate) {} MainParams::~MainParams() {} int Main(const MainParams& params) { MainDelegate* delegate = params.delegate; DCHECK(delegate); #if defined(OS_MACOSX) && BUILDFLAG(USE_ALLOCATOR_SHIM) base::allocator::InitializeAllocatorShim(); #endif #if !defined(TOOLKIT_QT) base::EnableTerminationOnOutOfMemory(); #endif #if defined(OS_LINUX) // The various desktop environments set this environment variable that allows // the dbus client library to connect directly to the bus. When this variable // is not set (test environments like xvfb-run), the dbus client library will // fall back to auto-launch mode. Auto-launch is dangerous as it can cause // hangs (crbug.com/715658) . This one line disables the dbus auto-launch, // by clobbering the DBUS_SESSION_BUS_ADDRESS env variable if not already set. // The old auto-launch behavior, if needed, can be restored by setting // DBUS_SESSION_BUS_ADDRESS="autolaunch:" before launching chrome. const int kNoOverrideIfAlreadySet = 0; setenv("DBUS_SESSION_BUS_ADDRESS", "disabled:", kNoOverrideIfAlreadySet); #endif #if defined(OS_WIN) base::win::RegisterInvalidParamHandler(); ui::win::CreateATLModuleIfNeeded(); #endif // defined(OS_WIN) #if !defined(OS_ANDROID) // On Android, the command line is initialized when library is loaded. int argc = 0; const char** argv = nullptr; #if defined(OS_POSIX) // argc/argv are ignored on Windows; see command_line.h for details. argc = params.argc; argv = params.argv; #endif base::CommandLine::Init(argc, argv); #if defined(OS_POSIX) && !defined(OS_FUCHSIA) PopulateFDsFromCommandLine(); #endif base::EnableTerminationOnHeapCorruption(); SetProcessTitleFromCommandLine(argv); #endif // !defined(OS_ANDROID) // On Android setlocale() is not supported, and we don't override the signal // handlers so we can get a stack trace when crashing. #if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA) // Set C library locale to make sure CommandLine can parse argument values in // the correct encoding. setlocale(LC_ALL, ""); SetupSignalHandlers(); #endif const auto& command_line = *base::CommandLine::ForCurrentProcess(); #if defined(OS_WIN) base::win::SetupCRT(command_line); #endif MainDelegate::InitializeParams init_params; #if defined(OS_MACOSX) // We need this pool for all the objects created before we get to the event // loop, but we don't want to leave them hanging around until the app quits. // Each "main" needs to flush this pool right before it goes into its main // event loop to get rid of the cruft. std::unique_ptr autorelease_pool = std::make_unique(); init_params.autorelease_pool = autorelease_pool.get(); InitializeMac(); #endif mojo::edk::Configuration mojo_config; ProcessType process_type = delegate->OverrideProcessType(); if (process_type == ProcessType::kDefault) { std::string type_switch = command_line.GetSwitchValueASCII(switches::kProcessType); if (type_switch == switches::kProcessTypeServiceManager) { mojo_config.is_broker_process = true; process_type = ProcessType::kServiceManager; } else if (type_switch == switches::kProcessTypeService) { process_type = ProcessType::kService; } else { process_type = ProcessType::kEmbedder; } } mojo_config.max_message_num_bytes = kMaximumMojoMessageSize; delegate->OverrideMojoConfiguration(&mojo_config); mojo::edk::Init(mojo_config); ui::RegisterPathProvider(); base::debug::GlobalActivityTracker* tracker = base::debug::GlobalActivityTracker::Get(); int exit_code = delegate->Initialize(init_params); if (exit_code >= 0) { if (tracker) { tracker->SetProcessPhase( base::debug::GlobalActivityTracker::PROCESS_LAUNCH_FAILED); tracker->process_data().SetInt("exit-code", exit_code); } return exit_code; } #if defined(OS_WIN) // Route stdio to parent console (if any) or create one. if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableLogging)) { base::RouteStdioToConsole(true); } #endif if (base::CommandLine::ForCurrentProcess()->HasSwitch( ::switches::kTraceToConsole)) { base::trace_event::TraceConfig trace_config = tracing::GetConfigForTraceToConsole(); base::trace_event::TraceLog::GetInstance()->SetEnabled( trace_config, base::trace_event::TraceLog::RECORDING_MODE); } switch (process_type) { case ProcessType::kDefault: NOTREACHED(); break; case ProcessType::kServiceManager: exit_code = RunServiceManager(delegate); break; case ProcessType::kService: CommonSubprocessInit(); exit_code = RunService(delegate); break; case ProcessType::kEmbedder: if (delegate->IsEmbedderSubprocess()) CommonSubprocessInit(); exit_code = delegate->RunEmbedderProcess(); break; } if (tracker) { if (exit_code == 0) { tracker->SetProcessPhaseIfEnabled( base::debug::GlobalActivityTracker::PROCESS_EXITED_CLEANLY); } else { tracker->SetProcessPhaseIfEnabled( base::debug::GlobalActivityTracker::PROCESS_EXITED_WITH_CODE); tracker->process_data().SetInt("exit-code", exit_code); } } #if defined(OS_MACOSX) autorelease_pool.reset(); #endif if (process_type == ProcessType::kEmbedder) delegate->ShutDownEmbedderProcess(); return exit_code; } } // namespace service_manager