[Problem] TR-064 Traffic-Abfrage liefert nur den Wert seit dem letzten Connect

Solange das funktioniert, ist ja alles schön ... wenn das jemand nachbauen will, würde ich demjenigen meinerseits trotzdem raten, daß er sich eher die (strukturierten) Daten aus der JSON-Datei greift anstatt die Angaben aus irgendwelchem HTML-Quelltext "zu kratzen".

So, wie das hier jetzt gelöst ist, reicht es schon aus, wenn einfach die Tabellendaten nicht mehr mit irgendwelchen Zeilenumbrüchen mitten in der (Tabellen-)Zeile ausgegeben werden (das abschließende "</td></tr></table>" steht auch alles direkt nach der letzten Tabellenzeile und die jeweiligen Tabellenzeilen (tr) fangen ohnehin nicht am Zeilenanfang an), das ändert an der Darstellung der Seite gar nichts (diese Zeilenenden sind sogar reiner Ballast im HTML an dieser Stelle), aber das "scraping" oben würde klaglos den Wert für "Heute" finden (shortest match from front and longest match from back will get deleted) und nicht einmal einen ordentlichen Fehler werfen.

Der Vorteil von strukturierten Daten wäre es, daß man eigentlich kaum einen falschen Eintrag für den richtigen halten könnte und an der Logik oben ändert sich (außer beim "mapfile") praktisch auch nichts wirklich, nur anstelle von GET müßte man POST nehmen beim "curl" und eine andere Seite (mit entsprechende Parametern beim POST) aufrufen. Für das Parsen von JSON in Shell-Code gibt es fertige Lösungen (als Binärcode und damit auch schnell) ... und selbst wenn man die nicht will, sollte es dort - eben wegen der "Strukturierung", die ja auch definierte Trennzeichen zwischen Daten beinhaltet - ohne weiteres möglich sein, das deutlich weniger anfällig für geänderten Text rundherum zu gestalten (das ginge oben sicherlich auch noch besser).

Also: Solange es nur einfach funktionieren soll ... alles gut. Ändert AVM nur minimal an der Lua-Datei und macht aus der Form
Code:
str = str .. [[
<td datalabel="" class="first_col">]]
.. box.tohtml(txt[request] or "")
.. [[
</td>
]]
(ohnehin ungewöhnlich und unübersichtlich, weil man erst mal "Klammern zählen" muß, um das Ende des Statements zu finden) dann diese
Code:
str = str .. [[ <td datalabel="" class="first_col">]] .. box.tohtml(txt[request] or "") .. [[ </td> ]]
, liefert das Auslesen prompt falsche Werte. Merkt man das, kann man es korrigieren ... richtig blöd wird es erst dann, wenn man es (Fehler treten ja trotzdem nicht auf) gar nicht mitbekommt und sich trotzdem bei irgendwelchen (automatischen) Entscheidungen dann auf den falschen Wert stützt.
 
Wie müsste Aufruf aussehen? Das klappt noch nicht:
Code:
jq -r data.comfort.func.netCnt /dev/shm/~json.out
jq: error: data/0 is not defined at <top-level>, line 1:
data.comfort.func.netCnt
jq: 1 compile error
Die json-Datei enthält den gesuchten Wert:
Code:
{"linktxt":"Online-Zähler","details":"30503 von 300000 MB","link":"netCnt"}],

PS: Gibt es auch eine .json von der Seite wo nur die Traffic-Daten stehen?
 
Zuletzt bearbeitet:
Wirklich als absolute Ausnahme, weil ich Dich ja vermutlich erst dazu "verleitet" habe:
Code:
jq '.data.comfort.func[] | select(.["link"] == "netCnt") | .details' input_file
wäre ein (möglicher) Aufruf, der am Ende die Zeichenkette liefert ... mit "-r" auch ohne die Anführungszeichen rundherum und mit
Code:
set -- $(jq -r '.data.comfort.func[] | select(.["link"] == "netCnt") | .details' input_file)
hast Du dann die beiden Werte auch noch sofort in $1 und $3 stehen. Ob sie gültig sind, solltest Du trotzdem testen, denn der Filter kann - bei gültiger JSON-Struktur und ohne passende Fundstelle - auch ein leeres Ergebnis liefern.

