Interactive mode and other improvements to vasum-cli tool 65/42065/8
authorLukasz Pawelczyk <l.pawelczyk@samsung.com>
Mon, 22 Jun 2015 12:03:15 +0000 (14:03 +0200)
committerLukasz Pawelczyk <l.pawelczyk@samsung.com>
Wed, 1 Jul 2015 10:02:29 +0000 (12:02 +0200)
[Feature]       These features have been implemented/updated:
                - interactive mode
                - persistent connection during interactive mode
                - better help
                - simple mode for cli auto-completion
                The interactive mode allows to make full use of queue
                lock/unlock commands. They can also be easily tested
                with it.
[Cause]         n/a
[Solution]      n/a
[Verification]  Run vasum-cli and executed several commands.
                Tested auto completion. Tested lock/unlock queue.

Change-Id: If623559c9250bf480cc600ba89c2d5fe02137a18

cli/CMakeLists.txt
cli/command-line-interface.cpp
cli/command-line-interface.hpp
cli/main.cpp
cli/support/vasum-cli-completion.sh.in
packaging/vasum.spec

index f8ad183..36750f0 100644 (file)
@@ -25,6 +25,23 @@ FILE(GLOB cli_SRCS *.cpp *.hpp)
 SET(CLI_CODENAME "${PROJECT_NAME}-cli")
 ADD_EXECUTABLE(${CLI_CODENAME} ${cli_SRCS})
 
+## Readline detection ##########################################################
+
+FIND_PATH(READLINE_INCLUDE_DIR readline/readline.h
+  HINTS ${READLINE_ROOT_DIR} PATH_SUFFIXES include)
+FIND_LIBRARY(READLINE_LIBRARY readline
+  HINTS ${READLINE_ROOT_DIR} PATH_SUFFIXES lib)
+FIND_LIBRARY(NCURSES_LIBRARY ncurses)   # readline depends on libncurses
+MARK_AS_ADVANCED(READLINE_INCLUDE_DIR READLINE_LIBRARY NCURSES_LIBRARY)
+
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG
+  READLINE_LIBRARY NCURSES_LIBRARY READLINE_INCLUDE_DIR)
+
+SET(READLINE_INCLUDE_DIRS ${READLINE_INCLUDE_DIR})
+SET(READLINE_LIBRARIES ${READLINE_LIBRARY} ${NCURSES_LIBRARY})
+INCLUDE_DIRECTORIES(${READLINE_INCLUDE_DIRS})
+TARGET_LINK_LIBRARIES(${CLI_CODENAME} ${READLINE_LIBRARIES})
 
 ## Link libraries ##############################################################
 PKG_CHECK_MODULES(LIB_DEPS REQUIRED vasum)
