8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / rcm_daemon / common / ttymux_rcm.c
blob5b50679a3ed569f3fe617f245aa2b70a64e9774a
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
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.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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>
49 #include <sys/param.h>
50 #include <sys/openpromio.h>
51 #include <sys/ttymuxuser.h>
53 #include "ttymux_rcm_impl.h"
54 #include "rcm_module.h"
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")
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)
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;
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 **);
95 static int get_devpath(char *, char **, dev_t *);
98 * Module-Private data
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
114 /*PRINTFLIKE1*/
115 static void
116 trace(char *fmt, ...)
118 va_list args;
119 char buf[256];
120 int sz;
122 va_start(args, fmt);
123 sz = vsnprintf(buf, sizeof (buf), fmt, args);
124 va_end(args);
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);
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);
140 } else {
141 rcm_log_message(RCM_TRACE1, _("%s"), buf);
146 * CACHE MANAGEMENT
147 * Resources managed by this module are stored in a list of rsrc_t
148 * structures.
152 * cache_lookup()
154 * Get a cache node for a resource. Call with cache lock held.
156 static rsrc_t *
157 cache_lookup(const char *resource)
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);
165 rsrc = rsrc->next;
167 return (NULL);
171 * Get a cache node for a minor node. Call with cache lock held.
173 static rsrc_t *
174 cache_lookup_bydevt(dev_t devt)
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;
183 return (NULL);
187 * free_node()
189 * Free a node. Make sure it isn't in the list!
191 static void
192 free_node(rsrc_t *node)
194 if (node) {
195 if (node->id) {
196 free(node->id);
198 free(node);
203 * cache_insert()
205 * Call with the cache_lock held.
207 static void
208 cache_insert(rsrc_t *node)
210 /* insert at the head for best performance */
211 node->next = cache_head.next;
212 node->prev = &cache_head;
214 node->next->prev = node;
215 node->prev->next = node;
219 * cache_create()
221 * Call with the cache_lock held.
223 static rsrc_t *
224 cache_create(const char *resource, dev_t dev)
226 rsrc_t *rsrc = malloc(sizeof (rsrc_t));
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;
238 } else {
239 _msg(0, ("TTYMUX: malloc failure for resource %s.\n",
240 resource));
242 return (rsrc);
246 * cache_get()
248 * Call with the cache_lock held.
250 static rsrc_t *
251 cache_get(const char *resource)
253 rsrc_t *rsrc = cache_lookup(resource);
254 if (rsrc == NULL) {
255 dev_t dev;
257 (void) get_devpath((char *)resource, NULL, &dev);
258 rsrc = cache_create(resource, dev);
260 return (rsrc);
264 * cache_remove()
266 * Call with the cache_lock held.
268 static void
269 cache_remove(rsrc_t *node)
271 node->next->prev = node->prev;
272 node->prev->next = node->next;
273 node->next = NULL;
274 node->prev = NULL;
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).
284 static int
285 open_file(char *fname, int flags)
287 int fd, cnt;
288 struct timespec ts;
290 if ((flags & O_EXCL) == 0)
291 return (open(fname, flags));
293 ts.tv_sec = 0;
294 ts.tv_nsec = 16000000; /* 16 milliseconds */
296 for (cnt = 0; cnt < 5 && (fd = open(fname, flags)) == -1; cnt++) {
297 (void) nanosleep(&ts, NULL);
298 ts.tv_nsec *= 2;
300 return (fd);
304 * No-op for creating an association between a pair of resources.
306 /*ARGSUSED*/
307 static int
308 nullconnect(link_t *link)
310 return (0);
314 * No-op for destroying an association between a pair of resources.
316 /*ARGSUSED*/
317 static int
318 nulldisconnect(link_t *link)
320 return (0);
324 * Record an actual or desired association between two resources
325 * identified by their rsrc_t structures.
327 static link_t *
328 add_dependency(rsrc_t *user, rsrc_t *used)
330 link_t *linkhead;
331 link_t *link;
333 if (user == NULL || used == NULL)
334 return (NULL);
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);
343 * Search for all resources that this resource user is depending
344 * upon.
346 linkhead = user->dependencies;
347 for (link = linkhead; link != NULL; link = link->next) {
349 * Does the using resource already depends on the used
350 * resource
352 if (link->used == used)
353 return (link);
356 link = malloc(sizeof (link_t));
358 if (link == NULL) {
359 rcm_log_message(RCM_ERROR, _("TTYMUX: Out of memory\n"));
360 return (NULL);
363 _msg(6, ("TTYMUX: New link user %s used %s\n", user->id, used->id));
365 link->user = user;
366 link->used = used;
367 link->linkid = 0;
368 link->state = UNKNOWN;
369 link->flags = 0;
371 link->connect = nullconnect;
372 link->disconnect = nulldisconnect;
373 link->next = linkhead;
375 user->dependencies = link;
377 return (link);
381 * Send an I_STR stream ioctl to a device
383 static int
384 istrioctl(int fd, int cmd, void *data, int datalen, int *bytes) {
385 struct strioctl ios;
386 int rval;
388 ios.ic_timout = 0; /* use the default */
389 ios.ic_cmd = cmd;
390 ios.ic_dp = (char *)data;
391 ios.ic_len = datalen;
393 rval = ioctl(fd, I_STR, (char *)&ios);
394 if (bytes)
395 *bytes = ios.ic_len;
396 return (rval);
400 * Streams link the driver identified by fd underneath a mux
401 * identified by ctrl_fd.
403 static int
404 plink(int ctrl_fd, int fd)
406 int linkid;
409 * pop any modules off the lower stream.
411 while (ioctl(fd, I_POP, 0) == 0)
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);
421 * Streams unlink the STREAM identified by linkid from a mux
422 * identified by ctrl_fd.
424 static int
425 punlink(int ctrl_fd, int linkid)
427 if (ioctl(ctrl_fd, I_PUNLINK, linkid) < 0)
428 return (errno);
429 else
430 return (0);
434 * Connect a pair of resources by establishing the dependency association.
435 * Only works for devices that support the TTYMUX ioctls.
437 static int
438 mux_connect(link_t *link)
440 int lfd;
441 int rv;
442 ttymux_assoc_t as;
443 uint8_t ioflags;
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));
450 _msg(12, ("TTYMUX: struct size = %d (plen %d)\n",
451 sizeof (as), PATH_MAX));
453 if (link->user->dev == NODEV || link->used->dev == NODEV) {
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.
459 return (EAGAIN);
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);
467 * Explicitly check for attempts to plumb the system console -
468 * required becuase not all serial devices support the
469 * O_EXCL open flag.
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"));
475 return (EPERM);
479 * Ensure that the input/output mode of the dependent is reasonable
481 if ((ioflags = link->flags & FORIO) == 0)
482 ioflags = FORIO;
485 * Open each resource participating in the association.
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);
498 return (errno);
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.
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).
515 as.ttymux_linkid = 0;
516 as.ttymux_ldev = link->used->dev;
518 if (istrioctl(muxfd, TTYMUX_GETLINK,
519 (void *)&as, sizeof (as), NULL) == 0) {
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);
533 * Now link and associate the used resource under the using resource.
535 as.ttymux_udev = link->user->dev;
536 as.ttymux_ldev = link->used->dev;
537 as.ttymux_tag = 0ul;
538 as.ttymux_ioflag = ioflags;
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)));
544 if (as.ttymux_udev == cn_dev) {
545 struct termios tc;
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));
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;
567 link->linkid = as.ttymux_linkid;
569 _msg(6, ("TTYMUX: associating\n"));
570 if (istrioctl(muxfd, TTYMUX_ASSOC, (void *)&as, sizeof (as), 0) != 0) {
571 rv = errno;
572 goto out;
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));
586 (void) close(lfd);
587 if (as.ttymux_linkid > 0) {
589 * There was an error so unwind the I_PLINK step
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);
596 return (rv);
600 * Disconnect a pair of resources by destroying the dependency association.
601 * Only works for devices that support the TTYMUX ioctls.
603 static int
604 mux_disconnect(link_t *link)
606 int rv;
607 ttymux_assoc_t as;
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)));
614 as.ttymux_ldev = link->used->dev;
616 if (istrioctl(muxfd, TTYMUX_GETLINK,
617 (void *)&as, sizeof (as), NULL) != 0) {
619 _msg(1, ("TTYMUX: %ld:%ld not linked [err %d]\n",
620 major(link->used->dev), minor(link->used->dev), errno));
621 return (0);
624 * Do not disassociate console resources - simply
625 * unlink them so that they remain persistent.
627 } else if (as.ttymux_udev != cn_dev &&
628 istrioctl(muxfd, TTYMUX_DISASSOC, (void *)&as,
629 sizeof (as), 0) == -1) {
631 rv = errno;
632 rcm_log_message(RCM_WARNING,
633 _("TTYMUX: Dissassociate error %d for %s\n"),
634 rv, link->used->id);
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));
645 link->state = DISCONNECTED;
646 link->linkid = 0;
647 rv = 0;
649 return (rv);
652 /* PESISTENCY */
655 * Given a special device file system path return the /devices path
656 * and/or the device number (dev_t) of the device.
658 static int
659 get_devpath(char *dev, char **cname, dev_t *devt)
661 struct stat sb;
663 if (cname != NULL)
664 *cname = NULL;
666 if (devt != NULL)
667 *devt = NODEV;
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];
675 if (stat(dev, &sb) < 0)
676 return (errno);
678 lsz = readlink(dev, linkbuf, PATH_MAX);
680 if (lsz <= 0)
681 return (ENODEV);
682 linkbuf[lsz] = '\0';
683 dev = strstr(linkbuf, "/devices");
684 if (dev == NULL)
685 return (ENODEV);
688 if (cname != NULL)
689 *cname = strdup(dev);
691 if (devt != NULL)
692 *devt = sb.st_rdev;
694 return (0);
698 * See routine locate_node
700 static int
701 locate_dev(di_node_t node, di_minor_t minor, void *arg)
703 char *devfspath;
704 char resource[PATH_MAX];
705 rsrc_t *rsrc;
707 if (di_minor_devt(minor) != (dev_t)arg)
708 return (DI_WALK_CONTINUE);
710 if ((devfspath = di_devfs_path(node)) == NULL)
711 return (DI_WALK_TERMINATE);
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);
719 di_devfs_path_free(devfspath);
721 rsrc = cache_lookup(resource);
722 if (rsrc == NULL &&
723 (rsrc = cache_create(resource, di_minor_devt(minor))) == NULL)
724 return (DI_WALK_TERMINATE);
726 rsrc->dev = di_minor_devt(minor);
727 rsrc->flags |= PRESENT;
728 rsrc->flags &= ~UNKNOWN;
729 return (DI_WALK_TERMINATE);
733 * Find a devinfo node that matches the device argument (dev).
734 * This is an expensive search of the whole device tree!
736 static rsrc_t *
737 locate_node(dev_t dev, di_node_t *root)
739 rsrc_t *rsrc;
741 assert(root != NULL);
743 if ((rsrc = cache_lookup_bydevt(dev)) != NULL)
744 return (rsrc);
746 (void) di_walk_minor(*root, NULL, 0, (void*)dev, locate_dev);
748 return (cache_lookup_bydevt(dev));
752 * Search for any existing dependency relationships managed by this
753 * RCM module.
755 static int
756 probe_dependencies()
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;
766 cnt = istrioctl(muxfd, TTYMUX_LIST, (void *)0, 0, 0);
768 _msg(8, ("TTYMUX: Probed %d links [%d]\n", cnt, errno));
770 if (cnt <= 0)
771 return (0);
773 if ((links.ttymux_assocs = calloc(cnt, sizeof (ttymux_assoc_t))) == 0)
774 return (EAGAIN);
776 links.ttymux_nlinks = cnt;
778 n = istrioctl(muxfd, TTYMUX_LIST, (void *)&links, sizeof (links), 0);
780 if (n == -1) {
781 _msg(2, ("TTYMUX: Probe error %s\n", strerror(errno)));
782 free(links.ttymux_assocs);
783 return (0);
786 asp = (ttymux_assoc_t *)links.ttymux_assocs;
788 if ((root = di_init("/", DINFOSUBTREE|DINFOMINOR)) == DI_NODE_NIL)
789 return (errno);
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)));
798 * The TTYMUX_LIST ioctl can return links relating
799 * to potential devices. Such devices are identified
800 * in the path field.
802 if (asp->ttymux_ldev == NODEV) {
803 char buf[PATH_MAX];
805 if (asp->ttymux_path == NULL ||
806 *asp->ttymux_path != '/')
807 continue;
809 if (snprintf(buf, sizeof (buf), "/devices%s",
810 asp->ttymux_path) > sizeof (buf))
811 continue;
813 used = cache_get(buf);
814 } else {
815 used = locate_node(asp->ttymux_ldev, &root);
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;
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;
827 _msg(6, ("TTYMUX: Probe: Restore %s <-> %s (id %d)\n",
828 ruser->id, used->id, asp->ttymux_linkid));
830 link = add_dependency(ruser, used);
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;
840 di_fini(root);
841 (void) mutex_unlock(&cache_lock);
842 free(links.ttymux_assocs);
843 return (0);
847 * A resource has become available. Re-establish any associations involving
848 * the resource.
850 static int
851 rsrc_available(rsrc_t *rsrc)
853 link_t *link;
854 rsrc_t *rs;
856 if (rsrc->dev == NODEV) {
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).
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));
870 * What does RCM do with failed online notifications.
872 return (RCM_FAILURE);
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);
885 return (RCM_SUCCESS);
889 * A resource is going away. Tear down any associations involving
890 * the resource.
892 static int
893 rsrc_unavailable(rsrc_t *rsrc)
895 link_t *link;
896 rsrc_t *rs;
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);
910 return (RCM_SUCCESS);
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.
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.
924 static rsrc_t *
925 get_next_user(rsrc_t *next, rsrc_t *rsrc, int redundancy)
927 rsrc_t *src;
928 link_t *link;
929 int cnt = 0;
930 boolean_t inuse;
932 src = (next != NULL) ? next->next : cache_head.next;
934 while (src != &cache_tail) {
935 inuse = B_FALSE;
937 for (link = src->dependencies, cnt = 0;
938 link != NULL;
939 link = link->next) {
941 if (link->state == CONNECTED)
942 cnt++;
944 if (link->used == rsrc)
945 inuse = B_TRUE;
947 if (inuse == B_TRUE &&
948 (redundancy <= 0 || cnt == redundancy)) {
949 return (src);
952 src = src->next;
955 _msg(8, ("TTYMUX: count_users(%s) res %d.\n", rsrc->id, cnt));
956 return (NULL);
960 * Common handler for RCM notifications.
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)
967 rsrc_t *rsrc, *user;
968 int rv, len;
969 char *tmp = NULL;
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);
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);
984 switch (op) {
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);
993 break;
995 case TTYMUX_REMOVE:
996 rsrc->flags |= UNKNOWN;
997 rsrc->flags &= ~(PRESENT | REGISTERED);
998 rv = RCM_SUCCESS;
999 break;
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);
1018 } else if (flag & RCM_FORCE) {
1019 rv = rsrc_unavailable(rsrc);
1021 if (rv == RCM_FAILURE) {
1022 if ((*reason = strdup(TTYMUX_OFFLINE_FAIL)) ==
1023 NULL) {
1024 rcm_log_message(RCM_ERROR,
1025 TTYMUX_MEMORY_ERR);
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);
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);
1052 if (rv == RCM_FAILURE) {
1053 _msg(4, ("TTYMUX: OFFLINE %s operation refused.\n",
1054 rsrc->id));
1056 } else {
1057 _msg(4, ("TTYMUX: OFFLINE %s res %d.\n", rsrc->id, rv));
1059 break;
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);
1068 break;
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);
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);
1086 (void) close(muxfd);
1087 (void) mutex_unlock(&cache_lock);
1088 return (rv);
1091 static boolean_t
1092 find_mux_nodes(char *drv)
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;
1101 (void) strcpy(muxctl, MUXCTLLINK);
1102 (void) strcpy(muxcon, MUXCONLINK);
1103 cn_rsrc = NULL;
1105 if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
1106 rcm_log_message(RCM_WARNING, _("di_init error\n"));
1107 return (B_FALSE);
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);
1117 * If the device is not a prom node do not continue.
1119 if (di_nodeid(node) != DI_PROM_NODEID) {
1120 di_fini(root);
1121 return (B_FALSE);
1123 if ((devfspath = di_devfs_path(node)) == NULL) {
1124 di_fini(root);
1125 return (B_FALSE);
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).
1133 dim = DI_MINOR_NIL;
1134 while ((dim = di_minor_next(node, dim)) != DI_MINOR_NIL) {
1136 _msg(7, ("MUXNODES: minor %s\n", di_minor_name(dim)));
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"));
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"));
1152 if (++nminors == 2)
1153 break;
1157 di_devfs_path_free(devfspath);
1158 di_fini(root);
1160 if ((muxfd = open_file(muxctl, oflags)) != -1) {
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)));
1169 cn_rsrc = cache_create(muxcon, cn_dev);
1170 if (cn_rsrc != NULL) {
1171 cn_rsrc->flags |= PRESENT;
1172 cn_rsrc->flags &= ~UNKNOWN;
1175 (void) close(muxfd);
1177 if (cn_dev != NODEV)
1178 return (B_TRUE);
1179 } else {
1180 _msg(1, ("TTYMUX: %s unavailable: %s\n",
1181 muxctl, strerror(errno)));
1184 return (B_FALSE);
1188 * Update registrations, and return the ops structure.
1190 struct rcm_mod_ops *
1191 rcm_mod_init()
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);
1201 * Find the multiplexer ctl and con nodes
1203 register_rsrcs = find_mux_nodes(TTYMUX_DRVNAME);
1205 return (&tty_ops);
1209 * Save state and release resources.
1212 rcm_mod_fini()
1214 rsrc_t *rsrc;
1215 link_t *link, *nlink;
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);
1223 for (link = rsrc->dependencies; link != NULL; ) {
1224 nlink = link->next;
1225 free(link);
1226 link = nlink;
1229 free_node(rsrc);
1230 rsrc = cache_head.next;
1232 (void) mutex_unlock(&cache_lock);
1234 (void) mutex_destroy(&cache_lock);
1235 return (RCM_SUCCESS);
1239 * Return a string describing this module.
1241 const char *
1242 rcm_mod_info()
1244 return ("Serial mux device module 1.1");
1248 * RCM Notification Handlers
1251 static int
1252 tty_register(rcm_handle_t *hd)
1254 rsrc_t *rsrc;
1255 link_t *link;
1256 int rv;
1258 if (register_rsrcs == B_FALSE)
1259 return (RCM_SUCCESS);
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);
1267 * Search for any new dependencies since the last notification or
1268 * since module was initialisated.
1270 (void) probe_dependencies();
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.
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));
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;
1291 for (link = rsrc->dependencies; link != NULL;
1292 link = link->next) {
1293 if ((link->used->flags & REGISTERED) != 0)
1294 continue;
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;
1309 (void) mutex_unlock(&cache_lock);
1310 (void) close(muxfd);
1311 return (RCM_SUCCESS);
1315 * Unregister all registrations.
1317 static int
1318 tty_unregister(rcm_handle_t *hd)
1320 rsrc_t *rsrc;
1322 (void) mutex_lock(&cache_lock);
1324 * Search every resource in the cache and if it has been registered
1325 * then unregister it from the RCM framework.
1327 for (rsrc = cache_head.next; rsrc != &cache_tail; rsrc = rsrc->next) {
1328 if ((rsrc->flags & REGISTERED) == 0)
1329 continue;
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;
1337 (void) mutex_unlock(&cache_lock);
1338 return (RCM_SUCCESS);
1342 * Report resource usage information.
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)
1349 rsrc_t *rsrc, *user;
1350 char *ru;
1351 size_t sz;
1353 (void) mutex_lock(&cache_lock);
1354 rsrc = cache_lookup(rsrcid);
1356 if (rsrc == NULL) {
1357 (void) mutex_unlock(&cache_lock);
1358 *errstr = strdup(gettext("Unmanaged resource"));
1359 return (RCM_FAILURE);
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;
1373 if (snprintf(ru, sz, ": %s%s", *info, user->id) > sz) {
1374 _msg(4, ("tty_getinfo: snprintf error.\n"));
1377 free(*info);
1379 *info = ru;
1381 if (*info == NULL) {
1382 (void) mutex_unlock(&cache_lock);
1383 *errstr = strdup(gettext("Short of memory resources"));
1384 return (RCM_FAILURE);
1387 (void) mutex_unlock(&cache_lock);
1388 return (RCM_SUCCESS);
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)
1396 return (rsrc_change_common(hd, TTYMUX_OFFLINE, rsrc, flags,
1397 reason, dependent_reason, NULL));
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)
1405 return (rsrc_change_common(hd, TTYMUX_REMOVE, rsrc, flags,
1406 reason, dependent_reason, NULL));
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)
1414 return (rsrc_change_common(hd, TTYMUX_SUSPEND, rsrc, flag,
1415 reason, dependent_reason, (void *)interval));
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)
1423 return (rsrc_change_common(hd, TTYMUX_ONLINE, rsrc, flags,
1424 reason, dependent_reason, NULL));
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)
1432 return (rsrc_change_common(hd, TTYMUX_RESUME, rsrc, flags,
1433 reason, dependent_reason, NULL));