2009年10月17日土曜日

PythonのC++インターフェイスboost.pythonの使用例

少し前に、PythonのC/C++へのインターフェイス作成手段としてはctypesとboost.pythonが良さそうだという話を書いた。その後ctypesの使用例をメモった。

すでにCで開発されたライブラリがある場合にはctypesを使った方が楽だと思う。しかしC++や、あるいは自分でさらから書く場合にはC++を使いたいところ。場合によっては基本的にはC++で開発し、その後より楽に使用するためにPythonからも使えるようにしたい事もある。
そういう訳で、個人的にはPythonのC/C++インターフェイス開発にはboost.pythonが一番だと思う。Python自体に標準的に付随するものではない点だけが気になるところだが、とても簡単にC++のプログラムへのインターフェイスが作成できる。

というわけで、boost.pythonの使用例をメモ。

まず、C++で適当にクラスでも作る。ここでは簡単のためにsuffixarray.hhというヘッダファイルだけ。

// suffixarray.hh
#include <iostream>
#include <algorithm>
using namespace std;

class SuffixarrayCmp : binary_function<const int,const int,bool> {
protected:
    const wchar_t* _buff;
    
public:
    SuffixarrayCmp(const wchar_t* buff) : _buff(buff) {};
    bool operator()(const int x, const int y) const
    {
 return wcscmp(_buff+x, _buff+y) < 0;
    }
};

class SuffixArray {
protected:
    wchar_t* _buff;
    int      _size;
    int*     _idx;

public:
    SuffixArray() : _buff(NULL), _size(0), _idx(NULL)
    {};
    ~SuffixArray() {
 if (_idx != NULL) delete[] _idx;
    };

    bool construct(wchar_t* buff, const int size) {
 _buff = buff;
 _size = size;
 _idx = new int[_size];
 if (_idx == NULL)
     return false;

 for (int i=0; i<size; i++) {
     _idx[i] = i;
 }
 sort(_idx, _idx+size, SuffixarrayCmp(_buff));
 return true;
    };
    
    int getSize() const {
 return _size;
    };
};
このプログラムはC++として完全に独立していて、Pythonとは関係なくコンパイルもできるし利用もできる。wchar_t型(ワイド文字)の配列、要するに日本語文字列などを想定して、そのサフィックスアレイを作るもの。日本語は今はUTF-8を使う事が多いようだけど、メモリ節約にはなってもランダムアクセスには不便なのでwchar_tの配列とした。
また、私の使っているPython(2.5.4 on Debian GNU/Linux x86_64)ではunicode文字列はUTF-16で表現しwchar_t配列に格納しているようなのでそれに合わせたという意味もある。
そうでない場合に対応するにはテンプレートにでもしておけば良いのかな。そういう意味もあってヘッダファイルのみにした。

次に、これをPythonから使えるようにする必要がある。
construct()メソッドにはwchar_t*を渡す必要があるので、Pythonのunicodeからの橋渡しをする必要もある。
そのためのファイルがsuffixarray_py.cc。
// suffixarray_py.cc。
#include "suffixarray.hh"
#include <boost/python.hpp>
using namespace boost::python;

class PySuffixArray : public SuffixArray {
public:
    bool construct(PyObject* op) { // op must be a unicode object
 _buff = (wchar_t*)(PyUnicode_AS_DATA(op));
 _size = PyUnicode_GET_SIZE(op);
 return SuffixArray::construct(_buff, _size);
    };
};

int compare(PyObject* x, PyObject* y) { // x, y must be unicode objects
    wchar_t* xp = (wchar_t*)(PyUnicode_AS_DATA(x));
    wchar_t* yp = (wchar_t*)(PyUnicode_AS_DATA(y));

    return wcscmp(xp, yp);
};

BOOST_PYTHON_MODULE(suffixarray)
{
    class_<PySuffixArray>("SuffixArray")
 .def("construct", &PySuffixArray::construct)
 .add_property("size", &PySuffixArray::getSize)
 ;

    def("compare", compare);
};
PySuffixArrayクラスはSuffixArrayクラスのconstruct()メソッドをオーバーライドしているが、その中でPythonのユニコードからwchar_t*本体とサイズを取り出し、SuffixArrayのconstruct()に渡している。
unicode文字列の橋渡しもboost.pythonがやってくれるようになるとこんな面倒も必要ないのだろうけれど...
整数や普通の文字列の橋渡しはboost.pythonが面倒をみてくれるようだ。

BOOST_PYTHON_MODULEというところがPythonから見えるモジュールの定義。
大体見れば分かると思うが、suffixarrayモジュールの中にSuffixArrayというクラスを定義し、construct()メソッドをPython側からも見えるようにしている。
ここでsizeは「プロパティ」である。
実はadd_property()には三つめの引数を渡すこともできるが、その場合には
.add_property("pyname", &cplusclass::getter, &cplusclass::setter)

として、プロパティのgetter/setterを設定できるようになっている。setterを省略するとそのプロパティ(attribute)はread onlyとなる。
最後のcompare()は、クラスではなくC++の関数を取り込むためのもの。

これをコンパイルするわけだが、boost.pythonのチュートリアルだとbjamというビルドツールを使っている。実は以前、boost.pythonを触ろうとした時にそれが嫌で一瞬でやめてしまった事がある。
要するにmakeの代わりだが、最近はJavaだとAnt、boostだとbjamと、「進化」したビルドツールがよく使われるようになってきているようだ。
しかし、面倒くさい...
サブプロジェクトを含むようなプロジェクトのビルドだとか、そういうことのために色々進化しているらしいが、めんどい...
XMLみたいの書かされるのはとてもめんどいです...
あんなもの人間様が手で書くもんじゃないよね....ツール使う?それもめんどい...

ということで時代に取り残されている気もしないでもないけれど、馴染のmakeにした。
Makefileは個人的にあれこれしているので、ここでは実際のコンパイルコマンドがどうなっているかだけ。オプションもboost.pythonのコンパイルに必要なものだけ。
$ g++ -c -o suffixarray_py.o -I/usr/include/python2.5 -fpic suffixarray_py.cc
$ g++ -shared -o suffixarray.so suffixarray_py.o -lboost_python

これでsuffixarray.soというシェアードオブジェクトができる。

これをPythonから使うのはこんな感じ。
>>> import suffixarray as S
>>> s = unicode('これはぺんですがあれはぺんしるです。', 'utf-8') ## 文字コードは環境依存
>>> sa = S.SuffixArray()
>>> sa.construct(s)
True
>>> sa.size
18
>>> sa.size = 10
Traceback (most recent call last):
File "", line 1, in 
AttributeError: can't set attribute
>>> 
もしPythonからの使い勝手が悪ければ、ラッパークラスを作ってもいい。面倒なのでboost.pythonのレベルで解決した方が良さそうだけど。

なお、Pythonのunicodeオブジェクトはimmutableだが、こうしてC++に渡してあげると自由に変更できる。

Bloggerにコード片を載せる時に利用できるもの・・・?

ちょこちょことコード片を載せてきたけれど、なんだかpreタグだとかじゃうまくいかなかったりでとても嫌。
プリティファイしてくれないまでも記号類やインデントも込みでそのまま表示してくれる程度の機能は備わっているものなのだろうと思ったんだけど、GoobleのやってるBloggerにもそういう機能はないんだな・・・。他でブログ書いたこともないので他所にもあるのかどうか知らないけれど、まあ、コード片載せるなんて一般人からみたら極一部の少数派なんだろうから、きっとないんだろう(それともあるのかな?)。
が、こんな記事を見付けた。

ハイライトもGoogle流 - "google-code-prettify"でソースコードに色付けを


というわけで、この記事を参考にやってみる。ことにした。が、なんだか記事読んでもわかんない。
自前のウェブページじゃなくてBloggerでそれやりたいんだけど・・・

