blob: a06fa0d0c3fc1eb88e418f504f54182dbf3fc4d8 [file] [log] [blame]
/*
* Copyright 2016 The Android Open Source Project
*
* Licensed 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.
*/
#include "ShellDriver.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <VtsDriverCommUtil.h>
#include <VtsDriverFileUtil.h>
#include "test/vts/proto/VtsDriverControlMessage.pb.h"
using namespace std;
namespace android {
namespace vts {
int VtsShellDriver::Close() {
cout << __func__ << endl;
int result = 0;
if (!this->socket_address_.empty()) {
result = unlink(this->socket_address_.c_str());
if (result != 0) {
cerr << __func__ << ":" << __LINE__
<< " ERROR closing socket (errno = " << errno << ")" << endl;
}
this->socket_address_.clear();
}
return result;
}
CommandResult* VtsShellDriver::ExecShellCommandPopen(const string& command) {
CommandResult* result = new CommandResult();
// TODO: handle no output case.
FILE* output_fp;
cout << "[Driver] Running command: " << command << endl << endl;
// execute the command.
output_fp = popen(command.c_str(), "r");
if (output_fp == NULL) {
cerr << "Failed to run command: " << command << endl;
result->exit_code = errno;
return result;
}
char buff[4096];
stringstream ss;
int bytes_read;
while (!feof(output_fp)) {
bytes_read = fread(buff, 1, sizeof(buff) - 1, output_fp);
// TODO: catch stderr
if (ferror(output_fp)) {
cerr << __func__ << ":" << __LINE__ << "ERROR reading shell output"
<< endl;
result->exit_code = -1;
return result;
}
cout << "[Driver] bytes read from output: " << bytes_read << endl;
buff[bytes_read] = '\0';
ss << buff;
}
cout << "[Driver] Returning output: " << ss.str() << endl << endl;
result->stdout = ss.str();
result->exit_code = pclose(output_fp) / 256;
return result;
}
CommandResult* VtsShellDriver::ExecShellCommandNohup(const string& command) {
CommandResult* result = new CommandResult();
string temp_dir = GetDirFromFilePath(this->socket_address_);
string temp_file_name_pattern = temp_dir + "/nohupXXXXXX";
int temp_file_name_len = temp_file_name_pattern.length() + 1;
char stdout_file_name[temp_file_name_len];
char stderr_file_name[temp_file_name_len];
strcpy(stdout_file_name, temp_file_name_pattern.c_str());
strcpy(stderr_file_name, temp_file_name_pattern.c_str());
int stdout_file = mkstemp(stdout_file_name);
int stderr_file = mkstemp(stderr_file_name);
close(stdout_file);
close(stderr_file);
stringstream ss;
ss << "nohup sh -c '" << command << "' >" << stdout_file_name << " 2>"
<< stderr_file_name;
// execute the command.
int exit_code = system(ss.str().c_str()) / 256;
result->exit_code = exit_code;
result->stdout = ReadFile(stdout_file_name);
result->stderr = ReadFile(stderr_file_name);
remove(stdout_file_name);
remove(stderr_file_name);
return result;
}
int VtsShellDriver::ExecShellCommand(
const string& command, VtsDriverControlResponseMessage* responseMessage) {
CommandResult* result = this->ExecShellCommandNohup(command);
responseMessage->add_stdout(result->stdout);
responseMessage->add_stderr(result->stderr);
int exit_code = result->exit_code;
responseMessage->add_exit_code(result->exit_code);
delete result;
return exit_code;
}
int VtsShellDriver::HandleShellCommandConnection(int connection_fd) {
VtsDriverCommUtil driverUtil(connection_fd);
VtsDriverControlCommandMessage cmd_msg;
int numberOfFailure = 0;
while (1) {
if (!driverUtil.VtsSocketRecvMessage(
static_cast<google::protobuf::Message*>(&cmd_msg))) {
cerr << "[Shell driver] receiving message failure." << endl;
return -1;
}
if (cmd_msg.command_type() == EXIT) {
cout << "[Shell driver] received exit command." << endl;
break;
} else if (cmd_msg.command_type() != EXECUTE_COMMAND) {
cerr << "[Shell driver] unknown command type " << cmd_msg.command_type()
<< endl;
continue;
}
cout << "[Shell driver] received " << cmd_msg.shell_command_size()
<< " command(s). Processing... " << endl;
// execute command and write back output
VtsDriverControlResponseMessage responseMessage;
for (const auto& command : cmd_msg.shell_command()) {
if (ExecShellCommand(command, &responseMessage) != 0) {
cerr << "[Shell driver] error during executing command [" << command
<< "]" << endl;
--numberOfFailure;
}
}
// TODO: other response code conditions
responseMessage.set_response_code(VTS_DRIVER_RESPONSE_SUCCESS);
if (!driverUtil.VtsSocketSendMessage(responseMessage)) {
fprintf(stderr, "Driver: write output to socket error.\n");
--numberOfFailure;
}
cout << "[Shell driver] finished processing commands." << endl;
}
if (driverUtil.Close() != 0) {
cerr << "[Driver] failed to close connection. errno: " << errno << endl;
--numberOfFailure;
}
return numberOfFailure;
}
int VtsShellDriver::StartListen() {
if (this->socket_address_.empty()) {
cerr << "[Driver] NULL socket address." << endl;
return -1;
}
cout << "[Driver] start listening on " << this->socket_address_ << endl;
struct sockaddr_un address;
int socket_fd, connection_fd;
socklen_t address_length;
pid_t child;
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (socket_fd < 0) {
cerr << "Driver: socket() failed: " << strerror(errno) << endl;
return socket_fd;
}
unlink(this->socket_address_.c_str());
memset(&address, 0, sizeof(struct sockaddr_un));
address.sun_family = AF_UNIX;
strncpy(address.sun_path, this->socket_address_.c_str(),
sizeof(address.sun_path) - 1);
if (::bind(socket_fd, (struct sockaddr*)&address,
sizeof(struct sockaddr_un)) != 0) {
cerr << "Driver: bind() failed: " << strerror(errno) << endl;
return 1;
}
if (listen(socket_fd, 5) != 0) {
cerr << "Driver: listen() failed: " << strerror(errno) << endl;
return errno;
}
while (1) {
address_length = sizeof(address);
// TODO(yuexima) exit message to break loop
connection_fd =
accept(socket_fd, (struct sockaddr*)&address, &address_length);
if (connection_fd == -1) {
cerr << "Driver: accept error: " << strerror(errno) << endl;
break;
}
child = fork();
if (child == 0) {
close(socket_fd);
// now inside newly created connection handling process
if (HandleShellCommandConnection(connection_fd) != 0) {
cerr << "[Driver] failed to handle connection." << endl;
close(connection_fd);
exit(1);
}
close(connection_fd);
exit(0);
} else if (child > 0) {
close(connection_fd);
} else {
cerr << "[Driver] create child process failed. Exiting..." << endl;
return (errno);
}
}
close(socket_fd);
return 0;
}
} // namespace vts
} // namespace android