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

Reply via email to