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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 32 /* 33 * STREAMS Administrative Driver 34 * 35 * Currently only handles autopush and module name verification. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/errno.h> 41 #include <sys/stream.h> 42 #include <sys/stropts.h> 43 #include <sys/strsubr.h> 44 #include <sys/strsun.h> 45 #include <sys/conf.h> 46 #include <sys/sad.h> 47 #include <sys/cred.h> 48 #include <sys/debug.h> 49 #include <sys/ddi.h> 50 #include <sys/sunddi.h> 51 #include <sys/stat.h> 52 #include <sys/cmn_err.h> 53 #include <sys/systm.h> 54 #include <sys/modctl.h> 55 #include <sys/priv_names.h> 56 #include <sys/sysmacros.h> 57 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); 61 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); 64 65 static void apush_ioctl(), apush_iocdata(); 66 static void vml_ioctl(), vml_iocdata(); 67 static int valid_major(major_t); 68 69 static dev_info_t *sad_dip; /* private copy of devinfo pointer */ 70 71 static struct module_info sad_minfo = { 72 0x7361, "sad", 0, INFPSZ, 0, 0 73 }; 74 75 static struct qinit sad_rinit = { 76 NULL, NULL, sadopen, sadclose, NULL, &sad_minfo, NULL 77 }; 78 79 static struct qinit sad_winit = { 80 sadwput, NULL, NULL, NULL, NULL, &sad_minfo, NULL 81 }; 82 83 struct streamtab sadinfo = { 84 &sad_rinit, &sad_winit, NULL, NULL 85 }; 86 87 DDI_DEFINE_STREAM_OPS(sad_ops, nulldev, nulldev, sad_attach, 88 nodev, nodev, sad_info, D_MTPERQ | D_MP, &sadinfo); 89 90 /* 91 * Module linkage information for the kernel. 92 */ 93 94 static struct modldrv modldrv = { 95 &mod_driverops, /* Type of module. This one is a pseudo driver */ 96 "STREAMS Administrative Driver 'sad' %I%", 97 &sad_ops, /* driver ops */ 98 }; 99 100 static struct modlinkage modlinkage = { 101 MODREV_1, &modldrv, NULL 102 }; 103 104 int 105 _init(void) 106 { 107 return (mod_install(&modlinkage)); 108 } 109 110 int 111 _fini(void) 112 { 113 return (mod_remove(&modlinkage)); 114 } 115 116 int 117 _info(struct modinfo *modinfop) 118 { 119 return (mod_info(&modlinkage, modinfop)); 120 } 121 122 static int 123 sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 124 { 125 int instance = ddi_get_instance(devi); 126 127 if (cmd != DDI_ATTACH) 128 return (DDI_FAILURE); 129 130 ASSERT(instance == 0); 131 if (instance != 0) 132 return (DDI_FAILURE); 133 134 if (ddi_create_minor_node(devi, "user", S_IFCHR, 135 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 136 return (DDI_FAILURE); 137 } 138 if (ddi_create_priv_minor_node(devi, "admin", S_IFCHR, 139 1, DDI_PSEUDO, PRIVONLY_DEV, PRIV_SYS_CONFIG, 140 PRIV_SYS_CONFIG, 0666) == DDI_FAILURE) { 141 ddi_remove_minor_node(devi, NULL); 142 return (DDI_FAILURE); 143 } 144 sad_dip = devi; 145 return (DDI_SUCCESS); 146 } 147 148 /* ARGSUSED */ 149 static int 150 sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 151 { 152 int error; 153 154 switch (infocmd) { 155 case DDI_INFO_DEVT2DEVINFO: 156 if (sad_dip == NULL) { 157 error = DDI_FAILURE; 158 } else { 159 *result = sad_dip; 160 error = DDI_SUCCESS; 161 } 162 break; 163 case DDI_INFO_DEVT2INSTANCE: 164 *result = (void *)0; 165 error = DDI_SUCCESS; 166 break; 167 default: 168 error = DDI_FAILURE; 169 } 170 return (error); 171 } 172 173 174 /* 175 * sadopen() - 176 * Allocate a sad device. Only one 177 * open at a time allowed per device. 178 */ 179 /* ARGSUSED */ 180 static int 181 sadopen( 182 queue_t *qp, /* pointer to read queue */ 183 dev_t *devp, /* major/minor device of stream */ 184 int flag, /* file open flags */ 185 int sflag, /* stream open flags */ 186 cred_t *credp) /* user credentials */ 187 { 188 int i; 189 190 if (sflag) /* no longer called from clone driver */ 191 return (EINVAL); 192 193 /* 194 * Both USRMIN and ADMMIN are clone interfaces. 195 */ 196 for (i = 0; i < sadcnt; i++) 197 if (saddev[i].sa_qp == NULL) 198 break; 199 if (i >= sadcnt) /* no such device */ 200 return (ENXIO); 201 202 switch (getminor(*devp)) { 203 case USRMIN: /* mere mortal */ 204 saddev[i].sa_flags = 0; 205 break; 206 207 case ADMMIN: /* privileged user */ 208 saddev[i].sa_flags = SADPRIV; 209 break; 210 211 default: 212 return (EINVAL); 213 } 214 215 saddev[i].sa_qp = qp; 216 qp->q_ptr = (caddr_t)&saddev[i]; 217 WR(qp)->q_ptr = (caddr_t)&saddev[i]; 218 219 /* 220 * NOTE: should the ADMMIN or USRMIN minors change 221 * then so should the offset of 2 below 222 * Both USRMIN and ADMMIN are clone interfaces and 223 * therefore their minor numbers (0 and 1) are reserved. 224 */ 225 *devp = makedevice(getemajor(*devp), i + 2); 226 qprocson(qp); 227 return (0); 228 } 229 230 /* 231 * sadclose() - 232 * Clean up the data structures. 233 */ 234 /* ARGSUSED */ 235 static int 236 sadclose( 237 queue_t *qp, /* pointer to read queue */ 238 int flag, /* file open flags */ 239 cred_t *credp) /* user credentials */ 240 { 241 struct saddev *sadp; 242 243 qprocsoff(qp); 244 sadp = (struct saddev *)qp->q_ptr; 245 sadp->sa_qp = NULL; 246 sadp->sa_addr = NULL; 247 qp->q_ptr = NULL; 248 WR(qp)->q_ptr = NULL; 249 return (0); 250 } 251 252 /* 253 * sadwput() - 254 * Write side put procedure. 255 */ 256 static int 257 sadwput( 258 queue_t *qp, /* pointer to write queue */ 259 mblk_t *mp) /* message pointer */ 260 { 261 struct iocblk *iocp; 262 263 switch (mp->b_datap->db_type) { 264 case M_FLUSH: 265 if (*mp->b_rptr & FLUSHR) { 266 *mp->b_rptr &= ~FLUSHW; 267 qreply(qp, mp); 268 } else 269 freemsg(mp); 270 break; 271 272 case M_IOCTL: 273 iocp = (struct iocblk *)mp->b_rptr; 274 switch (SAD_CMD(iocp->ioc_cmd)) { 275 case SAD_CMD(SAD_SAP): 276 case SAD_CMD(SAD_GAP): 277 apush_ioctl(qp, mp); 278 break; 279 280 case SAD_VML: 281 vml_ioctl(qp, mp); 282 break; 283 284 default: 285 miocnak(qp, mp, 0, EINVAL); 286 break; 287 } 288 break; 289 290 case M_IOCDATA: 291 iocp = (struct iocblk *)mp->b_rptr; 292 switch (SAD_CMD(iocp->ioc_cmd)) { 293 case SAD_CMD(SAD_SAP): 294 case SAD_CMD(SAD_GAP): 295 apush_iocdata(qp, mp); 296 break; 297 298 case SAD_VML: 299 vml_iocdata(qp, mp); 300 break; 301 302 default: 303 cmn_err(CE_WARN, 304 "sadwput: invalid ioc_cmd in case M_IOCDATA: %d", 305 iocp->ioc_cmd); 306 freemsg(mp); 307 break; 308 } 309 break; 310 311 default: 312 freemsg(mp); 313 break; 314 } /* switch (db_type) */ 315 return (0); 316 } 317 318 /* 319 * apush_ioctl() - 320 * Handle the M_IOCTL messages associated with 321 * the autopush feature. 322 */ 323 static void 324 apush_ioctl( 325 queue_t *qp, /* pointer to write queue */ 326 mblk_t *mp) /* message pointer */ 327 { 328 struct iocblk *iocp; 329 struct saddev *sadp; 330 uint_t size; 331 332 iocp = (struct iocblk *)mp->b_rptr; 333 if (iocp->ioc_count != TRANSPARENT) { 334 miocnak(qp, mp, 0, EINVAL); 335 return; 336 } 337 if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) { 338 miocnak(qp, mp, 0, EINVAL); 339 return; 340 } 341 342 sadp = (struct saddev *)qp->q_ptr; 343 switch (SAD_CMD(iocp->ioc_cmd)) { 344 case SAD_CMD(SAD_SAP): 345 if (!(sadp->sa_flags & SADPRIV)) { 346 miocnak(qp, mp, 0, EPERM); 347 break; 348 } 349 /* FALLTHRU */ 350 351 case SAD_CMD(SAD_GAP): 352 sadp->sa_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr; 353 if (SAD_VER(iocp->ioc_cmd) == 1) 354 size = STRAPUSH_V1_LEN; 355 else 356 size = STRAPUSH_V0_LEN; 357 mcopyin(mp, (void *)GETSTRUCT, size, NULL); 358 qreply(qp, mp); 359 break; 360 361 default: 362 ASSERT(0); 363 miocnak(qp, mp, 0, EINVAL); 364 break; 365 } /* switch (ioc_cmd) */ 366 } 367 368 /* 369 * apush_iocdata() - 370 * Handle the M_IOCDATA messages associated with 371 * the autopush feature. 372 */ 373 static void 374 apush_iocdata( 375 queue_t *qp, /* pointer to write queue */ 376 mblk_t *mp) /* message pointer */ 377 { 378 int i, ret; 379 struct copyresp *csp; 380 struct strapush *sap; 381 struct autopush *ap, *ap_tmp; 382 struct saddev *sadp; 383 uint_t size; 384 dev_t dev; 385 386 csp = (struct copyresp *)mp->b_rptr; 387 if (csp->cp_rval) { /* if there was an error */ 388 freemsg(mp); 389 return; 390 } 391 if (mp->b_cont) { 392 /* 393 * sap needed only if mp->b_cont is set. first figure out 394 * the size of the sap structure and then prepare caller 395 * supplied data for access with miocpullup(). 396 */ 397 size = sizeof (struct strapush); 398 if (SAD_VER(csp->cp_cmd) == 0) 399 size -= sizeof (struct apdata); 400 if ((ret = miocpullup(mp, size)) != 0) { 401 miocnak(qp, mp, 0, ret); 402 return; 403 } 404 sap = (struct strapush *)mp->b_cont->b_rptr; 405 dev = makedevice(sap->sap_major, sap->sap_minor); 406 } 407 switch (SAD_CMD(csp->cp_cmd)) { 408 case SAD_CMD(SAD_SAP): 409 410 /* currently we only support one SAD_SAP command */ 411 if (((long)csp->cp_private) != GETSTRUCT) { 412 cmn_err(CE_WARN, 413 "apush_iocdata: cp_private bad in SAD_SAP: %p", 414 (void *)csp->cp_private); 415 miocnak(qp, mp, 0, EINVAL); 416 return; 417 } 418 419 switch (sap->sap_cmd) { 420 default: 421 miocnak(qp, mp, 0, EINVAL); 422 return; 423 case SAP_ONE: 424 case SAP_RANGE: 425 case SAP_ALL: 426 /* allocate and initialize a new config */ 427 ap = sad_ap_alloc(); 428 ap->ap_common = sap->sap_common; 429 if (SAD_VER(csp->cp_cmd) > 0) 430 ap->ap_anchor = sap->sap_anchor; 431 for (i = 0; i < MIN(sap->sap_npush, MAXAPUSH); i++) 432 (void) strncpy(ap->ap_list[i], 433 sap->sap_list[i], FMNAMESZ); 434 435 /* sanity check the request */ 436 if (((ret = sad_ap_verify(ap)) != 0) || 437 ((ret = valid_major(ap->ap_major)) != 0)) { 438 sad_ap_rele(ap); 439 miocnak(qp, mp, 0, ret); 440 return; 441 } 442 443 /* check for overlapping configs */ 444 mutex_enter(&sad_lock); 445 if ((ap_tmp = sad_ap_find(&ap->ap_common)) != NULL) { 446 /* already configured */ 447 mutex_exit(&sad_lock); 448 sad_ap_rele(ap_tmp); 449 sad_ap_rele(ap); 450 miocnak(qp, mp, 0, EEXIST); 451 return; 452 } 453 454 /* add the new config to our hash */ 455 sad_ap_insert(ap); 456 mutex_exit(&sad_lock); 457 miocack(qp, mp, 0, 0); 458 return; 459 460 case SAP_CLEAR: 461 /* sanity check the request */ 462 if (ret = valid_major(sap->sap_major)) { 463 miocnak(qp, mp, 0, ret); 464 return; 465 } 466 467 /* search for a matching config */ 468 if ((ap = sad_ap_find_by_dev(dev)) == NULL) { 469 /* no config found */ 470 miocnak(qp, mp, 0, ENODEV); 471 return; 472 } 473 474 /* 475 * If we matched a SAP_RANGE config 476 * the minor passed in must match the 477 * beginning of the range exactly. 478 */ 479 if ((ap->ap_type == SAP_RANGE) && 480 (ap->ap_minor != sap->sap_minor)) { 481 sad_ap_rele(ap); 482 miocnak(qp, mp, 0, ERANGE); 483 return; 484 } 485 486 /* 487 * If we matched a SAP_ALL config 488 * the minor passed in must be 0. 489 */ 490 if ((ap->ap_type == SAP_ALL) && 491 (sap->sap_minor != 0)) { 492 sad_ap_rele(ap); 493 miocnak(qp, mp, 0, EINVAL); 494 return; 495 } 496 497 /* 498 * make sure someone else hasn't already 499 * removed this config from the hash. 500 */ 501 mutex_enter(&sad_lock); 502 ap_tmp = sad_ap_find(&ap->ap_common); 503 if (ap_tmp != ap) { 504 mutex_exit(&sad_lock); 505 sad_ap_rele(ap_tmp); 506 sad_ap_rele(ap); 507 miocnak(qp, mp, 0, ENODEV); 508 return; 509 } else 510 511 /* remove the config from the hash and return */ 512 sad_ap_remove(ap); 513 mutex_exit(&sad_lock); 514 515 /* 516 * Release thrice, once for sad_ap_find_by_dev(), 517 * once for sad_ap_find(), and once to free. 518 */ 519 sad_ap_rele(ap); 520 sad_ap_rele(ap); 521 sad_ap_rele(ap); 522 miocack(qp, mp, 0, 0); 523 return; 524 } /* switch (sap_cmd) */ 525 /*NOTREACHED*/ 526 527 case SAD_CMD(SAD_GAP): 528 switch ((long)csp->cp_private) { 529 530 case GETSTRUCT: 531 /* sanity check the request */ 532 if (ret = valid_major(sap->sap_major)) { 533 miocnak(qp, mp, 0, ret); 534 return; 535 } 536 537 /* search for a matching config */ 538 if ((ap = sad_ap_find_by_dev(dev)) == NULL) { 539 /* no config found */ 540 miocnak(qp, mp, 0, ENODEV); 541 return; 542 } 543 544 /* copy out the contents of the config */ 545 sap->sap_common = ap->ap_common; 546 if (SAD_VER(csp->cp_cmd) > 0) 547 sap->sap_anchor = ap->ap_anchor; 548 for (i = 0; i < ap->ap_npush; i++) 549 (void) strcpy(sap->sap_list[i], ap->ap_list[i]); 550 for (; i < MAXAPUSH; i++) 551 bzero(sap->sap_list[i], FMNAMESZ + 1); 552 553 /* release our hold on the config */ 554 sad_ap_rele(ap); 555 556 /* copyout the results */ 557 if (SAD_VER(csp->cp_cmd) == 1) 558 size = STRAPUSH_V1_LEN; 559 else 560 size = STRAPUSH_V0_LEN; 561 562 sadp = (struct saddev *)qp->q_ptr; 563 mcopyout(mp, (void *)GETRESULT, size, sadp->sa_addr, 564 NULL); 565 qreply(qp, mp); 566 return; 567 case GETRESULT: 568 miocack(qp, mp, 0, 0); 569 return; 570 571 default: 572 cmn_err(CE_WARN, 573 "apush_iocdata: cp_private bad case SAD_GAP: %p", 574 (void *)csp->cp_private); 575 freemsg(mp); 576 return; 577 } /* switch (cp_private) */ 578 /*NOTREACHED*/ 579 default: /* can't happen */ 580 ASSERT(0); 581 freemsg(mp); 582 return; 583 } /* switch (cp_cmd) */ 584 } 585 586 /* 587 * vml_ioctl() - 588 * Handle the M_IOCTL message associated with a request 589 * to validate a module list. 590 */ 591 static void 592 vml_ioctl( 593 queue_t *qp, /* pointer to write queue */ 594 mblk_t *mp) /* message pointer */ 595 { 596 struct iocblk *iocp; 597 598 iocp = (struct iocblk *)mp->b_rptr; 599 if (iocp->ioc_count != TRANSPARENT) { 600 miocnak(qp, mp, 0, EINVAL); 601 return; 602 } 603 ASSERT(iocp->ioc_cmd == SAD_VML); 604 mcopyin(mp, (void *)GETSTRUCT, 605 SIZEOF_STRUCT(str_list, iocp->ioc_flag), NULL); 606 qreply(qp, mp); 607 } 608 609 /* 610 * vml_iocdata() - 611 * Handle the M_IOCDATA messages associated with 612 * a request to validate a module list. 613 */ 614 static void 615 vml_iocdata( 616 queue_t *qp, /* pointer to write queue */ 617 mblk_t *mp) /* message pointer */ 618 { 619 long i; 620 int nmods; 621 struct copyresp *csp; 622 struct str_mlist *lp; 623 STRUCT_HANDLE(str_list, slp); 624 struct saddev *sadp; 625 626 csp = (struct copyresp *)mp->b_rptr; 627 if (csp->cp_rval) { /* if there was an error */ 628 freemsg(mp); 629 return; 630 } 631 632 ASSERT(csp->cp_cmd == SAD_VML); 633 sadp = (struct saddev *)qp->q_ptr; 634 switch ((long)csp->cp_private) { 635 case GETSTRUCT: 636 STRUCT_SET_HANDLE(slp, csp->cp_flag, 637 (struct str_list *)mp->b_cont->b_rptr); 638 nmods = STRUCT_FGET(slp, sl_nmods); 639 if (nmods <= 0) { 640 miocnak(qp, mp, 0, EINVAL); 641 break; 642 } 643 sadp->sa_addr = (caddr_t)(uintptr_t)nmods; 644 645 mcopyin(mp, (void *)GETLIST, nmods * sizeof (struct str_mlist), 646 STRUCT_FGETP(slp, sl_modlist)); 647 qreply(qp, mp); 648 break; 649 650 case GETLIST: 651 lp = (struct str_mlist *)mp->b_cont->b_rptr; 652 for (i = 0; i < (long)sadp->sa_addr; i++, lp++) { 653 lp->l_name[FMNAMESZ] = '\0'; 654 if (fmodsw_find(lp->l_name, FMODSW_LOAD) == NULL) { 655 miocack(qp, mp, 0, 1); 656 return; 657 } 658 } 659 miocack(qp, mp, 0, 0); 660 break; 661 662 default: 663 cmn_err(CE_WARN, "vml_iocdata: invalid cp_private value: %p", 664 (void *)csp->cp_private); 665 freemsg(mp); 666 break; 667 } /* switch (cp_private) */ 668 } 669 670 /* 671 * Validate a major number and also verify if 672 * it is a STREAMS device. 673 * Return values: 0 if a valid STREAMS dev 674 * error code otherwise 675 */ 676 static int 677 valid_major(major_t major) 678 { 679 int ret = 0; 680 681 if (etoimajor(major) == -1) 682 return (EINVAL); 683 684 /* 685 * attempt to load the driver 'major' and verify that 686 * it is a STREAMS driver. 687 */ 688 if (ddi_hold_driver(major) == NULL) 689 return (EINVAL); 690 691 if (!STREAMSTAB(major)) 692 ret = ENOSTR; 693 694 ddi_rele_driver(major); 695 696 return (ret); 697 } 698