index a8b7d1b..2b56594 100644 (file)
@@ -45,43 +45,9 @@ using namespace std;
 namespace vasum {
 namespace cli {
 
-namespace {
-
-/**
- * Invoke specific function on VsmClient
- *
- * @param fun Function to be called. It must not throw any exception.
- */
-void one_shot(const function<VsmStatus(VsmClient)>& fun)
-{
-    string msg;
-    VsmStatus status;
-    VsmClient client;
-
-    client = vsm_client_create();
-    if (NULL == client) {
-        msg = "Can't create client";
-        goto finish;
-    }
-
-    status = vsm_connect(client);
-    if (VSMCLIENT_SUCCESS != status) {
-        msg = vsm_get_status_message(client);
-        goto finish;
-    }
-
-    status = fun(client);
-    if (VSMCLIENT_SUCCESS != status) {
-        msg = vsm_get_status_message(client);
-        goto finish;
-    }
+VsmClient CommandLineInterface::client = NULL;
 
-finish:
-    vsm_client_free(client);
-    if (! msg.empty()) {
-        throw runtime_error(msg);
-    }
-}
+namespace {
 
 template<typename T>
 string stringAsInStream(const T& value)
@@ -181,11 +147,83 @@ enum macvlan_mode macvlanFromString(const std::string& mode) {
 
 } // namespace
 
+void CommandLineInterface::connect()
+{
+    string msg;
+    VsmStatus status;
+
+    CommandLineInterface::client = vsm_client_create();
+    if (NULL == CommandLineInterface::client) {
+        msg = "Can't create client";
+        goto finish;
+    }
+
+    status = vsm_connect(client);
+    if (VSMCLIENT_SUCCESS != status) {
+        msg = vsm_get_status_message(CommandLineInterface::client);
+    }
+
+finish:
+    if (!msg.empty()) {
+        vsm_client_free(CommandLineInterface::client);
+        CommandLineInterface::client = NULL;
+        throw runtime_error(msg);
+    }
+}
+
+void CommandLineInterface::disconnect()
+{
+    string msg;
+    VsmStatus status;
+
+    status = vsm_disconnect(CommandLineInterface::client);
+    if (VSMCLIENT_SUCCESS != status) {
+        msg = vsm_get_status_message(CommandLineInterface::client);
+    }
+
+    vsm_client_free(CommandLineInterface::client);
+    CommandLineInterface::client = NULL;
+
+    if (!msg.empty()) {
+        throw runtime_error(msg);
+    }
+}
+
+void CommandLineInterface::executeCallback(const function<VsmStatus(VsmClient)>& fun)
+{
+    string msg;
+    VsmStatus status;
+
+    status = fun(CommandLineInterface::client);
+    if (VSMCLIENT_SUCCESS != status) {
+        msg = vsm_get_status_message(CommandLineInterface::client);
+    }
+
+    if (!msg.empty()) {
+        throw runtime_error(msg);
+    }
+}
+const std::string& CommandLineInterface::getName() const
+{
+    return mName;
+}
+
+const std::string& CommandLineInterface::getDescription() const
+{
+    return mDescription;
+}
+
 void CommandLineInterface::printUsage(std::ostream& out) const
 {
-    out << mUsage << "\n\n"
+    out << mName;
+    for (const auto& args : mArgsSpec) {
+        out << " " << args.first;
+    }
+
+    out << "\n\n"
         << "\tDescription\n"
-        << "\t\t" << mUsageInfo << "\n";
+        << "\t\t" << mDescription << "\n";
+
     if (!mArgsSpec.empty()) {
         out << "\n\tOptions\n";
         for (const auto& args : mArgsSpec) {
@@ -195,90 +233,109 @@ void CommandLineInterface::printUsage(std::ostream& out) const
     out << "\n";
 }
 
-void CommandLineInterface::execute(int pos, int argc, const char** argv)
+bool CommandLineInterface::isAvailable(unsigned int mode) const
 {
-    mExecutorCallback(pos, argc, argv);
+    return (mAvailability & mode) == mode;
 }
 
+void CommandLineInterface::execute(const Args& argv)
+{
+    mExecutorCallback(argv);
+}
+
+
+void lock_queue(const Args& /*argv*/)
+{
+    CommandLineInterface::executeCallback(vsm_lock_queue);
+}
 
-void set_active_zone(int pos, int argc, const char** argv)
+void unlock_queue(const Args& /*argv*/)
+{
+    CommandLineInterface::executeCallback(vsm_unlock_queue);
+}
+
+void set_active_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_set_active_zone, _1, argv[pos + 1]));
+    CommandLineInterface::executeCallback(bind(vsm_set_active_zone, _1, argv[1].c_str()));
 }
 
-void create_zone(int pos, int argc, const char** argv)
+void create_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_create_zone, _1, argv[pos + 1], argv[pos + 2] ? argv[pos + 2] : nullptr));
+    if (argv.size() >= 3 && !argv[2].empty()) {
+        CommandLineInterface::executeCallback(bind(vsm_create_zone, _1, argv[1].c_str(), argv[2].c_str()));
+    } else {
+        CommandLineInterface::executeCallback(bind(vsm_create_zone, _1, argv[1].c_str(), nullptr));
+    }
 }
 
-void destroy_zone(int pos, int argc, const char** argv)
+void destroy_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_destroy_zone, _1, argv[pos + 1], 1));
+    CommandLineInterface::executeCallback(bind(vsm_destroy_zone, _1, argv[1].c_str(), 1));
 }
 
-void shutdown_zone(int pos, int argc, const char** argv)
+void shutdown_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_shutdown_zone, _1, argv[pos + 1]));
+    CommandLineInterface::executeCallback(bind(vsm_shutdown_zone, _1, argv[1].c_str()));
 }
 
-void start_zone(int pos, int argc, const char** argv)
+void start_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_start_zone, _1, argv[pos + 1]));
+    CommandLineInterface::executeCallback(bind(vsm_start_zone, _1, argv[1].c_str()));
 }
 
-void lock_zone(int pos, int argc, const char** argv)
+void lock_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_lock_zone, _1, argv[pos + 1]));
+    CommandLineInterface::executeCallback(bind(vsm_lock_zone, _1, argv[1].c_str()));
 }
 
-void unlock_zone(int pos, int argc, const char** argv)
+void unlock_zone(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_unlock_zone, _1, argv[pos + 1]));
+    CommandLineInterface::executeCallback(bind(vsm_unlock_zone, _1, argv[1].c_str()));
 }
 
