diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/.gitignore | 9 | ||||
-rw-r--r-- | src/cli_def.l | 424 | ||||
-rw-r--r-- | src/cli_new.c | 1938 | ||||
-rw-r--r-- | src/cli_objects.c | 343 | ||||
-rw-r--r-- | src/cli_objects.h | 65 | ||||
-rw-r--r-- | src/cli_parse.y | 206 | ||||
-rw-r--r-- | src/cli_path_utils.c | 535 | ||||
-rw-r--r-- | src/cli_path_utils.h | 72 | ||||
-rw-r--r-- | src/cli_val.h | 206 | ||||
-rw-r--r-- | src/cli_val.l | 293 | ||||
-rw-r--r-- | src/cli_val_engine.c | 881 | ||||
-rw-r--r-- | src/cli_val_engine.h | 86 | ||||
-rw-r--r-- | src/commit.c | 1364 | ||||
-rw-r--r-- | src/delete.c | 258 | ||||
-rw-r--r-- | src/set.c | 310 |
15 files changed, 6990 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..a2b4c94 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,9 @@ +cli_parse.tab.c +cli_parse.tab.h +cli_def.lex.c +cli_def.tab.c +cli_def.tab.h +cli_val.lex.c +delete +my_* +show diff --git a/src/cli_def.l b/src/cli_def.l new file mode 100644 index 0000000..41994d6 --- /dev/null +++ b/src/cli_def.l @@ -0,0 +1,424 @@ +%{ +#include "cli_val.h" +#include "cli_parse.h" +FILE *yy_cli_def_in; +static void make_def_value(vtw_type_e type); +static int cli_last_nl_returned=0; +#define STR_DELTA 2 +%} +%x str +%option noyywrap +%option nounput +%option never-interactive + +/* + * Regular expressions of IP and MAC addresses, URLs, etc. + */ + +/* + * IPv4 address representation. + */ +RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2}) +RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3} +RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9]) +RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN} + +/* + * IPv6 address representation in Augmented Backus-Naur Form (ABNF) + * as defined in RFC-2234. + * IPv6 address representation taken from RFC-3986: + * + * IPv6address = 6( h16 ":" ) ls32 + * / "::" 5( h16 ":" ) ls32 + * / [ h16 ] "::" 4( h16 ":" ) ls32 + * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + * / [ *4( h16 ":" ) h16 ] "::" ls32 + * / [ *5( h16 ":" ) h16 ] "::" h16 + * / [ *6( h16 ":" ) h16 ] "::" + * + * h16 = 1*4HEXDIG + * ls32 = ( h16 ":" h16 ) / IPv4address + * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + * dec-octet = DIGIT ; 0-9 + * / %x31-39 DIGIT ; 10-99 + * / "1" 2DIGIT ; 100-199 + * / "2" %x30-34 DIGIT ; 200-249 + * / "25" %x30-35 ; 250-255 + */ + +RE_H16 [a-fA-F0-9]{1,4} +RE_H16_COLON {RE_H16}":" +RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4}) +RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32} +RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32} +RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32} +RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32} +RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32} +RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32} +RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32} +RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16} +RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::" +RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9} +RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]? +RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN} + +/* + * Ethernet MAC address representation. + */ +RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5} + +/* + * URL-related regular expressions. + * + * Implementation is based on the BNF-like specification from: + * - RFC-1738: HTTP, FTP, FILE + * - RFC-3617: TFTP + * - RFC-3986: update of RFC-1738 + */ +RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP} + +/* + * URL schemeparts for IP based protocols. + * Representation taken from RFC-1738, and some of it is updated by RFC-3986. + * + * login = [ user [ ":" password ] "@" ] hostport + * hostport = host [ ":" port ] + * host = hostname | hostnumber + * hostname = *[ domainlabel "." ] toplabel + * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit + * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit + * alphadigit = alpha | digit + * hostnumber = digits "." digits "." digits "." digits + * port = digits + * user = *[ uchar | ";" | "?" | "&" | "=" ] + * password = *[ uchar | ";" | "?" | "&" | "=" ] + */ +RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT} +RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})? +RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL} +RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]" +RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+ +RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL} +RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT} +RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS} +RE_URL_PORT {RE_URL_DIGITS} +RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* +RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* + +/* + * FILE URL regular expression. + * Representation taken from RFC-1738. + * + * fileurl = "file://" [ host | "localhost" ] "/" fpath + */ +RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH} + +/* + * FTP URL regular expression. + * Representation taken from RFC-1738. + * + * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ] + * fpath = fsegment *[ "/" fsegment ] + * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ] + * ftptype = "A" | "I" | "D" | "a" | "i" | "d" + */ +RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)? +RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})* +RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")* +RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d" + +/* + * HTTP URL regular expression. + * Representation taken from RFC-1738. + * + * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ] + * hpath = hsegment *[ "/" hsegment ] + * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + */ +RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)? +RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})* +RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* +RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* + +/* + * TFTP URL regular expression. + * Representation taken from RFC-3617. + * + * tftpURI = "tftp://" host "/" file [ mode ] + * mode = ";" "mode=" ( "netascii" / "octet" ) + * file = *( unreserved / escaped ) + * host = <as specified by RFC 2732 [3]> + * unreserved = <as specified in RFC 2396 [4]> + * escaped = <as specified in RFC 2396> + */ +RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})? +RE_URL_TFTP_MODE ";""mode="("netascii"|"octet") +RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})* + +/* + * URL-related miscellaneous definitions. + * Representation taken from RFC-1738 and from RFC-3986. + * + * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | + * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | + * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + * "y" | "z" + * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + * alpha = lowalpha | hialpha + * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + * "8" | "9" + * safe = "$" | "-" | "_" | "." | "+" + * extra = "!" | "*" | "'" | "(" | ")" | "," + * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`" + * punctuation = "<" | ">" | "#" | "%" | <"> + * + * + * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" + * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + * "a" | "b" | "c" | "d" | "e" | "f" + * escape = "%" hex hex + * + * unreserved = alpha | digit | safe | extra + * uchar = unreserved | escape + * xchar = unreserved | reserved | escape + * digits = 1*digit + * + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +RE_URL_LOWALPHA [a-z] +RE_URL_HIALPHA [A-Z] +RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA} +RE_URL_DIGIT [0-9] +RE_URL_SAFE "$"|"-"|"_"|"."|"+" +RE_URL_EXTRA "!"|"*"|"'"|"("|")"|"," +RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`" +RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<"> +RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"=" +RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f] +RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG} +RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA} +RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE} +RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE} +RE_URL_DIGITS {RE_URL_DIGIT}{1,} +RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"=" + + +%% + char *string_buf=NULL; + char *string_buf_ptr=NULL; + int string_len=0, string_buf_len=0; + char str_delim=0; + +#[^\n]*(\\[ \t]*\n[^\n]*)*\n { /* comment */ } +\n {yy_cli_def_lineno++;return EOL;} +<<EOF>> { if(!cli_last_nl_returned) {cli_last_nl_returned=1;return EOL;} yyterminate(); } +[\`\"] { + BEGIN(str); + str_delim = yy_cli_def_text[0]; + string_buf_ptr = string_buf = my_malloc(STR_DELTA + 1, ""); + string_len = STR_DELTA; + string_buf_len = STR_DELTA; + } +<str>[\"\`] { + /* maybe a closing quote - all done */ + if(str_delim == yy_cli_def_text[0]) { + BEGIN(INITIAL); + *string_buf_ptr = 0; + /* return string constant token type and + * value to parser + */ + yy_cli_parse_lval.strp = string_buf; + return str_delim == '"'?STRING:EX_STRING; + }else{ + *string_buf_ptr++ = yy_cli_def_text[0]; + goto string_too_long; + } + } + +<str>\n { + /* error - unterminated string constant */ + yy_cli_def_lineno++; + } + +<str>\\n {*string_buf_ptr++ = '\n';goto string_too_long;} +<str>\\t {*string_buf_ptr++ = '\t';goto string_too_long;} +<str>\\r {*string_buf_ptr++ = '\r';goto string_too_long;} +<str>\\b {*string_buf_ptr++ = '\b';goto string_too_long;} +<str>\\f {*string_buf_ptr++ = '\f';goto string_too_long;} +<str>\\\n { yy_cli_def_lineno++; /* continuation => ignore */ } + +<str>\\. { + *string_buf_ptr++ = yy_cli_def_text[1]; +string_too_long: + *string_buf_ptr = 0; + /* printf("Cur string |%s|\n", string_buf); */ + if (!--string_len) { + string_buf = my_realloc(string_buf, + string_buf_len + STR_DELTA + 1, + "cli_def STRING"); + string_buf_ptr = string_buf + string_buf_len; + string_buf_len += STR_DELTA; + string_len = STR_DELTA; + } + } +<str>[^\\\n\"\`]+ { + char *yptr = yy_cli_def_text; + + while ( *yptr ){ + *string_buf_ptr++ = *yptr++; + if (!--string_len) { + string_buf = my_realloc(string_buf, + string_buf_len + STR_DELTA + 1, + "cli_def STRING"); + string_buf_ptr = string_buf + string_buf_len; + string_buf_len += STR_DELTA; + string_len = STR_DELTA; + } + } + *string_buf_ptr = 0; + } + +default: return DEFAULT; +tag: return TAG; +type: return TYPE; +help: return HELP; +syntax: return SYNTAX; +commit: return COMMIT; +check: yy_cli_parse_lval.action = syntax_act; return ACTION; +delete: yy_cli_parse_lval.action = delete_act; return ACTION; +update: yy_cli_parse_lval.action = update_act; return ACTION; +activate: yy_cli_parse_lval.action = activate_act; return ACTION; +create: yy_cli_parse_lval.action = create_act; return ACTION; +begin: yy_cli_parse_lval.action = begin_act; return ACTION; +end: yy_cli_parse_lval.action = end_act; return ACTION; +multi: return MULTI; + +:: { + make_def_value(IPV6_TYPE); + return VALUE; + } +txt { + yy_cli_parse_lval.type = TEXT_TYPE; + return TYPE_DEF; + } + +pattern return PATTERN; + +exec return EXEC; + +, return COMMA; +\|\| return OR; +\&\& return AND; +\= return ASSIGN; +\=\= {yy_cli_parse_lval.cond = EQ_COND; return COND;} +\!\= {yy_cli_parse_lval.cond = NE_COND; return COND;} +\< {yy_cli_parse_lval.cond = LT_COND; return COND;} +\> {yy_cli_parse_lval.cond = GT_COND; return COND;} +\<\= {yy_cli_parse_lval.cond = LE_COND; return COND;} +\>\= {yy_cli_parse_lval.cond = GE_COND; return COND;} +in {yy_cli_parse_lval.cond = IN_COND; return COND;} +\! { return NOT; } +\$\([^)]+\) { + yy_cli_parse_lval.strp = my_strdup(yy_cli_def_text, "TEXT"); + return VAR; + } +true { + make_def_value(BOOL_TYPE); + return VALUE; + } + +false { + make_def_value(BOOL_TYPE); + return VALUE; + } + +[0-9]+ { + make_def_value(INT_TYPE); + return VALUE; + } + +{RE_IPV4} { + make_def_value(IPV4_TYPE); + return VALUE; + } + +{RE_IPV4NET} { + make_def_value(IPV4NET_TYPE); + return VALUE; + } + +{RE_IPV6} { + make_def_value(IPV6_TYPE); + return VALUE; + } + +{RE_IPV6NET} { + make_def_value(IPV6NET_TYPE); + return VALUE; + } + +{RE_MACADDR} { + make_def_value(MACADDR_TYPE); + return VALUE; + } +u32 { + yy_cli_parse_lval.type = INT_TYPE; + return TYPE_DEF; + } + +ipv4 { + yy_cli_parse_lval.type = IPV4_TYPE; + return TYPE_DEF; + } + +ipv4net { + yy_cli_parse_lval.type = IPV4NET_TYPE; + return TYPE_DEF; + } +ipv6 { + yy_cli_parse_lval.type = IPV6_TYPE; + return TYPE_DEF; + } + +ipv6net { + yy_cli_parse_lval.type = IPV6NET_TYPE; + return TYPE_DEF; + } +bool { + yy_cli_parse_lval.type = BOOL_TYPE; + return TYPE_DEF; + } +macaddr { + yy_cli_parse_lval.type = MACADDR_TYPE; + return TYPE_DEF; + } +\( return LP; +\) return RP; +; return SEMI; + +\\\n { yy_cli_def_lineno++; /*whitespace -- continuation => ignore */ } + +[ \t]+ /* whitespace */ + +. { + /* everything else is a syntax error */ + return SYNTAX_ERROR; + } + +%% + +static void make_def_value(vtw_type_e type) +{ + memset(&yy_cli_parse_lval.val.val, 0, sizeof(yy_cli_parse_lval.val.val)); + yy_cli_parse_lval.val.free_me = TRUE; + yy_cli_parse_lval.val.val = my_strdup(yy_cli_def_text, "cli_parse.l"); + yy_cli_parse_lval.val.val_type = type; +} diff --git a/src/cli_new.c b/src/cli_new.c new file mode 100644 index 0000000..2f2801d --- /dev/null +++ b/src/cli_new.c @@ -0,0 +1,1938 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include "cli_val.h" +#include "cli_parse.h" +#include <regex.h> + +#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__) +static 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); +static 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 +***************************************************/ +/* 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; + + if (is_silent_msg()) + exit(0); + va_start(ap, msg); + if (is_echo()) + printf("echo \""); + vprintf(msg, ap); + printf(is_echo()? "\";":"\n"); + va_end(ap); + + exit(0); +} + +/* 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; i<srtp->num; ++i) + printf("%d %s\n", i, (char *)(srtp->ptrs[i])); +} + + +void di(vtw_sorted *srtp) +{ + int i; + for (i=0; i<srtp->num; ++i) + printf("%u %u\n", i, *(unsigned int *)(srtp->ptrs[i])); +} + +static char _lock_file_[1025] = {0}; +static int _lock_fd_=-1; + +static void clean_lock_file(void) { + if(_lock_file_[0]) { + unlink(_lock_file_); + _lock_file_[0]=0; + } + if(_lock_fd_!=-1) { + close(_lock_fd_); + _lock_fd_=-1; + } +} + +boolean get_config_lock(const char* adirp, const char* lock_name) { + + boolean ret = TRUE; + + sprintf(_lock_file_, "%s/%s", adirp, lock_name); + + _lock_fd_ = open(_lock_file_, O_WRONLY | O_CREAT | O_EXCL, 0644); + + ret = (_lock_fd_!=-1); + + if(ret) { + atexit(clean_lock_file); + } + + return ret; +} + +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, unsorted, left, child, right; + void *leftp, *rightp, *childp; + const char * format; + unsigned int *parts; + vtw_type_e type = valp->val_type; + char *cp; + int cur=0, par=0, partnum=0, res=0; + void *curp, *parp; + + sortp->num = valp->cnt?valp->cnt : 1; + sortp->ptrs = my_malloc(sortp->num * sizeof(void *), "sort_ptrs"); + sortp->partnum = cond_format_lens[type]; + 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: + printf("Unknown value in switch on line %d\n", __LINE__); + exit(-5); + } + if (sortp->num < 2) + return; + /* 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; + } + } + } +} + +/* 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); + printf("%s\n", exe_string); + fprintf(stderr, "%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)); + } + 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); + printf("%s\n", exe_string); + fprintf(stderr, "%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)); + } + 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; + + 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]; + 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: + printf("Unknown value in switch on line %d\n", __LINE__); + exit(-5); + } + /* 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); + if (status) + goto free_and_return; + status = eval_va(&right, cur->vtw_node_right); + 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; +} + +/**************************************************** + 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(stderr, exe_string); + fprintf(stderr, "\n"); + printf(exe_string); + printf("\n"); + } + } + 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(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(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: + printf("VAL op in check_syn\n"); + exit(-4); + case VAR_OP: + printf("VAR op in check_syn\n"); + exit(-4); + default: + printf("unknown op %d in check_syn\n", cur->vtw_node_oper); + exit(-4); + } +} + +/************************************************* + copy_path: + copy path + if destination path owns memory, free it +**************************************************/ +static 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; + + 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, + FALSE, + &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: + *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; act<top_act; ++ act) + if (defp->actions[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_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); +} + +static 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() +{ + printf("\n\t!!! OUT OF MEMORY !!!\n"); + exit(-1); +} + +/************************************************* + 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; i<gap; ++i) + parts[dot_dot_pos+i] = 0; + } +} + +/*************************************************** + switch_path: + switch m_path between mcd and ccd directories - + modified (UNIONFS) configuration and changed + (real) configuration +****************************************************/ +void switch_path(first_seg *segp) +{ + memcpy(m_path.path_buf + segp->f_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); + printf("%s\n", exe_string); + fprintf(stderr, "%s\n", exe_string); + } else { + printf("Incorrect type of %s, need %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; i<argc;++i) + len += strlen(argv[i]); + len += argc; + cp = my_malloc(len, "dump_log"); + len = 0; + for (i=0; i<argc;++i){ + strcpy(cp+len, argv[i]); + len += strlen(argv[i]); + cp[len]= ' '; + ++len; + } + cp[len-1]=0; + printf("Command: %s\n",cp); + my_free(cp); +} +#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); + + } 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); + + } + + return 0; + + } else { + + return -1; + + } +} + +/**********************************************************/ diff --git a/src/cli_objects.c b/src/cli_objects.c new file mode 100644 index 0000000..9bb36f0 --- /dev/null +++ b/src/cli_objects.c @@ -0,0 +1,343 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include "cli_val.h" +#include "cli_parse.h" +#include <regex.h> + +#include "cli_val_engine.h" + +#include "cli_objects.h" + +/************************ Storage area: *****************/ + +static char *at_string=NULL; +static boolean in_delete_action=FALSE; +static valstruct cli_value; +static boolean in_commit=FALSE; /* TRUE if in commit program*/ +static boolean in_exec=FALSE; /* TRUE if in exec */ +static boolean _is_echo=FALSE; +static boolean _is_silent_msg=FALSE; +static first_seg f_seg_a; +static first_seg f_seg_c; +static first_seg f_seg_m; +static int in_cond_tik=0; + +/******************** Accessors: ************************/ + +static char at_buffer[1024]={0}; + +/* the string to use as $(@), must be set + before call to expand_string */ +char* get_at_string(void) { + if(at_string) { + return at_string; + } else { + return at_buffer; + } +} + +void set_at_string(char* s) { + if(s!=at_buffer) { + at_string=s; + } else { + at_string=NULL; + } +} + +void free_at_string(void) { + if(at_string) { + if(at_string!=at_buffer) free(at_string); + at_string=NULL; + } +} + +boolean is_in_delete_action(void) { + return in_delete_action; +} + +void set_in_delete_action(boolean b) { + in_delete_action=b; +} + +boolean is_in_commit(void) { + return in_commit; +} + +void set_in_commit(boolean b) { + in_commit=b; +} + +boolean is_in_exec(void) { + return in_exec; +} + +void set_in_exec(boolean b) { + in_exec=b; +} + +boolean is_echo(void) { + return _is_echo; +} + +void set_echo(boolean b) { + _is_echo=b; +} + +boolean is_silent_msg(void) { + return _is_silent_msg; +} + +void set_silent_msg(boolean b) { + _is_silent_msg=b; +} + +valstruct* get_cli_value_ptr(void) { + return &cli_value; +} + +first_seg* get_f_seg_a_ptr(void) { + return &f_seg_a; +} + +first_seg* get_f_seg_c_ptr(void) { + return &f_seg_c; +} + +first_seg* get_f_seg_m_ptr(void) { + return &f_seg_m; +} + +int is_in_cond_tik(void) { + return in_cond_tik; +} + +void set_in_cond_tik(int ict) { + in_cond_tik=ict; +} + +void dec_in_cond_tik(void) { + --in_cond_tik; +} + +const char* get_tdirp(void) { + + const char* tdirp=getenv(ENV_T_DIR); + + if (!tdirp) + tdirp = DEF_T_DIR; + if(!tdirp) + tdirp=""; + + return tdirp; +} + +const char* get_cdirp(void) { + return getenv(ENV_C_DIR); +} + +const char* get_adirp(void) { + + const char* adirp=getenv(ENV_A_DIR); + + if (!adirp) + adirp = DEF_A_DIR; + if(!adirp) + adirp=""; + + return adirp; +} + +const char* get_mdirp(void) { + + const char* mdirp=getenv(ENV_M_DIR); + + if(!mdirp) + mdirp=""; + + return mdirp; +} + +const char* get_tmpp(void) { + + const char* tmpp=getenv(ENV_TMP_DIR); + + if(!tmpp) + tmpp=""; + + return tmpp; +} + +char* get_elevp(void) { + + static char elevp_buffer[2049]; + static char* elevp=NULL; + + if(elevp==NULL) { + + const char* tmp=getenv(ENV_EDIT_LEVEL); + + if(tmp) { + strncpy(elevp_buffer,tmp,sizeof(elevp_buffer)-1); + elevp=elevp_buffer; + } + } + + return elevp; +} + +char* get_tlevp(void) { + + static char tlevp_buffer[2049]; + static char* tlevp=NULL; + + if(tlevp==NULL) { + + const char* tmp=getenv(ENV_TEMPLATE_LEVEL); + + if(tmp) { + strncpy(tlevp_buffer,tmp,sizeof(tlevp_buffer)-1); + tlevp=tlevp_buffer; + } + } + + return tlevp; +} + +/************************* Init ***************************/ + +void init_edit() +{ + int elevlen = 0; + int tlevlen = 0; + + init_paths(TRUE); + if (!get_elevp()) + bye("Not in configuration mode"); + if (!get_tlevp()) + bye("INTERNAL: environment var |%s| is not set",ENV_TEMPLATE_LEVEL); + elevlen = strlen(get_elevp()); + tlevlen = strlen(get_tlevp()); + if (elevlen > 0 && get_elevp()[elevlen - 1]=='/') { + /* cut off terminateing slash */ + --elevlen; + get_elevp()[elevlen] = 0; + } + if (elevlen) { + char *slashp; + char * scanp; + if (*get_elevp()!='/') + INTERNAL; + scanp = get_elevp() + 1; + while (TRUE) { + slashp = strchr(scanp, '/'); + if (slashp) + *slashp = 0; + push_path(&m_path, scanp); + if (slashp) { + *slashp = '/'; + scanp = slashp+1; + }else + break; + } + } + switch_path(MPATH); + if (tlevlen > 0 && get_tlevp()[tlevlen - 1]=='/') { + /* cut off terminateing slash */ + --tlevlen; + get_tlevp()[tlevlen] = 0; + } + if (tlevlen) { + char *slashp; + char * scanp; + if (*get_tlevp()!='/') + INTERNAL; + scanp = get_tlevp() + 1; + while (TRUE) { + slashp = strchr(scanp, '/'); + if (slashp) + *slashp = 0; + push_path(&t_path, scanp); + if (slashp) { + *slashp = '/'; + scanp = slashp+1; + }else + break; + } + } +} + +void init_paths(boolean for_commit) +{ + struct stat statbuf; + const char* tdirp = get_tdirp(); + const char* cdirp = get_cdirp(); + const char* adirp = get_adirp(); + const char* mdirp = get_mdirp(); + const char* tmpp = get_tmpp(); + + if (!mdirp || !mdirp[0]) + bye("Environment variable %s for temp configuration is not set",ENV_M_DIR); + if (!cdirp) + bye("INTERNAL: environment var |%s| is not set", + ENV_C_DIR); + if (!tmpp || !tmpp[0]) + bye("INTERNAL: environment var |%s| is not set", + ENV_TMP_DIR); + /* make sure that template root is present */ + + if (lstat(tdirp, &statbuf) < 0) + bye("Template directory |%s| isn't present\n", tdirp); + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + bye("Template directory |%s| isn't a directory\n", tdirp); + /* set paths to current roots */ + if (for_commit) { + int max_len; + const char *startp; + + /* make sure that master configuration root is present */ + if (lstat(adirp, &statbuf) < 0) + bye("Master configuration directory |%s| isn't present\n", adirp); + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + bye("Master configuration directory |%s| isn't a directory\n", adirp); + + get_f_seg_a_ptr()->f_segp = adirp; + get_f_seg_c_ptr()->f_segp = cdirp; + get_f_seg_m_ptr()->f_segp = mdirp; + get_f_seg_a_ptr()->f_seglen = strlen(adirp); + get_f_seg_c_ptr()->f_seglen = strlen(cdirp); + get_f_seg_m_ptr()->f_seglen = strlen(mdirp); + if(get_f_seg_a_ptr()->f_seglen > get_f_seg_m_ptr()->f_seglen) { + max_len = get_f_seg_a_ptr()->f_seglen; + startp = adirp; + }else{ + max_len = get_f_seg_m_ptr()->f_seglen; + startp = mdirp; + } + if(get_f_seg_c_ptr()->f_seglen > max_len) { + max_len = get_f_seg_c_ptr()->f_seglen; + startp = cdirp; + } + get_f_seg_a_ptr()->f_segoff = max_len - get_f_seg_a_ptr()->f_seglen; + get_f_seg_c_ptr()->f_segoff = max_len - get_f_seg_c_ptr()->f_seglen; + get_f_seg_m_ptr()->f_segoff = max_len - get_f_seg_m_ptr()->f_seglen; + init_path(&m_path, startp); + switch_path(get_f_seg_c_ptr()); + m_path.print_offset = max_len; + }else + init_path(&m_path, mdirp); + + init_path(&t_path, tdirp); +} + +/**********************************************************/ diff --git a/src/cli_objects.h b/src/cli_objects.h new file mode 100644 index 0000000..12b6b38 --- /dev/null +++ b/src/cli_objects.h @@ -0,0 +1,65 @@ +#ifndef CLI_OBJ_H +#define CLI_OBJ_H + +#include "cli_val.h" + +#define APATH (get_f_seg_a_ptr()) +#define CPATH (get_f_seg_c_ptr()) +#define MPATH (get_f_seg_m_ptr()) + +/* names of VYATTA env vars */ +#define ENV_EDIT_LEVEL "VYATTA_EDIT_LEVEL" +#define ENV_TEMPLATE_LEVEL "VYATTA_TEMPLATE_LEVEL" +#define ENV_A_DIR "VYATTA_ACTIVE_CONFIGURATION_DIR" +#define ENV_C_DIR "VYATTA_CHANGES_ONLY_DIR" +#define ENV_M_DIR "VYATTA_TEMP_CONFIG_DIR" +#define ENV_T_DIR "VYATTA_CONFIG_TEMPLATE" +#define ENV_TMP_DIR "VYATTA_CONFIG_TMP" +#define DEF_A_DIR "/opt/vyatta/config/active" +#define DEF_T_DIR "/opt/vyatta/share/ofr/template" +#define ENV_OLD_PS1 "VYATTA_OLD_PS1" + +/* the string to use as $(@), must be set + before call to expand_string */ +char* get_at_string(void); +void set_at_string(char* s); +void free_at_string(void); + +boolean is_in_delete_action(void); +void set_in_delete_action(boolean b); + +boolean is_in_commit(void); +void set_in_commit(boolean b); + +boolean is_in_exec(void); +void set_in_exec(boolean b); + +boolean is_echo(void); +void set_echo(boolean b); + +boolean is_silent_msg(void); +void set_silent_msg(boolean b); + +valstruct* get_cli_value_ptr(void); + +first_seg* get_f_seg_a_ptr(void); +first_seg* get_f_seg_c_ptr(void); +first_seg* get_f_seg_m_ptr(void); + +int is_in_cond_tik(void); +void set_in_cond_tik(int ict); +void dec_in_cond_tik(void); + +const char* get_tdirp(void); +const char* get_cdirp(void); +const char* get_adirp(void); +const char* get_mdirp(void); +const char* get_tmpp(void); + +char* get_elevp(void); +char* get_tlevp(void); + +void init_edit(void); +void init_paths(boolean for_commit); + +#endif /* CLI_OBJ_H */ diff --git a/src/cli_parse.y b/src/cli_parse.y new file mode 100644 index 0000000..f0e43a8 --- /dev/null +++ b/src/cli_parse.y @@ -0,0 +1,206 @@ +%{ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "cli_val.h" + +extern int yy_cli_def_lineno; +static vtw_def *parse_defp; +static int parse_status; +static boolean cli_def_type_only=0; /*{ if (cli_def_type_only) YYACCEPT;}*/ +/* XXX: sigh, the -p flag to yacc should do this for us kkkk*/ +#define yystacksize tpltstacksize +#define yysslim tpltsslim + +/* forward prototypes */ +extern int yy_cli_parse_parse(); +extern int yy_cli_def_lex(); +extern void yy_cli_parse_error(const char *); +static void cli_deferror(const char *); + extern FILE *yy_cli_def_in; +#define YYDEBUG 1 +#define yy_cli_parse_lex yy_cli_def_lex +%} +%token EOL +%token MULTI +%token TAG +%token TYPE +%token HELP +%token DEFAULT +%token PATTERN +%token EXEC +%token SYNTAX +%token COMMIT +%token CHECK +%left SEMI +%token <val>VALUE +%token <type>TYPE_DEF +%token <strp>VAR +%token <strp> STRING +%token <strp> EX_STRING +%token SYNTAX_ERROR +%token <action>ACTION +%left ASSIGN +%left OR +%left AND +%right NOT +%left COMMA +%nonassoc <cond>COND +%token RP +%token LP +%type <nodep> val +%type <nodep> exp +%type <val> val0 +%type <nodep> action +%type <nodep> action0 + +%union { + char *strp; + valstruct val; + vtw_type_e type; + vtw_cond_e cond; + vtw_node *nodep; + vtw_act_type action; +} + +%% + +input: tag + | EOL input + | tag otherinput + ; + +otherinput: type EOL + | cause EOL + | otherinput type EOL + | otherinput cause EOL + | otherinput EOL + | syntax_error + ; + +tag: /* empty */ + | TAG EOL {parse_defp->tag = TRUE;} + | MULTI EOL {parse_defp->multi = TRUE;} + ; + +type: TYPE TYPE_DEF SEMI STRING + { parse_defp->def_type = $2; + parse_defp->def_type_help = $4; } + ; + + +type: TYPE TYPE_DEF + { parse_defp->def_type = $2; + } + ; + +cause: help_cause + | default_cause + | syntax_cause + | ACTION action { append(parse_defp->actions + $1, $2, 0);} + ; + +help_cause: HELP STRING + { parse_defp->def_type_help = $2; /* no semantics for now */ + } + +default_cause: DEFAULT VALUE + { + if ($2.val_type != parse_defp->def_type) + yy_cli_parse_error((const char *)"Bad default\n"); + parse_defp->def_default = $2.val; + } +default_cause: DEFAULT STRING + { + if (TEXT_TYPE != parse_defp->def_type) + yy_cli_parse_error((const char *)"Bad default\n"); + parse_defp->def_default = $2; + } + +syntax_cause: SYNTAX exp {append(parse_defp->actions + syntax_act, $2, 0);} + ; + +syntax_cause: COMMIT exp {append(parse_defp->actions + syntax_act, $2, 1);} + ; + +action0: STRING { $$ = make_node(EXEC_OP, make_str_node($1),NULL);} + ; +action: action0 + | action0 SEMI STRING + {$$ = make_node(HELP_OP, $1, make_str_node($3));} + | exp + ; +exp: LP exp RP + {$$=$2;} + | exp AND exp {$$ = make_node(AND_OP,$1,$3);} + | exp OR exp {$$ = make_node(OR_OP,$1,$3);} + | val COND val + {$$ = make_node(COND_OP,$1,$3);$$->vtw_node_aux = $2;} + | PATTERN VAR STRING + { $$ = make_node(PATTERN_OP,make_var_node($2), + make_str_node($3));} + | EXEC STRING + { $$ = make_node(EXEC_OP,make_str_node($2),NULL);} + | NOT exp {$$ = make_node(NOT_OP,$2,NULL);} + | exp SEMI STRING + {$$ = make_node(HELP_OP, $1, make_str_node($3));} + | VAR ASSIGN val + {$$ = make_node(ASSIGN_OP, make_var_node($1), $3);} + ; + +val: VAR {$$ = make_var_node($1);} + | val0 {$$ = make_val_node(&($1));} + | EX_STRING {$$=make_str_node0($1, B_QUOTE_OP);} + ; + +val0: VALUE + + | val0 COMMA val0 { add_val(&($1), &($3)); $$=$1; } + + | STRING {$$ = str2val($1);} + ; + +syntax_error: SYNTAX_ERROR { + cli_deferror("syntax error"); + } + ; + + +%% +char *parse_path; +int parse_def(vtw_def *defp, char *path, boolean type_only) +{ + int status; + yy_cli_def_lineno = 1; + parse_status = 0; + parse_defp = defp; + cli_def_type_only = type_only; + yy_cli_def_in = fopen(path, "r"); +#if 0 + yy_cli_parse_debug = 1; +#endif + if (!yy_cli_def_in) + return -5; + parse_path = path; + status = yy_cli_parse_parse(); /* 0 is OK */ + fclose(yy_cli_def_in); + return status; +} +static void +cli_deferror(const char *s) +{ + printf("Error: %s in file %s, line %d\n",s, parse_path, + yy_cli_def_lineno); +} + +void yy_cli_parse_error(const char *s) +{ + cli_deferror(s); +} + + + + + diff --git a/src/cli_path_utils.c b/src/cli_path_utils.c new file mode 100644 index 0000000..4045516 --- /dev/null +++ b/src/cli_path_utils.c @@ -0,0 +1,535 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli path-handling utilities + + **** End License **** + +*************************************************************************/ + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <regex.h> +#include <dirent.h> + +#include <string.h> + +#include "cli_path_utils.h" + +/********************* + * Data definitions + * + *********************/ + +typedef char* clind_dir_name; + +/** + * Definition of the path structure to hold all path-like information: + */ + +struct _clind_path_impl { + + int absolute; + int path_len; + char* path_string; + clind_dir_name* path; + +}; + +/****************************** + * Path utils. We use them + * to manipulate the path-like + * structures. + * + ******************************/ + +static void clind_reset_path_string(clind_path_impl* obj) { + + char* newpath=NULL; + + if(obj->path_len<1) { + + newpath=strdup(""); + + } else { + + int i=0; + + if(!obj->absolute || (strlen(obj->path[0])>0 && ((char*)(obj->path[0]))[0]=='/')) { + newpath=strdup(obj->path[0]); + } else { + newpath=(char*)malloc(strlen(obj->path[0])+1+1); + newpath[0]='/'; + strcpy(newpath+1,(char*)(obj->path[0])); + } + + for(i=1;i<obj->path_len;i++) { + newpath=(char*)realloc(newpath,strlen(newpath)+1+strlen(obj->path[i])+1); + strcpy(newpath+strlen(newpath),"/"); + strcpy(newpath+strlen(newpath),obj->path[i]); + } + } + + if(obj->path_string==NULL) { + obj->path_string=newpath; + } else { + obj->path_string=(char*)realloc(obj->path_string,strlen(newpath)+1); + strcpy(obj->path_string,newpath); + free(newpath); + } +} + +clind_path_ref clind_path_construct(const char* path) { + + if(!path) return NULL; + else { + + const char* delim="/ \t"; + + clind_path_impl *obj = (clind_path_impl*)malloc(sizeof(clind_path_impl)); + char* tokpath=strdup(path); + char* token=strtok(tokpath,delim); + + obj->path_len=0; + obj->path_string=strdup(""); + obj->path=NULL; + + while(token) { + clind_path_push((clind_path_ref)obj,token); + token=strtok(NULL,delim); + } + + free(tokpath); + + obj->absolute=(*path=='/'); + + clind_reset_path_string(obj); + + return (clind_path_ref)obj; + } +} + +void clind_path_destruct(clind_path_ref* path) { + + if(path && *path) { + + clind_path_impl* obj = (clind_path_impl*)(*path); + + if(obj->path_string) { + free(obj->path_string); + obj->path_string=NULL; + } + + if(obj->path) { + while(obj->path_len>0) { + char* dir_name = (char*)(obj->path[obj->path_len-1]); + if(dir_name) { + free(dir_name); + } + obj->path_len--; + } + free(obj->path); + obj->path=0; + } + + *path=0; + } +} + +clind_path_ref clind_path_clone(const clind_path_ref path) { + + clind_path_ref ret=NULL; + if(path) { + + clind_path_impl* obj = (clind_path_impl*)path; + + ret = clind_path_construct(obj->path_string); + + if(ret) { + + ((clind_path_impl*)ret)->absolute=obj->absolute; + + clind_reset_path_string((clind_path_impl*)ret); + } + } + + return ret; +} + +int clind_path_get_size(clind_path_ref path) { + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + return obj->path_len; + } + return 0; +} + +const char* clind_path_get_path_string(clind_path_ref path) { + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + if(obj->path_string) { + return obj->path_string; + } + } + return ""; +} + +int clind_path_is_absolute(clind_path_ref path) { + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + return obj->absolute; + } + return 0; +} + +void clind_path_push(clind_path_ref path,const char* dir) { + + if(path && dir && *dir) { + + clind_path_impl* obj = (clind_path_impl*)path; + int absolute=(*dir=='/'); + + while(*dir && *dir=='/') dir++; + + if(obj->path_len<=0) { + + obj->path_len=1; + obj->absolute=absolute; + + if(obj->path) { + free(obj->path); + } + + obj->path=(clind_dir_name*)malloc(sizeof(clind_dir_name)); + obj->path[0]=(clind_dir_name)strdup(dir); + + } else { + + obj->path_len++; + + obj->path=(clind_dir_name*)realloc(obj->path, + sizeof(clind_dir_name)*(obj->path_len)); + obj->path[obj->path_len-1]=strdup(dir); + } + + clind_reset_path_string(obj); + } +} + +char* clind_path_pop_string(clind_path_ref path) { + + char* ret=NULL; + + if(path) { + + clind_path_impl* obj = (clind_path_impl*)path; + + if(obj->path_len<=0) { + return ret; + } + + obj->path_len--; + + if(obj->path) { + if(obj->path[obj->path_len]) { + ret = obj->path[obj->path_len]; + obj->path[obj->path_len]=NULL; + } + } + + if(obj->path_len<1) { + obj->absolute=0; + } + + clind_reset_path_string(obj); + } + + return ret; +} + +int clind_path_pop(clind_path_ref path) { + + int ret=-1; + + char* ps = clind_path_pop_string(path); + + if(ps) { + free(ps); + ret=0; + } + + return ret; +} + +const char* clind_path_last_string(clind_path_ref path) { + + char* ret=NULL; + + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + if(obj->path && obj->path_len>0) { + ret=obj->path[obj->path_len-1]; + } + } + + return ret; +} + +void clind_path_unshift(clind_path_ref path,const char* dir) { + + if(path && dir && *dir) { + + clind_path_impl* obj = (clind_path_impl*)path; + + int absolute=(*dir=='/'); + + while(*dir && *dir=='/') dir++; + + if(obj->path_len<=0) { + + clind_path_push(path,dir); + + } else { + + obj->path_len++; + + obj->path=(clind_dir_name*)realloc(obj->path, + sizeof(clind_dir_name)*(obj->path_len)); + memmove((char*)(obj->path)+sizeof(clind_dir_name),(char*)(obj->path), + sizeof(clind_dir_name)*(obj->path_len-1)); + obj->path[0]=strdup(dir); + + } + + obj->absolute=absolute; + + clind_reset_path_string(obj); + } +} + +const char* clind_path_get_string(clind_path_ref path,int index) { + + const char* ret=NULL; + + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + if(obj->path && obj->path_len>index) { + ret=obj->path[index]; + } + } + + return ret; +} + +const char* clind_path_first_string(clind_path_ref path) { + return clind_path_get_string(path,0); +} + +char* clind_path_shift_string(clind_path_ref path) { + + char* ret=NULL; + + if(path) { + + clind_path_impl* obj = (clind_path_impl*)path; + + if(obj->path_len<=0) { + return ret; + } + + obj->path_len--; + + if(obj->path) { + if(obj->path[0]) { + ret = obj->path[0]; + obj->path[0]=NULL; + } + + memmove((char*)(obj->path),(char*)(obj->path)+sizeof(clind_dir_name), + sizeof(clind_dir_name)*obj->path_len); + } + + obj->absolute=0; + + clind_reset_path_string(obj); + } + + return ret; +} + +int clind_path_shift(clind_path_ref path) { + + int ret=-1; + + char* ps = clind_path_shift_string(path); + + if(ps) { + free(ps); + ret=0; + } + + return ret; +} + +void clind_path_debug_print(clind_path_ref path) { + + if(path) { + + int i=0; + clind_path_impl* obj = (clind_path_impl*)path; + + if(obj->path_string) { + printf("obj->path_string=%s, obj->path_len=%d,obj->absolute=%d\n", + obj->path_string,obj->path_len,obj->absolute); + } else { + printf("obj->path_string=NULL, obj->path_len=%d,obj->absolute=%d\n", + obj->path_len,obj->absolute); + } + + if(obj->path) { + for(i=0;i<obj->path_len;i++) { + if(obj->path[i]) { + printf(" obj->path[%d]=%s\n",i,obj->path[i]); + } else { + printf(" obj->path[%d]=NULL\n",i); + } + } + } else { + printf(" obj->path=NULL\n"); + } + } +} + +int clind_file_exists(const char* dir,const char* file) { + + int ret=0; + + if(file) { + + char* fname=strdup(file); + struct stat statbuf; + + if(dir) { + free(fname); + fname=(char*)malloc(strlen(dir)+1+strlen(file)+1); + strcpy(fname,dir); + strcpy(fname+strlen(fname),"/"); + strcpy(fname+strlen(fname),file); + } + + if (lstat(fname, &statbuf) == 0) { + ret=1; + } + + free(fname); + } + + return ret; +} + +char *clind_unescape(const char *name) +{ + const char *cp; + char *rcp, *ret; + char len; + + for(cp=name, len=0;*cp;++cp, ++len) + if(*cp=='%') + cp +=2; + rcp = ret = malloc(len+1); + for(cp=name, len=0;*cp;++cp, ++rcp) + if(*cp=='%') { + ++cp; + if (*cp >='a' && *cp<='f') + *rcp = (*cp-'a'+10)*16; + else if (*cp >='A' && *cp<='F') + *rcp = (*cp-'A'+10)*16; + else if (*cp >='0' && *cp<='9') + *rcp = (*cp-'0')*16; + else { + printf("Bad escape in |%s|\n", name); + exit(-1); + } + ++cp; + if (*cp >='a' && *cp<='f') + *rcp += (*cp-'a'+10); + else if (*cp >='A' && *cp<='F') + *rcp += (*cp-'A'+10); + else if (*cp >='0' && *cp<='9') + *rcp += (*cp-'0'); + else { + printf("Bad escape in |%s|\n", name); + exit(-1); + } + }else + *rcp = *cp; + *rcp = 0; + return ret; +} + +char* clind_quote(const char* s) { + + char* ret=NULL; + if(s) { + int i=0; + int len=strlen(s); + int sz=0; + char SQ='\''; + + ret=(char*)malloc(1+5*len+1+1+10); + ret[sz++]=SQ; + + for(i=0;i<len;i++) { + if(s[i]==SQ) { + ret[sz++]=SQ;/*'\''*/ + ret[sz++]='\\'; + ret[sz++]=SQ; + ret[sz++]=SQ; + } else { + ret[sz++]=s[i]; + } + } + + ret[sz++]=SQ; + ret[sz]=0; + } + return ret; +} + + diff --git a/src/cli_path_utils.h b/src/cli_path_utils.h new file mode 100644 index 0000000..6cc3be8 --- /dev/null +++ b/src/cli_path_utils.h @@ -0,0 +1,72 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli path-handling utilities + + **** End License **** + +*************************************************************************/ + +#if !defined(__CLI_PATH_UTILS__) +#define __CLI_PATH_UTILS__ + +/******************* + * Type definitions + * + *******************/ + +typedef struct _clind_path_impl clind_path_impl; +typedef clind_path_impl* clind_path_ref; + +/************************ + * Path utils + * + ************************/ + +clind_path_ref clind_path_construct(const char* path); +void clind_path_destruct(clind_path_ref* path); +clind_path_ref clind_path_clone(const clind_path_ref path); + +int clind_path_get_size(clind_path_ref path); +const char* clind_path_get_path_string(clind_path_ref path); +void clind_path_debug_print(clind_path_ref path); +int clind_path_is_absolute(clind_path_ref path); + +int clind_path_pop(clind_path_ref path); +char* clind_path_pop_string(clind_path_ref path); +const char* clind_path_last_string(clind_path_ref path); +void clind_path_push(clind_path_ref path,const char* dir); + +int clind_path_shift(clind_path_ref path); +char* clind_path_shift_string(clind_path_ref path); +const char* clind_path_get_string(clind_path_ref path,int index); +const char* clind_path_first_string(clind_path_ref path); +void clind_path_unshift(clind_path_ref path,const char* dir); + +int clind_file_exists(const char* dir,const char* file); + +char *clind_unescape(const char *name); +char* clind_quote(const char* s); + +#endif /* __CLI_PATH_UTILS__ */ diff --git a/src/cli_val.h b/src/cli_val.h new file mode 100644 index 0000000..8a98a88 --- /dev/null +++ b/src/cli_val.h @@ -0,0 +1,206 @@ +#ifndef CLI_DEF_H +#define CLI_DEF_H + +#define BITWISE 0 /* no partial commit */ + +#define boolean int +#define TRUE 1 +#define FALSE 0 + +/* allocation unit for vals in valstruct */ +#define MULTI_ALLOC 5 /* we have room if cnt%MULTI_ALLOC != 0 */ + +typedef enum { + do_del_mode, + del_mode, + create_mode, + update_mode +}vtw_cmode; +typedef enum { + ERROR_TYPE, + INT_TYPE, + IPV4_TYPE, + IPV4NET_TYPE, + IPV6_TYPE, + IPV6NET_TYPE, + MACADDR_TYPE, + DOMAIN_TYPE, /*end of addr types */ + TEXT_TYPE, + BOOL_TYPE +}vtw_type_e; + +typedef enum { + EQ_COND = 1, + NE_COND, + LT_COND, + LE_COND, + GT_COND, + GE_COND, + IN_COND, + TOP_COND +}vtw_cond_e; +/* IN_COND is like EQ for singular compare, but OR for multivalue right operand */ + +typedef enum { + LIST_OP, /* right is next, left is list elem */ + HELP_OP, /* right is help string, left is elem */ + EXEC_OP, /* left command string, right help string */ + PATTERN_OP, /* left to var, right to pattern */ + OR_OP, + AND_OP, + NOT_OP, + COND_OP, /* aux field specifies cond type (GT, GE, etc.)*/ + VAL_OP, /* for strings used in other nodes */ + VAR_OP, /* string points to var */ + B_QUOTE_OP, /* string points to operand to be executed */ + ASSIGN_OP /* left to var, right to exp */ +}vtw_oper_e; + +typedef struct { + vtw_type_e val_type; + char *val; + int cnt; /* >0 means multivalue */ + char **vals; /* We might union with val */ + boolean free_me; +}valstruct; + +typedef struct vtw_node{ + vtw_oper_e vtw_node_oper; + struct vtw_node *vtw_node_left; + struct vtw_node *vtw_node_right; + char *vtw_node_string; + int vtw_node_aux; + vtw_type_e vtw_node_type; + valstruct vtw_node_val; /* we'll union it later */ +}vtw_node; + +typedef struct { + vtw_node *vtw_list_head; + vtw_node *vtw_list_tail; +}vtw_list; + +typedef struct { + int t_lev; + int m_lev; +}vtw_mark; + +typedef enum { + delete_act, + create_act, + activate_act, + update_act, + syntax_act, + commit_act, + begin_act, + end_act, + top_act +}vtw_act_type; + +typedef struct { + vtw_type_e def_type; + char *def_type_help; + char *def_default; + boolean tag; + boolean multi; + vtw_list actions[top_act]; +}vtw_def; + +typedef struct { + const char *f_segp; + int f_seglen; + int f_segoff; +} first_seg; +/* the first segment might be ADIR, or CDIR, or MDIR + we reserve space large enough for any one. + If the shorter one is used, it right aligned. + path points to the start of the current first + segment +*/ +typedef struct { + char *path_buf; /* path buffer */ + char *path; /* path */ + int path_len; /* path length used */ + int path_alloc; /* allocated - 1*/ + int *path_ends; /* path ends for dif levels*/ + int path_lev; /* how many used */ + int path_ends_alloc; /* how many allocated */ + int print_offset; /* for additional optional output information */ +} vtw_path; /* vyatta tree walk */ + +typedef struct { + int num; + int partnum; + void **ptrs; + unsigned int *parts; +}vtw_sorted; + +extern int char2val(vtw_def *def, char *value, valstruct *valp); +extern int get_value(char **valpp, vtw_path *pathp); +extern int get_value_to_at_string(vtw_path *pathp); +extern vtw_node * make_node(vtw_oper_e oper, vtw_node *left, + vtw_node *right); +extern vtw_node *make_str_node(char *str); +extern vtw_node *make_var_node(char *str); +extern vtw_node *make_str_node0(char *str, vtw_oper_e op); +extern void append(vtw_list *l, vtw_node *n, int aux); +extern int parse_def(vtw_def *defp, char *path, boolean type_only); + +extern int yy_cli_val_lex(void); +extern void cli_val_start(char *s); +extern void cli_val_done(void); +extern void init_path(vtw_path *path, const char *root); +extern void pop_path(vtw_path *path); +extern void push_path(vtw_path *path, char *segm); +extern void free_def(vtw_def *defp); +extern void free_sorted(vtw_sorted *sortp); +extern void *my_malloc(size_t size, const char *name); +extern void *my_realloc(void *ptr, size_t size, const char *name); + +extern vtw_path m_path, t_path; + +/************************************************* + GLOBAL FUNCTIONS +***************************************************/ +extern void add_val(valstruct *first, valstruct *second); +extern int cli_val_read(char *buf, int max_size); +extern vtw_node *make_val_node(valstruct *val); +extern char *my_strdup(const char *s, const char *name); +extern valstruct str2val(char *cp); +extern void dump_tree(vtw_node *node, int lev); +extern void dump_def(vtw_def *defp); +extern boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond); +extern void out_of_memory(void); +extern boolean validate_value(vtw_def *def, + char *value); +extern void internal_error(int line, char *file); +extern void done(void); +extern void del_value(vtw_def *defp, char *cp); +extern void bye(char *msg, ...); +extern void print_msg(char *msg, ...); +extern void switch_path(first_seg *seg); +extern void vtw_sort(valstruct *valp, vtw_sorted *sortp); +extern void free_val(valstruct *val); +extern void my_free(void *ptr); +extern void touch(void); +extern void dump_log(int argc, char **argv); +extern char *type_to_name(vtw_type_e type); +extern boolean execute_list(vtw_node *cur, vtw_def *def); +extern void touch_dir(const char *dp); + +void mark_paths(vtw_mark *markp); +void restore_paths(vtw_mark *markp); + +extern boolean get_config_lock(const char* adirp, const char* lock_name); + +#define VTWERR_BADPATH -2 +#define VTWERR_OK 0 +#define TAG_NAME "node.tag" +#define DEF_NAME "node.def" +#define VAL_NAME "node.val" +#define MOD_NAME ".modified" +#define OPQ_NAME ".wh.__dir_opaque" +#define LOCK_NAME ".commit.lck" + +#define INTERNAL internal_error(__LINE__, __FILE__) + +#endif diff --git a/src/cli_val.l b/src/cli_val.l new file mode 100644 index 0000000..5856cea --- /dev/null +++ b/src/cli_val.l @@ -0,0 +1,293 @@ +%{ +#include "cli_val.h" +#include "cli_parse.h" +#include "cli_objects.h" +static void make_val_value(vtw_type_e type); +#define YY_INPUT(buf,result,max_size) (result)=cli_val_read((buf), (max_size)) +%} +%option noyywrap +%option nounput +%option never-interactive + +/* + * Regular expressions of IP and MAC addresses, URLs, etc. + */ + +/* + * IPv4 address representation. + */ +RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2}) +RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3} +RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9]) +RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN} + +/* + * IPv6 address representation in Augmented Backus-Naur Form (ABNF) + * as defined in RFC-2234. + * IPv6 address representation taken from RFC-3986: + * + * IPv6address = 6( h16 ":" ) ls32 + * / "::" 5( h16 ":" ) ls32 + * / [ h16 ] "::" 4( h16 ":" ) ls32 + * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + * / [ *4( h16 ":" ) h16 ] "::" ls32 + * / [ *5( h16 ":" ) h16 ] "::" h16 + * / [ *6( h16 ":" ) h16 ] "::" + * + * h16 = 1*4HEXDIG + * ls32 = ( h16 ":" h16 ) / IPv4address + * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + * dec-octet = DIGIT ; 0-9 + * / %x31-39 DIGIT ; 10-99 + * / "1" 2DIGIT ; 100-199 + * / "2" %x30-34 DIGIT ; 200-249 + * / "25" %x30-35 ; 250-255 + */ + +RE_H16 [a-fA-F0-9]{1,4} +RE_H16_COLON {RE_H16}":" +RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4}) +RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32} +RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32} +RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32} +RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32} +RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32} +RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32} +RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32} +RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16} +RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::" +RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9} +RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]? +RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN} + +/* + * Ethernet MAC address representation. + */ +RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5} + +/* + * URL-related regular expressions. + * + * Implementation is based on the BNF-like specification from: + * - RFC-1738: HTTP, FTP, FILE + * - RFC-3617: TFTP + * - RFC-3986: update of RFC-1738 + */ +RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP} + +/* + * URL schemeparts for IP based protocols. + * Representation taken from RFC-1738, and some of it is updated by RFC-3986. + * + * login = [ user [ ":" password ] "@" ] hostport + * hostport = host [ ":" port ] + * host = hostname | hostnumber + * hostname = *[ domainlabel "." ] toplabel + * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit + * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit + * alphadigit = alpha | digit + * hostnumber = digits "." digits "." digits "." digits + * port = digits + * user = *[ uchar | ";" | "?" | "&" | "=" ] + * password = *[ uchar | ";" | "?" | "&" | "=" ] + */ +RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT} +RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})? +RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL} +RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]" +RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+ +RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL} +RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT} +RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS} +RE_URL_PORT {RE_URL_DIGITS} +RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* +RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* + +/* + * FILE URL regular expression. + * Representation taken from RFC-1738. + * + * fileurl = "file://" [ host | "localhost" ] "/" fpath + */ +RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH} + +/* + * FTP URL regular expression. + * Representation taken from RFC-1738. + * + * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ] + * fpath = fsegment *[ "/" fsegment ] + * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ] + * ftptype = "A" | "I" | "D" | "a" | "i" | "d" + */ +RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)? +RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})* +RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")* +RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d" + +/* + * HTTP URL regular expression. + * Representation taken from RFC-1738. + * + * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ] + * hpath = hsegment *[ "/" hsegment ] + * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + */ +RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)? +RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})* +RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* +RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* + +/* + * TFTP URL regular expression. + * Representation taken from RFC-3617. + * + * tftpURI = "tftp://" host "/" file [ mode ] + * mode = ";" "mode=" ( "netascii" / "octet" ) + * file = *( unreserved / escaped ) + * host = <as specified by RFC 2732 [3]> + * unreserved = <as specified in RFC 2396 [4]> + * escaped = <as specified in RFC 2396> + */ +RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})? +RE_URL_TFTP_MODE ";""mode="("netascii"|"octet") +RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})* + +/* + * URL-related miscellaneous definitions. + * Representation taken from RFC-1738 and from RFC-3986. + * + * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | + * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | + * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + * "y" | "z" + * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + * alpha = lowalpha | hialpha + * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + * "8" | "9" + * safe = "$" | "-" | "_" | "." | "+" + * extra = "!" | "*" | "'" | "(" | ")" | "," + * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`" + * punctuation = "<" | ">" | "#" | "%" | <"> + * + * + * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" + * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + * "a" | "b" | "c" | "d" | "e" | "f" + * escape = "%" hex hex + * + * unreserved = alpha | digit | safe | extra + * uchar = unreserved | escape + * xchar = unreserved | reserved | escape + * digits = 1*digit + * + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +RE_URL_LOWALPHA [a-z] +RE_URL_HIALPHA [A-Z] +RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA} +RE_URL_DIGIT [0-9] +RE_URL_SAFE "$"|"-"|"_"|"."|"+" +RE_URL_EXTRA "!"|"*"|"'"|"("|")"|"," +RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`" +RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<"> +RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"=" +RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f] +RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG} +RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA} +RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE} +RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE} +RE_URL_DIGITS {RE_URL_DIGIT}{1,} +RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"=" + + +%% +:: { + make_val_value(IPV6_TYPE); + return VALUE; + } + +true { + make_val_value(BOOL_TYPE); + return VALUE; + } + +false { + make_val_value(BOOL_TYPE); + return VALUE; + } + +[0-9]+ { + make_val_value(INT_TYPE); + return VALUE; + } + +{RE_IPV4} { + make_val_value(IPV4_TYPE); + return VALUE; + } + +{RE_IPV4NET} { + make_val_value(IPV4NET_TYPE); + return VALUE; + } + +{RE_IPV6} { + make_val_value(IPV6_TYPE); + return VALUE; + } + +{RE_IPV6NET} { + make_val_value(IPV6NET_TYPE); + return VALUE; + } + +{RE_MACADDR} { + make_val_value(MACADDR_TYPE); + return VALUE; + } + +\\\n /* whitespace */ + +[ \t]+ /*whitespace */ +\n return EOL; +. { + /* everything else is a syntax error */ + return SYNTAX_ERROR; + } + + +%% +static void make_val_value(vtw_type_e type) +{ + memset(get_cli_value_ptr(), 0, sizeof(valstruct)); + get_cli_value_ptr()->free_me = TRUE; + get_cli_value_ptr()->val = my_strdup(yytext, "cli_val.l"); + get_cli_value_ptr()->val_type = type; +} + + + + + + + + + + + + + + + + + + diff --git a/src/cli_val_engine.c b/src/cli_val_engine.c new file mode 100644 index 0000000..d597c68 --- /dev/null +++ b/src/cli_val_engine.c @@ -0,0 +1,881 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli handler for the reference variables + + **** End License **** + +*************************************************************************/ + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <regex.h> +#include <dirent.h> + +#include <string.h> + +#include "cli_val_engine.h" + +/********************* + * Data definitions + * + *********************/ + +/** + * Special file names: + */ +#define VALUE_FILE ("node.val") +#define NODE_TAG ("node.tag") +#define NODE_DEF ("node.def") + +/** + * "Command" definition (element of a variable path): + */ + +typedef struct { + clind_cmd_type cmd_type; + const char* text[10]; +} cmd_parse_definition; + +/** + * Structure to hold information about possible command entries: + */ + +static cmd_parse_definition cmd_parse_definitions[] = { + { CLIND_CMD_PARENT_VALUE, {"..","@",NULL } }, + { CLIND_CMD_NEIGHBOR, {"..","*",NULL } }, + { CLIND_CMD_PARENT, {"..",NULL } }, + { CLIND_CMD_PARENT, {".","..",NULL } }, + { CLIND_CMD_CHILD, {".","*",NULL } }, + { CLIND_CMD_VALUE, {".","@",NULL } }, + { CLIND_CMD_SELF_NAME, {".",NULL } }, + { CLIND_CMD_VALUE, {"@",NULL } }, + { CLIND_CMD_MULTI_VALUE, {"@@",NULL } }, + { CLIND_CMD_CHILD, {"*",NULL } }, + { CLIND_CMD_UNKNOWN, {NULL} } +}; + +/************************ + * Cmd utils forward declarations + * + ************************/ + +static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd); + +/****************************** + * Variable evaluation engine * + * * + ******************************/ + +/** + * For a given config path, return the 'value' of the path. + * If the path ends with "node.val", then return the file content. + * If not, then return the last path element. + * If path is empty, or the file is empty, or the file does not exist, + * then return NULL. + * The user of this function is responsible for the memory deallocation. + */ + +static char** clind_get_current_value(clind_path_ref cfg_path, + clind_path_ref tmpl_path, + int check_existence, + vtw_type_e *val_type, + const char* root_tmpl_path, + int return_value_file_name, + int multi_value, + int *ret_size) { + + char** ret=NULL; + *ret_size=0; + + if(val_type) *val_type=TEXT_TYPE; + + if(cfg_path && (clind_path_get_size(cfg_path)>0)) { + + clind_path_ref tmpl_path_clone = clind_path_clone(tmpl_path); + + const char* cfg_path_string = clind_path_get_path_string(cfg_path); + const char* cfg_end = clind_path_last_string(cfg_path); + const char* tmpl_path_string = clind_path_get_path_string(tmpl_path_clone); + const char* tmpl_end = clind_path_last_string(tmpl_path_clone); + + /* + printf("%s:111.111:%s:%s:%s:%s:%d\n",__FUNCTION__, + cfg_path_string, + cfg_end, + tmpl_path_string, + tmpl_end, + multi_value); + */ + + if(cfg_path_string && cfg_end) { + + if(strcmp(cfg_end,VALUE_FILE)==0) { + + /* Value reference: */ + + if(return_value_file_name) { + + ret=(char**)realloc(ret,sizeof(char*)*1); + ret[0]=strdup(cfg_path_string); + *ret_size=1; + + } else { + + FILE* f = fopen(cfg_path_string,"r"); + if(f) { + char buffer[8193]; + if(multi_value) { + while(fgets(buffer, sizeof(buffer)-1,f)) { + int len=strlen(buffer); + while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) { + buffer[len-1]=0; + len--; + } + if(len>0) { + ret=(char**)realloc(ret,sizeof(char*)*(*ret_size+1)); + ret[*ret_size]=strdup(buffer); + *ret_size+=1; + } + } + } else { + int sz = fread(buffer, 1, sizeof(buffer)-1, f); + if(sz>0) { + int len=0; + buffer[sz]=0; + len=strlen(buffer); + while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) { + buffer[len-1]=0; + len--; + } + ret=(char**)malloc(sizeof(char*)*1); + ret[0]=strdup(buffer); + *ret_size=1; + } + } + fclose(f); + } + } + + } else if(return_value_file_name) { + + ret=(char**)realloc(ret,sizeof(char*)*1); + ret[0]=(char*)malloc(strlen(cfg_path_string)+1+strlen(VALUE_FILE)+1); + strcpy(ret[0],cfg_path_string); + strcpy(ret[0]+strlen(ret[0]),"/"); + strcpy(ret[0]+strlen(ret[0]),VALUE_FILE); + *ret_size=1; + + } else { + + struct stat statbuf; + + /* Directory reference: */ + + if(!check_existence || (lstat(cfg_path_string, &statbuf) == 0)) { + ret=(char**)realloc(ret,sizeof(char*)*1); + ret[0]=clind_unescape(cfg_end); + *ret_size=1; + } + } + + if(ret) { + if(tmpl_end && (strcmp(tmpl_end,NODE_TAG)==0)) { + clind_path_pop(tmpl_path_clone); + tmpl_path_string = clind_path_get_path_string(tmpl_path_clone); + tmpl_end = clind_path_last_string(tmpl_path_clone); + } + } + + if(ret && tmpl_path_string && !return_value_file_name) { + + vtw_def def; + struct stat statbuf; + int fn_node_def_size=strlen(tmpl_path_string)+1+strlen(NODE_DEF)+1; + char* fn_node_def=(char*)malloc(fn_node_def_size); + + memset(&def, 0, sizeof(def)); + + fn_node_def[0]=0; + + if(*tmpl_path_string!='/' && root_tmpl_path) { + fn_node_def_size+=strlen(root_tmpl_path+1); + fn_node_def=(char*)realloc(fn_node_def,fn_node_def_size); + strcpy(fn_node_def+strlen(fn_node_def),root_tmpl_path); + strcpy(fn_node_def+strlen(fn_node_def),"/"); + } + + strcpy(fn_node_def+strlen(fn_node_def),tmpl_path_string); + strcpy(fn_node_def+strlen(fn_node_def),"/"); + strcpy(fn_node_def+strlen(fn_node_def),NODE_DEF); + + if ((lstat(fn_node_def, &statbuf) == 0)&& + (parse_def(&def, fn_node_def, TRUE)==0)) { + + if(def.def_type != ERROR_TYPE) { + + int status=0; + valstruct res; + int i=0; + + memset(&res,0,sizeof(res)); + + for(i=0;i<*ret_size;i++) { + + if(ret[i]) { + + /* return the value in the correct type */ + status = char2val(&def, ret[i], &res); + + if(status==0) { + + if(val_type) *val_type=res.val_type; + + if(res.free_me && res.val) { + free(ret[i]); + ret[i]=res.val; + } + } else { + /* Bad value ? */ + } + } + } + } + + } else { + + while(*ret_size>0) { + if(ret[*ret_size-1]) { + free(ret[*ret_size-1]); + } + *ret_size-=1; + } + free(ret); + ret=NULL; + + } + + free(fn_node_def); + } + } + + clind_path_destruct(&tmpl_path_clone); + } + + return ret; +} + +/** + * Return TRUE if current node is a multi-node value + */ +static int is_multi_node(clind_path_ref tmpl_path) { + + int ret=0; + + if(tmpl_path) { + + const char* t_end = clind_path_last_string(tmpl_path); + + if(t_end && (strcmp(t_end,NODE_TAG)==0)) { + ret=1; + } + } + + return ret; +} + +/** + * Return TRUE if current node is node.def + */ +static int is_node_def(clind_path_ref tmpl_path) { + + int ret=0; + + if(tmpl_path) { + + const char* t_end = clind_path_last_string(tmpl_path); + + if(t_end && (strcmp(t_end,NODE_DEF)==0)) { + ret=1; + } + } + + return ret; +} + +/** + * Apply a single command to the configuration path. + * cfg_path - absolute configuration path, + * tmpl_path - logical template path, + * cmd - single reference command from the variable path. + * The result is the array of "derived" paths. + * result_len output parameter contains the array size. + */ + +static clind_path_ref* clind_config_engine_apply_command(clind_path_ref cfg_path, + clind_path_ref tmpl_path, + clind_cmd *cmd, + int *result_len) { + clind_path_ref* ret=NULL; + + if(cfg_path && tmpl_path && result_len && cmd) { + + /* + printf("%s:111.111:%s:%s:%d\n",__FUNCTION__, + clind_path_get_path_string(cfg_path), + clind_path_get_path_string(tmpl_path), + cmd->type); + */ + + switch (cmd->type) { + + case CLIND_CMD_PARENT: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } + } + + break; + + case CLIND_CMD_SELF_NAME: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + } + + break; + + case CLIND_CMD_CHILD: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + clind_path_push(cfg_path,cmd->value); + clind_path_push(tmpl_path,cmd->value); + } + break; + + case CLIND_CMD_NEIGHBOR: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + + clind_path_push(cfg_path,cmd->value); + clind_path_push(tmpl_path,cmd->value); + } + + break; + + case CLIND_CMD_VALUE: + + { + const char* t_path_string = clind_path_get_path_string(tmpl_path); + const char* t_end = clind_path_last_string(tmpl_path); + const char* c_end = clind_path_last_string(cfg_path); + + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(t_end && (strcmp(t_end,NODE_TAG)==0)) { + /* do nothing, we are there already */ + } else if(t_path_string && clind_file_exists(t_path_string,NODE_TAG)) { + clind_path_push(tmpl_path,NODE_TAG); + } else if(c_end && (strcmp(c_end,VALUE_FILE)==0)) { + /* do nothing, we are there already */ + } else { + clind_path_push(cfg_path,VALUE_FILE); + } + } + + break; + + case CLIND_CMD_PARENT_VALUE: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } + + break; + + case CLIND_CMD_MULTI_VALUE: + + { + const char* cfg_path_string = NULL; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + cfg_path_string = clind_path_get_path_string(cfg_path); + + if(cfg_path_string) { + + const char* c_end = clind_path_last_string(cfg_path); + + if(c_end && (strcmp(c_end,VALUE_FILE)==0)) { + + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + } else if(clind_file_exists(cfg_path_string,VALUE_FILE)) { + + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + clind_path_push(cfg_path,VALUE_FILE); + + } else { + + DIR* dir=NULL; + + dir = opendir(cfg_path_string); + + if(dir) { + + *result_len=0; + + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + + do { + struct dirent *de = readdir(dir); + + if(!de) break; + else if(de->d_name[0] && de->d_name[0]!='.') { + clind_path_ref cfg_path_1 = clind_path_clone(cfg_path); + clind_path_push(cfg_path_1,de->d_name); + (*result_len)++; + ret=(clind_path_ref*)(realloc(ret,*result_len * sizeof(clind_path_ref))); + ret[*result_len-1]=cfg_path_1; + } + } while(1); + + clind_path_push(tmpl_path,NODE_TAG); + + closedir(dir); + } + } + } + } + break; + + default: + ; + } + } + + return ret; +} + +/** + * cfg_path - absolute configuration path, + * tmpl_path - logical template path, + * cmd_path - variable command path. + */ + +int clind_config_engine_apply_command_path(clind_path_ref cfg_path_orig, + clind_path_ref tmpl_path_orig, + clind_path_ref cmd_path, + int check_existence, + clind_val* res, + const char* root_cfg_path, + const char* root_tmpl_path, + int return_value_file_name) { + + int ret=-1; + + /* + printf("%s:111.111:cfg_path=%s,tmpl_path=%s,cmd_path=%s,rtp=%s\n",__FUNCTION__, + clind_path_get_path_string(cfg_path_orig), + clind_path_get_path_string(tmpl_path_orig), + clind_path_get_path_string(cmd_path), + root_tmpl_path); + */ + + if(cfg_path_orig && tmpl_path_orig && cmd_path && res) { + + /* Command to be processed: */ + clind_cmd cmd; + + /* Array of configuration pointers. Initially, contains only one + element - cfg_path. */ + clind_path_ref* config_paths= + (clind_path_ref*)malloc(sizeof(clind_path_ref)*1); + + /* Size of the array (initially - just one): */ + int config_paths_size=1; + + /* Clone the input paths to preserve the input objects intact: */ + clind_path_ref tmpl_path=NULL; + clind_path_ref cfg_path=NULL; + + if(clind_path_is_absolute(cmd_path)) { + tmpl_path=clind_path_construct(root_tmpl_path); + if(!tmpl_path) { + return -1; + } + cfg_path=clind_path_construct(root_cfg_path); + if(!cfg_path) { + return -1; + } + } else { + cfg_path=clind_path_clone(cfg_path_orig); + tmpl_path=clind_path_clone(tmpl_path_orig); + } + + res->value=NULL; + res->val_type=TEXT_TYPE; + + /* Set the initial array content: */ + config_paths[0]=cfg_path; + + /* Apply the commands one-by-one: */ + while(clind_path_get_size(cmd_path)>0 && + (clind_path_shift_cmd(cmd_path,&cmd)==0)) { + + int i=0; + + /* Temporary array to keep the config paths for the next + command application: */ + clind_path_ref* new_config_paths=NULL; + int new_config_paths_size=0; + + /* This path contains the template path at the beginning + of the cycle: */ + clind_path_ref tmpl_path_curr=clind_path_clone(tmpl_path); + + for (i=0;i<config_paths_size;i++) { + + int size=0; + + clind_path_ref* new_config_paths_1=NULL; + + if(i==0) { + new_config_paths_1= + clind_config_engine_apply_command(config_paths[i], + tmpl_path, + &cmd, + &size); + } else { + clind_path_ref tmpl_path_curr_clone=clind_path_clone(tmpl_path_curr); + new_config_paths_1= + clind_config_engine_apply_command(config_paths[i], + tmpl_path_curr_clone, + &cmd, + &size); + clind_path_destruct(&tmpl_path_curr_clone); + } + + if(new_config_paths_1) { + + int j=0; + + for(j=0;j<size;j++) { + if(new_config_paths_1[j]) { + new_config_paths_size++; + new_config_paths= + (clind_path_ref*)realloc(new_config_paths, + sizeof(clind_path_ref)* + new_config_paths_size); + new_config_paths[new_config_paths_size-1]=new_config_paths_1[j]; + } + } + + free(new_config_paths_1); + } + + clind_path_destruct(&(config_paths[i])); + } + + free(config_paths); + config_paths=new_config_paths; + config_paths_size=new_config_paths_size; + clind_path_destruct(&tmpl_path_curr); + } + + if(config_paths) { + + char** sarr=NULL; + int sarrlen=0; + + vtw_type_e val_type=TEXT_TYPE; + + int i=0; + + for(i=0;i<config_paths_size;i++) { + + if(config_paths[i]) { + + int vallen=0; + + char** valarr=clind_get_current_value(config_paths[i], + tmpl_path, + check_existence, + &val_type, + root_tmpl_path, + return_value_file_name, + /*Last command: */ + (cmd.type==CLIND_CMD_MULTI_VALUE), + &vallen); + + clind_path_destruct(&config_paths[i]); + + if(valarr) { + + int k=0; + + for(k=0;k<vallen;k++) { + + char* s=valarr[k]; + + if(s) { + + /* search if we already have it: */ + int j=0; + for(j=0;j<sarrlen;j++) { + if(!strcmp(sarr[j],s)) { + break; + } + } + + if(j<sarrlen) { + free(s); + } else { + sarrlen++; + sarr=(char**)realloc(sarr,sizeof(char*)*sarrlen); + sarr[sarrlen-1]=s; + } + } + } + free(valarr); + } + } + } + + free(config_paths); + + if(sarr) { + + ret=0; + + if(sarrlen==1) { + + res->value=sarr[0]; + + } else { + + for(i=0;i<sarrlen;i++) { + + if(sarr[i]) { + + char* s = clind_quote(sarr[i]); + + free(sarr[i]); + sarr[i]=NULL; + + if(s) { + + if(!res->value) { + + res->value=s; + + } else if(res->value[0]==0) { + + free(res->value); + res->value=s; + + } else { + + res->value=(char*)realloc(res->value, + strlen(res->value)+1+strlen(s)+1); + + strcpy(res->value+strlen(res->value)," "); + strcpy(res->value+strlen(res->value),s); + + free(s); + } + } + } + } + } + free(sarr); + } + } + + clind_path_destruct(&cmd_path); + clind_path_destruct(&cfg_path); + clind_path_destruct(&tmpl_path); + } + + return ret; +} + +/****************************** + * Cmd utils. + * + ******************************/ + +static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd) { + + int ret=-1; + + if(cmd) { + + cmd->type = CLIND_CMD_UNKNOWN; + cmd->value[0]=0; + + if(path && clind_path_get_size(path)>0) { + + int i=0; + int done=0; + + while(cmd_parse_definitions[i].text!=NULL && cmd_parse_definitions[i].text[0]!=NULL) { + + int j=0; + + while(cmd_parse_definitions[i].text[j]) { + const char* str = clind_path_get_string(path,j); + if(str) { + if(!strcmp(cmd_parse_definitions[i].text[j],"*")) { + if(*str!='.' && *str!='@') { + j++; + strncpy(cmd->value,str,sizeof(cmd->value)-1); + continue; + } + } else if(!strcmp(cmd_parse_definitions[i].text[j],str)) { + j++; + strncpy(cmd->value,str,sizeof(cmd->value)-1); + continue; + } + } + j=0; + break; + } + + if(j<1) { + i++; + continue; + } else { + done=1; + } + + cmd->type = cmd_parse_definitions[i].cmd_type; + + while(j) { + clind_path_shift(path); + j--; + } + + break; + } + + if(done) { + ret=0; + } + } + } + + return ret; +} + diff --git a/src/cli_val_engine.h b/src/cli_val_engine.h new file mode 100644 index 0000000..0f32276 --- /dev/null +++ b/src/cli_val_engine.h @@ -0,0 +1,86 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli handler for the reference variables + + **** End License **** + +*************************************************************************/ + +#if !defined(__CLI_VAL_ENGINE__) +#define __CLI_VAL_ENGINE__ + +#include "cli_path_utils.h" +#include "cli_val.h" + +/******************* + * Type definitions + * + *******************/ + +typedef enum { + + CLIND_CMD_UNKNOWN=0, /* ??? */ + CLIND_CMD_PARENT, /* .. */ + CLIND_CMD_SELF_NAME, /* . */ + CLIND_CMD_CHILD, /* <name> */ + CLIND_CMD_NEIGHBOR, /* ../<name> */ + CLIND_CMD_VALUE, /* @ */ + CLIND_CMD_PARENT_VALUE, /* ../@ */ + CLIND_CMD_MULTI_VALUE /* @@ */ + +} clind_cmd_type; + +typedef struct { + + clind_cmd_type type; + char value[1025]; + +} clind_cmd; + +typedef struct { + + vtw_type_e val_type; + char* value; + +} clind_val; + +/******************************** + * Main command-handling method: + * + ********************************/ + +int clind_config_engine_apply_command_path(clind_path_ref cfg_path, + clind_path_ref tmpl_path, + clind_path_ref cmd_path, + int check_existence, + clind_val *res, + const char* root_cfg_path, + const char* root_tmpl_path, + int return_value_file_name); + + + + +#endif /* __CLI_VAL_ENGINE__*/ diff --git a/src/commit.c b/src/commit.c new file mode 100644 index 0000000..a136b58 --- /dev/null +++ b/src/commit.c @@ -0,0 +1,1364 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <limits.h> + +#include "cli_val.h" +#include "cli_objects.h" +#include "cli_parse.h" +#include "cli_path_utils.h" + +static char def_name[] = DEF_NAME; +static char tag_name[] = TAG_NAME; +static char opaque_name[] = OPQ_NAME; + +static int fin_commit(boolean ok); +static boolean commit_value(vtw_def *defp, char *cp, + vtw_cmode mode, boolean in_txn); +static void perform_create_node(); +static void perform_delete_node(); +static void perform_move(); +static boolean commit_delete_child(vtw_def *pdefp, char *child, + boolean deleting, boolean in_txn); +static boolean commit_delete_children(vtw_def *defp, boolean deleting, + boolean in_txn); +static boolean commit_update_children(vtw_def *defp, boolean creating, + boolean in_txn, boolean *parent_update); + +#if BITWISE +static void make_dir() +{ + struct stat statbuf; + if (lstat(m_path.path, &statbuf) < 0) { + char *command; + command = my_malloc(strlen(m_path.path) + 10, "set"); + sprintf(command, "mkdir -p %s", m_path.path); + system(command); + free(command); + return; + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("directory %s expected, found regular file", m_path.path); + } + return; +} +#endif + +/************************************************* + validate_dir_for_commit: + validate value.value if there is one, validate + subdirectries names (for tag directory, and for + regular directory); + validate subdirectories + returns TRUE if OK, FASLE if errors + exits with status != 0 in case of parse error +*/ +static boolean validate_dir_for_commit() +{ + struct stat statbuf; + int status=0; + vtw_def def; + boolean def_present=FALSE; + boolean value_present=FALSE; + int subdirs_number=0; + DIR *dp=NULL; + struct dirent *dirp=NULL; + char *cp=NULL; + boolean ret=TRUE; + char *uename = NULL; + +#ifdef DEBUG + printf("validating directory (node_cnt %d, free_node_cnt %d)\n" + "t_path |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + + /* find definition */ + + push_path(&t_path, def_name); /* PUSH 1 */ + if ((lstat(t_path.path, &statbuf) >= 0) && + ((statbuf.st_mode & S_IFMT) == S_IFREG)) { + /* definition present */ + def_present = TRUE; + memset(&def, 0, sizeof(def)); +#ifdef DEBUG1 + printf("Parsing definition\n"); +#endif + + if ((status = parse_def(&def, t_path.path, + FALSE))) { + exit(status); + } + + } +#ifdef DEBUG1 + else + printf("No definition\n"); +#endif + pop_path(&t_path); /* for PUSH 1 */ + + /* look at modified stuff */ + if ((dp = opendir(m_path.path)) == NULL){ + INTERNAL; + } + if (def_present && def.tag) { + push_path(&t_path, tag_name); /* PUSH 2a */ + } + + while ((dirp = readdir(dp)) != NULL) { + + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0 || + strcmp(dirp->d_name, MOD_NAME) == 0 || + strcmp(dirp->d_name, LOCK_NAME) == 0 || + strcmp(dirp->d_name, opaque_name) == 0 || + strncmp(dirp->d_name, ".wh.", 4) == 0) { + continue; /*ignore dot and dot-dot*/ + } + + subdirs_number++; + + if(uename) + my_free(uename); + + uename = clind_unescape(dirp->d_name); + + if (strcmp(uename, VAL_NAME) == 0) { + + value_present=TRUE; + + /* deal with the value */ + if (!def_present) { + printf("There is no definition specified in template\n" + "\t%s\n\t - therefore no value permitted\n", + t_path.path); + ret = FALSE; + continue; + } + + if (def.tag) { + printf("Tag specified in template\n" + "\t%s\n\t - therefore no value permitted\n", + t_path.path); + ret = FALSE; + continue; + } + + /* value is OK */ + /* read it */ + cp = NULL; + status = get_value(&cp, &m_path); + if (status == VTWERR_OK){ +#ifdef DEBUG1 + printf("Validating value |%s|\n" + "for path %s\n", cp, m_path.path); +#endif + status = validate_value(&def, cp); + ret = ret && status; + } + if (cp) + my_free(cp); + continue; + } + + push_path(&m_path, uename); /* PUSH 3 */ + if (lstat(m_path.path, &statbuf) < 0) { + printf("Can't read directory %s\n", + m_path.path); + ret = FALSE; + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + printf("Non directory file %s\n", m_path.path); + ret = FALSE; + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + + if (def_present && def.tag) { + /* do not push t_path, it is already pushed above */ + /* validate dir name against definition */ + boolean res = validate_value(&def, uename); + value_present=TRUE; + if (!res) { + ret = FALSE; + /* do not go inside bad directory */ + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + } else { + push_path(&t_path, uename); /* PUSH 2b the same as PUSH 2a */ + if (lstat(t_path.path, &statbuf) < 0) { + printf("No such template directory (%s)\n" + "for directory %s", + t_path.path, m_path.path); + ret = FALSE; + pop_path(&t_path); /* for PUSH 2b */ + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + printf("Non directory file %s\n", m_path.path); + ret = FALSE; + pop_path(&t_path); /* for PUSH 2b */ + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + } + + status = validate_dir_for_commit(); + ret = ret && status; + pop_path(&m_path); /* for PUSH 3 */ + if (!def_present || !def.tag) + pop_path(&t_path); /* for PUSH 2b */ + + } + status = closedir(dp); + if (status) + bye("Cannot close dir %s\n", m_path.path); + + if(!value_present && def_present && !def.tag) { + ret = ret && validate_value(&def, ""); + } + + if (def_present && def.tag) + pop_path(&t_path); /* for PUSH 3a */ + if (def_present) + free_def(&def); +#ifdef DEBUG + printf("directory done(node_cnt %d, free_node_cnt %d)\n" + "mpath |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + if (uename) + my_free(uename); + return ret; +} + +/*************************************************** + main: + main function (for now) +***************************************************/ + +int main(int argc, char **argv) +{ + + boolean status; + char *mod; + struct stat statbuf; + int st; + boolean update_pending = FALSE; + + set_in_commit(TRUE); + dump_log( argc, argv); + init_paths(TRUE); + mod = my_malloc(strlen(get_mdirp()) + strlen(MOD_NAME)+2, "COMMIT"); + sprintf(mod, "%s/%s", get_mdirp(), MOD_NAME); + st = lstat(mod, &statbuf); + my_free(mod); + if (st < 0 ) { + bye("No configuration changes to commit\n"); + exit(-1); + } + + if(!get_config_lock(get_adirp(), LOCK_NAME)) { + bye("Configuration is locked\n"); + } + + status = validate_dir_for_commit(); + if (status == TRUE) { + switch_path(CPATH); + status = commit_delete_children(NULL, FALSE, FALSE); + } + if (status == TRUE) + status = commit_update_children(NULL, FALSE, FALSE, &update_pending); + fin_commit(status); + + done(); + + return (status == TRUE) ? 0 : 1; +} + +/************************************************************* + perform_create_node - + remove node and descendent from woring path + and create a new node +*************************************************************/ +static void perform_create_node() +{ +#if BITWISE + static char format[]="rm -f -r %s;mkdir %s"; + char *command; + switch_path(APATH); + command = my_malloc(2 * strlen(m_path.path) + sizeof(format), + "commit_create"); + sprintf(command, format, m_path.path, m_path.path); + system(command); + my_free(command); + switch_path(CPATH); +#endif + return; +} + + +/************************************************************* + perform_delete_node - + delete node in current path +*************************************************************/ +static void perform_delete_node() +{ +#if BITWISE + static char format[]="rm -f -r %s"; + char *command; + command = my_malloc(strlen(m_path.path) + sizeof(format), + "commit_delete"); + sprintf(command, format, m_path.path); + system(command); + my_free(command); +#endif + return; +} +static void perform_move() +{ +#if BITWISE + static char format[] = "rm -r -f %s;mkdir %s;mv %s/" VAL_NAME " %s"; + char *a_path; + char *command; + switch_path(APATH); + a_path = my_strdup(m_path.path, ""); + switch_path(CPATH); + command = my_malloc(sizeof(format)+3*strlen(a_path)+strlen(m_path.path),""); + sprintf(command, format, a_path, a_path, m_path.path, a_path); + system(command); + my_free(command); + my_free(a_path); +#endif + return; +} + +/************************************************* + commit_update_child: + preconditions: t_path and c_path set up for parent + pdefp (IN) - parent definition (may be NULL) + child (IN) - name of the child + creating (IN) - mode of commiting (update or create) + update_parent (OUT) - unfulfilled update request + commit child and its descendants + returns TRUE if no errors + exit if error during execution +*/ +boolean commit_update_child(vtw_def *pdefp, char *child, + boolean creating, boolean in_txn, boolean *update_parent) +{ + boolean update_pending = FALSE; + boolean multi_tag = FALSE; + struct stat statbuf; + int status; + vtw_def def; + vtw_def *my_defp; /*def to be given to my children */ + vtw_def *act_defp;/*def to be used for actions */ + char *cp; + vtw_mark mark; + vtw_act_type act; + boolean do_end, ok, do_begin = FALSE, do_txn = FALSE; + + set_at_string(NULL); + + ok = TRUE; +#ifdef DEBUG + printf("commiting directory (node_cnt %d, free_node_cnt %d)\n" + "mpath |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + if (strcmp(child, ".") == 0 || + strcmp(child, "..") == 0 || + strcmp(child, MOD_NAME) == 0 || + strcmp(child, LOCK_NAME) == 0 || + strcmp(child, opaque_name) == 0) + return TRUE; + + /* deleted subdirectory */ + if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */ + /* ignore */ + return TRUE; + } + mark_paths(&mark); + if (!creating) { + /* are we marked with opaque */ + push_path(&m_path, child); + push_path(&m_path, opaque_name); + if (lstat(m_path.path, &statbuf) >= 0 && !creating) { + creating = TRUE; + } + pop_path(&m_path); + pop_path(&m_path); + } + /* find our definition */ + if (pdefp && pdefp->tag) { + my_defp = NULL; + act_defp = pdefp; + push_path(&t_path,tag_name); + } else { + push_path(&t_path, child); + push_path(&t_path, def_name); + if ((lstat(t_path.path, &statbuf) >= 0) && + ((statbuf.st_mode & S_IFMT) == S_IFREG)) { + /* defniition present */ + act_defp = my_defp = &def; + memset(&def, 0, sizeof(def)); +#ifdef DEBUG1 + printf("Parsing definition\n"); +#endif + if ((status = parse_def(&def, t_path.path, + FALSE))) + exit(status); + my_defp = act_defp = &def; + if (def.tag) + multi_tag = TRUE; + } else { + my_defp = act_defp = NULL; + } + pop_path(&t_path); /*def_name */ + } + do_end = FALSE; + if (!multi_tag && !in_txn && act_defp && + (act_defp->actions[begin_act].vtw_list_head || + act_defp->actions[end_act].vtw_list_head)){ + /* we are traversing change directory + if we are here, there is a change somewhere */ + do_txn = in_txn = TRUE; + if (act_defp->actions[end_act].vtw_list_head) + do_end = TRUE; + /* if creating, delete skipped this directory, + we have to do begin act */ + if (creating && + act_defp->actions[begin_act].vtw_list_head) + do_begin = TRUE; + } + push_path(&m_path, child); + /* are we a "value" parent node */ + if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) { + /* we are value node */ + if (!act_defp->multi) { + /* single node */ + /* create_mode do create, + update_mode do create or update */ + + if (creating) + act = create_act; + else + act = update_act; + + if ((act==update_act) && !act_defp->actions[update_act].vtw_list_head){ + /* updating but no action + ask parent to do - propagate up */ + *update_parent = TRUE; + } + /* look for actions */ + /* if act != create_act => run actions[act] if not empty */ + /* if act == create_act + * if actions[create_act] not empty + * run it + * else + * run actions[update_act] if not empty + * run actions[activate_act] if not empty + */ + if (act_defp->actions[act].vtw_list_head + || (act == create_act + && (act_defp->actions[activate_act].vtw_list_head + || act_defp->actions[update_act].vtw_list_head))) { + status = get_value_to_at_string(&m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + /* remove \n at the line end */ + cp = strchr(get_at_string(), '\n'); + if (cp) + *cp = 0; + if (act_defp->actions[act].vtw_list_head) { + status = execute_list(act_defp->actions[act].vtw_list_head, + act_defp); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } else { + /* creating but no create action */ + /* try update action */ + if ((act == create_act) + && act_defp->actions[update_act].vtw_list_head) { + status + = execute_list(act_defp->actions[update_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } + } + /* try activate action if creating */ + if ((act == create_act) + && act_defp->actions[activate_act].vtw_list_head) { + status + = execute_list(act_defp->actions[activate_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } + free_at_string(); + } + /* now handle commit value */ + if (!in_txn) { /* ELSE WAIT TILL THE END OF TXN */ + perform_move(); + perform_delete_node(); + } + goto restore; + } + /* else multi_node */ + cp = NULL; + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + ok = commit_value(act_defp, cp, creating?create_mode:update_mode, in_txn); + if (cp) + my_free(cp); + goto restore; + } + /* else not a value */ + /* regular */ + /* do not do anything for tag type multinode */ + if (!multi_tag && creating) { + set_at_string(child); /* for expand inside actions */ + if (do_begin) { + status = execute_list(act_defp-> + actions[begin_act]. + vtw_list_head, act_defp); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + + if (act_defp) { + if (act_defp->actions[create_act].vtw_list_head) { + status + = execute_list(act_defp->actions[create_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } else if (act_defp->actions[update_act].vtw_list_head) { + /* no create action => use update action */ + status + = execute_list(act_defp->actions[update_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } + /* not trying activate action here (activate after children are + * configured) + */ + } + } + if (creating && !in_txn) /* ELSE WAIT TILL THE END OF TXN */ + perform_create_node(); + /* children */ + ok = commit_update_children(my_defp, creating, in_txn, &update_pending); + if (!ok) + return(FALSE); + + if (update_pending){ + if (!multi_tag && act_defp && + act_defp->actions[update_act].vtw_list_head){ + set_at_string(child); /* for expand inside actions */ + ok = execute_list(act_defp->actions[update_act]. + vtw_list_head, act_defp); + if (!ok) + return(FALSE); + /* update_pending = FALSE; */ + } else + *update_parent = TRUE; + } + if (creating && !multi_tag && act_defp && + act_defp->actions[activate_act].vtw_list_head){ + set_at_string(child); /* for expand inside actions */ + ok = execute_list(act_defp->actions[activate_act]. + vtw_list_head, act_defp); + /* ignore result */ + } + if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */ + perform_delete_node(); + restore: + if (do_end){ + set_at_string(child); + ok = execute_list(act_defp->actions[end_act]. + vtw_list_head, act_defp); + } +#if BITWISE + if (do_txn && ok) { + int len; + char *command; + char format1[]="rm -r -f %s/*;cp -r -f %s/%s %s"; + char format2[]="rm -r -f %s/%s;mv %s/%s %s"; + char format3[]="rm -r -f %s/%s"; + restore_paths(&mark); + switch_path(MPATH); + len = sizeof(format1) + 2 * strlen(get_tmpp()) + + strlen(m_path.path) + strlen(child); + command = my_malloc(len, ""); + sprintf(command, format1, get_tmpp(), m_path.path, child, + get_tmpp()); + system(command); + my_free(command); + switch_path(APATH); + len = sizeof(format2) + 2 *strlen(m_path.path) + + 2 * strlen( child) + strlen(get_tmpp()); + command = my_malloc(len, ""); + sprintf(command, format2, m_path.path, child, get_tmpp(), + child, m_path.path); + system(command); + my_free(command); + switch_path(CPATH); + len = sizeof(format3) + strlen(m_path.path) + + strlen( child); + command = my_malloc(len, ""); + sprintf(command, format3, m_path.path, child); + system(command); + my_free(command); + } +#endif + restore_paths(&mark); + if (my_defp && my_defp != pdefp) + free_def(my_defp); + return ok; +} + + +/************************************************* + commit_delete_child: + preconditions: t_path and c_path set up for parent + pdefp (IN) - parent definition (may be NULL) + child (IN) - name of the child + do_del (IN) - if FALSE we are looking for delete target + if TRUE, we found and switched to A (working) PATH + commit deleted child and its descendants + returns TRUE if no errors + exit if error during execution +*/ +static boolean commit_delete_child(vtw_def *pdefp, char *child, + boolean deleting, boolean in_txn) +{ + boolean do_children, multi_tag = FALSE; + struct stat statbuf; + int status; + vtw_def def; + vtw_def *my_defp; /*def to be given to my children */ + vtw_def *act_defp;/*def to be used for actions */ + char *cp; + vtw_mark mark; + boolean ok, do_txn = FALSE; + int st; + ok = TRUE; + +#ifdef DEBUG + printf("commiting directory (node_cnt %d, free_node_cnt %d)\n" + "mpath |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + + /* deleted subdirectory */ + if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */ + if (deleting) + /* in do_delete mode we traverse A hierarchy, no white-outs possible */ + INTERNAL; /* it is exit */ + else { + /* deal with counterpart in working */ + switch_path(APATH); + push_path(&m_path, child+4); + st = lstat(m_path.path, &statbuf); + pop_path(&m_path); + if (st >= 0){ + /*get rid of ".wh. part in child name"*/ + /* deleting mode will handle txn, both + begin and end + */ + ok = commit_delete_child(pdefp, child + 4, TRUE, in_txn); + }else { + /* I do not understand how we could be here */ + printf("Mystery #1\n"); + ok = TRUE; + } + switch_path(CPATH); + if (ok) { + /* delete whiteout */ + if (!in_txn){ /*ELSE WAIT TILL THE END OF TXN*/ + push_path(&m_path, child); + perform_delete_node(); + pop_path(&m_path); + } + } + return ok; + } + /* done with whiteouts */ + } + /* not white out */ + mark_paths(&mark); + if (!deleting) { + int status; + /* are we marked with opaque */ + push_path(&m_path, child); + push_path(&m_path, opaque_name); + status = lstat(m_path.path, &statbuf); + pop_path(&m_path); + pop_path(&m_path); + if (status >= 0) { + /* brand new directory, nothing is + deleted there; + update will handle txn (both begin and end) + */ + return TRUE; + } + } + /* find our definition */ + if (pdefp && pdefp->tag) { + /* parent is a tag, node is a tag value node */ + my_defp = NULL; + act_defp = pdefp; + push_path(&t_path,tag_name); + } else { + push_path(&t_path, child); + push_path(&t_path, def_name); + if ((lstat(t_path.path, &statbuf) >= 0) && + ((statbuf.st_mode & S_IFMT) == S_IFREG)) { + /* defniition present */ + act_defp = my_defp = &def; + memset(&def, 0, sizeof(def)); +#ifdef DEBUG1 + printf("Parsing definition\n"); +#endif + if ((status = parse_def(&def, t_path.path, + FALSE))) + exit(status); + my_defp = act_defp = &def; + if (def.tag) + multi_tag = TRUE; /* tag node itself*/ + } else { + my_defp = act_defp = NULL; + } + pop_path(&t_path); /*def_name */ + } + push_path(&m_path, child); + if (!multi_tag) { + set_at_string(child); /* for expand inside actions */ + /* deal with txn */ + if (!in_txn && act_defp && + (act_defp->actions[begin_act].vtw_list_head || + act_defp->actions[end_act].vtw_list_head)) { + /* if we are here we have change, + we either in do_del and our node is change node + or we are in change directory and our node is + change node also */ + if (deleting) + /* if not deleting, update will handle values */ + do_txn = TRUE; + in_txn = TRUE; + if (act_defp->actions[begin_act].vtw_list_head){ + status = execute_list(act_defp->actions[begin_act]. + vtw_list_head, act_defp); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + } + } + /* are we a "value" parent node */ + if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) { + /* we are value node */ + if (!act_defp->multi) { + /* single node */ + if (!deleting) { + /* if it was whiteout, it was converted to do_del_mode */ + restore_paths(&mark); + return ok; + } + + /* do we have actions */ + if (act_defp->actions[delete_act].vtw_list_head){ + status = get_value_to_at_string(&m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + /* remove \n at the line end */ + cp = strchr(get_at_string(), '\n'); + if (cp) + *cp = 0; + if (act_defp->actions[delete_act].vtw_list_head) { + set_in_delete_action(TRUE); + status = execute_list(act_defp->actions[delete_act]. + vtw_list_head, act_defp); + set_in_delete_action(FALSE); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + free_at_string(); + } + /* now handle commit value */ + if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */ + perform_delete_node(); + goto restore; + } + /* else multi_node */ + cp = NULL; + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + ok = commit_value(act_defp, cp, deleting?do_del_mode:del_mode,in_txn); + if (cp) + my_free(cp); + goto restore; + } + /* else not a value */ + /* regular */ + do_children = TRUE; + /* do not do anything for tag itself, all action belong to values */ + if (!multi_tag) { + set_at_string(child); /* for expand inside actions */ + if (deleting) { + if (act_defp && + act_defp->actions[delete_act].vtw_list_head){ + do_children = FALSE; + set_in_delete_action(TRUE); + status = execute_list(act_defp->actions[delete_act]. + vtw_list_head, act_defp); + set_in_delete_action(FALSE); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + } + } + /* children */ + if (do_children){ + ok = commit_delete_children(my_defp, deleting, in_txn); + if (!ok) + goto restore; + } + if (deleting) { + if (do_txn && act_defp && + act_defp->actions[end_act].vtw_list_head) { + set_at_string(child); + ok = execute_list(act_defp->actions[end_act]. + vtw_list_head, act_defp); + if (!ok) + goto restore; + } + /* delete node and all its descendants */ + if (!in_txn || do_txn)/* ELSE WAIT TILL THE END OF TXN */ + perform_delete_node(); + } + restore: + restore_paths(&mark); + if (my_defp && my_defp != pdefp) + free_def(my_defp); + return ok; +} + +static boolean commit_delete_children(vtw_def *defp, boolean deleting, + boolean in_txn) +{ + DIR *dp; + int status; + struct dirent *dirp; + boolean ok = TRUE; + char *child; + vtw_type_e type; + valstruct mvals; + boolean first; + char *cp; + int elem, curi; + vtw_sorted cur_sorted; + char *uename = NULL; + + if ((dp = opendir(m_path.path)) == NULL){ + if (deleting) + return TRUE; + INTERNAL; + } + if (defp) + type = defp->def_type; + else + type = TEXT_TYPE; + if (type == ERROR_TYPE) + type = TEXT_TYPE; + first = TRUE; + memset(&mvals, 0, sizeof (valstruct)); + memset(&cur_sorted, 0, sizeof(vtw_sorted)); + + while ((dirp = readdir(dp)) != NULL) { + child = dirp->d_name; + if (strcmp(child, ".") == 0 || + strcmp(child, "..") == 0 || + strcmp(child, MOD_NAME) == 0 || + strcmp(child, LOCK_NAME) == 0 || + strcmp(child, OPQ_NAME) == 0) + continue; + uename = clind_unescape(child); + cp = uename; + if (first) { + mvals.free_me = TRUE; + mvals.val = cp; + mvals.val_type = type; + first = FALSE; + } else { + if (mvals.cnt%MULTI_ALLOC == 0) { + /* convert into multivalue */ + mvals.vals = my_realloc(mvals.vals, + (mvals.cnt + MULTI_ALLOC) * + sizeof(char *), "add_value"); + if (mvals.cnt == 0) { /* single value - convert */ + mvals.vals[0] = mvals.val; + mvals.cnt= 1; + mvals.val = NULL; + } + } + mvals.vals[mvals.cnt] = cp; + ++mvals.cnt; + } + } + status = closedir(dp); + if (status) + INTERNAL; + if (first) { + return TRUE; + } + vtw_sort(&mvals, &cur_sorted); + for (curi = 0; curi < cur_sorted.num && ok; ++curi){ + if (type == TEXT_TYPE || + type == BOOL_TYPE) + child = (char *)(cur_sorted.ptrs[curi]); + else { + elem = (((unsigned int *)(cur_sorted.ptrs[curi]))- + cur_sorted.parts)/ + cur_sorted.partnum; + child = mvals.cnt?mvals.vals[elem]: + mvals.val; + } + ok = commit_delete_child(defp, child, deleting, in_txn); + } + free_val(&mvals); + free_sorted(&cur_sorted); + return ok; +} + +static boolean commit_update_children(vtw_def *defp, boolean creating, + boolean in_txn, boolean *parent_update) +{ + DIR *dp; + int status; + struct dirent *dirp; + boolean ok = TRUE; + char *child; + vtw_type_e type; + valstruct mvals; + boolean first; + char *cp; + int elem, curi; + vtw_sorted cur_sorted; + char *uename = NULL; + + + if ((dp = opendir(m_path.path)) == NULL){ + printf("%s:%d: opendir error: path=%s\n", + __FUNCTION__,__LINE__,m_path.path); + INTERNAL; + } + + memset(&mvals, 0, sizeof (valstruct)); + memset(&cur_sorted, 0, sizeof(vtw_sorted)); + if (defp) + type = defp->def_type; + else + type = TEXT_TYPE; + if (type == ERROR_TYPE) + type = TEXT_TYPE; + first = TRUE; + + while ((dirp = readdir(dp)) != NULL) { + child = dirp->d_name; + if (strcmp(child, ".") == 0 || + strcmp(child, "..") == 0 || + strcmp(child, MOD_NAME) == 0 || + strcmp(child, LOCK_NAME) == 0 || + strcmp(child, OPQ_NAME) == 0) + continue; + cp = uename = clind_unescape(child); + if (first) { + mvals.free_me = TRUE; + mvals.val = cp; + mvals.val_type = type; + first = FALSE; + } else { + if (mvals.cnt%MULTI_ALLOC == 0) { + /* convert into multivalue */ + mvals.vals = my_realloc(mvals.vals, + (mvals.cnt + MULTI_ALLOC) * + sizeof(char *), "add_value"); + if (mvals.cnt == 0) { /* single value - convert */ + mvals.vals[0] = mvals.val; + mvals.cnt= 1; + mvals.val = NULL; + } + } + mvals.vals[mvals.cnt] = cp; + ++mvals.cnt; + } + } + status = closedir(dp); + if (status) + INTERNAL; + if (first) { + if (uename) + my_free(uename); + return TRUE; + } + vtw_sort(&mvals, &cur_sorted); + for (curi = 0; curi < cur_sorted.num && ok; ++curi){ + if (type == TEXT_TYPE || + type == BOOL_TYPE) + child = (char *)(cur_sorted.ptrs[curi]); + else { + elem = (((unsigned int *)(cur_sorted.ptrs[curi]))- + cur_sorted.parts)/ + cur_sorted.partnum; + child = mvals.cnt?mvals.vals[elem]: + mvals.val; + } + ok = commit_update_child(defp, child, creating, in_txn, parent_update); + } + free_val(&mvals); + free_sorted(&cur_sorted); + return ok; +} + + +/************************************************* + commit_value: + executes commit for the value leave node +**************************************************/ +static boolean commit_value(vtw_def *defp, char *cp, + vtw_cmode mode, boolean in_txn) +{ + + valstruct act_value; + int status; + int curi,acti, partnum, res=0; + void *actp, *curp; + boolean no_shadow; + boolean ok; + int total, a_res, c_res; + char **a_ptr, **c_ptr, *val_string; + boolean cur_pr_val; + int pr_index; + int sign; + boolean creating; + vtw_node *actions; + valstruct cur_value; + vtw_sorted cur_sorted; + vtw_sorted act_sorted; + + ok = TRUE; + actions = NULL; + if(mode == del_mode || mode == do_del_mode) { + creating = FALSE; + if (defp && defp->actions[delete_act].vtw_list_head) { + set_in_delete_action(TRUE); + actions = defp->actions[delete_act].vtw_list_head; + } + } else { + creating = TRUE; + if (defp && defp->actions[create_act].vtw_list_head) + actions = defp->actions[create_act].vtw_list_head; + } + /* prepare cur_value */ + + status = char2val(defp, cp, &cur_value); + if (mode != do_del_mode && mode != create_mode) { + /* get active value */ + switch_path(APATH); /* switch form CCD to ACD */ + status = get_value(&cp, &m_path); + switch_path(CPATH); /* back to CCD */ + if (status != VTWERR_OK) { + no_shadow = TRUE; + }else + no_shadow = FALSE; + } else { + no_shadow = TRUE; + } + vtw_sort(&cur_value, &cur_sorted); + if(no_shadow) { + act_sorted.num = 0; + }else { + status = char2val(defp, cp, &act_value); + if (status != VTWERR_OK) { + INTERNAL; + } + /* sort them */ + vtw_sort(&act_value, &act_sorted); + } + if (mode == do_del_mode) { + /* it was actually act_sorted, not cur_sorted */ + act_sorted = cur_sorted; + cur_sorted.num = 0; + act_value = cur_value; + /* act_value will be freed by freeing cur_value + do not zero out it here */ + } + + acti = 0; + curi = 0; + total = act_sorted.num + cur_sorted.num; + a_res=0; + c_res=0; + a_ptr = my_malloc(total*sizeof(char *), ""); + c_ptr = my_malloc(total*sizeof(char *), ""); + while (acti < act_sorted.num || curi < cur_sorted.num) { + if (acti == act_sorted.num) { + cur_pr_val = TRUE; + pr_index = curi; + sign = +1; + ++curi; + } else if (curi == cur_sorted.num) { + cur_pr_val = FALSE; + pr_index = acti; + sign = -1; + ++acti; + } else { + /* compare */ + actp = act_sorted.ptrs[acti]; + curp = cur_sorted.ptrs[curi]; + /* compare */ + if (act_sorted.partnum){ + for(partnum = 0; partnum < act_sorted.partnum; + ++partnum) { + res = *((int *)actp + partnum) - + *((int *)curp + partnum); + if (res) + break; + } + } else{ + res = strcmp((char *)actp, (char *) curp); + } + if (res == 0) { + /* the same */ + cur_pr_val = TRUE; + pr_index = curi; + sign = 0; + ++acti; + ++curi; + } else if (res < 0) { + /* act < cur, act is unmatched */ + cur_pr_val = FALSE; + pr_index = acti; + sign = -1; + ++acti; + }else { + /* cur < act, cur is unmatched */ + cur_pr_val = TRUE; + pr_index = curi; + sign = 1; + ++curi; + } + } + if (defp->def_type == TEXT_TYPE || + defp->def_type == BOOL_TYPE) { + val_string = cur_pr_val? + ((char *)(cur_sorted.ptrs[pr_index])): + ((char *)(act_sorted.ptrs[pr_index])); + } else { + if (cur_pr_val) { + int elem = (((unsigned int *)(cur_sorted.ptrs[pr_index]))- + cur_sorted.parts)/ + cur_sorted.partnum; + val_string = cur_value.cnt?cur_value.vals[elem]: + cur_value.val; + } else { + int elem = (((unsigned int *)(act_sorted.ptrs[pr_index]))- + act_sorted.parts)/ + act_sorted.partnum; + val_string = act_value.cnt?act_value.vals[elem]: + act_value.val; + } + } + set_at_string(val_string); + switch (sign) { + case 0: /* found in both, no actions, include in both */ + a_ptr[a_res++]=val_string; + c_ptr[c_res++]=val_string; + break; + case 1: /* found only in change */ + if (ok && creating) { + if (actions) { + /* do create action */ + ok = execute_list(actions, defp); + } else if (defp && defp->actions[update_act].vtw_list_head) { + /* no create action => use update action */ + ok = execute_list(defp->actions[update_act].vtw_list_head, defp); + } + if (ok && defp && defp->actions[activate_act].vtw_list_head) { + /* try activate action */ + ok = execute_list(defp->actions[activate_act].vtw_list_head, defp); + } + /* if succ, make it look old */ + if(ok) + a_ptr[a_res++]=val_string; + } + c_ptr[c_res++]=val_string; /* in all cases */ + break; + case -1: /* found only in working */ + if (ok && !creating && actions) {/* ok and deleting */ + ok = execute_list(actions, defp); + } + /* if succ and deleting - do nothing, else */ + if (!ok || creating) + a_ptr[a_res++]=val_string; + } + } + if (creating && ok) + c_res = 0; +#if BITWISE + if (!in_txn) {/* ELSE WAIT TILL THE END OF TXN */ + switch_path(APATH); + if (a_res) { + make_dir(); + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + for(i=0;i<a_res;++i) + if (fputs(a_ptr[i], fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + fclose(fp); + pop_path(&m_path); + }else{ + perform_delete_node(); + } + switch_path(CPATH); + if (c_res) { + make_dir(); + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + for(i=0;i<c_res;++i) + if (fputs(c_ptr[i], fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + fclose(fp); + pop_path(&m_path); + }else{ + perform_delete_node(); + } + } +#endif /*BITWISE*/ + if (mode == do_del_mode) + switch_path(APATH); + else + switch_path(CPATH); + if(act_sorted.num) + free_sorted(&act_sorted); + if(cur_sorted.num) + free_sorted(&cur_sorted); + my_free(a_ptr); + my_free(c_ptr); + free_val(&cur_value); + if (!no_shadow) + free_val(&act_value); + set_in_delete_action(FALSE); + return ok; +} + +static int fin_commit(boolean ok) +{ + char *command; + static char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/ + static char format2[]="sudo umount %s"; /*mdirp*/ + static char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true"; + /*tmpp*/ + static char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/ + static char format5[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*adirp*/ + static char format6[]="mv -f %s/* -t %s";/*tmpp, adirp*/ + static char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro" + " unionfs %s"; /*cdirp, adirp, mdirp*/ + int m_len = strlen(get_mdirp()); + int t_len = strlen(get_tmpp()); + int c_len = strlen(get_cdirp()); + int a_len = strlen(get_adirp()); + set_echo(TRUE); + if (!ok){ + printf("Commit FAILED!\n"); + return -1; + } + command = my_malloc(strlen(format1) + m_len + t_len, ""); + sprintf(command, format1, get_mdirp(), get_tmpp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format2) + m_len, ""); + sprintf(command, format2, get_mdirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format3) + c_len, ""); + sprintf(command, format3, get_tmpp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format4) + c_len, ""); + sprintf(command, format4, get_cdirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format5) + a_len, ""); + sprintf(command, format5, get_adirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format6) + t_len + a_len, ""); + sprintf(command, format6, get_tmpp(), get_adirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format7) + c_len + a_len + m_len, ""); + sprintf(command, format7, get_cdirp(), get_adirp(), get_mdirp()); + system(command); + my_free(command); + + return 0; +} + diff --git a/src/delete.c b/src/delete.c new file mode 100644 index 0000000..a825a22 --- /dev/null +++ b/src/delete.c @@ -0,0 +1,258 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <dirent.h> + +#include "cli_val.h" +#include "cli_objects.h" + +static void remove_rf(boolean do_umount) +{ + char *command; + touch(); + if (do_umount) { + command = my_malloc(strlen(get_mdirp()) + 20, "delete"); + sprintf(command, "sudo umount %s", get_mdirp()); + system(command); + free(command); + } + command = my_malloc(strlen(m_path.path) + 10, "delete"); + sprintf(command, "rm -rf %s", m_path.path); + system(command); + free(command); + if (do_umount) { + command = my_malloc(strlen(get_mdirp()) + strlen(get_cdirp()) + + strlen(get_mdirp()) + 100, + "delete"); + sprintf(command, "sudo mount -t unionfs -o dirs=%s=rw:%s=ro:" + " unionfs %s", get_cdirp(), get_adirp(), get_mdirp()); + system(command); + free(command); + } +} +/*************************************************** + set_validate: + validate value against definition + return TRUE if OK, FALSE otherwise +****************************************************/ +boolean set_validate(vtw_def *defp, char *valp) +{ + boolean res; + int status; + struct stat statbuf; + + pop_path(&t_path); /* it was tag or real value */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0 || + (statbuf.st_mode & S_IFMT) != S_IFREG) { + bye("Can not set value (2), no definition for %s", m_path.path); + } + /* defniition present */ + memset(defp, 0, sizeof(vtw_def)); + if ((status = parse_def(defp, t_path.path, FALSE))) + exit(status); + res = validate_value(defp, valp); + pop_path(&t_path); + return res; +} + +int main(int argc, char **argv) +{ + int ai; + struct stat statbuf; + vtw_def def; + boolean last_tag=0; + int status; + FILE *fp; + boolean res; + char *cp, *delp, *endp; + boolean do_umount; + + if (argc < 2) { + fprintf(stderr, "Need to specify the config node to delete\n"); + exit(1); + } + + dump_log( argc, argv); + do_umount = FALSE; + init_edit(); + /* extend both paths per arguments given */ + /* last argument is new value */ + for (ai = 1; ai < argc; ++ai) { + push_path(&t_path, argv[ai]); + push_path(&m_path, argv[ai]); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = FALSE; + continue; + } /*else */ + pop_path(&t_path); + push_path(&t_path, TAG_NAME); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = TRUE; + continue; + } + /* no match */ + break; + } + /* + cases: + multiple tag-value - not achild + mutilple tag-value - not the last child + multiple tag-value - last child + single value modified + signle value unmodified + multiple non-tag value - the last value + multiple non-tag value - not the last value + regular child + */ + if (ai == argc) { + /* full path found */ + /* all cases except multiple non-tag value */ + /* check for single value */ + if (last_tag) { + /* case of multiple tag-value + was this a real child? + was it the last child? + */ + struct dirent *dirp; + DIR *dp; + + if (lstat(m_path.path, &statbuf) < 0) + bye("Nothing to delete at %s", m_path.path); + remove_rf(FALSE); + pop_path(&m_path); + if ((dp = opendir(m_path.path)) == NULL){ + INTERNAL; + } + while ((dirp = readdir(dp)) != NULL) { + /*do we have real child */ + if (strcmp(dirp->d_name, ".") && + strcmp(dirp->d_name, "..") && + strcmp(dirp->d_name, MOD_NAME) && /* XXX */ + strcmp(dirp->d_name, LOCK_NAME) && /* XXX */ + strncmp(dirp->d_name, ".wh.", 4) ) + break; + } + if (dirp == NULL) { + /* no real children left */ + /* kill parent also */ + remove_rf(FALSE); + } + exit(0); + } + /*not tag */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) >= 0 && + (statbuf.st_mode & S_IFMT) == S_IFREG) { + /* defniition present */ + memset(&def, 0, sizeof(vtw_def)); + if ((status = parse_def(&def, t_path.path, FALSE))) + exit(status); + if (!def.tag && !def.multi && def.def_type!= ERROR_TYPE) { + /* signgle value */ + /* is it modified == + it is in C, but not OPAQUE */ + switch_path(CPATH); + if(lstat(m_path.path, &statbuf) >= 0) { + push_path(&m_path, OPQ_NAME); + if(lstat(m_path.path, &statbuf) < 0) { + /* yes remove from C only */ + pop_path(&m_path); + remove_rf(TRUE); + exit(0); + } + pop_path(&m_path); /*OPQ_NAME */ + } + switch_path(MPATH); + } + } + /* else no defnition, remove it also */ + remove_rf(FALSE); + exit(0); + } + if(ai < argc -1 || last_tag) { + bye("There is no appropriate template for %s", + m_path.path + strlen(get_mdirp())); + } + /*ai == argc -1, must be actual value */ + pop_path(&m_path); /*it was value, not path segment */ + push_path(&m_path, VAL_NAME); + /* set value */ + if (lstat(m_path.path, &statbuf) < 0) + bye("Nothing to delete at %s", m_path.path); + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + bye("Not a regular file %s", m_path.path); + /* get definition to deal with potential multi */ + pop_path(&t_path); /* it was tag or real value */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0 || + (statbuf.st_mode & S_IFMT) != S_IFREG) { + bye("Can not delete value, no definition for %s", m_path.path); + } + /* defniition present */ + memset(&def, 0, sizeof(vtw_def)); + if ((status = parse_def(&def, t_path.path, FALSE))) + exit(status); + if (def.multi) { + /* delete from multivalue */ + valstruct new_value, old_value; + status = char2val(&def, argv[argc - 1], &new_value); + if (status) + exit(0); + cp = NULL; + pop_path(&m_path); /* get_value will push VAL_NAME */ + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Cannot read old value %s\n", m_path.path); + status = char2val(&def, cp, &old_value); + if (status != VTWERR_OK) + bye("Corrupted old value ---- \n%s\n-----\n", cp); + res = val_cmp(&new_value, &old_value, IN_COND); + if (!res) + bye("Not in multivalue"); + touch(); + if (old_value.cnt) { + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + if (is_in_cond_tik()) { + for(delp=cp;delp && is_in_cond_tik(); dec_in_cond_tik()) { + delp = strchr(delp, '\n'); + if (!delp) + INTERNAL; + ++delp; /* over \n */ + } + /* write "left" of deleted */ + fwrite(cp, 1, delp-cp, fp); + }else + delp = cp; + /* find end of value */ + endp = strchr(delp, '\n'); + if (endp && *++endp) { + /* write "right" of deleted */ + fwrite(endp, 1, strlen(endp), fp); + /* need the final '\n' */ + fwrite("\n", 1, 1, fp); + } + fclose(fp); + return 0; + } + /* it multi with only 1 value, remove */ + remove_rf(FALSE); + return 0; + } + bye("There is no appropriate template for %s", + m_path.path + strlen(get_mdirp())); + + return 0; +} + diff --git a/src/set.c b/src/set.c new file mode 100644 index 0000000..566dfe0 --- /dev/null +++ b/src/set.c @@ -0,0 +1,310 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#include "cli_val.h" +#include "cli_objects.h" +#include "cli_path_utils.h" + +static void make_dir(void); +static void handle_defaults(void); + +static void make_dir() +{ + touch_dir(m_path.path); +} +/*************************************************** + set_validate: + validate value against definition + return TRUE if OK, FALSE otherwise +****************************************************/ +boolean set_validate(vtw_def *defp, char *valp, boolean empty_val) +{ + boolean res; + int status; + struct stat statbuf; + char* path_end=NULL; + + if (!empty_val) { + int i = 0; + int val_len = strlen(valp); + + for (i = 0; i < val_len; i++) { + if (valp[i] == '\'') { + fprintf(stderr, "Cannot use the \"'\" (single quote) character " + "in a value string\n"); + exit(1); + } + } + + { + clind_path_ref tp = clind_path_construct(t_path.path); + if(tp) { + path_end=clind_path_pop_string(tp); + } + clind_path_destruct(&tp); + } + + pop_path(&t_path); /* it was tag or real value */ + + } + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0 || + (statbuf.st_mode & S_IFMT) != S_IFREG) { + bye("Can not set value (4), no definition for %s, template %s", + m_path.path,t_path.path); + } + /* defniition present */ + memset(defp, 0, sizeof(vtw_def)); + if ((status = parse_def(defp, t_path.path, FALSE))) + exit(status); + pop_path(&t_path); + if(path_end) { + push_path(&t_path,path_end); + free(path_end); + path_end=NULL; + } + if (empty_val) { + if (defp->def_type != TEXT_TYPE || defp->tag || defp->multi){ + printf("Empty string may be assigned only to TEXT type leaf node\n"); + return FALSE; + } + return TRUE; + } + res = validate_value(defp, valp); + return res; +} + +int main(int argc, char **argv) +{ + + int ai; + struct stat statbuf; + vtw_def def; + boolean last_tag; + int status; + FILE *fp; + boolean res; + char *cp; + char *command; + boolean need_mod = FALSE, not_new = FALSE; + boolean empty_val = FALSE; + + dump_log( argc, argv); + init_edit(); + last_tag = FALSE; + + /* extend both paths per arguments given */ + /* last argument is new value */ + for (ai = 1; ai < argc; ++ai) { + if (!*argv[ai]) { /* empty string */ + if (ai < argc -1) { + bye("empty string in argument list \n"); + } + empty_val = TRUE; + last_tag = FALSE; + break; + } + push_path(&t_path, argv[ai]); + push_path(&m_path, argv[ai]); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = FALSE; + continue; + } /*else */ + pop_path(&t_path); + push_path(&t_path, TAG_NAME); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = TRUE; + /* every time tag match, need to verify*/ + if(!set_validate(&def, argv[ai], FALSE)) { + exit(1); + } + continue; + } + /* no match */ + break; + } + + if (ai == argc) { + /* full path found */ + /* every tag match validated already */ + /* non tag matches are OK by definition */ + /* do we already have it? */ + if (lstat(m_path.path, &statbuf) >= 0) + bye("Already exists %s", m_path.path + strlen(get_mdirp())); + /* else */ + /* prevent value node without actual value */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) >= 0) { + memset(&def, 0, sizeof(vtw_def)); + if ((status = parse_def(&def, t_path.path, FALSE))) + exit(status); + if (def.def_type != ERROR_TYPE && !def.tag) + bye("Must provide actual value\n"); + if (def.def_type == ERROR_TYPE && !def.tag) { + pop_path(&t_path); + if(!validate_value(&def, "")) { + exit(1); + } + push_path(&t_path, DEF_NAME); + } + } + touch(); + pop_path(&t_path); + make_dir(); + handle_defaults(); + exit(0); + } + if(ai < argc -1 || last_tag) { + fprintf(stderr, "There is no appropriate template for %s", + m_path.path + strlen(get_mdirp())); + exit(1); + } + /*ai == argc -1, must be actual value */ + if (!empty_val) + pop_path(&m_path); /*it was value, not path segment */ + + if(!set_validate(&def, argv[argc-1], empty_val)) { + exit(1); + } + push_path(&m_path, VAL_NAME); + /* set value */ + if (lstat(m_path.path, &statbuf) >= 0) { + valstruct new_value, old_value; + not_new = TRUE; + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + bye("Not a regular file at path \"%s\"", m_path.path); + /* check if this new value */ + status = char2val(&def, argv[argc - 1], &new_value); + if (status) + exit(0); + cp = NULL; + pop_path(&m_path); /* get_value will push VAL_NAME */ + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Cannot read old value %s\n", m_path.path); + status = char2val(&def, cp, &old_value); + if (status != VTWERR_OK) + bye("Corrupted old value ---- \n%s\n-----\n", cp); + res = val_cmp(&new_value, &old_value, IN_COND); + if (res) { + if (def.multi) { + bye("Already in multivalue"); + } else { + bye("The same value \"%s\" for path \"%s\"\n", cp, m_path.path); + } + } + } else { + pop_path(&m_path); + } + make_dir(); + push_path(&m_path, VAL_NAME); + if(not_new && !def.multi) { + /* it is not multi and seen from M */ + /* is it in C */ + switch_path(CPATH); + if (lstat(m_path.path, &statbuf) < 0) + /* yes, we are modifying original value */ + need_mod = TRUE; + switch_path(MPATH); + } + touch(); + /* in case of multi we always append, never overwrite */ + /* in case of single we always overwrite */ + /* append and overwrite work the same for new file */ + fp = fopen(m_path.path, def.multi?"a":"w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + if (fputs(argv[argc-1], fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + if (need_mod) { + pop_path(&m_path); /* get rid of "value" */ + command = my_malloc(strlen(m_path.path) + 30, "set"); + sprintf(command, "touch %s/" MOD_NAME, m_path.path); + system(command); + } + return 0; +} +/********************************************** + handle_defaults: + now deal with defaults for children + if child has definition and not tag, nor multi, and + has type, and has default, and not have value + already, make a default value +*/ + + +static void handle_defaults() +{ + DIR *dp; + int status; + struct dirent *dirp; + struct stat statbuf; + FILE *fp; + vtw_def def; + char *uename; + + if ((dp = opendir(t_path.path)) == NULL){ + INTERNAL; + } + while ((dirp = readdir(dp)) != NULL) { + if (strcmp(dirp->d_name, ".")==0 || + strcmp(dirp->d_name, "..")==0 || + strcmp(dirp->d_name, MOD_NAME) == 0 || + strcmp(dirp->d_name, LOCK_NAME) == 0 || + strcmp(dirp->d_name, DEF_NAME)==0) + continue; + uename = clind_unescape(dirp->d_name); + push_path(&t_path, uename); + if (lstat(t_path.path, &statbuf) < 0) { + bye("Cannot stat template directory %s\n", + t_path.path); + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("Non directory file %s\n", t_path.path); + } + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0) { + /* no definition */ + pop_path(&t_path); /* definition */ + pop_path(&t_path); /* child */ + continue; + } + memset(&def, 0, sizeof(def)); + if ((status = parse_def(&def, t_path.path, + FALSE))) + exit(status); + if (def.def_default) { + push_path(&m_path, uename); + push_path(&m_path, VAL_NAME); + if (lstat(m_path.path, &statbuf) < 0) { + /* no value, write one */ + pop_path(&m_path); + make_dir();/* make sure directory exist */ + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + if (fputs(def.def_default, fp) < 0 || + fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + fclose(fp); + } + pop_path(&m_path); /* value */ + pop_path(&m_path); /* child */ + } + free_def(&def); + pop_path(&t_path); /* definition */ + pop_path(&t_path); /* child */ + } +} |