で、ぐぐってみるとそのやり方を説明してくれているサイトがあった。
しかもさっきのツールを一発でBloggerに追加してくれるリンクまである。ありがたい。
が、追加してみてもなんだか効いてないような・・・
ということで、そのページには前から困ってた<と>を変換してくれるツールもあったので、それだけ利用させてもらうことにした。

2009年10月12日月曜日

EmacsのWanderlustからGmailの読み書き(.foldersの設定)

メインのノートPCを常に持ち歩いていた頃には、メールは全てノートに取り込み、Emacsの中のGnusやWanderlustで読み書きしていた。
しかし今では主にGmailを使っている。
どこからでもケータイからでもアクセスできてとても便利だし、大きな不満はないのだが、一つ嫌なことが・・・
このブログの文章を書くのも同じだが、文章をブラウザで書くのがイヤ・・・
とても書き難い。文章のチェックすらする気にならない。

なので時々はEmacsで文章を書いてから貼り付けたりすることもある。

それで、いっそのことEmacsからGmailを読み書きできるようにしておくことにした。
ググると結構そんな話があり、メイラはMewかWanderlustと言ったところのようだ。
となれば以前使っていたWanderlustだという事で、ヒットしたブログなどを参考に久し振りにWanderlustの設定をした。

.emacsや.wlの基本設定などはヒットした内外のページを参考にすれば何の問題もなかった。

しかし.foldersが良く分からなかった。
特に、皆「受信トレイ」位しか見ていないような記述ばかりで、Gmailのラベルはどうすればいいのか・・・と。
なので一応メモっておく。こんな感じ。

%Inbox "受信トレイ"
%Gmailのラベル1 "ラベル1"
%Gmail label2 "ラベル2"
カテゴリ1 {
%Gmailのラベル3 "ラベル3"
%Gmail label 4 "ラベル4"
}
%[Gmail]/スター付き "スター付き"
%[Gmail]/下書き "下書き"
%[Gmail]/すべてのメール "すべてのメール"
%[Gmail]/送信済みメール "送信済みメール"
%[Gmail]/迷惑メール "迷惑メール"
%[Gmail]/ゴミ箱 "ゴミ箱"


「%Inbox "受信トレイ"」は、Gmailの「受信トレイ」をWanderlust上でも「受信トレイ」と表示するためのもの。
「%[Gmail]/スター付き "スター付き"」は、Gmailの「スター付き」をWanderlustでも「スター付き」と表示するためのもの。
「%Gmailのラベル1 "ラベル1"」は、Gmailの「Gmailのラベル1」という名前のラベルをWanderlust上では「ラベル1」と表示するためのもの。
「%Gmail label2 "ラベル2"」は、Gmailの「Gmail label2」という名前のラベルをWanderlust上では「ラベル2」と表示するためのもの。
カテゴリ1というのは、Wanderlustでフォルダをグループ化して名前を付け、入れ子表示するためのもの。

また、「[Gmail]/xxxx」というのは、自分で作ったラベルではなく、Gmailのシステム側が用意してあるxxxxというラベルという意味だ。Inboxだけは例外。Gmailの「設定-ラベル」画面で確認できる。

うっかりGmailに無いラベル名を指定するとwlで接続に行った時にGmail側にそういう名前のラベルを作ろうとするので注意。間違って作ってしまった場合にはGmail側で削除する。

もう一つ、Gmailのラベル名に":"(コロン)を使っていると問題が発生するので注意。
aa:bbbなんて名前にすると、.foldersに「%aa:bbb」と記述する事になるが、bbb上のユーザーaaと認識するようで、aaのためのパスワードを要求されたりする。

最後に、念のためメモ。
日本語ラベルを使っているが、.foldersの記述もEmacs(23)のデフォルトエンコーディングも全てUTF-8にしてある。Emacsの事なので、それにWanderlustの開発者は日本人だった気がするので、その辺りはうまくやってくれていそうだが一応注意。


