You are on page 1of 10

lyuts.

net

Search

I'm a rebel in the S.D.G


Home FreeBSD ARM Blog Visual-DB Project About

Home Blogs lyutsai's blog

Recent blog posts

Tags in Tags

Neovim build on FreeBSD

Fan problem on ASUS NAS M-25

Forensics and static/dynamic

Submitted by lyutsai on Fri, 03/09/2012 - 17:45

analysis tools

Posted in asus

Static analysis benchmark links


Build bokken with custom prefix
Fan problem on ASUS NAS M-25
Webcam in Kopete on FreeBSD
Device is busy then unmounting
truecrypt container
Dvorak try out. Typing speed.
Google's application specific
password and Thunderbird
Dvorak try out. Day 3.

more

m-25

nas

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

Monthly archive
December 2008 (1)
September 2009 (21)
October 2009 (6)
November 2009 (1)
December 2009 (3)
January 2010 (4)
March 2010 (2)
April 2010 (2)
May 2010 (2)
June 2010 (1)
July 2010 (3)
September 2010 (3)
October 2010 (4)

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>

November 2010 (3)


December 2010 (1)

What other processes are running there?

January 2011 (2)


February 2011 (1)
July 2011 (2)

# ps | grep fan
496 root
3528 S
ld

fancontro

August 2011 (5)


October 2011 (1)
December 2011 (2)
March 2012 (1)
June 2012 (1)
January 2013 (1)
February 2014 (1)
March 2015 (1)

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
fa
ncontrold -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

fancontro

FreeBSD Qt C++
vim C QtCreator

gstreamer UNIX static


analysis clang asus arm
more tags

ld
3227 root
ld -h
# kill -9 3227
#

1680 T

