Implement CAPI 69/136669/25
authorPiotr Sawicki <p.sawicki2@partner.samsung.com>
Fri, 30 Jun 2017 10:34:27 +0000 (12:34 +0200)
committerPiotr Sawicki <p.sawicki2@partner.samsung.com>
Thu, 6 Jul 2017 17:59:00 +0000 (19:59 +0200)
* Bind askuser-client API to the GLib main loop mechanism.
* Add simple test program.
* Add an unknown error code type to API

Change-Id: Icc04d7021003bc7547f2294920011ec46eaa0344

CMakeLists.txt
packaging/askuser-notification.spec
src/capi/CMakeLists.txt
src/capi/impl/privacy_privilege_manager.c
src/capi/include/privacy_privilege_manager.h
src/capi/test/privacy_privilege_manager_test.c [new file with mode: 0644]

index 429a8ac..1b81172 100644 (file)
@@ -85,6 +85,7 @@ SET(TARGET_ASKUSER_NOTIFICATION_LIB "askuser-notification-ipc")
 SET(TARGET_ASKUSER_NOTIFICATION_LIB_TEST "askuser-notification-test")
 SET(TARGET_ASKUSER_NOTIFICATION_CLIENT_LIB "askuser-notification-client")
 SET(TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB "capi-privacy-privilege-manager")
+SET(TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST "capi-privacy-privilege-manager-test")
 
 ADD_SUBDIRECTORY(src/plugin)
 ADD_SUBDIRECTORY(src/notification-daemon)
index f76aa0d..f0ac888 100644 (file)
@@ -105,6 +105,13 @@ Summary: Privacy Privilege Manager in TIZEN C API development files
 %description -n capi-privacy-privilege-manager-devel
 Privacy Privilege Manager in TIZEN C API development files
 
+%package -n capi-privacy-privilege-manager-test
+Summary: Tests for Privacy Privilege Manager in TIZEN C API
+Requires: capi-privacy-privilege-manager
+
+%description -n capi-privacy-privilege-manager-test
+Provides tests for checking the API provided by capi-privacy-privilege-manager
+
 %prep
 %setup -q
 cp -a %{SOURCE1001} .
@@ -223,3 +230,6 @@ systemctl restart cynara.service
 %{_libdir}/pkgconfig/capi-privacy-privilege-manager.pc
 %{_libdir}/libcapi-privacy-privilege-manager.so
 
+%files -n capi-privacy-privilege-manager-test
+%manifest default.manifest
+%attr(755,root,root) %{_bindir}/capi-privacy-privilege-manager-test
index 46736c9..4e2cca7 100644 (file)
@@ -34,7 +34,12 @@ SET(PRIVACY_PRIVILEGE_MANAGER_SOURCES
     ${PRIVACY_PRIVILEGE_MANAGER_PATH}/impl/privacy_privilege_manager.c
    )
 
+SET(PRIVACY_PRIVILEGE_MANAGER_SOURCES_TEST_SOURCES
+    ${PRIVACY_PRIVILEGE_MANAGER_PATH}/test/privacy_privilege_manager_test.c
+   )
+
 ADD_LIBRARY(${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB} SHARED ${PRIVACY_PRIVILEGE_MANAGER_SOURCES})
