summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/VyattaConfig.pm548
-rw-r--r--scripts/VyattaConfigDOMTree.pm364
-rwxr-xr-xscripts/VyattaConfigLoad.pm340
-rwxr-xr-xscripts/VyattaConfigOutput.pm253
-rwxr-xr-xscripts/VyattaMisc.pm62
-rw-r--r--scripts/VyattaTypeChecker.pm179
-rwxr-xr-xscripts/XorpConfigParser.pm368
-rw-r--r--scripts/system/vyatta_update_login_user.pl172
-rw-r--r--scripts/system/vyatta_update_logrotate.pl55
-rw-r--r--scripts/system/vyatta_update_syslog.pl56
-rwxr-xr-xscripts/vyatta-cli-expand-var.pl64
-rwxr-xr-xscripts/vyatta-config-loader.pl51
-rwxr-xr-xscripts/vyatta-find-type.pl21
-rwxr-xr-xscripts/vyatta-load-config.pl69
-rwxr-xr-xscripts/vyatta-output-config.pl9
-rwxr-xr-xscripts/vyatta-save-config.pl45
-rwxr-xr-xscripts/vyatta-validate-type.pl15
-rwxr-xr-xscripts/xorp_tmpl_tool150
18 files changed, 2821 insertions, 0 deletions
diff --git a/scripts/VyattaConfig.pm b/scripts/VyattaConfig.pm
new file mode 100644
index 0000000..e9a1f97
--- /dev/null
+++ b/scripts/VyattaConfig.pm
@@ -0,0 +1,548 @@
+package VyattaConfig;
+
+use strict;
+
+use VyattaConfigDOMTree;
+
+my %fields = (
+ _changes_only_dir_base => $ENV{VYATTA_CHANGES_ONLY_DIR},
+ _new_config_dir_base => $ENV{VYATTA_TEMP_CONFIG_DIR},
+ _active_dir_base => $ENV{VYATTA_ACTIVE_CONFIGURATION_DIR},
+ _vyatta_template_dir => $ENV{VYATTA_CONFIG_TEMPLATE},
+ _current_dir_level => "/",
+ _level => undef,
+);
+
+sub new {
+ my $that = shift;
+ my $class = ref ($that) || $that;
+ my $self = {
+ %fields,
+ };
+
+ bless $self, $class;
+ return $self;
+}
+
+sub _set_current_dir_level {
+ my ($self) = @_;
+ my $level = $self->{_level};
+
+ $level =~ s/\//%2F/g;
+ $level =~ s/\s+/\//g;
+
+ $self->{_current_dir_level} = "/$level";
+ return $self->{_current_dir_level};
+}
+
+## setLevel("level")
+# if "level" is supplied, set the current level of the hierarchy we are working on
+# return the current level
+sub setLevel {
+ my ($self, $level) = @_;
+
+ $self->{_level} = $level if defined($level);
+ $self->_set_current_dir_level();
+
+ return $self->{_level};
+}
+
+## listNodes("level")
+# return array of all nodes at "level"
+# level is relative
+sub listNodes {
+ my ($self, $path) = @_;
+ my @nodes = ();
+
+ if (defined $path) {
+ $path =~ s/\//%2F/g;
+ $path =~ s/\s+/\//g;
+ $path = $self->{_new_config_dir_base} . $self->{_current_dir_level} . "/" . $path;
+ }
+ else {
+ $path = $self->{_new_config_dir_base} . $self->{_current_dir_level};
+ }
+
+ #print "DEBUG VyattaConfig->listNodes(): path = $path\n";
+ opendir DIR, "$path" or return ();
+ @nodes = grep !/^\./, readdir DIR;
+ closedir DIR;
+
+ my @nodes_modified = ();
+ while (@nodes) {
+ my $tmp = pop (@nodes);
+ $tmp =~ s/\n//g;
+ $tmp =~ s/%2F/\//g;
+ #print "DEBUG VyattaConfig->listNodes(): node = $tmp\n";
+ push @nodes_modified, $tmp;
+ }
+
+ return @nodes_modified;
+}
+
+## listOrigNodes("level")
+# return array of all original nodes (i.e., before any current change; i.e.,
+# in "working") at "level"
+# level is relative
+sub listOrigNodes {
+ my ($self, $path) = @_;
+ my @nodes = ();
+
+ if (defined $path) {
+ $path =~ s/%2F/\//g;
+ $path =~ s/\s+/\//g;
+ $path = $self->{_active_dir_base} . $self->{_current_dir_level} . "/"
+ . $path;
+ }
+ else {
+ $path = $self->{_active_dir_base} . $self->{_current_dir_level};
+ }
+
+ #print "DEBUG VyattaConfig->listNodes(): path = $path\n";
+ opendir DIR, "$path" or return ();
+ @nodes = grep !/^\./, readdir DIR;
+ closedir DIR;
+
+ my @nodes_modified = ();
+ while (@nodes) {
+ my $tmp = pop (@nodes);
+ $tmp =~ s/\n//g;
+ $tmp =~ s/%2F/\//g;
+ #print "DEBUG VyattaConfig->listNodes(): node = $tmp\n";
+ push @nodes_modified, $tmp;
+ }
+
+ return @nodes_modified;
+}
+
+## returnParent("level")
+# return the name of parent node relative to the current hierarchy
+# in this case "level" is set to the parent dir ".. .."
+# for example
+sub returnParent {
+ my ($self, $node) = @_;
+ my $x, my $tmp;
+
+ # split our hierarchy into vars on a stack
+ my @level = split /\s+/, $self->{_level};
+
+ # count the number of parents we need to lose
+ # and then pop 1 less
+ $x = split /\s+/, $node;
+ for ($tmp = 1; $tmp < $x; $tmp++) {
+ pop @level;
+ }
+
+ # return the parent
+ $tmp = pop @level;
+ return $tmp;
+}
+
+## returnValue("node")
+# returns the value of "node" or undef if the node doesn't exist .
+# node is relative
+sub returnValue {
+ my ( $self, $node ) = @_;
+ my $tmp;
+
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g;
+
+ if ( -f "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node/node.val" ) {
+ open FILE, "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node/node.val" || return undef;
+ read FILE, $tmp, 16384;
+ close FILE;
+
+ $tmp =~ s/\n$//;
+ return $tmp;
+ }
+ else {
+ return undef;
+ }
+}
+
+
+## returnOrigValue("node")
+# returns the original value of "node" (i.e., before the current change; i.e.,
+# in "working") or undef if the node doesn't exist.
+# node is relative
+sub returnOrigValue {
+ my ( $self, $node ) = @_;
+ my $tmp;
+
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g;
+ my $filepath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node";
+ if ( -f "$filepath/node.val") {
+ open FILE, "$filepath/node.val" || return undef;
+ read FILE, $tmp, 16384;
+ close FILE;
+
+ $tmp =~ s/\n$//;
+ return $tmp;
+ } else {
+ return undef;
+ }
+}
+
+## returnValues("node")
+# returns an array of all the values of "node", or an empty array if the values do not exist.
+# node is relative
+sub returnValues {
+ my $val = returnValue(@_);
+ my @values = split("\n", $val);
+ return @values;
+}
+
+## returnOrigValues("node")
+# returns an array of all the original values of "node" (i.e., before the
+# current change; i.e., in "working"), or an empty array if the values do not
+# exist.
+# node is relative
+sub returnOrigValues {
+ my $val = returnOrigValue(@_);
+ my @values = split("\n", $val);
+ return @values;
+}
+
+## exists("node")
+# Returns true if the "node" exists.
+sub exists {
+ my ( $self, $node ) = @_;
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g;
+
+ if ( -d "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node" ) {
+ #print "DEBUG: the dir is there\n";
+ return !0;
+ } else {
+ return undef;
+ }
+}
+
+## isDeleted("node")
+# is the "node" deleted. node is relative. returns true or false
+sub isDeleted {
+ my ($self, $node) = @_;
+ my $endnode = undef;
+ my $filepath = undef;
+ my @nodes = ();
+
+ # split the string into an array
+ (@nodes) = split /\s+/, $node;
+
+ # take the last node off the string
+ $endnode = pop @nodes;
+ # and modify it to match the whiteout name
+ $endnode = ".wh.$endnode";
+
+ # setup the path with the rest of the nodes
+ # use the change_dir
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g;
+ $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node";
+
+ # if the file exists, the node was deleted
+ if (-f "$filepath") { return 1; }
+ else { return 0; }
+}
+
+## listDeleted("level")
+# return array of deleted nodes in the "level"
+# "level" defaults to current
+sub listDeleted {
+ my ($self, $node) = @_;
+ my @return = ();
+ my $filepath = undef;
+ my $curpath = undef;
+ my @nodes = ();
+ my @curnodes = ();
+
+ # setup the entire path with the new level
+ # use the change_dir
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g;
+ $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node/";
+
+ $curpath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node/";
+
+ # let's see if the directory exists and find the the whiteout files
+ if (! -d "$filepath") { return undef; }
+ else {
+ opendir DIR, "$filepath" or return undef;
+ @nodes = grep /^\.wh./, readdir DIR;
+ closedir DIR;
+ }
+
+ if (! -d "$curpath") {
+ return undef;
+ } else {
+ opendir DIR, "$curpath" or return undef;
+ @curnodes = grep !/^\./, readdir DIR;
+ closedir DIR;
+ }
+
+ # get rid of the whiteout prefix
+ my $dir_opq = 0;
+ foreach $node (@nodes) {
+ $node =~ s/^\.wh\.(.+)/\1/;
+ $_ = $node;
+ if (! /__dir_opaque/) {
+ push @return, $node;
+ } else {
+ $dir_opq = 1;
+ }
+ }
+
+ if ($dir_opq) {
+ # if this node is "dir_opaque", it has been deleted and re-added.
+ # add all nodes in "active" to the return list (so that they will be
+ # marked "deleted"). note that if a node is also re-added, its status
+ # will be changed after the listDeleted call.
+ push @return, @curnodes;
+ }
+
+ return @return;
+}
+
+## isChanged("node")
+# will check the change_dir to see if the "node" has been changed from a previous
+# value. returns true or false.
+sub isChanged {
+ my ($self, $node) = @_;
+
+ # let's setup the filepath for the change_dir
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g, $node;
+ my $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node";
+
+ # if the node exists in the change dir, it's modified.
+ if (-e "$filepath") { return 1; }
+ else { return 0; }
+}
+
+## isAdded("node")
+# will compare the new_config_dir to the active_dir to see if the "node" has
+# been added. returns true or false.
+sub isAdded {
+ my ($self, $node) = @_;
+
+ #print "DEBUG VyattaConfig->isAdded(): node $node\n";
+ # let's setup the filepath for the modify dir
+ $node =~ s/\//%2F/g;
+ $node =~ s/\s+/\//g, $node;
+ my $filepath = "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node";
+
+ #print "DEBUG VyattaConfig->isAdded(): filepath $filepath\n";
+
+ # if the node doesn't exist in the modify dir, it's not
+ # been added. so short circuit and return false.
+ if (! -e "$filepath") { return 0; }
+
+ # now let's setup the path for the working dir
+ my $filepath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node";
+
+ # if the node is in the active_dir it's not new
+ if (-e "$filepath") { return 0; }
+ else { return 1; }
+}
+
+## listNodeStatus("level")
+# return a hash of the status of nodes at the current config level
+# node name is the hash key. node status is the hash value.
+# node status can be one of deleted, added, changed, or static
+sub listNodeStatus {
+ my ($self, $path) = @_;
+ my @nodes = ();
+ my %nodehash = ();
+ my $node = undef;
+
+ # find deleted nodes first
+ @nodes = $self->listDeleted("$path");
+ foreach $node (@nodes) {
+ if ($node =~ /.+/) { $nodehash{$node} = "deleted" };
+ }
+
+ @nodes = ();
+ @nodes = $self->listNodes("$path");
+ foreach $node (@nodes) {
+ if ($node =~ /.+/) {
+ #print "DEBUG VyattaConfig->listNodeStatus(): node $node\n";
+ if ($self->isAdded("$path $node")) { $nodehash{$node} = "added"; }
+ elsif ($self->isChanged("$path $node")) { $nodehash{$node} = "changed"; }
+ elsif ($self->isDeleted("$path $node")) { $nodehash{$node} = "deleted"; }
+ else { $nodehash{$node} = "static"; }
+ }
+ }
+
+ return %nodehash;
+}
+
+############ DOM Tree ################
+
+#Create active DOM Tree
+sub createActiveDOMTree {
+
+ my $self = shift;
+
+ my $tree = new VyattaConfigDOMTree($self->{_active_dir_base} . $self->{_current_dir_level},"active");
+
+ return $tree;
+}
+
+#Create changes only DOM Tree
+sub createChangesOnlyDOMTree {
+
+ my $self = shift;
+
+ my $tree = new VyattaConfigDOMTree($self->{_changes_only_dir_base} . $self->{_current_dir_level},
+ "changes_only");
+
+ return $tree;
+}
+
+#Create new config DOM Tree
+sub createNewConfigDOMTree {
+
+ my $self = shift;
+
+ my $tree = new VyattaConfigDOMTree($self->{_new_config_dir_base} . $self->{_current_dir_level},
+ "new_config");
+
+ return $tree;
+}
+
+
+###### functions for templates ######
+
+# $1: array representing the config path (note that path must be present
+# in current config)
+sub getTmplPath {
+ my $self = shift;
+ my @cfg_path = @{$_[0]};
+ my $tpath = $self->{_vyatta_template_dir};
+ for my $p (@cfg_path) {
+ if (-d "$tpath/$p") {
+ $tpath .= "/$p";
+ next;
+ }
+ if (-d "$tpath/node.tag") {
+ $tpath .= "/node.tag";
+ next;
+ }
+ # the path is not valid! not supposed to happen!
+ die "Node path \"" . (join ' ', @cfg_path) . "\" is not valid";
+ }
+ return $tpath
+}
+
+sub isTagNode {
+ my $self = shift;
+ my $cfg_path_ref = shift;
+ my $tpath = $self->getTmplPath($cfg_path_ref);
+ if (-d "$tpath/node.tag") {
+ return 1;
+ }
+ return 0;
+}
+
+sub hasTmplChildren {
+ my $self = shift;
+ my $cfg_path_ref = shift;
+ my $tpath = $self->getTmplPath($cfg_path_ref);
+ opendir(TDIR, $tpath) or return 0;
+ my @tchildren = grep !/^node\.def$/, (grep !/^\./, (readdir TDIR));
+ closedir TDIR;
+ if (scalar(@tchildren) > 0) {
+ return 1;
+ }
+ return 0;
+}
+
+# returns ($is_multi, $is_text)
+sub parseTmpl {
+ my $self = shift;
+ my $cfg_path_ref = shift;
+ my ($is_multi, $is_text) = (0, 0);
+ my $tpath = $self->getTmplPath($cfg_path_ref);
+ if (! -r "$tpath/node.def") {
+ return ($is_multi, $is_text);
+ }
+ open(TMPL, "<$tpath/node.def") or return ($is_multi, $is_text);
+ foreach (<TMPL>) {
+ if (/^multi:/) {
+ $is_multi = 1;
+ }
+ if (/^type:\s+txt\s*$/) {
+ $is_text = 1;
+ }
+ }
+ close TMPL;
+ return ($is_multi, $is_text);
+}
+
+###### misc functions ######
+
+# compare two value lists and return "deleted" and "added" lists.
+# since this is for multi-value nodes, there is no "changed" (if a value's
+# ordering changed, it is deleted then added).
+# $0: \@orig_values
+# $1: \@new_values
+sub compareValueLists {
+ my $self = shift;
+ my @ovals = @{$_[0]};
+ my @nvals = @{$_[1]};
+ my %comp_hash = (
+ 'deleted' => [],
+ 'added' => [],
+ );
+ my $idx = 0;
+ my %ohash = map { $_ => ($idx++) } @ovals;
+ $idx = 0;
+ my %nhash = map { $_ => ($idx++) } @nvals;
+ my $min_changed_idx = 2**31;
+ my %dhash = ();
+ foreach (@ovals) {
+ if (!defined($nhash{$_})) {
+ push @{$comp_hash{'deleted'}}, $_;
+ $dhash{$_} = 1;
+ if ($ohash{$_} < $min_changed_idx) {
+ $min_changed_idx = $ohash{$_};
+ }
+ }
+ }
+ foreach (@nvals) {
+ if (defined($ohash{$_})) {
+ if ($ohash{$_} != $nhash{$_}) {
+ if ($ohash{$_} < $min_changed_idx) {
+ $min_changed_idx = $ohash{$_};
+ }
+ }
+ }
+ }
+ foreach (@nvals) {
+ if (defined($ohash{$_})) {
+ if ($ohash{$_} != $nhash{$_}) {
+ if (!defined($dhash{$_})) {
+ push @{$comp_hash{'deleted'}}, $_;
+ $dhash{$_} = 1;
+ }
+ push @{$comp_hash{'added'}}, $_;
+ } elsif ($ohash{$_} >= $min_changed_idx) {
+ # ordering unchanged, but something before it is changed.
+ if (!defined($dhash{$_})) {
+ push @{$comp_hash{'deleted'}}, $_;
+ $dhash{$_} = 1;
+ }
+ push @{$comp_hash{'added'}}, $_;
+ } else {
+ # this is before any changed value. do nothing.
+ }
+ } else {
+ push @{$comp_hash{'added'}}, $_;
+ }
+ }
+ return %comp_hash;
+}
+
+
diff --git a/scripts/VyattaConfigDOMTree.pm b/scripts/VyattaConfigDOMTree.pm
new file mode 100644
index 0000000..d951202
--- /dev/null
+++ b/scripts/VyattaConfigDOMTree.pm
@@ -0,0 +1,364 @@
+#
+# Module: serial
+#
+# **** License ****
+# Version: VPL 1.0
+#
+# The contents of this file are subject to the Vyatta Public License
+# Version 1.0 ("License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://www.vyatta.com/vpl
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# This code was originally developed by Vyatta, Inc.
+# Portions created by Vyatta are Copyright (C) 2005, 2006, 2007 Vyatta, Inc.
+# All Rights Reserved.
+#
+# Author: Oleg Moskalenko
+# Date: 2007
+# Description:
+#
+# **** End License ****
+#
+#
+
+package VyattaConfigDOMTree;
+
+use strict;
+
+my %fields = (
+ _dir => undef,
+ _name => undef,
+ _value => undef,
+ _subnodes => undef
+ );
+
+sub new {
+
+ my $that = shift;
+ my $dir = shift;
+ my $name = shift;
+
+ my $class = ref ($that) || $that;
+
+ my $self = {
+ %fields
+ };
+
+ bless $self, $class;
+
+ $self->{_dir} = $dir;
+ $self->{_name} = $name;
+
+ return $self->_construct_dom_tree();
+}
+
+#Simple DOM Tree iteration and screen output
+#$1 - left screen offset (optional)
+sub print {
+
+ my $self = shift;
+ my $level = shift;
+
+ my $tree = $self;
+
+ if(!(defined $level)) {
+ $level="";
+ }
+
+ if(defined $tree) {
+
+ print("$level name=",$tree->getNodeName(),"\n");
+
+ my $value = $tree->getNodeValue();
+
+ if(defined $value) {
+
+ print("$level value=$value\n");
+
+ }
+
+ my @subnodes = $tree->getSubNodes();
+
+ while(@subnodes) {
+
+ my $subnode = shift @subnodes;
+ $subnode->print($level . " ");
+ }
+ }
+}
+
+#Return value of the tree node
+sub getNodeValue {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ if(defined $tree) {
+
+ $ret = $tree->{_value};
+ }
+
+ return $ret;
+}
+
+#Return value of the tree node.
+#If the value is nor defined, return empty string.
+sub getNodeValueAsString {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ if(defined $tree) {
+
+ $ret = $tree->getNodeValue();
+ }
+
+ if(!defined $ret) {
+ $ret = "";
+ }
+
+ return $ret;
+}
+
+#Return name of the tree node
+sub getNodeName {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ if(defined $tree) {
+
+ $ret = $tree->{_name};
+ }
+
+ return $ret;
+}
+
+#Return array of subnodes of the tree node
+sub getSubNodes {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my @ret = ();
+
+ if(defined $tree) {
+
+ my $subnodes = $tree->{_subnodes};
+
+ if(defined $subnodes) {
+
+ @ret = values %{$subnodes};
+
+ }
+ }
+
+ return @ret;
+}
+
+sub isLeafNode {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret=undef;
+
+ if(defined $tree) {
+
+ if(defined $tree->{_value}) {
+
+ if(! defined $tree->{_subnodes}) {
+
+ $ret="true";
+ }
+ }
+ }
+
+ return $ret;
+}
+
+#Return subtree of the tree according to the path list
+#$1, $2, ... - path to the subtree
+sub getSubNode {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ while(@_ && $tree) {
+
+ my $subnode = shift (@_);
+
+ my $subnodes = $tree->{_subnodes};
+
+ if(defined $subnodes) {
+
+ $tree = $subnodes->{$subnode};
+
+ } else {
+
+ $tree = undef;
+
+ }
+ }
+
+ $ret=$tree;
+
+ return $ret;
+}
+
+#Return value of the subnode of the tree according to the path list
+#$1, $2, ... - path to the subtree
+sub getSubNodeValue {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ if(defined $tree) {
+
+ my $node = $tree->getSubNode(@_);
+
+ if(defined $node) {
+
+ $ret=$node->getNodeValue();
+ }
+ }
+
+ return $ret;
+}
+
+#Return value of the subnode of the tree according to the path list.
+#If the value is not defined, return empty string.
+#$1, $2, ... - path to the subtree
+sub getSubNodeValueAsString {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ if(defined $tree) {
+
+ my $node = $tree->getSubNode(@_);
+
+ if(defined $node) {
+
+ $ret=$node->getNodeValue();
+ }
+ }
+
+ if(! defined $ret) {
+ $ret = "";
+ }
+
+ return $ret;
+}
+
+#Check if there is a subnode with the specified path.
+#$1, $2, ... - path to the subtree
+sub subNodeExist {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = undef;
+
+ if(defined $tree) {
+
+ my $node = $tree->getSubNode(@_);
+
+ if(defined $node) {
+
+ $ret="true";
+ }
+ }
+
+ return $ret;
+}
+
+#Return of the children of the node
+#$1, $2, ... - path to the subtree
+sub getSubNodesNumber {
+
+ my $self = shift;
+ my $tree = $self;
+
+ my $ret = 0;
+
+ if(defined $tree) {
+
+ my $node = $tree->getSubNode(@_);
+
+ if(defined $node) {
+
+ my @subs = $node->getSubNodes();
+
+ if(defined @subs) {
+ $ret = $#subs + 1;
+ }
+ }
+ }
+
+ return $ret;
+}
+
+#private method: costruct DOM Tree according to the absolute path provided
+sub _construct_dom_tree {
+
+ my $self = shift;
+
+ my $subnodesNum=0;
+ my $valuePresent=0;
+
+ if(!(defined $self)) {return undef;}
+
+ opendir DIR, $self->{_dir} or return undef;
+ my @entries = grep !/^\./, readdir DIR;
+ closedir DIR;
+
+ while(@entries) {
+
+ my $entry = shift @entries;
+
+ if($entry) {
+ my $fn = $self->{_dir} . "/" . $entry;
+ if( -f $fn) {
+ if($entry eq "node.val") {
+ my $value=`cat $fn`;
+ while(chomp $value) {};
+ $self->{_value} = $value;
+ $valuePresent++;
+ }
+ } elsif (-d $fn) {
+ my $subnode = new VyattaConfigDOMTree($fn,$entry);
+ if(defined $subnode) {
+ if(! defined $self->{_subnodes} ) {
+ $self->{_subnodes} = {};
+ }
+ $self->{_subnodes}->{$entry} = $subnode;
+ $subnodesNum++;
+ }
+ }
+ }
+ }
+
+ if($valuePresent<1 && $subnodesNum<1) {
+ return undef;
+ }
+
+ return $self;
+}
diff --git a/scripts/VyattaConfigLoad.pm b/scripts/VyattaConfigLoad.pm
new file mode 100755
index 0000000..eae2946
--- /dev/null
+++ b/scripts/VyattaConfigLoad.pm
@@ -0,0 +1,340 @@
+# Perl module for loading configuration.
+package VyattaConfigLoad;
+
+use strict;
+use sort 'stable';
+use lib "/opt/vyatta/share/perl5/";
+use XorpConfigParser;
+use VyattaConfig;
+
+# configuration ordering. higher rank configured before lower rank.
+my $default_rank = 0;
+my %config_rank = (
+ 'interfaces' => 100,
+ 'system' => 90,
+ );
+
+my @all_nodes = ();
+my @all_naked_nodes = ();
+
+sub get_config_rank {
+ # longest prefix match
+ my @path = @_;
+ while ((scalar @path) > 0) {
+ my $path_str = join ' ', @path;
+ if (defined($config_rank{$path_str})) {
+ return ($config_rank{$path_str});
+ }
+ pop @path;
+ }
+ return $default_rank;
+}
+
+sub applySingleQuote {
+ my @return = ();
+ foreach (@_) {
+ # change all single quotes to "'\''" since we're going to single-quote
+ # every component of the command
+ if (/^'(.*)'$/) {
+ $_ = $1;
+ }
+ $_ =~ s/'/'\\''/g;
+ # single-quote every component of the command
+ if (/^'.*'$/) {
+ push @return, $_;
+ } elsif (/^"(.*)"$/) {
+ push @return, "'$1'";
+ } else {
+ push @return, "'$_'";
+ }
+ }
+ return @return;
+}
+
+sub enumerate_branch {
+ my $cur_node = shift;
+ my @cur_path = @_;
+ # name not defined at root level
+ if (defined($cur_node->{'name'})) {
+ my $name = $cur_node->{'name'};
+ if ($name =~ /^\s*(\S+)\s+(\S.*)$/) {
+ push @cur_path, ($1, $2);
+ } else {
+ push @cur_path, $name;
+ }
+ }
+ my $terminal = 0;
+ if (!defined($cur_node->{'children'})) {
+ $terminal = 1;
+ } else {
+ foreach (@{$cur_node->{'children'}}) {
+ if (defined($_->{'name'})) {
+ enumerate_branch($_, @cur_path);
+ $terminal = 0;
+ }
+ }
+ }
+ if ($terminal) {
+ my $val = $cur_node->{'value'};
+ if (defined($val)) {
+ push @cur_path, $val;
+ }
+ push @all_naked_nodes, [ @cur_path ];
+ my @qpath = applySingleQuote(@cur_path);
+ push @all_nodes, [\@qpath, get_config_rank(@cur_path)];
+ }
+}
+
+# $0: config file to load
+# return: list of all config statement sorted by rank
+sub getStartupConfigStatements {
+ # clean up the lists first
+ @all_nodes = ();
+ @all_naked_nodes = ();
+
+ my $load_cfg = shift;
+ if (!defined($load_cfg)) {
+ return ();
+ }
+
+ my $xcp = new XorpConfigParser();
+ $xcp->parse($load_cfg);
+ my $root = $xcp->get_node( () );
+ if (!defined($root)) {
+ return ();
+ }
+ enumerate_branch($root, ( ));
+
+ @all_nodes = sort { ${$b}[1] <=> ${$a}[1] } @all_nodes;
+ return @all_nodes;
+}
+
+my %node_order = ();
+
+# $0: ref of list of parsed naked statements.
+# return: hash containing the config hierarchy.
+sub generateHierarchy {
+ my @node_list = @{$_[0]};
+ my %hash = ();
+ %node_order = ();
+ my $order = 0;
+ foreach my $node (@node_list) {
+ my @path = @{$node};
+ my $path_str = join ' ', @path;
+ $node_order{$path_str} = $order;
+ $order++;
+ my $cur_ref = \%hash;
+ foreach (@path) {
+ if (!defined($cur_ref->{$_})) {
+ $cur_ref->{$_} = { };
+ }
+ $cur_ref = $cur_ref->{$_};
+ }
+ }
+ return %hash;
+}
+
+# $0: config file to load.
+# return: hash containing the config hierarchy.
+sub loadConfigHierarchy {
+ # clean up the lists first
+ @all_nodes = ();
+ @all_naked_nodes = ();
+
+ my $load_cfg = shift;
+ if (!defined($load_cfg)) {
+ return ();
+ }
+
+ my $xcp = new XorpConfigParser();
+ $xcp->parse($load_cfg);
+ my $root = $xcp->get_node( () );
+ if (!defined($root)) {
+ return ();
+ }
+ enumerate_branch($root, ( ));
+
+ return generateHierarchy(\@all_naked_nodes);
+}
+
+# $0: ref of hierarchy root.
+# $1: display prefix.
+sub printHierarchy {
+ my $cur_ref = shift;
+ my $prefix = shift;
+ foreach (sort keys %{$cur_ref}) {
+ print "$prefix$_";
+ if (scalar(keys %{$cur_ref->{$_}}) == 0) {
+ print " (terminal)\n";
+ next;
+ } else {
+ print "\n";
+ }
+ printHierarchy($cur_ref->{$_}, "$prefix ");
+ }
+}
+
+# $0: hash ref representing a "multi:" node.
+# $1: array ref representing current config path.
+# returns the list of node values sorted by the original order.
+sub getSortedMultiValues {
+ my $nref = $_[0];
+ my @npath = @{$_[1]};
+ my $path_str = join ' ', @npath;
+ my @list = ();
+ foreach (keys %{$nref}) {
+ my $key = "$path_str $_";
+ push @list, [ $_, $node_order{$key} ];
+ }
+ my @slist = sort { ${$a}[1] <=> ${$b}[1] } @list;
+ @slist = map { ${$_}[0] } @slist;
+ return @slist;
+}
+
+my $active_cfg = undef;
+my $new_cfg_ref = undef;
+
+my @delete_list = ();
+
+# find specified node's values in active config that have been deleted from
+# new config.
+# $0: hash ref at the current hierarchy level (new config)
+# $1: array ref representing current config path (active config)
+sub findDeletedValues {
+ my $new_ref = $_[0];
+ my @active_path = @{$_[1]};
+ my ($is_multi, $is_text) = $active_cfg->parseTmpl(\@active_path);
+ $active_cfg->setLevel(join ' ', @active_path);
+ if ($is_multi) {
+ # for "multi:" nodes, need to sort the values by the original order.
+ my @nvals = getSortedMultiValues($new_ref, \@active_path);
+ if ($is_text) {
+ @nvals = map { /^"(.*)"$/; $1; } @nvals;
+ }
+ my @ovals = $active_cfg->returnOrigValues('');
+ my %comp_hash = $active_cfg->compareValueLists(\@ovals, \@nvals);
+ foreach (@{$comp_hash{'deleted'}}) {
+ my @plist = applySingleQuote(@active_path, $_);
+ push @delete_list, [\@plist, get_config_rank(@active_path, $_)];
+ }
+ } else {
+ # do nothing. if a single-value leaf node is deleted, it should have
+ # been detected at the previous level. since we are already at node.val,
+ # it can only be "added" or "changed", handled later.
+ }
+}
+
+# find nodes in active config that have been deleted from new config.
+# $0: hash ref at the current hierarchy level (new config)
+# $1: array ref representing current config path (active config)
+sub findDeletedNodes {
+ my $new_ref = $_[0];
+ my @active_path = @{$_[1]};
+ $active_cfg->setLevel(join ' ', @active_path);
+ my @active_nodes = $active_cfg->listOrigNodes();
+ foreach (@active_nodes) {
+ if ($_ eq 'node.val') {
+ findDeletedValues($new_ref, \@active_path);
+ next;
+ }
+ if (!defined($new_ref->{$_})) {
+ my @plist = applySingleQuote(@active_path, $_);
+ push @delete_list, [\@plist, get_config_rank(@active_path, $_)];
+ } else {
+ findDeletedNodes($new_ref->{$_}, [ @active_path, $_ ]);
+ }
+ }
+}
+
+my @set_list = ();
+
+# find specified node's values in active config that are set
+# (added or changed).
+# $0: hash ref at the current hierarchy level (new config)
+# $1: array ref representing current config path (active config)
+sub findSetValues {
+ my $new_ref = $_[0];
+ my @active_path = @{$_[1]};
+ my ($is_multi, $is_text) = $active_cfg->parseTmpl(\@active_path);
+ $active_cfg->setLevel(join ' ', @active_path);
+ if ($is_multi) {
+ # for "multi:" nodes, need to sort the values by the original order.
+ my @nvals = getSortedMultiValues($new_ref, \@active_path);
+ if ($is_text) {
+ @nvals = map { /^"(.*)"$/; $1; } @nvals;
+ }
+ my @ovals = $active_cfg->returnOrigValues('');
+ my %comp_hash = $active_cfg->compareValueLists(\@ovals, \@nvals);
+ foreach (@{$comp_hash{'added'}}) {
+ my @plist = applySingleQuote(@active_path, $_);
+ push @set_list, [\@plist, get_config_rank(@active_path, $_)];
+ }
+ } else {
+ my @nvals = keys %{$new_ref};
+ my $nval = $nvals[0];
+ if ($is_text) {
+ $nval =~ s/^"(.*)"$/$1/;
+ }
+ my $oval = $active_cfg->returnOrigValue('');
+ if (!defined($oval) || ($nval ne $oval)) {
+ my @plist = applySingleQuote(@active_path, $nval);
+ push @set_list, [\@plist, get_config_rank(@active_path, $nval)];
+ }
+ }
+}
+
+# find nodes in new config that are set (added or changed).
+# $0: hash ref at the current hierarchy level (new config)
+# $1: array ref representing current config path (active config)
+sub findSetNodes {
+ my $new_ref = $_[0];
+ my @active_path = @{$_[1]};
+ $active_cfg->setLevel(join ' ', @active_path);
+ my @active_nodes = $active_cfg->listOrigNodes();
+ my %active_hash = map { $_ => 1 } @active_nodes;
+ if (defined($active_hash{'node.val'})) {
+ # we are at a leaf node.
+ findSetValues($new_ref, \@active_path);
+ return;
+ }
+ foreach (sort keys %{$new_ref}) {
+ if (scalar(keys %{$new_ref->{$_}}) == 0) {
+ # we are at a non-value leaf node.
+ # check if we need to add this node.
+ if (!defined($active_hash{$_})) {
+ my @plist = applySingleQuote(@active_path, $_);
+ push @set_list, [\@plist, get_config_rank(@active_path, $_)];
+ } else {
+ # node already present. do nothing.
+ }
+ next;
+ }
+ # we recur regardless of whether it's in active. all changes will be
+ # handled when we reach leaf nodes (above).
+ findSetNodes($new_ref->{$_}, [ @active_path, $_ ]);
+ }
+}
+
+# compare the current active config with the specified hierarchy and return
+# the "diff".
+# $0: hash ref of config hierarchy.
+# return: hash containing the diff.
+sub getConfigDiff {
+ $active_cfg = new VyattaConfig;
+ $new_cfg_ref = shift;
+ @set_list = ();
+ @delete_list = ();
+ findDeletedNodes($new_cfg_ref, [ ]);
+ findSetNodes($new_cfg_ref, [ ]);
+ # don't really need to sort the lists by rank since we have to commit
+ # everything together anyway.
+ @delete_list = sort { ${$a}[1] <=> ${$b}[1] } @delete_list;
+ @set_list = sort { ${$b}[1] <=> ${$a}[1] } @set_list;
+ my %diff = (
+ 'delete' => \@delete_list,
+ 'set' => \@set_list,
+ );
+ return %diff;
+}
+
+1;
diff --git a/scripts/VyattaConfigOutput.pm b/scripts/VyattaConfigOutput.pm
new file mode 100755
index 0000000..874ed55
--- /dev/null
+++ b/scripts/VyattaConfigOutput.pm
@@ -0,0 +1,253 @@
+# Perl module for generating output of the configuration.
+#
+# outputNewConfig()
+# prints the "new" config, i.e., the active config with any un-committed
+# changes. 'diff' notation is also generated to indicate the changes.
+#
+# outputActiveConfig()
+# prints the "active" config. suitable for "saving", for example.
+
+package VyattaConfigOutput;
+
+use strict;
+use lib '/opt/vyatta/share/perl5/';
+use VyattaConfig;
+
+my $config = undef;
+
+# $0: array ref for path
+# $1: display prefix
+# $2: node name
+# $3: simple show (if defined, don't show diff prefix. used for "don't show as
+# deleted" from displayDeletedOrigChildren.)
+sub displayValues {
+ my @cur_path = @{$_[0]};
+ my $prefix = $_[1];
+ my $name = $_[2];
+ my $simple_show = $_[3];
+ my ($is_multi, $is_text) = $config->parseTmpl(\@cur_path);
+ $config->setLevel(join ' ', @cur_path);
+ if ($is_multi) {
+ my @ovals = $config->returnOrigValues('');
+ my @nvals = $config->returnValues('');
+ if ($is_text) {
+ @ovals = map { "\"$_\""; } @ovals;
+ @nvals = map { "\"$_\""; } @nvals;
+ }
+ my $idx = 0;
+ my %ohash = map { $_ => ($idx++) } @ovals;
+ $idx = 0;
+ my %nhash = map { $_ => ($idx++) } @nvals;
+ my @dlist = map { if (!defined($nhash{$_})) { $_; } else { undef; } }
+ @ovals;
+ if (defined($simple_show)) {
+ foreach my $oval (@ovals) {
+ print "$prefix$name $oval\n";
+ }
+ return;
+ }
+ foreach my $del (@dlist) {
+ if (defined($del)) {
+ print "-$prefix$name $del\n";
+ }
+ }
+ foreach my $nval (@nvals) {
+ my $diff = '+';
+ if (defined($ohash{$nval})) {
+ if ($ohash{$nval} != $nhash{$nval}) {
+ $diff = '>';
+ } else {
+ $diff = ' ';
+ }
+ }
+ print "$diff$prefix$name $nval\n";
+ }
+ } else {
+ my $oval = $config->returnOrigValue('');
+ my $nval = $config->returnValue('');
+ if ($is_text) {
+ if (defined($oval)) {
+ $oval = "\"$oval\"";
+ }
+ if (defined($nval)) {
+ $nval = "\"$nval\"";
+ }
+ }
+ if (defined($simple_show)) {
+ print "$prefix$name: $oval\n";
+ return;
+ }
+ my $value = $nval;
+ my $diff = ' ';
+ if (!defined($oval) && defined($nval)) {
+ $diff = '+';
+ } elsif (!defined($nval) && defined($oval)) {
+ $diff = '-';
+ $value = $oval;
+ } else {
+ # both must be defined
+ if ($oval ne $nval) {
+ $diff = '>';
+ }
+ }
+ print "$diff$prefix$name: $value\n";
+ }
+}
+
+# $0: array ref for path
+# $1: display prefix
+# $2: don't show as deleted? (if defined, config is shown as normal instead of
+# deleted.)
+sub displayDeletedOrigChildren {
+ my @cur_path = @{$_[0]};
+ my $prefix = $_[1];
+ my $dont_show_as_deleted = $_[2];
+ my $dprefix = '-';
+ if (defined($dont_show_as_deleted)) {
+ $dprefix = '';
+ }
+ $config->setLevel('');
+ my @children = $config->listOrigNodes(join ' ', @cur_path);
+ for my $child (sort @children) {
+ if ($child eq 'node.val') {
+ # should not happen!
+ next;
+ }
+ my $is_tag = $config->isTagNode([ @cur_path, $child ]);
+ $config->setLevel(join ' ', (@cur_path, $child));
+ my @cnames = sort $config->listOrigNodes();
+ if ($#cnames == 0 && $cnames[0] eq 'node.val') {
+ displayValues([ @cur_path, $child ], $prefix, $child,
+ $dont_show_as_deleted);
+ } elsif (scalar($#cnames) >= 0) {
+ if ($is_tag) {
+ foreach my $cname (@cnames) {
+ if ($cname eq 'node.val') {
+ # should not happen
+ next;
+ }
+ print "$dprefix$prefix$child $cname {\n";
+ displayDeletedOrigChildren([ @cur_path, $child, $cname ],
+ "$prefix ", $dont_show_as_deleted);
+ print "$dprefix$prefix}\n";
+ }
+ } else {
+ print "$dprefix$prefix$child {\n";
+ displayDeletedOrigChildren([ @cur_path, $child ], "$prefix ",
+ $dont_show_as_deleted);
+ print "$dprefix$prefix}\n";
+ }
+ } else {
+ my $has_tmpl_children = $config->hasTmplChildren([ @cur_path, $child ]);
+ print "$dprefix$prefix$child"
+ . ($has_tmpl_children ? " {\n$dprefix$prefix}\n" : "\n");
+ }
+ }
+}
+
+# $0: hash ref for children status
+# $1: array ref for path
+# $2: display prefix
+sub displayChildren {
+ my %child_hash = %{$_[0]};
+ my @cur_path = @{$_[1]};
+ my $prefix = $_[2];
+ for my $child (sort (keys %child_hash)) {
+ if ($child eq 'node.val') {
+ # should not happen!
+ next;
+ }
+ my ($diff, $vdiff) = (' ', ' ');
+ if ($child_hash{$child} eq 'added') {
+ $diff = '+';
+ $vdiff = '+';
+ } elsif ($child_hash{$child} eq 'deleted') {
+ $diff = '-';
+ $vdiff = '-';
+ } elsif ($child_hash{$child} eq 'changed') {
+ $vdiff = '>';
+ }
+ my $is_tag = $config->isTagNode([ @cur_path, $child ]);
+ $config->setLevel(join ' ', (@cur_path, $child));
+ my %cnodes = $config->listNodeStatus();
+ my @cnames = sort keys %cnodes;
+ if ($#cnames == 0 && $cnames[0] eq 'node.val') {
+ displayValues([ @cur_path, $child ], $prefix, $child);
+ } elsif (scalar($#cnames) >= 0) {
+ if ($is_tag) {
+ foreach my $cname (@cnames) {
+ if ($cname eq 'node.val') {
+ # should not happen
+ next;
+ }
+ my $tdiff = ' ';
+ if ($cnodes{$cname} eq 'deleted') {
+ $tdiff = '-';
+ } elsif ($cnodes{$cname} eq 'added') {
+ $tdiff = '+';
+ }
+ print "$tdiff$prefix$child $cname {\n";
+ if ($cnodes{$cname} eq 'deleted') {
+ displayDeletedOrigChildren([ @cur_path, $child, $cname ],
+ "$prefix ");
+ } else {
+ $config->setLevel(join ' ', (@cur_path, $child, $cname));
+ my %ccnodes = $config->listNodeStatus();
+ displayChildren(\%ccnodes, [ @cur_path, $child, $cname ],
+ "$prefix ");
+ }
+ print "$tdiff$prefix}\n";
+ }
+ } else {
+ print "$diff$prefix$child {\n";
+ if ($child_hash{$child} eq 'deleted') {
+ # this should not happen
+ displayDeletedOrigChildren([ @cur_path, $child ], "$prefix ");
+ } else {
+ displayChildren(\%cnodes, [ @cur_path, $child ], "$prefix ");
+ }
+ print "$diff$prefix}\n";
+ }
+ } else {
+ if ($child_hash{$child} eq 'deleted') {
+ $config->setLevel('');
+ my @onodes = $config->listOrigNodes(join ' ', (@cur_path, $child));
+ if ($#onodes == 0 && $onodes[0] eq 'node.val') {
+ displayValues([ @cur_path, $child ], $prefix, $child);
+ } else {
+ print "$diff$prefix$child {\n";
+ displayDeletedOrigChildren([ @cur_path, $child ], "$prefix ");
+ print "$diff$prefix}\n";
+ }
+ } else {
+ my $has_tmpl_children
+ = $config->hasTmplChildren([ @cur_path, $child ]);
+ print "$diff$prefix$child"
+ . ($has_tmpl_children ? " {\n$diff$prefix}\n" : "\n");
+ }
+ }
+ }
+}
+
+# @ARGV: represents the 'root' path. the output starts at this point under
+# the new config.
+sub outputNewConfig {
+ $config = new VyattaConfig;
+ $config->setLevel(join ' ', @_);
+ my %rnodes = $config->listNodeStatus();
+ if (scalar(keys %rnodes) > 0) {
+ displayChildren(\%rnodes, [ @_ ], '');
+ } else {
+ print "Current configuration is empty\n";
+ }
+}
+
+# @ARGV: represents the 'root' path. the output starts at this point under
+# the active config.
+sub outputActiveConfig {
+ $config = new VyattaConfig;
+ $config->setLevel(join ' ', @_);
+ displayDeletedOrigChildren([ @_ ], '', 1);
+}
+
+1;
diff --git a/scripts/VyattaMisc.pm b/scripts/VyattaMisc.pm
new file mode 100755
index 0000000..61c646b
--- /dev/null
+++ b/scripts/VyattaMisc.pm
@@ -0,0 +1,62 @@
+package VyattaMisc;
+require Exporter;
+@ISA = qw(Exporter);
+@EXPORT = qw(getNetAddIP, isIpAddress);
+@EXPORT_OK = qw(getNetAddIP, isIpAddress);
+
+use strict;
+
+sub getNetAddrIP {
+ my ($interface);
+ ($interface) = @_;
+
+ if ($interface eq '') {
+ print STDERR "Error: No interface specified.\n";
+ return undef;
+ }
+
+
+ my $ifconfig_out = `ifconfig $interface`;
+ $ifconfig_out =~ /inet addr:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
+ my $ip = $1;
+ if ($ip eq '') {
+ print STDERR "Error: Unable to determine IP address for interface \'$interface\'.\n";
+ return undef;
+ }
+
+
+ $ifconfig_out =~ /Mask:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/;
+ my $netmask = $1;
+ if ($netmask eq '') {
+ print STDERR "Error: Unable to determine netmask for interface \'$interface\'.\n";
+ return undef;
+ }
+
+ use NetAddr::IP; # This library is available via libnetaddr-ip-perl.deb
+ my $naip = new NetAddr::IP($ip, $netmask);
+ return $naip;
+}
+
+sub isIpAddress {
+ my $ip = shift;
+
+ $_ = $ip;
+ if ( ! /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
+ return 0;
+ }
+ else {
+ my @ips = split /\./, $ip;
+ my $octet = 0;
+ my $counter = 0;
+
+ foreach $octet (@ips) {
+ if (($octet < 0) || ($octet > 255)) { return 0; }
+ if (($counter == 0) && ($octet < 1)) { return 0; }
+ $counter++;
+ }
+ }
+
+ return 1;
+}
+
+return 1;
diff --git a/scripts/VyattaTypeChecker.pm b/scripts/VyattaTypeChecker.pm
new file mode 100644
index 0000000..451be52
--- /dev/null
+++ b/scripts/VyattaTypeChecker.pm
@@ -0,0 +1,179 @@
+# Perl module for type validation.
+# Usage 1: validate a value of a specific type.
+# use VyattaTypeChecker;
+# ...
+# if (VyattaTypeChecker::validateType('ipv4', '1.1.1.1')) {
+# # valid
+# ...
+# } else {
+# # not valie
+# ...
+# }
+#
+# Usage 2: find the type of a value (from a list of candidates), returns
+# undef if the value is not valid for any of the candidates.
+# $valtype = VyattaTypeChecker::findType('1.1.1.1', 'ipv4', 'ipv6');
+# if (!defined($valtype)) {
+# # neither ipv4 nor ipv6
+# ...
+# } else {
+# if ($valtype eq 'ipv4') {
+# ...
+# } else {
+# ...
+# }
+# }
+
+package VyattaTypeChecker;
+
+use strict;
+
+my %type_handler = (
+ 'ipv4' => \&validate_ipv4,
+ 'ipv4net' => \&validate_ipv4net,
+ 'ipv4_negate' => \&validate_ipv4_negate,
+ 'ipv4net_negate' => \&validate_ipv4net_negate,
+ 'protocol' => \&validate_protocol,
+ 'protocol_negate' => \&validate_protocol_negate,
+ 'macaddr' => \&validate_macaddr,
+ 'macaddr_negate' => \&validate_macaddr_negate,
+ 'ipv6' => \&validate_ipv6,
+ );
+
+sub validate_ipv4 {
+ $_ = shift;
+ return 0 if (!/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
+ return 0 if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255);
+ return 1;
+}
+
+sub validate_ipv4net {
+ $_ = shift;
+ return 0 if (!/^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/);
+ return 0 if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255 || $5 > 32);
+ return 1;
+}
+
+sub validate_ipv4_negate {
+ my $value = shift;
+ if ($value =~ m/^\!(.*)$/) {
+ $value = $1;
+ }
+ return validate_ipv4($value);
+}
+
+sub validate_ipv4net_negate {
+ my $value = shift;
+ if ($value =~ m/^\!(.*)$/) {
+ $value = $1;
+ }
+ return validate_ipv4net($value);
+}
+
+sub validate_protocol {
+ my $value = shift;
+ $value = lc $value;
+ return 1 if ($value eq 'all');
+ if (!open(IN, "</etc/protocols")) {
+ print "can't open /etc/protocols";
+ return 0;
+ }
+ my $ret = 0;
+ while (<IN>) {
+ s/^([^#]*)#.*$/$1/;
+ if ((/^$value\s/) || (/^\S+\s+$value\s/)) {
+ $ret = 1;
+ last;
+ }
+ }
+ close IN;
+ return $ret;
+}
+
+sub validate_protocol_negate {
+ my $value = shift;
+ if ($value =~ m/^\!(.*)$/) {
+ $value = $1;
+ }
+ return validate_protocol($value);
+}
+
+sub validate_macaddr {
+ my $value = shift;
+ $value = lc $value;
+ my $byte = '[0-9a-f]{2}';
+ return 1 if ($value =~ /^$byte(:$byte){5}$/);
+}
+
+sub validate_macaddr_negate {
+ my $value = shift;
+ if ($value =~ m/^\!(.*)$/) {
+ $value = $1;
+ }
+ return validate_macaddr($value);
+}
+
+# IPv6 syntax definition
+my $RE_IPV4_BYTE = '((25[0-5])|(2[0-4][0-9])|([01][0-9][0-9])|([0-9]{1,2}))';
+my $RE_IPV4 = "$RE_IPV4_BYTE(\.$RE_IPV4_BYTE){3}";
+my $RE_H16 = '([a-fA-F0-9]{1,4})';
+my $RE_H16_COLON = "($RE_H16:)";
+my $RE_LS32 = "(($RE_H16:$RE_H16)|($RE_IPV4))";
+my $RE_IPV6_P1 = "($RE_H16_COLON)\{6\}$RE_LS32";
+my $RE_IPV6_P2 = "::($RE_H16_COLON)\{5\}$RE_LS32";
+my $RE_IPV6_P3 = "($RE_H16)?::($RE_H16_COLON)\{4\}$RE_LS32";
+my $RE_IPV6_P4 = "(($RE_H16_COLON)\{0,1\}$RE_H16)?"
+ . "::($RE_H16_COLON)\{3\}$RE_LS32";
+my $RE_IPV6_P5 = "(($RE_H16_COLON)\{0,2\}$RE_H16)?"
+ . "::($RE_H16_COLON)\{2\}$RE_LS32";
+my $RE_IPV6_P6 = "(($RE_H16_COLON)\{0,3\}$RE_H16)?"
+ . "::($RE_H16_COLON)\{1\}$RE_LS32";
+my $RE_IPV6_P7 = "(($RE_H16_COLON)\{0,4\}$RE_H16)?::$RE_LS32";
+my $RE_IPV6_P8 = "(($RE_H16_COLON)\{0,5\}$RE_H16)?::$RE_H16";
+my $RE_IPV6_P9 = "(($RE_H16_COLON)\{0,6\}$RE_H16)?::";
+my $RE_IPV6 = "($RE_IPV6_P1)|($RE_IPV6_P2)|($RE_IPV6_P3)|($RE_IPV6_P4)"
+ . "|($RE_IPV6_P5)|($RE_IPV6_P6)|($RE_IPV6_P7)|($RE_IPV6_P8)"
+ . "|($RE_IPV6_P9)";
+
+sub validate_ipv6 {
+ $_ = shift;
+ return 0 if (!/^$RE_IPV6$/);
+ return 1;
+}
+
+sub validateType {
+ my ($type, $value) = @_;
+ if (!defined($type) || !defined($value)) {
+ return 0;
+ }
+ if (!defined($type_handler{$type})) {
+ print "type \"$type\" not defined\n";
+ return 0;
+ }
+ if (!&{$type_handler{$type}}($value)) {
+ print "\"$value\" is not a valid value of type \"$type\"\n";
+ return 0;
+ }
+
+ return 1;
+}
+
+sub findType {
+ my ($value, @candidates) = @_;
+ if (!defined($value) || ((scalar @candidates) < 1)) {
+ return undef;
+ }
+ foreach my $type (@candidates) {
+ if (!defined($type_handler{$type})) {
+ next;
+ }
+ if (&{$type_handler{$type}}($value)) {
+ # the first valid type is returned
+ return $type;
+ }
+ }
+ return undef;
+}
+
+1;
+
diff --git a/scripts/XorpConfigParser.pm b/scripts/XorpConfigParser.pm
new file mode 100755
index 0000000..e85410f
--- /dev/null
+++ b/scripts/XorpConfigParser.pm
@@ -0,0 +1,368 @@
+package XorpConfigParser;
+
+use lib "/opt/vyatta/share/perl5/";
+use strict;
+
+my %data;
+
+my %fields = (
+ _data => \%data
+);
+
+sub new {
+ my $that = shift;
+ my $class = ref ($that) || $that;
+ my $self = {
+ %fields,
+ };
+
+ bless $self, $class;
+ return $self;
+}
+
+
+sub copy_node {
+ my ($self, $from, $to, $name) = @_;
+ if (!defined($from) || !defined($to) || !defined($name)) {
+ return;
+ }
+
+ foreach my $node (@$from) {
+ my $stringNodeNameHere = $node->{'name'};
+ if ($stringNodeNameHere =~ /^$name.*/) {
+ foreach my $nodeCheck (@$to) {
+ my $stringCheck = $nodeCheck->{'name'};
+ if ($name eq $stringCheck) {
+ $nodeCheck->{'value'} = $node->{'value'};
+ $nodeCheck->{'children'} = $node->{'children'};
+ $nodeCheck->{'comment'} = $node->{'comment'};
+ return;
+ }
+ }
+ push(@$to, $node);
+ }
+ }
+}
+sub copy_multis {
+ my ($self, $nodes, $name) = @_;
+ if (!defined($nodes) || !defined($name)) {
+ return undef;
+ }
+
+ my @multis;
+
+ foreach my $node (@$nodes) {
+ my $stringNodeNameHere = $node->{'name'};
+ if ($stringNodeNameHere =~ /$name\s(\S+)/) {
+ my $stringNameHere = $1;
+ my %multi = (
+ 'name' => $stringNameHere,
+ 'comment' => $node->{'comment'},
+ 'value' => $node->{'value'},
+ 'children' => $node->{'children'}
+ );
+ push(@multis, \%multi);
+ }
+ }
+
+ return @multis;
+}
+sub comment_out_child {
+ my ($self, $children, $name, $comment) = @_;
+ if (!defined($children) || !defined($name)) {
+ return;
+ }
+
+ for (my $i = 0; $i < @$children; $i++) {
+ my $stringNodeNameHere = @$children[$i]->{'name'};
+ if ($name eq $stringNodeNameHere) {
+ @$children[$i]->{'comment_out'} = "1";
+ if (defined($comment)) {
+ @$children[$i]->{'comment_out'} = $comment;
+ }
+ }
+ }
+}
+sub create_node {
+ my ($self, $path) = @_;
+
+ my $hash = \%data;
+ foreach my $segment (@$path) {
+ my $children = $hash->{'children'};
+ if (!defined($children)) {
+ my @new_children;
+ $hash->{'children'} = \@new_children;
+ $children = \@new_children;
+ }
+ my $child_found = 0;
+ foreach my $child (@$children) {
+ if ($child->{'name'} eq $segment) {
+ $child_found = 1;
+ $hash = $child;
+ last;
+ }
+ }
+ if ($child_found == 0) {
+ my %new_hash = (
+ 'name' => $segment
+ );
+ push(@$children, \%new_hash);
+ $hash = \%new_hash;
+ }
+ }
+ return $hash;
+}
+sub delete_child {
+ my ($self, $children, $name) = @_;
+ if (!defined($children) || !defined($name)) {
+ return;
+ }
+
+ for (my $i = 0; $i < @$children; $i++) {
+ my $stringNodeNameHere = @$children[$i]->{'name'};
+ if ($name eq $stringNodeNameHere) {
+ @$children[$i] = undef;
+ }
+ }
+}
+sub find_child {
+ my ($self, $children, $name) = @_;
+ if (!defined($children) || !defined($name)) {
+ return undef;
+ }
+
+ foreach my $child (@$children) {
+ my $stringNodeNameHere = $child->{'name'};
+ if ($name eq $stringNodeNameHere) {
+ return $child;
+ }
+ }
+ return undef;
+}
+sub get_node {
+ my ($self, $path) = @_;
+
+ my $hash = $self->{_data};
+ foreach my $segment (@$path) {
+ my $children = $hash->{'children'};
+ if (!defined($children)) {
+ return undef;
+ }
+
+ my $child_found = 0;
+ foreach my $child (@$children) {
+ if ($child->{'name'} eq $segment) {
+ $child_found = 1;
+ $hash = $child;
+ last;
+ }
+ }
+
+ if ($child_found == 0) {
+ return undef;
+ }
+ }
+ return $hash;
+}
+
+sub push_comment {
+ my ($self, $path, $comment) = @_;
+
+ my $hash = \%data;
+ foreach my $segment (@$path) {
+ my $children = $hash->{'children'};
+ if (!defined($children)) {
+ my @children;
+ $hash->{'children'} = \@children;
+ $children = \@children;
+ }
+
+ my $child_found = 0;
+ foreach my $child (@$children) {
+ if ($child->{'name'} eq $segment) {
+ $child_found = 1;
+ $hash = $child;
+ last;
+ }
+ }
+
+ if ($child_found == 0) {
+ my %new_hash = (
+ 'name' => $segment
+ );
+ push(@$children, \%new_hash);
+ $hash = \%new_hash;
+ }
+ }
+
+ my %new_comment = (
+ 'comment' => $comment
+ );
+ my $childrenPush = $hash->{'children'};
+ if (!defined($childrenPush)) {
+ my @new_children;
+ $hash->{'children'} = \@new_children;
+ $childrenPush = \@new_children;
+ }
+ push(@$childrenPush, \%new_comment);
+}
+sub set_value {
+ my ($self, $path, $value) = @_;
+
+ my $hash = $self->create_node($path);
+ if (defined($hash)) {
+ $hash->{'value'} = $value;
+ }
+}
+sub output {
+ my ($self, $depth, $hash) = @_;
+
+ if (!defined($hash)) {
+ $hash = $self->{_data};
+ }
+
+ if ($hash->{'comment'} ne '') {
+ print '/*' . $hash->{'comment'} . "*/\n";
+ }
+ my $children = $hash->{'children'};
+ foreach my $child (@$children) {
+ if (defined($child)) {
+ if (defined($child->{'comment_out'})) {
+ print "\n";
+ if ($child->{'comment_out'} ne "1") {
+ print "/* --- $child->{'comment_out'} --- */\n";
+ }
+ print "/* --- CONFIGURATION COMMENTED OUT DURING MIGRATION BELOW ---\n";
+ }
+
+ print " " x $depth;
+ if ($child->{'value'} ne '') {
+ print "$child->{'name'}: $child->{'value'}";
+ print "\n";
+ } else {
+ my $print_brackets = 0;
+ my $children = $child->{'children'};
+ if (defined($children) && @$children > 0) {
+ $print_brackets = 1;
+ } elsif ($child->{'name'} ne '' && !($child->{'name'} =~ /\s/)) {
+ $print_brackets = 1;
+ }
+
+ if ($child->{'name'} ne '') {
+ print "$child->{'name'}";
+ if ($print_brackets) {
+ print " {";
+ }
+ print "\n";
+ }
+
+ $self->output($depth+1, $child);
+ if ($print_brackets) {
+ print " " x $depth;
+ print "}\n";
+ }
+ }
+
+ if (defined($child->{'comment_out'})) {
+ print " --- CONFIGURATION COMMENTED OUT DURING MIGRATION ABOVE --- */\n\n";
+ }
+
+ }
+ }
+}
+sub parse {
+ my ($self, $file) = @_;
+ open(INPUT, "< $file") or die "Error! Unable to open file \"$file\". $!";
+
+ my $contents = "";
+ while (<INPUT>) {$contents .= $_}
+ close INPUT;
+
+ my @array_contents = split('', $contents);
+# print scalar(@array_contents) . "\n";
+
+ my $length_contents = @array_contents;
+ my $colon = 0;
+ my $colon_quote = 0;
+ my $name = '';
+ my $value = undef;
+ my @path;
+ my %tree;
+ for (my $i = 0; $i < $length_contents;) {
+ my $c = $array_contents[$i];
+ my $cNext = $array_contents[$i+1];
+
+ if ($c eq '/' && $cNext eq '*') {
+ my $comment_text = '';
+ my $comment_end = index($contents, '*/', $i+2);
+ if ($comment_end == -1) {
+ $comment_text = substr($contents, $i+2);
+ } else {
+ $comment_text = substr($contents, $i+2, $comment_end - $i - 2);
+ $i = $comment_end + 2;
+ }
+# print 'Comment is: "' . $comment_text . "\"\n";
+ $self->push_comment(\@path, $comment_text);
+ } elsif ($colon == 0 && ($c eq '{' || $c eq ':' || $c eq "\n")) {
+ $name =~ s/^\s+|\s$//g;
+ if (length($name) > 0) {
+ push(@path, $name);
+# print "Path is: \"@path\" Name is: \"$name\"\n";
+ $self->set_value(\@path, $value);
+ $name = '';
+
+ if ($c eq "\n") {
+ pop(@path);
+ }
+ if ($c eq ':') {
+ $colon = 1;
+ }
+ }
+ $i++;
+ } elsif ($c eq '}') {
+ pop(@path);
+ $name = '';
+ $i++;
+ } elsif ($c eq ';') {
+ $i++;
+ } elsif ($colon == 1) {
+ my $value_end = 0;
+ if ($c eq '"') {
+ $value .= $c;
+ if ($colon_quote == 1) {
+ $value_end = 1;
+ } else {
+ $colon_quote = 1;
+ }
+ } elsif ($c eq '\\' && $cNext eq '"') {
+ $value .= '\\"';
+ $i++;
+ } else {
+ if ((length($value) > 0) || (!($c =~ /\s/))) {
+ $value .= $c;
+ }
+ }
+
+ if ($colon_quote == 0 && ($cNext eq '}' || $cNext eq ';' || $cNext =~ /\s/)) {
+ $value_end = 1;
+ }
+ $i++;
+
+ if ($value_end == 1) {
+ if (length($value) > 0) {
+# print "Path is: \"@path\" Value is: $value\n";
+ $self->set_value(\@path, $value);
+ $value = undef;
+ }
+ pop(@path);
+ $colon_quote = 0;
+ $colon = 0;
+ }
+ } else {
+ $name .= $c;
+ $i++;
+ }
+ }
+}
+
+
diff --git a/scripts/system/vyatta_update_login_user.pl b/scripts/system/vyatta_update_login_user.pl
new file mode 100644
index 0000000..86c0074
--- /dev/null
+++ b/scripts/system/vyatta_update_login_user.pl
@@ -0,0 +1,172 @@
+#!/usr/bin/perl
+
+use strict;
+use Fcntl;
+use POSIX qw(:unistd_h);
+
+# arg: login_name
+# returns the next available uid if login_name doesn't exist.
+# otherwise returns (undef, <passwd fields for login_name>).
+sub next_uid_if_not_exist {
+ my $login = shift;
+ my $min_uid = 1000;
+ my $max_uid = 60000;
+ if (open(LOGIN_DEF, "/etc/login.defs")) {
+ while (<LOGIN_DEF>) {
+ if (m/^\s*UID_MIN\s+(\d+)/) {
+ $min_uid = $1;
+ next;
+ }
+ if (m/^\s*UID_MAX\s+(\d+)/) {
+ $max_uid = $1;
+ next;
+ }
+ }
+ close LOGIN_DEF;
+ }
+
+ open(PASSWD, "/etc/passwd") or exit 1;
+ while (<PASSWD>) {
+ chomp;
+ my @passwd_fields = split /:/;
+ if ($passwd_fields[0] eq $login) {
+ close PASSWD;
+ return (undef, @passwd_fields);
+ }
+ if ($min_uid <= $passwd_fields[2]) {
+ next if ($passwd_fields[2] > $max_uid);
+ $min_uid = $passwd_fields[2] + 1;
+ next;
+ }
+ }
+ close PASSWD;
+ exit 2 if ($min_uid > $max_uid);
+ return ($min_uid);
+}
+
+# arg: login_name
+# returns the corresponding line in shadow or undef if login_name doesn't
+# exist.
+sub get_shadow_line {
+ my $login = shift;
+ open(SHADOW, "/etc/shadow") or exit 3;
+ while (<SHADOW>) {
+ chomp;
+ if (m/^$login:/) {
+ close SHADOW;
+ return $_;
+ }
+ }
+ close SHADOW;
+ return undef;
+}
+
+my $user = shift;
+my $full = shift;
+my $encrypted = shift;
+
+# emulate lckpwdf(3).
+# difference: we only try to lock it once (non-blocking). lckpwdf will block
+# for up to 15 seconds waiting for the lock.
+# note that the lock is released when file is closed (e.g., exit), so no need
+# for explicit unlock.
+my $flock = pack "ssa20", F_WRLCK, SEEK_SET, "\0";
+sysopen(PWDLCK, "/etc/.pwd.lock", O_WRONLY | O_CREAT, 0600) or exit 3;
+fcntl(PWDLCK, F_SETLK, $flock) or exit 3;
+
+if ($user eq "-d") {
+ $user = $full;
+ exit 4 if (!defined($user));
+
+ # check if user is using the system
+ my @pslines = `ps -U $user -u $user u`;
+ if ($#pslines != 0) {
+ # user is using the system
+ print STDERR "Delete failed: user \"$user\" is using the system\n";
+ exit 4;
+ }
+
+ my $ret = system("sed -i '/^$user:/d' /etc/passwd");
+ exit 5 if ($ret >> 8);
+ $ret = system("sed -i '/^$user:/d' /etc/shadow");
+ exit 6 if ($ret >> 8);
+ $ret = system("rm -rf /home/$user");
+ exit 7 if ($ret >> 8);
+ exit 0;
+}
+
+exit 4 if (!defined($user) || !defined($full) || !defined($encrypted));
+
+my $DEF_GROUP = "quagga";
+my $DEF_SHELL = "/bin/bash";
+
+open(GRP, "/etc/group") or exit 5;
+my $def_gid = undef;
+while (<GRP>) {
+ my @group_fields = split /:/;
+ if ($group_fields[0] eq $DEF_GROUP) {
+ $def_gid = $group_fields[2];
+ last;
+ }
+}
+exit 6 if (!defined($def_gid));
+
+my @vals = next_uid_if_not_exist($user);
+my ($new_user, $passwd_line, $shadow_line) = (0, "", "");
+if (defined($vals[0])) {
+ # add new user
+ $new_user = 1;
+ $passwd_line = "$user:x:$vals[0]:${def_gid}:$full:/home/$user:$DEF_SHELL";
+ my $sline = get_shadow_line($user);
+ exit 7 if (defined($sline));
+ my $seconds = `date +%s`;
+ my $days = int($seconds / 3600 / 24);
+ $shadow_line = "$user:$encrypted:$days:0:99999:7:::";
+} else {
+ # modify existing user
+ shift @vals;
+ $vals[4] = $full;
+ $passwd_line = join(':', @vals);
+ my $sline = get_shadow_line($user);
+ exit 8 if (!defined($sline));
+ @vals = split /:/, $sline;
+ $vals[1] = $encrypted;
+ for (my $padding = (9 - $#vals - 1); $padding > 0; $padding--) {
+ push @vals, '';
+ }
+ $shadow_line = join(':', @vals);
+}
+
+my $ret = 0;
+if (!$new_user) {
+ $ret = system("sed -i '/^$user:/d' /etc/passwd");
+ exit 9 if ($ret >> 8);
+ $ret = system("sed -i '/^$user:/d' /etc/shadow");
+ exit 10 if ($ret >> 8);
+}
+
+open(PASSWD, ">>/etc/passwd") or exit 11;
+print PASSWD "$passwd_line\n";
+close PASSWD;
+open(SHADOW, ">>/etc/shadow") or exit 12;
+print SHADOW "$shadow_line\n";
+close SHADOW;
+
+if (($new_user) && !(-e "/home/$user")) {
+ if (-d "/etc/skel") {
+ $ret = system("cp -a /etc/skel /home/$user");
+ exit 13 if ($ret >> 8);
+ $ret = system("chmod 755 /home/$user");
+ exit 14 if ($ret >> 8);
+ $ret = system("chown -R $user:$DEF_GROUP /home/$user");
+ exit 15 if ($ret >> 8);
+ } else {
+ $ret = system("mkdir -p /home/$user");
+ exit 16 if ($ret >> 8);
+ $ret = system("chmod 755 /home/$user");
+ exit 17 if ($ret >> 8);
+ }
+}
+
+exit 0;
+
diff --git a/scripts/system/vyatta_update_logrotate.pl b/scripts/system/vyatta_update_logrotate.pl
new file mode 100644
index 0000000..2740526
--- /dev/null
+++ b/scripts/system/vyatta_update_logrotate.pl
@@ -0,0 +1,55 @@
+#!/usr/bin/perl
+
+use strict;
+
+my $file = "messages";
+my $log_file = "/var/log/messages";
+if ($#ARGV == 3) {
+ $file = shift;
+ $log_file = "/var/log/user/$file";
+}
+my $files = shift;
+my $size = shift;
+my $set = shift;
+my $log_conf = "/etc/logrotate.d/$file";
+
+if (!defined($files) || !defined($size) || !defined($set)) {
+ exit 1;
+}
+
+if (!($files =~ m/^\d+$/) || !($size =~ m/^\d+$/)) {
+ exit 2;
+}
+
+# just remove it and make a new one below
+# (the detection mechanism in XORP doesn't work anyway)
+unlink $log_conf;
+
+open(OUT, ">>$log_conf") or exit 3;
+if ($set == 1) {
+ print OUT <<EOF;
+$log_file {
+ missingok
+ notifempty
+ rotate $files
+ size=${size}k
+ postrotate
+ kill -HUP `cat /var/run/syslogd.pid`
+ endscript
+}
+EOF
+}
+close OUT;
+
+sleep 1;
+# XXX somehow starting syslogd with 'start-stop-daemon --start...' here fails
+# with SEGV (?). just start syslogd directly.
+#if (system("/opt/vyatta/sbin/sysklogd.init restart")) {
+system("/opt/vyatta/sbin/sysklogd.init stop");
+sleep 1;
+if (system(". /etc/default/syslogd ; /sbin/syslogd \$SYSLOGD")) {
+ exit 4;
+}
+
+exit 0;
+
diff --git a/scripts/system/vyatta_update_syslog.pl b/scripts/system/vyatta_update_syslog.pl
new file mode 100644
index 0000000..315e2a9
--- /dev/null
+++ b/scripts/system/vyatta_update_syslog.pl
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+use strict;
+my $SYSLOG_CONF = '/etc/syslog.conf';
+
+my $match1 = shift;
+my $match2 = shift;
+my $update_line = shift;
+
+if (!defined($match1) || !defined($match2) || !defined($update_line)) {
+ exit 1;
+}
+
+if (system("touch $SYSLOG_CONF")) {
+ exit 2;
+}
+
+my $exp1 = "";
+my $exp2 = "";
+if ($match1 ne "") {
+ $exp1 = $match1;
+ if ($match2 ne "") {
+ $exp2 = $match2;
+ }
+} elsif ($match2 ne "") {
+ $exp1 = $match2;
+}
+
+if ($exp2 ne "") {
+ if (system("sed -i '/$exp1/{/$exp2/d}' $SYSLOG_CONF")) {
+ exit 2;
+ }
+} elsif ($exp1 ne "") {
+ if (system("sed -i '/$exp1/d' $SYSLOG_CONF")) {
+ exit 3;
+ }
+}
+
+open(OUT, ">>$SYSLOG_CONF") or exit 4;
+if ($update_line ne "") {
+ print OUT "$update_line";
+}
+close OUT;
+
+sleep 1;
+# XXX somehow starting syslogd with 'start-stop-daemon --start...' here fails
+# with SEGV (?). just start syslogd directly.
+#if (system("/opt/vyatta/sbin/sysklogd.init restart")) {
+system("/opt/vyatta/sbin/sysklogd.init stop");
+sleep 1;
+if (system(". /etc/default/syslogd ; /sbin/syslogd \$SYSLOGD")) {
+ exit 5;
+}
+
+exit 0;
+
diff --git a/scripts/vyatta-cli-expand-var.pl b/scripts/vyatta-cli-expand-var.pl
new file mode 100755
index 0000000..fcc2b43
--- /dev/null
+++ b/scripts/vyatta-cli-expand-var.pl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfig;
+
+# expand a variable reference
+if ($#ARGV != 0) {
+ print STDERR "usage: vyatta-cli-expand-var.pl '<var-ref>'\n";
+ exit 1;
+}
+
+$_ = $ARGV[0];
+
+# basic format check:
+# '(' ')' not allowed in reference.
+# only allow absolute path for now.
+if (!/^\$\(\/([^()]+)\)$/) {
+ print STDERR "invalid variable reference (invalid format)\n";
+ exit 1;
+}
+$_ = $1;
+
+my $multi_val = 1;
+if (s/^(.*)\/\@\@$/$1/) {
+ # return list of multi-node values
+ $multi_val = 1;
+} elsif (s/^(.*)\/\@$/$1/) {
+ # return single value
+ $multi_val = 0;
+} else {
+ # only allow the above 2 forms for now.
+ print STDERR "invalid variable reference (invalid value specification)\n";
+ exit 1;
+}
+
+if (/\@/) {
+ # '@' not allowed anywhere else in the reference for now.
+ print STDERR "invalid variable reference (extra value specification)\n";
+ exit 1;
+}
+
+my $config = new VyattaConfig;
+my $path_str = join ' ', (split /\//);
+my $val_str = "";
+if ($multi_val) {
+ my @tmp = $config->returnOrigValues($path_str);
+ if (scalar(@tmp) > 0) {
+ # we got multiple values back
+ $val_str = join ' ', @tmp;
+ } else {
+ # this node may be a 'tag' node. try listing children.
+ $config->setLevel($path_str);
+ @tmp = $config->listOrigNodes();
+ $val_str = join ' ', @tmp;
+ }
+} else {
+ $val_str = $config->returnOrigValue($path_str);
+}
+
+# expanded string is printed on stdout (multiple values separated by ' ').
+print "$val_str";
+exit 0;
+
diff --git a/scripts/vyatta-config-loader.pl b/scripts/vyatta-config-loader.pl
new file mode 100755
index 0000000..a3dfc44
--- /dev/null
+++ b/scripts/vyatta-config-loader.pl
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+# Perl script for loading the startup config file.
+# $0: startup config file.
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfigLoad;
+
+# get a list of all config statement in the startup config file
+# (sorted by rank).
+my @all_nodes = VyattaConfigLoad::getStartupConfigStatements($ARGV[0]);
+if (scalar(@all_nodes) == 0) {
+ # no config statements
+ exit 1;
+}
+my $cur_rank = ${$all_nodes[0]}[1];
+my $commit_cmd = '/opt/vyatta/sbin/xorp_tmpl_tool commit';
+my $cleanup_cmd = '/opt/vyatta/sbin/xorp_tmpl_tool cleanup';
+my $ret = 0;
+# higher-ranked statements committed before lower-ranked.
+foreach (@all_nodes) {
+ my ($path_ref, $rank) = @$_;
+ if ($rank != $cur_rank) {
+ # commit all nodes with the same rank together.
+ $ret = system("$commit_cmd");
+ if ($ret >> 8) {
+ print STDERR "Commit failed at rank $cur_rank\n";
+ system("$cleanup_cmd");
+ # continue after cleanup (or should we abort?)
+ }
+ $cur_rank = $rank;
+ }
+ my $cmd = '/opt/vyatta/sbin/xorp_tmpl_tool set ' . (join ' ', @$path_ref);
+ $ret = system("$cmd");
+ if ($ret >> 8) {
+ $cmd =~ s/^.*?set /set /;
+ print STDERR "[[$cmd]] failed\n";
+ # continue after set failure (or should we abort?)
+ }
+}
+$ret = system("$commit_cmd");
+if ($ret >> 8) {
+ print STDERR "Commit failed at rank $cur_rank\n";
+ system("$cleanup_cmd");
+ # exit normally after cleanup (or should we exit with error?)
+}
+
+# really clean up
+system('/opt/vyatta/sbin/xorp_tmpl_tool end_loading');
+
+exit 0;
diff --git a/scripts/vyatta-find-type.pl b/scripts/vyatta-find-type.pl
new file mode 100755
index 0000000..b6514f0
--- /dev/null
+++ b/scripts/vyatta-find-type.pl
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaTypeChecker;
+
+# find the type of a value (from a list of candidates)
+if ($#ARGV < 1) {
+ print "usage: vyatta-find-type.pl <value> <type> [<type> ...]\n";
+ exit 1;
+}
+
+if (my $type = VyattaTypeChecker::findType(@ARGV)) {
+ # type found
+ print "$type";
+ exit 0;
+}
+
+# value not valid for any of the candidates
+exit 1;
+
diff --git a/scripts/vyatta-load-config.pl b/scripts/vyatta-load-config.pl
new file mode 100755
index 0000000..7a1e01d
--- /dev/null
+++ b/scripts/vyatta-load-config.pl
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+# Perl script for loading config file at run time.
+# $0: config file.
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfigLoad;
+
+my $etcdir = $ENV{ofr_sysconfdir};
+my $bootpath = '';
+if (-r "$etcdir/bootfile_path") {
+ $bootpath = `cat $etcdir/bootfile_path`;
+}
+$bootpath =~ s/\/[^\/]+$//;
+
+if ($#ARGV != 0) {
+ print "Usage: load <config_file_name>\n";
+ exit 1;
+}
+
+my $load_file = $ARGV[0];
+if (!($load_file =~ /^\//)) {
+ # relative path
+ $load_file = "$bootpath/$load_file";
+}
+
+print "Loading config file $load_file...\n";
+my %cfg_hier = VyattaConfigLoad::loadConfigHierarchy($load_file);
+if (scalar(keys %cfg_hier) == 0) {
+ print "Load failed\n";
+ exit 1;
+}
+
+my %cfg_diff = VyattaConfigLoad::getConfigDiff(\%cfg_hier);
+
+my @delete_list = @{$cfg_diff{'delete'}};
+my @set_list = @{$cfg_diff{'set'}};
+
+foreach (@delete_list) {
+ my ($cmd_ref, $rank) = @{$_};
+ my @cmd = ( 'my_delete', @{$cmd_ref} );
+ my $cmd_str = join ' ', @cmd;
+ system("$cmd_str");
+ if ($? >> 8) {
+ $cmd_str =~ s/^my_//;
+ print "\"$cmd_str\" failed\n";
+ }
+}
+
+foreach (@set_list) {
+ my ($cmd_ref, $rank) = @{$_};
+ my @cmd = ( 'my_set', @{$cmd_ref} );
+ my $cmd_str = join ' ', @cmd;
+ system("$cmd_str");
+ if ($? >> 8) {
+ $cmd_str =~ s/^my_//;
+ print "\"$cmd_str\" failed\n";
+ }
+}
+
+system("my_commit");
+if ($? >> 8) {
+ print "Load failed (commit failed)\n";
+ exit 1;
+}
+
+print "Done\n";
+exit 0;
+
diff --git a/scripts/vyatta-output-config.pl b/scripts/vyatta-output-config.pl
new file mode 100755
index 0000000..7f3ea83
--- /dev/null
+++ b/scripts/vyatta-output-config.pl
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfigOutput;
+
+VyattaConfigOutput::outputNewConfig(@ARGV);
+exit 0;
+
diff --git a/scripts/vyatta-save-config.pl b/scripts/vyatta-save-config.pl
new file mode 100755
index 0000000..ad972b4
--- /dev/null
+++ b/scripts/vyatta-save-config.pl
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaConfigOutput;
+
+my $sbindir = $ENV{ofr_sbindir};
+my $etcdir = $ENV{ofr_sysconfdir};
+my $bootfile = '';
+if (-r "$etcdir/bootfile_path") {
+ $bootfile = `cat $etcdir/bootfile_path`;
+}
+my $bootpath = $bootfile;
+$bootpath =~ s/\/[^\/]+$//;
+
+if ($#ARGV > 0) {
+ print "Usage: save [config_file_name]\n";
+ exit 1;
+}
+
+my $save_file = "$bootfile";
+if (defined($ARGV[0])) {
+ $save_file = $ARGV[0];
+ if (!($save_file =~ /^\//)) {
+ # relative path
+ $save_file = "$bootpath/$save_file";
+ }
+}
+
+# this overwrites the file if it exists. we could create a backup first.
+if (! open(SAVE, ">$save_file")) {
+ print "Cannot open file '$save_file': $!\n";
+ exit 1;
+}
+
+print "Saving configuration to '$save_file'...";
+select SAVE;
+VyattaConfigOutput::outputActiveConfig();
+my $version_str = `/opt/vyatta/sbin/vyatta_current_conf_ver.pl`;
+print SAVE $version_str;
+select STDOUT;
+print "\nDone\n";
+close SAVE;
+exit 0;
+
diff --git a/scripts/vyatta-validate-type.pl b/scripts/vyatta-validate-type.pl
new file mode 100755
index 0000000..318572c
--- /dev/null
+++ b/scripts/vyatta-validate-type.pl
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use lib "/opt/vyatta/share/perl5/";
+use VyattaTypeChecker;
+
+# validate a value of a specific type
+if ($#ARGV != 1) {
+ print "usage: vyatta-validate-type.pl <type> <value>\n";
+ exit 1;
+}
+
+exit 0 if (VyattaTypeChecker::validateType($ARGV[0], $ARGV[1]));
+exit 1;
+
diff --git a/scripts/xorp_tmpl_tool b/scripts/xorp_tmpl_tool
new file mode 100755
index 0000000..ab25fa9
--- /dev/null
+++ b/scripts/xorp_tmpl_tool
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+UMASK_SAVE=`umask`
+umask 0111
+XORPLOGFILE=/tmp/xorp_tmpl_tool.log
+touch ${XORPLOGFILE}
+umask ${UMASK_SAVE}
+
+#need to pass in value to change... as part of set command...
+## cli ENV_EDIT_LEVEL
+export VYATTA_EDIT_LEVEL=/;
+## cli ENV_TEMPLATE_LEVEL
+export VYATTA_TEMPLATE_LEVEL=/;
+
+## cli ENV_A_DIR
+export VYATTA_ACTIVE_CONFIGURATION_DIR=/opt/vyatta/config/active;
+mkdir -p $VYATTA_ACTIVE_CONFIGURATION_DIR
+
+#now need to grab the parent pid.
+## XXX eventually, we will use each session's bash shell pid for this.
+## however, for now, to interact with XORP we will rely on a global lock
+## instead of separate config dirs.
+#export VTID=$PPID
+export VTID=XORP
+
+# lock for XORP
+export XORP_LOCK="/opt/vyatta/config/active/.xorp.lck"
+
+## cli ENV_C_DIR
+export VYATTA_CHANGES_ONLY_DIR=/opt/vyatta/config/tmp/changes_only_$VTID;
+mkdir -p $VYATTA_CHANGES_ONLY_DIR
+
+## cli ENV_M_DIR
+export VYATTA_TEMP_CONFIG_DIR=/opt/vyatta/config/tmp/new_config_$VTID;
+if [ ! -d $VYATTA_TEMP_CONFIG_DIR ]
+then
+ mkdir -p $VYATTA_TEMP_CONFIG_DIR
+ sudo mount -t unionfs -o dirs=${VYATTA_CHANGES_ONLY_DIR}=rw:/opt/vyatta/config/active=ro unionfs ${VYATTA_TEMP_CONFIG_DIR}
+fi
+
+## cli ENV_TMP_DIR
+export VYATTA_CONFIG_TMP=/opt/vyatta/config/tmp/tmp_$VTID;
+mkdir -p $VYATTA_CONFIG_TMP
+
+RET_STATUS=0
+#this needs to be the array string of commands, something like $[*] or whatever
+
+echo "Command: ${@}" | grep -v -i password >> ${XORPLOGFILE}
+
+#echo "ConfigDirectories BEFORE ========>>>>>>" >> ${XORPLOGFILE}
+#find /opt/vyatta/config -name "*" -print | grep interface >> ${XORPLOGFILE}
+#echo "<=========ConfigDirectories BEFORE" >> ${XORPLOGFILE}
+
+## for tracing command-line XRL calls.
+## 1 => info level
+## 2 => warning level
+#export CL_XRLTRACE=2
+
+UMASK_SAVE=`umask`
+umask 0111
+MYCMDERRLOGFILE=/tmp/my_cmd_err_${RANDOM}.log
+rm -rf ${MYCMDERRLOGFILE}
+umask ${UMASK_SAVE}
+
+case "$1" in
+ set)
+ /opt/vyatta/sbin/my_set "${@:2}" >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE}
+ RET_STATUS=$?
+ if [ $RET_STATUS != 0 ]; then
+ rm -rf $XORP_LOCK >&/dev/null
+ fi
+ ;;
+ delete)
+ /opt/vyatta/sbin/my_delete "${@:2}" >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE}
+ RET_STATUS=$?
+ if [ $RET_STATUS != 0 ]; then
+ rm -rf $XORP_LOCK >&/dev/null
+ fi
+ ;;
+ commit)
+ /opt/vyatta/sbin/my_commit >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE}
+ RET_STATUS=$?
+ rm -rf $XORP_LOCK >&/dev/null
+ ;;
+ test)
+ "${@:2}" >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE}
+ RET_STATUS=$?
+ ;;
+ cleanup)
+
+ LOCKTRYCOUNTER=0
+ LOCKTRYSTATUS=-1
+
+ while [[ ${LOCKTRYCOUNTER} -lt 60 && ${LOCKTRYSTATUS} -ne 0 ]] ; do
+
+ if mkdir $XORP_LOCK >&/dev/null ; then
+ LOCKTRYSTATUS=0
+ else
+ LOCKTRYCOUNTER=`expr ${LOCKTRYCOUNTER} + 1`
+ sleep 1;
+ fi
+ done
+
+ if [ ${LOCKTRYCOUNTER} -ge 60 ] ; then
+ echo "Cannot unlock configuration" >> ${MYCMDERRLOGFILE}
+ rm -rf ${XORP_LOCK}
+ mkdir $XORP_LOCK >&/dev/null
+ fi
+
+ sudo umount ${VYATTA_TEMP_CONFIG_DIR}
+ sudo rm -rf $VYATTA_CHANGES_ONLY_DIR/* $VYATTA_CHANGES_ONLY_DIR/.modified
+ sudo mount -t unionfs -o dirs=${VYATTA_CHANGES_ONLY_DIR}=rw:/opt/vyatta/config/active=ro unionfs ${VYATTA_TEMP_CONFIG_DIR}
+ RET_STATUS=0
+ ;;
+ end_loading)
+ sudo umount ${VYATTA_TEMP_CONFIG_DIR}
+ sudo rm -rf ${VYATTA_CHANGES_ONLY_DIR}
+ sudo rm -rf ${VYATTA_CONFIG_TMP}
+ sudo rm -rf ${VYATTA_TEMP_CONFIG_DIR}
+ RET_STATUS=0
+ ;;
+ rtrmgr_indirect_cleanup)
+ # do nothing now that we handle XORP interaction differently.
+ RET_STATUS=0
+ ;;
+ *)
+ rm -rf ${MYCMDERRLOGFILE}
+ exit 1
+ ;;
+esac
+
+if [ -f ${MYCMDERRLOGFILE} ] ; then
+
+ echo -n "STDERR:" >>${XORPLOGFILE}
+ cat ${MYCMDERRLOGFILE} >>${XORPLOGFILE}
+ echo "end of STDERR" >>${XORPLOGFILE}
+
+ cat ${MYCMDERRLOGFILE} 1>&2
+
+ rm -rf ${MYCMDERRLOGFILE}
+
+fi
+
+#echo "ConfigDirectories AFTER ========>>>>>>" >> ${XORPLOGFILE}
+#find /opt/vyatta/config -name "*" -print | grep interface >> ${XORPLOGFILE}
+#echo "<=========ConfigDirectories AFTER" >> ${XORPLOGFILE}
+
+echo "ret=${RET_STATUS}" >> ${XORPLOGFILE}
+exit $RET_STATUS
+