指向性メモ::Latest Notes

ページ情報
制作日
2004-07-07T09:52:18+09:00
最終更新日
2015-08-18T08:49:21Z
ページ内目次

最近の5件分。

ターミナルで動くプログラムをウェブアプリケーションとして共有するツール「GoTTY」を作った

Created:
2015-08-18T08:49:21Z

デバッグセッションなどで「ターミナルの画面を共有したいけど、tmuxのセッション共有の設定をするのは面倒くさい」ということがよくあるのと、モニタリングツールなどをいじっていると「とりあえずはtopとtailの結果を大画面に表示しておければそれでいいんだ」のようなことがあるので、ターミナルの出力をそのままウェブで共有するツールを作った。

インストールはGithubのReleasesページから直接バイナリをダウンロードしてくるか、go getもしくはHomebrewで行うことができる。

# go getの場合
$ go get github.com/yudai/gotty

# Homebrewの場合
$ brew tap yudai/gotty
$ brew install gotty

使い方は非常にシンプルで、共有したいコマンドの前にgottyを追加するだけで良い。

# topコマンドをウェブアプリケーションにする
$ gotty top

gottyコマンドを実行するとWebサーバがポート8080で起動するので、あとはブラウザでアクセスすれば実行されているコマンドが表示される。

デフォルトでは安全のためにクライアントからの操作は受け付けないようになっている。-wオプションをつけることでクライアントからの操作を受け付けるようにすることも可能だが、実行するコマンドによっては非常に危険な状態となるので注意が必要となる。

現在の実装では、クライアントがブラウザでアクセスするたびにgottyに与えられたコマンドが新しく起動するので、画面の共有に使いたい場合にはtmuxやGNU Screenなどと組み合わせて使う必要がある。例えば、tmuxの場合は次のようなコマンドでgottyを起動する。

$ gotty tmux new -A -s gotty top

画面の操作が必要な場合は、もう1枚のターミナルを開いてtmuxのセッションにアタッチすればよい。この場合、ブラウザ上のクライアントには操作を許さずに、ターミナルから直接tmuxに接続しているクライアントのみに操作を許すことができる。

$ tmux new -A -s gotty

将来的には簡単な画面共有機能をgottyにも実装するかもしれない。

クライアントごとに別のプロセスが起動することを利用して、Dockerと組み合せて遊ぶ事もできる。例えば以下の様なコマンドを実行すると、クライアントごとにDockerコンテナが立ち上がり、シェルで遊ぶことができる。

$ gotty -w docker run -it --rm busybox

GoTTY自体の実装は非常に単純で、PTYとブラウザをつなぐ単なるWebSocketサーバになっている。ブラウザ上で動作するターミナル部分はGoogleがChromeOS用に開発している「hterm」をそのまま流用しているので、GoTTY用に書いたコードはほとんど存在しない。htermの出来が非常に良いおかげで、大量のコードを書かずに欲しい機能を実装することができた。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo.php/2015/08/18/174921

TmuxでSSHセッションを量産するツールを作った

Created:
2015-07-25T13:51:20+09:00

tmuxを使っていると、今開いているSSHセッションを維持したままペインもしくはウィンドウを増やしたいことがある。たとえば、SSH先でviを開いてしまったが、同じホストで別のコマンドを実行したくなることはよくある。このような場合、viを1度閉じるか、新しいペインを分割して再度SSHコマンドを実行するしかないのだが、どちらにせよ若干の手間がかかる。サーバ管理をしているとこのようなことがよく発生するので、現在のSSHセッションを複製するための簡単なツールを作った。

実行ファイルは単なるシェルスクリプトなのでパスの通ったディレクトリに設置するだけでよい。環境によってはpgrepのインストールが別途必要となるかもしれない。

