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
usage(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
kvsetstr(char * dst,const char * src,size_t dstlen)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
kvsetfile(char * dst,const char * input,size_t dstlen)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
main(int argc,char * argv[])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