7
27
2012
8

fcitx-remote 接口通过 socat 跨主机使用

在使用 Mac OS X 时,我十分想念 fcitx.vim 插件在使用 Vim 时能智能切换输入法的激活状态。所以我换回 Arch Linux 了。关于 Mac OS X 与我的「不兼容」还是留到下次再说,这次解决的问题是,当我 ssh 到另一主机上使用 Vim 时,如何让 fcitx.vim 能够控制本机的输入法状态?

fcitx-remote 接口使用的是 UNIX 套接字文件,因此天生是不能跨主机通信的(因此不用担心局域网里其它人捣乱)。现在,为了进行跨主机通信,当然要使用网络套接字了。既然都是套接字,转发下就可以了嘛。于是想到 socat。

在远程机器监听一个套接字文件,转发到本地机器的 8989 端口:

socat UNIX-LISTEN:/tmp/fcitx-remote.sock,fork TCP:192.168.2.142:8989

在本地监听网络 8989 端口,转发到本地 fcitx 的套接字:

socat tcp-listen:8989,fork UNIX-CONNECT:/tmp/fcitx-socket-\\:0

fcitx.vim 使用更新后的 1.2 版,然后告诉它你要使用的套接字文件地址:

export FCITX_SOCKET=/tmp/fcitx-remote.sock                                                                                                                                               

然后就可以啦~

最后,贴一张测试过程中抓到的 htop 的图片,2 万多进程哦,htop 已经卡了,实际的 load 请看右下角的红色数字。我执行killall socat命令后等了几分钟,终于因为内存耗尽系统开始重新缓慢工作了。数次 killall 后终于恢复正常……再次测试前果断先ulimit -u 1000 :-)

Category: Linux | Tags: fcitx vim 网络 socat
6
6
2012
12

编程获取本机IPv4及IPv6地址

首先,我要通过编程直接获取,而不是去读诸如ifconfig等命令的输出。

其实是只想获取IPv6地址的,不过我猜想它们差不多,也确实看到不少相关搜索结果,于是顺带着看了。

首先,使用gethostbyname查自己通常是不行的,因为可能得到127.0.0.1,而且我猜,这样不能处理拥有多个IPv4地址的情况。另外一种方式是连上某个主机,然后调用getsockname。这样需要能够直接连上那个主机,好处是如果有多个网络接口,这样可以知道到底走的是哪个接口,调试网络时不错。我最满意的方案在这里,使用ioctl来获取。这个方法可以获取指定网络接口的IPv4地址。至于有哪些网络接口嘛,直接读/proc/net/dev吧。

import fcntl
import socket
import struct
ifname = b'eth0'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 0x8915 是 SIOCGIFADDR
ip = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24])
print(ip)

然而,这样只能获取IPv4地址。创建个AF_INET6的 socket 传过去会报错「Inappropriate ioctl for device」。那怎么办呢?Google 没找到,我去搜了下内核源码。inet_ioctl里有对SIOCGIFADDR的处理。但是,inet6_ioctl里却没有了。

于是,我只好去下载ifconfig所属的 net-tools 的源码,找到相关代码:

#if HAVE_AFINET6
    /* FIXME: should be integrated into interface.c.   */

    if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
    while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
              addr6p[0], addr6p[1], addr6p[2], addr6p[3],
              addr6p[4], addr6p[5], addr6p[6], addr6p[7],
          &if_idx, &plen, &scope, &dad_status, devname) != EOF) {
        if (!strcmp(devname, ptr->name)) {
        sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
            addr6p[0], addr6p[1], addr6p[2], addr6p[3],
            addr6p[4], addr6p[5], addr6p[6], addr6p[7]);

这里就是ifconfig输出IPv6部分的代码了。可以看到它打开了一个奇怪的文件。跟过去,发现是

#define _PATH_PROCNET_IFINET6       "/proc/net/if_inet6"

囧,这个文件我早就发现过了的。看来和IPv4的情况不同,IPv6地址只能通过/proc里的文件获取了。而且输出成人可读格式不容易(ifconfig是自己实现的)。

PS: 我还发现了件好玩的事,在 Linux 源码的include/linux/sockios.h中,SIOCGIFINDEX中的字母 C 写漏了。通过git blame我发现,这个拼写错误在至少七年前 Linux 内核代码迁移到 git 前就修正了。Linus Torvalds 说之前的代码导入到 git 后有 3.2GB。我不得不承认这是个无比正确的决定,因为现在的.git已经有600多兆了,git 不支持断点续传,clone 下来已经很不容易了。

另外,我还联想到了 Unix 系统调用中的creat,以及 HTTP 协议中的referer :D

#define SIOCGIFINDEX    0x8933      /* name -> if_index mapping */
#define SIOGIFINDEX SIOCGIFINDEX    /* misprint compatibility :-)   */
Category: Linux | Tags: C代码 linux python 网络
4
29
2012
53

放弃 gnome-terminal,转到 Xfce 终端

前日进行了系统升级,结果分外悲剧。在 Awesome 下,Empathy 和 gnome-terminal 都不能正常使用了。当然,Awesome 并没有升级,它很稳定。关于 Empathy 的事下篇再写,本篇只吐槽 gnome-terminal。

早些时候,我就发现我经常不能在 gnome-terminal 中成功打开输入法。昨天将 gnome-terminal 从 3.2.1-1 版本升级到 3.4.1.1-1 (以及相伴的其它组件升级)后,我发现我已经很难遇到输入法「恰巧」能够打开的时候了。于是给 GNOME 他们报了 bug。而昨天和今天早些时候,只好使用 vimim 或者复制的方式来在终端里输入中文。今天,收到 gnome-terminal 开发者 Christian Persch 的回复:

Please test if this is reproducible with either gnome-shell or metacity (latest versions from gnome 3.4) in click-to-focus mode. Anything else is entirely unsupported.

他说,如果不能在 GNOME 的组件中重现,那么他们不会修复。操你妹啊,你丫又没有平铺式WM!这不就相当于我说你们的某家电在我家里无法使用,经销商却说你们要是不能在商场里重现问题那不关他们的事么!

于是决心放弃一切有问题的 GNOME 组件,换终端了。我不想用 guake 那种特别的终端,因为一个普通的正常终端在 Awesome 下已经被我调教得很听话了。先尝试的是 lxterminal。忽略掉不完整的中文翻译,开始迁移自己的配置。终端光标不能更改为竖线就算了,竟然没办法通过命令行参数的方式指定窗口的任何可以用作区分的参数。原来 gnome-terminal 我是指定 class 的,这样我可以设置一个流动终端——按一个组合键把它叫过来,用完后再按个键让它离开。但这样做必须能够匹配到那个窗口;我还有些放 mosh 会话的终端,我可不希望它们也跟过来。

又尝试了 Xfce 的终端。这个长得和 gnome-terminal 已经比较像了。看看命令行选项,能设置 role。这个足够用了。配置下字体、颜色什么的,再去改改 Awesome 的配置就可以用了。途中还修整了下run_or_raise函数的匹配逻辑。

PS: GTK 3 更新到 3.4 后,UI 发生了很大的变化,如:

  • 数字输入框的加减号变大了,更占地方,点击时也需要移动更远的距离了;
  • 滚动条变窄变丑了,点中它更难了;
  • gnome-terminal 的颜色设置比 Windows 的取色器还难用了;
  • 好端端的复选框不用,非得弄成滑块,这个是要让用户练习鼠标的拖动操作么?
Category: Linux | Tags: GNOME gtk 终端 linux awesome
4
2
2012
0

Linux Multitask Mode

今天,哦不,已经是昨天了,看到 Google 的「Chrome Multitask Mode」。觉得挺好玩,于是亲自试了试——当然不是在 Chrome 里,而是在 Linux 里。准确地说,是在 X Window 里。我给系统弄出一对鼠标指针来了 ;-)

