对gtap的一些wordaround
February 28th, 2010前两天发现gtap这个项目,也是个Twitter API代理,基于GAE(Python)。想着Choqok、qwit都没法用birdnest,所以架了个玩玩。结果弄出一堆事来 =.=!
先是把gtap和其它几个app放在一起,如我上篇文章所讲的。这个挺简单:
diff --git a/gtap.py b/gtap.py index 1b9d8ed..472157d 100644 --- a/gtap.py +++ b/gtap.py @@ -16,6 +16,7 @@ class MainPage(webapp.RequestHandler): origUrl = self.request.url origBody = self.request.body (scm, netloc, path, params, query, _) = urlparse.urlparse(origUrl) + path = path[5:] # skip '/gtap' if path == '/': self.myOutput('text/html', 'here is the proxy of \"twitter.com\" by GTAP %s !' % (gtapVersion)) else: @@ -59,7 +60,7 @@ class MainPage(webapp.RequestHandler): self.doProxy('get') def main(): - application = webapp.WSGIApplication( [(r'/.*', MainPage)], debug=True) + application = webapp.WSGIApplication( [(r'/gtap/.*', MainPage)], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__":
在app.xml里加上:
- url: /gtap/.* script: gtap.py # secure: always
如果你把gtap单独架设在gae上的,而不是和几个app一起放在yourapp.appspot.com上的话,这步不需要。
接着,发现Choqok等客户端发出的用户身份验证请求是到api.twitter.com,但http://api.twitter.com/account/verify_credentials.xml这样请求无效,改成http://twitter.com/account/verify_credentials.xml即可:
diff --git a/gtap.py b/gtap.py index 472157d..f50f2dc 100644 --- a/gtap.py +++ b/gtap.py @@ -36,7 +39,7 @@ class MainPage(webapp.RequestHandler): netloc = 'search.twitter.com' newpath = path[7:] elif path_parts[1] == 'api': - netloc = 'api.twitter.com' + netloc = 'twitter.com' newpath = path[4:] else: netloc = 'twitter.com'
最后是最头疼的,本来经过上面两次改动echofon已经是可以正常收发推了。可偏偏Choqok和Qwit就是没法用gtap的api代理。老是提示”Could not authenticate you.“。经过抓包,我发现,这两个客户端竟然不会像其它客户端一样在http请求中加入Authorization头部,也就是用户名和密码,用来给网站验证的,难怪会出现错误。哪位朋友如果用Choqok/Qwit并且使用birdnest api代理的请支一声~ 经过一番摸索(省去纠结的zzzzzzzz过程),改动了gtap对http头部不含有Authorizatioin的处理:
diff --git a/gtap.py b/gtap.py index 472157d..f50f2dc 100644 --- a/gtap.py +++ b/gtap.py @@ -22,6 +22,9 @@ class MainPage(webapp.RequestHandler): else: if 'Authorization' not in self.request.headers : headers = {} + self.response.set_status(401, message="Unauthorized") + self.response.headers.add_header('WWW-Authenticate', 'Basic realm="Secure Area"') + self.response.out.write("401") else: auth_header = self.request.headers['Authorization'] auth_parts = auth_header.split(' ')
原理是如果客户端的http请求头部没有Authorization字段,那么gtap返回”401 Unauthorized”,让它重新发送用户验证信息。再次抓包,Choqok第一次还是没有Authorization,接着就收到服务器发来的401状态码,于是它乖乖的再次发送请求并包含Authorization。
这样改之后echofon等依然可以正常使用gtap的api代理。也许你有注意到我在app.xml里注释掉了使用安全链接”# secure: always”,经测试Choqok在http下可以正常处理收发tweets以及其它api操作,但改用https后,发推就不行了,其它还是可以的。Qwit因为貌似不支持https,所以只能用http的。
虽然经过这么workaround,Choqok、Qwit能看推发推了,但我还是觉得Choqok这个客户端自身有些问题,因为如果换用twip这个api代理的话,无论http、https都能正常使用,偏偏birdnest和gtap的不行。如果说gtap应用少的话,birdnest可是使用广泛的twitter api代理啊,我的Twitter Opera Widget就用的它。不想再折腾了,如果你也在用gtap,希望本文能给你些帮助
唉,连续两篇博文在讲twitter api代理的事儿了,老外上个网哪有天朝这么多的屁事,我朝屁民们在代理的事上浪费多少时间啊…
参考:
[1] 基本连接验证(Basic Access Authorization)的原理
[2] Status Code Definitions
[3] Authorization
[4] HTTP Request fields
[5] List of HTTP status codes
[6] Basic access authentication
UPDATE 2010-3-1
gtap的作者@rui7905非常迅速地更新到了0.3版,修正了我这篇文章提到的问题,并且做了原因分析,感兴趣得朋友移步讨论页。如果你用gtap,不要再用在下workaround的拙作了,升级到0.3吧~~
将Birdnest和opm-server-mirror合二为一
February 11th, 2010前者是Twitter的API proxy,后者是Opera Mini的代理,均基于Google App Engine(简称GAE)。一个GAE账户只能建10个app,分别在GAE上建这两个代理不免有些浪费。另一方面,多一个地址也不好记,我希望能够一个GAE app上面同时跑多个应用,记一个地址就行了,嘿嘿~
本文假设你会在GAE上建应用。
要把Birdnest和opm-server-mirror放在一个app里其实改动并不多。
- checkout birdnest
- 下载opm-server-mirror的opm.py,放到birdnest的目录中,并作如下改动:
- 更改birdnest的app.yaml:
svn co -r 97 http://birdnest.googlecode.com/svn/branches/gae birdnest_r97
注意一定要r97的,因为作者最近更新过code.py了,导致”internal sever error”,可能是因为GAE不支持socket模块的原因。就是因为co的版本不对我白弄了好久。。。
--- opm.py 2010-02-11 21:06:12.577148400 +0800 +++ opm (2).py 2010-02-11 13:04:15.739257800 +0800 @@ -29,7 +29,7 @@ self.response.out.write(result.content) application = webapp.WSGIApplication( - [('/', MainPage)], + [('/opm/', MainPage)], debug=True) def main():
--- app.yaml 2010-02-11 20:56:34.778320300 +0800 +++ app (2).yaml 2010-02-11 21:22:18.383789000 +0800 @@ -12,6 +12,8 @@ script: code.py - url: /image/.* script: code.py +- url: /opm/.* + script: opm.py - url: / static_files: index.html upload: index.html
最后appcfg.py update birdnest_r97上传至GAE,好了。以后你给twitter客户端设api代理的时候就用 http://fckgfw.appspot.com/api/,给Opera Mini设代理则用 http://fckgfw.appsot.com/opm/ 。嗯,和谐了。
迁移旧硬盘上的Linux到新硬盘(LVM)
February 7th, 2010买了块新的大硬盘,准备把原先硬盘上的Slackware迁移过去,并且采用LVM,xp不要了换成Win7。记录下迁移过程和遇到的一些问题及解决方法。
- 用Slackware 13.0 dvd系统盘引导启动,并分区。Slackware 从12.0开始支持安装系统到LVM。
- 建立LV分区并激活
- 格式化LV分区并挂载到新的root目录
- 挂载旧硬盘上的系统目录到旧root目录
- 迁移(复制)旧系统到新硬盘
- chroot到/new_root,制作initrd和LILO
- 在新系统的/dev目录下建立LVM设备文件
- 建立fstab文件
Device Boot Start End Blocks Id System
/dev/sda1 * 1 13 102400 7 HPFS/NTFS
/dev/sda2 13 2611 20869120 7 HPFS/NTFS
/dev/sda3 2611 3917 10485760 c W95 FAT32 (LBA)
/dev/sda4 3917 38913 281110368+ 5 Extended
/dev/sda5 3917 3981 519047 83 Linux
/dev/sda6 3982 4504 4200966 82 Linux swap
/dev/sda7 4505 38913 276390261 8e Linux LVM
这个是我新硬盘的最终分区情况。sda1是Win7保留的100M启动分区,sda2是Win7系统盘,sda3是D盘。500M大的sda5我原本想作为/boot,不过目前我就Win7+Slackware+LILO不需要,留作未来使用吧。swap(sda6)没有用LVM,剩下的260多G(sda7)都交给LVM管理了。
pvcreate /dev/sda7
vgcreate MYVG /dev/sda7
lvcreate -L 20G -n ROOT MYVG
lvcreate -L 10G -n USRLOCAL MYVG
lvcreate -L 100G -n DATA MYVG
lvcreate -L 50G -n HOME MYVG
vgscan --mknodes
vgchange -ay
mkreiserfs /dev/MYVG/ROOT
mkreiserfs /dev/MYVG/USRLOCAL
mkreiserfs /dev/MYVG/HOME
mkdir /new_root
mount -t reiserfs /dev/MYVG/ROOT /new_root
mkdir -p /new_root/usr/local
mount -t reiserfs /dev/MYVG/USRLOCAL /new_root/usr/local
mkdir /new_root/home
mount -t reiserfs /dev/MYVG/HOME /new_root/home
mkdir /old_root
mount -t reiserfs /dev/sda5 /old_root
mount -t reiserfs /dev/sda7 /old_root/usr/local
mount -t reiserfs /dev/sda8 /old_root/usr/local/opt
mount -t reiserfs /dev/sda9 /old_root/home
又买了个移动硬盘盒,旧硬盘就可以当移动硬盘了。
cd /old_root
/old_root/bin/tar cpBf - . --exclude=tmp/* | ( cd /new_root && /old_root/bin/tar xvpBf -)
漫长的等待…… /dev 啥的统统要拷过来。根据参考[5],到这里接着用直接chroot后就可以mkinitrd和lilo了,但事实不是这样的,还得额外多些操作。
mount --bind /proc /new_root/proc
mount --bind /sys /new_root/sys
mount --bind /dev /new_root/dev
chroot /new_root
cd /boot
mkinitrd -c -k 2.6.29.6-smp -m reiserfs -f reiserfs -r /dev/MYVG/ROOT -L -o /boot/initrd.gz
vim /etc/lilo.conf
lilo -v
三行mount –bind是必须的,slackware的README_LVM.TXT说安装程序会将/proc和/sys挂载上,但我目前不是全新安装没有使用setup安装程序,而且我运行setup企图让它帮我挂载/proc等未果
所以得用–bind(见参考[6]),不过我不是很了解这个选项。不这么mount的话执行mkinitrd会出现cat /proc/partitions错误,执行lilo会出现 Fatal: raid_setup: stat(“/dev/sda”)。
/etc/lilo.conf 也作相应的调整:
boot="/dev/sda"
......
image="/boot/vmlinuz"
initrd="/boot/initrd.gz"
root="/dev/MYVG/ROOT"
label="2.6.29.6-smp"
read-only
# Linux bootable partition config ends
# Windows bootable partition config begins
other = /dev/sda1
label="Windows7"
table = /dev/sda
# Windows bootable partition config ends
Win7的引导分区是sda1,不是系统盘sda2。马上就要好了
exit # 退出chroot
umount /new_root/dev
chroot /new_root
vgscan --mknodes
vgchange -ay
如果没有这一步,重启后LILO引导菜单已经可以看见,Win7引导正常,Slackware能引导起来,但是在挂载文件系统是提示找不到 /dev/MYVG/ROOT 文件。于是我就如上这么干了,在/dev下就会有MYVG/ROOT等LV设备文件了。不过我不太明白,必须得通过vgscan –mknodes的方式来建立几个lv分区的设备文件吗?那/dev/sda文件应该是我旧系统上复制过来的,换了硬盘后能直接使用?啊呀,这方面知识太欠缺了,我自己已经没法解释了,望路过的朋友帮我解惑一下
/dev/sda6 swap swap defaults 0 0
/dev/MYVG/ROOT / reiserfs defaults 1 1
/dev/MYVG/USRLOCAL /usr/local reiserfs defaults 1 2
/dev/MYVG/HOME /home reiserfs defaults 1 2
/dev/sda3 /media/D vfat uid=0,umask=0022,fmask=0111,shortname=mixed,iocharset=utf8 0 0
/dev/cdrom /mnt/cdrom auto defaults,user,ro,noexec 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
proc /proc proc defaults 0 0
至此我的系统迁移完毕。这一次让我发现又有很多不懂不明白啊~~~
参考:
[1] 通用线程: 学习 Linux LVM,第 1 部分
[2] 通用线程:学习 Linux LVM,第 2部分
[3] README_LVM.TXT
[4] LVM White Paper 本地PDF文件
[5] Slackware 12.1 on LVM – Installation and Migration
[6] Backup almost working, LILO hangs
承上启下
December 31st, 2009年头那几天跟@hkai同学聊起某事件似乎才刚发生过,2009就只剩1个多小时了。有08奥运今年开的的错觉,才发现这一年太多荒唐可笑之事发生在每个人身边。我总是记不清这些事件的具体时间,因为它们太应接不暇了,因为它们仍然在继续演出着。
昨天刚结束了一种生活状态,就和当年高二升高三分班一样,还处在镇痛期。09年许多事情让我比较清醒地认识到了自己的性格弱点,不管某个结果如何,未来的一年我都将迎来全新的生活。
2010我希望自己继续善其身,也希望能为争取个人权利多做些什么;希望肉食者能够偶尔吃点蔬菜,清清自己的宿便;更希望有那么一个人与我一起共赏这纠结而又精彩美好的世界
这是一篇比微博长,比博客短的日志~ 新年快乐
养一下驴子
December 19th, 2009用MLDonkey很久了下不热门的资源一直非常慢,以至失去用ed2k网络的兴趣了。最近才发现原来是我没有开启MLDonkey的KAD功能 -_____-!! 没有KAD之类的技术用P2P久失去了很大的乐趣。
下面记录下几点设置,我不太懂养驴子,应该还有使驴子跑得更快的设置,还请路过大大教教我
1. MLDonkey里的enable_kademlia设为true
2. 网上看到说需要nodes.dat和contact.dat(其实这个文件不需要了,是eDonkey的Overnet网络,现在eMule的ed2k和KAD网络足矣),而且还得手工添加。其实可以通过配置MLDonkey的download.ini文件来实现自动更新nodes.dat文件。
~/.mldonkey/download.ini
web_infos = [
("server.met", 0, "http://sn.im/server.met");
("nodes.gzip", 1, "http://upd.emule-security.net/nodes.dat");
("contact.dat", 168, "http://download.overnet.org/contact.dat");
("geoip.dat", 0, "http://www.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz");
("guarding.p2p", 96, "http://www.bluetack.co.uk/config/level1.gz");
(hublist, 0, "http://dchublist.com/hublist.config.bz2");]
主要修改”server.met”和”nodes.gzip”两段,server.met用的参考[1]里提供的链接,设成0是表示只需在MLDonkey启动时载入; nodes.gzip用的参考[2]里提供的链接,设成1是表示每1小时更新一次。其他字段默认。
参考:
[1] 安全eD2k服务器列表 2009-11-28
[2] Kad节点文件 nodes.dat
[3] 终于可以在ubuntu上养驴了
[4] [问题]mldonkey可以连接kad吗?
本地机器上运行远程X程序、X环境
November 6th, 2009X11Forwarding
X11转发,在本地机器(Linux/Windows)上运行远程服务器上的X程序
***Linux***
方法1、2是在本地Linux机器上的当前窗口上显示远程服务器上的X程序(单独窗口的形式);
方法2、3使用SSH的X11转发,因此确保远程服务器上的/etc/ssh/sshd_config的X11Forwarding为yes;
方法3、4是在本地Linux上新的控制台下的新的X server上运行远程服务器的完整X环境;
方法1、4需要设置DISPLAY环境变量;
* 所有方法在两端都不需要root权限
* 192.168.1.xx 为本地Linux的IP
方法一:
本地Linux:
$ xhost +
$ ssh user@remotehost
远程服务器:
$ export DISPLAY=192.168.1.xx:0[.0]
$ xeyes& ; nautilus&
* xhost + 允许远程X程序在当前X server上允许,详细man
方法二:
本地Linux:
$ xhost +
$ ssh -X user@remotehost
远程服务器:
$ xeyes&; konqueror&
方法三:
本地Linux:
$ X :12.0 vt12 2>&1 > /dev/null &
$ xterm -display :12.0 -e ssh -X user@remotehost &
切到Ctrl+Alt+F12,在里面的xterm里运行 xeyes 等X程序,或者gnome-session、
startkde等运行完成的X环境
方法四:
本地Linux:
$ X :11.0 vt11 2>&1 > /dev/null &
$ xterm -display :11.0 &
切到Ctrl+Alt+F11在xterm里运行:
$ xhost +
本地Linux:
$ ssh user@remotehost
$ export DISPLAY=192.168.1.xx
$ xeyes& or gnome-session& or startkde&
一般来讲用ssh -X比较方便,方法1、4只是在探讨原理,实际使用相对麻烦。
根据你的不同需求选择方法2、3,在本地只单独运行某些X程序,还是需要完整的X环境。
***Windows***
Windows下需要如下软件:
X server – 本文使用Xming
ssh client – 本本使用Putty
Putty设置:
Connect -> SSH -> X11,勾选”允许 X11 映射”,填入localhost:0.0,MIT-Magic-Cookie-1协议
Xming设置:
如果你只运行单独的X程序,选择哪种窗口模式无所谓;
如果你需要完整的X环境,请选择单窗口或者全屏模式;
一定要勾选上”No Access Control”
最后用Putty登录到远程服务器,运行xeyes等独立X程序,或者gnome-session、startkde完整X环境。
References:
[1] Linux下DISPLAY环境变量的作用
[2] 用SSH实现X11转发
Opera里域名跳转
October 31st, 2009用twitter多了就会发现有很多短网址,而且一些常见的bit.ly、j.mp都被墙了,这非常的杯具。今天在推特上看到@feelingluck兄写的一个短域名跳转的实现,方法很好,比起我原先想的好。东西在此:http://lab.gracecode.com/free-jump.php
不过这个方法在Opera里遇到了问题。Opera对于指向localhost(127.0.0.1)的域名会直接给你空白页面,而不是像firefox那样Unable to connect。你得不到任何关于此域名页面的信息,比如上面提到方法里的location.href,也就无法通过location.href把短网址传递到@feelinglucky的网页实现跳转了。
但Opera就是Opera,她依然有方法达到此目的,只需在你的menu.ini文件的[Link Popup Menu]段加一行:
Item, free-dump=New page & Go to page, "javascript:(function(){c=document.createElement('script');c.src='http://lab.gracecode.com/free-jump.php?url='+encodeURIComponent('%l');document.body.appendChild(c);})()"
使用的时候对着短网址右击就可以了。效果如下图,而且不需要修改hosts:

shortname以及自动挂载
August 18th, 2009一切问题来自我前些天在优盘上搞的移动版的wiki(采用DokuWiki),插上优盘后,直接编辑我的wiki。但是发现有些页面是空白,功能不正常。后来发现是因为某些文件文件名在Linux下挂载后发生了变化,比如:”JSON.php”变成了”json.php”,造成了wiki无法正常运行 -______-!!
/etc/mtab显示有一个选项是shortname=lower,于是顺藤摸瓜google,发现通过手工挂载用参数shortname=mixed就正常。Linux下mount对fat/vfat文件系统(通常就是优盘的文件系统格式)默认挂载参数shortname的值就是lower。其实这个不太好,所有有人给了patch建议改成默认为shortname=mixed,可以看如下代码,更多见LKML里的讨论:
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 8970d8c..f9af501 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -971,7 +971,7 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug, opts->codepage = fat_default_codepage; opts->iocharset = fat_default_iocharset; if (is_vfat) { - opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95; + opts->shortname = VFAT_SFN_DISPLAY_WINNT|VFAT_SFN_CREATE_WIN95; opts->rodir = 0; } else { opts->shortname = 0;
这个shortname参数都是因为有8.3文件名这种东西的存在而存在的,可以参考Wikipedia上的这篇文章。
还没完,每次手工挂载太蠢了。所以我想到了HAL,当时觉得可以改变默认的mount挂载参数(但事实不是)。还找到了如下的HAL policy:
<?xml version="1.0" encoding="UTF-8"?> <deviceinfo version="0.2"> <device> <match key="block.is_volume" bool="true"> <match key="volume.fsusage" string="filesystem"> <match key="volume.fstype" string="vfat"> <merge key="volume.policy.mount_option.shortname=" type="string">shortname=mixed</merge> </match> </match> </match> </device> </deviceinfo>
但是没有成功,lshal里看到了 volume.policy.mount_option.shortname= = ‘mixed’ (string) 这样的信息,可是我在Konqueror里打开仍然是以lower挂载的。在cppgx大牛的提醒和google到的这篇文章中,找到了两个原因:
- KDE这类DE会自己管理mount参数
- volume.policy.mount_option.shortname 这种写法已废弃
对于第一点,你可以在插上优盘后,打开Konqueror,到Storage Media里右击你的优盘盘符,属性里有一个挂载的标签,里面就有一些自定义的参数,其中就有shortname,而且默认就是lower。因此,如果你使用KDE,那么你可以在这个标签里将shortname改成mixed就可以正常显示优盘的文件名了。不过这不是全局的,你换了个优盘后,还得改一下。这些参数保存在~/.kde/share/config/mediamanagerrc
对于第二点,难怪在最新的hal-spec里打死找不到类似的方法。
到这里我才渐渐明白HAL是无法改变mount的挂载参数的,它可以发现硬件,然后提供给上层应用关于硬盘设备的很多信息,比如什么文件系统,可用的挂载参数等。像KDE这种能否自动挂载的,一定是从HAL那儿获得信息,然后有自己的程序去最终挂载设备,然后显示给用户。我很想看看KDE到底是怎么处理这些挂载参数的,所以经过一番长时间的搜索终于找到了相关代码(从没看过KDE代码,也不懂C++,而且觉得KDE网站搜索做的也不太好):
kdebase-3.5.10/kioslave/media/mediamanager/halbackend.cpp
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | QStringList HALBackend::mountoptions(const QString &name) { const Medium* medium = m_mediaList.findById(name); if (medium && !isInFstab(medium).isNull()) return QStringList(); // not handled by HAL - fstab entry KConfig config("mediamanagerrc"); config.setGroup(name); char ** array = libhal_device_get_property_strlist(m_halContext, name.latin1(), "volume.mount.valid_options", NULL); QMap<QString,bool> valids; for (int index = 0; array && array[index]; ++index) { QString t = array[index]; if (t.endsWith("=")) t = t.left(t.length() - 1); valids[t] = true; kdDebug() << "valid " << t << endl; } libhal_free_string_array(array); QStringList result; QString tmp; QString fstype = libhal_device_get_property_QString(m_halContext, name.latin1(), "volume.fstype"); if (fstype.isNull()) fstype = libhal_device_get_property_QString(m_halContext, name.latin1(), "volume.policy.mount_filesystem"); QString drive_udi = libhal_device_get_property_QString(m_halContext, name.latin1(), "block.storage_device"); bool removable = false; if ( !drive_udi.isNull() ) removable = libhal_device_get_property_bool(m_halContext, drive_udi.latin1(), "storage.removable", NULL) || libhal_device_get_property_bool(m_halContext, drive_udi.latin1(), "storage.hotpluggable", NULL); config.setGroup(drive_udi); bool value = config.readBoolEntry("automount", false); config.setGroup(name); if (libhal_device_get_property_bool(m_halContext, name.latin1(), "volume.disc.is_blank", NULL) || libhal_device_get_property_bool(m_halContext, name.latin1(), "volume.disc.is_vcd", NULL) || libhal_device_get_property_bool(m_halContext, name.latin1(), "volume.disc.is_svcd", NULL) || libhal_device_get_property_bool(m_halContext, name.latin1(), "volume.disc.is_videodvd", NULL) || libhal_device_get_property_bool(m_halContext, name.latin1(), "volume.disc.has_audio", NULL)) value = false; result << QString("automount=%1").arg(value ? "true" : "false"); if (valids.contains("ro")) { value = config.readBoolEntry("ro", false); tmp = QString("ro=%1").arg(value ? "true" : "false"); if (fstype != "iso9660") // makes no sense result << tmp; } if (valids.contains("quiet")) { value = config.readBoolEntry("quiet", false); tmp = QString("quiet=%1").arg(value ? "true" : "false"); if (fstype != "iso9660") // makes no sense result << tmp; } if (valids.contains("flush")) { value = config.readBoolEntry("flush", fstype.endsWith("fat")); tmp = QString("flush=%1").arg(value ? "true" : "false"); result << tmp; } if (valids.contains("uid")) { value = config.readBoolEntry("uid", true); tmp = QString("uid=%1").arg(value ? "true" : "false"); result << tmp; } if ( valids.contains("locale") ) { value = config.readBoolEntry( "locale", true ); tmp = QString( "locale=%1" ).arg( value ? "true" : "false" ); result << tmp; } if (valids.contains("utf8")) { value = config.readBoolEntry("utf8", true); tmp = QString("utf8=%1").arg(value ? "true" : "false"); result << tmp; } if (valids.contains("shortname")) { QString svalue = config.readEntry("shortname", "lower").lower(); if (svalue == "winnt") result << "shortname=winnt"; else if (svalue == "win95") result << "shortname=win95"; else if (svalue == "mixed") result << "shortname=mixed"; else result << "shortname=lower"; } if (valids.contains("sync")) { value = config.readBoolEntry("sync", ( valids.contains("flush") && !fstype.endsWith("fat") ) && removable); tmp = QString("sync=%1").arg(value ? "true" : "false"); if (fstype != "iso9660") // makes no sense result << tmp; } if (valids.contains("noatime")) { value = config.readBoolEntry("atime", !fstype.endsWith("fat")); tmp = QString("atime=%1").arg(value ? "true" : "false"); if (fstype != "iso9660") // makes no sense result << tmp; } QString mount_point = libhal_device_get_property_QString(m_halContext, name.latin1(), "volume.mount_point"); if (mount_point.isEmpty()) mount_point = libhal_device_get_property_QString(m_halContext, name.latin1(), "volume.policy.desired_mount_point"); mount_point = config.readEntry("mountpoint", mount_point); if (!mount_point.startsWith("/")) mount_point = "/media/" + mount_point; result << QString("mountpoint=%1").arg(mount_point); result << QString("filesystem=%1").arg(fstype); if (valids.contains("data")) { QString svalue = config.readEntry("journaling").lower(); if (svalue == "ordered") result << "journaling=ordered"; else if (svalue == "writeback") result << "journaling=writeback"; else if (svalue == "data") result << "journaling=data"; else result << "journaling=ordered"; } return result; } |
第10行就是获取HAL提供的有效参数(lshal | grep valid_options);
第13到19行的for循环将各个参数放到valids数组中;
第21行的QStringList result;是所有参数/值对列表;
第92行就是和shortname有关的了。它先是读取配置文件~/.kde/share/config/mediamanagerrc中某个设备的shortname参数值,如果没有默认就是lower。看来KDE里处理shortname也是如此。
不过从上述代码来看,提供给KDE配置mount参数仍然不是很多,比如dmask、fmask等都没有。通过HAL还是可以添加所有的参数到valid_options。
* BTW,上述代码是KDE 3.5.10环境下的,KDE 4默认的shortname已经是mixed了。不过不再是mediamanager了,相关代码还没找到去看。
最后提供个方法修改mount任意挂载参数,以挂载vfat文件系统为例:
$ cat /sbin/mount.vfat #!/bin/sh /sbin/mount -i -t vfat -o umask=0000,fmask=0111,dmask=0000,shortname=mixed "$@"
注意,这是全局修改vfat的挂载参数。KDE最后还是调用mount挂载的设备,因此有了这个文件你就可以自定义KDE等不支持的参数了。
EOF
Test
August 6th, 20092009/08/06 13:44