tinyds: add 'protocol_trace' feature 81/281781/1
authorduna.oh <duna.oh@samsung.com>
Fri, 16 Sep 2022 04:55:03 +0000 (13:55 +0900)
committerTizen Window System <tizen.windowsystem@gmail.com>
Thu, 22 Sep 2022 02:00:38 +0000 (11:00 +0900)
1. add rules in rule file
ALLOW iface=wl_pointer and msg=motion
ALLOW iface=wl_seat and type=event

2. set environment variables (or default path is given)
$export DS_PROTOCOL_RULE_FILE=/tmp/rule
$export DS_PROTOCOL_TRACE_FILE=/tmp/trace

3. will see protocol logs in trace file
$tail -f /tmp/trace
[1499148.714] Server->Client [PID:25430]wl_seat@7.capabilities(7), cmd: ds-simple-tbm
[1499148.810] Server->Client [PID:25430]wl_seat@7.name("seat0"), cmd: ds-simple-tbm
[1499155.391] Server   [PID:25430] client destroying

Change-Id: I9fff75555d002fa0c25a5f3d61ce31c8d7c0886b

examples/meson.build
examples/protocol-trace.c [new file with mode: 0644]
examples/protocol-trace.h [new file with mode: 0644]
examples/tinyds-tdm.c

