Intro
Those of you who own ASUS NAS M-25 might have noticed the weird behavior of its fan. The web interface is said to have a control for it, but I have searched it thoroughly and didn't find anything that has to do with fan.
Leaving it permanently on or off is not an option. By the way, when in terminal (via telnet) one can issue the following command
to turn the fan on at low rate:
# cpu_ctl fan_low
Similarly, use fan_stop and fan_high, for disabling and running the fan at its maximum correspondingly. Using these commands manually when intensive writing/reading is expected is not an option either. Yes, this is better than nothing, but you have to have telnetd enabled all the time, you have to log in and run the command.
Let us take a look at what can be done. Log in to your NAS. Remember that once NAS is started the telnet daemon is kept alive for 3 minutes, so we might want make sure it is alive longer, for at least this session. This is done by:
# ps | grep prod # kill <pid of product_test_daemon>
What other processes are running there?
# ps | grep fan 496 root 3528 S fancontrold
Hm, the daemon is running but it seems it is not doing what it is supposed to do. I would like to know what options does it have.
# fancontrold -h fancontrold: invalid option -- h [1] + Stopped fancontrold -h #
It looks like no way to find out. No need to have 2 instances running, so I just kill the last one.
# ps | grep fan 496 root 3528 S fancontrold 3227 root 1680 T fancontrold -h # kill -9 3227 #
Next idea is to try to find its config. I suppose it may be in /etc.
# ls -l /etc drwxr-xr-x 2 root root 1024 Oct 28 06:00 hotplug -rwxrwxrwx 1 root root 3572 Oct 27 13:43 iTunescript -rwxr-xr-x 1 root root 248 Oct 27 13:43 mt-daapd.playlist -rw-r--r-- 1 root root 188 Oct 27 13:43 nsswitch.conf drwxr-xr-x 2 root root 1024 Oct 28 06:00 pam.d drwxr-xr-x 2 root root 1024 Oct 28 06:00 plugins -rw-r--r-- 1 root root 1925 Mar 3 22:15 pure-ftpd.pem -rwxr-xr-x 1 root root 6500 Oct 27 13:43 rc.sh lrwxrwxrwx 1 root root 20 Oct 28 06:00 rtc.conf -> /system/cfg/rtc.conf -rwxr-xr-x 1 root root 75 Oct 27 13:43 start_services.sh -rwxr-xr-x 1 root root 73 Oct 27 13:43 stop_services.sh -rwxr-xr-x 1 root root 73 Oct 27 13:43 stop_services_firmwareupgrade.sh -rwxr-xr-x 1 root root 836 Oct 27 13:43 twonky.sh -rwxr-xr-x 1 root root 430 Oct 27 13:43 udhcpd.conf -rwxr-xr-x 1 root root 348 Oct 27 13:43 udhcpd.conf.def lrwxrwxrwx 1 root root 32 Oct 28 06:00 upnp_all_format.conf -> /system/cfg/upnp_all_format.conf lrwxrwxrwx 1 root root 33 Oct 28 06:00 upnp_open_format.conf -> /system/cfg/upnp_open_format.conf -rwxrwxrwx 1 root root 53 Oct 27 13:43 upnpc_fail.sh -rwxrwxrwx 1 root root 1679 Oct 27 13:43 upnpc_start.sh -rwxrwxrwx 1 root root 1318 Oct 27 13:43 upnpc_stop.sh -rwxrwxrwx 1 root root 325 Oct 27 13:43 upnpc_success.sh -rwxrwxr-x 1 root root 222 Oct 27 13:43 usbplug.sh -rw-r--r-- 1 root root 24 Oct 28 06:00 version.txt
Nothing here (I remove standard configs from the output). But notice that rc.sh script. We will have to look inside it some time later.
As a workaround solution we can write a script to be executed by cron, that will look at current temperature and when it goes high, it just turns the fan on, otherwise, turn it off.
Problem 1: How to find out the current temperature?
The web interface for NAS has a "System status" page under "Status" on the left side. It shows us the temperature of the whole system and for each hard drive.
HERE GOES THE IMAGE
It is time to find out how it is accomplished, so that we can use that approach in our script.
# cd /web/www/data ... ! web interface pages are most probably somewhere here... # ls CfgBackup function index.php server GetValue.php home language status JS iepngfix.htc loading.html style account images login_error.php disk imagesbutton-next-disable.gif logout.php empty.html index.html maintenance
Those directories look like the Web interface's tabs on the left. We need Status.
# cd status # ls GetValue.php hd_status.php printer_status.php syslog.php system_status.php
I think system_status.php is what we are looking for. There must be several occurrences of word "temperature" in this page.
# cat system_status.php | grep -in temp 19:$SYSTEM_TEMPERATURE_C=query("/system/system_information/temperature_c"); 20:$SYSTEM_TEMPERATURE_F=query("/system/system_information/temperature_f"); 21:$BATTERY=query("/temp/ups/percent"); 22:$STATUS=query("/temp/ups/state"); 28:var hdd_temp_info = new Array(); 29:var hdd_temp_str = "" 32:var usb1 = "<? echo query("/temp/usbport1/type"); ?>"; 33:var usb2 = "<? echo query("/temp/usbport2/type"); ?>"; 34:var usb3 = "<? echo query("/temp/usbport3/type"); ?>"; 35:var man1 = "<? echo query("/temp/usbport1/manufacturer"); ?>"; 36:var man2 = "<? echo query("/temp/usbport2/manufacturer"); ?>"; 37:var man3 = "<? echo query("/temp/usbport3/manufacturer"); ?>"; 38:var pro1 = "<? echo query("/temp/usbport1/product"); ?>"; 39:var pro2 = "<? echo query("/temp/usbport2/product"); ?>"; 40:var pro3 = "<? echo query("/temp/usbport3/product"); ?>"; 47: if (hdd_temp_info.length != 0) 49: hdd_temp_str = ""; 50: for (var i = 0; i < hdd_temp_info.length; i++) 52: if (hdd_temp_info[i]=="") 55: if (hdd_temp_str != "") 56: hdd_temp_str += ", "; 57: hdd_temp_str += "<?=$T_Slot ?> " + (i+1) + ": " + hdd_temp_info[i]; 59: getEle("hdd_temperature").innerHTML = hdd_temp_str; 60: getEle("hdd_temperature_div").style.display = ""; 63: if (hdd_temp_str != "") 64: getEle("hdd_temperature_div").style.display = ""; 66: getEle("hdd_temperature_div").style.display = "none"; 78: getEle("hdd_temperature_div").style.display = "none"; 168: <?=$T_System_Temperature ?>: 171: <span class="blackcolor" id="sys_temperature"><?=$SYSTEM_TEMPERATURE_C ?>б╟C/<?=$SYSTEM_TEMPERATURE_F ?>б╟F</span> 174: <tr id="hdd_temperature_div"> 176: <?=$T_HD_TEMP ?>: 178: <td id="hdd_temperature">
Lines 19 and 20 tell us how to read the system temperature.
Line 57 tells us how the "Hard Drive Temperature:" string is built.
Proceed with determining hard drive temperature.
# grep -rIn hdd_temp_info * GetValue.php:62: hdd_temp_info.length = 0; GetValue.php:86: echo "hdd_temp_info[".$idx."] = '".$temp_c."б╟C/".$temp_f."б╟F';"; GetValue.php:92: if ($temp_c=="") { echo "hdd_temp_info[".$idx."] = '';"; } system_status.php:28:var hdd_temp_info = new Array(); system_status.php:47: if (hdd_temp_info.length != 0) system_status.php:50: for (var i = 0; i < hdd_temp_info.length; i++) system_status.php:52: if (hdd_temp_info[i]=="") system_status.php:57: hdd_temp_str += "<?=$T_Slot ?> " + (i+1) + ": " + hdd_temp_info[i];
GetValue.php knows something, and we are going to read it. Here is the most important part:
/* HDD temp */
hdd_temp_info.length = 0;
<?
$idx=0;
$t_idx=1;
$hdd_temp_str="";
for("/RAID/disks/disk")
{
$temp_c="";
if(query("size") > 0)
{
$f_idx=1;
while($f_idx < 30) /*NOTE: 30 should be a safe figure, cyber*/
{
$f_name=query("/RAID/disks/disk:".$t_idx."/s_f:".$f_idx."/name");
if($f_name == "Temperature_Celsius")
{
$temp_c=query("/RAID/disks/disk:".$t_idx."/s_f:".$f_idx."/raw");
$f_idx = 30;
}
$f_idx++;
}
if($temp_c!="")
{
$temp_f = $temp_c*9/5+32;
echo "hdd_temp_info[".$idx."] = '".$temp_c."б╟C/".$temp_f."б╟F';";
}
//echo "hdd_temp_str[".$idx."] = '".$temp_c."б╟C/".$temp_f."б╟F'";
//$hdd_temp_str=$hdd_temp_str.$t_idx.": "$temp_c."б╟C/".$temp_f."б╟F";
}
if ($temp_c=="") { echo "hdd_temp_info[".$idx."] = '';"; }
$t_idx++;
$idx++;
}
?>
break;
Lines 75 and 78 are reading temperature. In fact, they iterate over all available parameters and find the one that is called Temperature_Celsius. As soon as it is found, its identifier is used to read the actual value.
Available parameters are "/RAID/disks/disk:
Raw_Read_Error_Rate
Throughput_Performance
Spin_Up_Time
Start_Stop_Count
Reallocated_Sector_Ct
Seek_Error_Rate
Seek_Time_Performance
Power_On_Hours
Spin_Retry_Count
Power_Cycle_Count
Power-Off_Retract_Count
Load_Cycle_Count
Temperature_Celsius
Reallocated_Event_Count
Current_Pending_Sector
Offline_Uncorrectable
UDMA_CRC_Error_Count
You will learn the way I got it a little bit later. So, at this point I know that if I query the parameter with f_id equal to 13 I will get the temperature of my hard drives. As stated above the temperature of the system is determined by
query("/system/system_information/temperature_c");
and the temperature of hard drives:
query("/RAID/disks/disk:1/s_f:13/raw"); query("/RAID/disks/disk:2/s_f:13/raw");
Those string values are not paths. You can easily check it by searching these files. You won't find anything. At least, I didn't find anything :). Those strings look like a path to a parameter within some database. And this is where /etc/rc.sh will help us.
# head -n40 /etc/rc.sh | tail -n10
#-------------------------------------------------------------------------------
/web/templates/initxmldb.sh
#LOG_MODULES=`xmldbc -g /system/log/memory_log_setting/status`
#if [ "$LOG_MODULES" = "1" ] ; then
# insmod /usr/lib/modules/logkk.ko
# logdb &
#fi
AUTO_PWR_RECOVERY=`xmldbc -g /system/apr`
The path in line 34 and in lint 40 look just like our strings. And xmldbc looks like a utility for managing some sort of xml database. I hope xmldbc has some usage help (unlike fancontrold).
# xmldbc -h Usage: xmldbc version 2 [OPTIONS] -h show this help message. -H show version number. -v verbose mode. -i ignore external function (like runtime). -g {node path} get value from {node path}. -s {node path} {value} set {value} in {node path}. -d {node path} delete {node path}. -l {XML file} reload XML file to database. -D {XML file} dump database to XML file. -S {unix socket} specify unix socket name, default is /var/run/xmldb_sock -A {ephp file} embeded php parse. -V {name=value} variable for ephp. -x {command} set extended get/set command. -t {tag:sec:command} schedule a timer. -k {tag} kill timers by tag. -X {node field} sorting. -I {node field} insert row. -p {node path} {file} print node to file.
Great. That what we were looking for, -s and -g switch in particular. Let us make sure this does what we expect it to do.
# xmldbc -g /system/system_information/temperature_c 50 # xmldbc -g /RAID/disks/disk:1/s_f:13/name Temperature_Celsius # xmldbc -g /RAID/disks/disk:1/s_f:13/raw 45 # xmldbc -g /RAID/disks/disk:1/s_f:13/val 133 # xmldbc -g /RAID/disks/disk:2/s_f:13/name Temperature_Celsius # xmldbc -g /RAID/disks/disk:2/s_f:13/raw 47 # xmldbc -g /RAID/disks/disk:2/s_f:13/val 127
Hints:
- xmldbc config file: /system/cfg/config.xml
- xmldbc initialization: /web/templates/initxmldb.sh
Problem 2: Write the script.
Now that we know how to get the temperature and how to turn on the fan we can proceed to writing a script. It is up to you to decide when to change the fan speed but for this example I will just take some values of temperature.
# cd /usr/local # touch my_fan_ctl.sh # chmod 755 my_fan_ctl.sh # cat << EOF > my_fan_ctl.sh #!/bin/sh STATE=off T0=38 T1=40 T2=42 while [ 1 -eq 1 ]; do TEMPERATURE=`xmldbc -g /system/system_information/temperature_c` case $STATE in off) if [ $TEMPERATURE -gt $T0 ]; then cpu_ctl fan_low STATE="low" fi ;; low) if [ $TEMPERATURE -lt $T0 ]; then cpu_ctl fan_stop STATE="off" else if [ $TEMPERATURE -gt $T2 ]; then cpu_ctl fan_high STATE="high" fi fi ;; high) if [ $TEMPERATURE -lt $T1 ]; then cpu_ctl fan_low STATE="low" fi ;; *) ;; esac sleep 60 done ^D
Problem 3: Run it at NAS startup.
Add insert the following line:
/usr/local/my_fan_ctl.sh &
after the start of fancontrold.
Note: At this point this doesn't work. Because after reboot there will be no my_fan_ctl.sh script under /usr/local as well as all the modifications to rc.sh will be lost.
Problem 4: Get fancontrold working.
Needs source code investigation, fixing and cross compilation. This hasn't been done yet.
Helpful info. Well documented
Your blog was a fortuitous discovery! Thank you for documenting this. The same setup (xmldb) is used in a standard-issue VDSL2 router here in sunny England. Very useful :-)
cheers, asbokid
good job! thanks. ps. about
good job! thanks.
ps. about autostart
/mnt/md1/@optware 960392288 645232188 315160100 67% /opt
cd /etc/init.d
# cat S99setup
#!/bin/sh
cp -r /mnt/md1/tmp/.mc /
/opt/bin/my_fan_ctl.sh &
thats all.
Post new comment