ippctl.c revision 7656:2621e50fdf4a
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27/*
28 * IP Policy Framework config driver
29 */
30
31#include <sys/types.h>
32#include <sys/cmn_err.h>
33#include <sys/kmem.h>
34#include <sys/errno.h>
35#include <sys/cpuvar.h>
36#include <sys/open.h>
37#include <sys/stat.h>
38#include <sys/conf.h>
39#include <sys/ddi.h>
40#include <sys/sunddi.h>
41#include <sys/modctl.h>
42#include <sys/stream.h>
43#include <ipp/ipp.h>
44#include <ipp/ippctl.h>
45#include <sys/nvpair.h>
46#include <sys/policy.h>
47
48/*
49 * Debug switch.
50 */
51
52#if	defined(DEBUG)
53#define	IPPCTL_DEBUG
54#endif
55
56/*
57 * Debug macros.
58 */
59
60#ifdef	IPPCTL_DEBUG
61
62#define	DBG_MODLINK	0x00000001ull
63#define	DBG_DEVOPS	0x00000002ull
64#define	DBG_CBOPS	0x00000004ull
65
66static	uint64_t	ippctl_debug_flags =
67/*
68 * DBG_MODLINK |
69 * DBG_DEVOPS |
70 * DBG_CBOPS |
71 */
720;
73
74static kmutex_t	debug_mutex[1];
75
76/*PRINTFLIKE3*/
77static void	ippctl_debug(uint64_t, char *, char *, ...)
78	__PRINTFLIKE(3);
79
80#define	DBG0(_type, _fmt)		    			\
81	ippctl_debug((_type), __FN__, (_fmt));
82
83#define	DBG1(_type, _fmt, _a1) 					\
84	ippctl_debug((_type), __FN__, (_fmt), (_a1));
85
86#define	DBG2(_type, _fmt, _a1, _a2)				\
87	ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2));
88
89#define	DBG3(_type, _fmt, _a1, _a2, _a3)			\
90	ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2),	\
91	    (_a3));
92
93#define	DBG4(_type, _fmt, _a1, _a2, _a3, _a4)			\
94	ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2),	\
95	    (_a3), (_a4));
96
97#define	DBG5(_type, _fmt, _a1, _a2, _a3, _a4, _a5)		\
98	ippctl_debug((_type), __FN__, (_fmt), (_a1), (_a2),	\
99	    (_a3), (_a4), (_a5));
100
101#else	/* IPPCTL_DBG */
102
103#define	DBG0(_type, _fmt)
104#define	DBG1(_type, _fmt, _a1)
105#define	DBG2(_type, _fmt, _a1, _a2)
106#define	DBG3(_type, _fmt, _a1, _a2, _a3)
107#define	DBG4(_type, _fmt, _a1, _a2, _a3, _a4)
108#define	DBG5(_type, _fmt, _a1, _a2, _a3, _a4, _a5)
109
110#endif	/* IPPCTL_DBG */
111
112/*
113 * cb_ops
114 */
115
116static int	ippctl_open(dev_t *, int, int, cred_t *);
117static int	ippctl_close(dev_t, int, int, cred_t *);
118static int	ippctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
119
120static	struct cb_ops	ippctl_cb_ops = {
121	ippctl_open,	/* cb_open */
122	ippctl_close,	/* cb_close */
123	nodev,		/* cb_strategy */
124	nodev,		/* cb_print */
125	nodev,		/* cb_dump */
126	nodev,		/* cb_read */
127	nodev,		/* cb_write */
128	ippctl_ioctl,	/* cb_ioctl */
129	nodev,		/* cb_devmap */
130	nodev,		/* cb_mmap */
131	nodev,		/* cb_segmap */
132	nochpoll,	/* cb_chpoll */
133	ddi_prop_op,	/* cb_prop_op */
134	0,		/* cb_str */
135	D_NEW | D_MP,	/* cb_flag */
136	CB_REV,		/* cb_rev */
137	nodev,		/* cb_aread */
138	nodev		/* cb_awrite */
139};
140
141/*
142 * dev_ops
143 */
144
145static	int	ippctl_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
146static	int	ippctl_attach(dev_info_t *, ddi_attach_cmd_t);
147static	int	ippctl_detach(dev_info_t *, ddi_detach_cmd_t);
148
149static	struct dev_ops	ippctl_dev_ops = {
150	DEVO_REV,		/* devo_rev, */
151	0,			/* devo_refcnt  */
152	ippctl_info,		/* devo_getinfo */
153	nulldev,		/* devo_identify */
154	nulldev,		/* devo_probe */
155	ippctl_attach,		/* devo_attach */
156	ippctl_detach,		/* devo_detach */
157	nodev,			/* devo_reset */
158	&ippctl_cb_ops,		/* devo_cb_ops */
159	(struct bus_ops *)0,	/* devo_bus_ops */
160	NULL,			/* devo_power */
161	ddi_quiesce_not_needed,		/* devo_quiesce */
162};
163
164static	struct modldrv modldrv = {
165	&mod_driverops,
166	"IP Policy Configuration Driver",
167	&ippctl_dev_ops,
168};
169
170static	struct modlinkage modlinkage = {
171	MODREV_1,
172	&modldrv,
173	NULL
174};
175
176/*
177 * Local definitions, types and prototypes.
178 */
179
180#define	MAXUBUFLEN	(1 << 16)
181
182#define	FREE_TEXT(_string)					\
183	kmem_free((_string), strlen(_string) + 1)
184
185#define	FREE_TEXT_ARRAY(_array, _nelt)				\
186	{							\
187		int	j;					\
188								\
189		for (j = 0; j < (_nelt); j++)			\
190			if ((_array)[j] != NULL)		\
191				FREE_TEXT((_array)[j]);		\
192		kmem_free((_array), (_nelt) * sizeof (char *));	\
193	}
194
195typedef	struct ippctl_buf	ippctl_buf_t;
196
197struct ippctl_buf {
198	char	*buf;
199	size_t	buflen;
200};
201
202static int	ippctl_copyin(caddr_t, int, char **, size_t *);
203static int	ippctl_copyout(caddr_t, int, char *, size_t);
204static int	ippctl_extract_op(nvlist_t *, uint8_t *);
205static int	ippctl_extract_aname(nvlist_t *, char **);
206static int	ippctl_extract_modname(nvlist_t *, char **);
207static int	ippctl_attach_modname(nvlist_t *nvlp, char *val);
208static int	ippctl_attach_modname_array(nvlist_t *nvlp, char **val, int);
209static int	ippctl_attach_aname_array(nvlist_t *nvlp, char **val, int);
210static int	ippctl_extract_flags(nvlist_t *, ipp_flags_t *);
211static int	ippctl_cmd(char *, size_t, size_t *);
212static int	ippctl_action_create(char *, char *, nvlist_t *, ipp_flags_t);
213static int	ippctl_action_destroy(char *, ipp_flags_t);
214static int	ippctl_action_modify(char *, nvlist_t *, ipp_flags_t);
215static int	ippctl_action_info(char *, ipp_flags_t);
216static int	ippctl_action_mod(char *);
217static int	ippctl_list_mods(void);
218static int	ippctl_mod_list_actions(char *);
219static int	ippctl_data(char **, size_t *, size_t *);
220static void	ippctl_flush(void);
221static int	ippctl_add_nvlist(nvlist_t *, int);
222static int	ippctl_callback(nvlist_t *, void *);
223static int	ippctl_set_rc(int);
224static void	ippctl_alloc(int);
225static void	ippctl_realloc(void);
226static void	ippctl_free(void);
227
228/*
229 * Global data
230 */
231
232static dev_info_t	*ippctl_dip = NULL;
233static kmutex_t		ippctl_lock;
234static boolean_t	ippctl_busy;
235static ippctl_buf_t	*ippctl_array = NULL;
236static int		ippctl_limit = -1;
237static int		ippctl_rindex = -1;
238static int		ippctl_windex = -1;
239
240/*
241 * Module linkage functions
242 */
243
244#define	__FN__	"_init"
245int
246_init(
247	void)
248{
249	int	rc;
250
251	if ((rc = mod_install(&modlinkage)) != 0) {
252		DBG0(DBG_MODLINK, "mod_install failed\n");
253		return (rc);
254	}
255
256	return (rc);
257}
258#undef	__FN__
259
260#define	__FN__	"_fini"
261int
262_fini(
263	void)
264{
265	int	rc;
266
267	if ((rc = mod_remove(&modlinkage)) == 0) {
268		return (rc);
269	}
270
271	DBG0(DBG_MODLINK, "mod_remove failed\n");
272	return (rc);
273}
274#undef	__FN__
275
276#define	__FN__	"_info"
277int
278_info(
279	struct modinfo	*modinfop)
280{
281	DBG0(DBG_MODLINK, "calling mod_info\n");
282	return (mod_info(&modlinkage, modinfop));
283}
284#undef	__FN__
285
286/*
287 * Driver interface functions (dev_ops and cb_ops)
288 */
289
290#define	__FN__	"ippctl_info"
291/*ARGSUSED*/
292static	int
293ippctl_info(
294	dev_info_t	*dip,
295	ddi_info_cmd_t	cmd,
296	void		*arg,
297	void 		**result)
298{
299	int		rc = DDI_FAILURE;
300
301	switch (cmd) {
302	case DDI_INFO_DEVT2INSTANCE:
303		*result = (void *)0;	/* Single instance driver */
304		rc = DDI_SUCCESS;
305		break;
306	case DDI_INFO_DEVT2DEVINFO:
307		*result = (void *)ippctl_dip;
308		rc = DDI_SUCCESS;
309		break;
310	default:
311		break;
312	}
313
314	return (rc);
315}
316#undef	__FN__
317
318#define	__FN__	"ippctl_attach"
319static	int
320ippctl_attach(
321	dev_info_t		*dip,
322	ddi_attach_cmd_t	cmd)
323{
324	switch (cmd) {
325	case DDI_ATTACH:
326		break;
327	case DDI_PM_RESUME:
328		/*FALLTHRU*/
329	case DDI_RESUME:
330		/*FALLTHRU*/
331	default:
332		return (DDI_FAILURE);
333	}
334
335	DBG0(DBG_DEVOPS, "DDI_ATTACH\n");
336
337	/*
338	 * This is strictly a single instance driver.
339	 */
340
341	if (ippctl_dip != NULL)
342		return (DDI_FAILURE);
343
344	/*
345	 * Create minor node.
346	 */
347
348	if (ddi_create_minor_node(dip, "ctl", S_IFCHR, 0,
349	    DDI_PSEUDO, 0) != DDI_SUCCESS)
350		return (DDI_FAILURE);
351
352	/*
353	 * No need for per-instance structure, just store vital data in
354	 * globals.
355	 */
356
357	ippctl_dip = dip;
358	mutex_init(&ippctl_lock, NULL, MUTEX_DRIVER, NULL);
359	ippctl_busy = B_FALSE;
360
361	return (DDI_SUCCESS);
362}
363#undef	__FN__
364
365#define	__FN__	"ippctl_detach"
366/*ARGSUSED*/
367static	int
368ippctl_detach(
369	dev_info_t		*dip,
370	ddi_detach_cmd_t	cmd)
371{
372	switch (cmd) {
373	case DDI_DETACH:
374		break;
375	case DDI_PM_SUSPEND:
376		/*FALLTHRU*/
377	case DDI_SUSPEND:
378		/*FALLTHRU*/
379	default:
380		return (DDI_FAILURE);
381	}
382
383	DBG0(DBG_DEVOPS, "DDI_DETACH\n");
384
385	ASSERT(dip == ippctl_dip);
386
387	ddi_remove_minor_node(dip, NULL);
388	mutex_destroy(&ippctl_lock);
389	ippctl_dip = NULL;
390
391	return (DDI_SUCCESS);
392}
393#undef	__FN__
394
395#define	__FN__	"ippctl_open"
396/*ARGSUSED*/
397static	int
398ippctl_open(
399	dev_t	*devp,
400	int	flag,
401	int	otyp,
402	cred_t	*credp)
403{
404	minor_t	minor = getminor(*devp);
405#define	LIMIT	4
406
407	DBG0(DBG_CBOPS, "open\n");
408
409	/*
410	 * Only allow privileged users to open our device.
411	 */
412
413	if (secpolicy_net_config(credp, B_FALSE) != 0) {
414		DBG0(DBG_CBOPS, "not privileged user\n");
415		return (EPERM);
416	}
417
418	/*
419	 * Sanity check other arguments.
420	 */
421
422	if (minor != 0) {
423		DBG0(DBG_CBOPS, "bad minor\n");
424		return (ENXIO);
425	}
426
427	if (otyp != OTYP_CHR) {
428		DBG0(DBG_CBOPS, "bad device type\n");
429		return (EINVAL);
430	}
431
432	/*
433	 * This is also a single dev_t driver.
434	 */
435
436	mutex_enter(&ippctl_lock);
437	if (ippctl_busy) {
438		mutex_exit(&ippctl_lock);
439		return (EBUSY);
440	}
441	ippctl_busy = B_TRUE;
442	mutex_exit(&ippctl_lock);
443
444	/*
445	 * Allocate data buffer array (starting with length LIMIT, defined
446	 * at the start of this function).
447	 */
448
449	ippctl_alloc(LIMIT);
450
451	DBG0(DBG_CBOPS, "success\n");
452
453	return (0);
454
455#undef	LIMIT
456}
457#undef	__FN__
458
459#define	__FN__	"ippctl_close"
460/*ARGSUSED*/
461static	int
462ippctl_close(
463	dev_t	dev,
464	int	flag,
465	int	otyp,
466	cred_t	*credp)
467{
468	minor_t	minor = getminor(dev);
469
470	DBG0(DBG_CBOPS, "close\n");
471
472	ASSERT(minor == 0);
473
474	/*
475	 * Free the data buffer array.
476	 */
477
478	ippctl_free();
479
480	mutex_enter(&ippctl_lock);
481	ippctl_busy = B_FALSE;
482	mutex_exit(&ippctl_lock);
483
484	DBG0(DBG_CBOPS, "success\n");
485
486	return (0);
487}
488#undef	__FN__
489
490#define	__FN__	"ippctl_ioctl"
491static int
492ippctl_ioctl(
493	dev_t			dev,
494	int			cmd,
495	intptr_t		arg,
496	int			mode,
497	cred_t			*credp,
498	int			*rvalp)
499{
500	minor_t			minor = getminor(dev);
501	char			*cbuf;
502	char			*dbuf;
503	size_t			cbuflen;
504	size_t			dbuflen;
505	size_t			nextbuflen;
506	int			rc;
507
508	/*
509	 * Paranoia check.
510	 */
511
512	if (secpolicy_net_config(credp, B_FALSE) != 0) {
513		DBG0(DBG_CBOPS, "not privileged user\n");
514		return (EPERM);
515	}
516
517	if (minor != 0) {
518		DBG0(DBG_CBOPS, "bad minor\n");
519		return (ENXIO);
520	}
521
522	switch (cmd) {
523	case IPPCTL_CMD:
524		DBG0(DBG_CBOPS, "command\n");
525
526		/*
527		 * Copy in the command buffer from user space.
528		 */
529
530		if ((rc = ippctl_copyin((caddr_t)arg, mode, &cbuf,
531		    &cbuflen)) != 0)
532			break;
533
534		/*
535		 * Execute the command.
536		 */
537
538		rc = ippctl_cmd(cbuf, cbuflen, &nextbuflen);
539
540		/*
541		 * Pass back the length of the first data buffer.
542		 */
543
544		DBG1(DBG_CBOPS, "nextbuflen = %lu\n", nextbuflen);
545		*rvalp = nextbuflen;
546
547		/*
548		 * Free the kernel copy of the command buffer.
549		 */
550
551		kmem_free(cbuf, cbuflen);
552		break;
553
554	case IPPCTL_DATA:
555		DBG0(DBG_CBOPS, "data\n");
556
557		/*
558		 * Grab the next data buffer from the array of pending
559		 * buffers.
560		 */
561
562		if ((rc = ippctl_data(&dbuf, &dbuflen, &nextbuflen)) != 0)
563			break;
564
565		/*
566		 * Copy it out to user space.
567		 */
568
569		rc = ippctl_copyout((caddr_t)arg, mode, dbuf, dbuflen);
570
571		/*
572		 * Pass back the length of the next data buffer.
573		 */
574
575		DBG1(DBG_CBOPS, "nextbuflen = %lu\n", nextbuflen);
576		*rvalp = nextbuflen;
577		break;
578
579	default:
580		DBG0(DBG_CBOPS, "unrecognized ioctl\n");
581		rc = EINVAL;
582		break;
583	}
584
585	DBG1(DBG_CBOPS, "rc = %d\n", rc);
586	return (rc);
587}
588#undef	__FN__
589
590/*
591 * Local functions
592 */
593
594#define	__FN__	"ippctl_copyin"
595static int
596ippctl_copyin(
597	caddr_t		arg,
598	int		mode,
599	char		**kbufp,
600	size_t		*kbuflenp)
601{
602	ippctl_ioctl_t	iioc;
603	caddr_t		ubuf;
604	char		*kbuf;
605	size_t		ubuflen;
606
607	DBG0(DBG_CBOPS, "copying in ioctl structure\n");
608
609	/*
610	 * Copy in the ioctl structure from user-space, converting from 32-bit
611	 * as necessary.
612	 */
613
614#ifdef	_MULTI_DATAMODEL
615	switch (ddi_model_convert_from(mode & FMODELS)) {
616	case DDI_MODEL_ILP32:
617		{
618			ippctl_ioctl32_t	iioc32;
619
620			DBG0(DBG_CBOPS, "converting from 32-bit\n");
621
622			if (ddi_copyin(arg, (caddr_t)&iioc32,
623			    sizeof (ippctl_ioctl32_t), mode) != 0)
624				return (EFAULT);
625
626			ubuf = (caddr_t)(uintptr_t)iioc32.ii32_buf;
627			ubuflen = (size_t)iioc32.ii32_buflen;
628		}
629		break;
630	case DDI_MODEL_NONE:
631		if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
632		    mode) != 0)
633			return (EFAULT);
634
635		ubuf = iioc.ii_buf;
636		ubuflen = iioc.ii_buflen;
637		break;
638	default:
639		return (EFAULT);
640	}
641#else	/* _MULTI_DATAMODEL */
642	if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
643	    mode) != 0)
644		return (EFAULT);
645
646	ubuf = iioc.ii_buf;
647	ubuflen = iioc.ii_buflen;
648#endif	/* _MULTI_DATAMODEL */
649
650	DBG1(DBG_CBOPS, "ubuf = 0x%p\n", (void *)ubuf);
651	DBG1(DBG_CBOPS, "ubuflen = %lu\n", ubuflen);
652
653	/*
654	 * Sanity check the command buffer information.
655	 */
656
657	if (ubuflen == 0 || ubuf == NULL)
658		return (EINVAL);
659	if (ubuflen > MAXUBUFLEN)
660		return (E2BIG);
661
662	/*
663	 * Allocate some memory for the command buffer and copy it in.
664	 */
665
666	kbuf = kmem_zalloc(ubuflen, KM_SLEEP);
667	DBG0(DBG_CBOPS, "copying in nvlist\n");
668	if (ddi_copyin(ubuf, (caddr_t)kbuf, ubuflen, mode) != 0) {
669		kmem_free(kbuf, ubuflen);
670		return (EFAULT);
671	}
672
673	*kbufp = kbuf;
674	*kbuflenp = ubuflen;
675	return (0);
676}
677#undef	__FN__
678
679#define	__FN__	"ippctl_copyout"
680static int
681ippctl_copyout(
682	caddr_t		arg,
683	int		mode,
684	char		*kbuf,
685	size_t		kbuflen)
686{
687	ippctl_ioctl_t	iioc;
688	caddr_t		ubuf;
689	int		ubuflen;
690
691	DBG0(DBG_CBOPS, "copying out ioctl structure\n");
692
693	/*
694	 * Copy in the ioctl structure from user-space, converting from 32-bit
695	 * as necessary.
696	 */
697
698#ifdef	_MULTI_DATAMODEL
699	switch (ddi_model_convert_from(mode & FMODELS)) {
700	case DDI_MODEL_ILP32:
701		{
702			ippctl_ioctl32_t	iioc32;
703
704			if (ddi_copyin(arg, (caddr_t)&iioc32,
705			    sizeof (ippctl_ioctl32_t), mode) != 0)
706				return (EFAULT);
707
708			ubuf = (caddr_t)(uintptr_t)iioc32.ii32_buf;
709			ubuflen = iioc32.ii32_buflen;
710		}
711		break;
712	case DDI_MODEL_NONE:
713		if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
714		    mode) != 0)
715			return (EFAULT);
716
717		ubuf = iioc.ii_buf;
718		ubuflen = iioc.ii_buflen;
719		break;
720	default:
721		return (EFAULT);
722	}
723#else	/* _MULTI_DATAMODEL */
724	if (ddi_copyin(arg, (caddr_t)&iioc, sizeof (ippctl_ioctl_t),
725	    mode) != 0)
726		return (EFAULT);
727
728	ubuf = iioc.ii_buf;
729	ubuflen = iioc.ii_buflen;
730#endif	/* _MULTI_DATAMODEL */
731
732	DBG1(DBG_CBOPS, "ubuf = 0x%p\n", (void *)ubuf);
733	DBG1(DBG_CBOPS, "ubuflen = %d\n", ubuflen);
734
735	/*
736	 * Sanity check the data buffer details.
737	 */
738
739	if (ubuflen == 0 || ubuf == NULL)
740		return (EINVAL);
741
742	if (ubuflen < kbuflen)
743		return (ENOSPC);
744	if (ubuflen > MAXUBUFLEN)
745		return (E2BIG);
746
747	/*
748	 * Copy out the data buffer to user space.
749	 */
750
751	DBG0(DBG_CBOPS, "copying out nvlist\n");
752	if (ddi_copyout((caddr_t)kbuf, ubuf, kbuflen, mode) != 0)
753		return (EFAULT);
754
755	return (0);
756}
757#undef	__FN__
758
759#define	__FN__	"ippctl_extract_op"
760static int
761ippctl_extract_op(
762	nvlist_t	*nvlp,
763	uint8_t		*valp)
764{
765	int		rc;
766
767	/*
768	 * Look-up and remove the opcode passed from libipp from the
769	 * nvlist.
770	 */
771
772	if ((rc = nvlist_lookup_byte(nvlp, IPPCTL_OP, valp)) != 0)
773		return (rc);
774
775	(void) nvlist_remove_all(nvlp, IPPCTL_OP);
776	return (0);
777}
778#undef	__FN__
779
780#define	__FN__	"ippctl_extract_aname"
781static int
782ippctl_extract_aname(
783	nvlist_t	*nvlp,
784	char		**valp)
785{
786	int		rc;
787	char		*ptr;
788
789	/*
790	 * Look-up and remove the action name passed from libipp from the
791	 * nvlist.
792	 */
793
794	if ((rc = nvlist_lookup_string(nvlp, IPPCTL_ANAME, &ptr)) != 0)
795		return (rc);
796
797	*valp = kmem_alloc(strlen(ptr) + 1, KM_SLEEP);
798	(void) strcpy(*valp, ptr);
799	(void) nvlist_remove_all(nvlp, IPPCTL_ANAME);
800	return (0);
801}
802#undef	__FN__
803
804#define	__FN__	"ippctl_extract_modname"
805static int
806ippctl_extract_modname(
807	nvlist_t	*nvlp,
808	char		**valp)
809{
810	int		rc;
811	char		*ptr;
812
813	/*
814	 * Look-up and remove the module name passed from libipp from the
815	 * nvlist.
816	 */
817
818	if ((rc = nvlist_lookup_string(nvlp, IPPCTL_MODNAME, &ptr)) != 0)
819		return (rc);
820
821	*valp = kmem_alloc(strlen(ptr) + 1, KM_SLEEP);
822	(void) strcpy(*valp, ptr);
823	(void) nvlist_remove_all(nvlp, IPPCTL_MODNAME);
824	return (0);
825}
826#undef	__FN__
827
828#define	__FN__	"ippctl_attach_modname"
829static int
830ippctl_attach_modname(
831	nvlist_t	*nvlp,
832	char		*modname)
833{
834	/*
835	 * Add a module name to an nvlist for passing back to user
836	 * space.
837	 */
838
839	return (nvlist_add_string(nvlp, IPPCTL_MODNAME, modname));
840}
841#undef	__FN__
842
843#define	__FN__	"ippctl_attach_modname_array"
844static int
845ippctl_attach_modname_array(
846	nvlist_t	*nvlp,
847	char		**modname_array,
848	int		nelt)
849{
850	/*
851	 * Add a module name array to an nvlist for passing back to user
852	 * space.
853	 */
854
855	return (nvlist_add_string_array(nvlp, IPPCTL_MODNAME_ARRAY,
856	    modname_array, nelt));
857}
858#undef	__FN__
859
860#define	__FN__	"ippctl_attach_aname_array"
861static int
862ippctl_attach_aname_array(
863	nvlist_t	*nvlp,
864	char		**aname_array,
865	int		nelt)
866{
867	/*
868	 * Add an action name array to an nvlist for passing back to user
869	 * space.
870	 */
871
872	return (nvlist_add_string_array(nvlp, IPPCTL_ANAME_ARRAY,
873	    aname_array, nelt));
874}
875#undef	__FN__
876
877#define	__FN__	"ippctl_extract_flags"
878static int
879ippctl_extract_flags(
880	nvlist_t	*nvlp,
881	ipp_flags_t	*valp)
882{
883	int		rc;
884
885	/*
886	 * Look-up and remove the flags passed from libipp from the
887	 * nvlist.
888	 */
889
890	if ((rc = nvlist_lookup_uint32(nvlp, IPPCTL_FLAGS,
891	    (uint32_t *)valp)) != 0)
892		return (rc);
893
894	(void) nvlist_remove_all(nvlp, IPPCTL_FLAGS);
895	return (0);
896}
897#undef	__FN__
898
899#define	__FN__	"ippctl_cmd"
900static int
901ippctl_cmd(
902	char		*cbuf,
903	size_t		cbuflen,
904	size_t		*nextbuflenp)
905{
906	nvlist_t	*nvlp = NULL;
907	int		rc;
908	char		*aname = NULL;
909	char		*modname = NULL;
910	ipp_flags_t	flags;
911	uint8_t		op;
912
913	/*
914	 * Start a new command cycle by flushing any previous data buffers.
915	 */
916
917	ippctl_flush();
918	*nextbuflenp = 0;
919
920	/*
921	 * Unpack the nvlist from the command buffer.
922	 */
923
924	if ((rc = nvlist_unpack(cbuf, cbuflen, &nvlp, KM_SLEEP)) != 0)
925		return (rc);
926
927	/*
928	 * Extract the opcode to find out what we should do.
929	 */
930
931	if ((rc = ippctl_extract_op(nvlp, &op)) != 0) {
932		nvlist_free(nvlp);
933		return (rc);
934	}
935
936	switch (op) {
937	case IPPCTL_OP_ACTION_CREATE:
938		/*
939		 * Create a new action.
940		 */
941
942		DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_CREATE\n");
943
944		/*
945		 * Extract the module name, action name and flags from the
946		 * nvlist.
947		 */
948
949		if ((rc = ippctl_extract_modname(nvlp, &modname)) != 0) {
950			nvlist_free(nvlp);
951			return (rc);
952		}
953
954		if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
955			FREE_TEXT(modname);
956			nvlist_free(nvlp);
957			return (rc);
958		}
959
960		if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
961			FREE_TEXT(aname);
962			FREE_TEXT(modname);
963			nvlist_free(nvlp);
964			return (rc);
965		}
966
967
968		rc = ippctl_action_create(modname, aname, nvlp, flags);
969		break;
970
971	case IPPCTL_OP_ACTION_MODIFY:
972
973		/*
974		 * Modify an existing action.
975		 */
976
977		DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_MODIFY\n");
978
979		/*
980		 * Extract the action name and flags from the nvlist.
981		 */
982
983		if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
984			nvlist_free(nvlp);
985			return (rc);
986		}
987
988		if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
989			FREE_TEXT(aname);
990			nvlist_free(nvlp);
991			return (rc);
992		}
993
994		rc = ippctl_action_modify(aname, nvlp, flags);
995		break;
996
997	case IPPCTL_OP_ACTION_DESTROY:
998
999		/*
1000		 * Destroy an action.
1001		 */
1002
1003		DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_DESTROY\n");
1004
1005		/*
1006		 * Extract the action name and flags from the nvlist.
1007		 */
1008
1009		if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
1010			nvlist_free(nvlp);
1011			return (rc);
1012		}
1013
1014		if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
1015			FREE_TEXT(aname);
1016			nvlist_free(nvlp);
1017			return (rc);
1018		}
1019
1020		nvlist_free(nvlp);
1021		rc = ippctl_action_destroy(aname, flags);
1022		break;
1023
1024	case IPPCTL_OP_ACTION_INFO:
1025
1026		/*
1027		 * Retrive the configuration of an action.
1028		 */
1029
1030		DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_INFO\n");
1031
1032		/*
1033		 * Extract the action name and flags from the nvlist.
1034		 */
1035
1036		if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
1037			nvlist_free(nvlp);
1038			return (rc);
1039		}
1040
1041		if ((rc = ippctl_extract_flags(nvlp, &flags)) != 0) {
1042			nvlist_free(nvlp);
1043			FREE_TEXT(aname);
1044			return (rc);
1045		}
1046
1047		nvlist_free(nvlp);
1048		rc = ippctl_action_info(aname, flags);
1049		break;
1050
1051	case IPPCTL_OP_ACTION_MOD:
1052
1053		/*
1054		 * Find the module that implements a given action.
1055		 */
1056
1057		DBG0(DBG_CBOPS, "op = IPPCTL_OP_ACTION_MOD\n");
1058
1059		/*
1060		 * Extract the action name from the nvlist.
1061		 */
1062
1063		if ((rc = ippctl_extract_aname(nvlp, &aname)) != 0) {
1064			nvlist_free(nvlp);
1065			return (rc);
1066		}
1067
1068		nvlist_free(nvlp);
1069		rc = ippctl_action_mod(aname);
1070		break;
1071
1072	case IPPCTL_OP_LIST_MODS:
1073
1074		/*
1075		 * List all the modules.
1076		 */
1077
1078		DBG0(DBG_CBOPS, "op = IPPCTL_OP_LIST_MODS\n");
1079
1080		nvlist_free(nvlp);
1081		rc = ippctl_list_mods();
1082		break;
1083
1084	case IPPCTL_OP_MOD_LIST_ACTIONS:
1085
1086		/*
1087		 * List all the actions for a given module.
1088		 */
1089
1090		DBG0(DBG_CBOPS, "op = IPPCTL_OP_LIST_MODS\n");
1091
1092		if ((rc = ippctl_extract_modname(nvlp, &modname)) != 0) {
1093			nvlist_free(nvlp);
1094			return (rc);
1095		}
1096
1097		nvlist_free(nvlp);
1098		rc = ippctl_mod_list_actions(modname);
1099		break;
1100
1101	default:
1102
1103		/*
1104		 * Unrecognized opcode.
1105		 */
1106
1107		nvlist_free(nvlp);
1108		rc = EINVAL;
1109		break;
1110	}
1111
1112	/*
1113	 * The length of buffer that we need to notify back to libipp with
1114	 * the command ioctl's return is the length of the first data buffer
1115	 * in the array. We only expact to pass back data buffers if the
1116	 * operation succeeds (NOTE: this does not mean the kernel call has
1117	 * to succeed, merely that we successfully issued it and processed
1118	 * the results).
1119	 */
1120
1121	if (rc == 0)
1122		*nextbuflenp = ippctl_array[0].buflen;
1123
1124	return (rc);
1125}
1126#undef	__FN__
1127
1128#define	__FN__	"ippctl_action_create"
1129static int
1130ippctl_action_create(
1131	char		*modname,
1132	char		*aname,
1133	nvlist_t	*nvlp,
1134	ipp_flags_t	flags)
1135{
1136	int		ipp_rc;
1137	int		rc;
1138	ipp_mod_id_t	mid;
1139	ipp_action_id_t	aid;
1140
1141	/*
1142	 * Look up the module id from the name and create the new
1143	 * action.
1144	 */
1145
1146	mid = ipp_mod_lookup(modname);
1147	FREE_TEXT(modname);
1148
1149	ipp_rc = ipp_action_create(mid, aname, &nvlp, flags, &aid);
1150	FREE_TEXT(aname);
1151
1152	/*
1153	 * Add an nvlist containing the kernel return code to the
1154	 * set of nvlists to pass back to libipp.
1155	 */
1156
1157	if ((rc = ippctl_set_rc(ipp_rc)) != 0) {
1158		if (nvlp != NULL) {
1159			nvlist_free(nvlp);
1160			if (ipp_action_destroy(aid, 0) != 0) {
1161				cmn_err(CE_PANIC,
1162				    "ippctl: unrecoverable error (aid = %d)",
1163				    aid);
1164				/*NOTREACHED*/
1165			}
1166		}
1167		return (rc);
1168	}
1169
1170	/*
1171	 * If the module passed back an nvlist, add this as
1172	 * well.
1173	 */
1174
1175	if (nvlp != NULL) {
1176		rc = ippctl_callback(nvlp, NULL);
1177		nvlist_free(nvlp);
1178	} else
1179		rc = 0;
1180
1181	return (rc);
1182}
1183#undef	__FN__
1184
1185#define	__FN__	"ippctl_action_destroy"
1186static int
1187ippctl_action_destroy(
1188	char		*aname,
1189	ipp_flags_t	flags)
1190{
1191	ipp_action_id_t	aid;
1192	int		ipp_rc;
1193	int		rc;
1194
1195	/*
1196	 * Look up the action id and destroy the action.
1197	 */
1198
1199	aid = ipp_action_lookup(aname);
1200	FREE_TEXT(aname);
1201
1202	ipp_rc = ipp_action_destroy(aid, flags);
1203
1204	/*
1205	 * Add an nvlist containing the kernel return code to the
1206	 * set of nvlists to pass back to libipp.
1207	 */
1208
1209	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1210		return (rc);
1211
1212	/*
1213	 * There's no more information to pass back.
1214	 */
1215
1216	return (0);
1217}
1218#undef	__FN__
1219
1220#define	__FN__	"ippctl_action_modify"
1221static int
1222ippctl_action_modify(
1223	char		*aname,
1224	nvlist_t	*nvlp,
1225	ipp_flags_t	flags)
1226{
1227	ipp_action_id_t	aid;
1228	int		ipp_rc;
1229	int		rc;
1230
1231	/*
1232	 * Look up the action id and modify the action.
1233	 */
1234
1235	aid = ipp_action_lookup(aname);
1236	FREE_TEXT(aname);
1237
1238	ipp_rc = ipp_action_modify(aid, &nvlp, flags);
1239
1240	/*
1241	 * Add an nvlist containing the kernel return code to the
1242	 * set of nvlists to pass back to libipp.
1243	 */
1244
1245	if ((rc = ippctl_set_rc(ipp_rc)) != 0) {
1246		if (nvlp != NULL)
1247			nvlist_free(nvlp);
1248		return (rc);
1249	}
1250
1251	/*
1252	 * If the module passed back an nvlist, add this as
1253	 * well.
1254	 */
1255
1256	if (nvlp != NULL) {
1257		rc = ippctl_callback(nvlp, NULL);
1258		nvlist_free(nvlp);
1259	} else
1260		rc = 0;
1261
1262	return (rc);
1263}
1264#undef	__FN__
1265
1266#define	__FN__	"ippctl_action_info"
1267static int
1268ippctl_action_info(
1269	char		*aname,
1270	ipp_flags_t	flags)
1271{
1272	ipp_action_id_t	aid;
1273	int		ipp_rc;
1274	int		rc;
1275
1276	/*
1277	 * Look up the action and call the information retrieval
1278	 * entry point.
1279	 *
1280	 * NOTE: The callback function that is passed in packs and
1281	 * stores each of the nvlists it is called with in the array
1282	 * that will be passed back to libipp.
1283	 */
1284
1285	aid = ipp_action_lookup(aname);
1286	FREE_TEXT(aname);
1287
1288	ipp_rc = ipp_action_info(aid, ippctl_callback, NULL, flags);
1289
1290	/*
1291	 * Add an nvlist containing the kernel return code to the
1292	 * set of nvlists to pass back to libipp.
1293	 */
1294
1295	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1296		return (rc);
1297
1298	/*
1299	 * There's no more information to pass back.
1300	 */
1301
1302	return (0);
1303}
1304#undef	__FN__
1305
1306#define	__FN__	"ippctl_action_mod"
1307static int
1308ippctl_action_mod(
1309	char		*aname)
1310{
1311	ipp_mod_id_t	mid;
1312	ipp_action_id_t	aid;
1313	char		*modname;
1314	nvlist_t	*nvlp;
1315	int		ipp_rc;
1316	int		rc;
1317
1318	/*
1319	 * Look up the action id and get the id of the module that
1320	 * implements the action. If that succeeds then look up the
1321	 * name of the module.
1322	 */
1323
1324	aid = ipp_action_lookup(aname);
1325	FREE_TEXT(aname);
1326
1327	if ((ipp_rc = ipp_action_mod(aid, &mid)) == 0)
1328		ipp_rc = ipp_mod_name(mid, &modname);
1329
1330	/*
1331	 * Add an nvlist containing the kernel return code to the
1332	 * set of nvlists to pass back to libipp.
1333	 */
1334
1335	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1336		return (rc);
1337
1338	/*
1339	 * If everything succeeded add an nvlist containing the
1340	 * module name to the set of nvlists to pass back to libipp.
1341	 */
1342
1343	if (ipp_rc == 0) {
1344		if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_SLEEP)) != 0)
1345			return (rc);
1346
1347		if ((rc = ippctl_attach_modname(nvlp, modname)) != 0) {
1348			nvlist_free(nvlp);
1349			return (rc);
1350		}
1351
1352		FREE_TEXT(modname);
1353
1354		rc = ippctl_callback(nvlp, NULL);
1355		nvlist_free(nvlp);
1356	} else
1357		rc = 0;
1358
1359	return (rc);
1360}
1361#undef	__FN__
1362
1363#define	__FN__	"ippctl_list_mods"
1364static int
1365ippctl_list_mods(
1366	void)
1367{
1368	nvlist_t	*nvlp;
1369	int		ipp_rc;
1370	int		rc = 0;
1371	ipp_mod_id_t	*mid_array;
1372	char		**modname_array = NULL;
1373	int		nelt;
1374	int		length;
1375	int		i;
1376
1377	/*
1378	 * Get a list of all the module ids. If that succeeds,
1379	 * translate the ids into names.
1380	 *
1381	 * NOTE: This translation may fail if a module is
1382	 * unloaded during this operation. If this occurs, EAGAIN
1383	 * will be passed back to libipp note that a transient
1384	 * problem occured.
1385	 */
1386
1387	if ((ipp_rc = ipp_list_mods(&mid_array, &nelt)) == 0) {
1388
1389		/*
1390		 * It is possible that there are no modules
1391		 * registered.
1392		 */
1393
1394		if (nelt > 0) {
1395			length = nelt * sizeof (char *);
1396			modname_array = kmem_zalloc(length, KM_SLEEP);
1397
1398			for (i = 0; i < nelt; i++) {
1399				if (ipp_mod_name(mid_array[i],
1400				    &modname_array[i]) != 0) {
1401					kmem_free(mid_array, nelt *
1402					    sizeof (ipp_mod_id_t));
1403					FREE_TEXT_ARRAY(modname_array, nelt);
1404					ipp_rc = EAGAIN;
1405					goto done;
1406				}
1407			}
1408
1409			kmem_free(mid_array, nelt * sizeof (ipp_mod_id_t));
1410
1411			if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME,
1412			    KM_SLEEP)) != 0) {
1413				FREE_TEXT_ARRAY(modname_array, nelt);
1414				return (rc);
1415			}
1416
1417			if ((rc = ippctl_attach_modname_array(nvlp,
1418			    modname_array, nelt)) != 0) {
1419				FREE_TEXT_ARRAY(modname_array, nelt);
1420				nvlist_free(nvlp);
1421				return (rc);
1422			}
1423
1424			FREE_TEXT_ARRAY(modname_array, nelt);
1425
1426			if ((rc = ippctl_callback(nvlp, NULL)) != 0) {
1427				nvlist_free(nvlp);
1428				return (rc);
1429			}
1430
1431			nvlist_free(nvlp);
1432		}
1433	}
1434
1435done:
1436	/*
1437	 * Add an nvlist containing the kernel return code to the
1438	 * set of nvlists to pass back to libipp.
1439	 */
1440
1441	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1442		return (rc);
1443
1444	return (0);
1445}
1446#undef	__FN__
1447
1448#define	__FN__	"ippctl_mod_list_actions"
1449static int
1450ippctl_mod_list_actions(
1451	char		*modname)
1452{
1453	ipp_mod_id_t	mid;
1454	nvlist_t	*nvlp;
1455	int		ipp_rc;
1456	int		rc = 0;
1457	ipp_action_id_t	*aid_array;
1458	char		**aname_array = NULL;
1459	int		nelt;
1460	int		length;
1461	int		i;
1462
1463	/*
1464	 * Get the module id.
1465	 */
1466
1467	mid = ipp_mod_lookup(modname);
1468	FREE_TEXT(modname);
1469
1470	/*
1471	 * Get a list of all the action ids for the module. If that succeeds,
1472	 * translate the ids into names.
1473	 *
1474	 * NOTE: This translation may fail if an action is
1475	 * destroyed during this operation. If this occurs, EAGAIN
1476	 * will be passed back to libipp note that a transient
1477	 * problem occured.
1478	 */
1479
1480	if ((ipp_rc = ipp_mod_list_actions(mid, &aid_array, &nelt)) == 0) {
1481
1482		/*
1483		 * It is possible that there are no actions defined.
1484		 * (This is unlikely though as the module would normally
1485		 * be auto-unloaded fairly quickly)
1486		 */
1487
1488		if (nelt > 0) {
1489			length = nelt * sizeof (char *);
1490			aname_array = kmem_zalloc(length, KM_SLEEP);
1491
1492			for (i = 0; i < nelt; i++) {
1493				if (ipp_action_name(aid_array[i],
1494				    &aname_array[i]) != 0) {
1495					kmem_free(aid_array, nelt *
1496					    sizeof (ipp_action_id_t));
1497					FREE_TEXT_ARRAY(aname_array, nelt);
1498					ipp_rc = EAGAIN;
1499					goto done;
1500				}
1501			}
1502
1503			kmem_free(aid_array, nelt * sizeof (ipp_action_id_t));
1504
1505			if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME,
1506			    KM_SLEEP)) != 0) {
1507				FREE_TEXT_ARRAY(aname_array, nelt);
1508				return (rc);
1509			}
1510
1511			if ((rc = ippctl_attach_aname_array(nvlp, aname_array,
1512			    nelt)) != 0) {
1513				FREE_TEXT_ARRAY(aname_array, nelt);
1514				nvlist_free(nvlp);
1515				return (rc);
1516			}
1517
1518			FREE_TEXT_ARRAY(aname_array, nelt);
1519
1520			if ((rc = ippctl_callback(nvlp, NULL)) != 0) {
1521				nvlist_free(nvlp);
1522				return (rc);
1523			}
1524
1525			nvlist_free(nvlp);
1526		}
1527	}
1528
1529done:
1530	/*
1531	 * Add an nvlist containing the kernel return code to the
1532	 * set of nvlists to pass back to libipp.
1533	 */
1534
1535	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1536		return (rc);
1537
1538	return (0);
1539}
1540#undef	__FN__
1541
1542#define	__FN__	"ippctl_data"
1543static int
1544ippctl_data(
1545	char	**dbufp,
1546	size_t	*dbuflenp,
1547	size_t	*nextbuflenp)
1548{
1549	int	i;
1550
1551	DBG0(DBG_CBOPS, "called\n");
1552
1553	/*
1554	 * Get the next data buffer from the array by looking at the
1555	 * 'read index'. If this is the same as the 'write index' then
1556	 * there's no more buffers in the array.
1557	 */
1558
1559	i = ippctl_rindex;
1560	if (i == ippctl_windex)
1561		return (ENOENT);
1562
1563	/*
1564	 * Extract the buffer details. It is a pre-packed nvlist.
1565	 */
1566
1567	*dbufp = ippctl_array[i].buf;
1568	*dbuflenp = ippctl_array[i].buflen;
1569
1570	DBG2(DBG_CBOPS, "accessing nvlist[%d], length %lu\n", i, *dbuflenp);
1571	ASSERT(*dbufp != NULL);
1572
1573	/*
1574	 * Advance the 'read index' and check if there's another buffer.
1575	 * If there is then we need to pass back its length to libipp so that
1576	 * another data ioctl will be issued.
1577	 */
1578
1579	i++;
1580	if (i < ippctl_windex)
1581		*nextbuflenp = ippctl_array[i].buflen;
1582	else
1583		*nextbuflenp = 0;
1584
1585	ippctl_rindex = i;
1586	return (0);
1587}
1588#undef	__FN__
1589
1590#define	__FN__	"ippctl_flush"
1591static void
1592ippctl_flush(
1593	void)
1594{
1595	int	i;
1596	char	*buf;
1597	size_t	buflen;
1598
1599	/*
1600	 * Free any buffers left in the array.
1601	 */
1602
1603	for (i = 0; i < ippctl_limit; i++) {
1604		if ((buflen = ippctl_array[i].buflen) > 0) {
1605			buf = ippctl_array[i].buf;
1606			ASSERT(buf != NULL);
1607			kmem_free(buf, buflen);
1608		}
1609	}
1610
1611	/*
1612	 * NULL all the entries.
1613	 */
1614
1615	bzero(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
1616
1617	/*
1618	 * Reset the indexes.
1619	 */
1620
1621	ippctl_rindex = 0;
1622	ippctl_windex = 1;
1623}
1624#undef	__FN__
1625
1626#define	__FN__	"ippctl_add_nvlist"
1627static int
1628ippctl_add_nvlist(
1629	nvlist_t	*nvlp,
1630	int		i)
1631{
1632	char		*buf;
1633	size_t		buflen;
1634	int		rc;
1635
1636	/*
1637	 * NULL the buffer pointer so that a buffer is automatically
1638	 * allocated for us.
1639	 */
1640
1641	buf = NULL;
1642
1643	/*
1644	 * Pack the nvlist and get back the buffer pointer and length.
1645	 */
1646
1647	if ((rc = nvlist_pack(nvlp, &buf, &buflen, NV_ENCODE_NATIVE,
1648	    KM_SLEEP)) != 0) {
1649		ippctl_array[i].buf = NULL;
1650		ippctl_array[i].buflen = 0;
1651		return (rc);
1652	}
1653
1654	DBG2(DBG_CBOPS, "added nvlist[%d]: length %lu\n", i, buflen);
1655
1656	/*
1657	 * Store the pointer an length in the array at the given index.
1658	 */
1659
1660	ippctl_array[i].buf = buf;
1661	ippctl_array[i].buflen = buflen;
1662
1663	return (0);
1664}
1665#undef	__FN__
1666
1667#define	__FN__	"ippctl_callback"
1668/*ARGSUSED*/
1669static int
1670ippctl_callback(
1671	nvlist_t	*nvlp,
1672	void		*arg)
1673{
1674	int		i;
1675	int		rc;
1676
1677	/*
1678	 * Check the 'write index' to see if there's space in the array for
1679	 * a new entry.
1680	 */
1681
1682	i = ippctl_windex;
1683	ASSERT(i != 0);
1684
1685	/*
1686	 * If there's no space, re-allocate the array (see comments in
1687	 * ippctl_realloc() for details).
1688	 */
1689
1690	if (i == ippctl_limit)
1691		ippctl_realloc();
1692
1693	/*
1694	 * Add the nvlist to the array.
1695	 */
1696
1697	if ((rc = ippctl_add_nvlist(nvlp, i)) == 0)
1698		ippctl_windex++;
1699
1700	return (rc);
1701}
1702#undef	__FN__
1703
1704#define	__FN__	"ippctl_set_rc"
1705static int
1706ippctl_set_rc(
1707	int		val)
1708{
1709	nvlist_t	*nvlp;
1710	int		rc;
1711
1712	/*
1713	 * Create an nvlist to store the return code,
1714	 */
1715
1716	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_SLEEP)) != 0)
1717		return (ENOMEM);
1718
1719	if ((rc = nvlist_add_int32(nvlp, IPPCTL_RC, val)) != 0) {
1720		nvlist_free(nvlp);
1721		return (rc);
1722	}
1723
1724	/*
1725	 * Add it at the beginning of the array.
1726	 */
1727
1728	rc = ippctl_add_nvlist(nvlp, 0);
1729
1730	nvlist_free(nvlp);
1731	return (rc);
1732}
1733#undef	__FN__
1734
1735#define	__FN__	"ippctl_alloc"
1736static void
1737ippctl_alloc(
1738	int	limit)
1739{
1740	/*
1741	 * Allocate the data buffer array and initialize the indexes.
1742	 */
1743
1744	ippctl_array = kmem_zalloc(limit * sizeof (ippctl_buf_t), KM_SLEEP);
1745	ippctl_limit = limit;
1746	ippctl_rindex = 0;
1747	ippctl_windex = 1;
1748}
1749#undef	__FN__
1750
1751#define	__FN__	"ippctl_realloc"
1752static void
1753ippctl_realloc(
1754	void)
1755{
1756	ippctl_buf_t	*array;
1757	int		limit;
1758	int		i;
1759
1760	/*
1761	 * Allocate a new array twice the size of the old one.
1762	 */
1763
1764	limit = ippctl_limit << 1;
1765	array = kmem_zalloc(limit * sizeof (ippctl_buf_t), KM_SLEEP);
1766
1767	/*
1768	 * Copy across the information from the old array into the new one.
1769	 */
1770
1771	for (i = 0; i < ippctl_limit; i++)
1772		array[i] = ippctl_array[i];
1773
1774	/*
1775	 * Free the old array.
1776	 */
1777
1778	kmem_free(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
1779
1780	ippctl_array = array;
1781	ippctl_limit = limit;
1782}
1783#undef	__FN__
1784
1785#define	__FN__	"ippctl_free"
1786static void
1787ippctl_free(
1788	void)
1789{
1790	/*
1791	 * Flush the array prior to freeing it to make sure no buffers are
1792	 * leaked.
1793	 */
1794
1795	ippctl_flush();
1796
1797	/*
1798	 * Free the array.
1799	 */
1800
1801	kmem_free(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
1802	ippctl_array = NULL;
1803	ippctl_limit = -1;
1804	ippctl_rindex = -1;
1805	ippctl_windex = -1;
1806}
1807#undef	__FN__
1808
1809#ifdef	IPPCTL_DEBUG
1810static void
1811ippctl_debug(
1812	uint64_t	type,
1813	char		*fn,
1814	char		*fmt,
1815			...)
1816{
1817	char		buf[255];
1818	va_list		adx;
1819
1820	if ((type & ippctl_debug_flags) == 0)
1821		return;
1822
1823	mutex_enter(debug_mutex);
1824	va_start(adx, fmt);
1825	(void) vsnprintf(buf, 255, fmt, adx);
1826	va_end(adx);
1827
1828	printf("%s: %s", fn, buf);
1829	mutex_exit(debug_mutex);
1830}
1831#endif	/* IPPCTL_DBG */
1832