PS/BTW: Wer sich seinen Filter selbst "zusammenschrauben" will, findet unter https://jqplay.org/ eine Seite, die dabei helfen kann (und nicht auf die Kommandozeile und kryptische Fehlermeldungen beschränkt ist).
 
Zuletzt bearbeitet:
  • Like
Reaktionen: MegaV0lt
Vielen Dank! Das hätte ich so schnell nicht hin bekommen...

Bin jetzt erst mal so weit:

Code:
f_fbtraffic(){
  avmfbip=${tempip:-fritz.box}
  avmfbuser=${tempuser:-'xxx'}
  avmfbpwd=${temppwd:-'xxx'}
  avmsidfile=${tempid:-/dev/shm/~avmsid}
  REQUESTPAGE='/internet/inetstat_counter.lua'
  OUTPUTFILE='/dev/shm/~fbout.html'

  [[ ! -f "$avmsidfile" ]] && touch "$avmsidfile"
  avmsid="$(< "$avmsidfile")"

  # check if current login is valid, otherwise generate session id
  result=$(curl -s "http://${avmfbip}/login_sid.lua?sid=$avmsid" | grep -c "0000000000000000")

  if [[ $result -gt 0 ]] ; then
      echo "Login neccessary"
      challenge="$(curl -s "http://${avmfbip}/login_sid.lua" |  grep -o "<Challenge>[a-z0-9]\{8\}" | cut -d'>' -f 2)"
      hash="$(echo -n "${challenge}-${avmfbpwd}" |sed -e 's,.,&\n,g' | tr '\n' '\0' | md5sum | grep -o "[0-9a-z]\{32\}")"
      curl -s "http://${avmfbip}/login_sid.lua" -d "response=${challenge}-${hash}" -d "username=${avmfbuser}" \
        | grep -o "<SID>[a-z0-9]\{16\}" |  cut -d'>' -f 2 > "$avmsidfile"
  fi
  avmsid="$(< "$avmsidfile")"
  echo "SID: $avmsid"

  # end of login function

  # JSON-Version
  curl --silent "http://${avmfbip}/data.lua"  \
    --data "xhr=1&lang=de&page=overview&xhrId=first&noMenuRef=1&no_sidrenew=&sid=${avmsid}" > "$OUTPUTFILE"

  # Zeile in ein Array  Beispiel: 30503 von 300000 MB
  NETCNT=($(jq -r '.data.comfort.func[] | select(.["link"] == "netCnt") | .details' "$OUTPUTFILE"))

  AM_TRAFFIC=${NETCNT[0]:-0}  # Falls Leer dann "0"
  echo "$AM_TRAFFIC MB"
  numfmt --from=si --to=iec-i --suffix=B --format='%.2f' "${AM_TRAFFIC}M" > "${CONKY_DATA}/traffic"
}

Noch jemand eine Ahnung, ob der Wert in M auf 1000 oder auf 1024 basiert? Momentan gehe ich von 1000 aus
 
Zu den Multiplikatoren weiß ich auch nichts ... letzte Bemerkung noch zu temporären Dateien in dem Skript: Wenn das nicht mehrmals innerhalb von 20 Minuten aufgerufen wird, ist die Speicherung der SID in einer Datei witzlos und "verführt" nur andere (je nachdem, wer da noch alles auf /dev/shm lesen darf) zu einem Angriff auf die Sitzung.

Das kann man ja auch problemlos in Variablen halten bzw. (nach dem Test, wenn es dann läuft) gleich als Pipe umsetzen (auch von "curl" direkt an "jq" - das liest die Daten auch von STDIN, wenn keine Datei beim Aufruf angegeben wurde und in den JSON-Daten steht die SID auch noch einmal drin) und selbst wenn man das tatsächlich in temporäre Dateien schreibt (wie gesagt, ist hier unnütz und auch das Speichern der SID hilft nur innerhalb der Timeout-Zeit für eine Session und das sind nun mal seit der 06.83 nur noch 20 Minuten - man könnte es sogar noch auf die Spitze treiben und sich gleich ordentlich "abmelden", wenn man die SID eigentlich nicht mehr braucht und die innerhalb der nächsten 20 Minuten auch nicht mehr brauchen wird, dann wird die direkt wieder ungültig und kann in der verbleibenden Zeit nicht mehr mißbraucht werden), dann sollte man die hinterher (im Rahmen dessen, was sinnvoll ist im jeweiligen Kontext) wieder entsorgen, auch wenn sie nur im volatilen Speicher liegen. Man weiß nicht 100%ig, wer da ggf. noch zugreifen könnte ...
 
