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