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