tmux
携帯、変えた
オイラーが使ってるのは、3G電波しか扱えない10年選手。携帯屋から、来年3月で使えなくなるから、新しいのにしてくださいって、再三案内が送られてきていた。ガン無視してたよ。そしたら、頻繁に催促メールや電話が来るようになった。うざいんでほってた。
とうとう、無料で新しいのにしてやるから、今使ってる携帯持って出頭せいって通告が来た。 そんな事で、新しい(けど、型落ち)のに交換してきたよ。新な契約になるんで、最低の契約(電話だけ)にした。
マニュアル見ながら、カスタマイズしてたら、着信バイブレーションが無駄に5パターンも選べるようになってた。順番に試してみたら、モールス信号で、A文字の振動と、N文字の振動パターンが含まれていた。
あれ、着メロディーは、登録出来ないのかな? 設定出来るなら、電話の場合はbreakぐらいにしたいな。SMSだったら、lazyぐらいが良い。ggして、先駆者が居ないか調べてみるか。
接続切れたを救う tmux
あるー日、森の中、くまさんが、、、じゃなくて、Windows側のPuTTYから接続してたdebian機への接続が途絶えた。以前にも発生した事があるんだ。2度有る事は3度以上有るって言うから、こういう時に備えて、防災訓練をやっておく。
debian:~$ tmux debian:~$ emacs ...... コーヒーブレークして帰ってきたら、PuTTYが、接続切れたぞーと言ってた。 再度、PuTTYを起動して login。そして、 debian:~$ tmux ls 0: 3 windows (created Mon Mar 1 05:23:37 2021) [80x31] (attached) debian:~$ tmux attach -t 0 これで、元の(emacs)画面に復帰した。
もっと簡単にやるには、下記で良いそうな。
debian:~$ tmux a #0
この際だからtmuxのマニュアルをチラ見したよ。時計モードなんてのが紹介されてた。
sakae@pen:~$ tmux clock-mode
これで、端末がクリアされて、デジタル時計が表示される。ESCキーを推すと、時計が消えて、元の画面が復帰されるよ。
防災訓練と言うか、防災備蓄が推奨されてた。 災害用の非常食にポテトチップス採用 ですって。ポテチなら無理なく備蓄出来るな。そして、古い奴から消費して、減った分は補充しておけ。ローリング・ストックとは良いネーミング。
そんな事より、災害救助に当たる自衛隊のコマンダーが言うには、1に体温の保存、2に水の確保らしい。食料より水が大事なんだな。オイラーは、腹が減ると性格が変わる(粗暴モードへ移行)らしいんで、ポテチの準備も宜しく。>女房
もう少しtmux
基本的なtmuxの使い方は上記通りなんだけど、もう少し突っ込んでみる。
debian:~$ tmux ls 1: 3 windows (created Mon Mar 1 06:14:15 2021) [80x31] (attached) 3: 1 windows (created Mon Mar 1 07:04:32 2021) [118x40] (attached)
今度は一度、debian側のLXDEなGUIにログインして、そこの端末でtmuxした。次にPuTTY側からもtmuxした(3画面分を確保)。そして3枚の画面を作った。 次に、GUI側のtmuxを終了。またtmuxを起動。
1: とか 3: とかは、tmuxのセッション番号なんだな。3 Windowsとかは、仮想端末の数、後に続くのは起動日時とか、端末のstty値だな。GUIの端末は画面が広いなんて事が分かる。
一度tmuxのセッションを起動すると、tmuxのサーバーが立ち上がってて、それが一手に管理をしてくれてるとな。クライアントは、/tmux/tmux-1000/配下に出来る、ソケットを頼りに、サーバーとネゴシエーションするんだな。1000って数字はuidなんだな。
クライアント/サーバーモデルって分かれば、理解が進むな。サーバーと切り離すには、Prefix+dのキーを押す。
debian:~$ ;; tmux の Prefix + d [detached (from session 1)] debian:~$ tmux ls 1: 3 windows (created Mon Mar 1 06:14:15 2021) [80x31] 3: 1 windows (created Mon Mar 1 07:04:32 2021) [118x40] (attached)
これで、セッションがデタッチされた。アタッチは上でやったように、tmux a だ。番号を指定しない限り、直前のセッションに接続される。
debian:~$ tmux new -s works debian:~$ tmux ls 1: 3 windows (created Mon Mar 1 06:14:15 2021) [80x31] 3: 1 windows (created Mon Mar 1 07:04:32 2021) [118x40] (attached) works: 1 windows (created Mon Mar 1 07:43:55 2021) [80x31] (attached)
このように起動すると、新しいセッションを初められる。余り作り過ぎると、わけわかめになるぞ。程々が良い。
debian:~$ tmux a -t 1 [detached (from session 1)] debian:~$ tmux a -t works [detached (from session works)]
使うなら、ちゃんと名前を付けておいて、きちんとセッション管理しよう。
tmux resource
tmux read
上記のHomeの所でmanしたら、OpenBSDのそれに案内された。OpenBSDはデフォルトで入っているからね。と、言う事はソース観光しれって事だな。
まずはソースの外観検査だな。/usr/src/usr.bin/tmux に多数のファイルが置いてある。数えてみたら127個も有った。とても見て行く気にはならない。
でも、ファイル名だけを確認してみると、cmd-xxx.cって名前が付いてるのが約半数あった。tmuxの各種コマンドを処理するんだな。注目は、cmd-parse.yってやつ。コマンドを解析してくれるやつだな。
ちょいと、上でやったclock-modeなんてのが、どの辺に潜んでいるか探ってみて、土地勘を養ってみる。
vbox$ grep clock-mode *.c cmd-copy-mode.c: .name = "clock-mode", key-bindings.c: "bind -N 'Show a clock' t clock-mode", options-table.c: { "clock-mode-color", "clock-mode-colour" }, options-table.c: { .name = "clock-mode-colour", options-table.c: { .name = "clock-mode-style", window-clock.c: .name = "clock-mode", window-clock.c: colour = options_get_number(wp->window->options, "clock-mode-colour"); window-clock.c: style = options_get_number(wp->window->options, "clock-mode-style");
cmd-copy-mode.cの中に対応するコードを押し込んでおきます。実際の下請けはwindow-clock.cです、って構図かな。デカの勘ですけどもね。
ええい、取り合えずgdbにかけられるようにコンパイルしてみるか。
gdbで閲覧作戦発動
取り合えずソース群を/tmpの下に移動。そしてMakefileを変更
CFLAGS = -O0 -g -I${.CURDIR} # CFLAGS += -I${.CURDIR}
全くのソースからだと、configureする時、–enable-debug とかってやるのかな。OpenBSDに収録されてるソースとMakefileは綺麗に整理されているので、ちょいと変更するだけ。
vbox$ ldd ./tmux ./tmux: Start End Type Open Ref GrpRef Name 163e4000 363f3000 exe 1 0 0 ./tmux 0dc6c000 2dc6e000 rlib 0 1 0 /usr/lib/libutil.so.15.0 0a67d000 2a681000 rlib 0 1 0 /usr/lib/libcurses.so.14.0 0018a000 2018c000 rlib 0 1 0 /usr/lib/libevent.so.4.1 047e1000 247e3000 rlib 0 1 0 /usr/lib/libm.so.10.1 07375000 27386000 rlib 0 1 0 /usr/lib/libc.so.96.0 0105e000 0105e000 ld.so 0 1 0 /usr/libexec/ld.so
こんな構成だった。libevent.soって、珍しいやつを使ってるな。そんじゃ、gdbにかけてみる。
(gdb) b main Breakpoint 1 at 0xc2432: file tmux.c, line 331. (gdb) r Starting program: /tmp/tmux/tmux Breakpoint 1, main (argc=1, argv=0xcf7ea924) at tmux.c:331 331 char *path = NULL, *label = NULL;
普通だったら、ここからステップ実行して行くんだけど、変な所に迷い込むとgdbの制御を失ってしまうので、要と思われる所にBPを置いてみる。
取り合えずlistって叩いて、要所と思われる所を見つける。
(gdb) l 456 /* 457 * The default shell comes from SHELL or from the user's passwdentry 458 * if available. 459 */ 460 shell = getshell(); 461 options_set_string(global_s_options, "default-shell", 0, "%s", shell); 462 463 /* Override keys to vi if VISUAL or EDITOR are set. */ 464 if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { 465 options_set_string(global_options, "editor", 0, "%s", s); (gdb) tb 461 Temporary breakpoint 2 at 0x14111ca9: file tmux.c, line 461.
tbは、一度だけ効力を発するBPだ。
(gdb) c Continuing. Temporary breakpoint 2, main (argc=0, argv=0xcf7ea928) at tmux.c:461 461 options_set_string(global_s_options, "default-shell", 0, "%s", s hell); (gdb) p shell $1 = 0xcf7eab72 "/bin/ksh"
OpenBSDのshellはkshなのさ。
(gdb) 496 flags |= CLIENT_DEFAULTSOCKET; 497 } 498 socket_path = path; 499 free(label); 500 501 /* Pass control to the client. */ 502 exit(client_main(event_init(), argc, argv, flags, feat)); 503 } (gdb) tb 502 Temporary breakpoint 3 at 0x1411201c: file tmux.c, line 502. (gdb) c Continuing. Temporary breakpoint 3, main (argc=0, argv=0xcf7ea928) at tmux.c:502 502 exit(client_main(event_init(), argc, argv, flags, feat)); (gdb) s event_init () at /usr/src/lib/libevent/event.c:99 99 struct event_base *base = event_base_new(); (gdb) finish Run till exit from #0 event_init () at /usr/src/lib/libevent/event.c:99 0x14112021 in main (argc=0, argv=0xcf7ea928) at tmux.c:502 502 exit(client_main(event_init(), argc, argv, flags, feat)); Value returned is $2 = (struct event_base *) 0x61040400
502行目は、サーバー側の最後の所。ここで、クライアントが動き出すはず。なんだけどエラーだ。どんなエラーかな?
(gdb) c Continuing. sessions should be nested with care, unset $TMUX to force [Inferior 1 (process 15545) exited with code 01]
このセッションは、既にtmuxが稼働してる所でやったので、多重起動は駄目って事なんだな。
tmux server/client
vbox$ ps awx | grep tmux 95225 ?? Rp 0:01.10 tmux: server (/tmp/tmux-1000/default) (tmux) 71158 p0 S+p 0:00.01 tmux: client (/tmp/tmux-1000/default) (tmux)
こんな風に、サーバー/クライアントのモードで動いている。
vbox$ gdb tmux -p 71158 : (gdb) bt #0 kevent () at /tmp/-:3 #1 0x0b80ac10 in kq_dispatch (base=0x5fc7bc00, arg=0x5f629c00, tv=0x0) at /usr/src/lib/libevent/kqueue.c:198 #2 0x0b80bd94 in event_base_loop (base=0x5fc7bc00, flags=1) at /usr/src/lib/libevent/event.c:474 #3 0x0b80bb04 in event_loop (flags=1) at /usr/src/lib/libevent/event.c:409 #4 0x14ec6990 in proc_loop (tp=0x5fc7b400, loopcb=0x0) at proc.c:202 #5 0x14e54345 in client_main (base=0x5fc7bc00, argc=0, argv=0xcf7d6868, flags=402718720, feat=0) at client.c:387 #6 0x14ef205a in main (argc=0, argv=0xcf7d6868) at tmux.c:502
client_main
の中で、アタッチ/出タッチの処理を行っている(ぽい)。libeventの力を借りているんだな。ってな事が分かる。
clock
clock-modeの実際の処理はwindows-clock.cの中でやってる。その証拠に、画面に表示される、数字とかのROMが格納されてて、それが使われいるから。
const char window_clock_table[14][5][5] = { { { 1,1,1,1,1 }, /* 0 */ { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,0,0,0,1 }, { 1,1,1,1,1 } }, { { 0,0,0,0,1 }, /* 1 */ { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 }, { 0,0,0,0,1 } }, :
5x5のドット文字が14種登録されてる。そしてこのROMを使って画面に描画するのが、
window_clock_draw_screen
っていう関数。表示色(colour)や(style)の切り替えデータも取り込んで使っている(12表記だと、AM/PMの表示付き)。
この手続きを呼び出してのに、 window_clock_timer_callback
等が居るんだけど、BPを貼っても引っかからない。一体どういう風にやってるの?
more sesource
いい加減、迷子になったので、先駆者の研究を参照。論文書く時も、巻末には参考にした文献を挙げるからね。それらをヒントに、新たな研究をして、知見を得ましたって感謝の気持ちです。
これらをヒントに、精進してみるか。
もっと証拠を
上記の解析メモさんを参考に、オイラーもやってみる。psのxjオプション知りませんでした。
vbox$ ps xj USER PID PPID PGID SESS JOBC STAT TT TIME COMMAND sakae 39421 94973 94973 0 0 S ?? 0:00.51 sshd: sakae@ttyp0 sakae 2529 1 2529 0 0 Sp ?? 0:04.88 tmux: server (/tm sakae 87590 39421 87590 0 0 Ip p0 0:00.06 -ksh (ksh) sakae 79191 87590 79191 0 1 I+p p0 0:00.01 tmux: client (/tm sakae 66798 2529 66798 0 0 Sp p1 0:00.04 -ksh (ksh) sakae 31005 66798 31005 0 1 R+pU p1 0:00.00 ps -xj sakae 62832 2529 62832 0 0 Ip p2 0:00.02 -ksh (ksh) sakae 63551 62832 63551 0 1 I+ p2 0:01.34 emacs (emacs-27.1
これ、tmuxを起動してshellの端末が開いた状態で、もう一つ窓を作ってそこからemacsを起動した図。いや、図ではなくてテーブルだな。
vbox$ pstree -+= 00001 root /sbin/init |-+= 57435 root sshd: /usr/sbin/sshd [listener] 0 of 10-100 startups (sshd) | \-+= 94973 root sshd: sakae [priv] (sshd) | \-+- 39421 sakae sshd: sakae@ttyp0 (sshd) | \-+= 87590 sakae -ksh (ksh) | \--= 79191 sakae tmux: client (/tmp/tmux-1000/default) (tmux) |-+= 02529 sakae tmux: server (/tmp/tmux-1000/default) (tmux) | |-+= 66798 sakae -ksh (ksh) | | \-+= 05210 sakae pstree | | \-+- 31232 sakae sh -c ps -kaxwwo user,pid,ppid,pgid,command | | \--- 80594 sakae ps -kaxwwo user | \-+= 62832 sakae -ksh (ksh) | \--= 63551 sakae emacs (emacs-27.1)
こちらが図になる(関係者以外は、消してあります)。
sshdで待ち構えている所に、puTTYでログイン、すかさずtmuxを起動。その流れが、pid 57435の幹だな。tmuxのクライアント起動で、サーバーが居なかったら、自動起動する。 普通は、サーバーを起動しておいてから、クライアントを起動ってばかり思っていたから、意表をつく動きだ。
サーバー側には、2つのshellが動いている。66798と62832だ。前者ではpstreeを実行。それが下請けを雇ったってな事が分かる。もう一方のshellはemacsに変身しているんだな。
この状態で、PuTTYを強制的に切断して(事故の真似)から、再度sshでログイン。すかさずpsすると、クライアントは居なくなってた。サーバーは、サーバーなんで、ちゃんと生きている。
vbox$ pstree -+= 00001 root /sbin/init |-+= 57435 root sshd: /usr/sbin/sshd [listener] 0 of 10-100 startups (sshd) | \-+= 34346 root sshd: sakae [priv] (sshd) | \-+- 15860 sakae sshd: sakae@ttyp0 (sshd) | \-+= 22133 sakae -ksh (ksh) | \-+= 61241 sakae pstree | \-+- 97478 sakae sh -c ps -kaxwwo user,pid,ppid,pgid,command | \--- 12684 sakae ps -kaxwwo user |-+= 02529 sakae tmux: server (/tmp/tmux-1000/default) (tmux) | |--= 66798 sakae -ksh (ksh) | \-+= 62832 sakae -ksh (ksh) | \--= 63551 sakae emacs (emacs-27.1)
この状態から、tmux a すると、クライアントが動き出して、サーバー側に預けてある、居残り組のshellやら、emacsが復活するとな。尚、サーバー側で管理してる物が全て無くなると、サーバーを動かしておく意味が無いので、サーバーは消滅する。
初回のtmux起動時に、tmux -v とかすると、ログが、起動した所に出て来る。
vbox$ wc *.log 42 322 2572 tmux-client-79191.log 222306 1258567 17607754 tmux-server-2529.log
まずは、クライアントのログ
1614718861.209234 client started (79191): version openbsd-6.8, socket /tmp/tmux-1000/default, protocol 8 1614718861.209361 on OpenBSD 6.8 GENERIC#5; libevent 1.4.15-stable (kqueue) 1614718861.209605 flags are 0x18010000 1614718861.209667 socket is /tmp/tmux-1000/default 1614718861.209714 trying connect 1614718861.209816 connect failed: No such file or directory 1614718861.209886 lock file is /tmp/tmux-1000/default.lock : 1614718861.212840 client loop enter 1614718861.624550 peer 0x7e2fb000 message 207 1614718861.624586 sending message 208 to peer 0x7e2fb000 (0 bytes) 1614722606.516370 client loop exit
次は、長ったらしい、サーバーのログ
1614718861.217785 server started (2529): version openbsd-6.8, socket /tmp/tmux-1000/default, protocol 8 1614718861.217937 on OpenBSD 6.8 GENERIC#5; libevent 1.4.15-stable (kqueue) 1614718861.218302 input_key_build: 0x1000000003 (PasteStart) is \033[200~ 1614718861.218333 input_key_build: 0x1000000004 (PasteEnd) is \033[201~ 1614718861.218353 input_key_build: 0x1000000099 (F1) is \033OP 1614718861.218374 input_key_build: 0x100000009a (F2) is \033OQ : 1614723977.801724 server_client_reset_state: cursor to 0,30 1614723977.801753 server_client_check_pane_buffer: pane %0 is on 1614723977.801777 @0 active pane changed 1614723977.801810 @0 name timer already queued (73380 left) 1614723977.801834 server_client_check_pane_buffer: pane %1 is on 1614723977.801859 @1 active pane not changed
左側の巨大な数値はepocだろう。意味なく確認してみると
vbox$ date -r 1614718861 Wed Mar 3 06:01:01 JST 2021 vbox$ date -r 1614723977 Wed Mar 3 07:26:17 JST 2021
起動したのは、朝も早からの6時で、それからうだうだと7時半近くまでやってるとな。このログを精査すれば、挙動が分かるとな。
tail -f tmux-server-2529.log したら、再帰(こんな所でも)しちゃって、止まらなくなっちゃった。どんなログを吐いているかと言うと、
1614724465.174770 screen_write_collect_end: 80 1614724464.720199 input_parse_buffer: %0 ground, 8021 bytes: \\r\\n1614724464.6710 (at 0,30) 1614724465.174800 screen_write_collect_add: wrapped at 80,30 1614724465.174825 screen_write_linefeed: at 80,30 (region 0-30) 1614724465.174850 screen_write_collect_scroll: at 80,30 (region 0-30)
再帰の片鱗がみえまーーす。
watch server
長い調査を経て、サーバーのログを見て桶が結論ぽいけど、伝統の観光をする。 こんな事もあろうかと、シリアル接続出来る口を用意してあるのさ(無理しなくても、2個目のPuTTYでおk)。お世話になるのは、tera termのシリアル接続。rootでログインして、
vbox# gdb ./tmux -p 2529 -q Reading symbols from ./tmux...done. Attaching to program: /home/sakae/tmux, process 2529 Reading symbols from /usr/lib/libutil.so.15.0...done. Reading symbols from /usr/lib/libcurses.so.14.0...done. Reading symbols from /usr/lib/libevent.so.4.1...done. Reading symbols from /usr/lib/libm.so.10.1...done. Reading symbols from /usr/lib/libc.so.96.0...done. Reading symbols from /usr/libexec/ld.so...done. [Switching to thread 414703] kevent () at /tmp/-:3 3 /tmp/-: No such file or directory. (gdb) bt #0 kevent () at /tmp/-:3 #1 0x07d13c10 in kq_dispatch (base=0x71f9ac00, arg=0x7c561600, tv=0xcf7c7a98) at /usr/src/lib/libevent/kqueue.c:198 #2 0x07d14d94 in event_base_loop (base=0x71f9ac00, flags=1) at /usr/src/lib/libevent/event.c:474 #3 0x07d14b04 in event_loop (flags=1) at /usr/src/lib/libevent/event.c:409 #4 0x17d36990 in proc_loop (tp=0x78a3e400, loopcb=0x17d515f0 <server_loop>) at proc.c:202 #5 0x17d50dd9 in server_start (client=0x60309c00, flags=402718720, base=0x71f9ac00, lockfd=6, lockfile=0x6ffa9b80 "server.c:py.c:dowhile (rswitch (fork()) {+ px, 1, ®match, eflags) == 0) {ct c", '\337' <repeats 48 times>, "\002") at server.c:235 #6 0x17cc4d41 in client_connect (base=0x71f9ac00, path=0x41cb4000 "/tmp/tmux-1000/default", flags=402718720) at client.c:161 #7 0x17cc3cb6 in client_main (base=0x71f9ac00, argc=0, argv=0xcf7c7edc, flags=402718720, feat=0) at client.c:292 #8 0x17d6205a in main (argc=0, argv=0xcf7c7edc) at tmux.c:502
これでやっと、観光出来るぞ。gdbのコマンド待ちになってる時は、当然、クライアント側ってか、緑の帯が出てる端末での入出力は、待ったをかけられているよ。
from log
観光も中々進まないので、ショートカットして、tmux clock-mode した時の挙動をログから抽出してみる。
vbox$ grep clock-mode tmux-server-5027.log 1614726467.258721 yylex_token: clock-mode 1614726467.258848 cmd_parse_build_commands: argv[4]=clock-mode 1614726467.259021 cmd_parse: bind-key: argv[4]=clock-mode 1614726467.259064 cmd_parse_build_commands: bind-key -N "Show a clock" t clock-mode 1614726467.448144 yylex_token: clock-mode 1614726467.448170 cmd_parse_build_commands: 0 clock-mode 1614726467.448193 cmd_parse_build_commands: argv[0]=clock-mode 1614726467.448218 cmd_parse: clock-mode: argv[0]=clock-mode 1614726467.448244 cmd_parse_build_commands: clock-mode 1614726487.855375 cmd_unpack_argv: argv[0]=clock-mode 1614726487.855434 cmd_parse_from_arguments: argv[0]=clock-mode 1614726487.855472 cmd_parse_from_arguments: at 0: argv[0]=clock-mode 1614726487.855519 cmd_parse_build_commands: 0 clock-mode 1614726487.855543 cmd_parse_build_commands: argv[0]=clock-mode 1614726487.855568 cmd_parse: clock-mode: argv[0]=clock-mode 1614726487.855600 cmd_parse_build_commands: clock-mode 1614726487.855628 cmdq_get_command: [clock-mode/0x4a99b180] group 1544 1614726487.855651 cmdq_append </dev/ttyp1>: [clock-mode/0x4a99b180] 1614726487.855769 cmdq_next </dev/ttyp1>: [clock-mode/0x4a99b180] (0), flags 0 1614726487.855803 message: /dev/ttyp1 command: clock-mode 1614726490.728035 /dev/ttyp0: vbox$ ./tmux clock-mode
まだまだ続きそうなので、次回に回す。
ruby debug
この間、目を付けておいた技術評論社の雑誌が、手に入らなかった。入荷数が少なくて売り切れちゃったのかな? ruby 3x3 の特集ですから。
本屋に行ったついでに、コンピュータ関係の棚を覗いたら、見事にpython一色でした。それも機械学習をターゲットに、数学と絡めたものとか。オイラーは図書館から借りてきた、中高生の数学特集ぐらいで、いいな。回転行列まで出て来ていたぞ。常識なんですな。
常識と言えば、rubyのデバッグの常識を知っておこう。 Rubyのいろいろなデバッグ方法
いつも、byebugが使えるとは限らないからね。こういう心がけも、災害対策の一環になるのかな。