Das Skript sichert alle TFFS-Nodes mit Inhalt (also wirklich alle, egal ob die ein char-Device zugeordnet haben oder nicht - es wird von 1 bis 255 hochgezählt) in ein TAR-File
Danke für die Erläuterung! Hier eine Rückmeldung.
Bei der FB 7412 und 7580 geht das.
Bei der FB7362SL erscheinen bei mir nur die minor 87 und 96. alle anderen sind 0.
Meine vage Vermutung: Das liegt an check_and_store_tffs_node_to yaffs, siehe hier:
http://ippf.eu/posts/2153440

Gibt es auch ein "trestore" oder muß man mit "tinstall" alle einzeln zurück schreiben?
Könnte man damit die Datenzähler nach einem Werksreset zurückschreiben?

Kann es mein Programm zum Entschlüsseln von Einstellungen finden, werden die vor der Sicherung auch entschlüsselt
Da erhalte ich bei der FB 7412 und 7580 folgende Fehlermeldungen:

Code:
Processing minor 113 (will go from 0 to 255) ... decoding contentexpr: syntax error
expr: syntax error
expr: syntax error
/var/tmp/crypto: line 572: arithmetic syntax error
var/tmp/decode_secrets: STDIN is a terminal device. If this was an intended call, specify the '-t' option to read input data from terminal.
Processing minor 114 (will go from 0 to 255) ... decoding contentexpr: syntax error
expr: syntax error
expr: syntax error
/var/tmp/crypto: line 572: arithmetic syntax error
var/tmp/decode_secrets: STDIN is a terminal device. If this was an intended call, specify the '-t' option to read input data from terminal.
Processing minor 115 (will go from 0 to 255) ... decoding contentexpr: syntax error
expr: syntax error
expr: syntax error
/var/tmp/crypto: line 572: arithmetic syntax error
var/tmp/decode_secrets: STDIN is a terminal device. If this was an intended call, specify the '-t' option to read input data from terminal.
Processing minor 118 (will go from 0 to 255) ... decoding contentexpr: syntax error
expr: syntax error
expr: syntax error
/var/tmp/crypto: line 572: arithmetic syntax error
var/tmp/decode_secrets: STDIN is a terminal device. If this was an intended call, specify the '-t' option to read input data from terminal.
 
Zuletzt bearbeitet:
Mein PC ist exklusiv von mir genutzt. Habe aber trotzdem die Vorschläge umgesetzt.
Das Skript fragt alle 5 oder 10 Minuten die Daten ab. Darum ist das mit der SID im Speicher schon nützlich.
Aktueller Stand (Auszug):
Code:
# Für die Abfrage des Traffics
FB_IP=${tempip:-fritz.box}                       # IP der Fritz!Box (Ohne http://)
FB_USER=${tempuser:-'xxx'}                  # Fritz!Box Benutzername (Login)
FB_PWD=${temppwd:-'xxx'}        # Fritz!Box Passwort (Login)
REQUESTPAGE='/data.lua'                          # Seite mit den Daten
FB_SID=''                                        # SID der Fritz!Box (Max. 20 Minuten gültig)
...
f_fbtraffic(){
  # Auf gültiges Login prüfen
  LOGIN_STAT=$(curl -s "http://${FB_IP}/login_sid.lua?sid=$FB_SID" | grep -c "0000000000000000")

  if [[ $LOGIN_STAT -gt 0 ]] ; then
      echo "Login nötig"
      challenge="$(curl -s "http://${FB_IP}/login_sid.lua" | grep -o "<Challenge>[a-z0-9]\{8\}" | cut -d'>' -f 2)"
      hash="$(echo -n "${challenge}-${FB_PWD}" |sed -e 's,.,&\n,g' | tr '\n' '\0' | md5sum | grep -o "[0-9a-z]\{32\}")"
      FB_SID=$(curl -s "http://${FB_IP}/login_sid.lua" -d "response=${challenge}-${hash}" -d "username=${FB_USER}" \
        | grep -o "<SID>[a-z0-9]\{16\}" | cut -d'>' -f 2)
  fi
  echo "SID: $FB_SID" # Ende der Login-Funktion

  # JSON-Version - Zeile in ein Array  Beispiel: 30503 von 300000 MB
  NETCNT=($(curl --silent "http://${FB_IP}${REQUESTPAGE}" \
    --data "xhr=1&lang=de&page=overview&xhrId=first&noMenuRef=1&no_sidrenew=&sid=${FB_SID}" \
      | jq -r '.data.comfort.func[] | select(.["link"] == "netCnt") | .details'))

  AM_TRAFFIC=${NETCNT[0]:-0}  # Falls Leer dann "0"
  echo "$AM_TRAFFIC MB"
  if [[ $AM_TRAFFIC -eq 0 ]] ; then
    echo 'Fehler!' > "${CONKY_DATA}/traffic"
  else
    numfmt --from=si --to=iec-i --suffix=B --format='%.2f' "${AM_TRAFFIC}M" > "${CONKY_DATA}/traffic"
  fi
}
...
 
