読者です 読者をやめる 読者になる 読者になる

tmux 2.2 以降で East Asian Ambiguous Width Character を正しく表示させる方法

これまで tmux は文字幅を得るために独自のテーブルを持っていて、その独自テーブルでは East Asian Ambiguous Width というものを一切考慮していないので、CJK な環境ではパッチをあてて使うことがよく行われていた (tmux cjk patch とかでググるといろいろ出てくると思う)。

tmux 2.2 からは wcwidth(1) を使うようになり、独自テーブルをやめてロケールの情報から文字幅を得るようになった https://github.com/tmux/tmux/commit/26945d7956bf1f160fba72677082e1a9c6968e0c 。 が、このコミットをよく見ると setlocale(LC_CTYPE, "en_US.UTF-8") で固定されており、LC_ALL や LC_CTYPE に関係なく en_US.UTF-8 が使われる。 tmux は UTF-8 を前提としており、そこを固定したい気持ちは分からなくもないが……

なので対応としては、en_US.UTF-8 ロケールで文字幅を変更してやれば、パッチなしで East Asian Ambiguous Width の問題を回避できる。 http://eagletmt.hateblo.jp/entry/2016/03/23/020117 に書いたような方法で /usr/share/i18n/charmaps/UTF-8-CJK.gz を生成し、/etc/locale.gen で en_US.UTF-8 UTF-8-CJK にして locale-gen したところうまくいった。 普段 en_US.UTF-8 と ja_JP.UTF-8 を使い分けているような人はこの方法だと厳しいけど、基本 ja_JP.UTF-8 しか使ってない人は en_US.UTF-8 で文字幅を変えても悪影響は無いと思う。

fluentd のバッファファイルを直接加工する

たとえば変なレコードが混じってしまったせいで何度リトライしてもバッファのフラッシュに失敗するようなときに、バッファファイル (buffer_type file で作られるやつ) を使っていれば、そのファイルをいじることで応急処置ができる。

バッファファイルは [tag, time, record] という三つ組の列を msgpack でシリアライズした形式になっていて、v0.12.x と v0.14.x で time の型が違う *1 けど、どちらのバージョンでも以下のようなコードでバッファファイルを読み書きできそう。 なお v0.12.x において Fluent::Engine.msgpack_factory が追加されたのは v0.12.17 から なので注意。

require 'fluent/engine'

in_path = '/path/to/some-buffer.q0123456789abcdef.log'
out_path = '/path/to/modified-buffer.log'

def modify(tag, time, record)
  # do something
  [tag, time, log]
end

File.open(in_path) do |fin|
  unpacker = Fluent::Engine.msgpack_factory.unpacker(fin)
  File.open(out_path, 'w') do |fout|
    packer = Fluent::Engine.msgpack_factory.packer(fout)
    unpacker.each do |triplet|
      packer.write(modify(*triplet))
    end
  end
end

一旦 fluentd を停止してバッファファイルが触られないようにしてから、上のようなスクリプトで変更を加えたバッファファイルを作成し、 mv /path/to/modified-buffer.log /path/to/some-buffer.q0123456789abcdef.log で上書きしてから fluentd を起動するとよさそう。

ここまでの流れ

学生や新卒の人と話したりするときに、これまでエンジニア的にどうやって今の状態になったかみたいな話を何度もする機会があって、 その度に色々思い出しながら話してたんだけど、自分用に整理したかったのでついでにオンラインでアクセスできる場所に置くことにする。 なにか新たに思い出したり思い出補正が発覚したりしたら適宜修正していく。

小学生くらい (- 2002)

学校に自由に使える PC があって、卒業するくらいの時期には古いやつと新しいやつの2つがあって、新しいやつのほうに入っていたタイピングゲームで主に友人の K 君と遊んでいた記憶がある。 もう全然覚えてないけど、時期的には古いほうが Windows 95 で新しいほうが Windows 98 だろうか。 自宅にも PC があって、麻雀ソリティアで遊んでいた記憶がある。 PC に初めて触れたのはこのくらい。タイピングゲームに熱中したおかげで、タッチタイピングはこのへんで習得していた *1

