システム奮闘記:その76

Linuxシステム起動スクリプト(inittab、rc.sysinit、rc)を読んでいく



Tweet

(2009年2月23日に掲載)
はじめに

 今回、取り上げる部分を図式化しました。

今回、取り上げる箇所
パソコンの電源を入れると、最初にBIOSの中にあるプログラムを
CPUが読み込んで各デバイスを認識させる。
そしてブートローダーを読み込み、起動させるOSを選択する。
詳しい事は「システム奮闘記:その71」の
ブートローダー(GRUB)についてをご覧ください。

今回、ブートローダーを使ってLinuxを読み込んだ後の
Linuxの各設定の初期化について取り上げます

 一見、短くて簡単そうに感じるのだが、実は知らなかった事が噴出し
大変な状態になりました。
 その格闘話を始めたいと思います。

注意点  CentOS51の話を中心に、RedHat系の起動スクリプトの話です。  他のディストリビューションに関しては、取り上げるだけの 実力も元気もなかったので割愛いたします。
2005年、最初の挑戦が始まった Linuxの設定で、私は、Postfixのデーモンを起動時から立ち上げる時、 rc.localファイルにPostfixのデーモンが起動するように書き込んだ。 だが、なぜ、rc.localファイルに書き込めば良いのかと聞かれると 毎度の如く同じ答えが返ってくる。  なぜか、わかりませーん (^^)  となる。  要するに、どこかで仕入れた知識を鵜のみにしている結果なのだ。  そこで、Linuxの起動で、カーネルが起動した後、 各種設定が行われる仕組みについて知ろうと考えた。  さて、次の本を用意した。 「図解でわかるLinux環境設定のすべて」(西村めぐみ:日本実業出版社) 「RedHat7.3対応で作るLinux7」(サーバー構築研究会:秀和システム) しかし、大きな問題が立ちはだかっていた。それは 全くシェルが読めませーん (^^;; そうなのです。全くと言って良い程、シェルを知らないのだ。 以前、データを夜間にテープやCD-RWへバックアップとるために、 cronを動かすシェルを組んだ事があるが、ほとんど見よう見まねな上、 ちょっとかじった程度で、とても知っているうちには入らない。 だが、Linux起動の際、カーネルを読み込んだ後の 初期設定の動きを見て行くには、シェルの読解力が必要となる。 それだけではない。システム管理の簡略化、作業の簡素化にも シェルは充分な働きをする。そんな便利な言語を知らないのだ。 そこで、シェルの勉強から始める事にした。
シェルのお勉強  シェルの勉強のため、次の本を用意した。 「Linux & UNIX Shellプログラミング」(デイビッドタンズリー著: 服部 由実子訳:ピアソンエデゥケーション) 分厚い本で約600ページもある。メチャクチャ分厚い。 2003年に、夜間にデータをCD-RWへバックアップをとる時に、 シェルを勉強するために買ってもらった本なのだが、必要な部分だけを ちょっとかじっただけで、ほとんど読んでいない。 いつもなら必要な部分を、つまみ食いしようとするのだが、今回は考えた。 どーせなら、徹底的に覚えよう! 「シェルなんて、わかんなーい」からの脱却を目指す事にした。 本ではBourne Shellについて触れられている。  Bourne Shellって何やねん? 初めの一歩から、つまづく私。前途多難の気運が立ち込める・・・。 シェルにはいくつか種類があるのだが、本にはBourne Shellは 全てのUNIXに標準で使われているシェルだと書かれている。 Linuxで使われているbashは、ほとんどBourne Shellを基本にしているという。 ここからシェルとの格闘が始まった。 なにせ、600ページもある。読み出した頃、気が遠くなる思いだった。 ただでさえ、物覚えの悪い私なのに、三十路を過ぎてからというもの、 自覚可能なくらい記憶力が落ちているのがわかる。 2日前の晩飯は何を食べたか覚えてないくらいだ。 そこで単にシェルの例文を実験するだけでなく、ノートを用意して 覚えた内容、何がわからないのか、気がついた事などをまとめていった。  仕事で空いて時間を見つけては、シェルの勉強を行う。  だが、分厚い本を読むために、凄い時間とエネルギーが必要だ。 それに、いづれはシェルを覚えないといけないため できるだけ早くシェルの本を読み終えたい。  なので、できるだけシェルの学習に時間を使った。 カフェイン中毒の私。コーヒーをガバガバ飲む。 頭を回転させているため、カフェインがないとやってられない。 受験勉強のように知識を詰め込むが、しかし、すぐに抜ける。ザル頭だ。 おまけに、夕方になると、疲れ果てて放心状態になっていた (^^;; もし、この本は原文で読むと素晴らしい内容だと感じた。 だが、翻訳がイマイチな部分が結構あり、読みづらい所がある。 そのため、頭の中で言葉を変換させていたので、エネルギーを使う (--;; 面白い雑学や、ワルな話などは、今でも頭にスッと入るのだが、 勉強となると、1歩前進、半歩後退の繰り返しだった。 覚えては忘れ、覚えては忘れで、己の記憶力のなさには感心する。 どうも真面目な事になると、私の脳ミソの海馬が働かないようだ (^^;; 本も中盤にさしかかった頃、LILOのMLで、あるコマンドが話題になった  そのコマンドは  toplessなのだ! (^^)  スケベな事を想像してしまいそうなコマンドなのだが、 シェルが書いている投稿を見て「おっ、この部分、わかる」と思った時は 結構、うれしかった (^^) 本を読みはじめ1ヶ月半、ようやく最後から1つ手前の章に到着した。 章の名前は「実行レベルスクリプト」で、まさに、カーネルを読み込んだ後の 初期化の処理の部分をとり上げていた。 ようやく当初の目的の「カーネルを読み込んだ後の初期化の処理を理解する」 の準備ができあがった。 ちなみに、最後の章はCGIスクリプトなので、最後の章だけは飛ばす事にした。
初期設定の仕組みに挑戦!  一通りシェルの学習を終えた。  そして、当初の目的である「カーネルを読み込んだ後の処理を理解する」に 挑戦する事にした。 そこで、さきほども紹介した3冊の本が登場する。 「図解でわかるLinux環境設定のすべて」(西村めぐみ:日本実業出版社) 「RedHat7.3対応で作るLinux7」(サーバー構築研究会:秀和システム) 「Linux & UNIX Shellプログラミング」(デイビッドタンズリー著: 服部 由実子訳:ピアソンエデゥケーション) さて、いよいよ解剖の開始となる。  手術を行う前の外科医の気分なのだ!!  フフフ、ブラックジャックや、スーパードクターKになった気分だ。 カーネルを読み込んだ後、initというプロセスが動き出す。 これはカーネルが最初に動かすプログラムで、全てのプロセスの親プロセスだ。
initプロセス
[suga@server suga]$ ps aux | more
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  1372  428 ?        S    Feb11   0:16 init [5] 
root         2  0.0  0.0     0    0 ?        SW   Feb11   3:59 [keventd]
root         3  0.0  0.0     0    0 ?        SW   Feb11   0:05 [kapmd]
root         4  0.0  0.0     0    0 ?        SWN  Feb11   0:06 [ksoftirqd_CPU0]
(以下、省略)
initは全てのプロセスの親らしく、PIDが1である。

  このinitプロセスに、どういう指示を与えているのかを記述しているのが
/etc/inittabファイルだ。

  そこで、実際に/etc/inittabファイルを見てみる事にする。

/etc/inittabファイルの中身(RedHat7.3)
# Default runlevel. The runlevels used by RHS are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
# 
id:5:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# Things to run in every runlevel.
ud::once:/sbin/update

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# Run xdm in runlevel 5
# xdm is now a separate service
x:5:respawn:/etc/X11/prefdm -nodaemon
赤い部分が何なのか。
最初から「わからない」でつまずいた (^^;;

 調べてみると「ID」のプロセスエントリー設定だという。

IDのプロセスエントリーの設定
IDのプロセスエントリーは唯一の名前にする。触らない方が無難。
ランレベルの部分は、該当のランレベルかどうかを見る。
もし、該当のランレベルであれば、プロセスを実行させる。
もし、ランレベルが「2」と「3」のどちらかの場合は「23」とし、
「2」、「3」、「4」、「5」のどれかの場合は「2345」という書き方になる。

  その前にランレベルって何?

  よく私がやってしまう事なのだが、意味を知ったつもりで話を書いてしまう。
  だが、立ち止まってみると「あれっ? 何だったっけ?」で、とても人には
説明できない状態に陥る。要するに・・・

 わかっていない証拠 (--;;

  調べてみると、ランレベルとは、Linuxの動作モードを表す数字だという。
  表に表すと以下のようになる。

ランレベルについて
ランレベル 意味状態
0halt システムが終了に向かう状態
1single システムがシングルユーザーモードで動作する状態
一人しかログインできない状態で、root権限が与えられる。
ログインの認証がなく、いきなりシェルの待ち受け画面になる。

利用例では、rootのパスワードを忘れた時、この状態で立ち上げ
パスワードファイルで、パスワードを消してから、
新たにパスワードを設定する方法がある。
2No NFS NFS関連以外のネットワークが使える状態の事。
セキュリティー的には「3」より安全性が高い事になる。
それ以外は、テキストモードでのログインと同じだ。
3Normal ネットワークの機能が全て使える状態の事。
通常のテキストモードでのログインの状態だ。
4Undefined 特に使い方が決まっているわけではないようだ。
一部のディストリビューターやUNIXでは使われているようだが、
私は使わないので、何も書かないし、書けないのらー (^^;;
5X Windows System GUI環境でログインできる状態。
「3」の状態にGUIを足した状態だ。
6Reboot システムの再起動に向かう状態

  起動、終了、再起動が同じinitプロセスで処理されているのだ。
  そう考えると、Linux(UNIX)は、なかなか奥が深いと思う。


  さて、RedHat7.3の場合、ランレベルの初期値は「5」になっている。
  コマンドで現在のランレベルを確認したい場合は、/sbin/runlevelコマンドを
使えばわかる。

  さて、inittabファイルのランレベルの初期値を「3」に変えてみる事にした。

ランレベルの初期値を「3」に変えてみた
id:3:initdefault:

  そして、再起動をしてみたら、グラフィックスモードではなく、
テキストモードのログイン画面が出てきた。

ランレベルの変更前と後
ランレベル「5」で起動 ランレベル「3」で起動
ランレベルを「3」にする事で、GUIの画面ではなく
テキストモードでログイン画面にたどり着く。

 ランレベルが「3」だと、重たいGNOMEが立ち上がらないで、
軽いテキストモードになる。非常に軽快に動く。

  これは便利や!

  と思った。
  ランレベルが「3」でも、各種デーモンが正常に動く事を確認した後、
DNSサーバーのランレベルの初期値を「3」にした。
  なぜなら、その時のDNSサーバーは、Pentium133MHz RAM48Mで動かしているため

  GNOMEは非常に重いのらー!!

  ハードディスクがガリガリと音を立てながら、根性で動かしていた。
  今まではテキストモードにする方法を知らなかったので、
止む得ず、GNOMEを使っていたのだが、今回、テキストモードにする方法を
知ったので、Pentium133MHzでも快適に動かせる!!

  ちなみに、その当時の自宅のパソコンにPlamoLinux4.0を入れていて
いつもテキストモードでログインを行い、startxコマンドで、
X Window Systemを立ち上げていた。
 今までは「なんてテキストモードで起動するねん」と不思議に思っていたが
今回、自宅のLinuxの/etc/inittabファイルを見て、ランレベルの初期値が
「3」になっているのが確認できたので、納得できた (^^)V


  ランレベル「5」で立ち上げたが、Linuxを再起動させずに
ランレベル「3」にしたい場合、次のコマンドがある

 initコマンドなのだ!

 さて、いくらコマンドを知っていても、自分の目で確かめないと
気が済まない。なので、実際に使ってみる。

init コマンドの使い方
initの引数にランレベルの値を記入すれば良い。
これを実行させると、該当のランレベルの画面が出てくる。

 実際に、どうなるのか。試してみた。

init コマンドの実行前
init コマンドの実行後
テキストモードのログイン画面が出てきた。
実験は成功なのだが、一度、ログインしているはずなのに
initコマンドを使う事で、再ログインしなければならない。

なぜだろうかと考えてしまった。



  さて、inittabファイルをみていると、「CTRL」+「Alt」+「DEL」の
3つのキーを同時に押した時の処理も記述されている。

「CTRL」+「Alt」+「DEL」の3つのキーを同時に押した時の処理(RedHat7.3)
# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
どのランレベルでも、この3つのキーが同時に押されると、
システムを正常終了させる設定になっている。

Windows98などで、システムが固まった時に、
「CTRL」+「Alt」+「DEL」の3つのキーを連打して
システムを強制終了させるのとは違うのだ。

  仮想コンソールの設定について見てみる事にした。

仮想コンソールの設定(RedHat7.3)
# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

  ところで仮想コンソールって何?

  うーん、知らない事が多すぎる (--;;

  その前にコンソールって何?

  己の無知な度合を露呈してしまう。
  そこで、まずは、コンソールの意味を、google先生を使って調べてみた。

  コンソールとは「パソコンモニターの事」だという。

  仮想コンソールとは、仮想モニターの事なのだろうか。
  となれば、どういう事なのだろうか。

  そこで「仮想コンソール」とは何かを調べてみた。
  すると、仮想コンソールとは、X Window Systemやネットワークを使わなくても、
いくつかのセッションを行う事を可能にする物。

  一体、どういう事やねん・・・

  よくよく読んで行くと、次のような事ができるという。
  テキストモードで起動した場合で、ftpでデータをダウンロードしながら
メールを読みたい場合があるとする。ftpは対話型式のソフトなので、
「&」を使って、バックグラウンドで走らせて、メールを読むという事は不可能だ。

  そこで、「CTRL」+「Alt」+ファンクションキー」を使って画面を切り替え
あたかも1台の端末で、何台も端末があるように見せる機能だ。
  (注意!) 左側の「Alt」キーを使ってください。右側だと使えませんので。

  上の設定では、6台に見せる事ができる。

仮想コンソールとは何か
仮想コンソールには「ttyN」(N:番号)が割り振られているため、
プロセスを見た場合、どの仮想コンソールから立ち上げているのかが
わかる仕組みになっている。

通常、ログインした時は「tty1」になる。
画面の切り替えは、tty2への場合は、「CTRL」+「Alt」+「F2」
tty3への場合は、「CTRL」+「Alt」+「F3」という方法になる。

  「百聞は一見にしかず」の言葉の通り、自分の目で確かめたい私。
  そこで、自宅のLinuxがテキストモードで立ち上がるので実験する事にした。
  ログイン画面が出てくるので、一旦、ログインを行う。
  そして「CTRL」+「Alt」+「F2」キーの3つを同時に押すと・・・

  ホンマに画面が切り替わる!

  ログイン画面が出てきたのだ!
  そして「CTRL」+「Alt」+「F1」キーを押すと、元の画面に切り替わる。

  これだと、ftpでデータをダウンロード中に、画面を切り替えて
emacsでメールを読んだり、プログラムを書いたりなどできる。

  「tty」という言葉が出てきた。
  仮想コンソールに「ttyN」(N:番号)が割り振られているが・・・

  「tty」って何 (^^;;

  そうなのです。「tty」という言葉を使いながら意味を知らないのである。
  知ったかぶりして、わざと「ttyとは何か?」に触れない事も可能だが
そんな事をしても、用語の使い方を誤ってしまう可能性が高いため

  すぐにメッキが剥がれるのらー!!

  メッキが剥がれた方がカッコが悪い (^^;;

  そこで調べる事にした。
  「tty」とは、「Teletype」から由来している。
  とっても「teletypeって何やねん」となるので、それも調べる事にした。
  
  Teletype社という会社が、遠隔操作で行えるタイプライターを
開発した事があるという。遠隔操作といってもネットワークを使ってではなく
今のキーボードのように、コードを使って、キーボードから本体に信号を送って
入力するタイプの物を1920年代に開発したという。

  Teletype社の名前から、入力デバイスを「tty」と名付けたようだ。

  Linux(UNIX系)の場合、キーボードやマウスのデバイスもファイルとして
扱われている。
  ふと思った。そういえば、キーボードからの入力デバイスファイルとして
/dev/ttyがある。

 これを以下のように使う事ができる。

入力デバイスファイル(/dev/tty)の実験
[suga@linux ~]$ cat sample-file > /dev/tty
This is a sample text.
I open this file by 'cat' command!
[suga@linux ~]$
ファイルの中身が、入力デバイスファイルへ送られるため
Linuxはキーボードから入力されたと感知する。
そのため、Linuxがキーボードから入力されたと認識した
文字列を画面に表示される仕組みだ。

  ところで、仮想コンソールと「tty」との関連性はと考えるが何も出てこない。
キーボードからの入力と仮想コンソールとが結びつかないからだ。

  だが、色々、見ていくと、ダイヤルアップ接続する場合には、mgettyという
デーモンが使われる。「modem get tty」の略だと思う。
  シリアルポートからの入力の場合は、gettyのデーモンが使われる。
「get tty」の略だと思う。
  COM1のデバイスファイルとしては、/dev/ttyS0が使われる。
  この事から「tty」はデータの入力に関する意味があると思った。

  だが、誤解をしている可能性があるので調べないと気が済まない。
  そこで、調べてみる事にした。

  「ttyN」(N:番号)は、仮想コンソールの意味だという。
  確かに入力という意味とは結び付く(かもしれない)
  ふと思った。仮想コンソールも/dev/tty2ファイルなので
データを送れば、2つ目の仮想コンソールに文字が入力されるのでは。
  そこで、実験を行ってみる事にした。すると予想通りだった。

/dev/tty2ファイルにデータを送る実験
「I live in kobe」を/dev/tty2ファイルにデータを送ると
「CTRL」+「Alt」+「F2」で切り替えた仮想コンソール(tty2)に書き込まれている。

  ところで、疑問が湧いてきた。最初にログインした場合は、「tty1」になる。
  では、「tty0」って何なのだろうか?
  調べて見ると「tty0は、カレント仮想端末のエイリアス」だという。

  カレント仮想端末って何?

  まずは、実験としてcat ファイル名 > /dev/tty0でデータを送り
どんな振るまいをするのか確かめてみる事にした。


cat ファイル名 > /dev/tty0の実験
[suga@linux ~]$ cat sample-file > /dev/tty0
This is a sample text.
I open this file by 'cat' command!
[suga@linux ~]$
ファイルの中身が表示された。
一体、どうしてだろうかと考えてしまった。

 なぜ、ファイルの中身が表示されたのか調べてみた。
 すると「日本の Linux 情報」のLinux JFを見ると、次のように書かれている。

「日本の Linux 情報」のLinux JF
/dev/tty0 はカレント仮想端末のエイリアスで、システムから
メッセージが送られます。
このように、システムメッセージは表示中の仮想端末にかかわらず
コンソール (モニタ) で見ることができるでしょう。

  この事から私は「tty0は、システムが現在使っている仮想コンソールへ
メッセージを送るデバイスファイルではないか」と思った。
  そう考えると、cat ファイル名 > /dev/tty0の実験結果が納得できる。


  ちなみに、失敗談としてcat > /dev/tty0としてキーボード入力から
表示させてみようとやってみたら・・・

  画面が固まってもうた (TT)

  ニッチモサッチもいかないので、強制的に電源を切る事にした (^^;;

psコマンドを使うと、使っている仮想コンソールがわかる
suga@jitaku[~]% ps aux
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
(以下、省略)

  さて、自宅で実験として、テキストモードの状態で立ち上げ、
仮想コンソールの機能を使って、emacsでファイルを編集しながら、
会社にSSHでログインをやってみる事にした。

  この時、tty2(「CTRL」+「Alt」+「F2」)は、ログインしただけの状態。
  tty3(「CTRL」+「Alt」+「F3」)では、emacsを使ってファイルの編集。
  tty4(「CTRL」+「Alt」+「F4」)では、SSHを使った会社へログイン。

  そして、起動中のプロセスを見てみる事にした。

起動中のプロセス(一部抜粋)
suga@jitaku[~]% ps aux | more
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
suga       191  0.0  1.3  3796 1732 tty2     S    14:00   0:00 -tcsh
suga       192  0.0  1.3  3804 1736 tty3     S    14:00   0:00 -tcsh
suga       193  0.0  1.4  3816 1780 tty4     S    14:00   0:00 -tcsh
suga       542  0.0  1.3  3804 1740 tty1     S    15:05   0:00 -tcsh
suga       588  0.0  1.1  2936 1436 tty4     S    15:53   0:00 ssh -1 -l suga XXX.YYY.ZZZ.AAA -i suga.ppk
suga       602  2.8  4.8  9576 6088 tty3     S    15:54   0:01 emacs file.dat
まさに、どのttyでプロセスが動いているのかが、よくわかる。
emacsは「tty3」で、SSHは「tty4」の仮想コンソールで実際に動いている事が
上の結果でわかる。
もちろん、ログインする場合には、シェルを動かす必要がある。
上の表示で、tcshが動いている事もわかる。

  順に見ているのだが、最初の部分にあるシステムの初期化のための
シェルスクリプトに注目する必要がある。

システムの初期化のシェルを起動させる部分
# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

  さて、このスクリプトを読解していこうと考えた。
  今思えば、結構、ムチャな事を思いついたと思った。

/etc/rc.d/rc.sysinitの中身(RedHat7.3)
#!/bin/bash
#
# /etc/rc.sysinit - run once at boot time
#
# Taken in part from Miquel van Smoorenburg's bcheckrc.
#

# Rerun ourselves through initlog
if [ -z "$IN_INITLOG" ]; then
 [ -f /sbin/initlog ] && exec /sbin/initlog $INITLOG_ARGS -r /etc/rc.sysinit
fi

# If we're using devfs, start devfsd now - we need the old device names
[ -e /dev/.devfsd -a -x /sbin/devfsd ] && /sbin/devfsd /dev

# Set the path
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH

HOSTNAME=`/bin/hostname`

# Read in config data.
if [ -f /etc/sysconfig/network ]; then
    . /etc/sysconfig/network
else
    NETWORKING=no
fi
(ここから先、ずーと長いので省略)
パスを決めたり、変数(HOSTNAME)にホスト名を代入したりするのがわかる。

ふと、青い部分に注目した。
/etc/sysconfig/networkのファイルが通常のファイルの場合
そのファイルを取り込み、もし、そうでない場合(ファイルがない場合など)は
変数(NETWORKING)に「no」という値が入る条件文になっている。

ちなみに、太字の. /etc/sysconfig/networkの部分は、
シェルの関数や変数を読み込む処理で、/etc/sysconfig/networkの
ファイルの中身を読み込んでいる。

  さて、実際に、/etc/sysconfig/networkファイルの中身を見てみる事にした。

/etc/sysconfig/networkファイルの中身
NETWORKING=yes
HOSTNAME='xxx.yyy.co.jp'
GATEWAY=192.168.1.X

  ネットワークに関する変数を取り込んでいる。
  /etc/rc.sysinitのシェルは、いくつかのシェル変数やシェル関数の
ファイルを取り込んでいる。
  そこで、どんな物を取り込んでいるのか、図で示す事にした。

rc.sysinitが取り込んでいる主なファイル(RedHat7.3)
ネットワークや時間設定、キーボードに関する変数や設定などの
ファイルを取り込んでいる。

図では主なファイルを描いていますが、私の見た感じで
「主なファイル」を選んでいますので、本当は重要なファイルでも
見落としている可能性があります。

 色々なファイルを見ないとLinuxの起動の際の初期化の処理が
わからないと思った。

 シェル以外にも、色々な知識が必要なので、
解読作業は困難だというのは明白だったのだが
ここまで来たからには

 勢いで突撃なのだ!

 という感じで強引に前に進む事にした。


 ファイルシステムの点検の有無に関する部分にさしかかる。
 どんな記述になっているのか、見てみる事にした。

rc.sysinitを一部抜粋(RedHat7.3)
if [ -f /forcefsck ]; then
        fsckoptions="-f $fsckoptions"
elif [ -f /.autofsck ]; then
        echo $"Your system appears to have shut down uncleanly"
        AUTOFSCK_TIMEOUT=5
        AUTOFSCK_DEF_CHECK=no
        [ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ] ; then
                AUTOFSCK_OPT=-f
        else
                AUTOFSCK_OPT=
        fi

        if [ "$PROMPT" != "no" ]; then
                if [ "$AUTOFSCK_DEF_CHECK" = "yes" ] ; then
                        if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press N within %d seconds to not force file system integrity check..." n ; then
                                AUTOFSCK_OPT=
                        fi
                else
                        if /sbin/getkey -c $AUTOFSCK_TIMEOUT -m $"Press Y within %d seconds to force file system integrity check..." y ; then
                                AUTOFSCK_OPT=-f
                        fi
                fi
                echo
        else
                # PROMPT not allowed
                if [ "$AUTOFSCK_DEF_CHECK" = "yes" ] ; then
                        echo $"Forcing file system integrity check due to default setting"
                else
                        echo $"Not forcing file system integrity check due to default setting"
                fi
        fi
        fsckoptions="$AUTOFSCK_OPT $fsckoptions"
fi
赤い部分はルートディレクトリに「.autofsck」ファイルが存在するのを
真とする条件文だ。
青い部分は条件文が「真」(「.autofsck」ファイルが存在する)場合
「システムを正常に終了しなかった」と表示する部分だ。

ピンクの部分はファイルシステムの点検開始までの秒読みを行う部分。

 上のピンクの部分は、ただ正しくは秒読みのコマンドというより
文字入力のための待ち受けコマンドなのだ。
 いかにもgetkeyコマンドが何かを知っていた感じの書き方だが
実際には・・・

 知らなかったのだ (^^)

 そう、知らなかったのだ。事務員なので知らなくて当然なのだ。
 今回のこの話で初めて知ったのだ。

 という事で調べた結果を載せる事にした。

getkeyコマンドの用法について
その1
引数に文字を入れると、その文字が入力されるまで
待ち受け画面になる。
その2
オプションに「-c」をつけて、後ろに数字を入れると
その数字の秒の間、待ち受け画面になる。
その秒が過ぎると、何も入力されなかった事になる。

オプション「-m」をつけて、後ろの文字列を入れると
その文字列が表示される。
その3
文字列の中にフォーマット指定子を入れると
秒読みを行ってくれる。

 これで、getkeyコマンドの使い方はわかった。
 次に、ファイルシステムの点検に記述の部分を見てみる。

rc.sysinitを一部抜粋(RedHat7.3)
if [ -z "$fastboot" -a "$ROOTFSTYPE" != "nfs" ]; then 

        STRING=$"Checking root filesystem"
        echo $STRING
        initlog -c "fsck -T -a $fsckoptions /"
        rc=$?

        if [ "$rc" = "0" ]; then
                success "$STRING"
                echo
        elif [ "$rc" = "1" ]; then
                passed $"$STRING"
                echo
        fi

        # A return of 2 or higher means there were serious problems.
        if [ $rc -gt 1 ]; then
                failure "$STRING"
                echo
                echo
                echo $"*** An error occurred during the file system check."
                echo $"*** Dropping you to a shell; the system will reboot"
                echo $"*** when you leave the shell."

                PS1=$"(Repair filesystem) \# # "; export PS1
                sulogin

                echo $"Unmounting file systems"
                umount -a
                mount -n -o remount,ro /
                echo $"Automatic reboot in progress."
                reboot -f
        elif [ "$rc" = "1" ]; then
                _RUN_QUOTACHECK=1
        fi
fi
赤い部分はファイルシステム点検を実行させる部分だ。
initlogコマンドに「-c」オプションをつけると
後ろのコマンドを実行させる意味になる。

青い部分は、コマンド実行の結果(この場合、ファイルシステムの点検)、
正常なら「0」、異常なら、その他の数値が返り値となる。
その返り値を変数「rc」に代入させている。

ピンクの部分はファイルシステムの点検が正常に終了した時に
処理される内容だ。
異常終了の場合は、ファイルシステムの点検が失敗に終わった事から
「起動できないという」表示が出てきて、再起動される。

 実際に、Linux(RedHat7.3)を強制終了させた後に起動すると
ファイルシステムの点検のための秒読み待ち受け画面が出てくる。

RedHat7.3の強制終了後の起動時の様子
ファイルシステム点検前の待ち受け画面
ピンクで囲んだ部分の拡大
Your system appears to have shut down uncleanly
Press Y within 2 seconds to force file system integrity check...
ファイルシステム点検中の様子
ピンクで囲んだ部分の拡大
Your system appears to have shut down uncleanly
Press Y within 2 seconds to force file system integrity check...
Checking root filesystem
/: clean, 64306/1003904 files, 247416/2006116 blocks
                                                           [  OK  ]
Remounting root filesystem in read-write mode:             [  OK  ]
Finding module dependencies:                               [  OK  ]
/boot was not cleanly unmounted. check forced.
/boot: |=====================================================    - 95.0%

 さて、起動時に.autofsckファイルがあると
ファイルシステムの点検が行われる事はわかったのだが
よく考えてみると

 .autofsckファイルって何やねん!

 見た感じでは、Linuxを正常終了させなかった事を知らせるための
目印のファイルだ。
 でも、断言できる自信がないので、調べてみる事にした。

.autofsckファイルの生成と消去が行われる場所
Linuxの起動時
Linux起動時のシェル「/etc/rc.d/rc.sysconfig」で
.autofsckファイルが存在するのどうかを調べる。

その後、一度、削除された後、.autofsckファイルの空ファイルを作成する。
Linuxの終了時
Linuxの再起動のための終了処理
Linuxの終了時は「/etc/rc.d/rc0.d/S01halt」のシェル。
Linuxの再起動を行う際に、一度、Linuxを終了させる処理は
「/etc/rc.d/rc6.d/S01reboot」が動くのだが、どちらとも
.autofsckファイルの消去を行う。

もし、正常終了させていない場合、.autofsckファイルの消去は
消去されていないため、Linux起動時のシェルで、それを検知し
ファイルシステムに異常がないかどうかを点検を行う仕組みになっている。

 実際のシェルの記述を拾い上げてみた。

.autofsckファイルの生成と消去が行われる場所
Linuxの起動時
(/etc/rc.d/rc.sysconfig)の一部抜粋

if [ -f /forcefsck ]; then
        fsckoptions="-f $fsckoptions"
elif [ -f /.autofsck ]; then
        echo $"Your system appears to have shut down uncleanly"
        AUTOFSCK_TIMEOUT=5
        AUTOFSCK_DEF_CHECK=no

(途中、省略)

# Clean out /etc.
rm -f /fastboot /fsckoptions /forcefsck /.autofsck /halt /poweroff

(途中、省略)

# create the crash indicator flag to warn on crashes, offer fsck with timeout
touch /.autofsck
赤い部分が.autofsckファイルの有無を調べる部分
もし、ファイルが存在すれば、ファイルシステムの点検が始まる。

青い部分は.autofsckファイルの作成。
touchコマンドなので空ファイルが作成される。
Linuxの終了時
(/etc/rc.d/rc0.d/S01halt)と
(/etc/rc.d/rc6.d/S01reboot)の一部抜粋

# remove the crash indicator flag
rm -f /.autofsck

.autofsckファイルの消去が行われる。
もし、正常終了しない場合は、この処理が行われないため
異常終了後、.autofsckファイルの有無で
正常終了したのかどうかが判定可能になる。

 システムが正常に終了したのかどうかの判定は
いままで、どういう仕組みなのか、わかっていなかったが実は

 単純な仕組みだったのだ!

 を初めて知った。


 ここでふと思った。

  Windows98の強制終了後もファイルシステムの点検が行われる。
 これも同じ原理なのだろうか。

Windows98の強制終了後の
ファイルシステムの点検の様子
今までWindows98(他のWindowsも含め)、強制終了した場合
ファイルシステムの点検を自動的に行うため
どうやって強制終了を認識できるのか不思議だった。

おそらくLinux(RedHat7.3)同様に、強制終了の判定のための
ファイルがあるのかなぁと思ったりした。

 この先も、/etc/rc.d/rc.sysinitを読んでいこうとしたが

 全然、わからへん (TT)

 だった。

 モジュールと言われても、その当時はわかっていなかったのだ。
 わからない所を、読み飛ばすと

 わかる所があらへん (^^;;

 だった。

 そのため、/etc/rc.d/rc.sysinitの解読を諦める事にした。

inittabファイルの中で「rc.sysinit」を実行させた後 「rc」のシェルファイルが実行される。
/etc/inittabファイルの中身(RedHat7.3)
# Default runlevel. The runlevels used by RHS are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
# 
id:5:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# Things to run in every runlevel.
ud::once:/sbin/update

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# Run xdm in runlevel 5
# xdm is now a separate service
x:5:respawn:/etc/X11/prefdm -nodaemon
赤が部分が先ほどまで取り上げていた
「rc.sysinit」のシェルの部分。

青い部分は、これから取り上げる部分で
/etc/rc.d/rcのシェルを呼び出し
起動させる部分。
各ランレベルに対応した引数を使って
起動させている。
そのためランレベルによって振舞が異なってくる
太い文字列部分はランレベル「5」の場合の部分になる。

 /etc/rc.d/rcのシェルを見ていく事にした。

/etc/rc.d/rc の一部抜粋
#!/bin/bash
#
# rc            This file is responsible for starting/stopping
#               services when the runlevel changes. It is also
#               responsible for the very first setup of basic
#               things, such as setting the hostname.
#
# Original Author:       
#               Miquel van Smoorenburg, 
#

# Now find out what the current and what the previous runlevel are.
argv1="$1"
set `/sbin/runlevel`
runlevel=$2
previous=$1
export runlevel previous
赤い部分は、rcシェルを起動させる時の引数を取り出す部分だ。
この引数は、ランレベルの値になる。
ランレベル「5」だと、argv1の変数には「5」の値が入る。

青い部分は「/sbin/runlevel」のコマンドを起動させる。
このコマンドは現在のランレベルと、前回のランレベルの値を
出力させるコマンドだ。

ピンクは変数「runlevel」に現在のランレベルの値を代入。
緑は1つ前のランレベルの値を変数「previous」に代入

 1つ前のランレベルの値がわかる。
 実際に /sbin/runlevel のコマンドを使ってみる。

/sbin/runlevel のコマンドを使ってみる
[suga@linux]$ /sbin/runlevel
N 5
[suga@linux]$ 
「N」と「5」が出力された。

このコマンドを使った時、ランレベル「5」で動かしていたので
現在のランレベルだという事はわかる。

「N」は1つ前のランレベルの値である事はわかるが、
数字はなく「N」なので、なんでやねんと思う。

でも、起動した時からランレベルを変更していなければ
1つ前のランレベルは存在しない。
推測だが「NULL」の意味で「N」を使っているかもしれない。

 ランレベルを「5」から「3」に変更してみた。

ランレベルを「5」から「3」に変更し
/sbin/runlevel のコマンドを使ってみる
[suga@linux]$ init 3

(GUIからテキストモードに変更される)

[suga@linux]$ /sbin/runlevel
5 3
[suga@linux]$ 
「5」と「3」が出力された。
出力結果の手前側が1つ前のランレベルが出て
後ろに現在のランレベルが出てくる。

 なぜ1つ前のランレベルの情報が必要なのか。
 この時、シェルの中身を見ても、わかるだけの実力はなかった。
 なので、深く見ていく事はしなかった。

 次に進める事にした。

/etc/rc.d/rc の一部抜粋
# Is there an rc directory for this new runlevel?
if [ -d /etc/rc$runlevel.d ]; then
        # First, run the KILL scripts.
        for i in /etc/rc$runlevel.d/K*; do
                # Check if the script is there.
                [ ! -f $i ] && continue

                # Don't run [KS]??foo.{rpmsave,rpmorig} scripts
                [ "${i%.rpmsave}" != "${i}" ] && continue
                [ "${i%.rpmorig}" != "${i}" ] && continue
                [ "${i%.rpmnew}" != "${i}" ] && continue

                # Check if the subsystem is already up.
                subsys=${i#/etc/rc$runlevel.d/K??}
                [ ! -f /var/lock/subsys/$subsys ] && \
                    [ ! -f /var/lock/subsys/${subsys}.init ] && continue

                # Bring the subsystem down.
                if egrep -q "(killproc |action )" $i ; then
                        $i stop
                else
                        action $"Stopping $subsys: " $i stop
                fi
        done
青い部分を見てみる。
「/etc/rc$runlevel.d」で見た事もないような
ディレクトリ名の感じだが、rc$runlevel.dを注意深く見ると
「$runlevel」はランレベルの値の入った変数なのだ。
なので「rc」と「ランレベルの値」と「.d」を合体させた
文字列という意味になる。
もし、ランレベル「5」の場合、rc$runlevel.dは
「rc5.d」になる。

青い部分の条件文の意味はランレベル「5」の場合なら
「/etc/rc5.d」のディレクトリの存在の有無の判定になる。
もし存在する場合は「真」になる。

赤い部分は「/etc/rcN.d/」(Nはランレベルの値)の
ディレクトリの中にあるファイル名の頭が
「K」のシェルを全て拾いあげている所だ。

そして拾い上げたファイル名のシェルを実行させているのが
ピンクの部分だ。

 次に進む。

/etc/rc.d/rc の一部抜粋
        # Now run the START scripts.
        for i in /etc/rc$runlevel.d/S*; do
                # Check if the script is there.
                [ ! -f $i ] && continue
                # Don't run [KS]??foo.{rpmsave,rpmorig} scripts
                [ "${i%.rpmsave}" != "${i}" ] && continue
                [ "${i%.rpmorig}" != "${i}" ] && continue
                [ "${i%.rpmnew}" != "${i}" ] && continue

                # Check if the subsystem is already up.
                subsys=${i#/etc/rc$runlevel.d/S??}
                [ -f /var/lock/subsys/$subsys ] || \
                    [ -f /var/lock/subsys/${subsys}.init ] && continue
                    
                # If we're in confirmation mode, get user confirmation
                [ -n "$CONFIRM" ]  && 
                  { 
                    confirm $subsys
                    case $? in
                      0)
                        :
                      ;;
                      2)
                        CONFIRM=
                      ;;
                      *)
                        continue
                      ;;
                    esac 
                  }

                # Bring the subsystem up.
                if egrep -q "(daemon |action )" $i ; then
                        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                                unset LANG
                                unset LC_ALL
                                unset TEXTDOMAIN
                                unset TEXTDOMAINDIR
                                exec $i start
                        else
                            $i start
                        fi
                else
                        if [ "$subsys" = "halt" -o "$subsys" = "reboot" -o "$subsys" = "single" -o "$subsys" = "local" ]; then
                            if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                                unset LANG
                                unset LC_ALL
                                unset TEXTDOMAIN
                                unset TEXTDOMAINDIR
                                exec $i start
                            fi
                            $i start
                        else
                            action $"Starting $subsys: " $i start
                        fi
                fi
        done
赤い部分は「/etc/rcN.d/」(Nはランレベルの値)の
ディレクトリの中にあるファイル名の頭が
「S」のシェルを全て拾いあげている所だ。

そして拾い上げたファイル名のシェルを実行させているのが
ピンクの部分だ。

 ところでランレベルが「5」の場合、/etc/rc5.dのディレクトリに
どんなシェルがあるのか見てみる事にした。

/etc/rc5.d ディレクトリの中身(RedHat7.3)
[suga@linux]$ ls
K20nfs        K74ypserv    S14nfslock     S56xinetd    S95anacron
K34yppasswdd  K74ypxfrd    S17keytable    S60lpd       S95atd
K35vncserver  S05kudzu     S20random      S80sendmail  S97rhnsd
K46radvd      S08ipchains  S25netfs       S85gpm       S99local
K50snmpd      S08iptables  S26apmd        S90FreeWnn
K50snmptrapd  S10network   S28autofs      S90canna
K65identd     S12syslog    S55sshd        S90crond
K74ntpd       S13portmap   S56rawdevices  S90xfs
[suga@linux]$
これだけシェルがある。
大抵は、各ソフトの起動のためのスクリプトなのだ。
rpmでのソフトのインストールが便利なのは、
インストール時に自動的に起動用のスクリプトを用意し
このディレクトリに設置してくれる事なのだ。

 その中の「S10network」に注目してみる事にした。
 ふと次の行を見た。

/etc/rc5.d/S10network の一部抜粋
CWD=`pwd`
cd /etc/sysconfig/network-scripts

. network-functions
どうやら「/etc/sysconfig/network-scripts」の
ディレクトリに移動している。
ここに何かがあると思った。

 そこで「/etc/sysconfig/network-scripts」の
ディレクトリに移動してみた。

/etc/sysconfig/network-scriptsのディレクトリ
[suga@linux]$ cd /etc/sysconfig/network-scripts
[suga@linux]$ ls
ifcfg-eth0    ifdown-ppp     ifup-ipsec   ifup-sl
ifcfg-lo      ifdown-routes  ifup-ipv6    ifup-tunnel
ifdown        ifdown-sit     ifup-ipx     ifup-wireless
ifdown-bnep   ifdown-sl      ifup-isdn    init.ipv6-global
ifdown-eth    ifdown-tunnel  ifup-plip    net.hotplug
ifdown-ippp   ifup           ifup-plusb   network-functions
ifdown-ipsec  ifup-aliases   ifup-post    network-functions-ipv6
ifdown-ipv6   ifup-bnep      ifup-ppp
ifdown-isdn   ifup-eth       ifup-routes
ifdown-post   ifup-ippp      ifup-sit
[suga@linux]$

 色々なシェルスクリプトのファイルがある。
 その中で「ifcfg-eth0」のファイルを見てみた。

「ifcfg-eth0」のファイルの中身
DEVICE='eth0'
BOOTPROTO='none'
ONBOOT='yes'
TYPE='Ethernet'
USERCTL='no'
NETWORK='192.168.10.0'
BROADCAST='192.168.10.255'
IPADDR='192.168.10.100'
GATEWAY='192.168.10.254'
NETMASK='255.255.255.0'
ネットワークに関する設定が記述されている。
ここを触れば設定が変更できるのではないかと思った。

 UNIX(Linux)は、設定関係がファイルで記述されているので
Windowsと違って「見える形」になっていると言われる。

 だが、私の場合、どのファイルを触って良いのか
全く知らなかったので、ネットワークの設定といえ
GUI上にある設定ツールを使っていた。

 RedHat7.3の場合、GNOMEのツールを使っていた。

RedHat7.3のGUI(GNOME)での
ネットワークの設定の様子
拡大

 さて、IPアドレスを指定した形になっているのだが
GUIを使って以下のようにIPアドレス自動取得の設定を変えてみる。

RedHat7.3のGUI(GNOME)での
ネットワークの設定の様子
設定をIPアドレス指定から、IPアドレス自動取得にしてみた。

 そして再び「ifcfg-eth0」ファイルを見てみる。
 すると

 記述が変わっているやん!

「ifcfg-eth0」ファイルを見てみる
DEVICE='eth0'
BOOTPROTO='dhcp'
ONBOOT='yes'
TYPE='Ethernet'
USERCTL='no'
NETWORK=192.168.10.0
BROADCAST=192.168.10.255
独自のIPアドレスを所有している設定から
DHCPクライアントの設定に変わっている。

 この時、思った。

 「ifcfg-eth0」を触れば
 ネットワークの設定変更ができるかも

 早速、ファイルの記述を変更した。

 案の定・・・

 見事、設定が変わった!!

 これでGUIの操作画面を触らなくても
ネットワークの設定が変更できるようになった。


 今まではGUIを使わないと設定変更ができなかったので、
いくらUNIX(Linux)は「見える形」と言われても
私の場合は、Windowsと変わらなかった。

 だが、今回のように設定ファイルの記述を触って変更した事で
この時初めて・・・

 Linuxは見えるのだ!

 と実感した。


 だが、rcのスクリプトも、わからない所が多く、読むのを断念。  次に取り掛かったのは、/etc/rc.d/rc.local のファイルは、 どの段階で読み込まれて起動するのかだ。  以前からLinuxの起動時に動かしたいソフトがあれば /etc/rc.d/rc.localファイルに記述する話は知っていた。
こんな感じでrc.localファイルに記述する
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

/usr/sbin/postfix start
青い部分はLinuxの起動時に、postfixも起動させるための記述。
あくまでもpostfixをソースコンパイルした時に記述するのだ。
だが、なぜ、このファイルに記述すれば良いのかは知らなかった。

 なので、余計にLinuxの起動時に、どの段階でrc.localファイルが
起動するのか知りたかった。


 だが、rcのスクリプトや、rc.sysinitスクリプトを見ても、
それらしい記述が見つからない。

 「なんでや」と思いながらも、ふとした事で発見した。
 それは /etc/rc.d/rc5.d のディレクトリを見た時だった。

/etc/rc.d/rc5.d のディレクトリ
[suga@linux]$ ls
合計 0
lrwxrwxrwx    1 root     root           13 12月  7 00:03 K20nfs -> ../init.d/nfs
lrwxrwxrwx    1 root     root           19 12月  7 00:03 K34yppasswdd -> ../init.d/yppasswdd
lrwxrwxrwx    1 root     root           19 12月  7 00:03 K35vncserver -> ../init.d/vncserver

(途中、省略)

lrwxrwxrwx    1 root     root           15 12月  7 00:01 S97rhnsd -> ../init.d/rhnsd
lrwxrwxrwx    1 root     root           11 12月  6 23:59 S99local -> ../rc.local
[suga@linux]$ 
/etc/rc.d/rc5.d のディレクトリの中身は、
ランレベル「5」の際に起動するスクリプトの集まりだ。

「ls」コマンドで見るとわからないが、「-l」のオプションをつけると
シンボリックリンクで結ばれているのがわかる。
赤い部分の「S99local」を見ると /etc/rc.d/rc.local の
ファイルからのシンボリックリンクだというのがわかる。

 これでrc.localファイルに記述する理由がわかった。
 なので

 なんだか賢くなった気分 (^^)V

 なのだ!!


 Linux起動時の際のスクリプトの起動の大まかな流れは
以下のように把握した。

起動の際の大まかな流れ
inittabの開始から始まり、rc.sysinitを呼び出したり
rcを呼び出したりする。

rc.localを呼び出す過程もわかった。

 少しづつだが起動の仕組みが見えてきた。
 でも、シェルをバリバリ読める実力は持っていない。
 そのため難攻不落の所を攻めている感じだった。

 システムに関する知識も乏しい。苦戦するし、
攻めるに攻められない所は読み飛ばす。
 その上、他にも色々あって精神的にも疲れていた。
 それでも気力と勢いで突き進んでいた。

  だが、2005年7月、わけありで、一時的だが
「システム奮闘記」が閉鎖に追い込まれたのだった。
  それまで気力と勢いで調べていたのだが・・・

  もう、疲れ果てた・・・

 で挫折してしまった。
  そのためLinuxの起動の際に稼働するシェルスクリプトの
解読作業が止まった。

  3ヶ月後、システム奮闘記は再開したものの、
この原稿を書く気力は起こらなかった。
 何せ、大きな話になると思い、気力が出てこないからだった。

2006年、再度の挑戦するが・・・ 2005年に力尽きて断念したスクリプトの解読作業。 2006年11月に、ふとした事で、inittabからシェルスクリプトを辿る。 すると、あれだけ苦しみながら前に進んでいたシェルだったのだが 意外と読める!! この原稿を放置していた1年間を振り返る。 シェルは触っていなかったが、C言語を触ったり、PHP言語を触ったりした。 自作のftpクライアントソフトを作成したり、Pukiwikiの解読や改造を 行ったりした。 なので、自然とソースを読む力がついているのだ。 この時、思った。 この原稿を書きあげないと!! というわけで、この原稿の続きを書く事にしたが  他の原稿の処理に追われる・・・  のため、あっさりと解読作業を放置する事になった。  そして、2年間の冬眠を行う事になった。
2008年、再々度の挑戦 2008年8月、最後までRedHat7.3を使っていたDNSサーバーが 仮想化技術導入のため、RedHat7.3とお別れになった。  仮想化技術導入について、詳しくは「システム奮闘記:その74」の 「仮想化技術入門 VMware Server導入」をご覧ください。  起動のためのスクリプト解読は時間切れに思われるが、 ここは考え方を変えて CentOS5.1で初期化の部分を読めばええやん!  CentOSもRedHat系なので、似たり寄ったりのはずだ。  そこで、CentOS5.1で解読していく事にした。  ところでLinux(CentOS5.1)の起動のためのスクリプトが どういう段階で動くのか簡単な図にしてみた。
スクリプトの起動の順序
大きくわけて3段階に分かれる。
これはRedHat系であれば、どれも同じみたいだ。


 さて、まずはinittabのスクリプトを見る。
 図にすると、以下の部分の段階の処理だ。

inittabスクリプト起動の段階

inittabのスクリプト(CentOS5.1)
#
# inittab       This file describes how the INIT process should set up
#               the system in a certain run-level.
#
# Author:       Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
#               Modified for RHS Linux by Marc Ewing and Donnie Barnes
#

# Default runlevel. The runlevels used by RHS are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
# 
id:5:initdefault:

# System initialization.
si::sysinit:/etc/rc.d/rc.sysinit

l0:0:wait:/etc/rc.d/rc 0
l1:1:wait:/etc/rc.d/rc 1
l2:2:wait:/etc/rc.d/rc 2
l3:3:wait:/etc/rc.d/rc 3
l4:4:wait:/etc/rc.d/rc 4
l5:5:wait:/etc/rc.d/rc 5
l6:6:wait:/etc/rc.d/rc 6

# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now

# When our UPS tells us power has failed, assume we have a few minutes
# of power left.  Schedule a shutdown for 2 minutes from now.
# This does, of course, assume you have powerd installed and your
# UPS connected and working correctly.  
pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down"

# If power was restored before the shutdown kicked in, cancel it.
pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled"


# Run gettys in standard runlevels
1:2345:respawn:/sbin/mingetty tty1
2:2345:respawn:/sbin/mingetty tty2
3:2345:respawn:/sbin/mingetty tty3
4:2345:respawn:/sbin/mingetty tty4
5:2345:respawn:/sbin/mingetty tty5
6:2345:respawn:/sbin/mingetty tty6

# Run xdm in runlevel 5
x:5:respawn:/etc/X11/prefdm -nodaemon

 これを見た時

 RedHat7.3と一緒やん!

 だった。

 なので、解読する事はないと思って飛ばす事にした。
 次に、rc.sysinitのファイルを解読していく事にした。


rc.sysinitのファイルを解読なのだ

 図にすると以下の部分の段階になる。
rc.sysinitスクリプト起動の段階

  /etc/rc.d/rc.sysinitファイルの内容を見ていく事にした。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
#!/bin/bash
#
# /etc/rc.d/rc.sysinit - run once at boot time
#
# Taken in part from Miquel van Smoorenburg's bcheckrc.
#
コメント文なのだが、このスクリプトを解読する際にあたり
赤い部分は重要な事を書いている。
赤い部分の意味は「このスクリプトはブート時に一度だけ動く」と
書いているのだ。
そのためランレベルを変更した場合でも、このスクリプトは動かないのだ。


 次へ進む。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
HOSTNAME=`/bin/hostname`
HOSTTYPE=`uname -m`
unamer=`uname -r`
赤い部分は起動するマシンのホスト名を取得し、
変数HOSTNAMEに代入する

青い部分はCPUの種類を取得する。
その値を変数HOSTTYPEに代入する。
私の環境の場合「i686」の値になる。

ピンクの部分はカーネルのバージョンを取得し
変数unamerに代入する。
私の環境(CentOS51)なら「2.6.18-53.el5」の値になる。

 ホスト名と、どんな種類のCPUと、OSのどのバージョンを
使っているのかを取得する箇所だ。

 次へ進む。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)

set -m
たった1行のコマンド。
あまり重要ではなさそうに感じる。

 setコマンド。
 たった1行の部分で、あまり重要でなさそうなので
読み飛ばそうと思ったのだが・・・

 どうしても気になるのらー!!

 そう。
 単純そうなコマンドだけに、ここでの処理の意味合いを
知りたくなるのだ。

 そこでsetコマンドの「-m」オプションについて調べてみた。
 manの結果は以下の通りだった。
 「man set」だと英語なので「man bash」で見てみた。

man bash の結果
-m      監視モード。ジョブ制御は有効になります。ジョブ制御 (前述の ジョブ制御セク
        ションを参照) をサポートしているシステム上の対話的シェルでは、このオプショ
        ンはデフォルトで有効です。別のプロセスグループで実行されたバックグラウンド
        プロセスと、これらの終了ステータスが書かれた行が、プロセスの終了時に表示さ
        れます。
setコマンドの「-m」オプションの説明の部分だ。
ジョブ制御を有効にする事みたいだ。

 これを見た時

 ジョブ制御って何やねん (TT)

 そこでman bashのジョブ制御を見てみる事にした。

man bash の結果(ジョブ制御の部分を一部抜粋)
ジョブ制御
       ジョブ制御(job control) とは、プロセスの実行を選択的に停止 (サスペンド/suspend) させ、
       後に再び実行を続けさせる (リジューム/resume) 機能のことです。ユーザは通常、システムの端末
       ドライバと bash を組み合わせて使い、対話的インタフェースを通してこの機能を利用します。

 これを見た時・・・

 意味がよくわからへんねん (TT)

 そこで、色々調べていく事にした。
 するとプロセスを停止状態(サスペンド)にする方法が書いてあった。

 CTRLキーとZキーを同時に押せば良いのだ!

 そこで以下の実験を行ってみた。

sleepコマンドが稼働している間に停止させてみる
[suga@linux]$ sleep 20 

[1]+  Stopped                 sleep 20
[suga@linux]$ 
単純な例として、sleepコマンドの振る舞いが思いついた。
おねんねさせている時に、CTRLとZキーを押せば
実行中(おねんね中)のプロセスを停止させる事ができる。

赤い部分でsleepコマンドが停止していると表示されている。

[1]で囲まれている部分だが、中の数値はジョブ番号だ。
この場合、ジョブ番号は「1」になる。
ジョブ番号は、プロセスIDとは違う概念の物だ。

 この時、sleepコマンドの状態が、どうなっているのか
psコマンドで見てみる事にした。

psコマンドでsleepプロセスの様子を見てみる
[suga@linux]$ sleep 20

[1]+  Stopped                 sleep 20
[suga@linux]$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   2060   536 ?        Ss   Feb12   0:00 init [5]                             
root         2  0.0  0.0      0     0 ?        S<   Feb12   0:00 [migration/0]
root         3  0.0  0.0      0     0 ?        SN   Feb12   0:00 [ksoftirqd/0]

(途中、省略)

suga     10510  0.1  0.1   5604  1472 pts/5    Ss   09:22   0:00 bash
suga     10534  0.0  0.0   4644   476 pts/5    T    09:22   0:00 sleep 20
suga     10535  0.0  0.0   5188   936 pts/5    R+   09:22   0:00 ps aux
[suga@linux]$ 
青い部分がsleepプロセスの状態だ。
sleepのプロセスのSTAT値が「T」になっている。
これは停止中という意味だ。

 フォアグランドで動かしているプロセスに対して
CTRL+Zキーを押すと、実際に、プロセスが停止するのがわかった。


 いかにもpsコマンドを使いこなしている書き方をしているのだが

 突貫工事で調べたのらー (^^)

 見栄を張っても、メッキなんぞ、簡単に剥がれるので
最初に自分から剥していれば楽なのだ (^^)

psコマンドで出力されるSTAT値について
S スリープ中。待ち受け状態の事
R 実行可能
W スワップアウト
D 割り込み不可のスリープ中
T 停止中、もしくはトレース中
Z ゾンビ
N Nice値を持っている
+ 手動で動かしているプロセス。
フォワグランドで動かしている物だと思う。
STAT値について
BSD形式(CentOSでは表示される)
< 優先度の高いプロセス
N 優先度の低いプロセス
s セッションリーダー
l マルチスレッド化されている
L 実メモリのページをロックして使用している

 STAT値を知って、よりプロセスの振る舞いがわかるようになった (^^)

 さて、話はジョブ制御に戻ります。
 CTRL+Zキーで停止させたジョブを再開させるには
どうすれば良いのか?

sleepコマンドが稼働している間に停止させてみる
[suga@linux]$ sleep 20 

[1]+  Stopped                 sleep 20
[suga@linux]$ 
何かの事情で一時的に、おねんね(ジョブ停止)させる事はあっても
ずーと、おねんねさせるわけにはいかない。

 man bashでは、ジョブの再開の方法が2つ書いてあった。

ジョブ再開のコマンド
fg %n
「n」はジョブ番号の値だ。
fgは「foreground」の略で、ジョブ番号「n」をプロセスを
フォアグランドで目覚めさせるコマンドだ。
bg %n
「n」はジョブ番号の値だ。
bgは「background」の略で、ジョブ番号「n」をプロセスを
バックグランドで目覚めさせるコマンドだ。

 さて、実際に自分の目で確かめる事にした。

フォアグランドで目覚めさせる
[suga@linux]$ sleep 20

[1]+  Stopped                 sleep 20
[suga@linux]$ fg %1
sleep 20

一度、おねんね(停止)させたプロセスを、fgコマンドで
目覚めさせてみた。フォアグランドで動いている事がわかる。
バックグランドで目覚めさせる
[suga@linux]$ sleep 20

[1]+  Stopped                 sleep 20
[suga@linux]$ bg %1
[1]+ sleep 20 &
[suga@linux]$ 
一度、おねんね(停止)させたプロセスを、bgコマンドで
目覚めさせてみた。バックグランドで動いている事がわかる。
青い部分で、ご丁寧に「&」をつけて、わかりやすく表示している。

 ジョブを停止させたり、ジョブを復活させる事を

 ジョブ制御なのらー!!

 
 ようやくジョブ制御が何なのかがわかった。

 ここで、ようやくsetコマンドの「-m」オプションの話に戻る。
 さて、もし、「-m」オプションを解除した状態だと
ジョブの制御が無効になるので、ジョブ制御ができなくなるのでは。

 そこで「-m」オプションの反対の作用の働きをする
ジョブ制御を無効にするオプションを調べてみた。

 マイナスの反対で「+m」にすれば良いのだ

 あっさり書いているようだが、正直な事を書きますと、
かなりの時間を費して調べた事を書いておきます (^^;;

ctrlとzキーを連打するのだが・・・
[suga@linux]$ set +m
[suga@linux]$ sleep 20

赤い部分でジョブ制御を無効にしてみた。
そしてsleepコマンドを実行した後、CTRLとZキーを押しているが、
全然、sleepプロセスが中断される様子はない。

 この瞬間・・・

 ジョブ制御の意味がハッキリわかった!!

 だった。
 やはり自分の目で確かめないと、物事がハッキリ見えないのだ。

 setコマンドで「+m」オプションを使って、ジョブ制御を無効にしてしまうと
色々、不便な事が出てきそうに思えた。
 そのため、明示的にジョブ制御を有効にしているのだと思った。


 それを確かめるべく、rc.sysinitスクリプトを以下のように書き換えてみた。
 敢えて、明示的にジョブ制御を無効にしてみたのだ。

明示的にジョブ制御を無効にしてみた
#!/bin/bash
#
# /etc/rc.d/rc.sysinit - run once at boot time
#
# Taken in part from Miquel van Smoorenburg's bcheckrc.
#

HOSTNAME=`/bin/hostname`
HOSTTYPE=`uname -m`
unamer=`uname -r`

set +m

(以下、省略)
青い部分を書き換えた。
ジョブ制御を明示的に「無効」をしてみたのだった。

 再起動をかけてみるのだが・・・

 何も変化があらへん (^^;;

 表面的には変化がないだけなのかもしれないが
何ら障害もなさそうだ。

 では、なぜ、この処理があるのか。
 もしかしたら、安全を考えて、敢えて明示的にジョブ制御を
有効にしているのではないかと思ってみた。

 それにしても、たった1行のために、ここまで費すとは
恐るべしsetコマンドの「-m」オプション。


 次へ進む。  ネットワーク関係の設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -f /etc/sysconfig/network ]; then
    . /etc/sysconfig/network
fi
if [ -z "$HOSTNAME" -o "$HOSTNAME" = "(none)" ]; then
    HOSTNAME=localhost
fi
/etc/sysconfig/networkファイルが存在すれば、それを読み込む。

そして変数HOSTNAMEの中身が空の場合は「localhost」という値を
代入する
変数HOSTNAMEだが、先ほどのスクリプトで/bin/hostnameコマンドを
実行させてホスト名を取得している上、仮に取得できていない場合でも
/etc/sysconfig/networkファイルに記述されている。
それでもダメな場合は「localhost」という値が入るというのだ。
/etc/sysconfig/network の中身
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=xxxx.yyyy.co.jp

 ところでファイルの有無に関する条件文や文字列の条件文は
( [ 条件式 ] )の形になっている。
 この時の書式を知らないと、これから先は読めない事態になるので
整理の意味で以下のようにまとめました。

 まずはファイルの有無に関して

ファイルに関する条件文について
オプション 意味合い
-e ファイルが存在すれば真
-f ファイルが存在し、かつ通常ファイルなら真
-r ファイルが存在し、かつ読み込み可能なら真
-w ファイルが存在し、かつ書き込み可能なら真
-x ファイルが存在し、かつ実行可能なら真
他にもファイルの属性(特殊ファイルとかソケット等)によって
条件づけを行う物もあるが、多いので省略します。

 変数に関しての条件文について、軽く触れたいと思います。

変数に関する条件文について
オプション意味合い
-n 文字列の中身がゼロでない場合なら真
-z 文字列の中身がゼロの場合なら真

 これを頭に入れれば大丈夫なのだ (^^)


 次を見ていく。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ ! -e /proc/mounts ]; then
        mount -n -t proc /proc /proc
        mount -n -t sysfs /sys /sys >/dev/null 2>&1
fi
もし、/proc/mountsのファイルが存在しない場合は
/proc と /sys をマウントする

 ところで

 /sysディレクトリって何やねん?

 /procディレクトリは知っていたが、/sysは知らなかった。

 そこで調べてみる事にして、2つのディレクトリの内容を
以下の表にしてみた。

/procディレクトリと/sysディレクトリについて
/proc
カーネル稼働中に、カーネルがメモリ上に保管している
システムの情報を見える形でファイル化した物を置いている
ディレクトリ。
ただし、このファイルは、あくまでもメモリ上の情報を
見える形にしているだけで、実体のないファイルだ。
/sys
カーネルが認識したデバイスの情報を
ファイルという形で格納したディレクトリ。
/proc同様、このディレクトリのファイルも
実体のないファイルなのだ。

/sysは2.6系から採用されている。

  2005年の時点では、こういう知識はなかったが、今はある。
 なので、rc.sysinitスクリプトの処理内容がわかり
読みやすくなる。

 さて、早速、/proc/mounts ファイルの中身を見てみる。

/proc/mounts の中身 (CentOS5.1の場合)
私が使っているマシンでは
rootfs / rootfs rw 0 0
/dev/root / ext3 rw,data=ordered 0 0
/dev /dev tmpfs rw 0 0
/proc /proc proc rw 0 0
/sys /sys sysfs rw 0 0
/proc/bus/usb /proc/bus/usb usbfs rw 0 0
devpts /dev/pts devpts rw 0 0
/dev/sda1 /boot ext2 rw 0 0
tmpfs /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/etc/auto.misc /misc autofs rw,fd=6,pgrp=1817,timeout=300,minproto=5,maxproto=5,indirect 0 0
-hosts /net autofs rw,fd=11,pgrp=1817,timeout=300,minproto=5,maxproto=5,indirect 0 0
カーネルが認識している(カーネルが使っているメモリ上に)
上のようなマウントの設定情報が入っている。

 Linuxのカーネル起動時に認識したファイルデバイスだ。
 rc.sysinitで、このファイルがない場合は、
/procや/sysが認識されていないため、Linuxの起動に支障が出るので
/procや/sysがマウントされるようだ。

 処理内容を見て合点がいく (^^)


 次に進む。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ ! -d /proc/bus/usb ]; then
        modprobe usbcore >/dev/null 2>&1 && mount -n -t usbfs /proc/bus/usb /proc/bus/usb
else
        mount -n -t usbfs /proc/bus/usb /proc/bus/usb
fi
/proc/bus/usb ディレクトリの存在(-d)がしていない場合
usbcoreモジュールを取り込み、 /proc/bus/usb をマウントする。

/proc/bus/usb ディレクトリが存在している場合は
単にマウントするだけだ。

 USBの認識の処理のようだ。
 デバイスの込み入った話になりそうなので

 私にはわかりませーん (^^)

 で逃げたいと思います。


 次に進む。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)

. /etc/init.d/functions

/etc/init.d/functionsを読み出している。
このシェルは、functionsという名前の通り、
Linuxの初期設定のシェル内で使う各種関数の定義を行っている。

ユーザー定義関数の定義を行っているという点で
RedHat7.3と同じだ。

もし、起動時のシェルで、不明なコマンドがあれば
このファイル内で定義している関数だと思って見た方が良いかも。


 次に進む。  なにやらSELinuxの設定に関する部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Check SELinux status
selinuxfs="$(fstab_decode_str `LC_ALL=C awk '/ selinuxfs / { print $2 }' /proc/mounts`)"
SELINUX_STATE=
if [ -n "$selinuxfs" ] && [ "`cat /proc/self/attr/current`" != "kernel" ]; then
        if [ -r "$selinuxfs/enforce" ] ; then
                SELINUX_STATE=`cat "$selinuxfs/enforce"`
        else
                # assume enforcing if you can't read it
                SELINUX_STATE=1
        fi
fi

if [ -n "$SELINUX_STATE" -a -x /sbin/restorecon ] && LC_ALL=C fgrep -q " /dev " /proc/mounts ; then
        /sbin/restorecon  -R /dev 2>/dev/null
fi

disable_selinux() {
        echo $"*** Warning -- SELinux is active"
        echo $"*** Disabling security enforcement for system recovery."
        echo $"*** Run 'setenforce 1' to reenable."
        echo "0" > "$selinuxfs/enforce"
}

relabel_selinux() {
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
        chvt 1
    fi
    # if /sbin/init is not labeled correctly this process is running in the
    # wrong context, so a reboot will be reuired after relabel

(途中、省略)

}
SELinuxに関する設定と、SELinuxの設定で使う関数の定義の部分。
私はSELinuxは使わない。難しくてわからないもーん (^^;;
なので、どういう仕組みなっているのか、調べずに読み飛ばす事にした。

 ところで、私がSELinuxに取り組める日が来るのは、
いつの日だろうか?
 そのため、SELinuxを

 理解できるだけの頭脳が欲しいのだ (^^)

 なのだ。


 次に進む。  どうやら暗号に関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
key_is_random() {
    [ "$1" = "/dev/urandom" -o "$1" = "/dev/hw_random" \
        -o "$1" = "/dev/random" ]
}

# Because of a chicken/egg problem, init_crypto must be run twice.  /var may be
# encrypted but /var/lib/random-seed is needed to initialize swap.
init_crypto() {
    local have_random dst src key opt mode owner params makeswap skip arg opt
    local param value rc ret mke2fs mdir

(途中、省略)

    return $ret
}
暗号に関する部分なのだが、よくわからん。
random-seedを調べると、乱数に関する物らしい。

 でも、わからない。
 だからといって調べ出すと泥沼にハマりそうだ。
 なのでお得意の

 忍法「読み飛ばしの術」を使うのらー!!

 という事で省略します。

 次に進む。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -x /sbin/setsysfont -a -c /dev/tty1 ]; then
   /sbin/setsysfont < /dev/tty1 > /dev/tty1 2>/dev/null
fi
フォントの準備を行う部分だ。
コンソールにフォントを認識させる部分のようだ。


 次に進む。  起動開始における画面表示に関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Print a text banner.
echo -en $"\t\tWelcome to "
read -r redhat_release < /etc/redhat-release
if [[ "$redhat_release" =~ "Red Hat" ]]; then
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;31m"
 echo -en "Red Hat"
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
 PRODUCT=`sed "s/Red Hat \(.*\) release.*/\1/" /etc/redhat-release`
 echo " $PRODUCT"
elif [[ "$redhat_release" =~ "Fedora" ]]; then
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;34m"
 echo -en "Fedora"
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
 PRODUCT=`sed "s/Fedora \(.*\) release.*/\1/" /etc/redhat-release`
 echo " $PRODUCT"
elif [[ "$redhat_release" =~ "CentOS" ]]; then
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;36m"
 PRODUCT=`sed "s/CentOS \(.*\) release.*/\1/" /etc/redhat-release`
 echo -e " $PRODUCT"
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;39m"
else
 PRODUCT=`sed "s/ release.*//g" /etc/redhat-release`
 echo "$PRODUCT"
fi
if [ "$PROMPT" != "no" ]; then
 echo -en $"\t\tPress 'I' to enter interactive startup."
 echo
fi
赤い部分はシェルの「read」コマンドを使っている。
これは入力文字列を読み込むコマンドで、
ここでの処理内容は、/etc/redhat_releaseファイルの中身を読み込んで
redhat_release変数に代入しているのだ

そして青い部分は「CentOS」の場合に動く箇所だ。

 上の部分で/etc/redhat_releaseファイルの中身の文字列で
「CentOS」が入っていたら、それ用の処理をしているのだ。
 実際に、/etc/redhat_releaseファイルの中身を見てみた。

/etc/redhat-release の中身(CentOS5.1の場合)
[suga@linux]$ more /etc/redhat-release 
CentOS release 5 (Final)
[suga@linux]$ 
「CentOS」という文字列のが入っている。

 青い部分の「echo」の所を見てみた。
 「echo -en "\\033[0;36m"」はどんな表示だろうか。
 早速、見てみる事にした。

echo -en "\\033[0;36m"
[suga@linux]$ echo -en "\\033[0;36m"
[suga@linux]$ 
文字列が「水色」になった。
元の黒字に戻すには「echo -en "\\033[0;39m"」を
実行すれば良い

 さて、実際の起動画面では、どういう風に表示されるのか
図にしてみた。

CentOS5.1の起動中の様子
ピンクで囲んだ部分が表示されている部分。
水色の表示がキチンと出ている。

  ところで、$BOOTUPの変数は、どこで宣言しているのか。
 もう一度、$BOOTUP変数の出てくる場所を見てみた。

$BOOTUP変数が出てくる場所
# Print a text banner.
echo -en $"\t\tWelcome to "

(途中、省略)

elif [[ "$redhat_release" =~ "CentOS" ]]; then
 [ "$BOOTUP" = "color" ] && echo -en "\\033[0;36m"
 PRODUCT=`sed "s/CentOS \(.*\) release.*/\1/" /etc/redhat-release`
 echo -e " $PRODUCT"

(途中、省略)

 echo
fi
$BOOTUP変数の値が「color」かどうかの条件文だ。
もし「color」という値であり、しかも「echo」の結果が真なら
先ほどの起動画面で水色の文字列が出てくる仕掛けなのだ。

  それに、どこで$BOOTUP変数の「color」という値を入れているのか。
  rc.sysinitのファイル内で代入されている箇所が
全く見つからないだけに・・・

  一体、どこにあんねん!

  と思った。
  そこで、/etc/rc.d/init.d/functionsに注目してみたら
該当する部分があった。

/etc/init.d/functions の中身(CentOS5.1の場合)
# Read in our configuration
if [ -z "${BOOTUP:-}" ]; then
  if [ -f /etc/sysconfig/init ]; then
      . /etc/sysconfig/init
  else
    # This all seem confusing? Look in /etc/sysconfig/init,
    # or in /usr/doc/initscripts-*/sysconfig.txt
    BOOTUP=color
    RES_COL=60
    MOVE_TO_COL="echo -en \\033[${RES_COL}G"
    SETCOLOR_SUCCESS="echo -en \\033[1;32m"
    SETCOLOR_FAILURE="echo -en \\033[1;31m"
    SETCOLOR_WARNING="echo -en \\033[1;33m"
    SETCOLOR_NORMAL="echo -en \\033[0;39m"
    LOGLEVEL=1
  fi
  if [ "$CONSOLETYPE" = "serial" ]; then
      BOOTUP=serial
      MOVE_TO_COL=
      SETCOLOR_SUCCESS=
      SETCOLOR_FAILURE=
      SETCOLOR_WARNING=
      SETCOLOR_NORMAL=
  fi
fi

if [ "${BOOTUP:-}" != "verbose" ]; then
   INITLOG_ARGS="-q"
else
   INITLOG_ARGS=
fi
$BOOTUPの変数の中身がない場合は
/etc/sysconfig/initファイルを読み込む。
大抵の場合、$BOOTUPに値が入っていないので
/etc/sysconfig/initファイルを読み込む。

$BOOTUP変数の値が空でない場合は、
このスクリプトの部分で値を代入している。

 大抵の場合、/etc/sysconfig/initファイルを読み込むため
中身を見てみる事にした。

/etc/sysconfig/init の中身(CentOS5.1の場合)
# color => new RH6.0 bootup
# verbose => old-style bootup
# anything else => new style bootup without ANSI colors or positioning
BOOTUP=color
# Turn on graphical boot
GRAPHICAL=yes
# column to start "[  OK  ]" label in
RES_COL=60
# terminal sequence to move to that column. You could change this
# to something like "tput hpa ${RES_COL}" if your terminal supports it
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
# terminal sequence to set color to a 'success' color (currently: green)
SETCOLOR_SUCCESS="echo -en \\033[0;32m"
# terminal sequence to set color to a 'failure' color (currently: red)
SETCOLOR_FAILURE="echo -en \\033[0;31m"
# terminal sequence to set color to a 'warning' color (currently: yellow)
SETCOLOR_WARNING="echo -en \\033[0;33m"
# terminal sequence to reset to the default color.
SETCOLOR_NORMAL="echo -en \\033[0;39m"
# default kernel loglevel on boot (syslog will reset this)
LOGLEVEL=3
# Set to anything other than 'no' to allow hotkey interactive startup...
PROMPT=yes
# Set to 'yes' to allow probing for devices with swap signatures
AUTOSWAP=no
赤い部分が変数BOOTUPの値を代入している所だ。
他にも横幅の指定やログレベルを「3」にする設定をしている。

初期化の設定やデーモンなどの起動の際に表示される
「成功」や「失敗」の表示の色の指定も、ここで行っている。

 単にBOOTUP変数の値を代入している部分だけでなく
起動時の表示の色指定の記述箇所までわかった。

 次に進む。  時間に関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Set the system clock.
update_boot_stage RCclock
ARC=0
SRM=0
UTC=0

if [ -f /etc/sysconfig/clock ]; then
   . /etc/sysconfig/clock

   # convert old style clock config to new values
   if [ "${CLOCKMODE}" = "GMT" ]; then
      UTC=true
   elif [ "${CLOCKMODE}" = "ARC" ]; then
      ARC=true
   fi
fi

CLOCKDEF=""
CLOCKFLAGS="$CLOCKFLAGS --hctosys"

case "$UTC" in
    yes|true)   CLOCKFLAGS="$CLOCKFLAGS --utc"
                CLOCKDEF="$CLOCKDEF (utc)" ;;
    no|false)   CLOCKFLAGS="$CLOCKFLAGS --localtime"
                CLOCKDEF="$CLOCKDEF (localtime)" ;;
esac
case "$ARC" in
    yes|true)   CLOCKFLAGS="$CLOCKFLAGS --arc"
                CLOCKDEF="$CLOCKDEF (arc)" ;;
esac
case "$SRM" in
    yes|true)   CLOCKFLAGS="$CLOCKFLAGS --srm"
                CLOCKDEF="$CLOCKDEF (srm)" ;;
esac

[ -x /sbin/hwclock ] && /sbin/hwclock $CLOCKFLAGS

action $"Setting clock $CLOCKDEF: `date`" /bin/true
「update_boot_stage」は
/etc/rc.d/init.d/functions で定義されている関数。
(この関数については後述しています)

ARCとSRMは、DECのAlphaのマシンの環境設定に使われる値。
私は、Alphaは使わないので、説明したくても全くできないため、
説明を省略します。

UTCは私の環境では「真」の「TRUE」の値になっている。
/etc/sysconfig/clock の中身(私の環境では)
# The ZONE parameter is only evaluated by system-config-date.
# The timezone of the system is defined by the contents of /etc/localtime.
ZONE="Asia/Tokyo"
UTC=true
ARC=false
時間の設定で、どの地域の時間なのかの設定値が格納されている。
ARCはDECのAlphaのみに使う変数なので、falseが正しい。

 UTCの値だが、インストール時に設定できる。

CentOS5.1のインストール時のUTCの設定
UTCをシステムクロックに使用する部分に印をつけると
システム時計がUTCに設定される。

 ここまで書くとUTCが何であるのか、わかったように思われるが
実際の所は・・・

 わかんないもーん (^^)

 なのだ。
 つまらぬ見栄を張って知ったかぶりをして大恥かくよりも
最初から「知らない」と言えば気楽で良いのだ!! (^^)


 今まではUTCは世界標準時のグリニッジ時刻だと思っていた。
 でも、思い込むは誤解を招く危険があるので、この際だと思い
UTCについて調べてみる事にする。

 厳密にはグリニッジと定義が違うのらー!!

GMT(グリニッジ)とUTCを比較
GMT
東経0度に位置するイギリスのグリニッジ天文台での
観測データを元に作成された時刻。
以前は、この時刻を世界標準時として使われていた。
でも、この場合だと地球の自転や公転誤差の影響が出てしまう。
UTC
現在の世界標準時として使われている。
正確な原子時計で刻まれる時刻を基準にしている。
時々、GMTとの誤差を修正するため、閏秒が設けられる。

 GMTは天文観測の結果から割り出された時刻で
UTCは原子時計から割り出された時刻の違いがある。


 調べた結果だが、多分、これで間違いはないと思う (^^;;
 と思ったのだが、Sambaの太田さんからご指摘があった。

太田さんからのご指摘 (2009/2/24)
UTCとGMTのくだりは、ちょっと違います。

http://jjy.nict.go.jp/mission/page1.html とか、Wikipediaの
GMTのあたりを見ると良いかも。

ちなみに、IT部門で一番やっかいなのはうるう秒ですね。
今年は閏秒が存在したので、1/1にNICTに見に行こうか、と
思ってました。根性なくて行きませんでしたけど。

ちなみに、UTCで動かすのは、そのシステムを使う用途次第で
あって、大昔、メインフレームをUTCで動かしたことがあります。

 うーん、頭の中をキチンと整理しないとダメだと思った。


 さて、日本の場合、UTCから9時間進んでいる時刻(JST)になる。
 これは兵庫県明石市の子午線135度の時刻なのだ。
 (私の自宅から電車で30分ぐらい)

 コンピューター社会が進んでいる現在、IT関係者が
GTMとUTCを混同すると具合が悪いようだが、
日常生活に於いてGMTとUTCとの違いを意識する必要は
全くないのだ (^^)


 ところでLinuxのインストールの際に
ハードウェア時刻をUTCにするか、ローカルタイム(日本の場合、JST)
にするかの選択が出てくる。

CentOS5.1のインストール時のUTCの設定
ここでハードウェアの時刻をUTCにするのか
それとも地域の時刻(日本だとJST)にするかを選択できる。

 ふと思う。

 なんで、こんな設定がいるねん!

 なので調べてみると意外な事がわかった!

 Linuxカーネルのシステム時刻はUTCなのらー!!

 こんな事は夢にも思っていなかった。
 なので、Linuxカーネルのシステム時刻と、ハードウェア時刻の
歩調をあわせるために、ハードウェア時刻をUTCに設定するのが
一般的のようだ。


 スクリプトを、もう1度見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Set the system clock.
update_boot_stage RCclock
ARC=0
SRM=0
UTC=0

if [ -f /etc/sysconfig/clock ]; then
   . /etc/sysconfig/clock

   # convert old style clock config to new values
   if [ "${CLOCKMODE}" = "GMT" ]; then
      UTC=true
   elif [ "${CLOCKMODE}" = "ARC" ]; then
      ARC=true
   fi
fi

CLOCKDEF=""
CLOCKFLAGS="$CLOCKFLAGS --hctosys"

case "$UTC" in
    yes|true)   CLOCKFLAGS="$CLOCKFLAGS --utc"
                CLOCKDEF="$CLOCKDEF (utc)" ;;
    no|false)   CLOCKFLAGS="$CLOCKFLAGS --localtime"
                CLOCKDEF="$CLOCKDEF (localtime)" ;;
esac
case "$ARC" in
    yes|true)   CLOCKFLAGS="$CLOCKFLAGS --arc"
                CLOCKDEF="$CLOCKDEF (arc)" ;;
esac
case "$SRM" in
    yes|true)   CLOCKFLAGS="$CLOCKFLAGS --srm"
                CLOCKDEF="$CLOCKDEF (srm)" ;;
esac

[ -x /sbin/hwclock ] && /sbin/hwclock $CLOCKFLAGS

action $"Setting clock $CLOCKDEF: `date`" /bin/true
赤色の部分は、UTC変数が「true」や「yes」の場合、
ハードウェア時刻をUTCにするための処理。

青い部分は、UTC変数が「false」や「no」の場合、
ハードウェア時刻を地域時刻にするための処理。

ピンクの部分は、hwclockコマンドを使って、ハードウェア時刻を
UTCもしくは、地域時刻に設定する部分だ。
それとハードウェア時刻をシステム時刻に反映させる処理も含まれている。

 hwclockのコマンドが出てきた。
 以前から、時々、見かけたコマンドなのだが

 具体的な働きはわからへん!!

 調べてみると、ハードウェア時刻の読み取りと設定に関する
コマンドだという。

 hwclockコマンドのオプションを見てみた。

hwclockコマンドのオプションについて
--hctosys
ハードウェア時刻をシステム時刻に反映させるためのオプション。
hctosysは「hardware clock to system clock」の略だと思う。
--systohc
ハードウェア時刻をシステム時刻に合わせるためのオプション。
systohcは「system clock to hardware clock」の略だと思う。
--utc ハードウェア時刻をUTCに設定する
--localtime ハードウェア時刻を地域時刻にする

 なので、Linuxの起動時に、ハードウェア時刻をUTCにした上で
ハードウェア時刻をシステム時刻に反映させたい場合は
hwclockコマンドのオプションは「--utc --hctosys」になる。

 ところで疑問が生まれる。

 なんで地域時刻とUTCの2種類の設定があるねん!

 UTCだけにすれば、すっきりする。
 でも、そういうわけいもいかないようだ。
 なぜなら・・・

 Windowsのシステム時刻が地域時刻なのだ!

 悪の権化のマイクロソフトと書きたいが、世界標準規格がどうなのか
わからないので、下手にMSの悪口は書けない (^^;;

 伝統的に、Windowsのハードウェア時刻は地域時刻で
UNIX系統はUTCを採用しているようだ。

 Linuxはハードウェア時刻をUTCでも地域時刻でも対応できる。

どっちでも採用できるLinux
hwclockコマンドを使う際、オプションに「--utc」を使えば
システム時刻をUTCに設定できる。
オプションに「--localtime」を使えば、地域時刻に設定できる。

 どっちでも採用できるため、以下の事ができる。

ハードウェア時刻をシステム時刻に反映させる場合
Linuxの場合、システム時刻はUTCを採用している。
そのため、ハードウェア時刻がUTCの場合、そのままシステム時刻に
反映させる仕掛けになっている。

もし、ハードウェア時刻が地域時刻の場合、途中でUTCに変換してから
システム時刻に反映しているのだ。

ハードウェア時刻をシステム時刻に反映させるためのオプションは
「--hctosys」を使う。

 だが、Windowsの場合、システム時刻が地域時刻になっている。
 そのためハードウェア時刻も地域時刻にしておく必要があるようだ。

Windowsの場合、システム時刻が地域時刻
ハードウェア時刻と、システム時刻を共にUTCにする必要があるようだ。
変換機能がないみたいだ。

 そのため、同じパソコン内で、LinuxとWindowsを共存させる
デゥアルブートの場合、ハードウェア時刻がUTCの場合だと
Windowsの時刻に不具合が生じる。
 そこで以下の事で回避している。

デゥアルブートの場合の回避法
Linuxはハードウェア時刻がUTCでも地域時刻でも対応できるため
Windowsに合わせて、ハードウェア時刻を地域時刻にしておけば
時刻の問題が回避できる。

 今までデゥアルブートをした事があるし、自宅でもやっていたが

 こんな話は知らへんかった!!

 勉強になった。
 それにしても、忍法「面倒なので飛ばしてしまえ術」で
この部分を飛ばそうと考えたのだが、調べておいて良かったと思う (^^)

 次に進む。  カーネルオプションの取得の部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Only read this once.
cmdline=$(cat /proc/cmdline)
/proc/cmdlineファイルの中身を
変数cmdlineの値として代入している。

 さて、/proc/cmdlineのファイルの中身を見てみる。

/proc/cmdlineのファイルの中身
[suga@linux]$ cat /proc/cmdline 
ro root=LABEL=/ rhgb quiet
[suga@linux]$ 
このファイルの中身は、まさにカーネルオプションだ。
このオプションは、LILOやGRUBのブートローダの設定で指定される。
GRUBの場合は、menu.lstの中で設定されている。

 カーネルオプションとなればGRUBの話で取り上げた。
 ある程度、知っている話なのでとっつきやすいのだ (^^)

 実際に、menu.lstの中身を見てみる。

GRUBのmenu.lstファイル(私の環境)
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /boot/, eg.
#          root (hd0,0)
#          kernel /vmlinuz-version ro root=/dev/sda2
#          initrd /initrd-version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-53.el5)
        root (hd0,0)
        kernel /vmlinuz-2.6.18-53.el5 ro root=LABEL=/ rhgb quiet
        initrd /initrd-2.6.18-53.el5.img
赤い部分がカーネルオプションになる。
この内容が「/proc/cmdline」ファイルの中身になる。

GRUBの部分でカーネルオプションについては
「システム奮闘記:その71」(ブートローダー(GRUB)の話)で
軽く触れたのだが、ここでもカーネルオプションの話が出てきた。

カーネルオプションの処理は、カーネル内部で処理されると思ったが
取り出してから、スクリプト上で処理される事を初めて知った。
そのためディストリビューションによって独自のオプションが
使える事がわかった。

CentOS5.1でのカーネルオプションの初期値はmenu.lstのファイルに
記述されている通り「ro root=LABEL=/ rhgb quiet」になる。


次に進む。  ハードウェアの初期化の処理の部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Initialize hardware
if [ -f /proc/sys/kernel/modprobe ]; then
   if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then
       sysctl -w kernel.modprobe="/sbin/modprobe" >/dev/null 2>&1
   else
       # We used to set this to NULL, but that causes 'failed to exec' messages"
       sysctl -w kernel.modprobe="/bin/true" >/dev/null 2>&1
   fi
fi

touch /dev/.in_sysinit >/dev/null 2>&1
赤い部分は「strstr」というユーザー定義関数を使っている。
青い部分にも知らないコマンドがある。

一体、どういう処理なのだろうか

 そこで処理の内容を見ていく事にした。
 まずは「strstr」というユーザー定義関数だ。

strstr関数の中身
# returns OK if $1 contains $2
strstr() {
  [ "${1#*$2*}" = "$1" ] && return 1
  return 0
}
/etc/rc.d/init.d/functionの中で定義された
ユーザー定義関数の1つだ。

処理内容だが、第1引数の文字列の中に、
第2引数の文字列が含まれていれば「1」(真)を返り値にし
そうでない場合は「0」(偽)を返り値にする。

「strstr "This is a pen" "is"」の場合
第1引数の文字列の中に第2引数の文字列「is」が含まれているので
「1」を返す形となる。

 関数の処理の内容がわかったので、もう1度、スクリプトを見てみる

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Initialize hardware
if [ -f /proc/sys/kernel/modprobe ]; then
   if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then
赤い部分は$cmdlineの変数の値(カーネルオプション)の
文字列の中に「nomodules」という文字列が含まれていたら
返り値が「1」になる。

でも、関数の手前に「!」の印がある事から、
赤い部分の結果を反転させた物が条件文の「真」になる。
ここの条件文では$cmdlineの変数の値の文字列の中に
「nomodules」が一切含まれない場合が「真」になる。

 同じ部分を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Initialize hardware
if [ -f /proc/sys/kernel/modprobe ]; then
   if ! strstr "$cmdline" nomodules && [ -f /proc/modules ] ; then
青い部分は「/proc/modules」のファイルの有無の判定で
ファイルが存在すれば「真」になる。

 ところで「/proc/modules」のファイルの中身は
どんな物か見てみる事にした。

/proc/modules (私の環境の場合)
[suga@linux]# more /proc/modules
vfat 15809 0 - Live 0xd6a7e000
fat 51165 1 vfat, Live 0xd6aa6000
usb_storage 76577 0 - Live 0xd6a92000
autofs4 24389 2 - Live 0xd69be000

(途中、省略)

ext3 123337 2 - Live 0xd68a5000
jbd 56553 1 ext3, Live 0xd6871000
ehci_hcd 32973 0 - Live 0xd6854000
ohci_hcd 23261 0 - Live 0xd6842000
uhci_hcd 25421 0 - Live 0xd683a000
[suga@linux]# 
カーネルが読み込んだモジュールの情報のようだ。
それにしてもモジュール情報の有無が必要なのか
この時点では、よくわからない。

 カーネルオプションで「nomodules」という文字列が含まていない上
/proc/modulesファイルが存在している場合、「真」になる条件文
 条件文を満たした時、どんな処理が行われるか見てみる事にした。
 
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
    sysctl -w kernel.modprobe="/sbin/modprobe" >/dev/null 2>&1
else
    # We used to set this to NULL, but that causes 'failed to exec' messages"
    sysctl -w kernel.modprobe="/bin/true" >/dev/null 2>&1
青い部分が条件文を満たした時、即ち、カーネルオプションに
「nomodules」の文字列が含まれていない場合のの処理内容だ。

「nomodules」という文字列から、この場合は、
モジュールを取り込まない場合だと連想できる。

 上の処理内容を見て思った。

  sysctlコマンドって何やねん!

 そう、知らないのだ。
 知らないから、どんな働きをしているのか、わからない。
 そこで調べてみる事にした。

 するとカーネルパラメータの設定に関するコマンドだという。
 sysctlコマンドに「-w」のオプションを付けると
カーネルの設定に関する部分だ。

 もう一度、処理内容を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
    sysctl -w kernel.modprobe="/sbin/modprobe" >/dev/null 2>&1
else
    # We used to set this to NULL, but that causes 'failed to exec' messages"
    sysctl -w kernel.modprobe="/bin/true" >/dev/null 2>&1
青い部分が条件文を満たした時の処理内容だ。
つまりモジュールを取り込む場合はカーネルパラメータの
1つである「kernel.modprobe」の値を
「/sbin/modprobe」に設定する。

赤い部分はモジュールを取り込まない場合
カーネルパラメータの1つである「kernel.modprobe」の値を
「/sbin/true」に設定する。


 次に進む。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Set default affinity
if [ -x /bin/taskset ]; then
   if strstr "$cmdline" default_affinity= ; then
     for arg in $cmdline ; do
         if [ "${arg##default_affinity=}" != "${arg}" ]; then
             /bin/taskset -p ${arg##default_affinity=} 1
         fi
     done
   fi
fi
赤い部分の「taskset」のコマンドに注目する。
manで何のコマンドかを見てみる。
「taskset - retrieve or set a processes’s CPU affinity」
の文字を見て、目が点なった。

意味がわからへん!!

それに何のコマンドかすら想像できない。

  そこで「affinity」の綴りを調べてみる。
 すると「婚姻、類似、好み」の意味があった。
 だけど・・・

 それでも、わからへん (TT)

  一体、どういう意味やねんと思い、ネットで調べてみると
「CPU の親和度」の話だそうだ。
 Manpage of SCHED_SETAFFINITY

  どうやら、マルチプロセッサ・システムの場合の話で、
CPUが1個の場合は関係ないようだ。
  なので、ここも飛ばす。だって・・・

 わからへんし、使わへんもーん (^^)


 次に進む。  RedHat系独特の処理の部分だ。 
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
nashpid=$(pidof nash 2>/dev/null)
[ -n "$nashpid" ] && kill $nashpid >/dev/null 2>&1
unset nashpid

 赤い部分のpidofコマンドが重要なのだが

 一体、どういうコマンドやねん!!

 なのだ。
 そこで、manコマンドでどういう物か見てみた。

manの内容 (CentOS5.1の場合)
PIDOF(1)                         User Commands                        PIDOF(1)

名前
       pidof - 名前でプロセスを見つけ、それらの PID を一覧表示する

書式
       pidof [-eg] name ...
       pidof -V

説明
       pidof  は、指定されたいずれかのコマンドを実行しているプロセス全ての PID
       を一覧表示する。
「こんなコマンドがあったとは」と思った。
わざわざpsコマンドを使って、プロセス名とプロセスIDを
照らし合わせる必要がなくなるからだ。

とはいえ、別に、このコマンドを使うような高度な事(?)を
やってはいないのだが (^^;;

 早速、pidofコマンドを使ってみる。

pidofコマンド
[suga@linux]$ ps aux | more
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1   2040   636 ?        Ss   09:23   0:00 init [5]  

(途中、省略)

suga      2992  0.4  3.7  19352 13120 pts/4    S    10:23   0:09 emacs setfile
suga      8358  0.0  0.2   5168   932 pts/3    R+   10:56   0:00 ps aux
suga      8359  0.0  0.1   4668   516 pts/3    R+   10:56   0:00 more
[suga@linux]$ 
[suga@linux]$ /sbin/pidof emacs
2992
[suga@linux]$
まずはpsコマンドで稼働中のプロセスを見てみる。
青い部分は、丁度、私が動かしているemacsのプロセスで
プロセスIDは「2292」だ。

実際に、pidofコマンドを使って、emacsのプロセスIDを
調べてみる。赤い部分はコマンド実行の部分で、
emacsのプロセスID「2992」を出力している。

 pidofコマンドが何なのか理解できた所で
処理内容を見ていく事にした。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
nashpid=$(pidof nash 2>/dev/null)
[ -n "$nashpid" ] && kill $nashpid >/dev/null 2>&1
unset nashpid
nashプロセスが走っていたら、そのプロセスID(PID)を拾い出し、
killコマンドでプロセスを殺す処理だ。

nashは、RedHat系独自のシェルだ。
BIOSの起動後、GRUB等のブートローダーが起動し
カーネルや、initrdを取り込んだ際、
メモリ上でinitrdを展開し、各ドライバなどを
モジュールとして取り込むのに使われるシェルなのだ。

他にもnashプロセスを起動させる部分があるかもしれないが
私が調べた範囲では見つからなかった。

多分、initrdを展開し、各ドライバをモジュールに取り込んだ後は
不要なプロセスなので、三途の川を渡ってもらうようにする。

nashプロセスが起動する話については「システム奮闘記:その72」の
「Linuxのinitrdファイルって何?」をご覧ください。


 次に進む。 udevに関する部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)

/sbin/start_udev
udevを開始するコマンドのようなのだ。
 ところで・・・  udevって一体、何やねん・・・  そこで次のサイトを見てみる事にした。  ソースコード・リテラシーのススメ:第12回 システム起動用のスクリプトを読む  udevの話を取り組んでいくと膨大になりそうだ。  そんな予感がした。なので、勇気を持って  撤退するのらー!!  あっさり決める私 (^^)  いずれは取り上げたいと思います。
 次に進む。  ユーザー定義モジュールの部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Load other user-defined modules
for file in /etc/sysconfig/modules/*.modules ; do
  [ -x $file ] && $file
done
ユーザー定義のモジュールの取り込みの部分だ。
この部分を見ると、モジュールを取り込むよりも
シェルファイルを呼び出して実行させる記述になっている。

 そこで私の環境(もちろん、CentOS5.1)で
/etc/sysconfig/modulesディレクトリの中を見てみる。

/etc/sysconfig/modulesディレクトリ
[suga@linux]# pwd
/etc/sysconfig/modules
[suga@linux]# ls
udev-stw.modules
[suga@linux]# 
赤い部分で示しているように
udev-stw.modulesファイルがあるのがわかる。

 さて、udev-stw.modulesファイルの中身を見てみる。

udev-stw.modulesファイルの中身
[suga@linux]# more udev-stw.modules
#!/bin/sh
for i in nvram floppy parport lp snd-powermac;do
        modprobe $i >/dev/null 2>&1
done
[suga@linux]# 
「nvram」「floppy」「parport」「lp」「snd-powermac」
の5つのモジュールを取り込む設定になっているようだ。

 要するに、モジュールを取り込むためのシェルを
/etc/sysconfig/modulesディレクトリに置き
そのディレクトリに置かれたシェルを呼び出す部分なのだ。

 でも、ユーザー定義モジュールを読み込む割には
フロッピーや、プリンタのモジュールはユーザー定義なのかと
思ってしまう部分はある。

 次に進む。  モジュールに関する部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Load modules (for backward compatibility with VARs)
if [ -f /etc/rc.modules ]; then
        /etc/rc.modules
fi
ここもモジュールの読み込み関係だ。
/etc/rc.modulesファイルがあれば、
そのファイルを呼び出す内容だ。

 でも、私の環境には「/etc/rc.modules」ファイルが

 存在しないのだ!! (^^)

 なので、どんなファイルかもわからない。
 そのため、ここは軽く流しておく (^^)

 次に進む。  CentOS独自の起動時のGUI画面に関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Start the graphical boot, if necessary; /usr may not be mounted yet, so we
# may have to do this again after mounting
RHGB_STARTED=0
mount -n /dev/pts >/dev/null 2>&1
[ -n "$SELINUX_STATE" ] && restorecon /dev/pts >/dev/null 2>&1

if strstr "$cmdline" rhgb && ! strstr "$cmdline" early-login && [ "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then
   ( . /etc/sysconfig/i18n 2>/dev/null ; /usr/bin/rhgb )
   RHGB_STARTED=1
fi
赤い部分はカーネルオプションの中に文字列「rhgb」が
含まれている時、「真」となる。

青い部分はカーネルオプションの中に文字列「early-login」が
含まれていない場合、「真」となる。

ピンクは変数BOOTUPの値が「color」という文字列で
かつ、変数GRAPHICALの値が「yes」という文字列で
かつ、/usr/bin/rhgb という実行ファイルが存在する場合
真になるという部分だ。

赤、青、ピンクの3つの条件が満たされた時
/etc/sysconfig/i18nを読み込まれ
/usr/bin/rhgb という実行ファイルが実行される。
そして変数RHGB_STARTEDに値「1」が代入される。

変数RHGB_STARTEDに値「1」というのは
起動画面はGUIの状態で動くという目印なのだ。

 最初、この部分を見た時、何かを行う上での
準備のための処理だと思った。

 「RHGB」の意味を考えると以下の略だと思う。

 Red Hat Graphical Boot

 この後に続くスクリプトを見ていると、変数RHGB_STARTEDの値を
「1」にするのは、ブート処理をGUI画面で起動するかどうかの
目印を作るための処理だというのがわかった。

 次に進む。  カーネルパラメータの取り込み部分になる。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Configure kernel parameters
update_boot_stage RCkernelparam
sysctl -e -p /etc/sysctl.conf &/dev/null 2>&1
「update_boot_stage」は時間の設定の部分でも出てきた。
/etc/rc.d/init.d/functions で定義されている関数。

sysctlコマンドは、カーネルパラメーターを設定する。

  「update_boot_stage」コマンドが出てきた。

 一体、どんなコマンドやねん!

  というわけで、どんなコマンドか調べてみる事にしたのだが、
/sbin や /usr/sbin のディレクトリーにある実行ファイルでもない。

  検索で調べると、英語のサイトで発見。
  /etc/rc.d/init.d/functionsで定義されているのだ。

/etc/rc.d/init.d/functionsで
定義されているupdate_boot_stage関数 (CentOS5.1の場合)
# Inform the graphical boot of our current state
update_boot_stage() {
  if [ "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb-client ]; then
    /usr/bin/rhgb-client --update="$1"
  fi
  return 0
}
update_boot_stageの引数を rhgb-clientの引数にしている。

変数GRAPHICALの値が「yes」という文字列で
/usr/bin/rhgb-clientという実行ファイルが存在すれば

「/usr/bin/rhgb-client --update=XXX」が実行される。
「XXX」はupdate_boot_stageの引数の値

 ところで

 rhgb-clientって、どんなコマンドやねん!

 肝心のコマンドの役割がわからなければ
どんな処理が行われているなんて、理解できない。
 そこでコマンドを入力してみた。

rhgb-clientコマンドを実行してみる
[root@linux]# /usr/bin/rhgb-client 
Error: at least one argument must be specified
Usage: rhgb-client [OPTION...]
  -u, --update=STRING      Update a service's status
  -d, --details=STRING     Show the details page (yes/no).
  -p, --ping               See if the server is alive
  -q, --quit               Tells the server to quit
  -s, --sysinit            Inform the server that we've finished rc.sysinit

Help options:
  -?, --help               Show this help message
  --usage                  Display brief usage message
[root@linux]# 
オプションが必要なコマンドだ。
でも、これを見ても、イマイチ、よくわからん。

 うーん、よくわからん。なので、こんな時は

 適当に触ってみるのが良いのだ!

 という事で、update_boot_stageの関数の記述を
以下のように変更してみる。

update_boot_stage関数の記述を書き換えてみる
# Inform the graphical boot of our current state
update_boot_stage() {
  if [ "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb-client ]; then
#    /usr/bin/rhgb-client --update="$1"
     /usr/bin/rhgb-client --update="XXX"
  fi
  return 0
}
青い部分は元々の記述で「#」を使って無効にした。
赤い部分は書き換えた後。
単に「--update」のオプションの値を「XXX」に変えただけだ。

 早速、記述の変更前と変更後の起動の様子を比較した。

変更前変更後
変更前と変更後の違いは2ヶ所ある。
進行状況を知らせるグラフが表示されなくなる事と
処理内容が表示されなくなる事だ。

 でも、これだけだったら、「rhgb-client」コマンドが
イマイチ見えてこない。
 そこで「details=yes」というオプションに変更してみた。

update_boot_stage関数の記述を書き換えてみる
# Inform the graphical boot of our current state
update_boot_stage() {
  if [ "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb-client ]; then
#    /usr/bin/rhgb-client --update="$1"
     /usr/bin/rhgb-client --details=yes
  fi
  return 0
}
青い部分は元々の記述で「#」を使って無効にした。
赤い部分は書き換えた後。
「--update」のオプションから「--details=yes」に
変えたのだ。

 実行結果は以下の通りだ。

「--details=yes」というオプションに変更
起動の様子の詳細が表示されるようになる。
どうやら「--details=yes」は起動の様子の詳細を
表示させるためのオプションだ。


 次に「--quit」オプションにしてみる事にした。

update_boot_stage関数の記述を書き換えてみる
# Inform the graphical boot of our current state
update_boot_stage() {
  if [ "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb-client ]; then
#    /usr/bin/rhgb-client --update="$1"
     /usr/bin/rhgb-client --quit
  fi
  return 0
}
青い部分は元々の記述で「#」を使って無効にした。
赤い部分は書き換えた後。
「--update」のオプションから「--quit」に
変えたのだ。

 さて結果を見てみると・・・

  GUIの起動画面が落ちてしまう!!

「--quit」というオプションに変更
GUIの画面になるかと思いきや、いきなり画面が落ちて
テキストで起動する形になった。

「quit」は「終了」の意味だから、GUIで起動する画面を
終了させるという意味だというのが実験でわかった。

 やはり実験してみないと、わからない物だ。


 ちなみに「--ping」オプションは次の働きをする。

「--ping」というオプションの役目
GUIの画面が正常に起動しているのかを確認するための
オプションなのだ。

もし、テキストで起動している場合は、GUIとは違うので
「違う」と判断させる事ができる。

  これでrhgb-clientコマンドの使い方がわかった。


 さて、元のスクリプト(rc.sysinit)の部分に戻る。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Configure kernel parameters
update_boot_stage RCkernelparam
sysctl -e -p /etc/sysctl.conf &/dev/null 2>&1
青い部分は、先ほど書いた説明から
カーネルパラメータの設定の段階に入った事を
表示するための処理になる。

そして赤い部分はsysctlコマンドを使って、
カーネルパラメーターを設定する。
「-p」は指定したファイルから読み込むためのオプションで
「-e」は不明な物を指定した時は、無視するためのオプションだ。

  sysctlコマンドで、/etc/sysctl.confファイルに記述されている
カーネルパラメーターを設定する。

  そこで/etc/sysctl.confファイルを見てみる事にした。

/etc/sysctl.conf (CentOS5.1の場合)
# Kernel sysctl configuration file for Red Hat Linux
#
# For binary values, 0 is disabled, 1 is enabled.  See sysctl(8) and
# sysctl.conf(5) for more details.

# Controls IP packet forwarding
net.ipv4.ip_forward = 0

# Controls source route verification
net.ipv4.conf.default.rp_filter = 1

# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0

# Controls the System Request debugging functionality of the kernel
kernel.sysrq = 0

# Controls whether core dumps will append the PID to the core filename
# Useful for debugging multi-threaded applications
kernel.core_uses_pid = 1

# Controls the use of TCP syncookies
net.ipv4.tcp_syncookies = 1

# Controls the maximum size of a message, in bytes
kernel.msgmnb = 65536

# Controls the default maxmimum size of a mesage queue
kernel.msgmax = 65536

# Controls the maximum shared segment size, in bytes
kernel.shmmax = 4294967295

# Controls the maximum number of shared memory segments, in pages
kernel.shmall = 268435456
カーネルパラメーターの設定が行われる。
逆に言えば、ここを触れば、初期設定の値の変更が可能なのだ。

 ふと思い出した。
 以前、Linuxでiptablesを使ってNATを設定していた時、
パケットの転送を行う必要があった。

 当時はカーネルオプションなんて知らなかった。
 でも、本の丸写し(Webの丸写し)で、起動時に
パケット転送の設定を反映させるため
/etc/sysctl.confの記述を触っていた。

/etc/sysctl.confファイルの記述を変更していた
# Kernel sysctl configuration file for Red Hat Linux
#
# For binary values, 0 is disabled, 1 is enabled.  See sysctl(8) and
# sysctl.conf(5) for more details.

# Controls IP packet forwarding
net.ipv4.ip_forward = 1

(以下、省略)
赤い部分の値を「0」から「1」にする事で
パケットの転送を行う設定になる。

iptableの話については「システム奮闘記:その21」の
iptablesとNATの設定の入門をご覧ください

 以前、わからなかった事が、ここに来て知識がつながった。
 粘り強く学習する事の大事さを改めて感じた。


 次へ進む。  キーボード関係の設定の部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -x /bin/loadkeys ]; then
 KEYTABLE=
 KEYMAP=
 if [ -f /etc/sysconfig/console/default.kmap ]; then
  KEYMAP=/etc/sysconfig/console/default.kmap
 else
  if [ -f /etc/sysconfig/keyboard ]; then
    . /etc/sysconfig/keyboard
  fi
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then
     KEYMAP="$KEYTABLE.map"
  fi
 fi
 if [ -n "$KEYMAP" ]; then
  if [ -n "$KEYTABLE" ]; then
    echo -n $"Loading default keymap ($KEYTABLE): "
  else
    echo -n $"Loading default keymap: "
  fi
  LOADKEYS=loadkeys
  if [ "${LANG}" != "${LANG%%.UTF-8}" -o "${LANG}" != "${LANG%%.utf8}" ]; then
        LOADKEYS="loadkeys -u"
  fi
  $LOADKEYS $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \
     success $"Loading default keymap" || failure $"Loading default keymap"
  echo
 fi
fi
キーボードの設定だ。
/etc/sysconfig/console/default.kmap ファイルが存在しなければ
/etc/sysconfig/keyboard ファイルが取り込まれる。

一体、どういう設定なのだろうか。

 疑問があるだけでは前に進まない。
 そこで、/etc/sysconfig/keyboard ファイルの中身を見てみる。

/etc/sysconfig/keyboard (CentOS5.1の場合)
というより私の使っている環境では
KEYBOARDTYPE="pc"
KEYTABLE="jp106"
日本語のキーボードの設定になっている。
「jp106」は日本語キーボードだと知っていたのだが
なぜ「jp106」という名称なんだろうか?

 今まで日本語のキーボードの事を「jp106」と呼ぶのは
知っていたのだが、よく考えると・・・

 そもそもjp106って何やねん?

 そこで調べてみた。
 すると以下の規格だという。

jp106キーボードとは
PC/AT互換機のキーボードの規格の1つだという。
101キーボードを基に、日本語の仮名を刻印し
日本語向けのキーを5つ追加したものだという。

106キーボード(e-words)
101キーボードとは
PC/AT互換機向けに開発されたキーボード。
101個のキーがある事から101キーボードと名付けられたという。

jp106は101キーボードに5つキーを追加したため、
106個のキーがある日本語キーボードという事で
「jp106」と名付けらたのが想像できる。

でも、私が使っているキーボードの数を数えたら

109個あるやん!!

というので109キーボードがあるのかどうかを
調べてみると、これが主流のキーボードだという。
109キーボードとは
106キーボードにWindowsキーを2つ追加したキーボードだという。
キーボードにWindowsを表す旗印のキーがそれになる。

残り1つは何だろうか。
メニューキーもWindowsキーの1つのようだ。
そうだとすれば、109個のキーがあるというのが納得できる。

 いつも使っているキーボードのキーの数なんて
数えた事なんてなかったし、jp106の106が
キーの数に由来している事なんぞ知らなかったし
ましてや普段使っているキーボードのキーの数が
109個だというのも知らなかった。


 さて、もう1度、スクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -x /bin/loadkeys ]; then
 KEYTABLE=
 KEYMAP=
 if [ -f /etc/sysconfig/console/default.kmap ]; then
  KEYMAP=/etc/sysconfig/console/default.kmap
 else
  if [ -f /etc/sysconfig/keyboard ]; then
    . /etc/sysconfig/keyboard
  fi
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then
     KEYMAP="$KEYTABLE.map"
  fi
 fi
 if [ -n "$KEYMAP" ]; then
  if [ -n "$KEYTABLE" ]; then
    echo -n $"Loading default keymap ($KEYTABLE): "
  else
    echo -n $"Loading default keymap: "
  fi
  LOADKEYS=loadkeys
  if [ "${LANG}" != "${LANG%%.UTF-8}" -o "${LANG}" != "${LANG%%.utf8}" ]; then
        LOADKEYS="loadkeys -u"
  fi
  $LOADKEYS $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \
     success $"Loading default keymap" || failure $"Loading default keymap"
  echo
 fi
fi
赤い部分はloadkeysコマンドを使うための準備だ。
loadkeysコマンドは、コンソール用のカーネルキーマップを
取り込むための物だ。

青い部分は、LANG変数の中身についての条件文だ。
なにやら「UTF-8」、もしくは「utf8」の文字列に
関する条件文だというのが推測できる。

 ところで、LANG変数は、どこで宣言しているのか。
 ふと勘が働いた。

 /etc/sysconfigディレクトリにあるかも

 そこで、/etc/sysconfigディレクトリを見てみる事にした。

/etc/sysconfig/i18n の中身(CentOS5.1の場合)
[suga@linux]$ cat /etc/sysconfig/i18n 
LANG="ja_JP.UTF-8"
[suga@linux]$ 
i18n ファイルだと思って、中身を見てみると
案の定、言語や文字コードに関する設定ファイルだった。

このファイルで変数LANGに「UTF-8」を含んだ文字列の値が
代入される。

内容から「UTF-8で日本語の使用」を意味する
使用言語の指定ファイルだと思われる。

 さて、もう1度、スクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -x /bin/loadkeys ]; then
 KEYTABLE=
 KEYMAP=
 if [ -f /etc/sysconfig/console/default.kmap ]; then
  KEYMAP=/etc/sysconfig/console/default.kmap
 else
  if [ -f /etc/sysconfig/keyboard ]; then
    . /etc/sysconfig/keyboard
  fi
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then
     KEYMAP="$KEYTABLE.map"
  fi
 fi
 if [ -n "$KEYMAP" ]; then
  if [ -n "$KEYTABLE" ]; then
    echo -n $"Loading default keymap ($KEYTABLE): "
  else
    echo -n $"Loading default keymap: "
  fi
  LOADKEYS=loadkeys
  if [ "${LANG}" != "${LANG%%.UTF-8}" -o "${LANG}" != "${LANG%%.utf8}" ]; then
        LOADKEYS="loadkeys -u"
  fi
  $LOADKEYS $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \
     success $"Loading default keymap" || failure $"Loading default keymap"
  echo
 fi
fi
青い部分は、LANG変数に関する条件文だ。
「%%」の部分が、どういう物なのかがわかれば
条件文の意味が理解できるはず。

 だが話は簡単ではない。
 そもそも・・・

 「%%」って、どういう特殊文字やねん!

 なのだ。
 という事で、シェルの本やサイトを調べるが

 見つからへんやん (TT)

 だった。
 なので、実際に手で触ってから、どういう特殊文字なのかを
推測してみる事にした。
 そこで、実験用のシェルを書いてみる。

実験用シェル ( lang1.sh )
#!/bin/sh

LANG="ja_JP.UTF-8"

echo "${LANG}"
echo "${LANG%%.UTF-8}"
実行結果
[suga@linux]$ ./lang1.sh 
ja_JP.UTF-8
ja_JP
[suga@linux]$
赤い部分の出力結果を、青色で表してみた。
「.UTF-8」という文字列が削除された形になる。

 ただ、これだけだと「%%」の役目がわからない。
 なので、次のシェルで実験してみた。

実験用シェル2 ( lang2.sh )
#!/bin/sh

LANG="ja_JP.EUC"

echo "${LANG}"
echo "${LANG%%.UTF-8}"
実行結果
[suga@linux]$ ./lang2.sh 
ja_JP.EUC
ja_JP.EUC
[suga@linux]$
赤い部分の出力結果を、青色で表してみた。
何も処理されていないようだ。

 うーん、ますます「%%」がわからなくなってきた。

 「こりゃ、わからへん」で逃げようと思いつつ、
調べていくと「%%」の用法が見つかった。
 パラメータ展開の部分にあった。

  http://www.linux.or.jp/JM/html/GNU_bash/man1/bash.1.html

 man bashの内容のサイトなのだ。
 man bashでも見れるので、manで見てみる事にした。

man bash の該当部分を抜粋
パラメータの展開
    ‘$’ 文字があると、パラメータ展開、コマンド置換、算術式展開が行われます。展開されるパラメータ名やシンボル
     は、ブレースで括ることもできます。ブレースは省略可能ですが、変数の直後に変数名の一部と解釈できる文字が置
     かれた場合に、その文字と共に変数が展開されてしまうのを防ぐために用意されています。
     ブレースを使った場合、マッチングを終えるブレースは最初の ‘}’ です。ただしバックスラッシュでエスケープ さ
     れているものやクォートされている文字列中のものは含まれませんし、算術式展開やコマンド置換、パラメータ展開
     に入っているものも含まれません。

     ${parameter}
            parameter の値が置換されます。ブレースが必要になるのは、 parameter が 2 桁以上の数字を持つ位置 パ
            ラメータの場合や、 parameter の直後の文字を名前の一部として解釈させたくない場合です。


(途中、省略)

       ${parameter%word}
       ${parameter%%word}
              word が展開され、パス名展開の場合と同じようなパターンを作ります。このパターンが parameter を展 開
              し た値の末尾の部分とマッチする場合、展開結果は parameter を展開した値から最短一致パターン (‘‘%’’
              の場合) または最長一致パターン (‘‘%%’’ の場合) を取り除いたものになります。 parameter が @ または
              *  である場合、パターンを削除する操作は全ての位置パラメータに順番に適用され、展開結果はリストとし
              て得られます。 parameter が @ または * が添字になっている配列変数である場合、パターンを削除する操
              作は配列の全ての要素に順番に適用され、展開結果はリストとして得られます。
「%%」の役目は特殊文字ではなかった。
文字列処理のための記号だったのだ。
なので、いくら特殊文字で調べても見つからないのは
当然の事なのだ。

それにしても「%%」が文字列処理のための文字だったとは
予想がつかなかった。

 ようやく意味がわかった所で、スクリプトに戻る。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -x /bin/loadkeys ]; then
 KEYTABLE=
 KEYMAP=
 if [ -f /etc/sysconfig/console/default.kmap ]; then
  KEYMAP=/etc/sysconfig/console/default.kmap
 else
  if [ -f /etc/sysconfig/keyboard ]; then
    . /etc/sysconfig/keyboard
  fi
  if [ -n "$KEYTABLE" -a -d "/lib/kbd/keymaps" ]; then
     KEYMAP="$KEYTABLE.map"
  fi
 fi
 if [ -n "$KEYMAP" ]; then
  if [ -n "$KEYTABLE" ]; then
    echo -n $"Loading default keymap ($KEYTABLE): "
  else
    echo -n $"Loading default keymap: "
  fi
  LOADKEYS=loadkeys
  if [ "${LANG}" != "${LANG%%.UTF-8}" -o "${LANG}" != "${LANG%%.utf8}" ]; then
        LOADKEYS="loadkeys -u"
  fi
  $LOADKEYS $KEYMAP < /dev/tty0 > /dev/tty0 2>/dev/null && \
     success $"Loading default keymap" || failure $"Loading default keymap"
  echo
 fi
fi
赤い部分はloadkeysコマンドを使うための準備だ。
loadkeysコマンドは、コンソール用のカーネルキーマップを
取り込むための物だ。

青い部分は、LANG変数に格納されている文字列についての条件文だ。
LANG変数に格納されている文字列で、尻尾の部分が「.UTF-8」、
もしくは「.utf8」の文字列が含まれている場合
「偽」となる条件文だ。

もし、尻尾の部分が「.UTF-8」、もしくは「.utf8」の文字列が
含まれていない場合は「真」なので、その場合は
ピンクの部分になる
loadkeysコマンドに「-u」のオプションが追加される形だ。

 だが、loadkeysコマンドが具体的にどういう役目なのかが

 よくわからないのらー (^^)

 なので、もっと知識をつけた時に取り上げたいと思います。


 ところで、Sambaの太田さんからご指摘があった。

太田さんからのご指摘 (2009/2/24)
loadkeysですね。これはキーマップをロードするための
コマンドです。

通常はJP106なキーボードを使っているわけですが、たまに
USなキーボードを使いたい場合(あるいはそれしかなかった場合)には、

loadkeys us

とやれば、キーマップがUSに切り替わります。
たとえば、VMイメージを持ち歩いて、あるところでデモするときに
USキーボードしかなかった場合には、このコマンドでキーマップを
変更しないと、文字入力がおかしくなってしまいます。

 loadkeysコマンドは、キーボードの変更時に有効なコマンドだ。


 次へ進む。  ホスト名の設定の部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Set the hostname.
update_boot_stage RChostname
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}
actionという関数が使われている。
一体、どんな関数なのだろうか。シェルで使われている関数ではないので
ユーザー定義関数だと推測ができる。

 actionという関数。
 ユーザー定義関数と推測できるので、ユーザー定義関数を
記述しているfunctionsのファイルを見てみる。

/etc/rc.d/init.d/functions (CentOS5.1の場合)
# Run some action. Log its output.
action() {
  local STRING rc

  STRING=$1
  echo -n "$STRING "
  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      echo -n "$STRING " > /etc/rhgb/temp/rhgb-console
  fi
  shift
  "$@" && success $"$STRING" || failure $"$STRING"
  rc=$?
  echo
  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      if [ "$rc" = "0" ]; then
        echo_success > /etc/rhgb/temp/rhgb-console
      else
        echo_failure > /etc/rhgb/temp/rhgb-console
        [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
      fi
      echo > /etc/rhgb/temp/rhgb-console
  fi
  return $rc
}
action関数の定義

「action」は「行動」、「行為」という意味だ。
なにかを処理するための関数だと想像できる。

ところで、この関数が実際にどういう処理を行うのかを知る上で
青色で示した「shift」コマンドで、赤色の変数が
重要な鍵を握ると直感的に思った。

 「$@」が重要な感じがするのだが・・・

 そもそも「$@」って何やねん!

 シェルのユーザー定義関数で、引数や引数に関連する表記を
調べてみることにした。
http://www.geocities.jp/geo_sunisland/parameter.html

シェルの引数に関する変数
$N
「N」は1以上の数字
N番目の引数の値を格納した変数になる。
$#
引数の数が格納された変数
$@
引数を全て取り込んだ変数。
「$*」とほぼ同じ役目で、わずかに違いがあるというが
その違いは、私にはわかりませーん (^^;;
$*
引数を全て取り込んだ変数。
「$@」とほぼ同じ役目で、わずかに違いがあるというが
その違いは、私にはわかりませーん (^^;;
$0
実行しているコマンドの名前が値になっている変数

./test.sh AAA BBB CCC 
を実行した場合、test.sh内部では、$0変数の値は
「./test.sh」になる。

  shiftコマンドも重要な気がするのだが

 shiftコマンドって一体、何やねん!

 調べてみると、引数を1つずらすコマンドだというのがわかった。

shiftコマンドについて
実験用のシェル(test.sh)
#!/bin/sh

echo "$@"
shift
echo "$@"
実行結果
[suga@linux]$ ./test1.sh AAA BBB CCC
AAA BBB CCC
BBB CCC
[suga@linux]$ 
引数の出力が最初は「AAA BBB CCC」だったのが
shiftコマンドを使うことで、1つずれるため
2回目の出力は「BBB CCC」になっている。
[suga@linux]$ 


 もう一度、action関数を見てみる。

/etc/rc.d/init.d/functions (CentOS5.1の場合)
# Run some action. Log its output.
action() {
  local STRING rc

  STRING=$1
  echo -n "$STRING "
  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      echo -n "$STRING " > /etc/rhgb/temp/rhgb-console
  fi
  shift
  "$@" && success $"$STRING" || failure $"$STRING"
  rc=$?
  echo
  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      if [ "$rc" = "0" ]; then
        echo_success > /etc/rhgb/temp/rhgb-console
      else
        echo_failure > /etc/rhgb/temp/rhgb-console
        [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
      fi
      echo > /etc/rhgb/temp/rhgb-console
  fi
  return $rc
}
赤い部分は、第一引数をSTRING変数に代入する。
そして青い部分は、STRING変数の文字列をechoコマンドで
画面出力する。

ただし、GUIで起動している場合、echoコマンドでは
画面出力できないので、ピンクの部分がその役目を果たす。
ピンクの部分の「/etc/rhgb/temp/rhgb-console」については
後述しています。

 もう一度、action関数を見てみる。

/etc/rc.d/init.d/functions (CentOS5.1の場合)
# Run some action. Log its output.
action() {
  local STRING rc

  STRING=$1
  echo -n "$STRING "
  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      echo -n "$STRING " > /etc/rhgb/temp/rhgb-console
  fi
  shift
  "$@" && success $"$STRING" || failure $"$STRING"
  rc=$?
  echo
  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
      if [ "$rc" = "0" ]; then
        echo_success > /etc/rhgb/temp/rhgb-console
      else
        echo_failure > /etc/rhgb/temp/rhgb-console
        [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
      fi
      echo > /etc/rhgb/temp/rhgb-console
  fi
  return $rc
}
赤い部分の「$?」は、直前に動かしたプロセスの返り値。
このスクリプトの場合、緑の部分で実行されたプロセスの返り値だ。
返り値「0」は正常で、それ以外は異常終了だ。

青い部分は正常終了した場合の処理だ。
ピンクの部分は異常終了した場合の処理だ。
異常終了した場合「/usr/bin/rhgb-client --details=yes」が
実行される。これはGUIの起動で詳細を出力させる命令になる。


 異常終了した時は、詳細画面に切り替わるという。
 実際に、その様子を見てみた。

起動時の処理で異常終了があった場合
起動時の処理中に、あるプログラムが異常終了した場合
上図のような詳細画面に切り替わる。
おそらく、どの部分で異常終了しているのかを知らせるために
詳細画面に切り替えていると考えられる。

 起動時の問題をわかりやすく伝える方法だ。


 さて、GUIで起動する際に、文字列を表示する際
文字列を「/etc/rhgb/temp/rhgb-console」ファイルへ
送っている事を書いた。

 実際に、/etc/rhgb/temp/rhgb-consoleファイルへ
文字列を送れば、表示されるのかどうか実験してみる事にした。

 そこで、rc.sysinitのスクリプトに以下の3行を追加した。

rc.sysinitに3行追加する
/usr/bin/rhgb-client --details=yes
echo -n "This is test message!! " > /etc/rhgb/temp/rhgb-console
echo > /etc/rhgb/temp/rhgb-console
/bin/sleep 10

# Set the hostname.
update_boot_stage RChostname
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}
赤い部分は、GUIの起動の際、詳細を出力するための部分。
青い部分は、肝心のファイルに文字列を送る部分。
ここでは「This is test message!!」の文字列を送っている

そしてピンク部分は、おねんね時間。10秒間おねんねさせる。
その方が、VMwareを使って画面をコピーしやすくなるのだ。

 そして実験開始すると以下の画面になった。

文字列を送信してみる
「This is test message!!」の文字列が表示されている。
頭では「こうであろう」と思っていても
実際に手を動かして目で確かめると安心できるし
少し賢くなった気になる (^^)

 だいぶ回り道になった。
 さて、本題に戻ります。以下のホスト名の設定部分が問題になっていた。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Set the hostname.
update_boot_stage RChostname
action $"Setting hostname ${HOSTNAME}: " hostname ${HOSTNAME}
action関数の第1引数(青い部分)は何の処理を行っているかを
表示するための文字列。
第2引数以降(ピンクの部分)は、処理コマンドとオプションになる。

ここでは処理コマンドは「hostname ホスト名」となる。
つまりホスト名の設定を行う部分で、処理が成功すれば
「OK」と表示され、失敗すると「FAILED」と表示される。

 たった2行のために、すっごく長くなってしまったが、
ようやく処理内容が理解できた (^^)

 次へ進む。 ACPIに関する部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Initialize ACPI bits
if [ -d /proc/acpi ]; then
    for module in /lib/modules/$unamer/kernel/drivers/acpi/* ; do
        module=${module##*/}
        module=${module%.ko}
        modprobe $module >/dev/null 2>&1
    done
fi
ACPIの設定に関する部分。
該当のモジュールを取り込む処理になっている。

 ここでふと思った。

 ところでACPIって何やねん?

 という事で調べてみると以下の略だという。

  Advanced Configuration and Power Interface

 だが、この略を見た時

 だから何やねん!

 だった。
 そこで調べてみる事にした。
 パソコンの電力管理に関する規格の1つで、
1996年にインテル、MS、東芝が共同で制定した規格だという。

 さて、LinuxのACPIについて調べてみる事にした。

 http://www.linux.or.jp/JF/JFdocs/ACPI-HOWTO.html

 うーん、難しそう。
  なのでお得意の忍法「先送り術」を使う事にした (^^)

 ところで、ACPIのモジュールは以下のディレクトリにある。

 /lib/modules/2.6.18-53.el5/kernel/drivers/acpi

 そのディレクトリからモジュールを読み込んでいる。

/lib/modules/2.6.18-53.el5/kernel/drivers/acpiディレクトリの中
(CentOS5.1の場合)
[suga@linux]$ ls
ac.ko         battery.ko  i2c_ec.ko    sbs.ko           video.ko
asus_acpi.ko  button.ko   ibm_acpi.ko  toshiba_acpi.ko
[suga@linux]$ ls
どうやら「video.ko」は画面出力に関する物だと推測できるし
「battert.ko」はバッテリー関連だと推測できる。

将来的には、取り上げたいと思いつつ、今は実力がないので
常套手段の忍法「先送り術」を使います (^^)


 次に進む。 ソフトウェアRAIDの部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# RAID setup
update_boot_stage RCraid
[ -x /sbin/nash ] && echo "raidautorun /dev/md0" | nash --quiet
if [ -f /etc/mdadm.conf ]; then
    /sbin/mdadm -A -s
fi
MDデバイスは、ソフトRAID用のデバイスだ。
でも、私はソフトRAID導入に断念して以来、
ハードウェアRAIDを使っているため、ここは読み飛ばす (^^)

 ソフトウェアRAIDは2003年に挑戦したのだが
実現には程遠く、ハードウェアRAIDの採用になった。
 詳しくは「システム奮闘記:その23」(RAIDの導入)をご覧下さい。

 次に進む。  デバイスマッピングの設定の部分のようだ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Device mapper & related initialization
if ! LC_ALL=C fgrep -q "device-mapper" /proc/devices 2>/dev/null ; then
        modprobe dm-mod >/dev/null 2>&1
fi
mkdir -p /dev/mapper >/dev/null 2>&1
mknod /dev/mapper/control c \
        $(awk '/ misc$/ { print $1 }' /proc/devices) \
        $(awk '/ device-mapper$/ { print $1 }' /proc/misc) >/dev/null 2>&1
[ -n "$SELINUX_STATE" ] && restorecon /dev/mapper /dev/mapper/control >/dev/null 2>&1

if [ -f /etc/crypttab ]; then
    s=$"Starting disk encryption:"
    echo "$s"
    init_crypto 0 && success "$s" || failure "$s"
    echo
fi

if [ -c /dev/mapper/control ]; then
        if ! strstr "$cmdline" nompath && [ -f /etc/multipath.conf -a \
                        -x /sbin/multipath.static ]; then
                modprobe dm-multipath > /dev/null 2>&1
                /sbin/multipath.static -v 0
                if [ -x /sbin/kpartx ]; then
                        /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p p"
                fi
        fi

        if ! strstr "$cmdline" nodmraid && [ -x /sbin/dmraid.static ]; then
                modprobe dm-mirror >/dev/null 2>&1
                for x in $(/sbin/dmraid.static -ay -i -p -t 2>/dev/null | \
                                egrep -iv "^no " | \
                                awk -F ':' '{ print $1 }') ; do
                        dmname=$(resolve_dm_name $x)
                        [ -z "$dmname" ] && continue
                        /sbin/dmraid.static -ay -i -p "$dmname" >/dev/null 2>&1
                        /sbin/kpartx -a -p p "/dev/mapper/$dmname"
                done
        fi

        if [ -x /sbin/lvm.static ]; then
                action $"Setting up Logical Volume Management:" /sbin/lvm.static vgchange -a y --ignorelockingfailure
        fi
fi
Device Mapperに関する設定。
調べてみると、LVM2に欠かせない物だという。

 本来なら調べていかねばならないと思うのだが

 私はLVM2を使わないもーん (^^)

 LVM2については、ディスクパーティションの際に
使えば便利のようだが、今の所、使う予定はない。
 それに調べだしたら膨大な量になるのも予想できる。

 なので、ここは忍法「使わないので説明する必要がない術」という
長い名前の忍法を使って逃げる事にします (^^)

 次へ進む。 ここからファイルシステムの点検に関する部分だ。  まずは準備のための処理が記述されている。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -f /fastboot ] || strstr "$cmdline" fastboot ; then
        fastboot=yes
fi
/fastboot ファイルが存在しているか、もしくは、
カーネルオプションに「fastboot」の文字列が
含まれている時、変数「fastboot」に「yes」を代入される。

 そして次の処理になる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -f /fsckoptions ]; then
        fsckoptions=`cat /fsckoptions`
