blob: 72d3c2e1627a4397bbf6e1a9dce2747d8df12ae2 [file] [log] [blame]
// Copyright (c) 2009-2010 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.
// This class is most definitely NOT re-entrant.
#include "login_manager/child_job.h"
#include <errno.h>
#include <glib.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <algorithm>
#include <string>
#include <vector>
#include <base/basictypes.h>
#include <base/command_line.h>
#include <base/file_path.h>
#include <base/logging.h>
#include <base/string_util.h>
namespace login_manager {
// static
const int ChildJobInterface::kCantSetUid = 127;
const int ChildJobInterface::kCantSetGid = 128;
const int ChildJobInterface::kCantSetGroups = 129;
const int ChildJobInterface::kCantExec = 255;
const int ChildJobInterface::kBWSI = 1;
// static
const char ChildJob::kLoginManagerFlag[] = "--login-manager";
// static
const char ChildJob::kLoginUserFlag[] = "--login-user=";
// static
const char ChildJob::kBWSIFlag[] = "--bwsi";
// static
const char ChildJob::kWindowManagerSuffix[] = "window-manager-session.sh";
// static
const int ChildJob::kRestartWindow = 1;
ChildJob::ChildJob(const std::vector<std::string>& arguments)
: arguments_(arguments),
desired_uid_(0),
is_desired_uid_set_(false),
last_start_(0),
removed_login_manager_flag_(false) {
}
ChildJob::~ChildJob() {
}
bool ChildJob::ShouldStop() const {
return (time(NULL) - last_start_ < kRestartWindow);
}
bool ChildJob::ShouldNeverKill() const {
if (arguments_.empty())
return false;
// Avoid killing the window manager process -- see http://crosbug.com/7901.
return EndsWith(arguments_[0], kWindowManagerSuffix, true);
}
void ChildJob::RecordTime() {
time(&last_start_);
}
void ChildJob::Run() {
// We try to set our UID/GID to the desired UID, and then exec
// the command passed in.
int exit_code = SetIDs();
if (exit_code)
exit(exit_code);
char const** argv = CreateArgv();
int ret = execv(argv[0], const_cast<char* const*>(argv));
// Should never get here, unless we couldn't exec the command.
LOG(ERROR) <<
StringPrintf(
"Error (%d) executing %s: %s", ret, argv[0], strerror(errno));
exit(kCantExec);
}
// When user logs in we want to restart chrome in browsing mode with user
// signed in. Hence we remove --login-manager flag and add --login-user
// flag. If it's BWSI mode, we add the corresponding flag as well.
// TODO(avayvod): Flags for jobs that correspond to all processes but
// Chromium should not be modified.
void ChildJob::StartSession(const std::string& email) {
std::vector<std::string>::iterator to_erase =
std::remove(arguments_.begin(),
arguments_.end(),
kLoginManagerFlag);
if (to_erase != arguments_.end()) {
arguments_.erase(to_erase, arguments_.end());
removed_login_manager_flag_ = true;
}
arguments_.push_back(kLoginUserFlag);
arguments_.back().append(email);
}
void ChildJob::StopSession() {
// The last element for started session is always login user flag.
arguments_.pop_back();
if (removed_login_manager_flag_) {
arguments_.push_back(kLoginManagerFlag);
removed_login_manager_flag_ = false;
}
}
uid_t ChildJob::GetDesiredUid() const {
DCHECK(IsDesiredUidSet());
return desired_uid_;
}
void ChildJob::SetDesiredUid(uid_t uid) {
is_desired_uid_set_ = true;
desired_uid_ = uid;
}
bool ChildJob::IsDesiredUidSet() const {
return is_desired_uid_set_;
}
const std::string ChildJob::GetName() const {
FilePath exec_file(arguments_[0]);
return exec_file.BaseName().value();
}
void ChildJob::SetArguments(const std::string& arguments) {
std::string argv0;
if (!arguments_.empty())
argv0 = arguments_[0];
arguments_.clear();
GError *error = NULL;
gchar **argv = NULL;
gint argc = 0;
if (!g_shell_parse_argv(arguments.c_str(), &argc, &argv, &error)) {
LOG(ERROR) << "Could not parse command: " << error->message;
g_error_free(error);
g_strfreev(argv);
if (!argv0.empty())
arguments_.push_back(argv0);
return;
}
CommandLine new_command_line(argc, argv);
arguments_.assign(new_command_line.argv().begin(),
new_command_line.argv().end());
g_strfreev(argv);
if (!argv0.empty()) {
if (arguments_.size())
arguments_[0] = argv0;
else
arguments_.push_back(argv0);
}
}
void ChildJob::SetExtraArguments(const std::vector<std::string>& arguments) {
extra_arguments_.assign(arguments.begin(), arguments.end());
}
void ChildJob::CopyArgsToArgv(const std::vector<std::string>& arguments,
char const** argv) const {
for (size_t i = 0; i < arguments.size(); ++i) {
size_t needed_space = arguments[i].length() + 1;
char* space = new char[needed_space];
strncpy(space, arguments[i].c_str(), needed_space);
argv[i] = space;
}
}
char const** ChildJob::CreateArgv() const {
size_t total_size = arguments_.size() + extra_arguments_.size();
char const** argv = new char const*[total_size + 1];
CopyArgsToArgv(arguments_, argv);
CopyArgsToArgv(extra_arguments_, argv + arguments_.size());
// Need to append NULL at the end.
argv[total_size] = NULL;
return argv;
}
int ChildJob::SetIDs() {
int to_return = 0;
if (IsDesiredUidSet()) {
struct passwd* entry = getpwuid(GetDesiredUid());
endpwent();
if (initgroups(entry->pw_name, entry->pw_gid) == -1)
to_return = kCantSetGroups;
if (setgid(entry->pw_gid) == -1)
to_return = kCantSetGid;
if (setuid(desired_uid_) == -1)
to_return = kCantSetUid;
}
if (setsid() == -1)
LOG(ERROR) << "can't setsid: " << strerror(errno);
return to_return;
}
} // namespace login_manager