-void get_zones_status(int /* pos */, int /* argc */, const char** /* argv */)
+void get_zones_status(const Args& /* argv */)
 {
     using namespace std::placeholders;
 
@@ -286,12 +343,12 @@ void get_zones_status(int /* pos */, int /* argc */, const char** /* argv */)
     VsmString activeId;
     Table table;
 
-    one_shot(bind(vsm_get_zone_ids, _1, &ids));
-    one_shot(bind(vsm_get_active_zone_id, _1, &activeId));
+    CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
+    CommandLineInterface::executeCallback(bind(vsm_get_active_zone_id, _1, &activeId));
     table.push_back({"Active", "Id", "State", "Terminal", "Root"});
     for (VsmString* id = ids; *id; ++id) {
         VsmZone zone;
-        one_shot(bind(vsm_lookup_zone_by_id, _1, *id, &zone));
+        CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, *id, &zone));
         assert(string(zone->id) == string(*id));
         table.push_back({string(zone->id) == string(activeId) ? "*" : "",
                          zone->id,
@@ -305,12 +362,12 @@ void get_zones_status(int /* pos */, int /* argc */, const char** /* argv */)
     cout << table << endl;
 }
 
-void get_zone_ids(int /* pos */, int /* argc */, const char** /* argv */)
+void get_zone_ids(const Args& /*argv*/)
 {
     using namespace std::placeholders;
 
     VsmArrayString ids;
-    one_shot(bind(vsm_get_zone_ids, _1, &ids));
+    CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
     string delim;
     for (VsmString* id = ids; *id; ++id) {
         cout << delim << *id;
@@ -320,136 +377,136 @@ void get_zone_ids(int /* pos */, int /* argc */, const char** /* argv */)
     vsm_array_string_free(ids);
 }
 
-void get_active_zone_id(int /* pos */, int /* argc */, const char** /* argv */)
+void get_active_zone_id(const Args& /*argv*/)
 {
     using namespace std::placeholders;
 
     VsmString id;
-    one_shot(bind(vsm_get_active_zone_id, _1, &id));
+    CommandLineInterface::executeCallback(bind(vsm_get_active_zone_id, _1, &id));
     cout << id << endl;
     vsm_string_free(id);
 }
 
-void lookup_zone_by_id(int pos, int argc, const char** argv)
+void lookup_zone_by_id(const Args& argv)
 {
     using namespace std::placeholders;
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
 
     VsmZone zone;
-    one_shot(bind(vsm_lookup_zone_by_id, _1, argv[pos + 1], &zone));
+    CommandLineInterface::executeCallback(bind(vsm_lookup_zone_by_id, _1, argv[1].c_str(), &zone));
     cout << zone << endl;
     vsm_zone_free(zone);
 }
 
-void grant_device(int pos, int argc, const char** argv)
+void grant_device(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
 
     uint32_t flags = O_RDWR;
-    one_shot(bind(vsm_grant_device, _1, argv[pos + 1], argv[pos + 2], flags));
+    CommandLineInterface::executeCallback(bind(vsm_grant_device, _1, argv[1].c_str(), argv[2].c_str(), flags));
 }
 
-void revoke_device(int pos, int argc, const char** argv)
+void revoke_device(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
 
-    one_shot(bind(vsm_revoke_device, _1, argv[pos + 1], argv[pos + 2]));
+    CommandLineInterface::executeCallback(bind(vsm_revoke_device, _1, argv[1].c_str(), argv[2].c_str()));
 }
 
-void create_netdev_veth(int pos, int argc, const char** argv)
+void create_netdev_veth(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 3) {
+    if (argv.size() <= 3) {
         throw runtime_error("Not enough parameters");
     }
-    one_shot(bind(vsm_create_netdev_veth,
+    CommandLineInterface::executeCallback(bind(vsm_create_netdev_veth,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
-                  argv[pos + 3]));
+                  argv[1].c_str(),
+                  argv[2].c_str(),
+                  argv[3].c_str()));
 }
 
-void create_netdev_macvlan(int pos, int argc, const char** argv)
+void create_netdev_macvlan(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 4) {
+    if (argv.size() <= 4) {
         throw runtime_error("Not enough parameters");
     }
-    one_shot(bind(vsm_create_netdev_macvlan,
+    CommandLineInterface::executeCallback(bind(vsm_create_netdev_macvlan,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
-                  argv[pos + 3],
-                  macvlanFromString(argv[pos + 4])));
+                  argv[1].c_str(),
+                  argv[2].c_str(),
+                  argv[3].c_str(),
+                  macvlanFromString(argv[4].c_str())));
 }
 
-void create_netdev_phys(int pos, int argc, const char** argv)
+void create_netdev_phys(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
-    one_shot(bind(vsm_create_netdev_phys,
+    CommandLineInterface::executeCallback(bind(vsm_create_netdev_phys,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2]));
+                  argv[1].c_str(),
+                  argv[2].c_str()));
 }
 
-void lookup_netdev_by_name(int pos, int argc, const char** argv)
+void lookup_netdev_by_name(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
     VsmNetdev vsmNetdev = NULL;
-    one_shot(bind(vsm_lookup_netdev_by_name,
+    CommandLineInterface::executeCallback(bind(vsm_lookup_netdev_by_name,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
+                  argv[1].c_str(),
+                  argv[2].c_str(),
                   &vsmNetdev));
     cout << vsmNetdev << endl;
     vsm_netdev_free(vsmNetdev);
 
 }
 
-void destroy_netdev(int pos, int argc, const char** argv)
+void destroy_netdev(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
-    one_shot(bind(vsm_destroy_netdev,
+    CommandLineInterface::executeCallback(bind(vsm_destroy_netdev,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2]));
+                  argv[1].c_str(),
+                  argv[2].c_str()));
 }
 
-void zone_get_netdevs(int pos, int argc, const char** argv)
+void zone_get_netdevs(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 1) {
+    if (argv.size() <= 1) {
         throw runtime_error("Not enough parameters");
     }
     VsmArrayString ids;
-    one_shot(bind(vsm_zone_get_netdevs,
+    CommandLineInterface::executeCallback(bind(vsm_zone_get_netdevs,
                   _1,
-                  argv[pos + 1],
+                  argv[1].c_str(),
                   &ids));
     string delim;
     for (VsmString* id = ids; *id; ++id) {
@@ -463,18 +520,18 @@ void zone_get_netdevs(int pos, int argc, const char** argv)
     vsm_array_string_free(ids);
 }
 
-void netdev_get_ipv4_addr(int pos, int argc, const char** argv)
+void netdev_get_ipv4_addr(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
     in_addr addr;
-    one_shot(bind(vsm_netdev_get_ipv4_addr,
+    CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv4_addr,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
+                  argv[1].c_str(),
+                  argv[2].c_str(),
                   &addr));
     char buf[INET_ADDRSTRLEN];
     if (inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN) == NULL) {
@@ -483,18 +540,18 @@ void netdev_get_ipv4_addr(int pos, int argc, const char** argv)
     cout << buf << endl;
 }
 
-void netdev_get_ipv6_addr(int pos, int argc, const char** argv)
+void netdev_get_ipv6_addr(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
     in6_addr addr;
-    one_shot(bind(vsm_netdev_get_ipv6_addr,
+    CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv6_addr,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
+                  argv[1].c_str(),
+                  argv[2].c_str(),
                   &addr));
     char buf[INET6_ADDRSTRLEN];
     if (inet_ntop(AF_INET6, &addr, buf, INET6_ADDRSTRLEN) == NULL) {
@@ -503,68 +560,68 @@ void netdev_get_ipv6_addr(int pos, int argc, const char** argv)
     cout << buf << endl;
 }
 
-void netdev_set_ipv4_addr(int pos, int argc, const char** argv)
+void netdev_set_ipv4_addr(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 4) {
+    if (argv.size() <= 4) {
         throw runtime_error("Not enough parameters");
     }
     in_addr addr;
-    if (inet_pton(AF_INET, argv[pos + 3], &addr) != 1) {
+    if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) {
         throw runtime_error("Wrong address format");
     };
-    one_shot(bind(vsm_netdev_set_ipv4_addr,
+    CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv4_addr,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
+                  argv[1].c_str(),
+                  argv[2].c_str(),
                   &addr,
-                  stoi(argv[pos + 4])));
+                  stoi(argv[4].c_str())));
 }
 
-void netdev_set_ipv6_addr(int pos, int argc, const char** argv)
+void netdev_set_ipv6_addr(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 4) {
+    if (argv.size() <= 4) {
         throw runtime_error("Not enough parameters");
     }
     in6_addr addr;
-    if (inet_pton(AF_INET6, argv[pos + 3], &addr) != 1) {
+    if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) {
         throw runtime_error("Wrong address format");
     };
-    one_shot(bind(vsm_netdev_set_ipv6_addr,
+    CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv6_addr,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2],
+                  argv[1].c_str(),
+                  argv[2].c_str(),
                   &addr,
-                  stoi(argv[pos + 4])));
+                  stoi(argv[4].c_str())));
 }
 
-void netdev_up(int pos, int argc, const char** argv)
+void netdev_up(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
-    one_shot(bind(vsm_netdev_up,
+    CommandLineInterface::executeCallback(bind(vsm_netdev_up,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2]));
+                  argv[1].c_str(),
+                  argv[2].c_str()));
 }
 
