xref: /openbsd/usr.sbin/hostctl/hostctl.c (revision 4bdff4be)
1 /*	$OpenBSD: hostctl.c,v 1.6 2023/01/07 06:40:21 asou Exp $	*/
2 
3 /*
4  * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
21 
22 #include <dev/pv/pvvar.h>
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <err.h>
31 #include <vis.h>
32 
33 #define KVBUFSZ	 4096				/* arbitrary value */
34 
35 char		*path_pvbus = "/dev/pvbus0";	/* the first hv interface */
36 int		 qflag = 0;			/* quiet */
37 int		 tflag = 0;			/* show type */
38 
39 __dead void	 usage(void);
40 int		 kvsetstr(char *, const char *, size_t);
41 int		 kvsetfile(char *, const char *, size_t);
42 
43 __dead void
44 usage(void)
45 {
46 	extern char	*__progname;
47 	fprintf(stderr, "usage: %s [-qt] [-f device] "
48 	    "[-i input] [-o output] key [value]\n", __progname);
49 	exit(1);
50 }
51 
52 int
53 kvsetstr(char *dst, const char *src, size_t dstlen)
54 {
55 	size_t	sz;
56 
57 	/* Sanitize the string before sending it to the kernel and host */
58 	if ((sz = strnvis(dst, src, dstlen, VIS_SAFE | VIS_CSTYLE)) >= dstlen)
59 		return (-1);
60 
61 	/* Remove trailing newline */
62 	dst[strcspn(dst, "\n")] = '\0';
63 
64 	return (0);
65 }
66 
67 int
68 kvsetfile(char *dst, const char *input, size_t dstlen)
69 {
70 	char	*buf = NULL;
71 	int	 ret = -1;
72 	FILE	*fp;
73 
74 	if (strcmp("-", input) == 0)
75 		fp = stdin;
76 	else if ((fp = fopen(input, "r")) == NULL)
77 		return (-1);
78 
79 	if ((buf = calloc(1, dstlen)) == NULL)
80 		goto done;
81 	if (fread(buf, 1, dstlen - 1, fp) == 0)
82 		goto done;
83 	if (kvsetstr(dst, buf, dstlen) == -1)
84 		goto done;
85 
86 	ret = 0;
87  done:
88 	free(buf);
89 	if (fp != stdin)
90 		fclose(fp);
91 	return (ret);
92 }
93 
94 int
95 main(int argc, char *argv[])
96 {
97 	const char		*key, *value, *in = NULL, *out = NULL;
98 	FILE			*outfp = stdout;
99 	int			fd, ret;
100 	struct pvbus_req	pvr;
101 	int			ch;
102 	unsigned long		cmd = 0;
103 	char			*str;
104 
105 	while ((ch = getopt(argc, argv, "f:i:o:qt")) != -1) {
106 		switch (ch) {
107 		case 'f':
108 			path_pvbus = optarg;
109 			break;
110 		case 'i':
111 			in = optarg;
112 			break;
113 		case 'o':
114 			out = optarg;
115 			break;
116 		case 'q':
117 			qflag++;
118 			break;
119 		case 't':
120 			tflag++;
121 			break;
122 		default:
123 			usage();
124 		}
125 	}
126 	argc -= optind;
127 	argv += optind;
128 
129 	if ((fd = open(path_pvbus, O_RDONLY)) == -1)
130 		err(1, "open: %s", path_pvbus);
131 
132 	if (out != NULL) {
133 		if (strcmp("-", out) == 0)
134 			outfp = stdout;
135 		else if ((outfp = fopen(out, "w")) == NULL)
136 			err(1, "fopen: %s", out);
137 	}
138 
139 	memset(&pvr, 0, sizeof(pvr));
140 	pvr.pvr_keylen = pvr.pvr_valuelen = KVBUFSZ;
141 	if ((pvr.pvr_key = calloc(1, pvr.pvr_keylen)) == NULL ||
142 	    (pvr.pvr_value = calloc(1, pvr.pvr_valuelen)) == NULL)
143 		err(1, "calloc");
144 
145 	if (tflag) {
146 		if (ioctl(fd, PVBUSIOC_TYPE, &pvr, sizeof(pvr)) == -1)
147 			err(1, "ioctl");
148 
149 		/* The returned type should be a simple single-line key */
150 		if (stravis(&str, pvr.pvr_key,
151 		    VIS_WHITE | VIS_DQ | VIS_CSTYLE) == -1)
152 			err(1, "stravis");
153 		fprintf(outfp, "%s: %s\n", path_pvbus, str);
154 		free(str);
155 		goto done;
156 	}
157 
158 	if (argc < 1)
159 		usage();
160 	key = argv[0];
161 
162 	if (kvsetstr(pvr.pvr_key, key, pvr.pvr_keylen) == -1)
163 		errx(1, "key too long");
164 
165 	/* Validate command line options for reading or writing */
166 	if (argc == 2 && in == NULL) {
167 		cmd = PVBUSIOC_KVWRITE;
168 		value = argv[1];
169 		if (kvsetstr(pvr.pvr_value, value, pvr.pvr_valuelen) == -1)
170 			errx(1, "value too long");
171 	} else if (argc == 1 && in != NULL) {
172 		cmd = PVBUSIOC_KVWRITE;
173 		if (kvsetfile(pvr.pvr_value, in, pvr.pvr_valuelen) == -1)
174 			errx(1, "input file");
175 	} else if (argc == 1) {
176 		cmd = cmd == 0 ? PVBUSIOC_KVREAD : cmd;
177 	} else
178 		usage();
179 
180 	/* Re-open read-writable */
181 	if (cmd != PVBUSIOC_KVREAD) {
182 		close(fd);
183 		if ((fd = open(path_pvbus, O_RDWR)) == -1)
184 			err(1, "open: %s", path_pvbus);
185 		if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == -1)
186 			err(1, "ioctl");
187 	} else {
188 		while (1) {
189 			if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == 0)
190 				break;
191 			if (errno == ERANGE &&
192 			    pvr.pvr_valuelen < PVBUS_KVOP_MAXSIZE) {
193 				/* the buffer is not enough, expand it */
194 				pvr.pvr_valuelen *= 2;
195 				if ((pvr.pvr_value = realloc(pvr.pvr_value,
196 				    pvr.pvr_valuelen)) == NULL)
197 					err(1, "realloc");
198 				continue;
199 			}
200 			err(1, "ioctl");
201 		}
202 	}
203 
204 	if (!qflag && strlen(pvr.pvr_value)) {
205 		/*
206 		 * The value can contain newlines and basically anything;
207 		 * only encode the unsafe characters that could perform
208 		 * unexpected functions on the terminal.
209 		 */
210 		if (stravis(&str, pvr.pvr_value, VIS_SAFE | VIS_CSTYLE) == -1)
211 			err(1, "stravis");
212 		fprintf(outfp, "%s\n", str);
213 		free(str);
214 	}
215 
216  done:
217 	if (outfp != stdout)
218 		fclose(outfp);
219 	free(pvr.pvr_value);
220 	free(pvr.pvr_key);
221 	close(fd);
222 
223 	return (0);
224 }
225