/etc/ファイル群の編集を簡単化するAugeas

 複雑な構成の設定ファイル群に新たな変更を施す場合、awkを用いてファイル中の要変更箇所を特定して臨機応変的な対処をするという自作スクリプトの活躍した時代は、今や終焉に近づいているのかもしれない。それというのも、設定ファイルの記述内容を自動で解読する Augeas というツールの登場により、人間は本来の目的である設定内容の変更だけに集中できるようになりつつあるからである。例えば仮に、何らかの設定ファイルにおける特定情報の記述位置が4列目に移動されていたとしても、Augeasを使えば該当する設定情報は以前と同様に提示されるため、そうした内部的な変更をユーザがいちいち気にする必要はなくなるのだ。

 Augeasを用いた設定ファイル群の編集はコマンドラインから直接実行できるが、あるいはこうした機能をネイティブのC APIおよび、Python、Ruby、OCamlのバインディングを用いてその他のプログラムに組み込むことも可能になっている。Augeasの入手法に関しては、Fedora 9の標準リポジトリに収録されている他、openSUSE 10.3については1-Clickインストールができるようになっているが、現状でUbuntu用のパッケージは用意されていない。私の場合は、64ビット版Fedora 8マシン上で「./configure; make; sudo make install」という通常のインストール手順にてソースコードからコンパイルしたAugeasバージョン0.1.1を使用している。

 なお上記の./configureを実行してから(READMEに示してあるように)Augeasをセットアップしても最初は上手く行かないはずで、それは設定ファイル群の認識にAugeasが使用するファイル群は/usr/localにインストールされているのに、実際には/usrを検索するためだ。こうした設定ファイルの認識法を規定しているのはレンズ(lens)と呼ばれているファイル群であり、ここではこれらlensファイル群についてもう少し説明することにしよう。先の通常インストールにて発生する問題を解決するには、下記に示したようにAUGEAS_LENS_LIBに正しいパスをエクスポートすればいい。ここではまず下記の実行例における1つ目のlsコマンドにて、ファイルディレクトリが何も出力されていない点に注目して頂きたい。

$ augtool
augtool> ls
Not enough arguments for ls
augtool> ls /
augeas/ = (none)
augtool>
$ export AUGEAS_LENS_LIB=/usr/local/share/augeas/lenses
$ augtool
augtool> ls /
augeas/ = (none)
files/ = (none)

 Augeasの認識した設定ファイル群の表示は、一種の仮想的なファイルシステムを2つ介して行われるようになっている。つまり/filesのパスでは設定ファイル群、そして/augeasではメタデータが表示されるのであり、例えば/etc/hostsのファイル群はAugeasにおいて/files/etc/hostsという扱いにされる。

 Augeasでサポートされているのは、aliases、aptsources、fstab、grub、hosts、ifcfg、inittab、pam、sshd、sysconfig、xinetd、yumの設定ファイルフォーマットである。その他の独自フォーマットの設定ファイルをAugeasで編集したい場合は、lensと呼ばれる機構を利用しなければならない。既にテキスト構文解析およびExtended Backus-Naur Form(EBNF)についての知識を有すユーザであれば、各自の設定ファイルに応じたAugeas用のlensファイルを作成するのはそれほど難しくないはずだが、EBNFに詳しくないユーザがlensファイルを作成するには、テキストの構文解析(parsing)と形式文法(formal grammar)に関する何らかの入門書に目を通しておかなくてはならないだろう。こうした標準的なEBNFの定義法とAugeas用lensの記述法を比べた場合、両者の間に存在する基本的な相違点の1つは、lensの用途は特定のテキストファイルを抽象構文木(abstract syntax tree)に変換する構文解析だけでなく、そうして取得した構文木からプレインテキストファイルへの再変換という双方向の処理に使用するという点である。

 これはAugeasのダウンロードページにも記載されている注意事項だが、現行のAugeasは未だ試作段階であるため、ここで編集した設定ファイルが損傷する可能性を想定しておかなくてはならない。そのためコマンドラインツールには、こうした事態に備えた“バックアップ”オプション、つまり変更を加える設定ファイルを別途保管しておく機能が用意されているのである。またこの機能を用いると“/”以外をシステムルートディレクトリとした状態でAugeasを実行させることもできる。この機能を使うと、例えば/tmp/augeas-root/etc/hostsなどの指定で各自の設定ファイルを一般ユーザの権限にてコピーしておき、このファイルに施した変更に問題がないかを一通りチェックしてから、変更後のファイルを改めて/etc/hostsにコピーし直すという処理ができる。

 下記に示してあるのは、Augeasの諸機能を試す目的で/etcの内容を別途コピーしておくコマンド操作例である。なお/etcの直下にあるgconfおよびselinuxというディレクトリに収められているファイル群は数が多い上に簡単なAugeasの機能テストをする対象として適していないため、ここでは/etcにある設定ファイル群をtarボール化する際の前処理として、これら2つのディレクトリをfindコマンドを用いて明示的に除外させている。

export AUGEAS_ROOT=/tmp/augeas-`id -u`-root
rm -rf $AUGEAS_ROOT
mkdir  $AUGEAS_ROOT
find /etc \( -path /etc/gconf -o -path /etc/selinux \) \
  -prune -o -type f \
  -print0 | xargs -0 tar cf $AUGEAS_ROOT/etc.tar
