#!/bin/bash ############################################################################### # # # IPFire.org - A linux based firewall # # Copyright (C) 2010 Michael Tremer & Christian Schmidt # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation, either version 3 of the License, or # # (at your option) any later version. # # # # 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. # # # # You should have received a copy of the GNU General Public License # # along with this program. If not, see . # # # ############################################################################### # A simple print statement print() { local fmt=${1}; shift printf -- "${fmt}\n" "$@" } # The args() function takes a number of arguments like # var1="abc d" var2="abc" var3="abcd e" # and splits them into several arguments, devided by newline args() { echo "$@" | xargs printf "%s\n" } unquote() { local var="$@" if [ "${var:0:1}" = "\"" ]; then var=${var:1} fi local last=$(( ${#var} - 1 )) if [ ${last} -ge 0 ] && [ "${var:${last}:1}" = "\"" ]; then var=${var:0:${last}} fi print "${var}" } quote() { print "\"%s\"" "$@" } strip() { local value="$@" # remove leading whitespace characters value="${value#"${value%%[![:space:]]*}"}" # remove trailing whitespace characters value="${value%"${value##*[![:space:]]}"}" print "${value}" } # Print a pretty error message error() { echo -e " ${CLR_RED_B}ERROR${CLR_RESET} : $@" >&2 } error_log() { log ERROR "$@" } # Print a pretty warn message warning() { echo -e " ${CLR_YELLOW_B}WARNING${CLR_RESET}: $@" >&2 } warning_log() { log WARNING "$@" } # Speedup function to avoid a call of the basename binary basename() { echo "${1##*/}" } format() { local key=${1} assert isset key local format=${2} assert isset format shift 2 printf -v "${key}" "${format}" "$@" } format_time() { local s=${1} local ret m local units="s m h" local unit for unit in ${units}; do m=$(( ${s} % 60 )) s=$(( ${s} / 60 )) if [ ${m} -gt 0 ]; then ret="${m}${unit} ${ret}" fi done # Remove whitespace echo ${ret} } parse_time() { local ret=0 local arg for arg in $@; do local unit case "${arg}" in *h|*m|*s) # Store unit unit="${arg: -1}" # Remove unit arg="${arg:0:-1}" ;; esac if ! isinteger arg; then return ${EXIT_ERROR} fi # Convert hours and minutes into seconds case "${unit}" in h) arg=$(( ${arg} * 3600 )) ;; m) arg=$(( ${arg} * 60 )) ;; esac # Add up everything ret=$(( ${ret} + ${arg} )) done print "${ret}" } assign() { local key=${1} assert isset key shift format "${key}" "%s" "$@" } fread() { local file=${1} assert isset file [ -r "${file}" ] || return ${EXIT_ERROR} print "$(<${file})" } fwrite() { local file=${1} assert isset file shift if [ ! -w "${file}" ]; then log ERROR "${file}: No such file" return ${EXIT_ERROR} fi print "%s" "$@" >> ${file} 2>/dev/null } make_parent_dir() { local path="${1}" local dirname="$(dirname "${path}")" mkdir -p "${dirname}" } enabled() { local param=${1} list_match "${!param}" yes on true 1 } mac_generate() { local b="$(random 12)" # Remove multicast bit # and set address is software assigned local first_byte=$(( 0x${b:0:2} & 0xfe )) first_byte=$(( ${first_byte} | 0x02 )) local output printf -v output "%02x" "${first_byte}" output="${output}:${b:2:2}:${b:4:2}:${b:6:2}:${b:8:2}:${b:10:2}" # Check if output is valid assert mac_is_valid "${output}" echo "${output}" } mac_format() { local mac=${1} assert isset mac # Remove all colons and make the rest lowercase. mac=${mac//:/} mac=${mac,,} local output if [ "${#mac}" = "12" ]; then # Add colons (:) to mac address output=${mac:0:2} local i for i in 2 4 6 8 10; do output="${output}:${mac:${i}:2}" done else output=${mac} fi assert mac_is_valid ${output} print "${output}" } mac_is_valid() { local mac=${1} [[ ${mac} =~ ^([0-9a-f]{2}\:){5}[0-9a-f]{2}$ ]] } uuid() { echo $(}:${BASH_LINENO[${i}]}" done } assert() { local assertion="$@" if ! ${assertion}; then error_log "Assertion '${assertion}' failed." backtrace exit ${EXIT_ERROR_ASSERT} fi return ${EXIT_OK} } # This function checks, if the given argument is an assert error # exit code. If this is the case, the script will halt immediately. assert_check_retval() { local ret=${1} if [ ${ret} -eq ${EXIT_ERROR_ASSERT} ]; then exit ${EXIT_ERROR_ASSERT} fi return ${ret} } # This function executes the given command and inverses the return code not() { local command="$@" ${command} && return ${EXIT_FALSE} || return ${EXIT_TRUE} } exec_cmd() { local cmd=$@ log DEBUG "Running command: ${cmd}" DEBUG=${DEBUG} \ LOG_DISABLE_STDOUT="${LOG_DISABLE_STDOUT}" \ LOG_FACILITY="${LOG_FACILITY}" \ ${SHELL} ${cmd} local ret=$? #log DEBUG "Returned with code '${ret}'" if [ ${ret} -eq ${EXIT_ERROR_ASSERT} ]; then error_log "Stopping parent process due to assertion error in child process: ${cmd}" exit ${EXIT_ERROR_ASSERT} fi return ${ret} } cmd() { local cmd=$@ log DEBUG "Running command: ${cmd}" ${cmd} local ret=$? log DEBUG "Returned with code '${ret}'" return ${ret} } cmd_quiet() { cmd $@ &>/dev/null } cmd_exec() { local cmd=$@ log DEBUG "Exec'ing command: ${cmd}" exec ${cmd} log ERROR "Could not exec-ute: ${cmd}" exit ${EXIT_ERROR} } cmd_not_implemented() { assert false "not implemented" } # Runs a command in a clean environment so that no confidential information # is leaked to any untrusted commands. cmd_clean_environment() { local cmd=$@ log DEBUG "Running command in a clean environment: ${cmd}" env -i -- ${cmd} local ret=${?} log DEBUG "Returned with code '${ret}'" return ${ret} } # Executes the given command in background cmd_background() { cmd_quiet $@ & } # Prints the PID of the process that was started last cmd_background_get_pid() { print "${!}" } cmd_background_result() { local pids=$@ wait ${pids} } # Increase security of the read command read() { builtin read -r $@ } seq() { if [ $# -eq 2 ]; then eval echo {${1}..${2}} elif [ $# -eq 3 ]; then eval echo {${1}..${3}..${2}} fi } range() { eval echo {0..$(( ${1} - 1 ))} } count() { local i=0 while read; do ((i++)) done echo ${i} } which() { type -P $@ } # Prints the number of seconds since epoch. timestamp() { date -u "+%s" } beautify_time() { local value=${1} local unit local limit for unit in s m h d w; do case "${unit}" in s|m|h) limit=60 ;; d) limit=24 ;; w) limit=7 ;; esac [ ${value} -lt ${limit} ] && break value=$(( ${value} / ${limit} )) done echo "${value}${unit}" } beautify_bytes() { local value=${1} local unit local limit=1024 for unit in B k M G T; do [ ${value} -lt ${limit} ] && break value=$(( ${value} / ${limit} )) done echo "${value}${unit}" } module_load() { local module=${1} if ! grep -q "^${module}" /proc/modules; then log DEBUG "Loading module '${module}'." modprobe ${module} fi } binary_exists() { local binary=${1} if [ -n "$(type -p ${binary})" ]; then return ${EXIT_OK} fi return ${EXIT_ERROR} } function_exists() { local function="${1}" if [ "$(type -t "${function}")" = "function" ]; then return ${EXIT_TRUE} fi return ${EXIT_FALSE} } process_kill() { local process=${1} if ! isinteger process; then process=$(pidof ${process}) fi local pid local sig for pid in ${process}; do for sig in 15 9; do [ -d "/proc/${pid}" ] || break kill -${sig} ${pid} sleep 1 done done } dec() { local hex=${1} if [ "${hex:0:2}" != "0x" ]; then hex="0x${hex}" fi printf "%d\n" "${hex}" } chr() { local char="${1}" [ ${char} -lt 256 ] || return ${EXIT_ERROR} printf "\\$(( ${char} / 64 * 100 + ${char} % 64 / 8 * 10 + ${char} % 8 ))\n" } ord() { LC_CTYPE="C" printf "%d\n" "'${1}" } hex() { printf "%X\n" "${1}" } network_is_running() { # Check, if the network service is running. service_is_active network } contains_spaces() { local var="$@" # Eliminate spaces. local var2=${var// /} if [ ${#var} -ne ${#var2} ]; then return ${EXIT_TRUE} fi return ${EXIT_FALSE} } string_split() { local string="$@" local pos=0 while [ ${pos} -lt ${#string} ]; do print "${string:${pos}:1}" pos=$(( ${pos} + 1 )) done return ${EXIT_OK} } string_reverse() { local string="$@" local output local pos=0 while [ ${pos} -lt ${#string} ]; do output="${string:${pos}:1}${output}" pos=$(( ${pos} + 1 )) done print "${output}" return ${EXIT_OK} } dec2bin() { local number="${1}" local output local i div for i in 7 6 5 4 3 2 1; do div=$(( 2 ** ${i} )) if [ $(( ${number} / ${div} )) -eq 1 ]; then output="${output}1" else output="${output}0" fi number="$(( ${number} % ${div} ))" done if [ $(( ${number} % 2 )) -eq 1 ]; then output="${output}1" else output="${output}0" fi print "${output}" } bin2dec() { local string="${1}" local number=0 local pos=0 char while [ ${pos} -lt ${#string} ]; do char="${string:${pos}:1}" pos=$(( ${pos} + 1 )) number=$(( ${number} << 1 )) case "${char}" in 0) ;; 1) number=$(( ${number} + 1 )) ;; *) assert false "Invalid character: ${char}" ;; esac done print "${number}" return ${EXIT_OK} } char2bin() { local dec="$(ord "${1}")" dec2bin "${dec}" } bin2char() { local dec="$(bin2dec "$@")" chr "${dec}" } bin2hex() { local dec="$(bin2dec "$@")" dec2hex "${dec}" } hex2bin() { local dec="$(hex2dec "$@")" dec2bin "${dec}" } hex2dec() { local hex="${1}" # Prepend 0x if necessary. [ "${hex:0:2}" = "0x" ] || hex="0x${hex}" printf "%d\n" "${hex}" } dec2hex() { printf "%02x\n" "${1}" } # This function just copy config files copy() { assert [ $# -eq 2 ] local src=${1} local dst=${2} # Check if ${dst} is a directory if [ -d "${dst}" ]; then log ERROR "${dst} is a directory" return ${EXIT_ERROR} fi if ! fread "${src}" > "${dst}"; then log ERROR "Could not copy data from ${src} to ${dst}" return ${EXIT_ERROR} fi }