同一脚本的不同答案取决于调用方(php-exec()与控制台)


Different answer from same script depending on caller (php exec() vs. console)

我通过二进制包装器从PHP(5.4)使用root权限运行Bash Scripts(请参阅[Execute root commands via PHP),除了下面的例子之外,它运行得很好。此外,我在CentOS7上的linux上使用zfs。

我准备了两个简单的例子Bash脚本:

test_zfsadd:

#!/bin/bash
#ARGS=1
err="$(zfs create $1 2>&1 > /dev/null)"
if [ $? -ne 0 ]
then
        echo $err
        exit 1
fi
echo "OK"
exit 0

test_zfspart:

#!/bin/bash
#ARGS=1
msg="$(zfs get mounted $1 -H | awk '{print $3}')"
echo $msg
exit 0

当我用例如从PHP调用相应的二进制文件时

<?php
$partition = 'raid1/testpart';
$ret = shell_exec("/path/test_zfsadd_bin $partition");
echo "<p>Return value: $ret</p>'n";
$ret = shell_exec("/path/test_zfspart_bin $partition");
echo "<p>Is mounted: $ret</p>'n";

输出为:

Return value: OK
Is mounted: yes 

这看起来不错,但当我直接从控制台调用"test_zfspart_bin raid1/testpart"时,我得到了正确的结果,即

no

(表示分区未装入,已在/proc/mounts中签入)。因此,根据上下文的不同,我从同一个脚本中得到了两个不同的答案。我最初认为这与SUID位有关,但在控制台中用无特权用户调用脚本效果良好。如果我尝试(作为root用户)

zfs mount raid1/testpart

在控制台中,我得到

filesystem 'raid1/testpart' is already mounted
cannot mount 'raid1/testpart': mountpoint or dataset is busy

这很奇怪。我也不能从控制台销毁"分区",这只能在PHP中使用。另一方面,如果我直接从bash创建一个分区作为root,并试图通过PHP删除它,它也不起作用。看起来分区在某种程度上被上下文分隔开了。如果我做,一切都会再次同步

systemctl restart httpd

我认为apache或PHP在一定程度上使zfs系统繁忙,但我完全不知道为什么以及如何。任何解释或变通方法都将不胜感激。

在此期间,我自己想好了。。问题不在于apache进程本身,而在于systemd如何启动它。有一个名为"PrivateTmp"的选项,默认情况下在httpd服务文件中设置为"true"(至少在CentOS7中)。手册页上写着

PrivateTmp=采用布尔参数。如果为true,则设置新的文件系统已执行进程的命名空间,并装载一个私有/tmp内部的目录,该目录不由外部的进程共享命名空间。这对于保护对临时文件的访问非常有用进程的,但通过/tmp在进程之间进行共享不可能的默认为false。

这就解释了我的想法。新创建的zfs分区安装在这个"虚拟"文件系统中,因此对系统的其他部分是不可见的,这在这种情况下是不需要的。apache进程无法在其命名空间之外装载或卸载文件系统。禁用该选项后,一切如预期。