diff options
-rw-r--r-- | Makefile.am | 16 | ||||
-rw-r--r-- | debian/vyatta-cfg.postinst.in | 1 | ||||
-rw-r--r-- | src/commit2.c | 755 | ||||
-rw-r--r-- | src/common/common.c | 11 | ||||
-rw-r--r-- | src/common/common.h | 53 | ||||
-rw-r--r-- | src/common/defs.h | 72 | ||||
-rw-r--r-- | src/common/priority | 3 | ||||
-rw-r--r-- | src/common/unionfs.c | 1086 | ||||
-rw-r--r-- | src/common/unionfs.h | 49 | ||||
-rw-r--r-- | src/dump_session.c | 166 | ||||
-rw-r--r-- | src/exe_action.c | 122 | ||||
-rw-r--r-- | templates/priority | 26 |
12 files changed, 2359 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 9e60f85..bd811aa 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,7 @@ defaultdir = /etc/default etc_shell_leveldir = $(sysconfdir)/shell/level dhcphookdir = /etc/dhcp3/dhclient-exit-hooks.d -AM_CFLAGS = -I src -Wall +AM_CFLAGS = -I src -Wall -I /usr/include/glib-2.0 -I /usr/lib/glib-2.0/include AM_YFLAGS = -d --name-prefix=yy_`basename $* .y`_ AM_LFLAGS = --prefix=yy_`basename $* .l`_ -olex.yy.c @@ -21,12 +21,26 @@ src_libvyatta_cfg_la_SOURCES = src/cli_parse.y src/cli_def.l src/cli_val.l \ src/cli_val_engine.c src/cli_objects.c CLEANFILES = src/cli_parse.c src/cli_parse.h src/cli_def.c src/cli_val.c LDADD = src/libvyatta-cfg.la +LDADD += /usr/lib/libglib-2.0.la + sbin_PROGRAMS = src/my_commit +sbin_PROGRAMS += src/my_commit2 +sbin_PROGRAMS += src/exe_action +sbin_PROGRAMS += src/dump sbin_PROGRAMS += src/my_delete sbin_PROGRAMS += src/my_set sbin_PROGRAMS += src/check_tmpl src_my_commit_SOURCES = src/commit.c +src_my_commit2_SOURCES = src/commit2.c +src_my_commit2_SOURCES += src/common/common.c +src_my_commit2_SOURCES += src/common/unionfs.c +src_exe_action_SOURCES = src/exe_action.c +src_exe_action_SOURCES += src/common/common.c +src_exe_action_SOURCES += src/common/unionfs.c +src_dump_SOURCES = src/dump_session.c +src_dump_SOURCES += src/common/common.c +src_dump_SOURCES += src/common/unionfs.c src_my_delete_SOURCES = src/delete.c src_my_set_SOURCES = src/set.c src_check_tmpl_SOURCES = src/check_tmpl.c diff --git a/debian/vyatta-cfg.postinst.in b/debian/vyatta-cfg.postinst.in index d934b84..0d2d3a3 100644 --- a/debian/vyatta-cfg.postinst.in +++ b/debian/vyatta-cfg.postinst.in @@ -18,6 +18,7 @@ if [ "$sysconfdir" != "/etc" ]; then done fi +mv /opt/vyatta/share/vyatta-cfg/templates/priority /opt/vyatta/share/vyatta-cfg/. # add group for configuration, if not already present: grep '^vyattacfg:' /etc/group >&/dev/null || addgroup --system vyattacfg diff --git a/src/commit2.c b/src/commit2.c new file mode 100644 index 0000000..dffdb02 --- /dev/null +++ b/src/commit2.c @@ -0,0 +1,755 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <glib-2.0/glib.h> +#include "common/common.h" + +boolean g_debug = FALSE; +boolean g_display_error_node = FALSE; +boolean g_coverage = FALSE; + +char* ActionNames[top_act] = { + "delete", + "create", + "activate", + "update", + "syntax", + "commit", + "begin", + "end" +}; + +extern boolean +check_syn(vtw_node *cur); + +GNode* +get_transactions(GNode*, boolean priority); + +boolean +complete(GNode *node, boolean test_mode); + +gboolean +sort_func(GNode *node, gpointer data, boolean priority_mode); + +gboolean +sort_func_priority(GNode *node, gpointer data); + +gboolean +sort_func_simple(GNode *node, gpointer data); + +void +cleanup(GNode *root_node); + +gboolean +dump_func(GNode *node, gpointer data); + +static gboolean +enclosing_process_func(GNode *node, gpointer data); + +static gboolean +process_func(GNode *node, gpointer data); + +boolean +process_priority_node(GNode *priority_node); + +static gboolean +enclosing_process_func(GNode *node, gpointer data); +/* +NOTES: reverse: use the n-nary tree in commit2.c and only encapuslate data store. pass in func pointer for processing of commands below. + +also, the algorithm for collapsing the tree into a transaction list is: +1) iterate through tree and mark all explicit transactions +2) when done, prune the tree of all root explicit transactions +3) Now iterate through remaining tree and remove each node and append to transaction list. + + +TODO: +> Implement transactional sorting functions (test) +> possibly add back validation sequence (difference in committing failed user w/o pw) +> memory handling code (i.e. all the frees I left out) +> test on boot by having boot call load rather than running through boot (and adding priority file) +> + + */ + +/** + * + * + **/ +void +usage() +{ + printf("commit2\n"); + printf("-d\t\tdebug mode\n"); + printf("-s\t\tdump sorted transactions and exit\n"); + printf("-p\t\tdisable priority mode\n"); + printf("-t\t\ttest mode (don't apply directory modifications)\n"); + printf("-e\t\tprint node where error occurred\n"); + printf("-c\t\tdump node coverage and execution times\n"); + printf("-h\t\thelp\n"); +} + +/** + * + * + **/ +int +main(int argc, char** argv) +{ + int ch; + boolean dump_trans = TRUE; + boolean priority_mode = TRUE; + boolean test_mode = FALSE; + + //grab inputs + while ((ch = getopt(argc, argv, "dpthsec")) != -1) { + switch (ch) { + case 'd': + g_debug = TRUE; + break; + case 'h': + usage(); + exit(0); + break; + case 'p': + priority_mode = FALSE; + break; + case 't': + test_mode = TRUE; + break; + case 's': + dump_trans = TRUE; + break; + case 'e': + g_display_error_node = TRUE; + break; + case 'c': + g_coverage = TRUE; + break; + default: + usage(); + exit(0); + } + } + + initialize_output(); + init_paths(TRUE); + if (g_debug) { + printf("commit2: starting up\n"); + } + + //get local session data plus configuration data + GNode *config_data = common_get_local_session_data(); + if (g_node_n_children(config_data) == 0) { + common_commit_clean_temp_config(test_mode); + fprintf(out_stream, "No configuration changes to commit\n"); + return 0; + } + + // Get collection of transactions, i.e. trans nodes that have been activated. + GNode *trans_coll = get_transactions(config_data, priority_mode); + if (trans_coll == NULL) { + printf("commit2: transactions collection is empty, exiting\n"); + exit(0); + } + + if (dump_trans == TRUE) { + printf("Dumping transactions\n"); + //iterate over config_data and dump... + g_node_traverse(trans_coll, + G_PRE_ORDER, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)dump_func, + (gpointer)NULL); + // exit(0); + } + + GNode *trans_child_node = (GNode*)g_node_first_child(trans_coll); + if (trans_child_node == NULL) { + printf("commit2: No child nodes to process, exiting\n"); + exit(0); + } + + boolean no_errors = TRUE; + set_in_commit(TRUE); + int i = 0; + do { + boolean success = TRUE; + if (g_debug == TRUE) { + printf("commit2: Starting new transaction processing pass on root\n"); + } + + //on each priority node now execute actions + if ((success = process_priority_node(trans_child_node)) == TRUE) { + //this below copies the node directory from the local to active location + success = complete(trans_child_node, test_mode); + } + + if (success == FALSE) { + no_errors = FALSE; + if (g_debug == TRUE) { + printf("commit2: Failed in processing node\n"); + } + } + ++i; + } while ((trans_child_node = (GNode*)g_node_nth_child((GNode*)trans_coll,(guint)i)) != NULL); + + if (no_errors == TRUE) { + common_commit_clean_temp_config(test_mode); + if (g_debug == TRUE) { + printf("commit2: successful commit, now cleaning up temp directories\n"); + } + } + set_in_commit(FALSE); + + cleanup(config_data); + cleanup(trans_child_node); + + if (g_debug) { + printf("DONE\n"); + } + exit(0); +} + +/** + * + * + **/ +static gboolean +process_func(GNode *node, gpointer data) +{ + if (node == NULL) { + return TRUE; + } + + struct Result *result = (struct Result*)data; + gpointer gp = ((GNode*)node)->data; + struct Config *c = &((struct VyattaNode*)gp)->_config; + struct Data *d = &((struct VyattaNode*)gp)->_data; + + int status = 0; + if (c->_def.actions && + c->_def.actions[result->_action].vtw_list_head){ + + if (g_debug) { + if (d->_name != NULL) { + printf("commit2::process_func(), calling process on : %s for action %d, type: %d, operation: %d, path: %s\n",d->_name,result->_action,c->_def.def_type, d->_operation, d->_path); + } + else { + printf("commit2::process_func(), calling process on : [n/a] for action %d, operation: %d, path: %s\n",result->_action, d->_operation, d->_path); + } + } + + /* + Needs to be cleaned up a bit such that this convoluted if clause is easier to read. Basically + is says: + if a node is SET, is not ACTIVE and is not a DELETE ACTION + or + if a node is SET, is ACTIVE, is not a DELETE ACTION or a CREATE ACTION + or + if a node is DELETE, is a DELETE ACTION or a END ACTION, or a BEGIN ACTION + */ + if ((IS_SET(d->_operation) && !IS_ACTIVE(d->_operation) && (result->_action != delete_act && result->_action != create_act)) || + (IS_CREATE(d->_operation) && !IS_ACTIVE(d->_operation) && (result->_action == syntax_act || result->_action == begin_act || result->_action == end_act || result->_action == create_act || (result->_action == update_act && !c->_def.actions[create_act].vtw_list_head))) || + (IS_SET_OR_CREATE(d->_operation) && IS_ACTIVE(d->_operation) && ((result->_action != delete_act) && (result->_action != create_act))) || + // (IS_DELETE(d->_operation) && ((result->_action == delete_act) || (result->_action == syntax_act) || (result->_action == begin_act) || (result->_action == end_act)) )) { + (IS_DELETE(d->_operation) && ((result->_action == delete_act) || (result->_action == begin_act) || (result->_action == end_act)) )) { + //NEED TO ADD IF CREATE, THEN CREATE OR UPDATE + //IF SET THEN UPDATE + + //let's skip the case where this is active and it's a delete--shouldn't be done, but needs to be include in the rule set above + if (IS_DELETE(d->_operation) && IS_ACTIVE(d->_operation) && result->_action == delete_act) { + return FALSE; + } + + //look at parent for multi tag + if (d->_value && d->_name) { + if (g_debug) { + printf("commit2::process_func(): @ value: %s\n",(char*)clind_unescape(d->_name)); + } + set_at_string((char*)clind_unescape(d->_name)); //embedded multinode value + } + else { + if (g_debug) { + printf("commit2::process_func(): boolean value is: %d\n",d->_value); + if (node->parent != NULL && ((struct VyattaNode*)(node->parent->data))->_data._name != NULL) { + printf("commit2::process_func(): parent has a name and it is: %s\n",((struct VyattaNode*)(node->parent->data))->_data._name); + } + printf("commit2::process_func(): @ value: [NULL]\n"); + } + } + + common_set_context(c->_path,d->_path); + if (g_debug) { + printf("Executing %s on this node\n", ActionNames[result->_action]); + } + + if (g_coverage) { + struct timeval t; + gettimeofday(&t,NULL); + printf("[START] %lu, %s@%s",(unsigned long)t.tv_sec,ActionNames[result->_action],d->_path); + } + + if (result->_action == delete_act) { + set_in_delete_action(TRUE); + } + status = execute_list(c->_def.actions[result->_action].vtw_list_head,&c->_def); + if (result->_action == delete_act) { + set_in_delete_action(FALSE); + } + + if (g_coverage) { + struct timeval t; + gettimeofday(&t,NULL); + printf("[END] %lu\n",t.tv_sec); + } + + if (!status) { //EXECUTE_LIST RETURNS FALSE ON FAILURE.... + if (g_display_error_node) { + fprintf(out_stream,"%s:[%s]\n",ActionNames[result->_action],d->_path); + } + result->_err_code = 1; + if (g_debug) { + printf("commit2::process_func(): FAILURE: status: %d\n",status); + } + return TRUE; //WILL STOP AT THIS POINT + } + } + } + return FALSE; +} + + +/** + * + **/ +boolean +complete(GNode *node, boolean test_mode) +{ + gpointer gp = ((GNode*)node)->data; + if (g_debug) { + if (((struct VyattaNode*)gp)->_data._name != NULL) { + printf("commit2::complete(): %s\n",((struct VyattaNode*)gp)->_data._name); + } + else { + printf("commit2::complete()\n"); + } + } + //on transactional nodes only, note to avoid calling this if a headless root + common_commit_copy_to_live_config(((struct VyattaNode*)gp)->_data._path, test_mode); + return TRUE; +} + + +/** + * + * + **/ +gboolean +sort_func_priority(GNode *node, gpointer data) +{ + return sort_func(node,data,TRUE); +} + +/** + * + * + **/ +gboolean +sort_func_simple(GNode *node, gpointer data) +{ + return sort_func(node,data,FALSE); +} + + + +/** + * + * + **/ +gboolean +sort_func(GNode *node, gpointer data, boolean priority_mode) +{ + gpointer gp = ((GNode*)node)->data; + GNode *root_node = (GNode*)data; + if (g_debug) { + if (((struct VyattaNode*)gp)->_data._name != NULL) { + printf("commit2::sort_func(): %s, node count: %d\n",((struct VyattaNode*)gp)->_data._name,g_node_n_children(root_node)); + } + else { + printf("commit2::sort_func(): [n/a], node count: %d\n",g_node_n_children(root_node)); + } + } + + //change action state of node according to enclosing behavior + if ((G_NODE_IS_ROOT(node) == FALSE) && + ((IS_SET_OR_CREATE(((struct VyattaNode*)gp)->_data._operation)) || + (IS_DELETE(((struct VyattaNode*)gp)->_data._operation))) && + (IS_NOOP(((struct VyattaNode*)(node->parent->data))->_data._operation))) { + + //first check if there is enclosing behavior + boolean enclosing = FALSE; + GNode *n = node; + while (TRUE) { + n = n->parent; + vtw_def def = ((struct VyattaNode*)(n->data))->_config._def; + if (def.actions[end_act].vtw_list_head || def.actions[begin_act].vtw_list_head || + (def.actions[syntax_act].vtw_list_head && def.actions[syntax_act].vtw_list_head->vtw_node_aux == 1)) { + enclosing = TRUE; + break; + } + if (G_NODE_IS_ROOT(n) == TRUE) { + break; + } + } + + //walk back up and flip operations until enclosing behavior + if (enclosing == TRUE) { + GNode *n = node; + while (TRUE) { + n = n->parent; + vtw_def def = ((struct VyattaNode*)(n->data))->_config._def; + ((struct VyattaNode*)(n->data))->_data._operation = ((struct VyattaNode*)gp)->_data._operation | K_ACTIVE_OP; + if (def.actions[end_act].vtw_list_head || def.actions[begin_act].vtw_list_head || + (def.actions[syntax_act].vtw_list_head && def.actions[syntax_act].vtw_list_head->vtw_node_aux == 1)) { + break; + } + if (G_NODE_IS_ROOT(n) == TRUE) { + break; + } + } + } + } + + if (priority_mode) { + if (((struct VyattaNode*)gp)->_priority < LOWEST_PRIORITY) { //only if priority is specified. + //unlink from original tree + g_node_unlink(node); + + GNode *new_node = g_node_copy(node); + GNode *sibling = root_node->children; + //now iterate through siblings of root_node and compare priority + + while (sibling != NULL && ((struct VyattaNode*)gp)->_priority > ((struct VyattaNode*)(sibling->data))->_priority) { + sibling = sibling->next; + if (sibling == NULL || ((struct VyattaNode*)gp)->_priority < ((struct VyattaNode*)(sibling->data))->_priority) { + break; + } + } + + if (g_debug) { + int pri = LOWEST_PRIORITY; + if (sibling != NULL) { + pri = ((struct VyattaNode*)(sibling->data))->_priority; + } + printf("commit2::sort_func(): inserting %s into transaction, priority: %d BEFORE %d\n", ((struct VyattaNode*)gp)->_data._name, ((struct VyattaNode*)gp)->_priority, pri); + } + g_node_insert_before(root_node,sibling,new_node); + } + } + else { + if (g_node_depth(node) == 2) { + if (g_debug) { + printf("commit2::sort_func(): inserting %s into transaction\n", ((struct VyattaNode*)gp)->_data._name); + } + GNode *new_node = g_node_copy(node); + g_node_insert(root_node,-1,new_node); //make a flat structure for now + } + } + return FALSE; +} + + +/** + * Gets a flat collection of nodes, sorted by priority + * + * + **/ +GNode* +get_transactions(GNode *config, boolean priority_mode) +{ + if (g_debug) { + printf("commit2::get_transactions()\n"); + } + + if (config == NULL) { + return NULL; + } + + gpointer gp = ((GNode*)config)->data; + + GNode *trans_root = g_node_new(gp); + if (priority_mode) { + g_node_traverse(config, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)sort_func_priority, + (gpointer)trans_root); + + //only do this if the root isn't empty + if (g_node_n_children(config) != 0) { + g_node_insert(trans_root,-1,config); //add what's left + } + } + else { + g_node_traverse(config, + G_IN_ORDER, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)sort_func_simple, + (gpointer)trans_root); + } + return trans_root; +} + + +/** + * + * + **/ +static gboolean +cleanup_func(GNode *node, gpointer data) +{ + struct VyattaNode *vn = ((struct VyattaNode*)(node->data)); + if (vn->_data._name) { + free(vn->_data._name); + } + if (vn->_data._path) { + free(vn->_data._path); + } + if (vn->_config._help) { + free(vn->_config._help); + } + if (vn->_config._default) { + free(vn->_config._default); + } + if (vn->_config._path) { + free(vn->_config._path); + } + return FALSE; +} + +/** + * + * + **/ +void +cleanup(GNode *root_node) +{ + if (root_node == NULL) { + return; + } + + g_node_traverse(root_node, + G_IN_ORDER, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)cleanup_func, + (gpointer)NULL); + + g_node_destroy(root_node); +} + +/** + * + **/ +gboolean +dump_func(GNode *node, gpointer data) +{ + if (node != NULL) { + guint depth = g_node_depth(node); + + if (depth == 2) { + printf("NEW TRANS\n"); + } + + gpointer gp = ((GNode*)node)->data; + if (((struct VyattaNode*)gp)->_data._name != NULL) { + int i; + + if (IS_DELETE(((struct VyattaNode*)gp)->_data._operation)) { + printf("-"); + } + else if (IS_CREATE(((struct VyattaNode*)gp)->_data._operation)) { + printf("+"); + } + else if (IS_SET(((struct VyattaNode*)gp)->_data._operation)) { + printf(">"); + } + else { + printf(" "); + } + for (i = 0; i < depth; ++i) { + printf(" "); + } + printf("%s (t: %d, p: %d)", ((struct VyattaNode*)gp)->_data._name,((struct VyattaNode*)gp)->_config._def.def_type,((struct VyattaNode*)gp)->_priority); + if (((struct VyattaNode*)gp)->_data._value == TRUE) { + printf(" [VALUE]"); + } + if (((struct VyattaNode*)gp)->_config._multi == TRUE) { + printf(" [MULTI]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head && + ((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head->vtw_node_aux == 0) { + printf(" [SYNTAX]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[create_act].vtw_list_head) { + printf(" [CREATE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[activate_act].vtw_list_head) { + printf(" [ACTIVATE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[update_act].vtw_list_head) { + printf(" [UPDATE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[delete_act].vtw_list_head) { + printf(" [DELETE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head && + ((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head->vtw_node_aux == 1) { + printf(" [COMMIT]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[begin_act].vtw_list_head) { + printf(" [BEGIN]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[end_act].vtw_list_head) { + printf(" [END]"); + } + if (((struct VyattaNode*)gp)->_config._help != NULL) { + // printf("[help: %s]",((struct VyattaNode*)gp)->_config._help); + } + printf("\n"); + } + + } + return FALSE; +} + + +/** + * + **/ +boolean +process_priority_node(GNode *priority_node) +{ + //on each node that is deleted run the delete action within the context of the transaction + struct Result result; + result._err_code = 0; + + if (priority_node == NULL) { + return FALSE; + } + + //if this node is an enclosing node, we'll skip this iteration + gpointer gp = ((GNode*)priority_node)->data; + struct Config *c = &((struct VyattaNode*)gp)->_config; + //does this node contain a begin or end statement? + boolean priority_node_is_enclosing_node = FALSE; + if (c->_def.actions && + (c->_def.actions[end_act].vtw_list_head || c->_def.actions[begin_act].vtw_list_head)){ + priority_node_is_enclosing_node = TRUE; + } + + if (priority_node_is_enclosing_node == FALSE) { + //traverse priority node from children up + g_node_traverse((GNode*)priority_node, + G_POST_ORDER, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)enclosing_process_func, + (gpointer)&result); + + if (result._err_code != 0) { + return FALSE; + } + } + //now perform processing on what's left outside of the enclosing begin/end statements + int i; + for (i = 0; i < top_act; ++i) { + int order; + if (i != delete_act) { + order = G_PRE_ORDER; + } + else { + order = G_POST_ORDER; + } + + result._action = i; + g_node_traverse((GNode*)priority_node, + order, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)process_func, + (gpointer)&result); + } + + if (result._err_code != 0) { + if (g_debug) { + printf("commit2::process_priority_node(): failure on processing pass: %d\n", i); + } + return FALSE; + } + return TRUE; +} + + +/** + * Look for begin/end statements to begin processing + * of actions. + **/ +static gboolean +enclosing_process_func(GNode *node, gpointer data) +{ + if (node == NULL) { + return TRUE; + } + + struct Result *result = (struct Result*)data; + gpointer gp = ((GNode*)node)->data; + struct Config *c = &((struct VyattaNode*)gp)->_config; + struct Data *d = &((struct VyattaNode*)gp)->_data; + + //does this node contain a begin or end statement? + if (c->_def.actions && + (c->_def.actions[end_act].vtw_list_head || c->_def.actions[begin_act].vtw_list_head)){ + //gotten to this point need to do a call around this enclosing being/end node + g_node_unlink(node); //removed this... + + if (g_debug) { + printf("commit2::enclosing_process_func(): enclosing statement found on: %s\n",d->_path); + } + //perform recursive calling on new process node... + + int i; + for (i = 0; i < top_act; ++i) { + int order; + if (i != delete_act) { + order = G_PRE_ORDER; + } + else { + order = G_POST_ORDER; + } + + result->_action = i; + g_node_traverse((GNode*)node, + order, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)process_func, + (gpointer)result); + } + + if (result->_err_code != 0) { //EXECUTE_LIST RETURNS FALSE ON FAILURE.... + if (g_debug) { + printf("commit2::enclosing_process_func(): FAILURE: status: %d\n",result->_err_code); + } + return TRUE; //WILL STOP AT THIS POINT + } + } + return FALSE; +} + + diff --git a/src/common/common.c b/src/common/common.c new file mode 100644 index 0000000..6cdf3e7 --- /dev/null +++ b/src/common/common.c @@ -0,0 +1,11 @@ +#include <glib-2.0/glib.h> +#include "common.h" + + + + +boolean +execute(char *cmd) +{ + return TRUE; +} diff --git a/src/common/common.h b/src/common/common.h new file mode 100644 index 0000000..c9cacc8 --- /dev/null +++ b/src/common/common.h @@ -0,0 +1,53 @@ +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include "defs.h" +#include "unionfs.h" + + +boolean +execute(char *cmd); + +/** + * + **/ +GNode* +common_get_local_session_data(); + +/** + * flushes local session + **/ +void +common_clear_local_session(); + +/** + * brings over local session conf to main config + **/ +void +commmon_copy_local_to_main(); + +/** + * sets system context for operation (i.e. hack for unionfs implementation) + **/ +void +common_set_context(char *cpath, char *dpath); + +/** + * sets system parent context for operation (i.e. hack for unionfs implementation) + **/ +void +common_set_parent_context(char *cpath, char *dpath); + +/** + * + **/ +void +common_commit_copy_to_live_config(char *path, boolean test_mode); + +/** + * + **/ +void +common_commit_clean_temp_config(boolean test_mode); + +#endif //__COMMON_H__ diff --git a/src/common/defs.h b/src/common/defs.h new file mode 100644 index 0000000..b489919 --- /dev/null +++ b/src/common/defs.h @@ -0,0 +1,72 @@ +#ifndef __DEFS_H__ +#define __DEFS_H__ + +#include <stdlib.h> +#include <stdio.h> +#include "cli_val.h" + +#define boolean int + +#define LOWEST_PRIORITY 1000 + +#define MAX_DEPTH 128 + +struct Result +{ + int _err_code; + char *_err_str; + int _action; +}; + +typedef enum { + K_NO_OP = 0x01, + K_ACTIVE_OP = 0x02, //as a result of an already created node, but assuming action + K_SET_OP = 0x04, + K_CREATE_OP = 0x08, + K_DEL_OP = 0x10 +} NODE_OPERATION; + +#define IS_SET(op) (op & K_SET_OP) +#define IS_ACTIVE(op) (op & K_ACTIVE_OP) +#define IS_CREATE(op) (op & K_CREATE_OP) +#define IS_DELETE(op) (op & K_DEL_OP) +#define IS_NOOP(op) (op & K_NO_OP) +#define IS_SET_OR_CREATE(op) ((op & K_SET_OP) || (op & K_CREATE_OP)) + +/** + * keeps both configuration and template data in single structure + * + **/ + +/* +TODO: either port over to new system or retain complete set of cli_val definiaitons. +remove _actions and rely on def in the future. + */ +struct Config +{ + boolean _multi; + vtw_def _def; //keep this here + char* _help; + char* _default; + char* _path; +}; + +/* + * is used to define embedded nodes (multi) and leafs + */ +struct Data +{ + char* _name; //name of this node + boolean _value; //is this a value? + char* _path; + NODE_OPERATION _operation; //no-op, set, or delete +}; + +struct VyattaNode +{ + struct Data _data; + struct Config _config; + int _priority; //used for setting priority +}; + +#endif //__DEFS_H__ diff --git a/src/common/priority b/src/common/priority new file mode 100644 index 0000000..ce7800b --- /dev/null +++ b/src/common/priority @@ -0,0 +1,3 @@ +200 firewall +300 interfaces +400 system diff --git a/src/common/unionfs.c b/src/common/unionfs.c new file mode 100644 index 0000000..e325058 --- /dev/null +++ b/src/common/unionfs.c @@ -0,0 +1,1086 @@ +#include <string.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <glib-2.0/glib.h> +#include "common/defs.h" +#include "common/unionfs.h" + +extern boolean g_debug; + +extern vtw_path m_path; +extern vtw_path t_path; + +void +retrieve_data(char* rel_data_path, GNode *node, char* root, NODE_OPERATION op); + +void +apply_priority(GNode *root_node); + +void +set_path(char *path, boolean config); + +struct VyattaNode* +copy_vyatta_node(struct VyattaNode* vn); + +void +get_term_data_values(GNode *node); + +void +dlist_test_func(GQuark key_id,gpointer data,gpointer user_data); + +void +match_priority_node(GNode *node, gchar** tok_str, int pri); + +GNode* +insert_sibling_in_order(GNode *parent, GNode *child); + +void +piecewise_remove(char* cbuf_root, char* abuf_root, char* path, boolean test_mode); +/** + * + * + Data is stored on the path: + + /opt/vyatta/config/tmp/new_config_5425/system/login/user/foo/authentication/plaintext-password + + Config is stored along this path: + + /opt/vyatta/config/template/vyatta-cfg/system/login/user/node.tag/authentication/plaintext-password + + 1) Need to split out relative path + + 2) if node is *MULTI* then the path value is the actual value + + 3) For the config copy the pointer from an existing config value for each multinode +<Need to figure out how this is going to happen> + +This should allow a combined data/config tree + + * + * + * + **/ +char* +get_config_path(GNode *node) +{ + char *buf; + buf = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + buf[0] = '/'; + buf[1] = '\0'; + if (node == NULL) { + return NULL; + } + + GNode *n = node; + while (G_NODE_IS_ROOT(n) != TRUE) { + struct VyattaNode *d = (struct VyattaNode*)n->data; + if (d == NULL) { + if (g_debug) { + printf("unionfs::get_config_path(): data ptr is null\n"); + } + return NULL; + } + + //if not found, mark current node as multinode + if (d->_data._name != NULL) { + char tmp[MAX_LENGTH_DIR_PATH]; + strcpy(tmp,buf); + + //need to check the configuration location for the existance of this node to determine if it's a multi + if (G_NODE_IS_ROOT(n->parent) != TRUE && + ((struct VyattaNode*)(n->parent->data))->_config._multi == TRUE) { + sprintf(buf,"node.tag/%s",tmp); + } + else { + sprintf(buf,"%s/%s",d->_data._name,tmp); + } + } + n = n->parent; + } + return buf; +} + + +/** + * Recurse from node identified as deleted + **/ +void +retrieve_delete_data(char* rel_data_path, GNode *node) +{ + //switch root + + return; +} + +/** + * + **/ +void +retrieve_data(char* rel_data_path, GNode *node, char* root, NODE_OPERATION op) +{ + boolean final_node = FALSE; + + if (node == NULL) { + return; + } + + char *tmp = root;//get_cdirp(); + char full_data_path[sizeof(char)*MAX_LENGTH_DIR_PATH]; + strcpy(full_data_path,tmp); + if (rel_data_path != NULL) { + strcat(full_data_path,rel_data_path); + } + + if (g_debug) { + printf("unionfs::retrieve_data(): %s\n", full_data_path); + } + + + //WE'LL WANT TO DO SOMETHING LIKE--IS THIS NODE A VALUE NODE, THEN PROCESS. + + //now check for value at this node and copy + char *cp = NULL; + //data_path needs to be of type vtw_path!! + vtw_path vpath; + init_path(&vpath, full_data_path); + //lstat for value file BEFORE retrieving + if (value_exists(full_data_path) && get_value(&cp, &vpath) == 0) { + //terminating value need to create new terminating node here! + struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); + GNode *new_node = g_node_new(vn); + new_node = insert_sibling_in_order(node,new_node); + // new_node = g_node_insert(node, -1, new_node); + vn->_data._name = cp; + vn->_data._value = FALSE;//TRUE; //data value + vn->_data._operation = op; + vn->_priority = LOWEST_PRIORITY; + vn->_data._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + sprintf(vn->_data._path,"%s",rel_data_path); + final_node = TRUE; + } + /* + NOTE: will need to implement ptr copy over for the config data that + traverses up multi-nodes. Currently this will just read the data from + the parser AGAIN, but in the name of memory and performance efficiency + this should be a copy of the already processed data. + */ + + //Now dig up the configuration data + char *config_path = get_config_path(node); + + if (config_path != NULL) { + char *conf_base = (char*)get_tdirp(); + char buf[MAX_LENGTH_HELP_STR]; + sprintf(buf,"%s/%snode.def",conf_base,config_path); + struct stat s; + if (g_debug) { + printf("unionfs::retrieve_data(): config path: %s\n",buf); + } + + struct VyattaNode* vn = (struct VyattaNode*)node->data; + vtw_def def; + memset(&def, 0, sizeof(def)); + if ((lstat(buf,&s) == 0) && S_ISREG(s.st_mode)) { + if (parse_def(&def, buf, FALSE) == 0) { + if (g_debug) { + printf("[FOUND node.def]"); + } + //either multi or tag--shouldn't have made a difference, but arkady was confused. + vn->_config._multi = (def.tag | def.multi); + } + } + + vn->_config._def = def; + vn->_config._path = config_path; + char *tmp = malloc(sizeof(char)*MAX_LENGTH_DIR_PATH); + strcpy(tmp,rel_data_path); + strcat(tmp,"/"); + vn->_data._path = tmp; + + //will stamp an embedded node as a value node + if (G_NODE_IS_ROOT(node) == FALSE) { + struct VyattaNode* vn_parent = (struct VyattaNode*)node->parent->data; + if (vn_parent->_config._multi == TRUE) { + ((struct VyattaNode*)node->data)->_data._value = TRUE; + + //now let's patch up the def multi-nodes + //move the def for the multinode from the parent to the value node + struct VyattaNode* vn2 = (struct VyattaNode*)node->data; + if (final_node == FALSE) { //non-term multi + if (g_node_n_children(node->parent) == 1) { + vn2->_config._def = ((struct VyattaNode*)node->parent->data)->_config._def; + memset(&((struct VyattaNode*)node->parent->data)->_config._def, 0, sizeof(vtw_def)); + } + else { //find node other than myself to copy defs across + GNode *first_child = g_node_first_child(node->parent); + if (first_child == node) { + first_child = g_node_next_sibling(first_child); + } + vn2->_config._def = ((struct VyattaNode*)first_child->data)->_config._def; + } + } + } + } + + if (g_debug) { + printf("\n"); + } + } + + if (final_node == TRUE) { + //move defs to child... + get_term_data_values(node); + + //fix operation on parent because this is a final node + char buf[MAX_LENGTH_HELP_STR]; + sprintf(buf,"%s/%s",get_adirp(),rel_data_path); + struct stat s; + if ((lstat(buf,&s) != 0) && S_ISREG(s.st_mode)) { + struct VyattaNode* vn = (struct VyattaNode*)node->data; + vn->_data._operation = K_CREATE_OP; + } + return; + } + + //iterate over directory here + DIR *dp; + if ((dp = opendir(full_data_path)) == NULL){ + if (g_debug) { + //could also be a terminating value now + printf("unionfs::retrieve_data(), failed to open directory: %s\n", full_data_path); + } + return; + } + //finally iterate over valid child directory entries + + boolean processed = FALSE; + struct dirent *dirp = NULL; + while ((dirp = readdir(dp)) != NULL) { + if (strcmp(dirp->d_name, ".") != 0 && + strcmp(dirp->d_name, "..") != 0 && + strcmp(dirp->d_name, MODIFIED_FILE) != 0 && + strcmp(dirp->d_name, DEF_FILE) != 0 && + strcmp(dirp->d_name, WHITEOUT_FILE) != 0 && + strcmp(dirp->d_name, VALUE_FILE) != 0) { + processed = TRUE; + char *data_buf = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + if (strncmp(dirp->d_name,DELETED_NODE,4) == 0) { + strcpy(data_buf,dirp->d_name+4); //SKIP THE .WH. + + //create new node and insert... + struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); + vn->_data._name = data_buf; + vn->_data._value = FALSE; + vn->_data._operation = K_DEL_OP; + vn->_priority = LOWEST_PRIORITY; + + char new_data_path[MAX_LENGTH_DIR_PATH]; + sprintf(new_data_path,"%s/%s",rel_data_path,data_buf); + + GNode *new_node = g_node_new(vn); + // new_node = g_node_insert(node, -1, new_node); + new_node = insert_sibling_in_order(node,new_node); + + //will need to enter a special recursion against the active configuration to mark nested delete nodes + retrieve_data(new_data_path,new_node,get_adirp(),K_DEL_OP); + } + else { + strcpy(data_buf,dirp->d_name); + //create new node and insert... + struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); + vn->_data._name = data_buf; + vn->_data._value = FALSE; + vn->_priority = LOWEST_PRIORITY; + + char new_data_path[MAX_LENGTH_DIR_PATH]; + sprintf(new_data_path,"%s/%s",rel_data_path,data_buf); + + char active_data_path[MAX_LENGTH_DIR_PATH]; + sprintf(active_data_path,"%s%s",get_adirp(),rel_data_path); + struct stat s; + + if (lstat(active_data_path,&s) == 0) { + vn->_data._operation = K_NO_OP; + } + else { + vn->_data._operation = K_CREATE_OP; + ((struct VyattaNode*)node->data)->_data._operation = K_CREATE_OP; + } + //set recursed entry op to del + if (op == K_DEL_OP) { + vn->_data._operation = K_DEL_OP; + } + GNode *new_node = g_node_new(vn); + // new_node = g_node_insert(node, -1, new_node); + new_node = insert_sibling_in_order(node,new_node); + retrieve_data(new_data_path,new_node,root,vn->_data._operation); + } + } + } + //catch hanging case where embedded multinode is new with no children + if (processed == FALSE) { + char active_data_path[MAX_LENGTH_DIR_PATH]; + sprintf(active_data_path,"%s/%s",get_adirp(),rel_data_path); + struct stat s; + if ((lstat(active_data_path,&s) != 0)) { + ((struct VyattaNode*)node->data)->_data._operation = K_CREATE_OP; + } + } + + closedir(dp); + return; +} + + +/** + * + **/ +GNode* +common_get_local_session_data() +{ + //get root directory + char *data_path = malloc(sizeof(char)*MAX_LENGTH_DIR_PATH); + data_path[0] = '\0'; + + struct VyattaNode *vn = calloc(1,sizeof(struct VyattaNode)); + vn->_data._name = NULL; //root node has null + vn->_data._operation = K_NO_OP; + vn->_priority = LOWEST_PRIORITY; + + //create first node + GNode *root_node = g_node_new(vn); + + //iterate through recursive calls to parse_new() calls (see original commit()) + retrieve_data(data_path,root_node,get_cdirp(),K_SET_OP); + + apply_priority(root_node); + + return root_node; +} + + +/** + * + **/ +boolean +value_exists(char *path) +{ + char buf[MAX_LENGTH_DIR_PATH]; + sprintf(buf, "%s/%s",path,VALUE_FILE); + struct stat stat_buf; + return !stat(buf,&stat_buf); +} + +/** + * need to compute parent as it might not be in the node structure + * + **/ +void +common_set_parent_context(char *cpath, char *dpath) +{ + printf("common_set_parent_context(incoming): %s, %s\n",cpath,dpath); + //strip off last path and set + int index = strlen(cpath)-1; + if (cpath[index] == '/') { + while (TRUE) { + if (cpath[--index] != '/') { + cpath[index] = '\0'; + break; + } + } + } + char *ptr = rindex(cpath,'/'); + if (ptr != NULL) { + *ptr = '\0'; + } + set_path(cpath,TRUE); + + index = strlen(dpath)-1; + if (dpath[index] == '/') { + while (TRUE) { + if (dpath[--index] != '/') { + dpath[index] = '\0'; + break; + } + } + } + ptr = rindex(dpath,'/'); + if (ptr != NULL) { + *ptr = '\0'; + } + set_path(dpath,FALSE); + printf("common_set_parent_context: %s, %s\n",cpath,dpath); +} + +/** + * + **/ +void +common_set_context(char *cpath, char *dpath) +{ + printf("common_set_context: %s, %s\n",cpath,dpath); + set_path(cpath,TRUE); + set_path(dpath,FALSE); +} + +/** + * + **/ +void +set_path(char *path, boolean config) +{ + //set t_path, m_path + //tokenize path and iterate + if (config == FALSE) { + init_path(&m_path, get_mdirp()); + } + else { + init_path(&t_path, get_tdirp()); + } + + char* start_ptr = NULL; + char* end_ptr = NULL; + + if (path == NULL) { + if (g_debug) { + printf("unionfs::set_path() null value on entry\n"); + } + return; + } + + start_ptr = path; + while((end_ptr = index(start_ptr+1,'/')) != NULL) { + char tmp[1024]; + + if (*start_ptr == '/') { + ++start_ptr; + } + + int size = end_ptr-start_ptr; + if (size < 1 || size > 1024) { + /* + if (g_debug) { + if (config == FALSE) { + printf("unionfs::set_path(): %s, %s\n", path,m_path.path); + } + else { + printf("unionfs::set_path(): %s, %s\n", path,t_path.path); + } + } + */ + return; + } + + memcpy(tmp, start_ptr, size); + tmp[size] = '\0'; + + if (config == FALSE) { + push_path_no_escape(&m_path, tmp); //data + } + else { + push_path_no_escape(&t_path, tmp); //config + } + start_ptr = end_ptr; + } + /* + if (g_debug) { + if (config == FALSE) { + printf("unionfs::set_path(): %s, %s\n", path,m_path.path); + } + else { + printf("unionfs::set_path(): %s, %s\n", path,t_path.path); + } + } + */ +} + +/** + * NEED TO PROTECT AGAINST NESTED COMMIT NODES. CANNOT BE SUPPORTED + * IN CURRENT HIERARCHICAL STRUCTURE WITHOUT CHANGING HOW UNDERLYING + * SYSTEM MAINTAINS DATA. + * +original commands: + static const char format1[]="cp -r -f %s/* %s"; //mdirp, tmpp + static const char format2[]="sudo umount %s"; //mdirp + static const char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true"; + //tmpp + static const char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; //cdirp + static const char format5[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; //adirp + static const char format6[]="mv -f %s/* -t %s";//tmpp, adirp + static const char format7[]="sudo mount -t $UNIONFS -o dirs=%s=rw:%s=ro $UNIONFS %s"; //cdirp, adirp, mdirp + * + **/ +void +common_commit_copy_to_live_config(char *path, boolean test_mode) +{ + //first check for existence of path before committing + + if (g_debug) { + printf("common_commit_copy_to_live_config(): %s\n",path); + } + char *command = malloc(MAX_LENGTH_DIR_PATH); + static const char format0[]="mkdir -p %s ; /bin/true"; + static const char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/ + + static const char format2[]="sudo umount %s"; //mdirp + + static const char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/ + + //walk up tree until diverge or skip + static const char format6[]="cp -rf %s/* -t %s";/*tmpp, adirp*/ + + static const char format7[]="rm -fr %s >&/dev/null ; /bin/true"; /*tmpp*/ + + static const char format8[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro unionfs %s"; //cdirp, adirp, mdirp + + set_echo(TRUE); + + char mbuf[MAX_LENGTH_DIR_PATH]; + sprintf(mbuf,"%s%s",get_mdirp(),path); + char cbuf[MAX_LENGTH_DIR_PATH]; + sprintf(cbuf,"%s%s",get_cdirp(),path); + char tbuf[MAX_LENGTH_DIR_PATH]; + sprintf(tbuf,"%s%s",get_tmpp(),path); + char abuf[MAX_LENGTH_DIR_PATH]; + sprintf(abuf,"%s%s",get_adirp(),path); + + char mbuf_root[MAX_LENGTH_DIR_PATH]; + sprintf(mbuf_root,"%s",get_mdirp()); + char cbuf_root[MAX_LENGTH_DIR_PATH]; + sprintf(cbuf_root,"%s",get_cdirp()); + char tbuf_root[MAX_LENGTH_DIR_PATH]; + sprintf(tbuf_root,"%s",get_tmpp()); + char abuf_root[MAX_LENGTH_DIR_PATH]; + sprintf(abuf_root,"%s",get_adirp()); + + //only operate on path if it exists + struct stat s; + if ((lstat(mbuf,&s) != 0) && S_ISREG(s.st_mode)) { + printf("common_commit_copy_to_live_config(): failed to find: %s, aborting copy\n",mbuf); + return; + } + + sprintf(command,format0,tbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format1, mbuf, tbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format2, mbuf_root); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command,format0,abuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + piecewise_remove(cbuf_root,abuf_root,path,test_mode); + + sprintf(command, format4, cbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + /* + sprintf(command, format5, abuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + */ + //special piecewise operation + //walk down tree and perform command where siblings diverge btwn tbuf and abuf + + sprintf(command, format6, tbuf, abuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + + sprintf(command, format7, tbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format8, cbuf_root,abuf_root,mbuf_root); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + fflush(NULL); + + free(command); + return; +} + +/** + * + **/ +void +common_commit_clean_temp_config(boolean test_mode) +{ + if (g_debug) { + printf("common_commit_clean_temp_config()\n"); + } + //first clean up the root + // common_commit_copy_to_live_config("/"); + + char *command; + command = malloc(MAX_LENGTH_DIR_PATH); + static const char format2[]="sudo umount %s"; //mdirp + static const char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true"; /*tmpp*/ + static const char format5[]="rm -fr %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/ + static const char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro unionfs %s"; //cdirp, adirp, mdirp + + + char tbuf[MAX_LENGTH_DIR_PATH]; + sprintf(tbuf,"%s",get_tmpp()); + char cbuf[MAX_LENGTH_DIR_PATH]; + sprintf(cbuf,"%s",get_cdirp()); + char mbuf[MAX_LENGTH_DIR_PATH]; + sprintf(mbuf,"%s",get_mdirp()); + char abuf[MAX_LENGTH_DIR_PATH]; + sprintf(abuf,"%s",get_adirp()); + + set_echo(TRUE); + + sprintf(command, format2, mbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format3, tbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format3, cbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format5, cbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + sprintf(command, format7, cbuf,abuf,mbuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + + /* notify other users in config mode */ + system("/opt/vyatta/sbin/vyatta-cfg-notify"); + + free(command); + return; +} + + +void +match_priority_node(GNode *node, gchar** tok_str, int pri) +{ + int index = g_node_depth(node)-1; + if (tok_str[index] == NULL) { + //MATCHED TO THE END + ((struct VyattaNode*)node->data)->_priority = pri; + return; + } + //iterate over children for a potential match + GNode *child = g_node_first_child(node); + while (child != NULL) { + char *compval = ((struct VyattaNode*)child->data)->_data._name; + if (strncmp(compval,tok_str[index],strlen(compval)) == 0) { + match_priority_node(child,tok_str,pri); + } + else if (strncmp(NODE_TAG_FILE,tok_str[index],strlen(NODE_TAG_FILE)) == 0) { + match_priority_node(child,tok_str,pri); + } + child = g_node_next_sibling(child); + } + return; +} + +/** + * + * + **/ +void +set_node_priority(GNode *root_node,char* path,unsigned long pri) +{ + if (root_node == NULL) { + return; + } + gchar** tok_str = g_strsplit(path,"/",MAX_DEPTH); + + struct PriData pri_data; + + pri_data._pri = pri; + pri_data._tok_str = tok_str; + + match_priority_node(root_node,tok_str,pri); + + g_strfreev(tok_str); + return; +} + + +/** + * + **/ +void +apply_priority(GNode *root_node) +{ + //parse file and find node and set value + FILE *fp = fopen(NODE_PRIORITY_FILE, "r"); + if (fp != NULL) { + if (g_debug) { + printf("unionfs::apply_priority(), found priority file\n"); + } + + char str[1025]; + while (fgets(str, 1024, fp) != 0) { + gchar** tok_str = g_strsplit(str," ",3); + char *path = tok_str[1]; + if (g_debug) { + printf("unionfs::apply_priority(), working on this %s\n",path); + } + + //now apply to node + set_node_priority(root_node,path,strtoul(tok_str[0],NULL,10)); + g_strfreev(tok_str); + } + fclose(fp); + } +} + +/** + * + **/ +struct VyattaNode* +copy_vyatta_node(struct VyattaNode* vn) +{ + struct VyattaNode *new_vn = calloc(1,sizeof(struct VyattaNode)); + + if (vn->_data._name != NULL) { + new_vn->_data._name = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + strcpy(new_vn->_data._name,vn->_data._name); + } + new_vn->_data._value = vn->_data._value; + if (vn->_data._path != NULL) { + new_vn->_data._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + strcpy(new_vn->_data._path,vn->_data._path); + } + new_vn->_data._operation = vn->_data._operation; + new_vn->_priority = vn->_priority; + + new_vn->_config._multi = new_vn->_config._multi; + // new_vn->_config._def = new_vn->_config._def; //cpy this? + if (vn->_config._default != NULL) { + new_vn->_config._default = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + strcpy(new_vn->_config._default,vn->_config._default); + } + if (vn->_config._path != NULL) { + new_vn->_config._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + strcpy(new_vn->_config._path,vn->_config._path); + } + return new_vn; +} + +/** + * + **/ +void +get_term_data_values(GNode *node) +{ + struct VyattaNode* vn = (struct VyattaNode*)node->data; + char full_new_data_path[MAX_LENGTH_DIR_PATH]; + char full_active_data_path[MAX_LENGTH_DIR_PATH]; + sprintf(full_active_data_path,"%s/%s",get_adirp(),vn->_data._path); + sprintf(full_new_data_path,"%s/%s",get_mdirp(),vn->_data._path); + + GData *datalist; + g_datalist_init(&datalist); + + //now check for value at this node and copy + char *cp = NULL; + //data_path needs to be of type vtw_path!! + vtw_path vpath; + //lstat for value file BEFORE retrievin + gchar **tok_str_new = NULL; + gchar **tok_str_active = NULL; + + //create full_data_path here + + init_path(&vpath, full_active_data_path); + if (value_exists(full_active_data_path) && get_value(&cp, &vpath) == 0) { + tok_str_active = g_strsplit(cp,"\n",0); + } + + + init_path(&vpath, full_new_data_path); + if (value_exists(full_new_data_path) && get_value(&cp, &vpath) == 0) { + tok_str_new = g_strsplit(cp,"\n",0); + } + + if (vn->_config._multi == TRUE) { + //add active elements + int i; + for (i = 0; tok_str_active != NULL && tok_str_active[i] != NULL; ++i) { + struct ValueData *data; + data = (struct ValueData*)calloc(1, sizeof(struct ValueData)); + //HANDLE EMPTY NEW CONFIG + data->_state = K_DEL_OP; + g_datalist_set_data(&datalist, tok_str_active[i], data); + } + + //add value elements not found in set yet + for (i = 0; tok_str_new != NULL && tok_str_new[i] != NULL; ++i) { + gpointer g; + if ((g = g_datalist_get_data(&datalist,tok_str_new[i])) == NULL) { + struct ValueData *data; + data = (struct ValueData*)calloc(1, sizeof(struct ValueData)); + data->_state = K_CREATE_OP; + g_datalist_set_data(&datalist, tok_str_new[i], data); + } + else { + ((struct ValueData*)g)->_state = K_NO_OP; + } + } + } + else { //leaf + struct ValueData *data; + data = (struct ValueData*)calloc(1, sizeof(struct ValueData)); + if ((tok_str_active == NULL || tok_str_active[0] == NULL) && + (tok_str_new == NULL || tok_str_new[0] == NULL)) { + // data->_state = K_NO_OP; + // g_datalist_set_data(&datalist, tok_str_active[0], data); + } + else if (tok_str_active == NULL || tok_str_active[0] == NULL) { + data->_state = K_CREATE_OP; + g_datalist_set_data(&datalist, tok_str_new[0], data); + } + else if (tok_str_new == NULL || tok_str_new[0] == NULL) { + data->_state = K_DEL_OP; + g_datalist_set_data(&datalist, tok_str_active[0], data); + } + else { + if (strcmp(tok_str_active[0],tok_str_new[0]) != 0) { + data->_state = K_SET_OP; + g_datalist_set_data(&datalist, tok_str_new[0], data); + } + else { + data->_state = K_NO_OP; + g_datalist_set_data(&datalist, tok_str_new[0], data); + } + } + } + + //now let's process the node's values.... + + g_datalist_foreach(&datalist, dlist_test_func, node); + struct VyattaNode *vn_parent = (struct VyattaNode*)node->data; + memset(&vn_parent->_config._def, 0, sizeof(vtw_def)); + + g_strfreev(tok_str_new); + g_strfreev(tok_str_active); + + // g_dataset_destroy(&datalist); + + return; +} + +/** + * + **/ +void +dlist_test_func(GQuark key_id,gpointer data,gpointer user_data) +{ + if (key_id == 0) { + return; + } + GNode *node = (GNode*)user_data; + struct VyattaNode *vn = (struct VyattaNode*)node->children->data; + struct VyattaNode *vn_parent = (struct VyattaNode*)node->data; + struct VyattaNode *new_vn = NULL; + //single entry has alread been created. + + + if (vn->_data._value == TRUE) { + new_vn = copy_vyatta_node(vn); + GNode *new_node = g_node_new(new_vn); + //g_node_insert(node, -1, new_node); + insert_sibling_in_order(node,new_node); + new_vn->_config._def = vn->_config._def; + // strcat(new_vn->_data._path,"/value"); + } + else { + new_vn = vn; + strcat(new_vn->_data._path,"/value"); + } + new_vn->_data._value = TRUE; + strcpy(new_vn->_data._name,(char*)g_quark_to_string(key_id)); + new_vn->_config._path = malloc(MAX_LENGTH_DIR_PATH*sizeof(char)); + sprintf(new_vn->_config._path,"%s/node.tag",vn_parent->_config._path); + new_vn->_data._operation = ((struct ValueData*)data)->_state; + new_vn->_config._def = vn_parent->_config._def; +} + +/** + * + **/ +void +piecewise_remove(char* cbuf_root, char* abuf_root, char* path, boolean test_mode) +{ + char cbuf[MAX_LENGTH_DIR_PATH]; + char abuf[MAX_LENGTH_DIR_PATH]; + + sprintf(cbuf,"%s/%s",cbuf_root,path); + sprintf(abuf,"%s/%s",abuf_root,path); + + //iterate over directory here + DIR *dp; + if ((dp = opendir(cbuf)) == NULL){ + if (g_debug) { + //could also be a terminating value now + printf("piecewise_remove(), failed to open directory: %s\n", cbuf); + } + + //if failed, then check if this is a whiteout directory itself, if so remove and return + //only can really happen on initial call + char tmp[MAX_LENGTH_DIR_PATH]; + sprintf(tmp,"%s/%s%s",cbuf_root,DELETED_NODE,path+1); //need to skip first slash + tmp[strlen(tmp)-1] = '\0'; //drop last slash too + struct stat s; + if ((lstat(tmp,&s) == 0) && S_ISREG(s.st_mode)) { + //whiteout root, remove and return + static const char format5[]="rm -fr %s >&/dev/null ; /bin/true"; /*adirp*/ + //remove these guys + char command[MAX_LENGTH_DIR_PATH]; + sprintf(command, format5, abuf); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + } + return; + } + + //finally iterate over valid child directory entries + struct dirent *dirp = NULL; + char local_path[MAX_LENGTH_DIR_PATH]; + while ((dirp = readdir(dp)) != NULL) { + + // printf("F: comparing: %s to %s up to %d\n",DELETED_NODE, dirp->d_name, strlen(DELETED_NODE)); + if (strncmp(DELETED_NODE,dirp->d_name,strlen(DELETED_NODE)) == 0) { + static const char format5[]="rm -fr %s >&/dev/null ; /bin/true"; /*adirp*/ + //remove these guys + char command[MAX_LENGTH_DIR_PATH]; + char tmp[MAX_LENGTH_DIR_PATH]; + sprintf(tmp,"%s/%s",abuf,dirp->d_name+strlen(DELETED_NODE)); + + sprintf(command, format5, tmp); + if (g_debug) { + printf("%s\n",command); + fflush(NULL); + } + if (test_mode == FALSE) { + system(command); + } + } + else if (strcmp(dirp->d_name, ".") != 0 && + strcmp(dirp->d_name, "..") != 0 && + strcmp(dirp->d_name, MODIFIED_FILE) != 0 && + strcmp(dirp->d_name, DEF_FILE) != 0 && + strcmp(dirp->d_name, VALUE_FILE) != 0) { + sprintf(local_path,"%s/%s",path,dirp->d_name); + piecewise_remove(cbuf_root,abuf_root,local_path,test_mode); + } + } + closedir(dp); +} + +/** + * + **/ +GNode* +insert_sibling_in_order(GNode *parent, GNode *child) +{ + //find alphabetical order to insert child into sibling + GNode *sibling = parent->children; + while (sibling != NULL) { + if (strcmp((((struct VyattaNode*)(child->data))->_data._name),((struct VyattaNode*)(sibling->data))->_data._name) > 0) { + break; + } + sibling = sibling->next; + } + GNode *new_node = g_node_insert_after(parent, sibling, child); + return new_node; +} diff --git a/src/common/unionfs.h b/src/common/unionfs.h new file mode 100644 index 0000000..098ac41 --- /dev/null +++ b/src/common/unionfs.h @@ -0,0 +1,49 @@ +#ifndef __UNIONFS_HH__ +#define __UNIONFS_HH__ + +#include <glib-2.0/glib.h> +#include <stdio.h> +#include "defs.h" +#include "../cli_val.h" +#include "../cli_objects.h" + +/* 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" +#define NODE_PRIORITY_FILE "/opt/vyatta/share/vyatta-cfg/priority" + +#define NODE_TAG_FILE "node.tag" +#define VALUE_FILE "node.val" +#define MODIFIED_FILE ".modified" +#define DEF_FILE "def" +#define WHITEOUT_FILE ".wh.__dir_opaque" +#define DELETED_NODE ".wh." + +#define MAX_LENGTH_DIR_PATH 1024 +#define MAX_LENGTH_HELP_STR 1024 + +boolean +value_exists(char *path); + +struct PriData { + unsigned long _pri; + gchar **_tok_str; +}; + + +struct ValueData +{ + boolean _leaf; + NODE_OPERATION _state; +}; + + +#endif //__UNIONFS_HH__ diff --git a/src/dump_session.c b/src/dump_session.c new file mode 100644 index 0000000..d6b85cf --- /dev/null +++ b/src/dump_session.c @@ -0,0 +1,166 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <glib-2.0/glib.h> +#include "common/common.h" + +boolean g_debug = FALSE; +boolean g_verbose = FALSE; + +static gboolean +dump_func(GNode *node, gpointer data); + +extern GNode* +common_get_local_session_data(); + +/* +NOTES: reverse: use the n-nary tree in commit2.c and only encapuslate data store. pass in func pointer for processing of commands below. + +also, the algorithm for collapsing the tree into a transaction list is: +1) iterate through tree and mark all explicit transactions +2) when done, prune the tree of all root explicit transactions +3) Now iterate through remaining tree and remove each node and append to transaction list. + + + */ + +/** + * + * + **/ +void +usage() +{ + printf("dump_session\n"); + printf("-d\t\tdebug mode\n"); + printf("-v\t\tverbose mode\n"); + printf("-h\t\thelp\n"); +} + +/** + * + * + **/ +int +main(int argc, char** argv) +{ + int ch; + //grab inputs + while ((ch = getopt(argc, argv, "dvh")) != -1) { + switch (ch) { + case 'd': + g_debug = TRUE; + break; + case 'v': + g_verbose = TRUE; + break; + case 'h': + usage(); + exit(0); + break; + default: + usage(); + exit(0); + } + } + + //get local session data plus configuration data + GNode *config_root_node = common_get_local_session_data(); + if (config_root_node == NULL) { + exit(0); + } + + printf("Starting dump\n"); + + //iterate over config_data and dump... + g_node_traverse(config_root_node, + G_PRE_ORDER, + G_TRAVERSE_ALL, + -1, + (GNodeTraverseFunc)dump_func, + (gpointer)NULL); + + return 0; + } + + +static gboolean +dump_func(GNode *node, gpointer data) +{ + if (node != NULL) { + guint depth = g_node_depth(node); + + gpointer gp = ((GNode*)node)->data; + if (((struct VyattaNode*)gp)->_data._name != NULL) { + int i; + + if (IS_DELETE(((struct VyattaNode*)gp)->_data._operation)) { + printf("-"); + } + else if (IS_CREATE(((struct VyattaNode*)gp)->_data._operation)) { + printf("+"); + } + else if (IS_SET(((struct VyattaNode*)gp)->_data._operation)) { + printf(">"); + } + else { + printf(" "); + } + for (i = 0; i < depth; ++i) { + printf(" "); + } + printf("%s (t: %d, p: %d)", ((struct VyattaNode*)gp)->_data._name,((struct VyattaNode*)gp)->_config._def.def_type,((struct VyattaNode*)gp)->_priority); + if (((struct VyattaNode*)gp)->_data._value == TRUE) { + printf(" [VALUE]"); + } + if (((struct VyattaNode*)gp)->_config._multi == TRUE) { + printf(" [MULTI]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head && + ((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head->vtw_node_aux == 0) { + printf(" [SYNTAX]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[create_act].vtw_list_head) { + printf(" [CREATE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[activate_act].vtw_list_head) { + printf(" [ACTIVATE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[update_act].vtw_list_head) { + printf(" [UPDATE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[delete_act].vtw_list_head) { + printf(" [DELETE]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head && + ((struct VyattaNode*)gp)->_config._def.actions[syntax_act].vtw_list_head->vtw_node_aux == 1) { + printf(" [COMMIT]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[begin_act].vtw_list_head) { + printf(" [BEGIN]"); + } + if (((struct VyattaNode*)gp)->_config._def.actions[end_act].vtw_list_head) { + printf(" [END]"); + } + + if (g_verbose == TRUE) { + printf("\n"); + for (i = 0; i < depth; ++i) { + printf(" "); + } + printf("%s\n",((struct VyattaNode*)gp)->_config._path); + for (i = 0; i < depth; ++i) { + printf(" "); + } + printf("%s\n",((struct VyattaNode*)gp)->_data._path); + } + + if (((struct VyattaNode*)gp)->_config._help != NULL) { + // printf("[help: %s]",((struct VyattaNode*)gp)->_config._help); + } + printf("\n"); + } + + } + return FALSE; +} diff --git a/src/exe_action.c b/src/exe_action.c new file mode 100644 index 0000000..200ec2d --- /dev/null +++ b/src/exe_action.c @@ -0,0 +1,122 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <glib-2.0/glib.h> +#include "common/common.h" + +boolean g_debug = FALSE; + +/** + * + * + **/ +void +usage() +{ + printf("exe_action\n"); + printf("-p\t\trelative path to node.def\n"); + printf("-a\t\taction (integer value)\n"); + printf("-d\t\tdebug mode\n"); + printf("-h\t\thelp\n"); +} + + +/** + * + **/ +const char* get_tmpp(void) { + + const char* tmpp=getenv(ENV_T_DIR); + + if(!tmpp) + tmpp=""; + + return tmpp; +} + + +/** + * + * + **/ +int +main(int argc, char** argv) +{ + int ch; + char *path = NULL; + unsigned long act = 0; + + //grab inputs + while ((ch = getopt(argc, argv, "dhp:a:")) != -1) { + switch (ch) { + case 'd': + g_debug = TRUE; + break; + case 'h': + usage(); + exit(0); + break; + case 'p': + path = optarg; + break; + case 'a': + act = strtoul(optarg,NULL,10); + break; + default: + usage(); + exit(0); + } + } + + + vtw_def def; + struct stat s; + const char *root = get_tmpp(); + char buf[2048]; + sprintf(buf,"%s/%snode.def",root,path); + printf("%s\n",buf); + initialize_output(); + init_paths(TRUE); + + printf("[path: %s][act: %lu]\n",buf,act); + + if ((lstat(buf,&s) == 0) && S_ISREG(s.st_mode)) { + memset(&def, 0, sizeof(def)); + if (parse_def(&def,buf,FALSE) == 0) { + //execute + int status; + if (def.actions[act].vtw_list_head) { + set_in_commit(TRUE); + + char foo[1024]; + sprintf(foo,"b"); + set_at_string(foo); + + //BROKEN--NEEDS TO BE FIX BELOW FOR DPATH AND CPATH + common_set_context(path,path); + + status = execute_list(def.actions[act].vtw_list_head,&def); + if (status == FALSE) { + printf("command failed! status: %d\n", status); + } + else { + printf("SUCCESS!\n"); + } + set_in_commit(FALSE); + } + else { + printf("no action for this node\n"); + } + } + else { + printf("failure to parse defination file\n"); + } + } + else { + printf("node.def not found at: %s\n",buf); + } + return 0; +} diff --git a/templates/priority b/templates/priority new file mode 100644 index 0000000..d288209 --- /dev/null +++ b/templates/priority @@ -0,0 +1,26 @@ +200 firewall +310 interfaces/bridge/node.tag +320 interfaces/bonding/node.tag +330 interfaces/ethernet/node.tag +340 interfaces/tunnel/node.tag +350 interfaces/adsl/node.tag +360 interfaces/loopback/node.tag +370 interfaces/openvpn/node.tag +480 interfaces/serial/node.tag +490 interfaces/wirelessmodem/node.tag +400 system +450 protocols/static +470 policy +500 protocols/bgp/node.tag/parameters/router-id +505 protocols/bgp/node.tag/redistribute +510 protocols/bgp/node.tag/neighbor/node.tag +520 protocols/bgp/node.tag/parameters +530 protocols/bgp +540 protocols/bgp/node.tag/parameters/disable-network-import-check +600 protocols/ospf/parameters +610 protocols/ospf +620 protocols/rip +630 protocols +700 vpn +800 interfaces/ethernet/node.tag/vrrp +800 interfaces/ethernet/node.tag/vif/node.tag/vrrp |