colaghost

自己的世界。。。

archlinux下给cpu和显卡节能设置

最近在折腾archlinux,把内核更新到3.0后,发现A卡的官方驱动上不了了,只能上下开源的驱动了,无奈开源驱动的电源管理实在不敢恭维,笔记本键盘部分老是暴热,受不鸟。

下面就从cpu和显卡两个发热大户入手吧!

1、显卡降低频率

从内核2.6.35起就加入了AMD开源驱动的电源管理,可以进行GPU频率调整、显存频率调整、电压调整和温度监控支持等。

我的是A卡,这时候就发挥了它的作用了。

可以用如下命令查看当前显卡的电源管理模式:

$ cat /sys/class/drm/card0/device/power_profile

默认的是”default”,这个使用显卡默认频率不做调整。可以将它改为auto,low,mid,high等模式,auto可以根据当前是使用电池或者电源来自动做调整。这里我将它设置为low,注意有的本本设置为low可能导致部分显示问题。

# echo low > /sys/class/drm/card0/device/power_profile

注意这个设置只在当前运行时立即生效,要在下次启动时也有效的话可以手动将它加进/etc/rc.local配置文件里。

2、CPU动态调节频率功能

查了下有cpufrequtils这个小工具,可以实现自动/手动调节CPU频率,对于笔记本来说是很有用的。

通过pacman安装即可。

$ pacman -S cpufrequtils

安装完后先手动加载驱动。

# modprobe acpi-cpufreq

为了下次启动时自动加载可以把它加到/etc/rc.conf配置文件的MODULES列。

加载完驱动后,可以执行cpureq-info查看下cpu信息。

cpufreq提供了好几个电源调整方案,我用的是cpufreq_ondemand,这个可以根据系统负载动态调整CPU频率,这个也需要加入/etc/rc.conf的MODULES列里。

最后还需要配置下/etc/rc.d/cpufreq,将#governor=”ondemand”那句取消注释后保存即可。

设置好配置文件后,可以运行以下命令来启动守护进程:

# /etc/rc.d/cpufreq start

启动后再执行下cpufreq-info看看当前频率是不是已经降下来了!

若要在启动时自动守护进程,要将cpufreq加入/etc/rc.conf配置文件的DEAMONS列里。

总结

这样设置后温度大概会比原来降低十度左右,不过貌似还是没有安装闭源驱动fglrx时温度低,但总算没那么烫手了!

ubuntu下自动切换声音输出设备

我有一个usb耳机,在ubuntu下驱动也正常,不过有一个蛋疼的地方就是每次插进usb耳机都不会自动切换到usb耳机对应的声音输出设备,次次都要手动去选择对应的,于是想着能不能写一个脚本来实现。
不过貌似关于这方面的中文资料不多,google了很久都没有什么结果,倒是找出一个freebsd上能实现的,就是sysctl,不过貌似ubuntu下实现不了。后来在stackoverflow上问老外才得知用pacmd可以列出各种可用的声音输出设备还有设置默认输出设备等操作神马的。
弄了一下,果真OK了,写随意写了一个脚本来测试了,这个脚本假设系统使用pulseaudio的。
原理也不难,就是检测所有的声音设备,找出当前默认的声音设备,然后把声音设备切换到下一个,重新运行脚本就可以把声音设备切换回原来默认的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
 
declare -i count=`pacmd list-sinks | grep -c index:[[:space:]][[:digit:]]`
declare -i active=`pacmd list-sinks | sed -n -e 's/\*[[:space:]]index:[[:space:]]\([[:digit:]]\)/\1/p'`
declare -i major=$count-1
declare -i next=0
 
if [ $active -ne $major ] ; then
next=active+1
fi
 
pacmd "set-default-sink ${next}"
 
for app in $(pacmd list-sink-inputs | sed -n -e 's/index:[[:space:]]\([[:digit:]]\)/\1/p');
do
pacmd "move-sink-input $app $next"
done
 
declare -i ndx=0
pacmd list-sinks | sed -n -e 's/device.description[[:space:]]=[[:space:]]"\(.*\)"/\1/p' | while read line;
do
if [ $next -eq $ndx ] ; then
notify-send -i notification-audio-volume-high "声音输出切换到" "$line"
exit
fi
ndx+=1
done;

脚本里在切换后会做notify-send的提示,假设找不到命令的可以执行sudo apt-get install libnotify-bin安装一下,但不安装也没有什么关系,只是一个提示而已。

