Implement app to test CAPI and simulate server side 43/137743/7
authorPiotr Sawicki <p.sawicki2@partner.samsung.com>
Thu, 6 Jul 2017 15:08:16 +0000 (17:08 +0200)
committerZofia Abramowska <z.abramowska@samsung.com>
Wed, 12 Jul 2017 14:37:48 +0000 (16:37 +0200)
* Add more detailed help
* Add switches for working as client or service

Change-Id: Iaef748054279cafc44f763be036d8c26db3e671d

src/capi/CMakeLists.txt
src/capi/test/privacy_privilege_manager_test.cpp

index ee7ac8a..70cf037 100644 (file)
@@ -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
index d9768d7..a596f38 100644 (file)
  * @brief       This file contains test of CAPI for Privacy Privilege Manager
  */
 
-#include <glib.h>
+#include <iostream>
+#include <map>
+#include <set>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
+#include <glib.h>
 #include <unistd.h>
 
+#include <attributes/attributes.h>
+#include <askuser-notification/ask-user-client-channel.h>
+#include <askuser-notification/ask-user-server-channel.h>
+#include <askuser-notification/ask-user-types.h>
 #include <privacy_privilege_manager.h>
 
-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<ServerPrivilegeRequest> 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<GIOCondition>(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<GIOCondition>(ret);
+}
+
+GIOCondition makeCondition(GIOCondition condition)
+{
+    return static_cast<GIOCondition>(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<ServerSimulator *>(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<ConnectionFd, Connection> m_connections;
+    std::map<ConnectionFd,  PrivilegeRequestSet> 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<ClientRequest *>(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 <privilege> - check privilege status\n");
-    printf("1 <privilege> - send popup request\n");
-    printf("h - print help\n");
-    printf("q - quit\n");
-    printf(">");
-    fflush(stdout);
+    printf("c <privilege>   check privilege status\n");
+    printf("r <privilege>   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_error_e>(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_error_e>(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_error_e>(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 <fd> <request id>    send ALLOW FOREVER response\n");
+    printf("rd <fd> <request id>    send DENY FOREVER response\n");
+    printf("ro <fd> <request id>    send DENY ONCE response\n");
+    printf("c  <fd>                 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<AppContext *>(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 <SMACK label of the application> > /proc/self/attr/current\n");
+    printf("#su - <user name>\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<GIOCondition>(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<GIOCondition>(G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+                   main_loop_cb, &ctx);
 
     g_main_loop_run(mainloop);