* Copyright (C) 2019 Red Hat, Inc.
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "config.h"
#include <assert.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-daemon.h>
#include "socket-io.h"
#include "utils.h"
#define UNIT_MAX 256
static int
match_job_removed (sd_bus_message *message,
void *user_data,
sd_bus_error *error)
{
const char **job_path = user_data;
const char *result;
const char *path;
debug (FACTORY, "Received JobRemoved signal:");
if (sd_bus_message_read (message, "uoss", NULL, &path, NULL, &result) < 0)
return 0;
debug (FACTORY, " -> path: %s, result: %s", path, result);
if (!*job_path || strcmp (path, *job_path) != 0)
return 0;
debug (FACTORY, " -> sending result.");
send_all (SD_LISTEN_FDS_START, result, strlen (result), 5 * 1000000);
*job_path = NULL;
return 0;
}
int
main (void)
{
char instance[WSINSTANCE_MAX];
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = NULL;
char unit[UNIT_MAX + 1];
sd_bus_message *reply = NULL;
const char *job_path = NULL;
char **fdnames;
int r;
if (sd_listen_fds_with_names (false, &fdnames) != 1 || strcmp (fdnames[0], "connection") != 0)
errx (EXIT_FAILURE, "Must be spawned from a systemd service on a socket with Accept=yes %s", fdnames[0]);
if (!recv_alnum (SD_LISTEN_FDS_START, instance, sizeof instance, 10 * 1000000))
errx (EXIT_FAILURE, "Didn't receive fingerprint");
r = sd_bus_open_system (&bus);
if (r < 0)
errx (EXIT_FAILURE, "Failed to connect to system bus: %s", strerror (-r));
* in two ways:
*
* - we set it to the path of the job that we're waiting to exit so
* that the match function knows which signal is for us
*
* - once the job is removed, the match function clears the variable
* back to NULL. that's how we know when to stop waiting.
*
* In effect, the duration of job_path being set to non-NULL is more
* or less equal to the duration of the existence of a job object at
* that path.
*/
r = sd_bus_match_signal_async (bus, NULL,
"org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", "JobRemoved",
match_job_removed, NULL, &job_path);
if (r < 0)
errx (EXIT_FAILURE, "Failed to install match rule: %s", strerror (-r));
r = snprintf (unit, sizeof unit, "cockpit-wsinstance-https@%s.socket", instance);
assert (0 < r && r < sizeof unit);
debug (FACTORY, "Requesting start of unit %s", unit);
r = sd_bus_call_method (bus,
"org.freedesktop.systemd1", "/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager", "StartUnit",
&error, &reply, "ss", unit, "replace");
if (r < 0)
errx (EXIT_FAILURE, "Method call failed: %s", error.message);
r = sd_bus_message_read (reply, "o", &job_path);
if (r < 0)
errx (EXIT_FAILURE, "Invalid message response: %s", strerror (-r));
debug (FACTORY, " -> job is %s", job_path);
debug (FACTORY, "Waiting for signal.");
do
r = sd_bus_process (bus, NULL);
while (r > 0);
if (r < 0)
errx (EXIT_FAILURE, "sd_bus_process() failed: %s", strerror (-r));
struct timespec start = { 0, 0 };
uint64_t remaining;
while (job_path && get_remaining_timeout (&start, &remaining, 20 * 1000000))
{
debug (FACTORY, "sd_bus_wait(%llu)", (long long) remaining);
r = sd_bus_wait (bus, remaining);
if (r < 0)
errx (EXIT_FAILURE, "Error while waiting for bus: %s", strerror (-r));
debug (FACTORY, "sd_bus_process():");
do
r = sd_bus_process (bus, NULL);
while (r > 0);
if (r < 0)
errx (EXIT_FAILURE, "sd_bus_process() failed: %s", strerror (-r));
debug (FACTORY, " -> done.");
}
sd_bus_message_unref (reply);
sd_bus_close (bus);
sd_bus_unref (bus);
return 0;
}