1*98dbb30eSasou /* $OpenBSD: hostctl.c,v 1.6 2023/01/07 06:40:21 asou Exp $ */
26fc48f48Sreyk
36fc48f48Sreyk /*
46fc48f48Sreyk * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
56fc48f48Sreyk *
66fc48f48Sreyk * Permission to use, copy, modify, and distribute this software for any
76fc48f48Sreyk * purpose with or without fee is hereby granted, provided that the above
86fc48f48Sreyk * copyright notice and this permission notice appear in all copies.
96fc48f48Sreyk *
106fc48f48Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116fc48f48Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126fc48f48Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136fc48f48Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146fc48f48Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156fc48f48Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166fc48f48Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
176fc48f48Sreyk */
186fc48f48Sreyk
196fc48f48Sreyk #include <sys/ioctl.h>
206fc48f48Sreyk #include <sys/types.h>
216fc48f48Sreyk
226fc48f48Sreyk #include <dev/pv/pvvar.h>
236fc48f48Sreyk
246fc48f48Sreyk #include <stdio.h>
256fc48f48Sreyk #include <stdlib.h>
266fc48f48Sreyk #include <string.h>
276fc48f48Sreyk #include <unistd.h>
286fc48f48Sreyk #include <fcntl.h>
296fc48f48Sreyk #include <errno.h>
306fc48f48Sreyk #include <err.h>
316fc48f48Sreyk #include <vis.h>
326fc48f48Sreyk
336fc48f48Sreyk #define KVBUFSZ 4096 /* arbitrary value */
346fc48f48Sreyk
356fc48f48Sreyk char *path_pvbus = "/dev/pvbus0"; /* the first hv interface */
366fc48f48Sreyk int qflag = 0; /* quiet */
376fc48f48Sreyk int tflag = 0; /* show type */
386fc48f48Sreyk
396fc48f48Sreyk __dead void usage(void);
406fc48f48Sreyk int kvsetstr(char *, const char *, size_t);
416fc48f48Sreyk int kvsetfile(char *, const char *, size_t);
426fc48f48Sreyk
436fc48f48Sreyk __dead void
usage(void)446fc48f48Sreyk usage(void)
456fc48f48Sreyk {
466fc48f48Sreyk extern char *__progname;
47883e45adSjmc fprintf(stderr, "usage: %s [-qt] [-f device] "
486fc48f48Sreyk "[-i input] [-o output] key [value]\n", __progname);
496fc48f48Sreyk exit(1);
506fc48f48Sreyk }
516fc48f48Sreyk
526fc48f48Sreyk int
kvsetstr(char * dst,const char * src,size_t dstlen)536fc48f48Sreyk kvsetstr(char *dst, const char *src, size_t dstlen)
546fc48f48Sreyk {
556fc48f48Sreyk size_t sz;
566fc48f48Sreyk
576fc48f48Sreyk /* Sanitize the string before sending it to the kernel and host */
589a4af82bSreyk if ((sz = strnvis(dst, src, dstlen, VIS_SAFE | VIS_CSTYLE)) >= dstlen)
596fc48f48Sreyk return (-1);
606fc48f48Sreyk
616fc48f48Sreyk /* Remove trailing newline */
622f9de9d2Ssf dst[strcspn(dst, "\n")] = '\0';
636fc48f48Sreyk
646fc48f48Sreyk return (0);
656fc48f48Sreyk }
666fc48f48Sreyk
676fc48f48Sreyk int
kvsetfile(char * dst,const char * input,size_t dstlen)686fc48f48Sreyk kvsetfile(char *dst, const char *input, size_t dstlen)
696fc48f48Sreyk {
706fc48f48Sreyk char *buf = NULL;
716fc48f48Sreyk int ret = -1;
726fc48f48Sreyk FILE *fp;
736fc48f48Sreyk
746fc48f48Sreyk if (strcmp("-", input) == 0)
756fc48f48Sreyk fp = stdin;
766fc48f48Sreyk else if ((fp = fopen(input, "r")) == NULL)
776fc48f48Sreyk return (-1);
786fc48f48Sreyk
796fc48f48Sreyk if ((buf = calloc(1, dstlen)) == NULL)
806fc48f48Sreyk goto done;
816fc48f48Sreyk if (fread(buf, 1, dstlen - 1, fp) == 0)
826fc48f48Sreyk goto done;
836fc48f48Sreyk if (kvsetstr(dst, buf, dstlen) == -1)
846fc48f48Sreyk goto done;
856fc48f48Sreyk
866fc48f48Sreyk ret = 0;
876fc48f48Sreyk done:
886fc48f48Sreyk free(buf);
896fc48f48Sreyk if (fp != stdin)
906fc48f48Sreyk fclose(fp);
916fc48f48Sreyk return (ret);
926fc48f48Sreyk }
936fc48f48Sreyk
946fc48f48Sreyk int
main(int argc,char * argv[])956fc48f48Sreyk main(int argc, char *argv[])
966fc48f48Sreyk {
976fc48f48Sreyk const char *key, *value, *in = NULL, *out = NULL;
986fc48f48Sreyk FILE *outfp = stdout;
996fc48f48Sreyk int fd, ret;
1006fc48f48Sreyk struct pvbus_req pvr;
1016fc48f48Sreyk int ch;
1026fc48f48Sreyk unsigned long cmd = 0;
1036fc48f48Sreyk char *str;
1046fc48f48Sreyk
1056fc48f48Sreyk while ((ch = getopt(argc, argv, "f:i:o:qt")) != -1) {
1066fc48f48Sreyk switch (ch) {
1076fc48f48Sreyk case 'f':
1086fc48f48Sreyk path_pvbus = optarg;
1096fc48f48Sreyk break;
1106fc48f48Sreyk case 'i':
1116fc48f48Sreyk in = optarg;
1126fc48f48Sreyk break;
1136fc48f48Sreyk case 'o':
1146fc48f48Sreyk out = optarg;
1156fc48f48Sreyk break;
1166fc48f48Sreyk case 'q':
1176fc48f48Sreyk qflag++;
1186fc48f48Sreyk break;
1196fc48f48Sreyk case 't':
1206fc48f48Sreyk tflag++;
1216fc48f48Sreyk break;
1226fc48f48Sreyk default:
1236fc48f48Sreyk usage();
1246fc48f48Sreyk }
1256fc48f48Sreyk }
1266fc48f48Sreyk argc -= optind;
1276fc48f48Sreyk argv += optind;
1286fc48f48Sreyk
1296fc48f48Sreyk if ((fd = open(path_pvbus, O_RDONLY)) == -1)
1306fc48f48Sreyk err(1, "open: %s", path_pvbus);
1316fc48f48Sreyk
1326fc48f48Sreyk if (out != NULL) {
1336fc48f48Sreyk if (strcmp("-", out) == 0)
1346fc48f48Sreyk outfp = stdout;
1356fc48f48Sreyk else if ((outfp = fopen(out, "w")) == NULL)
1366fc48f48Sreyk err(1, "fopen: %s", out);
1376fc48f48Sreyk }
1386fc48f48Sreyk
1396fc48f48Sreyk memset(&pvr, 0, sizeof(pvr));
1406fc48f48Sreyk pvr.pvr_keylen = pvr.pvr_valuelen = KVBUFSZ;
1416fc48f48Sreyk if ((pvr.pvr_key = calloc(1, pvr.pvr_keylen)) == NULL ||
1426fc48f48Sreyk (pvr.pvr_value = calloc(1, pvr.pvr_valuelen)) == NULL)
1436fc48f48Sreyk err(1, "calloc");
1446fc48f48Sreyk
1456fc48f48Sreyk if (tflag) {
146df69c215Sderaadt if (ioctl(fd, PVBUSIOC_TYPE, &pvr, sizeof(pvr)) == -1)
1476fc48f48Sreyk err(1, "ioctl");
1486fc48f48Sreyk
1496fc48f48Sreyk /* The returned type should be a simple single-line key */
1506fc48f48Sreyk if (stravis(&str, pvr.pvr_key,
1519a4af82bSreyk VIS_WHITE | VIS_DQ | VIS_CSTYLE) == -1)
1526fc48f48Sreyk err(1, "stravis");
1536fc48f48Sreyk fprintf(outfp, "%s: %s\n", path_pvbus, str);
1546fc48f48Sreyk free(str);
1556fc48f48Sreyk goto done;
1566fc48f48Sreyk }
1576fc48f48Sreyk
1586fc48f48Sreyk if (argc < 1)
1596fc48f48Sreyk usage();
1606fc48f48Sreyk key = argv[0];
1616fc48f48Sreyk
1626fc48f48Sreyk if (kvsetstr(pvr.pvr_key, key, pvr.pvr_keylen) == -1)
1636fc48f48Sreyk errx(1, "key too long");
1646fc48f48Sreyk
1656fc48f48Sreyk /* Validate command line options for reading or writing */
1666fc48f48Sreyk if (argc == 2 && in == NULL) {
1676fc48f48Sreyk cmd = PVBUSIOC_KVWRITE;
1686fc48f48Sreyk value = argv[1];
1696fc48f48Sreyk if (kvsetstr(pvr.pvr_value, value, pvr.pvr_valuelen) == -1)
1706fc48f48Sreyk errx(1, "value too long");
1716fc48f48Sreyk } else if (argc == 1 && in != NULL) {
1726fc48f48Sreyk cmd = PVBUSIOC_KVWRITE;
1736fc48f48Sreyk if (kvsetfile(pvr.pvr_value, in, pvr.pvr_valuelen) == -1)
1746fc48f48Sreyk errx(1, "input file");
1756fc48f48Sreyk } else if (argc == 1) {
1766fc48f48Sreyk cmd = cmd == 0 ? PVBUSIOC_KVREAD : cmd;
1776fc48f48Sreyk } else
1786fc48f48Sreyk usage();
1796fc48f48Sreyk
1806fc48f48Sreyk /* Re-open read-writable */
181*98dbb30eSasou if (cmd != PVBUSIOC_KVREAD) {
1826fc48f48Sreyk close(fd);
1836fc48f48Sreyk if ((fd = open(path_pvbus, O_RDWR)) == -1)
1846fc48f48Sreyk err(1, "open: %s", path_pvbus);
185df69c215Sderaadt if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == -1)
1866fc48f48Sreyk err(1, "ioctl");
187*98dbb30eSasou } else {
188*98dbb30eSasou while (1) {
189*98dbb30eSasou if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == 0)
190*98dbb30eSasou break;
191*98dbb30eSasou if (errno == ERANGE &&
192*98dbb30eSasou pvr.pvr_valuelen < PVBUS_KVOP_MAXSIZE) {
193*98dbb30eSasou /* the buffer is not enough, expand it */
194*98dbb30eSasou pvr.pvr_valuelen *= 2;
195*98dbb30eSasou if ((pvr.pvr_value = realloc(pvr.pvr_value,
196*98dbb30eSasou pvr.pvr_valuelen)) == NULL)
197*98dbb30eSasou err(1, "realloc");
198*98dbb30eSasou continue;
199*98dbb30eSasou }
200*98dbb30eSasou err(1, "ioctl");
201*98dbb30eSasou }
202*98dbb30eSasou }
2036fc48f48Sreyk
2046fc48f48Sreyk if (!qflag && strlen(pvr.pvr_value)) {
2056fc48f48Sreyk /*
2066fc48f48Sreyk * The value can contain newlines and basically anything;
2076fc48f48Sreyk * only encode the unsafe characters that could perform
2086fc48f48Sreyk * unexpected functions on the terminal.
2096fc48f48Sreyk */
2109a4af82bSreyk if (stravis(&str, pvr.pvr_value, VIS_SAFE | VIS_CSTYLE) == -1)
2116fc48f48Sreyk err(1, "stravis");
2126fc48f48Sreyk fprintf(outfp, "%s\n", str);
2136fc48f48Sreyk free(str);
2146fc48f48Sreyk }
2156fc48f48Sreyk
2166fc48f48Sreyk done:
2176fc48f48Sreyk if (outfp != stdout)
2186fc48f48Sreyk fclose(outfp);
2196fc48f48Sreyk free(pvr.pvr_value);
2206fc48f48Sreyk free(pvr.pvr_key);
2216fc48f48Sreyk close(fd);
2226fc48f48Sreyk
2236fc48f48Sreyk return (0);
2246fc48f48Sreyk }
225