Discussion:
Question: path-based deactivation or equivalent
Add Reply
m***@giassa.net
2017-09-26 23:15:47 UTC
Reply
Permalink
Raw Message
I have a project where I'm adding some services to a Raspberry Pi 3,
and have decided to go with systemd being (mostly) responsible for
launching all of the services. All of the server processes use a common
API to do some initial setup (argument parsing, init, etc), drop root
permissions, and then carry out their normal duties. One of these
services is the "master" service, which I want to use to
activate/deactivate other services on-demand.

I started out with path-based activation via systemd, and that
worked fine for bringing up the services as I requested them. However,
it seems there is no equivalent path-based deactivation functionality at
this time [1], so it's definitely not an option on the older OS deployed
on my dev board.

I've looked into finding a suitable workaround, but the only viable
option so far is a hack that involves creating two separate systemd
scripts for a single service [2]. Is there some other functionality to
systemd that would permit me to:
-Activate and deactivate a service on demand (i.e. start it; stop it via
SIGNIT and after a timeout send it SIGKILL).
-Only have a single systemd script per service rather than the
two-scripts-per-service hack noted in [2].
-Allow a non-root (unprivileged) user to invoke my API/wrapper to
start/stop the services/daemons I maintain.
-Provide similar activation logic to path-based activation (i.e. only
start the service if both its dependencies on other services has been
met, AND the path/socket/etc exists).

Some of these services are provided to me in binary-only form, so I
can't necessarily modify all of them from source. Also, I'm not allowed
to use the LD_PRELOAD approach to injecting features. For all intents
and purposes, let's assume I can only modify my "manager" service, and
the systemd scripts for all services.

Thank you for your time and assistance.

Addendum:
------------------------------------------------------------------------
I'm doing this to automate a test suite that evaulates how the
various services handle failure cases when one or more random services
goes up and down. The test suite, like the services/daemons themselves,
is not permitted root access, and has to be able to run as an
unprivileged user (i.e. no "systemctl $SERVICENAME start|stop" is
permitted via the command line). If there's an equivalent to the
path-based activation/deactivation as noted in [2], it makes my life a
lot easier.

Eventually, some services may be able to sidestep the "master"
service, and kill off other services (i.e. dependencies), so having the
"master" service just kill() the other services is not viable long-term.

References:
------------------------------------------------------------------------
1. "RFE: systemd.path should support deactivation #3642",
<https://github.com/systemd/systemd/issues/3642>, Last accessed 2017-09-26.

2. "Can I use systemd to start and stop a service based on the presence
of a file?",
<https://unix.stackexchange.com/questions/286769/can-i-use-systemd-to-start-and-stop-a-service-based-on-the-presence-of-a-file>,
Last accessed 2017-09-26.
Matthew Giassa
2017-09-27 04:07:16 UTC
Reply
Permalink
Raw Message
Additionally, does systemd support assigning an environment variable to an internal variable, ie:

Environment=MYPIDFILE=/var/run/mine.pid
PIDFILE=$MYPIDFILE

I know there are workarounds for ExecStart for example, using /bin/bash to evaluate environment variables, but I think that special case only applies to ExecStart, ExecStartPre, and Executor, as they actually get executed rather than just assigned, allowing for some degree of variable expansion.

There are a few systemd options I would like to set like this so that I don't have to type the same string literals twice (reduce chances of a typo causing a bug), and for the process to know certain systemd-related values without having to hard code them in systemd scripts and the project source (write once, use everywhere).

Thank you.

Sent from my BlackBerry 10 smartphone on the Bell network.
  Original Message  
From: ***@giassa.net
Sent: Tuesday, September 26, 2017 4:15 PM
To: systemd-***@lists.freedesktop.org
Subject: Question: path-based deactivation or equivalent

I have a project where I'm adding some services to a Raspberry Pi 3,
and have decided to go with systemd being (mostly) responsible for
launching all of the services. All of the server processes use a common
API to do some initial setup (argument parsing, init, etc), drop root
permissions, and then carry out their normal duties. One of these
services is the "master" service, which I want to use to
activate/deactivate other services on-demand.

