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
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]
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"
39 #include <libdevinfo.h>
41 #include <sys/sysmacros.h>
42 #include <sys/types.h>
44 #include <sys/stream.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};
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 **,
88 static int tty_offline(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
90 static int tty_online(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
92 static int tty_remove(rcm_handle_t
*, char *, id_t
, uint_t
, char **,
95 static int get_devpath(char *, char **, dev_t
*);
100 static struct rcm_mod_ops tty_ops
= {
116 trace(char *fmt
, ...)
123 sz
= vsnprintf(buf
, sizeof (buf
), fmt
, args
);
127 rcm_log_message(RCM_TRACE1
,
128 _("TTYMUX: vsnprintf parse error\n"));
129 else if (sz
> sizeof (buf
)) {
130 char *b
= malloc(sz
+ 1);
134 sz
= vsnprintf(b
, sz
+ 1, fmt
, args
);
137 rcm_log_message(RCM_TRACE1
, _("%s"), b
);
141 rcm_log_message(RCM_TRACE1
, _("%s"), buf
);
147 * Resources managed by this module are stored in a list of rsrc_t
154 * Get a cache node for a resource. Call with cache lock held.
157 cache_lookup(const char *resource
)
160 rsrc
= cache_head
.next
;
161 while (rsrc
!= &cache_tail
) {
162 if (rsrc
->id
&& strcmp(resource
, rsrc
->id
) == 0) {
171 * Get a cache node for a minor node. Call with cache lock held.
174 cache_lookup_bydevt(dev_t devt
)
177 rsrc
= cache_head
.next
;
178 while (rsrc
!= &cache_tail
) {
179 if (rsrc
->dev
== devt
)
189 * Free a node. Make sure it isn't in the list!
192 free_node(rsrc_t
*node
)
205 * Call with the cache_lock held.
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
;
221 * Call with the cache_lock held.
224 cache_create(const char *resource
, dev_t dev
)
226 rsrc_t
*rsrc
= malloc(sizeof (rsrc_t
));
229 if ((rsrc
->id
= strdup(resource
)) != NULL
) {
232 rsrc
->dependencies
= NULL
;
239 _msg(0, ("TTYMUX: malloc failure for resource %s.\n",
248 * Call with the cache_lock held.
251 cache_get(const char *resource
)
253 rsrc_t
*rsrc
= cache_lookup(resource
);
257 (void) get_devpath((char *)resource
, NULL
, &dev
);
258 rsrc
= cache_create(resource
, dev
);
266 * Call with the cache_lock held.
269 cache_remove(rsrc_t
*node
)
271 node
->next
->prev
= node
->prev
;
272 node
->prev
->next
= node
->next
;
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
285 open_file(char *fname
, int flags
)
290 if ((flags
& O_EXCL
) == 0)
291 return (open(fname
, flags
));
294 ts
.tv_nsec
= 16000000; /* 16 milliseconds */
296 for (cnt
= 0; cnt
< 5 && (fd
= open(fname
, flags
)) == -1; cnt
++) {
297 (void) nanosleep(&ts
, NULL
);
304 * No-op for creating an association between a pair of resources.
308 nullconnect(link_t
*link
)
314 * No-op for destroying an association between a pair of resources.
318 nulldisconnect(link_t
*link
)
324 * Record an actual or desired association between two resources
325 * identified by their rsrc_t structures.
328 add_dependency(rsrc_t
*user
, rsrc_t
*used
)
333 if (user
== NULL
|| used
== 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"));
343 * Search for all resources that this resource user is depending
346 linkhead
= user
->dependencies
;
347 for (link
= linkhead
; link
!= NULL
; link
= link
->next
) {
349 * Does the using resource already depends on the used
352 if (link
->used
== used
)
356 link
= malloc(sizeof (link_t
));
359 rcm_log_message(RCM_ERROR
, _("TTYMUX: Out of memory\n"));
363 _msg(6, ("TTYMUX: New link user %s used %s\n", user
->id
, used
->id
));
368 link
->state
= UNKNOWN
;
371 link
->connect
= nullconnect
;
372 link
->disconnect
= nulldisconnect
;
373 link
->next
= linkhead
;
375 user
->dependencies
= link
;
381 * Send an I_STR stream ioctl to a device
384 istrioctl(int fd
, int cmd
, void *data
, int datalen
, int *bytes
) {
388 ios
.ic_timout
= 0; /* use the default */
390 ios
.ic_dp
= (char *)data
;
391 ios
.ic_len
= datalen
;
393 rval
= ioctl(fd
, I_STR
, (char *)&ios
);
400 * Streams link the driver identified by fd underneath a mux
401 * identified by ctrl_fd.
404 plink(int ctrl_fd
, int fd
)
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
);
421 * Streams unlink the STREAM identified by linkid from a mux
422 * identified by ctrl_fd.
425 punlink(int ctrl_fd
, int linkid
)
427 if (ioctl(ctrl_fd
, I_PUNLINK
, linkid
) < 0)
434 * Connect a pair of resources by establishing the dependency association.
435 * Only works for devices that support the TTYMUX ioctls.
438 mux_connect(link_t
*link
)
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.
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"));
467 * Explicitly check for attempts to plumb the system console -
468 * required becuase not all serial devices support the
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"));
479 * Ensure that the input/output mode of the dependent is reasonable
481 if ((ioflags
= link
->flags
& FORIO
) == 0)
485 * Open each resource participating in the association.
487 lfd
= open(link
->used
->id
, O_EXCL
|O_RDWR
|O_NONBLOCK
|O_NOCTTY
);
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
);
494 rcm_log_message(RCM_WARNING
,
495 _("TTYMUX: open error %d for device %s\n"),
496 errno
, link
->used
->id
);
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
) {
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
;
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
) {
547 if (ioctl(lfd
, TCGETS
, &tc
) != -1) {
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
);
567 link
->linkid
= as
.ttymux_linkid
;
569 _msg(6, ("TTYMUX: associating\n"));
570 if (istrioctl(muxfd
, TTYMUX_ASSOC
, (void *)&as
, sizeof (as
), 0) != 0) {
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
;
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
));
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
);
600 * Disconnect a pair of resources by destroying the dependency association.
601 * Only works for devices that support the TTYMUX ioctls.
604 mux_disconnect(link_t
*link
)
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
));
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) {
632 rcm_log_message(RCM_WARNING
,
633 _("TTYMUX: Dissassociate error %d for %s\n"),
636 } else if (punlink(muxfd
, as
.ttymux_linkid
) != 0) {
638 rcm_log_message(RCM_WARNING
,
639 _("TTYMUX: Error %d unlinking %d:%d\n"),
640 errno
, major(link
->used
->dev
), minor(link
->used
->dev
));
642 _msg(6, ("TTYMUX: %s<->%s disconnected.\n",
643 link
->user
->id
, link
->used
->id
));
645 link
->state
= DISCONNECTED
;
655 * Given a special device file system path return the /devices path
656 * and/or the device number (dev_t) of the device.
659 get_devpath(char *dev
, char **cname
, dev_t
*devt
)
669 if (lstat(dev
, &sb
) < 0) {
671 } else if ((sb
.st_mode
& S_IFMT
) == S_IFLNK
) {
673 char linkbuf
[PATH_MAX
+1];
675 if (stat(dev
, &sb
) < 0)
678 lsz
= readlink(dev
, linkbuf
, PATH_MAX
);
683 dev
= strstr(linkbuf
, "/devices");
689 *cname
= strdup(dev
);
698 * See routine locate_node
701 locate_dev(di_node_t node
, di_minor_t minor
, void *arg
)
704 char resource
[PATH_MAX
];
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
);
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!
737 locate_node(dev_t dev
, di_node_t
*root
)
741 assert(root
!= NULL
);
743 if ((rsrc
= cache_lookup_bydevt(dev
)) != NULL
)
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
758 ttymux_assocs_t links
;
766 cnt
= istrioctl(muxfd
, TTYMUX_LIST
, (void *)0, 0, 0);
768 _msg(8, ("TTYMUX: Probed %d links [%d]\n", cnt
, errno
));
773 if ((links
.ttymux_assocs
= calloc(cnt
, sizeof (ttymux_assoc_t
))) == 0)
776 links
.ttymux_nlinks
= cnt
;
778 n
= istrioctl(muxfd
, TTYMUX_LIST
, (void *)&links
, sizeof (links
), 0);
781 _msg(2, ("TTYMUX: Probe error %s\n", strerror(errno
)));
782 free(links
.ttymux_assocs
);
786 asp
= (ttymux_assoc_t
*)links
.ttymux_assocs
;
788 if ((root
= di_init("/", DINFOSUBTREE
|DINFOMINOR
)) == DI_NODE_NIL
)
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
802 if (asp
->ttymux_ldev
== NODEV
) {
805 if (asp
->ttymux_path
== NULL
||
806 *asp
->ttymux_path
!= '/')
809 if (snprintf(buf
, sizeof (buf
), "/devices%s",
810 asp
->ttymux_path
) > sizeof (buf
))
813 used
= cache_get(buf
);
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
)));
823 _msg(7, ("TTYMUX: Probe: %ld:%ld not present\n",
824 major(asp
->ttymux_ldev
), minor(asp
->ttymux_ldev
)));
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
);
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
;
841 (void) mutex_unlock(&cache_lock
);
842 free(links
.ttymux_assocs
);
847 * A resource has become available. Re-establish any associations involving
851 rsrc_available(rsrc_t
*rsrc
)
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
) {
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
;
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
893 rsrc_unavailable(rsrc_t
*rsrc
)
898 for (rs
= cache_head
.next
; rs
!= &cache_tail
; rs
= rs
->next
) {
899 for (link
= rs
->dependencies
;
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.
925 get_next_user(rsrc_t
*next
, rsrc_t
*rsrc
, int redundancy
)
932 src
= (next
!= NULL
) ? next
->next
: cache_head
.next
;
934 while (src
!= &cache_tail
) {
937 for (link
= src
->dependencies
, cnt
= 0;
941 if (link
->state
== CONNECTED
)
944 if (link
->used
== rsrc
)
947 if (inuse
== B_TRUE
&&
948 (redundancy
<= 0 || cnt
== redundancy
)) {
955 _msg(8, ("TTYMUX: count_users(%s) res %d.\n", rsrc
->id
, cnt
));
960 * Common handler for RCM notifications.
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
)
971 (void) mutex_lock(&cache_lock
);
972 rsrc
= cache_lookup(rsrcid
);
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
);
988 _msg(4, ("TTYMUX: SUSPEND %s operation refused.\n",
990 if ((*reason
= strdup(TTYMUX_INVALID_ERR
)) == NULL
) {
991 rcm_log_message(RCM_ERROR
, TTYMUX_MEMORY_ERR
);
996 rsrc
->flags
|= UNKNOWN
;
997 rsrc
->flags
&= ~(PRESENT
| REGISTERED
);
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
);
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
)) ==
1024 rcm_log_message(RCM_ERROR
,
1029 } else if (user
!= NULL
) {
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
);
1038 rcm_log_message(RCM_ERROR
, TTYMUX_MEMORY_ERR
);
1042 rv
= rsrc_unavailable(rsrc
);
1043 if (rv
== RCM_FAILURE
) {
1044 if ((*reason
= strdup(TTYMUX_OFFLINE_FAIL
)) ==
1046 rcm_log_message(RCM_ERROR
,
1052 if (rv
== RCM_FAILURE
) {
1053 _msg(4, ("TTYMUX: OFFLINE %s operation refused.\n",
1057 _msg(4, ("TTYMUX: OFFLINE %s res %d.\n", rsrc
->id
, rv
));
1063 _msg(4, ("TTYMUX: RESUME %s operation refused.\n",
1065 if ((*reason
= strdup(TTYMUX_INVALID_ERR
)) == NULL
) {
1066 rcm_log_message(RCM_ERROR
, TTYMUX_MEMORY_ERR
);
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
);
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
);
1092 find_mux_nodes(char *drv
)
1094 di_node_t root
, node
;
1097 char muxctlname
[] = "ctl";
1098 char muxconname
[] = "con";
1101 (void) strcpy(muxctl
, MUXCTLLINK
);
1102 (void) strcpy(muxcon
, MUXCONLINK
);
1105 if ((root
= di_init("/", DINFOCPYALL
)) == DI_NODE_NIL
) {
1106 rcm_log_message(RCM_WARNING
, _("di_init error\n"));
1110 node
= di_drv_first_node(drv
, root
);
1111 if (node
== DI_NODE_NIL
) {
1112 _msg(4, ("no node for %s\n", drv
));
1117 * If the device is not a prom node do not continue.
1119 if (di_nodeid(node
) != DI_PROM_NODEID
) {
1123 if ((devfspath
= di_devfs_path(node
)) == NULL
) {
1129 * Loop through all the minor nodes the driver (drv) looking
1130 * for the ctl node (this is the device on which
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"));
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"));
1157 di_devfs_path_free(devfspath
);
1160 if ((muxfd
= open_file(muxctl
, oflags
)) != -1) {
1162 if (istrioctl(muxfd
, TTYMUX_CONSDEV
, (void *)&cn_dev
,
1163 sizeof (cn_dev
), 0) != 0) {
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
)
1180 _msg(1, ("TTYMUX: %s unavailable: %s\n",
1181 muxctl
, strerror(errno
)));
1188 * Update registrations, and return the ops structure.
1190 struct rcm_mod_ops
*
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
);
1209 * Save state and release resources.
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
) {
1223 for (link
= rsrc
->dependencies
; link
!= NULL
; ) {
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.
1244 return ("Serial mux device module 1.1");
1248 * RCM Notification Handlers
1252 tty_register(rcm_handle_t
*hd
)
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)
1296 _msg(6, ("TTYMUX: Registering rsrc %s\n",
1298 rv
= rcm_register_interest(hd
, link
->used
->id
,
1300 if (rv
!= RCM_SUCCESS
)
1301 rcm_log_message(RCM_WARNING
,
1302 _("TTYMUX: err %d registering %s\n"),
1303 rv
, link
->used
->id
);
1305 link
->used
->flags
|= REGISTERED
;
1309 (void) mutex_unlock(&cache_lock
);
1310 (void) close(muxfd
);
1311 return (RCM_SUCCESS
);
1315 * Unregister all registrations.
1318 tty_unregister(rcm_handle_t
*hd
)
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)
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
);
1335 rsrc
->flags
&= ~REGISTERED
;
1337 (void) mutex_unlock(&cache_lock
);
1338 return (RCM_SUCCESS
);
1342 * Report resource usage information.
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
;
1353 (void) mutex_lock(&cache_lock
);
1354 rsrc
= cache_lookup(rsrcid
);
1357 (void) mutex_unlock(&cache_lock
);
1358 *errstr
= strdup(gettext("Unmanaged resource"));
1359 return (RCM_FAILURE
);
1362 ru
= strdup(gettext("Resource Users"));
1364 while ((user
= get_next_user(user
, rsrc
, -1)) != NULL
) {
1366 sz
= strlen(*info
) + strlen(user
->id
) + 2;
1373 if (snprintf(ru
, sz
, ": %s%s", *info
, user
->id
) > sz
) {
1374 _msg(4, ("tty_getinfo: snprintf error.\n"));
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
);
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
));
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
));
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
));
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
));
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
));