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 }