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