Google Summer of CodeのPayment cardと格闘したメモ

GSoCの賞金はCitibank発行のVISAプリペイドで支払われるが,US在住でない学生にとっては結構めんどくさくて苦労した. メーリングリストで,US以外の生徒から結構Tipsが投稿されてるので,そっちも読むと良い. 以下日本人向けの,PIN設定とATM引き出し上限変更のTips. カードの所有がGoogle?なので,Citibankアメリカとやりとりをしなければならず,基本全部英語である.

PINの設定

Student Payment Cardsにある通り,+001-610-941-4607に電話をかけて設定する. 固定電話以外からは通じないが,Skypeからは通じるので,固定電話がないという人はSkypeのフリートライアルを使うと良い. PINの設定に関しては基本全部機械音声なので安心.

  1. +001-610-941-4607にかける
  2. カード番号を入力して最後に'#'を入力
  3. 郵便番号を聞かれるので,'Google本社'の郵便番号を入力する(2013年の時点では94043)
  4. 操作の選択肢が流れるので,2を押してPINの設定を選択する
  5. PINを入力しろって言われるので,4ケタで好きなPINを入力する
  6. 電話を切る

ATM引き出し上限の変更

実はこちらが厄介で,Citibankアメリカのオペレータと英語で会話しなければならない.デフォルトで400USドル/日が引き出し可能なので,そもそも英語を発音できる気がまったくしないという人はあきらめた方が良いかもしれない. ただ相手が人間といっても,コールセンターなのでマニュアル通りのことしか言ってこないので,英語がうまくなくてもがんばればなんとかなる(かもしれない).

  1. PINの設定の手順1-3を実行する
  2. 操作の選択肢が流れるので,3(General information)を選択する
  3. また操作の選択肢が流れるので,0を選択する
  4. オペレータに繋がるので頑張る
  5. 電話を切る

オペレータとの会話は大体こんな感じ(僕の場合).オペレータが喋ってた英語は覚えてないので日本語訳で:

op: <<機械音声で,この会話は録音されますみたいな注意が流れる>>
op: <<お決まりのお電話ありがとうございます的なアレ>>
op: アカウント番号をお願いします.
me: <<カード番号を一桁ずつ英語で読み上げ>>
op: お名前をフルネームでお願いします.
me: Yuto Kawamura
op: カードの登録住所をお願いします.
me: Hold on please
# 住所聞かれるの予想外でテンパる.カードの登録住所はGoogleなので,
# カードが送られてきた封筒を探して読み上げようとするも発音が分かんなくてグダる.
me: Sorry, I can't pronounce this address.
op: Okey...
# 明らかに困ってる
# 仕方ないので郵便番号で勘弁してもらおうとする
me: Could I tell you postal code instead?
op: Yes
me: nine, four, zero, four, three(94043)
op: Okey なんとかかんとか. What can I help you?
me: I would like to raise withdraw daily limit of my card.
me: Raise it up to thousand(1,000) dollers per day.
op: <<少々お待ちください的なこと>>
op: Okey, now your card's daily limit is 1,000 doller.
op: Can I help you something else?(他になにかありますか?的なこと)
me: No, that's all. Thank you very much.
op: <<お決まりのありがとうございました的なこと>>
me: 通話切る

大体こんな感じでした.全て聞き取れなくても,大体単語で何言ってるのか分かると思うし,さすがに世界中の人相手にしてるからか,今のはクソ発音だったなって自覚したときでもちゃんと聞き取ってくれました. アドバイスとして:

  • 少しの沈黙を恐れずに相手が喋り終わり,こっちのターンになったことをしっかり確認したほうがいいです
  • 聞き取れなかったときは,perdon?(パーデン)と言ってもう一回言ってもらう
  • それでもダメなときは,I'm sorry, I can't understand.(たぶん言い方を変えてくれるかゆっくり言ってくれる)
  • テンパってどうしようもなくなったら,I'm sorry, I will try again later.とか言って切って二度とかけなければいいと思います

細かく覚えてないので上のやりとりではハショりましたが,僕は最後の奴以外全部実践しました.まあ相手はコールサービスでこちらは一応お客なので,気にしないで迷惑かければいいと思いました.

libtoolでshared libraryを作る際の拡張子を指定する

Wiresharkのプラグイン等は,OSX環境等でも'.so'にしてくれないと読み込んでくれないので,どうすれば拡張子を指定できるのか調べた.

-shrext

libtoolには-shrextというオプションが用意されているのでこれを使う方法.Makefile.am等で,LDFLAGSに書けば良い.

