[Linux] sysstat – HISTORY=28 以上だと sa ログが2重書き込みされる不具合

sysstat 7.1.2 以前で HISTORY=28 以上の設定を行うと、閏年以外の2月のログが削除されず、3月分が上書きされてしまいます。具体的には次のようなログになります。

$ date
Mon Mar 30 13:46:45 JST 2009
$ ls -l /var/log/sa/sa*28
-rw-r–r– 1 root root 474864 Mar 28 23:50 /var/log/sa/sa28
-rw-r–r– 1 root root 539834 Mar 28 23:53 /var/log/sa/sar28
$ ls -l /var/log/sa/sa*29
-rw-r–r– 1 root root 237552 Mar 29 23:50 /var/log/sa/sa29
-rw-r–r– 1 root root 270539 Mar 29 23:53 /var/log/sa/sar29
 
$ sar -c -f /var/log/sa/sar28
Linux 2.6.18-53.1.14.el5 (example.com) 02/28/09
 
00:00:01 proc/s
00:10:01 0.03
00:20:01 0.03
00:30:01 0.03
00:40:01 0.03
00:50:01 0.03
01:00:01 0.03
#### 略 ####
23:00:01 0.03
23:10:01 0.03
23:20:01 0.03
23:30:01 0.03
23:40:01 0.03
23:50:01 0.03
00:00:01 0.14
00:10:01 0.03
00:20:01 0.03
00:30:01 0.03
00:40:01 0.03
00:50:01 0.03
01:00:01 0.03
#### 略 ###
22:20:01 0.03
22:30:01 0.03
22:40:01 0.03
22:50:01 0.03
23:00:01 0.03
23:10:01 0.04
23:20:01 0.03
23:30:01 0.03
23:40:01 0.03
23:50:01 0.03
Average: 0.13

3月のログが2月のログの後に追加で書き込まれるためヘッダは2月扱いになり、2/28の23:59 の後に3/28 の 00:00:00 のログが始まります。
 
この不具合は下記のとおり sysstat 7.1.3 で修正されたようですが、CentOS 5.2 など最新版のディストリビューションでも 7.0.2-1 が最新だったりするので、HISTORY=27 として対応するなど注意が必要です。

2007/03/27: Version 7.1.3 – Sebastien Godard (sysstat <at> wanadoo.fr)
 * Better support for keeping sar logfiles for more than one month
  (several bugs fixed in sa1 and sa2 scripts).

 
sysstat 7.1.3 以降は保存形式が変わって HISTORY=28 の制限がなくなり、一年以上でも保存できるようになったようです。
 
参考:
SYSSTAT utilities home page(公式)

[Linux] sar の結果からグラフレポートを作成する

