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