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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
32 * STREAMS Administrative Driver
34 * Currently only handles autopush and module name verification.
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/errno.h>
40 #include <sys/stream.h>
41 #include <sys/stropts.h>
42 #include <sys/strsubr.h>
43 #include <sys/strsun.h>
47 #include <sys/debug.h>
49 #include <sys/sunddi.h>
51 #include <sys/cmn_err.h>
52 #include <sys/systm.h>
53 #include <sys/modctl.h>
54 #include <sys/sysmacros.h>
56 #include <sys/policy.h>
58 static int sadopen(queue_t
*, dev_t
*, int, int, cred_t
*);
59 static int sadclose(queue_t
*, int, cred_t
*);
60 static int sadwput(queue_t
*qp
, mblk_t
*mp
);
62 static int sad_info(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
63 static int sad_attach(dev_info_t
*, ddi_attach_cmd_t
);
65 static void apush_ioctl(), apush_iocdata();
66 static void vml_ioctl(), vml_iocdata();
67 static int valid_major(major_t
);
69 static dev_info_t
*sad_dip
; /* private copy of devinfo pointer */
71 static struct module_info sad_minfo
= {
72 0x7361, "sad", 0, INFPSZ
, 0, 0
75 static struct qinit sad_rinit
= {
76 NULL
, NULL
, sadopen
, sadclose
, NULL
, &sad_minfo
, NULL
79 static struct qinit sad_winit
= {
80 sadwput
, NULL
, NULL
, NULL
, NULL
, &sad_minfo
, NULL
83 struct streamtab sadinfo
= {
84 &sad_rinit
, &sad_winit
, NULL
, NULL
87 DDI_DEFINE_STREAM_OPS(sad_ops
, nulldev
, nulldev
, sad_attach
,
88 nodev
, nodev
, sad_info
,
89 D_MP
| D_MTPERQ
| D_MTOUTPERIM
| D_MTOCEXCL
, &sadinfo
,
90 ddi_quiesce_not_supported
);
93 * Module linkage information for the kernel.
96 static struct modldrv modldrv
= {
97 &mod_driverops
, /* Type of module. This one is a pseudo driver */
98 "STREAMS Administrative Driver 'sad'",
99 &sad_ops
, /* driver ops */
102 static struct modlinkage modlinkage
= {
103 MODREV_1
, &modldrv
, NULL
109 return (mod_install(&modlinkage
));
115 return (mod_remove(&modlinkage
));
119 _info(struct modinfo
*modinfop
)
121 return (mod_info(&modlinkage
, modinfop
));
125 sad_attach(dev_info_t
*devi
, ddi_attach_cmd_t cmd
)
127 int instance
= ddi_get_instance(devi
);
129 if (cmd
!= DDI_ATTACH
)
130 return (DDI_FAILURE
);
132 ASSERT(instance
== 0);
134 return (DDI_FAILURE
);
136 if (ddi_create_minor_node(devi
, "user", S_IFCHR
,
137 0, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
138 return (DDI_FAILURE
);
140 if (ddi_create_minor_node(devi
, "admin", S_IFCHR
,
141 1, DDI_PSEUDO
, NULL
) == DDI_FAILURE
) {
142 ddi_remove_minor_node(devi
, NULL
);
143 return (DDI_FAILURE
);
146 return (DDI_SUCCESS
);
151 sad_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
156 case DDI_INFO_DEVT2DEVINFO
:
157 if (sad_dip
== NULL
) {
164 case DDI_INFO_DEVT2INSTANCE
:
177 * Allocate a sad device. Only one
178 * open at a time allowed per device.
183 queue_t
*qp
, /* pointer to read queue */
184 dev_t
*devp
, /* major/minor device of stream */
185 int flag
, /* file open flags */
186 int sflag
, /* stream open flags */
187 cred_t
*credp
) /* user credentials */
193 if (sflag
) /* no longer called from clone driver */
196 /* Only privileged process can access ADMINDEV */
197 if (getminor(*devp
) == ADMMIN
) {
200 err
= secpolicy_sadopen(credp
);
206 ns
= netstack_find_by_cred(credp
);
208 ss
= ns
->netstack_str
;
212 * Both USRMIN and ADMMIN are clone interfaces.
214 for (i
= 0; i
< ss
->ss_sadcnt
; i
++)
215 if (ss
->ss_saddev
[i
].sa_qp
== NULL
)
217 if (i
>= ss
->ss_sadcnt
) { /* no such device */
218 netstack_rele(ss
->ss_netstack
);
221 switch (getminor(*devp
)) {
222 case USRMIN
: /* mere mortal */
223 ss
->ss_saddev
[i
].sa_flags
= 0;
226 case ADMMIN
: /* privileged user */
227 ss
->ss_saddev
[i
].sa_flags
= SADPRIV
;
231 netstack_rele(ss
->ss_netstack
);
235 ss
->ss_saddev
[i
].sa_qp
= qp
;
236 ss
->ss_saddev
[i
].sa_ss
= ss
;
237 qp
->q_ptr
= (caddr_t
)&ss
->ss_saddev
[i
];
238 WR(qp
)->q_ptr
= (caddr_t
)&ss
->ss_saddev
[i
];
241 * NOTE: should the ADMMIN or USRMIN minors change
242 * then so should the offset of 2 below
243 * Both USRMIN and ADMMIN are clone interfaces and
244 * therefore their minor numbers (0 and 1) are reserved.
246 *devp
= makedevice(getemajor(*devp
), i
+ 2);
253 * Clean up the data structures.
258 queue_t
*qp
, /* pointer to read queue */
259 int flag
, /* file open flags */
260 cred_t
*credp
) /* user credentials */
265 sadp
= (struct saddev
*)qp
->q_ptr
;
267 sadp
->sa_addr
= NULL
;
268 netstack_rele(sadp
->sa_ss
->ss_netstack
);
271 WR(qp
)->q_ptr
= NULL
;
277 * Write side put procedure.
281 queue_t
*qp
, /* pointer to write queue */
282 mblk_t
*mp
) /* message pointer */
286 switch (mp
->b_datap
->db_type
) {
288 if (*mp
->b_rptr
& FLUSHR
) {
289 *mp
->b_rptr
&= ~FLUSHW
;
296 iocp
= (struct iocblk
*)mp
->b_rptr
;
297 switch (SAD_CMD(iocp
->ioc_cmd
)) {
298 case SAD_CMD(SAD_SAP
):
299 case SAD_CMD(SAD_GAP
):
308 miocnak(qp
, mp
, 0, EINVAL
);
314 iocp
= (struct iocblk
*)mp
->b_rptr
;
315 switch (SAD_CMD(iocp
->ioc_cmd
)) {
316 case SAD_CMD(SAD_SAP
):
317 case SAD_CMD(SAD_GAP
):
318 apush_iocdata(qp
, mp
);
327 "sadwput: invalid ioc_cmd in case M_IOCDATA: %d",
337 } /* switch (db_type) */
343 * Handle the M_IOCTL messages associated with
344 * the autopush feature.
348 queue_t
*qp
, /* pointer to write queue */
349 mblk_t
*mp
) /* message pointer */
355 iocp
= (struct iocblk
*)mp
->b_rptr
;
356 if (iocp
->ioc_count
!= TRANSPARENT
) {
357 miocnak(qp
, mp
, 0, EINVAL
);
360 if (SAD_VER(iocp
->ioc_cmd
) > AP_VERSION
) {
361 miocnak(qp
, mp
, 0, EINVAL
);
365 sadp
= (struct saddev
*)qp
->q_ptr
;
366 switch (SAD_CMD(iocp
->ioc_cmd
)) {
367 case SAD_CMD(SAD_SAP
):
368 if (!(sadp
->sa_flags
& SADPRIV
)) {
369 miocnak(qp
, mp
, 0, EPERM
);
374 case SAD_CMD(SAD_GAP
):
375 sadp
->sa_addr
= (caddr_t
)*(uintptr_t *)mp
->b_cont
->b_rptr
;
376 if (SAD_VER(iocp
->ioc_cmd
) == 1)
377 size
= STRAPUSH_V1_LEN
;
379 size
= STRAPUSH_V0_LEN
;
380 mcopyin(mp
, (void *)GETSTRUCT
, size
, NULL
);
386 miocnak(qp
, mp
, 0, EINVAL
);
388 } /* switch (ioc_cmd) */
393 * Handle the M_IOCDATA messages associated with
394 * the autopush feature.
398 queue_t
*qp
, /* pointer to write queue */
399 mblk_t
*mp
) /* message pointer */
402 struct copyresp
*csp
;
403 struct strapush
*sap
= NULL
;
404 struct autopush
*ap
, *ap_tmp
;
410 sadp
= (struct saddev
*)qp
->q_ptr
;
413 csp
= (struct copyresp
*)mp
->b_rptr
;
414 if (csp
->cp_rval
) { /* if there was an error */
420 * sap needed only if mp->b_cont is set. figure out the
421 * size of the expected sap structure and make sure
422 * enough data was supplied.
424 if (SAD_VER(csp
->cp_cmd
) == 1)
425 size
= STRAPUSH_V1_LEN
;
427 size
= STRAPUSH_V0_LEN
;
428 if (MBLKL(mp
->b_cont
) < size
) {
429 miocnak(qp
, mp
, 0, EINVAL
);
432 sap
= (struct strapush
*)mp
->b_cont
->b_rptr
;
433 dev
= makedevice(sap
->sap_major
, sap
->sap_minor
);
435 switch (SAD_CMD(csp
->cp_cmd
)) {
436 case SAD_CMD(SAD_SAP
):
438 /* currently we only support one SAD_SAP command */
439 if (((long)csp
->cp_private
) != GETSTRUCT
) {
441 "apush_iocdata: cp_private bad in SAD_SAP: %p",
442 (void *)csp
->cp_private
);
443 miocnak(qp
, mp
, 0, EINVAL
);
447 switch (sap
->sap_cmd
) {
449 miocnak(qp
, mp
, 0, EINVAL
);
454 /* allocate and initialize a new config */
456 ap
->ap_common
= sap
->sap_common
;
457 if (SAD_VER(csp
->cp_cmd
) > 0)
458 ap
->ap_anchor
= sap
->sap_anchor
;
459 for (i
= 0; i
< MIN(sap
->sap_npush
, MAXAPUSH
); i
++)
460 (void) strncpy(ap
->ap_list
[i
],
461 sap
->sap_list
[i
], FMNAMESZ
);
463 /* sanity check the request */
464 if (((ret
= sad_ap_verify(ap
)) != 0) ||
465 ((ret
= valid_major(ap
->ap_major
)) != 0)) {
467 miocnak(qp
, mp
, 0, ret
);
471 /* check for overlapping configs */
472 mutex_enter(&ss
->ss_sad_lock
);
473 ap_tmp
= sad_ap_find(&ap
->ap_common
, ss
);
474 if (ap_tmp
!= NULL
) {
475 /* already configured */
476 mutex_exit(&ss
->ss_sad_lock
);
477 sad_ap_rele(ap_tmp
, ss
);
479 miocnak(qp
, mp
, 0, EEXIST
);
483 /* add the new config to our hash */
484 sad_ap_insert(ap
, ss
);
485 mutex_exit(&ss
->ss_sad_lock
);
486 miocack(qp
, mp
, 0, 0);
490 /* sanity check the request */
491 if (ret
= valid_major(sap
->sap_major
)) {
492 miocnak(qp
, mp
, 0, ret
);
496 /* search for a matching config */
497 if ((ap
= sad_ap_find_by_dev(dev
, ss
)) == NULL
) {
498 /* no config found */
499 miocnak(qp
, mp
, 0, ENODEV
);
504 * If we matched a SAP_RANGE config
505 * the minor passed in must match the
506 * beginning of the range exactly.
508 if ((ap
->ap_type
== SAP_RANGE
) &&
509 (ap
->ap_minor
!= sap
->sap_minor
)) {
511 miocnak(qp
, mp
, 0, ERANGE
);
516 * If we matched a SAP_ALL config
517 * the minor passed in must be 0.
519 if ((ap
->ap_type
== SAP_ALL
) &&
520 (sap
->sap_minor
!= 0)) {
522 miocnak(qp
, mp
, 0, EINVAL
);
527 * make sure someone else hasn't already
528 * removed this config from the hash.
530 mutex_enter(&ss
->ss_sad_lock
);
531 ap_tmp
= sad_ap_find(&ap
->ap_common
, ss
);
533 mutex_exit(&ss
->ss_sad_lock
);
534 sad_ap_rele(ap_tmp
, ss
);
536 miocnak(qp
, mp
, 0, ENODEV
);
540 /* remove the config from the hash and return */
541 sad_ap_remove(ap
, ss
);
542 mutex_exit(&ss
->ss_sad_lock
);
545 * Release thrice, once for sad_ap_find_by_dev(),
546 * once for sad_ap_find(), and once to free.
551 miocack(qp
, mp
, 0, 0);
553 } /* switch (sap_cmd) */
556 case SAD_CMD(SAD_GAP
):
557 switch ((long)csp
->cp_private
) {
560 /* sanity check the request */
561 if (ret
= valid_major(sap
->sap_major
)) {
562 miocnak(qp
, mp
, 0, ret
);
566 /* search for a matching config */
567 if ((ap
= sad_ap_find_by_dev(dev
, ss
)) == NULL
) {
568 /* no config found */
569 miocnak(qp
, mp
, 0, ENODEV
);
573 /* copy out the contents of the config */
574 sap
->sap_common
= ap
->ap_common
;
575 if (SAD_VER(csp
->cp_cmd
) > 0)
576 sap
->sap_anchor
= ap
->ap_anchor
;
577 for (i
= 0; i
< ap
->ap_npush
; i
++)
578 (void) strcpy(sap
->sap_list
[i
], ap
->ap_list
[i
]);
579 for (; i
< MAXAPUSH
; i
++)
580 bzero(sap
->sap_list
[i
], FMNAMESZ
+ 1);
582 /* release our hold on the config */
585 /* copyout the results */
586 if (SAD_VER(csp
->cp_cmd
) == 1)
587 size
= STRAPUSH_V1_LEN
;
589 size
= STRAPUSH_V0_LEN
;
591 mcopyout(mp
, (void *)GETRESULT
, size
, sadp
->sa_addr
,
596 miocack(qp
, mp
, 0, 0);
601 "apush_iocdata: cp_private bad case SAD_GAP: %p",
602 (void *)csp
->cp_private
);
605 } /* switch (cp_private) */
607 default: /* can't happen */
611 } /* switch (cp_cmd) */
616 * Handle the M_IOCTL message associated with a request
617 * to validate a module list.
621 queue_t
*qp
, /* pointer to write queue */
622 mblk_t
*mp
) /* message pointer */
626 iocp
= (struct iocblk
*)mp
->b_rptr
;
627 if (iocp
->ioc_count
!= TRANSPARENT
) {
628 miocnak(qp
, mp
, 0, EINVAL
);
631 ASSERT(SAD_CMD(iocp
->ioc_cmd
) == SAD_VML
);
632 mcopyin(mp
, (void *)GETSTRUCT
,
633 SIZEOF_STRUCT(str_list
, iocp
->ioc_flag
), NULL
);
639 * Handle the M_IOCDATA messages associated with
640 * a request to validate a module list.
644 queue_t
*qp
, /* pointer to write queue */
645 mblk_t
*mp
) /* message pointer */
649 struct copyresp
*csp
;
650 struct str_mlist
*lp
;
651 STRUCT_HANDLE(str_list
, slp
);
654 csp
= (struct copyresp
*)mp
->b_rptr
;
655 if (csp
->cp_rval
) { /* if there was an error */
660 ASSERT(SAD_CMD(csp
->cp_cmd
) == SAD_VML
);
661 sadp
= (struct saddev
*)qp
->q_ptr
;
662 switch ((long)csp
->cp_private
) {
664 STRUCT_SET_HANDLE(slp
, csp
->cp_flag
,
665 (struct str_list
*)mp
->b_cont
->b_rptr
);
666 nmods
= STRUCT_FGET(slp
, sl_nmods
);
668 miocnak(qp
, mp
, 0, EINVAL
);
671 sadp
->sa_addr
= (caddr_t
)(uintptr_t)nmods
;
673 mcopyin(mp
, (void *)GETLIST
, nmods
* sizeof (struct str_mlist
),
674 STRUCT_FGETP(slp
, sl_modlist
));
679 lp
= (struct str_mlist
*)mp
->b_cont
->b_rptr
;
680 for (i
= 0; i
< (long)sadp
->sa_addr
; i
++, lp
++) {
681 lp
->l_name
[FMNAMESZ
] = '\0';
682 if (fmodsw_find(lp
->l_name
, FMODSW_LOAD
) == NULL
) {
683 miocack(qp
, mp
, 0, 1);
687 miocack(qp
, mp
, 0, 0);
691 cmn_err(CE_WARN
, "vml_iocdata: invalid cp_private value: %p",
692 (void *)csp
->cp_private
);
695 } /* switch (cp_private) */
699 * Validate a major number and also verify if
700 * it is a STREAMS device.
701 * Return values: 0 if a valid STREAMS dev
702 * error code otherwise
705 valid_major(major_t major
)
709 if (etoimajor(major
) == -1)
713 * attempt to load the driver 'major' and verify that
714 * it is a STREAMS driver.
716 if (ddi_hold_driver(major
) == NULL
)
719 if (!STREAMSTAB(major
))
722 ddi_rele_driver(major
);