メモの日々


2008年03月01日(土) [長年日記]

  • やること山積みなのにゴロゴロしてしまった。世界卓球観たり。韓陽安定。水谷もう一息。

[c++] boost/program_options の positional_options 動いた

昨日のプログラムは間違えていたようだ。

Keep It Simple, Stupid.にpositional_optionsを使ったサンプルがあったので試してみたら動いた。違いを調べると、positional_options_description単独で名前なしオプションを定義すればいいのだと思っていたが、options_descriptionで定義したオプションに対しpositional_options_descriptionを使うのが正しいみたい。

昨日のプログラムに対し

--- program_options.cpp.old     2008-02-28 13:07:50.000000000 +0900
+++ program_options.cpp 2008-02-28 12:54:50.000000000 +0900
@@ -14,7 +14,7 @@
 int main(int argc, char* argv[]) {
     po::options_description options("Allowed options");
     options.add_options()
-        ("aaaa,a", po::value<string>())
+        ("XXXX,a", po::value<string>())
         ("help,h", "help");
     po::positional_options_description xxxx;
     xxxx.add("XXXX", -1);
@@ -37,7 +37,7 @@
 //            exit(2);
         }

-        cout << "XXXX is " << vm["XXXX"].as<vector<string> >()[0];
+        cout << "XXXX is " << vm["XXXX"].as<string>() << "\n";
     } catch (exception& e) {
         cerr << e.what() << "\n" << options;
         exit(2);

という修正を行うことで、

$ ./a.out param
XXXX is param

$ ./a.out param param2
multiple_occurrences
Allowed options:
  -a [ --XXXX ] arg
  -h [ --help ]         help

$ ./a.out --XXXX param
XXXX is param

$ ./a.out --XXXX param param2
multiple_occurrences
Allowed options:
  -a [ --XXXX ] arg
  -h [ --help ]         help

のように動くようになった。

[howto] Debianでパッケージのソースをダウンロードする

方法を調べたので覚え書き。

準備として、/etc/apt/sources.list に deb-src で始まる行が必要みたい。その記述を追加したら、

apt-get source <パッケージ名>

とするとカレントディレクトリに関連ファイルのアーカイブがダウンロードされ、アーカイブの展開までされた。apt-getではなくaptitudeで実行する方法は分からなくて、aptitudeではできないのかもしれない。

[howto] Debianでパッケージに含まれるファイルの一覧を表示

この場合はdpkgコマンドを使う必要があって

dpkg -L <パッケージ名>

とするようだ。ちなみに、rpmの場合は

rpm -ql <パッケージ名>

だったはず。

[howto] Debianでファイルが含まれていたパッケージを表示

dpkg -S <ファイルパス>

とするみたい。rpmだと

rpm -qf <ファイルパス>

かな。

[howto][dev] gdbでソースファイルの場所の指定

directoryコマンドでできる。

[life] 敷金から引かれた額

以前の住処の敷金の戻り金額が通知された。9万円くらい引かれるとのこと。内訳は

壁紙貼替5万円
清掃工事3万円
エアコン内部洗浄1万円

て感じ。壁紙は10万円の所を貸主と50%ずつ負担、清掃/洗浄は100%負担。ボッタクラレテル?

やること

  • クレジットカード
  • 銀行
  • 新税務署
  • ダンボール
  • 水道料金
  • 敷金の確認
  • ゴミ箱を買う

2008年03月05日(水) [長年日記]

  • 日曜日から体調がよくない。胸がムカムカし、下痢。月曜日は仕事を休んだが、まだ完治していない。食欲がない。
  • QNの内部講習を受けた。まあわかったけど、しばらく使わないからきっと忘れる。

[java] EclEmma

先月、EclipseでカバレッジをとるためにdjUnitを使ったけど、WEB+DB Press Vol.43でEclEmmaというプラグインが紹介されていたのでメモ。いいみたい。

EclEmma is a free Java code coverage tool for Eclipse, available under the Eclipse Public License.

やること

  • クレジットカード
  • 銀行
  • 新税務署
  • ダンボール
  • 水道料金
  • 敷金の確認
  • ゴミ箱を買う

2008年03月06日(木) [長年日記]

  • NTTコミュニケーションズから葉書が来ていて、電話番号が変わったらNTTコミュニケーションズにも連絡してくれと書いてあった。でも理由が書かれていない。電話する必要はなさそうに思う。

[dev][web] XPathを使う

XPathを使う必要がある。XPathは過去に何度か勉強したがすぐに忘れてしまい身に付かない。身に付かないのはメモしないからなので、今回はメモする。

RubyのREXMLを使って次のスクリプトを書いた。

#!/usr/bin/ruby
require "rexml/document"

xml = <<EOF
<root>
  <momo>
    <buka>
      <name>hideyoshi</name>
      <type>saru</type>
      <weapon>panchi</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>
  <momo>
    <buka>
      <name>taro</name>
      <type>inu</type>
      <weapon>kick</weapon>
    </buka>
  </momo>
  <momo>
    <buka>
      <name>jiro</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>
  <momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>
  <momo>
    <buka>
      <name>unknown</name>
      <type>kiji</type>
      <weapon>tutuki</weapon>
      <weapon>tutuki</weapon>
      <weapon>tutuki</weapon>
      <weapon>tutuki</weapon>
    </buka>
  </momo>
</root>
EOF

doc = REXML::Document.new(xml)

xpaths = [
  ["/root/momo/buka/type", "/root/momo/buka/type"],
  ["//buka", "buka全て"],
  ["//type", "type全て"],
  ["//type/text()", "typeの内容全て"],
  ["//type[text()='inu']", "内容がinuのtype全て"],
  ["//momo[buka/type/text()='inu']", "内容がinuのtypeを持つmomo全て"],
  ["//momo[buka[type/text()='inu']]", "これでもいいみたい"],
  ["//momo[buka[type/text()='inu'][count(weapon)>=4]]",
   "内容がinuのtypeを持ち、weaponの数が4つ以上のmomo全て"],
  ["//momo[buka[type/text()='inu'][count(weapon[text()='kamituki'])>=3]]",
   "内容がinuのtypeを持ち、内容がkamitukiのweaponの数が4つ以上のmomo全て"],
  ["//momo[buka[type/text()='inu' and count(weapon[text()='kamituki'])>=3]]",
   "それともこう書くのかな"],
  ["//*[local-name()='momo']" +
      "[*[" +
          "[*[local-name()='type'][text()='inu']]" +
          "[count(*[local-name()='weapon'][text()='kamituki'])>=3]" +
      "]]",
   "訳あってlocal-nameだけを使って頑張って書く。合っているかわからん。"],
]

xpaths.each {|x, text|
  puts "-" * 40
  puts text
  puts "XPath: #{x}"
  puts "-" * 40
  puts REXML::XPath.match(doc, x)
  puts
}

実行結果は次の通り。

----------------------------------------
/root/momo/buka/type
XPath: /root/momo/buka/type
----------------------------------------
<type>saru</type>
<type>inu</type>
<type>inu</type>
<type>inu</type>
<type>kiji</type>

----------------------------------------
buka全て
XPath: //buka
----------------------------------------
<buka>
      <name>hideyoshi</name>
      <type>saru</type>
      <weapon>panchi</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
<buka>
      <name>taro</name>
      <type>inu</type>
      <weapon>kick</weapon>
    </buka>
<buka>
      <name>jiro</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
<buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
<buka>
      <name>unknown</name>
      <type>kiji</type>
      <weapon>tutuki</weapon>
      <weapon>tutuki</weapon>
      <weapon>tutuki</weapon>
      <weapon>tutuki</weapon>
    </buka>

----------------------------------------
type全て
XPath: //type
----------------------------------------
<type>saru</type>
<type>inu</type>
<type>inu</type>
<type>inu</type>
<type>kiji</type>

----------------------------------------
typeの内容全て
XPath: //type/text()
----------------------------------------
saru
inu
inu
inu
kiji

----------------------------------------
内容がinuのtype全て
XPath: //type[text()='inu']
----------------------------------------
<type>inu</type>
<type>inu</type>
<type>inu</type>

----------------------------------------
内容がinuのtypeを持つmomo全て
XPath: //momo[buka/type/text()='inu']
----------------------------------------
<momo>
    <buka>
      <name>taro</name>
      <type>inu</type>
      <weapon>kick</weapon>
    </buka>
  </momo>
<momo>
    <buka>
      <name>jiro</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>
<momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>

----------------------------------------
これでもいいみたい
XPath: //momo[buka[type/text()='inu']]
----------------------------------------
<momo>
    <buka>
      <name>taro</name>
      <type>inu</type>
      <weapon>kick</weapon>
    </buka>
  </momo>
<momo>
    <buka>
      <name>jiro</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>
<momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>

----------------------------------------
内容がinuのtypeを持ち、weaponの数が4つ以上のmomo全て
XPath: //momo[buka[type/text()='inu'][count(weapon)>=4]]
----------------------------------------
<momo>
    <buka>
      <name>jiro</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>
<momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>

----------------------------------------
内容がinuのtypeを持ち、内容がkamitukiのweaponの数が4つ以上のmomo全て
XPath: //momo[buka[type/text()='inu'][count(weapon[text()='kamituki'])>=3]]
----------------------------------------
<momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>

----------------------------------------
それともこう書くのかな
XPath: //momo[buka[type/text()='inu' and count(weapon[text()='kamituki'])>=3]]
----------------------------------------
<momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>

----------------------------------------
訳あってlocal-nameだけを使って頑張って書く。合っているかわからん。
XPath: //*[local-name()='momo'][*[[*[local-name()='type'][text()='inu']][count(*[local-name()='weapon'][text()='kamituki'])>=3]]]
----------------------------------------
<momo>
    <buka>
      <name>otosan</name>
      <type>inu</type>
      <weapon>kick</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
      <weapon>kamituki</weapon>
    </buka>
  </momo>

本当は名前空間も使う必要があって、その場合はまだよくわかってない。REXMLは名前空間をきちんと扱えないらしいしな。

参考

やること

  • クレジットカード
  • 新税務署
  • ダンボール

2008年03月11日(火) [長年日記]

  • 朝起きたら左目内側下部が赤く充血していた。痛みはない。
  • 今日はとても暖かい。
  • 2階のシュレッダーが新しくなっていた。
  • 今日の行数:1524

[work][life] 確定申告に行く 2008

去年より2週間くらい遅い。引っ越したので税務署も変わる。厳しい税務署だと嫌だなあ。少しわかりにくい場所にあって迷ったが、なんとか辿り着いた。

住所が変わったので、「所得税・消費税の納税地の異動に関する届出書」を事前に書いておいた。提出場所は2階の総務とのこと。異動前の税務署にも提出するよう言われたが、以前に電話で問い合わせたら異動後の税務署にだけ提出でよいと言われた旨伝えたら納得してくれたみたい。ついでに納税の自動引き落しについても手続きが必要か尋ねたら、税務署が変わる際は必要とのこと。ふむ、国税庁は電力会社に遅れをとっているな。

その後別の場所で確定申告書の提出。行列になっていたが職員も大勢おりそれほど待たされなかった。んで、今回は社会保険料控除の証明書類を返してくれた。必要ない、みたいに言われたがほんとかいな。

納税額は去年とあまり変わらない。国税から地方税への税源移譲によりだいぶ安くなるのかと思っていたのにな。住民税は随分高くなったと記憶しているのでおかしい。後で確認しないと。

やること

  • クレジットカード
  • 新税務署
  • ダンボール
  • 振替納税手続き
  • シュレッダー

2008年03月19日(水) [長年日記]

  • 今日の行数:2544
  • このプロジェクトはもうほとんどおしまい。

[dev][unix] The GNU Readline Libraryを使った

Readlineライブラリを使ってみたのでメモ。使用したReadlineのバージョンは5.2。

単純なサンプル

次のプログラムで、

  • 行の読み取り
  • 読み取った行の履歴を保存(C-pや↑で参照可能)
  • C-b, C-f, C-a, C-e などのお馴染の編集操作
  • ~/.inputrc ファイルによるカスタマイズ

などが可能になる。

#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>

int main() {
    char* line;
    while (line = readline("> ")) { /* 行の読み取り */
        printf("line is '%s'\n", line);
        if (strlen(line) > 0) {
            add_history(line); /* 履歴を保存 */
        }
        free(line);
    }
    printf("\nexit\n");
    return 0;
}

C-cで行のクリアがされるようにしたい

上のプログラムだと、C-cを入力するとプログラムが終了してしまった。C-c入力時は入力行を捨てて次の入力待ちになるようにしたい。

ドキュメントの 2.5 Readline Signal Handling にある機能を使えば制御できそうなんだけれど、うまくいかない。正しい使い方がよくわからん。

自分でシグナルハンドラを書いて、次のようにしたら一応実現できた。 Readlineの機能はrl_event_hook, rl_done, rl_delete_text() を利用している。

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

#include <signal.h>
#include <unistd.h>

#include <readline/readline.h>
#include <readline/history.h>

static volatile sig_atomic_t signal_handled = 0;

static void handle_signal(int signo) {
    signal_handled = 1;
}

static void set_signal_handler() {
    struct sigaction action;
    action.sa_handler = handle_signal;
    action.sa_flags = 0;
    if (sigemptyset(&action.sa_mask)) {
        perror("sigemptyset");
        exit(1);
    }
    if (sigaction(SIGINT, &action, NULL)) {
        perror("sigaction");
        exit(1);
    }
}

/* readline()内から定期的に呼ばれる関数 */
static int check_state() {
    if (signal_handled) {
        signal_handled = 0;

        /* 入力中のテキストを破棄 */
        rl_delete_text(0, rl_end);

        /* readline()をreturnさせる */
        rl_done = 1;
    }
    return 0;
}

int main() {
    set_signal_handler();

    /* check_state()が定期的に呼ばれるように設定 */
    rl_event_hook = check_state;

    char* line;
    while (line = readline("> ")) {
        printf("line is '%s'\n", line);
        if (strlen(line) > 0) {
            add_history(line);
        }
        free(line);
    }
    printf("\nexit\n");
    return 0;
}

C-cでreadline()がNULLを返すようにしたい

C-c入力時に行のクリアではなくプログラムが先に進むようにしたい場合はどうするのか。(最初のプログラムでC-cを入力すると最後の「exit」が出力されないが、これが出力されるようにしたい。)

上の2番目のプログラムのcheck_state()の内容を次のように変えると実現できた。

/* readline()内から定期的に呼ばれる関数 */
static int check_state() {
    if (signal_handled) {
        /* こうしないとこの関数がどんどん呼ばれてしまうみたい */
        rl_event_hook = 0;

        /* これを呼ばないとプログラム終了後の端末がエコーバックしない */
        /* 標準入力closeの前に実行しないとダメ */
        rl_deprep_terminal();

        /* 標準入力をcloseするとreadline()がNULLを返すみたい */
        close(STDIN_FILENO);
    }
    return 0;
}

コメントにも書いたけど、

  • rl_event_hook をクリアしないと無限ループしてしまってダメ。
  • rl_deprep_terminal() を呼ばないとC-cでのプログラム終了後に端末がエコーバックしなくなる。プログラム実行前のsttyの実行結果が「speed 38400 baud; line = 0;」だったのが、プログラム終了後は「lnext = <undef>; min = 1; time = 0; -icrnl -icanon -echo」になった。ちなみに、このように端末がおかしくなった場合は resetコマンド で元に戻せる。
  • rl_deprep_terminal()の代わりにrl_cleanup_after_signal()でもいいみたい。

標準入力をcloseするのは強引かもしれない。変数signal_handledを作っているのだから、main内のループにてこの変数が1ならbreakするようにする方がいいかも。そうすればrl_deprep_terminal()の呼び出しなども不要だ。

ReadlineはGPL

便利なReadlineだけれどライセンスはGPLなんだな。利用するプログラムをGPLコンパチにできないときは使えない。

やること

  • クレジットカード
  • ダンボール
  • 振替納税手続き
  • シュレッダー

2008年03月23日(日) [長年日記]

  • 一日には300行くらいしか書けないみたい。

[net][c][unix] 自IPアドレスの取得

スクリプトで取得するのではなくC言語で書く場合。自ホストIPアドレスの取得方法( Re: システムコールor 関数について) に書かれていたUDPソケットとgetsockname()を使う方法が分かりやすいと思った。ほとんど同じだけれど、自分で書いたプログラムをメモ。

  • UDPソケットに対するconnect呼び出しをしてもパケットは送信されない。UNIXネットワークプログラミングのUDPの章(8.11節)に説明があった。
  • connect呼び出しを行うには接続先のIPアドレスが必要になる。下のプログラムでは例示用のIPアドレスを使うようにしてみた。
  • やっていることはシンプルだがコードは結構長い。
  • アドレスを取得したいインタフェースの名前が分かっている場合はioctlを使うなどした方がきっと簡単。

(追記)

この方法だと、デフォルトゲートウェイが設定されていないホストで実行した場合にconnect()がエラーになってしまいIPアドレスを取得できないようだ。うーん。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>

int get_my_ip_address(
        const char* dst_address,
        char* buffer,
        size_t buffer_len) {
    struct sockaddr_in dst_sockaddr = {0};
    dst_sockaddr.sin_family = AF_INET;
    int r = inet_pton(AF_INET, dst_address, &dst_sockaddr.sin_addr);
    if (r <= 0) {
        if (r < 0) {
            perror("inet_pton");
        } else {
            fprintf(stderr, "'%s' is invalid.\n", dst_address);
        }
        return 1;
    }

    // UDPソケットをオープンして
    int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket");
        return 1;
    }

    int result = 1;

    // connectして
    if (connect(sockfd, (struct sockaddr*)&dst_sockaddr,
                sizeof(dst_sockaddr)) < 0) {
        perror("connect");
        goto end;
    }

    // getsocknameすれば自分のアドレスを取得できる
    struct sockaddr_in my_sockaddr;
    socklen_t len = sizeof(my_sockaddr);
    if (getsockname(sockfd, (struct sockaddr*)&my_sockaddr, &len) < 0) {
        perror("getsockname");
        goto end;
    }

    if (!inet_ntop(AF_INET, &my_sockaddr.sin_addr, buffer, buffer_len)) {
        perror("inet_ntop");
        goto end;
    }

    result = 0;
end:
    close(sockfd);
    return result;
}

