12
12
2020
38

一次失败的 KDE 尝试

前些天尝试了一下 KDE 桌面环境,不过实在是没能用下去。

首先要说的是,KDE 桌面确实漂亮,非常养眼。设置项也挺多,可定制性还是挺不错的。只可惜问题同样很多。

首先是显示器缩放的问题。受限于 X11,KDE 只能设置一下全局的缩放比例。所以我们只好缩放显示器显示的画面。不幸的是,一向相当体贴的 KDE 此时却笨笨的,在使用 xrandr 设置好之后需要重启 plasmashell 来使其获取 xrandr 的设置更新

kquitapp5 plasmashell && kstart5 plasmashell

KDE 的设置项很多,分门别类地在「设置」应用程序中集中列出来,然而问题也由此产生:同时只会显示一个「设置」窗口。也就是说,我配置快捷键时,想去窗口管理器那边看一看,调整一点选项,就必须放弃我当前打开的快捷键视图,放弃我键入的搜索词,并且选择「应用」或者「放弃」更改,才能切换到另一个「设置」组件中去。即使从 krunner 里打开某个组件的设置,它也会找到并更新已有的窗口。

我知道 Windows 10 也是这么个「单任务」设置的风格。可 Windows 10 也没有这么多可以设置的地方呀。后来获知有个命令可以打开单独组件的设置窗口。很不方便。它被隐藏起来的原因是这种窗口不能返回到组件列表界面,会让用户困惑。可是,为什么我不能同时打开「设置」的不同组件的多个窗口?单独组件的窗口会让用户困惑,那就不要用单独组件的窗口就好了嘛。

KDE 桌面还有个问题:启动特别慢。登录进入界面要好久,启动一个程序,它的图标也要跳好久窗口才会出现。不知道它在干什么。我甚至怀疑它是为了展示启动动画而故意推迟界面的显示。

KDE 有提供丰富的桌面部件。我往副显示器上放了一些系统状态的监视器——CPU、磁盘、网络啥的。然后问题来了:我凑齐了四个部件刚好形成2x2的网格,可是我要怎么对齐它们呢?并没有对齐的选项,也没有吸附的功能。在我找到它使用的配置文件并手动修改之前,我只能用肉眼瞅。可计算机不就是用来做这种人不擅长而机器擅长的事情的吗?

终端我还是用 GNOME Terminal,因为有些特性(比如超链接)只有它支持。但又出现问题了:它启动之后,pin 它的任务图标,或者通过任务栏图标创建新实例均会失败。把它 pin 到任务栏上,需要从主菜单的右键菜单里操作。即使这样,启动之后终端窗口还是会位于新的图标,旧图标还是不对应任何窗口。后来查了一下,GNOME 的东西都没有主动支持启动通知,导致 KDE 很多时候只能猜测,而这次它猜错了。解决的办法是给 GNOME Terminal 的 .desktop 文件加上正确的 StartupWMClass 项。这其实不是 KDE 的问题,但也没办法。KDE 不想为别人擦屁股,GNOME 不在意自己的软件在别的桌面上的可用性。

不过 Qt 写的 flameshot 我就不知道是怎么回事了。具体情况不记得了,反正就是显示异常。好像是全黑吧。我没来得及 debug 这个。

最后,让我决定放弃 KDE 的点来了:我设置不了我需要的窗口管理快捷键

切换窗口,默认是 Alt-tab 的那个,我好不容易在「快捷键」设置里找到了添加更多快捷键的方式,但我发现除了 Alt-tab,我自定义的都不能连续切换窗口。按一下,切换一下,然后就切不动了,只能放开快捷键。后来了解到这是设置更新方面的问题,kwin_x11 --replace一下就有效了。

切窗口其实问题不大。问题大的是切显示器屏幕。两个功能:一、把焦点切到另一个屏幕;二、把当前窗口移到另一个屏幕上。

前者可以勾选「分隔屏幕焦点」选项,然后调整一下「阻止盗取焦点」的级别。我也不知道这个级别都是啥意思。「无」我能理解,「低」「中」「高」「终极」都是些啥?反正调整一下,确实可以把窗口焦点切换到另一个屏幕去了,除了鼠标不会跟着过去!另外测试过程中,有时候焦点会丢失——我不知道当前什么窗口获得了焦点,也不知道接下来谁会获得焦点。比 Mac OS X 里焦点跑到一个窗口也没有的 Finder 上还要神秘。

不过这个倒是可以自己写个脚本解决:使用 xrandr 获取屏幕的大小和位置,通过 X 的接口获取鼠标的位置并通过 Xtest 扩展来移动它,然后再用某个 X 的接口去设置窗口焦点——完全绕过 KDE 的功能。

然后我被另一个问题难住了——我怎么把窗口移到另一个屏幕上并且把焦点也移过去呢?使用文档匮乏的 kwin script 是可以把窗口移过去,然后我没能找到移动鼠标光标的 API。通过 X 是可以移窗口的同时移鼠标,但是我拿不到带窗口装饰的窗口位置信息。kwin 有一个 getWindowInfo 的 D-Bus 接口,但是它接收的那个 UUID 参数,我没找到获取的方法。

