Asterisk Install Notes

This document describes the steps that one can take to build an Asterisk telephone switch on standard PC hardware using CentOS as the operating system platform. This has the advantage over canned Asterisk installations, such as Trixbox, in that other applications can be run on the same system, providing there are excess resources available. The resulting switch can be configured with: two or three FXO ports available for connection to the standard, PSTN; a line card with 8-12 FXS ports for connection to POTS extension phones; a SIP server for support of remote VOIP connections; and IP phones also for use as extensions. A flexible, configurable SOHO switch is the result, which can be used in a home or small business.

Asterisk Documentation

There is plenty of Asterisk documentation available for the avid reader. You might want to begin by reading The Starfish Book, available from O'Reilly:

     http://downloads.oreilly.com/books/9780596510480.pdf

Or, in HTML form from the Asterisk Web site:

     http://www.asterisk.name/asterisk/0596009623/main.html

Hardware Selection

For a small switch, the demands on system resources are not large. As one reference notes, "If you have less than five simultaneous conversations going on at once, pretty much any hardware will work with Asterisk." The main concerns are reliability, power consumption, cooling and the number of PCI slots available for installation of line cards, etc.

If you have available an older AT motherboard with a 1GHz Intel processor that can accomodate 512MB to 1GB of memory, has a built-in NIC and possibly VGA support (although why a telephone switch needs a video display may be a question that warrants further thought -- see Going Headless, below), and has six PCI slots, you may have a winner. The number of possibilites are endless. These links will take you to several sites that address how to make the choice:

     http://www.asterisk.name/asterisk/0596009623/asterisk-chp-2-sect-1.html
     http://asteriskglobe.blogspot.com/2008/06/hardware-recommendations.html
     http://www.voip-info.org/wiki/view/Asterisk+hardware

These install notes assume that you'll be using a RAID controller, with two disks, in a mirrored array configuration. We like the 3Ware RAID controllers. The Escalade 7006-2 RAID controllers are readily available on the various auction sites for very little money (and there's nothing wrong with a couple of top-end IDE drives [e.g. 80GB] in this system).

Going Headless

If your telephone switch is going to sit in a dark room and shovel voice packets back and forth, you aren't going to be needing a keyboard, a mouse, and the latest 27" display along with an accelerated graphics processor card with 1GB of memory, an advanced pixel shader, etc., etc. In fact, you might not need to have any kind of keyboard, mouse or video display attached to it at all.

Administration of a headless machine is quite possible using telnet, SSH or maybe VNC. You'll probably need a keyboard, mouse and basic video/card plus display to set the machine up, since most install DVDs now days require all of these things but, once the OS is installed and configured, these devices can be removed. Here's a few things to consider.

You can get rid of X-Windows and the whole Gnome/Desktop thing by booting the system into Runlevel 3 instead of Runlevel 5 (the default). This can be done by hacking inittab and changing the Runlevel to 3.

/etc/inittab:

.

       .

# id:5:initdefault:
id:3:initdefault:

If you do need to attach a display and use X-Windows, you can simply log in to the login prompt that comes up on the console and then type "startx".

If you're going to be using VNC, you should install all of the packages that are necessary for a display. This includes a desktop (i.e. Gnome or KDE), the D-Bus components and possibly SELinux (although it is not strictly required, Gnome runs like crap without it). Don't forget to install the VNC package itself.

If you're going to be using telnet (which you should probably always include anyway, just in case), you need to install the xinetd daemon as well as the telnet server. Then, you'll need to enable telnet (which comes disabled, by default, even though you installed it specifically). I know this is jumping ahead, a bit, but you can easily enable it with your favorite text editor like this:

/etc/xinetd.d/telnet

     Change the "Disable Yes" parameter to "Disable No".

Then, restart xinetd:

     /etc/init.d/xinetd restart

Incidentally, if you're using an older display (that doesn't have EDID) and/or X-Windows doesn't recognize the display properly, it may load the wrong display parameters, once you've completed the install, and the display will stop working (usually after you reboot from the install). You can fix this by booting a CD-only OS (such as Knoppix) and mounting the new system. Then, hack the /etc/xinetd.d/telnet file, as shown above, to enable telnet. Once that is done, you can reboot the new system and you'll be able to log in to telnet, whereupon you'll be able to repair the broken X configuration.

Installing The Latest CentOS

Download the latest CentOS ISO image and burn an install DVD from:

     http://www.centos.org/

Boot the install disk and choose the "Customize Now" option when you see it in the install sequence. In all cases shown below, the package section is chosen (e.g. Applications, Development), then the major package is chosen, and then the individual/optional packages are selected/unselected. The following packages should be chosen:

  1. Pick one desktop (either Gnome or KDE - Gnome appears more popular).
     For Gnome, pick: NetworkManager-gnome; gnome-utils; evince;
       gnome-backgrounds; gnome-system-monitor; gnome-themes; hal-gnome.
     2) Applications.
     Do not select Authoring and Publishing.
     Pick your favorite Editor.
     If you selected EMACS under your favorite Editor, you don't need to
       pick it here.
     Do not select Engineering and Scientific.
     Remove Games.
     For Graphical Internet, only install FireFox.
     Remove Graphics.
     Remove Office/Productivity.
     Remove Sound and Video.
     Remove Text-based Internet.
     3) Development.
     Do not install anything.
     4) Servers.
     Do not install the DNS Name Server.
     Pick the FTP Server (but no optional packages).
     Pick the Legacy Network Server (pick: telnet; xinetd; rsh-server).
     Pick the Mail Server (pick: sendmail; sendmail-cf).
     Do not install the MySQL database.
     Pick Network Servers (then, install: vnc-server).
     Do not install the News Server.
     Do not install the PostgreSQL Database.
     Do not install any Printing Support.
     Do not install the Server Configuration Tools.
     Install the Web Server (but no optional packages).
     If you need Samba (trust us, you will), install the Windows File Server
       (but no optional packages).
     5) Base System.
     Install the Administration Tools (pick: pirut; system-config-date;
       system-config-keyboard; system-config-network; system-config-users).
     Install the Base (choose: telnet; yum-updatesd; anacron; conman; dos2unix;
       ftp; lftp; logwatch; man-pages; pam-*; rsh; rsync; sendmail; sudo;
       unzip).
     Install Dialup Networking Support (select: minicom; wvdial).
     Do not install Java.
     Do not install the Legacy Software Support.
     Do not install OpenFabrics Enterprise Distribution.
     Install System Tools (pick: lslk; ntp; samba-client; vnc;
       wireshark-gnome).
     Install the X Window System (go with all of the default packages).
     6) Do not install Virtualization.
     7) Do not install Clustering.
     8) Do not install Cluster Storage.
     9) Do not install the CentOS Extras.
     10) Languages (pick only the language you need).

Once you've selected (or deselected) all of the packages to install, continue through the subsequent steps of the install, making the following choices:

  1. Turn off the firewall. Your telephone switch should be behind a real firewall. If it isn't, you're probably going to be in trouble.
  2. Disable SELinux. This security elephant is a feature whose time has not come (although you might wish to read the section on Going Headless, above).
  3. Set the date/time. Timestamping of events will require accurate timekeeping.
  4. Create an additional userid (besides root). Since your switch might be run headless and you'll, therefore, want to be logging in via telnet or ssh to actually get to it, you'll need a non-root userid to log in. Remote logins directly to root are prohibited so you'll need to first log in with this userid and then su or sudo to root to do any useful work.
  5. Disable the sound card, if any. This ain't a multimedia machine.
  6. Install any additional CDs, if you have them. If you picked the 3Ware RAID controller (as noted above), it should not require any additional installation CD, since it is supported by CentOS directly.

After the installation has been completed and the new system rebooted, you can run the Package Manager post-startup to remove extra packages but its not especially worth it (except for Network Manager, see below), particularly if you chose all of the right stuff at install time.

Incidentally, now would be a good time to set the default Runlevel to 3, in /etc/inittab, if you're going headless.

Updating Packages On Headless Machines

If you built a headless switch, you can still apply the latest patches to the system, despite the fact that most system updating is done nowdays with one of the GUI package managers. Under the covers, yum is still the base upon which all of the GUI package managers are built, so it is always possible to use it to apply package updates.

From the command propmpt of your telnet connection to the switch try this:

     su
     yum clean all
     yum check-update

This should give you a list of all the packages which can be updated. If you see any packages that you like and want to install, you can do it like this:

     su
     yum update package1 [package2] [...]

Where "package1", etc., are the names of the packages from the check-update list. Otherwise, if you want to go for broke, you can install all of the updates that are currently available like this:

     su
     yum update

You may have to reboot after some packages are installed:

     su
     /sbin/shutdown -r now

Also, note that, if any of the packages that you apply update the kernel, installing them will probably break DAHDI. This is because DAHDI installs many of its modules in the kernel tree and kernel updates change the configuration of this tree. If your switch doesn't come up properly, you should look in the Asterisk log for messages that look like this:

/var/log/asterisk/messages:

     Unable to open '/dev/dahdi/channel': No such file or directory

If you see such a message, you should shut down Asterisk and DAHDI and then try starting DAHDI on its own:

     su
     /etc/init.d/asterisk stop
     /etc/init.d/dahdi stop
     /etc/init.d/dahdi start

Check if this produces missing module error messages, for example:

     Loading DAHDI hardware modules:
     FATAL: Module dahdi not found.
     wct4xxp:  FATAL: Module wct4xxp not found.                    [FAILED]
     wcte12xp:  FATAL: Module wcte12xp not found.                  [FAILED]
          .
          .
          .

This indicates that the DAHDI modules can no longer be found in the kernel tree. You can check this by doing the following:

     su
     find /lib/modules -name wct\*

You should see a list of DAHDI modules that looks something like this:

     /lib/modules/2.6.18-164.11.1.el5/dahdi/wctc4xxp
     /lib/modules/2.6.18-164.11.1.el5/dahdi/wctc4xxp/wctc4xxp.ko
     /lib/modules/2.6.18-164.11.1.el5/dahdi/wctdm24xxp
     /lib/modules/2.6.18-164.11.1.el5/dahdi/wctdm24xxp/wctdm24xxp.ko
          .
          .
          .
     /lib/modules/2.6.18-164.15.1.el5/dahdi/wctc4xxp
     /lib/modules/2.6.18-164.15.1.el5/dahdi/wctc4xxp/wctc4xxp.ko
     /lib/modules/2.6.18-164.15.1.el5/dahdi/wctdm24xxp
     /lib/modules/2.6.18-164.15.1.el5/dahdi/wctdm24xxp/wctdm24xxp.ko
          .
          .
          .

In this case, we have DAHDI modules installed in two kernel trees. But, we need to verify that the DAHDI modules are in the current kernel tree. To do this, use the "uname -r" command to see what the current kernel is. You will see something like this:

     2.6.18-194.11.1.el5

This is bad news, since the DAHDI modules don't appear in the current kernel tree. To remedy this, you can try the following copy command:

     su
     cp -p -R /lib/modules/2.6.18-164.15.1.el5/dahdi /lib/modules/`uname -r`/

After you're done, you should now see an improved modules list, when you enter the command "find /lib/modules -name wct\*":

.

       .

/lib/modules/2.6.18-194.11.1.el5/dahdi/wctc4xxp /lib/modules/2.6.18-194.11.1.el5/dahdi/wctc4xxp/wctc4xxp.ko /lib/modules/2.6.18-194.11.1.el5/dahdi/wctdm24xxp /lib/modules/2.6.18-194.11.1.el5/dahdi/wctdm24xxp/wctdm24xxp.ko

       .
       .
       .

If so, reboot the system and see if DAHDI/Asterisk comes up. If not, you can do a rebuild of DAHDI and then reinstall it to fix the problem:

     cd .../asterisk/dahdi-linux-complete*
     make all
     su
     make install

You should now have no problem rebooting and DAHDI/Asterisk will come up.

EMACS

If you are an EMACS user, probably the very first thing you will want to do is to configure EMACS not to leave turds lying around everywhere. This way, you can then edit files with impunity and not have to worry about cleaning up Stallman's mess everywhere you go:

~/.emacs:
/root/.emacs:

Add, at least:

     (custom-set-variables
     '(make-backup-files nil))
     (custom-set-faces)

Or, if you prefer, set the global default to prevent emacs from leaving turds lying around. To do this, hack the file default.el, which is usually installed in the directory /usr/share/emacs/site-lisp. If this directory doesn't exist, look for the directory in the library path that contains the EMACS lisp modlues (".el").

/usr/share/emacs/site-lisp/default.el:

Add, at least:

     (custom-set-variables
     '(make-backup-files nil))

Networking Your Way

It would seem that the fabulous little package for managing the network, called Network Manager, has made its way from those user-oriented versions of Linux, such as Ubuntu, over to CentOS. While this gem may be a good idea for someone's laptop (where the network address is acquired automagically by DHCP, and/or networking is done via multiple connections), its probably going to be a real pain in the butt if you want your machine to have a single, static IP address. Basically, this package is a piece of scrap that does not work. Sure, as we said, it will configure your network for use by DHCP but, if you want to change one of the interfaces to a static IP address, you'll waste a lot of time before you figure out what a piece of junk it is. Sure, it looks like it is configuring your interfaces the way you want them but in reality it is leading you down the garden path. After you carefully configure the interface just the way you want it, nothing happens. And, when you reboot, all your changes are gone and you're back to DHCP. Who needs this kind of bulls**t?

So, if you don't want to bother with all of the b.s. caused by trying to set up static IP addresses via the Network Manager, begin by uninstalling the Network Manager using the Package Manager (yeah, we know that we told you to install the Gnome Network Manager package, above, but now we're telling you to uninstall it). This is by far and away the easiest way to disable it. Do a search for "network-manager" and you should see the package and related packages in the list. Uninstall them.

By now, you've probably installed one or more ethernet cards, according to the directions in the ethernet HowTo, when you set up your hardware. If you are lucky, the system install will have set up the proper device driver(s) for the cards and they should be working OK. If not, hack the following:

/etc/modprobe.conf:

Alias the ethernet device name to the proper module for the card. If the card is an ISA card, you might need to set the options to use for that card. Here's an example of three cards:

     alias eth0 rtl8139                    # A Realtek PCI 10/100BaseT
     alias eth1 rtl8169                    # A Realtek PCI 10/100/1000BaseT
     alias eth2 e1000                      # An Intel Pro 1000

Then, from Gnome, run /usr/bin/netcfg (you should see this program under the menu tree "System/Network". This program will allow you to configure the machine's host and domain names, DNS lookup servers, /etc/hosts data, the network adapter information and the routing information. The following information should be added to the sections noted:

Names (or DNS)

     Hostname: mysys
     Domain: leave blank
     Search for hostnames in additional domains: leave blank
     Nameservers: DNS server1
                  DNS server2
                       .
                       .
                       .

Hosts

      IP                Name              Nickname
  127.0.0.1       localhost.homeworld     localhost
  192.168.1.1     jump-gate.homeworld     jump-gate    (packet shoveller)
  192.168.1.5     laboratory.homeworld    laboratory
  192.168.1.6     phones.homeworld        phones

Note that it is very important that the name and IP address of the local machine be set here, as well as localhost, because the Asterisk Voicemail application will try to send email using the local host name, which will not be resolved if it is missing from hosts. The send will fail, the email will go bouncing all over the place and it will be a real mess.

Interfaces

     Interface        IP         Proto  Atboot   Active
        lo         127.0.0.1     none    yes     active
       eth0       192.168.1.x    none    yes     active
       eth1       192.168.1.y    none    yes     active
         .
         .
         .

When setting up IP local addresses, you should use Netmask: 255.255.255.0.

Routing

     Default Gateway: 192.168.1.1

/etc/sysconfig/network-scripts/ifcfg-ethx:

If the above setup doesn't give you what you want or you need to hack the definition of your ethernet cards directly, you can look in /etc/sysconfig/network-scripts/ifcfg-ethx, (where "x" is your network adapter's number) for the NIC setup. Note that you must not use uppercase letters in the hexadecimal MAC address set by the HWADDR parameter. If you do, the brain dead code in /sbin/ifup and /sbin/ifdown will not work properly. Here's a few samples:

/etc/sysconfig/network-scripts/ifcfg-eth0 (regular NIC):

     USERCTL=no
     PEERDNS=no
     TYPE=Ethernet
     [IPV6INIT=no]        # Optional for systems that support IPV6
     DEVICE=eth0
     HWADDR=00:50:ba:52:5b:8d
     BOOTPROTO=none
     ONBOOT=yes
     IPADDR=192.168.1.6
     NETMASK=255.255.255.0
     BROADCAST=192.168.1.255

/etc/sysconfig/network-scripts/ifcfg-eth1 (gig-o-bit NIC):

     USERCTL=no
     PEERDNS=no
     TYPE=Ethernet
     [IPV6INIT=no]        # Optional for systems that support IPV6
     DEVICE=eth1
     HWADDR=00:1b:21:20:f3:e3
     BOOTPROTO=none
     ONBOOT=yes
     IPADDR=192.168.11.6
     NETMASK=255.255.255.0
     BROADCAST=192.168.11.255

/etc/resolv.conf:

You'll also need to put your DNS servers in the resolv.conf file so that they look something like this:

     nameserver 151.203.0.84
     nameserver 151.203.0.85
     nameserver 204.122.16.8
     nameserver 216.231.41.2

Once you're done, a reboot should prove that everything is working OK. Make sure that you can ping other systems on your local network, that hostnames are resolved properly (if you care), and that you can reach systems on the Internet (i.e. that DNS and routing are working). Verify that you can ping the system from elsewhere on your local network and that you can telnet into it from elsewhere.

As a final check, make sure the system boots without a keyboard and display and that you can still get to it via telnet. You should now be prepared to run headless.

Installing The Build Prerequisites

Begin the Asterisk install by making sure that the current versions of all of the installed packages are up to date. Even if you just installed the OS from the install disk, its a good idea to check this, since the install disk may be a bit out of date. If you don't do this, problems can arise. For example, if the latest version of the kernel is not installed and the kernel-devel package is installed you may get source code that doesn't match your current kernel. Asterisk will not build without the kernel and source code matching.

To check for updates for all of the installed packages, type:

     su
     yum -y update

When you've done, reboot the machine, just to make sure in case the update installs a new kernel.

Now, since we built a stripped down version of CentOS, we need to get all of the dependencies that are needed to build Asterisk. We could have chosen them when we installed the OS but this way is easier. Simply cut and paste the following command and you're in business:

     su
     yum -y install make gcc gcc-c++ kernel-devel bison openssl-devel \
       libtermcap-devel ncurses-devel doxygen curl-devel newt-devel \
       mlocate lynx tar wget nmap bzip2 mod_ssl crontabs vixie-cron \
       speex speex-devel unixODBC unixODBC-devel libtool-ltdl \
       libtool-ltdl-devel mysql-connector-odbc mysql mysql-devel \
       mysql-server php-mysql php-mbstring php-mcrypt flex screen \
       libxml2 libxml2-devel libtiff libtiff-devel

Allowing Inbound FTP

CentOS uses VSFTP to provide FTP services to its clients. If you'd like to allow inbound FTP so that other systems can FTP files to your Asterisk system, follow the steps in this section. However, before you do, consider carefully the implications of allowing inbound FTP on your telephone switch. If phone service is critical, you should evaluate the convenience provided against the potential risk of an outside system getting at the Asterisk system's files. If your Asterisk system is critical, be sure you lock FTP down, disable it after you've set up the box, or perhaps not install it at all.

Begin by editing the VSFTP daemon's configuration file to set up FTP. The installation's default configuration file can be used as a base. The most common change to it is to set the timeout to some value longer than five minutes (e.g. "idle_session_timeout=7200" gives two hours). On the other hand, sticking with five minutes on the Asterisk system may be a good idea, to minimize the chances of an unattended FTP session being used for a crime of opportunity by a passer by.

/etc/vsftpd/vsftpd.conf:

     #
     # The default compiled in settings are fairly paranoid. This sample file
     # loosens things up a bit, to make the ftp daemon more usable.
     # Please see ftp vsftpd.conf.5 for all compiled in defaults.
     #
     # Allow anonymous FTP? (Beware - allowed by default if you comment
     # this out).
     anonymous_enable=NO
     #
     # Uncomment this to allow local users to log in.
     local_enable=YES
     #
     # Uncomment this to enable any form of FTP write command.
     write_enable=YES
     #
     # Default umask for local users is 077. You may wish to change this to 022,
     # if your users expect that (022 is used by most other ftpd's)
     local_umask=022
     #
     # Uncomment this to allow the anonymous FTP user to upload files. This only
     # has an effect if the above global write enable is activated. Also, you will
     # obviously need to create a directory writable by the FTP user.
     #anon_upload_enable=YES
     #
     # Uncomment this if you want the anonymous FTP user to be able to create
     # new directories.
     #anon_mkdir_write_enable=YES
     #
     # Activate directory messages - messages given to remote users when they
     # go into a certain directory.
     dirmessage_enable=YES
     #
     # Activate logging of uploads/downloads.
     xferlog_enable=YES
     #
     # Make sure PORT transfer connections originate from port 20 (ftp-data).
     connect_from_port_20=YES
     #
     # If you want, you can arrange for uploaded anonymous files to be owned by
     # a different user. Note! Using "root" for uploaded files is not
     # recommended!
     #chown_uploads=YES
     #chown_username=whoever
     #
     # The name of log file when xferlog_enable=YES and/or xferlog_std_format=YES
     # are set.  You may override where the log file goes if you like.
     # WARNING - changing this filename affects /etc/logrotate.d/vsftpd.log
     #xferlog_file=/var/log/vsftpd.log
     #
     # Switches between logging into vsftpd_log_file and xferlog_file files.
     # NO writes to vsftpd_log_file, YES to xferlog_file
     xferlog_std_format=YES
     #
     # You may change the default value for timing out an idle session.
     idle_session_timeout=600
     #
     # You may change the default value for timing out a data connection.
     #data_connection_timeout=120
     #
     # It is recommended that you define on your system a unique user which the
     # ftp server can use as a totally isolated and unprivileged user.
     #nopriv_user=ftpsecure
     #
     # Enable this and the server will recognise asynchronous ABOR requests. Not
     # recommended for security (the code is non-trivial). Not enabling it,
     # however, may confuse older FTP clients.
     #async_abor_enable=YES
     #
     # By default the server will pretend to allow ASCII mode but in fact ignore
     # the request. Turn on the below options to have the server actually do ASCII
     # mangling on files when in ASCII mode.
     # Beware that on some FTP servers, ASCII support allows a denial of service
     # attack (DoS) via the command "SIZE /big/file" in ASCII mode. vsftpd
     # predicted this attack and has always been safe, reporting the size of the
     # raw file.
     # ASCII mangling is a horrible feature of the protocol.
     #ascii_upload_enable=YES
     #ascii_download_enable=YES
     #
     # You may fully customise the login banner string:
     #ftpd_banner=Welcome to blah FTP service.
     #
     # You may specify a file of disallowed anonymous e-mail addresses. Apparently
     # useful for combatting certain DoS attacks.
     #deny_email_enable=YES
     # (default follows)
     #banned_email_file=/etc/vsftpd.banned_emails
     #
     # You may specify an explicit list of local users to chroot() to their home
     # directory. If chroot_local_user is YES, then this list becomes a list of
     # users to NOT chroot().
     #chroot_list_enable=YES
     # (default follows)
     #chroot_list_file=/etc/vsftpd.chroot_list
     #
     # You may activate the "-R" option to the builtin ls. This is disabled by
     # default to avoid remote users being able to cause excessive I/O on large
     # sites. However, some broken FTP clients such as "ncftp" and "mirror" assume
     # the presence of the "-R" option, so there is a strong case for enabling it.
     #ls_recurse_enable=YES
     #
     # When "listen" directive is enabled, vsftpd runs in standalone mode and
     # listens on IPv4 sockets. This directive cannot be used in conjunction
     # with the listen_ipv6 directive.
     listen=YES
     #
     # This directive enables listening on IPv6 sockets. To listen on IPv4 and
     # IPv6 sockets, you must run two copies of vsftpd whith two configuration
     # files.  Make sure, that one of the listen options is commented !!
     #listen_ipv6=YES
     pam_service_name=vsftpd
     userlist_enable=YES
     tcp_wrappers=YES

You will need to enable the VSFTP service:

     su
     /sbin/chkconfig vsftpd on

Then, you can start the service manually (or reboot):

     su
     /etc/init.d/vsftpd start

Setting Up Samba

While not strictly necessary for the operation of Asterisk, you may want to consider setting up Samba on your Asterisk system. With it, you can map the system's file system to another machine, allowing you to copy/paste files to it with drag and drop from any number of GUIs. This will be way better than using FTP to copy all of the files to/from the Asterisk system. On the other hand, as with inbound FTP, you should evaluate the convenience provided vs. the risk involved with allowing outside systems to get at the Asterisk system's file system. If your Asterisk system is critical, you may want to disable Samba after you've set up the box or maybe not install it at all.

The following URL has good notes about setting up Samba. Might want to read them, if you run into trouble:

     http://home.nyc.rr.com/computertaijutsu/samba.html

Note that, if you make any changes to Samba, you may have to reboot some or all of your Windows workstations (Windows caches SMBs and this can lead to changes not taking effect). You should always restart the smbd and nmbd processes.

.../etc/hosts & .../etc/lmhosts:

Add the Samba server name to these two files on all of the Windows boxes. On most Windows systems, .../etc/hosts is in /WINNT/system32/drivers/etc or /WINDOWS/system32/drivers/etc. So is lmhosts. For example:

     192.168.1.1          mysys
     192.168.1.1          jump_gate      # <==== Exact name used for server
          .
          .
          .
     #BEGIN_ALTERNATE
     #INCLUDE     C:\WINNT\system32\drivers\etc\lmhosts.npt
     #END_ALTERNATE

It is very important that you do this and that the name added to the hosts file matches the name the Samba server is advertising under exactly.

/etc/hosts:

If you wish to refer to the workstations by name on the Samba server (e.g. if you will be using Samba client), add the Windows workstation or other Samba server names to /etc/hosts on the Asterisk box. For example:

     127.0.0.1       localhost.homeworld     localhost
     192.168.1.1     jump-gate.homeworld     jump-gate
     192.168.1.2     gabriella.homeworld     gabriella  # <==== workstation
     192.168.1.3     clara-bow.homeworld     clara-bow  # <==== names
     192.168.1.3     clara_bow.homeworld     clara_bow  # <====

/etc/samba/lmhosts:

If you will be using Samba client, add the Win NT/98/2000/XP workstation names to /etc/samba/lmhosts on the Asterisk box. For example:

     127.0.0.1       localhost
     192.168.1.1     jump-gate
     192.168.1.1     jump_gate
     192.168.1.2     gabriella           # <==== Workstation names
     192.168.1.3     clara-bow           # <==== Note that Unix uses '-'
     192.168.1.3     clara_bow           # <==== Note that Winduhs uses '_'

The lmhosts file is basically a copy of /etc/hosts so you can start by copying it to samba/lmhosts and then just edit it down, if you already have the workstations defined in /etc/hosts:

     cp /etc/hosts /etc/samba/lmhosts

The permissions should look like:

     -rw-r--r--     root     root

/etc/samba/smbusers:

Set up equivalences between Windows login names and login names on the Asterisk server in this file. For example:

     # Unix_name = SMB_name1 SMB_name2 ...
     root = administrator admin
     nobody = guest pcguest smbguest
     joeblow = joe

pdbedit

Add passwords (by hand) using pdbedit:

     su
     pdbedit -a -u username

for each user that you wish to add. Note that the username is the local userid, not the Windows userid (which is mapped to a local userid by /etc/samba/smbusers, above).

/etc/samba/smb.conf:

Configure Samba by hacking /etc/samba/smb.conf. Pay attention to the following (especially the "interfaces" IP addresses, which should be set to your machine's IP address plus 127.0.0.1):

     workgroup = WORKGROUP
     # netbios name = MRSERVER  <== Set this only if you don't want to use the
                                    machine's name from /etc/sysconfig/network
     comment = mysys
     server string = Samba %v Server
     hosts allow = 192.168.1. 127.
     interfaces = 192.168.1.1/24 127.0.0.1    <==== Machine's IP address here
     passdb backend = tdbsam
     unix password sync = no
     remote browse sync = 192.168.1.255
     local master = yes
     domain master = yes
     [homes]
     [Root]

You can copy the sample config file from the build directory tree:

     cp ../examples/smb.conf.default /etc/samba/smb.conf

Or, if you'd like, here is a sample of a complete config file:

     # This is the main Samba configuration file. You should read the
     # smb.conf(5) manual page in order to understand the options listed
     # here. Samba has a huge number of configurable options (perhaps too
     # many!) most of which are not shown in this example
     #
     # Any line which starts with a ; (semi-colon) or a # (hash)
     # is a comment and is ignored. In this example we will use a #
     # for commentry and a ; for parts of the config file that you
     # may wish to enable
     #
     # NOTE: Whenever you modify this file you should run the command "testparm"
     # to check that you have not many any basic syntactic errors.
     #
     #======================= Global Settings ===================================
     [global]
     # workgroup = NT-Domain-Name or Workgroup-Name
         workgroup = WORKGROUP
         comment = mysys
     # server string is the equivalent of the NT Description field
         server string = Samba %v Server
     # This option is important for security. It allows you to restrict
     # connections to machines which are on your local network. The
     # following example restricts access to two C class networks and
     # the "loopback" interface. For more examples of the syntax see
     # the smb.conf(5) man page
         hosts allow = 192.168.1. 127.
     # if you want to automatically load your printer list rather
     # than setting them up individually then you'll need this
     ;    printcap name = /etc/printcap
     ;    load printers = yes
     # It should not be necessary to spell out the print system type unless
     # yours is non-standard. Currently supported print systems include:
     # cups, bsd, sysv, plp, lprng, aix, hpux, qnx
     ;   printing = cups
     # Uncomment this if you want a guest account, you must add this to
     # /etc/passwd otherwise the user "nobody" is used
     ;   guest account = pcguest
     # this tells Samba to use a separate log file for each machine
     # that connects
         log file = /var/log/samba/log.%m
     # Have no cap on log file size or put a cap on the size of the log
     # files (in Kb).
     ;   max log size = 0
         max log size = 50
     # Security mode. Most people will want user level security. See
     # security_level.txt for details.
         security = user
     # Use password server option only with security = server
     ;   password server = <NT-Server-Name>
     # Backend to store user information in. New installations should
     # use either tdbsam or ldapsam. smbpasswd is available for backwards
     # compatibility. tdbsam requires no further configuration.
         passdb backend = tdbsam
     ;   passdb backend = smbpasswd
     # Disallow access to accounts that have null passwords.
         null passwords = no
     # You may wish to use password encryption. Please read
     # ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation.
     # Do not enable this option unless you have read those documents
     ;   encrypt passwords = yes
     ;   smb passwd file = /etc/samba/smbpasswd
     # The following are needed to allow password changing from Windows to
     # update the Linux sytsem password also.
     # NOTE: Use these with 'encrypt passwords' and 'smb passwd file' above.
     # NOTE2: You do NOT need these to allow workstations to change only
     #        the encrypted SMB passwords. They allow the Unix password
     #        to be kept in sync with the SMB password.
         unix password sync = no
     ;   passwd program = /usr/bin/passwd %u
     ;   passwd chat = NewUNIXpassword %n\n ReTypenewUNIXpassword* %n\n \
                       passwd:allauthenticationtokensupdatedsuccessfully*
     # Unix users can map to different SMB User names
         username map = /etc/samba/smbusers
     # Using the following line enables you to customise your configuration
     # on a per machine basis. The %m gets replaced with the netbios name
     # of the machine that is connecting
     ;   include = /etc/samba/smb.conf.%m
     # Most people will find that this option gives better performance.
     # See speed.txt and the manual pages for details
     ;   socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
     # Configure Samba to use multiple interfaces
     # If you have multiple network interfaces then you must list them
     # here. See the man page for details.
     ;   interfaces = 192.168.1.1/24
         interfaces = 192.168.1.1/24 127.0.0.1
         bind interfaces only = True
     # Configure remote browse list synchronisation here
     #  request announcement to, or browse list sync from:
     #     a specific host or from / to a whole subnet (see below)
     ;   remote browse sync = 192.168.3.25 192.168.5.255
         remote browse sync = 192.168.1.255
     # Cause this host to announce itself to local subnets here
     ;   remote announce = 192.168.1.255 192.168.2.44
     # Browser Control Options:
     # set local master to no if you don't want Samba to become a master
     # browser on your network. Otherwise the normal election rules apply
         local master = no
     ;   local master = yes
     # OS Level determines the precedence of this server in master browser
     # elections. The default value should be reasonable
     ;   os level = 33
     # Domain Master specifies Samba to be the Domain Master Browser. This
     # allows Samba to collate browse lists between subnets. Don't use this
     # if you already have a Windows NT domain controller doing this job
     ;   domain master = yes
     # Preferred Master causes Samba to force a local browser election on
     # startup and gives it a slightly higher chance of winning the election
     ;   preferred master = yes
     # Use only if you have an NT server on your network that has been
     # configured at install time to be a primary domain controller.
     ;   domain controller = <NT-Domain-Controller-SMBName>
     # Enable this if you want Samba to be a domain logon server for
     # Windows95 workstations.
     ;   domain logons = yes
     # if you enable domain logons then you may want a per-machine or
     # per user logon script
     # run a specific logon batch file per workstation (machine)
     ;   logon script = %m.bat
     # run a specific logon batch file per username
     ;   logon script = %U.bat
     # Where to store roving profiles (only for Win95 and WinNT)
     #        %L substitutes for this servers netbios name, %U is username
     #        You must uncomment the [Profiles] share below
     ;   logon path = \\%L\Profiles\%U
     # All NetBIOS names must be resolved to IP Addresses
     # 'Name Resolve Order' allows the named resolution mechanism to be specified
     # the default order is "host lmhosts wins bcast". "host" means use the unix
     # system gethostbyname() function call that will use either /etc/hosts OR
     # DNS or NIS depending on the settings of /etc/host.config,
     # /etc/nsswitch.conf and the /etc/resolv.conf file. "host" therefore is
     # system configuration dependant. This parameter is most often of use to
     # prevent DNS lookups in order to resolve NetBIOS names to IP Addresses.
     # Use with care! The example below excludes use of name resolution for
     # machines that are NOT on the local network segment
     # - OR - are not deliberately to be known via lmhosts or via WINS.
     ; name resolve order = wins lmhosts bcast
     # Windows Internet Name Serving Support Section:
     # WINS Support - Tells the NMBD component of Samba to enable it's WINS Server
         wins support = no
     # WINS Server - Tells the NMBD components of Samba to be a WINS Client
     #     Note: Samba can be either a WINS Server, or a WINS Client, but NOT both
     ;   wins server = w.x.y.z
     # WINS Proxy - Tells Samba to answer name resolution queries on
     # behalf of a non WINS capable client, for this to work there must be
     # at least one     WINS Server on the network. The default is NO.
     ;   wins proxy = yes
     # DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names
     # via DNS nslookups. The built-in default for versions 1.9.17 is yes,
     # this has been changed in version 1.9.18 to no.
         dns proxy = no
      map to guest = never
      dead time = 0
      debug level = 0
     # Case Preservation can be handy - system default is no
     # NOTE: These can be set on a per share basis
     ;  preserve case = no
     ;  short preserve case = no
     # Default case is normally upper case for all DOS files
     ;  default case = lower
     # Be very careful with case sensitivity - it can break things!
     ;  case sensitive = no
     #============================ Share Definitions ============================
     [homes]
         comment = Home Directory
         browseable = no
         writable = yes
     [Root]
         comment = Root Directory
         path = /
         public = yes
         browseable = yes
         writeable = yes
         write list = @joeblow

You'll probably need to turn on the Samba service so that it starts at system startup and then start it by hand (or reboot):

     su
     /sbin/chkconfig smb on
     /etc/init.d/smb start

Obtaining The Asterisk Source

Obtain a copy of all of the latest Asterisk sources from Digium. The sources are in:

     http://downloads.digium.com/pub

We generally always get these:

     asterisk/asterisk-vernum-current.tar.gz
     asterisk/asterisk-addons-vernum-current.tar.gz
     telephony/dahdi-linux-complete/dahdi-linux-complete-current.tar.gz

Optionally, you may want to get the PRI library (e.g. if you'll be setting up a T1 connection):

     libpri/libpri-vernum-current.tar.gz

And, if you want SpanDSP (a library of many DSP functions for telephony that range from simple modules, such as DTMF detection, to a complete software FAX machine), you can find it here:

     http://soft-switch.org/downloads/spandsp/spandsp-0.0.6pre17.tgz

The current version of SpanDSP, in wide use, is 0.0.6pre17. But, SpanDSP is under continuous development and, although many of the functional modules in SpanDSP, like the soft FAX machine, have been providing good service to many people for a long time, subtle issues affecting small numbers of people are constantly being addressed. So, make sure you get the latest version of SpanDSP from the source mentioned.

SpanDSP is used by various popular applications packages, for example:

Building SpanDSP

If you downloaded SpanDSP (to use it for DTMF detection or a software FAX machine, for example), you should build it now, before you build Asterisk.

However, before compiling SpanDSP, beware that a number of Linux and other software distributions include SpanDSP, but they usually supply older versions of the library, which lack a lot of the features of the current version. Therefore, before installing SpanDSP, make sure there aren't any older versions already installed. Try:

     su
     whereis spandsp.so

This command will usually show you all the versions of SpanDSP in the machine's library search path. If there are SpanDSP files in directories other than the one where you intend to install the new SpanDSP, you should deal with those.

You will need libtiff installed on your machine. There have been several bugs related to FAX document handling in some older versions of libtiff, so check which version you have. Versions 3.5.7, 3.6.0, 3.7.1 and 3.8.2 seem to work OK. Some people have had trouble building SpanDSP, because they had more than one version of libtiff on their machine, installed in different directories. Try using the whereis command to find libtiff.so and then list the link given to see which version it points to:

     whereis libtiff.so
     [returns /usr/lib/libtiff.so]
     ls -l /usr/lib/libtiff*

You will also need to install libtiff-devel in order to build SpanDSP. The latest version of libtiff-devel, along with the latest version of libtiff.so, are both installed by the yum command shown in the Installing The Build Prerequisites section, above, so you should be all set.

You can build the SpanDSP library with the usual:

     cd .../asterisk
     tar -xvzf spandsp.tgz
     cd spandsp
     ./configure
     make
     su
     make install

However, if you use configure in this way, the SpanDSP library will be installed in /usr/local. In this case make sure your /etc/ld.so.conf.d directory has an entry for SpanDSP that points to /usr/local/lib:

     su
     cat > /etc/ld.so.conf.d/spandsp.conf << EOF
     /usr/local
     EOF
     ldconfig

We prefer to install the SpanDSP library in /usr by building it like this:

     cd .../asterisk
     tar -xvzf spandsp.tgz
     cd spandsp
     ./configure --prefix=/usr
     make
     su
     make install

Since some systems will happily build and install the SpanDSP library in a directory that isn't even included in the machine's library search path, as a final check that telephony applications will be able to find SpanDSP, try:

     whereis libspandsp.so

Make sure that the library is shown in the directory where you expect it to be.

Building libpri

The PRI library (libpri) is optional and only needed if you'll be using any of the E1 cards with one or more basic rate lines such as an E1 or T1. If you do need libpri, compile it now, before you build DAHDI or Asterisk, like this:

     cd .../asterisk
     tar -xvzf libpri.tar.gz
     cd libpri
     make clean
     make
     su
     make install

Building The OSLEC Echo Canceller

Many people have had good success with the OSLEC echo canceller. Its performance is claimed to be much better than some of the other echo cancellers that are available.

Unfortunately, this echo canceller is not yet included in the DAHDI build. In order to use it, you will have to go get the source yourself and then enable the "experimental" lines in the build rules that cause OSLEC to be built. You can try it, if you want, by following these instructions. But, we haven't been able to get the echo canceller built via this build process to install properly so it is probably best to skip ahead to Building The OSLEC Patch, below.

The source for OSLEC is actually found in the kernel build sources (2.6.28 and later), since this echo canceller is now part of the kernel. So, in order to get the source, you need to fetch the kernel source for (at least) 2.6.28. Take the first three sets of digits (before the dash) of the kernel that you wish to fetch and plug them into these commands:

     cd .../asterisk
     wget http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.28.tar.bz2
     tar -xvjf linux-2.6.28.tar.bz2

At this point, you can't get there from here. You need to install the source from the Linux kernel into the DAHDI source tree. But, the DAHDI source tree hasn't been built yet. So, we'll do things out of order here and extract the DAHDI source (you can skip this step in the next section):

     tar -xvzf dahdi-linux-complete*.tar.gz

Now, we can make a directory in the DAHDI source tree to hold the OSLEC echo canceller, copy the kernel source and then remove the kernel source tree (we don't really need it):

     cd dahdi-linux-complete*
     mkdir linux/drivers/staging
     cp -Rf ../linux-2.6.28/drivers/staging/echo linux/drivers/staging
     cd ..
     rm -rf linux-2.6.28

Now, edit drivers/dahdi/Kbuild and uncomment the two lines related to OSLEC. When DAHDI is built (in the Building DAHDI section), the OSLEC echo canceller should be built too.

Building The OSLEC Patch

Due to our lack of success, at this time, with the frakackta build process for the OSLEC module, we don't recommend that you build it as described in the Building The OSLEC Echo Canceller section, above. Instead, we recommend that you look at our Tech Note about how to build a working copy of OSLEC for DAHDI from our patched code, found at:

     http://www.bsmdevelopment.com/Reference/Tech_20100001.html

At least, if you use the source therein and follow the instructions to build it, you will get a working dahdi_echocan_oslec.ko that can be loaded by DAHDI and used for echo cancellation on its lines.

At some point, OSLEC should get rolled into the kernel and then, maybe DAHDI will take advantage of it and the whole issue will become moot.

Building DAHDI

We'll start the actual Asterisk build by installing DAHDI, since you're probably going to need DAHDI for the line cards that are used with Asterisk. DAHDI supports all of the Digium cards, as well as the Digium clone cards such as OpenVox. And, even if you don't need DAHDI for the hardware that it supports then at least build it for the dummy timer that it supplies.

Incidentally, if you are using any of the Digium clone cards (e.g. OpenVox), you may need to obtain additional source modules for the cards' drivers and insert them into the DAHDI build tree before proceeding with the build. See the appropriate section below, or the manufacturer's install notes, before running the build.

Also, if you need additional help, a fairly complete tutorial on DAHDI can be found at this URL:

     http://www.cadvision.com/blanchas/Asterisk/TestingDahdiHW.html

Build DAHDI like this:

     cd .../asterisk
     tar -xvzf dahdi-linux-complete.tar.gz
     cd dahdi-linux-complete
     [be sure you've installed any optional, additional echo cancellers]
     [be sure you've installed any third party drivers, per their instructions]
     make all
     su
     make install
     make config

The last step should install the DAHDI start script, which will cause it to be started by init when the system is booted. You should reboot the system and see that DAHDI does start automatically.

If it doesn't, you can try the following:

     su
     chkconfig --add dahdi
     chkconfig dahdi on
     service dahdi start

If the service starts OK now, try rebooting again to make sure that DAHDI comes up at boot time. You may also want to check /var/log/messages to see that DAHDI finds your installed line cards, when it comes up.

OpenVox DAHDI Drivers

The OpenVox A800P/A1200P series of line cards is supported via DAHDI under Asterisk. This support is provided by single device driver file named opvxa1200.c, which can be downloaded from:

     downloads.openvox.cn/pub/drivers/dahdi-patches/a800p_a1200p/opvxa1200.c

Begin by installing the hardware into the system. Note that, if you are using multiple cards, and your cards have clock line support (e.g. on the A1200P the connectors at J914 and J915 are present), you may wish to connect all of the cards' clocks together so that they all march to the same drummer and minimize jitter (which can be bad for FAX lines). See this link for more information:

     http://bbs.openvox.cn/viewthread.php?tid=874&extra=page%3D1

You can check that the hardware is present with lspci:

     /sbin/lspci

Which should give results like this:

.

       .
  02:0b.0 Network controller: Tiger Jet Network Inc. Tiger3XX Modem/ISDN interface
       .
       .
       .

If you see one or more Tiger Jet chip-based device, the line card is present.

Next, copy opvxa1200.c to the build directory:

     cp opvxa1200.c .../asterisk/dahdi-linux-complete*/linux/drivers/dahdi

Note that there appears to be a timing-related bug in initialization, whereby messages like these may be found in /var/log/messages for some A1200P FXS channels:

     Timeout waiting for calibration of module 1
     Timeout waiting for calibration of module 1
     Proslic Failed on Second Attempt to Auto Calibrate
     Init ProSlic with Manual Calibration
     Proslic Failed on Second Attempt to Calibrate Manually. \
       (Try -DNO_CALIBRATION in Makefile)

You won't be able to tell if this is happening until you've built the module for the first time but, if you do see such messages (initialization of the module may vary randomly and the messages may appear or disappear from time to time), the following patch to opvxa1200.c has been found to work for us (yes, we submitted it to OpenVox). You can come back and apply it later, if you find you have the problem, or you can just apply it now.

.../asterisk/dahdi-linux-complete*/linux/drivers/dahdi/opvxa1200.c:

     --- opvxa1200.c.orig 2010-06-24 08:29:01.000000000 -0400
     +++ opvxa1200.c  2010-07-05 22:05:21.000000000 -0400
     @@ -1699,7 +1699,7 @@
 
     }
     #if 1

-static int wctdm_proslic_calibrate(struct wctdm *wc, int card) +static int wctdm_proslic_calibrate(struct wctdm wc, int card, int delay)

     {
       unsigned long origjiffies;
       int x;
    @@ -1712,7 +1712,7 @@
       / Wait for it to finish */
       origjiffies = jiffies;
       while(wctdm_getreg(wc, card, 96)) {

Basically, the idea behind this patch is that the original code tried to auto calibrate the module using a preset time delay (i.e. to wait for calibration to settle). If it fails the first time, auto calibration is tried again with the same time delay. Hey, that doesn't make much sense. Apply this patch and the second attempt is given a delay that's twice as long. Works quite repeatably for us. Simply apply the patch before you build DAHDI and you're in business.

Another problem that we've noticed on A1200P FXS channels is that pulse dial telephones or devices do not work. This appears to be due to the hookswitch debounce value being set too high by default. The default value is 64 but we change it to 32 with this patch:

.../asterisk/dahdi-linux-complete*/linux/drivers/dahdi/opvxa1200.c:

     --- opvxa1200.c.orig 2010-07-05 23:36:31.000000000 -0400
     +++ opvxa1200.c  2012-01-07 18:41:49.000000000 -0500
     @@ -395,6 +395,10 @@
      static unsigned int battalarm;
      static unsigned int battthresh;
      static int ringdebounce = DEFAULT_RING_DEBOUNCE;
     +/* The dialdebounce parameter must be a multiple of 4ms so this value is
     + * actually multiplied by 4.  The original value was 64 but this prevents
     + * pulse dial phones from working so we use 32, which works well. /
     +static int dialdebounce = 32;
      static int fwringdetect = 0;
      static int debug = 0;
      static int robust = 0;
     @@ -1295,16 +1299,19 @@
        res = wc->reg0shadow[card];
        hook = (res & 1);
        if (hook != wc->mod[card].fxs.lastrxhook) {
     -    / Reset the debounce (must be multiple of 4ms) */
     -    wc->mod[card].fxs.debounce = 8 * (4  8);
     +    / Reset the debounce (must be a multiple of 4ms so we
     +     * multiply the parameter setting by 4) */
     +    wc->mod[card].fxs.debounce = dialdebounce * 4;
      #if 0
     -    printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce);
     +    printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n",
     +           card, hook, wc->mod[card].fxs.debounce);
      #endif
        } else {
          if (wc->mod[card].fxs.debounce > 0) {
     -      wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE;
     +      wc->mod[card].fxs.debounce -= 16 * DAHDI_CHUNKSIZE;
      #if 0
     -      printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce);
     +      printk(KERN_DEBUG "Sustaining hook %d, %d\n",
     +             hook, wc->mod[card].fxs.debounce);
      #endif
            if (!wc->mod[card].fxs.debounce) {
      #if 0
     @@ -2996,6 +3003,7 @@
      module_param(battthresh, uint, 0600);
      module_param(battalarm, uint, 0600);
      module_param(ringdebounce, int, 0600);
     +module_param(dialdebounce, int, 0600);
      module_param(fwringdetect, int, 0600);
      module_param(alawoverride, int, 0600);
      module_param(fastpickup, int, 0600);

This patch adds a parameter "dialdebounce" that can be set in the dahdi.conf file so that it will be passed to the opvxa1200.ko module when it is loaded via modprobe:

/etc/modprobe.d/dahdi.conf:

.

       .
  options opvxa1200 dialdebounce=32
       .
       .
       .

However, it also uses 32 for its default so you will probably not need to set the value at all. Testing with several pulse dialing devices has shown that setting the debounce value to 32 allows them all to work. As above, simply apply the patch before you build DAHDI and you're in business.

If you wish to apply both patches (there doesn't appear to be any harm in doing so), here's the patch that applies them both at the same time:

     --- opvxa1200.c.orig 2010-07-05 23:36:31.000000000 -0400
     +++ opvxa1200.c  2012-01-07 18:41:49.000000000 -0500
     @@ -395,6 +395,10 @@
      static unsigned int battalarm;
      static unsigned int battthresh;
      static int ringdebounce = DEFAULT_RING_DEBOUNCE;
     +/* The dialdebounce parameter must be a multiple of 4ms so this value is
     + * actually multiplied by 4.  The original value was 64 but this prevents
     + * pulse dial phones from working so we use 32, which works well. /
     +static int dialdebounce = 32;
      static int fwringdetect = 0;
      static int debug = 0;
      static int robust = 0;
     @@ -1295,16 +1299,19 @@
        res = wc->reg0shadow[card];
        hook = (res & 1);
        if (hook != wc->mod[card].fxs.lastrxhook) {
     -    / Reset the debounce (must be multiple of 4ms) */
     -    wc->mod[card].fxs.debounce = 8 * (4  8);
     +    / Reset the debounce (must be a multiple of 4ms so we
     +     * multiply the parameter setting by 4) */
     +    wc->mod[card].fxs.debounce = dialdebounce * 4;
      #if 0
     -    printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n", card, hook, wc->mod[card].fxs.debounce);
     +    printk(KERN_DEBUG "Resetting debounce card %d hook %d, %d\n",
     +           card, hook, wc->mod[card].fxs.debounce);
      #endif
        } else {
          if (wc->mod[card].fxs.debounce > 0) {
     -      wc->mod[card].fxs.debounce-= 16 * DAHDI_CHUNKSIZE;
     +      wc->mod[card].fxs.debounce -= 16 * DAHDI_CHUNKSIZE;
      #if 0
     -      printk(KERN_DEBUG "Sustaining hook %d, %d\n", hook, wc->mod[card].fxs.debounce);
     +      printk(KERN_DEBUG "Sustaining hook %d, %d\n",
     +             hook, wc->mod[card].fxs.debounce);
      #endif
            if (!wc->mod[card].fxs.debounce) {
      #if 0
     @@ -1699,7 +1706,7 @@
 
     }
     #if 1

-static int wctdm_proslic_calibrate(struct wctdm *wc, int card) +static int wctdm_proslic_calibrate(struct wctdm wc, int card, int delay)

     {
       unsigned long origjiffies;
       int x;
    @@ -1712,7 +1719,7 @@
       / Wait for it to finish */
       origjiffies = jiffies;
       while(wctdm_getreg(wc, card, 96)) {

And, speaking of building DAHDI, using your favorite text editor, edit Kbuild in the linux/drivers/dahdi directory. Find the line:

     obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM)             += wctdm.o

Add a new line after that line:

     obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM)             += opvxa1200.o

.../asterisk/dahdi-linux-complete*/linux/drivers/dahdi/Kbuild:

.

       .
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP)           += wct4xxp/
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP)          += wctc4xxp/
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP)        += wctdm24xxp/
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP)          += wcte12xp/
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM)             += wctdm.o
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM)             += opvxa1200.o
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_VOICEBUS)          += voicebus/
  obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCB4XXP)           += wcb4xxp/
       .
       .
       .

Now, go back to the section on Building DAHDI and build it as shown.

Building Asterisk

Now that we've got all of the prerequisites taken care of, we can build Asterisk itself:

     cd .../asterisk
     tar -xvzf asterisk-1.6.tar.gz
     cd asterisk-1.6
     ./configure

Choose which options to install (audio files, voicemail storage, codecs etc.). Basically, all of the default options are good but you might want to leave out some things (since pretty much everything is included by default). Do this with:

     make menuselect

Once you've chosen all of the options, build and install Asterisk like this:

     make
     su
     make install

Then, install the sample configuration files in /etc/asterisk and the documentation in docs/manpages. Also, build a default configuration for the currently-installed hardware:

     su
     make samples
     make progdocs
     make config

You can check that Asterisk starts properly:

     asterisk -vvvvvvvvvvvvvvvvvvvc
     core stop now