名前はいわゆる「cdd」のSSH版ということで「sshh」にした。引数の扱いもほぼ同じで、たとえばsshh 1というコマンドを実行すると、同一セッションの1番ウィンドウで実行中のSSHコマンドを現在のペインで実行する。ペイン指定したい場合はssh 1,2のようにすると、1番ウィンドウの2番ペインを指定できる。ペインが指定されなかった場合のデフォルト値は0番になる。本当であれば「1つ前に選択していたペイン」をデフォルトにしたいところではあるが、現在のところ値を取得する手段がない。なお、指定されたウィンドウもしくはペインで実行されているコマンドがSSHでない場合も、そのコマンドを現在のペインで実行することが出来る。この場合は実行前に確認のプロンプトが表示される。

このコマンドは単体で使うよりも、tmuxのショートカットキーに割り当てることで効果を発揮する。単体で使用する場合、コマンドヒストリーを「SSH」で検索する代わりにsshhコマンドを実行するだけなので、大した省力化は実現できない。ショートカットキーを設定する場合は、たとえば.tmux.confに以下のように記述する。

# C-sで縦分割したペインにSSHセッションを複製する
bind-key C-s run-shell "tmux split-window -h \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}' ENTER

# C-wで横分割したペインにSSHセッションを複製する
bind-key C-w run-shell "tmux split-window -v \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}' ENTER

# C-nで新規ウィンドウにSSHセッションを複製する
bind-key C-n run-shell "tmux new-window \"SSHH_INDEX=$(tmux display -p \"#{window_index},#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}' ENTER

上記の設定の場合、生成されたウィンドウもしくはペインはZSHで起動される。好みのシェルに合わせて、必要に応じてbashなどに置き換える必要がある。

なお、この例ではsshhコマンドの実行はsend-keysを用いて行われているため、ユーザーが手でコマンドを入力した場合と変わらない動作になる。言い換えると、SSHセッション終了後は、普通にSSHコマンドを終了したときのように、シェルに制御が戻る。用途によってはSSHの終了時にペインも閉じてしまいたいこともあるかもしれない。その場合は次のように書いておくと、他にペインが存在しない場合を除いて自動でペインを終了することが出来る。

bind-key C-s run-shell "tmux split-window -h \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}; if [ "$(tmux list-panes | wc -l)" -gt 1 ]; then exit; fi' ENTER

bind-key C-w run-shell "tmux split-window -v \"SSHH_INDEX=$(tmux display -p \",#{pane_index}\") zsh -l\"" \; send-keys ' sshh ${SSHH_INDEX}; if [ "$(tmux list-panes | wc -l)" -gt 1 ]; then exit; fi' ENTER
Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo.php/2015/07/25/135120

配列を代入すると値がコピーされる言語はいくつか存在する

Created:
2013-03-08T01:56:30+09:00

てゆうか配列の入った変数を他の変数に代入すると値がコピーされるのもワロスなポイント

PHPの配列はCの構造体みたいな扱いなんだろうか?

いずれにしてもそんな仕様のメジャーな言語は見たことが無い

Adaの場合、配列のAssignmentは値のコピーとなる。C言語と違い配列のポインタ(AdaのAccess)と配列が明確に区別されるため、混乱が生じにくい。

with Ada.Text_IO; use Ada.Text_IO;

procedure Array_Test is
   type Sample_Array_Type is array (1 .. 3) of Integer;
   From_Array : Sample_Array_Type;
   To_Array   : Sample_Array_Type;
