Ich versuche ein Shellscript zu schreiben was die gesamte Logginzeit eines Nutzers an einem Rechner ermittelt werden soll

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

Hinweis: In dem Thema Ich versuche ein Shellscript zu schreiben was die gesamte Logginzeit eines Nutzers an einem Rechner ermittelt werden soll gibt es 14 Antworten auf 2 Seiten. Der letzte Beitrag () befindet sich auf der letzten Seite.
  • Ich versuche ein Shellscript zu schreiben was die gesamte Logginzeit eines Nutzers an einem Rechner ermittelt werden soll

    Hallo, ich versuche ein Shellscript zu schreiben das ermitteln soll, wie lange der angegebene Nutzer insgesamt auf diesem Rechner eingeloggt war.

    um erstmal einmal die eingabe vom Nutzer auszuwerten habe ich if schleifen so wie das Kommando ping und finger benutzt

    Quellcode

    1. #!/bin/bash
    2. #Überprüfe Parametereingabe
    3. echo " "
    4. if [ $# == 2 ]
    5. then
    6. echo "Parameter wurden erfolgreich eingelesen"
    7. else
    8. echo "Parameter fehlt oder wurde nicht gefunden"
    9. exit
    10. fi
    11. #Überprüfen ob Rechner Exestiert.
    12. echo " "
    13. ping -c 1 $1 > /dev/null
    14. if [ $? -eq 0 ]
    15. then
    16. echo "*****Eingabe Wahr, $1 Existiert*****"
    17. else
    18. echo "*****Fehler $1 Exestiert nicht oder wurde nicht Gefunden*****"
    19. echo " Programm beendet!"
    20. exit
    21. fi
    22. #Überprüfen ob Nutzer Exestiert.
    23. echo " "
    24. finger $2
    25. if [ $? -eq 0 ]
    26. then
    27. echo "User $2 Exestiert"
    28. else
    29. echo "*****FEHLER User $2 Konnte nicht gefunden werden*****"
    30. echo " Programm beendet!"
    31. exit
    32. fi
    33. #Verzeichnis tmp anlegen
    34. echo " "
    35. mkdir tmp | echo $? Verzeichnis tmp wurde angelegt um wtmp zu entpacken
    36. echo "Kopiere alle log Daten in tmp"
    37. cp /var/log/wtmp* ~/tmp
    38. if [ $? -eq 0 ]
    39. then
    40. echo "wtmp wurden erfolgreich in das Verzeichnis tmp Kopiert"
    41. else
    42. echo "Fehler beim Kopieren"
    43. fi
    44. unxz ~/tmp/wtmp*.xz
    45. if [ $? -eq 0 ]
    46. then
    47. echo "wtmp* wurden erfolgreich entpackt"
    48. else
    49. echo "Fehler beim Entpacken der wtmp* "
    50. fi
    51. echo "Liste alle log daten vom Nutzer $2 auf"
    52. $login = last -F -f ~/tmp/wtmp | grep $1 | cut -c 50-58
    53. "$zeit = date"
    54. let "ergebnis=$zeit - $login"
    55. echo "$ergebnis"
    56. rm -r tmp
    57. echo "Verzeichnis \"tmp\" wurde gelöscht!"
    Alles anzeigen
    Meine Frage bezieht sich am Schluss. Wie kann ich die ermittelten Loginzeiten aus Last jetzt zusammenrechnen
    denn hier komme ich mit dem Befehl let nicht weiter. ?(

    Ich danke für die Antworten :thumbsup:

    Für den Inhalt des Beitrages 102448 haftet ausdrücklich der jeweilige Autor: Timmi0701

  • ich werde dir nicht recht weiterhelfen können, aber vermutlich wird auch interessant sein, was die Konsole ausgibt.
    Vor allem $ergebnis.

    Wenn ich die Beschreibung von let anschaue


    man let schrieb:


    let arg [arg ...]
    Each arg is an arithmetic expression to be evaluated (see ARITHMETIC EVALUATION above). If the last arg
    evaluates to 0, let returns 1; 0 is returned otherwise.
    sieht das für mich aus, als gäbe es sowieso nur einen boolschen Wert. (oder?)

    Für den Inhalt des Beitrages 102449 haftet ausdrücklich der jeweilige Autor: JeyF123

  • Ich denke mit einer einfachen Summierung ist es nicht getan.
    Es können ohne weiteres mehrere Logins des gleichen Users da sein, dann müsste man Minimum und Maximum finden und die Differenz berechnen.
    Es können Sitzungen über die Datumsgrenze hinweg gehen.
    Es kann crash und andere Einträge geben, die sich rechnerisch nicht bearbeiten lassen.

    Ich denke, wenn man alle Klippen umschifft hat, könnte es mit awk gehen.

    Für den Inhalt des Beitrages 102452 haftet ausdrücklich der jeweilige Autor: Tar Zahn

  • Ich würde da mal schlicht einen Befehl probieren:

    Quellcode

    1. # erst für das Leben lernen
    2. man last
    3. # und dann andächtig staunen.
    4. # is ja Weihnachten
    5. last
    Ich hänge später noch eine kleine vernichtend gigantische Kritik an; da sind einige Konstrukte eher nicht zu empfehlen.
    Sokrates sagte, dass er nichts wisse.
    Ich bin viel, viel klüger als Sokrates.
    Ich weiß ganz genau, dass ich gar nichts weiß.

    Für den Inhalt des Beitrages 102458 haftet ausdrücklich der jeweilige Autor: Berichtigung

  • JeyF123 schrieb:

    ich werde dir nicht recht weiterhelfen können, aber vermutlich wird auch interessant sein, was die Konsole ausgibt.
    Vor allem $ergebnis.

    Wenn ich die Beschreibung von let anschaue


    man let schrieb:

    let arg [arg ...]
    Each arg is an arithmetic expression to be evaluated (see ARITHMETIC EVALUATION above). If the last arg
    evaluates to 0, let returns 1; 0 is returned otherwise.
    sieht das für mich aus, als gäbe es sowieso nur einen boolschen Wert. (oder?)
    Das ist definitv falsch.
    JEDES Kommando gibt einen sogenannten EXIT CODE auch RETURN CODE genannt zurück.
    Das passiert auf Betriebssystemebene, was heißt, dass das der Kernel macht. Die Shell oder die Kommandos haben darauf KEINERLEI Einfluß.
    Man kann diesen Rückgabewert aus der besonderen Shellvariabeln $? lesen, oder ignorieren. Mehr aber auch nicht.

    Es ist also vollkommen OK, das zum Rechnen zu verwenden.
    Fast.
    Es ist "alte" POSIX Syntax.
    Es gibt besseres:
    echo $(( zeit - login ))
    ergebnis=(( zeit - login ))
    Da das doppelte Klammerpaar einen arithmetischen Kontext definiert, muss man innerhalb dieses doppelten Klammerpaares nicht einmal mehr das $- Zeichen zur Expansion bemühen.
    Sokrates sagte, dass er nichts wisse.
    Ich bin viel, viel klüger als Sokrates.
    Ich weiß ganz genau, dass ich gar nichts weiß.

    Für den Inhalt des Beitrages 102459 haftet ausdrücklich der jeweilige Autor: Berichtigung

  • mögliche Lösungen Datumsnumerik und "last" Teil 1

    Teil 1
    corrrected copy:

    Shell-Script

    1. #!/bin/bash
    2. #Überprüfe Parametereingabe
    3. echo " "
    4. if [ $# == 2 ]
    5. then
    6. echo "Parameter wurden erfolgreich eingelesen"
    7. else
    8. echo "Parameter fehlt oder wurde nicht gefunden"
    9. exit
    10. fi


    Das ist mal ganz schön frech gelogen. Gelesen hast du da nichts. Und unklare Sprache.
    Ein Parameter ist da, oder nicht. Suchen nutzt da ziemlich genau gar nix. Wo wolltest du denn da suchen?
    Das machen wir später korrekt.
    Schlecht jedoch ist, dass du das Script bei dem Parameterfehler mit Exitcode 0 (exit) beendest. (ein schlichtes "exit" ist die Abkürzung für exit 0
    Da es ein Fehler ist, muss das auch "gemeldet" werden. Korrekt wäre also ein exit 1.
    Oder jede andere ganze Zahl bis 255. Es gibt sogar dafür Konventionen. Man solle "Userfehler" immer bei 65 beginnen lassen und Werte von 0 bis 127 den Konventionen entsprechend für die "normalen" Systemfehler nehmen.
    Egal, was du machst: Ein Rückgabewert von 0 im Fehlerfall ist ein Fehler.

    Shell-Script

    1. #Überprüfen ob Rechner Exestiert.
    2. echo " "
    3. ping -c 1 $1 > /dev/null

    Das ist keine gültige Möglichkeit. Obwohl ein System, das sich korrekt nach den RFCs richtet, auf einen ping auch antworten sollte, haben viele vermeintliche Admins den Echoserver abgeschaltet. Da gilt das Vogel-Strauß-Prinzip: Wenn ich meinen Kopf in den Sand stecke, dann kann mich niemand sehen. Das ist heute -leider- sehr viel öfter der Fall, als es von den RFCs erlaubt ist.
    Hat man das Netzwerk unter Kontrolle, ist das schon OK. Aber man sollte das beachten. Mit nmap lassen sich Scans durchführen, die auch laufende Hosts entdecken, die auf keinen Ping antworten. Besser ist es jedoch immer, gleich den gewünschten Server auf dem jeweiligen Port für den jeweiligen Service direkt zu testen.

    Shell-Script

    1. if [ $? -eq 0 ]
    2. then
    3. echo "*****Eingabe Wahr, $1 Existiert*****"
    4. else
    5. echo "*****Fehler $1 Exestiert nicht oder wurde nicht Gefunden*****"
    6. echo " Programm beendet!"
    7. exit
    8. fi

    Siehe vorherige Bemerkung zu exit

    Shell-Script

    1. #Überprüfen ob Nutzer Exestiert.
    2. echo " "
    3. finger $2

    Jetzt kommen wir zu dem Denkfehler. Man mag zwar finger dafür verwenden, weil das auch auf dem lokalen Host funktioniert,
    aber auch hier gilt: Oft läuft der finger Dienst gar nicht.
    Da du zwei Parameter als Eingabe verlangst, aber nur einen Parameter (nämlich $2, was ein Username sein soll) verwendest, leite ich aus deinem Text ab, dass das der Hostname sein sollte. Von außen wirst du kaum einen Server finden, der dir anwortet. Du wirst immer sowas als Antwort erhalten:
    "finger: Der Name oder der Dienst ist nicht bekannt". Vergiss dieses Kommando einfach. Das war schon obsolet, als du geboren wurdest.
    Und für die lokale Abfrage ist das auch völlig unnötig.
    Und "Exestiert" existiert nicht einmal in kLEINSCHREIBUNG.
    Ich gehe also im Weiteren davon aus, dass dieses Script auf dem jeweilig zu untersuchenden Host läuft.

    Shell-Script

    1. #Verzeichnis tmp anlegen
    2. echo " "
    3. mkdir tmp | echo $? Verzeichnis tmp wurde angelegt um wtmp zu entpacken
    4. echo "Kopiere alle log Daten in tmp"
    5. cp /var/log/wtmp* ~/tmp

    Da hätte ich jetzt noch ein paar eher kosmetische Bemerkungen.
    Einmal gibt es natürlich auf jedem System bereits ein Tempverzeichnis. Es mag aber auch gute Gründe geben, seine Daten dort nicht auszulagern.
    Du erstellst einfach ein Verzeichnis. Das würde schon beim zweiten Aufruf zu einem Fehler führen, weil das Verzeichnis dann ja existiert.
    Man kann dazu mkdir -p something verwenden, was die Fehlerausgabe unterdrückt, und das Script läuft weiter.
    Es gibt aber besseres: Lass das doch einfach das System machen. Es gibt den Befehl mktemp. Der tut genau das. Er erzeugt dir ein temporäres File. Dabei wird vom System sichergestellt, dass es diese Datei noch nicht gibt.

    Shell-Script

    1. # eine temp Datei erstellen.
    2. # die XXXXX (es sind 10 Xse möglich) werden vom kernel durch einen Zufallsstring ersetzt
    3. mktemp my-temp-file_XXXXXXXXX
    4. # damit mir das später etwas sagt, verwende ich oft ein Präfix, das den Befehl mitdrin hat.
    5. # und das Datum, an dem es erzeugt wurde.
    6. mktemp $(basename $0)-temp-created-$(date +%Y-%m-%d_%H:%M:%S)_XXXXXXXXX
    7. # das lässt sich gut lesen und sortieren.
    8. # Wirklich und überall sicher ist die atomare Operation "Erstelle Verzeichnis":
    9. mktemp -d $(basename $0)-temp-created-$(date +%Y-%m-%d_%H:%M:%S)_XXXXXXXXX
    10. # -d meint directory.
    11. # und man sollte besser in Scripten prinzipiell immer mit absoluten Pfaden arbeiten.
    12. # nicht mit relativen, wie es hier ist.
    Alles anzeigen

    In Rechenzentren mit NAS (NetworkAttachedStorage) oder SAN (StorageAttachedNetwork) bei dem zahllose Hochleisungsrechner auf den Storagepool einhacken, kann es dennoch vorkommen, dass zwei Maschinen die gleiche Datei erzeugen wollen. Damit keine verliert, ist es sicherer ein Verzeichnis zu erstellen. Die Operation "Erstelle Verzeichnis" ist garantiert atomar. Was heißt, dass wirklich nur ein einziger Prozess das Verzeichnis erstellen kann. Es könnte sonst zu Rechteproblemen oder
    zu nicht gewolltem Informationsabfluss kommen.


    Jetzt kommen ein paar Verwirrungen, und wir zur eigentlichen Frage.
    Also fast gleich. (Das Kopieren der wtmps und Entpacken übergehe ich)

    Shell-Script

    1. echo "Liste alle log daten vom Nutzer $2 auf"
    2. $login = last -F -f ~/tmp/wtmp | grep $1 | cut -c 50-58

    Der grep Befehl nimmt einen anderen Parameter, als $2. Was steht denn __wirklich__ in $1?
    Ich nehme ja nur an, dass dort der Host stehen sollte. Du schweigst dich darüber aus.

    Damit führt die nächste Zeile ganz sicher zu einem Command-not-found Fehler,
    weil die Variable "zeit" niemals gesetzt wurde. Nach dem Parsen versucht die Bash also den Befehl:
    <leerzeichen>= date
    auszuführen. Den gibt es aber nirgends. Das ist also kapital falsch:

    Shell-Script

    1. "$zeit = date"


    Und das eigentliche Problem wird im nächsten Teil gelöst.
    (Grad mal 10 000 Zeichen zulässig! Wie soll ich da anständig labern?)
    Sokrates sagte, dass er nichts wisse.
    Ich bin viel, viel klüger als Sokrates.
    Ich weiß ganz genau, dass ich gar nichts weiß.

    Für den Inhalt des Beitrages 102469 haftet ausdrücklich der jeweilige Autor: Berichtigung

  • Teil 2

    Aber jetzt endgültig das eigentliche Problem.
    Du schneidest mit deiner Pipe das Login Datum und Uhrzeit aus.
    Die Dauer nicht. Was natürlich nicht genügt.
    Ziemlich einfach kann das mit awk bewerkstelligen. Der folgende Befehl macht das:
    last -F | awk -F '[ ]\+' '/'$user_to_search"'/{print $1,"\"" $4, $6, $7,$8 "\"","\""$10,$11,$12,$13 "\"", $15 }'
    Und das ist etwas erklärungsbedürftig.
    Klar ist der Anfang last -F | awk].
    Das -F '[ ]\+' schon weniger. Wir setzen damit die awk-interne Variable FS (FieldSeparator) auf de RegulärenExpression [ ]\+, der besagt, dass unser Feldtrenner eines oder mehrere Leerzeichen hintereinander ist. Da die "Felder" von last -F verschieden lang sein können, stehen zwischen den tatsächlichen Buchstaben verschieden viele Leerzeichen. awk aber interpretiert standardmäßig JEDES Leerzeichen als einen Feldtrenner, und die einzelnen "Felder" würden ganz verschiedene Nummern haben. (awk nummeriert die Felder jeder gelesenen Zeile einfach von 1 bin NF (interne awk Variable Number-of-Fields) durch. Die $0 wäre die gesamte Zeile. Nicht, das, was wir wollen. Aber mit unserem Regex haben wir das Anmeldedatum und das Abmelddatum an fixen Feldpositionen und können mit dem eigentlichen awk- Programm leicht das Gewünschte ausgeben.
    Ein awk Programm besteht aus Blöcken { dosomething }, dem ein logischer Ausdruck oder eine Adresse oder ein Adressbereich vorangestellt sein kann. Ist eine solcher Ausdruck einem Block vorangestellt, so wird der Block nur ausgeführt, wenn der Ausdruck wahr ergibt. Wir verwenden den Ausdruck [[/someUser/[/tt] und den Block { print........ $15 }. Damit wir in awk Shellvariablen verwenden können, gibt es mehrere Methoden. Ich bevorzuge die "Escape-to-Shell" Methode.
    '/'$user_to_search"'/ Der erste Apostroph beginnt das awk-Script. Und dort eröffnet der "Suchoperator" / den Ausdruck. Der zweite Apostroph beendet das awk-Script gleich wieder für kurze Zeit und "escaped" zur Bash, die jetzt für "$user_to_search" den aktuellen Inhalt der Shellvariablen user_to_search einsetzt. Das Quoting ist wichtig, damit nicht ein Leerzeichen innerhalb des Usernamens (ja, das ist erlaubt, aber dringend davon abzuraten) sofort zu einem Fehler führt. Man sollte (fast) IMMER solche Expansionen quoten.
    Jetzt drucken wir einfach die entsprechenden Felder, nicht ohne zwischen Login und Logout Zeitangaben doppelte Anführungszeichen einzufügen "\"" (awk will Zeichenketten. Innerhalb der "Zeichenkette" steht ein gequotetes ", was dann auch erscheint und eben kein "Hier-beginnt-ein-String"- Zeichen für awk ist.

    Damit erhalten wir Zeilen wie:
    looser "Wed 16 23:14:53 2016" "crash (20:52) "
    looser "Wed 16 21:45:37 2016" "Wed Nov 16 21:01:58"

    Und das lässt sich nun prima weiterverarbeiten. Wir haben auch noch die Dauer mitangeben. Wie man sieht kann ein Login auch crashen und die Einlogdauer ist dann kein korrektes Datum/Zeit Feld. Diese Behandlung überlasse ich euch.

    Wir machen jetzt aus den beiden Datum/Zeit Angaben rechenbare Sekundenangaben.
    Das liese sich sehr wohl auch in awk regeln. awk ist eine ziemlich mächtige Programmiersprache, die nicht nur Floatingpointrechnungen kann, was die Bash nicht kann, sondern auch allerlei Funktionen bereitstellt. Selbst Userdefined Functions und nachladbare awk-Bibilotheken kann man sich schreiben, laden und ausführen.
    Wir machen das dennoch in der Bash.
    Der Befehl date kann wesentlich mehr, als nur das aktuelle Datum ausgeben. Man kann sehr viele Datums und Zeitangaben basteln lassen.

    Shell-Script

    1. date -d "$(LANG=C date) + 7 days"
    2. date -d "$(LANG=C date) + 7 days" # relatives Datum/Uhrzeit
    3. date -d "$(LANG=C date) - 7 days + 3 hours"
    4. date +%s # heute in Sekunden seit 1.1.1970 00:00
    5. date -d "2016-7-14 - 10 years" +%A # welcher Tag in lokaler Sprache und langer Form war der
    6. #der 14.7. 1916?
    7. date -d @1234567890 +%c # welches Datum und Uhrzeit ist es 1234567890 Sekunden nach
    8. # dem 1.1.1970 in lokaler Sprache und Format
    9. # Und jetzt rechnen wir unser End_datum um.
    10. # blöd, das Start- und Enddatum in zwei verschiedenen Formaten vorliegen.
    11. startdatum="Wed 16 21:45:37 2016"
    12. end_datum="Wed Nov 16 21:01:58"
    13. # dem startdatum fehlt das Monat.
    14. # man müsste also aus den wtmps noch die vorhergenden Einträge heranziehen,
    15. # um festzustellen, ob um welches Monat es sich beim Startdatum handelt.
    16. # und das gilt auf für Jahrenwechsel generell.
    17. # Ich verzichte hier auf diese Tests.
    18. # Ich gehe von folgenden Formaten aus_
    19. startdatum="Nov 16 2016 21:45:37"
    20. end_datum="Nov 16 2016 23:01:58"
    21. timespan_in_seconds(){
    22. # wir sehen hier als $1 $2.. :
    23. # Nov 16 2016 21:45:37 Nov 16 2016 23:01:58
    24. start_seconds=$(date -d "$1 $2 $3 $4" +%s)
    25. end_seconds=$(date -d "$5 $6 $7 $8" +%s)
    26. echo $(( end_seconds - start_seconds ))
    27. }
    28. dauer_in_sekunden=$( timespan_in_seconds $start_datum $end_datum" )
    29. # und das noch schnell umgerechnet in Tage, Stunden, Minuten und Sekunden_
    30. tage=$(( dauer_in_sekunden / ( 60*60*24) )) # Sekunden divdiert durch das Produkt von (60Sekunden mal 60 Minuten mal 24 Stunden == Tage)
    31. # mit modulo nach dem Rest gucken:
    32. rest=$(( dauer % (60*60*24) )) # modulo ist Integerdivision, die den Rest ergibt.
    33. # die Stunden
    34. stunden=$(( rest / ( 60*60) )) # wie oben, aber halt ohne tage=24stunden
    35. rest=$(( rest % (60*60) )) # wir weisen den rest gleich wieder der var rest zu.
    36. # ... und nochmal für Minuten
    37. # und nochmal für Sekunden.
    38. echo user $user_to_search war insgesamt auf diesem System $([-z $tage || echo $tage) ..... eingeloggt.
    Alles anzeigen
    Es war mir nicht klar, was du mit dem Einlogzeitpunkt wolltest.
    Wenn du nur an der Gesamtzeit interessiert bist, geht das ganze natürlich viel einfacher. Ohne besondere Tests für Tages, Monats und Jahreswechsel, die hier gar nicht erscheinen.
    Ein schlichtes

    Shell-Script

    1. last -F | awk '/'"$user_to_search"'/{print $NF }'
    gibt dir alle Logindauereinträge für den User user_to_search. (Und interessiert sich auch nicht für evtl. aufgetretene Crashes oder sonstige Ungebilde)
    Die haben das Format (tage+stunden:minuten)

    Shell-Script

    1. dauer="(3+01:23)"
    2. dauer=${dauer:1:-1} # wegtrimmen der runden Klammern
    3. tage=${dauer%+*} # alles bis zum + Zeichen
    4. zeit=${dauer#*+} # alles nach dem +
    5. stunden="... wie oben...."
    6. minuten=" ... auch wie oben"
    7. # Umrechnung, wie gezeigt, wir brauchen alles in Sekunden.
    8. #alle Sekundeneinträge aufsummieren
    9. # und ausgeben.
    Natürlich sollte man die maximale Grösse von Integer, die die Bash verarbeiten kann berücksichtigen:

    Shell-Script

    1. echo dieses Script könnte Probleme machen, wenn Leute länger als
    2. $(( $(getconf INT_MAX) / (24 * 60* 60 *365 ) )) Jahre eingeloggt bleiben.
    3. echo und es macht ganz sicher Probleme wenn wir kurz vor dem $( date -d @$( getconf INT_MAX ) ) stehen.
    Sokrates sagte, dass er nichts wisse.
    Ich bin viel, viel klüger als Sokrates.
    Ich weiß ganz genau, dass ich gar nichts weiß.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Berichtigung ()

    Für den Inhalt des Beitrages 102470 haftet ausdrücklich der jeweilige Autor: Berichtigung


  • Wie man sieht kann ein Login auch crashen und die Einlogdauer ist dann kein korrektes Datum/Zeit Feld. Diese Behandlung überlasse ich euch.
    Faulpelz!

    Für den Inhalt des Beitrages 102475 haftet ausdrücklich der jeweilige Autor: Tar Zahn

  • Der Liebe Gott ist ein weiser Mann.
    Er gab mir meine überbordende Intelligenz,
    damit ich meine endlose Faulheit ausleben kann.

    Jeder hat mal Pech.
    In meiner Inkarnation ihr.
    Sokrates sagte, dass er nichts wisse.
    Ich bin viel, viel klüger als Sokrates.
    Ich weiß ganz genau, dass ich gar nichts weiß.

    Für den Inhalt des Beitrages 102533 haftet ausdrücklich der jeweilige Autor: Berichtigung

www.cyberport.de