7
16
2018
1

使用 iptables 透明代理 TCP 与 UDP

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

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

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

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

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

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

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

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

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

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

iptables -t nat -A PREROUTING -p tcp -s 192.168.57.0/24 ! -d 192.168.57.0/24 -j DNAT --to-destination 127.0.0.1:$PPROT
iptables -t mangle -A PREROUTING -p udp -s 192.168.57.0/24 ! -d 192.168.57.0/24 -j TPROXY --on-port $PPROT --on-ip 127.0.0.1 --tproxy-mark 1/1
Category: 网络 | Tags: linux 网络 iptables UDP
7
11
2018
8

Linux 下获取文件的创建时间

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

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

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

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

gcc 编译一下,还真好用:

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

使用 VirtualBox 启动本地磁盘上的其它系统

VBox 可以从一个指向本地硬盘的 vmdk 文件启动虚拟机。

首先,为了避免使用 root 运行 VBox,我们需要给自己访问磁盘的权限。我即将启动的是位于 sda5 上的 openSUSE。它使用 UEFI 启动,所以 UEFI 分区的权限也是需要的。创建 vmdk 文件的时候需要读取分区表,因此,还需要 sda 的权限:

sudo setfacl -m u:$USER:rw /dev/sda{,1,5}

然后我们创建 vmdk 文件。使用-partitions 1,5选项的话,只有这两个分区能在虚拟机里访问,别的分区读的时候是全零,写入操作会被忽略。-relative选择使用分区设备名(sda1、sda5),这样创建好之后 VBox 不再需要对整块硬盘 sda 的权限了。另外会附带创建一个名字以 -pt.vmdk 结尾的文件。它是单独的分区表。如果是 MBR 启动的话,是可以直接在虚拟机系统里更新引导器的,不影响外边的系统。不过我这次是使用 UEFI 启动,所以用不上了。

VBoxManage internalcommands createrawvmdk -filename hostdisk.vmdk -rawdisk /dev/sda -partitions 1,5 -relative

创建好之后就可以撤销对 sda 的权限了:

sudo setfacl -b /dev/sda

然后去 VirtualBox 界面那边创建新虚拟机,并「启用 EFI」。另外,可以在存储设置里,把「控制器: SATA」的「使用主机输入输出 (I/O) 缓存」启用,似乎这样 I/O 会快一点。

VBox 的 EFI 并不像电脑的那样,按 F12 可以选择启动项。因此,它会启动默认的那个,也就是 /EFI/Boot/bootx64.efi。如果你想启动的系统不是这个的话,就把它的 efi 文件复制过来覆盖它。比如我是这么做的:

cd /boot/EFI/Boot
sudo cp ../opensuse/grubx64.efi bootx64.efi

如果是 Windows 10 并使用 MBR 启动的话,可以在虚拟机里用如下命令更新 MBR,干掉原来用于多启动的 grub:

bootsect /nt60 c: /mbr

做好之后就可以启动啦~

对于设备的权限设置,重启之后会丢失的。需要的时候再加上好了。

PS: openSUSE 自带了 VBox 的驱动啊,不过剪贴板共享不能用,大概只有显示驱动没带上服务。

PPS: 启动没一会儿就通知我更新出现错误,一看软件源设置,果然是 HTTP 的,被垃圾鹏博士劫持了。

Category: Linux | Tags: 虚拟机 vbox linux
2
12
2018
14

大上 Paperlike HD 电子墨水屏开箱体验

刚听说就已心动,无奈当时并不支持 Linux。后来由于一些事情耽搁到现在,终于到手了~

开箱啦

显示器整体尺寸是31cm×27cm,其中显示屏尺寸不到26cm×21cm(请注意:手工测量有误差)。分辨率是2200x1650。也就是200dpi多,和我的 Kindle Paperwhite 一代差不多的。

显示器下方是两个纸盒,里边是Y形线(照片中没有)、简易支架(那根根子)、螺丝和仅几百M的驱动U盘。

附件

Y形线是附加了USB电源线的HDMI线。信号走HDMI,电源走USB,所以需要额外占用一个USB口。当然接充电器上也是可以的,只是在电脑边接个充电器更不方便。

工作时,USB端口的电流为30~50mA左右,并不像说明书上说的需要2A那么多,挺省电的。对比之下,我的罗技鼠标接收器也需要15mA左右的电流呢。

线都接上之后,屏幕会闪几下,然后就可以用了。只是每隔几分钟会弹出提示信息,要求安装并运行所谓的「驱动」。下图是默认的 floyd 模式:

未使用软件,显示的登录界面

为了更多的模式,以及最重要的,别显示那个提示信息,需要安装并运行大上提供的软件。