hogehoge_la_LDFLAGS = -shrext '.so'

-module

libtoolには-moduleというオプションもある.これは,コンパイル時にリンクされるライブラリではなく,実行後にdlopen(3)でリンクされるプラグイン用等のshared libraryを作る際に指定するオプションである.これを指定すると,拡張子も'.so'にしてくれるので,今回のケースではこちらを使う.

hogehoge_la_LDFLAGS = -module

Perlで10行テンプレートエンジン

コードジェネレータ書いてるときに生成するコード量が多いと,スクリプト中にコピペできる程度にミニミニなテンプレートエンジンが欲しいと思うことがあるので書いた.エスケープとかは必要ないので,ベリーシンプルな置換とevalで書ける.

my $Token = join '', map { chr(65 + rand(26)) } 1..64;
sub compile_template {
    my ($tmpl) = @_;

    $tmpl =~ s{<%(=)?(.*?)%>\n?|((?:(?!<%).)+)}{
        $2 ? $1 ? "\$$Token .= qq{\@{[do{ $2 }]}};" : $2
           : "chop( \$$Token .= <<$Token );\n".quotemeta($3)."\n$Token\n";
    }gse;
    eval "sub { my \$$Token = ''; $tmpl \$$Token }"
        or die "ERROR: Cannot compile template: $@";
}

my $renderer = compile_template <<'EOS';
static const key_value_t <%= $_[0]->{name} %>[] = {
<% for my $d (@{ $_[0]->{elems} }) { %>
    { <%= $d->{key} %>, <%= $d->{value} %> },
<% } %>
};
EOS

print $renderer->({
    name  => 'hogehoge',
    elems => [
        { key => 'hoge', value => 'fuga' },
        { key => 'foo',  value => 'bar'  },
    ],
});

こんな感じで.perlのコードがそのまま書けて,forとかifとか使えるのが良い.シンタックスはText::MicroTemplate風で.

実装としては,<% %>で囲まれたperlのコードをそのまま残し,<%= %>で囲まれた式の評価結果と,それ以外の文字列を変数にどんどんappendしていくように置換していく感じ.置換結果をsub{ }でくくってevalしてcoderefにして返すので,evalのコストはコンパイルの時だけ.

変数名が被ったり,heredocが予想外に終端されることを防ぐために64文字のランダムな文字列を生成して使用している.

GSoCで書いたコードにこんな感じで使ったりしている.

Cで配列に格納した命令を実行できる話

Cでは配列に命令を格納していくと,そのまま実行できるという話をチラ聞きしたので試してみた.

Cではメモリに記録された内容が何物であるかというのは,当該領域を参照する変数の型によって決定されるので,次のようなことが可能です.

int num = 10;
float *rnum = (float *)&num;
fprintf("%f\n", *rnum); /* => ??? */

大半の処理系では,intとfloatのサイズは同じく4バイトなので,このコードはコンパイルでき,実行できます. しかし,int(整数)とfloat(浮動小数点数)ではメモリ上の数の表現方法が異なるので,評価結果は'10'とはなりません.

ここで重要なのは,Cにおいて,プログラマが指定する型は絶対的だということです.あるメモリ上に存在するnバイトのデータは,プログラマがintだと言えばintだし,floatだと言えばfloatだし,charの配列だと言えばcharの配列なわけです.

これを利用することで,次のような事が可能です.(Gentoo 32bit on i386gcc (Gentoo 4.6.3 p1.9, pie-0.5.2)で動作確認.他の環境ではたぶん動きません)

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *plus;
    int (*plusop)(int, int);

    plus = malloc(13);
    if (plus == NULL)
        abort();
    plusop = (void *)plus;

    plus[ 0] = 0x55;                                  /* push   %ebp           */
    plus[ 1] = 0x89; plus[ 2] = 0xe5;                 /* mov    %esp,%ebp      */
    plus[ 3] = 0x8b; plus[ 4] = 0x45; plus[5] = 0x0c; /* mov    0xc(%ebp),%eax */
    plus[ 6] = 0x8b; plus[ 7] = 0x55; plus[8] = 0x08; /* mov    0x8(%ebp),%edx */
    plus[ 9] = 0x01; plus[10] = 0xd0;                 /* add    %edx,%eax      */
    plus[11] = 0x5d;                                  /* pop    %ebp           */
    plus[12] = 0xc3;                                  /* ret                   */

    printf("6 + 9 = %d\n", plusop(6, 9));
    free(plus);
    return 0;
}

