// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // Author: Zhangyi Chen (chenzhangyi01@baidu.com) // Date: 2017/11/04 17:37:43 #include <gflags/gflags.h> #include "butil/build_config.h" #include "butil/logging.h" #if defined(OS_LINUX) // clone is a linux specific syscall #include <sched.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> extern "C" { uint64_t BAIDU_WEAK bthread_usleep(uint64_t microseconds); } #endif namespace butil { #if defined(OS_LINUX) const int CHILD_STACK_SIZE = 256 * 1024; struct ChildArgs { const char* cmd; int pipe_fd0; int pipe_fd1; }; int launch_child_process(void* args) { ChildArgs* cargs = (ChildArgs*)args; dup2(cargs->pipe_fd1, STDOUT_FILENO); close(cargs->pipe_fd0); close(cargs->pipe_fd1); execl("/bin/sh", "sh", "-c", cargs->cmd, NULL); _exit(1); } int read_command_output_through_clone(std::ostream& os, const char* cmd) { int pipe_fd[2]; if (pipe(pipe_fd) != 0) { PLOG(ERROR) << "Fail to pipe"; return -1; } int saved_errno = 0; int wstatus = 0; pid_t cpid; int rc = 0; ChildArgs args = { cmd, pipe_fd[0], pipe_fd[1] }; char buffer[1024]; char* child_stack = NULL; char* child_stack_mem = (char*)malloc(CHILD_STACK_SIZE); if (!child_stack_mem) { LOG(ERROR) << "Fail to alloc stack for the child process"; rc = -1; goto END; } child_stack = child_stack_mem + CHILD_STACK_SIZE; // ^ Assume stack grows downward cpid = clone(launch_child_process, child_stack, __WCLONE | CLONE_VM | SIGCHLD | CLONE_UNTRACED, &args); if (cpid < 0) { PLOG(ERROR) << "Fail to clone child process"; rc = -1; goto END; } close(pipe_fd[1]); pipe_fd[1] = -1; for (;;) { const ssize_t nr = read(pipe_fd[0], buffer, sizeof(buffer)); if (nr > 0) { os.write(buffer, nr); continue; } else if (nr == 0) { break; } else if (errno != EINTR) { LOG(ERROR) << "Encountered error while reading for the pipe"; break; } } close(pipe_fd[0]); pipe_fd[0] = -1; for (;;) { pid_t wpid = waitpid(cpid, &wstatus, WNOHANG | __WALL); if (wpid > 0) { break; } if (wpid == 0) { if (bthread_usleep != NULL) { bthread_usleep(1000); } else { usleep(1000); } continue; } rc = -1; goto END; } if (WIFEXITED(wstatus)) { rc = WEXITSTATUS(wstatus); goto END; } if (WIFSIGNALED(wstatus)) { os << "Child process(" << cpid << ") was killed by signal " << WTERMSIG(wstatus); } rc = -1; errno = ECHILD; END: saved_errno = errno; if (child_stack_mem) { free(child_stack_mem); } if (pipe_fd[0] >= 0) { close(pipe_fd[0]); } if (pipe_fd[1] >= 0) { close(pipe_fd[1]); } errno = saved_errno; return rc; } DEFINE_bool(run_command_through_clone, false, "(Linux specific) Run command with clone syscall to " "avoid the costly page table duplication"); #endif // OS_LINUX #include <stdio.h> int read_command_output_through_popen(std::ostream& os, const char* cmd) { FILE* pipe = popen(cmd, "r"); if (pipe == NULL) { return -1; } char buffer[1024]; for (;;) { size_t nr = fread(buffer, 1, sizeof(buffer), pipe); if (nr != 0) { os.write(buffer, nr); } if (nr != sizeof(buffer)) { if (feof(pipe)) { break; } else if (ferror(pipe)) { LOG(ERROR) << "Encountered error while reading for the pipe"; break; } // retry; } } const int wstatus = pclose(pipe); if (wstatus < 0) { return wstatus; } if (WIFEXITED(wstatus)) { return WEXITSTATUS(wstatus); } if (WIFSIGNALED(wstatus)) { os << "Child process was killed by signal " << WTERMSIG(wstatus); } errno = ECHILD; return -1; } int read_command_output(std::ostream& os, const char* cmd) { #if !defined(OS_LINUX) return read_command_output_through_popen(os, cmd); #else return FLAGS_run_command_through_clone ? read_command_output_through_clone(os, cmd) : read_command_output_through_popen(os, cmd); #endif } } // namespace butil