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