Arch Linux 星球

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 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 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

June 04, 2018

ヨイツの賢狼ホロ

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

下载工具:

# sdk-assistant create $VENDOR-$DEVICE-xxxx
sdk-assistant create moto-shamu-latest http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Tooling-i486.tar.bz2
# 这一步和汝手机 CPU 的架构相关
sdk-assistant create moto-shamu-armv7hl http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Target-armv7hl.tar.bz2
sdk-assistant create moto-shamu-i486 http://releases.sailfishos.org/sdk/latest/Jolla-latest-Sailfish_SDK_Target-i486.tar.bz2

# 更新 chroot
sudo ssu re 2.1.4.13
sudo zypper ref
sudo zypper dup

# 安装工具
sudo zypper in android-tools createrepo zip

未完待续 😂

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

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

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 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

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

百合仙子

在 Linux 下设置录音笔的时间

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

咱买了一个录音笔,效果比使用笔记本话筒录音好多了还省电。当然啦,我也曾试过使用手机录音,结果是,没能录多久就中断了(Android 就是这么不靠谱)。

我的录音需要记录较为准确的时间信息。录音笔怎么知道现在是什么时间呢?还好它没有跟风,用不着联网!

它带了一个小程序,叫「录音笔专用时间同步工具」(英文叫「SetUDiskTime」,可以搜到的)。是一个 EXE 文件,以及一个 DLL 文件。功能很棒,没有广告,没有推荐,也不需要注册什么乱七八糟的账户,甚至都不需要打开浏览器访问人家官网。就弹一个框,显示当前时间,确定一下就设置好时间了。这年头,这么单纯的 Windows 软件还真是难得呢。

然而,它不支持我用的 Linux 啊。虽然我努力地保证这录音笔一直有电,但是时间还是丢失了几次,它的FAT文件系统也脏了几次。每次我都得开 WinXP 虚拟机来设置时间,好麻烦。

Wine 是不行的,硬件相关的东西基本上没戏。拿 Procmon 跟踪了一下,也没什么复杂的操作,主要部分就几个 DeviceIoControl 调用,但是看不到调用参数。试了试 IDA,基本看不懂……不过倒是能知道,它通过 IOCTL_SCSI_PASSTHROUGH 直接给设备发送了 SCSI 命令。

既然跟踪不到,试试抓 USB 的包好了。本来想用 Wireshark 的,但是 WinXP 版的 Wireshark 看来不支持。又尝试了设备分配给 VBox 然后在 Linux 上抓包,结果 permission denied……我是 root 啊都被 deny 了……

那么,还是在 Windows 上抓包吧。有一个软件叫 USBPcap,下载安装最新版,结果遇到 bug。那试试旧版本吧。官网没给出旧版本的下载地址,不过看到下载链接带上了版本号,这就好办了。去 commit log 里找到旧的版本号替换进去,https://dl.bintray.com/desowin/USBPcap/USBPcapSetup-1.0.0.7.exe,就好了~

抓好包,取到 Linux 下扔给 Wireshark 解读。挺小的呢,不到50个包,大部分还都是重复的。很快就定位到关键位置了:

关键 SCSI 命令

一个 0xcc 命令发过去,设备回复「ACTIONSUSBD」,大概是让设备做好准备。然后一个 0xb0 命令,带上7字节数据发过去,时间就设置好了。简单明了,不像那些小米空气净化器之类的所谓「物联网」,通讯加密起来不让人好好使用。

那么,这7字节是怎么传递时间数据的呢?我首先检查了UNIX时间戳,对不上。后来发送这个字串看上去挺像YYYYMMDDHHMMSS格式的,只是明显不是当时的时间。啊,它是十六进制的嘛!心算了几个,符合!再拿出我的 Python 牌计算器,确定年份是小端序的16位整数。

好了,协议细节都弄清楚了,接下来是实现。我原以为我得写个 C 程序,调几个 ioctl 的,后来网友说有个 sg3_utils 包。甚好,直接拿来用 Python 调,省得研究那几个 ioctl 要怎么写。

#!/usr/bin/env python3

import os
import sys
import struct
import subprocess
import datetime

def set_time(dev):
  cmd = ['sg_raw', '-s', '7', dev, 'b0', '00', '00', '00', '00', '00',
         '00', '07', '00', '00', '00', '00']
  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
  dt = datetime.datetime.now()
  data = struct.pack('<HBBBBB', dt.year, dt.month, dt.day,
                     dt.hour, dt.minute, dt.second)
  _, stderr = p.communicate(data)
  ret = p.wait()
  if ret != 0:
    raise subprocess.CalledProcessError(ret, cmd, stderr=stderr)

def actionsusbd(dev):
  cmd = ['sg_raw', '-r', '11', dev, 'cc', '00', '00', '00', '00', '00',
         '00', '0b', '00', '00', '00', '00']
  subprocess.run(cmd, check=True, stderr=subprocess.PIPE)

def main():
  if len(sys.argv) != 2:
    sys.exit('usage: setudisktime DEV')

  dev = sys.argv[1]
  if not os.access(dev, os.R_OK | os.W_OK):
    sys.exit(f'insufficient permission for {dev}')

  actionsusbd(dev)
  set_time(dev)

if __name__ == '__main__':
  main()

by 依云 at December 10, 2017 07:44 AM

November 21, 2017

百合仙子

我正在使用的火狐扩展

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

早就说了要介绍我在用的火狐扩展,现在终于来啦。

桌面版

Android 版

Android 版上使用的扩展比较少,大部分是桌面上用的扩展同时支持 Android,所以同步过来了。只有一个例外:Text Reflow WE

这个扩展是我移植到 Web Extensions 的。在点击内容时限制文本宽度,从而限制需要不断地横向滚动。原本是 Opera Mobile 的特色功能,后来我换到火狐时找了个替代品,结果它没更新了,不支持57+,我就移植了一下。代码十分简单,本来是一个 user script 就可以搞定的内容,但是 Android 上好像没 user script 扩展……


2017年11月25日更新:

装回了 cliget,一个为下载项生成 wget / curl / aria2 调用命令的工具。虽然因为没有 XUL API 可以修改保存对话框了,但是作者找到了一种不错的办法,把候选下载项保存到一个列表里方便取用。会有误判,忽略就好。这个列表是下载对话框弹出的时候就会更新,所以挺适合下载大的「百k盘」文件,下载链接超时失效之后可以获取一个新的让 wget 接着下。

暂时把 uBlock Origin 换回了 Adblock Plus。界面比前者好看许多,可惜还是没有规则使用计数和最后使用时间记录,不方便清理失效的规则。

启用了 ScrollAnywhere,因为妨碍 Foxy Gestures 使用右键的 bug 已经解决了。我使用的是 Nightly 版本所以很快就用上了,Linux 发行版如 Arch 也有做 backport。

考虑挽回 GreaseMonkey,不过发现有一个 bug 导致它不能在特定网站上触发安装操作。

by 依云 at November 21, 2017 08:43 AM

November 08, 2017

中文社区新闻

停止支持 i686 架构

随着9个月的逐步抛弃阶段结束,今日起我们正式停止对 i686 架构的支持。我们将于11月底从软件源服务器删除 i686 包,随后也将删除归档服务器上的包。

对于无法将硬件升级至 x86_64 架构的用户,我们建议他们使用 Arch Linux 32 作为替代,这是由社区维护的下游发行版。具体的迁移步骤请参考他们的网站。

by farseerfc at November 08, 2017 02:17 PM

Lainme

解决ArchLinux以DHCP联网失败的问题

昨天在一台新的计算机上安装Arch时发现没有办法联网。这台电脑是以有线方式连接的,网络有MAC地址控制。安装之前确定了Windows和Ubuntu的LiveCD都能正常联网。

Arch的LiveCD里是通过dhcpcd来进行自动IP获取的,它是base里的软件,所以也会在安装完后的系统里。发现无法联网后,我查看了dhcpcd的启动状态

systemctl status dhcpcd@[interface].service

发现它在soliciting a DHCP lease这里就显示timeout了。经过搜索后了解到dhcpcd是默认通过DHCP Unique Identifier (DUID)来进行标识的,而我所在的网络是带MAC控制的,很可能不认DUID。这个问题在Arch的wiki中也进行了说明,需要更改dhcpcd的配置文件/etc/dhcpcd.conf,将duid改为clientid,然后重启服务

systemctl restart dhcpcd@[interface].service

然而安装完毕后,dhcpcd经过上述修改后是可以的,但是Network Manager依然不工作。到最后发现是Network Manager已经不支持dhcpcd了,而dhclient默认没有安装 (why!?)。所以只需要安装上dhclient,

sudo pacman -S dhclient

然后重启Network Manager

sudo systemctl restart NetworkManager

[UPDATE]

今天Arch更新后又连不上网了…查了后发现竟然默认使用NetworkManager内置的dhcp了,但这个内置的显然不行。只好更改 /etc/NetworkManager/NetworkManager.conf 这个配置文件,增加

[main]
dhcp = dhclient

的选项,然后再重启服务

sudo systemctl restart NetworkManager

我想说你内置的功能不强就不要默认么,没有金刚钻揽什么瓷器活啊!

by lainme (lainme@undisclosed.example.com) at November 08, 2017 04:30 AM

November 06, 2017

百合仙子

使用 Python 读取火狐的 cookies

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

这事本来是有个 browsercookie 库可以做的,但是初看它就有不少问题:

  1. 不能指定要使用的火狐 profile(后来发现可以指定数据库文件)。
  2. 找不到 sessionstore.js 文件时会向标准输出打印一行信息。对于 cron 脚本,这种行为是非常非常讨厌的。

我在尝试解决这些问题时,又发现了额外的问题:它每次都要把所有的 cookie 全部读取。再加上不必要地导入 keyring、Crypto 等库,让我想放弃了。

于是我考虑自己实现一个 cookiejar。但发现它有如下问题:

  • 公开接口和实现细节没有清晰地分离
  • 没有提供存储和读取 cookie 的抽象,而是存在一个字典里

这样扩展起来就十分令人不爽了,也不知道能正常工作多久。

也罢,cookiejar 是个十分复杂的东西,我不如实现一个获取匹配的 cookie 的独立功能,然后通过各种姿势传给 HTTP 客户端库好了。

火狐的 cookie 数据库文件「cookies.sqlite」里就一个「moz_cookies」表,其结构也挺简单的。但是,怎么做 cookie 的匹配呢?既然决定放弃 Python 自带的 cookiejar,那就不看它,直接看火狐的源码好了。

于是去 DXR 上搜索火狐的源码。没费多少力气就找到了相关的部分,然后跟着代码就能知道是怎么匹配的了:

  1. 通过祼域名查得候选 cookie
  2. 根据域名、路径和 secure 等属性来过滤 cookie
  3. 就这样,没有第三步了

祼域名使用 tldextract 库来做,其它属性的匹配算法直接看火狐的代码。虽然是不熟悉的 C++ 代码,但是写得很棒,很容易理解。

把自己需要的部分写成 Python,得一新模块——firefoxcookies。就一个方法,返回一个 cookie 的字典,用起来也很方便。比如在我的 requestsutils.RequestsBase 中,这么干就可以了:

class FireRequests(RequestsBase):
  def initialize(self):
    self._fc = FirefoxCookies(os.path.expanduser(
      '~/.mozilla/firefox/nightly/cookies.sqlite'))

  def request(self, url, method=None, *args, **kwargs):
    if self.baseurl:
      url = urljoin(self.baseurl, url)

    cookies = self._fc.get_cookies(url)

    return super().request(url, method=method, cookies=cookies)

就这样就满足我的需求了。以后再有别的需求,再慢慢扩展。

by 依云 at November 06, 2017 10:55 AM

November 01, 2017

ヨイツの賢狼ホロ

如何在 IRC 上请求支援

来自 https://workaround.org/getting-help-on-irc/

IRC 是一种即时聊天工具,在不同的频道上有不同的话题进行讨论。

现在最大的 IRC 网络 irc.freenode.net 上有很多讨论操作系统,应用程序和开放源代码项目的 IRC 频道。 同时也成了获得帮助和支持的好地方。IRC 历史悠久而且还在持续进化,虽然有点像一群疯子的聚居地(雾)。

这篇文章希望能帮助汝理解 IRC 社群的工作原理并赐予汝充分利用它的力量的能力呗~

#1 – 别问“能不能问”问题

不要以“有人在吗?”,“我能在这里提问嘛?”一类的问题开始。 虽然在真实社交中这会让汝觉得彬彬有礼,但是 IRC 上没这个必要。 直接提出汝的问题就好(虽然很可能需要组织一下问题)。

#2 – 准确第一

“我的打印机坏了”,“我的邮件服务器发不出邮件”这样的问题完全会让人抓狂好不好~ 这样其他人怎么帮忙嘛,所以最好保证汝的话包含这几点:

  • 汝要干啥?(咱想用 POP3 协议接收邮件)
  • 哪里不对劲?(但是咱收不到)
  • 汝在用啥?(咱在 KDE 3.4 上用 kmail 1.8)(原文如此啦)
  • 汝已经试着做些啥了嘛?(咱确定咱的设置没有问题)
  • 有没有日志或者错误消息?(错误消息提示咱 “SSL negotiation failed.”)
  • 它曾经工作过嘛?汝改了哪些?(咱换了邮件提供商,前一家没问题)

包含的信息越多貌似汝的问题更容易被人关注到呗~

#3 – 讲出汝在做些啥

旁人只知道汝告诉他们的一切(就是汝不说别人不知道啦)。 “我改了某些地方然后遇到了别的问题”一类的话最容易让人放弃治疗啦 😂。 取得帮助的前提是合作啦~

#4 – 去读 /topic

汝的 IRC 客户端应该会在汝进入频道时显示频道的主题(topic)呗, 一个频道的主题通常包含大量信息(最常见的是 FAQ 和文档的链接)。 汝不想因为问 topic 或者 FAQ 中已经有答案的问题而被当作笨蛋,是呗~ 所以要是 topic 里有 FAQ 的地址的话,先花些时间去那里找找咯。

#5 – 别依赖谁

别指望谁会一直陪着汝在线直到汝的问题解决。IRC 是个无状态介质,于是谁啥时候都能下线。 那些留着的人只是他/她们乐意这样做而已啦,而且毕竟汝也没因为这个付费,是呗。

#6 – 别胡乱 /msg 其他人

汝可以在频道中公开的交谈,或者用 /msg 一类的命令和谁私聊。 不过随便和人搭讪容易被当作无礼,而且别只和一个人说话, 说不定其他人有兴趣或者有能力给汝一个解决方案呐~

#7 – 无意的无礼

有时汝大概会收到这样的回复:“重新安装”,“重启”,“去读 /usr/share/doc/mysoftware/README 里的文档” 一类的回复。虽然它短小精悍(也许带点火药味),不过别觉得被冒犯啦,也许给汝回答的人已经回答这样的问题 好多遍啦(庆幸他/她/它还没觉得厌烦),这样做最节省双方的时间而已。

#8 – 别重复自己

别一次次的问,要是一直没人说话的话,多半是时间不对(比如在吃饭或者睡觉), 大部分人在回来时看到聊天记录(如果他们的客户端支持的话),就能看到汝的问题了呗~

#9 – 讲英语(大多数情况下)

