xref: /titanic_41/usr/src/uts/common/ipp/ippctl.c (revision 193974072f41a843678abf5f61979c748687e66b)
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 		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"
1267 static int
ippctl_action_info(char * aname,ipp_flags_t flags)1268 ippctl_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"
1307 static int
ippctl_action_mod(char * aname)1308 ippctl_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"
1364 static int
ippctl_list_mods(void)1365 ippctl_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 
1435 done:
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"
1449 static int
ippctl_mod_list_actions(char * modname)1450 ippctl_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 
1529 done:
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"
1543 static int
ippctl_data(char ** dbufp,size_t * dbuflenp,size_t * nextbuflenp)1544 ippctl_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"
1591 static void
ippctl_flush(void)1592 ippctl_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"
1627 static int
ippctl_add_nvlist(nvlist_t * nvlp,int i)1628 ippctl_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*/
1669 static int
ippctl_callback(nvlist_t * nvlp,void * arg)1670 ippctl_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"
1705 static int
ippctl_set_rc(int val)1706 ippctl_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"
1736 static void
ippctl_alloc(int limit)1737 ippctl_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"
1752 static void
ippctl_realloc(void)1753 ippctl_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"
1786 static void
ippctl_free(void)1787 ippctl_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
1810 static void
ippctl_debug(uint64_t type,char * fn,char * fmt,...)1811 ippctl_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