Opennet Firmware
 Alle Dateien Funktionen Variablen Gruppen Seiten
services.sh
gehe zur Dokumentation dieser Datei
1 ## @defgroup services Dienste
2 ## @brief Verwaltung von Diensten (z.B. via olsrd-nameservice announciert)
3 # Beginn der Doku-Gruppe
4 ## @{
5 
6 VOLATILE_SERVICE_STATUS_DIR=/tmp/on-services-volatile.d
7 PERSISTENT_SERVICE_STATUS_DIR=/etc/on-services.d
8 # eine grosse Zahl sorgt dafuer, dass neu entdeckte Dienste hinten angehaengt werden
9 DEFAULT_SERVICE_RANK=10000
10 DEFAULT_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 # local_relay_port: der lokale Port, der für eine Dienst-Weiterleitung verwendet wird - er sollte über reboots hinweg stabil sein
19 # source: die Quelle des Diensts (olsrd/dns/manual) muss erhalten bleiben, um ihn später löschen zu können
20 PERSISTENT_SERVICE_ATTRIBUTES="service scheme host port protocol path uci_dependency file_dependency priority rank offset disabled source local_relay_port"
21 LOCAL_BIAS_MODULO=10
22 SERVICES_LOG_BASE=/var/log/on-services
23 
24 
25 ## @fn get_service_name()
26 ## @brief Ermittle en Namen eines Diensts basierend auf den Dienst-Attributen.
27 ## @details Reihenfolge der Eingabeparameter: SERVICE_TYPE SCHEMA HOST PORT PROTOCOL PATH
29  local service="$1"
30  local scheme="$2"
31  local host="$3"
32  local port="$4"
33  local protocol="$5"
34  local path="$6"
35  local name="${service}_${scheme}_${host}_${port}_${protocol}"
36  [ -n "${path#/}" ] && name="${name}_${path#/}"
37  echo "$name" | sed 's/[^A-Za-z0-9_]/_/g'
38 }
39 
40 
41 ## @fn notify_service()
42 ## @brief Aktualisiere den Zeitstempel und die Entfernung (etx) eines Dienstes
43 ## @returns Der Dienstname wird ausgegeben.
45  trap "error_trap notify_service '$*'" $GUARD_TRAPS
46  local service="$1"
47  local scheme="$2"
48  local host="$3"
49  local port="$4"
50  local protocol="$5"
51  local path="$6"
52  local source="$7"
53  local details="$8"
54  local service_name
55  service_name=$(get_service_name "$service" "$scheme" "$host" "$port" "$protocol" "$path")
56  if ! is_existing_service "$service_name"; then
57  # diese Attribute sind Bestandteil des Namens und aendern sich eigentlich nicht
58  set_service_value "$service_name" "service" "$service"
59  set_service_value "$service_name" "scheme" "$scheme"
60  set_service_value "$service_name" "host" "$host"
61  set_service_value "$service_name" "port" "$port"
62  set_service_value "$service_name" "protocol" "$protocol"
63  set_service_value "$service_name" "path" "$path"
64  fi
65  # dies sind die flexiblen Attribute
66  set_service_value "$service_name" "details" "$details"
67  set_service_value "$service_name" "timestamp" "$(get_uptime_minutes)"
68  set_service_value "$service_name" "source" "$source"
69  update_service_routing_distance "$service_name"
70  echo "$service_name"
71 }
72 
73 
74 ## @fn update_service_routing_distance()
75 ## @brief Aktualisiere Routing-Entfernung und Hop-Count eines Dienst-Anbieters
76 ## @param service_name der zu aktualisierende Dienst
77 ## @details Beide Dienst-Werte werden gelöscht, falls der Host nicht route-bar sein sollte.
78 ## Diese Funktion sollte regelmäßig für alle Hosts ausgeführt werden.
80  trap "error_trap update_service_routing_distance '$*'" $GUARD_TRAPS
81  local service_name="$1"
82  local hop
83  local etx
84  get_hop_count_and_etx "$(get_service_value "$service_name" "host")" | while read hop etx; do
85  set_service_value "$service_name" "distance" "$etx"
86  set_service_value "$service_name" "hop_count" "$hop"
87  done
88 }
89 
90 
91 ## @fn is_existing_service()
92 ## @brief Prüfe ob ein Service existiert
93 ## @param service_name der Name des Diensts
94 ## @returns exitcode=0 falls der Dienst existiert
96  local service_name="$1"
97  [ -n "$service_name" -a -e "$PERSISTENT_SERVICE_STATUS_DIR/$service_name" ] && return 0
98  trap "" $GUARD_TRAPS && return 1
99 }
100 
101 
102 ## @fn _get_local_bias_for_service()
103 ## @brief Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1) - abhängig von der lokalen IP und dem Dienstnamen.
104 ## @param service_name der Name des Diensts für den ein Bias-Wert zu ermitteln ist.
105 ## @details Dadurch können wir beim Sortieren strukturelle Bevorzugungen (z.B. durch alphabetische Sortierung) verhindern.
107  local service_name="$1"
108  # lade den Wert aus dem Cache, falls moeglich
109  local bias_cache
110  bias_cache=$(get_service_value "$service_name" "local_bias")
111  if [ -z "$bias_cache" ]; then
112  # Die resultierende host_number darf nicht zu gross sein (z.B. mit Exponentendarstellung),
113  # da andernfalls awk die Berechnung fehlerhaft durchführt.
114  local host_number
115  host_number=$(echo "$service_name$(get_local_bias_number)" | md5sum | sed 's/[^0-9]//g')
116  # Laenge von 'host_number' reduzieren (die Berechnung schlaegt sonst fehl)
117  # Wir fuegen die 1 an den Beginn, um die Interpretation als octal-Zahl zu verhindern (fuehrende Null).
118  bias_cache=$(( 1${host_number:0:6} % LOCAL_BIAS_MODULO))
119  set_service_value "$service_name" "local_bias" "$bias_cache"
120  fi
121  echo -n "$bias_cache"
122 }
123 
125 # Ermittle die Service-Prioritaet eines Dienstes.
126 # Der Wert ist beliebig und nur im Vergleich mit den Prioritaeten der anderen Dienste verwendbar.
127 # Als optionaler zweiter Parameter kann die Sortierung uebergeben werden. Falls diese nicht uebergeben wurde,
128 # wird die aktuell konfigurierte Sortierung benutzt.
129 # Sollte ein Dienst ein "priority"-Attribut tragen, dann wird die uebliche Dienst-Sortierung aufgehoben
130 # und lediglich "priority" (und gegebenenfalls separat "offset") beachtet.
131 get_service_priority() {
132  trap "error_trap get_service_priority '$*'" $GUARD_TRAPS
133  local service_name="$1"
134  local sorting="${2:-}"
135  local priority
136  priority=$(get_service_value "$service_name" "priority")
137  local rank
138  # priority wird von nicht-olsr-Clients verwendet (z.B. mesh-Gateways mit oeffentlichen IPs)
139  local base_priority
140  base_priority=$(
141  if [ -n "$priority" ]; then
142  # dieses Ziel traegt anscheinend keine Routing-Metrik
143  local offset
144  offset=$(get_service_value "$service_name" "offset" "0")
145  echo "$((priority + offset))"
146  else
147  # wir benoetigen Informationen fuer Ziele mit Routing-Metriken
148  # aus Performance-Gruenden kommt die Sortierung manchmal von aussen
149  [ -z "$sorting" ] && sorting=$(get_service_sorting)
150  if [ "$sorting" = "etx" -o "$sorting" = "hop" ]; then
151  get_distance_with_offset "$service_name" "$sorting"
152  elif [ "$sorting" = "manual" ]; then
153  get_service_value "$service_name" "rank" "$DEFAULT_SERVICE_RANK"
154  else
155  msg_error "Unknown sorting method for services: $sorting"
156  echo 1
157  fi
158  fi)
159  local service_bias
160  service_bias=$(_get_local_bias_for_service "$service_name")
161  echo "${base_priority:-$DEFAULT_SERVICE_RANK}" | awk '{ print $1 * 1000 + '$service_bias'; }'
162 }
163 
164 
165 get_distance_with_offset() {
166  trap "error_trap get_distance_with_offset '$*'" $GUARD_TRAPS
167  local service_name="$1"
168  local sorting="${2:-}"
169  local distance
170  local base_value=
171  local offset
172  # aus Performance-Gruenden wird manchmal das sorting von aussen vorgegeben
173  [ -z "$sorting" ] && sorting=$(get_service_sorting)
174  distance=$(get_service_value "$service_name" "distance")
175  [ -z "$distance" ] && return 0
176  offset=$(get_service_value "$service_name" "offset")
177  [ -z "$offset" ] && offset=0
178  if [ "$sorting" = "etx" ]; then
179  base_value="$distance"
180  elif [ "$sorting" = "hop" ]; then
181  base_value=$(get_service_value "$service_name" "hop_count")
182  else
183  msg_debug "get_distance_with_offset: sorting '$sorting' not implemented"
184  fi
185  [ -n "$base_value" ] && echo "$base_value" "$offset" | awk '{ print $1 + $2 }'
186  return 0
187 }
188 
189 
190 set_service_sorting() {
191  trap "error_trap set_service_sorting '$*'" $GUARD_TRAPS
192  local new_sorting="$1"
193  local old_sorting
194  old_sorting=$(get_service_sorting)
195  [ "$old_sorting" = "$new_sorting" ] && return 0
196  [ "$new_sorting" != "manual" -a "$new_sorting" != "hop" -a "$new_sorting" != "etx" ] && \
197  msg_error "Ignoring unknown sorting method: $new_sorting" && \
198  trap "" $GUARD_TRAPS && return 1
199  uci set "on-core.settings.service_sorting=$new_sorting"
200  apply_changes on-core
201 }
202 
203 
204 # Liefere die aktuelle Sortier-Methode.
205 # Falls eine ungueltige Sortier-Methode gesetzt ist, wird diese auf die Standard-Sortierung zurueckgesetzt.
206 # Die Ausgabe dieser Funktion ist also in jedem Fall eine gueltige Sortier-Methode.
207 get_service_sorting() {
208  trap "error_trap get_service_sorting '$*'" $GUARD_TRAPS
209  local sorting
210  sorting=$(uci_get "on-core.settings.service_sorting")
211  if [ "$sorting" = "manual" -o "$sorting" = "hop" -o "$sorting" = "etx" ]; then
212  # zulaessige Sortierung
213  echo -n "$sorting"
214  else
215  # unbekannte Sortierung: dauerhaft setzen
216  # keine Warnung falls die Sortierung nicht gesetzt wurde
217  [ -n "$sorting" ] && msg_error "coercing unknown sorting method: $sorting -> $DEFAULT_SERVICE_SORTING"
218  uci set "on-core.settings.service_sorting=$DEFAULT_SERVICE_SORTING"
219  echo -n "$DEFAULT_SERVICE_SORTING"
220  fi
221  return 0
222 }
223 
224 
225 ## @fn sort_services_by_priority()
226 ## @brief Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste.
227 ## @details Die Prioritätsinformation wird typischerweise für nicht-mesh-verteilte Dienste verwendet (z.B. den mesh-Tunnel).
229  trap "error_trap sort_services_by_priority '$*'" $GUARD_TRAPS
230  local service_name
231  local priority
232  local sorting
233  sorting=$(get_service_sorting)
234  while read service_name; do
235  priority=$(get_service_priority "$service_name" "$sorting")
236  # keine Entfernung (nicht erreichbar) -> ganz nach hinten sortieren (schmutzig, aber wohl ausreichend)
237  [ -z "$priority" ] && priority=999999999999999
238  echo "$priority" "$service_name"
239  done | sort -n | awk '{print $2}'
240 }
241 
242 
243 ## @fn filter_reachable_services()
244 ## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
245 ## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an die Standardausgabe
246 ## weitergeleitet, falls der Dienst erreichbar sind. "Erreichbarkeit" gilt als erreicht, wenn
247 ## der Host via olsr route-bar ist oder wenn er als DNS-entdeckter Dienst eine Priorität hat
248 ## oder wenn er manuell hinzugefügt wurde.
250  local service_name
251  while read service_name; do
252  { [ -n "$(get_service_value "$service_name" "distance")" ] \
253  || [ -n "$(get_service_value "$service_name" "priority")" ] \
254  || [ "$(get_service_value "$service_name" "source")" = "manual" ]
255  } && echo "$service_name"
256  true
257  done
258 }
259 
260 
261 ## @fn filter_enabled_services()
262 ## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden.
263 ## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an
264 ## die Standardausgabe weitergeleitet, falls der Dienst nicht abgewählt wurde.
266  local service_name
267  local disabled
268  while read service_name; do
269  disabled=$(get_service_value "$service_name" "disabled")
270  [ -n "$disabled" ] && uci_is_true "$disabled" && continue
271  echo "$service_name"
272  done
273 }
274 
275 
276 ## @fn pipe_service_attribute()
277 ## @brief Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück.
278 ## @param key Der Name eines Dienst-Attributs
279 ## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
280 ## @details Die Dienstenamen werden via Standardeingabe erwartet. Auf der Standardausgabe wird für
281 ## einen Dienst entweder ein Wert oder nichts ausgeliefert. Keine Ausgabe erfolgt, falls der
282 ## Wert des Dienste-Attributs leer ist. Bei der Eingabe von mehreren Diensten werden also
283 ## eventuell weniger Zeilen ausgegeben, als eingelesen wurden.
284 ## Falls der optionale zweite 'default'-Parameter nicht leer ist, dann wird bei einem leeren
285 ## Ergebnis stattdessen dieser Wert ausgegeben. Der 'default'-Parameter sorgt somit dafür, dass
286 ## die Anzahl der eingelesenen Zeilen in jedem Fall mit der Anzahl der ausgegebenen Zeilen
287 ## übereinstimmt.
289  local key="$1"
290  local default="${2:-}"
291  local service_name
292  local value
293  while read service_name; do
294  value=$(get_service_value "$service_name" "$key")
295  [ -z "$value" ] && value="$default"
296  [ -n "$value" ] && echo "$value" || true
297  done
298 }
299 
300 
301 ## @fn get_services()
302 ## @param service_type (optional) ein Service-Typ
303 ## @brief Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind.
304 ## Falls kein Typ angegben wird, dann werden alle Dienste ungeachtet ihres Typs ausgegeben.
305 get_services() {
306  trap "error_trap get_services '$*'" $GUARD_TRAPS
307  local service_type="${1:-}"
308  local services
309  local fname_persist
310  # alle Dienste ausgeben
311  # kein Dienste-Verzeichnis? Keine Ergebnisse ...
312  [ -e "$PERSISTENT_SERVICE_STATUS_DIR" ] || return 0
313  find "$PERSISTENT_SERVICE_STATUS_DIR" -type f -size +1c -print0 \
314  | xargs -0 -r -n 1 basename \
315  | if [ -n "$service_type" ]; then
316  filter_services_by_value "service" "$service_type"
317  else
318  cat -
319  fi
320 }
321 
322 
323 ## @fn filter_services_by_value()
324 ## @param key ein Schlüssel
325 ## @param value ein Wert
326 ## @details Als Parameter kann ein "key/value"-Schluesselpaar angegeben werden.
327 ## Nur diejenigen Dienste, auf die diese Bedingung zutrifft, werden zurueckgeliefert.
329  local key="$1"
330  local value="$2"
331  local service_name
332  while read service_name; do
333  [ "$value" != "$(get_service_value "$service_name" "$key")" ] || echo "$service_name"
334  done
335 }
336 
337 
338 # Setzen eines Werts fuer einen Dienst.
339 # Je nach Schluesselname wird der Inhalt in die persistente uci- oder
340 # die volatile tmpfs-Datenbank geschrieben.
341 set_service_value() {
342  local service_name="$1"
343  local attribute="$2"
344  local value="$3"
345  # unverändert? Schnell beenden
346  [ -n "$service_name" -a "$value" = "$(get_service_value "$service_name" "$attribute")" ] && return 0
347  [ -z "$service_name" ] \
348  && msg_error "No service given for attribute change ($attribute=$value)" \
349  && trap "" $GUARD_TRAPS && return 1
350  local dirname
351  if echo "$PERSISTENT_SERVICE_ATTRIBUTES" | grep -q -w "$attribute"; then
352  dirname="$PERSISTENT_SERVICE_STATUS_DIR"
353  else
354  dirname="$VOLATILE_SERVICE_STATUS_DIR"
355  fi
356  _set_file_dict_value "$dirname/$service_name" "$attribute" "$value"
357 }
358 
359 
360 ## @fn get_service_value()
361 ## @brief Auslesen eines Werts aus der Service-Datenbank.
362 ## @param key Der Name eines Dienst-Attributs
363 ## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
364 ## @details Falls das Attribut nicht existiert, wird ein leerer Text zurückgeliefert.
365 ## Es gibt keinen abschließenden Zeilenumbruch.
367  local service_name="$1"
368  local attribute="$2"
369  local default="${3:-}"
370  local value
371  local dirname
372  [ -z "$service_name" ] \
373  && msg_error "No service given for attribute request ('$attribute')" \
374  && trap "" $GUARD_TRAPS && return 1
375  value=$(_get_file_dict_value "$attribute" "$PERSISTENT_SERVICE_STATUS_DIR/$service_name" "$VOLATILE_SERVICE_STATUS_DIR/$service_name")
376  [ -n "$value" ] && echo -n "$value" || echo -n "$default"
377  return 0
378 }
379 
380 
381 # Liefere die Suffixe aller Schluessel aus der Service-Attribut-Datenbank,
382 # die mit dem gegebenen Praefix uebereinstimmen.
383 get_service_attributes() {
384  _get_file_dict_keys "$PERSISTENT_SERVICE_STATUS_DIR/$1" "$VOLATILE_SERVICE_STATUS_DIR/$1"
385 }
386 
387 
388 ## @fn print_services()
389 ## @brief menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
390 ## @param service_type (optional) ein Service-Type
391 ## @returns Ausgabe der bekannten Dienste (für Menschen - nicht parsebar)
392 print_services() {
393  trap "error_trap print_services '$*'" $GUARD_TRAPS
394  local service_name
395  local attribute
396  local value
397  get_services "$@" | while read service_name; do
398  echo "$service_name"
399  get_service_attributes "$service_name" | while read attribute; do
400  value=$(get_service_value "$service_name" "$attribute")
401  echo -e "\t$attribute=$value"
402  done
403  done
404  return 0
405 }
406 
407 
408 # Speichere das angegebene uci-Praefix als eine von einem Service abhaengige Konfiguration.
409 # Dies ist sinnvoll fuer abgeleitete VPN-Konfigurationen oder Portweiterleitungen.
410 # Schnittstelle: siehe _add_service_dependency
411 service_add_uci_dependency() {
412  _add_service_dependency "uci_dependency" "$@"
413 }
414 
415 
416 # Speichere einen Dateinamen als Abhaengigkeit eines Service.
417 # Dies ist sinnvoll fuer Dateien, die nicht mehr gebraucht werden, sobald der Service entfernt wird.
418 # Schnittstelle: siehe _add_service_dependency
419 service_add_file_dependency() {
420  _add_service_dependency "file_dependency" "$@"
421 }
422 
423 
424 # Speichere eine Abhaengigkeit fuer einen Dienst.
425 # Parameter: Service-Name
426 # Parameter: textuelle Darstellung einer Abhaengigkeit (ohne Leerzeichen)
427 _add_service_dependency() {
428  trap "error_trap _add_service_dependency '$*'" $GUARD_TRAPS
429  local dependency="$1"
430  local service_name="$2"
431  local token="$3"
432  local deps
433  local dep
434  deps=$(get_service_value "$service_name" "$dependency")
435  for dep in $deps; do
436  # schon vorhanden -> fertig
437  [ "$dep" = "$token" ] && return 0 || true
438  done
439  if [ -z "$deps" ]; then
440  deps="$token"
441  else
442  deps="$deps $token"
443  fi
444  set_service_value "$service_name" "$dependency" "$deps"
445 }
446 
447 
448 # Entferne alle mit diesem Service verbundenen Konfigurationen (inkl. Rekonfiguration von firewall, etc.).
449 cleanup_service_dependencies() {
450  trap "error_trap cleanup_service_dependencies '$*'" $GUARD_TRAPS
451  local service_name="$1"
452  local dep
453  local filename
454  local branch
455  # Dateien loeschen
456  for filename in $(get_service_value "$service_name" "file_dependency"); do
457  rm -f "$filename"
458  done
459  # uci-Sektionen loeschen
460  for dep in $(get_service_value "$service_name" "uci_dependency"); do
461  uci_delete "$dep"
462  # gib die oberste config-Ebene aus - fuer spaeteres "apply_changes"
463  echo "$dep" | cut -f 1 -d .
464  done | sort | uniq | while read branch; do
465  apply_changes "$branch"
466  done
467  set_service_value "$service_name" "uci_dependency" ""
468 }
469 
470 
471 delete_service() {
472  trap "error_trap delete_service '$*'" $GUARD_TRAPS
473  local service_name="$1"
474  [ -z "$service_name" ] && msg_error "No service given for deletion" && trap "" $GUARD_TRAPS && return 1
475  cleanup_service_dependencies "$service_name"
476  rm -f "$PERSISTENT_SERVICE_STATUS_DIR/$service_name"
477  rm -f "$VOLATILE_SERVICE_STATUS_DIR/$service_name"
478 }
479 
480 
481 # Durchlaufe alle Dienste und verteile Rangziffern ohne Doppelung an alle Dienste.
482 # Die Dienst-Arten (z.B. DNS und UGW) werden dabei nicht beachtet.
483 # Die Rangziffern sind anschliessend streng monoton aufsteigend - beginnend bei 1.
484 # Falls aktuell die manuelle Sortierung aktiv ist, wird deren Reihenfolge beibehalten.
485 # Ansonsten basiert die Vergabe der Rangziffern auf der Reihenfolge entsprechend der aktuell aktiven Sortierung.
486 _distribute_service_ranks() {
487  local service_name
488  local index=1
489  get_services | sort_services_by_priority | while read service_name; do
490  set_service_value "$service_name" "rank" "$index"
491  : $((index++))
492  done
493 }
494 
495 
496 ## @fn move_service_up()
497 ## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben
498 ## @param service_name der zu verschiebende Dienst
499 ## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
500 ## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
501 ## * manual: Verschiebung vor den davorplatzierten Dienst desselben Typs
502 ## * etx/hop: Reduzierung des Offsets um eins
503 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
504 move_service_up() {
505  trap "error_trap move_service_up '$*'" $GUARD_TRAPS
506  local service_name="$1"
507  shift
508  local sorting
509  local prev_service=
510  local current_service
511  local temp
512  sorting=$(get_service_sorting)
513  if [ "$sorting" = "hop" -o "$sorting" = "etx" ]; then
514  # reduziere den Offset um eins
515  temp=$(get_service_value "$service_name" "offset" 0)
516  temp=$(echo "$temp" | awk '{ print $1 - 1 }')
517  set_service_value "$service_name" "offset" "$temp"
518  elif [ "$sorting" = "manual" ]; then
519  get_services "$@" | sort_services_by_priority | while read current_service; do
520  if [ "$current_service" = "$service_name" ]; then
521  if [ -z "$prev_service" ]; then
522  # es gibt keinen Dienst oberhalb des zu verschiebenden
523  true
524  else
525  # wir verschieben den Dienst ueber den davor liegenden
526  temp=$(get_service_value "$prev_service" "rank" "$DEFAULT_SERVICE_RANK")
527  # ziehe einen halben Rang ab
528  temp=$(echo "$temp" | awk '{ print $1 - 0.5 }')
529  set_service_value "$service_name" "rank" "$temp"
530  # erneuere die Rang-Vergabe
531  _distribute_service_ranks
532  fi
533  # wir sind fertig
534  break
535  fi
536  prev_service="$current_service"
537  done
538  else
539  msg_info "Warning: [move_service_up] for this sorting method is not implemented: $sorting"
540  fi
541 }
542 
543 
544 ## @fn move_service_down()
545 ## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten
546 ## @param service_name der zu verschiebende Dienst
547 ## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
548 ## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
549 ## * manual: Verschiebung hinter den dahinterliegenden Dienst desselben Typs
550 ## * etx/hop: Erhöhung des Offsets um eins
551 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
553  trap "error_trap move_service_down '$*'" $GUARD_TRAPS
554  local service_name="$1"
555  shift
556  local sorting
557  local prev_service=
558  local current_service
559  local temp
560  sorting=$(get_service_sorting)
561  if [ "$sorting" = "hop" -o "$sorting" = "etx" ]; then
562  # reduziere den Offset um eins
563  temp=$(get_service_value "$service_name" "offset" 0)
564  temp=$(echo "$temp" | awk '{ print $1 + 1 }')
565  set_service_value "$service_name" "offset" "$temp"
566  elif [ "$sorting" = "manual" ]; then
567  get_services "$@" | sort_services_by_priority | while read current_service; do
568  if [ "$prev_service" = "$service_name" ]; then
569  # wir verschieben den Dienst hinter den danach liegenden
570  temp=$(get_service_value "$current_service" "rank" "$DEFAULT_SERVICE_RANK")
571  # fuege einen halben Rang hinzu
572  temp=$(echo "$temp" | awk '{ print $1 + 0.5 }')
573  set_service_value "$service_name" "rank" "$temp"
574  # erneuere die Rang-Vergabe
575  _distribute_service_ranks
576  # wir sind fertig
577  break
578  fi
579  prev_service="$current_service"
580  done
581  else
582  msg_info "Warning: [move_service_down] for this sorting method is not implemented: $sorting"
583  fi
584 }
585 
586 
587 ## @fn move_service_top()
588 ## @brief Verschiebe einen Dienst an die Spitze der Dienst-Sortierung
589 ## @param service_name der zu verschiebende Dienst
590 ## @param service_types ein oder mehrere Dienst-Typen, auf die die Ermittlung der Dienst-Liste begrenzt werden soll (z.B. "gw")
591 ## @details Der Dienst steht anschließend direkt vor dem bisher führenden Dienst der ausgewählten Typen (falls angegeben).
592 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste an die Spitze.
594  trap "error_trap move_service_top '$*'" $GUARD_TRAPS
595  local service_name="$1"
596  shift
597  local top_service
598  local sorting
599  local top_rank
600  local new_rank
601  local top_distance
602  local our_distance
603  local current_offset
604  local new_offset
605  top_service=$(get_services "$@" | sort_services_by_priority | head -1)
606  sorting=$(get_service_sorting)
607  # kein top-Service oder wir sind bereits ganz oben -> Ende
608  [ -z "$top_service" -o "$top_service" = "$service_name" ] && return 0
609  if [ "$sorting" = "hop" -o "$sorting" = "etx" ]; then
610  top_distance=$(get_distance_with_offset "$top_service" "$sorting")
611  our_distance=$(get_distance_with_offset "$service_name" "$sorting")
612  [ -z "$our_distance" ] && msg_info "Failed to move unreachable service ('$service_name') to top" && return 0
613  current_offset=$(get_service_value "$service_name" "offset" 0)
614  # wir verschieben unseren Offset, auf dass wir knapp ueber "top" stehen
615  new_offset=$(echo | awk "{ print $current_offset + int($top_distance - $our_distance) - 1 }")
616  set_service_value "$service_name" "offset" "$new_offset"
617  elif [ "$sorting" = "manual" ]; then
618  # setze den Rang des Dienstes auf den top-Dienst minus 0.5
619  top_rank=$(get_service_value "$top_service" "rank" "$DEFAULT_SERVICE_RANK")
620  new_rank=$(echo "$top_rank" | awk '{ print $1 - 0.5 }')
621  set_service_value "$service_name" "rank" "$new_rank"
622  # erneuere die Rang-Vergabe
623  _distribute_service_ranks
624  else
625  msg_info "Warning: [move_service_top] for this sorting method is not implemented: $sorting"
626  fi
627 }
628 
629 
630 ## @fn get_service_detail()
631 ## @brief Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
632 ## @param service_name Name eines Diensts
633 ## @param key Name des Schlüssels
634 ## @param default dieser Wert wird zurückgeliefert, falls der Schlüssel nicht gefunden wurde
635 ## @returns den ermittelten Wert aus dem Schlüssel-Wert-Paar
637  local service_name="$1"
638  local key="$2"
639  local default="${3:-}"
640  local value
641  value=$(get_service_value "$service_name" "details" | get_from_key_value_list "$key" ":")
642  [ -n "$value" ] && echo -n "$value" || echo -n "$default"
643  return 0
644 }
645 
646 
647 ## @fn set_service_detail()
648 ## @brief Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
649 ## @param service_name Name eines Diensts
650 ## @param key Name des Schlüssels
651 ## @param value der neue Wert
652 ## @details Ein leerer Wert löscht das Schlüssel-Wert-Paar.
654  local service_name="$1"
655  local key="$2"
656  local value="$3"
657  local new_details
658  new_details=$(get_service_value "$service_name" "details" | replace_in_key_value_list "$key" ":" "$value")
659  set_service_value "$service_name" "details" "$new_details"
660  return 0
661 }
662 
663 
664 # Liefere eine Semikolon-separierte Liste von Service-Eigenschaften zurueck.
665 # Jede Eigenschaft wird folgendermassen ausgedrueckt:
666 # type|source|key[|default]
667 # Dabei sind folgende Inhalte moeglich:
668 # type: Rueckgabetyp (string, number, bool)
669 # source: Quelle der Informationen (value, detail, function, id)
670 # key: Name des Werts, des Details oder der Funktion
671 # default: Standardwert, falls das Ergebnis leer sein sollte
672 # Wahrheitswerte werden als "true" oder "false" zurueckgeliefert. Alle anderen Rueckgabetypen bleiben unveraendert.
673 # Das Ergebnis sieht folgendermassen aus:
674 # SERVICE_NAME;RESULT1;RESULT2;...
675 get_service_as_csv() {
676  local service_name="$1"
677  shift
678  local separator=";"
679  local specification
680  local rtype
681  local source
682  local key
683  local default
684  local value
685  local func_call
686  # Abbruch mit Fehler bei unbekanntem Dienst
687  is_existing_service "$service_name" || { trap "" $GUARD_TRAPS && return 1; }
688  echo -n "$service_name"
689  for specification in "$@"; do
690  rtype=$(echo "$specification" | cut -f 1 -d "|")
691  source=$(echo "$specification" | cut -f 2 -d "|")
692  key=$(echo "$specification" | cut -f 3 -d "|")
693  default=$(echo "$specification" | cut -f 4- -d "|")
694  # Ermittlung des Funktionsaufrufs
695  if [ "$source" = "function" ]; then
696  if [ "$rtype" = "bool" ]; then
697  "$key" "$service_name" && value="true" || value="false"
698  else
699  value=$("$key" "$service_name")
700  fi
701  else
702  if [ "$source" = "value" ]; then
703  value=$(get_service_value "$service_name" "$key")
704  elif [ "$source" = "detail" ]; then
705  value=$(get_service_detail "$service_name" "$key")
706  else
707  msg_error "Unknown service attribute requested: $specification"
708  echo -n "${separator}"
709  continue
710  fi
711  [ -z "$value" ] && [ -n "$default" ] && value="$default"
712  if [ "$rtype" = "bool" ]; then
713  # Pruefung auf wahr/falsch
714  value=$(uci_is_true "$value" && echo "true" || echo "false")
715  fi
716  fi
717  echo -n "${separator}${value}"
718  done
719  # mit Zeilenumbruch abschliessen
720  echo
721 }
722 
723 
725 ## @brief Ermittle den Namen der Log-Datei für diesen Dienst. Zusätzliche Details (z.B. "openvpn mtu") sind möglich.
726 ## @param service Name eines Dienstes.
727 ## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
728 ## @details Die Funktion stellt sicher, dass das Verzeichnis der ermittelten Log-Datei anschließend existiert.
730  trap "error_trap get_service_log_filename '$*'" $GUARD_TRAPS
731  local service_name="$1"
732  shift
733  local filename="$service_name"
734  while [ $# -gt 0 ]; do
735  filename="${filename}.$1"
736  shift
737  done
738  local full_filename="$SERVICES_LOG_BASE/$(get_safe_filename "$filename").log"
739  mkdir -p "$(dirname "$full_filename")"
740  echo -n "$full_filename"
741 }
742 
743 
744 ## @fn get_service_log_content()
745 ## @brief Lies den Inhalt einer Log-Datei für einen Dienst aus.
746 ## @param service Name eines Dienstes.
747 ## @param max_lines maximale Anzahl der auszuliefernden Zeilen (unbegrenzt: 0)
748 ## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
749 ## @see get_service_log_filename
751  trap "error_trap get_service_log_content '$*'" $GUARD_TRAPS
752  local service_name="$1"
753  local max_lines="$2"
754  shift 2
755  local log_filename=$(get_service_log_filename "$service_name" "$@")
756  [ -e "$log_filename" ] || return 0
757  if [ "$max_lines" = "0" ]; then
758  # alle Einträge ausgeben
759  cat -
760  else
761  # nur die letzten Einträge ausliefern
762  tail -n "$max_lines"
763  fi <"$log_filename"
764 }
765 
766 
767 ## @fn is_service_routed_via_wan()
768 ## @brief Pruefe ob der Verkehr zum Anbieter des Diensts über ein WAN-Interface verlaufen würde.
769 ## @param service_name der Name des Diensts
770 ## @returns Exitcode == 0, falls das Routing über das WAN-Interface verläuft.
772  trap "error_trap is_service_routed_via_wan '$*'" $GUARD_TRAPS
773  local service_name="$1"
774  # verwende die übergebene TypeOfService-Angabe oder (falls vorhanden/installiert) den
775  # TOS-Wert, der fuer nicht-Tunnel-Verkehr verwendet wird (typischerweise ist dies die
776  # die Intention des Anfragenden)
777  local tos_field="${2:-}"
778  [ -z "$tos_field" ] && tos_field="${TOS_NON_TUNNEL:-}"
779  local host
780  local outgoing_device
781  local outgoing_zone
782  host=$(get_service_value "$service_name" "host")
783  outgoing_device=$(get_target_route_interface "$host" "$tos_field")
784  if is_device_in_zone "$outgoing_device" "$ZONE_WAN"; then
785  msg_debug "target '$host' routing through wan device: $outgoing_device"
786  return 0
787  else
788  outgoing_zone=$(get_zone_of_device "$outgoing_device")
789  msg_debug "warning: target '$host' is routed via interface '$outgoing_device' (zone '$outgoing_zone') instead of the expected WAN zone ($ZONE_WAN)"
790  trap "" $GUARD_TRAPS && return 1
791  fi
792 }
793 
794 
795 _notify_service_success() {
796  local service_name="$1"
797  set_service_value "$service_name" "status" "true"
798  set_service_value "$service_name" "status_fail_counter" ""
799  set_service_value "$service_name" "status_timestamp" "$(get_uptime_minutes)"
800 }
801 
802 
803 _notify_service_failure() {
804  local service_name="$1"
805  local max_fail_attempts="$2"
806  # erhoehe den Fehlerzaehler
807  local fail_counter
808  fail_counter=$(( $(get_service_value "$service_name" "status_fail_counter" "0") + 1))
809  set_service_value "$service_name" "status_fail_counter" "$fail_counter"
810  # Pruefe, ob der Fehlerzaehler gross genug ist, um seinen Status auf "fail" zu setzen.
811  if [ "$fail_counter" -ge "$max_fail_attempts" ]; then
812  # Die maximale Anzahl von aufeinanderfolgenden fehlgeschlagenen Tests wurde erreicht:
813  # markiere ihn als kaputt.
814  set_service_value "$service_name" "status" "false"
815  elif uci_is_true "$(get_service_value "$service_name" "status")"; then
816  # Bisher galt der Dienst als funktionsfaehig - wir setzen ihn auf "neutral" bis
817  # die maximale Anzahl aufeinanderfolgender Fehler erreicht ist.
818  set_service_value "$service_name" "status" ""
819  else
820  # Der Test gilt wohl schon als fehlerhaft - das kann so bleiben.
821  true
822  fi
823  set_service_value "$service_name" "status_timestamp" "$(get_uptime_minutes)"
824 }
825 
826 
827 ## @fn run_cyclic_service_tests()
828 ## @brief Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist
829 ## @param test_function der Name der zu verwendenden Test-Funktion für einen Dienst (z.B. "verify_vpn_connection")
830 ## @param test_period_minutes Wiederholungsperiode der Dienst-Prüfung
831 ## @param max_fail_attempts Anzahl von Fehlversuchen, bis ein Dienst von "gut" oder "unklar" zu "schlecht" wechselt
832 ## @details Die Diensteanbieter werden in der Reihenfolge ihrer Priorität geprüft.
833 ## Nach dem ersten Durchlauf dieser Funktion sollte typischerweise der nächstgelegene nutzbare Dienst
834 ## als funktionierend markiert sein.
835 ## Falls nach dem Durchlauf aller Dienste keiner positiv getestet wurde (beispielsweise weil alle Zeitstempel zu frisch sind),
836 ## dann wird in jedem Fall der älteste nicht-funktionsfähige Dienst getestet. Dies minimiert die Ausfallzeit im
837 ## Falle einer globalen Nicht-Erreichbarkeit aller Dienstenanbieter ohne auf den Ablauf der Test-Periode warten zu müssen.
838 ## @attention Seiteneffekt: die Zustandsinformationen des getesteten Diensts (Status, Test-Zeitstempel) werden verändert.
840  trap "error_trap test_openvpn_service_type '$*'" $GUARD_TRAPS
841  local test_function="$1"
842  local test_period_minutes="$2"
843  local max_fail_attempts="$3"
844  local service_name
845  local timestamp
846  local status
850  | while read service_name; do
851  timestamp=$(get_service_value "$service_name" "status_timestamp" "0")
852  status=$(get_service_value "$service_name" "status")
853  if [ -z "$status" ] || is_timestamp_older_minutes "$timestamp" "$test_period_minutes"; then
854  if "$test_function" "$service_name"; then
855  msg_debug "service $service_name successfully tested"
856  _notify_service_success "$service_name"
857  # wir sind fertig - keine weiteren Tests
858  return
859  else
860  msg_debug "failed to verify $service_name"
861  _notify_service_failure "$service_name" "$max_fail_attempts"
862  fi
863  elif uci_is_false "$status"; then
864  # Junge "kaputte" Dienste sind potentielle Kandidaten fuer einen vorzeitigen Test, falls
865  # ansonsten kein Dienst positiv getestet wurde.
866  echo "$timestamp $service_name"
867  else
868  # funktionsfaehige "alte" Dienste - es gibt nichts fuer sie zu tun
869  # Wir sortieren sie nach ganz oben, um bei Existenz eines lauffaehigen Diensts
870  # keine unnoetigen Tests von nicht-funktionierenden Hosts durchzufuehren.
871  echo "-1 $service_name"
872  fi
873  done | sort -n | while read timestamp service_name; do
874  # Mit dem Zeitstempel "-1" sind funktionierende Dienste markiert. Wir brauchen also keine
875  # weiteren Tests.
876  [ "$timestamp" = "-1" ] && return 0
877  # Hier landen wir nur, falls alle defekten Gateways zu jung fuer einen Test waren und
878  # gleichzeitig kein Dienst erfolgreich getestet wurde bzw. als erfolgreich gilt.
879  # Dies stellt sicher, dass nach einer kurzen Nicht-Erreichbarkeit aller Gateways (z.B. olsr-Ausfall)
880  # relativ schnell wieder ein funktionierender Gateway gefunden wird, obwohl alle Test-Zeitstempel noch recht
881  # frisch sind.
882  msg_debug "there is no service to be tested - thus we pick the service with the oldest test timestamp: $service_name"
883  "$test_function" "$service_name" \
884  && _notify_service_success "$service_name" \
885  || _notify_service_failure "$service_name" "$max_fail_attempts"
886  # wir wollen nur genau einen Test durchfuehren
887  break
888  done
889 }
890 
891 # Ende der Doku-Gruppe
892 ## @}
get_hop_count_and_etx(host)
Liefere den Hop-Count und den ETX-Wert für einen Zielhost zurück.
Definition: routing.sh:61
uci_delete(uci_path)
Lösche ein UCI-Element.
Definition: uci.sh:38
get_service_log_filename()
Ermittle den Namen der Log-Datei für diesen Dienst. Zusätzliche Details (z.B. "openvpn mtu"...
Definition: services.sh:124
get_services(service_type)
Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind. Falls kein Typ angegben wird...
Definition: services.sh:61
notify_service()
Aktualisiere den Zeitstempel und die Entfernung (etx) eines Dienstes.
Definition: services.sh:11
move_service_down(service_name, service_type)
Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten.
Definition: services.sh:97
_get_local_bias_for_service()
Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1, service_name) - abhängig von der l...
Definition: services.sh:27
get_service_log_content(service, max_lines)
Lies den Inhalt einer Log-Datei für einen Dienst aus.
Definition: services.sh:131
while read key value
Definition: core.sh:81
get_zone_of_device(interface)
Ermittle die Zone eines physischen Netzwerk-Interfaces.
Definition: network.sh:48
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
move_service_top(service_name, service_types)
Verschiebe einen Dienst an die Spitze der Dienst-Sortierung.
Definition: services.sh:104
pipe_service_attribute(key, default)
Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück. ...
Definition: services.sh:56
local key
Definition: core.sh:81
_get_file_dict_value(key)
Auslesen eines Werts aus einem Schlüssel/Wert-Eingabestrom.
Definition: core.sh:81
run_cyclic_service_tests(test_function)
Durchlaufe alle via STDIN angegebenen Dienste bis mindestens ein Test erfolgreich ist...
Definition: services.sh:149
sort_services_by_priority()
Sortiere den eingegebenen Strom von Dienstnamen und gib eine nach der Priorität sortierte Liste...
Definition: services.sh:31
filter_services_by_value(key, value)
Definition: services.sh:67
move_service_up(service_name, service_type)
Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben.
Definition: services.sh:88
msg_info(message)
Informationen und Fehlermeldungen ins syslog schreiben.
Definition: core.sh:15
set_service_detail(service_name, key, value)
Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts.
Definition: services.sh:118
print_services(service_type)
menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
Definition: services.sh:79
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
get_service_detail(service_name, key, default)
Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts. ...
Definition: services.sh:111
is_service_routed_via_wan(service_name)
Pruefe ob der Verkehr zum Anbieter des Diensts über ein WAN-Interface verlaufen würde.
Definition: services.sh:136
update_service_routing_distance(service_name)
Aktualisiere Routing-Entfernung und Hop-Count eines Dienst-Anbieters.
Definition: services.sh:17
done
Definition: core.sh:81
get_service_name()
Ermittle en Namen eines Diensts basierend auf den Dienst-Attributen.
Definition: services.sh:7
get_target_route_interface(target)
Ermittle das Netzwerkinterface, über den der Verkehr zu einem Ziel laufen würde.
Definition: routing.sh:26
is_existing_service(service_name)
Prüfe ob ein Service existiert.
Definition: services.sh:22