それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

gitlab を lxd/lxc などの仮想マシンに入れようとしてエラー

apt install gitlab-ce したらエラーになった。

 * execute[load sysctl conf kernel.sem] action run
      [execute] * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.sem.conf ...
                * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.shmall.conf ...
                * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.shmmax.conf ...
                sysctl: permission denied on key 'kernel.sem'
                sysctl: permission denied on key 'kernel.shmall'
                sysctl: permission denied on key 'kernel.shmmax'
                sysctl: permission denied on key 'fs.protected_hardlinks'
                sysctl: permission denied on key 'fs.protected_symlinks'
                * Applying /etc/sysctl.d/99-sysctl.conf ...
                * Applying /etc/sysctl.d/protect-links.conf ...
                * Applying /etc/sysctl.conf ...

      ================================================================================
      Error executing action `run` on resource 'execute[load sysctl conf kernel.sem]'
      ================================================================================

エラーになる。

Running handlers:
There was an error running gitlab-ctl reconfigure:

gitlab_sysctl[kernel.sem] (postgresql::enable line 81) had an error: Mixlib::ShellOut::ShellCommandFailed: execute[load sysctl conf kernel.sem] (/opt/gitlab/embedded/cookbooks/cache/cookbooks/package/resources/gitlab_sysctl.rb line 46) had an error: Mixlib::ShellOut::ShellCommandFailed: Expected process to exit with [0], but received '255'
---- Begin output of sysctl -e --system ----
STDOUT: * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.sem.conf ...
* Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.shmall.conf ...
* Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.shmmax.conf ...
* Applying /etc/sysctl.d/99-sysctl.conf ...
* Applying /etc/sysctl.d/protect-links.conf ...
* Applying /etc/sysctl.conf ...
STDERR: sysctl: permission denied on key 'kernel.sem'
sysctl: permission denied on key 'kernel.shmall'
sysctl: permission denied on key 'kernel.shmmax'
sysctl: permission denied on key 'fs.protected_hardlinks'
sysctl: permission denied on key 'fs.protected_symlinks'
---- End output of sysctl -e --system ----
Ran sysctl -e --system returned 255

sysctl の値がおかしい。

つまり、 lxc 内部から、sysctl を実行できないことに起因するっぽい

LXC のゲストとホストがあり、インストールをした直後の LXC ホストにLXCゲストを入れてて gitlab を apt install しようとしたら起動する

root@lxc-container $ exit
root@lxc-host# sysctl -e --system
root@lxc-host# lxc console  lxc-container
root@lxc-container $ sudo gitlab-ctl reconfigure

他にも sysctl 関連で次のようなエラーが出る。

execute[load sysctl conf net.core.somaxconn] action run
      [execute] * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.sem.conf ...
                * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.shmall.conf ...
                * Applying /etc/sysctl.d/90-omnibus-gitlab-kernel.shmmax.conf ...
                sysctl: permission denied on key 'kernel.sem'
                sysctl: permission denied on key 'kernel.shmall'
                sysctl: permission denied on key 'kernel.shmmax'
                sysctl: permission denied on key 'fs.protected_hardlinks'
                sysctl: permission denied on key 'fs.protected_symlinks'
                * Applying /etc/sysctl.d/99-sysctl.conf ...
                * Applying /etc/sysctl.d/protect-links.conf ...
                * Applying /etc/sysctl.conf ...

      ================================================================================
      Error executing action `run` on resource 'execute[load sysctl conf net.core.somaxconn]'
      ================================================================================

他の対処法があったり

lxc のゲストに自身の sys 設定を許可する設定をするとか

$ lxc config set container-name security.privileged true
$ lxc config set container-name raw.lxc "lxc.mount.auto=sys:rw proc:rw cgroup:mixed"
$ lxc config get container-name raw.lxc
lxc.mount.auto=sys:rw proc:rw cgroup:mixed
$ lxc start container-name 
$ lxc exec container-name 'sysctl -w kernel.shmmax=17179869184'

privileged で特権を入れてあげても良いかもしれない。

