[BACK]Return to hostctl.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / hostctl

File: [local] / src / usr.sbin / hostctl / hostctl.c (download)

Revision 1.6, Sat Jan 7 06:40:21 2023 UTC (17 months ago) by asou
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, HEAD
Changes since 1.5: +19 -5 lines

The maximum length of the value is extended to 64k bytes.

ok yasuoka

/*	$OpenBSD: hostctl.c,v 1.6 2023/01/07 06:40:21 asou Exp $	*/

/*
 * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/ioctl.h>
#include <sys/types.h>

#include <dev/pv/pvvar.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
#include <vis.h>

#define KVBUFSZ	 4096				/* arbitrary value */

char		*path_pvbus = "/dev/pvbus0";	/* the first hv interface */
int		 qflag = 0;			/* quiet */
int		 tflag = 0;			/* show type */

__dead void	 usage(void);
int		 kvsetstr(char *, const char *, size_t);
int		 kvsetfile(char *, const char *, size_t);

__dead void
usage(void)
{
	extern char	*__progname;
	fprintf(stderr, "usage: %s [-qt] [-f device] "
	    "[-i input] [-o output] key [value]\n", __progname);
	exit(1);
}

int
kvsetstr(char *dst, const char *src, size_t dstlen)
{
	size_t	sz;

	/* Sanitize the string before sending it to the kernel and host */
	if ((sz = strnvis(dst, src, dstlen, VIS_SAFE | VIS_CSTYLE)) >= dstlen)
		return (-1);

	/* Remove trailing newline */
	dst[strcspn(dst, "\n")] = '\0';

	return (0);
}

int
kvsetfile(char *dst, const char *input, size_t dstlen)
{
	char	*buf = NULL;
	int	 ret = -1;
	FILE	*fp;

	if (strcmp("-", input) == 0)
		fp = stdin;
	else if ((fp = fopen(input, "r")) == NULL)
		return (-1);

	if ((buf = calloc(1, dstlen)) == NULL)
		goto done;
	if (fread(buf, 1, dstlen - 1, fp) == 0)
		goto done;
	if (kvsetstr(dst, buf, dstlen) == -1)
		goto done;

	ret = 0;
 done:
	free(buf);
	if (fp != stdin)
		fclose(fp);
	return (ret);
}

int
main(int argc, char *argv[])
{
	const char		*key, *value, *in = NULL, *out = NULL;
	FILE			*outfp = stdout;
	int			fd, ret;
	struct pvbus_req	pvr;
	int			ch;
	unsigned long		cmd = 0;
	char			*str;

	while ((ch = getopt(argc, argv, "f:i:o:qt")) != -1) {
		switch (ch) {
		case 'f':
			path_pvbus = optarg;
			break;
		case 'i':
			in = optarg;
			break;
		case 'o':
			out = optarg;
			break;
		case 'q':
			qflag++;
			break;
		case 't':
			tflag++;
			break;
		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

	if ((fd = open(path_pvbus, O_RDONLY)) == -1)
		err(1, "open: %s", path_pvbus);

	if (out != NULL) {
		if (strcmp("-", out) == 0)
			outfp = stdout;
		else if ((outfp = fopen(out, "w")) == NULL)
			err(1, "fopen: %s", out);
	}

	memset(&pvr, 0, sizeof(pvr));
	pvr.pvr_keylen = pvr.pvr_valuelen = KVBUFSZ;
	if ((pvr.pvr_key = calloc(1, pvr.pvr_keylen)) == NULL ||
	    (pvr.pvr_value = calloc(1, pvr.pvr_valuelen)) == NULL)
		err(1, "calloc");

	if (tflag) {
		if (ioctl(fd, PVBUSIOC_TYPE, &pvr, sizeof(pvr)) == -1)
			err(1, "ioctl");

		/* The returned type should be a simple single-line key */
		if (stravis(&str, pvr.pvr_key,
		    VIS_WHITE | VIS_DQ | VIS_CSTYLE) == -1)
			err(1, "stravis");
		fprintf(outfp, "%s: %s\n", path_pvbus, str);
		free(str);
		goto done;
	}

	if (argc < 1)
		usage();
	key = argv[0];

	if (kvsetstr(pvr.pvr_key, key, pvr.pvr_keylen) == -1)
		errx(1, "key too long");

	/* Validate command line options for reading or writing */
	if (argc == 2 && in == NULL) {
		cmd = PVBUSIOC_KVWRITE;
		value = argv[1];
		if (kvsetstr(pvr.pvr_value, value, pvr.pvr_valuelen) == -1)
			errx(1, "value too long");
	} else if (argc == 1 && in != NULL) {
		cmd = PVBUSIOC_KVWRITE;
		if (kvsetfile(pvr.pvr_value, in, pvr.pvr_valuelen) == -1)
			errx(1, "input file");
	} else if (argc == 1) {
		cmd = cmd == 0 ? PVBUSIOC_KVREAD : cmd;
	} else
		usage();

	/* Re-open read-writable */
	if (cmd != PVBUSIOC_KVREAD) {
		close(fd);
		if ((fd = open(path_pvbus, O_RDWR)) == -1)
			err(1, "open: %s", path_pvbus);
		if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == -1)
			err(1, "ioctl");
	} else {
		while (1) {
			if ((ret = ioctl(fd, cmd, &pvr, sizeof(pvr))) == 0)
				break;
			if (errno == ERANGE &&
			    pvr.pvr_valuelen < PVBUS_KVOP_MAXSIZE) {
				/* the buffer is not enough, expand it */
				pvr.pvr_valuelen *= 2;
				if ((pvr.pvr_value = realloc(pvr.pvr_value,
				    pvr.pvr_valuelen)) == NULL)
					err(1, "realloc");
				continue;
			}
			err(1, "ioctl");
		}
	}

	if (!qflag && strlen(pvr.pvr_value)) {
		/*
		 * The value can contain newlines and basically anything;
		 * only encode the unsafe characters that could perform
		 * unexpected functions on the terminal.
		 */
		if (stravis(&str, pvr.pvr_value, VIS_SAFE | VIS_CSTYLE) == -1)
			err(1, "stravis");
		fprintf(outfp, "%s\n", str);
		free(str);
	}

 done:
	if (outfp != stdout)
		fclose(outfp);
	free(pvr.pvr_value);
	free(pvr.pvr_key);
	close(fd);

	return (0);
}