diff options
Diffstat (limited to 'src/cli_val_engine.c')
-rw-r--r-- | src/cli_val_engine.c | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/src/cli_val_engine.c b/src/cli_val_engine.c new file mode 100644 index 0000000..d597c68 --- /dev/null +++ b/src/cli_val_engine.c @@ -0,0 +1,881 @@ + +/************************************************************************ + + 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <regex.h> +#include <dirent.h> + +#include <string.h> + +#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; + *ret_size=0; + + 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: */ + + 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; + } + } + + if(ret) { + if(tmpl_end && (strcmp(tmpl_end,NODE_TAG)==0)) { + 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 && !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; + + 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); + */ + + 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;i<config_paths_size;i++) { + + int size=0; + + clind_path_ref* new_config_paths_1=NULL; + + if(i==0) { + new_config_paths_1= + clind_config_engine_apply_command(config_paths[i], + tmpl_path, + &cmd, + &size); + } else { + clind_path_ref tmpl_path_curr_clone=clind_path_clone(tmpl_path_curr); + new_config_paths_1= + clind_config_engine_apply_command(config_paths[i], + tmpl_path_curr_clone, + &cmd, + &size); + clind_path_destruct(&tmpl_path_curr_clone); + } + + if(new_config_paths_1) { + + int j=0; + + for(j=0;j<size;j++) { + if(new_config_paths_1[j]) { + new_config_paths_size++; + new_config_paths= + (clind_path_ref*)realloc(new_config_paths, + sizeof(clind_path_ref)* + new_config_paths_size); + new_config_paths[new_config_paths_size-1]=new_config_paths_1[j]; + } + } + + free(new_config_paths_1); + } + + clind_path_destruct(&(config_paths[i])); + } + + free(config_paths); + config_paths=new_config_paths; + config_paths_size=new_config_paths_size; + clind_path_destruct(&tmpl_path_curr); + } + + if(config_paths) { + + char** sarr=NULL; + int sarrlen=0; + + vtw_type_e val_type=TEXT_TYPE; + + int i=0; + + for(i=0;i<config_paths_size;i++) { + + if(config_paths[i]) { + + int vallen=0; + + char** valarr=clind_get_current_value(config_paths[i], + tmpl_path, + check_existence, + &val_type, + root_tmpl_path, + return_value_file_name, + /*Last command: */ + (cmd.type==CLIND_CMD_MULTI_VALUE), + &vallen); + + clind_path_destruct(&config_paths[i]); + + if(valarr) { + + int k=0; + + for(k=0;k<vallen;k++) { + + char* s=valarr[k]; + + if(s) { + + /* search if we already have it: */ + int j=0; + for(j=0;j<sarrlen;j++) { + if(!strcmp(sarr[j],s)) { + break; + } + } + + if(j<sarrlen) { + free(s); + } else { + sarrlen++; + sarr=(char**)realloc(sarr,sizeof(char*)*sarrlen); + sarr[sarrlen-1]=s; + } + } + } + free(valarr); + } + } + } + + free(config_paths); + + if(sarr) { + + ret=0; + + if(sarrlen==1) { + + res->value=sarr[0]; + + } else { + + for(i=0;i<sarrlen;i++) { + + if(sarr[i]) { + + char* s = clind_quote(sarr[i]); + + free(sarr[i]); + sarr[i]=NULL; + + if(s) { + + if(!res->value) { + + 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; +} + |