10
29
2013
11

不需要 root 权限的 ICMP ping

ICMP 套接字是两年前 Linux 内核新加入的功能,目的是允许不需要 set-user-id 和CAP_NET_RAW权限的 ping 程序的实现。大家都知道,set-user-id 程序经常成为本地提权的途径。在 Linux 内核加入此功能之前,以安全为目标的 Openwall GNU/*/Linux 实现了除 ping 程序之外的所有程序去 suid 化……这个功能也是由他们提出并加入的。

我并没有在 man 手册中看到关于 ICMP 套接字的信息。关于 ICMP 套接字使用的细节来自于内核邮件列表

使用 ICMP 套接字的好处

  1. 程序不需要特殊的权限;
  2. 内核会帮助搞定一些工作。

坏处是:

  1. 基本没有兼容性可讲;
  2. 需要调整一个内核参数。

这个内核参数net.ipv4.ping_group_range,是一对整数,指定了允许使用 ICMP 套接字的组 ID的范围。默认值为1 0,意味着没有人能够使用这个特性。手动修改下:

sudo sysctl -w net.ipv4.ping_group_range='0 10'

当然你可以直接去写/proc/sys/net/ipv4/ping_group_range文件。

如果系统不支持这个特性,在创建套接字的时候会得到「Protocol not supported」错误,而如果没有权限,则会得到「Permission denied」错误。

创建 ICMP 套接字的方法如下:

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_ICMP)

它的类型和 UDP 套接字一样,是SOCK_DGRAM,不是SOCK_RAW哦。这意味着你不会收到 20 字节的 IP 头。不仅仅如此,使用 ICMP 套接字不需要手工计算校验和,因为内核会重新计算的。ICMP id 也是由内核填的。在接收的时候,内核会只把相应 id 的 ICMP 回应返回给程序,不需要自己或者要求内核过滤了。

所以,要组装一个 ICMP ECHO 请求包头很容易了:

header = struct.pack('bbHHh', 8, 0, 0, 0, seq)

这五项依次是:类型(ECHO_REQUEST)、code(只能为零)、校验和(不需要管)、id(不需要管)、序列号。

接收起来也简单,只要看一下序列号知道是回应自己发的哪个包的就行了。

这里是我的一个很简单的实例。

附注:Mac OS X 在 Linux 之前实现了类似的功能。但是行为可能不太一样。有报告校验和需要自己计算的,也有报告发送正确但是返回报文是乱码的。另,FreeBSD 和 OpenBSD 不支持这个特性。

Category: Linux | Tags: linux python 网络 ICMP
10
25
2013
14

php.net 被攻破,Google Chrome 浏览器竟浑然不知

人们都说 Google Chrome 浏览器安全,可是——

昨日,有网友告诉我 php.net 被挂马了。于是我在火狐浏览器中访问,果然得到了警告:

php.net blocked by Firefox

再去 Google 搜索一下,果然也被标记上了:

php.net blocked by Google Search

直接点击搜索结果会得到 Google 的警告:

php.net blocked by Google

但是,Google Chrome 浏览器访问竟然没有任何提示

今天,这个警告已经移除了。php.net 官方发布了一些消息,证实它确实被攻破了。为了以防万一,他们吊销了 php.net 所使用的 SSL 证书。我于是尝试使用火狐访问使用了 SSL 的子站,比如这个 https://wiki.php.net/,火狐很明确地告诉我,它的证书已经被废弃了:

php.net SSL certificate revoked error

但是,Google Chrome 浏览器访问依然没有任何提示,表示安全的小绿锁依然鲜亮。

难道是 Google 认为 Google Chrome 浏览器本身已经足够安全,不仅能抵御任何它能检测出来的恶意网页,而且能在证书已被吊销的情况下判断出网页是否被篡改?

php.net with revoked certificate accepted by Google Chrome

注:以上均为浏览器默认安全配置,我没有手动修改任何安全相关的选项。

更新:在 Google Chrome 的「设置」中,点击「显示高级设置…」,往后滚,找到「HTTPS/SSL」,把「检查服务器证书吊销状态」前边的框勾上,Google Chrome 就也会报告证书不可信了。

再次更新:通过 GoAgent 访问,没有收到任何关于服务器证书已经失效的提示。证实了我之前关于通过 GoAgent 访问 HTTPS 站点会降低安全性的猜测。


感谢 Eleven.i386 提供了部分截图。

10
20
2013
5

通过 OpenVPN 让 TCP 使用 UDP 洞

上篇成功让 mosh 走 UDP 洞,连接上了在 NAT 后边的主机。然而,很多有用的协议都是走 TCP 的,比如能传文件的 ssh、访问我的 MediaWiki 的 HTTP。TCP 洞难打,于是在想,OpenVPN 可以使用 UDP 协议,那么把双方用 OpenVPN 连起来,不是可以想用什么传输层的协议都可以了吗!于是,有了新的脚本

与 mosh 相比,打洞部分主要的不同有:

  1. OpenVPN 的可配置性强,不需要 hack 即可让它绑定到需要的端口。
  2. OpenVPN 本身使用证书认证,因此把证书部分保存在客户端,余下的部分(包含双方使用的 IP 地址和端口号)可以通过打好的洞明文发送,不用怕被攻击。所以跑我这个脚本的话,当前工作目录要可写,以便保存双方即将使用的配置文件。
  3. OpenVPN 客户端会自动忽略对方发过来它不认识的配置信息,不用想办法避免。
  4. OpenVPN 需要 root 权限,因此脚本调用了 sudo,需要及时输入密码

在实验过程中也遇到了一些坑:

  1. Python 里没办法将已连接的 UDP socket「断开连接」,即将一个已经connect的 UDP socket 恢复到初始时可接收任意地址数据的状态。原本以为connect(('0.0.0.0', 0))可以的,结果客户端这边始终收不到服务端发送的 OpenVPN 配置信息。Wireshark 抓包看到内核收到数据后发了 ICMP Port Unreachable 错误之后才明白过来。
  2. MTU 的问题。默认值会导致刚开始传输正常,但随后收不到数据的情况。添加mssfix 1400配置解决。(其实这个 OpenVPN man 手册里有写。)
  3. 超时的问题。先是没注意到 OpenVPN 服务端说没有配置keepalive的警告,结果连接空闲几分钟之后,「洞」就失效了。加上keepalive 10 60解决。

配置中没有加默认路由,所以连接上之后唯一的效果就是,两个主机分别多出了同一网段的两个 IP 地址,相互间可以进行 TCP 通信了~~

对了,客户端连接时需要一个包含 OpenVPN 证书信息的文件,其格式为:

<ca>
# ca.crt 文件内容
</ca>

<cert>
# crt 文件内容(只需要 BEGIN 和 END 标记的那部分)
</cert>

<key>
# key 文件内容
</key>

PS: 有这个想法后不久,发现 None 已经做过类似的事情了。不过脚本有点多,是使用第三方服务器而不是像我这样手工交换地址的。

Category: 网络 | Tags: python 网络 openvpn UDP
10
17
2013
8

订阅至此收取点,使用 InoReader 阅读器

Google 阅读器死掉好久了,火狐却依旧惦记着它:

火狐,你看这样可好?

配置方法:打开about:config,查找并修改以下选项值:


2013年11月11日更新:原来 InoReader(以及火狐)支持直接添加为 RSS 阅读器。在 InoReader 的「用户偏好设置」里,「附加功能」选项卡下,滚动到最后有一个「点按这里将 InoReader 作为订阅源阅读器添加到 Firefox」——

 

其对应的代码为:

function add_ff_handler(){navigator.registerContentHandler("application/vnd.mozilla.maybe.feed",proto+"://www.inoreader.com/?add_feed=%s","InoReader");}
Category: 火狐 | Tags: rss 火狐
10
13
2013
13

通过 UDP 打洞连接 NAT 后边的 mosh

又是一篇关于 UDP 打洞的文章。之前写过关于在完全圆锥型(full cone)NAT的文章中如何使用 socat 命令打洞。根据那篇文章里的知识,连接到一个 full cone NAT 后边的 mosh 不成问题。不过,我现在的网络是受限圆锥型(restricted cone)NAT 了呢!

也就是复杂了一些。双方要向中间服务器和对方都发送数据包才可以。另外就是,客户端(mosh-client)这边得使用在打洞期间使用的端口号才行。

打洞流程根据维基百科,双方通过中间服务器(还是我的 udpaddr 啦)交换地址,双方均向得到的地址发送一数据包,然后开始正常通讯。比较麻烦,于是有了这个脚本

#!/usr/bin/env python3

import socket
import re
import sys
import subprocess

udp_server = ('xmpp.vim-cn.com', 2727)
addr_re = re.compile(r"\('(?P<ip>[^']+)', (?P<port>\d+)(?:, (?P<cport>\d+))?")

def main(server):
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  sock.settimeout(2)
  print('Send message...')
  sock.sendto(b'req from holepunch.py\n', udp_server)
  msg, addr = sock.recvfrom(1024)
  print('Got answer from %s: %s' % (addr, msg))
  m = addr_re.search(msg.decode())
  if not m:
    print("Error: can't parse answer.")
    sys.exit(1)
  m_ip = m.group('ip')
  m_port = int(m.group('port'))
  port = sock.getsockname()[1]
  print('Got my IP and Port: (%r, %s, %s).' % (m_ip, m_port, port))

  msg = input('> Peer address: ')
  m = addr_re.search(msg)
  if not m:
    print("Error: can't parse input.")
    sys.exit(2)
  p_ip = m.group('ip')
  p_port = int(m.group('port'))
  c_port = int(m.group('cport'))

  print('send initial packet and wait for answer...')
  sock.sendto(b'HELO\n', (p_ip, p_port))
  try:
    msg = sock.recvfrom(1024)
    print('Received:', msg)
  except socket.timeout:
    print("Timed out (it's normal).")

  if server:
    sock.close()
    print('Starting mosh server...')
    msg = subprocess.check_output(['mosh-server', 'new', '-p', str(port)])
    secret = msg.split()[3].decode()
    print('Connect with:\nMOSH_KEY=%s MOSH_CPORT=%s mosh-client %s %s' % (secret, c_port, m_ip, m_port))
  else:
    print('done.')

if __name__ == '__main__':
  server = len(sys.argv) == 2 and sys.argv[1] == '-s'
  main(server)

如果 mosh 服务器端位于受限 NAT 后,还需要给 mosh-client 打个(我随手写的很 dirty 的)补丁以便指定客户端使用的 UDP 端口号:

diff --git a/src/network/network.cc b/src/network/network.cc
index 2f4e0bf..718f6c5 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -176,6 +176,11 @@ Connection::Socket::Socket()
     perror( "setsockopt( IP_RECVTOS )" );
   }
 #endif
+
+  if ( getenv("MOSH_CPORT") ) {
+    int port = atoi(getenv("MOSH_CPORT"));
+    try_bind( _fd, INADDR_ANY, port, port);
+  }
 }

 void Connection::setup( void )

然后,连接流程如下:

  1. mosh 服务端运行holepunch.py -s命令,客户端运行holepunch.py
  2. 双方看到自己的地址信息(依次是IP, 外网端口, 本地端口)后,复制并发送给对方;
  3. 双方输入对方的地址。为了节省时间(mosh-server 只会等一分钟,NAT 上的端口映射也是有时效的),只要输入的一行内包含上述地址信息的文本即可,前后可以有不小心复制过来的多余字符;
  4. 服务端将得到一行包含 mosh 的密钥的命令。将此命令发送对客户端;客户端运行此命令连接。如果 mosh-client 的名字不是标准名字,需要自行修改;
  5. 一切顺利的话就连接上啦!

mosh 服务端输出示例:

>>> holepunch.py -s
Send message...
Got answer from ('202.133.113.62', 2727): b"Your address is ('180.109.80.47', 3169)\n"
Got my IP and Port: ('180.109.80.47', 3169, 8127).
> Peer address:  Port: ('222.95.148.73', 5223, 55473). 
send initial packet and wait for answer...
Timed out (it's normal).
Starting mosh server...

mosh-server (mosh 1.2.4)
Copyright 2012 Keith Winstein <mosh-devel@mit.edu>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

[mosh-server detached, pid = 5202]
Connect with:
MOSH_KEY=1apguNqtfN/K4JSvllnJxA MOSH_CPORT=55473 mosh-client 222.95.148.73 5223

mosh 客户端输出示例:

>>> holepunch.py
Send message...
Got answer from ('202.133.113.62', 2727): b"Your address is ('222.95.148.73', 5223)\n"
Got my IP and Port: ('222.95.148.73', 5223, 55473).
> Peer address: rt: ('180.109.80.47', 3169, 8127)
send initial packet and wait for answer...
Timed out (it's normal).
done.

2013年10月20日更新:想要通过打出来的 UDP 洞进行 TCP 通信吗?参见文章通过 OpenVPN 让 TCP 使用 UDP 洞

Category: 网络 | Tags: python 网络 mosh UDP
10
12
2013
24

GM 脚本:在 Disqus 中提示需要登录

Disqus 越来越受欢迎,然而,非 Disqus 用户评论越来越艰难

一开始,和 WordPress 一样,名字、电邮、网站。想要新评论通知?好呀,使用 Facebook、Twitter 或者 Google+ 登录下就好。

后来,「Twitter 用户,创建个 Disqus 帐号吧!」不想要 Disqus 帐号,那就不要登陆了,也甭想推广自己的博客,填上电邮地址显示个头像吧。当然,为了迫使你们登陆,名字和电邮信息也不像一般博客是记住的。下次继续填,继续勾选「以访客身份发布」。

现在,花了不少时间和心思写完很不错的评论,双击填名字的文本框填名字时,却经常发现刚展开的部分里那个「以访客身份发布」复选框没有了。「对不起,必须登录才能在此博客留言哦亲。」Holy shhhhhhhhhhhhhit!

此 GreaseMonkey 脚本为防止最后一种情况的发生,在你动手写下评论的时候明确告诉你不登录你的评论是发不出去的

不过,由于我现在取不到自己的 Disqus 帐号密码,所以不确定登录 Disqus 帐号之后这个脚本能否正确检测到。欢迎反馈!

点击安装

脚本全文如下:

// ==UserScript==
// @name        Disqus login required reminder
// @namespace   http://lilydjwg.is-programmer.com/
// @description Remind you if you can't post your comments because you aren't logged in
// @include     http://disqus.com/embed/comments/*
// @include     https://disqus.com/embed/comments/*
// @version     1
// ==/UserScript==

var check = function(){
  var el = document.querySelector('input[name="author-guest"]');
  if(!el){
    setTimeout(check, 100, false);
    return;
  }
  if(el.style.display == 'none'){
    console.log("login required");
    var msg = document.getElementsByClassName('placeholder')[0];
    msg.textContent = '需要登录 / Login Required!';
    msg.parentNode.addEventListener('blur', function(){
      var msg = document.getElementsByClassName('placeholder')[0];
      msg.textContent = '需要登录 / Login Required!';
    });
  }
};

setTimeout(check, 100, false);

点击安装


附:我始终认为,不管登陆评论能给用户和自己带来多大的好处,只要文章允许评论,来访者应当能够以最小成本发表评论并且署名。也就是,不需要注册,不需要登录,你就可以评论。最好支持 Gravatar 头像,最好支持链接到自己的网站,最好支持被回复时 Email 提醒。实际上本博客非登录用户需要填写验证码我已经很不爽了,只是 Chito 这个博客程序提供的另一种反垃圾策略——使用 Akismet——我这边已经坏掉了。

所以我越来越敬佩 WordPress。

Category: 火狐 | Tags: 博客 火狐 网页 GreaseMonkey
9
16
2013
64

为 Kindle 交叉编译 Zsh 和 Python 3.3

一些天前,根据加州旅客的文章《kindle paperwhite越狱更换屏保》获得了自己的 Kindle Paperwhite 的 root 权限,就开始想着给它编译些东西了。恰巧小虾也在玩交叉编译,而且是 Python,于是自己也照着编译。

交叉编译 Zsh

交叉编译 Python 比编译 zsh 要难不少,所以,先简要说一下 zsh 等能够「无障碍交叉编译」的使用 Autotools 构建系统的软件是怎么编译的。

首先,弄清楚几个概念。「host」是指程序要运行的目标平台,「build」是指编译该程序的平台,而在编译编译器时会遇到的「target」则指的是其生成的文件所运行的平台。(参见CLFS 构建手册

其次,得找个交叉编译器。有些发行版可能仓库里就有。但是 Arch 没有,所以我还是用的 zshaolin 的这个工具链。下回来解压了,把它的bin目录加到$PATH里。Zsh 里可以这么写:

path+=/path/to/arm-dyne-gcc_64bit-x-arm7a-21jan12/bin

然后就可以去 zsh 源码目录下开始编译啦。还是那三步,只是参数有些不一样而已:

mkdir build-arm && cd build-arm
../configure --host=arm-dyne-linux-gnueabi --enable-multibyte --enable-pcre --with-term-lib='ncursesw'
make
make DESTDIR=/kindle/software/zsh-5.0.2 install

且慢!我指定了--enable-pcre,却没有去检查自己是否有这个库。可能是 bug 吧,zsh 的 configure 脚本并没有检测到我其实没有 pcre 的 ARM 版库文件,于是它接着在很多检测过程中加入了-lpcre,导致检测结果与实际不符(比如它认为我的目标系统上没有setpgid()函数)。编译安装 pcre 到工具链的 sysroot 下后再次编译通过。

再编译一些需要的库

所以你看,使用 Autotools 构建系统的软件交叉编译起来挺容易的,加上需要的--host参数就好了。其它诸如 file、ncurses、readline 也是这么编译安装的。下边说说那些比较「个性」的软件的编译参数。它们基本上都必须在源码目录编译。

首先是 zlib:

CHOST=arm-dyne-linux-gnueabi ./configure
make && make DESTDIR=/kindle/software/zlib-1.2.8 install

TLS/SSL 很有用!OpenSSL:

CC=arm-dyne-linux-gnueabi-gcc LD=arm-dyne-linux-gnueabi-ld AR=arm-dyne-linux-gnueabi-ar RANLIB=arm-dyne-linux-gnueabi-ranlib ./Configure shared linux-armv4
make
make INSTALL_PREFIX=/ldata/media/temp/kindle/software/openssl-1.0.1e install_sw

注意最后不是make install哦,那个会安装一堆不需要的文档的。

对了,我好像忘记说了,以上软件make install之后的操作

  1. 删除文档,比如 rm -rf share/man
  2. 复制到工具链的 sysroot 下,命令:
    tar c . | tar xv -C /path/to/arm-dyne-gcc_64bit-x-arm7a-21jan12/arm-dyne-linux-gnueabi/sysroot
    
  3. 给二进制文件们减肥啦(以下命令需要 zsh;非 zsher 请自行使用合适的 find 命令代替):
arm-dyne-linux-gnueabi-strip **/*(*)

