phpから /tmp にファイルが書き込めない
問題
phpからファイル /tmp/foo/bar.log にfile_put_contents()しているのですが、全然書き込みできません。
[18-Jan-2020 02:02:21 UTC] PHP Warning: file_put_contents(/tmp/foo/bar.log): failed to open stream: No such file or directory in /xxx/yyy/zzz.php on line 75
手動でファイルを作るとちゃんと作成されるのですが…
答え
CentOS6以前は問題なかったのに、CentOS7、CentOS8などに移行するとこの問題に遭遇するパターンが多いかと思います。
CentOS7、CentOS8 では、systemdが採用されています。
systemdに登録したサービスには、/tmp 直下や /var/tmp 直下をそのまま使用させないで、/tmp や /var/tmp の中にサービス固有の一時ディレクトリが割り当てられる仕組みがあります。
サービスの設定が以下のようになっていたら、そうなっています。
PrivateTmp=true
例) /usr/lib/systemd/system/httpd.service
# See httpd.service(8) for more information on using the httpd service.
# Modifying this file in-place is not recommended, because changes
# will be overwritten during package upgrades. To customize the
# behaviour, run "systemctl edit httpd" to create an override unit.
# For example, to pass additional options (such as -D definitions) to
# the httpd binary at startup, create an override unit (as is done by
# systemctl edit) and enter the following:
# [Service]
# Environment=OPTIONS=-DMY_DEFINE
[Unit]
Description=The Apache HTTP Server
Wants=httpd-init.service
After=network.target remote-fs.target nss-lookup.target httpd-init.service
Documentation=man:httpd.service(8)
[Service]
Type=notify
Environment=LANG=C
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
# Send SIGWINCH for graceful stop
KillSignal=SIGWINCH
KillMode=mixed
PrivateTmp=true
[Install]
WantedBy=multi-user.target
実体は /tmp や /var/tmp に、このような名前で存在しています。
# ls -la /tmp | grep systemd drwx------ 3 root root 17 Jan 11 23:37 systemd-private-0be8b316abe94a70ba08d003eefed8e9-chronyd.service-4Fl9Cw drwx------ 3 root root 17 Jan 12 01:02 systemd-private-0be8b316abe94a70ba08d003eefed8e9-httpd.service-vkwxUU drwx------ 3 root root 17 Jan 14 15:00 systemd-private-0be8b316abe94a70ba08d003eefed8e9-mysqld.service-YgkgJk drwx------ 3 root root 17 Jan 17 14:45 systemd-private-0be8b316abe94a70ba08d003eefed8e9-php74-php-fpm.service-tdglJr drwx------ 3 root root 17 Jan 18 11:24 systemd-private-0be8b316abe94a70ba08d003eefed8e9-postfix.service-Yi7WNA
$ ls -la /var/tmp/
total 8
drwxrwxrwt. 7 root root 4096 Jan 18 00:00 .
drwxr-xr-x. 22 root root 4096 Jan 12 00:23 ..
drwx------ 3 root root 17 Jan 11 23:37 systemd-private-0be8b316abe94a70ba08d003eefed8e9-chronyd.service-EdfFon
drwx------ 3 root root 17 Jan 12 01:02 systemd-private-0be8b316abe94a70ba08d003eefed8e9-httpd.service-mppoNh
drwx------ 3 root root 17 Jan 14 15:00 systemd-private-0be8b316abe94a70ba08d003eefed8e9-mysqld.service-PyjPgi
drwx------ 3 root root 17 Jan 17 14:45 systemd-private-0be8b316abe94a70ba08d003eefed8e9-php74-php-fpm.service-4y40z6
drwx------ 3 root root 17 Jan 14 10:43 systemd-private-0be8b316abe94a70ba08d003eefed8e9-postfix.service-k21kk8
サービスが起動すると作成されます。サービスが停止すると削除されます。サービスが再起動すると、改めて固有パスで再作成され、一定のパスではありません。
/tmp ディレクトリを共有するのはセキュリティの面で危険なため、各サービスにプライベートな /tmp を持たせる仕組みです。
systemd管理のサービス用なので、サービスの実行ユーザーにsuしてコマンドでファイルを作るなどした場合はそのまま /tmp にファイルが作られます。
それでも /tmp に書き込みたい
以下のように設定すれば、/tmp に書き込み可能になります。
PrivateTmp=false
ただ、安全のための仕組みなので、安易に PrivateTmp=false に設定するのは推奨されません。
通常は困らない
永続的なログなどは、/var/log などの中に書き込まないと消えてしまうので、/tmp は使わないのがよいでしょう。
phpの処理の中では、特に意識しなくても ‘/tmp’ で指定すれば、’/tmp/systemd-private-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-php74-php-fpm.service-xxxxxx/tmp’ にファイルが作成されます。
Webのphpと、cronやコマンドラインのphpで、/tmp を共有しようとすると、ファイルがないということで混乱します。
コメント