Compare commits

...

18 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek a50414fce5
Merge pull request #14578 from keszybz/docs-index
Let's see if redirects work
2020-01-15 13:43:24 +01:00
Zbigniew Jędrzejewski-Szmek 8c5cd27dd1 docs: rename HACKING → Hacking
Let's see if this works at all.
2020-01-15 12:38:12 +01:00
Zbigniew Jędrzejewski-Szmek b6bcde2623 docs: shift console log on index page to the left 2020-01-15 11:58:08 +01:00
Lennart Poettering eea45a3399
Merge pull request #14424 from poettering/watch-bus-name-rework
pid1: simplify drastically how we watch bus names for service's BusName= setting
2020-01-15 11:46:11 +01:00
Lennart Poettering f6160131e7
Merge pull request #14573 from keszybz/docs-index
Docs index
2020-01-15 10:25:06 +01:00
Zbigniew Jędrzejewski-Szmek 6af0a04428 docs: add the systemd output example
It is still nice...
2020-01-15 08:57:37 +01:00
Zbigniew Jędrzejewski-Szmek 4e96d758f8 docs: update old para with links to the blog stories
They are of historical interest, but without links not very useful.
2020-01-15 08:57:37 +01:00
Zbigniew Jędrzejewski-Szmek 48f60ea9ad docs: remove markup from title
Github uses a different background for backticked text, and this stands out
(in a bad way) on the title page.
2020-01-15 08:57:37 +01:00
Zbigniew Jędrzejewski-Szmek d00386fc0b man: add commas and reword a sentence
On more careful reading, "exit status ... do not cause the unit to enter a
failure state" is not gramatically or logically correct.
2020-01-15 08:57:37 +01:00
Lennart Poettering fc67a943d9 core: drop initial ListNames() bus call from PID 1
Previously, when first connecting to the bus after connecting to it we'd
issue a ListNames() bus call to the driver to figure out which bus names
are currently active. This information was then used to initialize the
initial state for services that use BusName=.

This change removes the whole code for this and replaces it with
something vastly simpler.

First of all, the ListNames() call was issues synchronosuly, which meant
if dbus was for some reason synchronously calling into PID1 for some
reason we'd deadlock. As it turns out there's now a good chance it does:
the nss-systemd userdb hookup means that any user dbus-daemon resolves
might result in a varlink call into PID 1, and dbus resolves quite a lot
of users while parsing its policy. My original goal was to fix this
deadlock.

But as it turns out we don't need the ListNames() call at all anymore,
since #12957 has been merged. That PR was supposed to fix a race where
asynchronous installation of bus matches would cause us missing the
initial owner of a bus name when a service is first started. It fixed it
(correctly) by enquiring with GetOwnerName() who currently owns the
name, right after installing the match. But this means whenever we start watching a bus name we anyway
issue a GetOwnerName() for it, and that means also when first connecting
to the bus we don't need to issue ListNames() anymore since that just
tells us the same info: which names are currently owned.

hence, let's drop ListNames() and instead make better use of the
GetOwnerName() result: if it failed the name is not owned.

Also, while we are at it, let's simplify the unit's owner_name_changed()
callback(): let's drop the "old_owner" argument. We never used that
besides logging, and it's hard to synthesize from just the return of a
GetOwnerName(), hence don't bother.
2020-01-06 15:21:47 +01:00
Lennart Poettering a5b0784795 core: create/remove unit bus name slots always together
When a service unit watches a bus name (i.e. because of BusName= being
set), then we do two things: we install a match slot to watch how its
ownership changes, and we inquire about the current owner. Make sure we
always do both together or neither.

