| // Copyright (c) 2012 The Chromium OS 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 "login_manager/session_manager_impl.h" |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <sys/socket.h> |
| |
| #include <algorithm> |
| #include <locale> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include <base/base64.h> |
| #include <base/bind.h> |
| #include <base/files/file_util.h> |
| #include <base/memory/ptr_util.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/message_loop/message_loop.h> |
| #include <base/rand_util.h> |
| #include <base/run_loop.h> |
| #include <base/strings/string_tokenizer.h> |
| #include <base/strings/string_util.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/time/time.h> |
| #include <brillo/cryptohome.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <crypto/scoped_nss_types.h> |
| #include <dbus/message.h> |
| #include <dbus/object_proxy.h> |
| #include <install_attributes/libinstallattributes.h> |
| |
| #include "bindings/chrome_device_policy.pb.h" |
| #include "bindings/device_management_backend.pb.h" |
| #include "login_manager/crossystem.h" |
| #include "login_manager/dbus_signal_emitter.h" |
| #include "login_manager/dbus_util.h" |
| #include "login_manager/device_local_account_policy_service.h" |
| #include "login_manager/device_policy_service.h" |
| #include "login_manager/key_generator.h" |
| #include "login_manager/login_metrics.h" |
| #include "login_manager/nss_util.h" |
| #include "login_manager/policy_key.h" |
| #include "login_manager/policy_service.h" |
| #include "login_manager/process_manager_service_interface.h" |
| #include "login_manager/regen_mitigator.h" |
| #include "login_manager/system_utils.h" |
| #include "login_manager/upstart_signal_emitter.h" |
| #include "login_manager/user_policy_service_factory.h" |
| #include "login_manager/vpd_process.h" |
| |
| using base::FilePath; |
| using brillo::cryptohome::home::GetRootPath; |
| using brillo::cryptohome::home::GetUserPath; |
| using brillo::cryptohome::home::SanitizeUserName; |
| using brillo::cryptohome::home::kGuestUserName; |
| |
| namespace login_manager { // NOLINT |
| |
| const char SessionManagerImpl::kDemoUser[] = "demouser@"; |
| |
| const char SessionManagerImpl::kStarted[] = "started"; |
| const char SessionManagerImpl::kStopping[] = "stopping"; |
| const char SessionManagerImpl::kStopped[] = "stopped"; |
| |
| const char SessionManagerImpl::kLoggedInFlag[] = |
| "/run/session_manager/logged_in"; |
| const char SessionManagerImpl::kResetFile[] = |
| "/mnt/stateful_partition/factory_install_reset"; |
| |
| const char SessionManagerImpl::kStartUserSessionSignal[] = "start-user-session"; |
| |
| const char SessionManagerImpl::kArcContainerName[] = "android"; |
| const char SessionManagerImpl::kArcStartSignal[] = "start-arc-instance"; |
| const char SessionManagerImpl::kArcStopSignal[] = "stop-arc-instance"; |
| const char SessionManagerImpl::kArcNetworkStartSignal[] = "start-arc-network"; |
| const char SessionManagerImpl::kArcNetworkStopSignal[] = "stop-arc-network"; |
| const char SessionManagerImpl::kArcBootedSignal[] = "arc-booted"; |
| const char SessionManagerImpl::kArcRemoveOldDataSignal[] = |
| "remove-old-arc-data"; |
| |
| namespace { |
| // Constants used in email validation. |
| const char kEmailSeparator = '@'; |
| const char kEmailLegalCharacters[] = |
| "abcdefghijklmnopqrstuvwxyz" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| ".@1234567890!#$%&'*+-/=?^_`{|}~"; |
| |
| // Should match chromium AccountId::kKeyGaiaIdPrefix . |
| const char kGaiaIdKeyPrefix[] = "g-"; |
| // Should match chromium AccountId::kKeyAdIdPrefix . |
| const char kActiveDirectoryPrefix[] = "a-"; |
| const char kAccountIdKeyLegalCharacters[] = |
| "-0123456789" |
| "abcdefghijklmnopqrstuvwxyz" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| |
| // The flag to pass to chrome to open a named socket for testing. |
| const char kTestingChannelFlag[] = "--testing-channel=NamedTestingInterface:"; |
| |
| // Device-local account state directory. |
| const base::FilePath::CharType kDeviceLocalAccountStateDir[] = |
| FILE_PATH_LITERAL("/var/lib/device_local_accounts"); |
| |
| #if USE_CHEETS |
| // To launch ARC, certain amount of free disk space is needed. |
| // Path and the amount for the check. |
| constexpr base::FilePath::CharType kArcDiskCheckPath[] = "/home"; |
| constexpr int64_t kArcCriticalDiskFreeBytes = 64 << 20; // 64MB |
| constexpr size_t kArcContainerInstanceIdLength = 16; |
| |
| // Name of android-data directory. |
| const base::FilePath::CharType kAndroidDataDirName[] = |
| FILE_PATH_LITERAL("android-data"); |
| |
| // Name of android-data-old directory which RemoveArcDataInternal uses. |
| const base::FilePath::CharType kAndroidDataOldDirName[] = |
| FILE_PATH_LITERAL("android-data-old"); |
| |
| // To set the CPU limits of the Android container. |
| const char kCpuSharesFile[] = |
| "/sys/fs/cgroup/cpu/session_manager_containers/cpu.shares"; |
| const unsigned int kCpuSharesForeground = 1024; |
| const unsigned int kCpuSharesBackground = 64; |
| #endif |
| |
| // SystemUtils::EnsureJobExit() DCHECKs if the timeout is zero, so this is the |
| // minimum amount of time we must wait before killing the containers. |
| constexpr int kContainerTimeoutSec = 1; |
| |
| // The interval used to periodically check if time sync was done by tlsdated. |
| constexpr int kSystemClockLastSyncInfoRetryMs = 1000; |
| |
| bool IsIncognitoAccountId(const std::string& account_id) { |
| const std::string lower_case_id(base::ToLowerASCII(account_id)); |
| return (lower_case_id == kGuestUserName) || |
| (lower_case_id == SessionManagerImpl::kDemoUser); |
| } |
| |
| } // namespace |
| |
| SessionManagerImpl::Error::Error() : set_(false) {} |
| |
| SessionManagerImpl::Error::Error(const std::string& name, |
| const std::string& message) |
| : name_(name), message_(message), set_(true) {} |
| |
| SessionManagerImpl::Error::~Error() {} |
| |
| void SessionManagerImpl::Error::Set(const std::string& name, |
| const std::string& message) { |
| name_ = name; |
| message_ = message; |
| set_ = true; |
| } |
| |
| struct SessionManagerImpl::UserSession { |
| public: |
| UserSession(const std::string& username, |
| const std::string& userhash, |
| bool is_incognito, |
| crypto::ScopedPK11Slot slot, |
| std::unique_ptr<PolicyService> policy_service) |
| : username(username), |
| userhash(userhash), |
| is_incognito(is_incognito), |
| slot(std::move(slot)), |
| policy_service(std::move(policy_service)) {} |
| ~UserSession() {} |
| |
| const std::string username; |
| const std::string userhash; |
| const bool is_incognito; |
| crypto::ScopedPK11Slot slot; |
| std::unique_ptr<PolicyService> policy_service; |
| }; |
| |
| SessionManagerImpl::SessionManagerImpl( |
| std::unique_ptr<InitDaemonController> init_controller, |
| DBusSignalEmitterInterface* dbus_emitter, |
| base::Closure lock_screen_closure, |
| base::Closure restart_device_closure, |
| base::Closure start_arc_instance_closure, |
| base::Closure stop_arc_instance_closure, |
| KeyGenerator* key_gen, |
| ServerBackedStateKeyGenerator* state_key_generator, |
| ProcessManagerServiceInterface* manager, |
| LoginMetrics* metrics, |
| NssUtil* nss, |
| SystemUtils* utils, |
| Crossystem* crossystem, |
| VpdProcess* vpd_process, |
| PolicyKey* owner_key, |
| ContainerManagerInterface* android_container, |
| InstallAttributesReader* install_attributes_reader, |
| dbus::ObjectProxy* system_clock_proxy) |
| : session_started_(false), |
| session_stopping_(false), |
| screen_locked_(false), |
| supervised_user_creation_ongoing_(false), |
| system_clock_synchronized_(false), |
| init_controller_(std::move(init_controller)), |
| lock_screen_closure_(lock_screen_closure), |
| restart_device_closure_(restart_device_closure), |
| start_arc_instance_closure_(start_arc_instance_closure), |
| stop_arc_instance_closure_(stop_arc_instance_closure), |
| dbus_emitter_(dbus_emitter), |
| key_gen_(key_gen), |
| state_key_generator_(state_key_generator), |
| manager_(manager), |
| login_metrics_(metrics), |
| nss_(nss), |
| system_(utils), |
| crossystem_(crossystem), |
| vpd_process_(vpd_process), |
| owner_key_(owner_key), |
| android_container_(android_container), |
| install_attributes_reader_(install_attributes_reader), |
| system_clock_proxy_(system_clock_proxy), |
| mitigator_(key_gen), |
| weak_ptr_factory_(this) {} |
| |
| SessionManagerImpl::~SessionManagerImpl() { |
| device_policy_->set_delegate(NULL); // Could use WeakPtr instead? |
| } |
| |
| // static |
| bool SessionManagerImpl::ValidateAccountIdKey(const std::string& account_id) { |
| if (account_id.find_first_not_of(kAccountIdKeyLegalCharacters) != |
| std::string::npos) |
| return false; |
| |
| return base::StartsWith(account_id, kGaiaIdKeyPrefix, |
| base::CompareCase::SENSITIVE) || |
| base::StartsWith(account_id, kActiveDirectoryPrefix, |
| base::CompareCase::SENSITIVE); |
| } |
| |
| // static |
| bool SessionManagerImpl::ValidateEmail(const std::string& email_address) { |
| if (email_address.find_first_not_of(kEmailLegalCharacters) != |
| std::string::npos) { |
| return false; |
| } |
| |
| size_t at = email_address.find(kEmailSeparator); |
| // it has NO @. |
| if (at == std::string::npos) |
| return false; |
| |
| // it has more than one @. |
| if (email_address.find(kEmailSeparator, at + 1) != std::string::npos) |
| return false; |
| |
| return true; |
| } |
| |
| #if USE_CHEETS |
| // static |
| base::FilePath SessionManagerImpl::GetAndroidDataDirForUser( |
| const std::string& normalized_account_id) { |
| return GetRootPath(normalized_account_id).Append(kAndroidDataDirName); |
| } |
| |
| // static |
| base::FilePath SessionManagerImpl::GetAndroidDataOldDirForUser( |
| const std::string& normalized_account_id) { |
| return GetRootPath(normalized_account_id).Append(kAndroidDataOldDirName); |
| } |
| #endif // USE_CHEETS |
| |
| void SessionManagerImpl::SetPolicyServicesForTest( |
| std::unique_ptr<DevicePolicyService> device_policy, |
| std::unique_ptr<UserPolicyServiceFactory> user_policy_factory, |
| std::unique_ptr<DeviceLocalAccountPolicyService> |
| device_local_account_policy) { |
| device_policy_ = std::move(device_policy); |
| user_policy_factory_ = std::move(user_policy_factory); |
| device_local_account_policy_ = std::move(device_local_account_policy); |
| } |
| |
| void SessionManagerImpl::AnnounceSessionStoppingIfNeeded() { |
| if (session_started_) { |
| session_stopping_ = true; |
| DLOG(INFO) << "emitting D-Bus signal SessionStateChanged:" << kStopping; |
| dbus_emitter_->EmitSignalWithString(kSessionStateChangedSignal, kStopping); |
| } |
| } |
| |
| void SessionManagerImpl::AnnounceSessionStopped() { |
| session_stopping_ = session_started_ = false; |
| DLOG(INFO) << "emitting D-Bus signal SessionStateChanged:" << kStopped; |
| dbus_emitter_->EmitSignalWithString(kSessionStateChangedSignal, kStopped); |
| } |
| |
| bool SessionManagerImpl::ShouldEndSession() { |
| return screen_locked_ || supervised_user_creation_ongoing_; |
| } |
| |
| bool SessionManagerImpl::Initialize() { |
| key_gen_->set_delegate(this); |
| |
| system_clock_proxy_->WaitForServiceToBeAvailable( |
| base::Bind(&SessionManagerImpl::OnSystemClockServiceAvailable, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| if (!device_policy_) { |
| device_policy_.reset( |
| DevicePolicyService::Create(login_metrics_, owner_key_, &mitigator_, |
| nss_, crossystem_, vpd_process_)); |
| device_policy_->set_delegate(this); |
| |
| user_policy_factory_.reset( |
| new UserPolicyServiceFactory(getuid(), nss_, system_)); |
| device_local_account_policy_.reset(new DeviceLocalAccountPolicyService( |
| base::FilePath(kDeviceLocalAccountStateDir), owner_key_)); |
| |
| if (!device_policy_->Initialize()) { |
| return false; |
| } |
| device_local_account_policy_->UpdateDeviceSettings( |
| device_policy_->GetSettings()); |
| if (device_policy_->MayUpdateSystemSettings()) { |
| device_policy_->UpdateSystemSettings(PolicyService::Completion()); |
| } |
| } |
| return true; |
| } |
| |
| void SessionManagerImpl::Finalize() { |
| device_policy_->PersistPolicy(PolicyService::Completion()); |
| for (UserSessionMap::const_iterator it = user_sessions_.begin(); |
| it != user_sessions_.end(); ++it) { |
| if (it->second) |
| it->second->policy_service->PersistPolicy(PolicyService::Completion()); |
| } |
| // We want to stop any running containers. Containers are per-session and |
| // cannot persist across sessions. |
| android_container_->RequestJobExit(); |
| android_container_->EnsureJobExit( |
| base::TimeDelta::FromSeconds(kContainerTimeoutSec)); |
| } |
| |
| void SessionManagerImpl::EmitLoginPromptVisible() { |
| login_metrics_->RecordStats("login-prompt-visible"); |
| dbus_emitter_->EmitSignal(kLoginPromptVisibleSignal); |
| init_controller_->TriggerImpulse("login-prompt-visible", {}, |
| InitDaemonController::TriggerMode::ASYNC); |
| } |
| |
| bool SessionManagerImpl::EnableChromeTesting( |
| brillo::ErrorPtr* error, |
| bool in_force_relaunch, |
| const std::vector<std::string>& in_extra_arguments, |
| std::string* out_filepath) { |
| // Check to see if we already have Chrome testing enabled. |
| bool already_enabled = !chrome_testing_path_.empty(); |
| |
| if (!already_enabled) { |
| base::FilePath temp_file_path; // So we don't clobber chrome_testing_path_; |
| if (!system_->GetUniqueFilenameInWriteOnlyTempDir(&temp_file_path)) { |
| *error = CreateError( |
| dbus_error::kTestingChannelError, |
| "Could not create testing channel filename."); |
| return false; |
| } |
| chrome_testing_path_ = temp_file_path; |
| } |
| |
| if (!already_enabled || in_force_relaunch) { |
| // Delete testing channel file if it already exists. |
| system_->RemoveFile(chrome_testing_path_); |
| |
| // Add testing channel argument to extra arguments. |
| std::string testing_argument = kTestingChannelFlag; |
| testing_argument.append(chrome_testing_path_.value()); |
| std::vector<std::string> extra_args = in_extra_arguments; |
| extra_args.push_back(testing_argument); |
| manager_->RestartBrowserWithArgs(extra_args, true); |
| } |
| *out_filepath = chrome_testing_path_.value(); |
| return true; |
| } |
| |
| bool SessionManagerImpl::StartSession(brillo::ErrorPtr* error, |
| const std::string& in_account_id, |
| const std::string& in_unique_identifier) { |
| std::string actual_account_id; |
| if (!NormalizeAccountId(in_account_id, &actual_account_id, error)) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| // Check if this user already started a session. |
| if (user_sessions_.count(actual_account_id) > 0) { |
| constexpr char kMessage[] = "Provided user id already started a session."; |
| LOG(ERROR) << kMessage; |
| *error = CreateError(dbus_error::kSessionExists, kMessage); |
| return false; |
| } |
| |
| // Create a UserSession object for this user. |
| const bool is_incognito = IsIncognitoAccountId(actual_account_id); |
| auto user_session = |
| CreateUserSession(actual_account_id, is_incognito, error); |
| if (!user_session) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| // Check whether the current user is the owner, and if so make sure she is |
| // whitelisted and has an owner key. |
| bool user_is_owner = false; |
| if (!device_policy_->CheckAndHandleOwnerLogin( |
| user_session->username, user_session->slot.get(), &user_is_owner, |
| error)) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| // If all previous sessions were incognito (or no previous sessions exist). |
| bool is_first_real_user = AllSessionsAreIncognito() && !is_incognito; |
| |
| // Send each user login event to UMA (right before we start session |
| // since the metrics library does not log events in guest mode). |
| const DevModeState dev_mode_state = system_->GetDevModeState(); |
| if (dev_mode_state != DevModeState::DEV_MODE_UNKNOWN) { |
| login_metrics_->SendLoginUserType( |
| dev_mode_state != DevModeState::DEV_MODE_OFF, is_incognito, |
| user_is_owner); |
| } |
| |
| init_controller_->TriggerImpulse(kStartUserSessionSignal, |
| {"CHROMEOS_USER=" + actual_account_id}, |
| InitDaemonController::TriggerMode::ASYNC); |
| LOG(INFO) << "Starting user session"; |
| manager_->SetBrowserSessionForUser(actual_account_id, user_session->userhash); |
| session_started_ = true; |
| user_sessions_[actual_account_id] = std::move(user_session); |
| DLOG(INFO) << "emitting D-Bus signal SessionStateChanged:" << kStarted; |
| dbus_emitter_->EmitSignalWithString(kSessionStateChangedSignal, kStarted); |
| |
| // Active Directory managed devices are not expected to have a policy key. |
| // Don't create one for them. |
| const bool is_active_directory = |
| install_attributes_reader_->GetAttribute( |
| InstallAttributesReader::kAttrMode) == |
| InstallAttributesReader::kDeviceModeEnterpriseAD; |
| if (device_policy_->KeyMissing() && !is_active_directory && |
| !device_policy_->Mitigating() && is_first_real_user) { |
| // This is the first sign-in on this unmanaged device. Take ownership. |
| key_gen_->Start(actual_account_id); |
| } |
| |
| // Record that a login has successfully completed on this boot. |
| system_->AtomicFileWrite(base::FilePath(kLoggedInFlag), "1"); |
| return true; |
| } |
| |
| void SessionManagerImpl::StopSession(const std::string& in_unique_identifier) { |
| LOG(INFO) << "Stopping all sessions"; |
| // Most calls to StopSession() will log the reason for the call. |
| // If you don't see a log message saying the reason for the call, it is |
| // likely a DBUS message. See session_manager_dbus_adaptor.cc for that call. |
| manager_->ScheduleShutdown(); |
| // TODO(cmasone): re-enable these when we try to enable logout without exiting |
| // the session manager |
| // browser_.job->StopSession(); |
| // user_policy_.reset(); |
| // session_started_ = false; |
| } |
| |
| void SessionManagerImpl::StorePolicy( |
| const uint8_t* policy_blob, |
| size_t policy_blob_len, |
| SignatureCheck signature_check, |
| const PolicyService::Completion& completion) { |
| // Skipping of signature checks is gated on enterprise_ad device mode being |
| // locked into install attributes. |
| if (signature_check == SignatureCheck::kDisabled) { |
| const std::string& mode = install_attributes_reader_->GetAttribute( |
| InstallAttributesReader::kAttrMode); |
| if (mode != InstallAttributesReader::kDeviceModeEnterpriseAD) { |
| constexpr char kMessage[] = |
| "Device mode doesn't permit unsigned policy!"; |
| LOG(ERROR) << kMessage; |
| completion.Run(CreateError( |
| dbus_error::kPolicySignatureRequired, kMessage)); |
| return; |
| } |
| } |
| |
| int flags = PolicyService::KEY_ROTATE; |
| if (!session_started_) |
| flags |= PolicyService::KEY_INSTALL_NEW | PolicyService::KEY_CLOBBER; |
| device_policy_->Store(policy_blob, policy_blob_len, flags, signature_check, |
| completion); |
| } |
| |
| void SessionManagerImpl::RetrievePolicy(std::vector<uint8_t>* policy_data, |
| Error* error) { |
| if (!device_policy_->Retrieve(policy_data)) { |
| static const char msg[] = "Failed to retrieve policy data."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kSigEncodeFail, msg); |
| return; |
| } |
| } |
| |
| void SessionManagerImpl::StorePolicyForUser( |
| const std::string& account_id, |
| const uint8_t* policy_blob, |
| size_t policy_blob_len, |
| SignatureCheck signature_check, |
| const PolicyService::Completion& completion) { |
| // Skipping of signature checks is gated on enterprise_ad device mode being |
| // locked into install attributes. |
| if (signature_check == SignatureCheck::kDisabled) { |
| const std::string& mode = install_attributes_reader_->GetAttribute( |
| InstallAttributesReader::kAttrMode); |
| if (mode != InstallAttributesReader::kDeviceModeEnterpriseAD) { |
| constexpr char kMessage[] = |
| "Device mode doesn't permit unsigned policy!"; |
| LOG(ERROR) << kMessage; |
| completion.Run(CreateError( |
| dbus_error::kPolicySignatureRequired, kMessage)); |
| return; |
| } |
| } |
| |
| PolicyService* policy_service = GetPolicyService(account_id); |
| if (!policy_service) { |
| constexpr char kMessage[] = |
| "Cannot store user policy before session is started."; |
| LOG(ERROR) << kMessage; |
| completion.Run(CreateError(dbus_error::kSessionDoesNotExist, kMessage)); |
| return; |
| } |
| |
| policy_service->Store( |
| policy_blob, policy_blob_len, |
| PolicyService::KEY_INSTALL_NEW | PolicyService::KEY_ROTATE, |
| signature_check, completion); |
| } |
| |
| void SessionManagerImpl::RetrievePolicyForUser( |
| const std::string& account_id, |
| std::vector<uint8_t>* policy_data, |
| Error* error) { |
| PolicyService* policy_service = GetPolicyService(account_id); |
| if (!policy_service) { |
| static const char msg[] = |
| "Cannot retrieve user policy before session is started."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kSessionDoesNotExist, msg); |
| return; |
| } |
| if (!policy_service->Retrieve(policy_data)) { |
| static const char msg[] = "Failed to retrieve policy data."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kSigEncodeFail, msg); |
| } |
| } |
| |
| void SessionManagerImpl::StoreDeviceLocalAccountPolicy( |
| const std::string& account_id, |
| const uint8_t* policy_blob, |
| size_t policy_blob_len, |
| const PolicyService::Completion& completion) { |
| device_local_account_policy_->Store(account_id, policy_blob, policy_blob_len, |
| completion); |
| } |
| |
| void SessionManagerImpl::RetrieveDeviceLocalAccountPolicy( |
| const std::string& account_id, |
| std::vector<uint8_t>* policy_data, |
| Error* error) { |
| if (!device_local_account_policy_->Retrieve(account_id, policy_data)) { |
| static const char msg[] = "Failed to retrieve policy data."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kSigEncodeFail, msg); |
| return; |
| } |
| } |
| |
| const char* SessionManagerImpl::RetrieveSessionState() { |
| if (!session_started_) |
| return kStopped; |
| else |
| return (session_stopping_ ? kStopping : kStarted); |
| } |
| |
| void SessionManagerImpl::RetrieveActiveSessions( |
| std::map<std::string, std::string>* active_sessions) { |
| for (UserSessionMap::const_iterator it = user_sessions_.begin(); |
| it != user_sessions_.end(); ++it) { |
| if (it->second) { |
| (*active_sessions)[it->second->username] = it->second->userhash; |
| } |
| } |
| } |
| |
| void SessionManagerImpl::HandleSupervisedUserCreationStarting() { |
| supervised_user_creation_ongoing_ = true; |
| } |
| |
| void SessionManagerImpl::HandleSupervisedUserCreationFinished() { |
| supervised_user_creation_ongoing_ = false; |
| } |
| |
| void SessionManagerImpl::LockScreen(Error* error) { |
| if (!session_started_) { |
| static const char msg[] = "Attempt to lock screen outside of user session."; |
| LOG(WARNING) << msg; |
| error->Set(dbus_error::kSessionDoesNotExist, msg); |
| return; |
| } |
| // If all sessions are incognito, then locking is not allowed. |
| if (AllSessionsAreIncognito()) { |
| static const char msg[] = "Attempt to lock screen during Guest session."; |
| LOG(WARNING) << msg; |
| error->Set(dbus_error::kSessionExists, msg); |
| return; |
| } |
| if (!screen_locked_) { |
| screen_locked_ = true; |
| lock_screen_closure_.Run(); |
| } |
| LOG(INFO) << "LockScreen() method called."; |
| } |
| |
| void SessionManagerImpl::HandleLockScreenShown() { |
| LOG(INFO) << "HandleLockScreenShown() method called."; |
| dbus_emitter_->EmitSignal(kScreenIsLockedSignal); |
| } |
| |
| void SessionManagerImpl::HandleLockScreenDismissed() { |
| screen_locked_ = false; |
| LOG(INFO) << "HandleLockScreenDismissed() method called."; |
| dbus_emitter_->EmitSignal(kScreenIsUnlockedSignal); |
| } |
| |
| bool SessionManagerImpl::RestartJob(brillo::ErrorPtr* error, |
| const dbus::FileDescriptor& in_cred_fd, |
| const std::vector<std::string>& in_argv) { |
| struct ucred ucred = {0}; |
| socklen_t len = sizeof(struct ucred); |
| if (!in_cred_fd.is_valid() || |
| getsockopt( |
| in_cred_fd.value(), SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) { |
| PLOG(ERROR) << "Can't get peer creds"; |
| *error = CreateError("GetPeerCredsFailed", strerror(errno)); |
| return false; |
| } |
| |
| if (!manager_->IsBrowser(ucred.pid)) { |
| constexpr char kMessage[] = "Provided pid is unknown."; |
| LOG(ERROR) << kMessage; |
| *error = CreateError(dbus_error::kUnknownPid, kMessage); |
| return false; |
| } |
| |
| // To set "logged-in" state for BWSI mode. |
| if (!StartSession(error, kGuestUserName, "")) { |
| DCHECK(*error); |
| return false; |
| } |
| |
| manager_->RestartBrowserWithArgs(in_argv, false); |
| return true; |
| } |
| |
| bool SessionManagerImpl::StartDeviceWipe(brillo::ErrorPtr* error) { |
| if (system_->Exists(base::FilePath(kLoggedInFlag))) { |
| constexpr char kMessage[] = "A user has already logged in this boot."; |
| LOG(ERROR) << kMessage; |
| *error = CreateError(dbus_error::kSessionExists, kMessage); |
| return false; |
| } |
| InitiateDeviceWipe("session_manager_dbus_request"); |
| return true; |
| } |
| |
| void SessionManagerImpl::SetFlagsForUser( |
| const std::string& account_id, |
| const std::vector<std::string>& session_user_flags) { |
| manager_->SetFlagsForUser(account_id, session_user_flags); |
| } |
| |
| void SessionManagerImpl::RequestServerBackedStateKeys( |
| const ServerBackedStateKeyGenerator::StateKeyCallback& callback) { |
| if (system_clock_synchronized_) { |
| state_key_generator_->RequestStateKeys(callback); |
| } else { |
| pending_state_key_callbacks_.push_back(callback); |
| } |
| } |
| |
| void SessionManagerImpl::OnSystemClockServiceAvailable(bool service_available) { |
| if (!service_available) { |
| LOG(ERROR) << "Failed to listen for tlsdated service start"; |
| return; |
| } |
| |
| GetSystemClockLastSyncInfo(); |
| } |
| |
| void SessionManagerImpl::GetSystemClockLastSyncInfo() { |
| dbus::MethodCall method_call(system_clock::kSystemClockInterface, |
| system_clock::kSystemLastSyncInfo); |
| dbus::MessageWriter writer(&method_call); |
| |
| system_clock_proxy_->CallMethod( |
| &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
| base::Bind(&SessionManagerImpl::OnGotSystemClockLastSyncInfo, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void SessionManagerImpl::OnGotSystemClockLastSyncInfo( |
| dbus::Response* response) { |
| if (!response) { |
| LOG(ERROR) << system_clock::kSystemClockInterface << "." |
| << system_clock::kSystemLastSyncInfo << " request failed."; |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, base::Bind(&SessionManagerImpl::GetSystemClockLastSyncInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(kSystemClockLastSyncInfoRetryMs)); |
| return; |
| } |
| |
| dbus::MessageReader reader(response); |
| bool network_synchronized = false; |
| if (!reader.PopBool(&network_synchronized)) { |
| LOG(ERROR) << system_clock::kSystemClockInterface << "." |
| << system_clock::kSystemLastSyncInfo |
| << " response lacks network-synchronized argument"; |
| return; |
| } |
| |
| if (network_synchronized) { |
| system_clock_synchronized_ = true; |
| for (auto it = pending_state_key_callbacks_.begin(); |
| it != pending_state_key_callbacks_.end(); ++it) { |
| state_key_generator_->RequestStateKeys(*it); |
| } |
| pending_state_key_callbacks_.clear(); |
| } else { |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, base::Bind(&SessionManagerImpl::GetSystemClockLastSyncInfo, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::TimeDelta::FromMilliseconds(kSystemClockLastSyncInfoRetryMs)); |
| } |
| } |
| |
| void SessionManagerImpl::InitMachineInfo(const std::string& data, |
| Error* error) { |
| std::map<std::string, std::string> params; |
| if (!ServerBackedStateKeyGenerator::ParseMachineInfo(data, ¶ms)) |
| error->Set(dbus_error::kInitMachineInfoFail, "Parse failure."); |
| |
| if (!state_key_generator_->InitMachineInfo(params)) |
| error->Set(dbus_error::kInitMachineInfoFail, "Missing parameters."); |
| } |
| |
| void SessionManagerImpl::StartArcInstance( |
| const std::string& account_id, |
| bool skip_boot_completed_broadcast, |
| bool scan_vendor_priv_app, |
| std::string* container_instance_id_out, |
| Error* error) { |
| #if USE_CHEETS |
| arc_start_time_ = base::TimeTicks::Now(); |
| |
| // To boot ARC instance, certain amount of disk space is needed under the |
| // home. We first check it. |
| if (system_->AmountOfFreeDiskSpace(base::FilePath(kArcDiskCheckPath)) < |
| kArcCriticalDiskFreeBytes) { |
| static const char msg[] = "Low free disk under /home"; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kLowFreeDisk, msg); |
| return; |
| } |
| |
| brillo::ErrorPtr error_ptr; |
| std::string actual_account_id; |
| if (!NormalizeAccountId(account_id, &actual_account_id, &error_ptr)) { |
| DCHECK(error_ptr.get()); |
| error->Set(error_ptr->GetCode(), error_ptr->GetMessage()); |
| return; |
| } |
| if (user_sessions_.count(actual_account_id) == 0) { |
| static const char msg[] = "Provided user ID does not have a session."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kSessionDoesNotExist, msg); |
| return; |
| } |
| |
| const base::FilePath android_data_dir = |
| GetAndroidDataDirForUser(actual_account_id); |
| const base::FilePath android_data_old_dir = |
| GetAndroidDataOldDirForUser(actual_account_id); |
| |
| // When GetDevModeState() returns UNKNOWN, assign true to |is_dev_mode|. |
| const bool is_dev_mode = |
| system_->GetDevModeState() != DevModeState::DEV_MODE_OFF; |
| // When GetVmState() returns UNKNOWN, assign false to |is_inside_vm|. |
| const bool is_inside_vm = system_->GetVmState() == VmState::INSIDE_VM; |
| |
| const std::vector<std::string> keyvals = { |
| base::StringPrintf("ANDROID_DATA_DIR=%s", |
| android_data_dir.value().c_str()), |
| base::StringPrintf("ANDROID_DATA_OLD_DIR=%s", |
| android_data_old_dir.value().c_str()), |
| base::StringPrintf("CHROMEOS_USER=%s", actual_account_id.c_str()), |
| base::StringPrintf("CHROMEOS_DEV_MODE=%d", is_dev_mode), |
| base::StringPrintf("CHROMEOS_INSIDE_VM=%d", is_inside_vm), |
| base::StringPrintf("DISABLE_BOOT_COMPLETED_BROADCAST=%d", |
| skip_boot_completed_broadcast), |
| base::StringPrintf("ENABLE_VENDOR_PRIVILEGED=%d", |
| scan_vendor_priv_app), |
| }; |
| |
| if (!init_controller_->TriggerImpulse( |
| kArcStartSignal, keyvals, InitDaemonController::TriggerMode::SYNC)) { |
| static const char msg[] = "Emitting start-arc-instance signal failed."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kEmitFailed, msg); |
| return; |
| } |
| |
| bool started_container = false; |
| const char* dbus_error = nullptr; |
| std::string error_message; |
| // Container instance id needs to be valid ASCII/UTF-8, so encode as base64. |
| std::string container_instance_id = |
| base::RandBytesAsString(kArcContainerInstanceIdLength); |
| base::Base64Encode(container_instance_id, &container_instance_id); |
| if (!StartArcInstanceInternal(container_instance_id, &started_container, |
| &dbus_error, &error_message)) { |
| LOG(ERROR) << error_message; |
| error->Set(dbus_error, error_message); |
| init_controller_->TriggerImpulse(kArcStopSignal, {}, |
| InitDaemonController::TriggerMode::SYNC); |
| if (started_container) { |
| android_container_->RequestJobExit(); |
| android_container_->EnsureJobExit( |
| base::TimeDelta::FromSeconds(kContainerTimeoutSec)); |
| } |
| return; |
| } |
| *container_instance_id_out = std::move(container_instance_id); |
| start_arc_instance_closure_.Run(); |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif // !USE_CHEETS |
| } |
| |
| void SessionManagerImpl::StopArcInstance(Error* error) { |
| #if USE_CHEETS |
| pid_t pid; |
| if (!android_container_->GetContainerPID(&pid)) { |
| static const char msg[] = "Error getting Android container pid."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kContainerShutdownFail, msg); |
| return; |
| } |
| |
| android_container_->RequestJobExit(); |
| android_container_->EnsureJobExit( |
| base::TimeDelta::FromSeconds(kContainerTimeoutSec)); |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif // USE_CHEETS |
| } |
| |
| void SessionManagerImpl::SetArcCpuRestriction( |
| ContainerCpuRestrictionState state, Error* error) { |
| #if USE_CHEETS |
| std::string shares_out; |
| switch (state) { |
| case CONTAINER_CPU_RESTRICTION_FOREGROUND: |
| shares_out = std::to_string(kCpuSharesForeground); |
| break; |
| case CONTAINER_CPU_RESTRICTION_BACKGROUND: |
| shares_out = std::to_string(kCpuSharesBackground); |
| break; |
| default: |
| constexpr char msg[] = "Invalid CPU restriction state specified."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kArcCpuCgroupFail, msg); |
| return; |
| } |
| if (base::WriteFile(base::FilePath(kCpuSharesFile), shares_out.c_str(), |
| shares_out.length()) != shares_out.length()) { |
| constexpr char msg[] = "Error updating Android container's cgroups."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kArcCpuCgroupFail, msg); |
| } |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif |
| } |
| |
| void SessionManagerImpl::EmitArcBooted(const std::string& account_id, |
| Error* error) { |
| #if USE_CHEETS |
| std::vector<std::string> keyvals; |
| if (!account_id.empty()) { |
| brillo::ErrorPtr error_ptr; |
| std::string actual_account_id; |
| if (!NormalizeAccountId(account_id, &actual_account_id, &error_ptr)) { |
| DCHECK(error_ptr.get()); |
| error->Set(error_ptr->GetCode(), error_ptr->GetMessage()); |
| return; |
| } |
| const base::FilePath android_data_old_dir = |
| GetAndroidDataOldDirForUser(actual_account_id); |
| keyvals.emplace_back("ANDROID_DATA_OLD_DIR=" + |
| android_data_old_dir.value()); |
| } |
| |
| if (!init_controller_->TriggerImpulse( |
| kArcBootedSignal, keyvals, InitDaemonController::TriggerMode::SYNC)) { |
| static const char msg[] = "Emitting arc-booted upstart signal failed."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kEmitFailed, msg); |
| } |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif |
| } |
| |
| base::TimeTicks SessionManagerImpl::GetArcStartTime(Error* error) { |
| #if USE_CHEETS |
| if (arc_start_time_.is_null()) |
| error->Set(dbus_error::kNotStarted, "ARC is not started yet."); |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif // !USE_CHEETS |
| return arc_start_time_; |
| } |
| |
| void SessionManagerImpl::StartContainer(const std::string& name, Error* error) { |
| // TODO(dgreid): Add support for other containers. |
| const char msg[] = "Container not found."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kContainerStartupFail, msg); |
| } |
| |
| void SessionManagerImpl::StopContainer(const std::string& name, Error* error) { |
| // TODO(dgreid): Add support for other containers. |
| const char msg[] = "Container not found."; |
| LOG(ERROR) << msg; |
| error->Set(dbus_error::kContainerShutdownFail, msg); |
| } |
| |
| void SessionManagerImpl::RemoveArcData(const std::string& account_id, |
| Error* error) { |
| #if USE_CHEETS |
| pid_t pid = 0; |
| if (android_container_->GetContainerPID(&pid)) { |
| error->Set(dbus_error::kArcInstanceRunning, "ARC is currently running."); |
| return; |
| } |
| |
| brillo::ErrorPtr error_ptr; |
| std::string actual_account_id; |
| if (!NormalizeAccountId(account_id, &actual_account_id, &error_ptr)) { |
| DCHECK(error_ptr.get()); |
| error->Set(error_ptr->GetCode(), error_ptr->GetMessage()); |
| return; |
| } |
| const base::FilePath android_data_dir = |
| GetAndroidDataDirForUser(actual_account_id); |
| const base::FilePath android_data_old_dir = |
| GetAndroidDataOldDirForUser(actual_account_id); |
| |
| if (RemoveArcDataInternal(android_data_dir, android_data_old_dir, error)) |
| return; // all done. |
| |
| PLOG(WARNING) << "Failed to rename " << android_data_dir.value() |
| << "; directly deleting it instead"; |
| // As a last resort, directly delete the directory although it's not always |
| // safe to do. If session_manager is killed or the device is shut down while |
| // doing the removal, the directory will have an unusual set of files which |
| // may confuse ARC and prevent it from booting. |
| system_->RemoveDirTree(android_data_dir); |
| LOG(INFO) << "Finished removing " << android_data_dir.value(); |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif // USE_CHEETS |
| } |
| |
| bool SessionManagerImpl::RemoveArcDataInternal( |
| const base::FilePath& android_data_dir, |
| const base::FilePath& android_data_old_dir, |
| Error* error) { |
| #if USE_CHEETS |
| // It should never happen, but in case |android_data_old_dir| is a file, |
| // remove it. RemoveFile() immediately returns false (i.e. no-op) when |
| // |android_data_old_dir| is a directory. |
| system_->RemoveFile(android_data_old_dir); |
| |
| // Create |android_data_old_dir| if it doesn't exist. |
| if (!system_->DirectoryExists(android_data_old_dir)) { |
| if (!system_->CreateDir(android_data_old_dir)) { |
| PLOG(ERROR) << "Failed to create " << android_data_old_dir.value(); |
| return false; |
| } |
| } |
| |
| if (!system_->DirectoryExists(android_data_dir) && |
| system_->IsDirectoryEmpty(android_data_old_dir)) { |
| return true; // nothing to do. |
| } |
| |
| // Create a random temporary directory in |android_data_old_dir|. |
| // Note: Renaming a directory to an existing empty directory works. |
| base::FilePath target_dir_name; |
| if (!system_->CreateTemporaryDirIn(android_data_old_dir, &target_dir_name)) { |
| LOG(WARNING) << "Failed to create a temporary directory in " |
| << android_data_old_dir.value(); |
| return false; |
| } |
| LOG(INFO) << "Renaming " << android_data_dir.value() << " to " |
| << target_dir_name.value(); |
| |
| // Does the actual renaming here with rename(2). Note that if the process |
| // (or the device itself) is killed / turned off right before the rename(2) |
| // operation, both |android_data_dir| and |android_data_old_dir| will remain |
| // while ARC is disabled in the browser side. In that case, the browser will |
| // call RemoveArcData() later as needed, and both directories will disappear. |
| if (system_->DirectoryExists(android_data_dir)) { |
| if (!system_->RenameDir(android_data_dir, target_dir_name)) { |
| LOG(WARNING) << "Failed to rename " << android_data_dir.value() << " to " |
| << target_dir_name.value(); |
| return false; |
| } |
| } |
| |
| // Ask init to remove all files and directories in |android_data_old_dir|. |
| // Note that the init job never deletes |android_data_old_dir| itself so the |
| // rename() operation above never fails. |
| LOG(INFO) << "Removing contents in " << android_data_old_dir.value(); |
| if (!init_controller_->TriggerImpulse( |
| kArcRemoveOldDataSignal, |
| {"ANDROID_DATA_OLD_DIR=" + android_data_old_dir.value()}, |
| InitDaemonController::TriggerMode::SYNC)) { |
| LOG(ERROR) << "Failed to emit " << kArcRemoveOldDataSignal |
| << " upstart signal"; |
| } |
| #else |
| error->Set(dbus_error::kNotAvailable, "ARC not supported."); |
| #endif // USE_CHEETS |
| return true; |
| } |
| |
| void SessionManagerImpl::OnPolicyPersisted(bool success) { |
| device_local_account_policy_->UpdateDeviceSettings( |
| device_policy_->GetSettings()); |
| dbus_emitter_->EmitSignalWithSuccessFailure(kPropertyChangeCompleteSignal, |
| success); |
| } |
| |
| void SessionManagerImpl::OnKeyPersisted(bool success) { |
| dbus_emitter_->EmitSignalWithSuccessFailure(kOwnerKeySetSignal, success); |
| } |
| |
| void SessionManagerImpl::OnKeyGenerated(const std::string& username, |
| const base::FilePath& temp_key_file) { |
| ImportValidateAndStoreGeneratedKey(username, temp_key_file); |
| } |
| |
| void SessionManagerImpl::ImportValidateAndStoreGeneratedKey( |
| const std::string& username, |
| const base::FilePath& temp_key_file) { |
| DLOG(INFO) << "Processing generated key at " << temp_key_file.value(); |
| std::string key; |
| base::ReadFileToString(temp_key_file, &key); |
| PLOG_IF(WARNING, !base::DeleteFile(temp_key_file, false)) |
| << "Can't delete " << temp_key_file.value(); |
| device_policy_->ValidateAndStoreOwnerKey( |
| username, key, user_sessions_[username]->slot.get()); |
| } |
| |
| void SessionManagerImpl::InitiateDeviceWipe(const std::string& reason) { |
| // The log string must not be confused with other clobbers-state parameters. |
| // Sanitize by replacing all non-alphanumeric characters with underscores and |
| // clamping size to 50 characters. |
| std::string sanitized_reason(reason.substr(0, 50)); |
| std::locale locale("C"); |
| std::replace_if(sanitized_reason.begin(), sanitized_reason.end(), |
| [&locale](const std::string::value_type character) { |
| return !std::isalnum(character, locale); |
| }, |
| '_'); |
| const base::FilePath reset_path(kResetFile); |
| system_->AtomicFileWrite(reset_path, |
| "fast safe keepimg reason=" + sanitized_reason); |
| restart_device_closure_.Run(); |
| } |
| |
| // static |
| bool SessionManagerImpl::NormalizeAccountId(const std::string& account_id, |
| std::string* actual_account_id_out, |
| brillo::ErrorPtr* error_out) { |
| // Validate the |account_id|. |
| if (IsIncognitoAccountId(account_id) || ValidateAccountIdKey(account_id)) { |
| *actual_account_id_out = account_id; |
| return true; |
| } |
| |
| // Support legacy email addresses. |
| // TODO(alemate): remove this after ChromeOS will stop using email as |
| // cryptohome identifier. |
| const std::string& lower_email = base::ToLowerASCII(account_id); |
| if (!ValidateEmail(lower_email)) { |
| constexpr char kMessage[] = |
| "Provided email address is not valid. ASCII only."; |
| LOG(ERROR) << kMessage; |
| *error_out = CreateError(dbus_error::kInvalidAccount, kMessage); |
| return false; |
| } |
| |
| *actual_account_id_out = lower_email; |
| return true; |
| } |
| |
| bool SessionManagerImpl::AllSessionsAreIncognito() { |
| size_t incognito_count = 0; |
| for (UserSessionMap::const_iterator it = user_sessions_.begin(); |
| it != user_sessions_.end(); ++it) { |
| if (it->second) |
| incognito_count += it->second->is_incognito; |
| } |
| return incognito_count == user_sessions_.size(); |
| } |
| |
| std::unique_ptr<SessionManagerImpl::UserSession> |
| SessionManagerImpl::CreateUserSession(const std::string& username, |
| bool is_incognito, |
| brillo::ErrorPtr* error) { |
| std::unique_ptr<PolicyService> user_policy( |
| user_policy_factory_->Create(username)); |
| if (!user_policy) { |
| LOG(ERROR) << "User policy failed to initialize."; |
| *error = CreateError(dbus_error::kPolicyInitFail, "Can't create session."); |
| return nullptr; |
| } |
| |
| crypto::ScopedPK11Slot slot(nss_->OpenUserDB(GetUserPath(username))); |
| if (!slot) { |
| LOG(ERROR) << "Could not open the current user's NSS database."; |
| *error = CreateError(dbus_error::kNoUserNssDb, "Can't create session."); |
| return nullptr; |
| } |
| |
| return base::MakeUnique<UserSession>( |
| username, SanitizeUserName(username), is_incognito, std::move(slot), |
| std::move(user_policy)); |
| } |
| |
| PolicyService* SessionManagerImpl::GetPolicyService(const std::string& user) { |
| UserSessionMap::const_iterator it = user_sessions_.find(user); |
| return it == user_sessions_.end() ? NULL : it->second->policy_service.get(); |
| } |
| |
| bool SessionManagerImpl::StartArcInstanceInternal( |
| const std::string& container_instance_id, |
| bool* started_container_out, |
| const char** dbus_error_out, |
| std::string* error_message_out) { |
| *started_container_out = false; |
| *dbus_error_out = nullptr; |
| error_message_out->clear(); |
| if (!android_container_->StartContainer( |
| base::Bind(&SessionManagerImpl::OnAndroidContainerStopped, |
| weak_ptr_factory_.GetWeakPtr(), container_instance_id))) { |
| *dbus_error_out = dbus_error::kContainerStartupFail; |
| *error_message_out = "Starting Android container failed."; |
| return false; |
| } |
| *started_container_out = true; |
| |
| login_metrics_->StartTrackingArcUseTime(); |
| |
| base::FilePath root_path; |
| pid_t pid = 0; |
| if (!android_container_->GetRootFsPath(&root_path) || |
| !android_container_->GetContainerPID(&pid)) { |
| *dbus_error_out = dbus_error::kContainerStartupFail; |
| *error_message_out = "Getting Android container info failed."; |
| return false; |
| } |
| |
| // Also tell init to configure the network. |
| if (!init_controller_->TriggerImpulse( |
| kArcNetworkStartSignal, |
| {"CONTAINER_NAME=" + std::string(kArcContainerName), |
| "CONTAINER_PATH=" + root_path.value(), |
| "CONTAINER_PID=" + std::to_string(pid)}, |
| InitDaemonController::TriggerMode::SYNC)) { |
| *dbus_error_out = dbus_error::kEmitFailed; |
| *error_message_out = "Emitting start-arc-network signal failed."; |
| return false; |
| } |
| LOG(INFO) << "Started Android container pid " << pid; |
| |
| return true; |
| } |
| |
| void SessionManagerImpl::OnAndroidContainerStopped( |
| const std::string& container_instance_id, pid_t pid, bool clean) { |
| if (clean) { |
| LOG(INFO) << "Android Container with pid " << pid << " stopped"; |
| } else { |
| LOG(ERROR) << "Android Container with pid " << pid << " crashed"; |
| } |
| |
| stop_arc_instance_closure_.Run(); |
| |
| login_metrics_->StopTrackingArcUseTime(); |
| if (!init_controller_->TriggerImpulse( |
| kArcStopSignal, {}, InitDaemonController::TriggerMode::SYNC)) { |
| static const char msg[] = "Emitting stop-arc-instance init signal failed."; |
| LOG(ERROR) << msg; |
| } |
| |
| if (!init_controller_->TriggerImpulse( |
| kArcNetworkStopSignal, {}, InitDaemonController::TriggerMode::SYNC)) { |
| static const char msg[] = "Emitting stop-arc-network init signal failed."; |
| LOG(ERROR) << msg; |
| } |
| |
| dbus_emitter_->EmitSignalWithBoolAndString( |
| kArcInstanceStopped, clean, container_instance_id); |
| } |
| |
| } // namespace login_manager |