オフライン状態においてWSLでemacsやsudoが起動中に固まる(解決済)
結論
WSLを使うときは次のように/etc/hostsにマシンのホスト名を書くべし。
127.0.0.1 localhost puffy # puffyは私のwindowsマシン名
そうしないとemacs, sudoなどの、実行時にホスト名を解決しようとするソフトが固まる
問題の認識
数日前に買ったノートPCでWSLをしばらく快適に使っていたのですが、emacsが起動中に固まって動かなくなる問題がさきほど発生しました。その後sudoにも同じ問題が起きることがわかりました。つい数十分前には正しく使えており、かつ、その後Windows Updateはしていないし、設定変更もしていないので、これはおかしいです。
トラブルシューティング
最後に問題が発生していなかった時点(今朝9時ごろ)と問題発生を確認した時点(9時30分ごろ)の差分をしばらく考えてみたところ、前者はオンライン環境であり、後者はオフライン環境だったことを思い出しました。ネットワーク環境に関係あるのかを確認するために、オンラインにしてから同じ操作をすると、問題は発生しませんでした。ここで一つ「ネットワーク関係の疑いが強い」という切り分けに成功。
デバッグのためにstrace -o emacs.log emacsを実行すると、次のように/etc/resolv.confをopen(2)したり、uname(2)コマンドでホスト名を取ったりした後に発行した複数回のsenddmsg()にそれぞれ10秒前後時間がかかっていることがわかりました。
emacs.log
... 1492485708.123190 stat("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=189, ...}) = 0 1492485708.123497 open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 3 1492485708.123952 fstat(3, {st_mode=S_IFREG|0644, st_size=189, ...}) = 0 1492485708.124115 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa4fe940000 1492485708.124295 read(3, "# This file was automatically ge"..., 4096) = 189 1492485708.124478 read(3, "", 4096) = 0 1492485708.124648 close(3) = 0 1492485708.124820 munmap(0x7fa4fe940000, 4096) = 0 1492485708.124986 uname({sys="Linux", node="puffy", ...}) = 0 1492485708.125249 socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 3 1492485708.125475 connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.43.1")}, 16) = 0 1492485708.125710 gettimeofday({1492485708, 125750}, NULL) = 0 1492485708.125859 poll([{fd=3, events=POLLOUT}], 1, 0) = 1 ([{fd=3, revents=POLLOUT}]) 1492485708.126111 sendmmsg(3, {{{msg_name(0)=NULL, msg_iov(1)=[{"t\246\1\0\0\1\0\0\0\0\0\0\5puffy\0\0\1\0\1", 23}], msg_controllen=0, msg_flags=0}, 23}, {{msg_name(0)=NULL, msg_iov(1)=[{"_\200\1\0\0\1\0\0\0\0\0\0\5puffy\0\0\34\0\1", 2 3}], msg_controllen=0, msg_flags=0}, 23}}, 2, MSG_NOSIGNAL) = 2 1492485714.097840 poll([{fd=3, events=POLLIN}], 1, 5000) = 0 (Timeout) 1492485719.099159 socket(PF_INET6, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4 1492485719.099929 connect(4, {sa_family=AF_INET6, sin6_port=htons(53), inet_pton(AF_INET6, "fec0:0:0:ffff::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0 1492485719.100742 gettimeofday({1492485719, 100932}, NULL) = 0 1492485719.101843 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) 1492485719.103170 sendmmsg(4, {{{msg_name(0)=NULL, msg_iov(1)=[{"t\246\1\0\0\1\0\0\0\0\0\0\5puffy\0\0\1\0\1", 23}], msg_controllen=0, msg_flags=0}, 23}, {{msg_name(0)=NULL, msg_iov(1)=[{"_\200\1\0\0\1\0\0\0\0\0\0\5puffy\0\0\34\0\1", 2 3}], msg_controllen=0, msg_flags=0}, 23}}, 2, MSG_NOSIGNAL) = 2 1492485731.096900 poll([{fd=4, events=POLLIN}], 1, 3000) = 0 (Timeout) 1492485734.098345 socket(PF_INET6, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 5 1492485734.099193 connect(5, {sa_family=AF_INET6, sin6_port=htons(53), inet_pton(AF_INET6, "fec0:0:0:ffff::2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0 1492485734.099969 gettimeofday({1492485734, 100273}, NULL) = 0 1492485734.101202 poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}]) 1492485734.102447 sendmmsg(5, {{{msg_name(0)=NULL, msg_iov(1)=[{"t\246\1\0\0\1\0\0\0\0\0\0\5puffy\0\0\1\0\1", 23}], msg_controllen=0, msg_flags=0}, 23}, {{msg_name(0)=NULL, msg_iov(1)=[{"_\200\1\0\0\1\0\0\0\0\0\0\5puffy\0\0\34\0\1", 2 3}], msg_controllen=0, msg_flags=0}, 23}}, 2, MSG_NOSIGNAL) = 2 1492485746.097900 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} --- 1492485746.098406 rt_sigaction(SIGINT, {SIG_DFL, [INT], SA_RESTORER|SA_RESTART, 0x7fa4fc946cb0}, {0x4a6200, ~[RTMIN RT_1], SA_RESTORER, 0x7fa4fd000330}, 8) = 0 1492485746.099849 getpgrp() = 139 1492485746.100944 ioctl(0, TIOCGPGRP, [139]) = 0 1492485746.102129 rt_sigprocmask(SIG_BLOCK, [WINCH IO], NULL, 8) = 0 1492485746.103238 rt_sigaction(SIGIO, {SIG_IGN, [IO], SA_RESTORER|SA_RESTART, 0x7fa4fc946cb0}, {SIG_DFL, [], SA_RESTORER, 0x7f161b466cb0}, 8) = 0 1492485746.104837 exit_group(2) = ? 1492485746.105671 +++ exited with 2 +++
resolv.confの読み出しもuname(2)の発行もホスト名に関するものであり、かつ、sendmmsg()によって送信したメッセージの中にもホスト名を示す文字列(“puffy”)が入っていたので、問題はホスト名の解決に関するものかなと推測しました。
hostnameコマンドは一瞬で"puffy"を返すので問題ないとして、/etc/hostsはどうなっているかを見ると、127.0.0.1にpuffyは結び付けられていませんでした。
/etc/hosts
... 127.0.0.1 localhost ...
これを次のように変更したらオフラインでもうまく動くようになりました。
/etc/hosts
... 127.0.0.1 localhost puffy ...
Windows 10 Creators Updateを適用したらmoshが動くようになった
2, 3日前にWSL上でmoshを動かしたところ、ログインした瞬間にプロセスが終了してしまうという問題が発生しました。ぐぐったところ、既知の問題でした。
上記Issueを追ってみると、Creators Updateを適用すると直るとのことなので早速適用してみると、うまく動きました。やったぜ。
Windows UpdateによってSIMの通信規制された&対策をとった
最近、数年ぶりにWindows PCを買いました。モバイルノートなので、どれくらいバッテリが持つものなのかを試すために、外出先でテザリングによって通信しながらバッテリが切れるまで作業しました。すると、ある時点から通信が極端に遅くなってしまいました。なぜかと思ってスマホの通信量を確認すると、案の定、一日あたりの通信規制にひっかかる通信量を超えていました。あーあ。
きっとWindows Updateの自動ダウンロードの仕業だろうと思って、Windows Updateの設定から自動ダウンロードを無効化しようとするも、そういう設定はありませんでした。但し書きを見たところ、
利用可能な更新プログラムが自動的にダウンロードされ、インストールされます。従量制課金接続(料金が発生する場合があります)では実行されません。
と書いてありましたが、どうやって従量制課金接続かどうかを判定しているのかは不明でした。
調べた結果、wifiの設定の中でアクセスポイントごとに従量制課金接続かどうかを設定できるようです。具体的には次の設定項目です。
設定 → ネットワークとインターネット → wifi → <AP名> → 従量課金制接続として設定する
これを設定したら通信量の急激な増加が収まったので、おそらくWindows Updateが原因という仮説も正しかったのでしょう。
さくらVPSから無期限無料で使えるGCEインスタンスへ乗り換えた
私は、さくらVPS(1GBプラン)を数年間使ってきました。昔はこのサーバを多種多様な用途に使っていたのですが、最近では一部を除くほとんどの機能は無料で使える、他者が提供するwebサービス(github, qiita, はてなブログなど)に移行してしまいました。
現在の用途は次の通りです。
- 公開webサイト(ほぼ静的コンテンツ)の提供。アクセス数は一日に数百程度と、非常に少ない
- 自宅の計算能力の高いマシンのインターネット側IPアドレス(非固定)の記録: インターネットから自宅マシンに接続するために必要。
- ホームティレクトリ以外のシステム領域の変更も含めた種々の実験。動作確認程度なのでCPU性能は必要ない
用途がこれだけになったことによって、元々それほど性能が高くない上記VPSであっても、要求スペックに対する価格が若干上回ってしまっていました。そのため、次のような要求仕様のマシンをしばらく探していました。
- 1サーバを専有できる
- 静的IPアドレス
- CPU性能はほとんど必要ない
- メモリはUbuntuの要求仕様の最低である512MBもあれば十分
- ストレージは20GB程度あればOK。HDDかSSDかは問わない
- 現在のVPS(年間一万円強)より安価
その最中、無期限無償で使えるGCEインスタンスの存在を知りました。このインスタンスについては過去に以下のエントリで触れました。
satoru-takeuchi.hatenablog.com
このインスタンスはCPUとメモリのスペックが非常に低いのですが*1、ここ最近色々と評価した結果、私の用途では一応要件を満たすことを確認しました。VMインスタンス以外にも通信料と固定IPアドレスに関して課金要素がありますが、通信料については枠内に収まりそうですし、静的IPアドレスについては、マシンの電源を落とさなければ課金されない、かつ、落とすつもりはないので問題ありません。
本日移行したところ、移行そのものはあっさり終わって(待ち時間を含めて4,5時間)、いまのところ問題なく動いています。元々は、「今のところより安いものがあればいいな」程度に思っていましたが、まさか無料になってしまうとは思いませんでした。これだけのものが無料で使えるというのは凄いと共に、恐ろしいものです。今後はサービス規約の変更が無いかを注意深く観察しながら、しばらく使ってみようと思います。
sshを使ってリモートマシンでコマンドを叩く際の注意点
知ってる人には当たり前なのかもしれないですが、自分用のメモです。
先に結論を書くと次の通り。
- sshでリモートマシンにログインするのではなく<ssh command>によってコマンドを叩く場合には、ttyが割り当てられない。sshに-tオプションを付与すると、端末を無理矢理割り当てられる
- cronなどのttyが割り当てられていない環境からは-tオプションだけでは不十分で、-ttオプションが必要
事の経緯は、固定IPアドレスが無い自宅マシン(回線はフレッツ光)にインターネットからアクセスしたいというものでした。DDNSを使うんではなく、固定IPを持っている自前VPSと"who am i"を使えば、簡単になんとかできるだろ、と思ったのが運の尽き。どハマリしました。
最初に思い描いたサービスは次の通りです。
- 自宅マシンからVPSにsshで定期的に接続して、IPアドレスを所定のファイルに書き込む。ssh接続時はパスフレーズ無しの鍵を使う。接続時に使うユーザは、上記作業以外をする以外の権限を持たない特別なものを使う
- 自宅マシンのIPアドレスは、"who am i"の出力(例: “sat pts/8 2017-04-11 22:19 (xxx.xxx.xxx.xxx)”)の第5フィールドを整形する
- インターネットから自宅マシンに接続する前に、VPSに自宅マシンのIPアドレスを問い合わせる。もちろんIPアドレスの問い合わせとsshによる接続は1つにまとめてスクリプト化する
うまくいかなかったのは自宅マシンのIPアドレスを取得するところです。取得したIPアドレスを書き込んでいるはずのファイルが空になっていました。問題切り分けのために、インターネット上からsshで自宅マシンに一旦ログインして、そこからIPアドレス取得コマンドを実行するとうまくいきました。
色々とぐぐった結果、sshからログインでなく直接コマンドを叩くときは、ttyが割り当てられないことがわかりました。ttyが無いとwhoコマンドの出力の元ネタであるutmpファイルにログが残りませんので、IPアドレス取得コマンド内のsshの出力も空になった、それに伴ってコマンド全体の出力も空になってしまったというわけです。回避策は、以下の通りsshに-tオプションを付けることです。
man(1) sshより抜粋:
-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services.
まずはインターネット上からcrontabに書いたコマンドを実行すると、正しくIPアドレスが得られました。これで解決したと思ってcrontab上のsshに-tオプションを付与したものの、またしてもIPアドレスを書き込んでいるはずのファイルは空でした。
man(1) sshを再度読んでみると、さきほど抜粋した-tオプションの説明には続きがありました。
-t Force pseudo-terminal allocation. This can be used to execute arbitrary screen-based programs on a remote machine, which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even if ssh has no local tty.
cronなどのttyを持たないプロセス(daemon)からsshを実行する場合は、-tオプションだけでは不十分で、-ttオプションが必要と書いてあります。crontab上のsshに-ttオプションを付与したところ、無事問題は解決できました。最初からmanをちゃんと読んでおけばもう少し解決が楽でした。反省。
追記。優しい人に、IPアドレスを知る目的であれば$SSH_CLIENTないし$SSH_CONNECTIONが使えるということを教えていただきました。なんてこった
ノートPCを買い替えました
今使っているMacBook Air (13-inch, Mid 2012)のバッテリが劣化してきたので、新しいマシンを買いました。
HP EliteBook Folio G1/CT Notebook PC
あまりカスタマイズはしていませんが、ストレージだけは256GBに増量しました。SSDをSATA接続のものにするかNVMe接続のものにするかで少し悩んだのですが、後述の通り性能よりも駆動時間重視だったので、消費電力がSATAのものに比べて高くなると考えられるNVMe接続のものは避けました。ストレージをわざわざ増量したのは、増量することによって税引き価格が10万円を超えるのと、税引き価格が10万円を超えると7%offになるクーポンがあったからです(別サイト)。結果、5000円くらいの追加でSSDを128GB追加できました。まんまと売り手の思惑にハマってます。
もともと探していたマシンの要求仕様は次の通りです。一言で言えば性能はどうでもよくて、高性能なマシンに接続して繋ぐシンクラっぽい使い方を想定していました。
- ディスプレイの解像度はfull HDなら嬉しいけど1280x800あれば十分
- CPU性能にはこだわらない。Chromeとssh(mosh)クライアントがさくさく使えればそれでいい。前者についてはタブはあんまり開かないし、重い動画を見たりはしない
- ストレージ容量は購入後のアップデートに支障が無い程度(最悪32GBでもアリ?)でよい。その他のデータは上述の理由により手元に置いておかなくてもよい
- サイズは10インチ以上、13インチ以下
- 重量は1.2kg以下、できれば1kgを切って欲しい
- キーボードはプログラミングに支障を来さない程度に。変な配置になっていなければ各キーのサイズは若干小さくてもよい
- バッテリ駆動時間は最低8時間、できれば10時間以上
- OSにこだわりは無い
- 価格は5万円前後が嬉しい。出しても10万円くらいまで
これらを考慮した結果、次のような候補が挙がっていました
これらのうち、とくにaかcがいいかなと思っていました。バッテリ寿命はどちらも申し分なく、さらにaについてはとにかく軽いこと、cについては価格とディスプレイ解像度が高いことが魅力でした。ただしcについては、cの実機を触ったところ、キー配置が微妙であり、かつ、比較的近い使い方をしていると考えられる(プログラミング)実際のユーザのかたからも同じことを伺ったので、見送りました。
「さあaで決まりか」というときに、上述のideapadのユーザのかたに、上述のHPのマシンを教えていただきました(余談ですが、なぜかこれらの2つのマシン以外にも複数のマシンを同時に持ち歩いておられるとか…)。このマシンは価格帯はa, b, cよりもだいぶ上ですが、同一価格帯の他のマシンに比べてのコストパフォーマンスがぶっちぎりだったので、一目惚れで買ってしまいました。同じかたから、実機を触らなければ本来わからないキーボード配置についても不満無しという感想を得られたことも購入を後押ししました。
結局、性能はどうでいいというようなことを言いながら、そこそこの性能のものを買ってしまいましたが、「たまの散財はいいものだ」と自分を納得させることにしました。
一時的に使ってたGCEインスタンスを使い終えたので削除した
数日前に下記エントリに書いたGCEインスタンスを、本日旅から戻ったので削除しました。
satoru-takeuchi.hatenablog.com
サルベージが必要なデータは帰宅時点では既に無かったので、色々考えずに速攻で消せました。VMを作ったときからのVMのローカルストレージの増分は次の通り。
- カーネルビルド用にインストールしたいくつかのパッケージ => aptで持ってきただけのものだから不要
- linuxカーネルのソース => kernel.orgのgit repositoryから持ってきただけ。旅行中(残念ながら)カーネルパッチは書かなかったから自前データは無し
- githubから持ってきたいくつかのリポジトリ => 変更分はすべてgithubにpush済み
コストの面では、作業するときだけ起動させて運用してたので、旅の途中の一週間くらいの間で、総費用は800円未満。こういう一時的に作業環境が欲しいというときには、すぐに楽々使えて、かつお安く済むので、クラウドは便利ですね。ありがとうGCE(ステマ