From 44461ead945d681a7941e0d2a2dc10b7103330f7 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Wed, 29 Jul 2015 12:03:08 +0200 Subject: [PATCH 01/16] Fix pkg-config files from returning wrong relative paths. [Bug] The pkg-config files were using wrong relative paths [Cause] CMAKE_INSTALL_* variables are relative, cmake could handle them internally for INSTALL and similar, but for pkgconfig understands them literally. [Solution] Use absolute paths everywhere. [Verification] Check the content of *.pc files after make install. Change-Id: I2a14d0f9ddc45238be7e8d487d186df1877730bd --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5203a6a..735b349 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,15 +161,15 @@ IF(NOT DEFINED SYSCONF_INSTALL_DIR) ENDIF(NOT DEFINED SYSCONF_INSTALL_DIR) IF(NOT DEFINED LIB_INSTALL_DIR) - SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") ENDIF(NOT DEFINED LIB_INSTALL_DIR) IF(NOT DEFINED INCLUDE_INSTALL_DIR) - SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}") + SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") ENDIF(NOT DEFINED INCLUDE_INSTALL_DIR) IF(NOT DEFINED SCRIPT_INSTALL_DIR) - SET(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_SBINDIR}") + SET(SCRIPT_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}") ENDIF(NOT DEFINED SCRIPT_INSTALL_DIR) IF(NOT DEFINED SYSTEMD_UNIT_DIR) -- 2.7.4 From cb894f96478a1a9817ea45c04e9e95bf7b5e2b33 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 23 Jul 2015 16:27:57 +0200 Subject: [PATCH 02/16] Make vasum-cli arguments shorter/nicer with autocomplete, rename tool vasum-cli to vsm [Feature] Shorter and nicer arguments for command line tool [Cause] Messy underscore, too many commands [Solution] More generic commands, shorter, easier to enter names [Verification] Build, install, use vsm with various arguments Try autocompletion for arguments Change-Id: I2e8d1bff3a5dcf96df1250b29b8df55c8f23171d --- cli/CMakeLists.txt | 8 +- cli/command-line-interface.cpp | 344 +++++++++++--------- cli/command-line-interface.hpp | 81 ++--- cli/main.cpp | 576 ++++++++++++++------------------- cli/support/vasum-cli-completion.sh.in | 15 - cli/support/vsm-completion.sh.in | 10 + common/netlink/netlink.cpp | 8 +- packaging/vasum.spec | 4 +- server/netdev.cpp | 49 +-- 9 files changed, 500 insertions(+), 595 deletions(-) delete mode 100755 cli/support/vasum-cli-completion.sh.in create mode 100755 cli/support/vsm-completion.sh.in diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt index fe4b067..0f4fbfe 100644 --- a/cli/CMakeLists.txt +++ b/cli/CMakeLists.txt @@ -24,7 +24,7 @@ FILE(GLOB cli_SRCS *.cpp *.hpp ${COMMON_FOLDER}/utils/c-array.hpp) ## Setup target ################################################################ -SET(CLI_CODENAME "${PROJECT_NAME}-cli") +SET(CLI_CODENAME "vsm") ADD_EXECUTABLE(${CLI_CODENAME} ${cli_SRCS}) ## Readline detection ########################################################## @@ -52,11 +52,11 @@ INCLUDE_DIRECTORIES(${CLIENT_FOLDER}) INCLUDE_DIRECTORIES(${COMMON_FOLDER}) TARGET_LINK_LIBRARIES(${CLI_CODENAME} ${PROJECT_NAME}-client ${LIB_DEPS_LIBRARIES} Ipc) -CONFIGURE_FILE(support/vasum-cli-completion.sh.in - ${CMAKE_BINARY_DIR}/vasum-cli-completion.sh +CONFIGURE_FILE(support/vsm-completion.sh.in + ${CMAKE_BINARY_DIR}/vsm-completion.sh @ONLY) ## Install ##################################################################### INSTALL(TARGETS ${CLI_CODENAME} DESTINATION bin) -INSTALL(FILES ${CMAKE_BINARY_DIR}/vasum-cli-completion.sh +INSTALL(FILES ${CMAKE_BINARY_DIR}/vsm-completion.sh DESTINATION ${SYSCONF_INSTALL_DIR}/bash_completion.d) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 1338afe..e8922ee 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -36,11 +36,12 @@ #include #include #include -#include #include +#include #include #include #include +#include using namespace std; @@ -72,15 +73,6 @@ std::string zoneStateToString(const VsmZoneState& state) return name; } -std::string zoneToString(const VsmZone& zone) -{ - std::string out = std::string("Name: ") + vsm_zone_get_id(zone) - + std::string("\nTerminal: ") + std::to_string(vsm_zone_get_terminal(zone)) - + std::string("\nState: ") + zoneStateToString(vsm_zone_get_state(zone)) - + std::string("\nRoot: ") + vsm_zone_get_rootfs(zone); - return out; -} - std::string netdevTypeToString(const VsmNetdevType& netdevType) { std::string type; @@ -139,14 +131,61 @@ enum macvlan_mode macvlanFromString(const std::string& mode) { throw runtime_error("Unsupported macvlan mode"); } +void buildZoneList(std::vector& list) +{ + using namespace std::placeholders; + VsmArrayString ids; + + CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids)); + for (VsmString* id = ids; *id; ++id) { + list.push_back(*id); + } + vsm_array_string_free(ids); +} + } // namespace +const std::vector CommandLineInterface::buildCompletionList(const Args& a) const +{ + std::vector v; + + if (a.size() > mArgsSpec.size() + 1) { + return v; + } + + ArgSpec as = mArgsSpec[a.size() - 2]; + string::size_type s = 0U; + string::size_type e = s; + while (e != string::npos) { + e = as.format.find('|', s); + std::string ss = as.format.substr(s, e - s); + s = e + 1; + + if (ss == "{ZONE}") { + buildZoneList(v); + } + else if (ss == "{NETDEV}") { + //TODO: get list of available interfaces + v.push_back("lo"); + v.push_back("eth0"); + } + else if (ss.length() > 0) { + v.push_back(ss); + } + } + + return v; +} + void CommandLineInterface::connect() { VsmStatus status; + if (CommandLineInterface::client != nullptr) { + return; + } CommandLineInterface::client = vsm_client_create(); - if (NULL == CommandLineInterface::client) { + if (CommandLineInterface::client == nullptr) { throw runtime_error("Can't create client"); } @@ -154,7 +193,7 @@ void CommandLineInterface::connect() if (VSMCLIENT_SUCCESS != status) { string msg = vsm_get_status_message(CommandLineInterface::client); vsm_client_free(CommandLineInterface::client); - CommandLineInterface::client = NULL; + CommandLineInterface::client = nullptr; throw runtime_error(msg); } } @@ -164,13 +203,17 @@ void CommandLineInterface::disconnect() string msg; VsmStatus status; + if (CommandLineInterface::client == nullptr) { + return ; + } + status = vsm_disconnect(CommandLineInterface::client); if (VSMCLIENT_SUCCESS != status) { msg = vsm_get_status_message(CommandLineInterface::client); } vsm_client_free(CommandLineInterface::client); - CommandLineInterface::client = NULL; + CommandLineInterface::client = nullptr; if (VSMCLIENT_SUCCESS != status) { throw runtime_error(msg); @@ -179,9 +222,9 @@ void CommandLineInterface::disconnect() void CommandLineInterface::executeCallback(const function& fun) { - VsmStatus status; + CommandLineInterface::connect(); - status = fun(CommandLineInterface::client); + VsmStatus status = fun(CommandLineInterface::client); if (VSMCLIENT_SUCCESS != status) { throw runtime_error(vsm_get_status_message(CommandLineInterface::client)); } @@ -201,7 +244,7 @@ void CommandLineInterface::printUsage(std::ostream& out) const { out << mName; for (const auto& args : mArgsSpec) { - out << " " << args.first; + out << " " << args.name; } out << "\n\n" @@ -211,7 +254,7 @@ void CommandLineInterface::printUsage(std::ostream& out) const if (!mArgsSpec.empty()) { out << "\n\tOptions\n"; for (const auto& args : mArgsSpec) { - out << "\t\t" << args.first << " -- " << args.second << "\n"; + out << "\t\t" << args.name << " -- " << args.description << "\n"; } } out << "\n"; @@ -222,7 +265,7 @@ bool CommandLineInterface::isAvailable(unsigned int mode) const return (mAvailability & mode) == mode; } -void CommandLineInterface::execute(const Args& argv) +void CommandLineInterface::execute(const Args& argv) const { mExecutorCallback(argv); } @@ -242,7 +285,7 @@ void set_active_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -253,7 +296,7 @@ void create_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -268,7 +311,7 @@ void destroy_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -279,7 +322,7 @@ void shutdown_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -290,7 +333,7 @@ void start_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -301,7 +344,7 @@ void console_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -310,7 +353,7 @@ void console_zone(const Args& argv) if (zoneStateToString(vsm_zone_get_state(zone)) != "RUNNING") { vsm_zone_free(zone); - throw runtime_error("Zone is not running"); + throw runtime_error("Zone '" + argv[1] + "' is not running"); } std::string zonesPath = vsm_zone_get_rootfs(zone); @@ -334,7 +377,7 @@ void lock_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } @@ -345,14 +388,14 @@ void unlock_zone(const Args& argv) { using namespace std::placeholders; - if (argv.size() <= 1) { + if (argv.size() < 2) { throw runtime_error("Not enough parameters"); } CommandLineInterface::executeCallback(bind(vsm_unlock_zone, _1, argv[1].c_str())); } -void get_zones_status(const Args& /* argv */) +void get_zones_status(const Args& argv) { using namespace std::placeholders; @@ -360,14 +403,24 @@ void get_zones_status(const Args& /* argv */) VsmString activeId; Table table; - CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids)); + if (argv.size() < 2) { + CommandLineInterface::executeCallback(bind(vsm_get_zone_ids, _1, &ids)); + } + else { + ids = reinterpret_cast(calloc(argv.size(), sizeof(char*))); + for (unsigned i = 1; i Args; +struct ArgSpec { + std::string name; + std::string description; + std::string format; +}; + /** * Class that implements command pattern. */ @@ -54,7 +60,7 @@ public: /** * @see CommandLineInterface::CommandLineInterface */ - typedef std::vector> ArgsSpec; + typedef std::vector ArgsSpec; /** * Dummy constructor (for stl usage) @@ -133,8 +139,9 @@ public: * * @param argv Command line arguments */ - void execute(const Args& argv); + void execute(const Args& argv) const; + const std::vector buildCompletionList(const Args& argv) const; private: static VsmClient client; @@ -233,14 +240,7 @@ void get_zone_ids(const Args& argv); * * @see vsm_get_active_zone_id */ -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(const Args& argv); +void get_active_zone(const Args& argv); /** * Parses command line arguments and call vsm_grant_device @@ -257,32 +257,11 @@ void grant_device(const Args& 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(const Args& argv); - -/** - * Parses command line arguments and call vsm_create_netdev_macvlan - * - * @see vsm_create_netdev_macvlan - */ -void create_netdev_macvlan(const Args& argv); - -/** - * Parses command line arguments and call vsm_create_netdev_phys + * Parses command line arguments and call vsm_create_netdev_* * - * @see vsm_create_netdev_phys + * @see vsm_create_netdev_veth,vsm_create_netdev_macvlan,vsm_create_netdev_phys */ -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(const Args& argv); +void create_netdev(const Args& argv); /** * Parses command line arguments and call vsm_destroy_netdev @@ -292,39 +271,27 @@ void lookup_netdev_by_name(const Args& 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(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(const Args& argv); - -/** - * Parses command line arguments and and prints result of vsm_netdev_get_ipv6_addr + * Parses command line arguments and prints result of vsm_zone_get_netdevs, + * vsm_lookup_netdev_by_name, vsm_netdev_get_ipv4_addr, vsm_netdev_get_ipv6_addr * - * @see vsm_netdev_get_ipv6_addr + * @see vsm_zone_get_netdevs, vsm_lookup_netdev_by_name, + * @see vsm_netdev_get_ipv4_addr, vsm_netdev_get_ipv6_addr */ -void netdev_get_ipv6_addr(const Args& argv); +void netdev_list(const Args& argv); /** - * Parses command line arguments and call vsm_netdev_set_ipv4_addr + * Parses command line arguments and call vsm_netdev_set_ipv4_addr, vsm_netdev_set_ipv6_addr * - * @see vsm_netdev_set_ipv4_addr + * @see vsm_netdev_set_ipv4_addr, vsm_netdev_set_ipv6_addr */ -void netdev_set_ipv4_addr(const Args& argv); +void netdev_add_ip_addr(const Args& argv); /** - * Parses command line arguments and call vsm_netdev_set_ipv6_addr + * Parses command line arguments and call vsm_netdev_del_ipv4_addr, vsm_netdev_del_ipv6_addr * - * @see vsm_netdev_set_ipv6_addr + * @see vsm_netdev_del_ipv4_addr, vsm_netdev_del_ipv6_addr */ -void netdev_set_ipv6_addr(const Args& argv); +void netdev_del_ip_addr(const Args& argv); /** * Parses command line arguments and call vsm_netdev_up diff --git a/cli/main.cpp b/cli/main.cpp index efff7c6..9bec3e7 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -45,305 +45,195 @@ using namespace vasum::cli; namespace { static int interactiveMode = 0; -std::map commands = { +std::vector 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", - "Set active (foreground) zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } - }, - { - "create_zone", { - create_zone, - "create_zone", - "Create and add zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"[zone_tname]", "optional zone template name"}} - } - }, - { - "destroy_zone", { - destroy_zone, - "destroy_zone", - "Destroy zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } - }, - { - "shutdown_zone", { - shutdown_zone, - "shutdown_zone", - "Shutdown zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } + create_zone, + "create", + "Create and add zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", ""}, + {"[zone_tname]", "optional zone template name", ""}} }, { - "start_zone", { - start_zone, - "start_zone", - "Start zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } + destroy_zone, + "destroy", + "Destroy zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "console_zone", { - console_zone, - "console_zone", - "Log into zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } - }, - { - "lock_zone", { - lock_zone, - "lock_zone", - "Lock zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } + start_zone, + "start", + "Start zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "unlock_zone", { - unlock_zone, - "unlock_zone", - "Unlock zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } + console_zone, + "console", + "Attach to zone text console", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "get_zones_status", { - get_zones_status, - "get_zones_status", - "Get list of zone with some useful informations (id, state, terminal, root path)", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {} - } + shutdown_zone, + "shutdown", + "Shutdown zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "get_zone_ids", { - get_zone_ids, - "get_zone_ids", - "Get all zone ids", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {} - } + lock_zone, + "suspend", + "Suspend (lock) zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "get_active_zone_id", { - get_active_zone_id, - "get_active_zone_id", - "Get active (foreground) zone ids", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {} - } + unlock_zone, + "resume", + "Resume (unlock) zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "lookup_zone_by_id", { - lookup_zone_by_id, - "lookup_zone_by_id", - "Prints informations about zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } + set_active_zone, + "set-active", + "Set active (foreground) zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}} }, { - "grant_device", { - grant_device, - "grant_device", - "Grants access to the given device", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"device_name", " device name"}} - } + get_active_zone, + "get-active", + "Get active (foreground) zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {} }, { - "revoke_device", { - revoke_device, - "revoke_device", - "Revokes access to the given device", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"device_name", " device name"}} - } + get_zone_ids, + "list", + "Get available zone ids", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {} }, { - "create_netdev_veth", { - create_netdev_veth, - "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"}} - } + get_zones_status, + "status", + "List status for one or all zones (id, state, terminal, root path)", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"[zone_id]", "zone name", "{ZONE}"}} }, { - "create_netdev_macvlan", { - create_netdev_macvlan, - "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"}, - {"mode", "macvlan mode (private, vepa, bridge, passthru)"}} - } + clean_up_zones_root, + "clean", + "Clean up zones root directory", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {} }, { - "create_netdev_phys", { - create_netdev_phys, - "create_netdev_phys", - "Create/move netdev to zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"netdev_id", "network device name"}} - } + grant_device, + "device-grant", + "Grants access to the given device", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"device", "device name", ""}} }, { - "lookup_netdev_by_name", { - lookup_netdev_by_name, - "lookup_netdev_by_name", - "Get netdev flags", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"netdev_id", "network device name"}} - } + revoke_device, + "device-revoke", + "Revokes access to the given device", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"device", "device name", ""}} }, { - "destroy_netdev", { - destroy_netdev, - "destroy_netdev", - "Destroy netdev in zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"netdev_id", "network device name"}} - } + create_netdev, + "net-create", + "Create network interface in zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + { + {"zone_id", "zone name", "{ZONE}"}, + {"netdevtype", "interface type", "macvlan|phys|veth"}, + {"zone_netdev", "interface name (eth0)", "eth0|eth1"}, + {"host_netdev", "bridge name (virbr0)", "virbr0|virbr1"}, + {"mode", "macvlan mode (private, vepa, bridge, passthru)", "private|vepa|bridge|passthru"}} }, { - "zone_get_netdevs", { - zone_get_netdevs, - "zone_get_netdevs", - "List network devices in the zone", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}} - } + destroy_netdev, + "net-destroy", + "Destroy netdev in zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"netdev", "interface name (eth0)", "{NETDEV}"}} }, { - "netdev_get_ipv4_addr", { - netdev_get_ipv4_addr, - "netdev_get_ipv4_addr", - "Get ipv4 address", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"netdev_id", "network device name"}} - } + netdev_list, + "net-list", + "List network devices in the zone", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"[netdev]", "interface name (eth0)", "{NETDEV}"}} }, { - "netdev_get_ipv6_addr", { - netdev_get_ipv6_addr, - "netdev_get_ipv6_addr", - "Get ipv6 address", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {{"zone_id", "id zone name"}, - {"netdev_id", "network device name"}} - } + netdev_up, + "net-up", + "Setup a network device in the zone up", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"netdev", "interface name (eth0)", "{NETDEV}"}} }, { - "netdev_set_ipv4_addr", { - netdev_set_ipv4_addr, - "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"}, - {"prefix_len", "bit length of prefix"}} - } + netdev_down, + "net-down", + "Setup a network device in the zone down", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"netdev", "interface name (eth0)", "{NETDEV}"}} }, { - "netdev_set_ipv6_addr", { - netdev_set_ipv6_addr, - "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"}, - {"prefix_len", "bit length of prefix"}} - } + netdev_add_ip_addr, + "net-ip-add", + "Add ip/mask address to network interface", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"netdev", "interface name (eth0)", "{NETDEV}"}, + {"ip", "address IPv4 or IPv6", ""}, + {"prefix", "mask length in bits", "24"}} }, { - "netdev_up", { - netdev_up, - "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"}} - } + netdev_del_ip_addr, + "net-ip-del", + "Del ip/mask address from network interface", + MODE_COMMAND_LINE | MODE_INTERACTIVE, + {{"zone_id", "zone name", "{ZONE}"}, + {"netdev", "interface name (eth0)", "{NETDEV}"}, + {"ip", "address IPv4 or IPv6", ""}, + {"prefix", "mask length in bits", "24"}} }, { - "netdev_down", { - netdev_down, - "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"}} - } + lock_queue, + "qlock", + "Exclusively lock the command queue", + MODE_INTERACTIVE, + {} }, { - "zone_get_netdevs", { - zone_get_netdevs, - "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"}} - } + unlock_queue, + "qunlock", + "Unlock the queue", + MODE_INTERACTIVE, + {} }, - { - "clean_up_zones_root", { - clean_up_zones_root, - "clean_up_zones_root", - "Clean up zones root directory", - MODE_COMMAND_LINE | MODE_INTERACTIVE, - {} - } - } }; +std::map commandMap; + // wrappers for CommandLineInterface void printUsage(std::ostream& out, const std::string& name, unsigned int mode) { + const std::vector addLineBefore = {"device-grant", "net-create", "qlock"}; std::string n; if (!name.empty()) { n = name + " "; @@ -361,9 +251,12 @@ void printUsage(std::ostream& out, const std::string& name, unsigned int mode) out << "command can be one of the following:\n"; for (const auto& command : commands) { - if (command.second.isAvailable(mode)) { - out << " " << std::setw(30) << std::left << command.second.getName() - << command.second.getDescription() << "\n"; + if (command.isAvailable(mode)) { + if (std::find(addLineBefore.begin(), addLineBefore.end(), command.getName()) != addLineBefore.end()) { + out << std::endl; + } + out << " " << std::setw(25) << std::left << command.getName() + << command.getDescription() << std::endl; } } @@ -396,13 +289,14 @@ int disconnect() int executeCommand(const Args& argv, int mode) { - auto pair = commands.find(argv[0]); - if (pair == commands.end()) { + auto pair = commandMap.find(argv[0]); + if (pair == commandMap.end()) { return EXIT_FAILURE; } - CommandLineInterface& command = pair->second; + const CommandLineInterface& command = pair->second; if (!command.isAvailable(mode)) { + std::cerr << "Command not available in this mode" << std::endl; return EXIT_FAILURE; } @@ -423,81 +317,43 @@ int executeCommand(const Args& argv, int mode) } // readline completion support - -char* object_generator(const char* text, int state) +const std::vector buildComplList(const Args& argv); +char *completion_generator(const char* text, int state) { - static std::vector objs; - static size_t len = 0, index = 0; - + static std::vector list; + static unsigned index = 0; if (state == 0) { - objs.clear(); - len = ::strlen(text); + list.clear(); 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); - } - } + char *ln = rl_line_buffer; + std::istringstream iss(ln); + Args argv{std::istream_iterator{iss}, + std::istream_iterator{}}; - vsm_array_string_free(ids); + size_t len = strlen(text); + if (len == 0 && argv.size() > 0) { + argv.push_back(""); } - } - if (index < objs.size()) { - return ::strdup(objs[index++].c_str()); - } - - return NULL; -} - -char* cmd_generator(const char* text, int state) -{ - static std::vector 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); - } + const std::vector& l = buildComplList(argv); + for (const auto &i : l) { + if (strncmp(text, i.c_str(), len) == 0) { + list.push_back(i); } } } - - if (index < cmds.size()) { - return ::strdup(cmds[index++].c_str()); + if (index < list.size()) { + return ::strdup(list[index++].c_str()); } return NULL; } -char** completion(const char* text, int start, int /*end*/) +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; + ::rl_attempted_completion_over = 1; //disable default completion + return ::rl_completion_matches(text, &completion_generator); } static bool readline_from(const std::string& prompt, std::istream& stream, std::string& ln) @@ -529,6 +385,7 @@ static int processStream(std::istream& stream) return EXIT_FAILURE; } + int rc = EXIT_FAILURE; std::string ln; while (readline_from(">>> ", stream, ln)) { if (ln.empty() || ln[0] == '#') { //skip empty line or comment @@ -539,16 +396,18 @@ static int processStream(std::istream& stream) Args argv{std::istream_iterator{iss}, std::istream_iterator{}}; - if (commands.count(argv[0]) == 0) { + if (commandMap.count(argv[0]) == 0) { printUsage(std::cout, "", MODE_INTERACTIVE); continue; } - executeCommand(argv, MODE_INTERACTIVE); + rc = executeCommand(argv, MODE_INTERACTIVE); + if (rc == EXIT_FAILURE && !interactiveMode) { + break; + } } - disconnect(); - return EXIT_SUCCESS; + return rc; } static int processFile(const std::string& fn) @@ -563,15 +422,43 @@ static int processFile(const std::string& fn) return processStream(stream); } -int bashComplMode() +void printList(const std::vector& list) { - for (const auto& command : commands) { - if (command.second.isAvailable(MODE_COMMAND_LINE)) { - std::cout << command.second.getName() << "\n"; + for (const auto& i : list) { + std::cout << i << std::endl; + } +} + +const std::vector buildComplList(const Args& argv) +{ + if (argv.size() < 2) { + std::vector list; + for (const auto& command : commands) { + if (command.isAvailable(MODE_COMMAND_LINE)) { + list.push_back(command.getName()); + } + } + return list; + } else { + std::string cmd = argv[0]; + if (commandMap.find(cmd) != commandMap.end()) { + return commandMap[cmd].buildCompletionList(argv); } + return std::vector(); } +} - return EXIT_SUCCESS; +int bashComplMode(int argc, const char *argv[]) +{ + int rc = EXIT_FAILURE; + try { + Args args(argv, argv + argc); + printList(buildComplList(args)); + rc = EXIT_SUCCESS; + } catch (const std::runtime_error& ex) { + } + + return rc; } int cliMode(const int argc, const char** argv) @@ -581,22 +468,15 @@ int cliMode(const int argc, const char** argv) return EXIT_SUCCESS; } - if (commands.find(argv[1]) == commands.end()) { + if (commandMap.find(argv[1]) == commandMap.end()) { printUsage(std::cout, argv[0], MODE_COMMAND_LINE); return EXIT_FAILURE; } - //TODO: Connect when it is necessary - //f.e. vasum-cli -h doesn't need connection - if (connect() != EXIT_SUCCESS) { - return EXIT_FAILURE; - } - // pass all the arguments excluding argv[0] - the executable name - Args commandArgs(argv+1, argv+argc); + Args commandArgs(argv + 1, argv + argc); int rc = executeCommand(commandArgs, MODE_COMMAND_LINE); - disconnect(); return rc; } @@ -605,26 +485,38 @@ int cliMode(const int argc, const char** argv) int main(const int argc, const char *argv[]) { + for (const auto& command : commands) { + commandMap.insert(std::pair(command.getName(),command)); + } + + int rc = EXIT_FAILURE; if (argc > 1) { + //process arguments if (std::string(argv[1]) == "--bash-completion") { - return bashComplMode(); - } - - if (std::string(argv[1]) == "-f") { + rc = bashComplMode(argc - 2, argv + 2); + } else if (std::string(argv[1]) == "-f") { if (argc < 3) { std::cerr << "Filename expected" << std::endl; - return EXIT_FAILURE; + rc = EXIT_FAILURE; + } + else { + rc = processFile(std::string(argv[2])); } - return processFile(std::string(argv[2])); + } else { + rc = cliMode(argc, argv); } - return cliMode(argc, argv); - } + } else { + + if (isatty(0) == 1) { + interactiveMode = 1; + ::rl_attempted_completion_function = completion; + } - if (isatty(0) == 1) { - interactiveMode = 1; - ::rl_attempted_completion_function = completion; + rc = processStream(std::cin); } - return processStream(std::cin); + + disconnect(); + return rc; } diff --git a/cli/support/vasum-cli-completion.sh.in b/cli/support/vasum-cli-completion.sh.in deleted file mode 100755 index e04b56f..0000000 --- a/cli/support/vasum-cli-completion.sh.in +++ /dev/null @@ -1,15 +0,0 @@ -# Check for bash -[ -z "$BASH_VERSION" ] && return - -__@PROJECT_NAME@_cli() { - local cur="${COMP_WORDS[COMP_CWORD]}" - - COMPREPLY=() - if [ "$COMP_CWORD" == "1" ]; then - COMPREPLY=($(compgen -W "$(@CLI_CODENAME@ --bash-completion)" -- $cur)) - elif [ "$COMP_CWORD" == "2" ]; then - COMPREPLY=($(compgen -W "-h" -- $cur)) - fi -} - -complete -F __@PROJECT_NAME@_cli @CLI_CODENAME@ diff --git a/cli/support/vsm-completion.sh.in b/cli/support/vsm-completion.sh.in new file mode 100755 index 0000000..1177348 --- /dev/null +++ b/cli/support/vsm-completion.sh.in @@ -0,0 +1,10 @@ +# Check for bash +[ -z "$BASH_VERSION" ] && return + +__@PROJECT_NAME@_cli() { + local exe=$1 cur=$2 prev=$3 + words=`@CLI_CODENAME@ --bash-completion "${COMP_WORDS[@]:1}"` + COMPREPLY=($(compgen -W "$words" -- $cur)) +} + +complete -F __@PROJECT_NAME@_cli vsm diff --git a/common/netlink/netlink.cpp b/common/netlink/netlink.cpp index b048031..dd11ef3 100644 --- a/common/netlink/netlink.cpp +++ b/common/netlink/netlink.cpp @@ -109,7 +109,7 @@ void Netlink::open(int netNsPid) mFd = utils::passNamespacedFd(netNsPid, CLONE_NEWNET, fdFactory); } if (mFd == -1) { - throw VasumException("Can't open netlink connection"); + throw VasumException("Can't open netlink connection (zone not running)"); } sockaddr_nl local = utils::make_clean(); @@ -178,13 +178,13 @@ std::unique_ptr> Netlink::rcv(unsigned int nlmsgSeq) // It is NACK/ACK message nlmsgerr *err = reinterpret_cast(NLMSG_DATA(answer)); if (answer->nlmsg_seq != nlmsgSeq) { - throw VasumException("Sending failed: answer message was mismatched"); + throw VasumException("Receive failed: answer message was mismatched"); } if (err->error) { - throw VasumException("Sending failed: " + getSystemErrorMessage(-err->error)); + throw VasumException("Receive failed: " + getSystemErrorMessage(-err->error)); } } else if (answer->nlmsg_type == NLMSG_OVERRUN) { - throw VasumException("Sending failed: data lost"); + throw VasumException("Receive failed: data lost"); } } if (lastOk == NULL) { diff --git a/packaging/vasum.spec b/packaging/vasum.spec index 0ec93da..22de326 100644 --- a/packaging/vasum.spec +++ b/packaging/vasum.spec @@ -243,7 +243,7 @@ Command Line Interface for vasum. %files cli %defattr(644,root,root,755) -%attr(755,root,root) %{_bindir}/vasum-cli +%attr(755,root,root) %{_bindir}/vsm %package cli-completion Summary: Vasum Command Line Interface bash completion @@ -255,7 +255,7 @@ Requires: vasum-cli = %{epoch}:%{version}-%{release} Command Line Interface bash completion for vasum. %files cli-completion -%attr(755,root,root) %{_sysconfdir}/bash_completion.d/vasum-cli-completion.sh +%attr(755,root,root) %{_sysconfdir}/bash_completion.d/vsm-completion.sh ## Test Package ################################################################ %package tests diff --git a/server/netdev.cpp b/server/netdev.cpp index 8c53d38..bf2576c 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -488,35 +488,42 @@ Attrs getAttrs(const pid_t nsPid, const std::string& netdev) infoPeer.ifi_change = 0xFFFFFFFF; nlm.put(infoPeer) .put(IFLA_IFNAME, netdev); - NetlinkResponse response = send(nlm, nsPid); - if (!response.hasMessage()) { - throw VasumException("Can't get interface information"); - } - response.fetch(infoPeer); - Attrs attrs; - while (response.hasAttribute()) { - uint32_t mtu, link; - int attrType = response.getAttributeType(); - switch (attrType) { - case IFLA_MTU: - response.fetch(IFLA_MTU, mtu); - attrs.push_back(make_tuple("mtu", std::to_string(mtu))); - break; - case IFLA_LINK: - response.fetch(IFLA_LINK, link); - attrs.push_back(make_tuple("link", std::to_string(link))); - break; - default: - response.skipAttribute(); - break; + try { + NetlinkResponse response = send(nlm, nsPid); + if (!response.hasMessage()) { + throw VasumException("Can't get interface information"); } + response.fetch(infoPeer); + + while (response.hasAttribute()) { + uint32_t mtu, link; + int attrType = response.getAttributeType(); + switch (attrType) { + case IFLA_MTU: + response.fetch(IFLA_MTU, mtu); + attrs.push_back(make_tuple("mtu", std::to_string(mtu))); + break; + case IFLA_LINK: + response.fetch(IFLA_LINK, link); + attrs.push_back(make_tuple("link", std::to_string(link))); + break; + default: + response.skipAttribute(); + break; + } + } + } catch (const std::exception& ex) { + LOGE(ex.what()); + throw VasumException(netdev + ": " + ex.what()); } + attrs.push_back(make_tuple("flags", std::to_string(infoPeer.ifi_flags))); attrs.push_back(make_tuple("type", std::to_string(infoPeer.ifi_type))); for (const auto& address : getIpAddresses(nsPid, AF_INET, infoPeer.ifi_index)) { attrs.push_back(make_tuple("ipv4", joinAddresses(address))); } + for (const auto& address : getIpAddresses(nsPid, AF_INET6, infoPeer.ifi_index)) { attrs.push_back(make_tuple("ipv6", joinAddresses(address))); } -- 2.7.4 From d7572ebdc785cd0cc4b3c79dac92c9b44c61f400 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Tue, 28 Jul 2015 09:42:48 +0200 Subject: [PATCH 03/16] Add test for Lock/UnlockQueue API [Feature] Test for Lock/UnlockQueue API [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Id8a88af8459a63e61db06668d4b4a57b2578b4e2 --- tests/unit_tests/server/ut-zones-manager.cpp | 240 +++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/tests/unit_tests/server/ut-zones-manager.cpp b/tests/unit_tests/server/ut-zones-manager.cpp index 214ed1a..819da53 100644 --- a/tests/unit_tests/server/ut-zones-manager.cpp +++ b/tests/unit_tests/server/ut-zones-manager.cpp @@ -415,6 +415,28 @@ public: return fileFd; } + void callMethodLockQueue() + { + assert(isHost()); + GVariantPtr result = mClient->callMethod(api::dbus::BUS_NAME, + api::dbus::OBJECT_PATH, + api::dbus::INTERFACE, + api::dbus::METHOD_LOCK_QUEUE, + nullptr, + "()"); + } + + void callMethodUnlockQueue() + { + assert(isHost()); + GVariantPtr result = mClient->callMethod(api::dbus::BUS_NAME, + api::dbus::OBJECT_PATH, + api::dbus::INTERFACE, + api::dbus::METHOD_UNLOCK_QUEUE, + nullptr, + "()"); + } + private: const int mId; DbusConnection::Pointer mClient; @@ -556,6 +578,22 @@ public: return result->fd.value; } + void callMethodLockQueue() + { + auto result = mClient.callSync( + api::ipc::METHOD_LOCK_QUEUE, + nullptr, + EVENT_TIMEOUT*10); + } + + void callMethodUnlockQueue() + { + auto result = mClient.callSync( + api::ipc::METHOD_UNLOCK_QUEUE, + nullptr, + EVENT_TIMEOUT*10); + } + private: ipc::epoll::ThreadDispatcher mDispatcher; ipc::Client mClient; @@ -1177,4 +1215,206 @@ MULTI_FIXTURE_TEST_CASE(CreateWriteReadFile, F, ACCESSORS) BOOST_REQUIRE(::close(returnedFd) != -1); } +MULTI_FIXTURE_TEST_CASE(BasicLockUnlockQueue, F, ACCESSORS) +{ + ZonesManager cm(F::dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("test1", SIMPLE_TEMPLATE); + cm.createZone("test2", SIMPLE_TEMPLATE); + cm.restoreAll(); + + // two clients + typename F::HostAccessory host; + typename F::HostAccessory hostLocker; + + // set up active zone as test1 to have something to compare to + host.callMethodSetActiveZone("test1"); + + // lock queue to host 1 + hostLocker.callMethodLockQueue(); + + // try setting different active zone, should result in error + BOOST_REQUIRE_THROW(host.callMethodSetActiveZone("test2"), std::runtime_error); + BOOST_CHECK_EQUAL(host.callMethodGetActiveZoneId(), "test1"); + + // unlock + hostLocker.callMethodUnlockQueue(); + + // now switch should work + host.callMethodSetActiveZone("test2"); + BOOST_CHECK_EQUAL(host.callMethodGetActiveZoneId(), "test2"); +} + +MULTI_FIXTURE_TEST_CASE(LockAndDisconnectQueue, F, ACCESSORS) +{ + ZonesManager cm(F::dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("test1", SIMPLE_TEMPLATE); + cm.createZone("test2", SIMPLE_TEMPLATE); + cm.restoreAll(); + + // two clients + typename F::HostAccessory host; + + { + typename F::HostAccessory hostLocker; + + // set up active zone as test1 to have something to compare to + host.callMethodSetActiveZone("test1"); + + // lock queue to host 1 + hostLocker.callMethodLockQueue(); + + // try setting different active zone, should result in error + BOOST_REQUIRE_THROW(host.callMethodSetActiveZone("test2"), std::runtime_error); + BOOST_CHECK_EQUAL(host.callMethodGetActiveZoneId(), "test1"); + + // leaving scope simulates disconnect + } + + // now switch should work + host.callMethodSetActiveZone("test2"); + BOOST_CHECK_EQUAL(host.callMethodGetActiveZoneId(), "test2"); +} + +MULTI_FIXTURE_TEST_CASE(DoubleLockQueue, F, ACCESSORS) +{ + ZonesManager cm(F::dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + + typename F::HostAccessory host; + + // first lock - should succeed + host.callMethodLockQueue(); + + // second lock - should fail + BOOST_REQUIRE_THROW(host.callMethodLockQueue(), std::runtime_error); +} + +MULTI_FIXTURE_TEST_CASE(DoubleUnlockQueue, F, ACCESSORS) +{ + ZonesManager cm(F::dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + + typename F::HostAccessory host; + + // we are already unlocked - should return an error + BOOST_REQUIRE_THROW(host.callMethodUnlockQueue(), std::runtime_error); +} + +#ifdef DBUS_CONNECTION +// test cases similar to BasicLockUnlockQueue, however with cross-fixture calls +BOOST_AUTO_TEST_CASE(IPCLockFromDbusQueue) +{ + ZonesManager cm(dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("test1", SIMPLE_TEMPLATE); + cm.createZone("test2", SIMPLE_TEMPLATE); + cm.restoreAll(); + + IPCFixture::HostAccessory hostIPC; + DbusFixture::HostAccessory hostDbus; + + // we should be unlocked, Dbus should be able to call something + hostDbus.callMethodSetActiveZone("test1"); + + // lock the queue with IPC host + hostIPC.callMethodLockQueue(); + + // now Dbus should be unable to do calls + BOOST_REQUIRE_THROW(hostDbus.callMethodSetActiveZone("test2"), std::runtime_error); + BOOST_CHECK_EQUAL(hostDbus.callMethodGetActiveZoneId(), "test1"); + + // unlock + hostIPC.callMethodUnlockQueue(); + + // should be able to call now + hostDbus.callMethodSetActiveZone("test2"); + BOOST_CHECK_EQUAL(hostDbus.callMethodGetActiveZoneId(), "test2"); +} + +BOOST_AUTO_TEST_CASE(DbusLockFromIPCQueue) +{ + ZonesManager cm(dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("test1", SIMPLE_TEMPLATE); + cm.createZone("test2", SIMPLE_TEMPLATE); + cm.restoreAll(); + + IPCFixture::HostAccessory hostIPC; + DbusFixture::HostAccessory hostDbus; + + // Same approach as in IPCLockFromDbusQueue, however with flipped host types + hostIPC.callMethodSetActiveZone("test1"); + + hostDbus.callMethodLockQueue(); + + BOOST_REQUIRE_THROW(hostIPC.callMethodSetActiveZone("test2"), std::runtime_error); + BOOST_CHECK_EQUAL(hostIPC.callMethodGetActiveZoneId(), "test1"); + + hostDbus.callMethodUnlockQueue(); + + hostIPC.callMethodSetActiveZone("test2"); + BOOST_CHECK_EQUAL(hostIPC.callMethodGetActiveZoneId(), "test2"); +} + +// simulate disconnect cross-fixture +BOOST_AUTO_TEST_CASE(IPCLockFromDbusAndDisconnectQueue) +{ + ZonesManager cm(dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("test1", SIMPLE_TEMPLATE); + cm.createZone("test2", SIMPLE_TEMPLATE); + cm.restoreAll(); + + DbusFixture::HostAccessory hostDbus; + + { + IPCFixture::HostAccessory hostIPC; + + // we should be unlocked, Dbus should be able to call something + hostDbus.callMethodSetActiveZone("test1"); + + // lock the queue with IPC host + hostIPC.callMethodLockQueue(); + + // now Dbus should be unable to do calls + BOOST_REQUIRE_THROW(hostDbus.callMethodSetActiveZone("test2"), std::runtime_error); + BOOST_CHECK_EQUAL(hostDbus.callMethodGetActiveZoneId(), "test1"); + + // leaving scope should simulate disconnect + } + + // should be able to call now + hostDbus.callMethodSetActiveZone("test2"); + BOOST_CHECK_EQUAL(hostDbus.callMethodGetActiveZoneId(), "test2"); +} + +BOOST_AUTO_TEST_CASE(DbusLockFromIPCAndDisconnectQueue) +{ + ZonesManager cm(dispatcher.getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("test1", SIMPLE_TEMPLATE); + cm.createZone("test2", SIMPLE_TEMPLATE); + cm.restoreAll(); + + IPCFixture::HostAccessory hostIPC; + + { + DbusFixture::HostAccessory hostDbus; + + // Same approach as in IPCLockFromDbusAndDisconnectQueue, however with flipped host types + hostIPC.callMethodSetActiveZone("test1"); + + hostDbus.callMethodLockQueue(); + + BOOST_REQUIRE_THROW(hostIPC.callMethodSetActiveZone("test2"), std::runtime_error); + BOOST_CHECK_EQUAL(hostIPC.callMethodGetActiveZoneId(), "test1"); + } + + hostIPC.callMethodSetActiveZone("test2"); + BOOST_CHECK_EQUAL(hostIPC.callMethodGetActiveZoneId(), "test2"); +} +#endif + BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From b41fd1bbaf120f85b745d923070b1d020fdcc5d2 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Wed, 29 Jul 2015 15:06:02 +0200 Subject: [PATCH 04/16] lxcpp: setns wrapper [Feature] Added setns wrapper Added Namespace type [Cause] N/A [Solution] N/A [Verification] N/A Change-Id: Ib07374bb07183dba604053139d624bdf2a850268 --- common/utils/execute.cpp | 1 + libs/lxcpp/CMakeLists.txt | 1 + libs/lxcpp/exception.hpp | 6 ++ libs/lxcpp/namespace.cpp | 84 ++++++++++++++++++++++ libs/lxcpp/namespace.hpp | 56 +++++++++++++++ libs/lxcpp/process.cpp | 69 ++++++++++++++++-- libs/lxcpp/process.hpp | 10 ++- tests/unit_tests/lxcpp/ut-container.cpp | 6 +- tests/unit_tests/lxcpp/ut-namespace.cpp | 76 ++++++++++++++++++++ tests/unit_tests/lxcpp/ut-process.cpp | 121 ++++++++++++++++++++++++++++++++ 10 files changed, 418 insertions(+), 12 deletions(-) create mode 100644 libs/lxcpp/namespace.cpp create mode 100644 libs/lxcpp/namespace.hpp create mode 100644 tests/unit_tests/lxcpp/ut-namespace.cpp create mode 100644 tests/unit_tests/lxcpp/ut-process.cpp diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp index 4b6d59d..55fc912 100644 --- a/common/utils/execute.cpp +++ b/common/utils/execute.cpp @@ -120,6 +120,7 @@ bool waitPid(pid_t pid, int& status) { while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { + LOGE("waitpid() failed: " << getSystemErrorMessage()); return false; } } diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 0d49df9..65bd2fc 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -37,6 +37,7 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES ## Link libraries ############################################################## INCLUDE_DIRECTORIES(${LIBS_FOLDER}) +INCLUDE_DIRECTORIES(${COMMON_FOLDER}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} Logger) ## Generate the pc file ######################################################## diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index 9d59101..12ae299 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -45,6 +45,12 @@ struct ProcessSetupException: public Exception { ProcessSetupException(const std::string& message = "Error during setting up a process") : Exception(message) {} }; + +struct BadArgument: public Exception { + BadArgument(const std::string& message = "Bad argument passed") + : Exception(message) {} +}; + } // namespace lxcpp #endif // LXCPP_EXCEPTION_HPP diff --git a/libs/lxcpp/namespace.cpp b/libs/lxcpp/namespace.cpp new file mode 100644 index 0000000..35bc140 --- /dev/null +++ b/libs/lxcpp/namespace.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief lxcpp container factory definition + */ + +#include "lxcpp/namespace.hpp" +#include "lxcpp/exception.hpp" +#include "logger/logger.hpp" + +#include +#include + +namespace lxcpp { + +Namespace operator|(const Namespace a, const Namespace b) +{ + return static_cast(static_cast::type>(a) | + static_cast::type>(b)); +} + +std::string toString(const Namespace ns) +{ + switch(ns) { + case Namespace::USER: + return "user"; + case Namespace::MNT: + return "mnt"; + case Namespace::PID: + return "pid"; + case Namespace::UTS: + return "uts"; + case Namespace::IPC: + return "ipc"; + case Namespace::NET: + return "net"; + default: + LOGE("Bad namespace passed to the function"); + throw BadArgument("Bad namespace passed to the function"); + } +} + +int toFlag(const std::vector& namespaces) +{ + Namespace flag = std::accumulate(namespaces.begin(), + namespaces.end(), + static_cast(0), + std::bit_or()); + return static_cast(flag); +} + +int toFlag(const Namespace ns) +{ + return static_cast(ns); +} + +std::string getNsPath(const pid_t pid) +{ + return "/proc/" + std::to_string(pid) + "/ns"; +} + +std::string getPath(const pid_t pid, const Namespace ns) +{ + return getNsPath(pid) + "/" + toString(ns); +} + +} // namespace lxcpp diff --git a/libs/lxcpp/namespace.hpp b/libs/lxcpp/namespace.hpp new file mode 100644 index 0000000..be6d033 --- /dev/null +++ b/libs/lxcpp/namespace.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief process handling routines + */ + +#ifndef LXCPP_NAMESPACE_HPP +#define LXCPP_NAMESPACE_HPP + +#include +#include +#include + +namespace lxcpp { + +enum class Namespace : int { + USER = CLONE_NEWUSER, + MNT = CLONE_NEWNS, + PID = CLONE_NEWPID, + UTS = CLONE_NEWUTS, + IPC = CLONE_NEWIPC, + NET = CLONE_NEWNET +}; + +Namespace operator |(const Namespace a, const Namespace b); + +std::string toString(const Namespace ns); + +std::string getNsPath(const pid_t pid); + +std::string getPath(const pid_t pid, const Namespace ns); + +int toFlag(const Namespace ns); + +int toFlag(const std::vector& namespaces); + +} // namespace lxcpp + +#endif // LXCPP_NAMESPACE_HPP \ No newline at end of file diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index 200d12e..68fd62c 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -23,30 +23,85 @@ #include "lxcpp/process.hpp" #include "lxcpp/exception.hpp" + #include "logger/logger.hpp" +#include "utils/fd-utils.hpp" +#include "utils/exception.hpp" #include #include #include #include +#include namespace lxcpp { -pid_t clone(int (*function)(void *), int flags, void *args) { +pid_t clone(int (*function)(void *), + void *args, + const std::vector& namespaces, + const int additionalFlags) +{ // Won't fail, well known resource name size_t stackSize = ::sysconf(_SC_PAGESIZE); // PAGESIZE is enough, it'll exec after this char *stack = static_cast(::alloca(stackSize)); - pid_t ret; - ret = ::clone(function, stack + stackSize, flags | SIGCHLD, args); - if (ret < 0) { - LOGE("clone() failed"); - throw ProcessSetupException("clone() failed"); + pid_t pid = ::clone(function, stack + stackSize, toFlag(namespaces) | additionalFlags | SIGCHLD, args); + if (pid < 0) { + const std::string msg = utils::getSystemErrorMessage(); + LOGE("clone() failed: " << msg); + throw ProcessSetupException("clone() failed " + msg); + } + + return pid; +} + +void setns(const std::vector& namespaces) +{ + pid_t pid = ::getpid(); + + int dirFD = ::open(getNsPath(pid).c_str(), O_DIRECTORY | O_CLOEXEC); + if(dirFD < 0) { + const std::string msg = utils::getSystemErrorMessage(); + LOGE("open() failed: " << msg); + throw ProcessSetupException("open() failed: " + msg); + } + + // Open FDs connected with the requested namespaces + std::vector fds(namespaces.size(), -1); + for(size_t i = 0; i < namespaces.size(); ++i) { + fds[i] = ::openat(dirFD, toString(namespaces[i]).c_str(), O_RDONLY | O_CLOEXEC); + if(fds[i] < 0) { + const std::string msg = utils::getSystemErrorMessage(); + + for (size_t j = 0; j < i; ++j) { + utils::close(fds[j]); + } + utils::close(dirFD); + + LOGE("openat() failed: " << msg); + throw ProcessSetupException("openat() failed: " + msg); + } + } + + // Setns for every namespace + for(size_t i = 0; i < fds.size(); ++i) { + if(-1 == ::setns(fds[i], toFlag(namespaces[i]))) { + const std::string msg = utils::getSystemErrorMessage(); + + for (size_t j = i; j < fds.size(); ++j) { + utils::close(fds[j]); + } + utils::close(dirFD); + + LOGE("setns() failed: " << msg); + throw ProcessSetupException("setns() failed: " + msg); + } + utils::close(fds[i]); } - return ret; + utils::close(dirFD); } } // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index 8ca8aaf..1255589 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -24,11 +24,19 @@ #ifndef LXCPP_PROCESS_HPP #define LXCPP_PROCESS_HPP +#include "lxcpp/namespace.hpp" + #include +#include namespace lxcpp { -pid_t clone(int (*function)(void *), int flags, void *args); +pid_t clone(int (*function)(void *), + void *args, + const std::vector& namespaces, + const int additionalFlags = 0); + +void setns(const std::vector& namespaces); } // namespace lxcpp diff --git a/tests/unit_tests/lxcpp/ut-container.cpp b/tests/unit_tests/lxcpp/ut-container.cpp index 74ebbfe..6177502 100644 --- a/tests/unit_tests/lxcpp/ut-container.cpp +++ b/tests/unit_tests/lxcpp/ut-container.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved * - * Contact: Piotr Bartosiewicz + * Contact: Jan Olszak(j.olszak@samsung.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,8 +29,6 @@ #include "lxcpp/lxcpp.hpp" #include "lxcpp/exception.hpp" -#include - namespace { struct Fixture { diff --git a/tests/unit_tests/lxcpp/ut-namespace.cpp b/tests/unit_tests/lxcpp/ut-namespace.cpp new file mode 100644 index 0000000..9e10e81 --- /dev/null +++ b/tests/unit_tests/lxcpp/ut-namespace.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak(j.olszak@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Jan Olszak(j.olszak@samsung.com) + * @brief Unit tests of lxcpp namespace helpers + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "lxcpp/namespace.hpp" +#include +#include + +namespace { + +struct Fixture { + Fixture() {} + ~Fixture() {} +}; + +} // namespace + +BOOST_FIXTURE_TEST_SUITE(LxcppNamespaceSuite, Fixture) + +using namespace lxcpp; + +const std::array NAMESPACES {{ + Namespace::USER, + Namespace::MNT, + Namespace::PID, + Namespace::UTS, + Namespace::IPC, + Namespace::NET + }}; + +BOOST_AUTO_TEST_CASE(OR) +{ + Namespace a = Namespace::USER; + Namespace b = Namespace::MNT; + BOOST_CHECK_EQUAL(CLONE_NEWUSER | CLONE_NEWNS, static_cast(a | b)); +} + +BOOST_AUTO_TEST_CASE(GetPath) +{ + for(const auto ns: NAMESPACES) { + getPath(0, ns); + } +} + +BOOST_AUTO_TEST_CASE(ToString) +{ + for(const auto ns: NAMESPACES) { + toString(ns); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp new file mode 100644 index 0000000..df9f33d --- /dev/null +++ b/tests/unit_tests/lxcpp/ut-process.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Jan Olszak(j.olszak@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Jan Olszak(j.olszak@samsung.com) + * @brief Unit tests of lxcpp process helpers + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "lxcpp/process.hpp" +#include "lxcpp/exception.hpp" +#include "utils/exception.hpp" +#include "utils/execute.hpp" + +#include +#include +#include + +namespace { + +struct Fixture { + Fixture() {} + ~Fixture() {} +}; + +int clonefn(void* /*args*/) { + return 0; +} + +} // namespace + +BOOST_FIXTURE_TEST_SUITE(LxcppProcessSuite, Fixture) + +using namespace lxcpp; + +const std::vector NAMESPACES {{ + Namespace::USER, + Namespace::MNT, + Namespace::PID, + Namespace::UTS, + Namespace::IPC, + Namespace::NET + }}; + +BOOST_AUTO_TEST_CASE(Clone) +{ + BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, NAMESPACES)); + BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, {Namespace::MNT})); +} + +BOOST_AUTO_TEST_CASE(Setns) +{ + const int TEST_PASSED = 0; + const int ERROR = 1; + + pid_t pid = fork(); + if (pid==-1) { + BOOST_REQUIRE(false); + } else if(pid ==0) { + try { + setns({Namespace::MNT, + Namespace::PID, + Namespace::UTS, + Namespace::IPC, + Namespace::NET + }); + exit(TEST_PASSED); + } catch(...) { + exit(ERROR); + } + } else if(pid>0) { + int status = -1; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + } +} + +BOOST_AUTO_TEST_CASE(SetnsUserNamespace) +{ + const int TEST_PASSED = 0; + const int ERROR = -1; + + pid_t pid = fork(); + if (pid==-1) { + BOOST_REQUIRE(false); + } else if(pid ==0) { + try { + setns({Namespace::USER}); + exit(ERROR); + } catch(ProcessSetupException) { + exit(TEST_PASSED); + } catch(...) { + exit(ERROR); + } + } else if(pid>0) { + int status; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + } +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 6c71adb3430b415b16392a0d00772225d394e395 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Mon, 3 Aug 2015 15:52:58 +0200 Subject: [PATCH 05/16] Fix lxcpp tests [Bug] N/A [Cause] N/A [Solution] N/A [Verification] N/A Change-Id: Idf5939f96ffb6af7380cfc5fe216361b53402801 --- tests/unit_tests/lxcpp/ut-process.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp index df9f33d..cae893a 100644 --- a/tests/unit_tests/lxcpp/ut-process.cpp +++ b/tests/unit_tests/lxcpp/ut-process.cpp @@ -83,9 +83,9 @@ BOOST_AUTO_TEST_CASE(Setns) Namespace::IPC, Namespace::NET }); - exit(TEST_PASSED); + _exit(TEST_PASSED); } catch(...) { - exit(ERROR); + _exit(ERROR); } } else if(pid>0) { int status = -1; @@ -105,11 +105,11 @@ BOOST_AUTO_TEST_CASE(SetnsUserNamespace) } else if(pid ==0) { try { setns({Namespace::USER}); - exit(ERROR); + _exit(ERROR); } catch(ProcessSetupException) { - exit(TEST_PASSED); + _exit(TEST_PASSED); } catch(...) { - exit(ERROR); + _exit(ERROR); } } else if(pid>0) { int status; -- 2.7.4 From 3ba639b76de913af1a80d35c94cc2106dd6b1376 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Fri, 31 Jul 2015 17:11:16 +0200 Subject: [PATCH 06/16] lxcpp: Added utils sources [Feature] N/A [Cause] N/A [Solution] N/A [Verification] Link only against liblxcpp Change-Id: I7e9d32fc502a068dd9c50e5e45ab3ed07756f815 --- libs/lxcpp/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 65bd2fc..4ea53ef 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -22,14 +22,18 @@ PROJECT(lxcpp) MESSAGE(STATUS "") MESSAGE(STATUS "Generating makefile for the liblxcpp...") FILE(GLOB HEADERS *.hpp) +FILE(GLOB HEADERS_UTILS ${COMMON_FOLDER}/utils/fd-utils.hpp + ${COMMON_FOLDER}/utils/exception.hpp) FILE(GLOB SRCS *.cpp *.hpp) +FILE(GLOB SRCS_UTILS ${COMMON_FOLDER}/utils/fd-utils.cpp + ${COMMON_FOLDER}/utils/exception.cpp) SET(_LIB_VERSION_ "${VERSION}") SET(_LIB_SOVERSION_ "0") SET(PC_FILE "lib${PROJECT_NAME}.pc") ## Setup target ################################################################ -ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS}) +ADD_LIBRARY(${PROJECT_NAME} SHARED ${SRCS} ${SRCS_UTILS}) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${_LIB_SOVERSION_} VERSION ${_LIB_VERSION_} @@ -53,3 +57,5 @@ INSTALL(TARGETS ${PROJECT_NAME} INSTALL(FILES ${HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp) +INSTALL(FILES ${HEADERS_UTILS} + DESTINATION ${INCLUDE_INSTALL_DIR}/lxcpp/utils) -- 2.7.4 From aa482a5e599af2f33ce893dbef2c73a2e6804e3b Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 28 Jul 2015 15:55:25 +0200 Subject: [PATCH 07/16] Add daemonize function to work in the background. [Feature] Add daemonize function to work in the background. [Cause] N/A [Solution] N/A [Verification] Build, run tests, run server as daemon. Change-Id: Ie8987ac4d23ac350ab6036c476ade47436afbe69 --- common/utils/daemon.cpp | 93 +++++++++++++++++++++++++++++++++++++++++++++++++ common/utils/daemon.hpp | 34 ++++++++++++++++++ server/main.cpp | 11 +++++- zone-daemon/main.cpp | 11 +++++- 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 common/utils/daemon.cpp create mode 100644 common/utils/daemon.hpp diff --git a/common/utils/daemon.cpp b/common/utils/daemon.cpp new file mode 100644 index 0000000..8ee84c5 --- /dev/null +++ b/common/utils/daemon.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Dariusz Michaluk + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Dariusz Michaluk + * @brief Run a process as a daemon + */ + +#include "fd-utils.hpp" + +#include +#include +#include +#include +#include + +namespace utils { + +bool daemonize() +{ + pid_t pid = ::fork(); + + if (pid == -1) { + std::cerr << "Fork failed" << std::endl; + return false; + } else if (pid != 0) { + exit (0); + } + + if (::setsid() == -1) { + return false; + } + + pid = ::fork(); + + /* + * Fork a second child and exit immediately to prevent zombies. + * This causes the second child process to be orphaned, making the init + * process responsible for its cleanup. And, since the first child is + * a session leader without a controlling terminal, it's possible for + * it to acquire one by opening a terminal in the future (System V + * based systems). This second fork guarantees that the child is no + * longer a session leader, preventing the daemon from ever acquiring + * a controlling terminal. + */ + + if (pid == -1) { + std::cerr << "Fork failed" << std::endl; + return false; + } else if (pid != 0) { + exit(0); + } + + if (::chdir("/") == -1) { + return false; + } + + ::umask(0); + + /** Close all open standard file descriptors */ + int fd = ::open("/dev/null", O_RDWR); + if (fd == -1) { + return false; + } + + for (int i = 0; i <= 2; i++) { + if (::dup2(fd, i) == -1) { + utils::close(fd); + return false; + } + } + utils::close(fd); + + return true; +} + +} // namespace utils diff --git a/common/utils/daemon.hpp b/common/utils/daemon.hpp new file mode 100644 index 0000000..4b5924a --- /dev/null +++ b/common/utils/daemon.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Dariusz Michaluk + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Dariusz Michaluk + * @brief Run a process as a daemon + */ + +#ifndef COMMON_UTILS_DAEMON_HPP +#define COMMON_UTILS_DAEMON_HPP + +namespace utils { + +bool daemonize(); + +} // namespace utils + +#endif // COMMON_UTILS_DAEMON_HPP diff --git a/server/main.cpp b/server/main.cpp index e9440cb..7ed4ef1 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -34,6 +34,7 @@ #include "logger/backend-syslog.hpp" #include "utils/typeinfo.hpp" #include "utils/signal.hpp" +#include "utils/daemon.hpp" #include #include @@ -64,9 +65,10 @@ int main(int argc, char* argv[]) desc.add_options() ("help,h", "print this help") ("root,r", "Don't drop root privileges at startup") - ("version,v", "show application version") + ("daemon,d", "Run server as daemon") ("log-level,l", po::value()->default_value("DEBUG"), "set log level") ("check,c", "check runtime environment and exit") + ("version,v", "show application version") ; po::variables_map vm; @@ -103,6 +105,13 @@ int main(int argc, char* argv[]) return Server::checkEnvironment() ? 0 : 1; } + bool runAsDaemon = vm.count("daemon") > 0; + + if (runAsDaemon && !utils::daemonize()) { + std::cerr << "Failed to daemonize" << std::endl; + return 1; + } + Logger::setLogLevel(vm["log-level"].as()); #if !defined(NDEBUG) Logger::setLogBackend(new StderrBackend()); diff --git a/zone-daemon/main.cpp b/zone-daemon/main.cpp index 58fad9b..1bb3091 100644 --- a/zone-daemon/main.cpp +++ b/zone-daemon/main.cpp @@ -33,6 +33,7 @@ #include "logger/backend-journal.hpp" #include "logger/backend-syslog.hpp" #include "utils/typeinfo.hpp" +#include "utils/daemon.hpp" #include #include @@ -58,8 +59,9 @@ int main(int argc, char* argv[]) desc.add_options() ("help,h", "print this help") - ("version,v", "show application version") + ("daemon,d", "Run server as daemon") ("log-level,l", po::value()->default_value("DEBUG"), "set log level") + ("version,v", "show application version") ; po::variables_map vm; @@ -93,6 +95,13 @@ int main(int argc, char* argv[]) return 0; } + bool runAsDaemon = vm.count("daemon") > 0; + + if (runAsDaemon && !utils::daemonize()) { + std::cerr << "Failed to daemonize" << std::endl; + return 1; + } + Logger::setLogLevel(vm["log-level"].as()); #if !defined(NDEBUG) Logger::setLogBackend(new StderrBackend()); -- 2.7.4 From a18c42df8f27093a407ce428d98698c14b28fe73 Mon Sep 17 00:00:00 2001 From: Mateusz Malicki Date: Mon, 27 Jul 2015 17:28:22 +0200 Subject: [PATCH 08/16] InputMonitor connected to epoll dispatcher [Feature] InputMonitor connected to epoll dispatcher [Cause] N/A [Solution] N/A [Verification] Build, run tests Change-Id: If2bdcd8c8493bc22357aa93b43b5c6e530c954d4 --- server/input-monitor.cpp | 139 +++++++++++---------------- server/input-monitor.hpp | 36 +++---- server/zones-manager.cpp | 16 ++- server/zones-manager.hpp | 3 +- tests/unit_tests/server/ut-input-monitor.cpp | 125 ++++++++++++++++++------ 5 files changed, 179 insertions(+), 140 deletions(-) diff --git a/server/input-monitor.cpp b/server/input-monitor.cpp index e99906f..bb65b90 100644 --- a/server/input-monitor.cpp +++ b/server/input-monitor.cpp @@ -26,19 +26,17 @@ #include "input-monitor-config.hpp" #include "input-monitor.hpp" +#include "zones-manager.hpp" #include "exception.hpp" #include "logger/logger.hpp" #include "utils/exception.hpp" #include "utils/fs.hpp" -#include "utils/callback-wrapper.hpp" -#include "utils/scoped-gerror.hpp" +#include "utils/fd-utils.hpp" #include #include #include -#include -#include #include #include #include @@ -64,10 +62,13 @@ const std::string DEVICE_DIR = "/dev/input/"; } // namespace -InputMonitor::InputMonitor(const InputConfig& inputConfig, - const NotifyCallback& notifyCallback) - : mConfig(inputConfig), - mNotifyCallback(notifyCallback) +InputMonitor::InputMonitor(ipc::epoll::EventPoll& eventPoll, + const InputConfig& inputConfig, + ZonesManager* zonesManager) + : mConfig(inputConfig) + , mZonesManager(zonesManager) + , mFd(-1) + , mEventPoll(eventPoll) { if (mConfig.timeWindowMs > MAX_TIME_WINDOW_SEC * 1000L) { LOGE("Time window exceeds maximum: " << MAX_TIME_WINDOW_SEC); @@ -79,37 +80,39 @@ InputMonitor::InputMonitor(const InputConfig& inputConfig, throw InputMonitorException("Number of events exceeds maximum"); } - std::string devicePath = getDevicePath(); + mDevicePath = getDevicePath(); LOGT("Input monitor configuration: \n" << "\tenabled: " << mConfig.enabled << "\n" << "\tdevice: " << mConfig.device << "\n" - << "\tpath: " << devicePath << "\n" + << "\tpath: " << mDevicePath << "\n" << "\ttype: " << EV_KEY << "\n" << "\tcode: " << mConfig.code << "\n" << "\tvalue: " << KEY_PRESSED << "\n" << "\tnumberOfEvents: " << mConfig.numberOfEvents << "\n" << "\ttimeWindowMs: " << mConfig.timeWindowMs); - - createGIOChannel(devicePath); } InputMonitor::~InputMonitor() { + std::unique_lock mMutex; LOGD("Destroying InputMonitor"); - ScopedGError error; - g_io_channel_unref(mChannelPtr); - if (g_io_channel_shutdown(mChannelPtr, FALSE, &error) - != G_IO_STATUS_NORMAL) { - LOGE("Error during shutting down GIOChannel: " << error->message); - } + stop(); +} - if (!g_source_remove(mSourceId)) { - LOGE("Error during removing the source"); - } +void InputMonitor::start() +{ + std::unique_lock mMutex; + setHandler(mDevicePath); +} + +void InputMonitor::stop() +{ + leaveDevice(); } namespace { + bool isDeviceWithName(const boost::regex& deviceNameRegex, const fs::directory_entry& directoryEntry) { @@ -119,7 +122,7 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, return false; } - int fd = open(path.c_str(), O_RDONLY); + int fd = ::open(path.c_str(), O_RDONLY); if (fd < 0) { LOGD("Failed to open " << path); return false; @@ -128,13 +131,13 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, char name[DEVICE_NAME_LENGTH]; if (ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0) { LOGD("Failed to get the device name of: " << path); - if (close(fd) < 0) { + if (::close(fd) < 0) { LOGE("Error during closing file " << path); } return false; } - if (close(fd) < 0) { + if (::close(fd) < 0) { LOGE("Error during closing file " << path); } @@ -146,6 +149,7 @@ bool isDeviceWithName(const boost::regex& deviceNameRegex, return false; } + } // namespace std::string InputMonitor::getDevicePath() const @@ -174,80 +178,47 @@ std::string InputMonitor::getDevicePath() const return it->path().string(); } - - -void InputMonitor::createGIOChannel(const std::string& devicePath) +void InputMonitor::setHandler(const std::string& devicePath) { + using namespace std::placeholders; + // We need NONBLOCK for FIFOs in the tests - int fd = open(devicePath.c_str(), O_RDONLY | O_NONBLOCK); - if (fd < 0) { + mFd = ::open(devicePath.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (mFd < 0) { LOGE("Cannot create input monitor channel. Device file: " << devicePath << " doesn't exist"); throw InputMonitorException("Device does not exist"); } - - mChannelPtr = g_io_channel_unix_new(fd); - - // Read binary data - if (g_io_channel_set_encoding(mChannelPtr, - NULL, - NULL) != G_IO_STATUS_NORMAL) { - LOGE("Cannot set encoding for input monitor channel "); - throw InputMonitorException("Cannot set encoding"); - } - - using namespace std::placeholders; - ReadDeviceCallback callback = std::bind(&InputMonitor::readDevice, this, _1); - // Add the callback - mSourceId = g_io_add_watch_full(mChannelPtr, - G_PRIORITY_DEFAULT, - G_IO_IN, - readDeviceCallback, - utils::createCallbackWrapper(callback, mGuard.spawn()), - &utils::deleteCallbackWrapper); - if (!mSourceId) { - LOGE("Cannot add watch on device input file"); - throw InputMonitorException("Cannot add watch"); - } + mEventPoll.addFD(mFd, EPOLLIN, std::bind(&InputMonitor::handleInternal, this, _1, _2)); } -gboolean InputMonitor::readDeviceCallback(GIOChannel* gio, - GIOCondition /*condition*/, - gpointer data) -{ - const ReadDeviceCallback& callback = utils::getCallbackFromPointer(data); - callback(gio); - return TRUE; -} - -void InputMonitor::readDevice(GIOChannel* gio) +void InputMonitor::handleInternal(int /* fd */, ipc::epoll::Events events) { struct input_event ie; - gsize nBytesReadTotal = 0; - gsize nBytesRequested = sizeof(struct input_event); - ScopedGError error; - - do { - gsize nBytesRead = 0; - GIOStatus readStatus = g_io_channel_read_chars(gio, - &reinterpret_cast(&ie)[nBytesReadTotal], - nBytesRequested, - &nBytesRead, - &error); - - if (readStatus == G_IO_STATUS_ERROR) { - LOGE("Read from input monitor channel failed: " << error->message); + try { + if (events == EPOLLHUP) { + stop(); return; } - - nBytesRequested -= nBytesRead; - nBytesReadTotal += nBytesRead; - } while (nBytesRequested > 0); - - + utils::read(mFd, &ie, sizeof(struct input_event)); + } catch (const std::exception& ex) { + LOGE("Read from input monitor channel failed: " << ex.what()); + return; + } if (isExpectedEventSequence(ie)) { LOGI("Input monitor detected pattern."); - mNotifyCallback(); + if (mZonesManager->isRunning()) { + mZonesManager->switchingSequenceMonitorNotify(); + } + } +} + +void InputMonitor::leaveDevice() +{ + if (mFd != -1) { + mEventPoll.removeFD(mFd); + utils::close(mFd); + mFd = -1; } } diff --git a/server/input-monitor.hpp b/server/input-monitor.hpp index 2b0ed45..7ab5344 100644 --- a/server/input-monitor.hpp +++ b/server/input-monitor.hpp @@ -26,45 +26,45 @@ #define SERVER_INPUT_MONITOR_HPP #include "input-monitor-config.hpp" -#include "utils/callback-guard.hpp" +#include "ipc/epoll/event-poll.hpp" #include #include -#include -#include #include #include +#include namespace vasum { +class ZonesManager; + class InputMonitor { public: - typedef std::function NotifyCallback; - - InputMonitor(const InputConfig& inputConfig, - const NotifyCallback& notifyCallback); + InputMonitor(ipc::epoll::EventPoll& eventPoll, + const InputConfig& inputConfig, + ZonesManager* zonesManager); ~InputMonitor(); + void start(); + void stop(); private: - typedef std::function ReadDeviceCallback; + typedef std::mutex Mutex; InputConfig mConfig; - NotifyCallback mNotifyCallback; - + ZonesManager* mZonesManager; + int mFd; + ipc::epoll::EventPoll& mEventPoll; std::list mEventTimes; - GIOChannel* mChannelPtr; + std::string mDevicePath; + Mutex mMutex; std::string getDevicePath() const; - void createGIOChannel(const std::string& devicePath); - - // Internal callback to be registered at glib g_io_add_watch() - static gboolean readDeviceCallback(GIOChannel*, GIOCondition, gpointer); + void setHandler(const std::string& devicePath); + void handleInternal(int fd, ipc::epoll::Events events); + void leaveDevice(); bool isExpectedEventSequence(const struct input_event&); - void readDevice(GIOChannel*); - utils::CallbackGuard mGuard; - guint mSourceId; }; } // namespace vasum diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 484b3e9..0d88d24 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -173,6 +173,11 @@ ZonesManager::ZonesManager(ipc::epoll::EventPoll& eventPoll, const std::string& configPath, mDynamicConfig, getVasumDbPrefix()); + + if (mConfig.inputConfig.enabled) { + LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]"); + mSwitchingSequenceMonitor.reset(new InputMonitor(eventPoll, mConfig.inputConfig, this)); + } } ZonesManager::~ZonesManager() @@ -207,11 +212,8 @@ void ZonesManager::start() LOGD("ZonesManager object instantiated"); if (mConfig.inputConfig.enabled) { - LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]"); - mSwitchingSequenceMonitor.reset( - new InputMonitor(mConfig.inputConfig, - std::bind(&ZonesManager::switchingSequenceMonitorNotify, - this))); + LOGI("Starting input monitor "); + mSwitchingSequenceMonitor->start(); } // After everything's initialized start to respond to clients' requests @@ -238,6 +240,10 @@ void ZonesManager::stop(bool wait) // wait for all tasks to complete mWorker.reset(); mHostIPCConnection.stop(wait); + if (mConfig.inputConfig.enabled) { + LOGI("Stopping input monitor "); + mSwitchingSequenceMonitor->stop(); + } mIsRunning = false; } diff --git a/server/zones-manager.hpp b/server/zones-manager.hpp index de6663d..c6f6918 100644 --- a/server/zones-manager.hpp +++ b/server/zones-manager.hpp @@ -198,6 +198,8 @@ public: api::MethodResultBuilder::Pointer result); void handleCleanUpZonesRootCall(api::MethodResultBuilder::Pointer result); + void switchingSequenceMonitorNotify(); + private: typedef std::recursive_mutex Mutex; typedef std::unique_lock Lock; @@ -227,7 +229,6 @@ private: void saveDynamicConfig(); void updateDefaultId(); void refocus(); - void switchingSequenceMonitorNotify(); void generateNewConfig(const std::string& id, const std::string& templatePath); std::string getTemplatePathForExistingZone(const std::string& id); diff --git a/tests/unit_tests/server/ut-input-monitor.cpp b/tests/unit_tests/server/ut-input-monitor.cpp index 22fba47..75079a2 100644 --- a/tests/unit_tests/server/ut-input-monitor.cpp +++ b/tests/unit_tests/server/ut-input-monitor.cpp @@ -29,10 +29,12 @@ #include "input-monitor.hpp" #include "input-monitor-config.hpp" #include "exception.hpp" +#include "zones-manager.hpp" #include "utils/glib-loop.hpp" #include "utils/latch.hpp" #include "utils/scoped-dir.hpp" +#include "ipc/epoll/thread-dispatcher.hpp" #include #include @@ -44,7 +46,6 @@ #include #include - using namespace vasum; using namespace utils; @@ -58,16 +59,24 @@ const int EVENT_CODE = 139; const int EVENT_BUTTON_RELEASED = 0; const int EVENT_BUTTON_PRESSED = 1; -const int SINGLE_EVENT_TIMEOUT = 1000; +const int EVENT_TIMEOUT = 1000; + +const std::string CONFIG_DIR = VSM_TEST_CONFIG_INSTALL_DIR; +const std::string TEST_CONFIG_PATH = CONFIG_DIR + "/test-daemon.conf"; +const std::string SIMPLE_TEMPLATE = "console-ipc"; +const std::string ZONES_PATH = "/tmp/ut-zones"; // the same as in daemon.conf struct Fixture { utils::ScopedGlibLoop mLoop; ScopedDir mTestPathGuard; + ScopedDir mZonesPathGuard; InputConfig inputConfig; struct input_event ie; + ipc::epoll::ThreadDispatcher dispatcher; Fixture() : mTestPathGuard(TEST_DIR) + , mZonesPathGuard(ZONES_PATH) { inputConfig.numberOfEvents = 2; inputConfig.device = TEST_INPUT_DEVICE; @@ -84,46 +93,65 @@ struct Fixture { BOOST_CHECK(::mkfifo(TEST_INPUT_DEVICE.c_str(), S_IWUSR | S_IRUSR) >= 0); } + + ipc::epoll::EventPoll& getPoll() { + return dispatcher.getPoll(); + } }; + +template +bool spinWaitFor(int timeoutMs, Predicate pred) +{ + auto until = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs); + while (!pred()) { + if (std::chrono::steady_clock::now() >= until) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + return true; +} + } // namespace BOOST_FIXTURE_TEST_SUITE(InputMonitorSuite, Fixture) BOOST_AUTO_TEST_CASE(ConfigOK) { - InputMonitor inputMonitor(inputConfig, InputMonitor::NotifyCallback()); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + InputMonitor inputMonitor(getPoll(), inputConfig, &cm); } BOOST_AUTO_TEST_CASE(ConfigTimeWindowMsTooHigh) { + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); inputConfig.timeWindowMs = 50000; - BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(inputConfig, InputMonitor::NotifyCallback()), + BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(getPoll(), inputConfig, &cm), InputMonitorException, WhatEquals("Time window exceeds maximum")); } BOOST_AUTO_TEST_CASE(ConfigDeviceFilePathNotExisting) { + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); inputConfig.device = TEST_INPUT_DEVICE + "notExisting"; - BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(inputConfig, InputMonitor::NotifyCallback()), + BOOST_REQUIRE_EXCEPTION(InputMonitor inputMonitor(getPoll(), inputConfig, &cm), InputMonitorException, WhatEquals("Cannot find a device")); } namespace { -void sendNEvents(Fixture& f, unsigned int noOfEventsToSend) +void sendEvent(Fixture& f, ZonesManager& cm) { - Latch eventLatch; - - InputMonitor inputMonitor(f.inputConfig, [&] {eventLatch.set();}); - + InputMonitor inputMonitor(f.getPoll(), f.inputConfig, &cm); + inputMonitor.start(); int fd = ::open(TEST_INPUT_DEVICE.c_str(), O_WRONLY); BOOST_REQUIRE(fd >= 0); - for (unsigned int i = 0; i < noOfEventsToSend * f.inputConfig.numberOfEvents; ++i) { + for (int i = 0; i < f.inputConfig.numberOfEvents; ++i) { // button pressed event f.ie.value = EVENT_BUTTON_PRESSED; f.ie.time.tv_usec += 5; @@ -140,36 +168,52 @@ void sendNEvents(Fixture& f, unsigned int noOfEventsToSend) } BOOST_CHECK(::close(fd) >= 0); - BOOST_CHECK(eventLatch.waitForN(noOfEventsToSend, SINGLE_EVENT_TIMEOUT * noOfEventsToSend)); - - // Check if no more events are waiting - BOOST_CHECK(!eventLatch.wait(10)); } } // namespace -BOOST_AUTO_TEST_CASE(EventOneAtATime) +BOOST_AUTO_TEST_CASE(SingleEvent) { - sendNEvents(*this, 1); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + sendEvent(*this, cm); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == "zone2"; + })); } -BOOST_AUTO_TEST_CASE(EventTenAtATime) +BOOST_AUTO_TEST_CASE(MultipleEvent) { - sendNEvents(*this, 10); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + for (int i = 1; i < 10; ++i) { + sendEvent(*this, cm); + std::string zoneId = "zone" + std::to_string(i % 3 + 1); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == zoneId; + })); + } } namespace { -void sendNEventsWithPauses(Fixture& f, unsigned int noOfEventsToSend) +void sendEventWithPauses(Fixture& f, ZonesManager& cm) { - Latch eventLatch; - - InputMonitor inputMonitor(f.inputConfig, [&] {eventLatch.set();}); + InputMonitor inputMonitor(f.getPoll(), f.inputConfig, &cm); + inputMonitor.start(); int fd = ::open(TEST_INPUT_DEVICE.c_str(), O_WRONLY); BOOST_REQUIRE(fd >= 0); - for (unsigned int i = 0; i < noOfEventsToSend * f.inputConfig.numberOfEvents; ++i) { + for (int i = 0; i < f.inputConfig.numberOfEvents; ++i) { // Send first two bytes of the button pressed event f.ie.value = EVENT_BUTTON_PRESSED; f.ie.time.tv_usec += 5; @@ -191,22 +235,39 @@ void sendNEventsWithPauses(Fixture& f, unsigned int noOfEventsToSend) BOOST_CHECK(ret > 0); } BOOST_CHECK(::close(fd) >= 0); - BOOST_CHECK(eventLatch.waitForN(noOfEventsToSend, SINGLE_EVENT_TIMEOUT * noOfEventsToSend)); - - // Check if no more events are waiting - BOOST_CHECK(!eventLatch.wait(10)); } } // namespace -BOOST_AUTO_TEST_CASE(EventOneAtATimeWithPauses) +BOOST_AUTO_TEST_CASE(SingleEventWithPauses) { - sendNEventsWithPauses(*this, 1); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + sendEventWithPauses(*this, cm); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == "zone2"; + })); } -BOOST_AUTO_TEST_CASE(EventTenAtATimeWithPauses) +BOOST_AUTO_TEST_CASE(MultipleEventWithPauses) { - sendNEventsWithPauses(*this, 10); + ZonesManager cm(getPoll(), TEST_CONFIG_PATH); + cm.start(); + cm.createZone("zone1", SIMPLE_TEMPLATE); + cm.createZone("zone2", SIMPLE_TEMPLATE); + cm.createZone("zone3", SIMPLE_TEMPLATE); + cm.restoreAll(); + for (int i = 1; i < 10; ++i) { + sendEventWithPauses(*this, cm); + std::string zoneId = "zone" + std::to_string(i % 3 + 1); + BOOST_CHECK(spinWaitFor(EVENT_TIMEOUT, [&] { + return cm.getRunningForegroundZoneId() == zoneId; + })); + } } -- 2.7.4 From 7d2ce1f9cfb60d4de3336fc3e8d7296f183daace Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Tue, 4 Aug 2015 12:17:23 +0200 Subject: [PATCH 09/16] lxcpp: fix linking of 3rd party binaries with lxcpp [Feature] N/A [Cause] To be finally able to link with lxcpp of 3rd party code [Solution] N/A [Verification] Link non-vasum program against liblxcpp Change-Id: I218756ace9d9217db87d3e19b820d35653a22153 --- libs/lxcpp/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/lxcpp/CMakeLists.txt b/libs/lxcpp/CMakeLists.txt index 4ea53ef..c7bd926 100644 --- a/libs/lxcpp/CMakeLists.txt +++ b/libs/lxcpp/CMakeLists.txt @@ -40,9 +40,10 @@ SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES ) ## Link libraries ############################################################## -INCLUDE_DIRECTORIES(${LIBS_FOLDER}) -INCLUDE_DIRECTORIES(${COMMON_FOLDER}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} Logger) +FIND_PACKAGE(Boost COMPONENTS system filesystem) +INCLUDE_DIRECTORIES(${LIBS_FOLDER} ${COMMON_FOLDER}) +INCLUDE_DIRECTORIES(SYSTEM ${Boost_INCLUDE_DIRS}) +TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} ${Boost_LIBRARIES} Logger) ## Generate the pc file ######################################################## CONFIGURE_FILE(${PC_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PC_FILE} @ONLY) -- 2.7.4 From 991d45a32263a07df6d1e1af42fba048f2b06bf6 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Fri, 31 Jul 2015 15:01:38 +0200 Subject: [PATCH 10/16] Cleanup cppcheck statistics [Feature] N/A [Cause] Cppcheck results reported on jenkins contains error/warning messages [Solution] Make code correction to satisfy cppcheck, add cppcheck configuration to suppress some messages (e.g. API funcs) [Verification] Have installed cppcheck >= v1.66 Perform ./tests/cppcheck/cppcheck.sh script before commiting to review Change-Id: Ice10bbc118ab921feb9cb7261d4a2bdc22282353 --- common/netlink/netlink-message.cpp | 7 ++++--- common/utils/same-thread-guard.hpp | 4 ++-- common/utils/worker.cpp | 2 +- libs/config/fields.hpp | 5 +---- libs/config/sqlite3/connection.cpp | 4 +--- libs/config/types.hpp | 2 +- libs/ipc/client.cpp | 1 + libs/ipc/internals/processor.hpp | 4 +--- libs/ipc/internals/socket.cpp | 8 ++++++-- server/host-dbus-connection.cpp | 3 ++- server/host-dbus-connection.hpp | 2 +- server/netdev.cpp | 2 +- server/zone-provision.cpp | 8 ++++---- server/zone-provision.hpp | 8 ++++---- server/zones-manager.cpp | 2 +- tests/cppcheck/cppcheck.sh | 15 +++++++++++++++ tests/cppcheck/cppcheck.suppress | 7 +++++++ tests/unit_tests/config/testconfig-example.hpp | 13 +++++++------ tests/unit_tests/ipc/ut-ipc.cpp | 7 +------ tests/unit_tests/lxc/ut-zone.cpp | 4 ++-- tests/unit_tests/server/ut-server.cpp | 2 +- tests/unit_tests/server/ut-zone-provision.cpp | 6 +++--- tests/unit_tests/server/ut-zone.cpp | 4 ++-- wrapper/wrapper-compatibility.cpp | 21 ++++++++++----------- zone-daemon/daemon.cpp | 10 ++++++---- zone-daemon/daemon.hpp | 6 +++--- zone-daemon/runner.hpp | 4 ++-- 27 files changed, 90 insertions(+), 71 deletions(-) create mode 100755 tests/cppcheck/cppcheck.sh create mode 100644 tests/cppcheck/cppcheck.suppress diff --git a/common/netlink/netlink-message.cpp b/common/netlink/netlink-message.cpp index 29b079a..58aa1a2 100644 --- a/common/netlink/netlink-message.cpp +++ b/common/netlink/netlink-message.cpp @@ -289,14 +289,15 @@ inline int NetlinkResponse::getHdrPosition() const NetlinkResponse send(const NetlinkMessage& msg, int pid) { - assert(msg.hdr().nlmsg_flags & NLM_F_ACK); + const auto &hdr = msg.hdr(); + assert(hdr.nlmsg_flags & NLM_F_ACK); std::unique_ptr> data; Netlink nl; nl.open(pid); try { - nl.send(&msg.hdr()); - data = nl.rcv(msg.hdr().nlmsg_seq); + nl.send(&hdr); + data = nl.rcv(hdr.nlmsg_seq); } catch (const std::exception& ex) { LOGE("Sending failed (" << ex.what() << ")"); nl.close(); diff --git a/common/utils/same-thread-guard.hpp b/common/utils/same-thread-guard.hpp index 3a173b5..ec62b70 100644 --- a/common/utils/same-thread-guard.hpp +++ b/common/utils/same-thread-guard.hpp @@ -67,8 +67,8 @@ private: #else // ENABLE_SAME_THREAD_GUARD # define ASSERT_SAME_THREAD(g) - bool check() {return true;} - void reset() {} + static bool check() {return true;} + static void reset() {} #endif // ENABLE_SAME_THREAD_GUARD }; diff --git a/common/utils/worker.cpp b/common/utils/worker.cpp index 09069e8..eeac593 100644 --- a/common/utils/worker.cpp +++ b/common/utils/worker.cpp @@ -145,7 +145,7 @@ private: LOGT("Worker thread exited"); } - void execute(const TaskInfo& taskInfo) + static void execute(const TaskInfo& taskInfo) { try { LOGT("Executing task from subgroup " << taskInfo.groupID); diff --git a/libs/config/fields.hpp b/libs/config/fields.hpp index 8169cb4..d1fc4be 100644 --- a/libs/config/fields.hpp +++ b/libs/config/fields.hpp @@ -60,10 +60,7 @@ #define CONFIG_REGISTER_EMPTY \ template \ - void accept(Visitor ) { \ - } \ - template \ - void accept(Visitor ) const { \ + static void accept(Visitor ) { \ } \ #define CONFIG_REGISTER(...) \ diff --git a/libs/config/sqlite3/connection.cpp b/libs/config/sqlite3/connection.cpp index cb25694..3887352 100644 --- a/libs/config/sqlite3/connection.cpp +++ b/libs/config/sqlite3/connection.cpp @@ -50,9 +50,7 @@ Connection::Connection(const std::string& path) Connection::~Connection() { - if (::sqlite3_close(mDbPtr) != SQLITE_OK) { - throw ConfigException("Error during closing the database. Error: " + getErrorMessage()); - } + ::sqlite3_close(mDbPtr); } void Connection::exec(const std::string& query) diff --git a/libs/config/types.hpp b/libs/config/types.hpp index 3355f8f..f8b788d 100644 --- a/libs/config/types.hpp +++ b/libs/config/types.hpp @@ -41,4 +41,4 @@ struct FileDescriptor { }// config -#endif //COMMON_CONFIG_TYPES_HPP \ No newline at end of file +#endif //COMMON_CONFIG_TYPES_HPP diff --git a/libs/ipc/client.cpp b/libs/ipc/client.cpp index ad2eb47..245faee 100644 --- a/libs/ipc/client.cpp +++ b/libs/ipc/client.cpp @@ -32,6 +32,7 @@ namespace ipc { Client::Client(epoll::EventPoll& eventPoll, const std::string& socketPath) : mEventPoll(eventPoll), + mServiceID(-1), mProcessor(eventPoll, "[CLIENT] "), mSocketPath(socketPath) { diff --git a/libs/ipc/internals/processor.hpp b/libs/ipc/internals/processor.hpp index 4d0089f..7f7cd63 100644 --- a/libs/ipc/internals/processor.hpp +++ b/libs/ipc/internals/processor.hpp @@ -329,8 +329,6 @@ public: FileDescriptor getEventFD(); private: - typedef std::function& data)> SerializeCallback; - typedef std::function(int fd)> ParseCallback; typedef std::unique_lock Lock; typedef RequestQueue::Request Request; @@ -340,7 +338,7 @@ private: struct RegisterSignalsProtocolMessage { RegisterSignalsProtocolMessage() = default; - RegisterSignalsProtocolMessage(const std::vector ids) + RegisterSignalsProtocolMessage(const std::vector& ids) : ids(ids) {} std::vector ids; diff --git a/libs/ipc/internals/socket.cpp b/libs/ipc/internals/socket.cpp index 1961a52..c58809d 100644 --- a/libs/ipc/internals/socket.cpp +++ b/libs/ipc/internals/socket.cpp @@ -182,11 +182,15 @@ int Socket::createSocketInternal(const std::string& path) Socket Socket::createSocket(const std::string& path) { // Initialize a socket - int fd = -1; + int fd; #ifdef HAVE_SYSTEMD fd = getSystemdSocketInternal(path); + if (fd == -1) { + fd = createSocketInternal(path); + } +#else + fd = createSocketInternal(path); #endif // HAVE_SYSTEMD - fd = fd != -1 ? fd : createSocketInternal(path); return Socket(fd); } diff --git a/server/host-dbus-connection.cpp b/server/host-dbus-connection.cpp index 1229acb..ce415c8 100644 --- a/server/host-dbus-connection.cpp +++ b/server/host-dbus-connection.cpp @@ -424,8 +424,9 @@ void HostDbusConnection::onSignalCall(const std::string& /* senderBusName */, const std::string& objectPath, const std::string& interface, const std::string& /* signalName */, - GVariant* /* parameters */) + GVariant* /* parameters */) const { + (void)this; // satisfy cpp-check if (objectPath != api::dbus::OBJECT_PATH || interface != api::dbus::INTERFACE) { return; } diff --git a/server/host-dbus-connection.hpp b/server/host-dbus-connection.hpp index bb59afc..1a703e7 100644 --- a/server/host-dbus-connection.hpp +++ b/server/host-dbus-connection.hpp @@ -97,7 +97,7 @@ private: const std::string& objectPath, const std::string& interface, const std::string& signalName, - GVariant* parameters); + GVariant* parameters) const; }; diff --git a/server/netdev.cpp b/server/netdev.cpp index bf2576c..3086e7f 100644 --- a/server/netdev.cpp +++ b/server/netdev.cpp @@ -595,7 +595,7 @@ void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs) size_t pos = addrAttr.find(":"); if (pos == string::npos || pos == addrAttr.length()) { LOGE("Wrong input data format: ill formed address attribute: " << addrAttr); - VasumException("Wrong input data format: ill formed address attribute"); + throw VasumException("Wrong input data format: ill formed address attribute"); } attrs.push_back(make_tuple(addrAttr.substr(0, pos), addrAttr.substr(pos + 1))); } diff --git a/server/zone-provision.cpp b/server/zone-provision.cpp index 70c3711..4e99f83 100644 --- a/server/zone-provision.cpp +++ b/server/zone-provision.cpp @@ -255,7 +255,7 @@ void ZoneProvision::link(const ZoneProvisioningConfig::Link& config) throw UtilsException("Failed to hard link: path prefix is not valid"); } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::File& file) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::File& file) { //TODO output of type,flags and mode should be more user friendly return "file " + @@ -265,7 +265,7 @@ std::string ZoneProvision::getId(const ZoneProvisioningConfig::File& file) const std::to_string(file.mode); } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::Mount& mount) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::Mount& mount) { //TODO output of flags should be more user friendly return "mount " + @@ -276,12 +276,12 @@ std::string ZoneProvision::getId(const ZoneProvisioningConfig::Mount& mount) con mount.data; } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::Link& link) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::Link& link) { return "link " + link.source + " " + link.target; } -std::string ZoneProvision::getId(const ZoneProvisioningConfig::Provision& provision) const +std::string ZoneProvision::getId(const ZoneProvisioningConfig::Provision& provision) { using namespace vasum; if (provision.is()) { diff --git a/server/zone-provision.hpp b/server/zone-provision.hpp index 2f123ae..cf3d5c6 100644 --- a/server/zone-provision.hpp +++ b/server/zone-provision.hpp @@ -113,10 +113,10 @@ private: void file(const ZoneProvisioningConfig::File& config); void link(const ZoneProvisioningConfig::Link& config); - std::string getId(const ZoneProvisioningConfig::File& file) const; - std::string getId(const ZoneProvisioningConfig::Mount& mount) const; - std::string getId(const ZoneProvisioningConfig::Link& link) const; - std::string getId(const ZoneProvisioningConfig::Provision& provision) const; + static std::string getId(const ZoneProvisioningConfig::File& file); + static std::string getId(const ZoneProvisioningConfig::Mount& mount); + static std::string getId(const ZoneProvisioningConfig::Link& link); + static std::string getId(const ZoneProvisioningConfig::Provision& provision); }; } // namespace vasum diff --git a/server/zones-manager.cpp b/server/zones-manager.cpp index 0d88d24..5280d21 100644 --- a/server/zones-manager.cpp +++ b/server/zones-manager.cpp @@ -399,7 +399,7 @@ void ZonesManager::focusInternal(Zones::iterator iter) } Zone& zoneToFocus = get(iter); - const std::string idToFocus = zoneToFocus.getId(); + const std::string& idToFocus = zoneToFocus.getId(); if (idToFocus == mActiveZoneId) { return; diff --git a/tests/cppcheck/cppcheck.sh b/tests/cppcheck/cppcheck.sh new file mode 100755 index 0000000..b8bb637 --- /dev/null +++ b/tests/cppcheck/cppcheck.sh @@ -0,0 +1,15 @@ +#!/bin/bash +OPT="" +OPT+=" --enable=all" +OPT+=" --std=c++11" +OPT+=" --inconclusive" +OPT+=" --suppressions tests/cppcheck/cppcheck.suppress" +OPT+=" --suppress=missingIncludeSystem" +OPT+=" --inline-suppr" +OPT+=" -iCMakeFiles" +OPT+=" -I common" +OPT+=" -I libs" +OPT+=" -I client" +OPT+=" -I server" +OPT+=" -U__NR_capset" +cppcheck ${OPT} --template=gcc ./ 1>/dev/null diff --git a/tests/cppcheck/cppcheck.suppress b/tests/cppcheck/cppcheck.suppress new file mode 100644 index 0000000..d3b098f --- /dev/null +++ b/tests/cppcheck/cppcheck.suppress @@ -0,0 +1,7 @@ +// public API functions (or write tests to use them all) +unusedFunction:wrapper/wrapper-compatibility.cpp +unusedFunction:wrapper/wrapper.cpp + +// lambda not recognized to catch exception (v1.68) +exceptThrowInNoexecptFunction:client/vasum-client-impl.cpp + diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index ff5c2ef..549985b 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -34,13 +34,7 @@ struct TestConfig { struct SubConfig { struct SubSubConfig { - int intVal; - bool moved; - CONFIG_REGISTER - ( - intVal - ) SubSubConfig() : intVal(), moved(false) {} SubSubConfig(const SubSubConfig& config) : intVal(config.intVal), moved(false) {} SubSubConfig(SubSubConfig&& config) : intVal(std::move(config.intVal)), moved(false) { @@ -60,6 +54,13 @@ struct TestConfig { bool isMoved() const { return moved; } + + int intVal; + bool moved; + CONFIG_REGISTER + ( + intVal + ) }; int intVal; diff --git a/tests/unit_tests/ipc/ut-ipc.cpp b/tests/unit_tests/ipc/ut-ipc.cpp index ab920fe..8bb829e 100644 --- a/tests/unit_tests/ipc/ut-ipc.cpp +++ b/tests/unit_tests/ipc/ut-ipc.cpp @@ -159,15 +159,10 @@ struct EmptyData { struct ThrowOnAcceptData { template - void accept(Visitor) + static void accept(Visitor) { throw std::runtime_error("intentional failure in accept"); } - template - void accept(Visitor) const - { - throw std::runtime_error("intentional failure in accept const"); - } }; void returnEmptyCallback(const PeerID, diff --git a/tests/unit_tests/lxc/ut-zone.cpp b/tests/unit_tests/lxc/ut-zone.cpp index 6702773..80c003f 100644 --- a/tests/unit_tests/lxc/ut-zone.cpp +++ b/tests/unit_tests/lxc/ut-zone.cpp @@ -60,7 +60,7 @@ struct Fixture { cleanup(); } - void cleanup() + static void cleanup() { LxcZone lxc(ZONE_PATH, ZONE_NAME); if (lxc.isDefined()) { @@ -71,7 +71,7 @@ struct Fixture { } } - void waitForInit() + static void waitForInit() { // wait for init fully started (wait for bash to be able to trap SIGTERM) std::this_thread::sleep_for(std::chrono::milliseconds(200)); diff --git a/tests/unit_tests/server/ut-server.cpp b/tests/unit_tests/server/ut-server.cpp index e4d1ce4..79a7d72 100644 --- a/tests/unit_tests/server/ut-server.cpp +++ b/tests/unit_tests/server/ut-server.cpp @@ -63,7 +63,7 @@ struct Fixture { LOGI("------------ setup complete -----------"); } - void prepare() + static void prepare() { ScopedGlibLoop loop; ipc::epoll::ThreadDispatcher mDispatcher; diff --git a/tests/unit_tests/server/ut-zone-provision.cpp b/tests/unit_tests/server/ut-zone-provision.cpp index 45a74d7..034552e 100644 --- a/tests/unit_tests/server/ut-zone-provision.cpp +++ b/tests/unit_tests/server/ut-zone-provision.cpp @@ -69,7 +69,7 @@ struct Fixture { BOOST_REQUIRE(utils::saveFileContent(SOME_FILE_PATH.string(), "text")); } - ZoneProvision create(const std::vector& validLinkPrefixes) + static ZoneProvision create(const std::vector& validLinkPrefixes) { return ZoneProvision(ROOTFS_PATH.string(), TEST_CONFIG_PATH, @@ -78,12 +78,12 @@ struct Fixture { validLinkPrefixes); } - void load(ZoneProvisioningConfig& config) + static void load(ZoneProvisioningConfig& config) { config::loadFromKVStoreWithJsonFile(DB_PATH.string(), TEST_CONFIG_PATH, config, DB_PREFIX); } - void save(const ZoneProvisioningConfig& config) + static void save(const ZoneProvisioningConfig& config) { config::saveToKVStore(DB_PATH.string(), config, DB_PREFIX); } diff --git a/tests/unit_tests/server/ut-zone.cpp b/tests/unit_tests/server/ut-zone.cpp index e492c37..0072d74 100644 --- a/tests/unit_tests/server/ut-zone.cpp +++ b/tests/unit_tests/server/ut-zone.cpp @@ -102,12 +102,12 @@ struct Fixture { } - void ensureStarted() + static void ensureStarted() { // wait for zones init to fully start std::this_thread::sleep_for(std::chrono::milliseconds(200)); } - void ensureStop() + static void ensureStop() { // wait for fully stop std::this_thread::sleep_for(std::chrono::milliseconds(200)); diff --git a/wrapper/wrapper-compatibility.cpp b/wrapper/wrapper-compatibility.cpp index 634c187..04c043d 100644 --- a/wrapper/wrapper-compatibility.cpp +++ b/wrapper/wrapper-compatibility.cpp @@ -342,10 +342,9 @@ API int wait_for_pid_status(pid_t pid) API vsm_fso_type_t fso_string_to_type(char *str) { LOGS(""); - int len; int i; for (i = 0; i <= VSM_FSO_MAX_TYPE; i++) { - len = strlen(fso_type_strtab[i]); + int len = strlen(fso_type_strtab[i]); if (strncmp(str, fso_type_strtab[i], len) == 0) return static_cast(i); } @@ -358,12 +357,11 @@ API int mkdir_p(const char *dir, mode_t mode) LOGS(""); const char *tmp = dir; const char *orig = dir; - char *makeme; do { dir = tmp + strspn(tmp, "/"); tmp = dir + strcspn(dir, "/"); - makeme = strndup(orig, dir - orig); + char *makeme = strndup(orig, dir - orig); if (*makeme) { if (mkdir(makeme, mode) && errno != EEXIST) { free(makeme); @@ -421,8 +419,6 @@ API int remove_file(char *path) { LOGS(""); struct stat path_stat; - DIR *dp; - struct dirent *d; int status = 0; if (lstat(path, &path_stat) < 0) { @@ -433,6 +429,8 @@ API int remove_file(char *path) } if (S_ISDIR(path_stat.st_mode)) { + struct dirent *d; + DIR *dp; if ((dp = opendir(path)) == NULL) { ERROR("Unable to opendir %s", path); return -1; @@ -475,7 +473,6 @@ API int copy_file(const char *source, const char *dest, int /*flags*/) LOGS(""); int ret; FILE *sfp, *dfp; - size_t nread, nwritten, size = BUF_SIZE; char buffer[BUF_SIZE]; if ((sfp = fopen(source, "r")) == NULL) { @@ -490,6 +487,7 @@ API int copy_file(const char *source, const char *dest, int /*flags*/) } while (1) { + size_t nread, nwritten, size = BUF_SIZE; nread = fread(buffer, 1, size, sfp); if (nread != size && ferror(sfp)) { @@ -840,7 +838,7 @@ API pid_t get_init_pid(const char *name) fp = fopen(filename, "r"); if (fp != NULL) { - if (fscanf(fp, "%d", &ret) < 0) { + if (fscanf(fp, "%7d", &ret) < 0) { ERROR("Failed to read %s\n", filename); ret = -2; } @@ -894,7 +892,7 @@ API pid_t get_zone_pid(const char *name, const char *target) FILE *cmdfp; char cmdpath[PATH_MAX]; - res = sscanf(line, "%d", &pid); + res = sscanf(line, "%7d", &pid); if (res != 1) { ERROR("Failed to read %s\n", path); res = -1; @@ -915,7 +913,7 @@ API pid_t get_zone_pid(const char *name, const char *target) continue; } - if (fscanf(cmdfp, "%s", cmd) < 0) { + if (fscanf(cmdfp, "%1023s", cmd) < 0) { ERROR("Failed to read cmdline - pid : %d\n", pid); continue; } @@ -1271,11 +1269,12 @@ static int parse_statement(struct parser_context *ctx, int argc, char **argv, { struct parser_state state; char *args[PARSER_MAXARGS]; - int i, nargs, done, rc; + int i; int ret = 0; UNUSED(ctx); for (i = 0; i < argc; i++) { + int nargs, done, rc; done = nargs = 0; parser_init_state(&state, argv[i]); diff --git a/zone-daemon/daemon.cpp b/zone-daemon/daemon.cpp index 0ded40a..c4979e0 100644 --- a/zone-daemon/daemon.cpp +++ b/zone-daemon/daemon.cpp @@ -45,21 +45,23 @@ Daemon::~Daemon() { } -void Daemon::onNameLostCallback() +void Daemon::onNameLostCallback() const { + (void)this; // satisfy cpp-check //TODO: Try to reconnect or close the daemon. LOGE("Dbus name lost"); } -void Daemon::onGainFocusCallback() +void Daemon::onGainFocusCallback() const { + (void)this; // satisfy cpp-check LOGD("Gained Focus"); } -void Daemon::onLoseFocusCallback() +void Daemon::onLoseFocusCallback() const { + (void)this; // satisfy cpp-check LOGD("Lost Focus"); - } } // namespace zone_daemon diff --git a/zone-daemon/daemon.hpp b/zone-daemon/daemon.hpp index 92dcccf..ab52e69 100644 --- a/zone-daemon/daemon.hpp +++ b/zone-daemon/daemon.hpp @@ -40,9 +40,9 @@ public: virtual ~Daemon(); private: - void onNameLostCallback(); - void onGainFocusCallback(); - void onLoseFocusCallback(); + void onNameLostCallback() const; + void onGainFocusCallback() const; + void onLoseFocusCallback() const; std::unique_ptr mConnectionPtr; }; diff --git a/zone-daemon/runner.hpp b/zone-daemon/runner.hpp index 1a88530..1827553 100644 --- a/zone-daemon/runner.hpp +++ b/zone-daemon/runner.hpp @@ -39,13 +39,13 @@ public: /** * Starts all the daemon and blocks until SIGINT or SIGTERM */ - void run(); + static void run(); /** * Terminates the daemon. * Equivalent of sending SIGINT or SIGTERM signal */ - void terminate(); + static void terminate(); }; -- 2.7.4 From 36dfc7551f4fca721d364638b1a6191908ce34dd Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 30 Jul 2015 14:04:25 +0200 Subject: [PATCH 11/16] Support getting list of ip/mask for one interface, change netdev_set_ip* to netdev_add_ip* [Feature] Get list of configured ip/mask for zone interface [Cause] Many ip/mask entries can be assigned to one interface [Solution] Implement generic method getting list of network addresses [Verification] Build, install, use vsm net-add-ip to add ips vsm net-list to get configured ips Change-Id: I8d4c1b59e03800aa513811992cc13e71df8d599e --- cli/command-line-interface.cpp | 32 ++++++++------------ client/vasum-client-impl.cpp | 68 ++++++++++++++++++++++++++++-------------- client/vasum-client-impl.hpp | 29 +++++++++++++----- client/vasum-client.cpp | 49 +++++++++++++++++++++++++++--- client/vasum-client.h | 61 ++++++++++++++++++++++++++++++++++--- 5 files changed, 181 insertions(+), 58 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index e8922ee..86bc808 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -562,8 +562,6 @@ void netdev_list(const Args& argv) } else { VsmNetdev netdev = NULL; - in_addr ipv4; - in6_addr ipv6; char buf[INET_ADDRSTRLEN|INET6_ADDRSTRLEN]; CommandLineInterface::executeCallback(bind(vsm_lookup_netdev_by_name, _1, @@ -573,25 +571,21 @@ void netdev_list(const Args& argv) cout << netdevToString(netdev) << endl; vsm_netdev_free(netdev); - CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv4_addr, + VsmAddrList addrs = NULL; + CommandLineInterface::executeCallback(bind(vsm_netdev_get_ip_addr, _1, argv[1].c_str(), argv[2].c_str(), - &ipv4)); - if (inet_ntop(AF_INET, &ipv4, buf, INET_ADDRSTRLEN) == NULL) { - throw runtime_error("Wrong address received"); + &addrs)); + unsigned listsize = vsm_addrlist_size(addrs); + for (unsigned i=0; i < listsize; ++i) { + int type=vsm_addrlist_get_type(addrs, i); + if (inet_ntop(type, vsm_addrlist_get_addr(addrs, i), buf, INET6_ADDRSTRLEN) == NULL) { + throw runtime_error("Wrong address received ["+std::to_string(i)+"] type="+std::to_string(type)); + } + cout << buf << "/" << vsm_addrlist_get_prefix(addrs, i) << endl; } - cout << buf << endl; - - CommandLineInterface::executeCallback(bind(vsm_netdev_get_ipv6_addr, - _1, - argv[1].c_str(), - argv[2].c_str(), - &ipv6)); - if (inet_ntop(AF_INET6, &ipv6, buf, INET6_ADDRSTRLEN) == NULL) { - throw runtime_error("Wrong address received"); - } - cout << buf << endl; + vsm_addrlist_free(addrs); } } @@ -606,7 +600,7 @@ void netdev_add_ip_addr(const Args& argv) if (inet_pton(AF_INET, argv[3].c_str(), &addr) != 1) { throw runtime_error("Wrong address format"); }; - CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv4_addr, + CommandLineInterface::executeCallback(bind(vsm_netdev_add_ipv4_addr, _1, argv[1].c_str(), argv[2].c_str(), @@ -618,7 +612,7 @@ void netdev_add_ip_addr(const Args& argv) if (inet_pton(AF_INET6, argv[3].c_str(), &addr) != 1) { throw runtime_error("Wrong address format"); }; - CommandLineInterface::executeCallback(bind(vsm_netdev_set_ipv6_addr, + CommandLineInterface::executeCallback(bind(vsm_netdev_add_ipv6_addr, _1, argv[1].c_str(), argv[2].c_str(), diff --git a/client/vasum-client-impl.cpp b/client/vasum-client-impl.cpp index 15cccf6..e3aca1d 100644 --- a/client/vasum-client-impl.cpp +++ b/client/vasum-client-impl.cpp @@ -525,42 +525,48 @@ VsmStatus Client::vsm_zone_get_netdevs(const char* id, VsmArrayString* netdevIds VsmStatus Client::vsm_netdev_get_ip_addr(const char* id, const char* netdevId, - int type, - void* addr) noexcept + std::vector& addrs) noexcept { using namespace boost::algorithm; return coverException([&] { IS_SET(id); IS_SET(netdevId); - IS_SET(addr); + + addrs.clear(); api::GetNetDevAttrs attrs = *mClient->callSync( api::ipc::METHOD_GET_NETDEV_ATTRS, std::make_shared(api::GetNetDevAttrsIn{ id, netdevId })); - auto it = find_if(attrs.values.begin(), - attrs.values.end(), - [type](const api::StringPair& entry) { - return entry.first == (type == AF_INET ? "ipv4" : "ipv6"); - }); + for (const auto &attr : attrs.values) { + InetAddr addr; + if (attr.first == "ipv4") { + addr.type = AF_INET; + } + else if (attr.first == "ipv6") { + addr.type = AF_INET6; + } + else continue; - if (it != attrs.values.end()) { - vector addrAttrs; - for(auto addrAttr : split(addrAttrs, it->second, is_any_of(","))) { + std::vector addrAttrs; + for(const auto& addrAttr : split(addrAttrs, attr.second, is_any_of(","))) { size_t pos = addrAttr.find(":"); - if (addrAttr.substr(0, pos) == "ip") { - if (pos != string::npos && pos < addrAttr.length() && - inet_pton(type, addrAttr.substr(pos + 1).c_str(), addr) == 1) { - //XXX: return only one address - return; - } else { - throw InvalidResponseException("Wrong address format returned"); + if (pos == string::npos) continue; + + if (addrAttr.substr(0, pos) == "prefixlen") { + addr.prefix = atoi(addrAttr.substr(pos + 1).c_str()); + } + else if (addrAttr.substr(0, pos) == "ip") { + if (inet_pton(addr.type, addrAttr.substr(pos + 1).c_str(), &addr.addr) != 1) { + addr.type = -1; + break; } } } + if (addr.type >= 0) + addrs.push_back(addr); } - throw OperationFailedException("Address not found"); }); } @@ -568,17 +574,33 @@ VsmStatus Client::vsm_netdev_get_ipv4_addr(const char* id, const char* netdevId, struct in_addr* addr) noexcept { - return vsm_netdev_get_ip_addr(id, netdevId, AF_INET, addr); + std::vector addrs; + VsmStatus st=vsm_netdev_get_ip_addr(id, netdevId, addrs); + for (const auto& a : addrs) { + if (a.type == AF_INET) { + memcpy(addr, &a.addr, sizeof(*addr)); + break; + } + } + return st; } VsmStatus Client::vsm_netdev_get_ipv6_addr(const char* id, const char* netdevId, struct in6_addr* addr) noexcept { - return vsm_netdev_get_ip_addr(id, netdevId, AF_INET6, addr); + std::vector addrs; + VsmStatus st=vsm_netdev_get_ip_addr(id, netdevId, addrs); + for (const auto& a : addrs) { + if (a.type == AF_INET6) { + memcpy(addr, &a.addr, sizeof(*addr)); + break; + } + } + return st; } -VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* id, +VsmStatus Client::vsm_netdev_add_ipv4_addr(const char* id, const char* netdevId, struct in_addr* addr, int prefix) noexcept @@ -596,7 +618,7 @@ VsmStatus Client::vsm_netdev_set_ipv4_addr(const char* id, }); } -VsmStatus Client::vsm_netdev_set_ipv6_addr(const char* id, +VsmStatus Client::vsm_netdev_add_ipv6_addr(const char* id, const char* netdevId, struct in6_addr* addr, int prefix) noexcept diff --git a/client/vasum-client-impl.hpp b/client/vasum-client-impl.hpp index 4297c95..dbc9001 100644 --- a/client/vasum-client-impl.hpp +++ b/client/vasum-client-impl.hpp @@ -64,6 +64,19 @@ typedef struct NetdevStructure { } *Netdev; /** + * Network interface information structure + */ +typedef struct { + int type; + int prefix; + union { + struct in_addr ipv4; + struct in6_addr ipv6; + } addr; +} InetAddr; + + +/** * vasum's client definition. * * Client uses dbus API. @@ -227,6 +240,10 @@ public: */ VsmStatus vsm_zone_get_netdevs(const char* zone, VsmArrayString* netdevIds) noexcept; + VsmStatus vsm_netdev_get_ip_addr(const char* zone, + const char* netdevId, + std::vector& addrs) noexcept; + /** * @see ::vsm_netdev_get_ipv4_addr */ @@ -242,17 +259,17 @@ public: struct in6_addr *addr) noexcept; /** - * @see ::vsm_netdev_set_ipv4_addr + * @see ::vsm_netdev_add_ipv4_addr */ - VsmStatus vsm_netdev_set_ipv4_addr(const char* zone, + VsmStatus vsm_netdev_add_ipv4_addr(const char* zone, const char* netdevId, struct in_addr *addr, int prefix) noexcept; /** - * @see ::vsm_netdev_set_ipv6_addr + * @see ::vsm_netdev_add_ipv6_addr */ - VsmStatus vsm_netdev_set_ipv6_addr(const char* zone, + VsmStatus vsm_netdev_add_ipv6_addr(const char* zone, const char* netdevId, struct in6_addr *addr, int prefix) noexcept; @@ -376,10 +393,6 @@ private: bool isInternalDispatcherEnabled() const; ipc::epoll::EventPoll& getEventPoll() const; VsmStatus coverException(const std::function& worker) noexcept; - VsmStatus vsm_netdev_get_ip_addr(const char* zone, - const char* netdevId, - int type, - void* addr) noexcept; }; #endif /* VASUM_CLIENT_IMPL_HPP */ diff --git a/client/vasum-client.cpp b/client/vasum-client.cpp index ee3e6d4..9cf15b3 100644 --- a/client/vasum-client.cpp +++ b/client/vasum-client.cpp @@ -279,6 +279,21 @@ API VsmStatus vsm_zone_get_netdevs(VsmClient client, return getClient(client).vsm_zone_get_netdevs(zone, netdevIds); } +API VsmStatus vsm_netdev_get_ip_addr(VsmClient client, + const char* zone, + const char* netdevId, + VsmAddrList *addrs) +{ + std::vector addrlist; + VsmStatus status = getClient(client).vsm_netdev_get_ip_addr(zone, netdevId, addrlist); + int n = addrlist.size(); + InetAddr *a = (InetAddr *)malloc((n+1)*sizeof(InetAddr)); + std::copy(addrlist.begin(), addrlist.end(), a); + a[n].type=-1; + *addrs = a; + return status; +} + API VsmStatus vsm_netdev_get_ipv4_addr(VsmClient client, const char* zone, const char* netdevId, @@ -295,22 +310,22 @@ API VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, return getClient(client).vsm_netdev_get_ipv6_addr(zone, netdevId, addr); } -API VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, +API VsmStatus vsm_netdev_add_ipv4_addr(VsmClient client, const char* zone, const char* netdevId, struct in_addr *addr, int prefix) { - return getClient(client).vsm_netdev_set_ipv4_addr(zone, netdevId, addr, prefix); + return getClient(client).vsm_netdev_add_ipv4_addr(zone, netdevId, addr, prefix); } -API VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, +API VsmStatus vsm_netdev_add_ipv6_addr(VsmClient client, const char* zone, const char* netdevId, struct in6_addr *addr, int prefix) { - return getClient(client).vsm_netdev_set_ipv6_addr(zone, netdevId, addr, prefix); + return getClient(client).vsm_netdev_add_ipv6_addr(zone, netdevId, addr, prefix); } API VsmStatus vsm_netdev_del_ipv4_addr(VsmClient client, @@ -430,3 +445,29 @@ API VsmStatus vsm_clean_up_zones_root(VsmClient client) return getClient(client).vsm_clean_up_zones_root(); } +API unsigned int vsm_addrlist_size(VsmAddrList addrs) +{ + InetAddr *a = static_cast(addrs); + unsigned int i; + for (i = 0; a[i].type >= 0; ++i) ; + return i; +} + +API int vsm_addrlist_get_type(VsmAddrList addrs, unsigned int i) +{ + return static_cast(addrs)[i].type; +} + +API const void *vsm_addrlist_get_addr(VsmAddrList addrs, unsigned int i) +{ + return &static_cast(addrs)[i].addr; +} + +API unsigned int vsm_addrlist_get_prefix(VsmAddrList addrs, unsigned int i) +{ + return static_cast(addrs)[i].prefix; +} + +API void vsm_addrlist_free(VsmAddrList addrs) { + free(addrs); +} diff --git a/client/vasum-client.h b/client/vasum-client.h index b696dcf..253306d 100644 --- a/client/vasum-client.h +++ b/client/vasum-client.h @@ -186,6 +186,7 @@ typedef char* VsmString; */ typedef VsmString* VsmArrayString; +typedef void *VsmAddrList; /** * Completion status of libvasum-client's functions */ @@ -652,6 +653,29 @@ VsmStatus vsm_revoke_device(VsmClient client, const char* zone, const char* devi */ VsmStatus vsm_zone_get_netdevs(VsmClient client, const char* zone, VsmArrayString* netdevIds); + +/** + * Get ipv4 address for given netdevId + * + * @param[in] client vasum-server's client + * @param[in] zone zone name + * @param[in] netdevId netdev id + * @param[out] addrs ip address array + * @return status of this function call + * @remark Use vsm_netdev_addr_free() to free memory occupied by address array. + */ +VsmStatus vsm_netdev_get_ip_addr(VsmClient client, + const char* zone, + const char* netdevId, + VsmAddrList *addrs); + +/** + * Release VsmAddrList + * + * @param addrs VsmAddrList + */ +void vsm_addrlist_free(VsmAddrList addrs); + /** * Get ipv4 address for given netdevId * @@ -681,7 +705,7 @@ VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, struct in6_addr *addr); /** - * Set ipv4 address for given netdevId + * Add ipv4 address for given netdevId * * @param[in] client vasum-server's client * @param[in] zone zone name @@ -690,14 +714,14 @@ VsmStatus vsm_netdev_get_ipv6_addr(VsmClient client, * @param[in] prefix bit-length of the network prefix * @return status of this function call */ -VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, +VsmStatus vsm_netdev_add_ipv4_addr(VsmClient client, const char* zone, const char* netdevId, struct in_addr *addr, int prefix); /** - * Set ipv6 address for given netdevId + * Add ipv6 address for given netdevId * * @param[in] client vasum-server's client * @param[in] zone zone name @@ -706,7 +730,7 @@ VsmStatus vsm_netdev_set_ipv4_addr(VsmClient client, * @param[in] prefix bit-length of the network prefix * @return status of this function call */ -VsmStatus vsm_netdev_set_ipv6_addr(VsmClient client, +VsmStatus vsm_netdev_add_ipv6_addr(VsmClient client, const char* zone, const char* netdevId, struct in6_addr *addr, @@ -933,6 +957,35 @@ VsmStatus vsm_remove_declaration(VsmClient client, */ VsmStatus vsm_clean_up_zones_root(VsmClient client); +/** + * Retrieve array size + * + * @return array size + */ +unsigned int vsm_addrlist_size(VsmAddrList addrs); + +/** + * Get address type for i'th entry + * + * @return network type (AF_INET or AF_INET6) + */ +int vsm_addrlist_get_type(VsmAddrList addrs, unsigned int i); + +/** + * Get pointer to in_addr property for i'th entry + * see inet_ntop man pages + * + * @return poiner of in_addr + */ +const void *vsm_addrlist_get_addr(VsmAddrList addrs, unsigned int i); + +/** + * Get address prefix for i'th entry + * + * @return adress prefix (mask bits count) + */ +unsigned int vsm_addrlist_get_prefix(VsmAddrList addrs, unsigned int i); + #endif /* __VASUM_WRAPPER_SOURCE__ */ #ifdef __cplusplus -- 2.7.4 From 2e5e4e040a25202b5fdc868b24f0c2e0ba183662 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Fri, 7 Aug 2015 11:05:14 +0200 Subject: [PATCH 12/16] vsm: persistant history of entered commands in interactive mode [Feature] Keep history of entered commands (in ~/.vsm_history file) [Cause] No history after starting vsm [Solution] use read_history/write_history from readline library [Verification] Build, install, use vsm command Change-Id: I107ba49ac01d52825312416a11fbb094735401af --- cli/main.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cli/main.cpp b/cli/main.cpp index 9bec3e7..48a71f2 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -36,12 +36,15 @@ #include #include #include +#include #include #include using namespace vasum::cli; +namespace fs = boost::filesystem; + namespace { static int interactiveMode = 0; @@ -480,6 +483,12 @@ int cliMode(const int argc, const char** argv) return rc; } +fs::path getHomePath() { + const char *h = ::getenv("HOME"); + return fs::path(h ? h : ""); +} + + } // namespace @@ -508,13 +517,24 @@ int main(const int argc, const char *argv[]) } } else { + fs::path historyfile(".vsm_history"); if (isatty(0) == 1) { + fs::path home = getHomePath(); + if (!home.empty()) { + historyfile = home / historyfile; + } + ::read_history(historyfile.c_str()); + interactiveMode = 1; ::rl_attempted_completion_function = completion; } rc = processStream(std::cin); + + if (interactiveMode) { + ::write_history(historyfile.c_str()); + } } disconnect(); -- 2.7.4 From ec1876ec7d820ad1cd34952f5e63e1592c17cf65 Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Wed, 19 Aug 2015 12:51:22 +0200 Subject: [PATCH 13/16] vsm: make vsm> prompt, finish prompt line on exit in interactive mode [Feature] 'vsm>' prompt, new line when exit [Cause] prompt gives clear info what is the interpreter [Solution] N/A [Verification] Build, install, use vsm command Change-Id: Ic5b6751dbebc38d204565311380d6459448d25a0 --- cli/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/main.cpp b/cli/main.cpp index 48a71f2..739d293 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -390,7 +390,7 @@ static int processStream(std::istream& stream) int rc = EXIT_FAILURE; std::string ln; - while (readline_from(">>> ", stream, ln)) { + while (readline_from("vsm> ", stream, ln)) { if (ln.empty() || ln[0] == '#') { //skip empty line or comment continue; } @@ -534,6 +534,7 @@ int main(const int argc, const char *argv[]) if (interactiveMode) { ::write_history(historyfile.c_str()); + std::cout << std::endl; // finish prompt line } } -- 2.7.4 From e17105e146dd179c4f2cb756022cc995ad4cddec Mon Sep 17 00:00:00 2001 From: Krzysztof Dynowski Date: Thu, 20 Aug 2015 16:10:52 +0200 Subject: [PATCH 14/16] vsm: update network related help, formating changes [Feature] clarify help for net-create command [Cause] poor help on how to create virtual network [Solution] extend description format, inerpretation [Verification] Build, install, run vsm net-create help Change-Id: I09b877d090607fb9f8d3c5b367f6f6b2d1a7ed85 --- cli/command-line-interface.cpp | 37 +++++++++++++++++++++++++++--------- cli/main.cpp | 43 +++++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/cli/command-line-interface.cpp b/cli/command-line-interface.cpp index 86bc808..93201c3 100644 --- a/cli/command-line-interface.cpp +++ b/cli/command-line-interface.cpp @@ -143,6 +143,18 @@ void buildZoneList(std::vector& list) vsm_array_string_free(ids); } +void buildNetdevList(const std::string& zone,std::vector& list) +{ + using namespace std::placeholders; + VsmArrayString ids; + + CommandLineInterface::executeCallback(bind(vsm_zone_get_netdevs, _1, zone.c_str(), &ids)); + for (VsmString* id = ids; *id; ++id) { + list.push_back(*id); + } + vsm_array_string_free(ids); +} + } // namespace const std::vector CommandLineInterface::buildCompletionList(const Args& a) const @@ -165,9 +177,7 @@ const std::vector CommandLineInterface::buildCompletionList(const A buildZoneList(v); } else if (ss == "{NETDEV}") { - //TODO: get list of available interfaces - v.push_back("lo"); - v.push_back("eth0"); + buildNetdevList(a[a.size()-2],v); // zone name must precede netdev } else if (ss.length() > 0) { v.push_back(ss); @@ -242,19 +252,28 @@ const std::string& CommandLineInterface::getDescription() const void CommandLineInterface::printUsage(std::ostream& out) const { - out << mName; + out << "Syntax\n"; + out << "\t" << mName; for (const auto& args : mArgsSpec) { out << " " << args.name; } out << "\n\n" - << "\tDescription\n" - << "\t\t" << mDescription << "\n"; + "Description\n" + "\t" << mDescription << "\n"; if (!mArgsSpec.empty()) { - out << "\n\tOptions\n"; + out << "\n" + "Options\n"; for (const auto& args : mArgsSpec) { - out << "\t\t" << args.name << " -- " << args.description << "\n"; + out << "\t" << args.name << " -- "; + const std::string& d=args.description; + std::stringstream ss(d); + std::string item; + std::getline(ss, item); + out << item << std::endl; + while (std::getline(ss, item)) + out << "\t\t" << item << std::endl; } } out << "\n"; @@ -484,7 +503,7 @@ void create_netdev(const Args& argv) { using namespace std::placeholders; - if (argv.size() < 2) { + if (argv.size() < 3) { throw runtime_error("Not enough parameters"); } diff --git a/cli/main.cpp b/cli/main.cpp index 739d293..528c4c8 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -153,14 +153,24 @@ std::vector commands = { { create_netdev, "net-create", - "Create network interface in zone", + "Create network virtualization for the zone", MODE_COMMAND_LINE | MODE_INTERACTIVE, { {"zone_id", "zone name", "{ZONE}"}, - {"netdevtype", "interface type", "macvlan|phys|veth"}, + {"netdevtype", "interface type (veth, macvlan, phys)\n" + " veth - create new zone iface and bridge to host\n" + "macvlan - create new zone slave iface briged to master with specified mode\n" + " phys - move existing iface from host to zone (no way to move it back)", + "veth|macvlan|phys" + }, {"zone_netdev", "interface name (eth0)", "eth0|eth1"}, {"host_netdev", "bridge name (virbr0)", "virbr0|virbr1"}, - {"mode", "macvlan mode (private, vepa, bridge, passthru)", "private|vepa|bridge|passthru"}} + {"mode", "macvlan mode (private, vepa, bridge, passthru)\n" + " private - bridge but no comunicate with otheri vlan\n" + " vepa - ethernet switch\n" + " bridge - light weight to other vlan\n" + "passthru - only one vlan device", + "private|vepa|bridge|passthru"}} }, { destroy_netdev, @@ -242,14 +252,14 @@ void printUsage(std::ostream& out, const std::string& name, unsigned int mode) n = name + " "; } + out << "Usage: " << n << "[-h|help|-f |[ [-h|help|]]]\n\n"; if (mode == MODE_COMMAND_LINE) { - out << "Usage: " << n << "[-h|-f |[ [-h|]]\n" - << "Called without parameters enters interactive mode.\n" + out << "Description:\n" + << "\tCommand line tool to manage vasum containers.\n" + << "\tCalled without parameters enters interactive mode.\n" << "Options:\n" - << "-h print help\n" - << "-f read and execute commands from file\n\n"; - } else { - out << "Usage: [-h| [-h|]]\n\n"; + << "\t-h,help print this help\n" + << "\t-f read and execute commands from file\n\n"; } out << "command can be one of the following:\n"; @@ -258,12 +268,16 @@ void printUsage(std::ostream& out, const std::string& name, unsigned int mode) if (std::find(addLineBefore.begin(), addLineBefore.end(), command.getName()) != addLineBefore.end()) { out << std::endl; } - out << " " << std::setw(25) << std::left << command.getName() - << command.getDescription() << std::endl; + out << " " << std::setw(20) << std::left << command.getName(); + const std::string& d = command.getDescription(); + std::stringstream ss(d); + std::string item; + std::getline(ss, item); + out << item << std::endl; } } - out << "\nSee " << n << "command -h to read about a specific one.\n"; + out << "\nType '" << n << "command help' to read about a specific one.\n"; } int connect() @@ -303,8 +317,7 @@ int executeCommand(const Args& argv, int mode) return EXIT_FAILURE; } - auto it = std::find(argv.begin(), argv.end(), std::string("-h")); - if (it != argv.end()) { + if (argv.size() > 1 && (argv[1] == "-h" || argv[1] == "help")) { command.printUsage(std::cout); return EXIT_SUCCESS; } @@ -466,7 +479,7 @@ int bashComplMode(int argc, const char *argv[]) int cliMode(const int argc, const char** argv) { - if (std::string(argv[1]) == "-h") { + if (std::string(argv[1]) == "-h" || std::string(argv[1]) == "help") { printUsage(std::cout, argv[0], MODE_COMMAND_LINE); return EXIT_SUCCESS; } -- 2.7.4 From f220ce5658986db5cb0a7e67631b28ac41a678fb Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Tue, 18 Aug 2015 12:02:12 +0200 Subject: [PATCH 15/16] lxcpp: Simple attach implementation [Feature] Running code in the container's context Socketpair wrapper - Channel [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: Ib5b1011c5f8578ab9e258bcbea3cd7aa3bc233a3 --- common/utils/channel.cpp | 83 +++++++++++++++++++++++++++ common/utils/channel.hpp | 103 ++++++++++++++++++++++++++++++++++ common/utils/execute.cpp | 2 +- common/utils/fd-utils.cpp | 13 +++++ common/utils/fd-utils.hpp | 5 ++ libs/lxcpp/container-impl.cpp | 58 +++++++++++++++++++ libs/lxcpp/container-impl.hpp | 16 ++++++ libs/lxcpp/container.hpp | 6 ++ libs/lxcpp/process.cpp | 86 ++++++++++++++++++++++------ libs/lxcpp/process.hpp | 11 +++- tests/unit_tests/lxcpp/ut-process.cpp | 44 +++++++-------- tests/unit_tests/utils/ut-channel.cpp | 76 +++++++++++++++++++++++++ 12 files changed, 459 insertions(+), 44 deletions(-) create mode 100644 common/utils/channel.cpp create mode 100644 common/utils/channel.hpp create mode 100644 tests/unit_tests/utils/ut-channel.cpp diff --git a/common/utils/channel.cpp b/common/utils/channel.cpp new file mode 100644 index 0000000..fbb110d --- /dev/null +++ b/common/utils/channel.cpp @@ -0,0 +1,83 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief IPC implementation for related processes + */ + +#include "utils/channel.hpp" +#include "utils/exception.hpp" + +#include "logger/logger.hpp" + +#include + +namespace { +const int LEFT = 0; +const int RIGHT = 1; +} + +namespace utils { + +Channel::Channel() + : mSocketIndex(-1) +{ + if (::socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, mSockets) < 0) { + const std::string msg = "socketpair() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } +} + +Channel::~Channel() +{ + closeSocket(LEFT); + closeSocket(RIGHT); +} + +void Channel::setLeft() +{ + mSocketIndex = LEFT; + utils::close(mSockets[RIGHT]); + mSockets[RIGHT] = -1; +} + +void Channel::setRight() +{ + mSocketIndex = RIGHT; + utils::close(mSockets[LEFT]); + mSockets[LEFT] = -1; +} + +void Channel::shutdown() +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + closeSocket(mSocketIndex); +} + +void Channel::closeSocket(int socketIndex) +{ + utils::shutdown(mSockets[socketIndex]); + utils::close(mSockets[socketIndex]); + mSockets[socketIndex] = -1; +} + +} // namespace utils diff --git a/common/utils/channel.hpp b/common/utils/channel.hpp new file mode 100644 index 0000000..f537121 --- /dev/null +++ b/common/utils/channel.hpp @@ -0,0 +1,103 @@ +/* +* Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved +* +* Contact: Jan Olszak +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief IPC implementation for related processes + */ + +#ifndef COMMON_UTILS_CHANNEL_HPP +#define COMMON_UTILS_CHANNEL_HPP + +#include "utils/fd-utils.hpp" +#include + +namespace utils { + +/** + * Channel is implemented with a pair of anonymous sockets + */ +class Channel { +public: + Channel(); + ~Channel(); + + Channel(const Channel&) = delete; + Channel& operator=(const Channel&) = delete; + + /** + * Use the "left" end of the channel + * Closes the "right" end + */ + void setLeft(); + + /** + * Use the "right" end of the channel + * Closes the "left" end + */ + void setRight(); + + /** + * Gracefully shutdown the used end of the channel + */ + void shutdown(); + + /** + * Send the data to the other end of the channel + * + * @param data data to send + */ + template + void write(const Data& data); + + /** + * Receive data of a given type (size) + */ + template + Data read(); + +private: + + void closeSocket(int socketIndex); + + int mSocketIndex; + int mSockets[2]; +}; + +template +void Channel::write(const Data& data) +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + + utils::write(mSockets[mSocketIndex], &data, sizeof(Data)); +} + +template +Data Channel::read() +{ + assert(mSocketIndex != -1 && "Channel's end isn't set"); + + Data data; + utils::read(mSockets[mSocketIndex], &data, sizeof(Data)); + return data; +} + +} // namespace utils + +#endif // COMMON_UTILS_CHANNEL_HPP diff --git a/common/utils/execute.cpp b/common/utils/execute.cpp index 55fc912..d047a2e 100644 --- a/common/utils/execute.cpp +++ b/common/utils/execute.cpp @@ -118,7 +118,7 @@ bool executeAndWait(const char* fname, const char* const* argv) bool waitPid(pid_t pid, int& status) { - while (waitpid(pid, &status, 0) == -1) { + while (::waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { LOGE("waitpid() failed: " << getSystemErrorMessage()); return false; diff --git a/common/utils/fd-utils.cpp b/common/utils/fd-utils.cpp index 4fe9a17..030d1fc 100644 --- a/common/utils/fd-utils.cpp +++ b/common/utils/fd-utils.cpp @@ -111,6 +111,19 @@ void close(int fd) } } +void shutdown(int fd) +{ + if (fd < 0) { + return; + } + + if (-1 == ::shutdown(fd, SHUT_RDWR)) { + std::string msg = "shutdown() failed: " + getSystemErrorMessage(); + LOGE(msg); + throw UtilsException(msg); + } +} + void write(int fd, const void* bufferPtr, const size_t size, int timeoutMS) { chr::high_resolution_clock::time_point deadline = chr::high_resolution_clock::now() + diff --git a/common/utils/fd-utils.hpp b/common/utils/fd-utils.hpp index d6a4032..56e3f41 100644 --- a/common/utils/fd-utils.hpp +++ b/common/utils/fd-utils.hpp @@ -35,6 +35,11 @@ namespace utils { void close(int fd); /** + * Shut down part of a full-duplex connection + */ +void shutdown(int fd); + +/** * Write to a file descriptor, throw on error. * * @param fd file descriptor diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index e4f290d..6fd9775 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -23,6 +23,11 @@ #include "lxcpp/container-impl.hpp" #include "lxcpp/exception.hpp" +#include "lxcpp/process.hpp" + +#include "utils/exception.hpp" + +#include namespace lxcpp { @@ -94,4 +99,57 @@ std::string ContainerImpl::getRootPath() throw NotImplementedException(); } + +int ContainerImpl::attachChild(void* data) { + try { + return (*static_cast(data))(); + } catch(...) { + return -1; // Non-zero on failure + } + return 0; // Success +} + +void ContainerImpl::attachParent(utils::Channel& channel, const pid_t interPid) +{ + // TODO: Setup cgroups etc + pid_t childPid = channel.read(); + + // Wait for the Intermediate process + lxcpp::waitpid(interPid); + + // Wait for the Child process + lxcpp::waitpid(childPid); +} + +void ContainerImpl::attachIntermediate(utils::Channel& channel, Container::AttachCall& call) +{ + lxcpp::setns(mInitPid, mNamespaces); + + // PID namespace won't affect the returned pid + // CLONE_PARENT: Child's PPID == Caller's PID + const pid_t pid = lxcpp::clone(&ContainerImpl::attachChild, + &call, + CLONE_PARENT); + channel.write(pid); +} + +void ContainerImpl::attach(Container::AttachCall& call) +{ + utils::Channel channel; + + const pid_t interPid = lxcpp::fork(); + if (interPid > 0) { + channel.setLeft(); + attachParent(channel, interPid); + channel.shutdown(); + } else { + channel.setRight(); + attachIntermediate(channel, call); + channel.shutdown(); + ::_exit(0); + } +} + + + } // namespace lxcpp diff --git a/libs/lxcpp/container-impl.hpp b/libs/lxcpp/container-impl.hpp index f5d9547..e236932 100644 --- a/libs/lxcpp/container-impl.hpp +++ b/libs/lxcpp/container-impl.hpp @@ -25,6 +25,9 @@ #define LXCPP_CONTAINER_IMPL_HPP #include "lxcpp/container.hpp" +#include "lxcpp/namespace.hpp" + +#include "utils/channel.hpp" namespace lxcpp { @@ -49,6 +52,19 @@ public: void destroy(); void setRootPath(const std::string& path); std::string getRootPath(); + + // Other + void attach(Container::AttachCall& attachCall); + +private: + + // Methods for different stages of setting up the attachment + void attachParent(utils::Channel& channel, const pid_t pid); + void attachIntermediate(utils::Channel& channel, Container::AttachCall& call); + static int attachChild(void* data); + + pid_t mInitPid; + std::vector mNamespaces; }; } // namespace lxcpp diff --git a/libs/lxcpp/container.hpp b/libs/lxcpp/container.hpp index 74caa51..4f2d697 100644 --- a/libs/lxcpp/container.hpp +++ b/libs/lxcpp/container.hpp @@ -25,11 +25,14 @@ #define LXCPP_CONTAINER_HPP #include +#include namespace lxcpp { class Container { public: + typedef std::function AttachCall; + virtual ~Container() {}; virtual std::string getName() = 0; @@ -48,6 +51,9 @@ public: virtual void destroy() = 0; virtual void setRootPath(const std::string& path) = 0; virtual std::string getRootPath() = 0; + + // Other + virtual void attach(AttachCall& attachCall) = 0; }; } // namespace lxcpp diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index 68fd62c..b707353 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -36,10 +36,21 @@ namespace lxcpp { +pid_t fork() +{ + pid_t pid = ::fork(); + if (pid < 0) { + const std::string msg = "fork() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } + return pid; +} + pid_t clone(int (*function)(void *), void *args, - const std::vector& namespaces, - const int additionalFlags) + const int flags) { // Won't fail, well known resource name size_t stackSize = ::sysconf(_SC_PAGESIZE); @@ -47,56 +58,66 @@ pid_t clone(int (*function)(void *), // PAGESIZE is enough, it'll exec after this char *stack = static_cast(::alloca(stackSize)); - pid_t pid = ::clone(function, stack + stackSize, toFlag(namespaces) | additionalFlags | SIGCHLD, args); + pid_t pid = ::clone(function, stack + stackSize, flags | SIGCHLD, args); if (pid < 0) { - const std::string msg = utils::getSystemErrorMessage(); - LOGE("clone() failed: " << msg); - throw ProcessSetupException("clone() failed " + msg); + const std::string msg = "clone() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); } return pid; } -void setns(const std::vector& namespaces) +pid_t clone(int (*function)(void *), + void *args, + const std::vector& namespaces, + const int additionalFlags) { - pid_t pid = ::getpid(); + return clone(function, args, toFlag(namespaces) | additionalFlags); +} +void setns(const pid_t pid, const std::vector& namespaces) +{ int dirFD = ::open(getNsPath(pid).c_str(), O_DIRECTORY | O_CLOEXEC); if(dirFD < 0) { - const std::string msg = utils::getSystemErrorMessage(); - LOGE("open() failed: " << msg); - throw ProcessSetupException("open() failed: " + msg); + const std::string msg = "open() failed: " + + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); } // Open FDs connected with the requested namespaces std::vector fds(namespaces.size(), -1); for(size_t i = 0; i < namespaces.size(); ++i) { - fds[i] = ::openat(dirFD, toString(namespaces[i]).c_str(), O_RDONLY | O_CLOEXEC); + fds[i] = ::openat(dirFD, + toString(namespaces[i]).c_str(), + O_RDONLY | O_CLOEXEC); if(fds[i] < 0) { - const std::string msg = utils::getSystemErrorMessage(); + const std::string msg = "openat() failed: " + utils::getSystemErrorMessage(); for (size_t j = 0; j < i; ++j) { utils::close(fds[j]); } utils::close(dirFD); - LOGE("openat() failed: " << msg); - throw ProcessSetupException("openat() failed: " + msg); + LOGE(msg); + throw ProcessSetupException(msg); } } // Setns for every namespace for(size_t i = 0; i < fds.size(); ++i) { if(-1 == ::setns(fds[i], toFlag(namespaces[i]))) { - const std::string msg = utils::getSystemErrorMessage(); + const std::string msg = "setns() failed: " + utils::getSystemErrorMessage(); for (size_t j = i; j < fds.size(); ++j) { utils::close(fds[j]); } utils::close(dirFD); - LOGE("setns() failed: " << msg); - throw ProcessSetupException("setns() failed: " + msg); + LOGE(msg); + throw ProcessSetupException(msg); } utils::close(fds[i]); } @@ -104,4 +125,33 @@ void setns(const std::vector& namespaces) utils::close(dirFD); } +int waitpid(const pid_t pid) +{ + int status; + while (-1 == ::waitpid(pid, &status, 0)) { + if (errno == EINTR) { + LOGT("waitpid() interrupted, retrying"); + continue; + } + const std::string msg = "waitpid() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } + + // Return child's return status if everything is OK + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + + // Something went wrong in the child + std::string msg; + if (WIFSIGNALED(status)) { + msg = "Child killed by signal " + std::to_string(WTERMSIG(status)); + } else { + msg = "Unknown eror in child process"; + } + LOGE(msg); + throw ProcessSetupException(msg); +} + } // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index 1255589..a640e1a 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -31,12 +31,21 @@ namespace lxcpp { +pid_t fork(); + +pid_t clone(int (*function)(void *), + void *args, + const int flags); + pid_t clone(int (*function)(void *), void *args, const std::vector& namespaces, const int additionalFlags = 0); -void setns(const std::vector& namespaces); +void setns(const pid_t pid, + const std::vector& namespaces); + +int waitpid(const pid_t pid); } // namespace lxcpp diff --git a/tests/unit_tests/lxcpp/ut-process.cpp b/tests/unit_tests/lxcpp/ut-process.cpp index cae893a..662dd82 100644 --- a/tests/unit_tests/lxcpp/ut-process.cpp +++ b/tests/unit_tests/lxcpp/ut-process.cpp @@ -63,8 +63,8 @@ const std::vector NAMESPACES {{ BOOST_AUTO_TEST_CASE(Clone) { - BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, NAMESPACES)); - BOOST_CHECK_NO_THROW(clone(clonefn, nullptr, {Namespace::MNT})); + BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, NAMESPACES)); + BOOST_CHECK_NO_THROW(lxcpp::clone(clonefn, nullptr, {Namespace::MNT})); } BOOST_AUTO_TEST_CASE(Setns) @@ -72,22 +72,20 @@ BOOST_AUTO_TEST_CASE(Setns) const int TEST_PASSED = 0; const int ERROR = 1; - pid_t pid = fork(); - if (pid==-1) { - BOOST_REQUIRE(false); - } else if(pid ==0) { + pid_t pid = lxcpp::fork(); + if (pid == 0) { try { - setns({Namespace::MNT, - Namespace::PID, - Namespace::UTS, - Namespace::IPC, - Namespace::NET - }); - _exit(TEST_PASSED); + lxcpp::setns(::getpid(), {Namespace::MNT, + Namespace::PID, + Namespace::UTS, + Namespace::IPC, + Namespace::NET + }); + ::_exit(TEST_PASSED); } catch(...) { - _exit(ERROR); + ::_exit(ERROR); } - } else if(pid>0) { + } else if (pid > 0) { int status = -1; BOOST_REQUIRE(utils::waitPid(pid, status)); BOOST_REQUIRE(status == TEST_PASSED); @@ -99,19 +97,17 @@ BOOST_AUTO_TEST_CASE(SetnsUserNamespace) const int TEST_PASSED = 0; const int ERROR = -1; - pid_t pid = fork(); - if (pid==-1) { - BOOST_REQUIRE(false); - } else if(pid ==0) { + pid_t pid = lxcpp::fork(); + if (pid == 0) { try { - setns({Namespace::USER}); - _exit(ERROR); + lxcpp::setns(::getpid(), {Namespace::USER}); + ::_exit(ERROR); } catch(ProcessSetupException) { - _exit(TEST_PASSED); + ::_exit(TEST_PASSED); } catch(...) { - _exit(ERROR); + ::_exit(ERROR); } - } else if(pid>0) { + } else if (pid > 0) { int status; BOOST_REQUIRE(utils::waitPid(pid, status)); BOOST_REQUIRE(status == TEST_PASSED); diff --git a/tests/unit_tests/utils/ut-channel.cpp b/tests/unit_tests/utils/ut-channel.cpp new file mode 100644 index 0000000..8c469ee --- /dev/null +++ b/tests/unit_tests/utils/ut-channel.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief Unit tests of the channel class + */ + +#include "config.hpp" +#include "ut.hpp" + +#include "utils/channel.hpp" +#include "utils/execute.hpp" + +BOOST_AUTO_TEST_SUITE(ChannelSuite) + +using namespace utils; + +BOOST_AUTO_TEST_CASE(ConstructorDestructor) +{ + Channel c; +} + +BOOST_AUTO_TEST_CASE(SetLeftRight) +{ + const int TEST_PASSED = 0; + const int ERROR = 1; + const int DATA = 1234; + + Channel c; + + pid_t pid = ::fork(); + if (pid == -1) { + BOOST_REQUIRE(false); + } + + if (pid == 0) { + try { + c.setLeft(); + c.write(DATA); + c.shutdown(); + ::_exit(TEST_PASSED); + } catch(...) { + ::_exit(ERROR); + } + } + + c.setRight(); + + int recData = c.read(); + + BOOST_REQUIRE(recData == DATA); + + int status = -1; + BOOST_REQUIRE(utils::waitPid(pid, status)); + BOOST_REQUIRE(status == TEST_PASSED); + c.shutdown(); + +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From 690e0f29a5c8ede2c6edce6aaa37c4a4d87b0a02 Mon Sep 17 00:00:00 2001 From: Jan Olszak Date: Mon, 24 Aug 2015 11:13:05 +0200 Subject: [PATCH 16/16] lxcpp: Remounting /proc and /sys on attach [Feature] Remounting /proc /sys Filesystem handling functions [Cause] N/A [Solution] N/A [Verification] Build, install, run tests Change-Id: I8015b7a66c1fabab9133ad360e9e7a45c069082c --- libs/lxcpp/container-impl.cpp | 32 +++++++++++ libs/lxcpp/exception.hpp | 5 ++ libs/lxcpp/filesystem.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++ libs/lxcpp/filesystem.hpp | 52 ++++++++++++++++++ libs/lxcpp/process.cpp | 8 +++ libs/lxcpp/process.hpp | 2 + 6 files changed, 221 insertions(+) create mode 100644 libs/lxcpp/filesystem.cpp create mode 100644 libs/lxcpp/filesystem.hpp diff --git a/libs/lxcpp/container-impl.cpp b/libs/lxcpp/container-impl.cpp index 6fd9775..2ac232f 100644 --- a/libs/lxcpp/container-impl.cpp +++ b/libs/lxcpp/container-impl.cpp @@ -24,10 +24,14 @@ #include "lxcpp/container-impl.hpp" #include "lxcpp/exception.hpp" #include "lxcpp/process.hpp" +#include "lxcpp/filesystem.hpp" +#include "lxcpp/namespace.hpp" #include "utils/exception.hpp" #include +#include + namespace lxcpp { @@ -99,9 +103,37 @@ std::string ContainerImpl::getRootPath() throw NotImplementedException(); } +namespace { +void setupMountPoints() +{ + /* TODO: Uncomment when preparing the final attach() version + + // TODO: This unshare should be optional only if we attach to PID/NET namespace, but not MNT. + // Otherwise container already has remounted /proc /sys + lxcpp::unshare(Namespace::MNT); + + if (isMountPointShared("/")) { + // TODO: Handle case when the container rootfs or mount location is MS_SHARED, but not '/' + lxcpp::mount(nullptr, "/", nullptr, MS_SLAVE | MS_REC, nullptr); + } + + if(isMountPoint("/proc")) { + lxcpp::umount("/proc", MNT_DETACH); + lxcpp::mount("none", "/proc", "proc", 0, nullptr); + } + + if(isMountPoint("/sys")) { + lxcpp::umount("/sys", MNT_DETACH); + lxcpp::mount("none", "/sys", "sysfs", 0, nullptr); + } + + */ +} +} // namespace int ContainerImpl::attachChild(void* data) { try { + setupMountPoints(); return (*static_cast(data))(); } catch(...) { return -1; // Non-zero on failure diff --git a/libs/lxcpp/exception.hpp b/libs/lxcpp/exception.hpp index 12ae299..bdb56f7 100644 --- a/libs/lxcpp/exception.hpp +++ b/libs/lxcpp/exception.hpp @@ -46,6 +46,11 @@ struct ProcessSetupException: public Exception { : Exception(message) {} }; +struct FileSystemSetupException: public Exception { + FileSystemSetupException(const std::string& message = "Error during a file system operation") + : Exception(message) {} +}; + struct BadArgument: public Exception { BadArgument(const std::string& message = "Bad argument passed") : Exception(message) {} diff --git a/libs/lxcpp/filesystem.cpp b/libs/lxcpp/filesystem.cpp new file mode 100644 index 0000000..c085861 --- /dev/null +++ b/libs/lxcpp/filesystem.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief file system handling routines + */ + +#include "lxcpp/filesystem.hpp" +#include "lxcpp/exception.hpp" +#include "lxcpp/process.hpp" + +#include "utils/paths.hpp" +#include "utils/exception.hpp" +#include "logger/logger.hpp" + +#include +#include +#include +#include +#include + + +namespace lxcpp { + +void mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data) +{ + if (-1 == ::mount(source.c_str(), + target.c_str(), + filesystemtype.c_str(), + mountflags, + data.c_str())) { + const std::string msg = "mount() failed: src:" + source + + ", tgt: " + target + + ", filesystemtype: " + filesystemtype + + ", mountflags: " + std::to_string(mountflags) + + ", data: " + data + + ", msg: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw FileSystemSetupException(msg); + } +} + +void umount(const std::string& path, const int flags) +{ + if (-1 == ::umount2(path.c_str(), flags)) { + const std::string msg = "umount() failed: '" + path + "': " + utils::getSystemErrorMessage(); + LOGD(msg); + throw FileSystemSetupException(msg); + } +} + +bool isMountPoint(const std::string& path) +{ + std::string parentPath = utils::dirName(path); + + struct stat s1, s2; + if (-1 == ::stat(path.c_str(), &s1)) { + const std::string msg = "stat() failed: " + path + ": " + utils::getSystemErrorMessage(); + LOGE(msg); + throw FileSystemSetupException(msg); + } + + if (-1 == ::stat(parentPath.c_str(), &s2)) { + const std::string msg = "stat() failed: " + parentPath + ": " + utils::getSystemErrorMessage(); + LOGE(msg); + throw FileSystemSetupException(msg); + } + + return s1.st_dev != s2.st_dev; +} + +bool isMountPointShared(const std::string& path) +{ + std::ifstream fileStream("/proc/self/mountinfo"); + if (!fileStream.good()) { + const std::string msg = "Failed to open /proc/self/mountinfo"; + LOGE(msg); + throw FileSystemSetupException(msg); + } + + // Find the line corresponding to the path + std::string line; + while (std::getline(fileStream, line).good()) { + std::istringstream iss(line); + auto it = std::istream_iterator(iss); + std::advance(it, 4); + + if (it->compare(path)) { + // Wrong line, different path + continue; + } + + // Right line, check if mount point shared + std::advance(it, 2); + return it->find("shared:") != std::string::npos; + } + + // Path not found + return false; +} + +} // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/filesystem.hpp b/libs/lxcpp/filesystem.hpp new file mode 100644 index 0000000..6833ffc --- /dev/null +++ b/libs/lxcpp/filesystem.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * @author Jan Olszak (j.olszak@samsung.com) + * @brief file system handling routines + */ + +#ifndef LXCPP_FILESYSTEM_HPP +#define LXCPP_FILESYSTEM_HPP + +#include + +namespace lxcpp { + +void mount(const std::string& source, + const std::string& target, + const std::string& filesystemtype, + unsigned long mountflags, + const std::string& data); + +void umount(const std::string& path, const int flags); + +bool isMountPoint(const std::string& path); + +/** + * Detect whether path is mounted as MS_SHARED. + * Parses /proc/self/mountinfo + * + * @param path mount point + * @return is the mount point shared + */ +bool isMountPointShared(const std::string& path); + +} // namespace lxcpp + +#endif // LXCPP_FILESYSTEM_HPP \ No newline at end of file diff --git a/libs/lxcpp/process.cpp b/libs/lxcpp/process.cpp index b707353..96475d5 100644 --- a/libs/lxcpp/process.cpp +++ b/libs/lxcpp/process.cpp @@ -154,4 +154,12 @@ int waitpid(const pid_t pid) throw ProcessSetupException(msg); } +void unshare(const Namespace ns) +{ + if(-1 == ::unshare(toFlag(ns))) { + const std::string msg = "unshare() failed: " + utils::getSystemErrorMessage(); + LOGE(msg); + throw ProcessSetupException(msg); + } +} } // namespace lxcpp \ No newline at end of file diff --git a/libs/lxcpp/process.hpp b/libs/lxcpp/process.hpp index a640e1a..869e3d6 100644 --- a/libs/lxcpp/process.hpp +++ b/libs/lxcpp/process.hpp @@ -47,6 +47,8 @@ void setns(const pid_t pid, int waitpid(const pid_t pid); +void unshare(const Namespace ns); + } // namespace lxcpp #endif // LXCPP_PROCESS_HPP \ No newline at end of file -- 2.7.4