geom_part.c revision 258186
1/*-
2 * Copyright (c) 2007, 2008 Marcel Moolenaar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/9/sbin/geom/class/part/geom_part.c 258186 2013-11-15 20:23:52Z asomers $");
29
30#include <sys/stat.h>
31#include <sys/vtoc.h>
32
33#include <assert.h>
34#include <ctype.h>
35#include <err.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <libgeom.h>
39#include <libutil.h>
40#include <paths.h>
41#include <signal.h>
42#include <stdint.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <limits.h>
46#include <inttypes.h>
47#include <string.h>
48#include <strings.h>
49#include <unistd.h>
50
51#include "core/geom.h"
52#include "misc/subr.h"
53
54#ifdef STATIC_GEOM_CLASSES
55#define	PUBSYM(x)	gpart_##x
56#else
57#define	PUBSYM(x)	x
58#endif
59
60uint32_t PUBSYM(lib_version) = G_LIB_VERSION;
61uint32_t PUBSYM(version) = 0;
62
63static char sstart[32];
64static char ssize[32];
65volatile sig_atomic_t undo_restore;
66
67#define	GPART_AUTOFILL	"*"
68#define	GPART_FLAGS	"C"
69
70#define	GPART_PARAM_BOOTCODE	"bootcode"
71#define	GPART_PARAM_INDEX	"index"
72#define	GPART_PARAM_PARTCODE	"partcode"
73
74static struct gclass *find_class(struct gmesh *, const char *);
75static struct ggeom * find_geom(struct gclass *, const char *);
76static const char *find_geomcfg(struct ggeom *, const char *);
77static const char *find_provcfg(struct gprovider *, const char *);
78static struct gprovider *find_provider(struct ggeom *, off_t);
79static const char *fmtsize(int64_t);
80static int gpart_autofill(struct gctl_req *);
81static int gpart_autofill_resize(struct gctl_req *);
82static void gpart_bootcode(struct gctl_req *, unsigned int);
83static void *gpart_bootfile_read(const char *, ssize_t *);
84static void gpart_issue(struct gctl_req *, unsigned int);
85static void gpart_show(struct gctl_req *, unsigned int);
86static void gpart_show_geom(struct ggeom *, const char *, int);
87static int gpart_show_hasopt(struct gctl_req *, const char *, const char *);
88static void gpart_write_partcode(struct ggeom *, int, void *, ssize_t);
89static void gpart_write_partcode_vtoc8(struct ggeom *, int, void *);
90static void gpart_print_error(const char *);
91static void gpart_backup(struct gctl_req *, unsigned int);
92static void gpart_restore(struct gctl_req *, unsigned int);
93
94struct g_command PUBSYM(class_commands)[] = {
95	{ "add", 0, gpart_issue, {
96		{ 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING },
97		{ 'b', "start", GPART_AUTOFILL, G_TYPE_STRING },
98		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
99		{ 't', "type", NULL, G_TYPE_STRING },
100		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
101		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
102		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
103		G_OPT_SENTINEL },
104	    "-t type [-a alignment] [-b start] [-s size] [-i index] "
105		"[-l label] [-f flags] geom"
106	},
107	{ "backup", 0, gpart_backup, G_NULL_OPTS,
108	    "geom"
109	},
110	{ "bootcode", 0, gpart_bootcode, {
111		{ 'b', GPART_PARAM_BOOTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
112		{ 'p', GPART_PARAM_PARTCODE, G_VAL_OPTIONAL, G_TYPE_STRING },
113		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
114		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
115		G_OPT_SENTINEL },
116	    "[-b bootcode] [-p partcode -i index] [-f flags] geom"
117	},
118	{ "commit", 0, gpart_issue, G_NULL_OPTS,
119	    "geom"
120	},
121	{ "create", 0, gpart_issue, {
122		{ 's', "scheme", NULL, G_TYPE_STRING },
123		{ 'n', "entries", G_VAL_OPTIONAL, G_TYPE_NUMBER },
124		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
125		G_OPT_SENTINEL },
126	    "-s scheme [-n entries] [-f flags] provider"
127	},
128	{ "delete", 0, gpart_issue, {
129		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
130		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
131		G_OPT_SENTINEL },
132	    "-i index [-f flags] geom"
133	},
134	{ "destroy", 0, gpart_issue, {
135		{ 'F', "force", NULL, G_TYPE_BOOL },
136		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
137		G_OPT_SENTINEL },
138	    "[-F] [-f flags] geom"
139	},
140	{ "modify", 0, gpart_issue, {
141		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
142		{ 'l', "label", G_VAL_OPTIONAL, G_TYPE_STRING },
143		{ 't', "type", G_VAL_OPTIONAL, G_TYPE_STRING },
144		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
145		G_OPT_SENTINEL },
146	    "-i index [-l label] [-t type] [-f flags] geom"
147	},
148	{ "set", 0, gpart_issue, {
149		{ 'a', "attrib", NULL, G_TYPE_STRING },
150		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
151		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
152		G_OPT_SENTINEL },
153	    "-a attrib [-i index] [-f flags] geom"
154	},
155	{ "show", 0, gpart_show, {
156		{ 'l', "show_label", NULL, G_TYPE_BOOL },
157		{ 'r', "show_rawtype", NULL, G_TYPE_BOOL },
158		{ 'p', "show_providers", NULL, G_TYPE_BOOL },
159		G_OPT_SENTINEL },
160	    "[-l | -r] [-p] [geom ...]"
161	},
162	{ "undo", 0, gpart_issue, G_NULL_OPTS,
163	    "geom"
164	},
165	{ "unset", 0, gpart_issue, {
166		{ 'a', "attrib", NULL, G_TYPE_STRING },
167		{ 'i', GPART_PARAM_INDEX, G_VAL_OPTIONAL, G_TYPE_NUMBER },
168		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
169		G_OPT_SENTINEL },
170	    "-a attrib [-i index] [-f flags] geom"
171	},
172	{ "resize", 0, gpart_issue, {
173		{ 'a', "alignment", GPART_AUTOFILL, G_TYPE_STRING },
174		{ 's', "size", GPART_AUTOFILL, G_TYPE_STRING },
175		{ 'i', GPART_PARAM_INDEX, NULL, G_TYPE_NUMBER },
176		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
177		G_OPT_SENTINEL },
178	    "-i index [-a alignment] [-s size] [-f flags] geom"
179	},
180	{ "restore", 0, gpart_restore, {
181		{ 'F', "force", NULL, G_TYPE_BOOL },
182		{ 'l', "restore_labels", NULL, G_TYPE_BOOL },
183		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
184		G_OPT_SENTINEL },
185	    "[-lF] [-f flags] provider [...]"
186	},
187	{ "recover", 0, gpart_issue, {
188		{ 'f', "flags", GPART_FLAGS, G_TYPE_STRING },
189		G_OPT_SENTINEL },
190	    "[-f flags] geom"
191	},
192	G_CMD_SENTINEL
193};
194
195static struct gclass *
196find_class(struct gmesh *mesh, const char *name)
197{
198	struct gclass *classp;
199
200	LIST_FOREACH(classp, &mesh->lg_class, lg_class) {
201		if (strcmp(classp->lg_name, name) == 0)
202			return (classp);
203	}
204	return (NULL);
205}
206
207static struct ggeom *
208find_geom(struct gclass *classp, const char *name)
209{
210	struct ggeom *gp;
211
212	if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
213		name += sizeof(_PATH_DEV) - 1;
214	LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
215		if (strcmp(gp->lg_name, name) == 0)
216			return (gp);
217	}
218	return (NULL);
219}
220
221static const char *
222find_geomcfg(struct ggeom *gp, const char *cfg)
223{
224	struct gconfig *gc;
225
226	LIST_FOREACH(gc, &gp->lg_config, lg_config) {
227		if (!strcmp(gc->lg_name, cfg))
228			return (gc->lg_val);
229	}
230	return (NULL);
231}
232
233static const char *
234find_provcfg(struct gprovider *pp, const char *cfg)
235{
236	struct gconfig *gc;
237
238	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
239		if (!strcmp(gc->lg_name, cfg))
240			return (gc->lg_val);
241	}
242	return (NULL);
243}
244
245static struct gprovider *
246find_provider(struct ggeom *gp, off_t minsector)
247{
248	struct gprovider *pp, *bestpp;
249	const char *s;
250	off_t sector, bestsector;
251
252	bestpp = NULL;
253	bestsector = 0;
254	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
255		s = find_provcfg(pp, "start");
256		sector = (off_t)strtoimax(s, NULL, 0);
257		if (sector < minsector)
258			continue;
259		if (bestpp != NULL && sector >= bestsector)
260			continue;
261
262		bestpp = pp;
263		bestsector = sector;
264	}
265	return (bestpp);
266}
267
268static const char *
269fmtsize(int64_t rawsz)
270{
271	static char buf[5];
272
273	humanize_number(buf, sizeof(buf), rawsz, "", HN_AUTOSCALE,
274	    HN_B | HN_NOSPACE | HN_DECIMAL);
275	return (buf);
276}
277
278static const char *
279fmtattrib(struct gprovider *pp)
280{
281	static char buf[128];
282	struct gconfig *gc;
283	u_int idx;
284
285	buf[0] = '\0';
286	idx = 0;
287	LIST_FOREACH(gc, &pp->lg_config, lg_config) {
288		if (strcmp(gc->lg_name, "attrib") != 0)
289			continue;
290		idx += snprintf(buf + idx, sizeof(buf) - idx, "%s%s",
291		    (idx == 0) ? " [" : ",", gc->lg_val);
292	}
293	if (idx > 0)
294		snprintf(buf + idx, sizeof(buf) - idx, "] ");
295	return (buf);
296}
297
298#define	ALIGNDOWN(d, a)	((d) - (d) % (a))
299#define	ALIGNUP(d, a)	((d) % (a) ? (d) - (d) % (a) + (a): (d))
300
301static int
302gpart_autofill_resize(struct gctl_req *req)
303{
304	struct gmesh mesh;
305	struct gclass *cp;
306	struct ggeom *gp;
307	struct gprovider *pp;
308	off_t last, size, start, new_size;
309	off_t lba, new_lba, alignment, offset;
310	const char *s;
311	int error, idx, has_alignment;
312
313	idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
314	if (idx < 1)
315		errx(EXIT_FAILURE, "invalid partition index");
316
317	error = geom_gettree(&mesh);
318	if (error)
319		return (error);
320	s = gctl_get_ascii(req, "class");
321	if (s == NULL)
322		abort();
323	cp = find_class(&mesh, s);
324	if (cp == NULL)
325		errx(EXIT_FAILURE, "Class %s not found.", s);
326	s = gctl_get_ascii(req, "arg0");
327	if (s == NULL)
328		abort();
329	gp = find_geom(cp, s);
330	if (gp == NULL)
331		errx(EXIT_FAILURE, "No such geom: %s.", s);
332	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
333	if (pp == NULL)
334		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
335
336	s = gctl_get_ascii(req, "alignment");
337	has_alignment = (*s == '*') ? 0 : 1;
338	alignment = 1;
339	if (has_alignment) {
340		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
341		if (error)
342			errc(EXIT_FAILURE, error, "Invalid alignment param");
343		if (alignment == 0)
344			errx(EXIT_FAILURE, "Invalid alignment param");
345	} else {
346		lba = pp->lg_stripesize / pp->lg_sectorsize;
347		if (lba > 0)
348			alignment = lba;
349	}
350	error = gctl_delete_param(req, "alignment");
351	if (error)
352		errc(EXIT_FAILURE, error, "internal error");
353
354	s = gctl_get_ascii(req, "size");
355	if (*s == '*')
356		new_size = 0;
357	else {
358		error = g_parse_lba(s, pp->lg_sectorsize, &new_size);
359		if (error)
360			errc(EXIT_FAILURE, error, "Invalid size param");
361		/* no autofill necessary. */
362		if (has_alignment == 0)
363			goto done;
364	}
365
366	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
367	s = find_geomcfg(gp, "last");
368	if (s == NULL)
369		errx(EXIT_FAILURE, "Final block not found for geom %s",
370		    gp->lg_name);
371	last = (off_t)strtoimax(s, NULL, 0);
372	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
373		s = find_provcfg(pp, "index");
374		if (s == NULL)
375			continue;
376		if (atoi(s) == idx)
377			break;
378	}
379	if (pp == NULL)
380		errx(EXIT_FAILURE, "invalid partition index");
381
382	s = find_provcfg(pp, "start");
383	start = (off_t)strtoimax(s, NULL, 0);
384	s = find_provcfg(pp, "end");
385	lba = (off_t)strtoimax(s, NULL, 0);
386	size = lba - start + 1;
387
388	pp = find_provider(gp, lba + 1);
389	if (new_size > 0 && (new_size <= size || pp == NULL)) {
390		/* The start offset may be not aligned, so we align the end
391		 * offset and then calculate the size.
392		 */
393		new_size = ALIGNDOWN(start + offset + new_size,
394		    alignment) - start - offset;
395		goto done;
396	}
397	if (pp == NULL) {
398		new_size = ALIGNDOWN(last + offset + 1, alignment) -
399		    start - offset;
400		if (new_size < size)
401			return (ENOSPC);
402	} else {
403		s = find_provcfg(pp, "start");
404		new_lba = (off_t)strtoimax(s, NULL, 0);
405		/*
406		 * Is there any free space between current and
407		 * next providers?
408		 */
409		new_lba = ALIGNDOWN(new_lba + offset, alignment) - offset;
410		if (new_lba > lba)
411			new_size = new_lba - start;
412		else {
413			geom_deletetree(&mesh);
414			return (ENOSPC);
415		}
416	}
417done:
418	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)new_size);
419	gctl_change_param(req, "size", -1, ssize);
420	geom_deletetree(&mesh);
421	return (0);
422}
423
424static int
425gpart_autofill(struct gctl_req *req)
426{
427	struct gmesh mesh;
428	struct gclass *cp;
429	struct ggeom *gp;
430	struct gprovider *pp;
431	off_t first, last, a_first;
432	off_t size, start, a_lba;
433	off_t lba, len, alignment, offset;
434	uintmax_t grade;
435	const char *s;
436	int error, has_size, has_start, has_alignment;
437
438	s = gctl_get_ascii(req, "verb");
439	if (strcmp(s, "resize") == 0)
440		return gpart_autofill_resize(req);
441	if (strcmp(s, "add") != 0)
442		return (0);
443
444	error = geom_gettree(&mesh);
445	if (error)
446		return (error);
447	s = gctl_get_ascii(req, "class");
448	if (s == NULL)
449		abort();
450	cp = find_class(&mesh, s);
451	if (cp == NULL)
452		errx(EXIT_FAILURE, "Class %s not found.", s);
453	s = gctl_get_ascii(req, "arg0");
454	if (s == NULL)
455		abort();
456	gp = find_geom(cp, s);
457	if (gp == NULL)
458		errx(EXIT_FAILURE, "No such geom: %s.", s);
459	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
460	if (pp == NULL)
461		errx(EXIT_FAILURE, "Provider for geom %s not found.", s);
462
463	s = gctl_get_ascii(req, "alignment");
464	has_alignment = (*s == '*') ? 0 : 1;
465	alignment = 1;
466	if (has_alignment) {
467		error = g_parse_lba(s, pp->lg_sectorsize, &alignment);
468		if (error)
469			errc(EXIT_FAILURE, error, "Invalid alignment param");
470		if (alignment == 0)
471			errx(EXIT_FAILURE, "Invalid alignment param");
472	}
473	error = gctl_delete_param(req, "alignment");
474	if (error)
475		errc(EXIT_FAILURE, error, "internal error");
476
477	s = gctl_get_ascii(req, "size");
478	has_size = (*s == '*') ? 0 : 1;
479	size = 0;
480	if (has_size) {
481		error = g_parse_lba(s, pp->lg_sectorsize, &size);
482		if (error)
483			errc(EXIT_FAILURE, error, "Invalid size param");
484	}
485
486	s = gctl_get_ascii(req, "start");
487	has_start = (*s == '*') ? 0 : 1;
488	start = 0ULL;
489	if (has_start) {
490		error = g_parse_lba(s, pp->lg_sectorsize, &start);
491		if (error)
492			errc(EXIT_FAILURE, error, "Invalid start param");
493	}
494
495	/* No autofill necessary. */
496	if (has_size && has_start && !has_alignment)
497		goto done;
498
499	len = pp->lg_stripesize / pp->lg_sectorsize;
500	if (len > 0 && !has_alignment)
501		alignment = len;
502
503	/* Adjust parameters to stripeoffset */
504	offset = (pp->lg_stripeoffset / pp->lg_sectorsize) % alignment;
505	start = ALIGNUP(start + offset, alignment);
506	if (size > alignment)
507		size = ALIGNDOWN(size, alignment);
508
509	s = find_geomcfg(gp, "first");
510	if (s == NULL)
511		errx(EXIT_FAILURE, "Starting block not found for geom %s",
512		    gp->lg_name);
513	first = (off_t)strtoimax(s, NULL, 0);
514	s = find_geomcfg(gp, "last");
515	if (s == NULL)
516		errx(EXIT_FAILURE, "Final block not found for geom %s",
517		    gp->lg_name);
518	last = (off_t)strtoimax(s, NULL, 0);
519	grade = ~0ULL;
520	a_first = ALIGNUP(first + offset, alignment);
521	last = ALIGNDOWN(last + offset, alignment);
522	if (a_first < start)
523		a_first = start;
524	while ((pp = find_provider(gp, first)) != NULL) {
525		s = find_provcfg(pp, "start");
526		lba = (off_t)strtoimax(s, NULL, 0);
527		a_lba = ALIGNDOWN(lba + offset, alignment);
528		if (first < a_lba && a_first < a_lba) {
529			/* Free space [first, lba> */
530			len = a_lba - a_first;
531			if (has_size) {
532				if (len >= size &&
533				    (uintmax_t)(len - size) < grade) {
534					start = a_first;
535					grade = len - size;
536				}
537			} else if (has_start) {
538				if (start >= a_first && start < a_lba) {
539					size = a_lba - start;
540					grade = start - a_first;
541				}
542			} else {
543				if (grade == ~0ULL || len > size) {
544					start = a_first;
545					size = len;
546					grade = 0;
547				}
548			}
549		}
550
551		s = find_provcfg(pp, "end");
552		first = (off_t)strtoimax(s, NULL, 0) + 1;
553		if (first > a_first)
554			a_first = ALIGNUP(first + offset, alignment);
555	}
556	if (a_first <= last) {
557		/* Free space [first-last] */
558		len = ALIGNDOWN(last - a_first + 1, alignment);
559		if (has_size) {
560			if (len >= size &&
561			    (uintmax_t)(len - size) < grade) {
562				start = a_first;
563				grade = len - size;
564			}
565		} else if (has_start) {
566			if (start >= a_first && start <= last) {
567				size = ALIGNDOWN(last - start + 1, alignment);
568				grade = start - a_first;
569			}
570		} else {
571			if (grade == ~0ULL || len > size) {
572				start = a_first;
573				size = len;
574				grade = 0;
575			}
576		}
577	}
578	if (grade == ~0ULL) {
579		geom_deletetree(&mesh);
580		return (ENOSPC);
581	}
582	start -= offset;	/* Return back to real offset */
583done:
584	snprintf(ssize, sizeof(ssize), "%jd", (intmax_t)size);
585	gctl_change_param(req, "size", -1, ssize);
586	snprintf(sstart, sizeof(sstart), "%jd", (intmax_t)start);
587	gctl_change_param(req, "start", -1, sstart);
588	geom_deletetree(&mesh);
589	return (0);
590}
591
592static void
593gpart_show_geom(struct ggeom *gp, const char *element, int show_providers)
594{
595	struct gprovider *pp;
596	const char *s, *scheme;
597	off_t first, last, sector, end;
598	off_t length, secsz;
599	int idx, wblocks, wname, wmax;
600
601	scheme = find_geomcfg(gp, "scheme");
602	if (scheme == NULL)
603		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
604	s = find_geomcfg(gp, "first");
605	if (s == NULL)
606		errx(EXIT_FAILURE, "Starting block not found for geom %s",
607		    gp->lg_name);
608	first = (off_t)strtoimax(s, NULL, 0);
609	s = find_geomcfg(gp, "last");
610	if (s == NULL)
611		errx(EXIT_FAILURE, "Final block not found for geom %s",
612		    gp->lg_name);
613	last = (off_t)strtoimax(s, NULL, 0);
614	wblocks = strlen(s);
615	s = find_geomcfg(gp, "state");
616	if (s == NULL)
617		errx(EXIT_FAILURE, "State not found for geom %s", gp->lg_name);
618	if (s != NULL && *s != 'C')
619		s = NULL;
620	wmax = strlen(gp->lg_name);
621	if (show_providers) {
622		LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
623			wname = strlen(pp->lg_name);
624			if (wname > wmax)
625				wmax = wname;
626		}
627	}
628	wname = wmax;
629	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
630	secsz = pp->lg_sectorsize;
631	printf("=>%*jd  %*jd  %*s  %s  (%s)%s\n",
632	    wblocks, (intmax_t)first, wblocks, (intmax_t)(last - first + 1),
633	    wname, gp->lg_name,
634	    scheme, fmtsize(pp->lg_mediasize),
635	    s ? " [CORRUPT]": "");
636
637	while ((pp = find_provider(gp, first)) != NULL) {
638		s = find_provcfg(pp, "start");
639		sector = (off_t)strtoimax(s, NULL, 0);
640
641		s = find_provcfg(pp, "end");
642		end = (off_t)strtoimax(s, NULL, 0);
643		length = end - sector + 1;
644
645		s = find_provcfg(pp, "index");
646		idx = atoi(s);
647		if (first < sector) {
648			printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
649			    wblocks, (intmax_t)first, wblocks,
650			    (intmax_t)(sector - first), wname, "",
651			    fmtsize((sector - first) * secsz));
652		}
653		if (show_providers) {
654			printf("  %*jd  %*jd  %*s  %s %s (%s)\n",
655			    wblocks, (intmax_t)sector, wblocks,
656			    (intmax_t)length, wname, pp->lg_name,
657			    find_provcfg(pp, element), fmtattrib(pp),
658			    fmtsize(pp->lg_mediasize));
659		} else
660			printf("  %*jd  %*jd  %*d  %s %s (%s)\n",
661			    wblocks, (intmax_t)sector, wblocks,
662			    (intmax_t)length, wname, idx,
663			    find_provcfg(pp, element), fmtattrib(pp),
664			    fmtsize(pp->lg_mediasize));
665		first = end + 1;
666	}
667	if (first <= last) {
668		length = last - first + 1;
669		printf("  %*jd  %*jd  %*s  - free -  (%s)\n",
670		    wblocks, (intmax_t)first, wblocks, (intmax_t)length,
671		    wname, "",
672		    fmtsize(length * secsz));
673	}
674	printf("\n");
675}
676
677static int
678gpart_show_hasopt(struct gctl_req *req, const char *opt, const char *elt)
679{
680
681	if (!gctl_get_int(req, "%s", opt))
682		return (0);
683
684	if (elt != NULL)
685		errx(EXIT_FAILURE, "-l and -r are mutually exclusive");
686
687	return (1);
688}
689
690static void
691gpart_show(struct gctl_req *req, unsigned int fl __unused)
692{
693	struct gmesh mesh;
694	struct gclass *classp;
695	struct ggeom *gp;
696	const char *element, *name;
697	int error, i, nargs, show_providers;
698
699	element = NULL;
700	if (gpart_show_hasopt(req, "show_label", element))
701		element = "label";
702	if (gpart_show_hasopt(req, "show_rawtype", element))
703		element = "rawtype";
704	if (element == NULL)
705		element = "type";
706
707	name = gctl_get_ascii(req, "class");
708	if (name == NULL)
709		abort();
710	error = geom_gettree(&mesh);
711	if (error != 0)
712		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
713	classp = find_class(&mesh, name);
714	if (classp == NULL) {
715		geom_deletetree(&mesh);
716		errx(EXIT_FAILURE, "Class %s not found.", name);
717	}
718	show_providers = gctl_get_int(req, "show_providers");
719	nargs = gctl_get_int(req, "nargs");
720	if (nargs > 0) {
721		for (i = 0; i < nargs; i++) {
722			name = gctl_get_ascii(req, "arg%d", i);
723			gp = find_geom(classp, name);
724			if (gp != NULL)
725				gpart_show_geom(gp, element, show_providers);
726			else
727				errx(EXIT_FAILURE, "No such geom: %s.", name);
728		}
729	} else {
730		LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
731			gpart_show_geom(gp, element, show_providers);
732		}
733	}
734	geom_deletetree(&mesh);
735}
736
737static void
738gpart_backup(struct gctl_req *req, unsigned int fl __unused)
739{
740	struct gmesh mesh;
741	struct gclass *classp;
742	struct gprovider *pp;
743	struct ggeom *gp;
744	const char *s, *scheme;
745	off_t sector, end;
746	off_t length;
747	int error, i, windex, wblocks, wtype;
748
749	if (gctl_get_int(req, "nargs") != 1)
750		errx(EXIT_FAILURE, "Invalid number of arguments.");
751	error = geom_gettree(&mesh);
752	if (error != 0)
753		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
754	s = gctl_get_ascii(req, "class");
755	if (s == NULL)
756		abort();
757	classp = find_class(&mesh, s);
758	if (classp == NULL) {
759		geom_deletetree(&mesh);
760		errx(EXIT_FAILURE, "Class %s not found.", s);
761	}
762	s = gctl_get_ascii(req, "arg0");
763	if (s == NULL)
764		abort();
765	gp = find_geom(classp, s);
766	if (gp == NULL)
767		errx(EXIT_FAILURE, "No such geom: %s.", s);
768	scheme = find_geomcfg(gp, "scheme");
769	if (scheme == NULL)
770		abort();
771	pp = LIST_FIRST(&gp->lg_consumer)->lg_provider;
772	s = find_geomcfg(gp, "last");
773	if (s == NULL)
774		abort();
775	wblocks = strlen(s);
776	wtype = 0;
777	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
778		s = find_provcfg(pp, "type");
779		i = strlen(s);
780		if (i > wtype)
781			wtype = i;
782	}
783	s = find_geomcfg(gp, "entries");
784	if (s == NULL)
785		abort();
786	windex = strlen(s);
787	printf("%s %s\n", scheme, s);
788	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
789		s = find_provcfg(pp, "start");
790		sector = (off_t)strtoimax(s, NULL, 0);
791
792		s = find_provcfg(pp, "end");
793		end = (off_t)strtoimax(s, NULL, 0);
794		length = end - sector + 1;
795
796		s = find_provcfg(pp, "label");
797		printf("%-*s %*s %*jd %*jd %s %s\n",
798		    windex, find_provcfg(pp, "index"),
799		    wtype, find_provcfg(pp, "type"),
800		    wblocks, (intmax_t)sector,
801		    wblocks, (intmax_t)length,
802		    (s != NULL) ? s: "", fmtattrib(pp));
803	}
804	geom_deletetree(&mesh);
805}
806
807static int
808skip_line(const char *p)
809{
810
811	while (*p != '\0') {
812		if (*p == '#')
813			return (1);
814		if (isspace(*p) == 0)
815			return (0);
816		p++;
817	}
818	return (1);
819}
820
821static void
822gpart_sighndl(int sig __unused)
823{
824	undo_restore = 1;
825}
826
827static void
828gpart_restore(struct gctl_req *req, unsigned int fl __unused)
829{
830	struct gmesh mesh;
831	struct gclass *classp;
832	struct gctl_req *r;
833	struct ggeom *gp;
834	struct sigaction si_sa;
835	const char *s, *flags, *errstr, *label;
836	char **ap, *argv[6], line[BUFSIZ], *pline;
837	int error, forced, i, l, nargs, created, rl;
838	intmax_t n;
839
840	nargs = gctl_get_int(req, "nargs");
841	if (nargs < 1)
842		errx(EXIT_FAILURE, "Invalid number of arguments.");
843
844	forced = gctl_get_int(req, "force");
845	flags = gctl_get_ascii(req, "flags");
846	rl = gctl_get_int(req, "restore_labels");
847	s = gctl_get_ascii(req, "class");
848	if (s == NULL)
849		abort();
850	error = geom_gettree(&mesh);
851	if (error != 0)
852		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
853	classp = find_class(&mesh, s);
854	if (classp == NULL) {
855		geom_deletetree(&mesh);
856		errx(EXIT_FAILURE, "Class %s not found.", s);
857	}
858
859	sigemptyset(&si_sa.sa_mask);
860	si_sa.sa_flags = 0;
861	si_sa.sa_handler = gpart_sighndl;
862	if (sigaction(SIGINT, &si_sa, 0) == -1)
863		err(EXIT_FAILURE, "sigaction SIGINT");
864
865	if (forced) {
866		/* destroy existent partition table before restore */
867		for (i = 0; i < nargs; i++) {
868			s = gctl_get_ascii(req, "arg%d", i);
869			gp = find_geom(classp, s);
870			if (gp != NULL) {
871				r = gctl_get_handle();
872				gctl_ro_param(r, "class", -1,
873				    classp->lg_name);
874				gctl_ro_param(r, "verb", -1, "destroy");
875				gctl_ro_param(r, "flags", -1, "restore");
876				gctl_ro_param(r, "force", sizeof(forced),
877				    &forced);
878				gctl_ro_param(r, "arg0", -1, s);
879				errstr = gctl_issue(r);
880				if (errstr != NULL && errstr[0] != '\0') {
881					gpart_print_error(errstr);
882					gctl_free(r);
883					goto backout;
884				}
885				gctl_free(r);
886			}
887		}
888	}
889	created = 0;
890	while (undo_restore == 0 &&
891	    fgets(line, sizeof(line) - 1, stdin) != NULL) {
892		/* Format of backup entries:
893		 * <scheme name> <number of entries>
894		 * <index> <type> <start> <size> [label] ['['attrib[,attrib]']']
895		 */
896		pline = (char *)line;
897		pline[strlen(line) - 1] = 0;
898		if (skip_line(pline))
899			continue;
900		for (ap = argv;
901		    (*ap = strsep(&pline, " \t")) != NULL;)
902			if (**ap != '\0' && ++ap >= &argv[6])
903				break;
904		l = ap - &argv[0];
905		label = pline = NULL;
906		if (l == 1 || l == 2) { /* create table */
907			if (created)
908				errx(EXIT_FAILURE, "Incorrect backup format.");
909			if (l == 2)
910				n = strtoimax(argv[1], NULL, 0);
911			for (i = 0; i < nargs; i++) {
912				s = gctl_get_ascii(req, "arg%d", i);
913				r = gctl_get_handle();
914				gctl_ro_param(r, "class", -1,
915				    classp->lg_name);
916				gctl_ro_param(r, "verb", -1, "create");
917				gctl_ro_param(r, "scheme", -1, argv[0]);
918				if (l == 2)
919					gctl_ro_param(r, "entries",
920					    sizeof(n), &n);
921				gctl_ro_param(r, "flags", -1, "restore");
922				gctl_ro_param(r, "arg0", -1, s);
923				errstr = gctl_issue(r);
924				if (errstr != NULL && errstr[0] != '\0') {
925					gpart_print_error(errstr);
926					gctl_free(r);
927					goto backout;
928				}
929				gctl_free(r);
930			}
931			created = 1;
932			continue;
933		} else if (l < 4 || created == 0)
934			errx(EXIT_FAILURE, "Incorrect backup format.");
935		else if (l == 5) {
936			if (strchr(argv[4], '[') == NULL)
937				label = argv[4];
938			else
939				pline = argv[4];
940		} else if (l == 6) {
941			label = argv[4];
942			pline = argv[5];
943		}
944		/* Add partitions to each table */
945		for (i = 0; i < nargs; i++) {
946			s = gctl_get_ascii(req, "arg%d", i);
947			r = gctl_get_handle();
948			n = strtoimax(argv[0], NULL, 0);
949			gctl_ro_param(r, "class", -1, classp->lg_name);
950			gctl_ro_param(r, "verb", -1, "add");
951			gctl_ro_param(r, "flags", -1, "restore");
952			gctl_ro_param(r, GPART_PARAM_INDEX, sizeof(n), &n);
953			gctl_ro_param(r, "type", -1, argv[1]);
954			gctl_ro_param(r, "start", -1, argv[2]);
955			gctl_ro_param(r, "size", -1, argv[3]);
956			if (rl != 0 && label != NULL)
957				gctl_ro_param(r, "label", -1, argv[4]);
958			gctl_ro_param(r, "alignment", -1, GPART_AUTOFILL);
959			gctl_ro_param(r, "arg0", -1, s);
960			error = gpart_autofill(r);
961			if (error != 0)
962				errc(EXIT_FAILURE, error, "autofill");
963			errstr = gctl_issue(r);
964			if (errstr != NULL && errstr[0] != '\0') {
965				gpart_print_error(errstr);
966				gctl_free(r);
967				goto backout;
968			}
969			gctl_free(r);
970		}
971		if (pline == NULL || *pline != '[')
972			continue;
973		/* set attributes */
974		pline++;
975		for (ap = argv;
976		    (*ap = strsep(&pline, ",]")) != NULL;)
977			if (**ap != '\0' && ++ap >= &argv[6])
978				break;
979		for (i = 0; i < nargs; i++) {
980			l = ap - &argv[0];
981			s = gctl_get_ascii(req, "arg%d", i);
982			while (l > 0) {
983				r = gctl_get_handle();
984				gctl_ro_param(r, "class", -1, classp->lg_name);
985				gctl_ro_param(r, "verb", -1, "set");
986				gctl_ro_param(r, "flags", -1, "restore");
987				gctl_ro_param(r, GPART_PARAM_INDEX,
988				    sizeof(n), &n);
989				gctl_ro_param(r, "attrib", -1, argv[--l]);
990				gctl_ro_param(r, "arg0", -1, s);
991				errstr = gctl_issue(r);
992				if (errstr != NULL && errstr[0] != '\0') {
993					gpart_print_error(errstr);
994					gctl_free(r);
995					goto backout;
996				}
997				gctl_free(r);
998			}
999		}
1000	}
1001	if (undo_restore)
1002		goto backout;
1003	/* commit changes if needed */
1004	if (strchr(flags, 'C') != NULL) {
1005		for (i = 0; i < nargs; i++) {
1006			s = gctl_get_ascii(req, "arg%d", i);
1007			r = gctl_get_handle();
1008			gctl_ro_param(r, "class", -1, classp->lg_name);
1009			gctl_ro_param(r, "verb", -1, "commit");
1010			gctl_ro_param(r, "arg0", -1, s);
1011			errstr = gctl_issue(r);
1012			if (errstr != NULL && errstr[0] != '\0') {
1013				gpart_print_error(errstr);
1014				gctl_free(r);
1015				goto backout;
1016			}
1017			gctl_free(r);
1018		}
1019	}
1020	gctl_free(req);
1021	geom_deletetree(&mesh);
1022	exit(EXIT_SUCCESS);
1023
1024backout:
1025	for (i = 0; i < nargs; i++) {
1026		s = gctl_get_ascii(req, "arg%d", i);
1027		r = gctl_get_handle();
1028		gctl_ro_param(r, "class", -1, classp->lg_name);
1029		gctl_ro_param(r, "verb", -1, "undo");
1030		gctl_ro_param(r, "arg0", -1, s);
1031		gctl_issue(r);
1032		gctl_free(r);
1033	}
1034	gctl_free(req);
1035	geom_deletetree(&mesh);
1036	exit(EXIT_FAILURE);
1037}
1038
1039static void *
1040gpart_bootfile_read(const char *bootfile, ssize_t *size)
1041{
1042	struct stat sb;
1043	void *code;
1044	int fd;
1045
1046	if (stat(bootfile, &sb) == -1)
1047		err(EXIT_FAILURE, "%s", bootfile);
1048	if (!S_ISREG(sb.st_mode))
1049		errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
1050	if (sb.st_size == 0)
1051		errx(EXIT_FAILURE, "%s: empty file", bootfile);
1052	if (*size > 0 && sb.st_size > *size)
1053		errx(EXIT_FAILURE, "%s: file too big (%zu limit)", bootfile,
1054		    *size);
1055
1056	*size = sb.st_size;
1057
1058	fd = open(bootfile, O_RDONLY);
1059	if (fd == -1)
1060		err(EXIT_FAILURE, "%s", bootfile);
1061	code = malloc(*size);
1062	if (code == NULL)
1063		err(EXIT_FAILURE, NULL);
1064	if (read(fd, code, *size) != *size)
1065		err(EXIT_FAILURE, "%s", bootfile);
1066	close(fd);
1067
1068	return (code);
1069}
1070
1071static void
1072gpart_write_partcode(struct ggeom *gp, int idx, void *code, ssize_t size)
1073{
1074	char dsf[128];
1075	struct gprovider *pp;
1076	const char *s;
1077	char *buf;
1078	off_t bsize;
1079	int fd;
1080
1081	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1082		s = find_provcfg(pp, "index");
1083		if (s == NULL)
1084			continue;
1085		if (atoi(s) == idx)
1086			break;
1087	}
1088
1089	if (pp != NULL) {
1090		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1091		fd = open(dsf, O_WRONLY);
1092		if (fd == -1)
1093			err(EXIT_FAILURE, "%s", dsf);
1094		if (lseek(fd, size, SEEK_SET) != size)
1095			errx(EXIT_FAILURE, "%s: not enough space", dsf);
1096		if (lseek(fd, 0, SEEK_SET) != 0)
1097			err(EXIT_FAILURE, "%s", dsf);
1098
1099		/*
1100		 * When writing to a disk device, the write must be
1101		 * sector aligned and not write to any partial sectors,
1102		 * so round up the buffer size to the next sector and zero it.
1103		 */
1104		bsize = (size + pp->lg_sectorsize - 1) /
1105		    pp->lg_sectorsize * pp->lg_sectorsize;
1106		buf = calloc(1, bsize);
1107		if (buf == NULL)
1108			err(EXIT_FAILURE, "%s", dsf);
1109		bcopy(code, buf, size);
1110		if (write(fd, buf, bsize) != bsize)
1111			err(EXIT_FAILURE, "%s", dsf);
1112		free(buf);
1113		close(fd);
1114	} else
1115		errx(EXIT_FAILURE, "invalid partition index");
1116}
1117
1118static void
1119gpart_write_partcode_vtoc8(struct ggeom *gp, int idx, void *code)
1120{
1121	char dsf[128];
1122	struct gprovider *pp;
1123	const char *s;
1124	int installed, fd;
1125
1126	installed = 0;
1127	LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
1128		s = find_provcfg(pp, "index");
1129		if (s == NULL)
1130			continue;
1131		if (idx != 0 && atoi(s) != idx)
1132			continue;
1133		snprintf(dsf, sizeof(dsf), "/dev/%s", pp->lg_name);
1134		if (pp->lg_sectorsize != sizeof(struct vtoc8))
1135			errx(EXIT_FAILURE, "%s: unexpected sector "
1136			    "size (%d)\n", dsf, pp->lg_sectorsize);
1137		fd = open(dsf, O_WRONLY);
1138		if (fd == -1)
1139			err(EXIT_FAILURE, "%s", dsf);
1140		if (lseek(fd, VTOC_BOOTSIZE, SEEK_SET) != VTOC_BOOTSIZE)
1141			continue;
1142		/*
1143		 * We ignore the first VTOC_BOOTSIZE bytes of boot code in
1144		 * order to avoid overwriting the label.
1145		 */
1146		if (lseek(fd, sizeof(struct vtoc8), SEEK_SET) !=
1147		    sizeof(struct vtoc8))
1148			err(EXIT_FAILURE, "%s", dsf);
1149		if (write(fd, (caddr_t)code + sizeof(struct vtoc8),
1150		    VTOC_BOOTSIZE - sizeof(struct vtoc8)) != VTOC_BOOTSIZE -
1151		    sizeof(struct vtoc8))
1152			err(EXIT_FAILURE, "%s", dsf);
1153		installed++;
1154		close(fd);
1155		if (idx != 0 && atoi(s) == idx)
1156			break;
1157	}
1158	if (installed == 0)
1159		errx(EXIT_FAILURE, "%s: no partitions", gp->lg_name);
1160}
1161
1162static void
1163gpart_bootcode(struct gctl_req *req, unsigned int fl)
1164{
1165	struct gmesh mesh;
1166	struct gclass *classp;
1167	struct ggeom *gp;
1168	const char *s;
1169	void *bootcode, *partcode;
1170	size_t bootsize, partsize;
1171	int error, idx, vtoc8;
1172
1173	if (gctl_has_param(req, GPART_PARAM_BOOTCODE)) {
1174		s = gctl_get_ascii(req, GPART_PARAM_BOOTCODE);
1175		bootsize = 800 * 1024;		/* Arbitrary limit. */
1176		bootcode = gpart_bootfile_read(s, &bootsize);
1177		error = gctl_change_param(req, GPART_PARAM_BOOTCODE, bootsize,
1178		    bootcode);
1179		if (error)
1180			errc(EXIT_FAILURE, error, "internal error");
1181	} else {
1182		bootcode = NULL;
1183		bootsize = 0;
1184	}
1185
1186	s = gctl_get_ascii(req, "class");
1187	if (s == NULL)
1188		abort();
1189	error = geom_gettree(&mesh);
1190	if (error != 0)
1191		errc(EXIT_FAILURE, error, "Cannot get GEOM tree");
1192	classp = find_class(&mesh, s);
1193	if (classp == NULL) {
1194		geom_deletetree(&mesh);
1195		errx(EXIT_FAILURE, "Class %s not found.", s);
1196	}
1197	if (gctl_get_int(req, "nargs") != 1)
1198		errx(EXIT_FAILURE, "Invalid number of arguments.");
1199	s = gctl_get_ascii(req, "arg0");
1200	if (s == NULL)
1201		abort();
1202	gp = find_geom(classp, s);
1203	if (gp == NULL)
1204		errx(EXIT_FAILURE, "No such geom: %s.", s);
1205	s = find_geomcfg(gp, "scheme");
1206	if (s == NULL)
1207		errx(EXIT_FAILURE, "Scheme not found for geom %s", gp->lg_name);
1208	vtoc8 = 0;
1209	if (strcmp(s, "VTOC8") == 0)
1210		vtoc8 = 1;
1211
1212	if (gctl_has_param(req, GPART_PARAM_PARTCODE)) {
1213		s = gctl_get_ascii(req, GPART_PARAM_PARTCODE);
1214		partsize = vtoc8 != 0 ? VTOC_BOOTSIZE : bootsize * 1024;
1215		partcode = gpart_bootfile_read(s, &partsize);
1216		error = gctl_delete_param(req, GPART_PARAM_PARTCODE);
1217		if (error)
1218			errc(EXIT_FAILURE, error, "internal error");
1219	} else {
1220		partcode = NULL;
1221		partsize = 0;
1222	}
1223
1224	if (gctl_has_param(req, GPART_PARAM_INDEX)) {
1225		if (partcode == NULL)
1226			errx(EXIT_FAILURE, "-i is only valid with -p");
1227		idx = (int)gctl_get_intmax(req, GPART_PARAM_INDEX);
1228		if (idx < 1)
1229			errx(EXIT_FAILURE, "invalid partition index");
1230		error = gctl_delete_param(req, GPART_PARAM_INDEX);
1231		if (error)
1232			errc(EXIT_FAILURE, error, "internal error");
1233	} else
1234		idx = 0;
1235
1236	if (partcode != NULL) {
1237		if (vtoc8 == 0) {
1238			if (idx == 0)
1239				errx(EXIT_FAILURE, "missing -i option");
1240			gpart_write_partcode(gp, idx, partcode, partsize);
1241		} else {
1242			if (partsize != VTOC_BOOTSIZE)
1243				errx(EXIT_FAILURE, "invalid bootcode");
1244			gpart_write_partcode_vtoc8(gp, idx, partcode);
1245		}
1246	} else
1247		if (bootcode == NULL)
1248			errx(EXIT_FAILURE, "no -b nor -p");
1249
1250	if (bootcode != NULL)
1251		gpart_issue(req, fl);
1252
1253	geom_deletetree(&mesh);
1254}
1255
1256static void
1257gpart_print_error(const char *errstr)
1258{
1259	char *errmsg;
1260	int error;
1261
1262	error = strtol(errstr, &errmsg, 0);
1263	if (errmsg != errstr) {
1264		while (errmsg[0] == ' ')
1265			errmsg++;
1266		if (errmsg[0] != '\0')
1267			warnc(error, "%s", errmsg);
1268		else
1269			warnc(error, NULL);
1270	} else
1271		warnx("%s", errmsg);
1272}
1273
1274static void
1275gpart_issue(struct gctl_req *req, unsigned int fl __unused)
1276{
1277	char buf[4096];
1278	const char *errstr;
1279	int error, status;
1280
1281	if (gctl_get_int(req, "nargs") != 1)
1282		errx(EXIT_FAILURE, "Invalid number of arguments.");
1283	(void)gctl_delete_param(req, "nargs");
1284
1285	/* autofill parameters (if applicable). */
1286	error = gpart_autofill(req);
1287	if (error) {
1288		warnc(error, "autofill");
1289		status = EXIT_FAILURE;
1290		goto done;
1291	}
1292
1293	bzero(buf, sizeof(buf));
1294	gctl_rw_param(req, "output", sizeof(buf), buf);
1295	errstr = gctl_issue(req);
1296	if (errstr == NULL || errstr[0] == '\0') {
1297		if (buf[0] != '\0')
1298			printf("%s", buf);
1299		status = EXIT_SUCCESS;
1300		goto done;
1301	}
1302
1303	gpart_print_error(errstr);
1304	status = EXIT_FAILURE;
1305
1306 done:
1307	gctl_free(req);
1308	exit(status);
1309}
1310