2 ## @brief Logging, Datei-Operationen, DNS- und NTP-Dienste, Dictionary-Dateien, PID- und Lock-Behandlung, Berichte
3 # Beginn der Doku-Gruppe
7 ## @var Quelldatei für Standardwerte des Kern-Pakets
8 ON_CORE_DEFAULTS_FILE=/usr/share/opennet/core.defaults
9 ## @var Quelldatei für Standardwerte des Nutzer-VPN-Pakets
10 ON_OPENVPN_DEFAULTS_FILE=/usr/share/opennet/openvpn.defaults
11 ## @var Quelldatei für Standardwerte des Hotspot-Pakets
12 ON_WIFIDOG_DEFAULTS_FILE=/usr/share/opennet/wifidog.defaults
13 ## @var Pfad zur dnsmasq-Server-Datei zur dynamischen Aktualisierung durch Dienste-Erkennung
14 DNSMASQ_SERVERS_FILE_DEFAULT=/var/run/
dnsmasq.servers
15 ## @var Dateiname für erstellte Zusammenfassungen
16 REPORTS_FILE=/tmp/on_report.tar.gz
17 ## @var Basis-Verzeichnis für Log-Dateien
19 # beim ersten Pruefen wird der Debug-Modus ermittelt
25 ## @param message Debug-Nachricht
26 ## @brief Debug-Meldungen ins syslog schreiben
27 ## @details Die Debug-Nachrichten landen im syslog (siehe ``logread``).
28 ## Falls das aktuelle Log-Level bei ``info`` oder niedriger liegt, wird keine Nachricht ausgegeben.
30 # bei der ersten Ausfuehrung dauerhaft speichern
31 [ -z
"$DEBUG_ENABLED" ] && \
32 DEBUG_ENABLED=$(uci_is_true
"$(uci_get on-core.settings.debug false)" && echo 1 || echo 0)
33 [
"$DEBUG_ENABLED" =
"0" ] || logger -t
"$(basename "$0
")[$$]" "$1"
38 ## @param message Log-Nachricht
39 ## @brief Informationen und Fehlermeldungen ins syslog schreiben
40 ## @details Die Nachrichten landen im syslog (siehe ``logread``).
41 ## Die info-Nachrichten werden immer ausgegeben, da es kein höheres Log-Level gibt.
43 logger -t
"$(basename "$0
")[$$]" "$1"
47 ## @fn append_to_custom_log()
48 ## @brief Hänge eine neue Nachricht an ein spezfisches Protokoll an.
49 ## @param log_name Name des Log-Ziels
50 ## @param event die Kategorie der Meldung (up/down/???)
51 ## @param msg die textuelle Beschreibung des Ereignis (z.B. "connection with ... closed")
52 ## @details Die Meldungen werden beispielsweise von den konfigurierten openvpn-up/down-Skripten gesendet.
57 local logfile=
"$LOG_BASE_DIR/${log_name}.log"
58 echo
"$(date) openvpn [$event]: $msg" >>
"$logfile"
59 # Datei kuerzen, falls sie zu gross sein sollte
60 local filesize=$(get_filesize
"$logfile")
61 [
"$filesize" -gt 10000 ] && sed -i
"1,30d" "$logfile"
67 ## @brief Liefere den Inhalt eines spezifischen Logs (z.B. das OpenVPN-Verbindungsprotokoll) zurück.
68 ## @param log_name Name des Log-Ziels
69 ## @returns Zeilenweise Ausgabe der Protokollereignisse (aufsteigend nach Zeitstempel sortiert).
72 local logfile=
"$LOG_BASE_DIR/${log_name}.log"
73 [ -e
"$logfile" ] && cat
"$logfile" ||
true
77 ## @fn update_file_if_changed()
78 ## @param filename Name der Zieldatei
79 ## @brief Aktualisiere eine Datei, falls sich ihr Inhalt geändert haben sollte.
80 ## @details Der neue Inhalt der Datei wird auf der Standardeingabe erwartet.
81 ## Im Falle der Gleichheit von aktuellem Inhalt und zukünftigem Inhalt wird
82 ## keine Schreiboperation ausgeführt. Der Exitcode gibt an, ob eine Schreiboperation
83 ## durchgeführt wurde.
84 ## @return exitcode=0 (Erfolg) falls die Datei geändert werden musste
85 ## @return exitcode=1 (Fehler) falls es keine Änderung gab
87 local target_filename=
"$1"
88 local content=
"$(cat -)"
89 if [ -e
"$target_filename" ] && echo
"$content" | cmp -
s -
"$target_filename"; then
90 # the content did not change
91 trap
"" $GUARD_TRAPS &&
return 1
94 echo
"$content" >
"$target_filename"
100 ## @fn update_dns_servers()
101 ## @brief Übertrage die Liste der als DNS-Dienst announcierten Server in die dnsmasq-Konfiguration.
102 ## @details Die Liste der DNS-Server wird in die separate dnsmasq-Servers-Datei geschrieben (siehe @sa DNSMASQ_SERVERS_FILE_DEFAULT).
103 ## Die Server-Datei wird nur bei Änderungen neu geschrieben. Dasselbe gilt für den Neustart des Diensts.
104 ## Diese Funktion sollte via olsrd-nameservice-Trigger oder via cron-Job ausgeführt werden.
106 trap
"error_trap update_dns_servers '$*'" $GUARD_TRAPS
110 local use_dns=
"$(uci_get on-core.settings.use_olsrd_dns)"
111 # return if we should not use DNS servers provided via olsrd
112 uci_is_false
"$use_dns" &&
return 0
113 local servers_file=$(uci_get
"dhcp.@dnsmasq[0].serversfile")
114 # aktiviere die
"dnsmasq-serversfile"-Direktive, falls noch nicht vorhanden
115 if [ -z
"$servers_file" ]; then
116 servers_file=$DNSMASQ_SERVERS_FILE_DEFAULT
117 uci
set "dhcp.@dnsmasq[0].serversfile=$servers_file"
118 uci commit
"dhcp.@dnsmasq[0]"
121 # wir sortieren alphabetisch - Naehe ist uns egal
125 [ -n
"$port" -a
"$port" !=
"53" ] && host=
"$host#$port"
128 # es gab eine Aenderung
130 # Konfiguration neu einlesen
135 ## @fn update_ntp_servers()
136 ## @brief Übertrage die Liste der als NTP-Dienst announcierten Server in die sysntpd-Konfiguration.
137 ## @details Die Liste der NTP-Server wird in die uci-Konfiguration geschrieben.
138 ## Die uci-Konfiguration wird nur bei Änderungen neu geschrieben. Dasselbe gilt für den Neustart des Diensts.
139 ## Diese Funktion sollte via olsrd-nameservice-Trigger oder via cron-Job ausgeführt werden.
140 ## @sa http://wiki.openwrt.org/doc/uci/system#remote_time_ntp
142 trap
"error_trap update_ntp_servers '$*'" $GUARD_TRAPS
146 local use_ntp=
"$(uci_get on-core.settings.use_olsrd_ntp)"
147 # return if we should not use NTP servers provided via olsrd
148 uci_is_false
"$use_ntp" &&
return
149 # schreibe die Liste der NTP-Server neu
151 # wir sortieren alphabetisch - Naehe ist uns egal
155 [ -n
"$port" -a
"$port" !=
"123" ] && host=
"$host:$port"
162 ## @fn add_banner_event()
163 ## @brief Füge ein Ereignis zum dauerhaften Ereignisprotokoll (/etc/banner) hinzu.
164 ## @param event Ereignistext
165 ## @param timestamp [optional] Der Zeitstempel-Text kann bei Bedarf vorgegeben werden.
166 ## @details Ein Zeitstempel, sowie hübsche Formatierung wird automatisch hinzugefügt.
168 trap
"error_trap add_banner_event '$*'" $GUARD_TRAPS
170 # verwende den optionalen zweiten Parameter oder den aktuellen Zeitstempel
171 local timestamp=
"${2:-$(date)}"
172 local line=
" - $timestamp - $event -"
174 # Steht unser Text schon im Banner? Ansonsten hinzufuegen ...
175 # bis einschliesslich Version v0.5.0 war "clean_restart_log" das Schluesselwort
176 # ab v0.5.1 verwenden wir "system events"
177 if ! grep -qE
'(clean_restart_log|system events)' /etc/banner; then
178 echo
" ------------------- system events -------------------"
180 # die Zeile auffuellen
181 while [
"${#line}" -lt 54 ];
do line=
"$line-"; done
188 clean_restart_log() {
189 awk
'{if ($1 != "-") print}' /etc/banner >/tmp/banner
190 mv /tmp/banner /etc/banner
195 ## @fn _get_file_dict_value()
196 ## @brief Auslesen eines Werts aus einer Schlüssel/Wert-Datei
197 ## @param status_file der Name der Schlüssel/Wert-Datei
198 ## @param field das Schlüsselwort
199 ## @returns Den zum gegebenen Schlüssel gehörenden Wert aus der Schlüssel/Wert-Datei.
200 ## Falls kein passender Schlüssel gefunden wurde, dann ist die Ausgabe leer.
201 ## @details Jede Zeile dieser Datei enthält einen Feldnamen und einen Wert - beide sind durch
202 ## ein beliebiges whitespace-Zeichen getrennt.
203 ## Dieses Dateiformat wird beispielsweise für die Dienst-Zustandsdaten verwendet.
204 ## Zusätzlich ist diese Funktion auch zum Parsen von openvpn-Konfigurationsdateien geeignet.
208 ## @fn _get_file_dict_keys()
209 ## @brief Liefere alle Schlüssel aus einer Schlüssel/Wert-Datei.
210 ## @param status_files Namen der Schlüssel/Wert-Dateien
211 ## @returns Liste aller Schlüssel aus der Schlüssel/Wert-Datei.
212 ## @sa _get_file_dict_value
213 _get_file_dict_keys() { sed
's/[ \t].*//' "$@" 2>/dev/
null ||
true; }
216 ## @fn _set_file_dict_value()
217 ## @brief Schreiben eines Werts in eine Schlüssel/Wert-Datei
218 ## @param status_file der Name der Schlüssel/Wert-Datei
219 ## @param field das Schlüsselwort
220 ## @param value der neue Wert
221 ## @sa _get_file_dict_value
222 _set_file_dict_value() {
228 # Wert ist korrekt? Wir sind fertig ...
229 [
"$value" =
"$new_value" ] &&
return 0
230 # Filtere bisherige Zeilen mit dem
key heraus.
231 # Fuege anschliessend die Zeile mit dem neuen Wert an.
232 # Die Sortierung sorgt fuer gute Vergleichbarkeit, um die Anzahl der
233 # Schreibvorgaenge (=Wahrscheinlichkeit von gleichzeitigem Zugriff) zu reduzieren.
235 grep -v -w -
s "$field" "$status_file"
236 echo
"$field $new_value"
241 ## @fn get_on_core_default()
242 ## @brief Liefere einen der default-Werte der aktuellen Firmware zurück (Paket on-core).
243 ## @param key Name des Schlüssels
244 ## @details Die default-Werte werden nicht von der Konfigurationsverwaltung uci verwaltet.
245 ## Somit sind nach jedem Upgrade imer die neuesten Standard-Werte verfügbar.
246 get_on_core_default() {
251 ## @fn get_on_firmware_version()
252 ## @brief Liefere die aktuelle Firmware-Version zurück.
253 ## @returns Die zurückgelieferte Zeichenkette beinhaltet den Versionsstring (z.B. "0.5.0").
254 ## @details Per Konvention entspricht die Version jedes Firmware-Pakets der Firmware-Version.
255 get_on_firmware_version() {
256 opkg status on-core | awk
'{if (/Version/) print $2;}'
261 ## @param on_id die ID des AP - z.B. "1.96" oder "2.54"
262 ## @param on_ipschema siehe "get_on_core_default on_ipschema"
263 ## @param interface_number 0..X (das WLAN-Interface ist typischerweise Interface #0)
264 ## @attention Manche Aufrufende verlassen sich darauf, dass *on_id_1* und
265 ## *on_id_2* nach dem Aufruf verfügbar sind (also _nicht_ als "local"
266 ## Variablen deklariert wurden).
271 echo
"$on_id" | grep -q
"\." || on_id=1.$on_id
272 on_id_1=$(echo
"$on_id" | cut -
d . -f 1)
273 on_id_2=$(echo
"$on_id" | cut -
d . -f 2)
274 echo $(eval echo $on_ipschema)
279 ## @brief Liefere die aktuell konfigurierte Main-IP zurück.
280 ## @returns Die aktuell konfigurierte Main-IP des AP oder die voreingestellte IP.
281 ## @attention Seiteneffekt: die Variablen "on_id_1" und "on_id_2" sind anschließend verfügbar.
284 local on_id=$(uci_get on-core.settings.on_id
"$(get_on_core_default on_id_preset)")
285 local ipschema=$(get_on_core_default on_ipschema)
286 get_on_ip
"$on_id" "$ipschema" 0
290 # check if a given lock file:
291 # A) exists, but it is outdated (determined by the number of minutes given as second parameter)
292 # B) exists, but is fresh
294 # A + C return success and create that file
295 # B return failure and do not touch that file
298 local max_age_minutes=$2
299 [ ! -e
"$lock_file" ] && touch
"$lock_file" &&
return 0
300 local file_timestamp=$(get_file_modification_timestamp_minutes
"$lock_file")
301 # too old? We claim it
for ourself.
302 is_timestamp_older_minutes
"$file_timestamp" "$max_age_minutes" && touch
"$lock_file" &&
return 0
303 # lockfile is too young
304 trap
"" $GUARD_TRAPS &&
return 1
308 clean_stale_pid_file() {
310 [ -e
"$pid_file" ] ||
return 0
311 local pid=$(cat
"$pid_file" | sed
's/[^0-9]//g')
312 [ -z
"$pid" ] &&
msg_debug "removing broken PID file: $pid_file" && rm
"$pid_file" &&
return 0
313 [ ! -e
"/proc/$pid" ] &&
msg_debug "removing stale PID file: $pid_file" && rm
"$pid_file" &&
return 0
318 # Pruefe ob eine PID-Datei existiert und ob die enthaltene PID zu einem Prozess
319 # mit dem angegebenen Namen (nur Dateiname - ohne Pfad) verweist.
320 # Parameter PID-Datei: vollstaendiger Pfad
321 # Parameter Prozess-Name: Dateiname ohne Pfad
323 trap
"error_trap check_pid_file '$*'" $GUARD_TRAPS
325 local process_name=
"$2"
327 local current_process
328 [ -z
"$pid_file" -o ! -e
"$pid_file" ] && trap
"" $GUARD_TRAPS &&
return 1
329 pid=$(cat
"$pid_file" | sed
's/[^0-9]//g')
330 # leere/kaputte PID-Datei
331 [ -z
"$pid" ] && trap
"" $GUARD_TRAPS &&
return 1
332 # Prozess-Datei ist kein symbolischer Link?
333 [ ! -L
"/proc/$pid/exe" ] && trap
"" $GUARD_TRAPS &&
return 1
334 current_process=$(readlink
"/proc/$pid/exe")
335 [
"$process_name" !=
"$(basename "$current_process
")" ] && trap
"" $GUARD_TRAPS &&
return 1
343 # "on-core" achtet auch auf nicht-uci-Aenderungen (siehe PERSISTENT_SERVICE_STATUS_DIR)
344 [ -z
"$(uci changes "$config
")" -a
"$config" !=
"on-core" ] &&
return 0
347 system|network|firewall|dhcp)
348 reload_config ||
true
351 /etc/init.d/olsrd restart ||
true
354 /etc/init.d/openvpn reload || true
357 # TODO: verwenden wir ueberhaupt eine uci-Konfiguration?
358 /etc/init.d/openvpn reload ||
true
359 reload_config ||
true
366 # es ist nichts zu tun
369 msg_info "no handler defined for applying config changes for '$config'"
376 # Setzen einer Opennet-ID.
377 # 1) Hostnamen setzen
378 # 2) IPs fuer alle Opennet-Interfaces setzen
379 # 3) Main-IP in der olsr-Konfiguration setzen
380 # 4) IP des Interface "free" setzen
381 # 5) DHCP-Redirect fuer wifidog setzen
383 trap
"error_trap set_opennet_id '$*'" $GUARD_TRAPS
393 # ID normalisieren (AP7 -> AP1.7)
394 echo
"$new_id" | grep -q
"\." || new_id=1.$new_id
395 # ON_ID in on-core-Settings setzen
396 prepare_on_uci_settings
397 uci
set "on-core.settings.on_id=$new_id"
398 apply_changes on-core
399 # Hostnamen konfigurieren
400 find_all_uci_sections system system |
while read uci_prefix;
do
401 uci
set "${uci_prefix}.hostname=AP-$(echo "$new_id
" | tr . -)"
404 # IP-Adressen konfigurieren
405 ipschema=$(get_on_core_default on_ipschema)
406 netmask=$(get_on_core_default on_netmask)
407 main_ipaddr=$(get_on_ip
"$new_id" "$ipschema" 0)
408 for network in $(get_sorted_opennet_interfaces);
do
409 uci_prefix=network.$network
410 [
"$(uci_get "${uci_prefix}.proto
")" !=
"static" ] &&
continue
411 ipaddr=$(get_on_ip
"$new_id" "$ipschema" "$if_counter")
412 uci
set "${uci_prefix}.ipaddr=$ipaddr"
413 uci
set "${uci_prefix}.netmask=$netmask"
416 # OLSR-MainIP konfigurieren
417 olsr_set_main_ip
"$main_ipaddr"
419 # wifidog-Interface konfigurieren
420 if is_function_available
"get_on_wifidog_default"; then
421 ipschema=$(get_on_wifidog_default free_ipschema)
422 netmask=$(get_on_wifidog_default free_netmask)
423 free_ipaddr=$(get_on_ip
"$new_id" "$ipschema" 0)
424 uci_prefix=
"network.$NETWORK_FREE"
425 uci
set "${uci_prefix}=interface"
426 uci
set "${uci_prefix}.proto=static"
427 uci
set "${uci_prefix}.ipaddr=$free_ipaddr"
428 uci
set "${uci_prefix}.netmask=$netmask"
430 apply_changes network
431 # DHCP-Forwards fuer wifidog
432 # Ziel ist beispielsweise folgendes Setup:
433 # firewall.@redirect[0]=redirect
434 # firewall.@redirect[0].src=opennet
435 # firewall.@redirect[0].proto=udp
436 # firewall.@redirect[0].src_dport=67
437 # firewall.@redirect[0].target=DNAT
438 # firewall.@redirect[0].src_port=67
439 # firewall.@redirect[0].dest_ip=10.3.1.210
440 # firewall.@redirect[0].src_dip=192.168.1.210
441 find_all_uci_sections firewall redirect
"src=$ZONE_MESH" proto=udp src_dport=67 src_port=67 target=DNAT |
while read uci_prefix;
do
442 uci
set "${uci_prefix}.name=DHCP-Forward Opennet"
443 uci
set "${uci_prefix}.dest_ip=$free_ipaddr"
444 uci
set "${uci_prefix}.src_dip=$main_ipaddr"
446 apply_changes firewall
450 # Durchsuche eine Schluessel-Wert-Liste nach einem Schluessel und liefere den dazugehoerigen Wert zurueck.
453 # Der Separator ist konfigurierbar.
454 # Die Liste wird auf der Standardeingabe erwartet.
455 # Der erste und einzige Parameter ist der gewuenschte Schluessel.
456 get_from_key_value_list() {
457 local search_key=
"$1"
461 sed
's/[ \t]\+/\n/g' |
while read key_value;
do
462 key=$(echo
"$key_value" | cut -f 1 -
d "$separator")
463 [
"$key" =
"$search_key" ] && echo
"$key_value" | cut -f 2- -
d "$separator" &&
break ||
true
469 ## @fn replace_in_key_value_list()
470 ## @brief Ermittle aus einer mit Tabulatoren oder Leerzeichen getrennten Liste von Schlüssel-Wert-Paaren den Inhalt des Werts zu einem Schlüssel.
471 ## @param search_key der Name des Schlüsselworts
472 ## @param separator der Name des Trennzeichens zwischen Wert und Schlüssel
473 ## @returns die korrigierte Schlüssel-Wert-Liste wird ausgegeben (eventuell mit veränderten Leerzeichen oder Tabulatoren)
474 replace_in_key_value_list() {
475 local search_key=
"$1"
479 sed
's/[ \t]\+/\n/g' |
while read key_value;
do
480 key=$(echo
"$key_value" | cut -f 1 -
d "$separator")
481 if [
"$key" =
"$search_key" ]; then
482 # nicht ausgeben, falls der Wert leer ist
483 [ -n
"$value" ] && echo -n
" ${key_value}${separator}${value}" ||
true
485 echo -n
" $key_value"
492 # Wandle einen uebergebenene Parameter in eine Zeichenkette um, die sicher als Dateiname verwendet werden kann
493 get_safe_filename() {
494 echo
"$1" | sed
's/[^a-zA-Z0-9._\-]/_/g'
499 date +%
s | awk
'{print int($1/60)}'
503 get_file_modification_timestamp_minutes() {
505 date --reference
"$filename" +%
s | awk
'{ print int($1/60) }'
509 # Achtung: Zeitstempel aus der Zukunft gelten immer als veraltet.
510 is_timestamp_older_minutes() {
511 local timestamp_minute=
"$1"
512 local difference=
"$2"
513 local now=
"$(get_time_minute)"
515 [
"$now" -ge
"$((timestamp_minute+difference))" ] &&
return 0
516 # timestamp in future -> invalid -> let's claim it is too old
517 [
"$now" -lt
"$timestamp_minute" ] && \
518 msg_info
"WARNING: Timestamp from future found: $timestamp_minute (minutes since epoch)" && \
520 trap
"" $GUARD_TRAPS &&
return 1
524 ## @fn get_uptime_seconds()
525 ## @brief Ermittle die Anzahl der Sekunden seit dem letzten Bootvorgang.
526 get_uptime_seconds() {
527 cut -f 1 -
d . /proc/uptime
531 ## @fn run_delayed_in_background()
532 ## @brief Führe eine Aktion verzögert im Hintergrund aus.
533 ## @param delay Verzögerung in Sekunden
534 ## @param command alle weiteren Token werden als Kommando und Parameter interpretiert und mit Verzögerung ausgeführt.
535 run_delayed_in_background() {
538 (sleep
"$delay" &&
"$@") </dev/
null >/dev/
null 2>&1 &
542 ## @fn get_filesize()
543 ## @brief Ermittle die Größe einer Datei in Bytes.
544 ## @params filename Name der zu untersuchenden Datei.
547 wc -
c "$filename" | awk
'{ print $1 }'
552 # Der Name der erzeugten tar-Datei wird als Ergebnis ausgegeben.
554 trap
"error_trap generate_report '$*'" $GUARD_TRAPS
557 local temp_dir=$(mktemp -
d)
558 local reports_dir=
"$temp_dir/report"
559 local tar_file=$(mktemp)
561 # die Skripte duerfen davon ausgehen, dass wir uns im Zielverzeichnis befinden
562 mkdir -
p "$reports_dir"
564 find /usr/lib/opennet/reports -type f |
while read fname;
do
565 [ ! -x
"$fname" ] &&
msg_info "skipping non-executable report script: $fname" &&
continue
566 "$fname" ||
msg_info "ERROR: reports script failed: $fname"
569 tar czf
"$tar_file" "report"
571 mv
"$tar_file" "$REPORTS_FILE"
575 # Filtere aus den zugaenglichen Quellen moegliche Fehlermeldungen.
576 # Falls diese Funktion ein nicht-leeres Ergebnis zurueckliefert, dann kann dies als Hinweis fuer den
577 # Nutzer verwendet werden, auf dass er einen Fehlerbericht einreicht.
578 get_potential_error_messages() {
580 # 1) get_service_as_csv
581 # Wir ignorieren "get_service_as_csv"-Meldungen - diese werden durch asynchrone Anfragen des
582 # Web-Interface ausgeloest, die beim vorzeitigen Abbruch des Seiten-Lade-Vorgangs mit
583 # einem Fehler enden.
584 filters=
"${filters}|trapped.*get_service_as_csv"
585 # 2) openvpn.*Error opening configuration file
586 # Beim Booten des Systems wurde die openvpn-Config-Datei, die via uci referenziert ist, noch
587 # nicht erzeugt. Beim naechsten cron-Lauf wird dieses Problem behoben.
588 filters=
"${filters}|openvpn.*Error opening configuration file"
589 # 3) openvpn(...)[...]: Exiting due to fatal error
590 # Das Verzeichnis /var/etc/openvpn/ existiert beim Booten noch nicht.
591 filters=
"${filters}|openvpn.*Exiting due to fatal error"
592 # 4) openvpn(...)[...]: SIGUSR1[soft,tls-error] received, process restarting
593 # Diese Meldung taucht bei einem Verbindungsabbruch auf. Dieses Ereignis ist nicht
594 # ungewoehnlich und wird mittels des Verbindungsprotokolls bereits hinreichend gewuerdigt
595 filters=
"${filters}|openvpn.*soft,tls-error"
596 # 5) openvpn(...)[...]: TLS Error: TLS handshake failed
597 # Diese Meldung deutet einen fehlgeschlagenen Verbindungsversuch an. Dies ist nicht
598 # ungewoehnlich (beispielsweise auch fuer Verbindungstests).
599 filters=
"${filters}|openvpn.*TLS Error"
600 # 6) olsrd: /etc/rc.d/S65olsrd: startup-error: check via: '/usr/sbin/olsrd -f "/var/etc/olsrd.conf" -nofork'
601 # Falls noch kein Interface vorhanden ist (z.B. als wifi-Client), dann taucht diese Meldung
603 filters=
"${filters}|olsrd.*startup-error"
605 # Beim Booten tauchen Fehlermeldungen aufgrund nicht konfigurierter Netzwerk-Interfaces auf.
606 # TODO: ucarp nur noch als nachinstallierbares Paket markieren (erfordert Aenderung der Makefile-Erzeugung)
607 filters=
"${filters}|ucarp"
608 # 8) olsrd: /etc/rc.d/S65olsrd: ERROR: there is already an IPv4 instance of olsrd running (pid: '1099'), not starting.
609 # Dieser Fehler tritt auf, wenn der olsrd_check einen olsrd-Neustart ausloest, obwohl er schon laeuft.
610 filters=
"${filters}|olsrd: ERROR: there is already an IPv4 instance of olsrd running"
611 # 9) openvpn(...)[...]: Authenticate/Decrypt packet error
612 # Paketverschiebungen nach dem Verbindungsaufbau - anscheinend unproblematisch.
613 filters=
"${filters}|openvpn.*Authenticate/Decrypt packet error"
614 # 10) olsrd: ... olsrd_setup_smartgw_rules() Warning: kmod-ipip is missing.
615 # olsrd gibt beim Starten generell diese Warnung aus. Wir koennen sie ignorieren.
616 filters=
"${filters}|olsrd.*olsrd_setup_smartgw_rules"
617 # 11) olsrd: ... olsrd_write_interface() Warning: Interface '...' not found, skipped
618 # Falls das wlan-Interface beim Bootvorgang noch nicht aktiv ist, wenn olsrd startet, dann erscheint diese
620 filters=
"${filters}|olsrd.*Interface.*not found"
621 # 12) dropbear[...]: Exit (root): Error reading: Connection reset by peer
622 # Verbindungsverlust einer ssh-Verbindung. Dies darf passieren.
623 filters=
"${filters}|dropbear.*Connection reset by peer"
624 # 13) cron-error: nc.*: short write
625 # Falls die Routen via nc während eines olsrd-Neustarts ausgelesen werden, reisst eventuell die Socket-
626 # Verbindung ab - dies ist akzeptabel.
627 filters=
"${filters}|nc: short write"
628 # 14) openvpn(___service_name___)[...]: write UDPv4: Network is unreachable
629 # Beispielsweise bei einem olsrd-Neustart reisst die Verbindung zum UGW-Server kurz ab.
630 filters=
"${filters}|openvpn.*Network is unreachable"
631 # 15) wget: can't connect to remote host
632 # Eine frühe Geschwindigkeitsmessung (kurz nach dem Booten) darf fehlschlagen.
633 filters=
"${filters}|wget: can.t connect to remote host"
634 # System-Fehlermeldungen (inkl. "trapped")
635 logread | grep -i error | grep -vE
"(${filters#|})" ||
true
639 # Im openwrt-Build-Prozess wird aus bisher ungeklaerter Ursache die falsche opkg-Repository-URL gesetzt.
640 # Diese Funktion erlaubt die einfache Aenderung der opkg-URL.
641 # Parameter: URL-Bestandteile (z.B. "stable/0.5.0")
642 set_opkg_download_version() {
644 sed -i
"s#\(/openwrt\)/[^/]\+/[^/]\+/#\1/$version/#" /etc/opkg.conf
648 # Ersetze eine Zeile durch einen neuen Inhalt. Falls das Zeilenmuster nicht vorhanden ist, wird eine neue Zeile eingefuegt.
649 # Dies entspricht der Funktionalitaet des "lineinfile"-Moduls von ansible.
650 # Parameter filename: der Dateiname
651 # Parameter pattern: Suchmuster der zu ersetzenden Zeile
652 # Parameter new_line: neue Zeile
654 trap
"error_trap lineinfile '$*'" $GUARD_TRAPS
659 # Datei existiert nicht? Einfach mit dieser Zeile erzeugen.
660 [ ! -e
"$filename" ] && echo
"$new_line" >
"$filename" &&
return 0
661 # Datei einlesen - zum Muster passende Zeilen austauschen - notfalls neue Zeile anfuegen
664 echo
"$line" | grep -q
"$pattern" && echo
"$new_line" || echo
"$line"
666 # die neue Zeile hinzufuegen, falls das Muster in der alten Datei nicht vorhanden war
667 grep -q
"$pattern" "$filename" || echo
"$new_line"
672 ## @fn is_package_installed()
673 ## @brief Prüfe, ob ein opkg-Paket installiert ist.
674 ## @param package Name des Pakets
675 is_package_installed() {
677 opkg list-installed | grep -q
"^$package[\t ]" &&
return 0
678 trap
"" $GUARD_TRAPS &&
return 1
682 # Pruefe, ob eine Liste ein bestimmtes Element enthaelt
683 # Die Listenelemente sind durch beliebigen Whitespace getrennt.
688 for token in $list;
do
689 [
"$token" =
"$target" ] &&
return 0 ||
true
691 # kein passendes Token gefunden
692 trap
"" $GUARD_TRAPS &&
return 1
696 # Liefere den Inhalt einer Variable zurueck.
697 # Dies ist beispielsweise fuer lua-Skripte nuetzlich, da diese nicht den shell-Namensraum teilen.
698 # Paramter: Name der Variable
701 eval
"echo \"\$$var_name\""
705 # Pruefe, ob die angegebene Funktion definiert ist.
706 # Dies ersetzt opkg-basierte Pruefungen auf installierte opennet-Firmware-Pakete.
707 is_function_available() {
709 # "ash" liefert leider nicht den korrekten Wert "function" nach einem Aufruf von "type -t".
710 # Also verwenden wir die Textausgabe von "type".
711 # Die Fehlerausgabe von type wird ignoriert - im Falle der bash gibt es sonst unnoetige Ausgaben.
712 type
"$func_name" 2>/dev/
null | grep -q
"function$" &&
return 0
713 trap
"" $GUARD_TRAPS &&
return 1
717 ## @fn get_local_bias_numer()
718 ## @brief Ermittle eine lokale einzigartige Zahl, die als dauerhaft unveränderlich angenommen werden kann.
719 ## @returns Eine (initial zufällig ermittelte) Zahl zwischen 0 und 10^8-1, die unveränderlich zu diesem AP gehört.
720 ## @details Für ein paar gleichrangige Sortierungen (z.B. verwendete
721 ## UGW-Gegenstellen) benötigen wir ein lokales Salz, um strukturelle
722 ## Bevorzugungen zu vermeiden.
723 get_local_bias_number() {
724 trap
"error_trap get_local_bias_number '$*'" $GUARD_TRAPS
725 local bias=$(uci_get on-core.settings.local_bias_number)
726 # der Bias-Wert ist schon vorhanden - wir liefern ihn aus
727 if [ -z
"$bias" ]; then
728 # wir müssen einen Bias-Wert erzeugen: beliebige gehashte Inhalte ergeben eine akzeptable Zufallszahl
729 bias=$( (logread; dmesg; ps; date) | md5sum | tr
"abcdef" "012345" | cut -
c 1-8)
730 uci
set "on-core.settings.local_bias_number=$bias"
733 echo -n
"$bias" &&
return 0
737 ## @fn system_service_check()
738 ## @brief Prüfe ob ein Dienst läuft und ob seine PID-Datei aktuell ist.
739 ## @param executable Der vollständige Pfad zu dem auszuführenden Programm.
740 ## @param pid_file Der Name einer PID-Datei, die von diesem Prozess verwaltet wird.
741 ## @deteils Dabei wird die 'service_check'-Funktion aus der openwrt-Shell-Bibliothek genutzt.
742 system_service_check() {
743 local executable=
"$1"
745 . /lib/functions/service.sh
746 SERVICE_PID_FILE=
"$pid_file"
748 service_check
"$executable" &&
return 0
749 trap
"" $GUARD_TRAPS &&
return 1
753 ## @fn get_memory_size()
754 ## @brief Ermittle die Größe des Arbeitsspeichers in Megabyte.
755 ## @returns Der Rückgabewert (in Megabyte) ist etwas kleiner als der physische Arbeitsspeicher (z.B. 126 statt 128 MB).
757 local memsize_kb=$(grep
"^MemTotal:" /proc/meminfo | sed
's/[^0-9]//g')
758 echo $((memsize_kb / 1024))
763 trap
"error_trap run_parts '$*'" $GUARD_TRAPS
766 find
"$rundir" -maxdepth 1 |
while read fname;
do
767 # ignoriere verwaiste symlinks
768 [ ! -f
"$fname" ] &&
continue
769 msg_debug "on-run-parts: executing $fname"
770 # ignoriere Fehler bei der Ausfuehrung
775 # Ende der Doku-Gruppe