SOFTELメモ Developer's blog

会社概要 ブログ 調査依頼 社員募集 ...

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 を共有しようとすると、ファイルがないということで混乱します。

関連するメモ

コメント