Subversion Repositories HomeAutomation

Rev

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