In all of the function definitions in this document, if there is a ‘*’ next to the function name, you can execute
the function when you aren’t logged in. Otherwise, you must be logged into the system. In a related vein,
many functions have the notion of a current box. When you don’t give the -box option, they operate on the
current box. When you are not logged in, there is no notion of a current box and these functions require the
-box option.
Commands syntax is verb noun. Normally, if a command can return one or more results, the ‘s’ suffix is
left off the command (e.g., set box setting rather than set box settings). This makes it easier for the
programmer to remember the name of the command. Commands that require a box parameter (e.g., is box)
do not use the -box syntax; the box is simply given as a required parameter. The -box syntax is for options
only.
A fundamental theme in Amanda Portal is the notion of the box hierarchy and permissions related to the
box hierarchy. Boxes create other boxes in an ancestral tree structure. Generally, unless specified otherwise,
boxes only have permission to modify their box settings and the settings of their descendents. They cannot
modify the setting of their proper ancestors.
Every command in this document can get a “usage” error if you call the command wrong. In this cas e,
errorCode will be set to USAGE and interp->result will be the usage of the function.
Many commands in this system may process half an argument list before discovering an error. This means
that half of the arguments changed values in the database and the other half didn’t. Care is taken to
alleviate this problem as much as possible though, for example, by checking the syntax of the arguments
before executing transactions against the database.
4
Chapter 1
Introduction
The Amanda Portal system consists of a core set of functionality, which defines basic building blocks from
which a variety of call processing systems can be built. A variety of plug-in modules, in the form of Windows
DLLs, can be installed which augment the functionality of the core. By using plug-in DLLs, a system can
be configured to load only the code and features that it requires. Since third parties can also develop these
DLLs, there is a great deal of flexibility and extensibility in the system. The core and the plug-in modules
together define an API which is similar to that of an operating system. They define a security model, provide
networking features, etc.
Above this “API” layer is a scripting language, the To ol Command Language (Tcl), which is now familiar
to over one million programmers world-wide. The scripting language serves two different purposes:
1. It acts as a “glue” to tie together the functionality of the different loadable modules (DLLs). As such,
it acts as a very high level language for voice processing needs.
2. It allows quick and easy customization of an existing system, or rapid development of whole new voice
processing solutions. The entire Amanda@Work.Group Telephone User Interface (TUI) consists of
only a few thousand lines of Tcl code.
This architecture has several advantages:
• It separates functionality (how a particular task is implemented) from behavior (why a particular task
should happen, such as a certain sequence of DTMF digits were input by a user).
• Because Tcl is the only interface to the system, network and telephone clients use exactly the same
interface to accomplish tasks within the system. There is exactly one function which can be used to
send voice mail to a mailbox, for instance; this function is used by all types of clients.
• Because the functionality is implemented in C++, it operates very efficiently, while the telephone user
interface is scripted because this is where a great deal of flexibility is required. The TUI Tcl script
is basically just implementing flow of control across all the possible functional calls (record, play, get
digits, send voice mail, etc.).
• Since all the functionality is implemented in C++ and is not maliable, it is possible to implement a
secure system. Users are allowed to execute any Tcl commands that are made available to them but
cannot side-step the security model that is built into the system at the lower level.
5
• This architecture allows the TUI to b e completely flexible without having to reimplement any functionality at lower levels. For instance, it would be quite possible to build a speech-recognition based
voice mail system which would be quite different from the DTMF-based, Amanda@Work.Group-like
interface that is currently provided. The menuing system would presumably be much “flatter,” but
the underlying features such as sending and listening to voice mail, changing mailbox settings, etc.,
would remain exactly the same at the C++ level.
1.1Core Features
The core module implements a number of fundamental objects in the system: mailboxes, MultiMedia Objects
(MMOs), a number of databases (the MMO lookup table, the voice mailbox database, etc.), an autoscheduler,
a notifier and outbound call job management system, etc.
As part of the system’s security model, mailboxes are arranged in a hierarchy. Every mailbox has a “parent”
mailbox which is the one which created it. A mailbox may be given the right to create subordinate, or
children, mailboxes. A top-level mailbox’s parent is itself, since no mailbox created it, and such a box is
referred to as a superbox in this manual. Such a mailbox is given certain special privileges in Amanda Portal,
similar to the “root” user on a Unix system. Unlike Unix, however, there can potentially be more than one
top-level mailbox (a “forest” of mailbox trees), although it is unlikely that this feature will be needed in
practice.
Mailboxes have several components: they have some settings (the list of settings is extensible to make
development of new applications more flexible), they have a set of privileges which have known semantics to
the core, they may have messages which they have received, and the messages in turn may have zero or more
MMOs associated with them. Boxes may “publish” MMOs for others to use, such as greeting recordings.
Boxes can establish autoschedule and notify records which cause the system to execute Tcl code on their
behalf when certain events happen or certain times arrive.
1.2Tcl As An Extensible Glue Language
Tcl makes a good embedded systems language because it is high-level, easy to extend the language by adding
C functions as Tcl primitives and has security mechanisms built in using the nested interpreter model and
“code injection”1. Nested interpreters give you an operating system like “system call” interface where you
write your “system calls” in Tcl. In this model, the system calls always exist in your interpreter. If you are
not allowed to perform a call, the system call returns an error. The master interpreter operates in “kernel”
mode and the slave interpreter operates in “user” mode.
Code injection works slightly differently. The protected calls you need to use don’t check whether you have
permission to execute them; instead, the calls don’t exist in your interpreter until a specified event happens
(such as you log in). At this point, the call is inserted into your interpreter and you can use the new calls.
When you log out, the calls are removed from your interpreter.
Tcl is a Lisp like language so writing code with it is quite “functional.” You often write code like
eval modify elements [get elements handle]
1
A tAA defined term.
6
where get elements returns the operands to operate on as a Tcl list and modify elements operates on
those operands. Tcl also helps extensibility by being interpreted. This means that you can add code on the
fly to your program and modify the behavior of the code shipped with the system. Tcl also has associative
arrays built in; these are the regular Tcl array variables. Internally, they are implemented as hash tables
so lookup on them is very speedy. Multi-dimensional arrays can be simulated in Tcl by using concatenated
indices in regular arrays.
1.2.1An Introduction to T cl
Safe interpreters are simply regular interpreters with certain “unsafe” calls removed; that is, they are interpreters where some unsafe code has already been “dejected.” All Tcl interpreters in Amanda Portal are of
the “safe” variety, and as such, they have only the following standard Tcl commands: append, array, break,
This subsection gives a brief description of each of these commands. Those who are already familiar with
Tcl should skip ahead to the next section. There are many books available which cover the Tcl language,
including Tcl and the Tk Toolkit, by Tcl’s author, John Ousterhout, and Tcl For Dummies, in the famous
“Dummies” series. While those books should be consulted for in-depth examples, they also cover many more
features of Tcl, plus the Tk graphical extensions, which are not used in Amanda Portal.
Tcl Variables
Programming languages contain two fundamental elements: variables and procedures. Object-oriented languages combine these two features into objects (naturally). Variables in Tcl have three attributes:
name The variable’s name allows access to its contents.
scope The scope of a variable determines how long it will live. When it goes out of scope, a variable is
automatically destroyed.
value Finally, each variable has a value.
In many programming languages, variables have a fourth attribute, a type, which determines the kind of
data that the variable can have as its value, the size of the date (range of values, length of a string, etc.). In
these languages, variables are declared, and a variable can exist without having a defined value.
Tcl’s variables, like those of Amanda@Work.Group, do not have a specific type. Internally, they are all
represented as strings and are converted on-the-fly as needed to other types (floating point, integer) when
necessary. This feature makes T cl programming easier. A variable springs into existence the first time it is
used. This also means that it’s impossible to have a variable which does not have any value.
To create a variable in Tcl, you use the set command, like this:
set variable value
For example, to set the variable myname to Tim, you would simply type
7
set myname Tim
To access the value of a variable, put a dollar sign in front of its name. For sample, to copy the value of the
variable myname to a second variable called firstname, you would type
set firstname $myname
This process is called variable substitution: the name of the variable is replaced by its value.
Each executable statement in Tcl consists of a single command line, which is terminated with a newline or
a semicolon. For instance,
set x 14
set y 27
could just as easily be written
set x 14; set y 27
Tcl evaluates each command line using a two-step process: parsing and execution. During parsing, the
command line is divided into space-separated arguments, then variable substitution and other processing is
performed on the arguments. At this point, Tcl doesn’t attach any meaning to the arguments themselves;
it’s just doing simple string substitutions.
In the execution step, the first argument of the line is treated as a command name to be run. The other
arguments on the command line are passed on to the command, which will apply meaning, if any, to them.
The command will return a string value as the result of its processing. In each of the examples above, the
command name was set.
What would happen if you tried to execute the command
set name Tim Morgan
This is a problem because set will see three arguments rather than the two it’s expecting (a variable to set
and a value to set it to). Just as in Amanda/DOS, we can overcome this problem by using quotation marks
to “hide” the space from the parser:
set name "Tim Morgan"
Now suppose that we have two variables, firstname and lastname, and what we need is the person’s full
name. We can achieve this by writing
set name "$firstname $lastname"
8
On the other hand, if we write
set name {$firstname $lastname}
then no variable substitution will be performed. We can also mix and match:
set salary "$firstname $lastname \$30,000"
This would expand the values of firstname and lastname, but it would leave the dollar sign before 30,000.
Be careful of the following types of commands:
set x 4
set y x
set z x+y
set $y 5
The first sets x to 4, as expected, but the second command sets y to the string x, not to the value of x.
Remember that Tcl does not associate any meaning with the arguments as it parses them. Therefore, the
third command simply sets variable z to the string x+y. We’ll learn how to accomplish addition shortly.
The last command sets x to 5, not y.
We have already seen an example of backslash substitution when we used $ to produce a literal dollar sign
character in a string. Many of the backslash substitutions are the same as those in Amanda/DOS:
SymbolMeaning
\aAudible alert (bell)
\bBackspace
\fFormfeed
\nNewline
\rReturn
\tTab
\vVertical tab
\dddOctal value
\xhhHex value
\newlineSingle space
\$Dollar sign
\\Backslash
\[Open square bracket
Tcl Lists
A Tcl list is an ordered collection of strings. In its simplest form, a list is s imply a string, with spaces
separating the list’s elements. In this case, the elements cannot contain spaces.
Lists are usually written surrounded by curly braces. Sublists, or elements with spaces, are denoted by inner
braces. For example,
9
set x {a b {c d e} f}
sets x to a list containing four elements. The third element is the list (or string) c d e.
Tcl contains a number of built-in functions for manipulating lists.
Built-In Commands and Procs
So once the command to execute has been determined, and Tcl knows what the arguments are, what happens
next? Tcl will look up the command name to verify that it’s valid. If it isn’t, then an error is generated.
If it is a valid command, then there are two possibilities: the command is a “built in” one or it’s a “Tcl
procedure.”
Commands (procedures, or “procs”) which you define in Tcl are indistinguishable from the built-in commands, except that the built-in ones are faster and can access operating system and device driver level
calls.
A “built in” Tcl command has its underlying implementation in C. In this case, Tcl invokes the corresponding
C procedure and processing of the command is done in compiled code. The set command is a built-in, for
example.
Tcl defines a number of built-in commands, and applications using Tcl can define additional built-in commands. Not surprisingly, Amanda Portaldefines quite a few application-specific commands which will be
covered in the Amanda PortalProgramming class.
So far, we’ve seen two kinds of substitutions which happen automatically to commands: variable and backslash. We also know that all Tcl comm ands (procedures) return a value. This leads to the third type of
substitution, command substition. To illustrate it, we’ll introduce another built-in command, expr.
Expressions
The expr command concatenates all its arguments into one long string, then it evaluates that string as an
expression, and it returns the resulting value as its procedure value (reme mber that all procedures return a
value in Tcl).
Now we’re ready to discuss the third type of substitution, command substitution. Recall that every command
returns a string result. Command substitution allows us to replace a complete Tcl command with its result
for use in an enclosing command. To use it, just enclose a command inside square brackets: []. For example:
% set x 4
4
% set y 7
7
% set z [expr $x * $y]
28
%
Arrays
So far, the variables we’ve used have been simple variables, also called scalar. Tcl also provides another
kind of variable, called an associative array. An associative array is a collection of different values which are
indexed by a string (the element name). This is a very powerful mechanism, and associative arrays are used
extensively in Amanda Portal.
Array elements thus have two names: the name of the array plus the name of the element. The element
name is enclosed in parentheses. It may be a constant or a variable (or a combination), so long as it doesn’t
contain a space. If the element name has a space, then the entire variable name must be quoted to “hide”
the space.
For example, we could collect information on a person this way:
set person(name) "Tim Morgan"
set person(age) 37
set person(address) "23822 Brasilia Street"
set person(city) "Mission Viejo"
set person(state) California
set person(zip) 92691
On the other hand, if we have information about a number of people, we might use one array per attribute,
and use the names as the indices:
set age(Tim) 37
set address(Tim) "23822 Brasilia Street"
set city(Tim) "Mission Viejo"
set state(Tim) California
set zip(Tim) 92691
set age(Siamak) 36
set address(Siamak) "26321 Eva Street"
set city(Siamak) "Laguna Hills"
set state(Siamak) California
set zip(Siamak) 92656-3108
We can use the values in the array pretty much just as with simple variables:
11
% set foo $age(Tim)
37
% set name Tim
Tim
% set foo $age($name)
37
% set foo [expr $age(Tim) + $age(Siamak)]
73
%
Besides set, Tcl defines a few other built-in functions for working with variables:
append Appends one or more values to a variable.
incr Adds a value to an existing numeric variable.
unset Deletes one or more variables.
array Returns various information about array variables.
The syntax for the append command is as follows:
append varname value .. .
If varname is an existing variable, then value will be appended to it, as will any additional values.
If varname didn’t exist previously, then it is set to value as if the set command had been used.
Like set, append returns the new value of varname as its result.
The incr command is exactly like the + token in Amanda/DOS. It allows you to do an arithmetic addition
to an existing numeric variable. The value added may be positive or negative, and it defaults to 1.
% set i 1
1
% incr i
2
% incr i 2
4
% incr i -3
1
%
The variable must exist and have an integer value. The incr function returns the new value as its result.
The unset command is used to delete a variable and its associated value(s). If an array variable is deleted,
then all of its elements are deleted. The general syntax for the command is:
unset var . . .
12
The unset command always returns an empty string as its result:
% set age(Tim) 37
37
% set age(Siamak) 36
36
% unset age
%
The Array Command
The array command allows you to obtain information about a Tcl array. The general syntax is:
array cmd name args
where cmd is a special keyword for the array command. Some of the available keywords are:
names ary Returns a Tcl list of the names (valid indices) for array ary.
size ary Returns the number of elements in array ary.
exists var Returns whether var exists and is an array.
get name pattern Returns a list of pairs of element names and values from the array name. If pattern is
specified, then only those elements whose names match pattern will be returned. A list of name and
value pairs is called an a-list.
set name list Sets elements of the array name from list which should be of the same form as is returned
by array get.
Normally, the array command will be used in conjunction with command substitution along with some other
commands which we’ll examine next week. In the mean time, here are some standalone examples of array,
using the age array we defined earlier:
% array size age
2
% array names age
Tim Siamak
Notice that the result of the array names command will list the names in arbitrary order. If you want it
sorted a particular way, you can use the lsort command discussed on page 16.
Tcl Expressions
Expressions combine values with operators to produce a new value. Simple examples follow everyday arithmetic rules. For example:
13
% expr (8+4) * 6.2
74.4
%
In addition to the usual binary operators +, -, *, and /, there are a number of other important unary and
binary operators (and there are some less important ones which we won’t mention). There’s also %, which
computes the remainder from a division (this is the “mod” or “modulo” operator).
Most operators are called binary because they work on two values. For instance, + is a binary operator since
it is used to add two different values, which are written on either side of it.
Unary operators, in contrast, are operators which work on a single value which comes after the operator.
The most familiar one of these is unary minus, which negates the value of its operand.
% set x 4
4
% set y [expr -$x]
-4
%
The other important unary operator is logical not, which is written !. It changes true values to false, and
false values to true.
In Tcl, as in C, logical values are represented as numeric values. A false value is represented as 0, while a
true is any non-zero value. Therefore,
% set x 4
4
% set y [expr !$x]
0
% set x [expr !$y]
1
%
Relational operators are binary operators to compare one value to another. They all work on integer, real
(floating point), and string values.
SyntaxResult
a<b1 if a < b else 0
a>b1 if a > b else 0
a<=b1 if a ≤ b else 0
a>=b1 if a ≥ b else 0
a==b1 if a = b else 0
a!=b1 if a 6= b else 0
Often you want to combine the results of more than one logical test. The logical operators allow you to do
so. They follow the same syntax as in the C language.
14
Logical and is represented as &&. The expression a&&b is true (1) if both the expressions a and b are non-zero,
and zero otherwise.
Logical or is represented as ||. The expression a||b is true (1) if either of the expressions a or b is true,
and zero otherwise.
As in C, these operators are short-circuit: the second operand is not evaluated if the first shows the result
of the whole thing.
Here are some examples of logical operators:
% set a 1
1
% set b 0
1
% expr a&&b
0
% expr a || b
1
% expr b || a
1
% expr b && exp(sin(sqrt(2.0))) > 4
0
% expr a || exp(sin(sqrt(2.0))) > 4
1
%
In the last two examples, function foo is not called, because the result can be determined from the value of
the first argument alone.
List Commands
Tcl defines quite a few commands for operating on lists. We’re going to look at the following ones:
lindex Retrieves individual elements from a list.
llength Returns the length of a list.
lsort Returns a new list resulting from sorting an existing list.
lappend Appends new items to an existing list (variable).
split Returns a list by spliting up a string at each point where a given substring occurs.
The lindex function returns as its value an element of a list. The general syntax is:
lindex list index
The index must evaluate to an integer. The first element of the list is number 0. For example:
15
% set mylist {a b {c d e} f}
a b {c d e} f
% lindex $mylist 1
b
% lindex $mylist 2
c d e
% set x {a b c d e}
a b c d e
% lindex $x 4
e
%
The llength function simply returns the number of elements in a list. Its syntax is
llength list
Continuing from the previous example:
% llength $x
5
% llength $mylist
4
% set len [llength [lindex $mylist 2]]
3
%
The lsort command returns a new list which is created by sorting an existing list. The syntax is:
lsort flags list
The flags parameter can be a combination of a type and a direction. The types are:
-integer Treat the list elements as integers.
-ascii Treat the list elements as strings (default).
-real Treat the list elements as floating point (real) numbers.
The direction can be one of the following:
-increasing Sort the items in ascending order (default).
The lappend command appends one or more values to a variable as list elements. It creates the variable if it
doesn’t already exist. It differs from append because with lappend, the items are appended with separating
spaces and will be given braces where necessary to keep items separate; append simply appends each string
as-is.
The general syntax is:
lappend varname value .. .
The lappend function returns the new value of varname as its result value.
Here are some examples:
% set x {a b c}
a b c
% lappend x d e f
a b c d e f
% lappend x "Tim Morgan" "Carl Doss"
a b c d e f {Tim Morgan} {Carl Doss}
% llength $x
8
%
Notice that in the second lappend command, two values are being appended. This is reflected in the result
of llength: there are eventually 8 items in the list.
The split command creates a new list by splitting up a string. You can specify a set of separator characters
as the second argument, or let it default to standard white-space characters.
linsert can be used to insert items into an existing list variable at a particular index.
*lrange
Description
lrange can be used to extract a range of items from a list (returning a new list).
*lreplace
Description
lreplace can be used to create a new list from and old list with a range of items replaced by a new set of
items (not necessarily the same number of items).
Eval
One final note on parsing. Sometimes you have a list of items in a variable, and you want to pass that list
as arguments to some command, such as lappend. Here’s an example of the problem:
% set mylist {a b c}
a b c
% set additional_items {d e f}
d e f
% lappend mylist $additional_items
a b c {d e f}
%
What we really wanted was to get a resulting list of {a b c d e f}, not {a b c {d e f}}. The eval
command lets us solve this and other similar problems.
18
The eval command takes one or more arguments and concatenates them into one long string. It then
evaluates (executes) this string as as a Tcl command. Let’s look at how we’d use eval in the previous
example:
% set mylist {a b c}
a b c
% set additional_items {d e f}
d e f
% eval lappend mylist $additional_items
a b c d e f
%
Why does this work? The eval command sees three arguments: lappend, mylist, and a string “d e f”
(the quotation marks aren’t part of the string). It appends all of these strings together, with separating
spaces, arriving at the new command string “lappend mylist d e f” (again, the quotation marks aren’t
part of the string). Then eval executes the command, and mylist gets three new elements instead of one
sublist element.
Control Flow
Normally, a program is executed sequentially, one statement after the previous one. Most programming
languages, including Tcl, provide two basic ways to vary this.
Branching means skipping to or past some statements. This is provided with the if and switch commands.
Looping allows a set of statements to be repeated zero or more times. This is provided by the while, for,
and foreach commands, along with their helper commands break and continue.
The testn values may be any expression which evaluates to a Tcl boolean value (zero or non-zero). Typically,
they will include relational operators. The bodyn values may be any Tcl statement or list of statements.
Remember that statements may be separated by semicolons or returns, and uninterpreted lists may be
written using curly braces, and they “hide” any embedded returns.
19
This means you can write statements like:
if {$x < 0} "set x 0"
if {$x < 0} {set x 0; set y 7}
if {$x < 0} {
set x 0
set y 7
}
But you may not write
if {$x < 0}
{
set x 0
}
The most common way to write if statements is like this:
if {$x < 0} {
...
} elseif {$x == 0} {
...
} elseif {$x == 1 && $y == 7} {
...
} else {
...
}
The switch statement allows you to match a string against a number of patterns, and if a match is found,
to execute some code. The pattern matching may be one of:
-exact The strings must match exactly.
-glob Wildcards * and ? can be used, just like the DOS DIR command; this is the default).
-regexp Regular expression pattern matching is applied—see the manual for details.
Suppose that you want to test $x to see if it’s any of the strings a, b, or c. You could do this with if:
if {$x == "a"} {
incr t1
} elseif {$x == "b"} {
incr t2
} elseif {$x == "c"} {
incr t3
}
20
With the switch statement, you can write this more clearly:
switch $x {
a {incr t1}
b {incr t2}
c {incr t3}
}
While while statement’s syntax is:
while test body
Here’s an example of reversing a list a, putting the result into list b:
% set a {1 2 3 4 5 6}
1 2 3 4 5 6
% set b {}
% set i [expr [llength $a] - 1]
6
% while {$i >= 0} {
lappend b [lindex $a $i]
incr i -1
}
-1
% set b
6 5 4 3 2 1
%
The for statement is usually used to perform a loop for a specified number of times, using a control variable
to count the number of iterations. The Tcl syntax, like that of C, is more general:
for init test reinit body
The for statement executes the init statement. Then while the test is true, it executes body and reinit.
Thus, for is really a while statement with a few other statements thrown into the mix. You can accomplish
the same things with either statement. When a loop is controlled by a variable, then using for will result
in more easily understood code.
21
To iterate variable i from 1 to 10, you would write
for {set i 1} {$i <= 10} {incr i} {
set array($i) "This is item $i"
}
To reverse list a, putting the results in b:
set b {}
for {set i [expr [llength $a] - 1]} {
$i >= 0} {incr i -1} {
lappend b [lindex $a $i]
}
The foreach statement is used very frequently in Tcl. It iterates a control variable through each item in a
Tcl list, executing a body of statement(s) each time. So to reverse a list, one could write
set b {}
foreach i $a {
set b [linsert $b 0 $i]
}
To reset each element of an array to an empty string, one could write:
foreach i [array names a] {
set a($i) ""
}
Sometimes you want to leave a loop or repeat a loop from somewhere in its interior. This is the purpose of
the break statement. Suppose you know that ary(n) is equal to 7, for some value of n. To determine n,
you could write:
foreach i [array names ary] {
if {$ary($i) == 7} {break}
}
# At this point, $i is the index into
# "ary" for the value "7"
Using continue within a for causes the reinit code to be executed. With both for and while, the condition
must still be true before the body will repeat again.
Tcl Procedures
A Tcl procedure is a command defined solely in Tcl. It may use other Tcl procedures or built-in commands
to do its work. You may (re)define Tcl procedures whenever you wish using the procproc command. Tcl
procedures have three attributes:
22
name This is, of course, the command name which will invoke the procedure.
arg list This is a list of the formal parameters of the proce dure. That is, it’s a list of the names by which
this procedure will refer to its arguments. The number of arguments passed on the command line must
normally match the number of arguments in the list.
body The body is a list of Tcl commands to be executed. It’s just like the bodies of the if, while, etc.,
statements.
*proc name arg list body
Description
The proc command is used to (re)define a procedure. You always give it three argments: the name, the
argument list, and the procedure body. If a procedure (or a built-in) with the specified name already exists,
then the old one is deleted and replaced by the new one.
Procedures can return an explicit value by using the return statement. If the last statement of the procedure
body is not return, then the result of the procedure will be the result of that statement.
23
That means that the following two procedure definitions produce equivalent results. The first one is slightly
slower, but it’s clearer to someone reading the code that it’s intended to return the sum of the two arguments.
proc plus {a b} {return [expr $a + $b]}
proc plus {a b} {expr $a + $b}
Either way, you can use the new plus command as follows:
% proc plus {a b} {expr $a + $b}
% plus 2 3
5
%
Notice that the arguments which are written on the command line (2 and 3) are passed to the procedure
and it “knows” them by the names a and b.
% plus 2
no value given for parameter "b" to "plus"
% plus 2 3 4
called "plus" with too many arguments
We can also write a procedure which takes a variable number of arguments and returns the sum of all of
them, using the keyword args, whose value will be the list of the arguments passed:
% proc sum {args} {
set result 0
foreach i $args {incr result $i}
return $result
}
% sum 1 2 3
6
%
Notice in the previous example that we used a “helper” variable called result, inside pro ce dure sum. Whenever the set or related commands is used to create a variable inside a procedure, that variable is local to that
procedure. That is, its value is independent of any other variables named result in other procedures, and
when the procedure returns, this particular variable result and its value will be destroyed. T his process is
called “going out of scope”—once no code can possibly access that variable, then there’s no need for it any
more.
Thus, by default variables in Tcl are local. It is good coding practice to avoid the use of global variables
whenever possible.
Global Variables
As we’ve already seen, variables can exist outside the scope of any procedure. These variables are called
global, and they can be accessed from within procedures by declaring them with the special keyword global
prior to accessing them.
24
Here’s an example of a procedure accessing a global variable x. It accepts one argument a, adds it to the
global variables x and y, and returns that result.
Local variables, as well as arguments, are created separately each time a procedure is invoked. This allows
writing recursive procedures. Recursive procedures are procedures which may call themselves.
The classic example of a recursive procedure is one to calculate factorials. As you remember, by definition
n! = n × (n − 1) × . . . 1
so we can write a factorial function in Tcl as follows:
% proc fact {x} {
if {$x <= 1} {return 1}
expr $x * [fact [expr $x - 1]]
}
% fact 5
120
%
So far, every example of passing arguments to procedures has been pass by value: that is, the value of the
variable is passed to the procedure, which knows that value by its own name. What if, instead, we want to
write a procedure which can modify a variable which is passed as an argument?
To do this, or to pass an array as an argument, we need a way to pass arguments by reference. The upvar
command allows us to do so. When declaring a procedure which is to receive an argument by reference, use
a dummy name to mark the argument, then use upvar to assign this to a local name.
25
Notice that the variable foo is set to a new value by myproc:
% proc myproc {a} {
upvar $a myvar
set myvar 4
}
% set foo 3
3
% myproc foo
4
% set foo
4
%
Notice in this example that we can pass a whole associative array as an argument:
% proc array_size {a} {
upvar $a myarray
return [array size myarray]
}
% set age(Tim) 37
37
% set age(Siamak) 36
36
% array_size age
2
%
Errors and Exceptions
In older programming languages, all error conditions had to be handled by explicit checking throughout the
code. For instance, when you open a file, you have to check the return value to see if the open was successful,
and if not, take some action.
An exception is something out of the ordinary which occurs as a program is running. For instance, if you
divide by zero, an exception might be raised, and it would usually cause the operating system to abort the
execution of your program. Even DOS detec ts divide by zero exceptions, for example.
Modern thinking on programming languages is that exception and error handling should be concentrated in
a few places within a program rather than sprinkled throughout. This has several benefits: primarily, that
the code which does the “normal” work is much easier to read and understand (and therefore more likely to
work correctly), because it doesn’t have all the exceptional code interspersed.
When a Tcl procedure or built-in wants to raise an exception, it can execute a variation of the return
statement to return not only the normal return string (which would probably be an error message of some
type), but also an exception (or return) code. A code of 0 is the normal, non-error return. Codes 1, 2, and
3 are used for exception, break, and continue events. Other codes can be defined by the application.
26
When a non-zero return code is returned, Tcl will keep returning the code up the call stack until it either
reaches the top level or it reaches a procedure which is prepared to handle an exceptional return.
The Tcl command used to catch exceptional returns is catch. The easiest way to use it is
catch script var
This will execute the command(s) in script. If it and any procedures it calls all return normally, then
processing simply passes to the subsequent command. Otherwise, the catch command returns as its value
the error code, and var will be set to the error message (string) which was returned from the script.
Amanda Portal is designed to minimize the number of places that an application programmer will need to
use catch.
27
Here’s an example of using catch, plus the error which can be used to raise an exception:
proc foo {a} {
if {$a != "bar"} {
error "The argument was invalid"
}
return "Everything ok"
}
proc foo {} {
set code [catch {foo baz} result]
if {$code} {
puts "Error received: $result"
# Pass the error up the stack
return -code $code $result
} else {
puts "Execution ok: $result"
}
}
The info command is used to get various pieces of information from the Tcl interpreter. It has many
different options. The ones which are likely to be of interest are:
args proc Returns the list of arguments for procedure proc.
body proc Returns the body of procedure proc.
commands pattern Lists the built-in and Tcl procedures whose names match pattern. If no pattern is
specified, all names are returned.
exists var Returns true if var is currently a valid variable name. You might do this before trying to access
its value under some circumstances.
procs pattern This is like commands, but it returns only procedures defined in Tcl (those matching pattern
if it’s specified).
tclversion Returns the version number of the interpreter.
vars pattern Like commands, but it returns a list of variables (those matching pattern if it’s specified).
1.2.2Tcl and Multithreading
Tcl originally did not support multithreading in any way. Because Amanda Portal has a need for multiple
threads because so many things are happening at once, tAA added multithread safety to Tcl. Up com ing
versions of Tcl will include Unicode support for multiple languages and multithread safety; when Tcl 8.1 is
released, Amanda Portal will be switched to use that version.
28
The multithreading that was added only allows one thread per interpreter. This is a good thing because the
global variables errorCode and errorInfo are per interpreter and should not be shared between threads.
Interpreters (and the threads which are tightly bound to them) are spawned off whenever a new “task” needs
to be executed. A “task” is spawned for some event such as a call coming in. When a call comes in, for
instance, the core creates a new interpreter and a new thread to handle the call. The entry point in the Tcl
code can vary depending on configuration parameters used by that DLL.
1.2.3Tcl and State
Tcl allows you to store state in a number of places. You can store s tate globally, on the stack (local variables)
and in arrays. (Sort of. Tcl treats the variable as a single value with respect to scoping but you can store
aggregate information in the array.)
Tcl allows you to attach internal state to Tcl functions and interpreters and to install triggers on Tcl
variables. Interestingly, internal Tcl state cannot be attached to Tcl variables. Tcl state is attached to
functions through the ClientData parameter. This parameter gets set when you create the function. Internal
Tcl state is attached to an interpreter with the Tcl SetAssocData() and Tcl GetAssocData() functions.
Triggers (called “traces” in Tcl) can be attached to variables that fire off whenever the variable is read,
written or deleted.
1.2.4Assigning Scope to Procedures
Procedures in Tcl are not scoped. This is unfortunate because we want to make VP and LS device handles
and MMO handles into functions and be able to call the functions in an object-oriented fashion like
$vp play $mmo
which would play the sound asso ciated with the MMO handle on the specified VP device. Since procedures
are not scoped, the handles would not disappear if the stack returned. That is,
proc play_greeting {} {
grab_res vp vp_proc;# Get a single VP device. Return new proc name.
# Get greeting 1 from box 10. Return new proc.
lookup_mmo -box 10 "Grt 1 English" mmo_proc
$vp_proc play $mmo_proc ;# MMO specific data in ClientData of $mmo_proc.
}
would not delete the procedures named by the values of the variables vp proc and mmo proc when the
procedure is done because procedures are not scoped.
29
We can get around this problem by noticing two things: variables are scoped and behavior can be attached
to variables (through triggers) when variables are deleted. Therefore, to solve this problem, we instead
create both a variable and a procedure when we create an LS, VP or MMO handle. The variable has a
deletion trigger attached to it. When the variable leaves scope, the deletion trigger automatically deletes
the procedure, giving the procedure scope semantics.2The variable created is special: the Tcl variable has
a deletion callback attached to it internally. Most Tcl variables don’t. Also, the procedure handle created
has internal state associated with it (through ClientData). This state stores such things as the internal C
information associated with the VP device (for VPs) or the internal C information stored with the MMO
(for MMOs). Most regular Tcl commands don’t have internal state associated with them.
Let’s explore this idea with an example. Suppose you want to play an MMO on a VP device. First you grab
a VP device with the function grab res:
grab_res vp vp_var# Grab a single VP resource and put in vp_var
Grab res does the following when it is called:
1. It looks for a VP device and allocates it to the caller. If no valid VP device is found or available, an
error is returned.
2. It creates a new command, say “ vp0” and sets up that command to call an internal C function which
implements the behavior of VP devices. It sets the ClientData parameter for this procedure to the
internal state allocated for the new VP device.
3. It sets the variable vp var to the name of this new command and it sets a deletion callback on this
variable that will delete the command vp0 when the variable is delete d.
So if you now say
set vp_var# Returns variable’s value
you will get back the string vp0. What you won’t see is that the variable vp var has a deletion callback
assigned to it.
2
This is also useful in C++. C++ has destructors that are fired off whenever a variable leaves scope, even when an exception is
raised. These destructors can execute code you want executed every time a procedure on the stack returns. Suppose for example
you are calling a procedure that needs to execu te in a critical sectio n. You also have two functions: EnterCriticalSection and
LeaveCriticalSection that are put at the beginning and end of the procedure, respectively. The function looks like
int foo()
{
EnterCriticalSection();
...do some processing...
LeaveCriticalSection();
return 0;
}
It is imperative that you call LeaveCriticalSection or your co d e will have a serious bug. To be absolutely sure that your
code calls LeaveCriticalSection, you can have the system call it for you by putting the call to it in a deletion trigger of a
dummy variable in the scope of foo. When foo finishes, the deletion trigger is called and LeaveCriticalSect ion is executed.
For example,
int foo()
{
CriticalSectionObj dummy; // Calls EnterCriticalSection on creation.
// Deletion trigger for this vari able calls
// LeaveCriticalSection.
...do processing here...
}
30
1. set r $v1# Regular variable gets MMO handle.
2. set v1 $r# MMO handle set to regular variable.
3. set v1 $v2# MMO handle = MMO handle.
Figure 1.1: Different assignment scenarios.
Now suppose we want to play an MMO handle on this VP device. I’ll assume we have an MMO handle
stored in the variable mmo var which references the command mmo0 (MMO handles work just VP handles).
To play the MMO, we say
$vp play $mmo
The Tcl parser expands this to
_vp0 play _mmo0
and executes the command vp0 with the arguments play mmo0. The vp0 command then does the following:
1. It sees that it is a play command and looks for MMO functions as arguments to the play command.
2. It looks up the command named mmo0 and extracts the ClientData. It now has the internal C
information it needs to play this MMO. It also has the internal C information about the VP device
because this was passed in the ClientData parameter when vp0 was called.
Variable handles created this way are immutable. That is, they can be created and destroyed but not
assigned to. Let’s examine why this is so. Suppose r is a regular variable and v1 and v2 are MMO handle
variables. Figure 1.1 shows the three assignment scenarios that we need to be concerned with.
The first scenario cannot be prevented because Tcl doesn’t provide triggers on arbitrary non-existent variables. That is, you can set a write trigger on the variable r, but how do you know a priori which new
variables will be created? The semantics of operation 1 are to set r to the name of the procedure associated
with the variable v1. No deletion trigger is copied to r so setting r in this manner will create a “handle”
which doesn’t obey the scope rules. The handle function may fail if v1 leaves scope.
The second statement trashes an MMO variable. If this were allowed, the deletion trigger on v1 would have
to be executed on a write to delete the associated procedure and then v1 would revert to a regular variable.
This is not rocket science and could easily be accommodated.
31
The last assignment is the problem. In this scenario, an MMO handle is assigned to another MMO handle.
What do we do here? Do we execute the trigger on v1 to delete the proce dure associated with it and then set
up v1’s deletion trigger to be the same as v2’s? This won’t work. Deleting v1 will delete the procedure when
it is still in use by v2. We would need reference counting in the procedure deletion logic. We could also make
the operation turn v1 into a regular variable by running its deletion callback and setting the variable to the
name of v2’s procedure. This isn’t very useful though and doesn’t fit the clean definition of assignment. For
this reason, handles are immutable. (By the way, read-only access on the variable is simply implemented by
setting a “write” trigger which doesn’t allow assignment.)
1.3Control Flow
The Tcl code that defines the behavior of Amanda is called the Telephone User Interface or TUI. Tcl gives
the TUI great flexibility and extensibility and is Amanda Portal’s trump card. There are basically two ways
we could have structured the control flow in Amanda when doing a “task”:
1. Use a state machine. As each menu is presented, the user selects an option. When the option is
selected, the next state is traversed and the next menu is presented. The advantage of this approach is
that it uses a small stack and traversing from one unrelated state to another is easy. The problem with
the approach is that it is hard to understand the state table and to update it. This is the approach
used in many other voice mail systems and in Amanda@Work.Group.
2. Stack based procedure calling. This approach more naturally follows the flow and structure of the
menus being presented. Its main problem is that it is hard to get from one place in the tree to a
completely different place in the tree without using exceptions (you have to pop the stack and reload
it with different procedures corresponding to the new place in the menu hierarchy). Fortunately, we
don’t do this very much.
Figure 1.2 shows the general structure of the menu system and the Tcl stack at each point in time.
Errors in most Amanda primitive functions are handled differently than is normally done in Tcl. Normally,
when an error occurs in Tcl, an exception is raised, the message is returned in the “result” and errorCode
is set to the error code of the error. To detect errors, you have to use catch and check the errorCode to see
what the error was.
This is troublesome in Amanda because the people writing the Tcl code may not be programmers and
won’t understand exceptions. Secondly, it makes the code in the functions above unclean because you have
to handle two different cases, those where the function succeeded (catch returned 0) and those where it
failed (catch returned 1) quite differently. It would be better to simply return sentinel values for error
codes that don’t match any valid successful return value, then you could just use a big switch statement.
Lastly, exceptions pop the stack. Most of the time we don’t want to pop the stack. Error code returns are
appropriate in this case.
The rule is this: if you think the user of this function is going to want to stay in the same procedure and take
some action on an error, return a sentinel value. If you think this error is serious and we need to abort out
of the menu system, raise an exception. Some errors such as hangup are exceptions, so they are expressed
that way at the Tcl level. We want to unwind the stack and abort processing on hangup. Exceptions may
also be raised manually in the programmer’s Tcl code if he needs to unwind and go to another part of the
menu tree (not ancestrally related).
32
Play Message
“You have 1 new
message. . . ”
Login Menu
“Please type your
box number and. . . ”
Main Menu
“To listen to your
messages,press 1,
. . . ”
login menu
main menu
play msg
?
H
login menu
?
H
H
H
H
H
Hj
Set Options
“To set your
options, press. . . ”
login menu
main menu
?
login menu
main menu
set options
?
Figure 1.2: Flow control in Amanda Portal
33
?
To further make the TUI code clean, each VP device has a DTMF buffer that stores the digits that the
user has typed in. Certain kernel calls such as get digits will inspect this buffer before actually listening
for digits and return any digits that the user has previously pressed. This separates the synchronization
required between the time a user types a digit and the time the Tcl code listens for a digit. The get digits
call will return the digit the user typed, regardless of whether the user typed the digits before or after the
get digits Tcl call.
1.4Waiting for Events
In Amanda Portal, it is often useful to start off one or more threads asynchronously and then do other things
while waiting for the threads to finish. If you start off more than one thread, you may wish to wait and be
notified when a thread either finishes or needs to notify you about an event that occurred. You also need
a way to identify the different threads. This is done through “handles.” Handles are variables that point
to the name of an internal function that implements the behavior of the object the handle refers to (see
section 1.2.4). These handles serve a dual purpose: they have member functions that you can execute to
query and modify the objects the handles refer to and they can be used as synchronization mechanisms for
threads associated with the handle. If a different thread is associated with a handle, the thread is either
started when the handle is created or by a member function on the handle. When you delete a handle, your
“ownership” of that object ceases and the objec t referred to by the handle is either freed or released for
other users to use. Any threads associated with that handle are killed too.
Currently the only two type of handles that can start off asynchronous threads are VP device handles and
call queueing handles. VP device handles wait until an event like playing a message finishing or the user
hanging up. The queue handles wait for events like a call changing its position in the queue or a call reaching
the top of the queue. Each type of handle that allows you to fork off asynchronous threads must have a stop
member function that “stops” the operation in progress, whatever that means. In the case of VP device,
it will stop the play in progress; in the case of call queueing handles, it is a no-op. Stopping is necessary
because you often want to stop all the other threads from running when an event occurs. The basic wait
logic looks like
Wait on various handles
After an event, foreach handle that event didn’t occur on {
Stop the handle by issuing a stop command.
}
Fortunately, it is easier to stop the various other handles than by running this loop each time. The wait
command by default stops the other handles when it returns.
This is the main synchronization function for threads. Threads are referenced by handles and the functions
associated with handles are passed into this command. Normally, once an event occurs and wait returns,
all the handles are stopped (that is, “handle func stop” is executed automatically on all the other handles).
You can specify -nostop if you don’t want wait to stop any of the other handles. You can then stop them
individually if you wish. The -timeout option is used to return after a specified time. The -all option
waits for all the handle funcs to return.
Return values:
TIMEOUTA timeout occurred. Only returned if -timeout is given.
A list of 2-tuples.The first part of each 2-tuple is the function handle name and the second
part is the return result from the thread. Normally the second part will
also be its own list of the form
reason args. . .
Only one 2-tuple is returned unless you give the -all option. In this
case, more than one 2-tuple can be returned.
Amanda Portal comes with a small core which defines fundamental behavior such as the security model, the
model for boxes and messages, etc. Additionally functionality is loaded as DLLs. DLLs have a standard
interface that they must support when they are loaded into the Amanda Portal system. Loading external
DLLs leaves the system open to extendibility and modularizes components.
The list of DLLs that are loaded when Amanda Portal starts up is listed in the Configuration Database (the
dlls setting). When a DLL starts up, the DLLMain procedure initializes any information that the DLL may
need. For example, it may interrogate the Dialogic boards in the system to see how many resources they
have and then register these resources with the resource manager.
Some DLLs perform all their functionality in C (e.g., the SMTP DLL) while (most) others spawn a Tcl
interpreter and a new thread of control when an event happens (such as a call comes in). Privileges are
associated with an interpreter (through Tcl SetAssocData), so DLLs that don’t spawn a Tcl interpreter
cannot log in to a box and cannot have privileges associated with them.
Certain function names in a DLL are recognized as being special and are called at certain times by the
Amanda Portal system. For example, when you log in to a box, a special function is called in the DLL
(interp login). This function can inject new privileged commands into the interpreter that only logged in
users can use (the injection model). Also, on logout, another function is called that removes the privileged
commands that were added on login (interp logout).
The idea is to provide a set of commands for unauthenticated users that is a subset of the commands for
authenticated users. The Internet DLL provides good example of this. You can telnet into the Amanda
System and get a T cl prompt. You have a limited set of commands you can use until you log in. When you
log in, you can do more privileged things like grabbing resources (such as a voice or fax device).
35
You can view this set of DLLs as a forest of different C code threads listening for events and spawning
interpreters when events happen. The TUI is loaded and started by the core. Each incoming call spawns
a separate Tcl interpreter so multiple instances of the TUI will be running at once. You may call different
entry points in the Tcl code if you want different behavior in the TUI. For example, you may have the rule
that if calls come in on lines 1–3, we call the Tcl function amanda voicemail and if the call comes in on
lines 4–5 we call the Tcl function amanda forward.
1.6Resource Manager
The resource manager is a central registry where DLLs register the resources they provide and how many of
them they provide. The resources are typed. There are port resources (LS devices), voice processing devices
(VP devices), Fax devices, etc. When an interpreter is created and a new thread is started by a DLL, the
thread may have to acquire some resources to perform its “task.” To acquire these resources, the task checks
the resources out of the resource manager and checks them back in when it is done. If the interpreter dies
for some reason, the resources are automatically checked back in because the Tcl variable the resource is
associated with gets deleted and its deletion callback fires.
When acquiring resources, it is important to be able to acquire all the resources you need atomically to avoid
deadlock. For example, suppose there are only two LS resources and there are two tasks that need both LS
resources. If task 1 acquires the first LS resource and then task 2 acquires the second LS re source, they both
will not be able to continue until one of them releases their resource. To avoid this, Amanda allows you to
allocate all the resources you need atomically.
Most resources are indistinguishable. One fax resource is interchangable with another. Telephone ports, on
the other hand, are not. Furthermore, an administrator may wish to designate only certain ports to be used
for certain operations, such as notifications. For this reason, port groups exist. When you request a port
from the resource m anager, you may specify that it must be a member of a given group, such as the notify
group. These group names are not known to the system; they are created on an as-needed basis simply by
assigning the ports to the groups in the configuration database.
A common scenario for a task is to acquire a single LS resource and single VP resource. For example, a call
may come in and the LS resource is allocated for the port the call came in on and a VP resource is allocated
to listen to the caller or the buttons the caller presses on his telephone. The caller can then possibly leave
voice mail. If the call needs to be forwarded, another LS device could be allocated to send the call out on
another phone line.
The resource functions are defined as follows:
*list resource types
Description
Lists the resource types.
Return values:
A Tcl list of the different kinds of resource types.
36
*get resource stats resource type
Description
This function returns information on resource allocation. The array returned contains the following indices:
total definedTotal resources of this type that exist.
current availableTotal resources of this type that are currently available.
max ever usedMaximum resources of this type that have ever been used at once. (This
is not the maximum number of resources that have ever been used at
different times. That is, suppose you have three resources, LS1, LS2 and
LS3. All three LS resources have been used at one point in time, but
only one resource has been in use at once.) This value is useful to see
how close you are getting to saturation of a certain resource type.
times deniedThis value is also used to monitor saturation. The return value is another
a-list. One index into the a-list is all, which indicates how many times
a resource allocation of the resource type failed. When allocating porttype devices, you can specify an individual port or group you wish to
allocate from. Denials for these ports or groups are tallied s eparately.
The indices are the port numbers or group names. For non port-type
devices, only the all index is set. The number of units asked for does
not effect the denial number; that is, even if the process asks for 5 devices
and is denied, the denial count gets incremented by 1.
current waitThis value returns the number of resource units being waited on. Again,
the return value is an a-list as with times denied. However, the number
of units asked for does effect the count returned. For example, if there is
one person waiting on a type and he is waiting for 3 units of that type,
3 is returned for that type.
max ever waitThis is the maximum number of units that have ever been waited for
at once. That is, it is the maximum value current wait has ever been.
Again, the value is an a-list like times denied and current wait.
Return values:
A list of the statistics for the resource type given. The list is returned as an a-list as described above.
Error codes:
INVALIDRESTYPE type
resources with group group name
Description
This function returns a (possibly empty) list of all resource types which have one or more units in group
group name. This function can be used when you are writing code which you know wants to use, say, a notify
port, and you don’t want to care whether the system is installed with T1 or LoopStart lines. The output of
this command can then be used in arguments to the grab and grab res functions.
37
Return values:
A Tcl list of all resource types which have one or more units in group name.
grab res [-timeout tmo] [-unit unit ] [-port port] [-group group] [-specific type specific type] [-min
min] [-max max] [-ascending] [-descending] type var
Description
This function attempts to allocate a resource of type type. If successful, the selected unit’s control function
will be installed in the interpreter and assigned to var. The value of type can be any of the values returned
by the list resource types or the resources with group function.
If -group is specified, then the returned unit must be in the named group (this is used only to select ports
by logical group names rather than by number). Otherwise, you may limit the unit numbers by specifying
-min and -max, or as a shorthand, -unit (which just sets min = max = unit). Using -port is another
variation of -group; it will only return resources with that same port number associated with them, which
will normally identify a specific port-type resource within the system.
If -specific type is specified, then the returned resource must be of that specific type. This option is useful
when requesting a port resource, if you need to specify exactly what type of port, loop start (ls) or T-1 (t1),
you require. If -specific type is not specified, then when requesting a port, any type of port which meets
the other criteria will be returned.
Within the group/range specified, if more than one item is available for immediate selection, then the one with
the lowest unit number will be returned if -ascending is specified, the highest one available if -decending
is specified, and otherwise the least recently used one. This gives you great flexibility in how ports are used
to place outbound calls. For indistinguishable objects such as fax resources, use the LRU method since it
takes the least amount of time to perform the allocation because it doesn’t have to search all the available
units to see which one has the highest or lowest unit number (the list of units is maintained in LRU fashion,
so the LRU case simply takes the first available off the list).
If -timeout is not specified, then grab res will wait infinitely until a qualified unit (one in the group or
range specified) is available. You may specify any non-negative integer for tmo, including 0. A value of 0
effects a poll—if a qualified unit is available, it’s returned, or else no unit is selected.
When allocating items of more than one type, take care to allocate them always in the same order. Otherwise,
deadlocks can occur. Also, beware of requesting a group, specific type, port, and/or unit number which does
not exist in the system. Doing so will block forever, unless -timeout was also specified.
Return values:
If grab res fails to allocate a resource as requested, it returns -1. Otherwise, it returns the number of the
unit selected.
Error codes:
INVALIDRESTYPE type
NOTNONNEG tmo
grab [-group group] [-unit unit ] [-port port] [-type type] [-specific type specific type] [-net net ] [-vp
vp] [-ascending]
38
Description
The grab command is a convenience function which allocates a network device from of type type. The default
is to allocate from type port in descending order.
The device handle (Tcl variable) will be called net, which defaults to net. A DSP resource will then be
attached to net, called vp (defaulting to vp). Resources will be allocated as in grab res -decending order
unless the -ascending option is used. The -group argument is the same as for grab res; you may use it
or -unit, but not both simultaneously. Similarly, -specific type and -port are the same as for grab res
and have the same restrictions on its use as with that function.
If net and vp are defaulted, then the function also performs a make local net vp command to make those
handles’ member functions be “locally” available in the interpreter.
Return values:
A return of -1 indicates that the network device type type was unavailable, while -2 indicates that the
subsequent dsp attach failed. Otherwise, an empty string is returned to indicate success.
39
Chapter 2
Security Model
Amanda Portal has a two-dimensional security model. One dimension is “who” or the identity of the user
and the second dimension is “what” or what they can do. (Other systems often have a third dimension
“where”). The “who” dimension is the box number. When you log in, you are assigned that number. The
“what” dimension is your privileges.
2.1Ancestors and Descendents
Boxes are related to each other ancestrally. When a box creates another box, it is the ancestor of the box
and the created box is a descendent. Box creation occurs in box creation trees. The structure of these trees
is maintained internally by the system and the tree structure cannot be violated. That is, if you want to
delete a box, it must be a leaf node; you cannot delete internal nodes of the tree until all their descendents
are deleted.
Boxes start off with a set of privileges based on those of the box they are cloned from, intersected with the
set of privileges that the creating box has. You can later assign any privileges you have to a box, but you
cannot assign any privileges you don’t have to another box.
The topmost box of each tree is special. They are known as “superboxes” and they automatically get all
privileges. Currently there is only one tree hierarchy with box 999 as the root, but in the future there may
be a forest of these trees. Each topmost box in the forest will have all privileges but will only be able to
control the boxes in the tree under it.
2.2Privileges
The “what” dimension of the Amanda authentication system is handled by privileges. Each box is assigned
a set of privileges that that box has. A user of a box can grant those privileges to descendent boxes, but
only the privileges that the box owns. To acquire a new privilege, a proper ancestor must give it to you.
The privileges are mutually exclusive and not hierarchical; that is, owning a certain privilege does not imply
the ownership of any other privilege.
40
The top-most box can assign any privilege to any other box. Thus, the top-most box can extend the
privileges in the system by making up new ones. Child boxes can then assign those privileges to other boxes,
ad infinitum. However, the system only has built in knowledge of the privileges it is shipped with. The
interpretation of any user-defined privileges is up to the Tcl code the end-user writes.
There is an interesting anomaly with privileges: you can’t revoke your own privileges. You can only modify
the privileges of your proper descendents and since you are not a proper descendent, you cannot create or
revoke your own privileges. Your parent must do it.
Privileges are tested for using the has privilege command. When you are not logged in, you have no
privileges. When you log in to a box, you get the privileges associated with that box. Certain commands
are associated with privileges and you will not get these commands “injected” into your interpreter unless
you have the privilege. For example, if you don’t have the EDIT PMETHOD privilege, you won’t get the
store proc command.
Because privilege state is internal, there is no way to give yourself more privileges than you already have.
You have to modify privileges through the calls built into Amanda and these calls only let you manipulate
privileges in predefined ways.
Privileges are actually stored as internal state attached to an interpreter with Tcl SetAssocData. Therefore,
because Tcl SetAssocData is used, privileges can actually have values. This currently isn’t used but may
be in the future. Today, we only test for privilege existence.
The different privileges are
ANNOUNCEMENTSThis privilege allows you to create and modify announcements in your box.
BOX CREATIONThis privilege allows you to create non-guest boxes.
CALL SCREENAllows you to set the call screening (CALL SCREEN) and modified call screening
(MOD CALL SCREEN) setting on a box.
CHANGE METHODThis privilege allows you to change the CALLER CODE, USER CODE, DONE CODE,
RNA CODE, BUSY CODE or MENU* CODE methods for a b ox.
CHANGE TMAPPINGThis privilege allows you to change entries in the a trie mapping database (see
page 129).
CONFIGURATIONYou can change the Configuration Database settings.
COPY TOThis privilege allows you to change the list of boxes another box’s messages are
copied to. You change this list using the “list mapping” functions in section 10.1.
CREATE QUEUEThis privilege allows you to create a queue for your box or any descendent box
using the create queue call. It also allows you to set queue parameters with
set queue setting.
CREATE TMAPPINGThis privilege allows you to create and destroy trie mapping database.
CTRIGGERThis privilege allows you to change and add notify records (“conditional triggers”)
to boxes.
CTRIGGER CODEThis privilege allows you to create and modify the bodies of notify templates
(“conditional triggers”).
DNDAllows you to set the DND setting on a box.
EDIT PMETHODThis privilege allows you to create or change one of the persistent methods.
EXTENSIONAllows you to set the extension (EXTENSION) or location (LOCATION) on a box.
GENERAL ANNOUNCEThis privilege allows you to create and modify announcements in the announc-
ment mailbox.
GREETINGThis privilege allows you to change your greeting, to set a maximum length on a
greeting (GREET LENGTH), or to set the delay after saying a greeting (DELAY).
41
KILLThis privilege allows you to kill all the threads of a specific user or an individual
thread running on behalf of a user.
MESSAGE MOVEThis privilege allows you to move messages from one box to another
(move msgs to box).
MONITORThis privilege allows you to get status updates of tracing, ports, resources, and
system.
NOTIFY ANY BOXThis privilege allows you to schedule a notify within another mailbox.
RESET PORTThis privilege allows you to reset a port on a board.
RECORD MSGSThis privilege allows you to change whether a box receives messages
(RECORD MSGS) or not and to change the maximum length of the received messages
(MAX MSG LEN).
RESET STATSThis privilege allows you to reset the statistics gathering counters for a box (the
reset box stats call).
SCHEDULE LOCKYou can create/modify autoschedule records (“time triggers”)—see page 138).
SHUTDOWNThis privilege allows you to use the shutdown command, described on page 197.
The privilege commands are as follows
has privilege [-box box] [-use db] privilege
Description
This function is a predicate to determine whether your box has a certain privilege. If -box is given, test the
given box instead. You can only test the privileges of your box or descendents of your box. If you are testing
the privileges of the box you are logged in under, then the test is done against the state associated with the
interpreter (i.e., Tcl SetAssocData). If you test another box’s privileges, then the test occurs against the
privilege database. You can test against the database even when testing your own box by using the -use db
flag.
Return values:
0 or 1
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
set privilege value box privilege [value]
Description
This function sets a privilege on a box to the value given or the empty string if no value is given. You may
not set the value for your own privilege; you must give a descendent box. Also, you can only set privileges
that you yourself own. This function only sets the state in the database. Any existing interpreters logged
into the box set will not see the changes. The values currently are mostly unused. Only the privilege’s
existence is checked for. Therefore, the default empty string is a reasonable default.
As mentioned earlier, the superbox is allowed to set any privilege for any box, including itself.
42
Return values:
Value privilege was set to.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
INVALIDVALUE value
get privilege value [-box box ] [-use db] [privilege ]. . .
Description
This function returns the value of the privileges for the given box. You can only inspect your privileges
or descendent box privileges. If you inspect the privileges of your own box, the state associated with the
interpreter is used. If you inspect the privileges of another box, the privileges are looked up in the privilege
database. You can retrieve against the database with your own box by using the -use db flag. The only
time the values would be different is if another thread modified you privileges from under you.
Return values:
An a-list with the privilege as the index.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
PRIVNOTEXIST privilege
INVALIDVALUE value
delete privilege box [privilege]. . .
Description
This function deletes the privileges associated with the box given. You may not delete your own privileges.
You may only delete the privileges of proper descendents. The root box has all privileges implicitly and
cannot revoke any of its privileges.
This function lists all the privileges for the current box or the box given. If you retrieve the privileges of
your own box, the s tate associated with the interpreter is used. If you retrieve the privileges of another box,
the privileges are looked up in the privilege database. You can retrieve privileges from the database even
when specifying your own box by using the -use db flag.
Return values:
A Tcl list.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
INVALIDVALUE value
2.3Login and Logout
When you log into a box, you get the box’s privileges and new commands may be injected into your interpreter
that give you additional functionality. In addition, your password and your box number are set in the global
array taaPriv. When you log out, your password and box id are removed from the global variable taaPriv.
The login related commands are defined as follows:
*login box password
Description
This function logs you into a box. Many new commands are added once you log in. If the Configuration
Database parameter max login attempts is defined, then after that many failed login attempts in a row,
your interpreter will be killed. This command only exists when you are not logged in.
Return values:
Empty string.
Error codes:
LOGINFAILED
44
logout
Description
Logout of a box. Messages may be expunged if EXPUNGE LOGOUT is set for the box. This command exists
only if you are logged in.
Return values:
Empty string.
*verify sac box password
Description
This function verifies the password is the correct password for box.
Return values:
0 or 1
Error codes:
KILLED
45
Chapter 3
How Amanda Portal Interacts with
Telephone Switches
Commonly, Amanda Portal is used “behind” a telephone switch, acting as a voice mail system and/or an
autoattendant. Figure 3.1 shows Amanda hooked up to a phone switch. A number of outside lines come
into the phone switch. Amanda is hooked up to a number of phone switch lines and may be handling more
than one call at a time on these different lines. Phones are also connected to the switch. In figure 3.1 these
are referenced by the users “Tim” and “Scott.” Each connection on the switch is known as an “extension.”
Each connection on the back of the Amanda system is known as a “port.” Notice that the numbers need
not be the same. There is a mapping in the Configuration Database that tells Amanda which extension
is hooked up to which port. There is also a mapping in Amanda that tells which extension on the phone
switch is associated with which voice mail box in Amanda (in addition to other info such as the message
light status, phone set name, etc). Lastly, there may be a serial line connected from the phone switch to
Amanda to give Amanda information on calls being transferred to it if the switch uses “serial integration”
(explained later).
Switches come with a variety of “smarts” and each phone switch has its own peculiar way of doing things
(i.e., there is no standard). Smart switches generally cost more than dumb switches but both types of
hardware are extremely reliable.
Every phone switch can be told to transfer calls from one extension to another and every phone switch has
an audio jack for “hold” music where you can ho ok up a CD player or radio to play music to the person
when the person is put on hold. The switch automatically plays the music from this jack w henever a call is
put on hold. If you don’t hook anything up, the caller will hear silence or periodic beeps.
Integration information is information from the phone switch to Amanda that tells Amanda why the call was
transferred to Amanda, which extension the call is or was destined to go to, and possibly which extension the
call is coming from. Integration comes in two forms: “in-band integration” and “out-of-band integration.”
With in-band integration, Amanda is told information about a call by a sequence of DTMF tones on the
line the call came into Amanda on. Out-of-band integration, or “serial integration” (since the data comes
over a serial line), gives Amanda the call information through a separate serial line. This information
says something like “The call that just came in on extension 6 was destined for extension 8 and was from
extension 3.” Amanda has to ass ociate this information with the call on extension 6. Many switches send
the out-of-band integration information over the serial line according to a specification called SMDI from
Bellcore.
46
outside lines
phone
switch
ext. 3ext. 8
ext.
6
ext.
2
hunt
group
lines
port 5
Amanda
port 3
6
TimScott
serial integration
Figure 3.1: How Amanda and the Phone Switch are Connected.
With in-band integration, switches have settings in them that tell the switch which lines to send DTMF
integration information on and which lines not to. For example, in Figure 3.1, the lines going to Amanda
have “integration information on” and the other lines have “integration information off.” If the non-Amanda
lines didn’t have “integration information off,” then when you picked up the phone to answer a call you would
hear the integration information from the switch.
Some switches don’t send any integration information. With these types of switches, Amanda can’t be as
intelligent. When a call comes into Amanda, it can’t differentiate whether a call was destined for a user’s
phone and the user wasn’t there or whether a person just dialed in from the outside and should receive
the company greeting. In the first case, Amanda should give the voice mail greeting for the box the call
was destined for. In the second case, Amanda should begin call processing by playing a general greeting.
Without integration information, Amanda must always give the general greeting.
If the switch provides integration information to the voice mail system, then the phone switch allows an
autoattendant to do “blind-transfers.” With blind-transfers, Amanda can simply transfer a call to another
extension and drop the line. Once the line is dropped, Amanda can use it for other calls, such as a new
incoming call from the outside. If the person does not answer the phone or the extension is busy, the switch
automatically routes the call back to Amanda’s hunt group1and integration information tells Amanda that
the call was destined for the extension but didn’t answer or was busy.
1
The switch is set to call or not call Amanda back on a per extension basis according to settings in the Configuration
Database. The switch is configured according to these settings when Amanda starts up.
47
When blind transfers are not possible, Amanda must to a “supervised transfer.” With a supervised transfer,
Amanda must keep the line open and listen if the person picks up the line or is busy. The port cannot be
used for other purposes such as incoming calls during this time, so this form of transfer is less desirable.
There are two types of supervised transfer:
1. The switch tells whether the person answered or the phone is busy through in-band DTMF. In this
scenario, the switch gives different DTMF tones whether the person answered or whether the extension
is busy. Amanda listens for these tones.
2. Without such integration information, Amanda must do “Programmed Call Progress Monitoring” or
PCPM. With PCPM, Amanda actually listens on the phone line for the ringing, busy signal, or the
human voice to determine whether someone picked up the phone or not. With these types of switches,
the caller may miss the first part of the sp eech of the callee. The reason is that there is a delay in
recognizing the human voice and transferring the call to the callee’s extension. While the callee is
talking, Amanda is busy transferring the caller to that extension and the caller misses the first part of
the callee’s speech.
The lines running to Amanda from the phone switch are not special in any way. Amanda has the ability to
transfer a call to another extension only because the call was sent to Amanda. Amanda has no idea what
other calls are currently going on in the phone switch, the state of the extensions, etc.
Of course, Amanda need not be used as an autoattendant. The system can be configured to take voice mail
for each mailbox without trying to transfer the caller to the user’s extension first. Also, since the voice
processing boards in Amanda are capable of performing switching themselves over an internal TDM bus,
the Amanda system can act as a PBX itself by installing the proper boards and writing appropriate driver
and TUI code.
48
Chapter 4
Mailboxes
Each user in Amanda has one or more boxes (usually one). This b ox stores a variety of messages and each
message can have a number of MMOs in it. The messages in boxes are arranged in folders (see page 70).
Usually the messages in a box consist of a single voice MMO (i.e., a single voice mail message from someone).
You must login to a box to listen to the messages in it. Each box has a password so other people can’t listen
to your messages if they don’t have the password.
There are two types of boxes: guest boxes and non-guest boxes. When you create a guest box, the system
picks the box number for you. You may not select the box number to create. This is useful for something
like a law office where each lawyer may create boxes for his clients so that he can leave them confidential
messages for later pickup. Each client would have a different password on their box. Which box number is
selected is not important. When creating non-guest boxes, the creator gets to select the box number. There
are separate privileges for creating guest and non-guest boxes. To create non-guest boxes, you need the
BOX CREATION privilege. To create guest boxes, you simply need a positive BOXES LEFT setting on your
box. Every time you create a new non-guest or guest box, this number is decremented. So even if you have
the BOX CREATION privilege, you still must have a positive BOXES LEFT setting to create a non-guest box.
When you create a box, you clone the box from another box. Cloning copies the notify and autoschedule
records, the copy to lmapping mailing list, and the basic settings on the box. When you create a box, you can
specify which box to clone from. If you do not specify a box to clone from, a default box is used if it exists.
There are two default boxes in the Configuration Database: one for guest boxes (guest defaults) and one
for non-guest boxes (defaults box). These entries list the box number to clone from for the appropriate
box type. Having two different default “box clone” entries is important because guest boxes may not even
have a phone associated with them and non-guest boxes usually always will.
49
When a box is created, the password assigned depends on a number of factors. If there is no default box for
the box type or if its password is blank, the password assigned is the box number; otherwise, the password
is set to the same password as the default box. The intention here is to set the box password to something
known if there is no password, so the user gets a reasonable default. However, we don’t want people from
the outside breaking into the system because they know that the box password is set by default to the
box number on creation. To get around this, we allow the Amanda system administrator to set the default
password in the clone box to something he knows. This prevents outside users from breaking into the system.
Note that this behavior is the current behavior in Amanda@Work.Group.
4.1Box Manipulation Functions
*is box box
Description
This function is a predicate to determine whether a box exists.
Return values:
0 or 1
create box [-clone box] [-box box]
Description
This function creates a new box. If -box is given, then a non-guest box is created with the appropriate box
number; otherwise, a guest box is created. You will need the BOX CREATION privilege to create boxes
using the box number. You will need a p ositive BOXES LEFT box setting to create guest boxes. Every time
you create a guest box, this number is automatically decremented by the system. Also, every time you give
a child the ability to create guest boxes, your BOXES LEFT count is decremented. That is, you cannot create
more guest boxes than you are allocated by giving more boxes to your child boxes. When you give boxes to
your children, you do it by adjusting the child’s MAX BOXES setting. This value is intimately tied with the
BOXES LEFT setting but you cannot set the BOXES LEFT setting. It is maintained by the system.
If -clone is given, then the box is cloned from the indicated box number. If this flag isn’t given, the
defaults box and guest defaults boxes from the configuration database are used for cloning if they exist.
Autoschedule records, notify records, copy to lmapping records, privileges, and box settings are cloned.
Delete boxes. The boxes must be leaf nodes. If a box deleted is a guest box, then the immediate parent of
the box has its BOXES LEFT count incremented.
Changes the parent of a box. The box must not be currently logged in. The box and its new parent must
be descendents of the logged in box making the call. The box must not currently be an ancestor of its new
parent. The new parent must have enough guest allowances. This command will fail if the box has children
and move children is not specified.
This command returns the next box (numerically) in the system after box. If -1 is given for the box, return
the first box.
Return values:
Box number or empty string if box given is last box.
Error codes:
errorCodeDescription
NOTINTEGER box
OUTOFRANGE boxUser gave negative number besides -1.
*previous box box
Description
This command returns the previous box allocated in the system before box. If -1 is given for the box, return
the last box.
Return values:
Box number or empty string if box given is first box.
Error codes:
errorCodeDescription
NOTINTEGER box
OUTOFRANGE boxUser gave negative number besides -1.
*get box children [-box box ]
Description
Returns the child boxes of the current box or the box given. If you are not logged in, you must give the
-box option.
Return values:
A Tcl list containing the child boxes.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
BREQNLOGIN
INVALIDVALUE value
52
*get root [-box box]
Description
Returns the top most box in the box tree containing the current box or the box given. There may be a forest
of box trees. If you are not logged in, you must give the -box option.
Return values:
The top most box number.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
BREQNLOGIN
set box password [-box box ] password
Description
Sets the password for the current box or the box given. You can only set the password for your box or one
of your descendent boxes. There is no way to inspect the password of a box. You must reset the password
if you forget it.
Return values:
Empty string.
Error codes:
errorCodeDescription
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
INVALIDPASSWORD passwordPassword can only be numeric.
set box encrypted password [-box box ] encrypted password
Description
Sets the password for the current box or the box given. You can only set the password for your box or one
of your descendent boxes. There is no way to inspect the password of a box. You must reset the password
if you forget it.
This function is exactly the same as set box password except that it takes an already-encrypted string
as the password for the box, whereas set box password takes the plain-text version and encrypts it before
storing it in the database.
53
BoxKeyMutabilityDeleteabilityValue
1setting nameread-only
read-write
ancestor-read-write
Figure 4.1: Box Settings Attributes
Return values:
Empty string.
Error codes:
errorCodeDescription
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
deleteable
non-deleteable
4.2Box Settings
Each box has a number of settings that apply to it. Some of the settings are built into the system and some
have special semantics when they are set. You can also add new settings to a box if you wish. If you do, the
interpretation of those settings is up to the Tcl code you write. Each box setting has a number of attributes
as shown in Figure 4.1.
Each setting is assigned to a box number and has a name and a value. If a setting is read-only, you or your
ancestors can’t set it. Only the superbox can create and set read-only settings. If a setting is read-write,
you or any of your ancestor boxes can set it. If a setting is ancestor-read-write, only your proper ancestors
can create or set the value. Also, settings can be deleteable or non-deleteable. If a setting is deleteable,
then you or your ancestors can delete the value from the table. If the setting is non-deleteable, then it
cannot be deleted from the table. The settings which are built into the system cannot be deleted, though
any additional settings which were added and marked non-deletable can be deleted by the superbox.
The box settings functions are as follows:
*get box setting attrs [-box box ] [keys]. . .
Description
This function returns information about the mutability and deleteability of each of the keys given. If -box
isn’t given, get the attributes of the currently logged in box. You can read any box’s attributes, including
your ancestors.
Return values:
An a-list of a-lists.The first a-list is indexed by the keys given.The second a-list has two indices:
deleteability and mutability. Their values are the deleteability and mutability of the key.
54
Error codes:
NOTNONNEG box
KEYNOTEXIST box key
BOXNOTEXIST box
BREQNLOGIN
INVALIDVALUE value
Set the value of box settings atomically. If the key doesn’t exist, it is created. You can only change the
settings on your box or a descendent box. Only the superbox for the tree of the box in question can set a
setting to non-deleteable and only a superbox can delete a non-deleteable box setting.
The -mutability flag has varying restrictions depending on its value. Only the superbox can create and set
a setting with the read-only attribute. If you wish to set a value with the ancestor-read-write mutability,
you must set a value on one of your proper descendent boxes only. You cannot set a setting on your own
box with this mutability or you wouldn’t be able to modify or delete the setting. You can create a setting
with read-write mutability on your box or any of your descendent boxes.
The -deleteability and -mutability options apply to all the key/value pairs on the command line. If
you wish to issue separate attributes, you have to use different commands. The defaults are a deleteability
of deletable and a mutability of read-write.
Boolean values can be given as yes, true, 1 and no, false, 0. Integer values can be given in base 10, 8 or
The following error codes depend on the key in question:
Error codes:
errorCodeDescription
INVALIDVALUE key value
GBOXLIMIT limitFor MAX BOXES only.
delete box setting [-box box ] [key]. . .
55
Description
Delete the settings from the indicated box. If the mutability is read-only, only the superbox can delete
the setting. If the mutability is read-write, you or any of your ancestors can delete the setting. If the
setting is ancestor-read-write, then only your proper ancestors can delete the setting. Only the superbox
for the tree in question can delete deleteable settings. Of course, the deleteability attribute overrides the
mutability attribute.
This command ge ts the value of the settings of keys for a particular box atomically. You can read any box’s
settings, including your ancestors’.
Return values:
Value is returned as an a-list.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
KEYNOTEXIST box key
BREQNLOGIN
INVALIDVALUE value
*get box setting keys [-box box ]
Description
Get all the keys associated with a box.
Return values:
A Tcl list containing the keys for the box.
56
Error codes:
NOTNONNEG box
BOXNOTEXIST box
BREQNLOGIN
INVALIDVALUE value
Currently, the built-in box settings are as follows. Each key is non-deleteable.
KeyTypeMutabilitySemantics
ABBREV DATESboolread-writeControls how Amanda says the date if it is
today or yesterday. If true, say “today” or
“yesterday.” If false, say the date. Default:
true.
ABBREV GREETINGboolread-writeIf true, say “Please leave a message at the
tone.” If false, say “Please leave a message
for. . . ” Default: false.
BEGIN REC PROMPTboolread-writeThis setting controls the playing of the “Begin
recording at the tone. . . ” prompt for callers to
this mailbox.
BOXES LEFTnumread-onlyThe number of guest boxes left for this box to
allocate. If you allocate some boxes to your
children, this number will be decremented.
This number is decremented for guest boxes
only.
BUSY CODEstrread-writeCode to execute when a transfer for this box
detects a busy signal.
BUSY HOLDboolread-writeDetermines whether a caller can hold for a
busy extension.
CALLER CODEstrread-writeTcl code to execute when someone calls this
box. Usually you will set this to a procedure
name in the persistent method database. You
need the CHANGE METHOD privilege to set
this value.
CALLS COUNTnumread-onlyNumber of times someone has called into this
box or transferred to this box via one of the
code callbacks. This value is set with the
set internal box setting call.
CALL SCREENboolread-writeCall screening boolean. If on, the caller must
identify themselves to the system before the
call will go through. Then when the phone
rings, the system speaks the name they gave.
You need the CALL SCREEN privilege to
modify this field.
57
KeyTypeMutabilitySemantics
CALLS SECSnumread-onlyThe cumulative number of seconds that the
* CODE settings are run for this box. This value
is initialized with the
set internal box setting call and
terminated with the finish call time call.
CALLS TIMEnumread-onlyWhen a caller calls into this box and one of the
* CODE settings is run, this is the time that the
code started. This value is set with the
set internal box setting call.
COMMENTstrancestor-read-writeFree-form comment.
CONNECT COUNTnumread-only# of times a connection has been made
between caller and the person this box belongs
to. The person for this box must actually pick
up the phone. This count is not incremented if
the caller is sent to voice mail. This value is set
with the set internal box setting call.
CONNECT TIMEnumread-onlySet when a connection between a caller and the
person the box belongs to is made. This value
is set with the set internal box setting call.
CONNECT TONEboolread-writePlay a beep or don’t play a beep when the
called party connects to the phone.
CREATE TIMEnumread-onlyTime that this box was created in number of
seconds since midnight, January 1, 1970, GMT.
CTRIGGER COUNTnumread-only# of times that a notify record has been
executed for this box.
CTRIGGER TIMEnumread-onlyTime the last notify record executed.
CUSTOM BUSYboolread-writeDoes user want his custom busy message to
play? If so, the custom busy message is looked
up in the MMO Lookup Table under the key
busy msg.
DATE TIMEboolread-writePlay the date/time when before listening to
messages. Boolean.
DNDboolread-writeDo not disturb boolean. If on, the box’s
greeting is never played. You need the DND
privilege to change this value.
CODEstrread-writeCode to execute when a caller has finished
DONE
executing the instructions in this mailbox. You
need the CHANGE METHOD privilege to set
this value. The value is Tcl code to execute
and is usually a procedure name from the
persistent method database.
END REC MENUboolread-writeThis value controls the playing of the
post-record menu for both users and callers of
this box.
EXPUNGE DAYSnumread-writeIf read messages exist for longer than these
number of days, delete them.
EXPUNGE LOGOUTboolread-writeExpunge the deleted messages on logout.
EXTENSIONstrread-writeExtension the phone is on. May also contain
Amanda@Work.Group-style tokens. Need
EXTENSION privilege to modify.
58
KeyTypeMutabilitySemantics
GREET LENGTHnumancestor-read-writeMaximum number of seconds that a person’s
greeting can be. This is enforced in the Tcl
code only; therefore, if a person uses the telnet
interface, they can access the MMO Lookup
Table directly and get past this restriction.
This is not critical though.
CUR GRTnumread-writeNumber of the current greeting. You must have
the GREETING privilege to change this
number.
DELAYnumread-writeDelay in seconds after saying the greeting. It
gives the user time to think. You need the
GREETING privilege to modify this value.
ID CALLEEboolread/writeWhen doing a supervised transfer, and we
detect that the callee has answered the call,
then the system announces mailbox which is
transferring the call (e.g., for Tech Support, for
Scott Simpson, etc.) This allows you to have
two people using the same extension, or one
person acting in several capacities (sales, tech
support, and accounting, for example) to
answer the phone with an appropriate greeting.
LANGUAGEstrread-writeCurrent language for the box. If set to a
non-empty string, then the TUI will attempt to
load this language whenever the user logs into
this box.
LOCATIONstrread-writeLocation of user. Used for call routing. May
eventually store IP address or whatever. You
need the EXTENSION privilege to modify this
value.
COUNTnumread-only# of times user logged in.
LOGIN
LOGIN DURATIONnumread-onlyTime that this box has/was logged in in
seconds.
LOGIN TIMEnumread-onlyLast login time.
MAX BOXESnumancestor-read-writeMaximum number of guest boxes that this box
can create. This value can only be modified by
a proper ancestor. This value is intimately tied
with the BOXES LEFT setting. Ancestors can
modify this value but they cannot change the
BOXES LEFT value. If you change this setting
for a child, then your BOXES LEFT setting is
decremented appropriately. If you try to reduce
MAX BOXES for a child, you can only reduce the
value by an amount less than or equal to the
BOXES LEFT value for the child.
59
KeyTypeMutabilitySemantics
MAX MESSAGESnumancestor-read-writeMaximum number of messages allowed for this
mailbox. If this values is 0 then an infinite
number of messages can be stored. The
RECORD MSGS attribute take precedence over
this attribute. You’ll need the
RECORD MSGS privilege to change this value.
MAX MSG LENnumancestor-read-writeMaximum length of message in seconds. You’ll
need the RECORD MSGS privilege to change
this value.
MENU0 CODE
. . .
MENU9 CODEstrread-writeCode to execute when a caller presses 0
through 9 while listening to this box’s greeting.
You need the CHANGE METHOD privilege to
change these values.
MOD CALL SCRNboolread-writeAnnounce name and company when doing call
screening if false else announce the name and
extension if true. You need the
CALL SCREEN privilege to modify this
setting.
MOD BOXboolread-onlyThe last box to modify the settings on this box.
MOD TIMEnumread-onlyTime that any settings in this box were last
changed.
MSG HI WATERnumread-onlyHighest number of messages in box at one time.
MSGS RECEIVEDnumread-onlyCount of the number of messages that have
ever been received by this box over all time.
NEW FOLDERnumread-writeFolder number for new non-urgent messages.
(See also URGENT FOLDER.)
PARENTnumread-onlyParent box. Empty string for root box.
PLAY FROMboolread-writePlay back who the message was from (either
the box number or the name of the person).
PLAY NEW FIRSTboolread-writePlay the first unheard message first else play
the messages in order, regardless of whether
they were heard or not.
PLAY SKIPnumread-writeThe amount to skip forward or backwards when
this user is playing a message (see the -skipby
option of the play command on page 100).
RECORD MSGSboolread-writeWhether this box can record messages or not.
You need the RECORD MSGS privilege to
change this value.
RNA CODEstrread-writeCode to execute when doing a supervised
transfer and a ring-no-answer condition is
detected. You need the CHANGE METHOD
privilege to set this value.
RNA RINGSnumread-writeRing No Answer count. Ring this many times
before deciding that the extension isn’t going
to answer. Default: If 0, then the value of
n rings parameter in the Configuration
Database; if that value is not set, then 4.
60
KeyTypeMutabilitySemantics
STAT EPOCHnumread-onlyTime that statistical information started for
this box. This value is modified by the
reset box stats call. You must have the
RESET STATS privilege to use the
reset box stats call.
URGENT FOLDERnumread-writeFolder number for new urgent messages. (See
also NEW FOLDER.)
USER CODEstrread-writeTcl code to execute when user logs into their
box. You need the CHANGE METHOD
privilege to set to set this value.
reset box stats [-box box ]
Description
This command starts statistical information over for the specified box. The specified box must be either the
current box or a descendent box. Also, you need the RESET STATS privilege to execute this call.
Return values:
Empty string.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED
*set internal box setting box CALLS|CONNECT
Description
This command sets the box settings CALLS TIME and CALLS COUNT if CALLS is given or the settings
CONNECT TIME and CONNECT COUNT if CONNECT is given. This command is used in the TUI Tcl code. (Thiscall and finish call time are always called when you aren’t logged in.)
Return values:
Empty string.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
*finish call time box
61
Description
This command tells the system that a caller who visited this mailbox is chaining to another box or has hung
up; in either case, he is leaving this box, so the system should record the duration value in the CALLS SECS
setting. To be effective, of course, the TUI Tcl code must call this function when appropriate.
Return values:
Empty string.
Error codes:
errorCodeDescription
NOTNONNEG box
BOXNOTEXIST box
CTIMENOTSET boxThe call time hasn’t been set through the set internal box setting
call.
62
Chapter 5
Multimedia Objects (MMOs)
Amanda Portal deals with a variety of media types such as voice, fax, text, etc. Most the objects in the
system are voice messages such as prompts to play to the user when he dials up his voice mail, greetings and
messages people leave in other people’s boxes. However, there are also received faxes and e-mail messages in
the system too. To handle all these media types, Amanda Portal has the notion of a Multimedia Object or
MMO. An MMO is a handle to one of these multimedia objects. MMOs are persistent and are not deleted
until the last reference to them is deleted (i.e., they are reference counted). If the system dies ungracefully,
Amanda Portal runs an MMO integrity check program at startup to see if there are any MMOs stored on disk
that don’t have any references to them. If there are, they are deleted or moved to a recovery box. To move
them to a recovery box, you need to set the recovery box (recovery box) and folder # (recovery folder)
in the Configuration Database.
MMOs are typed, are read-only or read-write, and are forwardable or non-forwardable. MMOs “know” the
exact type of data that they hold; this is known as the MMO’s format. For convenience, you can also ask an
MMO what category of content it has, such as voice, fax, or text. If an MMO is read-only, you cannot change
its contents because there may be other references to that same MMO; you can only dereference it yourself.
When you overwrite writable MMOs, you may use a different type if you wish. If an MMO is “forwardable,”
then you are free to forward this MMO to another person’s mailbox. If an MMO is “non-forwardable,” then
you cannot store it in any way, typically such as forwarding it to another mailbox. Only you can read it.
The idea is that if someone sends you a message only they want you to listen to, you shouldn’t be able to
forward it to someone else so they can listen to it and you can’t store a copy of it somewhere where someone
else can access it.
MMO have “access implies listen” se mantics; that is, if you can get a handle to an MMO, you can listen
to it. However, you cannot get access to an MMO except through a certain set of functions provided by
the Amanda system. These sets of functions specifically restrict you from getting handles to MMOs you
shouldn’t have handles to.
MMO handles are immutable; that is, you can get MMO handles and delete MMO handles but you cannot
set them to a value. If you have a variable v that is an MMO handle and you try to do
set v 1
it will fail. This is implemented through the Tcl trigger mechanism which allows you to run code whenever
a variable is read, written or deleted. The reason for their immutability will be discussed later. However,
you can unset the variable and then set it to accomplish the same end result.
63
Every type of MMO has the following member functions:
*mmo func read only
Description
This function determines whether an MMO handle is read-only or read-write.
Return values:
0 or 1
*mmo func forwardable
Description
This function determines whether an MMO handle can be forwarded to a user or not.
Return values:
1MMO is forwardable.
0MMO is not forwardable.
*mmo func ref count
Description
This function returns the number of references to the MMO.
Return values:
Reference count.
*mmo func type
Description
This function returns the type of the MMO handle.
64
Return values:
audioAn audio MMO.
faxA fax MMO.
textA textual MMO.
unknownThe type is unknown. Until an MMO is assigned to after creation, its
type is unknown. You can reassign a new value to a writable MMO and
give it a different type.
*mmo func length
Description
This function returns the length of the MMO. The length value returned depends on the MMO type.
Return values:
secondsFor audio MMOs, the length returned is the number of seconds of audio.
pagesFor fax MMOs, the length of the fax in pages.
charactersFor textual MMOs, the length of the text in characters. For Unicode,
it is still the character length, which will be half the byte count since
Unicode uses 16-bit characters.
Error codes:
errorCodeDescription
UNKNOWNTYPEThe MMO has not been assigned to yet so the type is unknown.
*mmo func audio length
Description
This function is just like the length member function, but it returns the length of an audio recording in
milliseconds rather than in seconds. The extra precision is sometimes needed in the TUI.
An MMO of a category other than audio will return 0 as the result of this function.
Error codes:
errorCodeDescription
UNKNOWNTYPEThe MMO has not been assigned to yet so the type is unknown.
*mmo func format
Description
This function returns the format of the MMO. The format returned depends on the MMO type.
65
Return values:
Audio type only
mulawMu-law.
alawA-law.
waveWave. Header specifies format.
wave8Wave file known to be 8-bit mulaw.
wave16Wave file known to be 16-bit linear.
wavegsmWave file known to contain audio in MS GSM format (GSM 6.10).
gsmMS GSM format.
g72xG.721 adpcm.
oki-24Oki 24K adpcm.
oki-32Oki 32K adpcm.
sx9600SX9600 (I-Link proprietary) compressed audio
basicSun proprietary Audio-Basic format (Mu-law)
rht-24“Rhetorex” proprietary 24K adpcm.
rht-32“Rhetorex” proprietary 32K adpcm.
linear16-bit PCM “Linear”.
This function returns the last-modified date on the file underlying this MMO. The date is returned as an
integer number of seconds since midnight, January 1, 1970, GMT (i.e., the Coordinated Universal Time).
*mmo func convert to format
Description
This function can be used to change the format of a writable audio MMO. It could be used, for example,
just after the MMO has been recorded and before it is stored (which will convert it to non-writable). The
format value must be one of the audio formats listed above.
66
*mmo func append fax MMOs. . .
Description
This function can be used to append FAX-format MMOs together. This can be useful, for example, when
you have several documents that are to be sent to the same recipient in the same FAX transmission.
The mmo func may be either a FAX-format MMO or it may be an empty (typeless) MMO. In the former
case, the other MMOs will be appended onto the end of mmo func. Naturally, the MMOs may be read-only,
while mmo func must be writable.
*mmo func path
Description
This function returns the actual location (path) on the disk of the backing store of this MMO.
*mmo func date
Description
This function returns the last-mo dified timestamp on the file underlying this MMO. The result is the number
of seconds since 1/1/1970 GMT.
*mmo func id
Description
Returns a unique identifier for each MMO within the system. The same underlying MMO will give the same
unique id back regardless of how the handle to it was acquired or the name of the handle. This value can be
used for checking MMO equivalence.
*mmo func copy to format copy from mmo
Description
67
This function copies the copy from mmo into the current mmo using the specified format.
Return values:
Empty string.
*mmo func append to copy from mmo
Description
This function appends the copy from mmo into the current mmo. The format of both mmos must be the
same.
Return values:
Empty string.
The following calls are specific to textual MMOs. That is, they exist as member functions of textual MMOs,
but not of other MMOs.
*mmo func get text
Description
This function returns the text associated with the MMO.
Return values:
Text. Unicode MMOs currently come back as strings since Tcl doesn’t currently support Unicode.
*mmo func set text [-html] [args]. . .
Description
This function sets the value of the textual MMO to the arguments passed. If multiple arguments are passed,
it concatenates the strings using spaces. Of course, the MMO itself must be writable. If -html is specified,
then the MMO type is set to HTML, and otherwise it is set to ASCII.
Return values:
Text value MMO is set to.
Error codes:
MMODISKFULL
68
When you want to create a new MMO, you use the following function:
*create mmo [var]. . .
Description
This function creates a new MMO. A list of variable names should be given. A new MMO is created in
each variable. The MMOs created have no type. Types are assigned to them when you set the MMO. Any
existing variables with the same name are deleted first.
Return values:
Empty string.
Error codes:
MMODISKFULL
*dup mmo var
Description
Duplicate an existing MMO handle into a new variable var. The new variable will represent its own reference
to the MMO, so calling this function will increment the MMO’s reference count. It does not copy the contents
of the MMO into a new backing store location.
Return values:
Empty string.
print mmo var printer name
Description
Print an existing, viewable, MMO handle using the appropiate application through shell extensions.
This will use the specified printer name. This function uses the printto shell extension. By default,
Microsoft Notepad is used to print text and html files. Tiff files, however, do not print well with the default
Microsoft viewer. We recommend using Amanda Messenger to print tiff files.
Return values:
EMPTYThe var has no content.
NON PRINTABLEThe var is not viewable.
NO PRINTERUnable to get the default printer.
NO PRINTER DATAUnable to get the printer information.
INVALID PRINTERprinter name is invalid.
Messages consist of zero or more MMOs and some other metadata (such as the priority, privateness, who
it is from, etc.). The most common messages contain a single voice MMO; when a user calls you and you
aren’t in, they leave a message in your box. This message contains a single voice MMO. You can receive
faxes too and these can be stored in messages. Messages with multiple MMOs usually have been forwarded
to you from another user who recorded a prefix. You may receive a fax message and then decide to forward
the fax message to someone else. When you do this, you decide to precede the fax message with a voice mail
message that says “Jim, check out the prices for the new Gateway 2000 computer on this fax” followed by
the fax. You then forward these two MMOs to somebody else as a new message. When the recipient listens
to the message , Amanda announces that the current message contains a fax and then plays the voice portion.
The recipient can then receive the fax over the same phone line, send it to a printer, etc., by selecting the
appropriate option from the voice menu.
Some messages are private. Private messages cannot be forwarded onto other users. Messages also have a
priority level which is “urgent” or “normal.” This priority level is independent of the “privateness.” Each
message is also marked with as “read” or “unread” (i.e., “heard” or “unheard” for voice messages). You
mark a message as “read” by setting the -read flag to the set msg attr command (see page 74).
Messages are divided up into folders numbered 0 through 9. In your box settings, you specify where you want
received messages to go (see NEW FOLDER and URGENT FOLDER). When you log in, the TUI may announce
how many new normal and urgent messages you have.
Each folder also has the concept of a “current” message. As you move forward or backward in the folder,
the internal current mess age pointer gets updated. When you switch from one folder to another, you start
before the first message in the folder (i.e., you must do a get next msg call to get the first message).
All the messages in all the folders are actually stored in a single file for efficiency. When a new message
is added to any folder, it is simply appended to the file. The internal representation looks like Figure 5.1.
Whenever message numbers are passed into or returned by Tcl calls, the internal message numbers (35 and
14 in figure 5.1) are used. You never pass in the relative message numbers (the 0, 1, 2 above) in the folder
and you will never get them back from the internal functions.
70
Deleted messages are not deleted right away. Instead, they are marked for deletion. Later they can be
undeleted by using the set msg attr command. To delete a message permanently, you can use the expunge
command. The second box above shows a permanently deleted message. Permanently deleting a message
leaves a gap in the file that may be garbage collected at a later time. When the file contains only permanently
deleted messages on the end, it is truncated to a shorter length. Notice that each message is not labeled
with a number within its respective folder; its location is computed by searching left to right. Also, there is
only one current message pointer for all folders so when you switch folders, the current message pointer is
reset to the beginning.
Message may be expunged automatically according to a setting on your box (EXPUNGE LOGOUT). If this is
set to true, deleted messages will be expunged on logout or when the interpreter is deleted (deleting an
interpreter executes the logout function automatically).
To simulate the Amanda@Work.Group functionality using this scheme, we can simply stick all messages
in folder 0 and use the normal/urgent and read/unread flags to determine the message status. For nonAmanda@Work.Group b ehavior, we can stick urgent messages in one folder and normal messages in another
folder.
The following are the message related functions:
change folder folder
Description
This function changes the folder to the one given. The current message is set to right before the start of the
first message in the folder. That is, you must use get next msg to get the first message in the folder.
Return values:
Empty string.
Error codes:
NOTNONNEG folder
OUTOFRANGE folder
get current folder
Description
This function returns the current folder number. When you first log in, folder 0 is the current folder.
Return values:
Current folder number.
get next msg [-urgent|-normal] [-read|-unread] [-deleted|-undeleted] [-wrap] msg var
71
Description
This function returns the next message in the current folder. You can get the next message accord-
ing to a variety of criteria. These criteria are specified by the -urgent/-normal, -read/-unread and
-deleted/-undeleted flags. Normally, if you give no flags, then the next message is received no matter
what its attributes are. If you give one of the flags, then the message found must have that attribute. Also,
you cannot give both flags in the above pairs or a match would never succeed (i.e., a message can’t be b oth
urgent and normal).
The -wrap option says to start over at the beginning if you don’t find the message off the end of the folder.
msg var is a variable to be filled out with information about the message. If the variable already exists, it
is deleted first. To reset the message to the beginning, set your folder to the current folder (which has the
side effect of resetting the message pointer too).
72
Return values:
If no message is found that meets the criteria, then the string NOMESSAGE is returned. Otherwise, an empty
string is returned and a variety of information is placed in msg var. This information is basically divided into
two logical sections: 1) MMO handles for each part of the message and 2) information ab out the message
itself. For each MMO in the message, an index is set in msg var that indicates the MMO’s position in the
message and what the internal function for the MMO is. That is, if the message contains two MMOs, then
msg var may get set as follows:
set msg var(1) mmo0
set msg var(2) mmo1
where msg var(n) is a handle that has a deletion callback for the MMO corresponding to the internal
function mmo0.
The msg var variable also gets filled with other information about the message. The indices are as follows:
KeyValue
urgent1/0
read1/0
deleted1/0
subjectsubject MMO for message. Notice that subjects can be spoken so they
are MMOs, not text. This value is not set if there is no subject.
countNumber of MMOs in the message, excluding the subject.
folderfolder #
private1/0
relay phoneRelay phone number. This is only returned if there is a relay phone num-
ber for this message. The relay phone number is passed in to send msg.
recipient listA list of box numbers and/or lists this message was addressed to.
fromThe box number this message is from. If the message was sent by a
logged out interpreter, this entry is not set.
forwarded byIf the message was forwarded, who it was forwarded by. This will not be
set if the message wasn’t forwarded.
receipt messageread/deleted. Only set if message is a receipt message. This message
was sent in response to another box reading the message (i.e., setting
the state of the me ss age to read. See set msg attr). This is set on the
response message you get back, not on the message you sent. Receipt
messages have no MMOs, so this flag differentiates between a regular
message with no MMOs and a receipt message.
date timeDate and time of message in # seconds since 1970 in GMT.
msg numberMessage number.
msg idThis is an internally created number for the message that is unique within
the Amanda system. The system generates this value.
caller idCaller id for the message. If no caller id received, it is not set.
alternativeThis is a boolean value (0 or 1) which indicates whether the message’s
components are alternatives, in the MIME sense, or whether they are
truely separate comp onents. This value will be set only when parsing a
multipart/alternative email message.
It is important to note that MMOs in a message are order dependent and they may be of different types.
For example, if the first MMO is a voice message that says, “The following is a fax.” and the next MMO is
a fax, then it is important that you play the messages in order. Unfortunately, you cannot simply pass all
the MMOs to the play command in one fell swoop if this is the case because the play command only plays
voice messages.
get prev msg [-urgent|-normal] [-read|-unread] [-deleted|-undeleted] [-wrap] msg var
73
Description
This function is the same as get next msg except it goes backward instead of forwards within a folder. If the
user is positioned prior to the first message in the folder (as after change folder), then get prev msg will
wrap to the last message in the folder, regardless of whether the -wrap argument is used. But if positioned
on the first message in the folder, then it will wrap to the last message only if -wrap is specified.
Return values:
See get next msg for return value.
set msg attr [-read|-unread][-urgent|-normal][-deleted|-undeleted][-subject mmo func]
[msg num]. . .
Description
This function allows you to set some attributes of a message. If you don’t give any message numbers, the
current message is used. If you give a message number, the internal message number is used. (Remember,
relative message numbers are never used.) You can even change deleted messages before they are expunged.
The first time that you change an unread message to an read message or delete an unread message by
switching the state flag, a return receipt message is s ent to the sender if “return receipt requested” is set. A
return receipt is also sent when the message is expunged without being read.
This function moves messages from the current folder to the given folder. When the messages are moved,
they are inserted into the new folder in time order. That is, they may be interspersed into the new folder,
depending on when the existing messages arrived in the new folder. (The messages aren’t really moved.
Their folder attribute is just changed.)
This function permanently deletes all the messages marked as deleted in all the folders. It also sets the
folder to 0 and sets the message cursor to before the first message.
Return values:
Empty string.
get folder stats
Description
This function returns information about each message in the current folder.
Return values:
A list of 2-tuples is returned. The first part of the 2-tuple is the internal message number of the message.
The second part of the 2-tuple is an a-list describing information about the message. The location of the
2-tuple determines the relative message number. For example, the list returned may look like
Relative message 0 has internal message number 46, message 1 has internal message number 3, etc. The
information values returned are the same as those filled in by get next msg with the exception of MMOs
being returned.
has next prev
Description
This function returns an A-list containing two items: next and prev. Each is followed by a value specifying the number of the next/previous message relative to the current message. If there is no non-deleted
next/previous message, the message number will be specified as -1.
75
get box stats [-box box ]
Description
This function returns information about all the messages in a box, regardless of which folder they are in.
It is used to get summary information about a box. If -box is not specified, then information about the
currently-logged-in box is returned; otherwise, information about box is returned. When using -box, then
box must be a descendent of the currently-logged-in box.
Return values:
The return value is an a-list. The entries are
KeyValue
msgsTotal # of messages in all folders.
unread msgsTotal # of unread messages.
deleted msgsTotal # of deleted messages.
urgent msgsTotal # of urgent messages.
normal msgsTotal # of normal messages.
urgent unread msgsTotal # of unread urgent messages.
normal unread msgsTotal # of unread normal messages.
goto msg msg msg var
Description
This function goes to a message using the message number. This function automatically switches the current
message and folder corresponding to message msg when it is called.
Return values:
The return value is the same as get next msg.
Error codes:
NOTNONNEG msg
MSGNOTEXIST msg
set message box box
Description
76
This function is use to set which mailbox messages are being currently accessed. The default is that you
can only access the messages of your own mailbox. Once set for another box then you just use the same
tcl message commands as if they where your own message of your mailbox. To start accessing yoru own
messages again, just call this function again by passing in your mailbox. For security purposes the mailbox
that you want to access its message of, box , must grant you permission, unless it is your own mailbox. box
maintains a list mapping named msg access that lists all the mailboxes that has access rights to do anything
to its messages. In order to change this list, you must be the owner mailbox of the list or have the privilege
msg access prepended by the mailbox that you want to change, for example msg access100. If a mailbox
forwards a message for the owner mailbox, the true mailbox doing the forwarding will be marked as the
forwarded by. If a return receipt is requested of a message that is first read by a mailbox other than the
owner then the return receipt will be from the mailbox causing the return receipt, not the owner mailbox.
Note that calling this function will cause a possible purge and/or expunge of the previous mailbox’s messages
that was being accessed, for example the first time you call this in a session then that purge or e xpunge will
be on your own mailbox, just like if you were logging out of your mailbox.
Error codes:
PERMDENIED box
Messages can be sent either to individual boxes or mailing lists. When sending to mailing lists, the contents
of the mailing list are automatically looked up in the “list mapping database” (see page 125). Recipients are
specified with the syntax:
boxorlist *[box]
To send to a box, you just give the box number. To send to a mailing list, you send to a list number followed
by a ‘*’. Optionally, you can also give a box number for the mailing list if the mailing list doesn’t correspond
to the box you are currently logged in under. In fact, you must give the box number if you are not logged
in. Even users that are not logged in can send messages (using send msg) and check whether a box receives
messages or not.
Here are the calls related to sending a message:
*check recipients [box |mailing list]. . .
Description
This routine validates the recipients of a message. Boxes are validated to see if they exist and store messages.
A box “stores messages” if either it stores messages directly and/or has its messages copied to another box.
This rule is not transitive. That is, if there are three boxes A, B, and C and ‘→’ indicates copying messages
and A→B→C and A receives a message, it is stored in A (if A s tores messages) and in B, but not C. Mailing
lists are only validated by checking the box number (if given). We cannot check if the mailing list exists
because there is no difference between a mailing list not existing and being empty. (That is, the lmapping
routines return the same result for non-existence and emptiness). Also, if allowed to store messages then the
limit of the number of messages is checked. If you are not logged in and execute this call, the box number
for a mailing list must be given.
77
Return values:
The list of boxes or mailing lists that are invalid is returned. Invalid syntax arguments are returned also.
[-at time ] [-fcc folder ] [-caller id number] [-expire time] [-expire absolute] [recipient]. . .
Description
Send a message to a list of recipients. Recipients can be boxes or mailing lists with the syntax as described
above. The options are as follows:
-urgentMark the message as urgent.
-privateMark the message as private. The recipient will not be able to forward
the message or any of its contents to another user.
-receiptSend a return receipt. When the user reads the message, you will get a
message back saying it has been read. If the user deletes the message
without reading it, you will get a message back saying the message was
deleted without being read. This is implemented internally in the system
when the target box sets the unread or deleted flag on the message. When
this is done for the first time on the message, a return receipt message
with no MMOs is sent to the originating box with the receipt message
flag set to read or deleted.
-subject subjectSet the subject of the message to subject. Notice this is an MMO, so it
can be voice (or a fax!).
-relay numberSet the relay field in the message to number. This number can be used
by a notify template which is executed by one of the box’s notify records.
This template then can sends a page to the recipient telling him the
number in the relay field so that he can return the call directly without
having to listen to voice mail first.
-mmos mmo func listSend the MMOs listed to the person. This option will usually always
be given because you will usually be sending voice mail to people. It is
possible to send messages to somebody without any MMOs though. You
may wish to send just a subject for example.
-at timeSend the message at the indicated time. time is given in the number of
seconds since 1970 and is always given in GMT. Use time manipulation
functions described in section 17.2 to manipulate time values.
-fcc folderFile a copy of the message in the indicated folder.
-caller id numberSet the caller id for the message. The caller id field in the message
will be set when get next msg is used.
-expire timeExpire the message at the absolute time. time is given in GMT and is
the number of seconds since 1970 (the standard time count). If this is
not given, the message does not expire.
-absoluteExpire the message even if it was heard.
MMOs sent by this command will automatically be made read-only if they are not already. This prevents
modification of the MMO after sending it.
Return values:
The list of recipients that the send failed to. This list is usually empty. There is a race condition between the
time that you check whether a box is valid and the time you make the send msg cal l. If the box is deleted in
the interim, it will be returned as the result of this function but the message will be sent to the other boxes.
Forwards a message to the recipients listed. The MMOs in mmo func list are prepended to the original
message MMO list. See the send msg function for a description of the options. The MMOs in mmo func list
are made read only as described in the send msg command.
This command moves all messages from one of your descendent boxes to a different descendent box. The
messages stay in the same folder and retain their other attributes. You need the MESSAGE MOVE privilege
to execute this command. Neither box can be logged in at the time and the dest box must not have any
messages already.
This command is commonly used by hotels when a guest moves to a different room.
Only the normal notify type is fired off for the mailbox that the messages were moved to, no matter what
the status (new, old) or types (normal, urgent, relay) of the messages that were moved. A pickup notify
type is fired for the mailbox that the messages were moved from.
79
Internal MMO Database
– type
– length
– ref count
MMO Lookup Table
OwnerKeyDescAccessMMO
3grt1. . .public
private
6
Handle in memory
– read or read/write
– forwarding/no-forwarding
(No forwarding implies no
forwarding and no storage in
CO
CO
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
C
MMOsBoxMsg#ForwardabilityOther. . .
C
MMO Lookup Table).
Messages Table
51forwardable
non-forwardable
lookup mmo—Forwarding OK always set in
handle in memory. Handles always stored and
returned read-only.
get next msg—Private messages have “no for-
warding” set in handle, others set to “forwarding
OK.” Handles always stored and returned readonly.
Figure 5.2: Internal MMO database structure.
Return values:
Empty string.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
5.2MMO Database
MMOs are stored internally on disk as files. The types of these files vary depending on whether the MMO is
a voice file, text file, etc. Also, access to MMOs is restricted by the “kernel interface” of Amanda (the calls
built in). If access were not restricted, anybody could listen to anybody else’s messages.
MMOs are stored in an internal database that is referenced by two other databases: the MMO Lookup Table
and the Messages Table. Figure 5.2 shows the architecture. The Internal MMO Database stores the MMO
information persistently (as files) and stores some meta information about the MMO such as its type, length,
and a reference count.
80
The MMO Lookup Table is a persistent MMO storage area for MMOs. It stores such things as a person’s
greeting, the names they assign to folders, etc. Entries in this table can be public or private. Public entries
can be accessed (i.e., get an in me mory handle) by anyone, while private entries can only be accessed by the
owner of the box. In addition, MMO handles are always stored and retrieved from the MMO Lookup Table
read-only. When you s tore an MMO handle that is read-write (i.e., an MMO handle created by create mmo),
the MMO handle automatically converts from read-write to read-only as soon as you store it in the MMO
Lookup Table. This prevents you from overwriting the MMO if it is in use. No one else can have a writable
handle to the MMO because writable handles are created with create mmo and this call creates interpreter
specific handles. Any person can forward the MMO extracted from this table to anyone else. When the
MMO handle is created by lookup mmo, “forwarding OK” is always set to true.
The Messages Tables stores the MMOs that comprise an individual message in your box. You can’t access this
table directly except through the get next msg and get prev msg functions (see page 72). These functions
will return a list of MMO handles for the current message in your box. The handles returned will all be
read-only. If you then store the handles in the Lookup Table, they too will be read-only. Private messages
have “no forwarding” set in the handle when they are created by get next msg. If an MMO handle has “no
forwarding” set, you cannot forward this message to another user. In addition, if “no forwarding” is set,
you cannot store the message in the MMO Lookup Table. If you could, then you could bypass security by
extracting the message from the MMO Lookup Table which always sets forwarding to “forwarding OK.” The
whole forwarding concept is built into the system to support people sending you private messages. These
messages shouldn’t be able to be heard by anyone except the box owner the message was destined to.
Whenever you create a new MMO handle with the create mmo function, the handle is always read-write and
forwardable. As soon as you send the MMO to a person in a message, forward it or store it in the MMO
Lookup Table, the handle is immediately made read-only to prevent you from modifying it if someone else
is using it.
Recall that MMO handles are really special variables bound to internally created functions. The objectoriented functions supported by an MMO handle are rather limited (see page 64). Functions exist for
things like querying MMO handles about the read-only or read-write aspect, whether the MMO handle is
forwardable or not, what type the MMO handle is, etc. You cannot play an MMO handle using its member
functions; to play the MMO handle, you have to use a VP device’s member function and give the MMO
handle as an argument (see Chapter 6). Also, composing a forwarded message out of any “non-forwardable”
MMO handles is verboten.
Here are the commands for manipulating the MMO Lookup Table:
*lookup mmo [-box box] key var
Description
This function looks up an MMO in the MMO Lookup Table. You can use the -box option to specify a
descendent box to look up. Every MMO returned is read-only. If var exists, it is silently replaced.
Return values:
Empty stringThe MMO handle was successfully filled in.
EMPTYThe key was found, but the MMO handle was empty. In this case, var
is not set.
NOTFOUNDKey not found.
81
Error codes:
errorCodeDescription
NOTNONNEG box
PERMDENIED boxYou are not allowed to access the MMO. See lookup mmo attr for access
rights.
BOXNOTEXIST box
BREQNLOGIN
INVALIDVALUE value
*lookup mmo attr [-box box ] key [field ]. . .
Description
Lookup information in the MMO table about everything but the MMO. field is a field to look up. The
permission restrictions are the same as for lookup mmo. In fact, you cannot even look up the field private if
you don’t have permission to see if the field is private. That’s OK. The lookup mmo keys command doesn’t
return private keys when you don’t have permission to look at them. The set of fields are as follows:
worldAnyone can access the MMO, even non-logged in users.
logged inAny logged in mailbox can access the MMO.
familyOnly ancestors, descendants, and owner can access the MMO.
owner ancestorsOnly ancestors and owner can access the MMO.
owner descendantsOnly descendants and owner can access the MMO.
owner onlyOnly the owner can access the MMO.
Return values:
a-listAn a-list of the values found.NOTFOUNDThe key was not found.
Lookup all the keys in the MMO table for the current box. If -box is given, lookup the keys for the given
box instead. In this case, the access rights if the MMO is checked. If access is granted then the key is listed.
If a pattern is given, the keys returned must start with that pattern. See lookup mmo attr for access rights.
Return values:
An regular Tcl list of all the keys.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
INVALIDVALUE value
BREQNLOGIN
Populate the MMO table. Values are stored under the given key. When modifying values in the table, any
values not set are left unmodified. You can set the MMO or any of the other attributes in the table using
this function. This function will also create the key if it doesn’t exist. The defaults on a newly created key
are an empty MMO handle, the empty string for the description and -private world. You may also give
the empty string to unset the MMO. If the MMO you store is currently read-write, it will magically change
to read-only after calling this function. See lookup mmo attr for the private values.
Return values:
Empty string
Error codes:
NOTNONNEG box
BOXNOTEXIST box
PERMDENIED box
FORWNOTSTORE func name
CMDNOTEXIST mmo func
CMDNOTHANDLE mmo func
INVALIDVALUE value
delete mmo [-box box] [key]. . .
83
Description
Deletes the keys from the current box or the given box. Ancestral permission relationships apply.
Return values:
Empty string.
Error codes:
NOTNONNEG box
BOXNOTEXIST box
KEYNOTEXIST key
PERMDENIED box
INVALIDVALUE value
5.3Announcements
Announcements are messages that are sent out to a group. Unlike messages, announcements are owned
throughout its life time by the one who created the announcement. Where as in messages, once it is
receive the recipient owns the message. Therefore announcements can be changed after it was created by
the one who created it. Another difference between announcements and messages is the type of recipient.
Message recipients are mailboxes or mailing list. Announcement recipients is just an object name, the
application defines what the object name is and who the true recipients are. The last major difference
between announcements and messages: is the support for different languages of the same announcement.
A message is usually in one language for it is directed at a particular recipient. MMOs are used to store
the different languages. For example the administrator of the system can have a Message of the Day
announcement that everyone in the system listens to.
Announcements can be marked heard by a particular mailbox to remember what is heard or not. But, if the
owner of the announcement changes the announcement, it is marked unheard by everyone in the system.
To be able to create and manipulate announcements you must have the ANNOUNCEMENTS privilege.
Announcements can be set up to be manipulated by several different mailboxes. The announcements are
stored in a common mailbox designated by the global configuration parameter announcement box. You must
have the GENERAL ANNOUNCE privilege to create and/or manipulate announcements in the announcement box.
The following are the commands to manipulate announcements:
owner create announcement [-category category] [-begin time begin time] [-end time end time]
[-recip type list recip type list] [-outbound total outbound total] [-general boolean] [-mmo listmmo list]
Description
84
The function creates a new announcement. The category states the general subject of the announcement.
The begin time and end time defines when the announcement can be listened to. A recipient can not get
an announcment outside of these times. The recip type list is a list if names that the application defines
as to who gets the announcment. The outbound total defines how many times the announcement is to be
called out. The application itself handles the action of doing the outbound call. The -general defines if this
announcement is stored in the announcement box. The mmo list is a list of mmo var language pairs stating
the actual announcement in each language. The first pair will become the default if the recipient’s language
is not available. In this case the language will be called default.
Return values:
idThis is the id of the newly created announcement.
INVALIDTIMESThe begin time and/or end time is invalid.
PERMDENIEDNot allowed to create an announcement in announcement box .
BOXNOTEXISTThe configuration parameter announcement box is invalid.
BADMMOLISTThe mmo list is invalid, p oss ible bad mmo vars.
Error codes:
errorCodeDescription
CONFNOTEXIST
announcement box
owner edit announcement id [-category category] [-begin time begin time] [-end time end time]
[-recip type list recip type list ] [-outbound total outbound total] [-mmo list mmo list ]
Description
The function edits an existing announcement. The id states which announcement to edit. The category
states the general subject of the announcement. The begin time and end time defines when the announcement can be listened to. A recipient can not get an announcment outside of these times. The
recip type list is a list if names that the application defines as to who gets the announcment. The
outbound total defines how many times the announcement is to be called out. The application itself han-
dles the action of doing the outbound call. The mmo list is a list of mmo var language pairs stating the
actual announcement in each language. The first pair will become the default if the recipient’s language is
not available. In this case the language will be called default.
Return values:
INVALIDIDThe id of the announcement to edit is invalid.
INVALIDTIMESThe begin time and/or end time is invalid.
PERMDENIEDNot allowed to edit an announcement.
BOXNOTEXISTThe configuration parameter announcement box is invalid.
BADMMOLISTThe mmo list is invalid, p oss ible bad mmo vars.
Error codes:
errorCodeDescription
CONFNOTEXIST
announcement box
85
owner delete announcement id
Description
The function deletes an existing announcement. The id states which announcement to delete.
Return values:
INVALIDIDThe id of the announcement to edit is invalid.
PERMDENIEDNot allowed to delete an announcement.
BOXNOTEXISTThe configuration parameter announcement box is invalid.
Error codes:
errorCodeDescription
CONFNOTEXIST
announcement box
get announcement id var
Description
The function gets an existing announcement. The id states which announcement to get. The var will be
the name of the associative array that contains the relavent information of the announcement. If the owner
of the announcement is doing the getting then all the attributes are returned. If a recipient is doing the
getting then only the following attributes are returned: category, owner, heard, mmo vars, and id. The
mmo vars uses the name of the language that each mmo represents.
Return values:
INVALIDIDThe id of the announcement to edit is invalid. Possibly the id is good, but
the it is not the right time as defined by the begin time and end time
PERMDENIEDNot allowed to get an announcement.
owner list announcement [-general boolean] [-category category]
Description
The function gets a list of existing announcement this mailbox can manipulate. The general defines whether
or not to include general announcements. The category filters the list to only include those announcements
of the specified category.
Return values:
id listA list of announcement ids
86
Error codes:
errorCodeDescription
CONFNOTEXIST
announcement box
INVALIDVALUE value
recip heard announcement id
Description
The function marks that this mailbox heard the announcement.
Return values:
INVALIDIDThe id of the announcement to mark heard is invalid.
recip list announcement [-recip type list recip type list ] [-category category]
Description
The function gets a list of existing announcement this mailbox can receive. The recip type list filters the an-
nouncements for only those recipient types. The category filters the list to only include those announcements
of the specified category.
Return values:
id listA list of announcement ids
outbound list announcement [-category category]
Description
The function gets a list of existing announcement that have a positive outbound total. The category filters
the list to only include those announcements of the specified category.
Return values:
id listA list of announcement ids
is announcement heard id
Description
The function marks that this mailbox heard the announcement.
87
Return values:
INVALIDIDThe id of the announcement is invalid.
0The announcement has not been heard by the mailbox.
1The announcement has been heard by the mailbox.
5.4Published MMOs
Published MMOs are MMOs that are made public to the world. Anyone can publish a MMO, being logged
in is not required. Once a MMO is published then other threads can get a handle on that MMO. Publishing
a MMO can be selective on who it is published to.
Published MMOs are not persistent. When publishing an MMO a variable gets associated with the publication. When that variable is unset then the publication ends. Other threads must get the published MMO
before the variable goes out of scope. But, once the thread has a handle on the MMO then they have a valid
reference on that MMO to do as it pleases.
An example usage of Published MMOs is the following: A caller calls in and records a name and company
recording. That recording gets published to the callee. The callee can get the recording to figure out if the
callee wishes to talk to the caller, basicaly a form of call screening.
The following are the commands to manipulate announcements:
*publish mmo mmo func common key publish var [mailbox]. . .
Description
Publish the mmo func to the specified mailbox list. If no mailbox list is specified then all this is published
to everyone. If mailbox is EVERYONE then it explicitely states to publish the mmo to everyone regardless of
other mailbox in the list. If mailbox is LOGGED IN then the mmo is published only to those threads that are
logged in. The common key is used to create a unique key to represent the Published MMO. The value ofcommon key can be anything. The publish var is the name of the variable that is created by this function
to represent the lifespan of the publication. Once this variable goes out of scope then the MMO is no longer
published. During the published time the, the variable represents another reference on the MMO.
Return values:
BOXNOTEXISTmailbox is invalid
MMONOTFORWARDABLEmmo func is invalid
*get published mmo unique key mmo var
Description
Get the Published MMO as specified by the unique key and create a new handle for the Published MMO
that is named mmo var .
88
Return values:
KEYNOTFOUNDunique key is invalid
*list published mmos
Description
Get a list of Published MMOs that you have access to. What is returned is a list of unique key that can be
used by the get published mmo function to get the actual mmo. The returned list can be empty.
Return values:
unique key listA list of published mmo unique keys
89
Chapter 6
Voice and Fax Devices
Voice devices and to a lesser extent fax and internet devices form the heart of Amanda Portal. Currently, the
only voice boards supported by Amanda Portal are a certain set of Dialogic boards which support the SCBus
and a certain set of Rhetorex boards. In addition, the Dialogic GammaLink SCBus fax resource boards are
supported. These modules are Dialogic.DLL, RhetStd.dll, and Gamma.DLL. In addition, a SoundCard
driver is available which provides essentially the same functionality as one telephone port plus a telephone.
It is used for testing and demonstration purposes, and it is called Audio.DLL.
There are two sets of numbers assigned to devices in the system—ports and units—and they are not the
same. Unit numbers are unique for each device within that device’s class. That is, each VP device will have
a different unit number but there may be an LS device with the same unit number. In addition to unit
numbers, physical telephone ports are assigned unique port numbers.
When connecting devices on an SCBus, any device can broadcast to as many devices as possible but only
listen to one device at a time. Figure 6.1 shows the concept. The VP device is listening to device LS1and
devices LS2and LS3are listening to the VP device. It is possible for a device to listen to no one and send
to no one too.
6.1OOP Model
There are two approaches to use when defining Tcl commands: action-oriented and object-oriented. In the
action-oriented approach, there is a separate command for each action taken by an object and the object is
passed as the first parameter. That is, you would call play $mmo to play an MMO object. This works well
when there is a large number of objects.
LS
Figure 6.1: LS and VP device configuration
LS
1
6@
@R
VP
90
2
LS
3
In the object-oriented approach, there is one command for each object and the name of the command is the
name of the object. (In our implementation, a variable and the command name are bound together.) The
first argument to the command specifies the command to execute. For example, you would say “$vp play$mmo” to play the MMO on the VP device.
The object-oriented approach lends itself well when the number of objects is small and the operations on
the objects may vary. VP and MMO handles fit this description nicely. Also, Tcl allows you to add internal
state to functions (through ClientData) but not variables. Therefore, using the object-oriented approach
allows us to attach state to the function referenced by the $vp variable. We couldn’t do this using the
action-oriented approach. Currently we attach information about the VP or LS device in the state field.
This state is actually a C++ object that we can operate on once inside C++.
6.2Interconnection Limitations
Dialogic puts LS and VP devices on the same board or allows you to connect the devices on separate boards
through the SCBus. This bus is a connector cable between two different boards that allows the boards to
communicate with each other at high speed. Unfortunately, some Dialogic boards have limitations on the
separate, simultaneous use of the LS and VP devices such that it is better to use a corresponding VP device
with each LS device, rather than an arbitrary one. To accomodate this limitation, each LS and T1 device
“knows” the unit number of its corresponding VP device, and has a dsp attach command which checks out
that particular VP device from the resource manager and establishes a full-duplex connection with it.
6.3Make local and Default Commands
Each object-oriented command has a command named cmds which lists all the c omm ands that are valid for
that device. If you type the command
$vp_handle cmds
you may get back something like
unit pause resume stop skip result play play_hold play_prompt speak
record pcpm beep load_prompt_set dialtone get_digits dial load_tones set_param
which lists all the commands that the $vp handle command will take.
91
If you have a VP resource, you may not wish to keep typing
$vp handle play mmo funcs. . .
all the time. Instead it might be nice just to say
play mmo funcs. . .
and use $vp handle as the default VP device. The built-in make local command creates a new command
for each function that the handle passed as its argument supports. When the function is called, it internally
uses the handle as its default argument. That is, make local basically works as follows:
proc make_local {handle_var} {
upvar 1 $handle_var handle
foreach func [$handle cmds] {
## For every cmd this handle has, define a new command with
## an implicit $handle before it.
proc $func {args} [list uplevel 1 [list $handle] [list $func] {$args}]
## Delete procedure upon variable exit.
## There should already be an unset trace on
## this variable for handle cleanup
## but we can add many more for each function we
## just added.
uplevel [list trace variable $handle_var u [list _delete_cmd $func]]
}
}
### Internal routine used by make_local.
proc _delete_cmd {func var index op} {
catch {rename $func {}}
}
The make local command will create a command like
proc cmd {args} {
uplevel 1 {handle} {cmd} $args
}
Notice that make local cleans up the procedures it creates when the variable leaves scope by creating an
additional trace callback for each procedure that it creates. (This callback is a “lexical closure” for you Lisp
fanatics.) The actual make local command is a built-in command, not a proc.
get board serial number board
Description
92
Returns the serial number of the indicated board. This function is available only when the Dialogic DLL
has been loaded with the system and when the logged-in mailbox has the MONITOR privilege.
Return values:
Returns the indicated board’s serial number.
Error codes:
NOTINTEGER board
INVALIDVALUE board
6.4Network Device Commands
Each of the T1, LS, VP, and Fax commands can get internal errors generated by the Dialogic driver. If this
is the case, a Tcl exception will be raised with an errorCode corresponding to the format
BOARDERROR error num
and the Tcl result will be set to the board error message. Virtually every board related function can receive
an error of this type, so these error codes are not listed.
To allocate an LS, VP, or Fax device, you’ll need to use the grab res routine discussed on page 38 or the
dsp attach command.
The network device (T1 and LS) commands are as follows. For simplicity, we use ls to represent any kind of
network device, and note any differences that exist between the different device types which are supported
in describing the command.
*ls set hook boolean
Description
This function sets the phone on or off hook for the ls device. It is usually used when you are receiving a call
or responding to a detected hangup.
Return values:
Empty string.
Error codes:
NOTBOOLEAN boolean
*ls seize
Description
93
This function seizes a line for an outgoing call. Call this before you make an outgoing call. On an analog
line, this simply goes off hook and verifies that loop current is present. On a digital device such as a T1 line,
the seize operation is verified digitally by the far end.
Return values:
Empty string.
*ls disconnect
Description
Actively hang up on any existing phone connection and insure that the call is terminated. It is the opposite
of seize. For analog devices, the channel is held on hook long enough so that it doesn’t look like a flash hook.
This time is determined by the value of the tmo pickup parameter in the Configuration Database. For T1
lines, of course, the release operation is verified digitally by the far end.
Return values:
Empty string.
*ls connect [-half] resource func
Description
Connect the LS device to another resource. If -half is given, only a half duplex connection is set up;
otherwise, a full duplex connection is set up. With a half duplex connection, the resource func device listens
to the ls device but not vice-versa. The use of dsp attach is preferred over this command because it takes
into account dependencies between the LS and VP devices.
Each LS (and T1) device which lives on a board which has b oth telephone network interfaces and voice
processing resources “knows” the unit number of its corresponding VP device. This command tells the
network device to check out that specific VP device and then establish a full-duplex connection with it. The
handle for the checked out VP device is put in var .
94
If -timeout is given, time out after the given number of milliseconds if the VP device isn’t available.
Return values:
If -timeout is given, then TIMEOUT is returned on a timeout. Otherwise, the return value is the empty string,
and the var is set to the device’s handle.
Error codes:
RESALLOC2BIG vp
NOTNONNEG milliseconds
*ls wait off milliseconds
Description
Wait for the other side to hang up. On an analog line, this function waits for loop current to drop. Some
phone switches do not drop loop current when the far end hangs up, so take care in using this function in
this situation—it will not return if reorder tone is detected. On a T1 line, it waits for digital indication that
the far end has hung up.
If milliseconds is given, wait a maximum of milliseconds else wait forever.
Return values:
Empty string.Success.TIMEOUTWaited more than milliseconds.
Error codes:
NOTNONNEG milliseconds
*ls wait on milliseconds
Description
On an analog port, this function waits for loop current to start. On a digital line, it waits until the far end
attempts to establish a connection.
If milliseconds is given, wait a maximum of milliseconds else wait forever.
Return values:
Empty string.Success.TIMEOUTWaited more than milliseconds.
Error codes:
NOTNONNEG milliseconds
95
*ls wait ring milliseconds
Description
This function is implemented by analog ports only. It causes the port to wait for ring voltage to be detected
on the line. On a digital port, use the wait on command to accomplish the equivalent function. The system
must receive n rings rings before the wait is satisfied, if this PBX parameter has been specified for this
port.
If milliseconds isn’t given, then wait forever.
Return values:
Empty string.Success.TIMEOUTWaited more than milliseconds.
Error codes:
NOTNONNEG milliseconds
OFFHOOK port
*ls unit
Description
Return unit number of LS device. Devices of the same type have different unit numbers, but devices of
different types may have the same unit number. This command won’t return the BOARDERROR errorCode like
the other commands will because it doesn’t make calls to the device driver for the board.
Return values:
Unit number.
*ls port
Description
Return port number of LS device. Every network device in the system has a unique port number; i.e., port
numbers are not the same even for different device types. This command won’t return the BOARDERROR
errorCode like the other commands will because it doesn’t make calls to the device driver for the board.
Return values:
Unit number.
*ls set screen text [-box box] [-status text ]
96
Description
This function sets the text on the screen for the associated port. You can set either field or both at once.
This routine won’t return an BOARDERROR errorCode because it doesn’t call the device driver functions.
Return values:
Empty string.
*ls flashhook
Description
Do a flashhook. In some environments, with some analog boards, this function actually does an Earth Recall
function on the b oard (set during driver installation for some voice boards). This function is not supported
on E1 boards. On T1 devices, the A and B bits are dropped for the duration of the flashtm parameter, or
500ms if that parameter is not specified in the Configuration Database.
Return values:
Empty string.
*ls get dnis
Description
This function works only on GlobalCall network handles.
It returns the Dialed Number Identification String as returned by the GlobalCall driver for the associated
port. If no DNIS information is available, then an empty string is returned.
*ls get ani
Description
This function works only on GlobalCall network handles.
It returns the Automatic Number Identification string as returned by the GlobalCall driver for the associated
port. If no ANI information is available, then an empty string is returned.
97
6.5VP Commands
VP devices can perform long-term operations like playing a recording. Because of this, their operations may
be interrupted by the detection of an exceptional condition, such as detection of certain incoming tones like
a Fax CNG tone or TDD initiation tone, which indicate that special call processing is in order. We may also
detect that the other party has hung up. Hangup detection is complex because there are so many ways that
phone switches may indicate it to us:
1. On an analog port, loop current may be dropped. We look for this condition if the Configuration
Parameter hangup supervision is set to true for the port in the Configuration Database.
2. On digital ports, the port device will get a digital indication almost immediately if the other party
hangs up.
3. The VP device, while playing or recording, may detect a tone or cadence which indicates that the caller
has hung up, called a reorder tone. Or it may detect other special tones such as a Fax CNG tone.
4. The VP device may detect one or more DTMF digits which indicate hangup. Whenever digits are
received, the VP device checks them against the setting of dt hangup in the Configuration Database
for the port to which the VP device has most recently been dsp attach’d (remember that potentially
the same VP device can be used with different network ports which are connected to different telephone
switches which might send different hangup sequences).
In the first two cases, the network device will signal the VP device which was most recently dsp attach’d to
it, that it has detected hangup. In the latter two cases, the VP device itself does the detection. Either way,
the VP device will then terminate whatever function it was performing and throw a HANGUP exception. If
a Fax CNG tone is detected, a VP device will throw a FAX INITIATION exception.
Here are the VP commands:
*vp unit
Description
Return unit number of VP device. Devices of the same type have different unit numbers, but devices of
different types may have the same unit number. VP devices don’t have port numbers. They don’t stick out
of the back of a machine.
Return values:
Unit number.
*vp pause
Description
Pause playing or recording of the current audio. You can resume playing or recording with the resume
command later. This command should only be executed after you have started an asynchronous operation.
You can also skip backward and forward while paused.
98
Return values:
Empty string.Success.
NOOPINPROGRESSNo operation is currently in progress.
*vp resume
Description
Resume playing or recording. This command should only be executed after you have stopped an asynchronous
operation.
Return values:
Empty string.
Error codes:
NOOPINPROGRESS
*vp stop
Description
Stop playing or recording on the specified device. This command differs from the pause command in that you
cannot resume. This command should only be executed after you have started an asynchronous operation.
Return values:
Empty string.Success.
NOOPINPROGRESSNo operation is currently in progress.
*vp skip backward|forward
Description
Skip forward or backward in the audio that is playing by “skipby” seconds. “skipby” defaults to five seconds
or the value of the PLAY SKIP box setting and can be set with the -skipby option on the play command
line. This command should only be executed after you have started an asynchronous play operation. If you
are currently paused, you will skip forward or backward and play will resume.
Return values:
Empty string.Success.
NOOPINPROGRESSNo operation is currently in progress. This is not an exception because
you want to a avoid a race condition. When you execute this command,
play may have finished. This should not be an error.
99
Loading...
+ hidden pages
You need points to download manuals.
1 point = 1 manual.
You can buy points or you can get point for every manual you upload.