Add capmgr-groupctl
authorSangyoon Jang <jeremy.jang@samsung.com>
Fri, 9 Nov 2018 09:10:51 +0000 (18:10 +0900)
committer장상윤/Tizen Platform Lab(SR)/Engineer/삼성전자 <jeremy.jang@samsung.com>
Wed, 14 Nov 2018 09:13:20 +0000 (18:13 +0900)
This can be used for creating group, inviting device, etc.

Change-Id: If31973c7b575d3370c448e2f8b1b145a43475cd0
Signed-off-by: Sangyoon Jang <jeremy.jang@samsung.com>
packaging/capmgr.spec
src/CMakeLists.txt
src/groupctl/CMakeLists.txt [new file with mode: 0644]
src/groupctl/capmgr_groupmgr.cc [new file with mode: 0644]
src/groupctl/menu_descriptor.cc [new file with mode: 0644]
src/groupctl/menu_descriptor.h [new file with mode: 0644]
src/groupctl/menu_item.h [new file with mode: 0644]

index 1807d21b056b6a2f395a0eaf143919490ce1453d..b13960a7413908aaeb82d9be577f9a58595cdb20 100644 (file)
@@ -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
index 1d008faedd76f10b4ecd567e51ba0f6b37913f3f..6e8c416a50175f2fabf473d9e111f69ba23f7fed 100644 (file)
@@ -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 (file)
index 0000000..d1e66bf
--- /dev/null
@@ -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 (file)
index 0000000..3f9033c
--- /dev/null
@@ -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 <gio/gio.h>
+#include <glib.h>
+#include <mdg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#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<int, std::string> 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<mdg_device_h> 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<int>(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<char*>(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<char*>(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<MenuItem>(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<MenuItem>(new MainMenuItem(descriptor_)));
+}
+
+}  // namespace capmgr
+
+gboolean KeyboardInputCb(GIOChannel* src, GIOCondition cond, gpointer data) {
+  capmgr::MenuDescriptor* descriptor =
+      reinterpret_cast<capmgr::MenuDescriptor*>(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<capmgr::MenuItem> mainmenu =
+      std::shared_ptr<capmgr::MenuItem>(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 (file)
index 0000000..b44181a
--- /dev/null
@@ -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 <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <iostream>
+
+#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<MenuItem> 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> 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 (file)
index 0000000..891f330
--- /dev/null
@@ -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 <deque>
+#include <memory>
+
+namespace capmgr {
+
+// forward declaration
+class MenuItem;
+
+class MenuDescriptor {
+ public:
+  void PrintMenu();
+  void PrintPrompt();
+  void PushMenu(std::shared_ptr<MenuItem> menu);
+  void PopMenu();
+  void Eval(char* buf);
+  bool Empty();
+
+ private:
+  std::deque<std::shared_ptr<MenuItem>> 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 (file)
index 0000000..6e7ae94
--- /dev/null
@@ -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 <functional>
+#include <iostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+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<std::pair<std::string, std::function<void()>>> items_;
+};
+
+}  // namespace capmgr
+
+#endif  // GROUPCTL_MENU_ITEM_H_