On 07/04/13 20:45, Mantas Mikulėnas wrote:
On Sun, Apr 7, 2013 at 10:21 PM, John Lane <syst...@jelmail.com> wrote:
I have been trying to replace an rc script with a systemd unit file. It is
for an rvm (ruby version manager) environment and it starts a documentation
web server. I tried to create a service unit thus:
[Service]
Type=forking
User=rvm
ExecStartPre=/bin/bash --login /etc/profile.d/rvm.sh
ExecStartPre=/usr/local/rvm/bin/rvm use %i
ExecStart=/${GEM_HOME}/bin/gem server --daemon --port 8808
The rvm environment needs to be initialised (by running
/etc/profile.d/rvm.sh) and this needs to happen in a bash login shell (this
is due to the design of rvm).
I think the ExecStart* lines in the above example are each invoked in a
separate environment (so the initialisation performed by the first is lost
before the second and third items are processed). Is this right or do I
misunderstand?
That's right, but not because of systemd.
When you run "bash /etc/profile.d/rvm.sh", it works in non-interactive
mode: first it interprets the given script, then simply exits. This is
the same regardless of where and how it is run. Since profile.d/rvm.sh
just sets environment variables, and those do not propagate upwards,
the entire command has no effect.
In actual login sessions, your shell starts and "sources"
/etc/profile, which then "sources" the profile.d scriptlets – instead
of running a separate copy of bash for them. Since systemd is not a
shell and does not understand (ba)sh syntax, the same thing cannot be
done here, at least not directly.
yeah, I knew about /etc/profile being sourced and it sourcing the
scripts in profile.d and that resulting in configuring a current shell
environment. I had overlooked the fact that systemd isn't executing a
shell environment.
My question is to ask how would I create a unit that starts a service in an
properly initialised environment when the only way to initialise that
environment is to run a bash setup script that requires it to be run in a
login shell ?
My first thought is "fix the damn setup script". Surely rvm already
has a way to be used non-interactively?...
Actually – is the "login shell" requirement imposed by rvm.sh itself
checking for that, or simply by the fact that rvm.sh is in
/etc/profile.d? — If it's the former, then rvm is broken, but if it's
the latter, then it's not a real requirement as rvm.sh could be
sourced manually without relying on /etc/profile to do it.
The "login shell" requirement is imposed by rvm.sh but only because the
normal way of running it is, as you say, to source it via /etc/profile.
You can indeed directly source /etc/profile.d/rvm.sh.However, what it
does is set up a shell function called "rvm" that is used by subsequent
commands, so it all needs to happen in a single environment (my example
unit above absolutely listed the path to 'rvm' but it must not do this,
because 'rvm' should resolve to a shell function not to an executable. A
command in systemd ExecStart must be given absolutely so that's break.
It might be that all you need is to set $PATH and $GEM_HOME
environment variables using the Environment= property (or
EnvironmentFile=.)
(But note that you cannot use variables in the zeroth argument for ExecStart.)
Ah yes, I just relaized that. That is another spanner because the path
to ${GEM_HOME}/bin/gem is dependent on the rvm environment correctly
setting up ${GEM_HOME}.
My solution that works is to use a unit to invoke a bash script that
performs the initialisation and then starts the service. But I feel this
somewhat negates the benefits of systemd not being based on shell scripts.
I'd like to try and get it to work with a unit file and no supporting
scripts, if this is possible.
It doesn't completely negate the benefits, as the script only performs
initialization for your service – you still get the advantages of
systemd enforcing dependencies, starting multiple services in
parallel, process tracking, and pretty much everything else. The only
significant downsides I can think of are having to load heavy
/bin/bash, and similarly heavy rvm.
I use rbenv to manage different Ruby versions, and while it probably
doesn't offer the same functionality as rvm, it's also much simpler to
use: it only needs <rbenv-path>/shims to be added to $PATH.
I was going to look at rbenv a while back but I stuck with rvm because I
like the way it can easliy build new environments. I think what I have
learnt from this is that an rvm unit would need to set up a shell
environment that included the 'rvm' function and then call that function
to issue further commands. None of that is congruent with what systemd
does (it isn't a shell environment and it can only invoke executables
using an absolute path that cannot be specified using environment
variables). I think, in this case, using a supporting shell script is
the only way to go. My scripted solution works for me and I'm just gonna
run with it. I was trying to be too much of a purist :)
John.
--
Mantas Mikulėnas <graw...@gmail.com>
_______________________________________________
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel