play with ventoy

准备使用 ventoy 制作一个多功能启动盘。

ventoy 简介

“Ventoy 是一个制作可启动 U 盘的开源工具”,项目主页上如是说。下载 GUI 版本的安装工具, 直接运行安装即可。安装过程会重新格式化我的 U 盘,我在安装前设置了空余一部分硬盘空间自用。 安装完成后,第一个分区是 fat32 分区,可以作为普通 U 盘使用,也可以放入 ISO 等映像文件, Ventoy 启动的时候会自动找到可启动映像加入菜单,选择即可运行映像中的操作系统。支持大部分 的操作系统,一般使用非常方便。

U 盘安装 voidlinux

之前空余的空间自然是用来安装一个正常的 void 发行版。我分了两个分区:一个 ext2 格式用来 做 boot 分区,一个 btrfs 用来一般使用。btrfs 分区设置了两个子卷,@void 用来作为根目录, @home 用来作为个人目录。

直接使用下面命令安装基本系统:

# xbps-install -S -r /mnt -R "$REPO" base-system

其中 /mnt 是我挂载的根分区,$REPO 是我使用的镜像地址。安装完成后将我本地的 /etc/rc.conf/etc/default/libc-locales 文件复制过去,手写一个 /etc/fstab 文件。使用 xchroot 命令 进入新文件系统,设置 root 的密码,使用 xbps-reconfigure -f glibc-locales 生成 locale 信息。

安装 grub 比较特殊,因为我不能安装到正常的 EFI 分区,那样会影响到 Ventoy 的启动,因此挂载了 boot 分区,将原来 boot 目录中的内容移动过去。建立 /boot/efi/ 目录,然后运行下面的命令安装:

# xbps-install -S grub-x86_64-efi
# grub-install --target=x86_64-efi --efi-directory=/boot/efi --removable --no-nvram --force

使用 --force 参数是因为我的 efi 目录根本不是 EFI 分区,安装到这里只是为了将来使用 Ventoy 进行 chainloading。

最后执行 xbps-reconfigure -fa 确定所有的软件包配置成功。

配置 Ventoy 启动

在 U 盘的第一个分区设置一个 ventoy 目录,里面建立一个 ventoy_grub.cfg 文件,内容如下:

set mygrub=/grub/x86_64-efi/grub.efi

menuentry "Enter the Void" {
    search --set=root --file $mygrub
    chainloader $mygrub
}

这样在 Ventoy 运行时按 F6 就可以看到我的菜单,并启动 U 盘上的系统了。

using firejail as a restricted shell

准备把我的云主机上开放给其他用户,为了限制这些用户的权限,必须选择一个可以 控制权限的 shell。

firejail

选择 firejail 主要还是经过对比,感觉它功能丰富,配置也比较简单。它利用 Linux 的命名空间、Seccomp BPF 等技术,隔离应用程序的文件系统、网络和进程。通过配置 文件支持为每个应用程序定制配置文件,定义允许访问的资源。

作为登陆 shll

本来应该可以直接把 firejail 作为用户的登陆 shell,但在应用后,用户登陆时显示 没有指定 shell,连接立即被断开。可是使用 login.user 指定 --shell 参数会提示 这个参数已经 deprecated。用 default.profile 指定 shell 参数也总是说格式不正确。

简单的解决办法是写一个脚本作为用户的缺省 shell,而这个脚本直接使用 firejail 来执行 bash。

#!/bin/bash
exec firejail /bin/bash

这样用户登陆的时候就执行了一个受限的 bash,限制的条件完全通过配置文件来决定。

支持 scp 等远程命令

但这样设置的结果是用户在远程无法通过 scp 或者 rsync 命令传输文件。开始还以为是某些 权限设置的问题,调整了很久都没有效果。深入了解了这些命令才发现,它们实际上是通过 缺省 shell 启动了远程的其它程序。而之前的脚本无法处理这种情况。

比如用户执行了 scp 之后,缺省 shell 实际会收到如下的命令行参数:

-c /usr/lib/openssh/sftp-server

我写的脚本必须要处理这种情况。因此修改起来也比较简单,最后的脚本如下:

#!/bin/bash
exec firejail /bin/bash "$@"

支持使用其它 shell

这样带来的问题就是用户登陆后只能使用 bash。想使用其它的 shell,比如 zsh 的话就只能 自己登陆后运行或写到 ~/.bashrc 中。firejail 的主要应用场景是在沙箱中运行不可信的 程序。所以缺省配置中对各种本地的配置文件都做了保护。比如 .zshrc 是无法修改的,这 对于使用 zsh 非常不便。相应的配置在 disable-common.inc 中,而这个文件在 default.profile 中被包含。简单的解决办法是取消这个包含文件,这样大部分的软件都可以正常使用了。

限制应用

限制一个程序也非常简单。比如要让用户无法使用 ssh,就只需要在配置文件中增加:

blacklist /usr/bin/ssh

Using Kickstart.NeoVIM

一直不是 LSP(Language Server Protocol)的粉丝,总觉得配置比较麻烦,但最近 试用了 kickstart.nvim,LSP 开箱可用,感觉还是有一些帮助的。

kickstart.nvim 简介

kickstart.nvim 是一个单文件的 Neovim 配置文件,包含大量注释和最常用的插件配置。 最方便的在于可以自动安装缺失的插件。只要网络环境允许,安装了它的配置文件之后, 只要启动 nvim 就可以自动完成后续的配置。项目地址:kickstart.nvim

初使用

在安装了 kickstart.nvim 之后,立刻做了一些自己习惯键位的设置,并调整了部分插件, 的确感觉比较省心。我做的修改可以在我的 github 中找到:my nvim config

日常维护

插件系统使用 Lazy.nvim,需要更新插件的时候可以执行 :Lazy,根据菜单可以选择更新(Update)、 清理(Clean)等等。LSP 使用 Mason.nvim,也可以执行 :Mason 看到类似的菜单。

主要用法

一个对新手很有帮助的插件是 which-key.nvim,可以通过浮动窗口提示可用的命令按键。 比如在命令状态下按下 g 键,它就会提示你后续可以按的按键,下面代码是部分提示内容:

d -> LSP: [G]to [D]efinition
D -> LSP: [G]to [D]eclaration
I -> LSP: [G]to [I]mplementation

LSP 的功能也比较强大,比如可以在整个工程内进行变量改名:<leader>rn,还有一个是 Code Action, 通过 <leader>ca 启动,具体能实现的功能由 LSP 本身来决定。

LSP 配置

不同 LSP 的配置还不同。比如在使用 lua_ls 的时候,用 Code Action 功能关闭了对某个建议的提示, 后续找了半天没有找到从哪里恢复设置。结果最后发现保存在了 ~/.config/.luarc.json 这个文件中。 而 pylsp 的设置需要在 nvim 的配置文件中进行修改。具体其它一些用法还要在将来逐渐领会。

Font Fallback

让终端可以显示的更美观。

字体问题

只从更换到 alacritty 作为主要的终端模拟器之后,一直对它很满意,唯一遇到的问题是部分字形无法 显示,例如 💻 是一个电脑的图标,但在我之前的终端上显示不出来。但这种字形平时很少遇到,网页 上显示也都正常,就一直没有管它。究其根源就是这些字形在我目前使用的字体文件中没有定义,所以 无法显示。

字体 Fallback

最近又试用了 ghostty,它却可以正常显示这些字形,于是萌生了提升 alacritty 体验的想法。经过调研 发现 alacritty 没有字体 fallback 的设置,只能依赖系统的设置。解决的办法就是创建一个字体配置文件: .config/fontconfig/fonts.conf,内容如下:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <alias>
    <family>JetBrains Mono</family>
    <prefer>
      <family>JetBrains Mono</family>
      <family>Noto Color Emoji</family>
    </prefer>
  </alias>
    <alias>
    <family>JetBrains Mono Bold</family>
    <prefer>
      <family>JetBrains Mono Bold</family>
      <family>Noto Color Emoji</family>
    </prefer>
  </alias>
    <alias>
    <family>JetBrains Mono Italic</family>
    <prefer>
      <family>JetBrains Mono Italic</family>
      <family>Noto Color Emoji</family>
    </prefer>
  </alias>