The "make config" step should install the Asterisk start script, which will cause it to be started by init when the system is booted. You should reboot the system and see that Asterisk does start automatically.

If it doesn't, you can try the following:

     su
     chkconfig --add asterisk
     chkconfig asterisk on
     service asterisk start

If Asterisk starts OK now, try rebooting again to make sure that Asterisk comes up at boot time. You may also want to check /var/log/asterisk/messages to see that Asterisk starts, when the system boots up.

And, speaking of system log messages, the Asterisk install fails to set up logrotate for the Asterisk log files so you might want to do that now, by adding a file to the logrotate include directory that looks something like the one shown here.

/etc/logrotate.d/asterisk:

     /var/log/asterisk/*_log {
         missingok
         notifempty
     }
     /var/log/asterisk/messages {
         missingok
         notifempty
     }

Basic Asterisk Configuration

Once Asterisk is up and running, you should check the configuration files that were built by install to see that they are generally copacetic. Here are some things that you should look for.

At the top of /etc/asterisk/indications.conf, under the "[general]" section, check that the country is correct for the switch's location.

     country = us

Edit /etc/asterisk/modules.conf and uncomment the lines that preload the ODBC modules:

     preload => res_odbc.so
     preload => res_config_odbc.so

Also, add these lines to the end of the file to stop automatic loading of the modules in question:

     ;
     ; Local customization: modules not loaded.
     ;
     noload => pbx_ael.so
     noload => codec_dahdi.so

We can get rid of the AEL (Asterisk Extension Language) config file because nobody uses this dubious feature:

     su
     mv /etc/asterisk/extensions.ael /etc/asterisk/extensions.ael.orig

or, if you don't think you'll ever need it:

     su
     rm -f /etc/asterisk/extensions.ael

Also, we can get rid of the demo file because we're going straight to a working system (yeah, right):

     rm /etc/asterisk/extensions.lua

The extensions.conf file defines the dial plan used by Asterisk and can include a complete description of all the extensions used by the system. However, we prefer to keep it generalized and put all of the special definitions of local lines and extensions into separate files in a "local" subdirectory off of the main "asterisk" directory. To do this, create the "local" directory:

     su
     mkdir /etc/asterisk/local
     chown root:root /etc/asterisk/local
     chmod u=rwx,go=rx /etc/asterisk/local

Local configuration information can be put in one or more files in the "local" subdirectory and included in extensions.conf like this:

     #include local/variables.conf
     #include local/ivr-menutree.conf
     #include local/plan-features.conf
     #include local/plan-outsidelines.conf
     #include local/dahdi-extensions.conf
     #include local/dahdi-colines.conf

Now, we can change the extensions.conf configuration file so that it is generalized and all extensions work more like the way we want them to. Make the changes to the general section, shown here:

     [general]
          .
          .
          .
     writeprotect=yes
     autofallthrough=yes
     clearglobalvars=yes

Then, get rid of all the sections (e.g. dundi and iaxtel) that you don't need, along with all of the example contexts that aren't required. Keep the stdexten context, add the recordivr macro and include all of the local configuration files. Get rid of the demo.

Add Do Not Disturb, Call Forward and Busy Call Forward checking to the stdexten and stdPrivacyexten functions so that the caller will be sent straight to voicemail, if the user sets Do Not Disturb on their extension (see the Adding Special Features To The Dialplan section, below), or forwarded to another extension (either directly or as the result of BUSY or NOANSWER).

Remove the inclusion of the parking lot context. We'll take care of that in one of our local files.

Here is our complete, tuned up file:

/etc/asterisk/extensions.conf:

     ; extensions.conf - the Asterisk dial plan
     ;
     ; Static extension configuration file, used by
     ; the pbx_config module. This is where you configure all your 
     ; inbound and outbound calls in Asterisk. 
     ; 
     ; This configuration file is reloaded 
     ; - With the "dialplan reload" command in the CLI
     ; - With the "reload" command (that reloads everything) in the CLI
     ;
     ; The "General" category is for certain variables.  
     ;
     [general]
     ;
     ; If static is set to no, or omitted, then the pbx_config will rewrite
     ; this file when extensions are modified.  Remember that all comments
     ; made in the file will be lost when that happens. 
     ;
     ; XXX Not yet implemented XXX
     ;
     static=yes
     ;
     ; if static=yes and writeprotect=no, you can save dialplan by
     ; CLI command "dialplan save" too
     ;
     ;writeprotect=no
     writeprotect=yes
     ;
     ; If autofallthrough is set, then if an extension runs out of
     ; things to do, it will terminate the call with BUSY, CONGESTION
     ; or HANGUP depending on Asterisk's best guess. This is the default.
     ;
     ; If autofallthrough is not set, then if an extension runs out of 
     ; things to do, Asterisk will wait for a new extension to be dialed 
     ; (this is the original behavior of Asterisk 1.0 and earlier).
     ;
     ;autofallthrough=no
     autofallthrough=yes
     ;
     ;
     ;
     ; If extenpatternmatchnew is set (true, yes, etc), then a new algorithm that
     ; uses a Trie to find the best matching pattern is used. In dialplans
     ; with more than about 20-40 extensions in a single context, this
     ; new algorithm can provide a noticeable speedup. 
     ; With 50 extensions, the speedup is 1.32x
     ; with 88 extensions, the speedup is 2.23x
     ; with 138 extensions, the speedup is 3.44x
     ; with 238 extensions, the speedup is 5.8x
     ; with 438 extensions, the speedup is 10.4x
     ; With 1000 extensions, the speedup is ~25x
     ; with 10,000 extensions, the speedup is 374x
     ; Basically, the new algorithm provides a flat response 
     ; time, no matter the number of extensions.
     ;
     ; By default, the old pattern matcher is used. 
     ;
     ; ****This is a new feature! *********************
     ; The new pattern matcher is for the brave, the bold, and 
     ; the desperate. If you have large dialplans (more than about 50 extensions
     ; in a context), and/or high call volume, you might consider setting 
     ; this value to "yes" !!
     ; Please, if you try this out, and are forced to return to the
     ; old pattern matcher, please report your reasons in a bug report
     ; on bugs.digium.com. We have made good progress in providing something
     ; compatible with the old matcher; help us finish the job!
     ;
     ; This value can be switched at runtime using the cli command "dialplan set
     ; extenpatternmatchnew true" or "dialplan set extenpatternmatchnew false",
     ; so you can experiment to your hearts content.
     ;
     ;extenpatternmatchnew=no
     ;
     ; If clearglobalvars is set, global variables will be cleared 
     ; and reparsed on a dialplan reload, or Asterisk reload.
     ;
     ; If clearglobalvars is not set, then global variables will persist
     ; through reloads, and even if deleted from the extensions.conf or
     ; one of its included files, will remain set to the previous value.
     ;
     ; NOTE: A complication sets in, if you put your global variables into
     ; the AEL file, instead of the extensions.conf file. With clearglobalvars
     ; set, a "reload" will often leave the globals vars cleared, because it
     ; is not unusual to have extensions.conf (which will have no globals)
     ; load after the extensions.ael file (where the global vars are stored).
     ; So, with "reload" in this particular situation, first the AEL file will
     ; clear and then set all the global vars, then, later, when the
     ; extensions.conf file is loaded, the global vars are all cleared, and then
     ; not set, because they are not stored in the extensions.conf file.
     ;
     ;clearglobalvars=no
     clearglobalvars=yes
     ;
     ; If priorityjumping is set to 'yes', then applications that support
     ; 'jumping' to a different priority based on the result of their operations
     ; will do so (this is backwards compatible behavior with pre-1.2 releases
     ; of Asterisk). Individual applications can also be requested to do this
     ; by passing a 'j' option in their arguments.
     ;
     ;priorityjumping=yes
     ;
     ; User context is where entries from users.conf are registered.  The
     ; default value is 'default'
     ;
     ;userscontext=default
     ;
     ; You can include other config files, use the #include command
     ; (without the ';'). Note that this is different from the "include" command
     ; that includes contexts within other contexts. The include command works
     ; in all asterisk configuration files.
     ;include "filename.conf"
     ;include <filename.conf>
     ;include filename.conf
     ;
     ; You can execute a program or script that produces config files, and they
     ; will be inserted where you insert the #exec command. The exec command 
     ; works on all asterisk configuration files.  However, you will need to
     ; activate them within asterisk.conf with the "execincludes" option.  They
     ; are otherwise considered a security risk.
     ;exec /opt/bin/build-extra-contexts.sh
     ;exec /opt/bin/build-extra-contexts.sh --foo="bar"
     ;exec </opt/bin/build-extra-contexts.sh --foo="bar">
     ;#exec "/opt/bin/build-extra-contexts.sh --foo=\"bar\""
     ;
     ; The "Globals" category contains global variables that can be referenced
     ; in the dialplan with the GLOBAL dialplan function:
     ; ${GLOBAL(VARIABLE)}
     ; ${${GLOBAL(VARIABLE)}} or ${text${GLOBAL(VARIABLE)}} or any hybrid
     ; Unix/Linux environmental variables can be reached with the ENV dialplan
     ; function: ${ENV(VARIABLE)}
     ;
     [globals]
     CONSOLE=Console/dsp                          ; Console interface for demo
     ;CONSOLE=DAHDI/1
     ;CONSOLE=Phone/phone0
     IAXINFO=guest                                ; IAXtel username/password
     ;IAXINFO=myuser:mypass
     TRUNK=DAHDI/G2                               ; Trunk interface
     ;
     ; Note the 'G2' in the TRUNK variable above. It specifies which group (defined
     ; in chan_dahdi.conf) to dial, i.e. group 2, and how to choose a channel to
     ; use in the specified group. The four possible options are:
     ;
     ; g: select the lowest-numbered non-busy DAHDI channel
     ;    (aka. ascending sequential hunt group).
     ; G: select the highest-numbered non-busy DAHDI channel
     ;    (aka. descending sequential hunt group).
     ; r: use a round-robin search, starting at the next highest channel than last
     ;    time (aka. ascending rotary hunt group).
     ; R: use a round-robin search, starting at the next lowest channel than last
     ;    time (aka. descending rotary hunt group).
     ;
     TRUNKMSD=1                                   ; MSD digits to strip (usually
                                                  ; 1 or 0)
     ;TRUNK=IAX2/user:pass@provider
     ;FREENUMDOMAIN=mydomain.com                  ; domain to send on outbound
                                                  ; freenum calls (uses
                                                  ; outbound-freenum context)
     ;
     ; Include the local Variables.
     ;
     #include local/variables.conf
     ;
     ; WARNING WARNING WARNING WARNING
     ; If you load any other extension configuration engine, such as pbx_ael.so,
     ; your global variables may be overridden by that file.  Please take care to
     ; use only one location to set global variables, and you will likely save
     ; yourself a ton of grief.
     ; WARNING WARNING WARNING WARNING
     ;
     ; Any category other than "General" and "Globals" represent 
     ; extension contexts, which are collections of extensions.  
     ;
     ; Extension names may be numbers, letters, or combinations
     ; thereof. If an extension name is prefixed by a '_'
     ; character, it is interpreted as a pattern rather than a
     ; literal.  In patterns, some characters have special meanings:
     ;
     ;   X - any digit from 0-9
     ;   Z - any digit from 1-9
     ;   N - any digit from 2-9
     ;   [1235-9] - any digit in the brackets (in this example, 1,2,3,5,6,7,8,9)
     ;   . - wildcard, matches anything remaining (e.g. _9011. matches 
     ;        anything starting with 9011 excluding 9011 itself)
     ;   ! - wildcard, causes the matching process to complete as soon as
     ;       it can unambiguously determine that no other matches are possible
     ;
     ; For example, the extension _NXXXXXX would match normal 7 digit dialings, 
     ; while _1NXXNXXXXXX would represent an area code plus phone number
     ; preceded by a one.
     ;
     ; Each step of an extension is ordered by priority, which must always start
     ; with 1 to be considered a valid extension.  The priority "next" or "n" means
     ; the previous priority plus one, regardless of whether the previous priority
     ; was associated with the current extension or not.  The priority "same" or
     ; "s" means the same as the previously specified priority, again regardless of
     ; whether the previous entry was for the same extension.  Priorities may be
     ; immediately followed by a plus sign and another integer to add that amount
     ; (most useful with 's' or 'n').  Priorities may then also have an alias, or
     ; label, in parentheses after their name which can be used in goto situations.
     ;
     ; Contexts contain several lines, one for each step of each extension.  One
     ; may include another context in the current one as well, optionally with a
     ; date and time.  Included contexts are included in the order they are
     ; listed. Switches may also be included within a context.  The order of
     ; matching within a context is always exact extensions, pattern match
     ; extensions, includes, and switches.  Includes are always processed
     ; depth-first.  So for example, if you would like a switch "A" to match before
     ; context "B", simply put switch "A" in an included context "C", where "C" is
     ; included in your original context before "B".
     ;
     ; [context]
     ; exten => someexten,{priority|label{+|-}offset}[(alias)],\
     ;          application(arg1,arg2,...)
     ;
     ; Timing list for includes is 
     ;
     ;   <time range>,<days of week>,<days of month>,<months>[,<timezone>]
     ;
     ; Note that ranges may be specified to wrap around the ends.  Also, minutes
     ; are fine-grained only down to the closest even minute.
     ;
     ; include => daytime,9:00-17:00,mon-fri,*,
     ; include => weekend,,sat-sun,*,
     ; include => weeknights,17:02-8:58,mon-fri,,*
     ;
     ; ignorepat can be used to instruct drivers to not cancel dialtone upon
     ; receipt of a particular pattern.  The most commonly used example is of
     ; course '9' like this:
     ;
     ;ignorepat => 9
     ;
     ; so that dialtone remains even after dialing a 9.  Please note that ignorepat
     ; only works with channels which receive dialtone from the PBX, such as DAHDI,
     ; Phone, and VPB.  Other channels, such as SIP and MGCP, which generate their
     ; own dialtone and converse with the PBX only after a number is complete, are
     ; generally unaffected by ignorepat (unless DISA or another method is used to
     ; generate a dialtone after answering the channel).
     ;
     ;
     ; Include the local IVR menu.
     ;
     #include local/ivr-menutree.conf
     ;
     ; Include the special feature codes.
     ;
     #include local/plan-features.conf
     ;
     ; Include the dial plan for outside lines.
     ;
     #include local/plan-outsidelines.conf
     ;
     ; Include the dial plan for virtual extensions.
     ;
     #include local/plan-virtualexts.conf
     ;
     ; Include the local extensions and CO lines.
     ;
     #include local/dahdi-extensions.conf
     #include local/dahdi-colines.conf
     ;
     ; You can use an alternative switch type as well, to resolve
     ; extensions that are not known here, for example with remote 
     ; IAX switching you transparently get access to the remote
     ; Asterisk PBX
     ; 
     ; switch => IAX2/user:password@bigserver/local
     ;
     ; An "lswitch" is like a switch but is literal, in that
     ; variable substitution is not performed at load time
     ; but is passed to the switch directly (presumably to
     ; be substituted in the switch routine itself)
     ;
     ; lswitch => Loopback/12${EXTEN}@othercontext
     ;
     ; An "eswitch" is like a switch but the evaluation of
     ; variable substitution is performed at runtime before
     ; being passed to the switch routine.
     ;
     ; eswitch => IAX2/context@${CURSERVER}
     ;
     ; recordivr:
     ;
     ;   Macro to record a phrase as a ".wav" file in the /tmp directory, where
     ;   it can be saved for use in an IVR menu.
     ;
     ;   This uses the pls-rcrd-name-at-tone sound from the Asterisk extra sounds
     ;   in asterisk-extra-sounds-en-wav-current.tar.gz.  Its not the right prompt
     ;   but it is "a" prompt.  You could record a different prompt, using this
     ;   macro and then use that prompt for future recordings.
     ;
     ;   The result is put in /tmp/asterisk-recording.wav.  You can copy it
     ;   wherever you wish.
     ;
     [macro-recordivr]
     exten => s,1,Playback(vm-rec-temp)
     exten => s,n,Record(/tmp/asterisk-recording.wav)
     exten => s,n,Wait(2)
     exten => s,n,Playback(/tmp/asterisk-recording)
     exten => s,n,wait(2)
     exten => s,n,Hangup
     ;
     ; Standard extension subroutine:
     ;   ${EXTEN} - Extension
     ;   ${ARG1} - Device(s) to ring
     ;   ${ARG2} - Optional context in Voicemail (if empty, then "default")
     ;
     ; Note that the current version will drop through to the next priority in the
     ; case of their pressing '#'.  This gives more flexibility in what do to next:
     ; you can prompt for a new extension, or drop the call, or send them to a
     ; general delivery mailbox, or...
     ;
     ; The use of the LOCAL() function is purely for convenience.  Any variable
     ; initially declared as LOCAL() will disappear when the innermost Gosub
     ; context in which it was declared returns.  Note also that you can declare
     ; a LOCAL() variable on top of an existing variable, and its value will
     ; revert to its previous value (before being declared as LOCAL()) upon
     ; Return.
     ;
     [stdexten]
     exten => _X.,50000(stdexten),NoOp(Start stdexten)
     exten => _X.,n,Set(LOCAL(ext)=${EXTEN})
     exten => _X.,n,Set(LOCAL(dev)=${ARG1})
     exten => _X.,n,Set(LOCAL(cntx)=${ARG2})
     exten => _X.,n,Set(LOCAL(mbx)="${ext}"$["${cntx}" ? "@${cntx}" :: ""])
     ; If we were given a mailbox to use, on failure, by level 0 in the call
     ; forward recursion, use it instead.  This way, the caller ends up in
     ; the original mailbox, if the forwarded extension is DND, Busy or No
     ; Answer.
     exten => _X.,n,GotoIf($[${EXISTS(${mbxonfailure})}]?:checkfwd)
     exten => _X.,n,Set(LOCAL(mbx)=${mbxonfailure})
     ; First, check if forwarding is set on this extension.  If it is, go
     ; straight to the forwarded number without checking anything else.
     ;
     ; Note that this involves jumping back to the from-internal context to
     ; ring the forwarded extension.  However, it is possible that the
     ; forwarded extension is forwarded back to us, which will cause Asterisk
     ; to loop forever.  That being the case, we don't do the forward if the
     ; forwarded extension is also forwarded.  Its a rudimentary check but
     ; it works.  Incidentally, we do the check here because it may be
     ; possible for the users to sneak a loop by the UI.  Since we would
     ; actually loop here, this is the logical last-minute check.
     exten => _X.,n(checkfwd),Set(LOCAL(fwd)=${DB(Feature/FWD${ext})})
     exten => _X.,n,Set(LOCAL(bfwd)=${DB(Feature/BFWD${ext})})
     exten => _X.,n,GotoIf($[${ISNULL(${fwd})}]?checkdnd:)
     exten => _X.,n,Set(LOCAL(fwdloop)=${DB(Feature/FWD${fwd})})
     exten => _X.,n,GotoIf($[${ISNULL(${fwdloop})}]?:checkdnd)
     exten => _X.,n,GotoIf($[${EXISTS(${exitonfailure})}]?exitfwd:)
     exten => _X.,n,Set(mbxonfailure=${mbx})
     exten => _X.,n,Goto(from-internal,${fwd},1)  ; Recursion (1 level)
     exten => _X.,n(exitfwd),return()
     ; Check if DND is set on this extension.  Straight to Voicemail, if it is.
     exten => _X.,n(checkdnd),Set(LOCAL(dnd)=${DB(Feature/DND${ext})})
     exten => _X.,n,GotoIf($[${ISNULL(${dnd})}]?dialext:)
     exten => _X.,n,GotoIf($[${EXISTS(${exitonfailure})}]?exitfwd:)
     exten => _X.,n,Voicemail(${mbx},u)
     ; Dial the extension.
     exten => _X.,n(dialext),Dial(${dev},20,t)    ; Ring the interface, 20 seconds
                                                  ; maximum
     exten => _X.,n,Goto(stdexten-${DIALSTATUS},1)
                                                  ; Jump based on status
                                                  ; (NOANSWER,BUSY,CHANUNAVAIL,
                                                  ;   CONGESTION,ANSWER)
     exten => stdexten-NOANSWER,1,GotoIf($[${EXISTS(${exitonfailure})}]?exitnoans:)
     exten => stdexten-NOANSWER,n,GotoIf($[${ISNULL(${bfwd})}]?vmnoans:)
     exten => stdexten-NOANSWER,n,Set(LOCAL(bfwdloop)=${DB(Feature/BFWD${bfwd})})
     exten => stdexten-NOANSWER,n,GotoIf($[${ISNULL(${bfwdloop})}]?:vmnoans)
     exten => stdexten-NOANSWER,n,Set(mbxonfailure=${mbx})
     exten => stdexten-NOANSWER,n,Goto(from-internal,${bfwd},1)
                                                  ; Recursion (1 level)
     exten => stdexten-NOANSWER,n(vmnoans),Voicemail(${mbx},u)
                                                  ; If unavailable, send to
                                                  ; voicemail w/ unavail announce
     exten => stdexten-NOANSWER,n,NoOp(Finish stdexten NOANSWER)
     exten => stdexten-NOANSWER,n(exitnoans),Return()
                                                  ; If they press #, return to start
     exten => stdexten-BUSY,1,GotoIf($[${EXISTS(${exitonfailure})}]?exitbusy:)
     exten => stdexten-BUSY,n,GotoIf($[${ISNULL(${bfwd})}]?vmbusy:)
     exten => stdexten-BUSY,n,Set(LOCAL(bfwdloop)=${DB(Feature/BFWD${bfwd})})
     exten => stdexten-BUSY,n,GotoIf($[${ISNULL(${bfwdloop})}]?:vmbusy)
     exten => stdexten-BUSY,n,Set(mbxonfailure=${mbx})
     exten => stdexten-BUSY,n,Goto(from-internal,${bfwd},1)
                                                  ; Recursion (1 level)
     exten => stdexten-BUSY,n(vmbusy),Voicemail(${mbx},b)
                                                  ; If busy, send to voicemail w/
                                                  ; busy announce
     exten => stdexten-BUSY,n,NoOp(Finish stdexten BUSY)
     exten => stdexten-BUSY,n(exitbusy),Return()  ; If they press #, return to start
     exten => _stde[x]te[n]-.,1,Goto(stdexten-NOANSWER,1)
                                                  ; Treat anything else as no answer
     exten => a,1,VoicemailMain(${mbx})           ; If they press *, send the user
                                                  ; into VoicemailMain
     exten => a,n,Return()
     ;
     ; Standard privacy extension subroutine:
     ;   ${ARG1} - Extension
     ;   ${ARG2} - Device(s) to ring
     ;   ${ARG3} - Optional DONTCALL context name to jump to (assumes the s,1
     ;             extension-priority)
     ;   ${ARG4} - Optional TORTURE context name to jump to (assumes the s,1
     ;             extension-priority)`
     ;   ${ARG5} - Context in voicemail (if empty, then "default")
     ;
     ; See above note in stdexten about priority handling on exit.
     ;
     [stdPrivacyexten]
     exten => _X.,60000(stdPrivacyexten),NoOp(Start stdPrivacyexten)
     exten => _X.,n,Set(LOCAL(ext)=${ARG1})
     exten => _X.,n,Set(LOCAL(dev)=${ARG2})
     exten => _X.,n,Set(LOCAL(dontcntx)=${ARG3})
     exten => _X.,n,Set(LOCAL(tortcntx)=${ARG4})
     exten => _X.,n,Set(LOCAL(cntx)=${ARG5})
     exten => _X.,n,Set(LOCAL(mbx)="${ext}"$["${cntx}" ? "@${cntx}" :: ""])
     ; If we were given a mailbox to use, on failure, by level 0 in the call
     ; forward recursion, use it instead.  This way, the caller ends up in
     ; the original mailbox, if the forwarded extension is DND, Busy or No
     ; Answer.
     exten => _X.,n,GotoIf($[${EXISTS(${mbxonfailure})}]?:pvcheckfwd)
     exten => _X.,n,Set(LOCAL(mbx)=${mbxonfailure})
     ; First, check if forwarding is set on this extension.  If it is, go
     ; straight to the forwarded number without checking anything else.
     ;
     ; Note that this involves jumping back to the from-internal context to
     ; ring the forwarded extension.  However, it is possible that the
     ; forwarded extension is forwarded back to us, which will cause Asterisk
     ; to loop forever.  That being the case, we don't do the forward if the
     ; forwarded extension is also forwarded.  Its a rudimentary check but
     ; it works.  Incidentally, we do the check here because it may be
     ; possible for the users to sneak a loop by the UI.  Since we would
     ; actually loop here, this is the logical last-minute check.
     exten => _X.,n(pvcheckfwd),Set(LOCAL(fwd)=${DB(Feature/FWD${ext})})
     exten => _X.,n,Set(LOCAL(bfwd)=${DB(Feature/BFWD${ext})})
     exten => _X.,n,GotoIf($[${ISNULL(${fwd})}]?pvcheckdnd:)
     exten => _X.,n,Set(LOCAL(fwdloop)=${DB(Feature/FWD${fwd})})
     exten => _X.,n,GotoIf($[${ISNULL(${fwdloop})}]?:pvcheckdnd)
     exten => _X.,n,GotoIf($[${EXISTS(${exitonfailure})}]?pvexitfwd:)
     exten => _X.,n,Set(mbxonfailure=${mbx})
     exten => _X.,n,Goto(from-internal,${fwd},1)  ; Recursion (1 level)
     exten => _X.,n(pvexitfwd),return()
     ; Check if DND is set on this extension.  Straight to Voicemail, if it is.
     exten => _X.,n(pvcheckdnd),Set(LOCAL(dnd)=${DB(Feature/DND${ext})})
     exten => _X.,n,GotoIf($[${ISNULL(${dnd})}]?pvdialext:)
     exten => _X.,n,GotoIf($[${EXISTS(${exitonfailure})}]?pvexitfwd:)
     exten => _X.,n,Voicemail(${mbx},u)
     ; Dial the extension.
     exten => _X.,n(pvdialext),Dial(${dev},20,pt) ; Ring the interface, 20 seconds
                                                  ; maximum, call screening option
                                                  ; (or use P for databased call
                                                  ; _X.creening)
     exten => _X.,n,Goto(stdexten-${DIALSTATUS},1)
                                                  ; Jump based on status
                                                  ; (NOANSWER,BUSY,CHANUNAVAIL,
                                                  ; CONGESTION,ANSWER)
     exten => stdexten-NOANSWER,1,GotoIf($[${EXISTS(${exitonfailure})}]?pvexitnoans:)
     exten => stdexten-NOANSWER,n,GotoIf($[${ISNULL(${bfwd})}]?pvvmnoans:)
     exten => stdexten-NOANSWER,n,Set(LOCAL(bfwdloop)=${DB(Feature/BFWD${bfwd})})
     exten => stdexten-NOANSWER,n,GotoIf($[${ISNULL(${bfwdloop})}]?:pvvmnoans)
     exten => stdexten-NOANSWER,n,Set(mbxonfailure=${mbx})
     exten => stdexten-NOANSWER,n,Goto(from-internal,${bfwd},1)
                                                  ; Recursion (1 level)
     exten => stdexten-NOANSWER,n(pvvmnoans),Voicemail(${mbx},u)
                                                  ; If unavailable, send to
                                                  ; voicemail w/ unavail announce
     exten => stdexten-NOANSWER,n,NoOp(Finish stdPrivacyexten NOANSWER)
     exten => stdexten-NOANSWER,n(pvexitnoans),Return()
                                                  ; If they press #, return to start
     exten => stdexten-BUSY,1,GotoIf($[${EXISTS(${exitonfailure})}]?pvexitbusy:)
     exten => stdexten-BUSY,n,GotoIf($[${ISNULL(${bfwd})}]?pvvmbusy:)
     exten => stdexten-BUSY,n,Set(LOCAL(bfwdloop)=${DB(Feature/BFWD${bfwd})})
     exten => stdexten-BUSY,n,GotoIf($[${ISNULL(${bfwdloop})}]?:pvvmbusy)
     exten => stdexten-BUSY,n,Set(mbxonfailure=${mbx})
     exten => stdexten-BUSY,n,Goto(from-internal,${bfwd},1)
                                                  ; Recursion (1 level)
     exten => stdexten-BUSY,n(pvvmbusy),Voicemail(${mbx},b)
                                                  ; If busy, send to voicemail w/
                                                  ; busy announce                                               ; busy announce
     exten => stdexten-BUSY,n,NoOp(Finish stdPrivacyexten BUSY)
     exten => stdexten-BUSY,n(pvexitbusy),Return()
                                                  ; If they press #, return to start
     exten => stdexten-DONTCALL,1,Goto(${dontcntx},s,1)
                                                  ; Callee chose to send this call
                                                  ; to a polite "Don't call again"
                                                  ; script.
     exten => stdexten-TORTURE,1,Goto(${tortcntx},s,1)
                                                  ; Callee chose to send this call
                                                  ; to a telemarketer torture
                                                  ; script.
     exten => _stde[x]te[n]-.,1,Goto(stdexten-NOANSWER,1)
                                                  ; Treat anything else as no answer
     exten => a,1,VoicemailMain(${mbx})           ; If they press *, send the user
                                                  ; into VoicemailMain
     exten => a,n,Return
     ;
     ; Paging macro:
     ;
     ;       Check to see if SIP device is in use and DO NOT PAGE if they are
     ;
     ;   ${ARG1} - Device to page
     ;
     [macro-page];
     exten => s,1,ChanIsAvail(${ARG1},s)          ; s is for ANY call
     exten => s,n,GoToIf([${AVAILORIGCHAN} = ""]?fail:autoanswer)
     exten => s,n(autoanswer),Set(ALERTINFO="RA")
                                                  ; This is for the PolyComs
     exten => s,n,SIPAddHeader(Call-Info: Answer-After=0)
                                                  ; This is for the Grandstream,
                                                  ; Snoms, and Others
     exten => s,n,NoOp()                          ; Add others here and Post on
                                                  ; the Wiki!!!!
     exten => s,n,Dial(${ARG1})
     exten => s,n(fail),Hangup
     ;
     ; The page context calls up the page macro that sets variables needed for
     ; auto-answer.  It is in is own context to make calling it from the Page()
     ; application as simple as Local/{peername}@page
     ;
     [page]
     exten => _X.,1,Macro(page,SIP/${EXTEN})
     ;If you want to subscribe to the status of a parking space, this is
     ;how you do it. Subscribe to extension 6600 in sip, and you will see
     ;the status of the first parking lot with this extensions' help
     ;exten => 6600,hint,park:701@parkedcalls
     ;exten => 6600,1,noop
     ;
     ; Some other handy things are an extension for checking voicemail via
     ; voicemailmain
     ;
     ;exten => 8500,1,VoicemailMain
     ;exten => 8500,n,Hangup
     ;
     ; Or a conference room (you'll need to edit meetme.conf to enable this room)
     ;
     ;exten => 8600,1,Meetme(1234)
     ;
     ; Or playing an announcement to the called party, as soon it answers
     ;
     ;exten = 8700,1,Dial(${MARK},30,A(/path/to/my/announcemsg))
     ;
     ;
     ; "timezone" isn't a 'reserved' name in any way, and other places where
     ; the timezone is significant (e.g. calls to "SayUnixTime()", etc) will
     ; require modification as well.  Note that voicemail.conf already has
     ; a mechanism for timezones.
     ;
     [time]
     exten => _X.,30000(time),NoOp(Time: ${EXTEN} ${timezone})
     exten => _X.,n,Playback(at-tone-time-exactly)
     ; the amount of delay is set for English; you may need to adjust this time
     ; for other languages if there's no pause before the synchronizing beep.
     exten => _X.,n,Set(FUTURETIME=$[${EPOCH} + 12])
     exten => _X.,n,SayUnixTime(${FUTURETIME},Zulu,HNS)
     exten => _X.,n,SayPhonetic(z)
     ; use the timezone associated with the extension (sip only), or system-wide
     ; default if one hasn't been set.
     exten => _X.,n,SayUnixTime(${FUTURETIME},${timezone},HNS)
     exten => _X.,n,Playback(spy-local)
     exten => _X.,n,WaitUntil(${FUTURETIME})
     exten => _X.,n,Playback(beep)
     exten => _X.,n,Return()
     ;
     ; ANI context: use in the same way as "time" above
     ;
     [ani]
     exten => _X.,40000(ani),NoOp(ANI: ${EXTEN})
     exten => _X.,n,Playback(vm-from)
     exten => _X.,n,SayDigits(${CALLERID(ani)})
     exten => _X.,n,Wait(1.25)
     exten => _X.,n,SayDigits(${CALLERID(ani)})   ; playback again in case of
                                                  ; missed digit
     exten => _X.,n,Return()
     ; For more information on applications, just type "core show applications"
     ; at your friendly Asterisk CLI prompt.
     ;
     ; "core show application <command>" will show details of how you
     ; use that particular application in this file, the dial plan. 
     ; "core show functions" will list all dialplan functions
     ; "core show function <COMMAND>" will show you more information about
     ; one function. Remember that function names are UPPER CASE.

>>>>>>
Tidy up a bit (you can cut and paste all of this to the command line) cat > /etc/asterisk/sip.conf << EOF
[general]
context=default
allowguest=yes
match_auth_username=yes
allowoverlap=no
allowtransfer=no
realm=pbx12.vitell.co.uk
udpbindaddr=0.0.0.0
tcpenable=no
tlsenable=no
srvlookup=yes
pedantic=no
tos_sip=cs3
tos_audio=ef
tos_video=af41
tos_text=af41
cos_sip=3
cos_audio=5
cos_video=4
cos_text=3
maxexpiry=3600
minexpiry=60
defaultexpiry=120
mwiexpiry=3600
qualifyfreq=60
qualifygap=100
qualifypeers=1
vmexten=voicemail
disallow=all
allow=alaw
mohinterpret=default
mohsuggest=default
parkinglot=plaza
language=en
relaxdtmf=yes
useragent=AsteriskPBX
sdpsession=AsteriskPBX
promiscredir=no
dtmfmode=rfc2833
videosupport=no
callevents=no
alwaysauthreject=yes
shrinkcallerid=no
allowsubscribe=yes
subscribecontext=default
notifyringing=yes
notifyhold=yes
notifycid=yes
callcounter=yes
t38pt_udptl=yes,fec,maxdatagram=400
faxdetect=no
nat=no
directmedia=no
directrtpsetup=no
EOF
<<<<<<

Configuring DAHDI

The "make config" step of the Building DAHDI section should have created a set of default configuration files for DAHDI, based on the system's hardware configuration. However, if you ran "make config" before Asterisk was built and installed, some of the files needed by dahdi_genconf may have been missing and it wouldn't have generated all of the config files. So, you may want to run dahdi_genconf now to build the /etc/dahdi/system.conf and /etc/asterisk/dahdi-channels.conf files:

     su
     /usr/sbin/dahdi_genconf

If you have multiple line cards and you need to load them in a particular order (e.g. Digium X100P before TDM400P), you should edit the /etc/dahdi/modules file and order the corresponding drivers in ascending order, the way you want the line cards loaded. On the other hand, if you only have a single type of line card (or only one line card, for that matter), or you don't care which order the line cards are loaded, you can simply leave /etc/dahdi/modules as is.

The parameters in the genconf_parameters file affect the way dahdi_genconf works. Normally, you don't need to touch this file but the most likely change you might want to make is to change the base extension number that dahdi_genconf uses, so that the generated extension numbers match what you're using in your Asterisk config files. For example:

/etc/dahdi/genconf_parameters:

.

       .

# When generating extensions for chan_dahdi.conf or users.conf etc: the # extension number will be channel_number+base_exten . The default is:

  base_exten             600

On the other hand, we recommend running dahdi_genconf ONCE, manually, to configure the FXS, FXO and T1 lines, and then NEVER use it again. You can then edit the dahdi-channels.conf file to change extension information, etc., once it is generated the first time. If you choose to do this, you'll have complete control over how the extensions are set up. Just be sure to keep a copy of it elsewhere and/or make sure you never run dahdi_genconf. Here's an example of a single FXS entry (which uses FXO signalling), as you might wish it to appear:

/etc/asterisk/dahdi-channels.conf:

.

       .

;;; line="1 OPVXA1200/12/0 FXOKS
signalling=fxo_ks
callerid="Channel 1" <600>
mailbox=600
group=5
context=from-internal
channel => 1
callerid=
mailbox=
group=
context=default

As mentined, the signalling was set to "fxo_ks" to use FXO, as well as Koolstart (see below). The "callerid" parameter is set to a string that is used to identify the channel, when caller ID information is sent, along with an extension number, in angle brackets. The "mailbox" parameter indicates which mailbox DAHDI should check for pending voicemail and generate a stutter dialtone if mail exists. The "group" parameter lets related channels be grouped together for dialing purposes. The "context" parameter assigns the context to be used in the Asterisk dial plan.

And, here's an example of a single FXO entry (which uses FXS signalling), as you might wish it to appear:

/etc/asterisk/dahdi-channels.conf:

.

       .

;;; line="9 OPVXA1200/12/8 FXSKS"
signalling=fxs_ks
callerid=asreceived
group=0
context=from-pstn
channel => 9
callerid=
group=
context=default

Note that, for each generated channel, the "callerid", "mailbox", "group" and "context" parameters are set back to the defaults, after the channel is defined. You should follow this standard if you add any new channels.

We have just touched on setting up FXS and FXO channels because these are the ones that mimic those found in a basic PBX (e.g. a SOHO switch). However, the possibilites for the device types for the channels are quite extensive. You should see the information provided for the DAHDI system.conf file (below) for more information.

/etc/asterisk/chan_dahdi.conf:

Under the scheme used by dahdi_genconf, the Asterisk chan_dahdi.conf file is reserved for setting general DAHDI parameters and the included dahdi-channels.conf file is reserved for defining the individual DAHDI channels.

In the chan_dahdi.conf file, the "trunkgroups" section should be empty (for starters). The "channels" section should contain the general parameters settings and then include the dahdi-channels.conf file for configuration of the individual channels. Here's an example of the key parameters:

     [trunkgroups]
     [channels]
     usecallerid=yes
     hidecallerid=no
     callwaiting=yes
     usecallingpres=yes
     callwaitingcallerid=yes
     threewaycalling=yes
     transfer=yes
     canpark=yes
     cancallforward=yes
     callreturn=yes
     echocancel=yes
     echocancelwhenbridged=no
     faxdetect=both
     ;rxgain=0.0       ; Set this positive if you can't hear your callers or
                       ; negative if they're too loud
     ;txgain=0.0       ; Set this positive if your callers can't hear you or
                       ; negative if they think you're too loud
     group=1
     callgroup=1
     pickupgroup=1
     ; Uncomment these lines if you have problems with the disconection of your
     ; analog lines.
     ;busydetect=yes
     ;busycount=3

Note that the generated dahdi-channels.conf file should be included in the /etc/asterisk/chan_dahdi.conf configuration file. If it isn't, you should edit chan_dahdi.conf with your favorite editor and include it at the end of the file.

/etc/asterisk/chan_dahdi.conf:

.

       .

;
; Include the DAHDI channels that were generated by dahdi-genconf. ;
#include dahdi-channels.conf

Since dahdi-channels.conf is included in chan_dahdi.conf, the parameters used therein are the same as those described in the comments in chan_dahdi.conf. You can look there if you wish to understand them. Also, more information on configuring digital channels, as well as the options that can be used in both chan_dahdi.conf and dahdi-channels.conf can be found here:

     http://www.asteriskguide.com/mediawiki/index.php?title=Digital_Channels&;\
     redirect=no

The /etc/dahdi/system.conf file generated by dahdi_genconf is usually OK to use as is. However, you may wish to check that the "loadzone" and "defaultzone" parameters are correctly set to your country.

/etc/dahdi/system.conf:

.

       .

# Global data

     loadzone        = us
     defaultzone     = us

The dahdi_genconf generated file should pick the correct device type for all of your installed cards. Here is a list of the possibilites:

     e&m       Channels are signalled using E&M signalling on a T1 line.
               Specific implementation, such as Immediate, Wink, or Feature
               Group D are handled by the userspace library.
     e&me1     Channels are signalled using E&M signalling on an E1 line.
     fxsls     Channels are signalled using FXS Loopstart (see below) protocol.
     fxsgs     Channels are signalled using FXS Groundstart (see below)
               protocol.
     fxsks     Channels are signalled using FXS Koolstart (see below) protocol.
     fxols     Channels are signalled using FXO Loopstart (see below) protocol.
     fxogs     Channels are signalled using FXO Groundstart (see below)
               protocol.
     fxoks     Channels are signalled using FXO Koolstart (see below) protocol.
     sf        Channels are signalled using in-band single frequency tone.
               The syntax is as follows:
            ch# => sf:<rxfreq>,<rxbw>,<rxflag>,<txfreq>,<txlevel>,<txflag>
            rxfreq - rx tone frequency in Hz
            rxbw - rx notch (and decode) bandwith in hz (typically 10.0)
            rxflag - either 'normal' or 'inverted'
            txfreq - tx tone freq in hz
            txlevel - tx tone level in dbm
            txflag - either 'normal' or 'inverted'.
            Set rxfreq or txfreq to 0.0 if that tone is not desired.
     unused    No signalling is performed, each channel in the list remains idle.
     clear     Channels are bundled into a single span. No conversion or
               signalling is performed, and raw data is available on the master.
     bchan     Like clear except all channels are treated individually and are
               not bundled. inclear is an alias for this.
     rawhdlc   The DAHDI driver performs HDLC encoding and decoding on the
               bundle, and the resulting data is communicated via the master
               device.
     dchan     The DAHDI driver performs HDLC encoding and decoding on the
               bundle and also performs incoming and outgoing FCS insertion
               and verification. fcshdlc is an alias for this.
     hardhdlc  The hardware driver performs HDLC encoding and decoding on the
               bundle and also performs incoming and outgoing FCS insertion
               and verification. Is subject to limitations and support of
               underlying hardware. BRI spans serviced by the wcb4xxp driver
               must use hardhdlc channels for the signalling channels.
     nethdlc   The DAHDI driver bundles the channels together into an hdlc
               network device, which in turn can be configured with sethdlc
               (available separately). In 2.6.x kernels you can also optionally
               pass the name for the network interface after the channel list.
               Syntax:
            nethdlc=<channel list>[:interface name]
            Use original names, don't use the names which have been already
            registered in the system (e.g eth).
     dacs      The DAHDI driver cross connects the channels starting at the
               channel number listed at the end, after a colon.
     dacsrbs   The DAHDI driver cross connects the channels starting at the
               channel number listed at the end, after a colon and also
               performs the DACSing of RBS bits.

The three choices for the line start method, used on the FXS or FXO lines, are:

     Loopstart    The original analog circuit made the connection between the
                  customer premises and the central office, using two copper
                  wires. Typically there is a -48 VDC signal between the two
                  wires which is powered by the central office switch. When
                  the phone goes off hook, a switch on the phone closes the
                  connection between the two wires and current is drawn from
                  the central office switch. The switch determines that current
                  is being drawn and provides a dial tone. Loopstart does not
                  include hangup notification unless specifically done through
                  forward disconnect.
     Groundstart  One problem that was identified with loop start circuits is
                  a condition known as glare, which occurs when a call is
                  switched to the analog line at the central office, at the
                  same time that the phone goes off hook to make an outgoing
                  call. The ground start protocol was designed to eliminate the
                  glare problem. From the phone side, the ring lead is grounded
                  first. Then the central office circuit must ground the tip
                  lead before the switch can close the loop between tip and
                  ring. Ground start lines always have a little current runing
                  through them and the additional hardware drives up costs.
                  However, this format allows for signaling when shorting the
                  line to ground (one of the biggest uses of ground start was
                  in pay phones, where a ground start line could send variable
                  pulses to indicate the money being deposited, before the
                  switch sends a dial tone to the phone). Ground start does
                  include hangup notification.
     Koolstart    Koolstart is also known as Disconnect Supervision, and is
                  the line start method that you usually want to use for FXO
                  devices. If your phone provider doesn't support it, the
                  FXO device will not detect that the other side has hung up
                  and Asterisk will continue to service the port. If you can't
                  get your Telco to give you Disconnect Supervision on your
                  line there are workaround that are possible.

DAHDI uses modular echo cancellers that are configured on a per channel basis. The echo cancellers are compiled and installed as part of the dahdi-linux package. You can specify in the system.conf file the echo canceller to be used for each channel. The default behavior is for there to be no echo canceller on any channel. Since running without echo cancellation is not likely to give good results, it is important that you specify an echo canceller for each channel, if the line cards do not have built-in hardware echo cancellation.

The choices for echo cancellers are:

     mg2    This is the default echo canceller that is provided by the DAHDI
            driver. It does a fair to middling job of echo cancellation. It was
            hacked from the original Zaptel echo canceller by Michael Gernoth.
            Until the oslec echo canceller is widely available, this is probably
            the best we're going to get.
     kb1    This was the original Zaptel echo canceller by Kris Boutilier.
            Additional background on the techniques used in this echo canceller
            (as well as mg2) can be found in: Messerschmitt, David; Hedberg,
            David; Cole, Christopher; Haoui, Amine; Winship, Peter; "Digital
            Voice Echo Canceller with a TMS32020," in Digital Signal Processing
            Applications with the TMS320 Family, pp. 415-437, Texas Instruments,
            Inc., 1986. A PDF of this document is available by searching on the
            document title at http://www.ti.com/. It was hacked by Michael
            Gernoth to form mg2, which is supposed to be better.
     sec    Steve's echo canceller. An echo canceller, suitable for electrical
            and acoustic cancellation. This echo canceller does not currently
            comply with any relevant standards (e.g. G.164/5/7/8). It was
            written by Steve Underwood, with various optimizations and
            improvements by Mark Spencer and is based on a bit from here, a bit
            from there, eye of toad, ear of bat, etc. - plus, of course, his own
            2 cents.
     sec2   Steve's 2nd echo canceller. An echo canceller, suitable for
            electrical and acoustic cancellation. This echo canceller does not
            currently comply with any relevant standards (e.g. G.164/5/7/8). It
            was written by Steve Underwood and is based on a bit from here, a
            bit from there, eye of toad, ear of bat, etc. - plus, of course, his
            own 2 cents.
     jpah   An echo canceller based on mg2, sort of, by Jason Parker. According
            to Jason, this echo canceller will completely hose your audio. Don't
            use it unless you're absolutely sure you know what you're doing.
     hpec   This a proprietary high performance echo canceller which must be
            compiled in. It only works with Digium hardware. You'll probably
            want to use oslec instead, anyway.
     olsec  An open source high performance line echo canceller. When used
            with Asterisk it works well on lines where the built-in Zaptel
            echo canceller fails. No tweaks like rxgain/txgain or fxotrain
            are required. Oslec is supplied as GPL licensed C source code and
            is free as in speech. Oslec partially complies with the G168
            standard and runs in real time on x86 and Blackfin platforms.
            Patches exist to allow any Zaptel compatible hardware to use
            Oslec. It has been successfully tested on hardware ranging from
            single port X100P FXO cards to dual E1 systems. Hardware tested
            includes TDM400, X100P, Sangoma A104, Rhino E1 etc. It also works
            well on the IP04 embedded Asterisk platform. Michael Gernoth, the
            creator of mg2, says its better than mg2.

More information of echo cancellers can be found at:

     http://www.voip-info.org/wiki/view/Asterisk+echo+cancellation

The DAHDI startup script in init.d uses some configuration parameters that are defined in /etc/dahdi/init.conf. Generally, the parameter values set therein are adequate for most uses and will not need to be adjusted. However, you may need to adjust the time to wait for UDEV if you have a lot of lines, the startup is slow, etc. The default is 40 seconds, which should be ample but you might set it to something longer (e.g. 100 seconds).

/etc/dahdi/init.conf:

.

       .

# The maximal timeout (seconds) to wait for udevd to finish generating # device nodes after the modules have loaded and before running dahdi_cfg. DAHDI_DEV_TIMEOUT=100

After you've configured DAHDI, reboot your system. Some of the DAHDI parameters do not get reset upon reload so reboot is the best course of action. If you want to check that the DAHDI channels are set up properly, you can attach to the running Asterisk process and send it a show command:

     su
     /usr/sbin/asterisk -rx 'dahdi show channels'

If you saw the DAHDI channels, they have been loaded properly. The listing should look something like this:

     Chan Extension  Context       Language  MOH Interpret  Blocked  State     
   pseudo            default                 default                 In Service
        1            from-internal           default                 In Service
        2            from-internal           default                 In Service
        3            from-internal           default                 In Service
        4            from-internal           default                 In Service
        5            from-internal           default                 In Service
        6            from-internal           default                 In Service
        7            from-internal           default                 In Service
        8            from-internal           default                 In Service
        9            from-pstn               default                 In Service
       10            from-pstn               default                 In Service
       11            from-pstn               default                 In Service
       12            from-pstn               default                 In Service

Local Variables

To begin with the local configuration of Asterisk, we like to define a few local variables in a separate file so that the other local configuration files can be made more generic. These variables define an extension pattern, an optional virtual extension pattern, special extensions, the digits to use for outbound dialing, the code to use for accessing special features, and a LATA to be used for local area dialing.

/etc/asterisk/local/variables.conf:

     ; local/variables.conf - Local variables.
     ;
     ; This module is included at the appropriate spot in the dialplan (defined
     ; by extensions.conf).  It defines the local global variables that are used
     ; to describe the local configuration.
     ;
     ;
     ; We are (hopefully) already in the global context where any variables that
     ; we define are global.
     ;
     ; We'll define all of the variables (constants) that describe the local
     ; configuration, so that the other files can be generic.
     ;
     ; Pattern used to match extensions.  Set this to match the kind of extension
     ; numbers you wish to use.  For example "6XX" or "5XXX".
     EXTPATTERN=6XX
     ; Pattern used to match any virtual extensions.  This pattern should be
     ; defined whether you use virtual extensions or not.  You can pick some
     ; bogus pattern (such as 999) if you don't use virtual extensions, but it
     ; should be OK the way it is.
     ;
     ; Optional switch that turns on virtual extensions from inside (i.e. local
     ; extensions).
     ;
     ; Optional switch used to let outside callers through to the inside so
     ; that they can dial the virtual extensions too.  You may or may not want
     ; to do this, depending on what the virtual extensions do.
     VIRTPATTERN=8[0-8]X
     VIRTINSIDE=1
     VIRTOUTSIDE=1
     ; Special extensions used to get special services.
     EXTVMAIL=898                            ; Extension used to get voicemail
                                             ; from an outside call
     EXTRECORDIVR=899                        ; Extension used to record an IVR
                                             ; prompt
     ; Dial code used to grab an outbound trunk from one of the internal
     ; extensions.  Typically, this is a single digit, such as "9", but it
     ; can be any sequence you like.
     OUTBOUNDCODE=9
     ; Dial code used for special features (e.g. for turning on DoNotDisturb
     ; or CallWaiting or grabbing a specific outside line).  This must be a
     ; single digit, such as "".
     SPECIALCODE=
     ; Local LATA used to allow 7-digit dialing, even in the era of overlay
     ; plans.  If the user dials the OUTBOUNDCODE, followed by a 7-digit number,
     ; we can whack this LATA on the front to get the overlay plan number.
     LOCALLATA=416

Configuring Local DAHDI FXS/FXO Channels

The local FXS and FXO channels that are defined by DAHDI can be configured to implement local extensions and CO lines. To configure these extensions and CO lines, the user must make sure that the context "from-pstn" and "from-internal" are in the dial plan.

These two contexts can be configured in two separate files that are stored in the "local" subdirectory and that are included in the extensions.conf file. Here is an example of our local extensions file, that uses the "stdexten" routine, that is defined in extensions.conf, to define a number of local extensions, among other things.

/etc/asterisk/local/dahdi-extensions.conf:

     ; local/dahdi-extensions.conf - Local DAHDI extensions.
     ;
     ; This module is included at the appropriate spot in the dialplan (defined
     ; by extensions.conf).  It defines the local extensions that are implemented
     ; by DAHDI line cards, such as the Digium or OpenVox cards.
     ;
     ; The DAHDI utility dahdi_genconf automatically assigns DAHDI channels to all
     ; of the local lines implemented on DAHDI cards.  It assigns each channel to
     ; either the "from-internal" context, which we can use herein, or the
     ; "from-pstn" context.
     ;
     ;
     ; Local extensions defined as DAHDI devices.  The "from-internal" context
     ; contains all of the FXS lines supported by DAHDI.
     ;
     [from-internal]
     ; Don't break the dialtone, until after the user dials the first digit of
     ; the outbound code.  Note that the ignorepat must appear in this context,
     ; nowhere else.
     ignorepat => ${OUTBOUNDCODE:0:1}
     ; Include the definition of a standard extension from extensions.conf.
     include => stdexten
     ; Define the operator's extension as zero.  It rings whatever phone you
     ; wish it to ring (using ring cadence 2).  The extension is whatever
     ; mailbox you wish to use.
     exten => 0,1,Gosub(602,stdexten(dahdi/1r1))
     ; Define the local extensions.
     exten => 600,1,Gosub(${EXTEN},stdexten(dahdi/1))
     exten => 601,1,Gosub(${EXTEN},stdexten(dahdi/2))
     exten => 602,1,Gosub(${EXTEN},stdexten(dahdi/3))
     exten => 603,1,Gosub(${EXTEN},stdexten(dahdi/4))
     exten => 604,1,Gosub(${EXTEN},stdexten(dahdi/5))
     ; Allow a user to dial one of the local extensions, prefixed with an
     ; asterisk, to go directly to the extension's voicemail box.  This
     ; allows the user to transfer an incoming call to a voicemail box, say
     ; when the caller asks to speak to someone whom the user knows to be
     ; away.
     ;
     ; Note that, if the SPECIALCODE is set to "", the extensions could
     ; interfere with the special function codes that begin with the same
     ; digit as our extensions.  Fortunately, we use two-digit special function
     ; codes and three digit extensions, and asterisk knows how to sort it all
     ; out.
     exten => _${EXTPATTERN},1,VoiceMail(${EXTEN:1})
     ; Include the parking lot.  This will define extensions 700-7nn (depending
     ; on the settings in features.conf).
     include => parkedcalls
     ; We optionally enable the virtual extensions from inside lines, if the
     ; virtual inside switch is on.
     exten => _${VIRTPATTERN},1,Gotoif($[${EXISTS(${VIRTINSIDE})}]?\
                                       virtual-extensions,${EXTEN},1:)
     exten => _${VIRTPATTERN},n,Playback(sorry2)
     exten => _${VIRTPATTERN},n,Hangup()
     ; Define the same extension that gets us into voicemail from outside, just
     ; in case someone dials it from inside.
     exten => ${EXTVMAIL},1,VoicemailMain()
     exten => ${EXTVMAIL},n,Hangup()
     ; Define an extension used to record IVR prompts.
     exten => ${EXTRECORDIVR},1,Macro(recordivr)
     ; Include the special feature codes.
     include => feature-codes
     ; Include the dial plans for outside lines.
     include => outside-local
     include => outside-longdistance

And, here is an example of our local CO lines file, that uses the "ivrmenutree" macro, that is defined in the local ivr-menutree.conf file, to define the rules for answering the local CO lines and routing the answered calls.

/etc/asterisk/local/dahdi-colines.conf:

     ; local/dahdi-colines.conf - Local DAHDI CO lines.
     ;
     ; This module is included at the appropriate spot in the dialplan (defined
     ; by extensions.conf).  It defines the local CO lines that are implemented
     ; by DAHDI line cards, such as the Digium or OpenVox cards.
     ;
     ; The DAHDI utility dahdi_genconf automatically assigns DAHDI channels to all
     ; of the local lines implemented on DAHDI cards.  It assigns each channel to
     ; either the "from-internal" or "from-pstn" context, which we can use herein.
     ;
     ;
     ; Local CO lines defined as DAHDI devices.  The "from-pstn" context contains
     ; all of the FXO lines supported by DAHDI.
     ;
     [from-pstn]
     ; Based on the time of day, we either jump to the day ring or night ring
     ; context.  This lets the user ring some phones, during the day, before the
     ; IVR attendant gets involved.  Note that the times used herein need not
     ; match the times that the IVR attendant checks for to implement its menus.
     exten => s,1,GotoIfTime(08:00-21:00,*,*,*?dahdidayring,s,1)
     ; Fall through to the night ring.
     exten => s,n,Goto(dahdinightring,s,1)
     ;
     ; Day ringing for inbound calls on the DAHDI CO lines.  Ring a few phones
     ; for 20 seconds, in case the humans want to answer them, before we head
     ; off to the IVR attendant.
     ;
     ; Note that we pass the transfer and park flags so that whoever answers the
     ; call can transfer it somewhere else or put it into the parking orbit so
     ; that they can go to another extension and pick it up.
     ;
     [dahdidayring]
     exten => s,1,Dial(dahdi/1&dahdi/2,20,otk)
     exten => s,n,Macro(ivrmenutree)
     ;
     ; Nignt ringing for inbound calls on the DAHDI CO lines.  We just head
     ; straight off to the IVR attendant.
     ;
     [dahdinightring]
     exten => s,1,Macro(ivrmenutree)

Note that during daytime hours, a couple of the local DAHDI extensions are given twenty seconds to answer the incoming call before the IVR Attendant takes over. However, during nighttime hours, any incoming call is routed directly to the IVR attendant without ringing any local extensions.

DAHDI Dial Parameters

Just because they don't seem to be well documented elsewhere, here is a brief synopsis of the parameters that can be passed to DAHDI via the Asterisk Dial application:

     Dial(DAHDI/pseudo[/extension])
     Dial(DAHDI/<channel>[c|r<cadance>|d][/extension])
     Dial(DAHDI/(g|G|r|R)<group(0-63)>[c|r<cadance>|d][/extension])
     g - Search forward through channel group and use first available.
     G - Search backward through channel group and use first available.
     r - Allocate next in round robin search forward through channel group.
     R - Allocate next in round robin search backward through channel group.
     c - Wait for DTMF digit to confirm answer.
     r<cadance#> - Set distintive ring cadance number.
     d - Force bearer capability for ISDN/SS7 call to digital.
     Channel Groups
     dahdi/g1/5551212 dials 5551212 on the first available channel in group
     one, searching from lowest to highest.
     dahdi/G1/5551212 dials 5551212 on the first available channel in group
     one, searching from highest to lowest.
     dahdi/r1/5551212 dials 5551212 on the first available channel in group
     one, going in round-robin fashion (and remembering where it last left
     off), searching from lowest to highest.
     dahdi/R1/5551212 dials 5551212 on the first available channel in group
     one, searching in round-robin fashion from highest to lowest.
     Distinctive Ring
     dahdi/4r1 dials channel 4 (presumably an FXS channel), and uses
     distinctive ring style one.  There are four different distinctive ring
     styles, so you could replace "r1" with "r2", "r3", or "r4".
     Answer Confirmation
     dahdi/1c/5551212 tells Asterisk to dial 5551212 on DAHDI channel 1, and
     not consider the call answered until the called party presses #.  This
     might be useful because of the way analog signaling works.  Without this
     setting, Asterisk will consider any outbound analog call on an FXO port
     to be answered as soon as it has been dialed.
     Digital Calls
     dahdi/1d/5551212 tells Asterisk to dial 5551212 on DAHDI channel 1, and
     that it's a digital call.  This is useful when making ISDN calls to set
     the bearer capability.

The Parking Lot

We suppose that, just by random chance, some part of setting up your Asterisk system should be simple and easy. It looks like the parking lot may be the qualifying component.

Begin by editing a few of the parameters in the features.conf file to define how Call Parking should work. Basically, under the "general" context, we define what extension should be used for the parking lot, what extensions to park calls on, the context for the parking lot, and the parking time. The rest of call parking parameters should be OK with their default settings.

/etc/asterisk/features.conf:

.

       .
  [general]
  parkext => 700                ; What extension to dial to park (all
                                ; parking lots)
  parkpos => 701-720            ; What extensions to park calls on
                                ; (defafult parking lot).
                                ; Both extensions need to be numeric, as
                                ; Asterisk starts from the start position
                                ; and increments by one for the next
                                ; parked call.
  context => parkedcalls        ; Which context parked calls are in (default
                                ; parking lot)
  parkingtime => 60             ; Number of seconds a call can be parked
                                ; for (default is 45 seconds)

We remove the include of the "parkedcalls" context from the extensions.conf file (shown in the Basic Asterisk Configuration section).

We include the "parkedcalls" context in the dahdi-extensions.conf local file, as shown in the Configuring Local DAHDI FXS/FXO Channels section.

That's about all there is to it. Restart Asterisk and you can transfer calls to the parking lot by dialing the Call Parking extension (e.g. 700).

A Dial Plan For Outside Lines

OK, so its all a dial plan. But, to our traditional minds, the dial plan is the part that tells the switch how the digits that the user dials are interpreted. Specifically, how does outbound dialing work? When the user dials 9, followed by a 1 and a 10-digit number, do they get connected to a CO line and does a long distance call get dialed, or do they get a woo-woo tone?

So, in the spirit of traditional dial plans, we create a local module that defines the rules for obtaining an outside line and placing a local or long distance call. A simple set of rules can be employed to just grab a CO line and dial. Or, more complex rules can be used to route calls through trunks, VOIP or other means of delivering calls.

In addition to the dialing rules, we define a hunt group for the local CO lines, so that we may exactly control how the CO lines are used. Certainly, you can use the features of the DAHDI config file to group a set of lines and dial outbound calls based on which ones are available, in a rudimentary order (e.g. highest to lowest), but we like the ability to order the lines, in whatever order we want. Perhaps you can see some flaws in this system (i.e. that using DAHDI's features circumvent) but it seems to work for us.

Lastly, we give the user complete control over which CO line is used to place an outbound call by allowing them to prefix their call with a special code (in this case "*42" -- not strictly legit, according to the North American CLASS codes, but that's what we're using) that allows them to pick the line to use. This allows them to route a call directly to a copper line, say, when all of the VOIP lines are dead (not that that would ever happen).

Another use for this capability is to check for voicemail (from the PhoneCo switch) on a particular CO line. By selecting a specific line and then dialing that line's phone number, many PhoneCo switches direct the call to the voicemail box (instead of giving a busy signal) for that line. Thus, you can dial something like "*4212314567", to check the voicemail on line 1, which has a phone number of 2314567 in the local LATA.

/etc/asterisk/local/plan-outsidelines.conf:

     ; local/plan-outsidelines.conf - Dial plan for outside lines.
     ;
     ; This module is included at the appropriate spot in the dialplan (defined
     ; by extensions.conf).  It defines the dial plan for outside lines (i.e.
     ; how to grab a CO line to make a local or long distance call).
     ;
     ;
     ; Here we'll define contexts with all of the dialing sequences that grab an
     ; outside CO line and let the user make a local or long distance call.
     ; These contexts can be included in the contexts that define local
     ; extensions to let the local extensions dial out.
     ;
     [outside-local]
     ; The outbound code, followed by 7 digits gets the local LATA to accomodate
     ; 7-digit dialing, despite overlay plans.  Note that, if some office codes
     ; require 1 in front of them while others don't, you may have to write
     ; special rules for them here.
     ;exten => _${OUTBOUNDCODE}326XXXX,1,Goto(outside-huntgroup,\
                                              ${LOCALLATA}${EXTEN:1},1)
     ;exten => _${OUTBOUNDCODE}551XXXX,1,Goto(outside-huntgroup,\
                                              ${LOCALLATA}${EXTEN:1},1)
     ;exten => _${OUTBOUNDCODE}NXXXXXX,1,Goto(outside-huntgroup,\
                                              1${LOCALLATA}${EXTEN:1},1)
     exten => _${OUTBOUNDCODE}NXXXXXX,1,Goto(outside-huntgroup,\
                                             ${LOCALLATA}${EXTEN:1},1)
     ; We need to support 911 and the outbound code followed by 911.
     exten => 911,1,Goto(outside-huntgroup,911,1)
     exten => ${OUTBOUNDCODE}911,1,Goto(outside-huntgroup,911,1)
     ; The special code, followed by "42", followed by a CO line number, followed
     ; by 7 digits grabs a specific line and then gets the local LATA to
     ; accomodate 7-digit dialing, despite overlay plans.
     exten => _${SPECIALCODE}42[1-9]NXXXXXX,1,Dial(dahdi/$[8+${EXTEN:3:1}]/\
                                                   ${LOCALLATA}${EXTEN:4})
     ; We need to support the special code, followed by "42", followed by a CO
     ; line number plus 911.
     exten => ${SPECIALCODE}42[1-9]911,1,Dial(dahdi/$[8+${EXTEN:3:1}]/911)
     [outside-longdistance]
     ; The outbound code, followed by 10 digits or a 1 and 10 digits dials an
     ; overlay plan or long distance call.
     exten => _${OUTBOUNDCODE}NXXXXXXXXX,1,Goto(outside-huntgroup,${EXTEN:1},1)
     exten => _${OUTBOUNDCODE}1NXXXXXXXXX,1,Goto(outside-huntgroup,${EXTEN:1},1)
     ; The special code, followed by "42", followed by a CO line number,
     ; followed by 10 digits or a 1 and 10 digits dials an overlay plan or long
     ; distance call on that CO line.
     exten => _${SPECIALCODE}42[1-4]NXXXXXXXXX,1,Dial(dahdi/$[8+${EXTEN:3:1}]/\
                                                      ${EXTEN:4})
     exten => _${SPECIALCODE}42[1-4]1NXXXXXXXXX,1,Dial(dahdi/$[8+${EXTEN:3:1}]/\
                                                       ${EXTEN:4})
     ;
     ; Context that defines the outside line hunt group.  We could use a DAHDI
     ; group but this way we get to use lines out of order, resequence them at
     ; will, etc.
     ;
     [outside-huntgroup]
     exten => _X.,1,Dial(dahdi/10/${EXTEN})
     exten => _X.,n,Dial(dahdi/11/${EXTEN})
     exten => _X.,n,Dial(dahdi/9/${EXTEN})
     exten => _X.,n,Congestion()
     exten => _X.,n,Hangup()

Note that you should really test the 911 dialing rule to ensure that it works. You can do this by substituting a harmless number (e.g. your cell phone) in the rule, like this:

     exten => 911,1,Goto(outside-huntgroup,4161234567,1)
     exten => ${OUTBOUNDCODE}911,1,Goto(outside-huntgroup,4161234567,1)

Once you're sure that dialing 911 places the outside call properly, you can set the rule back to "911".

Pre-recorded IVR Sounds

IVR (Interactive voice responce) is one of the most used PBX applications so you'll probably want your dialplan to take advantage of this feature. In order to do so, you must create a set of voice menus that can be played to the user as they navigate through the system. These voice menus are constructed from pre-recorded sound files, which can be obtained in several ways.

There are several sets of pre-recorded professional IVR sounds available from Digium at http://downloads.asterisk.org/pub/telephony/sounds/. They are used by the voicemail system as well as IVR so the core sounds should always be installed on your Asterisk system. We like to get the ".wav" format sounds, so that Asterisk can convert the sounds to the best quality format to send out to the user, when each sound is played. You should download the following files from the URL mentioned above:

     asterisk-core-sounds-en-wav-current.tar.gz
     asterisk-extra-sounds-en-wav-current.tar.gz

The core sounds were probably already installed by your version of Asterisk, when you built and installed it. You can check the /var/lib/asterisk/sounds directory to see if it is there and make sure that there are lots of sound files in the subdirectories such as "en". If the sound files aren't present, you can install them like this:

     su
     mkpath /var/lib/asterisk/sounds/en
     cd /var/lib/asterisk/sounds/en
     tar -xvzf .../asterisk-core-sounds-en-wav-current.tar.gz

If you need them, you must always install the extra sounds manually, as follows, after you've installed the core sounds:

     su
     cd /var/lib/asterisk/sounds/en
     tar -xvzf .../asterisk-extra-sounds-en-wav-current.tar.gz

Instead of the Digium-supplied sounds, you may want to get the American female sounds file sounds-amer-fem-1.0-sln.tar.gz from Voice Vector at http://www.voicevector.com/Downloads.php. Follow their instructions to install it.

You can search the text indexes, provided with the sounds collections, to find the IVR menu sounds that you need. Once you find what you need (if you find what you need), you can string sound clips together like this:

     exten => s,n,Playback(thank-you-for-calling&if-u-know-ext-dial)

Recording Your Own IVR Sounds

You may be able to construct a set of useful IVR menus from the pre-recorded sounds that you can download from Digium (or other places). However, most of the available sound libraries seem to be designed more as plugs for the services of the voice talent than anything actually useful. Were this not the case, they would include words like "the" that could be used to construct real menus, not stories about monkeys or weasels.

In all probability, you'll want to record your own IVR sounds, based on your own menus, and not waste your time trying to string together a set of Alison's less-than-adequate utterances. If your friend Sophia has a mellifluous voice and you can convince her to record your voice prompts, or you happen to win big on Wait, Wait, Don't Tell me and Carl Castle is at your beck and call, you're all set.

Basically, we'll define an extension that you can call, which will record whatever is spoken into the phone, at which point the resultant file can be saved and included in an IVR menu tree. We can save them in our own IVR subdirectory off the standard sounds directory:

     su
     mkdir /var/lib/asterisk/sounds/ivr
     chown root:root /var/lib/asterisk/sounds/ivr
     chmod u=rwx,go=rx /var/lib/asterisk/sounds/ivr

We build a macro that can be used, wherever you define extensions, to define an extension that allows recording of IVR prompts. The macro, which looks like this, is added to extensions.conf:

     ;
     ; recordivr:
     ;
     ;   Macro to record a phrase as a ".wav" file in the /tmp directory, where
     ;   it can be saved for use in an IVR menu.
     ;
     ;   This uses the pls-rcrd-name-at-tone sound from the Asterisk extra sounds
     ;   in asterisk-extra-sounds-en-wav-current.tar.gz.  Its not the right prompt
     ;   but it is "a" prompt.  You could record a different prompt, using this
     ;   macro and then use that prompt for future recordings.
     ;
     ;   The result is put in /tmp/asterisk-recording.wav.  You can copy it
     ;   wherever you wish.
     ;
     [macro-recordivr]
     exten => s,1,Playback(pls-rcrd-name-at-tone)
     exten => s,n,Record(/tmp/asterisk-recording.wav)
     exten => s,n,Wait(2)
     exten => s,n,Playback(/tmp/asterisk-recording)
     exten => s,n,wait(2)
     exten => s,n,Hangup

To define an extension that can be used for IVR sound recording, you use the macro wherever you define the other extensions in your system. For example, if you have local DAHDI extensions defined in the user-defined file /etc/asterisk/local/dahdi-extensions.conf (as shown above in Defining Local FXS/FXO Lines), you might add this:

     ; Define an extension used to record IVR prompts.  We'll use 899.
     exten => 899,1,Macro(recordivr)

After you reload the dialplan, you simply dial the extension that you've defined (e.g. 899). When you hear Alison's prompt, speak your IVR message into the phone and press '' when you're done. Remember to speak clearly, using natural inflection, and include any pauses that you want to occur in the message. When you press the '', your message will be replayed after a two-second delay. If you are happy with it just hang up. Otherwise, hang up, redial the extension again, and start all over.

For each IVR message that you record, copy it from the /tmp directory to the IVR directory that you created (above):

     cp /tmp/asterisk-recording.wav /var/lib/asterisk/sounds/ivr/hi-there.wav

You will then be able to use the message in your IVR menus, like this:

     Exten => s,n,Playback(ivr/hi-there)

Converting Recorded Sounds For Use By Asterisk

You've just recorded a fabulous audio file to use with Asterisk in your IVR menu but you realize that Asterisk does not use normal ADPCM ".wav" format audio for the Playback or Background applications. Not to worry. The SOund eXchange (SOX) application that comes with CentOS can be used to convert to the proper format. The following command should do the trick:

     sox ivr-in.wav -r 8000 -c 1 -s -w ivr-out.wav resample -ql

It uses the resample filter to convert the input ".wav" file to single channel, 16-bit sound at a rate of 8000 samples per second. Just what Asterisk needs. Mind you, you'll probably get better results if you record your original ".wav" file in 16 bit, 8000 Hz, mono.

In IVR menus, excessive changes in volume or voices that are too quiet to understand are not a good thing. Normalizing the volume to a standard level and reducing volume fluctuations is called dynamic range compression. Using the sox compand filter, you can easily perform dynamic range compression on your sound files. Just remember to make sure to employ the compand filter before you resample the sound, so as to preserve as much quality as possible.

Here is an example of how to perform some appropriate dynamic range compression and normalization:

     sox ivr-in.wav -r 8000 -c 1 -s -w ivr-out.wav lowpass 4000 \
         compand 0.02,0.05 -60,-60,-30,-10,-20,-8,-5,-8,-2,-8 -8 -7 0.05 \
         resample -ql

Using Festival For IVR Menus

If you can't find suitable IVR sounds and don't want to record your own IVR messages, or you just like the thought of Clango (www.dieselsweeties.com) answering your phone, you might want to take a look at Festival. Alternately, if you want to actually put the I in IVR, by generating voice responses on the fly, Festival may also be for you. You can install it on CentOS with:

     su
     yum install festival

Once you have Festival installed, you can create IVR messages by simply typing the text of each message into a text file. Note that the Festival command that converts text into sound ignores case but respects punctuation. If you have long text files to convert, punctuation becomes important as you don't want what sounds like audible run-on speech.

The Festival command that converts text files to speech is called text2wave. Generating speach is as simple as typing the text2wave command along with the input, text file name, and the output, sound file name. But, beware that, by default, text2wave creates sound files with bit rates that Asterisk does not tolerate. You need to declare the bit rate as "8000" via the "-F" switch. Consequently, the syntax for the sound file generation is:

     text2wave -F 8000 -o ivr-menu-1.wav ivr-menu-1.txt

Asterisk will convert the generated ".wav" file using whatever codec a caller employs. If you're into pre-generating all of the available codecs, text2wave has a "-otype" switch. The syntax is:

     text2wave -F 8000 -o ivr-menu-1.wav -otype alaw ivr-menu-1.txt

As for generating speech on the fly, Asterisk has a direct link to Festival, through the Festival application. You can include a call to Festival to generate a speech clip, in the definition of an extension, and Asterisk will play the clip to the user. This requires that the Festival service be running before Asterisk is started.

Another way to generate speech on the fly is to call text2wave directly via the System application. This skips the need for the Festival service but requires an external, temporary file to pass the generated speech clip to Asterisk. You'd do it something like this:

     exten => s,1,System(echo 'Hello' | text2wave -F 8000 -o /tmp/festival.wav)
     exten => s,n,Background(/tmp/festival);

Using this scheme, we can also pass the argument to use a different voice:

     exten => s,1,System(echo 'Hello' | text2wave -F 8000 -o /tmp/festival.wav \
                                                  -eval "(voice_us1_mbrola)")

Creating An IVR Attendant

Whatever method you use to obtain the various prompts and messages that you'll use in your IVR menus, you should lay out a picture of the IVR Attendant menu tree and identify all of the sounds that you'll use at each step of the way. This will help you in obtaining the needed sounds and building the actual dialplan that implements the IVR Attendant. Here is an example of a simple IVR Attendant that we use:

     Daytime (09:00-18:59)
     --> play: thank-you-for-calling
               if-you-know-dial-any-time
               P1-for-dir-or-stay-on
         wait for dialed digits
        --> 0 --> ring the operator
            1 --> goto IVR Directory
            invalid --> play: not-valid-extension
                        retry
            timeout --> play: transfer-to-operator
                        ring the operator
            6XX --> ring the extension
     Nights/Weekend (19:00-08:59)
     --> play: thank-you-for-calling
               at-present-closed
               if-you-know-dial-any-time
               party-no-answer-voicemail
               if-dont-know-P1-for-dir
         wait for dialed digits
        --> 1 --> goto IVR Directory
            invalid --> play: not-valid-extension
                        retry
            timeout --> play: hasta-la-vista
                        hang up
            6XX --> ring the extension
     Vacation (July 4)
     --> play: thank-you-for-calling
               at-present-vacation
               if-you-know-dial-any-time
               party-no-answer-voicemail
               if-dont-know-P1-for-dir
         wait for dialed digits
        --> 1 --> goto IVR Directory
            invalid --> play: not-valid-extension
                        retry
            timeout --> play: hasta-la-vista
                        hang up
            6XX --> ring the extension
     Mental Health Hotline
     --> play: mental-health-hotline
         wait for dialed digits
        --> 1 --> goto IVR Directory
            invalid --> play: not-valid-extension
                        retry
            timeout --> play: hasta-la-vista
                        hang up
            6XX --> ring the extension
     IVR Directory
     --> play: dir-dial-three-letters
         wait for dialed digits
        --> invalid --> play: dir-dont-recognize-name
                        retry
            timeout --> play: goodbye
                        hang up
            2XX --> play: transfer-to-norm-cole
                    ring extension 601
            9XX --> play: transfer-to-ed-wilson
                    ring extension 600

The messages, shown in the menus above, have the following texts:

     at-present-closed -           At present, we are closed.
     at-present-vacation -         At present, we are taking some time off and
                                   are closed for vacation.
     dir-dial-three-letters -      Please dial the first three letters of your
                                   party's last name.
     dir-dont-recognize-name -     I'm sorry.  I don't recognize that person's
                                   name.  Please try again.
     goodbye -                     Goodbye.
     hasta-la-vista -              Hasta la vista, baby.
     if-you-know-dial-any-time -   If you know the extension of the party you
                                   wish to reach, you may dial it at any time.
     if-dont-know-P1-for-dir -     If you don't know your party's extension,
                                   press 1 for directory assistance
     mental-health-hotline -       Hello and welcome to The Mental Health
                                   Hotline.  If you are obsessive/compulsive,
                                   press 1 repeatedly.  If you are co-dependant,
                                   ask someone to press 2 for you.  If you have
                                   multiple personalities, press 3, 4, 5 and 6.
                                   If you are paranoid, we know what you are and
                                   what you want, stay on the line and we'll
                                   trace your call.  If you are delusional,
                                   press 7 and your call will be transferred to
                                   The Mother Ship.  If you are schizophrenic,
                                   listen carefully and a small voice will tell
                                   which number to press.  If you are depressive,
                                   it doesn't matter which number you press,
                                   no-one will answer you.  If you are dyslexic,
                                   press 69, 69, 69, 69.  If you have a nervous
                                   disorder, please fidget with the hash key
                                   until the beep.  After the beep, please wait
                                   for the beep.  If you have a short-term memory
                                   loss, please try your call again later, and if
                                   you have low self-esteem, hang up, all our
                                   operators are too busy to talk.
     not-valid-extension -         I'm sorry.  That is not a valid extension.
                                   Please try again.
     P1-for-dir-or-stay-on -       Press 1 for directory assistance or stay on
                                   the line for the operator.
     party-no-answer-voicemail -   If your party does not answer, you will be
                                   able to leave a voicemail message.
     thank-you-for-calling -       Thank-you for calling.
     transfer-to-ed-wilson -       Transferring to Ed Wilson at extension 600.
     transfer-to-norm-cole -       Transferring to Norm Cole at extension 601.
     transfer-to-operator -        Transferring to the operator.

The IVR Attendant's messages are actually stored in the local "ivr" subdirectory that we added under the Asterisk "sounds" directory (i.e. /var/lib/asterisk/sounds/ivr). This being the case, we must prefix the name of each message name with "ivr/", when we use it in the dial plan.

We prefer to build the IRV Attendant portion of the dial plan in a separate file, in the local directory, where it can be conveniently changed, if need be. This file can then be included in the exetensions.conf file and the "ivrmenutree" macro referenced in the appropriate place in the CO lines file.

/etc/asterisk/local/ivr-menutree.conf:

     ; local/ivr-menutree.conf - Local IVR menu tree.
     ;
     ; This module is contains a macro that defines the IVR menu tree used by the
     ; local CO lines.  Once this module is included in the CO lines file, the
     ; macro may be invoked to answer the phone and play the IVR menu.
     ;
     ; It also defines all of the sub-menus, including special sub-menus for
     ; off hours and holidays, as well as a sub-menu for doing a directory search
     ; by last name.
     ;
     ; Note that, before this module is included, you should define the global
     ; variable EXTPATTERN, which is used to define the pattern used to match
     ; extension numbers.  Typically, it is set to something like "6XX" or
     ; "5XXX".
     ;
     ;
     ; IVR attendant macro.  This starts up the IVR menu tree.  It checks the
     ; time and branches to the appropriate sub-menu for the date/time.  There
     ; is a special sub-menu for Dale.
     ;
     [macro-ivrmenutree]
     ; Answer the phone.
     exten => s,1,Answer()
     ; Dale always gets special treatment -- The Mental Health Hotline.
     exten => s,n,Gotoif($[${CALLERID(num)} = 1234567890]?ivrmental,s,1:)
     ; Check for the holidays first.  These override the regular hours.
     exten => s,n,GotoIfTime(*,*,4,jul?ivrholiday,s,1)
     ; If the office is closed, go to the closed greeting.
     exten => s,n,GotoIfTime(19:00-08:59,*,*,*?ivrclosed,s,1)
     ; Fall through to the regular, working hours greeting.
     exten => s,n,Goto(ivrworking,s,1)
     ;
     ; This is the regular, working hours menu.
     ;
     [ivrworking]
     exten => s,1,Background(ivr/thank-you-for-calling&\
                             ivr/if-you-know-dial-any-time&\
                             ivr/P1-for-dir-or-stay-on)
     exten => s,2,WaitExten(10)
     ; The user can press 0 for the operator.  This will make Dale happy.
     exten => 0,1,Goto(from-internal,0,1)
     ; The user can press 1 for the directory.
     exten => 1,1,Goto(ivrdirectory,s,1)
     ; If they don't press a valid key, give them an error.
     exten => i,1,Playback(ivr/not-valid-extension)
     exten => i,n,Goto(ivrworking,s,2)
     ; If the wait for a digit times out, transfer to the operator.
     exten => t,1,Playback(ivr/transfer-to-operator)
     exten => t,n,Goto(from-internal,0,1)
     ; Ring the extension that the user dialed (we do this by jumping to the
     ; from-internal context, as if one of the internal lines dialed an
     ; extension, so that we can use the same extension definitions found
     ; therein).
     exten => _${EXTPATTERN},1,Goto(from-internal,${EXTEN},1)
     ; We optionally enable the virtual extensions from outside lines, if the
     ; virtual outside switch is on.
     exten => _${VIRTPATTERN},1,Gotoif($[${EXISTS(${VIRTOUTSIDE})}]?\
                                       virtual-extensions,${EXTEN},1:)
     exten => _${VIRTPATTERN},n,Playback(sorry2)
     exten => _${VIRTPATTERN},n,Hangup()
     ; Define an extension that will get us into voicemail so that we can
     ; check it from outside.
     exten => ${EXTVMAIL},1,VoicemailMain()
     exten => ${EXTVMAIL},n,Hangup()
     ;
     ; This is the after hours menu.
     ;
     [ivrclosed]
     exten => s,1,Background(ivr/thank-you-for-calling&\
                             ivr/at-present-closed&\
                             ivr/if-you-know-dial-any-time&\
                             ivr/party-no-answer-voicemail&\
                             ivr/if-dont-know-P1-for-dir)
     exten => s,2,WaitExten(10)
     ; The user can press 1 for the directory
     exten => 1,1,Goto(ivrdirectory,s,1)
     ; If they don't press a valid key, give them an error.
     exten => i,1,Playback(ivr/not-valid-extension)
     exten => i,n,Goto(ivrclosed,s,2)
     ; If the wait for a digit times out, say goodbye and hang up.
     exten => t,1,Playback(ivr/hasta-la-vista)
     exten => t,n,Hangup()
     ; Ring the extension that the user dialed.
     exten => _${EXTPATTERN},1,Goto(from-internal,${EXTEN},1)
     ; We optionally enable the virtual extensions from outside lines, if the
     ; virtual outside switch is on.
     exten => _${VIRTPATTERN},1,Gotoif($[${EXISTS(${VIRTOUTSIDE})}]?\
                                       virtual-extensions,${EXTEN},1:)
     exten => _${VIRTPATTERN},n,Playback(sorry2)
     exten => _${VIRTPATTERN},n,Hangup()
     ; Define an extension that will get us into voicemail so that we can
     ; check it from outside.
     exten => ${EXTVMAIL},1,VoicemailMain()
     exten => ${EXTVMAIL},n,Hangup()
     ;
     ; This is the holiday menu.
     ;
     [ivrholiday]
     exten => s,1,Background(ivr/thank-you-for-calling&\
                             ivr/at-present-vacation&\
                             ivr/if-you-know-dial-any-time&\
                             ivr/party-no-answer-voicemail&\
                             ivr/if-dont-know-P1-for-dir)
     exten => s,2,WaitExten(10)
     ; The user can press 1 for the directory
     exten => 1,1,Goto(ivrdirectory,s,1)
     ; If they don't press a valid key, give them an error.
     exten => i,1,Playback(ivr/not-valid-extension)
     exten => i,n,Goto(ivrholiday,s,2)
     ; If the wait for a digit times out, say goodbye and hang up.
     exten => t,1,Playback(ivr/hasta-la-vista)
     exten => t,n,Hangup()
     ; Ring the extension that the user dialed.
     exten => _${EXTPATTERN},1,Goto(from-internal,${EXTEN},1)
     ; We optionally enable the virtual extensions from outside lines, if the
     ; virtual outside switch is on.
     exten => _${VIRTPATTERN},1,Gotoif($[${EXISTS(${VIRTOUTSIDE})}]?\
                                       virtual-extensions,${EXTEN},1:)
     exten => _${VIRTPATTERN},n,Playback(sorry2)
     exten => _${VIRTPATTERN},n,Hangup()
     ; Define an extension that will get us into voicemail so that we can
     ; check it from outside.
     exten => ${EXTVMAIL},1,VoicemailMain()
     exten => ${EXTVMAIL},n,Hangup()
     ;
     ; This is The Mental Health Hotline menu.
     ;
     [ivrmental]
     exten => s,1,Background(ivr/mental-health-hotline)
     exten => s,2,WaitExten(10)
     ; The user can press 0 for the operator.  This will make Dale happy.
     exten => 0,1,Goto(from-internal,0,1)
     ; The user can press 1 for the directory.
     exten => 1,1,Goto(ivrdirectory,s,1)
     ; If they don't press a valid key, give them an error.
     exten => i,1,Playback(ivr/not-valid-extension)
     exten => i,n,Goto(ivrmental,s,2)
     ; If the wait for a digit times out, say goodbye and hang up.
     exten => t,1,Playback(ivr/hasta-la-vista)
     exten => t,n,Hangup()
     ; Ring the extension that the user dialed (we do this by jumping to the
     ; from-internal context, as if one of the internal lines dialed an
     ; extension, so that we can use the same extension definitions found
     ; therein).
     exten => _${EXTPATTERN},1,Goto(from-internal,${EXTEN},1)
     exten => _${EXTPATTERN},n,Hangup()
     ;
     ; This is the IVR directory, where we look up the party's name and return
     ; their extension.  We'll ask the caller to enter the first three letters
     ; of the party's last name.
     ;
     [ivrdirectory]
     exten => s,1,Background(ivr/dir-dial-three-letters)
     exten => s,2,WaitExten(20)
     ; If they don't dial a valid name, give them an error.
     exten => i,1,Playback(ivr/dir-dont-recognize-name)
     exten => i,n,Goto(ivrdirectory,s,2)
     ; If the wait for a name times out, say goodbye and hang up.
     exten => t,1,Playback(ivr/goodbye)
     exten => t,n,Hangup()
     ; Here, we can use a pattern match to match the first three letters of
     ; the party's last name.  If the first one or two letters makes it unique,
     ; you can use the anymatch digit to blow away whatever else they type.
     ; Alternately, there's no need to listen for more digits, if you don't
     ; want to but it gives the impression we're a big-time organization.
     exten => _2XX,1,Playback(ivr/transfer-to-norm-cole)
     exten => _2XX,n,Goto(from-internal,601,1)
     exten => _9XX,1,Playback(ivr/transfer-to-ed-wilson)
     exten => _9XX,n,Goto(from-internal,600,1)

Current Weather

If you'd like to be able to give your Asterisk users the current weather and optionally the forecast, you can install the Weather::Underground module from CPAN and then use a Perl script to pull the weather conditions from Wunderground on a periodic basis. The weather conditions can be converted, using Festival, to a wave file which can be played by Asterisk, to a user.

Begin by installing the Weather::Underground Perl module:

     su
     perl -MCPAN -e shell
     install Weather::Underground

Then, put this perl script in a convenient location.

/etc/asterisk/local/FetchWeather.pl:

     #!/usr/bin/perl
     #
     # Get the local weather from Weather Underground.
     #
     #
     # Usage
     # -----
     #
     # FetchWeather.pl [--Forecast] [--Place=place] [output_file]
     #
     # Forecast          Switch indicating that the forecast is to be retrieved
     #                   as well as the weather data.  If the forecast can be
     #                   fetched, it will be appended to the current conditions.
     #
     # Place             The name of place whose weather data is to be retrieved.
     #                   This should be a string like "Norwood, Massachusetts".
     #                   The default place name is defined by the $DEFAULTPLACE
     #                   variable herein ("Norwood, Massachusetts").
     #
     # output_file       Optional output file name prefix.  Two files will be
     #                   generated, one with a ".txt" extension and one with a
     #                   ".wav" extension.  The prefix given here will be used to
     #                   name those files.  The default file name prefix is defined
     #                   by the $DEFAULTFILE variable herein
     #                   ("/home/monitor/wunderground/Weather-Norwood").
     #
     #
     # Description
     # -----------
     #
     # This program gets the current weather information (and optionally the
     # forecast) from Wunderground and produces a wave file which can be played by
     # Asterisk, when the user asks for the weather.
     #
     ##############################################################################
     #
     # Include all of the modules that we use.
     #
     use Getopt::Long;
     use LWP::UserAgent;                     # One include covers all of them!
     #db use LWP::Debug qw(+);               # LWP debugging, if needed
     use URI::URL;                           # Or, perhaps not
     use Weather::Underground;
     #
     # Local information, if not specified.
     #
     my $DEFAULTPLACE = "Norwood, Massachusetts";
     my $DEFAULTFILE = "/home/monitor/wunderground/Weather-Norwood";
     #
     # Wunderground XML API URL, used to pull forecasts.  Don't include the
     # "http://" on the front.  We add it automatically.
     #
     my $FORECASTURL = "api.wunderground.com/auto/wui/geo/ForecastXML/" .
                           "index.xml?query=";
     #
     # Table to convert Wunderground wind directions into something Festival
     # can say.
     #
     my %WindDir =
         (
         "N" => "North",
         "NNE" => "North North East",
         "NE" => "North East",
         "ENE" => "East North East",
         "E" => "East",
         "ESE" => "East South East",
         "SE" => "South East",
         "SSE" => "South South East",
         "S" => "South",
         "SSW" => "South South West",
         "SW" => "South West",
         "WSW" => "West South West",
         "W" => "West",
         "WNW" => "West North West",
         "NW" => "North West",
         "NNW" => "North North West"
         );
     #
     # Options.
     #
     my $Place = "";                         # Place to retrieve weather for
     my $OutputFile = "";                    # Prefix to use for generated files
     #
     # Option switches.
     #
     $WantForecast = 0;                      # Set true if we want the forecast
     #
     # The forecast, if there is one.
     #
     $Forecast = "";
     #
     # Process the command line options.  If this fails, give a summary of the
     # program's usage.
     #
     ProcessOptions() || UsageInfo();
     #
     # Use the default place, if we weren't given one.
     #
     $Place = $DEFAULTPLACE if ($Place eq "");
     #
     # Get the output file prefix, if any.
     #
     $OutputFile = shift;
     $OutputFile = $DEFAULTFILE if (length($OutputFile) <= 0);
     #
     # Open the output file.
     #
     open(TXTFILE, ">".$OutputFile.".txt")
         or die("Error, could not open ".$OutputFile.".txt: ".$@."\n");
     #
     # Get the weather from Wunderground.
     #
     my $WeatherHand = Weather::Underground->new(
         place => $Place,
         debug => 0)
         || die("Error, could not create new weather object: ".$@."\n");
     $Weather = $WeatherHand->get_weather()
         || die("Error, calling get_weather() failed: $@\n");
     #
     # Dump the weather data to a text file.
     #
     my ($Key, $Value);
     print(TXTFILE "Weather search for ".$Place.":\n\n");
     foreach (@$Weather)
         {
         print(TXTFILE "Matched weather for ".${%{$_}}{place}.":\n");
      while (($Key, $Value) = each %{$})
          {
          print(TXTFILE "\t$Key = $Value\n");
          }
      }

#
# If the user wants the forecast, see if we can get it. #
if ($WantForecast)

      {
      my ($URLFCast, $Req, $Resp, $FCast);
      #
      # Set up the HTTP headers used to fetch the forecast.  Just in case
      # they're watching, we pretend to be Mozilla.
      #
      my $HTTPHdrs = HTTP::Headers->new(
          'Accept' => 'text/xml,application/xml,application/xhtml+xml,' .
                      'text/html;q=0.9,text/plain;q=0.8,*/;q=0.5',
          'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,;q=0.7',
          'Accept-Language' => 'en-us,en;q=0.5',
          'User-Agent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.0; ' .
                          'en-US; rv:1.7.13) Gecko/20060414');
      #
      # Set up a user agent to do the fetching.  The agent looks like a
      # full-fledged browser.  And, it handles just about everything the remote
      # site can throw at it.
      #
      my $UserAgent = LWP::UserAgent->new(
          requestsredirectable => ['GET', 'HEAD', 'POST']);
      #
      # Build a URL that we can use to look up the forecast.
      #
      $URLFCast = $Place;
      $URLFCast =~ s/^\s+//; $URLFCast =~ s/\s+$//;
      $URLFCast =~ s/\s,\s+/,/g; $URLFCast =~ s/\s+/+/g;
      $URLFCast = URI::URL->new("http://".$FORECASTURL.$URLFCast);
      #
      # See if we can get the forecast.
      #
      $Req = HTTP::Request->new("GET", $URLFCast, $HTTPHdrs);
      $Resp = $UserAgent->request($Req);
      #
      # If we got a forecast, process all of the day forecasts within.
      #
      if ($Resp->is_success)
          {
          $Resp->content =~ /\<txt_forecast.?\>(.+?)\<\/txt_forecast/gis;
          $_ = $1;
          #
          # Loop through all of the day forecasts.
          #
          while (m/\<forecastday.?\>(.+?)\<\/forecastday/gis)
              {
              $FCast = $1;
              #
              # If we have a title, compose the forecast for that period.
              #
              if ($FCast =~ /\<title.?\>(.+?)\<\/title/)
                  {
                  $Forecast .= ("Forecast for ".lc($1));
                  if ($FCast =~ /\<fcttext.?\>(.+?)\<\/fcttext/)
                      {
                      #
                      # The droid has trouble with a few expressions and some
                      # of the punctuation that Wunderground likes to use.
                      #
                      $FCast = $1;
                      $FCast =~ s/\s\r?\n\s/ /gs;
                      $FCast =~ s/\s\.\.\.\s*/, /gs;
                      $FCast =~ s/\'/\'\'/gs;
                      $FCast =~ s/\bmph\b/miles per hour/gis;
                      $FCast =~ s/\bmph\.]/miles per hour./gis;
                      $Forecast .= (". ".$FCast." ");
                      }
                  else { $Forecast .= " is not available. "; }
                  }
              }
          }
      }

