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