大多数频道只讲英语(不过也有例外,例如 #debian-fr 就讲法语,#archlinux-cn 讲中文,等等), 所以汝最好也讲英语,不要太担心自己英语水平不足啦(说不定不少人和汝差不多)。 当然,要是在讲非母语的频道里见到老乡(雾),别马上换成汝的母语继续说 (这样大多数频道里的人就看不懂啦)。

#10 – 别偷懒,去读文档

要是有人让汝去读文档的话,别像个大笨驴一样说些既然他知道不如直接告诉我怎么解决 一类的话,不然汝就等着被所有人忽略吧……

不过要是汝在看他提供的文档却一头雾水不知如何下手时说不定可以问问,例如 “咱看了汝给咱的文档的第三章第一节,现在咱明白虚拟域名如何工作了,不过咱怎么把 虚拟域名和非虚拟域名组合到一起使用咧?”

#11 – 留下来给人机会回答

就算汝十万火急,也留些时间等等好啦~ 有些时候汝可能马上就有答案了,然而不一定汝每次 都是幸运儿,多等一会儿说不定会有懂得如何解决汝问题的人出现咯:

10:07 Foo joins

10:07 Foo> How can I install the shripdibble module?

10:09 Foo quit

要是汝像这样问完就跑的话,后面有明白 shripdibble 的人来都不知道。

#12 – 如果可能的话,留下来

用 IRC 就像索取和给予的过程。一开始汝大概是问题比答案多,于是就提问然后因为有人注意到 或者回答了而心怀感激。而帮汝的人甚至都没期望收获一个“谢谢”。

如果汝可以的话,最好留下来帮助其他人。从这个过程中,汝大概可以得到更多知识, 或许也能了解到怎么和人聊天(就算是虚拟的也没有关系)。

#13 – 回答汝所问的问题?

IRC 有些不太好跟踪所有的对话。但是有时想帮忙的人可能会向汝提出一些问题。 别忽略它们(除非汝想被人忽略),而且最好答其所问。汝自己保持对自己的问题关注的话, 说不定能更快的解决。

#14 – 说出汝的解决办法

要是汝后来自己解决了,别简单的说一句自己已经解决了就跑。 告诉大家汝是怎么解决的问题,这样都能从中学到些啥。 汝是不会被当作大笨驴的啦,相信咱~

#15 – 不要盲从

小心甄别汝收到的回复,其间可能夹杂着瞎猜(或者有根据的猜测), 或者有些人为了好玩讲的玩笑话(比如一个格式化硬盘的命令)等等 😂。 如果不确定的话,就去问问别人(不排除汝太大笨驴以至于整个频道在线的人都在调戏汝的可能)😂

#16 – 别向频道发洪水,用剪贴板服务

有时汝会考虑发送一行以上的消息(可能是配置文件或者日志), 不过别直接把文字复制到输入框里。记得使用一个 Pastebin 服务 (一种存放文字然后供其他人以 URL 形式访问的网站),当然也别直接贴个链接就算了 (说点啥啊 😂),例如“咱装了 foo 以后 bar 就挂了,这是 bar 日志的输出 http://…”

参阅

提问的智慧 (虽然咱知道汝可能已经看腻了这个了)

by ホロ at November 01, 2017 04:00 PM

October 27, 2017

ヨイツの賢狼ホロ

LEDE + iptables6 + Shadowsocks = ?

利用支持 LEDE 的路由器 + shadowsocks + iptables6 组建 nat6 + 透明代理 AP 接着搞大新闻…… 😂

所以 LEDE 又是个啥?😂

Linux嵌入式开发环境项目(Linux Embedded Development Environment,LEDE), 是OpenWRT专案的一个分支专案,继承原来OpenWRT的开发目标。 OpenWRT开发者社区内因各种决定迟迟未能在达成一致意见,导致长时间没有大幅更新, 这使得一群OpenWRT核心贡献者感到不满,而他们于2016年5月,决定另立新专案。 这个专案在一年后才正式定名为LEDE。LEDE的内核原始码基本继承自原OpenWRT, 但相应的开发者社区采用新的讨论规定和决议流程。 2017年6月,LEDE社区和OpwnWRT社区均通过决定,将原OpenWRT专案合并至LEDE专案之中。

https://lede-project.org

最后的效果大概是?

  • 连接到路由器的设备可以访问纯 IPv6 网站(通过 nat6)
  • 通过透明代理访问被墙网站

下载,安装和设置 LEDE

如果自己编译的话,可以考虑添加这些依赖(然而咱忘了都在哪了 😂):

libgcc  # LEDE:target/cpu型号/package目录下
libpthread # 同上
libpcre # package
libev # package
libsodium # package 最新版本需要libsodium版本 >= 1.0.12
libmbedtls # package
libcares
ip # base
ipset
iptables-mod-tproxy # 代理 UDP 流量需要
# nat6 需要这两个
ip6tables
kmod-ipt-nat6

刷入以后 ssh 上去(一般是 root@192.168.1.1 ),设置一下密码。


如果要用透明代理的话,没编译的话去装依赖啦 😂:

# opkg 是一个小型的软件包管理器 😂

opkg install ip-full ipset iptables-mod-tproxy libev libpthread libpcre libmbedtls

接下来下载软件,注意需要根据自己的CPU内核架构来进行选择。可使用如下命令查看架构类型:

opkg print-architecture

然后去 http://openwrt-dist.sourceforge.net/packages/LEDE/ 下载些 ipk:

libcares_x.xx-x_xxxx.ipk
libsodium_x.x.xx-x_xxxx.ipk
shadowsocks-libev_x.x.x-x_xxxx.ipk
dns-forwarder_x.x.x-x_xxxx.ipk
ChinaDNS_x.x.x-x_xxxx.ipk
luci-app-shadowsocks_x.x.x-x_all.ipk
luci-app-chinadns_x.x.x-x_all.ipk
luci-app-dns-forwarder_x.x.x-x_all.ipk

将下载的包通过sftp之类的工具上传至路由器的 /tmp 目录。(可能需要安装 openssh-sftpserver)

最后就是安装啦:

opkg install libcares*.ipk libsodium*.ipk
opkg install shadowsocks-libev*.ipk luci-app-shadowsocks*.ipk
opkg install ChinaDNS*.ipk luci-app-chinadns*.ipk
opkg install dns-forwarder*.ipk luci-app-dns-forwarder*.ipk

设置 iptables6

需要准备个自己上手的编辑器 😂 LEDE 内置的是 vi,喜欢 vim/nano 的话可以通过 opkg 安装或者编译时加上。
  • 修改 /etc/config/network , 加上或者修改这一段(ULA前缀):

    NAT6转换的ULA网段要求是2fff::/64网段,因此把ULA前缀改为2fff::/64内的任意网段, 为了让客户端尽可能使用 IPv6 需要修改这个 😂

config globals 'globals'
        option ula_prefix '2fff::/64'
  • 修改 /etc/config/dhcp 在 LAN 接口上启用宣告默认路由:
config dhcp 'lan'
    option ra_default '1'
  • 然后查看一下默认网关:

    ip -6 route | grep "default from"

    大概会得到这样的

    default from (ipv6 range) via (gateway) dev (intf) proto static metric 512

    就需要向下一级宣告默认网关,括号的内容自行替换为上面结果。

    ip -6 r add default via (gateway) dev (intf)

    比如咱的结果是这样:

    default from 2001:da8:a005:218::/64 via fe80::72ba:efff:fe82:c654 dev eth1 metric 512

    那咱就这么写:

    ip -6 r add default via fe80::72ba:efff:fe82:c654 dev eth1

  • 用 ip6tables 设置 NAT

    ip6tables -t nat -A POSTROUTING -o (intf) -j MASQUERADE

    记得把 intf 换成相应的界面名称(例如咱的eth1),然后拿起连接到这个网络的设备 试一下 IPv6 能不能用了呗~

  • 最后可以考虑写个脚本自动化这个过程 (其实咱是抄的 😂) :

#!/bin/sh
# filename: /etc/hotplug.d/iface/90-ipv6
# please make sure this file has permission 644

# check if it is the intf which has a public ipv6 address like "2001:da8:100d:aaaa:485c::1/64"
# interface_pulbic 是带有公网IPV6地址的接口地址,大多数情况下是 wan6
interface_public="wan6"
[ "$INTERFACE" = "$interface_public" ] || exit 0

res=`ip -6 route | grep "default from"`
gateway=`echo $res | awk '{print $5}'`
interface=`echo $res | awk '{print $7}'`

if [ "$ACTION" = ifup ]; then
    ip -6 r add default via $gateway dev $interface
    if !(ip6tables-save -t nat | grep -q "v6NAT"); then
        ip6tables -t nat -A POSTROUTING -o $interface -m comment --comment "v6NAT" -j MASQUERADE
    fi
else
    ip6tables -t nat -D POSTROUTING -o $interface -m comment --comment "v6NAT" -j MASQUERADE
    ip -6 r del default via $gateway dev $interface
fi

把这个脚本放进 /etc/hotplug.d/iface/90-ipv6 ,设置好权限(例如644), 效果就是每次ipv6连接和断开就自动添加和删除路由规则 😂

设置 Shadowsocks

😂 嗯……

用 Luci 设置(和需要更多设置)的话请参考最后参考来源那篇文章 (咱要求低到能用就行 😂)

打开或修改 /etc/config/shadowsocks

# 基本设置
config general
    # 启动延迟
    option startup_delay '0'

# 透明代理设置
config transparent_proxy
    # UDP 中继服务器是哪个,没有的话就是 nil
    # 然而内部用的啥名字咱没看明白 😂 于是还是用 Luci 改了
    option udp_relay_server 'nil'
    # 透明代理的本地端口和 mtu
    option local_port '1234'
    option mtu '1492'
    list main_server 'cfg104a8f'
    list main_server 'cfg0c4a8f'

# 设置 socks5 代理
config socks5_proxy
    list server 'nil'
    option local_port '1080'
    option mtu '1492'

# 设置端口转发
config port_forward
    list server 'nil'
    option local_port '5300'
    option destination '8.8.4.4:53'
    option mtu '1492'

# 访问控制
config access_control
    # 路由器自身的代理类型 (1 好像是普通代理)
    option self_proxy '1'
    # 直连地址列表
    option wan_bp_list '/etc/chinadns_chnroute.txt'
    # 强制使用代理的 IP 地址列表,一行一个。
    list wan_fw_ips '8.8.8.8'
    list wan_fw_ips '8.8.4.4'
    option lan_target 'SS_SPEC_WAN_AC'
    # 强制直连的 IP 地址列表,一行一个。
    list wan_bp_ips '59.67.0.0/9'
    list wan_bp_ips '172.18.0.0/9'

# 设置服务器(和 Shadowsocks 客户端一样)
config servers
    option alias 'example'
    option fast_open '1'
    option no_delay '0'
    option server 'ip_address'
    option server_port '80'
    option timeout '60'
    option password 'password'
    option encrypt_method 'aes-256-cfb'
    option plugin 'obfs-local'
    option plugin_opts ''

设置 DNS-forwarder

把 DNS 请求转发给特定的端口~

创建或修改 /etc/network/dns-forwarder :

config dns-forwarder
    # 上游 DNS 服务器
    option dns_servers '8.8.8.8'
    # 开不开 😂
    option enable '1'
    # 本地的地址和端口
    option listen_port '5311'
    option listen_addr '127.0.0.1'

创建或修改 /etc/network/chaindns :

通过比较国内和国外 DNS 解析的差别区分是不是国内网站~
config chinadns
    # 国内路由表的位置
    option chnroute '/etc/chinadns_chnroute.txt'
    # ChinaDNS 的端口(和上面的 DNS 转发要不一样)
    option port '5353'
    # 开不开 😂
    option enable '1'
    # 双向过滤 😂
    option bidirectional '1'
    # 上游服务器,第一个是汝 ISP 的DNS(
    option server '0.0.0.0,127.0.0.1#5311'

修改 /etc/network/dhcp :

config dnsmasq
    # 加一行 ChinaDNS 的服务器:
    list server '127.0.0.1#5353'

然后重启路由器应该就 OK (😂

by ホロ at October 27, 2017 04:00 PM

October 10, 2017

百合仙子

WireGuard: 简单好用的 VPN

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

家里和公司电脑连接,因为厌倦了一个个做端口映射,有些还因为安全原因得走 ssh,所以决定弄个 VPN。之前使用过 OpenVPN,然而现在懒得再去配置 OpenVPN 的证书了,所以决定尝试一下新东西。

首先,去 WireGuard 官网上转了一圈,结果还是没弄明白怎么配置。后来尝试了一下 demo,把服务端和客户端的脚本分别看了一下,才弄明白。其实在 WireGuard 里,客户端和服务端基本是平等的,差别只是谁主动连接谁而已。双方都会监听一个 UDP 端口。双方都需要一对密钥。双方都需要把对方的公钥加进来。最后一步,谁主动连接,谁就是客户端。因为家里路由器有公网 IP,我做了端口映射,所以我当然是从公司连家里方便了,用不着麻烦的打洞脚本。

首先 pacman -S wireguard-tools 安装。这也会安装上 WireGuard 的内核模块。然后使用熟悉的 ip 命令添加并配置 WireGuard 的网络接口:

# 生成密钥对
wg genkey | tee privatekey | wg pubkey > publickey

sudo ip link add dev wg0 type wireguard
sudo ip address add dev wg0 192.168.58.1/24
sudo wg set wg0 listen-port 60010 private-key privatekey
sudo ip link set wg0 up

这是我家里的配置。使用的是网段 192.168.58.0/24,因为 56 是 vbox 虚拟机用的,57 分配给 lxc 和我的网络命名空间了。指定了一下监听的端口号。我把之前给 mosh 配置了转发的端口号中最高的那个挪用了。

公司里也是同时的配置,只是不需要指定监听端口号,然后把家里那边设置成 peer,并且连过去(相同的命令我没写):

sudo wg set wg0 private-key privatekey peer 这里是公钥 endpoint 家里的IP:60010 allowed-ips 0.0.0.0/0 persistent-keepalive 180

allowed-ips 指定过来的 IP。这里没怎么限制。persistent-keepalive 是为 NAT 设置的。WireGuard 本来很安静,不需要说话的时候就不说话,但是要往 NAT 后边的主机发送信息,需要经常通信,让 NAT 记得对应的映射关系。

然后家里那边也需要添加一下公司这边的公钥:

sudo wg set wg0 peer YiyFylL+1Dr3j2Cyf0lwXQYz2qaNwm3XyV5YvMFp3Vs= allowed-ips 192.168.58.2/32

IP 限制加上也是没有问题的。这里就不用加上 endpoint 了,它连过来的时候自然就知道了。WireGuard 是支持漫游的,也就是说,双方不管谁的地址变动了,WireGuard 在看到对方从新地址说话的时候,就会记住它的新地址(跟 mosh 一样,不过是双向的)。所以双方要是一直保持在线,并且通信足够频繁的话(比如配置 persistent-keepalive),两边的 IP 都不固定也不影响的。

最后,用得不错,可以把这几条命令写到一个 systemd service 文件里,就可以不用每次敲一串命令了,也可以做到联网后自动启动。


刚刚找了一下,其实之前使用的证书什么的还在,而且还没过期。而且因为弄 nghttpx,用了一下 xca,比 easy-rsa 好用很多呢。不过 WireGuard 的双向漫游很棒啊~

by 依云 at October 10, 2017 01:55 PM

October 01, 2017

ヨイツの賢狼ホロ

常见的双因素验证类型

这就是咱鸽了两个月的理由 ?😂

Two-factor authentication (or 2FA) is one of the biggest-bang-for-your-buck ways to improve the security of your online accounts. Luckily, it's becoming much more common across the web. With often just a few clicks in a given account's settings, 2FA adds an extra layer of security to your online accounts on top of your password.

双因素认证(Two-factor authentication,有时也缩写成 2FA)是保护汝在线账户安全的有力工具之一。 值得高兴的是,越来越多的服务和网站开始支持这一功能,设置起来也越来越简单。 (大多数时候只要在网站的账户设置页面上按步骤点击几下鼠标敲击几下键盘就好啦) 双因素认证在汝的密码之上对账户增加了另一层保护。

In addition to requesting something you know to log in (in this case, your password), an account protected with 2FA will also request information from something you have (usually your phone or a special USB security key). Once you put in your password, you'll grab a code from a text or app on your phone or plug in your security key before you are allowed to log in. Some platforms call 2FA different things—Multi-Factor Authentication (MFA), Two Step Verification (2SV), or Login Approvals—but no matter the name, the idea is the same: Even if someone gets your password, they won't be able to access your accounts unless they also have your phone or security key.

如果汝启用了双因素认证,在一般的登录工作完成以后(在这个环境下是汝输入完密码以后) 会要求其它的信息(通常在汝的手机上,或者是一个特殊的 USB 安全密钥)。 然后汝可能就会从手机短信或者应用上获得一串代码(或者插入安全密钥)来完成这一登录过程。 不同的地方可能对双因素认证有不同的称呼(汝可能听说过多因素认证,两步认证或是登录批准一类的词了呐~) 但不同名字下的目的却是相同的:即使有人得到了汝的密码,没有汝的手机或者安全密钥也是无法登录的呗~

There are four main types of 2FA in common use by consumer websites, and it's useful to know the differences. Some sites offer only one option; other sites offer a few different options. We recommend checking twofactorauth.org to find out which sites support 2FA and how, and turning on 2FA for as many of your online accounts as possible. For more visual learners, this infographic from Access Now offers additional information.

这里举出了常见的四种双因素认证的例子,知道它们的区别还是有些用处的啦~ 有些网站只提供一种选项,有些会提供好几种。汝如果可以上 https://twofactorauth.org 确认一下哪些网站提供双因素认证,而且尽可能的在汝使用的网站上打开双因素认证的话最好啦~ 不够生动?Access Now 有一张介绍各种双因素验证方式优劣的图,汝最好也去看看呗~

Finally, the extra layer of protection from 2FA doesn't mean you should use a weak password. Always make unique, strong passwords for each of your accounts, and then put 2FA on top of those for even better log-in security.

最后,不要认为有了双因素验证的保护就可以用个弱密码啦 😡 ,还是和平常教导的一样, 为汝的每一个账户使用一个独立而且安全性足够强的密码,然后使用双因素认证多添一层保护咯~

SMS 2FA (基于短信的双因素认证)

When you enable a site's SMS 2FA option, you'll often be asked to provide a phone number. Next time you log in with your username and password, you'll also be asked to enter a short code (typically 5-6 digits) that gets texted to your phone. This is a very popular option for sites to implement, since many people have an SMS-capable phone number and it doesn't require installing an app. It provides a significant step up in account security relative to just a username and password.

汝启用这样的双因素验证方式时便会像网站提供一个手机号码。 下次登录的时候除了用户名和密码以外汝还会要求输入一个从手机收到的代码(一般是五六个数字)。 这是种很常见的双因素验证的方法,因为大多数人的手机都能收短信(雾),而且实现起来也容易。 关键是效果显著啊(看起来比只输入用户名和密码安全)。

There are some disadvantages, however. Some people may not be comfortable giving their phone number—a piece of potentially identifying information—to a given website or platform. Even worse, some websites, once they have your phone number for 2FA purposes, will use it for other purposes, like targeted advertising, conversion tracking, and password resets. Allowing password resets based on a phone number provided for 2FA is an especially egregious problem, because it means attackers using phone number takeovers could get access to your account without even knowing your password.

当然这种方法的缺点也是有的:

  • 有些人不想泄露自己的手机号(或者其他可能关联到自己身份的物件)给网站

  • 现在网站有了汝的手机号,怎么利用就看它们的人品了(例如可能会用来点对点投放广告……)

  • 如果这种方法能重置密码的话,攻击者只需要拿到汝的手机就能在不知道汝的账户的情况下重置密码然后控制汝的账户啦~

    详情可以看 这篇另一篇 报道。

Further, you can't log in with SMS 2FA if your phone is dead or can't connect to a mobile network. This can especially be a problem when travelling abroad. Also, it's often possible for an attacker to trick your phone company into assigning your phone number to a different SIM card, allowing them to receive your 2FA codes. Flaws in the SS7 telephony protocol can allow the same thing. Note that both of these attacks only reduce the security of your account to the security of your password.

再多想点,要是汝的手机坏了或者收不到信号(例如汝正在旅行)就收不到验证码了诶。 有小心机的攻击者可以用手段骗过运营商把汝的手机号转移给他,更高明的话可以 利用通信协议的漏洞 。 这样汝的验证码就无声无息的被它们收到了哦~

Authenticator App / TOTP 2FA (验证器应用 / 基于时间的一次性密码)

Another phone-based option for 2FA is to use an application that generates codes locally based on a secret key. Google Authenticator is a very popular application for this; FreeOTP is a free software alternative. The underlying technology for this style of 2FA is called Time-Based One Time Password (TOTP), and is part of the Open Authentication (OATH) architecture (not to be confused with OAuth, the technology behind "Log in with Facebook" and "Log in with Twitter" buttons).

另外一种基于手机的验证就是生成验证代码的应用啦,例如 Google Authenticator (相当流行) 或者 FreeOTP (一个自由软件实现)。 驱动这一种双因素认证的技术是“基于时间的一次性代码”(Time-Based One Time Password ,缩写成TOTP),它也是开放认证 ( Open Authentication)结构的一部分。

不过别和 OAUTH 搞混啦(就是网站上那些 “使用 Facebook 登录” “使用 Twitter 登录” 按钮背后的技术)

If a site offers this style of 2FA, it will show you a QR code containing the secret key. You can scan that QR code into your application. If you have multiple phones you can scan it multiple times; you can also save the image to a safe place or print it out if you need a backup. Once you've scanned such a QR code, your application will produce a new 6-digit code every 30 seconds. Similar to SMS 2FA, you'll have to enter one of these codes in addition to your username and password in order to log in.

如果汝正在使用的网站提供这种验证方法的话,汝会按要求扫描网站提供的二维码 (或者在生成器应用中输入一个密钥,其实二维码的内容就是这个密钥)。 当然汝也可以在汝的好几个不同的手机上重复同样的操作,也可以把二维码或者密钥打印出来放在个安全的地方备用。 汝的生成器应用会每隔一段时间提供一个新的代码(比较常见的是30秒?),和基于短信的 双因素认证类似,汝会在输入完用户名和密码后输入生成器生成的代码。

This style of 2FA improves on SMS 2FA because you can use it even when your phone is not connected to a mobile network, and because the secret key is stored physically on your phone. If someone redirects your phone number to their own phone, they still won't be able to get your 2FA codes. It also has some disadvantages: If your phone dies or gets stolen, and you don't have printed backup codes or a saved copy of the original QR code, you can lose access to your account. For this reason, many sites will encourage you to enable SMS 2FA as a backup. Also, if you log in frequently on different computers, it can be inconvenient to unlock your phone, open an app, and type in the code each time.

基于时间的一次性密码比基于短信的双因素认证好的一点是就算汝的手机没有连接到网络也可以使用。 而且因为密钥存储在你的手机上,所以得到了汝的手机号码的人也没法获得汝的验证代码。 当然缺点也不是没有:

  • 如果汝的手机出了点状况(例如坏了,丢了等等),汝就没办法获得验证代码啦…… 所以不少网站会建议汝添加一个手机号码备用。
  • 另外如果汝经常在不同的电脑上登录的话,打开手机-解锁-打开验证器应用这一串过程可能会觉得有点麻烦(lll¬ω¬)

Push-based 2FA (基于推送通知的双因素认证)

Some systems, like Duo Push and Apple's Trusted Devices method, can send a prompt to one of your devices during login. This prompt will indicate that someone (possibly you) is trying to log in, and an estimated location for the login attempt. You can then approve or deny the attempt.

Duo Push 和 Apple 的“信任的设备”功能可以在登录时像汝的设备发送一条提示, 提示有人(当然可能是汝自己啦)在尝试登录。同时会提供一个估计的地址。汝可以选择批准或拒绝登录。

This style of 2FA improves on authenticator apps in two ways: Acknowledging the prompt is slightly more convenient than typing in a code, and it is somewhat more resistant to phishing. With SMS and authenticator apps, a phishing site can simply ask for your code in addition to your password, and pass that code along to the legitimate site when logging in as you. Because push-based 2FA generally displays an estimated location based on the IP address from which a login was originated, and most phishing attacks don't happen to be operated from the same IP address ranges as their victims, you may be able to spot a phishing attack in progress by noticing that the estimated location differs from your actual location. However, this requires that you pay close attention to a subtle security indicator. And since location is only estimated, it's tempting to ignore any anomalies. So the additional phishing protection provided by push-based 2FA is limited.

相对于基于时间的一次性密码,这种方式的优点有: * 点个通知比敲几个数字或者字母少动手( * 一定程度上可以防范一定程度高深的钓鱼网站(雾

手法高明的钓鱼网站可以画一个和真网站一样的输入验证码的界面迷惑用户,再坑点还能 把登录信息提交到真网站并把受害者重定向到真网站上去(嘿嘿嘿……

不过由于推送通知一般会包含登录地点的估计地址(通常通过 IP 地址估计), 而大多数情况下钓鱼网站不会和用户是一个 IP 地址,因此可能会被识别出来。 然而地址是估计的嘛所以也没有多少保护力 (╯‵□′)╯︵┻━┻

Disadvantages of push-based 2FA: It's not standardized, so you can't choose from a variety of authenticator apps, and can't consolidate all your push-based credentials in a single app. Also, it requires a working data connection on your phone, while Authenticator apps don't require any connection, and SMS can work on an SMS-only phone plane (or in poor signal areas).

再相对的这个也有缺点: * 标准还没统一,这就意味着汝没得选替代应用,也没法让多个账户使用一个应用。 * 需要汝的手机有网络连接(lll¬ω¬),然而 SMS 可以在没有手机网络连接的地方工作(验证器的应用连网络都不要)……

FIDO U2F / Security Keys (FIDO 通用双重因素 / 安全密钥)

Universal Second Factor (U2F) is a relatively new style of 2FA, typically using small USB, NFC or Bluetooth Low Energy (BTLE) devices often called "security keys." To set it up on a site, you register your U2F device. On subsequent logins, the site will prompt you to connect your device and tap it to allow the login.

通用双重因素(U2F)是一种新兴的双因素验证方法。常见的是 USB,NFC 或者蓝牙低功耗设备。(有时它们也被叫做“安全密钥) 汝在网站上注册了设备以后,下次登录时汝会被要求连接汝的设备然后按下上面的按钮来允许登录。

Like push-based 2FA, this means you don't have to type any codes. Under the hood, the U2F device recognizes the site you are on and responds with a code (a signed challenge) that is specific to that site. This means that U2F has a very important advantage over the other 2FA methods: It is actually phishing-proof, because the browser includes the site name when talking to the U2F device, and the U2F device won't respond to sites it hasn't been registered to. U2F is also well-designed from a privacy perspective: You can use the same U2F device on multiple sites, but you have a different identity with each site, so they can't use a single unique device identity for tracking.

和基于推送通知的双因素认证一样,汝不用多输入些啥。在幕后,汝的 U2F 设备记得汝注册的网站并与它交涉(?)。 这就是 U2F 的一个显著优点,它能更有效的防御钓鱼攻击(因为它不认识钓鱼网站,因此拒绝沟通(lll¬ω¬))。 U2F 也可以做到保护隐私:汝可以一个 U2F 设备打天下,但是需要的话也可以买好几个各司其职 ★,°:.☆( ̄▽ ̄):.°★

The main downsides of U2F are browser support, mobile support, and cost. Right now only Chrome supports U2F, though Firefox is working on an implementation. The W3C is working on further standardizing the U2F protocol for the web, which should lead to further adoption. Additionally, mobile support is challenging, because most U2F devices use USB.

摆在 U2F 面前的主要障碍就是浏览器和移动设备的支持以及花费。 目前只有 Chrome 支持 U2F (不过 Firefox 也在努力赶上啦)。 另外 W3C 也在加紧标准化 U2F 协议咯~ 不过手机上好像还是很艰难的 o(><;)oo(因为大多数都是 USB 设备?)

There are a handful of U2F devices that work with mobile phones over NFC and BTLE. NFC is supported only on Android. On iOS, Apple does not currently allow apps to interact with the NFC hardware, which prevents effective use of NFC U2F. BTLE is much less desirable because a BTLE U2F device requires a battery, and the pairing experience is less intuitive that tapping an NFC device. However, poor mobile support doesn't mean that using U2F prevents you from logging in on mobile. Most sites that support U2F also support TOTP and backup codes. You can log in once on your mobile device using one of those options, while using your phishing-proof U2F device for logins on the desktop. This is particularly effective for mobile sites and apps that only require you to log in once, and keep you logged in.

有些 U2F 设备通过 NFC 或者 蓝牙低功耗一类的技术支持移动设备(但是 Apple 一直不给应用用 NFC 所以……), 不过这并不能阻止咱们用 U2F (笑),因为提供 U2F 登录的网站大多数也会同时提供 TOTP 或者备份代码验证。 而且大多数移动应用只需要登陆一次就可以在手机上记住登录状态, 所以汝大可以在手机上用传统的方式登录,而在电脑上照插不误((lll¬ω¬))。

Lastly, most other 2FA methods are free, assuming you already have a smartphone. Most U2F devices cost money. Brad Hill has put together a review of various U2F devices, which generally cost USD $10-$20. GitHub has written a free, software-based U2F authenticator for macOS, but using this as your only U2F device would mean that losing your laptop could result in losing access to your account.

最后,如果假设汝有一部智能手机的话,其它的双因素验证方法可以说没有额外开销。 但是 U2F 要稍微的投资一下(大概在 10-20 $ 之间?), Brad Hill 有一篇评测不同 U2F 设备的文章值得一读 。 如果汝在使用 MacOS 的话, 可以考虑试试 GitHub 编写的软 U2F 应用 工具, 不过还是不要把鸡蛋放在一个篮子里就是啦~

Bonus: Backup Codes (追加提示:备份代码 / 恢复代码)

Sites will often give you a set of ten backup codes to print out and use in case your phone is dead or you lose your security key. Hard-copy backup codes are also useful when traveling, or in other situations where your phone may not have signal or reliable charging. No matter which 2FA method you decide is right for you, it's a good idea to keep these backup codes in a safe place to make sure you don't get locked out of your account when you need them.

在完成双因素验证设置时网站会提供一些备份代码给汝呗,一般是十个一组。 如果汝的手机或者安全密钥出现了一些情况(例如坏了,没有信号,或者丢了 😂) 或者没这么坏(例如只是汝出门旅行手机没电 or 没信号时……),就到了这些备份代码发挥自己的 价值的时候啦(雾)。 不过不管汝选择的是哪一种双因素验证方式,务必像对待汝的密码一样对待这些备份代码啦~ 把它们放到一个只有汝知道又方便取用的地方吧。 (期望不要用到就是了)


翻译自 电子前哨基金会A Guide to Common Types of Two-Factor Authentication on the Web , 以 知识共享 署名 3.0 US 协议 授权。

电子前哨基金会是一个维护互联网上的公民自由的民权组织,汝可以通过 捐款, 成为会员 , 或者 更多其它途径 支持他们的工作咯~

by ホロ at October 01, 2017 04:00 PM

September 28, 2017

百合仙子

To hup or not to hup

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

故事起源于同事想在后台跑一个服务:

$ nohup node xxx.js &

一切如愿。

——是吗?

实际情况是,这时退出 bash 是如愿了,但是直接关掉终端窗口的话,那个服务会死掉。

bash 奇怪行为之五

(我好像没有写前四个耶。等有时间了简单写一下吧。)

man bash 然后搜索 SIGHUP,你会发现,其实默认设置,bash 正常退出时,根本不会杀害后台进程。它们会和从脚本里运行时一样欢快地继续跑下去。只有 bash 因为收到 SIGHUP 而退出时,它才会给后台进程发 SIGHUP。

所以,直接 Ctrl-D 或者 exit 退出的话,(处理好了重定向的话,)要不要 nohup 都一样,进程不会死。

zsh 默认退出时会给后台任务发送 SIGHUP(除非你 disown 了)。

但这还是不能解释关窗口的时候,服务为什么会死掉呀?nohup 不是已经忽略掉 SIGHUP 了么?

与众不同的 nodejs

通常情况下,nohup 工作得很好。但是,UNIX 世界里来了位不了解、也不愿意遵循 UNIX 传统惯例的年轻气盛的小伙子。

我还记得 npm 直接往 /usr 下安装东西。

我还记得 npm 把 http_proxy 当 https_proxy 而我的缓存代理不支持 HTTPS,造成无法安装任何东西。

现在,nodejs 将所有信号的处理重置为默认行为,除了它自己想处理的那几个。

「nohup?那是什么鬼?我搞不懂!」nodejs 说,然后它被 SIGHUP 杀死了。

结语

The devil is in the detail!

by 依云 at September 28, 2017 10:12 AM

September 24, 2017

百合仙子

书签搜索:藏在书签里的搜索引擎

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

最近开始用 Firefox nightly 了。纯 WebExtensions 时代就要来临了,然而 WebExtensions 还不能访问浏览器搜索引擎,所以我没办法选中文本,然后选个正确的搜索引擎了……

我之前使用的是 FireGuestures。选中文本,一个手势弹出我所有的搜索引擎,然后选一个使用。挺好挺方便。然而看现在使用 WebExtensions 的搜索扩展,因为无法访问浏览器搜索引擎,所以都是内置了几个搜索引擎,最多让用户手动添加自己想要使用的。多麻烦啊,而且还要维护额外的数据。

而火狐从一开始就可以「为此搜索引擎添加关键词」(如下图)。虽然也叫「搜索引擎」,但是并不是搜索栏用的那个,而是带关键字的书签。对于 GET 请求的搜索,是在 URL 里把搜索词写作「%s」。对于 POST 请求,「PostData」是用户看不到、扩展也访问不到的,也不能被同步。不过大部分搜索都是 GET 嘛。功能上是弱了一些,比如不能在只有关键词、没有搜索词的时候打开主页。但是书签可以同步呀!

添加用来搜索的书签

于是,利用书签来存储、同步搜索引擎的扩展——书签搜索——诞生了!效果图如下:

使用「书签搜索」上下文菜单

(这个其实是之前《改了一下 GTK 3 的默认主题》的配图 :-)

感兴趣就前往安装书签搜索吧~喜欢请给小星星(AMO 和 GitHub 都可以给星星的哦)~遇到问题请点击右侧「用户支持网站」报告~


这是我的第一个 WebExtensions 扩展,也是第一个正式发布的火狐扩展(其实也支持 Google Chrome 和 Opera;Microsoft Edge 没有尝试,大概也支持吧)。WebExtensions 写起来比 XUL 舒服很多,最主要是文档全面了!不用像写 XUL 那样,拿浏览器工具箱找要修改的 DOM,然后满世界找自己需要的 API。WebExtensions 提供的 API 就那么点,在 MDN 上都有写。另外就是 JavaScript 这些年来进步很大,越来越像 Python 了!可惜 Google Chrome 和 Opera 它们的扩展还不使用 Promise、不能用 await,不然写起来更舒服了。

by 依云 at September 24, 2017 09:20 AM

September 21, 2017

百合仙子

使用 Prince 转换 HTML 文档给 Kindle 阅读

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

ZeroMQ 的指南文档很长很长。我想放在 Kindle 里,上下班的时候看,长知识又不伤眼。

首先尝试 Push to Kindle。就是本博客每篇文章下边都有的那个链接里的东西。试了好几次终于成功了。然而,章节标题看不出来跑哪儿去了也就算了,代码去哪儿了?注意格式啊!

于是换浏览器,HTML 转 PDF。顺手按 F12,把每个标题右边的导航链接删掉了。然后打印~代码格式没有坏哦~然而,还是有很多代码没显示出来,倒是显示了一堆其它语言代码的链接……继续 F12 改样式表修了。这些都是小问题,最大的问题是,在我不断地调整页面大小的时候,我的火狐每次「准备……」的过程都特别漫长,那个负责转换的子进程吃很多很多 CPU,还卡死了所有它负责的标签页……终于,在等待近半小时它还没准备好的时候,我失去了耐心,杀掉了那个火狐子进程,换 Prince 了。这次我体会到多进程架构的好处了:页面卡了,换个标签页打开,分配到另外的子进程的时候就可以正常使用了。

Prince 是个非常不错的 HTML 转 PDF 软件。免费版本会有个它自己的图标放第一页右上角,没啥问题,打印的时候也不会出现。要是你非要去掉它的话,也可以找个 PDF 编辑工具删掉它。

然后是确定页面大小。因为代码的行都比较长,我决定横屏阅读,也就是「landscape」模式。然后拿尺子量了一下,差不多是 9cm×12cm。维基百科告诉我 Kindle Paperwhite 是6英寸的屏幕,但是我没有弄明白它的长和宽到底是多少,所以还是动手测量了。因为 Kindle 上字显示小一些也挺清晰的,所以我把短边乘以了二(好像并不合理啊,因为已经是 landscape 了,应该两边同步放大才对;不过其实我一开始想的是一页占两屏……)。

然后再加上针对 ZeroMQ 文档的修改,得样式表如下:

td + td {
  display: none;
}

.collapsible-block-folded {
  display: none;
}

.collapsible-block-unfolded {
  display: block !important;
}

.collapsible-block + br + span {
  display: none;
}

body {
  font-family: serif !important;
}

@page {
  size: 18cm 12cm landscape;
  margin: 0;
}

然后让我们的王子干活啦:

prince -s zguide.css zguide.html -o zguide.pdf

因为需要反复尝试,所以我已经把 ZeroMQ 那个巨大的 HTML 下载到本地了。

最终成果在这里。因为页边距为零,所以在一般的阅读器里看起来挺难看的,但是在 Kindle 里就挺适合了~

by 依云 at September 21, 2017 07:55 AM

September 14, 2017

百合仙子

放弃 you-get,转投 youtube-dl

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

you-get 是一个视频下载工具。我于七年零一周前(2012年9月7日)在 AUR 打包并维护其 git 版本。当时还是叫 python-you-get-git,后来根据 Arch 官方的推荐,与语言没什么关系的软件不带语言前缀,改名为 you-get-git

youtube-dl 是另一个差不多同时期出现的视频下载工具,一开始主要针对 YouTube 等跨国网站。

选择 you-get 一部分原因是当时它对国内网站的支持更好,另一方面也是支持国产。但是今天,我决定放弃 you-get 了。

五年来,我一直是支持 you-get 的。也尝试过向其贡献代码。目前已经有29个提交被合并,排名第五位。基本上都是一些很小的修改,比如编码问题、未回收的僵尸进程、标题的反转义、ffmpeg 命令的特殊字符转义、支持 python -m 调用、视频链接的解析更新和扩充、进度条的修正和优化,等等。

其实这些年来,我一直想做更多事:

  • 可选地使用 requests 库,以提高解析速度,改善用户体验
  • 支持使用 aria2c 下载视频链接
  • 支持网易云课堂的更高清晰度的未内嵌字幕的视频(pr#1002
  • 解析更准确的信息
  • 一些其它网站的解析器(比如 bilibili 的 bangumi.bilibili.com 子域)

但是,其中很多都没能完成。勉强完成的也很奇怪:明明是针对网易云课堂的解析,我还非得关心网易云音乐。一直以来,我对 you-get 的修改都是事倍功半。我也曾尝试过更深入的修改,但是牵一发而动全身,往往要改就得改很大很大一部分代码,然后完全没有办法保证其正确性。就像今天的事情一样。

我花了数小时的时间,牺牲睡眠,把命令行选项解析由 getopt 改成了 argparse(pr#2260)。促使我做此修改的原因是,我想下载 bilibili 一整个播放列表的视频。我记得 you-get 有下载整个播放列表的功能。但是我读了好几遍 help 信息,都没有找到那个选项。我记错了吗?阅读源码之后,我终于找到了那个选项。同时,我也看到了在 C 和 bash 代码里经常看到的,一长串 if/else 来解析命令行选项。翻了好几屏。

当一个相对独立的代码片断翻屏时,bug 数量会骤增。

曾经在公司里遇到过一个 case,非常直接地证明这句论断是有多么正确。那个函数刚好超过一屏数行,而在第二屏的那部分代码,有个「}」和「return」的顺序写反了。我也是拿 Vim 的匹配括号跳转功能才发现的。

当然了,不管怎样的代码,不动它是不会出新问题的。然而我动了它。回报我的是两个局部变量名忘记改了:pr#2346pr#2355

这种问题在 nvchecker 重构以支持 aiohttp 时并没有发生。为什么呢?我们有测试。如此明显的问题,只要 cover 了必然会发现。所以我可以放心大胆地重构。

you-get 呢?you-get 也有测试。我在提交 pull requests 之后有个习惯:盯着未完成的测试,直到它变绿。如果红了,赶紧看看是不是自己代码的问题,是就赶紧修掉。一些项目(比如 Tornado)的测试本地跑起来要配置环境、装不少东西,太麻烦了,所以我习惯先提了 pr,然后等 Travis-CI 的结果。可这次,测试过了。但是有两个重要的功能却并不能正常工作。

其实呢,对于这种简单的错误,通常 linter 会告诉我的。我有装 neomake,全面支持各种 linter,用起来十分惬意。但 pylint……就像 jslint 一样,我很讨厌它们。因为它们不仅检查潜在的问题,同时还检查代码风格。而代码风格这事是每个项目单独配置的,而不是开发者自己配置好,然后让自己参与的所有开源项目都遵守。不过今天我也终于知道了另一个 Python linter——pyflakes 很对我的味口:只管问题,不管风格。

总之呢,由于各种原因,重写中出了这么两个直接立刻让用户不能用的 bug。很抱歉。一般来说,出错了就改呗。更深入一些,分析一下为什么会出现这种错误,今后怎么避免同样的错误两次出现(早年向 Tornado 提交代码时,Ben Darnell 一个简单的行为教会了我一件事:修了 bug 就写个对应的测试)。但是 you-get 的协作者 rosynirvana 不按惯例来,反而要求放弃此修改。如果就如此也就算了,后续讨论中我意识到了一个真相——为什么我在 you-get 上的工作如此困难?

The best part of you-get is that it's not so pythonic so those who only know js or as3 can take part in, moving from the universal getopt to a py-domain-specific library cannot be a nice idea.

source

What library nowaday pythonists love do not really matter here because those one know js and as3 can contribute even more in this project.

source

因为 you-get 根本就是反 Pythoner 的!作为一个 Python 项目,you-get 想要吸引的是 JavaScript 和 ActionScript 3 开发者!

我很震惊。

  • 作为 Python 开发者,我已被他们刻意排斥在外。
  • 作为 JavaScript 开发者,我还是觉得 C 好难写,还是 pythonic 的代码比较好维护啊。
  • 作为 C 开发者,我倒是对这种长达数屏的作用域见怪不怪了。不过重复的逻辑,咱一般会用宏之类的手段给整成声明式的啊。

所以,我的努力注定不会有多少效果。

然后,我看了一眼 youtube-dl。其实就瞟了一眼,也没看出太多东西来,但是

  • 按 URL 进行正则匹配的,网易云音乐和网易云课堂可以分开处理了!
  • 解析器以 class 表达,有组织有纪律!不用用 Python 的语法写 C 了!

我 disown 了 AUR 和 [archlinuxcn] 里的 you-get-git 包。关闭了未完成的 issue 和 feature pr。等修复 argparse 引入的错误的 pr 被合并(不管是只修正问题还是退回到 getopt),事一了,我就删掉仓库,只保留网易云课堂的高清视频解析代码(花了我一整天的)。已安装的 you-get 暂时保留,但首选 youtube-dl,遇到问题有时间就去修一下。已经投入到 you-get 的时间是沉没成本,不必留恋。

by 依云 at September 14, 2017 04:49 PM

September 11, 2017

百合仙子

等连上互联网之后再来找我吧

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

最近公司弄了 Wi-Fi 登录。就是那个叫 captive portal 的东西。

Android 早就会在连接 Wi-Fi 时检测网络是不是要登录了,为此 Google 弄了个 /generate_204 的 URL。小米、高通、USTC、v2ex 也都提供了这个东西,方便广大中国大陆 Android 用户使用。(我发现我的 Android 使用的是高通的地址,没有用 Google 的。)

但我使用的 Arch Linux 自行开发的 netctl 网络管理工具没这种功能。火狐倒是不知道什么时候加上了,不过使用的地址 http://detectportal.firefox.com/success.txt 是返回 200 的。

所以我启动火狐就可以看到要登录的提示了。然而问题是,其它程序不知道要登录啊。像 offlineimap、openvpn、rescuetime 这种还好,会自己重试。可每次网络需要登录的时候 dcron 就会给我发一堆邮件告诉我我的 git pull 都失败了……当然还有我老早就注意到的 pkgstats,经常会因为启动过早而无法发送统计数据。

所以呢,得想个办法,等连上互联网之后再跑那些脚本啊服务什么的。

检测是不是连好了很简单,不断尝试就可以了。但我需要一个系统级的 Condition 对象来通知等待方可以继续了。然而我只知道 Linux 有提供信号量。难道要自己弄共享内存来用么?

#archlinux-cn 问了一下,farseerfc 说试试命名管道。我想了想,还真可以。只有读端的时候进程就会阻塞,一旦有写端就能成功打开了。当然没有读端的打开写端会打不开,不过没关系,反正这进程也不能退出,得一直拿着这个文件描述符。

没想到很少用到的命名管道有意想不到的用法呢。我以前还为了不阻塞而专门写了篇文章呢。

于是负责检测网络连通的 check-online 和等待网络连好的 wait-online 都写好了。

check-online 应当是个服务。那就交给 systemd 吧。然后……systemd 不是有个 network-online.target 么?正好可以让 check-online 来达成这个目标呢,多合适呀。

于是服务写好了。测试了几天,大成功!不仅 wait-online 很好地工作了,而且我发现 openvpn 和 pkgstats 自动排到 network-online.target 后边去了。nginx 的 OSCP staple 经常因为 DNS 失败而无法成功,我也可以在联好网之后去 reload 一下它了。(不是强依赖,我可不希望连不上网的时候我本地的 wiki 也访问不了。)

整个项目就叫作 wait-online,在 GitHub 上,欢迎送小星星哦~Arch Linux 包可以从 [archlinuxcn] 仓库 安装 wait-online-git 包。

by 依云 at September 11, 2017 10:58 AM

September 09, 2017

百合仙子

改了一下 GTK 3 的默认主题

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

最近开始用 Firefox nightly 了。它使用 GTK 3,于是对 GTK 3 主题的不满也逐渐表现出来了。

首先是选中的文本,以及菜单项。默认的那个蓝色太深了,我还是更喜欢 GTK 2 mist 主题那个浅蓝色。

然后,我是使用亮色主题的,黑黑的 tooltip 提示框太违和。而且边距那么大,多浪费空间啊。改成 GTK 2 时代那种简单的样子好了。

最后,那个又细又黑的滚动条好讨厌。改掉改掉!

最终效果图(好不容易才把这么多东西截到一张图里喵):

GTK 3 Adwaite tweaked

~/.config/gtk-3.0/gtk.css 代码:

/* For Adwaita {{{1
 */
/* scrollbars {{{2
 */
scrollbar {
  border-radius: 0;
  background-color: #eaeaea;
}
scrollbar slider {
  background-color: #bfbfbf;
  border-color: transparent;
  border-width: 4px;
  min-height: 10px;
  min-width: 10px;
  border-radius: 0;
}
scrollbar slider:hover, scrollbar slider:active {
  background-color: #bfbfbf;
}
/* tooltip {{{2
 */
tooltip {
  border: 1px solid #808080;
  background-color: rgba(254,254,228,0.9);
  border-radius: 0;
  padding: 0;
}

tooltip * {
  color: #000;
  padding: 0;
}
/* others {{{2
 */
*:selected, menuitem:hover {
  background-color: #d6e9f8;
  color: currentColor;
}

/* Vim modeline {{{1
 * vim:se fdm=marker:
 */

by 依云 at September 09, 2017 02:47 AM

September 02, 2017

中文社区新闻

Perl 库路径变更

现在开始 perl 包将会在编译模块的路径中添加版本号。这意味着针对不同版本的 perl 编译的模块将不会被加载,进而需要重新编译。

在升级的时候会有一个 pacman hook 对受影响的模块提出警告,类似这样:


WARNING: '/usr/lib/perl5/vendor_perl' contains data from at least 143 packages which will NOT be used by the installed perl interpreter.
-> Run the following command to get a list of affected packages: pacman -Qqo '/usr/lib/perl5/vendor_perl'

使用这些模块之前你必须重新编译他们。这一变动同样会影响到通过 CPAN 安装的模块。并且在下一次 Perl 的大版本更新时,比如更新到 5.28 或者 5.30 时将需要再次重新编译。

请注意早在此次变更之前 perl 已经要求重新编译模块了,然而从此之后 perl 将不再尝试载入那些模块并且会以奇怪的方式失败。

如果某些软件的构建系统不能找到正确的路径,你可以在 PKGBUILD 中使用 perl -V:vendorarch 来查询正确的路径。同样还有 sitearch 用于那些并非由 pacman 打包管理的软件。

by farseerfc at September 02, 2017 10:24 PM

August 31, 2017

百合仙子

新的火狐,新的旅程

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

Firedoge

今年底火狐将不再支持旧的基于 XUL 的扩展了。火狐53已经不支持 GTK 2 了。每人一只与众不同的火狐的时代行将结束,而生活仍要继续。

使用 ESR 版本只是短暂地续命罢了,stylo 也用不了。正巧ヨイツの賢狼ホロ[archlinuxcn] 仓库打包了可以与已有版本共存的 firefox-nightly 包,于是新的旅程开始了。

一开始,我尝试喂给它旧的 profile(复制了一份,然后用 firefox-nightly -no-remote -P 启动)。我原本有64个扩展,结果只有六个还兼容……

不如像之前升级火狐4那样,重练好了。重新取一块干净的画布,从头开始。

结果比预期中的好很多。

首先,新火狐很快。我不想办法关动画了。我不担心开新的窗口耗时了。多进程的架构也使得我不那么害怕单个标签页卡死导致什么也做不了了。不过不知道是进步明显,还是我那几十个XUL扩展太拖累了。

然后,同步很好用。虽然同步的内容有限,但是比手工搬移一些数据好多了,也可以把标签页发送到另外的设备阅读。同步的数据包括:书签,历史,表单自动填充,密码,从AMO安装的扩展(不过只会安装最新稳定版),部分首选项(只是一部分,所以还是有不少需要手动调整)。打开的标签页也可以从另外的设备看到。我同时也在 Android 上登录了同步,这样访问历史可以同步,方便在不同的场景下继续阅读。

不过也有点问题。首先是 tampermonkey 支持同步从URL安装的脚本,但是呢,一开始它没同步过来,我手动安装了。然后它又不知道什么时候同步了一份,于是重复了。删掉重复的,又过了一段时间,同步过来的那份也消失了……

还有一些设置上的调整。about:config 打开,搜索 x-western,改成 sans-serif 字体。剩下的中日韩字体设置,这次我直接在 fontconfig 那边配置了,就不用在火狐里点来点去的了。

安装中文语言包之后([archlinuxcn] 源里有),搜索 locale,把 en-US 改成 zh-CN。

搜索 urlbar,关闭自动填充、双击全选、隐藏 http:// 和 ftp://,地址栏建议数量增加为15,关闭搜索引擎的建议。

定制一下工具栏,使用紧凑布局,把不需要的扔掉,用得少的放进收纳盒里去。RSS 按钮丢最右上角好了。侧栏还是显示左边吧,那按钮也放左边来。

导入旧的书签。开了同步的话,注意一定不要从备份中恢复书签!因为恢复的时候会删掉已有的所有书签,包含移动端书签。导出成 HTML 再导入就会合并了。

打开 InoReader 设置界面,把 InoReader 添加为RSS阅读器。

最后就是各种扩展了。许多扩展没有迁移到 Web Extensions,但是一些还是有替代方案的。具体可以看这个表格。然后是配置这些扩展,包含各种扩展数据的导入。

还有很多扩展没有替代品,那也只能放弃了。有些扩展目前是不可能在 Web Extensions 上实现的。至于 UI,算了,接受现实吧。其实 nightly 的 GTK 3 界面做得比最开始的版本已经好了很多。

哦对了,我还换了个轻量级主题。之前使用的 White Lily,已经下架很久了。这次我换了这个,明快简洁,挺好看的。

具体扩展的介绍,我单独写了一篇

在导入网易云音乐的播放列表(我没有登录)的时候,我还发现一个事儿:火狐52的 devtools 有 bug,并不会显示 localStorage……

总体感受,忍痛割爱之后,又是一片新天地。nightly 确实很快。

by 依云 at August 31, 2017 08:37 AM

August 14, 2017

百合仙子

我为什么离开新浪

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

时隔两年,终于还是来写这篇文章了。

其实之前已经想写过多次,但是部分内容写起来十分消耗精力,所以一直没动手。但现在无所谓了。

名字问题

每个人都有名字。大部分人的名字都不是自己的,因为那是父母起的。自己相信它是自己的,然后不知道是它真的变成了自己,还是自己变成了它

然而我不要变成它。

在自由之前,呃,也就是经济独立、远离父母之前,我其实对于我自己是没有太多感受的。只是不断地按照他们的要求或者自己的兴趣,学习科学知识,上学考试,吃饭睡觉。甚至给仇人下跪。

后来,我终于自由了。至少是在客观上自由了。我开始渐渐地觉察到自己的存在,自己的需求、自己的感受。我开始变成真正独立自主的人。

然而,那个人给起的名字,却总是提醒我,曾经的伤痛,曾经只能把自己的想法深深地埋藏在心底不敢说出来,曾经不怎么敢说话,怕稍一不小心,哪句话就会激怒那个人。曾经为了生存,我尽量降低自己的存在感,好不招惹麻烦。

那是我不愿意面对的过往。我宁愿永远忘记它们,当作一切没有发生过。但是,看到那个人给起的看似威武、实则俗气的名字,我经常会想起那个人,想起那个人的凶狠,想起那些黑暗的日子,想起那些我憎恶暴力的缘由。

所以我非常厌恶实名制。支付宝有个界面会显示身份证姓名,所以不管它说有多少多少优惠我都不去。招商银行 app 开始在主界面显示身份证姓名之后,我也开始尽量避免使用它。

我之前的工作,都不太需要用到身份证上的名字。其实同事之前不用很容易啦。但是在新浪,每天早上一到公司,各种登录名都是那个名字的拼音。而且密码是动态密码,必须手动输入,所以通过编程自动登录是行不通的。

想想看,每天一早,你就不得不面对自己最想遗忘的事情,你还能好好地工作吗?

我跟 HR 的人反映过登录名的问题,然而并没有得到明确的回复。

我当然考虑过改名的事情了。我之前已经在知乎上问过相关问题。我也在利用不多的机会探听父母目前对我改名字的意见。我在意父母的意见,不是因为我有多在乎他们,而是因为改名需要的户口本在他们手里,而且我得在他们附近去办手续。目前看来还好,但是惨痛的经验使我明白,那个人是完全不能以理智所理解和预测的,所以我还是得做好应对最坏的情况的准备。秋天回去改名吧,夏天太热,心力不足,我怕出事。也不知道拿到新身份证之前坐高铁会不会有问题。

跳板机

新浪使用跳板机,上边跑个 script 程序记录用户操作。设计者的意思是,你们只需要在服务器上手敲命令就可以了。但是我需要传文件啊!需要跑脚本啊!在新浪,想往服务器上传个文件,需要以下步骤:

  1. 使用一个脚本把文件上传到专门的服务器
  2. 在跳板机上使用另一个脚本下载文件
  3. 使用 scp 命令把文件传到目标服务器

多了两步。而且这些步骤没办法使用脚本处理,因为没办法从本地在跳板机上执行命令。script 程序的存在使得无法通过 ssh 命令直接执行命令。

有人说,把所有操作放服务器上操作就好了嘛。可是给我服务器的 root 权限来装各种工具吗?而且 CentOS 6 的软件都死旧死旧的,给我换成 Arch?还是自己装需要的版本,然后再也不升级,直到遇到CVE-2017-1000117然后被入侵?然后装好图形界面我 VNC 过去?不然我怎么让服务器里的 Vim 控制我本地的输入法呢?怎么给浏览器发 URL 呢?怎么复制粘贴大段文字呢?

当然了,这个跳板机是不支持密钥登录这么方便的方式的。也不支持普通密码登录。动态密码,看你还怎么用脚本!

这就像,明明你有架飞机可以很快抵达目的地,但是对方规定你必须在他们边境上滚下来,换上马车慢慢走到天荒地老。

不测试,直接上线

开发完毕,然后呢?上线!

没有测试环境。连本地测试环境都没有,因为没人知道那东西怎么搭。真真正正的 push to deploy。连编译期检查都难。颤抖吧,开发者!敲错了一个字符,直接影响到至少 1/3 的用户!

我喜欢 Rust。在 Rust 之前我喜欢 Haskell,虽然它很难。这两门语言的特点是,类型系统很强大,以至于很多时候,类型检查通过了,代码就是正确的。我更喜欢 Rust 的原因之一是,Haskell 有异常。有时候明明通过了类型检查,但是跑起来,BOOM!文件打不开,或者取了空列表里的元素,就崩掉了。在 Rust 里我不可能会忘记处理它们。

我喜欢有信心地开发。开发完成,测试通过,上线。回家安安稳稳地睡觉,休息日放心大胆地不想不看工作上的事情。甚至开发完成,我就可以忘记它的细节,因为我不需要不断地去修各种不小心导致的 bug。我喜欢把 bug 消灭在襁褓之中,而不是等着它潜伏下来,趁人不备狠咬我一口。

所以我讨厌 PHP 扩展的开发。没有文档,只能翻源码。然而我一时半会看不完近千万行的代码。所以我开发完之后根本没把握自己开发的东西能够一直正确地运行下去。我不知道换下一个版本的 PHP 它会不会崩掉。我用 valgrind 检查了,但是我仍然不知道我是不是正确地释放了内存,有没有遗漏,有没有释放了不该我释放的内存。

可是,连最基本简单的测试都没有,我有什么理由认为我的代码不会出问题呢?连 Rust 都要有测试,何况 C?我又不是高德纳,能够洋洋洒洒写一大段代码还没有问题。我刚写好的代码,有明显的错漏也是很正常的事情。软件项目是工程,又不是艺术。我只要在交付的时候保证质量就好了嘛,为什么要求一次就写对呢?

结语

当然,离开新浪还有其它一些因素,我就不说了。

本文关闭评论,因为有些事情,我不介意公开,但并不想持续被提及。

by 依云 at August 14, 2017 10:42 AM

August 11, 2017

Felix Yan

尝鲜可能比 sunpinyin 好一点的新拼音输入法

大概很多人还不知道,在老K同学偷偷默默开发了很久后,新一代的 fcitx5 已经“能用”了。不过因为还处在早期开发阶段,现在只有拼音输入法能用,而且输入界面极挫,没有配置界面和任何命令行帮助信息等。

先来一段 demo:

下面简单介绍一下 Arch 里的安装及配置方法:

首先从 AUR 中安装相关的软件包:

yaourt -S fcitx5-git fcitx5-qt-git fcitx5-gtk-git fcitx5-chinese-addons-git

依赖中其他的相关包会被 yaourt 自动安装。如果你更喜欢手动安装,可以参考下面的顺序:

xcb-imdkit-git -> fcitx5-git -> libime-git -> fcitx5-chinese-addons-git -> fcitx5-gtk-git -> fcitx5-qt-git

注意原来的 fcitx 和这系列软件包冲突,可能会被提示卸载。

安装完成后,把下面的内容粘贴到 ~/.config/fcitx5/profile 中(来自老K的博客

[Profile]
# CurrentGroup
CurrentGroup=Default

[Profile/GroupOrder]
0=Default

[Profile/Groups/0]
# Default Input Method
DefaultIM=pinyin
# Layout
Default Layout=us
# Group Name
Name=Default

[Profile/Groups/0/Items/1]
# Layout
Layout=
# Name
Name=pinyin

[Profile/Groups/0/Items/0]
# Layout
Layout=
# Name
Name=keyboard-us

然后修改环境变量如下:

GTK_IM_MODULE=fcitx5
XMODIFIERS=@im=fcitx
QT_IM_MODULE=fcitx5

重启后,可能需要你手动运行一下 fcitx5,然后运行你喜欢的程序测试效果吧~

by Felix Yan at August 11, 2017 08:06 AM

Arch Linux devtools 简介 – 在干净的环境里编译软件包

devtools 是 Arch Linux 开发者们用来往官方仓库里推进软件包使用的一系列工具。由于里面有许多工具可能不是我们常用的,这里主要介绍里面的一部分——用于在干净的环境中编译软件包的命令。

为什么要在干净的环境里编译软件包?这里有几个常见的理由:

  • 避免忘记写依赖 – 当前环境中已安装的软件包可能在普通的 makepkg 过程中被忽略,最后在 depends 或者 makedepends 等列表中缺失。
  • 避免编译过程污染环境 – 因为一些你可能没有想到的原因,编译过程中可能会对你当前的系统产生污染,比如跑 npm install 的时候可能会把缓存塞到 $HOME。
  • 避免因环境不干净导致的奇怪编译错误 – 你的环境中可能有各种不干净的情况,比如 profile.d 里覆盖了 gcc 等常见命令、/usr/local/bin 里有覆盖常用命令、用非系统包管理器安装(覆盖)了一些东西等。
  • 或者你只是不想把这个软件包编译时需要的一堆乱七八糟的依赖都装在自己机器上。

如果你有这样的需求,可以考虑使用 devtools。安装过程很简单

pacman -S devtools

下面需要考虑的是,这个软件包是符合哪个仓库规范的。由于一般用户不需要考虑 staging 系列仓库和配置文件等问题,下面这个列表是从 ArchWiki 中简化的版本:

目标仓库 架构 使用命令
extra / community i686 extra-i686-build
extra / community x86_64 extra-x86_64-build
testing / community-testing i686 testing-i686-build
testing / community-testing x86_64 testing-x86_64-build
multilib x86_64 multilib-build
multilib-testing x86_64 multilib-testing-build
archlinuxcn x86_64 archlinuxcn-x86_64-build
archlinuxcn (但有用到 multilib 中的依赖) x86_64 multilib-archlinuxcn-build

(注意最后两个命令来自 devtools-cn-git 软件包,可以从 [archlinuxcn] 仓库中得到)

直接在包含 PKGBUILD 文件的目录中执行对应的命令(替换原来工作流里的 makepkg 命令)即可。

常见问题、小技巧

  • 软件包的依赖不在官方仓库中,如何把这个包“塞”到编译环境中?

extra-x86_64-build -- -I /var/cache/pacman/pkg/your-package-0-x86_64.pkg.tar.xz

  • 如何传入 makepkg 的命令行参数,比如如果我想跳过测试?

extra-x86_64-build -- -- --nocheck

  • 每次重新准备干净 chroot 的过程特别慢,如何可以提速呢?

可以考虑做一个 btrfs 镜像挂载到 /var/lib/archbuild,devtools 会自动使用 btrfs 来加速这个过程。参考制作过程:

truncate -s 100G /var/archbuild.img
mkfs.btrfs /var/archbuild.img
mount -o discard,compress=lzo,autodefrag /var/archbuild.img /var/lib/archbuild
echo "/var/archbuild.img /var/lib/archbuild btrfs defaults,discard,compress=lzo,autodefrag 0 0" >> /etc/fstab

(记得要先清空 /var/lib/archbuild 目录以便作为挂载点。)

by Felix Yan at August 11, 2017 03:18 AM

August 10, 2017

Felix Yan

给 Arch 打一个包 – Python 模块篇

这是一篇简化的教程,如果你有一个喜爱的 Python 模块不在 Arch 仓库里,AUR 里也没有,可以尝试读下去。

准备

对 Python 模块来说,一般仅仅一个 PKGBUILD 文件就足以完成所有的事情。现在你可以打开你最喜欢的文本编辑器,把下面这一个简单的 PKGBUILD 模板复制进去:

# $Id$
# Maintainer: Felix Yan <felixonmars@archlinux.org>

pkgbase=python-whatever
pkgname=('python-whatever' 'python2-whatever')
pkgver=0.4.3
pkgrel=1
pkgdesc='Easy way to make anonymous functions by partial application of operators'
arch=('any')
license=('BSD')
url='https://github.com/Suor/whatever'
makedepends=('python-setuptools' 'python2-setuptools')
checkdepends=('python-pytest-runner' 'python2-pytest-runner')
source=("$pkgbase-$pkgver.tar.gz::https://github.com/Suor/whatever/archive/$pkgver.tar.gz")
sha512sums=('162d66753ef4fb15279150b7fa953b4ecf086e2b36cc77531dac099ff4a25b3458af627bdf52e168b7b4b2163a1445f35c2c656b1c10c0c73502d2357ba42dd8')

prepare() {
  cp -a whatever-$pkgver{,-py2}
}

build() {
  cd "$srcdir"/whatever-$pkgver
  python setup.py build

  cd "$srcdir"/whatever-$pkgver-py2
  python2 setup.py build
}

check() {
  cd "$srcdir"/whatever-$pkgver
  python setup.py pytest

  cd "$srcdir"/whatever-$pkgver-py2
  python2 setup.py pytest
}

package_python-whatever() {
  depends=('python')

  cd whatever-$pkgver
  python setup.py install --root="$pkgdir" --optimize=1
  install -D -m644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
}

package_python2-whatever() {
  depends=('python2')

  cd whatever-$pkgver-py2
  python2 setup.py install --root="$pkgdir" --optimize=1
  install -D -m644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE
}

# vim:set ts=2 sw=2 et:

因为距离 Python 2 的废弃时间(2020年)还早,Arch 仓库中的 Python 模块包通常同时提供 Python 2/3 模块。上面例子里的 Python 软件包名叫 whatever,这也同样是它在 PyPI 中的名字。如果原来的包名中包含大写字母,在制作软件包时需要改成小写。

名字和源代码位置

现在你可以开始修改了。首先替换名字,一个简单的方法是首先找到你要处理的软件包的仓库,然后全局查找替换 GitHub URL 和软件包名(如果不在 GitHub 上,就稍微找一下吧)。

注:PyPI 的下载地址一般格式为:

"https://pypi.io/packages/source/喵/名字/名字-$pkgver.tar.gz"

注意把中间的“喵”换成包名的首字母。

依赖、介绍、授权

现在可以打开代码中的 setup.py 了。如果你看到了 install_requires=,那恭喜你,基本可以参照这个文件的内容(或者它打开的文件)来找到依赖列表。对每一个依赖,先检查一下是否在 Arch 仓库或 AUR 中(大部分都在),如果在的话,更新一下上面的 makedepends 列表和下方 python 2/3 对应的 package_*() 函数中的 depends 列表,加入这些依赖。接下来,在附近你可以找到模块介绍和授权,分别填入 pkgdesc 和 license 即可。

如果你看到的是一个只有包括 pbr=True 的很短的 setup.py 文件,说明这个项目使用了 pbr。这时候你需要打开 requirements.txt 查找依赖,并把 python{,2}-pbr 加入 makedepends。模块介绍和授权则会在同目录的 setup.cfg 文件中。

测试

下面是一个比较麻烦的步骤:确定测试如何运行。当然如果你对你的包已经信心满满根本不想跑测试,可以直接去掉 check() 和 checkdepends() (咳咳)然后完工。

一般来说项目自己跑着的持续集成会把测试工具定义在 .travis.yml 或者 tox.ini 这样的文件里,可以在这些地方找到一些线索。一般来说,如果测试使用的是 pytest/py.test 这样的命令,说明测试工具是 pytest,上面的模板已经是这个工具的用法了。模板里定义的 pytest-runner 是一个可以用来解决 PYTHONPATH、2to3、以及依赖等问题的 pytest 运行工具。

如果你看到了 nosetests,说明测试工具是 nose。把上面模板中的 pytest-runner 替换成 nose,并把下面的 setup.py pytest 替换为 setup.py nosetests 一般可以解决问题。

如果你看到的是普通的 setup.py test,则可以去掉 checkdepends,直接修改 check() 为一样的语句。

还有一些其他情况,可以试试在官方仓库里找找我对类似情况的处理进行参考 🙂

最后把定义在 setup.py 中 tests_requires 里,或者 tox.ini、.travis.yml 中的依赖加入 checkdepends 列表,大功告成!

现在你可以运行 makepkg 进行打包尝试了。如果上面的步骤都正确完成了,这里应该可以顺利完成(flag)。

疑难解答

  • 有些项目的测试需要 X 环境,而我的服务器上不一定启动了 X。

在 checkdepends 中加入 xorg-server-xvfb,然后在 check() 中两个运行测试的语句前面加上 xvfb-run 即可。

  • 打包过程中因为 UnicodeDecodeError 挂掉了。

在不同的条件下,这个问题的原因可能是多种多样的,但解决方法通常比较一致:在出问题的语句前面加上 LC_CTYPE=en_US.UTF-8。

  • pbr 提示无法找到版本。

在 prepare() 里加上:export PBR_VERSION=$pkgver

  • setuptools_scm 提示无法找到版本。

在 prepare() 里加上:export SETUPTOOLS_SCM_PRETEND_VERSION=$pkgver

  • 测试中用到了 entry_point 功能,因此在未安装的情况下测试软件会失败。

这种情况确实需要更多的 hack,我常用的一个简单方法是安装到临时目录,然后修改 PYTHONPATH:

check() {
  # Hack entry points by installing it

  cd "$srcdir"/whatever-$pkgver
  python setup.py install --root="$PWD/tmp_install" --optimize=1
  PYTHONPATH="$PWD/tmp_install/usr/lib/python3.6/site-packages:$PYTHONPATH" py.test

  cd "$srcdir"/whatever-$pkgver-py2
  python2 setup.py install --root="$PWD/tmp_install" --optimize=1
  PYTHONPATH="$PWD/tmp_install/usr/lib/python2.7/site-packages:$PYTHONPATH" py.test2
}

  • 我实在调不过测试了,但我确定这个包没问题;或者上游已经知道这个问题了,确定不是因为我打包方面的问题引起。

把这个测试跳过吧!(咳咳)

by Felix Yan at August 10, 2017 03:39 PM

August 02, 2017

ヨイツの賢狼ホロ

某胡言乱语的 IRC 教程

某个胡言乱语爪牙无措不知所云的 IRC 教程

所以 IRC 是个啥?

因特网中继聊天(英语:Internet Relay Chat,缩写即IRC)是一种历史悠久、 应用广泛、成熟稳定的网络即时通讯协议,被广泛地应用于在线通讯和网络聊天中。 IRC最早由芬兰人雅尔口·欧伊卡林恁(Jarkko Oikarinen) 1988年8月创造以取代一个叫做MUT的程序,随后便一直在互联网中扮演重要角色。 凡是支持互联网的操作系统,几乎都可以使用IRC。虽然在2003年以后, 许多功能更加丰富的聊天程序和服务取代了只能进行纯文字交流的IRC, 许多曾经的大型IRC服务器日渐式微,但对于许多应用来说,依然是一种方便可靠的通讯方式。

IRC是一种RFC标准公开规定的,分布式的主从式架构—— 即服务端对客户端进行服务的网络即时通讯协议,采用TCP和SSL协议。在IRC协议中, 不同的独立服务器可以互相进行中继,组成一个整体,提供更加大型聊天网络服务, 这也是IRC名字中“中继”的由来。

IRC是一种协议,或者说仅仅是提供了聊天的一种方式, 而不是指单一具体的某个聊天电脑程序、软件或者网络服务器。 换句话说,只要有服务器和客户端软件实现了IRC协议, 就能够以IRC设计的方式进行在线聊天。 例如,IRC服务器可以是某个组织公开所运营的大型网络,不需要客户注册和登录即可使用; 也可以是私自架设的内部IRC服务器,公司内部使用IRC进行交流。

https://zh.wikibooks.org/wiki/IRC

(话说本来想把这个教程写进 Wikibooks 去的但是就是严肃不起来啦 😂)

所以 IRC 就是一个 IM 协议啦,和 XMPP ,Matrix 一类的差不多。 虽然古老但是经久耐用啊(跑

后面都连最大的 IRC 服务器 https://freenode.net 好了 😂

irc://irc.freenode.net:7000

更多的端口和连法(例如 plain、Tor )请参阅 https://freenode.net/kb/answer/chat

各种 IRC 客户端推(an)荐(li)兼连接教程 😂

因为 IRC 协议开放,于是就有很多客户端可以用啦。 Wikipedia 上的这个页面 有各种 IRC 客户端的比较,咱就只挑几个介绍一下怎么连接好了(跑

webchat.freenode.net

freenode 的网页版,临时用下还好,不过还是用个客户端好呐~

https://webchat.freenode.net
freenode_web

从上到下分别是昵称,频道,是否使用认证和 Google 的验证码,填好后一把梭 connect 就行 😂

CIRC

可以从 Chrome web store 下载: https://chrome.google.com/webstore/detail/circ/bebigdkelppomhhjaaianniiifjbgocn?hl=zh-CN

circ

用 /nick 指定一个昵称啦~

circ

然后跟着指示操作就成?

https://github.com/flackr/circ/ <- 文档在这里 😂

ChatZilla

是 Firefox 的一个扩展啦: https://addons.mozilla.org/en-US/firefox/addon/chatzilla/

如果需要汉化的话: https://addons.mozilla.org/en-US/firefox/addon/chatzilla-zh-cn-language-pack/

chatzilla

可以先戳左边的昵称改一下昵称(默认是用户名)然后戳那个 freenode 连接啦~

chatzilla

http://chatzilla.hacksrus.com/intro <- 文档在这里(?) 😂

Polari

GNOME 的一部分: https://wiki.gnome.org/Apps/Polari <- 文档也在这(?) 😂

polari

直接戳那个 + ~

polari

可以看到 Polari 内置 freenode 啦 ~

polari

有需要的话右键网络名称改下昵称呗~

polari

然后就可以接着戳 + 加入聊天室啦 😂

polari

Thunderbird

喂喂咱只是个邮件客户端啊 😂

thunderbird

创建聊天账户(或者戳“聊天”然后添加账户)

thunderbird

选择聊天网络(当然是 IRC 啦 ……)

thunderbird

用户名和服务器(

thunderbird

这里应该填 SASL 的密码(?),不过现在啥都没有就直接下一步吧 😂

thunderbird

高级选项,应该写的够清楚了吧。只要保证用 SSL 就行(

thunderbird

保存!

thunderbird

在 Thunderbird 中加入频道的话,在聊天标签页中戳“加入聊天”, 然后输入相应的频道名称就好啦~

https://support.mozilla.org/en-US/kb/instant-messaging-and-chat <- 文档在这里 😂

WeeChat

喵喵喵?

weechat

大概像这个样子……看咱 connect 一把梭啦 😂

# 设置服务器和端口

/set irc.server.freenode.addresses "chat.freenode.net/7000"

# 开 SSL

/set irc.server.freenode.ssl on

# 设置昵称和备用昵称 (默认是汝的用户名)

/set irc.server.freenode.nicks "mynick,mynick2,mynick3,mynick4,mynick5"

# 设置自动连接

/set irc.server.freenode.autoconnect on

# 连接

/connect freenode

或者可以像咱一样搞个 relay 😂

https://freenode.net/kb/answer/registration

官方的速成指南: https://weechat.org/files/doc/stable/weechat_quickstart.en.html

或者更详(luo)细(suo)的文档:

https://weechat.org/files/doc/stable/weechat_user.en.html

更换和注册昵称

https://freenode.net/kb/answer/registration

有时汝可能会收到这样的消息:

This nickname is registered. Please choose a different nickname,

or identify via /msg NickServ identify <password>.

这就表示汝现在使用的 nickname 已经有人占啦 😂 这种时候汝首先应该换一个 nickname, 像这样(大部分 IRC 客户端应该都支持):

/nick Foo

然后汝是不是也想注册一个 nickname 呢?那首先汝先 /nick 一个没别人占而且合适的 nickname,然后:

/msg NickServ REGISTER password youremail@example.com

把 password 换成汝自己设置的密码,youremail@example.com 换成汝的一个邮箱。

顺便说一句大部分 IRC 客户端里 /msg 可以向一个用户发送 Private Message (私聊消息)?

然后应该会说些话(具体是啥咱忘了 😂),嘛打开汝的邮箱找 freenode.net 发来的邮件咯。 邮件里应该会有这一行:

/msg NickServ VERIFY REGISTER nick token

nick 是汝注册的 nickname ,token 是一串字母。把这行复制下来发出去就 OK 啦 😄

下次再登录的时候用 /msg NickServ identify <password> 验证(

一个账号上也可以注册多个 nickname:

  • 首先先登录上现有的昵称。
  • 然后使用 /nick 命令 切换到另一个昵称。
  • 最后用 /msg NickServ GROUP 归组昵称。

如果客户端支持的话,可以参考 https://freenode.net/kb/answer/sasl 的文档启用 SASL 登录,就不用每次都输入密码啦 😂 (或者让客户端记住密码)

可以与 NickServ 私聊时输入 HELP 获得更多用法。

加入频道

https://freenode.net/kb/answer/findingchannels 像这样搜索频道,不过大多数人应该目标明确……

大部分的客户端都可以用 /join #channel 的命令来加入频道,或者有加入频道的按钮。

进不去? 有的频道有不同的要求(例如只允许注册用户,强制 SSL ,需要邀请等等), 自己看客户端的报错咯 😂

其实还有可能是汝被 ban 啦,但是新人只要不乱说话应该不会 😂 ……

加入频道之后请马上看 topic (可能在加入时以一条消息的方式显示, 或者在客户端的某个位置。或者可以用 /topic 命令查看)。 一个频道的 topic 一般包含 了这个频道的基本规则,新消息和可用的资源(例如相关联的项目的官方网站)。如果汝是 去提问的,可能会在链接的资源里找到答案 :-)

交流

  • 大部分的频道都建议直奔主题,所以不要问“在不在”,“有人吗”之类的问题,直接提出问题就好啦~

    不过还是要记得提问的智慧……

  • 要提及某个用户(类似于其它 IM 上的 @),习惯上是这样:

    要提及用户的昵称: 消息文本

    大多数的 IRC 客户端会在这种情况下通知被提及的用户,有的客户端只要消息中有用户的 昵称就会通知 😂,不过记得不要滥用(有人觉得频繁的被通知是一种骚扰)

    有的客户端会帮汝补全昵称(自动或者按 Tab 键补全),或者点击昵称就会自动添加上面 那样的提及语句,如果有的话可以利用一下。

  • 多数IRC客户端具备信息记录功能。不过 IRC 服务器不会。

    再不过有的频道会使用某些方式(例如机器人)对记录进行存档(可能会在 Topic 上写出来),有需要的话可以问问。

  • 千万别刷屏!😂 也就是不要短时间内发好几条消息(或者一条太长的消息)。

    各个频道具体的限制的话可以去看看 Topic ,或者去问问管理员 (Operator,有时会缩写成 op,汝的 IRC 客户端应该会特别的表示它们)。

    刷屏可能(哦不,是一定)会使汝被频道封禁,严重的话可能会被服务器封禁。

    如果想发送文件或者一长段文字(例如软件的日志)的话,请使用 Pastebin 服务(一会儿再说)。

  • 有的频道里会有机器人,记得遵守频道里怎么用和啥时候用机器人的规则。

  • 然后,遵守 freenode 一般规则 ,频道的其它规则和普通的礼仪 😂

使用 Pastebin 类服务

Pastebin 是一类服务的称呼,可以叫“网络剪贴板”?

汝可以把信息发表到 Pastebin 上,会得到一个链接。然后把对应的网站链接发表在IRC频道内,解决刷屏问题。

解决刷屏问题。有些Pastebin还支持程序代码高亮,或者张贴图片,这样就可以解决IRC分享图片的问题啦~

嘛咱用过这两个:

其实类似的服务有不少,例如 Pastebin.com (但是被墙了), 各种自由软件项目通常也都会有自己的 Pastebin ,例如 GNOME, 和 openSUSE

嘛 Pastebin 的网站上一般都有用法介绍,咱就不啰嗦啦 😂

创建和注册频道

嘛不知道汝有啥动机要创建频道那就直接上了 😂

加入个没人的频道会让汝自动获得 Operator 权限,但是一掉就丢了…… 所以需要注册一下:

注册前速查表?

注册频道

首先先加入想要注册的频道,这时汝该会自动获得 Operator 权限。

然后和 ChanServ 私聊一下就好:

/msg ChanServ REGISTER ##channel

大概会收到这样的消息:

##channel is now registered to nickname.

Channel guidelines can be found on the freenode website: http://freenode.net/changuide

This is an "about" channel as per http://freenode.net/policies#channel-ownership

这样就表示注册成功啦~

当汝下次加入这个频道时,可以和 ChanServ 私聊重新拿回 Operator 权限。

/msg ChanServ OP ##channel nickname

频道的 Operator 可以用 /mode 命令为频道设置不同的模式 (参见 https://freenode.net/kb/answer/channelmodes )。

和 NickServ 类似,可以与 ChanServ 私聊时输入 HELP 获得更多用法。


最后记得 Freenode 的知识库 是汝的 friends 哦(雾

by ホロ at August 02, 2017 04:00 PM

July 30, 2017

ヨイツの賢狼ホロ

用内置硬盘安装 Arch Linux

用内置硬盘安装 Arch Linux,但是为啥不去买个 U 盘呢?😂

准备原材料

  • 就一个 ArchISO 😂
  • 用磁盘管理(或者其它汝中意的分区软件)分出一个比 ISO 大的分区,然后格式化成 FAT32。

在 Windows 中挂载 ESP

用 diskpart 就好啦 ……

diskpart
list disk # 列出硬盘
select disk 0 # 选择一块硬盘 (一般内置硬盘都是第0块)
list partition # 列出分区
select partition 1 # 选择一个分区
assign letter=b # 分配一个盘符
exit

或者 mountvol 😂

mountvol b: /s # 把 b: 换成一个没在用的盘符

然后以管理员身份重启文件资源管理器(不然写不进去 😂)

taskkill /im explorer.exe /f
explorer.exe

准备文件

  • 把 Arch ISO 的文件复制到新的分区上,然后照着 ISO 改一下卷标 😂
改卷标……
  • 把 Arch ISO 里的 EFI/boot/bootx64.efi 复制到 ESP 的EFI/boot/bootx64.efi 上(记得备份),需要的话也可以把 EFI Shell 复制过来。

  • 把 Arch ISO 里的 arch/boot/x86_64 文件夹原样复制到 ESP 上(需要 microcode 更新的可以带上 arch/boot/intel-ucode.img

  • 把 Arch ISO 里的 loader 文件夹原样复制到 ESP 上 😂

    如果没复制 arch/boot/intel-ucode.img 的话,去 archiso-x86-64.conf 把 initrd /arch/boot/intel_ucode.img 删掉(


然后重启试试? 😂

by ホロ at July 30, 2017 04:00 PM

July 29, 2017

ヨイツの賢狼ホロ

给 GNU/Linux 萌新的 Arch Linux 安装指南

给会用点 Windows 的彻头彻尾的 GNU/Linux 新手的 Arch Linux 安装指南 😣

为啥要搞这个?

因为 ArchWiki 上的 Beginner Guide 已经和 Installation Guide 合成一个啦😂,然后有小白开始抱怨看不懂啦(误 (╯・∧・)╯ ┻━┻

其实咱最早看的也是 Beginner Guide ……

算了概念用到时再解释 😂

我是一个彻头彻尾的Linux新手,我应该用Arch吗?

如果你是新手,要使用 Arch 就必须愿意花时间学习新系统,接受 Arch 是一个 DIY 的系统,每个用户都是自己系统的组建者。

在开始问任何问题之前,自己先通过Google、Wiki或者论坛进行搜索。我们为你创建了这些资源并让你可以随时访问,上千志愿者为你提供了大量的信息资源。

推荐阅读: Arch terminology#RTFM

首先说点废话

备份……

万一手抖格错了盘别抱怨 Linux ……

下载 ISO

https://www.archlinux.org/download/

BT 种子和磁力链接在上面,直接下载的话往下拉,找 China 下面的镜像网站挑一个下载就好。 (っ╹ ◡ ╹ )っ

确定启动类型

  • 首先打开设置 ( Windows 8/8.1 叫做 "电脑设置"),然后通过 "更新和恢复" -> "恢复" -> "高级启动" 重启电脑.

如果是 UEFI 启动的话,大概是这个样子:

UEFI 系统启动之后大概像这样

没错就是有个 "使用设备" 的选项 😂

  • 或者同时按下键盘上的 Windows 徽标键(就是有 Windows 标志那个) 和 R 键,会打开“运行” 对话框。
“运行”对话框

在里面输入 msinfo32 然后回车(按 Enter 键)确认,打开”系统信息”应用。

“系统信息”窗口

看“BIOS模式”里是不是 UEFI 😂😂,还有下面那个 “安全启动状态”是不是“以关闭”(咱这台电脑的 UEFI 太旧所以显示的是不支持)

如果安全启动是打开的还需要自己进 UEFI 固件设置里手动关闭 😂

具体怎么关因为每种电脑的方法不一样于是汝要自己 STFW (Search the f**king Web,搜索一下) 了😂
  • 再或者打开磁盘管理(Windows 8 以后的系统可以通过按下 Windows + X 的菜单里找到 “磁盘管理”)
磁盘管理在这~

嗯,大概就是这样子的呗 (虽然具体的磁盘分区可能和咱的不一样)

大概长这样~

看汝的硬盘上有没有一个 EFI 系统分区 😂😂😂


还是搞不懂的下面也不用看了,准备下最后的晚餐吧 😋 (误

在硬盘上准备一块空闲空间

不然要把 Linux 装到哪里去呐?

这里拿来演示的是 Windows 7 以后都自带的 “磁盘管理” 程序,应该能解决大多数问题 _(:з」∠)_

  • Windows 8 以后的系统可以通过按下 Windows + X 的菜单里找到 “磁盘管理”
磁盘管理在这~
  • 嗯,大概就是这样子的呗 (虽然具体的磁盘分区可能和咱的不一样)
大概长这样~
  • 汝哪个硬盘分区比较空闲? 右键点击它,有一个"压缩卷的选项"
”压缩卷“ 在这~
  • 输入压缩的大小 _(:з」∠)_
多少
  • 然后就多了一块未分配的空间 😂
多了一块未分配的空间

如果汝的硬盘分区有些刁钻而磁盘管理没法解决的话,AOMEI 家的分区助手不错, 这是官方网站 , 这是分区教程

制作启动盘

但前提是汝的电脑能从 U 盘启动 😂 (不过最近几年生产的电脑都应该可以了吧……

Windows 下咱比较推荐一个叫 rufus 的软件,官方网站在这

下载完以后双击运行,需要管理员权限,记得看有没有数字签名。(有数字签名时用户账户控制的对话框是蓝色的)

Rufus 自带多国语言(当然也包括中文啦),如果汝系统语言不是中文的话,点击那个地球图标就可以修改语言了啦~

选择语言

然后戳有点像光盘的按钮选择刚下载好的 ISO 镜像

选择映像

然后选择一种启动类型,UEFI 就选最后一个,不是的话就选第一个。

选择启动类型

写入方式选推荐的就好 (´_`)

选择写入方式

确认(要知道汝按下确认以后就没有回头路了,所以记得提前备份 U 盘上的资料 😂)

确认

然后坐等完成,完成以后汝的 U 盘卷标应该是 "ARCH_201610" 这样的 (后面四位年份和两位月份),不要改成别的,万一不对记得照 ISO 改回来 😂😂

准备启动

重启电脑,然后让电脑从 U 盘启动。

具体怎么搞还是要看电脑的硬件啦 😂
  • MBR 成功启动以后像这样
MBR

选第一项。😂 (除了 CPU 不支持的都应该用x86_64 😋)

  • UEFI 成功启动以后像这样
UEFI

还是选第一项。😂

然后等待一会以后会出现……

root@archiso ~ #

这就表示已经启动完毕啦 ~(>_<~)

root是用戶名,前面那個數字是上一個命令的exit status啦,如果正常結束的命令exit status是0,就不會顯示出來,你有 1 2 127 這種都是某種東西報錯了.

----现任 Arch Linux TU 之一的 farseerfchttps://www.zhihu.com/question/45329752/answer/98733823 中写到……

联网

首先当然是联网啦,如果是自动获取 IP 地址的有线网络,那么应该啥也不用做,ping 一下试试?

root@archiso ~ # ping archlinux.org

(把电脑用网线接到家里的路由器上就有相同的效果)

如果没网的话…… 😂


  • 先用 ip link 确定一下网卡
root@archiso ~ # ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp4s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN mode DEFAULT group default qlen 1000
    link/ether c8:9c:dc:a8:ab:c3 brd ff:ff:ff:ff:ff:ff
3: wlp0s29u1u1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
    link/ether 44:94:fc:0f:63:b9 brd ff:ff:ff:ff:ff:ff

这个例子里,lo 是本地环回不用管它,enp 开头的是有线网卡,wlp 开头的是无线网卡。

如果汝明明有无线网卡却没识别的话,有可能汝是某无线网卡厂商受害者😂😂

这时可以:

  • 有 Android 手机的话,手机连 WiFi ,然后用“USB 网络共享”共享给电脑。
  • 找个 USB 无线网卡插上 😂
  • 连有线😂😂
  • 如果有无线网卡的话,试试连接到 WiFi ……

** 输入 wifi-menu ,等一下会看到找到的 WiFi 网络的列表

WiFi 列表

** 选择一个网络,保存网络配置文件

保存配置文件

** 如果有密码的话,输入密码

WiFi 密码

** 然后按 Enter 确认,连到 WiFi 的话会返回 Shell。

谁叫 Arch 连不上网的话都装不了 😂

时间同步

timedatectl set-ntp true 保证时间同步 。

root@archiso ~ # timedatectl set-ntp true
root@archiso ~ # timedatectl status
    Local time: Fri 2016-10-28 17:39:42 UTC
Universal time: Fri 2016-10-28 17:39:42 UTC
        RTC time: Fri 2016-10-28 17:39:42
    Time zone: UTC (UTC, +0000)
Network time on: yes
NTP synchronized: no
RTC in local TZ: no

准备硬盘空间

这里用 cgdisk (UEFI)/ cfdisk (MBR) 来给硬盘分区。

两个看起来差不多所以咱偷会儿懒😂

首先输入 lsblk 看看汝的硬盘是哪个设备:

root@archiso ~ # lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0 463.9G  0 disk
├─sda1   8:1    0   512M  0 part
├─sda2   8:2    0    16M  0 part
├─sda3   8:3    0 103.4G  0 part
└─sda4   8:4    0 253.4G  0 part
sdb      8:16   1   7.5G  0 disk
└─sdb1   8:17   1   7.5G  0 part /run/archiso/bootmnt
sr0     11:0    1  1024M  0 rom
loop0    7:0    0 346.1M  1 loop /run/archiso/sfs/airootfs

比如咱这里 sda 是咱的硬盘,于是运行 cgdisk 时加上 /dev/sda 这个参数:

/dev 是一个虚拟文件夹(也就是并不在硬盘上),它会把电脑上的设备映射成一个个文件 _(:з」∠)_
root@archiso ~ # cgdisk /dev/sda
                                            cgdisk 1.0.1

                                        Disk Drive: /dev/sda
                                    Size: 972906545, 463.9 GiB

Part. #     Size        Partition Type            Partition Name
----------------------------------------------------------------
            1007.0 KiB  free space
1           512.0 MiB   EFI System                EFI system partition
2           16.0 MiB    Microsoft reserved        Microsoft reserved partition
3           103.4 GiB   Microsoft basic data      Basic data partition
4           253.4 GiB   Microsoft basic data      Basic data partition
            106.6 GiB   free space





    [ Align  ]  [ Backup ]  [  Help  ]  [  Load  ]  [  New   ]  [  Quit  ]  [ Verify ]  [ Write  ]

cgdisk 的界面大概像这样啦,用上下方向键把光标移动到汝之前的空闲空间上去(例如咱这里是最后一个)

新硬盘的话应该只有一个 free space 😂

用左右方向键把下面一排按钮上的光标移动到 New 上,然后按 Enter。

(这里看不出光标😂,黑色背景下光标应该是白的吧😂😂)

接下来会问几个问题(# 开头的是咱加上的注释😂):

# 数字可能和汝看到的不一样😂
# 起始扇区的位置,直接 Enter 就行
First sector (749424640-972906511, default = 749424640):
# 大小,可以是扇区数,也可以是实际的大小(例如 100M,20G一类的),要用掉整个剩余空闲空间的话,直接 Enter 就行。
Size in sectors or {KMGTP} (default = 223481872):
# 分区类型,默认的就好
# 但是如果要建立新的 EFI 系统分区的话 ,分区类型是 :code:`ef00`
# 但是如果要建立新的 交换空间(就是虚拟内存啦)的话 ,分区类型是 :code:`8200`
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300):
# 设置卷标,不设置也行。
Current partition name is ''
Enter new partition name, or <Enter> to use the current name:

然后汝应该会发现下面的空闲空间变成 Linux filesystem 了呗~

要保存分区表的话,用左右方向键把下面一排按钮上的光标移动到 Write 上,然后按 Enter。

Are you sure you want to write the partition table to disk? (yes or no):

        Warning!! This may destroy data on your disk!

在这里输入 yes (就是 yes,不是 y Y YES 啥的😂),然后按 Enter。

然后下面会闪过一行 "The operation has completed successfully" ,这时就可以退出了。

用左右方向键把下面一排按钮上的光标移动到 Quit 上,然后按 Enter。

然而汝以为这样就结束了?还没格式化呢 (╯°Д°)╯︵/(.□ . )

创建文件系统+挂载

首先还是用 lsblk 确定一下分区的名称,为了以防万一记得加上 -f 参数:

root@archiso ~ # lsblk -f
NAME   FSTYPE   LABEL       UUID                                 MOUNTPOINT
sda
├─sda1 vfat               3C44-B4ED
├─sda2
├─sda3 ntfs               42E243C5E243BBC3
├─sda4 ntfs   新加卷      58741F29741F0A00
└─sda5
sdb
└─sdb1 vfat   ARCH_201610 EAC8-F012                            /run/archiso/bootmnt
sr0
loop0  squashfs                                                  /run/archiso/sfs/airootfs

第一排分别表示设备名称,文件系统类型,卷标,UUID和挂载点。

咱这里的话 sda1 那个 vfat 分区就是 EFI 系统分区啦,sda5 就是刚刚新建的分区啦~(因为还没格式化所以没有文件系统😂)

mkfs.ext4 把那个分区格式化成 ext4 文件系统咯~

记得自己看清楚是哪个分区别格式化错了 😂
root@archiso ~ # mkfs.ext4 /dev/sda5
mke2fs 1.43.3 (04-Sep-2016)
Creating filesystem with 27935234 4k blocks and 6987776 inodes
Filesystem UUID: a3943e57-6217-4a5f-8e57-ade5771315c0
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
    4096000, 7962624, 11239424, 20480000, 23887872

Allocating group tables: done
Writing inode tables: done
Creating journal (131072 blocks): done
Writing superblocks and filesystem accounting information: done

root@archiso ~ #

等一排文字闪过就格式化完了……

如果要格式化新的 EFI 系统分区的话,用 mkfs.vfat

如果要格式化新的 交换空间的话,用 mkswap

接下来用 mount 挂载分区啦~ (。>ω<)。

# mount <设备名称> <目标文件夹>
# /mnt 挺合适的
root@archiso ~ # mount /dev/sda5 /mnt
# 如果要挂载 EFI 系统分区的话,建议挂载到 /mnt/boot
# 所以先建立相应的文件夹
root@archiso ~ # mkdir /mnt/boot
root@archiso ~ # mount /dev/sda1 /mnt/boot
# 有交换空间的话不用挂载,用 swapon 命令。
root@archiso ~ # swapon /dev/sda6

选择软件仓库镜像

软件仓库(在Debian系发行版中,又叫做“软件源”)是软件包存储的地方。通常我们所说的软件仓库指在线软件仓库,亦即用户从互联网获取软件的地方。

用 nano 打开 /etc/pacman.d/mirrorlist

root@archiso ~ # nano /etc/pacman.d/mirrorlist


GNU nano 2.7.0                        File: /etc/pacman.d/mirrorlist

##
## Arch Linux repository mirrorlist
## Sorted by mirror score from mirror status page
## Generated on 2016-10-01
##

## Score: 0.2, France
Server = http://archlinux.polymorf.fr/$repo/os/$arch
## Score: 0.3, France
Server = http://arch.tamcore.eu/$repo/os/$arch
## Score: 0.3, Germany
Server = http://mirrors.cicku.me/archlinux/$repo/os/$arch
## Score: 0.3, Czech Republic
Server = http://ftp.sh.cvut.cz/arch/$repo/os/$arch
## Score: 0.3, Germany
Server = http://mirror.js-webcoding.de/pub/archlinux/$repo/os/$arch
## Score: 0.4, Netherlands
Server = http://ftp.nluug.nl/os/Linux/distr/archlinux/$repo/os/$arch
## Score: 0.4, Poland
                                        [ Read 517 lines ]
^G Get Help    ^O Write Out   ^W Where Is    ^K Cut Text    ^J Justify     ^C Cur Pos     ^Y Prev Page
^X Exit        ^R Read File   ^\ Replace     ^U Uncut Text  ^T To Spell    ^_ Go To Line  ^V Next Page

这是 GNU nano 的主界面,最简单的方法还是把下面那些 Mirrors 先全删掉然后输入一个新的, 用光标指向某一行以后同时按下 Ctrl+K 就好。然后自己输入一个 Mirror ,下面给出几个中国国内的 Mirror:

(所谓的 Ctrl+K 就是这两个键一起按😂)

# 网易
Server = http://mirrors.163.com/archlinux/$repo/os/$arch
# 清华大学 TUNA 协会
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
# 中国科学技术大学
Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
# 西安交通大学
Server = https://mirrors.xjtu.edu.cn/archlinux/$repo/os/$arch

输入完以后按下 Ctrl+O 写入,按 Enter 确定,再按 Ctrl+X 退出。

然后用 pacman -Syy 刷新一下软件包数据库。

root@archiso ~ # pacman -Syy
:: Synchronizing package databases...
core                                  120.9 KiB  4.92M/s 00:00 [##################################] 100%
extra                                1755.6 KiB  5.24M/s 00:00 [##################################] 100%
community                               3.7 MiB  6.82M/s 00:01 [##################################] 100%
root@archiso ~ #

安装基本系统

用 pacstrap 安装基本系统,默认会安装 base 组,要通过 AUR 或者 ABS 编译安装软件包,还需要安装 base-devel 啦:

评论里 farseerfc 提到装几个连接无线网络需要的软件包。(iw dialog wpa_supplicant wpa_actiond)
root@archiso ~ # pacstrap /mnt base base-devel iw dialog wpa_supplicant wpa_actiond

这个组并没有包含全部 live 环境中的程序,有些需要额外安装, packages.both 页面包含了它们的差异呗~

其他软件以后会用 pacman 再安装啦~

安装完以后大概会是这个样子 (´・ω・`)

pacstrap /mnt base base-devel   29.09s user 2.61s system 85% cpu 37.271 total

准备进入 chroot 环境

生成 fstab 啦 ~

# genfstab
usage: genfstab [options] root

Options:
    -L             Use labels for source identifiers (shortcut for -t LABEL)
    -p             Exclude pseudofs mounts (default behavior)
    -P             Include printing mounts
    -t TAG         Use TAG for source identifiers
    -U             Use UUIDs for source identifiers (shortcut for -t UUID)

    -h             Print this help message

genfstab generates output suitable for addition to an fstab file based on the
devices mounted under the mountpoint specified by the given root.
root@archiso ~ # genfstab -U /mnt >> /mnt/etc/fstab

然后向新系统出发~

root@archiso ~ # arch-chroot -help
usage: arch-chroot chroot-dir [command]

    -h                  Print this help message
    -u <user>[:group]   Specify non-root user and optional group to use

If 'command' is unspecified, arch-chroot will launch /bin/bash.
root@archiso ~ # arch-chroot /mnt /bin/bash

设置基本系统

# 开头只表示以 root 用户运行,汝不用把 # 输入到终端里啦~
  • 设置时区(中国的时区是 Asia/Shanghai)
# ln -s <源文件> <目标> 创建一个符号链接

# ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  • 设置时间标准 为 UTC,并调整 时间漂移:
# hwclock --systohc --utc
  • /etc/locale.gen 是一个仅包含注释文档的文本文件。指定您需要的本地化类型,去掉对应行前面的注释符号(#)就可以啦,还是用 nano 打开,建议选择帶UTF-8的項:
# nano /etc/locale.gen

en_US.UTF-8 UTF-8
zh_CN.UTF-8 UTF-8
zh_TW.UTF-8 UTF-8
  • 执行 locale-gen 以生成 locale 讯息:
# locale-gen
  • 创建 locale.conf 并提交您的本地化选项:

    将系统 locale 设置为en_US.UTF-8,系统的 Log 就会用英文显示,这样更容易问题的判断和处理。用户可以设置自己的 locale。

    警告: 不推荐在此设置任何中文locale,或导致tty乱码。

# echo 用来输出某些文字,后面的大于号表示把输出保存到某个文件里啦~

# 或者可以用文字编辑器新建这个文件加上这一行。

# echo LANG=en_US.UTF-8 > /etc/locale.conf
  • 设置一个喜欢的主机名(用汝的主机名代替 myhostname ):
# echo myhostname > /etc/hostname
  • 设置 root 的密码(输入密码的时候就是啥也没有 ╮( ̄▽ ̄)╭ ):
[root@archiso /]# passwd
New password:
Retype new password:
passwd: password updated successfully
  • 安装启动管理器(例如 GRUB ):

** UEFI 用户先再安装几个必要的软件包咯~

# pacman -S efibootmgr dosfstools

** 然后安装 GRUB

# pacman -S grub os-prober

** 把 GRUB 安装到硬盘:

# MBR 用户这么做 (记得用汝自己硬盘的名称代替 sda ,不要带上表示分区的数字啦~):

# grub-install --target=i386-pc /dev/sda --recheck

# UEFI 用户这么做:

# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub --recheck

EFI 安装成功以后大概像这样 😂

[root@archiso /]# grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub --recheck
Installing for x86_64-efi platform.
Installation finished. No error reported.

然后生成必要的配置文件:

[root@archiso /]# grub-mkconfig -o /boot/grub/grub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-linux
Found initrd image(s) in /boot: initramfs-linux.img
Found fallback initrd image(s) in /boot: initramfs-linux-fallback.img
WARNING: Failed to connect to lvmetad. Falling back to device scanning.
done

这个时候多半找不到 Windows 😂,可以试试把 Windows 分区挂载上试试,或者重启以后再生成一下……

安装桌面环境 (可能不一定需要)

  • 安装桌面环境需要的基础包 (就是 xorg 啦)
[root@archiso /]# pacman -S xorg
:: There are 80 members in group xorg:
:: Repository extra
1) xf86-input-evdev  2) xf86-input-joystick  3) xf86-input-keyboard  4) xf86-input-libinput
5) xf86-input-mouse  6) xf86-input-synaptics  7) xf86-input-vmmouse  8) xf86-input-void
9) xf86-video-amdgpu  10) xf86-video-ark  11) xf86-video-ati  12) xf86-video-dummy
13) xf86-video-fbdev  14) xf86-video-glint  15) xf86-video-i128  16) xf86-video-intel
17) xf86-video-mach64  18) xf86-video-neomagic  19) xf86-video-nouveau  20) xf86-video-nv
21) xf86-video-openchrome  22) xf86-video-r128  23) xf86-video-savage  24) xf86-video-siliconmotion
25) xf86-video-sis  26) xf86-video-tdfx  27) xf86-video-trident  28) xf86-video-vesa
29) xf86-video-vmware  30) xf86-video-voodoo  31) xorg-bdftopcf  32) xorg-docs  33) xorg-font-util
34) xorg-fonts-100dpi  35) xorg-fonts-75dpi  36) xorg-fonts-encodings  37) xorg-iceauth
38) xorg-luit  39) xorg-mkfontdir  40) xorg-mkfontscale  41) xorg-server  42) xorg-server-common
43) xorg-server-devel  44) xorg-server-xdmx  45) xorg-server-xephyr  46) xorg-server-xnest
47) xorg-server-xvfb  48) xorg-server-xwayland  49) xorg-sessreg  50) xorg-setxkbmap
51) xorg-smproxy  52) xorg-x11perf  53) xorg-xauth  54) xorg-xbacklight  55) xorg-xcmsdb
56) xorg-xcursorgen  57) xorg-xdpyinfo  58) xorg-xdriinfo  59) xorg-xev  60) xorg-xgamma
61) xorg-xhost  62) xorg-xinput  63) xorg-xkbcomp  64) xorg-xkbevd  65) xorg-xkbutils  66) xorg-xkill
67) xorg-xlsatoms  68) xorg-xlsclients  69) xorg-xmodmap  70) xorg-xpr  71) xorg-xprop
72) xorg-xrandr  73) xorg-xrdb  74) xorg-xrefresh  75) xorg-xset  76) xorg-xsetroot  77) xorg-xvinfo
78) xorg-xwd  79) xorg-xwininfo  80) xorg-xwud