#
# Turn the wind speed and direction into English. #
my ($Wind);

     if ($Weather->[0]->{wind_milesperhour} eq "0.0")
         { $Wind = "the winds are calm"; }
     else
         {
         $Wind = "wind direction is ".
             ((exists($WindDir{$Weather->[0]->{wind_direction}}))
                 ? $WindDir{$Weather->[0]->{wind_direction}}
                     : $Weather->[0]->{wind_direction})."  " .
             "at ".$Weather->[0]->{wind_milesperhour}." miles per hour";
         }
     #
     # Figure out how to end the weather information.
     #
     my ($Sec, $Min, $Hour, $Finish);
     ($Sec, $Min, $Hour) = localtime(time());
     if (($Hour >= 23) || (($Hour >= 0) && ($Hour < 4)))
       { $Finish = "good night"; }
     elsif ($Hour >= 18) { $Finish = "pleasant evening"; }
     else { $Finish = "pleasant day"; }
     #
     # Let's get Festival to turn this into a wave file that Asterisk can use.
     #
     system("echo 'Weather for ".$Place.". " .
         "Current temperature is ".$Weather->[0]->{temperature_fahrenheit}." " .
             "degrees, " .
         "conditions are ".$Weather->[0]->{conditions}.", " .
         "visibility is ".$Weather->[0]->{visibility_miles}." miles, and " .
         $Wind.". " .
         "Current humidity is ".$Weather->[0]->{humidity}." percent, " .
         "and the dew point is ".$Weather->[0]->{dewpoint_fahrenheit}.". " .
         $Forecast .
         "Have a ".$Finish.".' " .
         "| /usr/bin/text2wave -F 8000 -o ".$OutputFile.".wav");
     #
     # We're getting outta here.
     #
     close(TXTFILE); exit 0;
     ##############################################################################
     sub ProcessOptions
     #
     # Process command line options and set the appropriate variables.
     #
     {
     my ($Result);
     #
     # Get options using standard option processor.
     #
     $Result = &GetOptions(
         "Forecast", \$WantForecast,         # Switch for forecast as well
         "Place=s", \$Place);                # The place to get weather for
     #
     # Return success if options gotten OK.
     #
     return ($Result);
     }
     ##############################################################################
     sub UsageInfo
     #
     # Routine to display program usage information.  Called whenever the command
     # line parameters don't make sense.
     #
     {
     #
     # Exit after giving help.
     #
     print <<USAGE ;
     Usage: $0
          [--Forecast] [--Place=place] [output_file]
     Forecast       Use this switch if you want the forecast as well.
     Place          The name of place whose weather data is to be retrieved.
     output_file    Optional output file name prefix.
     USAGE
     exit 0;
     }