index 6a81cbf..dbda372 100644 (file)
@@ -20,6 +20,7 @@ tinyds_tdm_files = [
   'pixman-helper.c',
   'pixman-tbm-helper.c',
   'tinyds-tdm-renderer.c',
+  'protocol-trace.c',
 ]
 
 executable('tinyds-tdm',
diff --git a/examples/protocol-trace.c b/examples/protocol-trace.c
new file mode 100644 (file)
index 0000000..d57e3c2
--- /dev/null
@@ -0,0 +1,1603 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libds/log.h>
+#include "protocol-trace.h"
+
+#define PATH_MAX 512
+#define MAX_RULE 64
+#define STRING_MAX 64
+
+#define BUF_SNPRINTF(fmt, ARG...) do { \
+    str_l = snprintf(str_buff, str_r, fmt, ##ARG); \
+    str_buff += str_l; \
+    str_r -= str_l; \
+} while (0)
+
+#ifndef REPLY
+    #define REPLY(fmt, ARG...)  \
+        do { \
+            if (reply && len && *len > 0) { \
+                int s = snprintf(reply, *len, fmt, ##ARG); \
+                reply += s; \
+                *len -= s; \
+            } \
+        } while (0)
+#endif
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+struct protocol_trace_tree_node
+{
+    struct protocol_trace_tree_node *left;
+    struct protocol_trace_tree_node *right;
+};
+
+struct protocol_trace_tree
+{
+    int size;
+    struct protocol_trace_tree_node *head;
+};
+
+//enums for protocol_trace_policy_type
+enum protocol_trace_policy_type
+{
+    PROTOCOL_POLICY_TYPE_UNDEFINED,
+    PROTOCOL_POLICY_TYPE_ALLOW,
+    PROTOCOL_POLICY_TYPE_DENY
+};
+
+struct protocol_trace_rule
+{
+    enum protocol_trace_policy_type policy;
+    struct protocol_trace_tree *tree;
+};
+
+struct protocol_trace_rule_checker
+{
+    struct protocol_trace_rule rules[MAX_RULE];
+    int count;
+};
+
+//enums for protocol_trace_protocol_log
+enum protocol_trace_protocol_type {
+    PROTOCOL_TYPE_REQUEST,
+    PROTOCOL_TYPE_EVENT
+};
+
+struct protocol_trace_protocol_log
+{
+    enum protocol_trace_protocol_type type;
+    int client_pid;
+    int target_id;
+    char name[PATH_MAX + 1];
+    char cmd[PATH_MAX + 1];
+};
+
+//enums for protocol_trace_rule_node
+enum protocol_trace_result_type
+{
+    PROTOCOL_TRACE_RESULT_UNKNOWN,
+    PROTOCOL_TRACE_RESULT_TRUE,
+    PROTOCOL_TRACE_RESULT_FALSE
+};
+
+enum protocol_trace_node_type
+{
+    PROTOCOL_TRACE_NODE_TYPE_NONE,
+    PROTOCOL_TRACE_NODE_TYPE_AND,
+    PROTOCOL_TRACE_NODE_TYPE_OR,
+    PROTOCOL_TRACE_NODE_TYPE_DATA,
+    PROTOCOL_TRACE_NODE_TYPE_ALL
+};
+
+enum protocol_trace_comparer
+{
+    PROTOCOL_TRACE_COMPARER_EQUAL,
+    PROTOCOL_TRACE_COMPARER_LESS,
+    PROTOCOL_TRACE_COMPARER_GREATER,
+    PROTOCOL_TRACE_COMPARER_LESS_EQ,
+    PROTOCOL_TRACE_COMPARER_GREATE_EQ,
+    PROTOCOL_TRACE_COMPARER_NOT_EQ
+};
+
+enum protocol_trace_data_type
+{
+    PROTOCOL_TRACE_DATA_TYPE_INTEGER,
+    PROTOCOL_TRACE_DATA_TYPE_STRING
+};
+
+struct protocol_trace_rule_node
+{
+    enum protocol_trace_node_type node_type;
+    enum protocol_trace_comparer comparer;
+    enum protocol_trace_data_type value_type;
+    enum protocol_trace_result_type result;
+    char variable_name[STRING_MAX];
+
+    union
+    {
+        char string[STRING_MAX];
+        int integer;
+    }value;
+};
+
+//enums for protocol_trace_token_data
+enum protocol_trace_token
+{
+    PROTOCOL_TRACE_TOKEN_UNKNOWN,
+    PROTOCOL_TRACE_TOKEN_L_BR,
+    PROTOCOL_TRACE_TOKEN_R_BR,
+    PROTOCOL_TRACE_TOKEN_NOT_EQ,
+    PROTOCOL_TRACE_TOKEN_EQUAL,
+    PROTOCOL_TRACE_TOKEN_LSS_THAN,
+    PROTOCOL_TRACE_TOKEN_LSS_EQ,
+    PROTOCOL_TRACE_TOKEN_GRT_THAN,
+    PROTOCOL_TRACE_TOKEN_GRT_EQ,
+    PROTOCOL_TRACE_TOKEN_AND,
+    PROTOCOL_TRACE_TOKEN_OR,
+    PROTOCOL_TRACE_TOKEN_SPACE,
+    PROTOCOL_TRACE_TOKEN_SYMBOL,
+    PROTOCOL_TRACE_TOKEN_NUMBER,
+    PROTOCOL_TRACE_TOKEN_EOS,
+};
+
+struct protocol_trace_token_data
+{
+    const char **string;
+    enum protocol_trace_token last_token;
+    const char *last_symbol;
+    int symbol_len;
+};
+
+struct protocol_trace_validate_args
+{
+    int type;
+    int target_id;
+    const char *name;
+    int pid;
+    const char *cmd;
+};
+
+struct argument_details {
+    char type;
+    int nullable;
+};
+
+struct protocol_trace_reply_buffer
+{
+    char **reply;
+    int *len;
+};
+
+enum protocol_trace_rule_set_result
+{
+    PROTOCOL_TRACE_RULE_SET_OK,
+    PROTOCOL_TRACE_RULE_SET_ERR_TOO_MANY_RULES,
+    PROTOCOL_TRACE_RULE_SET_ERR_PARSE,
+    PROTOCOL_TRACE_RULE_SET_ERR_NO_RULE
+};
+
+typedef int (*tree_traverse_cb) (struct protocol_trace_tree *tree,
+        struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *parent,
+        void *arg);
+static struct protocol_trace_tree_node *parser_parse_token(
+        struct protocol_trace_tree *tree,
+        struct protocol_trace_token_data *token);
+static bool rule_set(const int argc, const char **argv, char *reply, int *len);
+
+char trace_env_path[PATH_MAX + 1];
+static FILE *log_fp_ptrace = NULL;
+static struct wl_protocol_logger *ds_wl_protocol_logger;
+static struct protocol_trace_rule_checker *rc = NULL;
+static struct wl_display *display;
+
+static struct
+{
+    const char *token_char;
+    const int token_length;
+    enum protocol_trace_token token_name;
+} token_table[] =
+{
+    {"\0",  1, PROTOCOL_TRACE_TOKEN_EOS},
+    {"\t",  1, PROTOCOL_TRACE_TOKEN_SPACE},
+    {" ",   1, PROTOCOL_TRACE_TOKEN_SPACE},
+    {"!=",  2, PROTOCOL_TRACE_TOKEN_NOT_EQ},
+    {"&",   1, PROTOCOL_TRACE_TOKEN_AND},
+    {"&&",  2, PROTOCOL_TRACE_TOKEN_AND},
+    {"(",   1, PROTOCOL_TRACE_TOKEN_L_BR},
+    {")",   1, PROTOCOL_TRACE_TOKEN_R_BR},
+    {"<",   1, PROTOCOL_TRACE_TOKEN_LSS_THAN},
+    {"<=",  2, PROTOCOL_TRACE_TOKEN_LSS_EQ},
+    {"<>",  2, PROTOCOL_TRACE_TOKEN_NOT_EQ},
+    {"=",   1, PROTOCOL_TRACE_TOKEN_EQUAL},
+    {"==",  2, PROTOCOL_TRACE_TOKEN_EQUAL},
+    {">",   1, PROTOCOL_TRACE_TOKEN_GRT_THAN},
+    {">=",  2, PROTOCOL_TRACE_TOKEN_GRT_EQ},
+    {"and", 3, PROTOCOL_TRACE_TOKEN_AND},
+    {"or",  2, PROTOCOL_TRACE_TOKEN_OR},
+    {"|",   1, PROTOCOL_TRACE_TOKEN_OR},
+    {"||",  2, PROTOCOL_TRACE_TOKEN_OR},
+};
+
+static struct protocol_trace_tree_node *
+bintree_get_head(struct protocol_trace_tree *tree)
+{
+    return tree->head;
+}
+
+static void
+bintree_set_head(struct protocol_trace_tree *tree,
+        struct protocol_trace_tree_node *head)
+{
+    tree->head = head;
+}
+
+static struct protocol_trace_tree_node *
+bintree_get_left_child(struct protocol_trace_tree_node *node)
+{
+    return node->left;
+}
+
+static void
+bintree_set_left_child(struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *child)
+{
+    node->left = child;
+}
+
+static struct protocol_trace_tree_node *
+bintree_get_right_child(struct protocol_trace_tree_node *node)
+{
+    return node->right;
+}
+
+static void
+bintree_set_right_child(struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *child)
+{
+    node->right = child;
+}
+
+static void *
+bintree_get_node_data(struct protocol_trace_tree_node *node)
+{
+    return (void*)(node+1);
+}
+
+static int
+bintree_inorder_traverse_recursive(struct protocol_trace_tree *tree,
+        struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *parent,
+        tree_traverse_cb func, void *arg)
+{
+    if (node->left) {
+        if (bintree_inorder_traverse_recursive(tree, node->left, node,
+                 func, arg) != 0)
+            return 1;
+    }
+
+    if (func(tree, node, parent, arg))
+        return 1;
+
+    if (node->right) {
+        if (bintree_inorder_traverse_recursive(tree, node->right, node,
+                func, arg) != 0)
+            return 1;
+    }
+
+    return 0;
+}
+
+static void
+bintree_inorder_traverse(struct protocol_trace_tree *tree,
+        tree_traverse_cb func, void *arg)
+{
+    if (tree->head)
+        bintree_inorder_traverse_recursive(tree, tree->head, tree->head,
+                func, arg);
+}
+
+static int
+bintree_postorder_traverse_recursive(struct protocol_trace_tree *tree,
+        struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *parent,
+        tree_traverse_cb func, void *arg)
+{
+    if (node->left) {
+        if (bintree_postorder_traverse_recursive(tree, node->left, node,
+                func, arg) != 0)
+            return 1;
+    }
+    if (node->right) {
+        if (bintree_postorder_traverse_recursive(tree, node->right, node,
+                func, arg) != 0)
+            return 1;
+    }
+
+    return func(tree, node,parent, arg);
+}
+
+static void
+bintree_postorder_traverse(struct protocol_trace_tree *tree,
+        tree_traverse_cb func, void *arg)
+{
+    if (tree->head)
+        bintree_postorder_traverse_recursive(tree, tree->head, tree->head,
+                func, arg);
+}
+
+static struct protocol_trace_tree *
+bintree_create_tree(int size)
+{
+    struct protocol_trace_tree *tree;
+
+    tree = calloc(1, sizeof(struct protocol_trace_tree) + size);
+    if (!tree)
+        return NULL;
+
+    tree->size = size;
+    tree->head = NULL;
+
+    return tree;
+}
+static struct protocol_trace_tree_node *
+bintree_create_node(struct protocol_trace_tree *tree)
+{
+    struct protocol_trace_tree_node *node;
+
+    node = calloc(1, sizeof(struct protocol_trace_tree_node) + tree->size);
+    if (!node)
+        return NULL;
+
+    node->left = NULL;
+    node->right = NULL;
+
+    return node;
+}
+
+static void
+bintree_remove_node(struct protocol_trace_tree_node *node)
+{
+    free(node);
+}
+
+static void
+bintree_remove_node_recursive(struct protocol_trace_tree_node *node)
+{
+    if (node->left)
+        bintree_remove_node_recursive(node->left);
+    if (node->right)
+        bintree_remove_node_recursive(node->right);
+
+    bintree_remove_node(node);
+}
+
+static void
+bintree_destroy_tree(struct protocol_trace_tree *tree)
+{
+    if (tree->head)
+        bintree_remove_node_recursive(tree->head);
+    free(tree);
+}
+
+static int
+rulechecker_string_compare(enum protocol_trace_comparer comparer,
+        char *str2, const char *str1)
+{
+    int result;
+
+    result = strcasecmp(str2, str1);
+
+    switch (comparer) {
+        case PROTOCOL_TRACE_COMPARER_EQUAL:
+            return result == 0;
+        case PROTOCOL_TRACE_COMPARER_LESS:
+            return result < 0;
+        case PROTOCOL_TRACE_COMPARER_GREATER:
+            return result > 0;
+        case PROTOCOL_TRACE_COMPARER_LESS_EQ:
+            return result <= 0;
+        case PROTOCOL_TRACE_COMPARER_GREATE_EQ:
+            return result >= 0;
+        case PROTOCOL_TRACE_COMPARER_NOT_EQ:
+            return result != 0;
+    }
+
+    return 0;
+}
+
+static int
+rulechecker_int_compare(enum protocol_trace_comparer comparer,
+        int int2, int int1)
+{
+    switch (comparer) {
+        case PROTOCOL_TRACE_COMPARER_EQUAL:
+            return int1 == int2;
+        case PROTOCOL_TRACE_COMPARER_LESS:
+            return int1 < int2;
+        case PROTOCOL_TRACE_COMPARER_GREATER:
+            return int1 > int2;
+        case PROTOCOL_TRACE_COMPARER_LESS_EQ:
+            return int1 <= int2;
+        case PROTOCOL_TRACE_COMPARER_GREATE_EQ:
+            return int1 >= int2;
+        case PROTOCOL_TRACE_COMPARER_NOT_EQ:
+            return int1 != int2;
+    }
+
+    return 0;
+}
+
+static int
+rulechecker_validate_rule_func(struct protocol_trace_tree *tree,
+        struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *parent,
+        void *arg)
+{
+    struct protocol_trace_validate_args *args;
+    struct protocol_trace_tree_node *left, *right;
+    struct protocol_trace_rule_node *data, *left_data, *right_data;
+
+    args = (struct protocol_trace_validate_args *)arg;
+    data = (struct protocol_trace_rule_node *)bintree_get_node_data(node);
+    data->result = PROTOCOL_TRACE_RESULT_UNKNOWN;
+
+    if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_AND ||
+        data->node_type == PROTOCOL_TRACE_NODE_TYPE_OR) {
+        left = bintree_get_left_child(node);
+        right = bintree_get_right_child(node);
+        if (!left || !right) {
+            ds_err("Node error");
+            return -1;
+        }
+
+        left_data = (struct protocol_trace_rule_node *)bintree_get_node_data(left);
+        right_data = (struct protocol_trace_rule_node *)bintree_get_node_data(right);
+    }
+
+    if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_ALL) {
+        data->result = PROTOCOL_TRACE_RESULT_TRUE;
+    } else if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_DATA) {
+        char iface[64] = {0,};
+        char *msg = NULL;
+
+        if (args->name) {
+            msg = (char *) strchr(args->name, ':');
+        }
+        if (msg) {
+            int size_iface = sizeof(iface) -1;
+            int min = MIN(size_iface, msg-args->name);
+            strncpy(iface, args->name, min);
+            iface[min] = '\0';
+            msg++;
+        }
+        if (!strcasecmp(data->variable_name, "TYPE")) {
+            const char *type_string;
+            if (args->type == 0)
+                type_string = "REQUEST";
+            else if (args->type == 1)
+                type_string = "EVENT";
+            else {
+                ds_err("Invalid type %d", args->type);
+                return -1;
+            }
+
+            if (rulechecker_string_compare(data->comparer,
+                    data->value.string, type_string))
+                data->result = PROTOCOL_TRACE_RESULT_TRUE;
+            else
+                data->result = PROTOCOL_TRACE_RESULT_FALSE;
+        } else if (!strcasecmp(data->variable_name, "IFACE")) {
+            if (msg && iface[0] &&
+                rulechecker_string_compare(data->comparer,
+                        data->value.string, (const char *)iface))
+                data->result = PROTOCOL_TRACE_RESULT_TRUE;
+            else
+                data->result = PROTOCOL_TRACE_RESULT_FALSE;
+        } else if (!strcasecmp(data->variable_name, "MSG")) {
+            if (msg &&
+                rulechecker_string_compare(data->comparer,
+                        data->value.string, msg))
+                data->result = PROTOCOL_TRACE_RESULT_TRUE;
+            else
+                data->result = PROTOCOL_TRACE_RESULT_FALSE;
+        } else if (!strcasecmp(data->variable_name, "PID")) {
+            if (rulechecker_int_compare(data->comparer,
+                    data->value.integer, args->pid))
+                data->result = PROTOCOL_TRACE_RESULT_TRUE;
+            else
+                data->result = PROTOCOL_TRACE_RESULT_FALSE;
+        } else if (!strcasecmp(data->variable_name, "CMD") ||
+            !strcasecmp(data->variable_name, "COMMAND")) {
+            if (msg &&
+                rulechecker_string_compare(data->comparer,
+                        data->value.string, args->cmd))
+                data->result = PROTOCOL_TRACE_RESULT_TRUE;
+            else
+                data->result = PROTOCOL_TRACE_RESULT_FALSE;
+        }
+    } else if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_AND) {
+        if (left_data->result == PROTOCOL_TRACE_RESULT_TRUE &&
+            right_data->result == PROTOCOL_TRACE_RESULT_TRUE)
+            data->result = PROTOCOL_TRACE_RESULT_TRUE;
+        else
+            data->result = PROTOCOL_TRACE_RESULT_FALSE;
+    } else if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_OR) {
+        if (left_data->result == PROTOCOL_TRACE_RESULT_TRUE ||
+            right_data->result == PROTOCOL_TRACE_RESULT_TRUE)
+            data->result = PROTOCOL_TRACE_RESULT_TRUE;
+        else
+            data->result = PROTOCOL_TRACE_RESULT_FALSE;
+    } else {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+rulechecker_validate_rules(struct protocol_trace_rule_checker *rc,
+        int type, int target_id, const char *name, int pid, const char *cmd)
+{
+    struct protocol_trace_validate_args args = {type, target_id, name, pid, cmd};
+    struct protocol_trace_tree_node *node;
+    struct protocol_trace_rule_node *data;
+    enum protocol_trace_policy_type default_policy;
+
+    default_policy = PROTOCOL_POLICY_TYPE_DENY;
+
+    for (int i = rc->count - 1; i >= 0 ; i--) {
+        bintree_postorder_traverse(rc->rules[i].tree, rulechecker_validate_rule_func, &args);
+        node = (struct protocol_trace_tree_node *)bintree_get_head(rc->rules[i].tree);
+        data = (struct protocol_trace_rule_node *)bintree_get_node_data(node);
+
+        if (data->result == PROTOCOL_TRACE_RESULT_TRUE) {
+            return rc->rules[i].policy == PROTOCOL_POLICY_TYPE_ALLOW;
+        }
+    }
+
+    return default_policy == PROTOCOL_POLICY_TYPE_ALLOW;
+}
+
+static char *
+logger_cmd_get(char *path)
+{
+    char *p;
+
+    if (!path) return NULL;
+
+    p = strrchr(path, '/');
+
+    return (p) ? p+1 : path;
+}
+
+static bool
+logger_validate_rule(struct protocol_trace_protocol_log *log)
+{
+    const char *cmd = "";
+    int ret;
+
+    if (!rc)
+        return false;
+
+    cmd = logger_cmd_get(log->cmd);
+
+    ret = rulechecker_validate_rules(rc, log->type, log->target_id, log->name,
+            log->client_pid, cmd);
+
+    return ret;
+}
+
+static void
+logger_get_proc_name(pid_t pid, char *name, int size)
+{
+    FILE *h;
+    char proc[PATH_MAX], pname[PATH_MAX];
+    size_t len;
+
+    if (!name) return;
+
+    snprintf(proc, PATH_MAX, "/proc/%d/cmdline", pid);
+
+    h = fopen(proc, "r");
+    if (!h) return;
+
+    len = fread(pname, sizeof(char), PATH_MAX, h);
+    if (len > 0)
+        pname[len - 1]='\0';
+    else
+        strncpy(pname, "NO NAME", sizeof(pname));
+
+    fclose(h);
+
+    strncpy(name, pname, size);
+}
+
+static const char *
+logger_get_next_argument(const char *signature, struct argument_details *details)
+{
+    details->nullable = 0;
+
+    for (; *signature; ++signature) {
+        switch (*signature) {
+            case 'i':
+            case 'u':
+            case 'f':
+            case 's':
+            case 'o':
+            case 'n':
+            case 'a':
+            case 'h':
+                details->type = *signature;
+                return signature + 1;
+            case '?':
+                details->nullable = 1;
+        }
+    }
+    details->type = '\0';
+
+    return signature;
+}
+
+static void
+logger_handle_client_destroy(struct wl_listener *listener, void *data)
+{
+    struct wl_client *wc = (struct wl_client *)data;
+    struct timespec tp;
+    unsigned int time;
+    pid_t client_pid =-1;
+
+    char strbuf[PATH_MAX], *str_buff = strbuf;
+    int str_r, str_l;
+
+    str_buff[0] = '\0';
+    str_r = sizeof(strbuf);
+
+    wl_client_get_credentials(wc, &client_pid, NULL, NULL);
+
+    clock_gettime(CLOCK_MONOTONIC, &tp);
+    time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+    BUF_SNPRINTF("[%10.3f] Server   [PID:%d] client destroying", time / 1000.0,
+            client_pid);
+
+    if (log_fp_ptrace)
+        fprintf(log_fp_ptrace, "%s\n", strbuf);
+    else
+        ds_dbg("%s", strbuf);
+
+    wl_list_remove(&listener->link);
+    free(listener);
+    listener = NULL;
+}
+
+static void
+logger_add_client_destroy_listener(struct wl_client *client)
+{
+    struct wl_listener *destroy_listener;
+
+    destroy_listener = wl_client_get_destroy_listener(client,
+            logger_handle_client_destroy);
+    if (destroy_listener)
+        return;
+
+    destroy_listener = calloc(1, sizeof *destroy_listener);
+    if (!destroy_listener)
+        return;
+
+    destroy_listener->notify = logger_handle_client_destroy;
+    wl_client_add_destroy_listener(client, destroy_listener);
+}
+
+void
+logger_func(void *user_data, enum wl_protocol_logger_type direction,
+        const struct wl_protocol_logger_message *message)
+{
+    struct argument_details arg;
+    struct wl_client *wc;
+    const char *signature;;
+    pid_t client_pid = -1;
+    struct timespec tp;
+    unsigned int time;
+
+    char strbuf[PATH_MAX], *str_buff;
+    int str_r, str_l;
+
+    wc = wl_resource_get_client(message->resource);
+    if (wc) {
+        logger_add_client_destroy_listener(wc);
+        wl_client_get_credentials(wc, &client_pid, NULL, NULL);
+    }
+
+    clock_gettime(CLOCK_MONOTONIC, &tp);
+    time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+    struct protocol_trace_protocol_log elog = {PROTOCOL_TYPE_REQUEST,};
+    if (direction == WL_PROTOCOL_LOGGER_EVENT)
+        elog.type = PROTOCOL_TYPE_EVENT;
+    else
+        elog.type = PROTOCOL_TYPE_REQUEST;
+    elog.client_pid = client_pid;
+    elog.target_id = wl_resource_get_id(message->resource);
+    snprintf(elog.name, PATH_MAX,"%s:%s",
+            wl_resource_get_class(message->resource), message->message->name);
+
+    char name[PATH_MAX];
+    logger_get_proc_name(client_pid, name, PATH_MAX);
+    snprintf(elog.cmd, PATH_MAX, "%s", name);
+
+    if (!logger_validate_rule(&elog)) return;
+
+    str_buff = strbuf;
+    str_buff[0] = '\0';
+    str_r = sizeof(strbuf);
+
+    BUF_SNPRINTF("[%10.3f] %s%d%s%s@%u.%s(",
+            time / 1000.0,
+            elog.type ? "Server->Client [PID:" : "Server<-Client [PID:",
+            client_pid, "]",
+            wl_resource_get_class(message->resource),
+            wl_resource_get_id(message->resource),
+            message->message->name);
+
+    signature = message->message->signature;
+
+    for (int i = 0; i < message->arguments_count; i++) {
+        signature = logger_get_next_argument(signature, &arg);
+
+        if (i > 0) BUF_SNPRINTF(", ");
+
+        switch (arg.type) {
+            case 'u':
+                BUF_SNPRINTF("%u", message->arguments[i].u);
+                break;
+            case 'i':
+                BUF_SNPRINTF("%i", message->arguments[i].i);
+                break;
+            case 'f':
+                BUF_SNPRINTF("%f", wl_fixed_to_double(message->arguments[i].f));
+                break;
+            case 's':
+                BUF_SNPRINTF("\"%s\"", message->arguments[i].s);
+                break;
+            case 'o':
+                if (message->arguments[i].o) {
+                    struct wl_resource *resource;
+                    resource = (struct wl_resource *)message->arguments[i].o;
+                    BUF_SNPRINTF("%s@%u",
+                            wl_resource_get_class(resource),
+                            wl_resource_get_id(resource));
+                } else
+                    BUF_SNPRINTF("nil");
+                break;
+            case 'n':
+                BUF_SNPRINTF("new id %s@", (message->message->types[i]) ?
+                        message->message->types[i]->name : "[unknown]");
+                if (message->arguments[i].n != 0)
+                    BUF_SNPRINTF("%u", message->arguments[i].n);
+                else
+                    BUF_SNPRINTF("nil");
+                break;
+            case 'a':
+                BUF_SNPRINTF("array");
+                break;
+            case 'h':
+                BUF_SNPRINTF("fd %d", message->arguments[i].h);
+                break;
+        }
+    }
+
+    BUF_SNPRINTF("), cmd: %s", elog.cmd ? elog.cmd : "cmd is NULL");
+
+    if (log_fp_ptrace)
+        fprintf(log_fp_ptrace, "%s\n", strbuf);
+    else
+        ds_dbg("%s", strbuf);
+}
+
+static void
+logger_set(void)
+{
+    ds_dbg("IN >> logger_set");
+
+    log_fp_ptrace = fopen(trace_env_path, "a");
+    if (!log_fp_ptrace) {
+        ds_err("failed open file(%s)", trace_env_path);
+        return;
+    }
+    setvbuf(log_fp_ptrace, NULL, _IOLBF, 512);
+    ds_dbg("has log_fp_ptrace");
+
+    if (ds_wl_protocol_logger) {
+        ds_dbg("if has ds_wl_protocol_logger -> destroy");
+        wl_protocol_logger_destroy(ds_wl_protocol_logger);
+        ds_wl_protocol_logger = NULL;
+    }
+    ds_wl_protocol_logger =
+            wl_display_add_protocol_logger(display, logger_func, NULL);
+
+    ds_dbg("OUT << logger_set");
+}
+
+static void
+logger_unset(void)
+{
+    ds_dbg("IN >> logger_unset");
+
+    if (ds_wl_protocol_logger) {
+        wl_protocol_logger_destroy(ds_wl_protocol_logger);
+        ds_wl_protocol_logger = NULL;
+    }
+
+    ds_dbg("OUT << logger_unset");
+}
+
+static enum protocol_trace_token
+parser_get_next_token(const char **string)
+{
+    static int token_cnt;
+    int i, compare_res, found = 0, first, last;
+
+    first = 0;
+    last = token_cnt -1;
+    token_cnt = sizeof(token_table) / sizeof(token_table[0]);
+
+    i = (first + last) / 2;
+    while (1) {
+        compare_res = strncmp(*string, token_table[i].token_char,
+                token_table[i].token_length);
+        while (compare_res == 0) {
+            found = 1;
+            i++;
+            if (i == token_cnt)
+                break;
+            compare_res = strncmp(*string, token_table[i].token_char,
+                    token_table[i].token_length);
+        }
+
+        if (found) {
+            i--;
+            *string += token_table[i].token_length;
+            return token_table[i].token_name;
+        }
+
+        if (first >= last)
+            break;
+
+        if (compare_res > 0)
+            first = i + 1;
+        else
+            last = i - 1;
+
+        i = (first + last) / 2;
+    }
+
+    if (isalpha(**string)) {
+        (*string)++;
+        while (isalpha(**string) || isdigit(**string) ||
+                **string == '_' || **string == '-') {
+            (*string)++;
+        }
+
+        return PROTOCOL_TRACE_TOKEN_SYMBOL;
+    }
+
+    if (isdigit(**string)) {
+        (*string)++;
+        while (isdigit(**string))
+            (*string)++;
+
+        return PROTOCOL_TRACE_TOKEN_NUMBER;
+    }
+
+    return PROTOCOL_TRACE_TOKEN_UNKNOWN;
+}
+
+static void
+parser_process_token(struct protocol_trace_token_data *token)
+{
+    do {
+        token->last_symbol = *(token->string);
+        token->last_token = parser_get_next_token(token->string);
+        token->symbol_len = *(token->string) - token->last_symbol;
+    } while (token->last_token == PROTOCOL_TRACE_TOKEN_SPACE);
+}
+
+static struct protocol_trace_tree_node *
+parser_parse_statement(struct protocol_trace_tree *tree,
+        struct protocol_trace_token_data *token)
+{
+    struct protocol_trace_tree_node *node = NULL;
+    struct protocol_trace_rule_node *data;
+
+    if (token->last_token == PROTOCOL_TRACE_TOKEN_L_BR) {
+        parser_process_token(token);
+
+        node = parser_parse_token(tree, token);
+        if (!node)
+            return NULL;
+
+        if (token->last_token != PROTOCOL_TRACE_TOKEN_R_BR)
+            goto fail;
+
+        parser_process_token(token);
+
+        return node;
+    }
+
+    if (token->last_token != PROTOCOL_TRACE_TOKEN_SYMBOL)
+        goto fail;
+
+    node = bintree_create_node(tree);
+    if (!node)
+        goto fail;
+
+    data = (struct protocol_trace_rule_node *) bintree_get_node_data(node);
+
+    strncpy(data->variable_name, token->last_symbol, token->symbol_len);
+    data->variable_name[token->symbol_len] = '\0';
+
+    if (!strcasecmp(data->variable_name, "all")) {
+        ds_dbg("data = all");
+        data->node_type = PROTOCOL_TRACE_NODE_TYPE_ALL;
+        parser_process_token(token);
+
+        return node;
+    }
+
+    data->node_type = PROTOCOL_TRACE_NODE_TYPE_DATA;
+
+    parser_process_token(token);
+
+    switch (token->last_token) {
+        case PROTOCOL_TRACE_TOKEN_NOT_EQ:
+            data->comparer = PROTOCOL_TRACE_COMPARER_NOT_EQ;
+            break;
+        case PROTOCOL_TRACE_TOKEN_EQUAL:
+            data->comparer = PROTOCOL_TRACE_COMPARER_EQUAL;
+            break;
+        case PROTOCOL_TRACE_TOKEN_LSS_THAN:
+            data->comparer = PROTOCOL_TRACE_COMPARER_LESS_EQ;
+            break;
+        case PROTOCOL_TRACE_TOKEN_GRT_THAN:
+            data->comparer = PROTOCOL_TRACE_COMPARER_GREATER;
+            break;
+        case PROTOCOL_TRACE_TOKEN_GRT_EQ:
+            data->comparer = PROTOCOL_TRACE_COMPARER_GREATE_EQ;
+            break;
+        default:
+            goto fail;
+    }
+
+    parser_process_token(token);
+
+    if (token->last_token == PROTOCOL_TRACE_TOKEN_NUMBER) {
+        data->value_type = PROTOCOL_TRACE_DATA_TYPE_INTEGER;
+        data->value.integer = atoi(token->last_symbol);
+    } else if (token->last_token == PROTOCOL_TRACE_TOKEN_SYMBOL) {
+        data->value_type = PROTOCOL_TRACE_DATA_TYPE_STRING;
+        strncpy(data->value.string, token->last_symbol, token->symbol_len);
+        data->value.string[token->symbol_len] = '\0';
+    } else {
+        goto fail;
+    }
+
+    parser_process_token(token);
+
+    return node;
+
+fail:
+    if (node)
+        bintree_remove_node_recursive(node);
+
+    return NULL;
+}
+
+static struct protocol_trace_tree_node *
+parser_parse_token(struct protocol_trace_tree *tree,
+        struct protocol_trace_token_data *token)
+{
+    struct protocol_trace_tree_node *node, *left = NULL, *right = NULL;
+    struct protocol_trace_rule_node *data;
+
+    node = parser_parse_statement(tree,token);
+    if (!node) {
+        ds_err("PARSE statement error\n");
+        goto fail;
+    }
+
+    while (token->last_token == PROTOCOL_TRACE_TOKEN_AND) {
+        left = node;
+        node = NULL;
+
+        parser_process_token(token);
+
+        right = parser_parse_statement(tree, token);
+        if (!right)
+            goto fail;
+
+        node = bintree_create_node(tree);
+        if (!node)
+            goto fail;
+
+        data = (struct protocol_trace_rule_node *) bintree_get_node_data(node);
+        data->node_type = PROTOCOL_TRACE_NODE_TYPE_AND;
+        bintree_set_left_child(node, left);
+        bintree_set_right_child(node, right);
+    }
+
+    if (token->last_token == PROTOCOL_TRACE_TOKEN_OR) {
+        left = node;
+        node = NULL;
+
+        parser_process_token(token);
+
+        right = parser_parse_token(tree, token);
+        if (!right)
+            goto fail;
+
+        node = bintree_create_node(tree);
+        if (!node)
+            goto fail;
+
+        data = (struct protocol_trace_rule_node *) bintree_get_node_data(node);
+        data->node_type = PROTOCOL_TRACE_NODE_TYPE_OR;
+        bintree_set_left_child(node, left);
+        bintree_set_right_child(node, right);
+    }
+
+    return node;
+
+fail:
+    ds_dbg("[fail] recursive remove node");
+    if (left)
+        bintree_remove_node_recursive(left);
+
+    return NULL;
+}
+
+static struct protocol_trace_tree *
+parser_parse_rule_string(const char *string)
+{
+    struct protocol_trace_tree *tree;
+    struct protocol_trace_tree_node *node;
+    struct protocol_trace_token_data token;
+
+    token.string = &string;
+    parser_process_token(&token);
+
+    tree = bintree_create_tree(sizeof(struct protocol_trace_rule_node));
+    if (!tree)
+        return NULL;
+
+    node = parser_parse_token(tree, &token);
+    if (!node) {
+        bintree_destroy_tree(tree);
+        ds_dbg("finish destroy tree & return null");
+        return NULL;
+    }
+
+    bintree_set_head(tree, node);
+
+    return tree;
+}
+
+static enum protocol_trace_rule_set_result
+rulechecker_rule_add(struct protocol_trace_rule_checker *rc,
+        enum protocol_trace_policy_type policy, const char *rule_string)
+{
+    if (rc->count == MAX_RULE)
+        return PROTOCOL_TRACE_RULE_SET_ERR_TOO_MANY_RULES;
+
+    rc->rules[rc->count].tree = parser_parse_rule_string(rule_string);
+    if (!rc->rules[rc->count].tree) {
+        ds_dbg("parse error");
+        return PROTOCOL_TRACE_RULE_SET_ERR_PARSE;
+    }
+
+    rc->rules[rc->count].policy = policy;
+    rc->count++;
+
+    return PROTOCOL_TRACE_RULE_SET_OK;
+}
+
+static const char *
+rulechecker_usage_print()
+{
+    return
+          "##########################################################\n"
+          "###     Enlightenment Protocol Log filtering.          ###\n"
+          "##########################################################\n"
+          "\n"
+          "-----------------------------------------------------------------\n"
+          "How to read enlightenment_info protocol messages :\n"
+          "[timestamp]   Server --> Client [PID: [pid]] interface@id.message(arguments..) cmd: CMD\n"
+          "  ex)\n"
+          "[1476930.145] Server --> Client [PID: 103] wl_touch@10.down(758, 6769315, wl_surface@23, 0, 408.000, 831.000) cmd: /usr/bin/launchpad-loader\n"
+          "             ==> type = event && pid = 103 && cmd = launchpad-loader && iface = wl_touch && msg = up\n"
+          "[4234234.123] Server <-- Client [PID: 123] wl_seat@32.get_touch(new id wl_touch@22) cmd: /usr/bin/launchpad-loader\n"
+          "             ==> type = request && pid = 123 && cmd = launchpad-loader && iface = wl_seat && msg = get_touch\n"
+          "-----------------------------------------------------------------\n"
+          "Usage : enlightenment_info -protocol_rule add [POLICY] [RULE]\n"
+          "        enlightenment_info -protocol_rule remove [INDEX]\n"
+          "        enlightenment_info -protocol_rule file [RULE_FILE]\n"
+          "        enlightenment_info -protocol_rule print\n"
+          "        enlightenment_info -protocol_rule help\n"
+          "        [POLICY] : allow / deny \n"
+          "        [RULE] : C Language-style boolean expression syntax. [VARIABLE] [COMPAROTOR] [VALUE]\n"
+          "        [VARIABLE] : type / iface / msg / cmd(command) / pid\n"
+          "        [COMPARATOR] : & / && / and / | / || / or / = / == / != / > / >= / < / <=\n"
+          "        [VALUE] : string / number  \n"
+          "  ex)\n"
+          "        enlightenment_info -protocol_rule add allow \"(type=request) && (iface == wl_pointer and (msg = down or msg = up))\"\n"
+          "        enlightenment_info -protocol_rule add deny cmd!= launch-loader\n"
+          "        enlightenment_info -protocol_rule remove all\n"
+          "        enlightenment_info -protocol_rule remove 3\n"
+        "\n";
+}
+
+static int
+rulechecker_print_func(struct protocol_trace_tree *tree,
+        struct protocol_trace_tree_node *node,
+        struct protocol_trace_tree_node *parent, void *arg)
+{
+    struct protocol_trace_reply_buffer *buffer = (struct protocol_trace_reply_buffer *)arg;
+    char *reply = *buffer->reply;
+    int *len = buffer->len;
+    const char *operators[] = {"==", "<", ">", "<=", ">=", "!=" };
+    struct protocol_trace_rule_node *data;
+
+    data = (struct protocol_trace_rule_node *)bintree_get_node_data(node);
+
+    if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_ALL)
+        REPLY(" ALL");
+    else if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_AND)
+        REPLY(" AND");
+    else if (data->node_type == PROTOCOL_TRACE_NODE_TYPE_OR)
+        REPLY(" OR");
+    else { // data->node_type == PROTOCOL_TRACE_NODE_TYPE_DATA
+        if (node == bintree_get_left_child(parent))
+            REPLY(" (");
+        REPLY(" %s %s ",
+                data->variable_name, operators[data->comparer]);
+
+        if (data->value_type == PROTOCOL_TRACE_DATA_TYPE_INTEGER)
+            REPLY(" %d",
+                    data->value.integer);
+        else
+            REPLY(" %s",
+                    data->value.string);
+
+        if (node == bintree_get_right_child(parent))
+            REPLY(" )");
+    }
+
+    *buffer->reply = reply;
+
+    return 0;
+}
+
+static void
+rulechecker_print_rules(struct protocol_trace_rule_checker *rc,
+        char *reply, int *len)
+{
+    struct protocol_trace_reply_buffer buffer = {&reply, len};
+    int i;
+
+    REPLY("\n --------------------[ Protocol Filter Rules ]--------------------\n");
+    REPLY("  No      Policy              Rule\n");
+    REPLY(" -----------------------------------------------------------------\n");
+
+    for (i =0; i < rc->count; i++) {
+        REPLY("  %3d %10s \"", i,
+                rc->rules[i].policy == PROTOCOL_POLICY_TYPE_ALLOW ?
+                "ALLOW" : "DENY");
+        bintree_inorder_traverse(rc->rules[i].tree, rulechecker_print_func,
+                (void*) & buffer);
+        REPLY("\"\n");
+    }
+}
+
+static enum protocol_trace_rule_set_result
+rulechecker_rule_remove(struct protocol_trace_rule_checker *rc, int index)
+{
+    if (index < 0 || index >= rc->count)
+        return PROTOCOL_TRACE_RULE_SET_ERR_NO_RULE;
+
+    bintree_destroy_tree(rc->rules[index].tree);
+    rc->count--;
+    if (index != rc->count) {
+        memmove(&rc->rules[index], &rc->rules[index + 1],
+                sizeof(struct protocol_trace_rule)*(rc->count - index));
+    }
+
+    return PROTOCOL_TRACE_RULE_SET_OK;
+}
+
+static struct protocol_trace_rule_checker *
+rulechecker_init()
+{
+    struct protocol_trace_rule_checker *rc;
+
+    rc = calloc(1, sizeof *rc);
+    if (!rc)
+        return NULL;
+
+    rc->count = 0;
+
+    return rc;
+}
+
+static void rulechecker_destroy(struct protocol_trace_rule_checker *rc)
+{
+    for (int i = rc->count - 1; i >= 0; i--)
+        rulechecker_rule_remove(rc, i);
+
+    free(rc);
+}
+
+static bool
+rule_file_set(const char *filename, char *reply, int *len)
+{
+    int fd = -1, rule_len;
+    char fs[8096], *pfs;
+
+    ds_dbg("IN >> rule_file_set");
+
+    fd = open(filename, O_RDONLY);
+    if (fd < 0) {
+        ds_err("failed: open '%s'", filename);
+        return false;
+    }
+
+    rule_len = read(fd, fs, sizeof(fs));
+    pfs = fs;
+
+    while (pfs -fs < rule_len) {
+        int i, new_argc =3;
+        const char *new_argv[3] = {"add", };
+        char policy[64] = {0,};
+        char rule[1024] = {0,};
+
+        if (pfs[0] == ' ' || pfs[0] == '\n') {
+            pfs++;
+            continue;
+        }
+        for (i = 0; pfs[i] != ' '; i++)
+            policy[i] = pfs[i];
+
+        new_argv[1] = policy;
+        pfs += (strlen(new_argv[1]) + 1);
+
+        memset(rule, 0, sizeof(rule));
+        for (i = 0; pfs[i] != '\n'; i++)
+            rule[i] = pfs[i];
+
+        new_argv[2] = rule;
+
+        pfs += (strlen(new_argv[2]) +1);
+
+        if (!rule_set((const int)new_argc, (const char**)new_argv,
+                reply, len)) {
+            close(fd);
+            return false;
+        }
+    }
+    close(fd);
+
+    ds_dbg("OUT << rule_file_set");
+
+    return true;
+}
+
+static int
+rule_check_remove_rule(const char *str)
+{
+    char *endptr;
+    int index;
+
+    index = strtol(str, &endptr, 10);
+
+    if (errno == ERANGE) {
+        ds_err("Rule remove fail : overflow");
+        return -1;
+    }
+
+    if (errno != 0) {
+        ds_err("Rule remove fail : other error");
+        return -1;
+    }
+
+    if (endptr == 0) {
+        ds_err("Rule remove fail : non-numeric");
+        return -1;
+    }
+
+    if (*endptr != '\0') {
+        ds_err("Rule remove fail : non-numeric at end");
+        return -1;
+    }
+
+    if (isspace(*str)) {
+        ds_err("Rule remove fail : space at beginning");
+        return -1;
+    }
+
+    return index;
+}
+
+static void
+rule_arguments_merge(char *target, int target_size,
+        int argc, const char **argv)
+{
+    int i, len;
+
+    for (i = 0; i < argc; i++) {
+        len = snprintf(target, target_size, "%s", argv[i]);
+        target += len;
+        target_size -= len;
+
+        if (i != argc - 1) {
+            *(target++) = ' ';
+            target_size--;
+        }
+    }
+}
+
+static bool
+rule_set(const int argc, const char **argv, char *reply, int *len)
+{
+    const char * command;
+
+    ds_dbg("IN >> rule_set");
+    ds_dbg("(parameter) argc = %d, argv[0] = %s", argc, argv[0]);
+
+    if (argc == 0) {
+        rulechecker_print_rules(rc, reply, len);
+        return true;
+    }
+
+    command = argv[0];
+
+    if (!strcasecmp(command, "add")) {
+        ds_dbg("ADD");
+        enum protocol_trace_policy_type policy_type;
+        enum protocol_trace_rule_set_result result;
+        const char * policy = argv[1]; //allow, deny
+        char merge[8192] = {0,}, rule[8192] = {0,};
+        int i, index=0, size_rule, apply = 0, size_merge;
+
+        if (argc <3) {
+            ds_err("Error: Too few argumens.");
+            return false;
+        }
+
+        if (!strcasecmp(policy, "ALLOW"))
+            policy_type = PROTOCOL_POLICY_TYPE_ALLOW;
+        else if (!strcasecmp(policy, "DENY"))
+            policy_type = PROTOCOL_POLICY_TYPE_DENY;
+        else {
+            ds_err("Error : Unknown : [%s].\n should be ALLOW/DENY.", policy);
+            return false;
+        }
+
+        rule_arguments_merge(merge, sizeof(merge), argc -2, &(argv[2]));
+
+        size_rule = sizeof(rule) -1;
+        size_merge = strlen(merge);
+
+        for (i = 0; i < size_merge; i++) {
+            if (merge[i] == '\"' || merge[i] == '\'') {
+                rule[index++] = ' ';
+                if (index > size_rule)
+                    return false;
+                continue;
+            }
+            if (merge[i] == '+') {
+                rule[index++] = ' ';
+                if (index > size_rule)
+                    return false;
+                if (apply == 0) {
+                    const char * plus = "|| type=reply || type=error";
+                    int size_plus = strlen(plus);
+                    int len = MIN(size_rule - index, size_plus);
+                    snprintf(rule, sizeof(rule), "%s", plus);
+                    index += len;
+                    if (index >size_rule)
+                        return false;
+                    apply =1;
+                }
+                continue;
+            }
+            rule[index++] = merge[i];
+            if (index > size_rule)
+                return false;
+        }
+        ds_dbg("ADD :: rule = %s", rule);
+
+        result = rulechecker_rule_add(rc, policy_type, rule);
+
+        if (result == PROTOCOL_TRACE_RULE_SET_ERR_TOO_MANY_RULES) {
+            ds_err("Error: Too many rules were added.");
+            return false;
+        } else if (result == PROTOCOL_TRACE_RULE_SET_ERR_PARSE) {
+            ds_err("Error: parsing the rule [%s]", rule);
+            return false;
+        }
+        ds_dbg( "The rule was successfully added");
+
+    } else if (!strcasecmp(command, "remove")) {
+        const char * remove_idx;
+        int i;
+
+        ds_dbg("REMOVE");
+
+        if (argc < 2) {
+            ds_err("Error: Too few arguments");
+            return false;
+        }
+
+        for (i = 0; i < argc - 1; i++) {
+            remove_idx = argv[i + 1];
+            if (!strcasecmp(remove_idx, "all")) {
+                ds_dbg("REMOVE :: all");
+                rulechecker_destroy(rc);
+                rc = rulechecker_init();
+                if (!rc) {
+                    ds_err("Error: rules not removed");
+                    return false;
+                }
+            } else {
+                int index = rule_check_remove_rule(remove_idx);
+                if (index == -1) {
+                    ds_err("Rule remove fail : overflow");
+                } else {
+                    ds_dbg("REMOVE :: remove idx = %d", index);
+
+                    if (isdigit(*remove_idx) &&
+                        rulechecker_rule_remove(rc, index) == 0)
+                        ds_dbg("Rule remove success : rule [%d]", index);
+                    else
+                        ds_err("Rule remove fail : No rule [%s]", remove_idx);
+                }
+            }
+        }
+    } else if (!strcasecmp(command, "file")) {
+        ds_dbg("FILE");
+        if (argc <2) {
+            ds_err("Error: Too few argumens.");
+            return false;
+        }
+
+        if (!rule_file_set(argv[1], reply, len))
+            return false;
+
+        rulechecker_print_rules(rc, reply, len);
+    } else if (!strcasecmp(command, "print")) {
+        rulechecker_print_rules(rc, reply, len);
+    } else if (!strcasecmp(command, "help")) {
+        ds_dbg( "%s", rulechecker_usage_print());
+    } else {
+        ds_err("%s\nUnknown command : [%s] ", rulechecker_usage_print(), command);
+    }
+
+    ds_dbg("OUT << rule_set");
+
+    return true;
+}
+
+static bool
+rule_init(char *rule_path)
+{
+    bool ret = false;
+    const char *argv[2];
+    int argc = 2;
+
+    char tmpReply[4096];
+    int tmpLen = sizeof(tmpReply);
+    char *reply = tmpReply;
+    int *len = &tmpLen;
+
+    if (!rule_path || strlen(rule_path) <= 0) {
+        rule_path = alloca(10);
+        snprintf(rule_path, 10, "%s", "/tmp/rule");
+        ds_inf("rule path is default = %s", rule_path);
+    }
+
+    argv[0] = "file";
+    argv[1] = rule_path;
+
+    ds_dbg("rule_path = %s", rule_path);
+
+    ret = rule_set(argc, (const char**)&(argv[0]), reply, len);
+    ds_inf("%s", &tmpReply);
+
+    return ret;
+}
+
+static bool
+logger_init(char *trace_path)
+{
+    ds_dbg("IN >> logger_init");
+    if (!trace_path || strlen(trace_path) <= 0) {
+        snprintf(trace_env_path, sizeof(trace_env_path), "%s", "/tmp/trace");
+        ds_inf("trace path is default = %s", trace_env_path);
+    } else {
+        snprintf(trace_env_path, sizeof(trace_env_path), "%s", trace_path);
+    }
+
+    ds_dbg("saved trace path = %s", trace_env_path);
+
+    logger_unset();
+    ds_dbg("OUT << logger_init");
+
+    return true;
+}
+
+int
+protocol_trace_enable(bool state)
+{
+    if (log_fp_ptrace != NULL) {
+        fclose(log_fp_ptrace);
+        log_fp_ptrace = NULL;
+    }
+
+    if (state) {
+        //TODO: can change trace file path by cmd
+        ds_inf("state: protocol_trace enabled");
+        logger_set();
+        return 1;
+    } else {
+        ds_inf("state: protocol_trace disabled");
+        logger_unset();
+        return 0;
+    }
+
+    return -1;
+}
+
+bool
+protocol_trace_init(struct wl_display *d)
+{
+    bool ret = false;
+    char *env_path = NULL;
+
+    display = d;
+    rc = rulechecker_init();
+
+    env_path = getenv("DS_PROTOCOL_RULE_FILE");
+    ret = rule_init(env_path);
+
+    if (env_path) {
+        char *tmp = strdup(env_path);
+        if (!tmp) return false;
+        free(tmp);
+        env_path = NULL;
+    }
+    if (!ret)
+        return ret;
+
+    env_path = getenv("DS_PROTOCOL_TRACE_FILE");
+    ret = logger_init(env_path);
+
+    if (env_path)  {
+        char *tmp = strdup(env_path);
+        if (!tmp) return false;
+        free(tmp);
+        env_path = NULL;
+    }
+
+    return ret;
+}
+
+void
+protocol_trace_fini()
+{
+    rulechecker_destroy(rc);
+}
\ No newline at end of file
diff --git a/examples/protocol-trace.h b/examples/protocol-trace.h
new file mode 100644 (file)
index 0000000..973b30e
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef EXAMPLES_PROTOCOL_TRACE_H
+#define EXAMPLES_PROTOCOL_TRACE_H
+
+#include <wayland-server.h>
+
+bool protocol_trace_init(struct wl_display *display);
+int protocol_trace_enable(bool state);
+void protocol_trace_fini();
+
+#endif
index a797ca2..a915c21 100644 (file)
@@ -42,6 +42,7 @@
 #endif
 
 #include "pixman-helper.h"
+#include "protocol-trace.h"
 
 #define TINYDS_UNUSED   __attribute__((unused))
 struct tinyds_keyboard;
@@ -427,6 +428,9 @@ main(void)
 
     wl_display_run(display);
 
+    protocol_trace_enable(false);
+    protocol_trace_fini();
+
     wl_display_destroy_clients(display);
     wl_display_destroy(display);
 
@@ -1010,6 +1014,11 @@ init_server(struct tinyds_server *server, struct wl_display *display)
     if (!add_new_input_method(server))
         goto err;
 
+    if (!protocol_trace_init(display))
+        goto err;
+
+    protocol_trace_enable(true);
+
     return true;
 
 err: