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