我在用 offlineimap。用着用着就发现一个问题:偶尔 offlineimap 会卡在网络上不动弹了。跟 getmail 一个德性……
但是 offlineimap 又跟 getmail 有点不一样,它是持续运行着的。虽然非要把之前那个 killhung 程序拿来用不是不可以,但我还是重新弄了一个更优雅的方案:systemd watchdog。
我的 offlineimap 本来就是用 systemd 服务的方式来跑的,所以很适合这样的改造呢。只是,当我瞅了一眼源码之后,我就放弃了 patch offlineimap 的打算。很难在合适的地方添加 watchdog 相关的代码。
既然从内部着手不好做,那就从外部写一个 wrapper 好了,反正 offlineimap 跟 getmail 不一样,正常情况下一直在输出东西,就把这个作为它的「心跳」特征好了。当然这个 wrapper 还可以给其它程序用。
于是,watchoutput 程序诞生了!稍微改一下 offlineimap 的 .service 文件,像这样子就好了:
[Unit] Description=Offlineimap Service [Service] Type=notify ExecStart=.../watchoutput /usr/bin/offlineimap TimeoutStopSec=3s SyslogIdentifier=offlineimap Restart=on-failure WatchdogSec=70 LimitCORE=0 [Install] WantedBy=default.target
加上LimitCORE=0
是为了阻止重启的时候由于 SIGABRT 信号导致 coredump,浪费磁盘空间。
用了几天之后,终于观察到一次由 watchdog 触发的重启:
12月 19 12:26:53 lilywork offlineimap[21623]: Establishing connection to imap.exmail.qq.com:993 (main-remote) 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Watchdog timeout (limit 1min 10s)! 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Killing process 21623 (python3) with signal SIGABRT. 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Killing process 21625 (offlineimap) with signal SIGABRT. 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Main process exited, code=dumped, status=6/ABRT 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Unit entered failed state. 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Failed with result 'core-dump'. 12月 19 12:28:03 lilywork systemd[687]: offlineimap.service: Service hold-off time over, scheduling restart. 12月 19 12:28:03 lilywork systemd[687]: Stopped Offlineimap Service. 12月 19 12:28:03 lilywork systemd[687]: Starting Offlineimap Service... 12月 19 12:28:04 lilywork systemd[687]: Started Offlineimap Service.
没过几天,我又给这个 watchoutput 的脚本找到另外的用处:自动重启网络。
我家里的笔记本连 Wi-Fi 不知怎么,这些天经常会卡住(只发不收,一直处于 ARP 找网关的状态)。内核之前报过一次错,现在也没反应了。
于是:
[Unit] Description=Watch for network availability [Service] Type=notify ExecStart=.../watchoutput --retry-on-exit 2 --wait-before-retry 30 --ignore-stderr \ -- ping -i 30 192.168.1.1 Restart=on-failure WatchdogSec=70 StandardOutput=null StandardError=journal LimitCORE=0 SyslogIdentifier=watch-network [Install] WantedBy=default.target
拿 watchoutput 监控 ping 网关的输出,每30秒 ping 一次,如果70秒还没反应就重启它自己。然后我们还需要重新连接网络。在 /etc/systemd/system 下建立 netctl-auto@wlan0.service.d 目录,并在其下建立一个 watchdog.conf 文件,给 netctl-auto@wlan0.service 服务增加一项配置:
[Unit] PartOf=watch-network.service
这样当 watch-network.service 重启的时候,netctl-auto@wlan0.service 就会自动重启了~