Line data Source code
1 : /*
2 : * Copyright (c) 2014 Juniper Networks, Inc. All right reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include "oper/instance_task.h"
7 : #include "base/logging.h"
8 : #include "io/event_manager.h"
9 :
10 0 : InstanceTask::InstanceTask()
11 0 : : is_running_(false), start_time_(0), reattempts_(0)
12 0 : {}
13 :
14 0 : InstanceTaskExecvp::InstanceTaskExecvp(const std::string &name,
15 : const std::string &cmd,
16 0 : int cmd_type, EventManager *evm) :
17 0 : name_(name), cmd_(cmd), input_(*(evm->io_service())),
18 0 : setup_done_(false), pid_(0), cmd_type_(cmd_type), pipe_stdout_(false) {
19 0 : }
20 :
21 0 : void InstanceTaskExecvp::ReadData(const boost::system::error_code &ec,
22 : size_t read_bytes) {
23 0 : if (read_bytes) {
24 0 : std::string data(rx_buff_, read_bytes);
25 0 : if (!on_data_cb_.empty()) {
26 0 : on_data_cb_(this, data);
27 : }
28 0 : LOG(DEBUG, "Command output: " + data);
29 0 : }
30 :
31 0 : if (ec) {
32 0 : boost::system::error_code close_ec;
33 0 : input_.close(close_ec);
34 :
35 0 : if (!on_exit_cb_.empty()) {
36 0 : on_exit_cb_(this, ec);
37 : }
38 0 : LOG(DEBUG, "Command code: " + ec.message());
39 0 : return;
40 : }
41 :
42 0 : bzero(rx_buff_, sizeof(rx_buff_));
43 0 : input_.async_read_some(boost::asio::buffer(rx_buff_, kBufLen),
44 0 : boost::bind(&InstanceTaskExecvp::ReadData,
45 : this, boost::asio::placeholders::error,
46 : boost::asio::placeholders::bytes_transferred));
47 : }
48 :
49 0 : void InstanceTaskExecvp::Stop() {
50 0 : assert(pid_);
51 0 : kill(pid_, SIGTERM);
52 0 : }
53 :
54 0 : void InstanceTaskExecvp::Terminate() {
55 0 : assert(pid_);
56 0 : kill(pid_, SIGKILL);
57 0 : }
58 :
59 0 : bool InstanceTaskExecvp::IsSetup() {
60 0 : return setup_done_;
61 : }
62 :
63 : // If there is an error before the fork, task is set to "not running"
64 : // and "false" is returned to caller so that caller can take appropriate
65 : // action on task. If an error is encounted after fork, it is very
66 : // likely that child process is running so we keep the task status as
67 : // "running" and return "false" to caller, so that caller does not
68 : // attempt to run the same task again. In this case, the child process
69 : // exit notification can not be received by instance manager, hence
70 : // instance manager has to rely on TaskTimeout delete the task.
71 0 : bool InstanceTaskExecvp::Run() {
72 0 : std::vector<std::string> argv;
73 :
74 0 : is_running_ = true;
75 :
76 0 : boost::split(argv, cmd_, boost::is_any_of(" "), boost::token_compress_on);
77 0 : std::vector<const char *> c_argv(argv.size() + 1);
78 0 : for (std::size_t i = 0; i != argv.size(); ++i) {
79 0 : c_argv[i] = argv[i].c_str();
80 : }
81 :
82 : int err[2];
83 0 : if (pipe(err) < 0) {
84 0 : return is_running_ = false;
85 : }
86 :
87 0 : pid_ = vfork();
88 0 : if (pid_ == 0) {
89 0 : close(err[0]);
90 0 : if (pipe_stdout_) {
91 0 : dup2(err[1], STDOUT_FILENO);
92 : } else {
93 0 : dup2(err[1], STDERR_FILENO);
94 : }
95 0 : close(err[1]);
96 :
97 0 : if (!pipe_stdout_) {
98 0 : close(STDOUT_FILENO);
99 0 : close(STDIN_FILENO);
100 : }
101 :
102 : /* Close all the open fds before execvp */
103 0 : CloseTaskFds();
104 0 : execvp(c_argv[0], (char **) c_argv.data());
105 0 : perror("execvp");
106 :
107 0 : _exit(127);
108 : }
109 :
110 0 : close(err[1]);
111 :
112 0 : start_time_ = time(NULL);
113 :
114 0 : int fd = ::dup(err[0]);
115 0 : close(err[0]);
116 0 : if (fd == -1) {
117 : //is_running_ is still true indicating the child process is
118 : //running. Caller needs to verify the status before acting on
119 : //the task again
120 0 : return false;
121 : }
122 0 : boost::system::error_code ec;
123 0 : input_.assign(fd, ec);
124 0 : if (ec) {
125 0 : close(fd);
126 :
127 : //is_running_ is still true indicating the child process is
128 : //running. Caller needs to verify the status before acting on
129 : //the task again
130 0 : return false;
131 : }
132 0 : setup_done_ = true;
133 :
134 0 : bzero(rx_buff_, sizeof(rx_buff_));
135 0 : input_.async_read_some(boost::asio::buffer(rx_buff_, kBufLen),
136 0 : boost::bind(&InstanceTaskExecvp::ReadData,
137 : this, boost::asio::placeholders::error,
138 : boost::asio::placeholders::bytes_transferred));
139 0 : return true;
140 :
141 0 : }
142 :
143 2 : InstanceTaskQueue::InstanceTaskQueue(EventManager *evm) : evm_(evm),
144 4 : timeout_timer_(TimerManager::CreateTimer(
145 2 : *evm_->io_service(),
146 : "Instance Manager Task Timeout",
147 : TaskScheduler::GetInstance()->GetTaskId(
148 2 : INSTANCE_MANAGER_TASK_NAME), 0)) {
149 2 : }
150 :
151 2 : InstanceTaskQueue::~InstanceTaskQueue() {
152 2 : TimerManager::DeleteTimer(timeout_timer_);
153 2 : }
154 :
155 0 : void InstanceTaskQueue::StartTimer(int time) {
156 0 : timeout_timer_->Start(time,
157 : boost::bind(&InstanceTaskQueue::OnTimerTimeout,
158 : this),
159 : boost::bind(&InstanceTaskQueue::TimerErrorHandler,
160 : this, _1, _2));
161 0 : }
162 :
163 0 : void InstanceTaskQueue::StopTimer() {
164 0 : timeout_timer_->Cancel();
165 0 : }
166 :
167 0 : bool InstanceTaskQueue::OnTimerTimeout() {
168 0 : if (! on_timeout_cb_.empty()) {
169 0 : on_timeout_cb_(this);
170 : }
171 :
172 0 : return true;
173 : }
174 :
175 0 : void InstanceTaskQueue::TimerErrorHandler(const std::string &name, std::string error) {
176 0 : LOG(ERROR, "NetNS timeout error: " << error);
177 0 : }
178 :
179 2 : void InstanceTaskQueue::Clear() {
180 2 : timeout_timer_->Cancel();
181 :
182 2 : while(!task_queue_.empty()) {
183 0 : InstanceTask *task = task_queue_.front();
184 0 : task_queue_.pop();
185 0 : delete task;
186 : }
187 2 : }
|