总结一下,KDE 对快捷键的支持并没有想像的那么好,尤其是多显示器的支持。快捷键的设置是通过图形界面来操作的,虽然直观但是对于大量快捷键的管理来说非常困难。而对于大显示器来说,通过快捷键来管理窗口是十分必要的——因为我更难肉眼找到我的鼠标光标去了哪里。

接下来,我打算一边忍受着 Awesome 3.5.9 的旧与 bug,一边尝试将 i3 改造成我需要的样子。

Category: Linux | Tags: KDE X Window 窗口管理器
12
6
2020
2

i3 的 scratchpad 处理逻辑

i3 有个东西叫「scratchpad」,和我在 Awesome 里用的 run_or_raise 功能有些类似。

我的需求是某些浮动窗口可以「招之即来,挥之即去」。上次尝试切换 i3 遇到的一大麻烦就是,我经常从终端里启动图形界面的程序,而启动完之后我得手动给我的终端找个地方放着。i3 不支持最小化,也只有十个带数字快捷键、可以快速访问的工作区,所以 scratchpad 很重要,但是它的行为我有些捉摸不定。

首先是 move scratchpad 这个命令。它会把当前 con(窗口或者容器)浮动、取消全屏,然后移到一个叫 __i3_scratch 的不显示的工作区。

然后是 scratchpad show 命令(动词放后边了)。如果没有指定条件,它有如下复杂的处理逻辑:

  • 检查当前窗口是不是去过 scratchpad。如果是,就把它丢回去。
  • 否则检查当前工作区是否有另外的 scratchpad 窗口。如果有,就给它焦点。
  • 否则检查其它工作区是否有另外的 scratchpad 窗口。如果有,就把它移过来。
  • 否则把 __i3_scratch 里最久没有「见到光」的窗口移过来。

如果指定了条件,那么这样检查匹配的窗口:

  • 如果窗口不曾去过 scratchpad,什么也不做。
  • 否则如果窗口去过 scratchpad 并且在当前工作区,就隐藏它。
  • 否则就把它移过去。

总结一下,就是「回去,或者回来」。虽然动作的名字叫「show」,但其实是一个类似于 toggle 的功能。它的麻烦之处在于:如果你有多个去过 scratchpad 的窗口,你很难控制出现的是哪个窗口。一个绕过这个问题的办法是,总是带条件地使用 scratchpad。另一个小麻烦是:没有办法在匹配的窗口已经显示的时候,不要把它隐藏掉——有时候我只是习惯性地呼叫我的终端,而不看它是不是已经在我面前了。

对于浮动窗口,i3 有很多奇怪的限制,或者说是未实现:

  • 不支持最小化
  • 浮动窗口也不能显示在平铺窗口之下(加上上一条,就是没办法暂时藏起来)
  • 不支持最大化(手动调整窗口大小无法自动适配显示器大小,也没有「恢复」一说)
  • 不支持显示在最上层(当你在 GIMP 里开了一堆图片需要局部对比时)
  • 有全屏窗口时不能显示浮动窗口(看视频无法临时使用浮动窗口查个单词啥的)
  • 切换窗口时,平铺窗口和浮动窗口是隔绝的(需要单独的快捷键来切换)
Category: Linux | Tags: X Window i3 窗口管理器
11
21
2020
10

HiDPI 配置记录

首先,我是用 X11 窗口系统的,不同屏幕分别设置肯定没戏。所以只好让笔记本电脑的屏幕迁就一下我的4K主屏啦,把笔记本屏幕缩放一下。算一下 scale 值:192 / 120 = 1.6。不是整数,会糊,可总比放大两倍的巨大界面要好。

xrandr --output eDP-1 --scale 1.6 --auto --output DP-2 --auto --pos 3072x0 --primary --fb 6912x2160

这里要注意的是,要指定--pos(或者--panning),不然会重叠;要指定--fb,不然鼠标可能会有部分区域去不了。

然后开始设置。本来我是尝试了一下 KDE 的,但因为我将在下一篇文章中写的原因而放弃,回到了 Awesome。不过也不是全无收获。我把 KDE 的配置方案拿过来用了。你想问怎么拿的?我 btrfs 的文件系统,做好快照再 rsync -n 对比一下它动了哪些文件就有了。

首先是 X11 的资源。在~/.Xresources里写上Xft.dpi: 192,然后xrdb -merge ~/.Xresources一下就好了。顺便再xrandr --dpi 192一下,听说有些程序会读这个。

然后是 GTK。GTK 2 就放弃吧,没办法。文字会按设置的 Xft.dpi 放大,图标啥的不会。GTk 3,要设置两个环境变量:

export GDK_SCALE=2 GDK_DPI_SCALE=0.5

前一个是把界面放大,后一个是把文字缩回去,因为文字已经按 Xft.dpi 放大过,不能再放大一次了。