先看看我有哪些输入设备:

xinput --list
⎡ Virtual core pointer                          id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad                id=12   [slave  pointer  (2)]
⎜   ↳ TPPS/2 IBM TrackPoint                     id=13   [slave  pointer  (2)]
⎜   ↳ USB Optical Mouse                         id=9    [slave  pointer  (2)]
⎣ Virtual core keyboard                         id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Video Bus                                 id=7    [slave  keyboard (3)]
    ↳ Power Button                              id=8    [slave  keyboard (3)]
    ↳ Integrated Camera                         id=10   [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard              id=11   [slave  keyboard (3)]
    ↳ ThinkPad Extra Buttons                    id=14   [slave  keyboard (3)]

一个鼠标指针不够,我还要一个:

xinput --create-master xxx

现在屏幕上就多了个鼠标指针了。但是它还不能动,因为没有对应的设备。那好,我的 USB 鼠标,你去控制另外个指针吧:

xinput --reattach 9 "xxx pointer"

Voilà!两个鼠标指针都能用了哦~

不过,这样做还是有些缺点的。比如,它会让火狐等程序迷惑不解,还是当成只有一个鼠标。在火狐中,鼠标的形状总是一样的,并且,不能同时触发两个不同的 onhover 事件。再比如,没有相关联的键盘,所以这个「新的」鼠标指针没办法配合键盘工作的,比如按住某键再点击什么的就不行。

好了,玩完了,还是回到一个鼠标指针的模式吧:

xinput --reattach 9 "Virtual core pointer"
xinput --remove-master "xxx pointer"

参考文章

Category: Linux | Tags: linux X Window X window
3
18
2012
4

使用 gnokii 读取 3G 网卡的短信

使用 gnokii 读取 3G 网卡短信的方法ArchWiki上有写。安装 gnokii 后复制配置文件并将自己添加到uucp用户组中:

cp /etc/gnokiirc ~/.config/gnokii/config
sudo gpasswd -a `whoami` uucp

然后修改下配置文件,主要是port = /dev/ttyUSB0model = AT这两处。用户组的修改要下次登录时才生效,或者使用newgrp命令来登录到uucp组。据说此命令在某些 shell 里是内建命令,不过在 bash 和 zsh 里没有,只能调用外部命令,所以会开启一个新的 shell。

newgrp uucp

现在就可以读短信了。接收新信息并存储,可使用命令

gnokii --smsreader

完事之后按Ctrl-C中断。要把短信读出来,gnokii 可以把短信存储为 mbox 格式。这是一种邮件格式,使用 mutt 即可阅读。-f后边的sms即是要保存的文件名。注意,gnokii 仍会将消息输出到终端。ArchWiki 上说的是使用 xgnokii GUI 程序来读取,但是我没有找到这个程序(只有 manpage)。

gnokii --getsms SM 0 end -f sms

对于中文短信,这样会导致乱码。所以我写了个 Python 脚本来处理。三件事:一是将内容编码标识为 UTF-8,二是把按字节截断的邮件主题(短信正文的前若干个字节)最后几个无效的编码替换掉,三是将邮件主题按标准进行编码。这些事 Python 处理起来挺容易的 ;-)

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import os
import sys
import email.header

for i in os.fdopen(sys.stdin.fileno(), encoding='utf-8', errors='replace'):
  if i.startswith('Subject: '):
    s = i[9:-1]
    print('Content-Type: text/plain; charset=utf-8')
    print('Subject:', email.header.Header(s, 'utf-8').encode())
  else:
    sys.stdout.write(i)
Category: Linux | Tags: LInux Python 网络 mutt
3
2
2012
6

在 fcitx 中切换国标与传统引号

