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_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_port"
21 LOCAL_BIAS_MODULO=10
22 SERVICES_LOG_BASE=/var/log/on-services
23 
24 
25 get_service_name() {
26  local service="$1"
27  local scheme="$2"
28  local host="$3"
29  local port="$4"
30  local protocol="$5"
31  local path="$6"
32  local name="${service}_${scheme}_${host}_${port}_${protocol}"
33  [ -n "${path#/}" ] && name="${name}_${path#/}"
34  echo "$name" | sed 's/[^A-Za-z0-9_]/_/g'
35 }
36 
37 
38 # Aktualisiere den Zeitstempel und die Entfernung (etx) eines Dienstes
39 notify_service() {
40  trap "error_trap notify_service '$*'" $GUARD_TRAPS
41  local service="$1"
42  local scheme="$2"
43  local host="$3"
44  local port="$4"
45  local protocol="$5"
46  local path="$6"
47  local details="$7"
48  local source="$8"
49  local service_name=$(get_service_name "$service" "$scheme" "$host" "$port" "$protocol" "$path")
50  if ! is_existing_service "$service_name"; then
51  # diese Attribute sind Bestandteil des Namens und aendern sich eigentlich nicht
52  set_service_value "$service_name" "service" "$service"
53  set_service_value "$service_name" "scheme" "$scheme"
54  set_service_value "$service_name" "host" "$host"
55  set_service_value "$service_name" "port" "$port"
56  set_service_value "$service_name" "protocol" "$protocol"
57  set_service_value "$service_name" "path" "$path"
58  fi
59  # dies sind die flexiblen Attribute
60  set_service_value "$service_name" "details" "$details"
61  set_service_value "$service_name" "timestamp" "$(get_time_minute)"
62  set_service_value "$service_name" "source" "$source"
63  update_service_routing_distance "$service_name"
64 }
65 
66 
67 ## @fn update_service_routing_distance()
68 ## @brief Aktualisiere Routing-Entfernung und Hop-Count eines Dienst-Anbieters
69 ## @param service_name der zu aktualisierende Dienst
70 ## @details Beide Dienst-Werte werden gelöscht, falls der Host nicht route-bar sein sollte.
71 ## Diese Funktion sollte regelmäßig für alle Hosts ausgeführt werden.
73  trap "error_trap update_service_routing_distance '$*'" $GUARD_TRAPS
74  local service_name="$1"
75  local hop
76  local etx
77  get_hop_count_and_etx "$(get_service_value "$service_name" "host")" | while read hop etx; do
78  set_service_value "$service_name" "distance" "$etx"
79  set_service_value "$service_name" "hop_count" "$hop"
80  done
81 }
82 
83 
84 ## @fn is_existing_service()
85 ## @brief Prüfe ob ein Service existiert
86 ## @param service_name der Name des Diensts
87 ## @returns exitcode=0 falls der Dienst existiert
89  local service_name="$1"
90  [ -n "$service_name" -a -e "$PERSISTENT_SERVICE_STATUS_DIR/$service_name" ] && return 0
91  trap "" $GUARD_TRAPS && return 1
92 }
93 
94 
95 # Ermittle eine reproduzierbare Zahl von 0 bis (LOCAL_BIAS_MODULO-1) - abhaengig von der lokalen IP und der IP der Gegenstelle.
96 # Dadurch koennen wir beim Sortieren strukturelle Ungleichgewichte (z.B. durch alphabetische Sortierung) verhindern.
97 _get_local_bias_for_host() {
98  local ip="$1"
99  # Die resultierende host_number darf nicht zu gross sein (z.B. mit Exponentendarstellung),
100  # da andernfalls awk die Berechnung fehlerhaft durchführt.
101  local host_number=$(echo "$ip$(get_local_bias_number)" | md5sum | sed 's/[^0-9]//g')
102  # Laenge von 'host_number' reduzieren (die Berechnung schlaegt sonst fehl)
103  # Wir fuegen die 1 an den Beginn, um die Interpretation als octal-Zahl zu verhindern (fuehrende Null).
104  echo $(( 1${host_number:0:6} % LOCAL_BIAS_MODULO))
105 }
106 
107 
108 # Ermittle die Service-Prioritaet eines Dienstes.
109 # Der Wert ist beliebig und nur im Vergleich mit den Prioritaeten der anderen Dienste verwendbar.
110 # Als optionaler zweiter Parameter kann die Sortierung uebergeben werden. Falls diese nicht uebergeben wurde,
111 # wird die aktuell konfigurierte Sortierung benutzt.
112 # Sollte ein Dienst ein "priority"-Attribut tragen, dann wird die uebliche Dienst-Sortierung aufgehoben
113 # und lediglich "priority" (und gegebenenfalls separat "offset") beachtet.
114 get_service_priority() {
115  trap "error_trap get_service_priority '$*'" $GUARD_TRAPS
116  local service_name="$1"
117  local sorting="${2:-}"
118  local priority=$(get_service_value "$service_name" "priority")
119  local rank
120  # priority wird von nicht-olsr-Clients verwendet (z.B. mesh-Gateways mit oeffentlichen IPs)
121  local base_priority=$(
122  if [ -n "$priority" ]; then
123  # dieses Ziel traegt anscheinend keine Routing-Metrik
124  local offset=$(get_service_value "$service_name" "offset" "0")
125  echo "$((priority + offset))"
126  else
127  # wir benoetigen Informationen fuer Ziele mit Routing-Metriken
128  # aus Performance-Gruenden kommt die Sortierung manchmal von aussen
129  [ -z "$sorting" ] && sorting=$(get_service_sorting)
130  if [ "$sorting" = "etx" -o "$sorting" = "hop" ]; then
131  get_distance_with_offset "$service_name" "$sorting"
132  elif [ "$sorting" = "manual" ]; then
133  get_service_value "$service_name" "rank" "$DEFAULT_SERVICE_RANK"
134  else
135  msg_info "Unknown sorting method for services: $sorting"
136  echo 1
137  fi
138  fi | cut -f 1 -d .)
139  local host_bias=$(_get_local_bias_for_host "$(get_service_value "$service_name" "host")")
140  echo $(( ${base_priority:-$DEFAULT_SERVICE_RANK} * 1000 + host_bias))
141 }
142 
143 
144 get_distance_with_offset() {
145  trap "error_trap get_distance_with_offset '$*'" $GUARD_TRAPS
146  local service_name="$1"
147  local sorting="${2:-}"
148  # aus Performance-Gruenden wird manchmal das sorting von aussen vorgegeben
149  [ -z "$sorting" ] && sorting=$(get_service_sorting)
150  local distance=$(get_service_value "$service_name" "distance")
151  local base_value=
152  [ -z "$distance" ] && return 0
153  local offset=$(get_service_value "$service_name" "offset")
154  [ -z "$offset" ] && offset=0
155  if [ "$sorting" = "etx" ]; then
156  base_value="$distance"
157  elif [ "$sorting" = "hop" ]; then
158  base_value=$(get_service_value "$service_name" "hop_count")
159  else
160  msg_debug "get_distance_with_offset: sorting '$sorting' not implemented"
161  fi
162  [ -n "$base_value" ] && echo "$base_value" "$offset" | awk '{ print $1 + $2 }'
163  return 0
164 }
165 
166 
167 set_service_sorting() {
168  trap "error_trap set_service_sorting '$*'" $GUARD_TRAPS
169  local new_sorting="$1"
170  local old_sorting=$(get_service_sorting)
171  [ "$old_sorting" = "$new_sorting" ] && return 0
172  [ "$new_sorting" != "manual" -a "$new_sorting" != "hop" -a "$new_sorting" != "etx" ] && \
173  msg_info "Warning: Ignoring unknown sorting method: $new_sorting" && \
174  trap "" $GUARD_TRAPS && return 1
175  uci set "on-core.settings.service_sorting=$new_sorting"
176  apply_changes on-core
177 }
178 
179 
180 # Liefere die aktuelle Sortier-Methode.
181 # Falls eine ungueltige Sortier-Methode gesetzt ist, wird diese auf die Standard-Sortierung zurueckgesetzt.
182 # Die Ausgabe dieser Funktion ist also in jedem Fall eine gueltige Sortier-Methode.
183 get_service_sorting() {
184  trap "error_trap get_service_sorting '$*'" $GUARD_TRAPS
185  local sorting=$(uci_get "on-core.settings.service_sorting")
186  if [ "$sorting" = "manual" -o "$sorting" = "hop" -o "$sorting" = "etx" ]; then
187  # zulaessige Sortierung
188  echo -n "$sorting"
189  else
190  # unbekannte Sortierung: dauerhaft setzen
191  # keine Warnung falls die Sortierung nicht gesetzt wurde
192  [ -n "$sorting" ] && msg_info "Warning: coercing unknown sorting method: $sorting -> $DEFAULT_SERVICE_SORTING"
193  uci set "on-core.settings.service_sorting=$DEFAULT_SERVICE_SORTING"
194  echo -n "$DEFAULT_SERVICE_SORTING"
195  fi
196  return 0
197 }
198 
199 
200 sort_services_by_priority() {
201  trap "error_trap sort_services_by_priority '$*'" $GUARD_TRAPS
202  local service_name
203  local priority
204  local sorting=$(get_service_sorting)
205  while read service_name; do
206  priority=$(get_service_priority "$service_name" "$sorting")
207  # keine Entfernung (nicht erreichbar) -> ganz nach hinten sortieren (schmutzig, aber wohl ausreichend)
208  [ -z "$priority" ] && priority=999999999999999
209  echo "$priority" "$service_name"
210  done | sort -n | awk '{print $2}'
211 }
212 
213 
214 ## @fn sort_services_by()
215 ## @brief Sortiere den eingegebenen Strom von Dienstnamen und gib eine sortierte Liste entsprechende des Arguments aus.
216 ## @param sort_key Der Dienst-Wert, anhand dessen die Auswertung und Sortierung stattfinden soll.
218  trap "error_trap sort_services_by '$*'" $GUARD_TRAPS
219  local sort_key="$1"
220  local service_name
221  while read service_name; do
222  value=$(get_service_value "$service_name" "$sort_key" "_")
223  echo "$value" "$service_name"
224  done | sort -n | cut -f 2- -d " "
225 }
226 
227 
229 ## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die erreichbar sind.
230 ## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an die Standardausgabe
231 ## weitergeleitet, falls der Dienst erreichbar sind. "Erreichbarkeit" gilt als erreicht, wenn
232 ## der Host via olsr route-bar ist oder wenn er als DNS-entdeckter Dienst eine Priorität hat
233 ## oder wenn er manuell hinzugefügt wurde.
235  local service_name
236  while read service_name; do
237  { [ -n "$(get_service_value "$service_name" "distance")" ] \
238  || [ -n "$(get_service_value "$service_name" "priority")" ] \
239  || [ "$(get_service_value "$service_name" "source")" = "manual" ]
240  } && echo "$service_name"
241  true
242  done
243 }
244 
245 
246 ## @fn filter_enabled_services()
247 ## @brief Filtere aus einer Reihe eingehender Dienste diejenigen heraus, die nicht manuell ausgeblendet wurden.
248 ## @details Die Dienst-Namen werden über die Standardeingabe gelesen und an
249 ## die Standardausgabe weitergeleitet, falls der Dienst nicht abgewählt wurde.
251  local service_name
252  local disabled
253  while read service_name; do
254  disabled=$(get_service_value "$service_name" "disabled")
255  [ -n "$disabled" ] && uci_is_true "$disabled" && continue
256  echo "$service_name"
257  done
258 }
259 
260 
261 ## @fn pipe_service_attribute()
262 ## @brief Liefere zu einer Reihe von Diensten ein gewähltes Attribut dieser Dienste zurück.
263 ## @param key Der Name eines Dienst-Attributs
264 ## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
265 ## @details Die Dienstenamen werden via Standardeingabe erwartet. Auf der Standardausgabe wird für
266 ## einen Dienst entweder ein Wert oder nichts ausgeliefert. Keine Ausgabe erfolgt, falls der
267 ## Wert des Dienste-Attributs leer ist. Bei der Eingabe von mehreren Diensten werden also
268 ## eventuell weniger Zeilen ausgegeben, als eingelesen wurden.
269 ## Falls der optionale zweite 'default'-Parameter nicht leer ist, dann wird bei einem leeren
270 ## Ergebnis stattdessen dieser Wert ausgegeben. Der 'default'-Parameter sorgt somit dafür, dass
271 ## die Anzahl der eingelesenen Zeilen in jedem Fall mit der Anzahl der ausgegebenen Zeilen
272 ## übereinstimmt.
274  local key="$1"
275  local default="${2:-}"
276  local service_name
277  local value
278  while read service_name; do
279  value=$(get_service_value "$service_name" "$key")
280  [ -z "$value" ] && value="$default"
281  [ -n "$value" ] && echo "$value" || true
282  done
283 }
284 
285 
286 ## @fn get_services()
287 ## @param service_type (optional) ein Service-Typ
288 ## @brief Liefere alle Dienste zurueck, die dem angegebenen Typ zugeordnet sind.
289 ## Falls kein Typ angegben wird, dann werden alle Dienste ungeachtet ihres Typs ausgegeben.
290 get_services() {
291  trap "error_trap get_services '$*'" $GUARD_TRAPS
292  local services
293  local fname_persist
294  # alle Dienste ausgeben
295  # kein Dienste-Verzeichnis? Keine Ergebnisse ...
296  [ -e "$PERSISTENT_SERVICE_STATUS_DIR" ] || return 0
297  find "$PERSISTENT_SERVICE_STATUS_DIR" -type f -size +1c -print0 \
298  | xargs -0 -r -n 1 basename \
299  | if [ $# -gt 0 ]; then
300  filter_services_by_value "service" "$1"
301  else
302  cat -
303  fi
304 }
305 
306 
307 ## @fn filter_services_by_value()
308 ## @param key ein Schlüssel
309 ## @param value ein Wert
310 ## @details Als Parameter kann ein "key/value"-Schluesselpaar angegeben werden.
311 ## Nur diejenigen Dienste, auf die diese Bedingung zutrifft, werden zurueckgeliefert.
313  local key="$1"
314  local value="$2"
315  local service_name
316  while read service_name; do
317  [ "$value" = "$(get_service_value "$service_name" "$key")" ] && echo "$service_name" || true
318  done
319 }
320 
321 
322 # Setzen eines Werts fuer einen Dienst.
323 # Je nach Schluesselname wird der Inhalt in die persistente uci- oder
324 # die volatile tmpfs-Datenbank geschrieben.
325 set_service_value() {
326  local service_name="$1"
327  local attribute="$2"
328  local value="$3"
329  local dirname
330  [ -z "$service_name" ] \
331  && msg_info "Error: no service given for attribute change ($attribute=$value)" \
332  && trap "" $GUARD_TRAPS && return 1
333  if echo "$PERSISTENT_SERVICE_ATTRIBUTES" | grep -q -w "$attribute"; then
334  dirname="$PERSISTENT_SERVICE_STATUS_DIR"
335  else
336  dirname="$VOLATILE_SERVICE_STATUS_DIR"
337  fi
338  mkdir -p "$dirname"
339  _set_file_dict_value "$dirname/$service_name" "$attribute" "$value"
340 }
341 
342 
343 ## @fn get_service_value()
344 ## @brief Auslesen eines Werts aus der Service-Datenbank.
345 ## @param key Der Name eines Dienst-Attributs
346 ## @param default Der Standard-Wert wird anstelle des Attribut-Werts verwendet, falls dieser leer ist.
347 ## @details Falls das Attribut nicht existiert, wird ein leerer Text zurückgeliefert.
348 ## Es gibt keinen abschließenden Zeilenumbruch.
350  local service_name="$1"
351  local attribute="$2"
352  local default="${3:-}"
353  local value
354  local dirname
355  [ -z "$service_name" ] \
356  && msg_info "Error: no service given for attribute request ('$attribute')" \
357  && trap "" $GUARD_TRAPS && return 1
358  value=$(_get_file_dict_value "$attribute" "$PERSISTENT_SERVICE_STATUS_DIR/$service_name" "$VOLATILE_SERVICE_STATUS_DIR/$service_name")
359  [ -n "$value" ] && echo -n "$value" || echo -n "$default"
360  return 0
361 }
362 
363 
364 # Liefere die Suffixe aller Schluessel aus der Service-Attribut-Datenbank,
365 # die mit dem gegebenen Praefix uebereinstimmen.
366 get_service_attributes() {
367  _get_file_dict_keys "$PERSISTENT_SERVICE_STATUS_DIR/$1" "$VOLATILE_SERVICE_STATUS_DIR/$1"
368 }
369 
370 
371 ## @fn print_services()
372 ## @brief menschenfreundliche Ausgabe der aktuell angemeldeten Dienste
373 ## @param service_type (optional) ein Service-Type
374 ## @returns Ausgabe der bekannten Dienste (für Menschen - nicht parsebar)
375 print_services() {
376  trap "error_trap print_services '$*'" $GUARD_TRAPS
377  local service_name
378  local attribute
379  local value
380  get_services "$@" | while read service_name; do
381  echo "$service_name"
382  get_service_attributes "$service_name" | while read attribute; do
383  value=$(get_service_value "$service_name" "$attribute")
384  echo -e "\t$attribute=$value"
385  done
386  done
387  return 0
388 }
389 
390 
391 # Speichere das angegebene uci-Praefix als eine von einem Service abhaengige Konfiguration.
392 # Dies ist sinnvoll fuer abgeleitete VPN-Konfigurationen oder Portweiterleitungen.
393 # Schnittstelle: siehe _add_service_dependency
394 service_add_uci_dependency() {
395  _add_service_dependency "uci_dependency" "$@"
396 }
397 
398 
399 # Speichere einen Dateinamen als Abhaengigkeit eines Service.
400 # Dies ist sinnvoll fuer Dateien, die nicht mehr gebraucht werden, sobald der Service entfernt wird.
401 # Schnittstelle: siehe _add_service_dependency
402 service_add_file_dependency() {
403  _add_service_dependency "file_dependency" "$@"
404 }
405 
406 
407 # Speichere eine Abhaengigkeit fuer einen Dienst.
408 # Parameter: Service-Name
409 # Parameter: textuelle Darstellung einer Abhaengigkeit (ohne Leerzeichen)
410 _add_service_dependency() {
411  trap "error_trap _add_service_dependency '$*'" $GUARD_TRAPS
412  local dependency="$1"
413  local service_name="$2"
414  local token="$3"
415  local deps=$(get_service_value "$service_name" "$dependency")
416  local dep
417  for dep in $deps; do
418  # schon vorhanden -> fertig
419  [ "$dep" = "$token" ] && return 0 || true
420  done
421  if [ -z "$deps" ]; then
422  deps="$token"
423  else
424  deps="$deps $token"
425  fi
426  set_service_value "$service_name" "$dependency" "$deps"
427 }
428 
429 
430 # Entferne alle mit diesem Service verbundenen Konfigurationen (inkl. Rekonfiguration von firewall, etc.).
431 cleanup_service_dependencies() {
432  trap "error_trap cleanup_service_dependencies '$*'" $GUARD_TRAPS
433  local service_name="$1"
434  local dep
435  local filename
436  local branch
437  # Dateien loeschen
438  for filename in $(get_service_value "$service_name" "file_dependency"); do
439  rm -f "$filename"
440  done
441  # uci-Sektionen loeschen
442  for dep in $(get_service_value "$service_name" "uci_dependency"); do
443  uci_delete "$dep"
444  # gib die oberste config-Ebene aus - fuer spaeteres "apply_changes"
445  echo "$dep" | cut -f 1 -d .
446  done | sort | uniq | while read branch; do
447  apply_changes "$branch"
448  done
449  set_service_value "$service_name" "uci_dependency" ""
450 }
451 
452 
453 get_service_description() {
454  trap "error_trap get_service_description '$*'" $GUARD_TRAPS
455  local service_name="$1"
456  local scheme=$(get_service_value "$service_name" "scheme")
457  local host=$(get_service_value "$service_name" "host")
458  local port=$(get_service_value "$service_name" "port")
459  local proto=$(get_service_value "$service_name" "proto")
460  local details=$(get_service_value "$service_name" "details")
461  echo "$scheme://$host:$port ($proto) $details"
462 }
463 
464 
465 delete_service() {
466  trap "error_trap delete_service '$*'" $GUARD_TRAPS
467  local service_name="$1"
468  [ -z "$service_name" ] && msg_info "Error: no service given for deletion" && trap "" $GUARD_TRAPS && return 1
469  cleanup_service_dependencies "$service_name"
470  rm -f "$PERSISTENT_SERVICE_STATUS_DIR/$service_name"
471  rm -f "$VOLATILE_SERVICE_STATUS_DIR/$service_name"
472 }
473 
474 
475 # Durchlaufe alle Dienste und verteile Rangziffern ohne Doppelung an alle Dienste.
476 # Die Dienst-Arten (z.B. DNS und UGW) werden dabei nicht beachtet.
477 # Die Rangziffern sind anschliessend streng monoton aufsteigend - beginnend bei 1.
478 # Falls aktuell die manuelle Sortierung aktiv ist, wird deren Reihenfolge beibehalten.
479 # Ansonsten basiert die Vergabe der Rangziffern auf der Reihenfolge entsprechend der aktuell aktiven Sortierung.
480 _distribute_service_ranks() {
481  local service_name
482  local index=1
483  get_services | sort_services_by_priority | while read service_name; do
484  set_service_value "$service_name" "rank" "$index"
485  : $((index++))
486  done
487 }
488 
489 
490 ## @fn move_service_up()
491 ## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach oben
492 ## @param service_name der zu verschiebende Dienst
493 ## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
494 ## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
495 ## * manual: Verschiebung vor den davorplatzierten Dienst desselben Typs
496 ## * etx/hop: Reduzierung des Offsets um eins
497 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
498 move_service_up() {
499  trap "error_trap move_service_up '$*'" $GUARD_TRAPS
500  local service_name="$1"
501  shift
502  local sorting=$(get_service_sorting)
503  local prev_service=
504  local current_service
505  local temp
506  if [ "$sorting" = "hop" -o "$sorting" = "etx" ]; then
507  # reduziere den Offset um eins
508  temp=$(get_service_value "$service_name" "offset" 0)
509  temp=$(echo "$temp" | awk '{ print $1 - 1 }')
510  set_service_value "$service_name" "offset" "$temp"
511  elif [ "$sorting" = "manual" ]; then
512  get_services "$@" | sort_services_by_priority | while read current_service; do
513  if [ "$current_service" = "$service_name" ]; then
514  if [ -z "$prev_service" ]; then
515  # es gibt keinen Dienst oberhalb des zu verschiebenden
516  true
517  else
518  # wir verschieben den Dienst ueber den davor liegenden
519  temp=$(get_service_value "$prev_service" "rank" "$DEFAULT_SERVICE_RANK")
520  # ziehe einen halben Rang ab
521  temp=$(echo "$temp" | awk '{ print $1 - 0.5 }')
522  set_service_value "$service_name" "rank" "$temp"
523  # erneuere die Rang-Vergabe
524  _distribute_service_ranks
525  fi
526  # wir sind fertig
527  break
528  fi
529  prev_service="$current_service"
530  done
531  else
532  msg_info "Warning: [move_service_up] sorting method is not implemented: $sorting"
533  fi
534 }
535 
536 
537 ## @fn move_service_down()
538 ## @brief Verschiebe einen Dienst in der Dienst-Sortierung um eine Stufe nach unten
539 ## @param service_name der zu verschiebende Dienst
540 ## @param service_type der Service-Typ innerhalb derer Mitglieder die Verschiebung stattfinden soll
541 ## @details Für verschiedene Sortier-Modi hat dies verschiedene Auswirkungen:
542 ## * manual: Verschiebung hinter den dahinterliegenden Dienst desselben Typs
543 ## * etx/hop: Erhöhung des Offsets um eins
544 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste nach unten.
546  trap "error_trap move_service_down '$*'" $GUARD_TRAPS
547  local service_name="$1"
548  shift
549  local sorting=$(get_service_sorting)
550  local prev_service=
551  local current_service
552  local temp
553  if [ "$sorting" = "hop" -o "$sorting" = "etx" ]; then
554  # reduziere den Offset um eins
555  temp=$(get_service_value "$service_name" "offset" 0)
556  temp=$(echo "$temp" | awk '{ print $1 + 1 }')
557  set_service_value "$service_name" "offset" "$temp"
558  elif [ "$sorting" = "manual" ]; then
559  get_services "$@" | sort_services_by_priority | while read current_service; do
560  if [ "$prev_service" = "$service_name" ]; then
561  # wir verschieben den Dienst hinter den danach liegenden
562  temp=$(get_service_value "$current_service" "rank" "$DEFAULT_SERVICE_RANK")
563  # fuege einen halben Rang hinzu
564  temp=$(echo "$temp" | awk '{ print $1 + 0.5 }')
565  set_service_value "$service_name" "rank" "$temp"
566  # erneuere die Rang-Vergabe
567  _distribute_service_ranks
568  # wir sind fertig
569  break
570  fi
571  prev_service="$current_service"
572  done
573  else
574  msg_info "Warning: [move_service_down] sorting method is not implemented: $sorting"
575  fi
576 }
577 
578 
579 ## @fn move_service_top()
580 ## @brief Verschiebe einen Dienst an die Spitze der Dienst-Sortierung
581 ## @param service_name der zu verschiebende Dienst
582 ## @param service_types ein oder mehrere Dienst-Typen, auf die die Ermittlung der Dienst-Liste begrenzt werden soll (z.B. "gw")
583 ## @details Der Dienst steht anschließend direkt vor dem bisher führenden Dienst der ausgewählten Typen (falls angegeben).
584 ## Falls keine Dienst-Typen angegeben sind, bewegt der Dienst sich in der globalen Liste an die Spitze.
586  trap "error_trap move_service_top '$*'" $GUARD_TRAPS
587  local service_name="$1"
588  shift
589  local top_service=$(get_services "$@" | sort_services_by_priority | head -1)
590  local sorting=$(get_service_sorting)
591  local top_rank
592  local new_rank
593  local top_distance
594  local our_distance
595  local current_offset
596  local new_offset
597  # kein top-Service oder wir sind bereits ganz oben -> Ende
598  [ -z "$top_service" -o "$top_service" = "$service_name" ] && return 0
599  if [ "$sorting" = "hop" -o "$sorting" = "etx" ]; then
600  top_distance=$(get_distance_with_offset "$top_service" "$sorting")
601  our_distance=$(get_distance_with_offset "$service_name" "$sorting")
602  [ -z "$our_distance" ] && msg_info "Failed to move unreachable service ('$service_name') to top" && return 0
603  current_offset=$(get_service_value "$service_name" "offset" 0)
604  # wir verschieben unseren Offset, auf dass wir knapp ueber "top" stehen
605  new_offset=$(echo | awk "{ print $current_offset + int($top_distance - $our_distance) - 1 }")
606  set_service_value "$service_name" "offset" "$new_offset"
607  elif [ "$sorting" = "manual" ]; then
608  # setze den Rang des Dienstes auf den top-Dienst minus 0.5
609  top_rank=$(get_service_value "$top_service" "rank" "$DEFAULT_SERVICE_RANK")
610  new_rank=$(echo "$top_rank" | awk '{ print $1 - 0.5 }')
611  set_service_value "$service_name" "rank" "$new_rank"
612  # erneuere die Rang-Vergabe
613  _distribute_service_ranks
614  else
615  msg_info "Warning: [move_service_top] sorting method is not implemented: $sorting"
616  fi
617 }
618 
619 
620 ## @fn get_service_detail()
621 ## @brief Ermittle den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
622 ## @param service_name Name eines Diensts
623 ## @param key Name des Schlüssels
624 ## @param default dieser Wert wird zurückgeliefert, falls der Schlüssel nicht gefunden wurde
625 ## @returns den ermittelten Wert aus dem Schlüssel-Wert-Paar
627  local service_name="$1"
628  local key="$2"
629  local default="${3:-}"
630  local value=$(get_service_value "$service_name" "details" | get_from_key_value_list "$key" ":")
631  [ -n "$value" ] && echo -n "$value" || echo -n "$default"
632  return 0
633 }
634 
635 
636 ## @fn set_service_detail()
637 ## @brief Setze den Wert eines Schlüssel-Wert-Paars im "details"-Attribut eines Diensts
638 ## @param service_name Name eines Diensts
639 ## @param key Name des Schlüssels
640 ## @param value der neue Wert
641 ## @details Ein leerer Wert löscht das Schlüssel-Wert-Paar.
643  local service_name="$1"
644  local key="$2"
645  local value="$3"
646  local new_details=$(get_service_value "$service_name" "details" | replace_in_key_value_list "$key" ":" "$value")
647  set_service_value "$service_name" "details" "$new_details"
648  return 0
649 }
650 
651 
652 # Liefere eine Semikolon-separierte Liste von Service-Eigenschaften zurueck.
653 # Jede Eigenschaft wird folgendermassen ausgedrueckt:
654 # type|source|key[|default]
655 # Dabei sind folgende Inhalte moeglich:
656 # type: Rueckgabetyp (string, number, bool)
657 # source: Quelle der Informationen (value, detail, function, id)
658 # key: Name des Werts, des Details oder der Funktion
659 # default: Standardwert, falls das Ergebnis leer sein sollte
660 # Wahrheitswerte werden als "true" oder "false" zurueckgeliefert. Alle anderen Rueckgabetypen bleiben unveraendert.
661 # Das Ergebnis sieht folgendermassen aus:
662 # SERVICE_NAME;RESULT1;RESULT2;...
663 get_service_as_csv() {
664  local service_name="$1"
665  shift
666  local separator=";"
667  local specification
668  local rtype
669  local source
670  local key
671  local default
672  local value
673  local func_call
674  # Abbruch mit Fehler bei unbekanntem Dienst
675  is_existing_service "$service_name" || { trap "" $GUARD_TRAPS && return 1; }
676  echo -n "$service_name"
677  for specification in "$@"; do
678  rtype=$(echo "$specification" | cut -f 1 -d "|")
679  source=$(echo "$specification" | cut -f 2 -d "|")
680  key=$(echo "$specification" | cut -f 3 -d "|")
681  default=$(echo "$specification" | cut -f 4- -d "|")
682  # Ermittlung des Funktionsaufrufs
683  if [ "$source" = "function" ]; then
684  if [ "$rtype" = "bool" ]; then
685  "$key" "$service_name" && value="true" || value="false"
686  else
687  value=$("$key" "$service_name")
688  fi
689  else
690  if [ "$source" = "value" ]; then
691  value=$(get_service_value "$service_name" "$key")
692  elif [ "$source" = "detail" ]; then
693  value=$(get_service_detail "$service_name" "$key")
694  else
695  msg_info "Unknown service attribute requested: $specification"
696  echo -n "${separator}"
697  continue
698  fi
699  [ -z "$value" ] && [ -n "$default" ] && value="$default"
700  if [ "$rtype" = "bool" ]; then
701  # Pruefung auf wahr/falsch
702  value=$(uci_is_true "$value" && echo "true" || echo "false")
703  fi
704  fi
705  echo -n "${separator}${value}"
706  done
707  # mit Zeilenumbruch abschliessen
708  echo
709 }
710 
711 
713 ## @brief Ermittle den Namen der Log-Datei für diesen Dienst. Zusätzliche Details (z.B. "openvpn mtu") sind möglich.
714 ## @param service Name eines Dienstes.
715 ## @param other Eine beliebige Anzahl weiterer Parameter ist erlaubt: diese erweitern den typischen Log-Dateinamen für diesen Dienst.
716 ## @details Die Funktion stellt sicher, dass das Verzeichnis der ermittelten Log-Datei anschließend existiert.
718  local service_name="$1"
719  shift
720  local filename="$service_name"
721  while [ $# -gt 0 ]; do
722  filename="${filename}.$1"
723  shift
724  done
725  local full_filename="$SERVICES_LOG_BASE/$(get_safe_filename "$filename").log"
726  mkdir -p "$(dirname "$full_filename")"
727  echo -n "$full_filename"
728 }
729 
730 
731 ## @fn update_service_wan_status()
732 ## @brief Pruefe ob der Verkehr zum Anbieter des Diensts über ein WAN-Interface verlaufen würde. Das "wan_status"-Flag des Diensts wird daraufhin aktualisiert.
733 ## @param service_name der Name des Diensts
734 ## @details Diese Operation dauert ca. 5s, da zusätzlich die Ping-Zeit des Zielhosts ermittelt wird.
736  trap "error_trap ugw_update_wan_status '$*'" $GUARD_TRAPS
737  local service_name="$1"
738  local host=$(get_service_value "$service_name" "host")
739  local outgoing_device=$(get_target_route_interface "$host")
740  local ping_time
741  local outgoing_zone
742  if is_device_in_zone "$outgoing_device" "$ZONE_WAN"; then
743  set_service_value "$service_name" "wan_status" "true"
744  ping_time=$(get_ping_time "$host")
745  set_service_value "$service_name" "wan_ping" "$ping_time"
746  msg_debug "target '$host' routing through wan device: $outgoing_device"
747  msg_debug "average ping time for $host: ${ping_time}ms"
748  else
749  outgoing_zone=$(get_zone_of_device "$outgoing_device")
750  # ausfuehrliche Erklaerung, falls das Routing zuvor noch akzeptabel war
751  uci_is_true "$(get_service_value "$service_name" "wan_status" "false")" \
752  && msg_info "Routing switched away from WAN interface to '$outgoing_device'"
753  msg_debug "warning: target '$host' is routed via interface '$outgoing_device' (zone '$outgoing_zone') instead of the expected WAN zone ($ZONE_WAN)"
754  set_service_value "$service_name" "wan_status" "false"
755  set_service_value "$service_name" "wan_ping" ""
756  fi
757 }
758 
759 # Ende der Doku-Gruppe
760 ## @}