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