From fe7f641c720b2b58eac87a94017a277c419eb250 Mon Sep 17 00:00:00 2001 From: "adhavan.m" Date: Thu, 30 Aug 2018 18:59:32 +0530 Subject: [PATCH] Implementation of automatic command exchange feature. Change-Id: Ic9a971972fe6e929332621fbe23216f6f81c6f87 Signed-off-by: adhavan.m --- src/plugin.c | 159 ++++++++++++++++++++++++++++++++ src/plugin.h | 5 +- src/sdb.c | 7 +- src/services.c | 246 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 415 insertions(+), 2 deletions(-) diff --git a/src/plugin.c b/src/plugin.c index 73d095e..3deabcb 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -36,6 +36,8 @@ PLUGIN_ASYNCHRONOUS_CMD_PROC plugin_async_proc = NULL; hashtable* plugin_cmd_hashtable = NULL; +char* extcmd = NULL; + typedef struct _async_parameter { int cmd; parameters* in; @@ -119,6 +121,30 @@ static int load_plugin_not_default() return 1; } +void readxml() { + char * buffer = NULL; + long length; + FILE * fptr = fopen(PLUGIN_XML_PATH, "rb"); + + if (fptr == NULL) { + E("failed to open commands.xml\n"); + return; + } + + fseek(fptr, 0, SEEK_END); + length = ftell(fptr); + fseek(fptr, 0, SEEK_SET); + buffer = malloc(length); + if (buffer) { + fread(buffer, 1, length, fptr); + } + fclose(fptr); + if (buffer) { + D("command xml data: %s\n", buffer); + extcmd = buffer; + } +} + void load_sdbd_plugin() { int ret; @@ -137,6 +163,7 @@ void load_sdbd_plugin() D ( "using sdbd plugin interface.(%s)\n", PLUGIN_PATH ); plugin_init_proc ( plugin_event_handler, plugin_register_command ); + readxml(); } // use default plugin feature for the commands or events that are not supported by plugin @@ -154,6 +181,11 @@ void unload_sdbd_plugin() dlclose ( g_plugin_handle ); g_plugin_handle = NULL; } + + if (extcmd) { + free(extcmd); + extcmd = NULL; + } } // return 1 if plugin support given command @@ -265,6 +297,56 @@ static int create_async_proc_thread( int cmd, parameters* in ) return s[0]; } +static void *async_proc_extcmd_bootstrap_func(void *x) +{ + async_parameter *p = x; + plugin_async_proc(p->cmd, p->in, p->out_fd); + release_parameters(p->in); + free(p->in); + sdb_close(p->out_fd); + free(p); + return 0; +} + +int create_async_extcmd_proc_thread( int cmd, parameters* in ) +{ + async_parameter* async_param; + sdb_thread_t t; + int s[2]; + + if (sdb_socketpair(s)) { + release_parameters(in); + free(in); + E("cannot create extcmd async proc socket pair\n"); + return -1; + } + + async_param = (async_parameter*)malloc(sizeof(async_parameter)); + if (async_param == NULL) { + release_parameters(in); + free(in); + fatal("cannot allocate async_parameter"); + return -1; + } + + async_param->cmd = cmd; + async_param->in = in; + async_param->out_fd = s[1]; + + if (sdb_thread_create(&t, async_proc_extcmd_bootstrap_func, async_param)) { + free(async_param); + sdb_close(s[0]); + sdb_close(s[1]); + release_parameters(in); + free(in); + E("cannot create extcmd async proc thread\n"); + return -1; + } + + D("extcmd async proc thread started, %d:%d\n",s[0], s[1]); + return s[0]; +} + // return 1 if succeed to get capability from plugin // return 0 otherwise int request_capability_to_plugin ( int cap, char* out_buf, unsigned int out_len ) @@ -335,6 +417,45 @@ int request_validity_to_plugin ( int cmd, const char* in_buf ) return success; } +// return 1 if allowed by plugin (valid) +// return 0 if disallowed by plugin (invalid) +int request_extcmd_validity_to_plugin ( int cmd, const char* in_buf) +{ + int success = 0; + int ret; + parameters in, out; + + if ( in_buf != NULL ) { + in.number_of_parameter = 1; + in.array_of_parameter = ( parameter* ) malloc ( sizeof ( parameter ) ); + if (in.array_of_parameter == NULL) { + D("failed to allocate memory for the parameter\n"); + return success; + } + in.array_of_parameter[0].type = type_string; + in.array_of_parameter[0].v_string.length = strlen ( in_buf ); + in.array_of_parameter[0].v_string.data = strdup ( in_buf ); + } else { + in.number_of_parameter = 0; + in.array_of_parameter = NULL; + } + + D ("requested validity : %d, %s\n", cmd, in_buf); + + out.number_of_parameter = 0; + out.array_of_parameter = NULL; + ret = plugin_sync_proc ( cmd, &in, &out ); + if ( ret == PLUGIN_CMD_SUCCESS ) { + success = ( out.array_of_parameter[0].v_int32 == PLUGIN_RET_VALID ) ? 1 : 0; + release_parameters ( &out ); + + D ("request validity success : %d\n", success); + } + + release_parameters ( &in ); + return success; +} + // return 1 if succeed to convert // return 0 otherwise int request_conversion_to_plugin ( int cmd, const char* in_buf, char* out_buf, unsigned int out_len ) @@ -370,6 +491,44 @@ int request_conversion_to_plugin ( int cmd, const char* in_buf, char* out_buf, u return success; } +// return 1 if succeed to convert +// return 0 otherwise +int request_extcmd_conversion_to_plugin ( int cmd, const char* in_buf, char* out_buf, unsigned int out_len ) +{ + int success = 0; + int ret; + parameters in, out; + + if ( in_buf != NULL ) { + in.number_of_parameter = 1; + in.array_of_parameter = ( parameter* ) malloc ( sizeof ( parameter ) ); + if (in.array_of_parameter == NULL) { + E("failed to allocate memory for the parameter\n"); + return success; + } + in.array_of_parameter[0].type = type_string; + in.array_of_parameter[0].v_string.length = strlen ( in_buf ); + in.array_of_parameter[0].v_string.data = strdup ( in_buf ); + } else { + in.number_of_parameter = 0; + in.array_of_parameter = NULL; + } + + out.number_of_parameter = 0; + out.array_of_parameter = NULL; + + ret = plugin_sync_proc ( cmd, &in, &out ); + if ( ret == PLUGIN_CMD_SUCCESS ) { + strncpy ( out_buf, out.array_of_parameter[0].v_string.data, out_len - 1 ); + out_buf[out_len - 1] = '\0'; + success = 1; + release_parameters ( &out ); + } + + release_parameters ( &in ); + return success; +} + // return 1 if locked // return 0 if unlocked // return -1 if request failed diff --git a/src/plugin.h b/src/plugin.h index 47ed3dc..1b2ccbe 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -19,7 +19,10 @@ #include "sdbd_plugin.h" -#define PLUGIN_PATH "/usr/lib/libsdbd_plugin.so" +#define PLUGIN_PATH "/usr/lib/libsdbd_plugin.so" +#define PLUGIN_XML_PATH "/etc/sdbd/commands.xml" + +extern char* extcmd; int default_plugin_init ( eventfunc event_cb, registerfunc register_func ); int default_plugin_sync_proc ( int cmd, parameters* in, parameters* out ); diff --git a/src/sdb.c b/src/sdb.c index 38a6b21..8b79e84 100644 --- a/src/sdb.c +++ b/src/sdb.c @@ -628,8 +628,13 @@ static void send_connect(atransport *t) snprintf((char*) cp->data, sizeof cp->data, "%s::%s::%d", sdb_device_banner, device_name, status); } - D("CNXN data:%s\n", (char*)cp->data); + if (extcmd != NULL) { + char extbuf[4096]; + snprintf(extbuf, sizeof extbuf, "::%s", extcmd); + strcat((char*) cp->data, extbuf); + } cp->msg.data_length = strlen((char*) cp->data) + 1; + D("CNXN data: %s\n", (char*)cp->data); send_packet(cp, t); } diff --git a/src/services.c b/src/services.c index 7f61a0b..b6863b0 100644 --- a/src/services.c +++ b/src/services.c @@ -910,6 +910,191 @@ static int create_syncproc_thread() return ret_fd; } +static int create_extcmd_subproc_thread(const char *name, int lines, int columns) +{ + stinfo *sti; + sdb_thread_t t; + int ret_fd; + pid_t pid; + char *value = NULL; + char *trim_value = NULL; + char path[PATH_MAX]; + char *envp[MAX_TOKENS]; + int envp_cnt = 0; + + memset(path, 0, sizeof(path)); + memset(envp, 0, sizeof(envp)); + + envp[envp_cnt++] = strdup("TERM=linux"); + envp[envp_cnt++] = strdup("DISPLAY=:0"); + + if (should_drop_privileges()) { + if (g_sdk_home_dir_env) { + envp[envp_cnt++] = strdup(g_sdk_home_dir_env); + } else { + envp[envp_cnt++] = strdup("HOME=/home/owner"); + } + get_env("ENV_PATH", &value); + } else { + get_env("ENV_SUPATH", &value); + if(value == NULL) { + get_env("ENV_ROOTPATH", &value); + } + envp[envp_cnt++] = strdup("HOME=/root"); + } + if (value != NULL) { + trim_value = str_trim(value); + if (trim_value != NULL) { + // if string is not including 'PATH=', append it. + if (strncmp(trim_value, "PATH", 4)) { + snprintf(path, sizeof(path), "PATH=%s", trim_value); + } else { + snprintf(path, sizeof(path), "%s", trim_value); + } + envp[envp_cnt++] = strdup(path); + } else { + snprintf(path, sizeof(path), "%s", value); + envp[envp_cnt++] = strdup(path); + } + free(value); + } + + /* get environment variables from plugin */ + char *envp_plugin = NULL; + envp_plugin = malloc(ENV_BUF_MAX); + if (envp_plugin == NULL) { + E("Cannot allocate the shell commnad buffer."); + return -1; + } + memset(envp_plugin, 0, ENV_BUF_MAX); + if (!request_extcmd_conversion_to_plugin(PLUGIN_SYNC_CMD_GET_SHELL_ENV, NULL, + envp_plugin, ENV_BUF_MAX)) { + E("Failed to convert the shell command. (%s)\n", name); + free(envp_plugin); + return -1; + } else { + if(envp_plugin[0] != '\0') { + envp_cnt = tokenize_append(envp_plugin, "\n", envp, MAX_TOKENS, envp_cnt); + } + } + free(envp_plugin); + + /* Last element of envp must be the NULL-terminator to prevent execvp fail */ + envp[envp_cnt] = NULL; + + if(name) { // in case of shell execution directly + // Check the shell command validation. + if (!request_extcmd_validity_to_plugin(PLUGIN_SYNC_CMD_VERIFY_SHELLCMD, name)) { + E("This extcmd command is invalid. (%s)\n", name); + return -1; + } + + // Convert the shell command. + char *new_cmd = NULL; + new_cmd = malloc(SDBD_SHELL_CMD_MAX); + if(new_cmd == NULL) { + E("Cannot allocate the shell commnad buffer."); + return -1; + } + + memset(new_cmd, 0, SDBD_SHELL_CMD_MAX); + if(!request_extcmd_conversion_to_plugin(PLUGIN_SYNC_CMD_CONVERT_SHELLCMD, name, new_cmd, SDBD_SHELL_CMD_MAX)) { + E("Failed to convert the shell command. (%s)\n", name); + free(new_cmd); + return -1; + } + + D("converted cmd : %s\n", new_cmd); + + char *args[] = { + USER_DAEMON_COMMAND, + "-c", + NULL, + NULL, + }; + args[2] = new_cmd; + + ret_fd = create_userprocess(USER_DAEMON_COMMAND, &pid, (char * const*)args, (char * const*)envp); + free(new_cmd); + } else { // in case of shell interactively + // Check the capability for interactive shell support. + if (!is_support_interactive_shell()) { + E("This platform dose NOT support the interactive shell\n"); + return -1; + } + + char * const args[] = { + USER_DAEMON_COMMAND, + "-", + NULL, + }; + ret_fd = create_userprocess(USER_DAEMON_COMMAND, &pid, (char * const*)args, (char * const*)envp); +#if 0 // FIXME: should call login command instead of /bin/sh + if (should_drop_privileges()) { + char *args[] = { + USER_DAEMON_COMMAND, + "-", + NULL, + }; + ret_fd = create_userprocess(USER_DAEMON_COMMAND, &pid, args, envp); + } else { + char *args[] = { + LOGIN_COMMAND, + "-f", + SUPER_USER, + NULL, + }; + ret_fd = create_subprocess(LOGIN_COMMAND, &pid, args, envp); + } +#endif + } + + /* free environment variables */ + int i = 0; + if(envp_cnt > 0) { + for(i = 0; i < envp_cnt; i++) { + if(envp[i]) { + D("envp[%d] = %s\n", i, envp[i]); + free(envp[i]); + } + } + } + + D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); + + if (ret_fd < 0) { + E("cannot create service thread\n"); + return -1; + } + + if (lines > 0 && columns > 0) { + D("shell size lines=%d, columns=%d\n", lines, columns); + struct winsize win_sz; + win_sz.ws_row = lines; + win_sz.ws_col = columns; + + if (ioctl(ret_fd, TIOCSWINSZ, &win_sz) < 0) { + E("failed to sync window size.\n"); + } + } + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = subproc_waiter_service; + sti->cookie = (void*)((intptr_t)pid); + sti->fd = ret_fd; + + if(sdb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + sdb_close(ret_fd); + E("cannot create service thread\n"); + return -1; + } + + D("service thread started, fd=%d pid=%d\n",ret_fd, pid); + return ret_fd; +} + static void get_platforminfo(int fd, void *cookie) { pinfo sysinfo; @@ -1213,6 +1398,65 @@ void handle_grantfile(int fd, void *cookie) { sdb_close(fd); } +int request_extcmd_to_plugin(const char* in_buf) { + char full_cmd[ENV_BUF_MAX] = {0,}; + char *tokens[MAX_TOKENS]; + int args_cnt = tokenize(in_buf, ":", tokens, MAX_TOKENS); + if (args_cnt < 3) { + E("failed to parse extcmd.\n"); + return 0; + } + + char* cmd_name = tokens[0]; + char* cmd_no = tokens[args_cnt - 2]; + int cmd = atoi(cmd_no); + char* exec_type = tokens[args_cnt - 1]; + + strcat(full_cmd, cmd_name); + int i = 1; + for(;i < args_cnt - 2;i++) { + strcat(full_cmd, " "); + strcat(full_cmd, tokens[i]); + } + D("full extcmd: %s\n", full_cmd); + if (!strcmp(exec_type, "sync")) { + return create_extcmd_subproc_thread(full_cmd, 0, 0); + } + + else if (!strcmp(exec_type, "async")) { + parameters* in; + int fd; + + in = (parameters*) malloc(sizeof(parameters)); + if (in == NULL) { + E("failed to allocate memory for the parameters\n"); + return -1; + } + + if (full_cmd != NULL) { + in->number_of_parameter = 1; + in->array_of_parameter = (parameter*) malloc(sizeof(parameter)); + if (in->array_of_parameter == NULL) { + free(in); + E("failed to allocate memory for the parameter\n"); + return -1; + } + in->array_of_parameter[0].type = type_string; + in->array_of_parameter[0].v_string.length = strlen(full_cmd); + in->array_of_parameter[0].v_string.data = strdup(full_cmd); + } else { + in->number_of_parameter = 0; + in->array_of_parameter = NULL; + } + + fd = create_async_extcmd_proc_thread(cmd, in); + + return fd; + } + + return 0; +} + int service_to_fd(const char *name) { int ret = -1; @@ -1325,6 +1569,8 @@ int service_to_fd(const char *name) ret = create_service_thread(handle_grantfile, (void*)name+10); } else if(!strncmp(name, "appcmd:", 7)){ ret = request_appcmd_to_plugin(name+7); + } else if(!strncmp(name, "extcmd:", 7)) { + ret = request_extcmd_to_plugin(name+7); } if (ret >= 0) { -- 2.34.1