软件是为 Ubuntu 提供的,但是 Arch 下也可以使用。下载并解包,将得到的三个文件「PaperLikeHD」、「ResChange」、「DS.ico」放到/usr/local/sbin/下,然后sudo PaperLikeHD 运行,会弹出一个包含一列按钮的窗口。不给 root 权限是不行的,会什么也不做就退出。

但是这样还不够。我使用的时候它总是报告找不到显示器。客服不肯告诉我它到底使用什么机制检测的,所以我 strace 又反汇编看了一下,最后发现它在找 /dev/i2c-* 设备文件,而我的系统上没有它们。看了一下一堆以 i2c 开头的内核模块,最后发现只要加载 i2c-dev 模块就好了。大上的软件需要一些时间来检测,等一会儿它就会在终端打开出「setting...50」这样的文字,这时就好了。

在台灯下使用火狐发推

过程中我还专门启动到 Ubuntu live 系统里测试来着。后来才知道原来是自己的手速太快,软件还没检测到,其实等等就好 (╯‵□′)╯︵┻━┻

Ubuntu live

从图片中可以看到,floyd 模式的显示很粗糙。这是因为它实际上只有二阶灰阶,通过不同密度的点来近似不同的灰度。虽然显示有很严重的颗粒感,但是它响应飞快,常规打字操作时几乎感觉不到延迟,很适合写作和阅读文本。而真正的二阶灰阶模式 A2,只有黑白两色,虽然依旧反应快速,但是因为非黑即白,只能用来读纯文本,任何阴影或者灰色的字都不好处理。

16灰阶的 A16 模式,显示清晰、层次丰富,但是响应速度非常慢。也不是非常啦,除了屏幕大小外,无论显示效果还是响应速度都跟我的 Kindle 一样的。而与 Kindle 不同的是,它会在更新时将更新区域变黑再显示,造成闪烁。在此模式下使用鼠标非常非常费力,任何非静止的元素(比如火狐载入中的动画、时钟、光标移动)都挺分散注意力的。

以上都是所谓的「可变分辨率模式」。另外有个「固定1100x825模式」,也就是使用一半的分辨率。至于另一半去了哪里,看看它所支持的模式就能猜到了:

  • A5,也就是五灰阶。这是用四个墨滴来显示一个像素,每个墨滴只有黑与白两种状态。
  • A61,所谓的61灰阶。也是用四个墨滴来显示一个像素。不过每个墨滴竭尽所能有16种状态,也不知道大上是怎么组合出这61灰阶的。

也不知道大上为什么把这两种墨滴用法叫作「可变分辨率模式」和「固定1100x825模式」。哪里可变了,又哪里固定了呢?这两种模式切换时,会调用「ResChange」程序,它会影响当前的显示器布局,需要重新使用 xrandr 进行设置。

以下是各种模式显示这个灰阶测试网页的照片:

A2 模式:

A2 模式

A16 模式:

A16 模式

floyd 模式:

floyd 模式

A5 模式:

A5 模式

A61 模式:

A61 模式

半分辨率下,清晰度做出了很大的牺牲。以下是 A16 和 A61 模式显示 PaperLikeHD 软件自身界面的效果对比:

A16 A61

在 A2、A5、floyd 模式下,可以调节对比度,也就是墨滴到底显示为黑还是为白的值,以在显示不同的页面时都能将文本与背景良好区分开。

和 Kindle Paperwhite 不同的是,它没有背光。在晚上的时候,屏幕偏暗,附加一个台灯光源比较好。不过它的屏幕也不像 Kindle 那样偏黄。

这篇文章最终定稿,就是在 PaperLikeHD 上完成的,使用的是 floyd 模式。我个人觉得半分辨率的几个模式很鸡肋。目前觉得,写作用 floyd 模式,文本阅读使用 A2 模式,网页阅读使用 A16 模式,这样最好了~

floyd 模式还有个问题:在显示某些图片时(比如我的 Awesome 桌面壁纸),那些墨滴颗粒会不断地抖动,就像风在吹沙粒一样……

最后,这里是大上的官网链接

Category: 硬件 | Tags: linux 硬件 E-ink 显示器
2
10
2018
3

加固 systemd 服务

最近学 wzyboy 搭了一套 collectd + Graphite + Grafana 监控。collectd 和 Grafana 都比较好搞,Arch 官方源里有。但是 Graphite 就没有了。

我没有使用 Python 2 版、带 Web 前端的 Graphite 包,而是使用 graphite-api 提供 Web API,python2-carbon 存储数据。它们在 AUR 上有,其中 python2-carbon 是相当危险的(现在已经改了)。

为什么危险呢?

首先,最明显的,carbon 服务以 root 用户运行。它本身没有任何使用 root 权限的必要,所以专门创建一个 carbon 用户更好。

