8
26
2021
5

倾听蓝牙耳机的按键事件

缘起

我的蓝牙耳机有简单的多媒体按键:上一曲、下一曲、播放、暂停。这几个按键在 Android 手机上是开箱即用的,然而在 Arch Linux 上,尤其是我的 Awesome 桌面环境上,并不那么自动。

其实按键事件都能收到的啦。可以收到 XF86AudioPrev, XF86AudioNext, XF86AudioPlay, XF86AudioPause 这么几个按键。给它们绑定热键,去调用 MPRIS 就好了。我使用的是 playerctl 工具。mpv 的 MPRIS 支持用的是 mpv-mpris。火狐自动就支持了,不用做什么。

看起来这样就好了?我也以为如此,直到我离电脑远了一些……

问题

躺在床上玩手机时也可以用电脑听歌啦~你问我为什么不用手机听歌?因为我的电脑没有 NFC 功能,耳机切到手机碰一下就可以了,可是切到电脑上是要打命令抢连接的!所以就不切来切去啦,反正手机上的曲库和电脑上是同步的。

可是!耳机多媒体按键怎么不管用了呢?我瞟了一眼电脑,哦,它怎么屏幕还亮起来了?反复几次之后,我终于搞明白了——锁屏的时候按键事件全被锁屏软件给挡下来啦……

那怎么办呢?

我有看到 acpid 那边也收到了些事件,比如「cd/prev / CDPREV」和「cd/next / CDNEXT」。但是不是很稳定,时有时无,也没看到播放和暂停。再加上从作为系统服务的 acpid 将指令传到用户会话比较麻烦,就放弃了。

后来想到,既然能收到按键事件,那么应当有个输入设备在。xinput 看了一下,果然有个「WH-1000XM2 (AVRCP)」,用 evtest 在 /dev/input/ 也能找到对应的设备文件。那直接读这个设备文件不就好了?

解决

好是好,但是没权限啊。不过像/dev/video*之类的文件就有权限,是 systemd 拿 udev 规则给加上的。我之前也给 i2c 设备加过权限,只是那次是直接 chmod 了,这次想试试更优雅的方案——uaccess tag。

这个 uaccess tag 是 systemd 用来给当前会话的用户设备权限的,切换用户会话的时候权限会自动变化。不过没有文档 QAQ,所以只好自己研究了。最终的结果是这样:

SUBSYSTEM=="input", ATTRS{name}=="WH-1000XM2 (AVRCP)", TAG+="uaccess"

这个规则的序号需要小于70,不然赶不上处理 uaccess 的逻辑。sudo udevadm control --reload-rules 然后再 sudo udevadm trigger,就可以看到对应的 /dev/input/event* 文件上已经有 ACL 给我的用户权限了。不过多了写权限,问题不大。

然后就可以开始愉快地找设备文件、读取事件啦。我用 Rust 写的,日常练习嘛,顺便用用前不久看到的 eyre 和 tracing。有个 input-linux 库,不用自己拿 libc 调用 ioctl、定义 C 结构体了。不过这个库不支持从按键名到按键枚举值的转换,所以我 fork 了一下。蓝牙耳机说来来、说走走,所以 inotify 也是少不了的啦。然后还用 toml 整了个配置文件,好放出来给有需要的人用~

啊对了,程序里一上来就把对应的输入设备用 xinput 给禁用了。这样桌面环境就不会收到事件,不会唤醒屏幕,也不会有重复操作了。(不过它退出的时候并没有把设备重新启用,懒~)

代码

Category: Linux | Tags: 硬件 linux Rust
8
12
2021
10

使用 bwrap 沙盒

bwrap 是命令的名字。这个项目的名字叫 bubblewrap。它是一个使用 Linux 命名空间的非特权沙盒(有用户命名空间支持的话)。

我之前使用过 Gentoo 的 sandbox 工具。它是 Gentoo 用于打包的工具,使用的是 LD_PRELOAD 机制,所以并不可靠。主要用途也就是避免打包软件的时候不小心污染到用户家目录。

使用 bwrap 的话,限制是强制的,没那么容易绕过(至于像 Go 这种因为不使用 libc 而意外绕过就更难得了)。不过 bwrap 不会在触发限制的时候报错。

bwrap 的原理是,把 / 放到一个 tmpfs 上,然后需要允许访问的目录通过 bind mount 弄进来。所以没弄进来的部分就是不存在,写数据的话就存在内存里,用完就扔掉了。这一点和 systemd 也不一样——systemd 会把不允许的地方挂载一个没权限访问的目录过去。