@eisbaerin:
Es gibt kein "trestore", weil ich bisher dafür keine Notwendigkeit sah. In aller Regel brauche ich selbst an dieser Stelle nur das Wiederherstellen einzelner Dateien und dafür ist - in der Tat - dann "tinstall" gedacht. Das jetzt mit einer Liste der wiederherzustellenden Nodes (als Zahl, daher heißen auch die Dateien im Backup entsprechend) in einer kleinen for-Schleife aufzurufen, lohnt(e) - zumindest bisher - in meinen Augen den Aufwand nicht. Es ist auch nicht automatisch bei allen gesicherten Inhalten gut und sinnvoll, die so ohne weiteres wiederherzustellen. Wo das Sinn machen kann und wo nicht, hatte ich mal bei einer anderen Lösung ("bootmanager" als Shell-Skript) etwas näher beschrieben (da findet man auch eine Liste in irgendeiner Zeile, was bei der Umschaltung zwischen zwei Systemen gesichert und wiederhergestellt werden sollte und was nicht) - frag' mich bitte nicht nach einem Link zu dem Thread mit den Erläuterungen, ich weiß es auch nicht, wo das war und müßte selbst erst suchen.

Die Online-Zähler in der "stat.cfg" (in tffs.files sind die Nummern den Namen zugeordnet, soweit ich die kenne) sollten sich auch tatsächlich auf diesem Weg wiederherstellen lassen, ich würde aber zuvor den "ctlmgr" beenden (ctlmgr -s) und ihn im Anschluß neu starten (ctlmgr).

Da sich "tbackup" wirklich ausschließlich auf die Inhalte des TFFS-Speichers stürzt, kann es auch nur das sichern, was wirklich dort abgelegt wird. Wenn AVM in irgendeinem Gerät das Speichern von Einstellungen in das YAFFS2-Dateisystem (in der Partition mit dem Namen "config") ausgelagert hat (als "regular files") und dort nicht nur die Inodes für die TFFS-Minors anlegt, dann kann (und soll) "tbackup" da nichts sichern ... die 7362SL gehört auch nicht zu meinem "Arsenal", wo ich das selbst bemerkt hätte. Was ich mich an dieser Stelle frage ... hat AVM da auch den TFFS-Dump in den erweiterten Support-Daten anders angelegt? Ansonsten kriegt der auch - außer Environment und ein paar Nodes mit Nummern jenseits der für "Dateien" reservierten - in so einem Dump keine sinnvollen Inhalte und die "/bin/supportdata.tffs" in anderen Modellen sieht jetzt nicht direkt danach aus, als wären da (abseits von NAND vs. NOR/SPI) irgendwelche Unterscheidungen vorgesehen.

Woher die Syntax-Fehler jetzt konkret kommen, kann ich nicht wirklich sagen ... zumindest müßte ich wissen, welche Version des Shell-Skripts da verwendet wurde. Wobei ich bei der letzten Änderung von "tbackup" tatsächlich davon ausgegangen bin, daß man nicht die (vergleichsweise langsame) Shell-Version von "decode_secrets" verwendet (und wenn doch, dann die aktuelle - die sollte sich eigentlich mit der auch zu sehenden Nachricht "STDIN is a terminal device." verabschieden und nicht noch zusätzliche Fehler produzieren, aber es kann sein, daß da etwas nicht richtig abgefangen wird bei zu vielen Parametern), sondern die binäre Version von "decoder" (mit passendem Symlink als "decode_secrets", denn nur der wird von "find_executable()" gesucht) und die akzeptiert dann (anders als das Shell-Skript) die Eingabedaten entweder über STDIN oder auch die Angabe eines Dateinamens beim Aufruf.

