#include #include #include #include #include #include #include #include #include #include #include #include #include #include "cli_val.h" #include "cli_parse.h" #include #include "cli_objects.h" #include "cli_val_engine.h" /* Defines: */ #define EXE_STRING_DELTA 512 #define PATH_DELTA 1000 #define ENDS_ALLOC 20 #define PATH_CM_LOCATION 25 /* mcd vs. ccd location change when m_root changed */ /* Global vars: */ vtw_path m_path, t_path; /* Loval vars: */ static vtw_node *vtw_free_nodes; /* linked via left */ static char val_name[] = VAL_NAME; static int cond1[TOP_COND] ={5, 0,-1,-1, 0, 1, 0, 0}; static int cond2[TOP_COND] ={5, 0, 1,-1,-1, 1, 1, 0}; static char const *cond_formats[DOMAIN_TYPE] = { 0, "%u", /* INT_TYPE */ "%u.%u.%u.%u", /*IPV4_TYPE*/ "%u.%u.%u.%u/%u", /*IPV4NET_TYPE*/ 0, 0, "%x:%x:%x:%x:%x:%x" /* MACADDR_TYPE */ }; static int cond_format_lens[DOMAIN_TYPE] = { 0, 1, /* INT_TYPE */ 4, /*IPV4_TYPE*/ 5, /*IPV4NET_TYPE*/ 0, 0, 6 /* MACADDR_TYPE */ }; static int cli_val_len; static char *cli_val_alloc; static char *cli_val_ptr; static char *exe_string; static int exe_string_len; static int node_cnt; static int free_node_cnt; static boolean in_validate_val; static valstruct validate_value_val; /* value being validated to be used as $(@) */ /* Local function declarations: */ static int check_comp(vtw_node *cur); static boolean check_syn_func(vtw_node *cur,const char* func,int line); #define check_syn(cur) check_syn_func((cur),__FUNCTION__,__LINE__) void copy_path(vtw_path *to, vtw_path *from); static int eval_va(valstruct *res, vtw_node *node); static int expand_string(char *p); static void free_node(vtw_node *node); static void free_node_tree(vtw_node *node); static void free_reuse_list(void); void free_path(vtw_path *path); static void free_string(char *str); static vtw_node * get_node(void); static void scan_ipv6(char *val, unsigned int *parts); static int set_reference_environment(const char* var_reference, clind_path_ref *n_cfg_path, clind_path_ref *n_tmpl_path, clind_path_ref *n_cmd_path, int active); /************************************************* GLOBAL FUNCTIONS ***************************************************/ char *cli_operation_name = NULL; /* it is executed as "eval `my_set` in order to be able to modify BASH env therefore, all error will be reported as printf("echo \"bla-bla-bla%s\";", sptr) note very important ';' as the end of the format */ void bye(char *msg, ...) { va_list ap; fprintf(out_stream, "%s failed\n", (cli_operation_name) ? cli_operation_name : "Operation"); if (is_silent_msg()) exit(-1); va_start(ap, msg); if (is_echo()) printf("echo \""); vprintf(msg, ap); printf(is_echo()? "\";":"\n"); va_end(ap); exit(-1); } /* msg: print message, preceeded by "echo " if global flag echo set. This flag is used by program which are executed as eval `command` in order to modify BASH env */ void print_msg(char *msg, ...) { va_list ap; if (is_silent_msg()) return; va_start(ap, msg); if (is_echo()) printf("echo \""); vprintf(msg, ap); printf(is_echo()? "\";":"\n"); va_end(ap); } void touch_dir(const char *dp) { struct stat statbuf; if (lstat(dp, &statbuf) < 0) { char *command; command = my_malloc(strlen(dp) + 10, "set"); sprintf(command, "mkdir -p %s", dp); system(command); free(command); return; } if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { bye("directory %s expected, found regular file", dp); } return; } /***************************************************** add_val: verify that the types are the same; if first valstruct is single value, convert it into multivalue; add the value of second to the list of first; *****************************************************/ void add_val(valstruct *first, valstruct *second) { assert (first->free_me && second->free_me); assert(second->cnt == 0); if (first->val_type != second->val_type) { printf("Different types\n\n"); } else { if (first->cnt%MULTI_ALLOC == 0) { /* convert into multivalue */ first->vals = my_realloc(first->vals, (first->cnt + MULTI_ALLOC) * sizeof(char *), "add_value"); if (first->cnt == 0) { /* single value - convert */ first->vals[0] = first->val; first->cnt = 1; first->val = NULL; } } second->free_me = FALSE; /* we took its string */ first->vals[first->cnt] = second->val; ++first->cnt; } } /***************************************************** append - append node to the tail of list *****************************************************/ void append(vtw_list *l, vtw_node *n, int aux) { vtw_node *lnode; lnode = make_node(LIST_OP, n, NULL); lnode->vtw_node_aux = aux; if(l->vtw_list_tail) { assert(l->vtw_list_tail->vtw_node_right == NULL); l->vtw_list_tail->vtw_node_right = lnode; } else { assert(l->vtw_list_head == NULL); l->vtw_list_head = lnode; } l->vtw_list_tail = lnode; } void dt(vtw_sorted *srtp) { int i; for (i=0; inum; ++i) printf("%d %s\n", i, (char *)(srtp->ptrs[i])); } void di(vtw_sorted *srtp) { int i; for (i=0; inum; ++i) printf("%u %u\n", i, *(unsigned int *)(srtp->ptrs[i])); } #define LOCK_FILE "/opt/vyatta/config/.lock" static void release_config_lock() { unlink(LOCK_FILE); /* error ignored */ } int get_config_lock() { int fd = open(LOCK_FILE, O_WRONLY | O_CREAT | O_EXCL, 0660); if (fd == -1) { return -1; } if (close(fd) == -1) { release_config_lock(); return -1; } if (atexit(release_config_lock) != 0) { release_config_lock(); return -1; } return 0; } void internal_error(int line, char *file) { printf("\n\nInternal Error at line %d in %s\n", line, file); exit (-1); } /************************************************* vtw_sort: create sorted structure for the value, allocates ptrs and parts in this structure */ void vtw_sort(valstruct *valp, vtw_sorted *sortp) { int i; const char * format; unsigned int *parts; vtw_type_e type = valp->val_type; char *cp; sortp->num = valp->cnt?valp->cnt : 1; #ifdef CLI_DEBUG printf("vtw_sort type=%d num=%d\n", type, sortp->num); for (i = 0; i < sortp->num; i++) { printf(" [%s]\n", (valp->cnt) ? valp->vals[i] : valp->val); } #endif sortp->ptrs = my_malloc(sortp->num * sizeof(void *), "sort_ptrs"); sortp->partnum = (type < DOMAIN_TYPE) ? cond_format_lens[type] : 0; if (sortp->partnum) { sortp->parts = my_malloc(sortp->partnum * sortp->num * sizeof(void *), "sort_parts"); }else{ sortp->parts = NULL; } switch (type){ case IPV6_TYPE: case IPV6NET_TYPE: for (i = 0; i < sortp->num; ++i) { parts = sortp->parts + i * sortp->partnum; scan_ipv6(valp->cnt?valp->vals[i]:valp->val, parts); sortp->ptrs[i] = parts; } break; case IPV4_TYPE: case IPV4NET_TYPE: case MACADDR_TYPE: case INT_TYPE: format = cond_formats[valp->val_type]; for (i = 0; i < sortp->num; ++i) { cp = valp->cnt?valp->vals[i]:valp->val; parts = sortp->parts + i * sortp->partnum; switch (sortp->partnum) { case 1: (void) sscanf(cp, format, parts); break; case 2: (void) sscanf(cp, format, parts, parts+1); break; case 3: (void) sscanf(cp, format, parts, parts+1, parts+2); break; case 4: (void) sscanf(cp, format, parts, parts+1, parts+2, parts+3); break; case 5: (void) sscanf(cp, format, parts, parts+1, parts+2, parts+3, parts+4); break; case 6: (void) sscanf(cp, format, parts, parts+1, parts+2, parts+3, parts+4, parts+5); break; } sortp->ptrs[i] = parts; } break; case TEXT_TYPE: case BOOL_TYPE: for (i = 0; i < sortp->num; ++i) { sortp->ptrs[i] = valp->cnt?valp->vals[i]:valp->val; } break; default: bye("Unknown value in switch on line %d\n", __LINE__); } if (sortp->num < 2) return; #ifdef CLI_DEBUG if (sortp->parts) { int i, j; printf("sortp parts:\n"); for (i = 0; i < sortp->num; i++) { printf(" "); unsigned int *parts = (unsigned int *) sortp->ptrs[i]; for (j = 0; j < sortp->partnum; j++) { printf("%u ", parts[j]); } printf("\n"); } } #endif /* the following sort throws away the input ordering. */ /* NOT doing this for now */ #if 0 /* now do a heap sort */ /* build heap */ /* from left to right, we start with the heap of only one (first) element*/ for (i = 2; i <= sortp->num; ++i) { cur = i; do { curp = sortp->ptrs[cur - 1]; par = cur >> 1; parp = sortp->ptrs[par - 1]; if (sortp->partnum){ for(partnum = 0; partnum < sortp->partnum; ++partnum) { if (*((unsigned int *)curp + partnum)> *((unsigned int *)parp + partnum)){ res = 1; break; } if (*((unsigned int *)curp + partnum)< *((unsigned int *)parp + partnum)){ res = -1; break; } res = 0; } }else{ res = strcmp((char *)curp, (char *) parp); } if (res <= 0) break; /* swap them */ sortp->ptrs[cur - 1] = parp; sortp->ptrs[par - 1] = curp; } while ((cur = par) != 1); } /* convert heap into sorted array */ unsorted = sortp->num; /* sortp->num must be >= 2 */ while (TRUE) { void *tp; /* root to the sorted part */ tp = sortp->ptrs[0]; sortp->ptrs[0] = sortp->ptrs[--unsorted]; sortp->ptrs[unsorted] = tp; if (unsorted == 1) break; /* push down the new root */ par = 1; while(TRUE) { left = par << 1; /* left child */ if (left > unsorted) break; /* no children */ else { if (left == unsorted) { /* only left child */ child = left; } else { /* both children */ right = left+1; leftp = sortp->ptrs[left - 1]; rightp = sortp->ptrs[right - 1]; /* find larger child */ if (sortp->partnum){ for(partnum = 0; partnum < sortp->partnum; ++partnum) { if (*((unsigned int *)leftp + partnum) > *((unsigned int *)rightp + partnum)) { res = 1; break; } if (*((unsigned int *)leftp + partnum) < *((unsigned int *)rightp + partnum)) { res = -1; break; } res = 0; } }else{ res = strcmp((char *)leftp, (char *) rightp); } if (res >= 0) { child = left; /* left is larger or same*/ } else { child = right; } } /* compare parent and larger child */ parp = sortp->ptrs[par - 1]; childp = sortp->ptrs[child - 1]; if (sortp->partnum){ for(partnum = 0; partnum < sortp->partnum; ++partnum) { if (*((unsigned int *)parp + partnum) > *((unsigned int *)childp + partnum)){ res = 1; break; } if (*((unsigned int *)parp + partnum) < *((unsigned int *)childp + partnum)){ res = -1; break; } res = 0; } }else{ res = strcmp((char *)parp, (char *) childp); } if (res >= 0) { /* done with percolating down, parent larger than child */ break; } /* child greater, exchage and continue */ sortp->ptrs[par - 1] = childp; sortp->ptrs[child - 1] = parp; par = child; } } } #endif } /* returns FALSE if execution returns non-null, returns TRUE if every excution returns NULL */ boolean execute_list(vtw_node *cur, vtw_def *def) { boolean ret; int status; set_in_exec(TRUE); status = char2val(def, get_at_string(), &validate_value_val); if (status) return FALSE; ret = check_syn(cur); free_val(&validate_value_val); set_in_exec(FALSE); return ret; } /***************************************************** make_node - create a node with oper, left, and right *****************************************************/ vtw_node * make_node(vtw_oper_e oper, vtw_node *left, vtw_node *right) { vtw_node *ret ; ret = get_node(); ret->vtw_node_oper = oper; ret->vtw_node_left = left; ret->vtw_node_right = right; ret->vtw_node_string = NULL; ret->vtw_node_aux = 0; return ret; } vtw_node *make_str_node0(char *str, vtw_oper_e op) { vtw_node *ret; ret = make_node(op, NULL, NULL); ret->vtw_node_string = str; ret->vtw_node_type = TEXT_TYPE; return ret; } /***************************************************** make_str_node - create a VAL_OP node with str *****************************************************/ vtw_node *make_str_node(char *str) { return make_str_node0(str, VAL_OP); } /***************************************************** make_var_node - create a VAR_OP node with str *****************************************************/ vtw_node *make_var_node(char *str) { return make_str_node0(str, VAR_OP); } /***************************************************** make_val_node - create a VAl_OP node with str *****************************************************/ vtw_node *make_val_node(valstruct *val) { vtw_node *ret; assert(val->free_me); ret = make_node(VAL_OP, NULL, NULL); ret->vtw_node_val = *val; val->free_me = FALSE; return ret; } valstruct str2val(char *cp) { valstruct ret; memset(&ret, 0, sizeof(ret)); ret.val_type = TEXT_TYPE; ret.val = cp; ret.free_me = TRUE; return ret; } /**************************************************** STATIC FUNCTIONS ****************************************************/ /************************************************** char2val: convert string into valstruct verifying the type according to def ****************************************************/ int char2val(vtw_def *def, char *value, valstruct *valp) { int token; char *endp, *cp; int linecnt, cnt; int my_type = def->def_type; boolean first = TRUE; memset(valp, 0, sizeof (*valp)); if (my_type == ERROR_TYPE) { my_type = TEXT_TYPE; } if (my_type != TEXT_TYPE && my_type != ERROR_TYPE) { cli_val_len = strlen(value); cli_val_ptr = value; while(1) { token = yy_cli_val_lex(); if (token != VALUE) { if (first || token){ if (def->def_type_help){ set_at_string(value); (void)expand_string(def->def_type_help); fprintf(out_stream, "%s\n", exe_string); } else { print_msg("Wrong type of value in %s, " "need %s\n", m_path.path_buf + m_path.print_offset, type_to_name(my_type)); fprintf(out_stream, "\"%s\" is not a valid value of type \"%s\"\n", value, type_to_name(my_type)); } return -1; } return 0; } if (my_type != get_cli_value_ptr()->val_type) { if (def->def_type_help){ set_at_string(value); (void)expand_string(def->def_type_help); fprintf(out_stream, "%s\n", exe_string); } else { print_msg("Wrong type of value in %s, " "need %s\n", m_path.path_buf + m_path.print_offset, type_to_name(my_type)); fprintf(out_stream, "\"%s\" is not a valid value of type \"%s\"\n", value, type_to_name(my_type)); } my_free(get_cli_value_ptr()->val); if (first) return -1; return 0; } if (first) { *valp = *get_cli_value_ptr(); get_cli_value_ptr()->free_me = FALSE; first = FALSE; } else { if (def->multi) add_val(valp, get_cli_value_ptr()); else { print_msg("Unexpected multivalue in %s\n", m_path.path); free_val(get_cli_value_ptr()); } } token = yy_cli_val_lex(); if (!token) return 0; if (token != EOL) { print_msg("Badly formed value in %s\n", m_path.path + m_path.print_offset); if (token == VALUE) my_free(get_cli_value_ptr()->val); return 0; } } return 0; } valp->val_type = TEXT_TYPE; valp->free_me = TRUE; /* count lines */ linecnt = 0; for (cp = value; *cp; ++cp) if (*cp == '\n') ++linecnt; if (cp != value && cp[-1] != '\n') ++linecnt; /* last non empty non \n terminated string */ if (linecnt == 0) /* one empty non terminated string */ linecnt = 1; if (linecnt == 1) { valp->val=my_strdup(value, "char2val 1"); /*truncate '\n' etc */ endp = strchr(valp->val, '\n'); if (endp) *endp = 0; } else { valp->cnt = linecnt; cnt = (linecnt + MULTI_ALLOC - 1) / MULTI_ALLOC; cnt *= MULTI_ALLOC; valp->vals = my_malloc(cnt * sizeof(char *), "char2val 2"); for(cp = value, cnt = 0; cnt < linecnt; ++cnt) { endp = strchr(cp, '\n'); if (endp) *endp = 0; valp->vals[cnt]=my_strdup(cp, "char2val 3"); if (endp) { *endp = '\n'; cp = endp + 1; } else { /* non '\n' terinated string, must be last line, we are done */ ++cnt; assert(cnt == linecnt); break; } } } return 0; } /**************************************************** val_comp: compare two values per cond returns result of comparison ****************************************************/ boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond) { unsigned int left_parts[9], right_parts[9]; vtw_type_e val_type; int parts_num, lstop, rstop, lcur, rcur; char const *format; char *lval, *rval; int ret=0, step=0, res=0; val_type = left->val_type; if (left->cnt) lstop = left->cnt; else lstop = 1; if (right->cnt) rstop = right->cnt; else rstop = 1; DPRINT("val_cmp: type=%d count=(%d,%d) val=(%s,%s)\n", val_type, lstop, rstop, left->val, right->val); for(lcur = 0; lcur < lstop; ++lcur) { if (!lcur && !left->cnt) lval = left->val; else lval = left->vals[lcur]; for(rcur = 0; rcur < rstop; ++rcur) { if (!rcur && !right->cnt) rval = right->val; else rval = right->vals[rcur]; parts_num = 0; switch (val_type) { case IPV6_TYPE: parts_num = 8; goto ipv6_common; case IPV6NET_TYPE: parts_num = 9; ipv6_common: scan_ipv6(lval,left_parts); scan_ipv6(rval,right_parts); break; case IPV4_TYPE: case IPV4NET_TYPE: case MACADDR_TYPE: case INT_TYPE: format = cond_formats[val_type]; parts_num = cond_format_lens[val_type]; (void) sscanf(lval, format, left_parts, left_parts+1, left_parts+2, left_parts+3, left_parts+4, left_parts+5); (void) sscanf(rval, format, right_parts, right_parts+1, right_parts+2, right_parts+3, right_parts+4, right_parts+5); break; case TEXT_TYPE: case BOOL_TYPE: res = strcmp(lval, rval); goto done_comp; default: bye("Unknown value in switch on line %d\n", __LINE__); } /* here to do a multistep int compare */ for (step = 0; step < parts_num; ++ step) { if (left_parts[step] > right_parts[step]) { res = 1; break; /* no reason to continue checking other steps */ } if (left_parts[step] < right_parts[step]) { res = -1; break; /* no reason to continue checking other steps */ } res = 0; } done_comp: if(res > 0) res = 1; else if(res < 0) res = -1; ret = ((res == cond1[cond]) || (res == cond2[cond])); if (ret && cond == IN_COND) { set_in_cond_tik(rcur); /* for delete */ /* one success is enough for right cycle in case of IN_COND, continue left cycle */ break; } if (!ret && cond != IN_COND) /* one failure is enough in cases other than IN_COND - go out */ return ret; /* in all other cases: (fail & IN_COND) or (success & !IN_COND) contniue checking; */ } } return ret; } /**************************************************** check_comp: evaluate comparison node. returns boolean value of result ****************************************************/ static boolean check_comp(vtw_node *cur) { int ret; int status; valstruct left, right; memset(&left, 0 , sizeof(left)); memset(&right, 0 , sizeof(right)); ret = FALSE; /* in case of status */ status = eval_va(&left, cur->vtw_node_left); DPRINT("check_comp left status=%d type=%d cnt=%d val=[%s]\n", status, left.val_type, left.cnt, left.val); if (status) goto free_and_return; status = eval_va(&right, cur->vtw_node_right); DPRINT("check_comp right status=%d type=%d cnt=%d val=[%s]\n", status, right.val_type, right.cnt, right.val); if (status) goto free_and_return; if(left.val_type != right.val_type) { printf("Different types in comparison\n"); goto free_and_return; } ret = val_cmp(&left, &right,cur->vtw_node_aux); free_and_return: if (left.free_me) free_val(&left); if (right.free_me) free_val(&right); return ret; } /****************** Change value of var in the file *****************/ static int write_value_to_file(const char* var_path,const char* value) { if(var_path && value) { { /*Build directory, if necessary:*/ clind_path_ref var_dir_path=clind_path_construct(var_path); if(!var_dir_path) bye("Can not construct path %s", var_path); else { char* end = clind_path_pop_string(var_dir_path); if(!end || strcmp(end,VAL_NAME)) { bye("Wrong end of path: %s (%s)", end,var_path); } free(end);end=NULL; touch(); touch_dir(clind_path_get_path_string(var_dir_path)); clind_path_destruct(&var_dir_path); } } { /*Write to file*/ FILE* fp = fopen(var_path, "w"); if(!fp) bye("Can not open value file %s", var_path); if (fputs(value, fp) < 0 || fputc('\n',fp) < 0) bye("Error writing file %s", var_path); fclose(fp); } } return 0; } static int change_var_value(const char* var_reference,const char* value, int active_dir) { int ret=-1; if(var_reference && value) { char* var_path=NULL; clind_path_ref n_cfg_path=NULL; clind_path_ref n_tmpl_path=NULL; clind_path_ref n_cmd_path=NULL; if(set_reference_environment(var_reference, &n_cfg_path, &n_tmpl_path, &n_cmd_path, active_dir)==0) { clind_val cv; memset(&cv,0,sizeof(cv)); if(clind_config_engine_apply_command_path(n_cfg_path, n_tmpl_path, n_cmd_path, FALSE, &cv, get_cdirp(), get_tdirp(), TRUE)==0) { var_path=cv.value; } } if(n_cfg_path) clind_path_destruct(&n_cfg_path); if(n_tmpl_path) clind_path_destruct(&n_tmpl_path); if(n_cmd_path) clind_path_destruct(&n_cmd_path); if(var_path) { ret=write_value_to_file(var_path,value); free(var_path); } } return ret; } int system_out(const char *command); /**************************************************** check_syn: evaluate syntax tree; returns TRUE if all checks are OK, returns FALSE if check fails. ****************************************************/ static boolean check_syn_func(vtw_node *cur,const char* func,int line) { int status; int ret; int ii; switch(cur->vtw_node_oper) { case LIST_OP: ret = check_syn(cur->vtw_node_left); if (!is_in_commit() && cur->vtw_node_aux) ret = TRUE; if (!ret || !cur->vtw_node_right) /* or no right operand */ return ret; return check_syn(cur->vtw_node_right); case HELP_OP: ret = check_syn(cur->vtw_node_left); if (ret <= 0){ if (expand_string(cur->vtw_node_right->vtw_node_string) == VTWERR_OK) { fprintf(out_stream, "%s\n", exe_string); } } return ret; case ASSIGN_OP: if (is_in_exec()) { valstruct right; char* var_reference = NULL; memset(&right, 0, sizeof(right)); status = eval_va(&right, cur->vtw_node_right); if (status || right.cnt) { /* bad or multi */ if (right.free_me) free_val(&right); return FALSE; } var_reference = strdup(cur->vtw_node_left->vtw_node_string+2); { int i=0; while(var_reference[i]) { if(var_reference[i]==')') { var_reference[i]=0; break; } i++; } } change_var_value(var_reference,right.val,FALSE); change_var_value(var_reference,right.val,TRUE); if (right.free_me) free_val(&right); if(var_reference) free(var_reference); } return TRUE; case EXEC_OP: /* for every value */ if (in_validate_val) { char *save_at = get_at_string(); for(ii = 0; ii < validate_value_val.cnt || ii == 0; ++ii) { set_at_string(validate_value_val.cnt? validate_value_val.vals[ii]:validate_value_val.val); status = expand_string(cur->vtw_node_left->vtw_node_string); if (status != VTWERR_OK) { set_at_string(save_at); return FALSE; } ret = system_out(exe_string); if (ret) { set_at_string(save_at); return FALSE; } } set_at_string(save_at); return TRUE; } /* else */ status = expand_string(cur->vtw_node_left->vtw_node_string); if (status != VTWERR_OK) { return FALSE; } ret = system_out(exe_string); return !ret; case PATTERN_OP: /* left to var, right to pattern */ { valstruct left; regex_t myreg; boolean ret; int ii; ret = TRUE; status = eval_va(&left, cur->vtw_node_left); if (status) { ret = FALSE; goto free_and_return; } status = regcomp(&myreg, cur->vtw_node_right->vtw_node_string, REG_EXTENDED); if (status) bye("Can not compile regex |%s|, result %d\n", cur->vtw_node_right->vtw_node_string, status); /* for every value */ for(ii = 0; ii < left.cnt || ii == 0; ++ii) { status = regexec(&myreg, left.cnt? left.vals[ii]:left.val, 0, 0, 0); if(status) { ret = FALSE; break; } } free_and_return: if (left.free_me) free_val(&left); return ret; } case OR_OP: ret = check_syn(cur->vtw_node_left) || check_syn(cur->vtw_node_right); return ret; case AND_OP: ret = check_syn(cur->vtw_node_left) && check_syn(cur->vtw_node_right); return ret; case NOT_OP: ret = check_syn(cur->vtw_node_left); return !ret; case COND_OP: /* aux field specifies cond type (GT, GE, etc.)*/ ret = check_comp(cur); return ret; case VAL_OP: bye("VAL op in check_syn\n"); case VAR_OP: bye("VAR op in check_syn\n"); default: bye("unknown op %d in check_syn\n", cur->vtw_node_oper); } /* not reachable */ return FALSE; } /************************************************* copy_path: copy path if destination path owns memory, free it **************************************************/ void copy_path(vtw_path *to, vtw_path *from) { if (to->path_buf) my_free(to->path_buf); if (to->path_ends) my_free(to->path_ends); *to = *from; to->path_buf = (char *) my_malloc(from->path_alloc+2, "copy_path1"); memcpy(to->path_buf, from->path_buf, to->path_alloc + 1); to->path = to->path_buf + (from->path-from->path_buf); to->path_ends = (int *) my_malloc(to->path_ends_alloc * sizeof(int), "copy_path2"); memcpy(to->path_ends, from->path_ends, to->path_ends_alloc * sizeof(int)); } /***************************************************** eval_va: converts VAR_OP or VAL_OP node into valstruct in case of VAR_OP we need to find corresponding template node to obtain type. *****************************************************/ static int eval_va(valstruct *res, vtw_node *node) { char *cp=NULL; char *pathp=NULL; int status=0; switch (node->vtw_node_oper) { case VAR_OP: { char *endp = 0; clind_path_ref n_cfg_path=NULL; clind_path_ref n_tmpl_path=NULL; clind_path_ref n_cmd_path=NULL; pathp = node->vtw_node_string; DPRINT("eval_va var[%s]\n", pathp); assert(pathp[0]=='$' && pathp[1]=='('); pathp += 2; if(pathp[0] == '@' && pathp[1]!='@'){ /* this is why we passed at_val all around */ *res = validate_value_val; res->free_me = FALSE; return 0; } memset(res,0,sizeof(*res)); if ((endp = strchr(pathp, ')')) == NULL) { printf("invalid VAR_OP [%s]\n", node->vtw_node_string); return VTWERR_BADPATH; } *endp = 0; if(set_reference_environment(pathp, &n_cfg_path, &n_tmpl_path, &n_cmd_path, is_in_delete_action())==0) { clind_val cv; memset(&cv,0,sizeof(cv)); status=clind_config_engine_apply_command_path(n_cfg_path, n_tmpl_path, n_cmd_path, TRUE, &cv, get_cdirp(), get_tdirp(), FALSE); if(status==0) { if(cv.value) { res->val_type = cv.val_type; res->free_me = TRUE; res->val = cv.value; } } } if(n_cfg_path) clind_path_destruct(&n_cfg_path); if(n_tmpl_path) clind_path_destruct(&n_tmpl_path); if(n_cmd_path) clind_path_destruct(&n_cmd_path); *endp = ')'; return status; } case VAL_OP: DPRINT("eval_va val[%s]\n", res->val); *res = node->vtw_node_val; res->free_me = FALSE; return 0; case B_QUOTE_OP: { FILE *f; int a_len, len, rd; status = expand_string(node->vtw_node_string); if (status != VTWERR_OK) { return FALSE; } f = popen(exe_string, "r"); if (!f) return -1; #define LEN 24 len = 0; cp = my_malloc(LEN,""); a_len = LEN; for(;;){ rd = fread(cp + len, 1, a_len - len , f); len += rd; if (len < a_len) break; cp = my_realloc(cp, a_len+LEN, ""); a_len += LEN; } cp[len] = 0; pclose(f); memset(res, 0, sizeof (*res)); res->val_type = TEXT_TYPE; res->free_me = TRUE; res->val = cp; } return 0; default: return 0; } } /********************************************************** expand_string: expand string replacing var references with the appropriate values, the formed string is collected in the buffer pointed at by the global exe_string. The buffer dynamically allocated and reallocated. ***********************************************************/ static int expand_string(char *stringp) { char *scanp; char *resp = exe_string; int left = exe_string_len; int my_len; int len; scanp = stringp; /* save stringp for printf */ do{ if (left <= 1){ my_len = resp - exe_string; exe_string_len += EXE_STRING_DELTA; exe_string = my_realloc(exe_string, exe_string_len, "expand_string 1"); left += EXE_STRING_DELTA; resp = exe_string + my_len; /* back in business */ } if (*scanp != '$') { if(*scanp == '\\' && scanp[1] == '$') { /* escaped $, treat as regular char */ ++scanp; } *resp++ = *scanp++; --left; } else { char *cp=NULL; boolean my_cp=FALSE; if (scanp[1] != '('){ printf("Badly formed var reference in %s\n", stringp); exit (VTWERR_BADPATH); } else if(scanp[2] == '@' && scanp[3] == ')') { cp = get_at_string(); my_cp = FALSE; scanp += 4; } else { clind_path_ref n_cfg_path=NULL; clind_path_ref n_tmpl_path=NULL; clind_path_ref n_cmd_path=NULL; char *endp; endp = strchr(scanp, ')'); if (!endp ){ return -1; } scanp += 2; /* path reference */ *endp = 0; if (endp == scanp) bye("Empty path"); if(set_reference_environment(scanp, &n_cfg_path, &n_tmpl_path, &n_cmd_path, is_in_delete_action())==0) { clind_val cv; memset(&cv,0,sizeof(cv)); if(clind_config_engine_apply_command_path(n_cfg_path, n_tmpl_path, n_cmd_path, FALSE, &cv, get_cdirp(), get_tdirp(), FALSE)==0) { cp=cv.value; } } if(n_cfg_path) clind_path_destruct(&n_cfg_path); if(n_tmpl_path) clind_path_destruct(&n_tmpl_path); if(n_cmd_path) clind_path_destruct(&n_cmd_path); if(!cp) { cp=""; } else { my_cp=TRUE; } *endp = ')'; scanp = strchr(scanp, ')') + 1; } len = strlen(cp); while(len + 1 > left) { /* 1 for termination */ my_len = resp - exe_string; exe_string_len += EXE_STRING_DELTA; exe_string = my_realloc(exe_string, exe_string_len, "expand_string 2"); left += EXE_STRING_DELTA; resp = exe_string + my_len; /* back in business */ } strcpy(resp, cp); if(my_cp && cp) free(cp); resp += len; left -= len; } } while(*scanp); *resp = 0; return VTWERR_OK; } /***************************************************** free_sorted: free all memory allocated to sorted *****************************************************/ void free_sorted(vtw_sorted *sortp) { if(sortp->ptrs) my_free(sortp->ptrs); if (sortp->parts) my_free(sortp->parts); } /***************************************************** free_def: free all memory allocated to def *****************************************************/ void free_def(vtw_def *defp) { vtw_act_type act; for(act=0; actactions[act].vtw_list_head) free_node_tree(defp->actions[act].vtw_list_head); if (defp->def_type_help) my_free(defp->def_type_help); if (defp->def_node_help) { my_free(defp->def_node_help); } if (defp->def_default) my_free(defp->def_default); } /***************************************************** free_node - add node to free list *****************************************************/ static void free_node(vtw_node *node) { --node_cnt; ++free_node_cnt; node->vtw_node_left = vtw_free_nodes; vtw_free_nodes = node; } /***************************************************** free_node_tree - add all nodes of the tree to free list *****************************************************/ static void free_node_tree(vtw_node *node) { if (node->vtw_node_left) free_node_tree(node->vtw_node_left); if (node->vtw_node_right) free_node_tree(node->vtw_node_right); if (node->vtw_node_string) free_string(node->vtw_node_string); if (node->vtw_node_val.free_me) free_val(&(node->vtw_node_val)); free_node(node); } void free_path(vtw_path *path) { if (path->path_ends) my_free(path->path_ends); if (path->path_buf) { my_free(path->path_buf); } } static void free_reuse_list() { vtw_node *next; int cnt = 0; while (vtw_free_nodes) { next = vtw_free_nodes->vtw_node_left; my_free(vtw_free_nodes); ++cnt; vtw_free_nodes = next; --free_node_cnt; } #if DEBUG printf("%d nodes used\n", cnt); #endif } /***************************************************** free_val - dealloc allocated memory of valstruct *****************************************************/ void free_val(valstruct *val) { int cnt; assert(val->free_me); if (val->val) my_free(val->val); for (cnt = 0; cnt < val->cnt; ++ cnt) my_free(val->vals[cnt]); if(val->vals) my_free(val->vals); } /***************************************************** free_string - dealloc string just free for now, we might do something else later *****************************************************/ static void free_string(char *str) { my_free(str); } /***************************************************** get_node - take node from free list or allocate *****************************************************/ static vtw_node * get_node(void) { vtw_node *ret; if (vtw_free_nodes){ ret = vtw_free_nodes; vtw_free_nodes = vtw_free_nodes->vtw_node_left; --free_node_cnt; } else { ret = my_malloc(sizeof(vtw_node), "New node"); } ++node_cnt; memset(ret, 0, sizeof(vtw_node)); return ret; } /**************************************************** get_value: for a given path (*path) verify that value exists, open it and read it. The pointer to allocated memory is returned in valpp. It is responsibility of a caller to release memory. Returns: ******************************************************/ int get_value(char **valpp, vtw_path *pathp) { struct stat statbuf; int status = VTWERR_OK; char const *err = NULL; FILE *in = NULL; char *valp; int readcnt; /* find value */ *valpp = 0; push_path(pathp, val_name); if (lstat(pathp->path, &statbuf) < 0) { err = "no value file in [%s]\n"; goto bad_path; } if ((statbuf.st_mode & S_IFMT) != S_IFREG) { err = "no value file in [%s]\n"; goto bad_path; } in = fopen(pathp->path, "r"); if (!in) { err = "Can not open value file in [%s]\n"; goto bad_path; } valp = my_malloc(statbuf.st_size + 1, "get_value"); readcnt = fread(valp, 1, statbuf.st_size, in); if (readcnt != statbuf.st_size) { my_free(valp); cli_val_alloc = 0; err = "Error reading value file in [%s]\n"; goto bad_path; } valp[statbuf.st_size] = 0; /* remove \n at the line end */ if (valp[statbuf.st_size - 1] == '\n') valp[statbuf.st_size - 1] = 0; *valpp = valp; status = 0; pop: if (in) fclose(in); pop_path(pathp); if (err) { fprintf(stderr, err, pathp->path_buf + m_path.print_offset); printf(err, pathp->path_buf + m_path.print_offset); } return status; bad_path: status = VTWERR_BADPATH; goto pop; } int get_value_to_at_string(vtw_path *pathp) { char* at=NULL; int status = get_value(&at,pathp); if(status==0) { set_at_string(at); } else { set_at_string(NULL); } return status; } /***************************************************** out_of_memeory print out of memory message and exit *****************************************************/ void out_of_memory() { bye("\n\t!!! OUT OF MEMORY !!!\n"); } /************************************************* init_path: init path, exit if not able (out_of_memory) **************************************************/ void init_path(vtw_path *path, const char *root) { long path_len; memset(path, 0, sizeof(vtw_path)); path_len = pathconf(root, _PC_PATH_MAX); if (path_len < 0) path_len = PATH_DELTA; path->path_alloc = path_len - 2; /* 1 byte for null termination, and 1 byte for '/' */ path->path_buf = path->path = (char *)my_malloc(path_len, "init_path 1"); strcpy(path->path, root); path->path_len = strlen(root); path->path_ends = (int *)my_malloc(ENDS_ALLOC * sizeof(int *), "init_path 2"); path->path_lev = 1; path->path_ends[0] = path->path_len; path->path_ends_alloc = ENDS_ALLOC; } /***************************************************** pop_path - shorten path by one segment *****************************************************/ void pop_path(vtw_path *path) { if (--path->path_lev < 1) { INTERNAL; } path->path_len = path->path_ends[path->path_lev - 1]; path->path_buf[path->path_len] = 0; } void warrant_path(vtw_path *path, int len) { int delta = path->path - path->path_buf; while(path->path_alloc - path->path_len < len + 1){ path->path_buf = (char *)my_realloc(path->path_buf, path->path_alloc + PATH_DELTA, "push_path 1"); path->path_alloc += PATH_DELTA; } path->path = path->path_buf + delta; } /***************************************************** push_path - extend path by '/' and one new segment *****************************************************/ void push_path(vtw_path *path, char *segm) { int len; char *cp; char *pp; for(cp=segm, len=0;*cp;++cp, ++len) if(*cp=='%' || *cp=='/') len +=2; warrant_path(path, len + 1); path->path_buf[path->path_len] = '/'; path->path_buf[++path->path_len] = 0; for(pp=path->path_buf + path->path_len,cp=segm; *cp;++cp, ++pp) if(*cp=='%') { pp[0] = '%'; pp[1] = '2'; pp[2] = '5'; pp += 2; }else if (*cp == '/') { pp[0] = '%'; pp[1] = '2'; pp[2] = 'F'; pp += 2; }else *pp = *cp; *pp = 0; path->path_len += len; if (path->path_lev == path->path_ends_alloc){ path->path_ends_alloc += ENDS_ALLOC; path->path_ends = (int *)my_realloc(path->path_ends, sizeof(int *)*path->path_ends_alloc, "puhs_path 2"); } path->path_ends[path->path_lev++] = path->path_len; } /**************************************************** scan_ipv6: scans ipv6 or ipv6net pointed by val and returns as array of integers pointed by parts ***************************************************/ static void scan_ipv6(char *val, unsigned int *parts) { int num = 0; int num_cnt = 0; int dot_dot_pos = -1; int dot_cnt = 0; char c; char *p; int base = 16; int total = 8; int gap; for (p = val;TRUE; ++p) { switch ((c = *p)) { case '.': if (dot_cnt == 0) { /* turn out it was decimal, convert our wrong hex interpretation; decimal may not have more than 3 digits */ num = (num/256)*100 + (num%256)/16*10 + num%16; base = 10; } ++dot_cnt; break; case ':': if (p[1] == ':'){ ++p; dot_dot_pos = num_cnt + 1; } break; case '/': base = 10; total = 9; break; case 0: break; default: /* must be a digit */ c = tolower(*p); if (isdigit(c)) num = num * base + c - '0'; else num = num * base + c - 'a' + 10; continue; } /* close the number */ /* the case of "::234: etc " handled automatically as 0::234 with allowing :: to represent 0 or more groups instead of 1 or more */ parts[num_cnt] = num; num = 0; ++num_cnt; /* combine two decimal if needed */ if (dot_cnt == 2 || (dot_cnt == 3 && (c == 0 || c == '/'))) { --num_cnt; parts[num_cnt - 1] = parts[num_cnt - 1] * 256 + parts[num_cnt]; } if (*p == 0) break; } /* replace '::' with 0s */ if (dot_dot_pos != -1 && total != num_cnt) { int i; gap = total - num_cnt; if (dot_dot_pos != num_cnt) memmove(parts+dot_dot_pos+gap, parts+dot_dot_pos, (num_cnt-dot_dot_pos)*sizeof(int)); for (i = 0; if_segoff, segp->f_segp, segp->f_seglen); m_path.path = m_path.path_buf + segp->f_segoff; } /************************************************* validate_value: validates value against type and syntax return TRUE if OK, FALSE otherwise **************************************************/ boolean validate_value(vtw_def *def, char *cp) { int status; boolean ret=TRUE; /* prepare cur_value */ set_at_string(cp); status = char2val(def, cp, &validate_value_val); if (status != VTWERR_OK) return FALSE; if ((def->def_type!=ERROR_TYPE) && (validate_value_val.val_type != def->def_type)) { if (def->def_type_help){ (void)expand_string(def->def_type_help); fprintf(out_stream, "%s\n", exe_string); } else { fprintf(out_stream, "\"%s\" is not a valid value of type \"%s\"\n", cp, type_to_name(def->def_type)); } ret = FALSE; goto validate_value_free_and_return; } ret = TRUE; if (def->actions && def->actions[syntax_act].vtw_list_head){ in_validate_val = TRUE; ret = check_syn(def->actions[syntax_act].vtw_list_head); in_validate_val = FALSE; } validate_value_free_and_return: free_val(&validate_value_val); return ret; } int cli_val_read(char *buf, int max_size) { int len; if (cli_val_len > max_size) len = max_size; else len = cli_val_len; if (len) { (void)memcpy(buf, cli_val_ptr, len); cli_val_len -= len; cli_val_ptr += len; } return len; } /*==========================================================*/ /* MEMORY */ /*==========================================================*/ void *my_malloc(size_t size, const char *name) { return malloc(size); } void *my_realloc(void *ptr, size_t size, const char *name) { return realloc(ptr, size); } void my_free(void *ptr) { free(ptr); } /************************************************* my_strdup: do a strdup, exit on no memory **************************************************/ char *my_strdup(const char *s, const char *name) { return strdup(s); } void done() { free_reuse_list(); free_path(&t_path); free_path(&m_path); if (exe_string) my_free(exe_string); } void mark_paths(vtw_mark *markp) { markp->m_lev = m_path.path_lev; markp->t_lev = t_path.path_lev; } void restore_paths(vtw_mark *markp) { while(markp->m_lev < m_path.path_lev) pop_path(&m_path); while(markp->t_lev < t_path.path_lev) pop_path(&t_path); } void touch() { char *command; command = my_malloc(strlen(get_mdirp()) + 20, "delete"); sprintf(command, "touch %s/%s", get_mdirp(), MOD_NAME); system(command); free(command); } char *type_to_name(vtw_type_e type) { switch(type) { case INT_TYPE: return("u32"); case IPV4_TYPE: return("ipv4"); case IPV4NET_TYPE: return("ipv4net"); case IPV6_TYPE: return("ipv6"); case IPV6NET_TYPE: return("ipv6net"); case MACADDR_TYPE: return("macaddr"); case DOMAIN_TYPE: return("domain"); case TEXT_TYPE: return("text"); case BOOL_TYPE: return("bool"); default: return("unknown"); } } #if 1 void dump_log(int argc, char **argv) { } #else void dump_log(int argc, char **argv) { int i; int len; char *cp; len = 0; for (i=0; if_segoff, get_f_seg_m_ptr()->f_segp, get_f_seg_m_ptr()->f_seglen); n_vt_path.path = n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff; if(active) { vtw_path active_path; memset(&active_path,0,sizeof(active_path)); copy_path(&active_path, &n_vt_path); memcpy(active_path.path_buf + get_f_seg_a_ptr()->f_segoff, get_f_seg_a_ptr()->f_segp, get_f_seg_a_ptr()->f_seglen); active_path.path = active_path.path_buf + get_f_seg_a_ptr()->f_segoff; *n_cfg_path=clind_path_construct(active_path.path); free_path(&active_path); } else { *n_cfg_path=clind_path_construct(n_vt_path.path); } *n_tmpl_path=clind_path_construct(t_path.path); *n_cmd_path=clind_path_construct(var_reference); free_path(&n_vt_path); } return 0; } else { return -1; } } /*** output ***/ int out_fd = -1; FILE *out_stream = NULL; int err_fd = -1; FILE *err_stream = NULL; int new_out_fd = -1; int new_err_fd = -1; int initialize_output() { if ((out_fd = dup(STDOUT_FILENO)) == -1) { return -1; } if ((out_stream = fdopen(out_fd, "a")) == NULL) { return -1; } if ((err_fd = dup(STDOUT_FILENO)) == -1) { return -1; } if ((err_stream = fdopen(err_fd, "a")) == NULL) { return -1; } new_out_fd = open(LOGFILE_STDOUT, O_WRONLY | O_CREAT, 0660); new_err_fd = open(LOGFILE_STDERR, O_WRONLY | O_CREAT, 0660); if (new_out_fd == -1 || new_err_fd == -1) { return -1; } if ((lseek(new_out_fd, 0, SEEK_END) == ((off_t) -1)) || (lseek(new_err_fd, 0, SEEK_END) == ((off_t) -1))) { return -1; } if ((dup2(new_out_fd, STDOUT_FILENO) == -1) || (dup2(new_err_fd, STDERR_FILENO) == -1)) { return -1; } return 0; } int redirect_output() { if ((dup2(new_out_fd, STDOUT_FILENO) == -1) || (dup2(new_err_fd, STDERR_FILENO) == -1)) { return -1; } return 0; } int restore_output() { if ((dup2(out_fd, STDOUT_FILENO) == -1) || (dup2(err_fd, STDERR_FILENO) == -1)) { return -1; } return 0; } /* system_out: * call system() with output re-enabled. * output is again redirected before returning from here. */ int system_out(const char *command) { int ret = -1; if (restore_output() == -1) { return -1; } ret = system(command); if (redirect_output() == -1) { return -1; } return ret; } /**********************************************************/