The Man Who Fell From The Wrong Side Of Sky:最新 5 日分

2019/11/11(Mon)

[オレオレN6] 続・チラシの裏障害対応

先日のトラブル対応、そっちにちょいと追記しておいたけど、さくらのサポートは直近でメンテも障害も確認しておりませんゆう回答がきた。

前回の再起動時のdmesgは

Jul 22 02:15:54 tk2-219-19156 /netbsd: NetBSD 6.1_STABLE (GENERIC) #1: Thu Mar  1 11:15:04 JST 2018
Jul 22 02:15:54 tk2-219-19156 /netbsd:  root@tk2-219-19156.vs.sakura.ne.jp:/usr/obj.amd64/sys/arch/amd64/compile/GENERIC
Jul 22 02:15:54 tk2-219-19156 /netbsd: total memory = 1023 MB
Jul 22 02:15:54 tk2-219-19156 /netbsd: avail memory = 978 MB
Jul 22 02:15:54 tk2-219-19156 /netbsd: timecounter: Timecounters tick every 10.000 msec
Jul 22 02:15:54 tk2-219-19156 /netbsd: timecounter: Timecounter "i8254" frequency 1193182 Hz quality 100
Jul 22 02:15:54 tk2-219-19156 /netbsd: Red Hat KVM (RHEL 7.0.0 PC (i440FX + PIIX, 1996))
Jul 22 02:15:54 tk2-219-19156 /netbsd: mainbus0 (root)
Jul 22 02:15:54 tk2-219-19156 /netbsd: cpu0 at mainbus0 apid 0: Intel Core Processor (Broadwell), id 0x306d2
Jul 22 02:15:54 tk2-219-19156 /netbsd: cpu1 at mainbus0 apid 1: Intel Core Processor (Broadwell), id 0x306d2

そんで今回のdmesgだと

Nov  5 23:56:40 tk2-219-19156 /netbsd: NetBSD 8.1 (GENERIC) #0: Fri May 31 08:43:59 UTC 2019
Nov  5 23:56:40 tk2-219-19156 /netbsd:  mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/amd64/compile/GENERIC
...
Nov  5 23:56:40 tk2-219-19156 /netbsd: Red Hat KVM (RHEL 7.0.0 PC (i440FX + PIIX, 1996))
Nov  5 23:56:40 tk2-219-19156 /netbsd: mainbus0 (root)
...
Nov  5 23:56:40 tk2-219-19156 /netbsd: cpu0 at mainbus0 apid 0
Nov  5 23:56:40 tk2-219-19156 /netbsd: cpu0: Intel Core Processor (Broadwell), id 0x306d2
Nov  5 23:56:40 tk2-219-19156 /netbsd: cpu0: package 0, core 0, smt 0
Nov  5 23:56:40 tk2-219-19156 /netbsd: cpu1 at mainbus0 apid 1
Nov  5 23:56:40 tk2-219-19156 /netbsd: cpu1: Intel Core Processor (Broadwell), id 0x306d2
Nov  5 23:56:40 tk2-219-19156 /netbsd: cpu1: package 1, core 0, smt 0

ちゅーことで変わってないのでマジっぽい、じゃあなんで今認識しないのやら。

でも障害が起きたら事前通告なくフェールオーバーは発生する方が当たり前だし、ライブマイグレーションじゃなきゃ仮想マシンの移動先はまったく同一構成であることすら保証されんちゅーことまで留意して運用せなならんのは確かやな。 でも対応策としてKVMのバージョンに依存しないようVirtIO無効にしただけでは不十分で、OSが存じ上げないCPUだった場合に発生する障害には対応のしようがねえよな、ほら最近も systemdがAMD Zen2のRDRAND命令が続けて-1返すので起動しねえとかあったわけでな。 ということで止まると困るなら自分でちゃんと冗長化しておけってことだ罠。

そんで仮復旧にカーネルだけ8.1に入れ替えた後、syslogに

Nov 10 23:45:56 tk2-219-19156 /netbsd: in_cksum: out of data

が大量出力されとるのと、IPv6でのNTPの時刻同期に失敗してるのにたった今気づいたのだが、以前 pf(4)のバグ踏んだ件のうちv6パケットをv4としてチェックサムとって死ぬ件のfixがTNF本家だともう干支一回りするくらいにメンテ滞ってるので取り込まれてない件やなこれ。

この差分はオレオレN6にはマージしてあったんだけど、結局そもそもの発端である IPv6で自分自身に接続しようとすると謎ブロックされる現象までの解決には至っていないので困ったもんである、風の噂だとN10でpf(4)ごと消滅の予定だそうだから直るはずもねえかこれ。

