ラベル PHP の投稿を表示しています。 すべての投稿を表示
ラベル PHP の投稿を表示しています。 すべての投稿を表示

2011年12月21日水曜日

is_writable の実装探しの旅

pgpoolAdmin のインストール時のエラーの続き。

selinux が原因だったので、 is_writable の実装探しの旅に出ても結局分からなかったと思われる。
が、気になったので、実装探しの旅に出てみることにした。

emacs + cscope で読むことにする。
php のソースコードを展開して、cscope のファイルを作成。
$ cd php-5.3.8
$ find ./ -name "*.h" -or -name "*.c" > cscope.files
$ cscope -b -q -k
emacs で展開したディレクトリあたりを開いて、 M-x cscope-find-global-definition is_writable を実行。
まぁそう簡単には見つからない。M-x cscope-find-this-symbol is_writable でいくつか候補が。
その中でそれっぽいのが、
*** ext/standard/php_filestat.h:
<global>[39]                   PHP_FUNCTION(is_writable);
...
*** ext/standard/basic_functions.c:
ZEND_END_ARG_INFO[3194]        PHP_FE(is_writable, arginfo_is_writable)
宣言と定義っぽいのが見つかったが、マクロのようだ・・・

宣言っぽいほうを追っていくと
#define ZEND_FN(name) zif_##name
とか出てくる。zif_iswritable とか作られているのだろうか。