国家标准使用这些引号:‘’“”,而我发现传统中文的引号更漂亮:「」『』。我切换到传统引号已经有一段时间了,但最近发现有时还是需要使用国标引号,而 fcitx 的现任开发者认为不需要加入该切换功能。好在 fcitx 的配置文件都是文本,又有 fcitx-remote 工具,所以自己很容易地手动实现了两个版本的——Haskell 版本纯粹是练习用,因为没有扩展路径中的~的现成函数,所以只好自己找了个实现,代码有些长。

import Control.Applicative ((<$>))
import System.Cmd (rawSystem)
import System.Directory (getHomeDirectory)
import System.Posix.User
import qualified Data.Text as T
import qualified Data.Text.IO as TIO

main = do
  file <- getFile
  TIO.readFile file >>= (TIO.writeFile file) . (T.map (trChar "“”‘’『』「」" "『』「」“”‘’"))
  reloadFcitx

getFile :: IO FilePath
getFile = expandUser "~/.config/fcitx/data/punc.mb.zh_CN"

reloadFcitx :: IO ()
reloadFcitx = rawSystem "fcitx-remote" ["fcitx-remote", "-r"] >> return ()

expandUser :: FilePath -> IO FilePath
expandUser "~"         = getHomeDirectory
expandUser ('~':'/':p) = fmap (++ "/" ++ p) getHomeDirectory
expandUser ('~':up)    = let (u, p) = break (== '/') up
                             in fmap (++ tail p) (homeDirectory <$> getUserEntryForName u)
expandUser p           = return p

trChar :: [Char] -> [Char] -> Char -> Char
trChar from to ch = case i of
                         Just i -> to !! i
                         _      -> ch
                         where i = elemIndex ch from

Python 版本就很简洁了:

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import os

m = str.maketrans('“”‘’『』「」', '『』「」“”‘’')
file = os.path.expanduser("~/.config/fcitx/data/punc.mb.zh_CN")

c = open(file).read().translate(m)
open(file, 'w').write(c)
os.execvp('fcitx-remote', ['fcitx-remote', '-r'])
Category: Linux | Tags: fcitx Haskell python
2
28
2012
8

拯救分区表

今天,因为弄错了磁盘顺序,不小心把移动硬盘的前 40M 左右的数据覆盖掉了。所幸发现的时候移动硬盘还接在电脑上,除了第一个分区外,后边的还可以正常挂载和读取,这说明内核还记得分区表。但是fdisk已经读不到 MBR 了。

怎么办呢?我不想把几百 G 的数据拷一遍,而且理论上肯定是能够把分区表给完整的找回来的——内核不是还记得么?上网只搜到/proc/partitions这个文件,其中的内容如下:

major minor  #blocks  name

   8        0  312571224 sda
   8        1   52436128 sda1
   8        2   52428800 sda2
   8        3          1 sda3
   8        5     512000 sda5
   8        6   41943040 sda6
   8        7  161281024 sda7
   8        8    3964928 sda8
  11        0    1048575 sr0
   7        0      71680 loop0
   8       16  312571224 sdb
   8       17    1048576 sdb1
   8       18  209715200 sdb2
   8       19   10485760 sdb3
   8       20   91320320 sdb4

只有分区的大小信息,而且单位是。我按移动硬盘的大小推算了下,这里的块大小是1KiB(关于块大小,真够混乱的。ls默认的也是 1K,但是dd却是 512B)。

光知道了块大小不行啊。我先试了试著名的 testdisk 工具。它搜索了好久,最终只找到了两个分区,于是被我否决了。又继续找分区的更多信息。/proc下看完了,我又去不怎么了解的/sys下看,发现其下有个block目录,里面正是系统已经识别的块设备!

进到出事故的sdb下,再进入sdb1ls一下,看到sizestart都在呢!cat出来各是一个整数。经过一番猜测和计算,可以确定其单位是512B,也就是一个扇区

好了,可以开始重建分区表了。当然,我可不想手工去算和写那64字节的二进制数据。试了试 Arch 安装时所用的cfdisk。它有基于文本的图形化界面,比较友好。可是新建分区时才发现只能输入以1000进制MB为单位的大小,而我需要分毫不差的按原大小分区。只好退出,试试文本交互的fdisk。在不断地按m键查看帮助的情况下,终于把分区重建好了:

  Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048     2099199     1048576   83  Linux