另外再附上 ncurses 的配置命令好了:

../configure --host=arm-dyne-linux-gnueabi --with-shared --with-normal --without-debug --without-ada --enable-widec --enable-pc-files --prefix=/usr/local

是的,我把软件都安装到/usr/local了。在编译 Python 3.3 时的事实表明,这是给自己找麻烦……

编译 Python 啦

好了,准备工具完毕,我们来真正开始编译 Python 啦

按照小虾的文章,首先修改Modules/Setup.dist文件,把需要的模块去掉注释。一定要把最后的xxsubtype给注释掉!因为它只是很无聊的示例模块……

除了要安装模块对应的程序库外(比如要 curses 模块就得先安装 ncurses 等),还要注意一点:如果开启 readline 支持的话,把它后边的-ltermcap删掉!

开始配置了:

mkdir build-arm && cd build-arm
echo ac_cv_file__dev_ptmx=yes > config.site
echo ac_cv_file__dev_ptc=no >> config.site
export CONFIG_SITE=config.site
../configure --host=arm-dyne-linux-gnueabi --build=arm --enable-shared --disable-ipv6

根据实际 ssh 过去的结果,我的 Kindle 有/dev/ptmx但是没有/dev/ptc,所以往config.site文件里写上那么两句。没办法,交叉编译时脚本不知道目标系统里是否有这两个设备文件。当然,你都设置成no也是没什么问题的。

