blob: 8bfec4d6c2248d1a0abe275e9503f9416a151787 [file] [log] [blame]
Andrew Jeffery11cd2542021-05-03 11:03:30 +09301// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2021 IBM Corp.
3
Andrew Jefferyd65368b2022-01-19 15:59:20 +10304/*
Andrew Jefferye998ba72022-01-13 14:44:04 +10305 * debug-trigger listens for an external signal that the BMC is in some way unresponsive. When a
Andrew Jefferyd65368b2022-01-19 15:59:20 +10306 * signal is received it triggers a crash to collect debug data and reboots the system in the hope
7 * that it will recover.
8 *
9 * Usage: debug-trigger [SOURCE] [SINK]
10 *
Andrew Jefferye998ba72022-01-13 14:44:04 +103011 * Options:
12 * --sink-actions=ACTION
Andrew Jeffery86094692022-01-13 17:15:15 +103013 * Set the class of sink action(s) to be used. Can take the value of 'sysrq' or 'dbus'.
14 * Defaults to 'sysrq'.
Andrew Jefferye998ba72022-01-13 14:44:04 +103015 *
Andrew Jefferyd65368b2022-01-19 15:59:20 +103016 * Examples:
17 * debug-trigger
Andrew Jefferye998ba72022-01-13 14:44:04 +103018 * Set the source as stdin, the sink as stdout, and use the default 'sysrq' set of sink
19 * actions. Useful for testing.
20 *
21 * debug-trigger --sink-actions=sysrq
22 * Explicitly use the 'sysrq' set of sink actions with stdin as the source and stdout as the
23 * sink.
Andrew Jefferyd65368b2022-01-19 15:59:20 +103024 *
25 * debug-trigger /dev/serio_raw0 /proc/sysrq-trigger
Andrew Jefferye998ba72022-01-13 14:44:04 +103026 * Open /dev/serio_raw0 as the source and /proc/sysrq-trigger as the sink, with the default
27 * 'sysrq' set of sink actions. When 'D' is read from /dev/serio_raw0 'c' will be written to
28 * /proc/sysrq-trigger, causing a kernel panic. When 'R' is read from /dev/serio_raw0 'b' will
29 * be written to /proc/sysrq-trigger, causing an immediate reboot of the system.
Andrew Jeffery86094692022-01-13 17:15:15 +103030 *
31 * dbug-trigger --sink-actions=dbus /dev/serio_raw0
32 * Open /dev/serio_raw0 as the source and configure the 'dbus' set of sink actions. When 'D' is
33 * read from /dev/serio_raw0 create a dump via phosphor-debug-collector by calling through its
34 * D-Bus interface, then reboot the system by starting systemd's 'reboot.target'
Andrew Jefferyd65368b2022-01-19 15:59:20 +103035 */
Andrew Jeffery86094692022-01-13 17:15:15 +103036#define _GNU_SOURCE
37
38#include "config.h"
Andrew Jefferyd65368b2022-01-19 15:59:20 +103039
Andrew Jeffery11cd2542021-05-03 11:03:30 +093040#include <err.h>
Andrew Jefferyb1ea2542022-01-14 15:03:22 +103041#include <errno.h>
Andrew Jeffery11cd2542021-05-03 11:03:30 +093042#include <fcntl.h>
43#include <getopt.h>
44#include <libgen.h>
45#include <limits.h>
46#include <linux/reboot.h>
Andrew Jeffery86094692022-01-13 17:15:15 +103047#include <poll.h>
48#include <stdbool.h>
49#include <stdio.h>
Andrew Jeffery11cd2542021-05-03 11:03:30 +093050#include <stdlib.h>
51#include <string.h>
52#include <sys/reboot.h>
53#include <sys/stat.h>
54#include <sys/types.h>
55#include <unistd.h>
56
Andrew Jeffery86094692022-01-13 17:15:15 +103057#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
58
59struct sd_bus;
60
Andrew Jefferyb1ea2542022-01-14 15:03:22 +103061struct debug_source_ops {
62 int (*poll)(void *ctx, char *op);
63};
64
65struct debug_source {
66 const struct debug_source_ops *ops;
67 void *ctx;
68};
69
70struct debug_source_basic {
71 int source;
72};
73
Andrew Jeffery86094692022-01-13 17:15:15 +103074struct debug_source_dbus {
75 struct sd_bus *bus;
76#define DBUS_SOURCE_PFD_SOURCE 0
77#define DBUS_SOURCE_PFD_DBUS 1
78 struct pollfd pfds[2];
79};
80
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +103081struct debug_sink_ops {
82 void (*debug)(void *ctx);
83 void (*reboot)(void *ctx);
84};
85
86struct debug_sink {
87 const struct debug_sink_ops *ops;
88 void *ctx;
89};
90
91struct debug_sink_sysrq {
92 int sink;
93};
94
Andrew Jeffery86094692022-01-13 17:15:15 +103095struct debug_sink_dbus {
96 struct sd_bus *bus;
97};
98
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +103099static void sysrq_sink_debug(void *ctx)
Andrew Jefferydb47cd72022-01-13 14:14:41 +1030100{
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030101 struct debug_sink_sysrq *sysrq = ctx;
Andrew Jeffery30b64962022-01-19 16:58:28 +1030102 /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n93 */
Andrew Jefferydb47cd72022-01-13 14:14:41 +1030103 static const char action = 'c';
104 ssize_t rc;
105
106 sync();
107
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030108 if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action))
Andrew Jefferydb47cd72022-01-13 14:14:41 +1030109 return;
110
111 if (rc == -1) {
112 warn("Failed to execute debug command");
113 } else {
114 warnx("Failed to execute debug command: %zd", rc);
115 }
116}
117
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030118static void sysrq_sink_reboot(void *ctx)
Andrew Jeffery210ad632022-01-13 14:16:53 +1030119{
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030120 struct debug_sink_sysrq *sysrq = ctx;
Andrew Jeffery30b64962022-01-19 16:58:28 +1030121 /* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n90 */
122 static const char action = 'b';
Andrew Jeffery210ad632022-01-13 14:16:53 +1030123 ssize_t rc;
124
125 sync();
126
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030127 if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action))
Andrew Jeffery30b64962022-01-19 16:58:28 +1030128 return;
129
130 if (rc == -1) {
131 warn("Failed to reboot BMC");
132 } else {
133 warnx("Failed to reboot BMC: %zd", rc);
Andrew Jeffery210ad632022-01-13 14:16:53 +1030134 }
135}
136
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030137static int basic_source_poll(void *ctx, char *op)
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930138{
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030139 struct debug_source_basic *basic = ctx;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930140 ssize_t ingress;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930141
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030142 if ((ingress = read(basic->source, op, 1)) != 1) {
143 if (ingress < 0) {
144 warn("Failed to read from basic source");
145 return -errno;
146 }
147
148 /* Unreachable */
149 errx(EXIT_FAILURE, "Bad read, requested 1 got %zd", ingress);
150 }
151
152 return 0;
153}
154
Andrew Jeffery86094692022-01-13 17:15:15 +1030155const struct debug_sink_ops sysrq_sink_ops = {
156 .debug = sysrq_sink_debug,
157 .reboot = sysrq_sink_reboot,
158};
159
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030160const struct debug_source_ops basic_source_ops = {
161 .poll = basic_source_poll,
162};
163
Andrew Jeffery86094692022-01-13 17:15:15 +1030164#if HAVE_SYSTEMD
165#include <systemd/sd-bus.h>
166
167static void dbus_sink_reboot(void *ctx);
168static int dbus_sink_dump_progress(sd_bus_message *m, void *userdata,
169 sd_bus_error *ret_error __attribute__((unused)))
170{
171 struct debug_sink_dbus *dbus = userdata;
172 const char *status;
173 const char *iface;
174 int rc;
175
176 // sa{sv}as
177 rc = sd_bus_message_read_basic(m, 's', &iface);
178 if (rc < 0) {
179 warnx("Failed to extract interface from PropertiesChanged signal: %s",
180 strerror(-rc));
181 return rc;
182 }
183
184 /* Bail if it's not an update to the Progress interface */
185 if (strcmp(iface, "xyz.openbmc_project.Common.Progress"))
186 return 0;
187
188 rc = sd_bus_message_enter_container(m, 'a', "{sv}");
189 if (rc < 0)
190 return rc;
191
192 if (!rc)
193 return 0;
194
195 status = NULL;
196 while (1) {
197 const char *member;
198
199 rc = sd_bus_message_enter_container(m, 'e', "sv");
200 if (rc < 0)
201 return rc;
202
203 if (!rc)
204 break;
205
206 rc = sd_bus_message_read_basic(m, 's', &member);
207 if (rc < 0) {
208 warnx("Failed to extract member name from PropertiesChanged signal: %s",
209 strerror(-rc));
210 return rc;
211 }
212
213 if (!strcmp(member, "Status")) {
214 rc = sd_bus_message_enter_container(m, 'v', "s");
215 if (rc < 0) {
216 warnx("Failed to enter variant container in PropertiesChanged signal: %s",
217 strerror(-rc));
218 return rc;
219 }
220
221 if (!rc)
222 goto exit_dict_container;
223
224 rc = sd_bus_message_read_basic(m, 's', &status);
225 if (rc < 0) {
226 warnx("Failed to extract status value from PropertiesChanged signal: %s",
227 strerror(-rc));
228 return rc;
229 }
230
231 sd_bus_message_exit_container(m);
232 } else {
233 rc = sd_bus_message_skip(m, "v");
234 if (rc < 0) {
235 warnx("Failed to skip variant for unrecognised member %s in PropertiesChanged signal: %s",
236 member, strerror(-rc));
237 return rc;
238 }
239 }
240
241exit_dict_container:
242 sd_bus_message_exit_container(m);
243 }
244
245 sd_bus_message_exit_container(m);
246
247 if (!status)
248 return 0;
249
250 printf("Dump progress on %s: %s\n", sd_bus_message_get_path(m), status);
251
252 /* If we're finished with the dump, reboot the system */
253 if (!strcmp(status, "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")) {
254 sd_bus_slot *slot = sd_bus_get_current_slot(dbus->bus);
255 sd_bus_slot_unref(slot);
256 dbus_sink_reboot(userdata);
257 }
258
259 return 0;
260}
261
262static void dbus_sink_debug(void *ctx)
263{
264 sd_bus_error ret_error = SD_BUS_ERROR_NULL;
265 struct debug_sink_dbus *dbus = ctx;
266 sd_bus_message *reply;
267 sd_bus_slot *slot;
268 const char *path;
269 char *status;
270 int rc;
271
272 /* Start a BMC dump */
273 rc = sd_bus_call_method(dbus->bus,
274 "xyz.openbmc_project.Dump.Manager",
275 "/xyz/openbmc_project/dump/bmc",
276 "xyz.openbmc_project.Dump.Create",
277 "CreateDump",
278 &ret_error,
279 &reply, "a{sv}", 0);
280 if (rc < 0) {
281 warnx("Failed to call CreateDump: %s", strerror(-rc));
282 return;
283 }
284
285 /* Extract the dump path */
286 rc = sd_bus_message_read_basic(reply, 'o', &path);
287 if (rc < 0) {
288 warnx("Failed to extract dump object path: %s", strerror(-rc));
289 goto cleanup_reply;
290 }
291
292 /* Set up a match watching for completion of the dump */
293 rc = sd_bus_match_signal(dbus->bus,
294 &slot,
295 "xyz.openbmc_project.Dump.Manager",
296 path,
297 "org.freedesktop.DBus.Properties",
298 "PropertiesChanged",
299 dbus_sink_dump_progress,
300 ctx);
301 if (rc < 0) {
302 warnx("Failed to add signal match for progress status on dump object %s: %s",
303 path, strerror(-rc));
304 goto cleanup_reply;
305 }
306
307 /*
308 * Mark the slot as 'floating'. If a slot is _not_ marked as floating it holds a reference
309 * to the bus, and the bus will stay alive so long as the slot is referenced. If the slot is
310 * instead marked floating the relationship is inverted: The lifetime of the slot is defined
311 * in terms of the bus, which means we relieve ourselves of having to track the lifetime of
312 * the slot.
313 *
314 * For more details see `man 3 sd_bus_slot_set_floating`, also documented here:
315 *
316 * https://www.freedesktop.org/software/systemd/man/sd_bus_slot_set_floating.html
317 */
318 rc = sd_bus_slot_set_floating(slot, 0);
319 if (rc < 0) {
320 warnx("Failed to mark progress match slot on %s as floating: %s",
321 path, strerror(-rc));
322 goto cleanup_reply;
323 }
324
325 printf("Registered progress match on dump object %s\n", path);
326
327 /* Now that the match is set up, check the current value in case we missed any updates */
328 rc = sd_bus_get_property_string(dbus->bus,
329 "xyz.openbmc_project.Dump.Manager",
330 path,
331 "xyz.openbmc_project.Common.Progress",
332 "Status",
333 &ret_error,
334 &status);
335 if (rc < 0) {
336 warnx("Failed to get progress status property on dump object %s: %s",
337 path, strerror(-rc));
338 sd_bus_slot_unref(slot);
339 goto cleanup_reply;
340 }
341
342 printf("Dump state for %s is currently %s\n", path, status);
343
344 /*
345 * If we're finished with the dump, reboot the system. If the dump isn't finished the reboot
346 * will instead take place via the dbus_sink_dump_progress() callback on the match.
347 */
348 if (!strcmp(status, "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")) {
349 sd_bus_slot_unref(slot);
350 dbus_sink_reboot(ctx);
351 }
352
353cleanup_reply:
354 sd_bus_message_unref(reply);
355}
356
357static void dbus_sink_reboot(void *ctx)
358{
359 sd_bus_error ret_error = SD_BUS_ERROR_NULL;
360 struct debug_sink_dbus *dbus = ctx;
361 sd_bus_message *reply;
362 int rc;
363
364 warnx("Rebooting the system");
365
366 rc = sd_bus_call_method(dbus->bus,
367 "org.freedesktop.systemd1",
368 "/org/freedesktop/systemd1",
369 "org.freedesktop.systemd1.Manager",
370 "StartUnit",
371 &ret_error,
372 &reply,
373 "ss",
374 "reboot.target",
375 "replace-irreversibly");
376 if (rc < 0) {
377 warnx("Failed to start reboot.target: %s", strerror(-rc));
378 }
379}
380
381static int dbus_source_poll(void *ctx, char *op)
382{
383 struct debug_source_dbus *dbus = ctx;
384 int rc;
385
386 while (1) {
387 struct timespec tsto, *ptsto;
388 uint64_t dbusto;
389
390 /* See SD_BUS_GET_FD(3) */
391 dbus->pfds[DBUS_SOURCE_PFD_DBUS].fd = sd_bus_get_fd(dbus->bus);
392 dbus->pfds[DBUS_SOURCE_PFD_DBUS].events = sd_bus_get_events(dbus->bus);
393 rc = sd_bus_get_timeout(dbus->bus, &dbusto);
394 if (rc < 0)
395 return rc;
396
397 if (dbusto == UINT64_MAX) {
398 ptsto = NULL;
399 } else if (dbus->pfds[DBUS_SOURCE_PFD_DBUS].events == 0) {
400 ptsto = NULL;
401 } else {
402#define MSEC_PER_SEC 1000U
403#define USEC_PER_SEC (MSEC_PER_SEC * 1000U)
404#define NSEC_PER_SEC (USEC_PER_SEC * 1000U)
405#define NSEC_PER_USEC (NSEC_PER_SEC / USEC_PER_SEC)
406 tsto.tv_sec = dbusto / USEC_PER_SEC;
407 tsto.tv_nsec = (dbusto % USEC_PER_SEC) * NSEC_PER_USEC;
408 ptsto = &tsto;
409 }
410
411 if ((rc = ppoll(dbus->pfds, ARRAY_SIZE(dbus->pfds), ptsto, NULL)) < 0) {
412 warn("Failed polling source fds");
413 return -errno;
414 }
415
416 if (dbus->pfds[DBUS_SOURCE_PFD_SOURCE].revents) {
417 ssize_t ingress;
418
419 if ((ingress = read(dbus->pfds[DBUS_SOURCE_PFD_SOURCE].fd, op, 1)) != 1) {
420 if (ingress < 0) {
421 warn("Failed to read from basic source");
422 return -errno;
423 }
424
425 errx(EXIT_FAILURE, "Bad read, requested 1 got %zd", ingress);
426 }
427
428 return 0;
429 }
430
431 if (dbus->pfds[DBUS_SOURCE_PFD_DBUS].revents) {
432 if ((rc = sd_bus_process(dbus->bus, NULL)) < 0) {
433 warnx("Failed processing inbound D-Bus messages: %s",
434 strerror(-rc));
435 return rc;
436 }
437 }
438 }
439}
440#else
441static void dbus_sink_debug(void *ctx)
442{
443 warnx("%s: Configured without systemd, dbus sinks disabled", __func__);
444}
445
446static void dbus_sink_reboot(void *ctx)
447{
448 warnx("%s: Configured without systemd, dbus sinks disabled", __func__);
449}
450
451static int dbus_source_poll(void *ctx, char *op)
452{
453 errx(EXIT_FAILURE, "Configured without systemd, dbus sources disabled", __func__);
454}
455#endif
456
457const struct debug_sink_ops dbus_sink_ops = {
458 .debug = dbus_sink_debug,
459 .reboot = dbus_sink_reboot,
460};
461
462const struct debug_source_ops dbus_source_ops = {
463 .poll = dbus_source_poll,
464};
465
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030466static int process(struct debug_source *source, struct debug_sink *sink)
467{
468 char command;
469 int rc;
470
471 while (!(rc = source->ops->poll(source->ctx, &command))) {
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930472 switch (command) {
Andrew Jefferydb47cd72022-01-13 14:14:41 +1030473 case 'D':
Andrew Jeffery11616422022-01-14 22:57:16 +1030474 warnx("Debug action triggered\n");
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030475 sink->ops->debug(sink->ctx);
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930476 break;
477 case 'R':
Andrew Jeffery11616422022-01-14 22:57:16 +1030478 warnx("Reboot action triggered\n");
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030479 sink->ops->reboot(sink->ctx);
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930480 break;
481 default:
482 warnx("Unexpected command: 0x%02x (%c)", command, command);
483 }
484 }
485
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030486 if (rc < 0)
487 warnx("Failed to poll source: %s", strerror(-rc));
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930488
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030489 return rc;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930490}
491
492int main(int argc, char * const argv[])
493{
Andrew Jeffery86094692022-01-13 17:15:15 +1030494 struct debug_source_basic basic_source;
495 struct debug_source_dbus dbus_source;
496 struct debug_sink_sysrq sysrq_sink;
497 struct debug_sink_dbus dbus_sink;
Andrew Jefferye998ba72022-01-13 14:44:04 +1030498 const char *sink_actions = NULL;
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030499 struct debug_source source;
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030500 struct debug_sink sink;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930501 char devnode[PATH_MAX];
502 char *devid;
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030503 int sourcefd;
504 int sinkfd;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930505
Andrew Jefferye998ba72022-01-13 14:44:04 +1030506 /* Option processing */
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930507 while (1) {
508 static struct option long_options[] = {
Andrew Jefferye998ba72022-01-13 14:44:04 +1030509 {"sink-actions", required_argument, 0, 's'},
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930510 {0, 0, 0, 0},
511 };
512 int c;
513
514 c = getopt_long(argc, argv, "", long_options, NULL);
515 if (c == -1)
516 break;
Andrew Jefferye998ba72022-01-13 14:44:04 +1030517
518 switch (c) {
519 case 's':
520 sink_actions = optarg;
521 break;
522 default:
523 break;
524 }
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930525 }
526
Andrew Jefferyd65368b2022-01-19 15:59:20 +1030527 /*
Andrew Jefferye998ba72022-01-13 14:44:04 +1030528 * The default behaviour sets the source file descriptor as stdin and the sink file
529 * descriptor as stdout. This allows trivial testing on the command-line with just a
530 * keyboard and without crashing the system.
Andrew Jefferyd65368b2022-01-19 15:59:20 +1030531 */
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030532 sourcefd = 0;
533 sinkfd = 1;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930534
Andrew Jefferye998ba72022-01-13 14:44:04 +1030535 /* Handle the source path argument, if any */
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930536 if (optind < argc) {
537 char devpath[PATH_MAX];
538
Andrew Jefferyd65368b2022-01-19 15:59:20 +1030539 /*
540 * To make our lives easy with udev we take the basename of the source argument and
541 * look for it in /dev. This allows us to use %p (the devpath specifier) in the udev
542 * rule to pass the device of interest to the systemd unit.
543 */
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930544 strncpy(devpath, argv[optind], sizeof(devpath));
545 devpath[PATH_MAX - 1] = '\0';
546 devid = basename(devpath);
547
548 strncpy(devnode, "/dev/", sizeof(devnode));
549 strncat(devnode, devid, sizeof(devnode));
550 devnode[PATH_MAX - 1] = '\0';
551
Andrew Jeffery1dc6adc2022-01-13 14:34:37 +1030552 if ((sourcefd = open(devnode, O_RDONLY)) == -1)
Andrew Jefferyea84ad92022-01-14 22:07:30 +1030553 err(EXIT_FAILURE, "Failed to open source %s", devnode);
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930554
555 optind++;
556 }
557
Andrew Jefferye998ba72022-01-13 14:44:04 +1030558 /*
559 * Handle the sink path argument, if any. If sink_actions hasn't been set via the
560 * --sink-actions option, then default to 'sysrq'. Otherwise, if --sink-actions=sysrq has
561 * been passed, do as we're told and use the 'sysrq' sink actions.
562 */
563 if (!sink_actions || !strcmp("sysrq", sink_actions)) {
564 if (optind < argc) {
565 /*
566 * Just open the sink path directly. If we ever need different behaviour
567 * then we patch this bit when we know what we need.
568 */
569 if ((sinkfd = open(argv[optind], O_WRONLY)) == -1)
Andrew Jefferyea84ad92022-01-14 22:07:30 +1030570 err(EXIT_FAILURE, "Failed to open sink %s", argv[optind]);
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930571
Andrew Jefferye998ba72022-01-13 14:44:04 +1030572 optind++;
573 }
574
Andrew Jeffery86094692022-01-13 17:15:15 +1030575 basic_source.source = sourcefd;
576 source.ops = &basic_source_ops;
577 source.ctx = &basic_source;
578
579 sysrq_sink.sink = sinkfd;
Andrew Jefferye998ba72022-01-13 14:44:04 +1030580 sink.ops = &sysrq_sink_ops;
Andrew Jeffery86094692022-01-13 17:15:15 +1030581 sink.ctx = &sysrq_sink;
582 }
583
584 /* Set up the dbus sink actions if requested via --sink-actions=dbus */
585 if (sink_actions && !strcmp("dbus", sink_actions)) {
586 sd_bus *bus;
587 int rc;
588
589 rc = sd_bus_open_system(&bus);
590 if (rc < 0) {
591 errx(EXIT_FAILURE, "Failed to connect to the system bus: %s",
592 strerror(-rc));
593 }
594
595 dbus_source.bus = bus;
596 dbus_source.pfds[DBUS_SOURCE_PFD_SOURCE].fd = sourcefd;
597 dbus_source.pfds[DBUS_SOURCE_PFD_SOURCE].events = POLLIN;
598 source.ops = &dbus_source_ops;
599 source.ctx = &dbus_source;
600
601 dbus_sink.bus = bus;
602 sink.ops = &dbus_sink_ops;
603 sink.ctx = &dbus_sink;
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930604 }
605
Andrew Jefferyd65368b2022-01-19 15:59:20 +1030606 /* Check we're done with the command-line */
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930607 if (optind < argc)
Andrew Jeffery86094692022-01-13 17:15:15 +1030608 errx(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind);
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930609
Andrew Jeffery86094692022-01-13 17:15:15 +1030610 if (!(source.ops && source.ctx))
611 errx(EXIT_FAILURE, "Invalid source configuration");
612
613 if (!(sink.ops && sink.ctx))
614 errx(EXIT_FAILURE, "Unrecognised sink: %s", sink_actions);
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030615
Andrew Jefferyd65368b2022-01-19 15:59:20 +1030616 /* Trigger the actions on the sink when we receive an event from the source */
Andrew Jefferyb1ea2542022-01-14 15:03:22 +1030617 if (process(&source, &sink) < 0)
Andrew Jeffery11cd2542021-05-03 11:03:30 +0930618 errx(EXIT_FAILURE, "Failure while processing command stream");
619
620 return 0;
621}