int main(int argc, char* argv[]) {
    char* dst_address = 0;
    if (argc == 2) {
        dst_address = argv[1];
    } else {
        dst_address = "192.0.2.1"; // RFC 3330
    }

    char buffer[INET_ADDRSTRLEN];
    if (get_my_ip_address(dst_address, buffer, sizeof(buffer))) {
        printf("IP address is unknown.\n");
        exit(1);
    }
    printf("IP address: %s\n", buffer);
    return 0;
}

[life] 不動産屋と物件選びについて

今更ながら引越しについてメモ。まずは不動産屋をどう探すか。

今回、最初は現地にって不動産屋を探したが、不動産屋がたくさんありすぎてどこへ入ったらいいのか決めるのに困った。んで思ったのは、不動産屋へ行って物件を探すよりもウェブで探した方がいいということ。

不動産屋に行くと色々物件を紹介してもらえるけど、後からウェブで検索してみたらそのほとんどがウェブ上にもあった。また、不動産屋の外に貼ってある物件は中に入ると全然紹介されない(ただの客寄せのようだ)。だから、現地に行って色々探すよりもウェブで探した方が効率がいいと思う。

ウェブ上での検索サイトも色々あるけれど、おれが使ったのはフォレントYahoo!不動産。Yahooにはフォレントの情報も流れていた。こういう所で気になる物件を見つけ、メイルで申し込み不動産屋へ行って内見させてもらうという流れ。

