From e9a79a249cec69fc178098d2f75db9389068510a Mon Sep 17 00:00:00 2001 From: An-Cheng Huang Date: Tue, 25 Sep 2007 15:55:26 -0700 Subject: initial import (from eureka /cli) plus new build system. --- .gitignore | 33 + AUTHORS | 1 + COPYING | 27 + Makefile.am | 53 + NEWS | 1 + README | 3 + configure.ac | 38 + debian/README | 8 + debian/autogen.sh | 37 + debian/changelog | 5 + debian/compat | 1 + debian/control | 23 + debian/copyright | 34 + debian/docs | 2 + debian/linda | 1 + debian/lintian | 6 + debian/rules | 101 + etc/bash_completion.d/vyatta-cfg | 789 ++++++++ scripts/VyattaConfig.pm | 548 ++++++ scripts/VyattaConfigDOMTree.pm | 364 ++++ scripts/VyattaConfigLoad.pm | 340 ++++ scripts/VyattaConfigOutput.pm | 253 +++ scripts/VyattaMisc.pm | 62 + scripts/VyattaTypeChecker.pm | 179 ++ scripts/XorpConfigParser.pm | 368 ++++ scripts/system/vyatta_update_login_user.pl | 172 ++ scripts/system/vyatta_update_logrotate.pl | 55 + scripts/system/vyatta_update_syslog.pl | 56 + scripts/vyatta-cli-expand-var.pl | 64 + scripts/vyatta-config-loader.pl | 51 + scripts/vyatta-find-type.pl | 21 + scripts/vyatta-load-config.pl | 69 + scripts/vyatta-output-config.pl | 9 + scripts/vyatta-save-config.pl | 45 + scripts/vyatta-validate-type.pl | 15 + scripts/xorp_tmpl_tool | 150 ++ src/.gitignore | 9 + src/cli_def.l | 424 +++++ src/cli_new.c | 1938 ++++++++++++++++++++ src/cli_objects.c | 343 ++++ src/cli_objects.h | 65 + src/cli_parse.y | 206 +++ src/cli_path_utils.c | 535 ++++++ src/cli_path_utils.h | 72 + src/cli_val.h | 206 +++ src/cli_val.l | 293 +++ src/cli_val_engine.c | 881 +++++++++ src/cli_val_engine.h | 86 + src/commit.c | 1364 ++++++++++++++ src/delete.c | 258 +++ src/set.c | 310 ++++ templates/interfaces/ethernet/node.def | 8 + .../interfaces/ethernet/node.tag/address/node.def | 5 + .../ethernet/node.tag/description/node.def | 2 + .../interfaces/ethernet/node.tag/duplex/node.def | 6 + .../interfaces/ethernet/node.tag/enable/node.def | 3 + .../interfaces/ethernet/node.tag/hw-id/node.def | 2 + .../interfaces/ethernet/node.tag/mac/node.def | 4 + .../interfaces/ethernet/node.tag/mtu/node.def | 5 + .../interfaces/ethernet/node.tag/speed/node.def | 6 + .../interfaces/ethernet/node.tag/vif/node.def | 7 + .../node.tag/vif/node.tag/address/node.def | 5 + .../node.tag/vif/node.tag/description/node.def | 2 + .../ethernet/node.tag/vif/node.tag/enable/node.def | 3 + templates/interfaces/loopback/node.def | 8 + .../interfaces/loopback/node.tag/address/node.def | 6 + .../loopback/node.tag/description/node.def | 2 + templates/interfaces/node.def | 1 + templates/system/domain-name/node.def | 15 + templates/system/domain-search/domain/node.def | 12 + templates/system/gateway-address/node.def | 17 + templates/system/host-name/node.def | 24 + templates/system/login/node.def | 3 + templates/system/login/radius-server/node.def | 14 + .../login/radius-server/node.tag/port/node.def | 4 + .../login/radius-server/node.tag/secret/node.def | 2 + .../login/radius-server/node.tag/timeout/node.def | 3 + templates/system/login/user/node.def | 17 + .../authentication/encrypted-password/node.def | 2 + .../login/user/node.tag/authentication/node.def | 1 + .../authentication/plaintext-password/node.def | 9 + .../system/login/user/node.tag/full-name/node.def | 2 + templates/system/name-server/node.def | 17 + templates/system/ntp-server/node.def | 16 + templates/system/options/node.def | 1 + templates/system/options/reboot-on-panic/node.def | 16 + templates/system/package/auto-sync/node.def | 15 + templates/system/package/node.def | 1 + templates/system/package/repository/node.def | 14 + .../package/repository/node.tag/component/node.def | 2 + .../repository/node.tag/description/node.def | 2 + .../package/repository/node.tag/url/node.def | 2 + .../system/static-host-mapping/host-name/node.def | 4 + .../host-name/node.tag/alias/node.def | 33 + .../host-name/node.tag/inet/node.def | 30 + templates/system/static-host-mapping/node.def | 1 + templates/system/syslog/console/facility/node.def | 11 + .../console/facility/node.tag/level/node.def | 3 + templates/system/syslog/console/node.def | 1 + templates/system/syslog/file/node.def | 4 + .../syslog/file/node.tag/archive/files/node.def | 3 + .../system/syslog/file/node.tag/archive/node.def | 8 + .../syslog/file/node.tag/archive/size/node.def | 3 + .../system/syslog/file/node.tag/facility/node.def | 13 + .../file/node.tag/facility/node.tag/level/node.def | 3 + .../system/syslog/global/archive/files/node.def | 3 + templates/system/syslog/global/archive/node.def | 8 + .../system/syslog/global/archive/size/node.def | 3 + templates/system/syslog/global/facility/node.def | 11 + .../syslog/global/facility/node.tag/level/node.def | 3 + templates/system/syslog/global/node.def | 1 + templates/system/syslog/host/node.def | 3 + .../system/syslog/host/node.tag/facility/node.def | 11 + .../host/node.tag/facility/node.tag/level/node.def | 3 + templates/system/syslog/node.def | 3 + templates/system/syslog/user/node.def | 3 + .../system/syslog/user/node.tag/facility/node.def | 14 + .../user/node.tag/facility/node.tag/level/node.def | 3 + templates/system/time-zone/node.def | 16 + tools/rl_passwd.cc | 152 ++ 120 files changed, 11614 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 configure.ac create mode 100644 debian/README create mode 100755 debian/autogen.sh create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/docs create mode 100644 debian/linda create mode 100644 debian/lintian create mode 100755 debian/rules create mode 100644 etc/bash_completion.d/vyatta-cfg create mode 100644 scripts/VyattaConfig.pm create mode 100644 scripts/VyattaConfigDOMTree.pm create mode 100755 scripts/VyattaConfigLoad.pm create mode 100755 scripts/VyattaConfigOutput.pm create mode 100755 scripts/VyattaMisc.pm create mode 100644 scripts/VyattaTypeChecker.pm create mode 100755 scripts/XorpConfigParser.pm create mode 100644 scripts/system/vyatta_update_login_user.pl create mode 100644 scripts/system/vyatta_update_logrotate.pl create mode 100644 scripts/system/vyatta_update_syslog.pl create mode 100755 scripts/vyatta-cli-expand-var.pl create mode 100755 scripts/vyatta-config-loader.pl create mode 100755 scripts/vyatta-find-type.pl create mode 100755 scripts/vyatta-load-config.pl create mode 100755 scripts/vyatta-output-config.pl create mode 100755 scripts/vyatta-save-config.pl create mode 100755 scripts/vyatta-validate-type.pl create mode 100755 scripts/xorp_tmpl_tool create mode 100644 src/.gitignore create mode 100644 src/cli_def.l create mode 100644 src/cli_new.c create mode 100644 src/cli_objects.c create mode 100644 src/cli_objects.h create mode 100644 src/cli_parse.y create mode 100644 src/cli_path_utils.c create mode 100644 src/cli_path_utils.h create mode 100644 src/cli_val.h create mode 100644 src/cli_val.l create mode 100644 src/cli_val_engine.c create mode 100644 src/cli_val_engine.h create mode 100644 src/commit.c create mode 100644 src/delete.c create mode 100644 src/set.c create mode 100644 templates/interfaces/ethernet/node.def create mode 100644 templates/interfaces/ethernet/node.tag/address/node.def create mode 100644 templates/interfaces/ethernet/node.tag/description/node.def create mode 100644 templates/interfaces/ethernet/node.tag/duplex/node.def create mode 100644 templates/interfaces/ethernet/node.tag/enable/node.def create mode 100644 templates/interfaces/ethernet/node.tag/hw-id/node.def create mode 100644 templates/interfaces/ethernet/node.tag/mac/node.def create mode 100644 templates/interfaces/ethernet/node.tag/mtu/node.def create mode 100644 templates/interfaces/ethernet/node.tag/speed/node.def create mode 100644 templates/interfaces/ethernet/node.tag/vif/node.def create mode 100644 templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def create mode 100644 templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def create mode 100644 templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def create mode 100644 templates/interfaces/loopback/node.def create mode 100644 templates/interfaces/loopback/node.tag/address/node.def create mode 100644 templates/interfaces/loopback/node.tag/description/node.def create mode 100644 templates/interfaces/node.def create mode 100644 templates/system/domain-name/node.def create mode 100644 templates/system/domain-search/domain/node.def create mode 100644 templates/system/gateway-address/node.def create mode 100644 templates/system/host-name/node.def create mode 100644 templates/system/login/node.def create mode 100644 templates/system/login/radius-server/node.def create mode 100644 templates/system/login/radius-server/node.tag/port/node.def create mode 100644 templates/system/login/radius-server/node.tag/secret/node.def create mode 100644 templates/system/login/radius-server/node.tag/timeout/node.def create mode 100644 templates/system/login/user/node.def create mode 100644 templates/system/login/user/node.tag/authentication/encrypted-password/node.def create mode 100644 templates/system/login/user/node.tag/authentication/node.def create mode 100644 templates/system/login/user/node.tag/authentication/plaintext-password/node.def create mode 100644 templates/system/login/user/node.tag/full-name/node.def create mode 100644 templates/system/name-server/node.def create mode 100644 templates/system/ntp-server/node.def create mode 100644 templates/system/options/node.def create mode 100644 templates/system/options/reboot-on-panic/node.def create mode 100644 templates/system/package/auto-sync/node.def create mode 100644 templates/system/package/node.def create mode 100644 templates/system/package/repository/node.def create mode 100644 templates/system/package/repository/node.tag/component/node.def create mode 100644 templates/system/package/repository/node.tag/description/node.def create mode 100644 templates/system/package/repository/node.tag/url/node.def create mode 100644 templates/system/static-host-mapping/host-name/node.def create mode 100644 templates/system/static-host-mapping/host-name/node.tag/alias/node.def create mode 100644 templates/system/static-host-mapping/host-name/node.tag/inet/node.def create mode 100644 templates/system/static-host-mapping/node.def create mode 100644 templates/system/syslog/console/facility/node.def create mode 100644 templates/system/syslog/console/facility/node.tag/level/node.def create mode 100644 templates/system/syslog/console/node.def create mode 100644 templates/system/syslog/file/node.def create mode 100644 templates/system/syslog/file/node.tag/archive/files/node.def create mode 100644 templates/system/syslog/file/node.tag/archive/node.def create mode 100644 templates/system/syslog/file/node.tag/archive/size/node.def create mode 100644 templates/system/syslog/file/node.tag/facility/node.def create mode 100644 templates/system/syslog/file/node.tag/facility/node.tag/level/node.def create mode 100644 templates/system/syslog/global/archive/files/node.def create mode 100644 templates/system/syslog/global/archive/node.def create mode 100644 templates/system/syslog/global/archive/size/node.def create mode 100644 templates/system/syslog/global/facility/node.def create mode 100644 templates/system/syslog/global/facility/node.tag/level/node.def create mode 100644 templates/system/syslog/global/node.def create mode 100644 templates/system/syslog/host/node.def create mode 100644 templates/system/syslog/host/node.tag/facility/node.def create mode 100644 templates/system/syslog/host/node.tag/facility/node.tag/level/node.def create mode 100644 templates/system/syslog/node.def create mode 100644 templates/system/syslog/user/node.def create mode 100644 templates/system/syslog/user/node.tag/facility/node.def create mode 100644 templates/system/syslog/user/node.tag/facility/node.tag/level/node.def create mode 100644 templates/system/time-zone/node.def create mode 100644 tools/rl_passwd.cc 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 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 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 +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 on +Mon, 24 Sep 2007 17:31:53 -0700. + +It's original content from the GIT repository + +Upstream Author: + + + +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 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='' + 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="" + 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="" + vyatta_cfg_type="" + else + vyatta_parse_tmpl "$1/node.def" + fi + if [ $is_set == 1 -a ! -z "$vyatta_cfg_type" ]; then + # add a 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 () { + 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, ") { + 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 () {$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, ). +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 () { + 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 () { + 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 () { + 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 () { + 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 <>$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 ''\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 [ ...]\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 \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 \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 = + * unreserved = + * escaped = + */ +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;} +<> { 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; + } +[\"\`] { + /* 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; + } + } + +\n { + /* error - unterminated string constant */ + yy_cli_def_lineno++; + } + +\\n {*string_buf_ptr++ = '\n';goto string_too_long;} +\\t {*string_buf_ptr++ = '\t';goto string_too_long;} +\\r {*string_buf_ptr++ = '\r';goto string_too_long;} +\\b {*string_buf_ptr++ = '\b';goto string_too_long;} +\\f {*string_buf_ptr++ = '\f';goto string_too_long;} +\\\n { yy_cli_def_lineno++; /* continuation => ignore */ } + +\\. { + *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; + } + } +[^\\\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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cli_val.h" +#include "cli_parse.h" +#include + +#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; inum; ++i) + printf("%d %s\n", i, (char *)(srtp->ptrs[i])); +} + + +void di(vtw_sorted *srtp) +{ + int i; + for (i=0; inum; ++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; actactions[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; if_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; if_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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cli_val.h" +#include "cli_parse.h" +#include + +#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 +#include +#include + +#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 VALUE +%token TYPE_DEF +%token VAR +%token STRING +%token EX_STRING +%token SYNTAX_ERROR +%token ACTION +%left ASSIGN +%left OR +%left AND +%right NOT +%left COMMA +%nonassoc COND +%token RP +%token LP +%type val +%type exp +%type val0 +%type action +%type 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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;ipath_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;ipath_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;i0 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 = + * unreserved = + * escaped = + */ +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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;ivalue=sarr[0]; + + } else { + + for(i=0;ivalue) { + + 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, /* */ + CLIND_CMD_NEIGHBOR, /* ../ */ + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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&/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 +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} + -- cgit v1.2.3