Skip to content

Commit

Permalink
capture: Wait for StartTransientUnit() job finish (bsc#1217897)
Browse files Browse the repository at this point in the history
The parent may not be migrated when StartTransientUnit() DBus call
returns. Wait until proper notification via JobRemoved() DBus signal
arrives to be sure parent was migrated as expected.

Use sd_bus_add_match() instead of sd_bus_match_signal_async() since the
latter is only available from libsystemd v236.
  • Loading branch information
Werkov committed Feb 2, 2024
1 parent 4a5acb2 commit 0ae27f8
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ sapwmp_capture_CFLAGS = \
$(LIBSYSTEMD_CGLAGS)
sapwmp_capture_LDADD = \
$(LIBSYSTEMD_LIBS)
sapwmp_capture_SOURCES = src/main.c src/config.c src/log.c
sapwmp_capture_SOURCES = src/main.c src/config.c src/log.c src/dbus-job.c
117 changes: 117 additions & 0 deletions src/dbus-job.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#include "dbus-job.h"
#include "log.h"

/*
* The code waiting for org.freedesktop.systemd1.Manager.JobRemoved signal is
* based on systemd's src/shared/bus-wait-for-jobs.c
*/

struct waited_job {
const char *job;
char *result;
};

static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
assert(m);

log_info("D-Bus connection terminated while waiting for jobs.");
sd_bus_close(sd_bus_message_get_bus(m));

return 0;
}

static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
struct waited_job *wj = userdata;
const char *path, *result;
int r;

assert(m);

r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, /* unit = */ NULL, &result);
if (r < 0) {
log_error("DBus signal parsing error: %s", strerror(r));
return 0;
}

if (strcmp(path, wj->job))
return 0;

wj->result = strdup(result);
/* best effort upon ENOMEM */
if (!wj->result)
wj->result = "";

return 0;
}

int bus_setup_wait(sd_bus *bus, struct waited_job *wj) {
int r;

/* When we are a bus client we match by sender. Direct connections OTOH have no initialized sender
* field, and hence we ignore the sender then */
r = sd_bus_add_match(
bus,
NULL, /* slot removed eventually with sd_bus */
"type='signal',"
"sender='org.freedesktop.systemd1',"
"interface='org.freedesktop.systemd1.Manager',"
"member='JobRemoved',"
"path='/org/freedesktop/systemd1'",
match_job_removed, wj);
if (r < 0)
return r;

r = sd_bus_add_match(
bus,
NULL, /* slot removed eventually with sd_bus */
"type='signal',"
"sender='org.freedesktop.DBus.Local',"
"interface='org.freedesktop.DBus.Local',"
"member='Disconnected'",
match_disconnected, wj);
if (r < 0)
return r;

return 0;
}

static int bus_process_wait(sd_bus *bus, struct waited_job *wj) {
int r;

for (;;) {
r = sd_bus_process(bus, NULL);
if (r < 0)
return r;
if (r > 0 && wj->result) {
if (wj->result[0] == '\0')
return -ENOMEM;
return 0;
}

r = sd_bus_wait(bus, UINT64_MAX);
if (r < 0)
return r;
}
}

int wait_for_job(sd_bus *bus, const char *job) {
struct waited_job wj = { .job = job };
int r;

r = bus_setup_wait(bus, &wj);
if (r < 0)
return r;

r = bus_process_wait(bus, &wj);
if (r < 0)
return r;
log_debug("Job %s finished, result=%s", job, wj.result);
free(wj.result);
return r;
}

4 changes: 4 additions & 0 deletions src/dbus-job.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#include <systemd/sd-bus.h>

int wait_for_job(sd_bus *bus, const char *job);

17 changes: 14 additions & 3 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "config.h"
#include "log.h"
#include "dbus-job.h"

#define MAX_PIDS 16
#define CONF_FILE "/etc/sapwmp.conf"
Expand Down Expand Up @@ -76,8 +77,9 @@ int already_in_slice(pid_t pid, const char *target_slice) {
int migrate(sd_bus *bus, const char *target_unit, const char *target_slice,
struct unit_config *properties,
size_t n_pids, pid_t *pids) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error bus_error = SD_BUS_ERROR_NULL;
const char *job;
int r;

r = sd_bus_message_new_method_call(
Expand Down Expand Up @@ -166,11 +168,20 @@ int migrate(sd_bus *bus, const char *target_unit, const char *target_slice,
if (r < 0)
return r;

r = sd_bus_call(bus, m, 0, &bus_error, NULL);
r = sd_bus_call(bus, m, 0, &bus_error, &reply);
if (r < 0) {
log_info("DBus call error: %s", strerror(sd_bus_error_get_errno(&bus_error)));
return r;
}
r = sd_bus_message_read(reply, "o", &job);
if (r < 0) {
log_info("DBus read error: %s", strerror(sd_bus_error_get_errno(&bus_error)));
return r;
}
r = wait_for_job(bus, job);
if (r < 0) {
log_info("Start job failed: %s", strerror(sd_bus_error_get_errno(&bus_error)));
}
/* ignore reply, i.e. don't wait for the job to finish */
return r;
}

Expand Down

0 comments on commit 0ae27f8

Please sign in to comment.