LCOV - code coverage report
Current view: top level - vnsw/agent/oper - instance_task.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 12 103 11.7 %
Date: 2026-11-09 02:10:49 Functions: 3 14 21.4 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14