[life] 保証人の審査

今回、申し込んだが審査の際に保証人の収入が足りないという理由で契約できなかった物件があった。うちの親ももう歳で、あまり収入がなく保証人として認めてもらえないみたい。貧乏は辛いね。

結局、契約できた物件も親は保証人になれず、カード会社のオリコが保証人の代わりになるという仕組みに頼ることになった。手数料は家賃の1%なのでそれ程負担にはならない。

保証人不要で契約できるかどうかは不動産屋や大家の都合があるので、メイルで申し込む際に聞いておくのがいいと思う。

[life][service] 引越し屋選び

引越し屋選びも面倒。今回、見積依頼を代行してくれる引越し比較.comというサービスを利用したが、結果色々な引越し屋からメイルがどんどん来るし、勝手に電話を掛けてくる業者もいて辟易した。こんなことなら自分で見繕った引越し屋に個別に申し込む方がいいと思う。どこもメイルで概算の見積もりは出してくれると思う。

引越し業者の口コミ情報があるサイトを少しメモ。

まあ、良し悪しの印象は会社よりも引越しをしてくれる担当者で決まるような気がする。おれはどんどん来たメイルの中からそこそこ大手のダック引越しセンターに頼んで、担当者のお兄さんが爽やかないい人でラッキーだったと思う。2トン車、移動時間1時間、平日、作業員2人で31500円だった。

