Home | History | Annotate | Download | only in shell
      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 
     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 
     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 
     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 
    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 
    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 
    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