Arch Linux 星球

October 08, 2018

Felix Yan

请不要把 Wifi Dongle 翻译成加密狗

在许多翻译过来的文本里,无线网卡(Wifi Dongle / Wifi Adapter)被翻译成了“加密狗”。最近在微博大火的一篇《俄罗斯特工又蠢了!实名行动暴露自己还卖了300多特工队友…..》里面,又出现了类似的错误,让我不得不想说说这个问题。

先来看看这个例子:

文章描述了几个黑客在目标附近发射伪造热点钓鱼,此处的未翻译单词包括了 Wifi 和 Dongle。根据上下文,带一个无线网卡发射热点应该是正确的意思,而加密狗在这里和上下文没啥关系……

我尝试在搜索引擎里搜索对应词组,很不幸,惨状令人叹息:

就连正规电子产品的用户界面都中了枪:

这个问题的来源,看起来是国内加密狗早年被简单称呼为 Dongle:

Dongle,按照维基百科的解释,是和 Adapter(适配器)很类似的一个词,泛指了一大类连接、转换器。

Wireless/Wifi Dongle = Wifi 适配器,Bluetooth Dongle = 蓝牙适配器,以此类推。所以,在看到类似 USB Dongle 这样模糊名字的时候,请务必联系上下文。如果原文用了 Wifi Dongle 这样比较明确含义的词组,请翻译为 Wifi 适配器,或者无线适配器,不要再翻译成加密狗了。

by Felix Yan at October 08, 2018 08:25 AM

September 29, 2018

Phoenix Nemo

重构 StickerSetBot

关注 Telegraf 有一段时间了。特别是最近 Telegram 上 spammer 猖獗导致 Telegram 对于用户行为限制越来越严格,由此想过写一个简单的 bot 来处理加群请求之类的。

总之原因都是没时间。终于搞定一些事情之后发现之前瞎写的 Telegram 导出贴图 bot 居然备受欢迎…正好 Telegram Bot API 也更新了,来重构吧!

拆分逻辑代码

最头疼的事情首先是当时写这 bot 的时候只顾着考虑各种情况,逻辑像流水一样全部写成一坨。虽然实际不复杂吧但这不是 best practice。于是把每个功能单独拆出来先。

on('command') 的逻辑代码整块移出来作为 handler,然后能够原子化的功能再单独拆分成函数调用。目前的效果虽然还是有不少逻辑层在 handler 里,但是基本达到了比较方便维护的目的。

handler 本来就是拿来写逻辑的啊摔

接下来再清理冗余代码和各种 hard code,加了两个方法让代码看起来更整洁一些。于是就先这样。

迁移框架

好在 Telegraf 和之前用的框架在参数上很多兼容,所以这没有花太多时间。顺便尝试采用了一部分 ES6 的风格,嘛…果然不喜欢。

所以就不要吐槽为什么 ES5 和 ES6 的风格混写了。

之前要一大长串的传参现在只要一个 context 了好方便啊。中间件也好方便啊~

以上。

调试:无尽的 bugfix

并不指望一通大换血之后的代码能一次跑起来…但是没跑起来的原因是我传错了中间件值这不能忍!!为什么一会儿传的是函数本体一会儿传的是函数调用啊摔!!

而且这问题还让我调了两个小时!!!

调通了之后就很舒服了

