mosh: MITからモバイル時代のSSH代替品
- ローミング可能
- 断続的な接続でも平気
- ローカルエコーで快適なレスポンス
などの機能をそなえたSSH代替ターミナルソフト。その名も「モッシュ」
iPhone/iPadでウロウロしながらサーバ作業をするのを想定しているようだ。ドキュメントやパッケージの充実度からしてもかなり高質のプロジェクト。こいつら本気でSSHを越えようとしている。
こんな能書き
- IPが変っても大丈夫
- スリープ後もターミナルが生きている
- 遅いリンクでもローカルエコーにより快適
- 要は素早く打ち込んでいるときにスクリーンが止っちゃうことがない
- フルスクリーンモードでも有効
- サーバ側と同期化されていない入力はアンダーラインで記される
- ルート権限がなくてもサーバのインストール可能
- ユーザ認証はSSHに任せている
- 頑丈なターミナルソフト
- Control-Cがいつでもきく
ローミングとか今やっていないので、全ての恩恵にはあずかれないが、いくつか検証できた。
- ラップトップをスリープさせて起した後、ネットワークが接続するとターミナルが生き返った。これだけでも使う価値あり。
- ローカルエコー: 素早く打ち込んでも全くラグがない。これは快適。
これならサーバにつなげっぱなしにしたターミナルをずっと開けていられる。カフェに移動したら再接続とかする必要がなくなる。
これだけでも凄いが、このプロジェクトは単にローミング可のターミナルを作ったのではない。State Synchronization Protocolという状態同期可プロトコルを開発し、moshはそれをもちいた「最初のアプリケーション」という位置付けになっている。単にGUI環境を作るのでなく、それを可能にするプロトコルとツールキットを開発したMITらしい壮大な発想だ。
学術的な話を読みたが、取りあえず使えるようにしよう。
「Getting Mosh」セクションに重なOS環境のパッケージが準備されている。
Lucidにはadd-apt-repository、apt-get update、installですんなり入った。
ソースからインストールする際のはprotocol buffersとboostライブラリとヘッダーが必要となるが、依存モジュールもちゃんと明記してある。
ライアントコマンドはなんとperlスクリプト。IO::Ptyが必要になる。debianならlibio-pty-perlで入る。あるいはcpan IO::Ptyで入れる。
$ mosh
Can't locate IO/Pty.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.10.1 /usr/local/share/perl/5.10.1 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .) at /usr/local/bin/mosh line 24.
BEGIN failed--compilation aborted at /usr/local/bin/mosh line 24.
$ mosh
Usage: /usr/local/bin/mosh [options] [--] [user@]host [command...]
--client=PATH mosh client on local machine
(default: "mosh-client")
--server=PATH mosh server on remote machine
(default: "mosh-server")--predict=adaptive local echo for slower links [default]
-a --predict=always use local echo even on fast links
-n --predict=never never use local echo-p NUM --port=NUM server-side UDP port
--help this message
--version version and copyright informationPlease report bugs to mosh-devel@mit.edu.
Mosh home page: http://mosh.mit.edu
とりあえずサーバに向けてみる
サーバ側にmoshがインストールされていないとエラーになる。sshがユーザのパス上のmosh-serverを探して立ち上げる。
$ mosh example.com
bash: mosh-server: command not found
Connection to example.com closed.
/usr/local/bin/mosh: Did not find mosh server startup message.
Russ Coxの自己複製zipファイル
Quine の圧縮ファイル版のつくりかたをCox氏が種明かしする。
なんと次のようなファイルを作ることが可能なのだ:
$ gunzip < r.gz > r
$ cmp r r.gz
$
内容
- 「turtles all the way down」の逸話
- 「the way down」テーマのユーモラスな画像
- Quineの歴史とpython, scheme, go言語による実例
- Lempel-Zivの仕組み
- zipフォーマットレベルでロジックを解説
- 自己複製するファイル実例: r.gz r.tar.gz r.zip
- これらを生成するgoプログラム
こんな難問を娯楽とするぐらいでなければ偉大なハッカーにはなれないんだろうか…
いつか理解したいポスト: http://research.swtch.com/zip
Python小技: 辞書をプロパティ風にアクセス可能にする機能的ラッパー
foo=dict(bar=42)がある場合、いちいちfoo['bar']など書かず、JavaScriptのようにfoo.barと書きたい。
pythonはこういうことを簡単にさせてくれる:
http://odiak.net/blog/post/1618
class dotdict(dict):
__getattr__ = dict.__getitem_
あるいは
http://norvig.com/python-iaq.html
class Struct:
def __init__(self, **entries): self.__dict__.update(entries)
でも上記のやりかただと、この機能は一つの辞書で終ってしまい、オブジェクトグラフをdot表記で辿ることはできない。
foo=dict(bar=dict(baz=42))とある場合、foo.bar.bazとアクセスするにはもうちょっと工夫が必要だ。そこで:
class DotAccessible(object):
"""オブジェクトグラフ内の辞書要素をプロパティ風にアクセスすることを可能にするラッパー。
DotAccessible( { 'foo' : 42 } ).foo==42メンバーを帰納的にワップすることによりこの挙動を下層オブジェクトにも与える。
DotAccessible( { 'lst' : [ { 'foo' : 42 } ] } ).lst[0].foo==42
"""def __init__(self, obj):
self.obj=objdef __repr__(self):
return "DotAccessible(%s)" % repr(self.obj)def __getitem__(self, i):
"""リストメンバーをラップ"""
return self.wrap(self.obj[i])def __getslice__(self, i, j):
"""リストメンバーをラップ"""return map(self.wrap, self.obj.__getslice__(i,j))
def __getattr__(self, key):
"""辞書メンバーをプロパティとしてアクセス可能にする。
辞書キーと同じ名のプロパティはアクセス不可になる。
"""if isinstance(self.obj, dict):
try:
v=self.obj[key]
except KeyError:
v=self.obj.__getattribute__(key)
else:
v=self.obj.__getattribute__(key)return self.wrap(v)
def wrap(self, v):
"""要素をラップするためのヘルパー"""if isinstance(v, (dict,list,tuple)): # xx add set
return self.__class__(v)
return vif __name__=='__main__':
これだと芋蔓式に.accessができて便利だ。
辞書の中にリストがあってその中にまた辞書があったりするオブジェクト
da=DotAccessible(dict(foo=42,
dct=dict(foo=42),
lst=[dict(foo=42)],
tpl=[dict(foo=42)]))
print da
DotAccessible({'lst': [{'foo': 42}],
'tpl': [{'foo': 42}],
'bar': {'dct': 89},
'foo': 42})
辞書要素をプロパティとしてアクセス
assert da.foo==42
もともとあるプロパティは以前のようにアクセスできる
print 'items:', da.items()
ラップされたオブジェクト下の辞書もラップされている…
assert isinstance(da.dct, DotAccessible)
これにより連鎖的に.アクセスが可能になる
assert da.dct.foo==42
リストもラップされている
assert isinstance(da.lst, DotAccessible)
ので、リスト内の辞書要素も.アクセスが可能。
da.lst[0].foo==42
da.lst[:3][0].foo==42
tupleも同じく。
da.tpl[0].foo==42
doc stringの実例のテスト
assert DotAccessible( { 'lst' : [ { 'foo' : 42 } ] } ).lst[0].foo==42
assert DotAccessible( { 'foo' : 42 } ).foo==42
ユーザ定義クラスでも
class Foo(object):
def __init__(self):
self.dct=dict(foo=42)
assert DotAccessible(Foo()).dct.foo==42
dictのサブクラスを使うことを強制されないので、コントロール外のコードから来た辞書でも.アクセスができるようになるのがラッパーの利点だ。ラップして思う存分.アクセスした後はまた元のオブジェクトを関数にわたしたり返したりできるので、__getattr__などをオーバライドしていることによる弊害も.アクセスを使いたい自分のコードだけに限定できて安全だ。
何でも.アクセスできるようになってpythonを使っていて唯一javascriptが羨ましくなる問題が解消できた。
もろもろハッカーニュースアンケート
プログラミングはじめて何年?
Poll: How long have you been programming?
http://news.ycombinator.com/item?id=3786926
Less than 6 months (22)
1 year (29)
2 years (48)
3-5 years (180)
7 years (163)
10+ years (500)
20+ years (202)
30+ years (76)
50+ years (1)
メインOSは?
Poll: What is your primary operating system
http://news.ycombinator.com/item?id=3786674
OSX (500)
Windows (262)
Linux (404)
Other Unix variant (9)
iOS (15)
Android (14)
Chrome OS (3)
Other (2)
最近ハンケート多すぎない?
ハッカーニュースアンケートグラフ生成プログラム。Unixパイプに限界を感じたのでズルしてpythonで書いた。あとはこれをwebでホスティングすれば、完全自動化できる。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys,os
import re
import time
import lxml.html
import urllib2
import urllib
import jinja2default_template="""
<div>
<a href="{{original_url}}"><h3>{{title}}</h3></a>
<p>
<a href="{{original_url}}">{{original_url}}</a>
</p>
{% for vote in votes %}
<span>{{vote.0}} ({{vote.1}})</span>
<div style="width:{{vote.1}}px; height: 1em; background:#cceecc; border: 1px solid #000; margin-bottom: 1em;" >
</div>
{% endfor %}
</div>
"""def normalized_votes(trs, max_width=1000):
vts=list(votes(trs))
max_score=max([ pair[1] for pair in vts ])
return [ (label, max_width*score/max_score) for label, score in vts ]def votes(trs):
"""vote row elements to sequence of pairs (label, score)
"""
while True:
try:
item=trs.next()
comment=trs.next()
spacer=trs.next()
except StopIteration:
break
m=re.match(r'^(\d+)\s*\w+', comment.text_content())
if not m:
raise RuntimeError("failed to parse vote: " + str(item))
score=int(m.group(1))yield (item.text_content(), score)
def parse(html_str, **opt):
""" html_string --> page data dict to be rendered.
"""
lx = lxml.html.fromstring(html_str)
votes_table=lx.find('./body/center/table/tr/td/table/tr/td/table')
title_td=lx.xpath('.//td[@class="title"]')[0]
title_link=title_td.find('.//a')
return dict(votes=normalized_votes(iter(votes_table), **opt),
title=title_td.text_content(),
original_url=None if title_link is None \
else 'http://news.ycombinator.com/' + title_link.get('href'),
)def resolve_template(template=None):
# tdir,tname=os.path.split(template)
# env=jinja2.Environment(loader=jinja2.FileSystemLoader(tdir))
# return env.get_template(tname)if template is None:
template_str=default_template
elif os.path.exists(template):
template_str=file(template).read().decode('utf8')
else:
template_str=template
return jinja2.Environment().from_string(template_str)def fetch(url):
"""Resolve url|or-html-file to html string.
A simple caching is implemented so that you dont get banned from hn for repeated use.
"""
cache_file=os.path.join('/var/tmp/', 'hn-poll-'+urllib.quote(url).replace('/','-'))if not url.lower().startswith('http://'):
# accept local file
html_file=url
return file(html_file).read().decode('utf8')elif os.path.exists(cache_file) and time.time()-os.stat(cache_file).st_mtime<=3600:
# cache hit
print >>sys.stderr, 'cache-hit:', cache_file
html=file(cache_file).read()else:
# fetch
print >>sys.stderr, 'fetching:', url
html=urllib2.urlopen(url).read()
tmp_file=cache_file+'.tmp'
file(tmp_file, 'w').write(html)
os.rename(tmp_file, cache_file)return html.decode('utf8')
if __name__=='__main__':
import baker
import json@baker.command
def as_json(html_file, indent=None):
"""dumps (item,vote) pairs as json"""print json.dumps(parse(file(html_file).read()),
indent=indent if indent is None else int(indent))@baker.command
def as_html(hackernews_poll_url, template=None, max_width=1000):
"""render hackernews poll url into html graph.
"""
html=fetch(hackernews_poll_url)
template=resolve_template(template)
print template.render(**parse(html, max_width=int(max_width)))baker.run()
glibcの歴史
普段あまり意識しないしないlibcだが、カーネルの門番とも言える重要なコンポーネントだ。Linuxで標準のlibcはGNU libc、略してglibc。最近このglibcのsteering committeeが解散した。これをうけてlwn.netのJonathan Corbetがglibcの歴史をふりかえる。
http://lwn.net/SubscriberLink/488847/cb91a5cc3d179f3c/
とても良く書けている記事なので原文を読むことをすすめる。以下、面白いなと思ったところをいくつか紹介。
フォーク
FSFのプロジェクト管理に嫌気がさしたLinux開発者が90年代前半にlibcをフォークしていたとは知らなかった。当時のディストロたちは「Linux libc」と呼ばれるバージョンを使っていたそうな。
90年代半ばからUlrich Drepperがこつこつ開発する。その結果、glibc2はLinux libcより良いもになりdistroがglibcに戻る。
Uli時代
Ulrich Drepperはglicの独裁者的存在(これはkaratenの言葉)になる。殆どのコミットは彼がし、数少ない他コミッターは彼の承認なしには手を加えられない状況が続く。
変化の兆し
2009にコード管理をGitに移行。この際に元祖管理者のRoland McGrathがDrepperに釘をさす:
「コニュニティーが改善したい簡単な事項を、漠然とした保守主義や根拠のない不信により邪魔することのないように」
一つの時代の終り
2010年9月、UlrichはRed Hatを辞め、Goldman Sachsの技術部門へ。これでglibcのUlrich時代は幕をとじたと考えていいだろう。長いあいだご苦労さまでした。
コミュニティーによるglibc
2012年3月、RolandはSteering Committeeの解散を公表。その理由は「ボランティア活動の活性化により開発者コミュニティーが自己統治できるようになった」からだ。
新たな約束
さらに、開発者の一人はこう呼び掛ける:「以前、無礼な仕打ちをうけ、glibcへの貢献を諦めた人もまた試してみてください。今度はそういうことはありません。約束します」
ECMAchine: ブラウザのなかでうごくschemeマシン+おもちゃOS
https://github.com/AlexNisnevich/ECMAchine
repl: シェルの役割をはたす
JavaScript相互運用: JavaScriptの関数が呼び出せる
プロセス管理:
ミリ秒毎にコンテキストスイッチするスケジューラでプロセスを実装しているよう。つまりブラウザの中でバックグラウンド処理が可能ってことになる。
実用が目的でなく、SICPで勉強したことを応用してみたかったようだ。しかし、思わぬ用途が見付かるかもしれない。時間があるときにゆっくり吟味してみたいプロジェクトだ。