fi
/fsckoptions ファイルが存在している場合、
変数「fsckoptions」に、/fsckoptions ファイルの中身を代入する。

 /fsckoptions ファイルの中身を見てみたかったが、
私の環境では存在しなかったので、中身がどんな物かを
知る事ができず残念・・・。

 ところで、変数「fsckoptions」は、あとでわかるのだが
ファイルシステムの点検の際に使うコマンド「fsck」のオプションになる。
 この際、色々な条件によりオプションが追加されていき
オプションの数が増えていくという流れになっている。

 次を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -f /forcefsck ] || strstr "$cmdline" forcefsck ; then
        fsckoptions="-f $fsckoptions"
elif [ -f /.autofsck ]; then
        [ -f /etc/sysconfig/autofsck ] && . /etc/sysconfig/autofsck
        if [ "$AUTOFSCK_DEF_CHECK" = "yes" ]; then
                AUTOFSCK_OPT="$AUTOFSCK_OPT -f"
        fi
        if [ -n "$AUTOFSCK_SINGLEUSER" ]; then
                echo
                echo $"*** Warning -- the system did not shut down cleanly. "
                echo $"*** Dropping you to a shell; the system will continue"
                echo $"*** when you leave the shell."
                [ -n "$SELINUX_STATE" ] && echo "0" > $selinuxfs/enforce
                sulogin
                [ -n "$SELINUX_STATE" ] && echo "1" > $selinuxfs/enforce
        fi
        fsckoptions="$AUTOFSCK_OPT $fsckoptions"
