/************************************************************************ Module: cli **** License **** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. A copy of the GNU General Public License is available as `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'. You can also obtain it by writing to the Free Software Foundation, Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 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 #include "cli_objects.h" #include "cli_val_engine.h" static int is_multi_node(clind_path_ref tmpl_path); static boolean is_deactivated(const char *path, int in_active); /********************* * 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. * * in_active is for the deactivated check. */ 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, int in_active) { char** ret=NULL; int value_ref = 0; *ret_size=0; if (check_existence) { if (is_multi_node(tmpl_path)) { check_existence = FALSE; } } 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 { if (is_deactivated(clind_path_get_path_string(cfg_path), in_active) == FALSE) { 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) && is_deactivated(cfg_path_string, in_active) == FALSE)) { 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); 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)&& (is_deactivated(fn_node_def, in_active) == FALSE)&& (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. * * note: root_cfg_path should not be necessary as absolute paths are already * handled before this is called (see set_reference_environment() in * cli_new.c). cli_new.c was passing the wrong path (changes only) * anyway, causing problems with absolute paths (bug 5213). * * root_tmpl_path should not be necessary either but it's * used in one place in clind_get_current_value() (it's not clear * if that case is ever reached), so keep it for now. * * in_active is needed for the deactivated check. if operating on * active, deactivated check should be in active as well. this * makes a difference for nodes that are being deactivated (i.e., * deactivated in working but not in active). */ 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_tmpl_path, int return_value_file_name, int in_active) { 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] " "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_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; /* absolute paths have already been handled, i.e., the _orig paths * can be used directly. */ 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; } boolean is_deactivated(const char *path_string, int in_active) { int num = 0; boolean ret = FALSE; char **path_comps = cstore_path_string_to_path_comps(path_string, &num); void *csh = cstore_init(); /* XXX this lib should operate on "logical paths" only, but currently * it is using physical paths. convert to logical paths (remove the * prefix) to use the cstore library. * * XXX also, use is_in_delete_action() as in_active arg for the call. * this follows the original behavior that when in delete action, var * refs are obtained from the active config. the result of the original * behavior is that if a node is being deleted (i.e., in active but not * in working), a var ref for the node will still return the active value. * * by passing is_in_delete_action() here, the result is that if a node is * being deactivated (i.e., deactivated in working but not in active), * a var ref for the node will still return the active value. * * the original behavior may not be correct, but for compatibility * it's emulated here. should revisit later. */ if (num > 5 && cstore_cfg_path_deactivated(csh, (const char **) &(path_comps[5]), num - 5, in_active)) { ret = TRUE; } cstore_free_path_comps(path_comps, num); cstore_free(csh); return ret; }