main: Extract a 'sink' abstraction
In the future we'll add a set of sink actions that call through D-Bus to
generate a BMC dump and do a graceful reboot rather than crashing the
system through /proc/sysrq-trigger. Reorganise the code so the process()
function operates on abstract sink types. This way we can encapsulate
the D-Bus specifics in a way that doesn't affect the process()
implementation.
Concretely, there are two actions that can be triggered by the source,
'debug' and 'reboot'. Add the same as callbacks on the sink structure
and implement the 'sysrq' behaviours in terms of the new abstraction.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: Ic62348365e9d0f13abbdcd81848bc4ca344135ee
diff --git a/main.c b/main.c
index 871da1d..c3408c0 100644
--- a/main.c
+++ b/main.c
@@ -29,15 +29,30 @@
#include <sys/types.h>
#include <unistd.h>
-static void process_debug(int sink)
+struct debug_sink_ops {
+ void (*debug)(void *ctx);
+ void (*reboot)(void *ctx);
+};
+
+struct debug_sink {
+ const struct debug_sink_ops *ops;
+ void *ctx;
+};
+
+struct debug_sink_sysrq {
+ int sink;
+};
+
+static void sysrq_sink_debug(void *ctx)
{
+ struct debug_sink_sysrq *sysrq = ctx;
/* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n93 */
static const char action = 'c';
ssize_t rc;
sync();
- if ((rc = write(sink, &action, sizeof(action))) == sizeof(action))
+ if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action))
return;
if (rc == -1) {
@@ -47,15 +62,16 @@
}
}
-static void process_reboot(int sink)
+static void sysrq_sink_reboot(void *ctx)
{
+ struct debug_sink_sysrq *sysrq = ctx;
/* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/sysrq.rst?h=v5.16#n90 */
static const char action = 'b';
ssize_t rc;
sync();
- if ((rc = write(sink, &action, sizeof(action))) == sizeof(action))
+ if ((rc = write(sysrq->sink, &action, sizeof(action))) == sizeof(action))
return;
if (rc == -1) {
@@ -65,7 +81,12 @@
}
}
-static int process(int source, int sink)
+const struct debug_sink_ops sysrq_sink_ops = {
+ .debug = sysrq_sink_debug,
+ .reboot = sysrq_sink_reboot,
+};
+
+static int process(int source, struct debug_sink *sink)
{
ssize_t ingress;
char command;
@@ -73,10 +94,10 @@
while ((ingress = read(source, &command, sizeof(command))) == sizeof(command)) {
switch (command) {
case 'D':
- process_debug(sink);
+ sink->ops->debug(sink->ctx);
break;
case 'R':
- process_reboot(sink);
+ sink->ops->reboot(sink->ctx);
break;
default:
warnx("Unexpected command: 0x%02x (%c)", command, command);
@@ -91,10 +112,12 @@
int main(int argc, char * const argv[])
{
+ struct debug_sink_sysrq sysrq;
+ struct debug_sink sink;
char devnode[PATH_MAX];
char *devid;
- int source;
- int sink;
+ int sourcefd;
+ int sinkfd;
/* Option processing. Currently nothing implemented, but allows us to use optind */
while (1) {
@@ -112,8 +135,8 @@
* The default behaviour sets the source as stdin and the sink as stdout. This allows
* trivial testing on the command-line with just a keyboard and without crashing the system.
*/
- source = 0;
- sink = 1;
+ sourcefd = 0;
+ sinkfd = 1;
/* Handle the source argument, if any */
if (optind < argc) {
@@ -132,7 +155,7 @@
strncat(devnode, devid, sizeof(devnode));
devnode[PATH_MAX - 1] = '\0';
- if ((source = open(devnode, O_RDONLY)) == -1)
+ if ((sourcefd = open(devnode, O_RDONLY)) == -1)
err(EXIT_FAILURE, "Failed to open %s", devnode);
optind++;
@@ -144,7 +167,7 @@
* Just open the sink path directly. If we ever need different behaviour then we
* patch this bit when we know what we need.
*/
- if ((sink = open(argv[optind], O_WRONLY)) == -1)
+ if ((sinkfd = open(argv[optind], O_WRONLY)) == -1)
err(EXIT_FAILURE, "Failed to open %s", argv[optind]);
optind++;
@@ -154,8 +177,12 @@
if (optind < argc)
err(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind);
+ sysrq.sink = sinkfd;
+ sink.ops = &sysrq_sink_ops;
+ sink.ctx = &sysrq;
+
/* Trigger the actions on the sink when we receive an event from the source */
- if (process(source, sink) < 0)
+ if (process(sourcefd, &sink) < 0)
errx(EXIT_FAILURE, "Failure while processing command stream");
return 0;