Enter a selection (default=all):

这时会让汝选择需要哪些软件包啦,其实大多数时候默认的就行……

  • 接下来挑一个喜欢的桌面环境包组装上咯~

    (咱这里就只举例 GNOME KDE 和 xfce 啦,其他官方支持的桌面环境可以去 ArchWiki 查看)

    GNOME , 想要 GNOME 全家桶的话带上 gnome-extras

    # pacman -S gnome

    KDE Plasma , 想要 KDE 全家桶的话用 kde-applications 代替 kde-applications-meta, 还有中文翻译包:

    # pacman -S plasma kde-applications-meta sddm kde-l10n-zh_cn

    或者只安装 Dolphin (文件管理器),Kate(文字编辑器)和Konsole(终端模拟器)

    # pacman -S plasma dolphin kate konsole sddm kde-l10n-zh_cn

    xfce4,xfce 不带显示管理器,所以要装个其他的(例如 sddm )

    # pacman -S xfce4 xfce4-goodies sddm

    桌面环境大多数使用 NetworkManager :

    # pacman -S networkmanager

  • 然后安装中文字体( 同样 pacman -S 😋)

    Google Noto Fonts 系列: noto-fonts noto-fonts-cjk noto-fonts-emoji

    思源黑体:adobe-source-han-sans-otc-fonts

    文泉驿:wqy-microhei wqy-zenhei

更多的字体可以在 https://wiki.archlinux.org/index.php/Fonts_(简体中文) 找到。

  • 新建一个用户

    -m 为新用户创建一个文件夹,-s 设置用户的登录 Shell

    记得最后是用户名就好 😂

    # useradd -m -s /bin/bash horo

    然后设置密码

    # passwd horo

  • 激活需要的服务(显示管理器啦)

    # systemctl enable gdm

    or

    # systemctl enable sddm

    当然还有 NetworkManager:

    # systemctl enable NetworkManager

    (这个里面有大写😂)

  • 设置用户级别的 locale

    用 su 切换到刚建立的用户,然后编辑 ~/.config/locale.conf 修改自己的 Locale 。

    或者可以重启以后用桌面环境的设置程序改 😂

完工啦~

  • 离开 chroot 环境:

    # exit

  • 卸载挂载的分区,(其实不是必须的,因为马上就重启啦~)

    # umount -R /mnt

  • 重新启动,准备迎接新的系统吧 ~(>_<~)

by ホロ at July 29, 2017 04:00 PM