Da in "tbackup" die Eingabe ohne Redirection verwendet wird (es fehlt ein "<" vor dem Namen des Nodes), kann es in dieser Form nicht mit dem (neuen) Shell-Skript verwendet werden (das alte "micro_decode", was da zuvor verwendet wurde, kam auch mit Dateinamen beim Aufruf klar). Ich hatte diese ganzen Dateien auch erst vor kurzem weitgehend geändert, damit sie nur noch POSIX-kompatible Syntax verwenden - da ich das zweimal editiert habe (meine eigenen Versionen enthalten noch einige andere Möglichkeiten), kann es zu Fehlern kommen, wenn ich auf den Test der Dateien im Repo verzichtet habe und Fehler beim Editieren machte (wie z.B. bei dem fehlenden Semikolon zuvor - in anderen Dateien gibt es das "find_executable()" ja auch und da war das jeweils vorhanden). Wenn man also unbedingt die (aktuelle) Shell-Version verwenden will, muß man das "<" noch beim Aufruf vor der Eingabedatei ergänzen ... es schadet auch nicht, wenn das bei der binären "decoder"-Version verwendet würde.

Wenn ich weiß, welche "decode_secrets"-Version die o.a. Fehler beim "expr"-Aufruf produziert und es handelt sich um eine aktuelle, dann schaue ich mir das auch noch an.

@MegaV0lt:
Das war auch mehr für "Nachnutzer" als Anmerkung gedacht ... wobei ich zugegebenermaßen bei solchen Aussagen wie "Mein PC ist exklusiv von mir genutzt." immer nicht weiß, ob ich eher die darin zum Ausdruck gebrachte (oder zumindest "mitschwingende") Überzeugung: "Da läuft auch nur das, was ich selbst will und ich habe IMMER die volle Kontrolle." bewundern sollte oder den grenzenlosen Optimismus (bzw. die dann von Dir aufgewendete Arbeit, wenn das nicht ausschließlich auf optimistischen Annahmen beruht und Du tatsächlich mit LFS und eigenen Reviews arbeitest), der darin zum Ausdruck kommt. Es ist ja nun nicht so, daß es nicht auch schon "Übernahmen" ganzer Distributionen gegeben hätte - wenn ich mich richtig erinnere, war das bei "Mint" erst im vergangenen Jahr und auch "kernel.org" wurde schon früher gehackt. Auch für "nur meins"-Systeme kann es keinesfalls schaden, wenn man (trotzdem?) beim Design möglichst wenige "Schwachstellen" mit einbaut - deren Fehlen fällt ja in der Regel auch nicht negativ auf.
 
Zuletzt bearbeitet:
Mit der letzten Version werden doch keine Daten mehr gespeichert...
 
zumindest müßte ich wissen, welche Version des Shell-Skripts da verwendet wurde.
Ich habe die genommen, die du gestern verlinkt hast, also:
https://github.com/PeterPawn/decoder/blob/master/scripts/decode_secrets
decode_secrets, version 0.3, from decode_passwords

muß man das "<" noch beim Aufruf vor der Eingabedatei ergänzen
wenn du das in der tbackup so meinst:

if find_executable "decode_secrets"; then
printf "\x1B[5m decoding content\x1B[25m" 1>&2
decode_secrets < $td/cdev >$td/$minor
decoded=",decoded=1"
else

dann kommt zumindest ein Fehler weniger:
Code:
Processing minor 113 (will go from 0 to 255) ... decoding contentexpr: syntax error
expr: syntax error
/var/tmp/crypto: line 572: arithmetic syntax error
var/tmp/password_from_device: Unknown option ''.
var/tmp/decode_secrets: Error computing device cipher key, maybe it's not a FRITZ!OS system?
 
