diff options
120 files changed, 11614 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5036292 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +*~ +.*.swp +*.[oa] +*.l[oa] +*.so +*.libs +*.deps +.dirstamp +libtool +/aclocal.m4 +/autom4te.cache +/build-stamp +/ChangeLog +/config +/config.log +/config.guess +/config.status +/config.sub +/configure +/debian/files +/debian/vyatta-cfg +/INSTALL +/Makefile.in +/Makefile +/src/my_commit +/src/my_set +/src/my_delete +/src/cli_def.c +/src/cli_parse.c +/src/cli_parse.h +/src/cli_val.c +/tools/rl_passwd + @@ -0,0 +1 @@ +eng@vyatta.com @@ -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) @@ -0,0 +1 @@ +see http://www.vyatta.com/news/ @@ -0,0 +1,3 @@ +This package has the Vyatta configuration system, including the configuration +back-end, the base configuration templates, and the config-mode CLI completion +mechanism. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..f12cf4f --- /dev/null +++ b/configure.ac @@ -0,0 +1,38 @@ +# Process this file with autoconf to produce a configure script. +AC_PREREQ(2.59) + +m4_define([VERSION_ID], [m4_esyscmd([ + if test -f .version ; then + head -n 1 .version | tr -d \\n + else + echo -n 2.4 + fi])]) +AC_INIT([vyatta-cfg], VERSION_ID, [vyatta-support@vyatta.com]) + +test -n "$VYATTA_VERSION" || VYATTA_VERSION=$PACKAGE_VERSION + +AC_CONFIG_AUX_DIR([config]) +AM_INIT_AUTOMAKE([gnu no-dist-gzip dist-bzip2 subdir-objects]) +AC_PREFIX_DEFAULT([/opt/vyatta]) + +AC_PROG_CC +AM_PROG_AS +AM_PROG_CC_C_O +AC_PROG_LIBTOOL +AC_PROG_LEX +AC_PROG_YACC + +AC_CHECK_LIB(crypt, crypt, [ LIBS="$LIBS -lcrypt" ]) + +AC_ARG_ENABLE([nostrip], + AC_HELP_STRING([--enable-nostrip], + [include -nostrip option during packaging]), + [NOSTRIP=-nostrip], [NOSTRIP=]) + +AC_CONFIG_FILES( + [Makefile]) + +AC_SUBST(NOSTRIP) + +AC_OUTPUT + diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..bbf471b --- /dev/null +++ b/debian/README @@ -0,0 +1,8 @@ +The Debian Package vyatta-cfg +---------------------------- + +This package has the Vyatta configuration system, including the configuration +back-end, the base configuration templates, and the config-mode CLI completion +mechanism. + + -- An-Cheng Huang <ancheng@vyatta.com> Mon, 24 Sep 2007 diff --git a/debian/autogen.sh b/debian/autogen.sh new file mode 100755 index 0000000..ff125d1 --- /dev/null +++ b/debian/autogen.sh @@ -0,0 +1,37 @@ +#!/bin/sh + + +if [ -d .git ] ; then +# generate GNU/Debian format ChangeLog from git log + + rm -f ChangeLog + + if which git2cl >/dev/null ; then + git-log --pretty --numstat --summary | git2cl >> ChangeLog + else + git-log --pretty=short >> ChangeLog + fi + +# append repository reference + + url=` git repo-config --get remote.origin.url` + test "x$url" = "x" && url=`pwd` + + branch=`git-branch --no-color | sed '/^\* /!d; s/^\* //'` + test "x$branch" = "x" && branch=master + + sha=`git log --pretty=oneline --no-color -n 1 | cut -c-8` + test "x$sha" = "x" && sha=00000000 + + echo "$url#$branch-$sha" >> ChangeLog + +fi + +rm -rf config +rm -f aclocal.m4 config.guess config.statusconfig.sub configure INSTALL + +autoreconf --force --install + +rm -f config.sub config.guess +ln -s /usr/share/misc/config.sub . +ln -s /usr/share/misc/config.guess . diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..01b2125 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +vyatta-cfg (0.1) unstable; urgency=low + + * Initial Release. + + -- An-Cheng Huang <ancheng@vyatta.com> Mon, 24 Sep 2007 17:31:53 -0700 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..2c4e495 --- /dev/null +++ b/debian/control @@ -0,0 +1,23 @@ +Source: vyatta-cfg +Section: contrib/net +Priority: extra +Maintainer: An-Cheng Huang <ancheng@vyatta.com> +Build-Depends: debhelper (>= 5), autotools-dev +Standards-Version: 3.7.2 + +Package: vyatta-cfg +Architecture: any +Depends: bash (>= 3.1), + sed (>= 4.1.5), + perl (>= 5.8.8), + procps (>= 1:3.2.7-3), + coreutils (>= 5.97-5.3) +Suggests: util-linux (>= 2.13-5), + net-tools, + ethtool, + ncurses-bin (>= 5.5-5), + ntpdate +Description: Vyatta configuration system + This package has the Vyatta configuration system, including the configuration + back-end, the base configuration templates, and the config-mode CLI completion + mechanism. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..4c6e7e6 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,34 @@ +This package was debianized by An-Cheng Huang <ancheng@vyatta.com> on +Mon, 24 Sep 2007 17:31:53 -0700. + +It's original content from the GIT repository <http://vyatt.com/git/vyatta-cfg> + +Upstream Author: + + <eng@vyatta.com> + +Copyright: + + Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + +License: + + The contents of this package are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + +On Debian systems, the complete text of the GNU General +Public License can be found in `/usr/share/common-licenses/GPL'. + +The Debian packaging is (C) 2007, An-Cheng Huang <ancheng@vyatta.com> and +is licensed under the GPL, see above. diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..50bd824 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +NEWS +README diff --git a/debian/linda b/debian/linda new file mode 100644 index 0000000..0381d9d --- /dev/null +++ b/debian/linda @@ -0,0 +1 @@ +Tag: file-in-opt diff --git a/debian/lintian b/debian/lintian new file mode 100644 index 0000000..ad0df30 --- /dev/null +++ b/debian/lintian @@ -0,0 +1,6 @@ +vyatta-cfg: file-in-unusual-dir +vyatta-cfg: dir-or-file-in-opt +vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/my_set /opt/vyatta/lib +vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/my_commit /opt/vyatta/lib +vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/my_delete /opt/vyatta/lib +vyatta-cfg: binary-or-shlib-defines-rpath ./opt/vyatta/sbin/rl_passwd /opt/vyatta/lib diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..3a7fc97 --- /dev/null +++ b/debian/rules @@ -0,0 +1,101 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +PACKAGE=vyatta-cfg +PKGDIR=$(CURDIR)/debian/$(PACKAGE) + +CFLAGS = -Wall -g + +configure = ./configure +configure += --host=$(DEB_HOST_GNU_TYPE) +configure += --build=$(DEB_BUILD_GNU_TYPE) +configure += --prefix=/opt/vyatta +configure += --mandir=\$${prefix}/share/man +configure += --infodir=\$${prefix}/share/info +configure += CFLAGS="$(CFLAGS)" +configure += LDFLAGS="-Wl,-z,defs" + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +configure: + chmod +x debian/autogen.sh + debian/autogen.sh + +config.status: configure + dh_testdir + rm -f config.cache + $(configure) + +build: build-stamp + +build-stamp: config.status + dh_testdir + $(MAKE) + touch $@ + +clean: clean-patched + +# Clean everything up, including everything auto-generated +# at build time that needs not to be kept around in the Debian diff +clean-patched: + dh_testdir + dh_testroot + if test -f Makefile ; then $(MAKE) clean distclean ; fi + rm -f build-stamp + rm -f config.status config.sub config.guess config.log + rm -f aclocal.m4 configure Makefile.in Makefile INSTALL + rm -rf config + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + $(MAKE) DESTDIR=$(PKGDIR) install + + install -D --mode=0644 debian/lintian $(PKGDIR)/usr/share/lintian/overrides/$(PACKAGE) + install -D --mode=0644 debian/linda $(PKGDIR)/usr/share/linda/overrides/$(PACKAGE) + +# Build architecture-independent files here. +binary-indep: build install + dh_testdir + dh_testroot + dh_installchangelogs ChangeLog + dh_installdocs + dh_install + dh_installdebconf + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture-dependent files here. +binary-arch: build install +# This is an architecture independent package +# so; we have nothing to do by default. + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/etc/bash_completion.d/vyatta-cfg b/etc/bash_completion.d/vyatta-cfg new file mode 100644 index 0000000..b16ffbf --- /dev/null +++ b/etc/bash_completion.d/vyatta-cfg @@ -0,0 +1,789 @@ +# **** License **** +# Version: VPL 1.0 +# +# The contents of this file are subject to the Vyatta Public License +# Version 1.0 ("License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://www.vyatta.com/vpl +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2006, 2007 Vyatta, Inc. +# All Rights Reserved. +# +# Author: An-Cheng Huang +# Date: 2007 +# Description: bash completion for Vyatta configuration commands +# +# **** End License **** + +# only do this if we are going into configure mode +if [ "$_OFR_CONFIGURE" != "ok" ]; then + return 0 +fi + +if [ -r /etc/default/vyatta ]; then + source /etc/default/vyatta +fi + +declare is_set=0 +declare last_idx=0 +declare -a comp_words=() + +# commands to unalias +declare -a unalias_cmds=( clear configure date debug edit exit load \ + no show save terminal undebug ) +for cmd in "${unalias_cmds[@]}"; do + unalias $cmd >& /dev/null +done + +show () +{ + eval "${vyatta_sbindir}/vyatta-output-config.pl \ + \${VYATTA_EDIT_LEVEL//\// } $@" +} + +save () +{ + eval "${vyatta_sbindir}/vyatta-save-config.pl $@" +} + +load () +{ + eval "${vyatta_sbindir}/vyatta-load-config.pl $@" +} + +declare vyatta_cfg_prompt_level='' +set_config_ps1 () +{ + local level=$1 + if [ -z "$level" ]; then + export PS1="[edit]\n\u@\h# " + vyatta_cfg_prompt_level='' + else + export PS1="[edit $level]\n\u@\h# " + vyatta_cfg_prompt_level="$level" + fi +} + +edit () +{ + local num_comp=${#@} + local _mpath=${VYATTA_TEMP_CONFIG_DIR}/${VYATTA_EDIT_LEVEL} + local _tpath=${VYATTA_CONFIG_TEMPLATE}/${VYATTA_TEMPLATE_LEVEL} + local idx + for (( idx=1; idx <= num_comp; idx++ )); do + local comp + eval "comp=\$$idx" + vyatta_escape comp comp + push_path _mpath $comp + push_path _tpath $comp + if [ ! -d $_mpath ]; then + # "edit" only allows existing node + break + fi + + # check if it's not tag value + if [ -d $_tpath ]; then + continue + fi + + # check if it's tag value + pop_path _tpath + push_path _tpath $VYATTA_TAG_NAME + if [ -d $_tpath ]; then + continue + fi + pop_path _tpath + pop_path _mpath + break + done + # "edit" only valid for + # * "node.tag" level + # * "node.def" level without "type:" + if (( idx != ( num_comp + 1) )); then + echo "Invalid node \"$*\" for the 'edit' command" + return 1 + fi + if [ "${_tpath:((-9))}" != "/node.tag" ]; then + # we are not at "node.tag" level. look for "type:". + if [ ! -r "$_tpath/node.def" ]; then + vyatta_cfg_type="" + else + vyatta_parse_tmpl "$_tpath/node.def" + fi + if [ -n "$vyatta_cfg_type" ]; then + # "type:" present + echo "The 'edit' command cannot be issued at the \"$*\" level" + return 1 + fi + fi + export VYATTA_EDIT_LEVEL="${_mpath#$VYATTA_TEMP_CONFIG_DIR}/" + export VYATTA_TEMPLATE_LEVEL="${_tpath#$VYATTA_CONFIG_TEMPLATE}/" + + declare -a path_arr + path_str2arr VYATTA_EDIT_LEVEL path_arr + local path_str="${path_arr[*]}" + set_config_ps1 $path_str +} + +really_exit() +{ + umount $VYATTA_TEMP_CONFIG_DIR + rm -rf $VYATTA_TEMP_CONFIG_DIR $VYATTA_CHANGES_ONLY_DIR $VYATTA_CONFIG_TMP + unset _OFR_CONFIGURE + builtin exit 0 +} + +exit () +{ + local discard + if [ $# == 0 ]; then + discard=0 + elif [ $# == 1 ] && [ "$1" == "discard" ]; then + discard=1 + else + echo "Invalid argument \"$*\" for 'exit'" + return 1 + fi + + if [ "$VYATTA_EDIT_LEVEL" == "/" ]; then + # we are at the root level. check if we can really exit. + if [ -f "$VYATTA_TEMP_CONFIG_DIR/$VYATTA_MOD_NAME" ]; then + if (( ! discard )); then + echo "Cannot exit: configuration modified." + echo "Use 'exit discard' to discard the changes and exit." + return 1 + fi + fi + really_exit + fi + + # "exit" to the root level. + export VYATTA_EDIT_LEVEL="/" + export VYATTA_TEMPLATE_LEVEL="/" + set_config_ps1 '' +} + +declare v_cfg_completion_debug=0 +decho () +{ + if (( v_cfg_completion_debug )); then + echo -n "$*" + fi +} + +push_path_arr () +{ + # $1: \@path_arr + # $2: component + eval "$1=( \${$1[@]} '$2' )" +} + +pop_path_arr () +{ + # $1: \@path_arr + eval "$1=( \${$1[@]:0:((\${#$1[@]}-1))} )" +} + +path_arr2str () +{ + # $1: \@path_arr + # $2: \$path_str + eval "$2=\"\${$1[*]}\"" + eval "$2=/\${$2// //}" +} + +path_str2arr () +{ + # $1: \$path_str + # $2: \@path_arr + local tmp + eval "tmp=\${$1:1}" + eval "$2=( \${tmp//\// } )" +} + +push_path () +{ + # $1: \$path_str + # $2: component + declare -a path_arr + eval "path_str2arr $1 path_arr" + eval "push_path_arr path_arr '$2'" + eval "path_arr2str path_arr $1" +} + +pop_path () +{ + # $1: \$path_str + declare -a path_arr + eval "path_str2arr $1 path_arr" + pop_path_arr path_arr + eval "path_arr2str path_arr $1" +} + +get_filtered_dir_listing () +{ + # $1: path + # $2: \@listing + if [ ! -d $1 ]; then + eval "$2=()" + return + fi + local pattern='^node\.def$|^node\.tag$|^node\.val$|^\.modified$' + patterh=$pattern'|^\.commit\.lck$|^\.wh\.' + local cmd="ls $1 |egrep -v '$pattern'" + declare -a listing=( $(eval $cmd) ) + for enode in "${listing[@]}"; do + local unode + vyatta_unescape enode unode + eval "$2[\${#$2[@]}]=$unode" + done +} + +filter_existing_nodes () +{ + # $1: mpath + # $2: \@orig + # $3: \@filtered + declare -a orig + eval "orig=( \${$2[@]} )" + for node in "${orig[@]}"; do + if [ -d "$1/$node" ]; then + eval "$3[\${#$3[@]}]=$node" + fi + done +} + +get_prefix_filtered_list () +{ + # $1: prefix + # $2: \@list + # $3: \@filtered + declare -a olist + eval "olist=( \"\${$2[@]}\" )" + local idx=0 + for elem in "${olist[@]}"; do + local sub=${elem#$1} + if [ "$elem" == "$sub" ]; then + continue + fi + eval "$3[$idx]=$elem" + (( idx++ )) + done +} + +declare vyatta_cfg_help="" +declare vyatta_cfg_type="" +declare vyatta_cfg_tag=0 +declare vyatta_cfg_multi=0 +declare -a vyatta_cfg_allowed=() +vyatta_parse_tmpl () +{ + # $1: tmpl + vyatta_cfg_help="" + vyatta_cfg_type="" + vyatta_cfg_tag=0 + vyatta_cfg_multi=0 + vyatta_cfg_allowed=() + if [ ! -r $1 ]; then + return + fi + eval `sed -n ' + /^help:[ ]\+/,/^[a-z]\+:/ { + s/^help:[ ]\+/vyatta_cfg_help=/p + /^ /p + } + /^syntax:[ ]\+\$(@)[ ]\+in[ ]\+/ { + s/^syntax:[ ]\+\$(@)[ ]\+in[ ]\+/vyatta_cfg_allowed=( / + s/^\([^;]\+\);.*$/\1 )/ + s/[ ]*,[ ]*/ /gp + } + s/^tag:/vyatta_cfg_tag=1/p + s/^multi:/vyatta_cfg_multi=1/p + s/^type:[ ]\+\([^ ]\+\)$/vyatta_cfg_type=\1/p + ' $1` + if [ -z "$vyatta_cfg_help" ]; then + vyatta_cfg_help='<No help text available>' + fi +} + +# this fills in $vyatta_help_text +generate_help_text () +{ + # $1: \@items + # $2: \@help_strs + declare -a items + declare -a helps + eval "items=( \"\${$1[@]}\" )" + eval "helps=( \"\${$2[@]}\" )" + vyatta_help_text="\\nPossible completions:" + for (( idx = 0; idx < ${#items[@]}; idx++ )); do + vyatta_help_text="${vyatta_help_text}\\n\\x20\\x20" + if [ ${#items[$idx]} -lt 6 ]; then + vyatta_help_text="${vyatta_help_text}${items[$idx]}\\t\\t" + elif [ ${#items[$idx]} -lt 14 ]; then + vyatta_help_text="${vyatta_help_text}${items[$idx]}\\t" + else + vyatta_help_text="${vyatta_help_text}${items[$idx]}\\n\\x20\\x20\\t\\t" + fi + vyatta_help_text="${vyatta_help_text}${helps[$idx]}" + done +} + +# this fills in $vyatta_help_text +get_tmpl_subdir_help () +{ + # $1: path + # $2: \@subdirs + declare -a subdirs + eval "subdirs=( \${$2[@]} )" + if [ ${#subdirs[@]} == 0 ]; then + vyatta_help_text="" + return + fi + declare -a hitems=() + declare -a hstrs=() + for subdir in "${subdirs[@]}"; do + if [ ! -r $1/$subdir/node.def ]; then + vyatta_cfg_help="<No help text available>" + else + vyatta_parse_tmpl "$1/$subdir/node.def" + fi + hitems[${#hitems[@]}]=$subdir + hstrs[${#hstrs[@]}]=$vyatta_cfg_help + done + generate_help_text hitems hstrs +} + +# return 0 if yes. 1 if no. +item_in_list () +{ + # $1: item + # $2: \@list + declare -a olist + local item + eval "olist=( \${$2[@]} )" + for item in "${olist[@]}"; do + if [ "$1" == "$item" ]; then + return 0 + fi + done + return 1 +} + +append_allowed_values () +{ + # $1: tmpl_path + # $2: \@values + if [ ! -r "$1/node.def" ]; then + return + fi + vyatta_parse_tmpl "$1/node.def" + local item + for item in "${vyatta_cfg_allowed[@]}"; do + if ! item_in_list $item $2; then + eval "$2=( \${$2[@]} \"$item\" )" + fi + done +} + +# return 0 if yes. 1 if no. +is_setting_new_leaf () +{ + # $1: tmpl_path + if [ $is_set == 0 ]; then + return 1 + fi + vyatta_parse_tmpl "$1/node.def" + if [ -z "$vyatta_cfg_type" ]; then + return 1 + fi + return 0 +} + +# this fills in $vyatta_help_text +get_node_value_help () +{ + # $1: path + # $2: \@values + declare -a vals + eval "vals=( \"\${$2[@]}\" )" + if [ $is_set == 0 -a ${#vals[@]} == 0 ]; then + vyatta_help_text="" + return + fi + if [ ! -r "$1/node.def" ]; then + vyatta_cfg_help="<No help text available>" + vyatta_cfg_type="" + else + vyatta_parse_tmpl "$1/node.def" + fi + if [ $is_set == 1 -a ! -z "$vyatta_cfg_type" ]; then + # add a <type> value + local val="<$vyatta_cfg_type>" + vals=( $val "${vals[@]}" ) + fi + if [ ${#vals[@]} == 0 ]; then + vyatta_help_text="" + return + fi + declare -a hitems=() + declare -a hstrs=() + for val in "${vals[@]}"; do + hitems[${#hitems[@]}]=$val + hstrs[${#hstrs[@]}]=$vyatta_cfg_help + done + generate_help_text hitems hstrs +} + +get_value_list () +{ + # $1: path + # $2: \@listing + local vfile=$1/node.val + if [ ! -r $vfile ]; then + eval "$2=()" + return + fi + declare -a listing=() + eval `sed 's/^\(.*\)$/listing[\\${#listing[@]}]='\''\1'\''/' $vfile` + eval "$2=( \"\${listing[@]}\" )" +} + +vyatta_escape () +{ + # $1: \$original + # $2: \$escaped + eval "$2=\${$1//\%/%25}" + eval "$2=\${$2//\//%2F}" +} + +vyatta_unescape () +{ + # $1: \$escaped + # $2: \$original + eval "$2=\${$1//\%2F/\/}" + eval "$2=\${$2//\%25/%}" +} + +declare -a vyatta_completions +declare vyatta_help_text="\\nNo help text available" +declare vyatta_do_help=0 +vyatta_do_complete () +{ + # when this function is called, it is expected that: + # * "vyatta_help_text" is filled with the help text. + # * "vyatta_completions" is an array of "filtered" possible completions + # (i.e., only those starting with the current last component). + local do_help=$vyatta_do_help + + # we may not want to do the following +<<'ENDCOMMENT' + if [ ${#vyatta_completions[@]} == 1 ]; then + # no ambiguous completions. do completion instead of help. + do_help=0 + fi + + # now check if we can auto-complete at least 1 more character. + if (( do_help )); then + local schar="" + for comp in "${vyatta_completions[@]}"; do + local sub=$comp + if [ ! -z "${COMP_WORDS[COMP_CWORD]}" ]; then + sub=${comp#${comp_words[$last_idx]}} + if [ "$comp" == "$sub" ]; then + # should not happen since vyatta_completions should be filtered. + continue + fi + fi + if [ -z "$schar" ]; then + schar=${sub:0:1} + else + if [ "$schar" != "${sub:0:1}" ]; then + schar="" + break + fi + fi + done + if [ ! -z "$schar" ]; then + do_help=0 + fi + fi +ENDCOMMENT + + if (( do_help )); then + echo -en $vyatta_help_text + COMPREPLY=( $(compgen -W "== --") ) + else + COMPREPLY=( $(compgen -W "${vyatta_completions[*]}" \ + -- ${COMP_WORDS[COMP_CWORD]}) ) + fi + vyatta_help_text="\\nNo help text available" +} + +vyatta_config_complete () +{ + if [ "$COMP_LINE" == "$VYATTA_COMP_LINE" ]; then + VYATTA_COMP_LINE='' + vyatta_do_help=1 + else + VYATTA_COMP_LINE=$COMP_LINE + vyatta_do_help=0 + fi + + local command=${COMP_WORDS[0]} + # completion for "set" is different from other commands + is_set=0 + if [ "$command" == "set" ]; then + is_set=1 + fi + local end_space=0 + local num_comp=$COMP_CWORD + if [ -z "${COMP_WORDS[$COMP_CWORD]}" ]; then + end_space=1 + (( num_comp -= 1 )) + fi + (( last_idx = num_comp - 1 )) + comp_words=( ${COMP_WORDS[@]:1:$num_comp} ) + + # handle "exit" + if [ "$command" == "exit" ]; then + if (( num_comp > 1 || ( end_space && num_comp > 0 ) )); then + COMPREPLY=() + return + fi + declare -a hitems=( "discard" ) + declare -a hstrs=( "Discard any changes" ) + generate_help_text hitems hstrs + vyatta_completions=( "discard" ) + vyatta_do_complete + return + fi + + local _mpath=${VYATTA_TEMP_CONFIG_DIR}/${VYATTA_EDIT_LEVEL} + local _tpath=${VYATTA_CONFIG_TEMPLATE}/${VYATTA_TEMPLATE_LEVEL} + local last_tag=0 + local idx=0 + for (( idx=0; idx < num_comp; idx++ )); do + last_tag=0 + local comp=${comp_words[$idx]} + vyatta_escape comp comp + push_path _mpath $comp + push_path _tpath $comp + if [ -d $_tpath ]; then + if (( ! is_set )); then + # we are not in "set" => only allow existing node + if [ ! -d $_mpath ]; then + break + fi + fi + continue + fi + pop_path _tpath + push_path _tpath $VYATTA_TAG_NAME + if [ -d $_tpath ]; then + if (( ! is_set && end_space )); then + # we are not in "set" && last component is complete. + # => only allow existing tag value. + if [ ! -d $_mpath ]; then + break + fi + fi + if (( idx != last_idx )); then + # TODO validate value + # break if not valid + # XXX is this validation necessary? (set will validate anyway) + true + fi + last_tag=1 + continue + fi + pop_path _tpath + pop_path _mpath + break + done + # at the end of the loop, 3 possibilities: + # 1. (idx < last_idx): some component before the last is invalid + # => invalid command + # 2. (idx == last_idx): last component matches neither template nor node.tag + # => if end_space, then invalid command + # otherwise, may be an incomplete (non-tag) component, or incomplete + # "leaf value" + # => try matching dirs in _tpath or value(s) in _mpath/node.val + # 3. (idx == num_comp): the whole command matches templates/tags + if (( idx < last_idx || ( idx == last_idx && end_space ) )); then + # TODO error message? + COMPREPLY=() + return + fi + + declare -a matches + if (( idx == last_idx )); then + # generate possibile matches (dirs in _tpath, and "help" from + # node.def in each dir, or values in _mpath/node.val) + declare -a fmatches + if [ -f $_mpath/node.val ]; then + decho [1a] + get_value_list $_mpath matches + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + append_allowed_values $_tpath fmatches + get_node_value_help $_tpath fmatches + else + decho [1b] + # see if the last component is a new leaf node + fmatches=() + if is_setting_new_leaf $_tpath; then + append_allowed_values $_tpath fmatches + get_node_value_help $_tpath fmatches + else + # last component is a non-value node. look for child nodes. + if (( ! is_set )); then + # not "set". only complete existing nodes. + declare -a amatches=() + get_filtered_dir_listing $_tpath amatches + filter_existing_nodes $_mpath amatches matches + else + get_filtered_dir_listing $_tpath matches + fi + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + get_tmpl_subdir_help $_tpath fmatches + fi + fi + vyatta_completions=( ${fmatches[@]} ) + vyatta_do_complete + return + fi + + if (( last_tag && end_space )); then + # if not "set", check _mpath (last component is the tag) is valid + # generate possible matches (dirs in _tpath, and "help" from node.def + # in each dir) + decho [2] + if [ $is_set == 1 -o -d $_mpath ]; then + if (( ! is_set )); then + # not "set". only complete existing nodes. + declare -a fmatches=() + get_filtered_dir_listing $_tpath fmatches + filter_existing_nodes $_mpath fmatches matches + else + get_filtered_dir_listing $_tpath matches + fi + get_tmpl_subdir_help $_tpath matches + vyatta_completions=( ${matches[@]} ) + vyatta_do_complete + return + fi + return + fi + + if (( last_tag && !end_space )); then + # generate possible matches (dirs in _mpath, and "help" from node.def + # in dirs in _tpath) + decho [3] + pop_path _mpath + pop_path _tpath + get_filtered_dir_listing $_mpath matches + declare -a fmatches + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + append_allowed_values $_tpath fmatches + get_node_value_help $_tpath fmatches + vyatta_completions=( ${fmatches[@]} ) + vyatta_do_complete + return + fi + + if (( !last_tag && end_space )); then + # generate possible matches + # 1. dirs in _tpath, and "help" from node.def in each dir + # 2. value(s) in _mpath/node.val (only if _tpath/node.def is "multi:") + # 3. dirs in _mpath (only if _tpath/node.def is "tag:") + if [ -d $_tpath/node.tag ]; then + # last component is a "tag name". look for tag values. + decho [4a] + get_filtered_dir_listing $_mpath matches + append_allowed_values $_tpath matches + get_node_value_help $_tpath matches + elif [ -f $_mpath/node.val ]; then + # last component is a leaf node. look for values. + decho [4b] + get_value_list $_mpath matches + append_allowed_values $_tpath matches + get_node_value_help $_tpath matches + else + decho [4c] + # see if the last component is a new leaf node + matches=() + if is_setting_new_leaf $_tpath; then + append_allowed_values $_tpath matches + get_node_value_help $_tpath matches + else + # last component is a non-value node. look for child nodes. + if (( ! is_set )); then + # not "set". only complete existing nodes. + declare -a fmatches=() + get_filtered_dir_listing $_tpath fmatches + filter_existing_nodes $_mpath fmatches matches + else + get_filtered_dir_listing $_tpath matches + fi + get_tmpl_subdir_help $_tpath matches + fi + fi + vyatta_completions=( ${matches[@]} ) + vyatta_do_complete + return + fi + + if (( !last_tag && !end_space )); then + # generate possible matches (dirs in _tpath, and "help" from node.def + # in each dir) + decho [5] + pop_path _tpath + get_filtered_dir_listing $_tpath matches + declare -a fmatches + get_prefix_filtered_list ${comp_words[$last_idx]} matches fmatches + get_tmpl_subdir_help $_tpath fmatches + vyatta_completions=( ${fmatches[@]} ) + vyatta_do_complete + return + fi +} + +mkdir -p $VYATTA_ACTIVE_CONFIGURATION_DIR +mkdir -p $VYATTA_CHANGES_ONLY_DIR +mkdir -p $VYATTA_CONFIG_TMP +if [ ! -d $VYATTA_TEMP_CONFIG_DIR ]; then + mkdir -p $VYATTA_TEMP_CONFIG_DIR + sudo mount -t unionfs -o dirs=${VYATTA_CHANGES_ONLY_DIR}=rw:/opt/vyatta/config/active=ro unionfs ${VYATTA_TEMP_CONFIG_DIR} +fi + +# disallow 'Ctrl-D' exit, since we need special actions on 'exit' +set -o ignoreeof 1 + +set_config_ps1 '' +alias commit=my_commit +alias set=my_set +alias delete=my_delete + +export VYATTA_COMP_LINE="" + +# readline bindings +bind 'set show-all-if-ambiguous on' +if ! bind -p |grep -q '\\C-x\\C-t'; then + bind '"\C-x\C-t": kill-region' +fi +if ! bind -p |grep -q '\\C-x\\C-o'; then + bind '"\C-x\C-o": copy-region-as-kill' +fi + +complete -F vyatta_config_complete set +complete -F vyatta_config_complete delete +complete -F vyatta_config_complete show +complete -F vyatta_config_complete edit +complete -F vyatta_config_complete exit + diff --git a/scripts/VyattaConfig.pm b/scripts/VyattaConfig.pm new file mode 100644 index 0000000..e9a1f97 --- /dev/null +++ b/scripts/VyattaConfig.pm @@ -0,0 +1,548 @@ +package VyattaConfig; + +use strict; + +use VyattaConfigDOMTree; + +my %fields = ( + _changes_only_dir_base => $ENV{VYATTA_CHANGES_ONLY_DIR}, + _new_config_dir_base => $ENV{VYATTA_TEMP_CONFIG_DIR}, + _active_dir_base => $ENV{VYATTA_ACTIVE_CONFIGURATION_DIR}, + _vyatta_template_dir => $ENV{VYATTA_CONFIG_TEMPLATE}, + _current_dir_level => "/", + _level => undef, +); + +sub new { + my $that = shift; + my $class = ref ($that) || $that; + my $self = { + %fields, + }; + + bless $self, $class; + return $self; +} + +sub _set_current_dir_level { + my ($self) = @_; + my $level = $self->{_level}; + + $level =~ s/\//%2F/g; + $level =~ s/\s+/\//g; + + $self->{_current_dir_level} = "/$level"; + return $self->{_current_dir_level}; +} + +## setLevel("level") +# if "level" is supplied, set the current level of the hierarchy we are working on +# return the current level +sub setLevel { + my ($self, $level) = @_; + + $self->{_level} = $level if defined($level); + $self->_set_current_dir_level(); + + return $self->{_level}; +} + +## listNodes("level") +# return array of all nodes at "level" +# level is relative +sub listNodes { + my ($self, $path) = @_; + my @nodes = (); + + if (defined $path) { + $path =~ s/\//%2F/g; + $path =~ s/\s+/\//g; + $path = $self->{_new_config_dir_base} . $self->{_current_dir_level} . "/" . $path; + } + else { + $path = $self->{_new_config_dir_base} . $self->{_current_dir_level}; + } + + #print "DEBUG VyattaConfig->listNodes(): path = $path\n"; + opendir DIR, "$path" or return (); + @nodes = grep !/^\./, readdir DIR; + closedir DIR; + + my @nodes_modified = (); + while (@nodes) { + my $tmp = pop (@nodes); + $tmp =~ s/\n//g; + $tmp =~ s/%2F/\//g; + #print "DEBUG VyattaConfig->listNodes(): node = $tmp\n"; + push @nodes_modified, $tmp; + } + + return @nodes_modified; +} + +## listOrigNodes("level") +# return array of all original nodes (i.e., before any current change; i.e., +# in "working") at "level" +# level is relative +sub listOrigNodes { + my ($self, $path) = @_; + my @nodes = (); + + if (defined $path) { + $path =~ s/%2F/\//g; + $path =~ s/\s+/\//g; + $path = $self->{_active_dir_base} . $self->{_current_dir_level} . "/" + . $path; + } + else { + $path = $self->{_active_dir_base} . $self->{_current_dir_level}; + } + + #print "DEBUG VyattaConfig->listNodes(): path = $path\n"; + opendir DIR, "$path" or return (); + @nodes = grep !/^\./, readdir DIR; + closedir DIR; + + my @nodes_modified = (); + while (@nodes) { + my $tmp = pop (@nodes); + $tmp =~ s/\n//g; + $tmp =~ s/%2F/\//g; + #print "DEBUG VyattaConfig->listNodes(): node = $tmp\n"; + push @nodes_modified, $tmp; + } + + return @nodes_modified; +} + +## returnParent("level") +# return the name of parent node relative to the current hierarchy +# in this case "level" is set to the parent dir ".. .." +# for example +sub returnParent { + my ($self, $node) = @_; + my $x, my $tmp; + + # split our hierarchy into vars on a stack + my @level = split /\s+/, $self->{_level}; + + # count the number of parents we need to lose + # and then pop 1 less + $x = split /\s+/, $node; + for ($tmp = 1; $tmp < $x; $tmp++) { + pop @level; + } + + # return the parent + $tmp = pop @level; + return $tmp; +} + +## returnValue("node") +# returns the value of "node" or undef if the node doesn't exist . +# node is relative +sub returnValue { + my ( $self, $node ) = @_; + my $tmp; + + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + if ( -f "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node/node.val" ) { + open FILE, "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node/node.val" || return undef; + read FILE, $tmp, 16384; + close FILE; + + $tmp =~ s/\n$//; + return $tmp; + } + else { + return undef; + } +} + + +## returnOrigValue("node") +# returns the original value of "node" (i.e., before the current change; i.e., +# in "working") or undef if the node doesn't exist. +# node is relative +sub returnOrigValue { + my ( $self, $node ) = @_; + my $tmp; + + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + my $filepath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node"; + if ( -f "$filepath/node.val") { + open FILE, "$filepath/node.val" || return undef; + read FILE, $tmp, 16384; + close FILE; + + $tmp =~ s/\n$//; + return $tmp; + } else { + return undef; + } +} + +## returnValues("node") +# returns an array of all the values of "node", or an empty array if the values do not exist. +# node is relative +sub returnValues { + my $val = returnValue(@_); + my @values = split("\n", $val); + return @values; +} + +## returnOrigValues("node") +# returns an array of all the original values of "node" (i.e., before the +# current change; i.e., in "working"), or an empty array if the values do not +# exist. +# node is relative +sub returnOrigValues { + my $val = returnOrigValue(@_); + my @values = split("\n", $val); + return @values; +} + +## exists("node") +# Returns true if the "node" exists. +sub exists { + my ( $self, $node ) = @_; + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + + if ( -d "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node" ) { + #print "DEBUG: the dir is there\n"; + return !0; + } else { + return undef; + } +} + +## isDeleted("node") +# is the "node" deleted. node is relative. returns true or false +sub isDeleted { + my ($self, $node) = @_; + my $endnode = undef; + my $filepath = undef; + my @nodes = (); + + # split the string into an array + (@nodes) = split /\s+/, $node; + + # take the last node off the string + $endnode = pop @nodes; + # and modify it to match the whiteout name + $endnode = ".wh.$endnode"; + + # setup the path with the rest of the nodes + # use the change_dir + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node"; + + # if the file exists, the node was deleted + if (-f "$filepath") { return 1; } + else { return 0; } +} + +## listDeleted("level") +# return array of deleted nodes in the "level" +# "level" defaults to current +sub listDeleted { + my ($self, $node) = @_; + my @return = (); + my $filepath = undef; + my $curpath = undef; + my @nodes = (); + my @curnodes = (); + + # setup the entire path with the new level + # use the change_dir + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g; + $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node/"; + + $curpath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node/"; + + # let's see if the directory exists and find the the whiteout files + if (! -d "$filepath") { return undef; } + else { + opendir DIR, "$filepath" or return undef; + @nodes = grep /^\.wh./, readdir DIR; + closedir DIR; + } + + if (! -d "$curpath") { + return undef; + } else { + opendir DIR, "$curpath" or return undef; + @curnodes = grep !/^\./, readdir DIR; + closedir DIR; + } + + # get rid of the whiteout prefix + my $dir_opq = 0; + foreach $node (@nodes) { + $node =~ s/^\.wh\.(.+)/\1/; + $_ = $node; + if (! /__dir_opaque/) { + push @return, $node; + } else { + $dir_opq = 1; + } + } + + if ($dir_opq) { + # if this node is "dir_opaque", it has been deleted and re-added. + # add all nodes in "active" to the return list (so that they will be + # marked "deleted"). note that if a node is also re-added, its status + # will be changed after the listDeleted call. + push @return, @curnodes; + } + + return @return; +} + +## isChanged("node") +# will check the change_dir to see if the "node" has been changed from a previous +# value. returns true or false. +sub isChanged { + my ($self, $node) = @_; + + # let's setup the filepath for the change_dir + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g, $node; + my $filepath = "$self->{_changes_only_dir_base}$self->{_current_dir_level}/$node"; + + # if the node exists in the change dir, it's modified. + if (-e "$filepath") { return 1; } + else { return 0; } +} + +## isAdded("node") +# will compare the new_config_dir to the active_dir to see if the "node" has +# been added. returns true or false. +sub isAdded { + my ($self, $node) = @_; + + #print "DEBUG VyattaConfig->isAdded(): node $node\n"; + # let's setup the filepath for the modify dir + $node =~ s/\//%2F/g; + $node =~ s/\s+/\//g, $node; + my $filepath = "$self->{_new_config_dir_base}$self->{_current_dir_level}/$node"; + + #print "DEBUG VyattaConfig->isAdded(): filepath $filepath\n"; + + # if the node doesn't exist in the modify dir, it's not + # been added. so short circuit and return false. + if (! -e "$filepath") { return 0; } + + # now let's setup the path for the working dir + my $filepath = "$self->{_active_dir_base}$self->{_current_dir_level}/$node"; + + # if the node is in the active_dir it's not new + if (-e "$filepath") { return 0; } + else { return 1; } +} + +## listNodeStatus("level") +# return a hash of the status of nodes at the current config level +# node name is the hash key. node status is the hash value. +# node status can be one of deleted, added, changed, or static +sub listNodeStatus { + my ($self, $path) = @_; + my @nodes = (); + my %nodehash = (); + my $node = undef; + + # find deleted nodes first + @nodes = $self->listDeleted("$path"); + foreach $node (@nodes) { + if ($node =~ /.+/) { $nodehash{$node} = "deleted" }; + } + + @nodes = (); + @nodes = $self->listNodes("$path"); + foreach $node (@nodes) { + if ($node =~ /.+/) { + #print "DEBUG VyattaConfig->listNodeStatus(): node $node\n"; + if ($self->isAdded("$path $node")) { $nodehash{$node} = "added"; } + elsif ($self->isChanged("$path $node")) { $nodehash{$node} = "changed"; } + elsif ($self->isDeleted("$path $node")) { $nodehash{$node} = "deleted"; } + else { $nodehash{$node} = "static"; } + } + } + + return %nodehash; +} + +############ DOM Tree ################ + +#Create active DOM Tree +sub createActiveDOMTree { + + my $self = shift; + + my $tree = new VyattaConfigDOMTree($self->{_active_dir_base} . $self->{_current_dir_level},"active"); + + return $tree; +} + +#Create changes only DOM Tree +sub createChangesOnlyDOMTree { + + my $self = shift; + + my $tree = new VyattaConfigDOMTree($self->{_changes_only_dir_base} . $self->{_current_dir_level}, + "changes_only"); + + return $tree; +} + +#Create new config DOM Tree +sub createNewConfigDOMTree { + + my $self = shift; + + my $tree = new VyattaConfigDOMTree($self->{_new_config_dir_base} . $self->{_current_dir_level}, + "new_config"); + + return $tree; +} + + +###### functions for templates ###### + +# $1: array representing the config path (note that path must be present +# in current config) +sub getTmplPath { + my $self = shift; + my @cfg_path = @{$_[0]}; + my $tpath = $self->{_vyatta_template_dir}; + for my $p (@cfg_path) { + if (-d "$tpath/$p") { + $tpath .= "/$p"; + next; + } + if (-d "$tpath/node.tag") { + $tpath .= "/node.tag"; + next; + } + # the path is not valid! not supposed to happen! + die "Node path \"" . (join ' ', @cfg_path) . "\" is not valid"; + } + return $tpath +} + +sub isTagNode { + my $self = shift; + my $cfg_path_ref = shift; + my $tpath = $self->getTmplPath($cfg_path_ref); + if (-d "$tpath/node.tag") { + return 1; + } + return 0; +} + +sub hasTmplChildren { + my $self = shift; + my $cfg_path_ref = shift; + my $tpath = $self->getTmplPath($cfg_path_ref); + opendir(TDIR, $tpath) or return 0; + my @tchildren = grep !/^node\.def$/, (grep !/^\./, (readdir TDIR)); + closedir TDIR; + if (scalar(@tchildren) > 0) { + return 1; + } + return 0; +} + +# returns ($is_multi, $is_text) +sub parseTmpl { + my $self = shift; + my $cfg_path_ref = shift; + my ($is_multi, $is_text) = (0, 0); + my $tpath = $self->getTmplPath($cfg_path_ref); + if (! -r "$tpath/node.def") { + return ($is_multi, $is_text); + } + open(TMPL, "<$tpath/node.def") or return ($is_multi, $is_text); + foreach (<TMPL>) { + if (/^multi:/) { + $is_multi = 1; + } + if (/^type:\s+txt\s*$/) { + $is_text = 1; + } + } + close TMPL; + return ($is_multi, $is_text); +} + +###### misc functions ###### + +# compare two value lists and return "deleted" and "added" lists. +# since this is for multi-value nodes, there is no "changed" (if a value's +# ordering changed, it is deleted then added). +# $0: \@orig_values +# $1: \@new_values +sub compareValueLists { + my $self = shift; + my @ovals = @{$_[0]}; + my @nvals = @{$_[1]}; + my %comp_hash = ( + 'deleted' => [], + 'added' => [], + ); + my $idx = 0; + my %ohash = map { $_ => ($idx++) } @ovals; + $idx = 0; + my %nhash = map { $_ => ($idx++) } @nvals; + my $min_changed_idx = 2**31; + my %dhash = (); + foreach (@ovals) { + if (!defined($nhash{$_})) { + push @{$comp_hash{'deleted'}}, $_; + $dhash{$_} = 1; + if ($ohash{$_} < $min_changed_idx) { + $min_changed_idx = $ohash{$_}; + } + } + } + foreach (@nvals) { + if (defined($ohash{$_})) { + if ($ohash{$_} != $nhash{$_}) { + if ($ohash{$_} < $min_changed_idx) { + $min_changed_idx = $ohash{$_}; + } + } + } + } + foreach (@nvals) { + if (defined($ohash{$_})) { + if ($ohash{$_} != $nhash{$_}) { + if (!defined($dhash{$_})) { + push @{$comp_hash{'deleted'}}, $_; + $dhash{$_} = 1; + } + push @{$comp_hash{'added'}}, $_; + } elsif ($ohash{$_} >= $min_changed_idx) { + # ordering unchanged, but something before it is changed. + if (!defined($dhash{$_})) { + push @{$comp_hash{'deleted'}}, $_; + $dhash{$_} = 1; + } + push @{$comp_hash{'added'}}, $_; + } else { + # this is before any changed value. do nothing. + } + } else { + push @{$comp_hash{'added'}}, $_; + } + } + return %comp_hash; +} + + diff --git a/scripts/VyattaConfigDOMTree.pm b/scripts/VyattaConfigDOMTree.pm new file mode 100644 index 0000000..d951202 --- /dev/null +++ b/scripts/VyattaConfigDOMTree.pm @@ -0,0 +1,364 @@ +# +# Module: serial +# +# **** License **** +# Version: VPL 1.0 +# +# The contents of this file are subject to the Vyatta Public License +# Version 1.0 ("License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://www.vyatta.com/vpl +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# This code was originally developed by Vyatta, Inc. +# Portions created by Vyatta are Copyright (C) 2005, 2006, 2007 Vyatta, Inc. +# All Rights Reserved. +# +# Author: Oleg Moskalenko +# Date: 2007 +# Description: +# +# **** End License **** +# +# + +package VyattaConfigDOMTree; + +use strict; + +my %fields = ( + _dir => undef, + _name => undef, + _value => undef, + _subnodes => undef + ); + +sub new { + + my $that = shift; + my $dir = shift; + my $name = shift; + + my $class = ref ($that) || $that; + + my $self = { + %fields + }; + + bless $self, $class; + + $self->{_dir} = $dir; + $self->{_name} = $name; + + return $self->_construct_dom_tree(); +} + +#Simple DOM Tree iteration and screen output +#$1 - left screen offset (optional) +sub print { + + my $self = shift; + my $level = shift; + + my $tree = $self; + + if(!(defined $level)) { + $level=""; + } + + if(defined $tree) { + + print("$level name=",$tree->getNodeName(),"\n"); + + my $value = $tree->getNodeValue(); + + if(defined $value) { + + print("$level value=$value\n"); + + } + + my @subnodes = $tree->getSubNodes(); + + while(@subnodes) { + + my $subnode = shift @subnodes; + $subnode->print($level . " "); + } + } +} + +#Return value of the tree node +sub getNodeValue { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + if(defined $tree) { + + $ret = $tree->{_value}; + } + + return $ret; +} + +#Return value of the tree node. +#If the value is nor defined, return empty string. +sub getNodeValueAsString { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + if(defined $tree) { + + $ret = $tree->getNodeValue(); + } + + if(!defined $ret) { + $ret = ""; + } + + return $ret; +} + +#Return name of the tree node +sub getNodeName { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + if(defined $tree) { + + $ret = $tree->{_name}; + } + + return $ret; +} + +#Return array of subnodes of the tree node +sub getSubNodes { + + my $self = shift; + my $tree = $self; + + my @ret = (); + + if(defined $tree) { + + my $subnodes = $tree->{_subnodes}; + + if(defined $subnodes) { + + @ret = values %{$subnodes}; + + } + } + + return @ret; +} + +sub isLeafNode { + + my $self = shift; + my $tree = $self; + + my $ret=undef; + + if(defined $tree) { + + if(defined $tree->{_value}) { + + if(! defined $tree->{_subnodes}) { + + $ret="true"; + } + } + } + + return $ret; +} + +#Return subtree of the tree according to the path list +#$1, $2, ... - path to the subtree +sub getSubNode { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + while(@_ && $tree) { + + my $subnode = shift (@_); + + my $subnodes = $tree->{_subnodes}; + + if(defined $subnodes) { + + $tree = $subnodes->{$subnode}; + + } else { + + $tree = undef; + + } + } + + $ret=$tree; + + return $ret; +} + +#Return value of the subnode of the tree according to the path list +#$1, $2, ... - path to the subtree +sub getSubNodeValue { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + if(defined $tree) { + + my $node = $tree->getSubNode(@_); + + if(defined $node) { + + $ret=$node->getNodeValue(); + } + } + + return $ret; +} + +#Return value of the subnode of the tree according to the path list. +#If the value is not defined, return empty string. +#$1, $2, ... - path to the subtree +sub getSubNodeValueAsString { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + if(defined $tree) { + + my $node = $tree->getSubNode(@_); + + if(defined $node) { + + $ret=$node->getNodeValue(); + } + } + + if(! defined $ret) { + $ret = ""; + } + + return $ret; +} + +#Check if there is a subnode with the specified path. +#$1, $2, ... - path to the subtree +sub subNodeExist { + + my $self = shift; + my $tree = $self; + + my $ret = undef; + + if(defined $tree) { + + my $node = $tree->getSubNode(@_); + + if(defined $node) { + + $ret="true"; + } + } + + return $ret; +} + +#Return of the children of the node +#$1, $2, ... - path to the subtree +sub getSubNodesNumber { + + my $self = shift; + my $tree = $self; + + my $ret = 0; + + if(defined $tree) { + + my $node = $tree->getSubNode(@_); + + if(defined $node) { + + my @subs = $node->getSubNodes(); + + if(defined @subs) { + $ret = $#subs + 1; + } + } + } + + return $ret; +} + +#private method: costruct DOM Tree according to the absolute path provided +sub _construct_dom_tree { + + my $self = shift; + + my $subnodesNum=0; + my $valuePresent=0; + + if(!(defined $self)) {return undef;} + + opendir DIR, $self->{_dir} or return undef; + my @entries = grep !/^\./, readdir DIR; + closedir DIR; + + while(@entries) { + + my $entry = shift @entries; + + if($entry) { + my $fn = $self->{_dir} . "/" . $entry; + if( -f $fn) { + if($entry eq "node.val") { + my $value=`cat $fn`; + while(chomp $value) {}; + $self->{_value} = $value; + $valuePresent++; + } + } elsif (-d $fn) { + my $subnode = new VyattaConfigDOMTree($fn,$entry); + if(defined $subnode) { + if(! defined $self->{_subnodes} ) { + $self->{_subnodes} = {}; + } + $self->{_subnodes}->{$entry} = $subnode; + $subnodesNum++; + } + } + } + } + + if($valuePresent<1 && $subnodesNum<1) { + return undef; + } + + return $self; +} diff --git a/scripts/VyattaConfigLoad.pm b/scripts/VyattaConfigLoad.pm new file mode 100755 index 0000000..eae2946 --- /dev/null +++ b/scripts/VyattaConfigLoad.pm @@ -0,0 +1,340 @@ +# Perl module for loading configuration. +package VyattaConfigLoad; + +use strict; +use sort 'stable'; +use lib "/opt/vyatta/share/perl5/"; +use XorpConfigParser; +use VyattaConfig; + +# configuration ordering. higher rank configured before lower rank. +my $default_rank = 0; +my %config_rank = ( + 'interfaces' => 100, + 'system' => 90, + ); + +my @all_nodes = (); +my @all_naked_nodes = (); + +sub get_config_rank { + # longest prefix match + my @path = @_; + while ((scalar @path) > 0) { + my $path_str = join ' ', @path; + if (defined($config_rank{$path_str})) { + return ($config_rank{$path_str}); + } + pop @path; + } + return $default_rank; +} + +sub applySingleQuote { + my @return = (); + foreach (@_) { + # change all single quotes to "'\''" since we're going to single-quote + # every component of the command + if (/^'(.*)'$/) { + $_ = $1; + } + $_ =~ s/'/'\\''/g; + # single-quote every component of the command + if (/^'.*'$/) { + push @return, $_; + } elsif (/^"(.*)"$/) { + push @return, "'$1'"; + } else { + push @return, "'$_'"; + } + } + return @return; +} + +sub enumerate_branch { + my $cur_node = shift; + my @cur_path = @_; + # name not defined at root level + if (defined($cur_node->{'name'})) { + my $name = $cur_node->{'name'}; + if ($name =~ /^\s*(\S+)\s+(\S.*)$/) { + push @cur_path, ($1, $2); + } else { + push @cur_path, $name; + } + } + my $terminal = 0; + if (!defined($cur_node->{'children'})) { + $terminal = 1; + } else { + foreach (@{$cur_node->{'children'}}) { + if (defined($_->{'name'})) { + enumerate_branch($_, @cur_path); + $terminal = 0; + } + } + } + if ($terminal) { + my $val = $cur_node->{'value'}; + if (defined($val)) { + push @cur_path, $val; + } + push @all_naked_nodes, [ @cur_path ]; + my @qpath = applySingleQuote(@cur_path); + push @all_nodes, [\@qpath, get_config_rank(@cur_path)]; + } +} + +# $0: config file to load +# return: list of all config statement sorted by rank +sub getStartupConfigStatements { + # clean up the lists first + @all_nodes = (); + @all_naked_nodes = (); + + my $load_cfg = shift; + if (!defined($load_cfg)) { + return (); + } + + my $xcp = new XorpConfigParser(); + $xcp->parse($load_cfg); + my $root = $xcp->get_node( () ); + if (!defined($root)) { + return (); + } + enumerate_branch($root, ( )); + + @all_nodes = sort { ${$b}[1] <=> ${$a}[1] } @all_nodes; + return @all_nodes; +} + +my %node_order = (); + +# $0: ref of list of parsed naked statements. +# return: hash containing the config hierarchy. +sub generateHierarchy { + my @node_list = @{$_[0]}; + my %hash = (); + %node_order = (); + my $order = 0; + foreach my $node (@node_list) { + my @path = @{$node}; + my $path_str = join ' ', @path; + $node_order{$path_str} = $order; + $order++; + my $cur_ref = \%hash; + foreach (@path) { + if (!defined($cur_ref->{$_})) { + $cur_ref->{$_} = { }; + } + $cur_ref = $cur_ref->{$_}; + } + } + return %hash; +} + +# $0: config file to load. +# return: hash containing the config hierarchy. +sub loadConfigHierarchy { + # clean up the lists first + @all_nodes = (); + @all_naked_nodes = (); + + my $load_cfg = shift; + if (!defined($load_cfg)) { + return (); + } + + my $xcp = new XorpConfigParser(); + $xcp->parse($load_cfg); + my $root = $xcp->get_node( () ); + if (!defined($root)) { + return (); + } + enumerate_branch($root, ( )); + + return generateHierarchy(\@all_naked_nodes); +} + +# $0: ref of hierarchy root. +# $1: display prefix. +sub printHierarchy { + my $cur_ref = shift; + my $prefix = shift; + foreach (sort keys %{$cur_ref}) { + print "$prefix$_"; + if (scalar(keys %{$cur_ref->{$_}}) == 0) { + print " (terminal)\n"; + next; + } else { + print "\n"; + } + printHierarchy($cur_ref->{$_}, "$prefix "); + } +} + +# $0: hash ref representing a "multi:" node. +# $1: array ref representing current config path. +# returns the list of node values sorted by the original order. +sub getSortedMultiValues { + my $nref = $_[0]; + my @npath = @{$_[1]}; + my $path_str = join ' ', @npath; + my @list = (); + foreach (keys %{$nref}) { + my $key = "$path_str $_"; + push @list, [ $_, $node_order{$key} ]; + } + my @slist = sort { ${$a}[1] <=> ${$b}[1] } @list; + @slist = map { ${$_}[0] } @slist; + return @slist; +} + +my $active_cfg = undef; +my $new_cfg_ref = undef; + +my @delete_list = (); + +# find specified node's values in active config that have been deleted from +# new config. +# $0: hash ref at the current hierarchy level (new config) +# $1: array ref representing current config path (active config) +sub findDeletedValues { + my $new_ref = $_[0]; + my @active_path = @{$_[1]}; + my ($is_multi, $is_text) = $active_cfg->parseTmpl(\@active_path); + $active_cfg->setLevel(join ' ', @active_path); + if ($is_multi) { + # for "multi:" nodes, need to sort the values by the original order. + my @nvals = getSortedMultiValues($new_ref, \@active_path); + if ($is_text) { + @nvals = map { /^"(.*)"$/; $1; } @nvals; + } + my @ovals = $active_cfg->returnOrigValues(''); + my %comp_hash = $active_cfg->compareValueLists(\@ovals, \@nvals); + foreach (@{$comp_hash{'deleted'}}) { + my @plist = applySingleQuote(@active_path, $_); + push @delete_list, [\@plist, get_config_rank(@active_path, $_)]; + } + } else { + # do nothing. if a single-value leaf node is deleted, it should have + # been detected at the previous level. since we are already at node.val, + # it can only be "added" or "changed", handled later. + } +} + +# find nodes in active config that have been deleted from new config. +# $0: hash ref at the current hierarchy level (new config) +# $1: array ref representing current config path (active config) +sub findDeletedNodes { + my $new_ref = $_[0]; + my @active_path = @{$_[1]}; + $active_cfg->setLevel(join ' ', @active_path); + my @active_nodes = $active_cfg->listOrigNodes(); + foreach (@active_nodes) { + if ($_ eq 'node.val') { + findDeletedValues($new_ref, \@active_path); + next; + } + if (!defined($new_ref->{$_})) { + my @plist = applySingleQuote(@active_path, $_); + push @delete_list, [\@plist, get_config_rank(@active_path, $_)]; + } else { + findDeletedNodes($new_ref->{$_}, [ @active_path, $_ ]); + } + } +} + +my @set_list = (); + +# find specified node's values in active config that are set +# (added or changed). +# $0: hash ref at the current hierarchy level (new config) +# $1: array ref representing current config path (active config) +sub findSetValues { + my $new_ref = $_[0]; + my @active_path = @{$_[1]}; + my ($is_multi, $is_text) = $active_cfg->parseTmpl(\@active_path); + $active_cfg->setLevel(join ' ', @active_path); + if ($is_multi) { + # for "multi:" nodes, need to sort the values by the original order. + my @nvals = getSortedMultiValues($new_ref, \@active_path); + if ($is_text) { + @nvals = map { /^"(.*)"$/; $1; } @nvals; + } + my @ovals = $active_cfg->returnOrigValues(''); + my %comp_hash = $active_cfg->compareValueLists(\@ovals, \@nvals); + foreach (@{$comp_hash{'added'}}) { + my @plist = applySingleQuote(@active_path, $_); + push @set_list, [\@plist, get_config_rank(@active_path, $_)]; + } + } else { + my @nvals = keys %{$new_ref}; + my $nval = $nvals[0]; + if ($is_text) { + $nval =~ s/^"(.*)"$/$1/; + } + my $oval = $active_cfg->returnOrigValue(''); + if (!defined($oval) || ($nval ne $oval)) { + my @plist = applySingleQuote(@active_path, $nval); + push @set_list, [\@plist, get_config_rank(@active_path, $nval)]; + } + } +} + +# find nodes in new config that are set (added or changed). +# $0: hash ref at the current hierarchy level (new config) +# $1: array ref representing current config path (active config) +sub findSetNodes { + my $new_ref = $_[0]; + my @active_path = @{$_[1]}; + $active_cfg->setLevel(join ' ', @active_path); + my @active_nodes = $active_cfg->listOrigNodes(); + my %active_hash = map { $_ => 1 } @active_nodes; + if (defined($active_hash{'node.val'})) { + # we are at a leaf node. + findSetValues($new_ref, \@active_path); + return; + } + foreach (sort keys %{$new_ref}) { + if (scalar(keys %{$new_ref->{$_}}) == 0) { + # we are at a non-value leaf node. + # check if we need to add this node. + if (!defined($active_hash{$_})) { + my @plist = applySingleQuote(@active_path, $_); + push @set_list, [\@plist, get_config_rank(@active_path, $_)]; + } else { + # node already present. do nothing. + } + next; + } + # we recur regardless of whether it's in active. all changes will be + # handled when we reach leaf nodes (above). + findSetNodes($new_ref->{$_}, [ @active_path, $_ ]); + } +} + +# compare the current active config with the specified hierarchy and return +# the "diff". +# $0: hash ref of config hierarchy. +# return: hash containing the diff. +sub getConfigDiff { + $active_cfg = new VyattaConfig; + $new_cfg_ref = shift; + @set_list = (); + @delete_list = (); + findDeletedNodes($new_cfg_ref, [ ]); + findSetNodes($new_cfg_ref, [ ]); + # don't really need to sort the lists by rank since we have to commit + # everything together anyway. + @delete_list = sort { ${$a}[1] <=> ${$b}[1] } @delete_list; + @set_list = sort { ${$b}[1] <=> ${$a}[1] } @set_list; + my %diff = ( + 'delete' => \@delete_list, + 'set' => \@set_list, + ); + return %diff; +} + +1; diff --git a/scripts/VyattaConfigOutput.pm b/scripts/VyattaConfigOutput.pm new file mode 100755 index 0000000..874ed55 --- /dev/null +++ b/scripts/VyattaConfigOutput.pm @@ -0,0 +1,253 @@ +# Perl module for generating output of the configuration. +# +# outputNewConfig() +# prints the "new" config, i.e., the active config with any un-committed +# changes. 'diff' notation is also generated to indicate the changes. +# +# outputActiveConfig() +# prints the "active" config. suitable for "saving", for example. + +package VyattaConfigOutput; + +use strict; +use lib '/opt/vyatta/share/perl5/'; +use VyattaConfig; + +my $config = undef; + +# $0: array ref for path +# $1: display prefix +# $2: node name +# $3: simple show (if defined, don't show diff prefix. used for "don't show as +# deleted" from displayDeletedOrigChildren.) +sub displayValues { + my @cur_path = @{$_[0]}; + my $prefix = $_[1]; + my $name = $_[2]; + my $simple_show = $_[3]; + my ($is_multi, $is_text) = $config->parseTmpl(\@cur_path); + $config->setLevel(join ' ', @cur_path); + if ($is_multi) { + my @ovals = $config->returnOrigValues(''); + my @nvals = $config->returnValues(''); + if ($is_text) { + @ovals = map { "\"$_\""; } @ovals; + @nvals = map { "\"$_\""; } @nvals; + } + my $idx = 0; + my %ohash = map { $_ => ($idx++) } @ovals; + $idx = 0; + my %nhash = map { $_ => ($idx++) } @nvals; + my @dlist = map { if (!defined($nhash{$_})) { $_; } else { undef; } } + @ovals; + if (defined($simple_show)) { + foreach my $oval (@ovals) { + print "$prefix$name $oval\n"; + } + return; + } + foreach my $del (@dlist) { + if (defined($del)) { + print "-$prefix$name $del\n"; + } + } + foreach my $nval (@nvals) { + my $diff = '+'; + if (defined($ohash{$nval})) { + if ($ohash{$nval} != $nhash{$nval}) { + $diff = '>'; + } else { + $diff = ' '; + } + } + print "$diff$prefix$name $nval\n"; + } + } else { + my $oval = $config->returnOrigValue(''); + my $nval = $config->returnValue(''); + if ($is_text) { + if (defined($oval)) { + $oval = "\"$oval\""; + } + if (defined($nval)) { + $nval = "\"$nval\""; + } + } + if (defined($simple_show)) { + print "$prefix$name: $oval\n"; + return; + } + my $value = $nval; + my $diff = ' '; + if (!defined($oval) && defined($nval)) { + $diff = '+'; + } elsif (!defined($nval) && defined($oval)) { + $diff = '-'; + $value = $oval; + } else { + # both must be defined + if ($oval ne $nval) { + $diff = '>'; + } + } + print "$diff$prefix$name: $value\n"; + } +} + +# $0: array ref for path +# $1: display prefix +# $2: don't show as deleted? (if defined, config is shown as normal instead of +# deleted.) +sub displayDeletedOrigChildren { + my @cur_path = @{$_[0]}; + my $prefix = $_[1]; + my $dont_show_as_deleted = $_[2]; + my $dprefix = '-'; + if (defined($dont_show_as_deleted)) { + $dprefix = ''; + } + $config->setLevel(''); + my @children = $config->listOrigNodes(join ' ', @cur_path); + for my $child (sort @children) { + if ($child eq 'node.val') { + # should not happen! + next; + } + my $is_tag = $config->isTagNode([ @cur_path, $child ]); + $config->setLevel(join ' ', (@cur_path, $child)); + my @cnames = sort $config->listOrigNodes(); + if ($#cnames == 0 && $cnames[0] eq 'node.val') { + displayValues([ @cur_path, $child ], $prefix, $child, + $dont_show_as_deleted); + } elsif (scalar($#cnames) >= 0) { + if ($is_tag) { + foreach my $cname (@cnames) { + if ($cname eq 'node.val') { + # should not happen + next; + } + print "$dprefix$prefix$child $cname {\n"; + displayDeletedOrigChildren([ @cur_path, $child, $cname ], + "$prefix ", $dont_show_as_deleted); + print "$dprefix$prefix}\n"; + } + } else { + print "$dprefix$prefix$child {\n"; + displayDeletedOrigChildren([ @cur_path, $child ], "$prefix ", + $dont_show_as_deleted); + print "$dprefix$prefix}\n"; + } + } else { + my $has_tmpl_children = $config->hasTmplChildren([ @cur_path, $child ]); + print "$dprefix$prefix$child" + . ($has_tmpl_children ? " {\n$dprefix$prefix}\n" : "\n"); + } + } +} + +# $0: hash ref for children status +# $1: array ref for path +# $2: display prefix +sub displayChildren { + my %child_hash = %{$_[0]}; + my @cur_path = @{$_[1]}; + my $prefix = $_[2]; + for my $child (sort (keys %child_hash)) { + if ($child eq 'node.val') { + # should not happen! + next; + } + my ($diff, $vdiff) = (' ', ' '); + if ($child_hash{$child} eq 'added') { + $diff = '+'; + $vdiff = '+'; + } elsif ($child_hash{$child} eq 'deleted') { + $diff = '-'; + $vdiff = '-'; + } elsif ($child_hash{$child} eq 'changed') { + $vdiff = '>'; + } + my $is_tag = $config->isTagNode([ @cur_path, $child ]); + $config->setLevel(join ' ', (@cur_path, $child)); + my %cnodes = $config->listNodeStatus(); + my @cnames = sort keys %cnodes; + if ($#cnames == 0 && $cnames[0] eq 'node.val') { + displayValues([ @cur_path, $child ], $prefix, $child); + } elsif (scalar($#cnames) >= 0) { + if ($is_tag) { + foreach my $cname (@cnames) { + if ($cname eq 'node.val') { + # should not happen + next; + } + my $tdiff = ' '; + if ($cnodes{$cname} eq 'deleted') { + $tdiff = '-'; + } elsif ($cnodes{$cname} eq 'added') { + $tdiff = '+'; + } + print "$tdiff$prefix$child $cname {\n"; + if ($cnodes{$cname} eq 'deleted') { + displayDeletedOrigChildren([ @cur_path, $child, $cname ], + "$prefix "); + } else { + $config->setLevel(join ' ', (@cur_path, $child, $cname)); + my %ccnodes = $config->listNodeStatus(); + displayChildren(\%ccnodes, [ @cur_path, $child, $cname ], + "$prefix "); + } + print "$tdiff$prefix}\n"; + } + } else { + print "$diff$prefix$child {\n"; + if ($child_hash{$child} eq 'deleted') { + # this should not happen + displayDeletedOrigChildren([ @cur_path, $child ], "$prefix "); + } else { + displayChildren(\%cnodes, [ @cur_path, $child ], "$prefix "); + } + print "$diff$prefix}\n"; + } + } else { + if ($child_hash{$child} eq 'deleted') { + $config->setLevel(''); + my @onodes = $config->listOrigNodes(join ' ', (@cur_path, $child)); + if ($#onodes == 0 && $onodes[0] eq 'node.val') { + displayValues([ @cur_path, $child ], $prefix, $child); + } else { + print "$diff$prefix$child {\n"; + displayDeletedOrigChildren([ @cur_path, $child ], "$prefix "); + print "$diff$prefix}\n"; + } + } else { + my $has_tmpl_children + = $config->hasTmplChildren([ @cur_path, $child ]); + print "$diff$prefix$child" + . ($has_tmpl_children ? " {\n$diff$prefix}\n" : "\n"); + } + } + } +} + +# @ARGV: represents the 'root' path. the output starts at this point under +# the new config. +sub outputNewConfig { + $config = new VyattaConfig; + $config->setLevel(join ' ', @_); + my %rnodes = $config->listNodeStatus(); + if (scalar(keys %rnodes) > 0) { + displayChildren(\%rnodes, [ @_ ], ''); + } else { + print "Current configuration is empty\n"; + } +} + +# @ARGV: represents the 'root' path. the output starts at this point under +# the active config. +sub outputActiveConfig { + $config = new VyattaConfig; + $config->setLevel(join ' ', @_); + displayDeletedOrigChildren([ @_ ], '', 1); +} + +1; diff --git a/scripts/VyattaMisc.pm b/scripts/VyattaMisc.pm new file mode 100755 index 0000000..61c646b --- /dev/null +++ b/scripts/VyattaMisc.pm @@ -0,0 +1,62 @@ +package VyattaMisc; +require Exporter; +@ISA = qw(Exporter); +@EXPORT = qw(getNetAddIP, isIpAddress); +@EXPORT_OK = qw(getNetAddIP, isIpAddress); + +use strict; + +sub getNetAddrIP { + my ($interface); + ($interface) = @_; + + if ($interface eq '') { + print STDERR "Error: No interface specified.\n"; + return undef; + } + + + my $ifconfig_out = `ifconfig $interface`; + $ifconfig_out =~ /inet addr:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/; + my $ip = $1; + if ($ip eq '') { + print STDERR "Error: Unable to determine IP address for interface \'$interface\'.\n"; + return undef; + } + + + $ifconfig_out =~ /Mask:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/; + my $netmask = $1; + if ($netmask eq '') { + print STDERR "Error: Unable to determine netmask for interface \'$interface\'.\n"; + return undef; + } + + use NetAddr::IP; # This library is available via libnetaddr-ip-perl.deb + my $naip = new NetAddr::IP($ip, $netmask); + return $naip; +} + +sub isIpAddress { + my $ip = shift; + + $_ = $ip; + if ( ! /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { + return 0; + } + else { + my @ips = split /\./, $ip; + my $octet = 0; + my $counter = 0; + + foreach $octet (@ips) { + if (($octet < 0) || ($octet > 255)) { return 0; } + if (($counter == 0) && ($octet < 1)) { return 0; } + $counter++; + } + } + + return 1; +} + +return 1; diff --git a/scripts/VyattaTypeChecker.pm b/scripts/VyattaTypeChecker.pm new file mode 100644 index 0000000..451be52 --- /dev/null +++ b/scripts/VyattaTypeChecker.pm @@ -0,0 +1,179 @@ +# Perl module for type validation. +# Usage 1: validate a value of a specific type. +# use VyattaTypeChecker; +# ... +# if (VyattaTypeChecker::validateType('ipv4', '1.1.1.1')) { +# # valid +# ... +# } else { +# # not valie +# ... +# } +# +# Usage 2: find the type of a value (from a list of candidates), returns +# undef if the value is not valid for any of the candidates. +# $valtype = VyattaTypeChecker::findType('1.1.1.1', 'ipv4', 'ipv6'); +# if (!defined($valtype)) { +# # neither ipv4 nor ipv6 +# ... +# } else { +# if ($valtype eq 'ipv4') { +# ... +# } else { +# ... +# } +# } + +package VyattaTypeChecker; + +use strict; + +my %type_handler = ( + 'ipv4' => \&validate_ipv4, + 'ipv4net' => \&validate_ipv4net, + 'ipv4_negate' => \&validate_ipv4_negate, + 'ipv4net_negate' => \&validate_ipv4net_negate, + 'protocol' => \&validate_protocol, + 'protocol_negate' => \&validate_protocol_negate, + 'macaddr' => \&validate_macaddr, + 'macaddr_negate' => \&validate_macaddr_negate, + 'ipv6' => \&validate_ipv6, + ); + +sub validate_ipv4 { + $_ = shift; + return 0 if (!/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/); + return 0 if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255); + return 1; +} + +sub validate_ipv4net { + $_ = shift; + return 0 if (!/^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/); + return 0 if ($1 > 255 || $2 > 255 || $3 > 255 || $4 > 255 || $5 > 32); + return 1; +} + +sub validate_ipv4_negate { + my $value = shift; + if ($value =~ m/^\!(.*)$/) { + $value = $1; + } + return validate_ipv4($value); +} + +sub validate_ipv4net_negate { + my $value = shift; + if ($value =~ m/^\!(.*)$/) { + $value = $1; + } + return validate_ipv4net($value); +} + +sub validate_protocol { + my $value = shift; + $value = lc $value; + return 1 if ($value eq 'all'); + if (!open(IN, "</etc/protocols")) { + print "can't open /etc/protocols"; + return 0; + } + my $ret = 0; + while (<IN>) { + s/^([^#]*)#.*$/$1/; + if ((/^$value\s/) || (/^\S+\s+$value\s/)) { + $ret = 1; + last; + } + } + close IN; + return $ret; +} + +sub validate_protocol_negate { + my $value = shift; + if ($value =~ m/^\!(.*)$/) { + $value = $1; + } + return validate_protocol($value); +} + +sub validate_macaddr { + my $value = shift; + $value = lc $value; + my $byte = '[0-9a-f]{2}'; + return 1 if ($value =~ /^$byte(:$byte){5}$/); +} + +sub validate_macaddr_negate { + my $value = shift; + if ($value =~ m/^\!(.*)$/) { + $value = $1; + } + return validate_macaddr($value); +} + +# IPv6 syntax definition +my $RE_IPV4_BYTE = '((25[0-5])|(2[0-4][0-9])|([01][0-9][0-9])|([0-9]{1,2}))'; +my $RE_IPV4 = "$RE_IPV4_BYTE(\.$RE_IPV4_BYTE){3}"; +my $RE_H16 = '([a-fA-F0-9]{1,4})'; +my $RE_H16_COLON = "($RE_H16:)"; +my $RE_LS32 = "(($RE_H16:$RE_H16)|($RE_IPV4))"; +my $RE_IPV6_P1 = "($RE_H16_COLON)\{6\}$RE_LS32"; +my $RE_IPV6_P2 = "::($RE_H16_COLON)\{5\}$RE_LS32"; +my $RE_IPV6_P3 = "($RE_H16)?::($RE_H16_COLON)\{4\}$RE_LS32"; +my $RE_IPV6_P4 = "(($RE_H16_COLON)\{0,1\}$RE_H16)?" + . "::($RE_H16_COLON)\{3\}$RE_LS32"; +my $RE_IPV6_P5 = "(($RE_H16_COLON)\{0,2\}$RE_H16)?" + . "::($RE_H16_COLON)\{2\}$RE_LS32"; +my $RE_IPV6_P6 = "(($RE_H16_COLON)\{0,3\}$RE_H16)?" + . "::($RE_H16_COLON)\{1\}$RE_LS32"; +my $RE_IPV6_P7 = "(($RE_H16_COLON)\{0,4\}$RE_H16)?::$RE_LS32"; +my $RE_IPV6_P8 = "(($RE_H16_COLON)\{0,5\}$RE_H16)?::$RE_H16"; +my $RE_IPV6_P9 = "(($RE_H16_COLON)\{0,6\}$RE_H16)?::"; +my $RE_IPV6 = "($RE_IPV6_P1)|($RE_IPV6_P2)|($RE_IPV6_P3)|($RE_IPV6_P4)" + . "|($RE_IPV6_P5)|($RE_IPV6_P6)|($RE_IPV6_P7)|($RE_IPV6_P8)" + . "|($RE_IPV6_P9)"; + +sub validate_ipv6 { + $_ = shift; + return 0 if (!/^$RE_IPV6$/); + return 1; +} + +sub validateType { + my ($type, $value) = @_; + if (!defined($type) || !defined($value)) { + return 0; + } + if (!defined($type_handler{$type})) { + print "type \"$type\" not defined\n"; + return 0; + } + if (!&{$type_handler{$type}}($value)) { + print "\"$value\" is not a valid value of type \"$type\"\n"; + return 0; + } + + return 1; +} + +sub findType { + my ($value, @candidates) = @_; + if (!defined($value) || ((scalar @candidates) < 1)) { + return undef; + } + foreach my $type (@candidates) { + if (!defined($type_handler{$type})) { + next; + } + if (&{$type_handler{$type}}($value)) { + # the first valid type is returned + return $type; + } + } + return undef; +} + +1; + diff --git a/scripts/XorpConfigParser.pm b/scripts/XorpConfigParser.pm new file mode 100755 index 0000000..e85410f --- /dev/null +++ b/scripts/XorpConfigParser.pm @@ -0,0 +1,368 @@ +package XorpConfigParser; + +use lib "/opt/vyatta/share/perl5/"; +use strict; + +my %data; + +my %fields = ( + _data => \%data +); + +sub new { + my $that = shift; + my $class = ref ($that) || $that; + my $self = { + %fields, + }; + + bless $self, $class; + return $self; +} + + +sub copy_node { + my ($self, $from, $to, $name) = @_; + if (!defined($from) || !defined($to) || !defined($name)) { + return; + } + + foreach my $node (@$from) { + my $stringNodeNameHere = $node->{'name'}; + if ($stringNodeNameHere =~ /^$name.*/) { + foreach my $nodeCheck (@$to) { + my $stringCheck = $nodeCheck->{'name'}; + if ($name eq $stringCheck) { + $nodeCheck->{'value'} = $node->{'value'}; + $nodeCheck->{'children'} = $node->{'children'}; + $nodeCheck->{'comment'} = $node->{'comment'}; + return; + } + } + push(@$to, $node); + } + } +} +sub copy_multis { + my ($self, $nodes, $name) = @_; + if (!defined($nodes) || !defined($name)) { + return undef; + } + + my @multis; + + foreach my $node (@$nodes) { + my $stringNodeNameHere = $node->{'name'}; + if ($stringNodeNameHere =~ /$name\s(\S+)/) { + my $stringNameHere = $1; + my %multi = ( + 'name' => $stringNameHere, + 'comment' => $node->{'comment'}, + 'value' => $node->{'value'}, + 'children' => $node->{'children'} + ); + push(@multis, \%multi); + } + } + + return @multis; +} +sub comment_out_child { + my ($self, $children, $name, $comment) = @_; + if (!defined($children) || !defined($name)) { + return; + } + + for (my $i = 0; $i < @$children; $i++) { + my $stringNodeNameHere = @$children[$i]->{'name'}; + if ($name eq $stringNodeNameHere) { + @$children[$i]->{'comment_out'} = "1"; + if (defined($comment)) { + @$children[$i]->{'comment_out'} = $comment; + } + } + } +} +sub create_node { + my ($self, $path) = @_; + + my $hash = \%data; + foreach my $segment (@$path) { + my $children = $hash->{'children'}; + if (!defined($children)) { + my @new_children; + $hash->{'children'} = \@new_children; + $children = \@new_children; + } + my $child_found = 0; + foreach my $child (@$children) { + if ($child->{'name'} eq $segment) { + $child_found = 1; + $hash = $child; + last; + } + } + if ($child_found == 0) { + my %new_hash = ( + 'name' => $segment + ); + push(@$children, \%new_hash); + $hash = \%new_hash; + } + } + return $hash; +} +sub delete_child { + my ($self, $children, $name) = @_; + if (!defined($children) || !defined($name)) { + return; + } + + for (my $i = 0; $i < @$children; $i++) { + my $stringNodeNameHere = @$children[$i]->{'name'}; + if ($name eq $stringNodeNameHere) { + @$children[$i] = undef; + } + } +} +sub find_child { + my ($self, $children, $name) = @_; + if (!defined($children) || !defined($name)) { + return undef; + } + + foreach my $child (@$children) { + my $stringNodeNameHere = $child->{'name'}; + if ($name eq $stringNodeNameHere) { + return $child; + } + } + return undef; +} +sub get_node { + my ($self, $path) = @_; + + my $hash = $self->{_data}; + foreach my $segment (@$path) { + my $children = $hash->{'children'}; + if (!defined($children)) { + return undef; + } + + my $child_found = 0; + foreach my $child (@$children) { + if ($child->{'name'} eq $segment) { + $child_found = 1; + $hash = $child; + last; + } + } + + if ($child_found == 0) { + return undef; + } + } + return $hash; +} + +sub push_comment { + my ($self, $path, $comment) = @_; + + my $hash = \%data; + foreach my $segment (@$path) { + my $children = $hash->{'children'}; + if (!defined($children)) { + my @children; + $hash->{'children'} = \@children; + $children = \@children; + } + + my $child_found = 0; + foreach my $child (@$children) { + if ($child->{'name'} eq $segment) { + $child_found = 1; + $hash = $child; + last; + } + } + + if ($child_found == 0) { + my %new_hash = ( + 'name' => $segment + ); + push(@$children, \%new_hash); + $hash = \%new_hash; + } + } + + my %new_comment = ( + 'comment' => $comment + ); + my $childrenPush = $hash->{'children'}; + if (!defined($childrenPush)) { + my @new_children; + $hash->{'children'} = \@new_children; + $childrenPush = \@new_children; + } + push(@$childrenPush, \%new_comment); +} +sub set_value { + my ($self, $path, $value) = @_; + + my $hash = $self->create_node($path); + if (defined($hash)) { + $hash->{'value'} = $value; + } +} +sub output { + my ($self, $depth, $hash) = @_; + + if (!defined($hash)) { + $hash = $self->{_data}; + } + + if ($hash->{'comment'} ne '') { + print '/*' . $hash->{'comment'} . "*/\n"; + } + my $children = $hash->{'children'}; + foreach my $child (@$children) { + if (defined($child)) { + if (defined($child->{'comment_out'})) { + print "\n"; + if ($child->{'comment_out'} ne "1") { + print "/* --- $child->{'comment_out'} --- */\n"; + } + print "/* --- CONFIGURATION COMMENTED OUT DURING MIGRATION BELOW ---\n"; + } + + print " " x $depth; + if ($child->{'value'} ne '') { + print "$child->{'name'}: $child->{'value'}"; + print "\n"; + } else { + my $print_brackets = 0; + my $children = $child->{'children'}; + if (defined($children) && @$children > 0) { + $print_brackets = 1; + } elsif ($child->{'name'} ne '' && !($child->{'name'} =~ /\s/)) { + $print_brackets = 1; + } + + if ($child->{'name'} ne '') { + print "$child->{'name'}"; + if ($print_brackets) { + print " {"; + } + print "\n"; + } + + $self->output($depth+1, $child); + if ($print_brackets) { + print " " x $depth; + print "}\n"; + } + } + + if (defined($child->{'comment_out'})) { + print " --- CONFIGURATION COMMENTED OUT DURING MIGRATION ABOVE --- */\n\n"; + } + + } + } +} +sub parse { + my ($self, $file) = @_; + open(INPUT, "< $file") or die "Error! Unable to open file \"$file\". $!"; + + my $contents = ""; + while (<INPUT>) {$contents .= $_} + close INPUT; + + my @array_contents = split('', $contents); +# print scalar(@array_contents) . "\n"; + + my $length_contents = @array_contents; + my $colon = 0; + my $colon_quote = 0; + my $name = ''; + my $value = undef; + my @path; + my %tree; + for (my $i = 0; $i < $length_contents;) { + my $c = $array_contents[$i]; + my $cNext = $array_contents[$i+1]; + + if ($c eq '/' && $cNext eq '*') { + my $comment_text = ''; + my $comment_end = index($contents, '*/', $i+2); + if ($comment_end == -1) { + $comment_text = substr($contents, $i+2); + } else { + $comment_text = substr($contents, $i+2, $comment_end - $i - 2); + $i = $comment_end + 2; + } +# print 'Comment is: "' . $comment_text . "\"\n"; + $self->push_comment(\@path, $comment_text); + } elsif ($colon == 0 && ($c eq '{' || $c eq ':' || $c eq "\n")) { + $name =~ s/^\s+|\s$//g; + if (length($name) > 0) { + push(@path, $name); +# print "Path is: \"@path\" Name is: \"$name\"\n"; + $self->set_value(\@path, $value); + $name = ''; + + if ($c eq "\n") { + pop(@path); + } + if ($c eq ':') { + $colon = 1; + } + } + $i++; + } elsif ($c eq '}') { + pop(@path); + $name = ''; + $i++; + } elsif ($c eq ';') { + $i++; + } elsif ($colon == 1) { + my $value_end = 0; + if ($c eq '"') { + $value .= $c; + if ($colon_quote == 1) { + $value_end = 1; + } else { + $colon_quote = 1; + } + } elsif ($c eq '\\' && $cNext eq '"') { + $value .= '\\"'; + $i++; + } else { + if ((length($value) > 0) || (!($c =~ /\s/))) { + $value .= $c; + } + } + + if ($colon_quote == 0 && ($cNext eq '}' || $cNext eq ';' || $cNext =~ /\s/)) { + $value_end = 1; + } + $i++; + + if ($value_end == 1) { + if (length($value) > 0) { +# print "Path is: \"@path\" Value is: $value\n"; + $self->set_value(\@path, $value); + $value = undef; + } + pop(@path); + $colon_quote = 0; + $colon = 0; + } + } else { + $name .= $c; + $i++; + } + } +} + + diff --git a/scripts/system/vyatta_update_login_user.pl b/scripts/system/vyatta_update_login_user.pl new file mode 100644 index 0000000..86c0074 --- /dev/null +++ b/scripts/system/vyatta_update_login_user.pl @@ -0,0 +1,172 @@ +#!/usr/bin/perl + +use strict; +use Fcntl; +use POSIX qw(:unistd_h); + +# arg: login_name +# returns the next available uid if login_name doesn't exist. +# otherwise returns (undef, <passwd fields for login_name>). +sub next_uid_if_not_exist { + my $login = shift; + my $min_uid = 1000; + my $max_uid = 60000; + if (open(LOGIN_DEF, "/etc/login.defs")) { + while (<LOGIN_DEF>) { + if (m/^\s*UID_MIN\s+(\d+)/) { + $min_uid = $1; + next; + } + if (m/^\s*UID_MAX\s+(\d+)/) { + $max_uid = $1; + next; + } + } + close LOGIN_DEF; + } + + open(PASSWD, "/etc/passwd") or exit 1; + while (<PASSWD>) { + chomp; + my @passwd_fields = split /:/; + if ($passwd_fields[0] eq $login) { + close PASSWD; + return (undef, @passwd_fields); + } + if ($min_uid <= $passwd_fields[2]) { + next if ($passwd_fields[2] > $max_uid); + $min_uid = $passwd_fields[2] + 1; + next; + } + } + close PASSWD; + exit 2 if ($min_uid > $max_uid); + return ($min_uid); +} + +# arg: login_name +# returns the corresponding line in shadow or undef if login_name doesn't +# exist. +sub get_shadow_line { + my $login = shift; + open(SHADOW, "/etc/shadow") or exit 3; + while (<SHADOW>) { + chomp; + if (m/^$login:/) { + close SHADOW; + return $_; + } + } + close SHADOW; + return undef; +} + +my $user = shift; +my $full = shift; +my $encrypted = shift; + +# emulate lckpwdf(3). +# difference: we only try to lock it once (non-blocking). lckpwdf will block +# for up to 15 seconds waiting for the lock. +# note that the lock is released when file is closed (e.g., exit), so no need +# for explicit unlock. +my $flock = pack "ssa20", F_WRLCK, SEEK_SET, "\0"; +sysopen(PWDLCK, "/etc/.pwd.lock", O_WRONLY | O_CREAT, 0600) or exit 3; +fcntl(PWDLCK, F_SETLK, $flock) or exit 3; + +if ($user eq "-d") { + $user = $full; + exit 4 if (!defined($user)); + + # check if user is using the system + my @pslines = `ps -U $user -u $user u`; + if ($#pslines != 0) { + # user is using the system + print STDERR "Delete failed: user \"$user\" is using the system\n"; + exit 4; + } + + my $ret = system("sed -i '/^$user:/d' /etc/passwd"); + exit 5 if ($ret >> 8); + $ret = system("sed -i '/^$user:/d' /etc/shadow"); + exit 6 if ($ret >> 8); + $ret = system("rm -rf /home/$user"); + exit 7 if ($ret >> 8); + exit 0; +} + +exit 4 if (!defined($user) || !defined($full) || !defined($encrypted)); + +my $DEF_GROUP = "quagga"; +my $DEF_SHELL = "/bin/bash"; + +open(GRP, "/etc/group") or exit 5; +my $def_gid = undef; +while (<GRP>) { + my @group_fields = split /:/; + if ($group_fields[0] eq $DEF_GROUP) { + $def_gid = $group_fields[2]; + last; + } +} +exit 6 if (!defined($def_gid)); + +my @vals = next_uid_if_not_exist($user); +my ($new_user, $passwd_line, $shadow_line) = (0, "", ""); +if (defined($vals[0])) { + # add new user + $new_user = 1; + $passwd_line = "$user:x:$vals[0]:${def_gid}:$full:/home/$user:$DEF_SHELL"; + my $sline = get_shadow_line($user); + exit 7 if (defined($sline)); + my $seconds = `date +%s`; + my $days = int($seconds / 3600 / 24); + $shadow_line = "$user:$encrypted:$days:0:99999:7:::"; +} else { + # modify existing user + shift @vals; + $vals[4] = $full; + $passwd_line = join(':', @vals); + my $sline = get_shadow_line($user); + exit 8 if (!defined($sline)); + @vals = split /:/, $sline; + $vals[1] = $encrypted; + for (my $padding = (9 - $#vals - 1); $padding > 0; $padding--) { + push @vals, ''; + } + $shadow_line = join(':', @vals); +} + +my $ret = 0; +if (!$new_user) { + $ret = system("sed -i '/^$user:/d' /etc/passwd"); + exit 9 if ($ret >> 8); + $ret = system("sed -i '/^$user:/d' /etc/shadow"); + exit 10 if ($ret >> 8); +} + +open(PASSWD, ">>/etc/passwd") or exit 11; +print PASSWD "$passwd_line\n"; +close PASSWD; +open(SHADOW, ">>/etc/shadow") or exit 12; +print SHADOW "$shadow_line\n"; +close SHADOW; + +if (($new_user) && !(-e "/home/$user")) { + if (-d "/etc/skel") { + $ret = system("cp -a /etc/skel /home/$user"); + exit 13 if ($ret >> 8); + $ret = system("chmod 755 /home/$user"); + exit 14 if ($ret >> 8); + $ret = system("chown -R $user:$DEF_GROUP /home/$user"); + exit 15 if ($ret >> 8); + } else { + $ret = system("mkdir -p /home/$user"); + exit 16 if ($ret >> 8); + $ret = system("chmod 755 /home/$user"); + exit 17 if ($ret >> 8); + } +} + +exit 0; + diff --git a/scripts/system/vyatta_update_logrotate.pl b/scripts/system/vyatta_update_logrotate.pl new file mode 100644 index 0000000..2740526 --- /dev/null +++ b/scripts/system/vyatta_update_logrotate.pl @@ -0,0 +1,55 @@ +#!/usr/bin/perl + +use strict; + +my $file = "messages"; +my $log_file = "/var/log/messages"; +if ($#ARGV == 3) { + $file = shift; + $log_file = "/var/log/user/$file"; +} +my $files = shift; +my $size = shift; +my $set = shift; +my $log_conf = "/etc/logrotate.d/$file"; + +if (!defined($files) || !defined($size) || !defined($set)) { + exit 1; +} + +if (!($files =~ m/^\d+$/) || !($size =~ m/^\d+$/)) { + exit 2; +} + +# just remove it and make a new one below +# (the detection mechanism in XORP doesn't work anyway) +unlink $log_conf; + +open(OUT, ">>$log_conf") or exit 3; +if ($set == 1) { + print OUT <<EOF; +$log_file { + missingok + notifempty + rotate $files + size=${size}k + postrotate + kill -HUP `cat /var/run/syslogd.pid` + endscript +} +EOF +} +close OUT; + +sleep 1; +# XXX somehow starting syslogd with 'start-stop-daemon --start...' here fails +# with SEGV (?). just start syslogd directly. +#if (system("/opt/vyatta/sbin/sysklogd.init restart")) { +system("/opt/vyatta/sbin/sysklogd.init stop"); +sleep 1; +if (system(". /etc/default/syslogd ; /sbin/syslogd \$SYSLOGD")) { + exit 4; +} + +exit 0; + diff --git a/scripts/system/vyatta_update_syslog.pl b/scripts/system/vyatta_update_syslog.pl new file mode 100644 index 0000000..315e2a9 --- /dev/null +++ b/scripts/system/vyatta_update_syslog.pl @@ -0,0 +1,56 @@ +#!/usr/bin/perl + +use strict; +my $SYSLOG_CONF = '/etc/syslog.conf'; + +my $match1 = shift; +my $match2 = shift; +my $update_line = shift; + +if (!defined($match1) || !defined($match2) || !defined($update_line)) { + exit 1; +} + +if (system("touch $SYSLOG_CONF")) { + exit 2; +} + +my $exp1 = ""; +my $exp2 = ""; +if ($match1 ne "") { + $exp1 = $match1; + if ($match2 ne "") { + $exp2 = $match2; + } +} elsif ($match2 ne "") { + $exp1 = $match2; +} + +if ($exp2 ne "") { + if (system("sed -i '/$exp1/{/$exp2/d}' $SYSLOG_CONF")) { + exit 2; + } +} elsif ($exp1 ne "") { + if (system("sed -i '/$exp1/d' $SYSLOG_CONF")) { + exit 3; + } +} + +open(OUT, ">>$SYSLOG_CONF") or exit 4; +if ($update_line ne "") { + print OUT "$update_line"; +} +close OUT; + +sleep 1; +# XXX somehow starting syslogd with 'start-stop-daemon --start...' here fails +# with SEGV (?). just start syslogd directly. +#if (system("/opt/vyatta/sbin/sysklogd.init restart")) { +system("/opt/vyatta/sbin/sysklogd.init stop"); +sleep 1; +if (system(". /etc/default/syslogd ; /sbin/syslogd \$SYSLOGD")) { + exit 5; +} + +exit 0; + diff --git a/scripts/vyatta-cli-expand-var.pl b/scripts/vyatta-cli-expand-var.pl new file mode 100755 index 0000000..fcc2b43 --- /dev/null +++ b/scripts/vyatta-cli-expand-var.pl @@ -0,0 +1,64 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaConfig; + +# expand a variable reference +if ($#ARGV != 0) { + print STDERR "usage: vyatta-cli-expand-var.pl '<var-ref>'\n"; + exit 1; +} + +$_ = $ARGV[0]; + +# basic format check: +# '(' ')' not allowed in reference. +# only allow absolute path for now. +if (!/^\$\(\/([^()]+)\)$/) { + print STDERR "invalid variable reference (invalid format)\n"; + exit 1; +} +$_ = $1; + +my $multi_val = 1; +if (s/^(.*)\/\@\@$/$1/) { + # return list of multi-node values + $multi_val = 1; +} elsif (s/^(.*)\/\@$/$1/) { + # return single value + $multi_val = 0; +} else { + # only allow the above 2 forms for now. + print STDERR "invalid variable reference (invalid value specification)\n"; + exit 1; +} + +if (/\@/) { + # '@' not allowed anywhere else in the reference for now. + print STDERR "invalid variable reference (extra value specification)\n"; + exit 1; +} + +my $config = new VyattaConfig; +my $path_str = join ' ', (split /\//); +my $val_str = ""; +if ($multi_val) { + my @tmp = $config->returnOrigValues($path_str); + if (scalar(@tmp) > 0) { + # we got multiple values back + $val_str = join ' ', @tmp; + } else { + # this node may be a 'tag' node. try listing children. + $config->setLevel($path_str); + @tmp = $config->listOrigNodes(); + $val_str = join ' ', @tmp; + } +} else { + $val_str = $config->returnOrigValue($path_str); +} + +# expanded string is printed on stdout (multiple values separated by ' '). +print "$val_str"; +exit 0; + diff --git a/scripts/vyatta-config-loader.pl b/scripts/vyatta-config-loader.pl new file mode 100755 index 0000000..a3dfc44 --- /dev/null +++ b/scripts/vyatta-config-loader.pl @@ -0,0 +1,51 @@ +#!/usr/bin/perl +# Perl script for loading the startup config file. +# $0: startup config file. + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaConfigLoad; + +# get a list of all config statement in the startup config file +# (sorted by rank). +my @all_nodes = VyattaConfigLoad::getStartupConfigStatements($ARGV[0]); +if (scalar(@all_nodes) == 0) { + # no config statements + exit 1; +} +my $cur_rank = ${$all_nodes[0]}[1]; +my $commit_cmd = '/opt/vyatta/sbin/xorp_tmpl_tool commit'; +my $cleanup_cmd = '/opt/vyatta/sbin/xorp_tmpl_tool cleanup'; +my $ret = 0; +# higher-ranked statements committed before lower-ranked. +foreach (@all_nodes) { + my ($path_ref, $rank) = @$_; + if ($rank != $cur_rank) { + # commit all nodes with the same rank together. + $ret = system("$commit_cmd"); + if ($ret >> 8) { + print STDERR "Commit failed at rank $cur_rank\n"; + system("$cleanup_cmd"); + # continue after cleanup (or should we abort?) + } + $cur_rank = $rank; + } + my $cmd = '/opt/vyatta/sbin/xorp_tmpl_tool set ' . (join ' ', @$path_ref); + $ret = system("$cmd"); + if ($ret >> 8) { + $cmd =~ s/^.*?set /set /; + print STDERR "[[$cmd]] failed\n"; + # continue after set failure (or should we abort?) + } +} +$ret = system("$commit_cmd"); +if ($ret >> 8) { + print STDERR "Commit failed at rank $cur_rank\n"; + system("$cleanup_cmd"); + # exit normally after cleanup (or should we exit with error?) +} + +# really clean up +system('/opt/vyatta/sbin/xorp_tmpl_tool end_loading'); + +exit 0; diff --git a/scripts/vyatta-find-type.pl b/scripts/vyatta-find-type.pl new file mode 100755 index 0000000..b6514f0 --- /dev/null +++ b/scripts/vyatta-find-type.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaTypeChecker; + +# find the type of a value (from a list of candidates) +if ($#ARGV < 1) { + print "usage: vyatta-find-type.pl <value> <type> [<type> ...]\n"; + exit 1; +} + +if (my $type = VyattaTypeChecker::findType(@ARGV)) { + # type found + print "$type"; + exit 0; +} + +# value not valid for any of the candidates +exit 1; + diff --git a/scripts/vyatta-load-config.pl b/scripts/vyatta-load-config.pl new file mode 100755 index 0000000..7a1e01d --- /dev/null +++ b/scripts/vyatta-load-config.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# Perl script for loading config file at run time. +# $0: config file. + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaConfigLoad; + +my $etcdir = $ENV{ofr_sysconfdir}; +my $bootpath = ''; +if (-r "$etcdir/bootfile_path") { + $bootpath = `cat $etcdir/bootfile_path`; +} +$bootpath =~ s/\/[^\/]+$//; + +if ($#ARGV != 0) { + print "Usage: load <config_file_name>\n"; + exit 1; +} + +my $load_file = $ARGV[0]; +if (!($load_file =~ /^\//)) { + # relative path + $load_file = "$bootpath/$load_file"; +} + +print "Loading config file $load_file...\n"; +my %cfg_hier = VyattaConfigLoad::loadConfigHierarchy($load_file); +if (scalar(keys %cfg_hier) == 0) { + print "Load failed\n"; + exit 1; +} + +my %cfg_diff = VyattaConfigLoad::getConfigDiff(\%cfg_hier); + +my @delete_list = @{$cfg_diff{'delete'}}; +my @set_list = @{$cfg_diff{'set'}}; + +foreach (@delete_list) { + my ($cmd_ref, $rank) = @{$_}; + my @cmd = ( 'my_delete', @{$cmd_ref} ); + my $cmd_str = join ' ', @cmd; + system("$cmd_str"); + if ($? >> 8) { + $cmd_str =~ s/^my_//; + print "\"$cmd_str\" failed\n"; + } +} + +foreach (@set_list) { + my ($cmd_ref, $rank) = @{$_}; + my @cmd = ( 'my_set', @{$cmd_ref} ); + my $cmd_str = join ' ', @cmd; + system("$cmd_str"); + if ($? >> 8) { + $cmd_str =~ s/^my_//; + print "\"$cmd_str\" failed\n"; + } +} + +system("my_commit"); +if ($? >> 8) { + print "Load failed (commit failed)\n"; + exit 1; +} + +print "Done\n"; +exit 0; + diff --git a/scripts/vyatta-output-config.pl b/scripts/vyatta-output-config.pl new file mode 100755 index 0000000..7f3ea83 --- /dev/null +++ b/scripts/vyatta-output-config.pl @@ -0,0 +1,9 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaConfigOutput; + +VyattaConfigOutput::outputNewConfig(@ARGV); +exit 0; + diff --git a/scripts/vyatta-save-config.pl b/scripts/vyatta-save-config.pl new file mode 100755 index 0000000..ad972b4 --- /dev/null +++ b/scripts/vyatta-save-config.pl @@ -0,0 +1,45 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaConfigOutput; + +my $sbindir = $ENV{ofr_sbindir}; +my $etcdir = $ENV{ofr_sysconfdir}; +my $bootfile = ''; +if (-r "$etcdir/bootfile_path") { + $bootfile = `cat $etcdir/bootfile_path`; +} +my $bootpath = $bootfile; +$bootpath =~ s/\/[^\/]+$//; + +if ($#ARGV > 0) { + print "Usage: save [config_file_name]\n"; + exit 1; +} + +my $save_file = "$bootfile"; +if (defined($ARGV[0])) { + $save_file = $ARGV[0]; + if (!($save_file =~ /^\//)) { + # relative path + $save_file = "$bootpath/$save_file"; + } +} + +# this overwrites the file if it exists. we could create a backup first. +if (! open(SAVE, ">$save_file")) { + print "Cannot open file '$save_file': $!\n"; + exit 1; +} + +print "Saving configuration to '$save_file'..."; +select SAVE; +VyattaConfigOutput::outputActiveConfig(); +my $version_str = `/opt/vyatta/sbin/vyatta_current_conf_ver.pl`; +print SAVE $version_str; +select STDOUT; +print "\nDone\n"; +close SAVE; +exit 0; + diff --git a/scripts/vyatta-validate-type.pl b/scripts/vyatta-validate-type.pl new file mode 100755 index 0000000..318572c --- /dev/null +++ b/scripts/vyatta-validate-type.pl @@ -0,0 +1,15 @@ +#!/usr/bin/perl + +use strict; +use lib "/opt/vyatta/share/perl5/"; +use VyattaTypeChecker; + +# validate a value of a specific type +if ($#ARGV != 1) { + print "usage: vyatta-validate-type.pl <type> <value>\n"; + exit 1; +} + +exit 0 if (VyattaTypeChecker::validateType($ARGV[0], $ARGV[1])); +exit 1; + diff --git a/scripts/xorp_tmpl_tool b/scripts/xorp_tmpl_tool new file mode 100755 index 0000000..ab25fa9 --- /dev/null +++ b/scripts/xorp_tmpl_tool @@ -0,0 +1,150 @@ +#!/bin/bash + +UMASK_SAVE=`umask` +umask 0111 +XORPLOGFILE=/tmp/xorp_tmpl_tool.log +touch ${XORPLOGFILE} +umask ${UMASK_SAVE} + +#need to pass in value to change... as part of set command... +## cli ENV_EDIT_LEVEL +export VYATTA_EDIT_LEVEL=/; +## cli ENV_TEMPLATE_LEVEL +export VYATTA_TEMPLATE_LEVEL=/; + +## cli ENV_A_DIR +export VYATTA_ACTIVE_CONFIGURATION_DIR=/opt/vyatta/config/active; +mkdir -p $VYATTA_ACTIVE_CONFIGURATION_DIR + +#now need to grab the parent pid. +## XXX eventually, we will use each session's bash shell pid for this. +## however, for now, to interact with XORP we will rely on a global lock +## instead of separate config dirs. +#export VTID=$PPID +export VTID=XORP + +# lock for XORP +export XORP_LOCK="/opt/vyatta/config/active/.xorp.lck" + +## cli ENV_C_DIR +export VYATTA_CHANGES_ONLY_DIR=/opt/vyatta/config/tmp/changes_only_$VTID; +mkdir -p $VYATTA_CHANGES_ONLY_DIR + +## cli ENV_M_DIR +export VYATTA_TEMP_CONFIG_DIR=/opt/vyatta/config/tmp/new_config_$VTID; +if [ ! -d $VYATTA_TEMP_CONFIG_DIR ] +then + mkdir -p $VYATTA_TEMP_CONFIG_DIR + sudo mount -t unionfs -o dirs=${VYATTA_CHANGES_ONLY_DIR}=rw:/opt/vyatta/config/active=ro unionfs ${VYATTA_TEMP_CONFIG_DIR} +fi + +## cli ENV_TMP_DIR +export VYATTA_CONFIG_TMP=/opt/vyatta/config/tmp/tmp_$VTID; +mkdir -p $VYATTA_CONFIG_TMP + +RET_STATUS=0 +#this needs to be the array string of commands, something like $[*] or whatever + +echo "Command: ${@}" | grep -v -i password >> ${XORPLOGFILE} + +#echo "ConfigDirectories BEFORE ========>>>>>>" >> ${XORPLOGFILE} +#find /opt/vyatta/config -name "*" -print | grep interface >> ${XORPLOGFILE} +#echo "<=========ConfigDirectories BEFORE" >> ${XORPLOGFILE} + +## for tracing command-line XRL calls. +## 1 => info level +## 2 => warning level +#export CL_XRLTRACE=2 + +UMASK_SAVE=`umask` +umask 0111 +MYCMDERRLOGFILE=/tmp/my_cmd_err_${RANDOM}.log +rm -rf ${MYCMDERRLOGFILE} +umask ${UMASK_SAVE} + +case "$1" in + set) + /opt/vyatta/sbin/my_set "${@:2}" >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE} + RET_STATUS=$? + if [ $RET_STATUS != 0 ]; then + rm -rf $XORP_LOCK >&/dev/null + fi + ;; + delete) + /opt/vyatta/sbin/my_delete "${@:2}" >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE} + RET_STATUS=$? + if [ $RET_STATUS != 0 ]; then + rm -rf $XORP_LOCK >&/dev/null + fi + ;; + commit) + /opt/vyatta/sbin/my_commit >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE} + RET_STATUS=$? + rm -rf $XORP_LOCK >&/dev/null + ;; + test) + "${@:2}" >>${XORPLOGFILE} 2>>${MYCMDERRLOGFILE} + RET_STATUS=$? + ;; + cleanup) + + LOCKTRYCOUNTER=0 + LOCKTRYSTATUS=-1 + + while [[ ${LOCKTRYCOUNTER} -lt 60 && ${LOCKTRYSTATUS} -ne 0 ]] ; do + + if mkdir $XORP_LOCK >&/dev/null ; then + LOCKTRYSTATUS=0 + else + LOCKTRYCOUNTER=`expr ${LOCKTRYCOUNTER} + 1` + sleep 1; + fi + done + + if [ ${LOCKTRYCOUNTER} -ge 60 ] ; then + echo "Cannot unlock configuration" >> ${MYCMDERRLOGFILE} + rm -rf ${XORP_LOCK} + mkdir $XORP_LOCK >&/dev/null + fi + + sudo umount ${VYATTA_TEMP_CONFIG_DIR} + sudo rm -rf $VYATTA_CHANGES_ONLY_DIR/* $VYATTA_CHANGES_ONLY_DIR/.modified + sudo mount -t unionfs -o dirs=${VYATTA_CHANGES_ONLY_DIR}=rw:/opt/vyatta/config/active=ro unionfs ${VYATTA_TEMP_CONFIG_DIR} + RET_STATUS=0 + ;; + end_loading) + sudo umount ${VYATTA_TEMP_CONFIG_DIR} + sudo rm -rf ${VYATTA_CHANGES_ONLY_DIR} + sudo rm -rf ${VYATTA_CONFIG_TMP} + sudo rm -rf ${VYATTA_TEMP_CONFIG_DIR} + RET_STATUS=0 + ;; + rtrmgr_indirect_cleanup) + # do nothing now that we handle XORP interaction differently. + RET_STATUS=0 + ;; + *) + rm -rf ${MYCMDERRLOGFILE} + exit 1 + ;; +esac + +if [ -f ${MYCMDERRLOGFILE} ] ; then + + echo -n "STDERR:" >>${XORPLOGFILE} + cat ${MYCMDERRLOGFILE} >>${XORPLOGFILE} + echo "end of STDERR" >>${XORPLOGFILE} + + cat ${MYCMDERRLOGFILE} 1>&2 + + rm -rf ${MYCMDERRLOGFILE} + +fi + +#echo "ConfigDirectories AFTER ========>>>>>>" >> ${XORPLOGFILE} +#find /opt/vyatta/config -name "*" -print | grep interface >> ${XORPLOGFILE} +#echo "<=========ConfigDirectories AFTER" >> ${XORPLOGFILE} + +echo "ret=${RET_STATUS}" >> ${XORPLOGFILE} +exit $RET_STATUS + diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..a2b4c94 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,9 @@ +cli_parse.tab.c +cli_parse.tab.h +cli_def.lex.c +cli_def.tab.c +cli_def.tab.h +cli_val.lex.c +delete +my_* +show diff --git a/src/cli_def.l b/src/cli_def.l new file mode 100644 index 0000000..41994d6 --- /dev/null +++ b/src/cli_def.l @@ -0,0 +1,424 @@ +%{ +#include "cli_val.h" +#include "cli_parse.h" +FILE *yy_cli_def_in; +static void make_def_value(vtw_type_e type); +static int cli_last_nl_returned=0; +#define STR_DELTA 2 +%} +%x str +%option noyywrap +%option nounput +%option never-interactive + +/* + * Regular expressions of IP and MAC addresses, URLs, etc. + */ + +/* + * IPv4 address representation. + */ +RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2}) +RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3} +RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9]) +RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN} + +/* + * IPv6 address representation in Augmented Backus-Naur Form (ABNF) + * as defined in RFC-2234. + * IPv6 address representation taken from RFC-3986: + * + * IPv6address = 6( h16 ":" ) ls32 + * / "::" 5( h16 ":" ) ls32 + * / [ h16 ] "::" 4( h16 ":" ) ls32 + * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + * / [ *4( h16 ":" ) h16 ] "::" ls32 + * / [ *5( h16 ":" ) h16 ] "::" h16 + * / [ *6( h16 ":" ) h16 ] "::" + * + * h16 = 1*4HEXDIG + * ls32 = ( h16 ":" h16 ) / IPv4address + * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + * dec-octet = DIGIT ; 0-9 + * / %x31-39 DIGIT ; 10-99 + * / "1" 2DIGIT ; 100-199 + * / "2" %x30-34 DIGIT ; 200-249 + * / "25" %x30-35 ; 250-255 + */ + +RE_H16 [a-fA-F0-9]{1,4} +RE_H16_COLON {RE_H16}":" +RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4}) +RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32} +RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32} +RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32} +RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32} +RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32} +RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32} +RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32} +RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16} +RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::" +RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9} +RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]? +RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN} + +/* + * Ethernet MAC address representation. + */ +RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5} + +/* + * URL-related regular expressions. + * + * Implementation is based on the BNF-like specification from: + * - RFC-1738: HTTP, FTP, FILE + * - RFC-3617: TFTP + * - RFC-3986: update of RFC-1738 + */ +RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP} + +/* + * URL schemeparts for IP based protocols. + * Representation taken from RFC-1738, and some of it is updated by RFC-3986. + * + * login = [ user [ ":" password ] "@" ] hostport + * hostport = host [ ":" port ] + * host = hostname | hostnumber + * hostname = *[ domainlabel "." ] toplabel + * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit + * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit + * alphadigit = alpha | digit + * hostnumber = digits "." digits "." digits "." digits + * port = digits + * user = *[ uchar | ";" | "?" | "&" | "=" ] + * password = *[ uchar | ";" | "?" | "&" | "=" ] + */ +RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT} +RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})? +RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL} +RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]" +RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+ +RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL} +RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT} +RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS} +RE_URL_PORT {RE_URL_DIGITS} +RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* +RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* + +/* + * FILE URL regular expression. + * Representation taken from RFC-1738. + * + * fileurl = "file://" [ host | "localhost" ] "/" fpath + */ +RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH} + +/* + * FTP URL regular expression. + * Representation taken from RFC-1738. + * + * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ] + * fpath = fsegment *[ "/" fsegment ] + * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ] + * ftptype = "A" | "I" | "D" | "a" | "i" | "d" + */ +RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)? +RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})* +RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")* +RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d" + +/* + * HTTP URL regular expression. + * Representation taken from RFC-1738. + * + * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ] + * hpath = hsegment *[ "/" hsegment ] + * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + */ +RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)? +RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})* +RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* +RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* + +/* + * TFTP URL regular expression. + * Representation taken from RFC-3617. + * + * tftpURI = "tftp://" host "/" file [ mode ] + * mode = ";" "mode=" ( "netascii" / "octet" ) + * file = *( unreserved / escaped ) + * host = <as specified by RFC 2732 [3]> + * unreserved = <as specified in RFC 2396 [4]> + * escaped = <as specified in RFC 2396> + */ +RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})? +RE_URL_TFTP_MODE ";""mode="("netascii"|"octet") +RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})* + +/* + * URL-related miscellaneous definitions. + * Representation taken from RFC-1738 and from RFC-3986. + * + * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | + * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | + * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + * "y" | "z" + * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + * alpha = lowalpha | hialpha + * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + * "8" | "9" + * safe = "$" | "-" | "_" | "." | "+" + * extra = "!" | "*" | "'" | "(" | ")" | "," + * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`" + * punctuation = "<" | ">" | "#" | "%" | <"> + * + * + * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" + * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + * "a" | "b" | "c" | "d" | "e" | "f" + * escape = "%" hex hex + * + * unreserved = alpha | digit | safe | extra + * uchar = unreserved | escape + * xchar = unreserved | reserved | escape + * digits = 1*digit + * + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +RE_URL_LOWALPHA [a-z] +RE_URL_HIALPHA [A-Z] +RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA} +RE_URL_DIGIT [0-9] +RE_URL_SAFE "$"|"-"|"_"|"."|"+" +RE_URL_EXTRA "!"|"*"|"'"|"("|")"|"," +RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`" +RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<"> +RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"=" +RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f] +RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG} +RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA} +RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE} +RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE} +RE_URL_DIGITS {RE_URL_DIGIT}{1,} +RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"=" + + +%% + char *string_buf=NULL; + char *string_buf_ptr=NULL; + int string_len=0, string_buf_len=0; + char str_delim=0; + +#[^\n]*(\\[ \t]*\n[^\n]*)*\n { /* comment */ } +\n {yy_cli_def_lineno++;return EOL;} +<<EOF>> { if(!cli_last_nl_returned) {cli_last_nl_returned=1;return EOL;} yyterminate(); } +[\`\"] { + BEGIN(str); + str_delim = yy_cli_def_text[0]; + string_buf_ptr = string_buf = my_malloc(STR_DELTA + 1, ""); + string_len = STR_DELTA; + string_buf_len = STR_DELTA; + } +<str>[\"\`] { + /* maybe a closing quote - all done */ + if(str_delim == yy_cli_def_text[0]) { + BEGIN(INITIAL); + *string_buf_ptr = 0; + /* return string constant token type and + * value to parser + */ + yy_cli_parse_lval.strp = string_buf; + return str_delim == '"'?STRING:EX_STRING; + }else{ + *string_buf_ptr++ = yy_cli_def_text[0]; + goto string_too_long; + } + } + +<str>\n { + /* error - unterminated string constant */ + yy_cli_def_lineno++; + } + +<str>\\n {*string_buf_ptr++ = '\n';goto string_too_long;} +<str>\\t {*string_buf_ptr++ = '\t';goto string_too_long;} +<str>\\r {*string_buf_ptr++ = '\r';goto string_too_long;} +<str>\\b {*string_buf_ptr++ = '\b';goto string_too_long;} +<str>\\f {*string_buf_ptr++ = '\f';goto string_too_long;} +<str>\\\n { yy_cli_def_lineno++; /* continuation => ignore */ } + +<str>\\. { + *string_buf_ptr++ = yy_cli_def_text[1]; +string_too_long: + *string_buf_ptr = 0; + /* printf("Cur string |%s|\n", string_buf); */ + if (!--string_len) { + string_buf = my_realloc(string_buf, + string_buf_len + STR_DELTA + 1, + "cli_def STRING"); + string_buf_ptr = string_buf + string_buf_len; + string_buf_len += STR_DELTA; + string_len = STR_DELTA; + } + } +<str>[^\\\n\"\`]+ { + char *yptr = yy_cli_def_text; + + while ( *yptr ){ + *string_buf_ptr++ = *yptr++; + if (!--string_len) { + string_buf = my_realloc(string_buf, + string_buf_len + STR_DELTA + 1, + "cli_def STRING"); + string_buf_ptr = string_buf + string_buf_len; + string_buf_len += STR_DELTA; + string_len = STR_DELTA; + } + } + *string_buf_ptr = 0; + } + +default: return DEFAULT; +tag: return TAG; +type: return TYPE; +help: return HELP; +syntax: return SYNTAX; +commit: return COMMIT; +check: yy_cli_parse_lval.action = syntax_act; return ACTION; +delete: yy_cli_parse_lval.action = delete_act; return ACTION; +update: yy_cli_parse_lval.action = update_act; return ACTION; +activate: yy_cli_parse_lval.action = activate_act; return ACTION; +create: yy_cli_parse_lval.action = create_act; return ACTION; +begin: yy_cli_parse_lval.action = begin_act; return ACTION; +end: yy_cli_parse_lval.action = end_act; return ACTION; +multi: return MULTI; + +:: { + make_def_value(IPV6_TYPE); + return VALUE; + } +txt { + yy_cli_parse_lval.type = TEXT_TYPE; + return TYPE_DEF; + } + +pattern return PATTERN; + +exec return EXEC; + +, return COMMA; +\|\| return OR; +\&\& return AND; +\= return ASSIGN; +\=\= {yy_cli_parse_lval.cond = EQ_COND; return COND;} +\!\= {yy_cli_parse_lval.cond = NE_COND; return COND;} +\< {yy_cli_parse_lval.cond = LT_COND; return COND;} +\> {yy_cli_parse_lval.cond = GT_COND; return COND;} +\<\= {yy_cli_parse_lval.cond = LE_COND; return COND;} +\>\= {yy_cli_parse_lval.cond = GE_COND; return COND;} +in {yy_cli_parse_lval.cond = IN_COND; return COND;} +\! { return NOT; } +\$\([^)]+\) { + yy_cli_parse_lval.strp = my_strdup(yy_cli_def_text, "TEXT"); + return VAR; + } +true { + make_def_value(BOOL_TYPE); + return VALUE; + } + +false { + make_def_value(BOOL_TYPE); + return VALUE; + } + +[0-9]+ { + make_def_value(INT_TYPE); + return VALUE; + } + +{RE_IPV4} { + make_def_value(IPV4_TYPE); + return VALUE; + } + +{RE_IPV4NET} { + make_def_value(IPV4NET_TYPE); + return VALUE; + } + +{RE_IPV6} { + make_def_value(IPV6_TYPE); + return VALUE; + } + +{RE_IPV6NET} { + make_def_value(IPV6NET_TYPE); + return VALUE; + } + +{RE_MACADDR} { + make_def_value(MACADDR_TYPE); + return VALUE; + } +u32 { + yy_cli_parse_lval.type = INT_TYPE; + return TYPE_DEF; + } + +ipv4 { + yy_cli_parse_lval.type = IPV4_TYPE; + return TYPE_DEF; + } + +ipv4net { + yy_cli_parse_lval.type = IPV4NET_TYPE; + return TYPE_DEF; + } +ipv6 { + yy_cli_parse_lval.type = IPV6_TYPE; + return TYPE_DEF; + } + +ipv6net { + yy_cli_parse_lval.type = IPV6NET_TYPE; + return TYPE_DEF; + } +bool { + yy_cli_parse_lval.type = BOOL_TYPE; + return TYPE_DEF; + } +macaddr { + yy_cli_parse_lval.type = MACADDR_TYPE; + return TYPE_DEF; + } +\( return LP; +\) return RP; +; return SEMI; + +\\\n { yy_cli_def_lineno++; /*whitespace -- continuation => ignore */ } + +[ \t]+ /* whitespace */ + +. { + /* everything else is a syntax error */ + return SYNTAX_ERROR; + } + +%% + +static void make_def_value(vtw_type_e type) +{ + memset(&yy_cli_parse_lval.val.val, 0, sizeof(yy_cli_parse_lval.val.val)); + yy_cli_parse_lval.val.free_me = TRUE; + yy_cli_parse_lval.val.val = my_strdup(yy_cli_def_text, "cli_parse.l"); + yy_cli_parse_lval.val.val_type = type; +} diff --git a/src/cli_new.c b/src/cli_new.c new file mode 100644 index 0000000..2f2801d --- /dev/null +++ b/src/cli_new.c @@ -0,0 +1,1938 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include "cli_val.h" +#include "cli_parse.h" +#include <regex.h> + +#include "cli_objects.h" +#include "cli_val_engine.h" + +/* Defines: */ + +#define EXE_STRING_DELTA 512 +#define PATH_DELTA 1000 +#define ENDS_ALLOC 20 +#define PATH_CM_LOCATION 25 /* mcd vs. ccd location + change when m_root changed */ + +/* Global vars: */ +vtw_path m_path, t_path; + +/* Loval vars: */ +static vtw_node *vtw_free_nodes; /* linked via left */ +static char val_name[] = VAL_NAME; +static int cond1[TOP_COND] ={5, 0,-1,-1, 0, 1, 0, 0}; +static int cond2[TOP_COND] ={5, 0, 1,-1,-1, 1, 1, 0}; +static char const *cond_formats[DOMAIN_TYPE] = + { + 0, + "%u", /* INT_TYPE */ + "%u.%u.%u.%u", /*IPV4_TYPE*/ + "%u.%u.%u.%u/%u", /*IPV4NET_TYPE*/ + 0, + 0, + "%x:%x:%x:%x:%x:%x" /* MACADDR_TYPE */ + }; + +static int cond_format_lens[DOMAIN_TYPE] = + { + 0, + 1, /* INT_TYPE */ + 4, /*IPV4_TYPE*/ + 5, /*IPV4NET_TYPE*/ + 0, + 0, + 6 /* MACADDR_TYPE */ + }; + +static int cli_val_len; +static char *cli_val_alloc; +static char *cli_val_ptr; + +static char *exe_string; +static int exe_string_len; +static int node_cnt; +static int free_node_cnt; +static boolean in_validate_val; +static valstruct validate_value_val; /* value being validated + to be used as $(@) */ + +/* Local function declarations: */ + +static int check_comp(vtw_node *cur); +static boolean check_syn_func(vtw_node *cur,const char* func,int line); +#define check_syn(cur) check_syn_func((cur),__FUNCTION__,__LINE__) +static void copy_path(vtw_path *to, vtw_path *from); +static int eval_va(valstruct *res, vtw_node *node); +static int expand_string(char *p); +static void free_node(vtw_node *node); +static void free_node_tree(vtw_node *node); +static void free_reuse_list(void); +static void free_path(vtw_path *path); +static void free_string(char *str); +static vtw_node * get_node(void); + +static void scan_ipv6(char *val, unsigned int *parts); + +static int set_reference_environment(const char* var_reference, + clind_path_ref *n_cfg_path, + clind_path_ref *n_tmpl_path, + clind_path_ref *n_cmd_path, + int active); + +/************************************************* + GLOBAL FUNCTIONS +***************************************************/ +/* it is executed as "eval `my_set` in order to be able to + modify BASH env + therefore, all error will be reported as + printf("echo \"bla-bla-bla%s\";", sptr) + note very important ';' as the end of the format +*/ +void bye(char *msg, ...) +{ + va_list ap; + + if (is_silent_msg()) + exit(0); + va_start(ap, msg); + if (is_echo()) + printf("echo \""); + vprintf(msg, ap); + printf(is_echo()? "\";":"\n"); + va_end(ap); + + exit(0); +} + +/* msg: + print message, preceeded by "echo " if global + flag echo set. This flag is used by program + which are executed as eval `command` in order to + modify BASH env +*/ +void print_msg(char *msg, ...) +{ + va_list ap; + + if (is_silent_msg()) + return; + va_start(ap, msg); + if (is_echo()) + printf("echo \""); + vprintf(msg, ap); + printf(is_echo()? "\";":"\n"); + va_end(ap); +} + +void touch_dir(const char *dp) +{ + struct stat statbuf; + if (lstat(dp, &statbuf) < 0) { + char *command; + command = my_malloc(strlen(dp) + 10, "set"); + sprintf(command, "mkdir -p %s", dp); + system(command); + free(command); + return; + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("directory %s expected, found regular file", dp); + } + return; +} + +/***************************************************** + add_val: + verify that the types are the same; + if first valstruct is single value, convert it + into multivalue; + add the value of second to the list of first; +*****************************************************/ +void add_val(valstruct *first, valstruct *second) +{ + assert (first->free_me && second->free_me); + assert(second->cnt == 0); + if (first->val_type != second->val_type) { + printf("Different types\n\n"); + } else { + if (first->cnt%MULTI_ALLOC == 0) { + /* convert into multivalue */ + first->vals = my_realloc(first->vals, (first->cnt + MULTI_ALLOC) * + sizeof(char *), "add_value"); + if (first->cnt == 0) { /* single value - convert */ + first->vals[0] = first->val; + first->cnt = 1; + first->val = NULL; + } + } + second->free_me = FALSE; /* we took its string */ + first->vals[first->cnt] = second->val; + ++first->cnt; + } +} +/***************************************************** + append - append node to the tail of list +*****************************************************/ +void append(vtw_list *l, vtw_node *n, int aux) +{ + vtw_node *lnode; + lnode = make_node(LIST_OP, n, NULL); + lnode->vtw_node_aux = aux; + if(l->vtw_list_tail) { + assert(l->vtw_list_tail->vtw_node_right == NULL); + l->vtw_list_tail->vtw_node_right = lnode; + } else { + assert(l->vtw_list_head == NULL); + l->vtw_list_head = lnode; + } + l->vtw_list_tail = lnode; +} + +void dt(vtw_sorted *srtp) +{ + int i; + for (i=0; i<srtp->num; ++i) + printf("%d %s\n", i, (char *)(srtp->ptrs[i])); +} + + +void di(vtw_sorted *srtp) +{ + int i; + for (i=0; i<srtp->num; ++i) + printf("%u %u\n", i, *(unsigned int *)(srtp->ptrs[i])); +} + +static char _lock_file_[1025] = {0}; +static int _lock_fd_=-1; + +static void clean_lock_file(void) { + if(_lock_file_[0]) { + unlink(_lock_file_); + _lock_file_[0]=0; + } + if(_lock_fd_!=-1) { + close(_lock_fd_); + _lock_fd_=-1; + } +} + +boolean get_config_lock(const char* adirp, const char* lock_name) { + + boolean ret = TRUE; + + sprintf(_lock_file_, "%s/%s", adirp, lock_name); + + _lock_fd_ = open(_lock_file_, O_WRONLY | O_CREAT | O_EXCL, 0644); + + ret = (_lock_fd_!=-1); + + if(ret) { + atexit(clean_lock_file); + } + + return ret; +} + +void internal_error(int line, char *file) +{ + printf("\n\nInternal Error at line %d in %s\n", line, file); + exit (-1); +} + +/************************************************* + vtw_sort: + create sorted structure for the value, + allocates ptrs and parts in this structure +*/ +void vtw_sort(valstruct *valp, vtw_sorted *sortp) +{ + int i, unsorted, left, child, right; + void *leftp, *rightp, *childp; + const char * format; + unsigned int *parts; + vtw_type_e type = valp->val_type; + char *cp; + int cur=0, par=0, partnum=0, res=0; + void *curp, *parp; + + sortp->num = valp->cnt?valp->cnt : 1; + sortp->ptrs = my_malloc(sortp->num * sizeof(void *), "sort_ptrs"); + sortp->partnum = cond_format_lens[type]; + if (sortp->partnum) { + sortp->parts = my_malloc(sortp->partnum * sortp->num * sizeof(void *), + "sort_parts"); + }else{ + sortp->parts = NULL; + } + switch (type){ + case IPV6_TYPE: + case IPV6NET_TYPE: + for (i = 0; i < sortp->num; ++i) { + parts = sortp->parts + i * sortp->partnum; + scan_ipv6(valp->cnt?valp->vals[i]:valp->val, parts); + sortp->ptrs[i] = parts; + } + break; + case IPV4_TYPE: + case IPV4NET_TYPE: + case MACADDR_TYPE: + case INT_TYPE: + format = cond_formats[valp->val_type]; + for (i = 0; i < sortp->num; ++i) { + cp = valp->cnt?valp->vals[i]:valp->val; + parts = sortp->parts + i * sortp->partnum; + switch (sortp->partnum) { + case 1: + (void) sscanf(cp, format, parts); + break; + case 2: + (void) sscanf(cp, format, parts, parts+1); + break; + case 3: + (void) sscanf(cp, format, parts, parts+1, parts+2); + break; + case 4: + (void) sscanf(cp, format, parts, parts+1, parts+2, + parts+3); + break; + case 5: + (void) sscanf(cp, format, parts, parts+1, parts+2, + parts+3, parts+4); + break; + case 6: + (void) sscanf(cp, format, parts, parts+1, parts+2, + parts+3, parts+4, parts+5); + break; + } + sortp->ptrs[i] = parts; + } + break; + case TEXT_TYPE: + case BOOL_TYPE: + for (i = 0; i < sortp->num; ++i) { + sortp->ptrs[i] = valp->cnt?valp->vals[i]:valp->val; + } + break; + default: + printf("Unknown value in switch on line %d\n", __LINE__); + exit(-5); + } + if (sortp->num < 2) + return; + /* now do a heap sort */ + /* build heap */ + /* from left to right, we start with the heap of only one (first) element*/ + for (i = 2; i <= sortp->num; ++i) + { + cur = i; + do { + curp = sortp->ptrs[cur - 1]; + par = cur >> 1; + parp = sortp->ptrs[par - 1]; + if (sortp->partnum){ + for(partnum = 0; partnum < sortp->partnum; ++partnum) { + if (*((unsigned int *)curp + partnum)> + *((unsigned int *)parp + partnum)){ + res = 1; + break; + } + if (*((unsigned int *)curp + partnum)< + *((unsigned int *)parp + partnum)){ + res = -1; + break; + } + res = 0; + } + }else{ + res = strcmp((char *)curp, (char *) parp); + } + if (res <= 0) + break; + /* swap them */ + sortp->ptrs[cur - 1] = parp; + sortp->ptrs[par - 1] = curp; + + } while ((cur = par) != 1); + } + /* convert heap into sorted array */ + unsorted = sortp->num; /* sortp->num must be >= 2 */ + while (TRUE) { + void *tp; + /* root to the sorted part */ + tp = sortp->ptrs[0]; + sortp->ptrs[0] = sortp->ptrs[--unsorted]; + sortp->ptrs[unsorted] = tp; + if (unsorted == 1) + break; + /* push down the new root */ + par = 1; + while(TRUE) { + left = par << 1; /* left child */ + if (left > unsorted) + break; /* no children */ + else { + if (left == unsorted) { + /* only left child */ + child = left; + } else { + /* both children */ + right = left+1; + leftp = sortp->ptrs[left - 1]; + rightp = sortp->ptrs[right - 1]; + /* find larger child */ + if (sortp->partnum){ + for(partnum = 0; partnum < sortp->partnum; ++partnum) { + if (*((unsigned int *)leftp + partnum) > + *((unsigned int *)rightp + partnum)) { + res = 1; + break; + } + if (*((unsigned int *)leftp + partnum) < + *((unsigned int *)rightp + partnum)) { + res = -1; + break; + } + res = 0; + } + }else{ + res = strcmp((char *)leftp, (char *) rightp); + } + if (res >= 0) { + child = left; /* left is larger or same*/ + } else { + child = right; + } + } + /* compare parent and larger child */ + parp = sortp->ptrs[par - 1]; + childp = sortp->ptrs[child - 1]; + if (sortp->partnum){ + for(partnum = 0; partnum < sortp->partnum; ++partnum) { + if (*((unsigned int *)parp + partnum) > + *((unsigned int *)childp + partnum)){ + res = 1; + break; + } + if (*((unsigned int *)parp + partnum) < + *((unsigned int *)childp + partnum)){ + res = -1; + break; + } + res = 0; + } + }else{ + res = strcmp((char *)parp, (char *) childp); + } + if (res >= 0) { + /* done with percolating down, parent larger than child */ + break; + } + /* child greater, exchage and continue */ + sortp->ptrs[par - 1] = childp; + sortp->ptrs[child - 1] = parp; + par = child; + } + } + } +} + +/* returns FALSE if execution returns non-null, + returns TRUE if every excution returns NULL +*/ +boolean execute_list(vtw_node *cur, vtw_def *def) +{ + boolean ret; + int status; + set_in_exec(TRUE); + status = char2val(def, get_at_string(), &validate_value_val); + if (status) return FALSE; + ret = check_syn(cur); + free_val(&validate_value_val); + set_in_exec(FALSE); + return ret; +} + + +/***************************************************** + make_node - create a node with oper, left, and right +*****************************************************/ +vtw_node * make_node(vtw_oper_e oper, vtw_node *left, + vtw_node *right) +{ + vtw_node *ret ; + ret = get_node(); + ret->vtw_node_oper = oper; + ret->vtw_node_left = left; + ret->vtw_node_right = right; + ret->vtw_node_string = NULL; + ret->vtw_node_aux = 0; + return ret; +} +vtw_node *make_str_node0(char *str, vtw_oper_e op) +{ + vtw_node *ret; + ret = make_node(op, NULL, NULL); + ret->vtw_node_string = str; + ret->vtw_node_type = TEXT_TYPE; + return ret; +} +/***************************************************** + make_str_node - create a VAL_OP node with str +*****************************************************/ +vtw_node *make_str_node(char *str) +{ + return make_str_node0(str, VAL_OP); +} +/***************************************************** + make_var_node - create a VAR_OP node with str +*****************************************************/ +vtw_node *make_var_node(char *str) +{ + return make_str_node0(str, VAR_OP); +} +/***************************************************** + make_val_node - create a VAl_OP node with str +*****************************************************/ +vtw_node *make_val_node(valstruct *val) +{ + vtw_node *ret; + assert(val->free_me); + ret = make_node(VAL_OP, NULL, NULL); + ret->vtw_node_val = *val; + val->free_me = FALSE; + return ret; +} +valstruct str2val(char *cp) +{ + valstruct ret; + memset(&ret, 0, sizeof(ret)); + ret.val_type = TEXT_TYPE; + ret.val = cp; + ret.free_me = TRUE; + return ret; +} +/**************************************************** + STATIC FUNCTIONS +****************************************************/ + +/************************************************** + char2val: + convert string into valstruct verifying the type + according to def +****************************************************/ +int char2val(vtw_def *def, char *value, valstruct *valp) +{ + int token; + char *endp, *cp; + int linecnt, cnt; + int my_type = def->def_type; + boolean first = TRUE; + + memset(valp, 0, sizeof (*valp)); + + if (my_type == ERROR_TYPE) { + my_type = TEXT_TYPE; + } + + if (my_type != TEXT_TYPE && my_type != ERROR_TYPE) { + cli_val_len = strlen(value); + cli_val_ptr = value; + while(1) { + token = yy_cli_val_lex(); + if (token != VALUE) { + if (first || token){ + if (def->def_type_help){ + set_at_string(value); + (void)expand_string(def->def_type_help); + printf("%s\n", exe_string); + fprintf(stderr, "%s\n", exe_string); + } else { + print_msg("Wrong type of value in %s, " + "need %s\n", + m_path.path_buf + m_path.print_offset, + type_to_name(my_type)); + } + return -1; + } + return 0; + } + if (my_type != get_cli_value_ptr()->val_type) { + if (def->def_type_help){ + set_at_string(value); + (void)expand_string(def->def_type_help); + printf("%s\n", exe_string); + fprintf(stderr, "%s\n", exe_string); + } else { + print_msg("Wrong type of value in %s, " + "need %s\n", + m_path.path_buf + m_path.print_offset, + type_to_name(my_type)); + } + my_free(get_cli_value_ptr()->val); + if (first) + return -1; + return 0; + } + if (first) { + *valp = *get_cli_value_ptr(); + get_cli_value_ptr()->free_me = FALSE; + first = FALSE; + } else { + if (def->multi) + add_val(valp, get_cli_value_ptr()); + else { + print_msg("Unexpected multivalue in %s\n", m_path.path); + free_val(get_cli_value_ptr()); + } + } + token = yy_cli_val_lex(); + if (!token) + return 0; + if (token != EOL) { + print_msg("Badly formed value in %s\n", + m_path.path + m_path.print_offset); + if (token == VALUE) + my_free(get_cli_value_ptr()->val); + return 0; + } + } + return 0; + } + valp->val_type = TEXT_TYPE; + valp->free_me = TRUE; + /* count lines */ + linecnt = 0; + for (cp = value; *cp; ++cp) + if (*cp == '\n') + ++linecnt; + if (cp != value && cp[-1] != '\n') + ++linecnt; /* last non empty non \n terminated string */ + if (linecnt == 0) /* one empty non terminated string */ + linecnt = 1; + if (linecnt == 1) { + valp->val=my_strdup(value, "char2val 1"); + /*truncate '\n' etc */ + endp = strchr(valp->val, '\n'); + if (endp) + *endp = 0; + } else { + valp->cnt = linecnt; + cnt = (linecnt + MULTI_ALLOC - 1) / MULTI_ALLOC; + cnt *= MULTI_ALLOC; + valp->vals = my_malloc(cnt * sizeof(char *), "char2val 2"); + for(cp = value, cnt = 0; cnt < linecnt; ++cnt) { + endp = strchr(cp, '\n'); + if (endp) + *endp = 0; + valp->vals[cnt]=my_strdup(cp, "char2val 3"); + if (endp) { + *endp = '\n'; + cp = endp + 1; + } else { + /* non '\n' terinated string, must be last line, we are done */ + ++cnt; + assert(cnt == linecnt); + break; + } + } + } + return 0; +} + + +/**************************************************** + val_comp: + compare two values per cond + returns result of comparison +****************************************************/ +boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond) +{ + unsigned int left_parts[9], right_parts[9]; + vtw_type_e val_type; + int parts_num, lstop, rstop, lcur, rcur; + char const *format; + char *lval, *rval; + int ret=0, step=0, res=0; + + val_type = left->val_type; + if (left->cnt) + lstop = left->cnt; + else + lstop = 1; + if (right->cnt) + rstop = right->cnt; + else + rstop = 1; + + for(lcur = 0; lcur < lstop; ++lcur) { + if (!lcur && !left->cnt) + lval = left->val; + else + lval = left->vals[lcur]; + for(rcur = 0; rcur < rstop; ++rcur) { + if (!rcur && !right->cnt) + rval = right->val; + else + rval = right->vals[rcur]; + switch (val_type) { + case IPV6_TYPE: + parts_num = 8; + goto ipv6_common; + case IPV6NET_TYPE: + parts_num = 9; + ipv6_common: + scan_ipv6(lval,left_parts); + scan_ipv6(rval,right_parts); + break; + case IPV4_TYPE: + case IPV4NET_TYPE: + case MACADDR_TYPE: + case INT_TYPE: + format = cond_formats[val_type]; + parts_num = cond_format_lens[val_type]; + (void) sscanf(lval, format, left_parts, left_parts+1, + left_parts+2, left_parts+3, left_parts+4, + left_parts+5); + (void) sscanf(rval, format, right_parts, right_parts+1, + right_parts+2, right_parts+3, right_parts+4, + right_parts+5); + break; + case TEXT_TYPE: + case BOOL_TYPE: + res = strcmp(lval, rval); + goto done_comp; + default: + printf("Unknown value in switch on line %d\n", __LINE__); + exit(-5); + } + /* here to do a multistep int compare */ + for (step = 0; step < parts_num; ++ step) { + if (left_parts[step] > right_parts[step]) { + res = 1; + break; /* no reason to continue checking other steps */ + } + if (left_parts[step] < right_parts[step]) { + res = -1; + break; /* no reason to continue checking other steps */ + } + res = 0; + } + done_comp: + if(res > 0) res = 1; + else if(res < 0) res = -1; + ret = ((res == cond1[cond]) || + (res == cond2[cond])); + if (ret && cond == IN_COND) { + set_in_cond_tik(rcur); /* for delete */ + /* one success is enough for right cycle + in case of IN_COND, continue left cycle */ + break; + } + if (!ret && cond != IN_COND) + /* one failure is enough in cases + other than IN_COND - go out */ + return ret; + /* in all other cases: + (fail & IN_COND) or (success & !IN_COND) + contniue checking; */ + } + } + return ret; +} + + + +/**************************************************** + check_comp: + evaluate comparison node. + returns boolean value of result +****************************************************/ +static boolean check_comp(vtw_node *cur) +{ + int ret; + int status; + valstruct left, right; + + memset(&left, 0 , sizeof(left)); + memset(&right, 0 , sizeof(right)); + ret = FALSE; /* in case of status */ + status = eval_va(&left, cur->vtw_node_left); + if (status) + goto free_and_return; + status = eval_va(&right, cur->vtw_node_right); + if (status) + goto free_and_return; + if(left.val_type != right.val_type) { + printf("Different types in comparison\n"); + goto free_and_return; + } + ret = val_cmp(&left, &right,cur->vtw_node_aux); + free_and_return: + if (left.free_me) + free_val(&left); + if (right.free_me) + free_val(&right); + return ret; +} + +/****************** + Change value of var in the file + +*****************/ + +static int write_value_to_file(const char* var_path,const char* value) { + + if(var_path && value) { + + { + /*Build directory, if necessary:*/ + clind_path_ref var_dir_path=clind_path_construct(var_path); + + if(!var_dir_path) bye("Can not construct path %s", var_path); + else { + + char* end = clind_path_pop_string(var_dir_path); + + if(!end || strcmp(end,VAL_NAME)) { + bye("Wrong end of path: %s (%s)", end,var_path); + } + + free(end);end=NULL; + + touch(); + touch_dir(clind_path_get_path_string(var_dir_path)); + + clind_path_destruct(&var_dir_path); + } + } + + { + /*Write to file*/ + FILE* fp = fopen(var_path, "w"); + if(!fp) bye("Can not open value file %s", var_path); + + if (fputs(value, fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", var_path); + + fclose(fp); + + } + } + + return 0; +} + +static int change_var_value(const char* var_reference,const char* value, int active_dir) { + + int ret=-1; + + if(var_reference && value) { + + char* var_path=NULL; + + clind_path_ref n_cfg_path=NULL; + clind_path_ref n_tmpl_path=NULL; + clind_path_ref n_cmd_path=NULL; + + if(set_reference_environment(var_reference, + &n_cfg_path, + &n_tmpl_path, + &n_cmd_path, + active_dir)==0) { + + clind_val cv; + + memset(&cv,0,sizeof(cv)); + + if(clind_config_engine_apply_command_path(n_cfg_path, + n_tmpl_path, + n_cmd_path, + FALSE, + &cv, + get_cdirp(), + get_tdirp(), + TRUE)==0) { + var_path=cv.value; + + } + + } + + if(n_cfg_path) clind_path_destruct(&n_cfg_path); + if(n_tmpl_path) clind_path_destruct(&n_tmpl_path); + if(n_cmd_path) clind_path_destruct(&n_cmd_path); + + if(var_path) { + ret=write_value_to_file(var_path,value); + free(var_path); + } + } + + return ret; +} + +/**************************************************** + check_syn: + evaluate syntax tree; + returns TRUE if all checks are OK, + returns FALSE if check fails. +****************************************************/ +static boolean check_syn_func(vtw_node *cur,const char* func,int line) +{ + int status; + int ret; + int ii; + + switch(cur->vtw_node_oper) { + case LIST_OP: + ret = check_syn(cur->vtw_node_left); + if (!is_in_commit() && cur->vtw_node_aux) + ret = TRUE; + if (!ret || !cur->vtw_node_right) /* or no right operand */ + return ret; + return check_syn(cur->vtw_node_right); + case HELP_OP: + ret = check_syn(cur->vtw_node_left); + if (ret <= 0){ + if (expand_string(cur->vtw_node_right->vtw_node_string) == VTWERR_OK) { + fprintf(stderr, exe_string); + fprintf(stderr, "\n"); + printf(exe_string); + printf("\n"); + } + } + return ret; + + case ASSIGN_OP: + + if (is_in_exec()) { + + valstruct right; + + char* var_reference = NULL; + + memset(&right, 0, sizeof(right)); + status = eval_va(&right, cur->vtw_node_right); + + if (status || right.cnt) { /* bad or multi */ + if (right.free_me) free_val(&right); + return FALSE; + } + + var_reference = strdup(cur->vtw_node_left->vtw_node_string+2); + + { + int i=0; + while(var_reference[i]) { + if(var_reference[i]==')') { + var_reference[i]=0; + break; + } + i++; + } + } + + change_var_value(var_reference,right.val,FALSE); + change_var_value(var_reference,right.val,TRUE); + + if (right.free_me) free_val(&right); + + if(var_reference) free(var_reference); + } + + return TRUE; + + case EXEC_OP: + /* for every value */ + if (in_validate_val) { + char *save_at = get_at_string(); + for(ii = 0; ii < validate_value_val.cnt || ii == 0; ++ii) { + set_at_string(validate_value_val.cnt? + validate_value_val.vals[ii]:validate_value_val.val); + status = expand_string(cur->vtw_node_left->vtw_node_string); + if (status != VTWERR_OK) { + set_at_string(save_at); + return FALSE; + } + ret = system(exe_string); + if (ret) { + set_at_string(save_at); + return FALSE; + } + } + set_at_string(save_at); + return TRUE; + } + /* else */ + status = expand_string(cur->vtw_node_left->vtw_node_string); + if (status != VTWERR_OK) { + return FALSE; + } + ret = system(exe_string); + return !ret; + + case PATTERN_OP: /* left to var, right to pattern */ + { + valstruct left; + regex_t myreg; + boolean ret; + int ii; + + ret = TRUE; + status = eval_va(&left, cur->vtw_node_left); + if (status) { + ret = FALSE; + goto free_and_return; + } + status = regcomp(&myreg, cur->vtw_node_right->vtw_node_string, + REG_EXTENDED); + if (status) + bye("Can not compile regex |%s|, result %d\n", + cur->vtw_node_right->vtw_node_string, status); + /* for every value */ + for(ii = 0; ii < left.cnt || ii == 0; ++ii) { + status = regexec(&myreg, left.cnt? + left.vals[ii]:left.val, + 0, 0, 0); + if(status) { + ret = FALSE; + break; + } + } + free_and_return: + if (left.free_me) + free_val(&left); + return ret; + } + + case OR_OP: + ret = check_syn(cur->vtw_node_left) || + check_syn(cur->vtw_node_right); + return ret; + case AND_OP: + ret = check_syn(cur->vtw_node_left) && + check_syn(cur->vtw_node_right); + return ret; + case NOT_OP: + ret = check_syn(cur->vtw_node_left); + return !ret; + + case COND_OP: /* aux field specifies cond type (GT, GE, etc.)*/ + ret = check_comp(cur); + return ret; + + case VAL_OP: + printf("VAL op in check_syn\n"); + exit(-4); + case VAR_OP: + printf("VAR op in check_syn\n"); + exit(-4); + default: + printf("unknown op %d in check_syn\n", cur->vtw_node_oper); + exit(-4); + } +} + +/************************************************* + copy_path: + copy path + if destination path owns memory, free it +**************************************************/ +static void copy_path(vtw_path *to, vtw_path *from) +{ + if (to->path_buf) + my_free(to->path_buf); + if (to->path_ends) + my_free(to->path_ends); + *to = *from; + to->path_buf = (char *) my_malloc(from->path_alloc+2, "copy_path1"); + memcpy(to->path_buf, from->path_buf, to->path_alloc + 1); + to->path = to->path_buf + (from->path-from->path_buf); + to->path_ends = (int *) my_malloc(to->path_ends_alloc * sizeof(int), + "copy_path2"); + memcpy(to->path_ends, from->path_ends, + to->path_ends_alloc * sizeof(int)); +} + +/***************************************************** + eval_va: + converts VAR_OP or VAL_OP node into valstruct + in case of VAR_OP we need to find corresponding + template node to obtain type. + +*****************************************************/ +static int eval_va(valstruct *res, vtw_node *node) +{ + char *cp=NULL; + char *pathp=NULL; + int status=0; + + switch (node->vtw_node_oper) { + case VAR_OP: + + { + char *endp = 0; + clind_path_ref n_cfg_path=NULL; + clind_path_ref n_tmpl_path=NULL; + clind_path_ref n_cmd_path=NULL; + + pathp = node->vtw_node_string; + + assert(pathp[0]=='$' && pathp[1]=='('); + pathp += 2; + + if(pathp[0] == '@' && pathp[1]!='@'){ + /* this is why we passed at_val all around */ + *res = validate_value_val; + res->free_me = FALSE; + return 0; + } + + memset(res,0,sizeof(*res)); + + if ((endp = strchr(pathp, ')')) == NULL) { + printf("invalid VAR_OP [%s]\n", node->vtw_node_string); + return VTWERR_BADPATH; + } + + *endp = 0; + + if(set_reference_environment(pathp, + &n_cfg_path, + &n_tmpl_path, + &n_cmd_path, + is_in_delete_action())==0) { + + clind_val cv; + + memset(&cv,0,sizeof(cv)); + + status=clind_config_engine_apply_command_path(n_cfg_path, + n_tmpl_path, + n_cmd_path, + FALSE, + &cv, + get_cdirp(), + get_tdirp(), + FALSE); + + if(status==0) { + if(cv.value) { + res->val_type = cv.val_type; + res->free_me = TRUE; + res->val = cv.value; + } + } + } + + if(n_cfg_path) clind_path_destruct(&n_cfg_path); + if(n_tmpl_path) clind_path_destruct(&n_tmpl_path); + if(n_cmd_path) clind_path_destruct(&n_cmd_path); + + *endp = ')'; + + return status; + } + + case VAL_OP: + *res = node->vtw_node_val; + res->free_me = FALSE; + return 0; + case B_QUOTE_OP: + { + FILE *f; + int a_len, len, rd; + + status = expand_string(node->vtw_node_string); + if (status != VTWERR_OK) { + return FALSE; + } + f = popen(exe_string, "r"); + if (!f) + return -1; +#define LEN 24 + len = 0; + cp = my_malloc(LEN,""); + a_len = LEN; + for(;;){ + rd = fread(cp + len, 1, a_len - len , f); + len += rd; + if (len < a_len) + break; + cp = my_realloc(cp, a_len+LEN, ""); + a_len += LEN; + } + cp[len] = 0; + pclose(f); + memset(res, 0, sizeof (*res)); + res->val_type = TEXT_TYPE; + res->free_me = TRUE; + res->val = cp; + } + return 0; + default: + return 0; + } +} + +/********************************************************** + expand_string: + expand string replacing var references with the appropriate + values, the formed string is collected in the buffer pointed + at by the global exe_string. The buffer dynamically allocated + and reallocated. +***********************************************************/ +static int expand_string(char *stringp) +{ + char *scanp; + char *resp = exe_string; + int left = exe_string_len; + int my_len; + int len; + + scanp = stringp; /* save stringp for printf */ + + do{ + + if (left <= 1){ + my_len = resp - exe_string; + exe_string_len += EXE_STRING_DELTA; + exe_string = my_realloc(exe_string, exe_string_len, "expand_string 1"); + left += EXE_STRING_DELTA; + resp = exe_string + my_len; + /* back in business */ + } + if (*scanp != '$') { + if(*scanp == '\\' && scanp[1] == '$') { + /* escaped $, treat as regular char */ + ++scanp; + } + *resp++ = *scanp++; + --left; + } else { + + char *cp=NULL; + boolean my_cp=FALSE; + + if (scanp[1] != '('){ + + printf("Badly formed var reference in %s\n", stringp); + exit (VTWERR_BADPATH); + + } else if(scanp[2] == '@' && scanp[3] == ')') { + + cp = get_at_string(); + my_cp = FALSE; + scanp += 4; + + } else { + + clind_path_ref n_cfg_path=NULL; + clind_path_ref n_tmpl_path=NULL; + clind_path_ref n_cmd_path=NULL; + + char *endp; + + endp = strchr(scanp, ')'); + if (!endp ){ + return -1; + } + + scanp += 2; + /* path reference */ + *endp = 0; + if (endp == scanp) + bye("Empty path"); + + if(set_reference_environment(scanp, + &n_cfg_path, + &n_tmpl_path, + &n_cmd_path, + is_in_delete_action())==0) { + + clind_val cv; + + memset(&cv,0,sizeof(cv)); + + if(clind_config_engine_apply_command_path(n_cfg_path, + n_tmpl_path, + n_cmd_path, + FALSE, + &cv, + get_cdirp(), + get_tdirp(), + FALSE)==0) { + cp=cv.value; + + } + + } + + if(n_cfg_path) clind_path_destruct(&n_cfg_path); + if(n_tmpl_path) clind_path_destruct(&n_tmpl_path); + if(n_cmd_path) clind_path_destruct(&n_cmd_path); + + if(!cp) { + cp=""; + } else { + my_cp=TRUE; + } + + *endp = ')'; + + scanp = strchr(scanp, ')') + 1; + } + len = strlen(cp); + while(len + 1 > left) { /* 1 for termination */ + my_len = resp - exe_string; + exe_string_len += EXE_STRING_DELTA; + exe_string = my_realloc(exe_string, exe_string_len, "expand_string 2"); + left += EXE_STRING_DELTA; + resp = exe_string + my_len; + /* back in business */ + } + + strcpy(resp, cp); + if(my_cp && cp) free(cp); + resp += len; + left -= len; + } + + } while(*scanp); + + *resp = 0; + + return VTWERR_OK; +} + +/***************************************************** + free_sorted: + free all memory allocated to sorted +*****************************************************/ +void free_sorted(vtw_sorted *sortp) +{ + if(sortp->ptrs) + my_free(sortp->ptrs); + if (sortp->parts) + my_free(sortp->parts); +} + + +/***************************************************** + free_def: + free all memory allocated to def +*****************************************************/ +void free_def(vtw_def *defp) +{ + vtw_act_type act; + for(act=0; act<top_act; ++ act) + if (defp->actions[act].vtw_list_head) + free_node_tree(defp->actions[act].vtw_list_head); + if (defp->def_type_help) + my_free(defp->def_type_help); + if (defp->def_default) + my_free(defp->def_default); +} + +/***************************************************** + free_node - add node to free list +*****************************************************/ +static void free_node(vtw_node *node) +{ + --node_cnt; + ++free_node_cnt; + node->vtw_node_left = vtw_free_nodes; + vtw_free_nodes = node; +} + +/***************************************************** + free_node_tree - add all nodes of the tree to free list +*****************************************************/ +static void free_node_tree(vtw_node *node) +{ + if (node->vtw_node_left) + free_node_tree(node->vtw_node_left); + if (node->vtw_node_right) + free_node_tree(node->vtw_node_right); + if (node->vtw_node_string) + free_string(node->vtw_node_string); + if (node->vtw_node_val.free_me) + free_val(&(node->vtw_node_val)); + free_node(node); +} + +static void free_path(vtw_path *path) +{ + if (path->path_ends) + my_free(path->path_ends); + if (path->path_buf) { + my_free(path->path_buf); + } +} + +static void free_reuse_list() +{ + vtw_node *next; + int cnt = 0; + + while (vtw_free_nodes) { + next = vtw_free_nodes->vtw_node_left; + my_free(vtw_free_nodes); + ++cnt; + vtw_free_nodes = next; + --free_node_cnt; + } +#if DEBUG + printf("%d nodes used\n", cnt); +#endif +} + +/***************************************************** + free_val - dealloc allocated memory of valstruct +*****************************************************/ +void free_val(valstruct *val) +{ + int cnt; + + assert(val->free_me); + if (val->val) + my_free(val->val); + for (cnt = 0; cnt < val->cnt; ++ cnt) + my_free(val->vals[cnt]); + if(val->vals) + my_free(val->vals); +} +/***************************************************** + free_string - dealloc string + just free for now, we might do something else later +*****************************************************/ +static void free_string(char *str) +{ + my_free(str); +} + + +/***************************************************** + get_node - take node from free list or allocate +*****************************************************/ +static vtw_node * get_node(void) +{ + vtw_node *ret; + if (vtw_free_nodes){ + ret = vtw_free_nodes; + vtw_free_nodes = vtw_free_nodes->vtw_node_left; + --free_node_cnt; + } else { + ret = my_malloc(sizeof(vtw_node), "New node"); + } + ++node_cnt; + memset(ret, 0, sizeof(vtw_node)); + return ret; +} + +/**************************************************** + get_value: + for a given path (*path) verify that value exists, + open it and read it. The pointer to allocated + memory is returned in valpp. It is responsibility + of a caller to release memory. + Returns: +******************************************************/ +int get_value(char **valpp, vtw_path *pathp) +{ + struct stat statbuf; + int status = VTWERR_OK; + char const *err = NULL; + FILE *in = NULL; + char *valp; + int readcnt; + + + /* find value */ + *valpp = 0; + push_path(pathp, val_name); + + if (lstat(pathp->path, &statbuf) < 0) { + err = "no value file in [%s]\n"; + goto bad_path; + } + if ((statbuf.st_mode & S_IFMT) != S_IFREG) { + err = "no value file in [%s]\n"; + goto bad_path; + } + in = fopen(pathp->path, "r"); + if (!in) { + err = "Can not open value file in [%s]\n"; + goto bad_path; + } + valp = my_malloc(statbuf.st_size + 1, "get_value"); + readcnt = fread(valp, 1, statbuf.st_size, in); + if (readcnt != statbuf.st_size) { + my_free(valp); + cli_val_alloc = 0; + err = "Error reading value file in [%s]\n"; + goto bad_path; + } + valp[statbuf.st_size] = 0; + /* remove \n at the line end */ + if (valp[statbuf.st_size - 1] == '\n') + valp[statbuf.st_size - 1] = 0; + *valpp = valp; + status = 0; +pop: + if (in) + fclose(in); + pop_path(pathp); + if (err) { + fprintf(stderr, err, pathp->path_buf + m_path.print_offset); + printf(err, pathp->path_buf + m_path.print_offset); + } + return status; + bad_path: + status = VTWERR_BADPATH; + goto pop; +} + +int get_value_to_at_string(vtw_path *pathp) { + + char* at=NULL; + int status = get_value(&at,pathp); + + if(status==0) { + set_at_string(at); + } else { + set_at_string(NULL); + } + + return status; +} + +/***************************************************** + out_of_memeory + print out of memory message and exit +*****************************************************/ +void out_of_memory() +{ + printf("\n\t!!! OUT OF MEMORY !!!\n"); + exit(-1); +} + +/************************************************* + init_path: + init path, exit if not able (out_of_memory) +**************************************************/ +void init_path(vtw_path *path, const char *root) +{ + long path_len; + memset(path, 0, sizeof(vtw_path)); + path_len = pathconf(root, _PC_PATH_MAX); + if (path_len < 0) + path_len = PATH_DELTA; + path->path_alloc = path_len - 2; + /* 1 byte for null termination, and 1 byte for '/' */ + path->path_buf = path->path = + (char *)my_malloc(path_len, "init_path 1"); + strcpy(path->path, root); + path->path_len = strlen(root); + path->path_ends = (int *)my_malloc(ENDS_ALLOC * sizeof(int *), + "init_path 2"); + path->path_lev = 1; + path->path_ends[0] = path->path_len; + path->path_ends_alloc = ENDS_ALLOC; +} + +/***************************************************** + pop_path - shorten path by one segment +*****************************************************/ + +void pop_path(vtw_path *path) +{ + if (--path->path_lev < 1) { + INTERNAL; + } + path->path_len = path->path_ends[path->path_lev - 1]; + path->path_buf[path->path_len] = 0; +} +void warrant_path(vtw_path *path, int len) +{ + int delta = path->path - path->path_buf; + + while(path->path_alloc - path->path_len < len + 1){ + path->path_buf = (char *)my_realloc(path->path_buf, + path->path_alloc + + PATH_DELTA, "push_path 1"); + path->path_alloc += PATH_DELTA; + } + path->path = path->path_buf + delta; +} +/***************************************************** + push_path - extend path by '/' and one new segment +*****************************************************/ +void push_path(vtw_path *path, char *segm) +{ + int len; + char *cp; + char *pp; + + for(cp=segm, len=0;*cp;++cp, ++len) + if(*cp=='%' || *cp=='/') + len +=2; + warrant_path(path, len + 1); + path->path_buf[path->path_len] = '/'; + path->path_buf[++path->path_len] = 0; + for(pp=path->path_buf + path->path_len,cp=segm; + *cp;++cp, ++pp) + if(*cp=='%') { + pp[0] = '%'; + pp[1] = '2'; + pp[2] = '5'; + pp += 2; + }else if (*cp == '/') { + pp[0] = '%'; + pp[1] = '2'; + pp[2] = 'F'; + pp += 2; + }else + *pp = *cp; + *pp = 0; + + path->path_len += len; + if (path->path_lev == path->path_ends_alloc){ + path->path_ends_alloc += ENDS_ALLOC; + path->path_ends = (int *)my_realloc(path->path_ends, + sizeof(int *)*path->path_ends_alloc, "puhs_path 2"); + } + path->path_ends[path->path_lev++] = path->path_len; +} + +/**************************************************** + scan_ipv6: + scans ipv6 or ipv6net pointed by val + and returns as array of integers pointed + by parts +***************************************************/ +static void scan_ipv6(char *val, unsigned int *parts) +{ + int num = 0; + int num_cnt = 0; + int dot_dot_pos = -1; + int dot_cnt = 0; + char c; + char *p; + int base = 16; + int total = 8; + int gap; + + for (p = val;TRUE; ++p) { + switch ((c = *p)) { + case '.': + if (dot_cnt == 0) { + /* turn out it was decimal, convert our wrong + hex interpretation; + decimal may not have more than 3 digits */ + num = (num/256)*100 + (num%256)/16*10 + num%16; + base = 10; + } + ++dot_cnt; + break; + case ':': + if (p[1] == ':'){ + ++p; + dot_dot_pos = num_cnt + 1; + } + break; + case '/': + base = 10; + total = 9; + break; + case 0: + break; + default: + /* must be a digit */ + c = tolower(*p); + if (isdigit(c)) + num = num * base + c - '0'; + else + num = num * base + c - 'a' + 10; + continue; + } + /* close the number */ + /* the case of "::234: etc " + handled automatically as 0::234 + with allowing :: to represent 0 or more + groups instead of 1 or more */ + + parts[num_cnt] = num; + num = 0; + ++num_cnt; + /* combine two decimal if needed */ + if (dot_cnt == 2 || (dot_cnt == 3 && (c == 0 || c == '/'))) { + --num_cnt; + parts[num_cnt - 1] = parts[num_cnt - 1] * 256 + parts[num_cnt]; + } + if (*p == 0) + break; + } + /* replace '::' with 0s */ + if (dot_dot_pos != -1 && total != num_cnt) { + int i; + gap = total - num_cnt; + if (dot_dot_pos != num_cnt) + memmove(parts+dot_dot_pos+gap, parts+dot_dot_pos, + (num_cnt-dot_dot_pos)*sizeof(int)); + for (i = 0; i<gap; ++i) + parts[dot_dot_pos+i] = 0; + } +} + +/*************************************************** + switch_path: + switch m_path between mcd and ccd directories - + modified (UNIONFS) configuration and changed + (real) configuration +****************************************************/ +void switch_path(first_seg *segp) +{ + memcpy(m_path.path_buf + segp->f_segoff, segp->f_segp, + segp->f_seglen); + m_path.path = m_path.path_buf + segp->f_segoff; +} + +/************************************************* + validate_value: + validates value against type and syntax + return TRUE if OK, FALSE otherwise +**************************************************/ +boolean validate_value(vtw_def *def, char *cp) +{ + int status; + boolean ret=TRUE; + + /* prepare cur_value */ + set_at_string(cp); + status = char2val(def, cp, &validate_value_val); + if (status != VTWERR_OK) + return FALSE; + if ((def->def_type!=ERROR_TYPE) && + (validate_value_val.val_type != def->def_type)) { + if (def->def_type_help){ + (void)expand_string(def->def_type_help); + printf("%s\n", exe_string); + fprintf(stderr, "%s\n", exe_string); + } else { + printf("Incorrect type of %s, need %s\n", + cp, type_to_name(def->def_type)); + } + ret = FALSE; + goto validate_value_free_and_return; + } + ret = TRUE; + if (def->actions && def->actions[syntax_act].vtw_list_head){ + in_validate_val = TRUE; + ret = check_syn(def->actions[syntax_act].vtw_list_head); + in_validate_val = FALSE; + } + validate_value_free_and_return: + free_val(&validate_value_val); + return ret; +} + + +int cli_val_read(char *buf, int max_size) +{ + int len; + + if (cli_val_len > max_size) + len = max_size; + else + len = cli_val_len; + if (len) { + (void)memcpy(buf, cli_val_ptr, len); + cli_val_len -= len; + cli_val_ptr += len; + } + return len; +} +/*==========================================================*/ +/* MEMORY */ +/*==========================================================*/ + +void *my_malloc(size_t size, const char *name) +{ + return malloc(size); +} +void *my_realloc(void *ptr, size_t size, const char *name) +{ + return realloc(ptr, size); +} + +void my_free(void *ptr) +{ + free(ptr); +} + +/************************************************* + my_strdup: + do a strdup, + exit on no memory +**************************************************/ +char *my_strdup(const char *s, const char *name) +{ + return strdup(s); +} + +void done() +{ + free_reuse_list(); + free_path(&t_path); + free_path(&m_path); + if (exe_string) + my_free(exe_string); +} +void mark_paths(vtw_mark *markp) +{ + markp->m_lev = m_path.path_lev; + markp->t_lev = t_path.path_lev; +} +void restore_paths(vtw_mark *markp) +{ + while(markp->m_lev < m_path.path_lev) + pop_path(&m_path); + while(markp->t_lev < t_path.path_lev) + pop_path(&t_path); +} + +void touch() +{ + char *command; + command = my_malloc(strlen(get_mdirp()) + 20, "delete"); + sprintf(command, "touch %s/%s", get_mdirp(), MOD_NAME); + system(command); + free(command); +} + +char *type_to_name(vtw_type_e type) { + switch(type) { + case INT_TYPE: return("u32"); + case IPV4_TYPE: return("ipv4"); + case IPV4NET_TYPE: return("ipv4net"); + case IPV6_TYPE: return("ipv6"); + case IPV6NET_TYPE: return("ipv6net"); + case MACADDR_TYPE: return("macaddr"); + case DOMAIN_TYPE: return("domain"); + case TEXT_TYPE: return("text"); + case BOOL_TYPE: return("bool"); + default: return("unknown"); + } +} + +#if 1 +void +dump_log(int argc, char **argv) +{ +} +#else +void dump_log(int argc, char **argv) +{ + int i; + int len; + char *cp; + + len = 0; + for (i=0; i<argc;++i) + len += strlen(argv[i]); + len += argc; + cp = my_malloc(len, "dump_log"); + len = 0; + for (i=0; i<argc;++i){ + strcpy(cp+len, argv[i]); + len += strlen(argv[i]); + cp[len]= ' '; + ++len; + } + cp[len-1]=0; + printf("Command: %s\n",cp); + my_free(cp); +} +#endif + +/********************* New Dir ****************************/ + +static int set_reference_environment(const char* var_reference, + clind_path_ref *n_cfg_path, + clind_path_ref *n_tmpl_path, + clind_path_ref *n_cmd_path, + int active) { + + if(var_reference && n_cfg_path && n_tmpl_path && n_cmd_path) { + + if(*var_reference=='/') { + + if(is_in_delete_action()) { + *n_cfg_path=clind_path_construct(get_adirp()); + } else { + *n_cfg_path=clind_path_construct(get_mdirp()); + } + *n_tmpl_path=clind_path_construct(get_tdirp()); + *n_cmd_path=clind_path_construct(var_reference); + + } else { + + vtw_path n_vt_path; + + memset(&n_vt_path,0,sizeof(n_vt_path)); + + copy_path(&n_vt_path, &m_path); + /* switch to MPATH */ + memcpy(n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff, get_f_seg_m_ptr()->f_segp, + get_f_seg_m_ptr()->f_seglen); + n_vt_path.path = n_vt_path.path_buf + get_f_seg_m_ptr()->f_segoff; + + if(active) { + + vtw_path active_path; + + memset(&active_path,0,sizeof(active_path)); + + copy_path(&active_path, &n_vt_path); + + memcpy(active_path.path_buf + get_f_seg_a_ptr()->f_segoff, get_f_seg_a_ptr()->f_segp, + get_f_seg_a_ptr()->f_seglen); + active_path.path = active_path.path_buf + get_f_seg_a_ptr()->f_segoff; + + *n_cfg_path=clind_path_construct(active_path.path); + + } else { + + *n_cfg_path=clind_path_construct(n_vt_path.path); + + } + + *n_tmpl_path=clind_path_construct(t_path.path); + *n_cmd_path=clind_path_construct(var_reference); + + } + + return 0; + + } else { + + return -1; + + } +} + +/**********************************************************/ diff --git a/src/cli_objects.c b/src/cli_objects.c new file mode 100644 index 0000000..9bb36f0 --- /dev/null +++ b/src/cli_objects.c @@ -0,0 +1,343 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include "cli_val.h" +#include "cli_parse.h" +#include <regex.h> + +#include "cli_val_engine.h" + +#include "cli_objects.h" + +/************************ Storage area: *****************/ + +static char *at_string=NULL; +static boolean in_delete_action=FALSE; +static valstruct cli_value; +static boolean in_commit=FALSE; /* TRUE if in commit program*/ +static boolean in_exec=FALSE; /* TRUE if in exec */ +static boolean _is_echo=FALSE; +static boolean _is_silent_msg=FALSE; +static first_seg f_seg_a; +static first_seg f_seg_c; +static first_seg f_seg_m; +static int in_cond_tik=0; + +/******************** Accessors: ************************/ + +static char at_buffer[1024]={0}; + +/* the string to use as $(@), must be set + before call to expand_string */ +char* get_at_string(void) { + if(at_string) { + return at_string; + } else { + return at_buffer; + } +} + +void set_at_string(char* s) { + if(s!=at_buffer) { + at_string=s; + } else { + at_string=NULL; + } +} + +void free_at_string(void) { + if(at_string) { + if(at_string!=at_buffer) free(at_string); + at_string=NULL; + } +} + +boolean is_in_delete_action(void) { + return in_delete_action; +} + +void set_in_delete_action(boolean b) { + in_delete_action=b; +} + +boolean is_in_commit(void) { + return in_commit; +} + +void set_in_commit(boolean b) { + in_commit=b; +} + +boolean is_in_exec(void) { + return in_exec; +} + +void set_in_exec(boolean b) { + in_exec=b; +} + +boolean is_echo(void) { + return _is_echo; +} + +void set_echo(boolean b) { + _is_echo=b; +} + +boolean is_silent_msg(void) { + return _is_silent_msg; +} + +void set_silent_msg(boolean b) { + _is_silent_msg=b; +} + +valstruct* get_cli_value_ptr(void) { + return &cli_value; +} + +first_seg* get_f_seg_a_ptr(void) { + return &f_seg_a; +} + +first_seg* get_f_seg_c_ptr(void) { + return &f_seg_c; +} + +first_seg* get_f_seg_m_ptr(void) { + return &f_seg_m; +} + +int is_in_cond_tik(void) { + return in_cond_tik; +} + +void set_in_cond_tik(int ict) { + in_cond_tik=ict; +} + +void dec_in_cond_tik(void) { + --in_cond_tik; +} + +const char* get_tdirp(void) { + + const char* tdirp=getenv(ENV_T_DIR); + + if (!tdirp) + tdirp = DEF_T_DIR; + if(!tdirp) + tdirp=""; + + return tdirp; +} + +const char* get_cdirp(void) { + return getenv(ENV_C_DIR); +} + +const char* get_adirp(void) { + + const char* adirp=getenv(ENV_A_DIR); + + if (!adirp) + adirp = DEF_A_DIR; + if(!adirp) + adirp=""; + + return adirp; +} + +const char* get_mdirp(void) { + + const char* mdirp=getenv(ENV_M_DIR); + + if(!mdirp) + mdirp=""; + + return mdirp; +} + +const char* get_tmpp(void) { + + const char* tmpp=getenv(ENV_TMP_DIR); + + if(!tmpp) + tmpp=""; + + return tmpp; +} + +char* get_elevp(void) { + + static char elevp_buffer[2049]; + static char* elevp=NULL; + + if(elevp==NULL) { + + const char* tmp=getenv(ENV_EDIT_LEVEL); + + if(tmp) { + strncpy(elevp_buffer,tmp,sizeof(elevp_buffer)-1); + elevp=elevp_buffer; + } + } + + return elevp; +} + +char* get_tlevp(void) { + + static char tlevp_buffer[2049]; + static char* tlevp=NULL; + + if(tlevp==NULL) { + + const char* tmp=getenv(ENV_TEMPLATE_LEVEL); + + if(tmp) { + strncpy(tlevp_buffer,tmp,sizeof(tlevp_buffer)-1); + tlevp=tlevp_buffer; + } + } + + return tlevp; +} + +/************************* Init ***************************/ + +void init_edit() +{ + int elevlen = 0; + int tlevlen = 0; + + init_paths(TRUE); + if (!get_elevp()) + bye("Not in configuration mode"); + if (!get_tlevp()) + bye("INTERNAL: environment var |%s| is not set",ENV_TEMPLATE_LEVEL); + elevlen = strlen(get_elevp()); + tlevlen = strlen(get_tlevp()); + if (elevlen > 0 && get_elevp()[elevlen - 1]=='/') { + /* cut off terminateing slash */ + --elevlen; + get_elevp()[elevlen] = 0; + } + if (elevlen) { + char *slashp; + char * scanp; + if (*get_elevp()!='/') + INTERNAL; + scanp = get_elevp() + 1; + while (TRUE) { + slashp = strchr(scanp, '/'); + if (slashp) + *slashp = 0; + push_path(&m_path, scanp); + if (slashp) { + *slashp = '/'; + scanp = slashp+1; + }else + break; + } + } + switch_path(MPATH); + if (tlevlen > 0 && get_tlevp()[tlevlen - 1]=='/') { + /* cut off terminateing slash */ + --tlevlen; + get_tlevp()[tlevlen] = 0; + } + if (tlevlen) { + char *slashp; + char * scanp; + if (*get_tlevp()!='/') + INTERNAL; + scanp = get_tlevp() + 1; + while (TRUE) { + slashp = strchr(scanp, '/'); + if (slashp) + *slashp = 0; + push_path(&t_path, scanp); + if (slashp) { + *slashp = '/'; + scanp = slashp+1; + }else + break; + } + } +} + +void init_paths(boolean for_commit) +{ + struct stat statbuf; + const char* tdirp = get_tdirp(); + const char* cdirp = get_cdirp(); + const char* adirp = get_adirp(); + const char* mdirp = get_mdirp(); + const char* tmpp = get_tmpp(); + + if (!mdirp || !mdirp[0]) + bye("Environment variable %s for temp configuration is not set",ENV_M_DIR); + if (!cdirp) + bye("INTERNAL: environment var |%s| is not set", + ENV_C_DIR); + if (!tmpp || !tmpp[0]) + bye("INTERNAL: environment var |%s| is not set", + ENV_TMP_DIR); + /* make sure that template root is present */ + + if (lstat(tdirp, &statbuf) < 0) + bye("Template directory |%s| isn't present\n", tdirp); + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + bye("Template directory |%s| isn't a directory\n", tdirp); + /* set paths to current roots */ + if (for_commit) { + int max_len; + const char *startp; + + /* make sure that master configuration root is present */ + if (lstat(adirp, &statbuf) < 0) + bye("Master configuration directory |%s| isn't present\n", adirp); + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + bye("Master configuration directory |%s| isn't a directory\n", adirp); + + get_f_seg_a_ptr()->f_segp = adirp; + get_f_seg_c_ptr()->f_segp = cdirp; + get_f_seg_m_ptr()->f_segp = mdirp; + get_f_seg_a_ptr()->f_seglen = strlen(adirp); + get_f_seg_c_ptr()->f_seglen = strlen(cdirp); + get_f_seg_m_ptr()->f_seglen = strlen(mdirp); + if(get_f_seg_a_ptr()->f_seglen > get_f_seg_m_ptr()->f_seglen) { + max_len = get_f_seg_a_ptr()->f_seglen; + startp = adirp; + }else{ + max_len = get_f_seg_m_ptr()->f_seglen; + startp = mdirp; + } + if(get_f_seg_c_ptr()->f_seglen > max_len) { + max_len = get_f_seg_c_ptr()->f_seglen; + startp = cdirp; + } + get_f_seg_a_ptr()->f_segoff = max_len - get_f_seg_a_ptr()->f_seglen; + get_f_seg_c_ptr()->f_segoff = max_len - get_f_seg_c_ptr()->f_seglen; + get_f_seg_m_ptr()->f_segoff = max_len - get_f_seg_m_ptr()->f_seglen; + init_path(&m_path, startp); + switch_path(get_f_seg_c_ptr()); + m_path.print_offset = max_len; + }else + init_path(&m_path, mdirp); + + init_path(&t_path, tdirp); +} + +/**********************************************************/ diff --git a/src/cli_objects.h b/src/cli_objects.h new file mode 100644 index 0000000..12b6b38 --- /dev/null +++ b/src/cli_objects.h @@ -0,0 +1,65 @@ +#ifndef CLI_OBJ_H +#define CLI_OBJ_H + +#include "cli_val.h" + +#define APATH (get_f_seg_a_ptr()) +#define CPATH (get_f_seg_c_ptr()) +#define MPATH (get_f_seg_m_ptr()) + +/* names of VYATTA env vars */ +#define ENV_EDIT_LEVEL "VYATTA_EDIT_LEVEL" +#define ENV_TEMPLATE_LEVEL "VYATTA_TEMPLATE_LEVEL" +#define ENV_A_DIR "VYATTA_ACTIVE_CONFIGURATION_DIR" +#define ENV_C_DIR "VYATTA_CHANGES_ONLY_DIR" +#define ENV_M_DIR "VYATTA_TEMP_CONFIG_DIR" +#define ENV_T_DIR "VYATTA_CONFIG_TEMPLATE" +#define ENV_TMP_DIR "VYATTA_CONFIG_TMP" +#define DEF_A_DIR "/opt/vyatta/config/active" +#define DEF_T_DIR "/opt/vyatta/share/ofr/template" +#define ENV_OLD_PS1 "VYATTA_OLD_PS1" + +/* the string to use as $(@), must be set + before call to expand_string */ +char* get_at_string(void); +void set_at_string(char* s); +void free_at_string(void); + +boolean is_in_delete_action(void); +void set_in_delete_action(boolean b); + +boolean is_in_commit(void); +void set_in_commit(boolean b); + +boolean is_in_exec(void); +void set_in_exec(boolean b); + +boolean is_echo(void); +void set_echo(boolean b); + +boolean is_silent_msg(void); +void set_silent_msg(boolean b); + +valstruct* get_cli_value_ptr(void); + +first_seg* get_f_seg_a_ptr(void); +first_seg* get_f_seg_c_ptr(void); +first_seg* get_f_seg_m_ptr(void); + +int is_in_cond_tik(void); +void set_in_cond_tik(int ict); +void dec_in_cond_tik(void); + +const char* get_tdirp(void); +const char* get_cdirp(void); +const char* get_adirp(void); +const char* get_mdirp(void); +const char* get_tmpp(void); + +char* get_elevp(void); +char* get_tlevp(void); + +void init_edit(void); +void init_paths(boolean for_commit); + +#endif /* CLI_OBJ_H */ diff --git a/src/cli_parse.y b/src/cli_parse.y new file mode 100644 index 0000000..f0e43a8 --- /dev/null +++ b/src/cli_parse.y @@ -0,0 +1,206 @@ +%{ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "cli_val.h" + +extern int yy_cli_def_lineno; +static vtw_def *parse_defp; +static int parse_status; +static boolean cli_def_type_only=0; /*{ if (cli_def_type_only) YYACCEPT;}*/ +/* XXX: sigh, the -p flag to yacc should do this for us kkkk*/ +#define yystacksize tpltstacksize +#define yysslim tpltsslim + +/* forward prototypes */ +extern int yy_cli_parse_parse(); +extern int yy_cli_def_lex(); +extern void yy_cli_parse_error(const char *); +static void cli_deferror(const char *); + extern FILE *yy_cli_def_in; +#define YYDEBUG 1 +#define yy_cli_parse_lex yy_cli_def_lex +%} +%token EOL +%token MULTI +%token TAG +%token TYPE +%token HELP +%token DEFAULT +%token PATTERN +%token EXEC +%token SYNTAX +%token COMMIT +%token CHECK +%left SEMI +%token <val>VALUE +%token <type>TYPE_DEF +%token <strp>VAR +%token <strp> STRING +%token <strp> EX_STRING +%token SYNTAX_ERROR +%token <action>ACTION +%left ASSIGN +%left OR +%left AND +%right NOT +%left COMMA +%nonassoc <cond>COND +%token RP +%token LP +%type <nodep> val +%type <nodep> exp +%type <val> val0 +%type <nodep> action +%type <nodep> action0 + +%union { + char *strp; + valstruct val; + vtw_type_e type; + vtw_cond_e cond; + vtw_node *nodep; + vtw_act_type action; +} + +%% + +input: tag + | EOL input + | tag otherinput + ; + +otherinput: type EOL + | cause EOL + | otherinput type EOL + | otherinput cause EOL + | otherinput EOL + | syntax_error + ; + +tag: /* empty */ + | TAG EOL {parse_defp->tag = TRUE;} + | MULTI EOL {parse_defp->multi = TRUE;} + ; + +type: TYPE TYPE_DEF SEMI STRING + { parse_defp->def_type = $2; + parse_defp->def_type_help = $4; } + ; + + +type: TYPE TYPE_DEF + { parse_defp->def_type = $2; + } + ; + +cause: help_cause + | default_cause + | syntax_cause + | ACTION action { append(parse_defp->actions + $1, $2, 0);} + ; + +help_cause: HELP STRING + { parse_defp->def_type_help = $2; /* no semantics for now */ + } + +default_cause: DEFAULT VALUE + { + if ($2.val_type != parse_defp->def_type) + yy_cli_parse_error((const char *)"Bad default\n"); + parse_defp->def_default = $2.val; + } +default_cause: DEFAULT STRING + { + if (TEXT_TYPE != parse_defp->def_type) + yy_cli_parse_error((const char *)"Bad default\n"); + parse_defp->def_default = $2; + } + +syntax_cause: SYNTAX exp {append(parse_defp->actions + syntax_act, $2, 0);} + ; + +syntax_cause: COMMIT exp {append(parse_defp->actions + syntax_act, $2, 1);} + ; + +action0: STRING { $$ = make_node(EXEC_OP, make_str_node($1),NULL);} + ; +action: action0 + | action0 SEMI STRING + {$$ = make_node(HELP_OP, $1, make_str_node($3));} + | exp + ; +exp: LP exp RP + {$$=$2;} + | exp AND exp {$$ = make_node(AND_OP,$1,$3);} + | exp OR exp {$$ = make_node(OR_OP,$1,$3);} + | val COND val + {$$ = make_node(COND_OP,$1,$3);$$->vtw_node_aux = $2;} + | PATTERN VAR STRING + { $$ = make_node(PATTERN_OP,make_var_node($2), + make_str_node($3));} + | EXEC STRING + { $$ = make_node(EXEC_OP,make_str_node($2),NULL);} + | NOT exp {$$ = make_node(NOT_OP,$2,NULL);} + | exp SEMI STRING + {$$ = make_node(HELP_OP, $1, make_str_node($3));} + | VAR ASSIGN val + {$$ = make_node(ASSIGN_OP, make_var_node($1), $3);} + ; + +val: VAR {$$ = make_var_node($1);} + | val0 {$$ = make_val_node(&($1));} + | EX_STRING {$$=make_str_node0($1, B_QUOTE_OP);} + ; + +val0: VALUE + + | val0 COMMA val0 { add_val(&($1), &($3)); $$=$1; } + + | STRING {$$ = str2val($1);} + ; + +syntax_error: SYNTAX_ERROR { + cli_deferror("syntax error"); + } + ; + + +%% +char *parse_path; +int parse_def(vtw_def *defp, char *path, boolean type_only) +{ + int status; + yy_cli_def_lineno = 1; + parse_status = 0; + parse_defp = defp; + cli_def_type_only = type_only; + yy_cli_def_in = fopen(path, "r"); +#if 0 + yy_cli_parse_debug = 1; +#endif + if (!yy_cli_def_in) + return -5; + parse_path = path; + status = yy_cli_parse_parse(); /* 0 is OK */ + fclose(yy_cli_def_in); + return status; +} +static void +cli_deferror(const char *s) +{ + printf("Error: %s in file %s, line %d\n",s, parse_path, + yy_cli_def_lineno); +} + +void yy_cli_parse_error(const char *s) +{ + cli_deferror(s); +} + + + + + diff --git a/src/cli_path_utils.c b/src/cli_path_utils.c new file mode 100644 index 0000000..4045516 --- /dev/null +++ b/src/cli_path_utils.c @@ -0,0 +1,535 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli path-handling utilities + + **** End License **** + +*************************************************************************/ + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <regex.h> +#include <dirent.h> + +#include <string.h> + +#include "cli_path_utils.h" + +/********************* + * Data definitions + * + *********************/ + +typedef char* clind_dir_name; + +/** + * Definition of the path structure to hold all path-like information: + */ + +struct _clind_path_impl { + + int absolute; + int path_len; + char* path_string; + clind_dir_name* path; + +}; + +/****************************** + * Path utils. We use them + * to manipulate the path-like + * structures. + * + ******************************/ + +static void clind_reset_path_string(clind_path_impl* obj) { + + char* newpath=NULL; + + if(obj->path_len<1) { + + newpath=strdup(""); + + } else { + + int i=0; + + if(!obj->absolute || (strlen(obj->path[0])>0 && ((char*)(obj->path[0]))[0]=='/')) { + newpath=strdup(obj->path[0]); + } else { + newpath=(char*)malloc(strlen(obj->path[0])+1+1); + newpath[0]='/'; + strcpy(newpath+1,(char*)(obj->path[0])); + } + + for(i=1;i<obj->path_len;i++) { + newpath=(char*)realloc(newpath,strlen(newpath)+1+strlen(obj->path[i])+1); + strcpy(newpath+strlen(newpath),"/"); + strcpy(newpath+strlen(newpath),obj->path[i]); + } + } + + if(obj->path_string==NULL) { + obj->path_string=newpath; + } else { + obj->path_string=(char*)realloc(obj->path_string,strlen(newpath)+1); + strcpy(obj->path_string,newpath); + free(newpath); + } +} + +clind_path_ref clind_path_construct(const char* path) { + + if(!path) return NULL; + else { + + const char* delim="/ \t"; + + clind_path_impl *obj = (clind_path_impl*)malloc(sizeof(clind_path_impl)); + char* tokpath=strdup(path); + char* token=strtok(tokpath,delim); + + obj->path_len=0; + obj->path_string=strdup(""); + obj->path=NULL; + + while(token) { + clind_path_push((clind_path_ref)obj,token); + token=strtok(NULL,delim); + } + + free(tokpath); + + obj->absolute=(*path=='/'); + + clind_reset_path_string(obj); + + return (clind_path_ref)obj; + } +} + +void clind_path_destruct(clind_path_ref* path) { + + if(path && *path) { + + clind_path_impl* obj = (clind_path_impl*)(*path); + + if(obj->path_string) { + free(obj->path_string); + obj->path_string=NULL; + } + + if(obj->path) { + while(obj->path_len>0) { + char* dir_name = (char*)(obj->path[obj->path_len-1]); + if(dir_name) { + free(dir_name); + } + obj->path_len--; + } + free(obj->path); + obj->path=0; + } + + *path=0; + } +} + +clind_path_ref clind_path_clone(const clind_path_ref path) { + + clind_path_ref ret=NULL; + if(path) { + + clind_path_impl* obj = (clind_path_impl*)path; + + ret = clind_path_construct(obj->path_string); + + if(ret) { + + ((clind_path_impl*)ret)->absolute=obj->absolute; + + clind_reset_path_string((clind_path_impl*)ret); + } + } + + return ret; +} + +int clind_path_get_size(clind_path_ref path) { + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + return obj->path_len; + } + return 0; +} + +const char* clind_path_get_path_string(clind_path_ref path) { + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + if(obj->path_string) { + return obj->path_string; + } + } + return ""; +} + +int clind_path_is_absolute(clind_path_ref path) { + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + return obj->absolute; + } + return 0; +} + +void clind_path_push(clind_path_ref path,const char* dir) { + + if(path && dir && *dir) { + + clind_path_impl* obj = (clind_path_impl*)path; + int absolute=(*dir=='/'); + + while(*dir && *dir=='/') dir++; + + if(obj->path_len<=0) { + + obj->path_len=1; + obj->absolute=absolute; + + if(obj->path) { + free(obj->path); + } + + obj->path=(clind_dir_name*)malloc(sizeof(clind_dir_name)); + obj->path[0]=(clind_dir_name)strdup(dir); + + } else { + + obj->path_len++; + + obj->path=(clind_dir_name*)realloc(obj->path, + sizeof(clind_dir_name)*(obj->path_len)); + obj->path[obj->path_len-1]=strdup(dir); + } + + clind_reset_path_string(obj); + } +} + +char* clind_path_pop_string(clind_path_ref path) { + + char* ret=NULL; + + if(path) { + + clind_path_impl* obj = (clind_path_impl*)path; + + if(obj->path_len<=0) { + return ret; + } + + obj->path_len--; + + if(obj->path) { + if(obj->path[obj->path_len]) { + ret = obj->path[obj->path_len]; + obj->path[obj->path_len]=NULL; + } + } + + if(obj->path_len<1) { + obj->absolute=0; + } + + clind_reset_path_string(obj); + } + + return ret; +} + +int clind_path_pop(clind_path_ref path) { + + int ret=-1; + + char* ps = clind_path_pop_string(path); + + if(ps) { + free(ps); + ret=0; + } + + return ret; +} + +const char* clind_path_last_string(clind_path_ref path) { + + char* ret=NULL; + + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + if(obj->path && obj->path_len>0) { + ret=obj->path[obj->path_len-1]; + } + } + + return ret; +} + +void clind_path_unshift(clind_path_ref path,const char* dir) { + + if(path && dir && *dir) { + + clind_path_impl* obj = (clind_path_impl*)path; + + int absolute=(*dir=='/'); + + while(*dir && *dir=='/') dir++; + + if(obj->path_len<=0) { + + clind_path_push(path,dir); + + } else { + + obj->path_len++; + + obj->path=(clind_dir_name*)realloc(obj->path, + sizeof(clind_dir_name)*(obj->path_len)); + memmove((char*)(obj->path)+sizeof(clind_dir_name),(char*)(obj->path), + sizeof(clind_dir_name)*(obj->path_len-1)); + obj->path[0]=strdup(dir); + + } + + obj->absolute=absolute; + + clind_reset_path_string(obj); + } +} + +const char* clind_path_get_string(clind_path_ref path,int index) { + + const char* ret=NULL; + + if(path) { + clind_path_impl* obj = (clind_path_impl*)path; + if(obj->path && obj->path_len>index) { + ret=obj->path[index]; + } + } + + return ret; +} + +const char* clind_path_first_string(clind_path_ref path) { + return clind_path_get_string(path,0); +} + +char* clind_path_shift_string(clind_path_ref path) { + + char* ret=NULL; + + if(path) { + + clind_path_impl* obj = (clind_path_impl*)path; + + if(obj->path_len<=0) { + return ret; + } + + obj->path_len--; + + if(obj->path) { + if(obj->path[0]) { + ret = obj->path[0]; + obj->path[0]=NULL; + } + + memmove((char*)(obj->path),(char*)(obj->path)+sizeof(clind_dir_name), + sizeof(clind_dir_name)*obj->path_len); + } + + obj->absolute=0; + + clind_reset_path_string(obj); + } + + return ret; +} + +int clind_path_shift(clind_path_ref path) { + + int ret=-1; + + char* ps = clind_path_shift_string(path); + + if(ps) { + free(ps); + ret=0; + } + + return ret; +} + +void clind_path_debug_print(clind_path_ref path) { + + if(path) { + + int i=0; + clind_path_impl* obj = (clind_path_impl*)path; + + if(obj->path_string) { + printf("obj->path_string=%s, obj->path_len=%d,obj->absolute=%d\n", + obj->path_string,obj->path_len,obj->absolute); + } else { + printf("obj->path_string=NULL, obj->path_len=%d,obj->absolute=%d\n", + obj->path_len,obj->absolute); + } + + if(obj->path) { + for(i=0;i<obj->path_len;i++) { + if(obj->path[i]) { + printf(" obj->path[%d]=%s\n",i,obj->path[i]); + } else { + printf(" obj->path[%d]=NULL\n",i); + } + } + } else { + printf(" obj->path=NULL\n"); + } + } +} + +int clind_file_exists(const char* dir,const char* file) { + + int ret=0; + + if(file) { + + char* fname=strdup(file); + struct stat statbuf; + + if(dir) { + free(fname); + fname=(char*)malloc(strlen(dir)+1+strlen(file)+1); + strcpy(fname,dir); + strcpy(fname+strlen(fname),"/"); + strcpy(fname+strlen(fname),file); + } + + if (lstat(fname, &statbuf) == 0) { + ret=1; + } + + free(fname); + } + + return ret; +} + +char *clind_unescape(const char *name) +{ + const char *cp; + char *rcp, *ret; + char len; + + for(cp=name, len=0;*cp;++cp, ++len) + if(*cp=='%') + cp +=2; + rcp = ret = malloc(len+1); + for(cp=name, len=0;*cp;++cp, ++rcp) + if(*cp=='%') { + ++cp; + if (*cp >='a' && *cp<='f') + *rcp = (*cp-'a'+10)*16; + else if (*cp >='A' && *cp<='F') + *rcp = (*cp-'A'+10)*16; + else if (*cp >='0' && *cp<='9') + *rcp = (*cp-'0')*16; + else { + printf("Bad escape in |%s|\n", name); + exit(-1); + } + ++cp; + if (*cp >='a' && *cp<='f') + *rcp += (*cp-'a'+10); + else if (*cp >='A' && *cp<='F') + *rcp += (*cp-'A'+10); + else if (*cp >='0' && *cp<='9') + *rcp += (*cp-'0'); + else { + printf("Bad escape in |%s|\n", name); + exit(-1); + } + }else + *rcp = *cp; + *rcp = 0; + return ret; +} + +char* clind_quote(const char* s) { + + char* ret=NULL; + if(s) { + int i=0; + int len=strlen(s); + int sz=0; + char SQ='\''; + + ret=(char*)malloc(1+5*len+1+1+10); + ret[sz++]=SQ; + + for(i=0;i<len;i++) { + if(s[i]==SQ) { + ret[sz++]=SQ;/*'\''*/ + ret[sz++]='\\'; + ret[sz++]=SQ; + ret[sz++]=SQ; + } else { + ret[sz++]=s[i]; + } + } + + ret[sz++]=SQ; + ret[sz]=0; + } + return ret; +} + + diff --git a/src/cli_path_utils.h b/src/cli_path_utils.h new file mode 100644 index 0000000..6cc3be8 --- /dev/null +++ b/src/cli_path_utils.h @@ -0,0 +1,72 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli path-handling utilities + + **** End License **** + +*************************************************************************/ + +#if !defined(__CLI_PATH_UTILS__) +#define __CLI_PATH_UTILS__ + +/******************* + * Type definitions + * + *******************/ + +typedef struct _clind_path_impl clind_path_impl; +typedef clind_path_impl* clind_path_ref; + +/************************ + * Path utils + * + ************************/ + +clind_path_ref clind_path_construct(const char* path); +void clind_path_destruct(clind_path_ref* path); +clind_path_ref clind_path_clone(const clind_path_ref path); + +int clind_path_get_size(clind_path_ref path); +const char* clind_path_get_path_string(clind_path_ref path); +void clind_path_debug_print(clind_path_ref path); +int clind_path_is_absolute(clind_path_ref path); + +int clind_path_pop(clind_path_ref path); +char* clind_path_pop_string(clind_path_ref path); +const char* clind_path_last_string(clind_path_ref path); +void clind_path_push(clind_path_ref path,const char* dir); + +int clind_path_shift(clind_path_ref path); +char* clind_path_shift_string(clind_path_ref path); +const char* clind_path_get_string(clind_path_ref path,int index); +const char* clind_path_first_string(clind_path_ref path); +void clind_path_unshift(clind_path_ref path,const char* dir); + +int clind_file_exists(const char* dir,const char* file); + +char *clind_unescape(const char *name); +char* clind_quote(const char* s); + +#endif /* __CLI_PATH_UTILS__ */ diff --git a/src/cli_val.h b/src/cli_val.h new file mode 100644 index 0000000..8a98a88 --- /dev/null +++ b/src/cli_val.h @@ -0,0 +1,206 @@ +#ifndef CLI_DEF_H +#define CLI_DEF_H + +#define BITWISE 0 /* no partial commit */ + +#define boolean int +#define TRUE 1 +#define FALSE 0 + +/* allocation unit for vals in valstruct */ +#define MULTI_ALLOC 5 /* we have room if cnt%MULTI_ALLOC != 0 */ + +typedef enum { + do_del_mode, + del_mode, + create_mode, + update_mode +}vtw_cmode; +typedef enum { + ERROR_TYPE, + INT_TYPE, + IPV4_TYPE, + IPV4NET_TYPE, + IPV6_TYPE, + IPV6NET_TYPE, + MACADDR_TYPE, + DOMAIN_TYPE, /*end of addr types */ + TEXT_TYPE, + BOOL_TYPE +}vtw_type_e; + +typedef enum { + EQ_COND = 1, + NE_COND, + LT_COND, + LE_COND, + GT_COND, + GE_COND, + IN_COND, + TOP_COND +}vtw_cond_e; +/* IN_COND is like EQ for singular compare, but OR for multivalue right operand */ + +typedef enum { + LIST_OP, /* right is next, left is list elem */ + HELP_OP, /* right is help string, left is elem */ + EXEC_OP, /* left command string, right help string */ + PATTERN_OP, /* left to var, right to pattern */ + OR_OP, + AND_OP, + NOT_OP, + COND_OP, /* aux field specifies cond type (GT, GE, etc.)*/ + VAL_OP, /* for strings used in other nodes */ + VAR_OP, /* string points to var */ + B_QUOTE_OP, /* string points to operand to be executed */ + ASSIGN_OP /* left to var, right to exp */ +}vtw_oper_e; + +typedef struct { + vtw_type_e val_type; + char *val; + int cnt; /* >0 means multivalue */ + char **vals; /* We might union with val */ + boolean free_me; +}valstruct; + +typedef struct vtw_node{ + vtw_oper_e vtw_node_oper; + struct vtw_node *vtw_node_left; + struct vtw_node *vtw_node_right; + char *vtw_node_string; + int vtw_node_aux; + vtw_type_e vtw_node_type; + valstruct vtw_node_val; /* we'll union it later */ +}vtw_node; + +typedef struct { + vtw_node *vtw_list_head; + vtw_node *vtw_list_tail; +}vtw_list; + +typedef struct { + int t_lev; + int m_lev; +}vtw_mark; + +typedef enum { + delete_act, + create_act, + activate_act, + update_act, + syntax_act, + commit_act, + begin_act, + end_act, + top_act +}vtw_act_type; + +typedef struct { + vtw_type_e def_type; + char *def_type_help; + char *def_default; + boolean tag; + boolean multi; + vtw_list actions[top_act]; +}vtw_def; + +typedef struct { + const char *f_segp; + int f_seglen; + int f_segoff; +} first_seg; +/* the first segment might be ADIR, or CDIR, or MDIR + we reserve space large enough for any one. + If the shorter one is used, it right aligned. + path points to the start of the current first + segment +*/ +typedef struct { + char *path_buf; /* path buffer */ + char *path; /* path */ + int path_len; /* path length used */ + int path_alloc; /* allocated - 1*/ + int *path_ends; /* path ends for dif levels*/ + int path_lev; /* how many used */ + int path_ends_alloc; /* how many allocated */ + int print_offset; /* for additional optional output information */ +} vtw_path; /* vyatta tree walk */ + +typedef struct { + int num; + int partnum; + void **ptrs; + unsigned int *parts; +}vtw_sorted; + +extern int char2val(vtw_def *def, char *value, valstruct *valp); +extern int get_value(char **valpp, vtw_path *pathp); +extern int get_value_to_at_string(vtw_path *pathp); +extern vtw_node * make_node(vtw_oper_e oper, vtw_node *left, + vtw_node *right); +extern vtw_node *make_str_node(char *str); +extern vtw_node *make_var_node(char *str); +extern vtw_node *make_str_node0(char *str, vtw_oper_e op); +extern void append(vtw_list *l, vtw_node *n, int aux); +extern int parse_def(vtw_def *defp, char *path, boolean type_only); + +extern int yy_cli_val_lex(void); +extern void cli_val_start(char *s); +extern void cli_val_done(void); +extern void init_path(vtw_path *path, const char *root); +extern void pop_path(vtw_path *path); +extern void push_path(vtw_path *path, char *segm); +extern void free_def(vtw_def *defp); +extern void free_sorted(vtw_sorted *sortp); +extern void *my_malloc(size_t size, const char *name); +extern void *my_realloc(void *ptr, size_t size, const char *name); + +extern vtw_path m_path, t_path; + +/************************************************* + GLOBAL FUNCTIONS +***************************************************/ +extern void add_val(valstruct *first, valstruct *second); +extern int cli_val_read(char *buf, int max_size); +extern vtw_node *make_val_node(valstruct *val); +extern char *my_strdup(const char *s, const char *name); +extern valstruct str2val(char *cp); +extern void dump_tree(vtw_node *node, int lev); +extern void dump_def(vtw_def *defp); +extern boolean val_cmp(valstruct *left, valstruct *right, vtw_cond_e cond); +extern void out_of_memory(void); +extern boolean validate_value(vtw_def *def, + char *value); +extern void internal_error(int line, char *file); +extern void done(void); +extern void del_value(vtw_def *defp, char *cp); +extern void bye(char *msg, ...); +extern void print_msg(char *msg, ...); +extern void switch_path(first_seg *seg); +extern void vtw_sort(valstruct *valp, vtw_sorted *sortp); +extern void free_val(valstruct *val); +extern void my_free(void *ptr); +extern void touch(void); +extern void dump_log(int argc, char **argv); +extern char *type_to_name(vtw_type_e type); +extern boolean execute_list(vtw_node *cur, vtw_def *def); +extern void touch_dir(const char *dp); + +void mark_paths(vtw_mark *markp); +void restore_paths(vtw_mark *markp); + +extern boolean get_config_lock(const char* adirp, const char* lock_name); + +#define VTWERR_BADPATH -2 +#define VTWERR_OK 0 +#define TAG_NAME "node.tag" +#define DEF_NAME "node.def" +#define VAL_NAME "node.val" +#define MOD_NAME ".modified" +#define OPQ_NAME ".wh.__dir_opaque" +#define LOCK_NAME ".commit.lck" + +#define INTERNAL internal_error(__LINE__, __FILE__) + +#endif diff --git a/src/cli_val.l b/src/cli_val.l new file mode 100644 index 0000000..5856cea --- /dev/null +++ b/src/cli_val.l @@ -0,0 +1,293 @@ +%{ +#include "cli_val.h" +#include "cli_parse.h" +#include "cli_objects.h" +static void make_val_value(vtw_type_e type); +#define YY_INPUT(buf,result,max_size) (result)=cli_val_read((buf), (max_size)) +%} +%option noyywrap +%option nounput +%option never-interactive + +/* + * Regular expressions of IP and MAC addresses, URLs, etc. + */ + +/* + * IPv4 address representation. + */ +RE_IPV4_BYTE 25[0-5]|2[0-4][0-9]|[01][0-9][0-9]|([0-9]{1,2}) +RE_IPV4 {RE_IPV4_BYTE}(\.{RE_IPV4_BYTE}){3} +RE_IPV4_PREFIXLEN (3[012]|[12][0-9]|[0-9]) +RE_IPV4NET {RE_IPV4}"/"{RE_IPV4_PREFIXLEN} + +/* + * IPv6 address representation in Augmented Backus-Naur Form (ABNF) + * as defined in RFC-2234. + * IPv6 address representation taken from RFC-3986: + * + * IPv6address = 6( h16 ":" ) ls32 + * / "::" 5( h16 ":" ) ls32 + * / [ h16 ] "::" 4( h16 ":" ) ls32 + * / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + * / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + * / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + * / [ *4( h16 ":" ) h16 ] "::" ls32 + * / [ *5( h16 ":" ) h16 ] "::" h16 + * / [ *6( h16 ":" ) h16 ] "::" + * + * h16 = 1*4HEXDIG + * ls32 = ( h16 ":" h16 ) / IPv4address + * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + * dec-octet = DIGIT ; 0-9 + * / %x31-39 DIGIT ; 10-99 + * / "1" 2DIGIT ; 100-199 + * / "2" %x30-34 DIGIT ; 200-249 + * / "25" %x30-35 ; 250-255 + */ + +RE_H16 [a-fA-F0-9]{1,4} +RE_H16_COLON {RE_H16}":" +RE_LS32 (({RE_H16}":"{RE_H16})|{RE_IPV4}) +RE_IPV6_P1 {RE_H16_COLON}{6}{RE_LS32} +RE_IPV6_P2 "::"{RE_H16_COLON}{5}{RE_LS32} +RE_IPV6_P3 ({RE_H16})?"::"{RE_H16_COLON}{4}{RE_LS32} +RE_IPV6_P4 ({RE_H16_COLON}{0,1}{RE_H16})?"::"{RE_H16_COLON}{3}{RE_LS32} +RE_IPV6_P5 ({RE_H16_COLON}{0,2}{RE_H16})?"::"{RE_H16_COLON}{2}{RE_LS32} +RE_IPV6_P6 ({RE_H16_COLON}{0,3}{RE_H16})?"::"{RE_H16_COLON}{1}{RE_LS32} +RE_IPV6_P7 ({RE_H16_COLON}{0,4}{RE_H16})?"::"{RE_LS32} +RE_IPV6_P8 ({RE_H16_COLON}{0,5}{RE_H16})?"::"{RE_H16} +RE_IPV6_P9 ({RE_H16_COLON}{0,6}{RE_H16})?"::" +RE_IPV6 {RE_IPV6_P1}|{RE_IPV6_P2}|{RE_IPV6_P3}|{RE_IPV6_P4}|{RE_IPV6_P5}|{RE_IPV6_P6}|{RE_IPV6_P7}|{RE_IPV6_P8}|{RE_IPV6_P9} +RE_IPV6_PREFIXLEN 12[0-8]|1[01][0-9]|[0-9][0-9]? +RE_IPV6NET {RE_IPV6}"/"{RE_IPV6_PREFIXLEN} + +/* + * Ethernet MAC address representation. + */ +RE_MACADDR [a-fA-F0-9]{1,2}(:[a-fA-F0-9]{1,2}){5} + +/* + * URL-related regular expressions. + * + * Implementation is based on the BNF-like specification from: + * - RFC-1738: HTTP, FTP, FILE + * - RFC-3617: TFTP + * - RFC-3986: update of RFC-1738 + */ +RE_URL {RE_URL_FILE}|{RE_URL_FTP}|{RE_URL_HTTP}|{RE_URL_TFTP} + +/* + * URL schemeparts for IP based protocols. + * Representation taken from RFC-1738, and some of it is updated by RFC-3986. + * + * login = [ user [ ":" password ] "@" ] hostport + * hostport = host [ ":" port ] + * host = hostname | hostnumber + * hostname = *[ domainlabel "." ] toplabel + * domainlabel = alphadigit | alphadigit *[ alphadigit | "-" ] alphadigit + * toplabel = alpha | alpha *[ alphadigit | "-" ] alphadigit + * alphadigit = alpha | digit + * hostnumber = digits "." digits "." digits "." digits + * port = digits + * user = *[ uchar | ";" | "?" | "&" | "=" ] + * password = *[ uchar | ";" | "?" | "&" | "=" ] + */ +RE_URL_LOGIN ({RE_URL_USER}(":"{RE_URL_PASSWORD})?"@")?{RE_URL_HOSTPORT} +RE_URL_HOSTPORT {RE_URL_HOST}(":"{RE_URL_PORT})? +RE_URL_HOST {RE_URL_HOSTNAME}|{RE_IPV4}|{RE_URL_IP_LITERAL} +RE_URL_IP_LITERAL "["({RE_IPV6}|{RE_URL_IPV_FUTURE})"]" +RE_URL_IPV_FUTURE "v"({RE_URL_HEXDIG})+"."({RE_URL_UNRESERVED}|{RE_URL_SUBDELIMS}|":")+ +RE_URL_HOSTNAME ({RE_URL_DOMAINLABEL}".")*{RE_URL_TOPLABEL} +RE_URL_DOMAINLABEL {RE_URL_ALPHADIGIT}|{RE_URL_ALPHADIGIT}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_TOPLABEL {RE_URL_ALPHA}|{RE_URL_ALPHA}({RE_URL_ALPHADIGIT}|"-")*{RE_URL_ALPHADIGIT} +RE_URL_ALPHADIGIT {RE_URL_ALPHA}|{RE_URL_DIGIT} +RE_URL_HOSTNUMBER {RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS}"."{RE_URL_DIGITS} +RE_URL_PORT {RE_URL_DIGITS} +RE_URL_USER ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* +RE_URL_PASSWORD ({RE_URL_UCHAR}|";"|"?"|"&"|"=")* + +/* + * FILE URL regular expression. + * Representation taken from RFC-1738. + * + * fileurl = "file://" [ host | "localhost" ] "/" fpath + */ +RE_URL_FILE "file://"({RE_URL_HOST}|"localhost")?"/"{RE_URL_FPATH} + +/* + * FTP URL regular expression. + * Representation taken from RFC-1738. + * + * ftpurl = "ftp://" login [ "/" fpath [ ";type=" ftptype ] ] + * fpath = fsegment *[ "/" fsegment ] + * fsegment = *[ uchar | "?" | ":" | "@" | "&" | "=" ] + * ftptype = "A" | "I" | "D" | "a" | "i" | "d" + */ +RE_URL_FTP "ftp://"{RE_URL_LOGIN}("/"{RE_URL_FPATH}(";type="{RE_URL_FTPTYPE})?)? +RE_URL_FPATH {RE_URL_FSEGMENT}("/"{RE_URL_FSEGMENT})* +RE_URL_FSEGMENT ({RE_URL_UCHAR}|"?"|":"|"@"|"&"|"=")* +RE_URL_FTPTYPE "A"|"I"|"D"|"a"|"i"|"d" + +/* + * HTTP URL regular expression. + * Representation taken from RFC-1738. + * + * httpurl = "http://" hostport [ "/" hpath [ "?" search ] ] + * hpath = hsegment *[ "/" hsegment ] + * hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + * search = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + */ +RE_URL_HTTP "http://"{RE_URL_HOSTPORT}("/"{RE_URL_HPATH}("?"{RE_URL_SEARCH})?)? +RE_URL_HPATH {RE_URL_HSEGMENT}("/"{RE_URL_HSEGMENT})* +RE_URL_HSEGMENT ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* +RE_URL_SEARCH ({RE_URL_UCHAR}|";"|":"|"@"|"&"|"=")* + +/* + * TFTP URL regular expression. + * Representation taken from RFC-3617. + * + * tftpURI = "tftp://" host "/" file [ mode ] + * mode = ";" "mode=" ( "netascii" / "octet" ) + * file = *( unreserved / escaped ) + * host = <as specified by RFC 2732 [3]> + * unreserved = <as specified in RFC 2396 [4]> + * escaped = <as specified in RFC 2396> + */ +RE_URL_TFTP "tftp://"{RE_URL_HOST}"/"{RE_URL_TFTP_FILE}({RE_URL_TFTP_MODE})? +RE_URL_TFTP_MODE ";""mode="("netascii"|"octet") +RE_URL_TFTP_FILE ({RE_URL_UNRESERVED}|{RE_URL_ESCAPE})* + +/* + * URL-related miscellaneous definitions. + * Representation taken from RFC-1738 and from RFC-3986. + * + * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | + * "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | + * "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | + * "y" | "z" + * hialpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + * "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + * "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + * alpha = lowalpha | hialpha + * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + * "8" | "9" + * safe = "$" | "-" | "_" | "." | "+" + * extra = "!" | "*" | "'" | "(" | ")" | "," + * national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`" + * punctuation = "<" | ">" | "#" | "%" | <"> + * + * + * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" + * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | + * "a" | "b" | "c" | "d" | "e" | "f" + * escape = "%" hex hex + * + * unreserved = alpha | digit | safe | extra + * uchar = unreserved | escape + * xchar = unreserved | reserved | escape + * digits = 1*digit + * + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" + */ +RE_URL_LOWALPHA [a-z] +RE_URL_HIALPHA [A-Z] +RE_URL_ALPHA {RE_URL_LOWALPHA}|{RE_URL_HIALPHA} +RE_URL_DIGIT [0-9] +RE_URL_SAFE "$"|"-"|"_"|"."|"+" +RE_URL_EXTRA "!"|"*"|"'"|"("|")"|"," +RE_URL_NATIONAL "{"|"}"|"|"|"\"|"^"|"~"|"["|"]"|"`" +RE_URL_PUNCTUATION "<"|">"|"#"|"%"|<"> +RE_URL_RESERVED ";"|"/"|"?"|":"|"@"|"&"|"=" +RE_URL_HEXDIG {RE_URL_DIGIT}|[A-F]|[a-f] +RE_URL_ESCAPE "%"{RE_URL_HEXDIG}{RE_URL_HEXDIG} +RE_URL_UNRESERVED {RE_URL_ALPHA}|{RE_URL_DIGIT}|{RE_URL_SAFE}|{RE_URL_EXTRA} +RE_URL_UCHAR {RE_URL_UNRESERVED}|{RE_URL_ESCAPE} +RE_URL_XCHAR {RE_URL_UNRESERVED}|{RE_URL_RESERVED}|{RE_URL_ESCAPE} +RE_URL_DIGITS {RE_URL_DIGIT}{1,} +RE_URL_SUBDELIMS "!"|"$"|"&"|"'"|"("|")"|"*"|"+"|","|";"|"=" + + +%% +:: { + make_val_value(IPV6_TYPE); + return VALUE; + } + +true { + make_val_value(BOOL_TYPE); + return VALUE; + } + +false { + make_val_value(BOOL_TYPE); + return VALUE; + } + +[0-9]+ { + make_val_value(INT_TYPE); + return VALUE; + } + +{RE_IPV4} { + make_val_value(IPV4_TYPE); + return VALUE; + } + +{RE_IPV4NET} { + make_val_value(IPV4NET_TYPE); + return VALUE; + } + +{RE_IPV6} { + make_val_value(IPV6_TYPE); + return VALUE; + } + +{RE_IPV6NET} { + make_val_value(IPV6NET_TYPE); + return VALUE; + } + +{RE_MACADDR} { + make_val_value(MACADDR_TYPE); + return VALUE; + } + +\\\n /* whitespace */ + +[ \t]+ /*whitespace */ +\n return EOL; +. { + /* everything else is a syntax error */ + return SYNTAX_ERROR; + } + + +%% +static void make_val_value(vtw_type_e type) +{ + memset(get_cli_value_ptr(), 0, sizeof(valstruct)); + get_cli_value_ptr()->free_me = TRUE; + get_cli_value_ptr()->val = my_strdup(yytext, "cli_val.l"); + get_cli_value_ptr()->val_type = type; +} + + + + + + + + + + + + + + + + + + diff --git a/src/cli_val_engine.c b/src/cli_val_engine.c new file mode 100644 index 0000000..d597c68 --- /dev/null +++ b/src/cli_val_engine.c @@ -0,0 +1,881 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli handler for the reference variables + + **** End License **** + +*************************************************************************/ + +#if !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <stdarg.h> +#include <regex.h> +#include <dirent.h> + +#include <string.h> + +#include "cli_val_engine.h" + +/********************* + * Data definitions + * + *********************/ + +/** + * Special file names: + */ +#define VALUE_FILE ("node.val") +#define NODE_TAG ("node.tag") +#define NODE_DEF ("node.def") + +/** + * "Command" definition (element of a variable path): + */ + +typedef struct { + clind_cmd_type cmd_type; + const char* text[10]; +} cmd_parse_definition; + +/** + * Structure to hold information about possible command entries: + */ + +static cmd_parse_definition cmd_parse_definitions[] = { + { CLIND_CMD_PARENT_VALUE, {"..","@",NULL } }, + { CLIND_CMD_NEIGHBOR, {"..","*",NULL } }, + { CLIND_CMD_PARENT, {"..",NULL } }, + { CLIND_CMD_PARENT, {".","..",NULL } }, + { CLIND_CMD_CHILD, {".","*",NULL } }, + { CLIND_CMD_VALUE, {".","@",NULL } }, + { CLIND_CMD_SELF_NAME, {".",NULL } }, + { CLIND_CMD_VALUE, {"@",NULL } }, + { CLIND_CMD_MULTI_VALUE, {"@@",NULL } }, + { CLIND_CMD_CHILD, {"*",NULL } }, + { CLIND_CMD_UNKNOWN, {NULL} } +}; + +/************************ + * Cmd utils forward declarations + * + ************************/ + +static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd); + +/****************************** + * Variable evaluation engine * + * * + ******************************/ + +/** + * For a given config path, return the 'value' of the path. + * If the path ends with "node.val", then return the file content. + * If not, then return the last path element. + * If path is empty, or the file is empty, or the file does not exist, + * then return NULL. + * The user of this function is responsible for the memory deallocation. + */ + +static char** clind_get_current_value(clind_path_ref cfg_path, + clind_path_ref tmpl_path, + int check_existence, + vtw_type_e *val_type, + const char* root_tmpl_path, + int return_value_file_name, + int multi_value, + int *ret_size) { + + char** ret=NULL; + *ret_size=0; + + if(val_type) *val_type=TEXT_TYPE; + + if(cfg_path && (clind_path_get_size(cfg_path)>0)) { + + clind_path_ref tmpl_path_clone = clind_path_clone(tmpl_path); + + const char* cfg_path_string = clind_path_get_path_string(cfg_path); + const char* cfg_end = clind_path_last_string(cfg_path); + const char* tmpl_path_string = clind_path_get_path_string(tmpl_path_clone); + const char* tmpl_end = clind_path_last_string(tmpl_path_clone); + + /* + printf("%s:111.111:%s:%s:%s:%s:%d\n",__FUNCTION__, + cfg_path_string, + cfg_end, + tmpl_path_string, + tmpl_end, + multi_value); + */ + + if(cfg_path_string && cfg_end) { + + if(strcmp(cfg_end,VALUE_FILE)==0) { + + /* Value reference: */ + + if(return_value_file_name) { + + ret=(char**)realloc(ret,sizeof(char*)*1); + ret[0]=strdup(cfg_path_string); + *ret_size=1; + + } else { + + FILE* f = fopen(cfg_path_string,"r"); + if(f) { + char buffer[8193]; + if(multi_value) { + while(fgets(buffer, sizeof(buffer)-1,f)) { + int len=strlen(buffer); + while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) { + buffer[len-1]=0; + len--; + } + if(len>0) { + ret=(char**)realloc(ret,sizeof(char*)*(*ret_size+1)); + ret[*ret_size]=strdup(buffer); + *ret_size+=1; + } + } + } else { + int sz = fread(buffer, 1, sizeof(buffer)-1, f); + if(sz>0) { + int len=0; + buffer[sz]=0; + len=strlen(buffer); + while(len>0 && (buffer[len-1]==10 || buffer[len-1]==13)) { + buffer[len-1]=0; + len--; + } + ret=(char**)malloc(sizeof(char*)*1); + ret[0]=strdup(buffer); + *ret_size=1; + } + } + fclose(f); + } + } + + } else if(return_value_file_name) { + + ret=(char**)realloc(ret,sizeof(char*)*1); + ret[0]=(char*)malloc(strlen(cfg_path_string)+1+strlen(VALUE_FILE)+1); + strcpy(ret[0],cfg_path_string); + strcpy(ret[0]+strlen(ret[0]),"/"); + strcpy(ret[0]+strlen(ret[0]),VALUE_FILE); + *ret_size=1; + + } else { + + struct stat statbuf; + + /* Directory reference: */ + + if(!check_existence || (lstat(cfg_path_string, &statbuf) == 0)) { + ret=(char**)realloc(ret,sizeof(char*)*1); + ret[0]=clind_unescape(cfg_end); + *ret_size=1; + } + } + + if(ret) { + if(tmpl_end && (strcmp(tmpl_end,NODE_TAG)==0)) { + clind_path_pop(tmpl_path_clone); + tmpl_path_string = clind_path_get_path_string(tmpl_path_clone); + tmpl_end = clind_path_last_string(tmpl_path_clone); + } + } + + if(ret && tmpl_path_string && !return_value_file_name) { + + vtw_def def; + struct stat statbuf; + int fn_node_def_size=strlen(tmpl_path_string)+1+strlen(NODE_DEF)+1; + char* fn_node_def=(char*)malloc(fn_node_def_size); + + memset(&def, 0, sizeof(def)); + + fn_node_def[0]=0; + + if(*tmpl_path_string!='/' && root_tmpl_path) { + fn_node_def_size+=strlen(root_tmpl_path+1); + fn_node_def=(char*)realloc(fn_node_def,fn_node_def_size); + strcpy(fn_node_def+strlen(fn_node_def),root_tmpl_path); + strcpy(fn_node_def+strlen(fn_node_def),"/"); + } + + strcpy(fn_node_def+strlen(fn_node_def),tmpl_path_string); + strcpy(fn_node_def+strlen(fn_node_def),"/"); + strcpy(fn_node_def+strlen(fn_node_def),NODE_DEF); + + if ((lstat(fn_node_def, &statbuf) == 0)&& + (parse_def(&def, fn_node_def, TRUE)==0)) { + + if(def.def_type != ERROR_TYPE) { + + int status=0; + valstruct res; + int i=0; + + memset(&res,0,sizeof(res)); + + for(i=0;i<*ret_size;i++) { + + if(ret[i]) { + + /* return the value in the correct type */ + status = char2val(&def, ret[i], &res); + + if(status==0) { + + if(val_type) *val_type=res.val_type; + + if(res.free_me && res.val) { + free(ret[i]); + ret[i]=res.val; + } + } else { + /* Bad value ? */ + } + } + } + } + + } else { + + while(*ret_size>0) { + if(ret[*ret_size-1]) { + free(ret[*ret_size-1]); + } + *ret_size-=1; + } + free(ret); + ret=NULL; + + } + + free(fn_node_def); + } + } + + clind_path_destruct(&tmpl_path_clone); + } + + return ret; +} + +/** + * Return TRUE if current node is a multi-node value + */ +static int is_multi_node(clind_path_ref tmpl_path) { + + int ret=0; + + if(tmpl_path) { + + const char* t_end = clind_path_last_string(tmpl_path); + + if(t_end && (strcmp(t_end,NODE_TAG)==0)) { + ret=1; + } + } + + return ret; +} + +/** + * Return TRUE if current node is node.def + */ +static int is_node_def(clind_path_ref tmpl_path) { + + int ret=0; + + if(tmpl_path) { + + const char* t_end = clind_path_last_string(tmpl_path); + + if(t_end && (strcmp(t_end,NODE_DEF)==0)) { + ret=1; + } + } + + return ret; +} + +/** + * Apply a single command to the configuration path. + * cfg_path - absolute configuration path, + * tmpl_path - logical template path, + * cmd - single reference command from the variable path. + * The result is the array of "derived" paths. + * result_len output parameter contains the array size. + */ + +static clind_path_ref* clind_config_engine_apply_command(clind_path_ref cfg_path, + clind_path_ref tmpl_path, + clind_cmd *cmd, + int *result_len) { + clind_path_ref* ret=NULL; + + if(cfg_path && tmpl_path && result_len && cmd) { + + /* + printf("%s:111.111:%s:%s:%d\n",__FUNCTION__, + clind_path_get_path_string(cfg_path), + clind_path_get_path_string(tmpl_path), + cmd->type); + */ + + switch (cmd->type) { + + case CLIND_CMD_PARENT: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } + } + + break; + + case CLIND_CMD_SELF_NAME: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + } + + break; + + case CLIND_CMD_CHILD: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + clind_path_push(cfg_path,cmd->value); + clind_path_push(tmpl_path,cmd->value); + } + break; + + case CLIND_CMD_NEIGHBOR: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + + clind_path_push(cfg_path,cmd->value); + clind_path_push(tmpl_path,cmd->value); + } + + break; + + case CLIND_CMD_VALUE: + + { + const char* t_path_string = clind_path_get_path_string(tmpl_path); + const char* t_end = clind_path_last_string(tmpl_path); + const char* c_end = clind_path_last_string(cfg_path); + + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(t_end && (strcmp(t_end,NODE_TAG)==0)) { + /* do nothing, we are there already */ + } else if(t_path_string && clind_file_exists(t_path_string,NODE_TAG)) { + clind_path_push(tmpl_path,NODE_TAG); + } else if(c_end && (strcmp(c_end,VALUE_FILE)==0)) { + /* do nothing, we are there already */ + } else { + clind_path_push(cfg_path,VALUE_FILE); + } + } + + break; + + case CLIND_CMD_PARENT_VALUE: + + { + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } + + break; + + case CLIND_CMD_MULTI_VALUE: + + { + const char* cfg_path_string = NULL; + + if(is_multi_node(tmpl_path)) { + clind_path_pop(cfg_path); + clind_path_pop(tmpl_path); + } else if(is_node_def(tmpl_path)) { + clind_path_pop(tmpl_path); + } + + cfg_path_string = clind_path_get_path_string(cfg_path); + + if(cfg_path_string) { + + const char* c_end = clind_path_last_string(cfg_path); + + if(c_end && (strcmp(c_end,VALUE_FILE)==0)) { + + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + } else if(clind_file_exists(cfg_path_string,VALUE_FILE)) { + + *result_len=1; + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + cfg_path = clind_path_clone(cfg_path); + ret[0]=cfg_path; + + clind_path_push(cfg_path,VALUE_FILE); + + } else { + + DIR* dir=NULL; + + dir = opendir(cfg_path_string); + + if(dir) { + + *result_len=0; + + ret=(clind_path_ref*)(malloc(*result_len * sizeof(clind_path_ref))); + + do { + struct dirent *de = readdir(dir); + + if(!de) break; + else if(de->d_name[0] && de->d_name[0]!='.') { + clind_path_ref cfg_path_1 = clind_path_clone(cfg_path); + clind_path_push(cfg_path_1,de->d_name); + (*result_len)++; + ret=(clind_path_ref*)(realloc(ret,*result_len * sizeof(clind_path_ref))); + ret[*result_len-1]=cfg_path_1; + } + } while(1); + + clind_path_push(tmpl_path,NODE_TAG); + + closedir(dir); + } + } + } + } + break; + + default: + ; + } + } + + return ret; +} + +/** + * cfg_path - absolute configuration path, + * tmpl_path - logical template path, + * cmd_path - variable command path. + */ + +int clind_config_engine_apply_command_path(clind_path_ref cfg_path_orig, + clind_path_ref tmpl_path_orig, + clind_path_ref cmd_path, + int check_existence, + clind_val* res, + const char* root_cfg_path, + const char* root_tmpl_path, + int return_value_file_name) { + + int ret=-1; + + /* + printf("%s:111.111:cfg_path=%s,tmpl_path=%s,cmd_path=%s,rtp=%s\n",__FUNCTION__, + clind_path_get_path_string(cfg_path_orig), + clind_path_get_path_string(tmpl_path_orig), + clind_path_get_path_string(cmd_path), + root_tmpl_path); + */ + + if(cfg_path_orig && tmpl_path_orig && cmd_path && res) { + + /* Command to be processed: */ + clind_cmd cmd; + + /* Array of configuration pointers. Initially, contains only one + element - cfg_path. */ + clind_path_ref* config_paths= + (clind_path_ref*)malloc(sizeof(clind_path_ref)*1); + + /* Size of the array (initially - just one): */ + int config_paths_size=1; + + /* Clone the input paths to preserve the input objects intact: */ + clind_path_ref tmpl_path=NULL; + clind_path_ref cfg_path=NULL; + + if(clind_path_is_absolute(cmd_path)) { + tmpl_path=clind_path_construct(root_tmpl_path); + if(!tmpl_path) { + return -1; + } + cfg_path=clind_path_construct(root_cfg_path); + if(!cfg_path) { + return -1; + } + } else { + cfg_path=clind_path_clone(cfg_path_orig); + tmpl_path=clind_path_clone(tmpl_path_orig); + } + + res->value=NULL; + res->val_type=TEXT_TYPE; + + /* Set the initial array content: */ + config_paths[0]=cfg_path; + + /* Apply the commands one-by-one: */ + while(clind_path_get_size(cmd_path)>0 && + (clind_path_shift_cmd(cmd_path,&cmd)==0)) { + + int i=0; + + /* Temporary array to keep the config paths for the next + command application: */ + clind_path_ref* new_config_paths=NULL; + int new_config_paths_size=0; + + /* This path contains the template path at the beginning + of the cycle: */ + clind_path_ref tmpl_path_curr=clind_path_clone(tmpl_path); + + for (i=0;i<config_paths_size;i++) { + + int size=0; + + clind_path_ref* new_config_paths_1=NULL; + + if(i==0) { + new_config_paths_1= + clind_config_engine_apply_command(config_paths[i], + tmpl_path, + &cmd, + &size); + } else { + clind_path_ref tmpl_path_curr_clone=clind_path_clone(tmpl_path_curr); + new_config_paths_1= + clind_config_engine_apply_command(config_paths[i], + tmpl_path_curr_clone, + &cmd, + &size); + clind_path_destruct(&tmpl_path_curr_clone); + } + + if(new_config_paths_1) { + + int j=0; + + for(j=0;j<size;j++) { + if(new_config_paths_1[j]) { + new_config_paths_size++; + new_config_paths= + (clind_path_ref*)realloc(new_config_paths, + sizeof(clind_path_ref)* + new_config_paths_size); + new_config_paths[new_config_paths_size-1]=new_config_paths_1[j]; + } + } + + free(new_config_paths_1); + } + + clind_path_destruct(&(config_paths[i])); + } + + free(config_paths); + config_paths=new_config_paths; + config_paths_size=new_config_paths_size; + clind_path_destruct(&tmpl_path_curr); + } + + if(config_paths) { + + char** sarr=NULL; + int sarrlen=0; + + vtw_type_e val_type=TEXT_TYPE; + + int i=0; + + for(i=0;i<config_paths_size;i++) { + + if(config_paths[i]) { + + int vallen=0; + + char** valarr=clind_get_current_value(config_paths[i], + tmpl_path, + check_existence, + &val_type, + root_tmpl_path, + return_value_file_name, + /*Last command: */ + (cmd.type==CLIND_CMD_MULTI_VALUE), + &vallen); + + clind_path_destruct(&config_paths[i]); + + if(valarr) { + + int k=0; + + for(k=0;k<vallen;k++) { + + char* s=valarr[k]; + + if(s) { + + /* search if we already have it: */ + int j=0; + for(j=0;j<sarrlen;j++) { + if(!strcmp(sarr[j],s)) { + break; + } + } + + if(j<sarrlen) { + free(s); + } else { + sarrlen++; + sarr=(char**)realloc(sarr,sizeof(char*)*sarrlen); + sarr[sarrlen-1]=s; + } + } + } + free(valarr); + } + } + } + + free(config_paths); + + if(sarr) { + + ret=0; + + if(sarrlen==1) { + + res->value=sarr[0]; + + } else { + + for(i=0;i<sarrlen;i++) { + + if(sarr[i]) { + + char* s = clind_quote(sarr[i]); + + free(sarr[i]); + sarr[i]=NULL; + + if(s) { + + if(!res->value) { + + res->value=s; + + } else if(res->value[0]==0) { + + free(res->value); + res->value=s; + + } else { + + res->value=(char*)realloc(res->value, + strlen(res->value)+1+strlen(s)+1); + + strcpy(res->value+strlen(res->value)," "); + strcpy(res->value+strlen(res->value),s); + + free(s); + } + } + } + } + } + free(sarr); + } + } + + clind_path_destruct(&cmd_path); + clind_path_destruct(&cfg_path); + clind_path_destruct(&tmpl_path); + } + + return ret; +} + +/****************************** + * Cmd utils. + * + ******************************/ + +static int clind_path_shift_cmd(clind_path_ref path,clind_cmd *cmd) { + + int ret=-1; + + if(cmd) { + + cmd->type = CLIND_CMD_UNKNOWN; + cmd->value[0]=0; + + if(path && clind_path_get_size(path)>0) { + + int i=0; + int done=0; + + while(cmd_parse_definitions[i].text!=NULL && cmd_parse_definitions[i].text[0]!=NULL) { + + int j=0; + + while(cmd_parse_definitions[i].text[j]) { + const char* str = clind_path_get_string(path,j); + if(str) { + if(!strcmp(cmd_parse_definitions[i].text[j],"*")) { + if(*str!='.' && *str!='@') { + j++; + strncpy(cmd->value,str,sizeof(cmd->value)-1); + continue; + } + } else if(!strcmp(cmd_parse_definitions[i].text[j],str)) { + j++; + strncpy(cmd->value,str,sizeof(cmd->value)-1); + continue; + } + } + j=0; + break; + } + + if(j<1) { + i++; + continue; + } else { + done=1; + } + + cmd->type = cmd_parse_definitions[i].cmd_type; + + while(j) { + clind_path_shift(path); + j--; + } + + break; + } + + if(done) { + ret=0; + } + } + } + + return ret; +} + diff --git a/src/cli_val_engine.h b/src/cli_val_engine.h new file mode 100644 index 0000000..0f32276 --- /dev/null +++ b/src/cli_val_engine.h @@ -0,0 +1,86 @@ + +/************************************************************************ + + Module: cli + + **** License **** + Version: VPL 1.0 + + The contents of this file are subject to the Vyatta Public License + Version 1.0 ("License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.vyatta.com/vpl + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + This code was originally developed by Vyatta, Inc. + Portions created by Vyatta are Copyright (C) 2007 Vyatta, Inc. + All Rights Reserved. + + Author: Oleg Moskalenko + Date: 2007 + Description: "new" cli handler for the reference variables + + **** End License **** + +*************************************************************************/ + +#if !defined(__CLI_VAL_ENGINE__) +#define __CLI_VAL_ENGINE__ + +#include "cli_path_utils.h" +#include "cli_val.h" + +/******************* + * Type definitions + * + *******************/ + +typedef enum { + + CLIND_CMD_UNKNOWN=0, /* ??? */ + CLIND_CMD_PARENT, /* .. */ + CLIND_CMD_SELF_NAME, /* . */ + CLIND_CMD_CHILD, /* <name> */ + CLIND_CMD_NEIGHBOR, /* ../<name> */ + CLIND_CMD_VALUE, /* @ */ + CLIND_CMD_PARENT_VALUE, /* ../@ */ + CLIND_CMD_MULTI_VALUE /* @@ */ + +} clind_cmd_type; + +typedef struct { + + clind_cmd_type type; + char value[1025]; + +} clind_cmd; + +typedef struct { + + vtw_type_e val_type; + char* value; + +} clind_val; + +/******************************** + * Main command-handling method: + * + ********************************/ + +int clind_config_engine_apply_command_path(clind_path_ref cfg_path, + clind_path_ref tmpl_path, + clind_path_ref cmd_path, + int check_existence, + clind_val *res, + const char* root_cfg_path, + const char* root_tmpl_path, + int return_value_file_name); + + + + +#endif /* __CLI_VAL_ENGINE__*/ diff --git a/src/commit.c b/src/commit.c new file mode 100644 index 0000000..a136b58 --- /dev/null +++ b/src/commit.c @@ -0,0 +1,1364 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <limits.h> + +#include "cli_val.h" +#include "cli_objects.h" +#include "cli_parse.h" +#include "cli_path_utils.h" + +static char def_name[] = DEF_NAME; +static char tag_name[] = TAG_NAME; +static char opaque_name[] = OPQ_NAME; + +static int fin_commit(boolean ok); +static boolean commit_value(vtw_def *defp, char *cp, + vtw_cmode mode, boolean in_txn); +static void perform_create_node(); +static void perform_delete_node(); +static void perform_move(); +static boolean commit_delete_child(vtw_def *pdefp, char *child, + boolean deleting, boolean in_txn); +static boolean commit_delete_children(vtw_def *defp, boolean deleting, + boolean in_txn); +static boolean commit_update_children(vtw_def *defp, boolean creating, + boolean in_txn, boolean *parent_update); + +#if BITWISE +static void make_dir() +{ + struct stat statbuf; + if (lstat(m_path.path, &statbuf) < 0) { + char *command; + command = my_malloc(strlen(m_path.path) + 10, "set"); + sprintf(command, "mkdir -p %s", m_path.path); + system(command); + free(command); + return; + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("directory %s expected, found regular file", m_path.path); + } + return; +} +#endif + +/************************************************* + validate_dir_for_commit: + validate value.value if there is one, validate + subdirectries names (for tag directory, and for + regular directory); + validate subdirectories + returns TRUE if OK, FASLE if errors + exits with status != 0 in case of parse error +*/ +static boolean validate_dir_for_commit() +{ + struct stat statbuf; + int status=0; + vtw_def def; + boolean def_present=FALSE; + boolean value_present=FALSE; + int subdirs_number=0; + DIR *dp=NULL; + struct dirent *dirp=NULL; + char *cp=NULL; + boolean ret=TRUE; + char *uename = NULL; + +#ifdef DEBUG + printf("validating directory (node_cnt %d, free_node_cnt %d)\n" + "t_path |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + + /* find definition */ + + push_path(&t_path, def_name); /* PUSH 1 */ + if ((lstat(t_path.path, &statbuf) >= 0) && + ((statbuf.st_mode & S_IFMT) == S_IFREG)) { + /* definition present */ + def_present = TRUE; + memset(&def, 0, sizeof(def)); +#ifdef DEBUG1 + printf("Parsing definition\n"); +#endif + + if ((status = parse_def(&def, t_path.path, + FALSE))) { + exit(status); + } + + } +#ifdef DEBUG1 + else + printf("No definition\n"); +#endif + pop_path(&t_path); /* for PUSH 1 */ + + /* look at modified stuff */ + if ((dp = opendir(m_path.path)) == NULL){ + INTERNAL; + } + if (def_present && def.tag) { + push_path(&t_path, tag_name); /* PUSH 2a */ + } + + while ((dirp = readdir(dp)) != NULL) { + + if (strcmp(dirp->d_name, ".") == 0 || + strcmp(dirp->d_name, "..") == 0 || + strcmp(dirp->d_name, MOD_NAME) == 0 || + strcmp(dirp->d_name, LOCK_NAME) == 0 || + strcmp(dirp->d_name, opaque_name) == 0 || + strncmp(dirp->d_name, ".wh.", 4) == 0) { + continue; /*ignore dot and dot-dot*/ + } + + subdirs_number++; + + if(uename) + my_free(uename); + + uename = clind_unescape(dirp->d_name); + + if (strcmp(uename, VAL_NAME) == 0) { + + value_present=TRUE; + + /* deal with the value */ + if (!def_present) { + printf("There is no definition specified in template\n" + "\t%s\n\t - therefore no value permitted\n", + t_path.path); + ret = FALSE; + continue; + } + + if (def.tag) { + printf("Tag specified in template\n" + "\t%s\n\t - therefore no value permitted\n", + t_path.path); + ret = FALSE; + continue; + } + + /* value is OK */ + /* read it */ + cp = NULL; + status = get_value(&cp, &m_path); + if (status == VTWERR_OK){ +#ifdef DEBUG1 + printf("Validating value |%s|\n" + "for path %s\n", cp, m_path.path); +#endif + status = validate_value(&def, cp); + ret = ret && status; + } + if (cp) + my_free(cp); + continue; + } + + push_path(&m_path, uename); /* PUSH 3 */ + if (lstat(m_path.path, &statbuf) < 0) { + printf("Can't read directory %s\n", + m_path.path); + ret = FALSE; + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + printf("Non directory file %s\n", m_path.path); + ret = FALSE; + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + + if (def_present && def.tag) { + /* do not push t_path, it is already pushed above */ + /* validate dir name against definition */ + boolean res = validate_value(&def, uename); + value_present=TRUE; + if (!res) { + ret = FALSE; + /* do not go inside bad directory */ + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + } else { + push_path(&t_path, uename); /* PUSH 2b the same as PUSH 2a */ + if (lstat(t_path.path, &statbuf) < 0) { + printf("No such template directory (%s)\n" + "for directory %s", + t_path.path, m_path.path); + ret = FALSE; + pop_path(&t_path); /* for PUSH 2b */ + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + printf("Non directory file %s\n", m_path.path); + ret = FALSE; + pop_path(&t_path); /* for PUSH 2b */ + pop_path(&m_path); /* for PUSH 3 */ + continue; + } + } + + status = validate_dir_for_commit(); + ret = ret && status; + pop_path(&m_path); /* for PUSH 3 */ + if (!def_present || !def.tag) + pop_path(&t_path); /* for PUSH 2b */ + + } + status = closedir(dp); + if (status) + bye("Cannot close dir %s\n", m_path.path); + + if(!value_present && def_present && !def.tag) { + ret = ret && validate_value(&def, ""); + } + + if (def_present && def.tag) + pop_path(&t_path); /* for PUSH 3a */ + if (def_present) + free_def(&def); +#ifdef DEBUG + printf("directory done(node_cnt %d, free_node_cnt %d)\n" + "mpath |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + if (uename) + my_free(uename); + return ret; +} + +/*************************************************** + main: + main function (for now) +***************************************************/ + +int main(int argc, char **argv) +{ + + boolean status; + char *mod; + struct stat statbuf; + int st; + boolean update_pending = FALSE; + + set_in_commit(TRUE); + dump_log( argc, argv); + init_paths(TRUE); + mod = my_malloc(strlen(get_mdirp()) + strlen(MOD_NAME)+2, "COMMIT"); + sprintf(mod, "%s/%s", get_mdirp(), MOD_NAME); + st = lstat(mod, &statbuf); + my_free(mod); + if (st < 0 ) { + bye("No configuration changes to commit\n"); + exit(-1); + } + + if(!get_config_lock(get_adirp(), LOCK_NAME)) { + bye("Configuration is locked\n"); + } + + status = validate_dir_for_commit(); + if (status == TRUE) { + switch_path(CPATH); + status = commit_delete_children(NULL, FALSE, FALSE); + } + if (status == TRUE) + status = commit_update_children(NULL, FALSE, FALSE, &update_pending); + fin_commit(status); + + done(); + + return (status == TRUE) ? 0 : 1; +} + +/************************************************************* + perform_create_node - + remove node and descendent from woring path + and create a new node +*************************************************************/ +static void perform_create_node() +{ +#if BITWISE + static char format[]="rm -f -r %s;mkdir %s"; + char *command; + switch_path(APATH); + command = my_malloc(2 * strlen(m_path.path) + sizeof(format), + "commit_create"); + sprintf(command, format, m_path.path, m_path.path); + system(command); + my_free(command); + switch_path(CPATH); +#endif + return; +} + + +/************************************************************* + perform_delete_node - + delete node in current path +*************************************************************/ +static void perform_delete_node() +{ +#if BITWISE + static char format[]="rm -f -r %s"; + char *command; + command = my_malloc(strlen(m_path.path) + sizeof(format), + "commit_delete"); + sprintf(command, format, m_path.path); + system(command); + my_free(command); +#endif + return; +} +static void perform_move() +{ +#if BITWISE + static char format[] = "rm -r -f %s;mkdir %s;mv %s/" VAL_NAME " %s"; + char *a_path; + char *command; + switch_path(APATH); + a_path = my_strdup(m_path.path, ""); + switch_path(CPATH); + command = my_malloc(sizeof(format)+3*strlen(a_path)+strlen(m_path.path),""); + sprintf(command, format, a_path, a_path, m_path.path, a_path); + system(command); + my_free(command); + my_free(a_path); +#endif + return; +} + +/************************************************* + commit_update_child: + preconditions: t_path and c_path set up for parent + pdefp (IN) - parent definition (may be NULL) + child (IN) - name of the child + creating (IN) - mode of commiting (update or create) + update_parent (OUT) - unfulfilled update request + commit child and its descendants + returns TRUE if no errors + exit if error during execution +*/ +boolean commit_update_child(vtw_def *pdefp, char *child, + boolean creating, boolean in_txn, boolean *update_parent) +{ + boolean update_pending = FALSE; + boolean multi_tag = FALSE; + struct stat statbuf; + int status; + vtw_def def; + vtw_def *my_defp; /*def to be given to my children */ + vtw_def *act_defp;/*def to be used for actions */ + char *cp; + vtw_mark mark; + vtw_act_type act; + boolean do_end, ok, do_begin = FALSE, do_txn = FALSE; + + set_at_string(NULL); + + ok = TRUE; +#ifdef DEBUG + printf("commiting directory (node_cnt %d, free_node_cnt %d)\n" + "mpath |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + if (strcmp(child, ".") == 0 || + strcmp(child, "..") == 0 || + strcmp(child, MOD_NAME) == 0 || + strcmp(child, LOCK_NAME) == 0 || + strcmp(child, opaque_name) == 0) + return TRUE; + + /* deleted subdirectory */ + if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */ + /* ignore */ + return TRUE; + } + mark_paths(&mark); + if (!creating) { + /* are we marked with opaque */ + push_path(&m_path, child); + push_path(&m_path, opaque_name); + if (lstat(m_path.path, &statbuf) >= 0 && !creating) { + creating = TRUE; + } + pop_path(&m_path); + pop_path(&m_path); + } + /* find our definition */ + if (pdefp && pdefp->tag) { + my_defp = NULL; + act_defp = pdefp; + push_path(&t_path,tag_name); + } else { + push_path(&t_path, child); + push_path(&t_path, def_name); + if ((lstat(t_path.path, &statbuf) >= 0) && + ((statbuf.st_mode & S_IFMT) == S_IFREG)) { + /* defniition present */ + act_defp = my_defp = &def; + memset(&def, 0, sizeof(def)); +#ifdef DEBUG1 + printf("Parsing definition\n"); +#endif + if ((status = parse_def(&def, t_path.path, + FALSE))) + exit(status); + my_defp = act_defp = &def; + if (def.tag) + multi_tag = TRUE; + } else { + my_defp = act_defp = NULL; + } + pop_path(&t_path); /*def_name */ + } + do_end = FALSE; + if (!multi_tag && !in_txn && act_defp && + (act_defp->actions[begin_act].vtw_list_head || + act_defp->actions[end_act].vtw_list_head)){ + /* we are traversing change directory + if we are here, there is a change somewhere */ + do_txn = in_txn = TRUE; + if (act_defp->actions[end_act].vtw_list_head) + do_end = TRUE; + /* if creating, delete skipped this directory, + we have to do begin act */ + if (creating && + act_defp->actions[begin_act].vtw_list_head) + do_begin = TRUE; + } + push_path(&m_path, child); + /* are we a "value" parent node */ + if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) { + /* we are value node */ + if (!act_defp->multi) { + /* single node */ + /* create_mode do create, + update_mode do create or update */ + + if (creating) + act = create_act; + else + act = update_act; + + if ((act==update_act) && !act_defp->actions[update_act].vtw_list_head){ + /* updating but no action + ask parent to do - propagate up */ + *update_parent = TRUE; + } + /* look for actions */ + /* if act != create_act => run actions[act] if not empty */ + /* if act == create_act + * if actions[create_act] not empty + * run it + * else + * run actions[update_act] if not empty + * run actions[activate_act] if not empty + */ + if (act_defp->actions[act].vtw_list_head + || (act == create_act + && (act_defp->actions[activate_act].vtw_list_head + || act_defp->actions[update_act].vtw_list_head))) { + status = get_value_to_at_string(&m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + /* remove \n at the line end */ + cp = strchr(get_at_string(), '\n'); + if (cp) + *cp = 0; + if (act_defp->actions[act].vtw_list_head) { + status = execute_list(act_defp->actions[act].vtw_list_head, + act_defp); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } else { + /* creating but no create action */ + /* try update action */ + if ((act == create_act) + && act_defp->actions[update_act].vtw_list_head) { + status + = execute_list(act_defp->actions[update_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } + } + /* try activate action if creating */ + if ((act == create_act) + && act_defp->actions[activate_act].vtw_list_head) { + status + = execute_list(act_defp->actions[activate_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } + free_at_string(); + } + /* now handle commit value */ + if (!in_txn) { /* ELSE WAIT TILL THE END OF TXN */ + perform_move(); + perform_delete_node(); + } + goto restore; + } + /* else multi_node */ + cp = NULL; + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + ok = commit_value(act_defp, cp, creating?create_mode:update_mode, in_txn); + if (cp) + my_free(cp); + goto restore; + } + /* else not a value */ + /* regular */ + /* do not do anything for tag type multinode */ + if (!multi_tag && creating) { + set_at_string(child); /* for expand inside actions */ + if (do_begin) { + status = execute_list(act_defp-> + actions[begin_act]. + vtw_list_head, act_defp); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + + if (act_defp) { + if (act_defp->actions[create_act].vtw_list_head) { + status + = execute_list(act_defp->actions[create_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } else if (act_defp->actions[update_act].vtw_list_head) { + /* no create action => use update action */ + status + = execute_list(act_defp->actions[update_act].vtw_list_head, + act_defp); + if (!status) { + return (FALSE); + } + } + /* not trying activate action here (activate after children are + * configured) + */ + } + } + if (creating && !in_txn) /* ELSE WAIT TILL THE END OF TXN */ + perform_create_node(); + /* children */ + ok = commit_update_children(my_defp, creating, in_txn, &update_pending); + if (!ok) + return(FALSE); + + if (update_pending){ + if (!multi_tag && act_defp && + act_defp->actions[update_act].vtw_list_head){ + set_at_string(child); /* for expand inside actions */ + ok = execute_list(act_defp->actions[update_act]. + vtw_list_head, act_defp); + if (!ok) + return(FALSE); + /* update_pending = FALSE; */ + } else + *update_parent = TRUE; + } + if (creating && !multi_tag && act_defp && + act_defp->actions[activate_act].vtw_list_head){ + set_at_string(child); /* for expand inside actions */ + ok = execute_list(act_defp->actions[activate_act]. + vtw_list_head, act_defp); + /* ignore result */ + } + if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */ + perform_delete_node(); + restore: + if (do_end){ + set_at_string(child); + ok = execute_list(act_defp->actions[end_act]. + vtw_list_head, act_defp); + } +#if BITWISE + if (do_txn && ok) { + int len; + char *command; + char format1[]="rm -r -f %s/*;cp -r -f %s/%s %s"; + char format2[]="rm -r -f %s/%s;mv %s/%s %s"; + char format3[]="rm -r -f %s/%s"; + restore_paths(&mark); + switch_path(MPATH); + len = sizeof(format1) + 2 * strlen(get_tmpp()) + + strlen(m_path.path) + strlen(child); + command = my_malloc(len, ""); + sprintf(command, format1, get_tmpp(), m_path.path, child, + get_tmpp()); + system(command); + my_free(command); + switch_path(APATH); + len = sizeof(format2) + 2 *strlen(m_path.path) + + 2 * strlen( child) + strlen(get_tmpp()); + command = my_malloc(len, ""); + sprintf(command, format2, m_path.path, child, get_tmpp(), + child, m_path.path); + system(command); + my_free(command); + switch_path(CPATH); + len = sizeof(format3) + strlen(m_path.path) + + strlen( child); + command = my_malloc(len, ""); + sprintf(command, format3, m_path.path, child); + system(command); + my_free(command); + } +#endif + restore_paths(&mark); + if (my_defp && my_defp != pdefp) + free_def(my_defp); + return ok; +} + + +/************************************************* + commit_delete_child: + preconditions: t_path and c_path set up for parent + pdefp (IN) - parent definition (may be NULL) + child (IN) - name of the child + do_del (IN) - if FALSE we are looking for delete target + if TRUE, we found and switched to A (working) PATH + commit deleted child and its descendants + returns TRUE if no errors + exit if error during execution +*/ +static boolean commit_delete_child(vtw_def *pdefp, char *child, + boolean deleting, boolean in_txn) +{ + boolean do_children, multi_tag = FALSE; + struct stat statbuf; + int status; + vtw_def def; + vtw_def *my_defp; /*def to be given to my children */ + vtw_def *act_defp;/*def to be used for actions */ + char *cp; + vtw_mark mark; + boolean ok, do_txn = FALSE; + int st; + ok = TRUE; + +#ifdef DEBUG + printf("commiting directory (node_cnt %d, free_node_cnt %d)\n" + "mpath |%s|\n", + node_cnt, free_node_cnt, t_path.path); +#endif + + /* deleted subdirectory */ + if (strncmp(child, ".wh.", 4) == 0) { /* whiteout */ + if (deleting) + /* in do_delete mode we traverse A hierarchy, no white-outs possible */ + INTERNAL; /* it is exit */ + else { + /* deal with counterpart in working */ + switch_path(APATH); + push_path(&m_path, child+4); + st = lstat(m_path.path, &statbuf); + pop_path(&m_path); + if (st >= 0){ + /*get rid of ".wh. part in child name"*/ + /* deleting mode will handle txn, both + begin and end + */ + ok = commit_delete_child(pdefp, child + 4, TRUE, in_txn); + }else { + /* I do not understand how we could be here */ + printf("Mystery #1\n"); + ok = TRUE; + } + switch_path(CPATH); + if (ok) { + /* delete whiteout */ + if (!in_txn){ /*ELSE WAIT TILL THE END OF TXN*/ + push_path(&m_path, child); + perform_delete_node(); + pop_path(&m_path); + } + } + return ok; + } + /* done with whiteouts */ + } + /* not white out */ + mark_paths(&mark); + if (!deleting) { + int status; + /* are we marked with opaque */ + push_path(&m_path, child); + push_path(&m_path, opaque_name); + status = lstat(m_path.path, &statbuf); + pop_path(&m_path); + pop_path(&m_path); + if (status >= 0) { + /* brand new directory, nothing is + deleted there; + update will handle txn (both begin and end) + */ + return TRUE; + } + } + /* find our definition */ + if (pdefp && pdefp->tag) { + /* parent is a tag, node is a tag value node */ + my_defp = NULL; + act_defp = pdefp; + push_path(&t_path,tag_name); + } else { + push_path(&t_path, child); + push_path(&t_path, def_name); + if ((lstat(t_path.path, &statbuf) >= 0) && + ((statbuf.st_mode & S_IFMT) == S_IFREG)) { + /* defniition present */ + act_defp = my_defp = &def; + memset(&def, 0, sizeof(def)); +#ifdef DEBUG1 + printf("Parsing definition\n"); +#endif + if ((status = parse_def(&def, t_path.path, + FALSE))) + exit(status); + my_defp = act_defp = &def; + if (def.tag) + multi_tag = TRUE; /* tag node itself*/ + } else { + my_defp = act_defp = NULL; + } + pop_path(&t_path); /*def_name */ + } + push_path(&m_path, child); + if (!multi_tag) { + set_at_string(child); /* for expand inside actions */ + /* deal with txn */ + if (!in_txn && act_defp && + (act_defp->actions[begin_act].vtw_list_head || + act_defp->actions[end_act].vtw_list_head)) { + /* if we are here we have change, + we either in do_del and our node is change node + or we are in change directory and our node is + change node also */ + if (deleting) + /* if not deleting, update will handle values */ + do_txn = TRUE; + in_txn = TRUE; + if (act_defp->actions[begin_act].vtw_list_head){ + status = execute_list(act_defp->actions[begin_act]. + vtw_list_head, act_defp); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + } + } + /* are we a "value" parent node */ + if (act_defp && !act_defp->tag && act_defp->def_type != ERROR_TYPE ) { + /* we are value node */ + if (!act_defp->multi) { + /* single node */ + if (!deleting) { + /* if it was whiteout, it was converted to do_del_mode */ + restore_paths(&mark); + return ok; + } + + /* do we have actions */ + if (act_defp->actions[delete_act].vtw_list_head){ + status = get_value_to_at_string(&m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + /* remove \n at the line end */ + cp = strchr(get_at_string(), '\n'); + if (cp) + *cp = 0; + if (act_defp->actions[delete_act].vtw_list_head) { + set_in_delete_action(TRUE); + status = execute_list(act_defp->actions[delete_act]. + vtw_list_head, act_defp); + set_in_delete_action(FALSE); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + free_at_string(); + } + /* now handle commit value */ + if (!in_txn) /* ELSE WAIT TILL THE END OF TXN */ + perform_delete_node(); + goto restore; + } + /* else multi_node */ + cp = NULL; + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Can not read value at %s", m_path.path); + ok = commit_value(act_defp, cp, deleting?do_del_mode:del_mode,in_txn); + if (cp) + my_free(cp); + goto restore; + } + /* else not a value */ + /* regular */ + do_children = TRUE; + /* do not do anything for tag itself, all action belong to values */ + if (!multi_tag) { + set_at_string(child); /* for expand inside actions */ + if (deleting) { + if (act_defp && + act_defp->actions[delete_act].vtw_list_head){ + do_children = FALSE; + set_in_delete_action(TRUE); + status = execute_list(act_defp->actions[delete_act]. + vtw_list_head, act_defp); + set_in_delete_action(FALSE); + if (!status) { +#ifdef DEBUG + bye("begin action not NULL for %s\n", get_at_string()); +#else + return(FALSE); +#endif + } + } + } + } + /* children */ + if (do_children){ + ok = commit_delete_children(my_defp, deleting, in_txn); + if (!ok) + goto restore; + } + if (deleting) { + if (do_txn && act_defp && + act_defp->actions[end_act].vtw_list_head) { + set_at_string(child); + ok = execute_list(act_defp->actions[end_act]. + vtw_list_head, act_defp); + if (!ok) + goto restore; + } + /* delete node and all its descendants */ + if (!in_txn || do_txn)/* ELSE WAIT TILL THE END OF TXN */ + perform_delete_node(); + } + restore: + restore_paths(&mark); + if (my_defp && my_defp != pdefp) + free_def(my_defp); + return ok; +} + +static boolean commit_delete_children(vtw_def *defp, boolean deleting, + boolean in_txn) +{ + DIR *dp; + int status; + struct dirent *dirp; + boolean ok = TRUE; + char *child; + vtw_type_e type; + valstruct mvals; + boolean first; + char *cp; + int elem, curi; + vtw_sorted cur_sorted; + char *uename = NULL; + + if ((dp = opendir(m_path.path)) == NULL){ + if (deleting) + return TRUE; + INTERNAL; + } + if (defp) + type = defp->def_type; + else + type = TEXT_TYPE; + if (type == ERROR_TYPE) + type = TEXT_TYPE; + first = TRUE; + memset(&mvals, 0, sizeof (valstruct)); + memset(&cur_sorted, 0, sizeof(vtw_sorted)); + + while ((dirp = readdir(dp)) != NULL) { + child = dirp->d_name; + if (strcmp(child, ".") == 0 || + strcmp(child, "..") == 0 || + strcmp(child, MOD_NAME) == 0 || + strcmp(child, LOCK_NAME) == 0 || + strcmp(child, OPQ_NAME) == 0) + continue; + uename = clind_unescape(child); + cp = uename; + if (first) { + mvals.free_me = TRUE; + mvals.val = cp; + mvals.val_type = type; + first = FALSE; + } else { + if (mvals.cnt%MULTI_ALLOC == 0) { + /* convert into multivalue */ + mvals.vals = my_realloc(mvals.vals, + (mvals.cnt + MULTI_ALLOC) * + sizeof(char *), "add_value"); + if (mvals.cnt == 0) { /* single value - convert */ + mvals.vals[0] = mvals.val; + mvals.cnt= 1; + mvals.val = NULL; + } + } + mvals.vals[mvals.cnt] = cp; + ++mvals.cnt; + } + } + status = closedir(dp); + if (status) + INTERNAL; + if (first) { + return TRUE; + } + vtw_sort(&mvals, &cur_sorted); + for (curi = 0; curi < cur_sorted.num && ok; ++curi){ + if (type == TEXT_TYPE || + type == BOOL_TYPE) + child = (char *)(cur_sorted.ptrs[curi]); + else { + elem = (((unsigned int *)(cur_sorted.ptrs[curi]))- + cur_sorted.parts)/ + cur_sorted.partnum; + child = mvals.cnt?mvals.vals[elem]: + mvals.val; + } + ok = commit_delete_child(defp, child, deleting, in_txn); + } + free_val(&mvals); + free_sorted(&cur_sorted); + return ok; +} + +static boolean commit_update_children(vtw_def *defp, boolean creating, + boolean in_txn, boolean *parent_update) +{ + DIR *dp; + int status; + struct dirent *dirp; + boolean ok = TRUE; + char *child; + vtw_type_e type; + valstruct mvals; + boolean first; + char *cp; + int elem, curi; + vtw_sorted cur_sorted; + char *uename = NULL; + + + if ((dp = opendir(m_path.path)) == NULL){ + printf("%s:%d: opendir error: path=%s\n", + __FUNCTION__,__LINE__,m_path.path); + INTERNAL; + } + + memset(&mvals, 0, sizeof (valstruct)); + memset(&cur_sorted, 0, sizeof(vtw_sorted)); + if (defp) + type = defp->def_type; + else + type = TEXT_TYPE; + if (type == ERROR_TYPE) + type = TEXT_TYPE; + first = TRUE; + + while ((dirp = readdir(dp)) != NULL) { + child = dirp->d_name; + if (strcmp(child, ".") == 0 || + strcmp(child, "..") == 0 || + strcmp(child, MOD_NAME) == 0 || + strcmp(child, LOCK_NAME) == 0 || + strcmp(child, OPQ_NAME) == 0) + continue; + cp = uename = clind_unescape(child); + if (first) { + mvals.free_me = TRUE; + mvals.val = cp; + mvals.val_type = type; + first = FALSE; + } else { + if (mvals.cnt%MULTI_ALLOC == 0) { + /* convert into multivalue */ + mvals.vals = my_realloc(mvals.vals, + (mvals.cnt + MULTI_ALLOC) * + sizeof(char *), "add_value"); + if (mvals.cnt == 0) { /* single value - convert */ + mvals.vals[0] = mvals.val; + mvals.cnt= 1; + mvals.val = NULL; + } + } + mvals.vals[mvals.cnt] = cp; + ++mvals.cnt; + } + } + status = closedir(dp); + if (status) + INTERNAL; + if (first) { + if (uename) + my_free(uename); + return TRUE; + } + vtw_sort(&mvals, &cur_sorted); + for (curi = 0; curi < cur_sorted.num && ok; ++curi){ + if (type == TEXT_TYPE || + type == BOOL_TYPE) + child = (char *)(cur_sorted.ptrs[curi]); + else { + elem = (((unsigned int *)(cur_sorted.ptrs[curi]))- + cur_sorted.parts)/ + cur_sorted.partnum; + child = mvals.cnt?mvals.vals[elem]: + mvals.val; + } + ok = commit_update_child(defp, child, creating, in_txn, parent_update); + } + free_val(&mvals); + free_sorted(&cur_sorted); + return ok; +} + + +/************************************************* + commit_value: + executes commit for the value leave node +**************************************************/ +static boolean commit_value(vtw_def *defp, char *cp, + vtw_cmode mode, boolean in_txn) +{ + + valstruct act_value; + int status; + int curi,acti, partnum, res=0; + void *actp, *curp; + boolean no_shadow; + boolean ok; + int total, a_res, c_res; + char **a_ptr, **c_ptr, *val_string; + boolean cur_pr_val; + int pr_index; + int sign; + boolean creating; + vtw_node *actions; + valstruct cur_value; + vtw_sorted cur_sorted; + vtw_sorted act_sorted; + + ok = TRUE; + actions = NULL; + if(mode == del_mode || mode == do_del_mode) { + creating = FALSE; + if (defp && defp->actions[delete_act].vtw_list_head) { + set_in_delete_action(TRUE); + actions = defp->actions[delete_act].vtw_list_head; + } + } else { + creating = TRUE; + if (defp && defp->actions[create_act].vtw_list_head) + actions = defp->actions[create_act].vtw_list_head; + } + /* prepare cur_value */ + + status = char2val(defp, cp, &cur_value); + if (mode != do_del_mode && mode != create_mode) { + /* get active value */ + switch_path(APATH); /* switch form CCD to ACD */ + status = get_value(&cp, &m_path); + switch_path(CPATH); /* back to CCD */ + if (status != VTWERR_OK) { + no_shadow = TRUE; + }else + no_shadow = FALSE; + } else { + no_shadow = TRUE; + } + vtw_sort(&cur_value, &cur_sorted); + if(no_shadow) { + act_sorted.num = 0; + }else { + status = char2val(defp, cp, &act_value); + if (status != VTWERR_OK) { + INTERNAL; + } + /* sort them */ + vtw_sort(&act_value, &act_sorted); + } + if (mode == do_del_mode) { + /* it was actually act_sorted, not cur_sorted */ + act_sorted = cur_sorted; + cur_sorted.num = 0; + act_value = cur_value; + /* act_value will be freed by freeing cur_value + do not zero out it here */ + } + + acti = 0; + curi = 0; + total = act_sorted.num + cur_sorted.num; + a_res=0; + c_res=0; + a_ptr = my_malloc(total*sizeof(char *), ""); + c_ptr = my_malloc(total*sizeof(char *), ""); + while (acti < act_sorted.num || curi < cur_sorted.num) { + if (acti == act_sorted.num) { + cur_pr_val = TRUE; + pr_index = curi; + sign = +1; + ++curi; + } else if (curi == cur_sorted.num) { + cur_pr_val = FALSE; + pr_index = acti; + sign = -1; + ++acti; + } else { + /* compare */ + actp = act_sorted.ptrs[acti]; + curp = cur_sorted.ptrs[curi]; + /* compare */ + if (act_sorted.partnum){ + for(partnum = 0; partnum < act_sorted.partnum; + ++partnum) { + res = *((int *)actp + partnum) - + *((int *)curp + partnum); + if (res) + break; + } + } else{ + res = strcmp((char *)actp, (char *) curp); + } + if (res == 0) { + /* the same */ + cur_pr_val = TRUE; + pr_index = curi; + sign = 0; + ++acti; + ++curi; + } else if (res < 0) { + /* act < cur, act is unmatched */ + cur_pr_val = FALSE; + pr_index = acti; + sign = -1; + ++acti; + }else { + /* cur < act, cur is unmatched */ + cur_pr_val = TRUE; + pr_index = curi; + sign = 1; + ++curi; + } + } + if (defp->def_type == TEXT_TYPE || + defp->def_type == BOOL_TYPE) { + val_string = cur_pr_val? + ((char *)(cur_sorted.ptrs[pr_index])): + ((char *)(act_sorted.ptrs[pr_index])); + } else { + if (cur_pr_val) { + int elem = (((unsigned int *)(cur_sorted.ptrs[pr_index]))- + cur_sorted.parts)/ + cur_sorted.partnum; + val_string = cur_value.cnt?cur_value.vals[elem]: + cur_value.val; + } else { + int elem = (((unsigned int *)(act_sorted.ptrs[pr_index]))- + act_sorted.parts)/ + act_sorted.partnum; + val_string = act_value.cnt?act_value.vals[elem]: + act_value.val; + } + } + set_at_string(val_string); + switch (sign) { + case 0: /* found in both, no actions, include in both */ + a_ptr[a_res++]=val_string; + c_ptr[c_res++]=val_string; + break; + case 1: /* found only in change */ + if (ok && creating) { + if (actions) { + /* do create action */ + ok = execute_list(actions, defp); + } else if (defp && defp->actions[update_act].vtw_list_head) { + /* no create action => use update action */ + ok = execute_list(defp->actions[update_act].vtw_list_head, defp); + } + if (ok && defp && defp->actions[activate_act].vtw_list_head) { + /* try activate action */ + ok = execute_list(defp->actions[activate_act].vtw_list_head, defp); + } + /* if succ, make it look old */ + if(ok) + a_ptr[a_res++]=val_string; + } + c_ptr[c_res++]=val_string; /* in all cases */ + break; + case -1: /* found only in working */ + if (ok && !creating && actions) {/* ok and deleting */ + ok = execute_list(actions, defp); + } + /* if succ and deleting - do nothing, else */ + if (!ok || creating) + a_ptr[a_res++]=val_string; + } + } + if (creating && ok) + c_res = 0; +#if BITWISE + if (!in_txn) {/* ELSE WAIT TILL THE END OF TXN */ + switch_path(APATH); + if (a_res) { + make_dir(); + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + for(i=0;i<a_res;++i) + if (fputs(a_ptr[i], fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + fclose(fp); + pop_path(&m_path); + }else{ + perform_delete_node(); + } + switch_path(CPATH); + if (c_res) { + make_dir(); + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + for(i=0;i<c_res;++i) + if (fputs(c_ptr[i], fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + fclose(fp); + pop_path(&m_path); + }else{ + perform_delete_node(); + } + } +#endif /*BITWISE*/ + if (mode == do_del_mode) + switch_path(APATH); + else + switch_path(CPATH); + if(act_sorted.num) + free_sorted(&act_sorted); + if(cur_sorted.num) + free_sorted(&cur_sorted); + my_free(a_ptr); + my_free(c_ptr); + free_val(&cur_value); + if (!no_shadow) + free_val(&act_value); + set_in_delete_action(FALSE); + return ok; +} + +static int fin_commit(boolean ok) +{ + char *command; + static char format1[]="cp -r -f %s/* %s"; /*mdirp, tmpp*/ + static char format2[]="sudo umount %s"; /*mdirp*/ + static char format3[]="rm -f %s/" MOD_NAME " >&/dev/null ; /bin/true"; + /*tmpp*/ + static char format4[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*cdirp*/ + static char format5[]="rm -rf %s/{.*,*} >&/dev/null ; /bin/true"; /*adirp*/ + static char format6[]="mv -f %s/* -t %s";/*tmpp, adirp*/ + static char format7[]="sudo mount -t unionfs -o dirs=%s=rw:%s=ro" + " unionfs %s"; /*cdirp, adirp, mdirp*/ + int m_len = strlen(get_mdirp()); + int t_len = strlen(get_tmpp()); + int c_len = strlen(get_cdirp()); + int a_len = strlen(get_adirp()); + set_echo(TRUE); + if (!ok){ + printf("Commit FAILED!\n"); + return -1; + } + command = my_malloc(strlen(format1) + m_len + t_len, ""); + sprintf(command, format1, get_mdirp(), get_tmpp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format2) + m_len, ""); + sprintf(command, format2, get_mdirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format3) + c_len, ""); + sprintf(command, format3, get_tmpp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format4) + c_len, ""); + sprintf(command, format4, get_cdirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format5) + a_len, ""); + sprintf(command, format5, get_adirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format6) + t_len + a_len, ""); + sprintf(command, format6, get_tmpp(), get_adirp()); + system(command); + my_free(command); + + command = my_malloc(strlen(format7) + c_len + a_len + m_len, ""); + sprintf(command, format7, get_cdirp(), get_adirp(), get_mdirp()); + system(command); + my_free(command); + + return 0; +} + diff --git a/src/delete.c b/src/delete.c new file mode 100644 index 0000000..a825a22 --- /dev/null +++ b/src/delete.c @@ -0,0 +1,258 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <sys/stat.h> +#include <dirent.h> + +#include "cli_val.h" +#include "cli_objects.h" + +static void remove_rf(boolean do_umount) +{ + char *command; + touch(); + if (do_umount) { + command = my_malloc(strlen(get_mdirp()) + 20, "delete"); + sprintf(command, "sudo umount %s", get_mdirp()); + system(command); + free(command); + } + command = my_malloc(strlen(m_path.path) + 10, "delete"); + sprintf(command, "rm -rf %s", m_path.path); + system(command); + free(command); + if (do_umount) { + command = my_malloc(strlen(get_mdirp()) + strlen(get_cdirp()) + + strlen(get_mdirp()) + 100, + "delete"); + sprintf(command, "sudo mount -t unionfs -o dirs=%s=rw:%s=ro:" + " unionfs %s", get_cdirp(), get_adirp(), get_mdirp()); + system(command); + free(command); + } +} +/*************************************************** + set_validate: + validate value against definition + return TRUE if OK, FALSE otherwise +****************************************************/ +boolean set_validate(vtw_def *defp, char *valp) +{ + boolean res; + int status; + struct stat statbuf; + + pop_path(&t_path); /* it was tag or real value */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0 || + (statbuf.st_mode & S_IFMT) != S_IFREG) { + bye("Can not set value (2), no definition for %s", m_path.path); + } + /* defniition present */ + memset(defp, 0, sizeof(vtw_def)); + if ((status = parse_def(defp, t_path.path, FALSE))) + exit(status); + res = validate_value(defp, valp); + pop_path(&t_path); + return res; +} + +int main(int argc, char **argv) +{ + int ai; + struct stat statbuf; + vtw_def def; + boolean last_tag=0; + int status; + FILE *fp; + boolean res; + char *cp, *delp, *endp; + boolean do_umount; + + if (argc < 2) { + fprintf(stderr, "Need to specify the config node to delete\n"); + exit(1); + } + + dump_log( argc, argv); + do_umount = FALSE; + init_edit(); + /* extend both paths per arguments given */ + /* last argument is new value */ + for (ai = 1; ai < argc; ++ai) { + push_path(&t_path, argv[ai]); + push_path(&m_path, argv[ai]); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = FALSE; + continue; + } /*else */ + pop_path(&t_path); + push_path(&t_path, TAG_NAME); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = TRUE; + continue; + } + /* no match */ + break; + } + /* + cases: + multiple tag-value - not achild + mutilple tag-value - not the last child + multiple tag-value - last child + single value modified + signle value unmodified + multiple non-tag value - the last value + multiple non-tag value - not the last value + regular child + */ + if (ai == argc) { + /* full path found */ + /* all cases except multiple non-tag value */ + /* check for single value */ + if (last_tag) { + /* case of multiple tag-value + was this a real child? + was it the last child? + */ + struct dirent *dirp; + DIR *dp; + + if (lstat(m_path.path, &statbuf) < 0) + bye("Nothing to delete at %s", m_path.path); + remove_rf(FALSE); + pop_path(&m_path); + if ((dp = opendir(m_path.path)) == NULL){ + INTERNAL; + } + while ((dirp = readdir(dp)) != NULL) { + /*do we have real child */ + if (strcmp(dirp->d_name, ".") && + strcmp(dirp->d_name, "..") && + strcmp(dirp->d_name, MOD_NAME) && /* XXX */ + strcmp(dirp->d_name, LOCK_NAME) && /* XXX */ + strncmp(dirp->d_name, ".wh.", 4) ) + break; + } + if (dirp == NULL) { + /* no real children left */ + /* kill parent also */ + remove_rf(FALSE); + } + exit(0); + } + /*not tag */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) >= 0 && + (statbuf.st_mode & S_IFMT) == S_IFREG) { + /* defniition present */ + memset(&def, 0, sizeof(vtw_def)); + if ((status = parse_def(&def, t_path.path, FALSE))) + exit(status); + if (!def.tag && !def.multi && def.def_type!= ERROR_TYPE) { + /* signgle value */ + /* is it modified == + it is in C, but not OPAQUE */ + switch_path(CPATH); + if(lstat(m_path.path, &statbuf) >= 0) { + push_path(&m_path, OPQ_NAME); + if(lstat(m_path.path, &statbuf) < 0) { + /* yes remove from C only */ + pop_path(&m_path); + remove_rf(TRUE); + exit(0); + } + pop_path(&m_path); /*OPQ_NAME */ + } + switch_path(MPATH); + } + } + /* else no defnition, remove it also */ + remove_rf(FALSE); + exit(0); + } + if(ai < argc -1 || last_tag) { + bye("There is no appropriate template for %s", + m_path.path + strlen(get_mdirp())); + } + /*ai == argc -1, must be actual value */ + pop_path(&m_path); /*it was value, not path segment */ + push_path(&m_path, VAL_NAME); + /* set value */ + if (lstat(m_path.path, &statbuf) < 0) + bye("Nothing to delete at %s", m_path.path); + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + bye("Not a regular file %s", m_path.path); + /* get definition to deal with potential multi */ + pop_path(&t_path); /* it was tag or real value */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0 || + (statbuf.st_mode & S_IFMT) != S_IFREG) { + bye("Can not delete value, no definition for %s", m_path.path); + } + /* defniition present */ + memset(&def, 0, sizeof(vtw_def)); + if ((status = parse_def(&def, t_path.path, FALSE))) + exit(status); + if (def.multi) { + /* delete from multivalue */ + valstruct new_value, old_value; + status = char2val(&def, argv[argc - 1], &new_value); + if (status) + exit(0); + cp = NULL; + pop_path(&m_path); /* get_value will push VAL_NAME */ + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Cannot read old value %s\n", m_path.path); + status = char2val(&def, cp, &old_value); + if (status != VTWERR_OK) + bye("Corrupted old value ---- \n%s\n-----\n", cp); + res = val_cmp(&new_value, &old_value, IN_COND); + if (!res) + bye("Not in multivalue"); + touch(); + if (old_value.cnt) { + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + if (is_in_cond_tik()) { + for(delp=cp;delp && is_in_cond_tik(); dec_in_cond_tik()) { + delp = strchr(delp, '\n'); + if (!delp) + INTERNAL; + ++delp; /* over \n */ + } + /* write "left" of deleted */ + fwrite(cp, 1, delp-cp, fp); + }else + delp = cp; + /* find end of value */ + endp = strchr(delp, '\n'); + if (endp && *++endp) { + /* write "right" of deleted */ + fwrite(endp, 1, strlen(endp), fp); + /* need the final '\n' */ + fwrite("\n", 1, 1, fp); + } + fclose(fp); + return 0; + } + /* it multi with only 1 value, remove */ + remove_rf(FALSE); + return 0; + } + bye("There is no appropriate template for %s", + m_path.path + strlen(get_mdirp())); + + return 0; +} + diff --git a/src/set.c b/src/set.c new file mode 100644 index 0000000..566dfe0 --- /dev/null +++ b/src/set.c @@ -0,0 +1,310 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> + +#include "cli_val.h" +#include "cli_objects.h" +#include "cli_path_utils.h" + +static void make_dir(void); +static void handle_defaults(void); + +static void make_dir() +{ + touch_dir(m_path.path); +} +/*************************************************** + set_validate: + validate value against definition + return TRUE if OK, FALSE otherwise +****************************************************/ +boolean set_validate(vtw_def *defp, char *valp, boolean empty_val) +{ + boolean res; + int status; + struct stat statbuf; + char* path_end=NULL; + + if (!empty_val) { + int i = 0; + int val_len = strlen(valp); + + for (i = 0; i < val_len; i++) { + if (valp[i] == '\'') { + fprintf(stderr, "Cannot use the \"'\" (single quote) character " + "in a value string\n"); + exit(1); + } + } + + { + clind_path_ref tp = clind_path_construct(t_path.path); + if(tp) { + path_end=clind_path_pop_string(tp); + } + clind_path_destruct(&tp); + } + + pop_path(&t_path); /* it was tag or real value */ + + } + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0 || + (statbuf.st_mode & S_IFMT) != S_IFREG) { + bye("Can not set value (4), no definition for %s, template %s", + m_path.path,t_path.path); + } + /* defniition present */ + memset(defp, 0, sizeof(vtw_def)); + if ((status = parse_def(defp, t_path.path, FALSE))) + exit(status); + pop_path(&t_path); + if(path_end) { + push_path(&t_path,path_end); + free(path_end); + path_end=NULL; + } + if (empty_val) { + if (defp->def_type != TEXT_TYPE || defp->tag || defp->multi){ + printf("Empty string may be assigned only to TEXT type leaf node\n"); + return FALSE; + } + return TRUE; + } + res = validate_value(defp, valp); + return res; +} + +int main(int argc, char **argv) +{ + + int ai; + struct stat statbuf; + vtw_def def; + boolean last_tag; + int status; + FILE *fp; + boolean res; + char *cp; + char *command; + boolean need_mod = FALSE, not_new = FALSE; + boolean empty_val = FALSE; + + dump_log( argc, argv); + init_edit(); + last_tag = FALSE; + + /* extend both paths per arguments given */ + /* last argument is new value */ + for (ai = 1; ai < argc; ++ai) { + if (!*argv[ai]) { /* empty string */ + if (ai < argc -1) { + bye("empty string in argument list \n"); + } + empty_val = TRUE; + last_tag = FALSE; + break; + } + push_path(&t_path, argv[ai]); + push_path(&m_path, argv[ai]); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = FALSE; + continue; + } /*else */ + pop_path(&t_path); + push_path(&t_path, TAG_NAME); + if (lstat(t_path.path, &statbuf) >= 0) { + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("INTERNAL:regular file %s in templates", t_path.path); + } + last_tag = TRUE; + /* every time tag match, need to verify*/ + if(!set_validate(&def, argv[ai], FALSE)) { + exit(1); + } + continue; + } + /* no match */ + break; + } + + if (ai == argc) { + /* full path found */ + /* every tag match validated already */ + /* non tag matches are OK by definition */ + /* do we already have it? */ + if (lstat(m_path.path, &statbuf) >= 0) + bye("Already exists %s", m_path.path + strlen(get_mdirp())); + /* else */ + /* prevent value node without actual value */ + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) >= 0) { + memset(&def, 0, sizeof(vtw_def)); + if ((status = parse_def(&def, t_path.path, FALSE))) + exit(status); + if (def.def_type != ERROR_TYPE && !def.tag) + bye("Must provide actual value\n"); + if (def.def_type == ERROR_TYPE && !def.tag) { + pop_path(&t_path); + if(!validate_value(&def, "")) { + exit(1); + } + push_path(&t_path, DEF_NAME); + } + } + touch(); + pop_path(&t_path); + make_dir(); + handle_defaults(); + exit(0); + } + if(ai < argc -1 || last_tag) { + fprintf(stderr, "There is no appropriate template for %s", + m_path.path + strlen(get_mdirp())); + exit(1); + } + /*ai == argc -1, must be actual value */ + if (!empty_val) + pop_path(&m_path); /*it was value, not path segment */ + + if(!set_validate(&def, argv[argc-1], empty_val)) { + exit(1); + } + push_path(&m_path, VAL_NAME); + /* set value */ + if (lstat(m_path.path, &statbuf) >= 0) { + valstruct new_value, old_value; + not_new = TRUE; + if ((statbuf.st_mode & S_IFMT) != S_IFREG) + bye("Not a regular file at path \"%s\"", m_path.path); + /* check if this new value */ + status = char2val(&def, argv[argc - 1], &new_value); + if (status) + exit(0); + cp = NULL; + pop_path(&m_path); /* get_value will push VAL_NAME */ + status = get_value(&cp, &m_path); + if (status != VTWERR_OK) + bye("Cannot read old value %s\n", m_path.path); + status = char2val(&def, cp, &old_value); + if (status != VTWERR_OK) + bye("Corrupted old value ---- \n%s\n-----\n", cp); + res = val_cmp(&new_value, &old_value, IN_COND); + if (res) { + if (def.multi) { + bye("Already in multivalue"); + } else { + bye("The same value \"%s\" for path \"%s\"\n", cp, m_path.path); + } + } + } else { + pop_path(&m_path); + } + make_dir(); + push_path(&m_path, VAL_NAME); + if(not_new && !def.multi) { + /* it is not multi and seen from M */ + /* is it in C */ + switch_path(CPATH); + if (lstat(m_path.path, &statbuf) < 0) + /* yes, we are modifying original value */ + need_mod = TRUE; + switch_path(MPATH); + } + touch(); + /* in case of multi we always append, never overwrite */ + /* in case of single we always overwrite */ + /* append and overwrite work the same for new file */ + fp = fopen(m_path.path, def.multi?"a":"w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + if (fputs(argv[argc-1], fp) < 0 || fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + if (need_mod) { + pop_path(&m_path); /* get rid of "value" */ + command = my_malloc(strlen(m_path.path) + 30, "set"); + sprintf(command, "touch %s/" MOD_NAME, m_path.path); + system(command); + } + return 0; +} +/********************************************** + handle_defaults: + now deal with defaults for children + if child has definition and not tag, nor multi, and + has type, and has default, and not have value + already, make a default value +*/ + + +static void handle_defaults() +{ + DIR *dp; + int status; + struct dirent *dirp; + struct stat statbuf; + FILE *fp; + vtw_def def; + char *uename; + + if ((dp = opendir(t_path.path)) == NULL){ + INTERNAL; + } + while ((dirp = readdir(dp)) != NULL) { + if (strcmp(dirp->d_name, ".")==0 || + strcmp(dirp->d_name, "..")==0 || + strcmp(dirp->d_name, MOD_NAME) == 0 || + strcmp(dirp->d_name, LOCK_NAME) == 0 || + strcmp(dirp->d_name, DEF_NAME)==0) + continue; + uename = clind_unescape(dirp->d_name); + push_path(&t_path, uename); + if (lstat(t_path.path, &statbuf) < 0) { + bye("Cannot stat template directory %s\n", + t_path.path); + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + bye("Non directory file %s\n", t_path.path); + } + push_path(&t_path, DEF_NAME); + if (lstat(t_path.path, &statbuf) < 0) { + /* no definition */ + pop_path(&t_path); /* definition */ + pop_path(&t_path); /* child */ + continue; + } + memset(&def, 0, sizeof(def)); + if ((status = parse_def(&def, t_path.path, + FALSE))) + exit(status); + if (def.def_default) { + push_path(&m_path, uename); + push_path(&m_path, VAL_NAME); + if (lstat(m_path.path, &statbuf) < 0) { + /* no value, write one */ + pop_path(&m_path); + make_dir();/* make sure directory exist */ + push_path(&m_path, VAL_NAME); + fp = fopen(m_path.path, "w"); + if (fp == NULL) + bye("Can not open value file %s", m_path.path); + if (fputs(def.def_default, fp) < 0 || + fputc('\n',fp) < 0) + bye("Error writing file %s", m_path.path); + fclose(fp); + } + pop_path(&m_path); /* value */ + pop_path(&m_path); /* child */ + } + free_def(&def); + pop_path(&t_path); /* definition */ + pop_path(&t_path); /* child */ + } +} diff --git a/templates/interfaces/ethernet/node.def b/templates/interfaces/ethernet/node.def new file mode 100644 index 0000000..b5a320e --- /dev/null +++ b/templates/interfaces/ethernet/node.def @@ -0,0 +1,8 @@ +tag: +type: txt +help: "Ethernet interface name" +syntax: exec " \ + if [ -z \"`ip addr | grep $(@) `\" ]; then \ + echo ethernet interface $(@) doesn\\'t exist on this system ; \ + exit 1 ; \ + fi ; " diff --git a/templates/interfaces/ethernet/node.tag/address/node.def b/templates/interfaces/ethernet/node.tag/address/node.def new file mode 100644 index 0000000..41a0b06 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/address/node.def @@ -0,0 +1,5 @@ +multi: +type: ipv4net +help: "Set IPv4 address and prefix for this interface" +create: "ip addr add $(@) dev $(../@)"; "error setting address $(@) on dev $(../@)" +delete: "ip addr del $(@) dev $(../@)"; "error deleting address $(@) on dev $(../@)" diff --git a/templates/interfaces/ethernet/node.tag/description/node.def b/templates/interfaces/ethernet/node.tag/description/node.def new file mode 100644 index 0000000..abd0a26 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: "description for this interface" diff --git a/templates/interfaces/ethernet/node.tag/duplex/node.def b/templates/interfaces/ethernet/node.tag/duplex/node.def new file mode 100644 index 0000000..e15ef39 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/duplex/node.def @@ -0,0 +1,6 @@ +type: txt +help: "set the duplex for this interface" +syntax: $(@) in "half", "full"; "duplex must be half or full" +commit: $(../speed) != ""; "if duplex is hardcoded, speed must also be hardcoded" +create: "ethtool -s $(../@) speed $(../speed/@) duplex $(@) autoneg off" +delete: "ethtool -s $(../@) autoneg on" diff --git a/templates/interfaces/ethernet/node.tag/enable/node.def b/templates/interfaces/ethernet/node.tag/enable/node.def new file mode 100644 index 0000000..2b74d58 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/enable/node.def @@ -0,0 +1,3 @@ +help: "enable interface" +create: "ip link set $(../@) up"; "error enabling dev $(../@)" +delete: "ip link set $(../@) down"; "error disabling dev $(../@)" diff --git a/templates/interfaces/ethernet/node.tag/hw-id/node.def b/templates/interfaces/ethernet/node.tag/hw-id/node.def new file mode 100644 index 0000000..6097cff --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/hw-id/node.def @@ -0,0 +1,2 @@ +type: macaddr +help: "set the original MAC address for this interface" diff --git a/templates/interfaces/ethernet/node.tag/mac/node.def b/templates/interfaces/ethernet/node.tag/mac/node.def new file mode 100644 index 0000000..41e4313 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/mac/node.def @@ -0,0 +1,4 @@ +type: macaddr +help: "set the MAC address for this interface" +create: "ip link set $(../@) address $(@)"; "error setting MAC address on dev $(../@)" +delete: "ip link set $(../@) address $(../hw-id/@)"; "error setting MAC address on dev $(../@) to $(../hw-id/@)" diff --git a/templates/interfaces/ethernet/node.tag/mtu/node.def b/templates/interfaces/ethernet/node.tag/mtu/node.def new file mode 100644 index 0000000..33b1238 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/mtu/node.def @@ -0,0 +1,5 @@ +type: u32 +help: "set the MTU address for this interface" +syntax: $(@) >= 1 && $(@) <= 1500; "MTU must be between 1 and 1500" +create: "ip link set $(../@) mtu $(@)"; "error setting MAC address on dev $(../@)" +delete: "ip link set $(../@) mtu 1500"; "error deleteing MAC address on dev $(../@)" diff --git a/templates/interfaces/ethernet/node.tag/speed/node.def b/templates/interfaces/ethernet/node.tag/speed/node.def new file mode 100644 index 0000000..6983355 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/speed/node.def @@ -0,0 +1,6 @@ +type: txt +help: "set the speed for this interface" +syntax: $(@) in "10", "100", "1000"; "Speed must be 10, 100, or 1000" +commit: $(../duplex/@) != ""; "if speed is hardcoded, duplex must also be hardcoded" +create: "ethtool -s $(../@) speed $(@) duplex $(../duplex/@) autoneg off" +delete: "ethtool -s $(../@) autoneg on" diff --git a/templates/interfaces/ethernet/node.tag/vif/node.def b/templates/interfaces/ethernet/node.tag/vif/node.def new file mode 100644 index 0000000..06ee1c0 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/vif/node.def @@ -0,0 +1,7 @@ +tag: +type: u32 +help: "vlan ID" +syntax: $(@) >= 0 && $(@) <= 4095; "vlan ID must be between 0 and 4095" +create: "modprobe 8021q"; "error loading 802.1q driver" +create: "vconfig add $(../@) $(@)"; "error adding vlan id $(@) to dev $(../@)" +delete: "vconfig rem $(../@) $(@)"; "error removing vlan id $(@) from dev $(../@)" diff --git a/templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def b/templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def new file mode 100644 index 0000000..345a29e --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/vif/node.tag/address/node.def @@ -0,0 +1,5 @@ +multi: +type: ipv4net +help: "Set IPv4 address and prefix for this interface" +create: "ip addr add $(@) dev $(../../@)"; "error setting address $(@) on dev $(../../@)" +delete: "ip addr del $(@) dev $(../../@)"; "error deleteing address $(@) on dev $(../../@)" diff --git a/templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def b/templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def new file mode 100644 index 0000000..abd0a26 --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/vif/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: "description for this interface" diff --git a/templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def b/templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def new file mode 100644 index 0000000..1e1879f --- /dev/null +++ b/templates/interfaces/ethernet/node.tag/vif/node.tag/enable/node.def @@ -0,0 +1,3 @@ +help: "enable interface" +create: "ip link set $(../../@) up"; "error enabling dev $(../../@)" +delete: "ip link set $(../../@) down"; "error disabling dev $(../../@)" diff --git a/templates/interfaces/loopback/node.def b/templates/interfaces/loopback/node.def new file mode 100644 index 0000000..50df935 --- /dev/null +++ b/templates/interfaces/loopback/node.def @@ -0,0 +1,8 @@ +tag: +type: txt +help: "loopback interface name" +syntax: exec " \ + if [ -z \"`ip addr | grep $(@) `\" ]; then \ + echo loopback interface $(@) doesn\\'t exist on this system ; \ + exit 1 ; \ + fi ; " diff --git a/templates/interfaces/loopback/node.tag/address/node.def b/templates/interfaces/loopback/node.tag/address/node.def new file mode 100644 index 0000000..2e7e106 --- /dev/null +++ b/templates/interfaces/loopback/node.tag/address/node.def @@ -0,0 +1,6 @@ +multi: +type: ipv4net +help: "Set IPv4 address and prefix for this interface" +# TODO make sure 127.0.0.0/8 not in here. +create: "ip addr add $(@) dev $(../@)"; "error setting address $(@) on dev $(../@)" +delete: "ip addr del $(@) dev $(../@)"; "error deleteing address $(@) on dev $(../@)" diff --git a/templates/interfaces/loopback/node.tag/description/node.def b/templates/interfaces/loopback/node.tag/description/node.def new file mode 100644 index 0000000..abd0a26 --- /dev/null +++ b/templates/interfaces/loopback/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: "description for this interface" diff --git a/templates/interfaces/node.def b/templates/interfaces/node.def new file mode 100644 index 0000000..297604a --- /dev/null +++ b/templates/interfaces/node.def @@ -0,0 +1 @@ +help: "Network interface configuration" diff --git a/templates/system/domain-name/node.def b/templates/system/domain-name/node.def new file mode 100644 index 0000000..8f9d457 --- /dev/null +++ b/templates/system/domain-name/node.def @@ -0,0 +1,15 @@ +type: txt +help: "Configure system domain name" +syntax: pattern $(@) "^[-a-zA-Z0-9.]{0,63}$" ; "invalid domain name $(@)" +create: "sh -c \"if [ x$(@) == x ]; then exit 0; fi && \ +touch /etc/resolv.conf && \ +sed -i '/domain/d' /etc/resolv.conf && \ +echo \\\"domain\t $(@)\\\" >> /etc/resolv.conf\" " +# also add localhost line into /etc/hosts (see host-name template)? +update: "sh -c \"if [ x$(@) == x ]; then exit 0; fi && \ +touch /etc/resolv.conf && \ +sed -i '/domain/d' /etc/resolv.conf && \ +echo \\\"domain\t $(@)\\\" >> /etc/resolv.conf\" " +# also update localhost line in /etc/hosts (see host-name template)? +delete: "sh -c \"touch /etc/resolv.conf && \ +sed -i '/domain\\\\t $(@)/d' /etc/resolv.conf\" " diff --git a/templates/system/domain-search/domain/node.def b/templates/system/domain-search/domain/node.def new file mode 100644 index 0000000..d4e6c3c --- /dev/null +++ b/templates/system/domain-search/domain/node.def @@ -0,0 +1,12 @@ +multi: +type: txt +help: "Configure DNS domain completion order" +syntax: pattern $(@) "^[-a-zA-Z0-9.]+$" ; "invalid domain name $(@)" +create: "sh -c \"touch /etc/resolv.conf && \ +if grep -q 'search\t $(@)' /etc/resolv.conf; then exit 0; \ +else echo \\\"search\t $(@)\\\" >> /etc/resolv.conf; fi\" " +update: "sh -c \"touch /etc/resolv.conf && \ +if grep -q 'search\t $(@)' /etc/resolv.conf; then exit 0; \ +else echo \\\"search\t $(@)\\\" >> /etc/resolv.conf; fi\" " +delete: "sh -c \"touch /etc/resolv.conf && \ +sed -i '/search\\\\t $(@)/d' /etc/resolv.conf\" " diff --git a/templates/system/gateway-address/node.def b/templates/system/gateway-address/node.def new file mode 100644 index 0000000..c82e3fe --- /dev/null +++ b/templates/system/gateway-address/node.def @@ -0,0 +1,17 @@ +type: txt +help: "Configure default gateway" +create: "sh -c \"echo \ +'static_routes/static_routes/0.1/add_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' \ +>> /tmp/cli.log && \ +/opt/vyatta/libexec/xorp/call_xrl 'static_routes/static_routes/0.1/add_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' >> /tmp/cli.log ; \ +echo \\$? >> /tmp/cli.log\" " +update: "sh -c \"echo \ +'static_routes/static_routes/0.1/replace_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' \ +>> /tmp/cli.log && \ +/opt/vyatta/libexec/xorp/call_xrl 'static_routes/static_routes/0.1/replace_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=$(@)&metric:u32=1' >> /tmp/cli.log ; \ +echo \\$? >> /tmp/cli.log\" " +delete: "sh -c \"echo \ +'static_routes/static_routes/0.1/delete_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=0.0.0.0' \ +>> /tmp/cli.log && \ +/opt/vyatta/libexec/xorp/call_xrl 'static_routes/static_routes/0.1/delete_route4?unicast:bool=true&multicast:bool=false&network:ipv4net=0.0.0.0/0&nexthop:ipv4=0.0.0.0' >> /tmp/cli.log ; \ +echo \\$? >> /tmp/cli.log\" " diff --git a/templates/system/host-name/node.def b/templates/system/host-name/node.def new file mode 100644 index 0000000..fc7c91b --- /dev/null +++ b/templates/system/host-name/node.def @@ -0,0 +1,24 @@ +type: txt +help: "Configure system host name" +default: "vyatta" +syntax: pattern $(@) "^[-a-zA-Z0-9.]+$" ; "invalid host name $(@)" +create: "sh -c \"hostname '$(@)' && \ +touch /etc/hosts && \ +sed -i '/localhost/d' /etc/hosts && \ +echo \\\"127.0.0.1\t localhost $(@)\t #vyatta entry\\\" >> /etc/hosts && \ +if [ x$(../domain-name/@) != x ]; then \ +echo \\\"127.0.0.1\t localhost $(@).$(../domain-name/@)\t #vyatta entry\\\" \>> /etc/hosts; fi\" " +# do we need to add ntpd restart here? +update: "sh -c \"hostname '$(@)' && \ +touch /etc/hosts && \ +sed -i '/localhost/d' /etc/hosts && \ +echo \\\"127.0.0.1\t localhost $(@)\t #vyatta entry\\\" >> /etc/hosts && \ +if [ x$(../domain-name/@) != x ]; then \ +echo \\\"127.0.0.1\t localhost $(@).$(../domain-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; fi\" " +# do we need to add ntpd restart here? +delete: "sh -c \"echo > /etc/hostname.conf && hostname '' && \ +touch /etc/hosts && \ +sed -i '/localhost.*#vyatta entry/d' /etc/hosts && \ +if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " diff --git a/templates/system/login/node.def b/templates/system/login/node.def new file mode 100644 index 0000000..1246514 --- /dev/null +++ b/templates/system/login/node.def @@ -0,0 +1,3 @@ +help: "Configure user access" +delete: "sh -c \"echo User root cannot be deleted 1>&2 && exit 1\" " + diff --git a/templates/system/login/radius-server/node.def b/templates/system/login/radius-server/node.def new file mode 100644 index 0000000..d12be76 --- /dev/null +++ b/templates/system/login/radius-server/node.def @@ -0,0 +1,14 @@ +tag: +type: ipv4 +help: "Radius server authentication configuration" +# need mandatory secret. also need port & timeout (default values?) +create: "sh -c \"touch /etc/raddb/server && \ +sed -i '/$(@)/d' /etc/raddb/server && \ +echo \\\"$(@):$(port/@)\t$(secret/@)\t$(timeout/@)\\\" \ +>> /etc/raddb/server\" " +update: "sh -c \"touch /etc/raddb/server && \ +sed -i '/$(@)/d' /etc/raddb/server && \ +echo \\\"$(@):$(port/@)\t$(secret/@)\t$(timeout/@)\\\" \ +>> /etc/raddb/server\" " +delete: "sh -c \"touch /etc/raddb/server && \ +sed -i '/$(@)/d' /etc/raddb/server\" " diff --git a/templates/system/login/radius-server/node.tag/port/node.def b/templates/system/login/radius-server/node.tag/port/node.def new file mode 100644 index 0000000..8c856e7 --- /dev/null +++ b/templates/system/login/radius-server/node.tag/port/node.def @@ -0,0 +1,4 @@ +type: u32 +help: "Configure radius port" +syntax: ($(@) > 0 && $(@) < 65536) ; "port must be between 1 and 65535" +default: 1812 diff --git a/templates/system/login/radius-server/node.tag/secret/node.def b/templates/system/login/radius-server/node.tag/secret/node.def new file mode 100644 index 0000000..eb08eca --- /dev/null +++ b/templates/system/login/radius-server/node.tag/secret/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Secret for radius access" diff --git a/templates/system/login/radius-server/node.tag/timeout/node.def b/templates/system/login/radius-server/node.tag/timeout/node.def new file mode 100644 index 0000000..84bb442 --- /dev/null +++ b/templates/system/login/radius-server/node.tag/timeout/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "Timeout for radius session" +default: 2 diff --git a/templates/system/login/user/node.def b/templates/system/login/user/node.def new file mode 100644 index 0000000..0d09c4d --- /dev/null +++ b/templates/system/login/user/node.def @@ -0,0 +1,17 @@ +tag: +type: txt +help: "User account information" +syntax: pattern $(@) "^[a-zA-Z_][a-zA-Z0-9_-]*\\$?$" ; "invalid user name $(@)" +# line continuation and $() expansion are done by cli, not sh. +# need mandatory encrypted password. +end: "if [ -d /tmp/vyatta-delete-system-login-user-$(@).\\\$PPID ]; \ +then rm -rf /tmp/vyatta-delete-system-login-user-$(@).\\\$PPID && exit 0; \ +fi && \ +/opt/vyatta/sbin/vyatta_update_login_user.pl \ +'$(@)' '$(full-name/@)' '$(authentication/encrypted-password/@)'" +delete: "if [ x$(@) == x ]; then exit 1; fi && \ +if [ x$(@) == xroot ]; then echo Cannot delete user \"root\" 1>&2 && exit 2; \ +fi && \ +if mkdir /tmp/vyatta-delete-system-login-user-$(@).\\\$PPID >& /dev/null; \ +then /opt/vyatta/sbin/vyatta_update_login_user.pl -d '$(@)'; \ +else exit 1; fi" diff --git a/templates/system/login/user/node.tag/authentication/encrypted-password/node.def b/templates/system/login/user/node.tag/authentication/encrypted-password/node.def new file mode 100644 index 0000000..33a87f5 --- /dev/null +++ b/templates/system/login/user/node.tag/authentication/encrypted-password/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Configure encrypted password" diff --git a/templates/system/login/user/node.tag/authentication/node.def b/templates/system/login/user/node.tag/authentication/node.def new file mode 100644 index 0000000..8b0f312 --- /dev/null +++ b/templates/system/login/user/node.tag/authentication/node.def @@ -0,0 +1 @@ +help: "Authentication password" diff --git a/templates/system/login/user/node.tag/authentication/plaintext-password/node.def b/templates/system/login/user/node.tag/authentication/plaintext-password/node.def new file mode 100644 index 0000000..78619d7 --- /dev/null +++ b/templates/system/login/user/node.tag/authentication/plaintext-password/node.def @@ -0,0 +1,9 @@ +type: txt +help: "Configure plaintext password for encryption" +# if plaintext is empty, assume this is left-over from blanking the plaintext +# and do nothing. to set password to empty, user needs to set the +# "encrypted-password" to an empty string (which actually allows login without +# password). +update: $(@) == "" \ +|| ($(../encrypted-password/@) = `/opt/vyatta/sbin/rl_passwd '$(@)' dummy` \ + && $(@) = "") diff --git a/templates/system/login/user/node.tag/full-name/node.def b/templates/system/login/user/node.tag/full-name/node.def new file mode 100644 index 0000000..86b7c8d --- /dev/null +++ b/templates/system/login/user/node.tag/full-name/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Full name of the user (use quotes for names with spaces)" diff --git a/templates/system/name-server/node.def b/templates/system/name-server/node.def new file mode 100644 index 0000000..2afe0b0 --- /dev/null +++ b/templates/system/name-server/node.def @@ -0,0 +1,17 @@ +multi: +type: ipv4 +help: "Configure domain name server" +create: "sh -c \"touch /etc/resolv.conf && \ +if grep -q '$(@)' /etc/resolv.conf; then exit 0; \ +else echo \\\"nameserver\t $(@)\\\" >> /etc/resolv.conf; fi && \ +if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " +update: "sh -c \"touch /etc/resolv.conf && \ +if grep -q '$(@)' /etc/resolv.conf; then exit 0; \ +else echo \\\"nameserver\t $(@)\\\" >> /etc/resolv.conf; fi && \ +if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " +delete: "sh -c \"touch /etc/resolv.conf && \ +sed -i '/$(@)/d' /etc/resolv.conf && \ +if [ -f /etc/ntp/ntp.conf ] && grep -q 'server' /etc/ntp/ntp.conf; then \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " diff --git a/templates/system/ntp-server/node.def b/templates/system/ntp-server/node.def new file mode 100644 index 0000000..9f828e1 --- /dev/null +++ b/templates/system/ntp-server/node.def @@ -0,0 +1,16 @@ +multi: +type: txt +help: "IP address of NTP server" +# should help be "domain name" instead of "ip address", or change type to ipv4? +create: "sh -c \"touch /etc/ntp/ntp.conf && \ +if ! grep -q 'server.*$(@)' /etc/ntp/ntp.conf; then \ +echo \\\"server $(@)\\\" >> /etc/ntp/ntp.conf && \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " +update: "sh -c \"touch /etc/ntp/ntp.conf && \ +if ! grep -q 'server.*$(@)' /etc/ntp/ntp.conf; then \ +echo \\\"server $(@)\\\" >> /etc/ntp/ntp.conf && \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " +delete: "sh -c \"touch /etc/ntp/ntp.conf && \ +if grep -q 'server.*$(@)' /etc/ntp/ntp.conf; then \ +sed -i '/server $(@)/d' /etc/ntp/ntp.conf && \ +/opt/vyatta/sbin/ntpd.init restart; fi\" " diff --git a/templates/system/options/node.def b/templates/system/options/node.def new file mode 100644 index 0000000..1e49ee5 --- /dev/null +++ b/templates/system/options/node.def @@ -0,0 +1 @@ +help: "Configure system options" diff --git a/templates/system/options/reboot-on-panic/node.def b/templates/system/options/reboot-on-panic/node.def new file mode 100644 index 0000000..d84abc7 --- /dev/null +++ b/templates/system/options/reboot-on-panic/node.def @@ -0,0 +1,16 @@ +type: bool +help: "Configure if kernel panic causes reboot" +default: true +create: "sh -c \"if [ x$(@) == xfalse ]; \ +then \ + echo 0 > /proc/sys/kernel/panic; \ +else \ + echo 60 > /proc/sys/kernel/panic; \ +fi\" " +update: "sh -c \"if [ x$(@) == xfalse ]; \ +then \ + echo 0 > /proc/sys/kernel/panic; \ +else \ + echo 60 > /proc/sys/kernel/panic; \ +fi\" " +delete: "sh -c \"echo 60 > /proc/sys/kernel/panic\" " diff --git a/templates/system/package/auto-sync/node.def b/templates/system/package/auto-sync/node.def new file mode 100644 index 0000000..59f3e1e --- /dev/null +++ b/templates/system/package/auto-sync/node.def @@ -0,0 +1,15 @@ +# this will set APT::Periodic::Update-Package-Lists in /etc/apt/apt.conf +# apt.conf is in turn read by the apt cron file loacted in /etc/cron.daily/apt +# the /etc/crontab file must have the daily line for daily to be run +type: u32 +default: 1 +help: "Update the the repository cache every n days. 0 disables auto-update." +syntax: $(@) >= 0 && $(@) < 32 ; "auto-sync must be between 0 and 32 days" +create: "sh -c \"touch /etc/apt/apt.conf && \ +sed -i '/APT::Periodic::Update-Package-Lists*/d' /etc/apt/apt.conf && \ +echo \\\"APT::Periodic::Update-Package-Lists \\\"$(@)\\\";\\\" >> /etc/apt/apt.conf\" " +update: "sh -c \"touch /etc/apt/apt.conf && \ +sed -i '/APT::Periodic::Update-Package-Lists*/d' /etc/apt/apt.conf && \ +echo \\\"APT::Periodic::Update-Package-Lists \\\"$(@)\\\";\\\" >> /etc/apt/apt.conf\" " +delete: "sh -c \"touch /etc/apt/apt.conf && \ +sed -i '/APT::Periodic::Update-Package-Lists*/d' /etc/apt/apt.conf\" " diff --git a/templates/system/package/node.def b/templates/system/package/node.def new file mode 100644 index 0000000..ce185fa --- /dev/null +++ b/templates/system/package/node.def @@ -0,0 +1 @@ +help: "Package Update Repository Configuration" diff --git a/templates/system/package/repository/node.def b/templates/system/package/repository/node.def new file mode 100644 index 0000000..104089a --- /dev/null +++ b/templates/system/package/repository/node.def @@ -0,0 +1,14 @@ +tag: +type: txt +help: "Repository name" +# bug 1847: remove the previous repo line before adding new line. +# need to prohibit '!' in repo name (sed delimiter) +syntax: pattern $(@) "^[^!]+$" ; "Do not use '!' in repository name" +create: "sh -c \"touch /etc/apt/sources.list && \ +sed -i '\\!/ $(@) !d' /etc/apt/sources.list && \ +echo \\\"deb $(url/@)/ $(@) $(component/@)\\\" >> /etc/apt/sources.list\" " +update: "sh -c \"touch /etc/apt/sources.list && \ +sed -i '\\!/ $(@) !d' /etc/apt/sources.list && \ +echo \\\"deb $(url/@)/ $(@) $(component/@)\\\" >> /etc/apt/sources.list\" " +delete: "sh -c \"touch /etc/apt/sources.list && \ +sed -i '\\! $(@) $(component/@)!d' /etc/apt/sources.list\" " diff --git a/templates/system/package/repository/node.tag/component/node.def b/templates/system/package/repository/node.tag/component/node.def new file mode 100644 index 0000000..e87f88d --- /dev/null +++ b/templates/system/package/repository/node.tag/component/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Repository component names" diff --git a/templates/system/package/repository/node.tag/description/node.def b/templates/system/package/repository/node.tag/description/node.def new file mode 100644 index 0000000..9ce7dac --- /dev/null +++ b/templates/system/package/repository/node.tag/description/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Repository description" diff --git a/templates/system/package/repository/node.tag/url/node.def b/templates/system/package/repository/node.tag/url/node.def new file mode 100644 index 0000000..0e304b9 --- /dev/null +++ b/templates/system/package/repository/node.tag/url/node.def @@ -0,0 +1,2 @@ +type: txt +help: "Repository URL" diff --git a/templates/system/static-host-mapping/host-name/node.def b/templates/system/static-host-mapping/host-name/node.def new file mode 100644 index 0000000..ea0000a --- /dev/null +++ b/templates/system/static-host-mapping/host-name/node.def @@ -0,0 +1,4 @@ +tag: +type: txt +help: "Map DNS names to system interfaces" +syntax: pattern $(@) "^[-a-zA-Z0-9.]+$" ; "invalid host name $(@)" diff --git a/templates/system/static-host-mapping/host-name/node.tag/alias/node.def b/templates/system/static-host-mapping/host-name/node.tag/alias/node.def new file mode 100644 index 0000000..b6f897f --- /dev/null +++ b/templates/system/static-host-mapping/host-name/node.tag/alias/node.def @@ -0,0 +1,33 @@ +multi: +type: txt +help: "Alias for this address" +create: "sh -c \"touch /etc/hosts && \ +sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \ +/etc/hosts && \ +echo \\\"$(../inet/@)\t $(../@) $(@) \t #vyatta entry\\\" \ +>> /etc/hosts && \ +if [ x$(../../../domain-name/@) == x ]; \ +then \ +echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +else \ +echo \\\"127.0.0.1\t localhost \ +$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +fi\" " +update: "sh -c \"touch /etc/hosts && \ +sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \ +/etc/hosts && \ +echo \\\"$(../inet/@)\t $(../@) $(@) \t #vyatta entry\\\" \ +>> /etc/hosts && \ +if [ x$(../../../domain-name/@) == x ]; \ +then \ +echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +else \ +echo \\\"127.0.0.1\t localhost \ +$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +fi\" " +delete: "sh -c \"touch /etc/hosts && \ +sed -i '/ $(../@) .*#vyatta entry/{/localhost/!d}' /etc/hosts\" " diff --git a/templates/system/static-host-mapping/host-name/node.tag/inet/node.def b/templates/system/static-host-mapping/host-name/node.tag/inet/node.def new file mode 100644 index 0000000..192273c --- /dev/null +++ b/templates/system/static-host-mapping/host-name/node.tag/inet/node.def @@ -0,0 +1,30 @@ +type: ipv4 +help: "Internet address" +create: "sh -c \"touch /etc/hosts && \ +sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \ +/etc/hosts && \ +echo \\\"$(@)\t $(../@) \t #vyatta entry\\\" >> /etc/hosts && \ +if [ x$(../../../domain-name/@) == x ]; \ +then \ +echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +else \ +echo \\\"127.0.0.1\t localhost \ +$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +fi\" " +update: "sh -c \"touch /etc/hosts && \ +sed -i '/$(../@).*#vyatta entry/d;/127\\.0\\.0\\.1.*#vyatta entry/d' \ +/etc/hosts && \ +echo \\\"$(@)\t $(../@) \t #vyatta entry\\\" >> /etc/hosts && \ +if [ x$(../../../domain-name/@) == x ]; \ +then \ +echo \\\"127.0.0.1\t localhost $(../../../host-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +else \ +echo \\\"127.0.0.1\t localhost \ +$(../../../host-name/@).$(../../../domain-name/@)\t #vyatta entry\\\" \ +>> /etc/hosts; \ +fi\" " +delete: "sh -c \"touch /etc/hosts && \ +sed -i '/ $(../@) .*#vyatta entry/{/localhost/!d}' /etc/hosts\" " diff --git a/templates/system/static-host-mapping/node.def b/templates/system/static-host-mapping/node.def new file mode 100644 index 0000000..736413f --- /dev/null +++ b/templates/system/static-host-mapping/node.def @@ -0,0 +1 @@ +help: "Map DNS names to system interfaces" diff --git a/templates/system/syslog/console/facility/node.def b/templates/system/syslog/console/facility/node.def new file mode 100644 index 0000000..bfe3856 --- /dev/null +++ b/templates/system/syslog/console/facility/node.def @@ -0,0 +1,11 @@ +tag: +type: txt +help: "Configure facility for console logging" +create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '\\/dev\\/console' \ +\\\"$(@).\\\\\$LVL\t/dev/console\n\\\"\" " +update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '\\/dev\\/console' \ +\\\"$(@).\\\\\$LVL\t/dev/console\n\\\"\" " +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl \ +'$(@)\\.' '\\/dev\\/console' ''\" " diff --git a/templates/system/syslog/console/facility/node.tag/level/node.def b/templates/system/syslog/console/facility/node.tag/level/node.def new file mode 100644 index 0000000..9be459c --- /dev/null +++ b/templates/system/syslog/console/facility/node.tag/level/node.def @@ -0,0 +1,3 @@ +type: txt +help: "Configure the logging level" +default: "err" diff --git a/templates/system/syslog/console/node.def b/templates/system/syslog/console/node.def new file mode 100644 index 0000000..e30721d --- /dev/null +++ b/templates/system/syslog/console/node.def @@ -0,0 +1 @@ +help: "Configure console logging" diff --git a/templates/system/syslog/file/node.def b/templates/system/syslog/file/node.def new file mode 100644 index 0000000..d62d261 --- /dev/null +++ b/templates/system/syslog/file/node.def @@ -0,0 +1,4 @@ +tag: +type: txt +help: "Name of the syslog file to save log messages to" +syntax: pattern $(@) "^[-a-zA-Z0-9_.]+$" ; "invalid file name $(@)" diff --git a/templates/system/syslog/file/node.tag/archive/files/node.def b/templates/system/syslog/file/node.tag/archive/files/node.def new file mode 100644 index 0000000..ca2bf17 --- /dev/null +++ b/templates/system/syslog/file/node.tag/archive/files/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "Number of saved files" +default: 5 diff --git a/templates/system/syslog/file/node.tag/archive/node.def b/templates/system/syslog/file/node.tag/archive/node.def new file mode 100644 index 0000000..d0b0e23 --- /dev/null +++ b/templates/system/syslog/file/node.tag/archive/node.def @@ -0,0 +1,8 @@ +help: "Configure log file size and rotation characteristics" +# need mandatory files & size +create: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \ +'$(../@)' '$(files/@)' '$(size/@)' 1\" " +update: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \ +'$(../@)' '$(files/@)' '$(size/@)' 1\" " +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \ +'$(../@)' '$(files/@)' '$(size/@)' 0\" " diff --git a/templates/system/syslog/file/node.tag/archive/size/node.def b/templates/system/syslog/file/node.tag/archive/size/node.def new file mode 100644 index 0000000..a5ace52 --- /dev/null +++ b/templates/system/syslog/file/node.tag/archive/size/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "Size of log files (kbytes)" +default: 0 diff --git a/templates/system/syslog/file/node.tag/facility/node.def b/templates/system/syslog/file/node.tag/facility/node.def new file mode 100644 index 0000000..3ef56e3 --- /dev/null +++ b/templates/system/syslog/file/node.tag/facility/node.def @@ -0,0 +1,13 @@ +tag: +type: txt +help: "Configure facility for file logging" +create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \ +'\\/var\\/log\\/user\\/$(../@)' \ +\\\"$(@).\\\\\$LVL\t/var/log/user/$(../@) \n\\\"\" " +update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \ +'\\/var\\/log\\/user\\/$(../@)' \ +\\\"$(@).\\\\\$LVL\t/var/log/user/$(../@) \n\\\"\" " +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \ +'\\/var\\/log\\/user\\/$(../@)' ''\" " diff --git a/templates/system/syslog/file/node.tag/facility/node.tag/level/node.def b/templates/system/syslog/file/node.tag/facility/node.tag/level/node.def new file mode 100644 index 0000000..9be459c --- /dev/null +++ b/templates/system/syslog/file/node.tag/facility/node.tag/level/node.def @@ -0,0 +1,3 @@ +type: txt +help: "Configure the logging level" +default: "err" diff --git a/templates/system/syslog/global/archive/files/node.def b/templates/system/syslog/global/archive/files/node.def new file mode 100644 index 0000000..ca2bf17 --- /dev/null +++ b/templates/system/syslog/global/archive/files/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "Number of saved files" +default: 5 diff --git a/templates/system/syslog/global/archive/node.def b/templates/system/syslog/global/archive/node.def new file mode 100644 index 0000000..dcfc244 --- /dev/null +++ b/templates/system/syslog/global/archive/node.def @@ -0,0 +1,8 @@ +help: "Configure log file size and rotation characteristics" +# need mandatory files & size +create: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \ +'$(files/@)' '$(size/@)' 1\" " +update: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \ +'$(files/@)' '$(size/@)' 1\" " +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_logrotate.pl \ +'$(files/@)' '$(size/@)' 0\" " diff --git a/templates/system/syslog/global/archive/size/node.def b/templates/system/syslog/global/archive/size/node.def new file mode 100644 index 0000000..3c8da6e --- /dev/null +++ b/templates/system/syslog/global/archive/size/node.def @@ -0,0 +1,3 @@ +type: u32 +help: "Size of log files (kbytes)" +default: 250 diff --git a/templates/system/syslog/global/facility/node.def b/templates/system/syslog/global/facility/node.def new file mode 100644 index 0000000..cd037f6 --- /dev/null +++ b/templates/system/syslog/global/facility/node.def @@ -0,0 +1,11 @@ +tag: +type: txt +help: "Configure facility for system logging" +create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '' '\\/var\\/log\\/messages' \ +\\\"$(@).\\\\\$LVL\t/var/log/messages \n\\\"\" " +update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '' '\\/var\\/log\\/messages' \ +\\\"$(@).\\\\\$LVL\t/var/log/messages \n\\\"\" " +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl \ +'' '\\/var\\/log\\/messages' '*.warning\t/var/log/messages \n'\" " diff --git a/templates/system/syslog/global/facility/node.tag/level/node.def b/templates/system/syslog/global/facility/node.tag/level/node.def new file mode 100644 index 0000000..9be459c --- /dev/null +++ b/templates/system/syslog/global/facility/node.tag/level/node.def @@ -0,0 +1,3 @@ +type: txt +help: "Configure the logging level" +default: "err" diff --git a/templates/system/syslog/global/node.def b/templates/system/syslog/global/node.def new file mode 100644 index 0000000..b516645 --- /dev/null +++ b/templates/system/syslog/global/node.def @@ -0,0 +1 @@ +help: "Configure system logging" diff --git a/templates/system/syslog/host/node.def b/templates/system/syslog/host/node.def new file mode 100644 index 0000000..a349582 --- /dev/null +++ b/templates/system/syslog/host/node.def @@ -0,0 +1,3 @@ +tag: +type: txt +help: "IP address or hostname of remote syslog server" diff --git a/templates/system/syslog/host/node.tag/facility/node.def b/templates/system/syslog/host/node.tag/facility/node.def new file mode 100644 index 0000000..2dc56e6 --- /dev/null +++ b/templates/system/syslog/host/node.tag/facility/node.def @@ -0,0 +1,11 @@ +tag: +type: txt +help: "Configure facility for host logging" +create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '@$(../@) ' \ +\\\"$(@).\\\\\$LVL\t@$(../@) \n\\\"\" " +update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' '@$(../@) ' \ +\\\"$(@).\\\\\$LVL\t@$(../@) \n\\\"\" " +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl '$(@)\\.' \ +'@$(../@) ' ''\" " diff --git a/templates/system/syslog/host/node.tag/facility/node.tag/level/node.def b/templates/system/syslog/host/node.tag/facility/node.tag/level/node.def new file mode 100644 index 0000000..9be459c --- /dev/null +++ b/templates/system/syslog/host/node.tag/facility/node.tag/level/node.def @@ -0,0 +1,3 @@ +type: txt +help: "Configure the logging level" +default: "err" diff --git a/templates/system/syslog/node.def b/templates/system/syslog/node.def new file mode 100644 index 0000000..04d3675 --- /dev/null +++ b/templates/system/syslog/node.def @@ -0,0 +1,3 @@ +help: "Configure syslog daemon" +delete: "sh -c \"/opt/vyatta/sbin/vyatta_update_syslog.pl \ +'' '\\/var\\/log\\/messages' '*.warning\t/var/log/messages \n'\" " diff --git a/templates/system/syslog/user/node.def b/templates/system/syslog/user/node.def new file mode 100644 index 0000000..ba731ca --- /dev/null +++ b/templates/system/syslog/user/node.def @@ -0,0 +1,3 @@ +tag: +type: txt +help: "Configure syslog user account output" diff --git a/templates/system/syslog/user/node.tag/facility/node.def b/templates/system/syslog/user/node.tag/facility/node.def new file mode 100644 index 0000000..1b22747 --- /dev/null +++ b/templates/system/syslog/user/node.tag/facility/node.def @@ -0,0 +1,14 @@ +tag: +type: txt +help: "Configure facility for user logging" +create: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl \\\"$(@)\\.\\\\\$LVL\\\" \ +' $(../@) ' \ +\\\"$(@).\\\\\$LVL $(../@) \n\\\"\" " +update: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl \\\"$(@)\\.\\\\\$LVL\\\" \ +' $(../@) ' \ +\\\"$(@).\\\\\$LVL $(../@) \n\\\"\" " +delete: "sh -c \"LVL=`echo -n $(level/@) | tr '[a-z]' '[A-Z]'` && \ +/opt/vyatta/sbin/vyatta_update_syslog.pl \\\"$(@)\\.\\\\\$LVL\\\" \ +' $(../@) ' ''\" " diff --git a/templates/system/syslog/user/node.tag/facility/node.tag/level/node.def b/templates/system/syslog/user/node.tag/facility/node.tag/level/node.def new file mode 100644 index 0000000..9be459c --- /dev/null +++ b/templates/system/syslog/user/node.tag/facility/node.tag/level/node.def @@ -0,0 +1,3 @@ +type: txt +help: "Configure the logging level" +default: "err" diff --git a/templates/system/time-zone/node.def b/templates/system/time-zone/node.def new file mode 100644 index 0000000..00da013 --- /dev/null +++ b/templates/system/time-zone/node.def @@ -0,0 +1,16 @@ +type: txt +help: "Configure local timezone" +default: "GMT" +update: "LTF=\"/usr/share/zoneinfo\" && \ +case \"$(@)\" in \ + [Ll][Oo][Ss]*) LTF=\"\\\$LTF/US/Pacific\" ;; \ + [Dd][Ee][Nn]*) LTF=\"\\\$LTF/US/Mountain\" ;; \ + [Hh][Oo][Nn]*) LTF=\"\\\$LTF/US/Hawaii\" ;; \ + [Nn][Ee][Ww]*) LTF=\"\\\$LTF/US/Eastern\" ;; \ + [Cc][Hh][Ii]*) LTF=\"\\\$LTF/US/Central\" ;; \ + [Aa][Nn][Cc]*) LTF=\"\\\$LTF/US/Alaska\" ;; \ + [Pp][Hh][Oo]*) LTF=\"\\\$LTF/US/Arizona\" ;; \ + *) LTF=\"\\\$LTF/Etc/$(@)\" ;; \ +esac && \ +ln -fs \\\$LTF /etc/localtime" +delete: "ln -fs /usr/share/zoneinfo/GMT /etc/localtime" diff --git a/tools/rl_passwd.cc b/tools/rl_passwd.cc new file mode 100644 index 0000000..5a5610b --- /dev/null +++ b/tools/rl_passwd.cc @@ -0,0 +1,152 @@ +/* vi: set sw=4 ts=4: */ +/* + * Module: rl_passwd.cc + * + * **** License **** + * Version: VPL 1.0 + * + * The contents of this file are subject to the Vyatta Public License + * Version 1.0 ("License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.vyatta.com/vpl + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * This code was originally developed by Vyatta, Inc. + * Portions created by Vyatta are Copyright (C) 2005, 2006, 2007 Vyatta, Inc. + * All Rights Reserved. + * + * Author: Michael Larson + * Date: 2005 + * Description: + * + * **** End License **** + * + */ +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <utime.h> +#include <syslog.h> +#include <time.h> +#include <sys/types.h> +#include <shadow.h> +#include <pwd.h> +#include <sys/resource.h> +#include <errno.h> + +static char crypt_passwd[128]; + +static int new_password(char * password); +static char *pw_encrypt(const char *clear, const char *salt); + +void usage() +{ + printf("vyatta_pwd plainpwd username\n"); + +} + +int main(int argc, char **argv) +{ + char *name; + char * password; + + if (argc != 3) { + usage(); + return(1); + } + + /* "name" is unused */ + name = argv[2]; + password = argv[1]; + + if (new_password(password)) { + printf("error in password encrypt\n"); + return(1); + } + // printf("%s, %s\n",name,password); + printf("%s",crypt_passwd); + return (0); +} + + + +static int i64c(int i) +{ + if (i <= 0) + return ('.'); + if (i == 1) + return ('/'); + if (i >= 2 && i < 12) + return ('0' - 2 + i); + if (i >= 12 && i < 38) + return ('A' - 12 + i); + if (i >= 38 && i < 63) + return ('a' - 38 + i); + return ('z'); +} + +static char *crypt_make_salt(void) +{ + time_t now; + static unsigned long x; + static char result[3]; + + time(&now); + x += now + getpid() + clock(); + result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077); + result[1] = i64c(((x >> 12) ^ x) & 077); + result[2] = '\0'; + return result; +} + + +static int new_password(char * password) +{ + char *cp; + char salt[12]; /* "$N$XXXXXXXX" or "XX" */ + char orig[200]; + char pass[200]; + + orig[0] = '\0'; + + cp = (char*)password; + + strncpy(pass, cp, sizeof(pass)); + memset(cp, 0, strlen(cp)); + memset(cp, 0, strlen(cp)); + memset(orig, 0, sizeof(orig)); + memset(salt, 0, sizeof(salt)); + + strcpy(salt, "$1$"); + strcat(salt, crypt_make_salt()); + strcat(salt, crypt_make_salt()); + strcat(salt, crypt_make_salt()); + + strcat(salt, crypt_make_salt()); + cp = pw_encrypt(pass, salt); + + memset(pass, 0, sizeof pass); + strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); + return 0; +} + +char *pw_encrypt(const char *clear, const char *salt) +{ + static char cipher[128]; + char *cp; + cp = (char *) crypt(clear, salt); + /* if crypt (a nonstandard crypt) returns a string too large, + truncate it so we don't overrun buffers and hope there is + enough security in what's left */ + strncpy(cipher, cp, sizeof(cipher)); + return cipher; +} + |