This in particular fixes a corner-case memleak when destroying bus
connections, since we never freed the GetNameOwner() bus slots when
destroying a bus when they were still ongoing.
2020-01-06 15:21:44 +01:00
Lennart Poettering 5085ef0d71 core: no need to eat up error
This is a method call reply. We might as well propagate the error. The
worst that happens is that sd-bus logs about it.
2020-01-06 15:21:40 +01:00
Lennart Poettering 17bda1f19d core: shorten code a bit
The return parameter here cannot be NULL, the bus call either succeeds
or fails but will never uceed and return an empty owner.
2020-01-06 15:21:37 +01:00
Lennart Poettering a54654ba70 core: don't check potentially NULL error, it's not gonna work anyway 2020-01-06 15:21:33 +01:00
Lennart Poettering 42837b8134 core: don't check error parameter of get_name_owner_handler()
It's a *return* parameter, not an input parameter. Yes, this is a bit
confusing for method call replies, but we try to use the same message
handler for all incoming messages, hence the parameter. We are supposed
to write any error into it we encounter, if we want, and our caller will
log it, but that's it.
2020-01-06 15:21:30 +01:00
Lennart Poettering 3425c45e1e testsuite: drop "systemctl is-system-running --wait" invocation
We wait for "basic.target" being reached in the user instance anyway
before allowing the user's session to start, hence doing such a wait is
unnecessary, since that would just mean we'd wait for "default.target"
on top of "basic.target", but we shouldn#t need anything of that...

Hence, let's simplify this, reduce explicit sync points.
2020-01-06 15:21:27 +01:00
Lennart Poettering 13811aa5f6 test: don't rely on "nobody" user for TEST-43
The name is not as universal as we want, still, hence let's use our own
user we create with sysusers.d/. That should yield same behaviour
everywhere (and also test sysusers a bit as side effect).
2020-01-06 15:21:23 +01:00
Lennart Poettering 519b2e5212 test: hardcode shell to use
let's make sure we always invoke our commands through /bin/sh, since
on some distros su will use /bin/nologin (or whatever is listed in
/etc/passwd) as shell otherwise and we don#t want that.
2020-01-06 15:21:19 +01:00
12 changed files with 127 additions and 187 deletions

View File

@ -2,6 +2,8 @@
title: Hacking on systemd
category: Contributing
layout: default
redirect_from:
- HACKING/
---
# Hacking on systemd

View File

@ -1,10 +1,10 @@
---
title: Users, Groups, UIDs and GIDs on `systemd` Systems
title: Users, Groups, UIDs and GIDs on systemd Systems
category: Concepts
layout: default
---
# Users, Groups, UIDs and GIDs on `systemd` Systems
# Users, Groups, UIDs and GIDs on systemd Systems
Here's a summary of the requirements `systemd` (and Linux) make on UID/GID
assignments and their ranges.

View File