Make sure you've installed Festival (see the Using Festival For IVR Menus section, if you haven't). Once you have, you can add an entry to your crontab to fetch the current weather conditions periodically (we do it every fifteen minutes, at x:05, x:20, x:35 and x:50).

/etc/crontab:

.

       .

# Get the current weather conditions and forecast from Wunderground every # 15 minutes.
05,20,35,50 * * * * root /etc/asterisk/local/FetchWeather.pl \

                           --Forecast --Place="Boston, Massachusetts" \
                           /etc/asterisk/local/Weather-Boston \
                           >/dev/null 2>&1

For convenience sake, put a symlink in the local Asterisk directory to wherever you generate the current weather wave file so that the dial plan can access it:

     su
     ln -s /etc/asterisk/local/Weather-Boston.wav /etc/asterisk/local/weather.wav

Adding Special Features To The Dialplan

Before we begin, some features may be provided by the outside lines dial plan (see the A Dial Plan For Outside Lines section), such as siezing a particular outside CO line to place a call, using the special feature code "42". Other features such as being able to transfer a call directly to a voicemail box by dialing an extension prefixed by "" may be provided by the local extensions dial plan (see the Configuring Local DAHDI FXS/FXO Channels section, for example). These special feature codes should not be reused herein, so check for them before you begin. If you used the SPECIALCODE variable to initiate such code sequences, you can simply search all the local configuration files with grep to determine what the already-assigned special feature codes are.

