我有一个sh
脚本,它在我们的服务器上运行。它是用另一个脚本中的nohup
启动的,这样我就可以很容易地关闭会话,并且它仍然运行
主脚本(将启动此脚本)也是一个sh
脚本。它可以用两个参数执行:
--start (will start application)
--stop (will stop it using kill PID)
但由于应用程序是由我启动的,所以其他任何程序(除了root)都无法停止或重新启动已经在运行的实例
所以我更多地考虑将所有者更改为nobody
。这有帮助吗?如果是的话,那该怎么做呢?
设置脚本以在某个事件中关闭自己,而不是运行到终止。然后让主脚本在--stop
触发该事件(该事件可以是简单的事情,比如创建特定的文件,也可以是更复杂的事情,就像向脚本定期读取的fifo写入命令)。这样,其他用户应该能够关闭脚本,无论是谁启动的
如何做到这一点取决于脚本的细节。如果它没有一个可以检查关闭条件本身的循环,你可以做一些事情,比如让它生成一个后台进程,定期检查条件,并在满足条件时杀死它的父pid。
设置一个专门用于运行此脚本的用户。
并使用运行脚本
su -l <user> -c "<command>"
更新:
提出这种方法的前提是不能修改有问题的脚本。
将所有者更改为nobody
当然有助于实现您的目标。下面描述了一种通过使用用户不可见的专用帮助程序来实现这一点的方法。
解决方案:您可以使用setuid程序来确保脚本始终以同一用户身份运行。由于安全原因,您可能不允许将shell脚本设置为setuid,但您可以编写一个简单的启动器程序,可能是在C:中
#include <stdio.h>
#include <unistd.h>
#define PROG "./daemon.sh"
int main(int argc, char** argv)
{
char action[32];
if (argc < 2) {
fprintf(stderr, "Usage: %s { --start | --stop }n", argv[0]);
return 0;
}
snprintf(action, sizeof(action), "%s-the-real-thing", argv[1]);
return execl(PROG, PROG, action, (char *)0);
}
这里我们假设您的脚本名为daemon.sh
,并且位于运行脚本的同一目录中。您可以根据需要使用完整路径或以其他明显的方式进行更改。
编译并使setuid如下(这里假设名称为launcher.c
,我们决定以nobody
运行):
$ gcc -Wall launcher.c -o launcher
$ sudo chown nobody launcher
$ sudo chmod u+s launcher
您需要稍微修改您的shell脚本守护程序:
#!/bin/sh
prog=$(basename $0)
start()
{
i=0
while :; do
i=$(expr $i + 1)
echo "Daemon working like a mad horse... ($i)"
sleep 1
done
}
stop()
{
pids=$(ps axu | grep "$prog.*--start" | grep -v grep | awk '{print $2}')
echo "Killing [$pids]"
[ -n "$pids" ] && exec /bin/kill -KILL $pids
}
case "$1" in
--start|--stop) exec ./launcher $1;;
--start-the-real-thing) start;;
--stop-the-real-thing) stop;;
*) echo "Bad argument "$1"";;
esac
这里的start
函数是完成所有有趣的放克的地方,直到你通过调用stop
函数杀死程序。从任何地方运行这个程序都需要一些明显的更改,我希望您现有的kill
逻辑更优雅。
用法与以前类似:
./daemon.sh --start
./daemon.sh --stop
说明:以上将分别执行./launcher --start
或./launcher --stop
。这会将有效用户更改为nobody
(或您选择的设置程序uid的任何内容),并执行./daemon.sh --start-the-real-thing
或./daemon.sh --stop-the-real-thing
,执行所需的原始操作。
因为简单的启动器程序只允许运行命名脚本,所以安全性很容易控制(只是为了确保,您可以在启动器程序中检查argv[1]
是否真的是--start
或--stop
,然后可以运行两个命令行组合)。
您可以尝试以下选项:
- 在您的进程中创建一个TCP套接字,其他用户启动的客户端进程可以与之通信
- 从您的用户启动一个独立的TCP服务器,它可以通过TCP接受一些有限的命令集。该服务器可以接受来自任何用户的守护程序关闭命令。然后,该进程可以为您的主进程发出终止命令
- 创建一个名为fifo的文件,任何用户都可以写入该文件。创建一个子线程,尝试从fifo中读取。由于从本质上讲,fifo读取调用是阻塞调用,所以该线程将保持睡眠(不会消耗CPU)。在其他用户向fifo写入内容后,线程将唤醒&干净地终止父线程
- 与上面相同,但线程等待是另一个进程的一部分,由同一用户启动。该进程在唤醒时将杀死主进程
在我看来,第三种方法是三种方法中最干净的一种。
编辑:由于"主进程"实际上是一个shell脚本,因此"线程"将更改为子shell&等待的子线程将是简单的"cat fifo"。
如果您有root访问权限,可以使用sudo
并将其配置为允许以--stop
参数作为root运行脚本。