1 /* 2 * Copyright (C) 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "wificond/tests/shell_utils.h" 18 19 #include <fcntl.h> 20 #include <poll.h> 21 #include <signal.h> 22 #include <string.h> 23 #include <sys/types.h> 24 #include <sys/wait.h> 25 #include <time.h> 26 #include <unistd.h> 27 28 #include <android-base/logging.h> 29 #include <android-base/unique_fd.h> 30 31 using android::base::unique_fd; 32 33 namespace android { 34 namespace wificond { 35 namespace tests { 36 namespace integration { 37 namespace { 38 39 #ifdef __ANDROID__ 40 const char kShellPath[] = "/system/bin/sh"; 41 #else 42 const char kShellPath[] = "/bin/sh"; 43 #endif 44 45 const int kShellTimeoutMs = 30 * 1000; 46 const int kMillisecondsPerSecond = 1000; 47 const int kNanosecondsPerMillisecond = 1000 * 1000; 48 49 // Represents some arbitrary, non-decreasing time in milliseconds. 50 int64_t GetCurrentTimeMs() { 51 struct timespec ts; 52 clock_gettime(CLOCK_MONOTONIC, &ts); 53 return (int64_t{ts.tv_sec} * kMillisecondsPerSecond) + 54 (ts.tv_nsec / kNanosecondsPerMillisecond); 55 } 56 57 } // namespace 58 59 int RunShellCommand(const std::string& shell_command, std::string* output) { 60 int fds[2]; 61 if (pipe2(fds, O_NONBLOCK) != 0) { 62 LOG(FATAL) << "Failed to create pipe"; 63 } 64 unique_fd read_fd(fds[0]); 65 unique_fd write_fd(fds[1]); 66 fcntl(read_fd.get(), F_SETFL, O_CLOEXEC | O_NONBLOCK); 67 68 const pid_t child_pid = fork(); 69 if (child_pid == -1) { 70 LOG(FATAL) << "Failed to fork child for shell command: " << shell_command; 71 } 72 73 if (child_pid == 0) { // We are in the child process. 74 close(0); // Don't want to read anything in this process. 75 dup2(write_fd.get(), 1); // Replace existing stdout with the pipe. 76 read_fd.reset(); 77 write_fd.reset(); 78 // Note that we're keeping parent stderr. 79 execl(kShellPath, "sh", "-c", shell_command.c_str(), nullptr); 80 LOG(FATAL) << "exec() of child failed " << strerror(errno); 81 } 82 83 // We are in the parent process. 84 write_fd.reset(); // Close this or we never get HUP from child. 85 struct pollfd shell_output; 86 memset(&shell_output, 0, sizeof(shell_output)); 87 shell_output.fd = read_fd.get(); 88 shell_output.events = POLLIN; 89 90 ssize_t nread; 91 char buf[512]; 92 int64_t start_time_ms = GetCurrentTimeMs(); 93 while (GetCurrentTimeMs() - start_time_ms < kShellTimeoutMs) { 94 int64_t time_left_ms = kShellTimeoutMs - (GetCurrentTimeMs() - start_time_ms); 95 poll(&shell_output, 1, (time_left_ms < 0) ? 0 : time_left_ms); 96 // Blindly read from this file descriptor until there is no data available. 97 do { 98 nread = TEMP_FAILURE_RETRY(read(shell_output.fd, buf, sizeof(buf))); 99 if (output && nread > 0) { 100 output->append(buf, nread); 101 } 102 } while (nread > 0); 103 104 // We're done if the child process has closed its stdout. 105 if (shell_output.revents & POLLHUP) { 106 break; 107 } 108 } 109 110 // Reap our child's exit status. 111 int wait_status = 0; 112 int waitpid_ret = 0; 113 start_time_ms = GetCurrentTimeMs(); 114 auto NeedToWaitForChild = [child_pid, &wait_status, &waitpid_ret]() { 115 if (waitpid_ret == 0) { 116 waitpid_ret = waitpid(child_pid, &wait_status, WNOHANG); 117 if (waitpid_ret == -1) { 118 LOG(ERROR) << "waitpid() returned -1 on error(" << errno << "): " 119 << strerror(errno); 120 } 121 } 122 return waitpid_ret == 0; 123 }; 124 125 start_time_ms = GetCurrentTimeMs(); 126 while (NeedToWaitForChild() && GetCurrentTimeMs() - start_time_ms < 1000) { 127 usleep(1000); 128 } 129 130 // Child still hasn't died. Send our child the big hammer. 131 if (waitpid_ret != child_pid) { 132 int kill_ret = kill(child_pid, SIGKILL); 133 // Allow kill to fail with ESRCH, since it indicated that the child may 134 // have already died. 135 if (kill_ret != 0 && errno != ESRCH) { 136 LOG(ERROR) << "Failed to send signal to child: " << strerror(errno); 137 } 138 139 // Wait for the child to die after receiving that signal. 140 start_time_ms = GetCurrentTimeMs(); 141 while (NeedToWaitForChild() && GetCurrentTimeMs() - start_time_ms < 1000) { 142 usleep(1000); 143 } 144 } 145 146 if (waitpid_ret == child_pid && WIFEXITED(wait_status)) { 147 return WEXITSTATUS(wait_status); 148 } 149 150 LOG(ERROR) << "Shell command timed out."; 151 return -1; 152 } 153 154 } // namespace integration 155 } // namespace tests 156 } // namespace wificond 157 } // namespace android 158