systemd
菜鸟,在这里。
我一直在inotifywait
监视一个文件夹,并在满足条件时运行一个脚本。我设置了一个systemd
服务文件,以便在启动时将此脚本作为守护进程运行,但它以 root 身份运行该脚本。我想知道是否有办法让服务以当前用户身份运行(而不是在服务文件中指定用户),这样$HOME
对于当前用户来说是正确的,允许我添加任意数量的用户,所有用户都可以访问该脚本。
我已经阅读过systemctl --user
,和enable-linger
,但老实说,我不知道最好的解决方案。
最终,我希望服务能够识别当前用户并以$HOME
完整的用户身份运行本地脚本。
答案1
简短回答
我认为你想要做的是:
- 将 shell 脚本移动或链接到所有用户都可以访问的地方,并确保每个用户都有执行该脚本的权限。
- 对于每个用户,创建一个用户服务(使用 的类型
systemctl --user
)在启动时运行该脚本。
每个用户服务都是相同的;关键在于每个用户在启动时都有自己的服务为他们运行,链接到他们的"${HOME}"/.config/systemd/user/
目录,并systemctl --user enable
为他们服务。
用户服务以其用户身份(而非 root)运行,因此每个服务都将以其相应的用户身份运行并继承该用户的“环境”。该环境将传递给服务生成的任何进程 - 即您的 shell 脚本,$HOME
当 foo 用户的服务运行该脚本时,它将扩展为“/home/foo”之类的内容。当 bar 的服务运行它时,$HOME
它将扩展为“/home/bar”,依此类推。
更多信息
这是一些一般(和一些小众)信息,可能有助于理解正在发生的事情。
首先,系统服务和用户服务。
系统服务是“默认”类型的服务,您设置的服务可能是其中之一。
- 他们的服务文件已链接
/etc/systemd/system/
。 - 它们默认以 root 身份运行。⚠️(因此,他们拥有 root 权限!)
- 它们产生的进程不继承任何环境(例如,在服务运行的 shell 脚本中,
$HOME
实际上为空 --“”)。 - 他们被控制了
systemctl <command> <service>
。
用户服务与制作它们的用户有明确的联系。
- 他们的服务文件已链接
"${HOME}"/.config/systemd/user/
(如果不存在则可能必须创建此目录)。 - 它们默认以创建它们的用户身份运行(使用那用户的权限,不是根权限)。
- 它们产生的进程继承了该用户的环境(例如,在服务运行的 shell 脚本中,如果名为 foo 的用户创建了该服务,
$HOME
则会扩展为)。/home/foo
- 他们被控制着
systemctl --user <command> <service>
- 当该用户的会话结束时,它们也会终止(这可以通过
enable-linger
您阅读的命令覆盖,但我认为这与您的问题无关)。
最终,我希望服务能够识别当前用户并以 $HOME 完整的用户身份运行本地脚本。
这句话单独询问如何将当前用户的环境(例如,像这样的变量$HOME
)导入到由服务执行的脚本中。
导入环境的方法有很多种,从启动服务的命令的参数到 systemd 设置,再到服务文件中设置的选项。
这是我所知道的 3 个。
1. 简单地重新声明环境变量
这是最愚蠢的做法。
如果名为 foo 的用户想要$HOME
扩展到“/home/foo”并$BARHOME
扩展到“/home/bar”,他们可以将其放在脚本的顶部:
HOME=/home/foo
BARHOME=/home/bar
简单!当目录或文件路径发生变化时,维护工作将变成一场噩梦,所有执行此操作的脚本都需要更新以反映更改。
2. 使用系统服务中的User=
和选项Group=
我认为这就是您在“在服务文件中指定用户”中提到的:
我想知道是否有办法让服务以当前用户身份运行(而不是在服务文件中指定用户)
为了系统服务以 root 身份运行并且没有默认环境,foo 用户可以将此部分放在服务的.service
文件中:
[Service]
User=foo
Group=foo
这将导致系统服务以 foo(而非 root)身份运行,使用 foo 的权限(不再是 root 的权限)和 foo 的环境。因此,在服务执行的 shell 脚本中,$HOME
将扩展为“/home/foo”,$USER
将扩展为“foo”,依此类推。
如果您想知道,使用User=root
和Group=root
仅具有导入 root 环境的效果($HOME
将扩展为“/root”而不是“”,$USER
将扩展为“root”等);系统服务仍然以 root 身份运行,具有 root 权限,就像默认情况下一样。
笔记:这些选项仅允许系统服务。如果用户服务在其部分中有这些选项[Service]
,运行时会出错。用户服务都是明确绑定到用户的,因此它们不支持以与创建和运行它们的用户不同的用户身份运行。
以这种方式将用户环境导入系统服务的缺点是 root 权限会丢失,并被该用户的权限取代。(如果服务不需要 root 权限,则不会造成任何损害,尽管它可能是一个用户服务。
另一个缺点是它根本无法实现你想要的“多用户”功能。(再次强调,用户为每个用户提供服务可能是实现这一目标的最佳方式。
3. 使用 systemd 的DefaultEnvironment
变量
这是我导入环境变量的方式。这可能有点奇怪,但它有两个优点:
- 它独立于
User=
和Group=
选项工作,将服务环境与其用户分离。 - 它允许用户(例如root)使用他们的环境变量同时与其他用户的(例如,foo 的)环境变量。
我们假设 foo 用户在其 中声明了所有环境变量.profile
(这是惯例)。作为额外的改动,foo 用户将其主目录硬编码在名为 的变量中$UHOME
。因此,在 中/home/foo/.profile
,我们有:
export UHOME="/home/foo"
类似地, foo 保存二进制文件,可以像这样$UBIN
定义:/home/foo/.profile
export UBIN="${UHOME}"/bin
我们马上就会看到这会给我们带来什么。
在 中/etc/systemd/system.conf
,取消注释DefaultEnvironment
变量,并在其中声明一个名为$UPROFILE
hardcoded 的环境变量来引用 foo 的.profile
:
DefaultEnvironment="UPROFILE=/home/foo/.profile"
现在,在任何服务的 shell 脚本中,甚至是系统服务中,我们都可以将其放在脚本的顶部:
# Get foo's env vars.
source "${UPROFILE}"
即使以 root 身份运行的系统服务执行此脚本,foo 也.profile
将被获取,因此 foo 的环境变量将被加载,甚至与 root 的环境变量一起(假设系统服务使用User=root
和Group=root
继承 root 的环境)。因此,在脚本中,$HOME
将扩展到 root 的主目录(“/root”),而将扩展到 foo 的主目录(“/home/foo”)。这就是在 foo 中$UHOME
命名变量给我们带来的好处;它不会与 root 的环境变量发生冲突!$UHOME
.profile
$HOME
我们不必担心哪个用户运行该服务或他们的环境变量是什么!foo 的环境变量始终正确,因此我们总能找到 foo 的内容。而且,由于 foo 的环境变量在一个地方(foo 的.profile
)定义,因此对它们的更改会自动反映在系统脚本中,因为它们是 foo 的资源.profile
——无需维护脚本。
这对于在以 root 身份运行的系统服务脚本中的非 root 用户环境非常有用。
但是,这与您的问题完全无关,您的问题是:
我想知道是否有办法让服务以当前用户身份运行(而不是在服务文件中指定用户),以便 $HOME 对于当前用户来说是正确的,允许我添加任意数量的用户,并且所有用户都可以访问该脚本。
...如果你能摆脱这种情况,并且不介意它有多荒唐,你可以让每个用户在他们的 s 中使用自己的环境变量命名方案.profile
,这里的环境包含方法 #3 可以用来为每个用户在启动时做任何你想做的事情。一用户启动。
例如,用户 foo 可以$FOOHOME
在他们的中定义.profile
;用户 bar 可以$BARHOME
在他们的中定义.profile
;等等。
在中/etc/systemd/system.conf
,您可以引用每个用户的.profile
:
DefaultEnvironment="FOOPROFILE=/home/foo/.profile" "BARPROFILE=/home/bar/.profile"
然后,在 shell 脚本的顶部添加:
# Get all users' env vars.
source "${FOOPROFILE}"
source "${BARPROFILE}"
现在,当用户启动时,系统服务将在用户启动时运行该脚本,$FOOHOME
将扩展为“/home/foo”,$BARHOME
将扩展为“/home/bar”,依此类推。
但这只是疯狂的思考;听起来你真正想要的是我所说的简短回答部分。
如果您希望某个服务在任何用户启动时运行,并$HOME
在服务运行的 shell 脚本中扩展到该用户的主目录,则不必使用DefaultEnvironment
。创建一个用户为每个用户提供服务,并让所有用户服务都运行该脚本,该脚本位于他们都可以访问的位置。