其次,它运行起来之后,我发现是监听 0.0.0.0 的。这个也无必要:我的 collectd 就在本地呢。

最后,也是最吓人的:它默认开启了接收 pickle 数据的端口。Python pickle 模块的文档一打开,就能看到红色的警告,告诉人们不能接收不信任来源的 pickle 数据。而我曾经工作过的公司也发生过通过 pickle 注入代码的事情:攻击者发现了一个对外网开放的 Redis 服务,刚好那个 Redis 是给 Celery 用的。攻击者于是往里边写了条自己构造的 pickle,在解析时调用 curl 命令向其服务器报告IP、端口和当前UNIX用户的信息。

这接口,开在外网,就是远程代码招行;开在本地,就是本地提权。很危险的。

为了防止各种漏洞被利用,一个未雨绸缪的办法就是:权限最小化。本来这是件比较麻烦的事情,好在 systemd 提供了许多现成的配置项,使得给 carbon 这种服务加固简单易行。

首先创建用户,写一个 sysusers 文件就可以了:

u carbon - "carbon service" /var/lib/carbon

然后,它需要使用文件系统的某些部分。那么别的就用不着访问了,比如 /home。而 /dev、/tmp 这些,自己用自己的就好。连 / 也不让写,也不允许获取任何新特权了。其实使用 carbon 用户它本来就写不了 / 也没有任何特权了,但以防万一嘛,要是哪里来个本地提权漏洞呢?

[Unit]
Description=Graphite/Carbon
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/carbon-cache.py --config=/etc/carbon/carbon.conf start
User=carbon

PrivateTmp=true
PrivateDevices=true
ProtectSystem=full
ProtectHome=true
NoNewPrivileges=true
CapabilityBoundingSet=

ReadOnlyPaths=/
ReadWritePaths=/run
ReadWritePaths=/var/log/carbon
ReadWritePaths=/var/lib/carbon

[Install]
WantedBy=multi-user.target

限制文件系统的访问,systemd 配置起来很方便,我打包的时候喜欢尽量加上。

完整的 python2-carbon 服务配置和打包脚本在这里

以及,这里是 Arch Linux 中文社区的编译机的 Grafana

Category: Linux | Tags: Arch Linux systemd linux 安全
12
10
2017
11

在 Linux 下设置录音笔的时间

咱买了一个录音笔,效果比使用笔记本话筒录音好多了还省电。当然啦,我也曾试过使用手机录音,结果是,没能录多久就中断了(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()
Category: Linux | Tags: Linux USB 硬件 SCSI
10
10
2017
28

WireGuard: 简单好用的 VPN

家里和公司电脑连接,因为厌倦了一个个做端口映射,有些还因为安全原因得走 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 的双向漫游很棒啊~

Category: 网络 | Tags: linux 网络 UDP
9
28
2017
8

To hup or not to hup

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

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

Category: Linux | Tags: nodejs linux bash shell
9
11
2017
3

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

最近公司弄了 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 包。

8
11
2017
4

谁又用掉了我的磁盘空间?——魔改 ncdu 来对比文件树大小变化

磁盘空间不够用了,或者只是洁癖发作想清理了,可以用 ncdu 来查看到底是什么文件占用了磁盘。ncdu 基于 ncurses,对比 du,更方便交互使用,对比 baobab 这类的 GUI 的工具,ncdu 可以在服务器、Android、树莓派、路由器等没有或者不方便有图形界面的地方跑。

但是呢,我现在有很多很多不同时间的备份,我想知道,是什么东西突然用掉了我好几百兆的空间?我是不是需要把它排除在备份之外?

所以呢,我需要一个支持对比的工具。

本来我是打算什么时候有空了自己写一个的,然而我注意到 ncdu 可以把大小信息保存在文件里。其实我只要对比两个 ncdu 产生的文件,然后照着输出一个差异文件就可以了嘛。不用自己遍历文件树,不用自己做界面,多棒!而且也不一定要像我这样有不同时间的备份才有用。可以定时跑一跑 ncdu,把导出的文件保存起来,将来随时取用。

于是有了 ncdu-diff 脚本

然而事情总是不那么顺利。输出文件拿给 ncdu 加载的时候,ncdu 报错了——它不支持负数。我给它加了支持,然后再加载,BOOM!ncdu 挂掉了……有符号整型和无符号整型的事情,还有格式化输出的事情……总之花了一天,它终于不崩溃了。补丁也放在同一仓库了。

ncdu

从上图可以看出,Android 的 app 越更新越大……以及深入之后可以发现,微信的动画表情占了我好多好多的空间,我去删掉它们……

给 Arch Linux x86_64 现成的包:下载, 签名

Category: Linux | Tags: ncurses linux python

部分静态文件存储由又拍云存储提供。 | Theme: Aeros 2.0 by TheBuckmaker.com