2013-12-15

CentOS 6.3 で確認した Apache アクセスログのローテーション設定

CentOS 6.3 でのお話。


Apacheをyumでインストールすると、Apacheログのローテート設定ファイルが作られる。

# less /etc/logrotate.d/httpd
/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
    delaycompress
    postrotate
        /sbin/service httpd reload > /dev/null 2>/dev/null || true
    endscript
}



logrotateはcronで実行されるようになっている。

# ls /etc/cron.daily
logrotate




このlogrotateの中身を確認すると

#!/bin/sh

/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0



となっていて、以下の /etc/logrotate.conf の定義をもとにログをローテートしている。

# 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

# use date as a suffix of the rotated file
dateext

# 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 and btmp -- we'll rotate them here
/var/log/wtmp {
    monthly
    create 0664 root utmp
        minsize 1M
    rotate 1
}

/var/log/btmp {
    missingok
    monthly
    create 0600 root utmp
    rotate 1
}

# system-specific logs may be also be configured here.





というわけで、CentOS 6.3 デフォルトの場合、Apacheのログファイルは

weekly
週次にローテーション。

rotate 4
4世代前まで残す。

dateext
ローテーション後のログファイル末尾につく数値の代わりに、日付(-YYYYMMDD)をつける。

#compress
圧縮しない。

missingok
ログファイルが存在しない場合にエラーを出力しない。

notifempty
ログファイルが空の場合ローテーションしない。

sharedscripts
ログファイルを複数指定した場合、それぞれpostrotate、prerotate内のコマンドを実行する。

delaycompress
次回のログローテーションサイクルになるまで圧縮しない。
※compressが指定されていないため、次回のログローテーションサイクルになっても、圧縮されない。

postrotate
/sbin/service httpd reload > /dev/null 2>/dev/null || true
endscript
ログローテーション後に、httpd reload を実行する


となる。

apache アクセスログの圧縮

# less /etc/httpd/conf/httpd.conf
CustomLog "| /usr/sbin/rotatelogs /var/log/httpd/access_log.%Y%m%d 86400 540" combined


上記のように出力されているapacheアクセスログファイルを圧縮するスクリプト

#!/bin/bash
LOGDATE=`date -d '1 days ago' +'%Y%m%d'`
gzip /var/log/httpd/access_log.$LOGDATE


このスクリプトをcronで仕込めばOK。


参考にさせてもらったところ
■15分で理解するApacheのログローテート
http://wimax.blog64.fc2.com/blog-entry-91.html

2013-11-21

Google Maps JavaScript API バージョン 2(V2) 復活!?

2013/11/20 にV2のまま放置したサイトを見てみたら、地図が出ていなかった。

そりゃね、延期された Google Maps JavaScript API バージョン 2(V2)のサポートも、2013/11/18 に終わっちゃったしね。

と思って、11/21 にV2のまま放置した同じサイトを見てみたら、


でてるやん!


ということで、復活したのかなぁ

2013-09-13

空のディレクトリを削除する

find コマンドって便利ね。



空のディレクトリを探す
find . -type d -empty


空のディレクトリを削除する
find . -type d -empty -delete





2013-09-12

find xargs grep で、PHP のソースを検索

拡張子が .html のファイルから、PHP のソースが記述されているファイルを抽出する必要があったので、find と xargs と grep の合わせ技で検索してみた。

こんな感じ。

cd /var/www/html
find . -name "*.html" -type f -print0 | xargs -0 grep -P '<\?[\s\S]*?\?>'

grep の -P オプションで複数行を対象とし、

正規表現の [\s\S]*? で改行が含まれている範囲も検索している。





なぜ、拡張子が.html のファイルを対象にしているかって?

それはね、.htaccess に

AddType application/x-httpd-php .php .html

って定義してあったサイトをバックアップから戻したときに

「サーバでPGが動くファイルは、戻さないで」

っていうオーダーがあったから。

じゃあ、なぜそんなオーダーが来たかというと...云わずもがなです。






ちなみに、<??> で囲まれている箇所を抽出しているので、

