51c2e792149b9b662823520c670e10dc31b8c589
[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 # Latest change: Mon May 02 00:17:44 CEST 2005 [gebi]
8 ################################################################################
9
10
11 VERBOSE__=0
12 VERBOSE_TMP__=0
13
14 # FIXME maybe PROG_PATH__ for better error reporting?
15 PROG_NAME__=""    # initialised within init section
16
17 # directory for init scripts
18 INITD_DIR__="/etc/init.d/"
19
20 # >= level and the function will print the message
21 EPRINT__=1    # eprint (error print)
22 EEPRINT__=2   # 2print (intern error print)
23 DPRINT__=3    # dprint (debug print)
24
25 EXIT_FUNCTION__="_syslog"    # function to call upon die (can be set by user)
26
27 SYSLOG__="YES"
28
29 CMD_LINE__=""   # /proc/cmdline
30
31 LANG__="$LANG"
32 LC_ALL__="$LC_ALL"
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"; }
45 function restoreLang { LANG="$LANG__"; LC_ALL="$LC_ALL__"; }
46 function setCLang { saveLang; LANG="C"; LC_ALL="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 # i don't want to write exit status controle stuff every time
181 function execute
182 {
183   local to_exec_="$1"   # command to execute
184   local error_function_=${2:-"eprint"}    # function to call on error
185   local message_="$3"   # user supplied error message
186
187   local ret_=''
188
189   eval "$to_exec_"
190   ret_=$?
191
192   if [ $ret_ -eq 127 ]; then
193     syslog "problems executing ( $to_exec_ )" $ret_
194   fi
195   if [ $ret_ -ne 0 ]; then
196     if [ -z "$message_" ]; then
197       $error_function_ "problems executing ( $to_exec_ )" "$ret_"
198     else
199       $error_function_ "$message_" "$ret_"
200     fi
201   fi
202   dprint "exec-$error_function_: ( $to_exec_ ) ret($ret_)"
203   return $ret_
204 }
205
206 function silentExecute
207 {
208   unsetVerbose
209   execute "$@"
210   local ret_=$?
211   restoreVerbose
212   return $ret_
213 }
214
215
216 ###
217 #
218 # TEST FUNCTIONS
219 #
220 ###
221
222 # if the file DOES exist, everything is fine
223 function isExistent
224 {
225   local file_to_test_="$1"    # file to test
226   local error_function_=${2:-"eprint"}    # function to call on error
227   local message_="$3"    # user supplied error message
228
229   if [ ! -e "$file_to_test_" ]; then
230     if [ -z "$message_" ]; then
231       $error_function_ "file does not exist \"$file_to_test_\"" 66
232     else
233       $error_function_ "$message_"
234     fi
235     return 1
236   fi
237   dprint "isExistent(): file \"$1\" does exist => ready to go"
238   return 0
239 }
240
241 function isNotExistent
242 {
243   local file_to_test_="$1"    # file to test
244   local error_function_=${2:-"eprint"}    # function to call on error
245   local message_="$3"    # user supplied error message
246
247   if [ -e "$file_to_test_" ]; then
248     if [ -z "$message_" ]; then
249       $error_function_ "file does allready exist \"$file_to_test_\"" 67
250     else
251       $error_function_ "$message_"
252     fi
253     return 1
254   fi
255   dprint "isNotExistent(): file \"$1\" does not exist => ready to go"
256   return 0
257 }
258
259
260 function checkUser
261 {
262   local to_check_="$1"    # username to check against running process
263   local error_function_=${2:-"eprint"}    # function to call on error
264   local message_="$3"    # user supplied error message
265
266   local user_=''
267
268   user_=`id -un`
269   if [ $user_ != "$to_check_" ]; then
270     if [ -z "$message_" ]; then
271       $error_function_ "username \"$user_\" is not \"$to_check_\"" 77 $exit_function_
272     else
273       $error_function_ "$message_"
274     fi
275     return 1
276   else
277     dprint "checkUser(): accepted, username matches \"$to_check_\""
278     return 0
279   fi
280 }
281
282 function checkId
283 {
284   local to_check_="$1"    # user-id to check against running process
285   local error_function_=${2:-"eprint"}    # function to call on error
286   local message_="$3"    # user supplied error message
287
288   local user_id_=''
289
290   user_id_=`id -u`
291   if [ $user_id_ != "$to_check_" ]; then
292     if [ -z "$message_" ]; then
293       $error_function_ "UID \"$user_id_\" is not \"$to_check_\"" 77
294     else
295       $error_function_ "$message_"
296     fi
297     return 1
298   else
299     dprint "checkId(): accepted, UID matches \"$to_check_\""
300     return 0
301   fi
302 }
303
304 function checkRoot
305 {
306   checkId 0 "$1" "$2"
307 }
308
309 function isGrml
310 {
311   if [ -f /etc/grml_version ] ; then 
312     dprint "isGrml(): this seems to be a grml system"
313     return 0                       
314   else                               
315     dprint "isGrml(): this is not a grml system"
316     return 1                       
317   fi                                 
318 }
319
320 function runsFromHd
321 {
322   if [ -e "/etc/grml_cd" ]; then
323     dprint "runsFromHd(): grml is on CD"
324     return 1
325   else
326     dprint "runsFromHd(): grml is on HD"
327     return 0
328   fi
329 }
330
331 function runsFromCd
332 {
333   if [ -e "/etc/grml_cd" ]; then
334     dprint "runsFromCd(): grml is on CD"
335     return 0
336   else
337     dprint "runsFromCd(): grml is on HD"
338     return 1
339   fi
340 }
341
342
343 # secure input from console
344 function secureInput
345 {
346   local to_secure_="$1"
347   
348   local secured_=''
349
350   secured_=`echo -n "$to_secure_" |tr -c '[:alnum:]/.\-,\(\)' '_'`
351   dprint "secureInput(): \"$to_secure_\" => \"$secured_\""
352   echo "$secured_"
353 }
354
355
356 # convert all possible path formats to absolute paths
357 function relToAbs
358 {
359   local relpath_="$1"
360   local abspath_=''
361
362   abspath_="`readlink -f \"$relpath_\"`" || \
363     warn "relToAbs(): Problems getting absolute path" "$?" || return 1
364   dprint "relToAbs(): \"$relpath_\" => \"$abspath_\""
365   echo "$abspath_"
366 }
367
368 # Simple shell grep
369 function stringInFile
370 {
371   local to_test_="$1"   # matching pattern
372   local source_="$2"    # source-file to grep
373
374   if [ ! -e "$source_" ]; then
375     eprint "stringInFile(): \"$source_\" does not exist"
376     return 1
377   fi
378
379   case "$(cat $source_)" in *$to_test_*) return 0;; esac
380   return 1
381 }
382
383 # same for strings
384 function stringInString
385 {
386   local to_test_="$1"   # matching pattern
387   local source_="$2"    # string to search in
388
389   case "$source_" in *$to_test_*) return 0;; esac
390   return 1
391 }
392
393 # get value for bootparam given as first param
394 function getBootParam
395 {
396   local param_to_search_="$1"
397   local result_=''
398
399   stringInString " $param_to_search_=" "$CMD_LINE__" || return 1
400   result_="${CMD_LINE__##*$param_to_search_=}"
401   result_="${result_%%[   ]*}"
402   echo "$result_"
403   return 0
404 }
405
406 # Check boot commandline for specified option
407 function checkBootParam
408 {
409   stringInString " $1" "$CMD_LINE__"
410   return "$?"
411 }
412
413
414 # NETWORK  {{{
415
416 # validates an IP FIXME
417 function netValidIp
418 {
419   local ip_="$1"    # ip addresse to validate
420   local error_function_=${2:-"eprint"}    # function to call on error
421   local message_="$3"    # user supplied error message
422   
423   local ret_=''
424
425   echo "$ip_" | grep -E -q -e '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}' \
426     &>/dev/null
427   ret_=$?
428   if [ $ret_ -ne 0 ]; then
429     if [ -z "$message_" ]; then
430       "$error_function_" "ip-addresse \"$ip_\" is NOT valied" $ret_
431     else
432       "$error_function_" "$message_" $ret_
433     fi
434     return 1
435   fi
436
437   dprint "ip-addresse \"$ip_\" is valied" $ret_
438   return $ret_
439 }
440
441 function netGetIfaces
442 {
443   local error_function_=${1:-"eprint"}    # function to call on error
444   local message_="$2"    # user supplied error message
445   local if_=''
446   local ret_=''
447
448   #ip a|grep 'inet ' |awk '$NF !~ /lo/{print $NF}'
449   if_="`ip a|grep 'inet ' |awk '{print $NF}'`"
450   ret_=$?
451   if [ -z "$if_" ]; then
452     if [ -z "$message_" ]; then
453       "$error_function_" "no interfaces found" $ret_
454     else
455       "$error_function_" "$message_" $ret_
456     fi
457     return 1
458   fi
459   dprint "interfaces found" $ret_
460   echo "$if_"
461 }
462
463 # FIXME
464 function netGetDefaultGateway
465 {
466   local error_function_=${1:-"eprint"}    # function to call on error
467   local message_="$2"    # user supplied error message
468   
469   local ip_=''
470   local ret_=''
471   
472   setCLang
473   ip_=`route -n | awk '/^0\.0\.0\.0/{print $2; exit}'`
474   ret_=$?
475   restoreLang
476   if [ -z "$ip_" ]; then
477     if [ -z "$message_" ]; then
478       "$error_function_" "no default gateway found" $ret_
479     else
480       "$error_function_" "$message_" $ret_
481     fi
482     return 1
483   fi
484   dprint "default gateway is \"$ip_\"" $ret_
485   echo "$ip_"
486   return 0
487 }
488
489 # FIXME
490 function netGetNetmask
491 {
492   local iface_="$1"
493   local error_function_=${2:-"eprint"}    # function to call on error
494   local message_="$3"    # user supplied error message
495   
496   local nm_=''
497   local ret_=''
498   
499   setCLang
500   nm_=`ifconfig "$iface_" | awk '/[Mm]ask/{FS="[:   ]*"; $0=$0; print $8; exit}'`
501   ret_=$?
502   restoreLang
503   if [ -z "$nm_" ]; then
504     if [ -z "$message_" ]; then
505       "$error_function_" "could not find a netmask for \"$iface_\"" $ret_
506     else 
507       "$error_function_" "$message_" $ret_
508     fi
509     return 1
510   fi
511   dprint "netmask on \"$iface_\" is \"$nm_\"" $ret_
512   echo "$nm_"
513   return 0
514 }
515
516 # FIXME
517 function netGetIp
518 {
519   local iface_="$1"
520   local error_function_=${2:-"eprint"}    # function to call on error
521   local message_="$3"    # user supplied error message
522
523   local ip_=""
524   local ret_=""
525
526   setCLang
527   #ip_=`ip addr list eth0 |mawk '/inet/{split($2,A,"/"); print A[1]}'`
528   ip_=`ifconfig "$iface_" | awk '/[Ii]net [Aa]ddr/{FS="[:  ]*"; $0=$0; print $4; exit}'`
529   ret_=$?
530   restoreLang
531   if [ -z "$ip_" ]; then
532     if [ -z "$message_" ]; then
533       "$error_function_" "no ip for \"$iface_\" found" $ret_
534     else
535       "$error_function_" "$message_" $ret_
536     fi
537     return 1
538   fi
539   dprint "addresse for \"$iface_\" is \"$ip_\"" $ret_
540   echo "$ip_"
541   return 0
542
543
544 function netGetNameservers
545 {
546   local error_function_=${1:-"eprint"}    # function to call on error
547   local message_="$2"    # user supplied error message
548   
549   local file_="/etc/resolv.conf"
550   local ns_=""
551
552   if [ ! -e $file_ ]; then
553     warn "file \"$file_\" does not exist, could not get nameservers"
554     return 1
555   fi
556   
557   setCLang
558   ns_=`awk '/^nameserver/{printf "%s ",$2}' $file_ |xargs echo`
559   restoreLang
560   if [ -z "$ns_" ]; then
561     if [ -z "$message_" ]; then
562       "$error_function_" "no nameservers found" $ret_
563     else
564       "$error_function_" "$message_" $ret_
565     fi
566     return 1
567   fi
568   dprint "nameservers: \"$ns_\"" $ret_
569   echo "$ns_"
570   return 0
571 }
572
573 # }}}
574
575 # SERVICES {{{
576 function _touchService
577 {
578   local action_="${1:-"start"}"
579   local service_="$2"
580   local error_function_=${3:-"eprint"}    # function to call on error
581   local message_="$4"     # user supplied error message
582
583   local i=""
584   local known_action_='false'
585   for i in "start" "stop" "restart" "reload" "force-reload"; do
586     if [[ $i == $action_ ]]; then
587       known_action_='true'
588       break
589     fi
590   done
591   $known_action_ || warn "_touchService(): unknown action \"$action_\""
592
593
594   local service_path_=""
595   service_path_="${INITD_DIR__}/$service_"
596   if [ ! -e "$service_path_" ]; then
597     warn "_touchService(): service does not exist: \"$service_\""
598     return 1
599   fi
600   if [ ! -x "$service_path_" ]; then
601     warn "_touchService(): service is not executable: \"$service_\""
602   fi
603   
604   local ret_=""
605   "$service_path_" "$action_"
606   ret_=$?
607   if [[ $ret_ != 0 ]]; then
608     if [ -z "$message_" ]; then
609       "$error_function_" "Problems ${action_}ing service \"$service_\"" $ret_
610     else
611       "$error_function_" "$message_" $ret_
612     fi
613     return 1
614   fi
615   dprint "_touchService(): successfully started service \"$service_\""
616   return 0
617 }
618
619 function _createServiceFunctions
620 {
621   for i in "start" "stop" "restart" "reload" "force-reload"; do
622     eval "function ${i}Service { _touchService ${i} \"\$1\" \"\$2\" \"\$3\"; }"
623   done
624 }
625 _createServiceFunctions
626 # }}}
627
628 # prints the next free /dev/loop* to stdout
629 function findNextFreeLoop
630 {
631   local error_function_=${1:-"eprint"}    # function to call on error
632   local message_="$2"    # user supplied error message
633
634   local tmp_=''   # tmp
635   local i=''      # counter
636   local ret_=''   # saved return value
637   
638   for i in 'losetup' 'losetup.orig'; do
639     tmp_=`$i -f 2>/dev/null`
640     if [ $? -eq 0 ]; then
641       echo $tmp_
642       return 0
643     fi
644   done
645
646   # we have to search
647   dprint 'findNextFreeLoop(): losetup does not recognice option -f, searching next free loop device'
648   for i in `seq 0 100`; do
649     test -e /dev/loop$i || continue
650     losetup /dev/loop$i &>/dev/null
651     ret_=$?
652     case "$ret_" in
653       2) continue ;;  # losetup could not get status of loopdevice (EPERM)
654       0) continue ;;  # device exist
655       1) echo "/dev/loop$i"; return 0 ;;  # device does not exist and no error
656       ?) continue ;;  # return value not available in 'man losetup'
657     esac
658   done
659
660   # hmm... could not find a loopdevice
661   if [ -z "$message_" ]; then
662     $error_function_ "could not find a free loop device"
663   else
664     $error_function_ "$message_"
665   fi
666   return 1
667 }
668
669
670 # INIT {{{
671
672 function _initProgName
673 {
674   local name_="$1"    # program name
675   
676   local tmp_name_=`basename "$name_"` || \
677     logger -p user.alert -i "Init-initProgName: problems executing ( basename \"$name_\" ) ret($?)" >/dev/null
678   
679   secureInput "$tmp_name_"
680 }
681 PROG_NAME__=`_initProgName "$0"`
682
683
684 function _checkExecutables
685 {
686   local tmp_=""
687   for i in tr dirname basename id logger kill cat grep route awk ifconfig; do
688     type -p $i &>/dev/null || tmp_="${tmp_}$i "
689   done
690   if [ -n "$tmp_" ]; then
691     eprint "Init-checkExecutables: following executables not found or not executable:\n$tmp_"
692     #syslog "Init-checkExecutables: following executables not found or not executable: $tmp_"
693   fi
694 }
695 _checkExecutables
696
697
698 function _checkBootParam
699 {
700   local path_="/proc/cmdline"
701   if [ -e "$path_" ]; then
702     CMD_LINE__=`execute "cat $path_" warnLog`
703     return 0
704   fi
705   warnLog "$path_ does not exist, thus sh-lib may not work reliable!"
706   return 1
707 }
708 _checkBootParam
709
710
711 function _setDebugLevel
712 {
713   local debug_="${DEBUG:-0}"
714   VERBOSE__="$debug_"
715 }
716 _setDebugLevel
717 # }}}
718
719 # END OF FILE
720 ################################################################################
721 # vim:foldmethod=marker expandtab shiftwidth=2 tabstop=2