[systemd-devel] Packaging systemd user-instance service files

Colin Guthrie gmane at colin.guthr.ie
Mon Jul 20 03:05:10 PDT 2015


Ahmed S. Darwish wrote on 18/07/15 01:44:
> Hi Mantas,
> 
> On Sat, Jul 18, 2015 at 12:17:51AM +0300, Mantas Mikulėnas wrote:
>> On Fri, Jul 17, 2015 at 9:38 PM, Ahmed S. Darwish <darwish.07 at gmail.com>
>> wrote:
>>
>>> Hi everyone,
>>>
>>> I'm currently transforming a network daemon to become a native
>>> ``Type=notify'' systemd service. This daemon uses native PulseAudio
>>> simple APIs for output.
>>>
>>> Due to the PulseAudio dependency, the service needs to run under the
>>> user session. Otherwise, all PulseAudio APIs [e.g. pa_simple_new()]
>>> returns a ``Connection Refused'' error. [1]
>>>
>>> Given the above, I've built the following service file:
>>>
>>>     [Unit]
>>>     Description=AirTunes Synchronous Audio Service
>>>     [Service]
>>>     Type=notify
>>>     ExecStart=/usr/local/bin/shairport-sync
>>>     [Install]
>>>     WantedBy=default.target
>>>
>>> And the following snippet in the package Makefile.am:
>>>
>>>     cp scripts/shairport-sync.service /usr/lib/systemd/user/
>>>     systemctl --user daemon-reload
>>>     systemctl --user enable shairport-sync.service
>>>     systemctl --user start shairport-sync.service
>>>
>>
>> So the real problem is not that it *doesn't work*, but rather that
>> it *shouldn't be done*. That makefile is mixing entirely separate
>> things – installation (packaging), global configuration, and
>> per-user configuration.
>>
> 
> This is a small github project, and you have to handle the
> standard `./confugre', `make' and `sudo make install' sequence
> out of the box. There's no fancy Debian or Fedora packages for
> it even yet.
> 
> The per-use configuration part was forced upon us due to our
> dependency on native PulseAudio APIs; APIs which do not work
> except when our daemon is run from the same session PulseAudio
> is running from.
> 
>> (Not to mention the dangerous assumptions that 1) there's exactly
>> one user logged in during installation, and 2) that they'll
>> actually want to run the program right now...)
>>
> 
> Yes, `systemctl --user' is wrong and specific only to the current
> user, but that's why I was asking for an alernative, more generic,
> solution that will fit all users in the first place :-)
> 
>>> As you can see, the service is properly installed under
>>> ``/usr/lib/systemd/user/'' to run under the systemd user instance.
>>>
>>> Now the problem is that the Makefile commands above run as root,
>>> and thus all the ``systemctl --user'' commands fail with:
>>>
>>>     Failed to get D-Bus connection: Connection refused
>>>
>>> So, the question is, can I start ``systemctl --user daemon-reload''
>>> and ``systemctl --user enable'' above in some form while the
>>> Makefile is run from root?
>>>
>>
>> No, but you don't need to. Just install the file to /usr/lib/systemd/user,
>> and that's it. It'll be available to all users.
>>
>> If you want to forcefully enable the service for all users, then also
>> symlink it into /usr/lib/systemd/user/default.target.wants/, which is
>> almost exactly what `systemctl enable` does (except system-wide).
>> That'll make it start on login for everyone.
>>
> 
> Excellent! `systemctl --user enable' always did the symbolic
> link under `$HOME/.config/systemd/user/'; I mistakenly thought
> that it must be done _only_ this way for each and every user.
> 
> Now I understand; it seems it was done that way just not to affect
> other users of the system. It couldn't be done any other way due
> to the system permissions of /usr/lib/systemd/user

Note, that this *forces* it on every user, you can also enable it for
every user as part of package installation by running "systemctl
--global enable <yourservice>"

The --global flag means "globally for all users" (it only applies to the
user instance, so the --user part is not needed).

This writes the enablement symlink into
/etc/systemd/user/default.target.wants/ (or whatever your service file
states as it's WantedBy= directive).

Ultimately this is a packaging issue, so I would suggest that your "make
install" does not write anything here, but your document things for
packagers that *iff* they want the package to be enabled by all users,
then they run "systemctl --global enable <yourservice>" on initial
package installation (or better run "systemctl --global preset
<yourservice>" to allow package preset system to take on board the
distro-wide defaults from *.preset files (see "man 5 systemd.preset")

This is ultimately what PulseAudio itself does/recommends when started
from the user session as you are trying to do, so I'd suggest you follow
suit.

>> But the general rule is, do not start user-session processes from system
>> tasks.
> 
> Having the service installed `per-user' was something that was
> unfortunately forced. I will still inspect what will happen when
> two logged-in users lead to two instances of the daemon running :-(

Ultimately, if your daemon connects to PA, then you could react
appropriately. e.g. if PA is suspended because the user no longer has
access to the h/w devices, this information will be made available via
PA APIs and your daemon can react accordingly.

e.g. if you have two users, and one user wants this service and one does
not, then you can react accordingly. e.g. one user may expose their
sharing services as "Bob's Laptop" which may be getting used by some
systems, but when Alice, logs in (via fast user switch) then it might
expose also "Alice's Laptop" and let a different set of devices connect
to it.

This is kinda how things are meant to work, but it does expose some
issues in PA that cross some interesting boundaries. Ultimately people
want to use things in different ways... here, you probably don't want to
think about "Bob's Laptop" vs. "Alice's Laptop"... I'm guessing you just
want to think about "the Laptop that Bob normally uses, but Alice
sometimes logs into" and you want to play sound to it be able to play
sound on it at all times. This is understandable, but until very
recently, the security problems and management rules of device access
make this a very hard problem to solve in PA. With the advent of kdbus,
some of these security problems can be solved more efficiently now so
perhaps one day we'll see a newer version of PA based on kdbus which
splits into a system component and a per-user component (various changes
are required for proper application sandboxing too) or some other
mechanism that would allow you to do this "device level" configuration.
I don't honestly know, but there will need to be some serious
development done there at some point in the not too distant future. Who
will be able to step up and do that work is sadly unknown at this time
(to the best of my knowledge anyway).

Until then, the best situation is that a given device:

1. will typically belong to one user (I'd say most of the time, there is
only one user account anyway)
2. said user would auto-login and thus when the machine is on, the user
is logged in an PA is started
 or
2b. said user has "lingering" enabled (see "man 1 loginctl" and the
"enable-linger" verb) and thus starts a per-user dbus/PA/yourservice
even when the user has not yet logged in (i.e. waiting on gdm login
screen for username+password).

This solves the problem for the vast majority of users who want to use
your stuff on their own devices. Alice may still sometimes login to
Bob's computer and your application should still gracefully handle that
(ultimately if PA says it cannot access the audio device, you should
propagate that information to any remote clients - this will be as true
with any newer structure as it is now, so may as well code defensively
for that now!), but for the common case, everything just works.


> Can we disable multiple instances of the daemon in a systemd-native
> way, even when the service is run per user? 

No. Ultimately your daemon should just be capable of handling it's
access to audio h/w disappearing. Whether this means it just disconnects
clients and stops listening on the network, or whether it can pause/cork
itself neatly (I guess it depends on the remote protocol) is up to you,
but ultimately it should be handled for various reasons, not just this
one (e.g. say the user goes on a VoIP call. They likely want to
cork/pause any streams playing under those circumstances too - while
this isn't exactly the same, it's another situation where PA will tell
you to stop playing).

> Yes, this question
> implies a possible problem in the design, but again, having the
> service per-user is the only choice we have so far.
> 
> Thanks a lot Mantas for your help!


I hope my reply has shed some light on the situation for you.

I'll try and clarify anything that doesn't make sense, if you ask for
clarifications.

Cheers

Col


-- 

Colin Guthrie
gmane(at)colin.guthr.ie
http://colin.guthr.ie/

Day Job:
  Tribalogic Limited http://www.tribalogic.net/
Open Source:
  Mageia Contributor http://www.mageia.org/
  PulseAudio Hacker http://www.pulseaudio.org/
  Trac Hacker http://trac.edgewall.org/


More information about the systemd-devel mailing list