summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorslioch <slioch@eng-140.vyatta.com>2008-12-15 17:03:00 -0800
committerslioch <slioch@eng-140.vyatta.com>2008-12-15 17:03:00 -0800
commit5da26e172a8c1ed5201527aefddabd6e277e5c03 (patch)
tree17b9536117774b0dfd76969b1b3b0aed82c24117 /src
parent9ed9edcfe9aef3db9306a0d2b7c24f73831b1149 (diff)
downloadvyatta-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')
-rw-r--r--src/commit2.c755
-rw-r--r--src/common/common.c11
-rw-r--r--src/common/common.h53
-rw-r--r--src/common/defs.h72
-rw-r--r--src/common/priority3
-rw-r--r--src/common/unionfs.c1086
-rw-r--r--src/common/unionfs.h49
-rw-r--r--src/dump_session.c166
-rw-r--r--src/exe_action.c122
9 files changed, 2317 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;
+}
+
+
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;
+}