キーバインドなど細かいカスタマイズをしないとまだ快適とは言えないが、とりあえずWanderlustで読み書きできるようになった。サマリーで"$"キーでマークするとそれがGmailのスターと同期もしてくれるし、ひとまずOK。細かいことは普通にGmailでやればいいし。

古いシステムにOpenSSH5.1を入れる

sshで多段ログインする話を書いてきたので、ちょっと脇道になるがその中で必要だったこともメモ。

homepc - (mansion LAN - the internet) - gw1 - gw2 - workpc

という環境下で、gw1, gw2, workpcにそれぞれsshでログインできる状態だった。
しかしgw2のsshクライアントのバージョンが古く、そのため-Yオプションが無いためtursted なXの転送ができずにXアプリが使えない状況だった。

そのためgw2にOpenSSH 5.1をインストールした。
もちろん自分のホームディレクトリ以下にインストール。
幸いgccは使えたので助かった。

OpenSSHのコンパイルに新しめのzlibとopensslも必要だったので、それらもインストール。
いずれもソースは手元のDebianマシンで

$ apt-get source zlib

のようにしてゲット。それらをgw2に持っていき、

$ ./configure --prefix=/HOMEDIR/local
$ make
$ make install

などとした。
順番はzlib, openssl, openssh。
OpenSSHのconfigureの時には

$ ./configure --prefix=/HOMEDIR/local --with-zlib=/HOMEDIR/local

とした。OpenSSLやzlibでも何かオプションが必要だった気がするけど、忘れた・・・試してエラーが出てくれば分かるはずだからいいか・・・

最後にPATHに/HOMEDIR/local/binを追加して完了。
というか、gw2、そろそろなんとかした方が・・・

sshでリモートからemacsclient

という訳で、sshで自宅から職場の強力マシンに入れるようになったし、rsyncで同期もできるし、Xアプリも使えるようになった。自宅にいるも職場にいるもあまり意識せずにほぼ同じ感覚で作業できるようになったわけだ。

しかしもう一つ。

作業の多くをEmacsの中で行なっているので、当然Emacsは常に立ち上げっぱなし。終了して起動し直す事なんて特に理由がない限りはない。当然職場PC上でもEmacsは常時起動しっぱなしだ。
で、自宅から職場PCに入って一番最初に起動するのもEmacsなわけだが、職場でやっていた作業の続きができない。diredでディレクトリを開いていたりファイルを開いていたり、shell-modeの中で何かやっていたり、Pythonその他を動かしている事もある。場合によっては時間のかかる処理を流しっぱなしにしている事もある。
その様子を見たり続きをしたいこともあるが、それにはまさにその、職場PCで動いているEmacsに入らないといけない。sshログインして別のEmacsプロセスを起動しても無理だ。

そこで思い出したのがemacsclinet。(以前はgnuclientという名前だったような気が...)

職場PCで使っているEmacsで M-x server-start とすると、Emacs serverが起動する。
そして自宅からsshで職場PCにログインしたら

workpc$ emacsclient -d localhost:10.0 test.txt

のようにすると、手元(homepc)でEmacsのウィンドウ(フレーム)が開いてtest.txtファイルのバッファが開く。そしてそれは職場PCで動いているEmacsと同じプロセスであり、当然他のバッファも見られるしその中で作業もできる。本当に中途半端な状態で職場を出て、その続きを自宅でしたり、あるいはその逆もできる訳だ。

なお、-dで指定しているのはXのディスプレイだが、サーバーEmacsと同じディスプレイの場合には既に開いているEmacsの中でtest.txtバッファが開く。今回のように別のディスプレイを指定すると、新たなフレームが開く。

指定したファイル(test.txt)のバッファをkill-bufferすると、emacsclient自身は終了する。しかしフレームは残るのでその中で作業を続けることは普通にできる。
が、先にフレームを閉じてしまいtest.txtのバッファをkill-bufferし忘れると、なぜかemacsclient自体は終了しない。起動時にファイル名を求めるというのはそういう意味だったのか...
まあプロセスを殺せばいいんだけど、ちょっとしっくりしない。