bwrap 的挂载分为只读和可写挂载。默认是 nodev 的,所以在里边是不能挂载硬盘设备啥的。它也提供最简 /proc 和 /dev,需要手动指定。整个 / 都是通过命令行来一点点填充内容的,所以很容易漏掉部分内容(比如需要联网的时候忘记挂载 resolv.conf 或者 TLS 证书),而不会不小心允许不应当允许访问的地方(当然前提是不偷懒直接把外面的 / 挂载过去啦)。

至于别的命名空间,有 --unshare-all 选项,不用写一堆了。如果需要网络,就加个 --share-net(这个选项文档里没写)。没有别的网络方案,因为没特权,不能对网络接口进行各种操作。--die-with-parent 可以保证不会有残留进程一直跑着。

我目前的打包命令长这样:

alias makepkg='bwrap --unshare-all --share-net --die-with-parent \
  --ro-bind /usr /usr --ro-bind /etc /etc --proc /proc --dev /dev \
  --symlink usr/bin /bin --symlink usr/bin /sbin --symlink usr/lib /lib --symlink usr/lib /lib64 \
  --bind $PWD /build/${PWD:t} --ro-bind /var/lib/pacman /var/lib/pacman --ro-bind ~/.ccache ~/.ccache \
  --bind ~/.cache/ccache ~/.cache/ccache --chdir /build/${PWD:t} /usr/bin/makepkg'

以后应该随着问题的出现还会修改的。

其实我学 bwrap 主要不是自己打包啦(毕竟基本上都交给 lilac 了),而是给 lilac 加固。Arch 的打包脚本是 shell 脚本,所以很多时候不执行脚本就没办法获取一些信息、进行某些操作。唉,这些发行版都喜欢糙快猛的风格,然后在上边打各种补丁。deb 和 rpm 的打包也都是基于 shell 脚本的。而 lilac 经常通过脚本编辑打包脚本,或者从 AUR 取,万一出点事情,把不该删的东西给删掉了,或者把私钥给上传了,就不好了。所以前些天我给 lilac 执行 PKGBUILD 的地方全部加上了 bwrap。期间还发现 makepkg --printsrcinfo 不就是读取 PKGBUILD 然后打印点信息嘛,竟然不断要求读取 install 脚本,还要对打包目录可写……

另一个用法是,跑不那么干净的软件。有些软件不得不用,又害怕它在自己家里拉屎,就可以让它在沙盒里放肆了。比如使用反斜杠作为文件路径分隔符写一堆奇怪文件名的 WPS Office。再比如不确定软件会不会到处拉屎,所以事先确认一下。我以前使用的是基于 systemd-nspawn 和 overlayfs 的方案(改进自基于 aufs 和 lxc 的方案所以名字没改),不过显然 bwrap 更轻量一些。跑 GUI 的话,我用的命令长这样:

bwrap --unshare-all --die-with-parent --ro-bind / / \
  --tmpfs /sys --tmpfs /home --tmpfs /tmp --tmpfs /run --proc /proc --dev /dev \
  --ro-bind ~/.fonts ~/.fonts --ro-bind ~/.config/fontconfig ~/.config/fontconfig \
  --bind ~/.cache/fontconfig ~/.cache/fontconfig --ro-bind ~/.Xauthority ~/.Xauthority \
  --ro-bind /tmp/.X11-unix /tmp/.X11-unix --ro-bind /run/user/$UID/bus /run/user/$UID/bus \
  --chdir ~ /bin/bash

其实还可以用来给别的发行版编译东西,取代我之前使用 systemd-nspawn 的方案。bwrap 在命令行上指定如何挂载,倒是十分方便灵活,很适合这种需要共享工作目录的情况呢。以后有需要的时候我再试试看。(好像一般人都是使用 docker / podman 的,但是我喜欢使用自己建立和维护的 rootfs,便于开发和调试,也更安全。)

和 bwrap 类似的工具还有 SELinux 和 AppArmor。它们是作用于整个系统的,Arch Linux 安装会很麻烦,对于我的需求也过于复杂。Firejail 是面向应用程序的,但是配置起来也挺不容易。bwrap 更偏重于提供底层功能而不是完整的解决方案,具体用法可以让用户自由发挥。

Category: Linux | Tags: Arch Linux linux 安全

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com