Another thing to consider is to try to copy standard telephone feature codes to accomodate people's predefined notions of how things should work. They have probably been using specific codes for special features, for years, both with their home telephone service and/or at work. To make it easy for them to remember what the special feature codes are, choose codes that are already in their memories, based on what are used by the local PhoneCo and or popular version of Asterisk, phone switches, etc. Here is a list of North American CLASS codes that you should consider using:

     http://www.voip-info.org/wiki/view/CLASS

From that list, we've chosen some feature codes that you might want to implement. Note that we use the SPECIALCODE variable (defined in the local variables file) to initiate the special feature code. For purposes of discussion herein, we assume it is set to "*".

>>>>>>
MeetMe - To access a MeetMe conference room, dial 8 + your extension. (i.e. for extension 200, dial 8200). This will create a MeetMe room for you. Once the room is live, you can then transfer callers into it. <<<<<<

Directory - Allow the user to dial *411 to get the directory. Not strictly legit, according to the North American CLASS codes but we're not implementing six-way conference calling so who cares.

>>>>>>
CallPickup

CallReturn - Dial *69 to return the last incoming call.

FollowMeForward -
<<<<<<

CallForwarding - Dial *72 to enable Call Forwarding. Implemented by an Asterisk database variable that holds the current Call Forward extension. Note that you should check for loops (i.e. forward to forward) both in the UI and in the routine that actually dials extensions.

CancelCallForwarding - Dial *73 to disable Call Forwarding.

DoNotDisturb - Dial *78 to enable Do Not Disturb (all calls go immediately to voicemail). Implemented by an Asterisk database variable that holds the current state (off/on) of the Do Not Disturb setting.

CancelDoNotDisturb - Dial *79 to disable Do Not Disturb (calls ring the extension in the normal manner).

CallForwardOnBusy - Dial *90 to forward incoming calls to another extension if your extension is busy. We'll also forward if there is no answer on your extension.

CancelCallForwardOnBusy - Dial *91 to cancel forwarding of incoming calls to another extension if your extension is busy.

MessageCenter - Dial *97 to pick up the voicemail for your extension from Comedian (voicemail). Dialing the Message Center with this code does not ask for a password or extension.

MessageCenterNumber - Dial *98 to be prompted for a mailbox number and pick up the voicemail for that mailbox from Comedian (voicemail).

Time - Dial *60 to get the current system time. This takes advantage of the time routine that is included in the extensions.conf file (as described in the Basic Asterisk Configuration section).

Weather - Dial *61 to get the local weather report. This uses the current weather conditions file that is generated by the Perl script run by cron, every fifteen minutes, as described in the Current Weather section (above).

WakeupCall - To schedule a wakeup call to your extension, dial *62. This adds a call file to the /var/spool/asterisk/outgoing directory to cause Asterisk to place the call at the future wakeup time. The Asterisk database is used to save a feature code that causes daily wakeup calls to be scheduled forever (until cancelled). The call is placed using the local channel so that we can jump into the internal context to ring the extension.

Here is more information about Asterisk Call Files:

     http://www.voip-info.org/tiki-index.php?page=Asterisk+auto-dial+out

And, here is further information about the Asterisk local channel:

     http://www.voip-info.org/wiki/view/Asterisk+local+channels

CancelWakeupCall - To cancel a previously scheduled wakeup call, dial *63. This deletes all files belonging to your extension from the /var/spool/asterisk/outgoing directory and removes the feature code from the Asterisk database.

EchoTest - Dial *43 for an echo test. Essentially, this just runs the Asterisk Echo() application.

FeatureList - Dial *46 for a feature list. This lists all of the state variables for the extension in the Asterisk database.

ClearFeatures - Dial *47 to clear all features on an extension. This deletes all of the state variables in the Asterisk database for the extension.

>>>>>>
Note about MySQL vs. dbm ....
<<<<<<

Special Extensions For External Calls

If we add features like call forwarding, we may wish to forward calls to outside lines, if the users are not present to answer their phones. Or, we may wish to define special extensions that place outside calls that can be accessed from the IVR menu (allowing an outside caller to select a number of predefined extensions, that turn around and place outside calls, is much safer than enabling DISA). Or, using IAX2 or VOIP, we may wish to define extensions that ring remote switches or faraway lands. There are a lot of reasons why one might want to define an extension (that can be used anywhere an extension can be used) that does something special.

We'll call this kind of extension a virtual extension, since it doesn't actually connect to any physical extension. We've chosen to enable this feature by setting a number of variables in the variables.conf file:

.

       .

; Pattern used to match any virtual extensions. This pattern should be ; defined whether you use virtual extensions or not. You can pick some ; bogus pattern (such as 999) if you don't use virtual extensions, but it ; should be OK the way it is.
;
; Optional switch that turns on virtual extensions from inside (i.e. local ; extensions).
;
; Optional switch used to let outside callers through to the inside so ; that they can dial the virtual extensions too. You may or may not want ; to do this, depending on what the virtual extensions do. VIRTPATTERN=8[0-8]X
VIRTINSIDE=1
VIRTOUTSIDE=1

These three variables define what the virtual extensions look like and whether they can be invoked from within the system and/or from outside the system. They are checked at the approprate places in ivr-menutree.conf and dahdi-extensions.conf. If virtual extensions are enabled, they are invoked wherever an extension is dialed by jumping to the virtual-extensions context, which we've chosen to encapsulate in a single file plan-virtualexts.conf.