やること

  • シュレッダー
  • 健康保険料
  • 請求書

2008年03月24日(月) [長年日記]

[unix] プロセスの関係についてメモ

Unixのプロセスについて、詳解UNIXプログラミングで勉強。

Ctrl-Cによる割り込みシグナルの送り先

シェルからプログラムを起動したとき、Ctrl-Cによる割り込みシグナルがどのプロセスに送られるのかを知りたかった。「第10章 シグナル」のSIGINTの説明に

端末の割り込みキー(しばしば、DELETEやControl-C)を押すと、端末ドライバが生成するシグナルである。このシグナルは、フォアグラウンドプロセスグループのすべてのプロセスに対し送られる

とあった。フォアグラウンドプロセスグループとは何か。

フォアグラウンドプロセスグループ

フォアグラウンドプロセスグループを理解するには、「プロセスグループ」「セッション」「制御端末」について知る必要がありそう。「第9章 プロセスの関係」に説明がある。

プロセスグループ
プロセスは必ずプロセスグループに属する。forkにより生まれたプロセスは、親プロセスと同じプロセスグループに属する。setpgid()により、プロセスを別のプロセスグループに属させることができ、また、新しいプロセスグループを作成することもできる。
セッション
プロセスグループは必ずセッションに属する。setpgid()により新しく作られたプロセスグループは、対象プロセスが属していたのと同じセッションに属する。setsid()により、新しいセッションとプロセスグループを作成しプロセスをそのセッションに属させることができる。
制御端末
セッションは1つの制御端末を持つことができる。setsid()により新しく作られたセッションは、制御端末を持たない。制御端末を持つセッションの作り方は知らない。

