From badd67e83d8fe0de6b36f9364473d3b85663e9e1 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Wed, 31 Jan 2018 11:51:07 +0100 Subject: [PATCH] Let client create one connection per one request Change-Id: I220c399d125f3de70490855c3c4fe08a2958b3eb --- src/capi/impl/privacy_privilege_manager.c | 114 ++++++++++++++----------- src/client/api/ApiInterface.h | 8 +- src/client/api/askuser-notification-client.cpp | 6 +- src/client/impl/ApiInterfaceImpl.cpp | 81 +++++++++++------- src/client/impl/ApiInterfaceImpl.h | 22 +++-- src/client/impl/PopupCallbackClosure.h | 59 ------------- src/ipc/client-channel.cpp | 20 +++-- src/ipc/client-channel.h | 6 +- src/ipc/common-types.h | 7 +- 9 files changed, 153 insertions(+), 170 deletions(-) delete mode 100644 src/client/impl/PopupCallbackClosure.h diff --git a/src/capi/impl/privacy_privilege_manager.c b/src/capi/impl/privacy_privilege_manager.c index 98eed15..a009208 100644 --- a/src/capi/impl/privacy_privilege_manager.c +++ b/src/capi/impl/privacy_privilege_manager.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 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. @@ -28,12 +28,17 @@ #define UNUSED __attribute__((unused)) -struct ppm_private_s { - askuser_client *client; +struct ppm_channel_s { GIOChannel *channel; GIOCondition condition; guint watch_id; }; +typedef struct ppm_channel_s ppm_channel; + +struct ppm_private_s { + askuser_client *client; + GHashTable *channels; +}; typedef struct ppm_private_s ppm_private; struct ppm_callback_closure_s { @@ -44,40 +49,37 @@ typedef struct ppm_callback_closure_s ppm_callback_closure; static ppm_private *ppm_handle = NULL; -static void ppm_private_init(ppm_private *handle) +static void ppm_free_channel(gpointer data) { - handle->channel = NULL; - handle->condition = 0; - handle->watch_id = 0; + ppm_channel* channel = data; + + if (channel) { + g_source_remove(channel->watch_id); + g_io_channel_unref(channel->channel); + g_free(channel); + } } static ppm_error_e ask_user_to_ppm_error(int ask_error) { - ppm_error_e ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; - switch (ask_error) { case ASKUSER_API_SUCCESS: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; - break; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; case ASKUSER_API_UNKNOWN_ERROR: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; - break; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; case ASKUSER_API_OUT_OF_MEMORY: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; - break; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; case ASKUSER_API_INVALID_PARAM: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; - break; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER; case ASKUSER_API_CONNECTION_ERROR: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR; - break; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR; case ASKUSER_API_ALREADY_IN_PROGRESS: - ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_ALREADY_IN_PROGRESS; default: break; } - return ret; + return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; } static ppm_check_result_e ask_user_check_result_to_ppm(askuser_check_result result) @@ -98,7 +100,7 @@ static ppm_request_result_e askuser_client_request_result_to_ppm(askuser_popup_r { switch (result) { case ASKUSER_POPUP_RESULT_ALLOW_FOREVER: - return PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_ALLOW_FOREVER; + return PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_ALLOW_FOREVER; case ASKUSER_POPUP_RESULT_DENY_FOREVER: return PRIVACY_PRIVILEGE_MANAGER_REQUEST_RESULT_DENY_FOREVER; case ASKUSER_POPUP_RESULT_DENY_ONCE: @@ -138,11 +140,6 @@ static gboolean ppm_error_condition(GIOCondition cond) return !!(cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)); } -static gboolean ppm_is_connected(ppm_private *handle) -{ - return handle->channel != NULL; -} - static gboolean ppm_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data) { int fd, events; @@ -163,33 +160,38 @@ static void ask_status_callback(int fd, int events, void *p_user_data) GIOCondition gio_condition = askuser_events_to_g_io_condition(events); if (events == ASKUSER_EMPTY_EVENTS) { - if (ppm_is_connected(handle)) { - g_source_remove(handle->watch_id); - g_io_channel_unref(handle->channel); - ppm_private_init(handle); - } + g_hash_table_remove(handle->channels, GINT_TO_POINTER(fd)); return; } - if (!ppm_is_connected(handle)) { - handle->condition = gio_condition; - handle->channel = g_io_channel_unix_new(fd); + ppm_channel *channel = g_hash_table_lookup(handle->channels, GINT_TO_POINTER(fd)); + + if (channel == NULL) { + channel = (ppm_channel *) g_malloc0(sizeof(ppm_channel)); + if (!channel) { + return; + } + + channel->condition = gio_condition; + channel->channel = g_io_channel_unix_new(fd); - g_io_channel_set_encoding (handle->channel, NULL, NULL); - g_io_channel_set_close_on_unref(handle->channel, FALSE); + g_io_channel_set_encoding (channel->channel, NULL, NULL); + g_io_channel_set_close_on_unref(channel->channel, FALSE); - handle->watch_id = g_io_add_watch(handle->channel, - handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + channel->watch_id = g_io_add_watch(channel->channel, + channel->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL, ppm_gio_cb, handle); + + g_hash_table_insert(handle->channels, GINT_TO_POINTER(fd), (gpointer) channel); return; } - if (handle->condition != gio_condition) { - handle->condition = gio_condition; - g_source_remove(handle->watch_id); - handle->watch_id = g_io_add_watch(handle->channel, - handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + if (channel->condition != gio_condition) { + channel->condition = gio_condition; + g_source_remove(channel->watch_id); + channel->watch_id = g_io_add_watch(channel->channel, + channel->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL, ppm_gio_cb, handle); } @@ -220,7 +222,14 @@ static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_caus static void ppm_free_client() { if (ppm_handle != NULL) { - askuser_client_finalize(ppm_handle->client); + if (ppm_handle->client != NULL) { + askuser_client_finalize(ppm_handle->client); + } + + if (ppm_handle->channels != NULL) { + g_hash_table_destroy(ppm_handle->channels); + } + free(ppm_handle); ppm_handle = NULL; } @@ -234,20 +243,23 @@ static int ppm_init_client() return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; } - ppm_private_init(ppm_handle); + ppm_handle->channels = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, ppm_free_channel); + + if (ppm_handle->channels == NULL) { + ppm_free_client(); + return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY; + } int ret = askuser_client_initialize(&ppm_handle->client, ask_status_callback, ppm_handle); if (ret != ASKUSER_API_SUCCESS) { - free(ppm_handle); - ppm_handle = NULL; + ppm_free_client(); return ask_user_to_ppm_error(ret); } ret = atexit(ppm_free_client); if (ret != 0) { - askuser_client_finalize(ppm_handle->client); - free(ppm_handle); - ppm_handle = NULL; + ppm_free_client(); return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN; } } diff --git a/src/client/api/ApiInterface.h b/src/client/api/ApiInterface.h index 87bb603..c1b5d2c 100644 --- a/src/client/api/ApiInterface.h +++ b/src/client/api/ApiInterface.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 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. @@ -24,7 +24,6 @@ #include -#include #include namespace AskUser { @@ -40,8 +39,9 @@ public: virtual int process(int fd, int events) = 0; virtual askuser_check_result checkPrivilege(const std::string &privilege) = 0; - virtual RequestId popupRequest(const PopupCallbackClosure &closure, - const std::string &privilege) = 0; + virtual RequestId popupRequest(const std::string &privilege, + const askuser_popup_response_callback callback, + void *userData) = 0; virtual bool popupRequestInProgress(const std::string &privilege) const = 0; }; diff --git a/src/client/api/askuser-notification-client.cpp b/src/client/api/askuser-notification-client.cpp index 7faa1f2..1067816 100644 --- a/src/client/api/askuser-notification-client.cpp +++ b/src/client/api/askuser-notification-client.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 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. @@ -29,7 +29,6 @@ #include #include -#include #include #include @@ -129,8 +128,7 @@ int askuser_client_popup_request(askuser_client *p_client, const char *privilege return ASKUSER_API_ALREADY_IN_PROGRESS; } - AskUser::Client::PopupCallbackClosure closure(response_callback, privilege, p_user_data); - AskUser::Client::RequestId id = p_client->impl->popupRequest(closure, privilege); + AskUser::Client::RequestId id = p_client->impl->popupRequest(privilege, response_callback, p_user_data); if (p_request_id) { *p_request_id = id; diff --git a/src/client/impl/ApiInterfaceImpl.cpp b/src/client/impl/ApiInterfaceImpl.cpp index a1833e8..79619a2 100644 --- a/src/client/impl/ApiInterfaceImpl.cpp +++ b/src/client/impl/ApiInterfaceImpl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 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. @@ -20,6 +20,7 @@ * @brief The definition of ApiInterfaceImpl. */ +#include #include #include @@ -121,73 +122,91 @@ askuser_check_result ApiInterfaceImpl::checkPrivilege(const std::string &privile return ASKUSER_CHECK_RESULT_DENY; } -RequestId ApiInterfaceImpl::popupRequest(const PopupCallbackClosure &closure, - const std::string &privilege) +RequestId ApiInterfaceImpl::popupRequest(const std::string &privilege, + const askuser_popup_response_callback callback, + void *userData) { - RequestId id = static_cast(m_channel->popupRequest(privilege)); + Protocol::ConnectionContext conCtx = m_channel->popupRequest(privilege); - auto it = m_popupClosures.find(id); - if (it != m_popupClosures.end()) { - ALOGE("Popup closure exists for id: " << id << - " privilege: " << it->second.privilege() << ", replacing"); - popupResponse(id, ASKUSER_UNKNOWN_ERROR); + auto sameRequest = [&] (const Request &req) { + return req.m_requestId == conCtx.m_requestId; + }; + + auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameRequest); + if (reqIt != m_requests.end()) { + ALOGE("Popup closure exists for id: " << conCtx.m_requestId << + " privilege: " << reqIt->m_privilege << ", replacing"); + popupResponse(conCtx.m_requestId, ASKUSER_UNKNOWN_ERROR); } if (popupRequestInProgress(privilege)) { - ALOGE("Privilege " << closure.privilege() << " already exists in the pending privileges set"); + ALOGE("Privilege " << privilege << " already exists in the pending requests"); } - m_requestedPrivileges.insert(privilege); - m_popupClosures.insert({id, closure}); + m_requests.push_back({ conCtx.m_requestId, conCtx.m_fd, privilege, callback, userData }); - return id; + return conCtx.m_requestId; } bool ApiInterfaceImpl::popupRequestInProgress(const std::string &privilege) const { - return m_requestedPrivileges.find(privilege) != m_requestedPrivileges.end(); + auto samePrivilege = [&] (const Request &req) { + return req.m_privilege == privilege; + }; + + return std::find_if(m_requests.begin(), m_requests.end(), samePrivilege) != m_requests.end(); } void ApiInterfaceImpl::updateConnection(Protocol::ConnectionFd fd, int mask) { m_statusClosure(fd, askUserMaskToEvents(mask)); - // the connection is about to close, respond to all pending requests if (mask == ASKUSER_EMPTY_EVENTS) { - respondToAllRequests(ASKUSER_CALL_CAUSE_ERROR, ASKUSER_POPUP_RESULT_DENY_ONCE); + auto sameFd = [&] (const Request &req) { + return req.m_fd == fd; + }; + + auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameFd); + if (reqIt == m_requests.end()) { + return; + } + + ALOGW("askuser-notification has been unexpectedly stopped, " + "sending the error response for privilege: " + << reqIt->m_privilege << " id: " << reqIt->m_requestId); + + popupResponse(reqIt->m_requestId, ASKUSER_UNKNOWN_ERROR); } } void ApiInterfaceImpl::popupResponse(Protocol::RequestId id, int response) { - auto it = m_popupClosures.find(id); - if (it == m_popupClosures.end()) { - ALOGE("Couldn't find popup callback closure for id: " << id); - return; - } + auto sameRequestId = [&] (const Request &req) { + return req.m_requestId == id; + }; - const auto &closure = it->second; - if (!popupRequestInProgress(closure.privilege())) { - ALOGE("Couldn't find privilege " << closure.privilege() << " in the pending privileges set"); + auto reqIt = std::find_if(m_requests.begin(), m_requests.end(), sameRequestId); + if (reqIt == m_requests.end()) { + ALOGE("Couldn't find request for id: " << id); + return; } askuser_call_cause cause = deduceCauseFromResponse(response); askuser_popup_result res = responseToAskUserPopupResult(response); - closure(id, cause, res); + reqIt->m_callback(id, cause, res, reqIt->m_privilege.c_str(), reqIt->m_userData); - m_requestedPrivileges.erase(closure.privilege()); - m_popupClosures.erase(it); + m_requests.erase(reqIt); } void ApiInterfaceImpl::respondToAllRequests(askuser_call_cause cause, askuser_popup_result result) { - for (const auto &closure : m_popupClosures) { - closure.second(closure.first, cause, result); + for (const auto &req : m_requests) { + req.m_callback(req.m_requestId, cause, result, + req.m_privilege.c_str(), req.m_userData); } - m_requestedPrivileges.clear(); - m_popupClosures.clear(); + m_requests.clear(); } } // namespace Client diff --git a/src/client/impl/ApiInterfaceImpl.h b/src/client/impl/ApiInterfaceImpl.h index efa97c6..40e8d48 100644 --- a/src/client/impl/ApiInterfaceImpl.h +++ b/src/client/impl/ApiInterfaceImpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2018 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. @@ -22,12 +22,10 @@ #pragma once -#include #include -#include +#include #include -#include #include #include @@ -48,8 +46,9 @@ public: virtual int process(int fd, int events); virtual askuser_check_result checkPrivilege(const std::string &privilege); - virtual RequestId popupRequest(const PopupCallbackClosure &closure, - const std::string &privilege); + virtual RequestId popupRequest(const std::string &privilege, + const askuser_popup_response_callback callback, + void *userData); virtual bool popupRequestInProgress(const std::string &privilege) const; void updateConnection(Protocol::ConnectionFd fd, int mask); @@ -58,10 +57,17 @@ public: private: void respondToAllRequests(askuser_call_cause cause, askuser_popup_result result); + struct Request { + Protocol::RequestId m_requestId; + Protocol::ConnectionFd m_fd; + std::string m_privilege; + askuser_popup_response_callback m_callback; + void *m_userData; + }; + StatusCallbackClosure m_statusClosure; std::unique_ptr m_channel; - std::map m_popupClosures; - std::set m_requestedPrivileges; + std::vector m_requests; }; } // namespace Client diff --git a/src/client/impl/PopupCallbackClosure.h b/src/client/impl/PopupCallbackClosure.h deleted file mode 100644 index 9bb2953..0000000 --- a/src/client/impl/PopupCallbackClosure.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 - */ - -/** - * @file PopupCallbackClosure.h - * @author Piotr Sawicki - * @brief The definition of PopupCallbackClosure. - */ - -#pragma once - -#include - -#include - -namespace AskUser { - -namespace Client { - -class PopupCallbackClosure { -public: - PopupCallbackClosure(askuser_popup_response_callback callback, const char *privilege, void *userData) - : m_callback(callback) - , m_privilege(privilege) - , m_userData(userData) - {} - - void operator()(int requestId, askuser_call_cause cause, askuser_popup_result result) const - { - m_callback(requestId, cause, result, m_privilege.c_str(), m_userData); - } - - const std::string &privilege() const { - return m_privilege; - } - -private: - askuser_popup_response_callback m_callback; - std::string m_privilege; - void *m_userData; -}; - -} // namespace Client - -} // namespace AskUser - diff --git a/src/ipc/client-channel.cpp b/src/ipc/client-channel.cpp index e0c3553..52593b1 100644 --- a/src/ipc/client-channel.cpp +++ b/src/ipc/client-channel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co. + * Copyright (c) 2017 - 2018 Samsung Electronics Co. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,13 +43,14 @@ ClientChannel::~ClientChannel() { } } -void ClientChannel::init() { +int ClientChannel::connect() { Sock s(Sock::CLI_STREAM); if (0 > s.connect(getStreamSocketPath(geteuid()))) throw ConnectionException("Cannot connect to the server"); int fd = s.getFd(); m_sockets[fd] = SockDesc(std::move(s)); + return fd; } AskUser::Protocol::RequestId ClientChannel::generateRequestId() { @@ -57,23 +58,20 @@ AskUser::Protocol::RequestId ClientChannel::generateRequestId() { return requestId++; } -Protocol::RequestId ClientChannel::popupRequest(const std::string &privilege) { - if (m_sockets.empty()) { - init(); // client uses only one socket - } +Protocol::ConnectionContext ClientChannel::popupRequest(const std::string &privilege) { + int fd = connect(); // clients use only one connection per request std::stringstream ss; RequestId requestId = generateRequestId(); ss << MSGID_POPUP << " " << base64Encode(privilege) << " " << requestId << '\n'; - int fd = m_sockets.begin()->first; std::string str = ss.str(); std::copy(str.begin(), str.end(), std::back_inserter(m_sockets[fd].output)); m_callbacks->updateConnection(fd, FdMask::READ | FdMask::WRITE); - return requestId; + return ConnectionContext{requestId, fd}; } void ClientChannel::onAccept(int) { @@ -83,7 +81,7 @@ void ClientChannel::onClose(int fd) { m_callbacks->updateConnection(fd, 0); } -int ClientChannel::onReceive(UNUSED int fd, std::vector &&message) { +int ClientChannel::onReceive(int fd, std::vector &&message) { if (!message.size()) { ALOGE("Server received empty message"); return -EINVAL; @@ -103,6 +101,10 @@ int ClientChannel::onReceive(UNUSED int fd, std::vector &&message) int response = std::stoi(message[2]); m_callbacks->popupResponse(id, response); + + // after receiving the response just close the connection + closeConnection(fd); + break; } default : diff --git a/src/ipc/client-channel.h b/src/ipc/client-channel.h index 7f6b2a6..fd1c94d 100644 --- a/src/ipc/client-channel.h +++ b/src/ipc/client-channel.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co. + * Copyright (c) 2017 - 2018 Samsung Electronics Co. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,14 +66,14 @@ public: ClientChannel(ClientCallbacksPtr ptr); virtual ~ClientChannel(); - virtual Protocol::RequestId popupRequest(const std::string &privilege); + virtual ConnectionContext popupRequest(const std::string &privilege); private: virtual void onAccept(int fd); virtual void onClose(int fd); virtual int onReceive(int fd, std::vector &&message); virtual void onSend(int fd); - void init(); + int connect(); AskUser::Protocol::RequestId generateRequestId(); ClientCallbacksPtr m_callbacks; diff --git a/src/ipc/common-types.h b/src/ipc/common-types.h index 811abb5..aecafec 100644 --- a/src/ipc/common-types.h +++ b/src/ipc/common-types.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co. + * Copyright (c) 2017 - 2018 Samsung Electronics Co. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,11 @@ typedef int RequestId; typedef std::string Privilege; typedef std::vector PrivilegeVector; +struct ConnectionContext { + RequestId m_requestId; + ConnectionFd m_fd; +}; + const ConnectionFd INVALID_FD = -1; const RequestId INVALID_ID = -1; -- 2.7.4