/dev/sdb2         2099200   421529599   209715200   83  Linux
/dev/sdb3       421529600   442501119    10485760   83  Linux
/dev/sdb4       442501120   625141760    91320320+   7  HPFS/NTFS/exFAT

前面几个的大小比较整,我是按+1G等这样输入的。这里要注意下的是,G, M, K等单位是1024进制,而GB, MB, KB等单位是1000进制。最后那个给 Windows 留的 NTFS 分区不知道为什么并不是在磁盘的最后一个扇区结束的,我输入的是/sys/block/sdb/sdb4/size里写的大小。

p查看并确认分区表正确后,按w写入。然后使用partprobe命令通知内核更新分区表信息。

这步做完后,后边的三个分区就安全了。第一个分区是一些启动文件,我已经打算重新弄一遍了。实际上fsck.ext2跑完后也只是在lost+found里出现了一堆垃圾文件。mkfs.ext2重新格式化,却在安装 grub2 时遇到了问题。大致的错误信息是这样的:

/dev/sdb appears to contain a iso9660 filesystem which isn't known to reserve space for DOS-style boot.  Installing GRUB there could result in FILESYSTEM DESTRUCTION if valuable data is overwritten by grub-setup (--skip-fs-probe disables this check, use at your own risk)

中文消息是:

/dev/sda 中似乎包含一个不为 DOS 引导保留空间的 iso9660 文件系统。在此处安装 GRUB 可能导致 grub-setup 覆盖重要数据从而损坏文件系统(--skip-fs-probe 参数可以禁用这个检查,使用该选项风险自负)

这个「iso9660」文件系统就是我误dd过去的。使用grub-setup并加上--skip-fs-probe参数后依旧出错:

warn: Attempting to install GRUB to a disk with multiple partition labels or both partition label and filesystem.
error: embedding is not possible, but this is required for cross-disk install.

中文消息是:

警告:正在试图将 GRUB 安装至有多个分区标签的磁盘,或同时有分区标签和文件系统的磁盘。这样的操作尚未被支持。
错误:无法嵌入,但在跨盘安装时是必须的

加上--force参数也没有用。我尝试消除前446字节的数据,亦没有用。后来想起在使用fdisk分区时,第一分区的起始扇区必须大于等于2048。难道是这些扇区中的内容影响了 grub2 的安装?head -c 1024 > a然后用 bviplus 查看,发现果然如此,都看到那个已经不完整的 iso9660 文件系统的卷标了。果然给它 dd 掉:

sudo dd if=/dev/zero of=/dev/sdb seek=1 count=2047 bs=1b

再次尝试安装,一切顺利!

Category: Linux | Tags: linux 失误 数据恢复
2
23
2012
8

迁移到64位 Arch Linux

曾经因为不了解,所以一开始选择了32位系统;后来内存大了,ArchLinux 的内核没有编译PAE支持,只能用 3GiB 多的内存,不爽。朋友又给了这篇教程,于是略作准备就开始动手了。

可是,但我真正动手时,那篇文章已经被另一篇英文教程取代了。当时忘记了 MediaWiki 会记录所有的编辑历史,所以只好将就着看了。结果,重启时 udev 没跑起来,说找不到 librt.so。我当然不会就此罢休,花费了一些时间,不仅拯救了系统,成功迁移到了 64 位,而且确定了 ArchWiki 上那篇教程的错误。

先说这个错误吧。现在已经修正了。方法 2: 从正在运行的系统一节,在安装 busybox 的时候,现在是一个红色的警告框,暗示着这里曾经发生的悲剧——

警告: 不要现在安装 lib32-glibc 软件包。在执行命令 ldconfig 后,当你安装 linux(内核)时,生成的镜像文件中,librt.so 等库文件会在 /usr/lib32 目录下,启动的时候二进制文件不会在此搜索库文件,导致启动失败。

