1 /*
2 * Copyright 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 #define LOG_TAG "VtsShellDriver"
17
18 #include "ShellDriver.h"
19
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/un.h>
23 #include <sstream>
24
25 #include <VtsDriverCommUtil.h>
26 #include <VtsDriverFileUtil.h>
27 #include <android-base/logging.h>
28
29 #include "test/vts/proto/VtsDriverControlMessage.pb.h"
30
31 using namespace std;
32
33 // Threshold of serialized proto msg size sent over socket.
34 static constexpr long kProtoSizeThreshold = 1024 * 1024; // 1MB
35
36 namespace android {
37 namespace vts {
38
Close()39 int VtsShellDriver::Close() {
40 int result = 0;
41
42 if (!this->socket_address_.empty()) {
43 result = unlink(this->socket_address_.c_str());
44 if (result != 0) {
45 LOG(ERROR) << " ERROR closing socket (errno = " << errno << ")";
46 }
47 this->socket_address_.clear();
48 }
49
50 return result;
51 }
52
ExecShellCommandPopen(const string & command)53 CommandResult* VtsShellDriver::ExecShellCommandPopen(const string& command) {
54 CommandResult* result = new CommandResult();
55
56 // TODO: handle no output case.
57 FILE* output_fp;
58
59 // execute the command.
60 output_fp = popen(command.c_str(), "r");
61 if (output_fp == NULL) {
62 LOG(ERROR) << "Failed to run command: " << command;
63 result->exit_code = errno;
64 return result;
65 }
66
67 char buff[4096];
68 stringstream ss;
69
70 int bytes_read;
71 while (!feof(output_fp)) {
72 bytes_read = fread(buff, 1, sizeof(buff) - 1, output_fp);
73 // TODO: catch stderr
74 if (ferror(output_fp)) {
75 LOG(ERROR) << "ERROR reading shell output";
76 result->exit_code = -1;
77 return result;
78 }
79
80 buff[bytes_read] = '\0';
81 ss << buff;
82 }
83
84 LOG(DEBUG) << " Returning output: " << ss.str();
85 result->stdout = ss.str();
86
87 result->exit_code = pclose(output_fp) / 256;
88 return result;
89 }
90
ExecShellCommandNohup(const string & command)91 CommandResult* VtsShellDriver::ExecShellCommandNohup(const string& command) {
92 CommandResult* result = new CommandResult();
93
94 string temp_dir = GetDirFromFilePath(this->socket_address_);
95 string temp_file_name_pattern = temp_dir + "/nohupXXXXXX";
96 int temp_file_name_len = temp_file_name_pattern.length() + 1;
97 char stdout_file_name[temp_file_name_len];
98 char stderr_file_name[temp_file_name_len];
99 strcpy(stdout_file_name, temp_file_name_pattern.c_str());
100 strcpy(stderr_file_name, temp_file_name_pattern.c_str());
101 int stdout_file = mkstemp(stdout_file_name);
102 int stderr_file = mkstemp(stderr_file_name);
103 close(stdout_file);
104 close(stderr_file);
105
106 stringstream ss;
107 ss << "nohup sh -c '" << command << "' >" << stdout_file_name << " 2>"
108 << stderr_file_name;
109
110 // execute the command.
111 int exit_code = system(ss.str().c_str()) / 256;
112 result->exit_code = exit_code;
113
114 // If stdout size larger than threshold, send back the temp file path.
115 // Otherwise, send back the context directly.
116 long stdout_size = GetFileSize(stdout_file_name);
117 if (stdout_size > kProtoSizeThreshold) {
118 result->stdout = string(stdout_file_name);
119 } else {
120 result->stdout = ReadFile(stdout_file_name);
121 remove(stdout_file_name);
122 }
123
124 // If stderr size larger than threshold, send back the temp file path.
125 // Otherwise, send back the context directly.
126 long stderr_size = GetFileSize(stderr_file_name);
127 if (stderr_size > kProtoSizeThreshold) {
128 result->stderr = string(stderr_file_name);
129 } else {
130 result->stderr = ReadFile(stderr_file_name);
131 remove(stderr_file_name);
132 }
133
134 return result;
135 }
136
ExecShellCommand(const string & command,VtsDriverControlResponseMessage * responseMessage)137 int VtsShellDriver::ExecShellCommand(
138 const string& command, VtsDriverControlResponseMessage* responseMessage) {
139 CommandResult* result = this->ExecShellCommandNohup(command);
140
141 responseMessage->add_stdout(result->stdout);
142 responseMessage->add_stderr(result->stderr);
143
144 int exit_code = result->exit_code;
145 responseMessage->add_exit_code(result->exit_code);
146
147 delete result;
148 return exit_code;
149 }
150
HandleShellCommandConnection(int connection_fd)151 int VtsShellDriver::HandleShellCommandConnection(int connection_fd) {
152 VtsDriverCommUtil driverUtil(connection_fd);
153 VtsDriverControlCommandMessage cmd_msg;
154 int numberOfFailure = 0;
155
156 while (1) {
157 if (!driverUtil.VtsSocketRecvMessage(
158 static_cast<google::protobuf::Message*>(&cmd_msg))) {
159 LOG(ERROR) << "Receiving message failure.";
160 return -1;
161 }
162
163 if (cmd_msg.command_type() == EXIT) {
164 LOG(ERROR) << "Received exit command.";
165 break;
166 } else if (cmd_msg.command_type() != EXECUTE_COMMAND) {
167 LOG(ERROR) << "Unknown command type " << cmd_msg.command_type();
168 continue;
169 }
170 LOG(INFO) << "Received " << cmd_msg.shell_command_size()
171 << " command(s). Processing...";
172
173 // execute command and write back output
174 VtsDriverControlResponseMessage responseMessage;
175
176 for (const auto& command : cmd_msg.shell_command()) {
177 if (ExecShellCommand(command, &responseMessage) != 0) {
178 LOG(ERROR) << "Error during executing command [" << command << "]";
179 --numberOfFailure;
180 }
181 }
182
183 // TODO: other response code conditions
184 responseMessage.set_response_code(VTS_DRIVER_RESPONSE_SUCCESS);
185 if (!driverUtil.VtsSocketSendMessage(responseMessage)) {
186 LOG(ERROR) << "Write output to socket error.";
187 --numberOfFailure;
188 }
189 LOG(DEBUG) << "Finished processing commands.";
190 }
191
192 if (driverUtil.Close() != 0) {
193 LOG(ERROR) << "Failed to close connection. errno: " << errno;
194 --numberOfFailure;
195 }
196
197 return numberOfFailure;
198 }
199
StartListen()200 int VtsShellDriver::StartListen() {
201 if (this->socket_address_.empty()) {
202 LOG(ERROR) << "NULL socket address.";
203 return -1;
204 }
205
206 LOG(INFO) << "Start listening on " << this->socket_address_;
207
208 struct sockaddr_un address;
209 int socket_fd, connection_fd;
210 socklen_t address_length;
211 pid_t child;
212
213 socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
214 if (socket_fd < 0) {
215 PLOG(ERROR) << "socket() failed";
216 return socket_fd;
217 }
218
219 unlink(this->socket_address_.c_str());
220 memset(&address, 0, sizeof(struct sockaddr_un));
221 address.sun_family = AF_UNIX;
222 strncpy(address.sun_path, this->socket_address_.c_str(),
223 sizeof(address.sun_path) - 1);
224
225 if (::bind(socket_fd, (struct sockaddr*)&address,
226 sizeof(struct sockaddr_un)) != 0) {
227 PLOG(ERROR) << "bind() failed";
228 return 1;
229 }
230
231 if (listen(socket_fd, 5) != 0) {
232 PLOG(ERROR) << "listen() failed";
233 return errno;
234 }
235
236 while (1) {
237 address_length = sizeof(address);
238
239 // TODO(yuexima) exit message to break loop
240 connection_fd =
241 accept(socket_fd, (struct sockaddr*)&address, &address_length);
242 if (connection_fd == -1) {
243 PLOG(ERROR) << "accept failed";
244 break;
245 }
246
247 child = fork();
248 if (child == 0) {
249 close(socket_fd);
250 // now inside newly created connection handling process
251 if (HandleShellCommandConnection(connection_fd) != 0) {
252 LOG(ERROR) << "Failed to handle connection.";
253 close(connection_fd);
254 exit(1);
255 }
256 close(connection_fd);
257 exit(0);
258 } else if (child > 0) {
259 close(connection_fd);
260 } else {
261 LOG(ERROR) << "Create child process failed. Exiting...";
262 return (errno);
263 }
264 }
265 close(socket_fd);
266
267 return 0;
268 }
269
GetFileSize(const char * filename)270 long VtsShellDriver::GetFileSize(const char* filename) {
271 struct stat stat_buf;
272 int rc = stat(filename, &stat_buf);
273 return rc == 0 ? stat_buf.st_size : -1;
274 }
275
276 } // namespace vts
277 } // namespace android
278