再然后是 Qt。Qt4 早卸载干净了不用管。Qt5 嘛,也不用管。它自己会处理好。有个按不同屏幕缩放的环境变量QT_SCREEN_SCALE_FACTORS,效果跟 Windows 10 差不多的。但是我为了照顾其它程序已经把屏幕给 scale 过了,就不需要设置这个了。你要设置个QT_AUTO_SCREEN_SCALE_FACTOR=0也行,但这个是默认行为。

最后是个别的程序。

Telegram 直接在设置里关掉「默认界面缩放比例」并且设置缩放比例为 300% 就好了。我也不知道为什么,Telegram 默认的字总是很小。之前 120dpi 的时候我要 200% 缩放,现在 192dpi 需要 300% 缩放了。

YouTube,就是那个网站啦。它其实没什么显示上的问题,只是死活不会给我自动选择 1080p 以上的分辨率。经过仔细二分测试之后发现,把火狐的配置文件夹下的storage/default/https+++www.youtube.com目录删掉之后就好了。没发现删掉这个会有其它影响。

mpv 要修改配置文件,加上no-hidpi-window-scale参数,不然会把视频自动放大,4K视频一打开会只能看到四分之一的画面。加上这个参数,默认窗口大小时,一个视频里的像素会对应一个显示器上的像素,不大不小刚刚好。mpv 文档上说这是 OS X 系统上的默认行为,可我这是 Linux 桌面啊,你把别的平台上的习惯搬过来是几个意思?另外我加了个demuxer-readahead-secs = 20选项。我的大文件都在机械硬盘上,4K 码率又比较高,不多预读一点容易卡。

我的 qemu 之前使用的是-display gtk,也坏掉了。窗口那么大,虚拟机只用左下角那里四分之一的空间。spicy 也有问题,会告诉虚拟机只有 1080p。解决方法是 unset GDK_SCALE GDK_DPI_SCALE。它们在放大了自己的界面的同时,把显示的虚拟机的内容也给放大了,所以干脆叫它们别动。也没什么别的影响。

哦还有 Zoom。设置个QT_AUTO_SCREEN_SCALE_FACTOR=1似乎就好了?我试了一下QT_SCREEN_SCALE_FACTORS,会导致很怪异的行为。

以上解决了显示大小的问题,但我发现还有个问题:我的鼠标光标时大时小的……从 KDE 那边弄来几个设置之后就好了,而且主题也更加一致了呢。

首先是设置 xcursor 环境变量:

export XCURSOR_THEME=Vanilla-DMZ XCURSOR_SIZE=36

听说对应的 X 资源大家都不理睬,那我也就不设好了。

然后是 GTK 2 的~/.gtkrc-2.0文件里写上:

gtk-cursor-theme-name = "Vanilla-DMZ"
gtk-cursor-theme-size = 36

再接下来是 GTK 3 的~/.config/gtk-3.0/settings.ini

[Settings]
gtk-cursor-theme-name = Vanilla-DMZ
gtk-cursor-theme-size = 36

然后又没了。天知道为什么 Qt 那边啥都不干就好好的,GTK 却这么麻烦。

啊,你问这些环境变量在哪里设?我给写~/.xprofile里了。不过这还不够。有些 GUI 程序会由用户的 systemd启动(比如我的 Telegram 是由 systemd 启动的,为了在内存用得太多的时候自动重启),有些 GUI 程序会由 D-Bus 激活(比如 gnome-terminal)。这些是和登录会话分开的,所以要手动导入一下。以下是我的 .xprofile 中导入图形界面相关环境变量的部分:

_envs=(
  GDK_SCALE GDK_DPI_SCALE
  XCURSOR_THEME XCURSOR_SIZE
  XMODIFIERS QT_IM_MODULE GTK_IM_MODULE
  LIBVA_DRIVER_NAME GST_VAAPI_ALL_DRIVERS
)
dbus-update-activation-environment "${_envs[@]}"
systemctl --user import-environment "${_envs[@]}"

至于登录界面怎么办,我是在 lightdm 的 display-setup-script 里,跑了跑 xrandr,设置了一下 Xft.dpi 资源。环境变量啥的没动,反正用不上。当然你也可以去改~lightdm/.pam_environment来设环境变量,反正现在 Arch Linux 还是读它的。别的 dm 同理。

Category: Linux | Tags: screen linux hidpi 显示器
11
20
2020
14

让 QEMU 使用 SPICE 协议

缘起

我就是买了个4K显示器,咋这么多事呢……(有两篇文章还在路上)

第一个问题是,我的显卡 vGPU 最高只支持 1920x1200 的分辨率。行吧,我缩放成了吧?嘿嘿,-display gtk 的缩放不会保持比例,我只好算了算最大保持比例的大小,然后窗口切成浮动,再调用命令调整到指定的大小:

sleep 1 && xdotool getactivewindow windowsize 3280 2122

然后还要居中放置一下。多麻烦!

第二个问题是,我想在虚拟机里试试 i3,但是我的按键总是会被外边捕获。Super 键基本上是 Awesome 在用,而 Alt 键会撞上这个 GTK 窗口菜单栏的快捷键。