I started out with path-based activation via systemd, and that
worked fine for bringing up the services as I requested them. However,
it seems there is no equivalent path-based deactivation functionality at
this time [1], so it's definitely not an option on the older OS deployed
on my dev board.

I've looked into finding a suitable workaround, but the only viable
option so far is a hack that involves creating two separate systemd
scripts for a single service [2]. Is there some other functionality to
systemd that would permit me to:
-Activate and deactivate a service on demand (i.e. start it; stop it via
SIGNIT and after a timeout send it SIGKILL).
-Only have a single systemd script per service rather than the
two-scripts-per-service hack noted in [2].
-Allow a non-root (unprivileged) user to invoke my API/wrapper to
start/stop the services/daemons I maintain.
-Provide similar activation logic to path-based activation (i.e. only
start the service if both its dependencies on other services has been
met, AND the path/socket/etc exists).

Some of these services are provided to me in binary-only form, so I
can't necessarily modify all of them from source. Also, I'm not allowed
to use the LD_PRELOAD approach to injecting features. For all intents
and purposes, let's assume I can only modify my "manager" service, and
the systemd scripts for all services.

Thank you for your time and assistance.

Addendum:
------------------------------------------------------------------------
I'm doing this to automate a test suite that evaulates how the
various services handle failure cases when one or more random services
goes up and down. The test suite, like the services/daemons themselves,
is not permitted root access, and has to be able to run as an
unprivileged user (i.e. no "systemctl $SERVICENAME start|stop" is
permitted via the command line). If there's an equivalent to the
path-based activation/deactivation as noted in [2], it makes my life a
lot easier.

Eventually, some services may be able to sidestep the "master"
service, and kill off other services (i.e. dependencies), so having the
"master" service just kill() the other services is not viable long-term.

References:
------------------------------------------------------------------------
1. "RFE: systemd.path should support deactivation #3642",
<https://github.com/systemd/systemd/issues/3642>, Last accessed 2017-09-26.

2. "Can I use systemd to start and stop a service based on the presence
of a file?",
<https://unix.stackexchange.com/questions/286769/can-i-use-systemd-to-start-and-stop-a-service-based-on-the-presence-of-a-file>,
Last accessed 2017-09-26.
Matthew Giassa
2017-09-27 11:26:12 UTC
Reply
Permalink
Raw Message
Post by Matthew Giassa
I know there are workarounds for ExecStart for example, using /bin/bash to evaluate environment variables, but I think that special case only applies to ExecStart, ExecStartPre, and Executor, as they actually get executed rather than just assigned, allowing for some degree of variable expansion.
Correction: ExecStart, ExecStartPre, and ExecStop.
Post by Matthew Giassa
Thank you.
Sent from my BlackBerry 10 smartphone on the Bell network.
  Original Message  