<?xml version="1.0" encoding="UTF-8"?>

みたいな XML宣言 も抽出されてしまう。





OpenIDについてぼやく

FacebookもYahooもmixiもOpenIDに対応してるって言ってるけど、肝心のOpenIDを使ってログインできるページがどこにも見当たらない。

googleアカウント持ってるから、googleのOpenID使ってログインしたいんだけど、する場所がない。

googleにもない。

俺の理解が不足しているのか、探し方が下手なのか。

そもそも、OpenIDって、サイト間でログイン情報(ID、パスワード)を共有しましょうっていうあれでしょ?

Facebookアカウント持ってないし、Yahoo!のアカウントも持ってないし、mixiなんかもってのほか。

よくわからんのだが、googleのOpenIDでFacebookやYahoo!にログインして、FacebookやYahooのアカウントが無かったら、新しく作るページかなんかが開くと思ってるんだが、違うか?

教えて。

2013-08-25

MySQLiクラスをラップする

<?php
/**
 *  MySQLiクラスの拡張
 */
class MySQLiDB extends mysqli {

    private $_host;
    private $_user;
    private $_pass;
    private $_db;
    private $_char;
    private $_timeout;

    public function __construct($host, $user, $pass, $db, $char, $timeout) {

        parent::init();

        //  AUTOCOMMIT オフ
        if (!parent::options(MYSQLI_INIT_COMMAND, "SET AUTOCOMMIT = 0")) {
            die("Setting MYSQLI_INIT_COMMAND failed");
        }

        //  接続のタイムアウト 設定
        if (!parent::options(MYSQLI_OPT_CONNECT_TIMEOUT, $timeout)) {
            die("Setting MYSQLI_OPT_CONNECT_TIMEOUT failed");
        }

        //  接続
        if (!parent::real_connect($host, $user, $pass, $db)) {
            die("Connect Error (" . mysqli_connect_errno() . ") " . mysqli_connect_error());
        }

        //  デフォルトのクライアント文字エンコーディング 設定
        if (!parent::set_charset($char)) {
            die("Error loading character set {$char}: %s\n" . $mysqli->error);
        }

        //  ローカル変数に保存
        $this->_host = $host;
        $this->_user = $user;
        $this->_pass = $pass;
        $this->_db   = $db;
        $this->_char = $char;
        $this->_timeout = $timeout;

    }
}

2013-08-23

ファイル名やディレクトリ名にスペースを含むときに使える!chmodで再帰的にアクセス権を設定する

現在のディレクトリ以下のファイルとディレクトリに対して、再帰的にアクセス権を設定するには

find . -type f -print0 | xargs -0 chmod 644
find . -type d -print0 | xargs -0 chmod 755

とする。

-print0 | xargs -0

とすることで、名前にスペースを含むファイルやディレクトリが存在しても、処理してくれる。

2013-08-21

mb_send_mail() するなら mb_convert_encoding() は不要

おれも勘違いしていたが、mb_send_mail()を使ってメールを送るとき、mb_convert_encoding()使って、ISO-2022-JPへ変換しなくていい。

具体的には、たったこれだけ。

<?php
//  日本語だよって宣言して
mb_language('ja');

//  UTF-8で組んでるなら、文字エンコードをUTF-8に合わせて
mb_internal_encoding("UTF-8");

//  mb_send_mail()するだけ
mb_send_mail("dare@doko.koko", "メールの送信テスト", "これは、メールの送信テストです。");

これだけで、
件名は、mb_encode_mimeheader(mb_convert_encoding("件名", "ISO-2022-JP", "UTF-8"), "ISO-2022-JP") してくれるし
本文は、mb_convert_encoding("本文", "ISO-2022-JP", "UTF-8") してくれる。

ただ、Fromに日本語使いたいときだけは、自前で

$from = mb_encode_mimeheader(mb_convert_encoding("おれから", "ISO-2022-JP", "UTF-8"), "ISO-2022-JP")."<ore@doko.soko>";

ってやってから

mb_send_mail("dare@doko.koko", "メールの送信テスト", "これは、メールの送信テストです。", "From:".$from);