配置

经过多番尝试和摸索之后,确定了如下的参数:

  -display egl-headless,gl=on,rendernode=/dev/dri/renderD128
  -spice unix,addr=/run/user/1000/qemu/ArchKDE/spice.sock,disable-ticketing
  -device virtio-serial-pci
  -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0
  -chardev spicevmc,id=spicechannel0,name=vdagent

当然要gl=on啦,不然我怎么玩特效,我还不如回到 vbox 去呢(啊不,我的 vbox 不喜欢 btrfs,经常出错然后只读挂载,所以已经被删掉了)。要指定rendernode,不然它会 fallback 到 llvmpipe,然后还段错误崩掉……这也是我不-display spice-app的原因。

后边三行是从这个 QEMU + Spice with Copy & Paste 抄来的。搜索问题时不小心遇见,然后解决了这个我一直没有处理的问题。SPICE 不但能共享剪贴板,而且还支持 PRIMARY 选择区呢~虚拟机里要安装 spice-vdagent 并启动相应的服务。

然后是客户端的选择。一个很神奇的地方是,virt-viewer 看上去轻量,实际上只是选项少而已。它不光拖进来个 gtk-vnc 依赖,还把 libvirt 都给我带上了……然后 GNOME 的 vinagre,我不知道为啥,它就是连不上我的 spice+unix 地址。哦对了,virt-viewer 直接敲命令调用也是连不上,只能用 xdg-open 才能正常打开。

virt-viewer 有个依赖叫 spice-gtk。我试着 pacman -Ql 了一下,还真找到个 spicy 工具。比 virt-viewer 轻量多了,选项也更为丰富,比如可以选择不 grab 键盘。

一点额外的东西

在群里听说了 virtio-fs 共享方案,听说比 virtio + 9p 更高效。然后我用它成功取代了之前用的 NFS 方案(反正我的 vbox 虚拟机已经被删掉啦)。NFS 的服务也可以卸载啦(开放一堆端口到公网,看着有点怕怕的,虽然是 IPv6 地址不太会被扫到,但知道我的地址的咋办呢)。

virtio-fs 相比 virtio + 9p 的另一个优点是,和 NFS 一样,virtiofsd 是以 root 权限运行的,所以可以写入我的 pacman 缓存。qemu 那个 9p 似乎没有办法。至于启动嘛,用 systemd abstract socket 触发一下就好了。

另外,我使用 GVT-g 和 virtio 输出视频信号时,均遇到了声音在视频画面变化时声音卡顿的情况。一个绕过的办法是,通过设置 PULSE_SERVER 环境变量以及加载 module-native-protocol-tcp 模块,将音频信号直接通过网络发送到宿主机上,一点也不卡!

Category: Linux | Tags: spice qemu kvm 虚拟机 linux
11
16
2020
3

Python 小版本升级是怎么 break 已有项目的

近日,Arch Linux 终于开始升级到 Python 3.9 了。很多人认为 Python 小版本升级容易搞坏兼容性,导致项目无法在新的版本上运行。事实是这样的吗?我正好借着 Arch Linux 升级 3.9 的机会,分析一下打包过程中失败的项目到底是出了什么事。

需要说明的是,我仅大致地分析了打包的报错信息,不排除分析出错,或者有额外的问题没有被看见的情况。另外我是在打包过程中随机(arbitrarily)取样,并且排除了我不能确定问题所在的案例。

以下项目测试失败是和 Python 3.9 相关的。排序是按照项目开发者的无辜程度排序的。也就是说,排序越靠前的,我越是认为项目开发者是无辜的;而像「硬编码 Python 3.9 为未发布的版本」这种完全不 future-proof 的做法,现在坏掉了真是自找的。

