1## @defgroup services Dienste
2## @brief Verwaltung von Diensten (z.B. via olsrd-nameservice announciert)
3# Beginn der Doku-Gruppe
6VOLATILE_SERVICE_STATUS_DIR=/tmp/on-services-
volatile.d
7PERSISTENT_SERVICE_STATUS_DIR=/etc/on-services.d
8# eine grosse Zahl sorgt dafuer, dass neu entdeckte Dienste hinten angehaengt werden
9DEFAULT_SERVICE_RANK=10000
10DEFAULT_SERVICE_SORTING=etx
11# Die folgenden Attribute werden dauerhaft (im Flash) gespeichert. Häufige Änderungen sind also eher unerwünscht.
12# Gruende fuer ausgefallene/unintuitive Attribute:
13# uci_dependency: später zu beräumende uci-Einträge wollen wir uns merken
14# file_dependency: siehe uci_dependency
15# priority: DNS-entdeckte Dienste enthalten ein "priority"-Attribut, nach einem reboot wieder verfügbar sein sollte
16# rank/offset: Attribute zur Ermittlung der Dienstreihenfolge
17# disabled: der Dienst wurde vom Nutzenden an- oder abgewählt
18# source: die Quelle des Diensts (olsrd/dns/manual) muss erhalten bleiben, um ihn später löschen zu können
19# Wir beachten den vorherigen Zustand der Variable, damit andere Module (z.B. on-usergw) diese
20# ebenfalls beeinflussen können.
21PERSISTENT_SERVICE_ATTRIBUTES=
"${PERSISTENT_SERVICE_ATTRIBUTES:-} service scheme host port protocol path uci_dependency file_dependency priority rank offset disabled source"
23SERVICES_LOG_BASE=/var/log/on-services
24# Namenspräfix für weiterzuleitende Dienste
25RELAYABLE_SERVICE_PREFIX=
"proxy-"
26UPDATE_TRUSTED_SERVICES_PERIOD_MINUTES=360
27USER_SERVICES_URL=https:
29# andere Module fügen eventuell weitere URLs hinzu
30SERVICES_LIST_URLS=
"${SERVICES_LIST_URLS:-} $USER_SERVICES_URL"
33## @fn get_service_name()
34## @brief Ermittle en Namen eines Diensts basierend auf den Dienst-Attributen.
35## @details Reihenfolge der Eingabeparameter: SERVICE_TYPE SCHEMA HOST PORT PROTOCOL PATH
43 local name=
"${service}_${scheme}_${host}_${port}_${protocol}"
44 [ -n
"${path#/}" ] && name=
"${name}_${path#/}"
45 echo
"$name" | sed
's/[^A-Za-z0-9_]/_/g'
49## @fn notify_service()
50## @brief Aktualisiere den Zeitstempel und die Entfernung (etx) eines Dienstes
51## @param service z.B. "gw"
52## @param scheme z.B. "openvpn"
53## @param host z.B. "192.168.2.254"
54## @param port z.B. "1600"
55## @param path z.B. "/"
56## @param protocol z.B. "udp"
57## @param details z.B. "via:megumi"
58## @returns Der Dienstname wird ausgegeben.
60 trap
'error_trap notify_service "$*"' EXIT
61 # wir erwarten sieben Parameter
62 [
"$#" -eq 7 ] || [
"$#" -eq 8 ]
69## @fn notify_services()
70## @brief Siehe "notify_service" - jedoch effizienter im Umgang mit einer großen Anzahl von Diensten
71## @param source Quelle (z.B. "olsr")
72## @returns Alle Dienstnamen werden ausgegeben.
74 trap
'error_trap notify_services "$*"' EXIT
87 while read -r service scheme host port protocol path details;
do
88 service_name=
$(
get_service_name "$service" "$scheme" "$host" "$port" "$protocol" "$path")
89 # Diese Attribute duerften sich nicht aendern, aber wir wollen sicherheitshalber lieber kaputten
91 # "details", "timestamp" und "source" sind die flexiblen Werte.
93 "service" "$service" \
97 "protocol" "$protocol" \
99 "details" "$details" \
100 "timestamp" "$(get_uptime_minutes)" \
112## @fn is_existing_service()
113## @brief Prüfe ob ein Service existiert
114## @param service_name der Name des Diensts
115## @returns exitcode=0 falls der Dienst existiert
117 local service_name=
"$1"
118 if [ -n
"$service_name" ] && [ -e
"$PERSISTENT_SERVICE_STATUS_DIR/$service_name" ]; then
121 trap
"" EXIT &&
return 1
126## @fn _get_local_bias_for_service()
127## @brief Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1) - abhängig von der lokalen IP und dem Dienstnamen.
128## @param service_name der Name des Diensts für den ein Bias-Wert zu ermitteln ist.
129## @details Dadurch können wir beim Sortieren strukturelle Bevorzugungen (z.B. durch alphabetische Sortierung) verhindern.
131 local service_name=
"$1"
132 # lade den Wert aus dem Cache, falls moeglich
135 if [ -z
"$bias_cache" ]; then
136 # Die resultierende host_number darf nicht zu gross sein (z.B. mit Exponentendarstellung),
137 # da andernfalls awk die Berechnung fehlerhaft durchführt.
139 host_number=
$(echo
"$service_name$(get_local_bias_number)" | md5sum | sed
's/[^0-9]//g')
140 # Laenge von 'host_number' reduzieren (die Berechnung schlaegt sonst fehl)
141 # Wir fuegen die 1 an den Beginn, um die Interpretation als octal-Zahl zu verhindern (fuehrende Null).
142 bias_cache=
$(( 1
${host_number:0:6} % LOCAL_BIAS_MODULO))
145 echo -n "$bias_cache"
149# Ermittle die Service-Prioritaet eines Dienstes.
150# Der Wert ist beliebig und nur im Vergleich mit den Prioritaeten der anderen Dienste verwendbar.
151# Als optionaler zweiter Parameter kann die Sortierung uebergeben werden. Falls diese nicht uebergeben wurde,
152# wird die aktuell konfigurierte Sortierung benutzt.
153# Sollte ein Dienst ein "priority"-Attribut tragen, dann wird die uebliche Dienst-Sortierung aufgehoben
154# und lediglich "priority" (und gegebenenfalls separat "offset") beachtet.
155get_service_priority() {
156 trap
'error_trap get_service_priority "$*"' EXIT
157 local service_name=
"$1"
158 local sorting=
"${2:-}"
161 # priority wird von nicht-olsr-Clients verwendet (z.B. mesh-Gateways mit oeffentlichen IPs)
164 if [ -n
"$priority" ]; then
165 # dieses Ziel traegt anscheinend keine Routing-Metrik
168 echo
"$((priority + offset))"
170 # wir benoetigen Informationen fuer Ziele mit Routing-Metriken
171 # aus Performance-Gruenden kommt die Sortierung manchmal von aussen
172 [ -z
"$sorting" ] && sorting=
$(get_service_sorting)
173 if [
"$sorting" =
"etx" ] || [
"$sorting" =
"hop" ]; then
174 get_distance_with_offset
"$service_name" "$sorting"
175 elif [
"$sorting" =
"manual" ]; then
178 msg_error "Unknown sorting method for services: $sorting"
184 echo
"${base_priority:-$DEFAULT_SERVICE_RANK}" | awk
'{ print $1 * 1000 + '"$service_bias"'; }'
188get_distance_with_offset() {
189 trap
'error_trap get_distance_with_offset "$*"' EXIT
190 local service_name=
"$1"
191 local sorting=
"${2:-}"
195 # aus Performance-Gruenden wird manchmal das sorting von aussen vorgegeben
196 [ -z
"$sorting" ] && sorting=
$(get_service_sorting)
198 [ -z
"$distance" ] &&
return 0
200 [ -z
"$offset" ] && offset=0
201 if [
"$sorting" =
"etx" ]; then
202 base_value=
"$distance"
203 elif [
"$sorting" =
"hop" ]; then
206 msg_debug "get_distance_with_offset: sorting '$sorting' not implemented"
208 [ -n
"$base_value" ] && echo
"$base_value" "$offset" | awk
'{ print $1 + $2 }'
212set_service_sorting() {
213 trap
'error_trap set_service_sorting "$*"' EXIT
214 local new_sorting=
"$1"
216 old_sorting=
$(get_service_sorting)
217 [
"$old_sorting" =
"$new_sorting" ] &&
return 0
218 if [
"$new_sorting" !=
"manual" ] && [
"$new_sorting" !=
"hop" ] && [
"$new_sorting" !=
"etx" ]; then
219 msg_error "Ignoring unknown sorting method: $new_sorting"
220 trap
"" EXIT &&
return 1
222 uci set
"on-core.settings.service_sorting=$new_sorting"
223 apply_changes
"on-core"
228# Liefere die aktuelle Sortier-Methode.
229# Falls eine ungueltige Sortier-Methode gesetzt ist, wird diese auf die Standard-Sortierung zurueckgesetzt.
230# Die Ausgabe dieser Funktion ist also in jedem Fall eine gueltige Sortier-Methode.
231get_service_sorting() {
232 trap
'error_trap get_service_sorting "$*"' EXIT
234 sorting=
$(uci_get
"on-core.settings.service_sorting")
235 if [
"$sorting" =
"manual" ] || [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
236 # zulaessige Sortierung
239 # unbekannte Sortierung: dauerhaft setzen
240 # keine Warnung falls die Sortierung nicht gesetzt wurde
241 [ -n
"$sorting" ] &&
msg_error "coercing unknown sorting method: $sorting -> $DEFAULT_SERVICE_SORTING"
242 uci set
"on-core.settings.service_sorting=$DEFAULT_SERVICE_SORTING"
243 echo -n
"$DEFAULT_SERVICE_SORTING"
249## @fn sort_services_by_priority()
250## @brief Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste.
251## @details Die Prioritätsinformation wird typischerweise für nicht-mesh-verteilte Dienste verwendet (z.B. den mesh-Tunnel).
253 trap
'error_trap sort_services_by_priority "$*"' EXIT
257 sorting=
$(get_service_sorting)
258 while read -r service_name;
do
259 priority=
$(get_service_priority
"$service_name" "$sorting")
260 # keine Entfernung (nicht erreichbar) -> ganz nach hinten sortieren (schmutzig, aber wohl ausreichend)
261 [ -z
"$priority" ] && priority=999999999999999
262 echo
"$priority" "$service_name"
263 done | sort -n | awk
'{print $2}'
267## @fn filter_reachable_services()
268## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
269## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an die Standardausgabe
270## weitergeleitet, falls der Dienst erreichbar sind. "Erreichbarkeit" gilt als erreicht, wenn
271## der Host via olsr route-bar ist oder wenn er als DNS-entdeckter Dienst eine Priorität hat
272## oder wenn er manuell hinzugefügt wurde.
275 while read -r service_name;
do
276 { [ -n
"$(get_service_value "$service_name
" "distance
")" ] \
277 || [ -n
"$(get_service_value "$service_name
" "priority
")" ] \
278 || [
"$(get_service_value "$service_name
" "source
")" =
"manual" ]
279 } && echo
"$service_name"
285## @fn filter_enabled_services()
286## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden.
287## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an
288## die Standardausgabe weitergeleitet, falls der Dienst nicht abgewählt wurde.
292 while read -r service_name;
do
294 [ -n
"$disabled" ] && uci_is_true
"$disabled" &&
continue
300## @fn pipe_service_attribute()
301## @brief Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück.
302## @param key Der Name eines Dienst-Attributs
303## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
304## @details Die Dienstenamen werden via Standardeingabe erwartet. Auf der Standardausgabe wird für
305## einen Dienst entweder ein Wert oder nichts ausgeliefert. Keine Ausgabe erfolgt, falls der
306## Wert des Dienste-Attributs leer ist. Bei der Eingabe von mehreren Diensten werden also
307## eventuell weniger Zeilen ausgegeben, als eingelesen wurden.
308## Falls der optionale zweite 'default'-Parameter nicht leer ist, dann wird bei einem leeren
309## Ergebnis stattdessen dieser Wert ausgegeben. Der 'default'-Parameter sorgt somit dafür, dass
310## die Anzahl der eingelesenen Zeilen in jedem Fall mit der Anzahl der ausgegebenen Zeilen
312## Die Ausgabe besteht aus dem Service-Namen und dem Attributinhalt (getrennt durch einen Tabulator).
315 local
default=
"${2:-}"
318 while read -r service_name;
do
319 [ -z
"$service_name" ] &&
continue
321 [ -z
"$value" ] &&
value=
"$default"
322 [ -n
"$value" ] && printf
'%s\t%s\n' "$service_name" "$value"
329## @param service_type (optional) ein Service-Typ
330## @brief Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind.
331## Falls kein Typ angegben wird, dann werden alle Dienste ungeachtet ihres Typs ausgegeben.
333 trap
'error_trap get_services "$*"' EXIT
334 local service_type=
"${1:-}"
335 # alle Dienste ausgeben
336 # kein Dienste-Verzeichnis? Keine Ergebnisse ...
337 [ -e
"$PERSISTENT_SERVICE_STATUS_DIR" ] ||
return 0
338 find
"$PERSISTENT_SERVICE_STATUS_DIR" -type f -size +1c -print0 \
339 | xargs -0 -r -n 1 basename \
340 |
if [ -n
"$service_type" ]; then
348## @fn filter_services_by_value()
349## @param key ein Schlüssel
350## @param value ein Wert
351## @details Als Parameter kann ein "key/value"-Schluesselpaar angegeben werden.
352## Nur diejenigen Dienste, auf die diese Bedingung zutrifft, werden zurueckgeliefert.
357 while read -r service_name;
do
358 [
"$value" !=
"$(get_service_value "$service_name
" "$key
")" ] || echo
"$service_name"
363## @fn set_service_value()
364## @brief Setzen eines oder mehrerer Werte fuer einen Dienst.
365## Je nach Schluesselname wird der Inhalt in die persistente uci- oder
366# die volatile tmpfs-Datenbank geschrieben.
368 local service_name=
"$1"
373 [ -z
"$service_name" ] \
374 &&
msg_error "No service given for attribute change ($attribute=$value)" \
375 && trap
"" EXIT &&
return 1
376 while [
"$#" -gt 0 ];
do
380 # unverändert? ueberspringen ...
381 [
"$value" =
"$(get_service_value "$service_name
" "$attribute
")" ] &&
continue
382 if echo
"$PERSISTENT_SERVICE_ATTRIBUTES" | grep -q -w
"$attribute"; then
383 dirname=
"$PERSISTENT_SERVICE_STATUS_DIR"
385 dirname=
"$VOLATILE_SERVICE_STATUS_DIR"
387 _set_file_dict_value
"$dirname/$service_name" "$attribute" "$value"
392## @fn get_service_value()
393## @brief Auslesen eines Werts aus der Service-Datenbank.
394## @param key Der Name eines Dienst-Attributs
395## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
396## @details Falls das Attribut nicht existiert, wird ein leerer Text zurückgeliefert.
397## Es gibt keinen abschließenden Zeilenumbruch.
399 local service_name=
"$1"
401 local
default=
"${3:-}"
404 [ -z
"$service_name" ] \
405 &&
msg_error "No service given for attribute request ('$attribute')" \
406 && trap
"" EXIT &&
return 1
407 value=
$(
_get_file_dict_value "$attribute" "$PERSISTENT_SERVICE_STATUS_DIR/$service_name" "$VOLATILE_SERVICE_STATUS_DIR/$service_name")
408 [ -n
"$value" ] && echo -n
"$value" || echo -n
"$default"
413# Liefere die Suffixe aller Schluessel aus der Service-Attribut-Datenbank,
414# die mit dem gegebenen Praefix uebereinstimmen.
415get_service_attributes() {
416 _get_file_dict_keys
"$PERSISTENT_SERVICE_STATUS_DIR/$1" "$VOLATILE_SERVICE_STATUS_DIR/$1"
420## @fn print_services()
421## @brief menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
422## @param service_type (optional) ein Service-Type
423## @returns Ausgabe der bekannten Dienste (für Menschen - nicht parsebar)
425 trap
'error_trap print_services "$*"' EXIT
431 for attribute in
$(get_service_attributes
"$service_name");
do
432 printf
'\t%s=%s\n' "$attribute" "$(get_service_value "$service_name
" "$attribute
")"
439# Speichere das angegebene uci-Praefix als eine von einem Service abhaengige Konfiguration.
440# Dies ist sinnvoll fuer abgeleitete VPN-Konfigurationen oder Portweiterleitungen.
441# Schnittstelle: siehe _add_service_dependency
442service_add_uci_dependency() {
443 _add_service_dependency
"uci_dependency" "$@"
447# Speichere einen Dateinamen als Abhaengigkeit eines Service.
448# Dies ist sinnvoll fuer Dateien, die nicht mehr gebraucht werden, sobald der Service entfernt wird.
449# Schnittstelle: siehe _add_service_dependency
450service_add_file_dependency() {
451 _add_service_dependency
"file_dependency" "$@"
455# Speichere eine Abhaengigkeit fuer einen Dienst.
456# Parameter: Service-Name
457# Parameter: textuelle Darstellung einer Abhaengigkeit (ohne Leerzeichen)
458_add_service_dependency() {
459 trap
'error_trap _add_service_dependency "$*"' EXIT
460 local dependency=
"$1"
461 local service_name=
"$2"
467 # schon vorhanden -> fertig
468 [
"$dep" =
"$token" ] &&
return 0
471 if [ -z
"$deps" ]; then
480# Entferne alle mit diesem Service verbundenen Konfigurationen (inkl. Rekonfiguration von firewall, etc.).
481cleanup_service_dependencies() {
482 trap
'error_trap cleanup_service_dependencies "$*"' EXIT
483 local service_name=
"$1"
491 # uci-Sektionen loeschen
494 # gib die oberste config-Ebene aus - fuer spaeteres "apply_changes"
495 echo
"$dep" | cut -f 1 -
d .
496 done | sort | uniq |
while read -r branch;
do
497 apply_changes
"$branch"
504 trap
'error_trap delete_service "$*"' EXIT
505 local service_name=
"$1"
506 [ -z
"$service_name" ] &&
msg_error "No service given for deletion" && trap
"" EXIT &&
return 1
507 cleanup_service_dependencies
"$service_name"
508 rm -f
"$PERSISTENT_SERVICE_STATUS_DIR/$service_name"
509 rm -f
"$VOLATILE_SERVICE_STATUS_DIR/$service_name"
513# Durchlaufe alle Dienste und verteile Rangziffern ohne Doppelung an alle Dienste.
514# Die Dienst-Arten (z.B. DNS und UGW) werden dabei nicht beachtet.
515# Die Rangziffern sind anschliessend streng monoton aufsteigend - beginnend bei 1.
516# Falls aktuell die manuelle Sortierung aktiv ist, wird deren Reihenfolge beibehalten.
517# Ansonsten basiert die Vergabe der Rangziffern auf der Reihenfolge entsprechend der aktuell aktiven Sortierung.
518_distribute_service_ranks() {
528## @fn move_service_up()
529## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben
530## @param service_name der zu verschiebende Dienst
531## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
532## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
533## * manual: Verschiebung vor den davorplatzierten Dienst desselben Typs
534## * etx/hop: Reduzierung des Offsets um eins
535## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
537 trap
'error_trap move_service_up "$*"' EXIT
538 local service_name=
"$1"
542 local current_service
544 sorting=
$(get_service_sorting)
545 if [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
546 # reduziere den Offset um eins
548 temp=
$(echo
"$temp" | awk
'{ print $1 - 1 }')
550 elif [
"$sorting" =
"manual" ]; then
552 if [
"$current_service" =
"$service_name" ]; then
553 if [ -z
"$prev_service" ]; then
554 # es gibt keinen Dienst oberhalb des zu verschiebenden
557 # wir verschieben den Dienst ueber den davor liegenden
559 # ziehe einen halben Rang ab
560 temp=
$(echo
"$temp" | awk
'{ print $1 - 0.5 }')
562 # erneuere die Rang-Vergabe
563 _distribute_service_ranks
568 prev_service=
"$current_service"
571 msg_info "Warning: [move_service_up] for this sorting method is not implemented: $sorting"
576## @fn move_service_down()
577## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten
578## @param service_name der zu verschiebende Dienst
579## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
580## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
581## * manual: Verschiebung hinter den dahinterliegenden Dienst desselben Typs
582## * etx/hop: Erhöhung des Offsets um eins
583## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
585 trap
'error_trap move_service_down "$*"' EXIT
586 local service_name=
"$1"
590 local current_service
592 sorting=
$(get_service_sorting)
593 if [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
594 # reduziere den Offset um eins
596 temp=
$(echo
"$temp" | awk
'{ print $1 + 1 }')
598 elif [
"$sorting" =
"manual" ]; then
600 if [
"$prev_service" =
"$service_name" ]; then
601 # wir verschieben den Dienst hinter den danach liegenden
603 # fuege einen halben Rang hinzu
604 temp=
$(echo
"$temp" | awk
'{ print $1 + 0.5 }')
606 # erneuere die Rang-Vergabe
607 _distribute_service_ranks
611 prev_service=
"$current_service"
614 msg_info "Warning: [move_service_down] for this sorting method is not implemented: $sorting"
619## @fn move_service_top()
620## @brief Verschiebe einen Dienst an die Spitze der Dienst-Sortierung
621## @param service_name der zu verschiebende Dienst
622## @param service_types ein oder mehrere Dienst-Typen, auf die die Ermittlung der Dienst-Liste begrenzt werden soll (z.B. "gw")
623## @details Der Dienst steht anschließend direkt vor dem bisher führenden Dienst der ausgewählten Typen (falls angegeben).
624## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste an die Spitze.
626 trap
'error_trap move_service_top "$*"' EXIT
627 local service_name=
"$1"
638 sorting=
$(get_service_sorting)
639 # kein top-Service oder wir sind bereits ganz oben -> Ende
640 if [ -z
"$top_service" ] || [
"$top_service" =
"$service_name" ]; then
642 elif [
"$sorting" =
"hop" ] || [
"$sorting" =
"etx" ]; then
643 top_distance=
$(get_distance_with_offset
"$top_service" "$sorting")
644 our_distance=
$(get_distance_with_offset
"$service_name" "$sorting")
645 [ -z
"$our_distance" ] &&
msg_info "Failed to move unreachable service ('$service_name') to top" &&
return 0
647 # wir verschieben unseren Offset, auf dass wir knapp ueber "top" stehen
648 new_offset=
$(echo | awk
"{ print $current_offset + int($top_distance - $our_distance) - 1 }")
650 elif [
"$sorting" =
"manual" ]; then
651 # setze den Rang des Dienstes auf den top-Dienst minus 0.5
653 new_rank=
$(echo
"$top_rank" | awk
'{ print $1 - 0.5 }')
655 # erneuere die Rang-Vergabe
656 _distribute_service_ranks
658 msg_info "Warning: [move_service_top] for this sorting method is not implemented: $sorting"
663## @fn get_service_detail()
664## @brief Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
665## @param service_name Name eines Diensts
666## @param key Name des Schlüssels
667## @param default dieser Wert wird zurückgeliefert, falls der Schlüssel nicht gefunden wurde
668## @returns den ermittelten Wert aus dem Schlüssel-Wert-Paar
670 local service_name=
"$1"
672 local
default=
"${3:-}"
675 [ -n
"$value" ] && echo -n
"$value" || echo -n
"$default"
680## @fn set_service_detail()
681## @brief Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
682## @param service_name Name eines Diensts
683## @param key Name des Schlüssels
684## @param value der neue Wert
685## @details Ein leerer Wert löscht das Schlüssel-Wert-Paar.
687 local service_name=
"$1"
691 new_details=
$(
get_service_value "$service_name" "details" | replace_in_key_value_list
"$key" ":" "$value")
697# Liefere eine Semikolon-separierte Liste von Service-Eigenschaften zurueck.
698# Jede Eigenschaft wird folgendermassen ausgedrueckt:
699# type|source|key[|default]
700# Dabei sind folgende Inhalte moeglich:
701# type: Rueckgabetyp (string, number, bool)
702# source: Quelle der Informationen (value, detail, function, id)
703# key: Name des Werts, des Details oder der Funktion
704# default: Standardwert, falls das Ergebnis leer sein sollte
705# Wahrheitswerte werden als "true" oder "false" zurueckgeliefert. Alle anderen Rueckgabetypen bleiben unveraendert.
706# Das Ergebnis sieht folgendermassen aus:
707# SERVICE_NAME;RESULT1;RESULT2;...
708get_service_as_csv() {
709 local service_name=
"$1"
718 # Abbruch mit Fehler bei unbekanntem Dienst
720 echo -n
"$service_name"
721 for specification in
"$@";
do
722 rtype=
$(echo
"$specification" | cut -f 1 -
d "|")
723 source=
$(echo
"$specification" | cut -f 2 -
d "|")
724 key=
$(echo
"$specification" | cut -f 3 -
d "|")
725 default=
$(echo
"$specification" | cut -f 4- -
d "|")
726 # Ermittlung des Funktionsaufrufs
727 if [
"$source" =
"function" ]; then
728 if [
"$rtype" =
"bool" ]; then
729 "$key" "$service_name" &&
value=
"true" ||
value=
"false"
731 value=
$(
"$key" "$service_name")
734 if [ "$source" = "
value" ]; then
736 elif [ "$source" = "detail" ]; then
739 msg_error "Unknown service attribute requested: $specification"
740 echo -n "
${separator}
"
743 [ -z "$value" ] && [ -n "$default
" ] && value="$default
"
744 if [ "$rtype
" = "bool" ]; then
745 # Pruefung auf wahr/falsch
746 value=$(uci_is_true "$value" && echo "true" || echo "false")
751 # mit Zeilenumbruch abschliessen
756## @fn get_service_log_filename()
757## @brief Ermittle den Namen der Log-Datei für diesen Dienst. Zusätzliche Details (z.B. "openvpn mtu
") sind möglich.
758## @param service Name eines Dienstes.
759## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
760## @details Die Funktion stellt sicher, dass das Verzeichnis der ermittelten Log-Datei anschließend existiert.
761get_service_log_filename() {
762 trap 'error_trap get_service_log_filename "$*
"' EXIT
763 local service_name="$1
"
766 local filename="$service_name
"
767 while [ $# -gt 0 ]; do
768 filename="${filename}.$1
"
771 full_filename="$SERVICES_LOG_BASE/
$(get_safe_filename
"$filename").log
"
772 mkdir -p "$(dirname
"$full_filename")
"
773 echo -n "$full_filename
"
777## @fn get_service_log_content()
778## @brief Lies den Inhalt einer Log-Datei für einen Dienst aus.
779## @param service Name eines Dienstes.
780## @param max_lines maximale Anzahl der auszuliefernden Zeilen (unbegrenzt: 0)
781## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
782## @see get_service_log_filename
783get_service_log_content() {
784 trap 'error_trap get_service_log_content "$*
"' EXIT
785 local service_name="$1
"
789 log_filename=$(get_service_log_filename "$service_name
" "$@")
790 [ -e "$log_filename
" ] || return 0
791 if [ "$max_lines
" = "0
" ]; then
792 # alle Einträge ausgeben
795 # nur die letzten Einträge ausliefern
801## @fn is_service_routed_via_wan()
802## @brief Pruefe ob der Verkehr zum Anbieter des Diensts über ein WAN-Interface verlaufen würde.
803## @param service_name der Name des Diensts
804## @returns Exitcode == 0, falls das Routing über das WAN-Interface verläuft.
805is_service_routed_via_wan() {
806 trap 'error_trap is_service_routed_via_wan "$*
"' EXIT
807 local service_name="$1
"
809 local outgoing_device
811 host=$(get_service_value "$service_name
" "host
")
812 outgoing_device=$(get_target_route_interface "$host
")
813 if is_device_in_zone "$outgoing_device
" "$ZONE_WAN
"; then
814 msg_debug "target
'$host' routing through wan device: $outgoing_device
"
817 outgoing_zone=$(get_zone_of_device "$outgoing_device
")
818 msg_debug "warning: target
'$host' is routed via
interface '$outgoing_device' (zone '$outgoing_zone') instead of the expected WAN zone ($ZONE_WAN)"
819 trap "" EXIT &&
return 1
824_notify_service_success() {
825 local service_name=
"$1"
832_notify_service_failure() {
833 local service_name=
"$1"
834 local max_fail_attempts=
"$2"
835 # erhoehe den Fehlerzaehler
839 # Pruefe, ob der Fehlerzaehler gross genug ist, um seinen Status auf "fail" zu setzen.
840 if [
"$fail_counter" -ge
"$max_fail_attempts" ]; then
841 # Die maximale Anzahl von aufeinanderfolgenden fehlgeschlagenen Tests wurde erreicht:
842 # markiere ihn als kaputt.
844 elif uci_is_true
"$(get_service_value "$service_name
" "status
")"; then
845 # Bisher galt der Dienst als funktionsfaehig - wir setzen ihn auf "neutral" bis
846 # die maximale Anzahl aufeinanderfolgender Fehler erreicht ist.
849 # Der Test gilt wohl schon als fehlerhaft - das kann so bleiben.
856## @fn is_trusted_service_list_outdated()
857## @brief Ermittle ob mindestens ein Zeitstempel für einen "trusted" Dienst vorhanden ist, der nicht älter
858## als die vorgegebene Aktualisierungsperiode ist.
859## @returns Wahr, falls kein Diest mit aktuellem Zeitstempel gefunden wurde.
861 trap
'error_trap is_trusted_service_list_outdated "$*"' EXIT
862 local most_recent_timestamp
867 # kein Zeitstempel -> dies gilt als "veraltet"
868 [ -z
"$most_recent_timestamp" ] &&
return 0
869 # der aktuellste Zeitstempel ist zu alt
870 is_timestamp_older_minutes
"$most_recent_timestamp" "$UPDATE_TRUSTED_SERVICES_PERIOD_MINUTES" &&
return 0
871 trap
"" EXIT &&
return 1
875retrieve_service_list_url() {
877 if ! http_request
"$url"; then
878 if echo
"$url" | grep -q
'^https://'; then
879 # The URL probably uses a certificate signed by the Opennet CA,
880 # while "on-certificate" is not installed.
881 # Thus we do not emit any warnings.
884 msg_info "Failed to retrieve list of services from $url"
890# Beispiel zum Aufruf dieser Funktion: "on-function add_custom_proxy_gw itsuki.on-i.de 1600 udp"
891## @fn add_custom_proxy_gw()
892## @brief Fuege eine zusätzliches Dienst-Weiterleitung hinzu (proxy-gw).
893## @param host der DNS Name des OpenVPN Servers
894## @param port der Port des OpenVPN Server (i.d.R. 1600)
895## @param protocol udp oder tcp (i.d.R. udp)
897 local scheme=
"openvpn"
905 service_name=
$(
notify_service "manual" "proxy-gw" "$scheme" "$host" "$port" "$protocol" "/" "$details")
907 if is_function_available
"pick_local_service_relay_port"; then
908 pick_local_service_relay_port
"$service_name" >/dev/
null
913## @fn update_trusted_services_list()
914## @brief Hole die vertrauenswürdigen Dienste von signierten Opennet-Quellen.
915## @details Diese Dienste führen beispielsweise auf UGW-APs zur Konfiguration von Portweiterleitungen
916## ins Internet. Daher sind sie nur aus vertrauenswürdiger Quelle zu akzeptieren (oder manuell).
929 service_list=
$(
for url in $SERVICES_LIST_URLS;
do retrieve_service_list_url
"$url";
done)
930 # leeres Ergebnis? Noch keine Internet-Verbindung? Keine Aktualisierung, keine Beraeumung ...
931 [ -z
"$service_list" ] &&
msg_info "No trusted services discovered: skipping update." &&
return
932 echo
"$service_list" | grep -v
"^#" | sed
's/\t\+/\t/g' |
while read -r line;
do
933 service_type=
$(echo
"$line" | cut -f 1)
934 # falls der Dienst-Typ mit "proxy-" beginnt, soll er weitergeleitet werden
935 if [
"${service_type#$RELAYABLE_SERVICE_PREFIX}" =
"$service_type" ]; then
941 # entferne das Praefix
943 scheme=
$(echo
"$line" | cut -f 2)
944 host=
$(echo
"$line" | cut -f 3)
945 port=
$(echo
"$line" | cut -f 4)
946 protocol=
$(echo
"$line" | cut -f 5)
947 priority=
$(echo
"$line" | cut -f 6)
948 details=
$(echo
"$line" | cut -f 7-)
949 service_name=
$(
notify_service "trusted" "$service_type" "$scheme" "$host" "$port" "$protocol" "/" "$details")
951 if [ -n "$is_proxy" ]; then
952 if is_function_available "pick_local_service_relay_port"; then
953 pick_local_service_relay_port "$service_name" >/dev/
null
958 # veraltete Dienste entfernen
960 min_timestamp=
$((
$(get_uptime_minutes) -
$(get_on_core_default
"trusted_service_expire_minutes")))
961 # falls die uptime kleiner ist als die Verfallszeit, dann ist ein Test sinnfrei
962 if [
"$min_timestamp" -gt 0 ]; then
965 # Wurde der Service erst vor kurzem aktualisiert? Sonst loeschen ...
966 [
"$timestamp" -ge
"$min_timestamp" ] || delete_service
"$service_name"
969 # aktualisiere DNS- und NTP-Dienste
970 apply_changes on-core
974## @fn run_cyclic_service_tests()
975## @brief Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist
976## @param test_function der Name der zu verwendenden Test-Funktion für einen Dienst (z.B. "verify_vpn_connection")
977## @param test_period_minutes Wiederholungsperiode der Dienst-Prüfung
978## @param max_fail_attempts Anzahl von Fehlversuchen, bis ein Dienst von "gut" oder "unklar" zu "schlecht" wechselt
979## @details Die Diensteanbieter werden in der Reihenfolge ihrer Priorität geprüft.
980## Nach dem ersten Durchlauf dieser Funktion sollte typischerweise der nächstgelegene nutzbare Dienst
981## als funktionierend markiert sein.
982## Falls nach dem Durchlauf aller Dienste keiner positiv getestet wurde (beispielsweise weil alle Zeitstempel zu frisch sind),
983## dann wird in jedem Fall der älteste nicht-funktionsfähige Dienst getestet. Dies minimiert die Ausfallzeit im
984## Falle einer globalen Nicht-Erreichbarkeit aller Dienstenanbieter ohne auf den Ablauf der Test-Periode warten zu müssen.
985## @attention Seiteneffekt: die Zustandsinformationen des getesteten Diensts (Status, Test-Zeitstempel) werden verändert.
987 trap
'error_trap test_openvpn_service_type "$*"' EXIT
988 local test_function=
"$1"
989 local test_period_minutes=
"$2"
990 local max_fail_attempts=
"$3"
999 if [ -z
"$status" ] || is_timestamp_older_minutes
"$timestamp" "$test_period_minutes"; then
1000 if "$test_function" "$service_name"; then
1001 msg_debug "service $service_name successfully tested"
1002 _notify_service_success
"$service_name"
1003 # wir sind fertig - keine weiteren Tests
1004 # Da "return" aufgrund der Pipe nicht die gesamte Funktion beenden
1005 # würde, senden wir stattdessen die Markierung fuer "keine Tests
1006 # noetig" und beenden die Schleife.
1007 printf
'%s %s\n' "-1" "$service_name"
1010 msg_debug "failed to verify $service_name"
1011 _notify_service_failure
"$service_name" "$max_fail_attempts"
1013 elif uci_is_false
"$status"; then
1014 # Junge "kaputte" Dienste sind potentielle Kandidaten fuer einen vorzeitigen Test, falls
1015 # ansonsten kein Dienst positiv getestet wurde.
1016 echo
"$timestamp $service_name"
1018 # funktionsfaehige "alte" Dienste - es gibt nichts fuer sie zu tun
1019 # Wir sortieren sie nach ganz oben, um bei Existenz eines lauffaehigen Diensts
1020 # keine unnoetigen Tests von nicht-funktionierenden Hosts durchzufuehren.
1021 printf
'%s %s\n' "-1" "$service_name"
1023 done | sort -n |
while read -r timestamp service_name;
do
1024 # Mit dem Zeitstempel "-1" sind funktionierende Dienste markiert. Wir brauchen also keine
1026 [
"$timestamp" =
"-1" ] &&
return 0
1027 # Hier landen wir nur, falls alle defekten Gateways zu jung fuer einen Test waren und
1028 # gleichzeitig kein Dienst erfolgreich getestet wurde bzw. als erfolgreich gilt.
1029 # Dies stellt sicher, dass nach einer kurzen Nicht-Erreichbarkeit aller Gateways (z.B. olsr-Ausfall)
1030 # relativ schnell wieder ein funktionierender Gateway gefunden wird, obwohl alle Test-Zeitstempel noch recht
1032 msg_debug "there is no service to be tested - thus we pick the service with the oldest test timestamp: $service_name"
1033 if "$test_function" "$service_name"; then
1034 _notify_service_success
"$service_name"
1036 _notify_service_failure
"$service_name" "$max_fail_attempts"
1038 # wir wollen nur genau einen Test durchfuehren
1043# Ende der Doku-Gruppe
set eu case $1 in system dhcp network wireless firewall reload_config true
_get_file_dict_value(key)
Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom.
msg_debug(message)
Debug-Meldungen ins syslog schreiben.
msg_error(message)
Die Fehlermeldungen werden in die Standard-Fehlerausgabe und ins syslog geschrieben.
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
get_hop_count_and_etx(host)
Liefere den Hop-Count und den ETX-Wert für einen Zielhost zurück.
sort_services_by_priority()
Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste.
add_custom_proxy_gw()
Fuege eine zusätzliches Dienst-Weiterleitung hinzu (proxy-gw, host, port).
run_cyclic_service_tests(test_function)
Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist.
get_service_detail(service_name, key, default)
Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts.
_get_local_bias_for_service()
Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1, service_name) - abhängig von der l...
pipe_service_attribute(key, default)
Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück.
move_service_down(service_name, service_type)
Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten.
get_service_name()
Ermittle en Namen eines Diensts basierend auf den Dienst-Attributen.
is_existing_service(service_name)
Prüfe ob ein Service existiert.
filter_enabled_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden.
set_service_value()
Setzen eines oder mehrerer Werte fuer einen Dienst. Je nach Schluesselname wird der Inhalt in die per...
is_trusted_service_list_outdated()
Ermittle ob mindestens ein Zeitstempel für einen "trusted" Dienst vorhanden ist, der nicht älter als ...
get_services(service_type)
Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind. Falls kein Typ angegben wird,...
get_service_value(key, default)
Auslesen eines Werts aus der Service-Datenbank.
set_service_detail(service_name, key, value)
Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts.
notify_services(source)
Siehe "notify_service" - jedoch effizienter im Umgang mit einer großen Anzahl von Diensten.
move_service_top(service_name, service_types)
Verschiebe einen Dienst an die Spitze der Dienst-Sortierung.
notify_service()
Aktualisiere den Zeitstempel und die Entfernung (etx, service, scheme, host, port,...
print_services(service_type)
menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
update_trusted_services_list()
Hole die vertrauenswürdigen Dienste von signierten Opennet-Quellen.
move_service_up(service_name, service_type)
Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben.
filter_services_by_value(key, value)
filter_reachable_services()
Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
uci_delete(uci_path)
Lösche ein UCI-Element.
if[-d "/etc/on-firewall.d/"]
set eu grep root::etc shadow exit if command v chpasswd dev null
set eu on function print_services services log for dir in etc on services d var on services volatile d