</fontconfig>

这里定义了我如果使用 JetBrains Mono 字体的时候,如果遇到不能显示的字形,就要使用 Noto Color Emoji 字体来显示。设置之后重启 alacritty 就可以正确显示之前缺失的字形了。

存在的问题

  • Noto Color Emoji 是我为了这次调整新安装的字体,不知道有什么办法可以知道 ghostty 使用的什么字体,ghostty 甚至不依赖系统的字体设置。
  • alacritty 对字体的 ligature 没有支持,ghostty 支持的就很好。

Dual boot voidlinux from cloud

云平台上并没有 void 的镜像,本文记录如何在一个新安装的 arch 云主机上,安装并配置 void。

文件系统准备

在 arch 主机上使用的是 btrfs 文件系统,这会大大简化分区操作。首先看一下当前配置:

pluto@arch4 ~ > sudo btrfs su l /
ID 256 gen 126 top level 5 path var/lib/portables
ID 257 gen 126 top level 5 path var/lib/machines
ID 258 gen 61 top level 5 path swap

前面两个应该是 systemd 建立,目前都是空的,swap 是虚拟内存空间,需要保留。 由于还准备保留 arch,双系统共用 home 会比较方便,所以建立一个 home 子卷。

pluto@arch4 ~ > sudo btrfs su cr /@home
Create subvolume '//@home'
pluto@arch4 ~ > sudo cp /home/* /@home/ -a

然后设置 /etc/fstab 挂载 home 分区,添加如下内容:

UUID=<your-btrfs-device-uuid> /home btrfs subvol=@home,defaults 0 0

注意如果 /home 目录的权限是 700 的话,普通用户无法访问个人目录,需要改成 755。

重新生成启动配置文件,检查生成的文件是否合理:

pluto@arch4 / > sudo grub-mkconfig -o /boot/grub/grub.cfg

接下来把 arch 的 文件系统做个快照,以后作为 arch 的根目录,同时建立一个 void 的根目录。

pluto@arch4 / > sudo btrfs su sn / /@archroot
Create snapshot of '/' in '//@archroot'
pluto@arch4 / > sudo rmdir @archroot/@home
pluto@arch4 / > sudo btrfs su cr /@voidroot
Create subvolume '//@voidroot'

重启后确认文件系统挂载没有问题,删除原来根文件系统中的内容:

pluto@arch4 / > sudo mount /dev/sda3 /mnt
pluto@arch4 ~ > sudo find /mnt -mindepth 1 -maxdepth 1 ! -name '@archroot' ! -name '@home' ! -name '@voidroot ! -name swap -exec rm -rf {} +

此时必须重新安装一下 grub,否则可能进入到 grub rescue 模式(刚刚犯了这个错误)

pluto@arch4 ~ > sudo grub-install --efi-directory=/efi

安装 void

重启进入 arch 之后,挂载 @voidroot 目录:

pluto@arch4 ~ > sudo mount /dev/sda3 /mnt -o subvol=@voidroot

按照官方安装文档安装 void。 需要注意的主要有以下几项:

  1. 允许 dhcpd 和 sshd 两个服务,否则重启之后可能无法连上主机。
bash-5.2# ln -s /etc/sv/dhcpcd /etc/runit/runsvdir/default/
bash-5.2# ln -s /etc/sv/sshd /etc/runit/runsvdir/default/
  1. 不重复安装 grub,使用 arch 的启动管理器,在 /etc/grub.d/40_custom 中 填入如下内容。
menuentry 'Void Linux' {
    set root='hd0,gpt3'
    linux /@voidroot/boot/vmlinuz-6.6.59_1 root=UUID=<your-btrfs-device-uuid> rw rootflags=subvol=@voidroot  loglevel=3 quiet console=tty0 console=ttyS0,115200
    initrd /@voidroot/boot/initramfs-6.6.59_1.img
}
  1. 由于共享 home 分区,添加用户,注意 uid 和 gid 是否相同。
  2. 注意用户的 sudoer 设置,使用户可以获得 root 权限。
  3. 双系统使用不同的 ssh key 会让客户端很麻烦,因此将 arch 系统中的 key 复制到 void 系统中。
  4. void 更新了内核之后需要更新 grub 的配置。

使用

在 arch 系统中,可以使用

sudo grub-reboot 'Void Linux'

来让系统在重启之后进入到 void 系统。但由于文件系统是 btrfs,grub 无法直接修改,使得之后每次 启动都会在 void 系统中。解决方法是在 void 中安装 grub 工具,不执行 grub-install 安装。 然后用 grub-editenv 修改 arch 的启动设置。

pluto@void ~ > sudo mount /dev/sda3 -o subvol=@archroot /mnt
pluto@void /mnt/boot/grub > grub-editenv grubenv set next_entry='Arch Linux'

这样就可以在重启后进入 arch 系统。

using ffmpeg to stablize a video

手持拍摄的视频总会抖动,使用 Linux 的现成工具就可以进行修补。

首先将视频转换为图像文件:

$ ffmpeg -i ../INPUT.MP4 DSC_%03d.png

从图像中找到一个子图像用来对比,尽量小一点的图像便于快速定位,但必要要保证 图像有一定的特点,可以在视频中唯一定位。

接下来利用 ImageMagick 进行子图像的定位:

$ compare -metric RMSE -subimage-search big.png sub.png k.png

其中命令中的最后一个参数是具有定位信息的结果图像,在这里可以忽略。

编写一个简单的命令脚本将每次的定位结果写入到文件中:

$ for i in DSC_* ; do compare -metric RMSE -subimage-search $i sub.png k.png 2>> log.txt; echo >> log.txt; done
$ cat log.txt | awk '{print $4}' > pos.txt

pos.txt 中的坐标值就是子图像在每个视频帧的位置,然后编写一个小脚本对视频帧进行 剪裁。这里我用了 python 以增加扩展性,也许将来可以把全部内容写到一个程序中。

最后用 ffmpeg 将图像再编码成视频。

$ ffmpeg -f image2 -i pic%03d.png test.mp4

peek into my mifare card

校园一卡通是 Mifare 卡,一直想了解它的原理……

入手 ACR122U 读卡器

MIFARE 是 NXP 公司的商标,主要用于利用 NFC 技术通信的低成本ID/IC卡。 网上找相关标准比较复杂,还是从工程的角度入手。淘宝购入 ACR122U 芯片 的读卡器,然后用相关软件对卡进行操作来增加对卡的认识。

读卡器到手之后在 Linux 下面无法正常工作,dmesg 显示错误消息:

NFC: Couldn't poweron the reader (error -11)

以为是内核的问题,更新了 Debian 到最新版之后问题依旧。

安装驱动和软件

换个思路,在 ACS 下载了最新的驱动,并安装相关软件:

# dpkg -i libacsccid1_1.1.5-1~bpo9+1_amd64.deb
# apt-get install pcscd pcsc-tools

结果运行 pcsc_scan 之后,读卡器的红灯亮起,竟然开始工作了。 不过这个软件读到的只是卡的属性,并看不到具体的数据,继续安装了 nfc 软件

# apt-get install libnfc5 libnfc-bin libnfc-dev

使用 nfc-list 也只能读到ATQA(属性),UID(第一扇区前四个字节)和SAK(?)。

暴力破解

还需要下载一个强力的软件 mfoc,通过暴力破解获取数据。下载源码编译之后,运行

# ./mfoc -O mycard.mfd

就可以将数据完整读出了,但由于是暴力破解,所以需要的时间比较长。感觉 读一张卡片要 10 分钟左右,比 Windows 下面的软件要快一些,也更加稳定。

mfoc 读出数据的过程也把密钥获得了,将密钥保存到文件中,下次读卡的时候就会 快一些:

# ./mfoc -f keys.txt -O mycard.mfd

目前还有一个疑问,不同的校园卡用的密钥竟然不一样,这样如何管理呢?也许 密钥不唯一,暴力破解的时候就有可能得到不同的值,实际密钥会是不同的一个。

另外如果用校园卡做门禁,只要用到 UID 部分就可以,也不需要破解,速度很快。

using pandoc to convert latex to docx

由于我比较习惯使用 latex 写自己的各种文档,而提交的时候又往往需要 Word 格式, 有一个合适的转换器就变得非常必要了。

简介

将 latex 转换成 Word 格式的需求不断涌现,印象比较深的是 2011 年提交文稿的那次。 当时也是找了很多工具,最后使用了 tex4ht 工具,将文稿转换为 html,然后用自己 写的一些过滤脚本进行处理,最后才导入 Word。整个过程是比较痛苦而且错误百出的, 结果直到今天依然没有非常理想的工具。

最近又有了难以躲过的转换格式需求,pandoc 软件进入了我的视野,通过查看手册 发现 pandoc 简直是一个万能转换器,而我只是尝试了对 doc 的支持。

初使用

用它转换文件格式非常容易,不用配置脚本也没有复杂的开关选项。对于转换 latex 到 docx 只要如下的命令:

$ pandoc -f latex -t docx source.tex -o output.docx

不用解释就可以了解这个命令的使用方法,而且给我的第一感觉就是非常的快,甚至 比使用 latex 生成 pdf 文件的速度还快。唯一发现的问题是表格支持不太好,但表格 在我的文档中使用不多,手动调整一下就好了。

转换论文

这次的主要任务是转换科技论文,pandoc 又一次给我了惊喜,大部分公式和图片都 很好的进行转换,只有几个问题需要关注:

  1. pdf 格式的文件支持不好,必须先转换为 png 格式。
  2. matrix 公式支持不好
  3. 公式里面自定义的 argmax 命令不支持
  4. 公式和图没有编号

感觉 pandoc 并没有使用 latex 作为后端,而是用自己的解释器来分析文档,因此 稍微复杂一些的 latex 指令就无法支持了。虽然还没有尝试,估计 pgf 系列包都 不会支持的很好。

没有编号的问题比较大,交叉引用全部都失效了,这次我都通过手工修改了。网上看到 好像有过滤器可以做类似的事情,但语法格式都不是标准的 latex,我就只好放弃了。

参考文献

我开始以为参考文献是不被支持的,结果一个简单的过滤器就可以解决一半问题。

$ sudo apt-get install pandoc-citeproc
$ pandoc -f latex -t docx source.tex --bibliography=mybib.bibtex -o output.docx

这样是可以生成参考文献,但是格式不符合要求。经过网络搜索才知道还需要一个 格式描述文件(citation style language)ieee.csl。下载这个文件之后用如下命令就可以 搞定参考文献了。

$ pandoc -f latex -t docx source.tex --bibliography=mybib.bibtex --csl=ieee.csl -o output.docx

Gizwits ESP8266

尝试一下机智云的软硬件环境

机智云

机智云是一个比较常用的物联网云平台,由于手上有相关的硬件,因此准备尝试一下。

GAgent

GAgent 是机智云的固件产品,写入到 ESP8266 wifi 模块后可以简化物联网 wifi 上网的主要功能。烧写固件需要到乐鑫(Espressif)下载工具软件,并从 机智云下载固件映像。按照机智云上的固件烧写说明却无法连接成功。由于我 用的模块来自第三方,猜想是 GPIO0 的管脚位置不同,最后查了淘宝上的一款 ESP-12F 模块,才知道我的 GPIO0 在从指示灯开始数的第五个位置。

第一次烧还是不成功,报 efuse 错误,结果第二次烧就成功了。

机智云串口调试助手

使用调试助手和 wifi 模块连接上之后,点击模拟 V4MCU,则可以自动和 wifi 模块 通过串口协议通信。输入 product_key 之后,可以在界面上看到我在机智云上建立 的产品信息。

在手机端安装上机智云的产品调试 APP(登录有问题,用我注册的个人用户帐号好像 无法登录,不过可以跳过登录,不影响使用),在调试助手中打开指令界面,选择 AirLink按钮,然后在手机端输入 AP 的信息并搜索硬件。大概十几秒之后 wifi 模块就可以登录机智云了,在手机段也可以看到我的产品信息。

手机软件根据我设置的产品信息可以看到照明的按钮,我在手机上对这个按钮进行 操作就可以在调试助手中看到照明的数值被改变了,也就是基本实现了我所需要 的功能。

连接 Arduino

使用 Arduino 连接到串口 wifi 模块,比较麻烦的是每次下载代码必须把 wifi 模块拿掉,否则会干扰下载。按照示例的代码下载了之后,从 DEV 到 APP 的通道 没有问题。但从 APP 到 DEV 就是不行。通过机智云网上的日志可以看到,消息传递 没有问题,应该就是 Arduino 这边的代码问题了。

后来把检测是否有相关命令的 hasBeenSet 调用去掉,直接用 read 读取相应的值, 结果就正常了。顺便说一下机智云自带的调试 APP 和下载参考代码生成的 APP 都可以用,功能类似。(3月17日更新:这个部分后来发现还是我自己的代码问题)

写入 AT 固件

后来想在 ESP8266 模块中写入 AT 固件,自己控制 WIFI 接入,但确多次无法成功。 网上下载的不同固件都无法正常启动,在 minicom 中只能看到乱码。后来看资料 指导启动信息的波特率是 74880,minicom 和 screen 都不支持,最后还是 esptool 中带的 miniterm.py 支持。

看到的错误原因是 csum err,但 verify_image 也没有问题,不知道原因是什么。 网上看到有人说用 dout 方式写入就没有问题了,可用 win 下面的官方写入软件 根本就写不进去,最后下载开源的 esptool 就可以用 dout 方式写入了,结果看到 错误信息不一样了,变成

2nd boot version : 1.7(5d6f877)
SPI Speed : 40MHz
SPI Mode : DOUT
SPI Flash Size & Map: 32Mbit(512KB+512KB)
jump to run user1 @ 1000

mismatch map 5,spi_size_map 4
system_partition_table_regist fail

还是看网上的信息,说 1.7 的固件会有这个问题,但 1.6.2 的就没有问题了。 真是历尽千辛万苦终于获得了 ready 提示信息。

虽然记录的过程看来很简单,但实际经过的尝试复杂的多。

总结

总体感觉机智云上手还是比较容易的,从昨天看资料开始,全部用掉的时间估计在 4 个小时左右。要不是软件这边有一点坑,应该更快的可以完成设计。

Play with android

Google 现在好像不太支持命令行工具了,android 命令即将废弃,好像 已经没有办法只用命令行工具完成 Android 开发了。

安装 SDK

只安装了 SDK 之后其实离开始工作还有很大距离。运行 sdkmanager --list 可以看到能安装的组件,然后就可以用 sdkmanager 安装,例如

$ sdkmanager "emulator;platform-tools"

建立虚拟机

建立虚拟机可以用 avdmanager 命令,例如:

$ ./avdmanager create avd -n test2 -k "system-images;\
android-25;google_apis;arm64-v8a" -d "Galaxy Nexus"

其中 -d --device 参数在手册里面都没有提,在执行 create avd 命令的时候可以看到相关的提示。而可选的设备列表在 lib/devices.xml 文件中可以找到。

启动虚拟机

$ ./emulator -avd test2

这个命令在执行的时候有可能报告 libGL 的加载失败,可以在运行前指定:

$ export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1

建立新工程

原来的 android create project 命令已经不被支持,目前只能使用 android studio 建立工程。