xref: /openbsd-src/usr.sbin/hostctl/hostctl.c (revision 98dbb30ef2877b22f63981e6c70549f5664dbeb5)
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