作成者別アーカイブ: baw

baw について

常時睡魔と格闘を繰り広げる暇人。

waitコマンドとLinux/Unixの仕様メモ

バックグラウンドプロセス2個以上をwaitして実行結果を取得する際のメモです。Linux(RHEL7、CentOS7)で確認済みですが、Unixでも同じことが言えるはずです。

まずは間違っている例です。下記の様に、プロセスIDを取得して、waitする際に2個のプロセスIDを指定してしまうと、誤った結果が得られます。(最後に指定したプロセスIDの結果のみが取得できる。)

$ cat parent.sh
#!/bin/sh

./child.sh $1 &
child1p=$!

./child.sh $2 &
child2p=$!

sleep 3

wait $child1p $child2p
childr=$?

echo "child1p:$child1p, child2p:$child2p, childr:$childr"
$ cat child.sh
#!/bin/sh
sleep 1
exit $1

下記が実行結果です。

$ time ./parent.sh 1 1
child1p:10159, child2p:10160, childr:1

real    0m3.005s
user    0m0.005s
sys     0m0.003s
$ time ./parent.sh 0 0
child1p:10165, child2p:10166, childr:0

real    0m3.005s
user    0m0.004s
sys     0m0.003s
$ time ./parent.sh 0 1
child1p:10171, child2p:10172, childr:1

real    0m3.005s
user    0m0.001s
sys     0m0.006s

うまくいくやん! と、思われ方。要注意! 下記を実行すれば誤りに気付きます。

$ time ./parent.sh 1 0
child1p:10152, child2p:10153, childr:0

real    0m3.006s
user    0m0.004s
sys     0m0.004s

引数pidを指定した場合,waitコマンドは最後の完了を待ったプロセスのコマンドの終了コードで終了します。
http://itdoc.hitachi.co.jp/manuals/3021/3021313320/JPAS0399.HTM

正しい実装は下記。それぞれ個別にwaitする必要がある。

$ cat parent2.sh
#!/bin/sh

./child.sh 0 &
child1p=$!

./child.sh 1 &
child2p=$!

sleep 3

wait $child1p
child1r=$?

wait $child2p
child2r=$?

echo "child1p:$child1p, child2p:$child2p, child1r:$child1r, child2r:$child2r"

ん? と思われた方。そう、子プロセスが[sleep 1]していないにも関わらず、親プロセスで[sleep 3]しているため、waitにたどり着くまでに子プロセスは終了しています。「終了したプロセスの実行結果なんて取得できるの?」と。

waitコマンドについて詳しく言及した記事はありませんでしたが、waitシステムコールについて調べると、ありました。

終了したが、wait されていない子プロセスは「ゾンビ」になる。後で親プロセスが wait を実行して子プロセスについての情報を取得できるように、カーネルはゾンビプロセスについて最小限の情報 (PID、終了ステータス、 リソース使用状況) を保持する。ゾンビプロセスは、 wait によってシステムから削除されない限り、カーネルのプロセステーブルの 1 エントリーを消費する。このプロセステーブルが 一杯になると、新たにプロセスを作ることができなくなる。親プロセスが終了すると、その親プロセスの「ゾンビ」の子プロセスは (もしあれば) init(1) の養子となる。 init(1) は wait を自動的に実行し、ゾンビを削除する。
https://linuxjm.osdn.jp/html/LDP_man-pages/man2/wait.2.html

「養子」という表現、かなり好きです。(そこではない) つまり、子プロセスの実行結果は、親プロセスが終了するまでは間違いなく保持されるようです。そのため、上記コードは問題なしです。

$ time ./parent2.sh 1 0
child1p:10185, child2p:10186, child1r:0, child2r:1

real    0m3.006s
user    0m0.004s
sys     0m0.004s

位置100で不正な入力シーケンス(iconv)

「位置xxxで不正な入力シーケンス」は、いわゆるWindowsの機種依存文字である丸付き数字等をiconvでutf8→shift_jisに変換しようとしたときに発生するエラー。丸付き数字は、utf8では規定されているが、厳格なshift_jisには存在しないので、対応する文字無しということでエラーになる。(Windowsにおけるshift_jisは、正確にはshift_jisではなく、暗黙的に拡張文字(丸付き数字等)を含んだもの。)拡張文字も含んだcp932を変換後の文字コードとして指定してやれば解消する。

下記は、Linux上の日本語名ファイルを、zip等に纏めてftpでWindowsに送ったときに文字化けさせない対応のサンプルスクリプト。(utf8→cp932に変換している。)

mv "$filename" "`echo $filename | iconv -f utf8 -t cp932`"

