xref: /titanic_50/usr/src/cmd/vntsd/vntsdvcc.c (revision 48bc00d6814e04ff3edb32cafe7d1bc580baff68)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 #pragma ident	"%Z%%M%	%I%	%E% SMI"
26 
27 /*
28  * Configuration and setup interface to vcc driver.
29  * At intialization time, vntsd opens vcc ctrl port and read initial
30  * configuratioa. It manages console groups, creates the listen thread,
31  * dynamically adds and removes virtual console within a group.
32  */
33 
34 
35 #include <syslog.h>
36 #include <stdio.h>
37 #include <sys/types.h>
38 #include <sys/ipc.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <sys/socket.h>
43 #include <sys/ipc.h>
44 #include <sys/shm.h>
45 #include <sys/sem.h>
46 #include <wait.h>
47 #include <time.h>
48 #include <synch.h>
49 #include <netinet/in.h>
50 #include <thread.h>
51 #include <signal.h>
52 #include "vntsd.h"
53 
54 /* signal all clients that console has been deleted */
55 boolean_t
56 vntsd_notify_client_cons_del(vntsd_client_t *clientp)
57 {
58 	(void) mutex_lock(&clientp->lock);
59 	clientp->status |= VNTSD_CLIENT_CONS_DELETED;
60 	(void) thr_kill(clientp->cons_tid, SIGUSR1);
61 	(void) mutex_unlock(&clientp->lock);
62 	return (B_FALSE);
63 }
64 
65 /* free console  structure */
66 static void
67 free_cons(vntsd_cons_t *consp)
68 {
69 	assert(consp);
70 	(void) mutex_destroy(&consp->lock);
71 	(void) cond_destroy(&consp->cvp);
72 	if (consp->vcc_fd != -1)
73 		(void) close(consp->vcc_fd);
74 	free(consp);
75 }
76 
77 /* free group structure */
78 static void
79 free_group(vntsd_group_t *groupp)
80 {
81 	assert(groupp);
82 	(void) mutex_destroy(&groupp->lock);
83 	(void) cond_destroy(&groupp->cvp);
84 	if (groupp->sockfd != -1)
85 		(void) close(groupp->sockfd);
86 	free(groupp);
87 }
88 
89 /*
90  *  all clients connected to a console must disconnect before
91  *  removing a console.
92  */
93 static void
94 cleanup_cons(vntsd_cons_t *consp)
95 {
96 	vntsd_group_t	*groupp;
97 	timestruc_t	to;
98 
99 	assert(consp);
100 	D1(stderr, "t@%d vntsd_disconn_clients@%d\n", thr_self(),
101 	    consp->cons_no);
102 
103 	groupp = consp->group;
104 	assert(groupp);
105 
106 
107 	(void) mutex_lock(&consp->lock);
108 
109 	/* wait for all clients disconnect from the console */
110 	while (consp->clientpq != NULL) {
111 		consp->status |= VNTSD_CONS_SIG_WAIT;
112 
113 		/* signal client to disconnect the console */
114 		(void) vntsd_que_walk(consp->clientpq,
115 		    (el_func_t)vntsd_notify_client_cons_del);
116 
117 		(void) thr_kill(consp->wr_tid, SIGUSR1);
118 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
119 		to.tv_nsec = 0;
120 
121 		/* wait for clients to disconnect  */
122 		(void) cond_reltimedwait(&consp->cvp, &consp->lock, &to);
123 	}
124 
125 	/* reduce console count in the group */
126 	(void) mutex_lock(&groupp->lock);
127 	assert(groupp->num_cons > 0);
128 	groupp->num_cons--;
129 	(void) mutex_unlock(&groupp->lock);
130 
131 	(void) mutex_unlock(&consp->lock);
132 
133 	free_cons(consp);
134 }
135 
136 /* search for a group whose console is being deleted */
137 static boolean_t
138 find_clean_cons_group(vntsd_group_t *groupp)
139 {
140 	if (groupp->status & VNTSD_GROUP_CLEAN_CONS) {
141 		return (B_TRUE);
142 	} else {
143 		return (B_FALSE);
144 	}
145 }
146 
147 /* search for a console that is being deleted */
148 static boolean_t
149 find_clean_cons(vntsd_cons_t *consp)
150 {
151 	if (consp->status & VNTSD_CONS_DELETED) {
152 		return (B_TRUE);
153 	} else {
154 		return (B_FALSE);
155 	}
156 }
157 
158 /* delete a console */
159 void
160 vntsd_delete_cons(vntsd_t *vntsdp)
161 {
162 	vntsd_group_t *groupp;
163 	vntsd_cons_t *consp;
164 
165 	for (; ; ) {
166 		/* get the group contains deleted console */
167 		(void) mutex_lock(&vntsdp->lock);
168 		groupp = vntsd_que_walk(vntsdp->grouppq,
169 		    (el_func_t)find_clean_cons_group);
170 		if (groupp == NULL) {
171 			/* no more group has console deleted */
172 			(void) mutex_unlock(&vntsdp->lock);
173 			return;
174 		}
175 		(void) mutex_lock(&groupp->lock);
176 		groupp->status &= ~VNTSD_GROUP_CLEAN_CONS;
177 		(void) mutex_unlock(&groupp->lock);
178 		(void) mutex_unlock(&vntsdp->lock);
179 
180 		for (; ; ) {
181 			/* get the console to be deleted */
182 			(void) mutex_lock(&groupp->lock);
183 
184 			/* clean up any deleted console in the group */
185 			if (groupp->conspq != NULL) {
186 				consp = vntsd_que_walk(groupp->conspq,
187 				    (el_func_t)find_clean_cons);
188 				if (consp == NULL) {
189 					/* no more cons to delete */
190 					(void) mutex_unlock(&groupp->lock);
191 					break;
192 				}
193 
194 				/* remove console from the group */
195 				(void) vntsd_que_rm(&groupp->conspq, consp);
196 				(void) mutex_unlock(&groupp->lock);
197 
198 				/* clean up the console */
199 				cleanup_cons(consp);
200 			}
201 
202 			/* delete group? */
203 			if (groupp->conspq == NULL) {
204 				/* no more console in the group delete group */
205 				assert(groupp->vntsd);
206 
207 				(void) mutex_lock(&groupp->vntsd->lock);
208 				(void) vntsd_que_rm(&groupp->vntsd->grouppq,
209 						    groupp);
210 				(void) mutex_unlock(&groupp->vntsd->lock);
211 
212 				/* clean up the group */
213 				vntsd_clean_group(groupp);
214 				break;
215 			}
216 		}
217 	}
218 }
219 
220 /* clean up a group */
221 void
222 vntsd_clean_group(vntsd_group_t *groupp)
223 {
224 
225 	timestruc_t	to;
226 
227 	D1(stderr, "t@%d clean_group() group=%s tcp=%lld\n", thr_self(),
228 	    groupp->group_name, groupp->tcp_port);
229 
230 	(void) mutex_lock(&groupp->lock);
231 
232 	/* prevent from reentry */
233 	if (groupp->status & VNTSD_GROUP_IN_CLEANUP) {
234 		(void) mutex_unlock(&groupp->lock);
235 		return;
236 	}
237 	groupp->status |= VNTSD_GROUP_IN_CLEANUP;
238 
239 	/* mark group waiting for listen thread to exits */
240 	groupp->status |= VNTSD_GROUP_SIG_WAIT;
241 	(void) mutex_unlock(&groupp->lock);
242 
243 	vntsd_free_que(&groupp->conspq, (clean_func_t)cleanup_cons);
244 
245 	(void) mutex_lock(&groupp->lock);
246 	/* walk through no cons client queue */
247 	while (groupp->no_cons_clientpq != NULL) {
248 		(void) vntsd_que_walk(groupp->no_cons_clientpq,
249 		    (el_func_t)vntsd_notify_client_cons_del);
250 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
251 		to.tv_nsec = 0;
252 		(void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to);
253 	}
254 
255 	/* waiting for listen thread to exit */
256 	while (groupp->status & VNTSD_GROUP_SIG_WAIT) {
257 		/* signal listen thread to exit  */
258 		(void) thr_kill(groupp->listen_tid, SIGUSR1);
259 		to.tv_sec = VNTSD_CV_WAIT_DELTIME;
260 		to.tv_nsec = 0;
261 		/* wait listen thread to exit  */
262 		(void) cond_reltimedwait(&groupp->cvp, &groupp->lock, &to);
263 	}
264 
265 	(void) mutex_unlock(&groupp->lock);
266 	(void) thr_join(groupp->listen_tid, NULL, NULL);
267 	/* free group */
268 	free_group(groupp);
269 }
270 
271 /* allocate and initialize console structure */
272 static vntsd_cons_t *
273 alloc_cons(vntsd_group_t *groupp, vcc_console_t *consolep)
274 {
275 	vntsd_cons_t *consp;
276 	int	rv;
277 
278 	/* allocate console */
279 	consp = (vntsd_cons_t *)malloc(sizeof (vntsd_cons_t));
280 	if (consp == NULL) {
281 		vntsd_log(VNTSD_ERR_NO_MEM, "alloc_cons");
282 		return (NULL);
283 	}
284 
285 	/* intialize console */
286 	bzero(consp, sizeof (vntsd_cons_t));
287 
288 	(void) mutex_init(&consp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
289 	(void) cond_init(&consp->cvp, USYNC_THREAD, NULL);
290 
291 	consp->cons_no = consolep->cons_no;
292 	(void) strlcpy(consp->domain_name, consolep->domain_name, MAXPATHLEN);
293 	(void) strlcpy(consp->dev_name, consolep->dev_name, MAXPATHLEN);
294 	consp->wr_tid = (thread_t)-1;
295 	consp->vcc_fd = -1;
296 
297 	/* join the group */
298 	(void) mutex_lock(&groupp->lock);
299 
300 	if ((rv = vntsd_que_append(&groupp->conspq, consp)) !=
301 	    VNTSD_SUCCESS) {
302 		(void) mutex_unlock(&groupp->lock);
303 		vntsd_log(rv, "alloc_cons");
304 		free_cons(consp);
305 		return (NULL);
306 	}
307 	groupp->num_cons++;
308 	consp->group = groupp;
309 
310 	(void) mutex_unlock(&groupp->lock);
311 
312 	D1(stderr, "t@%d alloc_cons@%d %s %s\n", thr_self(),
313 	    consp->cons_no, consp->domain_name, consp->dev_name);
314 
315 	return (consp);
316 }
317 
318 /* compare tcp with group->tcp */
319 static boolean_t
320 grp_by_tcp(vntsd_group_t *groupp, uint64_t *tcp_port)
321 {
322 	assert(groupp);
323 	assert(tcp_port);
324 	return (groupp->tcp_port == *tcp_port);
325 }
326 
327 /* allocate and initialize group */
328 static vntsd_group_t *
329 alloc_group(vntsd_t *vntsdp, char *group_name, uint64_t tcp_port)
330 {
331 	vntsd_group_t *groupp;
332 
333 	/* allocate group */
334 	groupp = (vntsd_group_t *)malloc(sizeof (vntsd_group_t));
335 	if (groupp == NULL) {
336 		vntsd_log(VNTSD_ERR_NO_MEM, "alloc_group");
337 		return (NULL);
338 	}
339 
340 	/* initialize group */
341 	bzero(groupp, sizeof (vntsd_group_t));
342 
343 	(void) mutex_init(&groupp->lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
344 	(void) cond_init(&groupp->cvp, USYNC_THREAD, NULL);
345 
346 	if (group_name != NULL) {
347 		(void) memcpy(groupp->group_name, group_name, MAXPATHLEN);
348 	}
349 
350 	groupp->tcp_port = tcp_port;
351 	groupp->listen_tid = (thread_t)-1;
352 	groupp->sockfd = -1;
353 	groupp->vntsd = vntsdp;
354 
355 	D1(stderr, "t@%d alloc_group@%lld:%s\n", thr_self(), groupp->tcp_port,
356 	    groupp->group_name);
357 
358 	return (groupp);
359 }
360 
361 /* mark a deleted console */
362 boolean_t
363 vntsd_mark_deleted_cons(vntsd_cons_t *consp)
364 {
365 	(void) mutex_lock(&consp->lock);
366 	consp->status |= VNTSD_CONS_DELETED;
367 	(void) mutex_unlock(&consp->lock);
368 	return (B_FALSE);
369 }
370 
371 /*
372  * Initialize a console, if console is associated with with a
373  * new group, intialize the group.
374  */
375 static int
376 alloc_cons_with_group(vntsd_t *vntsdp, vcc_console_t *consp,
377     vntsd_group_t **new_groupp)
378 {
379 	vntsd_group_t	*groupp = NULL;
380 	int		rv;
381 
382 	*new_groupp = NULL;
383 
384 	/* match group by tcp port */
385 
386 
387 	(void) mutex_lock(&vntsdp->lock);
388 	groupp = vntsd_que_find(vntsdp->grouppq,
389 	    (compare_func_t)grp_by_tcp, (void *)&(consp->tcp_port));
390 	if (groupp != NULL)
391 		(void) mutex_lock(&groupp->lock);
392 
393 	(void) mutex_unlock(&vntsdp->lock);
394 
395 	if (groupp != NULL) {
396 		/*
397 		 *  group with same tcp port found.
398 		 *  if there is no console in the group, the
399 		 *  group should be removed and the tcp port can
400 		 *  be used for tne new group.
401 		 *  This is possible, when there is tight loop of
402 		 *  creating/deleting domains. When a vcc port is
403 		 *  removed, a read thread will have an I/O error because
404 		 *  vcc has closed the port. The read thread then marks
405 		 *  the console is removed and notify main thread to
406 		 *  remove the console.
407 		 *  Meanwhile, the same port and its group (with same
408 		 *  tcp port and group name) is created. Vcc notify
409 		 *  vntsd that new console is added.
410 		 *  Main thread now have two events. If main thread polls
411 		 *  out vcc notification first, it will find that there is
412 		 *  a group has no console.
413 		 */
414 
415 		if (vntsd_chk_group_total_cons(groupp) == 0) {
416 
417 			/* all consoles in the group have been removed */
418 			(void) vntsd_que_walk(groupp->conspq,
419 			    (el_func_t)vntsd_mark_deleted_cons);
420 			groupp->status |= VNTSD_GROUP_CLEAN_CONS;
421 			(void) mutex_unlock(&groupp->lock);
422 			groupp = NULL;
423 
424 		} else if (strcmp(groupp->group_name, consp->group_name)) {
425 			/* conflict group name */
426 			vntsd_log(VNTSD_ERR_VCC_GRP_NAME,
427 			    "group name is different from existing group");
428 			(void) mutex_unlock(&groupp->lock);
429 			return (VNTSD_ERR_VCC_CTRL_DATA);
430 
431 		} else {
432 			/* group already existed */
433 			(void) mutex_unlock(&groupp->lock);
434 		}
435 
436 	}
437 
438 	if (groupp == NULL) {
439 		/* new group */
440 		groupp = alloc_group(vntsdp, consp->group_name,
441 		    consp->tcp_port);
442 		if (groupp == NULL) {
443 			return (VNTSD_ERR_NO_MEM);
444 		}
445 
446 		assert(groupp->conspq == NULL);
447 		/* queue group to vntsdp */
448 		(void) mutex_lock(&vntsdp->lock);
449 		rv = vntsd_que_append(&vntsdp->grouppq, groupp);
450 		(void) mutex_unlock(&vntsdp->lock);
451 
452 		if (rv != VNTSD_SUCCESS) {
453 			return (rv);
454 		}
455 
456 		*new_groupp = groupp;
457 	}
458 
459 	/* intialize console */
460 	if (alloc_cons(groupp, consp) == NULL) {
461 		/* no memory */
462 		if (new_groupp != NULL) {
463 			/* clean up new group */
464 			free_group(groupp);
465 		}
466 
467 		return (VNTSD_ERR_NO_MEM);
468 	}
469 
470 	return (VNTSD_SUCCESS);
471 
472 }
473 
474 
475 /* create listen thread */
476 static boolean_t
477 create_listen_thread(vntsd_group_t *groupp)
478 {
479 
480 	char err_msg[VNTSD_LINE_LEN];
481 	int rv;
482 
483 	assert(groupp);
484 
485 	(void) mutex_lock(&groupp->lock);
486 	assert(groupp->num_cons);
487 
488 	D1(stderr, "t@%d create_listen:%lld\n", thr_self(), groupp->tcp_port);
489 
490 	if ((rv = thr_create(NULL, 0, (thr_func_t)vntsd_listen_thread,
491 			    (void *)groupp, THR_DETACHED, &groupp->listen_tid))
492 	    != 0) {
493 		(void) (void) snprintf(err_msg, sizeof (err_msg),
494 		    "Can not create listen thread for"
495 		    "group %s tcp %llx\n", groupp->group_name,
496 		    groupp->tcp_port);
497 		vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg);
498 
499 		/* clean up group queue */
500 		vntsd_free_que(&groupp->conspq, (clean_func_t)free_cons);
501 		groupp->listen_tid = (thread_t)-1;
502 	}
503 
504 	(void) mutex_unlock(&groupp->lock);
505 
506 	return (rv != 0);
507 }
508 
509 /* find deleted console by console no */
510 static boolean_t
511 deleted_cons_by_consno(vntsd_cons_t *consp, int *cons_no)
512 {
513 	vntsd_client_t *clientp;
514 
515 	assert(consp);
516 
517 	if (consp->cons_no != *cons_no)
518 		return (B_FALSE);
519 
520 	/* has console marked as deleted? */
521 	if ((consp->status & VNTSD_CONS_DELETED) == 0)
522 		return (B_TRUE);
523 
524 	if (consp->clientpq == NULL)
525 		/* there is no client for this console */
526 		return (B_TRUE);
527 
528 	/* need to notify clients of console ? */
529 	clientp = (vntsd_client_t *)consp->clientpq->handle;
530 
531 	if (clientp->status & VNTSD_CLIENT_CONS_DELETED)
532 		/* clients of console have notified */
533 		return (B_FALSE);
534 
535 	return (B_TRUE);
536 }
537 
538 /* find group structure from console no */
539 static boolean_t
540 find_cons_group_by_cons_no(vntsd_group_t *groupp, uint_t *cons_no)
541 {
542 	vntsd_cons_t *consp;
543 
544 	consp = vntsd_que_find(groupp->conspq,
545 	    (compare_func_t)deleted_cons_by_consno, cons_no);
546 	return (consp != NULL);
547 
548 }
549 
550 /* delete a console if the console exists in the vntsd */
551 static void
552 delete_cons_before_add(vntsd_t *vntsdp, uint_t cons_no)
553 {
554 	vntsd_group_t	    *groupp;
555 	vntsd_cons_t	    *consp;
556 
557 	/* group exists? */
558 	(void) mutex_lock(&vntsdp->lock);
559 	groupp = vntsd_que_find(vntsdp->grouppq,
560 	    (compare_func_t)find_cons_group_by_cons_no,
561 	    &cons_no);
562 	(void) mutex_unlock(&vntsdp->lock);
563 
564 	if (groupp == NULL) {
565 		/* no such group */
566 		return;
567 	}
568 
569 	/* group exists, if console exists? */
570 	(void) mutex_lock(&groupp->lock);
571 	consp = vntsd_que_find(groupp->conspq,
572 	    (compare_func_t)deleted_cons_by_consno, &cons_no);
573 
574 	if (consp == NULL) {
575 		/* no such console */
576 		(void) mutex_unlock(&groupp->lock);
577 		return;
578 	}
579 
580 	/* console exists - mark console for main thread to delete it */
581 	(void) mutex_lock(&consp->lock);
582 
583 	if (consp->status & VNTSD_CONS_DELETED) {
584 		/* already marked */
585 		(void) mutex_unlock(&consp->lock);
586 		(void) mutex_unlock(&groupp->lock);
587 		return;
588 	}
589 
590 	consp->status |= VNTSD_CONS_DELETED;
591 	groupp->status |= VNTSD_GROUP_CLEAN_CONS;
592 
593 	(void) mutex_unlock(&consp->lock);
594 	(void) mutex_unlock(&groupp->lock);
595 
596 }
597 
598 /* add a console */
599 static void
600 do_add_cons(vntsd_t *vntsdp, int cons_no)
601 {
602 	vcc_console_t	console;
603 	vntsd_group_t	*groupp;
604 	int		rv;
605 	char		err_msg[VNTSD_LINE_LEN];
606 
607 
608 	(void) snprintf(err_msg, sizeof (err_msg),
609 	    "do_add_cons():Can not add console=%d", cons_no);
610 
611 	/* get console configuration from vcc */
612 
613 	if ((rv = vntsd_vcc_ioctl(VCC_CONS_INFO, cons_no, (void *)&console))
614 	    != VNTSD_SUCCESS) {
615 		vntsd_log(rv, err_msg);
616 		return;
617 	}
618 
619 	/* clean up the console if console was deleted and added again */
620 	delete_cons_before_add(vntsdp, console.cons_no);
621 
622 	/* initialize console */
623 
624 	if ((rv = alloc_cons_with_group(vntsdp, &console, &groupp)) !=
625 	    VNTSD_SUCCESS) {
626 		/* no memory to add this new console */
627 		vntsd_log(rv, err_msg);
628 		return;
629 	}
630 
631 	if (groupp != NULL) {
632 		/* new group */
633 		/* create listen thread for this console */
634 		if (create_listen_thread(groupp)) {
635 			vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, err_msg);
636 			free_group(groupp);
637 		}
638 
639 	}
640 }
641 
642 /* daemon wake up */
643 void
644 vntsd_daemon_wakeup(vntsd_t *vntsdp)
645 {
646 
647 	vcc_response_t	inq_data;
648 
649 	/* reason to wake up  */
650 	if (vntsd_vcc_ioctl(VCC_INQUIRY, 0, (void *)&inq_data) !=
651 	    VNTSD_SUCCESS) {
652 		vntsd_log(VNTSD_ERR_VCC_IOCTL, "vntsd_daemon_wakeup()");
653 		return;
654 	}
655 
656 	D1(stderr, "t@%d vntsd_daemon_wakup:msg %d port %x\n", thr_self(),
657 	    inq_data.reason, inq_data.cons_no);
658 
659 	switch (inq_data.reason) {
660 
661 	case VCC_CONS_ADDED:
662 		do_add_cons(vntsdp, inq_data.cons_no);
663 		break;
664 
665 	case VCC_CONS_MISS_ADDED:
666 		/* an added port was deleted before vntsd can process it */
667 		return;
668 
669 	default:
670 		DERR(stderr, "t@%d daemon_wakeup:ioctl_unknown %d\n",
671 		    thr_self(), inq_data.reason);
672 		vntsd_log(VNTSD_ERR_UNKNOWN_CMD, "from vcc\n");
673 		break;
674 	}
675 }
676 
677 /* initial console configuration */
678 void
679 vntsd_get_config(vntsd_t *vntsdp)
680 {
681 
682 	int		i;
683 	int		num_cons;
684 	vcc_console_t	*consp;
685 	vntsd_group_t	*groupp;
686 
687 	/* num of consoles */
688 	num_cons = 0;
689 
690 	if (vntsd_vcc_ioctl(VCC_NUM_CONSOLE, 0, (void *)&num_cons) !=
691 	    VNTSD_SUCCESS) {
692 		vntsd_log(VNTSD_ERR_VCC_IOCTL, "VCC_NUM_CONSOLE failed\n");
693 		return;
694 	}
695 
696 	D3(stderr, "get_config:num_cons=%d", num_cons);
697 
698 	if (num_cons == 0) {
699 		return;
700 	}
701 
702 	/* allocate memory for all consoles */
703 	consp = malloc(num_cons*sizeof (vcc_console_t));
704 
705 	if (consp == NULL) {
706 		vntsd_log(VNTSD_ERR_NO_MEM, "for console table.");
707 		return;
708 	}
709 
710 	/* get console table */
711 	if (vntsd_vcc_ioctl(VCC_CONS_TBL, 0, (void *)consp) != VNTSD_SUCCESS) {
712 		vntsd_log(VNTSD_ERR_VCC_IOCTL, " VCC_CONS_TBL "
713 		    "for console table\n");
714 		return;
715 	}
716 
717 	/* intialize groups and consoles  */
718 	for (i = 0; i < num_cons; i++) {
719 		if (alloc_cons_with_group(vntsdp, &consp[i], &groupp)
720 		    != VNTSD_SUCCESS) {
721 			vntsd_log(VNTSD_ERR_ADD_CONS_FAILED, "get_config");
722 		}
723 	}
724 
725 	/* create listen thread for each group */
726 	(void) mutex_lock(&vntsdp->lock);
727 
728 	for (; ; ) {
729 		groupp = vntsd_que_walk(vntsdp->grouppq,
730 		    (el_func_t)create_listen_thread);
731 		if (groupp == NULL) {
732 			break;
733 		}
734 		vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR, "get config()");
735 	}
736 
737 	(void) mutex_unlock(&vntsdp->lock);
738 }
739