ディスク I/O 100% 状態からの復旧

Windows 8.1 を使っていて、ディスク I/O が 100% に張り付いて使い物にならないという状況に苦しめられました。 最終的にはその状態から回復できたので、私が行ったことをメモ書きしておきます。

きっかけ

小学生の子供達にもその Windows 8 マシンを使わせるため、ペアレンタルコントロールの設定をしました。 Windows 8 では 1日の使用時間の総量を制限することができるのですが、制限時間到達後に電源長押しを行って強制再起動すると何故かまた使えるようになるのだそうです (子供達の経験談)。 それを何度も何度も繰り返していたおかげで、一部のファイルの整合性が損なわれたようです。

ひどい子供達だ…。

状況は?

タスクマネージャーの「パフォーマンス」タブで確認するとディスクの「アクティブな時間」がずっと 100% に張り付いてしまって、何をやるにしても反応が遅く使い物になりませんでした。 Windows Update サービスを無効化すると症状が改善されたりしたので、当初は Windows Update 関連かと思ったのですが、イベントマネージャーの Windows ログ – Application を確認するとソースが ESENT のエラーが多数見受けられました。

初めて知ったのですが、ESENT とは拡張ストレージエンジン (Extensible Storage Engine) だそうで、Windows Update や Windows デスクトップ サーチで利用されているそうです。 で、子供たちの強制電源オフであちらこちらに論理障害が起こっていたと。 そういうことのようです。

何をやったか?

以下のあたりのファイルを消しました。 Windows Update 関連とイベントビューアーのログに書かれていたものです。

C:\Windows\SoftwareDistribution\DataStore
C:\Windows\SoftwareDistribution\Download
C:\ProgramData\Microsoft\Windows\AppRepository\*.log
C:\Users\ユーザー名\AppData\Local\Microsoft\Windows\WebCache

アプリケーションが使用中でロックされているものが一部あったりもしましたが、消せるものを消したら何とかなったようです。

それと、ESENT と関係があるかどうかわかりませんが、Windows Store 関連も怪しい動きだったので、以下のブログの記事の内容を実行しています。

具体的には以下の 2つのコマンドを実行しています。

powershell -ExecutionPolicy Unrestricted Add-AppxPackage -DisableDevelopmentMode -Register $Env:SystemRoot\WinStore\AppxManifest.xml

これでやっと使えるようになりました。 子供には間接的にも直接的にもいろんな形で時間を取られちゃいますね。 今だけなんでしょうが。

Firewalld の設定

CentOS 7 への移行に伴い iptables から Firewalld へ移行したのですが、Firewalld の wiki を見てもよくわからなかったところの覚書。

interface と source は OR 条件

サーバーに接続できる IP アドレスを制限しようとして、例えば以下のように設定したとします。

# firewall-cmd --zone=home --add-interface=eth0
# firewall-cmd --zone=home --add-source=11.22.33.44/32

すると意図に反して eth0 を使った通信は全て許可されてしまいます。 interface が eth0、またはソースアドレスが 11.22.33.44 の通信が許可されるのです。

正しくは –add-source のみ実施します。

現行設定と起動時設定は別々に行う

設定をすぐに反映させ、さらに起動時にもその設定を残るようにするには以下の 2つのコマンドを入力しなければなりません。

# firewall-cmd --zone=home --add-source=11.22.33.44/32
# firewall-cmd --permanent --zone=home --add-source=11.22.33.44/32

最初の行で現在の設定が変更され、–permanent をつけることで永続的な設定、すなわち firewalld 起動時の設定が変更されます。

まあ、移行してしまえば、全体的には iptables よりわかりやすいのかも知れませんが。

開発者向け情報の日本語化プロジェクトは不要

「日本語化」と一口に言ってもいろんな対象範囲があるので一緒くたにはできません。 エンドユーザー向けの情報やユーザーインターフェイスが日本語化されるのはとても意義のあることだとは思います。

でも、オープンソースなソフトウェアの開発者情報を日本語化することに労力をかけるのであれば、そのリソースは別のところで使った方が有効だと思います。 それなりの質の翻訳をするにはその技術を理解している人が翻訳する必要がありますが、英語が出来て技術を知っている人は別のところでそのプロジェクトに貢献すべきです。

例えば、本家情報を参照するためのガイド記事や補足記事を書いたりすることは有効だと思います。 また、日本の貢献を世界へ発信することも必要です。 開発情報の和訳をするぐらいであれば、これらの活動を行う方が良いと考えています。

