xref: /illumos-gate/usr/src/uts/common/ipp/ippctl.c (revision aab83bb8)
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 
66 static	uint64_t	ippctl_debug_flags =
67 /*
68  * DBG_MODLINK |
69  * DBG_DEVOPS |
70  * DBG_CBOPS |
71  */
72 0;
73 
74 static kmutex_t	debug_mutex[1];
75 
76 /*PRINTFLIKE3*/
77 static 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 
116 static int	ippctl_open(dev_t *, int, int, cred_t *);
117 static int	ippctl_close(dev_t, int, int, cred_t *);
118 static int	ippctl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
119 
120 static	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 
145 static	int	ippctl_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
146 static	int	ippctl_attach(dev_info_t *, ddi_attach_cmd_t);
147 static	int	ippctl_detach(dev_info_t *, ddi_detach_cmd_t);
148 
149 static	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 
164 static	struct modldrv modldrv = {
165 	&mod_driverops,
166 	"IP Policy Configuration Driver",
167 	&ippctl_dev_ops,
168 };
169 
170 static	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 
195 typedef	struct ippctl_buf	ippctl_buf_t;
196 
197 struct ippctl_buf {
198 	char	*buf;
199 	size_t	buflen;
200 };
201 
202 static int	ippctl_copyin(caddr_t, int, char **, size_t *);
203 static int	ippctl_copyout(caddr_t, int, char *, size_t);
204 static int	ippctl_extract_op(nvlist_t *, uint8_t *);
205 static int	ippctl_extract_aname(nvlist_t *, char **);
206 static int	ippctl_extract_modname(nvlist_t *, char **);
207 static int	ippctl_attach_modname(nvlist_t *nvlp, char *val);
208 static int	ippctl_attach_modname_array(nvlist_t *nvlp, char **val, int);
209 static int	ippctl_attach_aname_array(nvlist_t *nvlp, char **val, int);
210 static int	ippctl_extract_flags(nvlist_t *, ipp_flags_t *);
211 static int	ippctl_cmd(char *, size_t, size_t *);
212 static int	ippctl_action_create(char *, char *, nvlist_t *, ipp_flags_t);
213 static int	ippctl_action_destroy(char *, ipp_flags_t);
214 static int	ippctl_action_modify(char *, nvlist_t *, ipp_flags_t);
215 static int	ippctl_action_info(char *, ipp_flags_t);
216 static int	ippctl_action_mod(char *);
217 static int	ippctl_list_mods(void);
218 static int	ippctl_mod_list_actions(char *);
219 static int	ippctl_data(char **, size_t *, size_t *);
220 static void	ippctl_flush(void);
221 static int	ippctl_add_nvlist(nvlist_t *, int);
222 static int	ippctl_callback(nvlist_t *, void *);
223 static int	ippctl_set_rc(int);
224 static void	ippctl_alloc(int);
225 static void	ippctl_realloc(void);
226 static void	ippctl_free(void);
227 
228 /*
229  * Global data
230  */
231 
232 static dev_info_t	*ippctl_dip = NULL;
233 static kmutex_t		ippctl_lock;
234 static boolean_t	ippctl_busy;
235 static ippctl_buf_t	*ippctl_array = NULL;
236 static int		ippctl_limit = -1;
237 static int		ippctl_rindex = -1;
238 static int		ippctl_windex = -1;
239 
240 /*
241  * Module linkage functions
242  */
243 
244 #define	__FN__	"_init"
245 int
_init(void)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"
261 int
_fini(void)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"
277 int
_info(struct modinfo * modinfop)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*/
292 static	int
ippctl_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)293 ippctl_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"
319 static	int
ippctl_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)320 ippctl_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*/
367 static	int
ippctl_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)368 ippctl_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*/
397 static	int
ippctl_open(dev_t * devp,int flag,int otyp,cred_t * credp)398 ippctl_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*/
461 static	int
ippctl_close(dev_t dev,int flag,int otyp,cred_t * credp)462 ippctl_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"
491 static int
ippctl_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)492 ippctl_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"
595 static int
ippctl_copyin(caddr_t arg,int mode,char ** kbufp,size_t * kbuflenp)596 ippctl_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"
680 static int
ippctl_copyout(caddr_t arg,int mode,char * kbuf,size_t kbuflen)681 ippctl_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"
760 static int
ippctl_extract_op(nvlist_t * nvlp,uint8_t * valp)761 ippctl_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"
781 static int
ippctl_extract_aname(nvlist_t * nvlp,char ** valp)782 ippctl_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"
805 static int
ippctl_extract_modname(nvlist_t * nvlp,char ** valp)806 ippctl_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"
829 static int
ippctl_attach_modname(nvlist_t * nvlp,char * modname)830 ippctl_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"
844 static int
ippctl_attach_modname_array(nvlist_t * nvlp,char ** modname_array,int nelt)845 ippctl_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"
861 static int
ippctl_attach_aname_array(nvlist_t * nvlp,char ** aname_array,int nelt)862 ippctl_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"
878 static int
ippctl_extract_flags(nvlist_t * nvlp,ipp_flags_t * valp)879 ippctl_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"
900 static int
ippctl_cmd(char * cbuf,size_t cbuflen,size_t * nextbuflenp)901 ippctl_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"
1129 static int
ippctl_action_create(char * modname,char * aname,nvlist_t * nvlp,ipp_flags_t flags)1130 ippctl_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"
1186 static int
ippctl_action_destroy(char * aname,ipp_flags_t flags)1187 ippctl_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"
1221 static int
ippctl_action_modify(char * aname,nvlist_t * nvlp,ipp_flags_t flags)1222 ippctl_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 		nvlist_free(nvlp);
1247 		return (rc);
1248 	}
1249 
1250 	/*
1251 	 * If the module passed back an nvlist, add this as
1252 	 * well.
1253 	 */
1254 
1255 	if (nvlp != NULL) {
1256 		rc = ippctl_callback(nvlp, NULL);
1257 		nvlist_free(nvlp);
1258 	} else
1259 		rc = 0;
1260 
1261 	return (rc);
1262 }
1263 #undef	__FN__
1264 
1265 #define	__FN__	"ippctl_action_info"
1266 static int
ippctl_action_info(char * aname,ipp_flags_t flags)1267 ippctl_action_info(
1268 	char		*aname,
1269 	ipp_flags_t	flags)
1270 {
1271 	ipp_action_id_t	aid;
1272 	int		ipp_rc;
1273 	int		rc;
1274 
1275 	/*
1276 	 * Look up the action and call the information retrieval
1277 	 * entry point.
1278 	 *
1279 	 * NOTE: The callback function that is passed in packs and
1280 	 * stores each of the nvlists it is called with in the array
1281 	 * that will be passed back to libipp.
1282 	 */
1283 
1284 	aid = ipp_action_lookup(aname);
1285 	FREE_TEXT(aname);
1286 
1287 	ipp_rc = ipp_action_info(aid, ippctl_callback, NULL, flags);
1288 
1289 	/*
1290 	 * Add an nvlist containing the kernel return code to the
1291 	 * set of nvlists to pass back to libipp.
1292 	 */
1293 
1294 	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1295 		return (rc);
1296 
1297 	/*
1298 	 * There's no more information to pass back.
1299 	 */
1300 
1301 	return (0);
1302 }
1303 #undef	__FN__
1304 
1305 #define	__FN__	"ippctl_action_mod"
1306 static int
ippctl_action_mod(char * aname)1307 ippctl_action_mod(
1308 	char		*aname)
1309 {
1310 	ipp_mod_id_t	mid;
1311 	ipp_action_id_t	aid;
1312 	char		*modname;
1313 	nvlist_t	*nvlp;
1314 	int		ipp_rc;
1315 	int		rc;
1316 
1317 	/*
1318 	 * Look up the action id and get the id of the module that
1319 	 * implements the action. If that succeeds then look up the
1320 	 * name of the module.
1321 	 */
1322 
1323 	aid = ipp_action_lookup(aname);
1324 	FREE_TEXT(aname);
1325 
1326 	if ((ipp_rc = ipp_action_mod(aid, &mid)) == 0)
1327 		ipp_rc = ipp_mod_name(mid, &modname);
1328 
1329 	/*
1330 	 * Add an nvlist containing the kernel return code to the
1331 	 * set of nvlists to pass back to libipp.
1332 	 */
1333 
1334 	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1335 		return (rc);
1336 
1337 	/*
1338 	 * If everything succeeded add an nvlist containing the
1339 	 * module name to the set of nvlists to pass back to libipp.
1340 	 */
1341 
1342 	if (ipp_rc == 0) {
1343 		if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_SLEEP)) != 0)
1344 			return (rc);
1345 
1346 		if ((rc = ippctl_attach_modname(nvlp, modname)) != 0) {
1347 			nvlist_free(nvlp);
1348 			return (rc);
1349 		}
1350 
1351 		FREE_TEXT(modname);
1352 
1353 		rc = ippctl_callback(nvlp, NULL);
1354 		nvlist_free(nvlp);
1355 	} else
1356 		rc = 0;
1357 
1358 	return (rc);
1359 }
1360 #undef	__FN__
1361 
1362 #define	__FN__	"ippctl_list_mods"
1363 static int
ippctl_list_mods(void)1364 ippctl_list_mods(
1365 	void)
1366 {
1367 	nvlist_t	*nvlp;
1368 	int		ipp_rc;
1369 	int		rc = 0;
1370 	ipp_mod_id_t	*mid_array;
1371 	char		**modname_array = NULL;
1372 	int		nelt;
1373 	int		length;
1374 	int		i;
1375 
1376 	/*
1377 	 * Get a list of all the module ids. If that succeeds,
1378 	 * translate the ids into names.
1379 	 *
1380 	 * NOTE: This translation may fail if a module is
1381 	 * unloaded during this operation. If this occurs, EAGAIN
1382 	 * will be passed back to libipp note that a transient
1383 	 * problem occured.
1384 	 */
1385 
1386 	if ((ipp_rc = ipp_list_mods(&mid_array, &nelt)) == 0) {
1387 
1388 		/*
1389 		 * It is possible that there are no modules
1390 		 * registered.
1391 		 */
1392 
1393 		if (nelt > 0) {
1394 			length = nelt * sizeof (char *);
1395 			modname_array = kmem_zalloc(length, KM_SLEEP);
1396 
1397 			for (i = 0; i < nelt; i++) {
1398 				if (ipp_mod_name(mid_array[i],
1399 				    &modname_array[i]) != 0) {
1400 					kmem_free(mid_array, nelt *
1401 					    sizeof (ipp_mod_id_t));
1402 					FREE_TEXT_ARRAY(modname_array, nelt);
1403 					ipp_rc = EAGAIN;
1404 					goto done;
1405 				}
1406 			}
1407 
1408 			kmem_free(mid_array, nelt * sizeof (ipp_mod_id_t));
1409 
1410 			if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME,
1411 			    KM_SLEEP)) != 0) {
1412 				FREE_TEXT_ARRAY(modname_array, nelt);
1413 				return (rc);
1414 			}
1415 
1416 			if ((rc = ippctl_attach_modname_array(nvlp,
1417 			    modname_array, nelt)) != 0) {
1418 				FREE_TEXT_ARRAY(modname_array, nelt);
1419 				nvlist_free(nvlp);
1420 				return (rc);
1421 			}
1422 
1423 			FREE_TEXT_ARRAY(modname_array, nelt);
1424 
1425 			if ((rc = ippctl_callback(nvlp, NULL)) != 0) {
1426 				nvlist_free(nvlp);
1427 				return (rc);
1428 			}
1429 
1430 			nvlist_free(nvlp);
1431 		}
1432 	}
1433 
1434 done:
1435 	/*
1436 	 * Add an nvlist containing the kernel return code to the
1437 	 * set of nvlists to pass back to libipp.
1438 	 */
1439 
1440 	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1441 		return (rc);
1442 
1443 	return (0);
1444 }
1445 #undef	__FN__
1446 
1447 #define	__FN__	"ippctl_mod_list_actions"
1448 static int
ippctl_mod_list_actions(char * modname)1449 ippctl_mod_list_actions(
1450 	char		*modname)
1451 {
1452 	ipp_mod_id_t	mid;
1453 	nvlist_t	*nvlp;
1454 	int		ipp_rc;
1455 	int		rc = 0;
1456 	ipp_action_id_t	*aid_array;
1457 	char		**aname_array = NULL;
1458 	int		nelt;
1459 	int		length;
1460 	int		i;
1461 
1462 	/*
1463 	 * Get the module id.
1464 	 */
1465 
1466 	mid = ipp_mod_lookup(modname);
1467 	FREE_TEXT(modname);
1468 
1469 	/*
1470 	 * Get a list of all the action ids for the module. If that succeeds,
1471 	 * translate the ids into names.
1472 	 *
1473 	 * NOTE: This translation may fail if an action is
1474 	 * destroyed during this operation. If this occurs, EAGAIN
1475 	 * will be passed back to libipp note that a transient
1476 	 * problem occured.
1477 	 */
1478 
1479 	if ((ipp_rc = ipp_mod_list_actions(mid, &aid_array, &nelt)) == 0) {
1480 
1481 		/*
1482 		 * It is possible that there are no actions defined.
1483 		 * (This is unlikely though as the module would normally
1484 		 * be auto-unloaded fairly quickly)
1485 		 */
1486 
1487 		if (nelt > 0) {
1488 			length = nelt * sizeof (char *);
1489 			aname_array = kmem_zalloc(length, KM_SLEEP);
1490 
1491 			for (i = 0; i < nelt; i++) {
1492 				if (ipp_action_name(aid_array[i],
1493 				    &aname_array[i]) != 0) {
1494 					kmem_free(aid_array, nelt *
1495 					    sizeof (ipp_action_id_t));
1496 					FREE_TEXT_ARRAY(aname_array, nelt);
1497 					ipp_rc = EAGAIN;
1498 					goto done;
1499 				}
1500 			}
1501 
1502 			kmem_free(aid_array, nelt * sizeof (ipp_action_id_t));
1503 
1504 			if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME,
1505 			    KM_SLEEP)) != 0) {
1506 				FREE_TEXT_ARRAY(aname_array, nelt);
1507 				return (rc);
1508 			}
1509 
1510 			if ((rc = ippctl_attach_aname_array(nvlp, aname_array,
1511 			    nelt)) != 0) {
1512 				FREE_TEXT_ARRAY(aname_array, nelt);
1513 				nvlist_free(nvlp);
1514 				return (rc);
1515 			}
1516 
1517 			FREE_TEXT_ARRAY(aname_array, nelt);
1518 
1519 			if ((rc = ippctl_callback(nvlp, NULL)) != 0) {
1520 				nvlist_free(nvlp);
1521 				return (rc);
1522 			}
1523 
1524 			nvlist_free(nvlp);
1525 		}
1526 	}
1527 
1528 done:
1529 	/*
1530 	 * Add an nvlist containing the kernel return code to the
1531 	 * set of nvlists to pass back to libipp.
1532 	 */
1533 
1534 	if ((rc = ippctl_set_rc(ipp_rc)) != 0)
1535 		return (rc);
1536 
1537 	return (0);
1538 }
1539 #undef	__FN__
1540 
1541 #define	__FN__	"ippctl_data"
1542 static int
ippctl_data(char ** dbufp,size_t * dbuflenp,size_t * nextbuflenp)1543 ippctl_data(
1544 	char	**dbufp,
1545 	size_t	*dbuflenp,
1546 	size_t	*nextbuflenp)
1547 {
1548 	int	i;
1549 
1550 	DBG0(DBG_CBOPS, "called\n");
1551 
1552 	/*
1553 	 * Get the next data buffer from the array by looking at the
1554 	 * 'read index'. If this is the same as the 'write index' then
1555 	 * there's no more buffers in the array.
1556 	 */
1557 
1558 	i = ippctl_rindex;
1559 	if (i == ippctl_windex)
1560 		return (ENOENT);
1561 
1562 	/*
1563 	 * Extract the buffer details. It is a pre-packed nvlist.
1564 	 */
1565 
1566 	*dbufp = ippctl_array[i].buf;
1567 	*dbuflenp = ippctl_array[i].buflen;
1568 
1569 	DBG2(DBG_CBOPS, "accessing nvlist[%d], length %lu\n", i, *dbuflenp);
1570 	ASSERT(*dbufp != NULL);
1571 
1572 	/*
1573 	 * Advance the 'read index' and check if there's another buffer.
1574 	 * If there is then we need to pass back its length to libipp so that
1575 	 * another data ioctl will be issued.
1576 	 */
1577 
1578 	i++;
1579 	if (i < ippctl_windex)
1580 		*nextbuflenp = ippctl_array[i].buflen;
1581 	else
1582 		*nextbuflenp = 0;
1583 
1584 	ippctl_rindex = i;
1585 	return (0);
1586 }
1587 #undef	__FN__
1588 
1589 #define	__FN__	"ippctl_flush"
1590 static void
ippctl_flush(void)1591 ippctl_flush(
1592 	void)
1593 {
1594 	int	i;
1595 	char	*buf;
1596 	size_t	buflen;
1597 
1598 	/*
1599 	 * Free any buffers left in the array.
1600 	 */
1601 
1602 	for (i = 0; i < ippctl_limit; i++) {
1603 		if ((buflen = ippctl_array[i].buflen) > 0) {
1604 			buf = ippctl_array[i].buf;
1605 			ASSERT(buf != NULL);
1606 			kmem_free(buf, buflen);
1607 		}
1608 	}
1609 
1610 	/*
1611 	 * NULL all the entries.
1612 	 */
1613 
1614 	bzero(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
1615 
1616 	/*
1617 	 * Reset the indexes.
1618 	 */
1619 
1620 	ippctl_rindex = 0;
1621 	ippctl_windex = 1;
1622 }
1623 #undef	__FN__
1624 
1625 #define	__FN__	"ippctl_add_nvlist"
1626 static int
ippctl_add_nvlist(nvlist_t * nvlp,int i)1627 ippctl_add_nvlist(
1628 	nvlist_t	*nvlp,
1629 	int		i)
1630 {
1631 	char		*buf;
1632 	size_t		buflen;
1633 	int		rc;
1634 
1635 	/*
1636 	 * NULL the buffer pointer so that a buffer is automatically
1637 	 * allocated for us.
1638 	 */
1639 
1640 	buf = NULL;
1641 
1642 	/*
1643 	 * Pack the nvlist and get back the buffer pointer and length.
1644 	 */
1645 
1646 	if ((rc = nvlist_pack(nvlp, &buf, &buflen, NV_ENCODE_NATIVE,
1647 	    KM_SLEEP)) != 0) {
1648 		ippctl_array[i].buf = NULL;
1649 		ippctl_array[i].buflen = 0;
1650 		return (rc);
1651 	}
1652 
1653 	DBG2(DBG_CBOPS, "added nvlist[%d]: length %lu\n", i, buflen);
1654 
1655 	/*
1656 	 * Store the pointer an length in the array at the given index.
1657 	 */
1658 
1659 	ippctl_array[i].buf = buf;
1660 	ippctl_array[i].buflen = buflen;
1661 
1662 	return (0);
1663 }
1664 #undef	__FN__
1665 
1666 #define	__FN__	"ippctl_callback"
1667 /*ARGSUSED*/
1668 static int
ippctl_callback(nvlist_t * nvlp,void * arg)1669 ippctl_callback(
1670 	nvlist_t	*nvlp,
1671 	void		*arg)
1672 {
1673 	int		i;
1674 	int		rc;
1675 
1676 	/*
1677 	 * Check the 'write index' to see if there's space in the array for
1678 	 * a new entry.
1679 	 */
1680 
1681 	i = ippctl_windex;
1682 	ASSERT(i != 0);
1683 
1684 	/*
1685 	 * If there's no space, re-allocate the array (see comments in
1686 	 * ippctl_realloc() for details).
1687 	 */
1688 
1689 	if (i == ippctl_limit)
1690 		ippctl_realloc();
1691 
1692 	/*
1693 	 * Add the nvlist to the array.
1694 	 */
1695 
1696 	if ((rc = ippctl_add_nvlist(nvlp, i)) == 0)
1697 		ippctl_windex++;
1698 
1699 	return (rc);
1700 }
1701 #undef	__FN__
1702 
1703 #define	__FN__	"ippctl_set_rc"
1704 static int
ippctl_set_rc(int val)1705 ippctl_set_rc(
1706 	int		val)
1707 {
1708 	nvlist_t	*nvlp;
1709 	int		rc;
1710 
1711 	/*
1712 	 * Create an nvlist to store the return code,
1713 	 */
1714 
1715 	if ((rc = nvlist_alloc(&nvlp, NV_UNIQUE_NAME, KM_SLEEP)) != 0)
1716 		return (ENOMEM);
1717 
1718 	if ((rc = nvlist_add_int32(nvlp, IPPCTL_RC, val)) != 0) {
1719 		nvlist_free(nvlp);
1720 		return (rc);
1721 	}
1722 
1723 	/*
1724 	 * Add it at the beginning of the array.
1725 	 */
1726 
1727 	rc = ippctl_add_nvlist(nvlp, 0);
1728 
1729 	nvlist_free(nvlp);
1730 	return (rc);
1731 }
1732 #undef	__FN__
1733 
1734 #define	__FN__	"ippctl_alloc"
1735 static void
ippctl_alloc(int limit)1736 ippctl_alloc(
1737 	int	limit)
1738 {
1739 	/*
1740 	 * Allocate the data buffer array and initialize the indexes.
1741 	 */
1742 
1743 	ippctl_array = kmem_zalloc(limit * sizeof (ippctl_buf_t), KM_SLEEP);
1744 	ippctl_limit = limit;
1745 	ippctl_rindex = 0;
1746 	ippctl_windex = 1;
1747 }
1748 #undef	__FN__
1749 
1750 #define	__FN__	"ippctl_realloc"
1751 static void
ippctl_realloc(void)1752 ippctl_realloc(
1753 	void)
1754 {
1755 	ippctl_buf_t	*array;
1756 	int		limit;
1757 	int		i;
1758 
1759 	/*
1760 	 * Allocate a new array twice the size of the old one.
1761 	 */
1762 
1763 	limit = ippctl_limit << 1;
1764 	array = kmem_zalloc(limit * sizeof (ippctl_buf_t), KM_SLEEP);
1765 
1766 	/*
1767 	 * Copy across the information from the old array into the new one.
1768 	 */
1769 
1770 	for (i = 0; i < ippctl_limit; i++)
1771 		array[i] = ippctl_array[i];
1772 
1773 	/*
1774 	 * Free the old array.
1775 	 */
1776 
1777 	kmem_free(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
1778 
1779 	ippctl_array = array;
1780 	ippctl_limit = limit;
1781 }
1782 #undef	__FN__
1783 
1784 #define	__FN__	"ippctl_free"
1785 static void
ippctl_free(void)1786 ippctl_free(
1787 	void)
1788 {
1789 	/*
1790 	 * Flush the array prior to freeing it to make sure no buffers are
1791 	 * leaked.
1792 	 */
1793 
1794 	ippctl_flush();
1795 
1796 	/*
1797 	 * Free the array.
1798 	 */
1799 
1800 	kmem_free(ippctl_array, ippctl_limit * sizeof (ippctl_buf_t));
1801 	ippctl_array = NULL;
1802 	ippctl_limit = -1;
1803 	ippctl_rindex = -1;
1804 	ippctl_windex = -1;
1805 }
1806 #undef	__FN__
1807 
1808 #ifdef	IPPCTL_DEBUG
1809 static void
ippctl_debug(uint64_t type,char * fn,char * fmt,...)1810 ippctl_debug(
1811 	uint64_t	type,
1812 	char		*fn,
1813 	char		*fmt,
1814 			...)
1815 {
1816 	char		buf[255];
1817 	va_list		adx;
1818 
1819 	if ((type & ippctl_debug_flags) == 0)
1820 		return;
1821 
1822 	mutex_enter(debug_mutex);
1823 	va_start(adx, fmt);
1824 	(void) vsnprintf(buf, 255, fmt, adx);
1825 	va_end(adx);
1826 
1827 	printf("%s: %s", fn, buf);
1828 	mutex_exit(debug_mutex);
1829 }
1830 #endif	/* IPPCTL_DBG */
1831