当悲剧发生后,我有些紧张地拿出移动硬盘,先是进入移动硬盘上的 Arch,胡乱安装了几个 64 位软件包并重新生成 initramfs,结果连 init 也执行不了了。无奈我又启动64位内核的 PartedMagic chroot 进去查看。但是,chroot 失败了:

sh: cannot open shared object file

后来我才知道,因为我安装lib32-glibc/lib/ld-linux.so.2发生冲突,我覆盖了;后来卸载 32 位的 glibc 时,它被删除了。于是,动态链接的 32 位程序没有动态库加载器了。但其实还是有办法的,因为被误删的只是个软链接。

/usr/lib32/ld-linux.so.2 /bin/ls

老猫的提示下,手动指定ld-linux.so.2运行成功。我尝试把它链接过去,这个 32 位与 64 位库混合的系统开始有些可用了。接下来按 Wiki 里的指示操作就可以了。不过,我没有重装全部的库,而是只装了标明 i686 架构的库。这就是我之前那个用 Haskell 写的脚本的目的。不过还是出了点小问题——虽然我为防止程序运行不了而在开始之前把软件包列表生成了一份保存起来,但我忘了我的 HOME 是加密过的。PartedMagic 里没有 eCryptfs 工具,而 Arch 里的那个又因为库的原因运行不了,囧死了。。。幸运的是,我在迁移之前把 Dropbox 弄好同步了一遍,这个软件包列表也同步了。通过 Dropbox 网页界面下回来就好了。

弄好重启之后,整个事情还没完——我从 AUR 里编译安装的那些包还没重装呢。边重装边研究新的 64 位系统,发现 gcc 还是可以编译出 32 位程序的,只是要装gcc-multilib而已。现在可好了,既可以运行 64 位的程序,又可以运行 32 位的。库依赖少的 C 程序编译个 32 位的出来也没问题。

对了,最后说一句,vnstat 这厮的数据库格式竟然是平台相关的!换成 64 位后它就不断报错,直到我删除了以前的数据库。

Category: Linux | Tags: arch linux
1
28
2012
5

使用 eCryptfs 加密主目录

本文根据回忆记述在 Arch Linux 上为某一新用户建立使用 eCryptfs 加密的 $HOME 目录并使之在登录时自动解密挂载的过程。大量参考了 Unknown and partly hidden 的 eCryptfs and $HOME 一文。

依赖的软件包:ecryptfs-utils。

加密目录

# mkdir -p /home/.ecryptfs/user/private
# chmod 755 /home/.ecryptfs
# chmod -R 700 /home/.ecryptfs/user
# chown -R user:user /home/.ecryptfs/user
# ln -s /home/.ecryptfs/user/private /home/user/.private
# chmod 700 /home/user

注意:最后一步原文使用的是500权限,这里改成了700

第一次挂载加密目录:

# mount -t ecryptfs /home/user/.private /home/user

eCryptfs 会询问一些加密的选项,其中 Cypher(加密方法)和 Key byte 可自行选择:

Key type: passphrase
Passphrase: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Cypher: twofish
Key byte: 32
Plaintext passtrough: yes
Filename encryption: yes
Add signature to cache: yes

一定要记住密码,虽然可能并不怎么会用到。

mount命令的输出中找到这次挂载使用的参数,经过一些变更,把类似于以下的设置添加到/etc/fstab中:

/home/user/.private /home/user ecryptfs rw,user,noauto,exec,ecryptfs_sig=XYZ,ecryptfs_cipher=twofish,ecryptfs_key_bytes=32,ecryptfs_passthrough,ecryptfs_fnek_sig=XYZ,ecryptfs_unlink_sigs 0 0

注意:在登录挂载时,noexecnosuidnodev将会是默认选项。这里加上exec选项来覆盖掉noexec,这样加密的 $HOME 中才支持执行可执行文件。