ちなみに、localhost:10.0 というのは、sshで入った時のこちら側に割り当てられたDISPLAYだ。試している限りでは、一段sshと二段sshでは自動で環境変数DISPLAYが正しく設定されているが、三段sshでは自動では":0.0"と正しい値が設定されていない。なので自分で

workpc$ export DISPLAY=localhost:10.0

のようにしないとXのウィンドウを手元に持ってこられないので注意。
また、"10.0"の"10"の部分はログイン状況によって異なるようだ。
一段・二段sshの時の値から推測すると、10あたりから順番に大きな数を使うようになっているようだが、この辺りの正式な説明は見付けられなかった。

なにはともあれ自宅から職場PCで起動しているEmacsに横入りして作業できるようになった。
普通のターミナル上でならばscreenでも使うところなのだろうが、Emacsメインの人間にとってはemacsclientの方が100万倍便利。

ただしEmacsでサーバーを起動しておかないといけないので、うっかり忘れないように.emacsに下のコード片を加えて自動で起動するようにしておいた。

(when (equal (getenv "DISPLAY") ":0.0")
(server-start))

sshの圧縮オプション

いちおう追加メモ。

前回sshで二台越しのログインをし、Xのウィンドウも表示できるようになった。Emacsなどはほとんどリモートと意識しないで使える。
しかし、例えばgimpなどのお絵描き系の場合、やはりマウスの動きに2テンポ位遅れる感じだ。まあ仕方ないだろう。

次に試しにリモートのLinux上のVMware(の中のVista)を使ってみた。
結果から言えば、あまりガシガシ使う気にはならないが、Wordで簡単な文書を書いたりPowerPointで質素なスライドを作る位なら十分使える。オートシェイプを入れたり動かしたりするのはやはりとても苦しいが。またウィンドウズの装飾的な表示効果はなるべく切っておいた方が良さそうだ。
とりあえず万一自宅や職場のウィンドウズ環境がおかしくなった場合の最後の逃げ場にはなりそうだ。

ただしこれはsshに"-C"オプションを付けて圧縮した上で転送するようにした場合のこと。
-Cオプション無しではとてもとても遅くて耐えられなかった・・・

2009年10月10日土曜日

多段sshとrsyncでデータの同期

boost.pythonの利用例をメモっておこうと思ったけれど、最近分かったsshのポートフォワーディングの使い方を先にメモしておく。
そういう機能があるのは知っていたものの、せいぜいsecureなrlogin程度の使い方しかしていなかった。しかし最近やはり使ってみたくなった。

元々自宅と職場を行き来する中で、自宅マシンと職場マシンでのデータの共有という問題があったのだが、これまではノートPCを持ち歩き、基本的にそれを媒介とすることで目的を果たしていた。
自宅や職場でそのノートとrsyncで同期をとるという方式だ。
しかし、次第にノートを持ち歩くのが億劫になってきた。
持ち歩き用のノートはB5の小さなものだから、それはメインの作業場ではなく、自宅や職場ではより速く大きなディスプレイのマシンがメインの作業場になっている。そうなると持ち歩き用ノートはもはやデータを運ぶためのものでしかない。
かつては自宅でも職場でも出先でも同じノートをメインにしてた事があり、その頃にはメールでもデータでもノートが母艦で、時々他のディスクやマシンにバックアップを取っていた。しかし、今ではメールも基本的にはweb上に置いてあり、ケータイからも読み書きできる。もうメールやWebチェックのためにPCを持ち歩く時代ではなくなっている。
出張その他どこかへ出掛けるのでない限り、もはやノートPCはデータ同期の媒介としてしか持ち運んでいなかった。
また、その目的ならば、もはやポータブルHDDや、大容量化してきたUSBメモリで充分だ。

ポータブルHDDやUSBメモリでも、やはり一々それらに同期してから持っていき、またそれから同期するのが面倒になってきた。
そこで、自宅と職場のPCの間で直接同期(rsync)したくなった。

