summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore33
-rw-r--r--AUTHORS1
-rw-r--r--COPYING27
-rw-r--r--Makefile.am53
-rw-r--r--NEWS1
-rw-r--r--README3
-rw-r--r--configure.ac38
-rw-r--r--debian/README8
-rwxr-xr-xdebian/autogen.sh37
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control23
-rw-r--r--debian/copyright34
-rw-r--r--debian/docs2
-rw-r--r--debian/linda1
-rw-r--r--debian/lintian6
-rwxr-xr-xdebian/rules101
-rw-r--r--etc/bash_completion.d/vyatta-cfg789
-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
-rw-r--r--src/.gitignore9
-rw-r--r--src/cli_def.l424
-rw-r--r--src/cli_new.c1938
-rw-r--r--src/cli_objects.c343
-rw-r--r--src/cli_objects.h65
-rw-r--r--src/cli_parse.y206
-rw-r--r--src/cli_path_utils.c535
-rw-r--r--src/cli_path_utils.h72
-rw-r--r--src/cli_val.h206
-rw-r--r--src/cli_val.l293
-rw-r--r--src/cli_val_engine.c881
-rw-r--r--src/cli_val_engine.h86
-rw-r--r--src/commit.c1364
-rw-r--r--src/delete.c258
-rw-r--r--src/set.c310
-rw-r--r--templates/interfaces/ethernet/node.def8
-rw-r--r--templates/interfaces/ethernet/node.tag/address/node.def5
-rw-r--r--templates/interfaces/ethernet/node.tag/description/node.def2
-rw-r--r--templates/interfaces/ethernet/node.tag/duplex/node.def6
-rw-r--r--templates/interfaces/ethernet/node.tag/enable/node.def3
-rw-r--r--templates/interfaces/ethernet/node.tag/hw-id/node.def2
-rw-r--r--templates/interfaces/ethernet/node.tag/mac/node.def4
-rw-r--r--templates/interfaces/ethernet/node.tag/mtu/node.def5
-rw-r--r--templates/interfaces/ethernet/node.tag/speed/node.def6
-rw-r--r--templates/interfaces/ethernet/node.tag/vif/node.def7
-rw-r--r--templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def5
-rw-r--r--templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def2
-rw-r--r--templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def3
-rw-r--r--templates/interfaces/loopback/node.def8
-rw-r--r--templates/interfaces/loopback/node.tag/address/node.def6
-rw-r--r--templates/interfaces/loopback/node.tag/description/node.def2
-rw-r--r--templates/interfaces/node.def1
-rw-r--r--templates/system/domain-name/node.def15
-rw-r--r--templates/system/domain-search/domain/node.def12
-rw-r--r--templates/system/gateway-address/node.def17
-rw-r--r--templates/system/host-name/node.def24
-rw-r--r--templates/system/login/node.def3
-rw-r--r--templates/system/login/radius-server/node.def14
-rw-r--r--templates/system/login/radius-server/node.tag/port/node.def4
-rw-r--r--templates/system/login/radius-server/node.tag/secret/node.def2
-rw-r--r--templates/system/login/radius-server/node.tag/timeout/node.def3
-rw-r--r--templates/system/login/user/node.def17
-rw-r--r--templates/system/login/user/node.tag/authentication/encrypted-password/node.def2
-rw-r--r--templates/system/login/user/node.tag/authentication/node.def1
-rw-r--r--templates/system/login/user/node.tag/authentication/plaintext-password/node.def9
-rw-r--r--templates/system/login/user/node.tag/full-name/node.def2
-rw-r--r--templates/system/name-server/node.def17
-rw-r--r--templates/system/ntp-server/node.def16
-rw-r--r--templates/system/options/node.def1
-rw-r--r--templates/system/options/reboot-on-panic/node.def16
-rw-r--r--templates/system/package/auto-sync/node.def15
-rw-r--r--templates/system/package/node.def1
-rw-r--r--templates/system/package/repository/node.def14
-rw-r--r--templates/system/package/repository/node.tag/component/node.def2
-rw-r--r--templates/system/package/repository/node.tag/description/node.def2
-rw-r--r--templates/system/package/repository/node.tag/url/node.def2
-rw-r--r--templates/system/static-host-mapping/host-name/node.def4
-rw-r--r--templates/system/static-host-mapping/host-name/node.tag/alias/node.def33
-rw-r--r--templates/system/static-host-mapping/host-name/node.tag/inet/node.def30
-rw-r--r--templates/system/static-host-mapping/node.def1
-rw-r--r--templates/system/syslog/console/facility/node.def11
-rw-r--r--templates/system/syslog/console/facility/node.tag/level/node.def3
-rw-r--r--templates/system/syslog/console/node.def1
-rw-r--r--templates/system/syslog/file/node.def4
-rw-r--r--templates/system/syslog/file/node.tag/archive/files/node.def3
-rw-r--r--templates/system/syslog/file/node.tag/archive/node.def8
-rw-r--r--templates/system/syslog/file/node.tag/archive/size/node.def3
-rw-r--r--templates/system/syslog/file/node.tag/facility/node.def13
-rw-r--r--templates/system/syslog/file/node.tag/facility/node.tag/level/node.def3
-rw-r--r--templates/system/syslog/global/archive/files/node.def3
-rw-r--r--templates/system/syslog/global/archive/node.def8
-rw-r--r--templates/system/syslog/global/archive/size/node.def3
-rw-r--r--templates/system/syslog/global/facility/node.def11
-rw-r--r--templates/system/syslog/global/facility/node.tag/level/node.def3
-rw-r--r--templates/system/syslog/global/node.def1
-rw-r--r--templates/system/syslog/host/node.def3
-rw-r--r--templates/system/syslog/host/node.tag/facility/node.def11
-rw-r--r--templates/system/syslog/host/node.tag/facility/node.tag/level/node.def3
-rw-r--r--templates/system/syslog/node.def3
-rw-r--r--templates/system/syslog/user/node.def3
-rw-r--r--templates/system/syslog/user/node.tag/facility/node.def14
-rw-r--r--templates/system/syslog/user/node.tag/facility/node.tag/level/node.def3
-rw-r--r--templates/system/time-zone/node.def16
-rw-r--r--tools/rl_passwd.cc152
120 files changed, 11614 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5036292
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+*~
+.*.swp
+*.[oa]
+*.l[oa]
+*.so
+*.libs
+*.deps
+.dirstamp
+libtool
+/aclocal.m4
+/autom4te.cache
+/build-stamp
+/ChangeLog
+/config
+/config.log
+/config.guess
+/config.status
+/config.sub
+/configure
+/debian/files
+/debian/vyatta-cfg
+/INSTALL
+/Makefile.in
+/Makefile
+/src/my_commit
+/src/my_set
+/src/my_delete
+/src/cli_def.c
+/src/cli_parse.c
+/src/cli_parse.h
+/src/cli_val.c
+/tools/rl_passwd
+
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..ee635b2
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+eng@vyatta.com
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..8c03eb3
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,27 @@
+/*
+ * Package: vyatt-cfg
+ *
+ * **** 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) "YEAR" Vyatta, Inc.
+ * All Rights Reserved.
+ *
+ * Author: eng@vyatta.com
+ * Date: 2007
+ * Description: Vyatta configuration system
+ *
+ * **** End License ****
+ *
+ */
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..3ff591d
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,53 @@
+cfgdir = $(datadir)/vyatta-cfg/templates
+share_perl5dir = /opt/vyatta/share/perl5
+completiondir = /etc/bash_completion.d
+
+AM_CFLAGS = -I src -Wall
+AM_YFLAGS = -d --name-prefix=yy_`basename $* .y`_
+AM_LFLAGS = --prefix=yy_`basename $* .l`_ -olex.yy.c
+
+completion_DATA = etc/bash_completion.d/vyatta-cfg
+
+lib_LTLIBRARIES = src/libvyatta-cfg.la
+src_libvyatta_cfg_la_LDFLAGS = -version-info 1:0:0
+src_libvyatta_cfg_la_SOURCES = src/cli_parse.y src/cli_def.l src/cli_val.l \
+ src/cli_new.c src/cli_path_utils.c \
+ src/cli_val_engine.c src/cli_objects.c
+CLEANFILES = src/cli_parse.c src/cli_parse.h src/cli_def.c src/cli_val.c
+LDADD = src/libvyatta-cfg.la
+
+sbin_PROGRAMS = src/my_commit
+sbin_PROGRAMS += src/my_delete
+sbin_PROGRAMS += src/my_set
+sbin_PROGRAMS += tools/rl_passwd
+src_my_commit_SOURCES = src/commit.c
+src_my_delete_SOURCES = src/delete.c
+src_my_set_SOURCES = src/set.c
+tools_rl_passwd_SOURCES = tools/rl_passwd.cc
+
+sbin_SCRIPTS = scripts/xorp_tmpl_tool
+sbin_SCRIPTS += scripts/vyatta-validate-type.pl
+sbin_SCRIPTS += scripts/vyatta-find-type.pl
+sbin_SCRIPTS += scripts/system/vyatta_update_login_user.pl
+sbin_SCRIPTS += scripts/system/vyatta_update_logrotate.pl
+sbin_SCRIPTS += scripts/system/vyatta_update_syslog.pl
+sbin_SCRIPTS += scripts/vyatta-config-loader.pl
+sbin_SCRIPTS += scripts/vyatta-cli-expand-var.pl
+sbin_SCRIPTS += scripts/vyatta-output-config.pl
+sbin_SCRIPTS += scripts/vyatta-save-config.pl
+sbin_SCRIPTS += scripts/vyatta-load-config.pl
+
+share_perl5_DATA = scripts/XorpConfigParser.pm
+share_perl5_DATA += scripts/VyattaConfig.pm
+share_perl5_DATA += scripts/VyattaConfigDOMTree.pm
+share_perl5_DATA += scripts/VyattaConfigOutput.pm
+share_perl5_DATA += scripts/VyattaConfigLoad.pm
+share_perl5_DATA += scripts/VyattaMisc.pm
+share_perl5_DATA += scripts/VyattaTypeChecker.pm
+
+cpiop = find . ! -regex '\(.*~\|.*\.bak\|.*\.swp\|.*\#.*\#\)' -print0 | \
+ cpio -0pd
+
+install-exec-hook:
+ mkdir -p $(DESTDIR)$(cfgdir)
+ cd templates; $(cpiop) $(DESTDIR)$(cfgdir)
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..78fdaa6
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+see http://www.vyatta.com/news/
diff --git a/README b/README
new file mode 100644
index 0000000..62c936a
--- /dev/null
+++ b/README
@@ -0,0 +1,3 @@
+This package has the Vyatta configuration system, including the configuration
+back-end, the base configuration templates, and the config-mode CLI completion
+mechanism.
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f12cf4f
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,38 @@
+# Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.59)
+
+m4_define([VERSION_ID], [m4_esyscmd([
+ if test -f .version ; then
+ head -n 1 .version | tr -d \\n
+ else
+ echo -n 2.4
+ fi])])
+AC_INIT([vyatta-cfg], VERSION_ID, [vyatta-support@vyatta.com])
+
+test -n "$VYATTA_VERSION" || VYATTA_VERSION=$PACKAGE_VERSION
+
+AC_CONFIG_AUX_DIR([config])
+AM_INIT_AUTOMAKE([gnu no-dist-gzip dist-bzip2 subdir-objects])
+AC_PREFIX_DEFAULT([/opt/vyatta])
+
+AC_PROG_CC
+AM_PROG_AS
+AM_PROG_CC_C_O
+AC_PROG_LIBTOOL
+AC_PROG_LEX
+AC_PROG_YACC
+
+AC_CHECK_LIB(crypt, crypt, [ LIBS="$LIBS -lcrypt" ])
+
+AC_ARG_ENABLE([nostrip],
+ AC_HELP_STRING([--enable-nostrip],
+ [include -nostrip option during packaging]),
+ [NOSTRIP=-nostrip], [NOSTRIP=])
+
+AC_CONFIG_FILES(
+ [Makefile])
+
+AC_SUBST(NOSTRIP)
+
+AC_OUTPUT
+
diff --git a/debian/README b/debian/README
new file mode 100644
index 0000000..bbf471b
--- /dev/null
+++ b/debian/README
@@ -0,0 +1,8 @@
+The Debian Package vyatta-cfg
+----------------------------
+
+This package has the Vyatta configuration system, including the configuration
+back-end, the base configuration templates, and the config-mode CLI completion
+mechanism.
+
+ -- An-Cheng Huang <ancheng@vyatta.com> Mon, 24 Sep 2007
diff --git a/debian/autogen.sh b/debian/autogen.sh
new file mode 100755
index 0000000..ff125d1
--- /dev/null
+++ b/debian/autogen.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+
+if [ -d .git ] ; then
+# generate GNU/Debian format ChangeLog from git log
+
+ rm -f ChangeLog
+
+ if which git2cl >/dev/null ; then
+ git-log --pretty --numstat --summary | git2cl >> ChangeLog
+ else
+ git-log --pretty=short >> ChangeLog
+ fi
+
+# append repository reference
+
+ url=` git repo-config --get remote.origin.url`
+ test "x$url" = "x" && url=`pwd`
+
+ branch=`git-branch --no-color | sed '/^\* /!d; s/^\* //'`
+ test "x$branch" = "x" && branch=master
+
+ sha=`git log --pretty=oneline --no-color -n 1 | cut -c-8`
+ test "x$sha" = "x" && sha=00000000
+
+ echo "$url#$branch-$sha" >> ChangeLog
+
+fi
+
+rm -rf config
+rm -f aclocal.m4 config.guess config.statusconfig.sub configure INSTALL
+
+autoreconf --force --install
+
+rm -f config.sub config.guess
+ln -s /usr/share/misc/config.sub .
+ln -s /usr/share/misc/config.guess .
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..01b2125
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+vyatta-cfg (0.1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- An-Cheng Huang <ancheng@vyatta.com> Mon, 24 Sep 2007 17:31:53 -0700
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..2c4e495
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,23 @@
+Source: vyatta-cfg
+Section: contrib/net
+Priority: extra
+Maintainer: An-Cheng Huang <ancheng@vyatta.com>
+Build-Depends: debhelper (>= 5), autotools-dev
+Standards-Version: 3.7.2
+
+Package: vyatta-cfg
+Architecture: any
+Depends: bash (>= 3.1),
+ sed (>= 4.1.5),
+ perl (>= 5.8.8),
+ procps (>= 1:3.2.7-3),
+ coreutils (>= 5.97-5.3)
+Suggests: util-linux (>= 2.13-5),
+ net-tools,
+ ethtool,
+ ncurses-bin (>= 5.5-5),
+ ntpdate
+Description: Vyatta configuration system
+ This package has the Vyatta configuration system, including the configuration
+ back-end, the base configuration templates, and the config-mode CLI completion
+ mechanism.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..4c6e7e6
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,34 @@
+This package was debianized by An-Cheng Huang <ancheng@vyatta.com> on
+Mon, 24 Sep 2007 17:31:53 -0700.
+
+It's original content from the GIT repository <http://vyatt.com/git/vyatta-cfg>
+
+Upstream Author:
+
+ <eng@vyatta.com>
+
+Copyright:
+
+ Copyright (C) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+License:
+
+ The contents of this package 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) 2007 Vyatta, Inc.
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL'.
+
+The Debian packaging is (C) 2007, An-Cheng Huang <ancheng@vyatta.com> and
+is licensed under the GPL, see above.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..50bd824
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1,2 @@
+NEWS
+README
diff --git a/debian/linda b/debian/linda
new file mode 100644
index 0000000..0381d9d
--- /dev/null
+++ b/debian/linda
@@ -0,0 +1 @@
+Tag: file-in-opt
diff --git a/debian/lintian b/debian/lintian
new file mode 100644
index 0000000..ad0df30
--- /dev/null
+++ b/debian/lintian
@@ -0,0 +1,6 @@
+vyatta-cfg: file-in-unusual-dir
+vyatta-cfg: dir-or-file-in-opt
+vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/my_set /opt/vyatta/lib
+vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/my_commit /opt/vyatta/lib
+vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/my_delete /opt/vyatta/lib
+vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/rl_passwd /opt/vyatta/lib
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..3a7fc97
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,101 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+# These are used for cross-compiling and for saving the configure script
+# from having to guess our platform (since we know it already)
+DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+PACKAGE=vyatta-cfg
+PKGDIR=$(CURDIR)/debian/$(PACKAGE)
+
+CFLAGS = -Wall -g
+
+configure = ./configure
+configure += --host=$(DEB_HOST_GNU_TYPE)
+configure += --build=$(DEB_BUILD_GNU_TYPE)
+configure += --prefix=/opt/vyatta
+configure += --mandir=\$${prefix}/share/man
+configure += --infodir=\$${prefix}/share/info
+configure += CFLAGS="$(CFLAGS)"
+configure += LDFLAGS="-Wl,-z,defs"
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+ CFLAGS += -O0
+else
+ CFLAGS += -O2
+endif
+
+configure:
+ chmod +x debian/autogen.sh
+ debian/autogen.sh
+
+config.status: configure
+ dh_testdir
+ rm -f config.cache
+ $(configure)
+
+build: build-stamp
+
+build-stamp: config.status
+ dh_testdir
+ $(MAKE)
+ touch $@
+
+clean: clean-patched
+
+# Clean everything up, including everything auto-generated
+# at build time that needs not to be kept around in the Debian diff
+clean-patched:
+ dh_testdir
+ dh_testroot
+ if test -f Makefile ; then $(MAKE) clean distclean ; fi
+ rm -f build-stamp
+ rm -f config.status config.sub config.guess config.log
+ rm -f aclocal.m4 configure Makefile.in Makefile INSTALL
+ rm -rf config
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ $(MAKE) DESTDIR=$(PKGDIR) install
+
+ install -D --mode=0644 debian/lintian $(PKGDIR)/usr/share/lintian/overrides/$(PACKAGE)
+ install -D --mode=0644 debian/linda $(PKGDIR)/usr/share/linda/overrides/$(PACKAGE)
+
+# Build architecture-independent files here.
+binary-indep: build install
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs ChangeLog
+ dh_installdocs
+ dh_install
+ dh_installdebconf
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms
+ dh_installdeb
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+# Build architecture-dependent files here.
+binary-arch: build install
+# This is an architecture independent package
+# so; we have nothing to do by default.
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/etc/bash_completion.d/vyatta-cfg b/etc/bash_completion.d/vyatta-cfg
new file mode 100644
index 0000000..b16ffbf
--- /dev/null
+++ b/etc/bash_completion.d/vyatta-cfg
@@ -0,0 +1,789 @@
+# **** 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) 2006, 2007 Vyatta, Inc.
+# All Rights Reserved.
+#
+# Author: An-Cheng Huang
+# Date: 2007
+# Description: bash completion for Vyatta configuration commands
+#
+# **** End License ****
+
+# only do this if we are going into configure mode
+if [ "$_OFR_CONFIGURE" != "ok" ]; then
+ return 0
+fi
+
+if [ -r /etc/default/vyatta ]; then
+ source /etc/default/vyatta
+fi
+
+declare is_set=0
+declare last_idx=0
+declare -a comp_words=()
+
+# commands to unalias
+declare -a unalias_cmds=( clear configure date debug edit exit load \
+ no show save terminal undebug )
+for cmd in "${unalias_cmds[@]}"; do
+ unalias $cmd >& /dev/null
+done
+
+show ()
+{
+ eval "${vyatta_sbindir}/vyatta-output-config.pl \
+ \${VYATTA_EDIT_LEVEL//\// } $@"
+}
+
+save ()
+{
+ eval "${vyatta_sbindir}/vyatta-save-config.pl $@"
+}
+
+load ()
+{
+ eval "${vyatta_sbindir}/vyatta-load-config.pl $@"
+}
+
+declare vyatta_cfg_prompt_level=''
+set_config_ps1 ()
+{
+ local level=$1
+ if [ -z "$level" ]; then
+ export PS1="[edit]\n\u@\h# "
+ vyatta_cfg_prompt_level=''
+ else
+ export PS1="[edit $level]\n\u@\h# "
+ vyatta_cfg_prompt_level="$level"
+ fi
+}
+
+edit ()
+{
+ local num_comp=${#@}
+ local _mpath=${VYATTA_TEMP_CONFIG_DIR}/${VYATTA_EDIT_LEVEL}
+ local _tpath=${VYATTA_CONFIG_TEMPLATE}/${VYATTA_TEMPLATE_LEVEL}
+ local idx
+ for (( idx=1; idx <= num_comp; idx++ )); do
+ local comp
+ eval "comp=\$$idx"
+ vyatta_escape comp comp
+ push_path _mpath $comp
+ push_path _tpath $comp
+ if [ ! -d $_mpath ]; then
+ # "edit" only allows existing node
+ break
+ fi
+
+ # check if it's not tag value
+ if [ -d $_tpath ]; then
+ continue
+ fi
+
+ # check if it's tag value
+ pop_path _tpath
+ push_path _tpath $VYATTA_TAG_NAME
+ if [ -d $_tpath ]; then
+ continue
+ fi
+ pop_path _tpath
+ pop_path _mpath
+ break
+ done
+ # "edit" only valid for
+ # * "node.tag" level
+ # * "node.def" level without "type:"
+ if (( idx != ( num_comp + 1) )); then
+ echo "Invalid node \"$*\" for the 'edit' command"
+ return 1
+ fi
+ if [ "${_tpath:((-9))}" != "/node.tag" ]; then
+ # we are not at "node.tag" level. look for "type:".
+ if [ ! -r "$_tpath/node.def" ]; then
+ vyatta_cfg_type=""
+ else
+ vyatta_parse_tmpl "$_tpath/node.def"
+ fi
+ if [ -n "$vyatta_cfg_type" ]; then
+ # "type:" present
+ echo "The 'edit' command cannot be issued at the \"$*\" level"
+ return 1
+ fi
+ fi
+ export VYATTA_EDIT_LEVEL="${_mpath#$VYATTA_TEMP_CONFIG_DIR}/"
+ export VYATTA_TEMPLATE_LEVEL="${_tpath#$VYATTA_CONFIG_TEMPLATE}/"
+
+ declare -a path_arr
+ path_str2arr VYATTA_EDIT_LEVEL path_arr
+ local path_str="${path_arr[*]}"
+ set_config_ps1 $path_str
+}
+
+really_exit()
+{
+ umount $VYATTA_TEMP_CONFIG_DIR
+ rm -rf $VYATTA_TEMP_CONFIG_DIR $VYATTA_CHANGES_ONLY_DIR $VYATTA_CONFIG_TMP
+ unset _OFR_CONFIGURE
+ builtin exit 0
+}
+
+exit ()
+{
+ local discard
+ if [ $# == 0 ]; then
+ discard=0
+ elif [ $# == 1 ] && [ "$1" == "discard" ]; then
+ discard=1
+ else
+ echo "Invalid argument \"$*\" for 'exit'"
+ return 1
+ fi
+
+ if [ "$VYATTA_EDIT_LEVEL" == "/" ]; then
+ # we are at the root level. check if we can really exit.
+ if [ -f "$VYATTA_TEMP_CONFIG_DIR/$VYATTA_MOD_NAME" ]; then
+ if (( ! discard )); then
+ echo "Cannot exit: configuration modified."
+ echo "Use 'exit discard' to discard the changes and exit."
+ return 1
+ fi
+ fi
+ really_exit
+ fi
+
+ # "exit" to the root level.
+ export VYATTA_EDIT_LEVEL="/"
+ export VYATTA_TEMPLATE_LEVEL="/"
+ set_config_ps1 ''
+}
+
+declare v_cfg_completion_debug=0
+decho ()
+{
+ if (( v_cfg_completion_debug )); then
+ echo -n "$*"
+ fi
+}
+
+push_path_arr ()
+{
+ # $1: \@path_arr
+ # $2: component
+ eval "$1=( \${$1[@]} '$2' )"
+}
+
+pop_path_arr ()
+{
+ # $1: \@path_arr
+ eval "$1=( \${$1[@]:0:((\${#$1[@]}-1))} )"
+}
+
+path_arr2str ()
+{
+ # $1: \@path_arr
+ # $2: \$path_str
+ eval "$2=\"\${$1[*]}\""
+ eval "$2=/\${$2// //}"
+}
+
+path_str2arr ()
+{
+ # $1: \$path_str
+ # $2: \@path_arr
+ local tmp
+ eval "tmp=\${$1:1}"
+ eval "$2=( \${tmp//\// } )"
+}
+
+push_path ()
+{
+ # $1: \$path_str
+ # $2: component
+ declare -a path_arr
+ eval "path_str2arr $1 path_arr"
+ eval "push_path_arr path_arr '$2'"
+ eval "path_arr2str path_arr $1"
+}
+
+pop_path ()
+{
+ # $1: \$path_str
+ declare -a path_arr
+ eval "path_str2arr $1 path_arr"
+ pop_path_arr path_arr
+ eval "path_arr2str path_arr $1"
+}
+
+get_filtered_dir_listing ()
+{
+ # $1: path
+ # $2: \@listing
+ if [ ! -d $1 ]; then
+ eval "$2=()"
+ return
+ fi
+ local pattern='^node\.def$|^node\.tag$|^node\.val$|^\.modified$'
+ patterh=$pattern'|^\.commit\.lck$|^\.wh\.'
+ local cmd="ls $1 |egrep -v '$pattern'"
+ declare -a listing=( $(eval $cmd) )
+ for enode in "${listing[@]}"; do
+ local unode
+ vyatta_unescape enode unode
+ eval "$2[\${#$2[@]}]=$unode"
+ done
+}
+
+filter_existing_nodes ()
+{
+ # $1: mpath
+ # $2: \@orig
+ # $3: \@filtered
+ declare -a orig
+ eval "orig=( \${$2[@]} )"
+ for node in "${orig[@]}"; do
+ if [ -d "$1/$node" ]; then
+ eval "$3[\${#$3[@]}]=$node"
+ fi
+ done
+}
+
+get_prefix_filtered_list ()
+{
+ # $1: prefix
+ # $2: \@list
+ # $3: \@filtered
+ declare -a olist
+ eval "olist=( \"\${$2[@]}\" )"
+ local idx=0
+ for elem in "${olist[@]}"; do
+ local sub=${elem#$1}
+ if [ "$elem" == "$sub" ]; then
+ continue
+ fi
+ eval "$3[$idx]=$elem"
+ (( idx++ ))
+ done
+}
+
+declare vyatta_cfg_help=""
+declare vyatta_cfg_type=""
+declare vyatta_cfg_tag=0
+declare vyatta_cfg_multi=0
+declare -a vyatta_cfg_allowed=()
+vyatta_parse_tmpl ()
+{
+ # $1: tmpl
+ vyatta_cfg_help=""
+ vyatta_cfg_type=""
+ vyatta_cfg_tag=0
+ vyatta_cfg_multi=0
+ vyatta_cfg_allowed=()
+ if [ ! -r $1 ]; then
+ return
+ fi
+ eval `sed -n '
+ /^help:[ ]\+/,/^[a-z]\+:/ {
+ s/^help:[ ]\+/vyatta_cfg_help=/p
+ /^ /p
+ }
+ /^syntax:[ ]\+\$(@)[ ]\+in[ ]\+/ {
+ s/^syntax:[ ]\+\$(@)[ ]\+in[ ]\+/vyatta_cfg_allowed=( /
+ s/^\([^;]\+\);.*$/\1 )/
+ s/[ ]*,[ ]*/ /gp
+ }
+ s/^tag:/vyatta_cfg_tag=1/p
+ s/^multi:/vyatta_cfg_multi=1/p
+ s/^type:[ ]\+\([^ ]\+\)$/vyatta_cfg_type=\1/p
+ ' $1`
+ if [ -z "$vyatta_cfg_help" ]; then
+ vyatta_cfg_help='<No help text available>'
+ fi
+}
+
+# this fills in $vyatta_help_text
+generate_help_text ()
+{
+ # $1: \@items
+ # $2: \@help_strs
+ declare -a items
+ declare -a helps
+ eval "items=( \"\${$1[@]}\" )"
+ eval "helps=( \"\${$2[@]}\" )"
+ vyatta_help_text="\\nPossible completions:"
+ for (( idx = 0; idx < ${#items[@]}; idx++ )); do
+ vyatta_help_text="${vyatta_help_text}\\n\\x20\\x20"
+ if [ ${#items[$idx]} -lt 6 ]; then
+ vyatta_help_text="${vyatta_help_text}${items[$idx]}\\t\\t"
+ elif [ ${#items[$idx]} -lt 14 ]; then
+ vyatta_help_text="${vyatta_help_text}${items[$idx]}\\t"
+ else
+ vyatta_help_text="${vyatta_help_text}${items[$idx]}\\n\\x20\\x20\\t\\t"
+ fi
+ vyatta_help_text="${vyatta_help_text}${helps[$idx]}"
+ done
+}
+
+# this fills in $vyatta_help_text
+get_tmpl_subdir_help ()
+{
+ # $1: path
+ # $2: \@subdirs
+ declare -a subdirs
+ eval "subdirs=( \${$2[@]} )"
+ if [ ${#subdirs[@]} == 0 ]; then
+ vyatta_help_text=""
+ return
+ fi
+ declare -a hitems=()
+ declare -a hstrs=()
+ for subdir in "${subdirs[@]}"; do
+ if [ ! -r $1/$subdir/node.def ]; then
+ vyatta_cfg_help="<No help text available>"
+ else
+ vyatta_parse_tmpl "$1/$subdir/node.def"
+ fi
+ hitems[${#hitems[@]}]=$subdir
+ hstrs[${#hstrs[@]}]=$vyatta_cfg_help
+ done
+ generate_help_text hitems hstrs
+}
+
+# return 0 if yes. 1 if no.
+item_in_list ()
+{
+ # $1: item
+ # $2: \@list
+ declare -a olist
+ local item
+ eval "olist=( \${$2[@]} )"
+ for item in "${olist[@]}"; do
+ if [ "$1" == "$item" ]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+append_allowed_values ()
+{
+ # $1: tmpl_path
+ # $2: \@values
+ if [ ! -r "$1/node.def" ]; then
+ return
+ fi
+ vyatta_parse_tmpl "$1/node.def"
+ local item
+ for item in "${vyatta_cfg_allowed[@]}"; do
+ if ! item_in_list $item $2; then
+ eval "$2=( \${$2[@]} \"$item\" )"
+ fi
+ done
+}
+
+# return 0 if yes. 1 if no.
+is_setting_new_leaf ()
+{
+ # $1: tmpl_path
+ if [ $is_set == 0 ]; then
+ return 1
+ fi
+ vyatta_parse_tmpl "$1/node.def"
+ if [ -z "$vyatta_cfg_type" ]; then
+ return 1
+ fi
+ return 0
+}
+
+# this fills in $vyatta_help_text
+get_node_value_help ()
+{
+ # $1: path
+ # $2: \@values
+ declare -a vals
+ eval "vals=( \"\${$2[@]}\" )"
+ if [ $is_set == 0 -a ${#vals[@]} == 0 ]; then
+ vyatta_help_text=""
+ return
+ fi
+ if [ ! -r "$1/node.def" ]; then
+ vyatta_cfg_help="<No help text available>"
+ vyatta_cfg_type=""
+ else
+ vyatta_parse_tmpl "$1/node.def"
+ fi
+ if [ $is_set == 1 -a ! -z "$vyatta_cfg_type" ]; then
+ # add a <type> value
+ local val="<$vyatta_cfg_type>"
+ vals=( $val "${vals[@]}" )
+ fi
+ if [ ${#vals[@]} == 0 ]; then
+ vyatta_help_text=""
+ return
+ fi
+ declare -a hitems=()
+ declare -a hstrs=()
+ for val in "${vals[@]}"; do
+ hitems[${#hitems[@]}]=$val
+ hstrs[${#hstrs[@]}]=$vyatta_cfg_help
+ done
+ generate_help_text hitems hstrs
+}
+
+get_value_list ()
+{
+ # $1: path
+ # $2: \@listing
+ local vfile=$1/node.val
+ if [ ! -r $vfile ]; then
+ eval "$2=()"
+ return
+ fi
+ declare -a listing=()
+ eval `sed 's/^\(.*\)$/listing[\\${#listing[@]}]='\''\1'\''/' $vfile`
+ eval "$2=( \"\${listing[@]}\" )"
+}
+
+vyatta_escape ()
+{
+ # $1: \$original
+ # $2: \$escaped
+ eval "$2=\${$1//\%/%25}"
+ eval "$2=\${$2//\//%2F}"
+}
+
+vyatta_unescape ()
+{
+ # $1: \$escaped
+ # $2: \$original
+ eval "$2=\${$1//\%2F/\/}"
+ eval "$2=\${$2//\%25/%}"
+}
+
+declare -a vyatta_completions
+declare vyatta_help_text="\\nNo help text available"
+declare vyatta_do_help=0
+vyatta_do_complete ()
+{
+ # when this function is called, it is expected that:
+ # * "vyatta_help_text" is filled with the help text.
+ # * "vyatta_completions" is an array of "filtered" possible completions
+ # (i.e., only those starting with the current last component).
+ local do_help=$vyatta_do_help
+
+ # we may not want to do the following
+<<'ENDCOMMENT'
+ if [ ${#vyatta_completions[@]} == 1 ]; then
+ # no ambiguous completions. do completion instead of help.
+ do_help=0
+ fi
+
+ # now check if we can auto-complete at least 1 more character.
+ if (( do_help )); then
+ local schar=""
+ for comp in "${vyatta_completions[@]}"; do
+ local sub=$comp
+ if [ ! -z "${COMP_WORDS[COMP_CWORD]}" ]; then
+ sub=${comp#${comp_words[$last_idx]}}
+ if [ "$comp" == "$sub" ]; then
+ # should not happen since vyatta_completions should be filtered.
+ continue
+ fi
+ fi
+ if [ -z "$schar" ]; then
+ schar=${sub:0:1}
+ else
+ if [ "$schar" != "${sub:0:1}" ]; then
+ schar=""
+ break
+ fi
+ fi
+ done
+ if [ ! -z "$schar" ]; then
+ do_help=0
+ fi
+ fi
+ENDCOMMENT
+
+ if (( do_help )); then
+ echo -en $vyatta_help_text
+ COMPREPLY=( $(compgen -W "== --") )
+ else
+ COMPREPLY=( $(compgen -W "${vyatta_completions[*]}" \
+ -- ${COMP_WORDS[COMP_CWORD]}) )
+ fi
+ vyatta_help_text="\\nNo help text available"
+}
+
+vyatta_config_complete ()
+{
+ if [ "$COMP_LINE" == "$VYATTA_COMP_LINE" ]; then
+ VYATTA_COMP_LINE=''
+ vyatta_do_help=1
+ else
+ VYATTA_COMP_LINE=$COMP_LINE
+ vyatta_do_help=0
+ fi
+
+ local command=${COMP_WORDS[0]}
+ # completion for "set" is different from other commands
+ is_set=0
+ if [ "$command" == "set" ]; then
+ is_set=1
+ fi
+ local end_space=0
+ local num_comp=$COMP_CWORD
+ if [ -z "${COMP_WORDS[$COMP_CWORD]}" ]; then
+ end_space=1
+ (( num_comp -= 1 ))
+ fi
+ (( last_idx = num_comp - 1 ))
+ comp_words=( ${COMP_WORDS[@]:1:$num_comp} )
+
+ # handle "exit"
+ if [ "$command" == "exit" ]; then
+ if (( num_comp > 1 || ( end_space && num_comp > 0 ) )); then
+ COMPREPLY=()
+ return
+ fi
+ declare -a hitems=( "discard" )
+ declare -a hstrs=( "Discard any changes" )
+ generate_help_text hitems hstrs
+ vyatta_completions=( "discard" )
+ vyatta_do_complete
+ return
+ fi
+
+ local _mpath=${VYATTA_TEMP_CONFIG_DIR}/${VYATTA_EDIT_LEVEL}
+ local _tpath=${VYATTA_CONFIG_TEMPLATE}/${VYATTA_TEMPLATE_LEVEL}
+ local last_tag=0
+ local idx=0
+ for (( idx=0; idx < num_comp; idx++ )); do
+ last_tag=0
+ local comp=${comp_words[$idx]}
+ vyatta_escape comp comp
+ push_path _mpath $comp
+ push_path _tpath $comp
+ if [ -d $_tpath ]; then
+ if (( ! is_set )); then
+ # we are not in "set" => only allow existing node
+ if [ ! -d $_mpath ]; then
+ break
+ fi
+ fi
+ continue
+ fi
+ pop_path _tpath
+ push_path _tpath $VYATTA_TAG_NAME
+ if [ -d $_tpath ]; then
+ if (( ! is_set && end_space )); then
+ # we are not in "set" && last component is complete.
+ # => only allow existing tag value.
+ if [ ! -d $_mpath ]; then
+ break
+ fi
+ fi
+ if (( idx != last_idx )); then
+ # TODO validate value
+ # break if not valid
+ # XXX is this validation necessary? (set will validate anyway)
+ true
+ fi
+ last_tag=1
+ continue
+ fi
+ pop_path _tpath
+ pop_path _mpath
+ break
+ done
+ # at the end of the loop, 3 possibilities:
+ # 1. (idx < last_idx): some component before the last is invalid
+ # => invalid command
+ # 2. (idx == last_idx): last component matches neither template nor node.tag
+ # => if end_space, then invalid command
+ # otherwise, may be an incomplete (non-tag) component, or incomplete
+ # "leaf value"
+ # => try matching dirs in _tpath or value(s) in _mpath/node.val
+ # 3. (idx == num_comp): the whole command matches templates/tags
+ if (( idx < last_idx || ( idx == last_idx && end_space ) )); then
+ # TODO error message?
+ COMPREPLY=()
+ return
+ fi
+
+ declare -a matches
+ if (( idx == last_idx )); then
+ # generate possibile matches (dirs in _tpath, and "help" from
+ # node.def in each dir, or values in _mpath/node.val)
+ declare -a fmatches
+ if [ -f $_mpath/node.val ]; then
+ decho [1a]
+ get_value_list $_mpath matches
+ get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches
+ append_allowed_values $_tpath fmatches
+ get_node_value_help $_tpath fmatches
+ else
+ decho [1b]
+ # see if the last component is a new leaf node
+ fmatches=()
+ if is_setting_new_leaf $_tpath; then
+ append_allowed_values $_tpath fmatches
+ get_node_value_help $_tpath fmatches
+ else
+ # last component is a non-value node. look for child nodes.
+ if (( ! is_set )); then
+ # not "set". only complete existing nodes.
+ declare -a amatches=()
+ get_filtered_dir_listing $_tpath amatches
+ filter_existing_nodes $_mpath amatches matches
+ else
+ get_filtered_dir_listing $_tpath matches
+ fi
+ get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches
+ get_tmpl_subdir_help $_tpath fmatches
+ fi
+ fi
+ vyatta_completions=( ${fmatches[@]} )
+ vyatta_do_complete
+ return
+ fi
+
+ if (( last_tag && end_space )); then
+ # if not "set", check _mpath (last component is the tag) is valid
+ # generate possible matches (dirs in _tpath, and "help" from node.def
+ # in each dir)
+ decho [2]
+ if [ $is_set == 1 -o -d $_mpath ]; then
+ if (( ! is_set )); then
+ # not "set". only complete existing nodes.
+ declare -a fmatches=()
+ get_filtered_dir_listing $_tpath fmatches
+ filter_existing_nodes $_mpath fmatches matches
+ else
+ get_filtered_dir_listing $_tpath matches
+ fi
+ get_tmpl_subdir_help $_tpath matches
+ vyatta_completions=( ${matches[@]} )
+ vyatta_do_complete
+ return
+ fi
+ return
+ fi
+
+ if (( last_tag && !end_space )); then
+ # generate possible matches (dirs in _mpath, and "help" from node.def
+ # in dirs in _tpath)
+ decho [3]
+ pop_path _mpath
+ pop_path _tpath
+ get_filtered_dir_listing $_mpath matches
+ declare -a fmatches
+ get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches
+ append_allowed_values $_tpath fmatches
+ get_node_value_help $_tpath fmatches
+ vyatta_completions=( ${fmatches[@]} )
+ vyatta_do_complete
+ return
+ fi
+
+ if (( !last_tag && end_space )); then
+ # generate possible matches
+ # 1. dirs in _tpath, and "help" from node.def in each dir
+ # 2. value(s) in _mpath/node.val (only if _tpath/node.def is "multi:")
+ # 3. dirs in _mpath (only if _tpath/node.def is "tag:")
+ if [ -d $_tpath/node.tag ]; then
+ # last component is a "tag name". look for tag values.
+ decho [4a]
+ get_filtered_dir_listing $_mpath matches
+ append_allowed_values $_tpath matches
+ get_node_value_help $_tpath matches
+ elif [ -f $_mpath/node.val ]; then
+ # last component is a leaf node. look for values.
+ decho [4b]
+ get_value_list $_mpath matches
+ append_allowed_values $_tpath matches
+ get_node_value_help $_tpath matches
+ else
+ decho [4c]
+ # see if the last component is a new leaf node
+ matches=()
+ if is_setting_new_leaf $_tpath; then
+ append_allowed_values $_tpath matches
+ get_node_value_help $_tpath matches
+ else
+ # last component is a non-value node. look for child nodes.
+ if (( ! is_set )); then
+ # not "set". only complete existing nodes.
+ declare -a fmatches=()
+ get_filtered_dir_listing $_tpath fmatches
+ filter_existing_nodes $_mpath fmatches matches
+ else
+ get_filtered_dir_listing $_tpath matches
+ fi
+ get_tmpl_subdir_help $_tpath matches
+ fi
+ fi
+ vyatta_completions=( ${matches[@]} )
+ vyatta_do_complete
+ return
+ fi
+
+ if (( !last_tag && !end_space )); then
+ # generate possible matches (dirs in _tpath, and "help" from node.def
+ # in each dir)
+ decho [5]
+ pop_path _tpath
+ get_filtered_dir_listing $_tpath matches
+ declare -a fmatches
+ get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches
+ get_tmpl_subdir_help $_tpath fmatches
+ vyatta_completions=( ${fmatches[@]} )
+ vyatta_do_complete
+ return
+ fi
+}
+
+mkdir -p $VYATTA_ACTIVE_CONFIGURATION_DIR
+mkdir -p $VYATTA_CHANGES_ONLY_DIR
+mkdir -p $VYATTA_CONFIG_TMP
+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
+
+# disallow 'Ctrl-D' exit, since we need special actions on 'exit'
+set -o ignoreeof 1
+
+set_config_ps1 ''
+alias commit=my_commit
+alias set=my_set
+alias delete=my_delete
+
+export VYATTA_COMP_LINE=""
+
+# readline bindings
+bind 'set show-all-if-ambiguous on'
+if ! bind -p |grep -q '\\C-x\\C-t'; then
+ bind '"\C-x\C-t": kill-region'
+fi
+if ! bind -p |grep -q '\\C-x\\C-o'; then
+ bind '"\C-x\C-o": copy-region-as-kill'
+fi
+
+complete -F vyatta_config_complete set
+complete -F vyatta_config_complete delete
+complete -F vyatta_config_complete show
+complete -F vyatta_config_complete edit
+complete -F vyatta_config_complete exit
+
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
+
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..a2b4c94
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,9 @@
+cli_parse.tab.c
+cli_parse.tab.h
+cli_def.lex.c
+cli_def.tab.c
+cli_def.tab.h
+cli_val.lex.c
+delete
+my_*
+show
diff --git a/src/cli_def.l b/src/cli_def.l
new file mode 100644
index 0000000..41994d6
--- /dev/null
+++ b/src/cli_def.l
@@ -0,0 +1,424 @@
+%{
+#include "cli_val.h"
+#include "cli_parse.h"
+FILE *yy_cli_def_in;
+static void make_def_value(vtw_type_e type);
+static int cli_last_nl_returned=0;
+#define STR_DELTA 2
+%}
+%x str
+%option noyywrap
+%option nounput
+%option never-interactive
+
+/*
+ * Regular expressions of IP and MAC addresses, URLs, etc.
+ */
+
+/*
+ * IPv4 address representation.
+ */
+RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2})
+RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3}
+RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9])
+RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN}
+
+/*
+ * IPv6 address representation in Augmented Backus-Naur Form (ABNF)
+ * as defined in RFC-2234.
+ * IPv6 address representation taken from RFC-3986:
+ *
+ * IPv6address = 6( h16 ":" ) ls32
+ * / "::" 5( h16 ":" ) ls32
+ * / [ h16 ] "::" 4( h16 ":" ) ls32
+ * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ * / [ *4( h16 ":" ) h16 ] "::" ls32
+ * / [ *5( h16 ":" ) h16 ] "::" h16
+ * / [ *6( h16 ":" ) h16 ] "::"
+ *
+ * h16 = 1*4HEXDIG
+ * ls32 = ( h16 ":" h16 ) / IPv4address
+ * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+ * dec-octet = DIGIT ; 0-9
+ * / %x31-39 DIGIT ; 10-99
+ * / "1" 2DIGIT ; 100-199
+ * / "2" %x30-34 DIGIT ; 200-249
+ * / "25" %x30-35 ; 250-255
+ */
+
+RE_H16 [a-fA-F0-9]{1,4}
+RE_H16_COLON {RE_H16}":"
+RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4})
+RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32}
+RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32}
+RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32}
+RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32}
+RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32}
+RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32}
+RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32}
+RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16}
+RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::"
+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}
+RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]?
+RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN}
+
+/*
+ * Ethernet MAC address representation.
+ */
+RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5}
+
+/*
+ * URL-related regular expressions.
+ *
+ * Implementation is based on the BNF-like specification from:
+ * - RFC-1738: HTTP, FTP, FILE
+ * - RFC-3617: TFTP
+ * - RFC-3986: update of RFC-1738
+ */
+RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP}
+
+/*
+ * URL schemeparts for IP based protocols.
+ * Representation taken from RFC-1738, and some of it is updated by RFC-3986.
+ *
+ * login = [ user [ ":" password ] "@" ] hostport
+ * hostport = host [ ":" port ]
+ * host = hostname | hostnumber
+ * hostname = *[ domainlabel "." ] toplabel
+ * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit
+ * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit
+ * alphadigit = alpha | digit
+ * hostnumber = digits "." digits "." digits "." digits
+ * port = digits
+ * user = *[ uchar | ";" | "?" | "&" | "=" ]
+ * password = *[ uchar | ";" | "?" | "&" | "=" ]
+ */
+RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT}
+RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})?
+RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL}
+RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]"
+RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+
+RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL}
+RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT}
+RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}
+RE_URL_PORT {RE_URL_DIGITS}
+RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+
+/*
+ * FILE URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * fileurl = "file://" [ host | "localhost" ] "/" fpath
+ */
+RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH}
+
+/*
+ * FTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ]
+ * fpath = fsegment *[ "/" fsegment ]
+ * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ]
+ * ftptype = "A" | "I" | "D" | "a" | "i" | "d"
+ */
+RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)?
+RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})*
+RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")*
+RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d"
+
+/*
+ * HTTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ]
+ * hpath = hsegment *[ "/" hsegment ]
+ * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ */
+RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)?
+RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})*
+RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+
+/*
+ * TFTP URL regular expression.
+ * Representation taken from RFC-3617.
+ *
+ * tftpURI = "tftp://" host "/" file [ mode ]
+ * mode = ";" "mode=" ( "netascii" / "octet" )
+ * file = *( unreserved / escaped )
+ * host = <as specified by RFC 2732 [3]>
+ * unreserved = <as specified in RFC 2396 [4]>
+ * escaped = <as specified in RFC 2396>
+ */
+RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})?
+RE_URL_TFTP_MODE ";""mode="("netascii"|"octet")
+RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})*
+
+/*
+ * URL-related miscellaneous definitions.
+ * Representation taken from RFC-1738 and from RFC-3986.
+ *
+ * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+ * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+ * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+ * "y" | "z"
+ * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ * alpha = lowalpha | hialpha
+ * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ * "8" | "9"
+ * safe = "$" | "-" | "_" | "." | "+"
+ * extra = "!" | "*" | "'" | "(" | ")" | ","
+ * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`"
+ * punctuation = "<" | ">" | "#" | "%" | <">
+ *
+ *
+ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
+ * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ * "a" | "b" | "c" | "d" | "e" | "f"
+ * escape = "%" hex hex
+ *
+ * unreserved = alpha | digit | safe | extra
+ * uchar = unreserved | escape
+ * xchar = unreserved | reserved | escape
+ * digits = 1*digit
+ *
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ */
+RE_URL_LOWALPHA [a-z]
+RE_URL_HIALPHA [A-Z]
+RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA}
+RE_URL_DIGIT [0-9]
+RE_URL_SAFE "$"|"-"|"_"|"."|"+"
+RE_URL_EXTRA "!"|"*"|"'"|"("|")"|","
+RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`"
+RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<">
+RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"="
+RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f]
+RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG}
+RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA}
+RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE}
+RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE}
+RE_URL_DIGITS {RE_URL_DIGIT}{1,}
+RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"="
+
+
+%%
+ char *string_buf=NULL;
+ char *string_buf_ptr=NULL;
+ int string_len=0, string_buf_len=0;
+ char str_delim=0;
+
+#[^\n]*(\\[ \t]*\n[^\n]*)*\n { /* comment */ }
+\n {yy_cli_def_lineno++;return EOL;}
+<<EOF>> { if(!cli_last_nl_returned) {cli_last_nl_returned=1;return EOL;} yyterminate(); }
+[\`\"] {
+ BEGIN(str);
+ str_delim = yy_cli_def_text[0];
+ string_buf_ptr = string_buf = my_malloc(STR_DELTA + 1, "");
+ string_len = STR_DELTA;
+ string_buf_len = STR_DELTA;
+ }
+<str>[\"\`] {
+ /* maybe a closing quote - all done */
+ if(str_delim == yy_cli_def_text[0]) {
+ BEGIN(INITIAL);
+ *string_buf_ptr = 0;
+ /* return string constant token type and
+ * value to parser
+ */
+ yy_cli_parse_lval.strp = string_buf;
+ return str_delim == '"'?STRING:EX_STRING;
+ }else{
+ *string_buf_ptr++ = yy_cli_def_text[0];
+ goto string_too_long;
+ }
+ }
+
+<str>\n {
+ /* error - unterminated string constant */
+ yy_cli_def_lineno++;
+ }
+
+<str>\\n {*string_buf_ptr++ = '\n';goto string_too_long;}
+<str>\\t {*string_buf_ptr++ = '\t';goto string_too_long;}
+<str>\\r {*string_buf_ptr++ = '\r';goto string_too_long;}
+<str>\\b {*string_buf_ptr++ = '\b';goto string_too_long;}
+<str>\\f {*string_buf_ptr++ = '\f';goto string_too_long;}
+<str>\\\n { yy_cli_def_lineno++; /* continuation => ignore */ }
+
+<str>\\. {
+ *string_buf_ptr++ = yy_cli_def_text[1];
+string_too_long:
+ *string_buf_ptr = 0;
+ /* printf("Cur string |%s|\n", string_buf); */
+ if (!--string_len) {
+ string_buf = my_realloc(string_buf,
+ string_buf_len + STR_DELTA + 1,
+ "cli_def STRING");
+ string_buf_ptr = string_buf + string_buf_len;
+ string_buf_len += STR_DELTA;
+ string_len = STR_DELTA;
+ }
+ }
+<str>[^\\\n\"\`]+ {
+ char *yptr = yy_cli_def_text;
+
+ while ( *yptr ){
+ *string_buf_ptr++ = *yptr++;
+ if (!--string_len) {
+ string_buf = my_realloc(string_buf,
+ string_buf_len + STR_DELTA + 1,
+ "cli_def STRING");
+ string_buf_ptr = string_buf + string_buf_len;
+ string_buf_len += STR_DELTA;
+ string_len = STR_DELTA;
+ }
+ }
+ *string_buf_ptr = 0;
+ }
+
+default: return DEFAULT;
+tag: return TAG;
+type: return TYPE;
+help: return HELP;
+syntax: return SYNTAX;
+commit: return COMMIT;
+check: yy_cli_parse_lval.action = syntax_act; return ACTION;
+delete: yy_cli_parse_lval.action = delete_act; return ACTION;
+update: yy_cli_parse_lval.action = update_act; return ACTION;
+activate: yy_cli_parse_lval.action = activate_act; return ACTION;
+create: yy_cli_parse_lval.action = create_act; return ACTION;
+begin: yy_cli_parse_lval.action = begin_act; return ACTION;
+end: yy_cli_parse_lval.action = end_act; return ACTION;
+multi: return MULTI;
+
+:: {
+ make_def_value(IPV6_TYPE);
+ return VALUE;
+ }
+txt {
+ yy_cli_parse_lval.type = TEXT_TYPE;
+ return TYPE_DEF;
+ }
+
+pattern return PATTERN;
+
+exec return EXEC;
+
+, return COMMA;
+\|\| return OR;
+\&\& return AND;
+\= return ASSIGN;
+\=\= {yy_cli_parse_lval.cond = EQ_COND; return COND;}
+\!\= {yy_cli_parse_lval.cond = NE_COND; return COND;}
+\< {yy_cli_parse_lval.cond = LT_COND; return COND;}
+\> {yy_cli_parse_lval.cond = GT_COND; return COND;}
+\<\= {yy_cli_parse_lval.cond = LE_COND; return COND;}
+\>\= {yy_cli_parse_lval.cond = GE_COND; return COND;}
+in {yy_cli_parse_lval.cond = IN_COND; return COND;}
+\! { return NOT; }
+\$\([^)]+\) {
+ yy_cli_parse_lval.strp = my_strdup(yy_cli_def_text, "TEXT");
+ return VAR;
+ }
+true {
+ make_def_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+false {
+ make_def_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+[0-9]+ {
+ make_def_value(INT_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4} {
+ make_def_value(IPV4_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4NET} {
+ make_def_value(IPV4NET_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6} {
+ make_def_value(IPV6_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6NET} {
+ make_def_value(IPV6NET_TYPE);
+ return VALUE;
+ }
+
+{RE_MACADDR} {
+ make_def_value(MACADDR_TYPE);
+ return VALUE;
+ }
+u32 {
+ yy_cli_parse_lval.type = INT_TYPE;
+ return TYPE_DEF;
+ }
+
+ipv4 {
+ yy_cli_parse_lval.type = IPV4_TYPE;
+ return TYPE_DEF;
+ }
+
+ipv4net {
+ yy_cli_parse_lval.type = IPV4NET_TYPE;
+ return TYPE_DEF;
+ }
+ipv6 {
+ yy_cli_parse_lval.type = IPV6_TYPE;
+ return TYPE_DEF;
+ }
+
+ipv6net {
+ yy_cli_parse_lval.type = IPV6NET_TYPE;
+ return TYPE_DEF;
+ }
+bool {
+ yy_cli_parse_lval.type = BOOL_TYPE;
+ return TYPE_DEF;
+ }
+macaddr {
+ yy_cli_parse_lval.type = MACADDR_TYPE;
+ return TYPE_DEF;
+ }
+\( return LP;
+\) return RP;
+; return SEMI;
+
+\\\n { yy_cli_def_lineno++; /*whitespace -- continuation => ignore */ }
+
+[ \t]+ /* whitespace */
+
+. {
+ /* everything else is a syntax error */
+ return SYNTAX_ERROR;
+ }
+
+%%
+
+static void make_def_value(vtw_type_e type)
+{
+ memset(&yy_cli_parse_lval.val.val, 0, sizeof(yy_cli_parse_lval.val.val));
+ yy_cli_parse_lval.val.free_me = TRUE;
+ yy_cli_parse_lval.val.val = my_strdup(yy_cli_def_text, "cli_parse.l");
+ yy_cli_parse_lval.val.val_type = type;
+}
diff --git a/src/cli_new.c b/src/cli_new.c
new file mode 100644
index 0000000..2f2801d
--- /dev/null
+++ b/src/cli_new.c
@@ -0,0 +1,1938 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "cli_val.h"
+#include "cli_parse.h"
+#include <regex.h>
+
+#include "cli_objects.h"
+#include "cli_val_engine.h"
+
+/* Defines: */
+
+#define EXE_STRING_DELTA 512
+#define PATH_DELTA 1000
+#define ENDS_ALLOC 20
+#define PATH_CM_LOCATION 25 /* mcd vs. ccd location
+ change when m_root changed */
+
+/* Global vars: */
+vtw_path m_path, t_path;
+
+/* Loval vars: */
+static vtw_node *vtw_free_nodes; /* linked via left */
+static char val_name[] = VAL_NAME;
+static int cond1[TOP_COND] ={5, 0,-1,-1, 0, 1, 0, 0};
+static int cond2[TOP_COND] ={5, 0, 1,-1,-1, 1, 1, 0};
+static char const *cond_formats[DOMAIN_TYPE] =
+ {
+ 0,
+ "%u", /* INT_TYPE */
+ "%u.%u.%u.%u", /*IPV4_TYPE*/
+ "%u.%u.%u.%u/%u", /*IPV4NET_TYPE*/
+ 0,
+ 0,
+ "%x:%x:%x:%x:%x:%x" /* MACADDR_TYPE */
+ };
+
+static int cond_format_lens[DOMAIN_TYPE] =
+ {
+ 0,
+ 1, /* INT_TYPE */
+ 4, /*IPV4_TYPE*/
+ 5, /*IPV4NET_TYPE*/
+ 0,
+ 0,
+ 6 /* MACADDR_TYPE */
+ };
+
+static int cli_val_len;
+static char *cli_val_alloc;
+static char *cli_val_ptr;
+
+static char *exe_string;
+static int exe_string_len;
+static int node_cnt;
+static int free_node_cnt;
+static boolean in_validate_val;
+static valstruct validate_value_val; /* value being validated
+ to be used as $(@) */
+
+/* Local function declarations: */
+
+static int check_comp(vtw_node *cur);
+static boolean check_syn_func(vtw_node *cur,const char* func,int line);
+#define check_syn(cur) check_syn_func((cur),__FUNCTION__,__LINE__)
+static void copy_path(vtw_path *to, vtw_path *from);
+static int eval_va(valstruct *res, vtw_node *node);
+static int expand_string(char *p);
+static void free_node(vtw_node *node);
+static void free_node_tree(vtw_node *node);
+static void free_reuse_list(void);
+static void free_path(vtw_path *path);
+static void free_string(char *str);
+static vtw_node * get_node(void);
+
+static void scan_ipv6(char *val, unsigned int *parts);
+
+static int set_reference_environment(const char* var_reference,
+ clind_path_ref *n_cfg_path,
+ clind_path_ref *n_tmpl_path,
+ clind_path_ref *n_cmd_path,
+ int active);
+
+/*************************************************
+ GLOBAL FUNCTIONS
+***************************************************/
+/* it is executed as "eval `my_set` in order to be able to
+ modify BASH env
+ therefore, all error will be reported as
+ printf("echo \"bla-bla-bla%s\";", sptr)
+ note very important ';' as the end of the format
+*/
+void bye(char *msg, ...)
+{
+ va_list ap;
+
+ if (is_silent_msg())
+ exit(0);
+ va_start(ap, msg);
+ if (is_echo())
+ printf("echo \"");
+ vprintf(msg, ap);
+ printf(is_echo()? "\";":"\n");
+ va_end(ap);
+
+ exit(0);
+}
+
+/* msg:
+ print message, preceeded by "echo " if global
+ flag echo set. This flag is used by program
+ which are executed as eval `command` in order to
+ modify BASH env
+*/
+void print_msg(char *msg, ...)
+{
+ va_list ap;
+
+ if (is_silent_msg())
+ return;
+ va_start(ap, msg);
+ if (is_echo())
+ printf("echo \"");
+ vprintf(msg, ap);
+ printf(is_echo()? "\";":"\n");
+ va_end(ap);
+}
+
+void touch_dir(const char *dp)
+{
+ struct stat statbuf;
+ if (lstat(dp, &statbuf) < 0) {
+ char *command;
+ command = my_malloc(strlen(dp) + 10, "set");
+ sprintf(command, "mkdir -p %s", dp);
+ system(command);
+ free(command);
+ return;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("directory %s expected, found regular file", dp);
+ }
+ return;
+}
+
+/*****************************************************
+ add_val:
+ verify that the types are the same;
+ if first valstruct is single value, convert it
+ into multivalue;
+ add the value of second to the list of first;
+*****************************************************/
+void add_val(valstruct *first, valstruct *second)
+{
+ assert (first->free_me && second->free_me);
+ assert(second->cnt == 0);
+ if (first->val_type != second->val_type) {
+ printf("Different types\n\n");
+ } else {
+ if (first->cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ first->vals = my_realloc(first->vals, (first->cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (first->cnt == 0) { /* single value - convert */
+ first->vals[0] = first->val;
+ first->cnt = 1;
+ first->val = NULL;
+ }
+ }
+ second->free_me = FALSE; /* we took its string */
+ first->vals[first->cnt] = second->val;
+ ++first->cnt;
+ }
+}
+/*****************************************************
+ append - append node to the tail of list
+*****************************************************/
+void append(vtw_list *l, vtw_node *n, int aux)
+{
+ vtw_node *lnode;
+ lnode = make_node(LIST_OP, n, NULL);
+ lnode->vtw_node_aux = aux;
+ if(l->vtw_list_tail) {
+ assert(l->vtw_list_tail->vtw_node_right == NULL);
+ l->vtw_list_tail->vtw_node_right = lnode;
+ } else {
+ assert(l->vtw_list_head == NULL);
+ l->vtw_list_head = lnode;
+ }
+ l->vtw_list_tail = lnode;
+}
+
+void dt(vtw_sorted *srtp)
+{
+ int i;
+ for (i=0; i<srtp->num; ++i)
+ printf("%d %s\n", i, (char *)(srtp->ptrs[i]));
+}
+
+
+void di(vtw_sorted *srtp)
+{
+ int i;
+ for (i=0; i<srtp->num; ++i)
+ printf("%u %u\n", i, *(unsigned int *)(srtp->ptrs[i]));
+}
+
+static char _lock_file_[1025] = {0};
+static int _lock_fd_=-1;
+
+static void clean_lock_file(void) {
+ if(_lock_file_[0]) {
+ unlink(_lock_file_);
+ _lock_file_[0]=0;
+ }
+ if(_lock_fd_!=-1) {
+ close(_lock_fd_);
+ _lock_fd_=-1;
+ }
+}
+
+boolean get_config_lock(const char* adirp, const char* lock_name) {
+
+ boolean ret = TRUE;
+
+ sprintf(_lock_file_, "%s/%s", adirp, lock_name);
+
+ _lock_fd_ = open(_lock_file_, O_WRONLY | O_CREAT | O_EXCL, 0644);
+
+ ret = (_lock_fd_!=-1);
+
+ if(ret) {
+ atexit(clean_lock_file);
+ }
+
+ return ret;
+}
+
+void internal_error(int line, char *file)
+{
+ printf("\n\nInternal Error at line %d in %s\n", line, file);
+ exit (-1);
+}
+
+/*************************************************
+ vtw_sort:
+ create sorted structure for the value,
+ allocates ptrs and parts in this structure
+*/
+void vtw_sort(valstruct *valp, vtw_sorted *sortp)
+{
+ int i, unsorted, left, child, right;
+ void *leftp, *rightp, *childp;
+ const char * format;
+ unsigned int *parts;
+ vtw_type_e type = valp->val_type;
+ char *cp;
+ int cur=0, par=0, partnum=0, res=0;
+ void *curp, *parp;
+
+ sortp->num = valp->cnt?valp->cnt : 1;
+ sortp->ptrs = my_malloc(sortp->num * sizeof(void *), "sort_ptrs");
+ sortp->partnum = cond_format_lens[type];
+ if (sortp->partnum) {
+ sortp->parts = my_malloc(sortp->partnum * sortp->num * sizeof(void *),
+ "sort_parts");
+ }else{
+ sortp->parts = NULL;
+ }
+ switch (type){
+ case IPV6_TYPE:
+ case IPV6NET_TYPE:
+ for (i = 0; i < sortp->num; ++i) {
+ parts = sortp->parts + i * sortp->partnum;
+ scan_ipv6(valp->cnt?valp->vals[i]:valp->val, parts);
+ sortp->ptrs[i] = parts;
+ }
+ break;
+ case IPV4_TYPE:
+ case IPV4NET_TYPE:
+ case MACADDR_TYPE:
+ case INT_TYPE:
+ format = cond_formats[valp->val_type];
+ for (i = 0; i < sortp->num; ++i) {
+ cp = valp->cnt?valp->vals[i]:valp->val;
+ parts = sortp->parts + i * sortp->partnum;
+ switch (sortp->partnum) {
+ case 1:
+ (void) sscanf(cp, format, parts);
+ break;
+ case 2:
+ (void) sscanf(cp, format, parts, parts+1);
+ break;
+ case 3:
+ (void) sscanf(cp, format, parts, parts+1, parts+2);
+ break;
+ case 4:
+ (void) sscanf(cp, format, parts, parts+1, parts+2,
+ parts+3);
+ break;
+ case 5:
+ (void) sscanf(cp, format, parts, parts+1, parts+2,
+ parts+3, parts+4);
+ break;
+ case 6:
+ (void) sscanf(cp, format, parts, parts+1, parts+2,
+ parts+3, parts+4, parts+5);
+ break;
+ }
+ sortp->ptrs[i] = parts;
+ }
+ break;
+ case TEXT_TYPE:
+ case BOOL_TYPE:
+ for (i = 0; i < sortp->num; ++i) {
+ sortp->ptrs[i] = valp->cnt?valp->vals[i]:valp->val;
+ }
+ break;
+ default:
+ printf("Unknown value in switch on line %d\n", __LINE__);
+ exit(-5);
+ }
+ if (sortp->num < 2)
+ return;
+ /* now do a heap sort */
+ /* build heap */
+ /* from left to right, we start with the heap of only one (first) element*/
+ for (i = 2; i <= sortp->num; ++i)
+ {
+ cur = i;
+ do {
+ curp = sortp->ptrs[cur - 1];
+ par = cur >> 1;
+ parp = sortp->ptrs[par - 1];
+ if (sortp->partnum){
+ for(partnum = 0; partnum < sortp->partnum; ++partnum) {
+ if (*((unsigned int *)curp + partnum)>
+ *((unsigned int *)parp + partnum)){
+ res = 1;
+ break;
+ }
+ if (*((unsigned int *)curp + partnum)<
+ *((unsigned int *)parp + partnum)){
+ res = -1;
+ break;
+ }
+ res = 0;
+ }
+ }else{
+ res = strcmp((char *)curp, (char *) parp);
+ }
+ if (res <= 0)
+ break;
+ /* swap them */
+ sortp->ptrs[cur - 1] = parp;
+ sortp->ptrs[par - 1] = curp;
+
+ } while ((cur = par) != 1);
+ }
+ /* convert heap into sorted array */
+ unsorted = sortp->num; /* sortp->num must be >= 2 */
+ while (TRUE) {
+ void *tp;
+ /* root to the sorted part */
+ tp = sortp->ptrs[0];
+ sortp->ptrs[0] = sortp->ptrs[--unsorted];
+ sortp->ptrs[unsorted] = tp;
+ if (unsorted == 1)
+ break;
+ /* push down the new root */
+ par = 1;
+ while(TRUE) {
+ left = par << 1; /* left child */
+ if (left > unsorted)
+ break; /* no children */
+ else {
+ if (left == unsorted) {
+ /* only left child */
+ child = left;
+ } else {
+ /* both children */
+ right = left+1;
+ leftp = sortp->ptrs[left - 1];
+ rightp = sortp->ptrs[right - 1];
+ /* find larger child */
+ if (sortp->partnum){
+ for(partnum = 0; partnum < sortp->partnum; ++partnum) {
+ if (*((unsigned int *)leftp + partnum) >
+ *((unsigned int *)rightp + partnum)) {
+ res = 1;
+ break;
+ }
+ if (*((unsigned int *)leftp + partnum) <
+ *((unsigned int *)rightp + partnum)) {
+ res = -1;
+ break;
+ }
+ res = 0;
+ }
+ }else{
+ res = strcmp((char *)leftp, (char *) rightp);
+ }
+ if (res >= 0) {
+ child = left; /* left is larger or same*/
+ } else {
+ child = right;
+ }
+ }
+ /* compare parent and larger child */
+ parp = sortp->ptrs[par - 1];
+ childp = sortp->ptrs[child - 1];
+ if (sortp->partnum){
+ for(partnum = 0; partnum < sortp->partnum; ++partnum) {
+ if (*((unsigned int *)parp + partnum) >
+ *((unsigned int *)childp + partnum)){
+ res = 1;
+ break;
+ }
+ if (*((unsigned int *)parp + partnum) <
+ *((unsigned int *)childp + partnum)){
+ res = -1;
+ break;
+ }
+ res = 0;
+ }
+ }else{
+ res = strcmp((char *)parp, (char *) childp);
+ }
+ if (res >= 0) {
+ /* done with percolating down, parent larger than child */
+ break;
+ }
+ /* child greater, exchage and continue */
+ sortp->ptrs[par - 1] = childp;
+ sortp->ptrs[child - 1] = parp;
+ par = child;
+ }
+ }
+ }
+}
+
+/* returns FALSE if execution returns non-null,
+ returns TRUE if every excution returns NULL
+*/
+boolean execute_list(vtw_node *cur, vtw_def *def)
+{
+ boolean ret;
+ int status;
+ set_in_exec(TRUE);
+ status = char2val(def, get_at_string(), &validate_value_val);
+ if (status) return FALSE;
+ ret = check_syn(cur);
+ free_val(&validate_value_val);
+ set_in_exec(FALSE);
+ return ret;
+}
+
+
+/*****************************************************
+ make_node - create a node with oper, left, and right
+*****************************************************/
+vtw_node * make_node(vtw_oper_e oper, vtw_node *left,
+ vtw_node *right)
+{
+ vtw_node *ret ;
+ ret = get_node();
+ ret->vtw_node_oper = oper;
+ ret->vtw_node_left = left;
+ ret->vtw_node_right = right;
+ ret->vtw_node_string = NULL;
+ ret->vtw_node_aux = 0;
+ return ret;
+}
+vtw_node *make_str_node0(char *str, vtw_oper_e op)
+{
+ vtw_node *ret;
+ ret = make_node(op, NULL, NULL);
+ ret->vtw_node_string = str;
+ ret->vtw_node_type = TEXT_TYPE;
+ return ret;
+}
+/*****************************************************
+ make_str_node - create a VAL_OP node with str
+*****************************************************/
+vtw_node *make_str_node(char *str)
+{
+ return make_str_node0(str, VAL_OP);
+}
+/*****************************************************
+ make_var_node - create a VAR_OP node with str
+*****************************************************/
+vtw_node *make_var_node(char *str)
+{
+ return make_str_node0(str, VAR_OP);
+}
+/*****************************************************
+ make_val_node - create a VAl_OP node with str
+*****************************************************/
+vtw_node *make_val_node(valstruct *val)
+{
+ vtw_node *ret;
+ assert(val->free_me);
+ ret = make_node(VAL_OP, NULL, NULL);
+ ret->vtw_node_val = *val;
+ val->free_me = FALSE;
+ return ret;
+}
+valstruct str2val(char *cp)
+{
+ valstruct ret;
+ memset(&ret, 0, sizeof(ret));
+ ret.val_type = TEXT_TYPE;
+ ret.val = cp;
+ ret.free_me = TRUE;
+ return ret;
+}
+/****************************************************
+ STATIC FUNCTIONS
+****************************************************/
+
+/**************************************************
+ char2val:
+ convert string into valstruct verifying the type
+ according to def
+****************************************************/
+int char2val(vtw_def *def, char *value, valstruct *valp)
+{
+ int token;
+ char *endp, *cp;
+ int linecnt, cnt;
+ int my_type = def->def_type;
+ boolean first = TRUE;
+
+ memset(valp, 0, sizeof (*valp));
+
+ if (my_type == ERROR_TYPE) {
+ my_type = TEXT_TYPE;
+ }
+
+ if (my_type != TEXT_TYPE && my_type != ERROR_TYPE) {
+ cli_val_len = strlen(value);
+ cli_val_ptr = value;
+ while(1) {
+ token = yy_cli_val_lex();
+ if (token != VALUE) {
+ if (first || token){
+ if (def->def_type_help){
+ set_at_string(value);
+ (void)expand_string(def->def_type_help);
+ printf("%s\n", exe_string);
+ fprintf(stderr, "%s\n", exe_string);
+ } else {
+ print_msg("Wrong type of value in %s, "
+ "need %s\n",
+ m_path.path_buf + m_path.print_offset,
+ type_to_name(my_type));
+ }
+ return -1;
+ }
+ return 0;
+ }
+ if (my_type != get_cli_value_ptr()->val_type) {
+ if (def->def_type_help){
+ set_at_string(value);
+ (void)expand_string(def->def_type_help);
+ printf("%s\n", exe_string);
+ fprintf(stderr, "%s\n", exe_string);
+ } else {
+ print_msg("Wrong type of value in %s, "
+ "need %s\n",
+ m_path.path_buf + m_path.print_offset,
+ type_to_name(my_type));
+ }
+ my_free(get_cli_value_ptr()->val);
+ if (first)
+ return -1;
+ return 0;
+ }
+ if (first) {
+ *valp = *get_cli_value_ptr();
+ get_cli_value_ptr()->free_me = FALSE;
+ first = FALSE;
+ } else {
+ if (def->multi)
+ add_val(valp, get_cli_value_ptr());
+ else {
+ print_msg("Unexpected multivalue in %s\n", m_path.path);
+ free_val(get_cli_value_ptr());
+ }
+ }
+ token = yy_cli_val_lex();
+ if (!token)
+ return 0;
+ if (token != EOL) {
+ print_msg("Badly formed value in %s\n",
+ m_path.path + m_path.print_offset);
+ if (token == VALUE)
+ my_free(get_cli_value_ptr()->val);
+ return 0;
+ }
+ }
+ return 0;
+ }
+ valp->val_type = TEXT_TYPE;
+ valp->free_me = TRUE;
+ /* count lines */
+ linecnt = 0;
+ for (cp = value; *cp; ++cp)
+ if (*cp == '\n')
+ ++linecnt;
+ if (cp != value && cp[-1] != '\n')
+ ++linecnt; /* last non empty non \n terminated string */
+ if (linecnt == 0) /* one empty non terminated string */
+ linecnt = 1;
+ if (linecnt == 1) {
+ valp->val=my_strdup(value, "char2val 1");
+ /*truncate '\n' etc */
+ endp = strchr(valp->val, '\n');
+ if (endp)
+ *endp = 0;
+ } else {
+ valp->cnt = linecnt;
+ cnt = (linecnt + MULTI_ALLOC - 1) / MULTI_ALLOC;
+ cnt *= MULTI_ALLOC;
+ valp->vals = my_malloc(cnt * sizeof(char *), "char2val 2");
+ for(cp = value, cnt = 0; cnt < linecnt; ++cnt) {
+ endp = strchr(cp, '\n');
+ if (endp)
+ *endp = 0;
+ valp->vals[cnt]=my_strdup(cp, "char2val 3");
+ if (endp) {
+ *endp = '\n';
+ cp = endp + 1;
+ } else {
+ /* non '\n' terinated string, must be last line, we are done */
+ ++cnt;
+ assert(cnt == linecnt);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/****************************************************
+ val_comp:
+ compare two values per cond
+ returns result of comparison
+****************************************************/
+boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond)
+{
+ unsigned int left_parts[9], right_parts[9];
+ vtw_type_e val_type;
+ int parts_num, lstop, rstop, lcur, rcur;
+ char const *format;
+ char *lval, *rval;
+ int ret=0, step=0, res=0;
+
+ val_type = left->val_type;
+ if (left->cnt)
+ lstop = left->cnt;
+ else
+ lstop = 1;
+ if (right->cnt)
+ rstop = right->cnt;
+ else
+ rstop = 1;
+
+ for(lcur = 0; lcur < lstop; ++lcur) {
+ if (!lcur && !left->cnt)
+ lval = left->val;
+ else
+ lval = left->vals[lcur];
+ for(rcur = 0; rcur < rstop; ++rcur) {
+ if (!rcur && !right->cnt)
+ rval = right->val;
+ else
+ rval = right->vals[rcur];
+ switch (val_type) {
+ case IPV6_TYPE:
+ parts_num = 8;
+ goto ipv6_common;
+ case IPV6NET_TYPE:
+ parts_num = 9;
+ ipv6_common:
+ scan_ipv6(lval,left_parts);
+ scan_ipv6(rval,right_parts);
+ break;
+ case IPV4_TYPE:
+ case IPV4NET_TYPE:
+ case MACADDR_TYPE:
+ case INT_TYPE:
+ format = cond_formats[val_type];
+ parts_num = cond_format_lens[val_type];
+ (void) sscanf(lval, format, left_parts, left_parts+1,
+ left_parts+2, left_parts+3, left_parts+4,
+ left_parts+5);
+ (void) sscanf(rval, format, right_parts, right_parts+1,
+ right_parts+2, right_parts+3, right_parts+4,
+ right_parts+5);
+ break;
+ case TEXT_TYPE:
+ case BOOL_TYPE:
+ res = strcmp(lval, rval);
+ goto done_comp;
+ default:
+ printf("Unknown value in switch on line %d\n", __LINE__);
+ exit(-5);
+ }
+ /* here to do a multistep int compare */
+ for (step = 0; step < parts_num; ++ step) {
+ if (left_parts[step] > right_parts[step]) {
+ res = 1;
+ break; /* no reason to continue checking other steps */
+ }
+ if (left_parts[step] < right_parts[step]) {
+ res = -1;
+ break; /* no reason to continue checking other steps */
+ }
+ res = 0;
+ }
+ done_comp:
+ if(res > 0) res = 1;
+ else if(res < 0) res = -1;
+ ret = ((res == cond1[cond]) ||
+ (res == cond2[cond]));
+ if (ret && cond == IN_COND) {
+ set_in_cond_tik(rcur); /* for delete */
+ /* one success is enough for right cycle
+ in case of IN_COND, continue left cycle */
+ break;
+ }
+ if (!ret && cond != IN_COND)
+ /* one failure is enough in cases
+ other than IN_COND - go out */
+ return ret;
+ /* in all other cases:
+ (fail & IN_COND) or (success & !IN_COND)
+ contniue checking; */
+ }
+ }
+ return ret;
+}
+
+
+
+/****************************************************
+ check_comp:
+ evaluate comparison node.
+ returns boolean value of result
+****************************************************/
+static boolean check_comp(vtw_node *cur)
+{
+ int ret;
+ int status;
+ valstruct left, right;
+
+ memset(&left, 0 , sizeof(left));
+ memset(&right, 0 , sizeof(right));
+ ret = FALSE; /* in case of status */
+ status = eval_va(&left, cur->vtw_node_left);
+ if (status)
+ goto free_and_return;
+ status = eval_va(&right, cur->vtw_node_right);
+ if (status)
+ goto free_and_return;
+ if(left.val_type != right.val_type) {
+ printf("Different types in comparison\n");
+ goto free_and_return;
+ }
+ ret = val_cmp(&left, &right,cur->vtw_node_aux);
+ free_and_return:
+ if (left.free_me)
+ free_val(&left);
+ if (right.free_me)
+ free_val(&right);
+ return ret;
+}
+
+/******************
+ Change value of var in the file
+
+*****************/
+
+static int write_value_to_file(const char* var_path,const char* value) {
+
+ if(var_path && value) {
+
+ {
+ /*Build directory, if necessary:*/
+ clind_path_ref var_dir_path=clind_path_construct(var_path);
+
+ if(!var_dir_path) bye("Can not construct path %s", var_path);
+ else {
+
+ char* end = clind_path_pop_string(var_dir_path);
+
+ if(!end || strcmp(end,VAL_NAME)) {
+ bye("Wrong end of path: %s (%s)", end,var_path);
+ }
+
+ free(end);end=NULL;
+
+ touch();
+ touch_dir(clind_path_get_path_string(var_dir_path));
+
+ clind_path_destruct(&var_dir_path);
+ }
+ }
+
+ {
+ /*Write to file*/
+ FILE* fp = fopen(var_path, "w");
+ if(!fp) bye("Can not open value file %s", var_path);
+
+ if (fputs(value, fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", var_path);
+
+ fclose(fp);
+
+ }
+ }
+
+ return 0;
+}
+
+static int change_var_value(const char* var_reference,const char* value, int active_dir) {
+
+ int ret=-1;
+
+ if(var_reference && value) {
+
+ char* var_path=NULL;
+
+ clind_path_ref n_cfg_path=NULL;
+ clind_path_ref n_tmpl_path=NULL;
+ clind_path_ref n_cmd_path=NULL;
+
+ if(set_reference_environment(var_reference,
+ &n_cfg_path,
+ &n_tmpl_path,
+ &n_cmd_path,
+ active_dir)==0) {
+
+ clind_val cv;
+
+ memset(&cv,0,sizeof(cv));
+
+ if(clind_config_engine_apply_command_path(n_cfg_path,
+ n_tmpl_path,
+ n_cmd_path,
+ FALSE,
+ &cv,
+ get_cdirp(),
+ get_tdirp(),
+ TRUE)==0) {
+ var_path=cv.value;
+
+ }
+
+ }
+
+ if(n_cfg_path) clind_path_destruct(&n_cfg_path);
+ if(n_tmpl_path) clind_path_destruct(&n_tmpl_path);
+ if(n_cmd_path) clind_path_destruct(&n_cmd_path);
+
+ if(var_path) {
+ ret=write_value_to_file(var_path,value);
+ free(var_path);
+ }
+ }
+
+ return ret;
+}
+
+/****************************************************
+ check_syn:
+ evaluate syntax tree;
+ returns TRUE if all checks are OK,
+ returns FALSE if check fails.
+****************************************************/
+static boolean check_syn_func(vtw_node *cur,const char* func,int line)
+{
+ int status;
+ int ret;
+ int ii;
+
+ switch(cur->vtw_node_oper) {
+ case LIST_OP:
+ ret = check_syn(cur->vtw_node_left);
+ if (!is_in_commit() && cur->vtw_node_aux)
+ ret = TRUE;
+ if (!ret || !cur->vtw_node_right) /* or no right operand */
+ return ret;
+ return check_syn(cur->vtw_node_right);
+ case HELP_OP:
+ ret = check_syn(cur->vtw_node_left);
+ if (ret <= 0){
+ if (expand_string(cur->vtw_node_right->vtw_node_string) == VTWERR_OK) {
+ fprintf(stderr, exe_string);
+ fprintf(stderr, "\n");
+ printf(exe_string);
+ printf("\n");
+ }
+ }
+ return ret;
+
+ case ASSIGN_OP:
+
+ if (is_in_exec()) {
+
+ valstruct right;
+
+ char* var_reference = NULL;
+
+ memset(&right, 0, sizeof(right));
+ status = eval_va(&right, cur->vtw_node_right);
+
+ if (status || right.cnt) { /* bad or multi */
+ if (right.free_me) free_val(&right);
+ return FALSE;
+ }
+
+ var_reference = strdup(cur->vtw_node_left->vtw_node_string+2);
+
+ {
+ int i=0;
+ while(var_reference[i]) {
+ if(var_reference[i]==')') {
+ var_reference[i]=0;
+ break;
+ }
+ i++;
+ }
+ }
+
+ change_var_value(var_reference,right.val,FALSE);
+ change_var_value(var_reference,right.val,TRUE);
+
+ if (right.free_me) free_val(&right);
+
+ if(var_reference) free(var_reference);
+ }
+
+ return TRUE;
+
+ case EXEC_OP:
+ /* for every value */
+ if (in_validate_val) {
+ char *save_at = get_at_string();
+ for(ii = 0; ii < validate_value_val.cnt || ii == 0; ++ii) {
+ set_at_string(validate_value_val.cnt?
+ validate_value_val.vals[ii]:validate_value_val.val);
+ status = expand_string(cur->vtw_node_left->vtw_node_string);
+ if (status != VTWERR_OK) {
+ set_at_string(save_at);
+ return FALSE;
+ }
+ ret = system(exe_string);
+ if (ret) {
+ set_at_string(save_at);
+ return FALSE;
+ }
+ }
+ set_at_string(save_at);
+ return TRUE;
+ }
+ /* else */
+ status = expand_string(cur->vtw_node_left->vtw_node_string);
+ if (status != VTWERR_OK) {
+ return FALSE;
+ }
+ ret = system(exe_string);
+ return !ret;
+
+ case PATTERN_OP: /* left to var, right to pattern */
+ {
+ valstruct left;
+ regex_t myreg;
+ boolean ret;
+ int ii;
+
+ ret = TRUE;
+ status = eval_va(&left, cur->vtw_node_left);
+ if (status) {
+ ret = FALSE;
+ goto free_and_return;
+ }
+ status = regcomp(&myreg, cur->vtw_node_right->vtw_node_string,
+ REG_EXTENDED);
+ if (status)
+ bye("Can not compile regex |%s|, result %d\n",
+ cur->vtw_node_right->vtw_node_string, status);
+ /* for every value */
+ for(ii = 0; ii < left.cnt || ii == 0; ++ii) {
+ status = regexec(&myreg, left.cnt?
+ left.vals[ii]:left.val,
+ 0, 0, 0);
+ if(status) {
+ ret = FALSE;
+ break;
+ }
+ }
+ free_and_return:
+ if (left.free_me)
+ free_val(&left);
+ return ret;
+ }
+
+ case OR_OP:
+ ret = check_syn(cur->vtw_node_left) ||
+ check_syn(cur->vtw_node_right);
+ return ret;
+ case AND_OP:
+ ret = check_syn(cur->vtw_node_left) &&
+ check_syn(cur->vtw_node_right);
+ return ret;
+ case NOT_OP:
+ ret = check_syn(cur->vtw_node_left);
+ return !ret;
+
+ case COND_OP: /* aux field specifies cond type (GT, GE, etc.)*/
+ ret = check_comp(cur);
+ return ret;
+
+ case VAL_OP:
+ printf("VAL op in check_syn\n");
+ exit(-4);
+ case VAR_OP:
+ printf("VAR op in check_syn\n");
+ exit(-4);
+ default:
+ printf("unknown op %d in check_syn\n", cur->vtw_node_oper);
+ exit(-4);
+ }
+}
+
+/*************************************************
+ copy_path:
+ copy path
+ if destination path owns memory, free it
+**************************************************/
+static void copy_path(vtw_path *to, vtw_path *from)
+{
+ if (to->path_buf)
+ my_free(to->path_buf);
+ if (to->path_ends)
+ my_free(to->path_ends);
+ *to = *from;
+ to->path_buf = (char *) my_malloc(from->path_alloc+2, "copy_path1");
+ memcpy(to->path_buf, from->path_buf, to->path_alloc + 1);
+ to->path = to->path_buf + (from->path-from->path_buf);
+ to->path_ends = (int *) my_malloc(to->path_ends_alloc * sizeof(int),
+ "copy_path2");
+ memcpy(to->path_ends, from->path_ends,
+ to->path_ends_alloc * sizeof(int));
+}
+
+/*****************************************************
+ eval_va:
+ converts VAR_OP or VAL_OP node into valstruct
+ in case of VAR_OP we need to find corresponding
+ template node to obtain type.
+
+*****************************************************/
+static int eval_va(valstruct *res, vtw_node *node)
+{
+ char *cp=NULL;
+ char *pathp=NULL;
+ int status=0;
+
+ switch (node->vtw_node_oper) {
+ case VAR_OP:
+
+ {
+ char *endp = 0;
+ clind_path_ref n_cfg_path=NULL;
+ clind_path_ref n_tmpl_path=NULL;
+ clind_path_ref n_cmd_path=NULL;
+
+ pathp = node->vtw_node_string;
+
+ assert(pathp[0]=='$' && pathp[1]=='(');
+ pathp += 2;
+
+ if(pathp[0] == '@' && pathp[1]!='@'){
+ /* this is why we passed at_val all around */
+ *res = validate_value_val;
+ res->free_me = FALSE;
+ return 0;
+ }
+
+ memset(res,0,sizeof(*res));
+
+ if ((endp = strchr(pathp, ')')) == NULL) {
+ printf("invalid VAR_OP [%s]\n", node->vtw_node_string);
+ return VTWERR_BADPATH;
+ }
+
+ *endp = 0;
+
+ if(set_reference_environment(pathp,
+ &n_cfg_path,
+ &n_tmpl_path,
+ &n_cmd_path,
+ is_in_delete_action())==0) {
+
+ clind_val cv;
+
+ memset(&cv,0,sizeof(cv));
+
+ status=clind_config_engine_apply_command_path(n_cfg_path,
+ n_tmpl_path,
+ n_cmd_path,
+ FALSE,
+ &cv,
+ get_cdirp(),
+ get_tdirp(),
+ FALSE);
+
+ if(status==0) {
+ if(cv.value) {
+ res->val_type = cv.val_type;
+ res->free_me = TRUE;
+ res->val = cv.value;
+ }
+ }
+ }
+
+ if(n_cfg_path) clind_path_destruct(&n_cfg_path);
+ if(n_tmpl_path) clind_path_destruct(&n_tmpl_path);
+ if(n_cmd_path) clind_path_destruct(&n_cmd_path);
+
+ *endp = ')';
+
+ return status;
+ }
+
+ case VAL_OP:
+ *res = node->vtw_node_val;
+ res->free_me = FALSE;
+ return 0;
+ case B_QUOTE_OP:
+ {
+ FILE *f;
+ int a_len, len, rd;
+
+ status = expand_string(node->vtw_node_string);
+ if (status != VTWERR_OK) {
+ return FALSE;
+ }
+ f = popen(exe_string, "r");
+ if (!f)
+ return -1;
+#define LEN 24
+ len = 0;
+ cp = my_malloc(LEN,"");
+ a_len = LEN;
+ for(;;){
+ rd = fread(cp + len, 1, a_len - len , f);
+ len += rd;
+ if (len < a_len)
+ break;
+ cp = my_realloc(cp, a_len+LEN, "");
+ a_len += LEN;
+ }
+ cp[len] = 0;
+ pclose(f);
+ memset(res, 0, sizeof (*res));
+ res->val_type = TEXT_TYPE;
+ res->free_me = TRUE;
+ res->val = cp;
+ }
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+/**********************************************************
+ expand_string:
+ expand string replacing var references with the appropriate
+ values, the formed string is collected in the buffer pointed
+ at by the global exe_string. The buffer dynamically allocated
+ and reallocated.
+***********************************************************/
+static int expand_string(char *stringp)
+{
+ char *scanp;
+ char *resp = exe_string;
+ int left = exe_string_len;
+ int my_len;
+ int len;
+
+ scanp = stringp; /* save stringp for printf */
+
+ do{
+
+ if (left <= 1){
+ my_len = resp - exe_string;
+ exe_string_len += EXE_STRING_DELTA;
+ exe_string = my_realloc(exe_string, exe_string_len, "expand_string 1");
+ left += EXE_STRING_DELTA;
+ resp = exe_string + my_len;
+ /* back in business */
+ }
+ if (*scanp != '$') {
+ if(*scanp == '\\' && scanp[1] == '$') {
+ /* escaped $, treat as regular char */
+ ++scanp;
+ }
+ *resp++ = *scanp++;
+ --left;
+ } else {
+
+ char *cp=NULL;
+ boolean my_cp=FALSE;
+
+ if (scanp[1] != '('){
+
+ printf("Badly formed var reference in %s\n", stringp);
+ exit (VTWERR_BADPATH);
+
+ } else if(scanp[2] == '@' && scanp[3] == ')') {
+
+ cp = get_at_string();
+ my_cp = FALSE;
+ scanp += 4;
+
+ } else {
+
+ clind_path_ref n_cfg_path=NULL;
+ clind_path_ref n_tmpl_path=NULL;
+ clind_path_ref n_cmd_path=NULL;
+
+ char *endp;
+
+ endp = strchr(scanp, ')');
+ if (!endp ){
+ return -1;
+ }
+
+ scanp += 2;
+ /* path reference */
+ *endp = 0;
+ if (endp == scanp)
+ bye("Empty path");
+
+ if(set_reference_environment(scanp,
+ &n_cfg_path,
+ &n_tmpl_path,
+ &n_cmd_path,
+ is_in_delete_action())==0) {
+
+ clind_val cv;
+
+ memset(&cv,0,sizeof(cv));
+
+ if(clind_config_engine_apply_command_path(n_cfg_path,
+ n_tmpl_path,
+ n_cmd_path,
+ FALSE,
+ &cv,
+ get_cdirp(),
+ get_tdirp(),
+ FALSE)==0) {
+ cp=cv.value;
+
+ }
+
+ }
+
+ if(n_cfg_path) clind_path_destruct(&n_cfg_path);
+ if(n_tmpl_path) clind_path_destruct(&n_tmpl_path);
+ if(n_cmd_path) clind_path_destruct(&n_cmd_path);
+
+ if(!cp) {
+ cp="";
+ } else {
+ my_cp=TRUE;
+ }
+
+ *endp = ')';
+
+ scanp = strchr(scanp, ')') + 1;
+ }
+ len = strlen(cp);
+ while(len + 1 > left) { /* 1 for termination */
+ my_len = resp - exe_string;
+ exe_string_len += EXE_STRING_DELTA;
+ exe_string = my_realloc(exe_string, exe_string_len, "expand_string 2");
+ left += EXE_STRING_DELTA;
+ resp = exe_string + my_len;
+ /* back in business */
+ }
+
+ strcpy(resp, cp);
+ if(my_cp && cp) free(cp);
+ resp += len;
+ left -= len;
+ }
+
+ } while(*scanp);
+
+ *resp = 0;
+
+ return VTWERR_OK;
+}
+
+/*****************************************************
+ free_sorted:
+ free all memory allocated to sorted
+*****************************************************/
+void free_sorted(vtw_sorted *sortp)
+{
+ if(sortp->ptrs)
+ my_free(sortp->ptrs);
+ if (sortp->parts)
+ my_free(sortp->parts);
+}
+
+
+/*****************************************************
+ free_def:
+ free all memory allocated to def
+*****************************************************/
+void free_def(vtw_def *defp)
+{
+ vtw_act_type act;
+ for(act=0; act<top_act; ++ act)
+ if (defp->actions[act].vtw_list_head)
+ free_node_tree(defp->actions[act].vtw_list_head);
+ if (defp->def_type_help)
+ my_free(defp->def_type_help);
+ if (defp->def_default)
+ my_free(defp->def_default);
+}
+
+/*****************************************************
+ free_node - add node to free list
+*****************************************************/
+static void free_node(vtw_node *node)
+{
+ --node_cnt;
+ ++free_node_cnt;
+ node->vtw_node_left = vtw_free_nodes;
+ vtw_free_nodes = node;
+}
+
+/*****************************************************
+ free_node_tree - add all nodes of the tree to free list
+*****************************************************/
+static void free_node_tree(vtw_node *node)
+{
+ if (node->vtw_node_left)
+ free_node_tree(node->vtw_node_left);
+ if (node->vtw_node_right)
+ free_node_tree(node->vtw_node_right);
+ if (node->vtw_node_string)
+ free_string(node->vtw_node_string);
+ if (node->vtw_node_val.free_me)
+ free_val(&(node->vtw_node_val));
+ free_node(node);
+}
+
+static void free_path(vtw_path *path)
+{
+ if (path->path_ends)
+ my_free(path->path_ends);
+ if (path->path_buf) {
+ my_free(path->path_buf);
+ }
+}
+
+static void free_reuse_list()
+{
+ vtw_node *next;
+ int cnt = 0;
+
+ while (vtw_free_nodes) {
+ next = vtw_free_nodes->vtw_node_left;
+ my_free(vtw_free_nodes);
+ ++cnt;
+ vtw_free_nodes = next;
+ --free_node_cnt;
+ }
+#if DEBUG
+ printf("%d nodes used\n", cnt);
+#endif
+}
+
+/*****************************************************
+ free_val - dealloc allocated memory of valstruct
+*****************************************************/
+void free_val(valstruct *val)
+{
+ int cnt;
+
+ assert(val->free_me);
+ if (val->val)
+ my_free(val->val);
+ for (cnt = 0; cnt < val->cnt; ++ cnt)
+ my_free(val->vals[cnt]);
+ if(val->vals)
+ my_free(val->vals);
+}
+/*****************************************************
+ free_string - dealloc string
+ just free for now, we might do something else later
+*****************************************************/
+static void free_string(char *str)
+{
+ my_free(str);
+}
+
+
+/*****************************************************
+ get_node - take node from free list or allocate
+*****************************************************/
+static vtw_node * get_node(void)
+{
+ vtw_node *ret;
+ if (vtw_free_nodes){
+ ret = vtw_free_nodes;
+ vtw_free_nodes = vtw_free_nodes->vtw_node_left;
+ --free_node_cnt;
+ } else {
+ ret = my_malloc(sizeof(vtw_node), "New node");
+ }
+ ++node_cnt;
+ memset(ret, 0, sizeof(vtw_node));
+ return ret;
+}
+
+/****************************************************
+ get_value:
+ for a given path (*path) verify that value exists,
+ open it and read it. The pointer to allocated
+ memory is returned in valpp. It is responsibility
+ of a caller to release memory.
+ Returns:
+******************************************************/
+int get_value(char **valpp, vtw_path *pathp)
+{
+ struct stat statbuf;
+ int status = VTWERR_OK;
+ char const *err = NULL;
+ FILE *in = NULL;
+ char *valp;
+ int readcnt;
+
+
+ /* find value */
+ *valpp = 0;
+ push_path(pathp, val_name);
+
+ if (lstat(pathp->path, &statbuf) < 0) {
+ err = "no value file in [%s]\n";
+ goto bad_path;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
+ err = "no value file in [%s]\n";
+ goto bad_path;
+ }
+ in = fopen(pathp->path, "r");
+ if (!in) {
+ err = "Can not open value file in [%s]\n";
+ goto bad_path;
+ }
+ valp = my_malloc(statbuf.st_size + 1, "get_value");
+ readcnt = fread(valp, 1, statbuf.st_size, in);
+ if (readcnt != statbuf.st_size) {
+ my_free(valp);
+ cli_val_alloc = 0;
+ err = "Error reading value file in [%s]\n";
+ goto bad_path;
+ }
+ valp[statbuf.st_size] = 0;
+ /* remove \n at the line end */
+ if (valp[statbuf.st_size - 1] == '\n')
+ valp[statbuf.st_size - 1] = 0;
+ *valpp = valp;
+ status = 0;
+pop:
+ if (in)
+ fclose(in);
+ pop_path(pathp);
+ if (err) {
+ fprintf(stderr, err, pathp->path_buf + m_path.print_offset);
+ printf(err, pathp->path_buf + m_path.print_offset);
+ }
+ return status;
+ bad_path:
+ status = VTWERR_BADPATH;
+ goto pop;
+}
+
+int get_value_to_at_string(vtw_path *pathp) {
+
+ char* at=NULL;
+ int status = get_value(&at,pathp);
+
+ if(status==0) {
+ set_at_string(at);
+ } else {
+ set_at_string(NULL);
+ }
+
+ return status;
+}
+
+/*****************************************************
+ out_of_memeory
+ print out of memory message and exit
+*****************************************************/
+void out_of_memory()
+{
+ printf("\n\t!!! OUT OF MEMORY !!!\n");
+ exit(-1);
+}
+
+/*************************************************
+ init_path:
+ init path, exit if not able (out_of_memory)
+**************************************************/
+void init_path(vtw_path *path, const char *root)
+{
+ long path_len;
+ memset(path, 0, sizeof(vtw_path));
+ path_len = pathconf(root, _PC_PATH_MAX);
+ if (path_len < 0)
+ path_len = PATH_DELTA;
+ path->path_alloc = path_len - 2;
+ /* 1 byte for null termination, and 1 byte for '/' */
+ path->path_buf = path->path =
+ (char *)my_malloc(path_len, "init_path 1");
+ strcpy(path->path, root);
+ path->path_len = strlen(root);
+ path->path_ends = (int *)my_malloc(ENDS_ALLOC * sizeof(int *),
+ "init_path 2");
+ path->path_lev = 1;
+ path->path_ends[0] = path->path_len;
+ path->path_ends_alloc = ENDS_ALLOC;
+}
+
+/*****************************************************
+ pop_path - shorten path by one segment
+*****************************************************/
+
+void pop_path(vtw_path *path)
+{
+ if (--path->path_lev < 1) {
+ INTERNAL;
+ }
+ path->path_len = path->path_ends[path->path_lev - 1];
+ path->path_buf[path->path_len] = 0;
+}
+void warrant_path(vtw_path *path, int len)
+{
+ int delta = path->path - path->path_buf;
+
+ while(path->path_alloc - path->path_len < len + 1){
+ path->path_buf = (char *)my_realloc(path->path_buf,
+ path->path_alloc +
+ PATH_DELTA, "push_path 1");
+ path->path_alloc += PATH_DELTA;
+ }
+ path->path = path->path_buf + delta;
+}
+/*****************************************************
+ push_path - extend path by '/' and one new segment
+*****************************************************/
+void push_path(vtw_path *path, char *segm)
+{
+ int len;
+ char *cp;
+ char *pp;
+
+ for(cp=segm, len=0;*cp;++cp, ++len)
+ if(*cp=='%' || *cp=='/')
+ len +=2;
+ warrant_path(path, len + 1);
+ path->path_buf[path->path_len] = '/';
+ path->path_buf[++path->path_len] = 0;
+ for(pp=path->path_buf + path->path_len,cp=segm;
+ *cp;++cp, ++pp)
+ if(*cp=='%') {
+ pp[0] = '%';
+ pp[1] = '2';
+ pp[2] = '5';
+ pp += 2;
+ }else if (*cp == '/') {
+ pp[0] = '%';
+ pp[1] = '2';
+ pp[2] = 'F';
+ pp += 2;
+ }else
+ *pp = *cp;
+ *pp = 0;
+
+ path->path_len += len;
+ if (path->path_lev == path->path_ends_alloc){
+ path->path_ends_alloc += ENDS_ALLOC;
+ path->path_ends = (int *)my_realloc(path->path_ends,
+ sizeof(int *)*path->path_ends_alloc, "puhs_path 2");
+ }
+ path->path_ends[path->path_lev++] = path->path_len;
+}
+
+/****************************************************
+ scan_ipv6:
+ scans ipv6 or ipv6net pointed by val
+ and returns as array of integers pointed
+ by parts
+***************************************************/
+static void scan_ipv6(char *val, unsigned int *parts)
+{
+ int num = 0;
+ int num_cnt = 0;
+ int dot_dot_pos = -1;
+ int dot_cnt = 0;
+ char c;
+ char *p;
+ int base = 16;
+ int total = 8;
+ int gap;
+
+ for (p = val;TRUE; ++p) {
+ switch ((c = *p)) {
+ case '.':
+ if (dot_cnt == 0) {
+ /* turn out it was decimal, convert our wrong
+ hex interpretation;
+ decimal may not have more than 3 digits */
+ num = (num/256)*100 + (num%256)/16*10 + num%16;
+ base = 10;
+ }
+ ++dot_cnt;
+ break;
+ case ':':
+ if (p[1] == ':'){
+ ++p;
+ dot_dot_pos = num_cnt + 1;
+ }
+ break;
+ case '/':
+ base = 10;
+ total = 9;
+ break;
+ case 0:
+ break;
+ default:
+ /* must be a digit */
+ c = tolower(*p);
+ if (isdigit(c))
+ num = num * base + c - '0';
+ else
+ num = num * base + c - 'a' + 10;
+ continue;
+ }
+ /* close the number */
+ /* the case of "::234: etc "
+ handled automatically as 0::234
+ with allowing :: to represent 0 or more
+ groups instead of 1 or more */
+
+ parts[num_cnt] = num;
+ num = 0;
+ ++num_cnt;
+ /* combine two decimal if needed */
+ if (dot_cnt == 2 || (dot_cnt == 3 && (c == 0 || c == '/'))) {
+ --num_cnt;
+ parts[num_cnt - 1] = parts[num_cnt - 1] * 256 + parts[num_cnt];
+ }
+ if (*p == 0)
+ break;
+ }
+ /* replace '::' with 0s */
+ if (dot_dot_pos != -1 && total != num_cnt) {
+ int i;
+ gap = total - num_cnt;
+ if (dot_dot_pos != num_cnt)
+ memmove(parts+dot_dot_pos+gap, parts+dot_dot_pos,
+ (num_cnt-dot_dot_pos)*sizeof(int));
+ for (i = 0; i<gap; ++i)
+ parts[dot_dot_pos+i] = 0;
+ }
+}
+
+/***************************************************
+ switch_path:
+ switch m_path between mcd and ccd directories -
+ modified (UNIONFS) configuration and changed
+ (real) configuration
+****************************************************/
+void switch_path(first_seg *segp)
+{
+ memcpy(m_path.path_buf + segp->f_segoff, segp->f_segp,
+ segp->f_seglen);
+ m_path.path = m_path.path_buf + segp->f_segoff;
+}
+
+/*************************************************
+ validate_value:
+ validates value against type and syntax
+ return TRUE if OK, FALSE otherwise
+**************************************************/
+boolean validate_value(vtw_def *def, char *cp)
+{
+ int status;
+ boolean ret=TRUE;
+
+ /* prepare cur_value */
+ set_at_string(cp);
+ status = char2val(def, cp, &validate_value_val);
+ if (status != VTWERR_OK)
+ return FALSE;
+ if ((def->def_type!=ERROR_TYPE) &&
+ (validate_value_val.val_type != def->def_type)) {
+ if (def->def_type_help){
+ (void)expand_string(def->def_type_help);
+ printf("%s\n", exe_string);
+ fprintf(stderr, "%s\n", exe_string);
+ } else {
+ printf("Incorrect type of %s, need %s\n",
+ cp, type_to_name(def->def_type));
+ }
+ ret = FALSE;
+ goto validate_value_free_and_return;
+ }
+ ret = TRUE;
+ if (def->actions && def->actions[syntax_act].vtw_list_head){
+ in_validate_val = TRUE;
+ ret = check_syn(def->actions[syntax_act].vtw_list_head);
+ in_validate_val = FALSE;
+ }
+ validate_value_free_and_return:
+ free_val(&validate_value_val);
+ return ret;
+}
+
+
+int cli_val_read(char *buf, int max_size)
+{
+ int len;
+
+ if (cli_val_len > max_size)
+ len = max_size;
+ else
+ len = cli_val_len;
+ if (len) {
+ (void)memcpy(buf, cli_val_ptr, len);
+ cli_val_len -= len;
+ cli_val_ptr += len;
+ }
+ return len;
+}
+/*==========================================================*/
+/* MEMORY */
+/*==========================================================*/
+
+void *my_malloc(size_t size, const char *name)
+{
+ return malloc(size);
+}
+void *my_realloc(void *ptr, size_t size, const char *name)
+{
+ return realloc(ptr, size);
+}
+
+void my_free(void *ptr)
+{
+ free(ptr);
+}
+
+/*************************************************
+ my_strdup:
+ do a strdup,
+ exit on no memory
+**************************************************/
+char *my_strdup(const char *s, const char *name)
+{
+ return strdup(s);
+}
+
+void done()
+{
+ free_reuse_list();
+ free_path(&t_path);
+ free_path(&m_path);
+ if (exe_string)
+ my_free(exe_string);
+}
+void mark_paths(vtw_mark *markp)
+{
+ markp->m_lev = m_path.path_lev;
+ markp->t_lev = t_path.path_lev;
+}
+void restore_paths(vtw_mark *markp)
+{
+ while(markp->m_lev < m_path.path_lev)
+ pop_path(&m_path);
+ while(markp->t_lev < t_path.path_lev)
+ pop_path(&t_path);
+}
+
+void touch()
+{
+ char *command;
+ command = my_malloc(strlen(get_mdirp()) + 20, "delete");
+ sprintf(command, "touch %s/%s", get_mdirp(), MOD_NAME);
+ system(command);
+ free(command);
+}
+
+char *type_to_name(vtw_type_e type) {
+ switch(type) {
+ case INT_TYPE: return("u32");
+ case IPV4_TYPE: return("ipv4");
+ case IPV4NET_TYPE: return("ipv4net");
+ case IPV6_TYPE: return("ipv6");
+ case IPV6NET_TYPE: return("ipv6net");
+ case MACADDR_TYPE: return("macaddr");
+ case DOMAIN_TYPE: return("domain");
+ case TEXT_TYPE: return("text");
+ case BOOL_TYPE: return("bool");
+ default: return("unknown");
+ }
+}
+
+#if 1
+void
+dump_log(int argc, char **argv)
+{
+}
+#else
+void dump_log(int argc, char **argv)
+{
+ int i;
+ int len;
+ char *cp;
+
+ len = 0;
+ for (i=0; i<argc;++i)
+ len += strlen(argv[i]);
+ len += argc;
+ cp = my_malloc(len, "dump_log");
+ len = 0;
+ for (i=0; i<argc;++i){
+ strcpy(cp+len, argv[i]);
+ len += strlen(argv[i]);
+ cp[len]= ' ';
+ ++len;
+ }
+ cp[len-1]=0;
+ printf("Command: %s\n",cp);
+ my_free(cp);
+}
+#endif
+
+/********************* New Dir ****************************/
+
+static int set_reference_environment(const char* var_reference,
+ clind_path_ref *n_cfg_path,
+ clind_path_ref *n_tmpl_path,
+ clind_path_ref *n_cmd_path,
+ int active) {
+
+ if(var_reference && n_cfg_path && n_tmpl_path && n_cmd_path) {
+
+ if(*var_reference=='/') {
+
+ if(is_in_delete_action()) {
+ *n_cfg_path=clind_path_construct(get_adirp());
+ } else {
+ *n_cfg_path=clind_path_construct(get_mdirp());
+ }
+ *n_tmpl_path=clind_path_construct(get_tdirp());
+ *n_cmd_path=clind_path_construct(var_reference);
+
+ } else {
+
+ vtw_path n_vt_path;
+
+ memset(&n_vt_path,0,sizeof(n_vt_path));
+
+ copy_path(&n_vt_path, &m_path);
+ /* switch to MPATH */
+ memcpy(n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff, get_f_seg_m_ptr()->f_segp,
+ get_f_seg_m_ptr()->f_seglen);
+ n_vt_path.path = n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff;
+
+ if(active) {
+
+ vtw_path active_path;
+
+ memset(&active_path,0,sizeof(active_path));
+
+ copy_path(&active_path, &n_vt_path);
+
+ memcpy(active_path.path_buf + get_f_seg_a_ptr()->f_segoff, get_f_seg_a_ptr()->f_segp,
+ get_f_seg_a_ptr()->f_seglen);
+ active_path.path = active_path.path_buf + get_f_seg_a_ptr()->f_segoff;
+
+ *n_cfg_path=clind_path_construct(active_path.path);
+
+ } else {
+
+ *n_cfg_path=clind_path_construct(n_vt_path.path);
+
+ }
+
+ *n_tmpl_path=clind_path_construct(t_path.path);
+ *n_cmd_path=clind_path_construct(var_reference);
+
+ }
+
+ return 0;
+
+ } else {
+
+ return -1;
+
+ }
+}
+
+/**********************************************************/
diff --git a/src/cli_objects.c b/src/cli_objects.c
new file mode 100644
index 0000000..9bb36f0
--- /dev/null
+++ b/src/cli_objects.c
@@ -0,0 +1,343 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "cli_val.h"
+#include "cli_parse.h"
+#include <regex.h>
+
+#include "cli_val_engine.h"
+
+#include "cli_objects.h"
+
+/************************ Storage area: *****************/
+
+static char *at_string=NULL;
+static boolean in_delete_action=FALSE;
+static valstruct cli_value;
+static boolean in_commit=FALSE; /* TRUE if in commit program*/
+static boolean in_exec=FALSE; /* TRUE if in exec */
+static boolean _is_echo=FALSE;
+static boolean _is_silent_msg=FALSE;
+static first_seg f_seg_a;
+static first_seg f_seg_c;
+static first_seg f_seg_m;
+static int in_cond_tik=0;
+
+/******************** Accessors: ************************/
+
+static char at_buffer[1024]={0};
+
+/* the string to use as $(@), must be set
+ before call to expand_string */
+char* get_at_string(void) {
+ if(at_string) {
+ return at_string;
+ } else {
+ return at_buffer;
+ }
+}
+
+void set_at_string(char* s) {
+ if(s!=at_buffer) {
+ at_string=s;
+ } else {
+ at_string=NULL;
+ }
+}
+
+void free_at_string(void) {
+ if(at_string) {
+ if(at_string!=at_buffer) free(at_string);
+ at_string=NULL;
+ }
+}
+
+boolean is_in_delete_action(void) {
+ return in_delete_action;
+}
+
+void set_in_delete_action(boolean b) {
+ in_delete_action=b;
+}
+
+boolean is_in_commit(void) {
+ return in_commit;
+}
+
+void set_in_commit(boolean b) {
+ in_commit=b;
+}
+
+boolean is_in_exec(void) {
+ return in_exec;
+}
+
+void set_in_exec(boolean b) {
+ in_exec=b;
+}
+
+boolean is_echo(void) {
+ return _is_echo;
+}
+
+void set_echo(boolean b) {
+ _is_echo=b;
+}
+
+boolean is_silent_msg(void) {
+ return _is_silent_msg;
+}
+
+void set_silent_msg(boolean b) {
+ _is_silent_msg=b;
+}
+
+valstruct* get_cli_value_ptr(void) {
+ return &cli_value;
+}
+
+first_seg* get_f_seg_a_ptr(void) {
+ return &f_seg_a;
+}
+
+first_seg* get_f_seg_c_ptr(void) {
+ return &f_seg_c;
+}
+
+first_seg* get_f_seg_m_ptr(void) {
+ return &f_seg_m;
+}
+
+int is_in_cond_tik(void) {
+ return in_cond_tik;
+}
+
+void set_in_cond_tik(int ict) {
+ in_cond_tik=ict;
+}
+
+void dec_in_cond_tik(void) {
+ --in_cond_tik;
+}
+
+const char* get_tdirp(void) {
+
+ const char* tdirp=getenv(ENV_T_DIR);
+
+ if (!tdirp)
+ tdirp = DEF_T_DIR;
+ if(!tdirp)
+ tdirp="";
+
+ return tdirp;
+}
+
+const char* get_cdirp(void) {
+ return getenv(ENV_C_DIR);
+}
+
+const char* get_adirp(void) {
+
+ const char* adirp=getenv(ENV_A_DIR);
+
+ if (!adirp)
+ adirp = DEF_A_DIR;
+ if(!adirp)
+ adirp="";
+
+ return adirp;
+}
+
+const char* get_mdirp(void) {
+
+ const char* mdirp=getenv(ENV_M_DIR);
+
+ if(!mdirp)
+ mdirp="";
+
+ return mdirp;
+}
+
+const char* get_tmpp(void) {
+
+ const char* tmpp=getenv(ENV_TMP_DIR);
+
+ if(!tmpp)
+ tmpp="";
+
+ return tmpp;
+}
+
+char* get_elevp(void) {
+
+ static char elevp_buffer[2049];
+ static char* elevp=NULL;
+
+ if(elevp==NULL) {
+
+ const char* tmp=getenv(ENV_EDIT_LEVEL);
+
+ if(tmp) {
+ strncpy(elevp_buffer,tmp,sizeof(elevp_buffer)-1);
+ elevp=elevp_buffer;
+ }
+ }
+
+ return elevp;
+}
+
+char* get_tlevp(void) {
+
+ static char tlevp_buffer[2049];
+ static char* tlevp=NULL;
+
+ if(tlevp==NULL) {
+
+ const char* tmp=getenv(ENV_TEMPLATE_LEVEL);
+
+ if(tmp) {
+ strncpy(tlevp_buffer,tmp,sizeof(tlevp_buffer)-1);
+ tlevp=tlevp_buffer;
+ }
+ }
+
+ return tlevp;
+}
+
+/************************* Init ***************************/
+
+void init_edit()
+{
+ int elevlen = 0;
+ int tlevlen = 0;
+
+ init_paths(TRUE);
+ if (!get_elevp())
+ bye("Not in configuration mode");
+ if (!get_tlevp())
+ bye("INTERNAL: environment var |%s| is not set",ENV_TEMPLATE_LEVEL);
+ elevlen = strlen(get_elevp());
+ tlevlen = strlen(get_tlevp());
+ if (elevlen > 0 && get_elevp()[elevlen - 1]=='/') {
+ /* cut off terminateing slash */
+ --elevlen;
+ get_elevp()[elevlen] = 0;
+ }
+ if (elevlen) {
+ char *slashp;
+ char * scanp;
+ if (*get_elevp()!='/')
+ INTERNAL;
+ scanp = get_elevp() + 1;
+ while (TRUE) {
+ slashp = strchr(scanp, '/');
+ if (slashp)
+ *slashp = 0;
+ push_path(&m_path, scanp);
+ if (slashp) {
+ *slashp = '/';
+ scanp = slashp+1;
+ }else
+ break;
+ }
+ }
+ switch_path(MPATH);
+ if (tlevlen > 0 && get_tlevp()[tlevlen - 1]=='/') {
+ /* cut off terminateing slash */
+ --tlevlen;
+ get_tlevp()[tlevlen] = 0;
+ }
+ if (tlevlen) {
+ char *slashp;
+ char * scanp;
+ if (*get_tlevp()!='/')
+ INTERNAL;
+ scanp = get_tlevp() + 1;
+ while (TRUE) {
+ slashp = strchr(scanp, '/');
+ if (slashp)
+ *slashp = 0;
+ push_path(&t_path, scanp);
+ if (slashp) {
+ *slashp = '/';
+ scanp = slashp+1;
+ }else
+ break;
+ }
+ }
+}
+
+void init_paths(boolean for_commit)
+{
+ struct stat statbuf;
+ const char* tdirp = get_tdirp();
+ const char* cdirp = get_cdirp();
+ const char* adirp = get_adirp();
+ const char* mdirp = get_mdirp();
+ const char* tmpp = get_tmpp();
+
+ if (!mdirp || !mdirp[0])
+ bye("Environment variable %s for temp configuration is not set",ENV_M_DIR);
+ if (!cdirp)
+ bye("INTERNAL: environment var |%s| is not set",
+ ENV_C_DIR);
+ if (!tmpp || !tmpp[0])
+ bye("INTERNAL: environment var |%s| is not set",
+ ENV_TMP_DIR);
+ /* make sure that template root is present */
+
+ if (lstat(tdirp, &statbuf) < 0)
+ bye("Template directory |%s| isn't present\n", tdirp);
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+ bye("Template directory |%s| isn't a directory\n", tdirp);
+ /* set paths to current roots */
+ if (for_commit) {
+ int max_len;
+ const char *startp;
+
+ /* make sure that master configuration root is present */
+ if (lstat(adirp, &statbuf) < 0)
+ bye("Master configuration directory |%s| isn't present\n", adirp);
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+ bye("Master configuration directory |%s| isn't a directory\n", adirp);
+
+ get_f_seg_a_ptr()->f_segp = adirp;
+ get_f_seg_c_ptr()->f_segp = cdirp;
+ get_f_seg_m_ptr()->f_segp = mdirp;
+ get_f_seg_a_ptr()->f_seglen = strlen(adirp);
+ get_f_seg_c_ptr()->f_seglen = strlen(cdirp);
+ get_f_seg_m_ptr()->f_seglen = strlen(mdirp);
+ if(get_f_seg_a_ptr()->f_seglen > get_f_seg_m_ptr()->f_seglen) {
+ max_len = get_f_seg_a_ptr()->f_seglen;
+ startp = adirp;
+ }else{
+ max_len = get_f_seg_m_ptr()->f_seglen;
+ startp = mdirp;
+ }
+ if(get_f_seg_c_ptr()->f_seglen > max_len) {
+ max_len = get_f_seg_c_ptr()->f_seglen;
+ startp = cdirp;
+ }
+ get_f_seg_a_ptr()->f_segoff = max_len - get_f_seg_a_ptr()->f_seglen;
+ get_f_seg_c_ptr()->f_segoff = max_len - get_f_seg_c_ptr()->f_seglen;
+ get_f_seg_m_ptr()->f_segoff = max_len - get_f_seg_m_ptr()->f_seglen;
+ init_path(&m_path, startp);
+ switch_path(get_f_seg_c_ptr());
+ m_path.print_offset = max_len;
+ }else
+ init_path(&m_path, mdirp);
+
+ init_path(&t_path, tdirp);
+}
+
+/**********************************************************/
diff --git a/src/cli_objects.h b/src/cli_objects.h
new file mode 100644
index 0000000..12b6b38
--- /dev/null
+++ b/src/cli_objects.h
@@ -0,0 +1,65 @@
+#ifndef CLI_OBJ_H
+#define CLI_OBJ_H
+
+#include "cli_val.h"
+
+#define APATH (get_f_seg_a_ptr())
+#define CPATH (get_f_seg_c_ptr())
+#define MPATH (get_f_seg_m_ptr())
+
+/* names of VYATTA env vars */
+#define ENV_EDIT_LEVEL "VYATTA_EDIT_LEVEL"
+#define ENV_TEMPLATE_LEVEL "VYATTA_TEMPLATE_LEVEL"
+#define ENV_A_DIR "VYATTA_ACTIVE_CONFIGURATION_DIR"
+#define ENV_C_DIR "VYATTA_CHANGES_ONLY_DIR"
+#define ENV_M_DIR "VYATTA_TEMP_CONFIG_DIR"
+#define ENV_T_DIR "VYATTA_CONFIG_TEMPLATE"
+#define ENV_TMP_DIR "VYATTA_CONFIG_TMP"
+#define DEF_A_DIR "/opt/vyatta/config/active"
+#define DEF_T_DIR "/opt/vyatta/share/ofr/template"
+#define ENV_OLD_PS1 "VYATTA_OLD_PS1"
+
+/* the string to use as $(@), must be set
+ before call to expand_string */
+char* get_at_string(void);
+void set_at_string(char* s);
+void free_at_string(void);
+
+boolean is_in_delete_action(void);
+void set_in_delete_action(boolean b);
+
+boolean is_in_commit(void);
+void set_in_commit(boolean b);
+
+boolean is_in_exec(void);
+void set_in_exec(boolean b);
+
+boolean is_echo(void);
+void set_echo(boolean b);
+
+boolean is_silent_msg(void);
+void set_silent_msg(boolean b);
+
+valstruct* get_cli_value_ptr(void);
+
+first_seg* get_f_seg_a_ptr(void);
+first_seg* get_f_seg_c_ptr(void);
+first_seg* get_f_seg_m_ptr(void);
+
+int is_in_cond_tik(void);
+void set_in_cond_tik(int ict);
+void dec_in_cond_tik(void);
+
+const char* get_tdirp(void);
+const char* get_cdirp(void);
+const char* get_adirp(void);
+const char* get_mdirp(void);
+const char* get_tmpp(void);
+
+char* get_elevp(void);
+char* get_tlevp(void);
+
+void init_edit(void);
+void init_paths(boolean for_commit);
+
+#endif /* CLI_OBJ_H */
diff --git a/src/cli_parse.y b/src/cli_parse.y
new file mode 100644
index 0000000..f0e43a8
--- /dev/null
+++ b/src/cli_parse.y
@@ -0,0 +1,206 @@
+%{
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "cli_val.h"
+
+extern int yy_cli_def_lineno;
+static vtw_def *parse_defp;
+static int parse_status;
+static boolean cli_def_type_only=0; /*{ if (cli_def_type_only) YYACCEPT;}*/
+/* XXX: sigh, the -p flag to yacc should do this for us kkkk*/
+#define yystacksize tpltstacksize
+#define yysslim tpltsslim
+
+/* forward prototypes */
+extern int yy_cli_parse_parse();
+extern int yy_cli_def_lex();
+extern void yy_cli_parse_error(const char *);
+static void cli_deferror(const char *);
+ extern FILE *yy_cli_def_in;
+#define YYDEBUG 1
+#define yy_cli_parse_lex yy_cli_def_lex
+%}
+%token EOL
+%token MULTI
+%token TAG
+%token TYPE
+%token HELP
+%token DEFAULT
+%token PATTERN
+%token EXEC
+%token SYNTAX
+%token COMMIT
+%token CHECK
+%left SEMI
+%token <val>VALUE
+%token <type>TYPE_DEF
+%token <strp>VAR
+%token <strp> STRING
+%token <strp> EX_STRING
+%token SYNTAX_ERROR
+%token <action>ACTION
+%left ASSIGN
+%left OR
+%left AND
+%right NOT
+%left COMMA
+%nonassoc <cond>COND
+%token RP
+%token LP
+%type <nodep> val
+%type <nodep> exp
+%type <val> val0
+%type <nodep> action
+%type <nodep> action0
+
+%union {
+ char *strp;
+ valstruct val;
+ vtw_type_e type;
+ vtw_cond_e cond;
+ vtw_node *nodep;
+ vtw_act_type action;
+}
+
+%%
+
+input: tag
+ | EOL input
+ | tag otherinput
+ ;
+
+otherinput: type EOL
+ | cause EOL
+ | otherinput type EOL
+ | otherinput cause EOL
+ | otherinput EOL
+ | syntax_error
+ ;
+
+tag: /* empty */
+ | TAG EOL {parse_defp->tag = TRUE;}
+ | MULTI EOL {parse_defp->multi = TRUE;}
+ ;
+
+type: TYPE TYPE_DEF SEMI STRING
+ { parse_defp->def_type = $2;
+ parse_defp->def_type_help = $4; }
+ ;
+
+
+type: TYPE TYPE_DEF
+ { parse_defp->def_type = $2;
+ }
+ ;
+
+cause: help_cause
+ | default_cause
+ | syntax_cause
+ | ACTION action { append(parse_defp->actions + $1, $2, 0);}
+ ;
+
+help_cause: HELP STRING
+ { parse_defp->def_type_help = $2; /* no semantics for now */
+ }
+
+default_cause: DEFAULT VALUE
+ {
+ if ($2.val_type != parse_defp->def_type)
+ yy_cli_parse_error((const char *)"Bad default\n");
+ parse_defp->def_default = $2.val;
+ }
+default_cause: DEFAULT STRING
+ {
+ if (TEXT_TYPE != parse_defp->def_type)
+ yy_cli_parse_error((const char *)"Bad default\n");
+ parse_defp->def_default = $2;
+ }
+
+syntax_cause: SYNTAX exp {append(parse_defp->actions + syntax_act, $2, 0);}
+ ;
+
+syntax_cause: COMMIT exp {append(parse_defp->actions + syntax_act, $2, 1);}
+ ;
+
+action0: STRING { $$ = make_node(EXEC_OP, make_str_node($1),NULL);}
+ ;
+action: action0
+ | action0 SEMI STRING
+ {$$ = make_node(HELP_OP, $1, make_str_node($3));}
+ | exp
+ ;
+exp: LP exp RP
+ {$$=$2;}
+ | exp AND exp {$$ = make_node(AND_OP,$1,$3);}
+ | exp OR exp {$$ = make_node(OR_OP,$1,$3);}
+ | val COND val
+ {$$ = make_node(COND_OP,$1,$3);$$->vtw_node_aux = $2;}
+ | PATTERN VAR STRING
+ { $$ = make_node(PATTERN_OP,make_var_node($2),
+ make_str_node($3));}
+ | EXEC STRING
+ { $$ = make_node(EXEC_OP,make_str_node($2),NULL);}
+ | NOT exp {$$ = make_node(NOT_OP,$2,NULL);}
+ | exp SEMI STRING
+ {$$ = make_node(HELP_OP, $1, make_str_node($3));}
+ | VAR ASSIGN val
+ {$$ = make_node(ASSIGN_OP, make_var_node($1), $3);}
+ ;
+
+val: VAR {$$ = make_var_node($1);}
+ | val0 {$$ = make_val_node(&($1));}
+ | EX_STRING {$$=make_str_node0($1, B_QUOTE_OP);}
+ ;
+
+val0: VALUE
+
+ | val0 COMMA val0 { add_val(&($1), &($3)); $$=$1; }
+
+ | STRING {$$ = str2val($1);}
+ ;
+
+syntax_error: SYNTAX_ERROR {
+ cli_deferror("syntax error");
+ }
+ ;
+
+
+%%
+char *parse_path;
+int parse_def(vtw_def *defp, char *path, boolean type_only)
+{
+ int status;
+ yy_cli_def_lineno = 1;
+ parse_status = 0;
+ parse_defp = defp;
+ cli_def_type_only = type_only;
+ yy_cli_def_in = fopen(path, "r");
+#if 0
+ yy_cli_parse_debug = 1;
+#endif
+ if (!yy_cli_def_in)
+ return -5;
+ parse_path = path;
+ status = yy_cli_parse_parse(); /* 0 is OK */
+ fclose(yy_cli_def_in);
+ return status;
+}
+static void
+cli_deferror(const char *s)
+{
+ printf("Error: %s in file %s, line %d\n",s, parse_path,
+ yy_cli_def_lineno);
+}
+
+void yy_cli_parse_error(const char *s)
+{
+ cli_deferror(s);
+}
+
+
+
+
+
diff --git a/src/cli_path_utils.c b/src/cli_path_utils.c
new file mode 100644
index 0000000..4045516
--- /dev/null
+++ b/src/cli_path_utils.c
@@ -0,0 +1,535 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** 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) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli path-handling utilities
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <regex.h>
+#include <dirent.h>
+
+#include <string.h>
+
+#include "cli_path_utils.h"
+
+/*********************
+ * Data definitions
+ *
+ *********************/
+
+typedef char* clind_dir_name;
+
+/**
+ * Definition of the path structure to hold all path-like information:
+ */
+
+struct _clind_path_impl {
+
+ int absolute;
+ int path_len;
+ char* path_string;
+ clind_dir_name* path;
+
+};
+
+/******************************
+ * Path utils. We use them
+ * to manipulate the path-like
+ * structures.
+ *
+ ******************************/
+
+static void clind_reset_path_string(clind_path_impl* obj) {
+
+ char* newpath=NULL;
+
+ if(obj->path_len<1) {
+
+ newpath=strdup("");
+
+ } else {
+
+ int i=0;
+
+ if(!obj->absolute || (strlen(obj->path[0])>0 && ((char*)(obj->path[0]))[0]=='/')) {
+ newpath=strdup(obj->path[0]);
+ } else {
+ newpath=(char*)malloc(strlen(obj->path[0])+1+1);
+ newpath[0]='/';
+ strcpy(newpath+1,(char*)(obj->path[0]));
+ }
+
+ for(i=1;i<obj->path_len;i++) {
+ newpath=(char*)realloc(newpath,strlen(newpath)+1+strlen(obj->path[i])+1);
+ strcpy(newpath+strlen(newpath),"/");
+ strcpy(newpath+strlen(newpath),obj->path[i]);
+ }
+ }
+
+ if(obj->path_string==NULL) {
+ obj->path_string=newpath;
+ } else {
+ obj->path_string=(char*)realloc(obj->path_string,strlen(newpath)+1);
+ strcpy(obj->path_string,newpath);
+ free(newpath);
+ }
+}
+
+clind_path_ref clind_path_construct(const char* path) {
+
+ if(!path) return NULL;
+ else {
+
+ const char* delim="/ \t";
+
+ clind_path_impl *obj = (clind_path_impl*)malloc(sizeof(clind_path_impl));
+ char* tokpath=strdup(path);
+ char* token=strtok(tokpath,delim);
+
+ obj->path_len=0;
+ obj->path_string=strdup("");
+ obj->path=NULL;
+
+ while(token) {
+ clind_path_push((clind_path_ref)obj,token);
+ token=strtok(NULL,delim);
+ }
+
+ free(tokpath);
+
+ obj->absolute=(*path=='/');
+
+ clind_reset_path_string(obj);
+
+ return (clind_path_ref)obj;
+ }
+}
+
+void clind_path_destruct(clind_path_ref* path) {
+
+ if(path && *path) {
+
+ clind_path_impl* obj = (clind_path_impl*)(*path);
+
+ if(obj->path_string) {
+ free(obj->path_string);
+ obj->path_string=NULL;
+ }
+
+ if(obj->path) {
+ while(obj->path_len>0) {
+ char* dir_name = (char*)(obj->path[obj->path_len-1]);
+ if(dir_name) {
+ free(dir_name);
+ }
+ obj->path_len--;
+ }
+ free(obj->path);
+ obj->path=0;
+ }
+
+ *path=0;
+ }
+}
+
+clind_path_ref clind_path_clone(const clind_path_ref path) {
+
+ clind_path_ref ret=NULL;
+ if(path) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ ret = clind_path_construct(obj->path_string);
+
+ if(ret) {
+
+ ((clind_path_impl*)ret)->absolute=obj->absolute;
+
+ clind_reset_path_string((clind_path_impl*)ret);
+ }
+ }
+
+ return ret;
+}
+
+int clind_path_get_size(clind_path_ref path) {
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ return obj->path_len;
+ }
+ return 0;
+}
+
+const char* clind_path_get_path_string(clind_path_ref path) {
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ if(obj->path_string) {
+ return obj->path_string;
+ }
+ }
+ return "";
+}
+
+int clind_path_is_absolute(clind_path_ref path) {
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ return obj->absolute;
+ }
+ return 0;
+}
+
+void clind_path_push(clind_path_ref path,const char* dir) {
+
+ if(path && dir && *dir) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+ int absolute=(*dir=='/');
+
+ while(*dir && *dir=='/') dir++;
+
+ if(obj->path_len<=0) {
+
+ obj->path_len=1;
+ obj->absolute=absolute;
+
+ if(obj->path) {
+ free(obj->path);
+ }
+
+ obj->path=(clind_dir_name*)malloc(sizeof(clind_dir_name));
+ obj->path[0]=(clind_dir_name)strdup(dir);
+
+ } else {
+
+ obj->path_len++;
+
+ obj->path=(clind_dir_name*)realloc(obj->path,
+ sizeof(clind_dir_name)*(obj->path_len));
+ obj->path[obj->path_len-1]=strdup(dir);
+ }
+
+ clind_reset_path_string(obj);
+ }
+}
+
+char* clind_path_pop_string(clind_path_ref path) {
+
+ char* ret=NULL;
+
+ if(path) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ if(obj->path_len<=0) {
+ return ret;
+ }
+
+ obj->path_len--;
+
+ if(obj->path) {
+ if(obj->path[obj->path_len]) {
+ ret = obj->path[obj->path_len];
+ obj->path[obj->path_len]=NULL;
+ }
+ }
+
+ if(obj->path_len<1) {
+ obj->absolute=0;
+ }
+
+ clind_reset_path_string(obj);
+ }
+
+ return ret;
+}
+
+int clind_path_pop(clind_path_ref path) {
+
+ int ret=-1;
+
+ char* ps = clind_path_pop_string(path);
+
+ if(ps) {
+ free(ps);
+ ret=0;
+ }
+
+ return ret;
+}
+
+const char* clind_path_last_string(clind_path_ref path) {
+
+ char* ret=NULL;
+
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ if(obj->path && obj->path_len>0) {
+ ret=obj->path[obj->path_len-1];
+ }
+ }
+
+ return ret;
+}
+
+void clind_path_unshift(clind_path_ref path,const char* dir) {
+
+ if(path && dir && *dir) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ int absolute=(*dir=='/');
+
+ while(*dir && *dir=='/') dir++;
+
+ if(obj->path_len<=0) {
+
+ clind_path_push(path,dir);
+
+ } else {
+
+ obj->path_len++;
+
+ obj->path=(clind_dir_name*)realloc(obj->path,
+ sizeof(clind_dir_name)*(obj->path_len));
+ memmove((char*)(obj->path)+sizeof(clind_dir_name),(char*)(obj->path),
+ sizeof(clind_dir_name)*(obj->path_len-1));
+ obj->path[0]=strdup(dir);
+
+ }
+
+ obj->absolute=absolute;
+
+ clind_reset_path_string(obj);
+ }
+}
+
+const char* clind_path_get_string(clind_path_ref path,int index) {
+
+ const char* ret=NULL;
+
+ if(path) {
+ clind_path_impl* obj = (clind_path_impl*)path;
+ if(obj->path && obj->path_len>index) {
+ ret=obj->path[index];
+ }
+ }
+
+ return ret;
+}
+
+const char* clind_path_first_string(clind_path_ref path) {
+ return clind_path_get_string(path,0);
+}
+
+char* clind_path_shift_string(clind_path_ref path) {
+
+ char* ret=NULL;
+
+ if(path) {
+
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ if(obj->path_len<=0) {
+ return ret;
+ }
+
+ obj->path_len--;
+
+ if(obj->path) {
+ if(obj->path[0]) {
+ ret = obj->path[0];
+ obj->path[0]=NULL;
+ }
+
+ memmove((char*)(obj->path),(char*)(obj->path)+sizeof(clind_dir_name),
+ sizeof(clind_dir_name)*obj->path_len);
+ }
+
+ obj->absolute=0;
+
+ clind_reset_path_string(obj);
+ }
+
+ return ret;
+}
+
+int clind_path_shift(clind_path_ref path) {
+
+ int ret=-1;
+
+ char* ps = clind_path_shift_string(path);
+
+ if(ps) {
+ free(ps);
+ ret=0;
+ }
+
+ return ret;
+}
+
+void clind_path_debug_print(clind_path_ref path) {
+
+ if(path) {
+
+ int i=0;
+ clind_path_impl* obj = (clind_path_impl*)path;
+
+ if(obj->path_string) {
+ printf("obj->path_string=%s, obj->path_len=%d,obj->absolute=%d\n",
+ obj->path_string,obj->path_len,obj->absolute);
+ } else {
+ printf("obj->path_string=NULL, obj->path_len=%d,obj->absolute=%d\n",
+ obj->path_len,obj->absolute);
+ }
+
+ if(obj->path) {
+ for(i=0;i<obj->path_len;i++) {
+ if(obj->path[i]) {
+ printf(" obj->path[%d]=%s\n",i,obj->path[i]);
+ } else {
+ printf(" obj->path[%d]=NULL\n",i);
+ }
+ }
+ } else {
+ printf(" obj->path=NULL\n");
+ }
+ }
+}
+
+int clind_file_exists(const char* dir,const char* file) {
+
+ int ret=0;
+
+ if(file) {
+
+ char* fname=strdup(file);
+ struct stat statbuf;
+
+ if(dir) {
+ free(fname);
+ fname=(char*)malloc(strlen(dir)+1+strlen(file)+1);
+ strcpy(fname,dir);
+ strcpy(fname+strlen(fname),"/");
+ strcpy(fname+strlen(fname),file);
+ }
+
+ if (lstat(fname, &statbuf) == 0) {
+ ret=1;
+ }
+
+ free(fname);
+ }
+
+ return ret;
+}
+
+char *clind_unescape(const char *name)
+{
+ const char *cp;
+ char *rcp, *ret;
+ char len;
+
+ for(cp=name, len=0;*cp;++cp, ++len)
+ if(*cp=='%')
+ cp +=2;
+ rcp = ret = malloc(len+1);
+ for(cp=name, len=0;*cp;++cp, ++rcp)
+ if(*cp=='%') {
+ ++cp;
+ if (*cp >='a' && *cp<='f')
+ *rcp = (*cp-'a'+10)*16;
+ else if (*cp >='A' && *cp<='F')
+ *rcp = (*cp-'A'+10)*16;
+ else if (*cp >='0' && *cp<='9')
+ *rcp = (*cp-'0')*16;
+ else {
+ printf("Bad escape in |%s|\n", name);
+ exit(-1);
+ }
+ ++cp;
+ if (*cp >='a' && *cp<='f')
+ *rcp += (*cp-'a'+10);
+ else if (*cp >='A' && *cp<='F')
+ *rcp += (*cp-'A'+10);
+ else if (*cp >='0' && *cp<='9')
+ *rcp += (*cp-'0');
+ else {
+ printf("Bad escape in |%s|\n", name);
+ exit(-1);
+ }
+ }else
+ *rcp = *cp;
+ *rcp = 0;
+ return ret;
+}
+
+char* clind_quote(const char* s) {
+
+ char* ret=NULL;
+ if(s) {
+ int i=0;
+ int len=strlen(s);
+ int sz=0;
+ char SQ='\'';
+
+ ret=(char*)malloc(1+5*len+1+1+10);
+ ret[sz++]=SQ;
+
+ for(i=0;i<len;i++) {
+ if(s[i]==SQ) {
+ ret[sz++]=SQ;/*'\''*/
+ ret[sz++]='\\';
+ ret[sz++]=SQ;
+ ret[sz++]=SQ;
+ } else {
+ ret[sz++]=s[i];
+ }
+ }
+
+ ret[sz++]=SQ;
+ ret[sz]=0;
+ }
+ return ret;
+}
+
+
diff --git a/src/cli_path_utils.h b/src/cli_path_utils.h
new file mode 100644
index 0000000..6cc3be8
--- /dev/null
+++ b/src/cli_path_utils.h
@@ -0,0 +1,72 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** 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) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli path-handling utilities
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(__CLI_PATH_UTILS__)
+#define __CLI_PATH_UTILS__
+
+/*******************
+ * Type definitions
+ *
+ *******************/
+
+typedef struct _clind_path_impl clind_path_impl;
+typedef clind_path_impl* clind_path_ref;
+
+/************************
+ * Path utils
+ *
+ ************************/
+
+clind_path_ref clind_path_construct(const char* path);
+void clind_path_destruct(clind_path_ref* path);
+clind_path_ref clind_path_clone(const clind_path_ref path);
+
+int clind_path_get_size(clind_path_ref path);
+const char* clind_path_get_path_string(clind_path_ref path);
+void clind_path_debug_print(clind_path_ref path);
+int clind_path_is_absolute(clind_path_ref path);
+
+int clind_path_pop(clind_path_ref path);
+char* clind_path_pop_string(clind_path_ref path);
+const char* clind_path_last_string(clind_path_ref path);
+void clind_path_push(clind_path_ref path,const char* dir);
+
+int clind_path_shift(clind_path_ref path);
+char* clind_path_shift_string(clind_path_ref path);
+const char* clind_path_get_string(clind_path_ref path,int index);
+const char* clind_path_first_string(clind_path_ref path);
+void clind_path_unshift(clind_path_ref path,const char* dir);
+
+int clind_file_exists(const char* dir,const char* file);
+
+char *clind_unescape(const char *name);
+char* clind_quote(const char* s);
+
+#endif /* __CLI_PATH_UTILS__ */
diff --git a/src/cli_val.h b/src/cli_val.h
new file mode 100644
index 0000000..8a98a88
--- /dev/null
+++ b/src/cli_val.h
@@ -0,0 +1,206 @@
+#ifndef CLI_DEF_H
+#define CLI_DEF_H
+
+#define BITWISE 0 /* no partial commit */
+
+#define boolean int
+#define TRUE 1
+#define FALSE 0
+
+/* allocation unit for vals in valstruct */
+#define MULTI_ALLOC 5 /* we have room if cnt%MULTI_ALLOC != 0 */
+
+typedef enum {
+ do_del_mode,
+ del_mode,
+ create_mode,
+ update_mode
+}vtw_cmode;
+typedef enum {
+ ERROR_TYPE,
+ INT_TYPE,
+ IPV4_TYPE,
+ IPV4NET_TYPE,
+ IPV6_TYPE,
+ IPV6NET_TYPE,
+ MACADDR_TYPE,
+ DOMAIN_TYPE, /*end of addr types */
+ TEXT_TYPE,
+ BOOL_TYPE
+}vtw_type_e;
+
+typedef enum {
+ EQ_COND = 1,
+ NE_COND,
+ LT_COND,
+ LE_COND,
+ GT_COND,
+ GE_COND,
+ IN_COND,
+ TOP_COND
+}vtw_cond_e;
+/* IN_COND is like EQ for singular compare, but OR for multivalue right operand */
+
+typedef enum {
+ LIST_OP, /* right is next, left is list elem */
+ HELP_OP, /* right is help string, left is elem */
+ EXEC_OP, /* left command string, right help string */
+ PATTERN_OP, /* left to var, right to pattern */
+ OR_OP,
+ AND_OP,
+ NOT_OP,
+ COND_OP, /* aux field specifies cond type (GT, GE, etc.)*/
+ VAL_OP, /* for strings used in other nodes */
+ VAR_OP, /* string points to var */
+ B_QUOTE_OP, /* string points to operand to be executed */
+ ASSIGN_OP /* left to var, right to exp */
+}vtw_oper_e;
+
+typedef struct {
+ vtw_type_e val_type;
+ char *val;
+ int cnt; /* >0 means multivalue */
+ char **vals; /* We might union with val */
+ boolean free_me;
+}valstruct;
+
+typedef struct vtw_node{
+ vtw_oper_e vtw_node_oper;
+ struct vtw_node *vtw_node_left;
+ struct vtw_node *vtw_node_right;
+ char *vtw_node_string;
+ int vtw_node_aux;
+ vtw_type_e vtw_node_type;
+ valstruct vtw_node_val; /* we'll union it later */
+}vtw_node;
+
+typedef struct {
+ vtw_node *vtw_list_head;
+ vtw_node *vtw_list_tail;
+}vtw_list;
+
+typedef struct {
+ int t_lev;
+ int m_lev;
+}vtw_mark;
+
+typedef enum {
+ delete_act,
+ create_act,
+ activate_act,
+ update_act,
+ syntax_act,
+ commit_act,
+ begin_act,
+ end_act,
+ top_act
+}vtw_act_type;
+
+typedef struct {
+ vtw_type_e def_type;
+ char *def_type_help;
+ char *def_default;
+ boolean tag;
+ boolean multi;
+ vtw_list actions[top_act];
+}vtw_def;
+
+typedef struct {
+ const char *f_segp;
+ int f_seglen;
+ int f_segoff;
+} first_seg;
+/* the first segment might be ADIR, or CDIR, or MDIR
+ we reserve space large enough for any one.
+ If the shorter one is used, it right aligned.
+ path points to the start of the current first
+ segment
+*/
+typedef struct {
+ char *path_buf; /* path buffer */
+ char *path; /* path */
+ int path_len; /* path length used */
+ int path_alloc; /* allocated - 1*/
+ int *path_ends; /* path ends for dif levels*/
+ int path_lev; /* how many used */
+ int path_ends_alloc; /* how many allocated */
+ int print_offset; /* for additional optional output information */
+} vtw_path; /* vyatta tree walk */
+
+typedef struct {
+ int num;
+ int partnum;
+ void **ptrs;
+ unsigned int *parts;
+}vtw_sorted;
+
+extern int char2val(vtw_def *def, char *value, valstruct *valp);
+extern int get_value(char **valpp, vtw_path *pathp);
+extern int get_value_to_at_string(vtw_path *pathp);
+extern vtw_node * make_node(vtw_oper_e oper, vtw_node *left,
+ vtw_node *right);
+extern vtw_node *make_str_node(char *str);
+extern vtw_node *make_var_node(char *str);
+extern vtw_node *make_str_node0(char *str, vtw_oper_e op);
+extern void append(vtw_list *l, vtw_node *n, int aux);
+extern int parse_def(vtw_def *defp, char *path, boolean type_only);
+
+extern int yy_cli_val_lex(void);
+extern void cli_val_start(char *s);
+extern void cli_val_done(void);
+extern void init_path(vtw_path *path, const char *root);
+extern void pop_path(vtw_path *path);
+extern void push_path(vtw_path *path, char *segm);
+extern void free_def(vtw_def *defp);
+extern void free_sorted(vtw_sorted *sortp);
+extern void *my_malloc(size_t size, const char *name);
+extern void *my_realloc(void *ptr, size_t size, const char *name);
+
+extern vtw_path m_path, t_path;
+
+/*************************************************
+ GLOBAL FUNCTIONS
+***************************************************/
+extern void add_val(valstruct *first, valstruct *second);
+extern int cli_val_read(char *buf, int max_size);
+extern vtw_node *make_val_node(valstruct *val);
+extern char *my_strdup(const char *s, const char *name);
+extern valstruct str2val(char *cp);
+extern void dump_tree(vtw_node *node, int lev);
+extern void dump_def(vtw_def *defp);
+extern boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond);
+extern void out_of_memory(void);
+extern boolean validate_value(vtw_def *def,
+ char *value);
+extern void internal_error(int line, char *file);
+extern void done(void);
+extern void del_value(vtw_def *defp, char *cp);
+extern void bye(char *msg, ...);
+extern void print_msg(char *msg, ...);
+extern void switch_path(first_seg *seg);
+extern void vtw_sort(valstruct *valp, vtw_sorted *sortp);
+extern void free_val(valstruct *val);
+extern void my_free(void *ptr);
+extern void touch(void);
+extern void dump_log(int argc, char **argv);
+extern char *type_to_name(vtw_type_e type);
+extern boolean execute_list(vtw_node *cur, vtw_def *def);
+extern void touch_dir(const char *dp);
+
+void mark_paths(vtw_mark *markp);
+void restore_paths(vtw_mark *markp);
+
+extern boolean get_config_lock(const char* adirp, const char* lock_name);
+
+#define VTWERR_BADPATH -2
+#define VTWERR_OK 0
+#define TAG_NAME "node.tag"
+#define DEF_NAME "node.def"
+#define VAL_NAME "node.val"
+#define MOD_NAME ".modified"
+#define OPQ_NAME ".wh.__dir_opaque"
+#define LOCK_NAME ".commit.lck"
+
+#define INTERNAL internal_error(__LINE__, __FILE__)
+
+#endif
diff --git a/src/cli_val.l b/src/cli_val.l
new file mode 100644
index 0000000..5856cea
--- /dev/null
+++ b/src/cli_val.l
@@ -0,0 +1,293 @@
+%{
+#include "cli_val.h"
+#include "cli_parse.h"
+#include "cli_objects.h"
+static void make_val_value(vtw_type_e type);
+#define YY_INPUT(buf,result,max_size) (result)=cli_val_read((buf), (max_size))
+%}
+%option noyywrap
+%option nounput
+%option never-interactive
+
+/*
+ * Regular expressions of IP and MAC addresses, URLs, etc.
+ */
+
+/*
+ * IPv4 address representation.
+ */
+RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2})
+RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3}
+RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9])
+RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN}
+
+/*
+ * IPv6 address representation in Augmented Backus-Naur Form (ABNF)
+ * as defined in RFC-2234.
+ * IPv6 address representation taken from RFC-3986:
+ *
+ * IPv6address = 6( h16 ":" ) ls32
+ * / "::" 5( h16 ":" ) ls32
+ * / [ h16 ] "::" 4( h16 ":" ) ls32
+ * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
+ * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
+ * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
+ * / [ *4( h16 ":" ) h16 ] "::" ls32
+ * / [ *5( h16 ":" ) h16 ] "::" h16
+ * / [ *6( h16 ":" ) h16 ] "::"
+ *
+ * h16 = 1*4HEXDIG
+ * ls32 = ( h16 ":" h16 ) / IPv4address
+ * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
+ * dec-octet = DIGIT ; 0-9
+ * / %x31-39 DIGIT ; 10-99
+ * / "1" 2DIGIT ; 100-199
+ * / "2" %x30-34 DIGIT ; 200-249
+ * / "25" %x30-35 ; 250-255
+ */
+
+RE_H16 [a-fA-F0-9]{1,4}
+RE_H16_COLON {RE_H16}":"
+RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4})
+RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32}
+RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32}
+RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32}
+RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32}
+RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32}
+RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32}
+RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32}
+RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16}
+RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::"
+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}
+RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]?
+RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN}
+
+/*
+ * Ethernet MAC address representation.
+ */
+RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5}
+
+/*
+ * URL-related regular expressions.
+ *
+ * Implementation is based on the BNF-like specification from:
+ * - RFC-1738: HTTP, FTP, FILE
+ * - RFC-3617: TFTP
+ * - RFC-3986: update of RFC-1738
+ */
+RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP}
+
+/*
+ * URL schemeparts for IP based protocols.
+ * Representation taken from RFC-1738, and some of it is updated by RFC-3986.
+ *
+ * login = [ user [ ":" password ] "@" ] hostport
+ * hostport = host [ ":" port ]
+ * host = hostname | hostnumber
+ * hostname = *[ domainlabel "." ] toplabel
+ * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit
+ * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit
+ * alphadigit = alpha | digit
+ * hostnumber = digits "." digits "." digits "." digits
+ * port = digits
+ * user = *[ uchar | ";" | "?" | "&" | "=" ]
+ * password = *[ uchar | ";" | "?" | "&" | "=" ]
+ */
+RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT}
+RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})?
+RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL}
+RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]"
+RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+
+RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL}
+RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT}
+RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT}
+RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}
+RE_URL_PORT {RE_URL_DIGITS}
+RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")*
+
+/*
+ * FILE URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * fileurl = "file://" [ host | "localhost" ] "/" fpath
+ */
+RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH}
+
+/*
+ * FTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ]
+ * fpath = fsegment *[ "/" fsegment ]
+ * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ]
+ * ftptype = "A" | "I" | "D" | "a" | "i" | "d"
+ */
+RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)?
+RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})*
+RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")*
+RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d"
+
+/*
+ * HTTP URL regular expression.
+ * Representation taken from RFC-1738.
+ *
+ * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ]
+ * hpath = hsegment *[ "/" hsegment ]
+ * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ]
+ */
+RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)?
+RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})*
+RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")*
+
+/*
+ * TFTP URL regular expression.
+ * Representation taken from RFC-3617.
+ *
+ * tftpURI = "tftp://" host "/" file [ mode ]
+ * mode = ";" "mode=" ( "netascii" / "octet" )
+ * file = *( unreserved / escaped )
+ * host = <as specified by RFC 2732 [3]>
+ * unreserved = <as specified in RFC 2396 [4]>
+ * escaped = <as specified in RFC 2396>
+ */
+RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})?
+RE_URL_TFTP_MODE ";""mode="("netascii"|"octet")
+RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})*
+
+/*
+ * URL-related miscellaneous definitions.
+ * Representation taken from RFC-1738 and from RFC-3986.
+ *
+ * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" |
+ * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" |
+ * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
+ * "y" | "z"
+ * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
+ * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
+ * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
+ * alpha = lowalpha | hialpha
+ * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
+ * "8" | "9"
+ * safe = "$" | "-" | "_" | "." | "+"
+ * extra = "!" | "*" | "'" | "(" | ")" | ","
+ * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`"
+ * punctuation = "<" | ">" | "#" | "%" | <">
+ *
+ *
+ * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
+ * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ * "a" | "b" | "c" | "d" | "e" | "f"
+ * escape = "%" hex hex
+ *
+ * unreserved = alpha | digit | safe | extra
+ * uchar = unreserved | escape
+ * xchar = unreserved | reserved | escape
+ * digits = 1*digit
+ *
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+ * / "*" / "+" / "," / ";" / "="
+ */
+RE_URL_LOWALPHA [a-z]
+RE_URL_HIALPHA [A-Z]
+RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA}
+RE_URL_DIGIT [0-9]
+RE_URL_SAFE "$"|"-"|"_"|"."|"+"
+RE_URL_EXTRA "!"|"*"|"'"|"("|")"|","
+RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`"
+RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<">
+RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"="
+RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f]
+RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG}
+RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA}
+RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE}
+RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE}
+RE_URL_DIGITS {RE_URL_DIGIT}{1,}
+RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"="
+
+
+%%
+:: {
+ make_val_value(IPV6_TYPE);
+ return VALUE;
+ }
+
+true {
+ make_val_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+false {
+ make_val_value(BOOL_TYPE);
+ return VALUE;
+ }
+
+[0-9]+ {
+ make_val_value(INT_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4} {
+ make_val_value(IPV4_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV4NET} {
+ make_val_value(IPV4NET_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6} {
+ make_val_value(IPV6_TYPE);
+ return VALUE;
+ }
+
+{RE_IPV6NET} {
+ make_val_value(IPV6NET_TYPE);
+ return VALUE;
+ }
+
+{RE_MACADDR} {
+ make_val_value(MACADDR_TYPE);
+ return VALUE;
+ }
+
+\\\n /* whitespace */
+
+[ \t]+ /*whitespace */
+\n return EOL;
+. {
+ /* everything else is a syntax error */
+ return SYNTAX_ERROR;
+ }
+
+
+%%
+static void make_val_value(vtw_type_e type)
+{
+ memset(get_cli_value_ptr(), 0, sizeof(valstruct));
+ get_cli_value_ptr()->free_me = TRUE;
+ get_cli_value_ptr()->val = my_strdup(yytext, "cli_val.l");
+ get_cli_value_ptr()->val_type = type;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/cli_val_engine.c b/src/cli_val_engine.c
new file mode 100644
index 0000000..d597c68
--- /dev/null
+++ b/src/cli_val_engine.c
@@ -0,0 +1,881 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** 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) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli handler for the reference variables
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(_GNU_SOURCE)
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <regex.h>
+#include <dirent.h>
+
+#include <string.h>
+
+#include "cli_val_engine.h"
+
+/*********************
+ * Data definitions
+ *
+ *********************/
+
+/**
+ * Special file names:
+ */
+#define VALUE_FILE ("node.val")
+#define NODE_TAG ("node.tag")
+#define NODE_DEF ("node.def")
+
+/**
+ * "Command" definition (element of a variable path):
+ */
+
+typedef struct {
+ clind_cmd_type cmd_type;
+ const char* text[10];
+} cmd_parse_definition;
+
+/**
+ * Structure to hold information about possible command entries:
+ */
+
+static cmd_parse_definition cmd_parse_definitions[] = {
+ { CLIND_CMD_PARENT_VALUE, {"..","@",NULL } },
+ { CLIND_CMD_NEIGHBOR, {"..","*",NULL } },
+ { CLIND_CMD_PARENT, {"..",NULL } },
+ { CLIND_CMD_PARENT, {".","..",NULL } },
+ { CLIND_CMD_CHILD, {".","*",NULL } },
+ { CLIND_CMD_VALUE, {".","@",NULL } },
+ { CLIND_CMD_SELF_NAME, {".",NULL } },
+ { CLIND_CMD_VALUE, {"@",NULL } },
+ { CLIND_CMD_MULTI_VALUE, {"@@",NULL } },
+ { CLIND_CMD_CHILD, {"*",NULL } },
+ { CLIND_CMD_UNKNOWN, {NULL} }
+};
+
+/************************
+ * Cmd utils forward declarations
+ *
+ ************************/
+
+static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd);
+
+/******************************
+ * Variable evaluation engine *
+ * *
+ ******************************/
+
+/**
+ * For a given config path, return the 'value' of the path.
+ * If the path ends with "node.val", then return the file content.
+ * If not, then return the last path element.
+ * If path is empty, or the file is empty, or the file does not exist,
+ * then return NULL.
+ * The user of this function is responsible for the memory deallocation.
+ */
+
+static char** clind_get_current_value(clind_path_ref cfg_path,
+ clind_path_ref tmpl_path,
+ int check_existence,
+ vtw_type_e *val_type,
+ const char* root_tmpl_path,
+ int return_value_file_name,
+ int multi_value,
+ int *ret_size) {
+
+ char** ret=NULL;
+ *ret_size=0;
+
+ if(val_type) *val_type=TEXT_TYPE;
+
+ if(cfg_path && (clind_path_get_size(cfg_path)>0)) {
+
+ clind_path_ref tmpl_path_clone = clind_path_clone(tmpl_path);
+
+ const char* cfg_path_string = clind_path_get_path_string(cfg_path);
+ const char* cfg_end = clind_path_last_string(cfg_path);
+ const char* tmpl_path_string = clind_path_get_path_string(tmpl_path_clone);
+ const char* tmpl_end = clind_path_last_string(tmpl_path_clone);
+
+ /*
+ printf("%s:111.111:%s:%s:%s:%s:%d\n",__FUNCTION__,
+ cfg_path_string,
+ cfg_end,
+ tmpl_path_string,
+ tmpl_end,
+ multi_value);
+ */
+
+ if(cfg_path_string && cfg_end) {
+
+ if(strcmp(cfg_end,VALUE_FILE)==0) {
+
+ /* Value reference: */
+
+ if(return_value_file_name) {
+
+ ret=(char**)realloc(ret,sizeof(char*)*1);
+ ret[0]=strdup(cfg_path_string);
+ *ret_size=1;
+
+ } else {
+
+ FILE* f = fopen(cfg_path_string,"r");
+ if(f) {
+ char buffer[8193];
+ if(multi_value) {
+ while(fgets(buffer, sizeof(buffer)-1,f)) {
+ int len=strlen(buffer);
+ while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) {
+ buffer[len-1]=0;
+ len--;
+ }
+ if(len>0) {
+ ret=(char**)realloc(ret,sizeof(char*)*(*ret_size+1));
+ ret[*ret_size]=strdup(buffer);
+ *ret_size+=1;
+ }
+ }
+ } else {
+ int sz = fread(buffer, 1, sizeof(buffer)-1, f);
+ if(sz>0) {
+ int len=0;
+ buffer[sz]=0;
+ len=strlen(buffer);
+ while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) {
+ buffer[len-1]=0;
+ len--;
+ }
+ ret=(char**)malloc(sizeof(char*)*1);
+ ret[0]=strdup(buffer);
+ *ret_size=1;
+ }
+ }
+ fclose(f);
+ }
+ }
+
+ } else if(return_value_file_name) {
+
+ ret=(char**)realloc(ret,sizeof(char*)*1);
+ ret[0]=(char*)malloc(strlen(cfg_path_string)+1+strlen(VALUE_FILE)+1);
+ strcpy(ret[0],cfg_path_string);
+ strcpy(ret[0]+strlen(ret[0]),"/");
+ strcpy(ret[0]+strlen(ret[0]),VALUE_FILE);
+ *ret_size=1;
+
+ } else {
+
+ struct stat statbuf;
+
+ /* Directory reference: */
+
+ if(!check_existence || (lstat(cfg_path_string, &statbuf) == 0)) {
+ ret=(char**)realloc(ret,sizeof(char*)*1);
+ ret[0]=clind_unescape(cfg_end);
+ *ret_size=1;
+ }
+ }
+
+ if(ret) {
+ if(tmpl_end && (strcmp(tmpl_end,NODE_TAG)==0)) {
+ clind_path_pop(tmpl_path_clone);
+ tmpl_path_string = clind_path_get_path_string(tmpl_path_clone);
+ tmpl_end = clind_path_last_string(tmpl_path_clone);
+ }
+ }
+
+ if(ret && tmpl_path_string && !return_value_file_name) {
+
+ vtw_def def;
+ struct stat statbuf;
+ int fn_node_def_size=strlen(tmpl_path_string)+1+strlen(NODE_DEF)+1;
+ char* fn_node_def=(char*)malloc(fn_node_def_size);
+
+ memset(&def, 0, sizeof(def));
+
+ fn_node_def[0]=0;
+
+ if(*tmpl_path_string!='/' && root_tmpl_path) {
+ fn_node_def_size+=strlen(root_tmpl_path+1);
+ fn_node_def=(char*)realloc(fn_node_def,fn_node_def_size);
+ strcpy(fn_node_def+strlen(fn_node_def),root_tmpl_path);
+ strcpy(fn_node_def+strlen(fn_node_def),"/");
+ }
+
+ strcpy(fn_node_def+strlen(fn_node_def),tmpl_path_string);
+ strcpy(fn_node_def+strlen(fn_node_def),"/");
+ strcpy(fn_node_def+strlen(fn_node_def),NODE_DEF);
+
+ if ((lstat(fn_node_def, &statbuf) == 0)&&
+ (parse_def(&def, fn_node_def, TRUE)==0)) {
+
+ if(def.def_type != ERROR_TYPE) {
+
+ int status=0;
+ valstruct res;
+ int i=0;
+
+ memset(&res,0,sizeof(res));
+
+ for(i=0;i<*ret_size;i++) {
+
+ if(ret[i]) {
+
+ /* return the value in the correct type */
+ status = char2val(&def, ret[i], &res);
+
+ if(status==0) {
+
+ if(val_type) *val_type=res.val_type;
+
+ if(res.free_me && res.val) {
+ free(ret[i]);
+ ret[i]=res.val;
+ }
+ } else {
+ /* Bad value ? */
+ }
+ }
+ }
+ }
+
+ } else {
+
+ while(*ret_size>0) {
+ if(ret[*ret_size-1]) {
+ free(ret[*ret_size-1]);
+ }
+ *ret_size-=1;
+ }
+ free(ret);
+ ret=NULL;
+
+ }
+
+ free(fn_node_def);
+ }
+ }
+
+ clind_path_destruct(&tmpl_path_clone);
+ }
+
+ return ret;
+}
+
+/**
+ * Return TRUE if current node is a multi-node value
+ */
+static int is_multi_node(clind_path_ref tmpl_path) {
+
+ int ret=0;
+
+ if(tmpl_path) {
+
+ const char* t_end = clind_path_last_string(tmpl_path);
+
+ if(t_end && (strcmp(t_end,NODE_TAG)==0)) {
+ ret=1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Return TRUE if current node is node.def
+ */
+static int is_node_def(clind_path_ref tmpl_path) {
+
+ int ret=0;
+
+ if(tmpl_path) {
+
+ const char* t_end = clind_path_last_string(tmpl_path);
+
+ if(t_end && (strcmp(t_end,NODE_DEF)==0)) {
+ ret=1;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * Apply a single command to the configuration path.
+ * cfg_path - absolute configuration path,
+ * tmpl_path - logical template path,
+ * cmd - single reference command from the variable path.
+ * The result is the array of "derived" paths.
+ * result_len output parameter contains the array size.
+ */
+
+static clind_path_ref* clind_config_engine_apply_command(clind_path_ref cfg_path,
+ clind_path_ref tmpl_path,
+ clind_cmd *cmd,
+ int *result_len) {
+ clind_path_ref* ret=NULL;
+
+ if(cfg_path && tmpl_path && result_len && cmd) {
+
+ /*
+ printf("%s:111.111:%s:%s:%d\n",__FUNCTION__,
+ clind_path_get_path_string(cfg_path),
+ clind_path_get_path_string(tmpl_path),
+ cmd->type);
+ */
+
+ switch (cmd->type) {
+
+ case CLIND_CMD_PARENT:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ }
+ }
+
+ break;
+
+ case CLIND_CMD_SELF_NAME:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+ }
+
+ break;
+
+ case CLIND_CMD_CHILD:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ clind_path_push(cfg_path,cmd->value);
+ clind_path_push(tmpl_path,cmd->value);
+ }
+ break;
+
+ case CLIND_CMD_NEIGHBOR:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+
+ clind_path_push(cfg_path,cmd->value);
+ clind_path_push(tmpl_path,cmd->value);
+ }
+
+ break;
+
+ case CLIND_CMD_VALUE:
+
+ {
+ const char* t_path_string = clind_path_get_path_string(tmpl_path);
+ const char* t_end = clind_path_last_string(tmpl_path);
+ const char* c_end = clind_path_last_string(cfg_path);
+
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(t_end && (strcmp(t_end,NODE_TAG)==0)) {
+ /* do nothing, we are there already */
+ } else if(t_path_string && clind_file_exists(t_path_string,NODE_TAG)) {
+ clind_path_push(tmpl_path,NODE_TAG);
+ } else if(c_end && (strcmp(c_end,VALUE_FILE)==0)) {
+ /* do nothing, we are there already */
+ } else {
+ clind_path_push(cfg_path,VALUE_FILE);
+ }
+ }
+
+ break;
+
+ case CLIND_CMD_PARENT_VALUE:
+
+ {
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ }
+
+ break;
+
+ case CLIND_CMD_MULTI_VALUE:
+
+ {
+ const char* cfg_path_string = NULL;
+
+ if(is_multi_node(tmpl_path)) {
+ clind_path_pop(cfg_path);
+ clind_path_pop(tmpl_path);
+ } else if(is_node_def(tmpl_path)) {
+ clind_path_pop(tmpl_path);
+ }
+
+ cfg_path_string = clind_path_get_path_string(cfg_path);
+
+ if(cfg_path_string) {
+
+ const char* c_end = clind_path_last_string(cfg_path);
+
+ if(c_end && (strcmp(c_end,VALUE_FILE)==0)) {
+
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ } else if(clind_file_exists(cfg_path_string,VALUE_FILE)) {
+
+ *result_len=1;
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+ cfg_path = clind_path_clone(cfg_path);
+ ret[0]=cfg_path;
+
+ clind_path_push(cfg_path,VALUE_FILE);
+
+ } else {
+
+ DIR* dir=NULL;
+
+ dir = opendir(cfg_path_string);
+
+ if(dir) {
+
+ *result_len=0;
+
+ ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref)));
+
+ do {
+ struct dirent *de = readdir(dir);
+
+ if(!de) break;
+ else if(de->d_name[0] && de->d_name[0]!='.') {
+ clind_path_ref cfg_path_1 = clind_path_clone(cfg_path);
+ clind_path_push(cfg_path_1,de->d_name);
+ (*result_len)++;
+ ret=(clind_path_ref*)(realloc(ret,*result_len * sizeof(clind_path_ref)));
+ ret[*result_len-1]=cfg_path_1;
+ }
+ } while(1);
+
+ clind_path_push(tmpl_path,NODE_TAG);
+
+ closedir(dir);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * cfg_path - absolute configuration path,
+ * tmpl_path - logical template path,
+ * cmd_path - variable command path.
+ */
+
+int clind_config_engine_apply_command_path(clind_path_ref cfg_path_orig,
+ clind_path_ref tmpl_path_orig,
+ clind_path_ref cmd_path,
+ int check_existence,
+ clind_val* res,
+ const char* root_cfg_path,
+ const char* root_tmpl_path,
+ int return_value_file_name) {
+
+ int ret=-1;
+
+ /*
+ printf("%s:111.111:cfg_path=%s,tmpl_path=%s,cmd_path=%s,rtp=%s\n",__FUNCTION__,
+ clind_path_get_path_string(cfg_path_orig),
+ clind_path_get_path_string(tmpl_path_orig),
+ clind_path_get_path_string(cmd_path),
+ root_tmpl_path);
+ */
+
+ if(cfg_path_orig && tmpl_path_orig && cmd_path && res) {
+
+ /* Command to be processed: */
+ clind_cmd cmd;
+
+ /* Array of configuration pointers. Initially, contains only one
+ element - cfg_path. */
+ clind_path_ref* config_paths=
+ (clind_path_ref*)malloc(sizeof(clind_path_ref)*1);
+
+ /* Size of the array (initially - just one): */
+ int config_paths_size=1;
+
+ /* Clone the input paths to preserve the input objects intact: */
+ clind_path_ref tmpl_path=NULL;
+ clind_path_ref cfg_path=NULL;
+
+ if(clind_path_is_absolute(cmd_path)) {
+ tmpl_path=clind_path_construct(root_tmpl_path);
+ if(!tmpl_path) {
+ return -1;
+ }
+ cfg_path=clind_path_construct(root_cfg_path);
+ if(!cfg_path) {
+ return -1;
+ }
+ } else {
+ cfg_path=clind_path_clone(cfg_path_orig);
+ tmpl_path=clind_path_clone(tmpl_path_orig);
+ }
+
+ res->value=NULL;
+ res->val_type=TEXT_TYPE;
+
+ /* Set the initial array content: */
+ config_paths[0]=cfg_path;
+
+ /* Apply the commands one-by-one: */
+ while(clind_path_get_size(cmd_path)>0 &&
+ (clind_path_shift_cmd(cmd_path,&cmd)==0)) {
+
+ int i=0;
+
+ /* Temporary array to keep the config paths for the next
+ command application: */
+ clind_path_ref* new_config_paths=NULL;
+ int new_config_paths_size=0;
+
+ /* This path contains the template path at the beginning
+ of the cycle: */
+ clind_path_ref tmpl_path_curr=clind_path_clone(tmpl_path);
+
+ for (i=0;i<config_paths_size;i++) {
+
+ int size=0;
+
+ clind_path_ref* new_config_paths_1=NULL;
+
+ if(i==0) {
+ new_config_paths_1=
+ clind_config_engine_apply_command(config_paths[i],
+ tmpl_path,
+ &cmd,
+ &size);
+ } else {
+ clind_path_ref tmpl_path_curr_clone=clind_path_clone(tmpl_path_curr);
+ new_config_paths_1=
+ clind_config_engine_apply_command(config_paths[i],
+ tmpl_path_curr_clone,
+ &cmd,
+ &size);
+ clind_path_destruct(&tmpl_path_curr_clone);
+ }
+
+ if(new_config_paths_1) {
+
+ int j=0;
+
+ for(j=0;j<size;j++) {
+ if(new_config_paths_1[j]) {
+ new_config_paths_size++;
+ new_config_paths=
+ (clind_path_ref*)realloc(new_config_paths,
+ sizeof(clind_path_ref)*
+ new_config_paths_size);
+ new_config_paths[new_config_paths_size-1]=new_config_paths_1[j];
+ }
+ }
+
+ free(new_config_paths_1);
+ }
+
+ clind_path_destruct(&(config_paths[i]));
+ }
+
+ free(config_paths);
+ config_paths=new_config_paths;
+ config_paths_size=new_config_paths_size;
+ clind_path_destruct(&tmpl_path_curr);
+ }
+
+ if(config_paths) {
+
+ char** sarr=NULL;
+ int sarrlen=0;
+
+ vtw_type_e val_type=TEXT_TYPE;
+
+ int i=0;
+
+ for(i=0;i<config_paths_size;i++) {
+
+ if(config_paths[i]) {
+
+ int vallen=0;
+
+ char** valarr=clind_get_current_value(config_paths[i],
+ tmpl_path,
+ check_existence,
+ &val_type,
+ root_tmpl_path,
+ return_value_file_name,
+ /*Last command: */
+ (cmd.type==CLIND_CMD_MULTI_VALUE),
+ &vallen);
+
+ clind_path_destruct(&config_paths[i]);
+
+ if(valarr) {
+
+ int k=0;
+
+ for(k=0;k<vallen;k++) {
+
+ char* s=valarr[k];
+
+ if(s) {
+
+ /* search if we already have it: */
+ int j=0;
+ for(j=0;j<sarrlen;j++) {
+ if(!strcmp(sarr[j],s)) {
+ break;
+ }
+ }
+
+ if(j<sarrlen) {
+ free(s);
+ } else {
+ sarrlen++;
+ sarr=(char**)realloc(sarr,sizeof(char*)*sarrlen);
+ sarr[sarrlen-1]=s;
+ }
+ }
+ }
+ free(valarr);
+ }
+ }
+ }
+
+ free(config_paths);
+
+ if(sarr) {
+
+ ret=0;
+
+ if(sarrlen==1) {
+
+ res->value=sarr[0];
+
+ } else {
+
+ for(i=0;i<sarrlen;i++) {
+
+ if(sarr[i]) {
+
+ char* s = clind_quote(sarr[i]);
+
+ free(sarr[i]);
+ sarr[i]=NULL;
+
+ if(s) {
+
+ if(!res->value) {
+
+ res->value=s;
+
+ } else if(res->value[0]==0) {
+
+ free(res->value);
+ res->value=s;
+
+ } else {
+
+ res->value=(char*)realloc(res->value,
+ strlen(res->value)+1+strlen(s)+1);
+
+ strcpy(res->value+strlen(res->value)," ");
+ strcpy(res->value+strlen(res->value),s);
+
+ free(s);
+ }
+ }
+ }
+ }
+ }
+ free(sarr);
+ }
+ }
+
+ clind_path_destruct(&cmd_path);
+ clind_path_destruct(&cfg_path);
+ clind_path_destruct(&tmpl_path);
+ }
+
+ return ret;
+}
+
+/******************************
+ * Cmd utils.
+ *
+ ******************************/
+
+static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd) {
+
+ int ret=-1;
+
+ if(cmd) {
+
+ cmd->type = CLIND_CMD_UNKNOWN;
+ cmd->value[0]=0;
+
+ if(path && clind_path_get_size(path)>0) {
+
+ int i=0;
+ int done=0;
+
+ while(cmd_parse_definitions[i].text!=NULL && cmd_parse_definitions[i].text[0]!=NULL) {
+
+ int j=0;
+
+ while(cmd_parse_definitions[i].text[j]) {
+ const char* str = clind_path_get_string(path,j);
+ if(str) {
+ if(!strcmp(cmd_parse_definitions[i].text[j],"*")) {
+ if(*str!='.' && *str!='@') {
+ j++;
+ strncpy(cmd->value,str,sizeof(cmd->value)-1);
+ continue;
+ }
+ } else if(!strcmp(cmd_parse_definitions[i].text[j],str)) {
+ j++;
+ strncpy(cmd->value,str,sizeof(cmd->value)-1);
+ continue;
+ }
+ }
+ j=0;
+ break;
+ }
+
+ if(j<1) {
+ i++;
+ continue;
+ } else {
+ done=1;
+ }
+
+ cmd->type = cmd_parse_definitions[i].cmd_type;
+
+ while(j) {
+ clind_path_shift(path);
+ j--;
+ }
+
+ break;
+ }
+
+ if(done) {
+ ret=0;
+ }
+ }
+ }
+
+ return ret;
+}
+
diff --git a/src/cli_val_engine.h b/src/cli_val_engine.h
new file mode 100644
index 0000000..0f32276
--- /dev/null
+++ b/src/cli_val_engine.h
@@ -0,0 +1,86 @@
+
+/************************************************************************
+
+ Module: cli
+
+ **** 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) 2007 Vyatta, Inc.
+ All Rights Reserved.
+
+ Author: Oleg Moskalenko
+ Date: 2007
+ Description: "new" cli handler for the reference variables
+
+ **** End License ****
+
+*************************************************************************/
+
+#if !defined(__CLI_VAL_ENGINE__)
+#define __CLI_VAL_ENGINE__
+
+#include "cli_path_utils.h"
+#include "cli_val.h"
+
+/*******************
+ * Type definitions
+ *
+ *******************/
+
+typedef enum {
+
+ CLIND_CMD_UNKNOWN=0, /* ??? */
+ CLIND_CMD_PARENT, /* .. */
+ CLIND_CMD_SELF_NAME, /* . */
+ CLIND_CMD_CHILD, /* <name> */
+ CLIND_CMD_NEIGHBOR, /* ../<name> */
+ CLIND_CMD_VALUE, /* @ */
+ CLIND_CMD_PARENT_VALUE, /* ../@ */
+ CLIND_CMD_MULTI_VALUE /* @@ */
+
+} clind_cmd_type;
+
+typedef struct {
+
+ clind_cmd_type type;
+ char value[1025];
+
+} clind_cmd;
+
+typedef struct {
+
+ vtw_type_e val_type;
+ char* value;
+
+} clind_val;
+
+/********************************
+ * Main command-handling method:
+ *
+ ********************************/
+
+int clind_config_engine_apply_command_path(clind_path_ref cfg_path,
+ clind_path_ref tmpl_path,
+ clind_path_ref cmd_path,
+ int check_existence,
+ clind_val *res,
+ const char* root_cfg_path,
+ const char* root_tmpl_path,
+ int return_value_file_name);
+
+
+
+
+#endif /* __CLI_VAL_ENGINE__*/
diff --git a/src/commit.c b/src/commit.c
new file mode 100644
index 0000000..a136b58
--- /dev/null
+++ b/src/commit.c
@@ -0,0 +1,1364 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+#include "cli_parse.h"
+#include "cli_path_utils.h"
+
+static char def_name[] = DEF_NAME;
+static char tag_name[] = TAG_NAME;
+static char opaque_name[] = OPQ_NAME;
+
+static int fin_commit(boolean ok);
+static boolean commit_value(vtw_def *defp, char *cp,
+ vtw_cmode mode, boolean in_txn);
+static void perform_create_node();
+static void perform_delete_node();
+static void perform_move();
+static boolean commit_delete_child(vtw_def *pdefp, char *child,
+ boolean deleting, boolean in_txn);
+static boolean commit_delete_children(vtw_def *defp, boolean deleting,
+ boolean in_txn);
+static boolean commit_update_children(vtw_def *defp, boolean creating,
+ boolean in_txn, boolean *parent_update);
+
+#if BITWISE
+static void make_dir()
+{
+ struct stat statbuf;
+ if (lstat(m_path.path, &statbuf) < 0) {
+ char *command;
+ command = my_malloc(strlen(m_path.path) + 10, "set");
+ sprintf(command, "mkdir -p %s", m_path.path);
+ system(command);
+ free(command);
+ return;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("directory %s expected, found regular file", m_path.path);
+ }
+ return;
+}
+#endif
+
+/*************************************************
+ validate_dir_for_commit:
+ validate value.value if there is one, validate
+ subdirectries names (for tag directory, and for
+ regular directory);
+ validate subdirectories
+ returns TRUE if OK, FASLE if errors
+ exits with status != 0 in case of parse error
+*/
+static boolean validate_dir_for_commit()
+{
+ struct stat statbuf;
+ int status=0;
+ vtw_def def;
+ boolean def_present=FALSE;
+ boolean value_present=FALSE;
+ int subdirs_number=0;
+ DIR *dp=NULL;
+ struct dirent *dirp=NULL;
+ char *cp=NULL;
+ boolean ret=TRUE;
+ char *uename = NULL;
+
+#ifdef DEBUG
+ printf("validating directory (node_cnt %d, free_node_cnt %d)\n"
+ "t_path |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+
+ /* find definition */
+
+ push_path(&t_path, def_name); /* PUSH 1 */
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* definition present */
+ def_present = TRUE;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+
+ if ((status = parse_def(&def, t_path.path,
+ FALSE))) {
+ exit(status);
+ }
+
+ }
+#ifdef DEBUG1
+ else
+ printf("No definition\n");
+#endif
+ pop_path(&t_path); /* for PUSH 1 */
+
+ /* look at modified stuff */
+ if ((dp = opendir(m_path.path)) == NULL){
+ INTERNAL;
+ }
+ if (def_present && def.tag) {
+ push_path(&t_path, tag_name); /* PUSH 2a */
+ }
+
+ while ((dirp = readdir(dp)) != NULL) {
+
+ if (strcmp(dirp->d_name, ".") == 0 ||
+ strcmp(dirp->d_name, "..") == 0 ||
+ strcmp(dirp->d_name, MOD_NAME) == 0 ||
+ strcmp(dirp->d_name, LOCK_NAME) == 0 ||
+ strcmp(dirp->d_name, opaque_name) == 0 ||
+ strncmp(dirp->d_name, ".wh.", 4) == 0) {
+ continue; /*ignore dot and dot-dot*/
+ }
+
+ subdirs_number++;
+
+ if(uename)
+ my_free(uename);
+
+ uename = clind_unescape(dirp->d_name);
+
+ if (strcmp(uename, VAL_NAME) == 0) {
+
+ value_present=TRUE;
+
+ /* deal with the value */
+ if (!def_present) {
+ printf("There is no definition specified in template\n"
+ "\t%s\n\t - therefore no value permitted\n",
+ t_path.path);
+ ret = FALSE;
+ continue;
+ }
+
+ if (def.tag) {
+ printf("Tag specified in template\n"
+ "\t%s\n\t - therefore no value permitted\n",
+ t_path.path);
+ ret = FALSE;
+ continue;
+ }
+
+ /* value is OK */
+ /* read it */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status == VTWERR_OK){
+#ifdef DEBUG1
+ printf("Validating value |%s|\n"
+ "for path %s\n", cp, m_path.path);
+#endif
+ status = validate_value(&def, cp);
+ ret = ret && status;
+ }
+ if (cp)
+ my_free(cp);
+ continue;
+ }
+
+ push_path(&m_path, uename); /* PUSH 3 */
+ if (lstat(m_path.path, &statbuf) < 0) {
+ printf("Can't read directory %s\n",
+ m_path.path);
+ ret = FALSE;
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ printf("Non directory file %s\n", m_path.path);
+ ret = FALSE;
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+
+ if (def_present && def.tag) {
+ /* do not push t_path, it is already pushed above */
+ /* validate dir name against definition */
+ boolean res = validate_value(&def, uename);
+ value_present=TRUE;
+ if (!res) {
+ ret = FALSE;
+ /* do not go inside bad directory */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ } else {
+ push_path(&t_path, uename); /* PUSH 2b the same as PUSH 2a */
+ if (lstat(t_path.path, &statbuf) < 0) {
+ printf("No such template directory (%s)\n"
+ "for directory %s",
+ t_path.path, m_path.path);
+ ret = FALSE;
+ pop_path(&t_path); /* for PUSH 2b */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ printf("Non directory file %s\n", m_path.path);
+ ret = FALSE;
+ pop_path(&t_path); /* for PUSH 2b */
+ pop_path(&m_path); /* for PUSH 3 */
+ continue;
+ }
+ }
+
+ status = validate_dir_for_commit();
+ ret = ret && status;
+ pop_path(&m_path); /* for PUSH 3 */
+ if (!def_present || !def.tag)
+ pop_path(&t_path); /* for PUSH 2b */
+
+ }
+ status = closedir(dp);
+ if (status)
+ bye("Cannot close dir %s\n", m_path.path);
+
+ if(!value_present && def_present && !def.tag) {
+ ret = ret && validate_value(&def, "");
+ }
+
+ if (def_present && def.tag)
+ pop_path(&t_path); /* for PUSH 3a */
+ if (def_present)
+ free_def(&def);
+#ifdef DEBUG
+ printf("directory done(node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+ if (uename)
+ my_free(uename);
+ return ret;
+}
+
+/***************************************************
+ main:
+ main function (for now)
+***************************************************/
+
+int main(int argc, char **argv)
+{
+
+ boolean status;
+ char *mod;
+ struct stat statbuf;
+ int st;
+ boolean update_pending = FALSE;
+
+ set_in_commit(TRUE);
+ dump_log( argc, argv);
+ init_paths(TRUE);
+ mod = my_malloc(strlen(get_mdirp()) + strlen(MOD_NAME)+2, "COMMIT");
+ sprintf(mod, "%s/%s", get_mdirp(), MOD_NAME);
+ st = lstat(mod, &statbuf);
+ my_free(mod);
+ if (st < 0 ) {
+ bye("No configuration changes to commit\n");
+ exit(-1);
+ }
+
+ if(!get_config_lock(get_adirp(), LOCK_NAME)) {
+ bye("Configuration is locked\n");
+ }
+
+ status = validate_dir_for_commit();
+ if (status == TRUE) {
+ switch_path(CPATH);
+ status = commit_delete_children(NULL, FALSE, FALSE);
+ }
+ if (status == TRUE)
+ status = commit_update_children(NULL, FALSE, FALSE, &update_pending);
+ fin_commit(status);
+
+ done();
+
+ return (status == TRUE) ? 0 : 1;
+}
+
+/*************************************************************
+ perform_create_node -
+ remove node and descendent from woring path
+ and create a new node
+*************************************************************/
+static void perform_create_node()
+{
+#if BITWISE
+ static char format[]="rm -f -r %s;mkdir %s";
+ char *command;
+ switch_path(APATH);
+ command = my_malloc(2 * strlen(m_path.path) + sizeof(format),
+ "commit_create");
+ sprintf(command, format, m_path.path, m_path.path);
+ system(command);
+ my_free(command);
+ switch_path(CPATH);
+#endif
+ return;
+}
+
+
+/*************************************************************
+ perform_delete_node -
+ delete node in current path
+*************************************************************/
+static void perform_delete_node()
+{
+#if BITWISE
+ static char format[]="rm -f -r %s";
+ char *command;
+ command = my_malloc(strlen(m_path.path) + sizeof(format),
+ "commit_delete");
+ sprintf(command, format, m_path.path);
+ system(command);
+ my_free(command);
+#endif
+ return;
+}
+static void perform_move()
+{
+#if BITWISE
+ static char format[] = "rm -r -f %s;mkdir %s;mv %s/" VAL_NAME " %s";
+ char *a_path;
+ char *command;
+ switch_path(APATH);
+ a_path = my_strdup(m_path.path, "");
+ switch_path(CPATH);
+ command = my_malloc(sizeof(format)+3*strlen(a_path)+strlen(m_path.path),"");
+ sprintf(command, format, a_path, a_path, m_path.path, a_path);
+ system(command);
+ my_free(command);
+ my_free(a_path);
+#endif
+ return;
+}
+
+/*************************************************
+ commit_update_child:
+ preconditions: t_path and c_path set up for parent
+ pdefp (IN) - parent definition (may be NULL)
+ child (IN) - name of the child
+ creating (IN) - mode of commiting (update or create)
+ update_parent (OUT) - unfulfilled update request
+ commit child and its descendants
+ returns TRUE if no errors
+ exit if error during execution
+*/
+boolean commit_update_child(vtw_def *pdefp, char *child,
+ boolean creating, boolean in_txn, boolean *update_parent)
+{
+ boolean update_pending = FALSE;
+ boolean multi_tag = FALSE;
+ struct stat statbuf;
+ int status;
+ vtw_def def;
+ vtw_def *my_defp; /*def to be given to my children */
+ vtw_def *act_defp;/*def to be used for actions */
+ char *cp;
+ vtw_mark mark;
+ vtw_act_type act;
+ boolean do_end, ok, do_begin = FALSE, do_txn = FALSE;
+
+ set_at_string(NULL);
+
+ ok = TRUE;
+#ifdef DEBUG
+ printf("commiting directory (node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, opaque_name) == 0)
+ return TRUE;
+
+ /* deleted subdirectory */
+ if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */
+ /* ignore */
+ return TRUE;
+ }
+ mark_paths(&mark);
+ if (!creating) {
+ /* are we marked with opaque */
+ push_path(&m_path, child);
+ push_path(&m_path, opaque_name);
+ if (lstat(m_path.path, &statbuf) >= 0 && !creating) {
+ creating = TRUE;
+ }
+ pop_path(&m_path);
+ pop_path(&m_path);
+ }
+ /* find our definition */
+ if (pdefp && pdefp->tag) {
+ my_defp = NULL;
+ act_defp = pdefp;
+ push_path(&t_path,tag_name);
+ } else {
+ push_path(&t_path, child);
+ push_path(&t_path, def_name);
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* defniition present */
+ act_defp = my_defp = &def;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ my_defp = act_defp = &def;
+ if (def.tag)
+ multi_tag = TRUE;
+ } else {
+ my_defp = act_defp = NULL;
+ }
+ pop_path(&t_path); /*def_name */
+ }
+ do_end = FALSE;
+ if (!multi_tag && !in_txn && act_defp &&
+ (act_defp->actions[begin_act].vtw_list_head ||
+ act_defp->actions[end_act].vtw_list_head)){
+ /* we are traversing change directory
+ if we are here, there is a change somewhere */
+ do_txn = in_txn = TRUE;
+ if (act_defp->actions[end_act].vtw_list_head)
+ do_end = TRUE;
+ /* if creating, delete skipped this directory,
+ we have to do begin act */
+ if (creating &&
+ act_defp->actions[begin_act].vtw_list_head)
+ do_begin = TRUE;
+ }
+ push_path(&m_path, child);
+ /* are we a "value" parent node */
+ if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) {
+ /* we are value node */
+ if (!act_defp->multi) {
+ /* single node */
+ /* create_mode do create,
+ update_mode do create or update */
+
+ if (creating)
+ act = create_act;
+ else
+ act = update_act;
+
+ if ((act==update_act) && !act_defp->actions[update_act].vtw_list_head){
+ /* updating but no action
+ ask parent to do - propagate up */
+ *update_parent = TRUE;
+ }
+ /* look for actions */
+ /* if act != create_act => run actions[act] if not empty */
+ /* if act == create_act
+ * if actions[create_act] not empty
+ * run it
+ * else
+ * run actions[update_act] if not empty
+ * run actions[activate_act] if not empty
+ */
+ if (act_defp->actions[act].vtw_list_head
+ || (act == create_act
+ && (act_defp->actions[activate_act].vtw_list_head
+ || act_defp->actions[update_act].vtw_list_head))) {
+ status = get_value_to_at_string(&m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ /* remove \n at the line end */
+ cp = strchr(get_at_string(), '\n');
+ if (cp)
+ *cp = 0;
+ if (act_defp->actions[act].vtw_list_head) {
+ status = execute_list(act_defp->actions[act].vtw_list_head,
+ act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ } else {
+ /* creating but no create action */
+ /* try update action */
+ if ((act == create_act)
+ && act_defp->actions[update_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[update_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ }
+ /* try activate action if creating */
+ if ((act == create_act)
+ && act_defp->actions[activate_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[activate_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ free_at_string();
+ }
+ /* now handle commit value */
+ if (!in_txn) { /* ELSE WAIT TILL THE END OF TXN */
+ perform_move();
+ perform_delete_node();
+ }
+ goto restore;
+ }
+ /* else multi_node */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ ok = commit_value(act_defp, cp, creating?create_mode:update_mode, in_txn);
+ if (cp)
+ my_free(cp);
+ goto restore;
+ }
+ /* else not a value */
+ /* regular */
+ /* do not do anything for tag type multinode */
+ if (!multi_tag && creating) {
+ set_at_string(child); /* for expand inside actions */
+ if (do_begin) {
+ status = execute_list(act_defp->
+ actions[begin_act].
+ vtw_list_head, act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+
+ if (act_defp) {
+ if (act_defp->actions[create_act].vtw_list_head) {
+ status
+ = execute_list(act_defp->actions[create_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ } else if (act_defp->actions[update_act].vtw_list_head) {
+ /* no create action => use update action */
+ status
+ = execute_list(act_defp->actions[update_act].vtw_list_head,
+ act_defp);
+ if (!status) {
+ return (FALSE);
+ }
+ }
+ /* not trying activate action here (activate after children are
+ * configured)
+ */
+ }
+ }
+ if (creating && !in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_create_node();
+ /* children */
+ ok = commit_update_children(my_defp, creating, in_txn, &update_pending);
+ if (!ok)
+ return(FALSE);
+
+ if (update_pending){
+ if (!multi_tag && act_defp &&
+ act_defp->actions[update_act].vtw_list_head){
+ set_at_string(child); /* for expand inside actions */
+ ok = execute_list(act_defp->actions[update_act].
+ vtw_list_head, act_defp);
+ if (!ok)
+ return(FALSE);
+ /* update_pending = FALSE; */
+ } else
+ *update_parent = TRUE;
+ }
+ if (creating && !multi_tag && act_defp &&
+ act_defp->actions[activate_act].vtw_list_head){
+ set_at_string(child); /* for expand inside actions */
+ ok = execute_list(act_defp->actions[activate_act].
+ vtw_list_head, act_defp);
+ /* ignore result */
+ }
+ if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ restore:
+ if (do_end){
+ set_at_string(child);
+ ok = execute_list(act_defp->actions[end_act].
+ vtw_list_head, act_defp);
+ }
+#if BITWISE
+ if (do_txn && ok) {
+ int len;
+ char *command;
+ char format1[]="rm -r -f %s/*;cp -r -f %s/%s %s";
+ char format2[]="rm -r -f %s/%s;mv %s/%s %s";
+ char format3[]="rm -r -f %s/%s";
+ restore_paths(&mark);
+ switch_path(MPATH);
+ len = sizeof(format1) + 2 * strlen(get_tmpp()) +
+ strlen(m_path.path) + strlen(child);
+ command = my_malloc(len, "");
+ sprintf(command, format1, get_tmpp(), m_path.path, child,
+ get_tmpp());
+ system(command);
+ my_free(command);
+ switch_path(APATH);
+ len = sizeof(format2) + 2 *strlen(m_path.path) +
+ 2 * strlen( child) + strlen(get_tmpp());
+ command = my_malloc(len, "");
+ sprintf(command, format2, m_path.path, child, get_tmpp(),
+ child, m_path.path);
+ system(command);
+ my_free(command);
+ switch_path(CPATH);
+ len = sizeof(format3) + strlen(m_path.path) +
+ strlen( child);
+ command = my_malloc(len, "");
+ sprintf(command, format3, m_path.path, child);
+ system(command);
+ my_free(command);
+ }
+#endif
+ restore_paths(&mark);
+ if (my_defp && my_defp != pdefp)
+ free_def(my_defp);
+ return ok;
+}
+
+
+/*************************************************
+ commit_delete_child:
+ preconditions: t_path and c_path set up for parent
+ pdefp (IN) - parent definition (may be NULL)
+ child (IN) - name of the child
+ do_del (IN) - if FALSE we are looking for delete target
+ if TRUE, we found and switched to A (working) PATH
+ commit deleted child and its descendants
+ returns TRUE if no errors
+ exit if error during execution
+*/
+static boolean commit_delete_child(vtw_def *pdefp, char *child,
+ boolean deleting, boolean in_txn)
+{
+ boolean do_children, multi_tag = FALSE;
+ struct stat statbuf;
+ int status;
+ vtw_def def;
+ vtw_def *my_defp; /*def to be given to my children */
+ vtw_def *act_defp;/*def to be used for actions */
+ char *cp;
+ vtw_mark mark;
+ boolean ok, do_txn = FALSE;
+ int st;
+ ok = TRUE;
+
+#ifdef DEBUG
+ printf("commiting directory (node_cnt %d, free_node_cnt %d)\n"
+ "mpath |%s|\n",
+ node_cnt, free_node_cnt, t_path.path);
+#endif
+
+ /* deleted subdirectory */
+ if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */
+ if (deleting)
+ /* in do_delete mode we traverse A hierarchy, no white-outs possible */
+ INTERNAL; /* it is exit */
+ else {
+ /* deal with counterpart in working */
+ switch_path(APATH);
+ push_path(&m_path, child+4);
+ st = lstat(m_path.path, &statbuf);
+ pop_path(&m_path);
+ if (st >= 0){
+ /*get rid of ".wh. part in child name"*/
+ /* deleting mode will handle txn, both
+ begin and end
+ */
+ ok = commit_delete_child(pdefp, child + 4, TRUE, in_txn);
+ }else {
+ /* I do not understand how we could be here */
+ printf("Mystery #1\n");
+ ok = TRUE;
+ }
+ switch_path(CPATH);
+ if (ok) {
+ /* delete whiteout */
+ if (!in_txn){ /*ELSE WAIT TILL THE END OF TXN*/
+ push_path(&m_path, child);
+ perform_delete_node();
+ pop_path(&m_path);
+ }
+ }
+ return ok;
+ }
+ /* done with whiteouts */
+ }
+ /* not white out */
+ mark_paths(&mark);
+ if (!deleting) {
+ int status;
+ /* are we marked with opaque */
+ push_path(&m_path, child);
+ push_path(&m_path, opaque_name);
+ status = lstat(m_path.path, &statbuf);
+ pop_path(&m_path);
+ pop_path(&m_path);
+ if (status >= 0) {
+ /* brand new directory, nothing is
+ deleted there;
+ update will handle txn (both begin and end)
+ */
+ return TRUE;
+ }
+ }
+ /* find our definition */
+ if (pdefp && pdefp->tag) {
+ /* parent is a tag, node is a tag value node */
+ my_defp = NULL;
+ act_defp = pdefp;
+ push_path(&t_path,tag_name);
+ } else {
+ push_path(&t_path, child);
+ push_path(&t_path, def_name);
+ if ((lstat(t_path.path, &statbuf) >= 0) &&
+ ((statbuf.st_mode & S_IFMT) == S_IFREG)) {
+ /* defniition present */
+ act_defp = my_defp = &def;
+ memset(&def, 0, sizeof(def));
+#ifdef DEBUG1
+ printf("Parsing definition\n");
+#endif
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ my_defp = act_defp = &def;
+ if (def.tag)
+ multi_tag = TRUE; /* tag node itself*/
+ } else {
+ my_defp = act_defp = NULL;
+ }
+ pop_path(&t_path); /*def_name */
+ }
+ push_path(&m_path, child);
+ if (!multi_tag) {
+ set_at_string(child); /* for expand inside actions */
+ /* deal with txn */
+ if (!in_txn && act_defp &&
+ (act_defp->actions[begin_act].vtw_list_head ||
+ act_defp->actions[end_act].vtw_list_head)) {
+ /* if we are here we have change,
+ we either in do_del and our node is change node
+ or we are in change directory and our node is
+ change node also */
+ if (deleting)
+ /* if not deleting, update will handle values */
+ do_txn = TRUE;
+ in_txn = TRUE;
+ if (act_defp->actions[begin_act].vtw_list_head){
+ status = execute_list(act_defp->actions[begin_act].
+ vtw_list_head, act_defp);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ }
+ }
+ /* are we a "value" parent node */
+ if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) {
+ /* we are value node */
+ if (!act_defp->multi) {
+ /* single node */
+ if (!deleting) {
+ /* if it was whiteout, it was converted to do_del_mode */
+ restore_paths(&mark);
+ return ok;
+ }
+
+ /* do we have actions */
+ if (act_defp->actions[delete_act].vtw_list_head){
+ status = get_value_to_at_string(&m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ /* remove \n at the line end */
+ cp = strchr(get_at_string(), '\n');
+ if (cp)
+ *cp = 0;
+ if (act_defp->actions[delete_act].vtw_list_head) {
+ set_in_delete_action(TRUE);
+ status = execute_list(act_defp->actions[delete_act].
+ vtw_list_head, act_defp);
+ set_in_delete_action(FALSE);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ free_at_string();
+ }
+ /* now handle commit value */
+ if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ goto restore;
+ }
+ /* else multi_node */
+ cp = NULL;
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Can not read value at %s", m_path.path);
+ ok = commit_value(act_defp, cp, deleting?do_del_mode:del_mode,in_txn);
+ if (cp)
+ my_free(cp);
+ goto restore;
+ }
+ /* else not a value */
+ /* regular */
+ do_children = TRUE;
+ /* do not do anything for tag itself, all action belong to values */
+ if (!multi_tag) {
+ set_at_string(child); /* for expand inside actions */
+ if (deleting) {
+ if (act_defp &&
+ act_defp->actions[delete_act].vtw_list_head){
+ do_children = FALSE;
+ set_in_delete_action(TRUE);
+ status = execute_list(act_defp->actions[delete_act].
+ vtw_list_head, act_defp);
+ set_in_delete_action(FALSE);
+ if (!status) {
+#ifdef DEBUG
+ bye("begin action not NULL for %s\n", get_at_string());
+#else
+ return(FALSE);
+#endif
+ }
+ }
+ }
+ }
+ /* children */
+ if (do_children){
+ ok = commit_delete_children(my_defp, deleting, in_txn);
+ if (!ok)
+ goto restore;
+ }
+ if (deleting) {
+ if (do_txn && act_defp &&
+ act_defp->actions[end_act].vtw_list_head) {
+ set_at_string(child);
+ ok = execute_list(act_defp->actions[end_act].
+ vtw_list_head, act_defp);
+ if (!ok)
+ goto restore;
+ }
+ /* delete node and all its descendants */
+ if (!in_txn || do_txn)/* ELSE WAIT TILL THE END OF TXN */
+ perform_delete_node();
+ }
+ restore:
+ restore_paths(&mark);
+ if (my_defp && my_defp != pdefp)
+ free_def(my_defp);
+ return ok;
+}
+
+static boolean commit_delete_children(vtw_def *defp, boolean deleting,
+ boolean in_txn)
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ boolean ok = TRUE;
+ char *child;
+ vtw_type_e type;
+ valstruct mvals;
+ boolean first;
+ char *cp;
+ int elem, curi;
+ vtw_sorted cur_sorted;
+ char *uename = NULL;
+
+ if ((dp = opendir(m_path.path)) == NULL){
+ if (deleting)
+ return TRUE;
+ INTERNAL;
+ }
+ if (defp)
+ type = defp->def_type;
+ else
+ type = TEXT_TYPE;
+ if (type == ERROR_TYPE)
+ type = TEXT_TYPE;
+ first = TRUE;
+ memset(&mvals, 0, sizeof (valstruct));
+ memset(&cur_sorted, 0, sizeof(vtw_sorted));
+
+ while ((dirp = readdir(dp)) != NULL) {
+ child = dirp->d_name;
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, OPQ_NAME) == 0)
+ continue;
+ uename = clind_unescape(child);
+ cp = uename;
+ if (first) {
+ mvals.free_me = TRUE;
+ mvals.val = cp;
+ mvals.val_type = type;
+ first = FALSE;
+ } else {
+ if (mvals.cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ mvals.vals = my_realloc(mvals.vals,
+ (mvals.cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (mvals.cnt == 0) { /* single value - convert */
+ mvals.vals[0] = mvals.val;
+ mvals.cnt= 1;
+ mvals.val = NULL;
+ }
+ }
+ mvals.vals[mvals.cnt] = cp;
+ ++mvals.cnt;
+ }
+ }
+ status = closedir(dp);
+ if (status)
+ INTERNAL;
+ if (first) {
+ return TRUE;
+ }
+ vtw_sort(&mvals, &cur_sorted);
+ for (curi = 0; curi < cur_sorted.num && ok; ++curi){
+ if (type == TEXT_TYPE ||
+ type == BOOL_TYPE)
+ child = (char *)(cur_sorted.ptrs[curi]);
+ else {
+ elem = (((unsigned int *)(cur_sorted.ptrs[curi]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ child = mvals.cnt?mvals.vals[elem]:
+ mvals.val;
+ }
+ ok = commit_delete_child(defp, child, deleting, in_txn);
+ }
+ free_val(&mvals);
+ free_sorted(&cur_sorted);
+ return ok;
+}
+
+static boolean commit_update_children(vtw_def *defp, boolean creating,
+ boolean in_txn, boolean *parent_update)
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ boolean ok = TRUE;
+ char *child;
+ vtw_type_e type;
+ valstruct mvals;
+ boolean first;
+ char *cp;
+ int elem, curi;
+ vtw_sorted cur_sorted;
+ char *uename = NULL;
+
+
+ if ((dp = opendir(m_path.path)) == NULL){
+ printf("%s:%d: opendir error: path=%s\n",
+ __FUNCTION__,__LINE__,m_path.path);
+ INTERNAL;
+ }
+
+ memset(&mvals, 0, sizeof (valstruct));
+ memset(&cur_sorted, 0, sizeof(vtw_sorted));
+ if (defp)
+ type = defp->def_type;
+ else
+ type = TEXT_TYPE;
+ if (type == ERROR_TYPE)
+ type = TEXT_TYPE;
+ first = TRUE;
+
+ while ((dirp = readdir(dp)) != NULL) {
+ child = dirp->d_name;
+ if (strcmp(child, ".") == 0 ||
+ strcmp(child, "..") == 0 ||
+ strcmp(child, MOD_NAME) == 0 ||
+ strcmp(child, LOCK_NAME) == 0 ||
+ strcmp(child, OPQ_NAME) == 0)
+ continue;
+ cp = uename = clind_unescape(child);
+ if (first) {
+ mvals.free_me = TRUE;
+ mvals.val = cp;
+ mvals.val_type = type;
+ first = FALSE;
+ } else {
+ if (mvals.cnt%MULTI_ALLOC == 0) {
+ /* convert into multivalue */
+ mvals.vals = my_realloc(mvals.vals,
+ (mvals.cnt + MULTI_ALLOC) *
+ sizeof(char *), "add_value");
+ if (mvals.cnt == 0) { /* single value - convert */
+ mvals.vals[0] = mvals.val;
+ mvals.cnt= 1;
+ mvals.val = NULL;
+ }
+ }
+ mvals.vals[mvals.cnt] = cp;
+ ++mvals.cnt;
+ }
+ }
+ status = closedir(dp);
+ if (status)
+ INTERNAL;
+ if (first) {
+ if (uename)
+ my_free(uename);
+ return TRUE;
+ }
+ vtw_sort(&mvals, &cur_sorted);
+ for (curi = 0; curi < cur_sorted.num && ok; ++curi){
+ if (type == TEXT_TYPE ||
+ type == BOOL_TYPE)
+ child = (char *)(cur_sorted.ptrs[curi]);
+ else {
+ elem = (((unsigned int *)(cur_sorted.ptrs[curi]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ child = mvals.cnt?mvals.vals[elem]:
+ mvals.val;
+ }
+ ok = commit_update_child(defp, child, creating, in_txn, parent_update);
+ }
+ free_val(&mvals);
+ free_sorted(&cur_sorted);
+ return ok;
+}
+
+
+/*************************************************
+ commit_value:
+ executes commit for the value leave node
+**************************************************/
+static boolean commit_value(vtw_def *defp, char *cp,
+ vtw_cmode mode, boolean in_txn)
+{
+
+ valstruct act_value;
+ int status;
+ int curi,acti, partnum, res=0;
+ void *actp, *curp;
+ boolean no_shadow;
+ boolean ok;
+ int total, a_res, c_res;
+ char **a_ptr, **c_ptr, *val_string;
+ boolean cur_pr_val;
+ int pr_index;
+ int sign;
+ boolean creating;
+ vtw_node *actions;
+ valstruct cur_value;
+ vtw_sorted cur_sorted;
+ vtw_sorted act_sorted;
+
+ ok = TRUE;
+ actions = NULL;
+ if(mode == del_mode || mode == do_del_mode) {
+ creating = FALSE;
+ if (defp && defp->actions[delete_act].vtw_list_head) {
+ set_in_delete_action(TRUE);
+ actions = defp->actions[delete_act].vtw_list_head;
+ }
+ } else {
+ creating = TRUE;
+ if (defp && defp->actions[create_act].vtw_list_head)
+ actions = defp->actions[create_act].vtw_list_head;
+ }
+ /* prepare cur_value */
+
+ status = char2val(defp, cp, &cur_value);
+ if (mode != do_del_mode && mode != create_mode) {
+ /* get active value */
+ switch_path(APATH); /* switch form CCD to ACD */
+ status = get_value(&cp, &m_path);
+ switch_path(CPATH); /* back to CCD */
+ if (status != VTWERR_OK) {
+ no_shadow = TRUE;
+ }else
+ no_shadow = FALSE;
+ } else {
+ no_shadow = TRUE;
+ }
+ vtw_sort(&cur_value, &cur_sorted);
+ if(no_shadow) {
+ act_sorted.num = 0;
+ }else {
+ status = char2val(defp, cp, &act_value);
+ if (status != VTWERR_OK) {
+ INTERNAL;
+ }
+ /* sort them */
+ vtw_sort(&act_value, &act_sorted);
+ }
+ if (mode == do_del_mode) {
+ /* it was actually act_sorted, not cur_sorted */
+ act_sorted = cur_sorted;
+ cur_sorted.num = 0;
+ act_value = cur_value;
+ /* act_value will be freed by freeing cur_value
+ do not zero out it here */
+ }
+
+ acti = 0;
+ curi = 0;
+ total = act_sorted.num + cur_sorted.num;
+ a_res=0;
+ c_res=0;
+ a_ptr = my_malloc(total*sizeof(char *), "");
+ c_ptr = my_malloc(total*sizeof(char *), "");
+ while (acti < act_sorted.num || curi < cur_sorted.num) {
+ if (acti == act_sorted.num) {
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = +1;
+ ++curi;
+ } else if (curi == cur_sorted.num) {
+ cur_pr_val = FALSE;
+ pr_index = acti;
+ sign = -1;
+ ++acti;
+ } else {
+ /* compare */
+ actp = act_sorted.ptrs[acti];
+ curp = cur_sorted.ptrs[curi];
+ /* compare */
+ if (act_sorted.partnum){
+ for(partnum = 0; partnum < act_sorted.partnum;
+ ++partnum) {
+ res = *((int *)actp + partnum) -
+ *((int *)curp + partnum);
+ if (res)
+ break;
+ }
+ } else{
+ res = strcmp((char *)actp, (char *) curp);
+ }
+ if (res == 0) {
+ /* the same */
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = 0;
+ ++acti;
+ ++curi;
+ } else if (res < 0) {
+ /* act < cur, act is unmatched */
+ cur_pr_val = FALSE;
+ pr_index = acti;
+ sign = -1;
+ ++acti;
+ }else {
+ /* cur < act, cur is unmatched */
+ cur_pr_val = TRUE;
+ pr_index = curi;
+ sign = 1;
+ ++curi;
+ }
+ }
+ if (defp->def_type == TEXT_TYPE ||
+ defp->def_type == BOOL_TYPE) {
+ val_string = cur_pr_val?
+ ((char *)(cur_sorted.ptrs[pr_index])):
+ ((char *)(act_sorted.ptrs[pr_index]));
+ } else {
+ if (cur_pr_val) {
+ int elem = (((unsigned int *)(cur_sorted.ptrs[pr_index]))-
+ cur_sorted.parts)/
+ cur_sorted.partnum;
+ val_string = cur_value.cnt?cur_value.vals[elem]:
+ cur_value.val;
+ } else {
+ int elem = (((unsigned int *)(act_sorted.ptrs[pr_index]))-
+ act_sorted.parts)/
+ act_sorted.partnum;
+ val_string = act_value.cnt?act_value.vals[elem]:
+ act_value.val;
+ }
+ }
+ set_at_string(val_string);
+ switch (sign) {
+ case 0: /* found in both, no actions, include in both */
+ a_ptr[a_res++]=val_string;
+ c_ptr[c_res++]=val_string;
+ break;
+ case 1: /* found only in change */
+ if (ok && creating) {
+ if (actions) {
+ /* do create action */
+ ok = execute_list(actions, defp);
+ } else if (defp && defp->actions[update_act].vtw_list_head) {
+ /* no create action => use update action */
+ ok = execute_list(defp->actions[update_act].vtw_list_head, defp);
+ }
+ if (ok && defp && defp->actions[activate_act].vtw_list_head) {
+ /* try activate action */
+ ok = execute_list(defp->actions[activate_act].vtw_list_head, defp);
+ }
+ /* if succ, make it look old */
+ if(ok)
+ a_ptr[a_res++]=val_string;
+ }
+ c_ptr[c_res++]=val_string; /* in all cases */
+ break;
+ case -1: /* found only in working */
+ if (ok && !creating && actions) {/* ok and deleting */
+ ok = execute_list(actions, defp);
+ }
+ /* if succ and deleting - do nothing, else */
+ if (!ok || creating)
+ a_ptr[a_res++]=val_string;
+ }
+ }
+ if (creating && ok)
+ c_res = 0;
+#if BITWISE
+ if (!in_txn) {/* ELSE WAIT TILL THE END OF TXN */
+ switch_path(APATH);
+ if (a_res) {
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ for(i=0;i<a_res;++i)
+ if (fputs(a_ptr[i], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ pop_path(&m_path);
+ }else{
+ perform_delete_node();
+ }
+ switch_path(CPATH);
+ if (c_res) {
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ for(i=0;i<c_res;++i)
+ if (fputs(c_ptr[i], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ pop_path(&m_path);
+ }else{
+ perform_delete_node();
+ }
+ }
+#endif /*BITWISE*/
+ if (mode == do_del_mode)
+ switch_path(APATH);
+ else
+ switch_path(CPATH);
+ if(act_sorted.num)
+ free_sorted(&act_sorted);
+ if(cur_sorted.num)
+ free_sorted(&cur_sorted);
+ my_free(a_ptr);
+ my_free(c_ptr);
+ free_val(&cur_value);
+ if (!no_shadow)
+ free_val(&act_value);
+ set_in_delete_action(FALSE);
+ return ok;
+}
+
+static int fin_commit(boolean ok)
+{
+ char *command;
+ static char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/
+ static char format2[]="sudo umount %s"; /*mdirp*/
+ static char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true";
+ /*tmpp*/
+ static char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/
+ static char format5[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*adirp*/
+ static char format6[]="mv -f %s/* -t %s";/*tmpp, adirp*/
+ static char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro"
+ " unionfs %s"; /*cdirp, adirp, mdirp*/
+ int m_len = strlen(get_mdirp());
+ int t_len = strlen(get_tmpp());
+ int c_len = strlen(get_cdirp());
+ int a_len = strlen(get_adirp());
+ set_echo(TRUE);
+ if (!ok){
+ printf("Commit FAILED!\n");
+ return -1;
+ }
+ command = my_malloc(strlen(format1) + m_len + t_len, "");
+ sprintf(command, format1, get_mdirp(), get_tmpp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format2) + m_len, "");
+ sprintf(command, format2, get_mdirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format3) + c_len, "");
+ sprintf(command, format3, get_tmpp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format4) + c_len, "");
+ sprintf(command, format4, get_cdirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format5) + a_len, "");
+ sprintf(command, format5, get_adirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format6) + t_len + a_len, "");
+ sprintf(command, format6, get_tmpp(), get_adirp());
+ system(command);
+ my_free(command);
+
+ command = my_malloc(strlen(format7) + c_len + a_len + m_len, "");
+ sprintf(command, format7, get_cdirp(), get_adirp(), get_mdirp());
+ system(command);
+ my_free(command);
+
+ return 0;
+}
+
diff --git a/src/delete.c b/src/delete.c
new file mode 100644
index 0000000..a825a22
--- /dev/null
+++ b/src/delete.c
@@ -0,0 +1,258 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+
+static void remove_rf(boolean do_umount)
+{
+ char *command;
+ touch();
+ if (do_umount) {
+ command = my_malloc(strlen(get_mdirp()) + 20, "delete");
+ sprintf(command, "sudo umount %s", get_mdirp());
+ system(command);
+ free(command);
+ }
+ command = my_malloc(strlen(m_path.path) + 10, "delete");
+ sprintf(command, "rm -rf %s", m_path.path);
+ system(command);
+ free(command);
+ if (do_umount) {
+ command = my_malloc(strlen(get_mdirp()) + strlen(get_cdirp()) +
+ strlen(get_mdirp()) + 100,
+ "delete");
+ sprintf(command, "sudo mount -t unionfs -o dirs=%s=rw:%s=ro:"
+ " unionfs %s", get_cdirp(), get_adirp(), get_mdirp());
+ system(command);
+ free(command);
+ }
+}
+/***************************************************
+ set_validate:
+ validate value against definition
+ return TRUE if OK, FALSE otherwise
+****************************************************/
+boolean set_validate(vtw_def *defp, char *valp)
+{
+ boolean res;
+ int status;
+ struct stat statbuf;
+
+ pop_path(&t_path); /* it was tag or real value */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0 ||
+ (statbuf.st_mode & S_IFMT) != S_IFREG) {
+ bye("Can not set value (2), no definition for %s", m_path.path);
+ }
+ /* defniition present */
+ memset(defp, 0, sizeof(vtw_def));
+ if ((status = parse_def(defp, t_path.path, FALSE)))
+ exit(status);
+ res = validate_value(defp, valp);
+ pop_path(&t_path);
+ return res;
+}
+
+int main(int argc, char **argv)
+{
+ int ai;
+ struct stat statbuf;
+ vtw_def def;
+ boolean last_tag=0;
+ int status;
+ FILE *fp;
+ boolean res;
+ char *cp, *delp, *endp;
+ boolean do_umount;
+
+ if (argc < 2) {
+ fprintf(stderr, "Need to specify the config node to delete\n");
+ exit(1);
+ }
+
+ dump_log( argc, argv);
+ do_umount = FALSE;
+ init_edit();
+ /* extend both paths per arguments given */
+ /* last argument is new value */
+ for (ai = 1; ai < argc; ++ai) {
+ push_path(&t_path, argv[ai]);
+ push_path(&m_path, argv[ai]);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = FALSE;
+ continue;
+ } /*else */
+ pop_path(&t_path);
+ push_path(&t_path, TAG_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = TRUE;
+ continue;
+ }
+ /* no match */
+ break;
+ }
+ /*
+ cases:
+ multiple tag-value - not achild
+ mutilple tag-value - not the last child
+ multiple tag-value - last child
+ single value modified
+ signle value unmodified
+ multiple non-tag value - the last value
+ multiple non-tag value - not the last value
+ regular child
+ */
+ if (ai == argc) {
+ /* full path found */
+ /* all cases except multiple non-tag value */
+ /* check for single value */
+ if (last_tag) {
+ /* case of multiple tag-value
+ was this a real child?
+ was it the last child?
+ */
+ struct dirent *dirp;
+ DIR *dp;
+
+ if (lstat(m_path.path, &statbuf) < 0)
+ bye("Nothing to delete at %s", m_path.path);
+ remove_rf(FALSE);
+ pop_path(&m_path);
+ if ((dp = opendir(m_path.path)) == NULL){
+ INTERNAL;
+ }
+ while ((dirp = readdir(dp)) != NULL) {
+ /*do we have real child */
+ if (strcmp(dirp->d_name, ".") &&
+ strcmp(dirp->d_name, "..") &&
+ strcmp(dirp->d_name, MOD_NAME) && /* XXX */
+ strcmp(dirp->d_name, LOCK_NAME) && /* XXX */
+ strncmp(dirp->d_name, ".wh.", 4) )
+ break;
+ }
+ if (dirp == NULL) {
+ /* no real children left */
+ /* kill parent also */
+ remove_rf(FALSE);
+ }
+ exit(0);
+ }
+ /*not tag */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0 &&
+ (statbuf.st_mode & S_IFMT) == S_IFREG) {
+ /* defniition present */
+ memset(&def, 0, sizeof(vtw_def));
+ if ((status = parse_def(&def, t_path.path, FALSE)))
+ exit(status);
+ if (!def.tag && !def.multi && def.def_type!= ERROR_TYPE) {
+ /* signgle value */
+ /* is it modified ==
+ it is in C, but not OPAQUE */
+ switch_path(CPATH);
+ if(lstat(m_path.path, &statbuf) >= 0) {
+ push_path(&m_path, OPQ_NAME);
+ if(lstat(m_path.path, &statbuf) < 0) {
+ /* yes remove from C only */
+ pop_path(&m_path);
+ remove_rf(TRUE);
+ exit(0);
+ }
+ pop_path(&m_path); /*OPQ_NAME */
+ }
+ switch_path(MPATH);
+ }
+ }
+ /* else no defnition, remove it also */
+ remove_rf(FALSE);
+ exit(0);
+ }
+ if(ai < argc -1 || last_tag) {
+ bye("There is no appropriate template for %s",
+ m_path.path + strlen(get_mdirp()));
+ }
+ /*ai == argc -1, must be actual value */
+ pop_path(&m_path); /*it was value, not path segment */
+ push_path(&m_path, VAL_NAME);
+ /* set value */
+ if (lstat(m_path.path, &statbuf) < 0)
+ bye("Nothing to delete at %s", m_path.path);
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ bye("Not a regular file %s", m_path.path);
+ /* get definition to deal with potential multi */
+ pop_path(&t_path); /* it was tag or real value */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0 ||
+ (statbuf.st_mode & S_IFMT) != S_IFREG) {
+ bye("Can not delete value, no definition for %s", m_path.path);
+ }
+ /* defniition present */
+ memset(&def, 0, sizeof(vtw_def));
+ if ((status = parse_def(&def, t_path.path, FALSE)))
+ exit(status);
+ if (def.multi) {
+ /* delete from multivalue */
+ valstruct new_value, old_value;
+ status = char2val(&def, argv[argc - 1], &new_value);
+ if (status)
+ exit(0);
+ cp = NULL;
+ pop_path(&m_path); /* get_value will push VAL_NAME */
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Cannot read old value %s\n", m_path.path);
+ status = char2val(&def, cp, &old_value);
+ if (status != VTWERR_OK)
+ bye("Corrupted old value ---- \n%s\n-----\n", cp);
+ res = val_cmp(&new_value, &old_value, IN_COND);
+ if (!res)
+ bye("Not in multivalue");
+ touch();
+ if (old_value.cnt) {
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ if (is_in_cond_tik()) {
+ for(delp=cp;delp && is_in_cond_tik(); dec_in_cond_tik()) {
+ delp = strchr(delp, '\n');
+ if (!delp)
+ INTERNAL;
+ ++delp; /* over \n */
+ }
+ /* write "left" of deleted */
+ fwrite(cp, 1, delp-cp, fp);
+ }else
+ delp = cp;
+ /* find end of value */
+ endp = strchr(delp, '\n');
+ if (endp && *++endp) {
+ /* write "right" of deleted */
+ fwrite(endp, 1, strlen(endp), fp);
+ /* need the final '\n' */
+ fwrite("\n", 1, 1, fp);
+ }
+ fclose(fp);
+ return 0;
+ }
+ /* it multi with only 1 value, remove */
+ remove_rf(FALSE);
+ return 0;
+ }
+ bye("There is no appropriate template for %s",
+ m_path.path + strlen(get_mdirp()));
+
+ return 0;
+}
+
diff --git a/src/set.c b/src/set.c
new file mode 100644
index 0000000..566dfe0
--- /dev/null
+++ b/src/set.c
@@ -0,0 +1,310 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "cli_val.h"
+#include "cli_objects.h"
+#include "cli_path_utils.h"
+
+static void make_dir(void);
+static void handle_defaults(void);
+
+static void make_dir()
+{
+ touch_dir(m_path.path);
+}
+/***************************************************
+ set_validate:
+ validate value against definition
+ return TRUE if OK, FALSE otherwise
+****************************************************/
+boolean set_validate(vtw_def *defp, char *valp, boolean empty_val)
+{
+ boolean res;
+ int status;
+ struct stat statbuf;
+ char* path_end=NULL;
+
+ if (!empty_val) {
+ int i = 0;
+ int val_len = strlen(valp);
+
+ for (i = 0; i < val_len; i++) {
+ if (valp[i] == '\'') {
+ fprintf(stderr, "Cannot use the \"'\" (single quote) character "
+ "in a value string\n");
+ exit(1);
+ }
+ }
+
+ {
+ clind_path_ref tp = clind_path_construct(t_path.path);
+ if(tp) {
+ path_end=clind_path_pop_string(tp);
+ }
+ clind_path_destruct(&tp);
+ }
+
+ pop_path(&t_path); /* it was tag or real value */
+
+ }
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0 ||
+ (statbuf.st_mode & S_IFMT) != S_IFREG) {
+ bye("Can not set value (4), no definition for %s, template %s",
+ m_path.path,t_path.path);
+ }
+ /* defniition present */
+ memset(defp, 0, sizeof(vtw_def));
+ if ((status = parse_def(defp, t_path.path, FALSE)))
+ exit(status);
+ pop_path(&t_path);
+ if(path_end) {
+ push_path(&t_path,path_end);
+ free(path_end);
+ path_end=NULL;
+ }
+ if (empty_val) {
+ if (defp->def_type != TEXT_TYPE || defp->tag || defp->multi){
+ printf("Empty string may be assigned only to TEXT type leaf node\n");
+ return FALSE;
+ }
+ return TRUE;
+ }
+ res = validate_value(defp, valp);
+ return res;
+}
+
+int main(int argc, char **argv)
+{
+
+ int ai;
+ struct stat statbuf;
+ vtw_def def;
+ boolean last_tag;
+ int status;
+ FILE *fp;
+ boolean res;
+ char *cp;
+ char *command;
+ boolean need_mod = FALSE, not_new = FALSE;
+ boolean empty_val = FALSE;
+
+ dump_log( argc, argv);
+ init_edit();
+ last_tag = FALSE;
+
+ /* extend both paths per arguments given */
+ /* last argument is new value */
+ for (ai = 1; ai < argc; ++ai) {
+ if (!*argv[ai]) { /* empty string */
+ if (ai < argc -1) {
+ bye("empty string in argument list \n");
+ }
+ empty_val = TRUE;
+ last_tag = FALSE;
+ break;
+ }
+ push_path(&t_path, argv[ai]);
+ push_path(&m_path, argv[ai]);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = FALSE;
+ continue;
+ } /*else */
+ pop_path(&t_path);
+ push_path(&t_path, TAG_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("INTERNAL:regular file %s in templates", t_path.path);
+ }
+ last_tag = TRUE;
+ /* every time tag match, need to verify*/
+ if(!set_validate(&def, argv[ai], FALSE)) {
+ exit(1);
+ }
+ continue;
+ }
+ /* no match */
+ break;
+ }
+
+ if (ai == argc) {
+ /* full path found */
+ /* every tag match validated already */
+ /* non tag matches are OK by definition */
+ /* do we already have it? */
+ if (lstat(m_path.path, &statbuf) >= 0)
+ bye("Already exists %s", m_path.path + strlen(get_mdirp()));
+ /* else */
+ /* prevent value node without actual value */
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) >= 0) {
+ memset(&def, 0, sizeof(vtw_def));
+ if ((status = parse_def(&def, t_path.path, FALSE)))
+ exit(status);
+ if (def.def_type != ERROR_TYPE && !def.tag)
+ bye("Must provide actual value\n");
+ if (def.def_type == ERROR_TYPE && !def.tag) {
+ pop_path(&t_path);
+ if(!validate_value(&def, "")) {
+ exit(1);
+ }
+ push_path(&t_path, DEF_NAME);
+ }
+ }
+ touch();
+ pop_path(&t_path);
+ make_dir();
+ handle_defaults();
+ exit(0);
+ }
+ if(ai < argc -1 || last_tag) {
+ fprintf(stderr, "There is no appropriate template for %s",
+ m_path.path + strlen(get_mdirp()));
+ exit(1);
+ }
+ /*ai == argc -1, must be actual value */
+ if (!empty_val)
+ pop_path(&m_path); /*it was value, not path segment */
+
+ if(!set_validate(&def, argv[argc-1], empty_val)) {
+ exit(1);
+ }
+ push_path(&m_path, VAL_NAME);
+ /* set value */
+ if (lstat(m_path.path, &statbuf) >= 0) {
+ valstruct new_value, old_value;
+ not_new = TRUE;
+ if ((statbuf.st_mode & S_IFMT) != S_IFREG)
+ bye("Not a regular file at path \"%s\"", m_path.path);
+ /* check if this new value */
+ status = char2val(&def, argv[argc - 1], &new_value);
+ if (status)
+ exit(0);
+ cp = NULL;
+ pop_path(&m_path); /* get_value will push VAL_NAME */
+ status = get_value(&cp, &m_path);
+ if (status != VTWERR_OK)
+ bye("Cannot read old value %s\n", m_path.path);
+ status = char2val(&def, cp, &old_value);
+ if (status != VTWERR_OK)
+ bye("Corrupted old value ---- \n%s\n-----\n", cp);
+ res = val_cmp(&new_value, &old_value, IN_COND);
+ if (res) {
+ if (def.multi) {
+ bye("Already in multivalue");
+ } else {
+ bye("The same value \"%s\" for path \"%s\"\n", cp, m_path.path);
+ }
+ }
+ } else {
+ pop_path(&m_path);
+ }
+ make_dir();
+ push_path(&m_path, VAL_NAME);
+ if(not_new && !def.multi) {
+ /* it is not multi and seen from M */
+ /* is it in C */
+ switch_path(CPATH);
+ if (lstat(m_path.path, &statbuf) < 0)
+ /* yes, we are modifying original value */
+ need_mod = TRUE;
+ switch_path(MPATH);
+ }
+ touch();
+ /* in case of multi we always append, never overwrite */
+ /* in case of single we always overwrite */
+ /* append and overwrite work the same for new file */
+ fp = fopen(m_path.path, def.multi?"a":"w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ if (fputs(argv[argc-1], fp) < 0 || fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ if (need_mod) {
+ pop_path(&m_path); /* get rid of "value" */
+ command = my_malloc(strlen(m_path.path) + 30, "set");
+ sprintf(command, "touch %s/" MOD_NAME, m_path.path);
+ system(command);
+ }
+ return 0;
+}
+/**********************************************
+ handle_defaults:
+ now deal with defaults for children
+ if child has definition and not tag, nor multi, and
+ has type, and has default, and not have value
+ already, make a default value
+*/
+
+
+static void handle_defaults()
+{
+ DIR *dp;
+ int status;
+ struct dirent *dirp;
+ struct stat statbuf;
+ FILE *fp;
+ vtw_def def;
+ char *uename;
+
+ if ((dp = opendir(t_path.path)) == NULL){
+ INTERNAL;
+ }
+ while ((dirp = readdir(dp)) != NULL) {
+ if (strcmp(dirp->d_name, ".")==0 ||
+ strcmp(dirp->d_name, "..")==0 ||
+ strcmp(dirp->d_name, MOD_NAME) == 0 ||
+ strcmp(dirp->d_name, LOCK_NAME) == 0 ||
+ strcmp(dirp->d_name, DEF_NAME)==0)
+ continue;
+ uename = clind_unescape(dirp->d_name);
+ push_path(&t_path, uename);
+ if (lstat(t_path.path, &statbuf) < 0) {
+ bye("Cannot stat template directory %s\n",
+ t_path.path);
+ }
+ if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+ bye("Non directory file %s\n", t_path.path);
+ }
+ push_path(&t_path, DEF_NAME);
+ if (lstat(t_path.path, &statbuf) < 0) {
+ /* no definition */
+ pop_path(&t_path); /* definition */
+ pop_path(&t_path); /* child */
+ continue;
+ }
+ memset(&def, 0, sizeof(def));
+ if ((status = parse_def(&def, t_path.path,
+ FALSE)))
+ exit(status);
+ if (def.def_default) {
+ push_path(&m_path, uename);
+ push_path(&m_path, VAL_NAME);
+ if (lstat(m_path.path, &statbuf) < 0) {
+ /* no value, write one */
+ pop_path(&m_path);
+ make_dir();/* make sure directory exist */
+ push_path(&m_path, VAL_NAME);
+ fp = fopen(m_path.path, "w");
+ if (fp == NULL)
+ bye("Can not open value file %s", m_path.path);
+ if (fputs(def.def_default, fp) < 0 ||
+ fputc('\n',fp) < 0)
+ bye("Error writing file %s", m_path.path);
+ fclose(fp);
+ }
+ pop_path(&m_path); /* value */
+ pop_path(&m_path); /* child */
+ }
+ free_def(&def);
+ pop_path(&t_path); /* definition */
+ pop_path(&t_path); /* child */
+ }
+}
diff --git a/templates/interfaces/ethernet/node.def b/templates/interfaces/ethernet/node.def
new file mode 100644
index 0000000..b5a320e
--- /dev/null
+++ b/templates/interfaces/ethernet/node.def
@@ -0,0 +1,8 @@
+tag:
+type: txt
+help: "Ethernet interface name"
+syntax: exec " \
+ if [ -z \"`ip addr | grep $(@) `\" ]; then \
+ echo ethernet interface $(@) doesn\\'t exist on this system ; \
+ exit 1 ; \
+ fi ; "
diff --git a/templates/interfaces/ethernet/node.tag/address/node.def b/templates/interfaces/ethernet/node.tag/address/node.def
new file mode 100644
index 0000000..41a0b06
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/address/node.def
@@ -0,0 +1,5 @@
+multi:
+type: ipv4net
+help: "Set IPv4 address and prefix for this interface"
+create: "ip addr add $(@) dev $(../@)"; "error setting address $(@) on dev $(../@)"
+delete: "ip addr del $(@) dev $(../@)"; "error deleting address $(@) on dev $(../@)"
diff --git a/templates/interfaces/ethernet/node.tag/description/node.def b/templates/interfaces/ethernet/node.tag/description/node.def
new file mode 100644
index 0000000..abd0a26
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/description/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "description for this interface"
diff --git a/templates/interfaces/ethernet/node.tag/duplex/node.def b/templates/interfaces/ethernet/node.tag/duplex/node.def
new file mode 100644
index 0000000..e15ef39
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/duplex/node.def
@@ -0,0 +1,6 @@
+type: txt
+help: "set the duplex for this interface"
+syntax: $(@) in "half", "full"; "duplex must be half or full"
+commit: $(../speed) != ""; "if duplex is hardcoded, speed must also be hardcoded"
+create: "ethtool -s $(../@) speed $(../speed/@) duplex $(@) autoneg off"
+delete: "ethtool -s $(../@) autoneg on"
diff --git a/templates/interfaces/ethernet/node.tag/enable/node.def b/templates/interfaces/ethernet/node.tag/enable/node.def
new file mode 100644
index 0000000..2b74d58
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/enable/node.def
@@ -0,0 +1,3 @@
+help: "enable interface"
+create: "ip link set $(../@) up"; "error enabling dev $(../@)"
+delete: "ip link set $(../@) down"; "error disabling dev $(../@)"
diff --git a/templates/interfaces/ethernet/node.tag/hw-id/node.def b/templates/interfaces/ethernet/node.tag/hw-id/node.def
new file mode 100644
index 0000000..6097cff
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/hw-id/node.def
@@ -0,0 +1,2 @@
+type: macaddr
+help: "set the original MAC address for this interface"
diff --git a/templates/interfaces/ethernet/node.tag/mac/node.def b/templates/interfaces/ethernet/node.tag/mac/node.def
new file mode 100644
index 0000000..41e4313
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/mac/node.def
@@ -0,0 +1,4 @@
+type: macaddr
+help: "set the MAC address for this interface"
+create: "ip link set $(../@) address $(@)"; "error setting MAC address on dev $(../@)"
+delete: "ip link set $(../@) address $(../hw-id/@)"; "error setting MAC address on dev $(../@) to $(../hw-id/@)"
diff --git a/templates/interfaces/ethernet/node.tag/mtu/node.def b/templates/interfaces/ethernet/node.tag/mtu/node.def
new file mode 100644
index 0000000..33b1238
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/mtu/node.def
@@ -0,0 +1,5 @@
+type: u32
+help: "set the MTU address for this interface"
+syntax: $(@) >= 1 && $(@) <= 1500; "MTU must be between 1 and 1500"
+create: "ip link set $(../@) mtu $(@)"; "error setting MAC address on dev $(../@)"
+delete: "ip link set $(../@) mtu 1500"; "error deleteing MAC address on dev $(../@)"
diff --git a/templates/interfaces/ethernet/node.tag/speed/node.def b/templates/interfaces/ethernet/node.tag/speed/node.def
new file mode 100644
index 0000000..6983355
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/speed/node.def
@@ -0,0 +1,6 @@
+type: txt
+help: "set the speed for this interface"
+syntax: $(@) in "10", "100", "1000"; "Speed must be 10, 100, or 1000"
+commit: $(../duplex/@) != ""; "if speed is hardcoded, duplex must also be hardcoded"
+create: "ethtool -s $(../@) speed $(@) duplex $(../duplex/@) autoneg off"
+delete: "ethtool -s $(../@) autoneg on"
diff --git a/templates/interfaces/ethernet/node.tag/vif/node.def b/templates/interfaces/ethernet/node.tag/vif/node.def
new file mode 100644
index 0000000..06ee1c0
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/vif/node.def
@@ -0,0 +1,7 @@
+tag:
+type: u32
+help: "vlan ID"
+syntax: $(@) >= 0 && $(@) <= 4095; "vlan ID must be between 0 and 4095"
+create: "modprobe 8021q"; "error loading 802.1q driver"
+create: "vconfig add $(../@) $(@)"; "error adding vlan id $(@) to dev $(../@)"
+delete: "vconfig rem $(../@) $(@)"; "error removing vlan id $(@) from dev $(../@)"
diff --git a/templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def b/templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def
new file mode 100644
index 0000000..345a29e
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def
@@ -0,0 +1,5 @@
+multi:
+type: ipv4net
+help: "Set IPv4 address and prefix for this interface"
+create: "ip addr add $(@) dev $(../../@)"; "error setting address $(@) on dev $(../../@)"
+delete: "ip addr del $(@) dev $(../../@)"; "error deleteing address $(@) on dev $(../../@)"
diff --git a/templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def b/templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def
new file mode 100644
index 0000000..abd0a26
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "description for this interface"
diff --git a/templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def b/templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def
new file mode 100644
index 0000000..1e1879f
--- /dev/null
+++ b/templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def
@@ -0,0 +1,3 @@
+help: "enable interface"
+create: "ip link set $(../../@) up"; "error enabling dev $(../../@)"
+delete: "ip link set $(../../@) down"; "error disabling dev $(../../@)"
diff --git a/templates/interfaces/loopback/node.def b/templates/interfaces/loopback/node.def
new file mode 100644
index 0000000..50df935
--- /dev/null
+++ b/templates/interfaces/loopback/node.def
@@ -0,0 +1,8 @@
+tag:
+type: txt
+help: "loopback interface name"
+syntax: exec " \
+ if [ -z \"`ip addr | grep $(@) `\" ]; then \
+ echo loopback interface $(@) doesn\\'t exist on this system ; \
+ exit 1 ; \
+ fi ; "
diff --git a/templates/interfaces/loopback/node.tag/address/node.def b/templates/interfaces/loopback/node.tag/address/node.def
new file mode 100644
index 0000000..2e7e106
--- /dev/null
+++ b/templates/interfaces/loopback/node.tag/address/node.def
@@ -0,0 +1,6 @@
+multi:
+type: ipv4net
+help: "Set IPv4 address and prefix for this interface"
+# TODO make sure 127.0.0.0/8 not in here.
+create: "ip addr add $(@) dev $(../@)"; "error setting address $(@) on dev $(../@)"
+delete: "ip addr del $(@) dev $(../@)"; "error deleteing address $(@) on dev $(../@)"
diff --git a/templates/interfaces/loopback/node.tag/description/node.def b/templates/interfaces/loopback/node.tag/description/node.def
new file mode 100644
index 0000000..abd0a26
--- /dev/null
+++ b/templates/interfaces/loopback/node.tag/description/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "description for this interface"
diff --git a/templates/interfaces/node.def b/templates/interfaces/node.def
new file mode 100644
index 0000000..297604a
--- /dev/null
+++ b/templates/interfaces/node.def
@@ -0,0 +1 @@
+help: "Network interface configuration"
diff --git a/templates/system/domain-name/node.def b/templates/system/domain-name/node.def
new file mode 100644
index 0000000..8f9d457
--- /dev/null
+++ b/templates/system/domain-name/node.def
@@ -0,0 +1,15 @@
+type: txt
+help: "Configure system domain name"
+syntax: pattern $(@) "^[-a-zA-Z0-9.]{0,63}$" ; "invalid domain name $(@)"
+create: "sh -c \"if [ x$(@) == x ]; then exit 0; fi && \
+touch /etc/resolv.conf && \
+sed -i '/domain/d' /etc/resolv.conf && \
+echo \\\"domain\t $(@)\\\" >> /etc/resolv.conf\" "
+# also add localhost line into /etc/hosts (see host-name template)?
+update: "sh -c \"if [ x$(@) == x ]; then exit 0; fi && \
+touch /etc/resolv.conf && \
+sed -i '/domain/d' /etc/resolv.conf && \
+echo \\\"domain\t $(@)\\\" >> /etc/resolv.conf\" "
+# also update localhost line in /etc/hosts (see host-name template)?
+delete: "sh -c \"touch /etc/resolv.conf && \
+sed -i '/domain\\\\t $(@)/d' /etc/resolv.conf\" "
diff --git a/templates/system/domain-search/domain/node.def b/templates/system/domain-search/domain/node.def
new file mode 100644
index 0000000..d4e6c3c
--- /dev/null
+++ b/templates/system/domain-search/domain/node.def
@@ -0,0 +1,12 @@
+multi:
+type: txt
+help: "Configure DNS domain completion order"
+syntax: pattern $(@) "^[-a-zA-Z0-9.]+$" ; "invalid domain name $(@)"
+create: "sh -c \"touch /etc/resolv.conf && \
+if grep -q 'search\t $(@)' /etc/resolv.conf; then exit 0; \
+else echo \\\"search\t $(@)\\\" >> /etc/resolv.conf; fi\" "
+update: "sh -c \"touch /etc/resolv.conf && \
+if grep -q 'search\t $(@)' /etc/resolv.conf; then exit 0; \
+else echo \\\"search\t $(@)\\\" >> /etc/resolv.conf; fi\" "
+delete: "sh -c \"touch /etc/resolv.conf && \
+sed -i '/search\\\\t $(@)/d' /etc/resolv.conf\" "
diff --git a/templates/system/gateway-address/node.def b/templates/system/gateway-address/node.def
new file mode 100644
index 0000000..c82e3fe
--- /dev/null
+++ b/templates/system/gateway-address/node.def
@@ -0,0 +1,17 @@
+type: txt
+help: "Configure default gateway"
+create: "sh -c \"echo \
+'static_routes/static_routes/0.1/add_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' \
+>> /tmp/cli.log && \
+/opt/vyatta/libexec/xorp/call_xrl 'static_routes/static_routes/0.1/add_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' >> /tmp/cli.log ; \
+echo \\$? >> /tmp/cli.log\" "
+update: "sh -c \"echo \
+'static_routes/static_routes/0.1/replace_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' \
+>> /tmp/cli.log && \
+/opt/vyatta/libexec/xorp/call_xrl 'static_routes/static_routes/0.1/replace_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' >> /tmp/cli.log ; \
+echo \\$? >> /tmp/cli.log\" "
+delete: "sh -c \"echo \
+'static_routes/static_routes/0.1/delete_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=0.0.0.0' \
+>> /tmp/cli.log && \
+/opt/vyatta/libexec/xorp/call_xrl 'static_routes/static_routes/0.1/delete_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=0.0.0.0' >> /tmp/cli.log ; \
+echo \\$? >> /tmp/cli.log\" "
diff --git a/templates/system/host-name/node.def b/templates/system/host-name/node.def
new file mode 100644
index 0000000..fc7c91b
--- /dev/null
+++ b/templates/system/host-name/node.def
@@ -0,0 +1,24 @@
+type: txt
+help: "Configure system host name"
+default: "vyatta"
+syntax: pattern $(@) "^[-a-zA-Z0-9.]+$" ; "invalid host name $(@)"
+create: "sh -c \"hostname '$(@)' && \
+touch /etc/hosts && \
+sed -i '/localhost/d' /etc/hosts && \
+echo \\\"127.0.0.1\t localhost $(@)\t #vyatta entry\\\" >> /etc/hosts && \
+if [ x$(../domain-name/@) != x ]; then \
+echo \\\"127.0.0.1\t localhost $(@).$(../domain-name/@)\t #vyatta entry\\\" \>> /etc/hosts; fi\" "
+# do we need to add ntpd restart here?
+update: "sh -c \"hostname '$(@)' && \
+touch /etc/hosts && \
+sed -i '/localhost/d' /etc/hosts && \
+echo \\\"127.0.0.1\t localhost $(@)\t #vyatta entry\\\" >> /etc/hosts && \
+if [ x$(../domain-name/@) != x ]; then \
+echo \\\"127.0.0.1\t localhost $(@).$(../domain-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; fi\" "
+# do we need to add ntpd restart here?
+delete: "sh -c \"echo > /etc/hostname.conf && hostname '' && \
+touch /etc/hosts && \
+sed -i '/localhost.*#vyatta entry/d' /etc/hosts && \
+if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
diff --git a/templates/system/login/node.def b/templates/system/login/node.def
new file mode 100644
index 0000000..1246514
--- /dev/null
+++ b/templates/system/login/node.def
@@ -0,0 +1,3 @@
+help: "Configure user access"
+delete: "sh -c \"echo User root cannot be deleted 1>&2 && exit 1\" "
+
diff --git a/templates/system/login/radius-server/node.def b/templates/system/login/radius-server/node.def
new file mode 100644
index 0000000..d12be76
--- /dev/null
+++ b/templates/system/login/radius-server/node.def
@@ -0,0 +1,14 @@
+tag:
+type: ipv4
+help: "Radius server authentication configuration"
+# need mandatory secret. also need port & timeout (default values?)
+create: "sh -c \"touch /etc/raddb/server && \
+sed -i '/$(@)/d' /etc/raddb/server && \
+echo \\\"$(@):$(port/@)\t$(secret/@)\t$(timeout/@)\\\" \
+>> /etc/raddb/server\" "
+update: "sh -c \"touch /etc/raddb/server && \
+sed -i '/$(@)/d' /etc/raddb/server && \
+echo \\\"$(@):$(port/@)\t$(secret/@)\t$(timeout/@)\\\" \
+>> /etc/raddb/server\" "
+delete: "sh -c \"touch /etc/raddb/server && \
+sed -i '/$(@)/d' /etc/raddb/server\" "
diff --git a/templates/system/login/radius-server/node.tag/port/node.def b/templates/system/login/radius-server/node.tag/port/node.def
new file mode 100644
index 0000000..8c856e7
--- /dev/null
+++ b/templates/system/login/radius-server/node.tag/port/node.def
@@ -0,0 +1,4 @@
+type: u32
+help: "Configure radius port"
+syntax: ($(@) > 0 && $(@) < 65536) ; "port must be between 1 and 65535"
+default: 1812
diff --git a/templates/system/login/radius-server/node.tag/secret/node.def b/templates/system/login/radius-server/node.tag/secret/node.def
new file mode 100644
index 0000000..eb08eca
--- /dev/null
+++ b/templates/system/login/radius-server/node.tag/secret/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "Secret for radius access"
diff --git a/templates/system/login/radius-server/node.tag/timeout/node.def b/templates/system/login/radius-server/node.tag/timeout/node.def
new file mode 100644
index 0000000..84bb442
--- /dev/null
+++ b/templates/system/login/radius-server/node.tag/timeout/node.def
@@ -0,0 +1,3 @@
+type: u32
+help: "Timeout for radius session"
+default: 2
diff --git a/templates/system/login/user/node.def b/templates/system/login/user/node.def
new file mode 100644
index 0000000..0d09c4d
--- /dev/null
+++ b/templates/system/login/user/node.def
@@ -0,0 +1,17 @@
+tag:
+type: txt
+help: "User account information"
+syntax: pattern $(@) "^[a-zA-Z_][a-zA-Z0-9_-]*\\$?$" ; "invalid user name $(@)"
+# line continuation and $() expansion are done by cli, not sh.
+# need mandatory encrypted password.
+end: "if [ -d /tmp/vyatta-delete-system-login-user-$(@).\\\$PPID ]; \
+then rm -rf /tmp/vyatta-delete-system-login-user-$(@).\\\$PPID && exit 0; \
+fi && \
+/opt/vyatta/sbin/vyatta_update_login_user.pl \
+'$(@)' '$(full-name/@)' '$(authentication/encrypted-password/@)'"
+delete: "if [ x$(@) == x ]; then exit 1; fi && \
+if [ x$(@) == xroot ]; then echo Cannot delete user \"root\" 1>&2 && exit 2; \
+fi && \
+if mkdir /tmp/vyatta-delete-system-login-user-$(@).\\\$PPID >& /dev/null; \
+then /opt/vyatta/sbin/vyatta_update_login_user.pl -d '$(@)'; \
+else exit 1; fi"
diff --git a/templates/system/login/user/node.tag/authentication/encrypted-password/node.def b/templates/system/login/user/node.tag/authentication/encrypted-password/node.def
new file mode 100644
index 0000000..33a87f5
--- /dev/null
+++ b/templates/system/login/user/node.tag/authentication/encrypted-password/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "Configure encrypted password"
diff --git a/templates/system/login/user/node.tag/authentication/node.def b/templates/system/login/user/node.tag/authentication/node.def
new file mode 100644
index 0000000..8b0f312
--- /dev/null
+++ b/templates/system/login/user/node.tag/authentication/node.def
@@ -0,0 +1 @@
+help: "Authentication password"
diff --git a/templates/system/login/user/node.tag/authentication/plaintext-password/node.def b/templates/system/login/user/node.tag/authentication/plaintext-password/node.def
new file mode 100644
index 0000000..78619d7
--- /dev/null
+++ b/templates/system/login/user/node.tag/authentication/plaintext-password/node.def
@@ -0,0 +1,9 @@
+type: txt
+help: "Configure plaintext password for encryption"
+# if plaintext is empty, assume this is left-over from blanking the plaintext
+# and do nothing. to set password to empty, user needs to set the
+# "encrypted-password" to an empty string (which actually allows login without
+# password).
+update: $(@) == "" \
+|| ($(../encrypted-password/@) = `/opt/vyatta/sbin/rl_passwd '$(@)' dummy` \
+ && $(@) = "")
diff --git a/templates/system/login/user/node.tag/full-name/node.def b/templates/system/login/user/node.tag/full-name/node.def
new file mode 100644
index 0000000..86b7c8d
--- /dev/null
+++ b/templates/system/login/user/node.tag/full-name/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "Full name of the user (use quotes for names with spaces)"
diff --git a/templates/system/name-server/node.def b/templates/system/name-server/node.def
new file mode 100644
index 0000000..2afe0b0
--- /dev/null
+++ b/templates/system/name-server/node.def
@@ -0,0 +1,17 @@
+multi:
+type: ipv4
+help: "Configure domain name server"
+create: "sh -c \"touch /etc/resolv.conf && \
+if grep -q '$(@)' /etc/resolv.conf; then exit 0; \
+else echo \\\"nameserver\t $(@)\\\" >> /etc/resolv.conf; fi && \
+if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
+update: "sh -c \"touch /etc/resolv.conf && \
+if grep -q '$(@)' /etc/resolv.conf; then exit 0; \
+else echo \\\"nameserver\t $(@)\\\" >> /etc/resolv.conf; fi && \
+if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
+delete: "sh -c \"touch /etc/resolv.conf && \
+sed -i '/$(@)/d' /etc/resolv.conf && \
+if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
diff --git a/templates/system/ntp-server/node.def b/templates/system/ntp-server/node.def
new file mode 100644
index 0000000..9f828e1
--- /dev/null
+++ b/templates/system/ntp-server/node.def
@@ -0,0 +1,16 @@
+multi:
+type: txt
+help: "IP address of NTP server"
+# should help be "domain name" instead of "ip address", or change type to ipv4?
+create: "sh -c \"touch /etc/ntp/ntp.conf && \
+if ! grep -q 'server.*$(@)' /etc/ntp/ntp.conf; then \
+echo \\\"server $(@)\\\" >> /etc/ntp/ntp.conf && \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
+update: "sh -c \"touch /etc/ntp/ntp.conf && \
+if ! grep -q 'server.*$(@)' /etc/ntp/ntp.conf; then \
+echo \\\"server $(@)\\\" >> /etc/ntp/ntp.conf && \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
+delete: "sh -c \"touch /etc/ntp/ntp.conf && \
+if grep -q 'server.*$(@)' /etc/ntp/ntp.conf; then \
+sed -i '/server $(@)/d' /etc/ntp/ntp.conf && \
+/opt/vyatta/sbin/ntpd.init restart; fi\" "
diff --git a/templates/system/options/node.def b/templates/system/options/node.def
new file mode 100644
index 0000000..1e49ee5
--- /dev/null
+++ b/templates/system/options/node.def
@@ -0,0 +1 @@
+help: "Configure system options"
diff --git a/templates/system/options/reboot-on-panic/node.def b/templates/system/options/reboot-on-panic/node.def
new file mode 100644
index 0000000..d84abc7
--- /dev/null
+++ b/templates/system/options/reboot-on-panic/node.def
@@ -0,0 +1,16 @@
+type: bool
+help: "Configure if kernel panic causes reboot"
+default: true
+create: "sh -c \"if [ x$(@) == xfalse ]; \
+then \
+ echo 0 > /proc/sys/kernel/panic; \
+else \
+ echo 60 > /proc/sys/kernel/panic; \
+fi\" "
+update: "sh -c \"if [ x$(@) == xfalse ]; \
+then \
+ echo 0 > /proc/sys/kernel/panic; \
+else \
+ echo 60 > /proc/sys/kernel/panic; \
+fi\" "
+delete: "sh -c \"echo 60 > /proc/sys/kernel/panic\" "
diff --git a/templates/system/package/auto-sync/node.def b/templates/system/package/auto-sync/node.def
new file mode 100644
index 0000000..59f3e1e
--- /dev/null
+++ b/templates/system/package/auto-sync/node.def
@@ -0,0 +1,15 @@
+# this will set APT::Periodic::Update-Package-Lists in /etc/apt/apt.conf
+# apt.conf is in turn read by the apt cron file loacted in /etc/cron.daily/apt
+# the /etc/crontab file must have the daily line for daily to be run
+type: u32
+default: 1
+help: "Update the the repository cache every n days. 0 disables auto-update."
+syntax: $(@) >= 0 && $(@) < 32 ; "auto-sync must be between 0 and 32 days"
+create: "sh -c \"touch /etc/apt/apt.conf && \
+sed -i '/APT::Periodic::Update-Package-Lists*/d' /etc/apt/apt.conf && \
+echo \\\"APT::Periodic::Update-Package-Lists \\\"$(@)\\\";\\\" >> /etc/apt/apt.conf\" "
+update: "sh -c \"touch /etc/apt/apt.conf && \
+sed -i '/APT::Periodic::Update-Package-Lists*/d' /etc/apt/apt.conf && \
+echo \\\"APT::Periodic::Update-Package-Lists \\\"$(@)\\\";\\\" >> /etc/apt/apt.conf\" "
+delete: "sh -c \"touch /etc/apt/apt.conf && \
+sed -i '/APT::Periodic::Update-Package-Lists*/d' /etc/apt/apt.conf\" "
diff --git a/templates/system/package/node.def b/templates/system/package/node.def
new file mode 100644
index 0000000..ce185fa
--- /dev/null
+++ b/templates/system/package/node.def
@@ -0,0 +1 @@
+help: "Package Update Repository Configuration"
diff --git a/templates/system/package/repository/node.def b/templates/system/package/repository/node.def
new file mode 100644
index 0000000..104089a
--- /dev/null
+++ b/templates/system/package/repository/node.def
@@ -0,0 +1,14 @@
+tag:
+type: txt
+help: "Repository name"
+# bug 1847: remove the previous repo line before adding new line.
+# need to prohibit '!' in repo name (sed delimiter)
+syntax: pattern $(@) "^[^!]+$" ; "Do not use '!' in repository name"
+create: "sh -c \"touch /etc/apt/sources.list && \
+sed -i '\\!/ $(@) !d' /etc/apt/sources.list && \
+echo \\\"deb $(url/@)/ $(@) $(component/@)\\\" >> /etc/apt/sources.list\" "
+update: "sh -c \"touch /etc/apt/sources.list && \
+sed -i '\\!/ $(@) !d' /etc/apt/sources.list && \
+echo \\\"deb $(url/@)/ $(@) $(component/@)\\\" >> /etc/apt/sources.list\" "
+delete: "sh -c \"touch /etc/apt/sources.list && \
+sed -i '\\! $(@) $(component/@)!d' /etc/apt/sources.list\" "
diff --git a/templates/system/package/repository/node.tag/component/node.def b/templates/system/package/repository/node.tag/component/node.def
new file mode 100644
index 0000000..e87f88d
--- /dev/null
+++ b/templates/system/package/repository/node.tag/component/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "Repository component names"
diff --git a/templates/system/package/repository/node.tag/description/node.def b/templates/system/package/repository/node.tag/description/node.def
new file mode 100644
index 0000000..9ce7dac
--- /dev/null
+++ b/templates/system/package/repository/node.tag/description/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "Repository description"
diff --git a/templates/system/package/repository/node.tag/url/node.def b/templates/system/package/repository/node.tag/url/node.def
new file mode 100644
index 0000000..0e304b9
--- /dev/null
+++ b/templates/system/package/repository/node.tag/url/node.def
@@ -0,0 +1,2 @@
+type: txt
+help: "Repository URL"
diff --git a/templates/system/static-host-mapping/host-name/node.def b/templates/system/static-host-mapping/host-name/node.def
new file mode 100644
index 0000000..ea0000a
--- /dev/null
+++ b/templates/system/static-host-mapping/host-name/node.def
@@ -0,0 +1,4 @@
+tag:
+type: txt
+help: "Map DNS names to system interfaces"
+syntax: pattern $(@) "^[-a-zA-Z0-9.]+$" ; "invalid host name $(@)"
diff --git a/templates/system/static-host-mapping/host-name/node.tag/alias/node.def b/templates/system/static-host-mapping/host-name/node.tag/alias/node.def
new file mode 100644
index 0000000..b6f897f
--- /dev/null
+++ b/templates/system/static-host-mapping/host-name/node.tag/alias/node.def
@@ -0,0 +1,33 @@
+multi:
+type: txt
+help: "Alias for this address"
+create: "sh -c \"touch /etc/hosts && \
+sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \
+/etc/hosts && \
+echo \\\"$(../inet/@)\t $(../@) $(@) \t #vyatta entry\\\" \
+>> /etc/hosts && \
+if [ x$(../../../domain-name/@) == x ]; \
+then \
+echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+else \
+echo \\\"127.0.0.1\t localhost \
+$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+fi\" "
+update: "sh -c \"touch /etc/hosts && \
+sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \
+/etc/hosts && \
+echo \\\"$(../inet/@)\t $(../@) $(@) \t #vyatta entry\\\" \
+>> /etc/hosts && \
+if [ x$(../../../domain-name/@) == x ]; \
+then \
+echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+else \
+echo \\\"127.0.0.1\t localhost \
+$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+fi\" "
+delete: "sh -c \"touch /etc/hosts && \
+sed -i '/ $(../@) .*#vyatta entry/{/localhost/!d}' /etc/hosts\" "
diff --git a/templates/system/static-host-mapping/host-name/node.tag/inet/node.def b/templates/system/static-host-mapping/host-name/node.tag/inet/node.def
new file mode 100644
index 0000000..192273c
--- /dev/null
+++ b/templates/system/static-host-mapping/host-name/node.tag/inet/node.def
@@ -0,0 +1,30 @@
+type: ipv4
+help: "Internet address"
+create: "sh -c \"touch /etc/hosts && \
+sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \
+/etc/hosts && \
+echo \\\"$(@)\t $(../@) \t #vyatta entry\\\" >> /etc/hosts && \
+if [ x$(../../../domain-name/@) == x ]; \
+then \
+echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+else \
+echo \\\"127.0.0.1\t localhost \
+$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+fi\" "
+update: "sh -c \"touch /etc/hosts && \
+sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \
+/etc/hosts && \
+echo \\\"$(@)\t $(../@) \t #vyatta entry\\\" >> /etc/hosts && \
+if [ x$(../../../domain-name/@) == x ]; \
+then \
+echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+else \
+echo \\\"127.0.0.1\t localhost \
+$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \
+>> /etc/hosts; \
+fi\" "
+delete: "sh -c \"touch /etc/hosts && \
+sed -i '/ $(../@) .*#vyatta entry/{/localhost/!d}' /etc/hosts\" "
diff --git a/templates/system/static-host-mapping/node.def b/templates/system/static-host-mapping/node.def
new file mode 100644
index 0000000..736413f
--- /dev/null
+++ b/templates/system/static-host-mapping/node.def
@@ -0,0 +1 @@
+help: "Map DNS names to system interfaces"
diff --git a/templates/system/syslog/console/facility/node.def b/templates/system/syslog/console/facility/node.def
new file mode 100644
index 0000000..bfe3856
--- /dev/null
+++ b/templates/system/syslog/console/facility/node.def
@@ -0,0 +1,11 @@
+tag:
+type: txt
+help: "Configure facility for console logging"
+create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '\\/dev\\/console' \
+\\\"$(@).\\\\\$LVL\t/dev/console\n\\\"\" "
+update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '\\/dev\\/console' \
+\\\"$(@).\\\\\$LVL\t/dev/console\n\\\"\" "
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl \
+'$(@)\\.' '\\/dev\\/console' ''\" "
diff --git a/templates/system/syslog/console/facility/node.tag/level/node.def b/templates/system/syslog/console/facility/node.tag/level/node.def
new file mode 100644
index 0000000..9be459c
--- /dev/null
+++ b/templates/system/syslog/console/facility/node.tag/level/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: "Configure the logging level"
+default: "err"
diff --git a/templates/system/syslog/console/node.def b/templates/system/syslog/console/node.def
new file mode 100644
index 0000000..e30721d
--- /dev/null
+++ b/templates/system/syslog/console/node.def
@@ -0,0 +1 @@
+help: "Configure console logging"
diff --git a/templates/system/syslog/file/node.def b/templates/system/syslog/file/node.def
new file mode 100644
index 0000000..d62d261
--- /dev/null
+++ b/templates/system/syslog/file/node.def
@@ -0,0 +1,4 @@
+tag:
+type: txt
+help: "Name of the syslog file to save log messages to"
+syntax: pattern $(@) "^[-a-zA-Z0-9_.]+$" ; "invalid file name $(@)"
diff --git a/templates/system/syslog/file/node.tag/archive/files/node.def b/templates/system/syslog/file/node.tag/archive/files/node.def
new file mode 100644
index 0000000..ca2bf17
--- /dev/null
+++ b/templates/system/syslog/file/node.tag/archive/files/node.def
@@ -0,0 +1,3 @@
+type: u32
+help: "Number of saved files"
+default: 5
diff --git a/templates/system/syslog/file/node.tag/archive/node.def b/templates/system/syslog/file/node.tag/archive/node.def
new file mode 100644
index 0000000..d0b0e23
--- /dev/null
+++ b/templates/system/syslog/file/node.tag/archive/node.def
@@ -0,0 +1,8 @@
+help: "Configure log file size and rotation characteristics"
+# need mandatory files & size
+create: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \
+'$(../@)' '$(files/@)' '$(size/@)' 1\" "
+update: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \
+'$(../@)' '$(files/@)' '$(size/@)' 1\" "
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \
+'$(../@)' '$(files/@)' '$(size/@)' 0\" "
diff --git a/templates/system/syslog/file/node.tag/archive/size/node.def b/templates/system/syslog/file/node.tag/archive/size/node.def
new file mode 100644
index 0000000..a5ace52
--- /dev/null
+++ b/templates/system/syslog/file/node.tag/archive/size/node.def
@@ -0,0 +1,3 @@
+type: u32
+help: "Size of log files (kbytes)"
+default: 0
diff --git a/templates/system/syslog/file/node.tag/facility/node.def b/templates/system/syslog/file/node.tag/facility/node.def
new file mode 100644
index 0000000..3ef56e3
--- /dev/null
+++ b/templates/system/syslog/file/node.tag/facility/node.def
@@ -0,0 +1,13 @@
+tag:
+type: txt
+help: "Configure facility for file logging"
+create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \
+'\\/var\\/log\\/user\\/$(../@)' \
+\\\"$(@).\\\\\$LVL\t/var/log/user/$(../@) \n\\\"\" "
+update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \
+'\\/var\\/log\\/user\\/$(../@)' \
+\\\"$(@).\\\\\$LVL\t/var/log/user/$(../@) \n\\\"\" "
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \
+'\\/var\\/log\\/user\\/$(../@)' ''\" "
diff --git a/templates/system/syslog/file/node.tag/facility/node.tag/level/node.def b/templates/system/syslog/file/node.tag/facility/node.tag/level/node.def
new file mode 100644
index 0000000..9be459c
--- /dev/null
+++ b/templates/system/syslog/file/node.tag/facility/node.tag/level/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: "Configure the logging level"
+default: "err"
diff --git a/templates/system/syslog/global/archive/files/node.def b/templates/system/syslog/global/archive/files/node.def
new file mode 100644
index 0000000..ca2bf17
--- /dev/null
+++ b/templates/system/syslog/global/archive/files/node.def
@@ -0,0 +1,3 @@
+type: u32
+help: "Number of saved files"
+default: 5
diff --git a/templates/system/syslog/global/archive/node.def b/templates/system/syslog/global/archive/node.def
new file mode 100644
index 0000000..dcfc244
--- /dev/null
+++ b/templates/system/syslog/global/archive/node.def
@@ -0,0 +1,8 @@
+help: "Configure log file size and rotation characteristics"
+# need mandatory files & size
+create: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \
+'$(files/@)' '$(size/@)' 1\" "
+update: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \
+'$(files/@)' '$(size/@)' 1\" "
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \
+'$(files/@)' '$(size/@)' 0\" "
diff --git a/templates/system/syslog/global/archive/size/node.def b/templates/system/syslog/global/archive/size/node.def
new file mode 100644
index 0000000..3c8da6e
--- /dev/null
+++ b/templates/system/syslog/global/archive/size/node.def
@@ -0,0 +1,3 @@
+type: u32
+help: "Size of log files (kbytes)"
+default: 250
diff --git a/templates/system/syslog/global/facility/node.def b/templates/system/syslog/global/facility/node.def
new file mode 100644
index 0000000..cd037f6
--- /dev/null
+++ b/templates/system/syslog/global/facility/node.def
@@ -0,0 +1,11 @@
+tag:
+type: txt
+help: "Configure facility for system logging"
+create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '' '\\/var\\/log\\/messages' \
+\\\"$(@).\\\\\$LVL\t/var/log/messages \n\\\"\" "
+update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '' '\\/var\\/log\\/messages' \
+\\\"$(@).\\\\\$LVL\t/var/log/messages \n\\\"\" "
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl \
+'' '\\/var\\/log\\/messages' '*.warning\t/var/log/messages \n'\" "
diff --git a/templates/system/syslog/global/facility/node.tag/level/node.def b/templates/system/syslog/global/facility/node.tag/level/node.def
new file mode 100644
index 0000000..9be459c
--- /dev/null
+++ b/templates/system/syslog/global/facility/node.tag/level/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: "Configure the logging level"
+default: "err"
diff --git a/templates/system/syslog/global/node.def b/templates/system/syslog/global/node.def
new file mode 100644
index 0000000..b516645
--- /dev/null
+++ b/templates/system/syslog/global/node.def
@@ -0,0 +1 @@
+help: "Configure system logging"
diff --git a/templates/system/syslog/host/node.def b/templates/system/syslog/host/node.def
new file mode 100644
index 0000000..a349582
--- /dev/null
+++ b/templates/system/syslog/host/node.def
@@ -0,0 +1,3 @@
+tag:
+type: txt
+help: "IP address or hostname of remote syslog server"
diff --git a/templates/system/syslog/host/node.tag/facility/node.def b/templates/system/syslog/host/node.tag/facility/node.def
new file mode 100644
index 0000000..2dc56e6
--- /dev/null
+++ b/templates/system/syslog/host/node.tag/facility/node.def
@@ -0,0 +1,11 @@
+tag:
+type: txt
+help: "Configure facility for host logging"
+create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '@$(../@) ' \
+\\\"$(@).\\\\\$LVL\t@$(../@) \n\\\"\" "
+update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '@$(../@) ' \
+\\\"$(@).\\\\\$LVL\t@$(../@) \n\\\"\" "
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \
+'@$(../@) ' ''\" "
diff --git a/templates/system/syslog/host/node.tag/facility/node.tag/level/node.def b/templates/system/syslog/host/node.tag/facility/node.tag/level/node.def
new file mode 100644
index 0000000..9be459c
--- /dev/null
+++ b/templates/system/syslog/host/node.tag/facility/node.tag/level/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: "Configure the logging level"
+default: "err"
diff --git a/templates/system/syslog/node.def b/templates/system/syslog/node.def
new file mode 100644
index 0000000..04d3675
--- /dev/null
+++ b/templates/system/syslog/node.def
@@ -0,0 +1,3 @@
+help: "Configure syslog daemon"
+delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl \
+'' '\\/var\\/log\\/messages' '*.warning\t/var/log/messages \n'\" "
diff --git a/templates/system/syslog/user/node.def b/templates/system/syslog/user/node.def
new file mode 100644
index 0000000..ba731ca
--- /dev/null
+++ b/templates/system/syslog/user/node.def
@@ -0,0 +1,3 @@
+tag:
+type: txt
+help: "Configure syslog user account output"
diff --git a/templates/system/syslog/user/node.tag/facility/node.def b/templates/system/syslog/user/node.tag/facility/node.def
new file mode 100644
index 0000000..1b22747
--- /dev/null
+++ b/templates/system/syslog/user/node.tag/facility/node.def
@@ -0,0 +1,14 @@
+tag:
+type: txt
+help: "Configure facility for user logging"
+create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl \\\"$(@)\\.\\\\\$LVL\\\" \
+' $(../@) ' \
+\\\"$(@).\\\\\$LVL $(../@) \n\\\"\" "
+update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl \\\"$(@)\\.\\\\\$LVL\\\" \
+' $(../@) ' \
+\\\"$(@).\\\\\$LVL $(../@) \n\\\"\" "
+delete: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \
+/opt/vyatta/sbin/vyatta_update_syslog.pl \\\"$(@)\\.\\\\\$LVL\\\" \
+' $(../@) ' ''\" "
diff --git a/templates/system/syslog/user/node.tag/facility/node.tag/level/node.def b/templates/system/syslog/user/node.tag/facility/node.tag/level/node.def
new file mode 100644
index 0000000..9be459c
--- /dev/null
+++ b/templates/system/syslog/user/node.tag/facility/node.tag/level/node.def
@@ -0,0 +1,3 @@
+type: txt
+help: "Configure the logging level"
+default: "err"
diff --git a/templates/system/time-zone/node.def b/templates/system/time-zone/node.def
new file mode 100644
index 0000000..00da013
--- /dev/null
+++ b/templates/system/time-zone/node.def
@@ -0,0 +1,16 @@
+type: txt
+help: "Configure local timezone"
+default: "GMT"
+update: "LTF=\"/usr/share/zoneinfo\" && \
+case \"$(@)\" in \
+ [Ll][Oo][Ss]*) LTF=\"\\\$LTF/US/Pacific\" ;; \
+ [Dd][Ee][Nn]*) LTF=\"\\\$LTF/US/Mountain\" ;; \
+ [Hh][Oo][Nn]*) LTF=\"\\\$LTF/US/Hawaii\" ;; \
+ [Nn][Ee][Ww]*) LTF=\"\\\$LTF/US/Eastern\" ;; \
+ [Cc][Hh][Ii]*) LTF=\"\\\$LTF/US/Central\" ;; \
+ [Aa][Nn][Cc]*) LTF=\"\\\$LTF/US/Alaska\" ;; \
+ [Pp][Hh][Oo]*) LTF=\"\\\$LTF/US/Arizona\" ;; \
+ *) LTF=\"\\\$LTF/Etc/$(@)\" ;; \
+esac && \
+ln -fs \\\$LTF /etc/localtime"
+delete: "ln -fs /usr/share/zoneinfo/GMT /etc/localtime"
diff --git a/tools/rl_passwd.cc b/tools/rl_passwd.cc
new file mode 100644
index 0000000..5a5610b
--- /dev/null
+++ b/tools/rl_passwd.cc
@@ -0,0 +1,152 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Module: rl_passwd.cc
+ *
+ * **** 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: Michael Larson
+ * Date: 2005
+ * Description:
+ *
+ * **** End License ****
+ *
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <utime.h>
+#include <syslog.h>
+#include <time.h>
+#include <sys/types.h>
+#include <shadow.h>
+#include <pwd.h>
+#include <sys/resource.h>
+#include <errno.h>
+
+static char crypt_passwd[128];
+
+static int new_password(char * password);
+static char *pw_encrypt(const char *clear, const char *salt);
+
+void usage()
+{
+ printf("vyatta_pwd plainpwd username\n");
+
+}
+
+int main(int argc, char **argv)
+{
+ char *name;
+ char * password;
+
+ if (argc != 3) {
+ usage();
+ return(1);
+ }
+
+ /* "name" is unused */
+ name = argv[2];
+ password = argv[1];
+
+ if (new_password(password)) {
+ printf("error in password encrypt\n");
+ return(1);
+ }
+ // printf("%s, %s\n",name,password);
+ printf("%s",crypt_passwd);
+ return (0);
+}
+
+
+
+static int i64c(int i)
+{
+ if (i <= 0)
+ return ('.');
+ if (i == 1)
+ return ('/');
+ if (i >= 2 && i < 12)
+ return ('0' - 2 + i);
+ if (i >= 12 && i < 38)
+ return ('A' - 12 + i);
+ if (i >= 38 && i < 63)
+ return ('a' - 38 + i);
+ return ('z');
+}
+
+static char *crypt_make_salt(void)
+{
+ time_t now;
+ static unsigned long x;
+ static char result[3];
+
+ time(&now);
+ x += now + getpid() + clock();
+ result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
+ result[1] = i64c(((x >> 12) ^ x) & 077);
+ result[2] = '\0';
+ return result;
+}
+
+
+static int new_password(char * password)
+{
+ char *cp;
+ char salt[12]; /* "$N$XXXXXXXX" or "XX" */
+ char orig[200];
+ char pass[200];
+
+ orig[0] = '\0';
+
+ cp = (char*)password;
+
+ strncpy(pass, cp, sizeof(pass));
+ memset(cp, 0, strlen(cp));
+ memset(cp, 0, strlen(cp));
+ memset(orig, 0, sizeof(orig));
+ memset(salt, 0, sizeof(salt));
+
+ strcpy(salt, "$1$");
+ strcat(salt, crypt_make_salt());
+ strcat(salt, crypt_make_salt());
+ strcat(salt, crypt_make_salt());
+
+ strcat(salt, crypt_make_salt());
+ cp = pw_encrypt(pass, salt);
+
+ memset(pass, 0, sizeof pass);
+ strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
+ return 0;
+}
+
+char *pw_encrypt(const char *clear, const char *salt)
+{
+ static char cipher[128];
+ char *cp;
+ cp = (char *) crypt(clear, salt);
+ /* if crypt (a nonstandard crypt) returns a string too large,
+ truncate it so we don't overrun buffers and hope there is
+ enough security in what's left */
+ strncpy(cipher, cp, sizeof(cipher));
+ return cipher;
+}
+