• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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