自宅マシンから職場マシンへの三段sshでのログイン



ここで問題になるのが、自宅PCから職場PCへのアクセスだ。
もちろん、直接入れるようにはなっていない。
その間に二台のゲートウェイが挟まっている。それらをgw1, gw2とすると

homepc -(the internet) - gw1 - gw2 - workpc

という形だ。homepcは自宅のマンションLANの中でプライベートIPを振られているので外から直接アクセスすることができない。

この状況で、幸い(というか厄介事の種になっている訳だが...)gw1, gw2にもsshでログインする権利が与えられている。
ただし、インターネット側から直接はgw1にしか入れない。gw2に入るにはgw1に入ってそこからまたsshしないといけない。また、gw1からはgw2にしかsshできない。
gw2からはworkpcにsshで入れる。

workpcからは、gw1にもgw2にもsshで直接入る事ができる。ただしその他のポートには入れない。

こういう状況では、
homepc$ ssh gw1
でまずgw1に入り、次いでそこから
gw1$ ssh gw2
でgwに入り、さらにそこから
gw2$ ssh workpc
とすることで、自宅からworkpcに入ることができる。

しかしめんどい。
要するに三段越しのsshをしたいわけだが、少し調べると、こうすることで簡単に実現できることが分かった。

homepc$ ssh -t -Y gw1 ssh -t -Y gw2 ssh -Y workpc

ただし、gw1, gw2, workpcのパスワードはその順番で聞かれる。
(このあたりは設定で入力無しやパスフレーズを使うように変更できるようだが、今のところ面倒なのでノータッチ)

"-t"オプションは擬似端末を強制割り当てするものらしい。正直よく分からないが、これがないと文句を言われてしまう。
"-Y"オプションはtrusted X11 forwardingを有効にするもので、これを付けるとworkpcで実行しているXクライアントをローカルマシンに表示することができる。
ネット越しなのでいくらか遅いが、職場マシンのXクライアントを自宅でそのまま使えるのでとても重宝する。なお、-Yオプションは古いssh(私が使っているのはOpenSSH)ではサポートされていない。(untrustedな-Xオプションはあるようだ)

自宅マシンと職場マシンのrsync(三段ポートフォワーディング)



さて、これで自宅マシンから職場マシンに、間に二台のマシンを狭んでの三段sshで入れるようになった。
しかし目的は自宅マシン(homepc)と職場マシン(workpc)の間でのrsyncによる同期だ。

これにはポートフォワーディング機能を使う。
ネット上でも色々漁ってみたが、三段越しのrsyncというのはあまり見付からず、一段越しの例を参考にしてもなかなかうまくいかなかった。
正直sshの仕組みやオプションがよくわかっていなかった。今もそうだが。

結局、残念ながらコマンド一つではまだ実現できていないが、とりあえず次のようにすることでなんとか実現できた。

まず、三段sshと同じ要領で、ポートフォワーディングを三段繋ぐ。

homepc$ ssh -t -L 10022:localhost:10001 gw1 ssh -t -L 10001:localhost:10002 gw2 ssh -t -L 10002:localhost:22 workpc

これでworkpcにsshログインし、同時にhomepcのポート10022がworkpcのポート22(ssh)に、間の二台、gw1とgw2に中継されながらフォワードされる。
つまり、この状態でhomepcのポート10022に接続すると、それはworkpcのポート22に接続することになる。

この状態でhomepcとworkpcの間でrsyncを行なうには、次のようにする。

homepc$ rsync -avuSHx -e 'ssh -p 10022' /homepc/source/ localhost:/workpc/dest/

これで、homepcの/homepc/source/ディレクトリ以下がworkpcの/workpc/dest/にコピー(同期)される。実際にはいつも--deleteオプションも付けてソースにないファイルはデスティネーションからも削除している。
また、実際に行なう前に必ず--dry-runオプションを付けて削除される予定のファイルをチェックしている。
もちろん、引数の順番を逆にすればworkpcからhomepcにコピーすることもできる。