kSar(http://ksar.atomique.net/) が便利。
sar のテキスト出力を GUI インターフェイスでレポート形式に変換し、PDF や画像ファイルで保存できます。
 
sysstat で性能管理
kSar 公式(English)
kSarでsarデータをグラフにする
kSarによるsarのグラフ化について

Linuxで間違って削除したファイルを復元する

Sleuth kit というフォレンジックツールを使うとよさそうです。Autopsy という別ツールを組み合わせれば web ベース GUI が利用できます。
 
対象ファイルシステムは: NTFS, FAT, UFS1, UFS2, EXT2FS, EXT3FS, ISO9660 です。
 
Windows 系ファイルシステムや CD などメディアのデータ復旧にも使えるのがいいですね。
 
なお, FreeBSD ports は sysutils/sleuthkit, sysutils/autopsy にあります。
参考:
http://www.gentoo.org/news/ja/gmn/20080728-newsletter.xml

tput – 端末入力で遊ぶ

wget のプログレスバーや, lynx, w3m などの CUI ブラウザ、NetHack などの CUI ゲームなどのように、CUI 画面内の一部だけを動的に書き換える、という処理を実装する方法をメモしておきます。
 
基本として、CUI の描画方法を制御するには特定のキーコードに割り当てられている画面描画の処理命令を利用します。
標準出力に出力された時点で命令が実行されるので、制御コードを知ってさえいれば echo だけで画面描画が実現できてしまいます。次のように入力すると画面をクリアします。

$ echo \


capname については man terminfo の Cap-Name を参照。
例:

% zmodload zsh/terminfo
% echoti setaf 2
% echo Hello,world
Hello,world

参考: http://d.hatena.ne.jp/lurker/20061121
 
・tput
zsh 以外のシェルからでも使える汎用プログラム。Curses や NCurses に含まれています。なお、FreeBSD に最初から含まれている /usr/bin/tput は Curses 版です。
Curses 版の書式 (FreeBSDなど):

$ tput <TCapCode> [ 引数… ]

TCapCode については man termcap の TCapCode を参照。
 
NCurses 版の書式 (FreeBSD ports や Linux など):

$ tput <CapName>

CapName については man terminfo の Cap-Name を参照。
curses よりこちらのほうが一般的です。

curses tput の例:
15 行 20 文字目に文字列 OK を書き込む。

$ tput sc; tput cm 20 15; tput al;echo OK; tput rc

文字に下線を入れる

$ tput us ; echo hello; tput ue

Ncurses tput の例:
文字の色を赤に。

$ tput setaf 1

白に戻す。

$ tput setaf 9

参考: http://www.ibm.com/developerworks/jp/linux/aix/library/au-learningtput/index.html

複数ファイルの中身を一括置換するには

複数のファイルをまとめて置き換えたいときは sed を使うと便利です。
 
たとえばこのようにすると指定したディレクトリの中のすべての .html ファイルの内容を 2007年 から 2008 年に書き換えることができます。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  mv $i $i.bak
  sed -e ‘s/2007年/2008年/g’ $i.bak > $i
done

影響があったファイルの一覧を得るには次のようにします

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  diff -q $i.bak $i
done

影響があったファイルの内容を比較する場合も diff を使います。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  echo $i
  diff $i.bak $i
done

問題に気づいて元に戻したいときは *.bak を移動させれば元に戻ります。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  mv $i.bak $i
done

変更が問題なければ .bak ファイルを削除して置換完了です。

for i in `find /var/www/html -type f -iregex ‘.*\.html’`; do
  rm $i.bak
done

参考:
sed の使い方

sed の使い方

書式:

sed -e ‘コマンド1’ [-e ‘コマンド2’ […]] [‘ファイル名’]

指定したファイル(省略した場合は標準入力)を入力テキストとして読み込み、
-e で指定したスクリプトを順番に実行して標準出力に書き出します。
 
-e で指定できる書式は次のようなものがあります。
 
■命令のみ指定するタイプ( -e 命令 ):

-e p

行バッファの内容を出力(Print)する。バッファの内容は操作しないため、-ep -ep のように複数指定すると指定回出力されます。バッファの消去には後述の d を使用します。また、全てのスクリプトを実行したあとにもバッファ内容が出力されます。

-e d

行バッファの内容を消去(Delete)する。通常、p 命令と組み合わせて利用します。
 
■正規表現パターン + 処理内容指定をするタイプ(-e /正規表現/命令):

-e /^foo/p

foo から始まる行を出力します。

-e ‘/boO$/d’

boO で終わる行をバッファから削除します。

-e ‘/[J][j]ohn/p’

John または john が含まれる行を出力します。
 
■正規表現による置換(-e s/正規表現/置換内容/オプション)

-e s/foo/boo/

各行バッファで最初に見つかった foo を boo に置換します。

-e ‘s/a/A/g’

バッファ中の全ての a を A に置換します。

-e s/john/JOHN/ig

バッファ中の全ての john(大文字小文字問わない)を大文字の JOHN に統一します。
 
例:

echo “Hallo, world” | sed -e s/a/e/

typo を訂正する(Hallo -> Hello)。

cat /var/log/httpd/access_log | sed -e ‘/GET/p’ -e ‘/^127\.0\.0\.1/p’ -ed

Apache のローカルからの GET アクセスを抽出する。

作業中のプロセスを残したまま端末を終了する

何かをビルドしていて、時間が予想外にかかってしまい、nohup もしていないためになかなか端末を閉じることができずに予定が狂って結局残業時間がのびる、なんてことありませんか?
 
そういうときは disown を使うと実行中のジョブを端末から切り離すことができます。
(以下は bash の例です)

$ make # 何か時間のかかる作業
## 出力は略。途中で端末接続を切りたくなった(Ctrl-Z) ##
^Z
[1]+ Stopped make
 
$ bg # バックグラウンドで実行を再開
## 略。出力再開 ##
 
$ disown # 端末を切ってもプロセスが残るようにカレントジョブ( %1 )を端末から切り離す
$ exit # 終了

nohup と違い端末を切ると標準出力、エラー出力はどこにも出力されなくなりますが、これで端末を切断してもプロセスが実行されたままになります。
 
参考:
一度 tty から起動したプロセス(PBD)
一度 tty から起動したプロセス (csh/ksh 版)(にぽたん研究所)

tips – ltrace で共有ライブラリの関数呼び出しをトレースする

http://0xcc.net/blog/archives/000103.html
より。

ltrace [ {オプション} ] {実行するプログラム} [ {引数..} ]

のようにすると、任意のプログラムを動的にブレークポイントを挿入して実行し、共有ライブラリとシステムコールの呼び出しをトレースすることができます。
簡単にいうと GDB より高級なデバッガのような感じです。
 
Linux の場合は標準でインストールされることが多そうですが、yum や apt-get を使う場合は

yum install ltrace

または

apt-get install ltrace

でインストールできます。
また FreeBSD の場合も ports に含まれているため、

portinstall sysutils/ltrace

とすることでインストールできます。
 
利用例:
ls / を実行しライブラリ関数をトレースし、log.txt にトレース結果を出力する(-o)。

$ ltrace -o log.txt ls / # プログラムの実行とトレース
bin dev home lib media mnt proc sbin tmp var
boot etc initrd lost+found misc opt root sys usr
$ cat log.txt # トレース結果参照
_start(0xbff1abd4, 0xbff1abd7, 0, 0xbff1abd9, 0xbff1abf0 <unfinished …>
__libc_start_main(0x804ea41, 2, 0xbfe34df4, 0x8056f58, 0x8056fac <unfinished …
setlocale(6, “”) = “en_US”
bindtextdomain(“coreutils”, “/usr/share/locale”) = “/usr/share/locale”
textdomain(“coreutils”) = “coreutils”
__cxa_atexit(0x8053701, 0, 0, 0x805b570, 0xbfe34d68) = 0
is_selinux_enabled(0x8053701, 0xd1bbe4, 0x32206e75, 0x34312036, 0x3a30333a) = 0
isatty(1) = 1
# 以下略

利用例2:
ライブラリのトレースだけでなくシステムコールもトレース(-S)し、
関数呼び出しの関係を階層構造で出力する(-n {number})。

$ ltrace -o log2.txt -S -n 2 ls /
# 略

$ cat log2.txt
SYS_uname(0xbfe4106c) = 0

SYS_brk(NULL) = 0x99ca000
SYS_access(0xbffdc1, 4, 0xc03fd4, 0xbffdc1, 4) = -2
SYS_open(“/etc/ld.so.cache”, 0, 00) = 3
# 中略
SYS_munmap(0xb7f92000, 4096) = 0
_start(0xbff20bd4, 0xbff20bd7, 0, 0xbff20bd9, 0xbff20bf0 <unfinished …>
    __libc_start_main(0x804ea41, 2, 0xbfe412c4, 0x8056f58, 0x8056fac <unfinished
 …>
        setlocale(6, “” <unfinished …>
            SYS_open(“/usr/lib/locale/locale-archive”, 32768, 01540) = 3
            SYS_fstat64(3, 0xd301a0, 0xd2eff4, 0xd301a0, 0xd0fc60) = 0
            SYS_mmap2(0, 0x200000, 1, 2, 3) = 0xb7d82000
            SYS_mmap2(0, 204800, 1, 2, 3) = 0xb7d50000
            SYS_mmap2(0, 4096, 1, 2, 3) = 0xb7d4f000
            SYS_close(3) = 0
        <... setlocale resumed> ) = “en_US”
# 以下略

利用例3: プログラムの経過時間を一緒に出力する(パフォーマンスチューニング用途など)

$ ltrace -tt -o log.txt ls / # マイクロ秒単位
19:56:53.267749 SYS_uname(0xbff417dc) = 0
19:56:53.267865 SYS_brk(NULL) = 0x9e12000
19:56:53.267917 SYS_access(0xbffdc1, 4, 0xc03fd4, 0xbffdc1, 4) = -2
# 以下略

-t にすると 19:56:53 のように秒単位、-tt にすると例のようにマイクロ秒単位、 -ttt にすると マイクロ秒単位で 1201517930.289331 のような UNIX タイムスタンプ形式になります。

tips – 実行時に共有ライブラリの優先順位を変える(シンボル重複時の動作)

Linux では複数の共有ライブラリ(*.so)に同じ関数が定義されている場合、重複する関数については先に読み込まれた共有ライブラリが利用され、その他のライブラリで定義される重複シンボルについては無視されます。
 
共有ライブラリのロード順序は ld でのリンク順になります。
また、既存の実行可能ファイルのロード順序を調べるには ldd を使います。

$ gcc src.c -o foo -lc -lm # libc.so, libm.so の順にロード
$ ldd foo # foo では libc.so が優先されていることを確認
        libc.so.6 => /lib/tls/libc.so.6 (0x00c07000)
        libm.so.6 => /lib/tls/libm.so.6 (0x00d35000)
        /lib/ld-linux.so.2 (0x00bed000)
$ gcc src.c -o bar -lm -lc # 同じソースに対してリンク順序を入れ替えて bar 作成。
$ ldd bar # bar では libm.so が優先されていることを確認
        libm.so.6 => /lib/tls/libm.so.6 (0x00d35000)
        libc.so.6 => /lib/tls/libc.so.6 (0x00c07000)
        /lib/ld-linux.so.2 (0x00bed000)

 
今回の本題、どうやって共有ライブラリを優先するかを実行時に決めるか?ですが、
LD_PRELOAD を使うことでコンパイル時のロード順を入れ替えることができます。
 

>

$ # LD_PRELOAD に /lib/tls/libc.so.6 (libc.so へのパス)を指定
$ LD_PRELOAD=/lib/tls/libc.so.6
$ ldd bar # 優先順位が変わっていることを確認
        /lib/tls/libc.so.6 (0x00c07000)
libm.so.6 => /lib/tls/libm.so.6 (0x00d35000)
        /lib/ld-linux.so.2 (0x00bed000)

ちなみに、LD_PRELOAD を設定すると、(setuid/setgid されたプログラムなど)一部を除くほとんどのプログラムで任意のライブラリを事前ロードできます。
これを利用すると任意のプログラムのライブラリ定義関数を置き換えることが可能です(参考:hook_tcp.so)。
 
参考:
共有ライブラリの作成(HP テクニカルドキュメント)
 
hook_tcp.so(チームチドリ)

What is hook_tcp.so:
LD_PRELOADを利用して、プログラム中で実行される connect()の接続先をsyslogへ記録するプログラムです。

とのことで、libc.so の CONNECT(2) を hook して動作します。
 
4.17. LIDS を使う時は、LD_PRELOAD 環境変数に注意した方がいいですか?(LIDS FAQ)

tips – grep で前後の行も表示させるには

マッチ行直前の行を表示するには -B {行数}(または –before-context={行数})を、直後の行を表示するには -A {行数}(または –after-context={行数}) を指定します。
 
例: sh のマニュアル(manpage)から grep 検索する。
オプションなし(ただし -n は行番号を表示してわかりやすくするため付加しています)

$ man sh | grep -n UNIX
1378: A sh command, the Thompson shell, appeared in Version 1 AT&T UNIX. It
1379: was superseded in Version 7 AT&T UNIX by the Bourne shell, which inher-
1383: Bourne shell from AT&T System V.4 UNIX.

 
直前と直後 1 行ずつ表示

$ man sh | grep -n -A 1 -B 1 UNIX
1377-HISTORY
1378: A sh command, the Thompson shell, appeared in Version 1 AT&T UNIX. It
1379: was superseded in Version 7 AT&T UNIX by the Bourne shell, which inher-
1380- ited the name sh.

1382- This version of sh was rewritten in 1989 under the BSD license after the
1383: Bourne shell from AT&T System V.4 UNIX.
1384-