Sent: Tuesday, September 26, 2017 4:15 PM
Subject: Question: path-based deactivation or equivalent
I have a project where I'm adding some services to a Raspberry Pi 3,
and have decided to go with systemd being (mostly) responsible for
launching all of the services. All of the server processes use a common
API to do some initial setup (argument parsing, init, etc), drop root
permissions, and then carry out their normal duties. One of these
services is the "master" service, which I want to use to
activate/deactivate other services on-demand.
I started out with path-based activation via systemd, and that
worked fine for bringing up the services as I requested them. However,
it seems there is no equivalent path-based deactivation functionality at
this time [1], so it's definitely not an option on the older OS deployed
on my dev board.
I've looked into finding a suitable workaround, but the only viable
option so far is a hack that involves creating two separate systemd
scripts for a single service [2]. Is there some other functionality to
-Activate and deactivate a service on demand (i.e. start it; stop it via
SIGNIT and after a timeout send it SIGKILL).
-Only have a single systemd script per service rather than the
two-scripts-per-service hack noted in [2].
-Allow a non-root (unprivileged) user to invoke my API/wrapper to
start/stop the services/daemons I maintain.
-Provide similar activation logic to path-based activation (i.e. only
start the service if both its dependencies on other services has been
met, AND the path/socket/etc exists).
Some of these services are provided to me in binary-only form, so I
can't necessarily modify all of them from source. Also, I'm not allowed
to use the LD_PRELOAD approach to injecting features. For all intents
and purposes, let's assume I can only modify my "manager" service, and
the systemd scripts for all services.
Thank you for your time and assistance.
------------------------------------------------------------------------
I'm doing this to automate a test suite that evaulates how the
various services handle failure cases when one or more random services
goes up and down. The test suite, like the services/daemons themselves,
is not permitted root access, and has to be able to run as an
unprivileged user (i.e. no "systemctl $SERVICENAME start|stop" is
permitted via the command line). If there's an equivalent to the
path-based activation/deactivation as noted in [2], it makes my life a
lot easier.
Eventually, some services may be able to sidestep the "master"
service, and kill off other services (i.e. dependencies), so having the
"master" service just kill() the other services is not viable long-term.
------------------------------------------------------------------------
1. "RFE: systemd.path should support deactivation #3642",
<https://github.com/systemd/systemd/issues/3642>, Last accessed 2017-09-26.
2. "Can I use systemd to start and stop a service based on the presence
of a file?",
<https://unix.stackexchange.com/questions/286769/can-i-use-systemd-to-start-and-stop-a-service-based-on-the-presence-of-a-file>,
Last accessed 2017-09-26.
--
============================================================
Matthew Giassa, MASc, BASc, EIT
Principal Developer; Security and Embedded Systems Specialist
linkedin: https://ca.linkedin.com/in/giassa
e-mail: ***@giassa.net
website: www.giassa.net


The information contained in this e-mail is intended only for the
individual or entity to whom it is addressed. Its contents (including
any attachments) are confidential and may contain privileged
information. If you are not the intended recipient you must not use,
disclose, disseminate, copy or print its contents.
Lennart Poettering
2017-09-27 17:00:58 UTC
Reply
Permalink
Raw Message
Post by Matthew Giassa
Environment=MYPIDFILE=/var/run/mine.pid
PIDFILE=$MYPIDFILE
systemd unit files are not supposed to be a templating engine. If you
want one use m4 or such.

Environment= configures the environment for the executed process,
that's all really.
Post by Matthew Giassa
I know there are workarounds for ExecStart for example, using
/bin/bash to evaluate environment variables, but I think that
special case only applies to ExecStart, ExecStartPre, and Executor,
as they actually get executed rather than just assigned, allowing
for some degree of variable expansion.
Yes, ExecXYZ= do some basic variable expansion, and I regret these
days that I added that, it makes unit files a bit too magic, they are
supposed to be simply key-value files...

Note that the set of environment variables passed to executed
processes is the combination of a number of sources: the stuff from
Environment=, the stuff from EnvironmentFiles=, the system-wide
Environment=, the stuff pulled in via PassEnvironment=, the stuff set
via PAM modules (if PAMName= is used) as well as what systemd sets on
its own (MAINPID=, LISTEN_FDS=, NOTIFY_SOCKET= and such). Many of
these fields are only known at the moment of activation (for example,
MAINPID= is set to the main PID of a service, but that's only known if
we actually started that already), and hence we can't expand env vars
in other statements even if we wanted: loading configuration files
happens much much earlier than activation after all. Only in ExecXYZ=
we can do the minimal expansion we do, as right when we fork off the
process we actually know the environment we'll pass too.