tar -C $AUGEAS_ROOT -xf $AUGEAS_ROOT/etc.tar
sudo chown -R $USER $AUGEAS_ROOT
augtool -b

 この段階で私が気づいたのは、Augeas WebサイトにあるQuick Tourページの解説では/etc/hostsが表示されているのに、私のhostsファイルは表示されていないということだ。この問題に関しては、私のhostsファイルに空行があるのが原因であったようで、こうした空行をすべて削除したところ/etc/hostsファイルはAugeasにより認識されるようになった。

$ augtool
augtool> ls /files/etc
sysconfig/ = (none)
...
hosts/ = (none)
inittab/ = (none)
...
augtool> ls /files/etc/hosts
1/ = (none)
2/ = (none)
...
augtool> ls /files/etc/hosts/1
ipaddr = 127.0.0.1
canonical = localhost.localdomain
alias[1] = localhost
alias[2] = lincomvm

 上記実行例中のlsコマンドの出力にあるlocalhostの定義を見ると分かるように、1つのアイテムに複数のエントリが存在する場合は、nをインデックス番号とした連番[n]が付けられるようになっている。このようにhostsファイル中の定義情報を示す目的でサブディレクトリ群に1、2……、nという連番を付けるシンタックスについては、10.10.10.10など特定のIPアドレスに対するエントリが既に存在するかのチェックを困難にしているようにも思われるが、Augeasのパス指定ではワイルドカードがサポートされており、XPath系の一部シンタックスも使用できるようになっている。例えば下記の実行例における1つ目のコマンドは、/etc/hosts中に10.10.10.10という定義が既に存在しているかのチェックを行うものである。そして2つ目のコマンドでは、当該エントリの詳細を表示させている。次に私が実行したのは、setコマンド中にalias[1]を指定することで、当該ホストに対する直接のエイリアスを1つ作成することである。

 ドキュメントの記載によると最終ノードのインデックス番号はlast()関数の戻り値として取得できるはずなのだが、現行のAugeasではこうした状況での算術演算ができないため、last()+1をインデックス番号とした2つ目のエイリアス作成は失敗に終わってしまった。この問題に対する1つの簡易的な回避法は、事前にinsコマンドによる新規ノード追加を行っておき、その後でlast()の返す最終ノードのインデックス番号を用いたエイリアス作成を行うというものである。その後のmatchコマンドで実施しているのはエイリアスが存在するかのチェックであるが、ここではエイリアスのパスにおけるインデックス番号指定を省略することで、すべてのエイリアスノードをチェックさせている。最終行のsaveコマンドは、変更した内容を確定して保存させるための指示である。

augtool>  match /files/etc/hosts/*/ipaddr  10.10.10.10
/files/etc/hosts/10/ipaddr
augtool> print /files/etc/hosts/10
/files/etc/hosts/10
/files/etc/hosts/10/ipaddr = "10.10.10.10"
/files/etc/hosts/10/canonical = "linux.com.example.com"
augtool> set /files/etc/hosts/10/alias[1] "l.c.alias"
augtool> print /files/etc/hosts/10
/files/etc/hosts/10
/files/etc/hosts/10/ipaddr = "10.10.10.10"
/files/etc/hosts/10/canonical = "linux.com.example.com"
/files/etc/hosts/10/alias = "l.c.alias"
augtool> set /files/etc/hosts/10/alias[last()+1] "l.c2.alias"
Failed
augtool> ins alias after /files/etc/hosts/10/alias[last()]
augtool> set /files/etc/hosts/10/alias[last()] "l.c2.alias"
augtool> print /files/etc/hosts/10
/files/etc/hosts/10
/files/etc/hosts/10/ipaddr = "10.10.10.10"
/files/etc/hosts/10/canonical = "linux.com.example.com"
/files/etc/hosts/10/alias[1] = "l.c.alias"
/files/etc/hosts/10/alias[2] = "l.c2.alias"

augtool> match /files/etc/hosts/10/alias l.c2.alias
/files/etc/hosts/10/alias[2]

augtool> save

 このようにAugeasを利用することで、設定ファイルのアップデート時に独自のスクリプトを構築するという負担は大幅に軽減することができるだろう。もちろんごく単純に、特定の設定ファイルに新規の設定行を1つ追加するだけという作業であれば、Perl、awk、grep、wc、catなどでも対処可能ではあるが、過去に変更を施した既存の設定行を操作するとなると話はそれほど簡単には進まないはずだ。つまり過去に施した変更内容が複雑なものであればあるほどスクリプト処理による設定ファイルの編集作業は困難となり、オリジナル状態からの相違点が大きくなった各種の設定ファイル群に対処可能なスクリプトを整備する負担は過度に大きくなっていくはずなのである。

 確かにAugeasは誕生間もないプロジェクトであり、本文中で触れたような/etc/hostsファイルに空行があるだけで正常に対処できないなど若干の不備は残されているものの、設定ファイルの編集作業を大幅に簡単化するという本来の目的は現状の完成度においてもほぼ達成されていると評していいだろう。いずれにせよこうしたhostsファイルの空行認識などは構文解析にまつわる不備なのであり、今後Augeas用のlensファイルの改良が進んでいけば、この種の問題点も一括して解消されていくものと期待できるはずだ。

Ben Martinは10年以上にわたってファイルシステムに取り組んでおり、博士課程の修了後、現在はlibferris、ファイルシステム、検索ソリューションを中心としたコンサルティング業に従事している。

Linux.com 原文