これで何も持ち歩かなくてもデータの同期を取ることができるようになった。しかもこれまでも、きっとできるだろうと思いながらやっていなかった、自宅で職場PCのXクライアントのウインドウを表示して使うことも可能になった。

というわけで、一応三段ポートフォワーディングの説明をしておくと、上記のフォワーディングでは、まず

ssh -t -L 1022:localhost:10001 gw1

の部分で、gw1にsshでログインし、homepcのポート1022がgw1の10001にフォワードされる。
localhost:10001とは、gw1から見たlocalhostという名前(or IP addr)のマシンのポート10001という意味で、localhostとした場合にはもちろんgw1自身だ。
そして、この後に続く

ssh -t -L 10001:localhost:10002 gw2

の部分でgw2にログインし、このコマンドが実行されるgw1のポート10001がgw2から見たlocalhostのポート10002にフォワードされる。
ここまでで、homepcの10022はgw2の10002にフォワードされたことになる。
そして最後に

ssh -t -L 10002:localhost:22 workpc

だが、これはgw2で実行される。そしてgw2のポート10002をworkpcから見たlocalhost、すなわちworkpc自身のポート22(ssh)にフォワードする。
これで、homepcのポート10022へのアクセスはworkpcのポート22へのアクセスを意味する事になった。
rsyncのオプションの

-e 'ssh -p 10022'

の部分は、rsyncでsshを用いそのsshでは10022ポートを用いよという意味だ。
結局rsyncへのパラメータである

localhost:/workpc/dest/

では、localhostの10022ポートを使うことになり、これはworkpcへのssh接続ということになる。

という訳だが、以上はあれこれ試行錯誤した結果の自分なりの解釈なので、間違いがあるかもしれない。一応自分なりに一貫した解釈ができるようになったので良しとする。

職場マシンからマンションLAN内の自宅PC(プライベートIP)へのsshログイン



さてさて、ここまでで一段落、どうにか目的は達成されたが、なんとなくポートフォワーディングの使い方が分かってくるともう少し欲が出てきた。
職場から自宅PCにアクセスしたい。

自宅はマンション内LANでプライベートアドレスを割り当てられているので、簡単にはそれはできない。ゲートウェイにログインできるわけでもない。
しかし、多分やってみればなんとかなるんだろうなとは思っていた。

で、上記のようにsshで自宅からgw1やgw2にリンクを張れるなら、それを職場PCから辿っていけばなんとかなるだろうと思って試してみると、案外簡単にできてしまった。

"-L"オプションで実現しているのはローカルフォワーディングと呼ばれるらしい。
今度は、homepcからsshでgw1に入り、これまでとは逆にgw1へのアクセスをhomepcにフォワードしたい。こういう方向のものをリモートフォワーディングと呼ぶらしい。

結論から書くと自宅のマシンで

homepc$ ssh -t -g -Y -R 10022:localhost:22 gw1

としておいた上で、職場で

workpc$ ssh -t -Y gw1 ssh -Y -p 10022 localhost

とすることで、目的は達成された。
なお、workpcからはgw1へもgw2へもsshで入ることができる。
もしgw2にしか入れなければ、自宅からはgw2まで継いでおけば良い。

勝手に切断されないようにする



最後に、sshのコネクションは、何もパケットが流れないと5分程度で自動的に切断されてしまうらしい。
これを防ぐのにも色々な方法があるようだが、面倒なのでターミナルの一つで

$ tload -d 100

としてそのまま放置することにした。実際ロードを見たいことは多いし。

結局一切~/.ssh/以下の設定ファイルはいじらなかったが、設定ファイルをいじればコマンドラインを簡略化できるらしい。
しかしコマンドラインで書けるものならそうする方が好きなのでこのまま。
ただしaliasで単純な名前を付けておくので、簡単に実行することができるようにはなっている。


ということで、間に挟まったマシンにsshで入れる事が必要だが、おそらく何段挟まっていてもこの方法で同期や双方向のログインができると思う。