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
  • 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 at 10:55
  • @muru I have added a context section for background info – S. Green Jul 8 at 13:51
  • 1
    you can obtain the environment of any process with tr '\0' '\n' </proc/<PID>/environ. – Uncle Billy Jul 8 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 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 at 14:23
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.

  • 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 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.