遇到的坑还有 context 本身不能当 session 用,然而不想再引入 session 中间件于是自己写了个超简陋的内存 session。就是为了多语言支持。因为一觉醒来发现这 bot 语言莫名其妙变中文了(messages 成了全局变量 = =

当然还有 Telegram 自己的坑,比如什么贴纸就是死下载不能然后整个程序就 hang 着了。

一键导出贴纸包

终于!Telegram bot API 添加了 StickerSet 类型。只要有贴纸包名称,就可以获取整个贴纸包的信息。考虑不改变用户习惯的情况下(你哪有什么用户啊可恶)对本身处理贴纸和其他消息的函数做了修改,顺便又拆了俩函数出来(怎么代码越来越多了啊喂!

最后结果就是没有一屏看不到头的函数啦~(你快够

以及加入了用贴纸包链接导出一整组贴纸的功能,算是真正意义上的 StickerSetBot 了。

然后贴纸过多卡死了 Telegram 的 ratelimiting

直接导出单张贴纸

既然功能拆分了那也就方便加更多别的功能啦。比如不新建任务,直接甩过去一张贴纸来获得 PNG 文件~

这只 bot 在这里,源码在这里。欢迎各种玩坏~(记得去发 issue

就酱(,,•﹏•,,)

关注 Telegraf 有一段时间了。特别是最近 Telegram 上 spammer 猖獗导致 Telegram 对于用户行为限制越来越严格,由此想过写一个简单的 bot 来处理加群请求之类的。

总之原因都是没时间。终于搞定一些事情之后发现之前瞎写的 Telegram 导出贴图 bot 居然备受欢迎…正好 Telegram Bot API 也更新了,来重构吧!

September 29, 2018 06:26 AM

September 28, 2018

百合仙子

每次修 Python 代码的 bug 的时候总会想念 Rust

本文来自依云's Blog,转载请注明。

俗话说:由俭入奢易,由奢入俭难。

之前写 Python,老是在实现完一个特性之后,弄出来几个 AttributeError: 'NoneType' object has no attribute 或者 TypeError: list indices must be integers or slices, not str,还有 TypeError: can only concatenate str (not "int") to str 这样的错误。一看就明白自己又是哪里一不小心疏忽了,稍微修一下就好。

后来啊,我遇见了 Rust,整个流程就变了。之前写的时候,基本上都是通过手动测试来发现这种问题。为了高效、不破坏性地测试,需要控制测试的数据量,需要保证出错的时候相关的数据不会处于某种中间状态。当然在服务器上跑的脚本,我还要来来回回地传更新的脚本,或者弄个本地测试环境。而这一切,可能不过是为了跑一个成功之后再也不会用到的小程序,比如之前分析抓包数据的那次。而在 Rust 里,这些最容易犯的错误,cargo check 一下,编译器基本上能全给你指出来。所以有时候写一些小工具我也用 Rust,虽然写起来慢,但写好就能正常运行,不用反复试错,多好啊!

最近给 Arch Linux 中文社区的自动打包机器人 lilac 增加新特性。结果实现完部署之后,夜里就被 lilac 叫起来修 bug 了,还一下子就是仨……(lilac 很难本地测试,而短暂地服务中断又没多大影响,所以我都是不进行本地测试的。)

第一个 bug 是,与 dict.get 不一样,getattr 是没有默认值的。Python 里这种不一致很多,比如 configparser 里默认值要用关键字参数指定。Rust 遇到类似的情况,就会返回一个 Option。或者如果 API 决定如果不存在就 panic 的话,那么它就会直接返回我要取的值的类型,而不会包一层 Option。而我后边的代码是预期到这里可能取不到那个属性的,所以弄错了就会类型不匹配。

第二个 bug 是局部变量在一个分支上没有初始化。Rust 当然不会允许这种情况了。实际上 C 都不用担心这种问题,编译器会给出警告的,还有一些 linter 可以用。而 Python,很遗憾的是,我所使用的 pyflakes 并没有对此发出警告。我当然知道 pylint 那些。我很讨厌 pylint 和 jslint 这种不区分潜在 bug 和风格问题的 linter。我只需要工具在我可能疏忽的时候提醒我,而不需要它对我的编码风格指指点点,特别是那些指指点点往往是不对的。比如我的文件描述符变量名不叫 fd 难道要叫 fildes?

第三个 bug 是一个可能为 None 的变量我忘了先作 is not None 判断。这段代码如果初写的话我肯定是会注意到的,但是改的时候,只想着如果 pkg 里有冒号我得处理一下,就忘记了根本没有关联的包名的情况。Python 的 None,以及 C 和 C++ 的 NULL、Java 的 null、Lua 和 Ruby 的 nil、JavaScript 的 undefined 和 null,被称作是十亿美元错误,给无数程序员和用户带来了无尽的 bug。幸好这个东西在 Rust 里不存在:表达「没有值」的值没有被作为特殊值存在于几乎所有类型中,而是作为一类类型的可能的值之一。想要使用「正常」的值,就需要显式地进行类型转换,所以不可能被不小心忽略掉。顺便说一下,Go 里也有 nil 这种东西,以至于会出现这种不容易发现的 bug

Python 现在也给出了解决方案:类型注解,提供类似的类型检查。不过检查器是第三方的,也并不十分完善。等我找到机会试用过之后再来写感想啦。

by 依云 at September 28, 2018 01:37 PM

Felix Yan

Thunderbolt 3 eGPU Bumblebee 方案尝鲜

最近从公司淘了一块 GTX 1060,淘宝了一个显卡盒折腾 TB3 eGPU 方案。由于笔记本经常带出门,打算采用即插即用的 bumblebee 方案(回家插上盒子用独显,拔下盒子可以带出门,可以正常待机不用重启)。下面记录一下 Arch 上配置的步骤和遇到的坑。

1、准备软件包

# pacman -S bumblebee primus lib32-primus nvidia-dkms lib32-nvidia-utils bolt

2、修改 bumblebee 配置

修改 /etc/bumblebee/xorg.conf.nvidia,在 Section “Device” 中加入:

Option "AllowExternalGpus" "true"

修改 /etc/bumblebee/bumblebee.conf,在 [optirun] 中修改:

Bridge=primus

3、插入 TB 线,验证设备(不建议完全关闭设备验证!)

输入 boltctl 查看设备信息,记下设备的 uuid。

$ boltctl
 ● TUL TBX-550CA
   ├─ type:          peripheral
   ├─ name:          TBX-550CA
   ├─ vendor:        TUL
   ├─ uuid:          00xxxxxx-xxxx-xxxx-ffff-ffffffffffff
   ├─ status:        authorized
   │  ├─ domain:     domain0
   │  └─ authflags:  none
   ├─ authorized:    Fri 28 Sep 2018 08:23:25 AM UTC
   ├─ connected:     Fri 28 Sep 2018 08:23:21 AM UTC
   └─ stored:        Thu 27 Sep 2018 05:22:07 AM UTC
      ├─ policy:     auto
      └─ key:        no

验证设备并设置为自动验证:

$ boltctl enroll --policy=auto 00xxxxxx-xxxx-xxxx-ffff-ffffffffffff

4、启动 bumblebeed

# systemctl start bumblebeed

5、测试、运行程序

$ optirun glxspheres64
Polygons in scene: 62464 (61 spheres * 1024 polys/spheres)
Visual ID of window: 0x13f
Context is Direct
OpenGL Renderer: GeForce GTX 1060 5GB/PCIe/SSE2
……

6、安全删除设备(参考了 jpamills 博客里的脚本)

#!/bin/bash

secs=5
tbt_chain=/sys/bus/thunderbolt/devices/0-0/../../../..

echo "Unplug eGPU script started."
if [ "$(id -u)" != "0" ]; then
        echo "Please run using sudo. Exiting."
        exit 1
fi

systemctl restart bumblebeed
sleep 2
modprobe -r nvidia_modeset
modprobe -r nvidia-uvm
modprobe -r nvidia

if [ -e $tbt_chain/remove ]
then
        echo 1 > $tbt_chain/remove
        echo "Thunderbolt chain removed from PCI tree. Please unplug eGPU now."
        while [ $secs -gt 0 ]; do
                echo -ne "$secs to rescan...\033[0K\r"
                sleep 1
                : $((secs--))
        done
        echo 1 > /sys/bus/pci/rescan
        echo "Rescanned the PCI bus. Completed."
        exit 0
else
        echo "eGPU does not appear to be attached. Exiting."
        exit 1
fi

如果和我一样在盒子上插了外置硬盘,还可以在脚本里加上相应的 umount 语句。删除设备时需要先把使用设备的程序退出。

效果展示

剩下的问题

  • 脚本里可以考虑通过判断 nvidia-smi 列出的 PID 自动杀掉所有还在用卡的进程
  • Vulkan 应用程序暂时无法使用外置卡运行(仍然使用集显),可能需要 bumblebee 提供支持

by Felix Yan at September 28, 2018 08:54 AM

Phoenix Nemo

想要导出 Telegram 贴图

Telegram 上出现了越来越多的优质贴纸,想要把这些贴纸用到其他 IM 平台上的时候就会比较麻烦,所以一直想要一键导出一个贴纸包的功能。

可惜的是,Telegram bot API 的限制,并没有任何简单的办法通过贴纸消息获得贴纸包的信息。寻找另外的途径,例如 telegram.me 的贴纸链接会定向到 tg://addstickers?set=[StickerSet]。搜索了一下现成客户端的源码,都是交给 MTProto 的 API 处理,也没有明确的解析过程。而这些客户端所调用的 messages.getStickerSet 也没有在官方的文档中列出。(吐槽:Telegram 的协议、文档和代码真是糟糕,查阅的时候我的表情一直是 黑人问号.gif

由于最近状况不是很好,所以只好暂时放弃继续读 webogram 的源码。因为读 Angular 的东西实在是折磨…

所以依然是选择直接发 sticker 再转为图片发给用户的模式。这样的已经有了相关的 bot,于是改为多个 sticker 打包、支持多语言、支持 jpg 和 png 以及批量缩放功能的 bot。需要安装 Node.js v4.0 及以上版本和支持 webp 的 ImageMagick。

虽然实现效果看起来还可以,但是并未实现最初希望的功能,所以只能是练手用的轮子而已。不过,这个轮子稍微尝试了一些新的东西。例如超简陋的内存数据库,而且很多细节考量更加周到,例如任务锁虽然不是写过最麻烦的,不过应该算是相对完善的。当然也考虑了内存数据库的手动释放以防内存爆炸为此还特地在群里讨论了 object children 被 undefine 而 object 其他 children 还在被引用的状态下是否可以回收部分内存的问题

源码的实现非常简单,但是好久不写代码还是手生,折腾了一下午写功能加一晚上和朋友们 debug。读源码戳 GitHub

这里有一只 bot 跑在测试环境,所以可以尝试一下。如果没理你说明沙盒没开,那么就请自己去跑源码来使用辣ᕕ(ᐛ)ᕗ

有几点坑,比如这个 node-telegram-bot-apionText 方法无法正确匹配 Negative Lookahead 的正则表达式(不应该啊…然而没深究),adm-zip 非常非常不好用,jszip 文档表述不清 API 调用复杂然而用起来了就还不错。

但是最坑的是,只为实现这么一个简单功能的 bot,我的 node_modules 目录下居然有

1
2
Phoenix-X1-Carbon :: js/telegram-stickerimagebot/node_modules ‹master› » ll | wc -l                                                                                               1
104

WHAT??? 104 个依赖包!!!

真是可怕…明明我已经尽可能减少不必要的依赖了…

===== 2018/9/28 更新 =====

Telegram bot API 更新了(早就)

于是这只 bot 可以一键导出一组贴纸了。详情

Telegram 上出现了越来越多的优质贴纸,想要把这些贴纸用到其他 IM 平台上的时候就会比较麻烦,所以一直想要一键导出一个贴纸包的功能。

September 28, 2018 03:46 AM

September 27, 2018

ヨイツの賢狼ホロ

扫盲 XMPP(Jabber) - 一个自由开放的即时消息协议

这标题的命名方式哪里眼熟……

想和谁在网上聊天的话,可以用的 IM 软件有不少,当然槽点也很多(这家伙要求真多.png):

  • WhatsApp , Line 之流: 从客户端到协议都是私有的,以及似乎有向审查屈服的倾向?
  • Telegram ,服务端是私有的,官方客户端的源代码经常咕咕咕……
  • Signal 和 Wire ,虽然服务端和客户端都是自由的,但是有人成功运行起来自己的服务端嘛 🤔

……

于是是时候试试看 XMPP 啦(编不下去了.webp.png 😂)

XMPP 是啥?

可扩展消息与存在协议(XMPP,全称为Extensible Messaging and Presence Protocol,前称Jabber) 是一种以XML为基础的开放式即时通信协议,是经由互联网工程工作小组(IETF)通过的互联网标准。

Jabber是一个开放源代码形式组织产生的网络即时通信协议。XMPP原本是为即时通讯而量身定制, 但由于XML Stanza本身是XML元素,在基于XML灵活发展的特性下,使得XMPP也可以适用其他方面, 已经得到了IETF的批准。XMPP与IMPP、PRIM、SIP(SIMPLE)合称四大IM协议主流,在此4大协议中,XMPP是最灵活的。

(从维基百科抄的(雾))

有不少聊天软件是 XMPP 的实现,例如 Google Talk 和 Facebook Messenger,以及 AOL 和 一些网络游戏的聊天等。

因为传送的是 XML 嘛,所以 XMPP 协议本身十分灵活,不过传送二进制文件来就稍微有些困难了(不过有不同的扩展 可以提供文件上传等功能)。

和电子邮件一样, XMPP 也是非中心化的协议,可以均衡负载和对抗封锁。当然有可以多处登录啦, XMPP 通过优先级来 区分不同的设备,优先级高的设备会收到相应的消息(有人认为这是个缺点,于是有了 MAM 在不同的设备间同步消息)。

XMPP/Jabber实现多人聊天(MUC,Multiply User Chat)是采用聊天室的方式实现的,形如 room@domain.tld 的形式, 用户可以添加这个聊天室进行群聊,不过介于不少客户端不支持群聊加密,于是 XMPP 更多的还是以私聊为主。

注册一个 XMPP/Jabber 帐号

因为 XMPP 是个自由开放的协议啦,所以网上有很多公开的 XMPP 服务, 这里 有一个大而全的列表。 但是如果汝只是想找个速度合适的服务器的话,BLUG 整理出了一个 经过测试的部分 XMPP 服务器清单

可以看到大多数的 XMPP 服务器对注册没有任何要求,只需要汝选择一个 Jabber ID 然后输入密码就 OK 啦, 当然这么做的后果之一就是忘记密码就不能找回了,所以创建一个强密码并记住它特别重要。

除了在网页上注册以外,不少服务器也支持在客户端上注册,所以……

安装一个 XMPP 客户端

不同平台上可以推荐的客户端有很多:

  • 比如跨平台的 Pidgin (顺便还可以跨协议,但是 Pidgin 支持 XMPP 是挺优秀的)。 Windows 可以从 https://pidgin.im/download/ 下载安装包,GNU/Linux 可以从包管理器安装,或者下载源代码编译
  • macOS 用户可以安装 Adium,可以从 https://adium.im 下载。
  • iOS 用户推荐 ChatSecure,可以从 App Store 下载安装。
  • Android 用户推荐 XabberConversations 前者支持 XMPP 中常用的 OTR 端到端加密, 后者支持 MUC 和新的 OMEMO 和 openPGP 加密。

每一个 XMPP 客户端的登录和注册方法都不尽相同,但都比较简单。这里就偷个懒不指导啦 😂

和好友聊天或进行群聊

添加汝好友或群聊的 Jabber ID 就 OK 啦~

大概长这样

加密聊天

主要说的是端到端加密,毕竟 8102 年了,传输加密已经是刚需了吧 😂

目前 XMPP 中常用的端到端加密方式有三种 ,都是通过非对称加密实现的:

  • OTR(Off-the-Record Messaging,不留记录即时通讯) 是 XMPP 中最常用的端到端加密方式,被大多数客户端所支持。加密迅速而且前向安全。 缺点是更换客户端或重新登录时 OTR 指纹会发生变化, 可能会无法保证消息身份的一致性而需要重新验证。以及对文件的加密尚未标准化(不同客户端加密的文件 可能无法相互存取,因此习惯是发送文件的链接),另外称为下一代 OTR 的 OTRv4 正在开发中。
Pidgin 通过插件可以支持 OTR

Pidgin 通过插件可以支持 OTR,上面介绍的客户端中除了 Conversations 都支持 OTR。

  • OMEMO(OMEMO Multi-End Message and Object Encryption,OMEMO 多终端消息和对象加密) 是 一种新型的端到端加密协议,可以跨设备同步加密的消息记录和文件,验证起来也比较简单( 因为都盲目信任了? )。 不过除了 Conversations 以外好像都没支持好的样子……
  • openPGP 也能用于 XMPP 的端到端加密,不过毕竟是面向邮件的非对称加密,客户端不一定都支持 (Gajim、Psi+ 和 mcabber 支持)速度可能没那么快,还有可能把服务器娘累坏(雾)。

一些注意事项?

  • 因为大多数 XMPP 服务没有提供找回密码的功能,** 所以所以创建一个强密码并记住它!**
  • 传输加密是必须的,端到端加密最好也用上。
  • 端到端加密最好验证下双方的身份,除了传统的指纹验证以外 OTR 还可以通过共享秘密和问答验证。
  • 只使用自由开源的客户端, 这个不解释。

自己搭建 XMPP 服务器

好啊好啊,这就是非中心化服务的优势之一呢~

比较推荐的是 Prosodyejabberd , 前者轻量,配置简单。后者功能丰富,扩展性强。 咱就用 Prosody 搭了一个

读点别的?

https://tonghuix.io/2015/03/xmpp-chat/

⇪网页标题:弃用QQ和微信!全面转向基于XMPP(Jabber)的即时聊天

这篇文章简单的介绍了下 XMPP 的基本特点,也推荐了些不同平台的客户端。

https://beijinglug.club/wiki/lib/exe/fetch.php?media=xmpp-guide.pdf

⇪网页标题:XMPP(Jabber) 聊天快速指南

由北京 GNU/Linux 用户组(BLUG)成员编写的 XMPP 入门指南,图文并茂值得一读(?)

https://hardenedlinux.github.io/cryptography/2018/02/01/pidgin_xmpp_otr_debian.html

⇪网页标题:Debian/Ubuntu 用户使用基于 XMPP 即时通信协议的 OTR 保护隐私的标准化部署流程

介绍了 OTR 的原理, Debian 上使用 Pidgin 进行 OTR 聊天的流程,以及验证身份的一些技巧。

by ホロ at September 27, 2018 04:00 PM

September 20, 2018

百合仙子

永远不要 tail -f 管道

本文来自依云's Blog,转载请注明。

运维同事为了收集日志,配置程序将日志写入一个命名管道。然后他在外边拿 tail -f 去读,结果发生了灵异事件。通过 strace 可以看到,tail 进程读取了日志,但是却并没有再输出来。但是如果不启动输出日志的程序,而是在实例启动之后再进去往管道里写数据,却又是可以立即得到 tail 的输出的。

很奇怪的行为,一群人在那里研究半天,猜测是不是环境变量造成的啊,是不是放后台组执行造成的啊,是不是 XYZ 啊。——典型的「霰弹枪式」除错法

我当时也被带沟里了。于是跑去尝试复现,接着去读 tail 的源码。花了好久才明白这是一个很简单的问题:tail -f 的语义首先是 tail 这个词本身——先读文件最末尾的数据(默认是十行),然后再是 -f 选项的语义,即在文件更新时接着读取数据并输出。所以,当程序往里边写日志时,管道写端一直没关闭,tail 就一直读不到文件结束,也就无法确定最后十行是什么。当他们测试的时候,因为使用的是 echo shell 命令,打开文件、写入数据、关闭。这样 tail 一下子就读取到了文件末尾,然后把数据输出来了。接下来就是边读边输出了。

其实这种使用方法本身就很奇怪了,以至于这个执行流是兼容许多系统的 tail 的各种分支里,最最不常规、无可奈何的那一个分支。你都用管道了,cat 一下嘛。如果怕遇到管道被 reopen 的情况,就在 while true 里 cat 就好。

这个事件中,我也是见识了很多人解决问题的奇怪思路:「我猜猜猜。猜对了哦耶,猜错了,哎呀编程好难啊,Linux 系统好难啊……」猜你妹啊!你长的是大脑又不是骰子,用逻辑一步一步地取得结果不好吗!

有一个小游戏——猜数字。比如甲确定一个 1 到 1000 之内的整数,然后乙来猜。每当乙给出一个猜测时,甲回应猜对了,还是过大或者过小。如果乙知道什么叫二进制的话,乙可以保证在十次之内猜中的。

计算机系统和编程世界里,最棒的一点是确定性和逻辑性。虽然经常也不是像上例那样完全确定的,但至少比起人类社会要容易确定得多。特别是在有源码的时候。所以解决问题的路线也很简单,顺着问题的症状一路回溯,确认然后排除那些没有问题的部分,逐步缩小问题所在的范围,直接你看见它。就跟上边的猜数字游戏或者地毯式搜索一样。每一次猜测都是带着排除一部分没有问题的地方而去,而不是明明有证据表明某个地方不可能有问题,你还偏偏怀疑问题在那里,做无用功。

就像调查一个凶案,这些人放着有作案嫌疑的人不管,非要费劲地去调查那些有相当好的不在场证明的人。

Arch Linux 中文社区这边也有很多这种人。出了问题描述不清楚症状。新手嘛,没经验也没学习过如何描述事实,讲不清楚也没什么,引导对方获取截屏、日志,逐步排查问题就好了嘛。可就是有些热心人,喜欢提出自己的猜测。重点是:都不尝试证实猜测是否属实,就急着上解决方案。结果就是,我询问细节事实的消息没人理,求助者试试这个,试试那个,最终问题能否解决,就跟买彩票能否中奖一样,全凭运气。

by 依云 at September 20, 2018 03:08 PM

September 18, 2018

ヨイツの賢狼ホロ

听着节拍敲手指

这标题哪里眼熟……(划掉)

要是有一种游戏,它的唯一玩法就是按着音乐的节奏敲手指,要是有这样的游戏就好了。 但当时的我没有去做任何了解,只是将这个想法憋在了肚子里。 因为我觉得,这需求太小众了,才不会有人做这样的游戏的。

结果显然我错了,这样的游戏不但有,而且还有一大堆! 它们就叫做「音乐游戏」,简称「音游」。

听着节拍敲手指 - FiveYellowMice's Blog

不过按照维基百科的说法的话,音乐游戏其实还包括乐器模拟一类的游戏。而大家比较常说 的跟着节奏敲手指的那一类游戏的话,有一个更狭义的名称叫做节奏(动作)游戏 😂

所以下面所说的都是这种啦……

入坑

大概是六年前的时候,咱入手了咱的第一部 Android 系统的智能手机(Motorola XT535)。 随着 iPhone 和一批 Android 手机的出现,手机游戏的画质和体验突然就进步了起来 (和 Java 和 Symbian 时期的游戏相比)。于是当时有不少优秀的手机游戏出现了, (和不少推荐的文章)。

大概某一天咱在浏览游戏推荐文章的时候看到了 Cytus,画面看起来很漂亮,音乐也很动听。 于是就下载来试了试,就这样,一个新坑挖开了…… 😂

Cytus是款音樂節奏遊戲,遊戲方式簡單直覺,隨著歌曲旋律畫面上出現節奏拍點, 而玩家要做的就是配合動態掃描線的上下移動抓準時機點擊拍點。 遊戲畫面為細膩的手繪風格,同時網羅多位大牌日本樂師陣容參與音樂製作, 包括知名音樂遊戲的樂師Tsukasa,BMS同人編曲師Naotyu-、樂師Sta等人, 多樣化的曲風、高質感的畫面,是雷亞遊戲的自信力作。

(以上是官方介绍)

Cytus 的游戏截图

这就是 Cytus 的画面啦,音乐播放的时候,中间那条黑色的线(称作扫描线)会循环的上下移动, 汝要做的就是在扫描线和目标(像泡泡一样的)重合时触摸目标啦 ~ 按下的时机越准,分数就会越高呐~

后来这个游戏更新了数次,多了不少的新曲,甚至还出了续作)

https://play.google.com/store/apps/details?id=com.rayark.Cytus.full

借这个机会,咱也了解到了这个游戏的开发商 Rayark,一家台湾游戏公司。在 Cytus 之后,它们还制作了 数部音乐游戏作品,例如 Deemo 和 VOEZ。游戏的品质都属上乘,咱偶尔也会玩一玩(为啥是偶尔呢?)……

提速

后来咱买了第一部可以被称为“平板电脑”的家伙,就是 Samsung Galaxy Tab 2 啦,然后发现平板的 屏幕更大,同时可以用更多手指。可以说是玩音游的利器……

又是浏览游戏推荐文章的时候,发现有一篇文章除了提到咱玩过的 Cytus 和 Deemo 以外,还提到了另外两个游戏, Dynamix 和 OverRapid 。

OverRapid 的游戏截图

OverRapid 是一群韩国人开发的下落式音乐游戏(就是判定线不动,目标从上往下落下的类型, 这是音乐游戏的一种经典玩法)。不过这个游戏貌似主打的是高速(Rapid?),以及中间的两个 轨道有时会落下红色 note ( note 是音乐游戏对需要点击或按下的目标的一种习惯的表述方式), 这个时候就需要在判定线上滑动了,和普通的下落式游戏有些区别不是?

C4Cat推出的首款游戏Dynamix,是一款三重下落式音乐手机游戏。与一般下落式音乐游戏不同, Dynamix分为左、中、右3个下落方向的区域,每个区域作为一个乐器的音轨。 三区的设计令游戏更具变化,为游戏提供更有趣和更高难度的挑战。 另外,同时处理三个音轨也令玩家有亲身演奏整首音乐的临场感,加强音乐节拍。

(以上还是官方介绍)

Dynamix 的游戏截图

(三条轨道是不是很刺激?)(笑)咱一开始玩的时候也是手忙脚乱的呢,不过玩的久了就习惯了。 (虽然现在智能开着 Bleed 打打 12 以下的难度养老了, Event 肝不动啦……)

这两个游戏开始有一定难度了呢,不过咱还是会偶尔打开玩一阵的。

偶像的力量(雾)

曾经一阵很喜欢初音未来一系列的 VOCALOID 家族,(当然现在也很喜欢啦,不过现在更喜欢各大音乐游戏的原创曲了) 听歌的时候也接触到了以她们为主题的音乐游戏,就是 Project DIVA 系列啦~

Project DIVA Future Tone 的游戏截图

又是一种新的玩法,在画面上的目标和飞来的目标重合时按下对应的按键。也有长按和同时按等一系列的特殊音符。

既然叫做“初音未来 Project DIVA”系列嘛,自然收录的都是 Crypton 家的 VOCALOIDs 的曲目(3DS 版的 Project Mirai 有几首 INTERNET 家的 GUMI 客串出场)。每首歌都有特别制作的 PV (或者有时会偷懒直接拿原来的 PV),画面漂亮,就是偶尔 会光顾着看 PV 漏按了几个(雾),咱的 PSV 和 PS4 就是为了玩这个系列买的呢(然后又了解到了不少好游戏,又入了不同的坑)

新世界的大门

然后过了两三年。

有一天咱去街机游戏厅的时候,偶然瞥见了一台看起来很像洗衣机的街机(大概可以知道是啥了)。 有两位玩家正在游玩,还有一群人在围观。咱也凑近去看了一眼,感受到了熟悉的感觉,那个 随着音乐拍打按键的感觉……

一局游戏结束以后,有老玩家看到咱很有兴趣,便让出位置让咱来尝试一下。当时的感觉是记不清楚了, 只是觉得比手机上的那些不知道难了多少倍的样子……特别是看到老玩家们打高难度的歌曲时那行云流水的 样子,更是投下了羡慕的目光。

后来知道了这个游戏叫 maimai,当时还有个中文名叫做“舞萌”。这个名字听起来挺不错的,毕竟自己玩的 时候跟着音乐拍按键或者在屏幕上滑动的时候,真的和跳舞很像。不过咱当时还没有要深入的决心。

又过了一阵子,咱发现了家旁边的另一家游戏厅,于是便去看了看。这次除了 maimai 以外,还发现了其它的 音乐游戏街机。比如一个有按键两边还有盘子的(Beatmania IIDX)和看起来像弹吉他的(GutiarFreaks)。

这次不知道是为什么咱就去尝试了,虽然结果惨不忍睹到连 Beginner 难度都过不去( Beatmania IIDX 的 最简单的难度)。不过倒是喜欢上了,后来就经常去玩了。不知不觉就成了咱的兴趣之一了呢……

IIDX 的游戏截图

这就是 Beatmania IIDX 的游戏画面啦,没想到这个游戏竟然已经有接近 20 年的历史了呢~

Maimai 的玩法介绍

Maimai 的玩法介绍,目标靠近屏幕边缘时敲击屏幕周围的按键或者触摸屏幕边缘就好啦~

是不是很简单呢(露出了不怀好意的笑容.png)

太鼓达人的玩法介绍

像敲太鼓一样游玩的太鼓达人,除了街机版以外在 PSP/PSV/PS4/3DS/Nintendo Switch 等各种 平台上还有家用版。

遍地开花

后来了解信息的渠道越来越多,玩到的音乐游戏也越来越多。

在 Bilibili 上关注了些玩音乐游戏的 UP 主,接触到了不少优秀的音乐游戏,例如……

“和蔼的纯白色光芒,正在那失落世界的旋律斗争中等待着你。”

在这个挑战性十足的街机风格音乐游戏中随着旋律而触碰、按压与滑动; 聆听来自以日本为首的世界各地作曲者所写的歌曲!

体验全新的音符种类「音弧」:淋漓尽致地利用屏幕空间来滑动并跟随流动的旋律。 在这光芒与斗争相互纠缠的失落世界中遗失自我。

Arcaea 的游戏截图

特别像某个街机音游 SOUND VOLTEX 的 Arcaea,在普通的下落式的基础上加了第二条轨道, 玩起来完全是另一番景象。是咱现在经常玩的游戏之一。

https://play.google.com/store/apps/details?id=moe.low.arc

扮演主角探索地图,以音乐游戏进行「调律」,让黑白无声的世界恢复生机。
Lanota 的游戏截图

圆形的下落式音乐游戏,剧情和收录曲都很优秀。(然而咱才刚开始玩……)

https://play.google.com/store/apps/details?id=com.Noxygames.Lanota

Tone Sphere 的游戏截图

由 Bit192 Labs (其实只有 Sta 一个人)开发的音乐游戏, 看起来很像 osu! ,不过完成度似乎还可以。还是感叹又会画画又会作曲还能自己写游戏的 Sta 好天才啊……

https://play.google.com/store/apps/details?id=com.bit192labs.tonesphere

osu! 的游戏截图

俄亥俄州立大学专用播放器(雾)

一个结合了多种游戏模式的音乐游戏,曲库和谱面在玩家的努力下正在茁壮成长。

(其实优秀的音乐游戏还有不少啦,咱就先偷个懒…… 😂)

音乐游戏对咱的影响?

大概有:

  • 音乐播放器里的列表里都成了音乐游戏里玩过的曲目……
  • 因为在游戏里玩过某一部动画的曲目然后就去追了)
  • 听音乐时脑内会浮现出谱面(或者想象谱面)
  • ……

一些想法

玩了这么久的音乐游戏,还是觉得自己没入门……特别是看到其他人优秀的成绩的时候,心里还是会小别扭一下。 然后就继续练习去了……

音乐游戏玩家又被分成了不少更小的圈子呢,比如玩过街机音乐游戏的可以算上是“核心玩家”的一群人中的个别经常 会看不起只玩手机游戏的“休闲玩家”,反过来也有玩了某几部游戏以后就到处宣扬“** 天下第一”等等的(这种人好像 有些时候叫做睿智?)

然而周围的人看到自己在玩音乐游戏总是会问是不是节奏大师(&^$$^%^W^%$$^&......)

快取得突破的时候(例如要 Full Combo(没有失误)或者满分的时候)突然被打断, 又以“不就是游戏嘛”等一系列理由敷衍的时候……

在群聊打招呼的时候都说自己很弱然后突然发送了一张成绩很好的截图(大佬卖弱是音游玩家的一环?)

又说了不少的废话呢……

如果要咱推荐音乐游戏的话……

推荐手机上的音乐游戏的话:

  • 核心向(接近街机音乐游戏的游玩体验,有些挑战性):Arcaea,OverRapid,Dynamix,GROOVE COASTER 2 Original Style
  • 剧情向(画面出色,有一个好故事):Deemo,Lanota ,Tone Sphere,Cytus II
  • 听歌向(选曲优秀,简单上手):Cytus,VOEZ,MUSYNX

其实上面提到的游戏不止这一个方面有优势啦……

至于想体验街机音乐游戏的呢,(没想好)

😂


以上的游戏或网站截图来自各游戏的开发商。

by ホロ at September 18, 2018 04:00 PM

September 15, 2018

百合仙子

人生苦短,我用 skim

本文来自依云's Blog,转载请注明。

前两天我又看到了基于子序列匹配的字符串过滤工具 fzf 的绚丽效果了。实际上我很早就听说了这个工具,只是懒得动手配置。此次提及,我发现 fzf 已经在官方软件源里了,而我也正好有时间,所以打算试一试。

然后呢,Arch Linux CN 群组里艾穎初提到 skim 这么一个工具。了解了一下,这个就是 Rust 版本的 fzf,并且在 archlinuxcn 源里也有(git 版本,即 skim-git)。这太好了,就是它了!

skim 的操作很简单。文章开头的链接里已经有效果演示了。常用的也就是输入子序列去过滤,然后再输入一个进一步过滤,直到看到想要的。使用 ! 前缀可以反向过滤,^ 匹配开头 $ 匹配结尾。Ctrl-p/n 来上下移动。提示符那里也支持通常的行编辑。

到现在为止,我自行实现了 sk-cd、sk-search-history、sk-vim-mru 三个功能。另外使用了自带的 completion.zsh 文件。由于各种不满意,没有使用自带的 key-bindings.zsh 文件(也就包含 cd 和历史命令搜索功能啦)。

completion.zsh 里目前有两个功能。kill 时通过 ps 补全进程 pid。这个想法很好,以后我可能专门做一个通用的方便 strace 啊 lsof 啊 gdb 啊之类的用。

另一个是遇到两个星号(**)时按 Tab 补全,查找并替换成当前目录下的文件。

我实现的 sk-cd 是从 autojump 取目录列表,然后喂给 skim。于是就成了交互式的 autojump~这是一个我很需要的功能。原来我都是通过 Tab 补全列出可能的项,然后再 Tab 过去选的,有些慢也有些麻烦。

sk-search-history 就是在历史命令里找东西。因为遇到特殊字符时无法正确地加载预览,我并没有开启预览功能。反正找到的命令只会放在命令行上,并不会自动执行的,选错了可以及时取消。

以上两个功能分别绑定到 Alt-s d 和 Alt-s r 上。我使用 Alt-s 作为 skim 快捷键的开头,以便保留 zsh 原本的快捷键,避免冲突,特别是以后可能会有更多功能被加入。我在 Vim 里,也是类似的做法,Alt-q 是 easymotion 的开头快捷键,Alt-d 是 denite 的开头快捷键。

sk-vim-mru 仅仅是个命令了。使用的数据是 mru.vim 的历史记录文件。然后做了两个函数:vim-mru 使用 Vim 编辑文件,vv-mru 使用我自己的 vv 命令在已有的 gVim 里编辑文件。

我做的版本和 skim 自带版本,最大的差别在于,我的版本会尽量使用全部的窗口空间,而 skim 自带的总是会使用 40% 窗口高度。(所以我有个函数用来获取当前光标位置,有需要的可以自己拿去用。)

如果你想用我的配置,可以 wget https://github.com/lilydjwg/dotzsh/raw/master/plugins/sk-tools.zsh 回去,然后 source 一下就好。有需要的话(比如数据来源、键绑定等)可以自行修改。


2018年09月17日更新:我尝试了一下把 sk-search-history 映射到 Ctrl-r 上,然后很快就放弃了。因为 skim 的结果是不可预测的,而默认的 Ctrl-r 的结果是完全可预测的(只要还记得;当然你不能开(那个让我在服务器上误杀过进程的)实时历史共享)。可预测性对提高效率非常关键,因为你不需要中断思维,停下来等结果。

by 依云 at September 15, 2018 05:20 PM

September 12, 2018

quininer kel

the-grail-of-forward-secrecy

0-RTT and Forward Secrecy

Tue Sep 11 00:43:19 2018

前向保密是一個已經被人熟知的安全特性。 基於密鑰交換的前向安全廣泛存在於各種密碼協議之中,例如 TLS、Signal Protocol、WireGuard。

Session based

一般我們所說的前向保密方案是基於會話的,每次會話通過握手產生一個獨立的會話密鑰。 會話結束之後銷毀該會話密鑰便可保證前向保密。

但基於會話的前向保密並不適用於所有場景, 很多情況下我們沒有握手的條件、或者通信開銷非常之大。 例如 Email、IoTInternet of Things。

“如何在這些情況下保證前向保密”成爲了一個聖盃級的公開問題, 數十年來衆多密碼學家爲此付出心力。

幸運的是,這並非是無可能的。

Interval based

基於時間段的前向保密方案聽起來不太優雅,但非常實用。

基本思路是將密鑰分成不同的時間段。 在某個時間段過去之後,銷毀該時間段對應的私鑰,從而達到前向保密。

Weekend scheme

我相信這個方案在歷史上被設計過不止一次,最後一次見到它是在 openpgp 討論中 1。 我不清楚它的名字,本文將之稱爲週末方案。

用戶每逢週末更換一次自己的密鑰,並將新的公鑰公佈在網絡上。 其他用戶加密時必須檢索對方的最新公鑰。

這個方案很簡單,但缺點也很明顯

  • 要求其他用戶加密時有檢索最新公鑰的能力
  • 多設備不友好

每次都要更新公鑰實在是太麻煩了,那可不可以再給力點?

HIBE scheme

有的!我們首先通俗的介紹一下 HIBEHierarchical Identity Based Encryption2

HIBE 有一個中心的 PKGPrivate Key Generator,它可以通過 ID 產生所有用戶的私鑰。 每個用戶也能通過子用戶的 ID 產生子用戶的私鑰,自然的,子用戶不能恢復父用戶的私鑰。

其他用戶加密時只需要知道 MPKMaster Public Key 和子用戶的 ID。

我們可以基於 HIBE 構造出一個不需要更換公鑰的前向保密方案。

  1. 用戶加密時根據當時的時間產生用戶 ID,並用此進行加密。例如

    CT = enc(MPK, ["2018", "9", "10"], MSG)

  2. 用戶解密時,根據 PKG 或父用戶的私鑰產生對應 ID 的私鑰,並用其解密,例如

    SUB_SK = keygen(SK, ["2018", "9", "10"]) MSG = dec(SUB_SK, CT)

  3. 當一個時間段過去時,用戶先用 PKG 或父用戶的私鑰產生剩餘所有子用戶的私鑰,並銷毀 PKG 和父用戶的私鑰。例如

    (_, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC) = keygen_year(YEAR) delete(YEAR)

完美!這個方案完全解決了週末方案的兩個缺點, 其他用戶不需要時時檢索對方的最新公鑰,用戶自己也不需要在多個設備之間同步自己的新私鑰。

但是基於時間段的方案有一個共同的缺點,

試想一個情景,alice 在 2018年9月30號 執行加密,但由於各種原因到達 bob 手中時遲了一日。 可惜當時 bob 已經將 9月 所對應的私鑰銷毀了,所以他無法解密 alice 的密文。

這顯然不是我們想要的效果,那可不可以再給力點?

Puncture based

除了時間段這個思路之外,我們還有沒有其他實現前向保密的辦法呢?

可穿刺加密是另一種思路, 原始狀態下的私鑰包含所有信息,可以解密所有密文。 當用戶成功解密一個密文後,用戶可以從私鑰中消除(我們將之稱爲穿刺Puncture)一部分信息, 使得新私鑰不能解密該密文。

Binary Tree Encryption scheme

BTEBinary Tree Encryption3,4 是一個有趣的設計,它同樣基於 HIBE。

它相當於使用 IBE 產生一個巨大的私鑰集, 然後使用和密文對應的 tag 確定其中一個 ID 或密鑰(通過一個 Hash)。

穿刺時,通過 Hash 該 tag 來確定私鑰,然後銷毀它。

但是一個夠用的私鑰集相當大,假設一個私鑰大小爲 64b,我們期望有 2^32 個私鑰可以用, 那最終儲存這個密鑰集我們需要 274877.907 MB。

這是不現實的。

但我們可以通過 HIBE 來懶惰式的生成私鑰,這可以有效的將最壞情況下的密文尺寸降低一半。

Bloom Filter Encryption scheme

BFEBloom Filter Encryption5 是 BTE 的一個有趣變種。

它使用一個 Bloom Filter 來降低 tag 的碰撞率,代價是較快的空間使用速度。

  • 加密時,使用 IBBEIdentity Based Broadcast Encryption 將消息廣播加密 給 bloom filter 選擇出的 k 個 ID。
  • 解密時,使用 k 個 ID 中其中一個可用的 ID 進行解密,若均不可用則解密失敗。
  • 穿刺時,將這 k 個 ID 對應的私鑰銷毀。

無論如何,基於 IBE 的可穿刺加密仍有相當大的私鑰尺寸。那,可不可以再給力點?

Non-Monotonic ABE scheme

照例我們先簡單介紹一下 NM-ABENon-Monotonic Attribute based encryption6

ABE 就像 IBE,但它使用屬性Attribute 而不是 ID 來進行加密。 KPKey Policy-ABE 是指是加密時爲密文指定屬性,只有符合某個規則的私鑰可以解密。

而一般 ABE 的規則是單調的,即是說只有 And 和 Or 兩種規則。 非單調性 ABE 的有趣之處是支持 Not 規則。

有了 NM-ABE,構建可穿刺加密變得非常直觀。加密解密時和普通 ABE 一樣。 穿刺時,只需在私鑰規則中增加一條 Not(tag)7

至此,該方案的私鑰大小只會隨着穿刺次數線性增長,而不會爆炸。

Binary Tree Bloom Filter Encryption?

我們可以將 BTE 和 BFE 結合, 利用 HIBBEHierarchical Identity Based Broadcast Encryption 構造出 BTBFEBinary Tree Bloom Filter Encryption。

理論上這可以將 BFE 的私鑰尺寸降得更低,但我沒有找到合適的 HIBBE 方案實現它。

Perfect?

我們介紹了數個在限制情況下保證前向保密的加密方案,看起來似乎足夠讓人們戴着鐐銬跳舞。

如果這些方案足夠高效,是不是意味著我們再不需要握手,無延遲的 0-RTT 通信可以全面推廣呢?

很遺憾並不是,這些方案只能保證“前向保密”。

而基於會話的方案除了前向保密之外還保證了“弱後向保密”, 即在泄漏長期私鑰時,若敵手在之後沒有參與協議,那協議仍能保證之後密文的保密性。

當然,這一切在量子敵手面前都毫無意義。


  1. Clarify status of subkeys with certification use
  2. Hierarchical Identity Based Encryption
  3. Binary Tree Encryption: Constructions and Applications
  4. 0-RTT Key Exchange with Full Forward Secrecy
  5. Bloom Filter Encryption and Applications to Efficient Forward-Secret 0-RTT Key Exchange
  6. Encryption with non-monotone access structure
  7. Forward Secure Asynchronous Messaging from Puncturable Encryption

by quininer kel at September 12, 2018 09:23 AM

August 31, 2018

ヨイツの賢狼ホロ

Sailfish OS 移植中(2)- droid-hal 和根文件系统

Droid HAL,对应 HADK 文档的第七,第八和第十章 _(:з」∠)_

为新设备生成必要的模板

进入 Platform SDK (sfossdk),引入需要的环境变量(如果汝没有写进 bash_profile 或 bashrc 的话)。

接下来就是依样画葫芦啦 😂

PLATFORM_SDK $

cd $ANDROID_ROOT
mkdir rpm
cd rpm
git init
# 引入必要的 submodules (例如这里的 dhd)
git submodule add https://github.com/mer-hybris/droid-hal-device dhd
# 必要的替换成汝的手机型号和品牌
sed -e "s/@DEVICE@/shamu/" \
-e "s/@VENDOR@/moto/" \
-e "s/@DEVICE_PRETTY@/Nexus 6/" \
-e "s/@VENDOR_PRETTY@/Motorola/" \
dhd/droid-hal-@DEVICE@.spec.template > droid-hal-shamu.spec
# 然后提交到汝自己对应的 git 仓库中
# 最好提前检查一下新生成的文件中的内容
git add .
git commit -m "[dhd] Initial content"
# 如果汝偏好使用 SSH ,换成 git@github.com:myname/your_repo_name
# 例如 git@github.com:myname/droid-hal-shamu
# 以及别忘了把 myname 换成汝自己的用户名,下同……
git remote add myname https://github.com/myname/droid-hal-shamu.git
git push myname master
cd -
# 下面的其实也差不多 😂
mkdir -p hybris/droid-configs
cd hybris/droid-configs
git init
git submodule add https://github.com/mer-hybris/droid-hal-configs \
droid-configs-device
mkdir rpm
sed -e "s/@DEVICE@/shamu/" \
-e "s/@VENDOR@/moto/" \
-e "s/@DEVICE_PRETTY@/Nexus 6/" \
-e "s/@VENDOR_PRETTY@/Motorola/" \
droid-configs-device/droid-config-@DEVICE@.spec.template > \
rpm/droid-config-shamu.spec
# Please review rpm/droid-config-shamu.spec before committing!
git add .
git commit -m "[dcd] Initial content"
# Create this repository under your GitHub home
git remote add myname https://github.com/myname/droid-config-shamu.git
git push myname master
cd -
rpm/dhd/helpers/add_new_device.sh
#  On Nexus 6 the output of the last command is:
#  Creating the following nodes:
#   sparse/
#   patterns/
#   patterns/jolla-configuration-shamu.yaml
#   patterns/jolla-hw-adaptation-shamu.yaml
cd hybris/droid-configs
COMPOSITOR_CFGS=sparse/var/lib/environment/compositor
mkdir -p $COMPOSITOR_CFGS

把手机连接到电脑上,从日志输出中找到汝的触屏是哪一个设备(例如 /dev/input/event0), 然后新建一个 $COMPOSITOR_CFGS/droid-hal-device.conf :

# Config for $VENDOR/$DEVICE
EGL_PLATFORM=hwcomposer
QT_QPA_PLATFORM=hwcomposer
# 用汝获得的 /dev/input/event 替换 /dev/input/event0,还有记得这是一行
LIPSTICK_OPTIONS=-plugin evdevtouch:/dev/input/event0 -plugin evdevkeyboard:keymap=/usr/share/qt5/keymaps/droid.qmap

然后继续建立 Git 仓库:

git add .
git commit -m "[dcd] Patterns and compositor config"
git push myname master
cd -
mkdir -p hybris/droid-hal-version-shamu
cd hybris/droid-hal-version-shamu
git init
git submodule add https://github.com/mer-hybris/droid-hal-version
mkdir rpm
sed -e "s/@DEVICE@/shamu/" \
-e "s/@VENDOR@/moto/" \
-e "s/@DEVICE_PRETTY@/Nexus 6/" \
-e "s/@VENDOR_PRETTY@/Motorola/" \
droid-hal-version/droid-hal-version-@DEVICE@.spec.template > \
rpm/droid-hal-version-shamu.spec
# Please review rpm/droid-hal-version-shamu.spec before committing!
git add .
git commit -m "[dvd] Initial content"
# Create this repository under your GitHub home
git remote add myname \
https://github.com/myname/droid-hal-version-shamu.git
git push myname master

全部完成以后,别忘了修改汝自己的 manifest 文件,加上相应的仓库:

<project path="rpm/"
    name="myname/droid-hal-hammerhead" revision="master" />
<project path="hybris/droid-configs"
    name="myname/droid-config-hammerhead" revision="master" />
<project path="hybris/droid-hal-version-hammerhead"
    name="myname/droid-hal-version-hammerhead" revision="master" />

等汝的移植工作完成而且可用的时候,可以去向官方的 manifest 仓库提交一个汝的设备的 Pull Request :-)

打包 droid-hal-device

其实只有一步:

rpm/dhd/helpers/build_packages.sh

以后要是汝改了什么地方的话,记得再运行一次就 OK 啦~

如果汝遇到了 Installed (but unpackaged) file(s) found 错误的话,记下不在包中的文件, 把文件名添加到 rpm/droid-hal-$DEVICE.spec 的 %include 之前,例如:

%define straggler_files \
/init.mmi.boot.sh \
/init.mmi.touch.sh \
/init.qcom.ssr.sh \
/selinux_version \
/service_contexts \
%{nil}

然后在 droid-configs/patterns/jolla-hw-adaptation-$DEVICE.yaml 中加上 "- droid-hal-$DEVICE-detritus"。

再次打包就好。

打包 Sailfish OS 的根文件系统

生成 KickStart 文件:

rpm2cpio droid-local-repo/$DEVICE/droid-configs/droid-config-$DEVICE-ssu-kickstarts-1-1.armv7hl.rpm | cpio -idmv

HA_REPO="repo --name=adaptation-community-common-$DEVICE-@RELEASE@"
HA_DEV="repo --name=adaptation-community-$DEVICE-@RELEASE@"
KS="Jolla-@RELEASE@-$DEVICE-@ARCH@.ks"
sed \
    "/$HA_REPO/i$HA_DEV --baseurl=file:\/\/$ANDROID_ROOT\/droid-local-repo\/$DEVICE" \
    $ANDROID_ROOT/usr/share/kickstarts/$KS \
    > $KS

使用 MIC 生成安装包:

# 选择一个版本,例如写这篇文章时最新的 2.2.0.29
RELEASE=2.2.0.29
# 设置一个自定义名称,用来区分汝自己的不同版本,不过不能用 "."。
EXTRA_NAME=-my1
# 生成需要的 patterns 。
# 最后一定会抛出一个 AttributeError: "'NoneType' object has no attribute 'px_proxy_fa..
# 异常,可以安全的忽略掉。
hybris/droid-configs/droid-configs-device/helpers/process_patterns.sh
# 用 Mic 生成根文件系统安装包
sudo mic create fs --arch=$PORT_ARCH \
    --tokenmap=ARCH:$PORT_ARCH,RELEASE:$RELEASE,EXTRA_NAME:$EXTRA_NAME \
    --record-pkgs=name,url \
    --outdir=sfe-$DEVICE-$RELEASE$EXTRA_NAME \
    --pack-to=sfe-$DEVICE-$RELEASE$EXTRA_NAME.tar.bz2 \
    $ANDROID_ROOT/Jolla-@RELEASE@-$DEVICE-@ARCH@.ks

如果一切 OK 的话,汝就会在汝的 $ANDROID_ROOT 文件夹下发现一个 sfe-$DEVICE-$RELEASE$EXTRA_NAME 文件夹里面放着一个 sfe-$DEVICE-$RELEASE$EXTRA_NAME.zip ,这就是汝的根文件系统的刷机包咯~

刷入测试

进入汝手机的第三方 Recovery (例如 TWRP),把汝 hybris 对应的 LineageOS 的底包 和刚刚生成的根文件系统复制到汝的手机上并安装。或者 adb sideload 也行。

  • 如果一切都 OK 的话,汝应该能见到 Sailfish OS 的开机画面,试着完成设置看看吧~
  • 要是不行的话,在看看 HADK FAQ 找找问题, 或者上 IRC 问一下其他人吧 _(:з」∠)_

在启动早期和手机建立连接

如果手机和电脑相连以及 hybris 能成功加载的话,汝的电脑上应该会多出一个网络适配器, 和 Android 启动 USB 网络共享类似。 GNU/Linux 的话可以通过 ip 命令确认。

可以用 telnet 连接到早期启动阶段的手机:

telnet 192.168.2.15

如果连接不上,可能是 Sailfish OS 已经在加载但是显示不出界面。 这个时候 telnet 的端口是 2323。

连接上以后,可以用 devel-su 命令进入 root shell:

devel-su

然后就可以通过 dmesg 或者 journalctl 等命令查看日志啦,在调试的时候可能会派上用场。

如果反复重新启动

反复重新启动有这么几种情况:

  • 启动后立刻重启(或进入 Recovery 模式):大概是 SELinux 的问题 _(:з」∠)_

    所有基于 CyanogenMod 11 以后(Android 4.4)的移植都必须停用 SELinux。 尝试在编译内核的选项中停用 SELinux ,或者设置 CONFIG_SECURITY_SELINUX_BOOTPARAM=y , 然后在启动参数中加上 selinux=0 (启动参数通常在 $ANDROID_ROOT/device/$VENDOR//BoardConfig.mk 中的 BOARD_KERNEL_CMDLINE 中)

  • 启动后数分钟后重启:尝试停用 ofono 服务,启动时尽快通过 telnet 进入 root shell:

    ln -s /dev/null /etc/systemd/system/ofono.service

  • ……

by ホロ at August 31, 2018 04:00 PM

August 27, 2018

Lainme

在Linux上设置天河二的VPN和SSH

天河二的VPN用的是Hillstone的VPN,Windows和Mac都有客户端,但给出的Linux使用说明通常都是用命令行的VPNC,也没有配置路由的方法(否则VPN是全局的)。另外在SSH连接时也需要一些额外的设置,否则不是很好用。这里记录一下用Network Manager连接VPN和相关的配置。

VPN连接设置

先安装vpnc和nm插件

pacman -S vpnc networkmanager-vpnc

用Network Manager新建一个vpnc的VPN链接,在Identity栏设置如下:

至此应该可以连接VPN了。但默认情况下,VPN是全局的,也就是所有请求都会通过天河2,导致实际上无法上其它网址。要解决这个问题就需要设置路由。

在刚才建立的连接中切换到IPv4一栏,并设置如下:

SSH设置

由于登录节点IP会实际对应多个服务器,导致指纹校验经常失败。如果不想每次都去动known_hosts的话,可以在.ssh/config里设置如下:

Host tianhe
    HostName [登录节点IP]
    Port [登录节点端口]
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

这样就可以用

ssh tianhe

方便的直接登录了。

by lainme (lainme@undisclosed.example.com) at August 27, 2018 11:44 AM

“见鬼”的Gnome桌面冻结问题

最近去北京开会的时候,我的Archlinux突然不正常了。桌面每隔几分钟就要停止响应一会,键盘鼠标都不能动,甚是奇怪。这个系统已经挺久没更新了,之前也没法这个问题,怎么忽然就罢工了呢?Windows是没有故障的,所以硬件问题先排除了。用journalctl查了查日志,也没发现任何有价值的信息。

既然很久没更新那就先更新吧,虽然没滚挂,但问题也没解决。最开始,我感觉是显卡驱动的问题,在google上用“archlinux freeze”等关键词搜索了一番,确实找到很多症状和我很像的帖子(https://bbs.archlinux.org/viewtopic.php?id=236686),然后把里面提到的方法都试了一遍:

  • 改用Xorg登录,鼠标不怎么卡了,键盘依旧。
  • 改用LTS的内核
  • 改用旧版的驱动
  • 禁用TLP等电源管理程序
  • 禁用swap
  • 加内核参数intel_idle.max_cstat=0,1,2,都试了一遍

结果该怎么卡还是怎么卡,前前后后还花了不少时间。

最后想到有些帖子提到了无线网可能会引起问题,孤注一掷之下禁用了Networkmanager,居然奏效了……不过问题来源找到了,然而并没有解决方案。因为我的无线网卡是intel的,就对着iwlwifi各种折腾,当然都是徒劳的。当时想着这简直是要治我的网瘾啊……

就在我准备放弃治疗的时候,忽然发现Gnome的通知区域每隔一会就提示Gmail收信失败,非常的可疑,于是用gnome tweak tool把“gnome email notifications”插件给关掉了,再打开网络,世界终于清净了。

这样看来是由于开会地点的网络不好,代理可能也有点慢,导致插件频繁查信失败引起的卡机。但是dropbox等同样连不上却不会卡,是插件自身的问题么?于是我跑去插件的github主页看了看,发现它前段时间有个提交——“ Removed synchronous calls to prevent gnome shell from freezing up. ”。真相大白。

by lainme (lainme@undisclosed.example.com) at August 27, 2018 08:12 AM

August 26, 2018

Felix Yan

Arch Linux 社区中不成文的约定(一)

作为一个年轻和小众的社区(咳咳),Arch Linux 社区中有着许多不成文的约定。本文希望通过稍稍讲解一二,来消除一些新人们常常感到的困惑。由于预感到日后可能还会有更多问题,暂且认为这是系列里的第一篇 😛

  • 修理“坏”包和提醒更新

仓库里一般的包可能有一个或多个维护者,也可能没有维护者,成为“孤儿”。如果坏掉或者过期的包是一个孤儿,申请从仓库里删掉是更有效的方法,这样它可以在 AUR 找到新的主人。发邮件到 aur-general 解释一下情况,并表明自己或者别人想维护它,一般都会很快得到解决。

如果“坏”包有维护者,但他太忙了呢?这种情况下,这个包很可能已经在 Bug Tracker 里泥足深陷。这种时候可以通过帮维护者一个小忙的方式来促进问题的解决。如果有人找到了上游 Bug Report,并且上游已经修复提供了 patch 的话,可以考虑把 patch 贴到 Arch 的 Bug Tracker;如果这件事已经有人做了,可以修改 PKGBUILD 打上 patch,然后把改好的 PKGBUILD 贴上去;如果连 PKGBUILD 都有人准备好了,而且过去了一段时间的话,可以考虑把这份准备好的 srcpkg 或者 diff 直接发邮件给维护者,附上简短的感谢和一个笑脸 🙂

同样的道理,如果一个过期包已经被标记过期很久了,也可以准备一个新版的 PKGBUILD 发邮件给维护者。

  • 提供旧版软件包

因为懒得迁移各种各样的原因,你可能希望某些软件包的新旧版本同时提供。在 Arch 的实践中,这种情况被尽量避免了。把无法迁移的古老软件删掉还是提供一个旧版库让古老的软件能用,一定程度上取决于维护者对这个古老的软件有多坚持。

另一个考虑的因素是上游如何看待新旧版本。有的软件新版发布后,旧版就不再维护了,这种情况下 Arch 通常尽力避免成为新的上游。而有的上游则同时维护多个版本,Arch 这边根据其他软件的依赖情况,可能会有选择性地同时维护几个。

  • 第三方 patch 能不能加上

Arch 对 patch 的态度比较保守,在规则里写的是一般只有修复编译和主要功能的上游已经接受的 patch 会考虑。在实践中,有些时候没有这么严格,比如开发者自己写的 patch 一边提交给上游,还没等答复,一边就加到了包里。因为 Arch 打包并没有 Review 过程,实际上加了什么 patch、靠不靠谱就全靠开发者自己掂量了。

一般来说,被上游明确拒绝的功能性 patch 是不怎么会考虑的。修复一个特定问题,尤其是影响比较大的问题的上游有点意见的 patch 有可能会被考虑。修复一个安全问题的 patch 经常会被接受,尤其是已经拿到 CVE 号的。

  • 使用上游二进制 vs 从源码编译

一般常识是发行版们倾向于从源码编译一个软件,理由包括确保二进制真的来自这份源码、尽量使用系统中的动态链接库而不是静态编译一份以满足安全更新和体积方面的考虑、进行必要的修改等。

但是因为太懒一些问题,即使是在 Arch 官方仓库里也直接重新打包了一些上游的二进制。这些问题包括:源代码不开放(nvidia、flash 等)、编译过程中会去下载东西,而且不容易解决(dart、一些 java 软件等)。

  • 文档是否打包

现在互联网十分发达,开发者们查询文档通常都是直接上网搜索。在这样的背景下,是否打包软件包的文档(通常都是开发文档)成为了一个问题。早些时候的包开启文档较多,如果太占体积还会考虑拆一个单独的文档包。后来才增加的新包则很多都没有启用文档,除非被用户要求提供文档才考虑这件事。

by Felix Yan at August 26, 2018 05:33 PM

August 07, 2018

ヨイツの賢狼ホロ

和 Android 玩耍的那些日子

也可以说是 root Android 的过程 ……

最近 Android Pie 发布了,于是随便写写杂感 (雾)

Android 2.x :蛮荒时代和 ZergRush

(记得那是咱自己的第一部 Android 手机, Motorola Defy XT535) 拿来的时候还是 Android 2.3,当然是不能用现在的审美去看 Gingerbread 的界面啦……(不过 2.3 时期的 TouchWiz 是真的比 Motorola 那几乎没怎么改 的漂亮,虽然现在……)至于当时怎么想到去 root 了呢,动机大概是 想不起来了,大概只是为了移除某些系统应用而已 😂 。2.x 时期最著名的 大概就是 ZergRush 漏洞了,通吃大多数 2.2-2.3 版本……

后记:那 XT535 后来收到了 4.0.4 更新,然后又坚持了两年,最后掉了……

Android 3.x :没用过

于是跳过 😂

Android 4.x:CyanogenMod,CWM,CF-Auto-Root,Superuser,SuperSU 和 Xposed

和 XT535 同时入手的其实还有 Samsung Galaxy Tab2,当时那上面已经是 Android 4.0.3 了。虽然三星魔改的很厉害,但是乍一看就和旁边的 2.3 不一样不是? 😂

以及到了 4.0 时代, ZergRush 就不能用咯。当时咱还处于一种啥都不懂的状态 (当然现在也只是懂些皮毛而已啦),上网搜索到了一个叫做 CF-Auto-Root 的家伙。 ( https://autoroot.chainfire.eu/

用过三星手机的应该都知道,人家是没有 fastboot 模式的,但是有个 Download 模式 (因为早些时候的 Download 模式上有个施工造型的 Android 机器人,所以有时有人 也把它叫做挖煤模式)。然后需要用到一个叫做 odin 的工具刷机。

大概长这样

当时 root 手机的方法,基本上都是先把一个称作 Recovery 的东西放在手机上, 然后利用一个小 zip 包把 su 可执行文件放在对应的位置上,再安装一个应用来控制 root 权限就 OK 啦~

其实汝的手机上是有一个 Recovery 的,重置或者系统更新的时候会需要。不过 官方的 Recovery 通常功能不多而且会检查更新的签名,所以就需要刷入一个第三方 的 Recovery 来实现更多功能。比较著名的第三方 Recovery 的话,以前有 ClockWorkMod (简称 CWM),现在有 Team Win Recovery Project(简称 TWRP)

而 CF-Auto-Root 把这些过程简化到一个供 odin 刷入的 tar 包中,于是咱当时就用它 root 了。

至于 Root 权限管理应用的话,当时比较流行的是 Superuser (忘了是谁开发的了) 和 SuperSU (Chainfire,也是 CF-Auto-Root 的作者,虽然已经是前开发者了)。两个当时都很优秀 (虽然现在都凉了)

然后有一次咱尝试系统加密玩脱了,系统进不去了,于是死马当活马医的装了个 CyanogenMod 进去, 打开了新世界的大门(雾)。

以至于现在咱用不习惯任何非类原生 Android 界面了 😂

大概是 4.4 时期,有了个叫做 Xposed 的东西。大概是一个通用的系统框架, 可以实现上至系统界面调节下到应用权限控制等一系列千奇百怪的功能。咱当时也 装了不少的模块用了一阵子。

后记:那个 Galaxy Tab 2 一直坚持到 CyanogenMod 13.0 ( Android 6.0.1),性能是真的不行了, 于是就闲置在家里,然后屏幕外面的玻璃被压碎了……

Android 5.x - 6.0 时期:没啥特别的

大概是 15 年的时候买了台 Galaxy A8 ,然后兴致勃勃的用以前的方法 root 的时候, 遇到了一个叫做 KNOX 的神奇大坑。

4.3 以前的三星手机刷入自定义操作系统以后,启动时会显示一个感叹号三角形, Download 模式里也会记录刷入了自定义操作系统。不过当时可以很轻松的去掉 (例如用 Triangle Away)。后来有了 KNOX 以后,修改操作系统的行为会导致 KNOX WARRANTY VOID 变成 0x1 从而失去保修。以及某些功能将无法使用(例如 My Knox 和后来的 S Health,Samsung Pay 等等),而且据说因为这是个硬件 设计于是不可逆……

自此咱就再也没买过新的三星 Android 手机……

如果不考虑那些的话,还有一个问题,修改过系统分区的话,就不能收到厂商的系统更新了。 于是不动 /system 的 system-less root 方法应运而生,典型的方法是修改内核映像(boot.img)……

以及那时候自从 ART 代替 Dalvik 以后, Xposed 适配新版本的速度慢了下来。

Android 7.x - 8.x 时期:SafetyNet,Magisk ,LineageOS 和 Project Treble

7.0 一上来就是猛料,比如所谓的 SafetyNet ,反正咱现在也没搞清楚这是啥…… 以及不知道是不是 7.0 的 Xposed 鸽了太久的原因, 有人写出了 Magisk。 和 Xposed 大概具有类似的功能,但是不动系统分区,于是接收 OTA 更新能稍微容易一点…… 以及介于 Superuser 年久失修和 Chainfire 宣布不再参与 SuperSU 开发以后, Magisk 还有一个 Root 权限管理程序。总之也有不少人用啦~

这个时期也出现了很多不必须 root 权限的系统管理工具,例如 Brevent 和 AppOps, 它们都是通过 adb 启动一个外部脚本来协助完成某些任务,给了不 root 或没法 root 手机的人一些选择。

以及那年商业化失败的 CyanogenMod 宣布关闭,不过过了一阵子以后又有一群有志之士 成立了 LineageOS 社区,目前正在活跃开发中。

Oreo 时有了 Project Treble 和 A/B 无缝系统更新,前者把 Android 系统的部件和 OEM 定制的部分分开,声称能使 OEM 适配最新版 Android 系统的速度提升。后者可以做到更新时可以 继续使用设备,而且不容易坏(笑)。

对于喜欢搞机的开发者们呢?看起来 Project Treble 使第三方 ROM 移植起来稍微容易了一些, 比如汝可以在 xda 上找到 Treble 兼容的 AOSP 和 LineageOS 的通用系统映像(GSI),然后 刷入进汝支持 Treble 的手机上,大多数的功能应该都能运作。 不过 A/B 无缝更新可能就是个 美丽的麻烦了,因为没有了单独的 Recovery 分区,所以给这种手机适配 TWRP 的进度似乎慢了下来。

Android P ?

咱现在用的是 Nokia 7 plus ,当时有官方的 Android P Beta 支持,于是咱就这么用了下去。 除了前几个 DP 每次都要全新安装以外其实海星 😂 。 Magisk 在 Android P 上也比较稳,原来 不少需要魔改的功能也或多或少的集成进系统里了。然而以前那个愉快的折腾的日子似乎回不来了呢……

PS: #when_I_buy_a_phone

如果咱要换手机的话:

  • 能解 Bootloader 锁的通常很快都会有 LineageOS 适配,优先考虑。
  • 上面一条不满足的话,如果是类原生系统的体验也可以考虑。
  • 最好不是异形屏幕(就是所谓的刘海啥的),屏幕比例最好是普通的 16:9/16:10 一类。
  • 有物理键盘最好。

于是现在(这篇文章完成的时候)这么下来,貌似就不剩几个了吧 😂

by ホロ at August 07, 2018 04:00 PM

August 04, 2018

百合仙子

XZ2C: 没有 root 的日子(也还过得去)

本文来自依云's Blog,转载请注明。

现在手机两三年不换新,日子就没法过了!所以我明知没有 root 还是买了 Sony Xperia XZ2 Compact(XZ2C),用于替换之前使用的 Z5C。

嗯,还是 Compact 版本。尤其是尝试在地铁上操作小米 Note 3,结果发现为了避免自己跌倒或者手机被摔地上,我不得不使用嘴唇来点击部分区域的时候,我决定绝不放弃能够安稳地握紧的 Compact 版本了。

啊,我知道有单手模式的!XZ2C 底部那三个虚拟键的位置,向左或右滑,就可以启动了,很方便!然而,最需要此功能的小米 Note 3,使用的是实体触摸按键,中间还是个凹下去的指纹识别,我很难成功启动单手模式。而且由于手机过大,这个动作操作的时候,手只能握住手机的下半部分,重心还是悬在外边,列车适时地颠一下说不定就会脱手而出了。

XZ2C 比 Z5C 重了很多,背面还是弧面的,手感意外地很棒呢。指纹识别在背部,拿在手里的时候倒是很好按,也不会有小米那握不稳的问题。不过你们懂的,放桌面上的时候,不拿起来就按不了。而且双击唤醒屏幕被禁用了(除非已抬起手机,屏幕已显示时间),密码解锁也并不方便。双击唤醒大概还是可以启用的吧,不过 Z5C 拿在手里走路的时候,屏幕经常被会意外点亮不说,还会一不小心就把锁屏时钟给换了样式。所以就不用啦。

这指纹解锁速度比起 Z5C 的侧面那个小的,快了非常多。而且可以配上很好看动画效果啊!就是 Sony 宣传片里的那种,不过它竟然不仅仅是在主屏幕上呈现,在任何应用打开的时候也能出现呢。

哦还有,XZ2C 充电、看视频时都不会像 Z5C 那样烫了~

系统是 Android 8 Oreo。最棒的莫过于通知管理了!之前我一直希望某些不重要的通知出现,但是不要发出声音(比如微信的「加好友」通知),但是只能寄希望于应用本身提供这种设置。现在不用啦,系统通知管理可以直接设置了。而且应用还可以给通知分门别类,然后用户按需要给不同类别的通知不同的设置(比如 Telegram 就能精确到会话)。在通知出现的时候,长按选择「所有类别」打开设置的时候,对应的类别也会闪动一下,不用用户去猜这是哪个类别的通知。

其次是应用图标的长按功能,可以查应用信息,可以访问应用提供的快捷入口,也可以把这些入口添加到主屏幕,或者添加该应用的小部件。而且应用信息里可以直接打开 Google Play 了!我之前都只好去搜索应用名的。在 YouTube 上看到有人直接把应用图标拖到一个控件上就能打开商店,然而我并不知道那是什么应用。

还有很重要的一点是:运行很流畅!不只是我以前新手机刚到手的那种流畅,而且装个应用一两秒、开机启动完了我还没反应过来,超出了预期好多!当然并不是说开机刷一下子就完了,而是我没想到 Android 的开机能有这么快。

啊,最后进入正题:没有 root,只能找替代了。

首先是权限管理。Android O 的权限管理已经强大了不少,而且我已经把大部分国内应用转移到小米上了,所以还好吧。

时间显示秒。这个功能,在「系统界面调谐器」里已经有选项了。长按下拉通知出现的齿轮图标,直接它转动起来,然后就可以在「设置」里看到「系统界面调谐器」了。

互联网访问。放弃修补好的 fqrouter,发现现在的主流软件其实也挺不错的。只是偶尔需要手动切换节点了。另外就是 SSRR 这个东西,连接上的时候如果切换节点,有大概率导致网络故障(DNS 解析异常等)。需要重启手机才可解决。

绿色守护。被黑阈取代了。但我发现其实需要黑阈掉的应用也不多。我目前添加到黑阈名单里的有微信、彩云天气、企业微信、Inoreader、形色、高德地图等,基本上都是国产应用。微信一大堆服务在后台跑着,虽然支持 FCM(GCM 升级版),被黑阈干掉之后会收不到新消息(而 Telegram 和 Twitter 什么的好像都能)。

Wi-Fi adb。这个其实开机后接 USB 线,adb tcpip 5555 一下,就可以一直通过网络连接了。黑阈等需要通过 adb 执行命令的应用,也可以自行调用。

「去你大爷的内置浏览器」。现在火狐支持 Custom Tabs 了呢,所以大部分应用都能够调用火狐了。虽然 Custom Tabs 里是不加载扩展的,不过再选择在火狐中打开就可以了,大部分内容都缓存了,再加载一遍也是挺快的。Inoreader 有「webview」、「chrome tab」和「外置浏览器」三种方案,其中「chrome tab」其实就是「Custome Tabs」。Google 自家的大部分应用也会使用「Custom Tabs」,除了「Google 搜索」仍然坚持调用 Chrome,不过它可以设置为在外置浏览器中打开。「新闻与天气」也需要设置为在外置浏览器中打开,不然因为没加载扩展,会有大量广告辣眼睛的。微信当然是无解的,只能在需要时浪费些流量和时间手动选择在浏览器里再开一次了。

五笔输入法。这个之所以和 root 权限有关,是因为之前我都是拿 XPrivacy 禁用输入法的联网能力,所以什么百度啊触宝啊,我都能接受。现在没法断它网了,我自然是不会再用不开源又非系统自带的输入法了,尤其是触宝这种,一联网立马弹出好几个广告的。一开始找到叫「五笔输入法」的试验品。功能很简洁,但不愧是试验品,不能输入中文标点也就算了,一次输入英文还不能超过四个字母……然后想起 trime——rime 在 Android 平台上的版本。使用感受是:我从未经历过如此简单的码表导入!把我的 fcitx 码表导出,然后一行命令调整一下格式就可以了!然后把码表和五笔输入方案推到内部存储设备上,「部署」一下,我终于可以在手机上使用我自己的码表了!

ssh、备份和调试。这个没办法。通过 termux 能很方便地启动个 sshd,但是权限很有限,比如只能只读访问外置 SD 卡。所以我通过 adb 起了一个 dropbear 来同步这些内容。系统分区没办法访问,只能依靠 Google 和 Sony 的在线备份功能了。

啊对了,Sony 的「Xperia 换机助手」真是垃圾啊。首先,很多应用不能带数据迁移。其次,我这里一次传输只能传一项内容!不然就会出现连接错误。好不容易在论坛上找到的方案,一次只传少量数据,所以短信和通讯录什么的都可以过来,小一些的应用也能过来。大的应用就没办法了。

还有些小功能。通话振动。这也是个 Xposed 模块,就是电话接通的时候振动一下。属于锦上添花的功能,没了就没了吧……还有 Xposed Torch 也是。不能长按音量键开手电筒,那就划开通知点按钮好了。微X模块,很好用,但也不是非要不可。

整理下来,因为没有 root 而失去的功能并不是很多,最严重的是没有完整、增量的备份了。其它的,相对于运行流畅所带来的体验提升,其实并没有那么重要。这次我真的是换了手机才知道旧手机已经全面卡顿了……

by 依云 at August 04, 2018 11:37 AM

Phoenix Nemo

Office Service Router 解决方案:Arch Linux in RAM

一直把自己在办公室的 PC 保持开机用于连回办公区、存取数据工作需求。由于最近办公室所在的写字楼要全馆断电检点,所以诞生了构建一个 Service Router 的想法。

思路

运行在内存里对于 Linux 系统来说是完全可能(而且简单)的事情。

最直接的想法就是使用内核 hook 在启动时复制根分区到内存盘然后挂载内存里的数据作为根分区即可。

设备的话,设置 Power on AC 即可通电自启动。

ramroot

作为一只懒卷,这种简单的事情当然先顺手搜索下啦。然后就发现了几乎完美的解决方案——ramroot

ramroot 通过加入内核 hook 然后自动在内存建立 zram 分区,同步根分区数据再启动。还可以在启动时选择是否启动进内存,正好解决了所有的需求。

实现

硬件选择是一台便宜的 Intel NUC,安装两根 4GB LPDDR3 低压内存和一块 120G 2.5 SSD。虽然说起来其实并不需要 SSD(因为数据全部都在内存里,速度比 SSD 更快)但是毕竟日本多震,还是为数据安全着想。毕竟硬盘坏了的话内存系统也无法启动了。

当然如果有集成 32GB eMMC 的小型 PC 的话也是好的选择。

正常安装完 Arch Linux 系统,安装 openssh 和各种必要的服务程序,修改配置文件,然后安装 ramroot 并执行

1
# ramroot enable

此时先别急着重启,先把不需要的包、缓存等文件(/var/cache)删除,保持最小化的根分区。然后再重启。便可看到加载内核 hook 时的提示是否进入内存系统,默认超时后就会自动复制根分区到内存啦。

由于整个系统是运行在内存中的,所以完全没有等待读盘的时间。整个系统的响应速度非常快。限制是内存不够大的话运行一些业务会比较捉襟见肘,而且这样低功耗、低发热的 SoC 处理性能也只能运行一些轻型任务。

下面是一些 IO 性能测试

1
2
3
4
5
6
7
8
9
10
11
12
# ioping -s 1G /
1 GiB <<< . (ext4 /dev/zram0): request=1 time=1.04 s (warmup)
1 GiB <<< . (ext4 /dev/zram0): request=2 time=1.04 s
1 GiB <<< . (ext4 /dev/zram0): request=3 time=1.04 s
1 GiB <<< . (ext4 /dev/zram0): request=4 time=1.04 s
1 GiB <<< . (ext4 /dev/zram0): request=5 time=1.04 s
1 GiB <<< . (ext4 /dev/zram0): request=6 time=1.04 s ^C

--- / (ext4 /dev/zram0) ioping statistics ---
5 requests completed in 5.18 s, 5 GiB read, 0 iops, 988.4 MiB/s
generated 6 requests in 7.20 s, 6 GiB, 0 iops, 853.8 MiB/s
min/avg/max/mdev = 1.04 s / 1.04 s / 1.04 s / 550.7 us
1
2
3
4
5
6
# ioping -RD /

--- / (ext4 /dev/zram0) ioping statistics ---
530.0 k requests completed in 2.49 s, 2.02 GiB read, 212.9 k iops, 831.7 MiB/s
generated 530.0 k requests in 3.00 s, 2.02 GiB, 176.7 k iops, 690.1 MiB/s
min/avg/max/mdev = 3.44 us / 4.70 us / 69.0 us / 1.39 us

可以看到系统根分区在 zram 里,经过压缩因此 IO 带宽受到了 CPU 处理性能的限制。但是 IOPS 依然高得爆表,对比一下 Intel Optane 900P 的 IOPS 性能:

1
2
3
4
# ioping -RD /
--- / (ext4 /dev/nvme0n1p1) ioping statistics ---
163.1 k requests completed in 3.00 s, 56.5 k iops, 220.8 MiB/s
min/avg/max/mdev = 11 us / 17 us / 114 us / 4 us

炒鸡厉害对不对!

不过需要做永久性修改的话还是要下面的方法之一

  • 重新挂载磁盘(虽然并不麻烦)然后手动修改配置文件
  • 重新挂载磁盘然后 rsync zram 到磁盘(方便但是可能会多一些不必要的东西)
  • 重启进入磁盘系统然后运行修改(需要物理接触)

硬件设置

进入系统 BIOS 设置,开启 Power on AC 或设置 Power Failure 后的操作,选择为 Power On (默认一般是 Last State)。

关闭系统、拔出电源,或意外断电后,再接入电源即可自动开机引导系统。因为数据本身就只在内存中,除了运行中的临时更改会丢失,系统和硬盘本体都是安然无恙的。

再也不担心办公室断电检查啦。

大概就是这样。

一直把自己在办公室的 PC 保持开机用于连回办公区、存取数据工作需求。由于最近办公室所在的写字楼要全馆断电检点,所以诞生了构建一个 Service Router 的想法。

August 04, 2018 08:47 AM

August 03, 2018

Phoenix Nemo

通过 SSH 修正安装有 GPU 的 HPE Proliant 服务器

由于越来越多的渲染、压制等需求,托供货商的关系搞来一台带有独立显卡的 HPE 服务器。经过几番折腾(包括特别奇怪的 LS26-C14 电源线)麻烦了帮忙托管的数据中心的大兄弟好几回,终于算是上架可以开机了。

登入 iLO,安装许可证,启动 iLO Remote Console,打开电源,一切都很顺利。但是 Console 里显示 Early Initialization… 完成后,突然画面一黑,完全没了动静。

以为 iLO 出了 bug,冷重启好几次都是一样的结果。百思不得其解。

再重启一次。仔细观察了一番发现虽然没了画面,但是 POST Code 还是不断变化的,而且 Virtual Media 指示灯不断在闪烁,说明系统仍在正常运行,只是没有视频输出而已。

因此问题定位在视频输出而非系统硬件。既然这台服务器装了显卡,那么很可能是 PCI-e 初始化后视频输出全部交给显卡处理了。搜索了一下 HPE Community,确实有这样的情况存在。解决方案是通过 BIOS 修改显卡设置为默认集成显卡、备选独立显卡。

尝试在设备初始化阶段进入 BIOS,失败。

联系数据中心远程操作的话,可能要等一段时间。

纠结时随便点开 iLO 的管理页面,突然发现了华点:这货居然支持 SSH。

对啦,HPE 的底层系统几乎都是魔改版 Linux,连他们的 SmartArray 都是 Linux 启动一个 Firefox 浏览器来操作的(X

于是正好在网上搜到一篇通过 SSH 修改 BIOS 视频设置的方法。记录如下。

SSH 进入 iLO

确保 SSH 在 iLO 管理页面中已开启,然后使用 SSH 客户端正常连接:

1
ssh Administrator@10.6.254.121

(ssh 用户名是 Administrator 感觉各种违和)

连接到 Virtual Serial Port

命令很简单:vsp

在 iLO 管理页面重启系统,然后等待初始化完成。如果看到按下 F9 进入 BIOS 设置的提示,不要按下它否则会进入 GUI 模式(于是又去独立显卡了就。

看到 ESC + 9 进入 BIOS Setup Utility 时按下键组合,稍等一会儿应该就可以看到提示符 rbsu>

修改视频设置

命令 SHOW CONFIG VIDEO OPTIONS

显示如下

1
2
3
1|Optional Video Primary, Embedded Video Disabled <=
2|Optional Video Primary, Embedded Video Secondary
3|Embedded Video Primary, Optional Video Secondary

即默认关闭了集成显卡,只用独立显卡(不觉得很蠢吗!

于是修改为第三项,默认使用集成显卡,独立显卡作为备用。

1
SET CONFIG VIDEO OPTIONS 3
1
2
3
1|Optional Video Primary, Embedded Video Disabled
2|Optional Video Primary, Embedded Video Secondary
3|Embedded Video Primary, Optional Video Secondary <=

然后敲 EXIT 退出并重启系统。

安装系统和驱动

至此即可通过 iLO Advanced Console 正常安装操作系统。不过需要注意的是进入操作系统后即便安装了对应的显卡驱动,依然默认使用的是集成显卡。以及 RDP 只能使用软解,无法使用独立显卡加速视频输出。这不影响 Blender 或者 Cinema 4D 等直接操作显卡进行计算的程序,但是会影响直接输出视频到桌面的程序。通过 Teamviewer 则可以强制桌面运行在独立显卡上。

顺便吐槽:Blender 把我的工程材质弄丢了…

由于越来越多的渲染、压制等需求,托供货商的关系搞来一台带有独立显卡的 HPE 服务器。经过几番折腾(包括特别奇怪的 LS26-C14 电源线)麻烦了帮忙托管的数据中心的大兄弟好几回,终于算是上架可以开机了。

登入 iLO,安装许可证,启动 iLO Remote Console,打开电源,一切都很顺利。但是 Console 里显示 Early Initialization… 完成后,突然画面一黑,完全没了动静。

August 03, 2018 06:26 PM

quininer kel

ene

Encrypt on Email

什麼是 ENE?

ENE 是一個實驗性質的、設計用於郵件的、端到端End to End加密工具。 它基本上是我設想中的 OpenPGP 2.0,針對 PGP 的弱點作出了一些改進。

郵件是一個單次、無狀態的通信方案,這意味著爲它設計加密方案無法達到很多常見的安全要求。 例如經常被提到的 完美前向保密Perfect Forward Secrecy。 事實上放寬其中一個限制,我們就可以做到很多, 例如 OpenPGP 實際上有一個有狀態的前向保密方案1

但在這些條件下,我們可以做的事情並不多。

在早期,我們滿足於簡單的 KEMKey encapsulation mechanisms + Encrypt 加密方案, 這些方案缺失了身份認證、完整性驗證。

而在 2018 年,我們可以做得更好。

ENE 將會提供:

  • 認證密鑰交換Authenticated Key Exchange
  • 可否認的郵件認證Deniable authentication
  • 郵件完整性Integrity
  • 具有 Nonce-misuse Resistant 的加密方案
  • 實驗性的 Post-quantum 密碼套件

我們需要認證?

試想一個場景,Alice 向 Bob 發送一個沒有認證的加密消息,Mallory 可以輕鬆的攔截並轉發這個消息給 Bob。 Bob 會認爲這個消息是由 Mallory 發送的。

這是由於缺乏身份認證導致的。

可能很多人會覺得加簡單的增加一個簽名會解決所有問題,但並不是。簡單的簽名方案有兩個缺點

  1. 可否認性2的丟失

    • 當你使用私鑰簽名一段消息時,實際上你背負了一定的法律責任。你將無法否認那段消息出自於你的手筆。
  2. Surreptitious Forwarding Attack3,4

    • Mallory 可以將 Alice 的簽名剝離,使用自己的簽名代替。
    • 可能有人認爲 Sign-then-Encrypt 將會解決這個問題,但若加密方案達不到 CCA5,那它仍無法防止此攻擊。

ENE 有四種認證方案,用戶可以靈活選擇自己想要的安全性。

  1. One-Pass Authenticated Key Exchange6,7

    • 一次通信的認證密鑰交換有良好的性能,因爲它不涉及複雜的簽名算法。
    • 由於只能通信一次,我們仍無法做到完美前向保密。但我們可以做到發送者前向保密,這至少和公鑰加密一樣好。
    • one-pass ake 只提供隱式認證, 這意味著它無法抵抗 KCIKey Compromise Impersonation攻擊8
    • one-pass ake 是 ENE 中最弱的認證方案,但它仍能防禦大多數攻擊。 你應該用它代替早期郵件加密中無認證的加密方案。
  2. Signature-based Authenticated Key Exchange9,10

    • 在密鑰交換中加入簽名可以做到顯式認證。這個增強將可以抵抗 KCI 攻擊。
    • 簽名內容不包括消息,這意味著該方案仍能保持消息的可否認性。
  3. Signature Key Exchange and Message

    • 在 方案2 的基礎上,簽名內容包括明文消息。這將保證消息的不可否認性Non-Repudiation。
  4. Signature Only

    • 只簽名明文消息,不作任何加密。

我們需要 AEAD?

我相信我不需要在 8102 年贅述完整性驗證的重要性。

EFAIL 就是一個未能很好處理完整性驗證導致的漏洞。

實際上 OpenPGP 有自己的完整性驗證方案 MDC11, 但在大多數應用中只當作警告處理,並且存在降級攻擊12。 而在 ENE 中,我們可以使用更現代的 AEAD 方案,更好的保證完整性。

關於 AEAD13 的研究已經有很多年,更多的安全性質被提出,例如 Nonce-misuse Resistant14

由於郵件無狀態的特點,我們只能隨機的產生 nonce。 對於某些加密算法而言,nonce reuse 的後果是毀滅性的,例如 AES-GCM15。 雖然我們可能沒有如此大量的郵件以導致生日攻擊, 但使用一個 Nonce-misuse Resistant AEAD 將可以免疫這個問題。

我們需要 Post-quantum?

衆所周知,量子機可以打破所有當前流行的公鑰算法。 屆時,所有使用 RSA/ECC 加密的郵件都將無法保證保密性。 因此,有必要在實用的量子機出現之前進行防禦。

一般有兩種方法來對抗量子機

  1. 使用預共享密鑰。
  2. 使用 Post-quantum 密鑰交換16

考慮到郵件的使用場景,預共享密鑰在很多情況下不太現實。 因而使用 Post-quantum 算法是一个有意义的选择。

ENE 是完美的嗎?

不是!

ENE 未來仍有很多工作要做。例如

  • 子密鑰支持
  • 完全的前向保密17,18
  • 郵件列表Mailing list加密支持
  • 側信道攻擊Side-Channel attacks抗性
  • 協議形式化驗證Formal verification

ENE 是一個非常年輕的工具,一切在未來必將發生變化。

爲什麼不用 ENE?

ENE 不是萬能的。它被設計用於加密郵件,不是 PGP 的完全替代品。

  1. Web of Trust19

    • 我不打算討論 Web of Trust 的好壞。
    • ENE 不是信任系統,不考慮如何建立信任關係。
    • 若需要,你可以使用 PGP 簽署 ENE 公鑰。
  2. 爲 git commit、壓縮包以及其他公鑰簽名

    • ENE 可以做,但在這些方面不會比 PGP 有什麼優勢。
    • 當然,你也可以使用 pbp
  3. 加密巨大的文件

    • 通常郵件文本不會太大,所以 ENE 不考慮這一點。
    • 當然,這不意味著 GPG 適合做文件加密。
  4. 需要可靠的、穩定的加密方案

    • ENE 非常不穩定!隨時可能發生變化。
    • “現代 PGP” 依然是最好的選擇之一,例如 Sequoia

ENE 是開源的,代碼託管在 Github


  1. Forward Secrecy Extensions for OpenPGP
  2. Deniable authentication
  3. Should we sign-then-encrypt, or encrypt-then-sign?
  4. Defective Sign & Encrypt in S/MIME, PKCS#7, MOSS, PEM, PGP, and XML
  5. Chosen-ciphertext attack
  6. One-Pass HMQV and Asymmetric Key-Wrapping
  7. OAKE: a new family of implicitly authenticated diffie-hellman protocols
  8. Key Compromise Impersonation attacks (KCI)
  9. The SIGMA Family of Key-Exchange Protocols
  10. AEAD variant of the SIGMA-I protocol
  11. Modification Detection Code Packet
  12. OpenPGP SEIP downgrade attack
  13. Authenticated encryption
  14. Nonce misuse resistance 101
  15. Nonce-Disrespecting Adversaries: Practical Forgery Attacks on GCM in TLS
  16. Post-quantum cryptography
  17. Forward Secure Asynchronous Messaging from Puncturable Encryption
  18. 0-RTT Key Exchange with Full Forward Secrecy
  19. Web of trust

by quininer kel at August 03, 2018 03:09 PM

July 31, 2018

ヨイツの賢狼ホロ

su, sudo, pkexec 和 root 的二三事

到底有啥区别 _(:з」∠)_

什么是 root 权限?

这里的 root 权限,实际上指的是名为 root 的账户所具有的权限。“获取 root 权限”,指的实际是获取 root 账户的使用权。 root 是系统中权限最高的用户。和 Windows 的 Administrator 相比,权限不知道高到哪里去了。 (注:在 Windows 中想获取较高的权限,可以使用 NSudo)举个例子:Administrator 是删除不了 explorer.exe 文件的, 而 root 可以删除你当前正在使用的 shell 甚至整个系统 。

在 GNU/Linux 上 切换到 root 用户的方法大有三种:

su - 其实是切换用户 (switch user)

su [options] [-] [user [argument...]]

su 其实是用来切换用户的啦~ 如果没参数的话,默认是切换到 root 的,所以会询问 root 的密码。

以及 su 默认只改变 HOME 和 SHELL 变量,可以用 --login (或者更短些的 - )来规避混合环境变量的副作用:

su -

直到离开 shell 之前都能保持 root 权限。

sudo - 家庭常备(误)

sudo(substitute user do) 使得系统管理员可以授权特定用户或用户组作为 root 或他用户执行某些(或所有)命令, 同时还能够对命令及其参数提供审核跟踪。

一个典型的 sudo 操作大概像这样:

$ sudo pacman -Syu
# sudo 会请求当前用户的密码
[sudo] password for horo:
# 然后就可以用 root 权限执行某些操作啦~
:: Synchronizing package databases...

不过不是所有能用 sudo 这一个命令的家伙都能获得 root 权限的啦~ sudo 君有一个小名单,只有名单上的人才可以使用 sudo。这个名单就是 /etc/sudoers 。

为了确保正确的更改 /etc/sudoers 文件,通常会使用 visudo 命令。 visudo会锁住 sudoers 文件,保存修改到临时文件,然后检查文件格式, 确保正确后才会覆盖 sudoers 文件。

pkexec - Systemd 全家桶内置(误)

pkexec allows an authorized user to execute PROGRAM as another user. If PROGRAM is not specified.the default shell will be run. If username is not specified, then the program will be executed as the administrative super user, root.

pkexec 用法上和 sudo 大概差不多:

$ pkexec <command>

不过这个貌似桌面环境里用的比较多,在桌面环境上接下来会弹出一个对话框, 提示汝输入汝自己的密码,大概像这样(GNOME 大概像这样……):

pkexec

所以这仨到底有啥区别还是没搞清楚 _(:з」∠)_

by ホロ at July 31, 2018 04:00 PM

Sailfish OS 移植中(0)- 挖坑

心血来潮,能不能成功还是个未知数 _(:з」∠)_

那么问题来了……

Sailfish OS 是啥来着?

(此处应有斜眼)

先决条件

事前准备

咱是在 Parabola GNU/Linux Libre 上编译的,于是 Arch GNU/Linux 应该也可以用。 其它发行版就自行按图索骥好了 😂

安装编译 LineageOS 需要的软件 (因为现在移植到 Android 手机上的 Sailfish OS 是在 Android 上用 hybris 跑起来一个 GNU/Linux 的 用户空间嘛), AUR 上有个 lineageos-devel 的 metapackage。

可以参考 https://wiki.archlinux.org/index.php/Android#Building

因为要拖源代码回来,所以装个 git 和 repo ,然后设置一下汝的名称和邮箱(如果汝以前用过 Git 的话可以跳过这一步):

git config --global user.name "Your Name"

git config --global user.email "you@example.com"

设置相应的环境变量

# ~/.hadk.env
# Mer SDK 的位置
export MER_ROOT=/srv/mer
export PLATFORM_SDK_ROOT=$MER_ROOT
# 移植到的目标手机的厂商和代号,可以去 LineageOS Wiki 上找到
export VENDOR="moto"
export DEVICE="shamu"
# 移植的架构,和手机有关
export PORT_ARCH="armv7hl"

# 要移植的 hybris 和 Sailfish OS 版本
export ROOMSERVICE_BRANCHES="cm-14.1"
export HYBRIS_BRANCH="hybris-14.1"
export SAILFISH_VERSION=2.1.4.13

# 源代码放在哪
export ANDROID_ROOT=/home/repo/libhybris

下载源代码

初始化仓库:

$ mkdir -p $ANDROID_ROOT
$ cd $ANDROID_ROOT
$ repo init -u git://github.com/mer-hybris/android.git -b $HYBRIS_BRANCH

设置本地清单,用汝喜欢的文字编辑器打开 $ANDROID_ROOT/.repo/local_manifests (需要的话就新建一个),然后建立一个 {{汝设备的代号}}.xml ,加入适当的 repo :

  • 至少需要 Device 和 kernel ,在 Github 上搜索一下 “android_[device,kernel]_$VENDOR_$DEVICE”看看?

例如咱的 shamu :

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
    <project path="device/moto/shamu" remote="private" name="KenOokamihoro/android_device_moto_shamu"
    revision="cm-14.1" />
    <project path="kernel/moto/shamu" remote="private" name="KenOokamiHoro/android_kernel_moto_shamu"
    revision="cm-14.1" />
    <project name="TheMuppets/proprietary_vendor_motorola" path="vendor/motorola" revision="cm-14.1"  />
    <project path="rpm/" name="KenOokamiHoro/droid-hal-shamu" revision="master" />
    <project path="hybris/droid-configs" name="KenOokamiHoro/droid-config-shamu" revision="master" />
    <project path="hybris/droid-hal-version-shamu" name="KenOokamiHoro/droid-hal-version-shamu"
    revision="master"
    />
</manifest>

然后同步一下代码:

$ repo sync --fetch-submodules
  • 同步可能需要很长的时间,甚至有可能会失败,失败的话就多试几次 😂

设置一个 Python2 虚拟环境

因为 Android 这套工具还认为 python 是 python2 😂

用汝喜欢的虚拟环境工具就 Ok 啦,例如 python2-virtualenv :

$ virtualenv2 /path/to/your/virtualenv
# 以后要用的话:
$ source /path/to/your/virtualenv/bin/activate

安装 Platform SDK

从官方文档里抄的 😂
# 下载并解压 SDK 的 chroot
export PLATFORM_SDK_ROOT=/srv/mer
curl -k -O http://releases.sailfishos.org/sdk/installers/latest/Jolla-latest-SailfishOS_Platform_SDK_Chroot-i486.tar.bz2 ;
sudo mkdir -p $PLATFORM_SDK_ROOT/sdks/sfossdk ;
sudo tar --numeric-owner -p -xjf Jolla-latest-SailfishOS_Platform_SDK_Chroot-i486.tar.bz2 -C $PLATFORM_SDK_ROOT/sdks/sfossdk  ;
# 为 .bashrc 设置相应的 PS1 ,alias 和环境变量 (其它 Shell 用户请自行依样画葫芦)
echo "export PLATFORM_SDK_ROOT=$PLATFORM_SDK_ROOT" >> ~/.bashrc
echo 'alias sfossdk=$PLATFORM_SDK_ROOT/sdks/sfossdk/mer-sdk-chroot' >> ~/.bashrc ; exec bash ;
echo 'PS1="PlatformSDK $PS1"' > ~/.mersdk.profile ;
echo '[ -d /etc/bash_completion.d ] && for i in /etc/bash_completion.d/*;do . $i;done'  >> ~/.mersdk.profile ;
# 进去 😂
sfossdk

下载工具:

# 安装工具链
sudo zypper in android-tools-hadk tar
# 安装通用工具链
# sdk-manage target install <name> <url> --tooling <name> --toolchain <name>
sdk-manage target install SailfishOS-latest-armv7hl http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Target-armv7hl.tar.bz2 \
 --tooling SailfishOS-latest --tooling-url http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Tooling-i486.tar.bz2
# 安装汝设备使用的工具链
# target 的名称命名为 $VENDOR-$DEVICE-$PORT_ARCH,tooling 的名称则是 $VENDOR-$DEVICE
sdk-manage target install moto-shamu-armv7hl http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Target-armv7hl.tar.bz2 --tooling moto-shamu --tooling-url http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Tooling-i486.tar.bz2

# 更新 chroot
# 更新到最新的版本 (写这篇文章的时候最新的是 2.2.0.29)
sudo ssu re 2.2.0.29
sudo zypper ref
sudo zypper dup

未完待续 😂

by ホロ at July 31, 2018 04:00 PM

July 25, 2018

Phoenix Nemo

NetFLOW / sFLOW 流量报告:FastNetMon + InfluxDB + Grafana

最近稍微有点时间折腾了下 Cisco 的三层交换,尝试搭建了一套数据中心用的流量统计/监控/报告系统。过程不是很复杂,但是也只算利用了一套高级软件组合的一点点功能。之后打算继续研究更多的功能实现,不过也要看有没有时间了…

准备工作

首先确认出口路由设备支持 netflow/sflow 的对应版本。一般 Cisco 的路由器或者三层交换都是支持的。

然后准备一个常见的 Linux 系统,虚拟机或者物理机都可以。

出口路由设备能够连通到该 Linux 系统,并且 flow collector 设置到该 Linux 系统的 IP 地址和对应端口。

FastNetMon

安装 fastnetmon,只需要一条简单的脚本命令。

然后将所有要监控的网段加入 /etc/networks_list。一行一个,例如:

1
2
3
10.1.0.0/16
192.168.254.0/24
8.8.0.0/16

按照安装文档打开两个终端,分别启动主进程和客户端

1
/opt/fastnetmon/fastnetmon
1
/opt/fastnetmon/fastnetmon_client

如果没有问题,应该在客户端上可以看到收到的 flow 数据。

先关闭 fastnetmon 进程,修改配置文件打开 Graphite 支持:

1
2
3
4
graphite = on
graphite_host = 127.0.0.1
graphite_port = 2003
graphite_prefix = fastnetmon

=== 2018-07-26 更新 ===

如果有比较新的发行版(内核 >= 3.6)可以开启 AF_PACKET,安装并启动 irqbalance 来获得更好的抓包性能。

InfluxDB

安装 InfluxDB,官方提供了各种包管理器的安装方式。

配置文件一般位于 /etc/influxdb/influxdb.conf,需要根据环境做安全相关设置(侦听地址、端口、鉴权、etc)并打开 Graphite Simulation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[[graphite]]
enabled = true
bind-address = "127.0.0.1:2003"
database = "flow_dc1"
protocol = "tcp"
consistency-level = "one"
name-separator = "."

# batch-size / batch-timeout requires InfluxDB >= 0.9.3
batch-size = 5000 # will flush if this many points get buffered
batch-timeout = "1s" # will flush at least this often even if we haven't hit buffer limit

templates = [
"fastnetmon.hosts.* app.measurement.cidr.direction.function.resource",
"fastnetmon.networks.* app.measurement.cidr.direction.resource",
"fastnetmon.total.* app.measurement.direction.resource"
]

顺序重启 InfluxDB 和 fastnetmon。检查 flow 数据是否记录到 InfluxDB:

1
2
3
4
5
6
7
8
9
10
11
$ influx
Connected to http://localhost:8086 version 1.2.4
InfluxDB shell version: 1.2.4
> use flow_dc1
Using database flow_dc1
> select mean(value) from networks where direction = 'incoming' and resource = 'bps' group by *
name: networks
tags: app=fastnetmon, cidr=10_1_0_0_24, direction=incoming, resource=bps
time mean
---- ----
0 4735.049632696411

Grafana

Grafana 是一款非常强大且易用的数据可视化工具。安装 Grafana 然后修改配置文件的必要部分,配置文件一般位于 /etc/grafana/grafana.ini

完成后重启 Grafana,将浏览器指向 Grafana 的 HTTP 服务器地址即可看到登录界面。如果内部使用的话,建议关闭匿名访问和注册功能。

使用默认的 admin / admin 登录,按照引导完成配置、添加数据源(Data source),数据源即是 InfluxDB 的 HTTP API 地址。如果 Grafana 中限制了数据源白名单,需要将 InfluxDB 的 HTTP API 地址和端口加到白名单里。

添加面板、Graph,在 Graph 编辑模式里写入类似这样的查询语句:

1
SELECT mean("value") FROM "networks" WHERE "direction" = 'incoming' AND "resource" = 'bps' AND "cidr" =~ /^10_1_0_0_16/ AND $timeFilter GROUP BY time($interval) fill(previous)

即可看到有图表出现。根据需求完善查询语句和图表配置即可简单实现各种可视化效果。例如流量和数据包的实时报告:

Traffic Graph

PPS Graph

总结

通过配合 FastNetMon,InfluxDB 和 Grafana 即可快速实现一套基于 NetFLOW / sFLOW 的流量统计报告系统。但是 FastNetMon 的功能远不止流量统计,Grafana 也有大量插件和灵活的用法可以满足更多需求。如果配置合理,此方案也可适用于 40Gbps+ 接入的中型数据中心且成本低廉。以及——

  1. InfluxDB 真的很快!
  2. Grafana 的图表真的很省资源!
  3. Chronograph 卡死了我的浏览器!(i7-7700K / Chrome)

以及一大早手工修好了 K812 的耳机线,省掉了 2 万日元的线材费用非常开心

最近稍微有点时间折腾了下 Cisco 的三层交换,尝试搭建了一套数据中心用的流量统计/监控/报告系统。过程不是很复杂,但是也只算利用了一套高级软件组合的一点点功能。之后打算继续研究更多的功能实现,不过也要看有没有时间了…

July 25, 2018 04:37 PM

July 24, 2018

ヨイツの賢狼ホロ

咕咕咕

咕咕咕咕咕,咕咕咕,咕咕咕咕,咕咕咕……

挖坑一时爽,填坑火葬场(咕)

  • 某无名魔法书(?)因为名字始终没决定就咕了(偏谁呢~)
  • 前两篇提到的给 Nexus 6 移植 SailfishOS 的工作其实已经接近完成了(大概只剩手机网络和蓝牙了, 不过后来更新 2.2.0.29 (大概是这个版本号吧)以后就炸了 😂
  • 这就是汝咕了接近两个月的理由?

最近?

并没有收广告费的广告

有人群的地方就需要信仰,在這廣大的姬家也不例外,所以就讓我們一起信奉「山景城的始祖鳥〜谷鴿」吧! 參拜時別忘了添香油錢哦!也請大家多多介紹本神社給其他信眾, 別讓巫女「+ 雪月秋水」沒事可做喔!希望本神社能成為姬家的信仰中心,祝福大家好運隨你身,有參拜有保佑。

人群れがあれば、信仰が必要であり、こんな広いG+でも外されないです。だから私達はいっしょにこの 「マウンテンビューの始祖島~グーグル」を信奉しましょう。参拝する時、お賽銭を忘れないでくださいね! 皆さんにぜひ林靜(リンジン)神社を紹介しなければならなくください。巫女(+雪月秋水) は信者のために祈ります! 我々は、この神社をG+の信仰のセンターになることを願います!すべての信者は、幸運を得ることができます。

Where there are people, there is faith. On Google+, we have built a Jinja (Shinto shrine) to collect wishes and good luck. We believe that the Jinja can be a place where people can spread love and peace, rather than a religious site. In this community, we encourage communication among different cultures as well as sharing your wishes and expressing good faith. You can also post your short-term or long-term goals here, and many people will come to encourage you and wish you good luck. Yukizuki Akimizu, our miko (Shinto priestess) is waiting for your arrival at the entry. Please make donations to the Jinja to support the daily expense such as food and drinks for the miko.

—林静神社 - https://plus.google.com/communities/117905230530560564263

并没有收广告费的广告 02

广告位招租……

😂

by ホロ at July 24, 2018 04:00 PM

July 16, 2018

百合仙子

使用 iptables 透明代理 TCP 与 UDP

本文来自依云's Blog,转载请注明。

很早之前,我在《Linux「真」全局 HTTP 代理方案》中介绍了 redsocks 方案。不过它只处理了 TCP,并没有处理 UDP,DNS 也是采用强制 TCP 的方式来处理的,再加上它本身还要将请求转发到真正的代理客户端,延迟比较高。然后,还可以结合 Wi-Fi 分享 或者网络命令空间,玩点更有趣的。

首先要有支持的代理客户端,比如 ss-redir。这个就不用多介绍了,配置好、跑起来即可。以下假设此代理跑在 127.0.0.1 的 $PPROT 端口上。

然后,TCP 的代理设置。使用的是和 redoscks 一样的方案。这个比较简单,除了有一点需要注意:DNAT 到 127.0.0.1 时,需要设置内核选项net.ipv4.conf.all.route_localnet=1

最麻烦的是 UDP 的代理,使用的是 TPROXY。首先,需要把要走代理的数据包路由到本地。以下假设我们给要代理的数据包打上标签 1。那么执行:

ip rule add fwmark 1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

那个 100 是路由表的编号,可以自己选一个喜欢的。

然后,对于转发流量(来自局域网或者另外的网络命名空间),直接把需要代理的数据包扔给 TPROXY 目标,并且打上对应的标签即可。而对于本地产生的流量,不仅要带有对应的标签,而且需要在 OUTPUT 链打上一个(与之前不同的)标签,触发 reroute check 才行。

最后,对需要代理的数据包设置 iptables 规则:

协议 来源 目标
TCP 本地 nat OUTPUT -j REDIRECT --to-ports $PPROT
转发 PREROUTING -j DNAT --to-destination 127.0.0.1:$PPROT
UDP 本地 mangle OUTPUT
PREROUTING
-j MARK --set-mark 1
-j TPROXY --on-port $PPROT --on-ip 127.0.0.1
转发 PREROUTING -j TPROXY --on-port $PPROT --on-ip 127.0.0.1 --tproxy-mark 1/1

比如来自网络命名空间或者局域网的 IP 段 192.168.57.0/24 全部走代理:

iptables -t nat -A PREROUTING -p tcp -s 192.168.57.0/24 ! -d 192.168.57.0/24 -j DNAT --to-destination 127.0.0.1:$PPROT
iptables -t mangle -A PREROUTING -p udp -s 192.168.57.0/24 ! -d 192.168.57.0/24 -j TPROXY --on-port $PPROT --on-ip 127.0.0.1 --tproxy-mark 1/1

by 依云 at July 16, 2018 09:01 AM

July 15, 2018

中文社区新闻

libutf8proc>=2.1.1-3 更新需要手动干预

libutf8proc 在 2.1.1-3 之前的版本中含有错误的 soname 链接,我们在 2.1.1-3 版本中修复了这个问题,因此更新时需要手动覆盖掉 ldconfig 创建出的未被跟踪的文件。如果你看到如下错误:
libutf8proc: 文件系统中已存在 /usr/lib/libutf8proc.so.2
请执行:
pacman -Suy --overwrite usr/lib/libutf8proc.so.2
之后继续更新系统。

by farseerfc at July 15, 2018 02:40 AM

July 11, 2018

百合仙子

Linux 下获取文件的创建时间

本文来自依云's Blog,转载请注明。

其实 Linux 是支持文件的创建时间的呢。不过不是所有文件系统都支持,比如 ext4、xfs、btrfs 都支持,zfs、vfat、ntfs 不支持。

但是呢,用户基本上是看不到的。文件系统有记录,但是没有 API 可以获取到这个数据。所以你用 stat 命令的话,会看到「创建时间」一行总是「-」。用 debugfs 搞 ext4 是可以的,但是那个需要 root 权限,并且一不小心会搞坏文件系统。

最近,我阅读内核源码时,忽然发现内核已经通过 4.11 版本引入的 statx 系统调用支持获取创建时间了。字段名里用的是 btime(birth time),没有用 crtime(creation time),也没有用大写的 Btime 呢。

但是 glibc 并没有支持,所以要用 syscall 函数来调用。也不是很复杂。不过我正着手用 Rust 实现的时候,却在内核源码树里找到了 samples/statx/test-statx.c 这么个文件。原来有现成的啊!

gcc 编译一下,还真好用:

>>> statx /
statx(/) = 0
results=fff
  Size: 224             Blocks: 0          IO Block: 4096    directory
Device: fe:01           Inode: 96          Links: 17
Access: (0755/drwxr-xr-x)  Uid:     0   Gid:     0
Access: 2018-07-11 13:33:08.659477830+0800
Modify: 2018-03-30 15:06:02.645864827+0800
Change: 2018-03-30 15:06:02.645864827+0800
 Birth: 2017-06-19 21:07:53.653467000+0800

by 依云 at July 11, 2018 09:49 AM

July 05, 2018

Lainme

如何优雅的在Windows 10上装X

Windows 10带了一个Linux子系统(WSL)已经是旧闻了,作为偶尔需要用Windows的我也不能免俗的装了一个。当然,一开始我是不打算装X的,毕竟真正需要的只有Linux终端这种家的温馨。然而VIM不能方便的和Windows程序共享剪贴板内容,那个bash.exe又丑的不行,这怎么可以忍受。最后不得不求助于Xserver,把GVIM和Xterm都弄起来。

本文内容包括Linux子系统的安装,Xserver安装和相应配置,Xterm的快捷启动等。内容参考了很多不同的文章,链接在最后给出。

Linux子系统安装

以管理员身份打开Powershell并输入以下命令来启用WSL特性

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

重启后打开Windows应用商店,搜索linux,安装你想要的发行版。我个人选择了Ubuntu(毕竟最熟悉)。安装完毕后选择运行,会出现一个终端让你创建一个新用户,输入用户名和密码即可完成。现在你就可以在开始菜单里搜索bash或ubuntu来启动了。

如果有其他问题,可以参考微软的文档:https://docs.microsoft.com/zh-cn/windows/wsl/install-win10

Xserver的安装及配置

Windows上的Xserver有好几个选择,我用的是VCXSRV,安装时一路默认就可以了。

因为现在很多电脑都有HiDPI,需要对VCXSRV做点设置不然字体会发虚。首先找到软件的安装路径,比如C:\Program Files\VcXsrv,然后对两个可执行文件vcxsrv.exe和xlaunch.exe执行以下操作:

右键点击可执行文件 –> “属性” –> “兼容性” – > “更改高DPI设置” –> “替代高DPI缩放行为”。

在开始菜单查找xlaunch并运行,一路默认就可以开启Xserver。

系统设置

显示设置

DISPLAY变量可以放到~/.profile里,当然/etc/profile也是可以的,同时建议设置umask(默认是0000)

umask 0022
export DISPLAY=localhost:0.0

重新打开一个终端或者在当前终端里再export一下就能运行GUI程序了。

中文支持

先安装中文字体和输入法

sudo apt-get install fonts-wqy-microhei fonts-wqy-zenhei xfonts-wqy
sudo apt-get install fcitx fcitx-pinyin

输入法环境变量可以放到~/.profile

export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx

再更改dbus配置让fcitx能正常启动。修改/etc/dbus-1/session.conf里的几个配置(如果没有这个文件请自行创建),将

<listen>unix:tmpdir=/tmp</listen>
<auth>EXTERNAL</auth>

改为

<listen>tcp:host=localhost,port=0</listen>
<auth>ANONYMOUS</auth>
<allow_anonymous/>

最后解决一下Windows输入法和Linux输入法切换冲突问题,把快捷键给错开。这有很多种可能,我采用的方式是关闭Windows的Shift键中英切换,把左Shift让给开关fcitx用,同时关掉fcitx里的输入法循环切换快捷键(因为可选快捷键不够用了……)。具体来讲:

1. Windows: 设置 –> 时间和语言 –> 区域和语言 –> 中文(中华人民共和国)–> 选项 –> 微软输入法 → 按键 –> “中/英文模式切换” 选 “无”

2. Fcitx:用fcitx-configtool打开配置,设置如下

其他设置

我装了xterm, screen和vim-gtk3

sudo apt-get install xterm screen vim-gtk3

~/.profile中的相关设置如下

export SCREENDIR=$HOME/.screen # 修复screen创建目录的权限问题
xrdb -merge $HOME/.Xresources # xterm使用

最后如果你用VIM的深色主题,可以在.vimrc里设置

set t_ut=

以禁用背景色擦写(Background Color Erase)

快捷启动

如果每次启动都要先开xlaunch,再开ubuntu终端,再开程序就太麻烦了。因为我只需要xterm,可以写个vbs来方便开启,脚本如下

xterm.vbs
set ws=wscript.createobject("wscript.shell")
ws.run """C:\Program Files\VcXsrv\vcxsrv.exe"" :0 -ac -terminate -lesspointer -multiwindow -clipboard -wgl -silent-dup-error",0
WScript.Sleep 500
ws.run "C:\Windows\System32\bash.exe -l -c 'export DISPLAY=:0.0; fcitx &> /dev/null'",0,True
WScript.Sleep 500
ws.run "C:\Windows\System32\bash.exe -l -c 'cd $HOME; xterm'",0

这个脚本先开VCXSRV,再通过bash.exe开fcitx,最后开xterm,中间都有延时。双击就可以运行,最后会弹出xterm的界面。

注意fcitx这里其实很容易出问题,如果没有启动起来只能手动启动了……我想了点办法,但都没什么用。如果你打算装完整的DE,这个问题就容易解决了。

脚本可以放到任何方便的地方,比如“我的文档”,之后要用只需要按Win键,然后输入xterm就能搜到了,再按回车即可启动。

最后补一张图:

参考链接

by lainme (lainme@undisclosed.example.com) at July 05, 2018 01:21 PM

June 15, 2018

quininer kel

tamarin-and-badake

如何設計一個壞協議

Wed Jun 13 19:44:31 2018

近幾日看 The 8th BIU Winter School on Cryptography 的 slide, 這期冬令營主要內容是協議設計。

冬令營第二講 Implicitly Authenticated KE Protocols 拋磚引玉, 提出了一個相當簡潔的兩消息隱式帶認證密鑰交換Implicitly Authenticated Key Exchange協議,

後文將稱之爲 BADAKEBasic A-round Diffie-hellman Authenticated Key Exchange。

BADAKE

這是一個相當乾淨的協議,甚至可能大家自己就在實踐中設計過類似的協議。 但這不是一個安全的協議。

slide 中已經舉出了一些該協議的攻擊,這些攻擊並不那麼直觀。

Define

在描述這些攻擊之前,我們先澄清一些定義。

  • 隱式帶認證密鑰交換: 與顯式認證密鑰交換不同,隱式認證協議不會在協議執行中顯式的失敗, 但只有雙方身份正確時會生成相同的密鑰。
  • 認證協議安全要求: 一個合格的認證協議應該在協議完成之後保證
    • 基本僞裝抗性Basic Impersonation Resistance: 若 A 正確執行協議,當敵手不知道 A 的長期密鑰時,敵手不可以僞裝成 A。
  • 密鑰交換協議安全要求: 一個合格的密鑰交換協議應該保證,
    • 完美前向保密Perfect Forward Secrecy: 長期密鑰泄漏時,不會影響之前會話密鑰的安全性。
    • 弱前向保密weak Perfect Forward Secrecy: 由於兩消息的認證密鑰交換協議無可能滿足 PFS,所以對於這些協議,退而求其次,我們只要求 wPFS。 若敵手在長期密鑰泄漏之前沒有參與協議,那長期密鑰泄漏時,不會影響之前會話密鑰的安全性。
    • 已知密鑰安全Known-Key Security: 會話密鑰泄漏時,不會影響長期密鑰及其他會話密鑰的安全性。
    • 臨時祕密揭露抗性Ephemeral Secrets Reveal Resistance: 臨時密鑰泄漏時,不會影響長期密鑰及其他會話密鑰的安全性。
    • 未知密鑰共享Unknown Key-Share抗性: 當 A 和 C 協商出會話密鑰時,A 不會認爲該密鑰是與 B 協商得到的。

Signature + DH

在分析 BADAKE 前,slide 先給出了一個簡單的簽名與密鑰交換的組合協議。 這個協議在簽名中加入了目標身份,以避免交錯攻擊。

但這個協議仍然是不安全的。

當 A 的臨時密鑰 x 泄漏時, 攻擊者可以重放消息 g^x, SIG{A}(g^x, B),與 B 進行握手。 從而達成僞裝 A 身份的目的。

一般情況下防止重放攻擊的最好方案是添加一個 challenge, 但這需要增加一輪通信。

這個例子說明了使用 Signature + DH 的簡單組合無法實現安全的兩消息帶認證密鑰交換。

Implicitly Authenticated DH

slide 給出了 BADAKE 的兩種簡單組合方式。

第一種組合方式相當簡單, K = H(g^ab, g^xy)

slide 中描述它受到 Open to known key and interleaving attacks 的威脅。 一開始我不太明白這個攻擊如何實現。

藉助 tamarin, 我們可以輕鬆找到針對這個協議的多個攻擊方案,這裏我們舉出兩個

  • 反射攻擊

reflection attack

A 向 B 發起兩個握手請求,敵手將這兩個請求作爲響應發回給 A。

A 不會知道他正在和自己通信。(呆萌

  • 會話密鑰揭露時的交錯攻擊

interleaving attack

這圖片中描述的攻擊與 slide 可能有所不同。

此攻擊需要 B 在握手結束之後泄漏會話密鑰。

B 向 A 發起兩個握手請求,敵手將一個請求作爲響應發回給 B。 兩個會話產生相同的密鑰,結束其中一個會話,B 泄漏會話密鑰 K。 敵手使用該密鑰繼續與 B 通信。

最終達成僞造 A 身份的目的。

KCI Attack

第二種組合方式添加了角色因素, 可以對抗反射、並行會話等攻擊方式。 K = H(g^ab, g^xy, g^x, g^y)

這相比第一種組合方式要好一點,但仍不是一個理想的 AKE 方案。

slide 中提到了密鑰泄漏僞裝攻擊Key-Compromise Impersonation。 簡單來講就是,在 A 的長期密鑰泄漏之後,敵手不但可以僞裝成 A 與 B 通信,也可以僞裝成 B 與 A 通信。

這個漏洞在使用密鑰交換作認證的協議裏很常見,例如 Tox 就有這個問題。

很多人會覺得泄漏長期密鑰之後,協議可以不保證認證安全性。但一個好的認證協議應該要避免這一點。


上文的內容在冬令營的演講中只佔不到半分鐘時間,而後面關於 MQV、HMQV 的各種變種佔了將近兩個小時。

設計一個好的密碼協議並不是一件容易的事情。

而想要設計一個壞協議?只需要拍腦袋式的想一個簡潔的協議就可以了。

by quininer kel at June 15, 2018 04:15 AM

June 14, 2018

百合仙子

递归遍历目录:Python vs Go vs Rust

本文来自依云's Blog,转载请注明。

群友提出了一个简单的任务:递归遍历一个很大的目录,根据文件名数一下有多少 JPEG 文件。怎么最快呢?然后他用了 Go 语言实现。

我忽略想起 Python 3.5 的 What's New 里提到,他们优化了 os.scandir 使得目录遍历快了好几倍(PEP 471)。其核心思想是:不进行不必要的 stat 系统调用,因为读目录获得了不少信息,原来都是丢弃掉了,现在改成了通过 DirEntry 对象来返回。这些信息包括文件名等,刚好有我们需要的。

于是 Go 做了这个优化没有呢?

翻了一下代码。Go 自带的实现位于 src/path/filepath/path.go 文件中。可以看到,它对每一个文件都 lstat 了。后来一阁指出,不仅如此,而且它还莫名其妙地对目录下的文件名进行了排序

呃,前者可以说是疏忽了,毕竟 Python 也是直到 3.5 才优化的。可是,它排那个序干嘛呢……

然后我又想到,Rust 那边如何呢?

结果是,Rust 对它所包含的东西非常审慎,标准库里并没有递归遍历目录的函数。那我们自己写一个?才不呢,用第三方库啦!可以看到,它也是返回 DirEntry 对象的。

后来了解到,Go 也有一个第三方的实现 godirwalk,对这些细节进行了优化。

光是了解实现不够。我们让它们来比试一下吧。顺便,把 find 和 fd 也拖进来好了。

任务:数一数一个拥有近万文件的目录下有多少 JPEG 文件。

实现代码:walkdir-test

结果:

   Rust: top:    4.78, min:    4.72, avg:    4.90, max:    5.46, mdev:    0.17, cnt:  20
 Go_3rd: top:    7.71, min:    7.64, avg:    7.79, max:    8.41, mdev:    0.16, cnt:  20
   find: top:   11.49, min:   11.32, avg:   11.76, max:   14.18, mdev:    0.59, cnt:  20
     fd: top:   18.17, min:   15.18, avg:   21.29, max:   29.94, mdev:    3.84, cnt:  20
     Go: top:   21.08, min:   20.91, avg:   21.28, max:   22.70, mdev:    0.37, cnt:  20
 Python: top:   29.66, min:   29.51, avg:   30.43, max:   35.84, mdev:    1.45, cnt:  20
Python2: top:   30.37, min:   30.10, avg:   30.85, max:   33.15, mdev:    0.75, cnt:  20

Rust 如预期一样是最快的。Go_3rd 就是那个第三方库的实现,也非常快的。fd 是 Rust 实现的,目标之一是快,但是这次并没有比老牌的 find 快。Go 自带的那个实现,十分令人遗憾地连 find 都没比过呢,不过还是比 Python 快了不少。Python 2 这次终于没有跑在 Python 3 前边了(虽然差距很小),我猜是 PEP 471 那个优化的功劳。

对了,还有代码行数:

  15 Python/walk
  29 Rust/src/main.rs
  30 Go/walk.go
  33 Go_3rd/walk.go

Rust 竟然不是最长的。不过确实是字符数最多的。

话说 Go 的 } 竟然也是有规定的,结构体的不能另起一行写,只能跟 Lisp 的风格那样堆在一行的尾巴里。

PS: 没想到之前给 swapview 写的 benchmark 程序在另外的项目里用上了呢,果然写东西还是通用些的好。


更新:在群友的提示下,我找了一个更大的目录来测试,结果很不一样呢。这次遍历的目录是 /usr,共有 320397 个文件。

     fd: top:  265.80, min:  259.84, avg:  273.89, max:  319.76, mdev:   15.03, cnt:  20
   Rust: top:  269.98, min:  266.86, avg:  272.82, max:  282.84, mdev:    4.17, cnt:  20
 Go_3rd: top:  361.17, min:  359.05, avg:  363.82, max:  370.22, mdev:    3.31, cnt:  20
   find: top:  454.03, min:  450.79, avg:  458.51, max:  467.31, mdev:    5.08, cnt:  20
 Python: top:  624.80, min:  615.73, avg:  630.67, max:  640.88, mdev:    6.79, cnt:  20
     Go: top:  890.03, min:  876.98, avg:  910.63, max:  967.14, mdev:   24.84, cnt:  20
Python2: top: 1171.38, min: 1157.19, avg: 1189.99, max: 1228.09, mdev: 4186.28, cnt:  20

可以看到,唯一的并行版本 fd 胜出了~Rust 版本紧随其后,显然在此例中并行并没有多么有效。Go_3rd 还是慢于 Rust 但也并不多。然后,经过优化的 Python 终于在更大的数据量上明显胜过了 Go 以及 Python 2 这两个浪费了很多系统调用的版本。

by 依云 at June 14, 2018 07:55 AM

June 09, 2018

Lainme

使用面向对象的Fortran实现通用数据结构

Fortran自从2003以来增加了很多面向对象的特性,尽管和主流OOP语言相比并不完善,但也非常有用。比如可以实现一个通用的哈希表结构,并在此基础上写出类似Python的argparser和configparser等。

这里通过抽象类、多态、继承等实现一个可以存取任何数据对象的栈结构。

使用抽象类定义要存取的数据对象

因为我们希望这个栈能用于任何数据对象,所以先定义一个不包含具体元素的抽象派生类型(即抽象类)

type, abstract :: stack_structure
contains
    procedure(copy), deferred :: copy ! copy object.
end type stack_structure
 
abstract interface
    subroutine copy(self, object)
        import :: stack_structure
        class(stack_structure), intent(in) :: self
        class(stack_structure), intent(out) :: object
    end subroutine copy
end interface

这里的copy函数是用于pop数据时赋值的。因为我们后面传给stack类的数据都是以支持多态的class(stack_structure)类型传入,stack类并不知道里面的具体数据,因而无法直接赋值,需要迂回一下。

定义stack类

这里只实现两个功能,push和pop。类的定义如下

type :: stack_class
    private
    class(stack_structure), pointer :: object => null()
    type(stack_class), pointer :: next => null()
contains
    procedure :: push => stack_push
    procedure :: pop => stack_pop
    final :: stack_cleanup
 end type stack_class

在类里面定义了两个私有变量。第一个是存储的数据对象,用的是类指针,可以指向stack_structure及其任意子类,同时也能allocate成任何子类。第二个是指向下一个元素的指针。stack_cleanup是析构函数,用来释放内存等。

先看一下pop函数

subroutine stack_push(self, object)
    class(stack_class), intent(inout), target :: self
    class(stack_structure), intent(in) :: object
    class(stack_class), pointer :: next
 
    if (associated(self%object)) then
        next => self%next
        allocate(self%next)
        self%next%object => self%object
        self%next%next => next
    end if
 
    allocate(self%object, source=object)
end subroutine stack_push

这里比较关键的一句是

allocate(self%object, source=object)

函数传入object的形参虽然是class(stack_structure),但实参可以是任何stack_structure的子类,上面这句可以直接给self%object分配内存并赋值成object的实参数据,而不需要知道这个实参到底是什么样的。

再看pop

function stack_pop(self, object) result(success)
    class(stack_class), intent(inout), target :: self
    class(stack_structure), intent(out) :: object
    logical :: success
    class(stack_class), pointer :: next
 
    if (.not. associated(self%object)) then
        success = .false.
    else
        success = .true.
        call self%object%copy(object)
        deallocate(self%object)
    end if
 
    if (associated(self%next)) then
        next => self%next
        self%object => next%object
        self%next => next%next
        next%object => null()
        next%next => null()
        deallocate(next)
    end if
end function stack_pop

注意指针是否为空需要用associated来判断,但是通过allocate分配内存的指针一定要通过deallocate来释放,否则会内存泄漏。如上面所说,这里使用了copy函数来实现赋值

call self%object%copy(object)

如果采用了对指针类型动态分配内存的方法,一定要记得释放。这里对类里的变量可以通过析构函数来释放

elemental subroutine stack_cleanup(self)
    type(stack_class), intent(inout) :: self
 
    if (associated(self%object)) then
        deallocate(self%object)
    end if
 
    if (associated(self%next)) then
        deallocate(self%next)
    end if
end subroutine stack_cleanup

定义实际要存取的数据对象

type, extends(stack_structure) :: token_structure
    character(len=:), allocatable :: key
contains
    procedure :: copy => token_copy
end type token_structure
 
interface token_structure
    procedure :: token_create_object
end interface token_structure

这里继承了上面的stack_structure定义了一个token_structure,里面存入一个变长字符串。为了方便的生成对象,还定义了构造函数,但不是必要的。

copy函数的实现如下

subroutine token_copy(self, object)
    class(token_structure), intent(in) :: self
    class(stack_structure), intent(out) :: object
 
    select type(object)
    class is(token_structure)
        object%key = self%key
    end select
end subroutine token_copy

注意object是以stack_structure的类型传入的,必须要用select type判断到token_structure才能赋值。

最后是构造函数

elemental function token_create_object(key) result(token)
    character(len=*), intent(in) :: key
    type(token_structure) :: token
 
    token%key = trim(adjustl(key))
end function token_create_object

完整代码

stack.f90
module class_stack
    type, abstract :: stack_structure
    contains
        procedure(copy), deferred :: copy
    end type stack_structure
 
    abstract interface
        subroutine copy(self, object)
            import :: stack_structure
            class(stack_structure), intent(in) :: self
            class(stack_structure), intent(out) :: object
        end subroutine copy
    end interface
 
    type :: stack_class
        private
        class(stack_structure), pointer :: object => null()
        type(stack_class), pointer :: next => null()
    contains
        procedure :: push => stack_push
        procedure :: pop => stack_pop
        final :: stack_cleanup
    end type stack_class
contains
    subroutine stack_push(self, object)
        class(stack_class), intent(inout), target :: self
        class(stack_structure), intent(in) :: object
        class(stack_class), pointer :: next
 
        if (associated(self%object)) then
            next => self%next
            allocate(self%next)
            self%next%object => self%object
            self%next%next => next
        end if
 
        allocate(self%object, source=object)
    end subroutine stack_push
 
    function stack_pop(self, object) result(success)
        class(stack_class), intent(inout), target :: self
        class(stack_structure), intent(out) :: object
        logical :: success
        class(stack_class), pointer :: next
 
        if (.not. associated(self%object)) then
            success = .false.
        else
            success = .true.
            call self%object%copy(object)
            deallocate(self%object)
        end if
 
        if (associated(self%next)) then
            next => self%next
 
            self%object => next%object
            self%next => next%next
 
            next%object => null()
            next%next => null()
            deallocate(next)
        end if
    end function stack_pop
 
    elemental subroutine stack_cleanup(self)
        type(stack_class), intent(inout) :: self
 
        if (associated(self%object)) then
            deallocate(self%object)
        end if
 
        if (associated(self%next)) then
            deallocate(self%next)
        end if
    end subroutine stack_cleanup
end module class_stack
 
module class_token
    use class_stack
    type, extends(stack_structure) :: token_structure
        character(len=:), allocatable :: key
    contains
        procedure :: copy => token_copy
    end type token_structure
 
    interface token_structure
        procedure :: token_create_object
    end interface token_structure
contains
    elemental function token_create_object(key) result(token)
        character(len=*), intent(in) :: key
        type(token_structure) :: token
 
        token%key = trim(adjustl(key))
    end function token_create_object
 
    subroutine token_copy(self, object)
        class(token_structure), intent(in) :: self
        class(stack_structure), intent(out) :: object
 
        select type(object)
        class is(token_structure)
            object%key = self%key
        end select
    end subroutine token_copy
end module class_token
 
program main
    use class_stack
    use class_token
 
    type(stack_class) :: stack
    type(token_structure) :: token
 
    call stack%push(token_structure("world"))
    call stack%push(token_structure("hello"))
 
    do while(stack%pop(token))
        write(*,*) token%key
    end do
end program main

by lainme (lainme@undisclosed.example.com) at June 09, 2018 02:29 PM

使用Fortran的用户自定义派生类型IO (DTIO)

派生类型是Fortran很早就支持的特性,默认情况下在作为一个整体进行输入输出时会有一些限制。比如包含allocatable的元素时就不能进行整体输入输出:

type string
    character(len=:), allocatable :: s
end type string
 
type(string) :: zonename
zonename%s = "hello world"
write(*,*) zonename%s

Fortran 2003中加入了用户自定义派生类型IO (User-defined derived-type Input/Output) 的支持,可以突破上面的限制,实现类似下面的效果

type(string) :: scalar
type(string) :: vector(2)
....
write(*,*) scalar
write(*,*) vector

关于这个特性,很多文档里都有比较好的介绍,比如IBM的文档Intel的文档。不过我在使用时还是遇到了一点问题,因此记录一下。

一个完整的例子

module class_string
    type string
        character(len=:), allocatable :: s
    contains
        generic :: write(formatted) => formatted_write
        procedure, private :: formatted_write
    end type string
contains
    subroutine formatted_write(self, unit, iotype, vlist, iostat, iomsg)
        class(string), intent(in) :: self
        integer, intent(in) :: unit
        character(len=*), intent(in) :: iotype
        integer, dimension(:), intent(in) :: vlist
        integer, intent(out) :: iostat
        character(len=*), intent(inout) :: iomsg
 
        write(unit, "(A)", advance="no", iostat=iostat, iomsg=iomsg) self%s
    end subroutine formatted_write
end module class_string
 
program main
    use class_string
 
    type(string) :: vector(2)
    vector(1)%s = "hello"
    vector(2)%s = "world"
    write(*,*) vector
    write(*,"(A, 1X, 2(DT, 1X))") "Formatted", vector
end program main

在这个例子中,首先对派生类型绑定了用来输入输出的函数

generic :: write(formatted) => formatted_write
procedure, private :: formatted_write

其中第一行把格式化输出绑定到formatted_write这个私有函数上,第二行定义这个私有函数。

在IBM和Intel的文档中,formatted_write这个函数都是用的List-Directed Formatting输出,这有时不太符合我们的需求。我这里用了显式的格式化输出,并且加入了advance=“no” 以防止输出时加入多余换行。

write(unit, "(A)", advance="no", iostat=iostat, iomsg=iomsg) self%s

最后在使用时,可以用*或者格式化字符串控制输出。

write(*,"(A, 1X, 2(DT, 1X))") "Formatted", vector

这句通过格式化字符串输出vector中的两个数组值,其中到单个自定义类型的格式化字符串是“DT”。“2(DT, 1X)“表示数组的每个元素都按DT输出,并且随后跟一个空格,如果没有1X的话”hello”和“world”就会连起来。

by lainme (lainme@undisclosed.example.com) at June 09, 2018 11:39 AM

June 08, 2018

Lainme

在Fortran中调用Metis

Metis是比较有名的Graph Partitioning软件,我们一般用来切割计算网格做并行计算。这里稍微记录一下在Fortran中调用的方法和需要注意的问题。

数据类型长度的确定

由于Metis中的整型和实型可以编译为不同的长度,实型通常都是64位,但整型在不同发行版或者平台上可能是32位或者64位。这个地方不对的话就会出现类似下面的错误:

***Memory allocation failed for SetupCtrl: ctrl->tpwgts. 

C可以直接引用metis.h头文件,但Fortran稍微麻烦点(或者有简单方法我不知道)。在metis.h中寻找

define IDXTYPEWIDTH xx

就知道是几位,然后程序中保持一致。

如果你的程序需要适应不同的整型位数,可以在源码里加上预处理。比如

use iso_fortran_env
#if METIS_IDXTYPEWIDTH == 32
    integer, parameter :: METIS_INT = INT32
#else
    integer, parameter :: METIS_INT = INT64
#endif

然后编译的时候指定宏的值,比如

gfortran -cpp -DMETIS_IDXTYPEWIDTH=xx ...

使用Metis函数

以METIS_PartGraphRecursive函数为例(假定使用了上面的预处理定义):

integer(kind=METIS_INT), allocatable, dimension(:) :: xadj
integer(kind=METIS_INT), allocatable, dimension(:) :: adjncy
integer(kind=METIS_INT), allocatable, dimension(:) :: vwgt
integer(kind=METIS_INT), allocatable, dimension(:) :: adjwgt
integer(kind=METIS_INT), allocatable, dimension(:) :: part
integer(kind=METIS_INT), dimension(:), pointer :: ptr => null() ! Dummy.
integer(kind=METIS_INT) :: nvtxs
integer(kind=METIS_INT) :: nconn
integer(kind=METIS_INT) :: nparts
integer(kind=METIS_INT) :: objval

......

call METIS_PartGraphRecursive(nvtxs, nconn, xadj, adjncy, vwgt, ptr, adjwgt, nparts, ptr, ptr, ptr, objval, part)

其中可选参数如果不想提供(或接收)的话就用ptr代替。其它变量的具体含义可以看Metis的文档。

注意,Metis默认都是采用C的数组索引(即从0开始),这个行为可以通过传入options数组来改变。但为了防止options的enum值改变(毕竟不能直接用头文件)带来的麻烦,我都是这样做的

! C-style numbering.
xadj = xadj-1
adjncy = adjncy-1

call METIS_PartGraphRecursive(nvtxs, nconn, xadj, adjncy, vwgt, ptr, adjwgt, nparts, ptr, ptr, ptr, objval, part)

! Fortran-style numbering.
part = part+1
xadj = xadj+1
adjncy = adjncy+1

by lainme (lainme@undisclosed.example.com) at June 08, 2018 03:51 PM

升级至PHP7后页面空白、出现500错误等问题

很久没有编辑博客,前几天修改一些内容时发现会有页面空白、500 Internal Error等问题。查看Nginx日志后发现有类似这样的错误

PHP message: PHP Fatal error: Uncaught Error: Call to undefined function utf8_decode()

主要是一些扩展被分离出去了,成了单独的包。按照Nginx的错误日志,缺什么装什么就可以,以Dokuwiki来说,目前发现需要额外装这两个

aptitude install php-mbstring php-xml

其它分离的包如下(Debian更新日志)

  * Several extensions have been split into separate extension packages:
   - php-dba - Database (dbm-style) Abstraction Layer
   - php-mbstring - Multibyte String
   - php-soap - SOAP
   - php-xml - DOM, SimpleXML, WDDX, XML, XMLReader and XMLWriter
   - php-zip - Zip

by lainme (lainme@undisclosed.example.com) at June 08, 2018 03:01 PM

June 07, 2018

Phoenix Nemo

在线扩展 LVM root 分区

才不是没东西写了呢

遇到一个奇葩的原因导致 root 分区被占满的。而且还是奇葩的 CentOS,root 分区是 LVM,Hypervisor 里扩展磁盘后无法直接用 resize2fs。

既然如此就只能暴力重建分区咯。

重建分区

操作前确保操作的分区和之后新建时 Start 保持一致,修改分区表后不至于分区崩坏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
~> fidks /dev/sda
Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p

Disk /dev/sda: 103.1 GB, 103079215104 bytes, 201326592 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000a8e23

Device Boot Start End Blocks Id System
/dev/sda1 * 2048 2099199 1048576 83 Linux
/dev/sda2 2099200 50331647 24116224 8e Linux LVM

Command (m for help): d
Partition number (1,2, default 2): 2
Partition 2 is deleted

Command (m for help): n
Partition type:
p primary (1 primary, 0 extended, 3 free)
e extended
Select (default p):
Using default response p
Partition number (2-4, default 2):
First sector (2099200-201326591, default 2099200):
Using default value 2099200
Last sector, +sectors or +size{K,M,G} (2099200-201326591, default 201326591):
Using default value 201326591
Partition 2 of type Linux and of size 95 GiB is set

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

~> partprobe

现在就可以看到 /dev/sda2 的大小已经变化了:

1
2
3
4
5
6
7
8
~> lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 96G 0 disk
├─sda1 8:1 0 1G 0 part /boot
└─sda2 8:2 0 95G 0 part
├─centos-root 253:0 0 20.6G 0 lvm /
└─centos-swap 253:1 0 2.4G 0 lvm [SWAP]
sr0 11:0 1 906M 0 rom

扩展 Volume Group

VG 的好处也就是能够灵活扩展分区大小…

1
2
3
~> pvresize /dev/sda2
Physical volume "/dev/sda2" changed
1 physical volume(s) resized / 0 physical volume(s) not resized
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
~> vgdisplay
--- Volume group ---
VG Name centos
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 4
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 2
Open LV 2
Max PV 0
Cur PV 1
Act PV 1
VG Size <95.00 GiB
PE Size 4.00 MiB
Total PE 24319
Alloc PE / Size 5887 / <23.00 GiB
Free PE / Size 18432 / 72.00 GiB
VG UUID TpbtuH-AjTZ-PU3v-UN31-FvfX-kSLv-xLiJG7

至此已经可以看到 Free PE 的部分有多出的 72GB 空间。

扩展 Logic Volume

1
2
3
4
5
6
7
8
9
10
11
12
13
~> lvextend -r -l +100%FREE /dev/centos/root
Size of logical volume centos/root changed from 20.59 GiB (5272 extents) to 92.59 GiB (23704 extents).
Logical volume centos/root successfully resized.
meta-data=/dev/mapper/centos-root isize=512 agcount=4, agsize=1349632 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=0 spinodes=0
data = bsize=4096 blocks=5398528, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0 ftype=1
log =internal bsize=4096 blocks=2636, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
data blocks changed from 5398528 to 24272896

确认效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
~> lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 96G 0 disk
├─sda1 8:1 0 1G 0 part /boot
└─sda2 8:2 0 95G 0 part
├─centos-root 253:0 0 92.6G 0 lvm /
└─centos-swap 253:1 0 2.4G 0 lvm [SWAP]
sr0 11:0 1 906M 0 rom

~> df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/centos-root 93G 21G 73G 23% /
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 8.0K 3.9G 1% /dev/shm
tmpfs 3.9G 8.6M 3.9G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda1 1014M 185M 830M 19% /boot
tmpfs 783M 0 783M 0% /run/user/0

搞定收工(‘・ω・’)

后话

其实虚拟机还用 LVM 的话,直接新增一块虚拟硬盘是最方便的方案。直接 vgextend 一路搞定…

才不是没东西写了呢

遇到一个奇葩的原因导致 root 分区被占满的。而且还是奇葩的 CentOS,root 分区是 LVM,Hypervisor 里扩展磁盘后无法直接用 resize2fs。

June 07, 2018 02:50 PM

June 06, 2018

ヨイツの賢狼ホロ

Sailfish OS 移植中(1)- hybris-hal

Android HAL _(:з」∠)_

从这个阶段开始汝可能会遇到各种神奇的问题 😂,要是遇到了啥问题的话,不妨到这俩地方来问一问:

取得挂载点信息

HADK 文档里 讲的:

Systemd:怪我咯 😂

打开 hybris/hybris-boot/fixup-mountpoints ,大概像这样:

#!/bin/sh
# Fix up mount points device node names.
#
# /data needs to be mounted in initrd, but there is no udev that early,
# which means there is no /dev/block/platform/*/by-name/* (or bootdevice).
# This file is a map from the "by-name" path to /dev/mmcblkMpN.
# It also serves dhd to convert fstab and *.rc mount entries to systemd units,
# because at that boot stage there is still no udev "by-name" paths, even when
# systemd starts to execut the "*.mount" units.

DEVICE=$1
shift

echo "Fixing mount-points for device $DEVICE"

case "$DEVICE" in
    # 中间省去若干行 ……
    "shamu")
        sed -i \
            -e 's block/platform/msm_sdcc.1/by-name/aboot mmcblk0p7 ' \
            -e 's block/platform/msm_sdcc.1/by-name/abootBackup mmcblk0p13 ' \
            -e 's block/platform/msm_sdcc.1/by-name/boot mmcblk0p37 ' \
            -e 's block/platform/msm_sdcc.1/by-name/cache mmcblk0p38 ' \
            -e 's block/platform/msm_sdcc.1/by-name/cid mmcblk0p29 ' \
            -e 's block/platform/msm_sdcc.1/by-name/ddr mmcblk0p6 ' \
            -e 's block/platform/msm_sdcc.1/by-name/frp mmcblk0p18 ' \
            -e 's block/platform/msm_sdcc.1/by-name/keystore mmcblk0p24 ' \
            -e 's block/platform/msm_sdcc.1/by-name/kpan mmcblk0p36 ' \
            -e 's block/platform/msm_sdcc.1/by-name/logo mmcblk0p30 ' \
            -e 's block/platform/msm_sdcc.1/by-name/logs mmcblk0p25 ' \
            -e 's block/platform/msm_sdcc.1/by-name/mdm1dhob mmcblk0p28 ' \
            -e 's block/platform/msm_sdcc.1/by-name/mdm1hob mmcblk0p27 ' \
            -e 's block/platform/msm_sdcc.1/by-name/mdm1m9kefs1 mmcblk0p19 ' \
            -e 's block/platform/msm_sdcc.1/by-name/mdm1m9kefs2 mmcblk0p20 ' \
            -e 's block/platform/msm_sdcc.1/by-name/mdm1m9kefs3 mmcblk0p21 ' \
            -e 's block/platform/msm_sdcc.1/by-name/mdm1m9kefsc mmcblk0p33 ' \
            -e 's block/platform/msm_sdcc.1/by-name/metadata mmcblk0p2 ' \
            -e 's block/platform/msm_sdcc.1/by-name/misc mmcblk0p31 ' \
            -e 's block/platform/msm_sdcc.1/by-name/modem mmcblk0p1 ' \
            -e 's block/platform/msm_sdcc.1/by-name/oem mmcblk0p39 ' \
            -e 's block/platform/msm_sdcc.1/by-name/padA mmcblk0p11 ' \
            -e 's block/platform/msm_sdcc.1/by-name/padB mmcblk0p22 ' \
            -e 's block/platform/msm_sdcc.1/by-name/padC mmcblk0p40 ' \
            -e 's block/platform/msm_sdcc.1/by-name/padD mmcblk0p32 ' \
            -e 's block/platform/msm_sdcc.1/by-name/persist mmcblk0p26 ' \
            -e 's block/platform/msm_sdcc.1/by-name/recovery mmcblk0p35 ' \
            -e 's block/platform/msm_sdcc.1/by-name/rpm mmcblk0p8 ' \
            -e 's block/platform/msm_sdcc.1/by-name/rpmBackup mmcblk0p14 ' \
            -e 's block/platform/msm_sdcc.1/by-name/sbl1 mmcblk0p3 ' \
            -e 's block/platform/msm_sdcc.1/by-name/sbl1bak mmcblk0p12 ' \
            -e 's block/platform/msm_sdcc.1/by-name/sdi mmcblk0p4 ' \
            -e 's block/platform/msm_sdcc.1/by-name/sec mmcblk0p5 ' \
            -e 's block/platform/msm_sdcc.1/by-name/sp mmcblk0p23 ' \
            -e 's block/platform/msm_sdcc.1/by-name/ssd mmcblk0p34 ' \
            -e 's block/platform/msm_sdcc.1/by-name/system mmcblk0p41 ' \
            -e 's block/platform/msm_sdcc.1/by-name/tz mmcblk0p10 ' \
            -e 's block/platform/msm_sdcc.1/by-name/tzBackup mmcblk0p16 ' \
            -e 's block/platform/msm_sdcc.1/by-name/userdata mmcblk0p42 ' \
            -e 's block/platform/msm_sdcc.1/by-name/utags mmcblk0p9 ' \
            -e 's block/platform/msm_sdcc.1/by-name/utagsBackup mmcblk0p15 ' \
            -e 's block/platform/msm_sdcc.1/by-name/versions mmcblk0p17 ' \
            "$@"
        ;;
    *)
        cat <<EOF

****************************************************************
****************************************************************
ERROR: $DEVICE does not have mountpoint fixup data - see
    Sailfish OS HADK for details on how to fix this.
****************************************************************
****************************************************************

EOF
        exit 1
        ;;
esac

大概就是:

"{{汝设备的代号}}")
    sed -i \
        -e 's block/xxx xxx ' \
        ......
        "$@"
    ;;

这个样子 😂

那么中间一大把 -e 怎么生成呢?

把汝的手机接到电脑上,运行一下 adb :

$ adb shell
$ shamu:/ $ ls -l /dev/block/platform/
# 在这里按一下 TAB 补全一下:
$ shamu:/ $ ls -l /dev/block/platform/msm_sdcc.1/by-name/
total 0
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 aboot -> /dev/block/mmcblk0p7
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 abootBackup -> /dev/block/mmcblk0p13
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 boot -> /dev/block/mmcblk0p37
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 cache -> /dev/block/mmcblk0p38
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 cid -> /dev/block/mmcblk0p29
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 ddr -> /dev/block/mmcblk0p6
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 frp -> /dev/block/mmcblk0p18
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 keystore -> /dev/block/mmcblk0p24
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 kpan -> /dev/block/mmcblk0p36
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 logo -> /dev/block/mmcblk0p30
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 logs -> /dev/block/mmcblk0p25
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 mdm1dhob -> /dev/block/mmcblk0p28
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 mdm1hob -> /dev/block/mmcblk0p27
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 mdm1m9kefs1 -> /dev/block/mmcblk0p19
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 mdm1m9kefs2 -> /dev/block/mmcblk0p20
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 mdm1m9kefs3 -> /dev/block/mmcblk0p21
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 mdm1m9kefsc -> /dev/block/mmcblk0p33
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 metadata -> /dev/block/mmcblk0p2
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 misc -> /dev/block/mmcblk0p31
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 modem -> /dev/block/mmcblk0p1
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 oem -> /dev/block/mmcblk0p39
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 padA -> /dev/block/mmcblk0p11
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 padB -> /dev/block/mmcblk0p22
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 padC -> /dev/block/mmcblk0p40
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 padD -> /dev/block/mmcblk0p32
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 persist -> /dev/block/mmcblk0p26
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 recovery -> /dev/block/mmcblk0p35
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 rpm -> /dev/block/mmcblk0p8
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 rpmBackup -> /dev/block/mmcblk0p14
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 sbl1 -> /dev/block/mmcblk0p3
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 sbl1bak -> /dev/block/mmcblk0p12
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 sdi -> /dev/block/mmcblk0p4
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 sec -> /dev/block/mmcblk0p5
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 sp -> /dev/block/mmcblk0p23
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 ssd -> /dev/block/mmcblk0p34
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 system -> /dev/block/mmcblk0p41
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 tz -> /dev/block/mmcblk0p10
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 tzBackup -> /dev/block/mmcblk0p16
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 userdata -> /dev/block/mmcblk0p42
lrwxrwxrwx 1 root root 20 1970-02-24 06:55 utags -> /dev/block/mmcblk0p9
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 utagsBackup -> /dev/block/mmcblk0p15
lrwxrwxrwx 1 root root 21 1970-02-24 06:55 versions -> /dev/block/mmcblk0p17

以第一行为例,大概可以这样写:

-e 's block/platform/msm_sdcc.1/by-name/aboot mmcblk0p7 '

以此类推,可以考虑写个脚本简化这一过程,不过记住最后的反斜线后面应该是啥都没有的 😂 (说多了都是眼泪啊……)

编译 hybris-hal

设置相应的环境变量:

# 就是汝上一节写的那个啦
$ source ~/.hadk.env
$ cd $ANDROID_ROOT
# 换 python2
$ source venv/bin/activate
# 如果硬盘够用的话,可以考虑用 ccache 缓存中间文件加速编译
$ export USE_CCACHE=1
# 设置编译 Android 的环境变量
$ source build/envsetup.sh
$ export LC_ALL=C
$ breakfast $DEVICE

然后开始编译:

# 和其它编译类似,可以通过 -j 并行编译提高速度。
$ make -j4 hybris-hal

检查内核配置是否符合 hybris 的需要:

$ hybris/mer-kernel-check/mer_verify_kernel_config ./out/target/product/$DEVICE/obj/KERNEL_OBJ/.config

WARNING: CONFIG_CGROUP_MEM_RES_CTLR_KMEM is invalid
It is unset
Allowed values : y, !
Comment says: systemd (optional): http://0pointer.de/blog/projects/cgroups-vs-cgroups.html

WARNING: CONFIG_SECURITY_SELINUX_BOOTPARAM is invalid
It is unset
Allowed values : y, !
Comment says: Required by hybris, SELinux needs to be disabled.
Leave as not set, if you have unset AUDIT (read more about the CONFIG_AUDIT flag)

WARNING: CONFIG_CGROUP_MEM_RES_CTLR_SWAP is invalid
It is unset
Allowed values : y, !
Comment says: systemd (optional): http://0pointer.de/blog/projects/cgroups-vs-cgroups.html

WARNING: CONFIG_CGROUP_MEM_RES_CTLR is invalid
It is unset
Allowed values : y, !
Comment says: systemd (optional): http://0pointer.de/blog/projects/cgroups-vs-cgroups.html

WARNING: CONFIG_FW_LOADER_USER_HELPER is invalid
Value is: y
Allowed values : n, !
Comment says: it's actually needed by some Lollipop based devices;
systemd(optional): http://cgit.freedesktop.org/systemd/systemd/commit/README?id=713bc0cfa477ca1df8769041cb3dbc83c10eace2

可(ken)能(ding)会出一些 ERROR 和 WARNING ,按提示去改汝的 defconfig 去吧……

啥?汝不知道汝的 defconfig 是哪个?这里有几种确定的方法:

  • 看 $ANDROID_ROOT/device/$VENDOR/*/BoardConfig*.mk 里的 TARGET_KERNEL_CONFIG
  • 或者去 kernel/arch/arm/configs 里按 codename 瞅瞅? 😂

编辑完以后 make -j4 hybris-boot 重新生成内核再来一遍。 以及因为内核配置可能有依赖关系和这个检测脚本有点老的缘故,有时汝改了 defconfig 警告还是有 😂, 只要没有 ERROR 多半就 OK 😂

如果已经尽力消除所有 ERROR 和大多数 or 全部 WARNING以后,运行 make hybris-recovery 生成恢复内核。


未完待续 *2 😂

by ホロ at June 06, 2018 04:00 PM

May 24, 2018

百合仙子

这个博客要死了

本文来自依云's Blog,转载请注明。

很早的时候,那个时候我还在读大学,博客火了一阵。

那时候我刚拥有自己的计算机没多久。那时候我才开始在 Linux 系统里摸索着自学编程。犹犹豫豫地,我注册了一个 WordPress 账号。过了一两周的样子,我那个只有「Hello World」的博客随着众多 WordPress 博客一起被墙了。

那么就换一个地方吧。没过多久,我开通了 Google 提供的 blogger 博客。次日被墙。

那个时候,在国内的网站上发布内容还不需要提供手机号,位于国内的网站也不需要备案。所以我找了个国内的服务,也就是由当时我经常上的 Ubuntu 中文论坛所提供的博客服务。我在那里写过几篇不成熟的文章。过了挺长一段时间的。后来,他们宣布中止服务。

我后来就来到了这里,is-programmer.com。那时候这里还挺繁荣的,有好多人在这里写博客。那时候也没有多少垃圾评论。就这么安静地过了很长时间,站长突然联系不上了。再接着就是用户越来越少,垃圾评论越来越多。我挺担心哪天域名过期,然后自己的博客就消失了。还好我之前向站长申请过自定义域名,于是就用上了。使用 JavaScript 做了跳转和替换,Google 说这样子也是可以的,不过百度那边显然不认可这种做法。后来我又担心这VPS到期没人续费。还好这些都没有发生,域名和VPS都还活着。当然为了以防万一我写了个爬虫,把博客数据爬回来做了备份。

再后来,博客挂了差不多一天。我想方设法登录到了服务器上,删除了大量缓存和日志文件,腾出来些磁盘空间,这才又恢复。然后顺便把之前上传的文件也备份了回来,把域名跳转改成了通过301重定向的方式。不过很可惜,百度依旧不懂。

就这样勉强活着吧。我本来想自己写一个博客程序,可生活实在是太累了,就一直也没有写出来。也不太想用静态博客,因为不方便评论。特别是 Disqus 曾经用欺骗的方式拿了我的社交账号又要求注册 Disqus 账号,以及后来默认不允许匿名评论、匿名评论不显示头像,使得我对 Disqus 挺反感的。

而就在前几天,我这博客被入侵了。入侵者篡改了首页。虽然刷新缓存之后就恢复了,但我感觉很不好。

这博客,已经老了。就算我通过各种打补丁的方式,支持 HTTPS,支持 IPv6,更换自己控制的域名,使用脚本更改页面中的旧链接,但是,它还是在一点一点、不可避免地衰老。

by 依云 at May 24, 2018 05:01 PM

May 14, 2018

百合仙子

Windows 10 中配置网络共享

本文来自依云's Blog,转载请注明。

有一个很讨厌的公司叫「深信服」,英文名叫「Sangfor」。它开发的私有协议的 SSL VPN 客户端 EasyConnect 终于支持 Ubuntu 了,然而我没能在 Arch Linux 上正确地把它跑起来。图形界面是 electron 之类的,这部分没有问题。但有一个会往 /usr/share 下写日志的 root 进程,应该是负责真正的 VPN 创建的,死活连不上。

那么就在 Windows 虚拟机里跑吧。所以我的 Linux 系统需要通过 Windows 访问部分网络了。只是 HTTP 的话倒是可以装个 HTTP 代理搞定,但是我还要连 ssh 的。

正文开始了。其实整个过程并不难,关键在于很难找到资料。

首先,当然是给 Windows 一张 host only 的网卡,用于两个系统之间的连接。以及,把那个 VPN 连上。

然后,打开「网络设置」,选择「更改适配器选项」。右键单击要共享的网络适配器(比如此例中是那张 Sangfor VPN 的网卡),选择「属性」。

选择「共享」选项卡,把第一个框给勾选上,下边下拉菜单选择那张 host only 的适配器。确定。

就这样,OK 了。

Windows 10 中配置网络共享

要注意的是,此操作会将网卡的 IP 强制设置为「192.168.137.1」。就像图中可以看到的那样,微软总喜欢「提供策略,而非机制」。假定你是给你的家庭共享。假定你要共享的是 Internet 连接(本例中其实是 VPN 连接)。所以它也假定了你的「家庭」网络中 Windows 可以随意选择 IP 地址,假定你需要 DHCP 服务。

Linux 这边配置起来就容易多了。Virtualbox 的 vboxnet0 接口本来用的是 192.168.56.0/24 网段,但是给它配置另外的 IP 地址,往它里边扔它不了解的目标地址的包,它也不介意的。

sudo ip a add 192.168.137.2/24 dev vboxnet0
sudo ip r add 172.16.2.9/32 via 192.168.137.1

以上给 vboxnet0 添加了 IP 地址,并且让需要走 VPN 的目标地址(172.16.2.9)走 Windows 的网络。

PS: 开着防火墙的话,Windows 10 默认是忽略 ping 的。在防火墙的「入站规则」里启用「文件和打印机共享(回显请求—ICMPv4-In)」之后才能 ping 它。

另见阿森人的《伪·如何在 Linux 下使用深信服 SSL VPN》一文。

by 依云 at May 14, 2018 11:01 AM

May 10, 2018

quininer kel

wasm-stack-overflow

WebAssembly StackOverflow

Mon Apr 30 16:50:47 2018

WebAssembly 是一個設計運行在瀏覽器上的類彙編語言, 藉助它可以令 C / C++ 之類的語言運行在瀏覽器上,以期得到性能提升。

新功能自然會引入新攻擊面,可以預見 C / C++ 上經常出現的緩衝區溢出並不會在 WebAssembly 上消失。

有什麼不同

WebAssembly 同常見的彙編有很多不同的地方,例如

因爲第三點,WebAssembly 在一定程度上是 Harvard architecture,這爲 WebAssembly 帶來了很大的安全優勢,但棧溢出仍是可能的。

一個例子

我們舉個虛構的例子來證明 WebAssembly 棧溢出的可行性。

Rust 代碼:

```rust // ...

[wasm_bindgen]

pub fn hash_with_key(key: &str, data: &str) -> u32 { let mut val = [0; 32]; let key = key.as_bytes(); let data = data.as_bytes();

val[..3].copy_from_slice(&key[..3]);
val[3] = 0x33;
val[4..][..data.len()].copy_from_slice(&data);

js_hash(&val)

}

[wasm_bindgen]

pub fn table_index(i: usize) -> u8 { let a = [b'a', b'd', b'c', b'b']; unsafe { *a.get_unchecked(i) } } ```

WebAssembly 的 wat 格式基於 S-expression,指令精簡,與傳統彙編相比,相對容易閱讀。

但鑑於編譯出的 wasm 較長,下面我們只看我們關心的內容, 完整的內容可以看這裏

(global (;0;) (mut i32) (i32.const 1050544))

指令 global 定義了一塊可變的全局內存。 其中 1MB 是棧空間,剩下的是數據段。

``` (func $hash_with_key (type 2) (param i32 i32) (result i32) (local i32 i32 i32) get_global 0 i32.const 32 i32.sub

;; ...

    get_local 2
    get_local 0
    i32.load16_u align=1
    i32.store16
    get_local 2
    get_local 0
    i32.const 2
    i32.add
    i32.load8_u
    i32.store8 offset=2

;; ...

) ```

函數 hash_with_key 在棧上開闢 32bytes,將 key 寫入棧上,而退棧時未將其清零。

``` (func $table_index (type 4) (param i32) (result i32) (local i32) get_global 0 i32.const 16 i32.sub

;; ...

i32.const 12
i32.add
get_local 0
i32.add
i32.load8_u)

```

函數 table_index 在棧上開闢 16bytes,偏移 12bytes 讀取一個 byte。

簡單分析可以知道,在用戶正常調用 hash_with_key(..) 之後, 攻擊者只需要調用 table_index(16 - 12 - 32 + x) 便可獲得寫入棧上的 key。

Read Stack Overflow

侷限

雖然棧溢出是可行的, 但指令與數據的分離,使得我們只能讀寫棧內容而不能改變控制流。 這意味著 ROP、return2libc 之類的技巧不再有效, 棧溢出將只能在邏輯方面造成一些影響,危險度下降不止一個等級。

柳暗花明

而 WebAssembly 想要支持 函數指針、虛函數、trait 對象,就必須引入一些動態方案。

WebAssembly 的方案是將所有需要動態使用的函數放入 Table, 在運行時通過引索指定,並且在調用時會檢查函數簽名。

這個設計試圖在最大程度上降低風險,但無可避免的爲攻擊者開了一扇窗。

XSS Game

基於 WebAssembly 棧溢出,我做了一個簡單的 XSS Challenge, 源代碼託管在 wasm-xss-game。 有興趣的讀者可以一試。


  • 本文有意忽略堆溢出,以避免引入衆多 Allocator 的複雜性。
  • 作者不熟悉二進制安全、計算機原理。胡言亂語不知所云,有錯勿怪。

by quininer kel at May 10, 2018 06:31 PM

May 08, 2018

ヨイツの賢狼ホロ

把电脑上的 Shadowsocks 配置分享给手机使用的一点思路

限于 shadowsocks-libev 😂

问题

手机上的 Shadowsocks 客户端只能从 ss:// 开头的链接读取配置, 并不能直接用 libev 的配置文件 😂

Windows

Windows 的 Shadowsocks-windows 是可以生成 ss:// 的链接的,还可以生成二维码,于是不在本文的讨论范围内 (雾

生成链接

一个普通的 Shadowsocks-libev 的配置文件大概长这样:

{
"server":"server_address",
"server_port":9999,
"local_address": "127.0.0.1",
"local_port":8888,
"password":"some_password",
"timeout":300,
"method":"aes-128-gcm",
}

在 Python 里可以用 json.read() 把 JSON 文件转换成一个字典:

# 偷点懒不单独写一行开文件啦……
import json
config = json.load(open("/path/to/config.json"))

一个普通的 ss:// 链接大概像这样

ss://{base64编码过的加密方式和密码,中间用冒号分开}@服务器:端口?插件;插件选项#配置名称

如果服务器是个 IPv6 地址,那么两边包上中括号……

对一个字符串直接 encode() 就有二进制流,反过来对着二进制流 decode() 就可以有字符串(感觉这说法好不正经 😂)

以及可以用 base64 模块对二进制流进行 base64 编码和解码:

import base64
encoded = base64.encodebytes("some_string".encode())

当然出来的也是个二进制流,需要的话可以再整成字符串,以及消除空白 😂

encoded_string = encoded.decode().strip()

其它的选项从配置文件里直接读就好,配置名称需要的话就写一个。

生成二维码

方法有很多 😂 例如用 pypi 里的 qrcode:

import qrcode
img = qrcode.make("some_text")

需要的话可以把这个图像保存到某个地方,然后打开它:

img.save("/path/to/images")

要在 Python 里运行其它程序?最简单的方法是 os.system() 😂 稍微复杂一点的可以试试 Subprocess:

import os
import subprocess

os.system("xdg-open /path/to/images")
subprocess.Call(["xdg-open","/path/to/images"])

这样就会用默认的图片查看器打开刚生成的二维码啦,用汝的手机扫一下看看? 😂

水文 +1……

by ホロ at May 08, 2018 04:00 PM

May 07, 2018

Felix Yan

Linux 发行版:“强迫症患者”们的共识社区

世界上有几百个还在更新的 Linux 发行版。新手常常感叹挑花了眼,换来换去也找不到自己满意的。维护一个发行版需要花费很多时间、精力,为何人们要这样“重复劳动”呢?

  • “强迫症患者”

我小时候追求整齐、秩序,无论是家里的电灯开关还是电脑上的图标,一定要排列的整整齐齐,不惜自己接电线、一个个重命名文件。“我的电脑”、“我的文档”、“网上邻居”,下面的蓝色 e 名字太长,就改叫“上网浏览”吧……然而随着安装了越来越多的软件,这些“秩序”被不断破坏,自己不断妥协。有的程序在我的文档里乱放目录——忍。有的程序会自动下载更新,然后在我代码写到一半的时候弹出来更新提示——忍。有的程序会带各种运行时包安装、替换系统文件导致另外一个程序运行不了——忍。

每次出了大问题的时候,所有人都告诉我:“现在只能重装了。”

2008年的时候,厌倦了折腾各种魔改定制 WinPE 的我首次下定决心安装了 Ubuntu 8.04,从此打开了新世界的大门。

不再需要依赖猜测。手握源代码,就如同掌握了施工的图纸,一切不符合心中秩序的地方都能找到原理。一群志同道合的前辈早已构建了井井有条的目录结构、依赖关系、软件仓库、……一切都显得那么美好。和每个第一次玩 Linux 桌面的折腾狂一样,我花了很多时间试图让 GTK+ 和 Qt 的程序界面一样而且好看,以及折腾 compiz 特效。

然而事情也不是那么完美。当时我在压片组用 x264 压片,采用的方案是使用加上一些特别 filter、并且带 lavf 输入的 x264 命令行。我用 wxPython 做了一些小工具,比如音轨提取、mkv 合成等,但是自动压片脚本需要的 x264 不能用仓库里的版本。我注册了 Launchpad 帐号并创建了一个 PPA,接下去,我花了很长很长时间都没有学会打出一个靠谱的包。别说各种 macro 的使用,就连拆包的部分都让我焦头烂额了。

长久以来折腾的零碎结果——各种经验、配置文件、补丁、翻译,散落在各种网站、论坛、聊天工具,而自己真正想分享出去的成果——软件包,又不知如何下手。我想要为这份秩序做点贡献,怎么办呢?后来群里的大牛们给我了一个简单一些的方法——AUR。

我在 2011 年安装了 Arch,从此找到了贡献之路。

  • 共识社区

使用 Ubuntu 的时候,我有过很多想打包的软件,也曾经用简单的 checkinstall 打出过数个“勉强算是包”的包,但是这个过程一直十分痛苦,以至于我搁置了大量的 TODO。

而在 AUR 里,一个简单的十几二十行普通 bash 语法的 PKGBUILD 文件,就描述完了一个包。字体、游戏、输入法,开发中用到的 python 库,都被我轻松地打成了 Arch 的软件包。2012 年,我申请成为了 Arch 的 Trusted User,将这些包中使用率高的部分加入了官方仓库。在 IRC 和 TU 们的闲聊中,我能感受到或多或少大家都是因为 pacman / PKGBUILD 体系的简洁、维护轻松而走到了一起。

一个人的力量是有限的,我很高兴我找到了这样一群和我类似的“强迫症患者”。开源软件千千万万,自己选择费心费力。有的作者很马虎,有的作者不喜欢条条框框,有的作者对安全更新嗤之以鼻——而发行版的打包者们,则成为了一道防线。我们形成了一些共识,无论是代码里的编译选项还是软件的默认行为,都会尽力向上游作者们争取、提供修改意见,或者直接提交补丁。这样的共识或许不完全和内心的秩序一致,但也已经相当接近了。每人维护一部分的软件包,承担起相应的责任,而其他人就可以坐享这份成果,大家的“强迫症”都得到了满足。

在我看来,这就是一个发行版存在的意义。轻易切换发行版使用的用户们,可能只是不像我们这么傻,这么认真。

近年来每每被问起诸如“Flatpak 会让发行版大一统”、“Docker 会让发行版大一统”、“把 npm 仓库里的包打成 Linux 软件包毫无意义”等等问题时,我时常会感到难以回答。不是没有道理可讲,实在是不知如何讲起。这些都是很有意义的理念和技术,只是它们取代不了形成一个发行版的共识。所谓共识,无非是在取舍时看重哪边更多一点。

Linux 发行版,作为基于共识形成的社区,正是由我这样的“强迫症患者”所组成。

by Felix Yan at May 07, 2018 07:22 AM

May 05, 2018

中文社区新闻

js52 52.7.3-2 更新需要手动干预

由于 /usr/lib/libmozjs-52.so 的 SONAME 不符合其文件名, ldconfig 创建了未管理的文件 /usr/lib/libmozjs-52.so.0 。现在已经修正这一问题,两个文件都包含在新包中。

为防止更新报错,请于更新前删除 /usr/lib/libmozjs-52.so.0

by farseerfc at May 05, 2018 11:16 AM

April 30, 2018

Hexchain Tong

在 Linux 上使用 Docker 和 Java 开发酷 Q 插件

最近出于一些奇怪的原因,我需要写个 QQ 机器人。

酷 Q 是个比较常见且好用的机器人框架,但很可惜是 Windows only 的。虽然有官方支持的(使用 Wine 的) Linux Docker 镜像,但为了开发插件,还要设立交叉编译开发环境,十分麻烦。在论坛上发现酷 Q 有 Java 插件,就可以借助 JVM 使用各种原生的工具开发插件,然后扔进 Docker 里的 Wine 的 JRE 去运行。

获取 Docker 镜像和 Java 插件

首先要获取 CoolQ 的 Docker 镜像,并启动一次。这是为了建立基本的酷 Q 目录结构:

coolq/
├── app
│   ├── com.coxxs.music.cpk
│   ├── com.coxxs.start
│   │   └── start.cfg
│   ├── com.coxxs.start.cpk
│   ├── com.coxxs.status.cpk
│   └── moe.min.qa.cpk
├── bin
│   ├── CQP.dll
│   ├── gzip.dll
│   ├── htmlayout.dll
│   ├── libeay32.dll
│   ├── libiconv.dll
│   ├── sqlite3.dll
│   └── zlib1.dll
├── conf
└── CQA.exe

然后下载 酷 Q Java SDK 并按提示放入 coolq/app/ 目录中,启动酷 Q 并在插件管理器中启用 Java 插件。插件会提示找不到 JRE,暂时忽略。

下载 Windows JRE

然后,到 http://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html 获取32 位的 Windows JRE 压缩包,如 jre-8u172-windows-i586.tar.gz。这里使用压缩包是为了避免额外的安装步骤。

然后将 tar 包解压至 coolq 目录下,解压之后应该有 jre1.8.0_172/bin 等目录。

编辑 coolq/app/com.sobte.cqp.jcq/conf/setting.ini 文件,修改 JrePath 指定 JRE 路径(Docker 中的根目录对应 wine 中的 Z:\):

[JavaVM]
JrePath=Z:\home\user\coolq\jre1.8.0_172\

然后重启酷 Q,此时插件应该能找到 JRE。

做插件

参考论坛提供的 Demo 写插件。需要注意 json 文件中的 appid 字段,和 appInfo() 的返回格式。

然后编译构建,将 jar 包和 json 按正确的名称复制到 coolq/app/com.sobte.cqp.jcq/app/ 下,重启酷 Q 就会生效。

使用 maven-assembly-plugin 可以将依赖打进最终生成的 jar 包中。

stdout(竟然)是可用的,可以简单的使用 println() 输出调试信息。但输出的中文会乱码,原因是 Java 在 Windows 上的输出并不是 UTF-8 编码的。参考 https://stackoverflow.com/a/29231668 修改 System.out 的编码后解决。

by hexchain at April 30, 2018 10:10 AM

April 28, 2018

Phoenix Nemo

制作 Arch Linux 系统模板镜像

阿里云镜像制作踩坑记。

此文章主要记录按照阿里云 Customized Linux 制作 VPC 镜像的过程。一些部分也可用作制作其他平台镜像的参考。

当然记录的原因主要是 Arch 上的 cloud-init 打死无法在阿里云上修改 root 密码,就很气。

建立虚拟机

因为要制作 Customized Linux,所以第一步无法在阿里云平台上使用公共镜像制作。本机启动一个 Virtual Box,新建虚拟机,虚拟磁盘选择 RAW/IMG 格式即可。

按照一般步骤安装 Arch Linux,需要整个磁盘仅有一个分区。虽然很多平台支持多分区的镜像文件,但是莫名在这里踩了坑所以。

(另外吐槽:vps2arch 居然不帮我把 base-devel 装全了?!)

系统配置

安装一些必需的包。

1
# pacman -S qemu-guest-ga openssh

启用服务。

1
2
3
# systemctl enable qemu-ga
# systemctl enable sshd
# systemctl enable systemd-networkd

网络配置

哪个魂淡跟我讲 VPC 是 DHCP?装着 cloud-init 的 Arch 就可以自动设置内网 IP,这个没装的就 GG。

修改文件 /etc/systemd/network/default.network

1
2
3
4
5
[Match]
Name=en*

[Network]

DHCP=ipv4

总之先这样放着。

定制脚本

根据阿里云的文档,cloud init 不生效的时候需要用约定好的配置文件和脚本完成各种兼容动作。

新建目录 /aliyun_custom_image

新建文件 /usr/bin/aliyun-custom-os,写入内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/bin/bash

os_conf_dir=/aliyun_custom_image
os_conf_file=${os_conf_dir}/os.conf

load_os_conf() {
if [[ -f $os_conf_file ]]; then
. $os_conf_file
echo $password
return 0
else
return 1
fi
}

cleanup() {
# ensure $os_conf_file is deleted, to avoid repeating config system
rm $os_conf_file >& /dev/null
# ensure $os_conf_dir is exitst
mkdir -p $os_conf_dir
}

config_password() {
if [[ -n $password ]]; then
password=$(echo $password | base64 -d)
if [[ $? == 0 && -n $password ]]; then
echo "root:$password"
echo "root:$password" | chpasswd
fi
fi
}
config_hostname() {
if [[ -n $hostname ]]; then
echo "$hostname" > /etc/hostname
hostnamectl set-hostname $hostname
fi
}
config_network() {
if [[ -n $eth0_ip_addr ]]; then
config_interface
systemctl restart systemd-networkd
fi
}
config_interface() {
mask2cdr $eth0_netmask
cat << EOF > /etc/systemd/network/default.network
# Generated by Aliyun Custom OS helper
# DO NOT EDIT THIS FILE! IT WILL BE OVERWRITTEN

[Match]
Name=$(ip link | awk -F: '$0 !~ "lo|vir|wl|^[^0-9]"{print $2a;getline}' | sed -e 's/^[[:space:]]*//')

[Network]
Address=$eth0_ip_addr/$netmask
Gateway=$eth0_gateway

[Link]
MACAddress=$eth0_mac_address

[Address]
Address=$eth0_ip_addr/$netmask
EOF
echo "nameserver 1.1.1.1" > /etc/resolv.conf
for ns in $dns_nameserver
do
echo "nameserver $ns" >> /etc/resolv.conf
done
}

mask2cdr() {
# Assumes there's no "255." after a non-255 byte in the mask
local x=${1##*255.}
set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*}
x=${1%%$3*}
netmask=$(( $2 + (${#x}/4) ))
}

if load_os_conf ; then
config_password
config_hostname
config_network
cleanup
else
echo "not load $os_conf_file"
fi

赋予执行权限

1
# chmod +x /usr/bin/aliyun-custom-os

新建 systemd unit 文件 /usr/lib/systemd/system/aliyun-custom-os.service 写入内容

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=Aliyun Custom OS Helper Script

[Service]

Type=oneshot
ExecStart=/usr/bin/aliyun-custom-os
TimeoutSec=30
StandardInput=tty
RemainAfterExit=yes

[Install]

WantedBy=multi-user.target

然后启用这个服务

1
systemctl enable aliyun-custom-os

挂载镜像

正常 shutdown 虚拟机,然后拿到镜像文件的路径。例如 ~/vm/archlinux.img

接下来需要将此镜像挂载到宿主机系统中修改、清理文件。首先确定镜像文件中的分区位置:

1
2
$ file ~/vm/archlinux.img
archlinux.img: x86 boot sector; partition 1: ID=0x83, active, starthead 32, startsector 2048, 41938944 sectors, code offset 0x63

得知 startsector2048

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ fdisk -l ~/vm/archlinux.img
You must set cylinders.
You can do this from the extra functions menu.

Disk archlinux.img: 0 MB, 0 bytes
255 heads, 63 sectors/track, 0 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x91d8e293

Device Boot Start End Blocks Id System
archlinux.img1 * 1 2611 20969472 83 Linux
Partition 1 has different physical/logical endings:
phys=(1023, 254, 63) logical=(2610, 180, 2)

得知 sectorsize512

使用 mount 命令带 offset 参数挂载镜像中的分区:

1
2
$ sudo mkdir -p /mnt/img
$ sudo mount -t ext4 -o loop,offset=$((2048*512)) /path/to/archlinux.img /mnt/img/ # 更改 -t auto 或者其他此分区使用的文件系统格式

就可以 cd /mnt/img 看到镜像里的 rootfs 啦。

清理/检查文件

要删除的:

1
2
3
4
5
# rm /root/.bash_history # _(:з」∠)_
# rm /etc/ssh/ssh_host_* # 强制每次部署的时候重新生成密钥对
# rm -r /var/log/* # 清理不需要的日志
# rm -r /var/cache/* # 清理缓存
# rm /etc/resolv.conf.bak # 避免恢复成制作时的 DNS

要检查的:

/etc/hosts - 我不知道为什么,第一次的时候把这个文件留空了(:з」∠)

/etc/resolv.conf - 鉴于总是有人喜欢手动修改这个文件,所以直接把它写成静态文件好了。内容例如

1
2
nameserver 8.8.8.8
nameserver 8.8.4.4

/etc/ssh/sshd_config 中是否允许 root 密码登陆。

准备镜像

退出 /mnt/img 目录,然后卸载镜像

1
# umount /mnt/img

(可选)使用 qemu-img 转换镜像格式到 VHD,减少镜像文件大小。特别是对国内的小水管上传(心疼

1
$ qemu-img convert -f raw -O vpc archlinux.img archlinux.vhd

上传镜像

在相同的 region 创建一个 OSS bucket,然后创建一个 RAM 子用户赋予 OSS 写权限并创建 Access Key,使用 OSSBrowser 上传准备好的 VHD 文件。

上传完毕后,在 ECS 标签下的镜像标签即可导入镜像。如果是第一次操作,需要给 ECS 授权访问 OSS。在导入的页面提示中提供了授权的链接。镜像内容配置如下:

  • OSS Object 地址:镜像文件在 OSS 中的 URL
  • Image 名称:archlinux-2018.1-x86_64 … 等符合要求即可
  • 操作系统:Linux
  • 系统盘大小:40GB
  • 系统架构:x86_64
  • 系统平台:Customized Linux
  • 镜像格式:VHD(如果是 img 就选 RAW)
  • 镜像描述:随便写啦。

确定后应该就会开始制作镜像了。

测试

因为没有做经典实例的兼容,这个镜像只能用于 VPC 的实例。总体而言,cloud-init 本来兼容的 Arch 却无法更改 root 密码(其他的倒是没问题),所以才选择了用一个 dirty 的方案来实现。

不知道应该说阿里云的工程师对自定义镜像的考虑周到还是对不同发行版的考虑欠妥…?

最后庆幸倒腾来去上传了好多遍 20G 的文件,日本运营商家宽带宽对等真的是帮了大忙,不然一个镜像制作不知道要到什么时候 > > (斜眼看国内三大运营商

参考:

阿里云镜像制作踩坑记。

此文章主要记录按照阿里云 Customized Linux 制作 VPC 镜像的过程。一些部分也可用作制作其他平台镜像的参考。

当然记录的原因主要是 Arch 上的 cloud-init 打死无法在阿里云上修改 root 密码,就很气。

April 28, 2018 05:10 AM

April 20, 2018

中文社区新闻

glibc 2.27-2 和 pam 1.3.0-2 升级或需手动干预

glibc 最新版本删除了 NIS 和 NIS+ 的相关支持。在 filesystem 包中默认的 /etc/nsswitch.conf 文件已经根据这一变化做了相应修改。请在升级前确认是否存在 pacnew 文件,并确保已经合并了其中的变更。

如果需要 NIS 功能,仍然能通过安装 libnss_nis 包提供该功能。不过对于 NIS+ 目前官方源中没有替代方案。

同时 pam 1.3.0-2 包不再提供 pam_unix2 模块以及 pam_unix_*.so 兼容性符号链接。请在升级前仔细检查 /etc/pam.d 文件夹中的 PAM 配置,用 pam_unix.so 替换掉以上被删除的模块。使用 pam_unix2 的用户还需要在修改 PAM 配置之后重置他们的密码。 pambase 包提供的默认配置不需要额外修正。

by farseerfc at April 20, 2018 10:24 AM

April 18, 2018

Phoenix Nemo

在 Linux 服务器配置 LACP 与 VLAN

存储服务器不想放在 OVH 了。所以自己来托管一台机器,顺便折腾下 2x1Gbps 组 LACP Bonding。

前提:服务器需要至少 2 个千兆物理网卡,上联交换机支持 802.3ad。

配置交换机

这里使用的是 Cisco Nexus 3064PQ-10GE 交换机,我们的接口在 Eth1/21-22,port-channel 的配置如下:

1
2
3
4
5
6
7
8
9
# show interface trunk

--------------------------------------------------------------------------------
Port Native Status Port
Vlan Channel
--------------------------------------------------------------------------------

Eth1/21 1 trnk-bndl Po100
Eth1/22 1 trnk-bndl Po100
Po100 1 trunking --
1
2
3
4
5
6
7
8
9
10
show port-channel database
port-channel100
Last membership update is successful
2 ports in total, 2 ports up
First operational port is Ethernet1/21
Age of the port-channel is 0d:00h:02m:16s
Time since last bundle is 0d:00h:02m:04s
Last bundled member is Ethernet1/22
Ports: Ethernet1/21 [active ] [up] *
Ethernet1/22 [active ] [up]

配置服务器

服务器操作系统是 Arch Linux,由于蜜汁问题 netctl 无法启动网卡,就只好用 systemd-networkd 啦。

麻烦一些,但是也还算顺利。与往常一样,折腾服务器网络的时候需要备着 IPMI 以防 connection lost。

内核模块

需要加载 bonding 模块。将模块名写入列表,文件 /etc/modules-load.d/bonding.conf,内容只需要一行:

1
bonding

先别急着加载模块,为了防止模块自动建立一个默认网卡影响后续配置,以及设置 LACP Mode=4 … 等等,先加入一行参数。文件 /etc/modprobe.d/bonding.conf

1
options bonding mode=4 miimon=100 max_bonds=0

然后安装 ifenslave 包,再 modprobe bonding 即可。

bonding 虚拟网卡

首先创建一个虚拟网卡的设备。文件 /etc/systemd/network/bond0.netdev 内容为

1
2
3
4
5
6
7
8
9
[NetDev]
Name=bond0
Kind=bond

[Bond]

Mode=802.3ad
TransmitHashPolicy=layer2+3
LACPTransmitRate=fast
AdSelect=bandwidth

然后在此虚拟网卡上创建网络。这里使用两个物理网卡 eth0eth1 作为 bundle,交换机上的 VLAN id 是 113。文件 /etc/systemd/network/bond0.network 内容为

1
2
3
4
5
6
[Match]
Name=bond0

[Network]

VLAN=vlan113
BindCarrier=eth0 eth1

接下来分别为 eth0eth1 建立网络设置。

  • /etc/systemd/network/eth0.network
1
2
3
4
5
[Match]
Name=eth0

[Network]

Bond=bond0
  • /etc/systemd/network/eth1.network
1
2
3
4
5
[Match]
Name=eth1

[Network]

Bond=bond0

最后是 VLAN 的设置。前面设置了上联 VLAN id 是 113,这里分别建立 VLAN 的虚拟网卡(based on bond0) 并设置网络(IP, etc)。

  • /etc/systemd/network/vlan113.netdev
1
2
3
4
5
6
[NetDev]
Name=vlan113
Kind=vlan

[VLAN]

Id=113
  • /etc/systemd/network/vlan113.network
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Match]
Name=vlan113

[Network]

VLAN=vlan113

[Address]

Address=10.1.0.100/24

[Route]

Destination=0.0.0.0/0
Gateway=10.1.0.1
DNS=1.1.1.1

[Address]

Address=2600:x:x:x::2/64

[Route]

Gateway=2600:x:x:x::1

多个地址、IPv6 等可以写多个 [Address][Route]

至此就完成啦。开启 systemd-networkd 的自启动:

1
systemctl enable systemd-networkd.service

然后重启网络:

1
systemctl restart systemd-networkd.service

如果配置都没有问题,网络会中断十几秒然后恢复。现在查看网卡列表已经可以看到组合的网卡了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP mode DEFAULT group default qlen 1000
link/ether <REDACTED> brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc mq master bond0 state UP mode DEFAULT group default qlen 1000
link/ether <REDACTED> brd ff:ff:ff:ff:ff:ff
4: eth2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether <REDACTED> brd ff:ff:ff:ff:ff:ff
5: eno1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether <REDACTED> brd ff:ff:ff:ff:ff:ff
6: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether <REDACTED> brd ff:ff:ff:ff:ff:ff
7: vlan113@bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether <REDACTED> brd ff:ff:ff:ff:ff:ff

ethtool 查看 bond0 的速率显示 2000Mb/s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ethtool bond0
Settings for bond0:
Supported ports: [ ]
Supported link modes: Not reported
Supported pause frame use: No
Supports auto-negotiation: No
Supported FEC modes: Not reported
Advertised link modes: Not reported
Advertised pause frame use: No
Advertised auto-negotiation: No
Advertised FEC modes: Not reported
Speed: 2000Mb/s
Duplex: Full
Port: Other
PHYAD: 0
Transceiver: internal
Auto-negotiation: off
Link detected: yes

搞定收工(‘・ω・’)

Reference:

存储服务器不想放在 OVH 了。所以自己来托管一台机器,顺便折腾下 2x1Gbps 组 LACP Bonding。

April 18, 2018 10:49 AM

April 16, 2018

ヨイツの賢狼ホロ

GNU/Linux 中为不同的应用程序设置代理

方法挺多……

Web 浏览器(例如 Firefox 和 Chromium)

Firefox 可以在设置中自行设置 (参见 https://support.mozilla.org/en-US/kb/connection-settings-firefox )。

Chromium 默认会用系统的代理设置,不过可以用一个命令行选项强行在这次会话中使用代理,例如:

chromium --proxy-server="socks5://127.0.0.1:1080" --host-resolver-rules="MAP * 0.0.0.0 , EXCLUDE localhost"

不过也可以用个代理扩展,例如 SwitchyOmega

GNOME 3

可以从设置-网络-网络代理中设置:

GNOME 3 的代理设置

某些其它应用(是哪些 😂)

(例如 wget 和 curl),会使用形如 "proto_proxy" 的环境变量用来设置代理啦 (当然也有可能是全部大写的),例如:

# export var=value
export http_proxy=http://10.203.0.1:5187/
export https_proxy=$http_proxy
export ftp_proxy=$http_proxy
export rsync_proxy=$http_proxy
export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"

就设置成了 10.203.0.1:5187 作为 HTTP 代理,HTTPS、FTP 和 rsync 使用相同的代理。

于是可以把这样的语句写进一个脚本里在需要使用代理的时候 source 一下 😂

不过这样设置的环境变量一切换用户(例如 sudo)以后就没啦…… sudo 的话,可以用 -E 选项保持 环境变量,或者修改 /etc/sudoers 指定保持哪些环境变量:

Defaults env_keep += "http_proxy https_proxy ftp_proxy no_proxy"

socks5 代理

curl 和 pacman 可以设置 all_proxy 变量直接使用:

export all_proxy="socks5://your.proxy:1080"

那剩下的怎么办 😂

用 proxychains 让应用使用 socks5 代理

字如其名就是个链式代理工具啦 😂 Arch Linux 的话装上 proxychains-ng 就 OK 啦~

然后去修改 /etc/proxychains.conf ,选项有很多, 但是大多数时候只需要把最后一行换成汝自己的 socks5 代理的 IP 地址和端口就行😂

然后就可以像平常(?)一样:

proxychains -q /path/to/program

有关 proxychains 的更多用法可以去参考它的文档: http://proxychains.sourceforge.net/

用 privoxy 把 socks5 代理转化成 http 代理

Privoxy是一款不进行网页缓存且自带过滤功能的代理服务器,针对HTTP、HTTPS协议。 通过其过滤功能,用户可以保护隐私、对网页内容进行过滤、管理Cookie,以及拦阻各种广告等。 Privoxy可以单机使用,也可以应用到多用户的网络。 它也可以与其他代理相连(通常与Squid一起使用),更可以突破互联网审查。

https://zh.wikipedia.org/wiki/Privoxy

同样 Arch Linux 上装上 privoxy 就 OK 😂

然后去修改 /etc/privoxy/config ,选项还是有很多 😂, 但是大多数情况下只需要加上一行:

# forward-socks5   target_pattern  socks_proxy:port  http_proxy:port
# target_pattern 是 . 的话表示对所有域名适用
# http_proxy 是 . 的话表示继续不使用 HTTP 代理转发……
forward-socks5 . localhost:1080 .

然后启动 privoxy 服务,把 HTTP 代理设置成 localhost:8118 就好 😂

有关 Privoxy 的更多用法可以去参考它的文档: http://www.privoxy.org/user-manual/

by ホロ at April 16, 2018 04:00 PM

April 15, 2018

quininer kel

Stratis

Stratis

Sun Apr 15 18:14:39 2018

Stratis 是 redhat 放棄 Btrfs 之後推出的代替品, 使用類似 LVM 的方案來提供有如 btrfs、zfs 般強大的功能。

其實 Stratis 釋出 0.5 有一段時間了,今次抽出時間來嚐下鮮。 Stratis 目前沒有面向用戶的文檔,故久違的寫一篇博文用以記錄。

準備工作

從 AUR 安裝 stratisdstratis-cli, 然後啟動 stratisd

$ systemctl start stratisd

開始

首先我們清理要用的塊設備

$ sudo wipefs -a /dev/sda

但當前版本創建 pool 的時候會碰到錯誤,stratisd 認爲這個塊設備已經被使用

$ stratis pool create stratis /dev/sda Execution failed: 1: Device /dev/sda appears to belong to another application

根據 issue, 如果塊設備的前 4/8k 字節未置零,stratisd 不會使用它。

知道原因就簡單了,我們將它置零

$ dd if=/dev/zero of=/dev/sda bs=1024 count=8

然後就可以正常創建 pool

$ stratis pool create stratis /dev/sda $ stratis pool list Name Total Physical Size Total Physical Used stratis > 238.47 GiB 52 MiB

隨後創建 fs

$ stratis fs create stratis storage $ stratis fs list stratis Name storage $ la /dev/stratis/stratis/storage Permissions Size User Date Modified Name lrwxrwxrwx 9 root 15 4月 18:21 /dev/stratis/stratis/storage -> /dev/dm-6

完成

創建完成,直接掛載就可以使用啦

$ sudo mount /dev/stratis/stratis/storage /run/media/quininer/storage $ sudo chown quininer /run/media/quininer/storage

用得開心。 ;-)

by quininer kel at April 15, 2018 04:27 PM

April 09, 2018

ヨイツの賢狼ホロ

在 GNU/Linux 下制作 Windows 的安装 USB

嗯……

UEFI

最简单的方法是把 U 盘格式化成 FAT32 文件系统,然后把 ISO 里的文件复制到 U 盘上。 (不是把那一个 ISO 文件复制到 U 盘上啦~)

BIOS

貌似直接 dd 不起作用……

有人说 unetbootin 可行,但是咱装好以后一片空白…… 😂

或者开个 Windows 虚拟机试试 rufus 或者巨硬的 Media Creation Tool ?(……)

于是就 ms-sys 了(不是 msys 😂,是 http://ms-sys.sourceforge.net/

  • 首先把汝的 U 盘格式化成 MBR 分区表,然后新建一个 NTFS 分区并把它设置成活动分区 (cfdisk 一气呵成(误))

    于是假设它是 /dev/sdb ,如果 mkfs.ntfs 的时候太慢的话可以加个 -Q 试试(

  • 然后把 Windows 的安装 ISO 挂载上,里面的文件复制到 U 盘上。

  • 把 Windows 7 的 MBR 写进 U 盘里去:

    sudo ms-sys -7 /dev/sdb

  • 把 NTFS 的 PBR 写进 U 盘的第一个分区(大概也是唯一一个)里去:

    sudo ms-sys -n /dev/sdb1

大概就可以用啦 😂 于是咱又水了一次……

by ホロ at April 09, 2018 04:00 PM

April 06, 2018

百合仙子

小米 Note 3 令人失望地方

本文来自依云's Blog,转载请注明。

小米 Note 3 入手的时候简单写过一篇文章。现在用了一些天了,有些新的体会,也是我最不满意的地方。

双摄像头看上去很厉害,但是照片大小只有1-2MB,不像我的 Z5C 一样有5-6MB。为什么呢? 在手机上,因为小米相册放大倍数有限,根本看不出来差别。但是传输到电脑上之后可以很明显地看出差别:

小米 vss Z5C

从图中可以看到,小米拍摄的照片(左)不仅比我的 Z5C 的(右)分辨率要低,而且有偏色、模糊不清(照片拍摄的是 Paperlike HD 的 Floyd 模式,所以那些沙粒一样的效果是本来就那样的;背景色有点偏绿也是肉眼所见之状态)。

这意味着,用于记录的时候,比如拍摄白板、幻灯片、小区通知,习惯了 Z5C 之后,用小米很可能在需要的时候才发现根本看不清(我已经有好几张记录用的照片现在才发现基本上是毁了)。

小米摄像头另外有一个大问题:在光照很强的时候(比如晴天室外),摄像头会有反光,在照片中呈现出一个偏绿色的亮点

摄像头反光亮点

之前已经提到过,这扬声器的设计挑握持姿势,很讨厌很讨厌:我左手握持的时候,为了保证手机不在遇到碰撞不稳等情况的时候意外滑落,我会用小指托住底边,刚好堵住扬声器的孔……

小米云同步能是能同步,但是很不及时。一般情况下,我 Z5C 拍好照片过一会儿,就可以在 Google Photos 上看到照片了。小米不知道怎么回事,经常看不到照片,需要手动打开相册然后等一会儿才会有。

啊对了,小米右上角那个天气,也是要点开才会更新的样子……

by 依云 at April 06, 2018 08:24 AM

March 26, 2018

ヨイツの賢狼ホロ

所谓的近况2??

从摸鱼中惊醒

1月和2月,几乎全部家当都在学校(包括唯一一台放着登录 Github 和 VPS 的 SSH 私钥 和 GPG 私钥的笔记本),然后就咸了)

3月回学校以后又回到了以前沉迷游戏不能自拔的日子,又咸了……

😂

关于故人

其实每个月都有不少人去世(参照 https://zh.wikipedia.org/wiki/Portal:讣闻 ), 不过听说霍金去和爱因斯坦谈笑风生(那天其实也是爱因斯坦诞辰)时还是有点震惊(误)。 虽然咱也就是翻过《时间简史》和《果壳中的宇宙》而已😞

然后过几天看到了这个:

其实和这俩人不能算是太熟 😔 ,不过在几天之前 @zhangyubaka 就一直在寻求帮助了, 还试图去找 @ayakaneko (虽然最后好像还是受到了威胁)。嘛现在就愿 Neko 安好吧~

有关事件的部分信息可以去看看某篇知乎专栏文章: https://zhuanlan.zhihu.com/p/34901959

呜呜……说不出话来,只能说咱没经历过不太懂得受到的伤害 😭

关于音乐游戏

从一个跟风总结开始 😂

休闲了好几年…… 😂 顺便给新的 https://t.me/mug_zh 打个广告 (划掉

关于无名计划

说的就是 https://github.com/KenOokamiHoro/it_have_not_get_a_name_yet 啦😂

感觉当初定下来的目标太大了的样子,又要 GNU/Linux ,还要兼顾一些隐私和安全保护相关 的知识……加上咱有没有那么多技能点……

曾经一度想用一个 GNU 钦定的 GNU/Linux 发行版(参见 https://www.gnu.org/distros/free-distros.html ), 最后还是放弃了,Debian 其实也海星 😂

嘛总之还是慢慢填吧……

by ホロ at March 26, 2018 04:00 PM

March 24, 2018

百合仙子

小米 Note 3 入手体验

本文来自依云's Blog,转载请注明。

为了对付诸如支付宝、淘宝、摩拜等在我的 Z5C 上特别卡的手机软件,我入手了一台小米 Note 3。

最初的感觉是:很滑,手感很不错。下面指纹识别很方便。

有点大,单手不容易按到对面。因为下巴上都是按键,所以放桌面边缘时不容易拿起来,会误触那些键。不过作为备机,在家的时候放桌面上就好了,解锁直接按,不需要经常拿起来。

没有拍照键,但是可以设置音量当快门用。有快速启动,但是是在系统设置的「手势及快速启动」中配置的。另外有个「街拍模式」,就是拿着手机,长按快捷键开始连拍或者摄像,而屏幕没有任何显示。

拍照默认带水印,可以在选项里关掉。前置摄像头拍照时默认带美颜(i.e. 别当镜子用)。还带了性别和年龄检测,以及我没太搞懂的「魔镜」评分功能。

云存储会存储原始品质的照片,免费空间只有5G,不像 Google 是 10G,并且可以无限量存储高品质照片。黑屏的时候似乎并不会同步照片,反正我等了好久,在网页端没看到。等再次打开相册时,它告诉我正在同步呢。

自带手电筒功能,可配置成亮屏后长按菜单键启动。不能像我那个 Xposed 模块那样,直接从黑屏状态启动。不过也没多大差别。

音质好不好不清楚,反正是够响。不过扬声器位于下边缘右边,手挡到的话会非常影响音质。我用过的 Z5C、Z3C、LT26i 上均没有发现这种事情,倒是我第一个智能手机 E15i 是这样,扬声器在背面,听歌时得俯卧着。

充电很快。剩余一半多的电,开始充电时 5.7V 0.9A,后来到了 6.6V 0.7A。不过使用小米充电器,我的Z5C更快,系统显示充电电流为 1.9A。我原来的充电器给 Z5C 充电时只有 0.9A 的,快了一倍。不过发生了一件令人悲伤的事情——忘记取下我的USB电流电压测量仪,它工作太久,累坏了……

SIM 卡槽需要使用配备的捅针去捅出来。不过需要的时候,最好同时配备一只汉子,或者女汉子,因为真的要用很大力,偏偏捅针就一个圈,细细的,底端也是面积极小的边缘,皮肤不够糙的话很难使上劲。当然也可以想办法,比如把针插好,然后对着桌面使劲磕几下,卡槽就会出来个边了。这体验比起 Z5C 用指甲一剥即开的体验,是有趣不少。

不插SIM卡无法安装未知来源的应用(apk 文件),也不知道这是什么鬼逻辑。小米商店里的东西还比较多的,条码扫描器、Elixir 2、Telegram 都有。HE 的 Network Tools 没有,不过可以去百度那边搜到。Google 应用说是因为授权问题没有,会提供位于百度上的版本。我没试过能不能安装,反正安装了也用不了……

默认浏览器默认为MIUI自己的浏览器,如要更改,需要去「设置」->「更多应用」->右上角菜单「默认应用设置」里修改,不会像 Android 原生系统那样让用户自行选择,也不能通过默认浏览器应用信息里的「取消默认操作」来重置。

主页是类似于 iOS 的:没有下边的「所有应用」界面,只有混杂在一起的应用图标和小部件。最让人受不了的是,应用图标被加了个半径非常大的圆角矩形,还带个白底

顶栏默认不显示通知图标。这样也好,因为放一天之后我一看,近十条没什么用的通知呢……可以设置显示网速,不过不知道是下行速率还是总和。可惜不能显示时间显示秒数。

总体来说,功能挺丰富,但是有些混乱,界限不清。比如两种设置快捷键的途径(相机内、设置内,其实 Android 系统原生快捷键比如截屏也是可以用的,但是设置里是没有的),比如两种设置默认应用的方法。比如到处都是的推广。应用商店里几乎搜任何东西都能看到知乎,搜 Network Tools 能看到 RealCalc。「游戏」应用里竟然有「直播」。建立个放应用的文件夹都能给你推荐应用。说得好听点,叫「紧密集成」,说不好听,就叫「高耦合」。

哦对了,这机器需要保持干燥。也就是说,我洗澡时想听歌还是得带上我的 Z5C。

2018年04月06日更新:续篇《小米 Note 3 令人失望的地方》

by 依云 at March 24, 2018 10:42 AM

March 22, 2018

Phoenix Nemo

迁移 DokuWiki 到 BookStackApp

Wiki 这么反人类的语法是怎么存在这么久的???????

总之受不了 Wiki 语法的可维护性了。什么?这玩意儿有维护性???

以及万年不更新的各种插件。系统升级后 PHP 7 不兼容,一看还是 swiftmail 的问题。生气。

正好有需求要整合一套知识库平台,搜索了一下 Confluence 的 alternative,发现了 BookStackAppPhabricator

前者适合个人或开源社区使用,后者则是一整套企业协作解决方案。对于我的需求来讲,BookStackApp 就足够啦。

页面数据

DokuWiki 并不使用数据库,因此没有一个通用的中间件来实现数据格式转换。而 DokuWiki 的语法非常奇葩——比如,它的一级标题是 ====== 这样 ======,六级标题才是 = 这样 =,正好和一般的 Wikitext 倒置。图片、内链等的表达方式也相当愚蠢,这些问题使我在思考迁移方案的第一个小时内就放弃了直接从源码转移的途径。

顺便,还有另外一个问题——本来为了使 Wiki 易于编写,这 DokuWiki 还安装了 Markdown 插件。因此部分页面中混杂着 Markdown 语法,更增加了源码处理的复杂度。

综合来看,最通用的数据格式,就是最终渲染出来的 XHTML 了。

图片

DokuWiki 的图片存储策略也是非常的奇特。由于它没有数据库,因此为了保持图片与页面的对应,它将图片存储在每个页面同样的路径下,并通过执行 PHP 的方式获取(扶额。

更甚者!!!

外链的图片,也是通过 /lib/exe/fetch.php 带参数来获取!!

我 的 天 哪。

因此既然在页面数据的考量中决定了使用最终渲染输出的 XHTML 来处理数据格式,图片也需要特殊的下载和归档技巧。这将需要使用 sanitize-html 提供的 transformer 方法来实现。

逻辑实现

一开始尝试了一些 Site Exporter 插件,但遗憾的是并没有什么真正能派上用场。甚至一些暴力递归下载所有页面和资源的脚本的表现也非常糟糕。

但是根据 DokuWiki 的官方 Tips,它可以将文章内容单纯导出 XHTML,只需要加上 ?do=export_xhtmlbody 参数即可。这就方便了,因为这样只需要一个完整的页面列表就可以了。随便找一个可以输出子命名空间的插件,新建一个页面用于从根命名空间展开就 OK 啦。

请求这个列表页面的 XHTML body 输出,使用 cheerio 遍历所有的 a 标签,就获得了所有要导出的页面地址。分别再去请求这些页面的 XHTML body 输出,做如下处理:

  1. 跟踪所有的 img 标签,下载图片文件并按预定义的路径规则和文件名归档。
  2. sanitize-html 清除所有不必要的标签、样式、id 和 class。
  3. sanitize-html 按预定义的路径规则更新所有 aimg 标签属性。

看代码

后来发现 DokuWiki 的性能不足以支撑异步请求的速度,额外加上了 sleep 模块来控制请求频率(扶额。

脚本执行完后,将图片目录移动到 BookStackApp 的对应位置,便可以直接读取所有的 HTML 文件来导入数据啦。

用了这么久,才发现原来还有比 raw HTML 更难以维护的数据格式啊…(望天。

Wiki 这么反人类的语法是怎么存在这么久的???????

March 22, 2018 04:15 PM

February 23, 2018

中文社区新闻

zita-resampler 1.6.0-1 -> 2 升级需要用户手动干预

zita-resampler 包的 1.6.0-1 版本在打包时缺失了一个库文件的符号链接,我们已经在 1.6.0-2 中加入了这个文件。如果您安装过 1.6.0-1 ,那么 ldconfig 将会在安装时创建这个链接,从而它会和 1.6.0-2 打包中的链接文件形成文件冲突。如果 pacman 提示文件冲突,请手动删除 /usr/lib/libzita-resampler.so.1 ,然后继续正常升级。

by farseerfc at February 23, 2018 06:13 AM

February 14, 2018

百合仙子

使用 VirtualBox 启动本地磁盘上的其它系统

本文来自依云's Blog,转载请注明。

VBox 可以从一个指向本地硬盘的 vmdk 文件启动虚拟机。

首先,为了避免使用 root 运行 VBox,我们需要给自己访问磁盘的权限。我即将启动的是位于 sda5 上的 openSUSE。它使用 UEFI 启动,所以 UEFI 分区的权限也是需要的。创建 vmdk 文件的时候需要读取分区表,因此,还需要 sda 的权限:

sudo setfacl -m u:$USER:rw /dev/sda{,1,5}

然后我们创建 vmdk 文件。使用-partitions 1,5选项的话,只有这两个分区能在虚拟机里访问,别的分区读的时候是全零,写入操作会被忽略。-relative选择使用分区设备名(sda1、sda5),这样创建好之后 VBox 不再需要对整块硬盘 sda 的权限了。另外会附带创建一个名字以 -pt.vmdk 结尾的文件。它是单独的分区表。如果是 MBR 启动的话,是可以直接在虚拟机系统里更新引导器的,不影响外边的系统。不过我这次是使用 UEFI 启动,所以用不上了。

VBoxManage internalcommands createrawvmdk -filename hostdisk.vmdk -rawdisk /dev/sda -partitions 1,5 -relative

创建好之后就可以撤销对 sda 的权限了:

sudo setfacl -b /dev/sda

然后去 VirtualBox 界面那边创建新虚拟机,并「启用 EFI」。另外,可以在存储设置里,把「控制器: SATA」的「使用主机输入输出 (I/O) 缓存」启用,似乎这样 I/O 会快一点。

VBox 的 EFI 并不像电脑的那样,按 F12 可以选择启动项。因此,它会启动默认的那个,也就是 /EFI/Boot/bootx64.efi。如果你想启动的系统不是这个的话,就把它的 efi 文件复制过来覆盖它。比如我是这么做的:

cd /boot/EFI/Boot
sudo cp ../opensuse/grubx64.efi bootx64.efi

如果是 Windows 10 并使用 MBR 启动的话,可以在虚拟机里用如下命令更新 MBR,干掉原来用于多启动的 grub:

bootsect /nt60 c: /mbr

做好之后就可以启动啦~

对于设备的权限设置,重启之后会丢失的。需要的时候再加上好了。

PS: openSUSE 自带了 VBox 的驱动啊,不过剪贴板共享不能用,大概只有显示驱动没带上服务。

PPS: 启动没一会儿就通知我更新出现错误,一看软件源设置,果然是 HTTP 的,被垃圾鹏博士劫持了。

by 依云 at February 14, 2018 08:36 AM

February 12, 2018

百合仙子

大上 Paperlike HD 电子墨水屏开箱体验

本文来自依云's Blog,转载请注明。

刚听说就已心动,无奈当时并不支持 Linux。后来由于一些事情耽搁到现在,终于到手了~

开箱啦

显示器整体尺寸是31cm×27cm,其中显示屏尺寸不到26cm×21cm(请注意:手工测量有误差)。分辨率是2200x1650。也就是200dpi多,和我的 Kindle Paperwhite 一代差不多的。

显示器下方是两个纸盒,里边是Y形线(照片中没有)、简易支架(那根根子)、螺丝和仅几百M的驱动U盘。

附件

Y形线是附加了USB电源线的HDMI线。信号走HDMI,电源走USB,所以需要额外占用一个USB口。当然接充电器上也是可以的,只是在电脑边接个充电器更不方便。

工作时,USB端口的电流为30~50mA左右,并不像说明书上说的需要2A那么多,挺省电的。对比之下,我的罗技鼠标接收器也需要15mA左右的电流呢。

线都接上之后,屏幕会闪几下,然后就可以用了。只是每隔几分钟会弹出提示信息,要求安装并运行所谓的「驱动」。下图是默认的 floyd 模式:

未使用软件,显示的登录界面

为了更多的模式,以及最重要的,别显示那个提示信息,需要安装并运行大上提供的软件。

软件是为 Ubuntu 提供的,但是 Arch 下也可以使用。下载并解包,将得到的三个文件「PaperLikeHD」、「ResChange」、「DS.ico」放到/usr/local/sbin/下,然后sudo PaperLikeHD 运行,会弹出一个包含一列按钮的窗口。不给 root 权限是不行的,会什么也不做就退出。

但是这样还不够。我使用的时候它总是报告找不到显示器。客服不肯告诉我它到底使用什么机制检测的,所以我 strace 又反汇编看了一下,最后发现它在找 /dev/i2c-* 设备文件,而我的系统上没有它们。看了一下一堆以 i2c 开头的内核模块,最后发现只要加载 i2c-dev 模块就好了。大上的软件需要一些时间来检测,等一会儿它就会在终端打开出「setting...50」这样的文字,这时就好了。

在台灯下使用火狐发推

过程中我还专门启动到 Ubuntu live 系统里测试来着。后来才知道原来是自己的手速太快,软件还没检测到,其实等等就好 (╯‵□′)╯︵┻━┻

Ubuntu live

从图片中可以看到,floyd 模式的显示很粗糙。这是因为它实际上只有二阶灰阶,通过不同密度的点来近似不同的灰度。虽然显示有很严重的颗粒感,但是它响应飞快,常规打字操作时几乎感觉不到延迟,很适合写作和阅读文本。而真正的二阶灰阶模式 A2,只有黑白两色,虽然依旧反应快速,但是因为非黑即白,只能用来读纯文本,任何阴影或者灰色的字都不好处理。

16灰阶的 A16 模式,显示清晰、层次丰富,但是响应速度非常慢。也不是非常啦,除了屏幕大小外,无论显示效果还是响应速度都跟我的 Kindle 一样的。而与 Kindle 不同的是,它会在更新时将更新区域变黑再显示,造成闪烁。在此模式下使用鼠标非常非常费力,任何非静止的元素(比如火狐载入中的动画、时钟、光标移动)都挺分散注意力的。

以上都是所谓的「可变分辨率模式」。另外有个「固定1100x825模式」,也就是使用一半的分辨率。至于另一半去了哪里,看看它所支持的模式就能猜到了:

  • A5,也就是五灰阶。这是用四个墨滴来显示一个像素,每个墨滴只有黑与白两种状态。
  • A61,所谓的61灰阶。也是用四个墨滴来显示一个像素。不过每个墨滴竭尽所能有16种状态,也不知道大上是怎么组合出这61灰阶的。

也不知道大上为什么把这两种墨滴用法叫作「可变分辨率模式」和「固定1100x825模式」。哪里可变了,又哪里固定了呢?这两种模式切换时,会调用「ResChange」程序,它会影响当前的显示器布局,需要重新使用 xrandr 进行设置。

以下是各种模式显示这个灰阶测试网页的照片:

A2 模式:

A2 模式

A16 模式:

A16 模式

floyd 模式:

floyd 模式

A5 模式:

A5 模式

A61 模式:

A61 模式

半分辨率下,清晰度做出了很大的牺牲。以下是 A16 和 A61 模式显示 PaperLikeHD 软件自身界面的效果对比:

A16 A61

在 A2、A5、floyd 模式下,可以调节对比度,也就是墨滴到底显示为黑还是为白的值,以在显示不同的页面时都能将文本与背景良好区分开。

和 Kindle Paperwhite 不同的是,它没有背光。在晚上的时候,屏幕偏暗,附加一个台灯光源比较好。不过它的屏幕也不像 Kindle 那样偏黄。

这篇文章最终定稿,就是在 PaperLikeHD 上完成的,使用的是 floyd 模式。我个人觉得半分辨率的几个模式很鸡肋。目前觉得,写作用 floyd 模式,文本阅读使用 A2 模式,网页阅读使用 A16 模式,这样最好了~

floyd 模式还有个问题:在显示某些图片时(比如我的 Awesome 桌面壁纸),那些墨滴颗粒会不断地抖动,就像风在吹沙粒一样……

最后,这里是大上的官网链接

by 依云 at February 12, 2018 02:38 PM

December 31, 2017

Phoenix Nemo

自托管的在线协作翻译平台 Weblate

起因:Transifex 这货闭源一段时间后突然开始抢钱了。

正巧一堆开源项目需要一个在线协作的翻译平台,于是测试了几个比较知名的开源程序。一遍折腾下来,发现 Weblate 可以最大化满足要求。顺便提一句,Weblate 也是有 hosted 付费服务的,但是在预算内的源字符串等限制依旧太多,所以选择使用他们的源码来搭建一套。

以及:我讨厌 Docker。

Weblate 文档 提供了非常全面的从起步到上手到各种高级用法的指南,因此这里不多赘述安装的过程。只记录少许踩过的坑。

这套程序看似简单,但实际上是基于 Django、使用了一大堆组件的复杂程序。如果想保持 system clean,最好(最快)的办法还是使用 Docker。

准备

小型实例只需要一台虚拟机即可。但是即便只托管几个项目,它依旧会吃掉 2 个 CPU 核心和 4GB 内存,和曾经开源版的 Transifex 有得一拼 大概也解释了为何这类服务都死贵

如果是托管在公网上的实例,则推荐使用 HTTPS。Weblate 的 Docker compose 提供了 HTTPS 支持,稍后会提到。

安装 Git, Docker 和 docker compose,在一些软件仓库中一般是 docker-cedocker-compose,其他软件均不需要手动安装。

搭建

首先克隆 docker compose 配置到本地

1
2
git clone https://github.com/WeblateOrg/docker.git weblate-docker
cd weblate-docker

为了直接开始使用 HTTPS,现在需要先建立域名解析记录,将要使用的域名(例如 weblate.example.com)指向服务器 IP。然后在该目录下创建配置文件 docker-compose-https.override.yml 内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: '2'
services:
weblate:
environment:
- WEBLATE_DEBUG=0
- WEBLATE_EMAIL_HOST=smtp.gmail.com
- WEBLATE_EMAIL_HOST_USER=noreply@weblate.org
- WEBLATE_EMAIL_HOST_PASSWORD=system.email.password.here
- WEBLATE_ALLOWED_HOSTS=weblate.example.com
- WEBLATE_SERVER_EMAIL=noreply@weblate.org
- DEFAULT_FROM_EMAIL=noreply@weblate.org
- WEBLATE_REGISTRATION_OPEN=0
- WEBLATE_TIME_ZONE=UTC
- WEBLATE_OFFLOAD_INDEXING=1
- WEBLATE_REQUIRE_LOGIN=1
- WEBLATE_ADMIN_NAME=Weblate Admin
- WEBLATE_ADMIN_EMAIL=admin@weblate.org
- WEBLATE_ADMIN_PASSWORD=your+initial+password
https-portal:

environment:
DOMAINS: 'weblate.example.com -> http://weblate'

这份配置文件指定了:

  • 关闭 Django DEBUG mode (即生产模式)
  • 系统外发邮件服务器 smtp.gmail.com
  • 系统外发邮件用户名 noreply@weblate.org
  • 系统外发邮件密码 system.email.password.here
  • 允许使用的域名 weblate.example.com,如果有多个域名,使用逗号隔开
  • 系统外发邮件地址 noreply@weblate.org
  • 关闭注册通道,用户必须管理员手动添加
  • 设置系统时间为 UTC
  • 打开后台索引,降低运行时的负载
  • 执行任何操作前要求登陆
  • 默认管理员名称是 Weblate Admin
  • 默认管理员邮箱地址是 admin@weblate.org
  • 默认管理员密码是 your+initial+password

然后在 https-portal 容器中指定了要使用 SSL 的域名 weblate.example.com 和后端指向的容器 http://weblate

在当前目录中执行

1
docker-compose -f docker-compose-https.yml -f docker-compose-https.override.yml up

会顺序拉取、启动 4 个 docker 容器,分别是:

  • https-portal
  • weblate
  • postgresql
  • memcached

第一次启动需要一些时间拉取镜像并导入初始数据。全部完成后,访问 weblate.example.com 应该可以看到一个 HTTPS 的 Weblate 实例运行,使用之前定义的默认管理员邮箱地址和密码即可登入。

此时转回终端,按一次 ^C 等待四个容器正确关闭,然后编辑 docker-compose-https.override.yml,删除以下配置

  • WEBLATE_ADMIN_NAME
  • WEBLATE_ADMIN_EMAIL
  • WEBLATE_ADMIN_PASSWORD

否则,如果更改了默认管理员的信息(如用户名等)下次启动会再次创建管理员帐号,并使用相同的邮箱,导致默认管理员无法使用邮箱登陆(报错返回 2 个用户信息)。解决办法是使用用户名…(摔

集成配置

再次运行 docker-compose -f docker-compose-https.yml -f docker-compose-https.override.yml up 后可以很快启动所有需要的程序。此时登入 Weblate 实例,指向 /admin/ssh/ 点击创建 SSH 密钥。

在对单一 repo 提交的情况下,此 SSH Key 可作为 GitHub deploy key,但是如果需要多个不同 repo 提交时,有两种方法:

  • 创建一个 GitHub 用户,然后将此 SSH Key 添加到此用户下,再给此用户所有必要的写权限
  • 使用 Access Token 作为 HTTPS 密码访问必要的 repo

浏览器指向 /admin/trans/project/ 新建一个 Project。这个 Project 不仅指一个项目,也可以作为一个 Organization 的存在,更精确的解释是一个软件集,例如 KDE 套件可以包含一大堆的组件。

Weblate Project

指向 /admin/trans/subproject/ 这里才是可以添加要翻译的项目的地方。如果对应的 repo 添加了公钥,这里可以直接使用 SSH 方式的 push URL。

Weblate Import strings

File mask 填写所有语言文件相对 repo root 的路径,使用 * 代替语言代号。如果是 Monolingual language file,例如 key 是 user.info.comment_posted 这样而非原本即可阅读的文本,则 Monolingual base language fileBase file for new translations 均为源语言文件相对 repo root 的路径,这样即可正确识别源语言的字符串不至于让别人拿着 comment_posted 这样的 key 来猜意思

Weblate Translation Interface

持续集成

提交翻译后,Weblate 会在后台完成索引并提交必要的更改。当然也会一不小心刷了别人的屏…

Weblate continous translation

在 repo 的 settings -> integration 中可以添加 Weblate 作为集成,每次有新的提交即可触发 Weblate 更新源语言文件。

结论

我很开心可以省下每年数千美元来用一个非常卡的在线协作翻译平台

当然,我依旧讨厌 Docker。

起因:Transifex 这货闭源一段时间后突然开始抢钱了。

正巧一堆开源项目需要一个在线协作的翻译平台,于是测试了几个比较知名的开源程序。一遍折腾下来,发现 Weblate 可以最大化满足要求。顺便提一句,Weblate 也是有 hosted 付费服务的,但是在预算内的源字符串等限制依旧太多,所以选择使用他们的源码来搭建一套。

以及:我讨厌 Docker。

December 31, 2017 03:59 AM

December 30, 2017

ヨイツの賢狼ホロ

啥??两年了???

其实还差几天😂

状况

其实咱四月才装上 Piwik ,于是下面的数据都是四月以后的了……
PV

嗯……每天大概有二三十个人来看咱的样子(

地图

嗯……

设备s

嗯……大概知道这个 vivo 是谁撑起来的了 😂

操作系统谱系

嗯……用 Android 和 GNU/Linux 的越来越多了嘛,不知道有没有一天能超过 Windows 用户)

os_and_browsers.png

嗯……和大局一样,Chrome依旧领跑。(怎么还有 QQ 浏览器和 MIUI ??😂

嗯…… 😂

活动

  • 今年写的文章只有去年的一半……看来摸了不少🐟啊😂
  • 搞了个 X230,自己刷上了 Coreboot 换上了新的无线网卡,装上了 Parabola( 似乎走上了一条不归路…… 😂)
  • 自己建的 Telegram 群全都解散啦😂看来咱就是没有管理的能力啊……
  • 突然从 FT 跳进了一个更大的坑,没错就是 NIMABE BEMANI 😂
  • 然后突然又和某个如梦方醒的学长一起学算法去了……
  • 给别人安利音乐游戏然后自己被虐成渣…… 😂
  • 剩下的全忘啦😂

计划

其实有没有计划好像都没啥区别😂
  • 想考虑放个 PayPal 捐款链接上去(就是不知道哪里获取……)
  • 或者像 uva 一样放个 coinhive js 上去(不过好像不少人讨厌的样子……)
  • 不知道该写些啥……

期望

不只要成为回忆而已~

(诶咱在说些啥)

by ホロ at December 30, 2017 04:00 PM

December 18, 2017

Phoenix Nemo

使用 Blender 渲染 Minecraft 3D 效果图

突发奇想渲染 Minecraft 3D 效果图,首先用 Chunky 尝试了一下发现效果虽好但:

  • 人物动作过于限制
  • 渲染太!慢!了!

然而并买不起 Cinema 4D,所以来尝试一下 Blender 啦~

准备工作

  • 比较好的显卡。我的家用游戏机是 NVIDIA GTX 1080Ti,Cycles Render 可以 offload 掉绝大部分 CPU 的压力。
  • 安装 Blender
  • 下载 jmc2obj
  • 下载 MCPrep
  • 准备地图、材质、玩家皮肤等资源。

导出地图文件到 obj

启动 jmc2obj,在最上方选择地图存档位置并单击 load。在 UI 里选择要导出的地图部分,点击 Export。在左侧的选项中依次:

  1. Map Scale = 1.0
  2. Center 选中,否则可能会出现在距离地图很远的地方
  3. Texture Export
    • Pre-scale textures - 如果是原版材质,建议设置为 4x。如果是高清材质,按需要选择即可。
    • 不勾选 Export alpha channel in separate file(s)
    • 不勾选 Export all textures in a single file
    • 选择从 Minecraft 安装里导出默认材质,或自行选择一个额外的材质包。
    • 然后选择材质的导出位置。建议在目标目录中新建一个 textures 目录,然后导出到此目录中。此目录里面会出现一个 tex 目录,包含所有的材质文件。

材质导出进度完成后,开始导出地图文件。在右侧的选项中依次:

  1. 取消所有的选项勾选
  2. 勾选 Create a separate object for each material
  3. 可选勾选:
    • Render Entities
    • Occulude different adjacent materials
    • Optimize mesh
    • Do not allow duplicate vertexes
  4. 其余选项均保持非勾选状态
  5. 点击 Export 导出到之前创建的 textures同级目录
  6. 可能会遇到 banner 找不到材质的问题,忽略继续。导出后还不能使用,需要一个简单而 ugly 的 hack - 再导出一遍覆盖之前的 obj 和 mtl 文件。

导出后的工作目录如下:

Blender Working Directory

.obj 文件是地图数据,.mtl 则是刚才导出材质的材质索引,指向 textures/tex 的相对路径,因此这些文件的相对位置不能改变。

安装 MCPrep

启动 Blender,在 File -> User Preferences -> Add-ons 里,选择 Install Add-on from File...

然后点击下载好的 MCPrep 的 .zip 文件即可。

导入 blender

启动 Blender,先删掉默认的 object 和 lamp,然后依次选择 File -> Import -> Wavefront (.obj)

选择刚才生成的 .obj 文件导入。

(大概会卡一会儿… 喝杯茶先)

导入完成后,首先需要设置材质。选中所有 object (快捷键 A,如果被反选则再摁一次即可),左侧标签页切换到 MCPrep,然后点击 Prep Materials。

Prep Materials

进入 Walk Navigation 模式(或者其他熟悉的移动模式),Add -> lamp -> Sun 然后将添加的 Sun 光源移动到地图的合适位置(比如一个边角)。

Walk Navigation

然后移动 Viewport 到合适的位置,将默认的 Camera 对齐到当前视角。快捷键 Ctrl+Alt+0

Align Camera to View

设置背景

在 Blender 的上方菜单栏中,有 Blender Render 和 Cycles Render 两个选项。

如果使用 Blender Render,则需要在 Sun 的属性里设置 Sky & Atmosphere 并设置 Ray Shadow 以使光源和阴影正确对应。

如果使用 Cycles Render,则在 World 属性中设置 background 为 Sky Texture。

Sky texture

当然也可以使用自己的天空图像或其他材质。

渲染

在渲染属性中设置使用 CPU 或者 GPU 渲染图像,并可以设置分辨率等。

Render Properties

修改好设置,在 Blender 上方菜单栏中点击 Render -> Render Image 即可开始渲染啦~

玩家和实体

我相信熟练使用 C4D/Maya/Blender 的玩家们不需要看这篇教程… 所以玩家实体的高级用法不多讲。

需要的材料是一份玩家或者其他实体的 Rig,在 PlanetMinecraft 或者 MinecraftForum 上有很多。贴上皮肤就可以用啦。

在 player rig 中调整好 pose,保存。在要渲染的世界工程里,选择 File -> Append,然后选择刚才的玩家 pose 的 blender 文件,进入后选择 Scene 并 append。

选择 Scene 的原因是有不少 rig 并不是一个单一 object,如果是单一 object,则可以直接导入 object。

在右上方的 object 列表中选择刚才 append 的 scene,定位玩家实体并全部选中 -> Ctrl+C 复制 -> 返回渲染世界 -> Ctrl+V 粘贴。

选择粘贴进来的玩家实体,然后调整到合适的位置。

成果

这大概是我第一次玩 blender。

总之以下是成果啦。

比如某玩家的 pose 效果图:

leeder's pose

比 chunky 的动作自然多了吧~

最后是完成的效果:

Project xport

Happy Rendering~

突发奇想渲染 Minecraft 3D 效果图,首先用 Chunky 尝试了一下发现效果虽好但:

  • 人物动作过于限制
  • 渲染太!慢!了!

然而并买不起 Cinema 4D,所以来尝试一下 Blender 啦~

December 18, 2017 06:33 AM

不作死就不会死系列,TFTP 修复变砖的 Nighthawk X6

由于之前买的 AC87U 经常被 roommate 抱怨掉线(风评表示这货 5G 有问题,然而我连着 5G 毛事儿没有,隔壁用 2.4G 却一直掉线)…

新购入的路由器是 Netgear Nighthawk X6 R8000。

由于之前的 Security Advisory,所以到手第一件事就是配上网络更新固件啦。更新挺慢的于是点点点完事儿撸猫去了。过了一会儿回来一看怎么还没网络?得,砖了…

讲道理,Netgear 也算大厂了,这种 online update 干了不知道多少回,第一次遇到这都能变砖的(扶额。

现象就是电源橙色灯亮后一会儿变成白色灯闪烁,且网络服务没有启动。尝试过 factory reset 无效,官方提供的 TFTP 强刷工具也无效(刷不进…

解决方案反而是意想不到的简单。总之大概记录下修复的过程。

  1. 官方网站下载适用的新版固件并解压,应该得到一个 .chk 文件
  2. 关闭路由器电源等待 10 秒,网线插 LAN 口开机。
  3. 检查是否获得了正确的 IP。如果没有,可能 DHCP 服务没起来。手动设置一个正确的 IP 吧。然后能 ping 通路由器 IP 即可。
  4. 电源灯开始闪烁的时候,执行命令 tftp -i [router ip] put [path/to/firmware.chk]。例如 tftp -i 192.168.1.1 put ./R8000-V1.0.3.36_1.1.25.chk
  5. 等一会儿路由器自动重启,搞定。

配置都没丢…然后我依旧没有搞定 OCN 要怎么连 IPv6… 说好的 IPv6 PPPoE 呢…

心累.png

由于之前买的 AC87U 经常被 roommate 抱怨掉线(风评表示这货 5G 有问题,然而我连着 5G 毛事儿没有,隔壁用 2.4G 却一直掉线)…

新购入的路由器是 Netgear Nighthawk X6 R8000。

由于之前的 Security Advisory,所以到手第一件事就是配上网络更新固件啦。更新挺慢的于是点点点完事儿撸猫去了。过了一会儿回来一看怎么还没网络?得,砖了…

December 18, 2017 06:26 AM

December 10, 2017

ヨイツの賢狼ホロ

X230 + Coreboot + me_cleaner + ...... = ?

为 ThinkPad X230 刷 Coreboot (

预备知识

  • coreboot,原名LinuxBIOS,是一个旨在取代大多数计算机中专有韧体(BIOS或UEFI)的软件项目, 它采用轻量级固件设计,只执行加载和运行现代32位或64位操作系统所需的最少量任务。
  • me_cleaner 是一个移除固件中 Intel Management Engine 中非关键部分的 Python 脚本, 可以和 coreboot 结合使用。
  • Intel Management Engine 是一個擁有從底層控制硬件能力的、獨立與處理器和操作系統的框架, 它對於操作系統也是隱形的。簡單點說就是擁有極高的權限。 其中 Intel Active Management Technology(英特爾主動管理技術) 是運行與 ME 框架上的應用的一個例子。 (因为权限大又没有审计又经常爆出漏洞偶尔被戏称为 Malicious Engine)

关于 Intel Malicious Management Engine 的问题 某只咸鱼 写的还算清楚,推荐去读一读。

其实就是想去掉原厂 BIOS 里的白名单而已……

开始前要准备些啥?

  • 一台 X230 (这不是废话嘛)
  • 知道这样做可能会把电脑搞砖的风险(
  • 一个编程器
  • 另一台电脑(用于编译 coreboot 和操作编程器)
  • 热风和焊接工具(用于取下 BIOS IC)
  • 或者 SOP-8 测试夹(如果对自己的焊接技术没信心的话,比如咱……

取得 coreboot 和 flashrom 的源代码和制作工具链

日常 git 两连:

git clone https://review.coreboot.org/coreboot.git

git clone https://review.coreboot.org/flashrom.git

安装制作工具链需要的依赖(如果是 Arch 的话,其他的系统就只好自己发挥了 😂)

pacman -S base-devel gcc-ada nasm

从 AUR 装 uefitool-git(后面提取 VGA BIOS 需要)

然后在 coreboot 的目录下制作工具链:

make crossgcc

其它的选项和用法去看看 make help 咯 😂

备份原有固件和取出其中的 Blob

拆开电脑的掌托,揭开防静电贴纸就能看到两块 BIOS 芯片啦……

两块 BIOS 芯片的位置

然后拿出编程器在另一台电脑上开始 Dump 固件吧,例如这样:

flashrom 命令依编程器而异,不要照抄 😂

flashrom -p linux_spi:dev=/dev/spidev0.0 -r top.rom -c MX25L3206E/MX25L3208E

flashrom -p linux_spi:dev=/dev/spidev0.0 -r bottom.rom -c MX25L6406E/

然后接起来:

cat bottom.rom top.rom > complete.rom

最好多做几次然后验证一下散列值(为了确保备份的固件不是坏的?)

使固件可写(ifdtool 在 coreboot/utils 里)

$ ~/repo/coreboot/util/ifdtool/ifdtool -u complete.rom
File complete.rom is 12582912 bytes
Writing new image to complete.rom.new

用 me_cleaner 清除非必需 ME 固件(也在 coreboot/utils 里)

$ ~/repo/coreboot/util/me_cleaner/me_cleaner.py complete.rom.new
Full image detected
The ME/TXE region goes from 0x3000 to 0x500000
Found FPT header at 0x3010
Found 23 partition(s)
Found FTPR header: FTPR partition spans from 0x183000 to 0x24d000
ME/TXE firmware version 8.1.30.1350
Removing extra partitions...
Removing extra partition entries in FPT...
Removing EFFS presence flag...
Correcting checksum (0x7b)...
Reading FTPR modules list...
UPDATE           (LZMA   , 0x1cf4f2 - 0x1cf6b0): removed
ROMP             (Huffman, fragmented data    ): NOT removed, essential
BUP              (Huffman, fragmented data    ): NOT removed, essential
KERNEL           (Huffman, fragmented data    ): removed
POLICY           (Huffman, fragmented data    ): removed
HOSTCOMM         (LZMA   , 0x1cf6b0 - 0x1d648b): removed
RSA              (LZMA   , 0x1d648b - 0x1db6e0): removed
CLS              (LZMA   , 0x1db6e0 - 0x1e0e71): removed
TDT              (LZMA   , 0x1e0e71 - 0x1e7556): removed
FTCS             (Huffman, fragmented data    ): removed
ClsPriv          (LZMA   , 0x1e7556 - 0x1e7937): removed
SESSMGR          (LZMA   , 0x1e7937 - 0x1f6240): removed
The ME minimum size should be 1667072 bytes (0x197000 bytes)
The ME region can be reduced up to:
00003000:00199fff me
Checking the FTPR RSA signature... VALID
Done! Good luck!

用 ich_descriptors_tool 取出 blob(这个在 flashrom/util 里):

$ ~/repo/flashrom/util/ich_descriptors_tool/ich_descriptors_tool -f complete.rom.new -d
The flash image has a size of 12582912 [0xc00000] bytes.
Assuming chipset '6 series Cougar Point'.
=== Content Section ===
FLVALSIG 0x0ff0a55a
FLMAP0   0x03040103
FLMAP1   0x12100206
FLMAP2   0x00210120

--- Details ---
NR          (Number of Regions):                     4
FRBA        (Flash Region Base Address):         0x040
NC          (Number of Components):                  2
FCBA        (Flash Component Base Address):      0x030
ISL         (ICH/PCH Strap Length):                 18
FISBA/FPSBA (Flash ICH/PCH Strap Base Address):  0x100
NM          (Number of Masters):                     3
FMBA        (Flash Master Base Address):         0x060
MSL/PSL     (MCH/PROC Strap Length):                 1
FMSBA       (Flash MCH/PROC Strap Base Address): 0x200

=== Component Section ===
FLCOMP   0x4990001c
FLILL    0x00000000

--- Details ---
Component 1 density:            8 MB
Component 2 density:            4 MB
Read Clock Frequency:           20 MHz
Read ID and Status Clock Freq.: 33 MHz
Write and Erase Clock Freq.:    33 MHz
Fast Read is supported.
Fast Read Clock Frequency:      50 MHz
No forbidden opcodes.

=== Region Section ===
FLREG0   0x00000000
FLREG1   0x0bff0500
FLREG2   0x04ff0003
FLREG3   0x00020001

--- Details ---
Region 0 (Descr. ) 0x00000000 - 0x00000fff
Region 1 (BIOS   ) 0x00500000 - 0x00bfffff
Region 2 (ME     ) 0x00003000 - 0x004fffff
Region 3 (GbE    ) 0x00001000 - 0x00002fff

=== Master Section ===
FLMSTR1  0xffff0000
FLMSTR2  0xffff0000
FLMSTR3  0x08080118

--- Details ---
    Descr. BIOS ME GbE Platf.
BIOS    rw    rw  rw  rw   rw
ME      rw    rw  rw  rw   rw
GbE                   rw

=== Upper Map Section ===
FLUMAP1  0x000018df

--- Details ---
VTL (length in DWORDS) = 24
VTBA (base address)    = 0x000df0

VSCC Table: 12 entries
JID0  = 0x001620c2
VSCC0 = 0x20052005
    Manufacturer ID 0xc2, Device ID 0x2016
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID1  = 0x001720c2
VSCC1 = 0x20052005
    Manufacturer ID 0xc2, Device ID 0x2017
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID2  = 0x001820c2
VSCC2 = 0x20052005
    Manufacturer ID 0xc2, Device ID 0x2018
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID3  = 0x001640ef
VSCC3 = 0x20052005
    Manufacturer ID 0xef, Device ID 0x4016
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID4  = 0x001740ef
VSCC4 = 0x20052005
    Manufacturer ID 0xef, Device ID 0x4017
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID5  = 0x001840ef
VSCC5 = 0x20052005
    Manufacturer ID 0xef, Device ID 0x4018
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID6  = 0x0016ba20
VSCC6 = 0x20052005
    Manufacturer ID 0x20, Device ID 0xba16
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID7  = 0x0017ba20
VSCC7 = 0x20052005
    Manufacturer ID 0x20, Device ID 0xba17
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID8  = 0x0018ba20
VSCC8 = 0x20052005
    Manufacturer ID 0x20, Device ID 0xba18
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID9  = 0x0016701c
VSCC9 = 0x20052005
    Manufacturer ID 0x1c, Device ID 0x7016
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID10  = 0x0017701c
VSCC10 = 0x20052005
    Manufacturer ID 0x1c, Device ID 0x7017
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20
JID11  = 0x0018701c
VSCC11 = 0x20052005
    Manufacturer ID 0x1c, Device ID 0x7018
    BES=0x1, WG=1, WSR=0, WEWS=0, EO=0x20

=== Softstraps ===
--- North/MCH/PROC (1 entries) ---
STRP0  = 0x00000000

--- South/ICH/PCH (18 entries) ---
STRP0  = 0x4810d782
STRP1  = 0x0000010f
STRP2  = 0x00000000
STRP3  = 0x00000000
STRP4  = 0x00c8e102
STRP5  = 0x00000000
STRP6  = 0x00000000
STRP7  = 0x00000000
STRP8  = 0x00000000
STRP9  = 0x30000b80
STRP10 = 0x00410044
STRP11 = 0x99000097
STRP12 = 0x00000000
STRP13 = 0x00000000
STRP14 = 0x00000000
STRP15 = 0x0000437e
STRP16 = 0x00000000
STRP17 = 0x00000002

Detailed South/ICH/PCH information is probably not reliable, printing anyway.
--- PCH ---
Chipset configuration Softstrap 1: 1
Intel ME SMBus Select is enabled.
SMLink0 segment is enabled.
SMLink1 segment is enabled.
SMLink1 Frequency: 100 kHz
Intel ME SMBus Frequency: 100 kHz
SMLink0 Frequency: reserved
GPIO12 is used as LAN_PHY_PWR_CTRL.
LinkSec is disabled.
DMI RequesterID Checks are disabled.
BIOS Boot-Block size (BBBS): 256 kB.
Chipset configuration Softstrap 3: 0xf
Chipset configuration Softstrap 2: 0x1
ME SMBus ASD address is disabled.
ME SMBus Controller ASD Target address: 0x00
ME SMBus MCTP Address is disabled.
ME SMBus MCTP target address: 0x00
ME SMBus I2C address is disabled.
ME SMBus I2C target address: 0x00
Intel PHY is connected.
GbE MAC SMBus address is enabled.
GbE MAC SMBus address: 0x70
GbE PHY SMBus address: 0x64
Intel ME SMBus Subsystem Vendor ID: 0x0000
Intel ME SMBus Subsystem Device ID: 0x0000
PCI Express Port Configuration Strap 1: 4x1 Ports 1-4 (x1)
PCI Express Port Configuration Strap 2: 4x1 Ports 5-8 (x1)
PCIe Lane Reversal 1: PCIe Lanes 0-3 are not reserved.
PCIe Lane Reversal 2: PCIe Lanes 4-7 are not reserved.
DMI Lane Reversal: DMI Lanes 0-3 are not reserved.
ME Debug status writes over SMBUS are disabled.
ME Debug SMBus Emergency Mode address: 0x00 (raw)
Default PHY PCIe Port is 4.
Integrated MAC/PHY communication over PCIe is enabled.
PCIe ports Subtractive Decode Agent is disabled.
GPIO74 is used as PCHHOT#.
Management Engine will boot from ROM, then flash.
ME Debug SMBus Emergency Mode is disabled.
ME Debug SMBus Emergency Mode Address: 0x00
Integrated Clocking Configuration used: 0
PCH Signal CL_RST1# does not assert when Intel ME performs a reset.
ICC Profile is selected by BIOS.
Deep SX is supported on the platform.
ME Debug LAN Emergency Mode is enabled.
SMLink1 GP Address is enabled.
SMLink1 controller General Purpose Target address: 0x4b
SMLink1 I2C Target address is enabled.
SMLink1 I2C Target address: 0x4c
Chipset configuration Softstrap 6: 0
Integrated wired LAN is disabled.
Chipset configuration Softstrap 5: 0
SMLink1 provides temperature from the CPU, PCH and DIMMs.
GPIO29 is used as SLP_LAN#.
Integrated Clock: Full Integrated Clock Mode

The MAC address might be at offset 0x1000: 3c:97:0e:ac:31:c1
=== Dumping region files ===
Dumping 4096 bytes of the Descriptor region from 0x00000000-0x00000fff to complete.rom.new.Descriptor.bin... done.
Dumping 7340032 bytes of the BIOS region from 0x00500000-0x00bfffff to complete.rom.new.BIOS.bin... done.
Dumping 5230592 bytes of the ME region from 0x00003000-0x004fffff to complete.rom.new.ME.bin... done.
Dumping 8192 bytes of the GbE region from 0x00001000-0x00002fff to complete.rom.new.GbE.bin... done.

用 UEFITool 打开 complete.rom.new.BIOS.bin,搜索 “VGA compatible”(记得取消选择 Unicode), 大概会找到一 64 KB 的块,用 extract body 解压它,这就是汝的 VGA BIOS 啦 ~

然后把这些文件放在一起:

# 在汝的 coreboot 目录下新建一个存放 blob 的文件夹
mkdir -p ~/repo/coreboot/3rdparty/blobs/mainboard/lenovo/x230/
# 然后把刚取出的文件赋值过去
cp complete.rom.new.Descriptor.bin ~/repo/coreboot/3rdparty/blobs/mainboard/lenovo/x230/descriptor.bin
cp complete.rom.new.ME.bin ~/repo/coreboot/3rdparty/blobs/mainboard/lenovo/x230/me.bin
cp complete.rom.new.GBE.bin ~/repo/coreboot/3rdparty/blobs/mainboard/lenovo/x230/gbe.bin
cp vbios.bin ~/repo/coreboot/pci8086,0166.rom

make menuconfig

在 coreboot 目录下运行 make menuconfig :

make_menuconfig

是不是和编译内核时很像? 😂 然后在 General Setup 里选中 Allow use of binary-only repoistory:

Allow use of binary-only repoistory

然后在 Board 一节里选择 Lenovo X230:

Board

在 Chipset 中选择 Add Intel descriptor.bin file, Add Intel ME/TXE firmware 和 Add gigabit ethernet firmware,如果汝不是按刚才咱那样复制的话,记得改一下每一项 下面的文件位置选项:

Chipset

在 Display 一节中选择 Add a VGA BIOS image,如果汝不是按刚才咱那样复制的话, 记得改一下每一项下面的文件位置选项:

vgabios

在 Generic Drivers 一节里选中 PS/2 keyboard init (有需要的话可以顺便启用一下 TPM 支持):

device

在 Payload 一节选择一个合适的 Payload (大概是启动管理器)?

  • 默认的 SeaBIOS 是一个自由的 BIOS 引导实现。
  • 也可以让 coreboot 引导 GRUB2 或者直接启动 Linux 内核(使用 grub 或者 linux payload, 前提是 BIOS 芯片里还有地方……)
  • Tianocore 是一个开放源代码的 UEFI 实现。
payload

于是咱就用了 Tianocore ……

tianocore

最后保存配置文件:

save

然后运行 make 编译汝的 ROM 吧(最后会在 build/coreboot.rom 里)。

刷入……

首先把 ROM 拆开成两个芯片大小,用 dd 就可以搞定:

dd of=top.rom bs=1M if=coreboot.rom skip=8

dd of=bottom.rom bs=1M if=coreboot.rom count=8

然后再接上编程器,首先刷入上面的芯片,看看可不可以正常开机 😂

flashrom -p linux_spi:dev=/dev/spidev0.0 -w top.rom -c MX25L3206E/MX25L3208E

如果可以的话,再刷入下面一块:

flashrom -p linux_spi:dev=/dev/spidev0.0 -w bottom.rom -c MX25L6406E/MX25L6408E

如果可以正常启动的话,Cheers!汝已经成功装上 coreboot 啦(白名单和 ME 的大部分功能也没啦……)

😂

by ホロ at December 10, 2017 04:00 PM