summaryrefslogtreecommitdiffstats
path: root/usr.sbin/hostctl/hostctl.c
diff options
context:
space:
mode:
authorreyk <reyk@openbsd.org>2016-01-27 12:27:31 +0000
committerreyk <reyk@openbsd.org>2016-01-27 12:27:31 +0000
commit6fc48f48ca47010e94b6228ed7d4f7f8eb47d756 (patch)
tree2cfddec844068d9b85b00e901c78fbe6e2149fe5 /usr.sbin/hostctl/hostctl.c
parentRemoves the abstraction layer to support multiple executable binaries. (diff)
downloadwireguard-openbsd-6fc48f48ca47010e94b6228ed7d4f7f8eb47d756.tar.xz
wireguard-openbsd-6fc48f48ca47010e94b6228ed7d4f7f8eb47d756.zip
Add hostctl(8), a tool to access key-value stores on the host,
currently for hypervisor information stores on pvbus(4). As discussed with deraadt@, the generic name is used to potentially extend it for other use cases where the host or machine firmware provides a key-value store, hypervisors or things like openprom. Not enabled yet. OK mikeb@
Diffstat (limited to 'usr.sbin/hostctl/hostctl.c')
-rw-r--r--usr.sbin/hostctl/hostctl.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/usr.sbin/hostctl/hostctl.c b/usr.sbin/hostctl/hostctl.c
new file mode 100644
index 00000000000..1a2b510fc13
--- /dev/null
+++ b/usr.sbin/hostctl/hostctl.c
@@ -0,0 +1,211 @@
+/* $OpenBSD: hostctl.c,v 1.1 2016/01/27 12:27:31 reyk Exp $ */
+
+/*
+ * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <dev/pv/pvvar.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <err.h>
+#include <vis.h>
+
+#define KVBUFSZ 4096 /* arbitrary value */
+
+char *path_pvbus = "/dev/pvbus0"; /* the first hv interface */
+int qflag = 0; /* quiet */
+int tflag = 0; /* show type */
+
+__dead void usage(void);
+int kvsetstr(char *, const char *, size_t);
+int kvsetfile(char *, const char *, size_t);
+
+__dead void
+usage(void)
+{
+ extern char *__progname;
+ fprintf(stderr, "usage: %s [-t] [-f device] "
+ "[-i input] [-o output] key [value]\n", __progname);
+ exit(1);
+}
+
+int
+kvsetstr(char *dst, const char *src, size_t dstlen)
+{
+ size_t sz;
+
+ /* Sanitize the string before sending it to the kernel and host */
+ if ((sz = strnvis(dst, src, dstlen, VIS_OCTAL)) >= dstlen)
+ return (-1);
+
+ /* Remove trailing newline */
+ if (dst[sz - 1] == '\n')
+ dst[sz - 1] = '\0';
+
+ return (0);
+}
+
+int
+kvsetfile(char *dst, const char *input, size_t dstlen)
+{
+ char *buf = NULL;
+ int ret = -1;
+ FILE *fp;
+
+ if (strcmp("-", input) == 0)
+ fp = stdin;
+ else if ((fp = fopen(input, "r")) == NULL)
+ return (-1);
+
+ if ((buf = calloc(1, dstlen)) == NULL)
+ goto done;
+ if (fread(buf, 1, dstlen - 1, fp) == 0)
+ goto done;
+ if (kvsetstr(dst, buf, dstlen) == -1)
+ goto done;
+
+ ret = 0;
+ done:
+ free(buf);
+ if (fp != stdin)
+ fclose(fp);
+ return (ret);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *key, *value, *in = NULL, *out = NULL;
+ FILE *outfp = stdout;
+ int fd, ret;
+ struct pvbus_req pvr;
+ int ch;
+ unsigned long cmd = 0;
+ char *str;
+
+ while ((ch = getopt(argc, argv, "f:i:o:qt")) != -1) {
+ switch (ch) {
+ case 'f':
+ path_pvbus = optarg;
+ break;
+ case 'i':
+ in = optarg;
+ break;
+ case 'o':
+ out = optarg;
+ break;
+ case 'q':
+ qflag++;
+ break;
+ case 't':
+ tflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((fd = open(path_pvbus, O_RDONLY)) == -1)
+ err(1, "open: %s", path_pvbus);
+
+ if (out != NULL) {
+ if (strcmp("-", out) == 0)
+ outfp = stdout;
+ else if ((outfp = fopen(out, "w")) == NULL)
+ err(1, "fopen: %s", out);
+ }
+
+ memset(&pvr, 0, sizeof(pvr));
+ pvr.pvr_keylen = pvr.pvr_valuelen = KVBUFSZ;
+ if ((pvr.pvr_key = calloc(1, pvr.pvr_keylen)) == NULL ||
+ (pvr.pvr_value = calloc(1, pvr.pvr_valuelen)) == NULL)
+ err(1, "calloc");
+
+ if (tflag) {
+ if (ioctl(fd, PVBUSIOC_TYPE, &pvr, sizeof(pvr)) != 0)
+ err(1, "ioctl");
+
+ /* The returned type should be a simple single-line key */
+ if (stravis(&str, pvr.pvr_key,
+ VIS_WHITE | VIS_DQ | VIS_OCTAL) == -1)
+ err(1, "stravis");
+ fprintf(outfp, "%s: %s\n", path_pvbus, str);
+ free(str);
+ goto done;
+ }
+
+ if (argc < 1)
+ usage();
+ key = argv[0];
+
+ if (kvsetstr(pvr.pvr_key, key, pvr.pvr_keylen) == -1)
+ errx(1, "key too long");
+
+ /* Validate command line options for reading or writing */
+ if (argc == 2 && in == NULL) {
+ cmd = PVBUSIOC_KVWRITE;
+ value = argv[1];
+ if (kvsetstr(pvr.pvr_value, value, pvr.pvr_valuelen) == -1)
+ errx(1, "value too long");
+ } else if (argc == 1 && in != NULL) {
+ cmd = PVBUSIOC_KVWRITE;
+ if (kvsetfile(pvr.pvr_value, in, pvr.pvr_valuelen) == -1)
+ errx(1, "input file");
+ } else if (argc == 1) {
+ cmd = cmd == 0 ? PVBUSIOC_KVREAD : cmd;
+ } else
+ usage();
+
+ /* Re-open read-writable */
+ if (cmd == PVBUSIOC_KVWRITE) {
+ close(fd);
+ if ((fd = open(path_pvbus, O_RDWR)) == -1)
+ err(1, "open: %s", path_pvbus);
+ }
+
+ if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) != 0)
+ err(1, "ioctl");
+
+ if (!qflag && strlen(pvr.pvr_value)) {
+ /*
+ * The value can contain newlines and basically anything;
+ * only encode the unsafe characters that could perform
+ * unexpected functions on the terminal.
+ */
+ if (stravis(&str, pvr.pvr_value, VIS_OCTAL) == -1)
+ err(1, "stravis");
+ fprintf(outfp, "%s\n", str);
+ free(str);
+ }
+
+ done:
+ if (outfp != stdout)
+ fclose(outfp);
+ free(pvr.pvr_value);
+ free(pvr.pvr_key);
+ close(fd);
+
+ return (0);
+}