From: Sangyoon Jang Date: Fri, 9 Nov 2018 09:10:51 +0000 (+0900) Subject: Add capmgr-groupctl X-Git-Tag: submit/tizen/20190208.015210~10 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2daf38facf36496ab8b39856007cfae5676e1b64;p=platform%2Fcore%2Fappfw%2Fcapmgr.git Add capmgr-groupctl This can be used for creating group, inviting device, etc. Change-Id: If31973c7b575d3370c448e2f8b1b145a43475cd0 Signed-off-by: Sangyoon Jang --- diff --git a/packaging/capmgr.spec b/packaging/capmgr.spec index 1807d21..b13960a 100644 --- a/packaging/capmgr.spec +++ b/packaging/capmgr.spec @@ -76,6 +76,7 @@ systemctl daemon-reload %license LICENSE %defattr(-,root,root,-) %{_bindir}/capmgr +%{_bindir}/capmgr-groupctl %{_libdir}/libcapmgr-common.so* %{_sysconfdir}/dbus-1/system.d/%{name}.conf %{_unitdir}/%{name}.service diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1d008fa..6e8c416 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ ADD_SUBDIRECTORY(capmgr) ADD_SUBDIRECTORY(common) +ADD_SUBDIRECTORY(groupctl) ADD_SUBDIRECTORY(unit_tests) diff --git a/src/groupctl/CMakeLists.txt b/src/groupctl/CMakeLists.txt new file mode 100644 index 0000000..d1e66bf --- /dev/null +++ b/src/groupctl/CMakeLists.txt @@ -0,0 +1,14 @@ +SET(CAPMGR_GROUPCTL "capmgr-groupctl") + +AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} GROUPCTL_SRCS) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../) + +ADD_EXECUTABLE(${CAPMGR_GROUPCTL} ${GROUPCTL_SRCS}) + +APPLY_PKG_CONFIG(${CAPMGR_GROUPCTL} PUBLIC + GIO_DEPS + GLIB_DEPS + MDG_DEPS +) + +INSTALL(TARGETS ${CAPMGR_GROUPCTL} DESTINATION bin) diff --git a/src/groupctl/capmgr_groupmgr.cc b/src/groupctl/capmgr_groupmgr.cc new file mode 100644 index 0000000..3f9033c --- /dev/null +++ b/src/groupctl/capmgr_groupmgr.cc @@ -0,0 +1,375 @@ +// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by a apache 2.0 license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "groupctl/menu_descriptor.h" +#include "groupctl/menu_item.h" + +namespace { + +const int kRequestTimeout = 3; +const char kGroupName[] = "capmgrgroup"; +const char kPIN[] = "12341234"; + +const std::map kMDGErrorString = { + {MDG_ERROR_NONE, "MDG_ERROR_NONE"}, + {MDG_ERROR_IO_ERROR, "MDG_ERROR_IO_ERROR"}, + {MDG_ERROR_INVALID_PARAMETER, "MDG_ERROR_INVALID_PARAMETER"}, + {MDG_ERROR_OUT_OF_MEMORY, "MDG_ERROR_OUT_OF_MEMORY"}, + {MDG_ERROR_PERMISSION_DENIED, "MDG_ERROR_PERMISSION_DENIED"}, + {MDG_ERROR_NOT_SUPPORTED, "MDG_ERROR_NOT_SUPPORTED"}, + {MDG_ERROR_OPERATION_FAILED, "MDG_ERROR_OPERATION_FAILED"}, + {MDG_ERROR_NO_DATA, "MDG_ERROR_NO_DATA"}, + {MDG_ERROR_ALREADY_REGISTERED, "MDG_ERROR_ALREADY_REGISTERED"}, + {MDG_ERROR_IN_PROGRESS, "MDG_ERROR_IN_PROGRESS"}, + {MDG_ERROR_COMM_ERROR, "MDG_ERROR_COMM_ERROR"}, + {MDG_ERROR_NOT_STARTED, "MDG_ERROR_NOT_STARTED"}, + {MDG_ERROR_DB, "MDG_ERROR_DB"}, + {MDG_ERROR_NOT_PROPER_GROUP, "MDG_ERROR_NOT_PROPER_GROUP"}, + {MDG_ERROR_NOT_PROPER_DEVICE, "MDG_ERROR_NOT_PROPER_DEVICE"}, +}; + +std::string MDGErrorToString(int error) { + auto it = kMDGErrorString.find(error); + if (it == kMDGErrorString.end()) + return "MDG_ERROR_UNKNOWN"; + else + return it->second; +} + +struct MDGContext { + mdg_h mdg_handle; + mdg_group_h group_handle; + std::vector device_list; +}; + +struct MDGContext mdg_ctx; + +void CleanMDGDeviceList() { + std::for_each(mdg_ctx.device_list.begin(), mdg_ctx.device_list.end(), + [](mdg_device_h device) { mdg_device_info_destroy(device); }); + mdg_ctx.device_list.clear(); +} + +bool GroupFoundCb(mdg_group_type_e type, mdg_group_h group, void* user_data) { + char* group_name; + int ret = mdg_group_info_get_name(group, &group_name); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to get group name: " << MDGErrorToString(ret) + << std::endl; + return false; + } + + ret = strcmp(group_name, kGroupName); + free(group_name); + + // find local capmgrgroup only + if (ret == 0 && type == MDG_GROUP_TYPE_LOCAL) { + ret = mdg_group_info_clone(&mdg_ctx.group_handle, group); + if (ret != MDG_ERROR_NONE) + std::cout << "Failed to clone group info: " << MDGErrorToString(ret) + << std::endl; + } + + return true; +} + +void GroupFinishCb(int result, void* user_data) { + std::cout << "Find group finished: " << MDGErrorToString(result) << std::endl; +} + +bool DeviceFoundCb(mdg_device_h device, void* user_data) { + char* device_id; + bool is_invited; + char* model_name; + + int ret = mdg_device_info_get_device_id(device, &device_id); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to get device id: " << MDGErrorToString(ret); + return true; + } + + ret = mdg_device_info_is_invited(device, &is_invited); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to get is_invited: " << MDGErrorToString(ret) + << std::endl; + free(device_id); + return true; + } + + ret = mdg_device_info_get_model_name(device, &model_name); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to get model_name: " << MDGErrorToString(ret) + << std::endl; + free(device_id); + return true; + } + + std::cout << "device_id: " << device_id + << ", is_invited: " << is_invited + << ", model_name: " << model_name + << std::endl; + + free(model_name); + free(device_id); + + // TODO(jeremy.jang): Do not add device which doesn't have capmgr group + mdg_device_h clone; + ret = mdg_device_info_clone(&clone, device); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to clone device: " << MDGErrorToString(ret) + << std::endl; + } + + mdg_ctx.device_list.emplace_back(clone); + + return true; +} + +void DeviceFinishCb(int result, void* user_data) { + std::cout << "Find device finished: " << MDGErrorToString(result) + << std::endl; +} + +void GroupInviteFinishCb(int result, mdg_device_h invited_device, + void* user_data) { + std::cout << "Invite device finished: " << MDGErrorToString(result) + << std::endl; +} + +} // namespace + +namespace capmgr { + +class InviteDeviceMenuItem : public MenuItem { + public: + explicit InviteDeviceMenuItem(MenuDescriptor* descriptor); + + void SelectDeviceId(); + void Invite(); + private: + int idx_; +}; + +InviteDeviceMenuItem::InviteDeviceMenuItem(MenuDescriptor* descriptor) + : MenuItem(descriptor), idx_(0) { + items_ = { + {"Select Device Id", [=] { this->SelectDeviceId(); }}, + {"Invite", [=] { this->Invite(); }}, + }; +} + +void InviteDeviceMenuItem::SelectDeviceId() { + int i = 1; + + for (const auto& device : mdg_ctx.device_list) { + char* device_id; + int ret = mdg_device_info_get_device_id(device, &device_id); + if (ret != MDG_ERROR_NONE) + continue; + std::cout << i++ << " - " << device_id << std::endl; + free(device_id); + } + + // TODO(jeremy.jang): revise this + char input[8]; + std::cout << "input idx > "; + std::cin.getline(input, sizeof(input)); + if (std::cin.fail()) + std::cin.clear(); + + try { + i = std::stoi(input); + if (i < 1 || i > static_cast(mdg_ctx.device_list.size())) { + std::cout << "invalid number" << std::endl; + return; + } + idx_ = i - 1; + } catch (const std::invalid_argument& e) { + // wrong input + } +} + +void InviteDeviceMenuItem::Invite() { + if (mdg_ctx.device_list.size() == 0) { + std::cout << "Please search device first!" << std::endl; + return; + } + + if (!mdg_ctx.group_handle) { + std::cout << "Please search group first!" << std::endl; + return; + } + + mdg_device_h device = mdg_ctx.device_list.at(idx_); + char* device_id; + int ret = mdg_device_info_get_device_id(device, &device_id); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to get device id: " << MDGErrorToString(ret) + << std::endl; + } + + std::cout << "Invite device " << device_id << std::endl; + free(device_id); + + ret = mdg_group_invite_device(mdg_ctx.mdg_handle, mdg_ctx.group_handle, + device, const_cast(kPIN), &GroupInviteFinishCb, nullptr); + std::cout << "mdg_group_invite_device() returns: " << MDGErrorToString(ret) + << std::endl; +} + +class MainMenuItem : public MenuItem { + public: + explicit MainMenuItem(MenuDescriptor* descriptor); + + void CreateGroup(); + void SearchGroup(); + void FindDevices(); + void InviteDevice(); +}; + +MainMenuItem::MainMenuItem(MenuDescriptor* descriptor) : MenuItem(descriptor) { + items_ = { + {"Create capmgr Group", [=] { this->CreateGroup(); }}, + {"Search Groups", [=] { this->SearchGroup(); }}, + {"Find Devices", [=] { this->FindDevices(); }}, + {"Invite Device", [=] { this->InviteDevice(); }}, + }; +} + +void MainMenuItem::CreateGroup() { + int ret = mdg_group_create(mdg_ctx.mdg_handle, + const_cast(kGroupName)); + std::cout << "mdg_group_create() returns: " << MDGErrorToString(ret) + << std::endl; +} + +void MainMenuItem::SearchGroup() { + if (mdg_ctx.group_handle) { + std::cout << "Local capmgrgroup already found" << std::endl; + return; + } + + int ret = mdg_group_find(mdg_ctx.mdg_handle, kRequestTimeout, + &GroupFoundCb, &GroupFinishCb, this); + std::cout << "mdg_group_find() returns: " << MDGErrorToString(ret) + << std::endl; +} + +void MainMenuItem::FindDevices() { + int ret = mdg_device_find(mdg_ctx.mdg_handle, kRequestTimeout, false, + &DeviceFoundCb, &DeviceFinishCb, nullptr); + std::cout << "mdg_device_find() returns: " << MDGErrorToString(ret) + << std::endl; +} + +void MainMenuItem::InviteDevice() { + descriptor_->PushMenu( + std::shared_ptr(new InviteDeviceMenuItem(descriptor_))); +} + +class InitMenuItem : public MenuItem { + public: + explicit InitMenuItem(MenuDescriptor* descriptor); + ~InitMenuItem(); + + void Init(); +}; + +InitMenuItem::InitMenuItem(MenuDescriptor* descriptor) : MenuItem(descriptor) { + items_ = { + {"Init", [=] { this->Init(); }}, + }; +} + +InitMenuItem::~InitMenuItem() { + int ret; + + if (mdg_ctx.mdg_handle) { + ret = mdg_deinitialize(mdg_ctx.mdg_handle); + if (ret != MDG_ERROR_NONE) + std::cout << "Failed to deinitialize mdg: " << MDGErrorToString(ret) + << std::endl; + } + + if (mdg_ctx.group_handle) + mdg_group_info_destroy(mdg_ctx.group_handle); + + CleanMDGDeviceList(); +} + +void InitMenuItem::Init() { + if (!mdg_ctx.mdg_handle) { + int ret = mdg_initialize(&mdg_ctx.mdg_handle); + if (ret != MDG_ERROR_NONE) { + std::cout << "Failed to initialize mdg: " << MDGErrorToString(ret); + return; + } + } else { + std::cout << "Already initialized" << std::endl; + } + + descriptor_->PushMenu( + std::shared_ptr(new MainMenuItem(descriptor_))); +} + +} // namespace capmgr + +gboolean KeyboardInputCb(GIOChannel* src, GIOCondition cond, gpointer data) { + capmgr::MenuDescriptor* descriptor = + reinterpret_cast(data); + + char buf[1024]; + if (!fgets(buf, sizeof(buf), stdin)) + return FALSE; + // remove '\r' + buf[strlen(buf) - 1] = '\0'; + descriptor->Eval(buf); + descriptor->PrintPrompt(); + + return TRUE; +} + +int main(int argc, char* argv[]) { + capmgr::MenuDescriptor descriptor; + std::shared_ptr mainmenu = + std::shared_ptr(new capmgr::InitMenuItem(&descriptor)); + descriptor.PushMenu(mainmenu); + descriptor.PrintPrompt(); + + GMainLoop* mainloop = g_main_loop_new(nullptr, FALSE); + if (!mainloop) { + std::cout << "Failed to create main loop!" << std::endl; + return 1; + } + + GIOChannel* channel = g_io_channel_unix_new(STDIN_FILENO); + if (!channel) { + std::cout << "Failed to create stdin GIOChannel!" << std::endl; + g_main_loop_unref(mainloop); + return 1; + } + + guint sid = g_io_add_watch(channel, G_IO_IN, KeyboardInputCb, &descriptor); + + g_main_loop_run(mainloop); + + g_source_remove(sid); + g_io_channel_unref(channel); + g_main_loop_unref(mainloop); + + return 0; +} diff --git a/src/groupctl/menu_descriptor.cc b/src/groupctl/menu_descriptor.cc new file mode 100644 index 0000000..b44181a --- /dev/null +++ b/src/groupctl/menu_descriptor.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by a apache 2.0 license that can be +// found in the LICENSE file. + +#include "groupctl/menu_descriptor.h" + +#include +#include +#include + +#include +#include + +#include "groupctl/menu_item.h" + +namespace capmgr { + +void MenuDescriptor::PrintMenu() { + // MenuItem's menu + deque_.back()->PrintMenuItems(); + + // reserved menu + std::cout << "m - show menu" << std::endl; + std::cout << "p - previous menu" << std::endl; + std::cout << "q - quit" << std::endl; +} + +void MenuDescriptor::PrintPrompt() { + std::cout << "(" << std::setw(6) << getpid() << ") > "; + std::cout.flush(); +} + +void MenuDescriptor::PushMenu(std::shared_ptr menu) { + deque_.emplace_back(menu); + PrintMenu(); +} + +void MenuDescriptor::PopMenu() { + if (deque_.size() != 1) { + deque_.pop_back(); + PrintMenu(); + } +} + +void MenuDescriptor::Eval(char* buf) { + std::shared_ptr menuitem = deque_.back(); + try { + int idx = std::stoi(buf); + if (idx < 1 || idx > menuitem->NumOfItems()) { + std::cout << "invalid number" << std::endl; + return; + } + menuitem->Run(idx); + } catch (const std::invalid_argument& e) { + // maybe reserved menu + if (!strcmp(buf, "m")) + PrintMenu(); + else if (!strcmp(buf, "p")) + PopMenu(); + else if (!strcmp(buf, "q")) + // release? + exit(0); + } +} + +bool MenuDescriptor::Empty() { + return deque_.empty(); +} + +} // namespace capmgr diff --git a/src/groupctl/menu_descriptor.h b/src/groupctl/menu_descriptor.h new file mode 100644 index 0000000..891f330 --- /dev/null +++ b/src/groupctl/menu_descriptor.h @@ -0,0 +1,31 @@ +// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by a apache 2.0 license that can be +// found in the LICENSE file. + +#ifndef GROUPCTL_MENU_DESCRIPTOR_H_ +#define GROUPCTL_MENU_DESCRIPTOR_H_ + +#include +#include + +namespace capmgr { + +// forward declaration +class MenuItem; + +class MenuDescriptor { + public: + void PrintMenu(); + void PrintPrompt(); + void PushMenu(std::shared_ptr menu); + void PopMenu(); + void Eval(char* buf); + bool Empty(); + + private: + std::deque> deque_; +}; + +} // namespace capmgr + +#endif // GROUPCTL_MENU_DESCRIPTOR_H_ diff --git a/src/groupctl/menu_item.h b/src/groupctl/menu_item.h new file mode 100644 index 0000000..6e7ae94 --- /dev/null +++ b/src/groupctl/menu_item.h @@ -0,0 +1,45 @@ +// Copyright (c) 2018 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by a apache 2.0 license that can be +// found in the LICENSE file. + +#ifndef GROUPCTL_MENU_ITEM_H_ +#define GROUPCTL_MENU_ITEM_H_ + +#include +#include +#include +#include +#include + +namespace capmgr { + +// forward declaration +class MenuDescriptor; + +class MenuItem { + public: + explicit MenuItem(MenuDescriptor* descriptor) : descriptor_(descriptor) {} + virtual ~MenuItem() = default; + void PrintMenuItems() { + int idx = 1; + for (const auto& item : items_) + std::cout << idx++ << " - " << item.first << std::endl; + } + + void Run(int idx) { + const auto& item = items_.at(idx - 1); + item.second(); + } + + int NumOfItems() { + return items_.size(); + } + + protected: + MenuDescriptor* descriptor_; + std::vector>> items_; +}; + +} // namespace capmgr + +#endif // GROUPCTL_MENU_ITEM_H_