/** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ /* stdlib includes */ #include #include #include #include #include #include #include #include /* sudo apt-get install libedit-dev : Install editline */ #include /* OC includes */ #include #define HISTORY ".occli_history" /* Saved under ${HOME} direcotory */ #define SWAP(s, e) do { \ int32_t t; t = s; s = e; e = t; \ } while (0) /* MAX_CLIENTRY represents the maximum entries which autofill can take */ #define OCCLI_MAX_CLISTRING 1000 #define OCCLI_MAX_SUBSTRING 75 #define OCCLI_STRING_MAX_LEN 100 #define OCCLI_CALC_SCHEMA_SIZE 1 #define OCCLI_PARSE_SCHEMA 2 #define MAX_ETHERNET_PORT 4 #define OPENCELLULAR "opencellular# " extern char *rl_line_buffer; static char *s_allParams[OCCLI_MAX_CLISTRING]; static char *s_subSetParams[OCCLI_MAX_SUBSTRING]; static char *s_strCli = NULL; extern const Component sys_schema[]; /************************************************************************** * Function Name : occli_trim_extra_spaces * Description : This Function used to remove extra space inbetween * words and trailing spaces from the string * Input(s) : string * Output(s) : string ***************************************************************************/ int8_t occli_trim_extra_spaces(char *string) { int8_t length = 0; char *end = NULL; if (string == NULL) { logerr("Invalid memory location \n"); return FAILED; } /* Logic to remove trailing spaces */ length = strlen(string); end = string + length - 1; while(end >= string && isblank(*end)) { end--; } *(end + 1) = '\0'; return SUCCESS; } /************************************************************************** * Function Name : occli_copy_text * Description : This Function Return a copy of the string entered by the user in cli * Input(s) : * Output(s) : s_strCli ***************************************************************************/ static int8_t occli_copy_text () { int8_t ret = FAILED; int16_t length; if (isblank(rl_line_buffer[0])) { printf("\nBlank Space is not allowed as the first character\n"); printf("Please press ctrl+L to clear screen\n"); return ret; } length = rl_point; s_strCli = (char*)realloc(s_strCli, length + 1); if (s_strCli == NULL) { printf("\n ERROR: realloc"); } else { ret = SUCCESS; strncpy (s_strCli, rl_line_buffer, length); s_strCli[length] = '\0'; } if (ret == SUCCESS) { ret = occli_trim_extra_spaces(s_strCli); } return ret; } /************************************************************************** * Function Name : occli_init_subSetParams * Description : This Function is used to allocate memory for * subSetParams * Input(s) : text * Output(s) : ***************************************************************************/ static int8_t occli_init_subSetParams(const char *text) { char *paramStr = NULL; int32_t len = 0; int16_t listIdx = 0; int16_t subsetIdx = 0; if (text == NULL) { logerr("Invalid memory location \n"); return FAILED; } if (s_subSetParams != NULL) { for (listIdx = 0; (s_subSetParams[listIdx] != NULL && listIdx < OCCLI_MAX_SUBSTRING) ; listIdx++) { free(s_subSetParams[listIdx]); s_subSetParams[listIdx] = NULL; } } len = strlen(text); listIdx = 0; while ((paramStr = s_allParams[listIdx])) { if (strncmp(text, paramStr, len) == 0) { subsetIdx++; if(subsetIdx >= OCCLI_MAX_SUBSTRING) { break; } } listIdx++; } for (listIdx= 0; listIdx < subsetIdx; listIdx++) { s_subSetParams[listIdx] = (char *)calloc(1, OCCLI_STRING_MAX_LEN); if ((s_subSetParams[listIdx]) == NULL) { logerr("calloc error"); return -ENOMEM; } } return SUCCESS; } /************************************************************************** * Function Name : occli_all_param_generator * Description : This Function used to generate the all param * list * Input(s) : text, state * Output(s) : ***************************************************************************/ static char* occli_all_param_generator(const char* text, int32_t state) { char *paramstr = NULL; char *token = NULL; char subStr[OCCLI_STRING_MAX_LEN] = {0}; char tempStr[OCCLI_STRING_MAX_LEN] = {0}; static int32_t s_listidx = 0; static int32_t s_subSetIdx = 0; static int32_t s_len = 0; int8_t index = 0 ; bool isEntryFound = false; if (text == NULL) { logerr("Invalid memory location \n"); rl_attempted_completion_over = 1; return NULL; } if (state == 0) { s_listidx = 0; s_subSetIdx = 0; s_len = strlen(text); if (occli_init_subSetParams(text) != SUCCESS) { rl_attempted_completion_over = 1; return NULL; } } while ((paramstr = s_allParams[s_listidx]) != NULL) { isEntryFound = false; s_listidx++; if (strncmp(s_strCli, paramstr, s_len) == 0) { strcpy(subStr, paramstr); token = strtok(subStr + s_len, "."); memset(tempStr, 0, OCCLI_STRING_MAX_LEN); rl_completion_append_character = '\0'; if (token == NULL) { /* * case where the user has entered the complete string, * autofill is not needed in this case , append a space */ sprintf(tempStr, "%s ", text); } else { /* Autofill will be needed in all cases here */ if (strncmp(text, "", 1) == 0) { /* case where the user hasn't entered any string */ sprintf(tempStr, "%s", token); } else if(strncmp(paramstr + s_len, "." , 1) == 0) { /* * case where the user has entered complete string * for either subsystem/component (ie) "system" */ sprintf(tempStr, "%s.%s", text,token); } else { /* * case where the user has entered subset of the string * for example "sys" */ sprintf(tempStr, "%s%s", text,token); } } /* * The for loop below is to ensure that duplicate entries arent * displayed in the cli */ for (index = 0; index < s_subSetIdx; index++) { if (strcmp(s_subSetParams[index], tempStr) == 0) { isEntryFound = true; break; } } if(isEntryFound != true) { strcpy(s_subSetParams[s_subSetIdx], tempStr); s_subSetIdx++; return strdup(s_subSetParams[s_subSetIdx - 1]); } } } return NULL; } /************************************************************************** * Function Name : occli_custom_completion * Description : This Function used to handle the TAB operation * Input(s) : text, start, end * Output(s) : ***************************************************************************/ static char** occli_custom_completion(const char* text, int32_t start, int32_t end) { char** matches = NULL; int8_t ret = FAILED; if (text == NULL) { logerr("Invalid memory location \n"); rl_attempted_completion_over = 1; return NULL; } ret = occli_copy_text(); if (ret != SUCCESS) { rl_attempted_completion_over = 1; return NULL; } if (start == 0) { matches = rl_completion_matches(text, occli_all_param_generator); } else { rl_attempted_completion_over = 1; } return matches; } /************************************************************************** * Function Name : occli_strjoin * Description : This Function used to join the string * Input(s) : srcstr, delimstr * Output(s) : deststrPtr ***************************************************************************/ static int8_t occli_strjoin(char **deststrPtr, const char *srcstr, const char *delimstr) { char *tmp; const int32_t alloclen = OCCLI_STRING_SIZE; size_t destSize, deststrLen, srcstrLen, delimstrLen; if (deststrPtr == NULL || srcstr == NULL || delimstr == NULL) { logerr("NULL pointer error"); return -EFAULT; } if (*deststrPtr == NULL) { *deststrPtr = calloc(alloclen, sizeof(char)); if (*deststrPtr == NULL) { logerr("calloc error"); return -ENOMEM; } deststrLen = strlen(*deststrPtr); } else { delimstrLen = strlen(delimstr); srcstrLen = strlen(srcstr); deststrLen = strlen(*deststrPtr); destSize = ((deststrLen / alloclen) + 1) * alloclen; if ((srcstrLen + delimstrLen + 1) > (destSize - deststrLen)) { /* allocate more memory to concatenate the srcstr */ tmp = *deststrPtr; *deststrPtr = realloc(*deststrPtr, destSize + alloclen); if (*deststrPtr == NULL) { logerr("realloc error"); free(tmp); return -ENOMEM; } } } /* strcat the delimiting string to deststr, only when the deststr is not an empty string */ if (deststrLen > 0) { strcat(*deststrPtr, delimstr); } /* strcat the new string */ strcat(*deststrPtr, srcstr); return SUCCESS; } /************************************************************************** * Function Name : occli_alertthread_messenger_to_ocmw * Description : This Function is used to handle the alert message * Input(s) : pThreadData * Output(s) : ***************************************************************************/ void * occli_alertthread_messenger_to_ocmw(void *pThreadData) { char response[RES_STR_BUFF_SIZE] = {0}; int32_t ret = 0; /* Receive the CLI command execution response string from OCMW over UDP * socket */ while (1) { memset(response, 0, sizeof(response)); ret = occli_recv_alertmsg_from_ocmw(response, sizeof(response)); if (ret < 0) { printf("occli_recv_alertmsg_from_ocmw failed: error value : %d\n", ret); } else { printf("%s\n", response); } } } /************************************************************************** * Function Name : occli_parse_cliString * Description : This Function is used to check the validity of the * cli string * Input(s) : cliString * Output(s) : SUCCESS/FAILURE ***************************************************************************/ int8_t occli_parse_cliString(char *cliString) { int8_t ret = FAILED; int16_t index = 0; char tempStr[OCCLI_STRING_MAX_LEN] = {0}; char *token = NULL; if (cliString == NULL) { return ret; } strcpy(tempStr, cliString); token = strtok(tempStr, " "); for(index = 0; ((index < OCCLI_MAX_CLISTRING) && (s_allParams[index] != NULL)); index++) { if (strcmp(token, s_allParams[index]) == 0) { ret = SUCCESS; break; } } return ret; } /************************************************************************** * Function Name : occli_frame_commands * Description : This Function is used to frame the cli commands * Input(s) : root, occliData->option * Output(s) : s_allParams, occliData->totalStr, occliData->sizeNum ***************************************************************************/ int8_t occli_frame_commands(const Component *root, OCCLI_ARRAY_PARAM *occliData) { const Component *subSystem = root; const Component *component = subSystem->components; const Component *subComponent = NULL; const Command *command = NULL; const Driver *driver = NULL; if ((root == NULL) || (occliData == NULL)) { logerr("Invalid Memory \n"); return FAILED; } /* subsytem->component->command */ subSystem = root; while (subSystem && subSystem->name) { component = subSystem->components; while (component && component->name) { command = component->commands; while (command && command->name) { if (occliData->option == OCCLI_CALC_SCHEMA_SIZE) { occliData->sizeNum += 1; } else { snprintf(s_allParams[occliData->totalStr++], OCCLI_SNPRINTF_MAX_LEN, "%s.%s.%s", subSystem->name, component->name, command->name); } command += 1; } component += 1; } subSystem += 1; } /* subsytem->component->driver->command */ subSystem = root; while (subSystem && subSystem->name) { component = subSystem->components; while (component && component->name) { driver = component->driver; if(driver != NULL) { command = driver->commands; while (command && command->name) { if (occliData->option == OCCLI_CALC_SCHEMA_SIZE) { occliData->sizeNum += 1; } else { snprintf(s_allParams[occliData->totalStr++], OCCLI_SNPRINTF_MAX_LEN, "%s.%s.%s", subSystem->name, component->name, command->name); } command += 1; } } component += 1; } subSystem += 1; } /* subsytem->component->subComponent->driver->command */ subSystem = root; while (subSystem && subSystem->name) { component = subSystem->components; while (component && component->name) { subComponent = component->components; while (subComponent && subComponent->name) { driver = subComponent->driver; if(driver != NULL) { command = driver->commands; while (command && command->name) { if (occliData->option == OCCLI_CALC_SCHEMA_SIZE) { occliData->sizeNum += 1; } else { snprintf(s_allParams[occliData->totalStr++], OCCLI_STRING_MAX_LEN, "%s.%s.%s.%s", subSystem->name, component->name, subComponent->name, command->name); } command += 1; } } subComponent += 1; } component += 1; } subSystem += 1; } return SUCCESS; } /************************************************************************** * Function Name : occli_frame_string_from_schemaDriver * Description : This Function is used to frame the cli string * Input(s) : param, strFrame, msgTypeStr * Output(s) : ocmwclistr, occliData ***************************************************************************/ int8_t occli_frame_string_from_schemaDriver(const Parameter *param, OCCLI_ARRAY_PARAM *occliData, strMsgFrame *strFrame) { if ((occliData == NULL) || (param == NULL) || (strFrame == NULL)) { logerr("Invalid Memory \n"); return FAILED; } if (occliData->option == OCCLI_CALC_SCHEMA_SIZE) { occliData->sizeNum += 1; } else { if (strlen(strFrame->subcomponent)) { snprintf(s_allParams[occliData->totalStr++], OCCLI_SNPRINTF_MAX_LEN, "%s.%s.%s.%s.%s.%s", strFrame->subsystem, strFrame->component, strFrame->msgtype, strFrame->subcomponent, param->name, strFrame->parameter); } else { snprintf(s_allParams[occliData->totalStr++], OCCLI_SNPRINTF_MAX_LEN, "%s.%s.%s.%s.%s", strFrame->subsystem, strFrame->component, strFrame->msgtype, param->name, strFrame->parameter); } } return SUCCESS; } // TO use for POST /************************************************************************** * Function Name : occli_frame_string_from_postDriver * Description : This Function is used to frame the cli string * Input(s) : param, strFrame, msgTypeStr * Output(s) : ocmwclistr, occliData ***************************************************************************/ int8_t occli_frame_string_from_postDriver(const Post *param, OCCLI_ARRAY_PARAM *occliData, strMsgFrame *strFrame) { if ((occliData == NULL) || (param == NULL) || (strFrame == NULL)) { logerr("Invalid Memory \n"); return FAILED; } if (occliData->option == OCCLI_CALC_SCHEMA_SIZE) { occliData->sizeNum += 1; } else { if (strlen(strFrame->subcomponent)) { snprintf(s_allParams[occliData->totalStr++], OCCLI_SNPRINTF_MAX_LEN, "%s.%s.%s.%s.%s.%s", strFrame->subsystem, strFrame->component, strFrame->msgtype, strFrame->subcomponent, param->name, strFrame->parameter); } else { snprintf(s_allParams[occliData->totalStr++], OCCLI_SNPRINTF_MAX_LEN, "%s.%s.%s.%s.%s", strFrame->subsystem, strFrame->component, strFrame->msgtype, param->name, strFrame->parameter); } } return SUCCESS; } /************************************************************************** * Function Name : occli_frame_string_from_schema * Description : This Function is used to frame the cli string * Input(s) : devDriver, strFrame * Output(s) : ocmwclistr, occliData ***************************************************************************/ int8_t occli_frame_string_from_schema(const Driver *devDriver, OCCLI_ARRAY_PARAM *occliData, strMsgFrame *strFrame) { const Post *postParam = NULL; const Parameter *param = NULL; int8_t ret = SUCCESS; if ((devDriver == NULL) || (occliData == NULL) || (strFrame == NULL)) { logerr("Invalid Memory \n"); return FAILED; } postParam = devDriver->post; while (postParam && postParam->name) { strcpy(strFrame->msgtype, "post"); if (strncmp(postParam->name, "enable", strlen(postParam->name)) == 0) { strcpy(strFrame->parameter, "set"); } else { strcpy(strFrame->parameter, "get"); } ret = occli_frame_string_from_postDriver(postParam, occliData, strFrame); postParam++; } param = devDriver->config; while (param && param->name) { strcpy(strFrame->msgtype, "config"); strcpy(strFrame->parameter, "set"); ret = occli_frame_string_from_schemaDriver(param, occliData, strFrame); strcpy(strFrame->parameter, "get"); ret = occli_frame_string_from_schemaDriver(param, occliData, strFrame); param++; } param = devDriver->status; while (param && param->name) { strcpy(strFrame->msgtype, "status"); strcpy(strFrame->parameter, "get"); ret = occli_frame_string_from_schemaDriver(param, occliData, strFrame); param++; } #ifdef OCCLI_ALERT_STRING param = devDriver->alerts; strcpy(strFrame->msgtype, "alert"); ret = occli_frame_string_from_schemaDriver(param, occliData, strFrame); #endif return ret; } /************************************************************************** * Function Name : occli_frame_string * Description : This Function is used to frame the cli string * Input(s) : root * Output(s) : ocmwclistr, occliData ***************************************************************************/ int8_t occli_frame_string(const Component *root, OCCLI_ARRAY_PARAM *occliData) { const Component *component = NULL, *subComponent = NULL, *subSystem = root; const Driver *devDriver = NULL; int8_t ret = 0; strMsgFrame *strFrame = (strMsgFrame *) malloc(sizeof(strMsgFrame)); if ((strFrame == NULL) || (root == NULL) || (occliData == NULL)) { logerr("Invalid Memory \n"); return FAILED; } /* Config/Status/Alerts Table array entry Creation */ while (subSystem && subSystem->name) { memset(strFrame, '\0', sizeof(strMsgFrame)); strcpy(strFrame->subsystem, subSystem->name); component = subSystem->components; while (component && component->name) { memset(strFrame->subcomponent, '\0', OCCLI_CHAR_ARRAY_SIZE); strcpy(strFrame->component, component->name); devDriver = component->driver; if (devDriver != NULL) { ret = occli_frame_string_from_schema(devDriver, occliData, strFrame); if (ret == FAILED) { return ret; } } subComponent = component->components; while (subComponent && subComponent->name) { strcpy(strFrame->subcomponent, subComponent->name); devDriver = subComponent->driver; if (devDriver != NULL) { ret = occli_frame_string_from_schema(devDriver, occliData, strFrame); if (ret == FAILED) { return ret; } } subComponent += 1; } component += 1; } subSystem += 1; } free(strFrame); return SUCCESS; } /************************************************************************** * Function Name : main * Description : * Input(s) : argc, argv * Output(s) : ***************************************************************************/ int32_t main(int32_t argc, char *argv[]) { char *line = NULL; char *clistr = NULL; char response[RES_STR_BUFF_SIZE] = {0}; char historyFile[HIT_FILE_BUFF_SIZE]; char *cmdstr = NULL; const char* prompt = "opencellular# "; int32_t index = 0; int32_t ret = 0; pthread_t alertThreadId; sMsgParam msgFrameParam; OCCLI_ARRAY_PARAM occliArray; /* Initialize logging */ initlog("occli"); memset(&msgFrameParam, 0, sizeof(sMsgParam)); memset (&occliArray, 0, sizeof(OCCLI_ARRAY_PARAM)); occliArray.option = OCCLI_CALC_SCHEMA_SIZE; ret = occli_frame_string(sys_schema, &occliArray); if (ret != 0) { logerr("init_ocmw_comm() failed."); return FAILED; } ret = occli_frame_commands(sys_schema, &occliArray); if (ret != 0) { logerr("init_ocmw_comm() failed."); return FAILED; } for (index= 0; index< occliArray.sizeNum; index++) { if(!(s_allParams[index] = (char *)malloc(OCCLI_STRING_MAX_LEN))) { return FAILED; } } occliArray.option = OCCLI_PARSE_SCHEMA; ret = occli_frame_string(sys_schema, &occliArray); if (ret != 0) { logerr("init_ocmw_comm() failed."); return FAILED; } ret = occli_frame_commands(sys_schema, &occliArray); if (ret != 0) { logerr("init_ocmw_comm() failed."); return FAILED; } /* Initialize Middleware IPC communication */ ret = occli_init_comm(); if (ret != 0) { logerr("init_ocmw_comm() failed."); return FAILED; } ret = pthread_create(&alertThreadId, NULL, occli_alertthread_messenger_to_ocmw, NULL); if (ret != 0) { return ret; } /* Execute the OC command (argv[1:]) and exit */ if (strcmp("occmd", basename(argv[0])) == 0) { for (index= 1; index < argc; index++) { if (occli_strjoin(&cmdstr, argv[index], " ") != 0) { logerr("occli_strjoin error"); break; } } ret = occli_parse_cliString(cmdstr); if (ret != SUCCESS) { printf("%s : Error : Invalid String\n", cmdstr); free(cmdstr); cmdstr = NULL; } if (cmdstr != NULL) { occli_send_cmd_to_ocmw(cmdstr, strlen(cmdstr)); memset(response, 0, sizeof(response)); occli_recv_cmd_resp_from_ocmw(response, sizeof(response)); printf("%s\n", response); free(cmdstr); } else { printf("internal error\n"); } } /* Entering interactive CLI */ else if (strcmp("occli", basename(argv[0])) == 0) { /* Initialize readline */ using_history(); if ((snprintf(historyFile, HIT_FILE_BUFF_SIZE, "%s/%s", getenv("HOME"), HISTORY)) < 0) { return FAILED; } read_history(historyFile); rl_attempted_completion_function = occli_custom_completion; /*Printing hints at the start of opencelluar */ occli_print_opencelluar(); while (1) { line = readline(prompt); if (line == NULL) { break; } clistr = line; /* trim initial white spaces */ while (isblank(*clistr)) { ++clistr; } occli_trim_extra_spaces(clistr); if (strcmp(clistr, "") == 0) { continue; } /* Quit the CLI, when command 'quit' is received */ if (strcmp(clistr, "quit") == 0) { free(line); break; } /* Print the help manu */ if((strstr(clistr, "help"))) { if ((strstr(clistr, "--help"))) { ret = occli_printHelpMenu(sys_schema, clistr); if (ret == FAILED) { logerr("occli_printHelpMenu failed.\n"); } continue; } else { printf("%s : Error : Incorrect request\n", clistr); printf ("Usage : subsystem --help" " OR subsystem.component --help\n"); continue; } } ret = occli_parse_cliString(clistr); if (ret != SUCCESS) { printf("%s : Error : Invalid String\n", clistr); continue; } add_history(clistr); occli_send_cmd_to_ocmw(clistr, strlen(clistr)); memset(response, 0, sizeof(response)); occli_recv_cmd_resp_from_ocmw(response, sizeof(response)); /* Print the command execution results in the CLI */ printf("%s\n", response); free(line); } write_history(historyFile); } for (index= 0; ((s_allParams[index] != NULL) && (index < OCCLI_MAX_CLISTRING)); index++) { free(s_allParams[index]); } for (index= 0; ((s_subSetParams[index] != NULL) && (index < OCCLI_MAX_SUBSTRING)); index++) { free(s_subSetParams[index]); } occli_deinit_comm(); deinitlog(); return SUCCESS; }