+ADD_EXECUTABLE(${TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST} ${PRIVACY_PRIVILEGE_MANAGER_SOURCES_TEST_SOURCES})
 
 SET_TARGET_PROPERTIES(${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB}
     PROPERTIES
@@ -51,6 +56,8 @@ TARGET_LINK_LIBRARIES(
     ${PRIVACY_PRIVILEGE_MANAGER_DEP_LIBRARIES}
 )
 
+TARGET_LINK_LIBRARIES(${TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST} ${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB})
+
 INSTALL(TARGETS     ${TARGET_PRIVACY_PRIVILEGE_MANAGER_CAPI_LIB}
         DESTINATION ${LIB_INSTALL_DIR})
 
@@ -58,3 +65,5 @@ FILE(GLOB HEADERS   ${PRIVACY_PRIVILEGE_MANAGER_PATH}/include/*.h)
 INSTALL(FILES       ${HEADERS}
         DESTINATION ${INCLUDE_INSTALL_DIR}/privacy-privilege-manager)
 
+INSTALL(TARGETS     ${TARGET_PRIVACY_PRIVILEGE_MANAGER_TEST}
+        DESTINATION ${BIN_INSTALL_DIR})
index cc96bf0..ff97b23 100644 (file)
@@ -18,7 +18,7 @@
  * @file        privacy_privilege_manager.c
  * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
  * @version     1.0
- * @brief       This file contains implementation of privacy_privilege_manager.h CAPI.
+ * @brief       The implementation of Privacy Privilege Manager CAPI.
  */
 
 #include <stdlib.h>
 
 #include <privacy_privilege_manager.h>
 
-askuser_client *client = NULL;
-struct ppm_private_struct {} *ppm_private = NULL;
+#define UNUSED __attribute__((unused))
 
+struct ppm_private_s {
+    askuser_client *client;
+    GIOChannel *channel;
+    GIOCondition condition;
+    guint watch_id;
+};
+typedef struct ppm_private_s ppm_private;
+
+struct ppm_callback_closure_s {
+    void *user_data;
+    ppm_popup_response_cb callback;
+};
+typedef struct ppm_callback_closure_s ppm_callback_closure;
+
+static ppm_private *ppm_handle = NULL;
+
+static void ppm_private_init(ppm_private *handle)
+{
+    handle->channel = NULL;
+    handle->condition = 0;
+    handle->watch_id = 0;
+}
 
 static ppm_error_e ask_user_to_ppm_error(int ask_error)
 {
@@ -40,7 +61,7 @@ static ppm_error_e ask_user_to_ppm_error(int ask_error)
             ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
                 break;
         case ASKUSER_API_UNKNOWN_ERROR:
-            ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR;
+            ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
             break;
         case ASKUSER_API_OUT_OF_MEMORY:
             ret = PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
@@ -58,7 +79,7 @@ static ppm_error_e ask_user_to_ppm_error(int ask_error)
     return ret;
 }
 
-static ppm_check_result_e ask_user_check_result_to_pps(askuser_check_result result)
+static ppm_check_result_e ask_user_check_result_to_ppm(askuser_check_result result)
 {
     switch (result) {
         case ASKUSER_CHECK_RESULT_ALLOW:
@@ -72,6 +93,33 @@ static ppm_check_result_e ask_user_check_result_to_pps(askuser_check_result resu
     return PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY;
 }
 
+static ppm_popup_result_e askuser_client_popup_result_to_ppm(askuser_popup_result result)
+{
+    switch (result) {
+        case ASKUSER_POPUP_RESULT_ALLOW_FOREVER:
+               return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER;
+        case ASKUSER_POPUP_RESULT_DENY_FOREVER:
+            return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER;
+        case ASKUSER_POPUP_RESULT_DENY_ONCE:
+            return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE;
+    }
+
+    return PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE;
+}
+
+static ppm_call_cause_e askuser_client_popup_cause_to_ppm(askuser_call_cause cause)
+{
+    switch (cause) {
+        case ASKUSER_CALL_CAUSE_ANSWER:
+            return PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER;
+        case ASKUSER_CALL_CAUSE_ERROR:
+        case ASKUSER_CALL_CAUSE_FINALIZE:
+            return PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR;
+    }
+
+    return ASKUSER_CALL_CAUSE_ERROR;
+}
+
 static int g_io_condition_to_askuser_events(GIOCondition cond)
 {
     return ((cond & G_IO_IN) ? ASKUSER_READ_EVENT : 0) |
@@ -84,32 +132,122 @@ static GIOCondition askuser_events_to_g_io_condition(int events)
            ((events & ASKUSER_WRITE_EVENT) ? G_IO_OUT : 0);
 }
 
+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;
+    ppm_private *handle = (ppm_private *) data;
+
+    fd = g_io_channel_unix_get_fd(src);
+    events = g_io_condition_to_askuser_events(cond);
+
+    (void) askuser_client_process(handle->client, fd,
+                                  ppm_error_condition(cond) ? ASKUSER_EMPTY_EVENTS : events);
+
+    return TRUE;
+}
+
 static void ask_status_callback(int fd, int events, void *p_user_data)
 {
-    (void) fd;
-    (void) events;
-    (void) p_user_data;
-    g_io_condition_to_askuser_events(0);
-    askuser_events_to_g_io_condition(0);
-    // TODO register GIOChannel and GIOSource
+    ppm_private *handle = (ppm_private *) 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);
+        }
+        return;
+    }
+
+    if (!ppm_is_connected(handle)) {
+        handle->condition = gio_condition;
+        handle->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);
 
-    // TODO register callback
+        handle->watch_id = g_io_add_watch(handle->channel,
+                                          handle->condition | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                          ppm_gio_cb,
+                                          handle);
+        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,
+                                          ppm_gio_cb,
+                                          handle);
+    }
+}
+
+static void ppm_popup_response_callback(UNUSED int request_id, askuser_call_cause cause,
+                                        askuser_popup_result result, void *p_user_data)
+{
+    ppm_callback_closure *callback_closure = (ppm_callback_closure *) p_user_data;
+
+    /* Don't invoke callback while the application is finishing. The user data
+     * may already have been destroyed.
+     */
+    if (cause == ASKUSER_CALL_CAUSE_FINALIZE) {
+        free(callback_closure);
+        return;
+    }
+
+    ppm_call_cause_e ppm_cause = askuser_client_popup_cause_to_ppm(cause);
+    ppm_check_result_e ppm_result = askuser_client_popup_result_to_ppm(result);
+
+    callback_closure->callback(ppm_cause, ppm_result, callback_closure->user_data);
+
+    free(callback_closure);
+}
+
+static void ppm_free_client()
+{
+    if (ppm_handle != NULL) {
+        askuser_client_finalize(ppm_handle->client);
+        free(ppm_handle);
+        ppm_handle = NULL;
+    }
 }
 
 static int ppm_init_client()
 {
-    if (ppm_private == NULL) {
-        ppm_private = (struct ppm_private_struct *) malloc(sizeof(struct ppm_private_struct));
-        if (ppm_private == NULL) {
+    if (ppm_handle == NULL) {
+        ppm_handle = (ppm_private *) calloc(1, sizeof(ppm_private));
+        if (ppm_handle == NULL) {
             return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
         }
 
-        int ret = askuser_client_initialize(&client, ask_status_callback, ppm_private);
+        ppm_private_init(ppm_handle);
+
+        int ret = askuser_client_initialize(&ppm_handle->client, ask_status_callback, ppm_handle);
         if (ret != ASKUSER_API_SUCCESS) {
-            free(ppm_private);
-            ppm_private = NULL;
+            free(ppm_handle);
+            ppm_handle = NULL;
             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;
+            return PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN;
+        }
     }
 
     return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
@@ -118,8 +256,9 @@ static int ppm_init_client()
 EXPORT_API
 int ppm_check_privilege(const char *privilege, ppm_check_result_e *result)
 {
-    if (!privilege || !result)
+    if (!privilege || !result) {
         return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
+    }
 
     int ret = ppm_init_client();
     if (ret != PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
@@ -127,36 +266,18 @@ int ppm_check_privilege(const char *privilege, ppm_check_result_e *result)
     }
 
     askuser_check_result check_result;
-    ret = askuser_client_check_privilege(client, privilege, &check_result);
+    ret = askuser_client_check_privilege(ppm_handle->client, privilege, &check_result);
     if (ret != ASKUSER_API_SUCCESS) {
         return ask_user_to_ppm_error(ret);
     }
 
-    *result = ask_user_check_result_to_pps(check_result);
+    *result = ask_user_check_result_to_ppm(check_result);
 
     return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
 }
 
-static void ppm_popup_response_callback(int request_id, askuser_call_cause cause,
-                                        askuser_popup_result result, void *p_user_data)
-{
-    (void) request_id;
-    (void) cause;
-    (void) result;
-    (void) p_user_data;
-
-    // TODO find callback (request_id) and user_data
-
-    // TODO convert cause and result
-
-    // TODO call callback
-
-    // TODO unregister callback and user_data
-}
-
 EXPORT_API
-int ppm_popup_request(const char *privilege, ppm_popup_response_cb callback,
-                      void *user_data)
+int ppm_popup_request(const char *privilege, ppm_popup_response_cb callback, void *user_data)
 {
     if (!privilege || !callback) {
         return PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER;
@@ -167,16 +288,21 @@ int ppm_popup_request(const char *privilege, ppm_popup_response_cb callback,
         return ret;
     }
 
-    int request_id;
-    ret = askuser_client_popup_request(client, privilege, ppm_popup_response_callback,
-        ppm_private, &request_id);
+    ppm_callback_closure *callback_closure = (ppm_callback_closure *) calloc(1, sizeof(ppm_callback_closure));
+    if (callback_closure == NULL) {
+        return PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY;
+    }
+
+    callback_closure->callback = callback;
+    callback_closure->user_data = user_data;
+
+    ret = askuser_client_popup_request(ppm_handle->client, privilege, ppm_popup_response_callback,
+                                       callback_closure, NULL);
     if (ret != ASKUSER_API_SUCCESS) {
+        free(callback_closure);
         return ask_user_to_ppm_error(ret);
     }
 
-    // TODO store callback and user_data under request_id key
-    (void) callback;
-    (void) user_data;
-
     return PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE;
 }
+
index da5a7c0..780a76a 100644 (file)
@@ -51,6 +51,8 @@ typedef enum
     PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER    = TIZEN_ERROR_INVALID_PARAMETER,
     /** Out of memory */
     PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY        = TIZEN_ERROR_OUT_OF_MEMORY,
+    /** Unknown error */
+    PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN              = TIZEN_ERROR_UNKNOWN,
 } ppm_error_e;
 
 /**
@@ -136,6 +138,7 @@ typedef void (*ppm_popup_response_cb) (ppm_call_cause_e cause,
  * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR           I/O error
  * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER  Invalid parameter
  * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY      Out of memory
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN            Unknown error
  */
 int ppm_check_privilege(const char *privilege, ppm_check_result_e *result);
 
@@ -179,6 +182,7 @@ int ppm_check_privilege(const char *privilege, ppm_check_result_e *result);
  * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR           I/O error
  * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER  Invalid parameter
  * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY      Out of memory
+ * @retval #PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN            Unknown error
  *
  * @post ppm_popup_response_cb() will be invoked.
  * @see ppm_popup_response_cb()
diff --git a/src/capi/test/privacy_privilege_manager_test.c b/src/capi/test/privacy_privilege_manager_test.c
new file mode 100644 (file)
index 0000000..c37c62e
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ *  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        privacy_privilege_manager.c
+ * @author      Piotr Sawicki <p.sawicki2@partner.samsung.com>
+ * @version     1.0
+ * @brief       This file contains test of CAPI for Privacy Privilege Manager
+ */
+
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <privacy_privilege_manager.h>
+
+const char *result_to_str(ppm_popup_result_e result)
+{
+    switch (result) {
+        case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_ALLOW_FOREVER:
+            return "ALLOW_FOREVER";
+        case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_FOREVER:
+            return "DENY_FOREVER";
+        case PRIVACY_PRIVILEGE_MANAGER_POPUP_RESULT_DENY_ONCE:
+            return "DENY_ONCE";
+    }
+
+    return "UNKNOWN";
+}
+
+void popup_response_cb(ppm_call_cause_e cause, ppm_popup_result_e result, void *user_data)
+{
+    int id = (int) user_data;
+
+    printf("resp_id: %d ", id);
+    switch (cause) {
+        case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ANSWER:
+            printf("popup answer: %s\n", result_to_str(result));
+            break;
+        case PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR:
+            printf("popup cause PRIVACY_PRIVILEGE_MANAGER_CALL_CAUSE_ERROR\n");
+            break;
+    }
+
+    printf(">");
+    fflush(stdout);
+}
+
+static void print_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);
+}
+
+static void print_error(ppm_error_e error)
+{
+    switch (error) {
+        case PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE:
+            break;
+        case PRIVACY_PRIVILEGE_MANAGER_ERROR_IO_ERROR:
+            printf("I/O error\n");
+            break;
+        case  PRIVACY_PRIVILEGE_MANAGER_ERROR_INVALID_PARAMETER:
+            printf("Invalid parameters\n");
+            break;
+        case PRIVACY_PRIVILEGE_MANAGER_ERROR_OUT_OF_MEMORY:
+            printf("Out of memory\n");
+        case PRIVACY_PRIVILEGE_MANAGER_ERROR_UNKNOWN:
+            printf("Unknown error\n");
+            break;
+    }
+}
+
+static void print_check_result(ppm_check_result_e result)
+{
+    printf("res: ");
+
+    switch (result) {
+        case  PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ALLOW:
+            printf("ALLOW\n");
+            break;
+        case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_DENY:
+            printf("DENY\n");
+            break;
+        case PRIVACY_PRIVILEGE_MANAGER_CHECK_RESULT_ASK:
+            printf("ASK\n");
+            break;
+    }
+
+    printf(">");
+    fflush(stdout);
+}
+
+gboolean main_loop_cb(GIOChannel *channel, GIOCondition cond, gpointer user_data)
+{
+    int ret;
+    char buffer[4096];
+    ppm_check_result_e result;
+
+    (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;
+
+    switch (buffer[0]) {
+        case '0':
+            if (strlen(buffer) < 3) {
+                print_help();
+                break;
+            }
+            ret = ppm_check_privilege(&buffer[2], &result);
+            if (ret == PRIVACY_PRIVILEGE_MANAGER_ERROR_NONE) {
+                print_check_result(result);
+            }
+            else {
+                print_error(ret);
+            }
+            break;
+        case '1':
+            if (strlen(buffer) < 3) {
+                print_help();
+                break;
+            }
+            buffer[ret - 1] = '\0';
+            print_error(ppm_popup_request(&buffer[2], popup_response_cb, (void *)id));
+            break;
+        case 'q':
+            exit(0);
+        case 'h':
+        default:
+            print_help();
+            break;
+    }
+
+    return TRUE;
+}
+
+int main(int argc, char **argv)
+{
+    (void) argc; (void) argv;
+
+    srand(time(0));
+    print_help();
+
+    GMainLoop *mainloop = g_main_loop_new(NULL, FALSE);
+
+    GIOChannel *channel = g_io_channel_unix_new(0);
+    g_io_add_watch(channel, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, main_loop_cb, NULL);
+
+    g_main_loop_run(mainloop);
+
+    return 0;
+}