以上の概念を使うと、フォアグラウンドプロセスグループの説明ができる。

フォアグラウンドプロセスグループ
制御端末を持つセッションに属するプロセスグループの内の1つがフォアグラウンドプロセスグループになる。setpgid()により新しく作られたプロセスグループはフォアグラウンドではない。tcsetpgrp()により、プロセスグループをフォアグラウンドにすることができる。

試す

Linux 2.6.9 にて、以下に示す2つのプログラムを作って試してみた。2番目のプログラムの実行ファイル名を pausing として1番目のプログラムを実行すると、

  • fork & execl
  • fork & setpgid & execl
  • fork & setsid & execl

の3通りが行われる。プロセスの状態は次のpsコマンドで確認できる。

ps o pid,ppid,pgid,sid,tpgid,tty,stat,command --forest

サンプルプログラムに続けてこのpsコマンドを実行すると、

  PID  PPID  PGID   SID TPGID TT       STAT COMMAND
29135 29119 29119 29119    -1 ?        S    sshd: kenichi2@pts/6
29136 29135 29136 29136 31739 pts/6    Ss    \_ -bash
31739 29136 31739 29136 31739 pts/6    S+        \_ Parent
31740 31739 31739 29136 31739 pts/6    S+            \_ only fork
31741 31739 31741 29136 31739 pts/6    S             \_ do setpgid
31742 31739 31742 31742    -1 ?        Ss            \_ do setsid

