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