为了方便可以将脚本复制到/usr/bin目录下,然后在“键盘快捷键”里面添加多一个快捷键,命令就是脚本名了,最后设定好想要的快捷键,以后就可以直接切换了。

ubuntu下看flash视频时禁用屏保

最近两天在啃《unix环境高级编程》,一直转在ubuntu下。我吃饭时喜欢一边看电影,可是上优酷看flash视频时又有一个麻烦的地方,就是屏保并不会自动被禁用掉,而是得手动去禁用,看完又得去把它开启,甚是麻烦,就想着自己写一个脚本来实现开启/禁用屏保。

Google了下,关于这方面的结果倒是不少,下面是其中一个脚本:

1
2
3
4
5
6
7
8
#!/bin/sh
while true;
do
    if test -n "`file /tmp/Flash*|grep \"Macromedia Flash Video\\|MPEG v4\"`";then
        gnome-screensaver-command -p
    fi
    sleep 30
done

可惜我发现我的tmp目录下并不会生成/tmp/Flash*,我原先以为是我用shockwave flash的原因,不是在tmp目录下生成缓存。就自己搜索任何可能的目录,看下具体在哪个目录里生成缓存,可惜找不到。

这时候想到linux下是可以通过lsof来监测某一个进程当前打开了哪些文件,结果发现了其实是有在/tmp目录下生成Flash*这样的文件的,只是生成后马上又被删除了,这时候虽说进程还可以进行读写操作,可是在目录下却无法看到。

因此想到了另外一种方法,由于shockwave flash做为chrome下的一个插件,只要监测到chrome载入libgcflashplayer.so的这一个进程打开的所有文件下有/tmp/Flash*类似的文件即可知道当前在播放flash视频,这时候只要运行”gnome-screensaver-commad -p”模拟活动一下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/sh
while true;
do
	pid=`ps aux | grep /opt/google/chrome/libgcflashplayer.so | grep -v grep | awk '{print $2}'`
	if [ "$pid" != "" ]; then
		data=`lsof -p $pid | grep /tmp/Flash`
		if [ "$data" != "" ]; then
			echo "gnome-screensaver-command"
			gnome-screensaver-command -p
		fi
	fi
	sleep 30
done

用expect来实现自动交互的shell脚本

有时候可能实现自动交互过程,如自动登陆到远程主机等,由于需要输入用户名密码神马的,得自己呆在那里等它登陆完成,比较麻烦,单纯用shell脚本又没法完成,因为像ssh连接远程主机是不从stdin读入数据的。这时候就需要expect出马了。

Expect是基于TCL的,作为一个脚本语言,expect能在无需管理员参与的情况下实现自动交互(比如passwd,fsck,telnet等),expect也能用于自动测试一些应用程序。

搜索一下其实网上很多使用expect来完成密码登陆的例子的:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
auto_login_ssh () {
    expect -c "set timeout -1;
                spawn -noecho ssh -o StrictHostKeyChecking=no $2 ${@:3};
                expect *assword:*;
                send -- $1\r;
                interact;";
}
 
auto_login_ssh passwd user@

但实际上这个脚本不能做到完全自动化,一旦密码错误或者远程主机未响应神马的,expect还是正常退出,并不能判断到底哪一步出问题了。由于使用expect后,程序的exit status是expect的,所以这时候对expect做处理。当expect遇不到期望的输出值时,就会执行eof分支,这时候我们只要在eof分支加上退出值即可以判定到底是在哪一步出了问题。

这里用两个简单的脚本说明,这样比较容易弄清楚。

called.sh

1
2
#!/bin/bash
echo "a"

可以看出上述脚本是输出“a”,也就是期望输出值应该为”a”。下面看用expect实现的交互脚本:
caller.sh

1
2
3
4
5
6
7
8
9
#!/bin/sh
expect -c "set timeout -1;
           spawn called.sh;
           expect{
               \"b\"{exit 1;}
               eof{exit 2;}
           }
           "
echo $?

实际上called.sh的输出值是”a”,但caller.sh的期望值为”b”,这时候实际上是跳到eof,这时候按状态2退出,也就是说$?实际上为2.这时候就可以判断交互脚本具体的执行情况。

这里只是一个简单的实例而已,并没有任何实际的意义,但是它说明了如何处理一些异常的情况,这样只要将上面的自动完成ssh连接远程主机的脚本加上异常情况处理即可变成一个真正完全自动交互的脚本了,以下是做修改后的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
auto_smart_ssh () {
    expect -c "set timeout -1;
                spawn ssh -o StrictHostKeyChecking=no $2 ${@:3};
                expect {
                    *assword:* {send -- $1\r;
                                 expect {
                                    *denied* {exit 2;}
                                    eof
                                 }
                    }
                    eof         {exit 1;}
                }
                "
    return $?
}
 