何故こんなことを書くのかというと、以下のような状況があるからです。

  1. 訳した情報が陳腐化するスピードが速くて、タイムリーに翻訳していくにはコストがかかりすぎる。
  2. 古い情報が混じったり一部が未翻訳だったりするとまともな開発者は英語の本家情報を参照するようになってしまう。
  3. 参照する人が少ない情報は質・量を向上させることができず、結局悪循環となってしまう。 ならば無駄なことはしない方がよい。

ここまではオープンソースなプロジェクトを念頭に置いて書きましたが、例えば Microsoft 製品に関しても技術者は公式英語資料を参照することが多いと思います (昔からですね)。 その状況に不満を持つ方もいるのかも知れませんが、完璧な和訳情報を企業に求めても結局は製品価格に跳ね返るだけでしょう。

オープンソースプロジェクトからの視点で考えると、企業体でも出来ていないものをでやるのだから難度が高いということなのだと思います。

何にせよ個々の技術者としては、きちんとした知識を身に着けたいのであれば本家の情報を英語で参照すべきです。 自分達より若い世代ならば英語情報が標準になるのではなかろうかと思っていたのですが、どうもそうでもないようなので今更ながらに書いてみました。

英語が苦手という方もいるでしょうが大丈夫です。言葉は慣れれば何とかなりますから。

git-svn と GitHub を使ったワークフロー

WordPress のプラグイン開発で WordPress.org の Subversion リポジトリと GitHub を使っているのですが、最近ようやく git-svn を使ったワークフローをどうやったら良いかがわかってきたので要点をまとめておきます。

git-svn のワークフローのポイント

基本的なワークフローはここに書いてある通りです。 ポイントは以下の点です。

  • git svn dcommit は master ブランチで行う
  • リリース時に master、remotes/origin/master、remotes/trunk が同じコミットを指すように心がける
  • そのためには、リリース時は git svn dcommit してから git push する
  • Subversion リポジトリへのコミット回数を減らすには git merge –noff を使う。

リンク先記事は英語ですが、コマンドを自分で打ってみれば何をやっているかわかると思います。 –no-ff や –ff オプションについてはこちらの記事がとてもわかりやすいです。 私が WordPress プラグイン開発で使用している具体的なコマンド例についてはこちらの記事をご覧ください。

どこでつまづくのか

git svn dcommit を実行したときに新たなコミットがつくられてしまうので、git のリモートブランチ感覚で remotes/trunk を扱うとまずいのです。 git commit –amend と似た動作と言えばわかるでしょうか。

次の図のようにブランチ master と work が同じコミットを指した状態で master 上で git svn dcommit をすると master と work は分岐した異なるコミットを指すようになります。

git-svn-dcommit
git svn dcommit 時の動作

このような動作なので、Subversion 追跡ブランチを作ってその上で git svn dcommit を行うと master と remotes/trunk が平行する状態になって困ります。 ですので、必ず master ブランチ上で git svn dcommit を行い、その後に git push origin master を行います。 このあたりを意識できないと git-svn をうまく使えません。

既にぐじゃぐじゃなんですけど

では、頑張って直してみましょう。 master ブランチでは Subversion trunk の最新版に更に修正が加わっているものとします。 git svn dcommit 時に reset されてしまうので、差異がないとうまく行かないと思います。

work ブランチを作って master をマージします。 -s オプションでとにかく master を正としてマージします。

$ git checkout -b work trunk
$ git merge -s recursive -Xtheirs --no-ff master

work ブランチ上で作業するのもポイントで、逆に最初 master 上で work ブランチをマージしてしまうと git svn dcommit を実行したときに ‘Transaction is out of date’ と怒られたりします。

必要であれば次のコマンドでメッセージを変更したコミットを作っておきます。 merge の時に –edit オプションを指定しても良いです。

$ git commit --amend

さて、master ブランチを使って git svn dcommit を実行しましょう。

$ git checkout master
$ git merge --ff work
$ git svn dcommit
$ git push origin master

この後 work ブランチは消去してしまっても構いませんし、以下のようにリセットしておいて次のマージ時に使ってもよいでしょう。

$ git checkout work
$ git reset trunk

いかがでしょうか? これでやっと git-svn に悩まされることなく WordPress プラグイン開発に専念できそうです。 この手順は Subversion と Git で平行管理していたソースを統合するときにも使えるように思います。

最後にお約束の宣伝ですが、Standard Widget ExtensionsThin Out Revisions をよろしくお願いします! (まだまだこれからですが、) 着々とユーザーも増えております (SWETOR)。