意地でも正規表現を使わずに拡張子とファイル名を分割する

良く使うのに毎回調べている気がする正規表現をあえて意地でも使用せずに、拡張子(.tar.gz)とファイル名を分割する。ファイルのバックアップをアンダースコア+日付でcpして取得する場合が良くあるが、そういったときのアンダースコア分割にも応用できる。特にアンダースコアはファイル名自体にも使用されることがあるので、revで逆さにしてから変換する。(セパレータが何個存在するか分からないので、逆から処理するとうまくいく)

echo test.test.tar.gz | rev | cut -d '.' -f 3- | rev

逆にして、先頭からセパレータを探索、3個目以降を取得、再度逆にする。言われてみるとシンプルだけれど、なかなか出来なかった発想。

XREAマイグレーションに伴う自前設置WordPressの障害対応

XREAがサーバ老朽化に伴うマイグレーションを行いました。s353サーバに収容されていた当ウェブサイトも今日当たりました。で、同時にWordpressが動かなくなっていました……。色々と面倒だったので記しておきます。

Sorry. We are under maintenance.

移行前のウェブ領域は残されるようです。そのため、VALUE-DOMAIN以外のドメインレンタルサービスを使用していて、DNSレコードのIPアドレス修正が漏れていた場合は上記メッセージが表示され続けます。当ウェブサイト(jikkenjo.net)はVALUE-DOMAIN(eNom)だったので自動切換えでしたが、一部、他社からレンタルしているドメインを修正し忘れていて、本事象に当たりました。

他にも、ページが途中までしか表示されない事象が発生しました。吐き出されたHTMLソースを見ると、何の前触れもなくぷっつり切れていました。原因不明だったので下記対処をしましたが、結果として、DBをphpMyAdminでanalyze/optimizeすれば解消しました。詳細は不明ですが、恐らくどこかのレイヤでタイムアウトに引っかかってしまったのではないかと思われます。マイグレーション作業として行われたであろうDB移行が、物理移行(ファイルシステムごとコピー)ではなく論理移行(MySQLのダンプ・リストア)だったため、統計情報が更新されなかったと考えられます。(MySQLのバージョンが上がったので、物理移行は無理だった? それにしても、初回のanalyzeくらいは実行しておいてほしかった。)

wp-config.php に下記文言を追記してやると、Wordpressの内部エラーが出力されるようになります。私の場合は、「DBサーバとの接続が確立できませんでした」的なメッセージが出ていました。(connection refusedだったかな……)

define('WP_DEBUG',true);

AWS/EC2にfactorioサーバをたてる

久しぶりに技術記事です。夜な夜な友人とプレイしているfactorioが、普通にマルチプレイでやるとNW遅延が大きすぎるということで、流行りと無料キャンペーンに乗ってAWS/EC2にfactorioサーバをたててみました。

  • https://aws.amazon.com/jp/freeで無料キャンペーンを開催していることを確認します。
  • t2.micro インスタンスを作成します。途中でセキュリティグループの設定も行います。ssh接続するための鍵もダウンロードしておきます。(作成時に無料と書かれている)
  • セキュリティグループの設定(インバウンド)を変更して、既存のsshポートに加え、factorioのデフォルト使用ポートであるUDP/34197を許可します。
  • (ローカル端末でブラウザからログインした後)https://www.factorio.com/downloadからheadlessバージョンをダウンロードします。リモート先ではブラウザが使えないので、ローカル端末で行うことを勧めます。ダウンロードしたファイルを何かしらの手段を使ってリモート先の/home配下等に送ります。
  • インスタンス一覧画面の「接続」から、鍵等を使ったsshコマンドに与えるオプションのカンニングが出てくるので参考にします。(ユーザ名、接続サーバのDNS名、鍵ファイル名をオプションで与えます。)

sshクライアントにTeraTermを使う場合は、サーバ名を入力後、ユーザIDとダウンロードした鍵ファイルを指定して接続すればOKです。

下記の様に、/home配下に展開して起動してやっても出来なくはありませんが、serviceで起動してやった方が安定します。

tar xvfz factorio_headless_x64_0.14.22.tar.gz
cd factorio/bin/x64/
./factorio --create ../../saves/hogehoge.zip
./factorio --start-server ../../saves/hogehoge.zip

もう少しこだわる人は下記の手順メモを参考にしてください。

-- serviceに登録するプロジェクトをダウンロードするためgitをインストールします。
$ sudo yum install git

-- headless版のfactorioを/opt配下に展開します。
$ cd /opt/
$ sudo tar xvfz ~/factorio_headless_x64_0.14.22.tar.gz