auto_smart_ssh passwd user@host ls /var
echo -e "\n---Exit Status: $?"

注:以上自动完成ssh连接远程主机例子来自于apt-blog.net

缓冲区溢出攻击实验

使用系统环境为:ubuntu10.04 2.6.32-27-generic gcc4.4.3
这里先放出实验代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h> 
#include <stdlib.h>
#include  <ctype.h>
 
char *getxs(char *dest)
{
	int c;
	int even = 1;
	int otherd = 0;
	char *sp = dest;
	while ((c=getchar())!=EOF&&c!='\n')
	{
		if (isxdigit(c))
		{
			int val;
			if ('0'<=c&&c<='9')
				val = c - '0';
			else
				if ('A'<=c&&c<='F')
					val = c - 'A' +  10;
			else
				val = c - 'a' + 10;
			if (even)
			{
				otherd = val;
				even = 0;
			}
			else
			{
				*sp   = otherd*16 + val;
				even = 1;
			}
		}
	}
//	*sp   = '\0';
	return dest;}
 
int getbuf()
{
	char buf[12];
	getxs(buf);
	return 1;
}
 
void test()
{
	int val;
	printf("Type Hex string:");
	val = getbuf();
	printf("getbuf returned 0x%x\n", val);
}
int main()
{
	int buf[16];
	int offset = (((int)buf)&0xfff);
	int *space = (int*)alloca(offset);
	*space = 0;
	test();
	return 0;
}

其实这是《深入理解计算机系统》里的一个家庭作业,根据以上代码可以看出getbuf()函数在正常情况下无论怎么调用都会返回值1。任务是只简单地对提示符输入一个适当的十六进制字符串,就使getbuf对rest返回-559038737(0xdeadbeef)。
不过在目前的内核和编译器下要直接对原程序进行栈溢出攻击是不可能完成的了。现在的编译器对代码做了一些防止溢出攻击的保护措施,如%gs:0×14。每次运行程序时它都会随机产生一个类似验证码的值,当函数返回时它会验证此验证码是否被改变,由于它验证码的存放内存位置位于buf数组和保存在栈上的%ebp之间,只要数组一旦越界写入的话就会被改变,这时候也会触使保护代码运行。
不过我在编译时加进了-fno-stack-protector选项,这时候是不产生任何栈溢出保护代码的。
以下是源程序反汇编所得的主要汇编代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
08048569 :
 8048569:	55                   	push   %ebp
 804856a:	89 e5                	mov    %esp,%ebp
 804856c:	83 ec 28             	sub    $0x28,%esp
 804856f:	8d 45 ec             	lea    -0x14(%ebp),%eax
 8048572:	89 04 24             	mov    %eax,(%esp)
 8048575:	e8 2a ff ff ff       	call   80484a4
 804857a:	b8 01 00 00 00       	mov    $0x1,%eax
 804857f:	c9                   	leave
 8048580:	c3                   	ret    
 
08048581 :
 8048581:	55                   	push   %ebp
 8048582:	89 e5                	mov    %esp,%ebp
 8048584:	83 ec 28             	sub    $0x28,%esp
 8048587:	b8 e0 86 04 08       	mov    $0x80486e0,%eax
 804858c:	89 04 24             	mov    %eax,(%esp)
 804858f:	e8 38 fe ff ff       	call   80483cc
 
 8048594:	e8 d0 ff ff ff       	call   8048569
 8048599:	89 45 f4             	mov    %eax,-0xc(%ebp)
 804859c:	b8 f1 86 04 08       	mov    $0x80486f1,%eax
 80485a1:	8b 55 f4             	mov    -0xc(%ebp),%edx
 80485a4:	89 54 24 04          	mov    %edx,0x4(%esp)
 80485a8:	89 04 24             	mov    %eax,(%esp)
 80485ab:	e8 1c fe ff ff       	call   80483cc
 
 80485b0:	c7 04 24 07 87 04 08 	movl   $0x8048707,(%esp)
 80485b7:	e8 20 fe ff ff       	call   80483dc
 
 80485bc:	c9                   	leave
 80485bd:	c3                   	ret