VMware Player の NAT ネットワークを極める

VMware Player では仮想ネットワークに「NAT」、「ホストオンリー」、「カスタム」、「LAN セグメント」などの種類を選ぶことができます。 ゲスト OS に検証用のサーバー OS を入れてセットアップする際に、更新モジュールをダウンロードする等の理由で外部と通信したいことは多いと思います。

外部接続を行うという点では「NAT」を選択するのがお手軽ですが、ゲスト OS がどのように外部へアクセスするのかが気になります。 ここでは NAT の設定に絞り、この時の挙動を詳しく知ることで様々な場面に適用するためのヒントをまとめます。 実際のところ、NAT ネットワークを押さえておけばいろいろな用途で使うことができます。 なお、この記事は Windows 版 VMware Player 6.0.1 を前提として書いています。

ネットワークの構成

下図は NAT 仮想ネットワークの構成イメージを図にしたものです。

VMware NAT network

仮想セグメントのネットワークアドレスは 192.168.203.0/24 となっていますが、これは VMware Player のインストール時に自動的に決まるもので、必ずしもこのアドレスになるとは限りません。 192.168.0.0/16 の中から割り当てられると思いますが (少なくとも私はそうでした)、あくまでも一例です。

割り当てられるのは /24 ネットマスクのアドレスで、第 4オクテットの .1 がホスト OS に、.2 が仮想ルータに割り当てられるようです。 ゲスト OS から見たときにホスト OS へはこの .1 のアドレスでアクセスします。 また、外部へアクセスする際は仮想ルータをデフォルトゲートウェイとして用います。 更に .254 で DHCP サーバーが動作 (※追記参照) しており、外部アクセスに必要な DHCP パラメータがゲスト OS に提供されます。 従って、ゲスト OS は DHCP クライアントの設定がされていれば何も考えずに外部と通信できます。

仮想ルータ

仮想ルータの動作についてもう少し見てみます。

  • 仮想ルータはゲスト OS のために NAT を行います。 ゲスト OS の通信は、外部から見るとゲスト OS の物理インターフェイスの IP アドレスで通信しているように見えます。
  • ゲスト OS 用の DNS サーバーがこの仮想ルータ上で動いています。
  • 仮想ルータ上の DNS サーバーは受け取った名前解決要求をホスト OS の設定に従って解決します。 ホスト OS のリゾルバを使って解決するという言い方でも良いと思います。 つまり、ゲスト OS としては仮想ルータを DNS サーバーとして使えば後はうまく動作するということです。
  • DHCP サーバーの設定はホスト OS の ProgramData\VMware\vmnetdhcp.conf に書かれています。

ホスト OS のインターフェイス

さて、ホスト OS からはネットワークアダプタとして VMnet1 と VMnet8 という名前のアダプタが見えます。 VMware Player をインストールすると、これらのネットワークアダプタには静的に IP アドレスが割り当てられます。 このうち VMnet8 が図中の赤の NIC に相当します。 このネットワークアダプタについて補足説明します。

ゲスト OS とホスト OS 間の通信はこのアダプタを介して行われます。 例えば Windows 環境であれば「start \\192.168.203.1」などのコマンドでゲスト OS からホスト OS の共有フォルダにアクセスできるということです。

また、このネットワークアダプタを無効にするとゲスト OS とホスト OS の間の通信はできなくなりますが、隣の仮想ルータは生きたままです。 つまり、ゲスト OS のネットワークアダプタ VMnet8 を無効にしてもゲスト OS は NAT で外部と通信できてしまうということなので、注意が必要です。

ちなみに VMnet1 は NAT 構成では使用しないので無効化して問題ありません。

ゲスト OS に固定 IP アドレスを割り当てたい時

ゲスト OS は普通にインストールをすると DHCP クライアントとなってしまいますが、固定アドレスを割り当てたいときもあると思います。 これを行う方法は 2つあります。

  1. 先の vmnetdhcp.conf の内容を確認し、DHCP の対象になっていないレンジから割り当てる。
  2. DHCP で割り当てるのはそのままにし、MAC アドレスを使って DHCP サーバーから割り当てる IP アドレスを固定する。

2 の方法については次節で説明します。

DHCP サーバーのカスタマイズ

