111cd254bSAndrew Jeffery // SPDX-License-Identifier: Apache-2.0 211cd254bSAndrew Jeffery // Copyright (C) 2021 IBM Corp. 311cd254bSAndrew Jeffery 4*d65368beSAndrew Jeffery /* 5*d65368beSAndrew Jeffery * debug-trigger listens for an external signal that the BMC is in some way unresponsive. When the 6*d65368beSAndrew Jeffery * signal is received it triggers a crash to collect debug data and reboots the system in the hope 7*d65368beSAndrew Jeffery * that it will recover. 8*d65368beSAndrew Jeffery * 9*d65368beSAndrew Jeffery * Usage: debug-trigger [SOURCE] [SINK] 10*d65368beSAndrew Jeffery * 11*d65368beSAndrew Jeffery * Examples: 12*d65368beSAndrew Jeffery * debug-trigger 13*d65368beSAndrew Jeffery * Set the source as stdin and the sink as stdout. Useful for testing. 14*d65368beSAndrew Jeffery * 15*d65368beSAndrew Jeffery * debug-trigger /dev/serio_raw0 /proc/sysrq-trigger 16*d65368beSAndrew Jeffery * Open /dev/serio_raw0 as the source and /proc/sysrq-trigger as the sink. 17*d65368beSAndrew Jeffery */ 18*d65368beSAndrew 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 32db47cd7fSAndrew Jeffery static void process_debug(int sink) 33db47cd7fSAndrew Jeffery { 3430b6496aSAndrew Jeffery /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n93 */ 35db47cd7fSAndrew Jeffery static const char action = 'c'; 36db47cd7fSAndrew Jeffery ssize_t rc; 37db47cd7fSAndrew Jeffery 38db47cd7fSAndrew Jeffery sync(); 39db47cd7fSAndrew Jeffery 40db47cd7fSAndrew Jeffery if ((rc = write(sink, &action, sizeof(action))) == sizeof(action)) 41db47cd7fSAndrew Jeffery return; 42db47cd7fSAndrew Jeffery 43db47cd7fSAndrew Jeffery if (rc == -1) { 44db47cd7fSAndrew Jeffery warn("Failed to execute debug command"); 45db47cd7fSAndrew Jeffery } else { 46db47cd7fSAndrew Jeffery warnx("Failed to execute debug command: %zd", rc); 47db47cd7fSAndrew Jeffery } 48db47cd7fSAndrew Jeffery } 49db47cd7fSAndrew Jeffery 5030b6496aSAndrew Jeffery static void process_reboot(int sink) 5111cd254bSAndrew Jeffery { 5230b6496aSAndrew Jeffery /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n90 */ 5330b6496aSAndrew Jeffery static const char action = 'b'; 5411cd254bSAndrew Jeffery ssize_t rc; 5511cd254bSAndrew Jeffery 5611cd254bSAndrew Jeffery sync(); 5711cd254bSAndrew Jeffery 5830b6496aSAndrew Jeffery if ((rc = write(sink, &action, sizeof(action))) == sizeof(action)) 5930b6496aSAndrew Jeffery return; 6030b6496aSAndrew Jeffery 6130b6496aSAndrew Jeffery if (rc == -1) { 6211cd254bSAndrew Jeffery warn("Failed to reboot BMC"); 6330b6496aSAndrew Jeffery } else { 6411cd254bSAndrew Jeffery warnx("Failed to reboot BMC: %zd", rc); 6511cd254bSAndrew Jeffery } 66210ad636SAndrew Jeffery } 67210ad636SAndrew Jeffery 68210ad636SAndrew Jeffery static int process(int source, int sink) 69210ad636SAndrew Jeffery { 70210ad636SAndrew Jeffery ssize_t ingress; 71210ad636SAndrew Jeffery char command; 72210ad636SAndrew Jeffery 73210ad636SAndrew Jeffery while ((ingress = read(source, &command, sizeof(command))) == sizeof(command)) { 74210ad636SAndrew Jeffery switch (command) { 75210ad636SAndrew Jeffery case 'D': 76210ad636SAndrew Jeffery process_debug(sink); 77210ad636SAndrew Jeffery break; 78210ad636SAndrew Jeffery case 'R': 79210ad636SAndrew Jeffery process_reboot(sink); 8011cd254bSAndrew Jeffery break; 8111cd254bSAndrew Jeffery default: 8211cd254bSAndrew Jeffery warnx("Unexpected command: 0x%02x (%c)", command, command); 8311cd254bSAndrew Jeffery } 8411cd254bSAndrew Jeffery } 8511cd254bSAndrew Jeffery 8611cd254bSAndrew Jeffery if (ingress == -1) 8711cd254bSAndrew Jeffery warn("Failed to read from source"); 8811cd254bSAndrew Jeffery 8911cd254bSAndrew Jeffery return ingress; 9011cd254bSAndrew Jeffery } 9111cd254bSAndrew Jeffery 9211cd254bSAndrew Jeffery int main(int argc, char * const argv[]) 9311cd254bSAndrew Jeffery { 9411cd254bSAndrew Jeffery char devnode[PATH_MAX]; 9511cd254bSAndrew Jeffery char *devid; 9611cd254bSAndrew Jeffery int source; 9711cd254bSAndrew Jeffery int sink; 9811cd254bSAndrew Jeffery 99*d65368beSAndrew Jeffery /* Option processing. Currently nothing implemented, but allows us to use optind */ 10011cd254bSAndrew Jeffery while (1) { 10111cd254bSAndrew Jeffery static struct option long_options[] = { 10211cd254bSAndrew Jeffery {0, 0, 0, 0}, 10311cd254bSAndrew Jeffery }; 10411cd254bSAndrew Jeffery int c; 10511cd254bSAndrew Jeffery 10611cd254bSAndrew Jeffery c = getopt_long(argc, argv, "", long_options, NULL); 10711cd254bSAndrew Jeffery if (c == -1) 10811cd254bSAndrew Jeffery break; 10911cd254bSAndrew Jeffery } 11011cd254bSAndrew Jeffery 111*d65368beSAndrew Jeffery /* 112*d65368beSAndrew Jeffery * The default behaviour sets the source as stdin and the sink as stdout. This allows 113*d65368beSAndrew Jeffery * trivial testing on the command-line with just a keyboard and without crashing the system. 114*d65368beSAndrew Jeffery */ 11511cd254bSAndrew Jeffery source = 0; 11611cd254bSAndrew Jeffery sink = 1; 11711cd254bSAndrew Jeffery 118*d65368beSAndrew Jeffery /* Handle the source argument, if any */ 11911cd254bSAndrew Jeffery if (optind < argc) { 12011cd254bSAndrew Jeffery char devpath[PATH_MAX]; 12111cd254bSAndrew Jeffery 122*d65368beSAndrew Jeffery /* 123*d65368beSAndrew Jeffery * To make our lives easy with udev we take the basename of the source argument and 124*d65368beSAndrew Jeffery * look for it in /dev. This allows us to use %p (the devpath specifier) in the udev 125*d65368beSAndrew Jeffery * rule to pass the device of interest to the systemd unit. 126*d65368beSAndrew Jeffery */ 12711cd254bSAndrew Jeffery strncpy(devpath, argv[optind], sizeof(devpath)); 12811cd254bSAndrew Jeffery devpath[PATH_MAX - 1] = '\0'; 12911cd254bSAndrew Jeffery devid = basename(devpath); 13011cd254bSAndrew Jeffery 13111cd254bSAndrew Jeffery strncpy(devnode, "/dev/", sizeof(devnode)); 13211cd254bSAndrew Jeffery strncat(devnode, devid, sizeof(devnode)); 13311cd254bSAndrew Jeffery devnode[PATH_MAX - 1] = '\0'; 13411cd254bSAndrew Jeffery 13511cd254bSAndrew Jeffery if ((source = open(devnode, O_RDONLY)) == -1) 13611cd254bSAndrew Jeffery err(EXIT_FAILURE, "Failed to open %s", devnode); 13711cd254bSAndrew Jeffery 13811cd254bSAndrew Jeffery optind++; 13911cd254bSAndrew Jeffery } 14011cd254bSAndrew Jeffery 141*d65368beSAndrew Jeffery /* Handle the sink argument, if any */ 14211cd254bSAndrew Jeffery if (optind < argc) { 143*d65368beSAndrew Jeffery /* 144*d65368beSAndrew Jeffery * Just open the sink path directly. If we ever need different behaviour then we 145*d65368beSAndrew Jeffery * patch this bit when we know what we need. 146*d65368beSAndrew Jeffery */ 14711cd254bSAndrew Jeffery if ((sink = open(argv[optind], O_WRONLY)) == -1) 14811cd254bSAndrew Jeffery err(EXIT_FAILURE, "Failed to open %s", argv[optind]); 14911cd254bSAndrew Jeffery 15011cd254bSAndrew Jeffery optind++; 15111cd254bSAndrew Jeffery } 15211cd254bSAndrew Jeffery 153*d65368beSAndrew Jeffery /* Check we're done with the command-line */ 15411cd254bSAndrew Jeffery if (optind < argc) 15511cd254bSAndrew Jeffery err(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind); 15611cd254bSAndrew Jeffery 157*d65368beSAndrew Jeffery /* Trigger the actions on the sink when we receive an event from the source */ 15811cd254bSAndrew Jeffery if (process(source, sink) < 0) 15911cd254bSAndrew Jeffery errx(EXIT_FAILURE, "Failure while processing command stream"); 16011cd254bSAndrew Jeffery 16111cd254bSAndrew Jeffery return 0; 16211cd254bSAndrew Jeffery } 163