= initramfsで/bin/shのジョブコントロール = initramfsのシェル環境でそのまま/bin/shを起動すると、 {{{ /bin/sh: can't access tty; job control turned off }}} と怒られて、/bin/shでジョブコントロールが切られた状態で/bin/shが起動する。このままだと{{{^}}}C(SIGINT)とか{{{^}}}Z(SIGTSTP)とかができないために、pingコマンドなど、無限ループするようなプログラムを誤って起動してしまうと、シグナルをプログラムに送ることができないため、Ctrl-Alt-Delで再起動するしか道がなくなる。これはttyコマンドを叩いてみるとわかるが、制御端末が/dev/consoleに設定されているために怒られる。/dev/consoleはLinuxがカーネルが起動時にメッセージなどを出力するための端末で、「本物」の制御端末ではない。これはLinuxカーネルのソース上でブートするときのプロセスなどをシグナルによって止めるられることを防御するためにそのような設計になっているよう。ちなみに/dev/console以外にも制御端末としてなれないのは/dev/consoleだけでなく * /dev/tty0 * /dev/tty * PTYのマスター側 の3つがある。 さて、ジョブコントロールを行うためには「本物」の制御端末でなければならない。これを探してくれるのがcttyhackコマンド。このコマンドは「本物」の制御端末を探してその端末を現在の端末に設定してくれるコマンド。このコマンドを使うことで「本物」の端末を制御してくれる。 しかし、cttyhackを行っても依然としてcan't access ttyと怒られてジョブコントロールを切られた状態で/bin/shが起動してしまう。これはttyを制御端末にするために満たすべき次の条件のうち、cttyhackが2番めの条件のみを解決するため。 1. セッションオーナーかつプロセスグループリーダーである 2. 現在の端末が「本物」の制御端末であること 3. {{{^}}}Cや{{{^}}}Zなどが入力できる 4. 標準入力のttyを制御端末にすることができる 2番目の条件は満たされているが、1番目の条件が満たされていないので、/bin/shはジョブコントロールを制御できない。つまり、/bin/shをセッションオーナーかつプロセスグループリーダーにすればこの問題は解決する。 /bin/shをセッションオーナーかつプロセスグループリーダーにするにはsetsid(1)を次のように実行すればプログラムをセッションオーナーかつプロセスグループリーダーに変更することができる。 {{{#!highlight bash setsid cttyhack /bin/sh }}} setsidはプログラム名を引数にとって新しいセッションを開いてプログラムを実行するコマンド。内部でfork()した子プロセスが、setsid()システムコールによって新しいセッションを開く。親プロセスは子プロセスがsetsid()システムコールを発行する前に終了してしまうため、setsid(2)のマニュアルにも書いてあるとおり、子プロセスが新しく開いたセッションのオーナーとなる。更に、開いたセッションでは子プロセスのみが存在しているため子プロセスがプロセスグループのリーダーとなる。この状況で、execvp()システムコールで指定されたコマンドを実行することでsetsidコマンドで指定されたコマンドがセッションオーナーかつプロセスグループリーダーとなるようになっている。以上でsetsidコマンドによって1番目の条件が満たされる。 以上の手順を踏むことで起動した/bin/shでジョブコントロールができるようになる。