]> git.ipfire.org Git - people/ms/network.git/blob - src/functions/functions.util
Drop deprecated listmatch function
[people/ms/network.git] / src / functions / functions.util
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
22 # A simple print statement
23 print() {
24 local fmt=${1}; shift
25
26 printf -- "${fmt}\n" "$@"
27 }
28
29 # The args() function takes a number of arguments like
30 # var1="abc d" var2="abc" var3="abcd e"
31 # and splits them into several arguments, devided by newline
32 args() {
33 echo "$@" | xargs printf "%s\n"
34 }
35
36 unquote() {
37 local var="$@"
38
39 if [ "${var:0:1}" = "\"" ]; then
40 var=${var:1}
41 fi
42
43 local last=$(( ${#var} - 1 ))
44 if [ ${last} -ge 0 ] && [ "${var:${last}:1}" = "\"" ]; then
45 var=${var:0:${last}}
46 fi
47
48 print "${var}"
49 }
50
51 quote() {
52 print "\"%s\"" "$@"
53 }
54
55 strip() {
56 local value="$@"
57
58 # remove leading whitespace characters
59 value="${value#"${value%%[![:space:]]*}"}"
60
61 # remove trailing whitespace characters
62 value="${value%"${value##*[![:space:]]}"}"
63
64 print "${value}"
65 }
66
67 # Print a pretty error message
68 error() {
69 echo -e " ${CLR_RED_B}ERROR${CLR_RESET} : $@" >&2
70 }
71
72 error_log() {
73 log ERROR "$@"
74 }
75
76 # Print a pretty warn message
77 warning() {
78 echo -e " ${CLR_YELLOW_B}WARNING${CLR_RESET}: $@" >&2
79 }
80
81 warning_log() {
82 log WARNING "$@"
83 }
84
85 # Deprecated
86 listlength() {
87 list_length $@
88 }
89
90 # Speedup function to avoid a call of the basename binary
91 basename() {
92 echo "${1##*/}"
93 }
94
95 format() {
96 local key=${1}
97 assert isset key
98
99 local format=${2}
100 assert isset format
101
102 shift 2
103
104 printf -v "${key}" "${format}" "$@"
105 }
106
107 format_time() {
108 local s=${1}
109 local ret m
110
111 local units="s m h"
112
113 local unit
114 for unit in ${units}; do
115 m=$(( ${s} % 60 ))
116 s=$(( ${s} / 60 ))
117
118 if [ ${m} -gt 0 ]; then
119 ret="${m}${unit} ${ret}"
120 fi
121 done
122
123 # Remove whitespace
124 echo ${ret}
125 }
126
127 parse_time() {
128 local ret=0
129
130 local arg
131 for arg in $@; do
132 local unit
133
134 case "${arg}" in
135 *h|*m|*s)
136 # Store unit
137 unit="${arg: -1}"
138
139 # Remove unit
140 arg="${arg:0:-1}"
141 ;;
142 esac
143
144 if ! isinteger arg; then
145 return ${EXIT_ERROR}
146 fi
147
148 # Convert hours and minutes into seconds
149 case "${unit}" in
150 h)
151 arg=$(( ${arg} * 3600 ))
152 ;;
153 m)
154 arg=$(( ${arg} * 60 ))
155 ;;
156 esac
157
158 # Add up everything
159 ret=$(( ${ret} + ${arg} ))
160 done
161
162 print "${ret}"
163 }
164
165 assign() {
166 local key=${1}
167 assert isset key
168 shift
169
170 format "${key}" "%s" "$@"
171 }
172
173 fread() {
174 local file=${1}
175 assert isset file
176
177 [ -r "${file}" ] || return ${EXIT_ERROR}
178
179 print "$(<${file})"
180 }
181
182 fwrite() {
183 local file=${1}
184 assert isset file
185 shift
186
187 if [ ! -w "${file}" ]; then
188 log ERROR "${file}: No such file"
189 return ${EXIT_ERROR}
190 fi
191
192 print "%s" "$@" >> ${file} 2>/dev/null
193 }
194
195 make_parent_dir() {
196 local path="${1}"
197
198 local dirname="$(dirname "${path}")"
199 mkdir -p "${dirname}"
200 }
201
202 enabled() {
203 local param=${1}
204
205 list_match "${!param}" yes on true 1
206 }
207
208 mac_generate() {
209 local b="$(random 12)"
210
211 # Remove multicast bit
212 # and set address is software assigned
213 local first_byte=$(( 0x${b:0:2} & 0xfe ))
214 first_byte=$(( ${first_byte} | 0x02 ))
215
216 local output
217 printf -v output "%02x" "${first_byte}"
218
219 output="${output}:${b:2:2}:${b:4:2}:${b:6:2}:${b:8:2}:${b:10:2}"
220
221 # Check if output is valid
222 assert mac_is_valid "${output}"
223
224 echo "${output}"
225 }
226
227 mac_format() {
228 local mac=${1}
229 assert isset mac
230
231 # Remove all colons and make the rest lowercase.
232 mac=${mac//:/}
233 mac=${mac,,}
234
235 local output
236 if [ "${#mac}" = "12" ]; then
237 # Add colons (:) to mac address
238 output=${mac:0:2}
239 local i
240 for i in 2 4 6 8 10; do
241 output="${output}:${mac:${i}:2}"
242 done
243 else
244 output=${mac}
245 fi
246
247 assert mac_is_valid ${output}
248
249 print "${output}"
250 }
251
252 mac_is_valid() {
253 local mac=${1}
254
255 [[ ${mac} =~ ^([0-9a-f]{2}\:){5}[0-9a-f]{2}$ ]]
256 }
257
258 uuid() {
259 echo $(</proc/sys/kernel/random/uuid)
260 }
261
262 abs() {
263 local val=${1}
264
265 if [ ${val} -lt 0 ]; then
266 (( val *= -1 ))
267 fi
268
269 echo ${val}
270 }
271
272 rand() {
273 local uuid="$(uuid)"
274 echo "${uuid//-/}"
275 }
276
277 random() {
278 local length="${1:-8}"
279
280 local random
281 while [ ${#random} -lt ${length} ]; do
282 random="${random}$(rand)"
283 done
284
285 echo "${random:0:${length}}"
286 }
287
288 isset() {
289 local var=${1}
290
291 [ -n "${!var}" ]
292 }
293
294 isoneof() {
295 local var=${!1}
296 shift
297
298 list_match "${var}" "$@"
299 }
300
301 isbool() {
302 local var=${1}
303
304 isoneof ${var} 0 1 no yes on off true false
305 }
306
307 isinteger() {
308 local var=${!1}
309
310 [[ ${var} =~ ^[0-9]+$ ]]
311 }
312
313 ismac() {
314 local mac=${!1}
315
316 mac_is_valid ${mac}
317 }
318
319 isipaddress() {
320 local addr=${!1}
321
322 ip_is_valid ${addr}
323 }
324
325 mtu_is_valid() {
326 local proto=${1}
327 local mtu=${2}
328
329 case ${proto} in
330 ipv4)
331 [ ${mtu} -ge 576 ] && [ ${mtu} -le 9000 ]
332 ;;
333 ipv6)
334 [ ${mtu} -ge 1280 ] && [ ${mtu} -le 9000 ]
335 ;;
336 *)
337 error "${proto} is not a valid proto"
338 return ${EXIT_ERROR}
339 ;;
340 esac
341 }
342
343 backtrace() {
344 local start=1
345
346 echo # Empty line
347 error_log "Backtrace (most recent call in first line):"
348
349 local i source
350 for i in $(seq ${start} ${#BASH_SOURCE[*]}); do
351 [ -z "${FUNCNAME[${i}]}" ] && continue
352
353 # Print called binary with arguments.
354 if [ "${FUNCNAME[${i}]}" == "main" ]; then
355 local args="$(list_reverse ${BASH_ARGV[*]})"
356 printf -v source "%20s" "$0"
357 error_log " ${source} ${args}"
358 continue
359 fi
360
361 source=${BASH_SOURCE[$(( ${i} + 1 ))]}
362 error_log " $(printf "%20s" "'${FUNCNAME[${i}]}'") called from ${source:-<shell>}:${BASH_LINENO[${i}]}"
363 done
364 }
365
366 assert() {
367 local assertion="$@"
368
369 if ! ${assertion}; then
370 error_log "Assertion '${assertion}' failed."
371 backtrace
372 exit ${EXIT_ERROR_ASSERT}
373 fi
374
375 return ${EXIT_OK}
376 }
377
378 # This function checks, if the given argument is an assert error
379 # exit code. If this is the case, the script will halt immediately.
380 assert_check_retval() {
381 local ret=${1}
382
383 if [ ${ret} -eq ${EXIT_ERROR_ASSERT} ]; then
384 exit ${EXIT_ERROR_ASSERT}
385 fi
386
387 return ${ret}
388 }
389
390 # This function executes the given command and inverses the return code
391 not() {
392 local command="$@"
393
394 ${command} && return ${EXIT_FALSE} || return ${EXIT_TRUE}
395 }
396
397 exec_cmd() {
398 local cmd=$@
399
400 log DEBUG "Running command: ${cmd}"
401
402 DEBUG=${DEBUG} \
403 LOG_DISABLE_STDOUT="${LOG_DISABLE_STDOUT}" \
404 LOG_FACILITY="${LOG_FACILITY}" \
405 ${SHELL} ${cmd}
406 local ret=$?
407
408 #log DEBUG "Returned with code '${ret}'"
409
410 if [ ${ret} -eq ${EXIT_ERROR_ASSERT} ]; then
411 error_log "Stopping parent process due to assertion error in child process: ${cmd}"
412 exit ${EXIT_ERROR_ASSERT}
413 fi
414
415 return ${ret}
416 }
417
418 cmd() {
419 local cmd=$@
420
421 log DEBUG "Running command: ${cmd}"
422
423 ${cmd}
424 local ret=$?
425
426 log DEBUG "Returned with code '${ret}'"
427
428 return ${ret}
429 }
430
431 cmd_quiet() {
432 cmd $@ &>/dev/null
433 }
434
435 cmd_exec() {
436 local cmd=$@
437
438 log DEBUG "Exec'ing command: ${cmd}"
439
440 exec ${cmd}
441
442 log ERROR "Could not exec-ute: ${cmd}"
443 exit ${EXIT_ERROR}
444 }
445
446 cmd_not_implemented() {
447 assert false "not implemented"
448 }
449
450 # Runs a command in a clean environment so that no confidential information
451 # is leaked to any untrusted commands.
452 cmd_clean_environment() {
453 local cmd=$@
454
455 log DEBUG "Running command in a clean environment: ${cmd}"
456 env -i -- ${cmd}
457 local ret=${?}
458
459 log DEBUG "Returned with code '${ret}'"
460 return ${ret}
461 }
462
463 # Executes the given command in background
464 cmd_background() {
465 cmd_quiet $@ &
466 }
467
468 # Prints the PID of the process that was started last
469 cmd_background_get_pid() {
470 print "${!}"
471 }
472
473 cmd_background_result() {
474 local pids=$@
475
476 wait ${pids}
477 }
478
479 # Increase security of the read command
480 read() {
481 builtin read -r $@
482 }
483
484 seq() {
485 if [ $# -eq 2 ]; then
486 eval echo {${1}..${2}}
487 elif [ $# -eq 3 ]; then
488 eval echo {${1}..${3}..${2}}
489 fi
490 }
491
492 range() {
493 eval echo {0..$(( ${1} - 1 ))}
494 }
495
496 count() {
497 local i=0
498
499 while read; do
500 ((i++))
501 done
502
503 echo ${i}
504 }
505
506 which() {
507 type -P $@
508 }
509
510 # Prints the number of seconds since epoch.
511 timestamp() {
512 date -u "+%s"
513 }
514
515 beautify_time() {
516 local value=${1}
517
518 local unit
519 local limit
520 for unit in s m h d w; do
521 case "${unit}" in
522 s|m|h)
523 limit=60
524 ;;
525 d)
526 limit=24
527 ;;
528 w)
529 limit=7
530 ;;
531 esac
532
533 [ ${value} -lt ${limit} ] && break
534
535 value=$(( ${value} / ${limit} ))
536 done
537
538 echo "${value}${unit}"
539 }
540
541 beautify_bytes() {
542 local value=${1}
543
544 local unit
545 local limit=1024
546 for unit in B k M G T; do
547 [ ${value} -lt ${limit} ] && break
548 value=$(( ${value} / ${limit} ))
549 done
550
551 echo "${value}${unit}"
552 }
553
554 module_load() {
555 local module=${1}
556
557 if ! grep -q "^${module}" /proc/modules; then
558 log DEBUG "Loading module '${module}'."
559 modprobe ${module}
560 fi
561 }
562
563 binary_exists() {
564 local binary=${1}
565
566 if [ -n "$(type -p ${binary})" ]; then
567 return ${EXIT_OK}
568 fi
569
570 return ${EXIT_ERROR}
571 }
572
573 function_exists() {
574 local function="${1}"
575
576 if [ "$(type -t "${function}")" = "function" ]; then
577 return ${EXIT_TRUE}
578 fi
579
580 return ${EXIT_FALSE}
581 }
582
583 process_kill() {
584 local process=${1}
585
586 if ! isinteger process; then
587 process=$(pidof ${process})
588 fi
589
590 local pid
591 local sig
592 for pid in ${process}; do
593 for sig in 15 9; do
594 [ -d "/proc/${pid}" ] || break
595
596 kill -${sig} ${pid}
597 sleep 1
598 done
599 done
600 }
601
602 dec() {
603 local hex=${1}
604
605 if [ "${hex:0:2}" != "0x" ]; then
606 hex="0x${hex}"
607 fi
608
609 printf "%d\n" "${hex}"
610 }
611
612 chr() {
613 local char="${1}"
614
615 [ ${char} -lt 256 ] || return ${EXIT_ERROR}
616
617 printf "\\$(( ${char} / 64 * 100 + ${char} % 64 / 8 * 10 + ${char} % 8 ))\n"
618 }
619
620 ord() {
621 LC_CTYPE="C" printf "%d\n" "'${1}"
622 }
623
624 hex() {
625 printf "%X\n" "${1}"
626 }
627
628 network_is_running() {
629 # Check, if the network service is running.
630 service_is_active network
631 }
632
633 contains_spaces() {
634 local var="$@"
635
636 # Eliminate spaces.
637 local var2=${var// /}
638
639 if [ ${#var} -ne ${#var2} ]; then
640 return ${EXIT_TRUE}
641 fi
642
643 return ${EXIT_FALSE}
644 }
645
646 string_split() {
647 local string="$@"
648
649 local pos=0
650 while [ ${pos} -lt ${#string} ]; do
651 print "${string:${pos}:1}"
652 pos=$(( ${pos} + 1 ))
653 done
654
655 return ${EXIT_OK}
656 }
657
658 string_reverse() {
659 local string="$@"
660
661 local output
662 local pos=0
663 while [ ${pos} -lt ${#string} ]; do
664 output="${string:${pos}:1}${output}"
665 pos=$(( ${pos} + 1 ))
666 done
667
668 print "${output}"
669 return ${EXIT_OK}
670 }
671
672 dec2bin() {
673 local number="${1}"
674
675 local output
676
677 local i div
678 for i in 7 6 5 4 3 2 1; do
679 div=$(( 2 ** ${i} ))
680
681 if [ $(( ${number} / ${div} )) -eq 1 ]; then
682 output="${output}1"
683 else
684 output="${output}0"
685 fi
686 number="$(( ${number} % ${div} ))"
687 done
688
689 if [ $(( ${number} % 2 )) -eq 1 ]; then
690 output="${output}1"
691 else
692 output="${output}0"
693 fi
694
695 print "${output}"
696 }
697
698 bin2dec() {
699 local string="${1}"
700 local number=0
701
702 local pos=0 char
703 while [ ${pos} -lt ${#string} ]; do
704 char="${string:${pos}:1}"
705 pos=$(( ${pos} + 1 ))
706
707 number=$(( ${number} << 1 ))
708
709 case "${char}" in
710 0) ;;
711 1)
712 number=$(( ${number} + 1 ))
713 ;;
714 *)
715 assert false "Invalid character: ${char}"
716 ;;
717 esac
718 done
719
720 print "${number}"
721 return ${EXIT_OK}
722 }
723
724 char2bin() {
725 local dec="$(ord "${1}")"
726
727 dec2bin "${dec}"
728 }
729
730 bin2char() {
731 local dec="$(bin2dec "$@")"
732
733 chr "${dec}"
734 }
735
736 bin2hex() {
737 local dec="$(bin2dec "$@")"
738
739 dec2hex "${dec}"
740 }
741
742 hex2bin() {
743 local dec="$(hex2dec "$@")"
744
745 dec2bin "${dec}"
746 }
747
748 hex2dec() {
749 local hex="${1}"
750
751 # Prepend 0x if necessary.
752 [ "${hex:0:2}" = "0x" ] || hex="0x${hex}"
753
754 printf "%d\n" "${hex}"
755 }
756
757 dec2hex() {
758 printf "%02x\n" "${1}"
759 }