diff options
author | slioch <slioch@eng-140.vyatta.com> | 2008-12-15 17:03:00 -0800 |
---|---|---|
committer | slioch <slioch@eng-140.vyatta.com> | 2008-12-15 17:03:00 -0800 |
commit | 5da26e172a8c1ed5201527aefddabd6e277e5c03 (patch) | |
tree | 17b9536117774b0dfd76969b1b3b0aed82c24117 /src/commit2.c | |
parent | 9ed9edcfe9aef3db9306a0d2b7c24f73831b1149 (diff) | |
download | vyatta-cfg-5da26e172a8c1ed5201527aefddabd6e277e5c03.tar.gz vyatta-cfg-5da26e172a8c1ed5201527aefddabd6e277e5c03.zip |
initial checkin of new commit code--building but does not replace original commit. New commit may be accessed through
my_commit2 binary.
Diffstat (limited to 'src/commit2.c')
-rw-r--r-- | src/commit2.c | 755 |
1 files changed, 755 insertions, 0 deletions
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; +} + + |