Subversion Repositories HomeAutomation

Rev

Rev 1986 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /*
  2.  *
  3.  *  Copyright (C) 2010  Mattias Runge
  4.  *
  5.  *  This program is free software; you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation; either version 2 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License along
  16.  *  with this program; if not, write to the Free Software Foundation, Inc.,
  17.  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  *
  19.  */
  20.  
  21. #include <string>
  22. #include <vector>
  23. #include <iostream>
  24.  
  25. #include <signal.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <pwd.h>
  29.  
  30. #include <boost/lexical_cast.hpp>
  31. #include <boost/thread/mutex.hpp>
  32. #include <boost/thread/condition.hpp>
  33. #include <boost/thread/locks.hpp>
  34. #include <boost/algorithm/string.hpp>
  35. #include <boost/program_options.hpp>
  36.  
  37. #include "config.h"
  38.  
  39. #include "net/Manager.h"
  40. #include "net/Subscriber.h"
  41. #include "net/types.h"
  42.  
  43. #include "common/common.h"
  44. #include "common/log.h"
  45.  
  46. #include <readline/readline.h>
  47. #include <readline/history.h>
  48.  
  49. using namespace atom;
  50.  
  51. bool finish = false;
  52. common::StringList autocomplete_list;
  53. char* buffer = NULL;
  54. boost::condition on_message_condition;
  55. struct termios original_flags;
  56. std::string history_filename;
  57.  
  58. void Handler(int status);
  59. void CleanUp();
  60.  
  61. char* AutoCompleteGet(const char* text, int state);
  62. static char** AutoComplete(const char* text, int start, int end);
  63.  
  64. std::string GetUserHomeDirectory()
  65. {
  66.     return std::string(getpwuid(getuid())->pw_dir);
  67. }
  68.  
  69. class ConsoleClient : public net::Subscriber
  70. {
  71. public:
  72.     typedef boost::shared_ptr<ConsoleClient> Pointer;
  73.    
  74.     ConsoleClient(std::string address, unsigned int port)
  75.     {
  76.         this->client_id_ = net::Manager::Instance()->Connect(net::PROTOCOL_TCP, address, port);
  77.     }
  78.    
  79.     virtual ~ConsoleClient()
  80.     {
  81.         net::Manager::Instance()->Disconnect(this->client_id_);
  82.  
  83.         this->io_service_.stop();
  84.     }
  85.    
  86.     std::string GetPrompt()
  87.     {
  88.         return this->prompt_;
  89.     }
  90.    
  91.     void SendResponse(std::string payload)
  92.     {
  93.         std::string packet = "RESP";
  94.         packet += common::PadNumber(payload.length() + 1, 4);
  95.         packet += payload;
  96.        
  97.         net::Manager::Instance()->SendTo(this->client_id_, common::Byteset(packet.begin(), packet.end()));
  98.     }
  99.    
  100.     void AutoCompleteRequest(unsigned int arg_index, std::string commandline)
  101.     {
  102.         std::string payload = common::PadNumber(arg_index, 4);
  103.         payload += commandline;
  104.        
  105.         std::string packet = "COMP";
  106.         packet += common::PadNumber(payload.length() + 1, 4);
  107.         packet += payload;
  108.        
  109.         net::Manager::Instance()->SendTo(this->client_id_, common::Byteset(packet.begin(), packet.end()));
  110.     }
  111.    
  112. private:
  113.     net::SocketId client_id_;
  114.     std::string prompt_;
  115.     std::vector<unsigned char> buffer_;
  116.    
  117.     void SlotOnNewStateHandler(net::SocketId client_id, net::ClientState client_state)
  118.     {
  119.         if (client_state != net::CLIENT_STATE_CONNECTED)
  120.         {
  121.             std::cout << "Disconnected from server." << std::endl;
  122.            
  123.             this->client_id_ = 0;
  124.             finish = true;
  125.             on_message_condition.notify_all();
  126.         }
  127.     }
  128.    
  129.     void SlotOnNewClientHandler(net::SocketId id, net::SocketId server_id)
  130.     {
  131.     }
  132.    
  133.     void SlotOnNewDataHandler(net::SocketId client_id, common::Byteset data)
  134.     {
  135.         for (unsigned int n = 0; n < data.size(); n++)
  136.         {
  137.             this->buffer_.push_back(data[n]);
  138.         }
  139.  
  140.         while (this->buffer_.size() >= 8)
  141.         {
  142.             std::string command = "";
  143.             command += (char)this->buffer_[0];
  144.             command += (char)this->buffer_[1];
  145.             command += (char)this->buffer_[2];
  146.             command += (char)this->buffer_[3];
  147.  
  148.             std::string payload_length_str = "";
  149.             payload_length_str += (char)this->buffer_[4];
  150.             payload_length_str += (char)this->buffer_[5];
  151.             payload_length_str += (char)this->buffer_[6];
  152.             payload_length_str += (char)this->buffer_[7];
  153.            
  154.             unsigned int payload_length = boost::lexical_cast<unsigned int>(payload_length_str) - 1;
  155.            
  156.             if (this->buffer_.size() - 8 < payload_length)
  157.             {
  158.                 return;
  159.             }
  160.          
  161.             std::string payload = "";
  162.             for (unsigned int n = 8; n < payload_length + 8; n++)
  163.             {
  164.                 payload += (char)this->buffer_[n];
  165.             }
  166.            
  167.             this->buffer_.erase(this->buffer_.begin(), this->buffer_.begin() + payload_length + 8);
  168.            
  169.             if (command == "TEXT")
  170.             {
  171.                 std::cout << payload << std::flush;
  172.             }
  173.             else if (command == "PROM")
  174.             {
  175.                 this->prompt_ = payload;
  176.                 on_message_condition.notify_all();
  177.             }
  178.             else if (command == "COMP")
  179.             {
  180.                 autocomplete_list.clear();
  181.                
  182.                 if (payload.length() > 0)
  183.                 {
  184.                     boost::algorithm::split(autocomplete_list, payload, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
  185.                 }
  186.                
  187.                 on_message_condition.notify_all();
  188.             }
  189.             else
  190.             {
  191.                 std::cerr << "Could not parse package." << std::endl;
  192.                 finish = true;
  193.                 on_message_condition.notify_all();
  194.                 break;
  195.             }
  196.         }
  197.     }
  198. };
  199.  
  200. ConsoleClient::Pointer cc;
  201.  
  202.  
  203. int main(int argc, char **argv)
  204. {
  205.     // Signal handlers
  206.     signal(SIGTERM, Handler);
  207.     signal(SIGINT, Handler);
  208.     signal(SIGQUIT, Handler);
  209.     signal(SIGABRT, Handler);
  210.     signal(SIGPIPE, Handler);
  211.    
  212.     boost::mutex guard_mutex;
  213.    
  214.     // Setup readline
  215.     history_filename = GetUserHomeDirectory() + "/.atomic_history";
  216.     read_history(history_filename.data());
  217.    
  218.     rl_attempted_completion_function = AutoComplete;
  219.    
  220.     // Save command line state
  221.     tcgetattr(fileno(stdin), &original_flags);
  222.    
  223.     // Parse commandline
  224.     boost::program_options::options_description command_line;
  225.     boost::program_options::variables_map variable_map;
  226.    
  227.     //log::SetLevel(log::LOG_LEVEL_ALL);
  228.    
  229.     command_line.add_options()
  230.     ("help,h",    "produce help message")
  231.     ("command,c", boost::program_options::value<std::string>()->default_value(""), "command")
  232.     ("server,s",  boost::program_options::value<std::string>()->default_value("localhost"), "server address")
  233.     ("port,p",    boost::program_options::value<unsigned int>()->default_value(1202), "server port");
  234.    
  235.     try
  236.     {
  237.         boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(command_line).run(), variable_map);
  238.     }
  239.     catch (boost::program_options::unknown_option e)
  240.     {
  241.         std::cerr << e.what() << std::endl;
  242.         std::cout << command_line << std::endl;
  243.         CleanUp();
  244.         return EXIT_FAILURE;
  245.     }
  246.     catch (boost::program_options::invalid_syntax e)
  247.     {
  248.         std::cerr << e.what() << std::endl;
  249.         std::cout << command_line << std::endl;
  250.         CleanUp();
  251.         return EXIT_FAILURE;
  252.     }
  253.    
  254.     if (variable_map.count("help") != 0)
  255.     {
  256.         std::cout << command_line << std::endl;
  257.         CleanUp();
  258.         return EXIT_SUCCESS;
  259.     }
  260.    
  261.     net::Manager::Create();
  262.  
  263.     if (variable_map["command"].as<std::string>() == "")
  264.     {
  265.         std::cout << "\033[29;1mAtom Interactive Console, version " + std::string(VERSION) + " starting...\033[0m" << std::endl;
  266.         std::cout << "\033[29;1mReleased under " + std::string(LICENSE) + ".\033[0m" << std::endl;
  267.         std::cout << "Written by Mattias Runge 2010." << std::endl;
  268.        
  269.         std::cout << "Connecting to " << variable_map["server"].as<std::string>().data() << ":" << variable_map["port"].as<unsigned int>() << "..." << std::endl;
  270.     }
  271.    
  272.     try
  273.     {
  274.         cc = ConsoleClient::Pointer(new ConsoleClient(variable_map["server"].as<std::string>(), variable_map["port"].as<unsigned int>()));
  275.     }
  276.     catch (std::runtime_error& e)
  277.     {
  278.         std::cerr << "Connection error: " << e.what() << std::endl;
  279.         CleanUp();
  280.         return EXIT_FAILURE;
  281.     }
  282.    
  283.     if (variable_map["command"].as<std::string>() != "")
  284.     {
  285.         boost::mutex::scoped_lock guard(guard_mutex);
  286.         on_message_condition.wait(guard);
  287.        
  288.         if (!finish)
  289.         {
  290.             cc->SendResponse(variable_map["command"].as<std::string>());
  291.            
  292.             if (!finish)
  293.             {
  294.                 on_message_condition.wait(guard);
  295.             }
  296.         }
  297.     }
  298.     else
  299.     {
  300.         while (true)
  301.         {
  302.             boost::mutex::scoped_lock guard(guard_mutex);
  303.             on_message_condition.wait(guard);
  304.            
  305.             if (finish)
  306.             {
  307.                 break;
  308.             }
  309.            
  310.             while ((buffer = readline(cc->GetPrompt().data())) != NULL)
  311.             {
  312.                 if (finish)
  313.                 {
  314.                     break;
  315.                 }
  316.                
  317.                 if (strlen(buffer) == 0)
  318.                 {
  319.                     continue;
  320.                 }
  321.                
  322.                 break;
  323.             }
  324.            
  325.             if (finish)
  326.             {
  327.                 break;
  328.             }
  329.            
  330.             cc->SendResponse(buffer);
  331.             add_history(buffer);
  332.         }
  333.     }
  334.    
  335.     CleanUp();
  336.    
  337.     return EXIT_SUCCESS;
  338. }
  339.  
  340. static char** AutoComplete(const char* text, int start, int end)
  341. {
  342.     unsigned int count = 0;
  343.     boost::mutex guard_mutex;
  344.  
  345.     for (unsigned int n = 0; n < (unsigned int)start; n++)
  346.     {
  347.         if (rl_line_buffer[n] == ' ')
  348.         {
  349.             count++;
  350.         }
  351.     }
  352.    
  353.     cc->AutoCompleteRequest(count, rl_line_buffer);
  354.    
  355.     boost::mutex::scoped_lock guard(guard_mutex);
  356.     on_message_condition.wait(guard);
  357.    
  358.     /*if (autocomplete_list.size() == 0)
  359.     {
  360.         return NULL;
  361.     }*/
  362.    
  363.     return rl_completion_matches(text, &AutoCompleteGet);
  364. }
  365.  
  366. char* AutoCompleteGet(const char* text, int state)
  367. {
  368.     static int index = 0;
  369.     int length = strlen(text);
  370.     std::string name;
  371.    
  372.     if (!state) // First run
  373.     {
  374.         index = 0;
  375.     }
  376.    
  377.     while (autocomplete_list.size() > (unsigned int) index)
  378.     {
  379.         name = autocomplete_list[index];
  380.        
  381.         index++;
  382.        
  383.         if (strncmp(name.data(), text, length) == 0)
  384.         {
  385.             char *result = (char*)malloc(name.size() + 1);
  386.             strcpy(result, name.data());
  387.            
  388.             return result;
  389.         }
  390.     }
  391.    
  392.     return NULL;
  393. }
  394.  
  395. void CleanUp()
  396. {
  397.     std::cout << std::endl;
  398.     //std::cout << "Cleaning up..." << std::endl;
  399.  
  400.     write_history(history_filename.data());
  401.    
  402.     cc.reset();
  403.    
  404.     net::Manager::Delete();
  405.    
  406.     if (buffer != NULL)
  407.     {
  408.         free(buffer);
  409.     }
  410.    
  411.     //std::cout << "Thank you for using Atom. Goodbye!" << std::endl;
  412.    
  413.     tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
  414. }
  415.  
  416. void Handler(int status)
  417. {
  418.     std::string signal_name = "Unknown";
  419.    
  420.     switch (status)
  421.     {
  422.         case SIGTERM:
  423.         {
  424.             signal_name = "Terminate";
  425.             break;
  426.         }  
  427.         case SIGINT:
  428.         {
  429.             signal_name = "Interrupt";
  430.             break;
  431.         }  
  432.         case SIGQUIT:
  433.         {
  434.             signal_name = "Quit";
  435.             break;
  436.         }  
  437.         case SIGABRT:
  438.         {
  439.             signal_name = "Abort";
  440.             break;
  441.         }  
  442.         case SIGIO:
  443.         {
  444.             signal_name = "I/O";
  445.             break;
  446.         }  
  447.         case SIGPIPE:
  448.         {
  449.             signal_name = "Pipe";
  450.             break;
  451.         }
  452.     }
  453.    
  454.    
  455.     if (status != SIGPIPE)
  456.     {
  457.         CleanUp();
  458.         exit(0);
  459.     }
  460. }
  461.  
  462.