blob: 1ff1812ba1654efbfd643c47d5269735e97d18e0 [file] [log] [blame]
// Copyright 2014 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/chrome_setup.h"
#include <sys/stat.h>
#include <unistd.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/memory/scoped_ptr.h>
#include <base/strings/stringprintf.h>
#include <chromeos/ui/chromium_command_builder.h>
#include <chromeos/ui/util.h>
#include <chromeos/ui/x_server_runner.h>
using chromeos::ui::ChromiumCommandBuilder;
using chromeos::ui::XServerRunner;
using chromeos::ui::util::EnsureDirectoryExists;
using chromeos::ui::util::SetPermissions;
namespace login_manager {
namespace {
// Authority file used for running the X server.
const char kXauthPath[] = "/var/run/chromelogin.auth";
// Path to file containing developer-supplied modifications to Chrome's
// environment and command line. Passed to
// ChromiumCommandBuilder::ApplyUserConfig().
const char kChromeDevConfigPath[] = "/etc/chrome_dev.conf";
// Returns a base::FilePath corresponding to the DATA_DIR environment variable.
base::FilePath GetDataDir(ChromiumCommandBuilder* builder) {
return base::FilePath(builder->ReadEnvVar("DATA_DIR"));
}
// Returns a base::FilePath corresponding to the subdirectory of DATA_DIR where
// user data is stored.
base::FilePath GetUserDir(ChromiumCommandBuilder* builder) {
return base::FilePath(GetDataDir(builder).Append("user"));
}
// Called by AddUiFlags() to take a wallpaper flag type ("default" or "guest"
// or "child") and file type (e.g. "child", "default", "oem", "guest") and
// add the corresponding flags to |builder| if the files exist. Returns false
// if the files don't exist.
bool AddWallpaperFlags(ChromiumCommandBuilder* builder,
const std::string& flag_type,
const std::string& file_type) {
const base::FilePath large_path(base::StringPrintf(
"/usr/share/chromeos-assets/wallpaper/%s_large.jpg", file_type.c_str()));
const base::FilePath small_path(base::StringPrintf(
"/usr/share/chromeos-assets/wallpaper/%s_small.jpg", file_type.c_str()));
if (!base::PathExists(large_path) || !base::PathExists(small_path))
return false;
builder->AddArg(base::StringPrintf("--%s-wallpaper-large=%s",
flag_type.c_str(), large_path.value().c_str()));
builder->AddArg(base::StringPrintf("--%s-wallpaper-small=%s",
flag_type.c_str(), small_path.value().c_str()));
return true;
}
// Ensures that necessary directory exist with the correct permissions and sets
// related arguments and environment variables.
void CreateDirectories(ChromiumCommandBuilder* builder) {
const uid_t uid = builder->uid();
const gid_t gid = builder->gid();
const uid_t kRootUid = 0;
const gid_t kRootGid = 0;
const base::FilePath data_dir = GetDataDir(builder);
builder->AddArg("--user-data-dir=" + data_dir.value());
const base::FilePath user_dir = GetUserDir(builder);
CHECK(EnsureDirectoryExists(user_dir, uid, gid, 0755));
// TODO(keescook): Remove Chrome's use of $HOME.
builder->AddEnvVar("HOME", user_dir.value());
// Old builds will have a profile dir that's owned by root; newer ones won't
// have this directory at all.
CHECK(EnsureDirectoryExists(data_dir.Append("Default"), uid, gid, 0755));
// TODO(cmasone,derat): Stop using this directory and delete this code.
const base::FilePath state_dir("/var/run/state");
CHECK(base::DeleteFile(state_dir, true));
CHECK(EnsureDirectoryExists(state_dir, kRootUid, kRootGid, 0710));
// Create a directory where the session manager can store a copy of the user
// policy key, that will be readable by the chrome process as chronos.
const base::FilePath policy_dir("/var/run/user_policy");
CHECK(base::DeleteFile(policy_dir, true));
CHECK(EnsureDirectoryExists(policy_dir, kRootUid, gid, 0710));
// Create a directory where the chrome process can store a reboot request so
// that it persists across browser crashes but is always removed on reboot.
CHECK(EnsureDirectoryExists(
base::FilePath("/var/run/chrome"), uid, gid, 0700));
// Ensure the existence of the directory in which the whitelist and other
// ownership-related state will live. Yes, it should be owned by root. The
// permissions are set such that the chronos user can see the content of known
// files inside whitelist, but not anything else.
CHECK(EnsureDirectoryExists(
base::FilePath("/var/lib/whitelist"), kRootUid, gid, 0710));
// Create the directory where external data referenced by policies is cached
// for device-local accounts. This data is read and written by chronos.
CHECK(EnsureDirectoryExists(
base::FilePath("/var/cache/device_local_account_external_policy_data"),
uid, gid, 0700));
// Create the directory where the AppPack extensions are cached.
// These extensions are read and written by chronos.
CHECK(EnsureDirectoryExists(
base::FilePath("/var/cache/app_pack"), uid, gid, 0700));
// Create the directory where extensions for device-local accounts are cached.
// These extensions are read and written by chronos.
CHECK(EnsureDirectoryExists(
base::FilePath("/var/cache/device_local_account_extensions"),
uid, gid, 0700));
// Create the directory for shared installed extensions.
// Shared extensions are validated at runtime by the browser.
// These extensions are read and written by chronos.
CHECK(EnsureDirectoryExists(
base::FilePath("/var/cache/shared_extensions"), uid, gid, 0700));
// Tell Chrome where to write logging messages before the user logs in.
base::FilePath system_log_dir("/var/log/chrome");
CHECK(EnsureDirectoryExists(system_log_dir, uid, gid, 0755));
builder->AddEnvVar("CHROME_LOG_FILE",
system_log_dir.Append("chrome").value());
// Log directory for the user session. Note that the user dir won't be mounted
// until later (when the cryptohome is mounted), so we don't create
// CHROMEOS_SESSION_LOG_DIR here.
builder->AddEnvVar("CHROMEOS_SESSION_LOG_DIR",
user_dir.Append("log").value());
}
// Creates crash-handling-related directories and adds related arguments.
void InitCrashHandling(ChromiumCommandBuilder* builder) {
const base::FilePath user_dir = GetUserDir(builder);
const uid_t uid = builder->uid();
const gid_t gid = builder->gid();
// Force Chrome minidumps that are sent to the crash server to also be written
// locally. Chrome creates these files in
// ~/.config/google-chrome/Crash Reports/.
const base::FilePath stateful_etc("/mnt/stateful_partition/etc");
if (base::PathExists(stateful_etc.Append("enable_chromium_minidumps"))) {
builder->AddEnvVar("CHROME_HEADLESS", "1");
const base::FilePath reports_dir(
user_dir.Append(".config/google-chrome/Crash Reports"));
if (!base::PathExists(reports_dir)) {
base::FilePath minidump_dir("/var/minidumps");
EnsureDirectoryExists(minidump_dir, uid, gid, 0700);
EnsureDirectoryExists(reports_dir.DirName(), uid, gid, 0700);
base::CreateSymbolicLink(minidump_dir, reports_dir);
}
}
// Enable gathering of core dumps via a file in the stateful partition so it
// can be enabled post-build.
if (base::PathExists(stateful_etc.Append("enable_chromium_coredumps")))
builder->EnableCoreDumps();
}
// Adds system-related flags to the command line.
void AddSystemFlags(ChromiumCommandBuilder* builder) {
const base::FilePath data_dir = GetDataDir(builder);
// We need to delete these files as Chrome may have left them around from its
// prior run (if it crashed).
base::DeleteFile(data_dir.Append("SingletonLock"), false);
base::DeleteFile(data_dir.Append("SingletonSocket"), false);
builder->AddArg("--max-unused-resource-memory-usage-percentage=5");
// On developer systems, set a flag to let the browser know.
if (builder->is_developer_end_user())
builder->AddArg("--system-developer-mode");
}
// Adds UI-related flags to the command line.
void AddUiFlags(ChromiumCommandBuilder* builder) {
const base::FilePath data_dir = GetDataDir(builder);
// Force OOBE on test images that have requested it.
if (base::PathExists(base::FilePath("/root/.test_repeat_oobe"))) {
base::DeleteFile(data_dir.Append(".oobe_completed"), false);
base::DeleteFile(data_dir.Append("Local State"), false);
}
builder->AddArg("--login-manager");
builder->AddArg("--login-profile=user");
if (builder->UseFlagIsSet("natural_scroll_default"))
builder->AddArg("--enable-natural-scroll-default");
if (!builder->UseFlagIsSet("legacy_keyboard"))
builder->AddArg("--has-chromeos-keyboard");
if (builder->UseFlagIsSet("has_diamond_key"))
builder->AddArg("--has-chromeos-diamond-key");
if (builder->UseFlagIsSet("legacy_power_button"))
builder->AddArg("--aura-legacy-power-button");
if (builder->UseFlagIsSet("touchview")) {
builder->AddArg("--ash-enable-power-button-quick-lock");
builder->AddArg("--enable-centered-app-list");
}
if (builder->UseFlagIsSet("disable_login_animations")) {
builder->AddArg("--disable-login-animations");
builder->AddArg("--disable-boot-animation");
builder->AddArg("--ash-copy-host-background-at-boot");
} else if (builder->UseFlagIsSet("fade_boot_splash_screen")) {
builder->AddArg("--ash-animate-from-boot-splash-screen");
}
if (AddWallpaperFlags(builder, "default", "oem")) {
builder->AddArg("--default-wallpaper-is-oem");
} else {
AddWallpaperFlags(builder, "default", "default");
AddWallpaperFlags(builder, "child", "child");
}
AddWallpaperFlags(builder, "guest", "guest");
// TODO(yongjaek): Remove the following flag when the kiosk mode app is ready
// at crbug.com/309806.
if (builder->UseFlagIsSet("moblab"))
builder->AddArg("--disable-demo-mode");
}
// Adds enterprise-related flags to the command line.
void AddEnterpriseFlags(ChromiumCommandBuilder* builder) {
builder->AddArg("--enterprise-enable-forced-re-enrollment");
builder->AddArg("--enterprise-enrollment-initial-modulus=13");
builder->AddArg("--enterprise-enrollment-modulus-limit=17");
}
// Adds patterns to the --vmodule flag.
void AddVmodulePatterns(ChromiumCommandBuilder* builder) {
// There has been a steady supply of bug reports about screen locking. These
// messages are useful for determining what happened within feedback reports.
builder->AddVmodulePattern("screen_locker=1");
builder->AddVmodulePattern("webui_screen_locker=1");
// Turn on logging about external displays being connected and disconnected.
// Different behavior is seen from different displays and these messages are
// used to determine what happened within feedback reports.
builder->AddVmodulePattern("*ui/display/chromeos*=1");
builder->AddVmodulePattern("*ash/display*=1");
// Turn on basic logging for Ozone platform implementations.
builder->AddVmodulePattern("*ui/ozone*=1");
// Turn on plugin loading failure logging for crbug.com/314301.
builder->AddVmodulePattern("*zygote*=1");
builder->AddVmodulePattern("*plugin*=2");
}
} // namespace
void PerformChromeSetup(bool* is_developer_end_user_out,
std::map<std::string, std::string>* env_vars_out,
std::vector<std::string>* args_out,
uid_t* uid_out) {
DCHECK(env_vars_out);
DCHECK(args_out);
DCHECK(uid_out);
ChromiumCommandBuilder builder;
CHECK(builder.Init());
// Start X in the background before doing more-expensive setup.
scoped_ptr<XServerRunner> x_runner;
const base::FilePath xauth_path(kXauthPath);
const bool using_x11 = builder.UseFlagIsSet("X");
if (using_x11) {
x_runner.reset(new XServerRunner);
CHECK(x_runner->StartServer(
XServerRunner::kDefaultUser, XServerRunner::kDefaultVt,
builder.is_developer_end_user(), xauth_path));
}
builder.SetUpChromium(using_x11 ? xauth_path : base::FilePath());
// Please add new code to the most-appropriate helper function instead of
// putting it here. Things that to all Chromium-derived binaries (e.g.
// app_shell, content_shell, etc.) rather than just to Chrome belong in the
// ChromiumCommandBuilder class instead.
CreateDirectories(&builder);
InitCrashHandling(&builder);
AddSystemFlags(&builder);
AddUiFlags(&builder);
AddEnterpriseFlags(&builder);
AddVmodulePatterns(&builder);
// Apply any modifications requested by the developer.
if (builder.is_developer_end_user())
builder.ApplyUserConfig(base::FilePath(kChromeDevConfigPath));
*is_developer_end_user_out = builder.is_developer_end_user();
*env_vars_out = builder.environment_variables();
*args_out = builder.arguments();
*uid_out = builder.uid();
if (using_x11)
CHECK(x_runner->WaitForServer());
// Do not add code here. Potentially-expensive work should be done between
// StartServer() and WaitForServer().
}
} // namespace login_manager