-void netdev_down(int pos, int argc, const char** argv)
+void netdev_down(const Args& argv)
 {
     using namespace std::placeholders;
 
-    if (argc <= pos + 2) {
+    if (argv.size() <= 2) {
         throw runtime_error("Not enough parameters");
     }
-    one_shot(bind(vsm_netdev_down,
+    CommandLineInterface::executeCallback(bind(vsm_netdev_down,
                   _1,
-                  argv[pos + 1],
-                  argv[pos + 2]));
+                  argv[1].c_str(),
+                  argv[2].c_str()));
 }
 
 } // namespace cli
index 6e27c74..29282d9 100644 (file)
@@ -24,6 +24,8 @@
 #ifndef CLI_COMMAND_LINE_INTERFACE_HPP
 #define CLI_COMMAND_LINE_INTERFACE_HPP
 
+#include <vasum-client.h>
+
 #include <map>
 #include <vector>
 #include <functional>
 namespace vasum {
 namespace cli {
 
+#define MODE_COMMAND_LINE  (1 << 0)
+#define MODE_INTERACTIVE   (1 << 1)
+
+typedef std::vector<std::string> Args;
+
 /**
  * Class that implements command pattern.
  */
@@ -42,7 +49,7 @@ public:
     /**
      * @see CommandLineInterface::execute
      */
-    typedef std::function<void(int, int, const char**)> ExecutorCallback;
+    typedef std::function<void(const Args& argv)> ExecutorCallback;
 
     /**
      * @see CommandLineInterface::CommandLineInterface
@@ -52,7 +59,8 @@ public:
     /**
      * Dummy constructor (for stl usage)
      */
-    CommandLineInterface() {}
+    CommandLineInterface()
+        : mAvailability(0) {}
 
     /**
      *  Construct command
@@ -63,15 +71,48 @@ public:
      *  @param argsSpec Description of arguments
      */
     CommandLineInterface(const ExecutorCallback& executorCallback,
-                         const std::string& usage,
-                         const std::string& usageInfo,
+                         const std::string& name,
+                         const std::string& description,
+                         const unsigned int& availability,
                          const ArgsSpec& argsSpec)
         : mExecutorCallback(executorCallback),
-          mUsage(usage),
-          mUsageInfo(usageInfo),
+          mName(name),
+          mDescription(description),
+          mAvailability(availability),
           mArgsSpec(argsSpec) {}
 
     /**
+     * Set the class (static) in a connected state.
+     * This persistent connection to the vasum client is required
+     * for calls like lock/unlock queue to work.
+     */
+    static void connect();
+
+    /**
+     * Disconnect the class from a vasum client.
+     */
+    static void disconnect();
+
+    /**
+     * Execute a callback passing the connected VsmClient.
+     *
+     * @param fun A callback to execute
+     */
+    static void executeCallback(const std::function<VsmStatus(VsmClient)>& fun);
+
+    /**
+     * Get the command name
+     */
+    const std::string& getName() const;
+
+    /**
+     * Get the command description
+     *
+     * @param out Output stream
+     */
+    const std::string& getDescription() const;
+
+    /**
      * Print usage to stream
      *
      * @param out Output stream
@@ -79,6 +120,13 @@ public:
     void printUsage(std::ostream& out) const;
 
     /**
+     * Check if the command is available in specific mode
+     *
+     * @param mode A mode to check the command's availability in
+     */
+    bool isAvailable(unsigned int mode) const;
+
+    /**
      * Do the work
      *
      * It calls the callback passed in constructor
@@ -87,13 +135,16 @@ public:
      * @param argc Number of elements in argv
      * @param argv Command line arguments
      */
-    void execute(int pos, int argc, const char** argv);
+    void execute(const Args& argv);
 
 
 private:
+    static VsmClient client;
+
     const ExecutorCallback mExecutorCallback;
-    const std::string mUsage;
-    const std::string mUsageInfo;
+    const std::string mName;
+    const std::string mDescription;
+    const unsigned int mAvailability;
     const ArgsSpec mArgsSpec;
 };
 
@@ -102,174 +153,188 @@ private:
  *
  * @see vsm_set_active_zone
  */
-void set_active_zone(int pos, int argc, const char** argv);
+void lock_queue(const Args& argv);
+
+/**
+ * Parses command line arguments and call vsm_set_active_zone
+ *
+ * @see vsm_set_active_zone
+ */
+void unlock_queue(const Args& argv);
+
+/**
+ * Parses command line arguments and call vsm_set_active_zone
+ *
+ * @see vsm_set_active_zone
+ */
+void set_active_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_create_zone
  *
  * @see vsm_create_zone
  */
-void create_zone(int pos, int argc, const char** argv);
+void create_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_destroy_zone
  *
  * @see vsm_destroy_zone
  */
-void destroy_zone(int pos, int argc, const char** argv);
+void destroy_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_shutdown_zone
  *
  * @see vsm_shutdown_zone
  */
-void shutdown_zone(int pos, int argc, const char** argv);
+void shutdown_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_start_zone
  *
  * @see vsm_start_zone
  */
-void start_zone(int pos, int argc, const char** argv);
+void start_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_lock_zone
  *
  * @see vsm_lock_zone
  */
-void lock_zone(int pos, int argc, const char** argv);
+void lock_zone(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_unlock_zone
  *
  * @see vsm_unlock_zone
  */
-void unlock_zone(int pos, int argc, const char** argv);
+void unlock_zone(const Args& argv);
 
 /**
  * Parses command line arguments and prints list of zone with
  * some useful informations (id, state, terminal, root path)
  */
-void get_zones_status(int pos, int argc, const char** argv);
+void get_zones_status(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_get_zone_ids
  *
  * @see vsm_get_zone_ids
  */
-void get_zone_ids(int pos, int argc, const char** argv);
+void get_zone_ids(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_get_active_zone_id
  *
  * @see vsm_get_active_zone_id
  */
-void get_active_zone_id(int pos, int argc, const char** argv);
+void get_active_zone_id(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_lookup_zone_by_id
  *
  * @see vsm_lookup_zone_by_id
  */
-void lookup_zone_by_id(int pos, int argc, const char** argv);
+void lookup_zone_by_id(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_grant_device
  *
  * @see vsm_grant_device
  */
-void grant_device(int pos, int argc, const char** argv);
+void grant_device(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_revoke_device
  *
  * @see vsm_revoke_device
  */
-void revoke_device(int pos, int argc, const char** argv);
+void revoke_device(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_create_netdev_veth
  *
  * @see vsm_create_netdev_veth
  */
-void create_netdev_veth(int pos, int argc, const char** argv);
+void create_netdev_veth(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_create_netdev_macvlan
  *
  * @see vsm_create_netdev_macvlan
  */
-void create_netdev_macvlan(int pos, int argc, const char** argv);
+void create_netdev_macvlan(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_create_netdev_phys
  *
  * @see vsm_create_netdev_phys
  */
-void create_netdev_phys(int pos, int argc, const char** argv);
+void create_netdev_phys(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_lookup_netdev_by_name
  *
  * @see vsm_lookup_netdev_by_name
  */
-void lookup_netdev_by_name(int pos, int argc, const char** argv);
+void lookup_netdev_by_name(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_destroy_netdev
  *
  * @see vsm_destroy_netdev
  */
-void destroy_netdev(int pos, int argc, const char** argv);
+void destroy_netdev(const Args& argv);
 
 /**
  * Parses command line arguments and prints result of vsm_zone_get_netdevs
  *
  * @see vsm_zone_get_netdevs
  */
-void zone_get_netdevs(int pos, int argc, const char** argv);
+void zone_get_netdevs(const Args& argv);
 
 /**
  * Parses command line arguments and prints result of vsm_netdev_get_ipv4_addr
  *
  * @see vsm_netdev_get_ipv4_addr
  */
-void netdev_get_ipv4_addr(int pos, int argc, const char** argv);
+void netdev_get_ipv4_addr(const Args& argv);
 
 /**
  * Parses command line arguments and and prints result of vsm_netdev_get_ipv6_addr
  *
  * @see vsm_netdev_get_ipv6_addr
  */
-void netdev_get_ipv6_addr(int pos, int argc, const char** argv);
+void netdev_get_ipv6_addr(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_netdev_set_ipv4_addr
  *
  * @see vsm_netdev_set_ipv4_addr
  */
-void netdev_set_ipv4_addr(int pos, int argc, const char** argv);
+void netdev_set_ipv4_addr(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_netdev_set_ipv6_addr
  *
  * @see vsm_netdev_set_ipv6_addr
  */
-void netdev_set_ipv6_addr(int pos, int argc, const char** argv);
+void netdev_set_ipv6_addr(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_netdev_up
  *
  * @see vsm_netdev_up
  */
-void netdev_up(int pos, int argc, const char** argv);
+void netdev_up(const Args& argv);
 
 /**
  * Parses command line arguments and call vsm_netdev_down
  *
  * @see vsm_netdev_down
  */
-void netdev_down(int pos, int argc, const char** argv);
+void netdev_down(const Args& argv);
 
 } // namespace cli
 } // namespace vasum
index af36c74..dcb6891 100644 (file)
 #include <string>
 #include <iostream>
 #include <algorithm>
+#include <sstream>
+#include <iterator>
+#include <iomanip>
+#include <readline/readline.h>
+#include <readline/history.h>
 
 using namespace vasum::cli;
 
@@ -37,18 +42,38 @@ namespace {
 
 std::map<std::string, CommandLineInterface> commands = {
     {
+        "lock_queue", {
+            lock_queue,
+            "lock_queue",
+            "Exclusively lock the command queue",
+            MODE_INTERACTIVE,
+            {}
+        }
+    },
+    {
+        "unlock_queue", {
+            unlock_queue,
+            "unlock_queue",
+            "Unlock the queue",
+            MODE_INTERACTIVE,
+            {}
+        }
+    },
+    {
         "set_active_zone", {
             set_active_zone,
-            "set_active_zone zone_id",
+            "set_active_zone",
             "Set active (foreground) zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "create_zone", {
             create_zone,
-            "create_zone zone_id [zone_tname]",
+            "create_zone",
             "Create and add zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"[zone_tname]", "optional zone template name"}}
         }
@@ -56,40 +81,45 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "destroy_zone", {
             destroy_zone,
-            "destroy_zone zone_id",
+            "destroy_zone",
             "Destroy zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "shutdown_zone", {
             shutdown_zone,
-            "shutdown_zone zone_id",
+            "shutdown_zone",
             "Shutdown zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "start_zone", {
             start_zone,
-            "start_zone zone_id",
+            "start_zone",
             "Start zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "lock_zone", {
             lock_zone,
-            "lock_zone zone_id",
+            "lock_zone",
             "Lock zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "unlock_zone", {
             unlock_zone,
-            "unlock_zone zone_id",
+            "unlock_zone",
             "Unlock zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
@@ -98,6 +128,7 @@ std::map<std::string, CommandLineInterface> commands = {
             get_zones_status,
             "get_zones_status",
             "Get list of zone with some useful informations (id, state, terminal, root path)",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {}
         }
     },
@@ -106,6 +137,7 @@ std::map<std::string, CommandLineInterface> commands = {
             get_zone_ids,
             "get_zone_ids",
             "Get all zone ids",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {}
         }
     },
@@ -114,22 +146,25 @@ std::map<std::string, CommandLineInterface> commands = {
             get_active_zone_id,
             "get_active_zone_id",
             "Get active (foreground) zone ids",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {}
         }
     },
     {
         "lookup_zone_by_id", {
             lookup_zone_by_id,
-            "lookup_zone_by_id zone_id",
+            "lookup_zone_by_id",
             "Prints informations about zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "grant_device", {
             grant_device,
-            "grant_device zone_id device_name",
+            "grant_device",
             "Grants access to the given device",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"device_name", " device name"}}
         }
@@ -137,8 +172,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "revoke_device", {
             revoke_device,
-            "revoke_device zone_id device_name",
+            "revoke_device",
             "Revokes access to the given device",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"device_name", " device name"}}
         }
@@ -146,8 +182,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "create_netdev_veth", {
             create_netdev_veth,
-            "create_netdev_veth zone_id zone_netdev_id host_netdev_id",
+            "create_netdev_veth",
             "Create netdev in zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"zone_netdev_id", "network device id"},
              {"host_netdev_id", "host bridge id"}}
@@ -156,8 +193,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "create_netdev_macvlan", {
             create_netdev_macvlan,
-            "create_netdev_macvlan zone_id zone_netdev_id host_netdev_id mode",
+            "create_netdev_macvlan",
             "Create netdev in zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"zone_netdev_id", "network device id"},
              {"host_netdev_id", "host bridge id"},
@@ -167,8 +205,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "create_netdev_phys", {
             create_netdev_phys,
-            "create_netdev_phys zone_id netdev_id",
+            "create_netdev_phys",
             "Create/move netdev to zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"}}
         }
@@ -176,8 +215,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "lookup_netdev_by_name", {
             lookup_netdev_by_name,
-            "lookup_netdev_by_name zone_id netdev_id",
+            "lookup_netdev_by_name",
             "Get netdev flags",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"}}
         }
@@ -185,8 +225,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "destroy_netdev", {
             destroy_netdev,
-            "destroy_netdev zone_id devId",
+            "destroy_netdev",
             "Destroy netdev in zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"}}
         }
@@ -194,16 +235,18 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "zone_get_netdevs", {
             zone_get_netdevs,
-            "zone_get_netdevs zone_id",
+            "zone_get_netdevs",
             "List network devices in the zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"}}
         }
     },
     {
         "netdev_get_ipv4_addr", {
             netdev_get_ipv4_addr,
-            "netdev_get_ipv4_addr zone_id netdev_id",
+            "netdev_get_ipv4_addr",
             "Get ipv4 address",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"}}
         }
@@ -211,8 +254,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "netdev_get_ipv6_addr", {
             netdev_get_ipv6_addr,
-            "netdev_get_ipv6_addr zone_id netdev_id",
+            "netdev_get_ipv6_addr",
             "Get ipv6 address",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"}}
         }
@@ -220,8 +264,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "netdev_set_ipv4_addr", {
             netdev_set_ipv4_addr,
-            "netdev_set_ipv4_addr zone_id netdev_id address prefix_len",
+            "netdev_set_ipv4_addr",
             "Set ipv4 address",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"},
              {"address", "ipv4 address"},
@@ -231,8 +276,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "netdev_set_ipv6_addr", {
             netdev_set_ipv6_addr,
-            "netdev_set_ipv6_addr zone_id netdev_id address prefix_len",
+            "netdev_set_ipv6_addr",
             "Set ipv6 address",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device name"},
              {"address", "ipv6 address"},
@@ -242,8 +288,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "netdev_up", {
             netdev_up,
-            "netdev_up zone_id netdev_id",
+            "netdev_up",
             "Turn up a network device in the zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device id"}}
         }
@@ -251,8 +298,9 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "netdev_down", {
             netdev_down,
-            "netdev_down zone_id netdev_id",
+            "netdev_down",
             "Turn down a network device in the zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device id"}}
         }
@@ -260,50 +308,251 @@ std::map<std::string, CommandLineInterface> commands = {
     {
         "zone_get_netdevs", {
             zone_get_netdevs,
-            "zone_get_netdevs zone_id",
+            "zone_get_netdevs",
             "Turn down a network device in the zone",
+            MODE_COMMAND_LINE | MODE_INTERACTIVE,
             {{"zone_id", "id zone name"},
              {"netdev_id", "network device id"}}
         }
     }
 };
 
-void printUsage(std::ostream& out, const std::string& name)
+// wrappers for CommandLineInterface
+
+void printUsage(std::ostream& out, const std::string& name, unsigned int mode)
 {
-    out << "Usage: " << name << " [command [-h|args]]\n\n"
-        << "command can be one of the following:\n";
+    std::string n;
+    if (!name.empty()) {
+        n = name + " ";
+    }
+
+    out << "Usage: " << n << "[<command>] [-h|<args>]\n\n";
+    if (mode == MODE_COMMAND_LINE) {
+        out << "Called without parameters enters interactive mode.\n\n";
+    }
+    out << "command can be one of the following:\n";
 
     for (const auto& command : commands) {
-        command.second.printUsage(out);
+        if (command.second.isAvailable(mode)) {
+            out << "   " << std::setw(30) << std::left << command.second.getName()
+                << command.second.getDescription() << "\n";
+        }
     }
+
+    out << "\nSee " << n << "command -h to read about a specific one.\n";
 }
 
-} // namespace
+int connect()
+{
+    try {
+        CommandLineInterface::connect();
+    } catch (const std::runtime_error& ex) {
+        std::cerr << ex.what() << std::endl;
+        return EXIT_FAILURE;
+    }
 
-int main(const int argc, const char** argv)
+    return EXIT_SUCCESS;
+}
+
+int disconnect()
 {
-    if (argc < 2) {
-        printUsage(std::cout, argv[0]);
+    try {
+        CommandLineInterface::disconnect();
+    } catch (const std::runtime_error& ex) {
+        std::cerr << ex.what() << std::endl;
         return EXIT_FAILURE;
     }
-    if (commands.count(argv[1]) == 0) {
-        printUsage(std::cout, argv[0]);
+
+    return EXIT_SUCCESS;
+}
+
+int executeCommand(const Args& argv, int mode)
+{
+    auto pair = commands.find(argv[0]);
+    if (pair == commands.end()) {
+        return EXIT_FAILURE;
+    }
+
+    CommandLineInterface& command = pair->second;
+    if (!command.isAvailable(mode)) {
         return EXIT_FAILURE;
     }
 
-    CommandLineInterface& command = commands[argv[1]];
-    auto it = std::find(argv, argv+argc, std::string("-h"));
-    if (it != argv + argc) {
+    auto it = std::find(argv.begin(), argv.end(), std::string("-h"));
+    if (it != argv.end()) {
             command.printUsage(std::cout);
             return EXIT_SUCCESS;
     }
 
     try {
-        command.execute(1, argc, argv);
+        command.execute(argv);
     } catch (const std::runtime_error& ex) {
         std::cerr << ex.what() << std::endl;
         return EXIT_FAILURE;
     }
+
+    return EXIT_SUCCESS;
+}
+
+// readline completion support
+
+char* object_generator(const char* text, int state)
+{
+    static std::vector<std::string> objs;
+    static size_t len = 0, index = 0;
+
+    if (state == 0) {
+        objs.clear();
+        len = ::strlen(text);
+        index = 0;
+
+        using namespace std::placeholders;
+        VsmArrayString ids = NULL;
+        try {
+            CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids));
+        } catch (const std::runtime_error& ex) {
+            // Quietly ignore, nothing we can do anyway
+        }
+
+        if (ids != NULL) {
+            for (VsmString* id = ids; *id; ++id) {
+                if (::strncmp(text, *id, len) == 0) {
+                    objs.push_back(*id);
+                }
+            }
+
+            vsm_array_string_free(ids);
+        }
+    }
+
+    if (index < objs.size()) {
+        return ::strdup(objs[index++].c_str());
+    }
+
+    return NULL;
+}
+
+char* cmd_generator(const char* text, int state)
+{
+    static std::vector<std::string> cmds;
+    static size_t len = 0, index = 0;
+
+    if (state == 0) {
+        cmds.clear();
+        len = ::strlen(text);
+        index = 0;
+
+        for (const auto& command : commands) {
+            if (command.second.isAvailable(MODE_INTERACTIVE)) {
+                const std::string& cmd = command.second.getName();
+                if (::strncmp(text, cmd.c_str(), len) == 0) {
+                    cmds.push_back(cmd);
+                }
+            }
+        }
+    }
+
+    if (index < cmds.size()) {
+        return ::strdup(cmds[index++].c_str());
+    }
+
+    return NULL;
+}
+
+char** completion(const char* text, int start, int /*end*/)
+{
+    char **matches = NULL;
+
+    if (start == 0) {
+        matches = ::rl_completion_matches(text, &cmd_generator);
+    } else {
+        matches = ::rl_completion_matches(text, &object_generator);
+    }
+
+    return matches;
+}
+
+// handlers for the modes
+
+int interactiveMode()
+{
+    if (connect() != EXIT_SUCCESS)
+        return EXIT_FAILURE;
+
+    ::rl_attempted_completion_function = completion;
+
+    for (;;) {
+        char *line = ::readline(">>> ");
+
+        if (line == NULL) {
+            break;
+        }
+        if (line[0] == '\0') {
+            free(line);
+            continue;
+        }
+
+        std::istringstream iss(line);
+        Args argv{std::istream_iterator<std::string>{iss},
+                  std::istream_iterator<std::string>{}};
+
+        if (commands.count(argv[0]) == 0) {
+            printUsage(std::cout, "", MODE_INTERACTIVE);
+            free(line);
+            continue;
+        }
+
+        executeCommand(argv, MODE_INTERACTIVE);
+        ::add_history(line);
+        free(line);
+    }
+
+    disconnect();
     return EXIT_SUCCESS;
 }
 
+int bashComplMode()
+{
+    for (const auto& command : commands) {
+        if (command.second.isAvailable(MODE_COMMAND_LINE)) {
+            std::cout << command.second.getName() << "\n";
+        }
+    }
+
+    return EXIT_SUCCESS;
+}
+
+int cliMode(const int argc, const char** argv)
+{
+    if (commands.count(argv[1]) == 0) {
+        printUsage(std::cout, argv[0], MODE_COMMAND_LINE);
+        return EXIT_FAILURE;
+    }
+
+    if (connect() != EXIT_SUCCESS) {
+        return EXIT_FAILURE;
+    }
+
+    // pass all the arguments excluding argv[0] - the executable name
+    Args commandArgs(argv+1, argv+argc);
+    int rc = executeCommand(commandArgs, MODE_COMMAND_LINE);
+
+    disconnect();
+    return rc;
+}
+
+} // namespace
+
+
+int main(const int argc, const char** argv)
+{
+    if (argc == 1) {
+        return interactiveMode();
+    }
+
+    if (std::string(argv[1]) == "--bash-completion") {
+        return bashComplMode();
+    }
+
+    return cliMode(argc, argv);
+}
index 18dda54..e04b56f 100755 (executable)
@@ -6,7 +6,7 @@ __@PROJECT_NAME@_cli() {
 
     COMPREPLY=()
     if [ "$COMP_CWORD" == "1" ]; then
-        COMPREPLY=($(compgen -W "$(@CLI_CODENAME@ | grep --color=never -e '^\S' | tail -n +3 | cut -f1 -d' ')" -- $cur))
+        COMPREPLY=($(compgen -W "$(@CLI_CODENAME@ --bash-completion)" -- $cur))
     elif [ "$COMP_CWORD" == "2" ]; then
         COMPREPLY=($(compgen -W "-h" -- $cur))
     fi
index 13112b5..acb7ada 100644 (file)
@@ -23,6 +23,7 @@ BuildRequires:  cmake
 BuildRequires:  boost-devel
 BuildRequires:  pkgconfig(glib-2.0)
 BuildRequires:  lxc-devel
+BuildRequires:  readline-devel
 Requires:       lxc
 %if %{platform_type} == "TIZEN"
 Requires:       iproute2