中学生くらい (2002 - 2005)

自宅にインターネット回線や Windows 98 のラップトップがきた。もちろんラップトップは家族共用。 このへんでインターネットにはまり始めて、ゲームの攻略サイトの掲示板とかによく書き込んでた。 あと K 君が自分でちょっとしたゲームを作っていて、僕も気になったので HSP で何か書いてみていた。 プログラミングというものを初めてやったのがこれで、なんとなく楽しんでいた気がする。 ただ、そんなに長続きはしなかったと思う。 HSP については今は文法すら覚えてない。

高校生くらい (2005 - 2008)

高校進学と同時に自分用の Windows XP のラップトップを買ってもらい、インターネットにがっつりはまった。 というかだいたい 2ch を見ていた。 2ch にいるような人だとプログラマの割合が高めで、そういう人たちと話しているうちに、HSP の経験や PC というのに興味を持ち始めていたこともあって、何か作りたいものがあったわけじゃないけど C 言語に入門した。 猫でもわかるC言語プログラミング のサイトやそこの本を主に読んでた。 最初は Borland C++ Compiler を使っていたけど、cmd.exe にも多少慣れてきてから cygwin をメインで使うようになって gccコンパイルしていた。 エディタも TeraPad とか Notepad++ をメインで使いつつも、cygwin の中で Vim を使い始めていた。 cygwin もそうだけど、EmacsVim を使ったほうがプロっぽいみたいな風潮があって (?)、最初は Emacs にチャレンジしたんだけど小指を痛めて挫折し、一方 Vim のほうはそういうことがなかったので当時は謎の操作に戸惑いながら無理して Vim を使っていた。

C/C++ の初心者本に載ってるようなことはなんとなく分かるようになって、その後 C に構文が似ているらしいという理由で Perl も勉強し始めた。 CGI とかも書いてみてはいたけど、LWP でインターネットから情報を簡単にとってこれることに感動して、今でいうとスクレイピングみたいなことをよくやっていた。 HTML を DOM としてパースするみたいな発想は全くなくて (というかそもそも HTML/XML というフォーマットや DOM というものについて知らなかったと思う)、ひたすら正規表現だけでがんばっていた。 おかげで正規表現は覚えた。

このへんでプログラムを書くのがすごく楽しくて好きになっていた。 リアルでプログラムを書く仲間みたいなのはいなかった (K 君は別の高校に進学して疎遠になっていた) けど、2ch のプログラム板とかはよく見ていて、大学生の宿題の丸投げスレの問題に解答したり、コードゴルフというものを知って POJ で勝手にコードゴルフにチャレンジしたりしていた。 この頃 Java も少し書いてみたりしていたけど、あまり興味を持てず勉強はしていなかった *2Win32 API とか Swing とかで GUI を書いてみようとしていた時期もあったけど、めんどくさすぎるわりに大したものができなくて、ターミナルで満足していた。

大学生くらい (2008 - 2014)

進学と同時に MacBook を買って、cygwin から開放されて普通のターミナルの使いやすさに驚いた。cygwin ではビルドできなかったり動かなかったりした CLI ツールも動いてすごく便利に感じた。 その結果、色々なプログラミング言語を試せるようになって、Ruby とか Python とか Haskell とか Lisp とか色々入門していた。 その中でも RubyHaskell は手になじんで、その後もしばらく使い続けて、Haskell は最近はもう全然書く機会がなくなってしまったけど Ruby は仕事で毎日使うようになった。 あと大学の図書館で エキスパートCプログラミング という本に出会って、これが本当に面白くて、C を理解する上で大いに参考になっただけではなく、プログラミング言語そのものやランタイムやツールチェインに興味を持つきっかけの一つになったと思う。 そんな流れでもっとマイナーな言語を使ってみたりしつつ、プログラミング言語への興味が高まってそれ系の研究室に所属することになったりした。