LXC のホスト側から sysctl をガッツリ変えたものをシェアするとか

 /etc/sysctl.conf /etc/sysctl.d/*.conf | sysctl -e -p - 

lxc つかうとこういう事が起きるんですね。

最終的に

ホスト側に sysctl で次の値を設定して

takuya@m75q-1:~$ sudo sysctl -p
kernel.shmall = 4194304
kernel.shmmax = 17179869184
kernel.shmall = 4194304
kernel.sem = 250 32000 32 262
net.core.somaxconn = 1024
kernel.shmmax = 17179869184

ゲスト側に privellege と read/write を仕込んで無事動きましたね。

ただ、gitlab の更新が来たら、また動かくなったので

LXCで動かすのは不可能だと思いました。

関連資料

https://askubuntu.com/questions/831481/how-to-set-lxc-mount-auto-in-lxd

https://forum.proxmox.com/threads/installing-gitlab-into-lxc-container-sysctl-kernel-shmmax.49388/

IPアドレスから地域判定するgeoipをipinfoで実現する。香川県判定

ipinfo というサービスがありまして。

ここに、リクエストを投げると、JSONで位置情報をくれる。

curl ipinfo.io
$ curl https://ipinfo.io/json

{
  "ip": "49.105.999.999",
  "hostname": "mo49-105-999-99999.fix.mopera.net",
  "city": "Tokyo",
  "region": "Tokyo",
  "country": "JP",
  "loc": "35.6910,139.7679",
  "org": "AS9605 NTT DOCOMO, INC.",
  "postal": "101-0047",
  "timezone": "Asia/Tokyo",
  "readme": "https://ipinfo.io/missingauth"
}

/json にリクエストを投げるとリクエスト元のIPアドレスで情報が返ってくる。

指定したIPの都道府県情報を得る

GETのPATHにIPアドレスを含めることで指定したIPアドレスの地域情報を得ることができる。

curl https://ipinfo.io/49.111.111.XXX/json

指定したIPが香川県かどうか調べる

そうです。地域判定が必要なのは、香川です。kagawa を判定しないといけません。

[[ $( curl https://ipinfo.io/49.111.111.111 | jq .region ) == 'kagawa' ]]

nginx と組み合わせます。

nginx に cookie アクセス制限を取り付けて、cookie未発行なら、香川チェックを受けてもらいます。

https://takuya-1st.hatenablog.jp/entry/2018/05/08/182127

findコマンドでファイルの所有ユーザーを調べ所有者を間違っているファイルを探す。

find コマンドで、自分以外の所有者のファイルを探す。

ユーザーがtakuya 以外のファイルやディレクトリを探す。

find .   -not -user takuya

指定した所有者のファイルを探す。

not を外せば、所有者を限定してファイルを探す。

find .   -user takuya

オーナーではなく -user です。

指定したユーザーグループを探す

重ねがけが出来ます。

find .   -user takuya -and -group takuya

ユーザー設定が意図通りになっていないものを探す。

find .  -not -user takuya -and -not -group takuya

また、指定した所有者以外になってるものを探す。

find .  -not -user takuya -and -not -user www-data

and と or と not を組み合わせる

条件を組み合わせると、ファイルのオーナーやグループを限定して除外したり検索ができるので find コマンドの適用範囲は広がると思う。

-and XXX 
-and -not XXX
-or -not XXX
-or XXX

find コマンド 関連記事

すぐわかるfindコマンドの使い方 - それマグで!

find コマンドで所有者やグループが条件マッチするファイルを探す - それマグで!

Amazonで横幅が広がったパソコン向け表示になってしまったのをスマホサイトに戻す。PC→スマホ、スマホ→PC

アマゾンでスマホ向けサイトに戻したい。

Amazonを見ていると稀に、iPadなどタブレットがPC表示になってしまったりする。スマホでPC版サイトを開いたらスマホ版に戻れなくなったって割とあるんですよね。

f:id:takuya_1st:20200324155822p:plain:w320

スマホサイトに戻すには

モバイルサイトに戻すリンクをクリックします

リンク →モバイル表示: https://www.amazon.co.jp/gp/anywhere/site-view.html?&opt=mobile

もとに戻すには、リンクをクリックするだけす→ モバイルサイト・リンク

PCサイトを閲覧するには

リンク→ PC表示 : https://www.amazon.co.jp/gp/anywhere/site-view.html?&opt=desktop

リンクは、最下段にあります。

スマホで表示中に、パソコン向け表示→モバイル表示にもとに戻すには、一番下にあるリンクをクリックすればいいのですが、とてもタッチしにくいので、リンクを覚えておくほうが便利だと思います。

f:id:takuya_1st:20200324160611p:plain

文字が小さいので見にくい?

見にくいのは、拡大縮小である程度見ることが出来ます。

パソコン向け表示だと、多少は見にくいですが、検索結果で広告が見やすかったり、商品詳細が1ページで済むので操作が少なくなる良さもあります。上手に使い分けてみてはいかがでしょうか。

アプリ版は、複数タブを開けないのでとても不便ですね。

もっとカンタンに iOSショートカットで!

amazonスマホ表示に戻す ショートカット

AmazonをPC表示にする ショートカット

telnet / openssl を用いて pop3/pop3s のサーバーの疎通を確認する。

pop3 サーバーにtelnet でアクセスする

telnet  pop3.myserver.example.com 110 

over tls /ssl の場合は、openssl を使う

SSL/TLS を経由した暗号化通信をする場合は、openssl コマンドを使います。次の例は、googlegmailpop3 over ssl したときのアクセス例です。

openssl s_client -connect pop.gmail.com:995 -crlf

POP3でログインする。

+OK  ready for requests from 
USER USERNAME@myserver.example.com
+OK send PASS
PASS MY_PASSWORD
+OK Welcome.
STAT
+OK 260 4516798

入力待ちになったら、まずユーザー名を呈示します

USER ユーザー名@ドメイン{enter}

ユーザー名がOKならサーバーから +OK が返ります

+OK 

次にパスワードを入れます

PASS MY_SECRET{enter}

するとサーバーからOKが返ります。

+OK Welcome.

コマンドが間違っていると サーバーから ERRが返ります。

-ERR

応答を+/-だけ見ていればいいのでラクですね。

何件あるか

STAT

STAT を送信すると、件数が表示されます。( gmail の場合 迷惑メールやゴミ箱も併せて表示されるので、250 件前後になるそうです。)

メールの一覧

LIST

一覧が表示されます。 . ピリオドが来るまでが一覧と番号です。 UIDL (Unique ID Listing) でも可能です。

メールの取得

メールの取得は TOP か、RETR を使います、が、RETR は その名の通りRETREIVE で取り出して削除してしまうのであまり使いません。( gmail のpop サーバーはエラーになる。)。

メールを取り出します。メール取得は本文の行数分処理されます。

No. 260 を先頭から1000行取り出す例

TOP 260 1000

指定した行数に満たない場合は、有るだけ出てきます。これも . ピリオドが来るまで、データが転送されます。

メールのヘッダがほしいときは 0 を指定すると楽です。

TOP 260 0

接続の終了

終了には次の文字列を送信します。

QUIT

無事切断されました。

quit
+OK Farewell.
read:errno=0

実際の接続例

f:id:takuya_1st:20200323192014p:plain

gmail の pop接続した場合の注意点

最新の250件程度が表示されますが、スレッド表示されてないなど、GmailのWEBからみた表示とは異なります。

「250 件ほど」のメッセージのリストが提供される理由: POP クライアントから Gmail に対してメッセージのリストが要求されると、まだ POP でダウンロードされていない、日付の古い順に 250 件のスレッドが Gmail で取得されます。Gmail のスレッドは複数のメッセージで構成されている場合もあるため、メール クライアントでダウンロードされる個別のメッセージ数は通常 250 件よりも多くなります。 https://support.google.com/a/answer/6089246?hl=ja

POP3

そういえば、最近はPOPって使わなくなりましたね。IMAP / SMTP を直接叩いたことはあるんですが、popは長いこと触って無いなと思って改めてためしました。 とてもシンプルですね。

参考資料

POP3(Post Office Protocol version 3):インターネット・プロトコル詳説(7) - @IT

関連資料

IMAP の場合 telnet(openssl)でIMAPプロトコルを喋ってGmailに命令を出す。 - それマグで!

SMTPの場合 SMTP の認証プロトコルを手作業で確認する - それマグで!

docker のイメージやコンテナを削除する. まとめて全削除

docker でストレージが圧迫される

docker build とか docker hub でイメージ取得してるとありがち。ストレージ圧迫される。

全部のコンテナを削除する

docker rm $( docker ps -a -q )

全部のイメージを削除する

 docker image rm  $( docker images -q )

依存関係に注意。

ただし、イメージには依存関係がある。ubuntu を使ってる XXXイメージを消そうととするなら、ちゃんと先に使ってるXXXを消す必要がある。まとめて指定すれば依存関係は内部でチェックして問題なければ解決して削除する。

また、イメージとコンテナに依存関係があって、起動中や終了してるコンテナで利用されてたら、削除する事はできない。

コマンドのポイント

docker ls -a -q 
docker image ls -q 

オプションで -q をつけるのがポイント。ここ大事なので試験に出ます。

echo  $(  date   ) 

コマンドのドルマーク $( ) は、カッコの中身を先に実行して、その結果をコマンドの引数にします。改行やスペースで文字区切りされて引数として渡されます。

その他チェックするもの

ボリュームもチェックしておくといいです

docker volume ls 

容量不足に泣かされないでも、docker build は速くしたい→キャッシュサーバー

docker build をためしたり、dockerfile を作るときに大量につくってしまったり、ストレージを圧迫される事が多かったのでメモ

もし、削除しても専用のキャッシュサーバーを作っておくと手速い

また、ubuntu など apt を頻繁に回すときは apt-cacherを使うapt-cacherで docker buildを速くする - それマグで!

関連資料

PhpStorm/WebStorm/IntelliJ IDEA など でUndo(やり直し)回数を増やす.

Jetbrains の phpstorm 愛用してますが、アンドゥ回数が不満です。

不満で仕方ないです。コードをリファクタリングしてるときとか、ちょっと戻したり、ちょっと進めたり、undo/redo の Ctrl-Z(⌘-z)をめっちゃ押します。

でも、履歴件数が少なすぎます。いつもああああ戻れない。って焦ります。

アンドゥの代わりに、そのために git でまいかい gitでコミットしたり stash するのも不便です。マージとrebase だらけでヒストリもカオスになったり。 (git の勉強にはなるんだけどさ)

Undo 回数は増やせます。

「やり直し(もとに戻す Ctr-Z ⌘-Z) 」 の回数は増やせます。

それは IDEレジストリ(registry ) にあります。

Find Action をひらく

Find Action ( CMD-Shift-A ) から f:id:takuya_1st:20200319024624p:plain

レジストリ ( registry) を探します。

f:id:takuya_1st:20200319023952p:plain

レジストリの undo 項目を探します。

初期値(デフォルト値)は100 ですが、任意の回数に増やすことが出来ます。 f:id:takuya_1st:20200319023936p:plain

検索しづらい

アンドゥやレジストリという、WindowsやDockerのキーワードと「モロ被り」なのでちょっと検索がしにくいです。特にGoogle

これは docker 設定

こういうのが検索に掛かってきて、ちょっと不便ですね。

設定方法を忘れたときは、 「Android Sttudio やり直し回数 履歴」だとか、「やり直し 回数 phpstorm 」だとか、検索結果に妙に特徴のでる製品名を選んだらいいと思います。

レジストリ やり直し intellij」だと本当にひどい目に合う。キーワードから類似語だと認識される、窓OSのアフィブログまみれになります。

f:id:takuya_1st:20200319025011p:plain f:id:takuya_1st:20200319023956p:plain

参考資料

Is there a way to increase the undo history size? – IDEs Support (IntelliJ Platform) | JetBrains

PHPStormでUndo回数を増やす件 - uzullaがブログ

https://youtrack.jetbrains.com/issue/WI-21306?_ga=2.191367971.824244746.1584552897-6632410.1584552897#comment=27-639779

指定サイズのファイルを作る方法 ( head 編 )

任意の指定サイズのファイルを作る方法

head を使う場合。

takuya@~$ head -c 100k /dev/urandom > out
takuya@~$ ll out
-rw-r--r-- 1 takuya staff 100K 2020-03-18 13:59 out

ランダム( /dev/urandom) から読み出して、指定サイズを作ります。 head は -c でサイズ指定が出来ます。

色々なサイズ

1M ( 1024*1024 )のサイズを読みだした場合

takuya@~$ head -c 1m /dev/urandom > out
takuya@~$ ll out
-rw-r--r-- 1 takuya staff 1.0M 2020-03-18 14:01 out
takuya@~$ \ls -l out
-rw-r--r-- 1 takuya staff 1048576  3 18 14:01 out

1G ( 1024* 1024 * 1024 ) のサイズを読みだした場合

takuya@:~$ ll out
-rw-rw-r-- 1 takuya takuya 1.0G 2020-03-18 14:02 out
takuya@:~$ \ls -l  out
-rw-rw-r-- 1 takuya takuya 1073741824  3月 18 14:02 out

少数 0.1m や 1g が指定できないので、それをしたいときは bash の四則演算を使って計算すればいい

takuya@:~$ head -c $(( 1024 * 1024 * 2 / 10   )) /dev/urandom > out

他の方法

他の方法は、過去記事を参考にしてください。

windows なら fsutil 、 linux なら dd を使う方法もあります。

参考資料

  • man head

phpでプロスセス実行するライブラリ書いた。

php でコマンドのプロセス実行したい。

シェルコマンドの呼び出しをどうしてもやる必要があって、ずっと proc_open について調べてたり、pakagist / pear などのライブラリを見てたんだけど、代表的なものをいくつか試した。しかしコレと思えるものや使い方が気に入るものがなかった。仕方ないので自作した。

php のシェルコマンド実行の問題点

いくつか、気づいた問題点があり、それを解消できるパッケージがなかった。

プロセスを実行するライブラリには限界がある。

php には memory_limtit と max_execution_time という、プロセスを実行する妨げになる設定がある。この制限があるので、途中で処理が止まる。特に巨大なアウトプットで時間がかかる処理(たとえば ffmpeg ) を php から実行すると、予想外の箇所で停止することがある。途中でコマンド実行が止まってしまうと本当に駄目。巨大なアウトプットはメモリ使い果たすどころか、エラーにならないことすらある。

プロセスを実行するライブラリはほとんどが、結果を文字列で返そうとする。そのためメモリ制限に掛かりエラーになる。phpのmemory exhausted とか excess of memory とか急に出てくるのでライブラリを使うのを諦めた。

パイプで処理を渡したい

パイプで処理を渡す( echo hello | cat のようなもの ) 、このためのシェルコマンドのエスケープがうまく出来ない。または面倒だったり、またパイプを使うとなぜか詰まることがある。先のプロセスのoutput をうまく後者のstdin に繋げなかったphp のライブラリがある。

ライブラリの使い方が煩雑。

ライブラリを眺めてると、手順が煩雑だったり、インストール後のコードが依存関係で面倒になったりしてた。

vanilla な phpでストリームを扱いたい。

php でちゃんとstdout を扱うとしたら proc_open に合わせてストリームで処理する必要があるだろう。文字列で渡す前提なのでトラブルが多い。また一時ファイルを大量に使うことになって不便だった。

php の proc_openがきれいじゃない。

php でプロセスをある程度制御したいなら proc_open を使うのだが、これがC言語スタイルで良くない。手続きが煩雑なのでコードの見通しが悪い。

作った

仕方ないので、コロナ春休みを活用して作ることにして、完成した。

github.com

使い方

<?php

$proc = new Process();
$proc->setCmd('php')
    ->setInput('<?php echo "Hello World"')
    ->pipe('cat')
    ->pipe('cat')
    ->pipe('cat')
    ->wait();
 //
$fd = $proc->getOutput();
$out = stream_get_contents($fd);
print($out);// -> Hello World

コマンド実行をしたり、パイプで渡したり、ファイルディスクリプタを受け渡してストリームで処理する。

これで巨大なアウトプットを一時ファイル(php:/temp)を使って受け渡ししたり、プロセスをちゃんと終了させたり、タイムアウト待ちができるようになった。

シェルコマンドのリダイレクトの代わり

シェルコマンドのリダイレクトを文字列でやらずに、こう書くことができる

STDINのリダイレクト

標準入力のインプットを渡すときはシェルコマンドはこう

cat < out.txt

これと同じ、プロセスのコマンド実行を、次のように書けるようにした。

<?php

$proc = new Process();
$proc->setCmd('cat')
$proc->setInput('/tmp/out.txt"')
$proc->run();
 //
$fd = $proc->getOutput();

STDOUTのリダイレクト

標準出力のアウトプットを渡すときはシェルコマンドはこう

date > out.txt

これと同じ、標準出力のリダイレクトでプロセスのコマンド実行は、 次のように書けるようにした。

<?php

$proc = new Process('date');
$proc->setOutput('/tmp/out.txt"')
$proc->run();

パイプでつなぐ

パイプは、コマンド実行できるプロセスを2つ起動して、input と output をつなぐだけなので、そのように書けるようにした。

date | cat 

これと同じ実行を php でできるようになった。

<?php
$proc1 = new Process('date');
$proc1->setInput($str);
[$p1_out, $p1_err] = $proc1->start();

$proc2 = new Process('cat');
$proc2->setInput($p1_out);
$proc2->run();

$p2_out = $proc2->getOutput();

$str = stream_get_contents($p2_out);

stdout / stdin をつなぐだけなら、定形なのでもっと簡単にメソッドチェーンで書けるようにした。

パイプを複数つなげる。

メソッドチェーンで書けると書きやすいだろうし、そう書けるようにした。

<?php

$proc = new Process('date');
    ->pipe('cat')
    ->pipe('cat')
    ->wait();
//
$fd = $proc->getOutput();
$out = stream_get_contents($fd);
print($out);// -> Hello World

標準入力に文字列を渡す。

コマンド実行時に、標準入力として文字列を渡して、プロセスに実行させる。標準入力からファイルの中身を受け取るコマンドを実行する。

echo 'echo Hello World' | sh

標準入力に文字列を渡してコマンドを実行する。

<?php
$str = 'echo "Hello World"';
$proc = new Process('sh');
$proc->setInput($str );
$proc->run();
$fd = $proc->getOutput();
$out = stream_get_contents($fd);

標準入力に文字列を渡せば、標準入力読み取りに対応するコマンドなら実行できる。grepsedはもちろん、phppythonruby もだ。

php も実行できる

<?php
$str = '<?php echo "Hello World"';
$proc = new Process('php');
$proc->setInput($str );
$proc->run();
$fd = $proc->getOutput();
$out = stream_get_contents($fd);

コマンドに標準入力をちゃんと渡せるなら、phpから phpの proc_openもできるの。ライブラリを作ったことでだいぶ書きやすくなった。

python 実行も同様に

<?php
$proc = new Process('python');
$proc->setInput('
import sys
print(sys.path)
');
$proc->run();
$fd = $proc->getOutput();
$out = stream_get_contents($fd);
var_dump($out);

同様に、標準入力にソースコードを渡せば実行できるpython もちゃんと動かせてる。

ssh 経由でコマンドを実行する

ssh 経由での実行は、php の標準のプロセス実行をそのまま使うとエスケープ面倒 。それもやりたいので対応させた。

<?php
$proc = new Process(['ssh','root@192.168.2.1','sh -c date']);

$proc->run();

$fd = $proc->getOutput();
$out = stream_get_contents($fd);
var_dump($out);// -> Sat Mar 14 09:32:18 JST 2020

実行中にリアルタイムでなにか処理をいれる

プロセスの実行中に何かをやりたい。expect みたいに入力を判定させたりするコールバック関数を渡せるようにもした。

<?php

$proc = new Process('php');
$proc->setInput('<? sleep(1); echo "Hello World"');
$proc->start();

$proc->wait(
  function ($status,$pipes){
    var_dump('wating');
    usleep(1000*10);
  },
  function ($status,$pipes){
    var_dump('success.');
  },
  function ($status,$pipes){
    var_dump('error occured');
  }
);

実行エラーを例外に。

プロセスに実行エラーを例外として取得したいとき、コールバックで例外を投げればできる。

<?php

try{
  $proc = new Process('___noexists_command_');
  $proc->setOnError(function($pr,$io){
    throw new \Exception('error');
  });
  $proc->run();
  
}catch (\Exception $e){
  echo 'error occured';
}

proc_open 自体には例外がないので、ラッピングするしかなかった。

まとめ

シェルコマンドのプロセスの実行をあれこれするには、STDIN/STDOUTのパイプをファイルのディスクリプタで繋ぐ必要がある。phpではIOを stream 系のファイルディスクリプタの関数群でこれを使う必要がある。ところがコマンド実行ができるライブラリやパッケージで、ファイルディスクリプタをIOを扱えるパッケージはあまり見当たらなかった。

proc_open で確保されるデフォルトのpipeストリームは数MBを超える出力を扱うとなぜか固まってた。また起動したプロセスにうまく stdin を fclose して渡せないものだったり、fcloseのeof 待ちになってフリーズしてた。それの辺をどうしても解消したかった。併せてコードとしてシェル実行を書きやすい形にできるライブラリがほしかった。

packagist にも投げた

takuya/process - Packagist

php の proc_open の処理を callback で少し見通しよく

proc_open のコードがカオスになってる

proc_open でプロセスを起動して実行できる。

でも、あれこれ手順が煩雑でわかりにくい。

コールバックを使って整理する

こういうときは、関数の引数に関数を取れば整理されるはず。

<?php
function process_exec( 
    $cmd,
   $callback_on_finished=null,
   $callback_on_every_waiting=null,
   $callback_on_error=null
)
{
  
  $callback_on_finished = $callback_on_finished ?? function(){};
  $callback_on_every_waiting = $callback_on_every_waiting ?? function(){};
  $callback_on_error = $callback_on_error ?? function(){};
  
  $descriptor = [
    0 => ['pipe', 'r'],
    1 => ['pipe', 'w'],
    2 => ['pipe', 'w'],
  ];
  // 実行
  $process = proc_open($cmd, $descriptor, $pipes, sys_get_temp_dir());
  do {
    usleep(1000*1);
    $stat = proc_get_status($process);
    $callback_on_every_waiting($stat, $pipes, $process );
  } while(preg_match('/process/i', get_resource_type($process)) && $stat['running']);
  if( $stat['exitcode'] > 0 ) {
    $callback_on_error($stat, $pipes );
    proc_close($process);
    return ;
  }
  $callback_on_finished($stat, $pipes);
  proc_close($process);
}

実行する側

<?php
$start_time = time();

$waiting = function ( $status , $pipes) use ($start_time) {
  if(preg_match('/stream/i', get_resource_type($pipes[0]))){
    fwrite($pipes[0],"---\nabcd\nefgh\n----\n");
    fclose($pipes[0]);
    usleep(1000*1);
  }
  if ( $start_time + 3 < time() ){
    posix_kill($status['pid'], SIGKILL);
    die('force kill');
  }
};
$on_error = function (){

};
$str = '';
$finished = function ( $staus, $pipes ) use (&$str){
  $str = stream_get_contents($pipes[1]);
};

$cmd = [
  "cat",
];
process_exec( $cmd, $finished, $waiting, $on_error);
print($str);

パイプと最大実行時間

パイプと最大時間をうまく使えば、プロセス起動をうまく使ってゾンビを作らずに済むし、実行時間を気にず、細切れにジョブを実行できる。

関連資料

phpのproc_open関数でコマンドのstdin/stdout/パイプを使う例を確認しました。 - それマグで!

libvirtの仮想マシンの仮想マシン用可変イメージを作る

virt-manager で qcow 作ると・・・

スパースファイルなのに、指定した容量が作られてしまう。

f:id:takuya_1st:20200308173915p:plain

割当<=最大容量なので20GBを作成するには、20GBが確保されてしまいます。

 sudo ls -lh /var/lib/libvirt/images/test.qcow2
-rw------- 1 root root 21G Mar  8 17:22 /var/lib/libvirt/images/test.qcow2

もはやこれはバグではと思いたくなるレベルなのでした。

qcow2 とは

スパースファイルです。仮想マシン用に事前に20GB確保しても、実際には使用量しか消費しません。

その代わり、実際の使用量を超えた分の書き込みは都度確保され書き込まれます、その代わりパフォーマンスに多大な影響を及ぼすことになります。

パフォーマンス劣化と言ってもCPUは十分に早くてストレージが十分に速くnvme のSSDのいまはあまり気にならないレベルなんじゃないかなと思います。

qcow2 で可変サイズなディスクイメージファイルが作れます。

ちゃんと qcow2 のイメージを作る

20GB の qcow2 のスパースファイルを作成する場合はこれ.

qemu-img create -f qcow2  my-test.qcow2 20G

作った結果を確認

仮想マシン用にディスクのイメージを作成して

takuya@:~$ qemu-img create -f qcow2  my-test.qcow2 20G
Formatting 'my-test.qcow2', fmt=qcow2 size=21474836480 cluster_size=65536 lazy_refcounts=off refcount_bits=16

作成した結果を確認します。

takuya@:~$ qemu-img info  my-test.qcow2
image: my-test.qcow2
file format: qcow2
virtual size: 20G (21474836480 bytes)
disk size: 196K
cluster_size: 65536
Format specific information:
    compat: 1.1
    lazy refcounts: false
    refcount bits: 16
    corrupt: false

実際に利用された容量を見ます。

takuya@:~$ ls -lh   my-test.qcow2
-rw-r--r-- 1 takuya takuya 193K 2020-03-08 17:26 my-test.qcow2

うん、全然浪費してない。

結論

virt-manager 意味不明。ちゃんとコマンドから使う。

参考

KVM ディスク操作 - わすれないうちにメモしよう

https://research.sakura.ad.jp/2010/03/23/kvm-diskperf1/

暗号化ディスクを新しいSSDに移動させてサイズを増大させる enlarge lvm on Luks

ssd を移動させたい。しかし暗号化ディスクだ

HDD/ssd はデータ式年遷宮をするのだけど、今回は、暗号化ディスクだった。

手順

手順は次の通り

  • とりあえず、dd
  • ubuntu など live disk で起動させる
  • gdisk などでパーティションを拡大
  • crypt領域を拡大
  • lvm pv を拡大
  • lvm vg lv を拡大
  • ext4 を拡大

lvm の拡大とext4 の拡大は、よくあるので大丈夫でしょう。

問題は、 cryptdisk の拡大ですね。

他にも dump restore でまるっと新しいものにすることもできるけど、dump 結果の保存場所を考えるとやっぱり ddかなと

SSDの移動

ssd 間で データをコピーする

ubuntu の live usb で起動して

dd status=progress if=/dev/sda of=/dev/sdb bs=1M

gdisk でパーティションを最大にする

今回は、EFI パーティションを含むディスクなので、パーティションは3つ。 1 はEFI互換性システム、2がEFI。3がルートファイルシステムになる。

パーティションを最大にする

takuya@:~$ sudo gdisk /dev/sdb
GPT fdisk (gdisk) version 1.0.4

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624         2549759   732.0 MiB   8300
   3         2549760        69658623   32.0 GiB    8300  Linux filesystem

Command (? for help): d
Partition number (1-3): 3
Command (? for help): n
Partition number (3-128, default 3):
First sector (34-234441614, default = 2549760) or {+-}size{KMGTP}:
Last sector (2549760-234441614, default = 234441614) or {+-}size{KMGTP}: 
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'

Command (? for help): p

Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1050623   512.0 MiB   EF00  EFI System Partition
   2         1050624         2549759   732.0 MiB   8300
   3         2549760       234441614   110.6 GiB   8300  Linux filesystem

暗号化ディスクを最大にする

最初に 暗号化ディスクを解錠して読み込み可能にする。

cryptsetup luksOpen /dev/sdb my_crypt

つぎにリサイズをかける

cryptsetup resize /dev/mapper/my_crypt
Enter passphrase for /dev/sda3:

ここまでで、ほぼ完了に近い。

LVM PVでリサイズする

つぎに、lvm で pv を最大にする

pvresize /dev/mapper/my_crypt

確認する。

Before

takuya@:~$ sudo pvs
  PV                     VG Fmt  Attr PSize  PFree
  /dev/mapper/dm_crypt-0 vg lvm2 a--  27.92g    0

After

takuya@livaz:~$ sudo pvs
  PV                     VG Fmt  Attr PSize   PFree
  /dev/mapper/dm_crypt-0 vg lvm2 a--  110.55g 82.63g

LVM vg/lv を最大にする

このへんはいつもの

takuya@livaz:~$ sudo lvresize -l100%FREE /dev/mapper/vg-lv
  Size of logical volume vg/lv changed from 27.92 GiB (7148 extents) to 82.63 GiB (21154 extents).
  Logical volume vg/lv successfully resized.
takuya@livaz:~$

ext4 を最大にする

このへんも、いつものオンラインリサイズ

sudo resize2fs /dev/mapper/vg-lv

まとめ

cryptsetup resize

これを覚える

暗号化ディスクをddしてコピーしても大丈夫。 blkid などをちゃんと設定しておくと、ddしたそのままで動く。

参考資料

https://wiki.archlinux.org/index.php/Resizing_LVM-on-LUKS

php のコマンド実行のproc_open でexitcode を取得する

proc_open でexitcode を取得すると -1 になる

proc_open の関数で、exitcode を参照すると、常に -1 が固定で返ってくる件

proc_get_status($process)['exitcode']; // -1
proc_get_status($process)['exitcode']; // -1
array:8 [
  "command" => "ssh"
  "pid" => 6694
  "running" => false
  "signaled" => false
  "stopped" => false
  "exitcode" => -1
  "termsig" => 0
  "stopsig" => 0
]

原因

プロセス終了後に status 取得すると、常に -1 しか返ってこない。そういうものである

対策。

proc_get_status($process)['running']false になった瞬間の status を保存しておくと exitcode が正しく取れる。

終了後に初めて呼び出したときに、正しいexitcode が取れる。それ以降は「終了済み」を示す -1 が常に返ってくる。

<?php 
$cmd = ['ssh','localhost', '/bin/noexists_command'];
$process = proc_open($cmd, [2=>$fd_ferr=fopen('php://temp','w')] , $pipes);
if( ! is_resource($process) ) { throw new \Exception("実行エラー");}

do {
        $stat = proc_get_status($process);
        // waiting
        usleep(1000*1);
} while($stat['running']);

if ($stat['exitcode'] > 0 ){
        fseek($fd_ferr,0);
        $err = stream_get_contents($fd_ferr);
        throw new \Exception($err);
}

これは、気づかんわ。

参考資料

https://stackoverflow.com/questions/7645499/getting-the-real-exit-code-after-proc-open

phpのproc_open関数でコマンドのstdin/stdout/パイプを使う例を確認しました。

php でプロセス(プログラム実行)

phpでプログラム実行をするなんて邪悪なことを誰がやるんだろうかと。

symfony process でいいじゃないかと思うんですが、駄目なんですね。synfony processだと巨大なSTDIN/STDOUTを扱えないんですね。たとえば、ffmpegにパイプで渡すとか、ssh経由でデータを受け渡すとか出来ないんですね。

既存の解決策が行き詰まるとき、最後の砦になるのが、proc_openでのコマンドの実行です。

proc_openでプログラム実行 exec同等

単純に `command`exec('command') shell_exec と同じことを proc_openでやる。

echo exec('dd if=/dev/urandom bs=1M count=2')

これは、次のソースコードと同等になる。

<?php 
//
$cmd = preg_split('/\s+/','dd if=/dev/urandom bs=1M count=2');
//
$descriptor = [
    0 => STDIN,
    1 => STDOUT,
    2 => STDERR
  ];
//
$process = proc_open($cmd,$descriptor, $pipes);
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
while(proc_get_status($process)['running']){
  usleep(1000);
}
proc_close($process);

proc_open で開いたプロセスは、何らかの形でwait して待ってあげます。

上記の例では、proc_get_status() の結果が false になるまで wait しています。

wait するのはいくつが方法が考えられます。 たとえば、 proc_get_status($process); で開いた子プロセスの情報が取れるので、exitcodeを見ることも可能です。

wait を工夫すれば、一定時間経過したら強制終了も可能です。

もしやるなら、posix 関数posix_kill で一定時間経過したら KILLするなども出来ます。

proc_open は実質的には fork / exec ( php 関数では pcntl_fork/ pcntl_exec ) ですね。

コマンドからの出力

コマンドを実行して、起動したプロセスからの出力を操作する方法を見ていきます。

標準出力・エラーを捨てる

標準出力とエラーを捨てるときのコードは、こんな感じものがよく書かれますが

<?php 
passthru('ls -l > /dev/null 2> /dev/null');
echo exec('ls -l > /dev/null 2> /dev/null');

これは、proc_openでは次のようにする。

<?php 
$cmd = preg_split('/\s+/','dd if=/dev/urandom bs=1M count=2');
//
$descriptor = [
    0 => STDIN,
    1 => ['file', '/dev/null', 'w'],
    2 => ['file', '/dev/null', 'w']
  ];
//
$process = proc_open($cmd, $descriptor );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
while(proc_get_status($process)['running']){
  usleep(1000);
}
proc_close($process);

省略可能

また、$descriptor は指定しないもの以外を省略できる。

使わないディスクリプタは、省略して構わない。

省略するとデフォルト [ STDIN , STDOUT, STDERR ] が適用される。

<?php 
$descriptor = [
    // 0 => STDIN,
    1 => ['file', '/dev/null', 'w'],
    2 => ['file', '/dev/null', 'w']
  ];
//
$process = proc_open($cmd, $descriptor );

標準出力を指定のファイルに

コマンドの出力を別のファイルに接続する。シェルからのコマンド実行だと次のようになる。

<?php
exec(' ls -l > /tmp/out');

これをproc_open 場合は、次のように書くと同様になる。

<?php
//
$cmd = preg_split('/\s+/','ls -alt /tmp/');
//
$fout = fopen('/tmp/out', 'w+');
$descriptor = [
    1 => $fout,
  ];
//
$process = proc_open($cmd, $descriptor );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
while(proc_get_status($process)['running']){
  usleep(1000);
}

fseek($fout,0);
$stdout = stream_get_contents($fout);
var_dump($stdout);
//
proc_close($process);

fopen() で開いたファイルに、proc_openしたコマンドのプロセスの標準出力が次々とfwrite されます。

プロセス終了後にファイルのシークは末尾に来ている。

なので、読み出すならfseek($fout,0);をして先頭に持ってくる。

先頭に持ってくるとその場で読み込める。

出力結果を一時ファイルに出す。

出力先を、 php://temp に変更すると、一時ファイルを作成削除を考えずに済むので助かる。

<?php
$fout = fopen('php://temp', 'w+');
$descriptor = [
    1 => $fout,
  ];

php のコードを見ていると、一時ファイルを作成して削除するパターンを非常によく見かけるのだけれど、fopenでfd を持ち回したほうが管理が圧倒的に楽だと思うんです。

php://temp を使う場合の注意点

php://temp には少し癖があり、open するたびに、別のファイルとして扱われる。 必ず、fseek で動かすこと。

## 間違い
$fout = fopen('php://temp', 'w+');
fwrite($fout, 'aaaaaaa');
$out = file_get_contents('php://temp', 'r');
## 正しい
$fout = fopen('php://temp', 'w+');
fwrite($fout, 'aaaaaaa');
fseek($fout, 0);
$out = stream_get_contents($fd);

php://memory and php://temp are not reusable, i.e. after the streams have been closed there is no way to refer to them again.

詳しくは本家のマニュアルを読むこと https://www.php.net/manual/en/wrappers.php.php

標準エラー出力を別のファイルに

エラー出力を、指定したファイルに出す。

ls -alt 2> /tmp/error-log.txt

これと同等のことを proc_open でやると次のようになる。

<?php
//
$cmd = preg_split('/\s+/','ls -alt /tmp/');
//
$ferr = fopen('/tmp/error-log.txt', 'w+');
$descriptor = [
    2 => $ferr,
  ];
//
$process = proc_open($cmd, $descriptor );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
while(proc_get_status($process)['running']){
  usleep(1000);
}

//
proc_close($process);

エラー出力と標準出力をそれぞれ別のファイルに出す。

$descriptorをアレコレすると直接ファイルに書き出せる。

<?php
$descriptor = [
    1 => fopen('/tmp/log.txt', 'w+'),
    2 => fopen('/tmp/error-log.txt', 'w+'),
  ];

出力にパイプを使う。

パイプを使う場合は、次のようにしてパイプを指定する。

<?php
$descriptor = [
    1 => ['pipe','w'],
    2 => ['pipe','w'],
  ];
$process = proc_open($cmd, $descriptor, $pipes , '/tmp', []  );

パイプはファイルディスクリプタでプロセス起動後にアクセスができるようになる。

パイプを使う例

pipeを使うと、パイプされているfd から読み込まれる。proc_open() の3th 引数で取得できる。

<?php
//
$cmd = preg_split('/\s+/','ls -alt /tmp/');
//
$descriptor = [
    1 => ['pipe','w'],
  ];
//
$process = proc_open($cmd, $descriptor, $pipes , '/tmp', []  );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
while(proc_get_status($process)['running']){
  usleep(1000);
}

$out = stream_get_contents($pipes[1]);
var_dump($out);
//
proc_close($process);

パイプは内部で読み込みと書き込みにfd2つ開けて接続する。

out書き込み用は内部的に使われていて、関数の戻り値にもらえるのは読み込み用 open 後に読み込み用のfd (ファイルディスクリプタ)が返される

パイプ close タイミング

出力用 pipe は、fcloseを明示的に呼ばなくても良い、proc_close() したタイミングで、開いているファイルは close される。

<?php
//fclose( $pipes[1]); //←不要
//fclose( $pipes[2]); //←不要
proc_close($process);

コマンドへの入力

ここまでは、コマンドからの出力を読み込み用に貰ってくる話でした。

ここからは、コマンドへ入力する話です。

標準入力をプロセスにわたす。

実行プログラムの標準入力にファイルを渡すなら、シェルならリダイレクトで書きます。

cat < /tmp/out.txt

入力のリダイレクト、これを proc_open で書くと次のようになる。

<?php
//
$cmd = preg_split('/\s+/','cat');
//
//
$descriptor = [
  0 => fopen('/tmp/out.txt', 'r'),
  ];
//
$process = proc_open($cmd, $descriptor, $pipes , '/tmp', []  );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
while(proc_get_status($process)['running']){
  usleep(1000);
}
//
proc_close($process);

cat を起動して、stdin にfopen の結果をリダイレクトでつなぐ。

パイプでコマンドに入力を渡す。

入力を渡すのであれば、パイプを経由するのが一般的だと思います。

cat  /tmp/out.txt | cat 

これを proc_openで書くとしたら、pipe を 指定して、そこに書き込んで、fclose する。

<?php
//
$cmd = preg_split('/\s+/','cat');
//
//
$descriptor = [
  0 => ['pipe','r'],
  ];
//
$process = proc_open($cmd, $descriptor, $pipes , '/tmp', []  );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}

// write to pipe
$f_in = fopen('/tmp/out.txt', 'r');
while(!feof($f_in)){
  fwrite( $pipes[0], fread($f_in, 1024));
}
fclose($pipes[0]);
// waiting
while(proc_get_status($process)['running']){
  usleep(1000);
}
//
proc_close($process);

fclose($pipes[0]); で開けている パイプをcloseしないとプロセスはEOFが来ないので、読み込み待ちでずっとWaitingになるんで注意 0 => ['pipe','r'] で read を指定しているのは、プロセスからみて read ですね。返ってくる $pipeには プロセスへのwrite が戻されるね。

proc_close($process); するとパイプは自動で閉じられる。

一時ファイル作らずにパイプと標準入出力を使う。

php のコードを見ていると、よく見かけるのだけれど tempファイルをバンバン使うんですね。 でも、それってゴミファイルが残ったり管理がめんどくさいんですね。

<?php

$temp1 = tempnam(  sys_get_temp_dir() );
$temp2 = tempnam(  sys_get_temp_dir() );
$str = exec( 'cat '. $temp1 );
file_put_contents($temp2,$str);
pcntl_signal(SIGINT,  "sig_handler");
@unlink($temp1);
@unlink($temp2);

function sig_handler(){
@unlink($temp1);
@unlink($temp2);
}

削除処理が必要になる理由

一時ファイルを使うの用途にはメモリ上限になっちゃう巨大サイズのコマンド実行を扱うとき。

サイズが大きいデータをクラス間で受け渡すときによく使うんですね。だから一時ファイルを適切に削除しないと面倒が増える。

また、巨大なファイルを変数に読み込んでいると、php のメモリ上限などで困るんですよね。

プロセスを開いて流し込んでパイプでつなぎ、プロセス出力をファイルディスクリプタで受け取ればメモリを浪費せずに済む。

これで一時ファイルの管理の地獄にならないで助かる。

プロセスと一時ファイルを使う。

サイズの大きなファイルを扱うときに、変数に取り出したり、一時ファイルを作成して管理せずに直接にファイルディスクリプタを扱えば、管理から開放される。

<?php
function my_command(){
  $descriptor = [
    1 => $fd = fopen('php://temp','w'),
  ];
  
  
  $process = proc_open("cat", $descriptor, $pipes , '/tmp', []  );
  if( ! is_resource($process) ) {throw new \Exception("実行エラー");}
  
  while(proc_get_status($process)['running']) {
    usleep(1000);
  }
  fseek($fd);
  proc_close($process);
  
  return $fd;
  
}

php://temp をつかっておけば、SIG INIT や HUP時に残る一時ファイルはphpがうまいことやってくれる(はず)

proc_open で標準出力・入力を使ってコマンドを起動する

たとえば、ffmpeg を使って ssh 経由で 変換するとか

cat bigfile.ts | ssh ffmpeg -i   pipe:0   ...  pipe:1  | cat -> out.mp4
<?php
//
$cmd = preg_split('/\s+/','ssh server ffmpeg -i pipe:0  -f h265 pipe:1 ');
//
$f_in = fopen('/tmp/out.ts','r');
$f_out = fopen('/tmp/out.mp4','r');
$descriptor = [
  0 => ['pipe','r'],
  1 => ['pipe','w'],
  2 => STDERR,
  ];
//
$process = proc_open($cmd, $descriptor, $pipes , '/tmp', []  );
if( ! is_resource($process) ) {throw new \Exception("実行エラー");}

// write to pipe
while(!feof($f_in)){
  fwrite( $pipes[0], fread($f_in, 1024));
}
// fclose($pipes[0]);
// waiting
while(proc_get_status($process)['running']){
  usleep(1000);
}

while(!feof($pipes[1])){
  fwrite( $f_out, fread( $pipes[1], 1024));
}

//
proc_close($process);
//
fseek($f_out,0);
var_dump(fstat($f_out));

proc_close($process); するとパイプは自動で閉じられるので、パイプに残った内容を別のファイルに取り出す必要がありますね。

パイプでプロセスをつなぐ。

ここまで、わかれば、proc_open でプロセスをつないで、パイプパイプでプログラムを複数つなぐことができる。

<?php

/// プロセスA
$descriptor_a = [
  0 => $fin = fopen('/tmp/out.txt','r'),
  1 => ['pipe','w'],
  2 => STDERR,
];


$process_a = proc_open("cat", $descriptor_a, $pipes_a , '/tmp', []  );
if( ! is_resource($process_a) ) {throw new \Exception("実行エラー");}

/// プロセスB
$descriptor_b = [
  0 => $pipes_a[1],
  1 => ['pipe','w'],
  2 => STDERR,
];

$process_b = proc_open("cat", $descriptor_b, $pipes_b , '/tmp', []  );
if( ! is_resource($process_a) ) {throw new \Exception("実行エラー");}

// プロセス待ち
while(proc_get_status($process_a)['running']){
  usleep(1000);
}
while(proc_get_status($process_b)['running']){
  usleep(1000);
}
// 結果を取得
while(!feof($pipes_b[1])){
  fwrite( STDOUT, fread( $pipes_b[1], 1024));
}


//close
proc_close($process_a);
proc_close($process_b);

このようにすれば、シェル経由をせずともプロセスを複数つないで実行することができる。

注意点 : 2020-03-10 追加

proc_open を使っていてわかったのですが 、php の['pipe'] は、すぐに詰まります。 phpが proc_open のpipe を内部的にどうしてるのかわからないのですが。

pipe 経由で 数MBの出力をバッファリングすると、すぐにエラーになって固まります。ほんとうにこれphpのバグだと思う。

まとめ

proc_open は 標準出力・入力を指定して扱える。

デフォルト指定

$descriptor = [
  STDIN
  STDOUT,
  STDERR,
  ];

リダイレクト

入力をリダイレクト

$descriptor = [
  0 => fopen("/path/to", 'r')
  ];

出力をリダイレクト

$descriptor = [
  1 => fopen("/path/to", 'w')
  ];

エラーをリダイレクト

$descriptor = [
  2 => fopen("/path/to", 'w')
  ];

パイプ

出力をパイプする

$descriptor = [
  1 => ['pipe','w']
  ];
proc_open( $cmd, $descriptor, $pipes);
stream_get_content($pipes[1]);

入力をパイプする。

$descriptor = [
  0 => ['pipe','r']
  ];
proc_open( $cmd, $descriptor, $pipes);
fwrite($pipes[0], "aaaaaa");
fclose($pipes[0]);

一時ファイル php:/temp を使う

一時ファイルをコマンドの入力に渡す。

$fd_in = fopen('php://temp', 'w+');
fwrite($fd_in, "aaaaaa");
fseek($fd, 0);
$descriptor = [
  0 => $fd
  ];
proc_open( $cmd, $descriptor);

出力を一時ファイルに取り出す

$fd_in = fopen('php://temp', 'w+');
$descriptor = [
  1 => $fd
  ];
proc_open( $cmd, $descriptor);
fseek($fd, 0);

プロセスA→ プロセスB

プロセスをパイプでつなぐ

/// プロセスA
$descriptor_a = [
  0 => $fin = fopen('/tmp/out.txt','r'),
  1 => ['pipe','w'],
  2 => STDERR,
];
$process_a = proc_open("cat", $descriptor_a, $pipes_a , '/tmp', []  );

/// プロセスB
$descriptor_b = [
  0 => $pipes_a[1], // Aの結果をBへ
  1 => ['pipe','w'],
  2 => STDERR,
];
$process_b = proc_open("cat", $descriptor_b, $pipes_b , '/tmp', []  );

参考資料

おすすめ Linux(UNIX)のコマンドの実行とパイプの考え方が一番良くわかった本。

自動入力は悪と決めつけて邪悪な対応をするイオンカード。フィッシング・サイト対策にはむしろ自動入力を使いましょう。

イオンカードがまたやばい。

見に行ってみた。

うわああ邪悪すぎる。 input[type=url] にして自動入力をオフにしている。

f:id:takuya_1st:20200303152533p:plain

自動入力は「信用できる」

自動入力のほうがずっと信用できる。イオンカードのためにIDを覚えておくほうが不可能に近い。

IDも使いまわしてはいけない。

「パスワードの使いまわし、ダメ絶対」と一人歩きしているが、正しくは「ID/パスワードのペアの使いまわしは駄目」なんですよね。

ID自動入力を認めないと結局はIDも使いまわしされてしまう。

自動入力するひとはそもそも、自動生成もしてる可能性が高いし。ほんとうに何から何を守ってるんでしょうか。

ブラウザの入力履歴が悪とする、邪悪対応

ブラウザには入力履歴があり、入力履歴から自動入力が生成されるわけですが、履歴の自動保存をすると、間違った入力履歴も覚えてしまうのでトラブルのもとであるということでしょうか。

しかし、ここはインターネットです。インターネットではインターネットの流儀に従ってほしい。

謎リスクベース認証イオンカード

IPとCookieでリスクベース認証と謎なことを称するのであれば、ログイン後に閲覧時に追加パスワードを求めたらいい話である。

ログイン時にパスワードを排してメールアドレスだけで認証すればいい話である。

本当に何にコストを掛けているのやら。

そもそもID/PWを抜かれていたら、リスクベース認証もクソもないんですよ。

自動入力は「フィッシング詐欺」に対してとても有効

自動入力を使うと「ドメイン」が一致しないと、パスワードを入力できない。初心者にドメインを覚えろは不可能に近いので、自動入力を覚えろだと、とても有効である。なので、自動入力を封鎖するサイトは滅びていい

フィッシング対策としてとても有効なのです。初心者に「ドメイン覚えろ」なんて口が裂けても言えない。その代わり、「自動入力を使おう、もしパスワードが自動入力できないならそれは詐欺サイト」と呼びかけられる。

フィッシング詐欺サイトで自動入力が効かない例

これは昨日送られてきたAmazonフィッシング詐欺サイトだけど、ほら自動入力が効かないので、詐欺サイトと気づけるよね?

https://cdn-ak.f.st-hatena.com/images/fotolife/t/takuya_1st/20200307/20200307181306.gif

面倒はモノは使われない。

明細を確認するだけのために、「ワンタイムパスワード」「リスクベース認証」「自動保存入力不可」、どれだけ時間を掛けさせるんですか。無駄である。

しかも自動入力を使わせないのはフィッシング詐欺全盛期のいまは、安全策として最低である。

広告まみれのWEB明細

ログインしても広告まみれのWEB明細確認は、存在する目的を自問してほしいです。

メールアドレスで「認証」をでき、メールアドレスを信用するのであれば、そこあてに明細.pdf を送ってくればいいじゃないか。 PDF閲覧に必要なパスワードをWEBサイトで入手・登録すればいいわけですよ。