@eisbaerin:
Dann nehme ich zur Kenntnis, daß das Shell-Skript "decode_secrets" bei Dir nicht wie erwartet funktioniert, wenn es auf einer FRITZ!Box von 'tbackup' verwendet wird ... ich werde es mir ansehen. So aus dem Bauch heraus würde ich sagen hätte ich gesagt, daß da am Ende vielleicht auch nur das "crypto"-Skript dazu fehlt, aber das sollte eigentlich tatsächlich alles als möglicher Fehler abgefangen werden. Wobei ich auch nicht verstehe, wie es zu diesem Fehler
/var/tmp/crypto: line 572: arithmetic syntax error
kommt, wenn doch in Zeile 572 eigentlich nur eine simple Zuweisung steht: https://github.com/PeterPawn/decoder/blob/master/scripts/crypto#L572

Ich bin also meinerseits reichlich verwirrt ... aber ich werde das tatsächlich noch einmal auf einer FRITZ!Box mit der "ash" der BusyBox überprüfen - zunächst mal als einzelnen Aufruf und dann aus einem (geänderten) "tbackup" heraus. Schon die Feststellung, daß da eine "leere" Option ausgewertet werden soll (die ist dann natürlich "unknown") beim (internen) Aufruf von "password_from_device", macht mich neugierig.

========================================================

Aber ich hatte gestern ja in Wirklichkeit das gesamte "decoder"-Projekt verlinkt (und nicht das Skript und noch nicht einmal das "scripts"-Verzeichnis) und dort steht in der README.md im Projektverzeichnis meinerseits ausdrücklich:
YOU SHOULD REALLY USE THE C PROGRAM (see below), IF YOUR MAIN INTEREST ISN'T ADDITIONAL RESEARCH.
Die Shell-Skripte dienen also eigentlich nur dazu, das Prinzip an sich zu verstehen und ggf. selbst weiter zu forschen - wobei ich dem Fehler trotzdem auf den Grund gehen will.

Für die Benutzung auf einer FRITZ!Box mit VR9- oder GRX5-Prozessor ist die Datei für MIPS32R2 im "bin"-Unterordner dort wesentlich besser geeignet ... da findet sich auch eine GPG-Signatur für diese Datei oder man übersetzt sie sich selbst. Das funktioniert auch in der Freetz-Toolchain ganz ordentlich, dort ist das Projekt unter dem Namen "decrypt-fritzos-cfg" als Paket verfügbar (wenn man es nicht mit einer eigenen, sondern mit der Freetz-Toolchain übersetzen möchte).

@MegaV0lt:
Ist ja auch alles gut ... ich hatte gar nicht ernsthaft damit gerechnet, daß Du nach #24 noch bei Dir selbst änderst. Daher dann das spätere
Das war auch mehr für "Nachnutzer" als Anmerkung gedacht"
... und anstatt einfach nur zu postulieren: "Security beginnt beim Design.", wollte ich das halt begründen, warum - selbst auf einem Ein-Personen-Rechner in einem Ein-Personen-Haushalt in einer Ein-Personen-Hütte auf der Alm - es Sinn machen kann, wenn man sich über diese möglichen Angriffspunkte Gedanken macht (btw ... die FRITZ!Box arbeitet auch - zumindest intern - als "Ein-Account-System" und versucht mit allen möglichen Kopfständen anschließend wieder, Zugriffsbeschränkungen wenigsten beim NAS (und verwandten Diensten) durchzusetzen, aber jede auszunutzende Lücke in irgendeinem (AVM-)Service verschafft einem Angreifer automatisch auch Root-Rechte).
 
Zuletzt bearbeitet:
vielleicht auch nur das "crypto"-Skript dazu fehlt
Nö das ist vorhanden. Mit der Zeile 572 (rc=1) hatte ich mich auch schon gewundert.

Kann es aber sein, daß noch eine binary fehlt? Wegen:
and an OpenSSL (CLI) binary, which supports AES-256 decryption and MD5 digests.
Ich hatte mal die aus modfs genommen, hat aber nicht geholfen.

EDIT: OK jetzt habe ich es verstanden mit der decoder.mips32r2 und den Links.
Da klappt es auf Anhieb und man braucht auch keine OpenSSL.
 
Zuletzt bearbeitet:
Sooo ... das Problem ist erkannt, aber ich habe im Moment noch keine wirklich sinnvolle Lösung.

