INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/rpc-port FILES_MATCHING PATTERN "*.h")
INSTALL(DIRECTORY ${LIBRARY_OUTPUT_PATH}/ DESTINATION ${LIB_INSTALL_DIR} FILES_MATCHING PATTERN "*.so*")
+CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/conf/rpc-port.conf.in ${CMAKE_SOURCE_DIR}/conf/rpc-port.conf @ONLY)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/conf/rpc-port.conf DESTINATION /etc/dbus-1/system.d)
+
IF(NOT DEFINED MINIMUM_BUILD)
ENABLE_TESTING()
SET(RPC_PORT_UNITTESTS rpc-port_unittests)
ADD_SUBDIRECTORY(unit_tests)
ADD_DEPENDENCIES(${RPC_PORT_UNITTESTS} rpc-port)
ENDIF(NOT DEFINED MINIMUM_BUILD)
+
+ADD_SUBDIRECTORY(utils)
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy context="default">
+ <allow own="org.tizen.rpc.port.signal"/>
+ <allow send_type="signal" send_interface="org.tizen.rpc.port.signal" send_member="New"/>
+ <check send_type="signal" send_interface="org.tizen.rpc.port.signal" send_member="Debug" privilege="http://tizen.org/privilege/internal/default/platform"/>
+ </policy>
+ <policy user="app_fw">
+ <allow own="org.tizen.rpc.port.signal"/>
+ <allow send_type="signal" send_interface="org.tizen.rpc.port.signal"/>
+ </policy>
+ <policy user="root">
+ <allow own="org.tizen.rpc.port.signal"/>
+ <allow send_type="signal" send_interface="org.tizen.rpc.port.signal"/>
+ </policy>
+</busconfig>
<request>
<domain name="_"/>
</request>
+ <assign>
+ <filesystem path="/usr/bin/rpc-port-util" label="User::Shell" exec_label="User"/>
+ </assign>
</manifest>
%manifest %{name}.manifest
%attr(0644,root,root) %{_libdir}/lib%{name}.so.*
%license LICENSE.APLv2
+%{_bindir}/rpc-port-util
+%config %{_sysconfdir}/dbus-1/system.d/rpc-port.conf
%files devel
%{_includedir}/rpc-port/*.h
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+
+#include "debug-port-internal.hh"
+#include "log-private.hh"
+#include "parcel-internal.h"
+
+namespace rpc_port {
+namespace internal {
+
+static const char PATH_RPC_PORT_UTIL_SOCK[] =
+ "/run/aul/daemons/.rpc-port-util-sock";
+static const char RPC_PORT_SIGNAL_PATH[] = "/Org/Tizen/RPC/Port/Signal";
+static const char RPC_PORT_SIGNAL_INTERFACE[] = "org.tizen.rpc.port.signal";
+static const char RPC_PORT_SIGNAL_DEBUG[] = "Debug";
+static const char RPC_PORT_SIGNAL_NEW[] = "New";
+
+DebugPort::~DebugPort() {
+ if (!disposed_)
+ Dispose();
+}
+
+void DebugPort::Dispose() {
+ Unwatch();
+ Unsubscribe();
+ JoinThread();
+ disposed_ = true;
+}
+
+bool DebugPort::IsConnected() {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ return connected_;
+}
+
+void DebugPort::AddSession(std::string port_name, std::string destination,
+ int main_port, int delegate_port) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ sessions_.emplace_back(
+ new DebugPort::Session(
+ port_name, destination, main_port, delegate_port));
+}
+
+void DebugPort::RemoveSession(int port) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ auto iter = sessions_.begin();
+ while (iter != sessions_.end()) {
+ if ((*iter)->GetMainPort() == port ||
+ (*iter)->GetDelegatePort() == port) {
+ _W("Remove session. port(%d)", port);
+ iter = sessions_.erase(iter);
+ break;
+ }
+
+ ++iter;
+ }
+}
+
+std::shared_ptr<DebugPort::Session> DebugPort::FindSession(int port) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ for (auto& s : sessions_) {
+ if (s->GetMainPort() == port || s->GetDelegatePort() == port)
+ return s;
+ }
+
+ return {};
+}
+
+int DebugPort::Send(int port, bool is_read, uint32_t seq,
+ const void* buf, unsigned int size) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ if (!IsConnected())
+ return -1;
+
+ auto session = FindSession(port);
+ if (session.get() == nullptr) {
+ _E("Failed to find session. port(%d)", port);
+ return -1;
+ }
+
+ // time + port_name + destination + is_delegate + port + is_read + seq + size + data
+ Parcel parcel;
+ parcel.WriteInt64(time(nullptr));
+ parcel.WriteString(session->GetPortName().c_str());
+ parcel.WriteString(session->GetDestination().c_str());
+ parcel.WriteBool(session->GetDelegatePort() == port);
+ parcel.WriteInt32(port);
+ parcel.WriteBool(is_read);
+ parcel.WriteInt32(seq);
+ parcel.WriteInt32(size);
+ parcel.Write(static_cast<const unsigned char*>(buf), size);
+
+ queue_.Push(std::make_shared<Parcel>(parcel));
+ return 0;
+}
+
+void DebugPort::Init() {
+ Subscribe();
+ EmitSignal(RPC_PORT_SIGNAL_NEW);
+ is_running_ = false;
+ disposed_ = false;
+}
+
+int DebugPort::Connect() {
+ int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ _E("socket() is failed. errno(%d)", errno);
+ return -1;
+ }
+
+ struct sockaddr_un addr = { 0, };
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", PATH_RPC_PORT_UTIL_SOCK);
+
+ int ret = connect(fd, reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr));
+ if (ret < 0) {
+ _E("connect() is failed. fd(%d), errno(%d)", fd, errno);
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int DebugPort::Watch(int fd) {
+ GIOChannel* io = g_io_channel_unix_new(fd);
+ if (io == nullptr) {
+ _E("g_io_channel_unix_new() is failed");
+ return -1;
+ }
+
+ GIOCondition cond = static_cast<GIOCondition>(
+ (G_IO_ERR | G_IO_HUP | G_IO_NVAL));
+ guint tag = g_io_add_watch(io, cond, OnDebugPortDisconnectedCb, this);
+ if (tag == 0) {
+ _E("g_io_add_watch() is failed");
+ g_io_channel_unref(io);
+ return -1;
+ }
+
+ io_ = io;
+ watch_tag_ = tag;
+ return 0;
+}
+
+void DebugPort::Unwatch() {
+ if (watch_tag_) {
+ g_source_remove(watch_tag_);
+ watch_tag_ = 0;
+ }
+
+ if (io_) {
+ g_io_channel_unref(io_);
+ io_ = nullptr;
+ }
+}
+
+gboolean DebugPort::OnDebugPortDisconnectedCb(GIOChannel* io,
+ GIOCondition cond, gpointer data) {
+ _W("cond(%d)", static_cast<int>(cond));
+ auto* debug_port = static_cast<DebugPort*>(data);
+ std::lock_guard<std::recursive_mutex> lock(debug_port->GetMutex());
+ debug_port->SetConnectionStatus(false);
+ debug_port->watch_tag_ = 0;
+ debug_port->Unwatch();
+ debug_port->port_.reset();
+ _W("Disconnected");
+ return G_SOURCE_REMOVE;
+}
+
+GDBusConnection* DebugPort::GetConnection() {
+ if (conn_)
+ return conn_;
+
+ GError* error = nullptr;
+ conn_ = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
+ if (conn_ == nullptr) {
+ _E("g_bus_get_sync() is failed. error(%s)",
+ error ? error->message : "unknown");
+ g_clear_error(&error);
+ return nullptr;
+ }
+
+ return conn_;
+}
+
+void DebugPort::Subscribe() {
+ GDBusConnection* conn = GetConnection();
+ if (conn == nullptr)
+ return;
+
+ subs_tag_ = g_dbus_connection_signal_subscribe(conn,
+ nullptr,
+ RPC_PORT_SIGNAL_INTERFACE,
+ RPC_PORT_SIGNAL_DEBUG,
+ RPC_PORT_SIGNAL_PATH,
+ nullptr,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ OnGDBusSignalCb,
+ this,
+ nullptr);
+ if (subs_tag_ == 0) {
+ _E("g_dbus_connection_signal_subscribe() is failed");
+ return;
+ }
+
+ _D("tag(%u)", subs_tag_);
+}
+
+void DebugPort::Unsubscribe() {
+ if (subs_tag_)
+ g_dbus_connection_signal_unsubscribe(conn_, subs_tag_);
+
+ if (conn_)
+ g_object_unref(conn_);
+}
+
+void DebugPort::EmitSignal(std::string signal) {
+ GDBusConnection* conn = GetConnection();
+ if (conn == nullptr)
+ return;
+
+ GError* error = nullptr;
+ gboolean ret = g_dbus_connection_emit_signal(conn,
+ nullptr,
+ RPC_PORT_SIGNAL_PATH,
+ RPC_PORT_SIGNAL_INTERFACE,
+ signal.c_str(),
+ nullptr,
+ &error);
+ if (ret != TRUE) {
+ _E("g_dbus_connection_emit_signal() is failed. error(%s)",
+ error ? error->message : "unknown");
+ g_clear_error(&error);
+ return;
+ }
+
+ ret = g_dbus_connection_flush_sync(conn, nullptr, &error);
+ if (ret != TRUE) {
+ _E("g_dbus_connection_flush_sync() is failed. error(%s)",
+ error ? error->message : "unknown");
+ g_clear_error(&error);
+ return;
+ }
+
+ _W("EmitSignal(%s)", signal.c_str());
+}
+
+void DebugPort::OnGDBusSignalCb(GDBusConnection *connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ _W("signal_name(%s)", signal_name);
+ std::string signal(signal_name);
+ if (signal != RPC_PORT_SIGNAL_DEBUG)
+ return;
+
+ gchar* port_name = nullptr;
+ gint pid = -1;
+ g_variant_get(parameters, "(&si)", &port_name, &pid);
+ _W("port_name(%s), pid(%d)", port_name, pid);
+
+ if (pid != 0 && pid != getpid()) {
+ _W("Invalid pid(%d)", pid);
+ return;
+ }
+
+ auto* debug_port = static_cast<DebugPort*>(user_data);
+ int fd = debug_port->Connect();
+ if (fd < 0)
+ return;
+
+ std::lock_guard<std::recursive_mutex> lock(debug_port->GetMutex());
+ debug_port->port_.reset(new Port(fd, signal));
+ int ret = debug_port->Watch(fd);
+ if (ret < 0)
+ return;
+
+ debug_port->CreateThread();
+ debug_port->SetConnectionStatus(true);
+ _W("Connected");
+}
+
+void DebugPort::SetConnectionStatus(bool status) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ connected_ = status;
+}
+
+void DebugPort::CreateThread() {
+ if (is_running_)
+ return;
+
+ thread_ = std::thread([&]() {
+ _W("START");
+ do {
+ std::shared_ptr<Parcel> parcel;
+ queue_.WaitAndPop(parcel);
+ int len = parcel->GetRaw().size();
+ if (len == 0) {
+ _W("Done");
+ break;
+ }
+
+ if (!IsConnected())
+ continue;
+
+ int ret = port_->Write(reinterpret_cast<void*>(&len), sizeof(len));
+ if (ret < 0) {
+ _E("Failed to write size");
+ SetConnectionStatus(false);
+ continue;
+ }
+
+ ret = port_->Write(&*parcel->GetRaw().cbegin(), len);
+ if (ret < 0) {
+ _E("Failed to write data");
+ SetConnectionStatus(false);
+ }
+ } while (true);
+ _W("END");
+ });
+
+ is_running_ = true;
+}
+
+void DebugPort::JoinThread() {
+ if (is_running_)
+ queue_.Push(std::shared_ptr<Parcel>(new Parcel()));
+
+ if (thread_.joinable()) {
+ _W("Join thread");
+ thread_.join();
+ }
+}
+
+} // namespace internal
+} // namespace rpc_port
--- /dev/null
+/*
+ * Copyright (c) 2020 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 DEBUG_PORT_INTERNAL_HH_
+#define DEBUG_PORT_INTERNAL_HH_
+
+#include <gio/gio.h>
+#include <glib.h>
+
+#include <atomic>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include "parcel-internal.h"
+#include "port-internal.h"
+#include "shared-queue-internal.hh"
+
+namespace rpc_port {
+namespace internal {
+
+class DebugPort {
+ private:
+ DebugPort() = default;
+ ~DebugPort();
+
+ public:
+ class Session {
+ public:
+ Session(std::string port_name, std::string destination,
+ int main_port, int delegate_port)
+ : port_name_(std::move(port_name)),
+ destination_(std::move(destination)),
+ main_port_(main_port),
+ delegate_port_(delegate_port) {
+ }
+ virtual ~Session() = default;
+
+ const std::string& GetPortName() {
+ return port_name_;
+ }
+
+ const std::string& GetDestination() {
+ return destination_;
+ }
+
+ int GetMainPort() {
+ return main_port_;
+ }
+
+ int GetDelegatePort() {
+ return delegate_port_;
+ }
+
+ private:
+ std::string port_name_;
+ std::string destination_;
+ int main_port_;
+ int delegate_port_;
+ };
+
+ static DebugPort& GetInst() {
+ static DebugPort inst;
+
+ std::lock_guard<std::recursive_mutex> lock(inst.GetMutex());
+ if (inst.disposed_)
+ inst.Init();
+ return inst;
+ }
+
+ void Dispose();
+ bool IsConnected();
+
+ void AddSession(std::string port_name, std::string destination,
+ int main_port, int delegate_port);
+ void RemoveSession(int port);
+
+ int Send(int port, bool is_read, uint32_t seq,
+ const void* buf, unsigned int size);
+
+ private:
+ std::recursive_mutex& GetMutex() const {
+ return mutex_;
+ }
+
+ void Init();
+ int Connect();
+ int Watch(int fd);
+ void Unwatch();
+ GDBusConnection* GetConnection();
+ void Subscribe();
+ void Unsubscribe();
+ void EmitSignal(std::string signal);
+ void SetConnectionStatus(bool status);
+ void CreateThread();
+ void JoinThread();
+
+ std::shared_ptr<DebugPort::Session> FindSession(int port);
+
+ static gboolean OnDebugPortDisconnectedCb(GIOChannel* io,
+ GIOCondition cond, gpointer data);
+ static void OnGDBusSignalCb(GDBusConnection *connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ private:
+ bool disposed_ = true;
+ bool connected_ = false;
+ GDBusConnection* conn_ = nullptr;
+ guint subs_tag_ = 0;
+ std::unique_ptr<Port> port_;
+ GIOChannel* io_ = nullptr;
+ guint watch_tag_ = 0;
+ std::list<std::shared_ptr<DebugPort::Session>> sessions_;
+ std::thread thread_;
+ std::atomic<bool> is_running_;
+ SharedQueue<std::shared_ptr<Parcel>> queue_;
+ mutable std::recursive_mutex mutex_;
+};
+
+} // namespace internal
+} // namespace rpc_port
+
+#endif // DEBUG_PORT_INTERNAL_HH_
--- /dev/null
+/*
+ * Copyright (c) 2020 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 LOG_PRIVATE_HH_
+#define LOG_PRIVATE_HH_
+
+#include <dlog.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RPC_PORT"
+
+#undef _E
+#define _E LOGE
+
+#undef _W
+#define _W LOGW
+
+#undef _I
+#define _I LOGI
+
+#undef _D
+#define _D LOGD
+
+#endif // LOG_PRIVATE_HH_
namespace internal {
Port::Port(int fd, std::string id)
- : fd_(fd), id_(std::move(id)) {
+ : fd_(fd), id_(std::move(id)), instance_(""), seq_(0) {
char uuid[37];
uuid_t u;
uuid_generate(u);
}
Port::Port(int fd, std::string id, std::string instance)
- : fd_(fd), id_(std::move(id)), instance_(std::move(instance)) {}
+ : fd_(fd), id_(std::move(id)), instance_(std::move(instance)), seq_(0) {}
Port::~Port() {
close(fd_);
#include <mutex>
#include <string>
#include <memory>
+#include <atomic>
namespace rpc_port {
namespace internal {
return instance_;
}
+ uint32_t GetSeq() {
+ return ++seq_;
+ }
+
private:
int fd_;
std::string id_;
std::string instance_;
+ std::atomic<uint32_t> seq_;
mutable std::recursive_mutex mutex_;
};
#include <sys/socket.h>
#include <dlog.h>
+#include "debug-port-internal.hh"
+#include "log-private.hh"
#include "proxy-internal.h"
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-
-#define LOG_TAG "RPC_PORT"
-
#define EILLEGALACCESS 127
namespace rpc_port {
: fd_broker_(mock) {}
Proxy::~Proxy() {
- LOGD("Proxy::~Proxy");
+ _D("Proxy::~Proxy");
if (conn_timer_) {
g_source_remove(conn_timer_); // LCOV_EXCL_LINE
conn_timer_ = 0; // LCOV_EXCL_LINE
IEventListener* listener = proxy->listener_;
int fd = g_io_channel_unix_get_fd(gio);
- LOGW("Socket was disconnected. fd(%d)", fd);
+ _W("Socket was disconnected. fd(%d)", fd);
if (proxy->main_port_.get()->GetFd() == fd) {
proxy->listener_ = nullptr;
proxy->main_port_.reset();
proxy->delegate_port_.reset();
}
+ DebugPort::GetInst().RemoveSession(fd);
return FALSE;
}
if (proxy->delegate_port_.get()->GetFd() == fd) {
if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
- LOGW("Socket was disconnected by stub. fd(%d)", fd);
+ _W("Socket was disconnected by stub. fd(%d)", fd);
IEventListener* listener = proxy->listener_;
proxy->listener_ = nullptr;
proxy->delegate_port_.get()->SetSource(0);
if (listener)
listener->OnDisconnected(proxy->target_appid_);
+ DebugPort::GetInst().RemoveSession(proxy->main_port_->GetFd());
proxy->main_port_.reset();
proxy->delegate_port_.reset();
return FALSE;
void Proxy::OnPortAppeared(const std::string& appid,
const std::string& port_name) {
- LOGD("endpoint(%s), port_name(%s)", appid.c_str(), port_name.c_str());
+ _D("endpoint(%s), port_name(%s)", appid.c_str(), port_name.c_str());
if (conn_timer_) {
g_source_remove(conn_timer_);
conn_timer_ = 0;
// LCOV_EXCL_STOP
}
- LOGW("[__OnPortAppeared__] fds[0]: %d, fds[1]: %d", fds_[0], fds_[1]);
+ _W("[__OnPortAppeared__] fds[0]: %d, fds[1]: %d", fds_[0], fds_[1]);
}
void Proxy::OnPortVanished(const std::string& appid,
const std::string& port_name) {
- LOGW("[__OnPortVanished__] endpoint(%s), port_name(%s)",
+ _W("[__OnPortVanished__] endpoint(%s), port_name(%s)",
appid.c_str(), port_name.c_str());
}
void Proxy::OnPortConnected(const std::string& appid,
const std::string& port_name) {
- LOGW("[__OnPortConnected__] endpoint(%s), port_name(%s)",
+ _W("[__OnPortConnected__] endpoint(%s), port_name(%s)",
appid.c_str(), port_name.c_str());
if (!listener_) {
- LOGW("listener is null"); // LCOV_EXCL_LINE
+ _W("listener is null"); // LCOV_EXCL_LINE
return; // LCOV_EXCL_LINE
}
main_port_.reset(new ProxyPort(this, fds_[0], appid, false));
delegate_port_.reset(new ProxyPort(this, fds_[1], appid));
listener_->OnConnected(appid, main_port_.get());
+ DebugPort::GetInst().AddSession(port_name, appid, fds_[0], fds_[1]);
}
// LCOV_EXCL_START
void Proxy::OnPortDisconnected(const std::string& appid,
const std::string& port_name, bool cancel) {
- LOGW("[__OnPortDisconnected__] endporint(%s), port_name(%s)",
+ _W("[__OnPortDisconnected__] endporint(%s), port_name(%s)",
appid.c_str(), port_name.c_str());
if (cancel) {
}
if (!listener_) {
- LOGW("listener is null");
+ _W("listener is null");
return;
}
IEventListener* listener = listener_;
listener_ = nullptr;
listener->OnDisconnected(appid);
+ DebugPort::GetInst().RemoveSession(fds_[0]);
}
// LCOV_EXCL_STOP
return RPC_PORT_ERROR_INVALID_PARAMETER;
if (listener_ != nullptr) {
- LOGD("Already connected"); // LCOV_EXCL_LINE
+ _D("Already connected"); // LCOV_EXCL_LINE
return RPC_PORT_ERROR_INVALID_PARAMETER; // LCOV_EXCL_LINE
}
return RPC_PORT_ERROR_INVALID_PARAMETER;
if (listener_ != nullptr) {
- LOGW("Already connected");
+ _W("Already connected");
return RPC_PORT_ERROR_INVALID_PARAMETER;
}
main_port_.reset(new ProxyPort(this, fds_[0], target_appid_, false));
delegate_port_.reset(new ProxyPort(this, fds_[1], target_appid_));
listener_->OnConnected(target_appid_, main_port_.get());
+ DebugPort::GetInst().AddSession(port_name, appid, fds_[0], fds_[1]);
return RPC_PORT_ERROR_NONE;
}
gboolean Proxy::DbusNameTimeout(gpointer user_data) {
Proxy* obj = static_cast<Proxy*>(user_data);
- LOGW("[__DbusNameTimeout__] endpoint(%s)", obj->target_appid_.c_str());
+ _W("[__DbusNameTimeout__] endpoint(%s)", obj->target_appid_.c_str());
obj->conn_timer_ = 0;
if (obj->listener_) {
IEventListener* listener = obj->listener_;
gioc_ = g_io_channel_unix_new(fd);
if (!gioc_) {
- LOGE("Error is %s", strerror_r(errno, buf, sizeof(buf))); // LCOV_EXCL_LINE
+ _E("Error is %s", strerror_r(errno, buf, sizeof(buf))); // LCOV_EXCL_LINE
return -1; // LCOV_EXCL_LINE
}
Proxy::OnSocketDisconnected, parent_);
if (disconn_src_ == 0) {
// LCOV_EXCL_START
- LOGE("Failed to add watch on socket");
+ _E("Failed to add watch on socket");
g_io_channel_unref(gioc_);
gioc_ = nullptr;
return -1;
Proxy::OnDataReceived, parent_);
if (src_ == 0) {
// LCOV_EXCL_START
- LOGE("Failed to add watch on socket");
+ _E("Failed to add watch on socket");
g_source_remove(disconn_src_);
disconn_src_ = 0;
g_io_channel_unref(gioc_);
#include <string>
#include <memory>
-#include "port-internal.h"
#include "fdbroker-internal.h"
+#include "port-internal.h"
namespace rpc_port {
namespace internal {
return RPC_PORT_ERROR_INVALID_PARAMETER;
auto port = static_cast<Port*>(h);
+ uint32_t seq = 0;
+ int ret = port->Read(reinterpret_cast<uint32_t*>(&seq), sizeof(seq));
+ if (ret < 0)
+ return ret;
- return port->Read(buf, size);
+ ret = port->Read(buf, size);
+ if (ret < 0)
+ return ret;
+
+ auto& debug_port = DebugPort::GetInst();
+ debug_port.Send(port->GetFd(), true, seq, buf, size);
+ return RPC_PORT_ERROR_NONE;
}
RPC_API int rpc_port_write(rpc_port_h h, const void* buf, unsigned int size) {
return RPC_PORT_ERROR_INVALID_PARAMETER;
auto port = static_cast<Port*>(h);
+ uint32_t seq = port->GetSeq();
+ int ret = port->Write(reinterpret_cast<void*>(&seq), sizeof(seq));
+ if (ret < 0)
+ return ret;
+
+ ret = port->Write(buf, size);
+ if (ret < 0)
+ return ret;
- return port->Write(buf, size);
+ auto& debug_port = DebugPort::GetInst();
+ debug_port.Send(port->GetFd(), false, seq, buf, size);
+ return RPC_PORT_ERROR_NONE;
}
RPC_API int rpc_port_proxy_create(rpc_port_proxy_h* h) {
auto port = static_cast<Port*>(h);
return port->UnsetPrivateSharing();
-}
\ No newline at end of file
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 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 SHARED_QUEUE_INTERNAL_HH_
+#define SHARED_QUEUE_INTERNAL_HH_
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace rpc_port {
+namespace internal {
+
+template <class T>
+class SharedQueue {
+ public:
+ SharedQueue() = default;
+ virtual ~SharedQueue() = default;
+
+ void Push(T item) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ queue_.push(item);
+ cond_var_.notify_one();
+ }
+
+ bool TryAndPop(T& item) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (queue_.empty())
+ return false;
+
+ item = queue_.front();
+ queue_.pop_front();
+
+ return true;
+ }
+
+ void WaitAndPop(T& item) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ while (queue_.empty())
+ cond_var_.wait(lock);
+
+ item = queue_.front();
+ queue_.pop();
+ }
+
+ bool Empty() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return queue_.empty();
+ }
+
+ int Size() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return queue_.size();
+ }
+
+ private:
+ std::queue<T> queue_;
+ mutable std::mutex mutex_;
+ std::condition_variable cond_var_;
+};
+
+} // namespace internal
+} // namespace rpc_port
+
+#endif // SHARED_QUEUE_INTERNAL_HH_
* limitations under the License.
*/
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
#include <sys/types.h>
#include <sys/socket.h>
#include <dlog.h>
#include <aul.h>
#include <aul_rpc_port.h>
+#include "debug-port-internal.hh"
+#include "log-private.hh"
#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, bool mock)
- : fd_broker_(mock), port_name_(port_name) {}
+ : fd_broker_(mock),
+ port_name_(port_name) {
+}
Stub::~Stub() {
- LOGD("Stub::~Stub");
+ _D("Stub::~Stub");
}
int Stub::Listen(IEventListener* ev) {
while (iter != ports_.end()) {
if ((*iter)->GetInstance().compare(instance) == 0) {
LOGI("Close: fd(%d)", (*iter)->GetFd());
+ DebugPort::GetInst().RemoveSession((*iter)->GetFd());
iter = ports_.erase(iter);
} else {
iter++;
for (auto& p : stub->ports_) {
if (p->GetFd() == fd && !p->IsDelegate()) {
if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
- LOGW("Socket was disconnected from proxy. fd(%d)", fd);
+ _W("Socket was disconnected from proxy. fd(%d)", fd);
stub->listener_->OnDisconnected(p->GetId(), p->GetInstance());
stub->RemoveAcceptedPorts(p->GetInstance());
if (aul_rpc_port_notify_rpc_finished() != AUL_R_OK)
- LOGW("Failed to notify rpc finished"); // LCOV_EXCL_LINE
+ _W("Failed to notify rpc finished"); // LCOV_EXCL_LINE
return FALSE;
}
p.get());
if (ret != 0) {
- LOGW("Invalid protocol");
+ _W("Invalid protocol");
stub->listener_->OnDisconnected(p->GetId(), p->GetInstance());
stub->RemoveAcceptedPorts(p->GetInstance());
if (aul_rpc_port_notify_rpc_finished() != AUL_R_OK)
- LOGW("Failed to notify rpc finished"); // LCOV_EXCL_LINE
+ _W("Failed to notify rpc finished"); // LCOV_EXCL_LINE
return FALSE;
}
Stub* stub = static_cast<Stub*>(data);
int fd = g_io_channel_unix_get_fd(gio);
- LOGW("Socket was disconnected. fd(%d)", fd);
+ _W("Socket was disconnected. fd(%d)", fd);
for (auto& p : stub->ports_) {
if (p->GetFd() == fd) {
stub->listener_->OnDisconnected(p->GetId(), p->GetInstance());
if (aul_rpc_port_notify_rpc_finished() != AUL_R_OK)
- LOGW("Failed to notify rpc finished"); // LCOV_EXCL_LINE
+ _W("Failed to notify rpc finished"); // LCOV_EXCL_LINE
stub->RemoveAcceptedPorts(p->GetInstance());
break;
}
}
void Stub::OnFdReceived(const std::string& sender, int fds[2]) {
- LOGW("[__OnFdReceived__] fds[0]: %d, fds[1]: %d", fds[0], fds[1]);
+ _W("[__OnFdReceived__] fds[0]: %d, fds[1]: %d", fds[0], fds[1]);
auto* main_port = new AcceptedPort(this, false, fds[0], sender, true);
ports_.emplace_back(main_port);
ports_.emplace_back(new AcceptedPort(this, true, fds[1], sender,
break;
}
}
+ DebugPort::GetInst().AddSession(port_name_, sender, fds[0], fds[1]);
}
Stub::AcceptedPort::AcceptedPort(Stub* parent, bool isDelegate, int fd,
gioc_ = g_io_channel_unix_new(fd);
if (!gioc_) {
// LCOV_EXCL_START
- LOGE("Error is %s", strerror_r(errno, buf, sizeof(buf)));
+ _E("Error is %s", strerror_r(errno, buf, sizeof(buf)));
return -1;
// LCOV_EXCL_STOP
}
Stub::OnSocketDisconnected, parent_);
if (disconn_src_ == 0) {
// LCOV_EXCL_START
- LOGE("fail to add watch on socket");
+ _E("fail to add watch on socket");
g_io_channel_unref(gioc_);
gioc_ = nullptr;
return -1;
Stub::OnDataReceived, parent_);
if (src_ == 0) {
// LCOV_EXCL_START
- LOGE("fail to add watch on socket");
+ _E("fail to add watch on socket");
g_source_remove(disconn_src_);
disconn_src_ = 0;
g_io_channel_unref(gioc_);
#include <list>
#include <memory>
-#include "port-internal.h"
+#include "debug-port-internal.hh"
#include "fdbroker-internal.h"
+#include "port-internal.h"
namespace rpc_port {
namespace internal {
--- /dev/null
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+PROJECT(rpc-port-util CXX)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(rpc-port-util REQUIRED
+ aul
+ bundle
+ dlog
+ glib-2.0
+)
+
+FOREACH(flag ${rpc-port-util_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Werror -Winline -fPIE")
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++14")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+SET(SOURCES "")
+
+ADD_DEFINITIONS("-DFULLVER=\"${FULLVER}\"")
+
+INCLUDE_DIRECTORIES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../
+ ${CMAKE_CURRENT_SOURCE_DIR}/../include
+ )
+
+AUX_SOURCE_DIRECTORY(src SOURCES)
+ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${rpc-port-util_LDFLAGS} "-pie" rpc-port)
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin)
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <iostream>
+#include <vector>
+
+#include "debug-port.hh"
+#include "log-private.hh"
+#include "message.hh"
+
+namespace rpc_port {
+namespace util {
+
+static const char PATH_RPC_PORT_UTIL_SOCK[] =
+ "/run/aul/daemons/.rpc-port-util-sock";
+static const char RPC_PORT_SIGNAL_PATH[] = "/Org/Tizen/RPC/Port/Signal";
+static const char RPC_PORT_SIGNAL_INTERFACE[] = "org.tizen.rpc.port.signal";
+static const char RPC_PORT_SIGNAL_DEBUG[] = "Debug";
+static const char RPC_PORT_SIGNAL_NEW[] = "New";
+
+class DisconnectedEvent {
+ public:
+ DisconnectedEvent(int pid, int fd, void* data)
+ : pid_(pid), fd_(fd), data_(data) {
+ }
+
+ int GetPid() {
+ return pid_;
+ }
+
+ int GetFd() {
+ return fd_;
+ }
+
+ void* GetData() {
+ return data_;
+ }
+
+ private:
+ int pid_;
+ int fd_;
+ void* data_;
+};
+
+DebugPort::DebugPort(std::string port_name, int pid)
+ : port_name_(std::move(port_name)),
+ pid_(pid) {
+ Subscribe();
+
+ int fd = CreateSocket();
+ if (fd < 0) {
+ _E("Failed to create socket");
+ exit(EXIT_FAILURE);
+ }
+
+ fd_ = fd;
+ Watch(fd_);
+}
+
+DebugPort::~DebugPort() {
+ Unwatch();
+
+ if (fd_ > 0)
+ close(fd_);
+
+ Unsubscribe();
+}
+
+void DebugPort::EmitSignal() {
+ GDBusConnection* conn = GetConnection();
+ if (conn == nullptr)
+ return;
+
+ GError* error = nullptr;
+ gboolean ret = g_dbus_connection_emit_signal(conn,
+ nullptr,
+ RPC_PORT_SIGNAL_PATH,
+ RPC_PORT_SIGNAL_INTERFACE,
+ RPC_PORT_SIGNAL_DEBUG,
+ g_variant_new("(si)", port_name_.c_str(), pid_),
+ &error);
+ if (ret != TRUE) {
+ _E("g_dbus_connection_emit_signal() is failed. error(%s)",
+ error ? error->message : "Unknown");
+ g_clear_error(&error);
+ return;
+ }
+
+ ret = g_dbus_connection_flush_sync(conn, nullptr, &error);
+ if (ret != TRUE) {
+ _E("g_dbus_connection_flush_sync() is failed. error(%s)",
+ error ? error->message : "Unknown");
+ g_clear_error(&error);
+ return;
+ }
+
+ _W("EmitSignal");
+}
+
+GDBusConnection* DebugPort::GetConnection() {
+ if (conn_)
+ return conn_;
+
+ GError* error = nullptr;
+ conn_ = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
+ if (conn_ == nullptr) {
+ _E("g_bus_get_sync() is failed. error(%s)",
+ error ? error->message : "Unknown");
+ g_clear_error(&error);
+ return nullptr;
+ }
+
+ return conn_;
+}
+
+void DebugPort::Subscribe() {
+ GDBusConnection* conn = GetConnection();
+ if (conn == nullptr)
+ return;
+
+ subs_tag_ = g_dbus_connection_signal_subscribe(conn,
+ nullptr,
+ RPC_PORT_SIGNAL_INTERFACE,
+ RPC_PORT_SIGNAL_NEW,
+ RPC_PORT_SIGNAL_PATH,
+ nullptr,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ OnGDBusSignalCb,
+ this,
+ nullptr);
+ if (subs_tag_ == 0) {
+ _E("g_dbus_connection_signal_subscribe() is failed");
+ return;
+ }
+
+ _W("tag(%u)", subs_tag_);
+}
+
+void DebugPort::Unsubscribe() {
+ if (subs_tag_)
+ g_dbus_connection_signal_unsubscribe(conn_, subs_tag_);
+
+ if (conn_)
+ g_object_unref(conn_);
+}
+
+int DebugPort::CreateSocket() {
+ int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd < 0) {
+ _E("socket() is failed. errno(%d)", errno);
+ return -1;
+ }
+
+ struct sockaddr_un addr = { 0, };
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", PATH_RPC_PORT_UTIL_SOCK);
+ unlink(addr.sun_path);
+
+ int ret = bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
+ if (ret < 0) {
+ _E("bind() is failed. fd(%d), errno(%d)", fd, errno);
+ close(fd);
+ return -1;
+ }
+
+ ret = listen(fd, 128);
+ if (ret < 0) {
+ _E("listen() is failed. fd(%d), errno(%d)", fd, errno);
+ close(fd);
+ return -1;
+ }
+
+ ret = chmod(addr.sun_path, S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
+ S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
+ if (ret < 0)
+ _E("chmod() is failed. errno(%d)", errno);
+
+ return fd;
+}
+
+void DebugPort::OnDataReceived(int pid, rpc_port_parcel_h parcel) {
+ if (pid_ != 0 && pid_ != pid)
+ return;
+
+ long long time = 0;
+ rpc_port_parcel_read_int64(parcel, &time);
+ time_t sent_time = static_cast<time_t>(time);
+
+ char* port_name = nullptr;
+ rpc_port_parcel_read_string(parcel, &port_name);
+ std::unique_ptr<char, decltype(std::free)*> port_name_ptr(port_name,
+ std::free);
+ if (port_name == nullptr)
+ return;
+
+ if (port_name != port_name_)
+ return;
+
+ char* destination = nullptr;
+ rpc_port_parcel_read_string(parcel, &destination);
+ std::unique_ptr<char, decltype(std::free)*> destination_ptr(destination,
+ std::free);
+
+ bool is_delegate = false;
+ rpc_port_parcel_read_bool(parcel, &is_delegate);
+
+ int port = -1;
+ rpc_port_parcel_read_int32(parcel, &port);
+
+ bool is_read = false;
+ rpc_port_parcel_read_bool(parcel, &is_read);
+
+ uint32_t seq = 0;
+ rpc_port_parcel_read_int32(parcel, reinterpret_cast<int*>(&seq));
+
+ unsigned int size = 0;
+ rpc_port_parcel_read_int32(parcel, reinterpret_cast<int*>(&size));
+ if (size == 0) {
+ _E("Invalid size");
+ return;
+ }
+
+ auto* buf = new unsigned char[size];
+ rpc_port_parcel_burst_read(parcel, buf, size);
+ std::vector<unsigned char> data(buf, buf + size);
+ delete[] buf;
+
+ auto* msg = new Message(sent_time, pid, port_name, destination, is_delegate,
+ port, is_read, seq, data);
+ g_idle_add([](gpointer data) -> gboolean {
+ auto* msg = static_cast<Message*>(data);
+ msg->Print();
+ delete msg;
+ return G_SOURCE_REMOVE;
+ }, msg);
+}
+
+void DebugPort::OnDisconnected(int pid, int fd) {
+ auto* event = new DisconnectedEvent(pid, fd, this);
+ g_idle_add([](gpointer data) -> gboolean {
+ auto* event = static_cast<DisconnectedEvent*>(data);
+ auto* debug_port = static_cast<DebugPort*>(event->GetData());
+ debug_port->RemoveLogger(event->GetPid(), event->GetFd());
+ delete event;
+ return G_SOURCE_REMOVE;
+ }, event);
+}
+
+void DebugPort::OnGDBusSignalCb(GDBusConnection *connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ _E("signal_name(%s)", signal_name);
+ std::string signal(signal_name);
+ if (signal != RPC_PORT_SIGNAL_NEW)
+ return;
+
+ auto* debug_port = static_cast<DebugPort*>(user_data);
+ debug_port->EmitSignal();
+}
+
+int DebugPort::Watch(int fd) {
+ GIOChannel* io = g_io_channel_unix_new(fd);
+ if (io == nullptr) {
+ _E("g_io_channel_unix_new() is failed");
+ return -1;
+ }
+
+ GIOCondition cond = static_cast<GIOCondition>(
+ (G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL));
+ guint tag = g_io_add_watch(io, cond, OnDebugPortConnectedCb, this);
+ if (tag == 0) {
+ _E("g_io_add_watch() is failed");
+ g_io_channel_unref(io);
+ return -1;
+ }
+
+ io_ = io;
+ watch_tag_ = tag;
+ return 0;
+}
+
+void DebugPort::Unwatch() {
+ if (watch_tag_) {
+ g_source_remove(watch_tag_);
+ watch_tag_ = 0;
+ }
+
+ if (io_) {
+ g_io_channel_unref(io_);
+ io_ = nullptr;
+ }
+}
+
+int DebugPort::Accept(struct ucred* cred) {
+ struct sockaddr_un addr = { 0, };
+ socklen_t socklen = sizeof(addr);
+ int client_fd = accept(fd_, reinterpret_cast<struct sockaddr*>(&addr),
+ &socklen);
+ if (client_fd < 0) {
+ _E("accept() is failed. errno(%d)", errno);
+ return -1;
+ }
+
+ socklen = sizeof(struct ucred);
+ int ret = getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED,
+ cred, &socklen);
+ if (ret < 0) {
+ _E("Failed to get peer information. client_fd(%d), errno(%d)",
+ client_fd, errno);
+ close(client_fd);
+ return -1;
+ }
+
+ struct timeval tv = { 5, 200 * 1000 };
+ ret = setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+ if (ret < 0) {
+ _E("Failed to set socket option. client_fd(%d), errno(%d)",
+ client_fd, errno);
+ close(client_fd);
+ return -1;
+ }
+
+ return client_fd;
+}
+
+gboolean DebugPort::OnDebugPortConnectedCb(GIOChannel* io,
+ GIOCondition cond,
+ gpointer data) {
+ _E("cond(%d)", static_cast<int>(cond));
+
+ if (cond & (G_IO_IN | G_IO_PRI)) {
+ auto* debug_port = static_cast<DebugPort*>(data);
+ struct ucred cred = { 0, };
+ int client_fd = debug_port->Accept(&cred);
+ if (client_fd < 0)
+ return G_SOURCE_CONTINUE;
+
+ debug_port->AddLogger(cred.pid, client_fd);
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+void DebugPort::AddLogger(int pid, int fd) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ loggers_.emplace_back(new Logger(pid, fd, this));
+ _W("Logger added. pid(%d), fd(%d)", pid, fd);
+}
+
+void DebugPort::RemoveLogger(int pid, int fd) {
+ std::lock_guard<std::recursive_mutex> lock(GetMutex());
+ auto iter = loggers_.begin();
+ while (iter != loggers_.end()) {
+ if ((*iter)->GetPid() == pid &&
+ (*iter)->GetFd() == fd) {
+ _W("Logger removed. pid(%d), fd(%d)", pid, fd);
+ iter = loggers_.erase(iter);
+ break;
+ }
+
+ ++iter;
+ }
+}
+
+} // namespace util
+} // namespace rpc_port
--- /dev/null
+/*
+ * Copyright (c) 2020 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 DEBUG_PORT_HH_
+#define DEBUG_PORT_HH_
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <list>
+
+#include "logger.hh"
+
+namespace rpc_port {
+namespace util {
+
+class DebugPort : public Logger::IEvent {
+ public:
+ DebugPort(std::string port_name, int pid);
+ virtual ~DebugPort();
+
+ void EmitSignal();
+
+ private:
+ GDBusConnection* GetConnection();
+ void Subscribe();
+ void Unsubscribe();
+ int CreateSocket();
+ int Watch(int fd);
+ void Unwatch();
+ int Accept(struct ucred* cred);
+ void AddLogger(int pid, int fd);
+ void RemoveLogger(int pid, int fd);
+
+ void OnDataReceived(int pid, rpc_port_parcel_h parcel) override;
+ void OnDisconnected(int pid, int fd) override;
+
+ static void OnGDBusSignalCb(GDBusConnection *connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+ static gboolean OnDebugPortConnectedCb(GIOChannel* io,
+ GIOCondition cond,
+ gpointer data);
+
+ std::recursive_mutex& GetMutex() const {
+ return mutex_;
+ }
+
+ private:
+ std::string port_name_;
+ int pid_;
+ GDBusConnection* conn_ = nullptr;
+ guint subs_tag_ = 0;
+ int fd_ = -1;
+ GIOChannel* io_ = nullptr;
+ guint watch_tag_ = 0;
+ std::list<std::unique_ptr<Logger>> loggers_;
+ mutable std::recursive_mutex mutex_;
+};
+
+} // namespace util
+} // namespace rpc_port
+
+#endif // DEBUG_PORT_HH_
--- /dev/null
+/*
+ * Copyright (c) 2020 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 LOG_PRIVATE_HH_
+#define LOG_PRIVATE_HH_
+
+#include <dlog.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RPC_PORT_UTIL"
+
+#undef _E
+#define _E LOGE
+
+#undef _W
+#define _W LOGW
+
+#undef _I
+#define _I LOGI
+
+#undef _D
+#define _D LOGD
+
+#endif // LOG_PRIVATE_HH_
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "log-private.hh"
+#include "logger.hh"
+
+namespace rpc_port {
+namespace util {
+
+Logger::Logger(int pid, int fd, IEvent* listener)
+ : pid_(pid), fd_(fd), listener_(listener) {
+ thread_ = std::thread([&]() {
+ GMainContext* context = g_main_context_new();
+ GIOChannel* io = g_io_channel_unix_new(fd_);
+ GIOCondition cond = static_cast<GIOCondition>(
+ G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR | G_IO_NVAL);
+ GSource* source = g_io_create_watch(io, cond);
+ g_source_set_callback(source, (GSourceFunc)OnDataReceivedCb, this,
+ nullptr);
+ g_source_set_priority(source, G_PRIORITY_DEFAULT);
+ g_source_attach(source, context);
+ g_source_unref(source);
+
+ loop_ = g_main_loop_new(context, FALSE);
+ g_main_context_push_thread_default(context);
+
+ g_main_loop_run(loop_);
+
+ if (!g_source_is_destroyed(source))
+ g_source_destroy(source);
+
+ g_io_channel_unref(io);
+
+ g_main_context_pop_thread_default(context);
+ g_main_loop_unref(loop_);
+ g_main_context_unref(context);
+ });
+}
+
+Logger::~Logger() {
+ if (g_main_loop_is_running(loop_))
+ g_main_loop_quit(loop_);
+
+ thread_.join();
+
+ if (fd_ > 0)
+ close(fd_);
+}
+
+int Logger::GetPid() {
+ return pid_;
+}
+
+int Logger::GetFd() {
+ return fd_;
+}
+
+int Logger::Read(void* buf, unsigned int size) {
+ char* buffer = static_cast<char*>(buf);
+ unsigned int left = size;
+ while (left) {
+ ssize_t read_size = read(fd_, buffer, left);
+ if (read_size <= 0) {
+ _E("Failed to read data. fd(%d), errno(%d)", fd_, errno);
+ return -1;
+ }
+
+ left -= read_size;
+ buffer += read_size;
+ }
+ return 0;
+}
+
+rpc_port_parcel_h Logger::Read()
+{
+ int size = 0;
+ int ret = Read(reinterpret_cast<void*>(&size), sizeof(size));
+ if (ret < 0)
+ return nullptr;
+
+ auto* buf = new unsigned char[size];
+ ret = Read(static_cast<void*>(buf), size);
+ if (ret < 0) {
+ delete[] buf;
+ return nullptr;
+ }
+
+ rpc_port_parcel_h parcel = nullptr;
+ rpc_port_parcel_create(&parcel);
+ rpc_port_parcel_burst_write(parcel, buf, size);
+ delete[] buf;
+ return parcel;
+}
+
+gboolean Logger::OnDataReceivedCb(GIOChannel* io,
+ GIOCondition cond, gpointer data) {
+ auto* logger = static_cast<Logger*>(data);
+ auto* listener = logger->listener_;
+ int fd = g_io_channel_unix_get_fd(io);
+ int pid = logger->pid_;
+
+ if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+ _E("Error(%d), fd(%d)", cond, fd);
+ listener->OnDisconnected(pid, fd);
+ return G_SOURCE_REMOVE;
+ }
+
+ rpc_port_parcel_h parcel = logger->Read();
+ listener->OnDataReceived(pid, parcel);
+ rpc_port_parcel_destroy(parcel);
+ return G_SOURCE_CONTINUE;
+}
+
+} // namespace util
+} // namespace rpc_port
--- /dev/null
+/*
+ * Copyright (c) 2020 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 LOGGER_HH_
+#define LOGGER_HH_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <rpc-port-parcel.h>
+
+#include <string>
+#include <memory>
+#include <thread>
+
+namespace rpc_port {
+namespace util {
+
+class Logger {
+ public:
+ class IEvent {
+ public:
+ virtual void OnDataReceived(int pid, rpc_port_parcel_h parcel) = 0;
+ virtual void OnDisconnected(int pid, int fd) = 0;
+ };
+
+ Logger(int pid, int fd, IEvent* listener);
+ virtual ~Logger();
+
+ int GetPid();
+ int GetFd();
+
+ private:
+ int Read(void* buf, unsigned int size);
+ rpc_port_parcel_h Read();
+ static gboolean OnDataReceivedCb(GIOChannel* io,
+ GIOCondition cond, gpointer data);
+
+ private:
+ int pid_;
+ int fd_;
+ IEvent* listener_;
+ std::thread thread_;
+ GMainLoop* loop_ = nullptr;
+};
+
+} // namespace util
+} // namespace rpc_port
+
+#endif // LOGGER_HH_
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <glib.h>
+
+#include "debug-port.hh"
+#include "log-private.hh"
+#include "options.hh"
+
+using namespace rpc_port;
+using namespace rpc_port::util;
+
+namespace {
+
+class MainLoop {
+ public:
+ MainLoop() {
+ loop_ = g_main_loop_new(nullptr, FALSE);
+ }
+
+ ~MainLoop() {
+ g_main_loop_unref(loop_);
+ }
+
+ void Run() {
+ g_main_loop_run(loop_);
+ }
+
+ void Quit() {
+ g_main_loop_quit(loop_);
+ }
+
+ private:
+ GMainLoop* loop_;
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ std::unique_ptr<Options> options = Options::Parse(argc, argv);
+ if (!options) {
+ _E("options is nullptr");
+ exit(1);
+ }
+
+ DebugPort debug_port(options->GetPortName(), options->GetPid());
+ debug_port.EmitSignal();
+
+ MainLoop loop;
+ loop.Run();
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <stdio.h>
+
+#include <chrono>
+#include <ctime>
+#include <iostream>
+#include <iomanip>
+
+#include "message.hh"
+
+namespace rpc_port {
+namespace util {
+
+Message::Message(time_t time, int pid, std::string port_name,
+ std::string destination, bool is_delegate, int port, bool is_read,
+ uint32_t seq, std::vector<unsigned char> data)
+ : time_(time),
+ pid_(pid),
+ port_name_(std::move(port_name)),
+ destination_(std::move(destination)),
+ is_delegate_(is_delegate),
+ port_(port),
+ is_read_(is_read),
+ seq_(seq),
+ data_(std::move(data)) {
+}
+
+Message::~Message() = default;
+
+void Message::Print() {
+ struct tm sent_time;
+ localtime_r(&time_, &sent_time);
+ char buf[50];
+ fprintf(stdout, "%s", asctime_r(&sent_time, buf));
+ fprintf(stdout, "[%s / %s:%d] [%u] ",
+ port_name_.c_str(),
+ is_delegate_ ? "delegate" : "main",
+ port_,
+ seq_);
+ if (is_read_)
+ fprintf(stdout, "%s -> %d \n", destination_.c_str(), pid_);
+ else
+ fprintf(stdout, "%d -> %s \n", pid_, destination_.c_str());
+
+ Hexdump();
+}
+
+void Message::Hexdump() {
+ unsigned int address = 0;
+ unsigned int row = 0;
+ unsigned int nread = 0;
+ std::cout << std::hex << std::setfill('0');
+ while (true) {
+ if (address >= data_.size())
+ break;
+
+ std::cout << std::setw(8) << address;
+ nread = ((data_.size() - address) > 16) ? 16 : (data_.size() - address);
+
+ for (unsigned int i = 0; i < 16; ++i) {
+ if (i % 8 == 0)
+ std::cout << ' ';
+
+ if (i < nread) {
+ std::cout << ' ' << std::setw(2) <<
+ static_cast<int>(data_[16 * row + i]);
+ } else {
+ std::cout << " ";
+ }
+ }
+
+ std::cout << " ";
+ for (unsigned int i = 0; i < nread ; ++i) {
+ if (data_[16 * row + i] < 32)
+ std::cout << '.';
+ else
+ std::cout << data_[16 * row + i];
+ }
+
+ std::cout << std::endl;
+ address += 16;
+ row++;
+ }
+ std::cout << std::endl;
+}
+
+} // namespace util
+} // namespace rpc_port
--- /dev/null
+/*
+ * Copyright (c) 2020 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 MESSAGE_HH_
+#define MESSAGE_HH_
+
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+namespace rpc_port {
+namespace util {
+
+class Message {
+ public:
+ Message(time_t time, int pid, std::string port_name, std::string destination,
+ bool is_delegate, int port, bool is_read, uint32_t seq,
+ std::vector<unsigned char> data);
+ virtual ~Message();
+
+ void Print();
+
+ private:
+ void Hexdump();
+
+ private:
+ time_t time_;
+ int pid_;
+ std::string port_name_;
+ std::string destination_;
+ bool is_delegate_;
+ int port_;
+ bool is_read_;
+ uint32_t seq_;
+ std::vector<unsigned char> data_;
+};
+
+} // namespace util
+} // namespace rpc_port
+
+#endif // MESSAGE_HH_
--- /dev/null
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#include <getopt.h>
+
+#include <iostream>
+#include <cstring>
+#include <memory>
+
+#include "options.hh"
+
+namespace rpc_port {
+namespace util {
+
+Options::Options() {
+ help_ = R"__option_cb(
+Usage:
+ rpc-port-util [OPTION...]
+
+Help Options:
+ -h, --help Show help options
+
+Additional Options:
+ -n, --name=<Port Name> Port name
+ -p, --pid=<Process ID> Process ID
+
+Application Options:
+ -v, --version Show version information
+)__option_cb";
+}
+
+void Options::PrintUsage() {
+ std::cerr << help_ << std::endl;
+}
+
+void Options::PrintVersion() {
+ std::cerr << "rpc-port-util " << FULLVER << std::endl;
+}
+
+void Options::PrintSample() {
+ std::cerr << "rpc-port-util -n Message " << std::endl;
+}
+
+std::unique_ptr<Options> Options::Parse(int argc, char** argv) {
+ int cmd[CMD_MAX] = { 0, };
+ int opt[OPT_MAX] = { 0, };
+ auto options = std::unique_ptr<Options>(new Options());
+ int option_index = 0;
+
+ struct option long_options[] = {
+ {"version", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {"name", required_argument, NULL, 'n'},
+ {"pid", required_argument, NULL, 'p'},
+ {0, 0, 0, 0}
+ };
+
+ while (true) {
+ int c = getopt_long(argc, argv, "vhn:p:", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 0:
+ break;
+
+ case 'v':
+ cmd[CMD_VERSION] = 1;
+ break;
+
+ case 'h':
+ cmd[CMD_HELP] = 1;
+ break;
+
+ case 'n':
+ opt[OPT_NAME] = 1;
+ options->port_name_ = optarg;
+ break;
+
+ case 'p':
+ opt[OPT_PID] = 1;
+ options->pid_ = std::stoi(optarg);
+ break;
+
+ default:
+ cmd[CMD_HELP] = 1;
+ }
+ }
+
+ if (cmd[CMD_VERSION]) {
+ options->PrintVersion();
+ return std::unique_ptr<Options>(nullptr);
+ }
+
+ if (cmd[CMD_HELP]) {
+ options->PrintUsage();
+ return std::unique_ptr<Options>(nullptr);
+ } else if (!opt[OPT_NAME]) {
+ std::cerr << "Select a port name" << std::endl;
+ options->PrintSample();
+ return std::unique_ptr<Options>(nullptr);
+ }
+
+ return options;
+}
+
+} // namespace util
+} // namespace rpc_port
--- /dev/null
+/*
+ * Copyright (c) 2020 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 OPTIONS_HH_
+#define OPTIONS_HH_
+
+#include <string>
+#include <memory>
+
+namespace rpc_port {
+namespace util {
+
+class Options {
+ public:
+ Options();
+ ~Options() = default;
+
+ static std::unique_ptr<Options> Parse(int argc, char** argv);
+
+ std::string GetPortName() const {
+ return port_name_;
+ }
+
+ int GetPid() {
+ return pid_;
+ }
+
+ private:
+ enum Cmd {
+ CMD_VERSION,
+ CMD_HELP,
+ CMD_MAX
+ };
+
+ enum Opt {
+ OPT_NAME,
+ OPT_PID,
+ OPT_MAX
+ };
+
+ void PrintUsage();
+ void PrintVersion();
+ void PrintSample();
+
+ private:
+ std::string port_name_;
+ int pid_ = 0;
+ std::string help_;
+};
+
+} // namespace util
+} // namespace rpc_port
+
+#endif // OPTIONS_HH_