Bump Standards-Version to 3.9.4
[grml-shlib.git] / sh-lib
1 #!/bin/sh
2 # Filename:      sh-lib
3 # Purpose:       Shellscript library
4 # Authors:       grml-team (grml.org), (c) Michael Gebetsroither <gebi@grml.org>
5 # Bug-Reports:   see http://grml.org/bugs/
6 # License:       This file is licensed under the GPL v2.
7 ################################################################################
8
9 # VARIABLES {{{
10 VERBOSE__=0
11 VERBOSE_TMP__=0
12
13 # FIXME maybe PROG_PATH__ for better error reporting?
14 PROG_NAME__=""    # initialised within init section
15
16 # directory for init scripts
17 INITD_DIR__="/etc/init.d/"
18
19 # >= level and the function will print the message
20 EPRINT__=1    # eprint (error print)
21 EEPRINT__=2   # 2print (intern error print)
22 DPRINT__=3    # dprint (debug print)
23
24 EXIT_FUNCTION__="_syslog"    # function to call upon die (can be set by user)
25
26 SYSLOG__="YES"
27
28 CMD_LINE__=""   # /proc/cmdline
29
30 LANG__="$LANG"
31 LC_ALL__="$LC_ALL"
32 LANGUAGE__="$LANGUAGE"
33 # }}}
34
35 # CONFIG FUNCTIONS  {{{
36
37 setProgName() { PROG_NAME__="$1"; }
38
39 setExitFunction() { EXIT_FUNCTION__="$1"; }
40
41 disableSyslog() { SYSLOG__="NO";  }
42 enableSyslog() { SYSLOG__="YES"; }
43
44 saveLang() { LANG__="$LANG"; LC_ALL__="$LC_ALL"; LANGUAGE__="$LANGUAGE"; }
45 restoreLang() { LANG="$LANG__"; LC_ALL="$LC_ALL__"; LANGUAGE="$LANGUAGE__"; }
46 setCLang() { saveLang; LANG="C"; LC_ALL="C"; LANGUAGE="C"; }
47 # }}}
48
49 # DEBUG FRAMEWORK  {{{
50
51 setVerbose()     { VERBOSE__=${1:-1}; }
52 unsetVerbose()   { VERBOSE_TMP__=$VERBOSE__; VERBOSE__=0; }
53 restoreVerbose() { VERBOSE__=$VERBOSE_TMP__; }
54 getVerbose()     { echo "$VERBOSE__"; }
55
56 setDebug()       { setVerbose "$DPRINT__"; }
57 unsetDebug()     { restoreVerbose; }
58
59 setExitFunction()    { EXIT_FUNCTION__="$1"; }
60 resetExitFunction()  { EXIT_FUNCTION__="_syslog"; }
61 # }}}
62
63 # ERROR REPORTING FUNCTIONS  {{{
64
65 # default print backend (there may be other functions)
66 vprint()
67 {
68   local level_="$1"
69   local type_="$2"
70   local message_="$3"
71
72   if [ "$VERBOSE__" -ge "$level_" -a -n "$message_" ]; then
73     echo -n "$type_" >&2
74     echo "$message_" >&2
75   fi
76 }
77
78 # print error output
79 eprint()
80 {
81   # FIXME vprint should be a var, because we want to call user-defined functions
82   # global var (there should be a syslog, and vprint + syslog function)
83   vprint $EPRINT__ "Error - " "$1"
84 }
85
86 # should be used for intern silentExecutes
87 eeprint()
88 {
89   vprint $EEPRINT__ "  Error2 - " "$1"
90 }
91
92 # print debug output (function intern errors)
93 dprint()
94 {
95   vprint $DPRINT__ "Debug - " "$1"
96 }
97
98 # for program notice messages
99 notice()
100 {
101   vprint $EPRINT__ "Notice - " "$1"
102 }
103
104 die()
105 {
106   local error_message_="$1"   # print this error message
107   local exit_code_="$2"  # command exited with this exit code
108
109   echo -n "PANIC: $error_message_" >&2
110   if [ -n "$2" ]; then
111     echo "; ret($exit_code_)" >&2
112   else
113     echo >&2
114   fi
115
116   if [ -n "$EXIT_FUNCTION__" ]; then
117     $EXIT_FUNCTION__ "$error_message_" "$exit_code_" >&2
118   fi
119   kill $$
120 }
121
122 warn()
123 {
124   local error_message_="$1"   # print this error message
125   local exit_code_="$2"  # command exits with this exit code
126
127   echo -n "WARN: $error_message_" >&2
128   if [ -n "$exit_code_" ]; then
129     echo "; ret($exit_code_)" >&2
130   else
131     echo >&2
132   fi
133 }
134
135 _syslog()
136 {
137   local message_="$1"   # error message
138   local exit_code_="$2"
139
140   if [ "$SYSLOG__" = "YES" ]; then
141     if [ -n "$exit_code_" ]; then
142       logger -p user.alert -t "$PROG_NAME__" -i "$message_ ret($exit_code_)" >&2
143     else
144       logger -p user.alert -t "$PROG_NAME__" -i "$message_" >&2
145     fi
146   fi
147 }
148
149 syslog()
150 {
151   local message_="$1"   # error message
152   local exit_code_="$2"
153
154   if [ -n "$exit_code_" ]; then
155     logger -p user.alert -t "$PROG_NAME__" -i "$message_ ret($exit_code_)" >&2
156   else
157     logger -p user.alert -t "$PROG_NAME__" -i "$message_" >&2
158   fi
159 }
160
161 warnLog()
162 {
163   local error_message_="$1"   # print this error message
164   local exit_code_="$2"  # command exits with this exit code
165
166   warn "$error_message_" "$exit_code_"
167   syslog "$error_message_" "$exit_code_"
168 }
169 # }}}
170
171 # CORE FUNCTIONS {{{
172
173 ##
174 # ATTENTION... THIS FUNCTION IS A BIG SECURITY HOLE
175 # this function will be changed in future release
176 ##
177 # I don't want to write exit status control stuff every time
178 execute()
179 {
180   local to_exec_="$1"   # command to execute
181   local error_function_=${2:-"eprint"}    # function to call on error
182   local message_="$3"   # user supplied error message
183
184   local ret_=''
185
186   # NOT A GOOD IDEA
187   eval "$to_exec_"
188   ret_=$?
189
190   if [ $ret_ -eq 127 ]; then
191     syslog "problems executing ( $to_exec_ )" $ret_
192   fi
193   if [ $ret_ -ne 0 ]; then
194     if [ -z "$message_" ]; then
195       $error_function_ "problems executing ( $to_exec_ )" "$ret_"
196     else
197       $error_function_ "$message_" "$ret_"
198     fi
199   fi
200   dprint "exec-$error_function_: ( $to_exec_ ) ret($ret_)"
201   return $ret_
202 }
203
204 silentExecute()
205 {
206   unsetVerbose
207   execute "$@"
208   local ret_=$?
209   restoreVerbose
210   return $ret_
211 }
212
213
214 ###
215 #
216 # TEST FUNCTIONS
217 #
218 ###
219
220 # if the file DOES exist, everything is fine
221 isExistent()
222 {
223   local file_to_test_="$1"    # file to test
224   local error_function_=${2:-"eprint"}    # function to call on error
225   local message_="$3"    # user supplied error message
226
227   if [ ! -e "$file_to_test_" ]; then
228     if [ -z "$message_" ]; then
229       $error_function_ "file does not exist \"$file_to_test_\"" 66
230     else
231       $error_function_ "$message_"
232     fi
233     return 1
234   fi
235   dprint "isExistent(): file \"$1\" does exist => ready to go"
236   return 0
237 }
238
239 isNotExistent()
240 {
241   local file_to_test_="$1"    # file to test
242   local error_function_=${2:-"eprint"}    # function to call on error
243   local message_="$3"    # user supplied error message
244
245   if [ -e "$file_to_test_" ]; then
246     if [ -z "$message_" ]; then
247       $error_function_ "file does already exist \"$file_to_test_\"" 67
248     else
249       $error_function_ "$message_"
250     fi
251     return 1
252   fi
253   dprint "isNotExistent(): file \"$1\" does not exist => ready to go"
254   return 0
255 }
256
257
258 checkUser()
259 {
260   local to_check_="$1"    # username to check against running process
261   local error_function_=${2:-"eprint"}    # function to call on error
262   local message_="$3"    # user supplied error message
263
264   local user_=''
265
266   user_=`id -un`
267   if [ $user_ != "$to_check_" ]; then
268     if [ -z "$message_" ]; then
269       $error_function_ "username \"$user_\" is not \"$to_check_\"" 77 $exit_function_
270     else
271       $error_function_ "$message_"
272     fi
273     return 1
274   else
275     dprint "checkUser(): accepted, username matches \"$to_check_\""
276     return 0
277   fi
278 }
279
280 checkId()
281 {
282   local to_check_="$1"    # user-id to check against running process
283   local error_function_=${2:-"eprint"}    # function to call on error
284   local message_="$3"    # user supplied error message
285
286   local user_id_=''
287
288   user_id_=$(id -u)
289   if [ "$user_id_" != "$to_check_" ]; then
290     if [ -z "$message_" ]; then
291       $error_function_ "UID \"$user_id_\" is not \"$to_check_\"" 77
292     else
293       $error_function_ "$message_"
294     fi
295     return 1
296   else
297     dprint "checkId(): accepted, UID matches \"$to_check_\""
298     return 0
299   fi
300 }
301
302 checkRoot()
303 {
304   checkId 0 "$1" "$2"
305 }
306
307 isGrml()
308 {
309   if [ -f /etc/grml_version ] ; then
310     dprint "isGrml(): this seems to be a grml system"
311     return 0
312   else
313     dprint "isGrml(): this is not a grml system"
314     return 1
315   fi
316 }
317
318 runsFromHd()
319 {
320   if [ -e "/etc/grml_cd" ]; then
321     dprint "runsFromHd(): grml is on CD"
322     return 1
323   else
324     dprint "runsFromHd(): grml is on HD"
325     return 0
326   fi
327 }
328
329 runsFromCd()
330 {
331   if [ -e "/etc/grml_cd" ]; then
332     dprint "runsFromCd(): grml is on CD"
333     return 0
334   else
335     dprint "runsFromCd(): grml is on HD"
336     return 1
337   fi
338 }
339
340
341 # secure input from console
342 secureInput()
343 {
344   local to_secure_="$1"
345
346   local secured_=''
347
348   secured_=`echo -n "$to_secure_" |tr -c '[:alnum:]/.\-,\(\)' '_'`
349   dprint "secureInput(): \"$to_secure_\" => \"$secured_\""
350   echo "$secured_"
351 }
352
353
354 # convert all possible path formats to absolute paths
355 relToAbs()
356 {
357   local relpath_="$1"
358   local abspath_=''
359
360   abspath_="`readlink -f \"$relpath_\"`" || \
361     warn "relToAbs(): Problems getting absolute path" "$?" || return 1
362   dprint "relToAbs(): \"$relpath_\" => \"$abspath_\""
363   echo "$abspath_"
364 }
365
366
367 # Trim off white-space characters
368 # white-space in the "C" and "POSIX" locales are:
369 #   space
370 #   form-feed ('\f')
371 #   newline ('\n')
372 #   carriage return ('\r')
373 #   horizontal tab ('\t')
374 #   vertical tab ('\v')
375 stringTrim()
376 {
377   local str_="$1"
378   local result_=""
379
380   result_="`echo "$str_" | sed -e 's/^\s*//' -e 's/\s*$//'`" || \
381     warn "stringTrim(): Problems stripping of blanks" || return 1
382   dprint "stringTrim(): \"$str_\" => \"$result_\""
383   echo "$result_"
384 }
385
386 # Simple shell grep
387 stringInFile()
388 {
389   local to_test_="$1"   # matching pattern
390   local source_="$2"    # source-file to grep
391
392   if [ ! -e "$source_" ]; then
393     eprint "stringInFile(): \"$source_\" does not exist"
394     return 1
395   fi
396
397   case "$(cat $source_)" in *$to_test_*) return 0;; esac
398   return 1
399 }
400
401 # same for strings
402 stringInString()
403 {
404   local to_test_="$1"   # matching pattern
405   local source_="$2"    # string to search in
406
407   case "$source_" in *$to_test_*) return 0;; esac
408   return 1
409 }
410
411 # get value for bootparam given as first param
412 getBootParam()
413 {
414   local param_to_search_="$1"
415   local result_=''
416
417   stringInString " $param_to_search_=" "$CMD_LINE__" || return 1
418   result_="${CMD_LINE__##*$param_to_search_=}"
419   result_="${result_%%[   ]*}"
420   echo "$result_"
421   return 0
422 }
423
424 # Check boot commandline for specified option
425 checkBootParam()
426 {
427   stringInString " $1" "$CMD_LINE__"
428   return "$?"
429 }
430 # }}}
431
432 # NETWORK  {{{
433
434 # validates an IP FIXME
435 netValidIp()
436 {
437   local ip_="$1"    # ip address to validate
438   local error_function_=${2:-"eprint"}    # function to call on error
439   local message_="$3"    # user supplied error message
440
441   local ret_=''
442
443   echo "$ip_" | grep -E -q -e '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}' \
444     1>/dev/null 2>&1
445   ret_=$?
446   if [ $ret_ -ne 0 ]; then
447     if [ -z "$message_" ]; then
448       "$error_function_" "ip-address \"$ip_\" is NOT valid" $ret_
449     else
450       "$error_function_" "$message_" $ret_
451     fi
452     return 1
453   fi
454
455   dprint "ip-address \"$ip_\" is valid" $ret_
456   return $ret_
457 }
458
459 netGetIfaces()
460 {
461   local error_function_=${1:-"eprint"}    # function to call on error
462   local message_="$2"    # user supplied error message
463   local if_=''
464   local ret_=''
465
466   #ip a|grep 'inet ' |awk '$NF !~ /lo/{print $NF}'
467   if_="`ip a|grep 'inet ' |awk '{print $NF}'`"
468   ret_=$?
469   if [ -z "$if_" ]; then
470     if [ -z "$message_" ]; then
471       "$error_function_" "no interfaces found" $ret_
472     else
473       "$error_function_" "$message_" $ret_
474     fi
475     return 1
476   fi
477   dprint "interfaces found" $ret_
478   echo "$if_"
479 }
480
481 # FIXME
482 netGetDefaultGateway()
483 {
484   local error_function_=${1:-"eprint"}    # function to call on error
485   local message_="$2"    # user supplied error message
486
487   local ip_=''
488   local ret_=''
489
490   setCLang
491   ip_=`route -n | awk '/^0\.0\.0\.0/{print $2; exit}'`
492   ret_=$?
493   restoreLang
494   if [ -z "$ip_" ]; then
495     if [ -z "$message_" ]; then
496       "$error_function_" "no default gateway found" $ret_
497     else
498       "$error_function_" "$message_" $ret_
499     fi
500     return 1
501   fi
502   dprint "default gateway is \"$ip_\"" $ret_
503   echo "$ip_"
504   return 0
505 }
506
507 # FIXME
508 netGetNetmask()
509 {
510   local iface_="$1"
511   local error_function_=${2:-"eprint"}    # function to call on error
512   local message_="$3"    # user supplied error message
513
514   local nm_=''
515   local ret_=''
516
517   setCLang
518   nm_=`ifconfig "$iface_" | awk '/[Mm]ask/{FS="[:   ]*"; $0=$0; print $8; exit}'`
519   ret_=$?
520   restoreLang
521   if [ -z "$nm_" ]; then
522     if [ -z "$message_" ]; then
523       "$error_function_" "could not find a netmask for \"$iface_\"" $ret_
524     else
525       "$error_function_" "$message_" $ret_
526     fi
527     return 1
528   fi
529   dprint "netmask on \"$iface_\" is \"$nm_\"" $ret_
530   echo "$nm_"
531   return 0
532 }
533
534 # FIXME
535 netGetIp()
536 {
537   local iface_="$1"
538   local error_function_=${2:-"eprint"}    # function to call on error
539   local message_="$3"    # user supplied error message
540
541   local ip_=""
542   local ret_=""
543
544   setCLang
545   #ip_=`ip addr list eth0 |mawk '/inet/{split($2,A,"/"); print A[1]}'`
546   ip_=`ifconfig "$iface_" | awk '/[Ii]net [Aa]ddr/{FS="[:  ]*"; $0=$0; print $4; exit}'`
547   ret_=$?
548   restoreLang
549   if [ -z "$ip_" ]; then
550     if [ -z "$message_" ]; then
551       "$error_function_" "no ip for \"$iface_\" found" $ret_
552     else
553       "$error_function_" "$message_" $ret_
554     fi
555     return 1
556   fi
557   dprint "address for \"$iface_\" is \"$ip_\"" $ret_
558   echo "$ip_"
559   return 0
560 }
561
562 netGetNameservers()
563 {
564   local error_function_=${1:-"eprint"}    # function to call on error
565   local message_="$2"    # user supplied error message
566
567   local file_="/etc/resolv.conf"
568   local ns_=""
569
570   if [ ! -e $file_ ]; then
571     warn "file \"$file_\" does not exist, could not get nameservers"
572     return 1
573   fi
574
575   setCLang
576   ns_=`awk '/^nameserver/{printf "%s ",$2}' $file_ |xargs echo`
577   restoreLang
578   if [ -z "$ns_" ]; then
579     if [ -z "$message_" ]; then
580       "$error_function_" "no nameservers found" $ret_
581     else
582       "$error_function_" "$message_" $ret_
583     fi
584     return 1
585   fi
586   dprint "nameservers: \"$ns_\"" $ret_
587   echo "$ns_"
588   return 0
589 }
590
591 # }}}
592
593 # SERVICES {{{
594 _touchService()
595 {
596   local action_="${1:-"start"}"
597   local service_="$2"
598   local error_function_=${3:-"eprint"}    # function to call on error
599   local message_="$4"     # user supplied error message
600
601   local i=""
602   local known_action_='false'
603   for i in "start" "stop" "restart" "reload" "force-reload"; do
604     if [ "$i" = "$action_" ]; then
605       known_action_='true'
606       break
607     fi
608   done
609   $known_action_ || warn "_touchService(): unknown action \"$action_\""
610
611
612   local service_path_=""
613   service_path_="${INITD_DIR__}/$service_"
614   if [ ! -e "$service_path_" ]; then
615     warn "_touchService(): service does not exist: \"$service_\""
616     return 1
617   fi
618   if [ ! -x "$service_path_" ]; then
619     warn "_touchService(): service is not executable: \"$service_\""
620   fi
621
622   local ret_=""
623   "$service_path_" "$action_"
624   ret_=$?
625   if [ "$ret_" != "0" ]; then
626     if [ -z "$message_" ]; then
627       "$error_function_" "Problems ${action_}ing service \"$service_\"" $ret_
628     else
629       "$error_function_" "$message_" $ret_
630     fi
631     return 1
632   fi
633   dprint "_touchService(): successfully started service \"$service_\""
634   return 0
635 }
636
637 _createServiceFunctions()
638 {
639   for i in "start" "stop" "restart" "reload"; do
640     eval "${i}Service() { _touchService ${i} \"\$1\" \"\$2\" \"\$3\"; }"
641   done
642   eval "forceReloadService() { _touchService force-reload \"\$1\" \"\$2\" \"\$3\"; }"
643 }
644 _createServiceFunctions
645 # }}}
646
647 # LOSETUP HELPER FUNCTION {{{
648 # print next free /dev/loop* to stdout
649 findNextFreeLoop()
650 {
651   local error_function_=${1:-"eprint"}    # function to call on error
652   local message_="$2"    # user supplied error message
653
654   local tmp_=''   # tmp
655   local i=''      # counter
656   local ret_=''   # saved return value
657
658   for i in 'losetup' 'losetup.orig'; do
659     tmp_=`$i -f 2>/dev/null`
660     if [ $? -eq 0 ]; then
661       echo $tmp_
662       return 0
663     fi
664   done
665
666   # we have to search
667   dprint 'findNextFreeLoop(): losetup does not recognice option -f, searching next free loop device'
668   for i in `seq 0 100`; do
669     test -e /dev/loop$i || continue
670     losetup /dev/loop$i >/dev/null 2>&1
671     ret_=$?
672     case "$ret_" in
673       2) continue ;;  # losetup could not get status of loopdevice (EPERM)
674       0) continue ;;  # device exist
675       1) echo "/dev/loop$i"; return 0 ;;  # device does not exist and no error
676       ?) continue ;;  # return value not available in 'man losetup'
677     esac
678   done
679
680   # hmm... could not find a loopdevice
681   if [ -z "$message_" ]; then
682     $error_function_ "could not find a free loop device"
683   else
684     $error_function_ "$message_"
685   fi
686   return 1
687 }
688 # }}}
689
690 # INIT {{{
691
692 _initProgName()
693 {
694   local name_="$1"    # program name
695
696   local tmp_name_=`basename "$name_"` || \
697     logger -p user.alert -i "Init-initProgName: problems executing ( basename \"$name_\" ) ret($?)" >/dev/null
698
699   secureInput "$tmp_name_"
700 }
701 PROG_NAME__=`_initProgName "$0"`
702
703
704 _checkExecutables()
705 {
706   local tmp_=""
707   for i in tr dirname basename id logger kill cat grep route awk ifconfig; do
708     which $i >/dev/null 2>&1 || tmp_="${tmp_}$i "
709   done
710   if [ -n "$tmp_" ]; then
711     eprint "Init-checkExecutables: following executables not found or not executable:\n$tmp_"
712     #syslog "Init-checkExecutables: following executables not found or not executable: $tmp_"
713   fi
714 }
715 _checkExecutables
716
717
718 _checkBootParam()
719 {
720   local path_="/proc/cmdline"
721   if [ -e "$path_" ]; then
722     CMD_LINE__=`execute "cat $path_" warnLog`
723     return 0
724   fi
725   warnLog "$path_ does not exist, thus sh-lib may not work reliable!"
726   return 1
727 }
728 _checkBootParam
729
730 _setDebugLevel()
731 {
732   # accept only integer as arguments
733   if echo "$DEBUG" | grep -E -q '^[0-9]+$' ; then
734     local debug_="${DEBUG:-0}"
735   else
736     local debug_="0"
737   fi
738   VERBOSE__="$debug_"
739 }
740 _setDebugLevel
741 # }}}
742
743 # END OF FILE
744 ################################################################################
745 # vim:foldmethod=marker expandtab shiftwidth=2 tabstop=2