jueves, 28 de marzo de 2013

Adding custom tasks to Ubiquiti cameras


(versión en español)

After using Axis network cameras for several years (with no such satisfactory results in a long term), I began to consider alternatives. That's how my hands went through different brands and models that did not cover the minimum expectations that I expected to completely abandon Axis. Sounds like a bad joke that most of these cameras whose firmware is usually based on Linux, have mangement interfaces particulary oriented to browsers like Internet Explorer, while inspecting the camera from a browser in Linux usually look bad, with distorted screens, unusable menu options, among other defects.

Eventually I started doing some tests with Ubiquiti AirCam mini camera. From the beginning seemed to possess the characteristics necessary to meet the intended use. Also I became interested in some extra features, such as having an SNMP agent, and even has an SSH server, which allows me to access the camera in console mode.

When setting up the dynamic DNS service I encountered a problem: apparently is prepared to work with dyndns.org only, which unfortunately doest not provide for free accounts anymore.  The camera manual says otherwise, about supporting a wide range of dynamic DNS services, but the fact is that the log file information invalidates this claim. When configuring the dynamic DNS service with an no-ip.com user, the camera log shows that dynamic dns client is trying to connect to the site http://members.dyndns.org. Inquiring about it, I found a way to add tasks or services to the camera without redoing the firmware (although the latter is also an option, as Ubiquiti provided source code for that).

Getting familiar with the environment

While accessing the camera usign ssh, we find a typical file system for a Linux distribution on an embedded system (the reference to busybox from binaries, for instance). Digging a bit in the /etc/rc.d, we find that rc.sysinit script sets kernel parameters for the camera, and rc script starts services and plugins.

rc script uses functions defined in the file /usr/etc/rc.d/rc.funcs, particularly rc_start() function, where we can find references to some external scripts called /etc/persistent/rc.prestart and /etc/persistent/rc.poststart. Such as the name implies, these scripts are executed (if any) before and after starting the services, respectively. The /etc/persistent directory allows to save scripts and files: all we have to do is save the configuration after making changes, and the changes are not going to be lost on the next reboot.

crond daemon is one of the services supported by the camera that are not started from the beginning. I'm going to use that service to run a script periodically. This script determines if the public IP has changed on our connection, and then update this information in no-ip.com.


Generating additional files and scripts

As mentioned above, we will generate a file called /etc/persistent/rc.poststart. The content of this file is showing next:


#!/usr/bin/sh
mkdir /etc/crontabs
cp -a /etc/persistent/no-ip.crontab /etc/crontabs/root
echo "root" > /etc/crontabs/cron.update
crond


This script creates directory /etc/crontabs (used by crond), copy a file (whose contents are shown in the next paragraph) to that directory with the name of the user who will run the task periodically (root in this case), and generates a file named cron.update whose content is the name of the user that has been modified his cron entry (as a flag to crond daemon to reread his configuration files). As a last step, the script starts crond daemon. rc.poststart must have execute permissions.

File /etc/persistent/no-ip.crontab has a single line, and is shown next:

*/5 * * * * /etc/persistent/no-ip.client >/dev/null 2>&1

This line tells crond to run /etc/persistent/no-ip.client  script (whose contents are shown in the next paragraph) every 5 minutes, discarding screen output.

Finally, /etc/persistent/no-ip.client script contains the following:


#!/usr/bin/sh

DDNSUSER="UsuarioNOIP"
DDNSPASS="PasswordNOIP"
DDNSHOST="HostnameNOIP.no-ip.org"

IPADDRESS=`wget http://ipecho.net/plain -O - -q ; echo`
if [ "${IPADDRESS}" = "" ]; then
exit 1
fi

IPSAVED=""
if [ -f /tmp/ip.publico ]; then
IPSAVED=`cat /tmp/ip.publico`
fi

if [ "${IPADDRESS}" != "${IPSAVED}" ]; then
echo ${IPADDRESS} > /tmp/ip.publico
logger Se detecta IP publico ${IPADDRESS}
wget -q -O /dev/null "http://${DDNSUSER}:${DDNSPASS}@dynupdate.no-ip.com/nic/update?hostname=${DDNSHOST}&myip=${IPADDRESS}" true
fi


This script has defined three variables:

DDNSUSER: our user for no-ip.com
DDNSPASS: our password  for no-ip.com
DDNSHOST: hostname as defined in no-ip.com


The script gets the public IP address of our connection, using http://ipecho.net site, and stores it in the IPADDRESS variable. It also compares this value with the IP address stored in the file /tmp/ip.publico, saved from previous executions of the script (after the camera starts running, this file does not exist so the update of non-ip.com runs at least once). It also generates an entry in the log, informing the public IP address change detected. This script also must have execute permissions.


Once we generated the required files, you can save these changes executing the command:

cfgmtd -w -p /etc/

This command saves changes to flash memory.

We are now ready to restart the camera (using reboot command is an option), and check if the changes are maintained.


Controlling changes

If changes were successful, we see that:
  1. Files added in /etc/persistent are still there
  2. crond process is running.
  3. Now there are messages from crond in the system log, such as:
Mar 28 14:27:19 192.168.1.20 crond[292]: time disparity of 586527 minutes detected
Mar 28 14:30:01 192.168.1.20 crond[292]: crond: USER root pid 314 cmd /etc/persistent/no-ip.client >/dev/null 2>&1
Mar 28 14:30:02 192.168.1.20 root: Se detecta IP publico 1.2.3.4
Mar 28 14:35:01 192.168.1.20 crond[292]: crond: USER root pid 323 cmd /etc/persistent/no-ip.client >/dev/null 2>&1

The first message is a notice from crond, about which there was a significant shift of time. That is because the camera has no clock, and starts with an incorrect date and time until the clocks synchronizes through the Internet using NTP.

In the first run of the script, we see that the IP address was correctly detected, implying that the update to no-ip was succesfull. The value of this IP address should be correctly stored in the /tmp/ip.publico file.

It is left to the curious, the revision of cgi scripts for camera management, to detect the reason of this "bug" (assuming there is a bug). Anyway, if the apparent bug was due to a misunderstanding on my part, still is very useful to know some mechanisms for modifying or adding features to the camera.






No hay comentarios:

Publicar un comentario