Implement internal APIs for rpc-port 20/162120/14
authorJunghoon Park <jh9216.park@samsung.com>
Wed, 29 Nov 2017 07:20:34 +0000 (16:20 +0900)
committerJunghoon Park <jh9216.park@samsung.com>
Mon, 4 Dec 2017 03:03:50 +0000 (12:03 +0900)
Change-Id: I1cd72acc909afb06ecb908622161b1252e6933f9
Signed-off-by: Junghoon Park <jh9216.park@samsung.com>
src/fdbroker-internal.cc [new file with mode: 0644]
src/fdbroker-internal.h [new file with mode: 0644]
src/parcel-internal.h
src/port-internal.cc [new file with mode: 0644]
src/port-internal.h [new file with mode: 0644]
src/proxy-internal.cc [new file with mode: 0644]
src/proxy-internal.h [new file with mode: 0644]
src/stub-internal.cc [new file with mode: 0644]
src/stub-internal.h [new file with mode: 0644]

diff --git a/src/fdbroker-internal.cc b/src/fdbroker-internal.cc
new file mode 100644 (file)
index 0000000..8fce446
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <aul.h>
+#include <dlog.h>
+
+#include "fdbroker-internal.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "RPC_PORT"
+
+
+#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
+#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
+#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
+#define RPC_PORT_OBJECT_PATH "/org/tizen/rpcport"
+#define RPC_PORT_INTERFACE_PREFIX "org.tizen.rpcport._"
+
+namespace rpc_port {
+namespace internal {
+
+FdBroker::DBusConnectionManager& FdBroker::DBusConnectionManager::GetInst() {
+  static DBusConnectionManager dm;
+
+  if (dm.disposed_) {
+    dm.Init();
+  }
+
+  return dm;
+}
+
+void FdBroker::DBusConnectionManager::Dispose() {
+  if (!disposed_)
+    Fini();
+}
+
+GDBusConnection* FdBroker::DBusConnectionManager::GetConnection() {
+  return gdbus_conn_;
+}
+
+FdBroker::DBusConnectionManager::~DBusConnectionManager() {
+  if (!disposed_)
+    Fini();
+}
+
+void FdBroker::DBusConnectionManager::Init() {
+  GError* error = nullptr;
+
+  gdbus_conn_ = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
+  if (gdbus_conn_ == nullptr) {
+    if (error != nullptr) {
+      LOGE("Failed to get dbus [%s]", error->message);
+      g_error_free(error);
+    }
+
+    return;
+  }
+
+  disposed_ = false;
+}
+
+void FdBroker::DBusConnectionManager::Fini() {
+  if (gdbus_conn_ != nullptr) {
+    g_object_unref(gdbus_conn_);
+    gdbus_conn_ = nullptr;
+  }
+
+  disposed_ = true;
+}
+
+FdBroker::SocketPair::SocketPair() {
+  socks_[SENDER] = 0;
+  socks_[RECEIVER] = 0;
+}
+
+FdBroker::SocketPair::~SocketPair() {
+  if (socks_[SENDER] > 0)
+    close(socks_[SENDER]);
+  if (socks_[RECEIVER] > 0)
+    close(socks_[RECEIVER]);
+}
+
+int FdBroker::SocketPair::Request() {
+  if (aul_request_message_port_socket_pair(socks_) != AUL_R_OK) {
+    LOGE("error create socket pair");
+    return -1;
+  }
+
+  return 0;
+}
+
+int FdBroker::SocketPair::Get(Type t) const {
+  return socks_[t];
+}
+
+int FdBroker::SocketPair::Detach(Type t) {
+  int fd = socks_[t];
+
+  socks_[t] = 0;
+  return fd;
+}
+
+FdBroker::FdList::FdList() {
+  fd_list_ = g_unix_fd_list_new();
+}
+
+FdBroker::FdList::~FdList() {
+  g_object_unref(fd_list_);
+}
+
+int FdBroker::FdList::Add(int fd) {
+  GError *err = nullptr;
+
+  g_unix_fd_list_append(fd_list_, fd, &err);
+  close(fd);
+
+  if (err != NULL) {
+    LOGE("g_unix_fd_list_append [%s]", err->message);
+    g_error_free(err);
+    return -1;
+  }
+
+  return 0;
+}
+
+GUnixFDList* FdBroker::FdList::GetRaw() {
+  return fd_list_;
+}
+
+FdBroker::~FdBroker() {
+  if (registration_id_ > 0) {
+    g_dbus_connection_unregister_object(
+        DBusConnectionManager::GetInst().GetConnection(),
+        registration_id_);
+  }
+}
+
+std::string FdBroker::GetInterfaceName(const std::string& target_appid,
+                             const std::string& port_name) {
+  std::string interface_name = RPC_PORT_INTERFACE_PREFIX;
+
+  interface_name += target_appid;
+  interface_name += "_" + port_name;
+
+  return interface_name;
+}
+
+int FdBroker::Send(const std::string& target_appid,
+                   const std::string& port_name) {
+  std::string interface_name = GetInterfaceName(target_appid, port_name);
+  GDBusMessage *msg;
+  GError *err = nullptr;
+  GVariant *body;
+  SocketPair sock_pair;
+  FdList fd_list;
+  char sender_appid[255];
+
+  if (aul_app_get_appid_bypid(getpid(), sender_appid, sizeof(sender_appid)) < 0)
+    return -1;
+
+  if (sock_pair.Request() != 0)
+    return -1;
+
+  if (fd_list.Add(sock_pair.Detach(SocketPair::RECEIVER)) != 0)
+    return -1;
+
+  msg = g_dbus_message_new_method_call(interface_name.c_str(),
+                                       RPC_PORT_OBJECT_PATH,
+                                       interface_name.c_str(), "send_message");
+  if (!msg) {
+    LOGE("Can't allocate new method call");
+    return -1;
+  }
+
+  body = g_variant_new("(s)", sender_appid);
+  g_dbus_message_set_unix_fd_list(msg, fd_list.GetRaw());
+  g_dbus_message_set_body(msg, body);
+  g_dbus_connection_send_message(DBusConnectionManager::GetInst().GetConnection(),
+                                 msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                 nullptr, &err);
+  if (err != nullptr) {
+    LOGE("No reply. error = %s", err->message);
+    g_error_free(err);
+    g_object_unref(msg);
+    return -1;
+  }
+
+  int fd = sock_pair.Detach(SocketPair::SENDER);
+  g_object_unref(msg);
+
+  return fd;
+}
+
+void FdBroker::ReceiveMessage(GVariant* parameters,
+                              GDBusMethodInvocation* invocation) {
+  char* sender_appid = nullptr;
+  GDBusMessage* msg;
+  GUnixFDList* fd_list;
+  int fd_len;
+  int* returned_fds = nullptr;
+
+  g_variant_get(parameters, "(&s)", &sender_appid);
+
+  if (sender_appid == nullptr) {
+    LOGE("Invalid argument : sender_appid is NULL");
+    return;
+  }
+
+  msg = g_dbus_method_invocation_get_message(invocation);
+  fd_list = g_dbus_message_get_unix_fd_list(msg);
+
+  if (fd_list == nullptr)
+    return;
+
+  returned_fds = g_unix_fd_list_steal_fds(fd_list, &fd_len);
+  if (returned_fds == nullptr) {
+    LOGE("fail to get fds");
+    return;
+  }
+
+  listener_->OnFdReceived(sender_appid, returned_fds[0]);
+  free(returned_fds);
+}
+
+void FdBroker::OnReceiveDbusMethod(GDBusConnection *conn,
+    const gchar *sender, const gchar *object_path,
+    const gchar *iface_name, const gchar *method_name,
+    GVariant *parameters, GDBusMethodInvocation *invocation,
+    gpointer user_data) {
+  FdBroker* broker = static_cast<FdBroker*>(user_data);
+
+  broker->ReceiveMessage(parameters, invocation);
+  g_dbus_method_invocation_return_value(invocation, nullptr);
+}
+
+int FdBroker::GetOwnerId(const std::string& interface_name) {
+  int owner_id = 0;
+  GError *error = NULL;
+
+  GVariant* result = g_dbus_connection_call_sync(
+      DBusConnectionManager::GetInst().GetConnection(),
+      DBUS_SERVICE_DBUS,
+      DBUS_PATH_DBUS,
+      DBUS_INTERFACE_DBUS,
+      "RequestName",
+      g_variant_new("(su)", interface_name.c_str(), G_BUS_NAME_OWNER_FLAGS_NONE),
+      G_VARIANT_TYPE("(u)"),
+      G_DBUS_CALL_FLAGS_NONE,
+      -1,
+      nullptr,
+      &error);
+
+  if (error) {
+    LOGE("RequestName fail : %s", error->message);
+    g_error_free(error);
+    return -1;
+  }
+
+  if (result == nullptr) {
+    LOGE("fail to get name NULL");
+    return -1;
+  }
+
+  g_variant_get(result, "(u)", &owner_id);
+  if (owner_id == 0) {
+    LOGE("Acquiring the own name is failed");
+    g_variant_unref(result);
+    return -1;
+  }
+
+  LOGD("Acquiring the own name : %d", owner_id);
+  g_variant_unref(result);
+
+  return owner_id;
+}
+
+int FdBroker::RegisterDbusInterface(const std::string& port_name) {
+  static const GDBusInterfaceVTable interface_vtable = {
+    OnReceiveDbusMethod,
+    nullptr,
+    nullptr
+  };
+  static const char introspection_prefix[] =
+    "<node>"
+    "  <interface name='";
+  static const char introspection_postfix[] =
+    "'>"
+    "  <method name='send_message'>"
+    "    <arg type='s' name='sender_appid' direction='in'/>"
+    "  </method>"
+    "  </interface>"
+    "</node>";
+  char appid[255];
+
+  if (aul_app_get_appid_bypid(getpid(), appid, sizeof(appid)) < 0)
+    return -1;
+
+  std::string interface_name = GetInterfaceName(appid, port_name);
+
+  if (GetOwnerId(interface_name) < 0) {
+    LOGE("Failed to get owner id");
+    return -1;
+  }
+
+  std::string introspection_xml = introspection_prefix +
+                                  interface_name +
+                                  introspection_postfix;
+
+  GDBusNodeInfo* introspection_data = g_dbus_node_info_new_for_xml(
+      introspection_xml.c_str(), nullptr);
+  if (!introspection_data) {
+    LOGE("g_dbus_node_info_new_for_xml() is failed.");
+    return -1;
+  }
+
+  registration_id_ = g_dbus_connection_register_object(
+      DBusConnectionManager::GetInst().GetConnection(),
+      RPC_PORT_OBJECT_PATH, introspection_data->interfaces[0],
+      &interface_vtable, this, nullptr, nullptr);
+
+  g_dbus_node_info_unref(introspection_data);
+  if (registration_id_ == 0) {
+    LOGE("Failed to g_dbus_connection_register_object");
+    return -1;
+  }
+
+  return 0;
+}
+
+int FdBroker::Listen(IEventListener* ev, const std::string& port_name) {
+  if (listener_ != nullptr) {
+    LOGE("listener_ is not NULL");
+    return -1;
+  }
+
+  if (ev == nullptr) {
+    LOGE("ev is NULL");
+    return -1;
+  }
+
+  int ret = RegisterDbusInterface(port_name);
+
+  if (ret != 0) {
+    LOGE("Failed to register dbus interface");
+    return -1;
+  }
+
+  listener_ = ev;
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace rpc_port
\ No newline at end of file
diff --git a/src/fdbroker-internal.h b/src/fdbroker-internal.h
new file mode 100644 (file)
index 0000000..9d2cb85
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef FDBROKER_INTERNAL_H_
+#define FDBROKER_INTERNAL_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <glib-unix.h>
+
+#include <string>
+#include <memory>
+
+namespace rpc_port {
+namespace internal {
+
+class FdBroker {
+ public:
+  class IEventListener {
+   public:
+    virtual void OnFdReceived(const std::string& sender, int fd) = 0;
+  };
+
+  FdBroker() = default;
+  ~FdBroker();
+
+  static void Dispose() {
+    DBusConnectionManager::GetInst().Dispose();
+  }
+
+  int Send(const std::string& target_appid, const std::string& port_name);
+  int Listen(IEventListener* ev, const std::string& port_name);
+
+ private:
+  class DBusConnectionManager {
+   public:
+    DBusConnectionManager(const DBusConnectionManager&) = delete;
+    DBusConnectionManager& operator = (const DBusConnectionManager&) = delete;
+
+    static DBusConnectionManager& GetInst();
+    void Dispose();
+    GDBusConnection* GetConnection();
+
+   private:
+    DBusConnectionManager() = default;
+    ~DBusConnectionManager();
+
+    void Init();
+    void Fini();
+
+   private:
+    bool disposed_ = true;
+    GDBusConnection* gdbus_conn_ = nullptr;
+  };
+
+  class SocketPair {
+   public:
+    enum Type {
+      SENDER = 0,
+      RECEIVER = 1
+    };
+
+    SocketPair();
+    ~SocketPair();
+
+    int Request();
+    int Get(Type t) const;
+    int Detach(Type t);
+
+   private:
+    int socks_[2];
+  };
+
+  class FdList {
+   public:
+    FdList();
+    ~FdList();
+
+    int Add(int fd);
+    GUnixFDList* GetRaw();
+
+   private:
+    GUnixFDList* fd_list_;
+  };
+
+ private:
+  static void OnReceiveDbusMethod(GDBusConnection *conn, const gchar *sender,
+                                  const gchar *object_path,
+                                  const gchar *iface_name,
+                                  const gchar *method_name,
+                                  GVariant *parameters,
+                                  GDBusMethodInvocation *invocation,
+                                  gpointer user_data);
+  int GetOwnerId(const std::string& interface_name);
+  int RegisterDbusInterface(const std::string& port_name);
+  void ReceiveMessage(GVariant* parameters, GDBusMethodInvocation* invocation);
+  std::string GetInterfaceName(const std::string& target_appid,
+                               const std::string& port_name);
+
+ private:
+  IEventListener* listener_ = nullptr;
+  int registration_id_ = 0;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // FDBROKER_INTERNAL_H_
index 86f8e52..c729a49 100644 (file)
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef __PARCEL_INTERNAL_H__
-#define __PARCEL_INTERNAL_H__
+#ifndef PARCEL_INTERNAL_H_
+#define PARCEL_INTERNAL_H_
 
 #include <bundle.h>
 
@@ -78,4 +78,4 @@ class Parcel {
 
 }  // namespace rpc_port
 
-#endif  // __PARCEL_INTERNAL_H__
\ No newline at end of file
+#endif  // PARCEL_INTERNAL_H_
\ No newline at end of file
diff --git a/src/port-internal.cc b/src/port-internal.cc
new file mode 100644 (file)
index 0000000..caa354c
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <poll.h>
+#include <unistd.h>
+#include <dlog.h>
+
+#include "port-internal.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "RPC_PORT"
+
+#define MAX_RETRY_CNT 10
+#define SEND_TIMEOUT 500
+
+namespace rpc_port {
+namespace internal {
+
+Port::Port(int fd, const std::string& id)
+    : fd_(fd), id_(id) {}
+
+Port::~Port() {
+  close(fd_);
+}
+
+int Port::Read(void* buf, unsigned int size) {
+  unsigned int left = size;
+  ssize_t nb;
+  int retry_cnt = 0;
+  const struct timespec TRY_SLEEP_TIME = { 0, 500 * 1000 * 1000 };
+  int bytes_read = 0;
+  char* buffer = static_cast<char*>(buf);
+
+  while (left && (retry_cnt < MAX_RETRY_CNT)) {
+    nb = read(fd_, buffer, left);
+    if (nb == 0) {
+      LOGE("read_socket: ...read EOF, socket closed %d: nb %d\n", fd_, nb);
+      return -1;
+    } else if (nb == -1) {
+      if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
+        LOGE("read_socket: %d errno, sleep and retry ...", errno);
+        retry_cnt++;
+        nanosleep(&TRY_SLEEP_TIME, 0);
+        continue;
+      }
+      LOGE("read_socket: ...error fd %d: errno %d\n", fd_, errno);
+      return -1;
+    }
+
+    left -= nb;
+    buffer += nb;
+    bytes_read += nb;
+    retry_cnt = 0;
+  }
+
+  if (left != 0) {
+    LOGE("error fd %d: retry_cnt %d", fd_, retry_cnt);
+    return -1;
+  }
+
+  return 0;
+}
+
+int Port::Write(const void* buf, unsigned int size) {
+  unsigned int left = size;
+  ssize_t nb;
+  int retry_cnt = 0;
+  struct pollfd fds[1];
+  int ret;
+  int bytes_write = 0;
+  const char* buffer = static_cast<const char*>(buf);
+
+  fds[0].fd = fd_;
+  fds[0].events = POLLOUT;
+  fds[0].revents = 0;
+
+  ret = poll(fds, 1, SEND_TIMEOUT);
+  if (ret == 0) {
+    LOGE("write_socket: : fd %d poll timeout", fd_);
+    return -1;
+  }
+
+  while (left && (retry_cnt < MAX_RETRY_CNT)) {
+    nb = write(fd_, buffer, left);
+    if (nb == -1) {
+      if (errno == EINTR) {
+        LOGE("write_socket: EINTR error continue ...");
+        retry_cnt++;
+        continue;
+      }
+
+      LOGE("write_socket: ...error fd %d: errno %d\n", fd_, errno);
+      return -1;
+    }
+
+    left -= nb;
+    buffer += nb;
+    bytes_write += nb;
+    retry_cnt = 0;
+  }
+
+  if (left != 0) {
+    LOGE("error fd %d: retry_cnt %d", fd_, retry_cnt);
+    return -1;
+  }
+
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace rpc_port
\ No newline at end of file
diff --git a/src/port-internal.h b/src/port-internal.h
new file mode 100644 (file)
index 0000000..d612cb7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef PORT_INTERNAL_H_
+#define PORT_INTERNAL_H_
+
+#include <string>
+#include <memory>
+
+namespace rpc_port {
+namespace internal {
+
+class Port {
+ public:
+  Port(int fd, const std::string& id);
+  virtual ~Port();
+
+  int Read(void* buf, unsigned int size);
+  int Write(const void* buf, unsigned int size);
+  int GetFd() const {
+    return fd_;
+  }
+
+  const std::string& GetId() const {
+    return id_;
+  }
+
+ private:
+  int fd_;
+  std::string id_;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // PORT_INTERNAL_H_
diff --git a/src/proxy-internal.cc b/src/proxy-internal.cc
new file mode 100644 (file)
index 0000000..6cabd91
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <dlog.h>
+
+#include "proxy-internal.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "RPC_PORT"
+
+namespace rpc_port {
+namespace internal {
+
+Proxy::~Proxy() {
+  if (src_id_ > 0)
+    g_source_remove(src_id_);
+
+  if (gioc_ != nullptr) {
+    g_io_channel_shutdown(gioc_, TRUE, nullptr);
+    g_io_channel_unref(gioc_);
+  }
+}
+
+gboolean Proxy::OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
+                                     gpointer data) {
+  Proxy* proxy = static_cast<Proxy*>(data);
+
+  proxy->listener_->OnDisconnected(proxy->target_appid_);
+  proxy->src_id_ = 0;
+
+  return FALSE;
+}
+
+int Proxy::Watch(int fd) {
+  char buf[1024];
+
+  gioc_ = g_io_channel_unix_new(fd);
+  if (!gioc_) {
+    LOGE("Error is %s\n", strerror_r(errno, buf, sizeof(buf)));
+    return -1;
+  }
+
+  src_id_ = g_io_add_watch(gioc_,
+                            (GIOCondition)(G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                            OnSocketDisconnected, this);
+  if (src_id_ == 0) {
+    LOGE("fail to add watch on socket");
+    g_io_channel_shutdown(gioc_, TRUE, nullptr);
+    g_io_channel_unref(gioc_);
+    gioc_ = nullptr;
+    return -1;
+  }
+
+  return 0;
+}
+
+void Proxy::Connect(const std::string appid, const std::string& port_name,
+                    IEventListener* ev) {
+  if (ev == nullptr || listener_ != nullptr)
+    return;
+
+  listener_ = ev;
+  target_appid_ = appid;
+  int fd = fd_broker_.Send(appid, port_name);
+
+  if (fd <= 0) {
+    listener_->OnRejected(appid);
+    listener_ = nullptr;
+    return;
+  }
+
+  port_.reset(new Port(fd, port_name));
+  listener_->OnConnected(appid, *port_);
+  Watch(fd);
+}
+
+}  // namespace internal
+}  // namespace rpc_port
\ No newline at end of file
diff --git a/src/proxy-internal.h b/src/proxy-internal.h
new file mode 100644 (file)
index 0000000..caa683e
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef PROXY_INTERNAL_H_
+#define PROXY_INTERNAL_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+#include <string>
+#include <memory>
+
+#include "port-internal.h"
+#include "fdbroker-internal.h"
+
+namespace rpc_port {
+namespace internal {
+
+class Proxy {
+ public:
+  ~Proxy();
+
+  class IEventListener {
+   public:
+    virtual void OnConnected(const std::string& endpoint, Port& port) = 0;
+    virtual void OnDisconnected(const std::string& endpoint) = 0;
+    virtual void OnRejected(const std::string& endpoint) = 0;
+  };
+
+  void Connect(const std::string appid, const std::string& port_name,
+               IEventListener* ev);
+  std::shared_ptr<Port> GetPort() const {
+    return port_;
+  }
+
+ private:
+  static gboolean OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
+                                       gpointer data);
+  int Watch(int fd);
+
+ private:
+  std::shared_ptr<Port> port_;
+  IEventListener* listener_ = nullptr;
+  FdBroker fd_broker_;
+  std::string target_appid_;
+  GIOChannel* gioc_ = nullptr;
+  int src_id_ = 0;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // PROXY_INTERNAL_H_
diff --git a/src/stub-internal.cc b/src/stub-internal.cc
new file mode 100644 (file)
index 0000000..109ae8d
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <dlog.h>
+
+#include "stub-internal.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "RPC_PORT"
+
+namespace rpc_port {
+namespace internal {
+
+Stub::Stub(const std::string& port_name)
+    : port_name_(port_name) {}
+
+Stub::~Stub() {}
+
+void Stub::Listen(IEventListener* ev) {
+  if (ev == nullptr)
+    return;
+
+  listener_ = ev;
+  fd_broker_.Listen(this, port_name_);
+}
+
+gboolean Stub::OnDataReceived(GIOChannel *gio, GIOCondition cond,
+                              gpointer data) {
+  Stub* stub = static_cast<Stub*>(data);
+  int fd = g_io_channel_unix_get_fd(gio);
+
+  for (auto& p : stub->ports_) {
+    if (p->GetFd() == fd) {
+      stub->listener_->OnReceived(p->GetId(), *p);
+      break;
+    }
+  }
+
+  return TRUE;
+}
+
+gboolean Stub::OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
+                                    gpointer data) {
+  Stub* stub = static_cast<Stub*>(data);
+  int fd = g_io_channel_unix_get_fd(gio);
+
+  for (auto& p : stub->ports_) {
+    if (p->GetFd() == fd) {
+      stub->listener_->OnDisconnected(p->GetId());
+      stub->ports_.remove(p);
+      break;
+    }
+  }
+
+  return FALSE;
+}
+
+void Stub::OnFdReceived(const std::string& sender, int fd) {
+  ports_.emplace_back(new AcceptedPort(this, fd, sender));
+  listener_->OnConnected(sender);
+}
+
+Stub::AcceptedPort::AcceptedPort(Stub* parent, int fd, const std::string& id)
+    : Port(fd, id), parent_(parent) {
+  Watch();
+}
+
+Stub::AcceptedPort::~AcceptedPort() {
+  if (disconn_src_ > 0)
+    g_source_remove(disconn_src_);
+
+  if (src_ > 0)
+    g_source_remove(src_);
+
+  if (gioc_ != nullptr) {
+    g_io_channel_shutdown(gioc_, TRUE, nullptr);
+    g_io_channel_unref(gioc_);
+  }
+}
+
+int Stub::AcceptedPort::Watch() {
+  char buf[1024];
+  int fd = GetFd();
+
+  gioc_ = g_io_channel_unix_new(fd);
+  if (!gioc_) {
+    LOGE("Error is %s", strerror_r(errno, buf, sizeof(buf)));
+    return -1;
+  }
+
+  disconn_src_= g_io_add_watch(gioc_,
+                               (GIOCondition)(G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                               Stub::OnSocketDisconnected, parent_);
+  if (disconn_src_ == 0) {
+    LOGE("fail to add watch on socket");
+    g_io_channel_shutdown(gioc_, TRUE, nullptr);
+    g_io_channel_unref(gioc_);
+    gioc_ = nullptr;
+    return -1;
+  }
+
+  src_= g_io_add_watch(gioc_,
+                       (GIOCondition)(G_IO_IN),
+                       Stub::OnDataReceived, parent_);
+  if (src_ == 0) {
+    LOGE("fail to add watch on socket");
+    g_source_remove(disconn_src_);
+    disconn_src_ = 0;
+    g_io_channel_shutdown(gioc_, TRUE, nullptr);
+    g_io_channel_unref(gioc_);
+    gioc_ = nullptr;
+    return -1;
+  }
+
+  return 0;
+}
+
+}  // namespace internal
+}  // namespace rpc_port
\ No newline at end of file
diff --git a/src/stub-internal.h b/src/stub-internal.h
new file mode 100644 (file)
index 0000000..8c474ac
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef STUB_INTERNAL_H_
+#define STUB_INTERNAL_H_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+#include <string>
+#include <list>
+#include <memory>
+
+#include "port-internal.h"
+#include "fdbroker-internal.h"
+
+namespace rpc_port {
+namespace internal {
+
+class Stub : private FdBroker::IEventListener {
+ public:
+  class IEventListener {
+   public:
+    virtual void OnConnected(const std::string& sender) = 0;
+    virtual void OnDisconnected(const std::string& sender) = 0;
+    virtual void OnReceived(const std::string& sender, Port& port) = 0;
+  };
+
+  Stub(const std::string& port_name);
+  ~Stub();
+
+  void Listen(IEventListener* ev);
+
+ private:
+  class AcceptedPort : public Port {
+   public:
+    AcceptedPort(Stub* parent, int fd, const std::string& id);
+    virtual ~AcceptedPort();
+
+   private:
+    int Watch();
+
+   private:
+    GIOChannel* gioc_ = nullptr;
+    int disconn_src_ = 0;
+    int src_ = 0;
+    Stub* parent_;
+  };
+
+  static gboolean OnDataReceived(GIOChannel *gio, GIOCondition cond,
+                                 gpointer data);
+  static gboolean OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
+                                       gpointer data);
+
+  void OnFdReceived(const std::string& sender, int fd) override;
+
+ private:
+  std::list<std::shared_ptr<Port>> ports_;
+  IEventListener* listener_;
+  FdBroker fd_broker_;
+  std::string port_name_;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // STUB_INTERNAL_H_