Subversion Repositories HomeAutomation

Rev

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