Eine der irgendwann mal ausgeführten Änderungen beinhaltete das Einfügen von zwei Bindestrichen vor weiteren Argumenten für das "expr"-Kommando, nicht nur hier, sondern an anderen Stellen (in der Shell-Script-Library z.B.) auch. Warum braucht es die eigentlich?

Wenn ein "expr"-Kommando selbst Befehlszeilen-Optionen unterstützt (und es gibt verschiedene Inkarnationen, von GNU über FreeBSD/OpenBSD bis zum BusyBox-Applet, jeweils mit unterschiedlichem Funktionsumfang) und man diesem dann bei einem Aufruf
Code:
expr "$1" : "\(.\).*"
in "$1" einen Wert vorsetzt, der mit einem Bindestrich beginnt, könnte dieser Wert fälschlicherweise als Option für "expr" mißinterpretiert werden. Daher sieht der POSIX-Standard (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html) eigentlich folgende Regelung vor:
[...]
Guideline 4:
All options should be preceded by the '-' delimiter character.
[...]
Guideline 9:
All options should precede operands on the command line.
Guideline 10:
The first -- argument that is not an option-argument should be accepted as a delimiter indicating the end of options. Any following arguments should be treated as operands, even if they begin with the '-' character.
Damit sollte man also bei (compliant) Programmen durch die Angabe von "--" das Ende jedweder Optionsverarbeitung einleiten können und damit dann auch kein Problem mehr haben, wenn einer der folgenden Parameter gemäß Guideline 4 mit dem Bindestrich beginnt.

Leider sieht das aber das "expr"-Applet der BusyBox (zumindest bis inkl. der von Freetz (und in der Folge auch von mir für "modfs") verwendeten Version 1.24.2) anders, denn das reagiert nun seinerseits auf diese beiden Bindestriche allergisch:
Code:
$ expr "mktemp" : "\(.\).*"
m
$ expr -- "mktemp" : "\(.\).*"
expr: syntax error
Ich hatte diese Änderung ja extra vorgenommen, weil ansonsten auf anderen Systemen ein Aufruf wie
Code:
__is_option()
{
        [ "$(expr -- "$1" : "\(.\).*")" = "-" ] && return 0 || return 1
}
nicht funktioniert, wenn "$1" vom "expr"-Kommando als Option gewertet wird. Wie das aussehen würde, kann ja jeder mal mit einem eigenen Test ermitteln (hier mit der AVM-BusyBox auf einer 7580 mit 06.90):
Code:
# grep "-d" /var/tmp/scripts/crypto
grep: invalid option -- d
BusyBox v1.22.1 (2016-10-31 15:55:10 CET) multi-call binary.

Usage: grep [-HhnlLoqvsriwFE] [-m N] [-A/B/C N] PATTERN/-e PATTERN.../-f FILE [FILE]...

Search for PATTERN in FILEs (or stdin)

        -H      Add 'filename:' prefix
        -h      Do not add 'filename:' prefix
        -n      Add 'line_no:' prefix
        -l      Show only names of files that match
        -L      Show only names of files that don't match
        -c      Show only count of matching lines
        -o      Show only the matching part of line
        -q      Quiet. Return 0 if PATTERN is found, 1 otherwise
        -v      Select non-matching lines
        -s      Suppress open and read errors
        -r      Recurse
        -i      Ignore case
        -w      Match whole words only
        -x      Match whole lines only
        -F      PATTERN is a literal (not regexp)
        -E      PATTERN is an extended regexp
        -m N    Match up to N times per file
        -A N    Print N lines of trailing context
        -B N    Print N lines of leading context
        -C N    Same as '-A N -B N'
        -e PTRN Pattern to match
        -f FILE Read pattern from file

# grep -- "-d" /var/tmp/scripts/crypto
# 'enc -d' function.                                                                                  #
        __check_option "$1" "-d" "--debug" && return 0
__debug_on__="eval __debug_set__=1; __debug_text__=\"-d\";"
        __option_debug() { __nl; __option_show_opt ${1:-15} "-d" "--debug"; __option_show_desc "display debug info on STDERR; must prefix all other options, if used"; }
        [ "$1" = "-d" ] && dir=1 || dir=0
                while [ -d "$name" ]; do
        $reader | openssl enc -d ${3:--aes-256-cbc} -K "$1" -iv "$2" 2>/dev/null | $writer
