111cd254bSAndrew Jeffery // SPDX-License-Identifier: Apache-2.0
211cd254bSAndrew Jeffery // Copyright (C) 2021 IBM Corp.
311cd254bSAndrew Jeffery
4d65368beSAndrew Jeffery /*
5e998ba77SAndrew Jeffery * debug-trigger listens for an external signal that the BMC is in some way unresponsive. When a
6d65368beSAndrew Jeffery * signal is received it triggers a crash to collect debug data and reboots the system in the hope
7d65368beSAndrew Jeffery * that it will recover.
8d65368beSAndrew Jeffery *
9d65368beSAndrew Jeffery * Usage: debug-trigger [SOURCE] [SINK]
10d65368beSAndrew Jeffery *
11e998ba77SAndrew Jeffery * Options:
12e998ba77SAndrew Jeffery * --sink-actions=ACTION
1386094694SAndrew Jeffery * Set the class of sink action(s) to be used. Can take the value of 'sysrq' or 'dbus'.
1486094694SAndrew Jeffery * Defaults to 'sysrq'.
15e998ba77SAndrew Jeffery *
16d65368beSAndrew Jeffery * Examples:
17d65368beSAndrew Jeffery * debug-trigger
18e998ba77SAndrew Jeffery * Set the source as stdin, the sink as stdout, and use the default 'sysrq' set of sink
19e998ba77SAndrew Jeffery * actions. Useful for testing.
20e998ba77SAndrew Jeffery *
21e998ba77SAndrew Jeffery * debug-trigger --sink-actions=sysrq
22e998ba77SAndrew Jeffery * Explicitly use the 'sysrq' set of sink actions with stdin as the source and stdout as the
23e998ba77SAndrew Jeffery * sink.
24d65368beSAndrew Jeffery *
25d65368beSAndrew Jeffery * debug-trigger /dev/serio_raw0 /proc/sysrq-trigger
26e998ba77SAndrew Jeffery * Open /dev/serio_raw0 as the source and /proc/sysrq-trigger as the sink, with the default
27e998ba77SAndrew Jeffery * 'sysrq' set of sink actions. When 'D' is read from /dev/serio_raw0 'c' will be written to
28e998ba77SAndrew Jeffery * /proc/sysrq-trigger, causing a kernel panic. When 'R' is read from /dev/serio_raw0 'b' will
29e998ba77SAndrew Jeffery * be written to /proc/sysrq-trigger, causing an immediate reboot of the system.
3086094694SAndrew Jeffery *
3186094694SAndrew Jeffery * dbug-trigger --sink-actions=dbus /dev/serio_raw0
3286094694SAndrew Jeffery * Open /dev/serio_raw0 as the source and configure the 'dbus' set of sink actions. When 'D' is
3386094694SAndrew Jeffery * read from /dev/serio_raw0 create a dump via phosphor-debug-collector by calling through its
3486094694SAndrew Jeffery * D-Bus interface, then reboot the system by starting systemd's 'reboot.target'
35d65368beSAndrew Jeffery */
3686094694SAndrew Jeffery #define _GNU_SOURCE
3786094694SAndrew Jeffery
3886094694SAndrew Jeffery #include "config.h"
39d65368beSAndrew Jeffery
4011cd254bSAndrew Jeffery #include <err.h>
41b1ea254eSAndrew Jeffery #include <errno.h>
4211cd254bSAndrew Jeffery #include <fcntl.h>
4311cd254bSAndrew Jeffery #include <getopt.h>
4411cd254bSAndrew Jeffery #include <libgen.h>
4511cd254bSAndrew Jeffery #include <limits.h>
4611cd254bSAndrew Jeffery #include <linux/reboot.h>
4786094694SAndrew Jeffery #include <poll.h>
4886094694SAndrew Jeffery #include <stdbool.h>
4986094694SAndrew Jeffery #include <stdio.h>
5011cd254bSAndrew Jeffery #include <stdlib.h>
5111cd254bSAndrew Jeffery #include <string.h>
5211cd254bSAndrew Jeffery #include <sys/reboot.h>
5311cd254bSAndrew Jeffery #include <sys/stat.h>
5411cd254bSAndrew Jeffery #include <sys/types.h>
5511cd254bSAndrew Jeffery #include <unistd.h>
5611cd254bSAndrew Jeffery
5786094694SAndrew Jeffery #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
5886094694SAndrew Jeffery
5986094694SAndrew Jeffery struct sd_bus;
6086094694SAndrew Jeffery
61b1ea254eSAndrew Jeffery struct debug_source_ops {
62b1ea254eSAndrew Jeffery int (*poll)(void *ctx, char *op);
63b1ea254eSAndrew Jeffery };
64b1ea254eSAndrew Jeffery
65b1ea254eSAndrew Jeffery struct debug_source {
66b1ea254eSAndrew Jeffery const struct debug_source_ops *ops;
67b1ea254eSAndrew Jeffery void *ctx;
68b1ea254eSAndrew Jeffery };
69b1ea254eSAndrew Jeffery
70b1ea254eSAndrew Jeffery struct debug_source_basic {
71b1ea254eSAndrew Jeffery int source;
72b1ea254eSAndrew Jeffery };
73b1ea254eSAndrew Jeffery
7486094694SAndrew Jeffery struct debug_source_dbus {
7586094694SAndrew Jeffery struct sd_bus *bus;
7686094694SAndrew Jeffery #define DBUS_SOURCE_PFD_SOURCE 0
7786094694SAndrew Jeffery #define DBUS_SOURCE_PFD_DBUS 1
7886094694SAndrew Jeffery struct pollfd pfds[2];
7986094694SAndrew Jeffery };
8086094694SAndrew Jeffery
811dc6adc9SAndrew Jeffery struct debug_sink_ops {
821dc6adc9SAndrew Jeffery void (*debug)(void *ctx);
831dc6adc9SAndrew Jeffery void (*reboot)(void *ctx);
841dc6adc9SAndrew Jeffery };
851dc6adc9SAndrew Jeffery
861dc6adc9SAndrew Jeffery struct debug_sink {
871dc6adc9SAndrew Jeffery const struct debug_sink_ops *ops;
881dc6adc9SAndrew Jeffery void *ctx;
891dc6adc9SAndrew Jeffery };
901dc6adc9SAndrew Jeffery
911dc6adc9SAndrew Jeffery struct debug_sink_sysrq {
921dc6adc9SAndrew Jeffery int sink;
931dc6adc9SAndrew Jeffery };
941dc6adc9SAndrew Jeffery
9586094694SAndrew Jeffery struct debug_sink_dbus {
9686094694SAndrew Jeffery struct sd_bus *bus;
9786094694SAndrew Jeffery };
9886094694SAndrew Jeffery
sysrq_sink_debug(void * ctx)991dc6adc9SAndrew Jeffery static void sysrq_sink_debug(void *ctx)
100db47cd7fSAndrew Jeffery {
1011dc6adc9SAndrew Jeffery struct debug_sink_sysrq *sysrq = ctx;
10230b6496aSAndrew Jeffery /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n93 */
103db47cd7fSAndrew Jeffery static const char action = 'c';
104db47cd7fSAndrew Jeffery ssize_t rc;
105db47cd7fSAndrew Jeffery
106db47cd7fSAndrew Jeffery sync();
107db47cd7fSAndrew Jeffery
1081dc6adc9SAndrew Jeffery if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action))
109db47cd7fSAndrew Jeffery return;
110db47cd7fSAndrew Jeffery
111db47cd7fSAndrew Jeffery if (rc == -1) {
112db47cd7fSAndrew Jeffery warn("Failed to execute debug command");
113db47cd7fSAndrew Jeffery } else {
114db47cd7fSAndrew Jeffery warnx("Failed to execute debug command: %zd", rc);
115db47cd7fSAndrew Jeffery }
116db47cd7fSAndrew Jeffery }
117db47cd7fSAndrew Jeffery
sysrq_sink_reboot(void * ctx)1181dc6adc9SAndrew Jeffery static void sysrq_sink_reboot(void *ctx)
11911cd254bSAndrew Jeffery {
1201dc6adc9SAndrew Jeffery struct debug_sink_sysrq *sysrq = ctx;
12130b6496aSAndrew Jeffery /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n90 */
12230b6496aSAndrew Jeffery static const char action = 'b';
12311cd254bSAndrew Jeffery ssize_t rc;
12411cd254bSAndrew Jeffery
12511cd254bSAndrew Jeffery sync();
12611cd254bSAndrew Jeffery
1271dc6adc9SAndrew Jeffery if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action))
12830b6496aSAndrew Jeffery return;
12930b6496aSAndrew Jeffery
13030b6496aSAndrew Jeffery if (rc == -1) {
13111cd254bSAndrew Jeffery warn("Failed to reboot BMC");
13230b6496aSAndrew Jeffery } else {
13311cd254bSAndrew Jeffery warnx("Failed to reboot BMC: %zd", rc);
13411cd254bSAndrew Jeffery }
135210ad636SAndrew Jeffery }
136210ad636SAndrew Jeffery
basic_source_poll(void * ctx,char * op)137b1ea254eSAndrew Jeffery static int basic_source_poll(void *ctx, char *op)
138210ad636SAndrew Jeffery {
139b1ea254eSAndrew Jeffery struct debug_source_basic *basic = ctx;
140210ad636SAndrew Jeffery ssize_t ingress;
141210ad636SAndrew Jeffery
142b1ea254eSAndrew Jeffery if ((ingress = read(basic->source, op, 1)) != 1) {
143b1ea254eSAndrew Jeffery if (ingress < 0) {
144b1ea254eSAndrew Jeffery warn("Failed to read from basic source");
145b1ea254eSAndrew Jeffery return -errno;
146b1ea254eSAndrew Jeffery }
147b1ea254eSAndrew Jeffery
148b1ea254eSAndrew Jeffery /* Unreachable */
149b1ea254eSAndrew Jeffery errx(EXIT_FAILURE, "Bad read, requested 1 got %zd", ingress);
150b1ea254eSAndrew Jeffery }
151b1ea254eSAndrew Jeffery
152b1ea254eSAndrew Jeffery return 0;
153b1ea254eSAndrew Jeffery }
154b1ea254eSAndrew Jeffery
15586094694SAndrew Jeffery const struct debug_sink_ops sysrq_sink_ops = {
15686094694SAndrew Jeffery .debug = sysrq_sink_debug,
15786094694SAndrew Jeffery .reboot = sysrq_sink_reboot,
15886094694SAndrew Jeffery };
15986094694SAndrew Jeffery
160b1ea254eSAndrew Jeffery const struct debug_source_ops basic_source_ops = {
161b1ea254eSAndrew Jeffery .poll = basic_source_poll,
162b1ea254eSAndrew Jeffery };
163b1ea254eSAndrew Jeffery
16486094694SAndrew Jeffery #if HAVE_SYSTEMD
16586094694SAndrew Jeffery #include <systemd/sd-bus.h>
16686094694SAndrew Jeffery
16786094694SAndrew Jeffery static void dbus_sink_reboot(void *ctx);
dbus_sink_dump_progress(sd_bus_message * m,void * userdata,sd_bus_error * ret_error)16886094694SAndrew Jeffery static int dbus_sink_dump_progress(sd_bus_message *m, void *userdata,
16986094694SAndrew Jeffery sd_bus_error *ret_error __attribute__((unused)))
17086094694SAndrew Jeffery {
17186094694SAndrew Jeffery struct debug_sink_dbus *dbus = userdata;
17286094694SAndrew Jeffery const char *status;
17386094694SAndrew Jeffery const char *iface;
17486094694SAndrew Jeffery int rc;
17586094694SAndrew Jeffery
17686094694SAndrew Jeffery // sa{sv}as
17786094694SAndrew Jeffery rc = sd_bus_message_read_basic(m, 's', &iface);
17886094694SAndrew Jeffery if (rc < 0) {
17986094694SAndrew Jeffery warnx("Failed to extract interface from PropertiesChanged signal: %s",
18086094694SAndrew Jeffery strerror(-rc));
18186094694SAndrew Jeffery return rc;
18286094694SAndrew Jeffery }
18386094694SAndrew Jeffery
18486094694SAndrew Jeffery /* Bail if it's not an update to the Progress interface */
18586094694SAndrew Jeffery if (strcmp(iface, "xyz.openbmc_project.Common.Progress"))
18686094694SAndrew Jeffery return 0;
18786094694SAndrew Jeffery
18886094694SAndrew Jeffery rc = sd_bus_message_enter_container(m, 'a', "{sv}");
18986094694SAndrew Jeffery if (rc < 0)
19086094694SAndrew Jeffery return rc;
19186094694SAndrew Jeffery
19286094694SAndrew Jeffery if (!rc)
19386094694SAndrew Jeffery return 0;
19486094694SAndrew Jeffery
19586094694SAndrew Jeffery status = NULL;
19686094694SAndrew Jeffery while (1) {
19786094694SAndrew Jeffery const char *member;
19886094694SAndrew Jeffery
19986094694SAndrew Jeffery rc = sd_bus_message_enter_container(m, 'e', "sv");
20086094694SAndrew Jeffery if (rc < 0)
20186094694SAndrew Jeffery return rc;
20286094694SAndrew Jeffery
20386094694SAndrew Jeffery if (!rc)
20486094694SAndrew Jeffery break;
20586094694SAndrew Jeffery
20686094694SAndrew Jeffery rc = sd_bus_message_read_basic(m, 's', &member);
20786094694SAndrew Jeffery if (rc < 0) {
20886094694SAndrew Jeffery warnx("Failed to extract member name from PropertiesChanged signal: %s",
20986094694SAndrew Jeffery strerror(-rc));
21086094694SAndrew Jeffery return rc;
21186094694SAndrew Jeffery }
21286094694SAndrew Jeffery
21386094694SAndrew Jeffery if (!strcmp(member, "Status")) {
21486094694SAndrew Jeffery rc = sd_bus_message_enter_container(m, 'v', "s");
21586094694SAndrew Jeffery if (rc < 0) {
21686094694SAndrew Jeffery warnx("Failed to enter variant container in PropertiesChanged signal: %s",
21786094694SAndrew Jeffery strerror(-rc));
21886094694SAndrew Jeffery return rc;
21986094694SAndrew Jeffery }
22086094694SAndrew Jeffery
22186094694SAndrew Jeffery if (!rc)
22286094694SAndrew Jeffery goto exit_dict_container;
22386094694SAndrew Jeffery
22486094694SAndrew Jeffery rc = sd_bus_message_read_basic(m, 's', &status);
22586094694SAndrew Jeffery if (rc < 0) {
22686094694SAndrew Jeffery warnx("Failed to extract status value from PropertiesChanged signal: %s",
22786094694SAndrew Jeffery strerror(-rc));
22886094694SAndrew Jeffery return rc;
22986094694SAndrew Jeffery }
23086094694SAndrew Jeffery
23186094694SAndrew Jeffery sd_bus_message_exit_container(m);
23286094694SAndrew Jeffery } else {
23386094694SAndrew Jeffery rc = sd_bus_message_skip(m, "v");
23486094694SAndrew Jeffery if (rc < 0) {
23586094694SAndrew Jeffery warnx("Failed to skip variant for unrecognised member %s in PropertiesChanged signal: %s",
23686094694SAndrew Jeffery member, strerror(-rc));
23786094694SAndrew Jeffery return rc;
23886094694SAndrew Jeffery }
23986094694SAndrew Jeffery }
24086094694SAndrew Jeffery
24186094694SAndrew Jeffery exit_dict_container:
24286094694SAndrew Jeffery sd_bus_message_exit_container(m);
24386094694SAndrew Jeffery }
24486094694SAndrew Jeffery
24586094694SAndrew Jeffery sd_bus_message_exit_container(m);
24686094694SAndrew Jeffery
24786094694SAndrew Jeffery if (!status)
24886094694SAndrew Jeffery return 0;
24986094694SAndrew Jeffery
25086094694SAndrew Jeffery printf("Dump progress on %s: %s\n", sd_bus_message_get_path(m), status);
25186094694SAndrew Jeffery
25286094694SAndrew Jeffery /* If we're finished with the dump, reboot the system */
25386094694SAndrew Jeffery if (!strcmp(status, "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")) {
25486094694SAndrew Jeffery sd_bus_slot *slot = sd_bus_get_current_slot(dbus->bus);
25586094694SAndrew Jeffery sd_bus_slot_unref(slot);
25686094694SAndrew Jeffery dbus_sink_reboot(userdata);
25786094694SAndrew Jeffery }
25886094694SAndrew Jeffery
25986094694SAndrew Jeffery return 0;
26086094694SAndrew Jeffery }
26186094694SAndrew Jeffery
dbus_sink_debug(void * ctx)26286094694SAndrew Jeffery static void dbus_sink_debug(void *ctx)
26386094694SAndrew Jeffery {
26486094694SAndrew Jeffery sd_bus_error ret_error = SD_BUS_ERROR_NULL;
26586094694SAndrew Jeffery struct debug_sink_dbus *dbus = ctx;
26686094694SAndrew Jeffery sd_bus_message *reply;
26786094694SAndrew Jeffery sd_bus_slot *slot;
26886094694SAndrew Jeffery const char *path;
26986094694SAndrew Jeffery char *status;
27086094694SAndrew Jeffery int rc;
27186094694SAndrew Jeffery
27286094694SAndrew Jeffery /* Start a BMC dump */
27386094694SAndrew Jeffery rc = sd_bus_call_method(dbus->bus,
27486094694SAndrew Jeffery "xyz.openbmc_project.Dump.Manager",
27586094694SAndrew Jeffery "/xyz/openbmc_project/dump/bmc",
27686094694SAndrew Jeffery "xyz.openbmc_project.Dump.Create",
27786094694SAndrew Jeffery "CreateDump",
27886094694SAndrew Jeffery &ret_error,
27986094694SAndrew Jeffery &reply, "a{sv}", 0);
28086094694SAndrew Jeffery if (rc < 0) {
28186094694SAndrew Jeffery warnx("Failed to call CreateDump: %s", strerror(-rc));
28286094694SAndrew Jeffery return;
28386094694SAndrew Jeffery }
28486094694SAndrew Jeffery
28586094694SAndrew Jeffery /* Extract the dump path */
28686094694SAndrew Jeffery rc = sd_bus_message_read_basic(reply, 'o', &path);
28786094694SAndrew Jeffery if (rc < 0) {
28886094694SAndrew Jeffery warnx("Failed to extract dump object path: %s", strerror(-rc));
28986094694SAndrew Jeffery goto cleanup_reply;
29086094694SAndrew Jeffery }
29186094694SAndrew Jeffery
29286094694SAndrew Jeffery /* Set up a match watching for completion of the dump */
29386094694SAndrew Jeffery rc = sd_bus_match_signal(dbus->bus,
29486094694SAndrew Jeffery &slot,
29586094694SAndrew Jeffery "xyz.openbmc_project.Dump.Manager",
29686094694SAndrew Jeffery path,
29786094694SAndrew Jeffery "org.freedesktop.DBus.Properties",
29886094694SAndrew Jeffery "PropertiesChanged",
29986094694SAndrew Jeffery dbus_sink_dump_progress,
30086094694SAndrew Jeffery ctx);
30186094694SAndrew Jeffery if (rc < 0) {
30286094694SAndrew Jeffery warnx("Failed to add signal match for progress status on dump object %s: %s",
30386094694SAndrew Jeffery path, strerror(-rc));
30486094694SAndrew Jeffery goto cleanup_reply;
30586094694SAndrew Jeffery }
30686094694SAndrew Jeffery
30786094694SAndrew Jeffery /*
30886094694SAndrew Jeffery * Mark the slot as 'floating'. If a slot is _not_ marked as floating it holds a reference
30986094694SAndrew Jeffery * to the bus, and the bus will stay alive so long as the slot is referenced. If the slot is
31086094694SAndrew Jeffery * instead marked floating the relationship is inverted: The lifetime of the slot is defined
31186094694SAndrew Jeffery * in terms of the bus, which means we relieve ourselves of having to track the lifetime of
31286094694SAndrew Jeffery * the slot.
31386094694SAndrew Jeffery *
31486094694SAndrew Jeffery * For more details see `man 3 sd_bus_slot_set_floating`, also documented here:
31586094694SAndrew Jeffery *
31686094694SAndrew Jeffery * https://www.freedesktop.org/software/systemd/man/sd_bus_slot_set_floating.html
31786094694SAndrew Jeffery */
31886094694SAndrew Jeffery rc = sd_bus_slot_set_floating(slot, 0);
31986094694SAndrew Jeffery if (rc < 0) {
32086094694SAndrew Jeffery warnx("Failed to mark progress match slot on %s as floating: %s",
32186094694SAndrew Jeffery path, strerror(-rc));
32286094694SAndrew Jeffery goto cleanup_reply;
32386094694SAndrew Jeffery }
32486094694SAndrew Jeffery
32586094694SAndrew Jeffery printf("Registered progress match on dump object %s\n", path);
32686094694SAndrew Jeffery
32786094694SAndrew Jeffery /* Now that the match is set up, check the current value in case we missed any updates */
32886094694SAndrew Jeffery rc = sd_bus_get_property_string(dbus->bus,
32986094694SAndrew Jeffery "xyz.openbmc_project.Dump.Manager",
33086094694SAndrew Jeffery path,
33186094694SAndrew Jeffery "xyz.openbmc_project.Common.Progress",
33286094694SAndrew Jeffery "Status",
33386094694SAndrew Jeffery &ret_error,
33486094694SAndrew Jeffery &status);
33586094694SAndrew Jeffery if (rc < 0) {
33686094694SAndrew Jeffery warnx("Failed to get progress status property on dump object %s: %s",
33786094694SAndrew Jeffery path, strerror(-rc));
33886094694SAndrew Jeffery sd_bus_slot_unref(slot);
33986094694SAndrew Jeffery goto cleanup_reply;
34086094694SAndrew Jeffery }
34186094694SAndrew Jeffery
34286094694SAndrew Jeffery printf("Dump state for %s is currently %s\n", path, status);
34386094694SAndrew Jeffery
34486094694SAndrew Jeffery /*
34586094694SAndrew Jeffery * If we're finished with the dump, reboot the system. If the dump isn't finished the reboot
34686094694SAndrew Jeffery * will instead take place via the dbus_sink_dump_progress() callback on the match.
34786094694SAndrew Jeffery */
34886094694SAndrew Jeffery if (!strcmp(status, "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")) {
34986094694SAndrew Jeffery sd_bus_slot_unref(slot);
35086094694SAndrew Jeffery dbus_sink_reboot(ctx);
35186094694SAndrew Jeffery }
35286094694SAndrew Jeffery
35386094694SAndrew Jeffery cleanup_reply:
35486094694SAndrew Jeffery sd_bus_message_unref(reply);
35586094694SAndrew Jeffery }
35686094694SAndrew Jeffery
dbus_sink_reboot(void * ctx)35786094694SAndrew Jeffery static void dbus_sink_reboot(void *ctx)
35886094694SAndrew Jeffery {
35986094694SAndrew Jeffery sd_bus_error ret_error = SD_BUS_ERROR_NULL;
36086094694SAndrew Jeffery struct debug_sink_dbus *dbus = ctx;
36186094694SAndrew Jeffery sd_bus_message *reply;
36286094694SAndrew Jeffery int rc;
36386094694SAndrew Jeffery
36486094694SAndrew Jeffery warnx("Rebooting the system");
36586094694SAndrew Jeffery
36686094694SAndrew Jeffery rc = sd_bus_call_method(dbus->bus,
36786094694SAndrew Jeffery "org.freedesktop.systemd1",
36886094694SAndrew Jeffery "/org/freedesktop/systemd1",
36986094694SAndrew Jeffery "org.freedesktop.systemd1.Manager",
37086094694SAndrew Jeffery "StartUnit",
37186094694SAndrew Jeffery &ret_error,
37286094694SAndrew Jeffery &reply,
37386094694SAndrew Jeffery "ss",
37486094694SAndrew Jeffery "reboot.target",
37586094694SAndrew Jeffery "replace-irreversibly");
37686094694SAndrew Jeffery if (rc < 0) {
37786094694SAndrew Jeffery warnx("Failed to start reboot.target: %s", strerror(-rc));
37886094694SAndrew Jeffery }
37986094694SAndrew Jeffery }
38086094694SAndrew Jeffery
dbus_source_poll(void * ctx,char * op)38186094694SAndrew Jeffery static int dbus_source_poll(void *ctx, char *op)
38286094694SAndrew Jeffery {
38386094694SAndrew Jeffery struct debug_source_dbus *dbus = ctx;
38486094694SAndrew Jeffery int rc;
38586094694SAndrew Jeffery
38686094694SAndrew Jeffery while (1) {
38786094694SAndrew Jeffery struct timespec tsto, *ptsto;
38886094694SAndrew Jeffery uint64_t dbusto;
38986094694SAndrew Jeffery
39086094694SAndrew Jeffery /* See SD_BUS_GET_FD(3) */
39186094694SAndrew Jeffery dbus->pfds[DBUS_SOURCE_PFD_DBUS].fd = sd_bus_get_fd(dbus->bus);
39286094694SAndrew Jeffery dbus->pfds[DBUS_SOURCE_PFD_DBUS].events = sd_bus_get_events(dbus->bus);
39386094694SAndrew Jeffery rc = sd_bus_get_timeout(dbus->bus, &dbusto);
39486094694SAndrew Jeffery if (rc < 0)
39586094694SAndrew Jeffery return rc;
39686094694SAndrew Jeffery
39786094694SAndrew Jeffery if (dbusto == UINT64_MAX) {
39886094694SAndrew Jeffery ptsto = NULL;
39986094694SAndrew Jeffery } else if (dbus->pfds[DBUS_SOURCE_PFD_DBUS].events == 0) {
40086094694SAndrew Jeffery ptsto = NULL;
40186094694SAndrew Jeffery } else {
40286094694SAndrew Jeffery #define MSEC_PER_SEC 1000U
40386094694SAndrew Jeffery #define USEC_PER_SEC (MSEC_PER_SEC * 1000U)
40486094694SAndrew Jeffery #define NSEC_PER_SEC (USEC_PER_SEC * 1000U)
40586094694SAndrew Jeffery #define NSEC_PER_USEC (NSEC_PER_SEC / USEC_PER_SEC)
40686094694SAndrew Jeffery tsto.tv_sec = dbusto / USEC_PER_SEC;
40786094694SAndrew Jeffery tsto.tv_nsec = (dbusto % USEC_PER_SEC) * NSEC_PER_USEC;
40886094694SAndrew Jeffery ptsto = &tsto;
40986094694SAndrew Jeffery }
41086094694SAndrew Jeffery
41186094694SAndrew Jeffery if ((rc = ppoll(dbus->pfds, ARRAY_SIZE(dbus->pfds), ptsto, NULL)) < 0) {
41286094694SAndrew Jeffery warn("Failed polling source fds");
41386094694SAndrew Jeffery return -errno;
41486094694SAndrew Jeffery }
41586094694SAndrew Jeffery
41686094694SAndrew Jeffery if (dbus->pfds[DBUS_SOURCE_PFD_SOURCE].revents) {
41786094694SAndrew Jeffery ssize_t ingress;
41886094694SAndrew Jeffery
41986094694SAndrew Jeffery if ((ingress = read(dbus->pfds[DBUS_SOURCE_PFD_SOURCE].fd, op, 1)) != 1) {
42086094694SAndrew Jeffery if (ingress < 0) {
42186094694SAndrew Jeffery warn("Failed to read from basic source");
42286094694SAndrew Jeffery return -errno;
42386094694SAndrew Jeffery }
42486094694SAndrew Jeffery
42586094694SAndrew Jeffery errx(EXIT_FAILURE, "Bad read, requested 1 got %zd", ingress);
42686094694SAndrew Jeffery }
42786094694SAndrew Jeffery
42886094694SAndrew Jeffery return 0;
42986094694SAndrew Jeffery }
43086094694SAndrew Jeffery
43186094694SAndrew Jeffery if (dbus->pfds[DBUS_SOURCE_PFD_DBUS].revents) {
43286094694SAndrew Jeffery if ((rc = sd_bus_process(dbus->bus, NULL)) < 0) {
43386094694SAndrew Jeffery warnx("Failed processing inbound D-Bus messages: %s",
43486094694SAndrew Jeffery strerror(-rc));
43586094694SAndrew Jeffery return rc;
43686094694SAndrew Jeffery }
43786094694SAndrew Jeffery }
43886094694SAndrew Jeffery }
43986094694SAndrew Jeffery }
44086094694SAndrew Jeffery #else
dbus_sink_debug(void * ctx)44186094694SAndrew Jeffery static void dbus_sink_debug(void *ctx)
44286094694SAndrew Jeffery {
44386094694SAndrew Jeffery warnx("%s: Configured without systemd, dbus sinks disabled", __func__);
44486094694SAndrew Jeffery }
44586094694SAndrew Jeffery
dbus_sink_reboot(void * ctx)44686094694SAndrew Jeffery static void dbus_sink_reboot(void *ctx)
44786094694SAndrew Jeffery {
44886094694SAndrew Jeffery warnx("%s: Configured without systemd, dbus sinks disabled", __func__);
44986094694SAndrew Jeffery }
45086094694SAndrew Jeffery
dbus_source_poll(void * ctx,char * op)45186094694SAndrew Jeffery static int dbus_source_poll(void *ctx, char *op)
45286094694SAndrew Jeffery {
45386094694SAndrew Jeffery errx(EXIT_FAILURE, "Configured without systemd, dbus sources disabled", __func__);
45486094694SAndrew Jeffery }
45586094694SAndrew Jeffery #endif
45686094694SAndrew Jeffery
45786094694SAndrew Jeffery const struct debug_sink_ops dbus_sink_ops = {
45886094694SAndrew Jeffery .debug = dbus_sink_debug,
45986094694SAndrew Jeffery .reboot = dbus_sink_reboot,
46086094694SAndrew Jeffery };
46186094694SAndrew Jeffery
46286094694SAndrew Jeffery const struct debug_source_ops dbus_source_ops = {
46386094694SAndrew Jeffery .poll = dbus_source_poll,
46486094694SAndrew Jeffery };
46586094694SAndrew Jeffery
process(struct debug_source * source,struct debug_sink * sink)466b1ea254eSAndrew Jeffery static int process(struct debug_source *source, struct debug_sink *sink)
467b1ea254eSAndrew Jeffery {
468b1ea254eSAndrew Jeffery char command;
469b1ea254eSAndrew Jeffery int rc;
470b1ea254eSAndrew Jeffery
471b1ea254eSAndrew Jeffery while (!(rc = source->ops->poll(source->ctx, &command))) {
472210ad636SAndrew Jeffery switch (command) {
473210ad636SAndrew Jeffery case 'D':
4741161642dSAndrew Jeffery warnx("Debug action triggered\n");
4751dc6adc9SAndrew Jeffery sink->ops->debug(sink->ctx);
476210ad636SAndrew Jeffery break;
477210ad636SAndrew Jeffery case 'R':
4781161642dSAndrew Jeffery warnx("Reboot action triggered\n");
4791dc6adc9SAndrew Jeffery sink->ops->reboot(sink->ctx);
48011cd254bSAndrew Jeffery break;
48111cd254bSAndrew Jeffery default:
48211cd254bSAndrew Jeffery warnx("Unexpected command: 0x%02x (%c)", command, command);
48311cd254bSAndrew Jeffery }
48411cd254bSAndrew Jeffery }
48511cd254bSAndrew Jeffery
486b1ea254eSAndrew Jeffery if (rc < 0)
487b1ea254eSAndrew Jeffery warnx("Failed to poll source: %s", strerror(-rc));
48811cd254bSAndrew Jeffery
489b1ea254eSAndrew Jeffery return rc;
49011cd254bSAndrew Jeffery }
49111cd254bSAndrew Jeffery
main(int argc,char * const argv[])49211cd254bSAndrew Jeffery int main(int argc, char * const argv[])
49311cd254bSAndrew Jeffery {
49486094694SAndrew Jeffery struct debug_source_basic basic_source;
49586094694SAndrew Jeffery struct debug_source_dbus dbus_source;
49686094694SAndrew Jeffery struct debug_sink_sysrq sysrq_sink;
49786094694SAndrew Jeffery struct debug_sink_dbus dbus_sink;
498e998ba77SAndrew Jeffery const char *sink_actions = NULL;
499b1ea254eSAndrew Jeffery struct debug_source source;
5001dc6adc9SAndrew Jeffery struct debug_sink sink;
50111cd254bSAndrew Jeffery char devnode[PATH_MAX];
50211cd254bSAndrew Jeffery char *devid;
5031dc6adc9SAndrew Jeffery int sourcefd;
5041dc6adc9SAndrew Jeffery int sinkfd;
50511cd254bSAndrew Jeffery
506e998ba77SAndrew Jeffery /* Option processing */
50711cd254bSAndrew Jeffery while (1) {
50811cd254bSAndrew Jeffery static struct option long_options[] = {
509e998ba77SAndrew Jeffery {"sink-actions", required_argument, 0, 's'},
51011cd254bSAndrew Jeffery {0, 0, 0, 0},
51111cd254bSAndrew Jeffery };
51211cd254bSAndrew Jeffery int c;
51311cd254bSAndrew Jeffery
51411cd254bSAndrew Jeffery c = getopt_long(argc, argv, "", long_options, NULL);
51511cd254bSAndrew Jeffery if (c == -1)
51611cd254bSAndrew Jeffery break;
517e998ba77SAndrew Jeffery
518e998ba77SAndrew Jeffery switch (c) {
519e998ba77SAndrew Jeffery case 's':
520e998ba77SAndrew Jeffery sink_actions = optarg;
521e998ba77SAndrew Jeffery break;
522e998ba77SAndrew Jeffery default:
523e998ba77SAndrew Jeffery break;
524e998ba77SAndrew Jeffery }
52511cd254bSAndrew Jeffery }
52611cd254bSAndrew Jeffery
527d65368beSAndrew Jeffery /*
528e998ba77SAndrew Jeffery * The default behaviour sets the source file descriptor as stdin and the sink file
529e998ba77SAndrew Jeffery * descriptor as stdout. This allows trivial testing on the command-line with just a
530e998ba77SAndrew Jeffery * keyboard and without crashing the system.
531d65368beSAndrew Jeffery */
5321dc6adc9SAndrew Jeffery sourcefd = 0;
5331dc6adc9SAndrew Jeffery sinkfd = 1;
53411cd254bSAndrew Jeffery
535e998ba77SAndrew Jeffery /* Handle the source path argument, if any */
53611cd254bSAndrew Jeffery if (optind < argc) {
53711cd254bSAndrew Jeffery char devpath[PATH_MAX];
53811cd254bSAndrew Jeffery
539d65368beSAndrew Jeffery /*
540d65368beSAndrew Jeffery * To make our lives easy with udev we take the basename of the source argument and
541d65368beSAndrew Jeffery * look for it in /dev. This allows us to use %p (the devpath specifier) in the udev
542d65368beSAndrew Jeffery * rule to pass the device of interest to the systemd unit.
543d65368beSAndrew Jeffery */
54411cd254bSAndrew Jeffery strncpy(devpath, argv[optind], sizeof(devpath));
54511cd254bSAndrew Jeffery devpath[PATH_MAX - 1] = '\0';
54611cd254bSAndrew Jeffery devid = basename(devpath);
54711cd254bSAndrew Jeffery
54811cd254bSAndrew Jeffery strncpy(devnode, "/dev/", sizeof(devnode));
549*be3dd0e6SAndrew Jeffery strncat(devnode, devid, sizeof(devnode) - strlen("/dev/"));
55011cd254bSAndrew Jeffery devnode[PATH_MAX - 1] = '\0';
55111cd254bSAndrew Jeffery
5521dc6adc9SAndrew Jeffery if ((sourcefd = open(devnode, O_RDONLY)) == -1)
553ea84ad97SAndrew Jeffery err(EXIT_FAILURE, "Failed to open source %s", devnode);
55411cd254bSAndrew Jeffery
55511cd254bSAndrew Jeffery optind++;
55611cd254bSAndrew Jeffery }
55711cd254bSAndrew Jeffery
558e998ba77SAndrew Jeffery /*
559e998ba77SAndrew Jeffery * Handle the sink path argument, if any. If sink_actions hasn't been set via the
560e998ba77SAndrew Jeffery * --sink-actions option, then default to 'sysrq'. Otherwise, if --sink-actions=sysrq has
561e998ba77SAndrew Jeffery * been passed, do as we're told and use the 'sysrq' sink actions.
562e998ba77SAndrew Jeffery */
563e998ba77SAndrew Jeffery if (!sink_actions || !strcmp("sysrq", sink_actions)) {
56411cd254bSAndrew Jeffery if (optind < argc) {
565d65368beSAndrew Jeffery /*
566e998ba77SAndrew Jeffery * Just open the sink path directly. If we ever need different behaviour
567e998ba77SAndrew Jeffery * then we patch this bit when we know what we need.
568d65368beSAndrew Jeffery */
5691dc6adc9SAndrew Jeffery if ((sinkfd = open(argv[optind], O_WRONLY)) == -1)
570ea84ad97SAndrew Jeffery err(EXIT_FAILURE, "Failed to open sink %s", argv[optind]);
57111cd254bSAndrew Jeffery
57211cd254bSAndrew Jeffery optind++;
57311cd254bSAndrew Jeffery }
57411cd254bSAndrew Jeffery
57586094694SAndrew Jeffery basic_source.source = sourcefd;
57686094694SAndrew Jeffery source.ops = &basic_source_ops;
57786094694SAndrew Jeffery source.ctx = &basic_source;
57886094694SAndrew Jeffery
57986094694SAndrew Jeffery sysrq_sink.sink = sinkfd;
5801dc6adc9SAndrew Jeffery sink.ops = &sysrq_sink_ops;
58186094694SAndrew Jeffery sink.ctx = &sysrq_sink;
58286094694SAndrew Jeffery }
58386094694SAndrew Jeffery
58486094694SAndrew Jeffery /* Set up the dbus sink actions if requested via --sink-actions=dbus */
58586094694SAndrew Jeffery if (sink_actions && !strcmp("dbus", sink_actions)) {
58686094694SAndrew Jeffery sd_bus *bus;
58786094694SAndrew Jeffery int rc;
58886094694SAndrew Jeffery
58986094694SAndrew Jeffery rc = sd_bus_open_system(&bus);
59086094694SAndrew Jeffery if (rc < 0) {
59186094694SAndrew Jeffery errx(EXIT_FAILURE, "Failed to connect to the system bus: %s",
59286094694SAndrew Jeffery strerror(-rc));
59386094694SAndrew Jeffery }
59486094694SAndrew Jeffery
59586094694SAndrew Jeffery dbus_source.bus = bus;
59686094694SAndrew Jeffery dbus_source.pfds[DBUS_SOURCE_PFD_SOURCE].fd = sourcefd;
59786094694SAndrew Jeffery dbus_source.pfds[DBUS_SOURCE_PFD_SOURCE].events = POLLIN;
59886094694SAndrew Jeffery source.ops = &dbus_source_ops;
59986094694SAndrew Jeffery source.ctx = &dbus_source;
60086094694SAndrew Jeffery
60186094694SAndrew Jeffery dbus_sink.bus = bus;
60286094694SAndrew Jeffery sink.ops = &dbus_sink_ops;
60386094694SAndrew Jeffery sink.ctx = &dbus_sink;
604e998ba77SAndrew Jeffery }
605e998ba77SAndrew Jeffery
606e998ba77SAndrew Jeffery /* Check we're done with the command-line */
607e998ba77SAndrew Jeffery if (optind < argc)
60886094694SAndrew Jeffery errx(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind);
6091dc6adc9SAndrew Jeffery
61086094694SAndrew Jeffery if (!(source.ops && source.ctx))
61186094694SAndrew Jeffery errx(EXIT_FAILURE, "Invalid source configuration");
61286094694SAndrew Jeffery
61386094694SAndrew Jeffery if (!(sink.ops && sink.ctx))
61486094694SAndrew Jeffery errx(EXIT_FAILURE, "Unrecognised sink: %s", sink_actions);
615b1ea254eSAndrew Jeffery
616d65368beSAndrew Jeffery /* Trigger the actions on the sink when we receive an event from the source */
617b1ea254eSAndrew Jeffery if (process(&source, &sink) < 0)
61811cd254bSAndrew Jeffery errx(EXIT_FAILURE, "Failure while processing command stream");
61911cd254bSAndrew Jeffery
62011cd254bSAndrew Jeffery return 0;
62111cd254bSAndrew Jeffery }
622