148f512ceSopenharmony_ci/*
248f512ceSopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
348f512ceSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
448f512ceSopenharmony_ci * you may not use this file except in compliance with the License.
548f512ceSopenharmony_ci * You may obtain a copy of the License at
648f512ceSopenharmony_ci *
748f512ceSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
848f512ceSopenharmony_ci *
948f512ceSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
1048f512ceSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
1148f512ceSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1248f512ceSopenharmony_ci * See the License for the specific language governing permissions and
1348f512ceSopenharmony_ci * limitations under the License.
1448f512ceSopenharmony_ci */
1548f512ceSopenharmony_ci#include "tracked_command.h"
1648f512ceSopenharmony_ci#include <cerrno>
1748f512ceSopenharmony_ci#include <csignal>
1848f512ceSopenharmony_ci#include <sys/prctl.h>
1948f512ceSopenharmony_ci#include <sys/wait.h>
2048f512ceSopenharmony_ci#include <unistd.h>
2148f512ceSopenharmony_ci#include "debug_logger.h"
2248f512ceSopenharmony_ci
2348f512ceSopenharmony_cinamespace OHOS {
2448f512ceSopenharmony_cinamespace Developtools {
2548f512ceSopenharmony_cinamespace HiPerf {
2648f512ceSopenharmony_cistd::unique_ptr<TrackedCommand> TrackedCommand::CreateInstance(const std::vector<std::string> &args)
2748f512ceSopenharmony_ci{
2848f512ceSopenharmony_ci    std::unique_ptr<TrackedCommand> command {new (std::nothrow) TrackedCommand(args)};
2948f512ceSopenharmony_ci    if (!command) {
3048f512ceSopenharmony_ci        return nullptr;
3148f512ceSopenharmony_ci    }
3248f512ceSopenharmony_ci    if (!command->CreateChildProcess()) {
3348f512ceSopenharmony_ci        return nullptr;
3448f512ceSopenharmony_ci    }
3548f512ceSopenharmony_ci    return command;
3648f512ceSopenharmony_ci}
3748f512ceSopenharmony_ci
3848f512ceSopenharmony_ciTrackedCommand::TrackedCommand(const std::vector<std::string> &args) : command_ {args} {}
3948f512ceSopenharmony_ci
4048f512ceSopenharmony_ciTrackedCommand::~TrackedCommand()
4148f512ceSopenharmony_ci{
4248f512ceSopenharmony_ci    MakeInvalid();
4348f512ceSopenharmony_ci}
4448f512ceSopenharmony_ci
4548f512ceSopenharmony_civoid TrackedCommand::Stop()
4648f512ceSopenharmony_ci{
4748f512ceSopenharmony_ci    MakeInvalid();
4848f512ceSopenharmony_ci}
4948f512ceSopenharmony_ci
5048f512ceSopenharmony_cibool TrackedCommand::InitSignalPipes(int &startFd, int &ackFd)
5148f512ceSopenharmony_ci{
5248f512ceSopenharmony_ci    int startSignalPipe[2] {-1, -1};
5348f512ceSopenharmony_ci    if (pipe2(startSignalPipe, O_CLOEXEC) != 0) {
5448f512ceSopenharmony_ci        HLOGE("pipe2() failed in TrackedCommand::InitSignalPipes()");
5548f512ceSopenharmony_ci        return false;
5648f512ceSopenharmony_ci    }
5748f512ceSopenharmony_ci    startFd = startSignalPipe[0];
5848f512ceSopenharmony_ci    startFd_ = startSignalPipe[1];
5948f512ceSopenharmony_ci
6048f512ceSopenharmony_ci    int ackSignalPipe[2] {-1, -1};
6148f512ceSopenharmony_ci    if (pipe2(ackSignalPipe, O_CLOEXEC) != 0) {
6248f512ceSopenharmony_ci        HLOGE("pipe2() failed in TrackedCommand::InitSignalPipes()");
6348f512ceSopenharmony_ci        close(startFd);
6448f512ceSopenharmony_ci        close(startFd_);
6548f512ceSopenharmony_ci        startFd = -1;
6648f512ceSopenharmony_ci        startFd_ = -1;
6748f512ceSopenharmony_ci        return false;
6848f512ceSopenharmony_ci    }
6948f512ceSopenharmony_ci    ackFd = ackSignalPipe[1];
7048f512ceSopenharmony_ci    ackFd_ = ackSignalPipe[0];
7148f512ceSopenharmony_ci    return true;
7248f512ceSopenharmony_ci}
7348f512ceSopenharmony_ci
7448f512ceSopenharmony_cibool TrackedCommand::CreateChildProcess()
7548f512ceSopenharmony_ci{
7648f512ceSopenharmony_ci    int startFd {-1};
7748f512ceSopenharmony_ci    int ackFd {-1};
7848f512ceSopenharmony_ci    if (!InitSignalPipes(startFd, ackFd)) {
7948f512ceSopenharmony_ci        return false;
8048f512ceSopenharmony_ci    }
8148f512ceSopenharmony_ci    pid_t pid = fork();
8248f512ceSopenharmony_ci    if (pid == -1) {
8348f512ceSopenharmony_ci        HLOGE("fork() failed in TrackedCommand::CreateChildProcess()");
8448f512ceSopenharmony_ci        MakeInvalid();
8548f512ceSopenharmony_ci        return false;
8648f512ceSopenharmony_ci    } else if (pid == 0) {
8748f512ceSopenharmony_ci        close(startFd_);
8848f512ceSopenharmony_ci        close(ackFd_);
8948f512ceSopenharmony_ci        ExecuteCommand(startFd, ackFd);
9048f512ceSopenharmony_ci        _exit(0);
9148f512ceSopenharmony_ci    } else {
9248f512ceSopenharmony_ci        close(startFd);
9348f512ceSopenharmony_ci        close(ackFd);
9448f512ceSopenharmony_ci        childPid_ = pid;
9548f512ceSopenharmony_ci        state_ = State::COMMAND_WAITING;
9648f512ceSopenharmony_ci        return true;
9748f512ceSopenharmony_ci    }
9848f512ceSopenharmony_ci}
9948f512ceSopenharmony_ci
10048f512ceSopenharmony_cibool TrackedCommand::StartCommand()
10148f512ceSopenharmony_ci{
10248f512ceSopenharmony_ci    // send start signal to start execution of command
10348f512ceSopenharmony_ci    ssize_t nbyte {0};
10448f512ceSopenharmony_ci    char startSignal {1};
10548f512ceSopenharmony_ci    while (true) {
10648f512ceSopenharmony_ci        nbyte = write(startFd_, &startSignal, 1);
10748f512ceSopenharmony_ci        if (nbyte == -1) {
10848f512ceSopenharmony_ci            continue;
10948f512ceSopenharmony_ci        }
11048f512ceSopenharmony_ci        break;
11148f512ceSopenharmony_ci    }
11248f512ceSopenharmony_ci    HLOG_ASSERT(nbyte == 1);
11348f512ceSopenharmony_ci    // check execution state of command
11448f512ceSopenharmony_ci    // read acknowledgement signal
11548f512ceSopenharmony_ci    char ackSignal {0};
11648f512ceSopenharmony_ci    while (true) {
11748f512ceSopenharmony_ci        nbyte = read(ackFd_, &ackSignal, 1);
11848f512ceSopenharmony_ci        if (nbyte == -1 and (errno == EINTR or errno == EIO)) {
11948f512ceSopenharmony_ci            continue;
12048f512ceSopenharmony_ci        }
12148f512ceSopenharmony_ci        HLOGE("*** nbyte: %zd, ackSignal: %d ***\n", nbyte, ackSignal);
12248f512ceSopenharmony_ci        break;
12348f512ceSopenharmony_ci    }
12448f512ceSopenharmony_ci    if (nbyte == 0) {
12548f512ceSopenharmony_ci        state_ = State::COMMAND_STARTED;
12648f512ceSopenharmony_ci        return true;
12748f512ceSopenharmony_ci    }
12848f512ceSopenharmony_ci    HLOG_ASSERT(nbyte == 1);
12948f512ceSopenharmony_ci    state_ = State::COMMAND_FAILURE;
13048f512ceSopenharmony_ci    return false;
13148f512ceSopenharmony_ci}
13248f512ceSopenharmony_ci
13348f512ceSopenharmony_civoid TrackedCommand::ExecuteCommand(const int &startFd, const int &ackFd)
13448f512ceSopenharmony_ci{
13548f512ceSopenharmony_ci    HLOG_ASSERT(startFd != -1);
13648f512ceSopenharmony_ci    HLOG_ASSERT(ackFd != -1);
13748f512ceSopenharmony_ci    prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
13848f512ceSopenharmony_ci    // waiting start signal
13948f512ceSopenharmony_ci    char startSignal {0};
14048f512ceSopenharmony_ci    ssize_t nbyte {0};
14148f512ceSopenharmony_ci    while (true) {
14248f512ceSopenharmony_ci        nbyte = read(startFd, &startSignal, 1);
14348f512ceSopenharmony_ci        if (nbyte == -1) {
14448f512ceSopenharmony_ci            continue;
14548f512ceSopenharmony_ci        }
14648f512ceSopenharmony_ci        break;
14748f512ceSopenharmony_ci    }
14848f512ceSopenharmony_ci    HLOG_ASSERT(nbyte == 1);
14948f512ceSopenharmony_ci    // execute command
15048f512ceSopenharmony_ci    char *argv[command_.size() + 1];
15148f512ceSopenharmony_ci    for (size_t index = 0; index < command_.size(); ++index) {
15248f512ceSopenharmony_ci        argv[index] = const_cast<char *>(command_[index].c_str());
15348f512ceSopenharmony_ci    }
15448f512ceSopenharmony_ci    argv[command_.size()] = nullptr;
15548f512ceSopenharmony_ci    // On sucees, startFd and ackFd will be closed hence parent process reads EPIPE;
15648f512ceSopenharmony_ci    if (IsPath(argv[0])) {
15748f512ceSopenharmony_ci        execv(argv[0], argv);
15848f512ceSopenharmony_ci    } else {
15948f512ceSopenharmony_ci        execvp(argv[0], argv);
16048f512ceSopenharmony_ci    }
16148f512ceSopenharmony_ci    // execv() or execvp() failed, send failure signal
16248f512ceSopenharmony_ci    char ackSignal {1};
16348f512ceSopenharmony_ci    while (true) {
16448f512ceSopenharmony_ci        nbyte = write(ackFd, &ackSignal, 1);
16548f512ceSopenharmony_ci        if (nbyte == -1) {
16648f512ceSopenharmony_ci            continue;
16748f512ceSopenharmony_ci        }
16848f512ceSopenharmony_ci        break;
16948f512ceSopenharmony_ci    }
17048f512ceSopenharmony_ci    HLOG_ASSERT(nbyte == 1);
17148f512ceSopenharmony_ci    HLOGE("child process failed to execute command");
17248f512ceSopenharmony_ci}
17348f512ceSopenharmony_ci
17448f512ceSopenharmony_cibool TrackedCommand::WaitCommand(int &wstatus)
17548f512ceSopenharmony_ci{
17648f512ceSopenharmony_ci    if (childPid_ != -1) {
17748f512ceSopenharmony_ci        HLOG_ASSERT(state_ != State::COMMAND_STOPPED);
17848f512ceSopenharmony_ci        pid_t pid = waitpid(childPid_, &wstatus, WNOHANG);
17948f512ceSopenharmony_ci        if (pid == 0) {
18048f512ceSopenharmony_ci            return false;
18148f512ceSopenharmony_ci        } else { // pid == childPid_ or pid == -1
18248f512ceSopenharmony_ci            childPid_ = -1;
18348f512ceSopenharmony_ci            state_ = State::COMMAND_STOPPED;
18448f512ceSopenharmony_ci            return true;
18548f512ceSopenharmony_ci        }
18648f512ceSopenharmony_ci    }
18748f512ceSopenharmony_ci    return true;
18848f512ceSopenharmony_ci}
18948f512ceSopenharmony_ci
19048f512ceSopenharmony_civoid TrackedCommand::MakeInvalid()
19148f512ceSopenharmony_ci{
19248f512ceSopenharmony_ci    if (childPid_ != -1) {
19348f512ceSopenharmony_ci        HLOG_ASSERT(state_ != State::COMMAND_STOPPED);
19448f512ceSopenharmony_ci        int wstatus;
19548f512ceSopenharmony_ci        pid_t pid = waitpid(childPid_, &wstatus, WNOHANG);
19648f512ceSopenharmony_ci        if (pid != childPid_) {
19748f512ceSopenharmony_ci            kill(childPid_, SIGKILL);
19848f512ceSopenharmony_ci        }
19948f512ceSopenharmony_ci        childPid_ = -1;
20048f512ceSopenharmony_ci        state_ = State::COMMAND_STOPPED;
20148f512ceSopenharmony_ci    }
20248f512ceSopenharmony_ci    if (startFd_ != -1) {
20348f512ceSopenharmony_ci        close(startFd_);
20448f512ceSopenharmony_ci        startFd_ = -1;
20548f512ceSopenharmony_ci    }
20648f512ceSopenharmony_ci    if (ackFd_ != -1) {
20748f512ceSopenharmony_ci        close(ackFd_);
20848f512ceSopenharmony_ci        ackFd_ = -1;
20948f512ceSopenharmony_ci    }
21048f512ceSopenharmony_ci}
21148f512ceSopenharmony_ci} // namespace HiPerf
21248f512ceSopenharmony_ci} // namespace Developtools
21348f512ceSopenharmony_ci} // namespace OHOS
214