次に定義っぽい方を追っていくと、
const zend_function_entry basic_functions[] = { /* {{{ */
  [snip]
    PHP_FE(is_writable,       arginfo_is_writable)
となっている。
関数ポインタを持った構造体の配列?これを追うのは大変そうだ・・・そもそもの仕組み知らないし・・・呼び出し元が探せない・・・
という訳で、GDB でステップ実行してみることに。

http://d.hatena.ne.jp/anatoo/20111117/1321463886
を参考にまず、まずコンパイル。
$ ./configure --disable-all --enable-debug
$ make
でテスト用のスクリプト(~/test_is_writable.php)を作成。(前回通らないと思ってたが Web 経由だとユーザが異なっていてパーミッションの問題だったかも・・・パーミッション確認後、再度実行したらこのスクリプトは true となった)
<?php
echo is_writable('/usr/local/etc/pgpool.conf');
次に、GDB で実行。
$ gdb sapi/cli/php
(gdb) break zif_is_writable
Breakpoint 1 at 0x509b30: file /usr/local/src/php-5.3.8/ext/standard/filestat.c, line 1092.
(gdb) run ~/test_is_writable.php
後は、s と n で辿っていく。
ext/standard/filestat.c:782 の php_stat が実装のようだ。
さらに入っていくと、
eck_open_basedir_ex (path=0xb03350 "/usr/local/etc/pgpool.conf", warn=1)
    at /usr/local/src/php-5.3.8/main/fopen_wrappers.c:297

 if (PG(open_basedir) && *PG(open_basedir)) {
あたりだろうか。
PG の定義は、
# define PG(v) (core_globals.v)
extern ZEND_API struct _php_core_globals core_globals;
_php_core_globals の open_basedir が設定されているかどうかを見ているっぽい。
が、open_basedir が分からない。
ググッて見ると、php.ini にそういう項目があるようだ。
このあたりをいじればいけるかと思ったがそうでもなかった。

テストスクリプトだと true となるのでこのあたりで断念。
結局追えなかった・・・

2011年12月12日月曜日

pgpoolAdmin のインストール時のエラー

今回は、pgpoolAdmin を検証するためにインストールしていた時に嵌まったことを。

pgpoolAdmin を展開後、http://www.example.com/pgpoolAdmin/install/index.html にアクセスしてインストール作業を続けるわけだが、/usr/local/etc 以下の pgpool.conf や pcp.conf が "Write access denied" となっていて先に進めない。

ファイルのパーミッションや所有者を確認してみるが特に問題はなさそうだ。
PHP で作られているようなので中身を見てみることにした。

install/checkParameter.php 60
if (!is_writable($pgpool2_config_file)) {
  $msgPgpoolConfigFile = 'Write access denied';
  $error = TRUE;
}
となっている。

実行ユーザを調べるために
echo `id`;
を埋め込んでみるも、想定通りのユーザで動いてそうだ。

スクリプトからパーミッションを取得するために
var_dump(stat($pgpool2_config_file));
を埋め込んでみるも、
["mode"]=> int(33188)
となっており、8 進数で 100644 となり、これも問題なさそう。

is_writable のみを記述したテストプログラムを作成してみるが、この時点で false が帰る。後日、動かしてみたら true だった。おそらくこれはパーミッションだったかな・・・
これは困った。is_writable の実装を探す旅にでるしかないのかと思ったが、ふと英語が出来ないなりに「is_writable always false」でググったら一番上に出てきた。
selinux... すっかり忘れていた・・・コネクション周りなら思い出せたかもしれなかったが。

# setenforce 0
ついでに、/etc/selinux/config の SELINUX を disabled に変更。
で無事にインストールを続けることができた。

続く...

2011年10月21日金曜日

オーバーライド時のアクセス修飾子変更 PHP 編

オーバーライド時にアクセス修飾子の変更を検証。

まず、親クラス。
class ParentClass
{
  private function func1() { echo "Parent private func1\n"; }
  protected function func2() { echo "Parent protected func2\n"; }
  public function func3() { echo "Parent public func3\n"; }

  public function call()
  {
    $this->func1();
    $this->func2();
    $this->func3();
  }
}
private, protected, public の 3 種類のメソッドと、それらを呼び出すメソッドを作成。

子クラス1 は public に変更。
class Child1Class extends ParentClass
{
  public function func1() { echo "Child1 private -> public func1\n"; }
  public function func2() { echo "Child1 protected -> public func2\n"; }
  public function func3() { echo "Child1 public func3\n"; }
}
シンタックスエラーは起きない。

子クラス2 は protected に変更。
class Child2Class extends ParentClass
{
  protected function func1() { echo "Child2 private -> protected func1\n"; }
  protected function func2() { echo "Child2 protected func2\n"; }
  // Access level to Child2Class::func3() must be public (as in class ParentClass)
  // protected function func3() { echo "Child2 protected -> protected func3\n"; }
}
func3 の変更でエラーとなる。public を protected に変更できないようだ。

子クラス3 は private に変更。
class Child3Class extends ParentClass
{
  private function func1() { echo "Child3 private func1\n"; }
  // Access level to Child3Class::func2() must be protected (as in class ParentClass)
  // private function func2() { echo "Child3 protected -> private func2\n"; }
  // Access level to Child3Class::func3() must be public (as in class ParentClass)
  // private function func3() { echo "Child3 public -> private func3\n"; }
}
func2, func3 でエラーとなる。やはり、厳しい方向への変更はできないようだ。

実行してみると、
$p = new ParentClass();
$p->call();
echo "----------\n";
$c1 = new Child1Class();
$c1->call();
echo "----------\n";
$c2 = new Child2Class();
$c2->call();
echo "----------\n";
$c3 = new Child3Class();
$c3->call();
 
結果は、
Parent private func1
Parent protected func2
Parent public func3
----------
Parent private func1
Child1 protected -> public func2
Child1 public func3
----------
Parent private func1
Child2 protected func2
Parent public func3
----------
Parent private func1
Parent protected func2
Parent public func3

親クラスは当然の結果が出ている。
子クラス1の func1 は、親クラスの func1 が private なためオーバーライドできずに、親クラスの func1 が呼ばれている。
子クラス2の func1 も同様。func2, func3 はオーバーライドしていないためそのまま親クラスのものが呼ばれている。
それぞれの子クラスで、call を再定義してやると、それぞれの func1 が呼ばれる。

protected -> public への変更はできるが、public -> protected -> private への変更はできない。
private はオーバーライドできない。