我一直在论坛上搜索如何允许用户运行通常只由 root 或其自身允许的命令/进程。
这里有一些很好的资源,但没有一个能帮助我,我希望 Ubuntu 天才可以给我指明正确的方向。
我使用 apt-get install cpulimit 安装了 cpulimit,这是一个可以限制特定进程的 CPU 使用率的软件包。安装后,我可以在 /usr/bin/cpulimit 中看到安装。
我现在必须在运行用户处理的进程时运行 cpulimit,在该进程代码内。
我在 visudo 文件中添加了几次尝试,所有这些都是这样的:
{USER} ALL=(ALL:ALL) NOPASSWD:/usr/bin/cpulimit
但是,当我运行命令(限制=百分比)时:
cpulimit --pid 159845 --limit 60
响应是:
Error: Process 159845 detected, but you don't have permission to control it.
其中159845是正确的进程,它是mysqld。
我可以运行 top -i 并看到该进程由用户 mysql 运行:
PID | 用户 | 公共关系 | 你 | 虚拟仿真测试系统 | 可再生能源 | 自发性高血压 | 年代 | %中央处理器 | %内存 | 时间+ | 命令 |
---|---|---|---|---|---|---|---|---|---|---|---|
159845 | mysql | 20 | 0 | 1460764 | 608076 | 3772 | 年代 | 15.6 | 29.9 | 862:34.15 | mysqld |
这让我认为没有必要添加允许 {USER} 运行 cpulimit 的初始 visudo 行,但更重要的是,当 {USER} 针对问题 159845 中的进程运行 cpulimit 时,这是不允许的,因为 {USER} 没有权限接触 mysql 的进程。
我作为 {USER} 如何在 mysql 的进程 mysql 上运行 cpulimit?
GENERIC:如何在不同“用户/帐户/角色”拥有的进程上以 {USER} 身份运行命令?
编辑:该命令正在 {USER} 的 crontab 中触发的脚本中运行。
编辑2:以 sudo 形式运行命令后,如下所示:
$limit_mysqld_cpu_usage = "sudo -u mysql cpulimit --pid " . $mysqld_id_exe_output[0] . " --limit " . $cpu_limit_as_perc_of_cpus;
// Run command
exec($limit_mysqld_cpu_usage, $limit_mysqld_cpu_usage_output, $limit_mysqld_cpu_usage_id);
脚本似乎冻结了,而之前它会继续执行脚本。经过尝试和测试,脚本在这一点之后运行良好,不会触发上述命令。
问:为什么这个脚本没有执行然后继续执行其余部分?
答:因为命令运行并且它开始监视该过程,阻止它按照我们期望的返回值执行代码,所以它会永远挂在这里。
问:如何在后台触发 cpulimit 命令的 exec(),或者无需观察并等待响应后再继续?
A:如果使用该函数启动程序,为了使其继续在后台运行,必须将程序的输出重定向到文件或其他输出流。如果不这样做,将导致 PHP 挂起,直到程序执行结束。
http://php.net/manual/en/function.exec.php
因此,本质上我们需要 > 到另一个空间(例如 /dev/null),并通过附加 & 在后台运行该进程。 在我的情况下,命令将需要如下所示:
sudo -u {用户} cpulimit --pid {ID} --limit 60 > /dev/null &"
我还用 exec() 代替了 shell_exec(),它似乎不需要所有参数,也不会产生那么大的响应:
shell_exec(sudo -u {USER} cpulimit --pid {ID} --limit 60 > /dev/null &");
答案1
在@steeldriver 的帮助下,我找到了解决问题的方法。
按时间顺序回答这个问题:
如何以 {USER} 身份在不同“用户/帐户/角色”拥有的进程上运行命令?
为了在以 {USER} 身份在 {ALTERNATIVE_USER} 上运行进程,我们必须使用 sudo,然后您可以指定哪个确切的用户应该运行该命令,如下所示:
sudo -u {ALTERNATIVE_USER} {COMMAND}
我如何触发 {COMMAND} 在后台运行以允许当前脚本继续?
当进程被触发时,脚本似乎冻结了,但它并没有冻结,只是在等待新触发的命令结束,但它不会结束,因为它正在作用于最初触发的进程,而该进程永远不会结束。
为了在启动挂起的命令时执行此操作,我们必须为其指定一个出口,以便它有地方挂起。这可以通过指向替代位置来完成,在我的例子中,我使用了> /dev/null
。
最后,我需要做的是在后台运行它,这可以通过附加来完成&
。
最后一点,具体来说,对我来说,我不希望收到回复,我只是希望命令运行并按照我说的做,所以我用 shell_exec() 替换了 exec()。
shell_exec(sudo -u {USER} cpulimit --pid {ID} --limit 60 /dev/null &");