この頃からインターネットの中心 (?) が 2ch から Twitter にシフトしていきつつ、はてなダイアリーに何か書いたり、GitHub に dotfiles や雑コードを上げるようになったり、勉強会的なものに少し参加してみたりするようになった。 リアルのほうでも情報系の学科にいったので、プログラムの話をできる友人ができた。 Twitter がきっかけで B2 の後半くらいから学内の ICPC の練習会にも参加するようにもなった。 技術的な成長は B1 から B3 あたりの時期が際立っていたと思う。 色々な言語の仕様を読んだり処理系の一部を読んでみたり、ちょっと背伸びして公開されている論文を読んでみたり、なにかコードを書くアイデアが出たらあえてマイナーな言語で書いてみたり *3。 まぁ競技プログラミングに関しては全然強くはなれなかったけど……

B3 の頃からデスクトップ環境を Linux にしたり、Linux サーバで PT2 でアニメ録画をし始めたりして、Linux とかサーバ管理について少しずつ学んでいった。 Web アプリケーションに興味を持ち始めたのはたぶん B4 の後半か M1 の前半くらいで、sinatraRails に入門しつつ VPS を借りて nginx とか使い始めた。 GUI アプリケーションを書くより HTML を書くほうが圧倒的に楽だと感じて、ここから徐々に sinatraRails を使うようになっていた。 といっても不特定多数の人が使う (使える) ようなものではなく、ただ自分だけが使うツールの GUI として使っていた。 研究室に所属してからは同じ研究室の同期を始めとして同じフロアの人たちに恵まれて、似たような興味分野の人同士で色々話したり聞いたりできて本当に楽しかった。

現在

社会人になってからだいたい何してたかは書き残しているのでそっちに http://eagletmt.hateblo.jp/entry/2015/12/31/212651Rails とかインフラにちょっと詳しくなったりした。

プログラミングは手段でしかなくてゲームを作りたいとかサービスを作りたいとかそういう目的があってプログラミングを学んだり仕事にしたりする人もいると思うんだけど、僕は全くそんなことはなくてただ単純にコードを書くこと自体が面白くて楽しくて書き続けてきた。 なので宿題丸投げスレや競技プログラミング、そして仕事みたいに課題を与えてくれるものがあると助かっていた。 面接とかで今まで何作りましたかとか今後何作りたいですがみたいなことを聞かれても困ることが多かった。まぁこれは今もわりとそうな気がする。 コードを書くのは今では仕事になっているけど趣味でもあり続けている。

*1:これ以降、タイピング速度は今に至るまで上がってない気がする……

*2:そして今に至るまで真面目に書いたことはない

*3:なおこの頃書いたコードは後でほとんど Ruby で書き直された

RTX 810 と Openswan で site-to-site VPN

自宅から 10.0.0.3 とかで VPS にあるサーバにアクセスしたいし、逆に VPS から 192.168.10.8 とかで自宅のサーバにアクセスしたい。

環境

  • 自宅 (RTX 810)
    • グローバルIP 1.1.1.1
    • プライベートIP 192.168.10.1
    • サブネット 192.168.10.0/24
  • VPS (Openswan)
    • グローバルIP 2.2.2.2
    • プライベートIP 10.0.0.2
    • サブネット 10.0.0.0/16
  • pre-shared-key HOGEFUGA

自宅側の設定

Web UI から VPN 接続の設定→IPsecを使用したネットワーク型 LAN間接続VPN からだいたい設定できる。 最終的に以下のような設定になった。

...
ip route 10.0.0.0/16 gateway tunnel 1
...
tunnel select 1
 tunnel name vps
 ipsec tunnel 1
  ipsec sa policy 1 1 esp aes-cbc sha-hmac
  ipsec ike group 1 modp1024
  ipsec ike hash 1 sha
  ipsec ike keepalive use 1 off
  ipsec ike local address 1 192.168.10.1
  ipsec ike pre-shared-key 1 *
  ipsec ike remote address 1 1.1.1.1
  ipsec auto refresh 1 off
 ip tunnel tcp mss limit auto
 tunnel enable 1