其中,使用的公开特性变化导致问题的有 3 个,调用私有属性或者方法、依赖非正式的文本信息的有 11 个,使用已废弃的特性的有 8 个,使用已被修复的 bug 的有 2 个,使用未来注定会出问题的信息的有 3 个。总共 27 个。

  • freecad: PyTypeObject.tp_print 没了
  • python-llfuse: PyTypeObject.tp_print 没了
  • linux-tools: PyMODINIT_FUNC 的变化导致了警告,然后被转为错误
  • python-blist: _PyObject_GC_IS_TRACKED不再在第三方库中可用(被公开 API 取代)
  • python-pyflakes: Python 语法解析报告的列位置似乎不太对,应该是受新的语法解析器的影响
  • python-pylint: Python 语法解析报告的列位置似乎不太对,应该是受新的语法解析器的影响
  • python-typing_inspect: 使用私有名称 typing._GenericAlias,结果新版本变成了 typing._SpecialGenericAlias
  • python-sphinx-autodoc-typehints: 看上去是类型标注相关的内部更改移除了 typing.Dict.__parameters__ 属性造成的
  • python-fastnumbers: 看上去是内部函数 _Py_dg_stdnan 不再被默认包含导致的问题
  • python-libcst: 类型标注相关的内部更改移除了 typing.Dict.__args__ 属性造成的
  • monkeytype: typing.Dict 的类型从 type 变成了 typing._SpecialGenericAlias
  • scrapy: 由于 typing.Optional[str] 的字符串表示由 typing.Union[str, NoneType] 变成了 typing.Optional[str] 导致 mitmproxy 运行出错,进而使得 scrapy 的测试失败
  • python-billiard: 调用的私有方法 _posixsubprocess.fork_exec 参数发生了变化
  • python-pytest-benchmark: argparse 的帮助信息格式有优化
  • python-opentracing: 自 3.7 起废弃的 asyncio.Task.current_task 被移除
  • python-engineio: 自 3.7 起废弃的 asyncio.Task.all_tasks 被移除
  • impacket: 自 3.2 起废弃的 array.array.tostring() 被移除
  • python-pybtex: 自 3.2 起废弃的 xml.etree.ElementTree.Element.getchildren 被移除
  • python-jsonpickle: 自 3.1 起废弃的 base64.decodestring 被移除
  • python-ioflo: 自 3.1 起废弃的 json.loads() 参数 encoding 被移除
  • routersploit: 自 Python 3 起废弃的 threading.Thread.isAlive 终于被移除了
  • python-socketpool: 自 Python 3 起废弃的 threading.Thread.isAlive 终于被移除了
  • python-furl: Python 3.9 修正了一处 URL 解析 bug
  • python-stem: Python 3.9 移除了错误的 unittest.mock.__version__
  • python-natsort: Python 的 Unicode 支持更新到了 13.0.0 版本,CHORASMIAN NUMBER ONE 字符被判定为数字,但是测试代码不认识,认为程序出错
  • python-pony: 对新版本的 Python 报不支持的错误
  • python-dephell-pythons: 硬编码 Python 3.9 为未发布的版本,但现在 3.9 已经发布了

而以下项目的测试失败与 Python 3.9 没有直接关系,共 26 个。其中与 Python 生态有关的有 18 个,与其它项目有关的有 4 个,依赖外部信息的有 3 个,包括一个特别搞笑的依赖夏令时是否生效的。

  • python-eventlet: 调用的 dnspython 私有方法已不存在;DNS 解析超时
  • python-markdown2: 语法高亮的结果有少许变化,不符合预期。推测是 pygments 新版本的变化
  • python-flake8-typing-imports: 似乎是 flake8 能够检测到更多的问题了
  • python-babel: 使用了已废弃的特性,测试被 pytest 拒绝
  • python-pygal: pytest 6.1.0 移除了 Metafunc 的 funcargnames 属性
  • python-flask-gravatar: 使用了已废弃的特性,测试被 pytest 拒绝
  • python-pytest-relaxed: 使用了已废弃的特性,测试被 pytest 拒绝
  • python-pytest-randomly 使用了已废弃的特性,测试被 pytest 拒绝
  • python-deprecated: 测试所预期的警告文本信息已经发生变化
  • python-dbus-signature-pyparsing: 执行时间超过了测试设定的 200ms 时限
  • python-tinycss2: flake8 风格检查未通过
  • python-pytest-runner: black 风格检查未通过
  • python-portend: black 风格检查未通过
  • python-aiohttp: @coroutine 的 DeprecationWarning 被视作错误
  • python-poetry: poetry-core 的一项数据由 dict 改为 OrderedDict,使得输出顺序与测试预期的不一致
  • python-isort: 将使用旧版本 isort 的外部项目的 import 排序视为正确,然后它还真出错了
  • python-cachecontrol: Python 2.7 相关
  • python-zc.lockfile: 测试代码把 Python 3 代码喂给了 Python 2.7。可能是该库已经不支持 2.7 了
  • python-occ-core: 依赖 OpenCASCADE 的版本更新,不被支持
  • protobuf: C 整型比较因表示范围问题而恒为假,警告转错误。是因为新版本的 gcc 比较聪明么?
  • gnome-passwordsafe: 构建系统发现有依赖缺失
  • io: C 代码引用了不存在的系统头文件
  • ceph: C++ 相关问题
  • python-distlib: 调用远程 XML-RPC 太多被限制导致预期的数据与实际错位
  • python-requests-toolbelt: 测试所需要的 HTTP 资源 404 了
  • postgresql: 夏令时结束,导致实际时区与预期对不上。「所以冬天就不要滚包啦,冬天要冬眠!」

所以在这些升级 Python 3.9 的项目中,不兼容 Python 3.9 仅仅只占一半,其中又有一半多属于「总有一天会坏掉」的类型(一大半属于「不听话」,使用没有明确文档、预期为私有的特性,少数尝试当预言家但是失败了)。最后剩下的,再一大半是使用了至少两个版本前已经说了要废弃的特性,只有三个莫名地发现自己真的被 Python 坑了,还都是 C API 部分的。

所以我对我自己的脚本顺利升级到 Python 3.9 非常有信心呢。可能有些老代码使用了已经废弃的特性,所以我也设置了环境变量 PYTHONWARNINGS=default,ignore::ResourceWarning 以便及时得到提示。