/etc/asterisk/local/plan-virtualexts.conf:

     ; local/plan-virtualexts.conf - Dial plan for virtual extensions.
     ;
     ; This module is included at the appropriate spot in the dialplan (defined
     ; by extensions.conf).  It defines the dial plan for virtual extensions
     ; (i.e. special extensions that can make outside calls, connect to other
     ; Asterisk switches, make VOIP calls, etc.).
     ;
     ;
     ; Here we'll define a context with all of the virtual extensions.  Note
     ; that these extensions don't actually need to have any physical, local
     ; channel attached to them (hence the name virtual).  Instead, they may
     ; dial remote locations using IAX2, SIP or the PSTN.
     ;
     ; This context can be conditionally branched to by the IVR menu tree (if
     ; the virtual extensions are to be allowed from the outside lines) or by
     ; the local, inside lines, (if virtual extensions are to be allowed from
     ; inside lines).  For example:
     ;
     ;   exten => _${VIRTPATTERN},1,Gotoif($[${EXISTS(${VIRTOUTSIDE})}]?\
     ;                                     virtual-extensions,${EXTEN},1:)
     ;   exten => _${VIRTPATTERN},n,Playback(sorry2)
     ;   exten => _${VIRTPATTERN},n,Hangup()
     ;
     ; or
     ;
     ;   exten => _${VIRTPATTERN},1,Gotoif($[${EXISTS(${VIRTINSIDE})}]?\
     ;                                     virtual-extensions,${EXTEN},1:)
     ;   exten => _${VIRTPATTERN},n,Playback(sorry2)
     ;   exten => _${VIRTPATTERN},n,Hangup()
     ;
     ; This allows the outside lines to dial the virtual extensions and/or the
     ; local extensions to forward to or dial the virtual extensions.
     ;
     ; Virtual extensions can be used to dial complicated dialing sequences
     ; easily (i.e. like speed dial) or to transfer calls to outside phones
     ; via call forward or anywhere else you can use an extension.  But, beware
     ; the consequences if you open this up to the outside world and some yahoo
     ; figures out how to use it to their advantage.  In other words, think
     ; carefully about this before you open up DISA to the world.
     ;
     [virtual-extensions]
     ; The 801 virtual extension dials 201-123-4567 using the outside VOIP group.
     exten => 801,1,Goto(outside-virtgroup,2011234567,1)
     ; The 802 virtual extension dials 201-123-7654 using the outside VOIP group.
     exten => 802,1,Goto(outside-virtgroup,2011237654,1)
     ; The 810 virtual extension dials 416-123-4567 using the outside VOIP group.
     exten => 810,1,Goto(outside-virtgroup,4161234567,1)
     ; The 811 virtual extension dials 416-567-1234 using the outside VOIP group.
     exten => 811,1,Goto(outside-virtgroup,4165671234,1)
     ; In this context, we need to define the invalid extension, since the
     ; caller may enter one that is invalid.  Let's give them some kind of
     ; noncommittal error message.
     exten => i,1,Playback(sorry2)
     exten => i,n,Hangup()
     ;
     ; Context that defines the outside hunt group used by the virtual
     ; extensions.  This hunt group can be used by the virtual extensions to
     ; dial out on special lines (i.e. different from those used for regular
     ; outside dialing) only.  We could use a DAHDI group but this way we get
     ; to use lines out of order, resequence them at will, etc.
     ;
     [outside-virtgroup]
     exten => _X.,n,Dial(dahdi/12/${EXTEN})
     exten => _X.,1,Dial(dahdi/11/${EXTEN})
     exten => _X.,n,Playback(sorry2)
     exten => _X.,n,Hangup()

>>>>>>
Setting Up Voicemail


/var/spool/asterisk/voicemail/default/1234

Store in IMAP server or ODBC database. Why bother?

MWI should work if you have analog phones on your FXS ports that have message waiting lamps, and the correct vmail box is set in dahdi-channels.conf.

Only need this if vmail changed externally.

;pollmailboxes=no ; If mailboxes are changed anywhere outside of app_voice\ mail,

;                    ; then this option must be enabled for MWI to work.  This
;                    ; enables polling mailboxes for changes.  Normally, it wil\
l
;                    ; expect that changes are only made when someone called in
;                    ; to one of the voicemail applications.
;                    ;   Examples of situations that would require this option \
are
;                    ; web interfaces to voicemail or an email client in the ca\
se
;                    ; of using IMAP storage.
;
;pollfreq=30         ;   If the "pollmailboxes" option is enabled, this option
;                    ; sets the polling frequency.  The default is once every
;                    ; 30 seconds.

<<<<<<

Setting Up Music On Hold

Most probably, the music on hold that you will want to use will come as an MP3 file. This is definitely not what you want Asterisk to use for playing music on hold. Asterisk will use up a whole bunch of cycles converting the MP3 file to the proper format for the codec in use on the caller's line. The volume may be too low or too high and/or the dynamic range may be too large. The sampling rate will undoubtedly be more than the 8 kBps that is optimum for telephony and the high frequency components will be clipped. So, the best approach is to convert all of the MP3 files into the proper format and "tune them up" along the way.

In the nothing is ever simple department, we begin this process with lame. We'll use it to convert the MP3 files to WAV files. But, before we can do that, we have to install it on the system. But, before we can do that, we have to add the repository where it lives to the yum repository list. To do that, fire up your favorite text editor, as the super-dooper user, and edit the repository list. Add the ATrpms 3rd party repository to the end.

/etc/yum.repos.d/CentOS-Base.repo:

.

       .

#atrpms - ATrpms 3rd party RPMs - Only the stable RPMs are used. [atrpms]
name=CentOS-$releasever - ATrpms
baseurl=http://dl.atrpms.net/el$releasever-$basearch/atrpms/stable gpgcheck=1
gpgkey=http://ATrpms.net/RPM-GPG-KEY.atrpms

Now, we can install lame:

     su
     yum install lame

Next, try converting an MP3 to a WAV file.

     lame --decode AceInTheHole.mp3 AceInTheHole.temp.wav

Once we have it converted to a WAV file, we can use sox to resample it to 8 kHz and apply the compander to limit the dynamic range and cut down the volume (note that we're using a slightly different compander function than used for IVR menus in the Converting Recorded Sounds For Use By Asterisk section because music has more potential to blow one's ears off). The (Butterworth) lowpass filter rolls off the higher notes (above 4 kHz) which is desirable for telephony:

     sox AceInTheHole.temp.wav -r 8000 -c 1 -s -w AceInTheHole.wav \
         lowpass 4000 \
         compand 0.02,0.05 -60,-60,-30,-12,-20,-10,-5,-10,-2,-10 -10 -9 0.05 \
         resample -ql

You can test out your new music on hold file by copying it to Asterisk's moh directory (by default, /var/lib/asterisk/moh), and restarting Asterisk:

     su
     cp AceInTheHole.wav /var/lib/asterisk/moh
     /etc/init.d/asterisk restart

This should get you started with music on hold. If you make a call an place it on hold, you should hear the music. The output volume (if too loud) can be adjusted by reducing all of the compand filter results by a couple of dB, like this, for example:

     sox AceInTheHole.temp.wav -r 8000 -c 1 -s -w AceInTheHole.wav \
         lowpass 4000 \
         compand 0.02,0.05 -60,-60,-30,-14,-20,-12,-5,-12,-2,-12 -12 -11 0.05 \
         resample -ql

If you have a boat load of hold music that you wish to convert, this script should prove useful.

MP3ToMoH:

     #!/bin/sh
     # Process all of the MP3 files.
     for i in *.mp3; do
         mohfile=`basename $i .mp3`
      # Pass each MP3 through lame.
      echo Preprocessing $i
      lame --decode $i ${mohfile}.temp.wav
      # Pass each converted WAV file through sox.
      echo Converting to ${mohfile}.wav
      sox ${mohfile}.temp.wav -r 8000 -c 1 -s -w ${mohfile}.wav \
          lowpass 4000 \
          compand 0.02,0.05 -60,-60,-30,-12,-20,-10,-5,-10,-2,-10 -10 -9 0.05 \
          resample -ql
      # Clean up.
      rm -f ${mohfile}.temp.wav

done

After you change the permit-skis on the new script to allow it to be executed, change to the directory where all of the music on hold files are found and run the script (here, we assume the script and the files are in the same directory):

     chmod ugo+x MP3ToMoH
     ./MP3ToMoH

If you have many files, go for a serious cup of coffee. Converting them will take a long, long time. Once the conversions are all done, copy all of the new music on hold files to Asterisk's moh directory:

     su
     cp *.wav /var/lib/asterisk/moh

Edit the music on hold configuration file to change the sort order to random. Otherwise, there's not much point to having multiple music on hold files, since Asterisk will always start with the first one (which is all most users will ever hear). This is pretty much the only change needed to the music on hold configuration.

/etc/asterisk/musiconhold.conf:

.

       .

[default]
mode=files
directory=moh
sort=random

Once you've saved thise changes, restart Asterisk so that it will read all of the music on hold files into its internal list:

     /etc/init.d/asterisk restart

Sendmail Setup

So, "Why do you need to set up sendmail on a telephone switch," you ask? Its a good question. The short answer is that the voicemail application will send you an email notification (with the voicemail attached), whenever a caller leaves you a message. Its worth it for just that feature alone.

But, you may also want to monitor the status of the switch's attached UPS with NUT and send yourself email when a problem occurs. Other monitoring applications can also take advantage of sendmail to send email when certain events happen. Consequently, setting up sendmail makes good sense.

We're assuming that your Asterisk switch is a local machine and that it will deliver mail via your main mail server. You need to hack sendmail.mc on the Asterisk switch and then restart its local sendmail. Uncomment the SMART_HOST line in the macro file (probably /etc/mail/sendmail.mc) and aim it at your main mail server:

     define(`SMART_HOST',`jump-gate')

Rebuild the sendmail.cf file:

     /usr/bin/m4 sendmail.mc >sendmail.cf

Restart sendmail:

     /etc/rc.d/init.d/sendmail stop
     /etc/rc.d/init.d/sendmail start

You should now be able to send mail to the main mail server from the local machine (if relaying is denied, you may need to add the local machine's address to relay-domains on the main mail server). You might want to alias the following users in /etc/aliases on the local machine:

     root: joeblow@mydomain.com

As long as the aliased name contains a domain name, it will be forwarded by the local sendmail to the SMART_HOST. If the domain name is marked as a local domain on the main mail server, the mail will get delivered there. Note that you need to recycle sendmail on the local machine when you add aliases to /etc/aliases.

>>>>>>
Using NTP For Time Synchronization


<<<<<<

>>>>>>
Setting Up A Festival Server


<<<<<<

>>>>>>
Set up the Web UI
Get rid of the stupid manager and built-in HTTP Consider security implications.
<<<<<<

Network UPS Tools (NUT)

Your telephone switch is a pretty important piece of hardware so it is a good idea to power it with a UPS. This being the case, you may want to monitor it with the Network UPS Tools (NUT) package. This section tells you how to set up NUT and a UPS.

But, before we get on with setting up a UPS, the quick command to set the UPS battery date, after changing batteries is (note that /usr/local/ups/bin may just be /bin on some installations):

     /usr/local/ups/bin/upsrw -s battery.date=mm/dd/yy -u username -p password \
       upsname@localhost

This presupposes that the username and password are set in upsd.users and that "actions = set" are specified for the user. Also, upsd and the appropriate UPS driver must be up and running for the UPS in question.

Also, if you want to get rid of the "Change the battery" message and the red light on the UPS panel, you can force the battery test to be rerun immediately. However, before you do this, wait for three or four hours after the new battery is installed to give the UPS time to charge it first. Then, run the battery test, which will reset everything, if the new battery is OK. To do this, use:

     /usr/local/ups/bin/upscmd -u username -p password upsname@localhost \
       test.battery.start

The battery test should run (you'll see all of the messages it usually generates) for a few seconds and all will be well.

OK, having dispensed with the pleasantries, let's get on with the full installation of NUT. For some reason, in seems that CentOS does not supply a set of packages for NUT. Mind you, you don't have to take our word for it. You can try installing NUT by fetching and installing these packages:

     su
     yum install nut nut-client nut-cgi

If that doesn't work, we'll do the install the hard way. Start by fetching the latest release of NUT from http://www.networkupstools.org/. Unzip and untar the distro to a directory of your choice:

     mkdir /rpm/ups
     cp nut-2.2.1.tar.gz /rpm/ups
     cd /rpm/ups
     tar -xvzf nut-2.2.1.tar.gz

Create a new user, under which the UPS tools can run (otherwise they run as "nobody"). For example:

     /usr/sbin/useradd -c "UPS Monitoring Tools" -M -s /sbin/nologin ups

Add a directory where the UPS tools can store state information and make the new user the owner:

     su
     mkdir /var/state  (if necessary)
     mkdir /var/state/ups
     chown ups:ups /var/state/ups
     chmod u=rwx,g=rx,o= /var/state/ups

Change directory to the distribution directory and configure the build scripts:

     ./configure --with-user=ups --with-group=ups --sysconfdir=/etc/ups \
                 --mandir=/usr/share/man --with-cgi

Apply the patches for local source code changes or copy the local source code, if any, to the install directory before building. Currently, there are hacks to upsplot.pl, upsstats.c and upsmon.c (including upsmon.h). Put upsplot.pl in /etc/ups, after the "make install" is run (see below). Put the hacked ".c" and ".h" files in ./clients before doing the build.

Compile the source from the build directory, according to the INSTALL file and then install the build. Basically, you do the following, in the top level directory (but read the next two paragraphs first):

     make
     su
     make install

This should put most things in:

     /etc/ups
     /usr/local/ups/bin
     /usr/local/ups/sbin
     /usr/share/man

If you receive errors about "gd" during configure, go get it and install it before continuing. On some systems, you can do:

     su
     yum install gd gd-devel php-gd

Otherwise, you can get the source here:

     http://www.libgd.org/

In the event that you need libpng or zlib in order to compile gd, they can be found at these URLs:

     http://www.libpng.org/pub/png/pngcode.html
     http://www.gzip.org/zlib/

Also, if you're going to be using upsplot.pl, you will need to make sure that GD.pm is available. If not, you should install it from CPAN:

     perl -MCPAN -e shell
     install GD

Hack the udev rules for the tty device, that the UPS will be connected to, to give permissions to the ups user. These can be found in the /etc/udev/rules.d dirctory, typically in the file /etc/udev/rules.d/50-udev.rules. Add something to the effect of:

     # Special case for the UPS devices.
     KERNEL=="ttyS2",                NAME="%k", GROUP="ups", MODE="0660",
                                     OPTIONS="last_rule"

Add these lines before the general case rules for the tty devices.

Alternately, the best place to add your udev rules for NUT is in a separate rules file that you make up just for this purpose (that way, subsequent releases of NUT or the OS won't monkey with your rules for the serial port). We suggest calling the file "52_nut-ttyups.rules. It should be created in the /etc/udev/rules.d directory. You should add something like the following to that file, depending on which serial port you need to use:

     # udev rules for NUT serial drivers
     # Special case for the UPS devices
     KERNEL=="ttyS1", group="ups", MODE="0660"

/etc/ups/ups.conf:

Copy the file ups.conf.sample and hack it to set up the machine/UPS configuration, per the instructions in the INSTALL file. For an APC SmartUPS, the following config information applies:

     [bevatron]
          driver = apcsmart
          port = /dev/ttyS0

/etc/ups/upsd.conf:

On older versions of NUT, copy the file upsd.conf.sample. It is probably OK as is but, if you'd like to be able to monitor this machine's UPS from your home network, add something like:

     ACL homeworld 192.168.1.0/24
     ACCESS grant monitor homeworld

Or, if you don't want to be messing about and you want to be able to do anything from the local network (e.g. run battery tests), try replacing the existing rules with:

     ACL all 0.0.0.0/0
     ACL localhost 127.0.0.1/32
     ACL homeworld 192.168.1.0/24
     ACCEPT homeworld
     ACCEPT localhost
     REJECT all

On newer versions of NUT (e.g. version 2.4), copy the upsd.conf.sample file and replace the LISTEN directives with:

     LISTEN 127.0.0.1
     LISTEN 192.168.1.123 3493

You should use the actual IP address of the machine where NUT is being installed in place of 192.168.1.123 (above). If the machine has two or more NICs and you want NUT to listen on all of them, add additional LISTEN directives for the IP address of each of them.

Note that, if you have an older config file and are upgrading to a newer version of NUT, you will need to remove all of the ACL, ACCEPT and REJECT rules and simply use LISTEN. Apparently, the designers have decided that NUT should get out of the security business and leave everything to the firewall. Consequently, the rules are no longer accepted and LISTEN is simply used to tell NUT which port to listen on.

/etc/ups/upsd.users:

Copy the file upsd.users.sample. Add a user that will allow upsmon on the local host to shut down the machine if the power goes south as well as do periodic battery tests:

     [lmmonitor]
         password = ItsASecret
         allowfrom = localhost
         instcmds = test.battery.start
         actions = set
         upsmon master

/etc/ups/upsmon.conf:

Copy the file upsmon.conf.sample. Change the group ownership to the ups user and give it group permissions:

     chgrp ups /etc/ups/upsmon.conf
     chmod g+rw /etc/ups/upsmon.conf

Make the following changes to the file to begin monitoring the UPS:

     NOTIFYCMD /etc/ups/notify
     RUN_AS_USER ups
     MONITOR bevatron@localhost 1 lmmonitor ItsASecret master
     NOTIFYFLAG COMMBAD  SYSLOG+EXEC
     NOTIFYFLAG COMMOK   SYSLOG+EXEC
     NOTIFYFLAG FSD      SYSLOG+EXEC
     NOTIFYFLAG LOWBATT  SYSLOG+EXEC
     NOTIFYFLAG NOCOMM   SYSLOG+EXEC
     NOTIFYFLAG ONBATT   SYSLOG+WALL+EXEC
     NOTIFYFLAG ONLINE   SYSLOG+WALL+EXEC
     NOTIFYFLAG REPLBATT SYSLOG+EXEC
     NOTIFYFLAG SHUTDOWN SYSLOG+WALL+EXEC
     NOTIFYFLAG OVERTEMP SYSLOG+EXEC

If you want to monitor UPS temperature and you've applied the proper hacks (or the temperature hacks are included in your version of the source), add the following:

     # --------------------------------------------------------------------------
     # UPSOVERTEMP - Temperature (in Celcius) which is too high for operation
     #
     # upsmon will check all UPS that return temperature information against this
     # value.  If the UPS temperature exceeds this value, an OVERTEMP notification
     # will be generated.
     #
     # Note that certain UPS are renown for cooking and even burning up batteries
     # (some reports of spectacular battery fires have been received).  From
     # actual observed log data, it appears that prior to burning up the
     # batteries, the UPS internal temperature rises significantly.  Hence,
     # monitoring the UPS temperature can be a valuable tool towards detecting
     # battery cooking, before the UPS burns the place down (the UPS is supposed
     # to solve problems, not cause them, isn't it).
     #
     # Once again, typical observed internal temperatures are in the 40 to 50
     # degree Celcius range.  Observed temperatures of 80 degrees Celcius prior
     # to an actual battery failure are indicative of pending failure.  Thus, to
     # be safe, the the UPSOVERTEMP value should be set in the 60-70 degree
     # range.
     UPSOVERTEMP 60.0

/etc/sysconfig/upsd:

If you use any of the following scripts, you will need to either create or update /etc/sysconfig/upsd. This file contains the options that tell the UPS scripts which UPS boxes to monitor, what to do about logging, etc. Here is an example that can be used for a standalone Asterisk system:

     #
     # Configuration for the NUT UPS monitoring and power down daemons.
     #
     # The list of UPS boxes to be controlled are set by UPS_BOXES, for example:
     #
     #      "My_UPS"
     #      "UPS-1 UPS-2"
     #
     # For clustering support, the list of UPS boxes on the primary server is set
     # by UPS_BOXES_PRIMARY and the list of UPS boxes on the secondary server is
     # set by UPS_BOXES_SECONDARY.
     #
     # You may define all three parameters in this config file and the startup
     # script will choose the appropriate one, based on whether clustering is
     # enabled and, if so, which role the server is playing.
     #
     UPS_BOXES="bevatron"
     UPS_BOXES_PRIMARY=""
     UPS_BOXES_SECONDARY=""
     #
     # If you would like to start logging of UPS statistics at regular intervals
     # to a log file too, define the location of the log file and the logging
     # interval.  Otherwise, if the LOG_PATH is set to an empty string, the
     # logging daemon isn't started.
     #
     # A separate file is created for each UPS that is monitored.  The name of the
     # file is created by appending the UPS name to the LOG_PATH value, separated
     # by a period.  For example:
     #
     #      LOG_PATH="/var/log/upslog"
     #      UPS_BOXES="UPS1 UPS1"
     #
     #      Log files = /var/log/upslog.UPS1, /var/log/upslog.UPS2
     #
     # The log interval is given in seconds by LOG_INTERVAL.
     #
     # The group that will be used to create log files is set by UPS_GROUP.  You
     # may find it useful to have your logfiles set to a different group and
     # given read/write permissions so that they can be accessed by UPS users.
     # Otherwise, root permissions are used.
     #
     LOG_PATH="/var/log/upslog"
     LOG_INTERVAL=30
     UPS_GROUP="ups"
     #
     # For secondary and/or standalone servers, if you'd like the UPS logs to be
     # copied to the primary server for consolidation purposes, define the
     # server's name here and the logging directory where they will be copied to.
     #
     # PRI_SERVER="jump-gate"
     # PRI_LOG_PATH="/var/log/upslog"

While we're at it, we'd like the logfiles to be created with the correct permissions, so let's create an empty one for each UPS before we proceed any further. Logrotate will take care of this when it rotates the log files but let's start with:

     su
     touch /var/log/upslog.bevatron
     chgrp ups /var/log/upslog.bevatron
     chmod g+w /var/log/upslog.bevatron

/etc/ups/batterytest:

Set up a script that will test a UPS' battery at regular intervals. This script can be scheduled from crontab:

     #! /bin/sh
     #
     # batterytest - Script to test the battery of a UPS at regular intervals.
     #
     # This script is used by cron to periodically test the battery of a UPS.
     #
     #
     # Define the install path for the UPS binaries.
     #
     INSTALL_PATH="/usr/local/ups"
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ]; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we can test.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # For all of the UPS on this system, see which one we're testing.
     #
     for UPSBox in $UPS_BOXES; do
      if [ x"$UPSBox" == x"$1" ]; then
          $INSTALL_PATH/bin/upscmd -u jgmonitor -p ItsASecret \
              ${1}@localhost test.battery.start
      fi
     done

Change the permissions of the script, after you create it, to add execute permission:

     su
     chmod ugo+x /etc/ups/batterytest

/etc/ups/notify:

Create the following script to send event notifications to root, via email, whenever the UPS has something important to say. Note that the indentation in front of "ENDMSG" can be tabs only. If your lame-butt text editor sticks spaces in there, the shell script will get a syntax error. Here is the script:

     #!/bin/sh
     #
     # A shell script that can be used by the UPS power monitor to send messages
     # to root when power failures occur.
     #
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ]; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are logging to.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # Write the event to the log, if there is one.
     #
     if [ x"$LOG_PATH" != x ]; then
      timestamp=`date "+%Y/%m/%d %H:%M:%S"`
      for LogBox in $UPS_BOXES; do
          echo $UPSNAME | grep -q $LogBox
          matval=$?
          if [ $matval = 0 ] ; then
              echo $timestamp EVENT: $1 >> ${LOG_PATH}.$LogBox
          fi
      done

fi
#
# Some events we only log.
#
if ([ x"$NOTIFYTYPE" != xONBATT ]) && ([ x"$NOTIFYTYPE" != xONLINE ]) \

      && ([ x"$NOTIFYTYPE" != xREPLBATT ]) \
      && ([ x"$NOTIFYTYPE" != xSHUTDOWN ]) \
      && ([ x"$NOTIFYTYPE" != xOVERTEMP ]); then
      exit 0
     fi
     #
     # Email the message to root so that they can see everything that's going on.
     # After all, if you're omnipotent, you need to know everything.
     #
     HostName=`hostname`
     if [ x"$NOTIFYTYPE" != xSHUTDOWN ]; then
      /bin/mail -s "Message from the UPS" root <<-ENDMSG
          The power monitor on $HostName has generated the following message:
          $1
          ENDMSG
     else
      /bin/mail -s "Urgent message from the UPS" root <<-ENDMSG
          The power monitor on $HostName has generated the following message:
          $1
          ENDMSG
     fi

Note that, in the above script, the two sets of lines between "/bin/mail -s ... <<-ENDMSG" and up to and including the line with "ENDMSG" must either not be indented at all or only indented with actual tab characters (not blanks). If they are not, the script will fail.

Also, don't forget to add execute permissions to the script after you create it:

     su
     chmod ugo+x /etc/ups/notify

Then, it wouldn't hurt try it out after you've got it all set up. For example:

     UPSNAME=bevatron; export UPSNAME
     NOTIFYTYPE=ONBATT; export NOTIFYTYPE
     /etc/ups/notify "This is a test"

Check that root receives the message and that the message gets logged to bevatron's log file.

/etc/ups/hosts.conf:

Copy the file hosts.conf.sample. Add all of the machines that you want to be able to monitor from the Web. Also add the name of the logfile, if logged events will be displayed:

     MONITOR bevatron@localhost "jump-gate UPS"
     LOGFILE /var/log/upslog.bevatron

If you'll monitor the UPS via a Web server on another machine, add its information to the /etc/ups/hosts.conf file there, too.

/etc/ups/ftplogs:

If you'd like to build a consolidated graph of all your UPS activity and have a centralized server where logfiles can be copied for this purpose, you should create the file /etc/ups/ftplogs:

     #! /bin/sh
     #
     # ftplogs - Script to ftp the UPS logs to the primary server at regular
     #           intervals.
     #
     # This script is used both by cron, to send the current UPS logs to the
     # primary server, every 15 minutes, and by logrotate, to send the freshly
     # rotated UPS logs to the primary server whenever UPS logs are rotated.
     #
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ]; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are logging to.  If we aren't the secondary
     # or a standalone system, there are no logfiles to FTP.
     #
     if [ x"$SERVERROLE" == xSecondary ]; then
         UPS_BOXES=$UPS_BOXES_SECONDARY
     else
         if [ x"$SERVERROLE" != xStandalone ]; then
             exit 0
         fi
     fi
     #
     # If there's no primary server defined, we're all done.
     #
     if [ x"$PRI_SERVER" == x ]; then
         exit 0
     fi
     #
     # If we were passed a logfile name, send it to the primary server directly.
     #
     if test x"$1" != x; then
         echo -e "user ups ItsASecret\\nbin\\nput ${LOG_PATH}.$1 \
             ${PRI_LOG_PATH}.$1" | ftp -n $PRI_SERVER
     #
     # For all of the UPS on this system, send the logs to the primary.
     #
     else
         for LogBox in $UPS_BOXES; do
             echo -e "user ups ItsASecret\\nbin\\nput ${LOG_PATH}.$LogBox \
                 ${PRI_LOG_PATH}.$LogBox" | ftp -n $PRI_SERVER
         done
     fi

In the FTP commands in the script, you should pick a user that is valid on the primary server and use the right password for that user. Once you've saved the script, don't forget to add execute permissions to it:

     su
     chmod ugo+x /etc/ups/ftplogs

Next, on the primary server, you should create a couple of empty log files with the correct permissions so that FTP can overwrite them without getting permission denied:

     su
     touch /var/log/upslog.bevatron /var/log/upslog.bevatron.1
     chown root:ups /var/log/upslog.bevatron
     chmod g+w /var/log/upslog.bevatron

Then, it wouldn't hurt try out the script, now that you've got it all set up. For example:

     /etc/ups/ftplogs

Check on the primary server that the log files get copied over.

If you want the UPS to be shown in the graph of UPS activity, add its name and full load power to /etc/ups/upsplot.pl:

     my @UPSNames = (                        # Names of UPS boxes                    
         "Bevatron",
              .
              .
              .
     my @UPSWatts = (                        # Full load wattages of UPS boxes       
         466,
              .
              .
              .

Now, you can add the script to the cron table on the secondary server so that it runs at regular intervals (e.g. every 15 minutes), as shown in the next section.

/etc/crontab:

To schedule all of the UPS-related activities, add the following to your crontab:

     # Push the UPS logs over to the primary server every 15 minutes.
     05,20,35,50 * * * * root /etc/ups/ftplogs
     # Twice a month, have the UPS check its battery.
     00 10 4,18 * * root /etc/ups/batterytest bevatron

Pick some suitable times that won't collide with other machines sending their UPS files to the central server (if you care). Also, if the receiving server is rotating the copied logs (this need not be the case, since the sending server is rotating its logs and the copied log always replaces the log on the receiving server, thereby ensuring that rotation on the sending server is sufficient to guarantee that log never grows too big), you should time the push to be a few minutes after the receiving server rotates its logs so that it can get a good, rotated copy of the last log before the new log is sent over top of it. Typically, logrotate runs out of /etc/cron.daily which is usually run at 04:02 each day. Consequently, the value of five minutes past the hour (chosen above) is a good choice.

Once again, if you care, schedule the times for the battery test on days when the other UPS are not also testing their batteries (you don't want all the machines down at once, do you).

Note that most UPS will usually come with automatic self-test turned on and set to every 14 days. This means that the UPS will run its battery test itself every 14 days, exactly 1209600 seconds after it is turned on. How convenient is that? If you'd rather the test was done when you decide, in your crontab, you should turn off this dubious feature like so:

     /usr/local/ups/bin/upsrw -s ups.test.interval=0 -u username -p password \
       upsname@localhost

/etc/logrotate.conf, /etc/logrotate.d/ups:

Add the new UPS' logfile to either the global logrotate config file (/etc/logrotate.conf) or the specific UPS config file (/etc/logrotate.d/ups):

     /var/log/upslog.bevatron {
         notifempty
         missingok
         create 0664 root ups
         copytruncate
         postrotate
             echo HEADER: Bevatron >/var/log/upslog.bevatron
             /etc/ups/ftplogs bevatron.1
         endscript
     }

/etc/rc.d/init.d/upsd:

Install the following script using "chkconfig --add upsd" and "chkconfig upsd on". Don't forget to set its permissions to "ugo+x":

     #! /bin/sh
     #
     # upsd - Script to start/stop the NUT UPS monitoring and power down daemons.
     #
     # chkconfig: 2345 19 81
     # description: Uninterruptable Power Supply monitoring and power down daemons
     #
     # pidfile: /var/lock/subsys/upsd
     # pidfile: /var/lock/subsys/upsmon
     # pidfile: /var/lock/subsys/upsdrivers.upsname
     # pidfile: /var/lock/subsys/upslog.upsname
     # config:  /etc/ups/*
     #
     #
     # Define the install path for the UPS binaries, etc.
     #
     INSTALL_PATH="/usr/local/ups"
     #
     # Load the RedHat functions.
     #
     if [ -f /etc/redhat-release ]; then
         . /etc/rc.d/init.d/functions
     fi
     #
     # Source the UPS configuration.
     #
     if [ -f /etc/sysconfig/upsd ]; then
        . /etc/sysconfig/upsd
     fi
     #
     # Source the clustering configuration.
     #
     if [ -f /etc/sysconfig/clustering ] ; then
         . /etc/sysconfig/clustering
     else
         SERVERROLE=Standalone
     fi
     if [ x"$SERVERROLE" == x ]; then
         SERVERROLE=Standalone
     fi
     #
     # Determine which UPS boxes we are starting up.
     #
     if [ ${SERVERROLE} = "Primary" ] ; then
         UPS_BOXES=$UPS_BOXES_PRIMARY
     else
         if [ ${SERVERROLE} = "Secondary" ] ; then
             UPS_BOXES=$UPS_BOXES_SECONDARY
         fi
     fi
     #
     # Upon startup, start all the UPS drivers that are configured in
     # /etc/ups/ups.conf (or wherever you put it).
     #
     # Next, start the UPS daemon to begin collecting information about all of
     # the connected UPS'.
     #
     # Then, start the power monitoring daemon to watch for low power conditions
     # and shut down the machine, if found.
     #
     start()
         {
         #
         # Start a driver for each of our UPS.
         #
         for UPSBox in $UPS_BOXES; do
          echo -n "NUT starting UPS driver for $UPSBox: "
          $INSTALL_PATH/bin/upsdrvctl start $UPSBox >/dev/null 2>&1
          startval=$?
          if [ $startval = 0 ] ; then
              touch /var/lock/subsys/upsdrivers.$UPSBox
              echo_success
          else
              echo_failure
          fi
          echo
      done
      #
      # Start the UPS daemon to process service requests.
      #
      echo -n "NUT starting UPS daemon: "
      $INSTALL_PATH/sbin/upsd >/dev/null 2>&1
      startval=$?
      if [ $startval = 0 ] ; then
          touch /var/lock/subsys/upsd
          echo_success
          echo
      else
          echo_failure
          echo
          return $startval
      fi
      #
      # Start power monitoring.
      #
      echo -n "NUT starting power monitor: "
      $INSTALL_PATH/sbin/upsmon >/dev/null 2>&1
      startval=$?
      if [ $startval = 0 ] ; then
          touch /var/lock/subsys/upsmon
          echo_success
          echo
      else
          echo_failure
          echo
          return $startval
      fi
      #
      # If the user has given us a logging directory, start up logging.
      #
      if [ x"$LOG_PATH" != x ]; then
          echo -n "NUT starting UPS log: "
          timestamp=`date "+%Y/%m/%d %H:%M:%S"`
          #
          # Start up logging for each UPS box.
          #
          # Note that, above, we could have failed to start one or more UPS
          # and ignored the failure.  Here, since it is the last step, we
          # abort upon the failure of any log.
          #
          if [ x"$LOG_INTERVAL" == x ]; then
              LOG_INTERVAL=30
          fi
          for UPSBox in $UPS_BOXES; do
              if [ ! -f ${LOG_PATH}.$UPSBox ]; then
                  echo HEADER: $UPSBox > ${LOG_PATH}.$UPSBox
              fi
              echo $timestamp EVENT: Starting UPS logging \
                  >> ${LOG_PATH}.$UPSBox
              if [ x"$UPS_GROUP" != x ]; then
                  chgrp $UPS_GROUP ${LOG_PATH}.$UPSBox
                  chmod g=rw ${LOG_PATH}.$UPSBox
              fi
              $INSTALL_PATH/bin/upslog ${UPSBox}@localhost \
                  ${LOG_PATH}.$UPSBox $LOG_INTERVAL \
                  "%TIME @Y/@m/@d @H:@M:@S% %VAR input.voltage% \
                      %VAR output.voltage% %VAR input.frequency% \
                      %VAR battery.charge% %VAR ups.load% [%VAR ups.status%] \
                      %VAR ups.temperature%" \
                  >/dev/null 2>&1
              startval=$?
              if [ $startval = 0 ] ; then
                  touch /var/lock/subsys/upslog.$UPSBox
              else
                  echo_failure
                  echo
                  return $startval
              fi
          done
          echo_success
          echo
      fi
      #
      # We're all done with startup.
      #
      return $startval
      }

#
# Upon shutdown, do all of the startups in reverse: logging; power monitor; # UPS daemon; UPS drivers.
#
stop()

      {
      #
      # Shutdown logging for any log that we started.
      #
      echo -n "NUT stoping UPS log: "
      timestamp=`date "+%Y/%m/%d %H:%M:%S"`
      for UPSBox in $UPS_BOXES; do
          if [ -f /var/lock/subsys/upslog.$UPSBox ]; then
              LogPID=`ps -eo pid,args | grep upslog | grep $UPSBox \
                  | sed -n 's/^ \([0-9]\).*/\1/p'`
              if [ x"$LogPID" != x ]; then
                  kill -9 $LogPID
                  stopval=$?
                  echo $timestamp EVENT: Stopping UPS logging \
                      >> ${LOG_PATH}.$UPSBox
                  [ $stopval = 0 ] && rm -f /var/lock/subsys/upslog.$UPSBox
              fi
          fi
      done
      echo_success
      echo
      #
      # Stop power monitoring.
      #
      if [ -f /var/lock/subsys/upsmon ]; then
          echo -n "NUT stoping power monitor: "
          $INSTALL_PATH/sbin/upsmon -c stop >/dev/null 2>&1
          stopval=$?
          if [ $stopval = 0 ] ; then
              rm -f /var/lock/subsys/upsmon
              echo_success
          else
              echo_failure
          fi
          echo
      fi
      #
      # Stop the UPS daemon.
      #
      if [ -f /var/lock/subsys/upsd ]; then
          echo -n "NUT stoping UPS daemon: "
          $INSTALL_PATH/sbin/upsd -c stop >/dev/null 2>&1
          stopval=$?
          if [ $stopval = 0 ] ; then
              rm -f /var/lock/subsys/upsd
              echo_success
          else
              echo_failure
          fi
          echo
      fi
      #
      # Stop all of our UPS drivers.
      #
      for UPSBox in $UPS_BOXES; do
          echo -n "NUT stoping UPS driver for $UPSBox: "
          $INSTALL_PATH/bin/upsdrvctl stop $UPSBox >/dev/null 2>&1
          stopval=$?
          if [ $stopval = 0 ] ; then
              rm -f /var/lock/subsys/upsdrivers.$UPSBox
              echo_success
          else
              echo_failure
          fi
          echo
      done
      return 0
      }

#
# See how we were called.
#
case "$1" in

      #
      # Start.
      #
      start)
          start
          RETVAL=$?
          ;;
      #
      # Stop.
      #
      stop)
          stop
          RETVAL=$?
          ;;
      #
      # Restart or reload (whatever).
      #
      restart|reload)
          stop
          start
          RETVAL=$?
          ;;
      #
      # Conditional restart.
      #
      condrestart)
          if [ -f /var/lock/subsys/upsdrivers ]; then
              stop
              start
              RETVAL=$?
          fi
          ;;
      #
      # Give the status of all of the NUT components that are running.
      #
      status)
          #
          # If none of the UPS are running, assume that NUT is down.
          #
          if [ ! -f /var/lock/subsys/upsdrivers ]; then
              echo NUT is down
              exit 1
          fi
          #
          # Let's check each UPS.
          #
          for UPSBox in $UPS_BOXES; do
              $INSTALL_PATH/bin/upsc $UPSBox output.voltage >/dev/null 2>&1
              RETVAL=$?
              if [ $RETVAL -eq 0 ]; then
                  echo UPS $UPSBox is functioning normally
              else
                  echo UPS $UPSBox is down
              fi
          done
          #
          # Check the UPS daemon.
          #
          status upsd
          #
          # Check the power monitor.
          #
          status upsmon
          #
          # If we're logging then let's see what we're logging.
          #
          if [ x"$LOG_PATH" != x ]; then
              status upslog
              RETVAL=$?
              if [ $RETVAL -eq 0 ]; then
                  for UPSBox in $UPS_BOXES; do
                      if [ -f /var/lock/subsys/upslog.$UPSBox ]; then
                          echo UPS $UPSBox is being logged to $LOG_PATH.$UPSBox
                      fi
                  done
              fi
          fi
          ;;
      #
      # Help text.
      #
      *)
          echo $"Usage: $0 {start|stop|restart|condrestart|status}"
          exit 1

