xref: /openbsd/sbin/bioctl/bioctl.c (revision 1302b329)
1*1302b329Skn /* $OpenBSD: bioctl.c,v 1.157 2023/10/07 12:20:10 kn Exp $ */
2cf6503d7Sderaadt 
33af9de98Smarco /*
42f39728eSdlg  * Copyright (c) 2004, 2005 Marco Peereboom
53af9de98Smarco  * All rights reserved.
63af9de98Smarco  *
73af9de98Smarco  * Redistribution and use in source and binary forms, with or without
83af9de98Smarco  * modification, are permitted provided that the following conditions
93af9de98Smarco  * are met:
103af9de98Smarco  * 1. Redistributions of source code must retain the above copyright
113af9de98Smarco  *    notice, this list of conditions and the following disclaimer.
123af9de98Smarco  * 2. Redistributions in binary form must reproduce the above copyright
133af9de98Smarco  *    notice, this list of conditions and the following disclaimer in the
143af9de98Smarco  *    documentation and/or other materials provided with the distribution.
153af9de98Smarco  *
163af9de98Smarco  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
173af9de98Smarco  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
183af9de98Smarco  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
193af9de98Smarco  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
203af9de98Smarco  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
213af9de98Smarco  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
223af9de98Smarco  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
233af9de98Smarco  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
243af9de98Smarco  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
253af9de98Smarco  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
263af9de98Smarco  * SUCH DAMAGE.
273af9de98Smarco  *
283af9de98Smarco  */
293af9de98Smarco 
30b9fc9a72Sderaadt #include <sys/param.h>	/* NODEV */
31c2126c9aSmarco #include <sys/ioctl.h>
3291f4f7d8Sdlg #include <sys/dkio.h>
3346bc198bSmarco #include <sys/stat.h>
34aef7fe28Shshoexer #include <dev/softraidvar.h>
35e7d4f752Sderaadt #include <dev/biovar.h>
363af9de98Smarco 
37db2730c1Smarco #include <errno.h>
38db2730c1Smarco #include <err.h>
39db2730c1Smarco #include <fcntl.h>
408ccdd032Sderaadt #include <util.h>
41934f30d8Sderaadt #include <ctype.h>
42db2730c1Smarco #include <stdio.h>
43db2730c1Smarco #include <stdlib.h>
44db2730c1Smarco #include <string.h>
45ef0eb24eSjsing #include <time.h>
46db2730c1Smarco #include <unistd.h>
47b9fc9a72Sderaadt #include <limits.h>
4803b2dfbfShenning #include <vis.h>
499e8c6f5bShshoexer #include <readpassphrase.h>
50db2730c1Smarco 
516de960dcSmarco struct locator {
526de960dcSmarco 	int		channel;
536de960dcSmarco 	int		target;
546de960dcSmarco 	int		lun;
556de960dcSmarco };
566de960dcSmarco 
57d865b7d2Suebayasi struct timing {
58d865b7d2Suebayasi 	int		interval;
59d865b7d2Suebayasi 	int		start;
60d865b7d2Suebayasi };
61d865b7d2Suebayasi 
621f610680Stb static void __dead	usage(void);
6341eccc89Sderaadt const char 		*str2locator(const char *, struct locator *);
64d865b7d2Suebayasi const char 		*str2patrol(const char *, struct timing *);
65d313c28bSjsing void			bio_status(struct bio_status *);
6646bc198bSmarco int			bio_parse_devlist(char *, dev_t *);
67aef7fe28Shshoexer void			bio_kdf_derive(struct sr_crypto_kdfinfo *,
683487a6b1Sjsing 			    struct sr_crypto_pbkdf *, char *, int);
69aef7fe28Shshoexer void			bio_kdf_generate(struct sr_crypto_kdfinfo *);
70ef0eb24eSjsing int			bcrypt_pbkdf_autorounds(void);
713487a6b1Sjsing void			derive_key(u_int32_t, int, u_int8_t *, size_t,
7261f93244Sjsing 			    u_int8_t *, size_t, char *, int);
738ccdd032Sderaadt 
748ccdd032Sderaadt void			bio_inq(char *);
758ccdd032Sderaadt void			bio_alarm(char *);
76a15048bbSmarco int			bio_getvolbyname(char *);
77a15048bbSmarco void			bio_setstate(char *, int, char *);
78a928c459Sderaadt void			bio_setblink(char *, char *, int);
79a928c459Sderaadt void			bio_blink(char *, int, int);
800054cd36Sjsing void			bio_createraid(u_int16_t, char *, char *);
81c7c3e8aaSmarco void			bio_deleteraid(char *);
82c6446370Sjsing void			bio_changepass(char *);
83e8a57fdeSmarco u_int32_t		bio_createflags(char *);
8403b2dfbfShenning char			*bio_vis(char *);
8503b2dfbfShenning void			bio_diskinq(char *);
86d865b7d2Suebayasi void			bio_patrol(char *);
873af9de98Smarco 
883af9de98Smarco int			devh = -1;
89abe9d68eSderaadt int			human;
90abe9d68eSderaadt int			verbose;
91e8a57fdeSmarco u_int32_t		cflags = 0;
92b4f9a699Skn int			rflag = -1;	/* auto */
93dd81669fSkn char			*passfile;
943af9de98Smarco 
95545c4d7fSjsing void			*bio_cookie;
96545c4d7fSjsing 
975f219970Skn int interactive = 1;
983af9de98Smarco 
993af9de98Smarco int
main(int argc,char * argv[])1003af9de98Smarco main(int argc, char *argv[])
1013af9de98Smarco {
102545c4d7fSjsing 	struct bio_locate	bl;
103db2730c1Smarco 	u_int64_t		func = 0;
104e63d8b1dSdtucker 	char			*devicename = NULL;
105cf6503d7Sderaadt 	char			*realname = NULL, *al_arg = NULL;
1067195049bSmarco 	char			*bl_arg = NULL, *dev_list = NULL;
1070054cd36Sjsing 	char			*key_disk = NULL;
108aedd4f07Sdjm 	const char		*errstr;
109d313c28bSjsing 	int			ch, blink = 0, changepass = 0, diskinq = 0;
110c6446370Sjsing 	int			ss_func = 0;
1119ecba717Sderaadt 	u_int16_t		cr_level = 0;
112e37c64dbSjsing 	int			biodev = 0;
1133af9de98Smarco 
1143af9de98Smarco 	if (argc < 2)
1153af9de98Smarco 		usage();
1163af9de98Smarco 
117d865b7d2Suebayasi 	while ((ch = getopt(argc, argv, "a:b:C:c:dH:hik:l:O:Pp:qr:R:st:u:v")) !=
118c6446370Sjsing 	    -1) {
1193af9de98Smarco 		switch (ch) {
120edfd9792Smarco 		case 'a': /* alarm */
121edfd9792Smarco 			func |= BIOC_ALARM;
122edfd9792Smarco 			al_arg = optarg;
123edfd9792Smarco 			break;
124c55617f1Sdlg 		case 'b': /* blink */
125c55617f1Sdlg 			func |= BIOC_BLINK;
126a928c459Sderaadt 			blink = BIOC_SBBLINK;
127a928c459Sderaadt 			bl_arg = optarg;
128a928c459Sderaadt 			break;
129e8a57fdeSmarco 		case 'C': /* creation flags */
130e8a57fdeSmarco 			cflags = bio_createflags(optarg);
131e8a57fdeSmarco 			break;
1327195049bSmarco 		case 'c': /* create */
1337195049bSmarco 			func |= BIOC_CREATERAID;
13401de20b5Sstsp 			if (strcmp(optarg, "1C") == 0) {
13501de20b5Sstsp 				cr_level = 0x1C;
13601de20b5Sstsp 			} else if (isdigit((unsigned char)*optarg)) {
13792d21b5cStedu 				cr_level = strtonum(optarg, 0, 10, &errstr);
13892d21b5cStedu 				if (errstr != NULL)
13992d21b5cStedu 					errx(1, "Invalid RAID level");
14024b6f6bcSkn 			} else if (strlen(optarg) == 1) {
14198b750e4Stedu 				cr_level = *optarg;
14224b6f6bcSkn 			} else {
14324b6f6bcSkn 				errx(1, "Invalid RAID level");
14424b6f6bcSkn 			}
1457195049bSmarco 			break;
146c7c3e8aaSmarco 		case 'd':
147c7c3e8aaSmarco 			/* delete volume */
148c7c3e8aaSmarco 			func |= BIOC_DELETERAID;
149c7c3e8aaSmarco 			break;
150a928c459Sderaadt 		case 'u': /* unblink */
151a928c459Sderaadt 			func |= BIOC_BLINK;
152a928c459Sderaadt 			blink = BIOC_SBUNBLINK;
153c55617f1Sdlg 			bl_arg = optarg;
154c55617f1Sdlg 			break;
1556de960dcSmarco 		case 'H': /* set hotspare */
1566de960dcSmarco 			func |= BIOC_SETSTATE;
157a15048bbSmarco 			ss_func = BIOC_SSHOTSPARE;
1586de960dcSmarco 			al_arg = optarg;
1596de960dcSmarco 			break;
1608ccdd032Sderaadt 		case 'h':
1618ccdd032Sderaadt 			human = 1;
1628ccdd032Sderaadt 			break;
163db2730c1Smarco 		case 'i': /* inquiry */
164db2730c1Smarco 			func |= BIOC_INQ;
1653af9de98Smarco 			break;
1660054cd36Sjsing 		case 'k': /* Key disk. */
1670054cd36Sjsing 			key_disk = optarg;
1680054cd36Sjsing 			break;
1697195049bSmarco 		case 'l': /* device list */
1707195049bSmarco 			func |= BIOC_DEVLIST;
1717195049bSmarco 			dev_list = optarg;
1727195049bSmarco 			break;
173c6446370Sjsing 		case 'P':
174c6446370Sjsing 			/* Change passphrase. */
175c6446370Sjsing 			changepass = 1;
176c6446370Sjsing 			break;
17786735da2Smarco 		case 'p':
178dd81669fSkn 			passfile = optarg;
17986735da2Smarco 			break;
180aedd4f07Sdjm 		case 'r':
181ef0eb24eSjsing 			if (strcmp(optarg, "auto") == 0) {
182ef0eb24eSjsing 				rflag = -1;
183ef0eb24eSjsing 				break;
184ef0eb24eSjsing 			}
185b4f9a699Skn 			rflag = strtonum(optarg, 16, 1<<30, &errstr);
186aedd4f07Sdjm 			if (errstr != NULL)
18761f93244Sjsing 				errx(1, "number of KDF rounds is %s: %s",
188aedd4f07Sdjm 				    errstr, optarg);
189aedd4f07Sdjm 			break;
19050e55a42Sjsing 		case 'O':
19150e55a42Sjsing 			/* set a chunk to offline */
19250e55a42Sjsing 			func |= BIOC_SETSTATE;
19350e55a42Sjsing 			ss_func = BIOC_SSOFFLINE;
19450e55a42Sjsing 			al_arg = optarg;
19550e55a42Sjsing 			break;
196a15048bbSmarco 		case 'R':
197a15048bbSmarco 			/* rebuild to provided chunk/CTL */
198a15048bbSmarco 			func |= BIOC_SETSTATE;
199a15048bbSmarco 			ss_func = BIOC_SSREBUILD;
200a15048bbSmarco 			al_arg = optarg;
201a15048bbSmarco 			break;
202b96c6ce2Sckuethe 		case 's':
2035f219970Skn 			interactive = 0;
204b96c6ce2Sckuethe 			break;
205d865b7d2Suebayasi 		case 't': /* patrol */
206d865b7d2Suebayasi 			func |= BIOC_PATROL;
207d865b7d2Suebayasi 			al_arg = optarg;
208d865b7d2Suebayasi 			break;
209abe9d68eSderaadt 		case 'v':
210abe9d68eSderaadt 			verbose = 1;
211abe9d68eSderaadt 			break;
21203b2dfbfShenning 		case 'q':
21303b2dfbfShenning 			diskinq = 1;
21403b2dfbfShenning 			break;
2153af9de98Smarco 		default:
2163af9de98Smarco 			usage();
2173af9de98Smarco 			/* NOTREACHED */
2183af9de98Smarco 		}
2193af9de98Smarco 	}
220cf6503d7Sderaadt 	argc -= optind;
221cf6503d7Sderaadt 	argv += optind;
2223af9de98Smarco 
223c6446370Sjsing 	if (argc != 1 || (changepass && func != 0))
224cf6503d7Sderaadt 		usage();
225cf6503d7Sderaadt 
226dcbaf4c8Sderaadt 	if (func == 0)
227dcbaf4c8Sderaadt 		func |= BIOC_INQ;
228dcbaf4c8Sderaadt 
229e63d8b1dSdtucker 	devicename = argv[0];
230e63d8b1dSdtucker 	if (devicename == NULL)
231e37c64dbSjsing 		errx(1, "need device");
23210b411a7Smarco 
233e63d8b1dSdtucker 	devh = opendev(devicename, O_RDWR, OPENDEV_PART, &realname);
234e37c64dbSjsing 	if (devh == -1) {
23541eccc89Sderaadt 		devh = open("/dev/bio", O_RDWR);
2363af9de98Smarco 		if (devh == -1)
23741eccc89Sderaadt 			err(1, "Can't open %s", "/dev/bio");
2383af9de98Smarco 
239281272dcSpatrick 		memset(&bl, 0, sizeof(bl));
240e63d8b1dSdtucker 		bl.bl_name = devicename;
241df69c215Sderaadt 		if (ioctl(devh, BIOCLOCATE, &bl) == -1)
24210b411a7Smarco 			errx(1, "Can't locate %s device via %s",
24341eccc89Sderaadt 			    bl.bl_name, "/dev/bio");
244d313c28bSjsing 
245545c4d7fSjsing 		bio_cookie = bl.bl_bio.bio_cookie;
246e37c64dbSjsing 		biodev = 1;
247e63d8b1dSdtucker 		devicename = NULL;
248e37c64dbSjsing 	}
2493af9de98Smarco 
25003b2dfbfShenning 	if (diskinq) {
251e63d8b1dSdtucker 		bio_diskinq(devicename);
252e37c64dbSjsing 	} else if (changepass && !biodev) {
253e63d8b1dSdtucker 		bio_changepass(devicename);
25403b2dfbfShenning 	} else if (func & BIOC_INQ) {
255e63d8b1dSdtucker 		bio_inq(devicename);
256edfd9792Smarco 	} else if (func == BIOC_ALARM) {
257edfd9792Smarco 		bio_alarm(al_arg);
258c55617f1Sdlg 	} else if (func == BIOC_BLINK) {
259e63d8b1dSdtucker 		bio_setblink(devicename, bl_arg, blink);
260d865b7d2Suebayasi 	} else if (func == BIOC_PATROL) {
261d865b7d2Suebayasi 		bio_patrol(al_arg);
2626de960dcSmarco 	} else if (func == BIOC_SETSTATE) {
263a15048bbSmarco 		bio_setstate(al_arg, ss_func, argv[0]);
264e37c64dbSjsing 	} else if (func == BIOC_DELETERAID && !biodev) {
265e63d8b1dSdtucker 		bio_deleteraid(devicename);
2667195049bSmarco 	} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
2677195049bSmarco 		if (!(func & BIOC_CREATERAID))
2687195049bSmarco 			errx(1, "need -c parameter");
2697195049bSmarco 		if (!(func & BIOC_DEVLIST))
2707195049bSmarco 			errx(1, "need -l parameter");
271e37c64dbSjsing 		if (!biodev)
272e37c64dbSjsing 			errx(1, "must use bio device");
2730054cd36Sjsing 		bio_createraid(cr_level, dev_list, key_disk);
2743af9de98Smarco 	}
2753af9de98Smarco 
2763af9de98Smarco 	return (0);
2773af9de98Smarco }
2783af9de98Smarco 
2791f610680Stb static void __dead
usage(void)2803af9de98Smarco usage(void)
2813af9de98Smarco {
2823af9de98Smarco 	extern char		*__progname;
2833af9de98Smarco 
284d90b5d8bSjmc 	fprintf(stderr,
285d0b772c8Sjmc 		"usage: %s [-hiqv] [-a alarm-function] "
286d90b5d8bSjmc 		"[-b channel:target[.lun]]\n"
287d0b772c8Sjmc 		"\t[-H channel:target[.lun]] "
2881e4c8201Sschwarze 		"[-R chunk | channel:target[.lun]]\n"
2892e6b119fSjmc 		"\t[-t patrol-function] "
2902e6b119fSjmc 		"[-u channel:target[.lun]] "
291d0b772c8Sjmc 		"device\n"
292b96c6ce2Sckuethe 		"       %s [-dhiPqsv] "
293889853deSkn 		"[-C flag[,...]] [-c raidlevel] [-k keydisk]\n"
294889853deSkn 		"\t[-l chunk[,...]] "
29550e55a42Sjsing 		"[-O device | channel:target[.lun]]\n"
2961e4c8201Sschwarze 		"\t[-p passfile] [-R chunk | channel:target[.lun]]\n"
2972e6b119fSjmc 		"\t[-r rounds] "
298d0b772c8Sjmc 		"device\n", __progname, __progname);
299d90b5d8bSjmc 
3003af9de98Smarco 	exit(1);
3013af9de98Smarco }
3023af9de98Smarco 
30341eccc89Sderaadt const char *
str2locator(const char * string,struct locator * location)3046de960dcSmarco str2locator(const char *string, struct locator *location)
3056de960dcSmarco {
30650d3c4dcSdlg 	const char		*errstr;
30741eccc89Sderaadt 	char			parse[80], *targ, *lun;
3086de960dcSmarco 
30941eccc89Sderaadt 	strlcpy(parse, string, sizeof parse);
31041eccc89Sderaadt 	targ = strchr(parse, ':');
3116de960dcSmarco 	if (targ == NULL)
31241eccc89Sderaadt 		return ("target not specified");
3136de960dcSmarco 	*targ++ = '\0';
3146de960dcSmarco 
31550d3c4dcSdlg 	lun = strchr(targ, '.');
3166de960dcSmarco 	if (lun != NULL) {
3176de960dcSmarco 		*lun++ = '\0';
31850d3c4dcSdlg 		location->lun = strtonum(lun, 0, 256, &errstr);
31950d3c4dcSdlg 		if (errstr)
32041eccc89Sderaadt 			return (errstr);
3216de960dcSmarco 	} else
3226de960dcSmarco 		location->lun = 0;
3236de960dcSmarco 
32450d3c4dcSdlg 	location->target = strtonum(targ, 0, 256, &errstr);
32550d3c4dcSdlg 	if (errstr)
32641eccc89Sderaadt 		return (errstr);
32741eccc89Sderaadt 	location->channel = strtonum(parse, 0, 256, &errstr);
32850d3c4dcSdlg 	if (errstr)
32941eccc89Sderaadt 		return (errstr);
33041eccc89Sderaadt 	return (NULL);
3316de960dcSmarco }
3326de960dcSmarco 
333d865b7d2Suebayasi const char *
str2patrol(const char * string,struct timing * timing)334d865b7d2Suebayasi str2patrol(const char *string, struct timing *timing)
335d865b7d2Suebayasi {
336d865b7d2Suebayasi 	const char		*errstr;
337d865b7d2Suebayasi 	char			parse[80], *interval = NULL, *start = NULL;
338d865b7d2Suebayasi 
339d865b7d2Suebayasi 	timing->interval = 0;
340d865b7d2Suebayasi 	timing->start = 0;
341d865b7d2Suebayasi 
342d865b7d2Suebayasi 	strlcpy(parse, string, sizeof parse);
343d865b7d2Suebayasi 
344d865b7d2Suebayasi 	interval = strchr(parse, '.');
345d865b7d2Suebayasi 	if (interval != NULL) {
346d865b7d2Suebayasi 		*interval++ = '\0';
347d865b7d2Suebayasi 		start = strchr(interval, '.');
348d865b7d2Suebayasi 		if (start != NULL)
349d865b7d2Suebayasi 			*start++ = '\0';
350d865b7d2Suebayasi 	}
351d865b7d2Suebayasi 	if (interval != NULL) {
352d865b7d2Suebayasi 		/* -1 == continuously */
353d865b7d2Suebayasi 		timing->interval = strtonum(interval, -1, INT_MAX, &errstr);
354d865b7d2Suebayasi 		if (errstr)
355d865b7d2Suebayasi 			return (errstr);
356d865b7d2Suebayasi 	}
357d865b7d2Suebayasi 	if (start != NULL) {
358d865b7d2Suebayasi 		timing->start = strtonum(start, 0, INT_MAX, &errstr);
359d865b7d2Suebayasi 		if (errstr)
360d865b7d2Suebayasi 			return (errstr);
361d865b7d2Suebayasi 	}
362d865b7d2Suebayasi 
363d865b7d2Suebayasi 	return (NULL);
364d865b7d2Suebayasi }
365d865b7d2Suebayasi 
3663af9de98Smarco void
bio_status(struct bio_status * bs)367d313c28bSjsing bio_status(struct bio_status *bs)
368d313c28bSjsing {
3690a69bfccSjsing 	extern char		*__progname;
3700a69bfccSjsing 	char			*prefix;
371d313c28bSjsing 	int			i;
372d313c28bSjsing 
373d313c28bSjsing 	if (strlen(bs->bs_controller))
3740a69bfccSjsing 		prefix = bs->bs_controller;
3750a69bfccSjsing 	else
3760a69bfccSjsing 		prefix = __progname;
377d313c28bSjsing 
3780a69bfccSjsing 	for (i = 0; i < bs->bs_msg_count; i++)
3794bfefca2Skn 		fprintf(bs->bs_msgs[i].bm_type == BIO_MSG_INFO ?
3804bfefca2Skn 		    stdout : stderr, "%s: %s\n", prefix, bs->bs_msgs[i].bm_msg);
3810a69bfccSjsing 
3820a69bfccSjsing 	if (bs->bs_status == BIO_STATUS_ERROR) {
3830a69bfccSjsing 		if (bs->bs_msg_count == 0)
3840a69bfccSjsing 			errx(1, "unknown error");
3850a69bfccSjsing 		else
386d313c28bSjsing 			exit(1);
387d313c28bSjsing 	}
3880a69bfccSjsing }
389d313c28bSjsing 
390d313c28bSjsing void
bio_inq(char * name)3918ccdd032Sderaadt bio_inq(char *name)
392d4546a56Sdlg {
393cc711184Skettenis 	char 			*status, *cache;
394cc711184Skettenis 	char			size[64], scsiname[16], volname[32];
395d865b7d2Suebayasi 	char			percent[20], seconds[20];
396d313c28bSjsing 	int			i, d, volheader, hotspare, unused;
397aa65acf1Sderaadt 	char			encname[16], serial[32];
3988ccdd032Sderaadt 	struct bioc_inq		bi;
3998ccdd032Sderaadt 	struct bioc_vol		bv;
400c2b1f828Sderaadt 	struct bioc_disk	bd;
401db2730c1Smarco 
402db2730c1Smarco 	memset(&bi, 0, sizeof(bi));
4033af9de98Smarco 
404545c4d7fSjsing 	bi.bi_bio.bio_cookie = bio_cookie;
4053af9de98Smarco 
406df69c215Sderaadt 	if (ioctl(devh, BIOCINQ, &bi) == -1) {
40703b2dfbfShenning 		if (errno == ENOTTY)
40803b2dfbfShenning 			bio_diskinq(name);
40903b2dfbfShenning 		else
410da3b0664Shenning 			err(1, "BIOCINQ");
4113af9de98Smarco 		return;
4123af9de98Smarco 	}
4133af9de98Smarco 
414d313c28bSjsing 	bio_status(&bi.bi_bio.bio_status);
415d313c28bSjsing 
4168ccdd032Sderaadt 	volheader = 0;
4178ccdd032Sderaadt 	for (i = 0; i < bi.bi_novol; i++) {
418db2730c1Smarco 		memset(&bv, 0, sizeof(bv));
419545c4d7fSjsing 		bv.bv_bio.bio_cookie = bio_cookie;
4208ccdd032Sderaadt 		bv.bv_volid = i;
4210a92ff65Sderaadt 		bv.bv_percent = -1;
4229017fb97Sderaadt 		bv.bv_seconds = 0;
42370a2ae7bSmarco 
424df69c215Sderaadt 		if (ioctl(devh, BIOCVOL, &bv) == -1)
425da3b0664Shenning 			err(1, "BIOCVOL");
4263af9de98Smarco 
427d313c28bSjsing 		bio_status(&bv.bv_bio.bio_status);
428d313c28bSjsing 
4298ccdd032Sderaadt 		if (name && strcmp(name, bv.bv_dev) != 0)
4308ccdd032Sderaadt 			continue;
4318ccdd032Sderaadt 
4328ccdd032Sderaadt 		if (!volheader) {
4338ccdd032Sderaadt 			volheader = 1;
434150c22bbSjcs 			printf("%-11s %-10s %14s %-8s\n",
4358ccdd032Sderaadt 			    "Volume", "Status", "Size", "Device");
4368ccdd032Sderaadt 		}
4378ccdd032Sderaadt 
4380a92ff65Sderaadt 		percent[0] = '\0';
4399017fb97Sderaadt 		seconds[0] = '\0';
4400a92ff65Sderaadt 		if (bv.bv_percent != -1)
4410a92ff65Sderaadt 			snprintf(percent, sizeof percent,
4420a92ff65Sderaadt 			    " %d%% done", bv.bv_percent);
4439017fb97Sderaadt 		if (bv.bv_seconds)
4449017fb97Sderaadt 			snprintf(seconds, sizeof seconds,
4459017fb97Sderaadt 			    " %u seconds", bv.bv_seconds);
4468ccdd032Sderaadt 		switch (bv.bv_status) {
447db2730c1Smarco 		case BIOC_SVONLINE:
4488ccdd032Sderaadt 			status = BIOC_SVONLINE_S;
449db2730c1Smarco 			break;
450db2730c1Smarco 		case BIOC_SVOFFLINE:
4518ccdd032Sderaadt 			status = BIOC_SVOFFLINE_S;
452db2730c1Smarco 			break;
453db2730c1Smarco 		case BIOC_SVDEGRADED:
4548ccdd032Sderaadt 			status = BIOC_SVDEGRADED_S;
455db2730c1Smarco 			break;
4560a92ff65Sderaadt 		case BIOC_SVBUILDING:
4570a92ff65Sderaadt 			status = BIOC_SVBUILDING_S;
4580a92ff65Sderaadt 			break;
4590a92ff65Sderaadt 		case BIOC_SVREBUILD:
4600a92ff65Sderaadt 			status = BIOC_SVREBUILD_S;
4610a92ff65Sderaadt 			break;
4620a92ff65Sderaadt 		case BIOC_SVSCRUB:
4630a92ff65Sderaadt 			status = BIOC_SVSCRUB_S;
4640a92ff65Sderaadt 			break;
465db2730c1Smarco 		case BIOC_SVINVALID:
466db2730c1Smarco 		default:
4678ccdd032Sderaadt 			status = BIOC_SVINVALID_S;
468d4546a56Sdlg 		}
469cc711184Skettenis 		switch (bv.bv_cache) {
470cc711184Skettenis 		case BIOC_CVWRITEBACK:
471cc711184Skettenis 			cache = BIOC_CVWRITEBACK_S;
472cc711184Skettenis 			break;
473cc711184Skettenis 		case BIOC_CVWRITETHROUGH:
474cc711184Skettenis 			cache = BIOC_CVWRITETHROUGH_S;
475cc711184Skettenis 			break;
476cc711184Skettenis 		case BIOC_CVUNKNOWN:
477cc711184Skettenis 		default:
478cc711184Skettenis 			cache = BIOC_CVUNKNOWN_S;
479cc711184Skettenis 		}
4803af9de98Smarco 
481aa65acf1Sderaadt 		snprintf(volname, sizeof volname, "%s %u",
4828ccdd032Sderaadt 		    bi.bi_dev, bv.bv_volid);
483b9950701Smarco 
4849ecba717Sderaadt 		unused = 0;
4859ecba717Sderaadt 		hotspare = 0;
486aa65acf1Sderaadt 		if (bv.bv_level == -1 && bv.bv_nodisk == 1)
487aa65acf1Sderaadt 			hotspare = 1;
488b9950701Smarco 		else if (bv.bv_level == -2 && bv.bv_nodisk == 1)
489b9950701Smarco 			unused = 1;
490aa65acf1Sderaadt 		else {
4918ccdd032Sderaadt 			if (human)
4928ccdd032Sderaadt 				fmt_scaled(bv.bv_size, size);
4938ccdd032Sderaadt 			else
4948ccdd032Sderaadt 				snprintf(size, sizeof size, "%14llu",
4958ccdd032Sderaadt 				    bv.bv_size);
496d5e6461fSkn 			printf("%11s %-10s %14s %-7s ",
497d5e6461fSkn 			    volname, status, size, bv.bv_dev);
498da935596Stodd 			switch (bv.bv_level) {
499da935596Stodd 			case 'C':
500d5e6461fSkn 				printf("CRYPTO%s%s\n",
501da935596Stodd 				    percent, seconds);
502da935596Stodd 				break;
5032b5fc845Sjsing 			case 'c':
504d5e6461fSkn 				printf("CONCAT%s%s\n",
5052b5fc845Sjsing 				    percent, seconds);
5062b5fc845Sjsing 				break;
50701de20b5Sstsp 			case 0x1C:
50833c65cd8Skn 			case 0x1E:
509d5e6461fSkn 				printf("RAID%X%s%s %s\n",
51001de20b5Sstsp 				    bv.bv_level, percent, seconds, cache);
51101de20b5Sstsp 				break;
512da935596Stodd 			default:
513d5e6461fSkn 				printf("RAID%u%s%s %s\n",
514cc711184Skettenis 				    bv.bv_level, percent, seconds, cache);
515da935596Stodd 				break;
516da935596Stodd 			}
517da935596Stodd 
518aa65acf1Sderaadt 		}
5198ccdd032Sderaadt 
5208ccdd032Sderaadt 		for (d = 0; d < bv.bv_nodisk; d++) {
521db2730c1Smarco 			memset(&bd, 0, sizeof(bd));
522545c4d7fSjsing 			bd.bd_bio.bio_cookie = bio_cookie;
5238ccdd032Sderaadt 			bd.bd_diskid = d;
5248ccdd032Sderaadt 			bd.bd_volid = i;
525d865b7d2Suebayasi 			bd.bd_patrol.bdp_percent = -1;
526d865b7d2Suebayasi 			bd.bd_patrol.bdp_seconds = 0;
5273af9de98Smarco 
528df69c215Sderaadt 			if (ioctl(devh, BIOCDISK, &bd) == -1)
529da3b0664Shenning 				err(1, "BIOCDISK");
5303af9de98Smarco 
531d313c28bSjsing 			bio_status(&bd.bd_bio.bio_status);
532d313c28bSjsing 
5338ccdd032Sderaadt 			switch (bd.bd_status) {
534db2730c1Smarco 			case BIOC_SDONLINE:
5358ccdd032Sderaadt 				status = BIOC_SDONLINE_S;
536d4546a56Sdlg 				break;
537db2730c1Smarco 			case BIOC_SDOFFLINE:
5388ccdd032Sderaadt 				status = BIOC_SDOFFLINE_S;
539d4546a56Sdlg 				break;
540db2730c1Smarco 			case BIOC_SDFAILED:
5418ccdd032Sderaadt 				status = BIOC_SDFAILED_S;
542db2730c1Smarco 				break;
543db2730c1Smarco 			case BIOC_SDREBUILD:
5448ccdd032Sderaadt 				status = BIOC_SDREBUILD_S;
545db2730c1Smarco 				break;
546db2730c1Smarco 			case BIOC_SDHOTSPARE:
5478ccdd032Sderaadt 				status = BIOC_SDHOTSPARE_S;
548db2730c1Smarco 				break;
549db2730c1Smarco 			case BIOC_SDUNUSED:
5508ccdd032Sderaadt 				status = BIOC_SDUNUSED_S;
551db2730c1Smarco 				break;
552e1dfb373Sderaadt 			case BIOC_SDSCRUB:
553e1dfb373Sderaadt 				status = BIOC_SDSCRUB_S;
554e1dfb373Sderaadt 				break;
555db2730c1Smarco 			case BIOC_SDINVALID:
556d4546a56Sdlg 			default:
5578ccdd032Sderaadt 				status = BIOC_SDINVALID_S;
558d4546a56Sdlg 			}
559aa65acf1Sderaadt 
560b9950701Smarco 			if (hotspare || unused)
561aa65acf1Sderaadt 				;	/* use volname from parent volume */
562aa65acf1Sderaadt 			else
563aa65acf1Sderaadt 				snprintf(volname, sizeof volname, "    %3u",
564aa65acf1Sderaadt 				    bd.bd_diskid);
565aa65acf1Sderaadt 
5660054cd36Sjsing 			if (bv.bv_level == 'C' && bd.bd_size == 0)
5670054cd36Sjsing 				snprintf(size, sizeof size, "%14s", "key disk");
5680054cd36Sjsing 			else if (human)
5698ccdd032Sderaadt 				fmt_scaled(bd.bd_size, size);
5708ccdd032Sderaadt 			else
5718ccdd032Sderaadt 				snprintf(size, sizeof size, "%14llu",
5728ccdd032Sderaadt 				    bd.bd_size);
5738ccdd032Sderaadt 			snprintf(scsiname, sizeof scsiname,
57443d61178Sderaadt 			    "%u:%u.%u",
57543d61178Sderaadt 			    bd.bd_channel, bd.bd_target, bd.bd_lun);
5765978b28dSderaadt 			if (bd.bd_procdev[0])
577abe9d68eSderaadt 				strlcpy(encname, bd.bd_procdev, sizeof encname);
5785978b28dSderaadt 			else
579abe9d68eSderaadt 				strlcpy(encname, "noencl", sizeof encname);
580abe9d68eSderaadt 			if (bd.bd_serial[0])
581abe9d68eSderaadt 				strlcpy(serial, bd.bd_serial, sizeof serial);
582abe9d68eSderaadt 			else
583abe9d68eSderaadt 				strlcpy(serial, "unknown serial", sizeof serial);
5848ccdd032Sderaadt 
585d865b7d2Suebayasi 			percent[0] = '\0';
586d865b7d2Suebayasi 			seconds[0] = '\0';
587d865b7d2Suebayasi 			if (bd.bd_patrol.bdp_percent != -1)
588d865b7d2Suebayasi 				snprintf(percent, sizeof percent,
589d865b7d2Suebayasi 				    " patrol %d%% done", bd.bd_patrol.bdp_percent);
590d865b7d2Suebayasi 			if (bd.bd_patrol.bdp_seconds)
591d865b7d2Suebayasi 				snprintf(seconds, sizeof seconds,
592d865b7d2Suebayasi 				    " %u seconds", bd.bd_patrol.bdp_seconds);
593d865b7d2Suebayasi 
594150c22bbSjcs 			printf("%11s %-10s %14s %-7s %-6s <%s>\n",
595aa65acf1Sderaadt 			    volname, status, size, scsiname, encname,
5968ccdd032Sderaadt 			    bd.bd_vendor);
597abe9d68eSderaadt 			if (verbose)
598d865b7d2Suebayasi 				printf("%11s %-10s %14s %-7s %-6s '%s'%s%s\n",
599d865b7d2Suebayasi 				    "", "", "", "", "", serial, percent, seconds);
600d4546a56Sdlg 		}
601d4546a56Sdlg 	}
602d4546a56Sdlg }
603edfd9792Smarco 
604edfd9792Smarco void
bio_alarm(char * arg)605edfd9792Smarco bio_alarm(char *arg)
606edfd9792Smarco {
6078ccdd032Sderaadt 	struct bioc_alarm	ba;
608edfd9792Smarco 
609c2b1f828Sderaadt 	memset(&ba, 0, sizeof(ba));
610545c4d7fSjsing 	ba.ba_bio.bio_cookie = bio_cookie;
611edfd9792Smarco 
612edfd9792Smarco 	switch (arg[0]) {
613edfd9792Smarco 	case 'q': /* silence alarm */
614edfd9792Smarco 		/* FALLTHROUGH */
615edfd9792Smarco 	case 's':
6168ccdd032Sderaadt 		ba.ba_opcode = BIOC_SASILENCE;
617edfd9792Smarco 		break;
618edfd9792Smarco 
619edfd9792Smarco 	case 'e': /* enable alarm */
6208ccdd032Sderaadt 		ba.ba_opcode = BIOC_SAENABLE;
621edfd9792Smarco 		break;
622edfd9792Smarco 
623edfd9792Smarco 	case 'd': /* disable alarm */
6248ccdd032Sderaadt 		ba.ba_opcode = BIOC_SADISABLE;
625edfd9792Smarco 		break;
626edfd9792Smarco 
627edfd9792Smarco 	case 't': /* test alarm */
6288ccdd032Sderaadt 		ba.ba_opcode = BIOC_SATEST;
629edfd9792Smarco 		break;
630edfd9792Smarco 
631edfd9792Smarco 	case 'g': /* get alarm state */
6328ccdd032Sderaadt 		ba.ba_opcode = BIOC_GASTATUS;
633edfd9792Smarco 		break;
634edfd9792Smarco 
635edfd9792Smarco 	default:
636da3b0664Shenning 		errx(1, "invalid alarm function: %s", arg);
637edfd9792Smarco 	}
638edfd9792Smarco 
639df69c215Sderaadt 	if (ioctl(devh, BIOCALARM, &ba) == -1)
640da3b0664Shenning 		err(1, "BIOCALARM");
641edfd9792Smarco 
642d313c28bSjsing 	bio_status(&ba.ba_bio.bio_status);
643d313c28bSjsing 
644d313c28bSjsing 	if (arg[0] == 'g')
645edfd9792Smarco 		printf("alarm is currently %s\n",
6468ccdd032Sderaadt 		    ba.ba_status ? "enabled" : "disabled");
647edfd9792Smarco }
6486de960dcSmarco 
649a15048bbSmarco int
bio_getvolbyname(char * name)650a15048bbSmarco bio_getvolbyname(char *name)
651a15048bbSmarco {
652d313c28bSjsing 	int			id = -1, i;
653a15048bbSmarco 	struct bioc_inq		bi;
654a15048bbSmarco 	struct bioc_vol		bv;
655a15048bbSmarco 
656a15048bbSmarco 	memset(&bi, 0, sizeof(bi));
657545c4d7fSjsing 	bi.bi_bio.bio_cookie = bio_cookie;
658df69c215Sderaadt 	if (ioctl(devh, BIOCINQ, &bi) == -1)
659a15048bbSmarco 		err(1, "BIOCINQ");
660a15048bbSmarco 
661d313c28bSjsing 	bio_status(&bi.bi_bio.bio_status);
662d313c28bSjsing 
663a15048bbSmarco 	for (i = 0; i < bi.bi_novol; i++) {
664a15048bbSmarco 		memset(&bv, 0, sizeof(bv));
665545c4d7fSjsing 		bv.bv_bio.bio_cookie = bio_cookie;
666a15048bbSmarco 		bv.bv_volid = i;
667df69c215Sderaadt 		if (ioctl(devh, BIOCVOL, &bv) == -1)
668a15048bbSmarco 			err(1, "BIOCVOL");
669a15048bbSmarco 
670d313c28bSjsing 		bio_status(&bv.bv_bio.bio_status);
671d313c28bSjsing 
672a15048bbSmarco 		if (name && strcmp(name, bv.bv_dev) != 0)
673a15048bbSmarco 			continue;
674a15048bbSmarco 		id = i;
675a15048bbSmarco 		break;
676a15048bbSmarco 	}
677a15048bbSmarco 
678a15048bbSmarco 	return (id);
679a15048bbSmarco }
680a15048bbSmarco 
681ebaf584eSderaadt void
bio_setstate(char * arg,int status,char * devicename)6828d8693a2Sdtucker bio_setstate(char *arg, int status, char *devicename)
6836de960dcSmarco {
6846de960dcSmarco 	struct bioc_setstate	bs;
6856de960dcSmarco 	struct locator		location;
686a15048bbSmarco 	struct stat		sb;
68741eccc89Sderaadt 	const char		*errstr;
6886de960dcSmarco 
689a15048bbSmarco 	memset(&bs, 0, sizeof(bs));
690a15048bbSmarco 	if (stat(arg, &sb) == -1) {
691a15048bbSmarco 		/* use CTL */
69241eccc89Sderaadt 		errstr = str2locator(arg, &location);
69341eccc89Sderaadt 		if (errstr)
69441eccc89Sderaadt 			errx(1, "Target %s: %s", arg, errstr);
6956de960dcSmarco 		bs.bs_channel = location.channel;
6966de960dcSmarco 		bs.bs_target = location.target;
6976de960dcSmarco 		bs.bs_lun = location.lun;
698a15048bbSmarco 	} else {
699a15048bbSmarco 		/* use other id */
700a15048bbSmarco 		bs.bs_other_id = sb.st_rdev;
701a15048bbSmarco 		bs.bs_other_id_type = BIOC_SSOTHER_DEVT;
702a15048bbSmarco 	}
703a15048bbSmarco 
704545c4d7fSjsing 	bs.bs_bio.bio_cookie = bio_cookie;
705a15048bbSmarco 	bs.bs_status = status;
706a15048bbSmarco 
707d2647ac1Sjsing 	if (status != BIOC_SSHOTSPARE) {
708a15048bbSmarco 		/* make sure user supplied a sd device */
7098d8693a2Sdtucker 		bs.bs_volid = bio_getvolbyname(devicename);
710a15048bbSmarco 		if (bs.bs_volid == -1)
7118d8693a2Sdtucker 			errx(1, "invalid device %s", devicename);
712d2647ac1Sjsing 	}
7136de960dcSmarco 
714df69c215Sderaadt 	if (ioctl(devh, BIOCSETSTATE, &bs) == -1)
715da3b0664Shenning 		err(1, "BIOCSETSTATE");
716d313c28bSjsing 
717d313c28bSjsing 	bio_status(&bs.bs_bio.bio_status);
7186de960dcSmarco }
719c55617f1Sdlg 
720c55617f1Sdlg void
bio_setblink(char * name,char * arg,int blink)721a928c459Sderaadt bio_setblink(char *name, char *arg, int blink)
722c55617f1Sdlg {
72350d3c4dcSdlg 	struct locator		location;
724c2b1f828Sderaadt 	struct bioc_blink	bb;
72550d3c4dcSdlg 	struct bioc_inq		bi;
72650d3c4dcSdlg 	struct bioc_vol		bv;
72750d3c4dcSdlg 	struct bioc_disk	bd;
72841eccc89Sderaadt 	const char		*errstr;
72950d3c4dcSdlg 	int			v, d, rv;
730c55617f1Sdlg 
73141eccc89Sderaadt 	errstr = str2locator(arg, &location);
73241eccc89Sderaadt 	if (errstr)
73341eccc89Sderaadt 		errx(1, "Target %s: %s", arg, errstr);
73450d3c4dcSdlg 
7350505205bSdlg 	/* try setting blink on the device directly */
7360505205bSdlg 	memset(&bb, 0, sizeof(bb));
737545c4d7fSjsing 	bb.bb_bio.bio_cookie = bio_cookie;
7380505205bSdlg 	bb.bb_status = blink;
7390505205bSdlg 	bb.bb_target = location.target;
740649724a4Smarco 	bb.bb_channel = location.channel;
7410505205bSdlg 	rv = ioctl(devh, BIOCBLINK, &bb);
742d313c28bSjsing 
743d313c28bSjsing 	if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_UNKNOWN)
7440505205bSdlg 		return;
7450505205bSdlg 
746d313c28bSjsing 	if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_SUCCESS) {
747d313c28bSjsing 		bio_status(&bb.bb_bio.bio_status);
748d313c28bSjsing 		return;
749d313c28bSjsing 	}
750d313c28bSjsing 
751855d4e83Ssobrado 	/* if the blink didn't work, try to find something that will */
7520505205bSdlg 
75350d3c4dcSdlg 	memset(&bi, 0, sizeof(bi));
754545c4d7fSjsing 	bi.bi_bio.bio_cookie = bio_cookie;
755df69c215Sderaadt 	if (ioctl(devh, BIOCINQ, &bi) == -1)
756da3b0664Shenning 		err(1, "BIOCINQ");
75750d3c4dcSdlg 
758d313c28bSjsing 	bio_status(&bi.bi_bio.bio_status);
759d313c28bSjsing 
76050d3c4dcSdlg 	for (v = 0; v < bi.bi_novol; v++) {
76150d3c4dcSdlg 		memset(&bv, 0, sizeof(bv));
762545c4d7fSjsing 		bv.bv_bio.bio_cookie = bio_cookie;
76350d3c4dcSdlg 		bv.bv_volid = v;
764df69c215Sderaadt 		if (ioctl(devh, BIOCVOL, &bv) == -1)
765da3b0664Shenning 			err(1, "BIOCVOL");
76650d3c4dcSdlg 
767d313c28bSjsing 		bio_status(&bv.bv_bio.bio_status);
768d313c28bSjsing 
76950d3c4dcSdlg 		if (name && strcmp(name, bv.bv_dev) != 0)
77050d3c4dcSdlg 			continue;
77150d3c4dcSdlg 
77250d3c4dcSdlg 		for (d = 0; d < bv.bv_nodisk; d++) {
77350d3c4dcSdlg 			memset(&bd, 0, sizeof(bd));
774545c4d7fSjsing 			bd.bd_bio.bio_cookie = bio_cookie;
77550d3c4dcSdlg 			bd.bd_volid = v;
77650d3c4dcSdlg 			bd.bd_diskid = d;
77750d3c4dcSdlg 
778df69c215Sderaadt 			if (ioctl(devh, BIOCDISK, &bd) == -1)
779da3b0664Shenning 				err(1, "BIOCDISK");
78050d3c4dcSdlg 
781d313c28bSjsing 			bio_status(&bd.bd_bio.bio_status);
782d313c28bSjsing 
78350d3c4dcSdlg 			if (bd.bd_channel == location.channel &&
78450d3c4dcSdlg 			    bd.bd_target == location.target &&
78550d3c4dcSdlg 			    bd.bd_lun == location.lun) {
786d313c28bSjsing 				if (bd.bd_procdev[0] != '\0')
78750d3c4dcSdlg 					bio_blink(bd.bd_procdev,
788a928c459Sderaadt 					    location.target, blink);
789d313c28bSjsing 				else
790d313c28bSjsing 					warnx("Disk %s is not in an enclosure",
791d313c28bSjsing 					    arg);
79250d3c4dcSdlg 				return;
79350d3c4dcSdlg 			}
79450d3c4dcSdlg 		}
79550d3c4dcSdlg 	}
79650d3c4dcSdlg 
79741eccc89Sderaadt 	warnx("Disk %s does not exist", arg);
79850d3c4dcSdlg }
79950d3c4dcSdlg 
80050d3c4dcSdlg void
bio_blink(char * enclosure,int target,int blinktype)801a928c459Sderaadt bio_blink(char *enclosure, int target, int blinktype)
80250d3c4dcSdlg {
80350d3c4dcSdlg 	int			bioh;
804d313c28bSjsing 	struct bio_locate	bl;
80550d3c4dcSdlg 	struct bioc_blink	blink;
80650d3c4dcSdlg 
80741eccc89Sderaadt 	bioh = open("/dev/bio", O_RDWR);
80850d3c4dcSdlg 	if (bioh == -1)
80941eccc89Sderaadt 		err(1, "Can't open %s", "/dev/bio");
81050d3c4dcSdlg 
811c2b1f828Sderaadt 	memset(&bl, 0, sizeof(bl));
812d313c28bSjsing 	bl.bl_name = enclosure;
813df69c215Sderaadt 	if (ioctl(bioh, BIOCLOCATE, &bl) == -1)
81441eccc89Sderaadt 		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
815c55617f1Sdlg 
816c55617f1Sdlg 	memset(&blink, 0, sizeof(blink));
817545c4d7fSjsing 	blink.bb_bio.bio_cookie = bio_cookie;
818a928c459Sderaadt 	blink.bb_status = blinktype;
819c55617f1Sdlg 	blink.bb_target = target;
820c55617f1Sdlg 
821df69c215Sderaadt 	if (ioctl(bioh, BIOCBLINK, &blink) == -1)
822da3b0664Shenning 		err(1, "BIOCBLINK");
82350d3c4dcSdlg 
824d313c28bSjsing 	bio_status(&blink.bb_bio.bio_status);
825d313c28bSjsing 
82650d3c4dcSdlg 	close(bioh);
827c55617f1Sdlg }
8287195049bSmarco 
8297195049bSmarco void
bio_createraid(u_int16_t level,char * dev_list,char * key_disk)8300054cd36Sjsing bio_createraid(u_int16_t level, char *dev_list, char *key_disk)
8317195049bSmarco {
8327195049bSmarco 	struct bioc_createraid	create;
833aef7fe28Shshoexer 	struct sr_crypto_kdfinfo kdfinfo;
8343487a6b1Sjsing 	struct sr_crypto_pbkdf	kdfhint;
8350054cd36Sjsing 	struct stat		sb;
8368b0d0f28Sjsing 	int			rv, no_dev, fd;
8377f8eae2bSnicm 	dev_t			*dt;
8387195049bSmarco 	u_int16_t		min_disks = 0;
8397195049bSmarco 
8407195049bSmarco 	if (!dev_list)
8417195049bSmarco 		errx(1, "no devices specified");
8427195049bSmarco 
843f9b0dfcfStedu 	dt = calloc(1, BIOC_CRMAXLEN);
84446bc198bSmarco 	if (!dt)
84546bc198bSmarco 		err(1, "not enough memory for dev_t list");
84646bc198bSmarco 
84746bc198bSmarco 	no_dev = bio_parse_devlist(dev_list, dt);
84846bc198bSmarco 
8497195049bSmarco 	switch (level) {
8507195049bSmarco 	case 0:
85184e48fabSmarco 		min_disks = 2;
8527195049bSmarco 		break;
8537195049bSmarco 	case 1:
8547195049bSmarco 		min_disks = 2;
8557195049bSmarco 		break;
856e717853eSmarco 	case 5:
857e717853eSmarco 		min_disks = 3;
858e717853eSmarco 		break;
85984e48fabSmarco 	case 'C':
86001de20b5Sstsp 	case 0x1C:
861aef7fe28Shshoexer 		min_disks = 1;
86284e48fabSmarco 		break;
86398b750e4Stedu 	case 'c':
864b346a95bSkrw 		min_disks = 1;
86598b750e4Stedu 		break;
8667195049bSmarco 	default:
86724b6f6bcSkn 		errx(1, "unsupported RAID level");
8687195049bSmarco 	}
8697195049bSmarco 
87084e48fabSmarco 	if (no_dev < min_disks)
87184e48fabSmarco 		errx(1, "not enough disks");
87284e48fabSmarco 
873aef7fe28Shshoexer 	/* for crypto raid we only allow one single chunk */
874aef7fe28Shshoexer 	if (level == 'C' && no_dev != min_disks)
875818b0595Shalex 		errx(1, "not exactly one partition");
876aef7fe28Shshoexer 
8777195049bSmarco 	memset(&create, 0, sizeof(create));
878545c4d7fSjsing 	create.bc_bio.bio_cookie = bio_cookie;
8797195049bSmarco 	create.bc_level = level;
88046bc198bSmarco 	create.bc_dev_list_len = no_dev * sizeof(dev_t);
88146bc198bSmarco 	create.bc_dev_list = dt;
882e8a57fdeSmarco 	create.bc_flags = BIOC_SCDEVT | cflags;
8830054cd36Sjsing 	create.bc_key_disk = NODEV;
8847195049bSmarco 
88501de20b5Sstsp 	if ((level == 'C' || level == 0x1C) && key_disk == NULL) {
8860054cd36Sjsing 
887aef7fe28Shshoexer 		memset(&kdfinfo, 0, sizeof(kdfinfo));
888aef7fe28Shshoexer 		memset(&kdfhint, 0, sizeof(kdfhint));
889aef7fe28Shshoexer 
8900054cd36Sjsing 		create.bc_flags |= BIOC_SCNOAUTOASSEMBLE;
8910054cd36Sjsing 
892aef7fe28Shshoexer 		create.bc_opaque = &kdfhint;
893aef7fe28Shshoexer 		create.bc_opaque_size = sizeof(kdfhint);
894aef7fe28Shshoexer 		create.bc_opaque_flags = BIOC_SOOUT;
895aef7fe28Shshoexer 
896aef7fe28Shshoexer 		/* try to get KDF hint */
897df69c215Sderaadt 		if (ioctl(devh, BIOCCREATERAID, &create) == -1)
89883e979edShshoexer 			err(1, "ioctl");
89983e979edShshoexer 
900d313c28bSjsing 		bio_status(&create.bc_bio.bio_status);
901d313c28bSjsing 
90283e979edShshoexer 		if (create.bc_opaque_status == BIOC_SOINOUT_OK) {
903c6446370Sjsing 			bio_kdf_derive(&kdfinfo, &kdfhint, "Passphrase: ", 0);
904aef7fe28Shshoexer 			memset(&kdfhint, 0, sizeof(kdfhint));
905aef7fe28Shshoexer 		} else {
906aef7fe28Shshoexer 			bio_kdf_generate(&kdfinfo);
907aef7fe28Shshoexer 		}
908aef7fe28Shshoexer 
909aef7fe28Shshoexer 		create.bc_opaque = &kdfinfo;
910aef7fe28Shshoexer 		create.bc_opaque_size = sizeof(kdfinfo);
911aef7fe28Shshoexer 		create.bc_opaque_flags = BIOC_SOIN;
9120054cd36Sjsing 
91301de20b5Sstsp 	} else if ((level == 'C' || level == 0x1C) && key_disk != NULL) {
9140054cd36Sjsing 
9158b0d0f28Sjsing 		/* Get device number for key disk. */
9168b0d0f28Sjsing 		fd = opendev(key_disk, O_RDONLY, OPENDEV_BLCK, NULL);
9178b0d0f28Sjsing 		if (fd == -1)
9188b0d0f28Sjsing 			err(1, "could not open %s", key_disk);
9198b0d0f28Sjsing 		if (fstat(fd, &sb) == -1) {
920ffb4dd05Sguenther 			int saved_errno = errno;
9218b0d0f28Sjsing 			close(fd);
922ffb4dd05Sguenther 			errc(1, saved_errno, "could not stat %s", key_disk);
9238b0d0f28Sjsing 		}
9248b0d0f28Sjsing 		close(fd);
9250054cd36Sjsing 		create.bc_key_disk = sb.st_rdev;
9260054cd36Sjsing 
9270054cd36Sjsing 		memset(&kdfinfo, 0, sizeof(kdfinfo));
9280054cd36Sjsing 
9290054cd36Sjsing 		kdfinfo.genkdf.len = sizeof(kdfinfo.genkdf);
9300054cd36Sjsing 		kdfinfo.genkdf.type = SR_CRYPTOKDFT_KEYDISK;
9310054cd36Sjsing 		kdfinfo.len = sizeof(kdfinfo);
9320054cd36Sjsing 		kdfinfo.flags = SR_CRYPTOKDF_HINT;
9330054cd36Sjsing 
9340054cd36Sjsing 		create.bc_opaque = &kdfinfo;
9350054cd36Sjsing 		create.bc_opaque_size = sizeof(kdfinfo);
9360054cd36Sjsing 		create.bc_opaque_flags = BIOC_SOIN;
9370054cd36Sjsing 
938aef7fe28Shshoexer 	}
939aef7fe28Shshoexer 
9407195049bSmarco 	rv = ioctl(devh, BIOCCREATERAID, &create);
9410a488504Spelikan 	explicit_bzero(&kdfinfo, sizeof(kdfinfo));
942d313c28bSjsing 	if (rv == -1)
943da3b0664Shenning 		err(1, "BIOCCREATERAID");
944d313c28bSjsing 
945d313c28bSjsing 	bio_status(&create.bc_bio.bio_status);
94646bc198bSmarco 
94746bc198bSmarco 	free(dt);
94846bc198bSmarco }
94946bc198bSmarco 
950aef7fe28Shshoexer void
bio_kdf_derive(struct sr_crypto_kdfinfo * kdfinfo,struct sr_crypto_pbkdf * kdfhint,char * prompt,int verify)9513487a6b1Sjsing bio_kdf_derive(struct sr_crypto_kdfinfo *kdfinfo, struct sr_crypto_pbkdf
952c6446370Sjsing     *kdfhint, char* prompt, int verify)
953aef7fe28Shshoexer {
954aef7fe28Shshoexer 	if (!kdfinfo)
955aef7fe28Shshoexer 		errx(1, "invalid KDF info");
956aef7fe28Shshoexer 	if (!kdfhint)
957aef7fe28Shshoexer 		errx(1, "invalid KDF hint");
958aef7fe28Shshoexer 
9593487a6b1Sjsing 	if (kdfhint->generic.len != sizeof(*kdfhint))
960aef7fe28Shshoexer 		errx(1, "KDF hint has invalid size");
961aef7fe28Shshoexer 
962aef7fe28Shshoexer 	kdfinfo->flags = SR_CRYPTOKDF_KEY;
963aef7fe28Shshoexer 	kdfinfo->len = sizeof(*kdfinfo);
964aef7fe28Shshoexer 
9653487a6b1Sjsing 	derive_key(kdfhint->generic.type, kdfhint->rounds,
966aef7fe28Shshoexer 	    kdfinfo->maskkey, sizeof(kdfinfo->maskkey),
96761f93244Sjsing 	    kdfhint->salt, sizeof(kdfhint->salt),
96861f93244Sjsing 	    prompt, verify);
969aef7fe28Shshoexer }
970aef7fe28Shshoexer 
971aef7fe28Shshoexer void
bio_kdf_generate(struct sr_crypto_kdfinfo * kdfinfo)972aef7fe28Shshoexer bio_kdf_generate(struct sr_crypto_kdfinfo *kdfinfo)
973aef7fe28Shshoexer {
974aef7fe28Shshoexer 	if (!kdfinfo)
975aef7fe28Shshoexer 		errx(1, "invalid KDF info");
976aef7fe28Shshoexer 
977ef0eb24eSjsing 	if (rflag == -1)
978ef0eb24eSjsing 		rflag = bcrypt_pbkdf_autorounds();
979ef0eb24eSjsing 
9803487a6b1Sjsing 	kdfinfo->pbkdf.generic.len = sizeof(kdfinfo->pbkdf);
9812ba69c71Sjsing 	kdfinfo->pbkdf.generic.type = SR_CRYPTOKDFT_BCRYPT_PBKDF;
982b4f9a699Skn 	kdfinfo->pbkdf.rounds = rflag;
98361f93244Sjsing 
9840054cd36Sjsing 	kdfinfo->flags = SR_CRYPTOKDF_KEY | SR_CRYPTOKDF_HINT;
98561f93244Sjsing 	kdfinfo->len = sizeof(*kdfinfo);
986aef7fe28Shshoexer 
987aef7fe28Shshoexer 	/* generate salt */
9883487a6b1Sjsing 	arc4random_buf(kdfinfo->pbkdf.salt, sizeof(kdfinfo->pbkdf.salt));
989aef7fe28Shshoexer 
9903487a6b1Sjsing 	derive_key(kdfinfo->pbkdf.generic.type, kdfinfo->pbkdf.rounds,
991aef7fe28Shshoexer 	    kdfinfo->maskkey, sizeof(kdfinfo->maskkey),
9923487a6b1Sjsing 	    kdfinfo->pbkdf.salt, sizeof(kdfinfo->pbkdf.salt),
9935f219970Skn 	    "New passphrase: ", interactive);
994aef7fe28Shshoexer }
995aef7fe28Shshoexer 
99646bc198bSmarco int
bio_parse_devlist(char * lst,dev_t * dt)99746bc198bSmarco bio_parse_devlist(char *lst, dev_t *dt)
99846bc198bSmarco {
99946bc198bSmarco 	char			*s, *e;
100046bc198bSmarco 	u_int32_t		sz = 0;
100146bc198bSmarco 	int			no_dev = 0, i, x;
100246bc198bSmarco 	struct stat		sb;
1003b9fc9a72Sderaadt 	char			dev[PATH_MAX];
1004e37c64dbSjsing 	int			fd;
100546bc198bSmarco 
100646bc198bSmarco 	if (!lst)
100746bc198bSmarco 		errx(1, "invalid device list");
100846bc198bSmarco 
100946bc198bSmarco 	s = e = lst;
101046bc198bSmarco 	/* make sure we have a valid device list like /dev/sdNa,/dev/sdNNa */
101146bc198bSmarco 	while (*e != '\0') {
101246bc198bSmarco 		if (*e == ',')
101346bc198bSmarco 			s = e + 1;
101446bc198bSmarco 		else if (*(e + 1) == '\0' || *(e + 1) == ',') {
101546bc198bSmarco 			/* got one */
101646bc198bSmarco 			sz = e - s + 1;
10175c1f8f6bSdjm 			strlcpy(dev, s, sz + 1);
1018e37c64dbSjsing 			fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL);
1019e37c64dbSjsing 			if (fd == -1)
1020e37c64dbSjsing 				err(1, "could not open %s", dev);
1021e37c64dbSjsing 			if (fstat(fd, &sb) == -1) {
1022ffb4dd05Sguenther 				int saved_errno = errno;
1023e37c64dbSjsing 				close(fd);
1024ffb4dd05Sguenther 				errc(1, saved_errno, "could not stat %s", dev);
1025e37c64dbSjsing 			}
1026e37c64dbSjsing 			close(fd);
102746bc198bSmarco 			dt[no_dev] = sb.st_rdev;
102846bc198bSmarco 			no_dev++;
10295c1f8f6bSdjm 			if (no_dev > (int)(BIOC_CRMAXLEN / sizeof(dev_t)))
103046bc198bSmarco 				errx(1, "too many devices on device list");
103146bc198bSmarco 		}
103246bc198bSmarco 		e++;
103346bc198bSmarco 	}
103446bc198bSmarco 
103546bc198bSmarco 	for (i = 0; i < no_dev; i++)
103646bc198bSmarco 		for (x = 0; x < no_dev; x++)
103746bc198bSmarco 			if (dt[i] == dt[x] && x != i)
103846bc198bSmarco 				errx(1, "duplicate device in list");
103946bc198bSmarco 
104046bc198bSmarco 	return (no_dev);
10417195049bSmarco }
1042e8a57fdeSmarco 
1043e8a57fdeSmarco u_int32_t
bio_createflags(char * lst)1044e8a57fdeSmarco bio_createflags(char *lst)
1045e8a57fdeSmarco {
1046e8a57fdeSmarco 	char			*s, *e, fs[32];
1047e8a57fdeSmarco 	u_int32_t		sz = 0;
1048e8a57fdeSmarco 	u_int32_t		flags = 0;
1049e8a57fdeSmarco 
1050e8a57fdeSmarco 	if (!lst)
1051e8a57fdeSmarco 		errx(1, "invalid flags list");
1052e8a57fdeSmarco 
1053e8a57fdeSmarco 	s = e = lst;
1054e8a57fdeSmarco 	/* make sure we have a valid flags list like force,noassemeble */
1055e8a57fdeSmarco 	while (*e != '\0') {
1056e8a57fdeSmarco 		if (*e == ',')
1057e8a57fdeSmarco 			s = e + 1;
1058e8a57fdeSmarco 		else if (*(e + 1) == '\0' || *(e + 1) == ',') {
1059e8a57fdeSmarco 			/* got one */
1060e8a57fdeSmarco 			sz = e - s + 1;
1061e8a57fdeSmarco 			switch (s[0]) {
1062e8a57fdeSmarco 			case 'f':
1063e8a57fdeSmarco 				flags |= BIOC_SCFORCE;
1064e8a57fdeSmarco 				break;
1065e8a57fdeSmarco 			case 'n':
1066e8a57fdeSmarco 				flags |= BIOC_SCNOAUTOASSEMBLE;
1067e8a57fdeSmarco 				break;
1068e8a57fdeSmarco 			default:
1069e8a57fdeSmarco 				strlcpy(fs, s, sz + 1);
1070e8a57fdeSmarco 				errx(1, "invalid flag %s", fs);
1071e8a57fdeSmarco 			}
1072e8a57fdeSmarco 		}
1073e8a57fdeSmarco 		e++;
1074e8a57fdeSmarco 	}
1075e8a57fdeSmarco 
1076e8a57fdeSmarco 	return (flags);
1077e8a57fdeSmarco }
107803b2dfbfShenning 
1079c7c3e8aaSmarco void
bio_deleteraid(char * dev)1080c7c3e8aaSmarco bio_deleteraid(char *dev)
1081c7c3e8aaSmarco {
1082c7c3e8aaSmarco 	struct bioc_deleteraid	bd;
1083c7c3e8aaSmarco 	memset(&bd, 0, sizeof(bd));
1084c7c3e8aaSmarco 
1085545c4d7fSjsing 	bd.bd_bio.bio_cookie = bio_cookie;
1086a15048bbSmarco 	/* XXX make this a dev_t instead of a string */
1087c7c3e8aaSmarco 	strlcpy(bd.bd_dev, dev, sizeof bd.bd_dev);
1088df69c215Sderaadt 	if (ioctl(devh, BIOCDELETERAID, &bd) == -1)
1089d313c28bSjsing 		err(1, "BIOCDELETERAID");
1090d313c28bSjsing 
1091d313c28bSjsing 	bio_status(&bd.bd_bio.bio_status);
1092c7c3e8aaSmarco }
1093c7c3e8aaSmarco 
1094c6446370Sjsing void
bio_changepass(char * dev)1095c6446370Sjsing bio_changepass(char *dev)
1096c6446370Sjsing {
1097c6446370Sjsing 	struct bioc_discipline bd;
1098c6446370Sjsing 	struct sr_crypto_kdfpair kdfpair;
1099c6446370Sjsing 	struct sr_crypto_kdfinfo kdfinfo1, kdfinfo2;
11003487a6b1Sjsing 	struct sr_crypto_pbkdf kdfhint;
1101c6446370Sjsing 	int rv;
1102c6446370Sjsing 
1103c6446370Sjsing 	memset(&bd, 0, sizeof(bd));
1104c6446370Sjsing 	memset(&kdfhint, 0, sizeof(kdfhint));
1105c6446370Sjsing 	memset(&kdfinfo1, 0, sizeof(kdfinfo1));
1106c6446370Sjsing 	memset(&kdfinfo2, 0, sizeof(kdfinfo2));
1107c6446370Sjsing 
1108c6446370Sjsing 	/* XXX use dev_t instead of string. */
1109c6446370Sjsing 	strlcpy(bd.bd_dev, dev, sizeof(bd.bd_dev));
1110c6446370Sjsing 	bd.bd_cmd = SR_IOCTL_GET_KDFHINT;
1111c6446370Sjsing 	bd.bd_size = sizeof(kdfhint);
1112c6446370Sjsing 	bd.bd_data = &kdfhint;
1113c6446370Sjsing 
1114df69c215Sderaadt 	if (ioctl(devh, BIOCDISCIPLINE, &bd) == -1)
1115d313c28bSjsing 		err(1, "BIOCDISCIPLINE");
1116d313c28bSjsing 
1117d313c28bSjsing 	bio_status(&bd.bd_bio.bio_status);
1118c6446370Sjsing 
1119c6446370Sjsing 	/* Current passphrase. */
1120c6446370Sjsing 	bio_kdf_derive(&kdfinfo1, &kdfhint, "Old passphrase: ", 0);
1121c6446370Sjsing 
1122b4f9a699Skn 	if (rflag == -1) {
1123b4f9a699Skn 		rflag = bcrypt_pbkdf_autorounds();
1124b4f9a699Skn 
1125b4f9a699Skn 		/* Use previous number of rounds for the same KDF if higher. */
1126b4f9a699Skn 		if (kdfhint.generic.type == SR_CRYPTOKDFT_BCRYPT_PBKDF &&
1127b4f9a699Skn 		    rflag < kdfhint.rounds)
11281f1fa78aSjsing 			rflag = kdfhint.rounds;
1129b4f9a699Skn 	}
11301f1fa78aSjsing 
1131c6446370Sjsing 	/* New passphrase. */
11322c98a0f7Sjsing 	bio_kdf_generate(&kdfinfo2);
1133c6446370Sjsing 
1134c6446370Sjsing 	kdfpair.kdfinfo1 = &kdfinfo1;
1135c6446370Sjsing 	kdfpair.kdfsize1 = sizeof(kdfinfo1);
1136c6446370Sjsing 	kdfpair.kdfinfo2 = &kdfinfo2;
1137c6446370Sjsing 	kdfpair.kdfsize2 = sizeof(kdfinfo2);
1138c6446370Sjsing 
1139c6446370Sjsing 	bd.bd_cmd = SR_IOCTL_CHANGE_PASSPHRASE;
1140c6446370Sjsing 	bd.bd_size = sizeof(kdfpair);
1141c6446370Sjsing 	bd.bd_data = &kdfpair;
1142c6446370Sjsing 
1143c6446370Sjsing 	rv = ioctl(devh, BIOCDISCIPLINE, &bd);
1144c6446370Sjsing 
1145c6446370Sjsing 	memset(&kdfhint, 0, sizeof(kdfhint));
11460a488504Spelikan 	explicit_bzero(&kdfinfo1, sizeof(kdfinfo1));
11470a488504Spelikan 	explicit_bzero(&kdfinfo2, sizeof(kdfinfo2));
1148c6446370Sjsing 
1149df69c215Sderaadt 	if (rv == -1)
1150d313c28bSjsing 		err(1, "BIOCDISCIPLINE");
1151d313c28bSjsing 
1152d313c28bSjsing 	bio_status(&bd.bd_bio.bio_status);
1153c6446370Sjsing }
1154c6446370Sjsing 
115503b2dfbfShenning #define BIOCTL_VIS_NBUF		4
115603b2dfbfShenning #define BIOCTL_VIS_BUFLEN	80
115703b2dfbfShenning 
115803b2dfbfShenning char *
bio_vis(char * s)115903b2dfbfShenning bio_vis(char *s)
116003b2dfbfShenning {
116103b2dfbfShenning 	static char	 rbuf[BIOCTL_VIS_NBUF][BIOCTL_VIS_BUFLEN];
116203b2dfbfShenning 	static uint	 idx = 0;
116303b2dfbfShenning 	char		*buf;
116403b2dfbfShenning 
116503b2dfbfShenning 	buf = rbuf[idx++];
116603b2dfbfShenning 	if (idx == BIOCTL_VIS_NBUF)
116703b2dfbfShenning 		idx = 0;
116803b2dfbfShenning 
116903b2dfbfShenning 	strnvis(buf, s, BIOCTL_VIS_BUFLEN, VIS_NL|VIS_CSTYLE);
117003b2dfbfShenning 	return (buf);
117103b2dfbfShenning }
117203b2dfbfShenning 
117303b2dfbfShenning void
bio_diskinq(char * sd_dev)117403b2dfbfShenning bio_diskinq(char *sd_dev)
117503b2dfbfShenning {
117603b2dfbfShenning 	struct dk_inquiry	di;
117703b2dfbfShenning 
1178da3b0664Shenning 	if (ioctl(devh, DIOCINQ, &di) == -1)
1179da3b0664Shenning 		err(1, "DIOCINQ");
118003b2dfbfShenning 
118103b2dfbfShenning 	printf("%s: <%s, %s, %s>, serial %s\n", sd_dev, bio_vis(di.vendor),
118203b2dfbfShenning 	    bio_vis(di.product), bio_vis(di.revision), bio_vis(di.serial));
118303b2dfbfShenning }
1184aef7fe28Shshoexer 
1185aef7fe28Shshoexer void
bio_patrol(char * arg)1186d865b7d2Suebayasi bio_patrol(char *arg)
1187d865b7d2Suebayasi {
1188d865b7d2Suebayasi 	struct bioc_patrol	bp;
1189d865b7d2Suebayasi 	struct timing		timing;
1190d865b7d2Suebayasi 	const char		*errstr;
1191d865b7d2Suebayasi 
1192d865b7d2Suebayasi 	memset(&bp, 0, sizeof(bp));
1193d865b7d2Suebayasi 	bp.bp_bio.bio_cookie = bio_cookie;
1194d865b7d2Suebayasi 
1195d865b7d2Suebayasi 	switch (arg[0]) {
1196d865b7d2Suebayasi 	case 'a':
1197d865b7d2Suebayasi 		bp.bp_opcode = BIOC_SPAUTO;
1198d865b7d2Suebayasi 		break;
1199d865b7d2Suebayasi 
1200d865b7d2Suebayasi 	case 'm':
1201d865b7d2Suebayasi 		bp.bp_opcode = BIOC_SPMANUAL;
1202d865b7d2Suebayasi 		break;
1203d865b7d2Suebayasi 
1204d865b7d2Suebayasi 	case 'd':
1205d865b7d2Suebayasi 		bp.bp_opcode = BIOC_SPDISABLE;
1206d865b7d2Suebayasi 		break;
1207d865b7d2Suebayasi 
1208d865b7d2Suebayasi 	case 'g': /* get patrol state */
1209d865b7d2Suebayasi 		bp.bp_opcode = BIOC_GPSTATUS;
1210d865b7d2Suebayasi 		break;
1211d865b7d2Suebayasi 
1212d865b7d2Suebayasi 	case 's': /* start/stop patrol */
1213d865b7d2Suebayasi 		if (strncmp("sta", arg, 3) == 0)
1214d865b7d2Suebayasi 			bp.bp_opcode = BIOC_SPSTART;
1215d865b7d2Suebayasi 		else
1216d865b7d2Suebayasi 			bp.bp_opcode = BIOC_SPSTOP;
1217d865b7d2Suebayasi 		break;
1218d865b7d2Suebayasi 
1219d865b7d2Suebayasi 	default:
1220d865b7d2Suebayasi 		errx(1, "invalid patrol function: %s", arg);
1221d865b7d2Suebayasi 	}
1222d865b7d2Suebayasi 
1223d865b7d2Suebayasi 	switch (arg[0]) {
1224d865b7d2Suebayasi 	case 'a':
1225d865b7d2Suebayasi 		errstr = str2patrol(arg, &timing);
1226d865b7d2Suebayasi 		if (errstr)
1227d865b7d2Suebayasi 			errx(1, "Patrol %s: %s", arg, errstr);
1228d865b7d2Suebayasi 		bp.bp_autoival = timing.interval;
1229d865b7d2Suebayasi 		bp.bp_autonext = timing.start;
1230d865b7d2Suebayasi 		break;
1231d865b7d2Suebayasi 	}
1232d865b7d2Suebayasi 
1233df69c215Sderaadt 	if (ioctl(devh, BIOCPATROL, &bp) == -1)
1234d865b7d2Suebayasi 		err(1, "BIOCPATROL");
1235d865b7d2Suebayasi 
1236d865b7d2Suebayasi 	bio_status(&bp.bp_bio.bio_status);
1237d865b7d2Suebayasi 
1238d865b7d2Suebayasi 	if (arg[0] == 'g') {
1239d865b7d2Suebayasi 		const char *mode, *status;
1240d865b7d2Suebayasi 		char interval[40];
1241d865b7d2Suebayasi 
1242d865b7d2Suebayasi 		interval[0] = '\0';
1243d865b7d2Suebayasi 
1244d865b7d2Suebayasi 		switch (bp.bp_mode) {
1245d865b7d2Suebayasi 		case BIOC_SPMAUTO:
1246d865b7d2Suebayasi 			mode = "auto";
1247d865b7d2Suebayasi 			snprintf(interval, sizeof interval,
1248d865b7d2Suebayasi 			    " interval=%d next=%d", bp.bp_autoival,
1249d865b7d2Suebayasi 			    bp.bp_autonext - bp.bp_autonow);
1250d865b7d2Suebayasi 			break;
1251d865b7d2Suebayasi 		case BIOC_SPMMANUAL:
1252d865b7d2Suebayasi 			mode = "manual";
1253d865b7d2Suebayasi 			break;
1254d865b7d2Suebayasi 		case BIOC_SPMDISABLED:
1255d865b7d2Suebayasi 			mode = "disabled";
1256d865b7d2Suebayasi 			break;
1257d865b7d2Suebayasi 		default:
12582ad5ec6fSuebayasi 			mode = "unknown";
1259d865b7d2Suebayasi 			break;
1260d865b7d2Suebayasi 		}
1261d865b7d2Suebayasi 		switch (bp.bp_status) {
1262d865b7d2Suebayasi 		case BIOC_SPSSTOPPED:
1263d865b7d2Suebayasi 			status = "stopped";
1264d865b7d2Suebayasi 			break;
1265d865b7d2Suebayasi 		case BIOC_SPSREADY:
1266d865b7d2Suebayasi 			status = "ready";
1267d865b7d2Suebayasi 			break;
1268d865b7d2Suebayasi 		case BIOC_SPSACTIVE:
1269d865b7d2Suebayasi 			status = "active";
1270d865b7d2Suebayasi 			break;
1271d865b7d2Suebayasi 		case BIOC_SPSABORTED:
1272d865b7d2Suebayasi 			status = "aborted";
1273d865b7d2Suebayasi 			break;
1274d865b7d2Suebayasi 		default:
1275d865b7d2Suebayasi 			status = "unknown";
1276d865b7d2Suebayasi 			break;
1277d865b7d2Suebayasi 		}
1278d865b7d2Suebayasi 		printf("patrol mode: %s%s\n", mode, interval);
1279d865b7d2Suebayasi 		printf("patrol status: %s\n", status);
1280d865b7d2Suebayasi 	}
1281d865b7d2Suebayasi }
1282d865b7d2Suebayasi 
1283ef0eb24eSjsing /*
1284ef0eb24eSjsing  * Measure this system's performance by measuring the time for 100 rounds.
1285ef0eb24eSjsing  * We are aiming for something that takes around 1s.
1286ef0eb24eSjsing  */
1287ef0eb24eSjsing int
bcrypt_pbkdf_autorounds(void)1288ef0eb24eSjsing bcrypt_pbkdf_autorounds(void)
1289ef0eb24eSjsing {
1290ef0eb24eSjsing 	struct timespec before, after;
1291ef0eb24eSjsing 	char buf[SR_CRYPTO_MAXKEYBYTES], salt[128];
1292ef0eb24eSjsing 	int r = 100;
1293ef0eb24eSjsing 	int duration;
1294ef0eb24eSjsing 
1295ef0eb24eSjsing 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &before);
1296ef0eb24eSjsing 	if (bcrypt_pbkdf("testpassword", strlen("testpassword"),
1297ef0eb24eSjsing 	    salt, sizeof(salt), buf, sizeof(buf), r) != 0)
1298ef0eb24eSjsing 		errx(1, "bcrypt pbkdf failed");
1299ef0eb24eSjsing 	clock_gettime(CLOCK_THREAD_CPUTIME_ID, &after);
1300ef0eb24eSjsing 
1301ef0eb24eSjsing 	duration = after.tv_sec - before.tv_sec;
1302ef0eb24eSjsing 	duration *= 1000000;
1303ef0eb24eSjsing 	duration += (after.tv_nsec - before.tv_nsec) / 1000;
1304ef0eb24eSjsing 
1305ef0eb24eSjsing 	duration /= r;
1306ef0eb24eSjsing 	r = 1000000 / duration;
1307ef0eb24eSjsing 
1308ef0eb24eSjsing 	if (r < 16)
1309ef0eb24eSjsing 		r = 16;
1310ef0eb24eSjsing 
1311ef0eb24eSjsing 	return r;
1312ef0eb24eSjsing }
1313ef0eb24eSjsing 
1314d865b7d2Suebayasi void
derive_key(u_int32_t type,int rounds,u_int8_t * key,size_t keysz,u_int8_t * salt,size_t saltsz,char * prompt,int verify)13153487a6b1Sjsing derive_key(u_int32_t type, int rounds, u_int8_t *key, size_t keysz,
131661f93244Sjsing     u_int8_t *salt, size_t saltsz, char *prompt, int verify)
1317aef7fe28Shshoexer {
131886735da2Smarco 	FILE		*f;
131986735da2Smarco 	size_t		pl;
132086735da2Smarco 	struct stat	sb;
13219e8c6f5bShshoexer 	char		passphrase[1024], verifybuf[1024];
13225f219970Skn 	int		rpp_flag = RPP_ECHO_OFF;
1323aef7fe28Shshoexer 
1324aef7fe28Shshoexer 	if (!key)
1325aef7fe28Shshoexer 		errx(1, "Invalid key");
1326aef7fe28Shshoexer 	if (!salt)
1327aef7fe28Shshoexer 		errx(1, "Invalid salt");
132861f93244Sjsing 
13291a8c43f6Sjsing 	if (type != SR_CRYPTOKDFT_PKCS5_PBKDF2 &&
13301a8c43f6Sjsing 	    type != SR_CRYPTOKDFT_BCRYPT_PBKDF)
133161f93244Sjsing 		errx(1, "unknown KDF type %d", type);
13321a8c43f6Sjsing 
1333b4f9a699Skn 	if (rounds < (type == SR_CRYPTOKDFT_PKCS5_PBKDF2 ? 1000 : 16))
133461f93244Sjsing 		errx(1, "number of KDF rounds is too small: %d", rounds);
1335aef7fe28Shshoexer 
1336aef7fe28Shshoexer 	/* get passphrase */
1337dd81669fSkn 	if (passfile) {
1338dd81669fSkn 		if ((f = fopen(passfile, "r")) == NULL)
133986735da2Smarco 			err(1, "invalid passphrase file");
134086735da2Smarco 
134186735da2Smarco 		if (fstat(fileno(f), &sb) == -1)
134286735da2Smarco 			err(1, "can't stat passphrase file");
134386735da2Smarco 		if (sb.st_uid != 0)
134486735da2Smarco 			errx(1, "passphrase file must be owned by root");
134586735da2Smarco 		if ((sb.st_mode & ~S_IFMT) != (S_IRUSR | S_IWUSR))
134686735da2Smarco 			errx(1, "passphrase file has the wrong permissions");
134786735da2Smarco 
134886735da2Smarco 		if (fgets(passphrase, sizeof(passphrase), f) == NULL)
134986735da2Smarco 			err(1, "can't read passphrase file");
135086735da2Smarco 		pl = strlen(passphrase);
135186735da2Smarco 		if (pl > 0 && passphrase[pl - 1] == '\n')
135286735da2Smarco 			passphrase[pl - 1] = '\0';
135386735da2Smarco 		else
135486735da2Smarco 			errx(1, "invalid passphrase length");
135586735da2Smarco 
135686735da2Smarco 		fclose(f);
1357ba3d8661Smarco 	} else {
13585f219970Skn 		rpp_flag |= interactive ? RPP_REQUIRE_TTY : RPP_STDIN;
13595f219970Skn 
1360954c7a34Skn  retry:
1361c6446370Sjsing 		if (readpassphrase(prompt, passphrase, sizeof(passphrase),
1362b96c6ce2Sckuethe 		    rpp_flag) == NULL)
13637eef0726Stedu 			err(1, "unable to read passphrase");
1364*1302b329Skn 		if (*passphrase == '\0') {
1365*1302b329Skn 			warnx("invalid passphrase length");
1366*1302b329Skn 			if (interactive)
1367*1302b329Skn 				goto retry;
1368*1302b329Skn 			exit(1);
1369*1302b329Skn 		}
1370ba3d8661Smarco 	}
13719e8c6f5bShshoexer 
1372dd81669fSkn 	if (verify && !passfile) {
13739e8c6f5bShshoexer 		/* request user to re-type it */
13749e8c6f5bShshoexer 		if (readpassphrase("Re-type passphrase: ", verifybuf,
1375b96c6ce2Sckuethe 		    sizeof(verifybuf), rpp_flag) == NULL) {
13760a488504Spelikan 			explicit_bzero(passphrase, sizeof(passphrase));
13777eef0726Stedu 			err(1, "unable to read passphrase");
13789e8c6f5bShshoexer 		}
13799e8c6f5bShshoexer 		if ((strlen(passphrase) != strlen(verifybuf)) ||
13809e8c6f5bShshoexer 		    (strcmp(passphrase, verifybuf) != 0)) {
13810a488504Spelikan 			explicit_bzero(passphrase, sizeof(passphrase));
13820a488504Spelikan 			explicit_bzero(verifybuf, sizeof(verifybuf));
1383954c7a34Skn 			if (interactive) {
1384954c7a34Skn 				warnx("Passphrases did not match, try again");
1385954c7a34Skn 				goto retry;
1386954c7a34Skn 			}
13879e8c6f5bShshoexer 			errx(1, "Passphrases did not match");
13889e8c6f5bShshoexer 		}
13899e8c6f5bShshoexer 		/* forget the re-typed one */
13900a488504Spelikan 		explicit_bzero(verifybuf, sizeof(verifybuf));
13919e8c6f5bShshoexer 	}
1392aef7fe28Shshoexer 
1393aef7fe28Shshoexer 	/* derive key from passphrase */
13941a8c43f6Sjsing 	if (type == SR_CRYPTOKDFT_PKCS5_PBKDF2) {
1395ef0eb24eSjsing 		if (verbose)
1396ef0eb24eSjsing 			printf("Deriving key using PKCS#5 PBKDF2 with %i rounds...\n",
1397ef0eb24eSjsing 			    rounds);
13985c1f8f6bSdjm 		if (pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltsz,
13995c1f8f6bSdjm 		    key, keysz, rounds) != 0)
14001a8c43f6Sjsing 			errx(1, "pkcs5_pbkdf2 failed");
14011a8c43f6Sjsing 	} else if (type == SR_CRYPTOKDFT_BCRYPT_PBKDF) {
1402ef0eb24eSjsing 		if (verbose)
1403ef0eb24eSjsing 			printf("Deriving key using bcrypt PBKDF with %i rounds...\n",
1404ef0eb24eSjsing 			    rounds);
14051a8c43f6Sjsing 		if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, saltsz,
14061a8c43f6Sjsing 		    key, keysz, rounds) != 0)
14071a8c43f6Sjsing 			errx(1, "bcrypt_pbkdf failed");
14081a8c43f6Sjsing 	} else {
14091a8c43f6Sjsing 		errx(1, "unknown KDF type %d", type);
14101a8c43f6Sjsing 	}
1411aef7fe28Shshoexer 
1412aef7fe28Shshoexer 	/* forget passphrase */
14130a488504Spelikan 	explicit_bzero(passphrase, sizeof(passphrase));
1414aef7fe28Shshoexer }
1415