ではゲスト OS に割り当てる IP アドレスを固定化するために DHCP サーバーの動作をカスタマイズしましょう。 やり方は以下の通りです。

  1. ゲスト OS 上でコマンド (ifconfig とか ipconfig とか) を使ってインターフェイスの MAC アドレスを確認します。 ここでは MAC アドレスが「00:0C:29:AA:BB:CC」だったものとします。
  2. vmnetdhcp.conf を編集します。 host VMnet8 のブロックの次に以下のように追加します。

    host CentOS {
      hardware ethernet 00:0C:29:AA:BB:CC;
      fixed-address 192.168.203.50;
    }
    
  3. ファイルを保存後、ホスト OS の「VMware DHCP Service」を再起動します。

これで私の環境ではゲスト OS に固定の IP アドレスを割り当てることができました。 好みの問題でしょうが、ゲスト OS の IP アドレスは DHCP を有効にしたままで vmnetdhcp.conf で管理した方が楽かも知れません。

なお、仮想ネットワークのネットワークアドレスを変更するには、このファイルの変更の他にレジストリの編集が必要なようです。 私はそこまで必要なかったので試していません。

まとめ

というわけで NAT 仮想ネットワークを使用してわかったことをまとめてみました。 このあたりが理解できると「ネットワーク接続 = NAT」の設定を利用しやすくなるのではないでしょうか。


追記 (2014/11/29)
初版では DHCP サーバーは仮想ルータ上で動くと書いていましたが、DHCP サーバーのアドレスは別であることに気付いたため内容を修正しました。

Windows Update で C80003F9 エラーが出るのを直す

ふと見ると、子供とカミさんがメインで使っている PC で Windows Update がエラーとなっていました。 何と、半年近くアップデートができてません。 それはまずいだろうと解決を試みました。 カミさんは気づかないんだなあ…。

以下のような状況でした。

OS
Windows 7 (32ビット版)
エラーの内容
「C80003F9」というエラーコードらしきものが…。
Windows Update Error
Windows Update Error

いろいろ検索して試しましたが、結局以下のページの「対処方法 2. Windows Update のコンポーネントをリセットする」の部分に書かれている通りに Fix it を実行したら解決できました。

Fix it の直リンクは以下の通りです。 実行時に、「積極的な修正を実行する (非推奨)」を有効にするところがポイントです。

というわけで、現在 70個もの更新を適用中です (笑)。

404 というパーミッション

ロリポップの事件が話題になっていますが、「『当社のパーミッションの設定不備を利用』されたことが原因」とのことです。 ここでは、備忘録を兼ねて Unix 系システムのパーミッションについてまとめてみたいと思います。

Unix 系パーミッションの基本

Wikipedia に詳しくまとまっているので、ここで細かくは書きませんが、「ユーザー=自分 (u)」、「グループ (g)」、「その他 (o)」の 3つのクラスに分けて管理しているというのがポイントです。 それと「リード (r)」、「ライト (w)」、「実行 (x)」ですね。

まず、わかりにくいのが、あるファイルにアクセスするためにディレクトリにどのパーミッションを設定すれば良いかです。 アクセスするには、そのファイルのフルパスのディレクトリ階層を辿っていく途中の全てのディレクトリに x が必要です。

つまり、自分のホームディレクトリのパーミッションを確認して x があるのが u (自分) のみであれば他のユーザーから (サブディレクトリ含めて) ホームディレクトリ以下にあるファイルにアクセスされることはありません。 該当ファイル (例えば wp-config.php) のパーミッションだけを見てもアクセス可能かどうかはわからないのです。

なお、ディレクトリに設定された r はそのディレクトリに存在するファイル名一覧を取得するためのパーミッションで、各ファイルの内容にアクセスできるかどうかとは関係ありません。

705 というパーミッション

某共有レンタルサーバーを契約して使ってみて最初に違和感を覚えたのは、ホームディレクトリのパーミッションが 705 になっていることでした。 以下のようになっているわけです。

drwx---r-x  11 blogger323      users    1024 Jul 13 19:33 ./

「その他 (o)」に「リード (r)」と「実行 (x)」が許可されているので、普通の感覚だと「あれ? ホームディレクトリが公開されてる?」と思うわけです。 しかし、これは共有レンタルサーバーであるが故の設定だったのです。 「グループ (g)」で許可がなければ「その他 (o)」が許可されていても同じグループのユーザーはアクセスが拒否されます。 この共有レンタルサーバーのユーザーは全て users グループのみに所属するよう設定されているのでしょう。 「同じグループには許可しないけれど、他のグループならばリードと実行オッケー!」というとトリッキーな感じがしますが、そう考えてみると納得できます。

