/************************************************************************ Module: cli **** License **** Version: VPL 1.0 The contents of this file are subject to the Vyatta Public License Version 1.0 ("License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.vyatta.com/vpl Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. This code was originally developed by Vyatta, Inc. Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. All Rights Reserved. Author: Oleg Moskalenko Date: 2007 Description: "new" cli handler for the reference variables **** End License **** *************************************************************************/ #if !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cli_val_engine.h" /********************* * Data definitions * *********************/ /** * Special file names: */ #define VALUE_FILE ("node.val") #define NODE_TAG ("node.tag") #define NODE_DEF ("node.def") /** * "Command" definition (element of a variable path): */ typedef struct { clind_cmd_type cmd_type; const char* text[10]; } cmd_parse_definition; /** * Structure to hold information about possible command entries: */ static cmd_parse_definition cmd_parse_definitions[] = { { CLIND_CMD_PARENT_VALUE, {"..","@",NULL } }, { CLIND_CMD_NEIGHBOR, {"..","*",NULL } }, { CLIND_CMD_PARENT, {"..",NULL } }, { CLIND_CMD_PARENT, {".","..",NULL } }, { CLIND_CMD_CHILD, {".","*",NULL } }, { CLIND_CMD_VALUE, {".","@",NULL } }, { CLIND_CMD_SELF_NAME, {".",NULL } }, { CLIND_CMD_VALUE, {"@",NULL } }, { CLIND_CMD_MULTI_VALUE, {"@@",NULL } }, { CLIND_CMD_CHILD, {"*",NULL } }, { CLIND_CMD_UNKNOWN, {NULL} } }; /************************ * Cmd utils forward declarations * ************************/ static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd); /****************************** * Variable evaluation engine * * * ******************************/ /** * For a given config path, return the 'value' of the path. * If the path ends with "node.val", then return the file content. * If not, then return the last path element. * If path is empty, or the file is empty, or the file does not exist, * then return NULL. * The user of this function is responsible for the memory deallocation. */ static char** clind_get_current_value(clind_path_ref cfg_path, clind_path_ref tmpl_path, int check_existence, vtw_type_e *val_type, const char* root_tmpl_path, int return_value_file_name, int multi_value, int *ret_size) { char** ret=NULL; int value_ref = 0; *ret_size=0; DPRINT("get_current_value cfg[%s] tmpl[%s] chkexist=%d\n", clind_path_get_path_string(cfg_path), clind_path_get_path_string(tmpl_path), check_existence); if(val_type) *val_type=TEXT_TYPE; if(cfg_path && (clind_path_get_size(cfg_path)>0)) { clind_path_ref tmpl_path_clone = clind_path_clone(tmpl_path); const char* cfg_path_string = clind_path_get_path_string(cfg_path); const char* cfg_end = clind_path_last_string(cfg_path); const char* tmpl_path_string = clind_path_get_path_string(tmpl_path_clone); const char* tmpl_end = clind_path_last_string(tmpl_path_clone); /* printf("%s:111.111:%s:%s:%s:%s:%d\n",__FUNCTION__, cfg_path_string, cfg_end, tmpl_path_string, tmpl_end, multi_value); */ if(cfg_path_string && cfg_end) { if(strcmp(cfg_end,VALUE_FILE)==0) { /* Value reference: */ value_ref = 1; if(return_value_file_name) { ret=(char**)realloc(ret,sizeof(char*)*1); ret[0]=strdup(cfg_path_string); *ret_size=1; } else { FILE* f = fopen(cfg_path_string,"r"); if(f) { char buffer[8193]; if(multi_value) { while(fgets(buffer, sizeof(buffer)-1,f)) { int len=strlen(buffer); while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) { buffer[len-1]=0; len--; } if(len>0) { ret=(char**)realloc(ret,sizeof(char*)*(*ret_size+1)); ret[*ret_size]=strdup(buffer); *ret_size+=1; } } } else { int sz = fread(buffer, 1, sizeof(buffer)-1, f); if(sz>0) { int len=0; buffer[sz]=0; len=strlen(buffer); while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) { buffer[len-1]=0; len--; } ret=(char**)malloc(sizeof(char*)*1); ret[0]=strdup(buffer); *ret_size=1; } } fclose(f); } } } else if(return_value_file_name) { ret=(char**)realloc(ret,sizeof(char*)*1); ret[0]=(char*)malloc(strlen(cfg_path_string)+1+strlen(VALUE_FILE)+1); strcpy(ret[0],cfg_path_string); strcpy(ret[0]+strlen(ret[0]),"/"); strcpy(ret[0]+strlen(ret[0]),VALUE_FILE); *ret_size=1; } else { struct stat statbuf; /* Directory reference: */ if(!check_existence || (lstat(cfg_path_string, &statbuf) == 0)) { ret=(char**)realloc(ret,sizeof(char*)*1); ret[0]=clind_unescape(cfg_end); *ret_size=1; } else { /* we are checking existence, and it doesn't exist */ /* return empty string */ ret = (char**) realloc(ret, (sizeof(char *) * 1)); ret[0] = malloc(1); ret[0][0] = 0; *ret_size = 1; } } if(ret) { if(tmpl_end && (strcmp(tmpl_end,NODE_TAG)==0)) { /* since it's a tag, it should be treated as a value */ value_ref = 1; clind_path_pop(tmpl_path_clone); tmpl_path_string = clind_path_get_path_string(tmpl_path_clone); tmpl_end = clind_path_last_string(tmpl_path_clone); } } if(ret && tmpl_path_string && value_ref && !return_value_file_name) { vtw_def def; struct stat statbuf; int fn_node_def_size=strlen(tmpl_path_string)+1+strlen(NODE_DEF)+1; char* fn_node_def=(char*)malloc(fn_node_def_size); memset(&def, 0, sizeof(def)); fn_node_def[0]=0; if(*tmpl_path_string!='/' && root_tmpl_path) { fn_node_def_size+=strlen(root_tmpl_path+1); fn_node_def=(char*)realloc(fn_node_def,fn_node_def_size); strcpy(fn_node_def+strlen(fn_node_def),root_tmpl_path); strcpy(fn_node_def+strlen(fn_node_def),"/"); } strcpy(fn_node_def+strlen(fn_node_def),tmpl_path_string); strcpy(fn_node_def+strlen(fn_node_def),"/"); strcpy(fn_node_def+strlen(fn_node_def),NODE_DEF); if ((lstat(fn_node_def, &statbuf) == 0)&& (parse_def(&def, fn_node_def, TRUE)==0)) { if(def.def_type != ERROR_TYPE) { int status=0; valstruct res; int i=0; memset(&res,0,sizeof(res)); for(i=0;i<*ret_size;i++) { if(ret[i]) { /* return the value in the correct type */ status = char2val(&def, ret[i], &res); if(status==0) { if(val_type) *val_type=res.val_type; if(res.free_me && res.val) { free(ret[i]); ret[i]=res.val; } } else { /* Bad value ? */ } } } } } else { while(*ret_size>0) { if(ret[*ret_size-1]) { free(ret[*ret_size-1]); } *ret_size-=1; } free(ret); ret=NULL; } free(fn_node_def); } } clind_path_destruct(&tmpl_path_clone); } return ret; } /** * Return TRUE if current node is a multi-node value */ static int is_multi_node(clind_path_ref tmpl_path) { int ret=0; if(tmpl_path) { const char* t_end = clind_path_last_string(tmpl_path); if(t_end && (strcmp(t_end,NODE_TAG)==0)) { ret=1; } } return ret; } /** * Return TRUE if current node is node.def */ static int is_node_def(clind_path_ref tmpl_path) { int ret=0; if(tmpl_path) { const char* t_end = clind_path_last_string(tmpl_path); if(t_end && (strcmp(t_end,NODE_DEF)==0)) { ret=1; } } return ret; } /** * Apply a single command to the configuration path. * cfg_path - absolute configuration path, * tmpl_path - logical template path, * cmd - single reference command from the variable path. * The result is the array of "derived" paths. * result_len output parameter contains the array size. */ static clind_path_ref* clind_config_engine_apply_command(clind_path_ref cfg_path, clind_path_ref tmpl_path, clind_cmd *cmd, int *result_len) { clind_path_ref* ret=NULL; DPRINT("eng_apply_cmd cfg=[%s] tmpl=[%s] type=%d\n", clind_path_get_path_string(cfg_path), clind_path_get_path_string(tmpl_path), cmd->type); if(cfg_path && tmpl_path && result_len && cmd) { /* printf("%s:111.111:%s:%s:%d\n",__FUNCTION__, clind_path_get_path_string(cfg_path), clind_path_get_path_string(tmpl_path), cmd->type); */ switch (cmd->type) { case CLIND_CMD_PARENT: { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; if(is_multi_node(tmpl_path)) { clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } else if(is_node_def(tmpl_path)) { clind_path_pop(tmpl_path); } clind_path_pop(cfg_path); clind_path_pop(tmpl_path); if(is_multi_node(tmpl_path)) { clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } } break; case CLIND_CMD_SELF_NAME: { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; if(is_multi_node(tmpl_path)) { clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } else if(is_node_def(tmpl_path)) { clind_path_pop(tmpl_path); } } break; case CLIND_CMD_CHILD: { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; clind_path_push(cfg_path,cmd->value); clind_path_push(tmpl_path,cmd->value); } break; case CLIND_CMD_NEIGHBOR: { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; if(is_multi_node(tmpl_path)) { clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } else if(is_node_def(tmpl_path)) { clind_path_pop(tmpl_path); } clind_path_pop(cfg_path); clind_path_pop(tmpl_path); clind_path_push(cfg_path,cmd->value); clind_path_push(tmpl_path,cmd->value); } break; case CLIND_CMD_VALUE: { const char* t_path_string = clind_path_get_path_string(tmpl_path); const char* t_end = clind_path_last_string(tmpl_path); const char* c_end = clind_path_last_string(cfg_path); *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; if(t_end && (strcmp(t_end,NODE_TAG)==0)) { /* do nothing, we are there already */ } else if(t_path_string && clind_file_exists(t_path_string,NODE_TAG)) { clind_path_push(tmpl_path,NODE_TAG); } else if(c_end && (strcmp(c_end,VALUE_FILE)==0)) { /* do nothing, we are there already */ } else { clind_path_push(cfg_path,VALUE_FILE); } } break; case CLIND_CMD_PARENT_VALUE: { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; if(is_multi_node(tmpl_path)) { clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } else if(is_node_def(tmpl_path)) { clind_path_pop(tmpl_path); } clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } break; case CLIND_CMD_MULTI_VALUE: { const char* cfg_path_string = NULL; if(is_multi_node(tmpl_path)) { clind_path_pop(cfg_path); clind_path_pop(tmpl_path); } else if(is_node_def(tmpl_path)) { clind_path_pop(tmpl_path); } cfg_path_string = clind_path_get_path_string(cfg_path); if(cfg_path_string) { const char* c_end = clind_path_last_string(cfg_path); if(c_end && (strcmp(c_end,VALUE_FILE)==0)) { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; } else if(clind_file_exists(cfg_path_string,VALUE_FILE)) { *result_len=1; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); cfg_path = clind_path_clone(cfg_path); ret[0]=cfg_path; clind_path_push(cfg_path,VALUE_FILE); } else { DIR* dir=NULL; dir = opendir(cfg_path_string); if(dir) { *result_len=0; ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); do { struct dirent *de = readdir(dir); if(!de) break; else if(de->d_name[0] && de->d_name[0]!='.') { clind_path_ref cfg_path_1 = clind_path_clone(cfg_path); clind_path_push(cfg_path_1,de->d_name); (*result_len)++; ret=(clind_path_ref*)(realloc(ret,*result_len * sizeof(clind_path_ref))); ret[*result_len-1]=cfg_path_1; } } while(1); clind_path_push(tmpl_path,NODE_TAG); closedir(dir); } } } } break; default: ; } } return ret; } /** * cfg_path - absolute configuration path, * tmpl_path - logical template path, * cmd_path - variable command path. */ int clind_config_engine_apply_command_path(clind_path_ref cfg_path_orig, clind_path_ref tmpl_path_orig, clind_path_ref cmd_path, int check_existence, clind_val* res, const char* root_cfg_path, const char* root_tmpl_path, int return_value_file_name) { int ret=-1; /* printf("%s:111.111:cfg_path=%s,tmpl_path=%s,cmd_path=%s,rtp=%s\n",__FUNCTION__, clind_path_get_path_string(cfg_path_orig), clind_path_get_path_string(tmpl_path_orig), clind_path_get_path_string(cmd_path), root_tmpl_path); */ DPRINT("eng_apply_cmd_path cfg=[%s] tmpl=[%s] cmd=[%s] " "rcfg=[%s] rtmpl=[%s]\n", clind_path_get_path_string(cfg_path_orig), clind_path_get_path_string(tmpl_path_orig), clind_path_get_path_string(cmd_path), root_cfg_path, root_tmpl_path); if(cfg_path_orig && tmpl_path_orig && cmd_path && res) { /* Command to be processed: */ clind_cmd cmd; /* Array of configuration pointers. Initially, contains only one element - cfg_path. */ clind_path_ref* config_paths= (clind_path_ref*)malloc(sizeof(clind_path_ref)*1); /* Size of the array (initially - just one): */ int config_paths_size=1; /* Clone the input paths to preserve the input objects intact: */ clind_path_ref tmpl_path=NULL; clind_path_ref cfg_path=NULL; if(clind_path_is_absolute(cmd_path)) { tmpl_path=clind_path_construct(root_tmpl_path); if(!tmpl_path) { return -1; } cfg_path=clind_path_construct(root_cfg_path); if(!cfg_path) { return -1; } } else { cfg_path=clind_path_clone(cfg_path_orig); tmpl_path=clind_path_clone(tmpl_path_orig); } res->value=NULL; res->val_type=TEXT_TYPE; /* Set the initial array content: */ config_paths[0]=cfg_path; /* Apply the commands one-by-one: */ while(clind_path_get_size(cmd_path)>0 && (clind_path_shift_cmd(cmd_path,&cmd)==0)) { int i=0; /* Temporary array to keep the config paths for the next command application: */ clind_path_ref* new_config_paths=NULL; int new_config_paths_size=0; /* This path contains the template path at the beginning of the cycle: */ clind_path_ref tmpl_path_curr=clind_path_clone(tmpl_path); for (i=0;ival_type = val_type; for(k=0;kvalue=sarr[0]; } else { for(i=0;ivalue) { res->value=s; } else if(res->value[0]==0) { free(res->value); res->value=s; } else { res->value=(char*)realloc(res->value, strlen(res->value)+1+strlen(s)+1); strcpy(res->value+strlen(res->value)," "); strcpy(res->value+strlen(res->value),s); free(s); } } } } } free(sarr); } } clind_path_destruct(&cmd_path); clind_path_destruct(&cfg_path); clind_path_destruct(&tmpl_path); } return ret; } /****************************** * Cmd utils. * ******************************/ static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd) { int ret=-1; if(cmd) { cmd->type = CLIND_CMD_UNKNOWN; cmd->value[0]=0; if(path && clind_path_get_size(path)>0) { int i=0; int done=0; while(cmd_parse_definitions[i].text!=NULL && cmd_parse_definitions[i].text[0]!=NULL) { int j=0; while(cmd_parse_definitions[i].text[j]) { const char* str = clind_path_get_string(path,j); if(str) { if(!strcmp(cmd_parse_definitions[i].text[j],"*")) { if(*str!='.' && *str!='@') { j++; strncpy(cmd->value,str,sizeof(cmd->value)-1); continue; } } else if(!strcmp(cmd_parse_definitions[i].text[j],str)) { j++; strncpy(cmd->value,str,sizeof(cmd->value)-1); continue; } } j=0; break; } if(j<1) { i++; continue; } else { done=1; } cmd->type = cmd_parse_definitions[i].cmd_type; while(j) { clind_path_shift(path); j--; } break; } if(done) { ret=0; } } } return ret; }