って送ってあげる。

2013-07-25

SetEnvIf の Request_URI には Query String は含まれない

クエリストリングを含んでいたときだけ、ログに記録したくなかたったので、調べてみた。

まぁ、結論はタイトルに書いてあるのだが、

これを知らなかったおかげで、1時間無駄にした。


じゃあ、どうやって制御するかというとこんな感じ。

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_URI} ^/hogehoge/index\.html
    RewriteCond %{QUERY_STRING} id=123
    RewriteRule ^(.*)$ $1 [E=nolog:1]
</IfModule>
CustomLog "| /usr/sbin/rotatelogs /var/log/httpd/access_log.%Y%m%d 86400 540" combined env=!nolog


/hogehoge/index.html
はログに記録されるけど

/hogehoge/index.html?id=123
はログに記録されない。

2013-07-12

HTTP_HOST と SERVER_NAME の違い

あざーっす。

http://phpspot.org/blog/archives/2006/05/server_namehttp.html


--2013/9/17 追記--
上の説明を読んで色々試してみたんだけど、やっぱり違いがわからなかった。
そんな時は、やっぱりドキュメント読まなきゃね。

■Apache HTTP サーバ バージョン 2.2 ドキュメント
UseCanonicalName ディレクティブ

2013-07-01

maillogの世代管理

maillogの世代管理を、デフォルトの4週間から1年(54週)に延ばす。

# cd /etc/logrotate.d/
# cp syslog mail
# vi mail

/var/log/maillog {
    rotate 54
    sharedscripts
    postrotate
        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}


# vi syslog

/var/log/messages /var/log/secure /var/log/spooler /var/log/boot.log /var/log/cron {
    sharedscripts
    postrotate
        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}

2013-06-25

header()で飛ばされてもリファラは

例えば、

http://www.example.dom/a.html



<a href="./b.php">リンク</a>

のリンクを踏んで、b.php へ飛んだとする。

b.php が

<?php
header("Location: ./c.php");
?>

で、c.php が

<?php
echo $_SERVER['HTTP_REFERER'];
?>

だった場合、c.php で echo されるリファラは

http://www.example.dom/a.html

となる。

2013-04-05

postfix キュー削除

キューに溜まったメールを表示する。
mailq

キューに溜まったメールの配信・転送を停止する。
postsuper -h ALL

キューに溜まったメールから、特定のメールアドレスが含まれるメールを抽出する。
mailq | grep hoge@example.com

キューに溜まったメールを消す。
postsuper -d メッセージID

停止した配信・転送を再開する。
postsuper -r ALL


今日はこれで救われた。

2013-04-04

httpdの監視


httpdの監視

top -b -d 1 -n 10 1>> /var/tmp/top_`date +\%Y\%m\%d`.log 2>> /var/tmp/top_`date +\%Y\%m\%d`.log
apachectl fullstatus 1>> /var/tmp/httpd_status_`date +\%Y\%m\%d`.log 2>> /var/tmp/httpd_status_`date +\%Y\%m\%d`.log

こいつらをcronへ登録。

2013-04-03

PHPで非同期処理

こんな感じで開始時刻をミリ秒ではいて、5秒間待つだけの処理を

test_mulitproc.php
<?php
list($microSec, $timeStamp) = explode(" ", microtime());
error_log(date('Y-m-d H:i:', $timeStamp) . (date('s', $timeStamp) + $microSec));
sleep(5);



同じくこんな感じで実行してみると

test_multiexec.php
<?php
error_log('同期処理開始');
exec('php /var/www/test_multiproc.php 1> /dev/null 2>&1');
exec('php /var/www/test_multiproc.php 1> /dev/null 2>&1');
exec('php /var/www/test_multiproc.php 1> /dev/null 2>&1');
exec('php /var/www/test_multiproc.php 1> /dev/null 2>&1');
exec('php /var/www/test_multiproc.php 1> /dev/null 2>&1');
error_log('非同期処理開始');
exec('nohup php /var/www/test_multiproc.php > /dev/null &');
exec('nohup php /var/www/test_multiproc.php > /dev/null &');
exec('nohup php /var/www/test_multiproc.php > /dev/null &');
exec('nohup php /var/www/test_multiproc.php > /dev/null &');
exec('nohup php /var/www/test_multiproc.php > /dev/null &');



