リモートサーバでコマンドを実行するSSH2関数

/ php

PECLのssh2モジュールを使うと、PHPでも簡単にリモートサーバを直接操作することができるっぽいので、セキュリティうんぬんはさておき、試してみた。

PHP: Secure Shell2 関数

まずは、libssh2 のインストール。

# wget http://nchc.dl.sourceforge.net/sourceforge/libssh2/libssh2-0.17.tar.gz
# tar xvfz libssh2-0.17
# cd libssh2-0.17
# ./configure --prefix=/usr/local/
# make all install

続いて、ssh2モジュールをソースから導入。

# wget http://pecl.php.net/get/ssh2-0.10.tgz
# tar xvfz ssh2-0.10
# cd ssh2-0.10
# /usr/local/bin/phpize
# ./configure --with-ssh2=/usr/local/lib
# make

ここで、ひたすらmakeに失敗して焦る。原因は、すでに入っていた古いlibssh2を参照していたため。--with-ssh2でパスを指定したら、うまくいきました。ほっ。

ソースディレクトリ配下のmodulesの下に、ssh2.soができるので、これをphp.iniのextension_dirで指定したディレクトリにコピー。続けて、php.iniでextensionに追加する。

php.ini

; Directory in which the loadable extensions (modules) reside.
extension_dir = "/usr/local/lib/php/extensions/"

extension=ssh2.so

ssh2.soをコピー。

# cp /usr/local/src/ssh2-0.10/modules/ssh2.so "/usr/local/lib/php/extensions/

ここまでできたら、apacheを再起動。うまくインストールできていれば、phpinfoでssh2の項目がでてくる。

簡単な使い方。

$con = @ssh2_connect("server_name", 22);
ssh2_auth_password($con, 'user', 'password');
$stream = ssh2_exec($con, "ls -al | wc -l");
stream_set_blocking($stream, true);
echo fread($stream, 4096);
fclose($stream);

結果。

12

ファイル削除など、出力のないコマンドの実行結果をとるなら、こんな感じかな?

$con = @ssh2_connect("server_name", 22);
ssh2_auth_password($con, 'user', 'password');
$stream = ssh2_exec($con, "rm -rf /tmp/hoge; echo $?");
stream_set_blocking($stream, true);
echo fread($stream, 4096);
fclose($stream);

結果。

0

まあ、実際に使うことは無さそうだけど、PHPだとこういうことも簡単にできるんだなーということで、ひとり満足。もはや独自プロトコルの対応以外では、自分でソケット操作とか面倒でしてられないもん。

お疲れさまでした。

PHPで正規表現のメタ文字をエスケープする

/ php

PHPを始めて感じたのは、「PHPは痒いところに手が届いた関数が豊富」ということ。mb_strimwidth関数のように、自分でもすぐに書けそうな関数も標準装備してくれているのが助かる。そんなわけで、最近は車輪の再発明にならないように、関数を探すクセがついた。

で、正規表現のメタ文字をエスケープしてくれるような関数って無いかなーと思って、検索したけど見つからず。無念。ありそうなんだけどなあ。

ということで、適当に作ってみた。

function escapeRegexp($str)
{
    $str = str_replace('\\', '\\\\', $str);
    $str = str_replace('*', '\\*', $str);
    $str = str_replace('+', '\\+', $str);
    $str = str_replace('.', '\\.', $str);
    $str = str_replace('?', '\\?', $str);
    $str = str_replace('(', '\\(', $str);
    $str = str_replace(')', '\\)', $str);
    $str = str_replace('{', '\\{', $str);
    $str = str_replace('}', '\\}', $str);
    $str = str_replace('[', '\\[', $str);
    $str = str_replace(']', '\\]', $str);
    $str = str_replace('^', '\\^', $str);
    $str = str_replace('$', '\\$', $str);
    $str = str_replace('|', '\\|', $str);
     return $str;
}

全メタ文字が網羅されているか確認とってないけど、こんな感じで要望は満たせました。もっと効率的で確実な方法があると良いんだけど。教えて、エロい人!

chownで Unable to find uid エラー

/ php

PHPのchown関数を使ったらこんな警告が。

Warning: chown() [function.chown]: Unable to find uid for 1022

「uidが1022のユーザーなんていないよ」ってことなんだけど、実際に/etc/passwdとか見ても存在しているし、bashでchownコマンドを打つと正常に機能する。

該当処理は、こんな感じ。

public function my_chown($file, $uid, $gid)
{
    chown($file, $uid);
    chgrp($file, $gid);
}

30分ほどハマって、「もしかして、1022って名前のユーザを探してるんじゃ?」と思って調べたら、大正解。$uidと$gidがstringで渡ってきていたため、名前解決を頑張った結果だった。うわー。

var_dump($uid);

string(4) "1022"

ちゃんと判定して型キャストしてあげたら、思い通り動いてくれた。

public function my_chown($file, $uid, $gid)
{
    if (is_numeric($uid)) {
        $uid = (int) $uid;
    }
    if (is_numeric($gid)) {
        $gid = (int) $gid;
    }
    chown($file, $uid);
    chgrp($file, $gid);
}

これだから型が厳密じゃない言語ってのは…、と軽くキレそうになったけど、んなこと言っている暇があるたら、早く慣れてガリガリとアプリ開発できるように書き続けた方が、建設的だよね。ふぅ。