という結果になった。PIDはプロセスID、PPIDは親プロセスID、PGIDはプロセスグループID、SIDはセッションID、TPGIDは当該端末に関連するフォアグラウンドプロセスグループID、TTは制御端末を表す。フォアグラウンドプロセスグループにはSTAT欄に+が表示される。意図した通りの結果になっているように見える。

プログラムを実行した端末でCtrl-Cを入力すると、

[PID=31739] Parent received signal 2.
[PID=31740] only fork received signal 2.

と出力され、フォアグラウンドプロセスグループに対してだけSIGINTが配信されていることが確かめられた。このときにpsを実行すると

  PID  PPID  PGID   SID TPGID TT       STAT COMMAND
29135 29119 29119 29119    -1 ?        S    sshd: kenichi2@pts/6
29136 29135 29136 29136 29136 pts/6    Ss+   \_ -bash
31742     1 31742 31742    -1 ?        Ss   do setsid
31741     1 31741 29136 29136 pts/6    S    do setpgid

と出力され、フォアグラウンドはシェルのプロセスグループに変わっていた。更にシェルを終了させると

  PID  PPID  PGID   SID TPGID TT       STAT COMMAND
29135 29119 29119 29119    -1 ?        S    sshd: kenichi2@notty
31742     1 31742 31742    -1 ?        Ss   do setsid
31741     1 31741 29136    -1 ?        S    do setpgid