...
ip filter 200085 pass * 192.168.10.1 udp * 500
ip filter 200086 pass * 192.168.10.1 esp * *
...

VPS 側の設定

Openswan

/etc/ipsec.conf

version 2.0

config setup
    dumpdir=/var/run/pluto/
    virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v6:fd00::/8,%v6:fe80::/10
    oe=off
    protostack=auto

include /etc/ipsec.d/*.conf

/etc/ipsec.secrets

include /etc/ipsec.d/*.secrets

/etc/ipsec.d/rtx810.conf

conn rtx810
    auto=start
    type=tunnel
    authby=secret
    keyexchange=ike
    ike=aes128-sha1;modp1024
    phase2=esp
    phase2alg=aes128-sha1;modp1024
    pfs=no

    left=1.1.1.1
    leftid=1.1.1.1
    leftsourceip=10.0.0.200
    leftsubnet=10.0.0.2/16

    right=2.2.2.2
    rightid=192.168.10.1
    rightsubnet=192.168.10.1/24

/etc/ipsec.d/rtx810.secrets

1.1.1.1 192.168.10.1: PSK "HOGEFUGA"

/etc/sysctl.d/50-openswan.conf

net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.ip_forward = 1

これで ipsec verify に FAIL がなくなって起動するはず。

iptables

esp と udp 500 を開けておく。 あとは他のサーバから Openswan がいるサーバを通って通信させるために forward も許可しておく。

iptables -A INPUT -p udp -m multiport --dports 500 -j ACCEPT
iptables -A INPUT -p esp -j ACCEPT
iptables -A FORWARD -s 10.0.0.0/16 -d 192.168.10.0/24 -j ACCEPT
iptables -A FORWARD -s 192.168.10.0/24 -d 10.0.0.0/16 -j ACCEPT

これで VPN 接続を張れるはず。

route

Openswan を立てているサーバ以外から 192.168.10.0/24 にアクセスするときは 10.0.0.2 を通るようにする。 ip route add 192.168.10.0/24 via 10.0.0.2 dev ens4 とすればいいんだけど、この設定を永続化するために systemd-networkd では network ファイルに書いておく。

/etc/systemd/network/ens4.network

...

[Route]
Gateway=10.0.0.2
Destination=192.168.10.0/24

Docker コンテナ内の net.core.somaxconn を変える

--net=host でコンテナを起動すれば、network namespace が分離されないので net.core.somaxconn の値はホスト側と一致する。

% cat /proc/sys/net/core/somaxconn
1024
% docker run --net=host ubuntu:16.04 cat /proc/sys/net/core/somaxconn
1024

けど普通に docker run すると、ホスト側の値にかかわらず、コンテナ内ではデフォルトの128になる。

% cat /proc/sys/net/core/somaxconn
1024
% docker run ubuntu:16.04 cat /proc/sys/net/core/somaxconn
128

で、コンテナ内でこの値を変えようとしても、デフォルトでは許可されていない。

% docker run -it ubuntu:16.04 bash
root@1704e07731c0:/# cat /proc/sys/net/core/somaxconn
128
root@1704e07731c0:/# echo 512 > /proc/sys/net/core/somaxconn
bash: /proc/sys/net/core/somaxconn: Read-only file system

これを回避する方法はいくつかあって、1つは --privileged で実行する方法。

% docker run --privileged -it ubuntu:16.04 bash
root@41d5397d065b:/# echo 512 > /proc/sys/net/core/somaxconn
root@41d5397d065b:/# cat /proc/sys/net/core/somaxconn
512
root@41d5397d065b:/# exit
% cat /proc/sys/net/core/somaxconn
1024

ホスト側とは別の network namespace で値を変えているだけなので、ホスト側の値には影響は無い。 ただ、これだと不要な権限も色々と渡ってしまうのでできれば避けたい。

そこで、必要なものだけ rw で bind mount して、そこに書き込むという方法がある。

% docker run --volume /proc/sys/net/core/somaxconn:/somaxconn -it ubuntu:16.04 bash
root@9bc22f86b0f3:/# cat /somaxconn
128
root@9bc22f86b0f3:/# cat /proc/sys/net/core/somaxconn
128
root@9bc22f86b0f3:/# echo 512 > /somaxconn
root@9bc22f86b0f3:/# cat /somaxconn
512
root@9bc22f86b0f3:/# cat /proc/sys/net/core/somaxconn
512
root@9bc22f86b0f3:/# exit

なお Docker 1.12.0 からは --sysctl というオプションがつくようなので、試してないけど 1.12.0 以降は docker run --sysctl net.core.somaxconn=512 でよさそう。 https://github.com/docker/docker/pull/19265

3種類のドキュメント

ドキュメントが必要というのは誰もが思っていることだと思うけど、わかりやすくて過不足の無いドキュメントを書くのは難しい。 自分が実装したり構築したりするシステムのドキュメントを書くとき、最近は3種類の人に対して別々の文書を用意するように努力している。

ユーザ向け

どうやって使うのか、どうやって接続するのか、各エンドポイントがどんな意味を持っていてどんなリクエストを受け付けるのか、等々。 他のドキュメントと比べてこれが一番対象読者の数が多いので必ず書くようにしているんだけど、自分自身がユーザじゃないことが多くてどの情報が必要なのか正しく判断できず、過不足があることも多いのが悩ましい……

運用者向け

どうやってセットアップするのか、どのサーバで動いているのか、他のどのサービスに依存しているのか、等々。 僕の場合、自分が運用者になることも多く自分自身が対象読者に含まれているので、そのときにどんな情報が欲しいか想像しやすくてこの種類のドキュメントは書きやすい。 僕が書いたドキュメントに対して他の運用者が実際わかりやすいと思ってるのかどうかは知らないけど……

開発者向け

そのシステムを手元で動かすにはどうするのか、どういうポリシーでこの設計になっているのか、等々。 これも自分自身が対象読者に含まれているドキュメントではあるんだけど、運用者向けドキュメントと比べて何を明文化すべきなのかよく分からなくて、あまり書けていない……

ここで挙げた3つは完全に独立しているわけではなくて、例えば用語集とかログの場所とかは全員知っておいたほうがよさそうだし、全体的な構成は運用者も開発者も知っておいたほうがよさそうだったりする。 とはいえこういう区分でドキュメントを分けるのは個人的にはうまくいっていると思っていて、今後もこの3つの分類を意識していこうと思っている。

Linux デスクトップ環境 2016

5年半くらい前に http://d.hatena.ne.jp/eagletmt/20100905/1283686004 というのを書いたけど、そこから今どう変わっているのか。

こう列挙してみると2016年になっても Linux デスクトップは… みたいな気持ちも無いわけじゃないけど、色んなコンポーネントを好きなように設定できたり入れ替えることができたり、場合によってはパッチをあてることもできて、そのへんが好きで使っている。 もちろん、仕事では Linux で動作するようなコードばかり書いたり読んだりしているので、そのへんの知識を手元でも使えたり手元とサーバの違いではまったりしにくいから、というのもあるけど。

ディストリビューション

相変わらずずっと Arch Linux を使っている。 パッケージの更新が早かったり、最小限のデフォルトしか設定されていないのでディストリビューションが勝手に設定しているデフォルトにはまることがなかったり、PKGBUILD がシンプルで独自パッケージを作りやすかったり、ArchWiki が非常に充実していたり、全然不満は無い。

ウィンドウマネージャ

awesomeLua を書くのがつらくなって XMonad を使うようになっている。 最近はほぼ Haskell を書くことが無いので XMonad のために GHC を入れるのは若干だるいけど、XMonad 自体は快適なので使い続けている。

ディスプレイマネージャの類は今も昔も使っていない。普通に getty でログインして startx 叩いている。

ターミナル

mlterm はよくできてるんだけど動作が遅かったりチラつきが多かったりして rxvt-unicode を使ってたんだけど、最近また mlterm を使うようになっている。 でもやっぱり mlterm は遅いので、また rxvt-unicode を使うようになるかもしれない…… rxvt-unicode は異常に速い。

rxvt-unicode だといわゆる East Asian Ambiguous Width の問題があって「▽」とかを半角として認識してレンダリングされて困るんだけど、そのへんは ambiwidth.rbUTF-8-CJK という charmap を作って回避している。 いまググったら同じ方法で回避している人がいた https://github.com/hamano/locale-eaw

ちなみに tmux も同じ問題をかかえていて、しかし tmux は glibcロケールの定義は完全に無視して独自のテーブルを持っているので、wcwidth を使うようにしたパッチをあてて使っている https://github.com/eagletmt/arch.wanko.cc/tree/master/aur-eagletmt/PKGBUILDs/tmux-cjkwidth

と思ったら tmux 本体もついに wcwidth を使うようになったっぽいので、次のリリースからはパッチ不要かも https://github.com/tmux/tmux/commit/26945d7956bf1f160fba72677082e1a9c6968e0c

日本語入力

相変わらず uimuim-skk を使っている。 昔は skk-jisyo.L を直接使っていたけど、今は yaskkserv を使っている。 SKK server completion に対応しているし、あと変換候補が L 辞書に見付からなかったときに Google Japanese Input から探してくれて便利。とくにアニメキャラの名前等の固有名詞。

各種ビューア

画像には今も feh を使っている…… 薄い本をスキャンして電子化していたりするんだけど、そういうのを読むときは mcomix を使っている。

音声や動画には、昔は MPlayer を使っていたけど、その fork の MPlayer2 を経由して、最近はさらに別の fork の mpv を使っている。 MPlayer 系は余計な GUI 要素がなくて、キーボードで完結できるのがよい。

PDF は Evince がよくできているので使っている。これ入れると GNOME 系の一部が依存で入ってくるけど仕方ない… 別途 poppler-data というパッケージも入れないと日本語を表示できない点に注意。 パスワードが設定されている PDF も Evince で普通に扱えるんだけど、まぁだるいので適当にパスワードを外したりしている… https://github.com/eagletmt/misc/tree/master/cxx/pdf-unlock

スクリーンショット、スクリーンキャプチャ

ImageMagick に import というコマンドが含まれているので、それでスクリーンショットは簡単にとれる。

スクリーンキャプチャが欲しいときは FFmpeg を使って ffmpeg -f x11grab で。

Bluetooth

これは他に代替無いだろってかんじだけど BlueZ 5 を使っている。 BlueZ 4 -> 5 と PulseAudio 4 -> 5 の過渡期があってその時期はつらみがあったけど、まぁ今は BlueZ 5 + PulseAudio 8 で問題無く使えている。 そのへんの使い方とか設定はだいたい ArchWiki を見ればわかる。 https://wiki.archlinux.org/index.php/Bluetooth https://wiki.archlinux.org/index.php/Bluetooth_headset

フォント

最近は Web ページで色んなフォントが設定されていたり Unicode の絵文字が普通に使われるようになって、fontconfig でそのへんをちょっと調整しないとつらい時代になった。 基本的にはプロポーショナルフォントには Migu等幅フォントには Ricty を使っている。 これでカバーできていない絵文字やハングル等は Noto Font を使っている。 Linuxレンダリングすると汚ないようなフォントが一部サイトでは font-family に指定されているので、fontconfig で sans-serif とか monospace に書き換えていたりする。 https://github.com/eagletmt/dotfiles/blob/master/dot.config/fontconfig/fonts.conf

こういうのを設定していると「この文字のグリフを含むフォントは何だろ?」というのを知りたくなると思うんだけど、標準の fc- 系のコマンドでそれを達成する方法が分からなかったので、自分で書いたりした https://github.com/eagletmt/misc/tree/master/cxx/fc-find

fontconfig の設定については、例によって ArchWiki が参考になる https://wiki.archlinux.org/index.php/Font_configuration