tar にはコマンドオプションが多い。しかもバックアップ対象も多いと、例えば:
tar -g /home/hoge/cache/test.snar --exclude-from=/home/hoge/settings/exclude.txt -C / -czf /home/hoge/backup/test_050817.tgz home/hoge/data home/hoge/www home/hoge/.* home/hoge/Maildir
というようなことになり、とてもいちいちタイプしてはいられない。また、こうしたコマンドをひとつひとつシェルスクリプトにするのも、あまり格好良くないし、バックアップしたアーカイブの管理も面倒だ。そこで、システム上の全てのユーザが共通して使えて、システマティックに管理ができるスクリプトを作成して使っている。それが tarbut (TAR BackUp Tool) だ。
下記のシェル/ユーティリティが利用可能な UNIX 系システム
アーカイブをダウンロードし、適当な一時ディレクトリで解凍する:
user$ tar -xzpvf tarbut-1.x.x.tgz
以下のコンポーネントが解凍される:
tarbut/ └ tarbut (スクリプト本体) └ tarbut.conf (動作設定ファイル) └ tarbut.d/ └ sample.lst (ターゲットリスト記述例) └ sample.exc (リスト例`sample'用のバックアップ除外対象パターン定義) └ excludes (全ターゲット共通の除外パターン定義)
スクリプト本体をパスの通ったディレクトリに置く。例えば:
root# cp tarbut /usr/local/bin root# chmod 755 /usr/local/bin/tarbut
動作の設定は tarbut.conf にある以下の変数で設定する。ディレクトリ名の最後のスラッシュは無用。ここで指定した各ディレクトリは自動的には作られないので、実行前に作成しておく必要がある。
tarbut.conf はシステムデフォルト設定 /etc/tarbut.conf と、ユーザ毎の設定 ~/tarbut.conf の 2 段構え。ユーザによる設定変数は、グローバルファイルの設定よりも優先される。このファイルはメインスクリプトからインクルードされるので、記述様式は通常の BASH スクリプト規則に従う。
グローバル /etc/tarbut.conf の例:
CONFDIR=/etc/tarbut.d DESTDIR=/backup CACHEDIR=/var/lib/tarbut COMPRESS=none OPT="--ignore-failed-read" AUTOCLEAN_LEVEL1=0 MAX_DESTSIZE=716000 MULTI_COMP_THRESHOLD=90 MAX_VOL=9
ユーザの ~/tarbut.conf の例:
CONFDIR=~/tarbut.d DESTDIR=~/archives CACHEDIR=~/archives/cache COMPRESS=gzip OPT="--verbose" AUTOCLEAN_LEVEL1=1 MULTI_COMP_THRESHOLD=50
OPT 変数には、以下の tar オプションは書く必要がない。スクリプト本体が適宜付加するので、書いてはならない。
tarbut に、アーカイブに納める対象ファイルやディレクトリを指示するファイル。 tarbut は、コマンド引数で渡された <TARGET> を名前に持つ <TARGET>.lst ファイルを CONFDIR 直下から探し、その内容をシェルを通じて tar に渡す。つまり、sample.lst という名前のファイルを作成することそれ自体が、`sample' という「ターゲット」を「定義」したことになる。リストファイルつまりターゲットはいくつでも作成できる。
sample.lst を見ていただければ分かるが、ターゲットリストファイルには、バックアップしたいファイルやディレクトリのパスを 1 行に 1 つずつの単位で書く。記述のコツに関しては、tar プログラムそのものに依存する部分なので、 tar におけるディレクトリの扱われ方 (-C オプションなど) について少しばかり理解しておく必要がある。さわりだけ述べれば、以下のようになる:
※ いや、「シェルスタイル」ではなく、実際、アーカイブ時においては、ワイルドカードは tar でなくシェルによって展開・解釈されているとしか思えない。`-C /PATH' とワイルドカードを使うと、バックスラッシュでエスケープしようがシェル展開を阻止する `set -f' を唱えようが、tar はワイルドカードを正常に解釈できず「`hoge/*' というファイルはない」と跳ねつけられて。かたや、/PATH へあらかじめ cd しておくとエラーは出ないのだ。tarbut 1.2.0 で、-C をやめて事前に cd する方式に改めた。
tarbut 独自の仕様:
excludes ファイルと <TARGET>.exc ファイルは、アーカイブしない対象を定義するファイル。 excludes ファイルはそのユーザの全ターゲットに適用するデフォルトの除外パターン、 <TARGET>.exc はそのターゲットにのみ適用する除外パターンだ。 `sample' ターゲットを例にとると、sample.exc ファイルが存在すれば (中身が空でも) その内容が採用され、 sample.exc ファイルがなければ excludes の設定が採用される。両ファイルの「総和」ではない点に注意。この仕様によって、excludes に記述がある時でも、空の sample.exc ファイルを置いておけば、sample ターゲットではいかなる除外もさせないようにできる。
この機構は tar 自体の --exclude-from 機能を利用している。現在のところ GNU tar には下記のような仕様と注意事項 (GNU tar Reference Manual) がある。以下には tarbut 独自の仕様も補足として一部含めている。:
基本書式は、
tarbut [option] <TARGET>
<TARGET> はリストファイルから拡張子 .lst を除いた名称であり、どのオプションの時も必ず指定しなければならない (--help と --list を除く)。オプションは、なし、または下記のいずれかひとつ。いずれも、<TARGET> で指定されたターゲットに属するオブジェクトだけを処理する。下表中の <EXT> はバックアップアーカイブの拡張子で、 tarbut.conf の COMPRESS 設定によって変化する (tar, tgz, tar.bz2 のいずれか)。下記と同様の内容は tarbut --help または -h でも (英語だが) 見られる。
--level=0 | インクリメンタルバックアップ動作を行う。このように右辺が 0 の場合は、インクリメンタルバックアップの土台となるフルバックアップを行う。取られたバックアップは <TARGET>_lev0.<EXT> という名称になる。その際、 CACHEDIR にスナップショットファイル <TARGET>.snar が作成され、もし以前に作成された <TARGET>.snar が既に存在すれば、古いものは.back を付けて改名される。また、 tarbut.conf で AUTOCLEAN_LEVEL1 が 1 となっている場合は、既に存在する Level-1アーカイブ (下記) を、今回のアーカイブ処理が正常に完了したあと全て削除する。 |
--level=1 | やはりインクリメンタルバックアップだが、前回のインクリメンタルバックアップ時に作成あるいは更新されたスナップショットファイルを参照して、前回からの増分をアーカイブし、同スナップショットファイルを更新する。少なくとも Level-0 アーカイブとスナップショットファイルが存在しなければ、動作を拒否する。Level-1 の結果にできるアーカイブは作成時のタイムスタンプを利用して <TARGET>_yymmddHHMM.<EXT> となる。 |
オプションなし | 通常のバックアップを行う。存在するインクリメンタルバックアップやスナップショットファイルには一切タッチしない。定期外のフルバックアップを行う場合や、そもそも常にフルバックアップしか行わない人に適している。 |
--clean | インクリメンタル Level-1 の一連のアーカイブ全てと、既に *.snar.back に改名されている不要になったスナップショットファイルを削除する。 |
--cleanall | インクリメンタル Level-0, Level-1 の一連のアーカイブとスナップショットファイルを削除する。 |
--kill | 上記に加え、インクリメンタルでない通常のアーカイブも削除する。 |
--status | アーカイブの簡単な一貫性チェックを行い状態を表示する。フルバックアップの有無や、インクリメンタルバックアップのアーカイブがあるのにスナップショットファイルがなかったり、 Level-1 アーカイブ群のうち最も古いものより Level-0 アーカイブのほうが新しいといった異常が点検できる。 |
--list | 有効なターゲットをリストアップする。 |
hoge というユーザは `sample' ターゲットのフルバックアップを 1 日おきに取りたいとしよう。実行時刻は朝 3:30 とする。
hoge$ crontab -e
とコマンドすると vi か何か (システムの設定に依存) が開き、ユーザ専用 crontab ファイルの編集状態になるので、下のように入力してから保存終了する:
30 3 */2 * * /usr/local/bin/tarbut sample
するとジョブは自動的に /var/spool/cron/hoge ファイルとして保存され、準備OKとなる。 cron デーモンは 1 分毎に /var/spool/cron/ の変更を監視しているので、 cron を再起動する必要はない。
ここでは、せっかくなので、インクリメンタルバックアップを取り入れた、少しだけ複雑なスケジュールを組むことにしよう。ポリシーは:
下記のようなスクリプト (仮に systemtarbut としよう) を作ってそのパーミションを root.root 700 にする。そして、例えば /etc/cron.misc/ など、既存の cron スケジュールで run-parts に網羅されていないディレクトリに置く。 /usr/bin/run-parts は、 RedHat 系で crontabs パッケージとともにインストールされるシェルスクリプトで、指定したディレクトリ内にあるクリプトファイル (ただしファイル名が `~' または `,' で終わるものを除く) を全て実行するというもの。
#!/bin/sh if [ "$1" = "1" ]; then TARBUT="/usr/local/bin/tarbut --level=1" else TARBUT="/usr/local/bin/tarbut --level=0" fi $TARBUT sample $TARBUT some_else
root で実行するジョブとして登録するので /etc/crontab をエディタで開き、以下の記述を追加する:
05 4 * * 0 root /etc/cron.misc/systemtarbut (日曜日の Level-0) 05 4 * * 1-6 root /etc/cron.misc/systemtarbut 1 (月~土曜日の Level-1)
上の /etc/crontab 編集時には、ファイル冒頭にある環境変数宣言も確認しておこう。もし `HOME=/' という記述があれば、ここで cron ジョブ化する tarbut 用の tarbut.conf は、ルートファイルシステム直下に置かなければならない。変数宣言の内容のほうを変えるのは、他のジョブに影響を及ぼす可能性があるのでやめたほうがいい。
以上で準備は完了。 cron は 1分毎に /etc/crontab の最終更新日時をチェックしているので、 cron を再起動する必要はない。 sample ターゲットを例にすれば、 $DESTDIR 及び $CACHEDIR には、日曜の朝には sample_lev0.tar と sample.snar ができ、月曜には前日からの増分だけの sample_05200405.tar ができ、火曜には sample_05210405.tar が....という具合で、期間の終わりである土曜の朝には、
sample.snar sample_lev0.tar sample_05200405.tar sample_05210405.tar sample_05220405.tar sample_05230405.tar sample_05240405.tar sample_05250405.tar
というワンセットのバックアップが揃うことになる。言うまでもないが、なるべく毎日、未明にできたアーカイブと更新されたスナップショットファイルを別の媒体にコピーすることをお勧めする。なお、 Level-1 アーカイブはコピーでなく「移動」でもいいが、 tarbut --status の際の一貫性チェックの関係で、 Level-0 アーカイブは、ハードディスクに余裕があれば $DESTDIR にも残しておきたい。スナップショットファイルは必ず残さないと意味がない。リストア方法は前ページでクドいほど説明した。
Fedora Core 3 では、GNOME + Nautilus に CD/DVD ライターが統合されているので、「プログラムを指定して実行」窓かファイルブラウザのロケーションバーで、 burn:/// と入力するだけで、あとは直感的に書き込みができる。この CD/DVD ライティングアプリケーションは、リライタブルメディアであれば、まず全体を高速消去してから書き込みを行うようになっているようだ。
書き込みがうまくいかない時は以下の項目を検討してみる:
上記の Nautilus GUI アプリケーションも、舞台裏を覗いてみれば実は mkisofs と cdrecord を使っている。だったらということで、 BASH スクリプトで書き込みルーティンを作ってみた。この write-cd スクリプトは、CD-RW を (必要であれば) 一旦消去してから、引数で指定したディレクトリにあるファイル/ディレクトリを RW に書き込む (パケットライトではない)。 -v オプションを指定しない限りは、エラー時以外は一切メッセージを出さないようにしたので、 cron に組み込むにも便利だろう。現状、筆者の環境には DVD-RW ドライブはないので、DVD の場合は cdrecord や mkisofs に渡すオプション引数の部分を多少調整する必要があると思われる。
留意点がふたつ。 cdrecord は一部 root 権限がないと達成できないシステムコールを行うので、 write-cd スクリプトファイルは所有者もグループも root にして、 SetUID root (`chmod 4755') しておいたほうがいい(※)。また、同スクリプト冒頭の環境変数設定のうち、
DEV=/dev/hdc
という部分は、 Fedora Core 1/2 と 3 とでは指定方法が異なる。これは途中から cdrecord の仕様が変わり SCSI エミュレーションが不要となったため。 1 や 2 では `1,0,0' などといったもの (`cdrecord -scanbus' でプローブできる) になるが、 Fedora Core 3 では通常のブロックデバイスを指定する。 WRITESPEED=4 も自分のマシンの備える CD-RW のスピードに合わせて適宜直していただきたい。使い方の概略は `write-cd -h' で見られる。
※ 最近のLinux環境ではスクリプトの SetUID root が効かないようになっている。write-cdスクリプトを呼ぶ小さなCラッパーをビルドしてそれを SetUID rootする必要がある。