哦对了,Arch Linux 中受 Python 3.9 升级影响需要更新的软件包共有2077个,绝大部分我都没见着失败的。目前从开始升级到现在已经过去六天,还剩最后40个失败了的包。

10
29
2020
10

让 Arch Linux 系统和最新的镜像同步,从最快的镜像下载

Arch Linux 就是要追新!要追新自然要选择一个更新及时的软件仓库镜像啦,比如国内的 TUNA、USTC 同步都很及时。但是呢,这俩难兄难弟最近一段时间有些吃不消了,导致下载包的时候很慢,甚至超时失败,使用体验真糟糕。如果直接用上游镜像,比如 pkgbuild.com,漂洋过海的,也挺慢的。

而国内另一些镜像,比如网易腾讯云阿里云华为云,他们要么有 CDN,要么线路很好,下载速度飞快。但是呢,他们基本上每天才同步一次,阿里云还时不时连续数天都没能同步成功,这让喜欢追新的 Arch Linux 用户多不舒服呀。当群里的小伙伴们都用上了最新版本的软件,体会到了让人心痒痒的新特性和 bug 时,你 -Syu 却是「今日无事可做」,真是扫兴呢。

和最新的镜像同步,从最快的镜像下载,真的不可兼得吗?

非也。只需要稍微配置一下,用上我的 pacsync 脚本,就可以啦~

配置方式是,为 /etc/pacman.d 下的镜像列表文件创建一个.sync后缀的同名文件,里边指定用于同步的镜像,而不带.sync后缀的文件里按优先级列出多个镜像。pacman 在下载文件时,会按顺序依次尝试列出的镜像,如果遇到更新不及时 404 的时候,就会尝试另一个。这样,可以仅在下载快的镜像里还没有需要的包文件时,才转而从比较慢的镜像下载。

而需要同步 pacman 数据库的时候,使用pacsync脚本取代pacman -Sy。脚本会使用 bind mount 用.sync文件取代不.sync的版本,就能同步到最新的数据库了。原来的pacman -Syu命令要拆开来用,先pacsyncpacman -Su了。

脚本里使用了单独的挂载空间并且将挂载改为了私有,所以并不会影响到外边。

Category: Linux | Tags: Arch Linux linux
10
29
2020
3

tar 归档的权限问题

一次系统升级之后,我的许多 Python 程序突然开始报错:

[...]
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 2762, in _get_metadata
    for line in self.get_metadata_lines(name):
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1415, in get_metadata_lines
    return yield_lines(self.get_metadata(name))
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1405, in get_metadata
    value = self._get(path)
  File "/usr/lib/python3.8/site-packages/pkg_resources/__init__.py", line 1609, in _get
    with open(path, 'rb') as stream:
PermissionError: [Errno 13] Permission denied: '/usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/PKG-INFO'

WTF 滚坏了!立即回滚!

回滚完之后,我开始调查这个事件——因为 [archlinuxcn] 的这个包是我管的呀。而且我记得之前也遇到过一次类似的情况,当时没有深究。

检查一下软件包里的文件的权限:

>>> tar tvf python-telethon-1.17.4-1-any.pkg.tar.zst | grep PKG-INFO
-rw------- root/root      3659 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/PKG-INFO
>>> tar tvf python-telethon-1.17.4-1-any.pkg.tar.zst | grep -- ----
-rw------- root/root      3659 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/PKG-INFO
-rw------- root/root     12078 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/SOURCES.txt
-rw------- root/root         1 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/dependency_links.txt
-rw------- root/root        27 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/requires.txt
-rw------- root/root        15 2020-10-24 10:05 usr/lib/python3.8/site-packages/Telethon-1.17.4-py3.8.egg-info/top_level.txt

好奇怪,Telethon 这些包信息文件怎么只让 root 读了呢?

从 PyPI 上下载 Telethon 的原始 tar 归档回来看看,发现最近几个版本里,文件权限全部只有自己可以读(-rw-------),而所有者是 u0_a167/10167。开发者突然在 Android 系统上打包了呢……安装的时候,部分文件的权限被保留了下来(Arch Linux 打包时强烈反对使用 root 权限执行,因此我用 devtools 打包,解包部分自然是普通用户操作的,所有者无法被保留)。

然后我又看了一下之前的版本,哦豁,所有者成开发者的 id 了,但是有三个版本的 pyc 文件,还有好几个 pyc 文件都是 -rwxrwxrwx。大概系统上的低权限用户可以去改改,然后看谁跑 Telegram 机器人就拿谁的权限?

经过跟开发者的讨论,最终干掉了 pyc 文件,也不在 Android 上打包了。777 权限问题还待解决。不过我更在意的是,为什么会发生这种状况呢?setuptools 干嘛不修一修呢?别的工具创建的用于发布的 tar 归档会不会有类似的问题呢?

结果找了找,发现 setuptools 前年就有人报告这个问题,但是并没有解决。行吧,我打包时统一修正一下权限好了……