Lennart
--
Lennart Poettering, Red Hat
Lennart Poettering
2017-09-27 16:54:21 UTC
Reply
Permalink
Raw Message
Post by m***@giassa.net
I have a project where I'm adding some services to a Raspberry Pi 3,
and have decided to go with systemd being (mostly) responsible for
launching all of the services. All of the server processes use a common
API to do some initial setup (argument parsing, init, etc), drop root
permissions, and then carry out their normal duties. One of these
services is the "master" service, which I want to use to
activate/deactivate other services on-demand.
I started out with path-based activation via systemd, and that
worked fine for bringing up the services as I requested them. However,
it seems there is no equivalent path-based deactivation functionality at
this time [1], so it's definitely not an option on the older OS deployed
on my dev board.
Unit deactivation triggered by fs events has been requested before
(there's a github RFE issue about it somewhere), but I am not
convinced this is a good idea, and the semantics aren't clear about it
at all.

First of all, the states of .path units and the units they trigger are
actually connected: as long as the service a .path is supposed to
trigger is running the .path unit is in a "dormant" mode, and won't
care for any fs events, until the service is terminated again, at
which point the state is rechecked and things might trigger
again. This scheme is not compatible with .path based deactivation
really...

The other thing is: i fail to see the usecase. Things like PathExists=
and DirectoryNotEmpty= have a clear usecase where they aren't racy:
they trigger a service that processes files that are created somewhere
and delete the files when they are done with them. As long as there
are files there, the service will be restarted. And the service is
supposed to exit when it's done.

Now, if you want to use .path just a communication protocol: there are
way better ways to solve that. use systemctl, or use the D-Bus
API. File system objects are not a great replacement for an IPC.

Lennart
--
Lennart Poettering, Red Hat
Matthew Giassa
2017-09-27 17:01:21 UTC
Reply
Permalink
Raw Message
I've been considering the dbus API, as I arrived at a similar conclusion while researching this last night. The downside is that, since some services are provided to me in binary form only, I can't deploy it across all daemons. 

In short, I'd be happy with just being able to send SIGTERM and SIGKILL to systemd-managed daemons, along with spawning them on-demand, without having to do something along the lines of execve(systemctl...) (i.e. Directly via a C/C++ API), and not requiring root privileges to issue the call. ‎I don't see a way to do this directly via systemd without wrapping systemctl. 

Thank you for the feedback.

Sent from my BlackBerry 10 smartphone on the Bell network.
  Original Message  
From: Lennart Poettering
Sent: Wednesday, September 27, 2017 9:54 AM
To: ***@giassa.net
Cc: systemd-***@lists.freedesktop.org
Subject: Re: [systemd-devel] Question: path-based deactivation or equivalent
Post by m***@giassa.net
I have a project where I'm adding some services to a Raspberry Pi 3,
and have decided to go with systemd being (mostly) responsible for
launching all of the services. All of the server processes use a common
API to do some initial setup (argument parsing, init, etc), drop root
permissions, and then carry out their normal duties. One of these
services is the "master" service, which I want to use to
activate/deactivate other services on-demand.
I started out with path-based activation via systemd, and that
worked fine for bringing up the services as I requested them. However,
it seems there is no equivalent path-based deactivation functionality at
this time [1], so it's definitely not an option on the older OS deployed
on my dev board.
Unit deactivation triggered by fs events has been requested before
(there's a github RFE issue about it somewhere), but I am not
convinced this is a good idea, and the semantics aren't clear about it
at all.

First of all, the states of .path units and the units they trigger are
actually connected: as long as the service a .path is supposed to
trigger is running the .path unit is in a "dormant" mode, and won't
care for any fs events, until the service is terminated again, at
which point the state is rechecked and things might trigger
again. This scheme is not compatible with .path based deactivation
really...

The other thing is: i fail to see the usecase. Things like PathExists=
and DirectoryNotEmpty= have a clear usecase where they aren't racy:
they trigger a service that processes files that are created somewhere
and delete the files when they are done with them. As long as there
are files there, the service will be restarted. And the service is
supposed to exit when it's done.

Now, if you want to use .path just a communication protocol: there are
way better ways to solve that. use systemctl, or use the D-Bus
API. File system objects are not a great replacement for an IPC.

Lennart
--
Lennart Poettering, Red Hat
Loading...