実行結果はこんな感じ。

[01-Apr-2013 20:28:30] 同期処理開始
[01-Apr-2013 20:28:30] 2013-04-01 20:28:30.742573
[01-Apr-2013 20:28:35] 2013-04-01 20:28:36.031054
[01-Apr-2013 20:28:40] 2013-04-01 20:28:41.291464
[01-Apr-2013 20:28:46] 2013-04-01 20:28:46.56506
[01-Apr-2013 20:28:51] 2013-04-01 20:28:51.827265
[01-Apr-2013 20:28:56] 非同期処理開始
[01-Apr-2013 20:28:57] 2013-04-01 20:28:57.468525
[01-Apr-2013 20:28:57] 2013-04-01 20:28:57.616613
[01-Apr-2013 20:28:57] 2013-04-01 20:28:57.644256
[01-Apr-2013 20:28:57] 2013-04-01 20:28:57.69278
[01-Apr-2013 20:28:57] 2013-04-01 20:28:57.70006

2013-03-20

MTAとMUAと配信と転送と中継

メールのおべんきょ。


MTA(Mail Tranfer Agent)
メールを配信したり転送を行うプログラムのこと。メールサーバはこの機能を有している。


MUA(Mail User Agent)
MTA機能を持たず、ユーザのフロントエンドとして、電子メールの表示や読み書き、そしてメールサーバへの送信やサーバの自分のメールボックスからのメールの受信に専念するプログラム。


配信(delivery)
MTAが自分の所のローカルユーザーのメールボックスへメールを送る事を「配信」と言
う。



転送(transport)
送信先が自分の所のローカルユーザーでない場合、他のメールサーバーへメールを送る必要がある。MTAが他のメールサーバーへメールを送る事を「転送」と言う。


中継(relay)
MTAが、MUAや他のMTAからのメールを「転送」することを「中継」という。


とこんな感じかな。

転送と中継の説明がなってないような気がするが...

ツッコミよろしくです。

2013-03-18

そのまま移行してやがった

...だめだ、ここ。

昔のソース、そのまま移行してやがった。

外部入力値は、htmlspecialchars()通すか、htmlentities()通すかのどっちかなのに、

昔のソースそのまま移行してあったから(っていうか、昔も間違ってんだけど)

stripslashes()通した結果に対して、

「"」を「&quote;」にreplace()してた。

呆れてものもいえない。

2013-03-15

何もかもが中途半端

もう少し深いところで仕事がしたい。

あれもこれも何もかも一人でこなす

そんなスーパーな人材を

400万で雇おうなんて

都合が良すぎる。

2013-02-28

PHPで2重ポスト対策 その2 同一ページ内で処理

流れのサンプルなので、XSSとかSQLインジェクションとか考慮してません。

なので、使うときは考慮してね。

<?php
$act    = isset($_POST['act'])  ? $_POST['act']  : 'init';
$data   = isset($_POST['data']) ? $_POST['data'] : '';
$ticket = '';

session_start();

//  初期化処理
if ($act == 'init') {
    $act = 'check';
}
//  チェック処理
else if (($act == 'check') || ($act == 'regist')) {
    if (チェック処理正常) {
        $act = 'regist';
        $ticket = time();
        $_SESSION['ticket'] = $ticket;
    }
    //  登録処理
    else {
        //  2重ポスト時
        if (empty($_SESSION['ticket'])) {
            //  2重ポストされたときの処理
        }
        //  不正なポスト時
        else if ($ticket != $_SESSION['ticket']) {
            //  不正な処理
        }
        //  正常なポスト時
        else {
            //  先にチケットを空にする
            unset($_SESSION['ticket']);

            //  登録処理
            if (登録処理正常) {
                header("Location: 登録完了ページのURL", true, 303);
                exit();
            }
            else {
            }
        }
    }
}
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>sample</title>
<meta name="keywords" content="">
<meta name="description" content="">
</head>
<body>
<form method="post">
<input type="text"   name="data"   value="<?php echo $data ?>">
<input type="submit" value="<?php echo $caption ?>">
<input type="hidden" name="ticket" value="<?php echo $ticket ?>">
<input type="hidden" name="act"    value="<?php echo $act ?>">
</form>
</body>
</html>

