#include #include #include #include #include #include #include #include #include #include #include #include #include #include "cli_val.h" #include "cli_parse.h" #include #include #include #include #include "cli_objects.h" #include "cli_val_engine.h" #include "cstore/cstore-c.h" /* Defines: */ #define EXE_STRING_DELTA 512 #define PATH_DELTA 1000 #define ENDS_ALLOC 20 /* mcd vs. ccd location change when m_root changed */ #define PATH_CM_LOCATION 25 #define VAR_REF_MARKER "$VAR(" #define VAR_REF_MARKER_LEN 5 #define VAR_REF_SELF_MARKER "$VAR(@)" #define VAR_REF_SELF_MARKER_LEN 7 /* Global vars: */ vtw_path m_path, t_path; void *var_ref_handle = NULL; /* Local 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 */ "%x:%x:%x:%x:%x:%x:%x:%x", /* IPV6NET */ "%x:%x:%x:%x:%x:%x:%x:%x/%u", /* IPV6NET_TYPE */ "%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 */ 16, /* IPV6_TYPE */ 17, /* IPV6NET_TYPE */ 6 /* MACADDR_TYPE */ }; static int cli_val_len; 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 **outbuf, const char* func,int line); #define check_syn(cur,out_buf) check_syn_func((cur),(out_buf),__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); void subtract_values(char **lhs, const char *rhs); 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 ***************************************************/ const 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(const char *msg, ...) { va_list ap; OUTPUT_USER("%s failed\n", (cli_operation_name) ? cli_operation_name : "Operation"); 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(const 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); } int mkdir_p(const char *path) { if (mkdir(path, 0777) == 0) return 0; if (errno != ENOENT) return -1; char *tmp = strdup(path); if (tmp == NULL) { errno = ENOMEM; return -1; } char *slash = strrchr(tmp, '/'); if (slash == NULL) return -1; *slash = '\0'; /* recurse to make missing piece of path */ int ret = mkdir_p(tmp); if (ret == 0) ret = mkdir(path, 0777); free(tmp); return ret; } void touch_dir(const char *dp) { struct stat statbuf; if (lstat(dp, &statbuf) < 0) { if (errno != ENOENT) bye("can't access directory: %s (%s)", dp, strerror(errno)); if (mkdir_p(dp) < 0) bye("can't make directory: %s (%s)", dp, strerror(errno)); } else { if(!S_ISDIR(statbuf.st_mode)) bye("directory %s expected, found file", dp); utime(dp, NULL); } } /***************************************************** 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->cnt%MULTI_ALLOC == 0) { /* convert into multivalue */ first->vals = my_realloc(first->vals, (first->cnt + MULTI_ALLOC) * sizeof(char *), "add_value 1"); first->val_types = my_realloc(first->val_types,(first->cnt + MULTI_ALLOC) * sizeof(vtw_type_e), "add_value 2"); if (first->cnt == 0) { /* single value - convert */ first->vals[0] = first->val; first->val_types[0] = first->val_type; first->cnt = 1; first->val = NULL; } } second->free_me = FALSE; /* we took its string */ first->vals[first->cnt] = second->val; first->val_types[first->cnt] = second->val_type; ++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; } /* this recursive function walks the specified "vtw_node" tree representing * "syntax" actions and looks for the first "in" action on a "self ref" * specified as follows in template: * $VAR(@) in "val1", "val2", ... * * if found, the corresponding valstruct is returned. this is used by the * completion mechanism to get such "allowed" values specified by "syntax". */ const valstruct * get_syntax_self_in_valstruct(vtw_node *vnode) { const valstruct *ret = NULL; if (!vnode) { return NULL; } if (vnode->vtw_node_oper == COND_OP && vnode->vtw_node_aux == IN_COND && vnode->vtw_node_left && vnode->vtw_node_right) { vtw_node *ln = vnode->vtw_node_left; vtw_node *rn = vnode->vtw_node_right; if (ln->vtw_node_oper == VAR_OP && ln->vtw_node_string && strncmp(VAR_REF_SELF_MARKER, ln->vtw_node_string, VAR_REF_SELF_MARKER_LEN) == 0 && rn->vtw_node_oper == VAL_OP) { // found a matching syntax action. return valstruct. return &(rn->vtw_node_val); } } // this node does not match. walk down. ret = get_syntax_self_in_valstruct(vnode->vtw_node_left); if (!ret) { ret = get_syntax_self_in_valstruct(vnode->vtw_node_right); } return ret; } /* execute specified command and return output in specified buffer as * a null-terminated string. return number of characters in the output * or -1 if failed. * * NOTE: NO attempt is made to ensure the security of the specified command. * in other words, *DO NOT* use a user-supplied string as the command * for this function. */ int get_shell_command_output(const char *cmd, char *buf, unsigned int buf_size) { int ret = -1; FILE *cmd_in = NULL; size_t cnt = 0; if (!buf || !buf_size) { return -1; } if (!(cmd_in = popen(cmd, "r"))) { return -1; } cnt = fread(buf, 1, buf_size - 1, cmd_in); if (cnt == (buf_size - 1) || feof(cmd_in)) { /* buffer full or got the whole output. null terminate */ buf[cnt] = 0; ret = cnt; } if (pclose(cmd_in) == -1) { ret = -1; } return ret; } 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" #define COMMIT_CMD "/opt/vyatta/sbin/my_commit" static void release_config_lock() { unlink(LOCK_FILE); /* error ignored */ } /* try to clean up orphaned lock file. return -1 if failed */ static int try_lock_cleanup() { char buf[128]; char proc[128]; FILE *f = NULL; int ret = -1; struct stat statb; do { /* get the proc entry */ if ((f = fopen(LOCK_FILE, "r")) == NULL) { break; } if (fgets(proc, 128, f) == NULL) { break; } /* read the proc entry */ if (stat(proc, &statb) == -1) { if (errno == ENOENT) { /* proc entry doesn't exist. can clean up the lock now */ ret = 0; break; } } fclose(f); if ((f = fopen(proc, "r")) == NULL) { /* can't open proc entry. assume we can't clean up */ break; } if (fgets(buf, 128, f) == NULL) { /* can't read proc entry. assume we can't clean up */ break; } /* check if the process is commit */ if (strcmp(buf, COMMIT_CMD) == 0) { /* it is commit. can't clean up */ break; } /* can clean up the lock */ ret = 0; } while (0); if (f) { fclose(f); } if (ret == 0) { unlink(LOCK_FILE); if (stat(LOCK_FILE, &statb) != -1 || errno != ENOENT) { /* proc entry still exists. cleanup failed */ ret = -1; } } return ret; } static int create_lock_file(int try_cleanup) { int fd = -1; int i = 0; struct timespec req; #define LOCK_WAIT_TIME 2 #define LOCK_NUM_RETRIES 5 req.tv_sec = LOCK_WAIT_TIME; req.tv_nsec = 0; fd = open(LOCK_FILE, O_WRONLY | O_CREAT | O_EXCL, 0660); if (fd == -1) { for (i = 0; i < LOCK_NUM_RETRIES; i++) { nanosleep(&req, NULL); fd = open(LOCK_FILE, O_WRONLY | O_CREAT | O_EXCL, 0660); if (fd >= 0) { break; } } } if (fd == -1 && try_cleanup) { if (try_lock_cleanup() != -1) { /* cleanup succeeded */ fd = create_lock_file(0); } } return fd; } int get_config_lock() { int fd = -1; FILE *lfile = NULL; int ret = -1; do { /* create lock file */ fd = create_lock_file(1); if (fd == -1) { break; } /* write pid into lock file */ if ((lfile = fdopen(fd, "w")) == NULL) { break; } if (fprintf(lfile, "/proc/%u/cmdline", getpid()) < 0) { break; } /* fclose also closes fd */ if (fclose(lfile) != 0) { break; } /* clean up on exit */ if (atexit(release_config_lock) != 0) { break; } ret = 0; } while (0); if (ret == -1) { if (lfile) { fclose(lfile); } else if (fd != -1) { close(fd); } release_config_lock(); } return ret; } void internal_error(int line, const 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, const char **outbuf) { 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,outbuf); 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 ****************************************************/ int char2val_notext(vtw_def *def, int my_type, int my_type2, char *value, valstruct **valp, char *buf); int char2val_text(vtw_def *def, char *value, valstruct **valp); /************************************************** char2val: convert string into valstruct verifying the type according to def ****************************************************/ int char2val(vtw_def *def, char *value, valstruct *valp) { int my_type = def->def_type; int my_type2 = def->def_type2; memset(valp, 0, sizeof (*valp)); /* normal: A) single text type B) single non-text type new: C) Text plus non-text (NOT SUPPORTED)! D) 2 non-text (double up first loop) //so perhaps split the below into two functions, text and non-text */ if (my_type != TEXT_TYPE && my_type != ERROR_TYPE) { //since this is the restricted type //we'll either do two calls to this //or one call to this as text //currently fails to handle mixed text + non-text case... char buf1[2048]; if (char2val_notext(def,my_type,my_type2,value,&valp,buf1) != 0) { OUTPUT_USER("%s", buf1); return -1; //only single definition } return 0; } else { return char2val_text(def,value,&valp); } } //non-text type processing block int char2val_notext(vtw_def *def, int my_type, int my_type2, char *value, valstruct **valpp, char *err_buf) { valstruct *valp = *valpp; int token; boolean first = TRUE; cli_val_len = strlen(value); cli_val_ptr = value; char type_buf[256]; if (my_type2 != ERROR_TYPE) { sprintf(type_buf,"%s or %s",type_to_name(my_type),type_to_name(my_type2)); } else { sprintf(type_buf,"%s",type_to_name(my_type)); } 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); sprintf(err_buf, "%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_buf); token = yy_cli_val_lex(); sprintf(err_buf, "\"%s\" is not a valid value of type \"%s\"\n", value, type_buf); } return -1; } return 0; } if (my_type != get_cli_value_ptr()->val_type && (my_type2 != ERROR_TYPE && my_type2 != get_cli_value_ptr()->val_type)) { if (def->def_type_help){ set_at_string(value); (void)expand_string(def->def_type_help); sprintf(err_buf, "%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_buf); token = yy_cli_val_lex(); sprintf(err_buf, "\"%s\" is not a valid value of type \"%s\"\n", value, type_buf); } 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) { token = yy_cli_val_lex(); sprintf(err_buf, "\"%s\" is not a valid value\n", value); 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 -1; } } return 0; } int char2val_text(vtw_def *def, char *value, valstruct **valpp) { valstruct *valp = *valpp; char *endp, *cp; int linecnt, cnt; //PROCESSING IF TYPE IS TEXT TYPE valp->val_type = TEXT_TYPE; valp->val_types = NULL; 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"); valp->val_types = my_malloc(cnt * sizeof(vtw_type_e), "char2val 3"); int i; for (i=0;ival_types[i] = ERROR_TYPE; } 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(const valstruct *left, const 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; 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]; //don't bother comparing if these are different types. if ((rcur || right->cnt) && right->val_types != NULL && right->val_types[rcur] != ERROR_TYPE) { if (right->val_types[rcur] != val_type) { continue; } } 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); if ((rcur || right->cnt) && right->val_types != NULL && right->val_types[rcur] != ERROR_TYPE) { format = cond_formats[right->val_types[rcur]]; } (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) { if (var_ref_handle) { /* XXX current var ref lib implementation is fs-specific. * for now treat it as a part of the unionfs-specific * cstore implementation. * handle is set => we are in cstore operation. */ if (cstore_set_var_ref(var_ref_handle, var_reference, value, active_dir)) { ret = 0; } } else { /* legacy usage */ 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_tdirp(), TRUE, active_dir)==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, const char **outbuf); /**************************************************** 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 **outbuf,const char* func,int line) { int status; int ret; int ii; switch(cur->vtw_node_oper) { case LIST_OP: ret = TRUE; if (is_in_commit() || !cur->vtw_node_aux) { ret = check_syn(cur->vtw_node_left,outbuf); } if (!ret || !cur->vtw_node_right) /* or no right operand */ return ret; return check_syn(cur->vtw_node_right,outbuf); case HELP_OP: ret = check_syn(cur->vtw_node_left,outbuf); if (ret <= 0){ if (expand_string(cur->vtw_node_right->vtw_node_string) == VTWERR_OK) { if (outbuf == NULL) { OUTPUT_USER("%s\n", exe_string); } else { strcat((char*)*outbuf, 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; } if (strncmp(cur->vtw_node_left->vtw_node_string, VAR_REF_MARKER, VAR_REF_MARKER_LEN) != 0) { /* bad reference. should not happen */ return FALSE; } /* point to char next to '(' */ var_reference = strdup(cur->vtw_node_left->vtw_node_string + VAR_REF_MARKER_LEN); { 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,outbuf); 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,outbuf); 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,outbuf) || check_syn(cur->vtw_node_right,outbuf); return ret; case AND_OP: ret = check_syn(cur->vtw_node_left,outbuf) && check_syn(cur->vtw_node_right,outbuf); return ret; case NOT_OP: ret = check_syn(cur->vtw_node_left,outbuf); 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; pathp = node->vtw_node_string; DPRINT("eval_va var[%s]\n", pathp); assert(strncmp(pathp, VAR_REF_MARKER, VAR_REF_MARKER_LEN) == 0); pathp += VAR_REF_MARKER_LEN; 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 (var_ref_handle) { /* XXX current var ref lib implementation is fs-specific. * for now treat it as a part of the unionfs-specific * cstore implementation. * handle is set => we are in cstore operation. */ vtw_type_e vtype; char *vptr = NULL; if (!cstore_get_var_ref(var_ref_handle, pathp, &vtype, &vptr, is_in_delete_action())) { status = -1; } else { /* success */ status = 0; if(vptr) { res->val_type = vtype; res->val_types = NULL; res->free_me = TRUE; res->val = vptr; } } } else { /* legacy usage */ 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(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_tdirp(), FALSE, is_in_delete_action()); if(status==0) { if(cv.value) { res->val_type = cv.val_type; res->val_types = NULL; 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 != '$') { /* we don't check for '\''$' any more. * only "$VAR(" is significant now */ *resp++ = *scanp++; --left; } else if (strlen(scanp) < (VAR_REF_MARKER_LEN + 1 + 1)) { /* shorter than "$VAR(@)". cannot be a reference */ *resp++ = *scanp++; --left; } else if (strncmp(scanp, VAR_REF_MARKER, VAR_REF_MARKER_LEN) != 0) { /* doesn't start with "$VAR(". not a reference */ *resp++ = *scanp++; --left; } else { /* the first char is '$' * && remaining length is enough for a reference * && starts with marker. */ char *cp=NULL; boolean my_cp=FALSE; /* advance scanp to 'R'. */ scanp += 3; if(scanp[2] == '@' && scanp[3] == ')') { cp = get_at_string(); my_cp = FALSE; scanp += 4; } else { char *endp; endp = strchr(scanp, ')'); if (!endp ){ return -1; } scanp += 2; /* path reference */ *endp = 0; if (endp == scanp) bye("Empty path"); if (var_ref_handle) { /* XXX current var ref lib implementation is fs-specific. * for now treat it as a part of the unionfs-specific * cstore implementation. * handle is set => we are in cstore operation. */ vtw_type_e vtype; char *vptr = NULL; if (cstore_get_var_ref(var_ref_handle, scanp, &vtype, &vptr, is_in_delete_action()) && vptr) { cp = vptr; } } else { /* legacy usage */ 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(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, TRUE, &cv, get_tdirp(), FALSE, is_in_delete_action())==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); if(val->val_types) my_free(val->val_types); } /***************************************************** 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); 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, const char *segm) { int len; const 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, "push_path 2"); } path->path_ends[path->path_lev++] = path->path_len; // push_path_no_escape(); } /** * Version of above that doesn't escape value before stuffing. * **/ void push_path_no_escape(vtw_path *path, char *segm) { int len; char *cp; char *pp; for(cp=segm, len=0;*cp;++cp, ++len); 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) { *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; p = val; if (strncmp(p, ".wh.", 4) == 0) p = p + 4; for ( ;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; /* certain characters are not allowed */ { int i = 0; for (i = 0; i < strlen(cp); i++) { if (cp[i] == '\'') { OUTPUT_USER("Cannot use the \"'\" (single quote) character " "in a value string\n"); return FALSE; } if (cp[i] == '\n') { OUTPUT_USER("Cannot use the newline character in a value string\n"); return FALSE; } } } /* 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) && (validate_value_val.val_type != def->def_type2))) { if (def->def_type_help){ (void)expand_string(def->def_type_help); OUTPUT_USER("%s\n", exe_string); } else { OUTPUT_USER("\"%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,(const char **)NULL); in_validate_val = FALSE; } validate_value_free_and_return: free_val(&validate_value_val); return ret; } void subtract_values(char **lhs, const char *rhs) { size_t length = 0, lhs_cnt = 0, rhs_cnt = 0, i; const char *line = NULL; char *rhs_copy = NULL, *res = NULL; const char **head = NULL, **ptr = NULL; const char **new_head = NULL, **new_ptr = NULL; if (lhs == NULL || *lhs == NULL || **lhs == '\0' || rhs == NULL || *rhs == '\0') return; /* calculate number of rhs entries */ rhs_copy = strdup(rhs); line = strtok(rhs_copy, "\n\r"); while (line != NULL && *line != '\0') { rhs_cnt++; line = strtok(NULL, "\n\r"); } /* strtok destroys the string. dup again. */ free(rhs_copy); rhs_copy = strdup(rhs); /* allocate enough space for all old entries (to be subtracted) */ length = rhs_cnt * sizeof(char *); head = ptr = my_malloc(length, "subtract_values list1"); memset(head, 0, length); /* parse the entries and put them in head[] */ line = strtok(rhs_copy, "\n\r"); while (line != NULL && *line != '\0') { *ptr = line; ptr++; line = strtok(NULL, "\n\r"); } /* calculate number of lhs entries */ { char *lhs_copy = strdup(*lhs); line = strtok(lhs_copy, "\n\r"); while (line != NULL && *line != '\0') { lhs_cnt++; line = strtok(NULL, "\n\r"); } free(lhs_copy); } /* allocate enough space for all new entries */ length = lhs_cnt * sizeof(char *); new_head = new_ptr = my_malloc(length, "subtract_values list2"); memset(new_head, 0, length); /* reset length and lhs_cnt. they are now used for the "new" array (i.e., * after subtraction). */ length = 0; lhs_cnt = 0; line = strtok(*lhs, "\n\r"); while (line != NULL && *line != '\0') { for (i = 0; i < rhs_cnt; i++) { if (strncmp(line, head[i], strlen(line)) == 0) break; } if (i >= rhs_cnt) { *new_ptr = line; length += strlen(line) + 1; new_ptr++; lhs_cnt++; } line = strtok(NULL, "\n\r"); } res = (char *) my_malloc(length + 1, "subtract_values result"); *res = '\0'; for (i = 0; i < lhs_cnt; i++) { strcat(res, new_head[i]); strcat(res, "\n"); } my_free(head); my_free(new_head); if (rhs_copy != NULL) my_free(rhs_copy); my_free(*lhs); *lhs = res; } 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; } void done(void) { 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_file(const char *filename) { int fd = creat(filename, 0666); if (fd < 0) { if (errno == EEXIST) utime(filename, NULL); else bye("can't touch %s (%s)", filename, strerror(errno)); } else close(fd); } void touch(void) { char filename[strlen(get_mdirp()) + 20]; sprintf(filename, "%s/%s", get_mdirp(), MOD_NAME); touch_file(filename); } const 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("txt"); case BOOL_TYPE: return("bool"); default: return("unknown"); } } #ifdef CLI_DEBUG void dump_log(int argc, char **argv) { int i; printf("Command:"); for (i = 0; i < argc; ++i) { putchar(' '); puts(argv[i]); } putchar('\n'); } #endif /********************* New Dir ****************************/ 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) { if(var_reference && n_cfg_path && n_tmpl_path && n_cmd_path) { if(*var_reference=='/') { if(is_in_delete_action()) { *n_cfg_path=clind_path_construct(get_adirp()); } else { *n_cfg_path=clind_path_construct(get_mdirp()); } *n_tmpl_path=clind_path_construct(get_tdirp()); *n_cmd_path=clind_path_construct(var_reference); } else { vtw_path n_vt_path; memset(&n_vt_path,0,sizeof(n_vt_path)); copy_path(&n_vt_path, &m_path); /* switch to MPATH */ memcpy(n_vt_path.path_buf + get_f_seg_m_ptr()->f_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(const char *op) { 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; } cli_operation_name = op; 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. * note: this function may be used outside of actual CLI operations, so output * may not have been redirected. check out_stream for such cases. */ int old_system_out(const char *command) { int ret = -1; if (out_stream && restore_output() == -1) { return -1; } ret = system(command); if (out_stream && redirect_output() == -1) { return -1; } // fprintf(out_stream,"old system out: %d, for cmd: %s\n",ret,command); return ret; } int system_out(const char *cmd, const char **outbuf) { // fprintf(out_stream,"system out\n"); if (outbuf == NULL) { return old_system_out(cmd); } if (cmd == NULL) { return -1; } int cp[2]; // Child to parent pipe if( pipe(cp) < 0) { return -1; } pid_t pid = fork(); if (pid == 0) { //child close(1); // Close current stdout./ dup2( cp[1],1); // Make stdout go to write end of pipe. dup2( cp[1],2); // Make stderr go to write end of pipe. close(0); // Close current stdin. close( cp[0]); int ret = 0; if (execl("/bin/sh","sh","-c",cmd,NULL) == -1) { ret = errno; } close( cp[1]); exit(ret); } else { //parent char buf[1025]; memset(buf,'\0',1025); close(cp[1]); if (read(cp[0], &buf, 1024) > 0) { strcat((char*)*outbuf,buf); } //now wait on child to kick the bucket int status; wait(&status); close(cp[0]); return WEXITSTATUS(status); } } /**********************************************************/ boolean is_deactivated(const clind_path_ref *path) { if (path == NULL) { return FALSE; } const char* path_string = clind_path_get_path_string(*path); char buf[1024]; //ALSO USED AS LIMIT IN UNIONFS path length strcpy(buf,path_string); //first we'll check the current directory char file[1024]; sprintf(file,"%s/.disable",buf); struct stat s; if (lstat(file,&s) == 0) { return TRUE; } long min_len = strlen("/opt/vyatta/config/tmp/new_config_"); //now walk back up tree looking for disable flag..... while (TRUE) { int index = strlen(buf)-1; if (index < min_len) { return FALSE; } if (buf[index] == '/') { while (TRUE) { if (buf[--index] != '/') { buf[index] = '\0'; break; } } } char *ptr = rindex(buf,'/'); if (ptr != NULL) { *ptr = '\0'; } char file[1024]; sprintf(file,"%s/.disable",buf); struct stat s; if (lstat(file,&s) == 0) { return TRUE; } } return FALSE; }