[SCM] gitに欲しい新機能

ゲーム全くやらん人なのでネットの評判ちら見しただけなんだけどDeath Strandingってゲームのいいねシステムおもろいね、オープンソース界もgit favoriteとか実装してcommit単位で *1いいねつけられると少しは燃え尽きた作業者も息吹き返すのかなぁと思った、つかgit favoriteでなくgit thanksとかかな。 顔本ヒウイッヒーあたりのいいねは承認欲求丸出しのはた迷惑な妄言奇行に注ぐガソリンで世の害悪なんだけど、実際に手を動かした行為にありがとうを送れるのはプロジェクトも円滑になる。 ということで水にもありがとう以下略

ちなみにgit prise(blame)という機能は既にあるけどこれはエンバグさせた犯人を捜し出す責任追及機能なのでちょっと違う、またお前か(クソデカ溜息)。

でもgitの主要開発者がLinusだから実装されるとしたら👍(U+1F44D Thumbs Up Sign)でなく🖕(U+1F595 Reversed Hand With Middle Finger Extended)の方か、commitしたらLinusから即Fuck飛んでくるのはそれはそれで楽しそうではある(ぉ

*1:githubのプロジェクト単位にいいねとか芝生とか意味ねえしな。

2019/11/5(Tue)

[チラシの裏] オレオレN6とKVM VirtIOで障害

(追記) サポートから直近で障害メンテは無かったという回答ががが、じゃあなんでVirtIO死んだのやら…

さくらVPSで事前予告なしのKVMライブマイグレーションが発生したようなのだが、困ったことに移行先のKVMのバージョンが違うのかN6のvirtioドライバが対応してないようでvioif(4)が生きたまま死んどってネットワーク反応無しで警告が飛んできた。

ワイも最近頭ぶっ壊れつつあるので不用意にもとりあえずビールならぬ再起動(脳死)を実行してしまい、カーネルがld(4)認識できずに起動不能に陥って更に事態悪化という痛恨のミス、ログすら回収してねえ!

この時点でVPSコンソールで起動/シャットダウン/コンソールと操作がなんもできん状態になってしもうたのでKVMホスト側の障害かと、サポートも連休中は休みなのでメールだけ投げて放置。

とりあえずDNSいじってwwwのCNAMEを昔取得した無料ホームページサービスに飛ばすかーとも考えたんだけど、nginxにHSTS設定してたので証明書用意しhttpsの設定せんとブラウザエラーになるから手間なので止めた、どうせ誰も読んでねえわこんなチラシの裏。

そんで本日ようやっとサポートから返答あって

とのこと。 あれ頭に「ストレージが初期化されます」とあるので既存のディスクイメージ破棄されるのかと思って躊躇してたんだが、ISOインストールを選択した場合はストレージの内容は保持されるのね。

ということでN8.1のinstall CDで起動しkernel-GENERIC.tgzとmodules.tgzだけ手動展開し再起動かけて仮復旧。 そのうちオレオレN6のソースツリーにvirtioドライバの変更を反映したいところである。

つーか今後も同じようなトラブル起きる可能性あるわけで、コンサバ(スズキ目サバ科サバ属)にVirtIO無効でwm(4)とwd(4)使う方がええかもね…

2019/10/23(Wed)

[オレオレN6] 続・pkg_add(1) won't work with libarchive 3.4.0

前回は原因をarchive_write_data_blockと断定してしまったけど、ワイのgrep(1)ミスであり同じエラーメッセージを表示するarchive_write_header側の問題であった、メンゴメンゴ(死語)。

    683 static int
    684 extract_files(struct pkg_task *pkg)
    685 {
...
    779                 r = archive_write_header(writer, pkg->entry);
    780                 if (r != ARCHIVE_OK) {
    781                         warnx("Failed to write %s for %s: %s",
    782                             archive_entry_pathname(pkg->entry),
    783                             pkg->pkgname,
    784                             archive_error_string(writer));
    785                         goto out;
    786                 }

archive_write_data_blockは3.3.3→3.4.0でコードに変更はないので変だなと思って再度コード検索し直して気づいた。

最初はarchive_write_data_blockには ドキュメントと実装の相違があって、バイナリ互換性壊すし将来リリースで直すわという結論になっとるのでその関係かなと思ったんだけど無駄な時間だったね(およそ5分)。

ちなみにその部分の実装を軽く読んでみると

[libarchive/archive.h]
    191 #define ARCHIVE_EOF       1     /* Found end of archive. */
    192 #define ARCHIVE_OK        0     /* Operation was successful. */
    193 #define ARCHIVE_RETRY   (-10)   /* Retry might succeed. */
    194 #define ARCHIVE_WARN    (-20)   /* Partial success. */
    195 /* For example, if write_header "fails", then you can't push data. */
    196 #define ARCHIVE_FAILED  (-25)   /* Current operation cannot complete. */
    197 /* But if write_header is "fatal," then this archive is dead and useless. */
    198 #define ARCHIVE_FATAL   (-30)   /* No more operations are possible. */

[libarchive/archive_write_disk_posix.c]
   1545 static ssize_t
   1546 _archive_write_disk_data_block(struct archive *_a,
   1547     const void *buff, size_t size, int64_t offset)
   1548 {
   1549         struct archive_write_disk *a = (struct archive_write_disk *)_a;
   1550         ssize_t r;
   1551
   1552         archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
   1553             ARCHIVE_STATE_DATA, "archive_write_data_block");
   1554
   1555         a->offset = offset;
   1556         if (a->todo & TODO_HFS_COMPRESSION)
   1557                 r = hfs_write_data_block(a, buff, size);
   1558         else
   1559                 r = write_data_block(a, buff, size);
   1560         if (r < ARCHIVE_OK)
   1561                 return (r);
   1562         if ((size_t)r < size) {
   1563                 archive_set_error(&a->archive, 0,
   1564                     "Too much data: Truncating file at %ju bytes",
   1565                     (uintmax_t)a->filesize);
   1566                 return (ARCHIVE_WARN);
   1567         }
   1568 #if ARCHIVE_VERSION_NUMBER < 3999000
   1569         return (ARCHIVE_OK);
   1570 #else
   1571         return (size);
   1572 #endif
   1573 }

と将来的に修正する準備としてARCHIVE_VERSION_NUMBERでifdefしとる、でもこれ設計的にはこれプロトタイプ変更して

ようにすべきよね、まぁ人は愚かなので滅びるまで設計ミスは無くならんしこれ以上つっこまんとこ。

ということで仕切り直してarchive_write_headerの3.3.3→3.4.0への変更点をざっと眺めてみる。

--- libarchive.orig/files/libarchive/archive_write_disk_posix.c  2019-04-10 17:24:05.000000000 +0900
+++ libarchive/files/libarchive/archive_write_disk_posix.c      2019-10-04 23:49:49.000000000 +0900
@@ -165,6 +165,10 @@
 #define O_NOFOLLOW 0
 #endif

+#ifndef AT_FDCWD
+#define AT_FDCWD -100
+#endif
+
 struct fixup_entry {
        struct fixup_entry      *next;
        struct archive_acl       acl;
@@ -348,6 +352,8 @@

 #define HFS_BLOCKS(s)  ((s) >> 12)

+
+static int     la_opendirat(int, const char *);
 static void    fsobj_error(int *, struct archive_string *, int, const char *,
                    const char *);
 static int     check_symlinks_fsobj(char *, int *, struct archive_string *,
@@ -401,6 +407,37 @@
                    size_t, int64_t);

 static int
+la_opendirat(int fd, const char *path) {
+       const int flags = O_CLOEXEC
+#if defined(O_BINARY)
+           | O_BINARY
+#endif
+#if defined(O_DIRECTORY)
+           | O_DIRECTORY
+#endif
+#if defined(O_PATH)
+           | O_PATH
+#elif defined(O_SEARCH)
+           | O_SEARCH
+#elif defined(O_EXEC)
+           | O_EXEC
+#else
+           | O_RDONLY
+#endif
+           ;
+
+#if !defined(HAVE_OPENAT)
+       if (fd != AT_FDCWD) {
+               errno = ENOTSUP;
+               return (-1);
+       } else
+               return (open(fd, path, flags));
+#else
+       return (openat(fd, path, flags));
+#endif
+}
+
+static int

はい、POSIX:2008で新規に追加されたopenat(2)のラッパーが追加されとるわね。

このopenat(2)ってsyscallは以前にも 解説したTickTokなのかTOCTTTTTTTTOOOOOUUUUUUなのか毎回綴りを思い出せないファイルシステムの競合状態に関する脆弱性への対策として導入されたもの。 この問題は古くより存在するopen(2)が相対パスを処理する際、起点となる現在の作業ディレクトリはプロセス単位でグローバルだからスレッドセーフではないことで起きる。 なので明示的に起点をファイルディスクリプタとして引数に渡すもの。

もしこのAPIはじめて知ったなんて人おったら、もう追加されて干支一回りしとるので勉強してどうぞ( プレゼンでかるく触れた記憶があるぞ)。

ともかくN6の時点ではこのopenat(2)は未実装だったので、ラッパー中のエミュレーションコードが使われ結果としてバグっとるちゅーことなんやな。

なのでN6をとっくに切り捨てた現在のpkgsrcでは問題は起きないという事、もちろんマルチプラットホームを標榜するpkgsrcのサポートするOSの中でopenat(2)がまだ未実装のものものもあるだろうし、そっちでは当然発生すると思われる。 まあでもそんな環境でpkgsrc使う人なぞ世界で3人くらいしかおらんだろうから見過ごされとるんだろう。

んで再度さっきの差分を読むと

という破綻間違いなしの実装になっとるので、ここで引っかかっとるんだろうなと想像がつく。

ということでlibarchive 3.4.0に対してすべき作業は、完全なopenat(2)エミュレーションとして

を実装するかなんだけど、そもそもlibarchiveのAPIでこのエミュレーションを呼びだす箇所は

   1948         /* Try to record our starting dir. */
   1949         a->restore_pwd = la_opendirat(AT_FDCWD, ".");
...
   2642         chdir_fd = la_opendirat(AT_FDCWD, ".");
   2643         __archive_ensure_cloexec_flag(chdir_fd);
...
   2705                 } else if (S_ISDIR(st.st_mode)) {
   2706                         if (!last) {
   2707 #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
   2708                                 fd = la_opendirat(chdir_fd, head);
   2709                                 if (fd < 0)
   2710                                         r = -1;
   2711                                 else {
   2712                                         r = 0;
   2713                                         close(chdir_fd);
   2714                                         chdir_fd = fd;
   2715                                 }
   2716 #else
   2717                                 r = chdir(head);
   2718 #endif
...
   2811 #if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
   2812                                         fd = la_opendirat(chdir_fd, head);
   2813                                         if (fd < 0)
   2814                                                 r = -1;
   2815                                         else {
   2816                                                 r = 0;
   2817                                                 close(chdir_fd);
   2818                                                 chdir_fd = fd;
   2819                                         }
   2820 #else
   2821                                         r = chdir(head);
   2822 #endif

のように、HAVE_OPENATが定義されてない限りはAT_FDCWDつきで呼ばれとるんだよね…なので完全なエミュレーションは不要なはずなんだけど。

ということでもうちょっとコードを読み進めないとならんのだけど、いつ認知症老人による失火で焼け死んでも不思議でない明日は無い状態なので次回は未定。

2019/10/20(Sun)

[オレオレN6] libarchive 3.3.3 → 3.4.0へのアップグレードでbootstrapから入れたpkg_installが無事死亡

丸一日チラシの裏死んでおったけどようやく復旧、今回もpkg_rolling-replace走らせたら途中で止まっとって壊滅ちゅーパターンなので、そろそろチラシの裏も続けるなら他のOSに変更したいところではある。

そんでログをみるとpkg_install/add/perform.cの

    665                 r = archive_write_data_block(writer, buff, size, offset);
    666                 if (r != ARCHIVE_OK) {
    667                         warnx("Write error for %s: %s", filename,
    668                             archive_error_string(writer));
    669                         return -1;
    670                 }

のメッセージが出力されとったので、pkgsrc/packages/All以下に作成されたバイナリパッケージをpkg_add(1)しようとして死んどるもよう。

ので犯人はlibarchiveと断定、いったん3.3.3に巻き戻しあらためてbootstrapからやりなおした。

どうもうち以外では問題になっとらんようなので、N7以降では顕在化しないバグなんかなぁこれ。

2019/10/19(Sat)

[Windows] 死を招く古いグラフィックドライバとWindows 10(19H1以降)でのリモートデスクトップ

最近のWindows 10(19H1以降)、RDPでの接続に時間がかかったり接続エラーになったりブラックアウトやログインでのフリーズそしてとどめにBSoDからの再起動という現象がでて困っとったんだけども、どうやらグラボの問題なんやな。

例えばAMDはRadeon 4000以前のグラボはWindows 10サポートは打ち切ったぜと 公式リリース出しとるようで、自分とこで不具合が発生する環境は正にこいつRadeon HD 4200なのであった、合掌。

要はRDPがハードウェアアクセラレーションでドライバの非互換性(DirectX?)を踏んでクラッシュしとるちゅーこと、なお問題のマシン(HP Microserver N54L)に対応グラボを増設する余地は無し、無念。

しゃーないので回避策としてハードウェアアクセラレーションを無効にするためレジストリに

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Avalon.Graphics]
"DisableHWAcceleration"=dword:00000001

を追加して再起動、とりあえずこれだけでRDPは安定して使えるようになるもよう。