这里主要需要进行getbuf和test两个函数汇编代码的分析。
分析可以得出:test调用了getbuf函数,getbuf将0×1传送到%eax做为返回值。test将返回值赋值给val,最后以16进制打印出来,这个结果在正常情况下无论如何就只是输出0×1。
这里通过汇编代码我们可以发现两种思路,一种就是在getbuf中输入字符串时,覆盖栈中保存的%ebp值(跟原先的值一样),返回地址(使其直接跳转到test汇编代码的call 80483cc 这一句,以及返回地址上面的8个字节%ebp+4、%ebp+8(这两个是做为printf的两个参数);另外一种就是往buf里注入一段机器码(这段机器码主要是将%eax赋值为0xdeadbeef),当然这时候也需要覆盖返回地址,使其跳转到注入的机器码的起始地址,不过这种方法在目前的内核下并不可行,会引发系统某种保护机制而失败。
这里主要研究第一种思路,下面可以看下test和getbuf的部分栈的组织图。

getbuf里分配了40个字节的空间,不明白为什么需要这么多额外的。
这里主要需要获取%ebp的值,以及call 80483cc 这一句的地址(这个可以从汇编代码里得出为0×80485ab)。
%ebp的值需要调试运行在getbuf函数里面下断点读出。
用gdb调试过程如下:
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3 : GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “i486-linux-gnu”.
For bug reporting instructions, please see:

Reading symbols from /home/colaghost/Desktop/test2…done.
(gdb) break *0×804856c
Breakpoint 1 at 0×804856c: file test.c, line 42.
(gdb) r
Starting program: /home/colaghost/Desktop/test2
Breakpoint 1, 0×0804856c in getbuf () at test.c:42
warning: Source file is more recent than executable.
42 {
(gdb) print /x *(int*)($ebp)
$1 = 0xbfffefd8
这里得出保存的%ebp值为0xbfffefd8,下面可以得出要返回0xdeadbeef的字符串。
首先是输入任意40个十六进制字符填满char[12]和额外的8个字节,继续输入保存的%ebp的值,返回地址0×80485ab,printf的第一个参数的存放地址,即 0×80486f1,最后是我们要打印的字符0xdeadbeef。
最终得到的字符串就是这样的:
b8ef bead deba 6985 0408 ffe2 0000 0000 0000 0000 d8ef ffbf ab85 0408 f186 0408 efbe adde
后记:最先由于%gs:0×14,以及系统的一些保护机制,在正确的思路下花费了一整晚的时间都没有任何进展,一直是以sementation fault告终。第二天上午在gdb调试运行状态下才算成功得到返回值。输入同样的字符串,在gdb运行状态下可以得到返回值,并且显示程序正确结束;不过在一般运行状态下test方法返回后一直出现sementation fault的错误,到现在都未找到实际原因。

ubuntu10.04下无法连接网络(acer 4745G笔记本)

今天在笔记本上安装了ubuntu10.04,但无奈网卡驱动不了,一直都在爆没有合适的驱动设备,有线、无线都无法连接。

后来google了一下,找到了一个Atheros AR8151的有线网卡驱动(4745G的为此有线网卡),有需要的童鞋可以到这里下载:AR81Family-Linux-v1.0.1.9.tar.gz

下载后编译安装就行了!怎么,不会编译安装?解压后打开终端进入文件夹路径,然后敲入make && sudo make install,直接回车就行了。

安装后应该就可以连接有线网络了,但这时候无线的话还得另外一个驱动。不过这时候在系统->系统管理->硬件驱动里可以找到合适的,系统已经帮你搞好了,激活后就可以了。

统计源码文件行数的小程序

这些天在公司一直在看一个开源包,由于某种原因突然想看下这个开源包到底写了多少行代码。简单的方法:一个个文件查看再利用计算器进行加法运算!但这方法也太笨了点吧,再说,源文件那么多,我又那么懒。

想来想去还是写个脚本吧,以后或许也还有用。

记得wc -l可以直接统计文件的行数,不过输出结果是类似于“86 a.cpp”这样的形式的,还得用cut把后面的文件名给去掉。这样只需把整个文件夹的文件扫描一遍即可得到最后想要的行数了。

不过还有两个问题:一个是文件夹里的子文件夹没被扫描到,因为循环时是判断为普通文件的才进行统计;另一个是由于源文件后缀形式多样,有.java,.c,.c++等等等,必须有参数把它们传进脚本里。

第一个问题好办,不就是个递归吗?可是由于本人对SHELL编程的熟悉程度不够,递归时如何返回值就搞了很久,一直用return却只是获取不到,不知何故!最后直接用一个全局变量解决了这个问题。

第二个问题最开始就想着把每个后缀名作为单独的参数传进去,这样就涉及到参数个数不确定的问题。知道有shift这个东东可以循环扫描参数,但是又不知道如何把它们保存下来。搞到最后还是直接用”c:java”这种形式做为一个参数传进去算了,再用awk进行分解,awk里又涉及到访问外部变量和循环语句等的使用,甚是蛋疼,查了一部分资料。

最终效果是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#! /bin/sh
lines=0
count_line ()
{
	cd $1
	for file in $(ls); do
		if [ -d $file ]; then
			count_line $file $2
		else
			flag=$(echo $2 | awk -v exc=${file##*.} 'BEGIN {FS=":"}{for(i=1; i<=NF; i++){if($i==exc){print 1;break;} if(i==NF)print 0}}')
			if [ $flag -eq 1 ]; then
				lines=$(($lines+$(echo $(wc -l $file) | cut -d ' ' -f 1)))
			fi
		fi
	done
	if [ "$1"!="." ]; then
		cd ..
	fi
}
 
if [ $# -lt 2 ]; then
	echo "Requires the path and the param!"
else
	count_line $1 $2
	echo "All files have $lines lines"
fi

错误生涯:Package requirements (speex >= 1.2beta3) were not met:

checking for SPEEX… configure: error: Package requirements (speex >= 1.2beta3) were not met:

No package ’speex’ found

Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.

这些天做的一个开发要用到mediastreamer2这个开发包,configure时就遇到这个错误。上网CHECK了一下,原来mediastreamer2依赖speex这个开源音频压缩包,那解决就不麻烦了,下个下来编译安装就行了。

很奇怪,编译安装后重新对mediastreamer2进行configure,怎么还是爆那个错啊?莫非speex安装没成功,查看了下/usr/local/lib,怪了,已经在了啊。

再次查看下错误,Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix.哦,原来问题出在这啊,看来是默认没把/usr/local/lib/pkgconfig加到这个环境变量里了。这样解决起来就不难了,把路径添加进去就行了:

export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH

重新进行编译,错误解决。

PS:由于在编译的时候需要判断系统里是否存在某个库以及是否达到版本要求,这是通过提取*.pc这些文件里的信息来进行判断的,默认会到/usr/lib/pkgconfig这个文件夹进行信息提取。由于上面的speex库是放到/usr/local/lib这个文件夹里,自然它们的pc文件也是放到/usr/local/lib/pkgconfig文件夹里。这样就算安装了也还是会出现找不到某个package的错误,这时候就可以手动把路径加入到PKG_CONFIG_PATH变量里,这样编译时就能到手工指定的路径里进行搜索。

Unix心传:无名师与MCSE能者(伪文言翻译)(转载)

无名师与MCSE能者

一通熟Windows之能者见无名师,曰:“闻师深修Unix之道,吾以密巧相述,相益,可乎?”

无名师曰:“求智,善矣;然Unix之道并无甚密巧。”

能者惑:“人曰师乃当世Unix大道之贤者,通晓其中之变法,如吾之于Windows;吾乃MCSE,有世所罕之证书,吾铭记各注册表项功能于心,更可细述任一Windows API,乃至微软所未曾详述者。师,所以为师,为智,皆揭而知他人之不知也。”

无名师曰:“怠矣,本无秘,何以揭?”

能者嗔:“若无密巧,何以修乃成师之业?”

无名师曰:“求智者,以他人之不知为智,犹如求光明者,拥烛笼火,以为珍贵,乃终被灼也。”

听此,能者顿觉灵光。

Master Foo and the MCSE

Once, a famous Windows system administrator came to Master Foo and asked him for instruction: “I have heard that you are a powerful Unix wizard. Let us trade secrets, that we may both gain thereby.”

Master Foo said: “It is good that you seek wisdom. But in the Way of Unix, there are no secrets.”

The administrator looked puzzled at this. “But it is said that you are a great Unix guru who knows all the innermost mysteries. As do I in Windows; I am an MCSE, and I have many other certifications of knowledge not common in the world. I know even the most obscure registry entries by heart. I can tell you everything about the Windows API, yes, even secrets those of Redmond have half-forgotten. What is the arcane lore that gives you your power?”

Master Foo said: “I have none. Nothing is hidden, nothing is revealed.”

Growing angry, the administrator said “Very well, if you hold no secrets, then tell me: what do I have to know to become as powerful in the Unix way as you?”

Master Foo said: “A man who mistakes secrets for knowledge is like a man who, seeking light, hugs a candle so closely that he smothers it and burns his hand.”

Upon hearing this, the administrator was enlightened.

转载源:http://apt-blog.net/master-foo-and-the-mcse