@ -8,8 +8,6 @@ systemd provides aggressive parallelization capabilities, uses socket and D-Bus
Other parts include a logging daemon, utilities to control basic system configuration like the hostname, date, locale, maintain a list of logged-in users and running containers and virtual machines, system accounts, runtime directories and settings, and daemons to manage simple network configuration, network time synchronization, log forwarding, and name resolution.
See the introductory blog story and three status updates for a longer introduction. Also see the [Wikipedia article](https://en.wikipedia.org/wiki/systemd).
---
{% assign by_category = site.pages | group_by:"category" %}
@ -24,3 +22,74 @@ See the introductory blog story and three status updates for a longer introducti
{% endif %}
{% endfor %}
## See also
* [Introductory blog story](http://0pointer.de/blog/projects/systemd.html)
* [Three](http://0pointer.de/blog/projects/systemd-update.html) [status](http://0pointer.de/blog/projects/systemd-update-2.html) [updates](http://0pointer.de/blog/projects/systemd-update-3.html)
* The [Wikipedia article](https://en.wikipedia.org/wiki/systemd)
---
<pre style="color:white; background-color:black; font-size:smaller; width:45%; padding:6pt 8pt">
Welcome to <span style="color:blue">Fedora 20 (Heisenbug)</span>!
[ <span style="color:green">OK</span> ] Reached target Remote File Systems.
[ <span style="color:green">OK</span> ] Listening on Delayed Shutdown Socket.
[ <span style="color:green">OK</span> ] Listening on /dev/initctl Compatibility Named Pipe.
[ <span style="color:green">OK</span> ] Reached target Paths.
[ <span style="color:green">OK</span> ] Reached target Encrypted Volumes.
[ <span style="color:green">OK</span> ] Listening on Journal Socket.
Mounting Huge Pages File System...
Mounting POSIX Message Queue File System...
Mounting Debug File System...
Starting Journal Service...
[ <span style="color:green">OK</span> ] Started Journal Service.
Mounting Configuration File System...
Mounting FUSE Control File System...
[ <span style="color:green">OK</span> ] Created slice Root Slice.
[ <span style="color:green">OK</span> ] Created slice User and Session Slice.
[ <span style="color:green">OK</span> ] Created slice System Slice.
[ <span style="color:green">OK</span> ] Reached target Slices.
[ <span style="color:green">OK</span> ] Reached target Swap.
Mounting Temporary Directory...
[ <span style="color:green">OK</span> ] Reached target Local File Systems (Pre).
Starting Load Random Seed...
Starting Load/Save Random Seed...
[ <span style="color:green">OK</span> ] Mounted Huge Pages File System.
[ <span style="color:green">OK</span> ] Mounted POSIX Message Queue File System.
[ <span style="color:green">OK</span> ] Mounted Debug File System.
[ <span style="color:green">OK</span> ] Mounted Configuration File System.
[ <span style="color:green">OK</span> ] Mounted FUSE Control File System.
[ <span style="color:green">OK</span> ] Mounted Temporary Directory.
[ <span style="color:green">OK</span> ] Started Load Random Seed.
[ <span style="color:green">OK</span> ] Started Load/Save Random Seed.
[ <span style="color:green">OK</span> ] Reached target Local File Systems.
Starting Recreate Volatile Files and Directories...
Starting Trigger Flushing of Journal to Persistent Storage...
[ <span style="color:green">OK</span> ] Started Recreate Volatile Files and Directories.
Starting Update UTMP about System Reboot/Shutdown...
[ <span style="color:green">OK</span> ] Started Trigger Flushing of Journal to Persistent Storage.
[ <span style="color:green">OK</span> ] Started Update UTMP about System Reboot/Shutdown.
[ <span style="color:green">OK</span> ] Reached target System Initialization.
[ <span style="color:green">OK</span> ] Reached target Timers.
[ <span style="color:green">OK</span> ] Listening on D-Bus System Message Bus Socket.
[ <span style="color:green">OK</span> ] Reached target Sockets.
[ <span style="color:green">OK</span> ] Reached target Basic System.
Starting Permit User Sessions...
Starting D-Bus System Message Bus...
[ <span style="color:green">OK</span> ] Started D-Bus System Message Bus.
Starting Login Service...
Starting Cleanup of Temporary Directories...
[ <span style="color:green">OK</span> ] Started Permit User Sessions.
[ <span style="color:green">OK</span> ] Started Cleanup of Temporary Directories.
Starting Console Getty...
[ <span style="color:green">OK</span> ] Started Console Getty.
[ <span style="color:green">OK</span> ] Reached target Login Prompts.
[ <span style="color:green">OK</span> ] Started Login Service.
[ <span style="color:green">OK</span> ] Reached target Multi-User System.
Fedora release 20 (Heisenbug)
Kernel 3.9.2-200.fc18.x86_64 on an x86_64 (console)
fedora login:
</pre>

View File

@ -46,14 +46,14 @@
Control Group Interfaces</ulink> for an introduction on how to make
use of scope units from programs.</para>
<para>Note that unlike service units scope units have no "main" process, all processes in the scope are
equivalent. The lifecycle of the scope unit is thus not bound to the lifetime of one specific process but
to the existance of any processes in the scope. This also means that the exit status of these processes
do not cause the scope unit to enter a failure state. Scope units may still enter a failure state, for
example due to resource exhaustion or stop timeouts being reached, but not due to programs inside of them
terminating uncleanly. Since processes managed as scope units generally remain children of the original
process that forked them off it's also the job of that process to collect their exit statuses and act on
them as needed.</para>
<para>Note that, unlike service units, scope units have no "main" process: all processes in the scope are
equivalent. The lifecycle of the scope unit is thus not bound to the lifetime of one specific process,
but to the existence of at least one process in the scope. This also means that the exit statuses of
these processes are not relevant for the scope unit failure state. Scope units may still enter a failure
state, for example due to resource exhaustion or stop timeouts being reached, but not due to programs
inside of them terminating uncleanly. Since processes managed as scope units generally remain children of
the original process that forked them off, it is also the job of that process to collect their exit
statuses and act on them as needed.</para>
</refsect1>
<refsect1>

View File

@ -2993,7 +2993,7 @@ install_data('LICENSE.GPL2',
'docs/CODING_STYLE.md',
'docs/DISTRO_PORTING.md',
'docs/ENVIRONMENT.md',
'docs/HACKING.md',
'docs/Hacking.md',
'docs/TRANSIENT-SETTINGS.md',
'docs/TRANSLATORS.md',
'docs/UIDS-GIDS.md',

View File

@ -719,114 +719,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void
return 0;
}
static int manager_dispatch_sync_bus_names(sd_event_source *es, void *userdata) {
_cleanup_strv_free_ char **names = NULL;
Manager *m = userdata;
const char *name;
Iterator i;
Unit *u;
int r;
assert(es);
assert(m);
assert(m->sync_bus_names_event_source == es);
/* First things first, destroy the defer event so that we aren't triggered again */
m->sync_bus_names_event_source = sd_event_source_unref(m->sync_bus_names_event_source);
/* Let's see if there's anything to do still? */
if (!m->api_bus)
return 0;
if (hashmap_isempty(m->watch_bus))
return 0;
/* OK, let's sync up the names. Let's see which names are currently on the bus. */
r = sd_bus_list_names(m->api_bus, &names, NULL);
if (r < 0)
return log_error_errno(r, "Failed to get initial list of names: %m");
/* We have to synchronize the current bus names with the
* list of active services. To do this, walk the list of
* all units with bus names. */
HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) {
Service *s = SERVICE(u);
assert(s);
if (!streq_ptr(s->bus_name, name)) {
log_unit_warning(u, "Bus name has changed from %s → %s, ignoring.", s->bus_name, name);
continue;
}
/* Check if a service's bus name is in the list of currently
* active names */
if (strv_contains(names, name)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
const char *unique;
/* If it is, determine its current owner */
r = sd_bus_get_name_creds(m->api_bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds);
if (r < 0) {
log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get bus name owner %s: %m", name);
continue;
}
r = sd_bus_creds_get_unique_name(creds, &unique);
if (r < 0) {
log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get unique name for %s: %m", name);
continue;
}
/* Now, let's compare that to the previous bus owner, and
* if it's still the same, all is fine, so just don't
* bother the service. Otherwise, the name has apparently
* changed, so synthesize a name owner changed signal. */
if (!streq_ptr(unique, s->bus_name_owner))
UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name_owner, unique);
} else {
/* So, the name we're watching is not on the bus.
* This either means it simply hasn't appeared yet,
* or it was lost during the daemon reload.
* Check if the service has a stored name owner,
* and synthesize a name loss signal in this case. */
if (s->bus_name_owner)
UNIT_VTABLE(u)->bus_name_owner_change(u, s->bus_name_owner, NULL);
}
}
return 0;
}
int manager_enqueue_sync_bus_names(Manager *m) {
int r;
assert(m);
/* Enqueues a request to synchronize the bus names in a later event loop iteration. The callers generally don't
* want us to invoke ->bus_name_owner_change() unit calls from their stack frames as this might result in event
* dispatching on its own creating loops, hence we simply create a defer event for the event loop and exit. */
if (m->sync_bus_names_event_source)
return 0;
r = sd_event_add_defer(m->event, &m->sync_bus_names_event_source, manager_dispatch_sync_bus_names, m);
if (r < 0)
return log_error_errno(r, "Failed to create bus name synchronization event: %m");
r = sd_event_source_set_priority(m->sync_bus_names_event_source, SD_EVENT_PRIORITY_IDLE);
if (r < 0)
return log_error_errno(r, "Failed to set event priority: %m");
r = sd_event_source_set_enabled(m->sync_bus_names_event_source, SD_EVENT_ONESHOT);
if (r < 0)
return log_error_errno(r, "Failed to set even to oneshot: %m");
(void) sd_event_source_set_description(m->sync_bus_names_event_source, "manager-sync-bus-names");
return 0;
}
static int bus_setup_api(Manager *m, sd_bus *bus) {
Iterator i;
char *name;
@ -910,10 +802,6 @@ int bus_init_api(Manager *m) {
m->api_bus = TAKE_PTR(bus);
r = manager_enqueue_sync_bus_names(m);
if (r < 0)
return r;
return 0;
}
@ -1051,13 +939,10 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
/* Make sure all bus slots watching names are released. */
HASHMAP_FOREACH(u, m->watch_bus, i) {
if (!u->match_bus_slot)
continue;
if (sd_bus_slot_get_bus(u->match_bus_slot) != *bus)
continue;
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
if (u->match_bus_slot && sd_bus_slot_get_bus(u->match_bus_slot) == *bus)
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
if (u->get_name_owner_slot && sd_bus_slot_get_bus(u->get_name_owner_slot) == *bus)
u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
}
/* Get rid of tracked clients on this bus */

View File

@ -21,8 +21,6 @@ int bus_fdset_add_all(Manager *m, FDSet *fds);
void bus_track_serialize(sd_bus_track *t, FILE *f, const char *prefix);
int bus_track_coldplug(Manager *m, sd_bus_track **t, bool recursive, char **l);
int manager_enqueue_sync_bus_names(Manager *m);
int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata);
int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error);

View File

@ -1373,7 +1373,6 @@ Manager* manager_free(Manager *m) {
sd_event_source_unref(m->jobs_in_progress_event_source);
sd_event_source_unref(m->run_queue_event_source);
sd_event_source_unref(m->user_lookup_event_source);
sd_event_source_unref(m->sync_bus_names_event_source);
safe_close(m->signal_fd);
safe_close(m->notify_fd);
@ -1610,9 +1609,6 @@ static void manager_ready(Manager *m) {
manager_recheck_journal(m);
manager_recheck_dbus(m);
/* Sync current state of bus names with our set of listening units */
(void) manager_enqueue_sync_bus_names(m);
/* Let's finally catch up with any changes that took place while we were reloading/reexecing */
manager_catchup(m);

View File

@ -219,8 +219,6 @@ struct Manager {
int user_lookup_fds[2];
sd_event_source *user_lookup_event_source;
sd_event_source *sync_bus_names_event_source;
UnitFileScope unit_file_scope;
LookupPaths lookup_paths;
Hashmap *unit_id_map;

View File

@ -4062,24 +4062,17 @@ static int service_get_timeout(Unit *u, usec_t *timeout) {
return 1;
}
static void service_bus_name_owner_change(
Unit *u,
const char *old_owner,
const char *new_owner) {
static void service_bus_name_owner_change(Unit *u, const char *new_owner) {
Service *s = SERVICE(u);
int r;
assert(s);
assert(old_owner || new_owner);
if (old_owner && new_owner)
log_unit_debug(u, "D-Bus name %s changed owner from %s to %s", s->bus_name, old_owner, new_owner);
else if (old_owner)
log_unit_debug(u, "D-Bus name %s no longer registered by %s", s->bus_name, old_owner);
if (new_owner)
log_unit_debug(u, "D-Bus name %s now owned by %s", s->bus_name, new_owner);
else
log_unit_debug(u, "D-Bus name %s now registered by %s", s->bus_name, new_owner);
log_unit_debug(u, "D-Bus name %s now not owned by anyone.", s->bus_name);
s->bus_name_good = !!new_owner;

View File

@ -3201,24 +3201,21 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
}
static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *name, *old_owner, *new_owner;
const char *new_owner;
Unit *u = userdata;
int r;
assert(message);
assert(u);
r = sd_bus_message_read(message, "sss", &name, &old_owner, &new_owner);
r = sd_bus_message_read(message, "sss", NULL, NULL, &new_owner);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
old_owner = empty_to_null(old_owner);
new_owner = empty_to_null(new_owner);
if (UNIT_VTABLE(u)->bus_name_owner_change)
UNIT_VTABLE(u)->bus_name_owner_change(u, old_owner, new_owner);
UNIT_VTABLE(u)->bus_name_owner_change(u, empty_to_null(new_owner));
return 0;
}
@ -3234,42 +3231,35 @@ static int get_name_owner_handler(sd_bus_message *message, void *userdata, sd_bu
u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
if (sd_bus_error_is_set(error)) {
log_error("Failed to get name owner from bus: %s", error->message);
return 0;
}
e = sd_bus_message_get_error(message);
if (sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
return 0;
if (e) {
log_error("Unexpected error response from GetNameOwner: %s", e->message);
return 0;
}
if (!sd_bus_error_has_name(e, "org.freedesktop.DBus.Error.NameHasNoOwner"))
log_unit_error(u, "Unexpected error response from GetNameOwner(): %s", e->message);
r = sd_bus_message_read(message, "s", &new_owner);
if (r < 0) {
bus_log_parse_error(r);
return 0;
}
new_owner = NULL;
} else {
r = sd_bus_message_read(message, "s", &new_owner);
if (r < 0)
return bus_log_parse_error(r);
new_owner = empty_to_null(new_owner);
assert(!isempty(new_owner));
}
if (UNIT_VTABLE(u)->bus_name_owner_change)
UNIT_VTABLE(u)->bus_name_owner_change(u, NULL, new_owner);
UNIT_VTABLE(u)->bus_name_owner_change(u, new_owner);
return 0;
}
int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
const char *match;
int r;
assert(u);
assert(bus);
assert(name);
if (u->match_bus_slot)
if (u->match_bus_slot || u->get_name_owner_slot)
return -EBUSY;
match = strjoina("type='signal',"
@ -3279,19 +3269,27 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) {
"member='NameOwnerChanged',"
"arg0='", name, "'");
int r = sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
r = sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u);
if (r < 0)
return r;
return sd_bus_call_method_async(bus,
&u->get_name_owner_slot,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetNameOwner",
get_name_owner_handler,
u,
"s", name);
r = sd_bus_call_method_async(
bus,
&u->get_name_owner_slot,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetNameOwner",
get_name_owner_handler,
u,
"s", name);
if (r < 0) {
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
return r;
}
log_unit_debug(u, "Watching D-Bus name '%s'.", name);
return 0;
}
int unit_watch_bus_name(Unit *u, const char *name) {
@ -3314,6 +3312,7 @@ int unit_watch_bus_name(Unit *u, const char *name) {
r = hashmap_put(u->manager->watch_bus, name, u);
if (r < 0) {
u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot);
u->get_name_owner_slot = sd_bus_slot_unref(u->get_name_owner_slot);
return log_warning_errno(r, "Failed to put bus name to hashmap: %m");
}

View File

@ -530,7 +530,7 @@ typedef struct UnitVTable {
void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds);
/* Called whenever a name this Unit registered for comes or goes away. */
void (*bus_name_owner_change)(Unit *u, const char *old_owner, const char *new_owner);
void (*bus_name_owner_change)(Unit *u, const char *new_owner);
/* Called for each property that is being set */
int (*bus_set_property)(Unit *u, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);