From 325b6ce8d90d21598be2bcffc991adab2ad4a0ae Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Thu, 6 Jul 2017 17:08:16 +0200 Subject: [PATCH] Implement app to test CAPI and simulate server side * Add more detailed help * Add switches for working as client or service Change-Id: Iaef748054279cafc44f763be036d8c26db3e671d --- src/capi/CMakeLists.txt | 1 + .../test/privacy_privilege_manager_test.cpp | 530 ++++++++++++++++-- 2 files changed, 476 insertions(+), 55 deletions(-) diff --git a/src/capi/CMakeLists.txt b/src/capi/CMakeLists.txt index ee7ac8a..70cf037 100644 --- a/src/capi/CMakeLists.txt +++ b/src/capi/CMakeLists.txt @@ -26,6 +26,7 @@ PKG_CHECK_MODULES(PRIVACY_PRIVILEGE_MANAGER_DEP INCLUDE_DIRECTORIES(SYSTEM ${PRIVACY_PRIVILEGE_MANAGER_DEP_INCLUDE_DIRS}) INCLUDE_DIRECTORIES( + ${ASKUSER_PATH}/common ${ASKUSER_PATH}/ipc-lib ${PRIVACY_PRIVILEGE_MANAGER_PATH}/include ${ASKUSER_PATH}/client/include diff --git a/src/capi/test/privacy_privilege_manager_test.cpp b/src/capi/test/privacy_privilege_manager_test.cpp index d9768d7..a596f38 100644 --- a/src/capi/test/privacy_privilege_manager_test.cpp +++ b/src/capi/test/privacy_privilege_manager_test.cpp @@ -21,15 +21,292 @@ * @brief This file contains test of CAPI for Privacy Privilege Manager */ -#include +#include +#include +#include #include -#include #include +#include #include +#include +#include +#include +#include #include -const char *result_to_str(ppm_popup_result_e result) +using namespace AskUser::Protocol; + +struct ServerPrivilegeRequest { + + ServerPrivilegeRequest(RequestId reqId) + : m_id(reqId) + {} + + ServerPrivilegeRequest(RequestId reqId, const Privilege &privilege) + : m_id(reqId) + , m_privilege(privilege) + {} + + bool operator<(const ServerPrivilegeRequest &other) const { + return m_id < other.m_id; + } + + RequestId m_id; + Privilege m_privilege; +}; + +typedef std::set PrivilegeRequestSet; + +struct ClientRequest { + RequestId m_id; + Privilege m_privilege; +}; + +struct Connection +{ + GIOChannel *gio_channel; + GIOCondition condition; + guint watch_id; +}; + +enum class Mode { + CLIENT, + SERVER, + UNKNOWN +}; + +const GIOCondition GIO_ERROR_CONDITION = static_cast(G_IO_ERR | G_IO_HUP | G_IO_NVAL); + +GIOCondition maskToGIOCondition(int mask) +{ + int ret = (mask & FdMask::READ ? G_IO_IN : 0) | + (mask & FdMask::WRITE ? G_IO_OUT : 0); + return static_cast(ret); +} + +GIOCondition makeCondition(GIOCondition condition) +{ + return static_cast(condition | GIO_ERROR_CONDITION); +} + +bool isErrorCondition(GIOCondition condition) +{ + return !!(GIO_ERROR_CONDITION & condition); +} + +void print_prompt(Mode mode) +{ + switch (mode) { + case Mode::CLIENT: + printf("Client "); + break; + case Mode::SERVER: + printf("Server "); + break; + case Mode::UNKNOWN: + printf("Unknown "); + break; + } + + printf("> "); + fflush(stdout); +} + +struct ServerSimulator : public IServerCallbacks { + + ServerSimulator() + : m_channel(nullptr) + { + } + + virtual void newConnection(ConnectionFd fd, const Credentials &creds) { + printf("\nnew connection fd: %d credentials = { label: \"%s\" uid: %s }", + fd, creds.label.c_str(), creds.uid.c_str()); + } + + static gboolean server_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data) + { + ServerSimulator *handle = static_cast(data); + + int fd = g_io_channel_unix_get_fd(src); + int events = (cond & G_IO_IN ? FdMask::READ : 0) | + (cond & G_IO_OUT ? FdMask::WRITE : 0); + + handle->m_channel->process(fd, isErrorCondition(cond) ? 0 : events); + + return TRUE; + } + + virtual void updateConnection(ConnectionFd fd, int mask) { + if (fd == -1) { + printf("ERROR: updateConnection() called with fd == -1\n"); + return; + } + + GIOCondition gio_condition = maskToGIOCondition(mask); + + auto it = m_connections.find(fd); + + if (mask == 0) { + if (it == m_connections.end()) + return; + + auto &connection = it->second; + + g_source_remove(connection.watch_id); + g_io_channel_unref(connection.gio_channel); + + m_connections.erase(fd); + m_requests.erase(fd); + + printf("Connection for fd: %d has been closed\n", fd); + print_prompt(Mode::SERVER); + return; + } + + if (it == m_connections.end()) { + GIOChannel *gio_channel = g_io_channel_unix_new(fd); + + g_io_channel_set_encoding (gio_channel, nullptr, nullptr); + g_io_channel_set_close_on_unref(gio_channel, FALSE); + + guint watch_id = g_io_add_watch(gio_channel, + makeCondition(gio_condition), + server_gio_cb, + this); + + m_connections.insert({ fd, Connection{gio_channel, gio_condition, watch_id }}); + + return; + } + + auto &connection = it->second; + + if (connection.condition != gio_condition) { + connection.condition = gio_condition; + g_source_remove(connection.watch_id); + connection.watch_id = g_io_add_watch(connection.gio_channel, + makeCondition(connection.condition), + server_gio_cb, + this); + } + } + + virtual void popup(ConnectionFd fd, RequestId id, Privilege &&privilege) { + printf("\npopup request fd: %d requestId: %d privilege: \"%s\"\n", fd, id, privilege.c_str()); + addRequest(fd, id, privilege); + print_prompt(Mode::SERVER); + } + + void addRequest(ConnectionFd fd, RequestId id, const Privilege &privilege) { + auto fdIt = m_requests.find(fd); + if (fdIt == m_requests.end()) { + m_requests.insert({ fd, PrivilegeRequestSet() }); + } + + auto &s = m_requests[fd]; + + auto reqIt = s.find(id); + if (reqIt != s.end()) { + printf("ERROR: We already have request id: %d for fd: %d\n", id, fd); + return; + } + + s.insert(ServerPrivilegeRequest(id, privilege)); + } + + void sendResponse(ConnectionFd fd, RequestId id, int response) { + auto fdIt = m_requests.find(fd); + if (fdIt == m_requests.end()) { + printf("ERROR: Connection for fd: %d doesn't exist!\n", fd); + return; + } + + auto &s = fdIt->second; + + auto reqIt = s.find(id); + if (reqIt == s.end()) { + printf("ERROR: Request %d doesn't exist for fd: %d\n", id, fd); + return; + } + + if (m_channel) { + m_channel->popupResponse(fd, id, response); + + // delete request + s.erase(reqIt); + if (s.empty()) { + m_requests.erase(fdIt); + } + } + } + + void showPrivilegeRequests() { + printf("fd requestId privilege\n"); + for (const auto &con : m_requests) { + for (const auto &req : con.second) { + printf("%-11d %-11d %-20s\n", con.first, req.m_id, req.m_privilege.c_str()); + } + } + } + + void closeConnection(int fd) { + auto it = m_connections.find(fd); + if (it == m_connections.end()) { + printf("ERROR: Connection fd: %d not found\n", fd); + print_prompt(Mode::SERVER); + return; + } + + m_requests.erase(fd); + m_channel->process(fd, 0); + } + + void showConnections() { + printf("fd\n"); + for (const auto &con : m_connections) { + printf("%d\n", con.first); + } + } + + void setServerChannel(ServerChannel *ptr) { + m_channel = ptr; + } + +private: + ServerChannel *m_channel; + std::map m_connections; + std::map m_requests; +}; + +struct AppContext { + AppContext() + : m_serverSimulator(nullptr) + , m_channel(nullptr) + , m_mode(Mode::UNKNOWN) + {} + + ~AppContext() { + if (m_channel != nullptr) { + delete m_channel; + } + } + + void initialize() { + if (m_mode == Mode::SERVER) { + m_serverSimulator = new ServerSimulator(); + m_channel = new ServerChannel(ServerCallbacksPtr(m_serverSimulator)); + m_serverSimulator->setServerChannel(m_channel); + } + } + + ServerSimulator *m_serverSimulator; + ServerChannel *m_channel; + Mode m_mode; +}; + +const char *popup_result_to_str(ppm_popup_result_e result) { switch (result) { case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER: @@ -45,33 +322,38 @@ const char *result_to_str(ppm_popup_result_e result) void popup_response_cb(ppm_call_cause_e cause, ppm_popup_result_e result, void *user_data) { - int id = (int) user_data; + ClientRequest *request = static_cast(user_data); + if (request == nullptr) { + printf("ERROR: User data is nullptr\n"); + return; + } + + printf("localId: %d privilege: \"%s\" ", request->m_id, request->m_privilege.c_str()); - printf("resp_id: %d ", id); switch (cause) { case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER: - printf("popup answer: %s\n", result_to_str(result)); + printf("popup response ANSWER: %s\n", popup_result_to_str(result)); break; case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR: - printf("popup cause PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n"); + printf("popup response ERROR cause: PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n"); break; } - printf(">"); - fflush(stdout); + delete request; + + print_prompt(Mode::CLIENT); } -static void print_help() +void print_client_help() { - printf("0 - check privilege status\n"); - printf("1 - send popup request\n"); - printf("h - print help\n"); - printf("q - quit\n"); - printf(">"); - fflush(stdout); + printf("c check privilege status\n"); + printf("r send popup request\n"); + printf("h print help\n"); + printf("q quit\n"); + print_prompt(Mode::CLIENT); } -static void print_error(ppm_error_e error) +void print_client_error(ppm_error_e error) { switch (error) { case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE: @@ -84,13 +366,14 @@ static void print_error(ppm_error_e error) break; case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY: printf("Out of memory\n"); + break; case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN: printf("Unknown error\n"); break; } } -static void print_check_result(ppm_check_result_e result) +static void print_client_check_result(ppm_check_result_e result) { printf("res: "); @@ -106,74 +389,211 @@ static void print_check_result(ppm_check_result_e result) break; } - printf(">"); - fflush(stdout); + print_prompt(Mode::CLIENT); } -gboolean main_loop_cb(GIOChannel *channel, GIOCondition cond, gpointer user_data) +void handle_client(char *buffer) { - int ret; - char buffer[4096]; ppm_check_result_e result; - ppm_error_e err; - - (void) channel; (void) cond; (void) user_data; - - ret = read(0, buffer, sizeof(buffer)); - if (ret < 0) { - printf("read from stdin failed\n"); - exit(1); - } - - buffer[ret - 1] = '\0'; - int id = rand() % 1024; + ppm_error_e err = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE; + ClientRequest *request = nullptr; + char *privilege = nullptr; + static RequestId clientRequestId; switch (buffer[0]) { - case '0': + case 'c': if (strlen(buffer) < 3) { - print_help(); + print_client_help(); break; } err = static_cast(ppm_check_privilege(&buffer[2], &result)); if (err == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) { - print_check_result(result); - } - else { - print_error(err); + print_client_check_result(result); } break; - case '1': + case 'r': if (strlen(buffer) < 3) { - print_help(); + print_client_help(); break; } - buffer[ret - 1] = '\0'; - err = static_cast(ppm_popup_request(&buffer[2], popup_response_cb, (void *)id)); - print_error(err); + + buffer[strlen(buffer)] = '\0'; // erase new line + privilege = &buffer[2]; + + printf("sending localId: %d privilege: \"%s\"\n", clientRequestId, privilege); + + request = new ClientRequest{ clientRequestId++, privilege }; + err = static_cast(ppm_popup_request(privilege, popup_response_cb, request)); + if (err != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) { + delete request; + } + break; case 'q': - exit(0); + exit(EXIT_SUCCESS); case 'h': default: - print_help(); + print_client_help(); + return; + } + + print_client_error(err); + print_prompt(Mode::CLIENT); +} + +void print_server_help() +{ + printf("l list all connections\n"); + printf("s list all requests\n"); + printf("ra send ALLOW FOREVER response\n"); + printf("rd send DENY FOREVER response\n"); + printf("ro send DENY ONCE response\n"); + printf("c close connection\n"); + printf("h print help\n"); + printf("q quit\n"); + print_prompt(Mode::SERVER); +} + +void handle_server(const char *buffer, ServerSimulator *serverSimulator) +{ + int res; + ConnectionFd fd; + RequestId requestId; + + switch (buffer[0]) { + case 'l': + serverSimulator->showConnections(); + break; + case 's': + serverSimulator->showPrivilegeRequests(); + break; + case 'r': + res = sscanf(&buffer[2], "%d %d", &fd, &requestId); + if (res != 2) { + print_server_help(); + return; + } + switch (buffer[1]) { + case 'a': + serverSimulator->sendResponse(fd, requestId, ASKUSER_ALLOW_FOREVER); + break; + case 'd': + serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_FOREVER); + break; + case 'o': + serverSimulator->sendResponse(fd, requestId, ASKUSER_DENY_ONCE); + break; + default: + print_server_help(); + return; + } + break; + case 'c': + res = sscanf(&buffer[1], "%d", &fd); + if (res != 1) { + print_server_help(); + return; + } + serverSimulator->closeConnection(fd); + return; + case 'h': + print_server_help(); + return; + case 'q': + exit(EXIT_SUCCESS); + } + + print_prompt(Mode::SERVER); +} + +gboolean main_loop_cb(UNUSED GIOChannel *channel, UNUSED GIOCondition cond, gpointer user_data) +{ + char buffer[4096]; + + int ret = read(0, buffer, sizeof(buffer)); + if (ret <= 0) { + printf("read from stdin failed\n"); + exit(EXIT_FAILURE); + } + + buffer[ret - 1] = '\0'; + + AppContext *ctx = static_cast(user_data); + switch (ctx->m_mode) { + case Mode::CLIENT: + handle_client(buffer); break; + case Mode::SERVER: + handle_server(buffer, ctx->m_serverSimulator); + break; + case Mode::UNKNOWN: + default: + printf("Mode not set, exiting.\n"); + exit(EXIT_FAILURE); } return TRUE; } +void print_help(const char *programName) +{ + printf("Usage: %s [OPTIONS]\n", programName); + printf("Allows to test CAPI for Privacy Privilege Manager.\n"); + printf("This program can run in two modes: as a client and as a server.\n"); + printf("You can use it in two scenarios:\n"); + printf("1. Run as a client (%s -c) to trigger popups in the askuser-notification daemon.\n", programName); + printf("2. Run one part as a client (%s -c) and other one as a server (%s -s).\n", + programName, programName); + printf("If you want to run this program as a root you need to create /var/run/user_ext/0 folder.\n"); + printf("To run this program in context of some Tizen's application, type in the console:\n"); + printf("#echo > /proc/self/attr/current\n"); + printf("#su - \n\n"); + printf("Options:\n"); + printf("-s run as a server\n"); + printf("-c run as a client (uses CAPI)\n"); + printf("-h display this help end exit\n"); +} + int main(int argc, char **argv) { - (void) argc; (void) argv; + AppContext ctx; + int c; - srand(time(0)); - print_help(); + opterr = 0; + while ((c = getopt (argc, argv, "sch")) != -1) { + switch (c) { + case 's': + ctx.m_mode = Mode::SERVER; + print_server_help(); + break; + case 'c': + ctx.m_mode = Mode::CLIENT; + print_client_help(); + break; + case 'h': + default: + break; + } + } + + if (ctx.m_mode == Mode::UNKNOWN) { + print_help(argv[0]); + exit(EXIT_SUCCESS); + } + + try { + ctx.initialize(); + } + catch (...) { + printf("Unknwon exception occured, exiting.\n"); + exit(EXIT_FAILURE); + } - GMainLoop *mainloop = g_main_loop_new(NULL, FALSE); + GMainLoop *mainloop = g_main_loop_new(nullptr, FALSE); - GIOChannel *channel = g_io_channel_unix_new(0); - GIOCondition condition = static_cast(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); - g_io_add_watch(channel, condition, main_loop_cb, NULL); + GIOChannel *gio_channel = g_io_channel_unix_new(0); + g_io_add_watch(gio_channel, static_cast(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL), + main_loop_cb, &ctx); g_main_loop_run(mainloop); -- 2.34.1