#!/bin/sh cfg_common_ver_=2.356 # set -x ############################################################################## # Copyright (C) cfg.common, C. Ostheimer and , licensed under # GNU General Public License version 2 (GPL v2.0) # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details; spblinux.de/fbox.new/gpl-2.0.txt # # by dynamic and spblinux, 2008-05 # cfg.common # - helper functions to control addons # - to be sourced by cfg_xxxx installation scripts # - all variables used(*) by this script have a trailing underline, e.g. i_ # *) they may get changed, overwritten, deleted #################################################################### # Changes: # 11.05.2008 Major modifications to support new version of cfg_xxxxx install scripts # # Suggestions for future versions: # - removal of outdated functions required for compatibility reasons with older cfg_xxxx scripts #################################################################### cmp_version() # check if $2 >= $1, with $1 Version-Required and $2 Cur_Version { major_req_=${1%\.*} major_cur_=${2%\.*} [ $major_cur_ -gt $major_req_ ] && return 0 [ $major_cur_ -lt $major_req_ ] && return 1 if [ $major_cur_ -eq $major_req_ ];then [ "$major_req_" = "$1" ] && return 0 [ "$major_cur_" = "$2" ] && return 1 minor_req_=${1#*\.} minor_cur_=${2#*\.} req_digit_=${minor_req_%${minor_req_#?}} cur_digit_=${minor_cur_%${minor_cur_#?}} while [ "$req_digit_" != "" ] do [ "$cur_digit_" = "" ] && return 1 [ $cur_digit_ -gt $req_digit_ ] && return 0 [ $cur_digit_ -lt $req_digit_ ] && return 1 minor_req_=${minor_req_#?} minor_cur_=${minor_cur_#?} req_digit_=${minor_req_%${minor_req_#?}} cur_digit_=${minor_cur_%${minor_cur_#?}} done return 0 fi return 1 } if [ "$1" = "cmp_version" ];then if cmp_version $2 $cfg_common_ver_;then echo "`basename $0`: current v$cfg_common_ver_ >= required v$2 -> OK";exit else echo "`basename $0`: current v$cfg_common_ver_ < required v$2 -> NOK, need to upgrade";exit fi fi errorexit() { echo -e "$@" exit 1 } is_addon_installed() ### Returns 0 if addon is installed and 1 if not ### sets install type in variable $device (SQF or USB) ### sets USB source path in variable $source_dir if install type is USB { if mount |grep -q "on /var/$addon";then device="SQF" addon_installed_=0 else device="USB" if [ -L /bin/$addon_bin -a -x /bin/$addon_bin ];then addon_installed_=0 source_dir=`ls -l /bin/$addon_bin` source_dir=${source_dir#*-> } source_dir=${source_dir%*/bin/$addon_bin} else addon_installed_=1 fi fi return $addon_installed_ } ######################################################################## # internal function: replace_word # purpose: replace_word file_with_path old_word new_word [description] ######################################################################## replace_word() # replace_word file_with_path old_word new_word [description] { f_tmp_=/var/tmp/`basename "$1"`_$PPID cp $1 $f_tmp_ if cat $f_tmp_ |sed "s/$2/$3/" >$1; then rm $f_tmp_ else echo "Error: failed to write $3 ($4) into $1\n" fi unset f_tmp_ } ######################################################################## # internal function: shift_parms_ # - remove parm1_ and copy # parm1_=parm2_, parm2_=parm3_ ... parm<$i_-1>_=parm<$i_>_ # unset parm<$i_>_ # let i_-=1 # purpose: allow optional parameters ######################################################################## shift_parms_() { if [ $i_ -ge 1 ]; then j_=1 k_=2 while [ $k_ -le $i_ ]; do eval parm${j_}_=`eval echo \\$parm${k_}_` let j_+=1 let k_+=1 done eval unset parm${i_}_ let i_-=1 fi } ######################################################################## # internal function: mkdir_rcrsv_ [ ] # - create all levels of required directory structure provided in # - consider to be relative to if parm is set # - if is omitted, is assumed to be an absolute directory path ######################################################################## mkdir_rcrsv_() { if [ $# -gt 1 ];then create_dir_=$1 parms_=$2 [ -d $create_dir_ ] || errorexit "$funct_: Base directory $create_dir_ does not exist." else create_dir_="" parms_=$1 fi for d_ in `echo $parms_ | sed "s/\// /g"`;do create_dir_=$create_dir_/$d_ if ! [ -d $create_dir_ ];then mkdir $create_dir_ || errorexit "$funct_: No RW File System ($create_dir_)" fi done } ######################################################################## # internal function: get_inst_basedir_ # - returns the installation base directory D to stdout using /var/${addon}_usb # (the symlink /var/${addon}_usb points to D/addons/$addon) ######################################################################## get_inst_basedir_() { if [ -L /var/${addon}_usb ]; then ls -l /var/${addon}_usb |sed "s/.* \([^ ]*\)\/addons\/$addon$/\1/" fi } ######################################################################## # internal function: get_inst_realdir_ [installation_directory] # - returns the real installation base directory D_real # (D_real is the real mountpoint of D; (D=`get_inst_basedir_`); # either D == D_real or D is a symlink or a relative path pointing to D_real) ######################################################################## get_inst_realdir_() { if [ $# -eq 1 ]; then dir_tmp_=$1 else dir_tmp_=`get_inst_basedir_` fi if [ -d "$dir_tmp_" ]; then if ! [ -L $dir_tmp_ ] && [ $dir_tmp_ != ${dir_tmp_#/} ] && \ ! echo $dir_tmp_ |grep -q "\.\." ; then echo $dir_tmp_ else mkdir $dir_tmp_/testfs.$PPID || errorexit "function get_inst_realdir: ERROR: $dir_tmp_ is not writable" mount -t tmpfs testfs $dir_tmp_/testfs.$PPID || errorexit "function get_inst_realdir: ERROR: cannot mount tmpfs on $dir_tmp_" mount |grep "testfs.$PPID" |sed "s/^testfs on \([^ ]*\)\/testfs.$PPID type tmpfs.*$/\1/" umount $dir_tmp_/testfs.$PPID || errorexit "function get_inst_realdir" rm -r $dir_tmp_/testfs.$PPID || errorexit "function get_inst_realdir" fi fi unset dir_tmp_ } ######################################################################## # internal function: get_missing_file_ [-x | -X | -s | -i] [path/]file_name [args...] # - checks if file f is in /var: if yes, return true and make executable if -x is given # - then tries to: copy f to /var | execute f with args| source f | insmod f # - first, if given, from path given with file_name, # and from directory where $0 is called from # - second, if /var/${addon}_usb exists, from /a/b/c/.. # if ${addon}_usb points to /a/b/c # - third from $server$svrsub # - fourth from $server # return value = true if: found |result of execution|result of insmod ######################################################################## get_missing_file_() { unset is_exec_ do_exec_ do_source_ do_insmod_ while [ $# -gt 1 -a x${1#-} != x$1 ]; do case $1 in -x) is_exec_=1;; -X) do_exec_=1;; -s) do_source_=1;; -i) do_insmod_=1;; esac shift done fb_=${1##*/} fm_=/var/$fb_ while ! [ -f $fm_ ]; do [ -f $1 ] && fm_=$1 && break [ -f ${0%/*}/$fb_ ] && fm_=${0%/*}/$fb_ && break d_=`get_inst_basedir_` [ "$d_" ] && [ -f $d_/addons/$fb_ ] && fm_=$d_/addons/$fb_ && break wget $server${uclibc#/0.9.28}$svrsub/$fb_ -O $fm_ && break wget $server/$fb_ -O $fm_ unset fm_ break done if [ "$fm_" ]; then if [ "$do_insmod_" ]; then if [ "$verbose" ]; then insmod $fm_; else insmod $fm_ 2>/dev/null; fi elif [ "$do_source_" ]; then . $fm_ elif [ "$do_exec_" -a $# -gt 1 ]; then chmod +x $fm_ shift $fm_ $@ else [ -f /var/$fb_ ] || cp $fm_ /var/$fb_ [ "$is_exec_" ] && chmod +x /var/$fb_ # create return value [ -f /var/$fb_ ] fi else # create return value [ "$fm_" ] fi } ######################################################################## # internal function: symlink_dir_contents_ [RCRSV|NORCRSV] # : target path to create symlinks in # : source path with files & dirs to be symlinked to # : Indicates whether existing files in target_path should be overwritten by source_path contents # RCRSV : If RCRSV is sent subdirectory contents are recursively symlinked as well # otherwise only contents of the first direcory level are symlinked # Used to symlink files from source directories to RW root paths # - RCRSVs through $source_path_ and creates directory structure in RW $target_path # at the same time symlinking files from source directories into created RW target directories # - for source directories with ".default" extension the extension is omitted in target directory # Dependencies: # $root_bind_ is epxetcted to point to mounted RW-Root path # $bind_on_ is epxetcted to point to RW-Root moint point ######################################################################## symlink_dir_contents_() { [ -d $1 ] || mkdir_rcrsv_ $1 local loop_=${4-"NORCRSV"} local buffer_=$new_sftlnks_ local src_name_="" new_sftlnks_=0 for src_name_ in `ls -1A $2`;do ### Determine src name wihtout possible ".default" extension - required for historical compatibility reasons local src_name_no_def_=${src_name_%*.default} ### If $2 is a file then ls -1A will already return absolute path inkl. filename instead just filename if [ -f $2 ];then local abs_src_name_=$2 src_name_no_def_=${src_name_no_def_##*/} else local abs_src_name_=$2/$src_name_ fi ### L: Source File is Softlink #### ### D: Source is Directory #### ### F: Source is regular file or other device file #### if [ -L $abs_src_name_ ];then [ -e $abs_src_name_.default ] && continue stat_="L" soft_linked_to_=`ls -l $abs_src_name_` soft_linked_to_=${soft_linked_to_#*-> } else stat_="" fi [ -d $abs_src_name_ ] && stat_=$stat_"D:" || stat_=$stat_"F:" [ -L $1/$src_name_no_def_ ] && stat_=$stat_"L" if [ -d $1/$src_name_no_def_ ];then stat_=$stat_"D" else [ -e $1/$src_name_no_def_ ] && stat_=$stat_"F" fi case $stat_ in F: ) ln -s $abs_src_name_ $1/$src_name_no_def_ let new_sftlnks_+=1 ;; D: ) if [ "$loop_" = "RCRSV" ];then symlink_dir_contents_ $1/$src_name_no_def_ $abs_src_name_ $3 RCRSV else ln -s $abs_src_name_ $1/$src_name_no_def_ let new_sftlnks_+=1 fi ;; LD: ) if [ "$loop_" = "RCRSV" ];then soft_linked_to_=`ls -l $abs_src_name_` soft_linked_to_=${soft_linked_to_#*-> } symlink_dir_contents_ $1/$src_name_no_def_ $soft_linked_to_ $3 RCRSV else cp -P $abs_src_name_ $1 fi ;; LF: ) cp -P $abs_src_name_ $1 # ln -s $soft_linked_to_ $1/$src_name_no_def_ ;; D:D ) ### Source: Directory, Target: Existing RW Directory from previous installs ### ### Combine contents of existing and new directory in RW target ### symlink_dir_contents_ $1/$src_name_no_def_ $abs_src_name_ $3 $loop_ ;; LD:D ) soft_linked_to_=`ls -l $abs_src_name_` soft_linked_to_=${soft_linked_to_#*-> } symlink_dir_contents_ $1/$src_name_no_def_ $soft_linked_to_ $3 $loop_ ;; D:LD) ### Source: Directory, Target: Symlink to a directory from previous installs ### ### Convert symlink in Target to true Directory and combine contents of both in newly created directory ### ### Skip if source and target are pointing to the same directory if [ "$soft_linked_to_" != "$abs_src_name_" ];then soft_linked_to_=`ls -l $1/$src_name_no_def_ ` soft_linked_to_=${soft_linked_to_#*-> } rm $1/$src_name_no_def_ ### Symlink only first level - no RCRSV required, as existing dir appears to be on RW device already symlink_dir_contents_ $1/$src_name_no_def_ $soft_linked_to_ $3 symlink_dir_contents_ $1/$src_name_no_def_ $abs_src_name_ $3 $loop_ fi ;; LD:F|LD:LF|D:F|D:LF ) errorexit "$funct_: Source dir $src_name_def_ is conflicting with existing file $bind_on_${1#$root_bind_*}/${src_name_no_def_}. Aborting installation!" ;; LF:D|LF:LD|F:D|F:LD ) errorexit "$funct_: Source file $src_name_def_ is conflicting with existing dir $bind_on_${1#$root_bind_*}/${src_name_no_def_}. Aborting installation!" ;; F:F|F:LF|LF:F|LF:LF ) ### Source: File, Target: App File or symlink to App file ### ### Overwrite with symlink to new file, if RW flag was set ### if [ "$3" = "RW" ];then [ $verbose ] && echo " -> File $bind_on_${1#$root_bind_*}/$src_name_no_def_ replaced with SYMLINK to file $abs_src_name_" rm $1/$src_name_no_def_ ln -s $abs_src_name_ $1/$src_name_no_def_ let new_sftlnks_+=1 else [ $verbose ] && echo " -> $bind_on_${1#$root_bind_*}/$src_name_no_def_ NOT REPLACED with SYMLINK to file $abs_src_name_, as RO parm was set." fi ;; esac done [ $verbose ] && echo " -> $bind_on_${1#$root_bind_*} - ADDED SOFTLINKS: $new_sftlnks_" || echo -n "." let tot_sftlnks_+=$new_sftlnks_ let new_sftlnks_=$buffer_ } conf_in_flash_() { fingerprint_="____cfg.configs____" c_=/var/flash/cfg.configs f_=/var/tmp/cfg.configs.tar tmp_dir_=/var/_RW_/_tmp_untar_ [ -d $tmp_dir_ ] && rm -rf $tmp_dir_/* mkdir_rcrsv_ $tmp_dir_ major_=`grep tffs /proc/devices` major_=${major_%%tffs} ### Check if $2 contains correct numeric value for minor ### if echo $2 | grep -q "[^0-9]";then errorexit "ERROR: minor number provided needs to be numeric. You gave:\"$2\"" else minor_=$2 if [ $minor_ -ge 1 -a $minor_ -le 254 ];then [ "$verbose" ] && echo "INFORMATION: Using minor $minor_ for flash config device $c_" else errorexit "ERROR: $minor_ is not a minor number between 1 and 254; NOT using flash config device" fi fi if ! [ -c $c_ ];then if ls -l /var/flash/ |grep "$major_, *$minor_" | grep -qv "`basename $c_`$";then ls -l /var/flash/ |grep "$major_, *$minor_" errorexit "ERROR: another flash device exists with minor $minor_ given for asterisk!" else mknod $c_ c $major_ $minor_ fi fi if ! checkempty $c_ 2>/dev/null; then ### Check if $c contains cfg_xxxx config files -> looks for $fingerprint_ identifier ### if ! cat $c_ | tar t 2> /dev/null | grep -q $fingerprint_;then if [ "$forced" -a "$1" = "clearflash" ];then key_=n echo "ATTENTION: $c_ does not contain valid $addon or other cfg_xxx application data." echo " All contents will be irreversibly cleared if you continue and override." echo "QUESTION: Do you REALLY want to clear contents of $c ?" echo " ( PROCEED ONLY IF YOU REALLY KNOW WHAT YOU DO! )" echo "USER INPUT: (y/N +enter)" read key_ if ! [ "$key_" = "y" -o "$key_" = "Y" ];then errorexit "Exited without performing any action." fi else errorexit "ERROR: Character device $c_ does not contain valid $addon config-files!! Aborted!" fi else ### Expand current contents of $c to $tmp_dir_ -> only required for config2flash ### [ "$1" = "config2flash" ] && cat $c_ | tar x -C $tmp_dir_ fi else case $1 in config2flash ) touch $tmp_dir_/$fingerprint_ ;; flash2config ) errorexit "ERROR: flash device ($c_) is empty. Nothing to restore!" ;; clearflash ) errorexit "ERROR: flash device ($c_) is empty. Nothing to clear!" ;; esac fi case $1 in config2flash) is_addon_installed || errorexit "ERROR: No $addon installation detected. Nothing to do ... aborting!" [ "$device" = "USB" ] && errorexit "ERROR: $1 is only available for RAM installations. Aborting!" ### Create list of files to be archived ### shift 2 num_files_=0 for dir_ in $@;do if [ -d $dir_ ];then [ "$verbose" ] && echo "INFORMATION: Checking -> $dir_" files_="`ls -1F $dir_ | grep -v -e "@$" -e "~$"`" if [ "$files_" != "" ];then [ -d $tmp_dir_/$addon/$dir_ ] || mkdir_rcrsv_ $tmp_dir_/$addon/$dir_ ### Archive only files that are no symlinks -> assuming non symlinks were modified files ### for file_ in $files_;do [ "$verbose" ] && echo "INFORMATION: Marked for archiving -> $dir_/${file_}" let num_files_+=1 cp -p $dir_/$file_ $tmp_dir_/$addon/$dir_/ done fi fi done if [ $num_files_ > 0 ];then if tar cf $f_ -C $tmp_dir_ ./; then cursize_=`ls -l $f_ |sed "s/^.*root *\([0-9]*\) .*/\1/"` [ $cursize_ -gt $max_conf_size ] && errorexit "ERROR: bigger than $max_conf_size bytes, not stored in flash ($f_)" if [ "$forced" ];then key_=y else key_=n echo "QUESTION: Do you want to archive $num_files_ conf files ($cursize_ bytes) to flash memory?" echo " (if there is not enough free flash your fritzbox might stop working)" echo "USER INPUT: (y/N +enter)" read key_ fi if [ "$key_" = "y" -o "$key_" = "Y" ];then if cat $f_ >$c_; then [ "$verbose" ] && echo "SUCCESS: Marked configuration files archived in $c_" else errorexit "ERROR: copying $f_ to flash file $c_ failed" fi fi else errorexit "ERROR: creation of $f_ failed" fi else echo "WARNING: No configuration files detected that require archiving - ABORTING" fi rm -rf $f_ $tmp_dir_ ;; flash2config) if [ "$forced" ]; then key_=y else key_=n echo "QUESTION: Do you want to restore your $addon configuration from flash ?" echo "USER INPUT: (y/N +enter)" read key_ fi if [ "$key_" = "y" -o "$key_" = "Y" ];then [ -d $tmp_dir_ ] || mkdir_rcrsv_ $tmp_dir_ tarlist_="`cat $c_ | tar t | grep "^./$addon/"`" if ! [ "$tarlist_" = "" ];then if cat $c_ | tar x -C $tmp_dir_ $tarlist_;then for file_ in $tarlist_;do file_=${file_#./$addon} [ -d $file_ ] && continue if [ -L $file_ ];then rm $file_ else if [ "$forced" ]; then key_=y else echo "QUESTION: Target $file_ contains user changes. Do you want to replace ?" echo "USER INPUT: (y/N +enter)" key_=n read key_ fi if [ "$key_" = "y" -o "$key_" = "Y" ];then [ "$verbose" ] && echo "INFORMATION: Restoring from flash -> $file_" else [ "$verbose" ] && echo "INFORMATION: Skipped file -> $file_" rm $tmp_dir_/$addon/$file_ fi fi done else errorexit "ERROR: Extraction of $c_ failed" fi cp -rp $tmp_dir_/$addon/* / rm -rf $f_ $tmp_dir_ else echo "WARNING: Nothing to restore in flash for $addon - IGNORED" fi fi ;; clearflash) if [ "$forced" ]; then key_=y else key_=n echo "QUESTION: Do you really want to REMOVE your $addon configuration from flash? (y/N +enter)" echo "USER INPUT: (y/N +enter)" read key_ fi if [ "$key_" = "y" -o "$key_" = "Y" ];then [ -d $tmp_dir_ ] || mkdir_rcrsv_ $tmp_dir_ cat $c_ | tar x -C $tmp_dir_ 2> /dev/null rm -rf $tmp_dir_/$addon tarlist_="" for i_ in "`ls -A $tmp_dir_`";do tarlist_="$tarlist_ ./$i_" done if [ "$tarlist_" = " ./" ];then > $c_ rm $c_ else if tar cf $f_ -C $tmp_dir_ $tarlist_; then if cat $f_ >$c_; then echo "Success: Cleared $addon configuration files from flash $c_" else errorexit "ERROR: copying $f_ to flash file $c_ failed" fi else errorexit "ERROR: creation of $f_ failed" fi fi rm -rf $f_ $tmp_dir_ fi ;; esac } ######################################################### # usage: _fct funct_name@parm1[:parm2][:parm3]...[:parm{n}] ######################################################### _fct() { # Determine function funct_=${1%%@*} case $funct_ in #non verbose functions (which return their value by stdout) must be listed here modext | svrsub | path_abs) ;; *) [ $verbose ] && echo "EXECUTING: _fct $1" esac i_=0 # Determine parameters for function and store in variables parm1_, parm2_, parm3_, ... parm{n}_ args_=`echo \${1#*@} | sed "s/:/ /g"` for parm_ in $args_;do let i_+=1 eval parm${i_}_="$parm_" done case $funct_ in ######################################################### instdir) # instdir@/var/media/ftp|::varname_install_root_dir:varname_fs_type # Determine if "/var/media/ftp" is provided # or assume otherwise. # Return install_root_dir and fs-type information stored in variable names provided. # Also confirm that FS is RW and create on determined . Errorexit if it is nor a RW Filesystem. # PARMS: # /var/media/ftp: absolute path to default FBF USB moint-point , where attached and mounted USB-drives are expected ( if usb-device is to be determined under /var/media/ftp/* ) # : absolute directory name, where $addon is to be installed, in case end-user has provided fully qialified directory name for installation. # : addons/$addon package name, e.g. addons/asterisk, addons/dropbear etc. # varname_install_root_dir: name of variable, where fully specified installation directory will be returned in # varname_fs_type: name of variable, where fs-type of fully specified installation directory will be returned in [ $i_ -eq 4 ] || errorexit "$funct_: Insufficient number of parameters" if [ $parm1_ = "/var/media/ftp" -o $parm1_ = "/var/media/ftp/" ];then unset path_ for path_ in `mount | grep "on $parm1_" | sed "s,^.* \(.*\) \(.*\) \(.*\) \(.*\),\1,"`;do [ -d $path_/$parm2_ ] && break done [ -d "$path_" ] || errorexit "$funct_: No attached and mounted usb-device found under \"/var/media/ftp/\"" else [ -d $parm1_ ] || errorexit "$funct_: Installation path provided \"$parm1_\" does not exist!" path_=$parm1_ fi det_mnt_=$path_ while [ $det_mnt_ ];do # If installation path is references by a Symbolinc Link, then determine the realpath if [ -L $det_mnt_ ];then # temporarily use det_mnt_ as buffer variable det_mnt_="`ls -l $det_mnt_`" # set det_mnt_ to realpath of symbolic link ( basically the path the symbolinc link is pointing to) det_mnt_=${det_mnt_##* -> }; fi # Determine FS-Type if mount-point located if mnt_pnt_=`mount | grep "on $det_mnt_ "`;then # Errorexit if Installation path provided is a "read only (RO)" File Sysren echo $mnt_pnt_ | sed "s,^.* \(.*\) \(.*\) \(.*\) \(.*\),\4," | grep -q "[(,]ro[,)]" && errorexit "$funct_: Installation path \"$path_\" is on a Read Only FS!" # Store FS-Type of mount point located in variable name provided by $parm4_ eval $parm4_=`echo $mnt_pnt_ | sed "s,^.* \(.*\) \(.*\) \(.*\) \(.*\),\3,"` # Store install root dir information in variable name provided by $parm3_ eval $parm3_=$path_ mkdir_rcrsv_ $path_ $parm2_ break; fi # go up one level in directory hierarchy det_mnt_=${det_mnt_%/*} done ;; ######################################################### cfgctl) # cfgctl@:[||]:[SILENT] # Controls mode of cfg_xxxx application status ( enabled / disabled ) # PARMS: # : Name of application script invoking this function # : Command to check application status and store result in variable "varname" # : Re-activates application and allows normal start / stop / install # : Disables application and prevents start / stop / [usb_]install|remove ######################################################### unset j_ [ $i_ -gt 1 ] && case $parm2_ in check ) [ $i_ -ge 3 ] && j_=1;; enable | disable ) [ $i_ -ge 2 ] && j_=1;; * ) errorexit "$funct_: Wrong parameter \"$parm1_\" used in function";; esac [ "$j_" ] || errorexit "$funct_: Wrong number of parameters" ctrl_file_=/var/flash/debug.cfg ctrl_file_tmp_=/var/`basename $ctrl_file_`.tmp ctrl_pattern_="###DON'T REMOVE. PUTS FOLLOWING APP INTO MAINT-MODE: " if grep -q "${ctrl_pattern_}${parm1_}" $ctrl_file_ 2>/dev/null; then case $parm2_ in check ) eval $parm3_=disabled;; enable ) grep -v "${ctrl_pattern_}${parm1_}" $ctrl_file_ >$ctrl_file_tmp_ && \ cat $ctrl_file_tmp_ >$ctrl_file_ if [ "`eval echo \\$parm${i_}_`" != "SILENT" ];then echo "********************************************************************" echo "ATTENTION: $parm1_ has been put to normal operation mode again." echo " Application will allow to start and install again!" echo "********************************************************************" else echo "INFORMATION: ROOT overlaying phase finished. $addon starting enabled!" fi ;; esac else case $parm2_ in check ) eval $parm3_=enabled;; disable ) if cat $0 >/dev/null; then # after factory reset /var/flash/debug.cfg does not exist; create it (but make sure that cat and /dev/null are working) cat $ctrl_file_ >/dev/null 2>&1 || echo "#" >$ctrl_file_ fi cat $ctrl_file_ >$ctrl_file_tmp_ && \ echo -e "${ctrl_pattern_}${parm1_} \c" >> $ctrl_file_tmp_ && \ cat $ctrl_file_tmp_ >$ctrl_file_ if [ "`eval echo \\$parm${i_}_`" != "SILENT" ];then echo "********************************************************************" echo "ATTENTION: $parm1_ has been put into system maintenance mode now!" echo " It will not allow to start or install until you enable via" echo " \"$parm1_ enable\" again" echo "********************************************************************" else echo "INFORMATION: $addon start disabled during ROOT overlaying phase ..." fi ;; esac fi ;; ######################################################### ovrly) # ovrly@[RO|RW|NL[,USB|SQF]:[:][:][: into /var/_RW_/_ and softlinks files and directories from # PARMS: # :If set to "RO" existing files in will be kept and not replaced by another softlink # If set to "RW" existing files in will be replaced by subsequent files and directories # If set to "NL" files and directores in ovr_dir will not by symlinked into overlayed structure # :If set to SQF items from abs_ref are recursively symlinked from RO source to RW root paths # : Read Only dir that will be overlayed and look as being Read Write # : Absolute reference to file or directory which will be overlayed in / onto # Important Variables : # ro_source_ : variable pointing to path where "/" has been mounted to ( via mount -o bind ) # rw_target_ : variable pointing to path where RW strcuture will be recreated ######################################################### # Attention: # Even though it is possible to change files in /lib /bin and /sbin etc. it is recommended not to do so, # unless you really know what you do and why you do it! # Be aware that directory cannot be unmounted while other processes are accessing files on it. # A full uninstall will therefore only be possible after cfg_xxx applications have been put to "disable" mode # ( maintenance mode ) and the system has been rebooted. ######################################################### [ $i_ -ge 2 ] || errorexit "$funct_: Insufficient number of parameters" #set -x ro_source_=/var/_RO_ rw_target_=/var/_RW_ if ! mount | grep "on $ro_source_ " | sed "s/^\([^ ]*\) .*/\1/" | grep -q /dev/root;then [ -d $ro_source_ ] || mkdir_rcrsv_ $ro_source_ mount -o bind / $ro_source_ || errorexit "$funct_: Failed to mount / to $ro_source_" fi let x_=3 new_sftlnks_=0 tot_sftlnks_=0 mounted_=false case $parm2_ in /dev/pts* ) bind_on_="/dev/pts";; /etc* | /usr* | /bin* | /lib* | /sbin*) bind_on_=${parm2_%/${parm2_#?*/}};; * ) errorexit "$funct_: Overlaying not supported for directory $parm2_!";; esac root_bind_=$rw_target_/`echo $bind_on_ | sed "s/\//_/g"` ovr_dir_=$root_bind_${parm2_#$bind_on_*} [ -d $ovr_dir_ ] || mkdir_rcrsv_ $ovr_dir_ loop_="NORCRSV";access_="RO";no_lnkng_=false for sub_parms_ in `echo $parm1_ | sed "s/,/ /g"`;do case $sub_parms_ in SQF ) loop_="RCRSV";; USB ) loop_="NORCRSV";; RW ) access_="RW";; RO ) access_="RO";; NL ) no_lnkng_=true;; *) errorexit "$funct_: Unknown parameter $sub_parms_ in function!";; esac done #### Skip symlinking root dir if already mounted by another cfg_xxx application ### if ! mount | grep "on $bind_on_ " | sed "s/^\([^ ]*\) .*/\1/" | grep -q "[ramfs|tmpfs]";then #### Skip symlinking root dir if NO LINKING ("NL") parm set ### $no_lnkng_ || symlink_dir_contents_ $root_bind_ $ro_source_$bind_on_ $access_ mount --bind $root_bind_ $bind_on_ || errorexit "$funct_: Failed to mount --bind $root_bind_ to $bind_on_" fi while [ $x_ -le $i_ ] do ref_item_=`eval echo \\$parm${x_}_` symlink_dir_contents_ $ovr_dir_ $ref_item_ $access_ $loop_ let x_+=1 done [ $verbose ] && echo "INFORMATION: _fct $1: Total $tot_sftlnks_ softlinks added to $bind_on_/..." ;; ######################################################### mkdr) # mkdr@:[:][:]...[:] # Create directory path ... relative to # PARMS: # : absolute path to RW filesystem # : drectory paths to be created relative to [ $i_ -ge 2 ] || errorexit "$funct_: Insufficient number of parameters" let x_=2 while [ $x_ -le $i_ ] do mkdir_rcrsv_ $parm1_ `eval echo \\$parm${x_}_` let x_+=1 done ;; ######################################################### cpnr) # cpnr@[copy_options:]:[:file1][:file2]...[:file{n}] # Copy file1 ... file{n} ( or * if no files specified ) from to . File attributes will be preserved. # PARMS: # : options for cp command with leading "-" # : absolute path to copy from # : absolute target path to copy files to. will be created if not existing. # file{n}: optional file names to be copied. Copy "*" ( all ) assumed if no file name(s) specified unset options_ if [ $i_ -ge 3 -a ! -d "$parm1_" ]; then # assume $parm1_ contains options options_=$parm1_ shift_parms_ fi [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number ($i_) of parameters into function: $funct_" ! [ -d $parm2_ ] && mkdir_rcrsv_ $parm2_ if [ $i_ -gt 2 ];then let x_=3 while [ $x_ -le $i_ ] do cp $options_ $parm1_/`eval echo \\$parm${x_}_` $parm2_ let x_+=1 done else # copy all files unless directory is empty for f_ in $parm1_/*; do [ "$f_" = "$parm1_/*" ] && break cp $options_ $parm1_/* $parm2_ break done fi ;; ######################################################### cprs) # cprs@[copy_options:]:[:exclude_dir[:exclude_dir2[:...]]] # Copy recursively to . File attributes will be preserved. # PARMS: # : options for cp command with leading "-" # : absolute path to copy from # : absolute target path to copy files to. will be created if not existing. unset options_ if [ $i_ -ge 3 -a ! -d "$parm1_" ]; then # assume $parm1_ contains options options_=$parm1_ shift_parms_ fi [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number ($i_) of parameters into function: $funct_" [ -d $parm2_ ] || mkdir_rcrsv_ $parm2_ if [ $i_ -eq 2 ]; then for f_ in $parm1_/*; do [ "$f_" = "$parm1_/*" ] && break cp -R $options_ $parm1_/* $parm2_ break done else for f_ in $parm1_/*; do [ "$f_" = "$parm1_/*" ] && break unset is_exclude_dir_ let x_=3 while [ $x_ -le $i_ -a -z "$is_exclude_dir" ]; do exclude_dir_=`eval echo \\$parm${x_}_` [ `basename $f_` = $exclude_dir_ ] && is_exclude_dir_=1 let x_+=1 done [ "$is_exclude_dir_" ] && continue cp -R $options_ $f_ $parm2_ done fi ;; ######################################################### cpdf) # cpdf@[copy_options:]:[:exclude_dir[:exclude_dir2[:...]]] # Copy *.default from to . ".default" extension is omitted in . Existing files in WILL BE KEPT / NOT BE OVERWRITTEN. # PARMS: # : options for cp command with leading "-" # : absolute path to copy from # : absolute target path to copy files to. will be created if not existing. unset options_ if [ $i_ -eq 3 -a ! -d "$parm1_" ]; then # assume $parm1_ contains options options_=$parm1_ shift_parms_ fi [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number ($i_) of parameters into function: $funct_" ! [ -d $parm2_ ] && mkdir_rcrsv_ $parm2_ for f_ in $parm1_/*.default; do [ "$f_" = "$parm1_/*.default" ] && break f_=${f_##*/} unset is_exclude_dir_ let x_=3 while [ $x_ -le $i_ -a -z "$is_exclude_dir" ]; do exclude_dir_=`eval echo \\$parm${x_}_` [ $f_ = $exclude_dir_ ] && is_exclude_dir_=1 let x_+=1 done [ "$is_exclude_dir_" ] && continue if [ -f $parm2_/${f_%.default} ];then echo "not overwriting $parm2_/${f_%.default}" else cp $options_ $parm1_/$f_ $parm2_/${f_%.default} fi done ;; ######################################################### cpnL) # cpnL@[copy_options:]:[:exclude_dir[:exclude_dir2[:...]]] # Copy * from to . Ignore if file is SYMBOLIC LINK! # PARMS: # : options for cp command with leading "-" # : absolute path to copy from # : absolute target path to copy files to. will be created if not existing. unset options_ if [ $i_ -eq 3 -a ! -d "$parm1_" ]; then # assume $parm1_ contains options options_=$parm1_ shift_parms_ fi [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number ($i_) of parameters into function: $funct_" ! [ -d $parm2_ ] && mkdir_rcrsv_ $parm2_ for f_ in $parm1_/*;do [ "$f_" = "$parm1_/*" ] && break unset is_exclude_dir_ let x_=3 while [ $x_ -le $i_ -a -z "$is_exclude_dir" ]; do exclude_dir_=`eval echo \\$parm${x_}_` [ ${f_##*/} = $exclude_dir_ ] && is_exclude_dir_=1 let x_+=1 done [ "$is_exclude_dir_" ] && continue [ -L $f_ ] || cp $options_ $f_ $parm2_/${f_##*/} done ;; ######################################################### lnsf) # lnsf@<[target_dir/]link_name|:[:]...[:] # Create a link named link_name in current (or target) directory to # OR create one ore more symbolic links to in # (warning: existing directories will be removed and replaced by symbolic links) # PARMS: # : name of symbolic link to be created in current (or target) dir ( $PWD ) # : target path where symbolic links to will be created # : relative (or absolute) directory or file path to be linked in [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" let x_=2 while [ $x_ -le $i_ ] do softlink_=`eval echo \\$parm${x_}_` [ -d $parm1_/`basename $softlink_` ] && rm -r $parm1_/`basename $softlink_` ln -sf $softlink_ $parm1_ let x_+=1 done ;; ######################################################### lnsf2) # lnsf2@:[:]...[:]: # Create one ore more symbolic links to in # (warning: existing directories will be removed and replaced by symbolic links) # PARMS: # : name(s) (without path) of symbolic link(s) to be created in target dir # : target path where symbolic links to will be created # : relative (or absolute) directory or file path to be linked in [ $i_ -ge 3 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" softlinkpath_=`eval echo \\$parm${i_}_` let x_=2 while [ $x_ -lt $i_ ] do softlinkname_=`eval echo \\$parm${x_}_` [ -d $parm1_/$softlinkname_ ] && rm -r $parm1_/$softlinkname_ ln -sf $softlinkpath_ $parm1_/$softlinkname_ let x_+=1 done ;; ######################################################### lndf) # Create forced softlink to Files/Dirs to $parm2_/$parmX if no similar items exist in $parm1_ already if [ $parm1_ = "DEFAULT"];then [ $i_ -eq 3 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" shift_parms_ ! [ -d $parm2_ ] && mkdir_rcrsv_ $parm2_ for ro_item_ in $parm1_/*.default; do [ "$ro_item_" = "$parm1_/*.default" ] && break ro_item_=${ro_item_##*/} if [ -f $parm2_/${ro_item_%.default} ];then echo "not overwriting $parm2_/${ro_item_%.default}" else ln -sf $parm1_/$ro_item_ $parm2_/${ro_item_%.default} fi done else [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" if [ $i_ -gt 2 ];then let x_=3 while [ $x_ -le $i_ ] do softlink_=`eval echo \\$parm${x_}_` for ro_item_ in $parm2_/$softlink_;do if [ -f $parm1_/$ro_item_ -o -d $parm1_/$ro_item_ ];then echo "File exists in RW space. Not linking $parm2_/$ro_item_." else [ -L $parm1_/$ro_item_ ] || ln -sf $parm2_/$ro_item_ $parm1_ fi done let x_+=1 done else for ro_item_ in $parm2_/*;do [ "$ro_item_" = "$parm2_/*" ] && break if [ -f $parm1_/${ro_item_##*/} -o -d $parm1_/${ro_item_##*/} ]; then echo "File {ro_item_##*/} exists in RW space. Not linking $ro_item_." else ln -sf $ro_item_ $parm1_ fi done fi fi ;; ######################################################### lndfe) # Create forced softlink to Files/Dirs to $parm2_/$parmX if no similar items exist in $parm1_ already # parm1: directory into which symlinks are written # parm2: contents of this directory are the targets of the symlinks # optional: parm3, parm4 ... directories (or files) which have to be excluded [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" for ro_item_ in $parm2_/*;do [ "$ro_item_" = "$parm2_/*" ] && break unset is_exclude_dir_ if [ $i_ -gt 2 ]; then let x_=3 while [ $x_ -le $i_ -a -z "$is_exclude_dir" ]; do exclude_dir_=`eval echo \\$parm${x_}_` [ `basename $ro_item_` = $exclude_dir_ ] && is_exclude_dir_=1 let x_+=1 done fi [ "$is_exclude_dir_" ] && continue if [ -f $parm1_/${ro_item_##*/} -o -d $parm1_/${ro_item_##*/} ]; then echo "File {ro_item_##*/} exists in RW space. Not linking $ro_item_." else ln -sf $ro_item_ $parm1_ fi done ;; ######################################################### lndf2) # lndf2@:: # For files in / create forced symbolic links # pointing to /f (if f is a file in /) # All links are created in target dir; do not overwrite existing files # [ $i_ -eq 3 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" for ro_item_ in $parm2_/$parm3_/*;do [ "$ro_item_" = "$parm2_/$parm3_/*" ] && break if [ -f $parm1_/${ro_item_##*/} -o -d $parm1_/${ro_item_##*/} ]; then echo "File {ro_item_##*/} exists in RW space. Not linking to $ro_item_." else ln -sf $parm3_/${ro_item_##*/} $parm1_ fi done ;; ######################################################### wgetx | wgety) # Download $parm${n}_ files from source $parm2_ to directory $parm3_, if files not already in $parm3_ # wgetx@http|ftp:[//][user[:password]@]www.serverfrom.com:/target/dir:file1:[file2]:[file{n}] # wgety: like wgetx but continue in case of errors; just return false then. [ $i_ -ge 4 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" [ $parm1_ = "http" -o $parm1_ = "ftp" ] || errorexit "$funct_: Unknown protocol \"$parm1_\" used for wget function." # allow ftp://user:pw@server (only if passwort does not contain ":" or "@") if [ $parm3_ != ${parm3_%@*} ]; then parm3_=$parm2_:$parm3_ parm2_=$parm1_ shift_parms_ fi if [ $funct_ = wgety ]; then result_=0 else unset result_ fi let x_=4 while [ $x_ -le $i_ ] do file_=`eval echo \\$parm${x_}_` if [ -b $parm3_ ]; then # target is block device (e.g. ramdisk) # trim leading "//" if $parm2_ is in format "//www.spblinux.de" if ! wget $parm1_://${parm2_##*//}/$file_ -O $parm3_; then [ "$result_" ] || errorexit "$funct_: Couldn't download \"${parm2_##*//}/$file_\"" result_=1 fi else if ! [ -f $parm3_/$file_ ];then # trim leading "//" if $parm2_ is in format "//www.spblinux.de" if ! wget $parm1_://${parm2_##*//}/$file_ -O $parm3_/$file_; then [ "$result_" ] || errorexit "$funct_: Couldn't download \"${parm2_##*//}/$file_\"" result_=1 fi fi fi let x_+=1 done # create return value (true or false) for wgety [ "$result_" ] && [ $result_ -eq 0 ] ;; ######################################################### rdsk) # ramdisk helper functions # usage: _fct rdsk@ # usage: _fct rdsk@:ramdisk_device # usage: _fct rdsk@new:varname_ramdisk_device[:dir_with_freeramdisk] # usage: _fct rdsk@get-device:varname_ramdisk_device:mount_directory # start/stop: load/unload ramdisk kernel module # new: return a free (unmounted) ramdisk in variable given by $parm2_ # get_device: return at $parm3_ mounted ramdisk in variable given by $parm2_ # in_use: return true if ramdisk given by $parm2_ (absolute path!) is mounted # free: run freeramdisk for ramdisk given in $parm2_ # unset j_ [ $i_ -ge 1 ] && case $parm1_ in start) [ $i_ -le 2 ] && j_=1;; stop) [ $i_ -eq 1 ] && j_=1;; new | free) [ $i_ -eq 2 -o $i_ -eq 3 ] && j_=1;; in_use) [ $i_ -eq 2 ] && j_=1;; get_device) [ $i_ -eq 3 ] && j_=1;; esac [ "$j_" ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" result_=0 case $parm1_ in start) if uname -r |grep -q "^2.6"; then modext_=ko; else modext_=o; fi f_=rd.$modext_ while [ 1 ]; do lsmod |grep -q "^${f_%%.*} " && break [ -d "$parm2_" ] && f_=${parm2_%/}/$f_ get_missing_file_ -i $f_ && break errorexit "$funct_: failed to load ramdisk kernel module" done ;; stop) if [ "$verbose" ]; then rmmod rd; else rmmod rd 2>/dev/null; fi result_=$? [ $result_ -eq 0 ] || echo "warning ($funct_ $parm1_): failed to unload module rd (other mounted ramdisks?)" ;; get_device) ramdev_=`mount |grep "on $parm3_ " |sed "s/^\([^ ]*\) .*/\1/"` eval $parm2_=$ramdev_ [ -b "$ramdev_" ] || result_=$? ;; in_use) ! [ -b "$parm2_" ] && echo "warning ($funct_ $parm1_): blockdevice \"$parm2_\" does not exist" && break mount |grep -q "^$parm2_ " || result_=$? ;; new | free) if [ $parm1_ = free ]; then ramdev_=$parm2_ else for j_ in 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 none; do mount |sed "s@^\([^ ]\+\).*@\1@" |grep -q "^/[dv][ea][vr]/r[da][/m]$j_" && continue break done [ $j_ = none ] && errorexit "ERROR: no free ramdisk found" [ -b /var/ram$j_ ] || mknod /var/ram$j_ b 1 $j_ ramdev_=/var/ram$j_ eval $parm2_=/var/ram$j_ fi f_=freeramdisk [ -d "$parm3_" ] && f_=${parm3_%/}/$f_ while [ 1 ]; do ! [ -b "$ramdev_" ] && echo "warning ($funct_ $parm1_): blockdevice \"$ramdev_\" does not exist" && break get_missing_file_ -X $f_ $ramdev_ && break echo "warning ($funct_ $parm1_): failed to free $ramdev_" break done result_=$? ;; esac # create return value (true or false) [ "$result_" ] && [ $result_ -eq 0 ] ;; ######################################################### mntif) # mount $parm${n-1} to parm${n} using optional mount flags defined in $parm${n-2}, $parm${n-3}, ... [ $i_ -ge 2 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" [ -d `eval echo \\$parm${i_}_` ] || mkdir_rcrsv_ `eval echo \\$parm${i_}_` mount | grep -q "on `eval echo \\$parm${i_}_`" || mount $args_ [ $? -eq 0 ] || errorexit "$funct_: Failed to mount `eval echo \\$parm${i_}_`" ;; fallback) # called if lzma mount of squashfs ramdisk fails ($parm1_=[path]sqf_file) # - if sqf_file is given with path, rename sqf_file to *.lmza if [ $modext = o -a -z "$svr24" ]; then for f_ in $parm1_ $parm2_; do if [ -f "$f_" ]; then if [ -f "$f_.lzma" ]; then rm $f_; else mv $f_ $f_.lzma; fi [ -f "$f_" ] && errorexit "ERROR: cannot remove unmountable sqf file $f_" fi done echo "warning: mount (or download) failure. - Trying fallback to zlib 16kB compression" echo "(if zlib install is successful, e.g. pre 2006 firmware, you should use" echo " ${0##*/} -z install (zlib) or ${0##*/} -Z install (zlib 16kb blocksize))" echo echo "Restarting installation with ${0##*/} -Z install" exec $0 -Z install else errorexit "ERROR: failed to download or to mount $parm1_" fi ;; modext) # has to be listed as non verbose at the beginning of _fct() # returns "ko" (linux 2.6) or "o" (linux 2.4) str_="o" uname -r |grep -q "^2.6" && str_="ko" echo "$str_" unset str_ ;; svrsub) # has to be listed as non verbose at the beginning of _fct() # returns the sub directory used for firmware version specific files # (which is user defined or defaults to /24, /26, /26-ar7) if uname -r |grep -q "^2.6"; then str_="/26" [ "$svr26" ] && str_="${svr26%/}" uname -r |grep "^2\.6" |grep -q "ar7$" && str_=${str_}-ar7 uname -r |grep "^2\.6" |grep -q "\.19" && str_=${str_}-ur8 else str_="/24" [ "$svr24" ] && str_="${svr24%/}" fi echo "/${str_#/}" unset str_ ;; path_abs) # has to be listed as non verbose at the beginning of _fct() # usage: path_abs@filename # returns absolute path to file given by filename # (without trailing slash and without filename) [ $i_ -eq 1 ] || errorexit "$funct_: Incorrect number of parameters into function: $funct_" if [ "${parm1_%%/*}" = "" ];then # $parm1_ is starting with "/" ( absolute path ) str_=${parm1_%/*} else if [ "${parm1_%%/*}" = "." ];then # $parm1_ is starting with "./" ( relative path to $PWD) str_=$PWD/${0#./*} str_=${str_%/*} else # $parm1_ is neither starting with "./" nor with "/" ( relative path to $PWD ) str_=$PWD/${parm1_%/*} fi fi echo "$str_" unset str_ ;; *) errorexit "_fct: Function <$funct_> is not defined" ;; esac }