1 /* $OpenBSD: hostctl.c,v 1.3 2016/01/27 16:01:36 jmc 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 if (dst[sz - 1] == '\n') 63 dst[sz - 1] = '\0'; 64 65 return (0); 66 } 67 68 int 69 kvsetfile(char *dst, const char *input, size_t dstlen) 70 { 71 char *buf = NULL; 72 int ret = -1; 73 FILE *fp; 74 75 if (strcmp("-", input) == 0) 76 fp = stdin; 77 else if ((fp = fopen(input, "r")) == NULL) 78 return (-1); 79 80 if ((buf = calloc(1, dstlen)) == NULL) 81 goto done; 82 if (fread(buf, 1, dstlen - 1, fp) == 0) 83 goto done; 84 if (kvsetstr(dst, buf, dstlen) == -1) 85 goto done; 86 87 ret = 0; 88 done: 89 free(buf); 90 if (fp != stdin) 91 fclose(fp); 92 return (ret); 93 } 94 95 int 96 main(int argc, char *argv[]) 97 { 98 const char *key, *value, *in = NULL, *out = NULL; 99 FILE *outfp = stdout; 100 int fd, ret; 101 struct pvbus_req pvr; 102 int ch; 103 unsigned long cmd = 0; 104 char *str; 105 106 while ((ch = getopt(argc, argv, "f:i:o:qt")) != -1) { 107 switch (ch) { 108 case 'f': 109 path_pvbus = optarg; 110 break; 111 case 'i': 112 in = optarg; 113 break; 114 case 'o': 115 out = optarg; 116 break; 117 case 'q': 118 qflag++; 119 break; 120 case 't': 121 tflag++; 122 break; 123 default: 124 usage(); 125 } 126 } 127 argc -= optind; 128 argv += optind; 129 130 if ((fd = open(path_pvbus, O_RDONLY)) == -1) 131 err(1, "open: %s", path_pvbus); 132 133 if (out != NULL) { 134 if (strcmp("-", out) == 0) 135 outfp = stdout; 136 else if ((outfp = fopen(out, "w")) == NULL) 137 err(1, "fopen: %s", out); 138 } 139 140 memset(&pvr, 0, sizeof(pvr)); 141 pvr.pvr_keylen = pvr.pvr_valuelen = KVBUFSZ; 142 if ((pvr.pvr_key = calloc(1, pvr.pvr_keylen)) == NULL || 143 (pvr.pvr_value = calloc(1, pvr.pvr_valuelen)) == NULL) 144 err(1, "calloc"); 145 146 if (tflag) { 147 if (ioctl(fd, PVBUSIOC_TYPE, &pvr, sizeof(pvr)) != 0) 148 err(1, "ioctl"); 149 150 /* The returned type should be a simple single-line key */ 151 if (stravis(&str, pvr.pvr_key, 152 VIS_WHITE | VIS_DQ | VIS_CSTYLE) == -1) 153 err(1, "stravis"); 154 fprintf(outfp, "%s: %s\n", path_pvbus, str); 155 free(str); 156 goto done; 157 } 158 159 if (argc < 1) 160 usage(); 161 key = argv[0]; 162 163 if (kvsetstr(pvr.pvr_key, key, pvr.pvr_keylen) == -1) 164 errx(1, "key too long"); 165 166 /* Validate command line options for reading or writing */ 167 if (argc == 2 && in == NULL) { 168 cmd = PVBUSIOC_KVWRITE; 169 value = argv[1]; 170 if (kvsetstr(pvr.pvr_value, value, pvr.pvr_valuelen) == -1) 171 errx(1, "value too long"); 172 } else if (argc == 1 && in != NULL) { 173 cmd = PVBUSIOC_KVWRITE; 174 if (kvsetfile(pvr.pvr_value, in, pvr.pvr_valuelen) == -1) 175 errx(1, "input file"); 176 } else if (argc == 1) { 177 cmd = cmd == 0 ? PVBUSIOC_KVREAD : cmd; 178 } else 179 usage(); 180 181 /* Re-open read-writable */ 182 if (cmd == PVBUSIOC_KVWRITE) { 183 close(fd); 184 if ((fd = open(path_pvbus, O_RDWR)) == -1) 185 err(1, "open: %s", path_pvbus); 186 } 187 188 if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) != 0) 189 err(1, "ioctl"); 190 191 if (!qflag && strlen(pvr.pvr_value)) { 192 /* 193 * The value can contain newlines and basically anything; 194 * only encode the unsafe characters that could perform 195 * unexpected functions on the terminal. 196 */ 197 if (stravis(&str, pvr.pvr_value, VIS_SAFE | VIS_CSTYLE) == -1) 198 err(1, "stravis"); 199 fprintf(outfp, "%s\n", str); 200 free(str); 201 } 202 203 done: 204 if (outfp != stdout) 205 fclose(outfp); 206 free(pvr.pvr_value); 207 free(pvr.pvr_key); 208 close(fd); 209 210 return (0); 211 } 212