Callmonitor 1.*

Status
Für weitere Antworten geschlossen.
kay1234 schrieb:
Vor allem das mittlere Ergebnis verstehe ich gar nicht. Das hätte nicht passieren dürfen. Muss das % evtl. gequotet werden?
Argh, ja, muss es, da das URL-Argument noch von printf interpretiert wird, und dort werden die Parameter ja durch % eingeleitet. Mit einem doppelten Prozentzeichen solltest du also ein "%" in der Ausgabe bekommen.

(Das mit den Variablen, die direkt Teile der URL ersetzen, war ein schneller Hack; das zahlt sich jetzt heim. Daran sollte ich vielleicht noch einmal etwas machen.)

Andreas
 
T-Net-Box

Warum krieg ich eigentlich nicht die T-Net-Box-Nummer als Caller (08003302424) eingetragen? Mit
Code:
08003302424 T-Net-Box

zeigt er nicht den Namen an. Ändere ich die 08003302424 in eine "normale" (mit ortsgebundener Vorwahl) Nummer, dann wird auch der Name korrekt angezeigt.

EDIT: Ich habs jetzt mit den Listeners gemacht. Dazu wollte ich dann die ganzen Befehle auslagern:
Code:
in:req,in:dis,out:req   ^   ^   runcaller()
ins Webinterface sowie eine Datei /tmp/flash/callmonitor/actions.d/*.sh mit dem Inhalt
Code:
runcaller() {
yac 192.168.1.23 "T-Net-Box hat neue Nachrichten"
}

In dem Fall kommt dann ein
Code:
including /usr/lib/callmonitor/actions.d/dboxlcd.sh
including /usr/lib/callmonitor/actions.d/dial.sh
including /usr/lib/callmonitor/actions.d/dropbear.sh
including /usr/lib/callmonitor/actions.d/mail.sh
including /usr/lib/callmonitor/actions.d/messages.sh
>>> in:request ID=1 TIMESTAMP=29.05.06 13:19 SOURCE=08003302424 DEST=0 EXT=4 DURATION=16
[0] EVENT=in:request SOURCE='08003302424' DEST='0' SOURCE_NAME='' DEST_NAME='' ID=1 EXT=4 DURATION=16 TIMESTAMP='29.05.06 13:19'
[0:0] processing rule 'in:req,in:dis,out:req' '^' '^' 'runcaller()'
[0:0] event 'in:request' matches pattern 'in:req'
[0:0] parameter SOURCE='08003302424' matches pattern '^'
[0:0] parameter DEST='0' matches pattern '^'
[0:0] SUCCEEDED: executing 'runcaller()'
/usr/sbin/callmonitor-test: eval: 1: Syntax error: end of file unexpected
Was mache ich falsch? Und wie kann ich mein Shellskript dann permanent implementieren außer es über die debug.cfg erzeugen zu lassen?

EDIT 2:
Code:
in:request      08003302424 ^ for client in "127.0.0.1" "192.168.1.22" "192.168.1.23" do yac $client "T-Net-Box hat neue Nachrichten" done
Sowas wäre eigentlich am besten. Loops scheinen aber wohl nicht zu gehen? Zumindest kommt der gleiche Fehler wie oben?
Gruß Niko
 
Zuletzt bearbeitet:
for_leases Funktion

Ich hab mal wieder ne Frage zu der for_leases Funktion...
Irgendwie scheint die nciht (mehr?) zu funktionieren?
bei mir wird bei diesem listener:
Code:
in:request,in:cancel ^ ^ for_leases 192.168.178. yac
und einer solchen multid.leases
Code:
lease 00:xx:xx:xx:xx:F8 192.168.178.50 1149127775 "rechner-a" 
lease 00:xx:xx:xx:xx:8E 192.168.178.21 1149101725 "rechner-b"
lease 00:xx:xx:xx:xx:B9 192.168.178.20 1149127303 "rechner-c"
immer nur der 1. Rechner benachrichtigt (also hier .50). Wenn der rechner nciht an ist kommt im callmonitor-testanruf-log ein "nc: Timed out" aber die anderen rechner bekommen trotzdem nix...
wenn ich die reihenfolge in der multid.leases verändere bekommt dann wieder nur der rechner der dann als erstes drinsteht die benachrichtigung...
Woran liegt das?

edit: kann das evtl. mit dem zusammenhängen was mein vorposter in seinem edit beschreibt, das loops nciht gehen? weil die for_leases funktion ist ja eigentlich auch nur ne for-Schleife...
 
jesus.christ schrieb:
Code:
[0:0] SUCCEEDED: executing 'runcaller()'
/usr/sbin/callmonitor-test: eval: 1: Syntax error: end of file unexpected
Was mache ich falsch?
Beim Aufruf einer Shell-Funktion dürfen keine Klammern verwendet werden; das Paar leerer Klammern deutet an, dass die Definition der Funktion folgt (meist in geschweiften Klammern): Deswegen das unerwartete Dateiende.

Code:
in:request      08003302424 ^ for client in "127.0.0.1" "192.168.1.22" "192.168.1.23" do yac $client "T-Net-Box hat neue Nachrichten" done
Sowas wäre eigentlich am besten. Loops scheinen aber wohl nicht zu gehen?
Syntax-Fehler; wenn du eine for-Schleife in einer Zeile schreibst, brauchst du vor dem "do" und vor dem "done" je ein Semikolon (bei Verteilung auf mehrere Zeilen sorgt sonst der Zeilenumbruch für die Trennung):
Code:
for n in 1 2 3; do echo $n; done
Du solltest dir vielleicht eine Einführung zur Shellprogrammierung suchen. In den Listeners funktioniert jeder beliebige Shell-Code-Einzeiler, aber korrekt muss er schon sein. ;-)

Andreas
 
ich hatte mir sogar eine shell-einführung angeguckt ^^ aber leider stand in dieser nix zu einzeilern drin. jetzt funktioniert es prächtig, vielen dank :)
 
Hi!
also ich hab jetzt mein problem mit der for_leases Funktion mal selber etwas genauer untersucht... das Probelm scheint darin zu liegen, dass sich "for" innerhalb des callmonitors irgendwie anders verhält als auf der Konsole....
auf der Konsole erzeugt folgender Befehl:
Code:
/var/mod/root # for IP in `fgrep -i "192.168.178." /var/flash/multid.leases | awk '{ print $3 }'`; do echo "z$IP"; done        
z192.168.178.50
z192.168.178.20
/var/mod/root #
(man beachte das z)
schreibt man diese Zeile nun in die Listeners Datei erscheint im log vom Testanruf folgendes:
Code:
[0:0] SUCCEEDED: executing 'for IP in `fgrep -i "192.168.178." /var/flash/multid.leases | awk '{ print $3 }'`; do echo "z$IP"; done'
z192.168.178.50
192.168.178.20
An dem z kann man sehen das die Schleife hier nur einmal ausgeführt wird und in $IP beide IPs mit einer Zeilenschaltung dazwischen stehen...
erzeugt werden die Zeilenschaltungen in dem print von awk, da dies immer eine Zeilenschaltung ans Ende stellt.
Dies ist auf der Konsole also nciht anders... nur dort Interpretiert das for das anscheinend anders. (wobei sich mir die frage stellt ob das for in der Konsole das nciht eigneltich falsch macht, denn so wie ich das verstanden hab sollten die for listen per definition mit leerzeichen und nciht mit zeilenschaltungen getrennt sein, oder?)

Meine Lösung für dieses Problem ist ein Workaround der dem awk sagt doch bitte statt der Zeilenschaltung nur ein leerzeichen einzufügen:
Code:
for IP in `fgrep -i "192.168.178." /var/flash/multid.leases | awk '{ ORS=OFS; print $3 }'`; do `yac "$IP" &`; done
allerdings halt ich das irgendwie für keine besonders schöne Lösung...
@buehmann: Könntest du das vieleicht trotzdem in die /usr/lib/callmonitor/actions.d/messages.sh aufnehmen, falls du keine bessere lösung findest?
 
ist es eigentlich auch möglich, den callmonitor zu priorisieren? wenn ich ihm mit nice eine höhere priorität zuordne scheint das nämlich nicht groß zu interessieren...
 
Hallo SeeDyX,

danke für deine ausführliche Analyse. Sie hat mich direkt zum eigentlichen Problem geführt. Das unterschiedliche Verhalten von for lag am unterschiedlichen Wert der Shell-Variablen IFS, die angibt, welche Buchstaben Felder trennen. IFS bekam in einer Funktion einen anderen Wert; diese Änderung blieb leider nicht lokal. (In Shell-Programmen ist leider jede Variable von vornherein erst einmal eine globale; für größere Programme ist das einfach nichts. :-()

denn so wie ich das verstanden hab sollten die for listen per definition mit leerzeichen und nciht mit zeilenschaltungen getrennt sein, oder?
Genau das hat mich auf das Problem geführt; mit IFS lässt sich das Verhalten einstellen.

Ich habe den Fehler korrigiert und werde spätestens Ende des Wochenendes eine neue Version onlinestellen.

Andreas
 
jesus.christ schrieb:
ist es eigentlich auch möglich, den callmonitor zu priorisieren?
Hast du dir eine eigene busybox mit nice bzw. renice erstellt? Sonst ist im ds-mod nice doch gar nicht dabei.
wenn ich ihm mit nice eine höhere priorität zuordne scheint das nämlich nicht groß zu interessieren...
Was meinst du mit interessieren? Wenn ich z.B. "/etc/init.d/rc.callmonitor stop; nice -n -20 /etc/init.d/rc.callmonitor start" sage, läuft der Callmonitor hinterher mit höchster Priorität.

Andreas
 
Also,
als Tool verwende ich snice, siehe
http://www.ip-phone-forum.de/showthread.php?p=589458&highlight=snice#post589458

Da ist auch ein top (procps version 3.2.6) dabei, welches dann auch die priorität zeigt, im gegensatz zum top aus der busybox vom ds-mod. In diesem top bekomme ich aber seltsamerweise nicht den callmonitor angezeigt und folglicherweise auch nicht die Priorität, deswegen meine etwas ungeschickte Aussage "es interessiert ihn nicht". Bin jetzt deswegen noch etwas verwirrt ;) Ich hoffe halt auch, dass ich ihn durch die höhere Priorität dazu bewegen zu können, die Fenster etwas flotter anzuzeigen als er es im Moment tut, auch wenn meine Anweisungen bei den Listeners schon halbwegs komplex sind^^

Gruß Niko
 
Hallo Niko,
jesus.christ schrieb:
Da ist auch ein top (procps version 3.2.6) dabei, welches dann auch die priorität zeigt, im gegensatz zum top aus der busybox vom ds-mod.
ah, schick. In dem top sehe ich allerdings den callmonitor auch (hast du vielleicht nicht richtig sortiert; ich habe hier den Namen als Kriterium genommen):
Code:
top - 07:36:36 up 2 days, 13:32,  0 users,  load average: 0.26, 0.08, 0.02
Tasks:  39 total,   3 running,  36 sleeping,   0 stopped,   0 zombie
Cpu(s):   1.3% user,   3.2% system,   0.0% nice,  95.5% idle
Mem:     30780k total,    30216k used,      564k free,     1668k buffers
Swap:        0k total,        0k used,        0k free,    15484k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    5 root       9   0     0    0    0 S  0.0  0.0   0:00.00 bdflush
 7116 root       5 -10   496  496  460 S  0.0  1.6   0:00.00 callmonitor
 7107 root      -1 -20   496  496  456 S  0.0  1.6   0:00.09 callmonitor
 7115 root      14  10   508  508  460 S  0.0  1.7   0:00.01 callmonitor
 7117 root       9   0   496  496  428 S  0.0  1.6   0:00.03 callmonitor
  474 root       9   0     0    0    0 S  0.0  0.0   0:00.00 capitransp
  653 root       8   0   356  356  308 S  0.0  1.2   0:00.01 crond
Die verschiedenen nice-Werte habe ich mal zum Spaß mit der eingebauten Renice-Funktion von top (Taste "r") zugewiesen.
auch wenn meine Anweisungen bei den Listeners schon halbwegs komplex sind
:) Was machst du denn an komplexen Dingen? Den Callmonitor konnte ich damals stark beschleunigen, indem ich die Erzeugung neuer Prozess konsequent vermieden habe (keine Aufrufe von "["; "$(...)" nur, wenn nötig; etc.).

Andreas
 
callmonitor-1.3

Hallo,

1.3 ist ein Bugfix-Release:

[size=+1]Behobene Fehler[/size]
  • messages.sh: [post=605603]XBOX_CAPTION und DREAM_CAPTION vernünftig kodieren[/post] (URL + printf). Dank an kay1234.
  • callmonitor.sh: [post=613001]for_leases funktionierte nicht[/post], weil IFS einen falschen Wert hatte. Dank an SeeDyX.
Gruß,
Andreas
 
buehmann schrieb:
Hallo Niko,

ah, schick. In dem top sehe ich allerdings den callmonitor auch (hast du vielleicht nicht richtig sortiert;
Natürlich hast du mal wieder recht ;) Die PIDs vom Callmonitor sind so hoch, dass er sie nie angezeigt hat. Jedoch hab ich immer alle anderen Prozesse, die mich interessiert haben, gesehen, sodass mir nie aufgefallen ist, dass estwas fehlt^^

buehmann schrieb:
:) Was machst du denn an komplexen Dingen? Den Callmonitor konnte ich damals stark beschleunigen, indem ich die Erzeugung neuer Prozess konsequent vermieden habe (keine Aufrufe von "["; "$(...)" nur, wenn nötig; etc.).
Gut, was heißt komplex... einer meiner Listeners (OK, das ist auch der größte) sieht so aus:
Code:
#Festplatte mounten
#Nummer: 000 MOUNTHDD
out:request ^ 00066868433 umount /var/media/ftp/USB-Partition-0-1;mkdir /var/media/ftp/share/;
if mount -rw /dev/scsi/host0/bus0/target0/lun0/part1 /var/media/ftp/share -t vfat -o sync,uid=1; then
 if mount --bind /var/media/ftp/share/webtransmission/ /var/torrent && mount --bind /var/media/ftp/share/webtransmission/sysctl /var/system && mount --bind /var/media/ftp/share /var/mod/home/samba;then
 for client in "127.0.0.1" "192.168.1.22" "192.168.1.23" "192.168.1.26"; do yac $client "HDD wurde erfolgreich gemountet.";done;
else for client in "127.0.0.1" "192.168.1.22" "192.168.1.23" "192.168.1.26"; do yac $client "HDD wurde gemountet, jedoch trat beim Binding ein Fehler auf";done;fi;
else for client in "127.0.0.1" "192.168.1.22" "192.168.1.23" "192.168.1.26"; do yac $client "HDD wurde nicht gemountet.";done;fi
Wohl ziemlich unelegant, aber allzuviel Möglichkeiten hab ich da wohl nicht^^ Die Verzögerung ist halt schon gut zu spüren, liegt aber wohl auch eben an den mounts und den Schleifen.

Gruß Niko
 
Hallo Niko,

ja, das ist komplex. :) Ich würde dir für die Übersicht empfehlen, den Code in ein externes Skript zu stecken und in den Listeners nur aufzurufen; z.B. so:

In /tmp/flash/callmonitor/actions.d/jesus.sh:
Code:
jesus_mounthdd() {
    local share="/var/media/ftp/share"
    umount /var/media/ftp/USB-Partition-0-1
    mkdir "$share"
    if ! mount -rw /dev/scsi/host0/bus0/target0/lun0/part1 \
        "$share" -t vfat -o sync,uid=1
    then
        jesus_notify "HDD wurde nicht gemountet."
        return 1
    fi
    if  mount --bind "$share/webtransmission/" /var/torrent &&
        mount --bind "$share/webtransmission/sysctl" /var/system &&
        mount --bind "$share" /var/mod/home/samba
    then
        jesus_notify "HDD wurde erfolgreich gemountet."
        return 0
    else
        jesus_notify "HDD wurde gemountet, jedoch trat beim Binding ein Fehler auf"
        return 2
    fi
}

jesus_notify() {
    local msg=$1 client
    for client in "127.0.0.1" "192.168.1.22" "192.168.1.23" "192.168.1.26"; do
        yac "$client" "$mgs" &
    done
}
(zum Wirksamwerden den Callmonitor neustarten). In den Listeners dann nur
Code:
#Festplatte mounten
#Nummer: 000 MOUNTHDD
out:request ^ 00066868433 jesus_mounthdd
liegt aber wohl auch eben an den mounts und den Schleifen.
Ja, die vielen mounts brauchen schon ihre Zeit. Bei der Benachrichtigungsschleife könntest du mal probieren, ob parallele Ausführung etwas bringt. Ich habe im aufgeräumten Code oben mal ein passendes "&" eingefügt.

Gruß,
Andreas
 
Hi Andreas,
schon einmal vielen Dank im Voraus für deine Anstrengungen :)

Hab das Skript jetzt mal erstellt und den Listener angepusst. Wenn ich mit "/etc/init.d/rc.callmonitor stop" und "/etc/init.d/rc.callmonitor start" den Anrufmonitor neu starte, sagt er "jesus_mounthdd()" not found. Muss ich über das Webinterface neu starten? Oder irgendwo das zusätzliche Skript includen?

EDIT: Argh, eben hab ich den SSH-Server anstelle des Callmonitors übers Webinterface gestoppt und ich bin ja gar nicht zu Hause :( *KopfgegenWandhau* Muss jetzt wohl bis heut Abend warten, bis ich weiter spielen kann^^
EDIT2: Gibt es einen eleganten Weg, eigene Skripte dauerhaft im Flash zu speichern außer über die debug.cfg eine Datei zu erstellen?

Gruß Niko
 
Muss ich über das Webinterface neu starten? Oder irgendwo das zusätzliche Skript includen?
Nein, das Starten über das Webinterface macht genau das, was du von Hand gemacht hast. Und die Skripte werden automatisch eingebunden (wenn du den Debug-Modus anschaltest, siehst du das auch im Log). Ich hatte mich nur im Pfad geirrt: Es muss /tmp/flash/callmonitor/actions.local.d heißen.

Gibt es einen eleganten Weg, eigene Skripte dauerhaft im Flash zu speichern außer über die debug.cfg eine Datei zu erstellen?
Klar, nachdem du im obigen Verzeichnis die Datei erstellt hast (oder irgendwo sonst unterhalb von /tmp/flash), rufst du einfach "modsave flash" auf. Das packt /tmp/flash zusammen und speichert es im Flash.

Andreas
 
Cm / Yac

Hallo allerseits,

Ich versuche (vorerst) nur, die Kombination von CallMonitor und YAC zu optimieren: bei in,out:request soll beim listener das WaveOutVolumeLevel auf 50% vom aktuellen Wert gesetzt werden, bei in,out:disconnect dann wieder auf den alten Wert. Außerdem möchte ich die $DEST mir ebenfalls vom YAC-Listener anzeigen lasen - und nicht nur $SOURCE_NAME und $SOURCE.

unter actions.d/messages.sh hab ich folgendes gefunden:

yac() {
rawmsg -p 10629 -t "%s\0" -d default_yac "$@"
}
default_yac() {
echo "@CALL$SOURCE_NAME~$SOURCE"
}

Wenn ich jetzt als action nicht "yac 192.168.2.2" sondern
rawmsg -p 10629 -t "%s\0" -d echo "@CALL$SOURCE_NAME~$SOURCE" "$@"
nehme, erziele ich also schonmal das gleiche Ergebnis -
also wenn ich als action bspw.
rawmsg -p 10629 -t "%s\0" -d echo "@CALL$EVENT~$DEST~$SOURCE_NAME~$SOURCE" "$@" einsetze, müsste ich dann nur noch den YAC-Listener in die Richtung anpassen, dass er das event richtig interpretiert? (Request: volume down, disconnect: volume up) .. naja, und dann halt noch die DEST in den TrayBallonTip dazuschreiben.

Da ich mit meinem VS2005 allerdings nicht meine modifizierte YAC-Source kompilieren kann (war mal ein MS Visual c++ 7.00 projekt - irgendwas haut da mit der Konvertierung nicht hin - und ich hab auch kein Plan von C ..), frag ich mich nu obs nicht noch andere Programme gibt die ähnlich YAC auf RawMSGs warten und diese interpretieren?

Nur mal nebenbei noch die Meldungen von VS2005, vielleicht kann mir ja jemand mit etwas C-Erfahrung helfen:
fatal error CVT1100: Doppelte Ressource. type:MANIFEST, name:1,
language:0x0409. CVTRES

fatal error LNK1123: Fehler bei der Konvertierung in COFF: Datei ist
ungültig oder beschädigt. yac

Source von YAC gibts unter: http://sunflowerhead.com/software/yac/yac-0.16-src-win32.zip

Viele Grüße,
kugel
 
OK, ich habe jetzt mal weiterprobiert und es funktioniert auch sehr gut. Das parallele Abarbeiten vom YAC bringt übrigens einiges an Performance, danke für den Tipp!

Wenn jetzt das mit den Zeilenumbrüchen noch funktionieren würde wäre es nur noch genial ;)

Gruß Niko
 
Oh man, jetzt hab ich fast 10hrs durchgefrickelt.. ;)

Nachdem ich die anfänglichen Kompiler-Schwierigkeiten aus der Welt geschafft hatte, musst ich erstmal merken dass ich noch nie im Leben versucht habe C++ Code zu verstehen oder gar zu schreiben.. aber habs nun geschaft, dass der Listener wie gewollt die Destination-Nummer mit in den Tray-PopUp schreibt, und je nach Event die Volume auf 1/3 bzw. das 3-fache des aktuellen Wertes zu setzen..
d.h.:
im Haus gibts 2 MSNs, die in Benutzung sind:
- eine klingelt an beiden Nebenstellen, die andere nur an einer.
-> MSN1 -> MSN2
- der Computer der geregelt werden soll steht in der Nähe vom Telefon, welches auf beiden Nummern klingelt.
-
Leise gestellt wird bei in:request ^ ^ und out:request MSN1 ^:
- beim eingehenden Gespräch, egal auf welcher MSN, und beim Rauswählen vom Telefon in der Nähe des PCs

Anschließend musste ich auf folgende Möglichkeiten eingehen:
- connect - auf NST1 (MSN1) (ich geh ans Telefon) soll leise bleiben
- connect - auf NST2 (MSN1) (an der andern NST) wieder laut
- rein obligatorisch: keine NST geht ans Telefon, folglich kein connect -> wieder laut
- gesetzt 1. (ich geh ans Telefon) - muss bei in:disconnect auch wieder laut gestellt werden.

dazu kommen die events beim outgoing:
- connect - bleibt leise (ich will ja telefonieren und offensichtlich ist jemand rangegangen - die out:connect greift ja nur, wenn SOURCE MSN1 ist.
- disconnect - wird wieder laut (habe wohl telefoniert und aufgelegt)
- cancel - der Angerufene geht nicht ran etc..

Der YAC-Lister versteht das wie folgt:
"@CALL$SOURCE_NAME~$SOURCE~VD1~$DEST"
bsp: VD1 steht hier fuer: VolumeDown - und die 1 dafür, dass das Ereignis im Tooltray angezeigt wird und in die Log geschrieben wird - ist ja logischerweise bswp. bei out:disconnect nicht sinnvoll.

Mit VU0 könnte man also also Volume wieder hochstellen lassen, ohne einen sichtbaren Output beim YAC-Listener zu haben.

Unter Preferences kann man ja von der grafischen Oberfläche auf Windows Ballon Tips umsetllen - hab allerdings die Ausgabe der angerufenen Nummer (DEST) vorerst mal nicht im ToolTyp eingebaut.

Vielleicht will ja jemand von euch auch, dass sofort wenn jemand anruft die Musik leiser wird, und sobald ihr wieder auflegt, wieder lauter... ;)

Werde heute, wenn Interesse besteht, mal meine modifizierte YAC-Version uploaden, und kann auch gerne die dann zu verwendenen Actions im callmonitor gerne noch erläutern..

.. jetzt allerdings erstmal schlafen gehen.

Greets, kugel
 
Status
Für weitere Antworten geschlossen.

Zurzeit aktive Besucher

Statistik des Forums

Themen
246,146
Beiträge
2,246,880
Mitglieder
373,654
Neuestes Mitglied
hstoff
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.