blob: c425a8d35eb666b703c5eb49ca6efcf0ffda8f6d [file] [log] [blame]
mukesh agrawalae30e9e2013-05-28 14:09:16 -07001// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/external_task.h"
6
mukesh agrawal9da07772013-05-15 14:15:17 -07007#include <base/bind.h>
8#include <base/bind_helpers.h>
9
mukesh agrawalae30e9e2013-05-28 14:09:16 -070010#include "shill/error.h"
mukesh agrawal9da07772013-05-15 14:15:17 -070011#include "shill/event_dispatcher.h"
mukesh agrawalae30e9e2013-05-28 14:09:16 -070012#include "shill/process_killer.h"
13
14namespace shill {
15
16using base::FilePath;
17using std::map;
18using std::string;
19using std::vector;
20
21ExternalTask::ExternalTask(
22 ControlInterface *control,
23 GLib *glib,
24 const base::WeakPtr<RPCTaskDelegate> &task_delegate,
25 const base::Callback<void(pid_t, int)> &death_callback)
26 : control_(control),
27 glib_(glib),
28 process_killer_(ProcessKiller::GetInstance()),
29 task_delegate_(task_delegate),
30 death_callback_(death_callback),
31 pid_(0),
32 child_watch_tag_(0) {
33 CHECK(task_delegate_);
34}
35
36ExternalTask::~ExternalTask() {
37 ExternalTask::Stop();
38}
39
mukesh agrawal9da07772013-05-15 14:15:17 -070040void ExternalTask::DestroyLater(EventDispatcher *dispatcher) {
41 // Passes ownership of |this| to Destroy.
42 dispatcher->PostTask(base::Bind(&Destroy, this));
43}
44
mukesh agrawalae30e9e2013-05-28 14:09:16 -070045bool ExternalTask::Start(const FilePath &program,
46 const vector<string> &arguments,
47 const map<string, string> &environment,
48 Error *error) {
49 CHECK(!pid_);
50 CHECK(!child_watch_tag_);
51 CHECK(!rpc_task_);
52
53 scoped_ptr<RPCTask> local_rpc_task(new RPCTask(control_, this));
54
55 // const_cast is safe here, because exec*() (and SpawnAsync) do not
56 // modify the strings passed to them. This isn't captured in the
57 // exec*() prototypes, due to limitations in ISO C.
58 // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
59 vector<char *> process_args;
60 process_args.push_back(const_cast<char *>(program.value().c_str()));
61 for (const auto &option : arguments) {
62 process_args.push_back(const_cast<char *>(option.c_str()));
63 }
64 process_args.push_back(NULL);
65
66 vector<char *> process_env;
67 vector<string> env_vars(local_rpc_task->GetEnvironment());
68 for (const auto &env_pair : environment) {
69 env_vars.push_back(string(env_pair.first + "=" + env_pair.second));
70 }
71 for (const auto &env_var : env_vars) {
72 // See above regarding const_cast.
73 process_env.push_back(const_cast<char *>(env_var.c_str()));
74 }
75 process_env.push_back(NULL);
76
77 if (!glib_->SpawnAsync(NULL,
78 process_args.data(),
79 process_env.data(),
80 G_SPAWN_DO_NOT_REAP_CHILD,
81 NULL,
82 NULL,
83 &pid_,
84 NULL)) {
85 Error::PopulateAndLog(error, Error::kInternalError,
86 string("Unable to spawn: ") + process_args[0]);
87 return false;
88 }
89 child_watch_tag_ = glib_->ChildWatchAdd(pid_, OnTaskDied, this);
90 rpc_task_.reset(local_rpc_task.release());
91 return true;
92}
93
94void ExternalTask::Stop() {
95 if (child_watch_tag_) {
96 glib_->SourceRemove(child_watch_tag_);
97 child_watch_tag_ = 0;
98 }
99 if (pid_) {
100 process_killer_->Kill(pid_, base::Closure());
101 pid_ = 0;
102 }
103 rpc_task_.reset();
104}
105
106void ExternalTask::GetLogin(string *user, string *password) {
107 return task_delegate_->GetLogin(user, password);
108}
109
110void ExternalTask::Notify(const string &event,
111 const map<string, string> &details) {
112 return task_delegate_->Notify(event, details);
113}
114
115// static
116void ExternalTask::OnTaskDied(GPid pid, gint status, gpointer data) {
117 LOG(INFO) << __func__ << "(" << pid << ", " << status << ")";
118 ExternalTask *me = reinterpret_cast<ExternalTask *>(data);
119 me->child_watch_tag_ = 0;
120 CHECK_EQ(pid, me->pid_);
121 me->pid_ = 0;
122 me->death_callback_.Run(pid, status);
123}
124
mukesh agrawal9da07772013-05-15 14:15:17 -0700125// static
126void ExternalTask::Destroy(ExternalTask *task) {
127 delete task;
128}
129
mukesh agrawalae30e9e2013-05-28 14:09:16 -0700130} // namespace shill