Opennet Firmware
 Alle Dateien Funktionen Variablen Gruppen Seiten
core.sh
gehe zur Dokumentation dieser Datei
1 ## @defgroup core Kern
2 ## @brief Logging, Datei-Operationen, DNS- und NTP-Dienste, Dictionary-Dateien, PID- und Lock-Behandlung, Berichte
3 # Beginn der Doku-Gruppe
4 ## @{
5 
6 
7 # Quelldatei für Standardwerte des Kern-Pakets
8 ON_CORE_DEFAULTS_FILE="${IPKG_INSTROOT:-}/usr/share/opennet/core.defaults"
9 # Pfad zur dnsmasq-Server-Datei zur dynamischen Aktualisierung durch Dienste-Erkennung
10 DNSMASQ_SERVERS_FILE_DEFAULT="${IPKG_INSTROOT:-}/var/run/dnsmasq.servers"
11 # DNS-Suffix, das vorrangig von den via olsrd publizierten Nameservern ausgeliefert werden soll
12 INTERN_DNS_DOMAIN=on
13 # Dateiname für erstellte Zusammenfassungen
14 REPORTS_FILE="${IPKG_INSTROOT:-}/tmp/on_report.tar.gz"
15 # Basis-Verzeichnis für Log-Dateien
16 LOG_BASE_DIR="${IPKG_INSTROOT:-}/var/log"
17 # maximum length of message lines (logger seems to resctrict lines incl. timestamp to 512 characters)
18 LOG_MESSAGE_LENGTH=420
19 # Verzeichnis für auszuführende Aktionen
20 SCHEDULING_DIR="${IPKG_INSTROOT:-}/var/run/on-scheduling.d"
21 # beim ersten Pruefen wird der Debug-Modus ermittelt
22 DEBUG_ENABLED=
23 
24 
25 # Aufteilung ueberlanger Zeilen
26 _split_lines() {
27  local line_length="$1"
28  # ersetze alle whitespace-Zeichen durch Nul
29  # Gib anschliessend soviele Token wie moeglich aus, bis die Zeilenlaenge erreicht ist.
30  tr '\n\t ' '\0' | xargs -0 -s "$line_length" echo
31 }
32 
33 
34 ## @fn msg_debug()
35 ## @param message Debug-Nachricht
36 ## @brief Debug-Meldungen ins syslog schreiben
37 ## @details Die Debug-Nachrichten landen im syslog (siehe ``logread``).
38 ## Falls das aktuelle Log-Level bei ``info`` oder niedriger liegt, wird keine Nachricht ausgegeben.
40  # bei der ersten Ausfuehrung dauerhaft speichern
41  [ -z "$DEBUG_ENABLED" ] && \
42  DEBUG_ENABLED=$(uci_is_true "$(uci_get on-core.settings.debug false)" && echo 1 || echo 0)
43  [ "$DEBUG_ENABLED" = "0" ] || echo "$1" | _split_lines "$LOG_MESSAGE_LENGTH" | logger -t "$(basename "$0")[$$]"
44 }
45 
46 
47 ## @fn msg_info()
48 ## @param message Log-Nachricht
49 ## @brief Informationen und Fehlermeldungen ins syslog schreiben
50 ## @details Die Nachrichten landen im syslog (siehe ``logread``).
51 ## Die info-Nachrichten werden immer ausgegeben, da es kein höheres Log-Level als "debug" gibt.
52 msg_info() {
53  echo "$1" | _split_lines "$LOG_MESSAGE_LENGTH" | logger -t "$(basename "$0")[$$]"
54 }
55 
56 
57 ## @fn msg_error()
58 ## @param message Fehlermeldung
59 ## @brief Die Fehlermeldungen werden in die Standard-Fehlerausgabe und ins syslog geschrieben
60 ## @details Jede Meldung wird mit "ERROR" versehen, damit diese Meldungen von
61 ## "get_potential_error_messages" erkannt werden.
62 ## Die error-Nachrichten werden immer ausgegeben, da es kein höheres Log-Level als "debug" gibt.
63 msg_error() {
64  echo "$1" | _split_lines "$LOG_MESSAGE_LENGTH" | logger -s -t "$(basename "$0")[$$]" "[ERROR] $1"
65 }
66 
67 
68 ## @fn append_to_custom_log()
69 ## @param log_name Name des Log-Ziels
70 ## @param event die Kategorie der Meldung (up/down/???)
71 ## @param msg die textuelle Beschreibung des Ereignis (z.B. "connection with ... closed")
72 ## @brief Hänge eine neue Nachricht an ein spezfisches Protokoll an.
73 ## @details Die Meldungen werden beispielsweise von den konfigurierten openvpn-up/down-Skripten gesendet.
75  local log_name="$1"
76  local event="$2"
77  local msg="$3"
78  local logfile=$(get_custom_log_filename "$log_name")
79  echo "$(date) openvpn [$event]: $msg" >>"$logfile"
80  # Datei kuerzen, falls sie zu gross sein sollte
81  local filesize
82  filesize=$(get_filesize "$logfile")
83  [ "$filesize" -gt 10000 ] && sed -i "1,30d" "$logfile"
84  return 0
85 }
86 
87 
88 ## @fn get_custom_log_filename()
89 ## @param log_name Name des Log-Ziels
90 ## @brief Liefere den Inhalt eines spezifischen Logs (z.B. das OpenVPN-Verbindungsprotokoll) zurück.
91 ## @returns Zeilenweise Ausgabe der Protokollereignisse (aufsteigend nach Zeitstempel sortiert).
93  local log_name="$1"
94  # der Aufrufer darf sich darauf verlassen, dass er in die Datei schreiben kann
95  mkdir -p "$LOG_BASE_DIR"
96  echo "$LOG_BASE_DIR/${log_name}.log"
97 }
98 
99 
100 ## @fn get_custom_log_content()
101 ## @param log_name Name des Log-Ziels
102 ## @brief Liefere den Inhalt eines spezifischen Logs (z.B. das OpenVPN-Verbindungsprotokoll) zurück.
103 ## @returns Zeilenweise Ausgabe der Protokollereignisse (aufsteigend nach Zeitstempel sortiert).
105  local log_name="$1"
106  local logfile=$(get_custom_log_filename "$log_name")
107  [ -e "$logfile" ] && cat "$logfile" || true
108 }
109 
110 
111 ## @fn update_file_if_changed()
112 ## @param target_filename Name der Zieldatei
113 ## @brief Aktualisiere eine Datei, falls sich ihr Inhalt geändert haben sollte.
114 ## @details Der neue Inhalt der Datei wird auf der Standardeingabe erwartet.
115 ## Im Falle der Gleichheit von aktuellem Inhalt und zukünftigem Inhalt wird
116 ## keine Schreiboperation ausgeführt. Der Exitcode gibt an, ob eine Schreiboperation
117 ## durchgeführt wurde.
118 ## @return exitcode=0 (Erfolg) falls die Datei geändert werden musste
119 ## @return exitcode=1 (Fehler) falls es keine Änderung gab
121  local target_filename="$1"
122  local content="$(cat -)"
123  if [ -e "$target_filename" ] && echo "$content" | cmp -s - "$target_filename"; then
124  # the content did not change
125  trap "" $GUARD_TRAPS && return 1
126  else
127  # updated content
128  local dirname
129  dirname=$(dirname "$target_filename")
130  [ -d "$dirname" ] || mkdir -p "$dirname"
131  echo "$content" > "$target_filename"
132  return 0
133  fi
134 }
135 
136 
137 ## @fn update_dns_servers()
138 ## @brief Übertrage die Liste der als DNS-Dienst announcierten Server in die dnsmasq-Konfiguration.
139 ## @details Die Liste der DNS-Server wird in die separate dnsmasq-Servers-Datei geschrieben (siehe @sa DNSMASQ_SERVERS_FILE_DEFAULT).
140 ## Die Server-Datei wird nur bei Änderungen neu geschrieben. Dasselbe gilt für den Neustart des Diensts.
141 ## Diese Funktion sollte via olsrd-nameservice-Trigger oder via cron-Job ausgeführt werden.
143  trap "error_trap update_dns_servers '$*'" $GUARD_TRAPS
144  local host
145  local port
146  local service
147  # wenn wir eine VPN-Tunnel-Verbindung aufgebaut haben, sollten wir DNS-Anfragen über diese Crypto-Verbindung lenken
148  local preferred_servers
149  local use_dns="$(uci_get on-core.settings.use_olsrd_dns)"
150  # return if we should not use DNS servers provided via olsrd
151  uci_is_false "$use_dns" && return 0
152  local servers_file
153  servers_file=$(uci_get "dhcp.@dnsmasq[0].serversfile")
154  # aktiviere die "dnsmasq-serversfile"-Direktive, falls noch nicht vorhanden
155  if [ -z "$servers_file" ]; then
156  servers_file="$DNSMASQ_SERVERS_FILE_DEFAULT"
157  uci set "dhcp.@dnsmasq[0].serversfile=$servers_file"
158  uci commit "dhcp.@dnsmasq[0]"
159  reload_config
160  fi
161  preferred_servers=$(is_function_available "get_mig_tunnel_servers" && get_mig_tunnel_servers "DNS" || true)
162  # wir sortieren alphabetisch - Naehe ist uns egal
163  (
164  get_services "dns" | filter_reachable_services | filter_enabled_services | sort | while read service; do
165  host=$(get_service_value "$service" "host")
166  port=$(get_service_value "$service" "port")
167  [ -n "$port" -a "$port" != "53" ] && host="$host#$port"
168  # Host nur schreiben, falls kein bevorzugter Host gefunden wurde
169  [ -z "$preferred_servers" ] && echo "server=$host"
170  # Die interne Domain soll vorranging von den via olsrd verbreiteten DNS-Servern bedient werden.
171  # Dies ist vor allem fuer UGW-Hosts wichtig, die über eine zweite DNS-Quelle (lokaler uplink)
172  # verfügen.
173  echo "server=/$INTERN_DNS_DOMAIN/$host"
174  done
175  # eventuell bevorzugte Hosts einfuegen
176  for host in $preferred_servers; do
177  echo "server=$host"
178  done
179  ) | update_file_if_changed "$servers_file" || return 0
180  # es gab eine Aenderung
181  msg_info "updating DNS servers"
182  # Konfiguration neu einlesen
183  killall -s HUP dnsmasq 2>/dev/null || true
184 }
185 
186 
187 ## @fn update_ntp_servers()
188 ## @brief Übertrage die Liste der als NTP-Dienst announcierten Server in die sysntpd-Konfiguration.
189 ## @details Die Liste der NTP-Server wird in die uci-Konfiguration geschrieben.
190 ## Die uci-Konfiguration wird nur bei Änderungen neu geschrieben. Dasselbe gilt für den Neustart des Diensts.
191 ## Diese Funktion sollte via olsrd-nameservice-Trigger oder via cron-Job ausgeführt werden.
192 ## @sa http://wiki.openwrt.org/doc/uci/system#remote_time_ntp
194  trap "error_trap update_ntp_servers '$*'" $GUARD_TRAPS
195  local host
196  local port
197  local service
198  local preferred_servers
199  local use_ntp="$(uci_get on-core.settings.use_olsrd_ntp)"
200  # return if we should not use NTP servers provided via olsrd
201  uci_is_false "$use_ntp" && return
202  preferred_servers=$(is_function_available "get_mig_tunnel_servers" && get_mig_tunnel_servers "NTP" || true)
203  # schreibe die Liste der NTP-Server neu
204  uci_delete system.ntp.server
205  # wir sortieren alphabetisch - Naehe ist uns egal
206  if [ -n "$preferred_servers" ]; then
207  for host in $preferred_servers; do
208  uci_add_list "system.ntp.server" "$host"
209  done
210  else
211  get_services "ntp" | filter_reachable_services | filter_enabled_services | sort | while read service; do
212  host=$(get_service_value "$service" "host")
213  port=$(get_service_value "$service" "port")
214  [ -n "$port" -a "$port" != "123" ] && host="$host:$port"
215  uci_add_list "system.ntp.server" "$host"
216  done
217  fi
218  apply_changes system
219 }
220 
221 
222 ## @fn add_banner_event()
223 ## @param event Ereignistext
224 ## @param timestamp [optional] Der Zeitstempel-Text kann bei Bedarf vorgegeben werden.
225 ## @brief Füge ein Ereignis zum dauerhaften Ereignisprotokoll (/etc/banner) hinzu.
226 ## @details Ein Zeitstempel, sowie hübsche Formatierung wird automatisch hinzugefügt.
228  trap "error_trap add_banner_event '$*'" $GUARD_TRAPS
229  local event="$1"
230  # verwende den optionalen zweiten Parameter oder den aktuellen Zeitstempel
231  local timestamp="${2:-$(date)}"
232  local line=" - $timestamp - $event -"
233  (
234  # Steht unser Text schon im Banner? Ansonsten hinzufuegen ...
235  # bis einschliesslich Version v0.5.0 war "clean_restart_log" das Schluesselwort
236  # ab v0.5.1 verwenden wir "system events"
237  if ! grep -qE '(clean_restart_log|system events)' /etc/banner; then
238  echo " ------------------- system events -------------------"
239  fi
240  # die Zeile auffuellen
241  while [ "${#line}" -lt 54 ]; do line="$line-"; done
242  echo "$line"
243  ) >>/etc/banner
244  sync
245 }
246 
247 
248 ## @fn clean_restart_log()
249 ## @brief Alle Log-Einträge aus der banner-Datei entfernen.
251  awk '{if ($1 != "-") print}' /etc/banner >/tmp/banner
252  mv /tmp/banner /etc/banner
253  sync
254 }
255 
256 
257 ## @fn _get_file_dict_value()
258 ## @param key das Schlüsselwort
259 ## @brief Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom
260 ## @returns Den zum gegebenen Schlüssel gehörenden Wert aus dem Schlüssel/Wert-Eingabestrom
261 ## Falls kein passender Schlüssel gefunden wurde, dann ist die Ausgabe leer.
262 ## @details Jede Zeile der Standardeingabe enthält einen Feldnamen und einen Wert - beide sind durch
263 ## ein beliebiges whitespace-Zeichen getrennt.
264 ## Dieses Dateiformat wird beispielsweise für die Dienst-Zustandsdaten verwendet.
265 ## Zusätzlich ist diese Funktion auch zum Parsen von openvpn-Konfigurationsdateien geeignet.
266 _get_file_dict_value() { local key="$1"; shift; { grep "^$key[[:space:]]" "$@" 2>/dev/null || true; } | while read key value; do echo -n "$value"; done; }
267 
268 
269 ## @fn _get_file_dict_keys()
270 ## @brief Liefere alle Schlüssel aus einem Schlüssel/Wert-Eingabestrom.
271 ## @returns Liste aller Schlüssel aus dem Schlüssel/Wert-Eingabestrom.
272 ## @sa _get_file_dict_value
273 _get_file_dict_keys() { sed 's/[ \t].*//' "$@" 2>/dev/null || true; }
274 
275 
276 ## @fn _set_file_dict_value()
277 ## @param field das Schlüsselwort
278 ## @param value der neue Wert
279 ## @brief Ersetzen oder Einfügen eines Werts in einen Schlüssel/Wert-Eingabestrom.
280 ## @sa _get_file_dict_value
281 _set_file_dict_value() {
282  local status_file="$1"
283  local field="$2"
284  local new_value="$3"
285  [ -z "$field" ] && msg_error "Ignoring empty key for _set_file_dict_value" && return
286  # Filtere bisherige Zeilen mit dem key heraus.
287  # Fuege anschliessend die Zeile mit dem neuen Wert an.
288  # Die Sortierung sorgt fuer gute Vergleichbarkeit, um die Anzahl der
289  # Schreibvorgaenge (=Wahrscheinlichkeit von gleichzeitigem Zugriff) zu reduzieren.
290  (
291  grep -v -w -s "$field" "$status_file"
292  echo "$field $new_value"
293  ) | sort | update_file_if_changed "$status_file" || true
294 }
295 
296 
297 ## @fn get_on_core_default()
298 ## @param key Name des Schlüssels
299 ## @brief Liefere einen der default-Werte der aktuellen Firmware zurück (Paket on-core).
300 ## @details Die default-Werte werden nicht von der Konfigurationsverwaltung uci verwaltet.
301 ## Somit sind nach jedem Upgrade imer die neuesten Standard-Werte verfügbar.
302 get_on_core_default() {
303  local key="$1"
304  _get_file_dict_value "$key" "$ON_CORE_DEFAULTS_FILE"
305 }
306 
307 
308 ## @fn get_on_firmware_version()
309 ## @brief Liefere die aktuelle Firmware-Version zurück.
310 ## @returns Die zurückgelieferte Zeichenkette beinhaltet den Versionsstring (z.B. "0.5.0").
311 ## @details Per Konvention entspricht die Version jedes Firmware-Pakets der Firmware-Version.
312 ## Um locking-Probleme zu vermeiden, lesen wir den Wert direkt aus der control-Datei des Pakets.
313 ## Das ist nicht schoen - aber leider ist die lock-Datei nicht konfigurierbar.
314 get_on_firmware_version() {
315  trap "error_trap get_on_firmware_version '$*'" $GUARD_TRAPS
316  local status_file="${IPKG_INSTROOT:-}/usr/lib/opkg/info/on-core.control"
317  [ -e "$status_file" ] || return 0
318  awk '{if (/^Version:/) print $2;}' <"$status_file"
319 }
320 
321 
322 ## @fn get_on_ip()
323 ## @param on_id die ID des AP - z.B. "1.96" oder "2.54"
324 ## @param on_ipschema siehe "get_on_core_default on_ipschema"
325 ## @param interface_number 0..X (das WLAN-Interface ist typischerweise Interface #0)
326 ## @attention Manche Aufrufende verlassen sich darauf, dass *on_id_1* und
327 ## *on_id_2* nach dem Aufruf verfügbar sind (also _nicht_ als "local"
328 ## Variablen deklariert wurden).
329 get_on_ip() {
330  local on_id="$1"
331  local on_ipschema="$2"
332  local interface_number="$3"
333  # das "on_ipschema" erwartet die Variable "no"
334  local no="$interface_number"
335  echo "$on_id" | grep -q "\." || on_id=1.$on_id
336  on_id_1=$(echo "$on_id" | cut -d . -f 1)
337  on_id_2=$(echo "$on_id" | cut -d . -f 2)
338  echo $(eval echo $on_ipschema)
339 }
340 
341 
342 ## @fn get_main_ip()
343 ## @brief Liefere die aktuell konfigurierte Main-IP zurück.
344 ## @returns Die aktuell konfigurierte Main-IP des AP oder die voreingestellte IP.
345 ## @attention Seiteneffekt: die Variablen "on_id_1" und "on_id_2" sind anschließend verfügbar.
346 ## @sa get_on_ip
347 get_main_ip() {
348  local on_id
349  local ipschema
350  on_id=$(uci_get on-core.settings.on_id "$(get_on_core_default on_id_preset)")
351  ipschema=$(get_on_core_default on_ipschema)
352  get_on_ip "$on_id" "$ipschema" 0
353 }
354 
355 
356 # Pruefe ob eine PID-Datei existiert und ob die enthaltene PID zu einem Prozess
357 # mit dem angegebenen Namen (nur Dateiname - ohne Pfad) verweist.
358 # Parameter PID-Datei: vollstaendiger Pfad
359 # Parameter Prozess-Name: Dateiname ohne Pfad
360 check_pid_file() {
361  trap "error_trap check_pid_file '$*'" $GUARD_TRAPS
362  local pid_file="$1"
363  local process_name="$2"
364  local pid
365  local current_process
366  [ -z "$pid_file" -o ! -e "$pid_file" ] && trap "" $GUARD_TRAPS && return 1
367  pid=$(cat "$pid_file" | sed 's/[^0-9]//g')
368  # leere/kaputte PID-Datei
369  [ -z "$pid" ] && trap "" $GUARD_TRAPS && return 1
370  # Prozess-Datei ist kein symbolischer Link?
371  [ ! -L "/proc/$pid/exe" ] && trap "" $GUARD_TRAPS && return 1
372  current_process=$(readlink "/proc/$pid/exe")
373  [ "$process_name" != "$(basename "$current_process")" ] && trap "" $GUARD_TRAPS && return 1
374  return 0
375 }
376 
377 
378 ## @fn apply_changes()
379 ## @param configs Einer oder mehrere uci-Sektionsnamen.
380 ## @brief Kombination von uci-commit und anschliessender Inkraftsetzung fuer verschiedene uci-Sektionen.
381 ## @details Dienst-, Netzwerk- und Firewall-Konfigurationen werden bei Bedarf angewandt.
382 ## Zuerst werden alle uci-Sektionen commited und anschliessend werden die Trigger ausgefuehrt.
383 apply_changes() {
384  local config
385  for config in "$@"; do
386  # Opennet-Module achten auch auf nicht-uci-Aenderungen
387  if echo "$config" | grep -q "^on-"; then
388  uci -q commit "$config" || true
389  echo "$config"
390  elif [ -z "$(uci changes "$config")" ]; then
391  # keine Aenderungen?
392  true
393  else
394  uci commit "$config"
395  echo "$config"
396  fi
397  done | grep -v "^$" | sort | uniq | while read config; do
398  # wir wollen die Aktionen erst nach allen commits ausfuehren
399  case "$config" in
400  system|dhcp)
401  reload_config || true
402  ;;
403  network|wireless|firewall)
404  reload_config || true
405  # Zonen- und IP-Aenderungen koennen Policy-Routing-Aenderungen erfordern
407  # eventuelle Zonen-Zuordnungen zu olsr uebertragen
408  update_olsr_interfaces
409  ;;
410  olsrd)
411  /etc/init.d/olsrd reload || true
412  ;;
413  openvpn)
414  # eventuell ist das openvpn-Paket nicht installiert (siehe on-migrations)
415  [ -e /etc/init.d/openvpn ] && /etc/init.d/openvpn reload || true
416  ;;
417  nodogsplash)
418  captive_portal_reload || true
419  ;;
420  on-core)
423  ;;
424  on-openvpn)
426  ;;
427  on-usergw)
429  update_service_relay_status
430  ;;
431  on-captive-portal)
432  update_captive_portal_status
433  apply_changes nodogsplash
434  ;;
435  on-monitoring)
436  update_monitoring_state
437  ;;
438  on-goodies)
439  # es gibt nichts zu tun
440  ;;
441  *)
442  msg_error "no handler defined for applying config changes for '$config'"
443  ;;
444  esac
445  done
446  return 0
447 }
448 
449 
450 # Setzen einer Opennet-ID.
451 # 1) Hostnamen setzen
452 # 2) IPs fuer alle Opennet-Interfaces setzen
453 # 3) Main-IP in der olsr-Konfiguration setzen
454 # 4) IP des Interface "free" setzen
455 set_opennet_id() {
456  trap "error_trap set_opennet_id '$*'" $GUARD_TRAPS
457  local new_id="$1"
458  local network
459  local uci_prefix
460  local ipaddr
461  local main_ipaddr
462  local free_ipaddr
463  local ipschema
464  local netmask
465  local if_counter=0
466  # ID normalisieren (AP7 -> AP1.7)
467  echo "$new_id" | grep -q "\." || new_id=1.$new_id
468  # ON_ID in on-core-Settings setzen
469  prepare_on_uci_settings
470  uci set "on-core.settings.on_id=$new_id"
471  apply_changes on-core
472  # Hostnamen konfigurieren
473  find_all_uci_sections system system | while read uci_prefix; do
474  uci set "${uci_prefix}.hostname=AP-$(echo "$new_id" | tr . -)"
475  done
476  apply_changes system
477  # IP-Adressen konfigurieren
478  ipschema=$(get_on_core_default on_ipschema)
479  netmask=$(get_on_core_default on_netmask)
480  main_ipaddr=$(get_on_ip "$new_id" "$ipschema" 0)
481  for network in $(get_sorted_opennet_interfaces); do
482  uci_prefix=network.$network
483  [ "$(uci_get "${uci_prefix}.proto")" != "static" ] && continue
484  ipaddr=$(get_on_ip "$new_id" "$ipschema" "$if_counter")
485  uci set "${uci_prefix}.ipaddr=$ipaddr"
486  uci set "${uci_prefix}.netmask=$netmask"
487  : $((if_counter++))
488  done
489  # OLSR-MainIP konfigurieren
490  olsr_set_main_ip "$main_ipaddr"
491  apply_changes olsrd network
492 }
493 
494 
495 # Durchsuche eine Schluessel-Wert-Liste nach einem Schluessel und liefere den dazugehoerigen Wert zurueck.
496 # Beispiel:
497 # foo=bar baz=nux
498 # Der Separator ist konfigurierbar.
499 # Die Liste wird auf der Standardeingabe erwartet.
500 # Der erste und einzige Parameter ist der gewuenschte Schluessel.
501 get_from_key_value_list() {
502  local search_key="$1"
503  local separator="$2"
504  local key_value
505  local key
506  { sed 's/[ \t]\+/\n/g'; echo; } | while read key_value; do
507  key=$(echo "$key_value" | cut -f 1 -d "$separator")
508  [ "$key" = "$search_key" ] && echo "$key_value" | cut -f 2- -d "$separator" && break || true
509  done
510  return 0
511 }
512 
513 
514 ## @fn replace_in_key_value_list()
515 ## @param search_key der Name des Schlüsselworts
516 ## @param separator der Name des Trennzeichens zwischen Wert und Schlüssel
517 ## @brief Ermittle aus einer mit Tabulatoren oder Leerzeichen getrennten Liste von Schlüssel-Wert-Paaren den Inhalt des Werts zu einem Schlüssel.
518 ## @returns die korrigierte Schlüssel-Wert-Liste wird ausgegeben (eventuell mit veränderten Leerzeichen oder Tabulatoren)
519 replace_in_key_value_list() {
520  local search_key="$1"
521  local separator="$2"
522  local value="$3"
523  local key_value
524  sed 's/[ \t]\+/\n/g' | while read key_value; do
525  key=$(echo "$key_value" | cut -f 1 -d "$separator")
526  if [ "$key" = "$search_key" ]; then
527  # nicht ausgeben, falls der Wert leer ist
528  [ -n "$value" ] && echo -n " ${key_value}${separator}${value}" || true
529  else
530  echo -n " $key_value"
531  fi
532  done | sed 's/^ //'
533  return 0
534 }
535 
536 
537 # Wandle einen uebergebenene Parameter in eine Zeichenkette um, die sicher als Dateiname verwendet werden kann
538 get_safe_filename() {
539  echo "$1" | sed 's/[^a-zA-Z0-9._\-]/_/g'
540 }
541 
542 
543 ## @fn get_uptime_minutes()
544 ## @brief Ermittle die seit dem Systemstart vergangene Zeit in Minuten
545 ## @details Diese Zeit ist naturgemäß nicht für die Speicherung an Orten geeignet, die einen reboot überleben.
546 get_uptime_minutes() {
547  awk '{print int($1/60)}' /proc/uptime
548 }
549 
550 
551 get_file_modification_timestamp_minutes() {
552  local filename="$1"
553  date --reference "$filename" +%s | awk '{ print int($1/60) }'
554 }
555 
556 
557 ## @fn is_timestamp_older_minutes()
558 ## @param timestamp_minute der zu prüfende Zeitstempel (in Minuten seit dem Systemstart)
559 ## @param difference zulässige Zeitdifferenz zwischen jetzt und dem Zeitstempel
560 ## @brief Prüfe, ob ein gegebener Zeitstempel älter ist, als die vorgegebene Zeitdifferenz.
561 ## @returns Exitcode Null (Erfolg), falls der gegebene Zeitstempel mindestens 'difference' Minuten zurückliegt.
562 # Achtung: Zeitstempel aus der Zukunft oder leere Zeitstempel gelten immer als veraltet.
563 is_timestamp_older_minutes() {
564  local timestamp_minute="$1"
565  local difference="$2"
566  [ -z "$timestamp_minute" ] && return 0
567  local now
568  now="$(get_uptime_minutes)"
569  # it is older
570  [ "$now" -ge "$((timestamp_minute + difference))" ] && return 0
571  # timestamp in future -> invalid -> let's claim it is too old
572  [ "$now" -lt "$timestamp_minute" ] && \
573  msg_info "WARNING: Timestamp from future found: $timestamp_minute (minutes since epoch)" && \
574  return 0
575  trap "" $GUARD_TRAPS && return 1
576 }
577 
578 
579 ## @fn get_uptime_seconds()
580 ## @brief Ermittle die Anzahl der Sekunden seit dem letzten Bootvorgang.
581 get_uptime_seconds() {
582  cut -f 1 -d . /proc/uptime
583 }
584 
585 
586 ## @fn run_delayed_in_background()
587 ## @param delay Verzögerung in Sekunden
588 ## @param command alle weiteren Token werden als Kommando und Parameter interpretiert und mit Verzögerung ausgeführt.
589 ## @brief Führe eine Aktion verzögert im Hintergrund aus.
590 run_delayed_in_background() {
591  local delay="$1"
592  shift
593  (sleep "$delay" && "$@") </dev/null >/dev/null 2>&1 &
594 }
595 
596 
597 ## @fn get_filesize()
598 ## @brief Ermittle die Größe einer Datei in Bytes.
599 ## @params filename Name der zu untersuchenden Datei.
600 get_filesize() {
601  local filename="$1"
602  wc -c "$filename" | awk '{ print $1 }'
603 }
604 
605 
606 # Bericht erzeugen
607 # Der Name der erzeugten tar-Datei wird als Ergebnis ausgegeben.
608 generate_report() {
609  trap "error_trap generate_report '$*'" $GUARD_TRAPS
610  local fname
611  local pid
612  local reports_dir
613  local temp_dir
614  local tar_file
615  temp_dir=$(mktemp -d)
616  reports_dir="$temp_dir/report"
617  tar_file=$(mktemp)
618  msg_debug "Creating a report"
619  # die Skripte duerfen davon ausgehen, dass wir uns im Zielverzeichnis befinden
620  mkdir -p "$reports_dir"
621  cd "$reports_dir"
622  find /usr/lib/opennet/reports -type f | while read fname; do
623  [ ! -x "$fname" ] && msg_info "skipping non-executable report script: $fname" && continue
624  "$fname" || msg_error "reports script failed: $fname"
625  done
626  cd "$temp_dir"
627  tar czf "$tar_file" "report"
628  rm -r "$temp_dir"
629  mv "$tar_file" "$REPORTS_FILE"
630 }
631 
632 
633 ## @fn get_potential_error_messages()
634 ## @param max_lines die Angabe einer maximalen Anzahl von Zeilen ist optional - andernfalls werden alle Meldungen ausgegeben
635 ## @brief Filtere aus allen zugänglichen Quellen mögliche Fehlermeldungen.
636 ## @details Falls diese Funktion ein nicht-leeres Ergebnis zurückliefert, kann dies als Hinweis für den
637 ## Nutzer verwendet werden, auf dass er einen Fehlerbericht einreicht.
638 get_potential_error_messages() {
639  local max_lines=${1:-}
640  local filters=
641  # 1) get_service_as_csv
642  # Wir ignorieren "get_service_as_csv"-Meldungen - diese werden durch asynchrone Anfragen des
643  # Web-Interface ausgeloest, die beim vorzeitigen Abbruch des Seiten-Lade-Vorgangs mit
644  # einem Fehler enden.
645  filters="${filters}|trapped.*get_service_as_csv"
646  # 2) openvpn.*Error opening configuration file
647  # Beim Booten des Systems wurde die openvpn-Config-Datei, die via uci referenziert ist, noch
648  # nicht erzeugt. Beim naechsten cron-Lauf wird dieses Problem behoben.
649  filters="${filters}|openvpn.*Error opening configuration file"
650  # 3) openvpn(...)[...]: Exiting due to fatal error
651  # Das Verzeichnis /var/etc/openvpn/ existiert beim Booten noch nicht.
652  filters="${filters}|openvpn.*Exiting due to fatal error"
653  # 4) openvpn(...)[...]: SIGUSR1[soft,tls-error] received, process restarting
654  # Diese Meldung taucht bei einem Verbindungsabbruch auf. Dieses Ereignis ist nicht
655  # ungewoehnlich und wird mittels des Verbindungsprotokolls bereits hinreichend gewuerdigt
656  filters="${filters}|openvpn.*soft,tls-error"
657  # 5) openvpn(...)[...]: TLS Error: TLS handshake failed
658  # Diese Meldung deutet einen fehlgeschlagenen Verbindungsversuch an. Dies ist nicht
659  # ungewoehnlich (beispielsweise auch fuer Verbindungstests).
660  filters="${filters}|openvpn.*TLS Error"
661  # 6) olsrd: /etc/rc.d/S65olsrd: startup-error: check via: '/usr/sbin/olsrd -f "/var/etc/olsrd.conf" -nofork'
662  # Falls noch kein Interface vorhanden ist (z.B. als wifi-Client), dann taucht diese Meldung
663  # beim Booten auf.
664  filters="${filters}|olsrd.*startup-error"
665  # 7) ucarp
666  # Beim Booten tauchen Fehlermeldungen aufgrund nicht konfigurierter Netzwerk-Interfaces auf.
667  # TODO: ucarp nur noch als nachinstallierbares Paket markieren (erfordert Aenderung der Makefile-Erzeugung)
668  filters="${filters}|ucarp"
669  # 8) olsrd: /etc/rc.d/S65olsrd: ERROR: there is already an IPv4 instance of olsrd running (pid: '1099'), not starting.
670  # Dieser Fehler tritt auf, wenn der olsrd_check einen olsrd-Neustart ausloest, obwohl er schon laeuft.
671  filters="${filters}|olsrd: ERROR: there is already an IPv4 instance of olsrd running"
672  # 9) openvpn(...)[...]: Authenticate/Decrypt packet error
673  # Paketverschiebungen nach dem Verbindungsaufbau - anscheinend unproblematisch.
674  filters="${filters}|openvpn.*Authenticate/Decrypt packet error"
675  # 10) olsrd: ... olsrd_setup_smartgw_rules() Warning: kmod-ipip is missing.
676  # olsrd gibt beim Starten generell diese Warnung aus. Wir koennen sie ignorieren.
677  filters="${filters}|olsrd.*olsrd_setup_smartgw_rules"
678  # 11) olsrd: ... olsrd_write_interface() Warning: Interface '...' not found, skipped
679  # Falls das wlan-Interface beim Bootvorgang noch nicht aktiv ist, wenn olsrd startet, dann erscheint diese
680  # harmlose Meldung.
681  filters="${filters}|olsrd.*Interface.*not found"
682  # 12) dropbear[...]: Exit (root): Error reading: Connection reset by peer
683  # Verbindungsverlust einer ssh-Verbindung. Dies darf passieren.
684  filters="${filters}|dropbear.*Connection reset by peer"
685  # 13) cron-error: nc.*: short write
686  # Falls die Routen via nc während eines olsrd-Neustarts ausgelesen werden, reisst eventuell die Socket-
687  # Verbindung ab - dies ist akzeptabel.
688  filters="${filters}|nc: short write"
689  # 14) openvpn(___service_name___)[...]: write UDPv4: Network is unreachable
690  # Beispielsweise bei einem olsrd-Neustart reisst die Verbindung zum UGW-Server kurz ab.
691  filters="${filters}|openvpn.*Network is unreachable"
692  # 15) wget: can't connect to remote host
693  # Eine frühe Geschwindigkeitsmessung (kurz nach dem Booten) darf fehlschlagen.
694  filters="${filters}|wget: can.t connect to remote host"
695  # 16) openvpn(...)[...]: Options error: Unrecognized option or missing parameter(s) in [PUSH-OPTIONS]:11: explicit-exit-notify (2.3.6)
696  # OpenVPN-Versionen, die ohne die "--extras"-Option gebaut wurden, unterstuetzen keine exit-Notification.
697  # Dies ist unproblematisch - es ist eher eine Sache der Höflichkeit..
698  filters="${filters}|openvpn.*Options error.*explicit-exit-notify"
699  # 17) ddns-scripts[...]: myddns_ipv4: ...
700  # ddns meldet leidet beim Starten einen Fehler, solange es unkonfiguriert ist.
701  filters="${filters}|ddns-scripts.*myddns_ipv[46]"
702  # 18) Collected errors:
703  # opkg-Paketinstallationen via Web-Interface erzeugen gelegentlich Fehlermeldungen (z.B. Entfernung
704  # abhängiger Pakete), die dem Nutzer im Web-Interface angezeigt werden. Diese Fehlermeldungen landen
705  # zusätzlich auch im log-Buffer. Da der Nutzer sie bereits gesehen haben dürfte, können wir sie ignorieren
706  # (zumal die konkreten Fehlermeldungen erst in den folgenden Zeilen zu finden und somit schlecht zu filtern
707  # sind).
708  filters="${filters}|Collected errors:"
709  # 19) uhttpd[...]: sh: write error: Broken pipe
710  # http-Requests die von seiten des Browser abgebrochen wurden
711  filters="${filters}|uhttpd.*: sh: write error: Broken pipe"
712  # 20) __main__ get_variable ...
713  # Der obige "Broken pipe"-Fehler unterbricht dabei auch die akuell laufende Funktion - dies ist
714  # sehr häufig die Variablen-Auslesung (seltsamerweise).
715  filters="${filters}|__main__ get_variable "
716  # System-Fehlermeldungen (inkl. "trapped")
717  logread | grep -i error | grep -vE "(${filters#|})" | if [ -z "$max_lines" ]; then
718  # alle Einträge ausgeben
719  cat -
720  else
721  # nur die letzten Einträge ausliefern
722  tail -n "$max_lines"
723  fi
724 }
725 
726 
727 # Ersetze eine Zeile durch einen neuen Inhalt. Falls das Zeilenmuster nicht vorhanden ist, wird eine neue Zeile eingefuegt.
728 # Dies entspricht der Funktionalitaet des "lineinfile"-Moduls von ansible.
729 # Parameter filename: der Dateiname
730 # Parameter pattern: Suchmuster der zu ersetzenden Zeile
731 # Parameter new_line: neue Zeile
732 line_in_file() {
733  trap "error_trap line_in_file '$*'" $GUARD_TRAPS
734  local filename="$1"
735  local pattern="$2"
736  local new_line="$3"
737  local line
738  # Datei existiert nicht? Einfach mit dieser Zeile erzeugen.
739  [ ! -e "$filename" ] && echo "$new_line" >"$filename" && return 0
740  # Datei einlesen - zum Muster passende Zeilen austauschen - notfalls neue Zeile anfuegen
741  (
742  while read line; do
743  echo "$line" | grep -q "$pattern" && echo "$new_line" || echo "$line"
744  done <"$filename"
745  # die neue Zeile hinzufuegen, falls das Muster in der alten Datei nicht vorhanden war
746  grep -q "$pattern" "$filename" || echo "$new_line"
747  ) | update_file_if_changed "$filename" || true
748 }
749 
750 
751 # Pruefe, ob eine Liste ein bestimmtes Element enthaelt
752 # Die Listenelemente sind durch beliebigen Whitespace getrennt.
753 is_in_list() {
754  local target="$1"
755  local list="$2"
756  local token
757  for token in $list; do
758  [ "$token" = "$target" ] && return 0 || true
759  done
760  # kein passendes Token gefunden
761  trap "" $GUARD_TRAPS && return 1
762 }
763 
764 
765 # Liefere den Inhalt einer Variable zurueck.
766 # Dies ist beispielsweise fuer lua-Skripte nuetzlich, da diese nicht den shell-Namensraum teilen.
767 # Paramter: Name der Variable
768 get_variable() {
769  local var_name="$1"
770  eval "echo \"\$$var_name\""
771 }
772 
773 
774 # Pruefe, ob die angegebene Funktion definiert ist.
775 # Dies ersetzt opkg-basierte Pruefungen auf installierte opennet-Firmware-Pakete.
776 is_function_available() {
777  local func_name="$1"
778  # "ash" liefert leider nicht den korrekten Wert "function" nach einem Aufruf von "type -t".
779  # Also verwenden wir die Textausgabe von "type".
780  # Die Fehlerausgabe von type wird ignoriert - im Falle der bash gibt es sonst unnoetige Ausgaben.
781  type "$func_name" 2>/dev/null | grep -q "function$" && return 0
782  trap "" $GUARD_TRAPS && return 1
783 }
784 
785 
786 ## @fn get_random()
787 ## @brief Liefere eine Zufallszahl innerhalb des gegebenen Bereichs.
788 ## @returns Eine zufällige Ganzzahl.
789 get_random() {
790  local range="$1"
791  echo "$range" | awk '{srand(systime()); print int(rand() * $1); }'
792 }
793 
794 
795 ## @fn get_local_bias_numer()
796 ## @brief Ermittle eine lokale einzigartige Zahl, die als dauerhaft unveränderlich angenommen werden kann.
797 ## @returns Eine (initial zufällig ermittelte) Zahl zwischen 0 und 10^8-1, die unveränderlich zu diesem AP gehört.
798 ## @details Für ein paar gleichrangige Sortierungen (z.B. verwendete
799 ## UGW-Gegenstellen) benötigen wir ein lokales Salz, um strukturelle
800 ## Bevorzugungen zu vermeiden.
801 get_local_bias_number() {
802  trap "error_trap get_local_bias_number '$*'" $GUARD_TRAPS
803  local bias
804  bias=$(uci_get on-core.settings.local_bias_number)
805  # der Bias-Wert ist schon vorhanden - wir liefern ihn aus
806  if [ -z "$bias" ]; then
807  # wir müssen einen Bias-Wert erzeugen: beliebige gehashte Inhalte ergeben eine akzeptable Zufallszahl
808  bias=$(get_random 100000000)
809  uci set "on-core.settings.local_bias_number=$bias"
810  uci commit on-core
811  fi
812  echo -n "$bias" && return 0
813 }
814 
815 
816 ## @fn system_service_check()
817 ## @brief Prüfe ob ein Dienst läuft und ob seine PID-Datei aktuell ist.
818 ## @param executable Der vollständige Pfad zu dem auszuführenden Programm.
819 ## @param pid_file Der Name einer PID-Datei, die von diesem Prozess verwaltet wird.
820 ## @deteils Dabei wird die 'service_check'-Funktion aus der openwrt-Shell-Bibliothek genutzt.
821 system_service_check() {
822  local executable="$1"
823  local pid_file="$2"
824  local result=$(set +eu; . /lib/functions/service.sh; SERVICE_PID_FILE="$pid_file"; service_check "$executable" && echo "ok"; set -eu)
825  [ -n "$result" ] && return 0
826  trap "" $GUARD_TRAPS && return 1
827 }
828 
829 
830 ## @fn get_memory_size()
831 ## @brief Ermittle die Größe des Arbeitsspeichers in Megabyte.
832 ## @returns Der Rückgabewert (in Megabyte) ist etwas kleiner als der physische Arbeitsspeicher (z.B. 126 statt 128 MB).
833 get_memory_size() {
834  local memsize_kb
835  memsize_kb=$(grep "^MemTotal:" /proc/meminfo | sed 's/[^0-9]//g')
836  echo $((memsize_kb / 1024))
837 }
838 
839 
840 # Liefere alle Dateien in einem Verzeichnis zurück, die entsprechend der "run-parts"-Funktionalität
841 # beachtet werden sollten.
842 _get_parts_dir_files() {
843  local parts_dir="$1"
844  local fname
845  # Abbruch, falls es das Verzeichnis nicht gibt
846  [ -e "$parts_dir" ] || return 0
847  # ignoriere Dateinamen mit ungueltigen Zeichen (siehe 'man run-parts')
848  find "$parts_dir" -maxdepth 1 | grep "/[a-zA-Z0-9_-]\+$" | while read fname; do
849  # ignoriere verwaiste symlinks
850  [ -f "$fname" ] || continue
851  # ignoriere Dateien ohne Ausführungsrechte
852  [ -x "$fname" ] || continue
853  echo "$fname"
854  done
855 }
856 
857 
858 ## @fn run_parts()
859 ## @brief Führe alle Skripte aus, die in einem bestimmten Verzeichnis liegen und gewissen Konventionen genügen.
860 ## @details Die Namenskonventionen und das Verhalten entspricht dem verbreiteten 'run-parts'-Werkzeug.
861 ## Die Dateien müssen ausführbar sein.
862 run_parts() {
863  trap "error_trap run_parts '$*'" $GUARD_TRAPS
864  local rundir="$1"
865  local fname
866  _get_parts_dir_files "$rundir" | while read fname; do
867  msg_debug "on-run-parts: executing $fname"
868  # ignoriere Fehler bei der Ausfuehrung
869  "$fname" || true
870  done
871 }
872 
873 
874 ## @fn run_scheduled_tasks()
875 ## @brief Führe die zwischenzeitlich für die spätere Ausführung vorgemerkten Aufgaben aus.
876 ## @details Unabhängig vom Ausführungsergebnis wird das Skript anschließend gelöscht.
877 run_scheduled_tasks() {
878  trap "error_trap run_scheduled_tasks '$*'" $GUARD_TRAPS
879  local fname
880  local temp_fname
881  [ -d "$SCHEDULING_DIR" ] || return 0
882  find "$SCHEDULING_DIR" -type f | grep -v "\.running$" | while read fname; do
883  temp_fname="${fname}.running"
884  # zuerst schnell wegbewegen, damit wir keine Ereignisse verpassen
885  # Im Fehlerfall (eine race condition) einfach beim naechsten Eintrag weitermachen.
886  mv "$fname" "$temp_fname" 2>/dev/null || continue
887  (/bin/sh "$temp_fname" | logger -t "on-scheduled") 2>&1 | logger -t "on-scheduled-error"
888  rm -f "$temp_fname"
889  done
890 }
891 
892 
893 ## @fn schedule_task()
894 ## @brief Erzeuge ein Start-Skript für die baldige Ausführung einer Aktion.
895 ## @details Diese Methode sollte für Aufgaben verwendet werden, die nicht unmittelbar ausgeführt
896 ## werden müssen und im Zweifelsfall nicht parallel ablaufen sollen (ressourcenschonend).
897 schedule_task() {
898  trap "error_trap schedule_task '$*'" $GUARD_TRAPS
899  local script_content
900  local unique_key
901  script_content=$(cat -)
902  # wir sorgen fuer die Wiederverwendung des Dateinamens, um doppelte Ausführungen zu verhindern
903  unique_key=$(echo "$script_content" | md5sum | awk '{ print $1 }')
904  mkdir -p "$SCHEDULING_DIR"
905  local target_file="$SCHEDULING_DIR/$unique_key"
906  # das Skript existiert? Nichts zu tun ...
907  [ -e "$target_file" ] && return 0
908  echo "$script_content" >"$target_file"
909 }
910 
911 
912 ## @fn schedule_parts()
913 ## @brief Merke alle Skripte in einem Verzeichnis für die spätere Ausführung via 'run_scheduled_tasks' vor.
914 ## @details Die Namenskonventionen und das Verhalten entspricht dem verbreiteten 'run-parts'-Werkzeug.
915 ## Die Dateien müssen ausführbar sein.
916 schedule_parts() {
917  trap "error_trap schedule_parts '$*'" $GUARD_TRAPS
918  local schedule_dir="$1"
919  local fname
920  _get_parts_dir_files "$schedule_dir" | while read fname; do
921  msg_debug "on-schedule-parts: scheduling $fname"
922  # ignoriere Fehler bei der Ausfuehrung
923  echo "$fname" | schedule_task
924  done
925 }
926 
927 
928 ## @fn read_data_bytes()
929 ## @brief Bytes von einem Blockdevice lesen
930 ## @param source das Quell-Blockdevice (oder die Datei)
931 ## @param size die Anzahl der zu uebertragenden Bytes
932 ## @param transfer_blocksize die Blockgroesse bei der Uebertragung (Standard: 65536)
933 ## @details Die verwendete Uebertragung in grossen Bloecken ist wesentlich schneller als das byteweise EinlesenaKopie.sh_backup
934 ## Der abschliessende unvollstaendige Block wird byteweise eingelesen.
935 read_data_bytes() {
936  local size="$1"
937  local transfer_blocksize="${2:-65536}"
938  # "conv=sync" ist fuer die "yes"-Quelle erforderlich - sonst fehlt gelegentlich der letzte Block.
939  # Es scheint sich dazu bei um eine race-condition zu handeln.
940  dd "bs=$transfer_blocksize" "count=$((size / transfer_blocksize))" conv=sync 2>/dev/null
941  [ "$((size % transfer_blocksize))" -ne 0 ] && dd bs=1 "count=$((size % transfer_blocksize))" 2>/dev/null
942  true
943 }
944 
945 
946 ## @fn get_flash_backup()
947 ## @brief Erzeuge einen rohen Dump des Flash-Speichers. Dieser ermöglicht den Austausch des Flash-Speichers.
948 ## @param include_private Kopiere neben den nur-Lese-Bereichen auch die aktuelle Konfiguration inkl. eventueller privater Daten.
949 ## @details Alle mtd-Partition bis auf den Kernel und die Firmware werden einzeln kopiert und dann komprimiert.
950 ## Beispiel-Layout einer Ubiquiti Nanostation:
951 ## dev: size erasesize name
952 ## mtd0: 00040000 00010000 "u-boot"
953 ## mtd1: 00010000 00010000 "u-boot-env"
954 ## mtd2: 00760000 00010000 "firmware"
955 ## mtd3: 00102625 00010000 "kernel"
956 ## mtd4: 0065d9db 00010000 "rootfs"
957 ## mtd5: 00230000 00010000 "rootfs_data"
958 ## mtd6: 00040000 00010000 "cfg"
959 ## mtd7: 00010000 00010000 "EEPROM"
960 ## Dabei ignorieren wir bei Bedarf "rootfs_data" (beschreibbarer Bereich der Firmware).
961 get_flash_backup() {
962  trap "error_trap get_flash_backup '$*'" $GUARD_TRAPS
963  local include_private="${1:-}"
964  local name
965  local size
966  local blocksize
967  local label
968  grep "^mtd[0-9]\+:" /proc/mtd | while read name size blocksize label; do
969  # abschliessenden Doppelpunkt entfernen
970  name="${name%:}"
971  # hexadezimal-Zahl umrechnen
972  size=$(echo | awk "{print 0x$size }")
973  # Anfuehrungszeichen entfernen
974  label=$(echo "$label" | cut -f 2 -d '"')
975  # Firmware-Partitionen ueberspringen
976  if [ "$label" = "rootfs" ]; then
977  local rootfs_device="/dev/$name"
978  local rootfs_full_size="$size"
979  elif [ "$label" = "rootfs_data" ]; then
980  # schreibe das komplette rootfs _ohne_ das aktuelle rootfs_data
981  echo >&2 "Read: root-RO $((rootfs_full_size - size))"
982  # Transfer blockweise vornehmen - byteweise dauert es zu lang
983  read_data_bytes "($((rootfs_full_size - size)))" <"$rootfs_device"
984  if [ -z "$include_private" ]; then
985  echo >&2 "Read: root-zero ($size)"
986  # erzeuge 0xFF auf obskure Weise (fuer maximale Flash-Schreibgeschwindigkeit)
987  # siehe http://stackoverflow.com/a/10905109
988  yes $'\xFF' | tr -d '\n' | read_data_bytes "$size"
989  #yes $'\xFF' | tr -d '\n' | dd bs=1 "count=$size"
990  #local count=0
991  #while [ "$count" -lt "$size" ]; do
992  #echo -n $'\xFF'
993  #: $((count++))
994  #done
995  else
996  echo >&2 "Read: root-RW ($size)"
997  # auch das private rootfs-Dateisystem (inkl. Schluessel, Passworte, usw.) auslesen
998  read_data_bytes "$size" <"/dev/$name"
999  fi
1000  elif [ "$label" = "firmware" ]; then
1001  echo >&2 "Skip: $label ($size)"
1002  # ignoriere die meta-Partition (kernel + rootfs)
1003  true
1004  else
1005  echo >&2 "Read: $label ($size)"
1006  cat "/dev/$name"
1007  fi
1008  done
1009 }
1010 
1011 
1012 ## @fn has_flash_or_filesystem_error_indicators()
1013 ## @brief Prüfe ob typische Indikatoren (vor allem im Kernel-Log) vorliegen, die auf einen Flash-Defekt hinweisen.
1014 has_flash_or_filesystem_error_indicators() {
1015  trap "error_trap get_flash_backup '$*'" $GUARD_TRAPS
1016  dmesg | grep -q "jffs2.*CRC" && return 0
1017  dmesg | grep -q "SQUASHFS error" && return 0
1018  # keine Hinweise gefunden -> wir liefern "nein"
1019  trap "" $GUARD_TRAPS && return 1
1020 }
1021 
1022 # Ende der Doku-Gruppe
1023 ## @}
uci_delete(uci_path)
Lösche ein UCI-Element.
Definition: uci.sh:38
get_services(service_type)
Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind. Falls kein Typ angegben wird...
Definition: services.sh:61
add_banner_event(event, timestamp)
Füge ein Ereignis zum dauerhaften Ereignisprotokoll (/etc/banner) hinzu.
Definition: core.sh:68
update_file_if_changed(target_filename)
Aktualisiere eine Datei, falls sich ihr Inhalt geändert haben sollte.
Definition: core.sh:49
append_to_custom_log(log_name, event)
Hänge eine neue Nachricht an ein spezfisches Protokoll an.
Definition: core.sh:29
get_custom_log_filename(log_name)
Liefere den Inhalt eines spezifischen Logs (z.B. das OpenVPN-Verbindungsprotokoll) zurück...
Definition: core.sh:34
update_ntp_servers()
Übertrage die Liste der als NTP-Dienst announcierten Server in die sysntpd-Konfiguration.
Definition: core.sh:62
while read key value
Definition: core.sh:81
update_dns_servers()
Übertrage die Liste der als DNS-Dienst announcierten Server in die dnsmasq-Konfiguration.
Definition: core.sh:55
filter_reachable_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
Definition: services.sh:38
filter_enabled_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden...
Definition: services.sh:43
uci_add_list(uci_path, new_item)
Füge einen neuen Wert zu einer UCI-Liste hinzu und achte dabei auf Einmaligkeit.
Definition: uci.sh:10
update_on_usergw_status()
Baue Verbindungen auf oder trenne sie - je nach Modul-Status.
Definition: on-usergw.sh:68
clean_restart_log()
Alle Log-Einträge aus der banner-Datei entfernen.
Definition: core.sh:71
local key
Definition: core.sh:81
_get_file_dict_value(key)
Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom.
Definition: core.sh:81
get_mig_tunnel_servers()
Ermittle die Server für den gewünschen Dienst, die via Tunnel erreichbar sind. stype Dienst-Typ (z...
Definition: on-openvpn.sh:76
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
Definition: core.sh:15
get_custom_log_content(log_name)
Liefere den Inhalt eines spezifischen Logs (z.B. das OpenVPN-Verbindungsprotokoll) zurück...
Definition: core.sh:39
captive_portal_reload()
Neukonfiguration der Captive-Portal-Software, falls Änderungen aufgetreten sind.
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
Definition: core.sh:9
set eu on function print_services services log for dir in etc on services d var on services volatile d
Definition: services:13
get_service_value(key, default)
Auslesen eines Werts aus der Service-Datenbank.
Definition: services.sh:74
msg_error(message)
Die Fehlermeldungen werden in die Standard-Fehlerausgabe und ins syslog geschrieben.
Definition: core.sh:22
shift
Definition: core.sh:81
set eu grep root::etc shadow exit if which chpasswd dev null
Definition: on-password:12
initialize_olsrd_policy_routing()
Policy-Routing-Initialisierung nach dem System-Boot und nach Interface-Hotplug-Ereignissen.
Definition: routing.sh:43
done
Definition: core.sh:81
update_mig_connection_status()
Je nach Status des Moduls: prüfe die VPN-Verbindungen bis mindestens eine Verbindung aufgebaut wurde ...
Definition: on-openvpn.sh:68