13101ed1bSPoul-Henning Kamp /*-
23728855aSPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
33728855aSPedro F. Giffuni *
43101ed1bSPoul-Henning Kamp * Copyright (c) 2002 Poul-Henning Kamp
53101ed1bSPoul-Henning Kamp * Copyright (c) 2002 Networks Associates Technology, Inc.
63101ed1bSPoul-Henning Kamp * All rights reserved.
77f16b501SAlexander Motin * Copyright (c) 2022 Alexander Motin <mav@FreeBSD.org>
83101ed1bSPoul-Henning Kamp *
93101ed1bSPoul-Henning Kamp * This software was developed for the FreeBSD Project by Poul-Henning Kamp
103101ed1bSPoul-Henning Kamp * and NAI Labs, the Security Research Division of Network Associates, Inc.
113101ed1bSPoul-Henning Kamp * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
123101ed1bSPoul-Henning Kamp * DARPA CHATS research program.
133101ed1bSPoul-Henning Kamp *
143101ed1bSPoul-Henning Kamp * Redistribution and use in source and binary forms, with or without
153101ed1bSPoul-Henning Kamp * modification, are permitted provided that the following conditions
163101ed1bSPoul-Henning Kamp * are met:
173101ed1bSPoul-Henning Kamp * 1. Redistributions of source code must retain the above copyright
183101ed1bSPoul-Henning Kamp * notice, this list of conditions and the following disclaimer.
193101ed1bSPoul-Henning Kamp * 2. Redistributions in binary form must reproduce the above copyright
203101ed1bSPoul-Henning Kamp * notice, this list of conditions and the following disclaimer in the
213101ed1bSPoul-Henning Kamp * documentation and/or other materials provided with the distribution.
223101ed1bSPoul-Henning Kamp * 3. The names of the authors may not be used to endorse or promote
233101ed1bSPoul-Henning Kamp * products derived from this software without specific prior written
243101ed1bSPoul-Henning Kamp * permission.
253101ed1bSPoul-Henning Kamp *
263101ed1bSPoul-Henning Kamp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
273101ed1bSPoul-Henning Kamp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
283101ed1bSPoul-Henning Kamp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
293101ed1bSPoul-Henning Kamp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
303101ed1bSPoul-Henning Kamp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
313101ed1bSPoul-Henning Kamp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
323101ed1bSPoul-Henning Kamp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
333101ed1bSPoul-Henning Kamp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
343101ed1bSPoul-Henning Kamp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
353101ed1bSPoul-Henning Kamp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
363101ed1bSPoul-Henning Kamp * SUCH DAMAGE.
373101ed1bSPoul-Henning Kamp */
383101ed1bSPoul-Henning Kamp
393101ed1bSPoul-Henning Kamp #include <sys/param.h>
403101ed1bSPoul-Henning Kamp #include <sys/systm.h>
413101ed1bSPoul-Henning Kamp #include <sys/conf.h>
423101ed1bSPoul-Henning Kamp #include <sys/malloc.h>
43fb9483afSPoul-Henning Kamp #include <sys/sbuf.h>
443101ed1bSPoul-Henning Kamp
453117e544SPoul-Henning Kamp #include <vm/vm.h>
463117e544SPoul-Henning Kamp #include <vm/vm_extern.h>
473117e544SPoul-Henning Kamp
483101ed1bSPoul-Henning Kamp #include <geom/geom.h>
493101ed1bSPoul-Henning Kamp #include <geom/geom_int.h>
5063728c47SPoul-Henning Kamp #define GCTL_TABLE 1
513117e544SPoul-Henning Kamp #include <geom/geom_ctl.h>
523117e544SPoul-Henning Kamp
53fb9483afSPoul-Henning Kamp #include <machine/stdarg.h>
54fb9483afSPoul-Henning Kamp
553101ed1bSPoul-Henning Kamp static d_ioctl_t g_ctl_ioctl;
563101ed1bSPoul-Henning Kamp
57d49d7ca5SPoul-Henning Kamp static struct cdevsw g_ctl_cdevsw = {
58dc08ffecSPoul-Henning Kamp .d_version = D_VERSION,
59283a5a37SWarner Losh .d_flags = 0,
60d49d7ca5SPoul-Henning Kamp .d_ioctl = g_ctl_ioctl,
61d49d7ca5SPoul-Henning Kamp .d_name = "g_ctl",
623101ed1bSPoul-Henning Kamp };
633101ed1bSPoul-Henning Kamp
6401b9c48bSAlexander Motin CTASSERT(GCTL_PARAM_RD == VM_PROT_READ);
6501b9c48bSAlexander Motin CTASSERT(GCTL_PARAM_WR == VM_PROT_WRITE);
6601b9c48bSAlexander Motin
67d49d7ca5SPoul-Henning Kamp void
g_ctl_init(void)683101ed1bSPoul-Henning Kamp g_ctl_init(void)
693101ed1bSPoul-Henning Kamp {
70d49d7ca5SPoul-Henning Kamp
7123b70c1aSKonstantin Belousov make_dev_credf(MAKEDEV_ETERNAL, &g_ctl_cdevsw, 0, NULL,
72d49d7ca5SPoul-Henning Kamp UID_ROOT, GID_OPERATOR, 0640, PATH_GEOM_CTL);
733101ed1bSPoul-Henning Kamp }
743101ed1bSPoul-Henning Kamp
753117e544SPoul-Henning Kamp /*
76625ee733SAndrey V. Elsukov * Report an error back to the user in ascii format. Return nerror
77625ee733SAndrey V. Elsukov * or EINVAL if nerror isn't specified.
783117e544SPoul-Henning Kamp */
7963728c47SPoul-Henning Kamp int
gctl_error(struct gctl_req * req,const char * fmt,...)80fb9483afSPoul-Henning Kamp gctl_error(struct gctl_req *req, const char *fmt, ...)
813117e544SPoul-Henning Kamp {
82fb9483afSPoul-Henning Kamp va_list ap;
833117e544SPoul-Henning Kamp
8483d771deSPoul-Henning Kamp if (req == NULL)
8583d771deSPoul-Henning Kamp return (EINVAL);
8683d771deSPoul-Henning Kamp
8783d771deSPoul-Henning Kamp /* We only record the first error */
88cdae8431SPawel Jakub Dawidek if (sbuf_done(req->serror)) {
89cdae8431SPawel Jakub Dawidek if (!req->nerror)
90cdae8431SPawel Jakub Dawidek req->nerror = EEXIST;
91c8cc5689SKirk McKusick #ifdef DIAGNOSTIC
92c8cc5689SKirk McKusick printf("gctl_error: buffer closed, message discarded.\n");
93c8cc5689SKirk McKusick #endif
9483d771deSPoul-Henning Kamp return (req->nerror);
95625ee733SAndrey V. Elsukov }
96625ee733SAndrey V. Elsukov if (!req->nerror)
97625ee733SAndrey V. Elsukov req->nerror = EINVAL;
9883d771deSPoul-Henning Kamp
99c8cc5689SKirk McKusick /* If this is the last of several messages, indent it on a new line */
100c8cc5689SKirk McKusick if (sbuf_len(req->serror) > 0)
101c8cc5689SKirk McKusick sbuf_cat(req->serror, "\n\t");
102fb9483afSPoul-Henning Kamp va_start(ap, fmt);
10383d771deSPoul-Henning Kamp sbuf_vprintf(req->serror, fmt, ap);
104a1a9b445SPoul-Henning Kamp va_end(ap);
105c8cc5689SKirk McKusick gctl_post_messages(req);
106c8cc5689SKirk McKusick return (req->nerror);
107c8cc5689SKirk McKusick }
108c8cc5689SKirk McKusick
109c8cc5689SKirk McKusick /*
110c8cc5689SKirk McKusick * The gctl_error() function will only report a single message.
111c8cc5689SKirk McKusick * Commands that handle multiple devices may want to report a
112c8cc5689SKirk McKusick * message for each of the devices. The gctl_msg() function
113c8cc5689SKirk McKusick * can be called multiple times to post messages. When done
114c8cc5689SKirk McKusick * the application must either call gctl_post_messages() or
115c8cc5689SKirk McKusick * call gctl_error() to cause the messages to be reported to
116c8cc5689SKirk McKusick * the calling process.
11790e29718SKirk McKusick *
11890e29718SKirk McKusick * The errno argument should be zero if it is an informational
11990e29718SKirk McKusick * message or an errno value (EINVAL, EBUSY, etc) if it is an error.
12090e29718SKirk McKusick * If any of the messages has a non-zero errno, the utility will
12190e29718SKirk McKusick * EXIT_FAILURE. If only informational messages (with zero errno)
12290e29718SKirk McKusick * are posted, the utility will EXIT_SUCCESS.
123c8cc5689SKirk McKusick */
124c8cc5689SKirk McKusick void
gctl_msg(struct gctl_req * req,int errno,const char * fmt,...)12590e29718SKirk McKusick gctl_msg(struct gctl_req *req, int errno, const char *fmt, ...)
126c8cc5689SKirk McKusick {
127c8cc5689SKirk McKusick va_list ap;
128c8cc5689SKirk McKusick
129c8cc5689SKirk McKusick if (req == NULL)
130c8cc5689SKirk McKusick return;
131c8cc5689SKirk McKusick if (sbuf_done(req->serror)) {
132c8cc5689SKirk McKusick #ifdef DIAGNOSTIC
133c8cc5689SKirk McKusick printf("gctl_msg: buffer closed, message discarded.\n");
134c8cc5689SKirk McKusick #endif
135c8cc5689SKirk McKusick return;
136c8cc5689SKirk McKusick }
13790e29718SKirk McKusick if (req->nerror == 0)
13890e29718SKirk McKusick req->nerror = errno;
139c8cc5689SKirk McKusick /* Put second and later messages indented on a new line */
140c8cc5689SKirk McKusick if (sbuf_len(req->serror) > 0)
141c8cc5689SKirk McKusick sbuf_cat(req->serror, "\n\t");
142c8cc5689SKirk McKusick va_start(ap, fmt);
143c8cc5689SKirk McKusick sbuf_vprintf(req->serror, fmt, ap);
144c8cc5689SKirk McKusick va_end(ap);
145c8cc5689SKirk McKusick }
146c8cc5689SKirk McKusick
147c8cc5689SKirk McKusick /*
148c8cc5689SKirk McKusick * Post the messages to the user.
149c8cc5689SKirk McKusick */
150c8cc5689SKirk McKusick void
gctl_post_messages(struct gctl_req * req)151c8cc5689SKirk McKusick gctl_post_messages(struct gctl_req *req)
152c8cc5689SKirk McKusick {
153c8cc5689SKirk McKusick
154c8cc5689SKirk McKusick if (sbuf_done(req->serror)) {
155c8cc5689SKirk McKusick #ifdef DIAGNOSTIC
156c8cc5689SKirk McKusick printf("gctl_post_messages: message buffer already closed.\n");
157c8cc5689SKirk McKusick #endif
158c8cc5689SKirk McKusick return;
159c8cc5689SKirk McKusick }
16083d771deSPoul-Henning Kamp sbuf_finish(req->serror);
16168f15640SPoul-Henning Kamp if (g_debugflags & G_F_CTLDUMP)
162c8cc5689SKirk McKusick printf("gctl %p message(s) \"%s\"\n", req,
163c8cc5689SKirk McKusick sbuf_data(req->serror));
1643117e544SPoul-Henning Kamp }
1653117e544SPoul-Henning Kamp
1663117e544SPoul-Henning Kamp /*
1673117e544SPoul-Henning Kamp * Allocate space and copyin() something.
1683117e544SPoul-Henning Kamp * XXX: this should really be a standard function in the kernel.
1693117e544SPoul-Henning Kamp */
1703117e544SPoul-Henning Kamp static void *
geom_alloc_copyin(struct gctl_req * req,void * uaddr,size_t len)17183d771deSPoul-Henning Kamp geom_alloc_copyin(struct gctl_req *req, void *uaddr, size_t len)
1723117e544SPoul-Henning Kamp {
1733117e544SPoul-Henning Kamp void *ptr;
1743117e544SPoul-Henning Kamp
1753117e544SPoul-Henning Kamp ptr = g_malloc(len, M_WAITOK);
176625ee733SAndrey V. Elsukov req->nerror = copyin(uaddr, ptr, len);
17783d771deSPoul-Henning Kamp if (!req->nerror)
1783117e544SPoul-Henning Kamp return (ptr);
1793117e544SPoul-Henning Kamp g_free(ptr);
1803117e544SPoul-Henning Kamp return (NULL);
1813117e544SPoul-Henning Kamp }
1823117e544SPoul-Henning Kamp
18383d771deSPoul-Henning Kamp static void
gctl_copyin(struct gctl_req * req)18463728c47SPoul-Henning Kamp gctl_copyin(struct gctl_req *req)
1853117e544SPoul-Henning Kamp {
18663728c47SPoul-Henning Kamp struct gctl_req_arg *ap;
1873117e544SPoul-Henning Kamp char *p;
188b99bce73SPedro F. Giffuni u_int i;
1893117e544SPoul-Henning Kamp
190cd2e9086SMark Johnston if (req->narg > GEOM_CTL_ARG_MAX) {
19160a92c78SMark Johnston gctl_error(req, "too many arguments");
19260a92c78SMark Johnston req->arg = NULL;
19360a92c78SMark Johnston return;
19460a92c78SMark Johnston }
19560a92c78SMark Johnston
19683d771deSPoul-Henning Kamp ap = geom_alloc_copyin(req, req->arg, req->narg * sizeof(*ap));
19763728c47SPoul-Henning Kamp if (ap == NULL) {
1987f0e13dfSAndrey V. Elsukov gctl_error(req, "bad control request");
19983d771deSPoul-Henning Kamp req->arg = NULL;
20083d771deSPoul-Henning Kamp return;
20163728c47SPoul-Henning Kamp }
20263728c47SPoul-Henning Kamp
20383d771deSPoul-Henning Kamp /* Nothing have been copyin()'ed yet */
20483d771deSPoul-Henning Kamp for (i = 0; i < req->narg; i++) {
20583d771deSPoul-Henning Kamp ap[i].flag &= ~(GCTL_PARAM_NAMEKERNEL|GCTL_PARAM_VALUEKERNEL);
20683d771deSPoul-Henning Kamp ap[i].flag &= ~GCTL_PARAM_CHANGED;
20783d771deSPoul-Henning Kamp ap[i].kvalue = NULL;
20883d771deSPoul-Henning Kamp }
20983d771deSPoul-Henning Kamp
21083d771deSPoul-Henning Kamp for (i = 0; i < req->narg; i++) {
21163728c47SPoul-Henning Kamp if (ap[i].nlen < 1 || ap[i].nlen > SPECNAMELEN) {
2127f0e13dfSAndrey V. Elsukov gctl_error(req,
21383d771deSPoul-Henning Kamp "wrong param name length %d: %d", i, ap[i].nlen);
2143117e544SPoul-Henning Kamp break;
21563728c47SPoul-Henning Kamp }
21683d771deSPoul-Henning Kamp p = geom_alloc_copyin(req, ap[i].name, ap[i].nlen);
21763728c47SPoul-Henning Kamp if (p == NULL)
2183117e544SPoul-Henning Kamp break;
21963728c47SPoul-Henning Kamp if (p[ap[i].nlen - 1] != '\0') {
2207f0e13dfSAndrey V. Elsukov gctl_error(req, "unterminated param name");
2213117e544SPoul-Henning Kamp g_free(p);
2223117e544SPoul-Henning Kamp break;
2233117e544SPoul-Henning Kamp }
22463728c47SPoul-Henning Kamp ap[i].name = p;
22583d771deSPoul-Henning Kamp ap[i].flag |= GCTL_PARAM_NAMEKERNEL;
226e8cde1acSPoul-Henning Kamp if (ap[i].len <= 0) {
2277f0e13dfSAndrey V. Elsukov gctl_error(req, "negative param length");
22883d771deSPoul-Henning Kamp break;
2293117e544SPoul-Henning Kamp }
23001b9c48bSAlexander Motin if (ap[i].flag & GCTL_PARAM_RD) {
23183d771deSPoul-Henning Kamp p = geom_alloc_copyin(req, ap[i].value, ap[i].len);
23283d771deSPoul-Henning Kamp if (p == NULL)
23383d771deSPoul-Henning Kamp break;
23483d771deSPoul-Henning Kamp if ((ap[i].flag & GCTL_PARAM_ASCII) &&
23583d771deSPoul-Henning Kamp p[ap[i].len - 1] != '\0') {
2367f0e13dfSAndrey V. Elsukov gctl_error(req, "unterminated param value");
23783d771deSPoul-Henning Kamp g_free(p);
23883d771deSPoul-Henning Kamp break;
23983d771deSPoul-Henning Kamp }
24001b9c48bSAlexander Motin } else {
24101b9c48bSAlexander Motin p = g_malloc(ap[i].len, M_WAITOK | M_ZERO);
24201b9c48bSAlexander Motin }
24383d771deSPoul-Henning Kamp ap[i].kvalue = p;
24483d771deSPoul-Henning Kamp ap[i].flag |= GCTL_PARAM_VALUEKERNEL;
24583d771deSPoul-Henning Kamp }
2463117e544SPoul-Henning Kamp req->arg = ap;
24783d771deSPoul-Henning Kamp return;
2483117e544SPoul-Henning Kamp }
24983d771deSPoul-Henning Kamp
25083d771deSPoul-Henning Kamp static void
gctl_copyout(struct gctl_req * req)25183d771deSPoul-Henning Kamp gctl_copyout(struct gctl_req *req)
25283d771deSPoul-Henning Kamp {
25383d771deSPoul-Henning Kamp int error, i;
25483d771deSPoul-Henning Kamp struct gctl_req_arg *ap;
25583d771deSPoul-Henning Kamp
25683d771deSPoul-Henning Kamp if (req->nerror)
25783d771deSPoul-Henning Kamp return;
25883d771deSPoul-Henning Kamp error = 0;
25983d771deSPoul-Henning Kamp ap = req->arg;
26083d771deSPoul-Henning Kamp for (i = 0; i < req->narg; i++, ap++) {
26183d771deSPoul-Henning Kamp if (!(ap->flag & GCTL_PARAM_CHANGED))
26283d771deSPoul-Henning Kamp continue;
26383d771deSPoul-Henning Kamp error = copyout(ap->kvalue, ap->value, ap->len);
26483d771deSPoul-Henning Kamp if (!error)
26583d771deSPoul-Henning Kamp continue;
26683d771deSPoul-Henning Kamp req->nerror = error;
26783d771deSPoul-Henning Kamp return;
26883d771deSPoul-Henning Kamp }
26983d771deSPoul-Henning Kamp return;
2703117e544SPoul-Henning Kamp }
2713117e544SPoul-Henning Kamp
2723117e544SPoul-Henning Kamp static void
gctl_free(struct gctl_req * req)2733e7b7bb1SPoul-Henning Kamp gctl_free(struct gctl_req *req)
2743e7b7bb1SPoul-Henning Kamp {
275b99bce73SPedro F. Giffuni u_int i;
2763e7b7bb1SPoul-Henning Kamp
277524d7a4dSAndrey V. Elsukov sbuf_delete(req->serror);
27883d771deSPoul-Henning Kamp if (req->arg == NULL)
27983d771deSPoul-Henning Kamp return;
2803e7b7bb1SPoul-Henning Kamp for (i = 0; i < req->narg; i++) {
28183d771deSPoul-Henning Kamp if (req->arg[i].flag & GCTL_PARAM_NAMEKERNEL)
2823e7b7bb1SPoul-Henning Kamp g_free(req->arg[i].name);
28383d771deSPoul-Henning Kamp if ((req->arg[i].flag & GCTL_PARAM_VALUEKERNEL) &&
28483d771deSPoul-Henning Kamp req->arg[i].len > 0)
28583d771deSPoul-Henning Kamp g_free(req->arg[i].kvalue);
2863e7b7bb1SPoul-Henning Kamp }
2873e7b7bb1SPoul-Henning Kamp g_free(req->arg);
2883e7b7bb1SPoul-Henning Kamp }
2893e7b7bb1SPoul-Henning Kamp
2903e7b7bb1SPoul-Henning Kamp static void
gctl_dump(struct gctl_req * req,const char * what)291dd7a5bc1SAlexander Motin gctl_dump(struct gctl_req *req, const char *what)
2923117e544SPoul-Henning Kamp {
2937f0e13dfSAndrey V. Elsukov struct gctl_req_arg *ap;
2943117e544SPoul-Henning Kamp u_int i;
29583d771deSPoul-Henning Kamp int j;
2963117e544SPoul-Henning Kamp
297dd7a5bc1SAlexander Motin printf("Dump of gctl %s at %p:\n", what, req);
29883d771deSPoul-Henning Kamp if (req->nerror > 0) {
29983d771deSPoul-Henning Kamp printf(" nerror:\t%d\n", req->nerror);
30083d771deSPoul-Henning Kamp if (sbuf_len(req->serror) > 0)
30183d771deSPoul-Henning Kamp printf(" error:\t\"%s\"\n", sbuf_data(req->serror));
3023117e544SPoul-Henning Kamp }
3037f0e13dfSAndrey V. Elsukov if (req->arg == NULL)
3047f0e13dfSAndrey V. Elsukov return;
3053117e544SPoul-Henning Kamp for (i = 0; i < req->narg; i++) {
3063117e544SPoul-Henning Kamp ap = &req->arg[i];
30783d771deSPoul-Henning Kamp if (!(ap->flag & GCTL_PARAM_NAMEKERNEL))
30883d771deSPoul-Henning Kamp printf(" param:\t%d@%p", ap->nlen, ap->name);
30983d771deSPoul-Henning Kamp else
3103117e544SPoul-Henning Kamp printf(" param:\t\"%s\"", ap->name);
31163728c47SPoul-Henning Kamp printf(" [%s%s%d] = ",
31263728c47SPoul-Henning Kamp ap->flag & GCTL_PARAM_RD ? "R" : "",
31363728c47SPoul-Henning Kamp ap->flag & GCTL_PARAM_WR ? "W" : "",
31463728c47SPoul-Henning Kamp ap->len);
31583d771deSPoul-Henning Kamp if (!(ap->flag & GCTL_PARAM_VALUEKERNEL)) {
31683d771deSPoul-Henning Kamp printf(" =@ %p", ap->value);
31783d771deSPoul-Henning Kamp } else if (ap->flag & GCTL_PARAM_ASCII) {
31883d771deSPoul-Henning Kamp printf("\"%s\"", (char *)ap->kvalue);
3193117e544SPoul-Henning Kamp } else if (ap->len > 0) {
320fd02a423SPoul-Henning Kamp for (j = 0; j < ap->len && j < 512; j++)
32183d771deSPoul-Henning Kamp printf(" %02x", ((u_char *)ap->kvalue)[j]);
3223117e544SPoul-Henning Kamp } else {
32383d771deSPoul-Henning Kamp printf(" = %p", ap->kvalue);
3243117e544SPoul-Henning Kamp }
3253117e544SPoul-Henning Kamp printf("\n");
3263117e544SPoul-Henning Kamp }
3273117e544SPoul-Henning Kamp }
3283117e544SPoul-Henning Kamp
32941063f93SMarcel Moolenaar int
gctl_set_param(struct gctl_req * req,const char * param,void const * ptr,int len)33041063f93SMarcel Moolenaar gctl_set_param(struct gctl_req *req, const char *param, void const *ptr,
33141063f93SMarcel Moolenaar int len)
3321ee055e6SPoul-Henning Kamp {
333b99bce73SPedro F. Giffuni u_int i;
3341ee055e6SPoul-Henning Kamp struct gctl_req_arg *ap;
3351ee055e6SPoul-Henning Kamp
3361ee055e6SPoul-Henning Kamp for (i = 0; i < req->narg; i++) {
3371ee055e6SPoul-Henning Kamp ap = &req->arg[i];
3381ee055e6SPoul-Henning Kamp if (strcmp(param, ap->name))
3391ee055e6SPoul-Henning Kamp continue;
34041063f93SMarcel Moolenaar if (!(ap->flag & GCTL_PARAM_WR))
34141063f93SMarcel Moolenaar return (EPERM);
34241063f93SMarcel Moolenaar ap->flag |= GCTL_PARAM_CHANGED;
34383d771deSPoul-Henning Kamp if (ap->len < len) {
34441063f93SMarcel Moolenaar bcopy(ptr, ap->kvalue, ap->len);
34541063f93SMarcel Moolenaar return (ENOSPC);
3461ee055e6SPoul-Henning Kamp }
34783d771deSPoul-Henning Kamp bcopy(ptr, ap->kvalue, len);
34841063f93SMarcel Moolenaar return (0);
3491ee055e6SPoul-Henning Kamp }
35041063f93SMarcel Moolenaar return (EINVAL);
35141063f93SMarcel Moolenaar }
35241063f93SMarcel Moolenaar
35341063f93SMarcel Moolenaar void
gctl_set_param_err(struct gctl_req * req,const char * param,void const * ptr,int len)35441063f93SMarcel Moolenaar gctl_set_param_err(struct gctl_req *req, const char *param, void const *ptr,
35541063f93SMarcel Moolenaar int len)
35641063f93SMarcel Moolenaar {
35741063f93SMarcel Moolenaar
35841063f93SMarcel Moolenaar switch (gctl_set_param(req, param, ptr, len)) {
35941063f93SMarcel Moolenaar case EPERM:
36041063f93SMarcel Moolenaar gctl_error(req, "No write access %s argument", param);
36141063f93SMarcel Moolenaar break;
36241063f93SMarcel Moolenaar case ENOSPC:
36341063f93SMarcel Moolenaar gctl_error(req, "Wrong length %s argument", param);
36441063f93SMarcel Moolenaar break;
36541063f93SMarcel Moolenaar case EINVAL:
3661ee055e6SPoul-Henning Kamp gctl_error(req, "Missing %s argument", param);
36741063f93SMarcel Moolenaar break;
36841063f93SMarcel Moolenaar }
3691ee055e6SPoul-Henning Kamp }
3701ee055e6SPoul-Henning Kamp
37163728c47SPoul-Henning Kamp void *
gctl_get_param_flags(struct gctl_req * req,const char * param,int flags,int * len)3727f16b501SAlexander Motin gctl_get_param_flags(struct gctl_req *req, const char *param, int flags, int *len)
37363728c47SPoul-Henning Kamp {
374b99bce73SPedro F. Giffuni u_int i;
37563728c47SPoul-Henning Kamp void *p;
37663728c47SPoul-Henning Kamp struct gctl_req_arg *ap;
37763728c47SPoul-Henning Kamp
37863728c47SPoul-Henning Kamp for (i = 0; i < req->narg; i++) {
37963728c47SPoul-Henning Kamp ap = &req->arg[i];
38063728c47SPoul-Henning Kamp if (strcmp(param, ap->name))
38163728c47SPoul-Henning Kamp continue;
3827f16b501SAlexander Motin if ((ap->flag & flags) != flags)
38363728c47SPoul-Henning Kamp continue;
38483d771deSPoul-Henning Kamp p = ap->kvalue;
38563728c47SPoul-Henning Kamp if (len != NULL)
38683d771deSPoul-Henning Kamp *len = ap->len;
38783d771deSPoul-Henning Kamp return (p);
38883d771deSPoul-Henning Kamp }
38983d771deSPoul-Henning Kamp return (NULL);
39083d771deSPoul-Henning Kamp }
39183d771deSPoul-Henning Kamp
3927f16b501SAlexander Motin void *
gctl_get_param(struct gctl_req * req,const char * param,int * len)3937f16b501SAlexander Motin gctl_get_param(struct gctl_req *req, const char *param, int *len)
3947f16b501SAlexander Motin {
3957f16b501SAlexander Motin
3967f16b501SAlexander Motin return (gctl_get_param_flags(req, param, GCTL_PARAM_RD, len));
3977f16b501SAlexander Motin }
3987f16b501SAlexander Motin
39983d771deSPoul-Henning Kamp char const *
gctl_get_asciiparam(struct gctl_req * req,const char * param)40083d771deSPoul-Henning Kamp gctl_get_asciiparam(struct gctl_req *req, const char *param)
40183d771deSPoul-Henning Kamp {
40283d771deSPoul-Henning Kamp char const *p;
4037f16b501SAlexander Motin int len;
40483d771deSPoul-Henning Kamp
4057f16b501SAlexander Motin p = gctl_get_param_flags(req, param, GCTL_PARAM_RD, &len);
4067f16b501SAlexander Motin if (p == NULL)
4077f16b501SAlexander Motin return (NULL);
4087f16b501SAlexander Motin if (len < 1) {
4097f16b501SAlexander Motin gctl_error(req, "Argument without length (%s)", param);
41083d771deSPoul-Henning Kamp return (NULL);
41183d771deSPoul-Henning Kamp }
4127f16b501SAlexander Motin if (p[len - 1] != '\0') {
41383d771deSPoul-Henning Kamp gctl_error(req, "Unterminated argument (%s)", param);
41483d771deSPoul-Henning Kamp return (NULL);
41583d771deSPoul-Henning Kamp }
41663728c47SPoul-Henning Kamp return (p);
41763728c47SPoul-Henning Kamp }
41863728c47SPoul-Henning Kamp
419ca3d750eSPoul-Henning Kamp void *
gctl_get_paraml_opt(struct gctl_req * req,const char * param,int len)4201e00bb45SChuck Silvers gctl_get_paraml_opt(struct gctl_req *req, const char *param, int len)
421ca3d750eSPoul-Henning Kamp {
422ca3d750eSPoul-Henning Kamp int i;
423ca3d750eSPoul-Henning Kamp void *p;
424ca3d750eSPoul-Henning Kamp
425ca3d750eSPoul-Henning Kamp p = gctl_get_param(req, param, &i);
4261e00bb45SChuck Silvers if (i != len) {
427ca3d750eSPoul-Henning Kamp p = NULL;
428ca3d750eSPoul-Henning Kamp gctl_error(req, "Wrong length %s argument", param);
429ca3d750eSPoul-Henning Kamp }
430ca3d750eSPoul-Henning Kamp return (p);
431ca3d750eSPoul-Henning Kamp }
432ca3d750eSPoul-Henning Kamp
4331e00bb45SChuck Silvers void *
gctl_get_paraml(struct gctl_req * req,const char * param,int len)4341e00bb45SChuck Silvers gctl_get_paraml(struct gctl_req *req, const char *param, int len)
4351e00bb45SChuck Silvers {
4361e00bb45SChuck Silvers void *p;
4371e00bb45SChuck Silvers
4381e00bb45SChuck Silvers p = gctl_get_paraml_opt(req, param, len);
4391e00bb45SChuck Silvers if (p == NULL)
4401e00bb45SChuck Silvers gctl_error(req, "Missing %s argument", param);
4411e00bb45SChuck Silvers return (p);
4421e00bb45SChuck Silvers }
4431e00bb45SChuck Silvers
44483d771deSPoul-Henning Kamp struct g_class *
gctl_get_class(struct gctl_req * req,char const * arg)44583d771deSPoul-Henning Kamp gctl_get_class(struct gctl_req *req, char const *arg)
44663728c47SPoul-Henning Kamp {
44783d771deSPoul-Henning Kamp char const *p;
44863728c47SPoul-Henning Kamp struct g_class *cp;
44963728c47SPoul-Henning Kamp
45083d771deSPoul-Henning Kamp p = gctl_get_asciiparam(req, arg);
4510ab851aaSXin LI if (p == NULL) {
4520ab851aaSXin LI gctl_error(req, "Missing %s argument", arg);
45363728c47SPoul-Henning Kamp return (NULL);
4540ab851aaSXin LI }
45563728c47SPoul-Henning Kamp LIST_FOREACH(cp, &g_classes, class) {
45683d771deSPoul-Henning Kamp if (!strcmp(p, cp->name))
45763728c47SPoul-Henning Kamp return (cp);
45863728c47SPoul-Henning Kamp }
4590ab851aaSXin LI gctl_error(req, "Class not found: \"%s\"", p);
46063728c47SPoul-Henning Kamp return (NULL);
46163728c47SPoul-Henning Kamp }
46263728c47SPoul-Henning Kamp
46383d771deSPoul-Henning Kamp struct g_geom *
gctl_get_geom(struct gctl_req * req,struct g_class * mp,char const * arg)464a450ecfdSXin LI gctl_get_geom(struct gctl_req *req, struct g_class *mp, char const *arg)
46563728c47SPoul-Henning Kamp {
46683d771deSPoul-Henning Kamp char const *p;
46763728c47SPoul-Henning Kamp struct g_geom *gp;
46863728c47SPoul-Henning Kamp
469a450ecfdSXin LI MPASS(mp != NULL);
47083d771deSPoul-Henning Kamp p = gctl_get_asciiparam(req, arg);
4710ab851aaSXin LI if (p == NULL) {
4720ab851aaSXin LI gctl_error(req, "Missing %s argument", arg);
473a92b7d49SXin LI return (NULL);
4740ab851aaSXin LI }
475a450ecfdSXin LI LIST_FOREACH(gp, &mp->geom, geom)
47683d771deSPoul-Henning Kamp if (!strcmp(p, gp->name))
47763728c47SPoul-Henning Kamp return (gp);
478a92b7d49SXin LI gctl_error(req, "Geom not found: \"%s\"", p);
47963728c47SPoul-Henning Kamp return (NULL);
48063728c47SPoul-Henning Kamp }
48163728c47SPoul-Henning Kamp
48283d771deSPoul-Henning Kamp struct g_provider *
gctl_get_provider(struct gctl_req * req,char const * arg)48383d771deSPoul-Henning Kamp gctl_get_provider(struct gctl_req *req, char const *arg)
48463728c47SPoul-Henning Kamp {
48583d771deSPoul-Henning Kamp char const *p;
48663728c47SPoul-Henning Kamp struct g_provider *pp;
48763728c47SPoul-Henning Kamp
48883d771deSPoul-Henning Kamp p = gctl_get_asciiparam(req, arg);
4890ab851aaSXin LI if (p == NULL) {
4900ab851aaSXin LI gctl_error(req, "Missing '%s' argument", arg);
49163728c47SPoul-Henning Kamp return (NULL);
4920ab851aaSXin LI }
493d6c5e716SPoul-Henning Kamp pp = g_provider_by_name(p);
494d6c5e716SPoul-Henning Kamp if (pp != NULL)
49563728c47SPoul-Henning Kamp return (pp);
496a92b7d49SXin LI gctl_error(req, "Provider not found: \"%s\"", p);
49763728c47SPoul-Henning Kamp return (NULL);
49863728c47SPoul-Henning Kamp }
49963728c47SPoul-Henning Kamp
50083d771deSPoul-Henning Kamp static void
g_ctl_getxml(struct gctl_req * req,struct g_class * mp)5017f16b501SAlexander Motin g_ctl_getxml(struct gctl_req *req, struct g_class *mp)
5027f16b501SAlexander Motin {
5037f16b501SAlexander Motin const char *name;
5047f16b501SAlexander Motin char *buf;
5057f16b501SAlexander Motin struct sbuf *sb;
5067f16b501SAlexander Motin int len, i = 0, n = 0, *parents;
5077f16b501SAlexander Motin struct g_geom *gp, **gps;
5087f16b501SAlexander Motin struct g_consumer *cp;
5097f16b501SAlexander Motin
5107f16b501SAlexander Motin parents = gctl_get_paraml(req, "parents", sizeof(*parents));
5117f16b501SAlexander Motin if (parents == NULL)
5127f16b501SAlexander Motin return;
5137f16b501SAlexander Motin name = gctl_get_asciiparam(req, "arg0");
5147f16b501SAlexander Motin n = 0;
5157f16b501SAlexander Motin LIST_FOREACH(gp, &mp->geom, geom) {
5167f16b501SAlexander Motin if (name && strcmp(gp->name, name) != 0)
5177f16b501SAlexander Motin continue;
5187f16b501SAlexander Motin n++;
5197f16b501SAlexander Motin if (*parents) {
5207f16b501SAlexander Motin LIST_FOREACH(cp, &gp->consumer, consumer)
5217f16b501SAlexander Motin n++;
5227f16b501SAlexander Motin }
5237f16b501SAlexander Motin }
5247f16b501SAlexander Motin gps = g_malloc((n + 1) * sizeof(*gps), M_WAITOK);
5257f16b501SAlexander Motin i = 0;
5267f16b501SAlexander Motin LIST_FOREACH(gp, &mp->geom, geom) {
5277f16b501SAlexander Motin if (name && strcmp(gp->name, name) != 0)
5287f16b501SAlexander Motin continue;
5297f16b501SAlexander Motin gps[i++] = gp;
5307f16b501SAlexander Motin if (*parents) {
5317f16b501SAlexander Motin LIST_FOREACH(cp, &gp->consumer, consumer) {
5327f16b501SAlexander Motin if (cp->provider != NULL)
5337f16b501SAlexander Motin gps[i++] = cp->provider->geom;
5347f16b501SAlexander Motin }
5357f16b501SAlexander Motin }
5367f16b501SAlexander Motin }
5377f16b501SAlexander Motin KASSERT(i == n, ("different number of geoms found (%d != %d)",
5387f16b501SAlexander Motin i, n));
5397f16b501SAlexander Motin gps[i] = 0;
5407f16b501SAlexander Motin
5417f16b501SAlexander Motin buf = gctl_get_param_flags(req, "output", GCTL_PARAM_WR, &len);
5427f16b501SAlexander Motin if (buf == NULL) {
5437f16b501SAlexander Motin gctl_error(req, "output parameter missing");
5447f16b501SAlexander Motin g_free(gps);
5457f16b501SAlexander Motin return;
5467f16b501SAlexander Motin }
5477f16b501SAlexander Motin sb = sbuf_new(NULL, buf, len, SBUF_FIXEDLEN | SBUF_INCLUDENUL);
5487f16b501SAlexander Motin g_conf_specific(sb, gps);
5497f16b501SAlexander Motin gctl_set_param(req, "output", buf, 0);
5507f16b501SAlexander Motin if (sbuf_error(sb))
5517f16b501SAlexander Motin gctl_error(req, "output buffer overflow");
5527f16b501SAlexander Motin sbuf_delete(sb);
5537f16b501SAlexander Motin g_free(gps);
5547f16b501SAlexander Motin }
5557f16b501SAlexander Motin
5567f16b501SAlexander Motin static void
g_ctl_req(void * arg,int flag __unused)55783d771deSPoul-Henning Kamp g_ctl_req(void *arg, int flag __unused)
55863728c47SPoul-Henning Kamp {
55963728c47SPoul-Henning Kamp struct g_class *mp;
56083d771deSPoul-Henning Kamp struct gctl_req *req;
56183d771deSPoul-Henning Kamp char const *verb;
56263728c47SPoul-Henning Kamp
56363728c47SPoul-Henning Kamp g_topology_assert();
56483d771deSPoul-Henning Kamp req = arg;
56583d771deSPoul-Henning Kamp mp = gctl_get_class(req, "class");
5660ab851aaSXin LI if (mp == NULL)
56783d771deSPoul-Henning Kamp return;
56840fcadedSMarcel Moolenaar verb = gctl_get_param(req, "verb", NULL);
56940fcadedSMarcel Moolenaar if (verb == NULL) {
57040fcadedSMarcel Moolenaar gctl_error(req, "Verb missing");
57140fcadedSMarcel Moolenaar return;
57240fcadedSMarcel Moolenaar }
5737f16b501SAlexander Motin if (strcmp(verb, "getxml") == 0) {
5747f16b501SAlexander Motin g_ctl_getxml(req, mp);
5757f16b501SAlexander Motin } else if (mp->ctlreq == NULL) {
5767f16b501SAlexander Motin gctl_error(req, "Class takes no requests");
5777f16b501SAlexander Motin } else {
57883d771deSPoul-Henning Kamp mp->ctlreq(req, mp, verb);
5797f16b501SAlexander Motin }
58063728c47SPoul-Henning Kamp g_topology_assert();
58163728c47SPoul-Henning Kamp }
58263728c47SPoul-Henning Kamp
5833117e544SPoul-Henning Kamp static int
g_ctl_ioctl_ctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)58489c9c53dSPoul-Henning Kamp g_ctl_ioctl_ctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
5853117e544SPoul-Henning Kamp {
58663728c47SPoul-Henning Kamp struct gctl_req *req;
58785986ce0SPoul-Henning Kamp int nerror;
5883117e544SPoul-Henning Kamp
5893117e544SPoul-Henning Kamp req = (void *)data;
59083d771deSPoul-Henning Kamp req->nerror = 0;
59163728c47SPoul-Henning Kamp /* It is an error if we cannot return an error text */
59283d771deSPoul-Henning Kamp if (req->lerror < 2)
5933117e544SPoul-Henning Kamp return (EINVAL);
59463728c47SPoul-Henning Kamp if (!useracc(req->error, req->lerror, VM_PROT_WRITE))
59563728c47SPoul-Henning Kamp return (EINVAL);
59663728c47SPoul-Henning Kamp
597d14a7ff1SAndrey V. Elsukov req->serror = sbuf_new_auto();
59863728c47SPoul-Henning Kamp /* Check the version */
599d14a7ff1SAndrey V. Elsukov if (req->version != GCTL_VERSION) {
600d14a7ff1SAndrey V. Elsukov gctl_error(req, "kernel and libgeom version mismatch.");
601d14a7ff1SAndrey V. Elsukov req->arg = NULL;
602d14a7ff1SAndrey V. Elsukov } else {
60363728c47SPoul-Henning Kamp /* Get things on board */
60483d771deSPoul-Henning Kamp gctl_copyin(req);
60563728c47SPoul-Henning Kamp
606afa2a5aaSPoul-Henning Kamp if (g_debugflags & G_F_CTLDUMP)
607dd7a5bc1SAlexander Motin gctl_dump(req, "request");
60883d771deSPoul-Henning Kamp
60983d771deSPoul-Henning Kamp if (!req->nerror) {
61083d771deSPoul-Henning Kamp g_waitfor_event(g_ctl_req, req, M_WAITOK, NULL);
611dd7a5bc1SAlexander Motin
612dd7a5bc1SAlexander Motin if (g_debugflags & G_F_CTLDUMP)
613dd7a5bc1SAlexander Motin gctl_dump(req, "result");
614dd7a5bc1SAlexander Motin
61583d771deSPoul-Henning Kamp gctl_copyout(req);
61663728c47SPoul-Henning Kamp }
617d14a7ff1SAndrey V. Elsukov }
618cdae8431SPawel Jakub Dawidek if (sbuf_done(req->serror)) {
619*bbf221e3SMark Johnston nerror = copyout(sbuf_data(req->serror), req->error,
620cdae8431SPawel Jakub Dawidek imin(req->lerror, sbuf_len(req->serror) + 1));
621*bbf221e3SMark Johnston if (nerror != 0 && req->nerror == 0)
622*bbf221e3SMark Johnston req->nerror = nerror;
623cdae8431SPawel Jakub Dawidek }
62483d771deSPoul-Henning Kamp
62585986ce0SPoul-Henning Kamp nerror = req->nerror;
6263e7b7bb1SPoul-Henning Kamp gctl_free(req);
62785986ce0SPoul-Henning Kamp return (nerror);
6283117e544SPoul-Henning Kamp }
6293117e544SPoul-Henning Kamp
6306b4abfd6SPoul-Henning Kamp static int
g_ctl_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)63189c9c53dSPoul-Henning Kamp g_ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
6323101ed1bSPoul-Henning Kamp {
6333101ed1bSPoul-Henning Kamp int error;
6343101ed1bSPoul-Henning Kamp
6353101ed1bSPoul-Henning Kamp switch(cmd) {
6363117e544SPoul-Henning Kamp case GEOM_CTL:
6373117e544SPoul-Henning Kamp error = g_ctl_ioctl_ctl(dev, cmd, data, fflag, td);
6383117e544SPoul-Henning Kamp break;
6393101ed1bSPoul-Henning Kamp default:
64083d771deSPoul-Henning Kamp error = ENOIOCTL;
6413101ed1bSPoul-Henning Kamp break;
6423101ed1bSPoul-Henning Kamp }
6433101ed1bSPoul-Henning Kamp return (error);
6443101ed1bSPoul-Henning Kamp
6453101ed1bSPoul-Henning Kamp }
646