下一个 GitHub 生成的 tar 归档看看?咦,-rw-rw-r-- root/root,是处理过了么?啊对,git archive 生成的包是怎么样的?去试了试,原来一样的啊。看来 git 想到了这个问题并且处理了,只是 002 的 umask 有点意外。

Arch Linux 为了普通用户打出文件为 root 所有的 tar 归档使用了 fakeroot,那么 git 是怎么实现的呢?翻了翻代码,git 是自己生成 tar 文件的,写死了所有者是 root/root,但是权限位还是有专门的 umask,默认是 002。可以配置,比如git config --global tar.umask user一下,就会取当前 umask 作为 tar 归档里文件的 umask 了。

至于传统的 GNU autotools 构建系统创建的 tar 归档,我也创建了一个看了一下,并没有特殊处理,跟手动跑 tar 一样。

Category: Linux | Tags: linux python Git
7
22
2020
10

Linux 的环境变量怎么设

最近,Arch Linux 的 pam 包将更新到 1.4.0,然后因为一个字符的变化,不少中文用户都开始避难了:pam_env 将默认不读取用户的环境变量设置。许多中文用户使用~/.pam_environment文件来配置 fcitx 输入法所需要的那三个环境变量。更新之后,这些配置将不再生效,意味着他们可能无法输入中文了,于是大家热烈讨论现在要在哪里配置环境变量比较好。

当然了,在 pam 的配置文件里加上user_readenv=1就可以恢复原先的行为。只是,pam 的开发者这么改当然是有原因的:CVE-2010-4708。简单地说,就是在鉴权模块里设置用户指定的环境变量不安全,怕被提权。

2020年08月20日更新:Arch Linux 打包时添加了以上这个配置

当然啦,桌面用户完全可以去改系统级的环境变量设置,只要你愿意让你个人的配置信息跑到系统配置那边去的话。

正文

用户经常需要设置一些环境变量,比如输入法,比如语言和地区选项,比如自定义的 PATH,等等。然而 Linux 的环境变量又不像 Windows 那样有一个专门设置所谓「系统环境变量」的地方,而是所有环境变量全部由子进程继承父进程获得,这么一路继承下来的。于是,想要给所有想设置上的进程都设置上,就不是一件简单的事情了。

这里关注的是桌面用户,自然首先从图形界面说起。

使用 X11 的桌面环境,通常通过 display manager 来登录,比如 lightdm 和 sddm。这俩都支持 ~/.xprofile。这个文件会在启动过程中被 source,使用的 shell 是由 dm 自己确定的。lightdm 和 sddm 都是用的 /bin/sh(分别位于 /etc/lightdm/Xsession 和 /usr/share/sddm/scripts/Xsession 文件里)。可以看到,除了读取 .xprofile 外,lightdm 也会读取 .profile。sddm 甚至连 bash、zsh、tcsh、fish 的启动配置脚本都给读了。

对于 Wayland,我这儿只有 sddm,类似的,也是各种 shell 的都给你读了。(当然就不读 X11 相关的东西了。)

而对于手动 startx 的用户,~/.xinitrc 里自己写上呗。还有 VNC 啥的,也都可以自己看启动脚本找到方法。

没有图形界面的登录,通常是从 tty 或者 ssh 登录。这两者都是登录到命令行 shell。写在 shell 配置里就可以了。具体到 zsh,我是写到 ~/.zprofile,这样不管是否交互,只要是登录 shell 就执行,不登录的大概已经继承到了。另外有些情况可能写这儿也没有用,比如 cron 调用的时候。非要搞的话其实可以写到 ~/.zshenv,这个基本上所有的 zsh 都会读,只能被系统级设置或者命令行参数禁用掉。而如果是 bash 用户,.bash_profile .bash_login .profile 这仨,会读找到的第一个(sddm 也会按这个顺序找,但是 lightdm 只会找 .profile)。Arch Linux 现在默认会给新用户建立 .bash_profile,我建议 bash 用户把它改名成 .profile。这里有个图 ,大致上说明了 bash 和 zsh 不同情况下会读取的配置文件

systemd 用户实例也有一份环境变量,用于通过 systemd 启动的用户级服务,通过 systemctl --user 的子命令可以查看和更新。D-Bus 也有一份,可以通过 dbus-update-activation-environment 命令来更新。这俩通常不需要用户操心,桌面环境应该会维护好。

如果你用 tmux 的话,那么 tmux 还有一份,用于新创建的窗格。tmux 会在被连接时更新 update-environment 选项指定的环境变量。当然你也可以用 tmux setenv 子命令去更新。这个尤其值得注意,因为 tmux 可能在不同的环境中被连接,导致环境变量混乱(比如 X11 下没有 DISPLAY 变量)。

要注意的一点是,不同的登录过程并不是只会读取自己专属的配置文件,尽管大家都在努力,也并没有完全统一好用的配置文件。所以有些环境变量,你可能想尽量避免重复设置(比如冗长重复的 PATH 项就很讨厌)。好在这些都是 shell 脚本,不光能设置环境变量,也能做条件判断。

