If you have used SSH to connect to a remote machine before, you know the procedure: open a terminal, type in the SSH command and the host IP, enter the password. This is probably easy for a single connection, but if you are a system administrator looking after several remote machines and have a need to manage multiple SSH connections, you will need a better and easier solution. You need PAC Manager.
We must confess that we are not as fond of the Linux command line as some Linux users — we’ll take a good GUI, and a mouse over a keyboard any time we can. But for those that love working at the Linux command prompt (and that probably don’t make nearly as many typos as we do), we thought you might appreciate this series from the catonmat blog:
Author Peteris Krumins used these articles as the starting points for his e-books. In addition to the e-books listed there, it appears that he has authored this paperback edition that can be purchased from Amazon (this is an affiliate link, so if you buy it from here we’ll get a small commission):
This is a heavily edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.
This was originally posted in August, 2011. Unless you are deeply in love with Perl, I suggest you also take a look at the newer article, A Bash script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP. Although it is still considered experimental, it is simpler than the script shown here, does not require the installation of additional modules, and the text has been updated somewhat to reflect the changes in FreePBX since this article was written.
This post is going to be a bit long because I first need to explain the “why” behind this script, then how to obtain the prerequisite Perl modules, then the script itself and how to test it after installation.
If you are using a recent version of Asterisk and FreePBX you may be using the Asterisk SIP Settings module (under the “Tools” tab) to automatically set various SIP parameters. This module is a great help to those who don’t know what they are doing, but there is a trap for the unwary (and in this case it’s NOT the fault of FreePBX – it’s a longstanding bug in Asterisk that’s the problem).
At the top of the Asterisk SIP Settings configuration page, in the NAT Settings section, there are two options that can be set. The first is NAT and there are four possible choices:
yes = Always ignore info and assume NAT
no = Use NAT mode only according to RFC3581
never = Never attempt NAT mode or RFC3581
route = Assume NAT, don’t send rport
In theory, if you have a fixed IP address AND your Asterisk server is not behind an external router that does NAT translation, you should use “no” (and most of the rest of this article will not be relevant to you). This article is intended more for home and SOHO users that both have their Asterisk server behind a hardware router of some kind, and that get their broadband service from a company that occasionally changes their IP address without warning. For such users, the preferred setting is “yes”. I’m not enough of a networking guru to tell you under what circumstances one of the other settings might be appropriate (if you understand this stuff, feel free to leave a comment and enlighten us).
It’s the next set of settings that can get us into trouble. This is the IP Configuration and there are three possible choices:
Public IP
Static IP
Dynamic IP
If your IP address never changes AND you aren’t behind a hardware firewall then you can usually just set this to “Public IP” and let it go at that. You will not be asked to fill in any other values. But most users that are not in that situation will pick one of the other two choices, and this is where the problem arises. Conventional wisdom has it that if your ISP ever changes your IP address without advance warning (which is the case for most cable broadband and DSL users), you should use the Dynamic IP setting. In this case there is an auto-configure button that will fill out the fields for you, although you may need to fill in the Dynamic Host field yourself. This is the “External FQDN as seen on the WAN side of the router and updated dynamically, e.g. mydomain.dyndns.com” (as explained if you mouse over the words “Dynamic Host”). You can use a DynDNS address (or an address from a similar service) or an address you have purchased. But the problem is that for some users, THIS METHOD SIMPLY DOES NOT WORK.
If you try to use Dynamic IP and it won’t work for you, what happens is you will get all sorts of weird errors. You may get one way audio, some calls may disconnect for no apparent reason after about five seconds, and you will see other weird errors in your CLI. If you change this setting to “Static IP” and click the auto-configure button and then submit the changes, the problems magically go away – UNTIL your ISP changes your IP address, at which point you suddenly have no connectivity to the outside world. If you ask for help, everybody and their brother will tell you to use the Dynamic IP setting, and the minute you try that you’ll get all the weird errors again.
So if that’s your situation, you need this Perl script. Coupled with a cron job, it goes out and checks your IP address every five minutes and if it notices it has changed, it changes it in the MySQL database (same as if you entered it into the External IP text box on the Asterisk SIP Settings configuration page) and then reloads Asterisk. Therefore, you can use the Static IP method and it hopefully it will work reliably. If and when your IP address changes, you should only be down for about five to ten minutes at most (hopefully your broadband provider usually does such changes in the middle of the night!).
Prerequisites:
You still have to use a Dynamic DNS service to keep track of your IP address if you want external extensions to be able to find your server on the Internet. It’s not required for this script to work, though, so I won’t say any more about that except to note that if you use a recent vintage hardware router, it probably has DDNS support built in.
You may have to install some Perl modules on your system. This script uses two or three: WWW::Mechanize (only if you use the first variation of the script shown below), Data::Validate::IP, and DBD::mysql. There are typically two ways to install any missing Perl modules on your system. One is to do this from the Linux command prompt:
perl -MCPAN -e shell
This will put you into a Perl CPAN shell and if it’s the very first time you’ve ever run this, it may ask you to do some configuration first. Go ahead and do it. If you don’t know how to answer a particular question, accepting the default is usually a pretty safe bet (if you disagree with me on this, then you know enough to know how to answer the questions, so you don’t need my help). However there are a couple questions related to buffers where you have the option to not create one, and I usually don’t because I don’t spend much time in the Perl shell. Just read the questions and either use the default answer, or another suggested answer that fits your preferences. When it comes time to pick servers (from which you will download modules), just pick two or three that are close to you.
After you’ve done the configuration, just install each module (if you already have it, it may say “nothing to do” and stop). Alternately, if you configured Perl to ask before downloading dependencies, you may need to answer “yes” a few times to allow dependencies to be downloaded and installed. To install the required modules from within the CPAN shell, just do these, one at a time:
install WWW::Mechanize (only if you use the first variation of the script) install Data::Validate::IP install DBD::mysql (you might already have this).
To quit the CPAN shell, just type quit and press Enter.
Alternately, in some distributions you can get certain Perl modules from the distribution’s repository. For example, in Centos you may be able to use:
yum install perl-WWW-Mechanize.noarch (only if you use the first variation of the script) yum install perl-Data-Validate-IP.noarch yum install perl-DBD-MySQL.noarch
Or in any Debian-based Linux, including Ubuntu Server, try these:
sudo apt install libwww-mechanize-perl (only if you use the first variation of the script) sudo apt install libdata-validate-ip-perl sudo apt install libdbd-mysql-perl
It’s likely you already have the Mysql module. Depending on your distro you may have to leave off the .noarch, or find a specific version in an appropriate repository. Installing from the CPAN shell make take a bit more time for the initial configuration, and some people find the CPAN shell difficult to use, but you will always get the correct version of the module.
Note that if you use Webmin, there is a third way – you can install modules from the Other | Perl Modules | Install Module page. BUT, that may not work correctly until you have configured CPAN as mentioned above. Don’t let that stop you from trying it, though!
The Script:
There are now two versions of this script. One uses WWW::Mechanize to get your IP address from a web site that returns only your IP address, while the second uses a dig command to get your IP address. We recommend the second one, since public sites that return only your IP address have a nasty habit of disappearing. Note that as always, these WILL overflow the lines in WordPress, so you will want to cut and paste your preferred script into a text editor. Also note that WordPress MAY change apostrophes and quotes into “prettified” versions, and if it does that will totally mess up Perl. I’m going to put this in a preformatted text block so hopefully WordPress won’t change anything (it doesn’t appear that it has), but you never know. One final note, don’t confuse backticks (`) with apostrophes (‘) – backticks are used to run a command that would normally be run from a Linux command prompt.
These scripts were written for use with FreePBX 2.11; there are a couple of lines that need to be changed for FreePBX 12 and above which will be posted below the main scripts:
#!/usr/bin/perl
# This program gets the current IP address (as assigned by the ISP) from
# a web page and modifies the FreePBX Asterisk SIP settings if the
# external IP address has changed. Invoke it as cron job that runs every 5 minutes.
use strict;
use warnings;
use WWW::Mechanize;
use Data::Validate::IP qw(is_public_ipv4);
# GET CURRENT IP ADDRESS
my $mech = WWW::Mechanize->new( autocheck => 1 );
# NOTE THE http QUERY IN THE NEXT LINE - PLEASE PASTE THIS INTO YOUR WEB
# BROWSER AND MAKE SURE IT RETURNS YOUR IP ADDRESS AND NOTHING ELSE.
$mech->get('http://some_web_site_that_returns_your_IP_address');
$mech->success or die 'Cannot connect to web site';
my ($ip) = ($mech->content() =~ /(d+.d+.d+.d+)/);
# VALIDATE RESULT RECEIVED
if (is_public_ipv4($ip)) {
# SET UP TO CONNECT TO MySQL DATABASE
use DBI();
# CONNECT TO DATABASE
my $connect = DBI->connect("DBI:mysql:database=asterisk;host=localhost", "user", "pw", {'RaiseError' => 1});
# GET IP ADDRESS FROM DATABASE
my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");
# COMPARE IP ADDRESSES
if ($externip ne $ip) {
# WAIT 5 SECONDS AND RECHECK IP TO AVOID FALSE POSITIVES
sleep 5;
$mech->get('http://some_web_site_that_returns_your_IP_address');
$mech->success or die 'Cannot connect to web site';
my ($ip) = ($mech->content() =~ /(d+.d+.d+.d+)/);
if ($externip ne $ip) {
# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");
# WRITE CONFIG FILES AND RELOAD ASTERISK
`/var/lib/asterisk/bin/module_admin reload`;
# OPTIONAL SEND EMAIL TO SYSTEM ADMINISTRATOR(S)
# my $mailstring = 'echo "This is an automated message - please do not reply. Either we had a power or Internet outage (in which case there is a slight chance you may receive this message even if our IP address is unchanged), or our Internet Service Provider has changed the IP address of our phone server to ' . $ip . '" | mail -s "ISP may have changed our IP address" someaddress@gmail.com,anotheraddress@somewhere.com';
# system($mailstring);
};
};
};
Variation #2 (recommended) – note, do NOT confuse backticks and apostrophes, since both are used in this script and they are NOT interchangeable!
#!/usr/bin/perl
# This program gets the current IP address (as assigned by the ISP) from
# OpenDNS and modifies the FreePBX Asterisk SIP settings if the external IP
# address has changed. Invoke it as cron job that runs every 5 minutes.
use strict;
use warnings;
use Data::Validate::IP qw(is_public_ipv4);
my $dig = 'dig +short myip.opendns.com @resolver1.opendns.com';
my $ip=`$dig`;
chomp $ip;
if ($ip=~/((\d){1,3}\.){3}(\d){1,3}/) {
if (is_public_ipv4($ip)) {
# SET UP TO CONNECT TO MySQL DATABASE
use DBI();
# CONNECT TO DATABASE
my $connect = DBI->connect("DBI:mysql:database=asterisk;host=localhost", "user", "pw", {'RaiseError' => 1});
# GET IP ADDRESS FROM DATABASE
my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");
# COMPARE IP ADDRESSES
if ($externip ne $ip) {
# WAIT 5 SECONDS AND RECHECK IP TO AVOID FALSE POSITIVES
sleep 5;
$ip=`$dig`;
chomp $ip;
if ($ip=~/((\d){1,3}\.){3}(\d){1,3}/) {
if (is_public_ipv4($ip)) {
if ($externip ne $ip) {
# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");
# WRITE CONFIG FILES AND RELOAD ASTERISK
`/var/lib/asterisk/bin/module_admin reload`;
# OPTIONAL SEND EMAIL TO SYSTEM ADMINISTRATOR(S)
# my $mailstring = 'echo "This is an automated message - please do not reply. Either we had a power or Internet outage (in which case there is a slight chance you may receive this message even if our IP address is unchanged), or our Internet Service Provider has changed the IP address of our phone server to ' . $ip . '" | mail -s "ISP may have changed our IP address" someaddress@gmail.com,anotheraddress@somewhere.com';
# system($mailstring);
};
};
};
};
};
};
NOTES on the above scripts, including THINGS YOU MUST CHANGE:
In the first script, change both occurrences of http://some_web_site_that_returns_your_IP_address to a web address that returns only your IP address and nothing else. Enter the link into a web browser to make sure you get the expected result — it should show your external IP address and nothing else. These services tend to come and go, and you’ll need to find one that returns your IP address, and ONLY your IP address, with no extraneous HTML formatting or text. If you don’t know of such a source, then try the second variation.
These rest of this applies to both scripts:
Note the two bolded variables user and pw. These must be changed to the correct values for YOUR system. You will usually find these in one of two places. You can look in /etc/amportal.conf and look for the variables AMPDBUSER and AMPDBPASS — these will usually be near the bottom of the file in newer installs, in a “— CATEGORY: Bootstrapped or Legacy Settings —” section, but they can be anywhere in the file.
Another place they may be found is in the file /etc/freepbx.conf — in that file, look for lines similar to:
Those will give you the values to insert into the user and pw variables in the script. YOU MUST INSERT THE CORRECT VALUES OR THE SCRIPT WILL NOT WORK! By the way, if you have both of the above-mentioned files, make sure that the AMPDBUSER and AMPDBPASS variables are set to the same respective values in both files, otherwise your CDR Reports page may not work.
Finally, if you want an e-mail notification when your IP address has changed, uncomment the two lines under “# OPTIONAL SEND EMAIL TO SYSTEM ADMINISTRATOR(S)” and modify the first line appropriately (make sure you use one or more valid e-mail addresses!). BE CAREFUL NOT TO DELETE THE TRAILING APOSTROPHE (just before the semicolon). Yeah, I did that once. 🙁
IF YOUR ARE RUNNING FREEPBX 12 (NOT FreePBX 14, see below), it appears they have changed the location where the IP address is stored in the database. In that case, two sections of the script need to be changed:
Change this:
# GET IP ADDRESS FROM DATABASE
my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");
To this:
# GET IP ADDRESS FROM DATABASE
my ($externip) = $connect->selectrow_array("SELECT val FROM kvstore WHERE `key` = ?", undef, "externip");
Change this:
# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");
To this:
# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
$connect->do("UPDATE kvstore SET val = ? WHERE `key` = ?", undef, "$ip", "externip");
IF YOU ARE RUNNING FREEPBX 14, it appears they have changed the location again where the IP address is stored in the database. In that case, two sections of the script need to be changed:
Change this:
# GET IP ADDRESS FROM DATABASE
my ($externip) = $connect->selectrow_array("SELECT data FROM sipsettings WHERE keyword like ?", undef, "externip_val");
To this:
# GET IP ADDRESS FROM DATABASE
my ($externip) = $connect->selectrow_array("SELECT val FROM kvstore_Sipsettings WHERE `key` = ?", undef, "externip");
Change this:
# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
$connect->do("UPDATE sipsettings SET data = ? WHERE keyword = ?", undef, "$ip", "externip_val");
To this:
# IP HAS CHANGED SO UPDATE IP ADDRESS IN DATABASE
$connect->do("UPDATE kvstore_Sipsettings SET val = ? WHERE `key` = ?", undef, "$ip", "externip");
(Thanks to “Tony” for posing the FreePBX 14 changes in the comment section.)
Regardless of which version you are running (12 or 14), BE CAREFUL, in both of the above lines the word keyword (without quotes) is changed to `key` (with backtick quotes). If you leave out the backticks, or change them to something else such as apostrophes, IT WILL NOT WORK.
Save your script to either the /root directory or the /var/lib/asterisk/agi-bin directory, or to another location of your choosing. I named it checkip.pl, solely because that was the name of a previous script I had run and I had already created a cron job for it. You must make the script executable, for example:
chmod u+rx /var/lib/asterisk/agi-bin/checkip.pl
Of course you will specify the correct filename and directory. Now it’s time to test the script. From the Linux command prompt, navigate to the directory where you stored the script:
cd /var/lib/asterisk/agi-bin
Now run the script from the command prompt:
./checkip.pl
Hopefully you won’t see any error messages. Remember it’s going out to do a query to get your external IP address, so don’t get concerned if it takes a second or two. If you had an incorrect address stored in your FreePBX Asterisk SIP Settings configuration, it will take longer because it will reload the FreePBX configuration. The script has a couple of different checks to make sure it only stores a real IP address (and not something invalid like an error message) in the database, so if it appears to not be working, make sure the underlying call to the web server or the dig command (depending on which variation you use) is returning a valid IP address.
Usually if you do see errors they will fall into one of two categories. The first is a missing Perl module, which you will need to obtain as described above. The second is a syntax error, which you should not get if you cut and pasted the script, and made the changes noted above. If you get a permissions error, you probably forgot to make the script executable!
Setting up a cron job:
Once it runs without errors, you will want to create a cron job so it runs automatically every five minutes. Do NOT run it more often than that, or the lookup service may ban your IP address, and you don’t want that to happen (whatismyip.com would do that, which is another reason not to use them), and besides, it’s not polite to hog the resources of someone else’s server! And if you are running it on multiple servers at the same IP address, then adjust the polling speed so that the total polling from all servers doesn’t exceed once every five minutes. An occasional additional test is probably not an issue, but if you try to poll every minute you just might get banned!
The usual way to add a cron job is to run this command:
crontab -e
(If you’re not currently running as root use sudo crontab -e instead)
This will open a text editor showing your current cron jobs. Just add a new line to the bottom of the file with your new cron job. To run the script every five minutes, you could use something like this:
*/5 * * * * /var/lib/asterisk/agi-bin/checkip.pl
Or to be more specific as to when the script runs (this will run it exactly on the hour, at five minutes after the hour, at ten minutes after the hour, and so on):
Just save the changed file when you are finished. The alternate method is to use Webmin’s System | Scheduled Cron Jobs module to set up your cron job.
Final testing:
The easiest way to test to make sure this is all working is to wait until a time that there are no active calls on the system, then go to the Asterisk SIP Settings configuration page and change the External IP address to something invalid (just change the last digit of the current address and Submit Changes, then do the usual orange bar reload). On the next five minute interval, the script should detect that the external IP address doesn’t match the one stored in the database, and it will write the correct value to the database and reload Asterisk. If you watch the Asterisk CLI during this time, you should actually see the reload take place. After that, if you go back to the Asterisk SIP Settings configuration page, the correct IP address should be there. To be extra safe, you should also view the contents of the file /etc/asterisk/sip_general_additional.conf and make sure that the externip= line shows the correct IP address.
Now you don’t have to worry about frantic calls from users at inopportune times because your ISP changed your IP address and none of the phones are working, and you also won’t have any of the problems associated with the Dynamic IP method!
I want to thank Moshe Brevda for giving me the information I needed to do the MySQL database write, after a particularly frustrating middle of the night session (not helped by bumping into a truly arrogant bastard on an IRC channel), and also for one correction to this article (see my comment in the comments section below). If any “Perl purists” are reading this and you want to offer a constructive comment without giving me any attitude, I’m fine with that. But if you are like some of your I-know-it-all-and-your-coding-sucks brethren in the IRC channel, don’t even waste your time posting a comment, because I won’t approve it. No, you really DON’T need to use any other Perl database modules to do this simple task, and no, I DON’T want to learn your philosophy of writing Perl code (there are some really sucky mom’s-basement-dwellers inhabiting the #perl IRC channel — some of those folks really need to get professional help, and that is all I will say about that). EDIT: Credit to the article Quickly Get an External IP Address from the Command Line (OS X Daily) for revealing the method of using dig with OpenDNS to get your IP address.
NOTE: As usual, there are no warranties — we’re experimenters here, and sometimes we don’t catch all the bugs, especially on the first go around! However, I would assume that anyone who is running a “professional” installation would pay their ISP for a true static IP address (one that never changes), and therefore wouldn’t need one of these scripts in the first place.
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.
I wanted to find a way to send Caller ID popups to a Ubuntu Linux box, and in the process I discovered a different method of sending such notifications. There are pros and cons to using the new method, so let me explain those first:
Pros:
Can send notifications to any computer that supports command line generated notifications (so it could also be used with Growl, if you can use growlnotify from a command prompt to generate a notification).
Can send notifications to any computer that you can SSH into, provided you have it set up to use public/private key authentication rather than password authentication.
Cons:
Notifications typically display a couple of seconds later than under the previous method. I suspect this is due to the SSH authentication taking a second or two.
It’s a little bit more complicated to set this up, though not horribly so.
Because this uses SSH and requires that Asterisk be granted permission to establish an SSH connection as the super user (by using sudo), there may be unforeseen security risks.
Read that last point again, and please understand that as with all projects on this site, I offer this for experimental purposes only. I explicitly do not warrant this method as being 100% secure, nor will I tell you that it could not be exploited to do bad things on your system. I don’t think it can (and feel free to leave a comment if you think I’m wrong), but I just don’t know that for sure. So, if you decide to use anything in this article, you agree to assume all risks. If you’re the type that likes to sue other people when something goes wrong, then you do not have permission to use this code. We’re all experimenters here, so no guarantees!
As with the previous method, you must have the Perl language installed on your Asterisk server, and you must have the Asterisk::AGI module installed (I’m going to assume you know how to install a Perl module from the CPAN repository – if you have Webmin installed, it can be done from within Webmin). Chances are you already have Asterisk::AGI installed, unless you built your Asterisk server “from scratch” and never installed it.
There’s one additional thing you must do on the Asterisk server before this will run, and that’s allow Asterisk to run the ssh command as root. So, add this to your /etc/sudoers file (probably at the very end, but in any case it should be obvious where to add this because it will be in a section where Asterisk is granted similar privileges with regard to other programs):
asterisk ALL = NOPASSWD: /usr/bin/ssh
Next you want to copy and paste the following Perl script to the filename /var/lib/asterisk/agi-bin/notifysend.agi on your Asterisk server (to create a non-existent file, you can use the touch command, and after that you can edit it in Midnight Commander or by using the text editor of your choice). If this code looks somewhat familiar, it’s because it’s adapted from some code that originally appeared in a FreePBX How-To, which I have modified.
#!/usr/bin/perl
use strict;
use warnings;
use Asterisk::AGI;
my $agi = new Asterisk::AGI;
my %input = $agi->ReadParse();
# Next two lines fork the process so Asterisk can get on with handling the call
open STDOUT, '>/dev/null';
fork and exit;
my $num = $input{'callerid'};
my $name = $input{'calleridname'};
my $ext = $input{'extension'};
my $user = $ARGV[0];
my $ip = $ARGV[1];
if ( $ip =~ /^([0-9a-f]{2}(:|$)){6}$/i ) {
$ip = $agi->database_get('growlsend',uc($ip));
}
# OMIT this section if you don't want IP address
# checking (e.g. you want to use foo.bar.com)
unless ( $ip =~ /^(d+).(d+).(d+).(d+)$/ ) {
exit;
}
if ( $ARGV[2] ne "" ) {
$ext = $ARGV[2];
}
my @months = (
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
);
my @weekdays = (
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
);
my (
$sec, $min, $hour, $mday, $mon,
$year, $wday, $yday, $isdst
) = localtime(time);
my $ampm = "AM";
if ( $hour > 12 ) {
$ampm = "PM";
$hour = ( $hour - 12 );
}
elsif ( $hour eq 12 ) { $ampm = "PM"; }
elsif ( $hour eq 0 ) { $hour = "12"; }
if ( $min < 10 ) { $min = "0" . $min; }
$year += 1900;
my $fulldate =
"$hour:$min $ampm on $weekdays[$wday], $months[$mon] $mday, $year";
# Next two lines normalize NANP numbers, probably not wanted outside of U.S.A./Canada/other NANP places
$num =~ s/^([2-9])(d{2})([2-9])(d{2})(d{4})$/$1$2-$3$4-$5/;
$num =~ s/^(1)([2-9])(d{2})([2-9])(d{2})(d{4})$/$1-$2$3-$4$5-$6/;
my $cmd = qq(./remotenotify.sh "$name" "$num calling $ext at $fulldate");
$cmd = "sudo ssh $user@$ip '$cmd'";
exec "$cmd";
Also, if you want to be able to specify computers that you wish to send notifications to using MAC addresses rather than IP addresses (in case computers on your network get their addresses via DHCP, and therefore the IP address of the target computer can change from time to time), then you must in addition install the following Perl script (if you have not already done so when using the previous method). Note that if you have a mix of computers on your network and you are using both the new and old methods, you only need to do this once — it works with both methods (hence the reference to “growlsend” in the database and “gshelper” as the name of this script). Call it /var/lib/asterisk/agi-bin/gshelper.agi and note that there is a line within it that you may need to change to reflect the scope of your local network:
#!/usr/bin/perl
use strict;
use warnings;
my ($prev, @mac, @ip);
# Change the 192.168.0.0/24 in the following line to reflect the scope of your local network, if necessary
my @nmap = `nmap -sP 192.168.0.0/24|grep -B 1 MAC`;
foreach (@nmap) {
if (index($_, "MAC Address:") >= 0) {
@mac = split(" ");
@ip = split(" ",$prev);
`/usr/sbin/asterisk -rx "database put growlsend $mac[2] $ip[1]"`;
}
$prev=$_;
}
Make sure to modify the permissions on both scripts to make them the same as other scripts in that directory (owner and group should be asterisk, and the file should be executable), and if you use the gshelper script, make sure to set up a cron job to run it every so often (I would suggest once per hour, but it’s up to you).
Now go to this page and search for the paragraph starting with, “After you have created that file, check the ownership and permissions” (it’s right under a code block, just a bit more than halfway down the page) and if you are using FreePBX follow the instructions from there on out (if you are not using FreePBX then just read that section of the page so you understand how this works, and in any case ignore the top half of the page, it’s talking about a different notification system entirely). However, note that the syntax used in extensions_custom.conf differs from what is shown there, depending on whether you are specifying an IP address or a MAC address to identify the target computer.
First, if you are specifying the IP address of the target computer, then instead of using this syntax:
Note that username is the account name you use when doing an ssh login into the destination system, and it should also be the desktop user on the system (not root!). Let’s say that the system is currently at IP address 192.168.0.123. In order for this to work, you need to be able to ssh into your Ubuntu box from your Asterisk server, using the following command from the Asterisk server’s command line:
It’s probably easiest to configure each computer that is to receive notifications to use a static IP address. But note that if you use the above code and have the gshelper.agi program running as a cron job, then after the first time it has run while the computer to receive the notifications is online you should be able to use a computer’s MAC address instead of the IP address. This only works if you’ve used the modified script on this page, not the one shown in the FreePBX How-To. As an example, instead of
(the above is all one line) where 01:23:45:AB:CD:EF is the MAC address of the computer you want to send the notification to. Once again, just in case you missed it the first time I said it, this won’t work until the gshelper.agi script has been run at least once while the computer to receive the notifications was online. If for some reason it still doesn’t appear to work, run the nmap command (from gshelper.agi) including everything between the two backticks (`) directly from a Linux command prompt and see if it’s finding the computer (depending on the size of your network, it might be several seconds before you see any output, which is why I don’t try to run this in real time while a call is coming in).
If you are NOT running FreePBX, but instead writing your Asterisk dial plans by hand, then you will have to insert a line similar to one of the above examples into your dial plan, except that you don’t need the four asterisks (****) in front of the extension number, and if it’s not the first line in the context, you’ll probably want to use n rather than 1 for the line designator (and, you won’t be putting the line into extensions_custom.conf because you probably don’t have such a file; instead you’ll just put it right in the appropriate section of your dial plan). In other words, something like this (using extension 525 as an example):
This line should go before the line that actually connects the call through to extension 525. I do not write Asterisk dial plans by hand, so that’s about all the help I can give you. And if you don’t write your dial plans by hand, but you aren’t using FreePBX, then I’m afraid you’ll have to ask for help in whatever forum you use for advice on the particular software that you do use to generate dial plans, because I can’t tell you how to insert the above line (or something like it) into your dial plan.
Now is where it gets just a bit more complicated than in the original method. If you have followed the above instructions, you’ll be able to send the notifications to the remote system using SSH, but there will be nothing there to receive them. So we have to create a small script on the receiving system to do something with the received notifications. That script will vary depending on the receiving system, but it must be named remotenotify.sh and it must be placed in the destination user’s home directory, and don’t forget to make it executable! Here’s one that will work in most Ubuntu installations that have Notify OSD installed:
Those two lines are all you need. On a different type of system (or if you have multiple displays) you may need to or wish to do something different. For example, as I mentioned above, if the destination system is running Growl then your remotenotify.sh script will need to call growlnotify, but beyond that I wouldn’t know what to use there (EDIT: But if the target system is a Mac that is running OS X, a pretty good guess would probably be that you’d only need one line, something like this:
growlnotify -s -p 1 -a Telephone -m "$2" $1
In this case it should make the notification sticky until dismissed by the user, give it a priority of 1 — the default is 0 — and use the application icon from the “Telephone” application if you have it installed. Instead of -a to specify an application’s icon you could use -I followed by a path to an .icns file that contains an icon you want to use. Type growlnotify –help to see all the growlnotify options. Oh, and before you can make an SSH connection to a Mac you have to go into System Preferences | Sharing and turn on Remote Login).
The beauty of this approach is that you can make the remotenotify.sh script as simple or as complicated as you need — you could even make it forward a notification to other devices if you wish, but figuring out how to do that is up to you (if you come up with something good, please leave a comment and tell us about it!).
If you’re running Ubuntu on the target system, here’s a few articles you may wish to use to help you get your notifications to look the way you want them to appear:
If you want to be able to review missed notifications, you may be able to use this (as a side note, why don’t they have something like this for Growl?):
The idea behind the shell script that runs on the target system was found in a comment on the following article, which may be of special interest to MythTV users:
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.
Notice
EDIT March, 2014 and August 2020: If you are running OS X Mavericks or later, or any version of MacOS we recommend that you do NOT use the script shown here, but instead send notifications to a XMPP/Jabber account and use either Apple’s Messages app (formerly iChat) or a third party messaging program such as Adium to receive them, since the message will then display in the Notifications Center and you do not need Growl. See How to send various types of notifications on an incoming call in FreePBX for more information. You may also find this thread on the RasPBX forum useful.
What follows will probably not work on ANY currently supported version of MacOS and is left here as a historical reference only.
Quite some time ago, I wrote a post explaining how you could poll a Linksys or Sipura VoIP adapter or phone once per second, and whenever there was an incoming call, generate a notification popup on your computer, if you have the Growl notification service installed. However, that method doesn’t work if you’re not using a Linksys or Sipura phone or device.
If you are running Asterisk, there’s another way to do it, and that’s to get Asterisk to send the notifications directly. In order for this to work, the computer on which you want to receive the notifications has to be running Growl (under Mac OS X) or Growl for Windows. You must also configure Growl to receive network notifications. I will note here that if you are using a Mac and have never done that before, you may want to make sure that Growl network notifications work before proceeding, because it appears that under OS X, it’s pretty much a crap shoot whether Growl network notifications will work at all, and when they don’t the Growl folks apparently have no clue as to why they don’t. It seems to be a machine-specific thing – on some Macs they work fine, while on others they don’t work at all.
You must have the Perl language installed on your Asterisk server, and you must have the Net::Growl and Asterisk::AGI modules installed (I’m going to assume you know how to install a Perl module from the CPAN repository – if you have Webmin installed, it can be done from within Webmin). Chances are you already have Asterisk::AGI installed, unless you built your Asterisk server “from scratch” and never installed it, but if you’ve never installed Net::Growl you’ll need to do that first.
Next you want to copy and paste the following Perl script to the filename /var/lib/asterisk/agi-bin/growlsend.agi on your Asterisk server (to create a non-existent file, you can use the touch command, and after that you can edit it in Midnight Commander or by using the text editor of your choice). If this code looks somewhat familiar, it’s because it’s adapted from some code that originally appeared in a FreePBX How-To, which I modified.
#!/usr/bin/perl
use strict;
use warnings;
use Net::Growl;
use Asterisk::AGI;
my $agi = new Asterisk::AGI;
my %input = $agi->ReadParse();
my $num = $input{'callerid'};
my $name = $input{'calleridname'};
my $ext = $input{'extension'};
my $ip = $ARGV[0];
if ( $ip =~ /^([0-9a-f]{2}(:|$)){6}$/i ) {
$ip = $agi->database_get('growlsend',uc($ip));
}
unless ( $ip =~ /^(d+).(d+).(d+).(d+)$/ ) {
exit;
}
open STDOUT, '>/dev/null';
fork and exit;
if ( $ARGV[2] ne "" ) {
$ext = $ARGV[2];
}
# Define months and weekdays in English
my @months = (
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
);
my @weekdays = (
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
);
# Construct date/time string
my (
$sec, $min, $hour, $mday, $mon,
$year, $wday, $yday, $isdst
) = localtime(time);
my $ampm = "AM";
if ( $hour > 12 ) {
$ampm = "PM";
$hour = ( $hour - 12 );
}
elsif ( $hour eq 12 ) { $ampm = "PM"; }
elsif ( $hour eq 0 ) { $hour = "12"; }
if ( $min < 10 ) { $min = "0" . $min; }
$year += 1900;
my $fulldate =
"$hour:$min $ampm on $weekdays[$wday], $months[$mon] $mday, $year";
# Next two lines normalize NANP numbers, probably not wanted outside of U.S.A./Canada/other NANP places
$num =~ s/^([2-9])(d{2})([2-9])(d{2})(d{4})$/$1$2-$3$4-$5/;
$num =~ s/^(1)([2-9])(d{2})([2-9])(d{2})(d{4})$/$1-$2$3-$4$5-$6/;
register(host => "$ip",
application=>"Incoming Call",
password=>"$ARGV[1]", );
notify(host => "$ip",
application=>"Incoming Call",
title=>"$name",
description=>"$numnfor $extn$fulldate",
priority=>1,
sticky=>'True',
password=>"$ARGV[1]",
);
Also, if you want to be able to specify computers that you wish to send notifications to using MAC addresses rather than IP addresses (in case computers on your network get their addresses via DHCP, and therefore the IP address of the target computer can change from time to time), then you must in addition install the following Perl script. It requires a command-line utility caller arp-scan so install that if you need to – I used to use nmap for this but they changed the output format, making it harder to parse, and arp-scan is much faster anyway. Call it /var/lib/asterisk/agi-bin/gshelper.agi and note that there are two references to 192.168.0… within it that you may need to change to reflect the scope of your local network, if your network’s IP addresses don’t start with 192.168.0.:
#!/usr/bin/perl
use strict;
use warnings;
my @mac;
# Change the following lines to reflect the scope of your local network, if necessary
my @arp = `arp-scan --quiet --interface=eth0 192.168.0.0/24`;
foreach (@arp) {
if (index($_, "192.168.0.") == 0) {
@mac = split(" ");
`/usr/sbin/asterisk -rx "database put growlsend \U$mac[1] $mac[0]"`;
}
}
Make sure to modify the permissions on both scripts to make them the same as other scripts in that directory (owner and group should be asterisk, and the file should be executable), and also, if you use the gshelper script, make sure to set up a cron job to run it every so often (I would suggest once per hour, but it’s up to you).
Now go to this page and search for the paragraph starting with, “After you have created that file, check the ownership and permissions” (it’s right under a code block, just a bit more than halfway down the page) and if you are using FreePBX follow the instructions from there on out (if you are not using FreePBX then just read that section of the page so you understand how this works, and in any case ignore the top half of the page, it’s talking about a different notification system entirely). But note that if you use the above code and have the gshelper.agi program running as a cron job, then after the first time it has run while the computer to receive the notifications is online you should be able to use a computer’s MAC address instead of the IP address. This only works if you’ve used the modified script on this page, not the one shown in the FreePBX How-To. As an example, instead of
(the above is all one line) where 01:23:45:AB:CD:EF is the MAC address of the computer you want to send the notification to. Once again, just in case you missed it the first time I said it, this won’t work until the gshelper.agi script has been run at least once while the computer to receive the notifications was online. If for some reason it still doesn’t appear to work, run the nmap command including everything between the two backticks (`) directly from a Linux command prompt and see if it’s finding the computer (depending on the size of your network, it might be several seconds before you see any output, which is why I don’t try to run this in real time while a call is coming in).
If you are NOT running FreePBX, but instead writing your Asterisk dial plans by hand, then you will have to insert a line similar to one of the above examples into your dial plan, except that you don’t need the four asterisks (****) in front of the extension number, and if it’s not the first line in the context, you’ll probably want to use n rather than 1 for the line designator (and, you won’t be putting the line into extensions_custom.conf because you probably don’t have such a file; instead you’ll just put it right in the appropriate section of your dial plan). In other words, something like this (using extension 525 as an example):
This line should go before the line that actually connects the call through to extension 525. I do not write Asterisk dial plans by hand, so that’s about all the help I can give you. And if you don’t write your dial plans by hand, but you aren’t using FreePBX, then I’m afraid you’ll have to ask for help in whatever forum you use for advice on the particular software that you do use to generate dial plans, because I can’t tell you how to insert the above line (or something like it) into your dial plan.
Virtually everything in this article has already been published in one place or another, but I wanted to get it into an article with a relevant title and cut out some of the extraneous explanations and such. There are links to all the original sources throughout the article, so feel free to follow those if you want more in-depth commentary.
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.
PLEASE NOTE: This article has been updated as of December 30, 2008. This now works with a Mac or Win32 computer (and Linux computers with libnotify installed or readily available, such as those running Ubuntu) and has been updated to reflect that fact. Also, please note that previous versions may have failed on devices/phones with more than two lines – this is (hopefully) fixed as of version 0.7.
If all of the following are true:
You have a Macintosh computer with OS X installed, or a PC with any 32-bit version of Windows installed (basically Windows ’98 through XP), or any version of Linux with libnotify installed
You have Growl (if you have a Mac) or Snarl (if you have a PC) notifications installed (EDIT: There is now a version of Growl for Windows but at the moment I only have an experimental version of the script for that – see bottom of this page for more information. It MIGHT work with 32-bit OR 64-bit Windows 7 – feel free to test it).
You have a Linksys or Sipura VoIP adapter on your local subnet or home network and receive calls over it
You would like to see Growl, Snarl or libnotify popups on your computer when a call comes in, showing the caller’s name and number, along with the line that the call came in on and the time and date the call arrived (in case you are out when the call comes in)
You have previously run Perl scripts on your computer, OR are reasonably good at following instructions and problem-solving
AND you are willing to run a script that comes with NO WARRANTY whatsoever (if it breaks, you can keep all the pieces)
Then download this file (now at version 0.92), unzip it and read the Instructions.txt file in the folder appropriate to your computer.
This script is being offered under the GNU General Public License, so if you want to modify it to work on other platforms, you can do that under certain conditions (see the Instructions.txt file for details). Mainly, I’d hope that you’d contribute the modifications back (and please leave a comment on this article if you do that).
I don’t have any kind of regular web page up for this yet, for one thing it’s very rough (very little error-checking) and for another I’m very tired, having spent way too many late nights trying to get this to work. So this post will be more terse than most of my posts, but I think most everything you need to know is in Instructions.txt (and for Mac users, the “How to run at login.rtfd” file) inside the .zip file. Feel free to repost this information to other forums if you think anyone else might be interested.
For those Mac users that wish this were an app: I understand that there is an app called Platypus that allows Perl scripts (and any other types of scripts) to be converted to OS X app bundles. However, what it does not seem to include is any way to specify the command line options, or to load any missing Perl modules. So for now, this script will probably only be usable by those with sufficient knowledge to run a Perl script on their Mac. If I were a bit more knowledgeable, I’d build a preference pane to go in System Preferences, and then have the script read that for its configuration options. But I still have no idea how to make an app install missing Perl modules, particularly when OS X does not come with “make” installed until and unless the Developer Tools are installed (adding something like 3 GB of stuff that is mostly useless to non-developers to your hard drive!).
EDIT: I read somewhere that you can install make without installing the bloated Developer Tools package if you instead install Fink. Then, from a terminal prompt, you can type fink -b install make and supposedly that will do the trick. However, I am told that Fink has not been updated for Snow Leopard, but there is a make package in Rudix that should work with Snow Leopard (mind your paths – Rudix installs make in the /usr/local/bin directory and by default CPAN expects it in /usr/bin, so you may want to adjust the path during CPAN setup, or make a symbolic link in /usr/bin). Since I have not personally tried either of these I have not updated the instructions in the download to reflect this, but if it works you can skip the whole process involved in installing the Developer Tools.
Because this is a Perl script, it lends itself to custom modifications. For example, let’s suppose you have this script running on a Mac, and you are sending Growl notifications to the Mac, but you also have a home theater PC that runs XBMC and/or Boxee, and you’d like to send Caller ID notifications to it as well. Assuming that Boxee and/or XBMC is configured to allow control via a Web interface, at a fixed IP address and port (192.168.0.150 port 8080 in this example), you could add a line such as this to the script (this is all one line; select and copy to get it all if it gets truncated on your display):
The above assumes that you have placed the icon file phone.png (shown at right — right click on the icon and save it) in the user home directory on the destination system (the one running XBMC or Boxee), and that you change ‘username’ to the actual name of the user’s home directory. Note that the icon path requires %2F in place of forward slashes (therefore %2Fhome%2Fusername%2Fphone.png really means /home/username/phone.png) and this refers to the icon directory and filename — if you choose not to use an icon then leave that part out, along with the %2C that comes just before it. If you are running XBMC or Boxee on the same system that’s running the script then you should be able to replace 192.168.0.150 with localhost or 127.0.0.1. The above line should be inserted just above the comment line “# Make output string in chosen format” near the end of the Perl script. Keep in mind that this won’t work if you don’t enable control via Web server in XBMC or Boxee, and make sure the port number matches the port in your added line. Depending on the skin you use, this is generally accomplished by going to Settings, then Network (and in Boxee, then Servers). Then check “Allow control of XBMC via HTTP” (in XBMC) or enable the Web server (in Boxee) and verify the port number is correct.
Starting in Version 0.7 there is a minimal logging function, allowing all detected incoming calls (whether answered or not) to be saved to a text file and/or a comma-quote delimited file. I probably could support other simple formats, but don’t even think about asking for anything more complex (like a rather humorous friend of mine who asked for MySQL integration – considering that he knows how little knowledge I have about Perl programming, and that I have even less knowledge about databases, I’m sure he thought it extremely amusing to make that request). The one thing I really don’t like about offering these scripts in Perl is that it requires the user to know how to install modules from CPAN (or an alternative source if using Win32), but I barely know how to do this stuff in Perl and don’t know any other languages (well, except for QBASIC under MSDOS, but that’s even less compatible across platforms than Perl!).
Starting in Version 0.9 you can use a plain-text file of number-name substitutions, so (for example) if calls from a particular number always display a cryptic Caller ID name, you can change them to say “Uncle Bob” (or some other name if Bob’s not your Uncle, or it’s someone else’s number!). Read the sample config file to see the file formats. Note that the plain text file of number-name substitutions is a separate file, not a section of the optional configuration file, and also note that you must enter the numbers exactly as your VoIP provider sends them (in other words, if they send 8005551234 and you use 18005551234 or 800-555-1234 it will NOT match!).
Starting in Version 0.91 you can use a plain-text file of number-path/file substitutions, so (for example) if calls from a particular number are always from Uncle Bob, you can display Uncle Bob’s picture as the icon whenever a call arrives from that number. Read the sample config file for more information. Note that the plain text file of number-path/file substitutions is a separate file, not a section of the optional configuration file, and also note once again that you must enter the numbers exactly as your VoIP provider sends them.
Version 0.92 sets a rather short timeout on page fetches (still much longer than should be necessary to get the data), in an attempt to resolve a problem where very occasionally the script would just go into a coma, not exiting cleanly but still using memory and CPU cycles, without doing anything useful. I have been running this version for over six months now and have yet to see the script go into a coma, as it often seemed to do in previous versions.
(EDIT added September, 2010:) NOTE regarding EXPERIMENTAL version to work with Growl for Windows. You should still download the main archive to get the instructions and such, but if you’d prefer to use Growl for Windows rather than Snarl, you can try this experimental version of the script. If you do try it, please let me know if it works as expected (and thanks to Andy Singh for his help with getting this working under Windows 7). Please read the Perl source code to find the module requirements (mentioned on or near line 15 of the script) as they differ slightly from the Snarl version.
Recent Comments