111cd254bSAndrew Jeffery // SPDX-License-Identifier: Apache-2.0 211cd254bSAndrew Jeffery // Copyright (C) 2021 IBM Corp. 311cd254bSAndrew Jeffery 4d65368beSAndrew Jeffery /* 5d65368beSAndrew Jeffery * debug-trigger listens for an external signal that the BMC is in some way unresponsive. When the 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 * 11d65368beSAndrew Jeffery * Examples: 12d65368beSAndrew Jeffery * debug-trigger 13d65368beSAndrew Jeffery * Set the source as stdin and the sink as stdout. Useful for testing. 14d65368beSAndrew Jeffery * 15d65368beSAndrew Jeffery * debug-trigger /dev/serio_raw0 /proc/sysrq-trigger 16d65368beSAndrew Jeffery * Open /dev/serio_raw0 as the source and /proc/sysrq-trigger as the sink. 17d65368beSAndrew Jeffery */ 18d65368beSAndrew Jeffery 1911cd254bSAndrew Jeffery #include <err.h> 2011cd254bSAndrew Jeffery #include <fcntl.h> 2111cd254bSAndrew Jeffery #include <getopt.h> 2211cd254bSAndrew Jeffery #include <libgen.h> 2311cd254bSAndrew Jeffery #include <limits.h> 2411cd254bSAndrew Jeffery #include <linux/reboot.h> 2511cd254bSAndrew Jeffery #include <stdlib.h> 2611cd254bSAndrew Jeffery #include <string.h> 2711cd254bSAndrew Jeffery #include <sys/reboot.h> 2811cd254bSAndrew Jeffery #include <sys/stat.h> 2911cd254bSAndrew Jeffery #include <sys/types.h> 3011cd254bSAndrew Jeffery #include <unistd.h> 3111cd254bSAndrew Jeffery 32*1dc6adc9SAndrew Jeffery struct debug_sink_ops { 33*1dc6adc9SAndrew Jeffery void (*debug)(void *ctx); 34*1dc6adc9SAndrew Jeffery void (*reboot)(void *ctx); 35*1dc6adc9SAndrew Jeffery }; 36*1dc6adc9SAndrew Jeffery 37*1dc6adc9SAndrew Jeffery struct debug_sink { 38*1dc6adc9SAndrew Jeffery const struct debug_sink_ops *ops; 39*1dc6adc9SAndrew Jeffery void *ctx; 40*1dc6adc9SAndrew Jeffery }; 41*1dc6adc9SAndrew Jeffery 42*1dc6adc9SAndrew Jeffery struct debug_sink_sysrq { 43*1dc6adc9SAndrew Jeffery int sink; 44*1dc6adc9SAndrew Jeffery }; 45*1dc6adc9SAndrew Jeffery 46*1dc6adc9SAndrew Jeffery static void sysrq_sink_debug(void *ctx) 47db47cd7fSAndrew Jeffery { 48*1dc6adc9SAndrew Jeffery struct debug_sink_sysrq *sysrq = ctx; 4930b6496aSAndrew Jeffery /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n93 */ 50db47cd7fSAndrew Jeffery static const char action = 'c'; 51db47cd7fSAndrew Jeffery ssize_t rc; 52db47cd7fSAndrew Jeffery 53db47cd7fSAndrew Jeffery sync(); 54db47cd7fSAndrew Jeffery 55*1dc6adc9SAndrew Jeffery if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action)) 56db47cd7fSAndrew Jeffery return; 57db47cd7fSAndrew Jeffery 58db47cd7fSAndrew Jeffery if (rc == -1) { 59db47cd7fSAndrew Jeffery warn("Failed to execute debug command"); 60db47cd7fSAndrew Jeffery } else { 61db47cd7fSAndrew Jeffery warnx("Failed to execute debug command: %zd", rc); 62db47cd7fSAndrew Jeffery } 63db47cd7fSAndrew Jeffery } 64db47cd7fSAndrew Jeffery 65*1dc6adc9SAndrew Jeffery static void sysrq_sink_reboot(void *ctx) 6611cd254bSAndrew Jeffery { 67*1dc6adc9SAndrew Jeffery struct debug_sink_sysrq *sysrq = ctx; 6830b6496aSAndrew Jeffery /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n90 */ 6930b6496aSAndrew Jeffery static const char action = 'b'; 7011cd254bSAndrew Jeffery ssize_t rc; 7111cd254bSAndrew Jeffery 7211cd254bSAndrew Jeffery sync(); 7311cd254bSAndrew Jeffery 74*1dc6adc9SAndrew Jeffery if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action)) 7530b6496aSAndrew Jeffery return; 7630b6496aSAndrew Jeffery 7730b6496aSAndrew Jeffery if (rc == -1) { 7811cd254bSAndrew Jeffery warn("Failed to reboot BMC"); 7930b6496aSAndrew Jeffery } else { 8011cd254bSAndrew Jeffery warnx("Failed to reboot BMC: %zd", rc); 8111cd254bSAndrew Jeffery } 82210ad636SAndrew Jeffery } 83210ad636SAndrew Jeffery 84*1dc6adc9SAndrew Jeffery const struct debug_sink_ops sysrq_sink_ops = { 85*1dc6adc9SAndrew Jeffery .debug = sysrq_sink_debug, 86*1dc6adc9SAndrew Jeffery .reboot = sysrq_sink_reboot, 87*1dc6adc9SAndrew Jeffery }; 88*1dc6adc9SAndrew Jeffery 89*1dc6adc9SAndrew Jeffery static int process(int source, struct debug_sink *sink) 90210ad636SAndrew Jeffery { 91210ad636SAndrew Jeffery ssize_t ingress; 92210ad636SAndrew Jeffery char command; 93210ad636SAndrew Jeffery 94210ad636SAndrew Jeffery while ((ingress = read(source, &command, sizeof(command))) == sizeof(command)) { 95210ad636SAndrew Jeffery switch (command) { 96210ad636SAndrew Jeffery case 'D': 97*1dc6adc9SAndrew Jeffery sink->ops->debug(sink->ctx); 98210ad636SAndrew Jeffery break; 99210ad636SAndrew Jeffery case 'R': 100*1dc6adc9SAndrew Jeffery sink->ops->reboot(sink->ctx); 10111cd254bSAndrew Jeffery break; 10211cd254bSAndrew Jeffery default: 10311cd254bSAndrew Jeffery warnx("Unexpected command: 0x%02x (%c)", command, command); 10411cd254bSAndrew Jeffery } 10511cd254bSAndrew Jeffery } 10611cd254bSAndrew Jeffery 10711cd254bSAndrew Jeffery if (ingress == -1) 10811cd254bSAndrew Jeffery warn("Failed to read from source"); 10911cd254bSAndrew Jeffery 11011cd254bSAndrew Jeffery return ingress; 11111cd254bSAndrew Jeffery } 11211cd254bSAndrew Jeffery 11311cd254bSAndrew Jeffery int main(int argc, char * const argv[]) 11411cd254bSAndrew Jeffery { 115*1dc6adc9SAndrew Jeffery struct debug_sink_sysrq sysrq; 116*1dc6adc9SAndrew Jeffery struct debug_sink sink; 11711cd254bSAndrew Jeffery char devnode[PATH_MAX]; 11811cd254bSAndrew Jeffery char *devid; 119*1dc6adc9SAndrew Jeffery int sourcefd; 120*1dc6adc9SAndrew Jeffery int sinkfd; 12111cd254bSAndrew Jeffery 122d65368beSAndrew Jeffery /* Option processing. Currently nothing implemented, but allows us to use optind */ 12311cd254bSAndrew Jeffery while (1) { 12411cd254bSAndrew Jeffery static struct option long_options[] = { 12511cd254bSAndrew Jeffery {0, 0, 0, 0}, 12611cd254bSAndrew Jeffery }; 12711cd254bSAndrew Jeffery int c; 12811cd254bSAndrew Jeffery 12911cd254bSAndrew Jeffery c = getopt_long(argc, argv, "", long_options, NULL); 13011cd254bSAndrew Jeffery if (c == -1) 13111cd254bSAndrew Jeffery break; 13211cd254bSAndrew Jeffery } 13311cd254bSAndrew Jeffery 134d65368beSAndrew Jeffery /* 135d65368beSAndrew Jeffery * The default behaviour sets the source as stdin and the sink as stdout. This allows 136d65368beSAndrew Jeffery * trivial testing on the command-line with just a keyboard and without crashing the system. 137d65368beSAndrew Jeffery */ 138*1dc6adc9SAndrew Jeffery sourcefd = 0; 139*1dc6adc9SAndrew Jeffery sinkfd = 1; 14011cd254bSAndrew Jeffery 141d65368beSAndrew Jeffery /* Handle the source argument, if any */ 14211cd254bSAndrew Jeffery if (optind < argc) { 14311cd254bSAndrew Jeffery char devpath[PATH_MAX]; 14411cd254bSAndrew Jeffery 145d65368beSAndrew Jeffery /* 146d65368beSAndrew Jeffery * To make our lives easy with udev we take the basename of the source argument and 147d65368beSAndrew Jeffery * look for it in /dev. This allows us to use %p (the devpath specifier) in the udev 148d65368beSAndrew Jeffery * rule to pass the device of interest to the systemd unit. 149d65368beSAndrew Jeffery */ 15011cd254bSAndrew Jeffery strncpy(devpath, argv[optind], sizeof(devpath)); 15111cd254bSAndrew Jeffery devpath[PATH_MAX - 1] = '\0'; 15211cd254bSAndrew Jeffery devid = basename(devpath); 15311cd254bSAndrew Jeffery 15411cd254bSAndrew Jeffery strncpy(devnode, "/dev/", sizeof(devnode)); 15511cd254bSAndrew Jeffery strncat(devnode, devid, sizeof(devnode)); 15611cd254bSAndrew Jeffery devnode[PATH_MAX - 1] = '\0'; 15711cd254bSAndrew Jeffery 158*1dc6adc9SAndrew Jeffery if ((sourcefd = open(devnode, O_RDONLY)) == -1) 15911cd254bSAndrew Jeffery err(EXIT_FAILURE, "Failed to open %s", devnode); 16011cd254bSAndrew Jeffery 16111cd254bSAndrew Jeffery optind++; 16211cd254bSAndrew Jeffery } 16311cd254bSAndrew Jeffery 164d65368beSAndrew Jeffery /* Handle the sink argument, if any */ 16511cd254bSAndrew Jeffery if (optind < argc) { 166d65368beSAndrew Jeffery /* 167d65368beSAndrew Jeffery * Just open the sink path directly. If we ever need different behaviour then we 168d65368beSAndrew Jeffery * patch this bit when we know what we need. 169d65368beSAndrew Jeffery */ 170*1dc6adc9SAndrew Jeffery if ((sinkfd = open(argv[optind], O_WRONLY)) == -1) 17111cd254bSAndrew Jeffery err(EXIT_FAILURE, "Failed to open %s", argv[optind]); 17211cd254bSAndrew Jeffery 17311cd254bSAndrew Jeffery optind++; 17411cd254bSAndrew Jeffery } 17511cd254bSAndrew Jeffery 176d65368beSAndrew Jeffery /* Check we're done with the command-line */ 17711cd254bSAndrew Jeffery if (optind < argc) 17811cd254bSAndrew Jeffery err(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind); 17911cd254bSAndrew Jeffery 180*1dc6adc9SAndrew Jeffery sysrq.sink = sinkfd; 181*1dc6adc9SAndrew Jeffery sink.ops = &sysrq_sink_ops; 182*1dc6adc9SAndrew Jeffery sink.ctx = &sysrq; 183*1dc6adc9SAndrew Jeffery 184d65368beSAndrew Jeffery /* Trigger the actions on the sink when we receive an event from the source */ 185*1dc6adc9SAndrew Jeffery if (process(sourcefd, &sink) < 0) 18611cd254bSAndrew Jeffery errx(EXIT_FAILURE, "Failure while processing command stream"); 18711cd254bSAndrew Jeffery 18811cd254bSAndrew Jeffery return 0; 18911cd254bSAndrew Jeffery } 190