#
Die beiden "grep"-Aufrufe sind bis auf "--" identisch und zeitigen aber komplett unterschiedliche Ergebnisse, weil eben das "-d" nicht als der gesuchte Wert, sondern als Option für "grep" angesehen wird.

Nun bin ich etwas unschlüssig, was ich hier machen sollte/will ... entweder ich finde eine Unterscheidungsmöglichkeit und wende die an (was dann so schön simple Funktionen natürlich unnötig verkompliziert) oder ich patche die BusyBox vor dem Build, damit sie sich an den POSIX-Standard hält (das könnte man dann auch beim Upstream einreichen, aber es ändert natürlich weder etwas an der (noch älteren) AVM-Version noch an bereits vorhandenen Images).

Wobei ich diese Überlegungen auch eher für andere Skript-Dateien als diese hier anstelle ... es betrifft ja auch andere Funktionen aus der Script-Library, wenn das bei der BusyBox nicht funktioniert und diese Dateien hier müßte man nur entsprechend patchen, wenn man sie auf eine FRITZ!Box (bzw. in eine Umgebung, wo das "expr"-Kommando ebenfalls so vergurkt ist) bringen will. Ohne die beiden Bindestriche beim "expr" sollten die Shell-Skripte aus "decoder" auch mit einer BusyBox wieder funktioneren. Dafür reicht schon ein
Code:
for f in $(grep -l -- "expr --" <path_to_scripts>/*); do sed -e "s|expr --|expr|g" -i $f; done
aus, wenn man "<path_to_scripts>" richtig ersetzt.

Ansonsten gab es noch ein Problem, weil ich es versäumt habe, die Anzahl der Zeichen in einer "echten" Seriennummer tatsächlich mal nachzuzählen und ich aus den bisher verwendeten 16 Nullen im Environment auch bretthart auf 16 Zeichen in der Seriennummer geschlossen habe. Nun ist die aber nur 15 Zeichen lang (ohne die Punkte) und damit stimmten einerseits Hilfetexte nicht (eine läßliche Sünde), aber weil ich zu genau auf die Länge von Parametern teste, paßte die bei einer GRX5-Box dann auch nicht, denn dort wird die Nummer ja im Environment hinterlegt und die hat eben nur 15 Stellen.

Das habe ich korrigiert ... allerdings die Binärdateien noch nicht neu übersetzt, denn denen ist das ziemlich egal, solange man nicht die Möglichkeit der direkten Angabe der Seriennummer auf der Kommandozeile verwendet, da beim Auslesen aus einer Environment-Kopie (Option --alt-env) auch keine Längenprüfung stattfindet.

Dann gab es noch eine Ungenauigkeit in drei Shell-Skripten beim internen Aufruf von weiteren Skript-Dateien, denn bei
Code:
... ${altenv:+-a} "$altenv"
wird wegen der Anführungszeichen um das zweite "$altenv", die dagegen schützen sollen, daß der Dateiname event. Leerzeichen enthält, ein leerer (aber existenter) Parameter generiert:
Code:
$ sh -c 'echo $#' arg0 ${test:+-a} "$test"
1
$ sh -c 'echo $#' arg0 ${test:+-a "$test"}
0
Daher kam dann das
unknown option ''
bei bestimmten Aufrufen.

Wie ich mit dem "expr" am Ende umgehen werde, weiß ich noch nicht genau (das gibt es eben auch in den Skript-Funktionen in "scriptlib" noch an fünf Stellen und ich will schon eine einheitliche Behandlung haben) ... ansonsten sollte es ausreichen, den o.a. "Patch" auf die Shell-Skripte anzuwenden, wenn man sie mit der BusyBox benutzen will.

Ich tendiere im Moment zu einem Patch für die BusyBox, denn die POSIX-Spezifikation merkt auch noch an:
Also note that this volume of POSIX.1-2008 permits implementations to extend utilities. The expr utility permits the integer arguments to be preceded with a unary minus. This means that an integer argument could look like an option. Therefore, the conforming application must employ the "--" construct of Guideline 10 of XBD Utility Syntax Guidelines to protect its operands if there is any chance the first operand might be a negative integer (or any string with a leading minus).
 
Zuletzt bearbeitet:
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.