fancontro

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.playl
ist
-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 -> /s
ystem/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.de
f
lrwxrwxrwx
1 root
root
32 Oct 28 06:00 upnp_all_forma
t.conf -> /system/cfg/upnp_all_form
at.conf
lrwxrwxrwx
1 root
root
33 Oct 28 06:00 upnp_open_form
at.conf -> /system/cfg/upnp_open_fo
rmat.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
func
tion
index.ph
p
server
GetValue.php
home
language
status
JS
iepn
gfix.htc
loading.
html
style
account
imag
es
login_er
ror.php
disk
imag
esbutton-next-disable.gif logout.p
hp
empty.html
inde
x.html
maintena
nce
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("/sy
stem/system_information/temperature
_c");
20:$SYSTEM_TEMPERATURE_F=query("/sy
stem/system_information/temperature
_f");
21:$BATTERY=query("/temp/ups/percen
t");
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_tem
p_info[i]=="")
55:
if (hdd_tem
p_str != "")
56:
hdd
_temp_str += ", ";
57:
hdd_temp_st
r += "<?=$T_Slot ?> " + (i+1) + ":
" + hdd_temp_info[i];
59:
getEle("hdd_tempera
ture").innerHTML = hdd_temp_str;
60:
getEle("hdd_tempera
ture_div").style.display = "";
63:
if (hdd_temp_str != "")
64:
getEle("hdd_tempera
ture_div").style.display = "";
66:
getEle("hdd_tempera
ture_div").style.display = "none";
78:
getEle("hdd_temperature_div
").style.display = "none";
168:
<?=$T_System_Temperatu
re ?>:
171:
<span class="blackcolo
r" id="sys_temperature"><?=$SYSTEM_
TEMPERATURE_C ?>C/<?=$SYSTEM_TEMP
ERATURE_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_t
emp_info[".$idx."] = '';"; }
system_status.php:28:var hdd_temp_i

nfo = new Array();


system_status.php:47:
if (hdd_tem
p_info.length != 0)
system_status.php:50:
for
(var i = 0; i < hdd_temp_info.leng
th; 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:
61. /* HDD temp */
62.
hdd_temp_info.length = 0
;
63.
<?
64.
$idx=0;
65.
$t_idx=1;
66.
$hdd_temp_str="";
67.
for("/RAID/disks/disk")
68.
{
69.
$temp_c="";
70.
if(query("size") > 0
)
71.
{
72.
$f_idx=1;
73.
while($f_idx < 3
0) /*NOTE: 30 should be a sa
fe figure, cyber*/
74.
{
75.
$f_name=quer
y("/RAID/disks/disk:".$t_idx
."/s_f:".$f_idx."/name");
76.
if($f_name =
= "Temperature_Celsius")
77.
{
78.
$temp_c=
query("/RAID/disks/disk:".$t
_idx."/s_f:".$f_idx."/raw");
79.
$f_idx =
30;
80.
}
81.
$f_idx++;
82.
}
83.
if($temp_c!="")
84.
{
85.
$temp_f = $t
emp_c*9/5+32;
86.
echo "hdd_te
mp_info[".$idx."] = '".$temp
_c."C/".$temp_f."F';";
87.
}
88.
//echo "hdd_temp
_str[".$idx."] = '".$temp_c.
"C/".$temp_f."F'";
89.
//$hdd_temp_str=
$hdd_temp_str.$t_idx.": "$te
mp_c."C/".$temp_f."F";
90.
}
91.
92.
if ($temp_c=="") { echo
"hdd_temp_info[".$idx."] = '
';"; }
93.
$t_idx++;
94.
$idx++;
95.
}
96.
?>
97.
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:/s_f:/name", where <t_id> is the
number of a hard drive (starting from 1) and <f_id>
from 1 to 29 inclusively (according to source code).
Here is the list:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.

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/t
emperature_c");
and the temperature of hard drives:
query("/RAID/disks/disk:1/s_f:13/ra
w");
query("/RAID/disks/disk:2/s_f:13/ra
w");
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.
31. # head -n40 /etc/rc.sh | tai
l -n10
32. #----------------------------------------------------------------------------33. /web/templates/initxmldb.sh
34.
35. #LOG_MODULES=`xmldbc -g /sys
tem/log/memory_log_setting/s
tatus`
36. #if [ "$LOG_MODULES" = "1" ]
; then
37. #
insmod /usr/lib/modu
les/logkk.ko
38. #
logdb &
39. #fi
40.
41. 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 versi
on number.
-v
verbose mo
de.
-i
ignore ext
ernal function (like runtime).
-g {node path}
get value
from {node path}.
-s {node path} {value} set {valu
e} in {node path}.
-d {node path}
delete {no
de path}.
-l {XML file}
reload XML
file to database.
-D {XML file}
dump datab
ase to XML file.
-S {unix socket}
specify un
ix socket name, default is /var/run
/xmldb_sock
-A {ephp file}
embeded ph
p parse.
-V {name=value}
variable f
or ephp.
-x {command}
set extend
ed get/set command.
-t {tag:sec:command}
schedule a
timer.
-k {tag}
kill timer
s 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_informat
ion/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:
1. xmldbc config file: /system/cfg/config.xml
2. 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_sto
p
STATE="off"
else
if [ $TEMPERATU
RE -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.
Post to Facebook
Post to Twitter
Add to LinkedIn
Post to Delicious
Post to Digg
Post on Google Buzz
Add to Google Bookmarks
Send via Gmail
Share on Google Reader
Add to Evernote
Print with PrintFriendly
Send via Yahoo Mail
lyutsai's blog

Helpful info. Well


documented
Submitted by Anonymous on Mon, 04/09/2012 06:38.

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
reply

good job! thanks. ps.


about
Submitted by ungewissheit (not verified) on
Thu, 01/16/2014 - 03:19.

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.
reply

Post new comment


Your name: *
Anonymous

E-mail: *
The content of this field is kept private and will not be
shown publicly.

Homepage:

Subject:

Comment: *

Input format
CAPTCHA
This question is for testing whether you are
a human visitor and to prevent automated
spam submissions.
Enter the 2 missing characters from the
following word: *
_ali_ation:

Preview
0