From e468e1e7159dd97e5000606d300853f61665f4c3 Mon Sep 17 00:00:00 2001 From: "duna.oh" Date: Fri, 16 Sep 2022 13:55:03 +0900 Subject: [PATCH] tinyds: add 'protocol_trace' feature 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 | 1 + examples/protocol-trace.c | 1603 +++++++++++++++++++++++++++++++++++++++++++++ examples/protocol-trace.h | 10 + examples/tinyds-tdm.c | 9 + 4 files changed, 1623 insertions(+) create mode 100644 examples/protocol-trace.c create mode 100644 examples/protocol-trace.h diff --git a/examples/meson.build b/examples/meson.build index 6a81cbf..dbda372 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -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 index 0000000..d57e3c2 --- /dev/null +++ b/examples/protocol-trace.c @@ -0,0 +1,1603 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000..973b30e --- /dev/null +++ b/examples/protocol-trace.h @@ -0,0 +1,10 @@ +#ifndef EXAMPLES_PROTOCOL_TRACE_H +#define EXAMPLES_PROTOCOL_TRACE_H + +#include + +bool protocol_trace_init(struct wl_display *display); +int protocol_trace_enable(bool state); +void protocol_trace_fini(); + +#endif diff --git a/examples/tinyds-tdm.c b/examples/tinyds-tdm.c index a797ca2..a915c21 100644 --- a/examples/tinyds-tdm.c +++ b/examples/tinyds-tdm.c @@ -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: -- 2.7.4