となり、制御端末は無くなった。セッション29136は残っている。

また、以下のプログラムのコメントアウト部(tcsetpgrpを実行している部分)を生かすと、

  PID  PPID  PGID   SID TPGID TT       STAT COMMAND
  617   608   608   608    -1 ?        S    sshd: kenichi2@pts/6
  618   617   618   618   693 pts/6    Ss    \_ -bash
  691   618   691   618   693 pts/6    S         \_ Parent
  692   691   691   618   693 pts/6    S             \_ only fork
  693   691   693   618   693 pts/6    S+            \_ do setpgid
  694   691   694   694    -1 ?        Ss            \_ do setsid

のようにフォアグラウンドプロセスIDが変わっていることを確かめられる。

以下、プログラムのソース。

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

static pid_t spawn(int flag) {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        char* name;
        switch (flag) {
        case 0:
            name = "only fork";
            break;
        case 1:
            // 新しいプロセスグループを作り本プロセスがそこに属する
            if (setpgid(0, 0) < 0) {
                perror("setpgid");
                exit(1);
            }
            name = "do setpgid";
            break;
        case 2:
            // 新しいセッションを作り本プロセスがそこに属する
            if (setsid() < 0) {
                perror("setsid");
                exit(1);
            }
            name = "do setsid";
            break;
        default:
            name = "Invalid";
            break;
        }
        execl("./pausing", name, NULL);
        perror("execl");
        exit(1);
    } else {
//        switch (flag) {
//        case 1:
//            if (tcsetpgrp(0, pid) < 0) {
//                perror("tcsetpgrp");
//                exit(1);
//            }
//            break;
//        }
    }
    return pid;
}

int main() {
    spawn(0);
    spawn(1);
    spawn(2);

    execl("./pausing", "Parent", NULL);
    perror("execl");
    exit(1);
}
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

static volatile sig_atomic_t signal_number = 0;

static void handle_signal(int signo) {
    signal_number = signo;
}

static void set_signal_hanlder(int signo) {
    if (signal(signo, handle_signal) == SIG_ERR) {
        fprintf(stderr, "signal() failed.\n");
        exit(1);
    }
}

int main(int argc, char* argv[]) {
    set_signal_hanlder(SIGINT);
    set_signal_hanlder(SIGTERM);
    set_signal_hanlder(SIGHUP);
    set_signal_hanlder(SIGTSTP);
    set_signal_hanlder(SIGCONT);

    pid_t pid = getpid();

    printf("[PID=%d] %s is ready.\n", pid, argv[0]);
    for (;;) {
        pause();
        printf("[PID=%d] %s received signal %d.\n",
                pid, argv[0], signal_number);

        switch (signal_number) {
        case SIGINT:
        case SIGTERM:
        case SIGHUP:
            exit(0);
        default:
            continue;
        }
    }
    return 0;
}

やること

  • シュレッダー
  • 健康保険料
  • 請求書