-- 英語版Wikiに従ってfactorioユーザを追加します。
-- これを怠ってしまうと、後々factorioユーザを前提とした設定で面倒です。
$ sudo useradd factorio
$ sudo chown -R factorio:factorio factorio/

-- serviceに登録する有志のプロジェクトをダウンロードしてきます。
$ sudo git clone https://github.com/Bisa/factorio-init.git
$ sudo chown -R factorio:factorio factorio-init/

-- 意図がわかりませんでしたが、README.mdに書いてあったので設定。
-- 設定後にbashを再起動します。
$ sudo ln -s /opt/factorio-init/bash_autocomplete /etc/bash_completion.d/factorio
$ echo "source /opt/factorio-init/bash_autocomplete" >> ~/.bashrc
$ exec bash

-- serviceを登録してやります。
$ sudo ln -s /opt/factorio-init/factorio /etc/init.d/factorio
$ sudo chmod +x /opt/factorio-init/factorio

-- 自動的にfactorioをUpdateするスクリプトもダウンロードしておきます。
-- 手動でやるという人は不要です。こちらも有志のプロジェクトです。
$ sudo git clone https://github.com/narc0tiq/factorio-updater
$ sudo chown -R factorio:factorio factorio-updater/
$ ll
total 16
drwxr-xr-x 5 root     root     4096 Apr  2 05:38 aws
drwxr-xr-x 4 factorio factorio 4096 Apr 20 00:01 factorio
drwxr-xr-x 3 factorio factorio 4096 Apr 20 00:05 factorio-init
drwxr-xr-x 3 factorio factorio 4096 Apr 20 00:29 factorio-updater

-- ↑こんな感じの状態になっていればOKです。

-- ここからはfactorioユーザで作業した方が効率的です。
$ sudo su - factorio

-- exampleを参考に設定ファイルを作成します。
$ cd factorio-init/
$ cp -p config.example config
$ vi config

-- 触るところは基本的にありませんが、先ほどの手順でfactorio-updaterをダウンロードした人のみ下記を設定します。
-- UPDATE_SCRIPTの設定と、USERNAME/TOKENにfactorio公式のアカウントを設定すればよいと思います。
# absolute path to the factorio-updater script
UPDATE_SCRIPT=/opt/factorio-updater/update_factorio.py
# Note that if you set HEADLESS=1 the username/token will not be used as the headless
# download is provided free of charge
HEADLESS=1
UPDATE_USERNAME=you
UPDATE_TOKEN=yourtoken
UPDATE_EXPERIMENTAL=0
UPDATE_TMPDIR=/tmp

-- serviceに正常に登録できたことを確認します。
$ service factorio help

-- 自動更新の設定が正常にできたことを確認します。(当然、最新です。)
$ python update_factorio.py -l
Available packages:
        core-linux_headless64
$ python update_factorio.py -p core-linux_headless64
No updates available for version None (latest stable is 0.14.22). Did you want `--experimental`?
$ python update_factorio.py -p core-linux_headless64 -f 0.14.22 -x
No updates available for version 0.14.22 (latest experimental is 0.14.22).

-- factorio本体の設定を行います。適宜修正してください。
-- name/descriptionあたりは全員変更の必要があると思います。
-- 友人だけのサーバの場合は、public/game_passwordあたりを設定してください。
$ cd /opt/factorio/data/
$ cp server-settings.example.json server-settings.json
$ vi server-settings.json

-- factorioの初期設定をするため、初回のみセーブファイルを作成する手順が必要です。
$ cd /opt/factorio/
$ ./bin/x64/factorio --create ./saves/mysavegame

-- serviceで起動してやります。
$ service factorio start

下記に途中で出たエラーと対処法を記しておきます。(メッセージを読めば分かるので、私は特にハマりませんでしたが)

-- 設定ファイルが不足しています。exampleを参考に作成してください。
$ service factorio start
Could not find factorio server settings file: /opt/factorio/data/server-settings.json
Update your config and point SERVER_SETTINGS to a modified version of data/server-settings.example.json

-- 初期設定のため、初回のみセーブデータを手動で作ってください。
$ service factorio start
Could not find factorio config file: /opt/factorio/config/config.ini
If this is the first time you run this script you need to generate the config.ini by starting the server manually.
(also make sure you have a save to run or the server will not start)

Create save: sudo -u factorio /opt/factorio/bin/x64/factorio --create /opt/factorio/saves/my_savegame
Start server: sudo -u factorio /opt/factorio/bin/x64/factorio --start-server-load-latest

(If you rather store the config.ini in another location, set FCONF='/your/path' in this scripts config file)

参考