要查看当前进程的环境变量,可以用 env 命令。要查看别的进程的,去读它的 /proc/PID/environ 文件,或者开个 htop 然后在进程上按 e 键。要看看某个环境变量在不同进程里是什么情况,可以用我写的小工具 compare-env

Linux 软件生态的特点就是这样,没有谁规定一定要用怎样的方式做一件事情,所以大家的想法总会有些出入,就导致设置个环境变量还有这么一大群配置文件(systemd 还有一份呢,我还没读没试所以没说)。但另一方面,理解它们也是十分容易的:不用猜测,不用撞大运般地耗费时间去尝试,顺着代码摸过去,总能弄明白软件的行事逻辑。即使文档像 pam_env 那样前后矛盾,也有源代码这条路可以精确刻画软件的行为。

Category: Linux | Tags: linux 环境变量
6
9
2020
0

终端色彩总结

终端转义序列里有一个 SGR 代码用于表示「粗体或者增加强度」的字体渲染。相当多的终端将其实现作更明亮的颜色而非粗体字,也有很多终端应用程序如此(比如 mutt 中的 brightXXX 颜色使用此代码;新一点的 mutt 支持使用 lightXXX 来表示亮色了)。

然而最近,GNOME 终端决定将这个「或」字去掉,让 SGR 代码 1 表示粗体,亮色使用另外的颜色代码来表示。这无疑导致了我看到的许多软件里出现粗体字。有时候看着还行(比如 systemd、htop 中的),有时候会让人觉得很不舒服(比如 zsh 里打着命令,字体一会儿变粗一会儿变普通粗细)。我就认真去查了一下相关信息,作出相应的配置更改,顺便做个总结笔记好了。

转义序列

「选择图形展现」(Select Graphic Rendition; SGR)序列用于设置字体的渲染方式。其格式为CSI (code ";")* code m。CSI 就是 ESC "["。一个序列里可以给出多个代码,使用分号分隔。如果一个都不给出,当作代码 0 解释。

八色

这是最基础的八种颜色,代码 30-37 设置前景色,40-47 设置背景色。

16色

这就是大多数终端设置里,那个14色的调色板所指定的色彩了。为什么只有14色呢?因为默认的前景色和背景色是单独的设置项。

如序言中所说,相当多的终端将代码 1 解释为亮色,也就是上述八色再加上对应的明亮版本。

自 GNOME 终端此次改革之后,代码 1 只管粗体。要亮色,使用代码 90-97 设置前景色,100-107 设置背景色。除了 GNOME 终端外,也有一些其它终端跟进,比如 alacritty。有些终端保留了将代码 1 解释为亮色的选项,而 GNOME 终端最近去掉了这个选项(但是还有一个将粗体字显示为亮色的选项)。

256色

16色怎么够?于是出现了256色。

ESC[38;5;<n>m用于设置前景色,ESC[48;5;<n>m用于设置背景色。其中,n 是一个数,0-7 同代码 30-37,是那最初八种颜色。8-15 是它们的亮色变种。16-231 是216种彩色,在RGB空间构成了一个6x6x6的立方体。232-255 是24个灰阶色彩。

一些标准中,这中间的分隔符使用冒号。

真彩色

现代显示器普遍使用8位值表示RGB中每一个通道、共计24位了,终端也不能落后呀。于是有些终端也支持了这个被称为「真彩色」的24位色彩了。

代码格式是256色的扩充。ESC[38;2;<r>;<g>;<b>m来设置前景色,ESC[48;2;<r>;<g>;<b>m设置背景色。这里的 r、g、b 当然就是RGB色彩空间的值了,每一项的取值范围是 0-255。

测试脚本

整合了我从各处收集到的展示方式,写了一个脚本,可以直观地感受终端对各种色彩的支持情况:

colors.py

参考资料

Category: Linux | Tags: linux 终端 色彩
5
19
2020
2

桥接无线网卡!

众所周知,大部分无线网卡是不支持桥接操作的。

但是 VirtualBox 就是能,因为它做了特殊处理:来回改 MAC。

那么,我的 LXCnetnsKVM 啥的也想这么玩,成不?

实际上不仅能成,而且 Debian Wiki 还给出了两个方案。方案一是用 ebtables 来回改 MAC。不过我失败了,可能是 ebtables 不支持改完 MAC 再把包发往另外的网络接口吧。

方案二是内核的一个叫 Proxy ARP 的功能。设置起来超级简单:往/proc/sys/net/ipv4/conf/all/proxy_arp里写1,然后给需要的 IP 地址加一条 /32 路由项就可以了。

这方案相比起 VirtualBox 来是非常手动了,也不支持 DHCP 自动配置的 IP 地址,但好歹能用。至少微信备份能用。(火狐的 Wi-Fi 远程调试已经坏掉了,倒是那个「USB 调试」其实只要 adb 连接上就能用,不一定要走 USB 线。)

Category: 网络 | Tags: linux 网络 虚拟机

| Theme: Aeros 2.0 by TheBuckmaker.com