Amanda Portal System Reference Manual

Amanda Portal System Reference Manual
Version 1.27E
The Amanda Company, Inc.
January 9, 2003
Contents
1 Introduction 5
1.1 Core Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Tcl As An Extensible Glue Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2.1 An Introduction to Tcl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.2 Tcl and Multithreading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
1.2.3 Tcl and State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.2.4 Assigning Scope to Procedures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.3 Control Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1.4 Waiting for Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.5 Loadable DLLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.6 Resource Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.1 Ancestors and Descendents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.2 Privileges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.3 Login and Logout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3 How Amanda Portal Interacts with Telephone Switches 46
4.1 Box Manipulation Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.2 Box Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
1
5 Multimedia Objects (MMOs) 63
5.1 Messages and Folders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
5.2 MMO Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.3 Announcements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
5.4 Published MMOs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.1 OOP Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.2 Interconnection Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.3 Make local and Default Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.4 Network Device Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.5 VP Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
6.6 Port Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
6.7 Miscellaneous Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
7 Fax Commands 113
8 Internet E-Mail 115
9 Serial Devices 121
10 Miscellaneous Databases 125
10.1 List Mapping Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
10.2 Trie Mapping Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
11 The Configuration Database 133
12 Triggers 138
12.1 Autoschedules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
12.2 Notifications and the Job Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
12.2.1 The Notify Record Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
2
12.2.2 The Notify Instance (Job Queue) Database . . . . . . . . . . . . . . . . . . . . . . . . 144
12.2.3 The Notify Template Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
12.3 Data Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
13 Persistent Procedures 162
14 Integration 164
14.0.1 Serial Integration Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
15 Call Queueing 168
16 Connecting to External Databases (ODBC) 191
17 Miscellaneous 197
17.1 The tokens Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
17.2 Time Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
17.3 Terminating Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
17.4 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
17.5 Speech Processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
17.6 Web Client Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
17.7 TCP Client Connections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
17.8 VoIP Appliance Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
17.9 COM/OLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
A Error Codes and Messages 218
3
Preface
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 func­tionality 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.1 Core 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.2 Tcl 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.1 An Introduction to T cl
Safe interpreters are simply regular interpreters with certain “unsafe” calls removed; that is, they are inter­preters 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,
case, catch, concat, continue, error, eval, expr, for, foreach, format, gets, global, if, incr, info, interp, join, lappend, lindex, linsert, list, llength, lower, lrange, lreplace, lsearch, lsort, proc, regexp, regsub, return, scan, set, split, source, string, switch, time, trace, unset, uplevel, upvar,
and while.
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 lan­guages 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:
Symbol Meaning
\a Audible alert (bell) \b Backspace \f Formfeed \n Newline \r Return \t Tab \v Vertical tab \ddd Octal value \xhh Hex value \newline Single 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 com­mands, 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 com­mands. 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 back­slash. 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).
Here are some example interactions with Tcl:
% expr 2*3 6 % set x 4 4 % expr $x * 7 28 % expr ($x * $x) / 2 8 %
10
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 arith­metic 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.
Syntax Result
a<b 1 if a < b else 0 a>b 1 if a > b else 0 a<=b 1 if a b else 0 a>=b 1 if a b else 0 a==b 1 if a = b else 0 a!=b 1 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).
-decreasing Sort in decending order.
Here are some examples of lsort:
16
% set x {22 12 9 97} 22 12 9 97 % lsort $x 12 22 9 97 % lsort -integer $x 9 12 22 97 % set sorted_list [lsort -integer $x] 9 12 22 97 %
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.
% split "abc/def/ghi" "/" abc def ghi % split "abc.def/ghi" "/." abc def ghi %
17
* linsert
Description
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 general syntax for the if statement is:
if test1 body1 [elseif test2 body2 . . . ] [else bodyN ]
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. When­ever 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.
% set x 4 4 % set y 5 5 % proc foo {a} {
global x y
return [expr $x + $a + $y] } % foo 3 12 % set x 6 6 % foo 3 14 %
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.2 Tcl 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.3 Tcl 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.4 Assigning 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 vari­ables. 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.3 Control 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.4 Waiting 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.
Here is the definition of the wait command:
* wait [-nostop] [-timeout milliseconds] [handle func]. . .
Description
34
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:
TIMEOUT A 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.
Error codes:
CMDNOTEXIST handle func CMDNOTHANDLE handle func NOTHREAD handle func
1.5 Loadable DLLs
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.6 Resource 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 defined Total resources of this type that exist. current available Total resources of this type that are currently available. max ever used Maximum 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 denied This 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 port­type 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 wait This 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 wait This 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.1 Ancestors 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.2 Privileges
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
ANNOUNCEMENTS This privilege allows you to create and modify announcements in your box. BOX CREATION This privilege allows you to create non-guest boxes. CALL SCREEN Allows you to set the call screening (CALL SCREEN) and modified call screening
(MOD CALL SCREEN) setting on a box.
CHANGE METHOD This 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 TMAPPING This privilege allows you to change entries in the a trie mapping database (see
page 129).
CONFIGURATION You can change the Configuration Database settings. COPY TO This 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 QUEUE This 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 TMAPPING This privilege allows you to create and destroy trie mapping database. CTRIGGER This privilege allows you to change and add notify records (“conditional triggers”)
to boxes. CTRIGGER CODE This privilege allows you to create and modify the bodies of notify templates
(“conditional triggers”).
DND Allows you to set the DND setting on a box. EDIT PMETHOD This privilege allows you to create or change one of the persistent methods. EXTENSION Allows you to set the extension (EXTENSION) or location (LOCATION) on a box. GENERAL ANNOUNCE This privilege allows you to create and modify announcements in the announc-
ment mailbox. GREETING This 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
KILL This privilege allows you to kill all the threads of a specific user or an individual
thread running on behalf of a user. MESSAGE MOVE This privilege allows you to move messages from one box to another
(move msgs to box). MONITOR This privilege allows you to get status updates of tracing, ports, resources, and
system.
NOTIFY ANY BOX This privilege allows you to schedule a notify within another mailbox. RESET PORT This privilege allows you to reset a port on a board. RECORD MSGS This 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 STATS This privilege allows you to reset the statistics gathering counters for a box (the
reset box stats call). SCHEDULE LOCK You can create/modify autoschedule records (“time triggers”)—see page 138). SHUTDOWN This 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.
Return values:
Empty string.
Error codes:
NOTNONNEG box BOXNOTEXIST box PERMDENIED box PRIVNOTEXIST privilege
43
errorCode
INVALIDVALUE value
get privilege keys [-box box ] [-use db]
Description
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.3 Login 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. 3 ext. 8
ext. 6
ext. 2
hunt group lines
port 5
Amanda
port 3
6
Tim Scott
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.1 Box 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.
Return values:
The box number created.
GBOXLIMIT
Error codes:
NOTNONNEG box
50
errorCode
PERMDENIED SYSBOXFULL BOXEXISTS box BOXNOTEXIST clone box
delete box [box]. . .
Description
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.
Return values:
Empty string. BOXLOGGEDIN delete box
Error codes:
NOTNONNEG box PERMDENIED login box deletion box BOXHASCHILD delete box child box INVALIDVALUE value
reparent box [move children] move box new parent
Description
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.
Return values:
Empty string.
Error codes:
PERMDENIED login box move box BOXNOTEXIST move box BOXHASCHILD move box child box BOXLOGGEDIN move box GBOXLIMIT move box BOXES LEFT
* next box box
51
Description
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:
errorCode Description
NOTINTEGER box OUTOFRANGE box User 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:
errorCode Description
NOTINTEGER box OUTOFRANGE box User 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:
errorCode Description
NOTNONNEG box BOXNOTEXIST box PERMDENIED box INVALIDPASSWORD password Password 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
Box Key Mutability Deleteability Value
1 setting name read-only
read-write ancestor-read-write
Figure 4.1: Box Settings Attributes
Return values:
Empty string.
Error codes:
errorCode Description
NOTNONNEG box BOXNOTEXIST box PERMDENIED box
deleteable non-deleteable
4.2 Box 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 box setting [-deleteability deleteable|non-deleteable] [-mutability read-only | read-write | ancestor-read-write] [-box box ] [key value]. . .
Description
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
16.
Return values:
Empty string.
Error codes:
NOTNONNEG box BOXNOTEXIST box KEYNOTEXIST key PERMDENIED box key
The following error codes depend on the key in question:
Error codes:
errorCode Description
INVALIDVALUE key value GBOXLIMIT limit For 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.
Return values:
Empty string.
Error codes:
NOTNONNEG box BOXNOTEXIST box PERMDENIED key KEYNOTEXIST box key INVALIDVALUE value
* get box setting [-box box ] [key]. . .
Description
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.
Key Type Mutability Semantics
ABBREV DATES bool read-write Controls 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 GREETING bool read-write If true, say “Please leave a message at the
tone.” If false, say “Please leave a message for. . . ” Default: false.
BEGIN REC PROMPT bool read-write This setting controls the playing of the “Begin
recording at the tone. . . ” prompt for callers to this mailbox.
BOXES LEFT num read-only The 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 CODE str read-write Code to execute when a transfer for this box
detects a busy signal.
BUSY HOLD bool read-write Determines whether a caller can hold for a
busy extension.
CALLER CODE str read-write Tcl 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 COUNT num read-only Number 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 SCREEN bool read-write Call 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
Key Type Mutability Semantics
CALLS SECS num read-only The 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 TIME num read-only When 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. COMMENT str ancestor-read-write Free-form comment. CONNECT COUNT num read-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 TIME num read-only Set 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 TONE bool read-write Play a beep or don’t play a beep when the
called party connects to the phone. CREATE TIME num read-only Time that this box was created in number of
seconds since midnight, January 1, 1970, GMT. CTRIGGER COUNT num read-only # of times that a notify record has been
executed for this box.
CTRIGGER TIME num read-only Time the last notify record executed. CUSTOM BUSY bool read-write Does 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 TIME bool read-write Play the date/time when before listening to
messages. Boolean. DND bool read-write Do not disturb boolean. If on, the box’s
greeting is never played. You need the DND
privilege to change this value.
CODE str read-write Code 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 MENU bool read-write This value controls the playing of the
post-record menu for both users and callers of
this box. EXPUNGE DAYS num read-write If read messages exist for longer than these
number of days, delete them.
EXPUNGE LOGOUT bool read-write Expunge the deleted messages on logout. EXTENSION str read-write Extension the phone is on. May also contain
Amanda@Work.Group-style tokens. Need
EXTENSION privilege to modify.
58
Key Type Mutability Semantics
GREET LENGTH num ancestor-read-write Maximum 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 GRT num read-write Number of the current greeting. You must have
the GREETING privilege to change this
number. DELAY num read-write Delay in seconds after saying the greeting. It
gives the user time to think. You need the
GREETING privilege to modify this value. ID CALLEE bool read/write When 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. LANGUAGE str read-write Current 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. LOCATION str read-write Location of user. Used for call routing. May
eventually store IP address or whatever. You
need the EXTENSION privilege to modify this
value.
COUNT num read-only # of times user logged in.
LOGIN LOGIN DURATION num read-only Time that this box has/was logged in in
seconds.
LOGIN TIME num read-only Last login time. MAX BOXES num ancestor-read-write Maximum 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
Key Type Mutability Semantics
MAX MESSAGES num ancestor-read-write Maximum 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 LEN num ancestor-read-write Maximum length of message in seconds. You’ll
need the RECORD MSGS privilege to change
this value.
MENU0 CODE
. . . MENU9 CODE str read-write Code 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 SCRN bool read-write Announce 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 BOX bool read-only The last box to modify the settings on this box. MOD TIME num read-only Time that any settings in this box were last
changed.
MSG HI WATER num read-only Highest number of messages in box at one time. MSGS RECEIVED num read-only Count of the number of messages that have
ever been received by this box over all time. NEW FOLDER num read-write Folder number for new non-urgent messages.
(See also URGENT FOLDER.)
PARENT num read-only Parent box. Empty string for root box. PLAY FROM bool read-write Play back who the message was from (either
the box number or the name of the person). PLAY NEW FIRST bool read-write Play the first unheard message first else play
the messages in order, regardless of whether
they were heard or not. PLAY SKIP num read-write The 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 MSGS bool read-write Whether this box can record messages or not.
You need the RECORD MSGS privilege to
change this value. RNA CODE str read-write Code 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 RINGS num read-write Ring 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
Key Type Mutability Semantics
STAT EPOCH num read-only Time 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 FOLDER num read-write Folder number for new urgent messages. (See
also NEW FOLDER.) USER CODE str read-write Tcl 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. (This call 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:
errorCode Description
NOTNONNEG box BOXNOTEXIST box CTIMENOTSET box The 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:
1 MMO is forwardable. 0 MMO 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:
audio An audio MMO. fax A fax MMO. text A textual MMO. unknown The 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:
seconds For audio MMOs, the length returned is the number of seconds of audio. pages For fax MMOs, the length of the fax in pages. characters For 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:
errorCode Description
UNKNOWNTYPE The 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:
errorCode Description
UNKNOWNTYPE The 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
mulaw Mu-law. alaw A-law. wave Wave. Header specifies format. wave8 Wave file known to be 8-bit mulaw. wave16 Wave file known to be 16-bit linear. wavegsm Wave file known to contain audio in MS GSM format (GSM 6.10). gsm MS GSM format. g72x G.721 adpcm. oki-24 Oki 24K adpcm. oki-32 Oki 32K adpcm. sx9600 SX9600 (I-Link proprietary) compressed audio basic Sun proprietary Audio-Basic format (Mu-law) rht-24 “Rhetorex” proprietary 24K adpcm. rht-32 “Rhetorex” proprietary 32K adpcm. linear 16-bit PCM “Linear”.
Text type only.
html HyperText Markup Language. unicode Unicode. ascii Ascii.
Fax type only.
tiff Fax TIFF/F image.
Other
grammar Compiled SAPI speech recognition grammar.
Error codes:
UNKNOWNTYPE
* mmo func date
Description
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:
EMPTY The var has no content. NON PRINTABLE The var is not viewable. NO PRINTER Unable to get the default printer. NO PRINTER DATA Unable to get the printer information. INVALID PRINTER printer name is invalid.
69
msg 0 1 2
msg#35 folder 0 urgent unread non-deleted private mmos
msg#14 folder 3 normal read deleted public mmos
-
? ? ?
Figure 5.1: Internal Message Representation.
PRINT FAILURE Failed to print the var.
5.1 Messages and Folders
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 non­Amanda@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:
Key Value
urgent 1/0 read 1/0 deleted 1/0 subject subject 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.
count Number of MMOs in the message, excluding the subject. folder folder # private 1/0 relay phone Relay 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 list A list of box numbers and/or lists this message was addressed to. from The box number this message is from. If the message was sent by a
logged out interpreter, this entry is not set. forwarded by If the message was forwarded, who it was forwarded by. This will not be
set if the message wasn’t forwarded. receipt message read/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 time Date and time of message in # seconds since 1970 in GMT. msg number Message number. msg id This is an internally created number for the message that is unique within
the Amanda system. The system generates this value.
caller id Caller id for the message. If no caller id received, it is not set. alternative This 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.
Return values:
Empty string.
Error codes:
NOTNONNEG msg MSGNOTEXIST msg CMDNOTEXIST mmo func CMDNOTHANDLE mmo func MMOWRONGTYPE mmo func NOCURMSG
move msg folder [msg]. . .
Description
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.)
Return values:
Empty string.
74
Error codes:
NOTNONNEG folder |msg OUTOFRANGE folder MSGNOTEXIST msg
expunge
Description
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
46 {urgent 1 read 0 ...} 3 {urgent 0 read 1 deleted 0 ...} ...
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 speci­fying 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
Key Value
msgs Total # of messages in all folders. unread msgs Total # of unread messages. deleted msgs Total # of deleted messages. urgent msgs Total # of urgent messages. normal msgs Total # of normal messages. urgent unread msgs Total # of unread urgent messages. normal unread msgs Total # 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:
box or list *[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 ABC 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.
* send msg [-urgent] [-private] [-receipt] [-subject mmo func] [-relay number] [-mmos mmo func list]
[-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:
-urgent Mark the message as urgent.
-private Mark the message as private. The recipient will not be able to forward
the message or any of its contents to another user.
-receipt Send 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 subject Set the subject of the message to subject. Notice this is an MMO, so it
can be voice (or a fax!).
-relay number Set 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 list Send 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 time Send 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 folder File a copy of the message in the indicated folder.
-caller id number Set the caller id for the message. The caller id field in the message
will be set when get next msg is used.
-expire time Expire 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.
-absolute Expire 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.
78
Error codes:
INVRECIPIENT recipient INVALIDTIME time NOTNONNEG folder OUTOFRANGE folder CMDNOTEXIST mmo func CMDNOTHANDLE mmo func UNKNOWNTYPE mmo func MMONOTFORW mmo func
forward msg [-receipt] [-at time] [-urgent] [-private] [-subject subject] [-mmos mmo func list] [-fcc folder] [-expire time] [-expire absolute] msg [recipient]. . .
Description
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.
Return values:
See the send msg call for the return value.
Error codes:
INVRECIPIENT recipient INVALIDTIME time CMDNOTEXIST mmo func CMDNOTHANDLE mmo func UNKNOWNTYPE subject func MMONOTFORW subject func MSGNOTEXIST msg MSGNOTFORW msg
move msgs to box from box dest box
Description
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
Owner Key Desc Access MMO
3 grt1 . . . 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
MMOs Box Msg# Forwardability Other. . .
C
MMO Lookup Table).
Messages Table
5 1 forwardable
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 read­only.
Figure 5.2: Internal MMO database structure.
Return values:
Empty string.
Error codes:
NOTNONNEG box BOXNOTEXIST box PERMDENIED box
5.2 MMO 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 object­oriented 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 string The MMO handle was successfully filled in.
EMPTY The key was found, but the MMO handle was empty. In this case, var
is not set. NOTFOUND Key not found.
81
Error codes:
errorCode Description
NOTNONNEG box PERMDENIED box You 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:
description Description field. private Privateness access field box Owner mailbox field. key Key field.
Valid values for the private field:
world Anyone can access the MMO, even non-logged in users. logged in Any logged in mailbox can access the MMO. family Only ancestors, descendants, and owner can access the MMO. owner ancestors Only ancestors and owner can access the MMO. owner descendants Only descendants and owner can access the MMO. owner only Only the owner can access the MMO.
Return values:
a-list An a-list of the values found. NOTFOUND The key was not found.
Error codes:
NOTNONNEG box BOXNOTEXIST box PERMDENIED box FIELDNOTEXIST field INVALIDVALUE value BREQNLOGIN
82
errorCode
* lookup mmo keys [-box box ] [pattern]
Description
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
store mmo [-box box] [-mmo mmo func] [-description value] [-private value] key
Description
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.3 Announcements
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 list mmo 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:
id This is the id of the newly created announcement.
INVALIDTIMES The begin time and/or end time is invalid. PERMDENIED Not allowed to create an announcement in announcement box . BOXNOTEXIST The configuration parameter announcement box is invalid. BADMMOLIST The mmo list is invalid, p oss ible bad mmo vars.
Error codes:
errorCode Description
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 an­nouncement 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:
INVALIDID The id of the announcement to edit is invalid. INVALIDTIMES The begin time and/or end time is invalid. PERMDENIED Not allowed to edit an announcement. BOXNOTEXIST The configuration parameter announcement box is invalid. BADMMOLIST The mmo list is invalid, p oss ible bad mmo vars.
Error codes:
errorCode Description
CONFNOTEXIST
announcement box
85
owner delete announcement id
Description
The function deletes an existing announcement. The id states which announcement to delete.
Return values:
INVALIDID The id of the announcement to edit is invalid. PERMDENIED Not allowed to delete an announcement. BOXNOTEXIST The configuration parameter announcement box is invalid.
Error codes:
errorCode Description
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:
INVALIDID The 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
PERMDENIED Not 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 list A list of announcement ids
86
Error codes:
errorCode Description
CONFNOTEXIST
announcement box
INVALIDVALUE value
recip heard announcement id
Description
The function marks that this mailbox heard the announcement.
Return values:
INVALIDID The 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 list A 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 list A list of announcement ids
is announcement heard id
Description
The function marks that this mailbox heard the announcement.
87
Return values:
INVALIDID The id of the announcement is invalid. 0 The announcement has not been heard by the mailbox. 1 The announcement has been heard by the mailbox.
5.4 Published 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 publi­cation. 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 of common 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:
BOXNOTEXIST mailbox is invalid MMONOTFORWARDABLE mmo 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:
KEYNOTFOUND unique 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 list A 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.1 OOP 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.2 Interconnection 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.3 Make 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.4 Network 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.
Return values:
Empty string.
Error codes:
CMDNOTEXIST resource func CMDNOTHANDLE resource func HWRONGTYPE resource func
* ls dsp attach [-timeout milliseconds] var
Description
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. TIMEOUT Waited 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. TIMEOUT Waited 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. TIMEOUT Waited 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.5 VP 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.
NOOPINPROGRESS No 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.
NOOPINPROGRESS No 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.
NOOPINPROGRESS No 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...