2013-02-23

リクエストはキャンセルできない

基本的なことを知らなかった。


<?php
$fp = fopen('/var/tmp/hoge.txt', 'wb');
for ($i = 0; $i < 1000; $i++) {
    //  カウンタをファイルへ出力
    fwrite($fp, "{$i}\n");
    //  1秒待機
    sleep(1);
}



千秒カウントし、カウンタをファイルに出力するPGを作ってみた。

このPGへブラウザからアクセスして、途中で [ Esc ] キーでキャンセルした。



つもりだった。



で、サーバー上のファイルを less コマンドで開いて [Shift] + G で何気に表示を更新していたら、ファイルにカウンタが出力され続けていた。



・・・一度受け付けたリクエストは、サーバー上で処理が終わるか、サービスが再起動されるまで処理され続けることがわかった。





知らなかったおれ、恥ずかしい・・・

2013-02-19

postgresqlでシーケンスの一覧を取得する

SELECT relname 
FROM pg_class AS c 
JOIN pg_namespace AS n 
ON (c.relnamespace = n.oid) 
WHERE c.relkind = 'S' 
AND n.nspname = 'public';

2013-01-29

PDOのプリペアドステートメントで同じ名前のパラメータマーカは使えない

PDOでプリペアドステートメントを使うとき、クエリ中にパラメータマーカを指定するが、このとき、同じ名前のパラメータマーカは使えない。
でも、使ってもエラーにならない。

クエリの結果を精査すれば、クエリで想定した結果にならないためわかるのだが、テストが嫌いな俺は、クエリそのものを検査することにした。

//  すべてのパラメータマーカを取得する。
preg_match_all('/(:[\w]+)/', $sql, $matches);

//  パラメータマーカの出現回数を得る。
$param = array_count_values($matches[1]);

//  2回以上出現したパラメータマーカを調べる。
foreach($param as $key => $val) {
  if($val > 1) {
    echo "パラメータマーカ {$key} を {$val} 個検出しました。";
    //  ここでエラーを発生させるか、強制終了する。
  }
}

こんな感じのロジックをPDOを継承したクラスに仕込んでおけばOK。

class dao extends PDO
{
    const DAO_ERROR_PARAMETER_MARKER_DUPLICATED = 777;

    //  今回必要なところだけ記述
    public function prepare($statement, $options = array())
    {
        preg_match_all('/(:[\w]+)/', $sql, $matches);
        $param = array_count_values($matches[1]);
        foreach($param as $key => $val) {
            if($val > 1) {
                //  エラーモードに応じて、例外を発生させるか、falseを返す。
                $err_mode = $this->getAttribute(PDO::ATTR_ERRMODE);
                if($err_mode === PDO::ERRMODE_EXCEPTION) {
                    throw new Exception("パラメータマーカ {$key} を {$val} 個検出しました。"
                                      , self::DAO_ERROR_PARAMETER_MARKER_DUPLICATED);
                }
                else {
                    return false;
                }
            }
        }
        return parent::prepare($statement, $options);
    }
}

postgresqlでunixtimeを使う

timestamp型からの変換しかできないので、timestamp型にキャストしてから。

SELECT EXTRACT(EPOCH FROM TIMESTAMP WITHOUT TIME ZONE '2013-04-05 06:07:08');

結果:
1365109628

2013-01-25

.htaccessで403を404で返す

久しぶりにリライトルールを書いたよ。 ドキュメントルートに.htaccessを置く場合の記述↓ # 403.html は実体がなくてよい。この設定をすることで、あっても使われなくなる。 ErrorDocument 403 /403.html # 独自の404ページをドキュメ...