From 429bc68e90cb522d89bdf59eccbaf9faf3021d51 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Thu, 17 Jul 2025 23:03:59 -0400 Subject: usr.bin: Add 'nerve' program to poke ctlfs The 'nerve' program allows users to poke at a ctlfs entry and write their own data. Signed-off-by: Ian Moffett --- usr.bin/Makefile | 1 + usr.bin/nerve/Makefile | 6 ++ usr.bin/nerve/nerve.c | 274 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 usr.bin/nerve/Makefile create mode 100644 usr.bin/nerve/nerve.c diff --git a/usr.bin/Makefile b/usr.bin/Makefile index f30905e..802b773 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -21,3 +21,4 @@ all: make -C login/ $(ARGS) make -C sleep/ $(ARGS) make -C kstat/ $(ARGS) + make -C nerve/ $(ARGS) diff --git a/usr.bin/nerve/Makefile b/usr.bin/nerve/Makefile new file mode 100644 index 0000000..cc0fd91 --- /dev/null +++ b/usr.bin/nerve/Makefile @@ -0,0 +1,6 @@ +include user.mk + +CFILES = $(shell find . -name "*.c") + +$(ROOT)/base/usr/bin/nerve: + gcc $(CFILES) -o $@ $(INTERNAL_CFLAGS) diff --git a/usr.bin/nerve/nerve.c b/usr.bin/nerve/nerve.c new file mode 100644 index 0000000..d79c4cb --- /dev/null +++ b/usr.bin/nerve/nerve.c @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Verb numeric defs (see string defs) */ +#define VERB_UNKNOWN -1 +#define VERB_POKE 0x0000 + +/* Verb string defs (see numeric defs) */ +#define SVERB_POKE "poke" + +/* Nerve numeric defs (see string defs) */ +#define NERVE_UNKNOWN -1 +#define NERVE_CONSATTR 0x0000 + +/* Nerve string defs (see numeric defs) */ +#define SNERVE_CONSATTR "consattr" + +/* Misc defines */ +#define NERVE_PACKET_LEN 16 + +struct verb_handler; + +static int poke_nerve(const char *nerve, struct verb_handler *h); + +/* + * Contains verb handlers called when a verb + * (e.g., 'poke') is matched. + */ +struct verb_handler { + int(*run)(const char *nerve, struct verb_handler *h); + char **argv; + size_t argc; +}; + +/* + * Holds information that may be sent down + * my nerves. + * + * Example: nerve poke 1 0 1 + * * * * + * +--------+ / / / + * | meow | <------+ / / + * |--------| / / + * | foo | <------+ / + * |--------| / + * | foobar | <------+ + * +--------+ + * packet + */ +struct nerve_payload { + uint32_t packet[NERVE_PACKET_LEN]; + uint16_t len; +}; + +/* + * Verb handler table, when a verb is matched, + * its respective handler is called. + */ +static struct verb_handler verbtab[] = { + { poke_nerve } +}; + +/* + * Print list of available options as well as + * information about the program. + */ +static void +help(void) +{ + printf( + "nerve: usage: nerve [ .. data ..]\n" + "verb 'poke': Poke a control (/ctl) nerve\n" + "???????????????? NERVES ????????????????\n" + "consattr: Console attributes\n" + ); +} + +/* + * The user gets to send data down my nerves through + * a nerve payload. This function acquires the nerve + * payload. Please don't hurt me. + * + * @argc: Number of arguments within argv + * @argv: Argument vector + * @res: Where the payload goes + */ +static int +get_nerve_payload(int argc, char *argv[], struct nerve_payload *res) +{ + char *payload_str; + uint8_t datum; + + /* Do we have a nerve payload? */ + if (argc < 4) { + printf("[!] missing nerve payload\n"); + return -1; + } + + /* Reset fields */ + res->len = 0; + memset(res->packet, 0, sizeof(res->packet)); + + /* Start grabbing bytes */ + for (int i = 3; i < argc; ++i) { + payload_str = argv[i]; + datum = atoi(payload_str); + res->packet[res->len++] = datum; + } + + return 0; +} + +/* + * Poke a control nerve located in /ctl/ + * + * @nerve: Name of the nerve (e.g., consattr) + * @h: Verb handler, instance of self + * + * Returns less than zero if the nerve does not + * match. + */ +static int +poke_nerve(const char *nerve, struct verb_handler *h) +{ + struct nerve_payload payload; + int error, nerve_idx = -1; + + if (nerve == NULL || h == NULL) { + return -EINVAL; + } + + /* + * Now we need to parse the nerve string + * and see if it matches with anything + * that we know. + */ + switch (*nerve) { + case 'c': + if (strcmp(nerve, SNERVE_CONSATTR) == 0) { + nerve_idx = NERVE_CONSATTR; + error = get_nerve_payload(h->argc, h->argv, &payload); + + if (error < 0) { + printf("[!] nerve error\n"); + return -1; + } + } + } + + switch (nerve_idx) { + case NERVE_CONSATTR: + { + struct console_attr c; + int fd; + + c.cursor_x = payload.packet[0]; + c.cursor_y = payload.packet[1]; + + fd = open("/ctl/console/attr", O_WRONLY); + write(fd, payload.packet, sizeof(c)); + close(fd); + break; + } + default: + printf("[&^]: This is not my nerve.\n"); + break; + } + + return -1; +} + +/* + * Convert a string verb, passed in through the command + * line, into a numeric definition + * + * @verb: String verb + */ +static int +verb_to_def(const char *verb) +{ + if (verb == NULL) { + return -EINVAL; + } + + /* + * Parse the verb and try to match it against + * a constant. + * + * TODO: Add 'peek' + * + * XXX: Here we are first matching the first character + * before we match the entire verb as that is more + * efficient than scanning each entire string until + * one matches. + */ + switch (*verb) { + case 'p': + if (strcmp(verb, SVERB_POKE) == 0) { + return VERB_POKE; + } + default: + printf("[!] bad verb \"%s\"\n", verb); + return VERB_UNKNOWN; + } + + return VERB_UNKNOWN; +} + +int +main(int argc, char **argv) +{ + struct verb_handler *verbd; + int verb; + + if (argc < 2) { + help(); + return -1; + } + + verb = verb_to_def(argv[1]); + if (verb < 0) { + return -1; + } + + /* Make sure the arguments match */ + switch (verb) { + case VERB_POKE: + if (argc < 3) { + printf("[!] missing nerve name\n"); + help(); + return -1; + } + break; + } + + verbd = &verbtab[verb]; + verbd->argv = argv; + verbd->argc = argc; + return verbd->run(argv[2], verbd); +} -- cgit v1.2.3