1 /*
2  * Copyright (c) 2022-2023 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 
16 #include "b_process/b_process.h"
17 
18 #include <algorithm>
19 #include <cstddef>
20 #include <cstdio>
21 #include <functional>
22 #include <memory>
23 #include <regex>
24 #include <string_view>
25 #include <sys/wait.h>
26 #include <tuple>
27 #include <unistd.h>
28 
29 #include "b_error/b_error.h"
30 #include "b_process/b_guard_signal.h"
31 #include "errors.h"
32 #include "filemgmt_libhilog.h"
33 #include "securec.h"
34 #include "unique_fd.h"
35 
36 namespace OHOS::FileManagement::Backup {
37 using namespace std;
38 
WaitForChild(pid_t pid,unique_ptr<FILE,function<void (FILE *)>> pipeStream,function<bool (string_view)> DetectFatalLog)39 static tuple<bool, ErrCode> WaitForChild(pid_t pid,
40                                          unique_ptr<FILE, function<void(FILE *)>> pipeStream,
41                                          function<bool(string_view)> DetectFatalLog)
42 {
43     const int BUF_LEN = 1024;
44     auto buf = make_unique<char[]>(BUF_LEN);
45     int status = 0;
46 
47     do {
48         regex reg("^\\W*$");
49         while ((void)memset_s(buf.get(), BUF_LEN, 0, BUF_LEN),
50                fgets(buf.get(), BUF_LEN - 1, pipeStream.get()) != nullptr) {
51             if (regex_match(buf.get(), reg)) {
52                 continue;
53             }
54             HILOGE("child process output error: %{public}s", buf.get());
55             if (DetectFatalLog && DetectFatalLog(string_view(buf.get()))) {
56                 return {true, EPERM};
57             }
58         }
59 
60         if (waitpid(pid, &status, 0) == -1) {
61             throw BError(BError::Codes::UTILS_INVAL_PROCESS_ARG, generic_category().message(errno));
62         } else if (WIFEXITED(status)) {
63             return {false, WEXITSTATUS(status)};
64         } else if (WIFSIGNALED(status)) {
65             // 因某种信号中断获取状态
66             // 异常机制存在问题,导致应用在正常的错误下Crash。为确保测试顺利展开,此处暂时屏蔽崩溃错误。
67             HILOGE("some fatal errors occurred, child process is killed by a signal.");
68             return {true, EPERM};
69         }
70     } while (!WIFEXITED(status) && !WIFSIGNALED(status));
71 
72     HILOGE("If you look at me, there are some fatal errors occurred!!!");
73     return {true, EPERM};
74 }
75 
ExecuteCmd(vector<string_view> argvSv,function<bool (string_view)> DetectFatalLog)76 tuple<bool, ErrCode> BProcess::ExecuteCmd(vector<string_view> argvSv, function<bool(string_view)> DetectFatalLog)
77 {
78     vector<const char *> argv;
79     auto getStringViewData = [](const auto &arg) { return arg.data(); };
80     transform(argvSv.begin(), argvSv.end(), back_inserter(argv), getStringViewData);
81     argv.push_back(nullptr);
82 
83     // 临时将SIGCHLD恢复成默认值,从而能够从作为僵尸进程的子进程中获得返回值
84     BGuardSignal guard(SIGCHLD);
85 
86     int pipeFd[2];
87     if (pipe(pipeFd) < 0) {
88         throw BError(BError::Codes::UTILS_INTERRUPTED_PROCESS, generic_category().message(errno));
89     }
90 
91     pid_t pid = 0;
92     if ((pid = fork()) == 0) {
93         close(STDIN_FILENO);
94         close(STDOUT_FILENO);
95         close(pipeFd[0]);
96         UniqueFd fd(pipeFd[1]);
97         if (dup2(pipeFd[1], STDERR_FILENO) == -1) {
98             throw BError(BError::Codes::UTILS_INTERRUPTED_PROCESS, generic_category().message(errno));
99         }
100         exit((execvp(argv[0], const_cast<char **>(argv.data())) == -1) ? errno : 0);
101     }
102 
103     UniqueFd fd(pipeFd[0]);
104     close(pipeFd[1]);
105     if (pid == -1) {
106         throw BError(BError::Codes::UTILS_INVAL_PROCESS_ARG, generic_category().message(errno));
107     }
108     unique_ptr<FILE, function<void(FILE *)>> pipeStream {fdopen(pipeFd[0], "r"), fclose};
109     if (!pipeStream) {
110         throw BError(errno);
111     }
112     fd.Release();
113 
114     return WaitForChild(pid, move(pipeStream), DetectFatalLog);
115 }
116 } // namespace OHOS::FileManagement::Backup