fi
まずは赤い部分。
/forcefsckファイルが存在しているか
もしくはカーネルオプションに「forcefsck」がある場合
fsckオプションに「-f」を追加する設定だ。

青い部分は「.autofsck」ファイルが正常終了したかどうかの
目印のファイルである事は、RedHat7.3と同じだ。

 次に進んでみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ "$BOOTUP" = "color" ]; then
        fsckoptions="-C $fsckoptions"
else
        fsckoptions="-V $fsckoptions"
fi
BOOTUP変数の値が「color」であれば
fcskのオプションに「-C」をつける
そうでなければ「-V」になる。

 ところで「-C」と「-V」のオプションの違いを調べてみた。
 すると以下の違いがあった。

fsckコマンドのオプションの違い
-C 作業の残りを棒グラフで表示してくれる
-V 詳細な表示をする

 うーん、わからん!

 というわけで実際に自分の目で違いを確かめる事にした。

fsckのオプション「-V」と「-C」の違い
何もつけない
[root@linux]# fsck -a /dev/sda1
fsck 1.39 (29-May-2006)
/boot: 38/26104 files (7.9% non-contiguous), 10308/104388 blocks
[root@linux]# 
オプション「-C」
[root@linux]# fsck -a -f -C /dev/sda1
fsck 1.39 (29-May-2006)
/: |===============================                                 / 48.5%
オプション「-V」
[root@linux]# fsck -a -f -V /dev/sda1
fsck 1.39 (29-May-2006)
[/sbin/fsck.ext2 (1) -- /boot] fsck.ext2 -a -f /dev/sda1
/boot: 38/26104 files (7.9% non-contiguous), 10308/104388 blocks
[root@linux]# 
オプションの違いによる出力結果の違いを色で表してみた。
「-C」は棒グラフになるのは、よくわかる。
でも「-V」は詳細といいながら、詳細なのと思ったりする (^^;;

 ここで一旦、ファイルシステムの点検に必要な準備
即ち、条件を判定しながらfcskコマンドの
オプションを決める処理が終了する。

 次へ進む。  ルートディレクトリ「/」を読み取り専用として マウントするのかどうかの判定と、 ルートディレクトリを読み取り専用としてマウントする場合の 処理の準備を部分だ。 まずは全体像を見てみる。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
READONLY=
if [ -f /etc/sysconfig/readonly-root ]; then
        . /etc/sysconfig/readonly-root
fi
if strstr "$cmdline" readonlyroot ; then
        READONLY=yes
        [ -z "$RW_MOUNT" ] && RW_MOUNT=/var/lib/stateless/writable
fi
if strstr "$cmdline" noreadonlyroot ; then
        READONLY=no
fi

if [ "$READONLY" = "yes" -o "$TEMPORARY_STATE" = "yes" ]; then

        mount_empty() {
                if [ -e "$1" ]; then
                        echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        mount_dirs() {
                if [ -e "$1" ]; then
                        mkdir -p "$RW_MOUNT$1"
                        # fixme: find is bad
                        find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        mount_files() {
                if [ -e "$1" ]; then
                        cp -a --parents "$1" "$RW_MOUNT"
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        # Common mount options for scratch space regardless of
        # type of backing store
        mountopts=

        # Scan partitions for local scratch storage
        rw_mount_dev=$(blkid -t LABEL="$RW_LABEL" -o device | awk '{ print ; exit }')

        # First try to mount scratch storage from /etc/fstab, then any
        # partition with the proper label.  If either succeeds, be sure
        # to wipe the scratch storage clean.  If both fail, then mount
        # scratch storage via tmpfs.
        if mount $mountopts "$RW_MOUNT" > /dev/null 2>&1 ; then
                rm -rf "$RW_MOUNT" > /dev/null 2>&1
        elif [ x$rw_mount_dev != x ] && mount $rw_mount_dev $mountopts "$RW_MOUNT" > /dev/null 2>&1; then
                rm -rf "$RW_MOUNT"  > /dev/null 2>&1
        else
                mount -n -t tmpfs $mountopts none "$RW_MOUNT"
        fi

        for file in /etc/rwtab /etc/rwtab.d/* ; do
                is_ignored_file "$file" && continue
                [ -f $file ] && cat $file | while read type path ; do
                        case "$type" in
                                empty)
                                        mount_empty $path
                                        ;;
                                files)
                                        mount_files $path
                                        ;;
                                dirs)
                                        mount_dirs $path
                                        ;;
                                *)
                                        ;;
                        esac
                        [ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
                done
        done

        # In theory there should be no more than one network interface active
        # this early in the boot process -- the one we're booting from.
        # Use the network address to set the hostname of the client.  This
        # must be done even if we have local storage.
        ipaddr=
        if [ "$HOSTNAME" = "localhost" -o "$HOSTNAME" = "localhost.localdomain" ]; then
                ipaddr=$(ip addr show to 0/0 scope global | awk '/[[:space:]]inet / { print gensub("/.*","","g",$2) }')
                if [ -n "$ipaddr" ]; then
                        eval $(ipcalc -h $ipaddr 2>/dev/null)
                        hostname ${HOSTNAME}
                fi
        fi

        # Clients with read-only root filesystems may be provided with a
        # place where they can place minimal amounts of persistent
        # state.  SSH keys or puppet certificates for example.
        #
        # Ideally we'll use puppet to manage the state directory and to
        # create the bind mounts.  However, until that's all ready this
        # is sufficient to build a working system.

        # First try to mount persistent data from /etc/fstab, then any
        # partition with the proper label, then fallback to NFS
        state_mount_dev=$(blkid -t LABEL="$STATE_LABEL" -o device | awk '{ print ; exit }')
        if mount $mountopts "$STATE_MOUNT" > /dev/null 2>&1 ; then
                /bin/true
        elif [ x$state_mount_dev != x ] && mount $state_mount_dev $mountopts "$STATE_MOUNT" > /dev/null 2>&1;  then
                /bin/true
        elif [ -n "$CLIENTSTATE" ]; then
                # No local storage was found.  Make a final attempt to find
                # state on an NFS server.

                mount -t nfs $CLIENTSTATE/$HOSTNAME $STATE_MOUNT -o rw,nolock
        fi

        if [ -d $STATE_MOUNT/etc ]; then
                # Copy the puppet CA's cert from the r/o image into the
                # state directory so that we can create a bind mount on
                # the ssl directory for storing the client cert.  I'd really
                # rather have a unionfs to deal with this stuff
                cp --parents -f -p /var/lib/puppet/ssl/certs/ca.pem $STATE_MOUNT 2>/dev/null

                # In the future this will be handled by puppet
                for i in $(grep -v "^#" $STATE_MOUNT/files); do
                        if [ -e $i ]; then
                                mount -n -o bind $STATE_MOUNT/${i} ${i}
                        fi
                done
        fi
fi

 長い、長い処理なのだ。
 でも・・・

 どんな処理なのか、全然わからへん (TT)

 なので分解しながら、じっくり見ていく事にした。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
READONLY=
if [ -f /etc/sysconfig/readonly-root ]; then
        . /etc/sysconfig/readonly-root
fi
if strstr "$cmdline" readonlyroot ; then
        READONLY=yes
        [ -z "$RW_MOUNT" ] && RW_MOUNT=/var/lib/stateless/writable
fi
if strstr "$cmdline" noreadonlyroot ; then
        READONLY=no
fi
ルートディレクトリ「/」が読み込み専用の場合
READONLYの変数に「yes」にする設定だ。

まずは/etc/sysconfig/readonly-rootファイルを読み込む。

 さて、上での処理は、/etc/sysconfig/readonly-rootファイルを
読み込んでいるのだが、そのファイルのどんな中身なのか
見てみないと、上に書いてあるスクリプトの処理内容が見えてこない。

 そこで/etc/sysconfig/readonly-rootファイルの中身を
見てみる事にした。

/etc/sysconfig/readonly-rootファイルの中身(私の環境)
# Set to 'yes' to mount the system filesystems read-only.
READONLY=no
# Set to 'yes' to mount various temporary state as either tmpfs
# or on the block device labelled RW_LABEL. Implied by READONLY
TEMPORARY_STATE=no
# Place to put a tmpfs for temporary scratch writable space
RW_MOUNT=/var/lib/stateless/writable
# Label on local filesystem which can be used for temporary scratch space
RW_LABEL=stateless-rw
# Label for partition with persistent data
STATE_LABEL=stateless-state
# Where to mount to the persistent data
STATE_MOUNT=/.snapshot
赤色は変数READONLYの初期値の設定をしている
初期値は「no」になっている。

他にも青色の部分の変数RW_MOUNTのディレクトリの指定がある。
これは少し後の部分に必要になってくる。

 さて/etc/sysconfig/readonly-rootファイルの中身が
わかった所で、再度、READONLYの変数の処理部分を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
READONLY=
if [ -f /etc/sysconfig/readonly-root ]; then
        . /etc/sysconfig/readonly-root
fi
if strstr "$cmdline" readonlyroot ; then
        READONLY=yes
        [ -z "$RW_MOUNT" ] && RW_MOUNT=/var/lib/stateless/writable
fi
if strstr "$cmdline" noreadonlyroot ; then
        READONLY=no
fi
赤い部分はカーネルオプションに「readonlyroot」が含まれる場合
真になる条件文だ。
もし、真になれば変数READONLYの値は「yes」を代入。(青い部分)

そしてピンクの部分は変数RW_MOUNTの中身が空の場合
変数の値を「var/lib/stateless/writable」を代入している。
ただ、ピンクの部分は念のための設定だと思う。
/etc/sysconfig/readonly-rootファイルの中に
変数RW_MOUNTの初期値の設定が行われているからだ。


 ルートディレクトリ「/」を読み取り専用にするかどうかの
判定処理を見てみた。
 ここまでの流れを見て、ルートディレクトリ「/」を
読み取り専用にする場合、以下の2つの設定が行われる。

行われる設定 (CentOS5.1の場合)
その1
変数READONLYの値を「yes」にする
その2
変数W_MOUNTの値を「var/lib/stateless/writable」にする。

 これでルートディレクトリ「/」をマウントする際、
読み取り専用にする仕掛けが整う。


 ところで、ルートディレクトリをマウントする場合
読み取り専用にする際は、以下の3点のどれかを設定する必要がある。
 ただし、その3については上記では説明していませんが、
読み取り専用の処理が出てきたら、理由がわかりますので
説明は省略します。

判定方法 (CentOS5.1の場合)
その1
/etc/sysconfig/readonly-rootファイルで設定している
変数READONLYの値が「yes」の場合
その2
カーネルオプションの値の中に「readonlyroot」という
文字列が含まれている場合
その3
変数TEMPORARY_STATEの値が「yes」の場合。
/etc/sysconfig/readonly-rootファイルで設定している。
初期値は「no」なので、必要があれば、そのファイルを開いて
初期値を変更すれば良い

 さて、ルートディレクトリ「/」を読み込み専用の場合の
処理を見ていく事にする。
 ここも処理が長いので項目を分けて見ていく事にした。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ "$READONLY" = "yes" -o "$TEMPORARY_STATE" = "yes" ]; then

        mount_empty() {
                if [ -e "$1" ]; then
                        echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        mount_dirs() {
                if [ -e "$1" ]; then
                        mkdir -p "$RW_MOUNT$1"
                        # fixme: find is bad
                        find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        mount_files() {
                if [ -e "$1" ]; then
                        cp -a --parents "$1" "$RW_MOUNT"
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }
まず、ルートディレクトリ「/」を読み取り専用にマウントするために
必要なユーザー定義関数を記述している。
だが、どの処理でこの関数を使っているのか見てみない事には
関数の記述を見てもピンとこない。

 ユーザー定義関数の部分は後に触れるとして、次を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
        # Common mount options for scratch space regardless of
        # type of backing store
        mountopts=

        # Scan partitions for local scratch storage
        rw_mount_dev=$(blkid -t LABEL="$RW_LABEL" -o device | awk '{ print ; exit }')

        # First try to mount scratch storage from /etc/fstab, then any
        # partition with the proper label.  If either succeeds, be sure
        # to wipe the scratch storage clean.  If both fail, then mount
        # scratch storage via tmpfs.
        if mount $mountopts "$RW_MOUNT" > /dev/null 2>&1 ; then
                rm -rf "$RW_MOUNT" > /dev/null 2>&1
        elif [ x$rw_mount_dev != x ] && mount $rw_mount_dev $mountopts "$RW_MOUNT" > /dev/null 2>&1; then
                rm -rf "$RW_MOUNT"  > /dev/null 2>&1
        else
                mount -n -t tmpfs $mountopts none "$RW_MOUNT"
        fi

        for file in /etc/rwtab /etc/rwtab.d/* ; do
                is_ignored_file "$file" && continue
                [ -f $file ] && cat $file | while read type path ; do
                        case "$type" in
                                empty)
                                        mount_empty $path
                                        ;;
                                files)
                                        mount_files $path
                                        ;;
                                dirs)
                                        mount_dirs $path
                                        ;;
                                *)
                                        ;;
                        esac
                        [ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
                done
        done
赤い部分を見てみる。
/etc/rwtabファイルと、/etc/rwtab.dディレクトリの中の
ファイルを読みにいっている。

 どうも、何がしたいのか、よくわからない。
 そこで、/etc/rwtabファイルの中身を見てみる事にした。

/etc/rwtabの中身 (CentOS5.1の場合)
dirs    /var/cache/man
dirs    /var/gdm
dirs    /var/lock
dirs    /var/log
dirs    /var/run

empty   /tmp
empty   /var/cache/foomatic
empty   /var/cache/logwatch
empty   /var/cache/mod_ssl
empty   /var/cache/mod_proxy
empty   /var/cache/php-pear
empty   /var/cache/systemtap
empty   /var/db/nscd
empty   /var/lib/dav
empty   /var/lib/dhcp
empty   /var/lib/dhclient
empty   /var/lib/php
empty   /var/lib/ups
empty   /var/tmp
empty   /var/tux

files   /etc/adjtime
files   /etc/fstab
files   /etc/mtab
files   /etc/ntp.conf
files   /etc/resolv.conf
files   /etc/lvm/.cache
files   /var/account
files   /var/arpwatch
files   /var/cache/alchemist
files   /var/lib/iscsi
files   /var/lib/logrotate.status
files   /var/lib/ntp
files   /var/lib/xen
ディレクトリの名前が出ている。
なぜ、こんな物があるのだろうか?

 一体、何をするためのファイルなのかが、わからなくなってきた。
 その前に、もっと根本的な事を知りたくなった。

 読み取り専用で起動したら、どうなるねん?

 論より証拠で読み取り専用で起動してみる事にした。
 カーネルオプションに「readonlyroot」を追加する。

/boot/grub/grub.conf を書き加える
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /boot/, eg.
#          root (hd0,0)
#          kernel /vmlinuz-version ro root=/dev/sda2
#          initrd /initrd-version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.18-53.el5)
        root (hd0,0)
        kernel /vmlinuz-2.6.18-53.el5 ro root=LABEL=/ rhgb quiet readonlyroot
        initrd /initrd-2.6.18-53.el5.img
GRUBの記述ファイルは/etc/grub/menu.lstなのだが
CentOSの場合、grub.confファイルになっているので注意。
詳しくは「システム奮闘記:その71」のブートローダー(GRUB)の話をご覧ください。

赤い部分が追加したカーネルオプションで
「readonlyroot」を付ける事で、ルートディレクトリが
読み込み専用になるという。

 早速、再起動してみる。
 意外にも普通にログイン画面までたどり着いた。


ログイン画面

 別に何も変わらへんやん!

 そう思いつつログインしてみた。
 すると予想外の画面が出てきた。

ログインしてみると
拡大してみると
「フェイルセーフなセッションからログイン」と言われている。
せやけど、フェイルセーフって何やねんと思った。

 とりあえず詳細を見てみる事にした。
 すると次の画面が出てきた。

詳細も表示してみた
詳細表示を見ても、意味がわからない。
一体、こんな不具合が出るのは何故だろうかと考えてしまう。

 なので、とりあえず「OK」ボタンを押した。
 すると、最初のログインの待ち受け画面に戻った。

 エラー表示の際の「セッション」という文字が出てきた。
 なので、以下の部分を選択してみた。

ログイン画面にあるセッションの選択
今まで何気なく見ていたログイン画面だったが
選択メニューに「セッション」があったのには気づかなかった。

 メニューを選択すると以下の画面が出てきた。

セッションの選択
セッションの選択でピンクで囲んだ
「フェイルセーフの端末」を選ぶ。

 さて、ログイン画面に戻り、ログインしてみる。
 すると以下の画面が出てきた。

フェイルセーフでログインしてみると
拡大してみる
どうもフェイルセーフという言葉の意味がわからない。
カタカナ用語なのかわからん。

 とりあえず「OK」で前に進む。
 すると以下の画面が出てきた。

xtermの画面が出てくるが
xtermが出てくる。
でも、それ以外は何もない。dfコマンドなどが使えない。

 このままだと画面は小さい上、日本語表示が化けるので
どうも使い勝手が悪い。そこで他のマシンからtelnetでログインして
使ってみる事にした。

他のマシンからtelnetでログイン
[suga@server]$ telnet 192.168.1.217
Trying 192.168.1.217...
Connected to 192.168.1.217 (192.168.1.217).
Escape character is '^]'.
CentOS release 5 (Final)
Kernel 2.6.18-53.el5 on an i686
login: suga
Password: 
Last login: Sun Jan 25 16:18:25 from aaa.bbb.co.jp
[suga@localhost suga]# df
df: cannot read table of mounted file systems
[suga@localhost suga]# 
[suga@localhost ~]$ touch testfile
touch: cannot touch `testfile': 読み込み専用ファイルシステムです
[suga@localhost ~]$ 
他のマシンからtelnetでログインするのは可能だ。

ピンクの部分はdfコマンドを使った時のエラー。
書き込み不可能なため、dfコマンドが使えないようだ。

青い部分はtouchコマンドでtestfileというファイルを作成。
しかし書き込み不可能なため、赤い部分のエラー表示が出る。

 完全に書き込み禁止になっている。
 そして、ふと思った。

 どうやって元に戻したら、ええんや!

 書き込み禁止になっている。
 でも、ふと思った。
 /boot/grub/grub.confファイルは、別のデバイスなので
書き込みは可能なのかも。

実験機のパーティションの状態
[root@linux]# df
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/sda2              9803884   2631160   6666680  29% /
/dev/sda1               101086      7006     88861   8% /boot
tmpfs                   204932         0    204932   0% /dev/shm
[root@linux]# 
赤い部分はルートディレクトリのパーティションで
実験の結果、この部分は読み込む専用になってしまっている。

でも、青い部分は通常通り、読み書き可能なのだ。
/boot/grub/grub.confファイルの書き換えはできる。

 なので、/boot/grub/grub.confファイルを元の設定に書き換えて
再起動させると、元に戻った。

 やれやれなのらー!! (^^;;

 ところでルートディレクトリ「/」を読み込み専用にするのは
なぜなのか。疑問に思ったりする。
 なので、早速調べてみるが・・・

 全然、見つからへん (TT)

 困った。
 そこでスクリプトを眺めていると、ヒントがあった。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ "$READONLY" = "yes" -o "$TEMPORARY_STATE" = "yes" ]; then

(途中、省略)

        else
                mount -n -t tmpfs $mountopts none "$RW_MOUNT"
        fi

        for file in /etc/rwtab /etc/rwtab.d/* ; do
                is_ignored_file "$file" && continue
                [ -f $file ] && cat $file | while read type path ; do
                        case "$type" in
                                empty)
                                        mount_empty $path
                                        ;;
(途中、省略)

        fi
fi
赤い部分に反応した。
直感的に「rwtab」というファイル名が何かヒントになると思った。

 そこで「rwtab」を語句とした検索を行ってみた。
 すると・・・

 Stateless Linuxが出てきたのらー!!

 なんだか、とんでもない掘り出し物を探した感じだった。
 だが、その前に・・・

 そもそもStateless Linuxって何やねん!

 調べてみる。
 RedHat系独自の物で、RHEL5から採用された物だという。
 ディストリビューションではなく、RedHatの付属機能だという。

 日経ITProの「Red Hat Enterprise Linux 5の全貌」の記事

 これを見る限り、Stateless Linuxは、各Linuxサーバーやクライアントの
システムファイルを一括管理するのと、ルートディレクトリ「/」の
共有化にあるようだ。
 そのため、共有しているルートディレクトリを勝手に変更されないように
読み込み専用にしているように思える。

 そして、より詳しい記事があった。

 日経ITProの「Stateless Linux」の詳細記事

 でも・・・

 いまいちピンとこないのだ (--;;

 実際に自分の手で触って試行錯誤していかないと、
いまいち具体的な物が思い浮かべる事ができないのだ。


 ところで、ルートディレクトリを読み込み専用にした場合
/etcや/varなどのディレクトリは、Linuxのインストール時に
別にパーティションを作成してマウントさせる必要があるのだろうか

 でも、以下の部分に注目してみた。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ "$READONLY" = "yes" -o "$TEMPORARY_STATE" = "yes" ]; then

        mount_empty() {
                if [ -e "$1" ]; then
                        echo "$1" | cpio -p -vd "$RW_MOUNT" &>/dev/null
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        mount_dirs() {
                if [ -e "$1" ]; then
                        mkdir -p "$RW_MOUNT$1"
                        # fixme: find is bad
                        find "$1" -type d -print0 | cpio -p -0vd "$RW_MOUNT" &>/dev/null
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }

        mount_files() {
                if [ -e "$1" ]; then
                        cp -a --parents "$1" "$RW_MOUNT"
                        mount -n --bind "$RW_MOUNT$1" "$1"
                fi
        }


(途中、省略)

        for file in /etc/rwtab /etc/rwtab.d/* ; do
                is_ignored_file "$file" && continue
                [ -f $file ] && cat $file | while read type path ; do
                        case "$type" in
                                empty)
                                        mount_empty $path
                                        ;;
                                files)
                                        mount_files $path
                                        ;;
                                dirs)
                                        mount_dirs $path
                                        ;;
                                *)
                                        ;;
                        esac
                        [ -n "$SELINUX_STATE" -a -e "$path" ] && restorecon -R "$path"
                done
        done

(途中、省略)

fi
3つの関数の定義の部分で変数RW_MOUNTがある。
これは/etc/sysconfig/readonly-rootファイルで宣言されている。
初期値は「/var/lib/stateless/writable」だ。

3つのユーザー定義関数は/etc/rwtabの中身に記述している
ファイルやディレクトリを作成するための関数だ。赤い部分。

 実際に、/var/lib/stateless/writableディレクトリに
ファイルやディレクトリが作成されているかどうか
確認してみる事にした。


/var/lib/stateless/writableディレクトリ
[suga@linux]$ cd /var/lib/stateless/writable/
[suga@linux]$ ls -l
合計 0
drwxr-xr-x  2 root root 140  2月  1 13:39 etc
drwxrwxrwt  5 root root 140  2月  1 13:39 tmp
drwxr-xr-x 11 root root 220  2月  1 13:39 var
[suga@linux]$ 
[suga@linux]$ cd etc
[suga@linux]$ ls -l
合計 16
-rw-r--r-- 1 root root   44  2月  1 13:38 adjtime
-rw-r--r-- 1 root root  532  1月 16 09:31 fstab
-rw-r--r-- 1 root root    0  2月  1 13:39 mtab
-rw-r--r-- 1 root root 1833 11月 10  2007 ntp.conf
-rw-r--r-- 1 root root   44  1月 16 09:57 resolv.conf
[suga@linux]$ 
/var/lib/stateless/writableディレクトリに
3つディレクトリが作成されているのがわかる。
そして、その中のetcディレクトリに入ると
各種ファイルがコピーされているのがわかる。

/var/lib/stateless/writableディレクトリだが
よく見るとwritableは「書き込み可能」の意味だ。
要するに、ルートディレクトリにありそうなディレクトリやファイルで
書き込み可能にしたい物をコピーなどをしているのだ。

 今はStateless Linuxの導入の予定はない。
 でも、将来は導入するかもしれない。
 そのための頭の片隅に「Stateless Linux」の文字を入れるのに
良い機会になった (^^)

 次へ進む。  ファイルシステムの点検の準備に話が戻る。  ここでもfsckコマンドのオプションを設定していく部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if ! [[ " $fsckoptions" =~ " -y" ]]; then
        fsckoptions="-a $fsckoptions"
fi
変数fsckoptionsの値(文字列)に「-y」が含まれていない場合
変数fsckoptionsの値に「-a」の文字列が追加される。

ちなみにfsckのオプションで「-y」は、常に自動的に破損した
ファイルシステムを修復するための物だ。
それがない場合は「-a」オプションをつけるというのだ。

「-a」は何の問い合わせもなく、自動的にファイルシステムの
修復を行うためのオプションだ。

似たような物だ。fsckコマンドは、ほとんど使った事がないので
違いはわからないのだ (^^)

 要するに、fsckコマンドでファイルシステムの点検を行う時に
ファイルシステムに破損があった場合、自動的に修復するように
設定しているというのだ。


 ディスクの利用状況などを知るquotaコマンドの オプションに関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
_RUN_QUOTACHECK=0
if [ -f /forcequotacheck ] || strstr "$cmdline" forcequotacheck ; then
        _RUN_QUOTACHECK=1
fi
/forcequotacheckファイルが存在するか、
もしくはカーネルオプションに「forcequotacheck」があれば
強制的にquotaの点検を行う仕掛けに準備に思える


 次へ進む。  ここがファイルシステムの点検と修復を行う場所になる。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -z "$fastboot" -a "$READONLY" != "yes" ]; then

        STRING=$"Checking filesystems"
        echo $STRING
        if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then
                fsck -T -t noopts=_netdev -A $fsckoptions > /etc/rhgb/temp/rhgb-console
        else
                fsck -T -t noopts=_netdev -A $fsckoptions
        fi
        rc=$?

        if [ "$rc" -eq "0" ]; then
                success "$STRING"
                echo
        elif [ "$rc" -eq "1" ]; then
                passed "$STRING"
                echo
        elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then
                echo $"Unmounting file systems"
                umount -a
                mount -n -o remount,ro /
                echo $"Automatic reboot in progress."
                reboot -f
        fi

        # A return of 4 or higher means there were serious problems.
        if [ $rc -gt 1 ]; then
                if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
                    chvt 1
                fi

                failure "$STRING"
                echo
                echo
                echo $"*** An error occurred during the file system check."
                echo $"*** Dropping you to a shell; the system will reboot"
                echo $"*** when you leave the shell."

                str=$"(Repair filesystem)"
                PS1="$str \# # "; export PS1
                [ "$SELINUX_STATE" = "1" ] && disable_selinux
                sulogin

                echo $"Unmounting file systems"
                umount -a
                mount -n -o remount,ro /
                echo $"Automatic reboot in progress."
                reboot -f
        elif [ "$rc" -eq "1" ]; then
                _RUN_QUOTACHECK=1
        fi
        if [ -f /.autofsck -a -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
                chvt 8
        fi
fi
赤い部分はファイルシステムの点検と修復を行う際の
条件判断を行う。

変数fastbootの値(文字列)の長さがゼロの場合で、かつ、
変数READONLYの値が「yes」(ルートディレクトリのマウントが読み込み専用)
でない場合が、ファイルシステムの点検・修復を行う場合だ。

ところで、fastbootはブートの速度を向上させるため、
ファイルシステムの点検・修復の工程を省いている。

READONLYは読み込み専用なので、ファイルシステムの修復を行うと
修復中にファイルの中身を書き換えてしまうため
矛盾がでないように、点検・修復作業を省く形になっている。


青い部分は、GUI画面で起動する場合の点検・修復作業の部分。
ピンクの部分は、テキストでの起動する場合の点検・修復作業の部分。
両方ともオプションの違いはない。

緑色はfsckコマンドの結果(返り値)を変数rcに代入している。

 fsckコマンドの返り値を調べてみた。

fsckコマンドの返り値
0
エラーなし
1
エラーはあったが修正できた
2
システムを再起動する必要がある。
異常ありで修正ができない感じ
4
エラーが修正できない箇所がある。
8
操作エラー

 他にも数値があがっているのだが、どれも正常終了ではない。
 それを頭に入れた上で、もう一度、スクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -z "$fastboot" -a "$READONLY" != "yes" ]; then

        STRING=$"Checking filesystems"
        echo $STRING
        if [ "${RHGB_STARTED}" != "0" -a -w /etc/rhgb/temp/rhgb-console ]; then
                fsck -T -t noopts=_netdev -A $fsckoptions > /etc/rhgb/temp/rhgb-console
        else
                fsck -T -t noopts=_netdev -A $fsckoptions
        fi
        rc=$?

        if [ "$rc" -eq "0" ]; then
                success "$STRING"
                echo
        elif [ "$rc" -eq "1" ]; then
                passed "$STRING"
                echo
        elif [ "$rc" -eq "2" -o "$rc" -eq "3" ]; then
                echo $"Unmounting file systems"
                umount -a
                mount -n -o remount,ro /
                echo $"Automatic reboot in progress."
                reboot -f
        fi

        # A return of 4 or higher means there were serious problems.
        if [ $rc -gt 1 ]; then
                if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
                    chvt 1
                fi

                failure "$STRING"
                echo
                echo
                echo $"*** An error occurred during the file system check."
                echo $"*** Dropping you to a shell; the system will reboot"
                echo $"*** when you leave the shell."

                str=$"(Repair filesystem)"
                PS1="$str \# # "; export PS1
                [ "$SELINUX_STATE" = "1" ] && disable_selinux
                sulogin

                echo $"Unmounting file systems"
                umount -a
                mount -n -o remount,ro /
                echo $"Automatic reboot in progress."
                reboot -f
        elif [ "$rc" -eq "1" ]; then
                _RUN_QUOTACHECK=1
        fi
        if [ -f /.autofsck -a -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
                chvt 8
        fi
fi
赤い部分はfsckコマンド(ファイルシステムの点検)が
正常に終了した場合の処理。

青い部分は、ファイルシステムの点検で不具合を発見し
無事、修正が完了した時の処理。

ピンクの部分は、ファイルシステムの点検で不具合があり
しかも再起動の必要があった場合、再起動を行う処理だ。

緑の部分は返り値が「2」以上の場合になっているが
既に「2」、「3」の場合は、再起動が走っているため、
実際には返り値は「4」以上の場合の処理になる。
返り値が「4」以上は重大な問題を引き起こしているという事で
再起動をかけている。

 ファイルシステムの点検と修正の箇所は、ここで終わった。


 次へ進む。  quotaによるディスクの利用状況などに関する設定部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Update quotas if necessary
if [ X"$_RUN_QUOTACHECK" = X1 -a -x /sbin/quotacheck ]; then
        if [ -x /sbin/convertquota ]; then
            # try to convert old quotas
            for mountpt in `LC_ALL=C awk '$4 ~ /quota/{print $2}' /etc/mtab` ; do
                mountpt="$(fstab_decode_str "$mountpt")"
                if [ -f "$mountpt/quota.user" ]; then
                    action $"Converting old user quota files: " \
                    /sbin/convertquota -u "$mountpt" && \
                        rm -f "$mountpt/quota.user"
                fi
                if [ -f "$mountpt/quota.group" ]; then
                    action $"Converting old group quota files: " \
                    /sbin/convertquota -g "$mountpt" && \
                        rm -f "$mountpt/quota.group"
                fi
            done
        fi
        action $"Checking local filesystem quotas: " /sbin/quotacheck -aRnug
fi

 でも、今の私はquotaを使わないし、使った事すらない。
 なのでお得意の・・・

 忍法「棚上げの術」なのだ (^^)
 
 というわけで、ディスクの利用状況などについて
quotaの知識が必要になった時に取り上げたいと思います。

 次へ進む。  ここではルートディレクトリの再マウントを行う部分だ。  最初のマウントはシェルのinitが走り出す前の段階で マウントされる。その時は、読み込み可能の状態で書き込み不可能になっている。  再マウントでは読み込みだけでなく、書き込み可能にしている。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Remount the root filesystem read-write.
update_boot_stage RCmountfs
state=`LC_ALL=C awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`
[ "$state" != "rw" -a "$READONLY" != "yes" ] && \
  action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /
赤い部分が変数stateへの代入部分だ。

そして青い部分は再マウントするための条件を満たしているかどうかの
判定を行う部分だ。READONLY変数の値が「yes」でない場合は
ルートディレクトリのマウントを読み込みだけでないので
再マウントを行う。
それと同時に、state変数が「rw」でない場合も条件にはいる。

条件を満たせば、action関数に実行したいコマンド(ピンクの部分)を送っている。
action関数内でピンクの部分(再マウントの処理)が行われる。

 条件の部分が気になる。
 state変数の中身は、一体、何なのか。
 そこで実際に、state変数がどんな値なのか見てみる事にした。

state変数を求める (私の環境)
[suga@linux]$ LC_ALL=C awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts
rw,data=ordered
[suga@linux]$ 
/proc/mountsファイルの中から「rw,data=ordered」の文字列を
抽出している事がわかる。

 なぜ、awkの条件文に触れずに出力結果から先に出したのか
 理由は簡単です。

 だって、awkの知識は乏しいもーん (^^)

 なので、awkの条件文を見て、どういう処理をしているのかを
確かめる前に、実際に出力結果からを見てみる事にしたのだ。

 さて、/proc/mountsの中身をawkで処理しているが
何の事やら、よくわからない。
 そこで私の環境での/proc/mountsの中身を見てみる。

/proc/mountsの中身 (私の環境)
[suga@linux]$ cat /proc/mounts 
rootfs / rootfs rw 0 0
/dev/root / ext3 rw,data=ordered 0 0
/dev /dev tmpfs rw 0 0
/proc /proc proc rw 0 0
/sys /sys sysfs rw 0 0
/proc/bus/usb /proc/bus/usb usbfs rw 0 0
devpts /dev/pts devpts rw 0 0
/dev/sda1 /boot ext2 rw 0 0
tmpfs /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/etc/auto.misc /misc autofs rw,fd=6,pgrp=1723,timeout=300,minproto=5,maxproto=5,indirect 0 0
-hosts /net autofs rw,fd=11,pgrp=1723,timeout=300,minproto=5,maxproto=5,indirect 0 0
[suga@linux]$ 
/procディレクトリは、カーネルがメモリ上に保持している情報を
目に見える形に表示するためのディレクトリーだ。
なので、/proc/mountsは、カーネルが保持しているマウントの情報になる。

 加工したデータの中身がわかったので、次に加工処理である
awkの条件文をじっくり見てみる事にした。

awkの処理内容を見てみる
久々に、awkについて書かれている本を取り出してみた。

ルートディレクトリを指す部分と、3列目の文字列が
「rootfs」という文字列を含まないという2つの条件を満たす場合
4列目の文字列を出力するという物だ。

awkやsedは、LinuxをはじめとしたUNIX系のOSを使う際
便利なコマンドなのだが、私は相変わらず覚えれないでいる (^^;;

 さて、awkで抽出するための条件が理解できた所で
先ほどのawkの条件に当てはまるものを見てみる。

/proc/mountsの中身 (私の環境)
[suga@linux]$ cat /proc/mounts 
rootfs / rootfs rw 0 0
/dev/root / ext3 rw,data=ordered 0 0
/dev /dev tmpfs rw 0 0
/proc /proc proc rw 0 0
/sys /sys sysfs rw 0 0
/proc/bus/usb /proc/bus/usb usbfs rw 0 0
devpts /dev/pts devpts rw 0 0
/dev/sda1 /boot ext2 rw 0 0
tmpfs /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
/etc/auto.misc /misc autofs rw,fd=6,pgrp=1723,timeout=300,minproto=5,maxproto=5,indirect 0 0
-hosts /net autofs rw,fd=11,pgrp=1723,timeout=300,minproto=5,maxproto=5,indirect 0 0
[suga@linux]$ 
(条件)
「 / 」の文字列を含む物。(「/」の前後に空白を含む)
そして3列目は「rootfs」の文字列を含まない物。

この2つの条件を満たす物は、青色の部分になる。
青色部分の4列目を取り出すのだから、取り出される文字列は
「rw,data=ordered」になる。

 ところで上の結果「rw」を見て「ちょっと待てよ」と思った。
 ルートディレクトリが読み書き可能になっている。
 最初、Linux起動する際に、ルートディレクトリを
マウントする場合は、読み込み専用になっているはず。

 なので「rw」という出力結果は矛盾している。
 一体、どうなっているのだろうか。

 そこで実際の起動時での、awkの結果を見てみる事にした。
 以下のようにスクリプトに追加を行った。


スクリプトに以下の2行を追加(色をつけています)
# Remount the root filesystem read-write.
update_boot_stage RCmountfs
state=`LC_ALL=C awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`

echo $state > /etc/rhgb/temp/rhgb-console
/bin/sleep 10

[ "$state" != "rw" -a "$READONLY" != "yes" ] && \
  action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /
赤い部分は、変数stateをGUIの起動画面で表示させるための部分。
青い部分は、じっくり表示が見れるように、10秒ほど、おねんねさせる部分。

 早速、Linux(CentOS5.1)を再起動をさせてみた。

起動の結果
起動中は「ro.data=ordered」という文字列が出力された。
この時点では、ルートディレクトリは読み取り専用で
マウントされている事がわかる。

なので、この状態だとルートディレクトリを
読み書き可能にするために、再マウントする必要がある事もわかる。

 もう1度、スクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Remount the root filesystem read-write.
update_boot_stage RCmountfs
state=`LC_ALL=C awk '/ \/ / && ($3 !~ /rootfs/) { print $4 }' /proc/mounts`
[ "$state" != "rw" -a "$READONLY" != "yes" ] && \
  action $"Remounting root filesystem in read-write mode: " mount -n -o remount,rw /
赤い部分がルートディレクトリの状態が、読み書き可能かどうかの判定で
読み書き可能なら除外される。
既に、読み書き可能なので、再マウントで読み書き可能にする必要がないからだ。

そして青い部分は再マウントするための条件を満たしているかどうかの
判定を行う部分だ。READONLY変数の値が「yes」でない場合は
ルートディレクトリのマウントを読み込みだけでないので
再マウントを行う。

条件を満たせば、action関数に実行したいコマンド(ピンクの部分)を送っている。
action関数内でピンクの部分(再マウントの処理)が行われる。

 これですっきりした。
 たった4行の再マウントするための処理のために、
色々、調べたなぁ・・・。

 次へ進む。  SELinuxに関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean up SELinux labels
if [ -n "$SELINUX_STATE" ]; then
   restorecon /etc/mtab /etc/ld.so.cache /etc/blkid/blkid.tab /etc/resolv.conf >/dev/null 2>&1
fi

 でも、私はSELinuxは使わない(使える程の実力がない)ので
ここは飛ばします。


 次に進む。  /etc/mtabファイルに関する処理だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clear mtab
(> /etc/mtab) &> /dev/null

# Remove stale backups
rm -f /etc/mtab~ /etc/mtab~~

# Enter mounted filesystems into /etc/mtab
mount -f /
mount -f /proc >/dev/null 2>&1
mount -f /sys >/dev/null 2>&1
mount -f /dev/pts >/dev/null 2>&1
mount -f /proc/bus/usb >/dev/null 2>&1

# Mount all other filesystems (except for NFS and /proc, which is already
# mounted). Contrary to standard usage,
# filesystems are NOT unmounted in single user mode.
action $"Mounting local filesystems: " mount -a -t nonfs,nfs4,smbfs,ncpfs,cifs,gfs -O no_netdev

 見た感じでは、/etc/mtabファイルやマウントの設定に関する記述だが

 せやけど、よくわからん!!

 なので個別に見ていく事にする。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clear mtab
(> /etc/mtab) &> /dev/null
「> (ファイル名)」を実行すれば
該当のファイルの中身が空になる。

/etc/mtabファイルの中身をきれいに消去して
空ファイルにする処理だ。

 そして/etc/mtabを編集する際のバックアップファイルとして
チルド「~」がついたファイル「/etc/mtab~」を消去する。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Remove stale backups
rm -f /etc/mtab~ /etc/mtab~~

 ところで、そもそも

 /etc/mtabファイルって何やねん!

 いくら/etc/mtabや関連のファイルを消去する処理だとわかっても
肝心の/etc/mtabファイルが、どういうファイルなのかを知らないと
どういう処理なのかが理解できない。

 調べてみると次のような役割のファイルだった。

 現在、マウントされているデバイスを記録するためのファイル

  ファイル名のmtabは「mounted filesystem table」の略だという。

 実際に、/etc/mtabファイルの中身を見てみた。

/etc/mtabファイルの中身(私の環境)
[suga@linux]$ df -a
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/sda2              2692164   2243904    309300  88% /
proc                         0         0         0   -  /proc
sysfs                        0         0         0   -  /sys
devpts                       0         0         0   -  /dev/pts
/dev/sda1               101086      7004     88863   8% /boot
tmpfs                   127812         0    127812   0% /dev/shm
none                         0         0         0   -  /proc/sys/fs/binfmt_misc
[suga@linux]$
[suga@linux]$ more /etc/mtab
/dev/sda2 / ext3 rw 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
devpts /dev/pts devpts rw,gid=5,mode=620 0 0
/dev/sda1 /boot ext2 rw 0 0
tmpfs /dev/shm tmpfs rw 0 0
none /proc/sys/fs/binfmt_misc binfmt_misc rw 0 0
[suga@linux]$ 
dfコマンドの結果と、/etc/mtabファイルの表示の2つを出す事で
わかりやすくなる。

しかもマウントの状態はdfコマンドの出力結果よりも
詳細に出ている。

 ここで思うのは・・・

 fstabと何が違うねん!

 調べてみると、次の違いがあったのだ。

fsckコマンドのオプションの違い
fstab 起動時の初期状態でマウントされるべき
デバイス等を記述している。
mtab
現在、マウントされているデバイス等が
記述されている。もし、マウントの状態に
変化があると、このファイルの記述も変更される。

現在進行形のマウントの状態を記述したファイルなのだ。

 mtabファイルの存在、そして意味合いを初めて知る事ができた。
 うーん、知らない事だらけだなぁ・・・。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Enter mounted filesystems into /etc/mtab
mount -f /
mount -f /proc >/dev/null 2>&1
mount -f /sys >/dev/null 2>&1
mount -f /dev/pts >/dev/null 2>&1
mount -f /proc/bus/usb >/dev/null 2>&1
各ディレクトリをマウントしているのだが
既にマウント済のディレクトリまでマウントしているので
エラーが出るはず。

 mountコマンドのオプション「-f」に注目してみた。

man mountの出力結果
-f     実際のシステムコール以外を除いてすべての動作をする。もうちょっとわかりやすく言うと、ファイ
       ルシステムのマウント動作を「行うふり」をする。このオプションは -v フラグとともに用いると便
       利 で、 mount コマンドが行おうとすることを確認できる。また以前に -n オプションを用いてマウ
       ントされたデバイスのエントリーを /etc/mtab に書き込む目的にも用いることができる。

 mountコマンドの「-f」オプションは、マウント動作を「行うふり」なので
既にマウント済みのデバイスがあっても支障がない。

 /etc/mtabファイルへマウント情報を書き込むための
便宜的な処理だというのが、わかった。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Mount all other filesystems (except for NFS and /proc, which is already
# mounted). Contrary to standard usage,
# filesystems are NOT unmounted in single user mode.
action $"Mounting local filesystems: " mount -a -t nonfs,nfs4,smbfs,ncpfs,cifs,gfs -O no_netdev
action関数を使っているため、実行される部分は
赤い部分になる。
既にマウント済のデバイスであってもエラーが出ない。

赤い部分のmountコマンドのオプションをじっくり見てみる。
「-O no_netdev」は/etc/fstabファイルに登録されている物を
全てマウントするためのオプションだ。
他は、それ以外のファイルシステムのマウントだ。

可能な限り、マウントをしていくのだ。
そして/etc/mtabへマウントの状況を書き込んでいくのだ。

 ここまで来て、ようやく処理内容がわかってきた。

 /etc/mtabファイルに記述される内容を正確にするため
一度、消去した後、現在、可能なだけのマウントを行い
その際に/etc/mtabファイルにマウント情報を記録するのだ。

 ここまで手のこんだ処理をしているとは

 初めて/etc/mtabファイルが存在を知った部分だった。

 次へ進む。  quotaに関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ -x /sbin/quotaon ]; then
    action $"Enabling local filesystem quotas: " /sbin/quotaon -aug
fi

 でも、私はquotaを使わないので飛ばします。


 次へ進む。  SELinuxに関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Check to see if a full relabel is needed
if [ -n "$SELINUX_STATE" -a "$READONLY" != "yes" ]; then
    if [ -f /.autorelabel ] || strstr "$cmdline" autorelabel ; then
        relabel_selinux
    fi
else
    if [ -d /etc/selinux -a "$READONLY" != "yes" ]; then
        [ -f /.autorelabel ] || touch /.autorelabel
    fi
fi

 私はSELinuxを使わないので、読み飛ばします (^^)


 次へ進む。  GUIでブートする画面を起動させるための処理。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Start the graphical boot, if necessary and not done yet.
if strstr "$cmdline" rhgb && ! strstr "$cmdline" early-login && [ "$RHGB_STARTED" -eq 0 -a "$BOOTUP" = "color" -a "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb ]; then
   ( . /etc/sysconfig/i18n 2>/dev/null ; /usr/bin/rhgb )
   RHGB_STARTED=1
fi
もし、この手前の段階でテキストモードでブートしていた場合
ここでテキストモードでブート続行か、それともGUIでブートなのかを判断させる。


 次へ進む。  乱数に関する設定だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Initialize pseudo-random number generator
if [ -f "/var/lib/random-seed" ]; then
        cat /var/lib/random-seed > /dev/urandom
else
        [ "$READONLY" != "yes" ] && touch /var/lib/random-seed
fi
if [ "$READONLY" != "yes" ]; then
        chmod 600 /var/lib/random-seed
        dd if=/dev/urandom of=/var/lib/random-seed count=1 bs=512 2>/dev/null
fi


# Use the hardware RNG to seed the entropy pool, if available
#[ -x /sbin/rngd -a -c /dev/hw_random ] && rngd

if [ -f /etc/crypttab ]; then
    s=$"Starting disk encryption using the RNG:"
    echo "$s"
    init_crypto 1 && success "$s" || failure "$s"
    echo
fi

 乱数の初期化や暗号化に関する部分らしいが・・・

 見てもわからへん (TT)

 背伸びしても倒れるだけなので、ここは飛ばします (^^)

 次へ進む。  画面やキーボードの設定に不具合(?)や未設定の場合に起動する部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Configure machine if necessary.
if [ -f /.unconfigured ]; then
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
        chvt 1
    fi

    if [ -x /usr/bin/system-config-keyboard ]; then
        /usr/bin/system-config-keyboard
    fi
    if [ -x /usr/bin/passwd ]; then
        /usr/bin/passwd root
    fi
    if [ -x /usr/sbin/netconfig ]; then
        /usr/sbin/netconfig
    fi
    if [ -x /usr/sbin/timeconfig ]; then
        /usr/sbin/timeconfig
    fi
    if [ -x /usr/sbin/authconfig ]; then
        /usr/sbin/authconfig --nostart
    fi
    if [ -x /usr/sbin/ntsysv ]; then
        /usr/sbin/ntsysv --level 35
    fi

    # Reread in network configuration data.
    if [ -f /etc/sysconfig/network ]; then
        . /etc/sysconfig/network

        # Reset the hostname.
        action $"Resetting hostname ${HOSTNAME}: " hostname ${HOSTNAME}
    fi

    rm -f /.unconfigured

    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
        chvt 8
    fi
fi
ルートディレクトリに「.unconfigured」ファイルがあれば
この処理が動き出すという。(赤い部分)

 これだけ見ても・・・

 どんな働きをするねん?

 なのだ。

 実際に、自分の目で処理内容を見て、どんな処理なのかを
理解していった方が早いと思った。

 そこでルートディレクトリに「.unconfigured」ファイルを置き
再起動をかけてみた。
 最初は普通にブートする画面だった。

ブートの画面が立ち上がる
通常通りの画面だ。
このまま進めば問題ないのだが・・・

 しかし、いきなり以下の画面に変わった。

テキストでの起動画面になった
ピンクで囲んだ部分は「GUIでの起動は失敗」と表示されている。
茶色の部分は「代わりに、テキストでの起動が開始」と表示。

 そして言語の設定画面が出てくる。

言語の設定画面

 言語の設定が終わると、管理者であるrootのパスワードの
設定が行われる。

rootのパスワードの設定画面

 次に、時刻設定を、どの地域の時間にするのかの設定だ。

時刻の地域を選択する画面

 次に、Linuxの起動時に自動的に立ち上がるデーモンの選択画面だ。

Linuxの起動時に自動的に立ち上がるデーモンの設定

 これが終わると、何事もなかったの如く、GUIでの起動画面の
続きが走り出す。

GUIでの起動画面に戻る

 そして普通にログイン画面が出てくる。
 ここまでの流れを見てわかった事は

 画面やキーボードの再設定を行うための処理

 だった。

 そこでスクリプトを、もう一度、見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Configure machine if necessary.
if [ -f /.unconfigured ]; then
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
        chvt 1
    fi

(途中、省略)

fi
赤い部分は、GUIでの起動画面になっているかどうかの判定で
GUIの画面なら真となる。

青い部分は、GUI画面で起動している場合
「chvt 1」というコマンドが動くという。

 chvtコマンドって何やねん?

 調べるよりも、自分の目で見た方が早いと思ったので、
早速、chvtコマンドを使ってみる事にした。

chtvコマンドを使う前
chtvコマンドを使った後
「chvt 1」コマンドを実行させた事で、GUI画面から
テキスト画面に変わった。

 そこでchvtコマンドを調べてみる。
 すると仮想ターミナルの変更コマンドだという。

man chvtの結果
NAME
       chvt - change foreground virtual terminal

SYNOPSIS
       chvt N

DESCRIPTION
       The  command chvt N makes /dev/ttyN the foreground terminal.  (The cor-
       responding screen is created if it did not exist yet.  To  get  rid  of
       unused  VTs,  use deallocvt(1).)  The key combination (Ctrl-)LeftAlt-FN
       (with N in the range 1-12) usually has a similar effect.

                                26 January 1997                        CHVT(1)
RedHat7.3の実験の時、CTRL+Alt+F1を押したら
tty1の画面が出てきた。もちろんCentOS51でも同じ。

そして「chvt 1」を実行させると同じ事が起こる。
単にキーを押すのが大変な上、スクリプト上の処理では
キー操作ができないため、コマンドがあるのだと思う。

ちなみに、元のGUI画面に戻すには「chvt 8」を
実行させれば良い。

 RedHat7.3のスクリプトを解読(途中で頓挫したけど・・・)で
身につけた知識と照らし合わせる事ができた (^^)


 次を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Configure machine if necessary.
if [ -f /.unconfigured ]; then
    if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
        chvt 1
    fi

    if [ -x /usr/bin/system-config-keyboard ]; then
        /usr/bin/system-config-keyboard
    fi
    if [ -x /usr/bin/passwd ]; then
        /usr/bin/passwd root
    fi

(途中、省略)

fi
赤い部分は「/usr/bin/system-config-keyboard」の
ファイルが存在し、実行可能ならば真となる。

青い部分は、真の場合に実行される。
要するに「/usr/bin/system-config-keyboard」は
コマンドなのだ。

 /usr/bin/system-config-keyboardコマンドを実行してみる。

/usr/bin/system-config-keyboardコマンドを実行
GUI画面において
[suga@linux]$ /usr/bin/system-config-keyboard 
Loading /lib/kbd/keymaps/i386/qwerty/jp106.map.gz
[suga@linux]$
/usr/bin/system-config-keyboardコマンドを実行
テキスト画面において
キーボードの設定画面が出てきた。

 GUIとテキストでは違った画面が出てくるのだが
それでもキーボードの設定画面に変わりはない。

 この後のスクリプトの部分は、rootのパスワードの変更や
ネットワークの変更画面になっていく。

 しかし、一つ疑問が生まれた。

 /.unconfiguredファイルがある場合って・・・

 どんな時やねん?

 これがわからないままだ。
 でも、忍法「わからないので先送りにする術」で逃げよう (^^)

 次へ進む。  ファイルシステムの点検の際に使ったファイルの後始末を行う部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean out /.
rm -f /fastboot /fsckoptions /forcefsck /.autofsck /forcequotacheck /halt \
        /poweroff &> /dev/null
ファイルシステムの点検に使ったファイルを、
一旦、ここで全て消去する処理だ。



 次へ進む。  wtmpxファイルと、utmpxファイルに関する部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Do we need (w|u)tmpx files? We don't set them up, but the sysadmin might...
_NEED_XFILES=
[ -f /var/run/utmpx -o -f /var/log/wtmpx ] && _NEED_XFILES=1

 その前に

 wtmpx、utmpxってどんなファイルやねん!

 調べてみると、HP-UXで使われるログファイルだ。
 Linuxでは使われる事はなさそうだ。ここで、謎が生まれた。

 なんでLinuxの起動ファイルに記述されてるねん!

 うーん、謎だ。
 でも、私はutmpxやwtpmxを知らなくても支障は出ない。
 なので、私は覚える必要はないので飛ばします (^^)

 あとでわかった話、wtmpxは現在ログインしている
ユーザー情報を格納したファイル。
 utmpxはログイン履歴を格納したファイルなのだ。

 Linuxで使われるユーザー情報とログイン履歴については
この後のスクリプトの記述に登場しますので、
そこで取り上げたいと思います。

 次へ進む。  /var/runディレクトリと/var/lockディレクトリ上にある ファイルやディレクトリを削除する処理だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean up /var.  I'd use find, but /usr may not be mounted.
for afile in /var/lock/* /var/run/* ; do
        if [ -d "$afile" ]; then
           case "$afile" in
                */news|*/mon)   ;;
                */sudo)         rm -f $afile/*/* ;;
                */vmware)       rm -rf $afile/*/* ;;
                */samba)        rm -rf $afile/*/* ;;
                */screen)       rm -rf $afile/* ;;
                */cvs)          rm -rf $afile/* ;;
                */dovecot)      rm -f $afile/*/* ;;
                */cups)         rm -f $afile/*/* ;;
                *)              rm -f $afile/* ;;
           esac
        else
           rm -f $afile
        fi
done
rm -f /var/lib/rpm/__db* &> /dev/null
rm -f /var/gdm/.gdmfifo &> /dev/null
/var/runディレクトリ、/var/lockディレクトリの中の
ファイルやディレクトリ名で「sudo」、「vmware」
「samba」と名前を付くものを削除する処理だ。

 削除するための処理だというのはわかっても、
何のための処理なのかが、全くわからない。

 首を傾げながら、ふと思った。
 そもそも/var/runディレクトリと、/var/lockのディレクトリは、
どういったファイル等が格納されるのだろうか?

 もっと根本的な事を考えると

 /varディレクトリって何やねん?

 そこで調べてみる事にした。
 その結果、以下の事だという。

/varディレクトリの役目
システムが提供する各種サービスのログや
メールやプリンタの一時ファイルを保管するディレクトリ

 わかったような、わからんような内容だ・・・。
 もっと調べてみたら、以下の結果だった。

/varディレクトリの役目
常に変化していくシステムファイル、システム関連ファイルを
置くためのディレクトリ
varは「variable」(変化していく)の略だという。

ログファイルを/varディレクトリに置いているのは
ログが常に最新の情報を追加したり、古い情報を削除していて
中身が変化している事から、/varディレクトリに置かれている。

ログ以外でも、常に変化するシステムの情報が格納されたファイルを
保管する場所にも使われる。

 これを見ると、なんとなく、わかってきた感じだ。

 次のサイトを発見した。
 Linuxのディレクトリ構造の一覧

 /var/runや、/var/lockディレクトリの中に置くファイルの
解説が載っている。

/varディレクトリの役目について
/var/run
システムが起動してからのシステムデータ格納のための
ディレクトリ
/var/lock
2重起動の確認のためのロックファイルが格納されている
ディレクトリ

要するに、システム起動に必要なプログラムが
重複して起動しないように、起動を知らせる目印になる
ファイルを格納するためのディレクトリだ。

 実際に、個々のディレクトリを見てみた。

/var/runディレクトリ
[suga@linux]$ pwd
/var/run
[suga@linux]$ ls -l
合計 300
drwxr-xr-x 2 root  root   4096  5月 25  2008 NetworkManager
srw-rw-rw- 1 root  root      0  2月  3 11:16 acpid.socket
-rw-r--r-- 1 root  root      5  2月  3 11:16 atd.pid
srw-r----- 1 root  root      0  2月  3 11:16 audispd_events

(途中、省略)

drwxr-xr-x 2 root  root   4096  5月 26  2008 winbindd
drwxr-xr-x 2 root  root   4096  5月 25  2008 wpa_supplicant
-rw-r--r-- 1 root  root      6  2月  3 11:16 xfs.pid
-rw-r--r-- 1 root  root      5  2月  3 11:16 xinetd.pid
[suga@linux]$ 
何種類かのファイルがあるが、目立つのは拡張子が「pid」のファイルだ。
このファイルは、そのデーモンのPID(プロセスID)の値が格納されている。
プロセスIDがあるだけでも、そのデーモンに情報が得られるので
現在、起動中のデーモンのプロセスIDを記録していると思ったりする。
/var/lockディレクトリ
[suga@linux]$ pwd
/var/lock
[suga@linux]$ ls -l
合計 24
drwxr-xr-x 2 root root 4096  5月 25  2008 dmraid
-rw-r--r-- 1 root root    0  2月  3 11:16 irqbalance
drwx------ 2 root root 4096  1月  7 19:27 lvm
drwxr-xr-x 2 root root 4096  2月  3 12:22 subsys
[suga@linux]$ cd subsys/
[suga@linux]$ ls -l
合計 0
-rw-r--r-- 1 root root 0  2月  3 11:16 acpid
-rw-r--r-- 1 root root 0  2月  3 11:16 atd
-rw-r--r-- 1 root root 0  2月  3 11:16 auditd
-rw-r--r-- 1 root root 0  2月  3 11:16 autofs

(途中、省略)

-rw------- 1 root root 0  2月  3 11:16 syslog
-rw-r--r-- 1 root root 0  2月  3 11:16 vmware
-rw-r--r-- 1 root root 0  2月  3 11:16 xfs
-rw-r--r-- 1 root root 0  2月  3 11:16 xinetd
-rw-r--r-- 1 root root 0  2月  3 11:16 yum-updatesd
[suga@linux]$ 
ファイルの大きさを見ると空ファイルばかりだ。
おそらくデーモンが起動中かどうかの確認だけなので
空ファイルを作成して、それを目印にしていると思う。

 もう1度、スクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean up /var.  I'd use find, but /usr may not be mounted.
for afile in /var/lock/* /var/run/* ; do
        if [ -d "$afile" ]; then
           case "$afile" in
                */news|*/mon)   ;;
                */sudo)         rm -f $afile/*/* ;;
                */vmware)       rm -rf $afile/*/* ;;
                */samba)        rm -rf $afile/*/* ;;
                */screen)       rm -rf $afile/* ;;
                */cvs)          rm -rf $afile/* ;;
                */dovecot)      rm -f $afile/*/* ;;
                */cups)         rm -f $afile/*/* ;;
                *)              rm -f $afile/* ;;
           esac
        else
           rm -f $afile
        fi
done
rm -f /var/lib/rpm/__db* &> /dev/null
rm -f /var/gdm/.gdmfifo &> /dev/null
/var/run、/var/lockディレクトリに格納される
ファイルの意味合いがわかると、この処理の意味が見えてくる。

起動もしていないデーモンに関するファイルが存在するのは
おかしい上、該当のデーモンが起動しなくなる。

Linuxが稼働中に、いきなり電源が切れたりした場合、
これらのファイルが消去されずに残ったままになる。
他にも、デーモンが終了しても、2重起動の目印のファイルが
消去されずに残る場合も考えられる。

そのため、起動時に、円滑にデーモンを立ち上げるために
このディレクトリにファイルがあるのは残骸だと見なして、
消去を行うための処理なのだ。

 なかなか手のこんだ処理やん!

 でも、このおかげで、悪さをする残骸ファイル(?)ために
デーモンが立ち上がらず。利用者が考え込む問題を回避してくれる。


 次へ進む。  デバイスへの接続権限に関する設定の部分だ。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Reset pam_console permissions
[ -x /sbin/pam_console_apply ] && /sbin/pam_console_apply -r

 そもそもpam_console_applyコマンドは何をする物か
調べてみた。
 すると、デバイスへの接続権限に関する初期設定ファイル
(/etc/security/console.perms.d/50-default.perms)
を変更した際に、その内容を反映させるためのコマンドだという。

 少し、/etc/security/console.perms.d/50-default.permsファイルの
中身を見てみる事にした。

50-default.permsファイルの中身を一部抜粋 (CentOS5.1の場合)
# device classes -- these are shell-style globs
<floppy>=/dev/fd[0-1]* \
         /dev/floppy* /mnt/floppy*
<sound>=/dev/dsp* /dev/audio* /dev/midi* \
        /dev/mixer* /dev/sequencer* \
        /dev/sound/* /dev/beep \
        /dev/snd/* /dev/adsp*
<cdrom>=/dev/cdrom* /dev/cdroms/* /dev/cdwriter* /mnt/cdrom*
<pilot>=/dev/pilot

(途中、省略)

# permission definitions
<console>  0660 <floppy>     0660 root.floppy
<console>  0600 <sound>      0600 root
<console>  0600 <cdrom>      0660 root.disk
<console>  0600 <pilot>      0660 root.uucp

(途中、省略)

各デバイスへの接続に関する権限が記述されている。
フロッピーの場合、所有者と所有グループが接続権限があり
所有者はrootで、グループはfloppyのようだ。

各デバイスごとに、どのユーザー、どのグループが
接続権限を持たせるか、細かく設定されている。

 だが、このファイルは安易に書き換えるのは良くないようだ。
 もし、記述の内容を変更したい場合は、別の記述ファイルを作成するのが

  コンソールからファイルにアクセスできるようにする方法

 ここでの処理は、どうやら各種デバイスへの接続の権限を
初期設定の記述通りに再設定するための感じだ。

 次へ進む。  こんな書式の部分と出会う。
複合コマンド
{
コマンド1
コマンド2
  ・
  ・
  ・
コマンドn
}
nは任意の数。
{}の括弧の中に、複数のコマンドを入れる。
{}を使う事で、あたかも1つのコマンドであるように見せている。
そのため「複合コマンド」と呼ぶ。

 まずは複合コマンドの全体を表示してみた。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
{
# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
  > /var/run/utmpx
  touch /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi

# Clean up various /tmp bits
[ -n "$SELINUX_STATE" ] && restorecon /tmp
rm -f /tmp/.X*-lock /tmp/.lock.* /tmp/.gdm_socket /tmp/.s.PGSQL.*
rm -rf /tmp/.X*-unix /tmp/.ICE-unix /tmp/.font-unix /tmp/hsperfdata_* \
       /tmp/kde-* /tmp/ksocket-* /tmp/mc-* /tmp/mcop-* /tmp/orbit-*  \
       /tmp/scrollkeeper-*  /tmp/ssh-* \
       /dev/.in_sysinit

# Make ICE directory
mkdir -m 1777 -p /tmp/.ICE-unix >/dev/null 2>&1
chown root:root /tmp/.ICE-unix
[ -n "$SELINUX_STATE" ] && restorecon /tmp/.ICE-unix >/dev/null 2>&1

#if [ -x /etc/init.d/diskdump ]; then
#       /etc/init.d/diskdump swapsavecore
#fi

# Start up swapping.
update_boot_stage RCswap
action $"Enabling /etc/fstab swaps: " swapon -a -e
if [ "$AUTOSWAP" = "yes" ]; then
        curswap=$(awk '/^\/dev/ { print $1 }' /proc/swaps | while read x; do get_numeric_dev dec $x ;
echo -n " "; done)
        swappartitions=`blkid -t TYPE=swap -o device`
        if [ x"$swappartitions" != x ]; then
                for partition in $swappartitions ; do
                        [ ! -e $partition ] && continue
                        majmin=$(get_numeric_dev dec $partition)
                        echo $curswap | grep -qw "$majmin" || action $"Enabling local swap partitions: " swapon $partition
                done
        fi
fi

# Set up binfmt_misc
/bin/mount -t binfmt_misc none /proc/sys/fs/binfmt_misc > /dev/null 2>&1
# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
  > /var/run/utmpx
  touch /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi
# Initialize the serial ports.
if [ -f /etc/rc.serial ]; then
        . /etc/rc.serial
fi

# If they asked for ide-scsi, load it
if strstr "$cmdline" ide-scsi ; then
        modprobe ide-cd >/dev/null 2>&1
        modprobe ide-scsi >/dev/null 2>&1
fi

# Boot time profiles. Yes, this should be somewhere else.
if [ -x /usr/sbin/system-config-network-cmd ]; then
  if strstr "$cmdline" netprofile= ; then
    for arg in $cmdline ; do
        if [ "${arg##netprofile=}" != "${arg}" ]; then
            /usr/sbin/system-config-network-cmd --profile ${arg##netprofile=}
        fi
    done
  fi
fi

# Now that we have all of our basic modules loaded and the kernel going,
# let's dump the syslog ring somewhere so we can find it later
dmesg -s 131072 > /var/log/dmesg

# create the crash indicator flag to warn on crashes, offer fsck with timeout
touch /.autofsck &> /dev/null

if [ "$PROMPT" != no ]; then
    while :; do
        pid=$(/sbin/pidof getkey)
        [ -n "$pid" -o -e /var/run/getkey_done ] && break
        usleep 100000
    done# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
  > /var/run/utmpx
  touch /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi
    [ -n "$pid" ] && kill -TERM "$pid" >/dev/null 2>&1
fi
} &
複合コマンドの最後に「&」があるので
バックグラウンドでコマンドを動かすという意味になる。
つまり、この複合コマンドはバックグラウンドで動かすようになっている。

 後でわかった話だが、この複合コマンドは
バックグラウンドで走らせるため、rc.sysinitのプロセスの
子プロセスとなる。

複合コマンドは子プロセスになる
親プロセスのrc.sysinitから複合コマンドの部分が
分裂した感じになる。

ここでは気がつかなかったが、あとになる
親子関係に気づかないと、理解できない記述箇所があるのだ。

 実際、スクリプトの中で複合コマンドをバックグランドで
動かした場合、子プロセスになるのかを実験してみた。

実験スクリプト(parent.sh)
#!/bin/bash

{
/bin/ps axjf
} &

wait
echo "End"
実験結果
[suga@linux]$ ./parents.sh 
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0     1     1     1 ?           -1 Ss       0   0:00 init [5]                                            
    1     2     1     1 ?           -1 S        0   0:00 [migration/0]

(途中、省略)

 2706  3307  3307  3307 pts/3     3334 Ss     500   0:00  \_ bash
 3307  3334  3334  3307 pts/3     3334 S+     500   0:00      \_ /bin/bash ./parents.sh
 3334  3335  3334  3307 pts/3     3334 S+     500   0:00          \_ /bin/bash ./parents.sh
 3335  3336  3334  3307 pts/3     3334 R+     500   0:00              \_ /bin/ps axjf
複合プロセスをバックグラウンドで動かせば
子プロセスになるのは、結果を見れば一目瞭然だ。

 うーん、今までシェルのプログラムで、親子関係を
考えた事がなかったので勉強になった (^^)


 さて、この複合コマンドは、あまりにも長いため

 分解していかんと、わからへん!!

 なのだ。
 そこで、長い複合コマンドを分解して、見ていく事にした。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
{
# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
  > /var/run/utmpx
  touch /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi
赤い部分は、utmpファイルを空にする設定。

青い部分は、wtmpファイルの修正時刻を
touchコマンドを実行した時刻にする。
だが、この時、touchコマンドは空ファイルを作成するコマンドと
思い込んでいた。

 ここで思った。

 utmp、wtmpファイルって何やねん!

 wtmpファイルから調べてみる。
 するとログに関する設定ファイルで、wtmpに関する記述があった。
 /etc/logrotate.confファイルだった

/etc/logrotate.conf (CentOS5.1の場合)
# see "man logrotate" for details
# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# uncomment this if you want your log files compressed
#compress

# RPM packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp -- we'll rotate them here
/var/log/wtmp {
    monthly
    create 0664 root utmp
    rotate 1
}

# system-specific logs may be also be configured here.
ログ関連の設定ファイルにwtmpに関する記述があった。
今まで、ログ関連のファイルを触った事がなかったので
一体、どういう関連があるのか知りたくなった。

 wtmpファイルは、/var/logディレクトリにあるようだ。
 実際に見てみる。

/var/logディレクトリの中
[suga@linux]$ ls -l
合計 2520
-rw-r--r-- 1 root root   31237  1月  1 11:56 Xorg.0.log
-rw-r--r-- 1 root root   31237  1月  1 11:52 Xorg.0.log.old
-rw-r----- 1 root root    5710  1月  1 11:56 acpid

(途中、省略)

-rw------- 1 root root       0  8月 14 18:27 tallylog
-rw-rw-r-- 1 root utmp   16896  1月 31 09:49 wtmp
-rw-rw-r-- 1 root utmp   83712  1月  1 11:56 wtmp.1
-rw------- 1 root root    1203  1月 30 17:06 xferlog
-rw-r--r-- 1 root root       0  8月 14 18:42 yum.log
[suga@linux]$ 

 wtmpファイルがどういうファイルなのかを調べていくと

 ログインの履歴を記述したファイル

 だという事がわかった。

 でも、そのままではバイナリデータなので見る事はできない。
 そこでwtpmファイルの中身はどんな物かと調べる。
 lastコマンドを使えば、wtpmファイルに記録されている
ログインの履歴が見れるというのだ。

lastコマンドを使ってみる
[suga@linux]$ last
suga     pts/0        192.168.X.YYY    Sat Jan 31 09:49   still logged in   
suga     pts/1        192.168.X.YYY    Fri Jan 30 17:06 - 17:08  (00:01)    
suga     pts/0        192.168.X.YYY    Fri Jan 30 09:03 - 17:35  (08:32)    
suga     pts/0        192.168.X.YYY    Thu Jan 29 10:15 - 18:12  (07:56)    
suga     pts/0        192.168.X.YYY    Wed Jan 28 10:16 - 18:26  (08:10)    
suga     pts/1        192.168.X.YYY    Tue Jan 27 09:47 - 18:29  (08:42)    
suga     pts/0        192.168.X.YYY    Tue Jan 27 09:05 - 18:29  (09:24)    
suga     pts/0        192.168.X.YYY    Mon Jan 26 17:24 - 18:27  (01:03)    
suga     pts/0        192.168.X.YYY    Thu Jan 22 14:06 - 18:19  (04:12)    
suga     pts/0        192.168.X.YYY    Wed Jan 21 09:01 - 18:23  (09:21)    
suga     pts/0        192.168.X.YYY    Tue Jan 20 10:54 - 18:07  (07:12)    
suga     pts/0        192.168.X.YYY    Mon Jan 19 09:25 - 18:29  (09:04)    
suga     pts/2        192.168.X.YYY    Thu Jan 15 10:57 - 17:56 (2+06:59)   
suga     pts/1        192.168.X.YYY    Thu Jan 15 10:08 - 17:56 (2+07:48)   
suga     pts/1        192.168.X.YYY    Thu Jan 15 10:05 - 10:08  (00:02)    
suga     pts/0        192.168.X.YYY    Wed Jan 14 10:58 - 17:57 (3+06:58)   
suga     pts/0        192.168.X.YYY    Wed Jan  7 13:11 - 18:01  (04:49)    
root     tty1                          Mon Jan  5 10:16   still logged in   

wtmp begins Fri Jan  2 16:49:30 2009
[suga@linux]$ 
注意深く見ると赤い部分の「wtmp」の文字がある。
今までlastコマンドは嫌ほど使ってきたのだが、
ここで初めてwtmpファイルに記録されているデータを
表示させているのを知った。

 過去のログイン履歴を見る事ができる。

 ふと思った。
 この時、私はtouchコマンドは、空ファイルを作成するコマンドと
思っていた。
 だが、touchコマンドが空ファイル作成のコマンドだと
Linuxを起動する事に、ログインの履歴ファイルが空になるのだが
過去の履歴がしっかり残っている。

 一体、何でやねん?

 そこでtouchコマンドを調べてみた。
 すると、思いもよらなかった事がわかった。

touchコマンドの役目
touchコマンドは、ファイルの修正時刻を変更するコマンドだという。
もし、「touch ファイル名」を行えば、touchコマンドを実行した時刻が、
ファイルの修正時刻になる。

 実際に、touchコマンドを使う前と、使った後のファイルの
修正時刻を比較してみた。

touchコマンドを使う前
[suga@linux]$ ls -l
合計 4
-rw-rw-r-- 1 suga suga 58  1月 31 17:04 testfile
[suga@linux]$ 
実験用のファイルを作成した。 赤い部分がファイルの修正時刻になる。17:04を指している。 そこで、しばらく時間をおいてみる。
touchコマンドを使った後
[suga@linux]$ date
2009年  1月 31日 土曜日 17:19:25 JST
[suga@linux]$ touch testfile 
[suga@linux]$ ls -l
合計 4
-rw-rw-r-- 1 suga suga 58  1月 31 17:19 testfile
[suga@linus]$ 
touchコマンドを使う直前の時刻を見る。17:19を指している(赤い部分)
そしてtouchコマンドを使う(青色の部分)。
ファイルの修正時刻を見ると19:04から19:19に変わっている(ピンク)

この事から、touchコマンドはファイルの修正時刻を、
touchコマンドを使った時刻に変更されるのがわかる。

もし、touchコマンドを使う時に、指定したファイルが存在しない場合は
空ファイルが作成されるため、空ファイルの作成を行う時にも使われる。

 この時はじめて

 touchコマンドの意味がわかった!!

 のだった。

 次に、utmpファイルが何かを調べてみる。
 すると、現在、ログインしているユーザー名の情報を保持するファイルだ。

wコマンドを使ってみる
[suga@linux]$ w
 17:27:56 up 30 days,  5:46,  2 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
root     tty1     -                05Jan09 26days  0.01s  0.01s -bash
suga     pts/0    192.168.X.YYY    09:49    0.00s  0.05s  0.00s w
[suga@linux]$
管理者権限のrootが長時間ログインしている。
私(suga)がログインしたので、それも載っている。
このようにログインしているユーザーを見る事ができる上、
簡単な利用状況も見る事ができる。

 さて、もう1度、先ほどまで見た処理のスクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
  > /var/run/utmpx
  touch /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi
赤い部分はutmpファイルを空にしている。
このファイルは現在ログインしているユーザー情報なので
起動時にログインしているはずがない。
そのため残骸があった場合は、空にしているのだ。

青い部分はログイン履歴のファイルだ。
単に修正時間を起動時に変更しているだけだ。

 正常にシステムを動かすための仕掛けがわかる。
 奥が深いなぁ・・・。


 続きを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean up utmp/wtmp
> /var/run/utmp
touch /var/log/wtmp
chgrp utmp /var/run/utmp /var/log/wtmp
chmod 0664 /var/run/utmp /var/log/wtmp
if [ -n "$_NEED_XFILES" ]; then
  > /var/run/utmpx
  touch /var/log/wtmpx
  chgrp utmp /var/run/utmpx /var/log/wtmpx
  chmod 0664 /var/run/utmpx /var/log/wtmpx
fi
赤い部分は条件文だが、ここの条件を満たす場合は
Linuxでは考えにくい。

wtmpx、utmpxのファイルはLinux系では使わない。
となれば、なぜ、これらのファイルの処理の過程が存在するのか
全くわからない。

 わからない上、使う事はないので、謎のままにしておこう (^^)


 次へ進む。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Clean up various /tmp bits
[ -n "$SELINUX_STATE" ] && restorecon /tmp
rm -f /tmp/.X*-lock /tmp/.lock.* /tmp/.gdm_socket /tmp/.s.PGSQL.*
rm -rf /tmp/.X*-unix /tmp/.ICE-unix /tmp/.font-unix /tmp/hsperfdata_* \
       /tmp/kde-* /tmp/ksocket-* /tmp/mc-* /tmp/mcop-* /tmp/orbit-*  \
       /tmp/scrollkeeper-*  /tmp/ssh-* \
       /dev/.in_sysinit
赤い部分はSELinuxに関係する部分なので飛ばす。

それ以降は、/tmpディレクトリ内にあるファイルやディレクトリの
削除を行っている。

/tmpディレクトリはアプリケーションが起動中に作成する
一時ファイルを保管する場所だ。
なので、Linux起動時に/tmpディレクトリにアプリケーションの類の
ファイルがあると、誤作動の原因になりかねないため
削除するように設定されていると思う。

 いかにも/tmpディレクトリの事がわかった上で
上の説明を書いているように思われるが、実は・・・

 /varとの違いを調べた時に知ったのだ!

 ここで知識の整理のため、/tmpと/varの違いを比較してみた。

/tmpと/varとの違い
/tmp
/tmpディレクトリははアプリケーションが使う
一時的なファイルを保管する場所。
あくまでも一時的なファイルのため、再起動した場合は
このディレクトリのファイルで、一部の有名なアプリの
一時ファイルは綺麗に削除される。
/var
/varディレクトリは、システムやシステムに関連した
ログなどのファイルを保管する。
このディレクトリに保管されるファイルは
「変化していく」ファイルのため、ログなどが置かれる。

あくまでも「変化していく」ファイルであって
一時的なファイルでないため、再起動しても
必要なファイルは保管される。

 2つの違いについての整理ができた。
 今までは何気なく使っていたディレクトリなのだが、
実は、重要な意味合いがあったのは初めて知った。
 非常に勉強になる。


 次へ進む。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Make ICE directory
mkdir -m 1777 -p /tmp/.ICE-unix >/dev/null 2>&1
chown root:root /tmp/.ICE-unix
[ -n "$SELINUX_STATE" ] && restorecon /tmp/.ICE-unix >/dev/null 2>&1
ICEに関するディレクトリを作成する部分だ。
/tmpディレクトリなので一時的なファイルの保管だ。

 これを見て・・・

 ICEって、一体、何やねん?

 そこで調べてみる事にした。
 
 ICEとは、マイコン基板を開発する際に使うデバッガだという。
 この瞬間、調べる事をやめた。なぜなら・・・

 マイコンの開発なんてせぇへんもーん (^^)

 組み込み系の制御ができるような技術や知識があれば、
今頃、私は引っ張りだこなのだ。でも、そんな実力は私にはない。

 ところで、ICEはマイコン基板開発のためのデバッガとは限らない。

 HP社のInside Control Environment for Linux (ICE-Linux)がある。
略して「ICE」と使う事もあるみたいだ。


 次へ進む。
 スワップに関する設定だ。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Start up swapping.
update_boot_stage RCswap
action $"Enabling /etc/fstab swaps: " swapon -a -e
if [ "$AUTOSWAP" = "yes" ]; then
        curswap=$(awk '/^\/dev/ { print $1 }' /proc/swaps | while read x; do get_numeric_dev dec $x ;
echo -n " "; done)
        swappartitions=`blkid -t TYPE=swap -o device`
        if [ x"$swappartitions" != x ]; then
                for partition in $swappartitions ; do
                        [ ! -e $partition ] && continue
                        majmin=$(get_numeric_dev dec $partition)
                        echo $curswap | grep -qw "$majmin" || action $"Enabling local swap partitions: " swapon $partition
                done
        fi
fi

 これを見た時は・・・

 これでは、わからへん!!

 分解して見ていく事にする

/etc/rc.d/rc.sysinit (CentOS5.1の場合)

action $"Enabling /etc/fstab swaps: " swapon -a -e
swaponコマンドを実行させる部分だ。
このコマンドを調べてみると、デバイスやファイルへの
ページングやスワップピングを有効にするという。

無効にするには「swapoff」コマンドを使う。

 ところで上のswaponのコマンドを使う際に
「-a」、「-e」のオプションが付けられている。
 どんなオプションなのか調べてみた。

swaponコマンドのオプションについて
-a
/etc/fstabの中で「swap」という印が付いている
スワップデバイスを全て有効にする。
-e
「-a」オプションが付いている場合で、
/etc/fstabに記述されているが、
実際には存在しないデバイスに出くわした時
エラーを出さずに飛ばしてくれる

 オプションの意味がわかった所で、もう一度、スクリプトを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)

action $"Enabling /etc/fstab swaps: " swapon -a -e
/etc/fstabに記述されているスワップデバイスを有効にする。
もし、該当のデバイスが存在しない場合は、そのまま無視する。

 ここで初めて気づいた。
 もしかして、スワップ領域を設けただけではスワップせず
有効にする処理をしなければダメなのか。
 そうかもしれない。

 そこで、もし、swaponコマンドを使わなかった場合を

swaponコマンドを無効にした実験
# Start up swapping.
update_boot_stage RCswap
#action $"Enabling /etc/fstab swaps: " swapon -a -e
if [ "$AUTOSWAP" = "yes" ]; then
        curswap=$(awk '/^\/dev/ { print $1 }' /proc/swaps | while read x; do get_numeric_dev dec $x ;
echo -n " "; done)
        swappartitions=`blkid -t TYPE=swap -o device`
        if [ x"$swappartitions" != x ]; then
                for partition in $swappartitions ; do
                        [ ! -e $partition ] && continue
                        majmin=$(get_numeric_dev dec $partition)
                        echo $curswap | grep -qw "$majmin" || action $"Enabling local swap partitions: " swapon $partition
                done
        fi
fi
swaponコマンドを実行させる部分の先頭に「#」をいれて
無効にしてみた。

 そしてシステムを再起動してみる。

起動時にswaponのコマンドを使わなかった場合
freeコマンドの実行結果(私の環境)
[suga@linux]$ free
             total       used       free     shared    buffers     cached
Mem:        409868     375956      33912          0      36004     185680
-/+ buffers/cache:     154272     255596
Swap:            0          0          0
[suga@linux]$ 
スワップメモリの領域が「0」になっている。
つまりスワップ領域が有効になっていない証拠だ。

 この事からスワップ領域を作成しても
swaponコマンドで有効にしておかないと
スワップ領域が活用されない事がわかる。

 これで賢くなったのらー!! (^^)


 ちなみに、スワップ領域を無効にするコマンドは
swapoffコマンドを使う。

 実際に、スワップ領域が無効になるのかどうか
自分の目で確かめてみた。
 というより確かめないと気が済まない性格なのらー (^^)

swapoff 使用前
[root@linux]# free
             total       used       free     shared    buffers     cached
Mem:        409868     376028      33840          0      36084     185512
-/+ buffers/cache:     154432     255436
Swap:       257032          0     257032
[root@linux]# 
赤い部分がスワップ領域の容量だ。
250Mバイトの仮想メモリとして使える状態だ。
swapoff 使用後
[root@linux]# swapoff -a
[root@linux]# free
             total       used       free     shared    buffers     cached
Mem:        409868     375956      33912          0      36004     185680
-/+ buffers/cache:     154272     255596
Swap:            0          0          0
[root@linux]#
そこでswapoffコマンドを使ってみる。青い部分。

すると、スワップ領域が0Mバイトになる。ピンクの部分。
仮想メモリが全く使えない事を意味する。

 論より証拠だ。
 ところで、物理メモリが足らない状態で、スワップ領域を
無効にした場合、どうなるのか、見てみる事にした。

 物理メモリを96Mバイトに設定してみた。
 VMwareを使うと簡単にできる (^^)

物理メモリを96Mバイトにして
スワップ領域を無効にしてみる
[root@linux]# swapoff -a
swapoff: /dev/sda3: メモリを確保できません
swapoff: LABEL=SWAP-sda3: メモリを確保できません
[root@linus]# 
メモリが確保できないため、スワップ領域を無効にできなかった。
今でこそ、大容量のメモリが安く手に入るため
スワップ領域がなくても問題なく使える場合が多くなったが
以前は、スワップ領域がないと、システムが動かなかったのだなぁと
思ったりする。

 swapon、swapoffのコマンドの役目を覚えた (^^)


 続きを見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ "$AUTOSWAP" = "yes" ]; then
        curswap=$(awk '/^\/dev/ { print $1 }' /proc/swaps | while read x; do get_numeric_dev dec $x ;
echo -n " "; done)
        swappartitions=`blkid -t TYPE=swap -o device`
        if [ x"$swappartitions" != x ]; then
                for partition in $swappartitions ; do
                        [ ! -e $partition ] && continue
                        majmin=$(get_numeric_dev dec $partition)
                        echo $curswap | grep -qw "$majmin" || action $"Enabling local swap partitions: " swapon $partition
                done
        fi
fi
赤い部分の変数AUTOSWAPだが、これは/etc/sysconfig/initファイルで
宣言されている。
初期設定では変数の値は「no」のため、この条件文の処理は行われない。

青い部分は、/proc/swapsファイルから行の文頭に「/dev」の
文字列を含む場合、1列目の文字列だけ拾い上げる。
デバイスを拾い上げる処理になる。

ピンクの部分はブロックデバイスの属性を見つけるコマンド
blkidを使っている。

 blkidって、どんなコマンドやねん!

 そこで実行してみる事にした。

blkidの実行結果(私の環境)
[suga@linux]$ /sbin/blkid
/dev/sda3: TYPE="swap" LABEL="SWAP-sda3" 
/dev/sda2: LABEL="/" UUID="87bbba08-24bd-4d57-9c90-0f5bdc5da40e" SEC_TYPE="ext2" TYPE="ext3" 
/dev/sda1: LABEL="/boot" UUID="92bbbb15-6595-4ffe-9022-6c93771301d9" TYPE="ext2"
/dev/loop0: LABEL="CDROM" TYPE="iso9660" 
[suga@linux]$ 
各種デバイスが出てきた
blkidは、使用中のデバイスの情報を出力するコマンドだと思った。

 もう少し調べてみると、blkidはあるファイルを元に
上のような結果を出力しているという。
 それは/etc/blkid/blkid.tabファイルなのだ。

/etc/blkid/blkid.tabファイル (私の環境)
<device DEVNO="0x0803" TIME="1233964518" TYPE="swap" LABEL="SWAP-sda3">/dev/sda3</device>
<device DEVNO="0x0802" TIME="1233964517" LABEL="/" UUID="87bbba08-24bd-4d57-9c90-0f5bdc5da40e" SEC_TYPE="ext2" TYPE="ext3">/dev/sda2</device>
<device DEVNO="0x0801" TIME="1233964524" LABEL="/boot" UUID="92bbbb15-6595-4ffe-9022-6c93771301d9" TYPE="ext2">/dev/sda1</device>
<device DEVNO="0x0700" TIME="1233040575" LABEL="CDROM" TYPE="iso9660">/dev/loop0</device>
各種デバイスの情報が、XML形式で保管されている。
blkidコマンドは、このファイルを元に出力しているようだ。

 ふと思った。
 blkid.tabファイルは、いつの段階で生成されるのか?
 そこで調べてみたのだが・・・

 調べても、わからへん (TT)

 読んでいても、私の頭が理解できないのだ。

 なので、ここは涙を飲んで前に進むのを断念したと書きたいが
突っ込まれるのは間違いなしなので、正直に書きます。
 単なる忍法「先送りの術」なのだ (^^)

 スワップ関係の話は、ここで終わります。


 次へ進む。

binfmt_miscに関する設定
# Set up binfmt_misc
/bin/mount -t binfmt_misc none /proc/sys/fs/binfmt_misc > /dev/null 2>&1

 これを見た時・・・

 binfmtって一体、何やねん!

 これがわからないと、何なのか、わからん。
 調べてみる事にした。

 Binary Formatの略だという。

 でも、これだけだとバイナリーデータの形式の略であって
どういう物なのかが、わからない。

 そこでスクリプトにあるように「binfmt_misc」
という語句で調べてみた。すると以下のサイトが見つかった。
 binfmt_misc.txt

 好みの実行ファイル形式で、バイナリファイルを
実行させるのに必要なもモジュールだという。


 実行ファイル形式でふと思い出した。
 カーネルが実行ファイルを起動させる際に、
実行ファイル形式の話が出てきた。
 そこで、久々に、分厚い本を出してきた。

 詳解Linuxカーネル(第2版)なのだ!!

 以前は、この本に負けないくらい、私の三段腹は分厚かったが
今では私がメタボだった事を信じてくれないぐらい痩せてしまい、
このネタが使えないのが残念・・・。

 763ページ辺りを見ていくと、Linux標準の実行ファイル形式に
関する記述がある。
 
Linux標準の実行ファイル形式
a.out
Assembler OUTput Formatの略。
古い型のUNIXの実行ファイルの形式だ。
C言語のプログラムをgcc でコンパイルした時、
何もオプションをつけないと、ファイル名が
a.outになっている。もしかして、このファイルも
a.out形式なのか?
ELF
Executable and Linking Formatの略。
UNIX System Laboraroriesで開発されて
UNIXの世界では広く使われている形式

 現在ではELF形式が主流だという。

 ところでbinfmtを調べてみると、以下のサイトを発見した。
 WATARU'S MEMO

 読んでみると、カーネルが実行ファイルを起動させる際の
主要処理にbinfmtモジュールが行っているという。

 ファイル形式をカーネルに認識してもらうために、
/proc/sys/fs/binfmt_misc/registerに登録するというのだ。

 私の環境で、どうなっているのか見てみる事にしたのだが・・・

 空ファイルやん!!

registerファイルを見てみる
[root@linux]# more /proc/sys/fs/binfmt_misc/register 
[root@linux]# more /proc/sys/fs/binfmt_misc/status 
enabled
[root@linux]# 
registerファイルは空だった。
statusファイルがあるのだが、中身は「enable」だった。
statusファイルが、どんなファイルなのかは
わからないのだが・・・

 うーん、具体的な物がわからないと
どうもピンとこない・・・ (--;;

 さて、ここで整理のため、もう一度、スクリプトを見てみる。

binfmt_miscに関する設定
# Set up binfmt_misc
/bin/mount -t binfmt_misc none /proc/sys/fs/binfmt_misc > /dev/null 2>&1
複数の実行ファイル形式を使えるようにするため
この処理が行われる。

 これ以上、調べていくと、ドツボにハマる。
 なので・・・

 事務員にはわかりません!

 という常套手段で逃げる事にした (^^)



 次へ進む。

シリアルの初期化
# Initialize the serial ports.
if [ -f /etc/rc.serial ]; then
        . /etc/rc.serial
fi
シリアルに関する初期化の設定を行う部分だ。
でも、私の環境では /etc/rc.serialファイルが
存在しないので、処理されない。

なので、ここは飛ばします (^^)


 次へ進む。

モジュールの取り込み
# If they asked for ide-scsi, load it
if strstr "$cmdline" ide-scsi ; then
        modprobe ide-cd >/dev/null 2>&1
        modprobe ide-scsi >/dev/null 2>&1
fi
カーネルオプションに「ide-scsi」という文字列があれば
ide-cdと、ide-scsiというモジュールを取り込んでいる

 「ide-scsi」はどっかで見た事があるぞ!

 CD-RやCD-RWドライブを認識させる時だ!

 だいぶ前、Linuxサーバー機(普通のパソコンだけど)に
バックアップ用のCD-RWドライブを付けた時に
付けた覚えがある。

 なので自分で書いた「システム奮闘記:その23」を読み返す。
 (危機管理体制強化:RAID、CD-RW、UPSでバックアップ)
 でも、肝心な事が、あまり書かれてない・・・。

 当時、そんな知識なかったもーん (^^;

 2003年の夏に取り組んだ。6年近く前の話だ。
 まだ30歳になりたての頃だ。懐かしい。
 当時は全然、技術力がなかったので調べたくても無理だった。
 実際、こんな事を書いて逃げたのだ。

システム奮闘記:その23の記述
ATAPIのCD-RWなのだが、サーバーにはSCSIだとダマして設定する方法が取られる。

ブート方法は、LILOを使っているので、lilo.confのファイルの最後に
append="hdc=ide-scsi"という呪文をつけた。
これの意味は、よくわからないが、調べても、今の私の知識では
太刀打ちできないので、ここは丸写しにした。

 それから6年近くたったのだが、何もしていなかった。
 なので・・・

 6年近く放置していたのか!

 と突っ込みを入れられそうだ。


 さて、調べてみるとPlamoLinuxの小島さんのサイトで
答えが書いてあった。
 ATAPI CD-R ドライブの使い方

 Linux上で、CD-Rドライブを使って、CD-Rを書き込むには
cdrecordというソフトを使っていた。
 だが、このソフトはSCSI接続のCD-Rドライブ用に設計されているという。

 そのため通常では、ATAPIのCD-Rドライブは使えないというのだ。
 それを解決するため、ATAPI接続のCD-Rドライブを
SCSI用のCD-Rドライブに見せかける必要がある。

 そのため「ide-scsi」という騙しのモジュールができたという。
 そして、騙しのモジュールを取り込むために、
lilo.confのファイルの中にappend="hdc=ide-scsi"という
呪文をつける意味がわかった。


 次へ進む。

ネットワーク関連の設定
# Boot time profiles. Yes, this should be somewhere else.
if [ -x /usr/sbin/system-config-network-cmd ]; then
  if strstr "$cmdline" netprofile= ; then
    for arg in $cmdline ; do
        if [ "${arg##netprofile=}" != "${arg}" ]; then
            /usr/sbin/system-config-network-cmd --profile ${arg##netprofile=}
        fi
    done
  fi
fi
カーネルオプションに「netprofile=」の
文字列があれば動く部分だ。

 私の環境ではカーネルオプションに「netprofile=」の
文字列を含む設定を行わないため、飛ばしても良い部分だが
ちょっと興味が出てきた。

 system-config-network-cmdコマンドを調べてみる。

 すると、ネットワークの設定を保管・復元するためのコマンドだという。
  system-config-network-cmdコマンド(RedHatのサイト)

 「-e」オプションを使えば、現在のネットワークの設定が
出力されるという。試しに使ってみる。

system-config-network-cmdコマンドを使ってみる
[root@linux]# /usr/sbin/system-config-network-cmd -e 
DeviceList.Ethernet.eth0.HardwareAddress=00:0C:29:85:42:86
DeviceList.Ethernet.eth0.Type=Ethernet
DeviceList.Ethernet.eth0.Netmask=255.255.255.0
DeviceList.Ethernet.eth0.BootProto=static
DeviceList.Ethernet.eth0.Device=eth0
DeviceList.Ethernet.eth0.OnBoot=true
DeviceList.Ethernet.eth0.IP=192.168.X.AAA
DeviceList.Ethernet.eth0.DeviceId=eth0
DeviceList.Ethernet.eth0.Gateway=192.168.X.BBB
HardwareList.Ethernet.eth0.Status=ok
HardwareList.Ethernet.eth0.Name=eth0
HardwareList.Ethernet.eth0.Type=Ethernet
HardwareList.Ethernet.eth0.Card.ModuleName=pcnet32
HardwareList.Ethernet.eth0.Description=Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE]
ProfileList.default.ActiveDevices.1=eth0
ProfileList.default.HostsList.1.IP=127.0.0.1
ProfileList.default.HostsList.1.Hostname=localhost.localdomain
ProfileList.default.HostsList.1.AliasList.1=localhost
ProfileList.default.HostsList.2.IP=::1
ProfileList.default.HostsList.2.Hostname=localhost6.localdomain6
ProfileList.default.HostsList.2.AliasList.1=localhost6
ProfileList.default.DNS.SecondaryDNS=
ProfileList.default.DNS.SearchList.1=localdomain
ProfileList.default.DNS.Domainname=
ProfileList.default.DNS.Hostname=localhost.localdomain
ProfileList.default.DNS.TertiaryDNS=
ProfileList.default.DNS.PrimaryDNS=192.168.X.CCC
ProfileList.default.Active=true
ProfileList.default.ProfileName=default
[root@linux]# 
このコマンドは管理者権限でしか動かないため
ユーザーはrootを使う必要がある。

ネットワークの設定に関する細かい情報まで
出力されている。これを設定情報として保管するというのだ。

 この出力された物は、ネットワークの設定で使う場合があるという。
 プロファイルの作業(RedHatのサイト)

 だが、このサイトを見た瞬間・・・

 私には、わからへん (^^;;

 でも、私は使わないので、気にしない。
 なので、ここの部分は飛ばします。


 次へ進む。

カーネルのログ出力
# Now that we have all of our basic modules loaded and the kernel going,
# let's dump the syslog ring somewhere so we can find it later
dmesg -s 131072 > /var/log/dmesg
dmesgコマンドは、カーネルが起動する時のログを
表示するコマンドだと思っていた。
でも、それだけではなさそうだ。

カーネルの状態の変化などを知らせるログの側面もあるようだ。
カーネルの状態を知る上での様々な情報を含んでいるため
このコマンドを活用できたら、面白いと思ったりする。

「-s」オプションだが、調べてみると、カーネルのメッセージの
量を指定するためのオプションだという。
初期設定(上の記述)では、131072バイトだという
リングバッファ形式のため、指定の量を越えると
古い物から削除されていく仕組みだ。


ここでの処理は、ここに至るまでのカーネルのログの量を
131072バイトに定めて、/var/log/dmesgファイルに吐き出す
処理なのだ。


 次へ進む。
 ここはRedHat7.3のスクリプトの話にも出てきた部分で
システムが正常に終了したかどうかの目印を作成する部分だ。

システムが正常の終了したかどうかの目印を作成
# create the crash indicator flag to warn on crashes, offer fsck with timeout
touch /.autofsck &> /dev/null
.autofsckファイルを作成しておく。
正常にシステムが終了しなかった場合、このファイルが残るため
起動する際に、正常終了か異常終了したのかの目印になる。


 次へ進む。
 複合コマンドの最終地点だ。
 ここでは待ち受け画面があれば、それが終了するまで
待機するようにする処理だ。

待ち受け画面の終了待ちの処理
if [ "$PROMPT" != no ]; then
    while :; do
        pid=$(/sbin/pidof getkey)
        [ -n "$pid" -o -e /var/run/getkey_done ] && break
        usleep 100000
    done
    [ -n "$pid" ] && kill -TERM "$pid" >/dev/null 2>&1
fi
} &
赤い部分のPROMPT変数は、/etc/sysconfig/initで宣言されている。
初期値では「yes」なので、この条件を満たす。

そして無限ループに入る。

青い部分は、getkeyのプロセスIDを取得する部分だ。
もし、プロセスが稼働していたら、IDに値が入る。

ピンクの部分は、プロセスIDが空でない場合
(要するにgetkeyプロセスが稼働している場合)
もしくは、/var/run/getkey_doneファイルが存在する場合
無限ループから抜けるのだ。

/var/run/getkey_doneファイルは待ち受け終了の
合図のためのファイルだと思う。

  無限ループを抜ければ、長かった複合コマンドが終了する。

 複合コマンドがバックグラウンドで動いていたが それと並行して、表(?)で動くスクリプトを見てみる。
/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if strstr "$cmdline" confirm ; then
        touch /var/run/confirm
fi
if [ "$PROMPT" != "no" ]; then
        /sbin/getkey i && touch /var/run/confirm
        touch /var/run/getkey_done
fi
wait
[ "$PROMPT" != no ] && rm -f /var/run/getkey_done

# Let rhgb know that we're leaving rc.sysinit
if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
    /usr/bin/rhgb-client --sysinit
fi

 これも分解して、見ていく事にする。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if strstr "$cmdline" confirm ; then
        touch /var/run/confirm
fi
カーネルオプションに「confirm」という文字列が
含まれていれば、/var/run/confirmという空ファイルが
作成される。

confirmの働きは、次の/etc/rc.d/rcのスクリプトで
反映される。
このファイルはデーモン起動時の際、1つ1つ起動の有無を
確認するための目印ファイルとなる。
確認の目印なので「confirm」という名前が使われている。

 実際にカーネルオプションに「confirm」を入れると
デーモン起動の有無を確認してくる。

デーモン起動の有無の確認
デーモン起動の有無を尋ねてくる。
「Y」もしくはENTERを押せば、起動する。
「N」を選べば、そのデーモンは起動しない。
「C」を選べば、確認作業はせずに、デーモンは起動するようになる。

この仕掛けは後述していますので、ここでは結果だけ紹介します。

 次を見てみる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
if [ "$PROMPT" != "no" ]; then
        /sbin/getkey i && touch /var/run/confirm
        touch /var/run/getkey_done
fi
wait
赤い部分のPROMPT変数は、/etc/sysconfig/initで宣言されている。
初期値では「yes」なので、この条件を満たす。

青の部分は getkeyコマンドで待ち受け画面にし
なおかつデーモンの起動の有無を確認するための
目印ファイルを作成している。
ここでgetkeyで待ち受けプロセスを生成するのは
子プロセス側でプロセスを終了させるために
getkeyプロセスが動かしている。

そしてピンクの部分は子プロセスの終了を待っている。

 さて、子プロセスが終了したのを確認できれば
親プロセスであるrc.sysinitスクリプトも最終処理が行われる。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
[ "$PROMPT" != no ] && rm -f /var/run/getkey_done
/var/run/getkey_doneは、子プロセスに対して
待ち受け終了の合図となるファイルだが、子プロセスが
終了したため、不要になり、消去している。

 いよいよ最終段階に進みます。

/etc/rc.d/rc.sysinit (CentOS5.1の場合)
# Let rhgb know that we're leaving rc.sysinit
if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
    /usr/bin/rhgb-client --sysinit
fi
赤い部分はGUIの起動画面のプログラムに対して
rc.sysinitのスクリプトが終了したという合図を送る命令だ。

 これでrc.sysinitの処理を全て見ていった。

 非常に長かったのらー V(^^)V


/etc/rc.d/rc の解読  長い、長い、/etc/rc.d/rc.sysinitファイルの解読作業が 終わった。まぁ、だいぶ抜けがあるけど、完璧に解読できるだけの 知識はないので、当然なのだ (^^)
rcスクリプトの起動の段階
いよいよ最後の段階の解読になる。

 rcスクリプトを見ていく事にする。

/etc/rc.d/rc (CentOS5.1の場合)
#! /bin/bash
#
# rc            This file is responsible for starting/stopping
#               services when the runlevel changes.
#
# Original Author:       
#               Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
#
コメント行なので飛ばしてしまいそうなのだが
赤い部分に注目したら、このスクリプトの特徴を垣間見る事ができる。
電源を入れて起動する時以外にも、ランレベルの変更時に動く事を示しているのだ。


 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
set -m
rc.sysinitのスクリプトでも出てきた「set -m」の処理。
setコマンドに「-m」を付ける事で、ジョブ制御を有効にする。
明示的に設定を行う事で、何らかの現象で
ジョブ制御ができていなくても安心できる。


 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
# check a file to be a correct runlevel script
check_runlevel ()
{
        # Check if the file exists at all.
        [ -x "$1" ] || return 1
        is_ignored_file "$1" && return 1
        return 0
}
関数の名前「check_runlevel」を見た限りでは
ファイルの名前の突き合わせのように思える。

 だが、ここで関数の事を書いても、わかりずらいので
ここでは置いておいて、後で説明します。

 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
# Now find out what the current and what the previous runlevel are.
argv1="$1"
set `/sbin/runlevel`
runlevel=$2
previous=$1
export runlevel previous
赤い部分は、変数argvに、このスクリプトの第1引数を代入する。
起動するランレベルの値を変数argv1に代入するのだ。

青い部分は、setコマンドで何かの設定を行っている。

 ここで思った。

 set `/sbin/runlevel`はどういう処理やねん!

 そこで、この部分を分解してみる事にした。

 まずは「`/sbin/runlevel`」が何を意味するのか見てみる。

 /sbin/runlevelは現在のランレベルと、前のランレベルを出力するコマンドだ。

/sbin/runlevelを実行してみる
[suga@linux]$ /sbin/runlevel
N 5
[suga@linux]$ 
1列目は前のランレベル(この時は「N」)が表示される。
2列目は現在のランレベル(この時は「5」)が表示される。

前のランレベルが「N」の場合、今のランレベルが当初の起動時の
ランレベルと同じのため、前のランレベルがないため
「None」もしくは「NULL」といった意味の「N」と表示される。

 さて、bashではコマンドを「`」で囲むと、コマンドの実行結果が
文字列として変換される形となる。

 言葉では表現しずらいので、具体例をあげてみます。
 なので「set `/sbin/runlevel`」がbashの上では

set `/sbin/runlevel`は、次のように変換される
変換前
set `/sbin/runlevel`
変換後
set 前のランレベル 現在のランレベルの値

 変換された形で作用するのはわかった。
 さて、ふと思った。

 「set 引数1 引数2」はどんな働きをするねん?

 そこでsetコマンドの働きを調べるため、man bashを行った。

man bash の結果の一部抜粋
set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
       オプション無しの場合は、シェル変数全ての名前と値の組が表示されます。表示は、入力として再利用できるフォ
       ーマットで行われます。出力は現在のロケールに従ってソートされます。オプションが指定されている場合、オプ
       ションはシェルの属性を設定または解除します。オプションが処理された後に残っている引き数があれば、これは
       位置パラメータの値として扱われ、 $1, $2, ...  $n の順に代入されます。オプションが指定されていれば、 以
       下の意味を持ちます:
赤い部分に注目。オプションの後ろにパラメータ値をつけるが、
それ以外の値があれば、変数$1、$2に代入していく形になる。

もし、setコマンドでオプションなしで、setコマンドの後ろに
文字列や数字の値を並べると、先頭の文字列(もしくは数字)が
$1、$2・・・$n と代入されていく。

 これを見ると「set `/sbin/runlevel`」を実行させると
変数$1には現在のランレベルの値が代入され、変数$2には
前のランレベルの値が代入される。

 この事を踏まえて、もう一度、スクリプトを見てみる。

/etc/rc.d/rc (CentOS5.1の場合)
# Now find out what the current and what the previous runlevel are.
argv1="$1"
set `/sbin/runlevel`
runlevel=$2
previous=$1
export runlevel previous
赤い部分で、現在のランレベルの値を変数$1に代入し
前のランレベルの値を変数$2に代入する。

青い部分では、変数$1の値(現在のランレベルの値)を
変数runlevelに代入

ピンクの部分では、変数$2の値(前のランレベルの値)を
変数previousに代入する。

 うーん、たった4行なのに、長い説明になった。
 というより、単に私が知らなかっただけなのだが (^^;;

 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
. /etc/init.d/functions
ユーザー定義関数を取り込む

 rcスクリプトでも、ユーザー定義関数を色々使うみたいだ。


 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
# See if we want to be in user confirmation mode
if [ "$previous" = "N" ]; then
        if [ -f /var/run/confirm ]; then
                echo $"Entering interactive startup"
        else
                echo $"Entering non-interactive startup"
        fi
fi
前のランレベルが「N」の場合、即ち、ランレベルの変更ではなく、
電源入れて起動した時に、デーモンの起動の有無を確認する
目印ファイル「/var/run/confirm」があれば
「Entering interactive startup」(対話式入力の開始)と表示される。
デーモンの起動の有無の確認を行わない場合は
「Entering non-interactive startup」(非対話式入力の開始)と表示される


 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
# Get first argument. Set new runlevel to this argument.
[ -n "$argv1" ] && runlevel="$argv1"

# Is there an rc directory for this new runlevel?
[ -d /etc/rc$runlevel.d ] || exit 0
赤い部分は、rcスクリプトの第1引数の値を、現在のランレベルの値として
代入している。この行はランレベルの変更時に意味を持つ部分だ。
このランレベルで稼働するという意味合いだ。

青い部分は、稼働したいランレベルに該当する
/etc/rcN.d(Nは稼働したいランレベルの値)のディレクトリが
存在するかどうかを確認し、存在しなければ「0」を返り値として
このスクリプトを終了させる。


 次へ進む。  ここでは現在稼働中のデーモンがあれば、全て終了させる処理だ。  Killなので「ぶっ殺す」と書きたい所だが、表現が物騒なので 「全て終了」が無難かも (^^;;
/etc/rc.d/rc (CentOS5.1の場合)
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/K??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                || continue

        # Bring the subsystem down.
        if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
                $i stop
        else
                action $"Stopping $subsys: " $i stop
        fi
done
赤い部分は/etc/rcN.d (Nは該当のランレベルの値)のディレクトリで
頭にKが付くファイル名を絶対パス付で、変数iに代入する。

青い部分は絶対パスのファイル名を引数に、
関数check_runlevelに代入している。
関数の返り値が「偽」(0以外)は
この巡回に関しては飛ばす意味のcontinueが作動する。

 ところで、関数check_runlevelなのだが

 一体、何の関数やねん!

 という疑問は当然湧いてくる。

 そこで関数check_runlevelを見ていく事にした。

check_runlevel関数
check_runlevel ()
{
        # Check if the file exists at all.
        [ -x "$1" ] || return 1
        is_ignored_file "$1" && return 1
        return 0
}
引数(変数$1)は、デーモンの起動や停止に関する
スクリプトのファイル名が値として入っている。
青い部分は、引数に代入されている値(ファイル名)が存在しない、
もしくは実行可能でない場合、返り値を「1」にする部分だ。

 この関数名(check_runlevel)を見た感じでは、ランレベルの確認のように
思えるのだが、どうやらそうではなく、デーモンのスクリプトが
正常に起動できるかどうかの確認のための関数のようだ。

/etc/rc.d/rc (CentOS5.1の場合)
check_runlevel ()
{
        # Check if the file exists at all.
        [ -x "$1" ] || return 1
        is_ignored_file "$1" && return 1
        return 0
}
デーモンファイルをis_ignored_file関数に送った結果が
真であれば、返り値を「1」にする部分だ。

 これを見て・・・

 一体、is_ignored_file関数はどういう関数やねん?

 ファイル名にヒントが隠されている。
 「無視するファイル」なのだ。では、何を無視するのか?

 この関数は、/etc/rc.d/init.d/functionsで定義されている。
 そこでis_ignored_file関数を見てみる事にした。

is_ignored_file関数の中身
# Check whether file $1 is a backup or rpm-generated file and should be ignored
is_ignored_file() {
    case "$1" in
        *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave)
            return 0
            ;;
    esac
    return 1
}
引数(デーモンのスクリプトのファイル名)の中で「~」がついていたり、
拡張子がbak、orig、rpmnew、rpmorig、rpmsaveの場合
返り値を「0」にしている。
バックアップファイルなので無視したい。
そこで無視したいファイルの場合、返り値を「0」にして
無視できないファイルは「1」にして区別をしている。

 そこでcheck_runlevel関数の記述を見てみる。

/etc/rc.d/rc (CentOS5.1の場合)
check_runlevel ()
{
        # Check if the file exists at all.
        [ -x "$1" ] || return 1
        is_ignored_file "$1" && return 1
        return 0
}
赤い部分は、デーモンのスクリプトが実行可能かどうかの確認で
青い部分は、無視するバックアップファイルなのかの確認だ。
もし、実行可能で、バックアップファイルでない場合は
返り値は「0」にするのだ。

 ようやくcheck_runlevel関数で処理される内容がわかった。
 そこでrcスクリプトに戻ってみる。

/etc/rc.d/rc (CentOS5.1の場合)
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/K??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                || continue

        # Bring the subsystem down.
        if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
                $i stop
        else
                action $"Stopping $subsys: " $i stop
        fi
done
青い部分では、デーモンの起動の有無を確認するため
起動・停止に関するファイルを代入し、実行可能でかつ
無視しないファイルであるかどうかを確かめ、
その条件を満たせば、次に進むのだが、
そうでない場合は、この巡回だけは飛ばすための
continueが働くようになっている。

 うーん、込み入った感じがする。
 先に進む。

/etc/rc.d/rc (CentOS5.1の場合)
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/K??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                || continue

        # Bring the subsystem down.
        if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
                $i stop
        else
                action $"Stopping $subsys: " $i stop
        fi
done
赤い部分は/etc/rcN.d (Nは該当のランレベルの値)のディレクトリで
頭にKが付くファイル名を絶対パス付で、変数iに代入する。

青い部分は変数iから「/etc/rcN.d/K??」(Nは該当のランレベルの値)で
「??」は2桁の数字を取り除いたものを、変数subsysに代入する。

 わかったような、わからんような説明になってしまった。
 言葉での説明は、結構、難しい。

 なので、論より証拠で以下のスクリプトを走らせてみる事にした。

test.sh (実験スクリプト)
#!/bin/bash

for i in /etc/rc5.d/K* ; do
    echo $i

    subsys=${i#/etc/rc5.d/K??}
    echo "subsys = $subsys"
done
稼働結果
[suga@linux]$ ./test.sh
/etc/rc5.d/K02NetworkManager
subsys = NetworkManager
/etc/rc5.d/K02NetworkManagerDispatcher
subsys = NetworkManagerDispatcher
/etc/rc5.d/K02avahi-dnsconfd
subsys = avahi-dnsconfd

(途中、省略)

/etc/rc5.d/K89pand
subsys = pand
/etc/rc5.d/K89rdisc
subsys = rdisc
[suga@linux]$ 
言葉で説明するよりも、実際に目で見える形にした方が
わかりやすい。

要するに、絶対パスでのファイル名と
ファイル名からデーモン名だけを抜き取った物を
取り出しているというわけなのだ。

 抽出する代物がわかった所で、もう1度、スクリプトを見てみる。

/etc/rc.d/rc (CentOS5.1の場合)
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/K??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                || continue

        # Bring the subsystem down.
        if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
                $i stop
        else
                action $"Stopping $subsys: " $i stop
        fi
done
青い部分は、デーモンの名前だけを抽出した物が
変数subsysに代入される。
ピンクの部分は、/var/lock/subsysディレクトリに
デーモン名そのもののファイル名、もしくはデーモン名と
拡張子がinitのファイル名が存在するかどうかの確認を行う。
存在しない場合は、この巡回を飛ばすようになっている。

 ところで、、/var/lock/subsysディレクトリには
どんなファイルが置かれているのか。
 自分の目で確かめる事にした。

/var/lock/subsysディレクトリを見てみる (私の環境)
[suga@linux]# cd /var/lock/subsys
[suga@linux]# ls -l
合計 0
-rw-r--r-- 1 root root 0  1月  2 12:07 acpid
-rw-r--r-- 1 root root 0  1月  2 12:07 atd
-rw-r--r-- 1 root root 0  1月  2 12:07 auditd
-rw-r--r-- 1 root root 0  1月  2 12:07 autofs
-rw-r--r-- 1 root root 0  1月  2 12:07 avahi-daemon
-rw-r--r-- 1 root root 0  1月  2 12:07 bluetooth
-rw-r--r-- 1 root root 0  1月  2 12:07 crond
-rw-r--r-- 1 root root 0  1月  2 12:07 gpm
-rw-r--r-- 1 root root 0  1月  2 12:07 haldaemon
-rw-r--r-- 1 root root 0  1月  2 12:07 hcid
-rw-r--r-- 1 root root 0  1月  2 12:07 hidd
-rw-r--r-- 1 root root 0  1月  2 12:07 kudzu
-rw-r--r-- 1 root root 0  1月  2 12:07 local
-rw-r--r-- 1 root root 0  1月  2 12:07 messagebus
-rw-r--r-- 1 root root 0  1月  2 12:07 microcode_ctl
-rw-r--r-- 1 root root 0  1月  2 12:07 netfs
-rw-r--r-- 1 root root 0  1月  2 12:07 network
-rw-r--r-- 1 root root 0  1月  2 12:07 sdpd
-rw-r--r-- 1 root root 0  1月  2 12:07 sshd
-rw------- 1 root root 0  1月  2 12:07 syslog
-rw-r--r-- 1 root root 0  1月  2 12:07 xfs
-rw-r--r-- 1 root root 0  1月  2 12:07 xinetd
-rw-r--r-- 1 root root 0  1月  2 12:07 yum-updatesd
[suga@linux]# 
容量がゼロのファイルがばかりだ。
ディレクトリ名が/var/lock/subsysなので
名前からデーモンの起動の有無の目印ファイルである事が伺える。

このファイルがある事で、重複してデーモンを起動するのを
防ぐ役目がある。そのためディレクトリ名に「lock」になっている。

 安定的にシステムを動かすために、見えない所で
色々、仕掛けがあるのだと思う。

 つまり既に起動しているデーモンがない(起動の目印がない)場合
この先の処理は不要なので、巡回を飛ばす仕掛けになっている。

/etc/rc.d/rc (CentOS5.1の場合)
# First, run the KILL scripts.
for i in /etc/rc$runlevel.d/K* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/K??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                || continue

        # Bring the subsystem down.
        if LC_ALL=C egrep -q "^..*init.d/functions" $i ; then
                $i stop
        else
                action $"Stopping $subsys: " $i stop
        fi
done
赤、青共に、デーモンをおねんねさせる処理なのだ。
ランレベルを変更する場合、何らかの理由で
一度、デーモンを停止させる事を意味する。


 次へ進む。  今度は、起動させたいデーモンを起動する時の処理だ。
/etc/rc.d/rc (CentOS5.1の場合)
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                && continue
                    
        # If we're in confirmation mode, get user confirmation
        if [ -f /var/run/confirm ]; then
                confirm $subsys
                test $? = 1 && continue
        fi

        update_boot_stage "$subsys"
        # Bring the subsystem up.
        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                export LC_ALL=C
                exec $i start
        fi
        if LC_ALL=C egrep -q "^..*init.d/functions" $i \
                        || [ "$subsys" = "single" -o "$subsys" = "local" ]; then
                $i start
        else
                action $"Starting $subsys: " $i start
        fi
done
赤い部分は/etc/rcN.d (Nはランレベルの値)のディレクトリで
頭に「S」がつくファイル(スクリプト)を拾い上げる。
そして青い部分で、実行可能でバックアップファイルでない
スクリプトかどうかの確認を行う。
もし、実行可能でない、もしくはバックアップファイルなら、
この周回は飛ばす処理になっている。

 次を見てみる。

/etc/rc.d/rc (CentOS5.1の場合)
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                && continue
                    
        # If we're in confirmation mode, get user confirmation
        if [ -f /var/run/confirm ]; then
                confirm $subsys
                test $? = 1 && continue
        fi

        update_boot_stage "$subsys"
        # Bring the subsystem up.
        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                export LC_ALL=C
                exec $i start
        fi
        if LC_ALL=C egrep -q "^..*init.d/functions" $i \
                        || [ "$subsys" = "single" -o "$subsys" = "local" ]; then
                $i start
        else
                action $"Starting $subsys: " $i start
        fi
done
赤い部分は、起動のスクリプト名からデーモンの名前だけを抽出する部分。
青い部分は、既に、そのデーモンが起動しているかどうかを確認する。
このディレクトリに該当のファイルがあれば、起動していると見なし
この周回は飛ばす設定になっている。

 次を見てみる。

/etc/rc.d/rc (CentOS5.1の場合)
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                && continue
                    
        # If we're in confirmation mode, get user confirmation
        if [ -f /var/run/confirm ]; then
                confirm $subsys
                test $? = 1 && continue
        fi

        update_boot_stage "$subsys"
        # Bring the subsystem up.
        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                export LC_ALL=C
                exec $i start
        fi
        if LC_ALL=C egrep -q "^..*init.d/functions" $i \
                        || [ "$subsys" = "single" -o "$subsys" = "local" ]; then
                $i start
        else
                action $"Starting $subsys: " $i start
        fi
done
赤い部分は、デーモン起動の有無を確認する目印ファイルがあるかどうかを
見ている。もし、目印ファイルがあれば青い部分に続く。

青い部分は、confirm関数の返り値が「1」の場合、
この巡回は飛ばす処理にしている。

だが、返り値の意味合いがわからないと
青い部分の処理の理由が見えてこない。

 青い部分の処理の理由を知るために、confirm関数の振る舞いを
知る必要がある。

 confirm関数は、/etc/rc.d/init.d/functionsで定義されている。
 早速、confirm関数を見てみる事にした。

confirm関数の中身
# Confirm whether we really want to run this service
confirm() {
  [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
  while : ; do 
      echo -n $"Start service $1 (Y)es/(N)o/(C)ontinue? [Y] "
      read answer
      if strstr $"yY" "$answer" || [ "$answer" = "" ] ; then
         return 0
      elif strstr $"cC" "$answer" ; then
         rm -f /var/run/confirm
         [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=no
         return 2
      elif strstr $"nN" "$answer" ; then
         return 1
      fi
  done
}
赤い部分は画面表示で、利用者に選択肢の表示を行う部分。
デーモンの開始を行う(Y)なのか、開始をしない(N)なのか
続行(C)なのかを選ぶ。
続行(C)とは起動の有無の確認しないで、全て起動させるという意味になる。
(なぜ「続行」が、確認の有無をしないで全部起動させるのかは後述しています)

青い部分は、利用者からの解答を待ち受ける部分。

 目印のファイル「/var/run/confirmファイル」があると
デーモンの起動の有無の確認を行おうとする。
 実際に、どんな画面が出ているのか再度、見てみる事にする。

デーモン起動の有無の確認

 確認の画面で3つの選択肢がある。
 それぞれの処理をconfirm関数の中で実行されるのだが、
3つの選択肢の中から、それぞれを選んだ後の処理を見てみる。

confirm関数の中身
# Confirm whether we really want to run this service
confirm() {
  [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
  while : ; do 
      echo -n $"Start service $1 (Y)es/(N)o/(C)ontinue? [Y] "
      read answer
      if strstr $"yY" "$answer" || [ "$answer" = "" ] ; then
         return 0
      elif strstr $"cC" "$answer" ; then
         rm -f /var/run/confirm
         [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=no
         return 2
      elif strstr $"nN" "$answer" ; then
         return 1
      fi
  done
}
赤い部分はデーモンを起動させるの「Y」を選んだ場合。
返り値を「0」にしている。

青い部分は続行の「C」を選んだ場合。
この場合、デーモンの確認の有無の目印である 
/var/run/confirmファイルを消去し、返り値を「2」にする。

ピンクの部分は、デーモン起動を行わない「N」を選んだ場合。
この場合、返り値「1」を返す。

 confirm関数がどんな関数かがわかったので、
もう一度、rcのスクリプトを見てみる。

/etc/rc.d/rc (CentOS5.1の場合)
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                && continue
                    
        # If we're in confirmation mode, get user confirmation
        if [ -f /var/run/confirm ]; then
                confirm $subsys
                test $? = 1 && continue
        fi

        update_boot_stage "$subsys"
        # Bring the subsystem up.
        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                export LC_ALL=C
                exec $i start
        fi
        if LC_ALL=C egrep -q "^..*init.d/functions" $i \
                        || [ "$subsys" = "single" -o "$subsys" = "local" ]; then
                $i start
        else
                action $"Starting $subsys: " $i start
        fi
done
赤い部分のconfirm関数の返り値が「1」の場合
即ち、デーモンの起動を行わない場合、
この巡回で、これ以上の処理は必要ない。
そのため、青い部分の処理で返り値が「1」の場合
この巡回を飛ばす設定になっている。

 そしてデーモン起動を選ぶと次の部分の処理が始まる。

/etc/rc.d/rc (CentOS5.1の場合)
# Now run the START scripts.
for i in /etc/rc$runlevel.d/S* ; do
        check_runlevel "$i" || continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys -o -f /var/lock/subsys/$subsys.init ] \
                && continue
                    
        # If we're in confirmation mode, get user confirmation
        if [ -f /var/run/confirm ]; then
                confirm $subsys
                test $? = 1 && continue
        fi

        update_boot_stage "$subsys"
        # Bring the subsystem up.
        if [ "$subsys" = "halt" -o "$subsys" = "reboot" ]; then
                export LC_ALL=C
                exec $i start
        fi
        if LC_ALL=C egrep -q "^..*init.d/functions" $i \
                        || [ "$subsys" = "single" -o "$subsys" = "local" ]; then
                $i start
        else
                action $"Starting $subsys: " $i start
        fi
done
デーモンの起動を開始させている。
特にピンクの部分は、起動中に何を起動させているのかを
画面表示させているのだ。


 次へ進む。
/etc/rc.d/rc (CentOS5.1の場合)
rm -f /var/run/confirm
色々なデーモンの起動の有無を確認するための目印が
不要になったので消去している。

 不要な物は消す。
 これは基本なのだが、改めて、自分自身が実践できるかと
問われると、答えづらいものがある (^^;;

 次へ進む。  rcスクリプトの最後の部分だ。
/etc/rc.d/rc (CentOS5.1の場合)
# check a file to be a correct runlevel script
if [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --ping ; then
  /usr/bin/rhgb-client --quit
fi
もし、起動画面がGUIで動いていたら、
それを終了させるための処理だ。
rcスクリプトの最後の部分で、GUI起動画面を終了させているのだ。

 ここでようやくrcスクリプトが終了した。

まとめ  長いスクリプトの解読作業が終わった。  といっても、未解読箇所や、理解できなかった所は多々あるが 今の実力では、これが限界だ (--;;  今回、読んだ主な3つのスクリプトを簡単にまとめると 以下のようになる。
3つのスクリプトをまとめると
inittab
Linuxカーネルがが起動した後、initが走る。
このinitプロセスがinittabを呼び出して
スクリプトに記述されている事が実行される。
rc.sysinit
システムの初期化を行うスクリプトで
inittabスクリプトから呼び出される。

ネットワーク、ハードウェアの認識、カーネルオプション
カーネルパラメーターの反映などが行われる。
rc
各種デーモンの起動を行うスクリプトで
inittabスクリプトから呼び出される。

 簡単に書けば、こうなのだが、中身の解読となれば
今ままで説明した通り、大変な量と知識が必要になる。

 この解読を通じて知った知識をまとめると以下の通りです。

解読を通じて知った知識
(1)
bashの知識。まだ記述はできないが
ある程度は読めるようになった。
読めない場合はman bashを使えば良いのだ (^^)
(2)
システムの設定はGUI頼りだったが
スクリプトを追いかければ設定箇所に辿り着くため
仮にGUI画面が開けなくなっても、
ある程度の設定変更が可能になった。
(3)
ランレベルの概念と変更方法を覚えた。
これによって、普段触らないサーバーの
ランレベルを「3」にする事で、システムの負荷を
低い状態で運用ができるようになった。

仮想化技術導入時に、複数の仮想OS導入を入れる際
役に立った。
(4)
異常終了の後に起動すると、ファイルシステムの点検・修復の
fsckコマンドが実行される仕掛けがわかったと同時に、
fsckコマンドの使い方を覚えた。あまり使わない事を祈る。
(5)
GMTとUTCの違いを理解した上、UTCと地域時刻の
設定をわける理由もわかった。
安心して、どの時刻にするかを選択できるようになった。
(6) ジョブ制御の話を覚えた。
(7)
カーネルオプション、カーネルパラメータが
rc.sysinitスクリプト上で反映される場所がわかった。
(8)
通常の日本語キーボードの鍵盤が109個ある事を知った。
jp106の意味を初めて理解できた。
(9)
Statement Linuxの存在を初めて知った。
将来、導入するための足掛かりになったとは言えないが
頭の片隅にはいれておく事ができた。
(10)
/etc/fstabと/etc/mtabのファイルの違いを知った。
マウントの初期設定と、現状のマウントの状態を記述した
ファイルの違いだ。
(11)
/etcディレクトリがアプリケーションの
一時ファイルの保管場所だと知った。
(12)
/varディレクトリがシステムに関する
ログファイルなど、変化していく内容のファイルを
保管する場所だというのを知った。
(13)
wtmpがログインの履歴を記録したファイルで
utmpが現在ログイン中のユーザーを記録したファイルだと知った。
(14)
touchコマンドは空ファイルを作成する役目だけでなく
既存のファイルの修正時刻を直す役目があり、そちらが主な役目だ。
(15)
スワップ領域を確保しても、swaponコマンドで
有効にしないとダメだと知った。

 まだ他にもあるのだが、挙げ出すとキリがないので
この辺にしておきます。


 それにしても、2005年にRedHat7.3のスクリプトの
解読作業を開始したのだが、途中、挫折して
2008年にCentOS51の解読に切り替え、調べている間に
気がつけば2009年になっていました。
 4年近くの歳月をかけて、なんとか書けたので
やれやれという気分です (^^;;


次章:「VMwareESXiのインストールと設定」を読む
前章:「仮想化ソフト「Xen」の導入に挑戦」を読む
目次:システム奮闘記に戻る