Coverage Report

Created: 2025-11-01 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/butil/popen.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
// Date: 2017/11/04 17:37:43
19
20
#include <gflags/gflags.h>
21
#include "butil/build_config.h"
22
#include "butil/logging.h"
23
24
#if defined(OS_LINUX)
25
// clone is a linux specific syscall
26
#include <sched.h>
27
#include <errno.h>
28
#include <sys/types.h>
29
#include <sys/wait.h>
30
31
extern "C" {
32
int BAIDU_WEAK bthread_usleep(uint64_t microseconds);
33
}
34
#endif
35
36
namespace butil {
37
38
#if defined(OS_LINUX)
39
40
const int CHILD_STACK_SIZE = 256 * 1024;
41
42
struct ChildArgs {
43
    const char* cmd;
44
    int pipe_fd0;
45
    int pipe_fd1;
46
};
47
48
0
int launch_child_process(void* args) {
49
0
    ChildArgs* cargs = (ChildArgs*)args;
50
0
    dup2(cargs->pipe_fd1, STDOUT_FILENO);
51
0
    close(cargs->pipe_fd0);
52
0
    close(cargs->pipe_fd1);
53
0
    execl("/bin/sh", "sh", "-c", cargs->cmd, NULL);
54
0
    _exit(1);
55
0
}
56
57
0
int read_command_output_through_clone(std::ostream& os, const char* cmd) {
58
0
    int pipe_fd[2];
59
0
    if (pipe(pipe_fd) != 0) {
60
0
        PLOG(ERROR) << "Fail to pipe";
61
0
        return -1;
62
0
    }
63
0
    int saved_errno = 0;
64
0
    int wstatus = 0;
65
0
    pid_t cpid;
66
0
    int rc = 0;
67
0
    ChildArgs args = { cmd, pipe_fd[0], pipe_fd[1] };
68
0
    char buffer[1024];
69
70
0
    char* child_stack = NULL;
71
0
    char* child_stack_mem = (char*)malloc(CHILD_STACK_SIZE);
72
0
    if (!child_stack_mem) {
73
0
        LOG(ERROR) << "Fail to alloc stack for the child process";
74
0
        rc = -1;
75
0
        goto END;
76
0
    }
77
0
    child_stack = child_stack_mem + CHILD_STACK_SIZE;  
78
                               // ^ Assume stack grows downward
79
0
    cpid = clone(launch_child_process, child_stack,
80
0
                 __WCLONE | CLONE_VM | SIGCHLD | CLONE_UNTRACED, &args);
81
0
    if (cpid < 0) {
82
0
        PLOG(ERROR) << "Fail to clone child process";
83
0
        rc = -1;
84
0
        goto END;
85
0
    }
86
0
    close(pipe_fd[1]);
87
0
    pipe_fd[1] = -1;
88
89
0
    for (;;) {
90
0
        const ssize_t nr = read(pipe_fd[0], buffer, sizeof(buffer));
91
0
        if (nr > 0) {
92
0
            os.write(buffer, nr);
93
0
            continue;
94
0
        } else if (nr == 0) {
95
0
            break;
96
0
        } else if (errno != EINTR) {
97
0
            LOG(ERROR) << "Encountered error while reading for the pipe";
98
0
            break;
99
0
        }
100
0
    }
101
102
0
    close(pipe_fd[0]);
103
0
    pipe_fd[0] = -1;
104
105
0
    for (;;) {
106
0
        pid_t wpid = waitpid(cpid, &wstatus, WNOHANG | __WALL);
107
0
        if (wpid > 0) {
108
0
            break;
109
0
        }
110
0
        if (wpid == 0) {
111
0
            if (bthread_usleep != NULL) {
112
0
                bthread_usleep(1000);
113
0
            } else {
114
0
                usleep(1000);
115
0
            }
116
0
            continue;
117
0
        }
118
0
        rc = -1;
119
0
        goto END;
120
0
    }
121
122
0
    if (WIFEXITED(wstatus)) {
123
0
        rc = WEXITSTATUS(wstatus);
124
0
        goto END;
125
0
    }
126
127
0
    if (WIFSIGNALED(wstatus)) {
128
0
        os << "Child process(" << cpid << ") was killed by signal "
129
0
           << WTERMSIG(wstatus);
130
0
    }
131
132
0
    rc = -1;
133
0
    errno = ECHILD;
134
135
0
END:
136
0
    saved_errno = errno;
137
0
    if (child_stack_mem) {
138
0
        free(child_stack_mem);
139
0
    }
140
0
    if (pipe_fd[0] >= 0) {
141
0
        close(pipe_fd[0]);
142
0
    }
143
0
    if (pipe_fd[1] >= 0) {
144
0
        close(pipe_fd[1]);
145
0
    }
146
0
    errno = saved_errno;
147
0
    return rc;
148
0
}
149
150
DEFINE_bool(run_command_through_clone, false,
151
            "(Linux specific) Run command with clone syscall to "
152
            "avoid the costly page table duplication");
153
154
#endif // OS_LINUX
155
156
#include <stdio.h>
157
158
0
int read_command_output_through_popen(std::ostream& os, const char* cmd) {
159
0
    FILE* pipe = popen(cmd, "r");
160
0
    if (pipe == NULL) {
161
0
        return -1;
162
0
    }
163
0
    char buffer[1024];
164
0
    for (;;) {
165
0
        size_t nr = fread(buffer, 1, sizeof(buffer), pipe);
166
0
        if (nr != 0) {
167
0
            os.write(buffer, nr);
168
0
        }
169
0
        if (nr != sizeof(buffer)) {
170
0
            if (feof(pipe)) {
171
0
                break;
172
0
            } else if (ferror(pipe)) {
173
0
                LOG(ERROR) << "Encountered error while reading for the pipe";
174
0
                break;
175
0
            }
176
            // retry;
177
0
        }
178
0
    }
179
180
0
    const int wstatus = pclose(pipe);
181
182
0
    if (wstatus < 0) {
183
0
        return wstatus;
184
0
    }
185
0
    if (WIFEXITED(wstatus)) {
186
0
        return WEXITSTATUS(wstatus);
187
0
    }
188
0
    if (WIFSIGNALED(wstatus)) {
189
0
        os << "Child process was killed by signal "
190
0
           << WTERMSIG(wstatus);
191
0
    }
192
0
    errno = ECHILD;
193
0
    return -1;
194
0
}
195
196
0
int read_command_output(std::ostream& os, const char* cmd) {
197
#if !defined(OS_LINUX)
198
    return read_command_output_through_popen(os, cmd);
199
#else
200
0
    return FLAGS_run_command_through_clone
201
0
        ? read_command_output_through_clone(os, cmd)
202
0
        : read_command_output_through_popen(os, cmd);
203
0
#endif
204
0
}
205
206
}  // namespace butil