挂载时生成了/root/.ecryptfs目录。我们先在里边保存些文件:

# touch /root/.ecryptfs/auto-mount
# ecryptfs-wrap-passphrase /root/.ecryptfs/wrapped-passphrase
Passphrase to wrap: [输入加密口令]
Wrapping passphrase: [输入用户的登录口令]

现在,使用用户的登录口令可以从文件/root/.ecryptfs/wrapped-passphrase中得到 eCryptfs 的加密口令。即使加密口令很强,如果登录口令弱的话,文件信息还是会泄漏的。所以,得选个强的登录口令,不然就不要玩登录时自动挂载加密 $HOME 了。

或者,你也可以玩点有趣的,把这个wrapped-passphrase文件放在 U 盘里,只留下一个指向 U 盘里的此文件的软链接。然后配置好 U 盘自动挂载,就做成了个简单的「U 盾」!

好了,现在卸载 $HOME:

# umount /home/user

自动挂载

先把 eCryptfs 的那个在/root下的目录弄回来:

# mv /root/.ecryptfs /home/.ecryptfs/user
# chown -R user:user /home/.ecryptfs/user/.ecryptfs
# ln -s /home/.ecryptfs/user/.ecryptfs /home/user/.ecryptfs

接下来,创建一个挂载用的脚本,暂时叫它/home/profile.sh吧。它将被写到用户登录时的自动执行脚本中,如~/.profile,或者~/.zprofile,如果你用 Zsh 的话。

if [ -r "$HOME/.ecryptfs/auto-mount" ]; then
  grep -qs "$HOME ecryptfs" /proc/mounts
  if [ $? -ne 0 ]; then
    mv $HOME/.Xauthority /tmp 2>/dev/null
    mount -i "$HOME"
    cd "$HOME"
    mv /tmp/.Xauthority $HOME 2>/dev/null
    (
      systemctl --user daemon-reload
      systemctl --user default
    ) &
  fi
fi

注意到这里加入了对~/.Xauthority文件的处理,不然从图形界面登录时,执行挂载命令后,会因授权文件不见了而失败。之前把 $HOME 的权限设置成700也是为了这个。单纯地允许写~/.Xauthority不行,因为 xauth 需要创建临时文件以防止此文件同时被多个进程修改。

现在,我们需要在用户登录时自动 unwrap 之前创建的那个wrapped-passphrase文件。在/etc/pam.d/login中添加几行(注意顺序):

#%PAM-1.0
#...
auth                required        pam_unix.so nullok
auth                required        pam_ecryptfs.so unwrap
#...
password            required        pam_ecryptfs.so
#password           required        pam_unix.so sha512 shadow use_authtok
#...

好了,我们先手动试试:

# su user
$ ecryptfs-insert-wrapped-passphrase-into-keyring /home/user/.ecryptfs/wrapped-passphrase
Passphrase: [输入用户密码]
$ mount -i /home/user

如果正确挂载的话,接下来就可以开始建设你的新 $HOME 了,比如把你以前的各种文件复制过去,等等。注意不要在加密的目录内进行 BT 下载哦。你可以建立个/home/.ecryptfs/user/public目录然后软链接到 $HOME 内来使用。

我同时还修改了/etc/pam.d/slim,似乎这样才能在使用 slim 登录时也有效。

呃,还没有结束呢。得把之前的/home/profile.sh文件弄进来。这里演示时只是创建了一个新的.profile文件。如果你已经有了此文件的话,一定不要将其加密,而要将其与此挂载脚本合并。它只能不加密,否则挂载后会出现两个.profile(一个加密了的,一个未加密、passthrough 来的),从而导致一些问题。

# umount /home/user
# chmod 600 /home/profile.sh
# chown user:user /home/profile.sh
# mv /home/profile.sh /home/.ecryptfs/user/private/.profile
# ln -s /home/.ecryptfs/user/private/.profile /home/user/.profile

好了,到此一切结束。

Category: Linux | Tags: linux 安全
12
28
2011
11