上のコードでは,mallocで確保した13byteの領域に,8個のx86命令を格納しています.これらの命令は,二つの引数を足して返す関数として機能します.この配列の先頭アドレスを,int (int, int)型の関数ポインタに入れてやり,普通の関数ポインタよろしく'()'を付けてやることで,関数として実行できます.つまりプログラマが,mallocで確保したアドレスに格納されているのは,関数として妥当な命令のリストだと明示したことにより,Cは忠実にそれを信じて実行してくれたわけです.

もちろん出来るというだけで実用性はまったくありませんが,Cはその気になればこんなことさえできてしまうという,とてもおもしろい例だと思います.

error: /usr/kerberos/include: No such file or directory

Gentoolibvirtをコンパイルしようとしたら

error: /usr/kerberos/include: No such file or directory

と言われたので,なにかなーと思って見てみると,libsslとlibcryptoのpkg-configファイルが古いkerberosのパスを指していたので,

sudo perl -i'' -npe 's{/usr/kerberos/include}{/usr/include/krb5}' /usr/lib/pkgconfig/lib{ssl,crypto}.pc

などとして解決した.

Wiresharkをソースから入れるとヘッダファイルをインストールしてくれなくて困る

Wiresharkのtarball落としてきてmake installしてもヘッダファイルがインストールされない. Makefile.amを読んだ感じ,ヘッダファイルはインストールしないようにしてるっぽく,手動でやるしかないのかーと思っていると,emergeしたほうのWiresharkについてはヘッダファイルがインストールされている事に気づく. なんでかなーと思って/usr/portage/net-analyzer/wireshark/wireshark-1.10.1.ebuildを読んでみると,179行目あたりに以下のような記述があった.

    # install headers
    local wsheader
    for wsheader in $( echo $(< debian/wireshark-dev.header-files ) ); do
        insinto /usr/include/wireshark/$( dirname ${wsheader} )
        doins ${wsheader}
    done

なるほど自前ですか.ということで,同じようなことを自前でやってやる.

mkdir -p $PREFIX/include/wireshark
eval rsync -R $(< debian/wireshark-dev.header-files) $PREFIX/include/wireshark/

evalしてるのはクオートされちゃってるファイルグロブを展開するため. rsyncしてるのはOSXのcpに-p(--parents)相当のオプションがないため.プラットフォーム毎にオプションの意味が変わるとか高品質のハマり要因なのでほんとやめてほしい.

Wireshark pluginをビルドするのにヘッダファイル必要なんだからデフォルトで入るようにしてくれよ.

Hook after emerge

emergeした後にhookの設定ができたら便利だなーと思っていたら実はできました.

Ref: https://forums-lb.gentoo.org/viewtopic-t-772340-start-0.html

/etc/portage/bashrcにpost_pkg_postinstというfunctionを定義することで,各パッケージのインストール後に実行してもらえます. 特定パッケージのインストール後のみ実行したい操作がある場合は,$CATEGORY,$PNという変数にそれぞれパッケージカテゴリとパッケージ名が入っているので,これらを利用して処理したい対象のパッケージか判定します.

firefoxのAdobeReaderがC:\nppdf32Log\debuglog.txtというファイル名を作る問題の件も以下のように設定することで,app-text/acroreadがemergeされる毎にnppdf.soの中身を書き換えてくれるので,手動で書き換える手間が省けて大変はかどりますね.

post_pkg_postinst() {
    if [ "$CATEGORY/$PN" = "app-text/acroread" ]; then
        cp /opt/netscape/plugins/nppdf.so /opt/netscape/plugins/nppdf.so.orig
        perl -i'' -pe's{C:\\nppdf32Log\\debuglog\.txt}{"/dev/null"."\0"x17}e' /opt/netscape/plugins/nppdf.so
    fi
}
$ diff <(od -tc /opt/netscape/plugins/nppdf.so.orig) <(od -tc /opt/netscape/plugins/nppdf.so)
8571,8573c8571,8573
< 0414460   e   s   s   a   g   e  \0   a   b  \0   C   :   \   n   p   p
< 0414500   d   f   3   2   L   o   g   \   d   e   b   u   g   l   o   g
< 0414520   .   t   x   t  \0   X   X   X   :   G   i   v   i   n   g    
---
> 0414460   e   s   s   a   g   e  \0   a   b  \0   /   d   e   v   /   n
> 0414500   u   l   l  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
> 0414520  \0  \0  \0  \0  \0   X   X   X   :   G   i   v   i   n   g    

ちゃんと置換できてます.