Use grml-core for boot params
[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 . /usr/lib/grml/core.sh
10
11 # VARIABLES {{{
12 VERBOSE__=0
13 VERBOSE_TMP__=0
14
15 # FIXME maybe PROG_PATH__ for better error reporting?
16 PROG_NAME__=""    # initialised within init section
17
18 # directory for init scripts
19 INITD_DIR__="/etc/init.d/"
20
21 # >= level and the function will print the message
22 EPRINT__=1    # eprint (error print)
23 EEPRINT__=2   # 2print (intern error print)
24 DPRINT__=3    # dprint (debug print)
25
26 EXIT_FUNCTION__="_syslog"    # function to call upon die (can be set by user)
27
28 SYSLOG__="YES"
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   getbootparam $1
415   return "$?"
416 }
417
418 # Check boot commandline for specified option
419 checkBootParam()
420 {
421   checkbootparam $1
422   return "$?"
423 }
424 # }}}
425
426 # NETWORK  {{{
427
428 # validates an IP FIXME
429 netValidIp()
430 {
431   local ip_="$1"    # ip address to validate
432   local error_function_=${2:-"eprint"}    # function to call on error
433   local message_="$3"    # user supplied error message
434
435   local ret_=''
436
437   echo "$ip_" | grep -E -q -e '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}' \
438     1>/dev/null 2>&1
439   ret_=$?
440   if [ $ret_ -ne 0 ]; then
441     if [ -z "$message_" ]; then
442       "$error_function_" "ip-address \"$ip_\" is NOT valid" $ret_
443     else
444       "$error_function_" "$message_" $ret_
445     fi
446     return 1
447   fi
448
449   dprint "ip-address \"$ip_\" is valid" $ret_
450   return $ret_
451 }
452
453 netGetIfaces()
454 {
455   local error_function_=${1:-"eprint"}    # function to call on error
456   local message_="$2"    # user supplied error message
457   local if_=''
458   local ret_=''
459
460   #ip a|grep 'inet ' |awk '$NF !~ /lo/{print $NF}'
461   if_="`ip a|grep 'inet ' |awk '{print $NF}'`"
462   ret_=$?
463   if [ -z "$if_" ]; then
464     if [ -z "$message_" ]; then
465       "$error_function_" "no interfaces found" $ret_
466     else
467       "$error_function_" "$message_" $ret_
468     fi
469     return 1
470   fi
471   dprint "interfaces found" $ret_
472   echo "$if_"
473 }
474
475 # FIXME
476 netGetDefaultGateway()
477 {
478   local error_function_=${1:-"eprint"}    # function to call on error
479   local message_="$2"    # user supplied error message
480
481   local ip_=''
482   local ret_=''
483
484   setCLang
485   ip_=`route -n | awk '/^0\.0\.0\.0/{print $2; exit}'`
486   ret_=$?
487   restoreLang
488   if [ -z "$ip_" ]; then
489     if [ -z "$message_" ]; then
490       "$error_function_" "no default gateway found" $ret_
491     else
492       "$error_function_" "$message_" $ret_
493     fi
494     return 1
495   fi
496   dprint "default gateway is \"$ip_\"" $ret_
497   echo "$ip_"
498   return 0
499 }
500
501 # FIXME
502 netGetNetmask()
503 {
504   local iface_="$1"
505   local error_function_=${2:-"eprint"}    # function to call on error
506   local message_="$3"    # user supplied error message
507
508   local nm_=''
509   local ret_=''
510
511   setCLang
512   nm_=`ifconfig "$iface_" | awk '/[Mm]ask/{FS="[:   ]*"; $0=$0; print $8; exit}'`
513   ret_=$?
514   restoreLang
515   if [ -z "$nm_" ]; then
516     if [ -z "$message_" ]; then
517       "$error_function_" "could not find a netmask for \"$iface_\"" $ret_
518     else
519       "$error_function_" "$message_" $ret_
520     fi
521     return 1
522   fi
523   dprint "netmask on \"$iface_\" is \"$nm_\"" $ret_
524   echo "$nm_"
525   return 0
526 }
527
528 # FIXME
529 netGetIp()
530 {
531   local iface_="$1"
532   local error_function_=${2:-"eprint"}    # function to call on error
533   local message_="$3"    # user supplied error message
534
535   local ip_=""
536   local ret_=""
537
538   setCLang
539   #ip_=`ip addr list eth0 |mawk '/inet/{split($2,A,"/"); print A[1]}'`
540   ip_=`ifconfig "$iface_" | awk '/[Ii]net [Aa]ddr/{FS="[:  ]*"; $0=$0; print $4; exit}'`
541   ret_=$?
542   restoreLang
543   if [ -z "$ip_" ]; then
544     if [ -z "$message_" ]; then
545       "$error_function_" "no ip for \"$iface_\" found" $ret_
546     else
547       "$error_function_" "$message_" $ret_
548     fi
549     return 1
550   fi
551   dprint "address for \"$iface_\" is \"$ip_\"" $ret_
552   echo "$ip_"
553   return 0
554 }
555
556 netGetNameservers()
557 {
558   local error_function_=${1:-"eprint"}    # function to call on error
559   local message_="$2"    # user supplied error message
560
561   local file_="/etc/resolv.conf"
562   local ns_=""
563
564   if [ ! -e $file_ ]; then
565     warn "file \"$file_\" does not exist, could not get nameservers"
566     return 1
567   fi
568
569   setCLang
570   ns_=`awk '/^nameserver/{printf "%s ",$2}' $file_ |xargs echo`
571   restoreLang
572   if [ -z "$ns_" ]; then
573     if [ -z "$message_" ]; then
574       "$error_function_" "no nameservers found" $ret_
575     else
576       "$error_function_" "$message_" $ret_
577     fi
578     return 1
579   fi
580   dprint "nameservers: \"$ns_\"" $ret_
581   echo "$ns_"
582   return 0
583 }
584
585 # }}}
586
587 # SERVICES {{{
588 _touchService()
589 {
590   local action_="${1:-"start"}"
591   local service_="$2"
592   local error_function_=${3:-"eprint"}    # function to call on error
593   local message_="$4"     # user supplied error message
594
595   local i=""
596   local known_action_='false'
597   for i in "start" "stop" "restart" "reload" "force-reload"; do
598     if [ "$i" = "$action_" ]; then
599       known_action_='true'
600       break
601     fi
602   done
603   $known_action_ || warn "_touchService(): unknown action \"$action_\""
604
605
606   local service_path_=""
607   service_path_="${INITD_DIR__}/$service_"
608   if [ ! -e "$service_path_" ]; then
609     warn "_touchService(): service does not exist: \"$service_\""
610     return 1
611   fi
612   if [ ! -x "$service_path_" ]; then
613     warn "_touchService(): service is not executable: \"$service_\""
614   fi
615
616   local ret_=""
617   "$service_path_" "$action_"
618   ret_=$?
619   if [ "$ret_" != "0" ]; then
620     if [ -z "$message_" ]; then
621       "$error_function_" "Problems ${action_}ing service \"$service_\"" $ret_
622     else
623       "$error_function_" "$message_" $ret_
624     fi
625     return 1
626   fi
627   dprint "_touchService(): successfully started service \"$service_\""
628   return 0
629 }
630
631 _createServiceFunctions()
632 {
633   for i in "start" "stop" "restart" "reload"; do
634     eval "${i}Service() { _touchService ${i} \"\$1\" \"\$2\" \"\$3\"; }"
635   done
636   eval "forceReloadService() { _touchService force-reload \"\$1\" \"\$2\" \"\$3\"; }"
637 }
638 _createServiceFunctions
639 # }}}
640
641 # LOSETUP HELPER FUNCTION {{{
642 # print next free /dev/loop* to stdout
643 findNextFreeLoop()
644 {
645   local error_function_=${1:-"eprint"}    # function to call on error
646   local message_="$2"    # user supplied error message
647
648   local tmp_=''   # tmp
649   local i=''      # counter
650   local ret_=''   # saved return value
651
652   for i in 'losetup' 'losetup.orig'; do
653     tmp_=`$i -f 2>/dev/null`
654     if [ $? -eq 0 ]; then
655       echo $tmp_
656       return 0
657     fi
658   done
659
660   # we have to search
661   dprint 'findNextFreeLoop(): losetup does not recognice option -f, searching next free loop device'
662   for i in `seq 0 100`; do
663     test -e /dev/loop$i || continue
664     losetup /dev/loop$i >/dev/null 2>&1
665     ret_=$?
666     case "$ret_" in
667       2) continue ;;  # losetup could not get status of loopdevice (EPERM)
668       0) continue ;;  # device exist
669       1) echo "/dev/loop$i"; return 0 ;;  # device does not exist and no error
670       ?) continue ;;  # return value not available in 'man losetup'
671     esac
672   done
673
674   # hmm... could not find a loopdevice
675   if [ -z "$message_" ]; then
676     $error_function_ "could not find a free loop device"
677   else
678     $error_function_ "$message_"
679   fi
680   return 1
681 }
682 # }}}
683
684 # INIT {{{
685
686 _initProgName()
687 {
688   local name_="$1"    # program name
689
690   local tmp_name_=`basename "$name_"` || \
691     logger -p user.alert -i "Init-initProgName: problems executing ( basename \"$name_\" ) ret($?)" >/dev/null
692
693   secureInput "$tmp_name_"
694 }
695 PROG_NAME__=`_initProgName "$0"`
696
697
698 _checkExecutables()
699 {
700   local tmp_=""
701   for i in tr dirname basename id logger kill cat grep route awk ifconfig; do
702     which $i >/dev/null 2>&1 || tmp_="${tmp_}$i "
703   done
704   if [ -n "$tmp_" ]; then
705     eprint "Init-checkExecutables: following executables not found or not executable:\n$tmp_"
706     #syslog "Init-checkExecutables: following executables not found or not executable: $tmp_"
707   fi
708 }
709 _checkExecutables
710
711
712 _setDebugLevel()
713 {
714   # accept only integer as arguments
715   if echo "$DEBUG" | grep -E -q '^[0-9]+$' ; then
716     local debug_="${DEBUG:-0}"
717   else
718     local debug_="0"
719   fi
720   VERBOSE__="$debug_"
721 }
722 _setDebugLevel
723 # }}}
724
725 # END OF FILE
726 ################################################################################
727 # vim:foldmethod=marker expandtab shiftwidth=2 tabstop=2