PHPで連想配列を複数項目でソート

/ php

SQLのORDER BYみたく、きめ細やかなソートをするには、usort関数を使う。

name と price というキーに対して、それぞれ値を持つ配列があったとき、これをpriceの降順、nameの昇順に並べる例。

// 比較関数
function compare($a, $b) {
        if ($a['price'] > $b['price']) {
                return -1;
        } else if ($a['price'] < $b['price']) {
                return 1;
        } else {
                return strnatcmp($a['name'], $b['name']);
        }
}

$list = array( array( 'name' => 'apple', 'price' => 100, ), array( 'name' => 'grape', 'price' => 200, ), array( 'name' => 'orange', 'price' => 100, ), array( 'name' => 'banana', 'price' => 200, ), );
usort($list, "compare"); print_r($list);

ソート結果。

Array
(
    [0] => Array
        (
            [name] => banana
            [price] => 200
        )
    [1] => Array
        (
            [name] => grape
            [price] => 200
        )
    [2] => Array
        (
            [name] => apple
            [price] => 100
        )
    [3] => Array
        (
            [name] => orange
            [price] => 100
        )
)

usortの第2引数に、比較演算をする独自関数名を指定する。クラスのメンバ関数を指定したい場合は、配列で渡すとできるみたい。

// AnyClass::compare を指定する場合
usort($list, array("AnyClass", "compare"));

javaのComparatorインタフェースを使ったソートに近い。でも、こういうのって使い切りがほとんどだから、無名関数で指定できたりすると便利なんだけどなあ…。そういう方法もあったりするんだろうか。

関数名の頭に@(アットマーク)の謎

/ php

実践でPHPを習得しよう企画、3日目。企画はいま思いついたけど、3日前からやっているから、3日目。

PEARでインストールしたライブラリを眺めていると、見たことのない記法を発見。

if (file_exists($filename)) {
     @unlink($filename);
}

関数名の頭にある@(アットマーク)は、PHP特有のエラー制御演算子というものらしい。これを書くと、その処理内でエラーがあっても出力されなくなる。なるほど、ライブラリに余計なエラーログを吐かれるのは迷惑ってことか。

詳細はPHPマニュアルを参照。

関数名とかじゃなく、式全般で使えるのね。でも、通常の開発では使うことは無さそう。ライブラリでエラーが出たときに、この@演算子を外すばエラー詳細を確認できるというノウハウってところでしょうか。

演算子でつまづくとは、習得の道のりは険しい。

クラスをnewするときのアレ

/ php

PHPでクラスをnewするときのアレの話。

$foo = new FooClass();
$bar =& new BarClass();

ずっと下のやり方で書いてきたのだけど、よく考えてみると理由があいまい。参照を明示するメリットってなんだろう。「みんな、そう書いているし、これがPHPの文化なのかな」とか、郷に入っては郷ひろみ的発想で流していた。

こちらに詳しいこと書いてありました。

まあ、結論としては
1. 参照を返す関数から参照を受け取り、セットする場合は「=&」を用いること。
2. クラスをnewするときも「=&」すること。
がPHP4での注意点となります。

PHP5だと、そもそもオブジェクトは参照渡しが基本なんで、そりゃメリットも感じないか。まあ、でもスゴイ勉強になった。PHP4をやるときに、参照渡しを肝に命ずる次第っす。ありがとうございます。

トラックバックが文字化けする

/ php

php で書いたトラックバック受信用のプログラムが、ping リクエストの内容を文字化けして吐き出すようになった。別サーバではバッチリ動いているのに、なぜ。

$charset = $_POST['charset'];
if (empty($charset)) {
    $charset = "utf-8";
}
$excerpt = mb_convert_encoding($_POST['excerpt'], "euc-jp", $charset);

リクエストに charset の指定が無い場合は、デフォルトで「utf-8」を使用することにしていたのだけれど、どうもこれがいけないらしい。この処理を省いたら、文字化けしなくなった。

それでも別サーバでは正常稼動していたのだ。どうも釈然としないなあ。

続きを読む "トラックバックが文字化けする"

Smarty がミリ秒を扱わない迷惑さ

/ php

PHPの優秀なテンプレートエンジンとして名を馳せているらしい Smarty が、何かミリ秒に対応していないようでして。今日びミリ秒が扱えないって、「ウチのテレビはリモコンが無いんですのよ、オホホホホ」ってくらい迷惑ですよ。

事の発端は、Flash で日付データを取扱うために、サーバから現在時刻を取得しようと考えたところにある。ローカル時間だと不正が効くからね。普通なら、直接 Flash から CGI にリクエスト投げて終わりなんだけど、今回は好き勝手にプログラムを追加できない事情にさいなまれ、苦心。

ここからが長かった…。(ネズミの時間で1週間くらい)

続きを読む "Smarty がミリ秒を扱わない迷惑さ"

GD のインストール

/ php

Teenage Funclub でも聴きながら、まったり、しっとりと php とか書いてみようかって思った矢先。

よく見てみると、手元のサーバに既存で入っている php には GD が入ってないんですよ。このご時勢ですから、GD をシカトするなんざ、死に等しいかっていうと、そうでもないか。つーことで、GD を追加インストール。例によって、以下はそのメモ。

続きを読む "GD のインストール"