David Sugar
Open Source Telecom.
2003-01-03
sugar@gnu.org, http://www.gnu.org/software/bayonne
This document is new and very incomplete. It currently has material
leftover from the original Bayonne manual, but will be written in a better
form to support the other two manuals, installation and scripting, as soon
as this is possible to do.
The most current release represents milestone #8, and represents the
final form of GNU Bayonne for the 1.0 release, scheduled for early July.
Between now and July the existing package will be refined and this
documentation will be extensively revised.
With Milestone #7 we introduced broad support for high density digital
telephony hardware, primarily from Intel/Dialogic and Aculab. With high
density digital (PRI) support, it became possible to use GNU Bayonne for
various carrier class applications.
Milestone #6 introduced support for XML based scripting. XML support was
created by directly transcoding XML content into the Bayonne ccScript virtual
execution environment, and then running standard Bayonne applications.
Starting With milestone #5, Bayonne supported FreeBSD in addition to GNU/Linux
(and possibly Solaris). 0.5.0 is the first release which demonstrability
works under FreeBSD and also happens to support the Voicetronix 4 port
analog DSP card which will be used as the core of Bayonne VoIP
development in coming weeks. In addition, support for Aculab and Dialogic
hardware has been initiated, though it is not yet complete.
The fourth Bayonne milestone featured many changes, starting with
international support and the new French language vocabulary. There are
many less visible changes that effect and expand the scope of DSO
capabilities for the Bayonne server as well as greatly improve stability
of the server. With 0.4.2, expanded support for initiating dial-out
sessions has been added along with preliminary support for locating
streams and the Dialogic SDK.
The 3rd milestone release of Bayonne is being made available from
ftp://www.voxilla.org/pub/bayonne immediately. This release represents
both a more rigorously tested server, and a "usable" or "deployable"
server. An audio library has been recorded for use with the new
phrasebook system, and some documentation has been drafted. Many
features have also been clarified and greatly expanded upon for better
use in deployable servers. A lot of bugs were also found and fixed
between 0.2.1 and 0.3.
In particular a number of new issues were identified for both QuickNet
and Pika hardware. In the case of QuickNet cards, it was found to be
necessary to do a full close/reopen of the /dev/phone devices to
reset the card otherwise they stop responding between calls, and this was
done in 0.2. When the server is started as root, the /dev/phone devices
are initially opened under root, but then the server reduces its
privilege to "mail" and can no longer re-open /dev/phone. In 0.3,
the server now resets file ownership of /dev/phone devices.
~
/.bayonne. This has not been
changed in awhile, and is only useful if one either always runs bayonne
unprivileged, or if one is starting multiple instances for different
users.
Virtually all basic server administration functions are performed through
a single script file, which runs and manages the bayonne service on your
server. This script file is named ``bayonne'' and lives in
/usr[/local]/sbin. This script can often be executed either by the root
user or by a user under the effective id or with shared group permission
with the ``bayonne'' user, which should have been created in /etc/passwd
and /etc/groups.
To start ``bayonne'' in a runtime installation, it is often only nessisary
to execute /usr/sbin/bayonne. The script will then run the server based
on the current configuration file settings. The default driver specified
in /etc/sysconfig/bayonne will be used for telephony control, and the
server will start in deamon mode.
To shutdown the bayonne service, you can use the command ``bayonne
-down''. This sends a shutdown request to the server, which then exits
from daemon mode.
To start the server in runtime configuration for testing, you can use the
command ``bayonne -trace''. This starts bayonne in trace mode, and runs
the server in the foreground. All server activity will then be shown on
the console.
To issue control requests to a bayonne server running on your machine in
daemon mode, you can use ``bayonne -control''. Following ``-control''
you would enter a fifo control command, and one or more arguments. For
example, to start an outbound call session from the daemon, you can use
``bayonne -control start 2 myscript''.
To get the daemon to recompile scripts you can use ``bayonne -compile''
as a convenient shortcut. You can also use ``bayonne -service [mode]''
to set the service level of the daemon while running.
Since we no longer distribute most voice libraries with Bayonne itself, we
now use the bayonne script to install these remotely from the gnu
repository. To install the ``RussianF'' voice library, for example, one
would use ``bayonne -voices RussianF''. This will then fetch and install
the specified voice library from ftp.gnu.org.
The TGI equivalent of ``Hello World'' in Perl using the TGI module is:
hello.scr:
answer 2 # pick up the phone libexec 10 hello.pl slog %return
hello.pl:
use TGI; use lib $ENV{'SERVER_LIBEXEC'}; TGI::set(return => 'Hello World!');
This will cause the message 'Hello World!' to be slogged from
Bayonne. Here's a more complex example:
hello.scr:
answer 2 set %a 1 set %b 1 libexec 10 adder.pl %a %b if % return -eq -1 ::error slog %return # outputs ``2'' ::error play *::999 # ``An error has occured, please see the server error log for details.'' slog ``Something went wrong a couple lines ago. Bailing out.'' hangup exit
adder.pl:
use TGI; use lib $ENV{'SERVER_LIBEXEC'}; my $a = $TGI::QUERY{'a'}; my $b = $TGI::QUERY{'b'}; if(! defined $a || ! defined $b) { printf(``Missing either A or B or both.''); TGI::set(return => -1); exit(1); } TGI::set(return => $a + $b);
This should output the number 2. Note the extensive error checking.
Telephony applications are very unlike other computer applications in
that they need to be more ``fail-soft'' than a traditional windowed
application. In other words, only very serious errors such as an
inability to contact a database server or a critical transaction
failure should prevent the user from continuing with the telephone
call. If a possible error really isn't that serious and it's possible
to continue with the phone call even if it occurs, make sure to insert
a comment noting that fact in your code.
The rule of thumb for handling TGI errors is: hanging up on users
without telling them why is just plain rude. Don't do it. Make sure
that you have consistency in your error handlers and that you always
play a speech message indicating that something went wrong before
hanging up. It's a good idea to have the generic error message
include an instruction for the caller to ``call the people who are
hosting the application with the current time'' as that will help them
search the error logs for the output that occurred during the failed
call.
use TGI.pm; use lib $ENV{'SERVER_LIBEXEC'}; TGI::set(return => 0); # 0 is the return value exit(0);
Variables set with the TGI::set method become accessible in the
calling ccScript via the ccScript variable of the same name. In this
example, the %return variable will be set to 0. These return values
can be numbers or characters. You can return as many or as few
variables as you want, but it's typical for an application to return a
numeric error code in the %return variable so that error checking
code after libexecs is more consistent.
This is done in the [wrappers] section of Bayonne.conf. Basically one
enters the user id and a list of permitted scripts. This list of
permitted scripts always execute from
/usr(/local)/share/bayonne/aawrappers. Bayonne_wrapper itself runs as a
setuid executable, and the scripts or shell commands found in aawrappers
are given the user id of the Bayonne server. These scripts also receive
useful information in their environment space that is similar to "TGI",
but without port specific information.
In particular, a wrapper initiated image receives the server software
and version information (BAYONNE_SOFTWARE, BAYONNE_PROTOCOL,
BAYONNE_PLATFORM), the token separator to use for fifo control
(BAYONNE_TOKEN), the Bayonne node id (BAYONNE_NODE), and the location of
the Bayonne fifo and control files (BAYONNE_CONTROL). In addition, the
defined trunk group policies are passed as "BAYONNE_POLICIES". No
"PORT_xxx" symbols are passed since the wrapper script is not necessarily
executing on behalf of a specific and dedicated port resource.
NOTE: These symbols were originally named "SERVER_" in bayonne_wrapper,
but were changed to "BAYONNE_" starting with 0.5.17 to avoid conflicts
with CGI initiated scripts.
A web server or any other application can use wrapper to invoke
executables in aawrappers that manipulate the Bayonne server in a
controlled and secure fashion. A wrapper can be invoked thru a CGI and
then receive both the web server's CGI state information, and the Bayonne
symbol set. A CGI executed wrapper of course can send html back thru
stdout to form a web page for the user.
Starting with "0.5.17", Bayonne wrappers can also be used to execute
scripts that require interpreters in a fairly direct fashion. For
example, if we have a "webdial.cgi" script written in Perl, you will be
able to specify as the first line of your script file:
#!/usr/sbin/bayonne --wrapper -perl
This will re-execute the script with the Perl interpreter under the
specified user id of Bayonne. Similarly, one can then use -bash for
/bin/bash, and others for other common shells and interpreters. This
provides a simple and direct means to integrate cgi operation with a
Bayonne server. In fact, one could use the alias map of apache or other
web servers to map the aawrappers directory under a "/bayonne" url as a
convenient means to access commonly used wrappers in this way, and
in the future some scripts will be provided in the default distribution
of Bayonne for this purpose.
Bayonne -wrapper support the use of "ssh" to invoke a bayonne wrapper
application on a Bayonne server from a remote machine. This is done by
placing a truncated /etc/bayonne.conf file which indicates the node name
of the Bayonne server it should contact, and the wrapper permissions to
use. The "bayonne_wrapper" will assume to automatically use ssh if the
/usr/share/aawrappers directory is not found on the local machine.
On the remote (web server) machine, you will need to create a bayonne
"user id". This user id should contain a .ssh/config "entry" with the
same name as the Bayonne "node" id in the local /etc/bayonne.conf file,
and should list the hostname to actually contact.
You will also need a passwordless ssh key to use between the Bayonne
server and the web server. This will allow the bayonne_wrapper to
automatically hop between the machines.
The bayonne_wrapper can be symlinked or placed in the web server's cgi
directory under the name you will execute the program in "aawrapper" on
the Bayonne server under. Hence, you would take bayonne_wrapper, call it
for example "webdial.cgi", and place it on the cgi directory of your web
server. Since there is no local aawrappers directory, it will initiate a
ssh to the Bayonne box and then execute the "real" webdial.cgi from the
aawrappers directory there.
SSH does not automatically preserve environment state variables. While
bayonne_wrapper has supported ssh for awhile, it is only with 0.5.17 and
above does it now preserves key CGI environment variables when it hops
between the local and remote machine.
Certainly one can execute and access Bayonne resources directly if one
places a web server on a machine running Bayonne and then has it execute
under the same user id that Bayonne does. This allows one to use
one of the various highly efficient "mod_xxx" interpreters (like mod_php
or mod_perl) to build a web integrated Bayonne service rather than
requiring cgi wrappers everywhere.
Incidentally, Bayonne can of course alternately be executed under the same
user id as the web server. Which one to change probably depends on your
actual need. You can of course also run multiple web server daemons, and
have one of them execute under Bayonne's user id.
Why we choose not to use an existing framework is also simple
to explain. We knew we needed threading, and socket support,
and a few other things. There were no single framework that
did all these things except a few that were very large and
complex which did far more than we needed. We wanted a small
footprint for Bayonne, and the most adaptable framework that
we found at the time typically added several megabyte of core
image just for the runtime library.
GNU Common C++ (originally APE) was created to provide a very easy
to comprehend and portable class abstraction for threads, sockets,
semaphores, exceptions, etc. This has since grown into it's own
and is now used as a foundation of a number of projects as well as
being a part of GNU.
In addition to having portable C++ threading, we needed a scripting
engine. This scripting system had to operate in conjunction with a
non-blocking state-transition call processing system. It also had
to offer immediate call response, and support several hundred to a
thousand instances running concurrently in one server image.
Many extension languages assume a separate execution instance (thread or
process) for each interpreter instance. These were unsuitable. Many
extension languages assume expression parsing with non-deterministic
run time. An expression could invoke recursive functions or entire
subprograms for example. Again, since we wanted not to have a separate
execution instance for each interpreter instance, and have each instance
respond to the leading edge of an event call-back from the telephony
driver as it steps through a state machine, none of the existing common
solutions like TCL, Perl, guile, etc, would immediately work for us.
Instead, we created a non-blocking and deterministic scripting engine,
GNU ccScript.
GNU ccScript is unique in several ways. It is step executed, and is
non-blocking. Statements either execute and return immediately, or they
schedule their completion for a later time with the executive. A given
"step" is executed, rather than linearly. This allows a single thread
to invoke and manage multiple interpreter instances. While GNU Bayonne
can support interacting with hundreds of simultaneous telephone callers on
high density carrier scale hardware, we do not require hundreds of
native "thread" instances running in the server, and we have a very modest
CPU load.
Another way GNU ccScript is unique is in support for memory loaded
scripts. To avoid delay or blocking while loading scripts, all scripts
are loaded and parsed into a virtual machine structure in memory. When
we wish to change scripts, a brand new virtual machine instance is created
to contain these scripts. Calls currently in progress continue under the
old virtual machine and new callers are offered the new virtual machine.
When the last old call terminates, the entire old virtual machine is then
disposed of. This allows for 100 % uptime even while services are
modified.
Finally, GNU ccScript allows direct class extension of the
script interpreter. This allows one to easily create a derived dialect
specific to a given application, or even specific to a given GNU Bayonne
driver, simply by deriving it from the core language through standard C++
class extension.
To do this, we made Bayonne itself operate as a series of base classes, and
then create derived classes which implement functionality as separate plug-in
modules with static initialized objects. The server exports it's own symbol
map much like a library, and so when the derived plug-in is loaded, the static
objects are instantiated, and their constructors are called. These
constructors link with and are resolved to base class constructors in the
server image which are then automatically invoked and are used to register
the plug-in with the server.
The server itself also instantiates some objects at startup even before
main() runs. These are typically objects related to plug-in registration
or parsing of the configuration file.
The Bayonne driver architecture uses a ``plug-in'' style interface to
abstract driver-specific details from the ccScript application
accessing the hardware. Each plug-in provides state handlers for each
call state (ringing, playing audio, conferenced, etc) and possibly an
event thread.
All Bayonne drivers have a static instance of their driver class
defined at the bottom of driver.cpp. For example, the Voicetronix
driver has the following statement at the bottom of its driver.cpp
file:
VPBDriver vpbivr;
The VPBDriver class (which represents the driver for all Voicetronix
cards in the system) inherits from the Driver class, so a call to the
Driver class constructor is triggered when the Voicetronix driver is
opened. This is a feature of most UNIX implementations of C++. On
those platforms (which are at least BSD and Linux), if at run-time, a
shared object containing a static instance of a class is loaded, that
class' constructor will be called as well as constructors for any
parent classes.
The Driver class constructor is defined as follows:
Driver::Driver() : aaScript() { active = false; if(driver) throw(this); groups = NULL; driver = this; status = NULL; memset(spans, 0, sizeof(spans)); memset(cards, 0, sizeof(cards)); }
The ``driver'' variable seen here is of type ``Driver *'' and is a
static global variable defined in bayonne.h. After pointing the
``driver'' global pointer to the instance of the driver containined in
the driver plug-in, the result is that the rest of the bayone code
(mainly in server.cpp) can now access the underlying driver via the
abstraction functions defined in class Driver. The two most important
functions are start() and stop(), which allow Bayonne to control a
driver's active state. There are also several ``get'' functions
defined in class Driver which allows Bayonne to interrogate a driver's
capabilities and port count. To recap, here's a summary of the driver
start-up sequence:
1) The driver DSO is loaded via plugins.loadDrivers().
2) This results in a call to Driver::Driver().
3) This causes the global ``driver'' pointer to point to the newly
created instance of the child class driver.
4) The child class driver constructor (e.g. VPBDriver::VPBdriver()) is
called. This is where ports actually get initialized and the
hardware is prepared to begin receiving calls.
5) Bayonne calls driver->start().
6) This results in a call to the driver-specific start() function,
because Driver::start() is declared virtual. In the case of the
Voicetronix driver, this is VPBDriver::start(). Any event threads
are started up and the hardware begins processing calls.
The proposed solution is to create a libbayonne.so and have a simple
wrapper program that starts up Bayonne and links in the drivers at
run-time. This has not been completed as of yet.
Each state's state handler is a function of the name
``Trunk::stateHandler'', where Trunk is the driver's trunk class
(DialogicTrunk, VPBTrunk), and ``state'' is replaced with the state
name that will be handled. For example, the ring handler for the capi
driver is CapiTrunk::ringHandler. The state handlers are handed
arguments of type TrunkEvent, which contains the event ID and a union
containing data the event handler will need to serve the event (such
as digits to be dialed or timeout durations).
Each state handler is initially called with an event type of
TRUNK_ENTER_STATE by postEvent, which gives the state handler a chance
to set up anything it needs to service other events while in that
state. postEvent and the event thread(s) (if any) then continue to
call the event handler as events come in and need to be processed.
Note that the execution of the state handler (or rather the
``handler'' function pointer defined in the Trunk coass for the
driver) is protected by a mutex so that it is not possible to have
more than one state handler executing concurrently on a given trunk.
However, state handlers do execute concurrently across trunks. That
is, a machine with 4 analog trunks will most likely have four trunk
threads going simultaneously, possibly served by a single event
thread. The programmer is urged to keep in mind the current thread
context whenever dealing with data that could possibly be accessed by
several trunks simultaneously, and use mutexes as appropriate to
protect the data.
accept
Supported by: dialogic
Comes from: step
Goes to: step
Accepts an incoming ISDN call and opens up a timeslot for it. See reject.
answer
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step
Places the port off-hook.
busy
Supported by: dialogic, dummy, phonedev, pika, vmodem, vpb
Comes from: idle
Goes to: idle
Busies out the port so incoming calls are denied. For analog cards,
places the port off-hook. For digital cards, FIXME does what?
collect
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step
Begins collecting digits into the digit buffer. When the specified
number of digits has been collected or the specified timeout has
occured, stops collecting digits and continues executing the script.
detect
Supported by: capi20 (broken), pika (broken)
Comes from: dial, step
Goes to: step
For the Pika driver, handles tone events from the event thread. The
reference in the capi20 driver is an anomaly and will be removed.
dial
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: flash, step
Goes to: step
Dials DTMF digits.
duplex
Supported by: capi20, pika
Comes from: step
Goes to: step, exit
Begins recording and playing audio. Its presence in the capi20 driver
is an anomaly.
exit
Supported by: pika
Comes from: duplex, play, record
Goes to: play
FIXME: not sure
flash
Supported by: aculab (broken),
Comes from: step
Goes to: step, dial
Does a hookflash.
flashon
Supported by: capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: dial, step, flashoff
Places the port temporarily on-hook to start a hookflash.
flashoff
Supported by: dialogic, dummy, phonedev, pika, vpb
Comes from: flashon
Goes to: dial, step
Places the port back off-hook at the end of a hookflash.
hangup
Supported by: aculab, capi20, dialogic, dummy, oh323, phonedev, pika, rtp, vpb
Comes from: hangup, seize, dial, step
Goes to: idle
Places the port on-hook (analog cards) or shuts down the timeslot
(digital cards).
idle
Supported by: all drivers (this is a required state)
Comes from: driver startup, hangup, ring
Goes to: busy, seize, step, release, ring
Stops activity on the port and shuts down any executing script images.
join
Supported by: aculab, capi20, dialogic, pika, vpb
Comes from: step
Goes to: step
Joins two ports together so they can hear and speak to each other.
The target port must be in the ``wait'' state.
load
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step
Loads an XML service. See the section on web services in this manual.
play
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step
Begins playing audio from a file.
playwait
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step, play
Waits for the completion of a play service thread.
record
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step
Begins recording audio to a file.
reject
Supported by: dialogic
Comes from: step
Goes to: step
Rejects an incoming call via the digital signalling channel. See
accept.
release
Supported by: dialogic
Comes from: (nowhere)
Goes to: idle
FIXME: Doesn't seem to be called from anywhere in the Dialogic driver.
ring
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vmodem, vpb
Comes from: idle
Goes to: step, idle
Recognizes an incoming ring event and increments the ring counter,
then determines whether to start up a script. In some drivers (such
as Voicetronix), captures caller ID.
rtp
Supported by: vpb (broken)
Comes from: (broken)
Goes to: (broken)
Connects a call with a RTP stream. Doesn't work right now.
seize
Supported by: dialogic, dummy, phonedev, vmodem, vpb
Comes from: idle
Goes to: step, hangup
Grabs a port in preparation for outbound dialing.
sleep
Supported by: aculab, capi20, dialogic, dummy, phonedev, pika, vpb
Comes from: step
Goes to: step
Waits for a specified duration or number of rings.
step
Supported by: all drivers (this is a required state)
Comes from: all states
Goes to: most states
Executes the next step in the script image and changes the current
state if needed. This is probably the most frequently used state.
tone
Supported by: aculab, capi20, dialogic, pika, vpb
Comes from: step
Goes to: step
Generates a tone of user-specified frequency and duration.
wait
Supported by: phonedev, vpb
Comes from: step
Goes to: step, dial
FIXME: I'm not sure what's going on in the phonedev driver, but the
vpb driver is placed into the wait state in preparation for a join
from another port.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts
This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.70)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -nosubdir -split 0 admin.tex
The translation was initiated by on 2004-02-04