esac

     exit $RETVAL

/etc/rc.d/init.d/upsdown:

To facillitate physical shutdown of the UPS power, when the system is shut down, install the following script using "chkconfig --add upsdown" and "chkconfig upsdown on". Don't forget to set its permissions to "ugo+x":

     #! /bin/sh
     #
     # upsd - Script to shut down the UPS upon system shutdown due to \
     #        power failure.
     #
     # Revision History:
     # ewilde      2003Apr06  Initial coding.
     #
     # chkconfig: 12345 01 99
     # description: Shut down Uninteruptable Power Supply on power fail shutdown
     #
     # This script runs on startup, right after new microcode has been installed
     # on the machine but before any file system activity.  It can sleep for a
     # while until the UPS batteries have a chance to recharge to a sufficient
     # level to ensure an orderly shutdown, should power fail again.  If you wish
     # to do this, set the START_SLEEP value to a valid "sleep(1)" value (e.g.
     # "5m") and make sure POWERDOWNFLAG points to the same file as found in the
     # upsmon.conf file.
     #
     # It also runs on shutdown, as the system cleans up after entering runlevel
     # 0 (shutdown) or 6 (reboot).  If the UPS POWERDOWNFLAG is set, it physically
     # powers off the UPS.  This should only happen in runlevel 0.
     # Define the install path for the UPS binaries, etc.
     INSTALL_PATH="/usr/local/ups"
     POWERDOWNFLAG="/etc/killpower"
     START_SLEEP=""
     if [ -f /etc/redhat-release ]; then
         . /etc/rc.d/init.d/functions
     fi
     # See how we were called.
     case "$1" in
      # Start.
      start)
          if [ -f $POWERDOWNFLAG ]; then
              echo -n "NUT waiting for UPS batteries to recharge: "
              if [ x"$START_SLEEP" != x ]; then
                sleep $START_SLEEP
              fi
              echo_success
              echo
          fi
          ;;
      # Stop.
      stop)
          if [ -f $POWERDOWNFLAG ]; then
              echo -n "NUT shutting down the UPS: "
              echo_success
              echo
              $INSTALL_PATH/bin/upsdrvctl shutdown
          fi
          ;;
      # Help text (pushing it, I know).
      *)
          echo $"Usage: $0 {start|stop}"
          exit 1

esac

     exit 0

Change the group ownership of the UPS port(s) to the ups user:

     chgrp ups /dev/ttyS2

If you have one of the later versions of Linux that use udev for dynamically creating the "/dev" device space, you will, as well, need to hack the udev rules for the tty device. These can be found in the /etc/udev/rules.d dirctory, typically in the file /etc/udev/rules.d/50-udev.rules. Add something to the effect of:

     # Special case for the UPS devices.
     KERNEL=="ttyS2",                NAME="%k", GROUP="ups", MODE="0660",
                                     OPTIONS="last_rule"

Add these lines before the general case rules for tty devices.

Alternately, later installs of NUT add a set of rules for the USB drivers in a file named 52_nut-usbups.rules. You could add the following to that file instead of hacking the existing 50-udev.rules:

     # Special case for the UPS devices
     KERNEL=="ttyS1", group="nut", MODE="0660"

Some serial ports will come up with the correct baud rate and interrupt address set automagically. After you've made the changes to the new serial port, as described above, to make it accessible to the UPS userid, you should check that the serial port is configured correctly (reboot the machine first, if you had to change the rules in /etc/udev). To do this use:

     su
     setserial /dev/ttySx

If the system figures things out and sets the serial ports correctly, all on its own, go with its settings because this will cause a lot less grief, in the long run. However, if the type of UART, port address and interrupt vector, along with the baud rate, aren't set correctly (usually looking at /dev/ttyS0 will give some clues), you should add some lines to /etc/rc.serial to set the serial port up at boot time. If it already exists, you can edit file but, if it doesn't already exist, you can just create it with your text editor.

To determine what interrupt numbers and port addresses to use, you should list the PCI devices like this:

     /sbin/lspci -v

For each serial port, figure out from the information given by lspci what values to use and then add something like this to /etc/rc.serial:

     setserial /dev/ttyS2 port 0xdf88 UART 16550A irq 225 Baud_base 115200

When you're done, the permissions on /etc/rc.serial should be:

     -rw-r--r--    1 root     root          138 Aug  6  2005 /etc/rc.serial

Finally, not all UPS come with their control parameters set correctly (especially if you'v bought a refurbished one) so you may want to check that they are what you expect them to be:

     /usr/local/ups/bin/upsc upsname@localhost

To find out which parameters you can set, run upsrw with no options:

     /usr/local/ups/bin/upsrw upsname@localhost

If any of the settable parameters are set to values that you don't like, you can change them with upsrw, like this:

     /usr/local/ups/bin/upsrw -s ups.test.interval=1209600 -u username \
       -p password upsname@localhost

For a typical APC SmartUPS, you may wish to set the following:

     battery.alarm.threshold     L
     battery.charge.restart 00
     battery.date                [batdate]
     battery.runtime.low 120
     input.sensitivity           H
     input.transfer.high 132
     input.transfer.low 103
     output.voltage.nominal 115       (only necessary on 240V units)
     ups.delay.shutdown 020
     ups.delay.start 000
     ups.id                      [upsname]
     ups.test.interval 1209600   (see notes on periodic test, above)

>>>>>>
Adding A New Extension


VOIP Security

>>>>>>
Hello

        I need to connect an Asterisk server to the Net so that 1) remote

users can register and 2) Internet users can ring any extension on the server.

I'll use iptables to prevent hackers from trying to register.

I was wondering what solution to use to block brute force attempts:

This is on an embedded Linux, so there isn't enough RAM to run Python-based fail2ban.

If you have installed Asterisk and iptables, which solution did you end up using?

Thank you.


> I need to connect an Asterisk server to the Net so that 1) remote > users can register and 2) Internet users can ring any extension on > the server.
>
> I'll use iptables to prevent hackers from trying to register. >
> I was wondering what solution to use to block brute force attempts: >
> - just rely on iptables since it offers a way, eg. "iptables -I INPUT > -p udp --dport 5060 -m state --state NEW -m recent --update --seconds > 600 --hitcount 2 -j DROP"

2 in 600 might be low for --hitcount, high for --seconds. NEW is just wrong, because attacks will be ESTABLISHED. See also:

     http://www.spinics.net/lists/netfilter/msg49598.html
     http://www.spinics.net/lists/netfilter/msg49660.html
     http://www.spinics.net/lists/netfilter/msg49676.html

My ruleset is working, at least to block the SIP attackers, but I still don't know if a non-whitelisted Internet SIP user could ring extensions. From discussion with a SIP expert, I think even my --hitcount of 9 in 30 or 18 in 45 might still be too low.

But no more log floods, which is good, my main goal.

> - add Brute Force Detection (BFD), which is a shell script that is > called by CRON (ie. every minute at most) > www.rfxn.com/projects/brute-force-detection/ >
> - add SSHGuard, which is apparently a stand-alone binary program that > doesn't rely on CRON
> www.sshguard.net
>
> This is on an embedded Linux, so there isn't enough RAM to run > Python-based fail2ban.

On an embedded system, I would use a remote syslog server anyway. Let your log parsing be done on a less-restricted machine, and have it ssh in and sudo to do what needs to be done.

> If you have installed Asterisk and iptables, which solution did you > end up using?

But as mentioned in one of the links above, I don't like log parsing in general. Just experiment with the -m recent rules and try some calls.
<<<<<<

Notes About VOIP Under VPN

>>>>>>
E:

I think the number one source of the problem is the VPN connection to the switch. The VoIP portion of the call proceeds from the laptop, through OpenVPN to an ASUS RT-N12/D1 router running Tomato Shibby, thenceforth to the Asterisk switch.

The ASUS router uses a MIPS/R2 300 MHz processor. Not the fastest kid on the block. In addition, since I'm paranoid, the block cypher is 256-bit AES. Also not the fastest kid on the block. Anyway, the upshot of all this is consistent round-trip ping times of 250ms. In other words, we have a 125ms penalty up front, before the jitter buffer ever comes into play.

I set the fixed jitter buffer to 60ms (about the smallest value that gave acceptable voice quality). I tried the adaptive jitter buffer with a high limit of 100ms but the adaptation algorithm fell on the floor, for whatever reasons. So, I went back to the fixed buffer. One would like to keep it as small as possible, since it also bones all of the analog calls that pass through the line card but 40ms, for example, didn't work.

There's probably another 20-30ms trip time through the switch and I'm sure the telco is inserting somewhere around half of their allotted delay (i.e. 70ms). By the time you add it all up, you're talking about 300ms one way.

Were it not for the VPN tunnel, the trip time to the switch would be more like 30-40ms. Not only that, but we could probably dispense with the jitter buffer because we'd be using UDP. That would make a huge difference in the 300ms one-way time.

Regarding your remark about Skype, there's a whole lot of stuff that Skype doesn't have to do. Basically, the call is just between two peers so the delay is only the packet transit time plus any processing time at either end. I could easily see this being less than 150ms, since I get consistent ping times over many hops to faraway places that are in the 80-100ms range.

As we said on the phone, its probably the best I could do, Jimmy, but it ain't going to cut it for anything other than the occasional call.

I:

I don't see the connection between UDP and the jitter buffer. The buffer is there to compensate for variations in transit delay. If there is no buffer, the codec starts decoding as soon as it gets its first frame of, say, 40ms and expects to find another frame of 40ms once its finished. No frame = problem for the codec. Let's say you have an average transit delay of 40ms, so the unwise would have a 40ms jitter buffer. This works great up to the point that the delay between any two packets doesn't exceed 40 ms, since you can work on saved data in the buffer. However, we all know what 'average' implies, so sooner or later, the delay between 2 packets will exceed 40ms, and the codec is boned. What you need is the standard deviation and use 2 SDs worth in the jitter buffer. Most codecs will tolerate 5% packet loss.

E:

I don't see the connection between UDP and the jitter buffer.

What I meant was, if you're using UDP, you can run without or much less jitter buffer and, when a packet is dropped, just take the hit.

Most codecs will tolerate 5% packet loss.

Even using UDP, any communications link that is dropping 5% of the packets on the floor is total s**t. So, one would expect something better than that. If the codec can hack 5% loss, using UDP and no jitter buffer might be a heck of a lot better than TCP with a 60ms buffer. You get way better RTT at the price of an occasional hiccup.

Thanks for your insights. It pays to know when its time to hang up your butt-set and drink ginger beer.
<<<<<<

A Basic User Guide

Since Asterisk is configured by you, there is no standard user guide for its operation. However, this doesn't mean your users won't need one. So, as you go about configuring Asterisk, make notes about what features you are implementing and build your user guid as you go. Here is one, based on what we've defined in these notes.

Dial 911 for emergencies.

Dial 0 for the Operator.

Dialing extension - To dial an internal extension, dial 6xx.

Dialing out - To grab a free outside line from the hunt group and dial a number on the PSTN, dial 9xxxxxx.... You may dial a seven-digit number after the 9, in which case the local LATA will be prefixed to the number. You may dial a ten-digit number after the 9, in which case an overlay plan number or a long distance number will be dialed (depending on what is appropriate). You may dial 91, followed by a ten-digit number, in which case a long distance call will be made.

Transfer - To transfer a call to another extension, dial #, followed by the extension number.

TransferVoicemail - To transfer a call directly to a voicemail box, dial #*, followed by the extension (mailbox) number.

Voicemail - Dial extension 898 to access the voicemail system from an outside line (e.g. when dialing in from outside the system). You may also transfer a caller to this extension, if a caller requests that you send them to voicemail to check their mail (i.e. a fellow user, calling from outside).

RecordIVRMessage - Dial extension 899 to record a message for use with the IVR menus. The message is always stored in /tmp/asterisk-recording.wav. Note that this extension only works from an inside line.

CallParking - Parking a call is just like transfering a call to an extension. While talking to the caller, dial "#" (to start the transfer process) and then dial "700" (the parking lot extension). Asterisk will respond with the actual parking extension assigned to the parked call. Hang up and move to another extension or announce the parked call to another user. Pick up the phone and dial the parked extension (e.g. 701).

>>>>>>
MeetMe - To access a MeetMe conference room, dial 8 + your extension. (i.e. for extension 200, dial 8200). This will create a MeetMe room for you. Once the room is live, you can then transfer callers into it. <<<<<<

Directory - Dial *411.

When dialing *411, you get to the phone system's "yellow pages" where you can find the extensions of other users by typing in the three first letters in their last name. Directory lookup uses these keys for the letters shown:

     2 - ABC
     3 - DEF
     4 - GHI
     5 - JKL
     6 - MNO
     7 - PQRS
     8 - TUV
     9 - WXYZ

Example: You want to find the phone number to Mr. Smith. Call *411 and dial 764. You will be given a list of all users that match the pattern and asked to choose Mr. Smith from among them (if his name isn't unique).

OutsideLine - To dial out on a specific outside line (rather than the next available line in the hunt group), dial *42, followed by the single digit line number, followed by the number to dial. This allows you to route a call directly to a copper line, say, when all of the VOIP lines are dead (not that that would ever happen).

Another use for this capability is to check for voicemail (from the PhoneCo switch) on a particular CO line. By selecting a specific line and then dialing that line's phone number, many PhoneCo switches direct the call to the voicemail box (instead of giving a busy signal) for that line. Thus, you can dial something like "*4212314567", to check the voicemail on line 1, which has a phone number of 2314567 in the local LATA.

>>>>>>
CallReturn - Dial *69 to return the last incoming call. <<<<<<

CallForwarding - Dial *72 to enable Call Forwarding.

CancelCallForwarding - Dial *73 to disable Call Forwarding.

DoNotDisturb - Dial *78 to enable Do Not Disturb (all calls go immediately to voicemail).

CancelDoNotDisturb - Dial *79 to disable Do Not Disturb (calls ring the extension in the normal manner).

CallForwardOnBusy - Dial *90 to forward incoming calls to another extension if your extension is busy. In an effor to keep the number of feature codes to a workable number, call are also forwarded if there is no answer on your extension.

CancelCallForwardOnBusy - Dial *91 to cancel forwarding of incoming calls to another extension if your extension is busy.

MessageCenter - Dial *97 to pick up the voicemail for your extension from Comedian (voicemail). Dialing the Message Center with this code does not ask for a password or extension.

MessageCenterNumber - Dial *98 to be prompted for a mailbox number and pick up the voicemail for that mailbox from Comedian (voicemail).

The menus in the Message Center are organized like this:

     0) Mailbox options
        1) Record your Un-Available message
        2) Record your Busy message
        3) Record your Name
        4) Record Temporary Greeting
        5) Change mailbox password
     1) Listen to new messages
     2) Change folders
        0) New Messages
        1) Old Messages
        2) Work Messages
        3) Family Messages
        4) Friends Messages
     3) Advanced options
        5) To leave a message
     5) Repeat the last message
     6) Play the next message
     7) Delete the current message
     8) Forward the current message to another mailbox (enter the extension
        and press #)
        1) Prepend a message to the forwarded message
        2) Forward without prepending any message
     9) Save the current message in:
        0) New Messages
        1) Old Messages
        2) Work Messages
        3) Family Messages
        4) Friends Messages
     *) Give help
     #) Return/exit to/from the Main Menu

Time - Dial *60 to get the current system time.

Weather - Dial *61 to get the local weather report.

WakeupCall - To schedule a wakeup call to your extension, dial *62. You can schedule a one time call or a daily call at the same time every day.

CancelWakeupCall - To cancel a previously scheduled wakeup call, dial *63.

EchoTest - Dial *43 for an echo test. Hang up when you're done.

FeatureList - Dial *46 for a feature list.

ClearFeatures - Dial *47 to clear all features on an extension.