利用脚本提升 Wine QQ 登录体验

我从某处下载的QQ2010,其它都好,就是登录时焦点在密码框时,QQ就会崩溃。解决办法是使用QQ自带的软键盘输入密码。但在这个「半字母顺序」排列软键盘上找需要的需要实在费事。作为一名 Linuxer,我自然得想办法将其自动化。

很久之前就已经看到这个Xpresser软件,但可惜的是,它在Arch下跑不起来。但我从中学到了简单的图像匹配,再加上自己对 Xtest 的了解,解决方案呼之欲出。

本来是三个月前就打算写篇文章的,因各种原因迟迟未写。现在因为各种原因再次折腾这家伙,还是写出来分享一下吧。使用OpenCV做图像匹配部分我就不写了,有兴趣的自己去看 Xpresser 或者 winterpy 中的代码。

首先,介绍一下依赖。本脚本依赖众多的东西,其中我自己写的部分在 winterpy 里有,主要是 OpenCV 图像匹配,以及之前写过的 Xtest 调用使用 GDK 截图。最终,我利用它们写成了 xauto.py 库,功能还十分欠缺,但自动登录Wine QQ足够了,因为我做这些的主要目的就是这可恶的QQ。

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import os
import sys
from xauto import XAuto, Image

QQNo = 'YourQQNo'
QQPwd = 'YourQQPassword'

def main():
  if os.fork() == 0:
    if os.fork() == 0:
      os.execlp('rwine', 'rwine')
    else:
      sys.exit()
  os.chdir(os.path.split(sys.argv[0])[0])

  rect = (20, 150, 500, 500)
  xa = XAuto()
  w, h = xa.screensize
  target_w, target_h = 500, 300
  w, h = w - target_w, h - target_h
  w, h = w // 2, h // 2
  center = (w, h, target_w, target_w)
  xa.default_rect = center

  xa.find_and_click('ok.png', repeat=10) or sys.exit('click 确定')
  xa.find_and_click('qq.png', repeat=10) or sys.exit('find qq no input')
  xa.wait(1)

  for k in QQNo:
    xa.key(k)

  xa.wait(0.4)
  pwd_pos = xa.find('input_pwd.png')
  xa.click(pwd_pos)
  caps = Image('caps.png')
  xa.wait(0.4)
  for ch in QQPwd:
    xa.find_and_click('%s.png' % ch) or sys.exit(2)
    xa.wait(0.1)
    xa.find_and_moveto(caps)
    xa.wait(0.1)
  xa.moveto(pwd_pos)
  xa.wait(0.4)
  xa.find_and_click('login.png')

if __name__ == '__main__':
  main()

几点说明:

  1. 执行以下命令禁止QQ记住用户信息,这样再次启动时焦点会在输入QQ号的地方而不是会导致崩溃的密码框。如果你使用我给的压缩包的话应该可以跳过。
    rm -rf Users/All\ Users
    mkdir Users/All\ Users
    chmod -w Users/All\ Users
    
  2. 需要 wine 1.3.32 或更低,以及 wine_gecko 1.3 或更低。新版本在调用 IE 的组件进行显示时会崩溃,这包括「消息管理器」、「查看聊天历史」、「聊天窗口」的侧栏等。
  3. 我执行的是自己包装过的具有隐私保护功能的「rwine」程序。不过也不是特别安全,QQ仍能够访问剪贴板、截图等。
  4. 密码当然是明文保存。你觉得有必要折腾的话可以自己修改。
  5. 程序中需要的图片自己截。应该很容易知道应该截哪里。这样也避免了字体不同导致图像匹配失败。
  6. 此版本的 QQ 可以在这里下载:115 网盘

另注:更简洁好用的 TM2009 没有 wine 成功,登录时弹出错误


2014年3月25日更新:TM2009 以及 TM2013 后来均 Wine 成功了,并且在输入密码时不会崩溃。详情见此文

Category: Linux | Tags: python QQ wine 腾讯

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com