diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/VyattaConfig.pm | 548 | ||||
-rw-r--r-- | scripts/VyattaConfigDOMTree.pm | 364 | ||||
-rwxr-xr-x | scripts/VyattaConfigLoad.pm | 340 | ||||
-rwxr-xr-x | scripts/VyattaConfigOutput.pm | 253 | ||||
-rwxr-xr-x | scripts/VyattaMisc.pm | 62 | ||||
-rw-r--r-- | scripts/VyattaTypeChecker.pm | 179 | ||||
-rwxr-xr-x | scripts/XorpConfigParser.pm | 368 | ||||
-rw-r--r-- | scripts/system/vyatta_update_login_user.pl | 172 | ||||
-rw-r--r-- | scripts/system/vyatta_update_logrotate.pl | 55 | ||||
-rw-r--r-- | scripts/system/vyatta_update_syslog.pl | 56 | ||||
-rwxr-xr-x | scripts/vyatta-cli-expand-var.pl | 64 | ||||
-rwxr-xr-x | scripts/vyatta-config-loader.pl | 51 | ||||
-rwxr-xr-x | scripts/vyatta-find-type.pl | 21 | ||||
-rwxr-xr-x | scripts/vyatta-load-config.pl | 69 | ||||
-rwxr-xr-x | scripts/vyatta-output-config.pl | 9 | ||||
-rwxr-xr-x | scripts/vyatta-save-config.pl | 45 | ||||
-rwxr-xr-x | scripts/vyatta-validate-type.pl | 15 | ||||
-rwxr-xr-x | scripts/xorp_tmpl_tool | 150 |
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 + |