Subversion Repositories HomeAutomation

Rev

Rev 1649 | Rev 1657 | 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::ClientId client_id_;
  113.     std::string prompt_;
  114.    
  115.    
  116.     void SlotOnNewStateHandler(net::ClientId client_id, net::ServerId server_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.            
  125.             kill(getpid(), SIGTERM);
  126.         }
  127.     }
  128.    
  129.     void SlotOnNewDataHandler(net::ClientId client_id, net::ServerId server_id, common::Byteset data)
  130.     {
  131.         std::string s(data.ToCharString());
  132.  
  133.         while (s.length() > 0)
  134.         {
  135.             std::string command = s.substr(0, 4);
  136.             unsigned int payload_length = boost::lexical_cast<unsigned int>(s.substr(4, 4));
  137.            
  138.             if (command == "TEXT")
  139.             {
  140.                 std::cout << s.substr(8, payload_length - 1) << std::flush;
  141.             }
  142.             else if (command == "PROM")
  143.             {
  144.                 this->prompt_ = s.substr(8, payload_length - 1);
  145.                 on_message_condition.notify_all();
  146.             }
  147.             else if (command == "COMP")
  148.             {
  149.                 autocomplete_list.clear();
  150.                
  151.                 if (payload_length > 0)
  152.                 {
  153.                     std::string payload = s.substr(8, payload_length - 1);
  154.                    
  155.                     if (payload.length() > 0)
  156.                     {
  157.                         boost::algorithm::split(autocomplete_list, payload, boost::is_any_of("\n"), boost::algorithm::token_compress_on);
  158.                     }
  159.                 }
  160.                
  161.                 on_message_condition.notify_all();
  162.             }
  163.             else
  164.             {
  165.                 std::cerr << "Unknown data received: " << s << std::endl;
  166.                 finish = true;
  167.                 on_message_condition.notify_all();
  168.                 break;
  169.             }
  170.            
  171.             if (8 + payload_length >= s.length())
  172.             {
  173.                 break;
  174.             }
  175.            
  176.             s = s.substr(8 + payload_length);
  177.         }
  178.     }
  179. };
  180.  
  181. ConsoleClient::Pointer cc;
  182.  
  183.  
  184. int main(int argc, char **argv)
  185. {
  186.     // Signal handlers
  187.     signal(SIGTERM, Handler);
  188.     signal(SIGINT, Handler);
  189.     signal(SIGQUIT, Handler);
  190.     signal(SIGABRT, Handler);
  191.     signal(SIGPIPE, Handler);
  192.    
  193.     boost::mutex guard_mutex;
  194.    
  195.     // Setup readline
  196.     history_filename = GetUserHomeDirectory() + "/.atomic_history";
  197.     read_history(history_filename.data());
  198.    
  199.     rl_attempted_completion_function = AutoComplete;
  200.    
  201.     // Save command line state
  202.     tcgetattr(fileno(stdin), &original_flags);
  203.    
  204.     // Parse commandline
  205.     boost::program_options::options_description command_line;
  206.     boost::program_options::variables_map variable_map;
  207.    
  208.     command_line.add_options()
  209.     ("help,h",    "produce help message")
  210.     ("server,s",  boost::program_options::value<std::string>()->default_value("localhost"), "server address")
  211.     ("port,p",    boost::program_options::value<unsigned int>()->default_value(1202), "server port");
  212.    
  213.     try
  214.     {
  215.         boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(command_line).run(), variable_map);
  216.     }
  217.     catch (boost::program_options::unknown_option e)
  218.     {
  219.         std::cerr << e.what() << std::endl;
  220.         std::cout << command_line << std::endl;
  221.         CleanUp();
  222.         return EXIT_FAILURE;
  223.     }
  224.     catch (boost::program_options::invalid_syntax e)
  225.     {
  226.         std::cerr << e.what() << std::endl;
  227.         std::cout << command_line << std::endl;
  228.         CleanUp();
  229.         return EXIT_FAILURE;
  230.     }
  231.    
  232.     if (variable_map.count("help") != 0)
  233.     {
  234.         std::cout << command_line << std::endl;
  235.         CleanUp();
  236.         return EXIT_SUCCESS;
  237.     }
  238.    
  239.    
  240.     std::cout << "Atom Interactive Console, version " + std::string(VERSION) + " starting..." << std::endl;
  241.     std::cout << "Written by Mattias Runge 2010." << std::endl;
  242.     std::cout << "Released under GPL version 2." << std::endl;
  243.    
  244.     net::Manager::Create();
  245.        
  246.     std::cout << "Connecting to " << variable_map["server"].as<std::string>().data() << ":" << variable_map["port"].as<unsigned int>() << "..." << std::endl;
  247.    
  248.     try
  249.     {
  250.         cc = ConsoleClient::Pointer(new ConsoleClient(variable_map["server"].as<std::string>(), variable_map["port"].as<unsigned int>()));
  251.     }
  252.     catch (std::runtime_error& e)
  253.     {
  254.         std::cout << "error!" << std::endl;
  255.         std::cerr << e.what() << std::endl;
  256.         CleanUp();
  257.         return EXIT_FAILURE;
  258.     }
  259.    
  260.     while (true)
  261.     {
  262.         boost::mutex::scoped_lock guard(guard_mutex);
  263.         on_message_condition.wait(guard);
  264.        
  265.         if (finish)
  266.         {
  267.             break;
  268.         }
  269.        
  270.         while ((buffer = readline(cc->GetPrompt().data())) != NULL)
  271.         {
  272.             if (strlen(buffer) == 0)
  273.             {
  274.                 continue;
  275.             }
  276.            
  277.             break;
  278.         }
  279.        
  280.         cc->SendResponse(buffer);
  281.         add_history(buffer);
  282.     }
  283.    
  284.     CleanUp();
  285.    
  286.     return EXIT_SUCCESS;
  287. }
  288.  
  289. static char** AutoComplete(const char* text, int start, int end)
  290. {
  291.     unsigned int count = 0;
  292.     boost::mutex guard_mutex;
  293.  
  294.     for (unsigned int n = 0; n < start; n++)
  295.     {
  296.         if (rl_line_buffer[n] == ' ')
  297.         {
  298.             count++;
  299.         }
  300.     }
  301.    
  302.     cc->AutoCompleteRequest(count, rl_line_buffer);
  303.    
  304.     boost::mutex::scoped_lock guard(guard_mutex);
  305.     on_message_condition.wait(guard);
  306.    
  307.     /*if (autocomplete_list.size() == 0)
  308.     {
  309.         return NULL;
  310.     }*/
  311.    
  312.     return rl_completion_matches(text, &AutoCompleteGet);
  313. }
  314.  
  315. char* AutoCompleteGet(const char* text, int state)
  316. {
  317.     static int index = 0;
  318.     int length = strlen(text);
  319.     std::string name;
  320.    
  321.     if (!state) // First run
  322.     {
  323.         index = 0;
  324.     }
  325.    
  326.     while (autocomplete_list.size() > index)
  327.     {
  328.         name = autocomplete_list[index];
  329.        
  330.         index++;
  331.        
  332.         if (strncmp(name.data(), text, length) == 0)
  333.         {
  334.             char *result = (char*)malloc(name.size() + 1);
  335.             strcpy(result, name.data());
  336.            
  337.             return result;
  338.         }
  339.     }
  340.    
  341.     return NULL;
  342. }
  343.  
  344. void CleanUp()
  345. {
  346.     std::cout << "Cleaning up..." << std::endl;
  347.  
  348.     write_history(history_filename.data());
  349.    
  350.     cc.reset();
  351.    
  352.     net::Manager::Delete();
  353.    
  354.     if (buffer != NULL)
  355.     {
  356.         free(buffer);
  357.     }
  358.    
  359.     std::cout << "Thank you for using Atom. Goodbye!" << std::endl;
  360.    
  361.     tcsetattr(fileno(stdin), TCSANOW, &original_flags); // Restore
  362. }
  363.  
  364. void Handler(int status)
  365. {
  366.     std::string signal_name = "Unknown";
  367.    
  368.     switch (status)
  369.     {
  370.         case SIGTERM:
  371.         {
  372.             signal_name = "Terminate";
  373.             break;
  374.         }  
  375.         case SIGINT:
  376.         {
  377.             signal_name = "Interupt";
  378.             break;
  379.         }  
  380.         case SIGQUIT:
  381.         {
  382.             signal_name = "Quit";
  383.             break;
  384.         }  
  385.         case SIGABRT:
  386.         {
  387.             signal_name = "Abort";
  388.             break;
  389.         }  
  390.         case SIGIO:
  391.         {
  392.             signal_name = "I/O";
  393.             break;
  394.         }  
  395.         case SIGPIPE:
  396.         {
  397.             signal_name = "Pipe";
  398.             break;
  399.         }
  400.     }
  401.    
  402.    
  403.     if (status != SIGPIPE)
  404.     {
  405.         CleanUp();
  406.         exit(0);
  407.     }
  408. }