【ポケモンSM】最適なメタモン厳選方法メモ

今作は仲間を呼ぶの連鎖でV確定数を増やせることが知られています。色んなサイトを参考にさせて頂きながら、最適解と思われる方法を試行錯誤しながら見つけたのでメモしておきます。時間が掛かったり、どこかでミスをしておじゃんになるので、結構面倒だけれど準備した方が最終的には効率が良いです。

準備

  • スリーパー(スリープLv26進化)に「すりかえ」を思い出させる(ウラウラ島>ラナキラマウンテン>ポケモンセンター@要ハートのうろこ)。
  • ヤブクロン@あくしゅう(ウラウラ島>マリエシティ>はずれの岬>草むら)にリサイクルを思い出させ、リサイクル以外の技を忘れさせる(メレメレ島>ハウオリシティ>ポケモンセンター)。
  • 目的のせいかくを持つケーシィを用意。
  • ハイパーボールを多めに購入。(念のため)
  • ビビリだまを多めに購入。(仲間を呼びやすくなる。)
  • ニューラ(ラナキラマウンテン)にみねうち(技マシン54@ハラ勝利後にメレメレ島>研究所>ククイ博士)、みがわり、格闘技(かわらわり)を覚えさせる。

並び順

  1. ケーシィ@戦闘不能
  2. ヤブクロン@けむりだま
  3. スリーパー(順不同)@ヒメリのみ(PP回復)
  4. ニューラ(順不同)@ヒメリのみ(無くても良い)

行動

  1. ウラウラ島>ホクラニ岳>草むらに移動。
  2. がくしゅうそうちをOFFにする。
  3. レポートを書く。
  4. メタモンと遭遇するまで粘る。(同じく稀に出現するダンバルは捕獲率が伝説級らしい。全く捕獲できなくてイライラする。)
  5. 戦闘不能のケーシィを飛ばして、ヤブクロンを繰り出す。
  6. メタモンがヤブクロンに変身する。(以降、リサイクルのみ連打となる。)
  7. スリーパーに交換。
  8. すりかえを選択。(メタモンにヒメリのみを押し付ける。以降、リサイクルPP切れ→ヒメリのみ消費→リサイクルで回収を連打となる。)
  9. ニューラに交換。
  10. みがわりを選択。(かわりものメタモン対策。)
  11. みねうちを選択。(HP1になるまで連打。HPが低いほど仲間を呼びやすくなる。)
  12. ビビリだまを使用。
  13. 仲間を呼んだ場合:呼ばれた方(ヤブクロンにへんしんしてない方)を格闘技で戦闘不能にする。
  14. 仲間を呼んでいない場合:ビビリだまを使用。(効果のない道具を使用した場合はアイテムの消費なくターンを無為にやり過ごせる。)
  15. 繰り返す。31匹目に呼び出された方(ヤブクロンにへんしんしていない方)を捕獲。PPでカウントすれば間違い難い。

以上で4V確定です。

PostgreSQLでSQL発行時のエラー対応

PostgreSQLでselect句に定数が含まれていた場合、下記エラーが出力されることがあります。後に続いて型を決めてやれば問題は解消します。::textの部分です。

failed to find conversion function from unknown to text
select *
from (select a_column, 'const'::text as "b_column" from c_table)
  as d_table

メモでした。

JMeter走行でTimeoutedException

Windows端末でJMeter走行中にJMeter自身のExceptionが「結果をツリーで表示」リスナーの結果画面に表示される場合があります。これは、スレッド数を多くした場合や、画像やjavascriptやcssが多い場合に発生します。最終的にはクライアント端末の同時接続数を超えたことが原因です。

Windowsのレジストリに項目を追加することで、本事象を回避することが出来るのでメモしておきます。下記内容をテキストファイルに保存して、拡張子を.regとして実行すれば簡単です。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"MaxUserPort"=dword:0000FFFE
"TcpTimedWaitDelay"=dword:0000001E

GoogleGroupsで謎のエラー

謎のエラーが出てdocomoアドレスをGoogleGroupsに登録できませんでした。

エラーメッセージをパッと見て思いついたのが、登録済みGmailのGoogleアカウントに、追加しようとしているdocomoアドレスを結びつけている可能性でした。

エラーメッセージを見る限り、「予備のメールアドレス」の部分が妙に引っかかります。特に原因特定するつもりもありませんが、同様のメッセージで悩んでいる人のためになるかもしれないと記しておきます。

招待状が送信されませんでした
新しい招待状が送信されませんでした
1 人は既にグループのメンバーです。指定されたメールアドレスがこのユーザーのメインのメールアドレスか予備のメールアドレスかは不明です。