]> git.ipfire.org Git - people/ms/network.git/blame - src/functions/functions.util
util: add normalize function
[people/ms/network.git] / src / functions / functions.util
CommitLineData
1848564d
MT
1#!/bin/bash
2###############################################################################
3# #
4# IPFire.org - A linux based firewall #
5# Copyright (C) 2010 Michael Tremer & Christian Schmidt #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
21
97cb552e 22# A simple print statement
1c6a4e30 23print() {
acc9efd5
MT
24 local fmt=${1}; shift
25
40e3553f 26 printf -- "${fmt}\n" "$@"
97cb552e
MT
27}
28
67baa452
MT
29print_indent() {
30 local i=${1}
31 shift
32
33 while (( i-- )); do
34 printf "\t"
35 done
36
37 print "%s" "$@"
38}
39
cb965348
MT
40# The args() function takes a number of arguments like
41# var1="abc d" var2="abc" var3="abcd e"
42# and splits them into several arguments, devided by newline
1c6a4e30 43args() {
cb965348
MT
44 echo "$@" | xargs printf "%s\n"
45}
46
1c6a4e30 47unquote() {
04854c77
MT
48 local var="$@"
49
50 if [ "${var:0:1}" = "\"" ]; then
51 var=${var:1}
52 fi
53
54 local last=$(( ${#var} - 1 ))
55 if [ ${last} -ge 0 ] && [ "${var:${last}:1}" = "\"" ]; then
56 var=${var:0:${last}}
57 fi
58
59 print "${var}"
60}
61
1c6a4e30 62quote() {
04854c77
MT
63 print "\"%s\"" "$@"
64}
65
1c6a4e30 66strip() {
fe52c5e0
MT
67 local value="$@"
68
69 # remove leading whitespace characters
70 value="${value#"${value%%[![:space:]]*}"}"
71
72 # remove trailing whitespace characters
73 value="${value%"${value##*[![:space:]]}"}"
74
75 print "${value}"
76}
77
1848564d 78# Print a pretty error message
1c6a4e30 79error() {
fcbf6823 80 echo -e " ${CLR_RED_B}ERROR${CLR_RESET} : $@" >&2
1848564d
MT
81}
82
1c6a4e30 83error_log() {
1b7a1578
MT
84 log ERROR "$@"
85}
86
1848564d 87# Print a pretty warn message
1c6a4e30 88warning() {
fcbf6823 89 echo -e " ${CLR_YELLOW_B}WARNING${CLR_RESET}: $@" >&2
1848564d
MT
90}
91
1c6a4e30 92warning_log() {
1b7a1578
MT
93 log WARNING "$@"
94}
95
1848564d 96# Speedup function to avoid a call of the basename binary
1c6a4e30 97basename() {
1848564d
MT
98 echo "${1##*/}"
99}
100
1c6a4e30 101format() {
e5651e17
MT
102 local key=${1}
103 assert isset key
104
105 local format=${2}
106 assert isset format
107
108 shift 2
109
110 printf -v "${key}" "${format}" "$@"
111}
112
d13929d4
MT
113format_time() {
114 local s=${1}
115 local ret m
116
117 local units="s m h"
118
119 local unit
120 for unit in ${units}; do
121 m=$(( ${s} % 60 ))
122 s=$(( ${s} / 60 ))
123
124 if [ ${m} -gt 0 ]; then
125 ret="${m}${unit} ${ret}"
126 fi
127 done
128
129 # Remove whitespace
130 echo ${ret}
131}
132
b383499d
MT
133parse_time() {
134 local ret=0
135
136 local arg
2212045f 137 for arg in "$@"; do
b383499d
MT
138 local unit
139
140 case "${arg}" in
141 *h|*m|*s)
142 # Store unit
143 unit="${arg: -1}"
144
145 # Remove unit
146 arg="${arg:0:-1}"
147 ;;
148 esac
149
150 if ! isinteger arg; then
151 return ${EXIT_ERROR}
152 fi
153
154 # Convert hours and minutes into seconds
155 case "${unit}" in
156 h)
157 arg=$(( ${arg} * 3600 ))
158 ;;
159 m)
160 arg=$(( ${arg} * 60 ))
161 ;;
162 esac
163
164 # Add up everything
165 ret=$(( ${ret} + ${arg} ))
166 done
167
168 print "${ret}"
169}
170
1c6a4e30 171assign() {
b79ad79b
MT
172 local key=${1}
173 assert isset key
174 shift
175
e5651e17 176 format "${key}" "%s" "$@"
b79ad79b
MT
177}
178
1c6a4e30 179fread() {
b79ad79b
MT
180 local file=${1}
181 assert isset file
182
183 [ -r "${file}" ] || return ${EXIT_ERROR}
184
185 print "$(<${file})"
186}
187
1c6a4e30 188fwrite() {
b79ad79b
MT
189 local file=${1}
190 assert isset file
191 shift
192
644d3bb8
MT
193 if ! print "%s" "$@" > ${file} 2>/dev/null; then
194 error "Could not write to file: ${file}"
195 return ${EXIT_ERROR}
196 fi
197
198 return ${EXIT_OK}
199}
200
201fappend() {
202 local file=${1}
203 assert isset file
204 shift
205
e1947a76 206 if [ -e "${file}" ] && [ ! -w "${file}" ]; then
8d4e0d52
MT
207 log ERROR "${file}: No such file"
208 return ${EXIT_ERROR}
209 fi
210
211 print "%s" "$@" >> ${file} 2>/dev/null
b79ad79b
MT
212}
213
fa33d830
MT
214file_delete() {
215 local file=${1}
216
217 unlink "${file}" 2>/dev/null
218}
219
e1947a76
MT
220file_exists() {
221 local file=${1}
222
223 [ -e "${file}" ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
224}
225
226file_is_newer_than() {
227 local file1="${1}"
228 local file2="${2}"
229
230 local age1=$(file_get_age "${file1}")
231 local age2=$(file_get_age "${file2}")
232
233 if [ ${age1} -gt ${age2} ]; then
234 return ${EXIT_TRUE}
235 else
236 return ${EXIT_FALSE}
237 fi
238}
239
240file_get_age() {
241 local file="${1}"
242
243 if [ -e "${file}" ]; then
244 stat --format="%Y" "${file}"
245 return $?
246 fi
247
248 return ${EXIT_ERROR}
249}
250
04c27fe9
MT
251make_directory() {
252 local path="${1}"
253
254 mkdir -p "${path}"
255}
256
46954be3 257make_parent_directory() {
c041b631
MT
258 local path="${1}"
259
04c27fe9 260 make_directory "$(dirname "${path}")"
c041b631
MT
261}
262
1c6a4e30 263enabled() {
1848564d
MT
264 local param=${1}
265
e726ef8d 266 list_match "${!param}" yes on true 1
1848564d
MT
267}
268
1c6a4e30 269mac_generate() {
fb1416c6
MT
270 local b="$(random 12)"
271
272 # Remove multicast bit
273 # and set address is software assigned
274 local first_byte=$(( 0x${b:0:2} & 0xfe ))
275 first_byte=$(( ${first_byte} | 0x02 ))
1848564d
MT
276
277 local output
fb1416c6 278 printf -v output "%02x" "${first_byte}"
790b7ec9 279
fb1416c6 280 output="${output}:${b:2:2}:${b:4:2}:${b:6:2}:${b:8:2}:${b:10:2}"
1848564d
MT
281
282 # Check if output is valid
fb1416c6 283 assert mac_is_valid "${output}"
1848564d 284
790b7ec9 285 echo "${output}"
1848564d
MT
286}
287
1c6a4e30 288mac_format() {
18b43372 289 local mac=${1}
48bc31eb 290 assert isset mac
18b43372 291
48bc31eb
MT
292 # Remove all colons and make the rest lowercase.
293 mac=${mac//:/}
294 mac=${mac,,}
18b43372 295
48bc31eb 296 local output
18b43372
MT
297 if [ "${#mac}" = "12" ]; then
298 # Add colons (:) to mac address
299 output=${mac:0:2}
300 local i
301 for i in 2 4 6 8 10; do
302 output="${output}:${mac:${i}:2}"
303 done
48bc31eb
MT
304 else
305 output=${mac}
18b43372
MT
306 fi
307
308 assert mac_is_valid ${output}
309
48bc31eb 310 print "${output}"
18b43372
MT
311}
312
1c6a4e30 313mac_is_valid() {
1848564d
MT
314 local mac=${1}
315
316 [[ ${mac} =~ ^([0-9a-f]{2}\:){5}[0-9a-f]{2}$ ]]
317}
318
1c6a4e30 319uuid() {
de543653 320 echo $(</proc/sys/kernel/random/uuid)
1848564d
MT
321}
322
a24cff8f
JS
323abs() {
324 local val=${1}
325
326 if [ ${val} -lt 0 ]; then
327 (( val *= -1 ))
328 fi
329
330 echo ${val}
331}
332
fb1416c6
MT
333rand() {
334 local uuid="$(uuid)"
335 echo "${uuid//-/}"
336}
337
338random() {
339 local length="${1:-8}"
340
341 local random
342 while [ ${#random} -lt ${length} ]; do
343 random="${random}$(rand)"
344 done
345
346 echo "${random:0:${length}}"
347}
348
1c6a4e30 349isset() {
1848564d
MT
350 local var=${1}
351
352 [ -n "${!var}" ]
353}
354
1c6a4e30 355isoneof() {
1848564d
MT
356 local var=${!1}
357 shift
358
e726ef8d 359 list_match "${var}" "$@"
1848564d
MT
360}
361
1c6a4e30 362isbool() {
1848564d
MT
363 local var=${1}
364
ec6afbdd 365 isoneof ${var} 0 1 no yes on off true false
1848564d
MT
366}
367
1c6a4e30 368isinteger() {
1848564d
MT
369 local var=${!1}
370
371 [[ ${var} =~ ^[0-9]+$ ]]
372}
373
1c6a4e30 374ismac() {
1848564d
MT
375 local mac=${!1}
376
377 mac_is_valid ${mac}
378}
379
1c6a4e30 380isipaddress() {
fef4edaf
MT
381 local addr=${!1}
382
383 ip_is_valid ${addr}
384}
385
48a64768
JS
386mtu_is_valid() {
387 local proto=${1}
388 local mtu=${2}
389
390 case ${proto} in
391 ipv4)
392 [ ${mtu} -ge 576 ] && [ ${mtu} -le 9000 ]
393 ;;
394 ipv6)
395 [ ${mtu} -ge 1280 ] && [ ${mtu} -le 9000 ]
396 ;;
397 *)
398 error "${proto} is not a valid proto"
399 return ${EXIT_ERROR}
400 ;;
401 esac
402}
403
1c6a4e30 404backtrace() {
711ffac1
MT
405 local start=1
406
407 echo # Empty line
408 error_log "Backtrace (most recent call in first line):"
409
04608623 410 local i source
711ffac1
MT
411 for i in $(seq ${start} ${#BASH_SOURCE[*]}); do
412 [ -z "${FUNCNAME[${i}]}" ] && continue
6396ccab
MT
413
414 # Print called binary with arguments.
415 if [ "${FUNCNAME[${i}]}" == "main" ]; then
416 local args="$(list_reverse ${BASH_ARGV[*]})"
417 printf -v source "%20s" "$0"
418 error_log " ${source} ${args}"
419 continue
420 fi
711ffac1 421
04608623
MT
422 source=${BASH_SOURCE[$(( ${i} + 1 ))]}
423 error_log " $(printf "%20s" "'${FUNCNAME[${i}]}'") called from ${source:-<shell>}:${BASH_LINENO[${i}]}"
711ffac1
MT
424 done
425}
426
1c6a4e30 427assert() {
1848564d
MT
428 local assertion="$@"
429
430 if ! ${assertion}; then
4c670d7c 431 error_log "Assertion '${assertion}' failed."
711ffac1 432 backtrace
cfbe0802 433 exit ${EXIT_ERROR_ASSERT}
1848564d
MT
434 fi
435
436 return ${EXIT_OK}
437}
cad8bd85 438
b0b2f995
MT
439# This function checks, if the given argument is an assert error
440# exit code. If this is the case, the script will halt immediately.
1c6a4e30 441assert_check_retval() {
b0b2f995
MT
442 local ret=${1}
443
444 if [ ${ret} -eq ${EXIT_ERROR_ASSERT} ]; then
445 exit ${EXIT_ERROR_ASSERT}
446 fi
447
448 return ${ret}
449}
450
2bb20bbd
SS
451# This function executes the given command and inverses the return code
452not() {
453 local command="$@"
454
455 ${command} && return ${EXIT_FALSE} || return ${EXIT_TRUE}
456}
457
1c6a4e30 458exec_cmd() {
711ffac1
MT
459 local cmd=$@
460
461 log DEBUG "Running command: ${cmd}"
462
b816e04b 463 DEBUG=${DEBUG} \
8c63fa13
MT
464 LOG_DISABLE_STDOUT="${LOG_DISABLE_STDOUT}" \
465 LOG_FACILITY="${LOG_FACILITY}" \
b816e04b 466 ${SHELL} ${cmd}
711ffac1
MT
467 local ret=$?
468
469 #log DEBUG "Returned with code '${ret}'"
470
471 if [ ${ret} -eq ${EXIT_ERROR_ASSERT} ]; then
472 error_log "Stopping parent process due to assertion error in child process: ${cmd}"
473 exit ${EXIT_ERROR_ASSERT}
474 fi
475
476 return ${ret}
477}
478
1c6a4e30 479cmd() {
b816e04b
MT
480 local cmd=$@
481
482 log DEBUG "Running command: ${cmd}"
483
4b75fc83
MT
484 env -i -- \
485 HOME="${HOME}" \
486 PATH="${PATH}" \
487 TERM="${TERM}" \
488 ${cmd}
489 local ret=$?
b816e04b 490
4b75fc83
MT
491 case "${ret}" in
492 ${EXIT_OK})
493 return ${EXIT_OK}
494 ;;
495 *)
496 log DEBUG "Returned with code '${ret}'"
497 return ${ret}
498 ;;
499 esac
b816e04b
MT
500}
501
1c6a4e30 502cmd_quiet() {
2212045f 503 cmd "$@" &>/dev/null
3efecbb3
MT
504}
505
1c6a4e30 506cmd_exec() {
f80ce052
MT
507 local cmd=$@
508
509 log DEBUG "Exec'ing command: ${cmd}"
510
511 exec ${cmd}
512
513 log ERROR "Could not exec-ute: ${cmd}"
514 exit ${EXIT_ERROR}
515}
516
1c6a4e30 517cmd_not_implemented() {
2181765d
MT
518 assert false "not implemented"
519}
520
f5ee091e
MT
521# Executes the given command in background
522cmd_background() {
2212045f 523 cmd_quiet "$@" &
f5ee091e
MT
524}
525
526# Prints the PID of the process that was started last
527cmd_background_get_pid() {
528 print "${!}"
529}
530
531cmd_background_result() {
532 local pids=$@
533
534 wait ${pids}
535}
536
b8026986 537# Increase security of the read command
1c6a4e30 538read() {
2212045f 539 builtin read -r "$@"
b8026986
MT
540}
541
1c6a4e30 542seq() {
3efecbb3
MT
543 if [ $# -eq 2 ]; then
544 eval echo {${1}..${2}}
545 elif [ $# -eq 3 ]; then
546 eval echo {${1}..${3}..${2}}
547 fi
548}
549
de72bd91
MT
550range() {
551 eval echo {0..$(( ${1} - 1 ))}
552}
553
554count() {
555 local i=0
556
557 while read; do
558 ((i++))
559 done
560
561 echo ${i}
562}
563
1c6a4e30 564which() {
2212045f 565 type -P "$@"
76e6cd51
MT
566}
567
fe52c5e0 568# Prints the number of seconds since epoch.
1c6a4e30 569timestamp() {
fe52c5e0
MT
570 date -u "+%s"
571}
572
1c6a4e30 573beautify_time() {
d82cf370
MT
574 local value=${1}
575
576 local unit
577 local limit
578 for unit in s m h d w; do
579 case "${unit}" in
580 s|m|h)
581 limit=60
582 ;;
583 d)
584 limit=24
585 ;;
586 w)
587 limit=7
588 ;;
589 esac
590
591 [ ${value} -lt ${limit} ] && break
592
593 value=$(( ${value} / ${limit} ))
594 done
595
596 echo "${value}${unit}"
597}
711ffac1 598
1c6a4e30 599beautify_bytes() {
711ffac1
MT
600 local value=${1}
601
602 local unit
603 local limit=1024
604 for unit in B k M G T; do
605 [ ${value} -lt ${limit} ] && break
606 value=$(( ${value} / ${limit} ))
607 done
608
609 echo "${value}${unit}"
610}
943e3f7e 611
1c6a4e30 612module_load() {
943e3f7e
MT
613 local module=${1}
614
dd891ec4
MT
615 # Do nothing if the module is already loaded
616 if [ -d "/sys/module/${module//-/_}" ]; then
617 return ${EXIT_OK}
943e3f7e 618 fi
dd891ec4
MT
619
620 log DEBUG "Loading kernel module ${module}"
621 modprobe "${module}"
943e3f7e 622}
6b3f9c85 623
1c6a4e30 624binary_exists() {
6b3f9c85
MT
625 local binary=${1}
626
627 if [ -n "$(type -p ${binary})" ]; then
628 return ${EXIT_OK}
629 fi
630
631 return ${EXIT_ERROR}
632}
d76f5107 633
1c6a4e30 634function_exists() {
1e6f187e
MT
635 local function="${1}"
636
637 if [ "$(type -t "${function}")" = "function" ]; then
638 return ${EXIT_TRUE}
639 fi
640
641 return ${EXIT_FALSE}
642}
643
1c6a4e30 644process_kill() {
d76f5107
MT
645 local process=${1}
646
647 if ! isinteger process; then
648 process=$(pidof ${process})
649 fi
650
651 local pid
652 local sig
653 for pid in ${process}; do
654 for sig in 15 9; do
655 [ -d "/proc/${pid}" ] || break
656
657 kill -${sig} ${pid}
658 sleep 1
659 done
660 done
661}
feb76eaf 662
1c6a4e30 663dec() {
feb76eaf
MT
664 local hex=${1}
665
666 if [ "${hex:0:2}" != "0x" ]; then
667 hex="0x${hex}"
668 fi
669
670 printf "%d\n" "${hex}"
671}
3a7fef62 672
1c6a4e30 673chr() {
5cf0edf9
MT
674 local char="${1}"
675
676 [ ${char} -lt 256 ] || return ${EXIT_ERROR}
677
678 printf "\\$(( ${char} / 64 * 100 + ${char} % 64 / 8 * 10 + ${char} % 8 ))\n"
679}
680
1c6a4e30 681ord() {
5cf0edf9
MT
682 LC_CTYPE="C" printf "%d\n" "'${1}"
683}
684
1c6a4e30 685hex() {
5cf0edf9
MT
686 printf "%X\n" "${1}"
687}
688
1c6a4e30 689network_is_running() {
3a7fef62
MT
690 # Check, if the network service is running.
691 service_is_active network
692}
f80ce052 693
1c6a4e30 694contains_spaces() {
f80ce052
MT
695 local var="$@"
696
697 # Eliminate spaces.
698 local var2=${var// /}
699
700 if [ ${#var} -ne ${#var2} ]; then
701 return ${EXIT_TRUE}
702 fi
703
704 return ${EXIT_FALSE}
0d645497
MT
705}
706
707string_match() {
708 local match=${1}
709 local string=${2}
710
711 [[ ${string} =~ ${match} ]] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
f80ce052 712}
5cf0edf9 713
1c6a4e30 714string_split() {
5cf0edf9
MT
715 local string="$@"
716
717 local pos=0
718 while [ ${pos} -lt ${#string} ]; do
719 print "${string:${pos}:1}"
720 pos=$(( ${pos} + 1 ))
721 done
722
723 return ${EXIT_OK}
724}
725
1c6a4e30 726string_reverse() {
5cf0edf9
MT
727 local string="$@"
728
729 local output
730 local pos=0
731 while [ ${pos} -lt ${#string} ]; do
732 output="${string:${pos}:1}${output}"
733 pos=$(( ${pos} + 1 ))
734 done
735
736 print "${output}"
737 return ${EXIT_OK}
738}
739
1c6a4e30 740dec2bin() {
5cf0edf9
MT
741 local number="${1}"
742
743 local output
744
745 local i div
746 for i in 7 6 5 4 3 2 1; do
747 div=$(( 2 ** ${i} ))
748
749 if [ $(( ${number} / ${div} )) -eq 1 ]; then
750 output="${output}1"
751 else
752 output="${output}0"
753 fi
754 number="$(( ${number} % ${div} ))"
755 done
756
757 if [ $(( ${number} % 2 )) -eq 1 ]; then
758 output="${output}1"
759 else
760 output="${output}0"
761 fi
762
763 print "${output}"
764}
765
1c6a4e30 766bin2dec() {
5cf0edf9
MT
767 local string="${1}"
768 local number=0
769
770 local pos=0 char
771 while [ ${pos} -lt ${#string} ]; do
772 char="${string:${pos}:1}"
773 pos=$(( ${pos} + 1 ))
774
775 number=$(( ${number} << 1 ))
776
777 case "${char}" in
778 0) ;;
779 1)
780 number=$(( ${number} + 1 ))
781 ;;
782 *)
783 assert false "Invalid character: ${char}"
784 ;;
785 esac
786 done
787
788 print "${number}"
789 return ${EXIT_OK}
790}
791
1c6a4e30 792char2bin() {
5cf0edf9
MT
793 local dec="$(ord "${1}")"
794
795 dec2bin "${dec}"
796}
797
1c6a4e30 798bin2char() {
5cf0edf9
MT
799 local dec="$(bin2dec "$@")"
800
801 chr "${dec}"
802}
803
1c6a4e30 804bin2hex() {
5cf0edf9
MT
805 local dec="$(bin2dec "$@")"
806
807 dec2hex "${dec}"
808}
809
1c6a4e30 810hex2bin() {
5cf0edf9
MT
811 local dec="$(hex2dec "$@")"
812
813 dec2bin "${dec}"
814}
815
1c6a4e30 816hex2dec() {
5cf0edf9
MT
817 local hex="${1}"
818
819 # Prepend 0x if necessary.
820 [ "${hex:0:2}" = "0x" ] || hex="0x${hex}"
821
822 printf "%d\n" "${hex}"
823}
824
1c6a4e30 825dec2hex() {
5cf0edf9
MT
826 printf "%02x\n" "${1}"
827}
a95d16fc 828
10b53c87 829# This function just copy config files
a95d16fc 830copy() {
a95d16fc
JS
831 assert [ $# -eq 2 ]
832
833 local src=${1}
834 local dst=${2}
835
287c2e60
MT
836 # Check if we can read from the source
837 if [ ! -r "${src}" ]; then
838 log ERROR "Cannot read ${src}"
839 return ${EXIT_ERROR}
840 fi
841
a95d16fc 842 # Check if ${dst} is a directory
1fade616 843 if [ -d "${dst}" ]; then
9c802e1d 844 log ERROR "${dst} is a directory"
a95d16fc
JS
845 return ${EXIT_ERROR}
846 fi
847
848 if ! fread "${src}" > "${dst}"; then
849 log ERROR "Could not copy data from ${src} to ${dst}"
850 return ${EXIT_ERROR}
851 fi
852}
a8d8d2c5
JS
853
854normalize() {
855 local string="$@"
856
857 tr -sc [:alnum:] "-" < <(printf "%s" "${string,,}")
858}