begin
   From_Array := (1, 2, 3);
   To_Array   := From_Array;
   From_Array := (others => 0);

   for I in Sample_Array_Type'Range loop
      Put_Line(Integer'Image(From_Array(I)));
   end loop;

   for I in Sample_Array_Type'Range loop
      Put_Line(Integer'Image(To_Array(I)));
   end loop;
end Array_Test;
% ./array_test
 0
 0
 0
 1
 2
 3

Fortranなども配列を=で代入すると値がコピーされようだ。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo.php/2013/03/08/015630

UbuntuのvirtualカーネルでKVMが実行できない問題

Created:
2012-07-30T19:49:16+09:00

Ubuntuのイメージをpython-vm-builderなどで作る際にflavorをvirtualにすると、カーネルモジュールが最低限しかインストールされない。これにより、kvm.koなどのKVMの実行に必要なモジュールもインストールされないため、ゲスト上でKVMを実行する場合(nested KVM)にmodprobeに失敗する。この問題はlinux-image-extra-virtualパッケージをインストールすることで解決できる。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo.php/2012/07/30/194916

GNU Screenを改造してhardstatusを積極的に使う

Created:
2012-05-14T23:50:45+09:00

今時はTmuxを使うらしいですが、GNU Screenでもうちょっとがんばってみます。今回はScreenを少しだけ改造してzshとの相性を強めてみました。


Screenのウィンドウタイトルを直前に実行したコマンドで更新している人は多いと思います。最近まで私もそうしていました。シェルがzshならpreexec内でscreen用のエスケープコードを出力するのが定番でしょうか。この方法の欠点はエスケープコードがscreen専用なので、何も対策をしていないと、例えばxtermから直接アクセスした時に変なエコーバックが表示されてしまうところです。もちろん、普通は環境変数をみてscreenから繋いでるときだけウィンドウタイトルの更新を行うように設定しているでしょうから、大抵は問題になりません。手元の.zshrcでも$STYという変数をみて切替を行っていました。なんで$TERM見ないのっていう意見もあるかもしれませんが、歴史的経緯によってScreen上でもxterm-256colorになってる人も多いかと思います。この場合、少なくとも私が知る範囲では$STYを見るぐらいしかScreen経由でシェルを起動しているかを見分ける方法がありません。

さて、この状況で困るのがSSH先のシェルからは接続元のシェルがScreen経由で起動されているかどうかを簡単に見分ける方法が無いことです。見分けが付かない場合は安全側に倒すのが普通ですから、SSH先のシェルからローカルScreenのタイトルを更新することが出来ません。こうなると沢山SSHの接続ウィンドウを開いて、特に同じホストが多かったりする場合に困ります。ウィンドウタイトルがみんな同じになってしまうので、どのウィンドウで何をしていたのかが分からなくなってしまい、しばしば迷子になるのです。できればローカルでコマンドを打ったときのように、リモートのシェル内で打ったコマンドをScreenのウィンドウタイトルに反映したいところです。でもでも、リモート側ではScreen経由かどうか見分けが付かないのでエスケープコードを出すわけにもいかず、それは夢という事になってしまいます。

解決策はhardstatusを使うことです。普段ウィンドウタイトル(.screenrcでいうところの%t)を使っていると忘れがちですがScreenではhardstatus(%h)の値も使うことが出来ます。hardstatusの値はネイティブな(?)ターミナル、例えば本当のxtermやTerminal.appにおけるウィンドウのタイトル部分に設定されるべき値が代入されています。言い換えれば、ScreenでもxtermでもTerminal.appでも共通のエスケープコードで更新できるのがhardstatusなのです。もちろん世の中にはxterm意外にも色々なターミナルがあるので、そういうものを使ってる人には通用しないのですが、少なくとも普段xterm互換のターミナルしか使っていない私のような人間には、Screenでもなんでも対応できるエスケープコード(\e]0;)は便利なわけです。

ただ、%h変数には1つ問題があります。それは%tに対する%wに当たる変数が存在しないことです。%wはアクティブなウィンドウ以外のウィンドウリストですから、これに対応する変数が存在しないということは、hardstatusだけではウィンドウリストが作れないと言うことです。アクティブなウィンドウの分は%hで済みますが、他のウィンドウのhardstatusが取れなければ結局%tに、つまりはScreen依存のエスケープコードに依存することになります。これは困った。

仕方がないのでScreenを改造して、hardstatus版の%wとして、%Xという変数を追加しました。改造版のコードはgithubに置いてあります。.screenrcにオリジナルの変数を追加すると他のホストで使い回せずに困ることもあるので、%wでhardstatus版のウィンドウリストを表示するように上書きしたoverwrite_wブランチも用意してあります。

そんなわけで、この改造版Screenを使うとSSH先でも安心してウィンドウタイトルの更新が出来るようになります。やりましたね。

Comments
0
Trackbacks
0
PermaLink
http://yudai.arielworks.com/memo.php/2012/05/14/235045
連絡先、リンク、転載や複製などについては『サイト案内』をご覧ください。Powered by HIMMEL

I ♥ Validator