では何故そこまでして「その他」にパーミッションを与えるのでしょう? 通常 Apache などのデーモン (サービス) は root でない、そのアプリケーション固有のランタイムユーザーID、グループID で実行されています。 そのため、それらのサービスがファイルにアクセスするために許可が必要なのです。 このようなときは私の感覚だとグループを使ってアクセス制御しそうになるのですが、共有サーバーではそれが難しいので「その他」に許可しているのです。

アプリケーションの設定

ファイルシステムのアクセス制御だけを考えるならばここまでで十分なのですが、実際にはサービス経由のアクセスも考慮する必要があります。 Apache サービスがファイルにアクセスできるので、Apache 経由で他のユーザーがアクセスできるようになっていないか、すなわち Apache の設定についても考慮が必要となるのです。

ロリポップの件ではここにも問題があったようで、SymLinksIfOwnerMatch が未設定だったためシンボリックリンク経由で他のユーザーのファイルを読み出せていたようです (参考)。

ロリポップの wp-config.php が 400 な件

ロリポップでは suEXEC を導入していて CGI の実行権限は各ユーザーの権限となるようです (参考)。 であれば、今回の対応で変更したように wp-config.php のパーミッションはオーナーのみアクセス可能な 400 で十分ということになります。

ついでの宣伝

今回は濡れ衣を着せられた WordPress ですが、テーマやプラグインは信頼できるところから入手したものでない限り使ってはなりません。 ブラウザの管理画面から新しいテーマやプラグインが導入できるということは WordPress、すなわち Apache サービスがファイルを書き換えることができるということです。 もちろん、WordPress はデータベースのデータの読み書きも可能です。 ということは WordPress で動作するテーマやプラグインに悪意のあるコードが含まれている場合はもちろん、脆弱性が含まれる場合も悲惨な結果を招いてしまいます。

ちなみに私はそうとうメジャーなものと自分で開発したもの (その1その2) のみ使うようにしています。

あれ、タイトルが?

404 の話になってないし。ま、いっか…w

WordPress のリビジョンに覚書としてメモをつける

手前味噌な話題で恐縮なんですが、WordPress 用プラグインの Thin Out Revisions がバージョンアップして投稿/ページのリビジョンにメモを付けられるようになりました。

Revision Memo

上の画像のように編集画面に新たに「リビジョンメモ」が現れます。 また、「リビジョン」の部分の表示に入力メモが表示されます。

なお、一度入力し確定したメモは修正できません。まあ、リビジョンなんでそういう仕様ということで。 気に入ったら是非 Twitter や Facebook 等で広めてくださいね。

WordPress でのデバッグ用メッセージ出力

WordPress でテーマやプラグインの開発をしているとデバッグ用のメッセージを出力したいときがあると思います。 しかし、リダイレクト関連や Ajax 関連の動きを調べるときなどは簡単に画面出力できません。 そんな時にファイルへ出力するための簡単な方法を紹介します。

wp-confing.php に以下の 2行を入れます。

define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);

後は必要なところで PHP 標準関数の trigger_error を呼ぶだけです。

trigger_error('出力したいメッセージ');

すると trigger_error に渡したメッセージが wp-content/debug.log に出力されます。 お手軽ですね。

ただし、この設定だとスクリーンにも表示されるので、ファイルのみに出力したいときは WP_DEBUG_DISPLAY を false に設定します。 参考情報はこちら

Facebook プラグインを使ったときの OGP 画像設定

先日、WordPress 用 Facebook プラグインを使ったときの OGP 画像設定のために、強引に SQL でアイキャッチ画像を設定する方法を書きました。 しかし、わざわざアイキャッチ画像を設定しなくても Facebook プラグインのカスタムフィルタを使用すれば OGP 画像を設定できることに後から気づきました。

方法はプラグインのサポートフォーラムで紹介されていますが、以下のように fb_meta_tags フィルタを使用すればよいのです。

function my_ogp_filter( $meta_tags ) {
  $meta_tags['http://ogp.me/ns#image'][] = array(
    'url' => 'http://example.com/wp-content/uploads/2012/12/logo200x200.png',
    'type' => 'image/png',
    'width' => 200,
    'height' => 200
  );
  return $meta_tags;
}

add_filter( 'fb_meta_tags', 'my_ogp_filter' );

ただし、フォーラムで「add a fallback image」と書かれていますが、単純に画像もう 1枚分の Meta タグを加えているだけなので、既にアイキャッチ画像が設定されているポストでは OGP 画像設定用タグ (og:image 関連) が 2回含まれることになります。 それでも Facebook 開発者ページを見ると「You may include multiple og:image tags to associate multiple images with your page.」と書かれているので問題ないようです。