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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <stdarg.h> 31 #include <unistd.h> 32 #include <assert.h> 33 #include <string.h> 34 #include <limits.h> 35 #include <synch.h> 36 #include <libintl.h> 37 #include <errno.h> 38 #include <libdevinfo.h> 39 #include <sys/uio.h> 40 #include <sys/sysmacros.h> 41 #include <sys/types.h> 42 #include <stropts.h> 43 #include <sys/stream.h> 44 #include <fcntl.h> 45 #include <sys/stat.h> 46 #include <sys/mkdev.h> 47 48 #include <sys/param.h> 49 #include <sys/openpromio.h> 50 #include <sys/ttymuxuser.h> 51 52 #include "ttymux_rcm_impl.h" 53 #include "rcm_module.h" 54 55 #define TTYMUX_OFFLINE_ERR gettext("Resource is in use by") 56 #define TTYMUX_UNKNOWN_ERR gettext("Unknown Operation attempted") 57 #define TTYMUX_ONLINE_ERR gettext("Failed to connect under multiplexer") 58 #define TTYMUX_INVALID_ERR gettext("Invalid Operation on this resource") 59 #define TTYMUX_OFFLINE_FAIL gettext("Failed to disconnect from multiplexer") 60 #define TTYMUX_MEMORY_ERR gettext("TTYMUX: strdup failure\n") 61 62 63 static int msglvl = 6; /* print messages less than this level */ 64 #define TEST(cond, stmt) { if (cond) stmt; } 65 #define _msg(lvl, args) TEST(msglvl > (lvl), trace args) 66 67 static int oflags = O_EXCL|O_RDWR|O_NONBLOCK|O_NOCTTY; 68 static dev_t cn_dev = NODEV; 69 static rsrc_t *cn_rsrc = NULL; 70 static rsrc_t cache_head; 71 static rsrc_t cache_tail; 72 static mutex_t cache_lock; 73 static char muxctl[PATH_MAX] = {0}; 74 static char muxcon[PATH_MAX] = {0}; 75 static int muxfd; 76 static boolean_t register_rsrcs; 77 78 /* module interface routines */ 79 static int tty_register(rcm_handle_t *); 80 static int tty_unregister(rcm_handle_t *); 81 static int tty_getinfo(rcm_handle_t *, char *, id_t, uint_t, char **, 82 char **, nvlist_t *, rcm_info_t **); 83 static int tty_suspend(rcm_handle_t *, char *, id_t, 84 timespec_t *, uint_t, char **, rcm_info_t **); 85 static int tty_resume(rcm_handle_t *, char *, id_t, uint_t, char **, 86 rcm_info_t **); 87 static int tty_offline(rcm_handle_t *, char *, id_t, uint_t, char **, 88 rcm_info_t **); 89 static int tty_online(rcm_handle_t *, char *, id_t, uint_t, char **, 90 rcm_info_t **); 91 static int tty_remove(rcm_handle_t *, char *, id_t, uint_t, char **, 92 rcm_info_t **); 93 94 static int get_devpath(char *, char **, dev_t *); 95 96 /* 97 * Module-Private data 98 */ 99 static struct rcm_mod_ops tty_ops = { 100 RCM_MOD_OPS_VERSION, 101 tty_register, 102 tty_unregister, 103 tty_getinfo, 104 tty_suspend, 105 tty_resume, 106 tty_offline, 107 tty_online, 108 tty_remove, 109 NULL, 110 NULL 111 }; 112 113 /*PRINTFLIKE1*/ 114 static void 115 trace(char *fmt, ...) 116 { 117 va_list args; 118 char buf[256]; 119 int sz; 120 121 va_start(args, fmt); 122 sz = vsnprintf(buf, sizeof (buf), fmt, args); 123 va_end(args); 124 125 if (sz < 0) 126 rcm_log_message(RCM_TRACE1, 127 _("TTYMUX: vsnprintf parse error\n")); 128 else if (sz > sizeof (buf)) { 129 char *b = malloc(sz + 1); 130 131 if (b != NULL) { 132 va_start(args, fmt); 133 sz = vsnprintf(b, sz + 1, fmt, args); 134 va_end(args); 135 if (sz > 0) 136 rcm_log_message(RCM_TRACE1, _("%s"), b); 137 free(b); 138 } 139 } else { 140 rcm_log_message(RCM_TRACE1, _("%s"), buf); 141 } 142 } 143 144 /* 145 * CACHE MANAGEMENT 146 * Resources managed by this module are stored in a list of rsrc_t 147 * structures. 148 */ 149 150 /* 151 * cache_lookup() 152 * 153 * Get a cache node for a resource. Call with cache lock held. 154 */ 155 static rsrc_t * 156 cache_lookup(const char *resource) 157 { 158 rsrc_t *rsrc; 159 rsrc = cache_head.next; 160 while (rsrc != &cache_tail) { 161 if (rsrc->id && strcmp(resource, rsrc->id) == 0) { 162 return (rsrc); 163 } 164 rsrc = rsrc->next; 165 } 166 return (NULL); 167 } 168 169 /* 170 * Get a cache node for a minor node. Call with cache lock held. 171 */ 172 static rsrc_t * 173 cache_lookup_bydevt(dev_t devt) 174 { 175 rsrc_t *rsrc; 176 rsrc = cache_head.next; 177 while (rsrc != &cache_tail) { 178 if (rsrc->dev == devt) 179 return (rsrc); 180 rsrc = rsrc->next; 181 } 182 return (NULL); 183 } 184 185 /* 186 * free_node() 187 * 188 * Free a node. Make sure it isn't in the list! 189 */ 190 static void 191 free_node(rsrc_t *node) 192 { 193 if (node) { 194 if (node->id) { 195 free(node->id); 196 } 197 free(node); 198 } 199 } 200 201 /* 202 * cache_insert() 203 * 204 * Call with the cache_lock held. 205 */ 206 static void 207 cache_insert(rsrc_t *node) 208 { 209 /* insert at the head for best performance */ 210 node->next = cache_head.next; 211 node->prev = &cache_head; 212 213 node->next->prev = node; 214 node->prev->next = node; 215 } 216 217 /* 218 * cache_create() 219 * 220 * Call with the cache_lock held. 221 */ 222 static rsrc_t * 223 cache_create(const char *resource, dev_t dev) 224 { 225 rsrc_t *rsrc = malloc(sizeof (rsrc_t)); 226 227 if (rsrc != NULL) { 228 if ((rsrc->id = strdup(resource)) != NULL) { 229 rsrc->dev = dev; 230 rsrc->flags = 0; 231 rsrc->dependencies = NULL; 232 cache_insert(rsrc); 233 } else { 234 free(rsrc); 235 rsrc = NULL; 236 } 237 } else { 238 _msg(0, ("TTYMUX: malloc failure for resource %s.\n", 239 resource)); 240 } 241 return (rsrc); 242 } 243 244 /* 245 * cache_get() 246 * 247 * Call with the cache_lock held. 248 */ 249 static rsrc_t * 250 cache_get(const char *resource) 251 { 252 rsrc_t *rsrc = cache_lookup(resource); 253 if (rsrc == NULL) { 254 dev_t dev; 255 256 (void) get_devpath((char *)resource, NULL, &dev); 257 rsrc = cache_create(resource, dev); 258 } 259 return (rsrc); 260 } 261 262 /* 263 * cache_remove() 264 * 265 * Call with the cache_lock held. 266 */ 267 static void 268 cache_remove(rsrc_t *node) 269 { 270 node->next->prev = node->prev; 271 node->prev->next = node->next; 272 node->next = NULL; 273 node->prev = NULL; 274 } 275 276 /* 277 * Open a file identified by fname with the given open flags. 278 * If the request is to open a file with exclusive access and the open 279 * fails then backoff exponentially and then retry the open. 280 * Do not wait for longer than about a second (since this may be an rcm 281 * framework thread). 282 */ 283 static int 284 open_file(char *fname, int flags) 285 { 286 int fd, cnt; 287 struct timespec ts; 288 289 if ((flags & O_EXCL) == 0) 290 return (open(fname, flags)); 291 292 ts.tv_sec = 0; 293 ts.tv_nsec = 16000000; /* 16 milliseconds */ 294 295 for (cnt = 0; cnt < 5 && (fd = open(fname, flags)) == -1; cnt++) { 296 (void) nanosleep(&ts, NULL); 297 ts.tv_nsec *= 2; 298 } 299 return (fd); 300 } 301 302 /* 303 * No-op for creating an association between a pair of resources. 304 */ 305 /*ARGSUSED*/ 306 static int 307 nullconnect(link_t *link) 308 { 309 return (0); 310 } 311 312 /* 313 * No-op for destroying an association between a pair of resources. 314 */ 315 /*ARGSUSED*/ 316 static int 317 nulldisconnect(link_t *link) 318 { 319 return (0); 320 } 321 322 /* 323 * Record an actual or desired association between two resources 324 * identified by their rsrc_t structures. 325 */ 326 static link_t * 327 add_dependency(rsrc_t *user, rsrc_t *used) 328 { 329 link_t *linkhead; 330 link_t *link; 331 332 if (user == NULL || used == NULL) 333 return (NULL); 334 335 if (user->id && used->id && strcmp(user->id, used->id) == 0) { 336 _msg(2, ("TTYMUX: attempt to connect devices created by " 337 "the same driver\n")); 338 return (NULL); 339 } 340 341 /* 342 * Search for all resources that this resource user is depending 343 * upon. 344 */ 345 linkhead = user->dependencies; 346 for (link = linkhead; link != NULL; link = link->next) { 347 /* 348 * Does the using resource already depends on the used 349 * resource 350 */ 351 if (link->used == used) 352 return (link); 353 } 354 355 link = malloc(sizeof (link_t)); 356 357 if (link == NULL) { 358 rcm_log_message(RCM_ERROR, _("TTYMUX: Out of memory\n")); 359 return (NULL); 360 } 361 362 _msg(6, ("TTYMUX: New link user %s used %s\n", user->id, used->id)); 363 364 link->user = user; 365 link->used = used; 366 link->linkid = 0; 367 link->state = UNKNOWN; 368 link->flags = 0; 369 370 link->connect = nullconnect; 371 link->disconnect = nulldisconnect; 372 link->next = linkhead; 373 374 user->dependencies = link; 375 376 return (link); 377 } 378 379 /* 380 * Send an I_STR stream ioctl to a device 381 */ 382 static int 383 istrioctl(int fd, int cmd, void *data, int datalen, int *bytes) { 384 struct strioctl ios; 385 int rval; 386 387 ios.ic_timout = 0; /* use the default */ 388 ios.ic_cmd = cmd; 389 ios.ic_dp = (char *)data; 390 ios.ic_len = datalen; 391 392 rval = ioctl(fd, I_STR, (char *)&ios); 393 if (bytes) 394 *bytes = ios.ic_len; 395 return (rval); 396 } 397 398 /* 399 * Streams link the driver identified by fd underneath a mux 400 * identified by ctrl_fd. 401 */ 402 static int 403 plink(int ctrl_fd, int fd) 404 { 405 int linkid; 406 407 /* 408 * pop any modules off the lower stream. 409 */ 410 while (ioctl(fd, I_POP, 0) == 0) 411 ; 412 413 if ((linkid = ioctl(ctrl_fd, I_PLINK, fd)) < 0) 414 rcm_log_message(RCM_ERROR, 415 _("TTYMUX: I_PLINK error %d.\n"), errno); 416 return (linkid); 417 } 418 419 /* 420 * Streams unlink the STREAM identified by linkid from a mux 421 * identified by ctrl_fd. 422 */ 423 static int 424 punlink(int ctrl_fd, int linkid) 425 { 426 if (ioctl(ctrl_fd, I_PUNLINK, linkid) < 0) 427 return (errno); 428 else 429 return (0); 430 } 431 432 /* 433 * Connect a pair of resources by establishing the dependency association. 434 * Only works for devices that support the TTYMUX ioctls. 435 */ 436 static int 437 mux_connect(link_t *link) 438 { 439 int lfd; 440 int rv; 441 ttymux_assoc_t as; 442 uint8_t ioflags; 443 444 _msg(6, ("TTYMUX: mux_connect (%ld:%ld<->%ld:%ld %s <-> %s\n", 445 major(link->user->dev), minor(link->user->dev), 446 major(link->used->dev), minor(link->used->dev), 447 link->user->id, link->used->id)); 448 449 _msg(12, ("TTYMUX: struct size = %d (plen %d)\n", 450 sizeof (as), PATH_MAX)); 451 452 if (link->user->dev == NODEV || link->used->dev == NODEV) { 453 /* 454 * One of the resources in the association is not 455 * present (wait for the online notification before 456 * attempting to establish the dependency association. 457 */ 458 return (EAGAIN); 459 } 460 if (major(link->user->dev) == major(link->used->dev)) { 461 _msg(2, ("TTYMUX: attempt to link devices created by " 462 "the same driver\n")); 463 return (EINVAL); 464 } 465 /* 466 * Explicitly check for attempts to plumb the system console - 467 * required becuase not all serial devices support the 468 * O_EXCL open flag. 469 */ 470 if (link->used->dev == cn_dev) { 471 rcm_log_message(RCM_WARNING, _("TTYMUX: Request to link the " 472 " system console under another device not allowed!\n")); 473 474 return (EPERM); 475 } 476 477 /* 478 * Ensure that the input/output mode of the dependent is reasonable 479 */ 480 if ((ioflags = link->flags & FORIO) == 0) 481 ioflags = FORIO; 482 483 /* 484 * Open each resource participating in the association. 485 */ 486 lfd = open(link->used->id, O_EXCL|O_RDWR|O_NONBLOCK|O_NOCTTY); 487 if (lfd == -1) { 488 if (errno == EBUSY) { 489 rcm_log_message(RCM_WARNING, _("TTYMUX: device %s is " 490 " busy - " " cannot connect to %s\n"), 491 link->used->id, link->user->id); 492 } else { 493 rcm_log_message(RCM_WARNING, 494 _("TTYMUX: open error %d for device %s\n"), 495 errno, link->used->id); 496 } 497 return (errno); 498 } 499 /* 500 * Note: Issuing the I_PLINK and TTYMUX_ASSOC request on the 'using' 501 * resource is more generic: 502 * muxfd = open(link->user->id, oflags); 503 * However using the ctl (MUXCTLLINK) node means that any current opens 504 * on the 'using' resource are uneffected. 505 */ 506 507 /* 508 * Figure out if the 'used' resource is already associated with 509 * some resource - if so tell the caller to try again later. 510 * More generally if any user or kernel thread has the resource 511 * open then the association should not be made. 512 * The ttymux driver makes this check (but it should be done here). 513 */ 514 as.ttymux_linkid = 0; 515 as.ttymux_ldev = link->used->dev; 516 517 if (istrioctl(muxfd, TTYMUX_GETLINK, 518 (void *)&as, sizeof (as), NULL) == 0) { 519 520 _msg(7, ("TTYMUX: %ld:%ld (%d) (udev %ld:%ld) already linked\n", 521 major(as.ttymux_ldev), minor(as.ttymux_ldev), 522 as.ttymux_linkid, major(as.ttymux_udev), 523 minor(as.ttymux_udev))); 524 link->linkid = as.ttymux_linkid; 525 if (as.ttymux_udev != NODEV) { 526 (void) close(lfd); 527 return (EAGAIN); 528 } 529 } 530 531 /* 532 * Now link and associate the used resource under the using resource. 533 */ 534 as.ttymux_udev = link->user->dev; 535 as.ttymux_ldev = link->used->dev; 536 as.ttymux_tag = 0ul; 537 as.ttymux_ioflag = ioflags; 538 539 _msg(6, ("TTYMUX: connecting %ld:%ld to %ld:%ld\n", 540 major(as.ttymux_ldev), minor(as.ttymux_ldev), 541 major(as.ttymux_udev), minor(as.ttymux_udev))); 542 543 if (as.ttymux_udev == cn_dev) { 544 struct termios tc; 545 546 if (ioctl(lfd, TCGETS, &tc) != -1) { 547 tc.c_cflag |= CREAD; 548 if (ioctl(lfd, TCSETSW, &tc) == -1) { 549 rcm_log_message(RCM_WARNING, 550 _("TTYMUX: error %d whilst enabling the " 551 "receiver on device %d:%d\n"), 552 errno, major(as.ttymux_ldev), 553 minor(as.ttymux_ldev)); 554 } 555 } 556 } 557 558 if (as.ttymux_linkid <= 0 && (as.ttymux_linkid = 559 plink(muxfd, lfd)) <= 0) { 560 rcm_log_message(RCM_WARNING, 561 _("TTYMUX: Link error %d for device %s\n"), 562 errno, link->used->id); 563 rv = errno; 564 goto out; 565 } 566 link->linkid = as.ttymux_linkid; 567 568 _msg(6, ("TTYMUX: associating\n")); 569 if (istrioctl(muxfd, TTYMUX_ASSOC, (void *)&as, sizeof (as), 0) != 0) { 570 rv = errno; 571 goto out; 572 } 573 _msg(6, ("TTYMUX: Succesfully connected %ld:%ld to %ld:%ld\n", 574 major(as.ttymux_ldev), minor(as.ttymux_ldev), 575 major(as.ttymux_udev), minor(as.ttymux_udev))); 576 link->state = CONNECTED; 577 (void) close(lfd); 578 return (0); 579 out: 580 rcm_log_message(RCM_WARNING, 581 _("TTYMUX: Error [%d] connecting %d:%d to %d:%d\n"), 582 rv, major(as.ttymux_ldev), minor(as.ttymux_ldev), 583 major(as.ttymux_udev), minor(as.ttymux_udev)); 584 585 (void) close(lfd); 586 if (as.ttymux_linkid > 0) { 587 /* 588 * There was an error so unwind the I_PLINK step 589 */ 590 if (punlink(muxfd, as.ttymux_linkid) != 0) 591 rcm_log_message(RCM_WARNING, 592 _("TTYMUX: Unlink error %d (%s).\n"), 593 errno, link->used->id); 594 } 595 return (rv); 596 } 597 598 /* 599 * Disconnect a pair of resources by destroying the dependency association. 600 * Only works for devices that support the TTYMUX ioctls. 601 */ 602 static int 603 mux_disconnect(link_t *link) 604 { 605 int rv; 606 ttymux_assoc_t as; 607 608 _msg(6, ("TTYMUX: mux_disconnect %s<->%s (%ld:%ld<->%ld:%ld)\n", 609 link->user->id, link->used->id, 610 major(link->user->dev), minor(link->user->dev), 611 major(link->used->dev), minor(link->used->dev))); 612 613 as.ttymux_ldev = link->used->dev; 614 615 if (istrioctl(muxfd, TTYMUX_GETLINK, 616 (void *)&as, sizeof (as), NULL) != 0) { 617 618 _msg(1, ("TTYMUX: %ld:%ld not linked [err %d]\n", 619 major(link->used->dev), minor(link->used->dev), errno)); 620 return (0); 621 622 /* 623 * Do not disassociate console resources - simply 624 * unlink them so that they remain persistent. 625 */ 626 } else if (as.ttymux_udev != cn_dev && 627 istrioctl(muxfd, TTYMUX_DISASSOC, (void *)&as, 628 sizeof (as), 0) == -1) { 629 630 rv = errno; 631 rcm_log_message(RCM_WARNING, 632 _("TTYMUX: Dissassociate error %d for %s\n"), 633 rv, link->used->id); 634 635 } else if (punlink(muxfd, as.ttymux_linkid) != 0) { 636 rv = errno; 637 rcm_log_message(RCM_WARNING, 638 _("TTYMUX: Error %d unlinking %d:%d\n"), 639 errno, major(link->used->dev), minor(link->used->dev)); 640 } else { 641 _msg(6, ("TTYMUX: %s<->%s disconnected.\n", 642 link->user->id, link->used->id)); 643 644 link->state = DISCONNECTED; 645 link->linkid = 0; 646 rv = 0; 647 } 648 return (rv); 649 } 650 651 /* PESISTENCY */ 652 653 /* 654 * Given a special device file system path return the /devices path 655 * and/or the device number (dev_t) of the device. 656 */ 657 static int 658 get_devpath(char *dev, char **cname, dev_t *devt) 659 { 660 struct stat sb; 661 662 if (cname != NULL) 663 *cname = NULL; 664 665 if (devt != NULL) 666 *devt = NODEV; 667 668 if (lstat(dev, &sb) < 0) { 669 return (errno); 670 } else if ((sb.st_mode & S_IFMT) == S_IFLNK) { 671 int lsz; 672 char linkbuf[PATH_MAX+1]; 673 674 if (stat(dev, &sb) < 0) 675 return (errno); 676 677 lsz = readlink(dev, linkbuf, PATH_MAX); 678 679 if (lsz <= 0) 680 return (ENODEV); 681 linkbuf[lsz] = '\0'; 682 dev = strstr(linkbuf, "/devices"); 683 if (dev == NULL) 684 return (ENODEV); 685 } 686 687 if (cname != NULL) 688 *cname = strdup(dev); 689 690 if (devt != NULL) 691 *devt = sb.st_rdev; 692 693 return (0); 694 } 695 696 /* 697 * See routine locate_node 698 */ 699 static int 700 locate_dev(di_node_t node, di_minor_t minor, void *arg) 701 { 702 char *devfspath; 703 char resource[PATH_MAX]; 704 rsrc_t *rsrc; 705 706 if (di_minor_devt(minor) != (dev_t)arg) 707 return (DI_WALK_CONTINUE); 708 709 if ((devfspath = di_devfs_path(node)) == NULL) 710 return (DI_WALK_TERMINATE); 711 712 if (snprintf(resource, sizeof (resource), "/devices%s:%s", 713 devfspath, di_minor_name(minor)) > sizeof (resource)) { 714 di_devfs_path_free(devfspath); 715 return (DI_WALK_TERMINATE); 716 } 717 718 di_devfs_path_free(devfspath); 719 720 rsrc = cache_lookup(resource); 721 if (rsrc == NULL && 722 (rsrc = cache_create(resource, di_minor_devt(minor))) == NULL) 723 return (DI_WALK_TERMINATE); 724 725 rsrc->dev = di_minor_devt(minor); 726 rsrc->flags |= PRESENT; 727 rsrc->flags &= ~UNKNOWN; 728 return (DI_WALK_TERMINATE); 729 } 730 731 /* 732 * Find a devinfo node that matches the device argument (dev). 733 * This is an expensive search of the whole device tree! 734 */ 735 static rsrc_t * 736 locate_node(dev_t dev, di_node_t *root) 737 { 738 rsrc_t *rsrc; 739 740 assert(root != NULL); 741 742 if ((rsrc = cache_lookup_bydevt(dev)) != NULL) 743 return (rsrc); 744 745 (void) di_walk_minor(*root, NULL, 0, (void*)dev, locate_dev); 746 747 return (cache_lookup_bydevt(dev)); 748 } 749 750 /* 751 * Search for any existing dependency relationships managed by this 752 * RCM module. 753 */ 754 static int 755 probe_dependencies() 756 { 757 ttymux_assocs_t links; 758 ttymux_assoc_t *asp; 759 int cnt, n; 760 rsrc_t *ruser; 761 rsrc_t *used; 762 link_t *link; 763 di_node_t root; 764 765 cnt = istrioctl(muxfd, TTYMUX_LIST, (void *)0, 0, 0); 766 767 _msg(8, ("TTYMUX: Probed %d links [%d]\n", cnt, errno)); 768 769 if (cnt <= 0) 770 return (0); 771 772 if ((links.ttymux_assocs = calloc(cnt, sizeof (ttymux_assoc_t))) == 0) 773 return (EAGAIN); 774 775 links.ttymux_nlinks = cnt; 776 777 n = istrioctl(muxfd, TTYMUX_LIST, (void *)&links, sizeof (links), 0); 778 779 if (n == -1) { 780 _msg(2, ("TTYMUX: Probe error %s\n", strerror(errno))); 781 free(links.ttymux_assocs); 782 return (0); 783 } 784 785 asp = (ttymux_assoc_t *)links.ttymux_assocs; 786 787 if ((root = di_init("/", DINFOSUBTREE|DINFOMINOR)) == DI_NODE_NIL) 788 return (errno); 789 790 (void) mutex_lock(&cache_lock); 791 for (; cnt--; asp++) { 792 _msg(7, ("TTYMUX: Probed: %ld %ld %ld:%ld <-> %ld:%ld\n", 793 asp->ttymux_udev, asp->ttymux_ldev, 794 major(asp->ttymux_udev), minor(asp->ttymux_udev), 795 major(asp->ttymux_ldev), minor(asp->ttymux_ldev))); 796 /* 797 * The TTYMUX_LIST ioctl can return links relating 798 * to potential devices. Such devices are identified 799 * in the path field. 800 */ 801 if (asp->ttymux_ldev == NODEV) { 802 char buf[PATH_MAX]; 803 804 if (asp->ttymux_path == NULL || 805 *asp->ttymux_path != '/') 806 continue; 807 808 if (snprintf(buf, sizeof (buf), "/devices%s", 809 asp->ttymux_path) > sizeof (buf)) 810 continue; 811 812 used = cache_get(buf); 813 } else { 814 used = locate_node(asp->ttymux_ldev, &root); 815 } 816 if ((ruser = locate_node(asp->ttymux_udev, &root)) == NULL) { 817 _msg(7, ("TTYMUX: Probe: %ld:%ld not present\n", 818 major(asp->ttymux_udev), minor(asp->ttymux_udev))); 819 continue; 820 } 821 if (used == NULL) { 822 _msg(7, ("TTYMUX: Probe: %ld:%ld not present\n", 823 major(asp->ttymux_ldev), minor(asp->ttymux_ldev))); 824 continue; 825 } 826 _msg(6, ("TTYMUX: Probe: Restore %s <-> %s (id %d)\n", 827 ruser->id, used->id, asp->ttymux_linkid)); 828 829 link = add_dependency(ruser, used); 830 831 if (link != NULL) { 832 link->flags = (uint_t)asp->ttymux_ioflag; 833 link->linkid = asp->ttymux_linkid; 834 link->state = CONNECTED; 835 link->connect = mux_connect; 836 link->disconnect = mux_disconnect; 837 } 838 } 839 di_fini(root); 840 (void) mutex_unlock(&cache_lock); 841 free(links.ttymux_assocs); 842 return (0); 843 } 844 845 /* 846 * A resource has become available. Re-establish any associations involving 847 * the resource. 848 */ 849 static int 850 rsrc_available(rsrc_t *rsrc) 851 { 852 link_t *link; 853 rsrc_t *rs; 854 855 if (rsrc->dev == NODEV) { 856 /* 857 * Now that the resource is present obtain its device number. 858 * For this to work the node must be present in the /devices 859 * tree (see devfsadm(1M) or drvconfig(1M)). 860 * We do not use libdevinfo because the node must be present 861 * under /devices for the connect step below to work 862 * (the node needs to be opened). 863 */ 864 (void) get_devpath(rsrc->id, NULL, &rsrc->dev); 865 if (rsrc->dev == NODEV) { 866 _msg(4, 867 ("Device node %s does not exist\n", rsrc->id)); 868 /* 869 * What does RCM do with failed online notifications. 870 */ 871 return (RCM_FAILURE); 872 } 873 } 874 for (rs = cache_head.next; rs != &cache_tail; rs = rs->next) { 875 for (link = rs->dependencies; 876 link != NULL; 877 link = link->next) { 878 if (link->user == rsrc || link->used == rsrc) { 879 _msg(6, ("TTYMUX: re-connect\n")); 880 (void) link->connect(link); 881 } 882 } 883 } 884 return (RCM_SUCCESS); 885 } 886 887 /* 888 * A resource is going away. Tear down any associations involving 889 * the resource. 890 */ 891 static int 892 rsrc_unavailable(rsrc_t *rsrc) 893 { 894 link_t *link; 895 rsrc_t *rs; 896 897 for (rs = cache_head.next; rs != &cache_tail; rs = rs->next) { 898 for (link = rs->dependencies; 899 link != NULL; 900 link = link->next) { 901 if (link->user == rsrc || link->used == rsrc) { 902 _msg(6, ("TTYMUX: unavailable %s %s\n", 903 link->user->id, link->used->id)); 904 (void) link->disconnect(link); 905 } 906 } 907 } 908 909 return (RCM_SUCCESS); 910 } 911 912 /* 913 * Find any resources that are using a given resource (identified by 914 * the rsrc argument). The search begins after the resource identified 915 * by the next argument. If next is NULL start at the first resource 916 * in this RCM modules resource list. If the redundancy argument is 917 * greater than zero then a resource which uses rsrc will only be 918 * returned if it is associated with >= redundancy dependents. 919 * 920 * Thus, provided that the caller keeps the list locked he can iterate 921 * through all the resources in the cache that depend upon rsrc. 922 */ 923 static rsrc_t * 924 get_next_user(rsrc_t *next, rsrc_t *rsrc, int redundancy) 925 { 926 rsrc_t *src; 927 link_t *link; 928 int cnt = 0; 929 boolean_t inuse; 930 931 src = (next != NULL) ? next->next : cache_head.next; 932 933 while (src != &cache_tail) { 934 inuse = B_FALSE; 935 936 for (link = src->dependencies, cnt = 0; 937 link != NULL; 938 link = link->next) { 939 940 if (link->state == CONNECTED) 941 cnt++; 942 943 if (link->used == rsrc) 944 inuse = B_TRUE; 945 } 946 if (inuse == B_TRUE && 947 (redundancy <= 0 || cnt == redundancy)) { 948 return (src); 949 } 950 951 src = src->next; 952 } 953 954 _msg(8, ("TTYMUX: count_users(%s) res %d.\n", rsrc->id, cnt)); 955 return (NULL); 956 } 957 958 /* 959 * Common handler for RCM notifications. 960 */ 961 /*ARGSUSED*/ 962 static int 963 rsrc_change_common(rcm_handle_t *hd, int op, const char *rsrcid, uint_t flag, 964 char **reason, rcm_info_t **dependent_reason, void *arg) 965 { 966 rsrc_t *rsrc, *user; 967 int rv, len; 968 char *tmp = NULL; 969 970 (void) mutex_lock(&cache_lock); 971 rsrc = cache_lookup(rsrcid); 972 if (rsrc == NULL) { 973 /* shouldn't happen because rsrc has been registered */ 974 (void) mutex_unlock(&cache_lock); 975 return (RCM_SUCCESS); 976 } 977 if ((muxfd = open_file(muxctl, oflags)) == -1) { 978 rcm_log_message(RCM_ERROR, _("TTYMUX: %s unavailable: %s\n"), 979 muxctl, strerror(errno)); 980 (void) mutex_unlock(&cache_lock); 981 return (RCM_SUCCESS); 982 } 983 switch (op) { 984 985 case TTYMUX_SUSPEND: 986 rv = RCM_FAILURE; 987 _msg(4, ("TTYMUX: SUSPEND %s operation refused.\n", 988 rsrc->id)); 989 if ((*reason = strdup(TTYMUX_INVALID_ERR)) == NULL) { 990 rcm_log_message(RCM_ERROR, TTYMUX_MEMORY_ERR); 991 } 992 break; 993 994 case TTYMUX_REMOVE: 995 rsrc->flags |= UNKNOWN; 996 rsrc->flags &= ~(PRESENT | REGISTERED); 997 rv = RCM_SUCCESS; 998 break; 999 1000 case TTYMUX_OFFLINE: 1001 user = get_next_user(NULL, rsrc, 1); 1002 if (flag & RCM_QUERY) { 1003 rv = ((flag & RCM_FORCE) || (user == NULL)) ? 1004 RCM_SUCCESS : RCM_FAILURE; 1005 if (rv == RCM_FAILURE) { 1006 tmp = TTYMUX_OFFLINE_ERR; 1007 assert(tmp != NULL); 1008 len = strlen(tmp) + strlen(user->id) + 2; 1009 if ((*reason = (char *)malloc(len)) != NULL) { 1010 (void) snprintf(*reason, len, 1011 "%s %s", tmp, user->id); 1012 } else { 1013 rcm_log_message(RCM_ERROR, TTYMUX_MEMORY_ERR); 1014 } 1015 } 1016 1017 } else if (flag & RCM_FORCE) { 1018 rv = rsrc_unavailable(rsrc); 1019 1020 if (rv == RCM_FAILURE) { 1021 if ((*reason = strdup(TTYMUX_OFFLINE_FAIL)) == 1022 NULL) { 1023 rcm_log_message(RCM_ERROR, 1024 TTYMUX_MEMORY_ERR); 1025 } 1026 } 1027 1028 } else if (user != NULL) { 1029 rv = RCM_FAILURE; 1030 tmp = TTYMUX_OFFLINE_ERR; 1031 assert(tmp != NULL); 1032 len = strlen(tmp) + strlen(user->id) + 2; 1033 if ((*reason = (char *)malloc(len)) != NULL) { 1034 (void) snprintf(*reason, len, 1035 "%s %s", tmp, user->id); 1036 } else { 1037 rcm_log_message(RCM_ERROR, TTYMUX_MEMORY_ERR); 1038 } 1039 1040 } else { 1041 rv = rsrc_unavailable(rsrc); 1042 if (rv == RCM_FAILURE) { 1043 if ((*reason = strdup(TTYMUX_OFFLINE_FAIL)) == 1044 NULL) { 1045 rcm_log_message(RCM_ERROR, 1046 TTYMUX_MEMORY_ERR); 1047 } 1048 } 1049 } 1050 1051 if (rv == RCM_FAILURE) { 1052 _msg(4, ("TTYMUX: OFFLINE %s operation refused.\n", 1053 rsrc->id)); 1054 1055 } else { 1056 _msg(4, ("TTYMUX: OFFLINE %s res %d.\n", rsrc->id, rv)); 1057 } 1058 break; 1059 1060 case TTYMUX_RESUME: 1061 rv = RCM_FAILURE; 1062 _msg(4, ("TTYMUX: RESUME %s operation refused.\n", 1063 rsrc->id)); 1064 if ((*reason = strdup(TTYMUX_INVALID_ERR)) == NULL) { 1065 rcm_log_message(RCM_ERROR, TTYMUX_MEMORY_ERR); 1066 } 1067 break; 1068 1069 case TTYMUX_ONLINE: 1070 _msg(4, ("TTYMUX: ONLINE %s res %d.\n", rsrc->id, rv)); 1071 rv = rsrc_available(rsrc); 1072 if (rv == RCM_FAILURE) { 1073 if ((*reason = strdup(TTYMUX_ONLINE_ERR)) == NULL) { 1074 rcm_log_message(RCM_ERROR, TTYMUX_MEMORY_ERR); 1075 } 1076 } 1077 break; 1078 default: 1079 rv = RCM_FAILURE; 1080 if ((*reason = strdup(TTYMUX_UNKNOWN_ERR)) == NULL) { 1081 rcm_log_message(RCM_ERROR, TTYMUX_MEMORY_ERR); 1082 } 1083 } 1084 1085 (void) close(muxfd); 1086 (void) mutex_unlock(&cache_lock); 1087 return (rv); 1088 } 1089 1090 static boolean_t 1091 find_mux_nodes(char *drv) 1092 { 1093 di_node_t root, node; 1094 di_minor_t dim; 1095 char *devfspath; 1096 char muxctlname[] = "ctl"; 1097 char muxconname[] = "con"; 1098 int nminors = 0; 1099 1100 (void) strcpy(muxctl, MUXCTLLINK); 1101 (void) strcpy(muxcon, MUXCONLINK); 1102 cn_rsrc = NULL; 1103 1104 if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 1105 rcm_log_message(RCM_WARNING, _("di_init error\n")); 1106 return (B_FALSE); 1107 } 1108 1109 node = di_drv_first_node(drv, root); 1110 if (node == DI_NODE_NIL) { 1111 _msg(4, ("no node for %s\n", drv)); 1112 di_fini(root); 1113 return (B_FALSE); 1114 } 1115 /* 1116 * If the device is not a prom node do not continue. 1117 */ 1118 if (di_nodeid(node) != DI_PROM_NODEID) { 1119 di_fini(root); 1120 return (B_FALSE); 1121 } 1122 if ((devfspath = di_devfs_path(node)) == NULL) { 1123 di_fini(root); 1124 return (B_FALSE); 1125 } 1126 1127 /* 1128 * Loop through all the minor nodes the driver (drv) looking 1129 * for the ctl node (this is the device on which 1130 * to issue ioctls). 1131 */ 1132 dim = DI_MINOR_NIL; 1133 while ((dim = di_minor_next(node, dim)) != DI_MINOR_NIL) { 1134 1135 _msg(7, ("MUXNODES: minor %s\n", di_minor_name(dim))); 1136 1137 if (strcmp(di_minor_name(dim), muxctlname) == 0) { 1138 if (snprintf(muxctl, sizeof (muxctl), 1139 "/devices%s:%s", devfspath, 1140 di_minor_name(dim)) > sizeof (muxctl)) { 1141 _msg(1, ("muxctl:snprintf error\n")); 1142 } 1143 if (++nminors == 2) 1144 break; 1145 } else if (strcmp(di_minor_name(dim), muxconname) == 0) { 1146 if (snprintf(muxcon, sizeof (muxcon), 1147 "/devices%s:%s", devfspath, 1148 di_minor_name(dim)) > sizeof (muxcon)) { 1149 _msg(1, ("muxcon:snprintf error\n")); 1150 } 1151 if (++nminors == 2) 1152 break; 1153 } 1154 } 1155 1156 di_devfs_path_free(devfspath); 1157 di_fini(root); 1158 1159 if ((muxfd = open_file(muxctl, oflags)) != -1) { 1160 1161 if (istrioctl(muxfd, TTYMUX_CONSDEV, (void *)&cn_dev, 1162 sizeof (cn_dev), 0) != 0) { 1163 cn_dev = NODEV; 1164 } else { 1165 _msg(8, ("MUXNODES: found sys console: %ld:%ld\n", 1166 major(cn_dev), minor(cn_dev))); 1167 1168 cn_rsrc = cache_create(muxcon, cn_dev); 1169 if (cn_rsrc != NULL) { 1170 cn_rsrc->flags |= PRESENT; 1171 cn_rsrc->flags &= ~UNKNOWN; 1172 } 1173 } 1174 (void) close(muxfd); 1175 1176 if (cn_dev != NODEV) 1177 return (B_TRUE); 1178 } else { 1179 _msg(1, ("TTYMUX: %s unavailable: %s\n", 1180 muxctl, strerror(errno))); 1181 } 1182 1183 return (B_FALSE); 1184 } 1185 1186 /* 1187 * Update registrations, and return the ops structure. 1188 */ 1189 struct rcm_mod_ops * 1190 rcm_mod_init() 1191 { 1192 _msg(4, ("TTYMUX: mod_init:\n")); 1193 cache_head.next = &cache_tail; 1194 cache_head.prev = NULL; 1195 cache_tail.prev = &cache_head; 1196 cache_tail.next = NULL; 1197 (void) mutex_init(&cache_lock, NULL, NULL); 1198 1199 /* 1200 * Find the multiplexer ctl and con nodes 1201 */ 1202 register_rsrcs = find_mux_nodes(TTYMUX_DRVNAME); 1203 1204 return (&tty_ops); 1205 } 1206 1207 /* 1208 * Save state and release resources. 1209 */ 1210 int 1211 rcm_mod_fini() 1212 { 1213 rsrc_t *rsrc; 1214 link_t *link, *nlink; 1215 1216 _msg(7, ("TTYMUX: freeing cache.\n")); 1217 (void) mutex_lock(&cache_lock); 1218 rsrc = cache_head.next; 1219 while (rsrc != &cache_tail) { 1220 cache_remove(rsrc); 1221 1222 for (link = rsrc->dependencies; link != NULL; ) { 1223 nlink = link->next; 1224 free(link); 1225 link = nlink; 1226 } 1227 1228 free_node(rsrc); 1229 rsrc = cache_head.next; 1230 } 1231 (void) mutex_unlock(&cache_lock); 1232 1233 (void) mutex_destroy(&cache_lock); 1234 return (RCM_SUCCESS); 1235 } 1236 1237 /* 1238 * Return a string describing this module. 1239 */ 1240 const char * 1241 rcm_mod_info() 1242 { 1243 return ("Serial mux device module 1.1"); 1244 } 1245 1246 /* 1247 * RCM Notification Handlers 1248 */ 1249 1250 static int 1251 tty_register(rcm_handle_t *hd) 1252 { 1253 rsrc_t *rsrc; 1254 link_t *link; 1255 int rv; 1256 1257 if (register_rsrcs == B_FALSE) 1258 return (RCM_SUCCESS); 1259 1260 if ((muxfd = open_file(muxctl, oflags)) == -1) { 1261 rcm_log_message(RCM_ERROR, _("TTYMUX: %s unavailable: %s\n"), 1262 muxctl, strerror(errno)); 1263 return (RCM_SUCCESS); 1264 } 1265 /* 1266 * Search for any new dependencies since the last notification or 1267 * since module was initialisated. 1268 */ 1269 (void) probe_dependencies(); 1270 1271 /* 1272 * Search the whole cache looking for any unregistered used resources 1273 * and register them. Note that the 'using resource' (a ttymux device 1274 * node) is not subject to DR operations so there is no need to 1275 * register them with the RCM framework. 1276 */ 1277 (void) mutex_lock(&cache_lock); 1278 for (rsrc = cache_head.next; rsrc != &cache_tail; rsrc = rsrc->next) { 1279 _msg(6, ("TTYMUX: REGISTER rsrc %s flags %d\n", 1280 rsrc->id, rsrc->flags)); 1281 1282 if (rsrc->dependencies != NULL && 1283 (rsrc->flags & REGISTERED) == 0) { 1284 _msg(6, ("TTYMUX: Registering rsrc %s\n", rsrc->id)); 1285 rv = rcm_register_interest(hd, rsrc->id, 0, NULL); 1286 if (rv == RCM_SUCCESS) 1287 rsrc->flags |= REGISTERED; 1288 } 1289 1290 for (link = rsrc->dependencies; link != NULL; 1291 link = link->next) { 1292 if ((link->used->flags & REGISTERED) != 0) 1293 continue; 1294 1295 _msg(6, ("TTYMUX: Registering rsrc %s\n", 1296 link->used->id)); 1297 rv = rcm_register_interest(hd, link->used->id, 1298 0, NULL); 1299 if (rv != RCM_SUCCESS) 1300 rcm_log_message(RCM_WARNING, 1301 _("TTYMUX: err %d registering %s\n"), 1302 rv, link->used->id); 1303 else 1304 link->used->flags |= REGISTERED; 1305 } 1306 } 1307 1308 (void) mutex_unlock(&cache_lock); 1309 (void) close(muxfd); 1310 return (RCM_SUCCESS); 1311 } 1312 1313 /* 1314 * Unregister all registrations. 1315 */ 1316 static int 1317 tty_unregister(rcm_handle_t *hd) 1318 { 1319 rsrc_t *rsrc; 1320 1321 (void) mutex_lock(&cache_lock); 1322 /* 1323 * Search every resource in the cache and if it has been registered 1324 * then unregister it from the RCM framework. 1325 */ 1326 for (rsrc = cache_head.next; rsrc != &cache_tail; rsrc = rsrc->next) { 1327 if ((rsrc->flags & REGISTERED) == 0) 1328 continue; 1329 1330 if (rcm_unregister_interest(hd, rsrc->id, 0) != RCM_SUCCESS) 1331 rcm_log_message(RCM_WARNING, 1332 _("TTYMUX: Failed to unregister %s\n"), rsrc->id); 1333 else 1334 rsrc->flags &= ~REGISTERED; 1335 } 1336 (void) mutex_unlock(&cache_lock); 1337 return (RCM_SUCCESS); 1338 } 1339 1340 /* 1341 * Report resource usage information. 1342 */ 1343 /*ARGSUSED*/ 1344 static int 1345 tty_getinfo(rcm_handle_t *hd, char *rsrcid, id_t id, uint_t flag, char **info, 1346 char **errstr, nvlist_t *proplist, rcm_info_t **depend_info) 1347 { 1348 rsrc_t *rsrc, *user; 1349 char *ru; 1350 size_t sz; 1351 1352 (void) mutex_lock(&cache_lock); 1353 rsrc = cache_lookup(rsrcid); 1354 1355 if (rsrc == NULL) { 1356 (void) mutex_unlock(&cache_lock); 1357 *errstr = strdup(gettext("Unmanaged resource")); 1358 return (RCM_FAILURE); 1359 } 1360 1361 ru = strdup(gettext("Resource Users")); 1362 user = NULL; 1363 while ((user = get_next_user(user, rsrc, -1)) != NULL) { 1364 *info = ru; 1365 sz = strlen(*info) + strlen(user->id) + 2; 1366 ru = malloc(sz); 1367 if (ru == NULL) { 1368 free(*info); 1369 *info = NULL; 1370 break; 1371 } 1372 if (snprintf(ru, sz, ": %s%s", *info, user->id) > sz) { 1373 _msg(4, ("tty_getinfo: snprintf error.\n")); 1374 } 1375 1376 free(*info); 1377 } 1378 *info = ru; 1379 1380 if (*info == NULL) { 1381 (void) mutex_unlock(&cache_lock); 1382 *errstr = strdup(gettext("Short of memory resources")); 1383 return (RCM_FAILURE); 1384 } 1385 1386 (void) mutex_unlock(&cache_lock); 1387 return (RCM_SUCCESS); 1388 } 1389 1390 /*ARGSUSED*/ 1391 static int 1392 tty_offline(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 1393 char **reason, rcm_info_t **dependent_reason) 1394 { 1395 return (rsrc_change_common(hd, TTYMUX_OFFLINE, rsrc, flags, 1396 reason, dependent_reason, NULL)); 1397 } 1398 1399 /*ARGSUSED*/ 1400 static int 1401 tty_remove(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 1402 char **reason, rcm_info_t **dependent_reason) 1403 { 1404 return (rsrc_change_common(hd, TTYMUX_REMOVE, rsrc, flags, 1405 reason, dependent_reason, NULL)); 1406 } 1407 1408 /*ARGSUSED*/ 1409 static int 1410 tty_suspend(rcm_handle_t *hd, char *rsrc, id_t id, timespec_t *interval, 1411 uint_t flag, char **reason, rcm_info_t **dependent_reason) 1412 { 1413 return (rsrc_change_common(hd, TTYMUX_SUSPEND, rsrc, flag, 1414 reason, dependent_reason, (void *)interval)); 1415 } 1416 1417 /*ARGSUSED*/ 1418 static int 1419 tty_online(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 1420 char **reason, rcm_info_t **dependent_reason) 1421 { 1422 return (rsrc_change_common(hd, TTYMUX_ONLINE, rsrc, flags, 1423 reason, dependent_reason, NULL)); 1424 } 1425 1426 /*ARGSUSED*/ 1427 static int 1428 tty_resume(rcm_handle_t *hd, char *rsrc, id_t id, uint_t flags, 1429 char **reason, rcm_info_t **dependent_reason) 1430 { 1431 return (rsrc_change_common(hd, TTYMUX_RESUME, rsrc, flags, 1432 reason, dependent_reason, NULL)); 1433 } 1434