--build=arm这系统纯粹是 Python 的这个配置脚本要求的,和之前所说的常见使用方法不一样的。IPv6 支持需要的某函数配置脚本找不到,那就禁用掉好了。

配置完成,开始 make?

根据所开启的模块支持不同,在编译过程中很有可能地,你会遇到Parser/pgen无法执行的问题。它被编译成 ARM 版了,当然无法执行了!解决方案是这样子的:

修改pyconfig.hSIZEOF_LONG为正确值(比如 64 位 x86 下是8)。如果已经是对的就不要动了。然后重新生成个本地可运行的pgen

rm Parser/*
make CC=gcc Parser/pgen

接着把pyconfig.h改回去。然后,为了避免pgen被重建,我们让 make 认为pyconfig.h没有被修改过:

touch -t 200001010000 pyconfig.h

继续编译!

如果又出来了架构不对的情况,删除刚刚编译pgen时编译出来的目标文件吧:

rm Parser/*.o
touch -t 210001010000 Parser/pgen

touch pgen 的原因是,不能让 make 又把它编译成本地不能运行的 ARM 架构的了。

架构不对的目标文件可能还有一些,按照错误提示删掉就好了。

最后,要生成 Python 的可执行文件啦!很可能地,你会遇到类似这个的错误(我自己这里的错误信息已经没啦,下边这个由加州旅客提供):

libpython3.3m.a(timemodule.o):在函数‘py_process_time’中:
/home/jiazhoulvke/Python-3.3.2/./Modules/timemodule.c:1076:对‘clock_gettime’未定义的引用
/home/jiazhoulvke/Python-3.3.2/./Modules/timemodule.c:1082:对‘clock_getres’未定义的引用

查阅clock_getres的 man 文档得知:

Link with -lrt (only for glibc versions before 2.17).

于是,复制 make 最后执行的那条链接命令,在后边加上lrt吧。如果crypt没有定义的话,还要加上-lcrypt

终于可以安装啦

接下来,当然是把程序安装到 Kindle 上啦!首先执行个make DESTDIR=xxx install安装到某个目录,然后进去清理下吧:

arm-dyne-linux-gnueabi-strip **/*(*)
cd lib/python3.3
# 删除所有你不想要的模块,比如测试代码(`test`,不是`unitest`哦)、tk/idle,
# 还有 distutils 里一堆乱七八糟的东东

