blob: 4ea88d4a6feac1bad9397ca2753766e2755d7843 [file] [log] [blame]
// Copyright (c) 2013 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 "shill/external_task.h"
#include <signal.h>
#include <sys/prctl.h>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include "shill/error.h"
#include "shill/event_dispatcher.h"
#include "shill/process_killer.h"
namespace shill {
using base::FilePath;
using std::map;
using std::string;
using std::vector;
ExternalTask::ExternalTask(
ControlInterface *control,
GLib *glib,
const base::WeakPtr<RPCTaskDelegate> &task_delegate,
const base::Callback<void(pid_t, int)> &death_callback)
: control_(control),
glib_(glib),
process_killer_(ProcessKiller::GetInstance()),
task_delegate_(task_delegate),
death_callback_(death_callback),
pid_(0),
child_watch_tag_(0) {
CHECK(task_delegate_);
}
ExternalTask::~ExternalTask() {
ExternalTask::Stop();
}
void ExternalTask::DestroyLater(EventDispatcher *dispatcher) {
// Passes ownership of |this| to Destroy.
dispatcher->PostTask(base::Bind(&Destroy, this));
}
bool ExternalTask::Start(const FilePath &program,
const vector<string> &arguments,
const map<string, string> &environment,
bool terminate_with_parent,
Error *error) {
CHECK(!pid_);
CHECK(!child_watch_tag_);
CHECK(!rpc_task_);
scoped_ptr<RPCTask> local_rpc_task(new RPCTask(control_, this));
// const_cast is safe here, because exec*() (and SpawnAsync) do not
// modify the strings passed to them. This isn't captured in the
// exec*() prototypes, due to limitations in ISO C.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
vector<char *> process_args;
process_args.push_back(const_cast<char *>(program.value().c_str()));
for (const auto &option : arguments) {
process_args.push_back(const_cast<char *>(option.c_str()));
}
process_args.push_back(NULL);
vector<char *> process_env;
vector<string> env_vars(local_rpc_task->GetEnvironment());
for (const auto &env_pair : environment) {
env_vars.push_back(string(env_pair.first + "=" + env_pair.second));
}
for (const auto &env_var : env_vars) {
// See above regarding const_cast.
process_env.push_back(const_cast<char *>(env_var.c_str()));
}
process_env.push_back(NULL);
GSpawnChildSetupFunc child_setup_func =
terminate_with_parent ? SetupTermination : NULL;
if (!glib_->SpawnAsync(NULL,
process_args.data(),
process_env.data(),
G_SPAWN_DO_NOT_REAP_CHILD,
child_setup_func,
NULL,
&pid_,
NULL)) {
Error::PopulateAndLog(error, Error::kInternalError,
string("Unable to spawn: ") + process_args[0]);
return false;
}
child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnTaskDied, this);
rpc_task_.reset(local_rpc_task.release());
return true;
}
void ExternalTask::Stop() {
if (child_watch_tag_) {
glib_->SourceRemove(child_watch_tag_);
child_watch_tag_ = 0;
}
if (pid_) {
process_killer_->Kill(pid_, base::Closure());
pid_ = 0;
}
rpc_task_.reset();
}
void ExternalTask::GetLogin(string *user, string *password) {
return task_delegate_->GetLogin(user, password);
}
void ExternalTask::Notify(const string &event,
const map<string, string> &details) {
return task_delegate_->Notify(event, details);
}
// static
void ExternalTask::OnTaskDied(GPid pid, gint status, gpointer data) {
LOG(INFO) << __func__ << "(" << pid << ", " << status << ")";
ExternalTask *me = reinterpret_cast<ExternalTask *>(data);
me->child_watch_tag_ = 0;
CHECK_EQ(pid, me->pid_);
me->pid_ = 0;
me->death_callback_.Run(pid, status);
}
// static
void ExternalTask::Destroy(ExternalTask *task) {
delete task;
}
// static
void ExternalTask::SetupTermination(gpointer /*glib_user_data*/) {
prctl(PR_SET_PDEATHSIG, SIGTERM);
}
} // namespace shill