2

Question

Given a systemctl template and unit file as follows, I am trying to obtain the environment variables that will ultimately be passed to a process on start (regardless of whether the service is currently running):

/etc/systemd/system/example-template@.service

...
[Service]
Environment="VAR1=val1"
Environment="VAR2=val2"
...

/etc/systemd/system/example-template@example-unit.service.d/unit.conf

[Service]
Environment="VAR2=Overridden by %i"

After exploring the options offered by systemctl I haven't been able to find anything other than systemctl show example-template@example-unit --property=Environment, the output of which contains the variables as a single string:

Environment=VAR1=val1 VAR2=Overridden by example-unit

However as it contains no convenient delimiter I would prefer to avoid having to parse this to recover the original variables.

Originally I had thought to utilise the EnvironmentFile directive by moving the variables into their own files and sourcing them from my script. Unfortunately at least one of my variables requires the %i parameter expansion which is not performed on those defined in an EnvironmentFile.

Is there a better way to achieve what I need?

Context

We have 80 Tomcat instances which have been migrated from an OpenSUSE server to a CentOS 7 server. Historically these have been managed via a System V init script which was cloned for each instance. The init scripts are functionally identical, but differ by the values they export before delegating start/stop commands to the catalina.sh script shipped with Tomcat. Updating any common functionality requires applying the change to all 80 files.

In addition to this we have a wrapper script which delegates to catalina.sh, adding such options deleting deployed files or tailing log files. This is the script which requires knowledge of the environment variables; currently these are exposed by invoking the init script with particular arguments, whether the server is running or not.

Instead of sticking with System V and consolidating the common functionality into a single sourceable script (my original intention), I decided to migrate to systemd to take advantage of its support for unit file templates as they seemed designed with this scenario in mind. This would allow me to globally define our default Tomcat settings while allowing individual instances to override certain variables (e.g. to increase JVM memory or test an applciation against a new version of Tomcat). The environment variables unique to each instance include such things as the PID file (which catalina.sh needs to be made aware of) and Tomcat base (config) location.

With that in mind the issue I'm facing is that the aforementioned wrapper script would no longer have access to the environment variables for a Tomcat instance (for example one of the variables is the CATALINA_HOME path which contains the deployed applications, logs, and config files for the server). Some of the functions of the script do not require the server to be running (such as cleaning a stopped server), so obtaining the environment from the process is not viable.

Now I could make the wrapper script derive (guess) this information from the unit name, or forego running the Tomcat servers as a service entirely by making the wrapper script invoke the catalina.sh script directly; however I was hoping to have the sole source of information about the service be the service itself, and also be able to utilise some of the features of systemd such as automatically restarting the service if it enters a failed state.

Version Info

  • CentOS Linux release 7.6.1810
  • systemd 219
  • bash 4.2.46(2)-release
10
  • 1
    "Is there a better way to achieve what I need?" ... why do you need the environment variables? You have an XY problem, so start with how you ended up at this problem.
    – muru
    Jul 8, 2019 at 10:55
  • @muru I have added a context section for background info
    – S. Green
    Jul 8, 2019 at 13:51
  • 1
    you can obtain the environment of any process with tr '\0' '\n' </proc/<PID>/environ.
    – user313992
    Jul 8, 2019 at 14:08
  • If the service is logging properly, getting logs is easy: journalctl -u foo@instance. I imagine similar better ways could exist for the rest of your wrapper script's functionality.
    – muru
    Jul 8, 2019 at 14:11
  • @UncleBilly my apologies, I had not included that some of the wrapper functions do not require the server to be running. Question updated.
    – S. Green
    Jul 8, 2019 at 14:23

1 Answer 1

0

I recently completed a migration like this.

Defaults that apply to all units go in Environment= directives in the template file. EnvironmentFile=production.%i.env is used to load per-instance overrides.

Don't worry that you can't use %i in production.foo.env. Just hardcode "foo" in that file. That can be automated if needed.

1
  • Thanks for your answer. That will work as long as none of the variables that the script needs to know about require a default, as otherwise I can just source the .env file. I will need to analyse the script a bit closer to confirm whether that is the case.
    – S. Green
    Jul 9, 2019 at 12:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .