summaryrefslogtreecommitdiff
path: root/usr.bin/nerve/nerve.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/nerve/nerve.c')
-rw-r--r--usr.bin/nerve/nerve.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/usr.bin/nerve/nerve.c b/usr.bin/nerve/nerve.c
new file mode 100644
index 0000000..75a19be
--- /dev/null
+++ b/usr.bin/nerve/nerve.c
@@ -0,0 +1,377 @@
+/*
+ * 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 <sys/errno.h>
+#include <sys/console.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+/* Verb numeric defs (see string defs) */
+#define VERB_UNKNOWN -1
+#define VERB_POKE 0x0000
+#define VERB_PEEK 0x0001
+
+/* Verb string defs (see numeric defs) */
+#define SVERB_POKE "poke"
+#define SVERB_PEEK "peek"
+
+/* Nerve numeric defs (see string defs) */
+#define NERVE_UNKNOWN -1
+#define NERVE_CONSATTR 0x0000
+#define NERVE_CONSFEAT 0x0001
+
+/* Nerve string defs (see numeric defs) */
+#define SNERVE_CONSATTR "consattr"
+#define SNERVE_CONSFEAT "consfeat"
+
+/* Misc defines */
+#define NERVE_PACKET_LEN 16
+
+struct verb_handler;
+
+static int poke_nerve(const char *nerve, struct verb_handler *h);
+static int peek_nerve(const char *nerve, struct verb_handler *h);
+static int nerve_to_def(const char *nerve);
+
+/*
+ * 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 <x> 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 },
+ { peek_nerve }
+};
+
+/*
+ * Print list of available options as well as
+ * information about the program.
+ */
+static void
+help(void)
+{
+ printf(
+ "nerve: usage: nerve <verb> [ .. data ..]\n"
+ "verb 'poke': Poke a control (/ctl) nerve\n"
+ "???????????????? NERVES ????????????????\n"
+ "consattr: Console attributes\n"
+ "consfeat: Console features\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;
+ uint32_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) {
+ if (res->len >= NERVE_PACKET_LEN) {
+ printf("[*] truncated packet\n");
+ break;
+ }
+ payload_str = argv[i];
+ datum = atoi(payload_str);
+ res->packet[res->len++] = datum;
+ }
+
+ return 0;
+}
+
+/*
+ * Peek at a control nerve located in /ctl/
+ *
+ * @nerve: Name of nerve to peek at
+ * @h: Verb handler, instance of self
+ *
+ * Returns less than zero if the nerve does
+ * not match.
+ */
+static int
+peek_nerve(const char *nerve, struct verb_handler *h)
+{
+ int error, nerve_idx = -1;
+
+ if (nerve == NULL || h == NULL) {
+ return -EINVAL;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ switch (nerve_idx) {
+ case NERVE_CONSATTR:
+ {
+ struct console_attr c;
+ int fd;
+
+ fd = open("/ctl/console/attr", O_RDONLY);
+ read(fd, &c, sizeof(c));
+ printf("(cursx=%d, cursy=%d)\n", c.cursor_x, c.cursor_y);
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ fd = open("/ctl/console/feat", O_RDONLY);
+ read(fd, &f, sizeof(f));
+ printf("ansi_esc=%d\n", f.ansi_esc);
+ printf("show_curs=%d\n", f.show_curs);
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ 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;
+ }
+
+ /* Grab the nerve table index */
+ nerve_idx = nerve_to_def(nerve);
+ if (nerve_idx == NERVE_UNKNOWN) {
+ printf("[&^]: This is not my nerve.\n");
+ return -1;
+ }
+
+ /* Grab the payload passed by the user */
+ 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, &c, sizeof(c));
+ close(fd);
+ break;
+ }
+ case NERVE_CONSFEAT:
+ {
+ struct console_feat f;
+ int fd;
+
+ f.ansi_esc = payload.packet[0] & 0xFF;
+ f.show_curs = payload.packet[1] & 0xFF;
+
+ fd = open("/ctl/console/feat", O_WRONLY);
+ write(fd, &f, sizeof(f));
+ close(fd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Convert a nerve name into a numeric nerve
+ * definition
+ *
+ * @nerve: Nerve name to convert
+ */
+static int
+nerve_to_def(const char *nerve)
+{
+ /*
+ * 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) {
+ return NERVE_CONSATTR;
+ } else if (strcmp(nerve, SNERVE_CONSFEAT) == 0) {
+ return NERVE_CONSFEAT;
+ }
+ }
+
+ return NERVE_UNKNOWN;
+}
+
+/*
+ * 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.
+ *
+ * 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;
+ }
+ if (strcmp(verb, SVERB_PEEK) == 0) {
+ return VERB_PEEK;
+ }
+ 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);
+}