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