2008年03月25日(火) [長年日記]

  • アンケートを依頼されたので答えた。アンケートが役に立つのを見たことがない。無駄なアンケートを禁止する法律があればいいのに。

やること

  • シュレッダー
  • 健康保険料
  • 請求書

2008年03月31日(月) [長年日記]

  • やっとzshに手を出してみた。
  • IE7で見てみた。レイアウトが結構崩れているな。
  • Firefoxだとpreの表示が時々おかしくなる。何故だ。
  • Operaだと問題ないみたい。

[howto][shell] screenのキャプションにディレクトリ名 or コマンド名を表示

screenのキャプションにディレクトリ名やコマンド名を表示するための設定を調べたのでメモ。

キャプションの表示形式

まず、screenのキャプションがごちゃごちゃしていたので整理することにした。ウィンドウの番号と名前だけが表示されればよい。.screenrcに次を書く。

caption always "%?%F%{= wk}%:%{= ww}%?%-w%{=u Wk}%n %t%{-}%+w"

以前にメモした アキレス亀日記 と、マニュアルの文字エスケープの節を参考にした。

ウィンドウ名の変更

screenのキャプションには、ウィンドウの名前として実行されたプログラム名が入るみたい。なので普通に使うとシェルの名前(bashとかzshとか)が表示される。

ウィンドウの名前は「<esc>k名前<esc>\」というエスケープシーケンスで変更できる。例えば

echo -ne '\ekhello\e\\'

を実行すると「hello」に変わる。

カレントディレクトリ名の表示

zshの場合は、カレントディレクトリが変わるとchpwd()が呼ばれるようなので、chpwd()内で

echo -ne "\ek$(basename $(pwd))\e\\"

を実行するようにしたらウィンドウ名にカレントディレクトリ名が設定されるようになった。これだけだとディレクトリを移動するまではzshと表示されちゃうけれど。

bashの場合はプロンプトが表示される度に環境変数PROMPT_COMMANDの内容が実行されるようなので、ここに上のechoを設定したらうまくいったみたい。

実行中のコマンドの表示

screenには、ウィンドウ名に実行中コマンドを設定するのをサポートする機能があるようだ。screenのマニュアルのタイトル (ウィンドウの命名)の節に説明がある。

  • シェルのプロンプトに「<esc>k<esc>\」を含める。例えばbashなら「PROMPT_COMMAND='echo -ne "\033k\033\134"'」とすればいいみたい。
  • .screenrcに「shelltitle '$ |hoge'」などと書く。$ の所はシェルのプロンプトの末尾の文字を指定する。

とすると、何も実行していないウィンドウにはhogeが、何かを実行しているウィンドウにはコマンド名がウィンドウ名として表示された。

最後に実行したコマンドの表示

zshなら、最後に実行したコマンドを常に表示することができた。コマンドの実行前にpreexec()が呼ばれるようなので、ここで

echo -ne "\ek${1%% *}\e\\"

を実行するようにすればよい。「${1%% *}」の所でコマンド名を切り出しているんだろうけど意味は分かっていなくて、Setting the title to the name of the running program にあったものそのまま。

実行中のコマンドまたはカレントディレクトリ名の表示

んで、コマンド実行中はコマンド名を、未実行ならカレントディレクトリ名を表示したい。zshなら、

  • preexec()内で「echo -ne "\ek${1%% *}\e\\"」を実行
  • precmd()内で「echo -ne "\ek$(basename $(pwd))\e\\"」を実行

としたらできたみたい。後者は上でchpwd()内で実行していたのをprecmd()内で実行するように変えただけ。preexec()が設定した値をprecmd()で上書きするようにしている。

まとめ

現在は最後の「実行中のコマンドまたはカレントディレクトリ名の表示」となるようにして試用中。 関連する設定を抜き出しておく。

.zshrc

case "${TERM}" in screen)
    preexec() {
        echo -ne "\ek#${1%% *}\e\\"
    }
    precmd() {
        echo -ne "\ek$(basename $(pwd))\e\\"
    }
esac

.screenrc

caption always "%?%F%{= wk}%:%{= ww}%?%-w%{=u Wk}%n %t%{-}%+w"

screenshot

やること

  • シュレッダー
  • 健康保険料
  • 請求書