1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "executor/cmd_dumper.h"
16 
17 #include "dump_common_utils.h"
18 #include "securec.h"
19 
20 namespace OHOS {
21 namespace HiviewDFX {
22 const std::string CMD_PREFIX = "/system/bin/";
CMDDumper()23 CMDDumper::CMDDumper() : fp_(nullptr)
24 {
25 }
26 
~CMDDumper()27 CMDDumper::~CMDDumper()
28 {
29     if (fp_ != nullptr) {
30         pclose(fp_);
31         fp_ = nullptr;
32     }
33 }
34 
PreExecute(const std::shared_ptr<DumperParameter>& parameter, StringMatrix dumpDatas)35 DumpStatus CMDDumper::PreExecute(const std::shared_ptr<DumperParameter>& parameter,
36     StringMatrix dumpDatas)
37 {
38     if (dumpDatas.get() == nullptr) {
39         return DumpStatus::DUMP_FAIL;
40     }
41     std::string cmd = ptrDumpCfg_->target_;
42     if (ptrDumpCfg_->args_ != nullptr) {
43         if (ptrDumpCfg_->args_->HasPid()) {
44             int pid = ptrDumpCfg_->args_->GetPid();
45             if (ptrDumpCfg_->name_ == "dumper_stack" && !DumpCommonUtils::IsUserPid(std::to_string(pid))) {
46                 return DumpStatus::DUMP_FAIL;
47             }
48             ReplacePidInCmd(cmd, pid);
49         }
50         if (ptrDumpCfg_->args_->HasCpuId()) {
51             int cpuId = ptrDumpCfg_->args_->GetCpuId();
52             ReplaceCpuIdInCmd(cmd, cpuId);
53         }
54     }
55     cmd_ = cmd;
56     needLoop_ = (ptrDumpCfg_->loop_ == DumperConstant::LOOP);
57     if (fp_ == nullptr) {
58         if ((fp_ = popen((CMD_PREFIX + cmd_).c_str(), "r")) == nullptr) {
59             return DumpStatus::DUMP_FAIL;
60         }
61     }
62     dumpDatas_ = dumpDatas;
63 
64     // Add only once!
65     if (lineData_.size() <= 0) {
66         std::string lineStr = "\ncmd is: ";
67         lineStr = lineStr + cmd_ + "\n\n";
68         lineData_.push_back(lineStr);
69         dumpDatas_->push_back(lineData_);
70     }
71     return DumpStatus::DUMP_OK;
72 }
73 
Execute()74 DumpStatus CMDDumper::Execute()
75 {
76     DUMPER_HILOGI(MODULE_COMMON, "info|CMDDumper Execute");
77     DumpStatus ret = DumpStatus::DUMP_OK;
78     if (needLoop_) {
79         // cmd dump one line
80         return ReadLineInCmd();
81     } else {
82         // cmd dump all line
83         do {
84             if (IsCanceled()) {
85                 break;
86             }
87             ret = ReadLineInCmd();
88         } while (ret == DumpStatus::DUMP_MORE_DATA);
89     }
90     DUMPER_HILOGI(MODULE_COMMON, "info|CMDDumper Execute end");
91     return ret;
92 }
93 
AfterExecute()94 DumpStatus CMDDumper::AfterExecute()
95 {
96     if (!moreData_) {
97         if (fp_ != nullptr) {
98             pclose(fp_);
99             fp_ = nullptr;
100         }
101         return DumpStatus::DUMP_OK;
102     }
103     return DumpStatus::DUMP_MORE_DATA;
104 }
105 
106 // provide interface to others
GetCmdInterface(const std::string& cmd, StringMatrix dumpDatas)107 DumpStatus CMDDumper::GetCmdInterface(const std::string& cmd, StringMatrix dumpDatas)
108 {
109     DumpStatus ret = DumpStatus::DUMP_MORE_DATA;
110     FILE* fp = popen((CMD_PREFIX + cmd).c_str(), "r");
111     if (fp == nullptr) {
112         return DumpStatus::DUMP_FAIL;
113     }
114     do {
115         ret = GetLineData(fp, dumpDatas);
116     } while (ret == DumpStatus::DUMP_MORE_DATA);
117     pclose(fp);
118     return ret;
119 }
120 
GetLineData(FILE* fp, StringMatrix dumpDatas)121 DumpStatus CMDDumper::GetLineData(FILE* fp, StringMatrix dumpDatas)
122 {
123     DumpStatus ret = DumpStatus::DUMP_MORE_DATA;
124     char* line_buffer = nullptr;
125     size_t len = 0;
126     ssize_t read = 0;
127     read = getline(&line_buffer, &len, fp);
128     if (read != -1) {
129         if (line_buffer[read - 1] == '\n') {
130             line_buffer[read - 1] = '\0'; // replease \n
131         }
132         std::string line = line_buffer;
133         std::vector<std::string> line_vector;
134         line_vector.push_back(line);
135         dumpDatas->push_back(line_vector);
136     } else {
137         if (feof(fp) == 0) { // ferror()
138             ret = DumpStatus::DUMP_FAIL;
139         } else {
140             ret = DumpStatus::DUMP_OK;
141         }
142     }
143     if (line_buffer != nullptr) {
144         free(line_buffer);
145         line_buffer = nullptr;
146     }
147     return ret;
148 }
149 
150 // read one line
ReadLineInCmd()151 DumpStatus CMDDumper::ReadLineInCmd()
152 {
153     if (fp_ == nullptr) {
154         return DumpStatus::DUMP_FAIL;
155     }
156     DumpStatus ret = GetLineData(fp_, dumpDatas_);
157     moreData_ = (ret == DumpStatus::DUMP_MORE_DATA);
158     return ret;
159 }
160 
ReplacePidInCmd(std::string &cmd, int pid)161 void CMDDumper::ReplacePidInCmd(std::string &cmd, int pid)
162 {
163     size_t pos = cmd.find("%pid");
164     if (pos != std::string::npos) {
165         cmd = cmd.replace(pos, strlen("%pid"), std::to_string(pid));
166         cmd_ = cmd;
167     }
168 }
169 
ReplaceCpuIdInCmd(std::string &cmd, int cpuId)170 void CMDDumper::ReplaceCpuIdInCmd(std::string &cmd, int cpuId)
171 {
172     size_t pos = cmd.find("%cpuid");
173     if (pos != std::string::npos) {
174         cmd = cmd.replace(pos, strlen("%cpuid"), std::to_string(cpuId));
175         cmd_ = cmd;
176     }
177 }
178 } // namespace HiviewDFX
179 } // namespace OHOS
180