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 13*86094694SAndrew Jeffery * Set the class of sink action(s) to be used. Can take the value of 'sysrq' or 'dbus'. 14*86094694SAndrew 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. 30*86094694SAndrew Jeffery * 31*86094694SAndrew Jeffery * dbug-trigger --sink-actions=dbus /dev/serio_raw0 32*86094694SAndrew Jeffery * Open /dev/serio_raw0 as the source and configure the 'dbus' set of sink actions. When 'D' is 33*86094694SAndrew Jeffery * read from /dev/serio_raw0 create a dump via phosphor-debug-collector by calling through its 34*86094694SAndrew Jeffery * D-Bus interface, then reboot the system by starting systemd's 'reboot.target' 35d65368beSAndrew Jeffery */ 36*86094694SAndrew Jeffery #define _GNU_SOURCE 37*86094694SAndrew Jeffery 38*86094694SAndrew 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> 47*86094694SAndrew Jeffery #include <poll.h> 48*86094694SAndrew Jeffery #include <stdbool.h> 49*86094694SAndrew 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 57*86094694SAndrew Jeffery #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) 58*86094694SAndrew Jeffery 59*86094694SAndrew Jeffery struct sd_bus; 60*86094694SAndrew 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 74*86094694SAndrew Jeffery struct debug_source_dbus { 75*86094694SAndrew Jeffery struct sd_bus *bus; 76*86094694SAndrew Jeffery #define DBUS_SOURCE_PFD_SOURCE 0 77*86094694SAndrew Jeffery #define DBUS_SOURCE_PFD_DBUS 1 78*86094694SAndrew Jeffery struct pollfd pfds[2]; 79*86094694SAndrew Jeffery }; 80*86094694SAndrew 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 95*86094694SAndrew Jeffery struct debug_sink_dbus { 96*86094694SAndrew Jeffery struct sd_bus *bus; 97*86094694SAndrew Jeffery }; 98*86094694SAndrew Jeffery 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 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 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 155*86094694SAndrew Jeffery const struct debug_sink_ops sysrq_sink_ops = { 156*86094694SAndrew Jeffery .debug = sysrq_sink_debug, 157*86094694SAndrew Jeffery .reboot = sysrq_sink_reboot, 158*86094694SAndrew Jeffery }; 159*86094694SAndrew Jeffery 160b1ea254eSAndrew Jeffery const struct debug_source_ops basic_source_ops = { 161b1ea254eSAndrew Jeffery .poll = basic_source_poll, 162b1ea254eSAndrew Jeffery }; 163b1ea254eSAndrew Jeffery 164*86094694SAndrew Jeffery #if HAVE_SYSTEMD 165*86094694SAndrew Jeffery #include <systemd/sd-bus.h> 166*86094694SAndrew Jeffery 167*86094694SAndrew Jeffery static void dbus_sink_reboot(void *ctx); 168*86094694SAndrew Jeffery static int dbus_sink_dump_progress(sd_bus_message *m, void *userdata, 169*86094694SAndrew Jeffery sd_bus_error *ret_error __attribute__((unused))) 170*86094694SAndrew Jeffery { 171*86094694SAndrew Jeffery struct debug_sink_dbus *dbus = userdata; 172*86094694SAndrew Jeffery const char *status; 173*86094694SAndrew Jeffery const char *iface; 174*86094694SAndrew Jeffery int rc; 175*86094694SAndrew Jeffery 176*86094694SAndrew Jeffery // sa{sv}as 177*86094694SAndrew Jeffery rc = sd_bus_message_read_basic(m, 's', &iface); 178*86094694SAndrew Jeffery if (rc < 0) { 179*86094694SAndrew Jeffery warnx("Failed to extract interface from PropertiesChanged signal: %s", 180*86094694SAndrew Jeffery strerror(-rc)); 181*86094694SAndrew Jeffery return rc; 182*86094694SAndrew Jeffery } 183*86094694SAndrew Jeffery 184*86094694SAndrew Jeffery /* Bail if it's not an update to the Progress interface */ 185*86094694SAndrew Jeffery if (strcmp(iface, "xyz.openbmc_project.Common.Progress")) 186*86094694SAndrew Jeffery return 0; 187*86094694SAndrew Jeffery 188*86094694SAndrew Jeffery rc = sd_bus_message_enter_container(m, 'a', "{sv}"); 189*86094694SAndrew Jeffery if (rc < 0) 190*86094694SAndrew Jeffery return rc; 191*86094694SAndrew Jeffery 192*86094694SAndrew Jeffery if (!rc) 193*86094694SAndrew Jeffery return 0; 194*86094694SAndrew Jeffery 195*86094694SAndrew Jeffery status = NULL; 196*86094694SAndrew Jeffery while (1) { 197*86094694SAndrew Jeffery const char *member; 198*86094694SAndrew Jeffery 199*86094694SAndrew Jeffery rc = sd_bus_message_enter_container(m, 'e', "sv"); 200*86094694SAndrew Jeffery if (rc < 0) 201*86094694SAndrew Jeffery return rc; 202*86094694SAndrew Jeffery 203*86094694SAndrew Jeffery if (!rc) 204*86094694SAndrew Jeffery break; 205*86094694SAndrew Jeffery 206*86094694SAndrew Jeffery rc = sd_bus_message_read_basic(m, 's', &member); 207*86094694SAndrew Jeffery if (rc < 0) { 208*86094694SAndrew Jeffery warnx("Failed to extract member name from PropertiesChanged signal: %s", 209*86094694SAndrew Jeffery strerror(-rc)); 210*86094694SAndrew Jeffery return rc; 211*86094694SAndrew Jeffery } 212*86094694SAndrew Jeffery 213*86094694SAndrew Jeffery if (!strcmp(member, "Status")) { 214*86094694SAndrew Jeffery rc = sd_bus_message_enter_container(m, 'v', "s"); 215*86094694SAndrew Jeffery if (rc < 0) { 216*86094694SAndrew Jeffery warnx("Failed to enter variant container in PropertiesChanged signal: %s", 217*86094694SAndrew Jeffery strerror(-rc)); 218*86094694SAndrew Jeffery return rc; 219*86094694SAndrew Jeffery } 220*86094694SAndrew Jeffery 221*86094694SAndrew Jeffery if (!rc) 222*86094694SAndrew Jeffery goto exit_dict_container; 223*86094694SAndrew Jeffery 224*86094694SAndrew Jeffery rc = sd_bus_message_read_basic(m, 's', &status); 225*86094694SAndrew Jeffery if (rc < 0) { 226*86094694SAndrew Jeffery warnx("Failed to extract status value from PropertiesChanged signal: %s", 227*86094694SAndrew Jeffery strerror(-rc)); 228*86094694SAndrew Jeffery return rc; 229*86094694SAndrew Jeffery } 230*86094694SAndrew Jeffery 231*86094694SAndrew Jeffery sd_bus_message_exit_container(m); 232*86094694SAndrew Jeffery } else { 233*86094694SAndrew Jeffery rc = sd_bus_message_skip(m, "v"); 234*86094694SAndrew Jeffery if (rc < 0) { 235*86094694SAndrew Jeffery warnx("Failed to skip variant for unrecognised member %s in PropertiesChanged signal: %s", 236*86094694SAndrew Jeffery member, strerror(-rc)); 237*86094694SAndrew Jeffery return rc; 238*86094694SAndrew Jeffery } 239*86094694SAndrew Jeffery } 240*86094694SAndrew Jeffery 241*86094694SAndrew Jeffery exit_dict_container: 242*86094694SAndrew Jeffery sd_bus_message_exit_container(m); 243*86094694SAndrew Jeffery } 244*86094694SAndrew Jeffery 245*86094694SAndrew Jeffery sd_bus_message_exit_container(m); 246*86094694SAndrew Jeffery 247*86094694SAndrew Jeffery if (!status) 248*86094694SAndrew Jeffery return 0; 249*86094694SAndrew Jeffery 250*86094694SAndrew Jeffery printf("Dump progress on %s: %s\n", sd_bus_message_get_path(m), status); 251*86094694SAndrew Jeffery 252*86094694SAndrew Jeffery /* If we're finished with the dump, reboot the system */ 253*86094694SAndrew Jeffery if (!strcmp(status, "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")) { 254*86094694SAndrew Jeffery sd_bus_slot *slot = sd_bus_get_current_slot(dbus->bus); 255*86094694SAndrew Jeffery sd_bus_slot_unref(slot); 256*86094694SAndrew Jeffery dbus_sink_reboot(userdata); 257*86094694SAndrew Jeffery } 258*86094694SAndrew Jeffery 259*86094694SAndrew Jeffery return 0; 260*86094694SAndrew Jeffery } 261*86094694SAndrew Jeffery 262*86094694SAndrew Jeffery static void dbus_sink_debug(void *ctx) 263*86094694SAndrew Jeffery { 264*86094694SAndrew Jeffery sd_bus_error ret_error = SD_BUS_ERROR_NULL; 265*86094694SAndrew Jeffery struct debug_sink_dbus *dbus = ctx; 266*86094694SAndrew Jeffery sd_bus_message *reply; 267*86094694SAndrew Jeffery sd_bus_slot *slot; 268*86094694SAndrew Jeffery const char *path; 269*86094694SAndrew Jeffery char *status; 270*86094694SAndrew Jeffery int rc; 271*86094694SAndrew Jeffery 272*86094694SAndrew Jeffery /* Start a BMC dump */ 273*86094694SAndrew Jeffery rc = sd_bus_call_method(dbus->bus, 274*86094694SAndrew Jeffery "xyz.openbmc_project.Dump.Manager", 275*86094694SAndrew Jeffery "/xyz/openbmc_project/dump/bmc", 276*86094694SAndrew Jeffery "xyz.openbmc_project.Dump.Create", 277*86094694SAndrew Jeffery "CreateDump", 278*86094694SAndrew Jeffery &ret_error, 279*86094694SAndrew Jeffery &reply, "a{sv}", 0); 280*86094694SAndrew Jeffery if (rc < 0) { 281*86094694SAndrew Jeffery warnx("Failed to call CreateDump: %s", strerror(-rc)); 282*86094694SAndrew Jeffery return; 283*86094694SAndrew Jeffery } 284*86094694SAndrew Jeffery 285*86094694SAndrew Jeffery /* Extract the dump path */ 286*86094694SAndrew Jeffery rc = sd_bus_message_read_basic(reply, 'o', &path); 287*86094694SAndrew Jeffery if (rc < 0) { 288*86094694SAndrew Jeffery warnx("Failed to extract dump object path: %s", strerror(-rc)); 289*86094694SAndrew Jeffery goto cleanup_reply; 290*86094694SAndrew Jeffery } 291*86094694SAndrew Jeffery 292*86094694SAndrew Jeffery /* Set up a match watching for completion of the dump */ 293*86094694SAndrew Jeffery rc = sd_bus_match_signal(dbus->bus, 294*86094694SAndrew Jeffery &slot, 295*86094694SAndrew Jeffery "xyz.openbmc_project.Dump.Manager", 296*86094694SAndrew Jeffery path, 297*86094694SAndrew Jeffery "org.freedesktop.DBus.Properties", 298*86094694SAndrew Jeffery "PropertiesChanged", 299*86094694SAndrew Jeffery dbus_sink_dump_progress, 300*86094694SAndrew Jeffery ctx); 301*86094694SAndrew Jeffery if (rc < 0) { 302*86094694SAndrew Jeffery warnx("Failed to add signal match for progress status on dump object %s: %s", 303*86094694SAndrew Jeffery path, strerror(-rc)); 304*86094694SAndrew Jeffery goto cleanup_reply; 305*86094694SAndrew Jeffery } 306*86094694SAndrew Jeffery 307*86094694SAndrew Jeffery /* 308*86094694SAndrew Jeffery * Mark the slot as 'floating'. If a slot is _not_ marked as floating it holds a reference 309*86094694SAndrew Jeffery * to the bus, and the bus will stay alive so long as the slot is referenced. If the slot is 310*86094694SAndrew Jeffery * instead marked floating the relationship is inverted: The lifetime of the slot is defined 311*86094694SAndrew Jeffery * in terms of the bus, which means we relieve ourselves of having to track the lifetime of 312*86094694SAndrew Jeffery * the slot. 313*86094694SAndrew Jeffery * 314*86094694SAndrew Jeffery * For more details see `man 3 sd_bus_slot_set_floating`, also documented here: 315*86094694SAndrew Jeffery * 316*86094694SAndrew Jeffery * https://www.freedesktop.org/software/systemd/man/sd_bus_slot_set_floating.html 317*86094694SAndrew Jeffery */ 318*86094694SAndrew Jeffery rc = sd_bus_slot_set_floating(slot, 0); 319*86094694SAndrew Jeffery if (rc < 0) { 320*86094694SAndrew Jeffery warnx("Failed to mark progress match slot on %s as floating: %s", 321*86094694SAndrew Jeffery path, strerror(-rc)); 322*86094694SAndrew Jeffery goto cleanup_reply; 323*86094694SAndrew Jeffery } 324*86094694SAndrew Jeffery 325*86094694SAndrew Jeffery printf("Registered progress match on dump object %s\n", path); 326*86094694SAndrew Jeffery 327*86094694SAndrew Jeffery /* Now that the match is set up, check the current value in case we missed any updates */ 328*86094694SAndrew Jeffery rc = sd_bus_get_property_string(dbus->bus, 329*86094694SAndrew Jeffery "xyz.openbmc_project.Dump.Manager", 330*86094694SAndrew Jeffery path, 331*86094694SAndrew Jeffery "xyz.openbmc_project.Common.Progress", 332*86094694SAndrew Jeffery "Status", 333*86094694SAndrew Jeffery &ret_error, 334*86094694SAndrew Jeffery &status); 335*86094694SAndrew Jeffery if (rc < 0) { 336*86094694SAndrew Jeffery warnx("Failed to get progress status property on dump object %s: %s", 337*86094694SAndrew Jeffery path, strerror(-rc)); 338*86094694SAndrew Jeffery sd_bus_slot_unref(slot); 339*86094694SAndrew Jeffery goto cleanup_reply; 340*86094694SAndrew Jeffery } 341*86094694SAndrew Jeffery 342*86094694SAndrew Jeffery printf("Dump state for %s is currently %s\n", path, status); 343*86094694SAndrew Jeffery 344*86094694SAndrew Jeffery /* 345*86094694SAndrew Jeffery * If we're finished with the dump, reboot the system. If the dump isn't finished the reboot 346*86094694SAndrew Jeffery * will instead take place via the dbus_sink_dump_progress() callback on the match. 347*86094694SAndrew Jeffery */ 348*86094694SAndrew Jeffery if (!strcmp(status, "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")) { 349*86094694SAndrew Jeffery sd_bus_slot_unref(slot); 350*86094694SAndrew Jeffery dbus_sink_reboot(ctx); 351*86094694SAndrew Jeffery } 352*86094694SAndrew Jeffery 353*86094694SAndrew Jeffery cleanup_reply: 354*86094694SAndrew Jeffery sd_bus_message_unref(reply); 355*86094694SAndrew Jeffery } 356*86094694SAndrew Jeffery 357*86094694SAndrew Jeffery static void dbus_sink_reboot(void *ctx) 358*86094694SAndrew Jeffery { 359*86094694SAndrew Jeffery sd_bus_error ret_error = SD_BUS_ERROR_NULL; 360*86094694SAndrew Jeffery struct debug_sink_dbus *dbus = ctx; 361*86094694SAndrew Jeffery sd_bus_message *reply; 362*86094694SAndrew Jeffery int rc; 363*86094694SAndrew Jeffery 364*86094694SAndrew Jeffery warnx("Rebooting the system"); 365*86094694SAndrew Jeffery 366*86094694SAndrew Jeffery rc = sd_bus_call_method(dbus->bus, 367*86094694SAndrew Jeffery "org.freedesktop.systemd1", 368*86094694SAndrew Jeffery "/org/freedesktop/systemd1", 369*86094694SAndrew Jeffery "org.freedesktop.systemd1.Manager", 370*86094694SAndrew Jeffery "StartUnit", 371*86094694SAndrew Jeffery &ret_error, 372*86094694SAndrew Jeffery &reply, 373*86094694SAndrew Jeffery "ss", 374*86094694SAndrew Jeffery "reboot.target", 375*86094694SAndrew Jeffery "replace-irreversibly"); 376*86094694SAndrew Jeffery if (rc < 0) { 377*86094694SAndrew Jeffery warnx("Failed to start reboot.target: %s", strerror(-rc)); 378*86094694SAndrew Jeffery } 379*86094694SAndrew Jeffery } 380*86094694SAndrew Jeffery 381*86094694SAndrew Jeffery static int dbus_source_poll(void *ctx, char *op) 382*86094694SAndrew Jeffery { 383*86094694SAndrew Jeffery struct debug_source_dbus *dbus = ctx; 384*86094694SAndrew Jeffery int rc; 385*86094694SAndrew Jeffery 386*86094694SAndrew Jeffery while (1) { 387*86094694SAndrew Jeffery struct timespec tsto, *ptsto; 388*86094694SAndrew Jeffery uint64_t dbusto; 389*86094694SAndrew Jeffery 390*86094694SAndrew Jeffery /* See SD_BUS_GET_FD(3) */ 391*86094694SAndrew Jeffery dbus->pfds[DBUS_SOURCE_PFD_DBUS].fd = sd_bus_get_fd(dbus->bus); 392*86094694SAndrew Jeffery dbus->pfds[DBUS_SOURCE_PFD_DBUS].events = sd_bus_get_events(dbus->bus); 393*86094694SAndrew Jeffery rc = sd_bus_get_timeout(dbus->bus, &dbusto); 394*86094694SAndrew Jeffery if (rc < 0) 395*86094694SAndrew Jeffery return rc; 396*86094694SAndrew Jeffery 397*86094694SAndrew Jeffery if (dbusto == UINT64_MAX) { 398*86094694SAndrew Jeffery ptsto = NULL; 399*86094694SAndrew Jeffery } else if (dbus->pfds[DBUS_SOURCE_PFD_DBUS].events == 0) { 400*86094694SAndrew Jeffery ptsto = NULL; 401*86094694SAndrew Jeffery } else { 402*86094694SAndrew Jeffery #define MSEC_PER_SEC 1000U 403*86094694SAndrew Jeffery #define USEC_PER_SEC (MSEC_PER_SEC * 1000U) 404*86094694SAndrew Jeffery #define NSEC_PER_SEC (USEC_PER_SEC * 1000U) 405*86094694SAndrew Jeffery #define NSEC_PER_USEC (NSEC_PER_SEC / USEC_PER_SEC) 406*86094694SAndrew Jeffery tsto.tv_sec = dbusto / USEC_PER_SEC; 407*86094694SAndrew Jeffery tsto.tv_nsec = (dbusto % USEC_PER_SEC) * NSEC_PER_USEC; 408*86094694SAndrew Jeffery ptsto = &tsto; 409*86094694SAndrew Jeffery } 410*86094694SAndrew Jeffery 411*86094694SAndrew Jeffery if ((rc = ppoll(dbus->pfds, ARRAY_SIZE(dbus->pfds), ptsto, NULL)) < 0) { 412*86094694SAndrew Jeffery warn("Failed polling source fds"); 413*86094694SAndrew Jeffery return -errno; 414*86094694SAndrew Jeffery } 415*86094694SAndrew Jeffery 416*86094694SAndrew Jeffery if (dbus->pfds[DBUS_SOURCE_PFD_SOURCE].revents) { 417*86094694SAndrew Jeffery ssize_t ingress; 418*86094694SAndrew Jeffery 419*86094694SAndrew Jeffery if ((ingress = read(dbus->pfds[DBUS_SOURCE_PFD_SOURCE].fd, op, 1)) != 1) { 420*86094694SAndrew Jeffery if (ingress < 0) { 421*86094694SAndrew Jeffery warn("Failed to read from basic source"); 422*86094694SAndrew Jeffery return -errno; 423*86094694SAndrew Jeffery } 424*86094694SAndrew Jeffery 425*86094694SAndrew Jeffery errx(EXIT_FAILURE, "Bad read, requested 1 got %zd", ingress); 426*86094694SAndrew Jeffery } 427*86094694SAndrew Jeffery 428*86094694SAndrew Jeffery return 0; 429*86094694SAndrew Jeffery } 430*86094694SAndrew Jeffery 431*86094694SAndrew Jeffery if (dbus->pfds[DBUS_SOURCE_PFD_DBUS].revents) { 432*86094694SAndrew Jeffery if ((rc = sd_bus_process(dbus->bus, NULL)) < 0) { 433*86094694SAndrew Jeffery warnx("Failed processing inbound D-Bus messages: %s", 434*86094694SAndrew Jeffery strerror(-rc)); 435*86094694SAndrew Jeffery return rc; 436*86094694SAndrew Jeffery } 437*86094694SAndrew Jeffery } 438*86094694SAndrew Jeffery } 439*86094694SAndrew Jeffery } 440*86094694SAndrew Jeffery #else 441*86094694SAndrew Jeffery static void dbus_sink_debug(void *ctx) 442*86094694SAndrew Jeffery { 443*86094694SAndrew Jeffery warnx("%s: Configured without systemd, dbus sinks disabled", __func__); 444*86094694SAndrew Jeffery } 445*86094694SAndrew Jeffery 446*86094694SAndrew Jeffery static void dbus_sink_reboot(void *ctx) 447*86094694SAndrew Jeffery { 448*86094694SAndrew Jeffery warnx("%s: Configured without systemd, dbus sinks disabled", __func__); 449*86094694SAndrew Jeffery } 450*86094694SAndrew Jeffery 451*86094694SAndrew Jeffery static int dbus_source_poll(void *ctx, char *op) 452*86094694SAndrew Jeffery { 453*86094694SAndrew Jeffery errx(EXIT_FAILURE, "Configured without systemd, dbus sources disabled", __func__); 454*86094694SAndrew Jeffery } 455*86094694SAndrew Jeffery #endif 456*86094694SAndrew Jeffery 457*86094694SAndrew Jeffery const struct debug_sink_ops dbus_sink_ops = { 458*86094694SAndrew Jeffery .debug = dbus_sink_debug, 459*86094694SAndrew Jeffery .reboot = dbus_sink_reboot, 460*86094694SAndrew Jeffery }; 461*86094694SAndrew Jeffery 462*86094694SAndrew Jeffery const struct debug_source_ops dbus_source_ops = { 463*86094694SAndrew Jeffery .poll = dbus_source_poll, 464*86094694SAndrew Jeffery }; 465*86094694SAndrew Jeffery 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': 4741dc6adc9SAndrew Jeffery sink->ops->debug(sink->ctx); 475210ad636SAndrew Jeffery break; 476210ad636SAndrew Jeffery case 'R': 4771dc6adc9SAndrew Jeffery sink->ops->reboot(sink->ctx); 47811cd254bSAndrew Jeffery break; 47911cd254bSAndrew Jeffery default: 48011cd254bSAndrew Jeffery warnx("Unexpected command: 0x%02x (%c)", command, command); 48111cd254bSAndrew Jeffery } 48211cd254bSAndrew Jeffery } 48311cd254bSAndrew Jeffery 484b1ea254eSAndrew Jeffery if (rc < 0) 485b1ea254eSAndrew Jeffery warnx("Failed to poll source: %s", strerror(-rc)); 48611cd254bSAndrew Jeffery 487b1ea254eSAndrew Jeffery return rc; 48811cd254bSAndrew Jeffery } 48911cd254bSAndrew Jeffery 49011cd254bSAndrew Jeffery int main(int argc, char * const argv[]) 49111cd254bSAndrew Jeffery { 492*86094694SAndrew Jeffery struct debug_source_basic basic_source; 493*86094694SAndrew Jeffery struct debug_source_dbus dbus_source; 494*86094694SAndrew Jeffery struct debug_sink_sysrq sysrq_sink; 495*86094694SAndrew Jeffery struct debug_sink_dbus dbus_sink; 496e998ba77SAndrew Jeffery const char *sink_actions = NULL; 497b1ea254eSAndrew Jeffery struct debug_source source; 4981dc6adc9SAndrew Jeffery struct debug_sink sink; 49911cd254bSAndrew Jeffery char devnode[PATH_MAX]; 50011cd254bSAndrew Jeffery char *devid; 5011dc6adc9SAndrew Jeffery int sourcefd; 5021dc6adc9SAndrew Jeffery int sinkfd; 50311cd254bSAndrew Jeffery 504e998ba77SAndrew Jeffery /* Option processing */ 50511cd254bSAndrew Jeffery while (1) { 50611cd254bSAndrew Jeffery static struct option long_options[] = { 507e998ba77SAndrew Jeffery {"sink-actions", required_argument, 0, 's'}, 50811cd254bSAndrew Jeffery {0, 0, 0, 0}, 50911cd254bSAndrew Jeffery }; 51011cd254bSAndrew Jeffery int c; 51111cd254bSAndrew Jeffery 51211cd254bSAndrew Jeffery c = getopt_long(argc, argv, "", long_options, NULL); 51311cd254bSAndrew Jeffery if (c == -1) 51411cd254bSAndrew Jeffery break; 515e998ba77SAndrew Jeffery 516e998ba77SAndrew Jeffery switch (c) { 517e998ba77SAndrew Jeffery case 's': 518e998ba77SAndrew Jeffery sink_actions = optarg; 519e998ba77SAndrew Jeffery break; 520e998ba77SAndrew Jeffery default: 521e998ba77SAndrew Jeffery break; 522e998ba77SAndrew Jeffery } 52311cd254bSAndrew Jeffery } 52411cd254bSAndrew Jeffery 525d65368beSAndrew Jeffery /* 526e998ba77SAndrew Jeffery * The default behaviour sets the source file descriptor as stdin and the sink file 527e998ba77SAndrew Jeffery * descriptor as stdout. This allows trivial testing on the command-line with just a 528e998ba77SAndrew Jeffery * keyboard and without crashing the system. 529d65368beSAndrew Jeffery */ 5301dc6adc9SAndrew Jeffery sourcefd = 0; 5311dc6adc9SAndrew Jeffery sinkfd = 1; 53211cd254bSAndrew Jeffery 533e998ba77SAndrew Jeffery /* Handle the source path argument, if any */ 53411cd254bSAndrew Jeffery if (optind < argc) { 53511cd254bSAndrew Jeffery char devpath[PATH_MAX]; 53611cd254bSAndrew Jeffery 537d65368beSAndrew Jeffery /* 538d65368beSAndrew Jeffery * To make our lives easy with udev we take the basename of the source argument and 539d65368beSAndrew Jeffery * look for it in /dev. This allows us to use %p (the devpath specifier) in the udev 540d65368beSAndrew Jeffery * rule to pass the device of interest to the systemd unit. 541d65368beSAndrew Jeffery */ 54211cd254bSAndrew Jeffery strncpy(devpath, argv[optind], sizeof(devpath)); 54311cd254bSAndrew Jeffery devpath[PATH_MAX - 1] = '\0'; 54411cd254bSAndrew Jeffery devid = basename(devpath); 54511cd254bSAndrew Jeffery 54611cd254bSAndrew Jeffery strncpy(devnode, "/dev/", sizeof(devnode)); 54711cd254bSAndrew Jeffery strncat(devnode, devid, sizeof(devnode)); 54811cd254bSAndrew Jeffery devnode[PATH_MAX - 1] = '\0'; 54911cd254bSAndrew Jeffery 5501dc6adc9SAndrew Jeffery if ((sourcefd = open(devnode, O_RDONLY)) == -1) 55111cd254bSAndrew Jeffery err(EXIT_FAILURE, "Failed to open %s", devnode); 55211cd254bSAndrew Jeffery 55311cd254bSAndrew Jeffery optind++; 55411cd254bSAndrew Jeffery } 55511cd254bSAndrew Jeffery 556e998ba77SAndrew Jeffery /* 557e998ba77SAndrew Jeffery * Handle the sink path argument, if any. If sink_actions hasn't been set via the 558e998ba77SAndrew Jeffery * --sink-actions option, then default to 'sysrq'. Otherwise, if --sink-actions=sysrq has 559e998ba77SAndrew Jeffery * been passed, do as we're told and use the 'sysrq' sink actions. 560e998ba77SAndrew Jeffery */ 561e998ba77SAndrew Jeffery if (!sink_actions || !strcmp("sysrq", sink_actions)) { 56211cd254bSAndrew Jeffery if (optind < argc) { 563d65368beSAndrew Jeffery /* 564e998ba77SAndrew Jeffery * Just open the sink path directly. If we ever need different behaviour 565e998ba77SAndrew Jeffery * then we patch this bit when we know what we need. 566d65368beSAndrew Jeffery */ 5671dc6adc9SAndrew Jeffery if ((sinkfd = open(argv[optind], O_WRONLY)) == -1) 56811cd254bSAndrew Jeffery err(EXIT_FAILURE, "Failed to open %s", argv[optind]); 56911cd254bSAndrew Jeffery 57011cd254bSAndrew Jeffery optind++; 57111cd254bSAndrew Jeffery } 57211cd254bSAndrew Jeffery 573*86094694SAndrew Jeffery basic_source.source = sourcefd; 574*86094694SAndrew Jeffery source.ops = &basic_source_ops; 575*86094694SAndrew Jeffery source.ctx = &basic_source; 576*86094694SAndrew Jeffery 577*86094694SAndrew Jeffery sysrq_sink.sink = sinkfd; 5781dc6adc9SAndrew Jeffery sink.ops = &sysrq_sink_ops; 579*86094694SAndrew Jeffery sink.ctx = &sysrq_sink; 580*86094694SAndrew Jeffery } 581*86094694SAndrew Jeffery 582*86094694SAndrew Jeffery /* Set up the dbus sink actions if requested via --sink-actions=dbus */ 583*86094694SAndrew Jeffery if (sink_actions && !strcmp("dbus", sink_actions)) { 584*86094694SAndrew Jeffery sd_bus *bus; 585*86094694SAndrew Jeffery int rc; 586*86094694SAndrew Jeffery 587*86094694SAndrew Jeffery rc = sd_bus_open_system(&bus); 588*86094694SAndrew Jeffery if (rc < 0) { 589*86094694SAndrew Jeffery errx(EXIT_FAILURE, "Failed to connect to the system bus: %s", 590*86094694SAndrew Jeffery strerror(-rc)); 591*86094694SAndrew Jeffery } 592*86094694SAndrew Jeffery 593*86094694SAndrew Jeffery dbus_source.bus = bus; 594*86094694SAndrew Jeffery dbus_source.pfds[DBUS_SOURCE_PFD_SOURCE].fd = sourcefd; 595*86094694SAndrew Jeffery dbus_source.pfds[DBUS_SOURCE_PFD_SOURCE].events = POLLIN; 596*86094694SAndrew Jeffery source.ops = &dbus_source_ops; 597*86094694SAndrew Jeffery source.ctx = &dbus_source; 598*86094694SAndrew Jeffery 599*86094694SAndrew Jeffery dbus_sink.bus = bus; 600*86094694SAndrew Jeffery sink.ops = &dbus_sink_ops; 601*86094694SAndrew Jeffery sink.ctx = &dbus_sink; 602e998ba77SAndrew Jeffery } 603e998ba77SAndrew Jeffery 604e998ba77SAndrew Jeffery /* Check we're done with the command-line */ 605e998ba77SAndrew Jeffery if (optind < argc) 606*86094694SAndrew Jeffery errx(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind); 6071dc6adc9SAndrew Jeffery 608*86094694SAndrew Jeffery if (!(source.ops && source.ctx)) 609*86094694SAndrew Jeffery errx(EXIT_FAILURE, "Invalid source configuration"); 610*86094694SAndrew Jeffery 611*86094694SAndrew Jeffery if (!(sink.ops && sink.ctx)) 612*86094694SAndrew Jeffery errx(EXIT_FAILURE, "Unrecognised sink: %s", sink_actions); 613b1ea254eSAndrew Jeffery 614d65368beSAndrew Jeffery /* Trigger the actions on the sink when we receive an event from the source */ 615b1ea254eSAndrew Jeffery if (process(&source, &sink) < 0) 61611cd254bSAndrew Jeffery errx(EXIT_FAILURE, "Failure while processing command stream"); 61711cd254bSAndrew Jeffery 61811cd254bSAndrew Jeffery return 0; 61911cd254bSAndrew Jeffery } 620