# 删除 Python 源码和 pyc 文件,我们只要 pyo 文件就好啦=w=
rm **/*.pyc?
# 在 zip 文件里 Python 可不认 __pycache__……
perl-rename 's=__pycache__/([^.]+).cpython-33.pyo$=\1.pyo=' **/*.pyo
rmdir **/*(/)
zip -9r ../python33.zip .

然后,把生成的python33.zip放到 Kindle 的/usr/local/lib目录下,Python 二进制文件也放到对应的位置。记住,库文件要使用 tar 而非 scp 来传输!像这样子:

tar c libz.so* | ssh kindle tar xv -C /usr/local/lib

其实不少库 Kindle 上已经有了,比如这里的 zlib。不过很奇怪,使用系统自带的 zlib 运行 Python 时会报如下警告:

python3: /usr/lib/libz.so.1: no version information available (required by /usr/local/lib/libpython3.3m.so.1.0)

另一个我发现无关紧要的警告是让你设置PYTHONHOME环境变量的:

Could not find platform independent libraries <prefix>
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]

其实不设置也是没关系的。

哦对了,现在我们的 Python 应该还跑不起来的吧!有可能缺少一点库文件的哦!把之前编译生成的对应的库文件全部拿tar扔到/usr/local/lib下吧。再说一遍,使用scp传输的话软链接会变成其指向的文件,浪费掉 Kindle 上宝贵的存储空间!

库文件扔进去之后,首先确认/etc/ld.so.conf里已经包含了/usr/local/lib,然后执行下ldconfig

终于,我们的 Python 在 Kindle 上跑起来啦!

后记,及下载链接

这个是我编译的 Python 的文件大小:

-rwxr-xr-x 1 root root 5.4K Sep 15 00:15 /usr/local/bin/python3.3
-r-xr-xr-x 1 root root 4.0M Sep 15 00:15 /usr/local/lib/libpython3.3m.so.1.0
-rw-r–r– 1 root root 2.2M Sep 15 00:43 /usr/local/lib/python33.zip

主要支持特性有:SSL、readline、ncurese、zlib、中日编码集、Unicode 数据库等。最后再放百度网盘下载链接(包括好些东东哦)。

最后我要说一句,Kindle 才是真正的 Linux 啊!编译起来如此方便!还各种常见库(包括 glibc、zlib、OpenSSL、GTK 2)都有。想之前给 Android 编译点东西得砍掉多少特性啊!又有多少软件死活编译不成功 :-(

PS: Kindle 虽然也用 Java 的,但是它有 X Window,还有 GTK 2 以及 Awesome 窗口管理器哦~可惜它的 Awesome 没开启 D-Bus 支持。

更新:lxml

今天(2013年9月17日),成功编译了 Python 最著名的 XML 处理模块——lxml。编译方法是,指定CC环境变量,复制python3 setup.py build时出错的那两条编译命令并修改,编译出来目标文件存起来。将CC指定为自己的脚本来「生成」它想要的文件。链接时手动改命令链好就行。

因为有 .so 文件,lxml 不能打成 zip 包。因此我直接将*.pyc*.pyo文件连同.so文件一同复制到 Kindle 的/usr/local/lib/python3.3/lxml下。由于版本不匹配,需要把libxml2.so.2.9.1文件也传到 Kindle 中去,覆盖了 Kindle 中旧版本的库文件,希望不会有问题

Category: Linux | Tags: python zsh 交叉编译 kindle
9
16
2013
4

记一次 Wine 乱码

今天发现 QQWry 升级程序的一些按钮和对话框中文显示乱码,但是在全新建立的 Wine prefix 中正常。经与新 prefix 的比对,将以下注册表信息写入system.reg后即恢复正常:

[System\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset]
"ANSI(00)"="YES"
"OEM(FF)"="YES"
"SYMBOL(02)"="NO"
Category: 中文支持 | Tags: 乱码 wine 中文支持
8
29
2013
0

不是所有 PAGER 都叫 less

在 Linux 下,最常见的 pager(翻页器)就是 less 了,所以很多时候,我都忘记了还有$PAGER这个环境变量,直到有一天我写了这么个 shell 函数:

repodo () {
  for f in $(cat ~/workspace/.my-repos); do
    echo "\n>>> $f\n"
    cd ~/workspace/$f && stdoutisatty $@
    cd - > /dev/null
  done | less
}

这个函数对于~/workspace/.my-repos中记录的每一个项目,在对应的目录下执行同一条命令,并使用 less 来查看输出。其中,stdoutisatty 是一个把标准输出伪装成 tty 的脚本,这样一些命令就不会因为实际输出到管道而关掉彩色高亮之类的了。

比如

repodo git st

ststatus的 git 别名。

这一句命令就可以查看所有项目的工作区状态了。

后来,我执行这样一条命令,它就出问题了:

repodo git grep string

因为 stdoutisatty 的缘故,git grep 会自动调用翻页器。于是,出现了两个 less 同时要读终端输入。

首先想到的是 git 的--no-pager参数,但这个很显然对其它命令无效。于是才想起自设置之后一直没再搭理的$PAGER环境变量:

repodo () {
  for f in $(cat ~/workspace/.my-repos); do
    echo "\n>>> $f\n"
    cd ~/workspace/$f && PAGER=cat stdoutisatty $@
    cd - > /dev/null
  done | less
}

PAGER指定为cat直接输出,这样就不会有多个 less 在运行了。

但这样还没有结束,因为我的不少脚本里都是直接调用 less 的,现在得改成这样子了:

command | ${PAGER:-less}

或者在 Python 里:

p = subprocess.Popen([os.environ.get('PAGER', 'less')], stdin=subprocess.PIPE,
                      universal_newlines=True)

附:less 默认是会转义来自输入的彩色转义字符序列的。我使用了-FRXM参数,也是通过环境变量传递的:

export LESS=-FRXM

这四个选项的意义是:

-F
如果一屏能显示下,那么显示完就退出
-R
不要转义 ANSI 彩色转义字符序列
-X
不要发布终端初始化和结束字符串。这样才不会使用终端的备用屏幕,less 的输出才会留在主屏幕上(使用-F选项时必须,不然可能看不到东西)
-M
在 less 提示符(最后一行)显示更多信息(比如文件的百分比位置)
Category: shell | Tags: linux shell 环境变量 less
8
23
2013
11

xmodmap 和 fcitx 配合使用

很早之前,因为有了 fcitx-keyboard,fcitx 能够管理键盘布局了。于是乎,经常干了什么事情之后,xmodmap 的效果就没了。

为了解决这个问题,fcitx 可以在相关事件时自动调用 xmodmap 命令。然后我发现,xmodmap 命令经常会调用很多很多次。我笔记本的配置还好,那个 xmodmap 配置调用多次会有命令失败,所以只要调整下顺序就可以保证键映射正确:

keysym Pause = Print
remove Lock = Caps_Lock
keysym Escape = Caps_Lock
keysym Caps_Lock = Escape
add Lock = Caps_Lock
keycode 107 = Super_R Sys_Req Super_R Sys_Req

这样子日志里会多一些消息,无所谓了,反正桌面日志我只保留最近的一份。

可是,我另外的系统上只需要交换 EscCaps Lock 这两个键:

remove Lock = Caps_Lock
keysym Escape = Caps_Lock
keysym Caps_Lock = Escape
add Lock = Caps_Lock

于是,当 fcitx 调用偶数次 xmodmap 时,这两个键就给交换回去了……实际的效果是,我几乎每次从挂起中恢复,都需要手动执行一次 xmodmap。更烦的是,几乎每次在 gnome-screensaver 里输入密码时,大小写切换键默认是开着的。这时候我得按按 Esc 或者 Caps Lock,或者是输入一个字符后再按它们中的一个。一直以来没找到规律……

最后,终于查阅 xmodmap 手册,写了下面这个简单的脚本:

#!/bin/bash -e

[[ -n $(xmodmap -pk | awk '$1 == 66 && $3 == "(Escape)"') ]] || xmodmap ~/.Xmodmap

如果 Esc 键已经交换过了,就不要再交换一次了。

再设置 fcitx 执行这个我自己的脚本就可以了:

fcitx 中的 xmodmap 配置

2013年8月28日更新:csslayer 最近已经修复了 fcitx 多次调用 xmodmap 的问题,不再需要这样特别的设置了。感谢 csslayer 的及时修正=w=

Category: Linux | Tags: fcitx X Window X window xmodmap

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com