Bash 脚本在由 Apache 运行时和由 www-data 手动运行时的行为不同:为什么?

Bash 脚本在由 Apache 运行时和由 www-data 手动运行时的行为不同:为什么?

我下面有一个测试脚本,尝试执行一些文件系统维护。这只是一个简单的片段;它卸载 LVM 逻辑卷(安装在),并在设备上/data运行。e2fsck该脚本必须通过 Apache 远程运行。

问题是,当我登录远程并以 user 身份手动运行该脚本时,该脚本www-data可以工作,但当 Apache 运行它时,该脚本就不起作用。当 Apache 运行它时,脚本认为它已成功卸载/data,但e2fsck随后失败,因为它认为设备仍然繁忙。但是,测试fuser不会显示任何内容,并且当脚本终止时设备仍处于安装状态。

我手动运行它如下:

root@vserver # su www-data -s /bin/bash
www-data@vserver $ cfg-test.sh
Status: 200 OK
Content-type: text/html

<p>Completed Ok.</p>

完成后,远程系统的状态如预期:/data已卸载,系统日志包含:

Jan 23 15:51:26 vserver cfg-test.sh: Restarting as root
Jan 23 15:51:26 vserver cfg-test.sh: Unmounting /data...
Jan 23 15:51:27 vserver cfg-test.sh: Unmount succeeded; ll /data: (total 0)
Jan 23 15:51:27 vserver cfg-test.sh: e2fsck Ok

这正是我所期待的。我可以通过浏览到 来在网络上运行它https://vserver/cfg-test.sh。然而,这是行不通的;浏览器报告Terminated with code 3. See the syslog for details.系统日志显示:

Jan 23 15:48:49 vserver cfg-test.sh: Restarting as root
Jan 23 15:48:49 vserver cfg-test.sh: Unmounting /data...
Jan 23 15:48:49 vserver cfg-test.sh: Unmount succeeded; ll /data: (total 0)
Jan 23 15:48:49 vserver cfg-test.sh: fuser -c /dev/mapper/vg0-data: ()
Jan 23 15:48:49 vserver cfg-test.sh: e2fsck failed; terminating (/dev/mapper/vg0-data is in use.#012e2fsck: Cannot continue, aborting.) (/dev/mapper/vg0-data)

然而,他们umount确实不是成功;/data仍然安装在远程服务器上。为什么这个结果与手动运行脚本不同?如果相关的话,远程是 Ubuntu 22.04 VM。该脚本位于文件中,允许无需密码sudoers即可运行。www-data


#!/bin/bash

scriptname=$(basename "$0")
dev=/dev/mapper/vg0-data

retcode=0

# log messages to syslog
log() {
    if (($# < 1)); then
        return
    fi

    msg="$1"
    shift
    while (($# > 0))
    do
        msg+=" ($1)"
        shift
    done
    logger -t "$scriptname" "$msg"
}

if ((EUID > 0)); then
    log "Restarting as root"
    exec sudo "$0" "$@"

fi

while true ; do
    log "Unmounting /data..."
    output=$(umount -vvv --force /data 2>&1)
    code=$?
    if ((code > 0)); then
        log "umount /data failed; terminating" "$code" "$output"
        log "ll /data:" "$(ls -l /data)"
        retcode=1
        break
    fi

    # check mount's output
    if output=$(mount | grep "$dev" 2>&1); then
        log "/data is still mounted!" "$output"
        log "ll /data:" "$(ls -l /data)"
        retcode=2
        break
    fi


    # exit codes 0 and 1 are both Ok
    log "Unmount succeeded; ll /data:" "$(ls -l /data)"
    output=$(e2fsck -p -f "$dev" 2>&1)
    if (($? > 1)); then
        log "fuser -c $dev:" "$(fuser -c $dev)"
        log "e2fsck failed; terminating" "$output" "$dev"
        retcode=3
        break
    fi

    log "e2fsck Ok"
    break
done

if ((retcode == 0)); then
    echo "Status: 200 OK"
    echo "Content-type: text/html"
    echo ""
    echo "<p>Completed Ok.</p>"
else
    echo "Status: 400 Bad Request"
    echo "Content-type: text/html"
    echo ""
    echo "<p>Terminated with code $retcode. See the syslog for details.</p>"
fi

相关内容