xref: /illumos-gate/usr/src/cmd/vntsd/console.c (revision cd61ae21816e53b94bc1673f3f1aa651fc3115e8)
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  * Listen thread creates a console thread whenever there is a tcp client
28  * made a conection to its port. In the console thread, if there are
29  * multiple consoles in the group, client will be asked for a console selection.
30  * a write thread for a console is created when first client connects to a
31  * selected console and console thread becomes read thread for the client.
32  */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <thread.h>
42 #include <synch.h>
43 #include <signal.h>
44 #include <assert.h>
45 #include <ctype.h>
46 #include <syslog.h>
47 #include <libintl.h>
48 #include <netdb.h>
49 #include "vntsd.h"
50 #include "chars.h"
51 
52 /*  display domain names in the group */
53 static boolean_t
54 display_domain_name(vntsd_cons_t *consp,  int  *fd)
55 {
56 	char	buf[VNTSD_LINE_LEN];
57 	char	*status;
58 
59 
60 	if (consp->clientpq != NULL) {
61 		status = gettext("connected");
62 	} else if (consp->status & VNTSD_CONS_DELETED) {
63 		status = gettext("removing...");
64 	} else {
65 		status = gettext("online");
66 	}
67 
68 	(void) snprintf(buf, sizeof (buf), "%-20d%-30s%-25s%s",
69 	    consp->cons_no, consp->domain_name, status, vntsd_eol);
70 
71 	return (vntsd_write_fd(*fd, buf, strlen(buf)) != VNTSD_SUCCESS);
72 }
73 
74 /* output connected message to tcp client */
75 static int
76 write_connect_msg(vntsd_client_t *clientp, char *group_name,
77     char *domain_name)
78 {
79 
80 	int	rv = VNTSD_SUCCESS;
81 	char	buf[VNTSD_LINE_LEN];
82 
83 	if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) !=
84 	    VNTSD_SUCCESS) {
85 		return (rv);
86 	}
87 
88 	(void) snprintf(buf, sizeof (buf),
89 	    gettext("Connecting to console \"%s\" in group \"%s\" ...."),
90 	    domain_name, group_name);
91 
92 	if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) {
93 		return (rv);
94 	}
95 
96 	if ((rv = vntsd_write_line(clientp,
97 	    gettext("Press ~? for control options .."))) != VNTSD_SUCCESS) {
98 		return (rv);
99 	}
100 
101 	return (VNTSD_SUCCESS);
102 }
103 
104 static int
105 create_write_thread(vntsd_cons_t *consp)
106 {
107 
108 	assert(consp);
109 
110 	/* create write thread for the console */
111 	(void) mutex_lock(&consp->lock);
112 	if (thr_create(NULL, 0, (thr_func_t)vntsd_write_thread,
113 	    (void *)consp, 0, &consp->wr_tid)) {
114 
115 		DERR(stderr, "t@%d create_rd_wr_thread@%d: "
116 		    "create write thread failed\n",
117 		    thr_self(), consp->cons_no);
118 		(void) close(consp->vcc_fd);
119 		consp->vcc_fd = -1;
120 		(void) mutex_unlock(&consp->lock);
121 
122 		return (VNTSD_ERR_CREATE_WR_THR);
123 	}
124 	(void) mutex_unlock(&consp->lock);
125 	return (VNTSD_SUCCESS);
126 }
127 
128 /* Display all domain consoles in a group. */
129 static int
130 list_all_domains(vntsd_group_t *groupp, vntsd_client_t *clientp)
131 {
132 	char	    vntsd_line[VNTSD_LINE_LEN];
133 	int	    rv = VNTSD_SUCCESS;
134 
135 	if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN))
136 	    != VNTSD_SUCCESS) {
137 		return (rv);
138 	}
139 
140 	/*
141 	 * TRANSLATION_NOTE
142 	 * The following three strings of the form "DOMAIN .." are table
143 	 * headers and should be all uppercase.
144 	 */
145 	(void) snprintf(vntsd_line, sizeof (vntsd_line),
146 	    "%-20s%-30s%-25s",
147 	    gettext("DOMAIN ID"), gettext("DOMAIN NAME"),
148 	    gettext("DOMAIN STATE"));
149 
150 	if ((rv = vntsd_write_line(clientp, vntsd_line)) != VNTSD_SUCCESS) {
151 		return (rv);
152 	}
153 
154 	(void) mutex_lock(&groupp->lock);
155 
156 	if (vntsd_que_find(groupp->conspq, (compare_func_t)display_domain_name,
157 	    &(clientp->sockfd)) != NULL) {
158 		rv = VNTSD_ERR_WRITE_CLIENT;
159 	}
160 
161 	(void) mutex_unlock(&groupp->lock);
162 
163 	return (rv);
164 }
165 
166 /* display help */
167 static int
168 display_help(vntsd_client_t *clientp)
169 {
170 	int	rv = VNTSD_SUCCESS;
171 	char	*bufp;
172 
173 	rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN);
174 	if (rv != VNTSD_SUCCESS) {
175 		return (rv);
176 	}
177 
178 	/*
179 	 * TRANSLATION_NOTE
180 	 * The following three strings of the form ". -- ..." are help
181 	 * messages for single character commands. Do not translate the
182 	 * character before the --.
183 	 */
184 	bufp = gettext("h -- this help");
185 
186 	if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) {
187 		return (rv);
188 	}
189 
190 	bufp = gettext("l -- list of consoles");
191 
192 	if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) {
193 		return (rv);
194 	}
195 
196 	bufp = gettext("q -- quit");
197 
198 	if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) {
199 		return (rv);
200 	}
201 
202 	/*
203 	 * TRANSLATION_NOTE
204 	 * In the following string, "id" is a short mnemonic for
205 	 * "identifier" and both occurrences should be translated.
206 	 */
207 
208 	bufp = gettext("c{id}, n{name} -- connect to a console of domain {id}"
209 	    " or domain {name}");
210 
211 	if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) {
212 		return (rv);
213 	}
214 
215 	return (VNTSD_SUCCESS);
216 }
217 
218 /* cons_by_name() - find a console structure according to  a ldom's name */
219 static boolean_t
220 cons_by_name(vntsd_cons_t *consp, char *name)
221 {
222 	if (consp->status & VNTSD_CONS_DELETED) {
223 		return (B_FALSE);
224 	}
225 	return (strcmp(consp->domain_name, name) == 0);
226 }
227 
228 /* name_to_cons_no - convert a ldom's name to its consno */
229 static int
230 name_to_cons_no(vntsd_group_t *groupp, char *name)
231 {
232 	vntsd_cons_t *consp;
233 
234 	consp = (vntsd_cons_t *)vntsd_que_find(groupp->conspq,
235 	    (compare_func_t)cons_by_name, name);
236 
237 	if (consp == NULL) {
238 		return (-1);
239 	}
240 
241 	return (consp->cons_no);
242 }
243 
244 /* select a console to connect */
245 static int
246 select_cons(vntsd_group_t *groupp, vntsd_cons_t **consp,
247     vntsd_client_t *clientp, char c)
248 {
249 	int	    cons_no = -1;
250 	int	    n;
251 	int	    i;
252 	char	    buf[VNTSD_LINE_LEN];
253 	int	    rv;
254 
255 
256 
257 	(void) mutex_lock(&groupp->lock);
258 	if (groupp->num_cons == 0) {
259 		(void) mutex_unlock(&groupp->lock);
260 		/* no console in this group */
261 		return (VNTSD_STATUS_NO_CONS);
262 	}
263 	(void) mutex_unlock(&groupp->lock);
264 
265 
266 	/* c{id} or n{name} */
267 
268 	n = VNTSD_LINE_LEN;
269 
270 	if ((rv = vntsd_read_line(clientp, buf, &n)) != VNTSD_SUCCESS) {
271 		return (rv);
272 	}
273 
274 	/* parse command */
275 	for (i = 0; i < n; i++) {
276 		switch (c) {
277 
278 		case 'c':
279 			/* c{id} or c {id} */
280 			if (isspace(buf[i])) {
281 				continue;
282 			}
283 
284 			if (!isdigit(buf[i])) {
285 				return (VNTSD_ERR_INVALID_INPUT);
286 			}
287 
288 			cons_no = atoi(buf + i);
289 			break;
290 
291 		case 'n':
292 			/* n{name) or n {name} */
293 			if (isspace(buf[i])) {
294 				continue;
295 			}
296 
297 			buf[n-1] = 0;
298 			cons_no = name_to_cons_no(groupp, buf+i);
299 			break;
300 
301 		default:
302 			/* should never get here */
303 			return (VNTSD_ERR_INVALID_INPUT);
304 
305 		}
306 
307 		/* got user selection */
308 		break;
309 	}
310 
311 	if (cons_no < 0) {
312 		return (VNTSD_ERR_INVALID_INPUT);
313 	}
314 
315 	/* get selected console */
316 	(void) mutex_lock(&groupp->lock);
317 
318 	*consp = (vntsd_cons_t *)vntsd_que_find(groupp->conspq,
319 	    (compare_func_t)vntsd_cons_by_consno, &cons_no);
320 
321 	if (*consp == NULL) {
322 		/* during console selection, the console has been  deleted */
323 		(void) mutex_unlock(&groupp->lock);
324 
325 		return (VNTSD_ERR_INVALID_INPUT);
326 	}
327 	if ((*consp)->status & VNTSD_CONS_DELETED) {
328 		return (VNTSD_ERR_INVALID_INPUT);
329 	}
330 
331 	(void) mutex_unlock(&groupp->lock);
332 
333 	return (VNTSD_SUCCESS);
334 }
335 
336 /* compare if there is a match console in the gorup */
337 static boolean_t
338 find_cons_in_group(vntsd_cons_t *consp_in_group, vntsd_cons_t *consp)
339 {
340 	if (consp_in_group == consp) {
341 		return (B_TRUE);
342 	} else {
343 		return (B_FALSE);
344 	}
345 }
346 
347 /* connect a client to a console */
348 static int
349 connect_cons(vntsd_cons_t *consp, vntsd_client_t *clientp)
350 {
351 	int	rv, rv1;
352 	vntsd_group_t *groupp;
353 
354 	assert(consp);
355 	groupp = consp->group;
356 	assert(groupp);
357 	assert(clientp);
358 
359 	(void) mutex_lock(&groupp->lock);
360 
361 	/* check if console is valid */
362 	consp = vntsd_que_find(groupp->conspq,
363 	    (compare_func_t)find_cons_in_group, consp);
364 
365 	if (consp == NULL) {
366 		(void) mutex_unlock(&groupp->lock);
367 		return (VNTSD_STATUS_NO_CONS);
368 	}
369 	if (consp->status & VNTSD_CONS_DELETED) {
370 		(void) mutex_unlock(&groupp->lock);
371 		return (VNTSD_STATUS_NO_CONS);
372 	}
373 
374 	(void) mutex_lock(&consp->lock);
375 	(void) mutex_lock(&clientp->lock);
376 
377 
378 	clientp->cons = consp;
379 
380 	/* enable daemon cmd */
381 	clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD;
382 
383 	if (consp->clientpq == NULL && consp->vcc_fd == -1) {
384 
385 		/*
386 		 *  the first connection to a console - a writer
387 		 *  and the console has not opened.
388 		 */
389 		consp->vcc_fd = vntsd_open_vcc(consp->dev_name, consp->cons_no);
390 		if (consp->vcc_fd < 0) {
391 			(void) mutex_unlock(&clientp->lock);
392 			(void) mutex_unlock(&consp->lock);
393 			(void) mutex_unlock(&groupp->lock);
394 			assert(consp->group);
395 			return (vntsd_vcc_err(consp));
396 		}
397 	}
398 
399 	(void) mutex_unlock(&clientp->lock);
400 
401 	/*
402 	 * move the client from group's no console selected queue
403 	 * to cons queue
404 	 */
405 
406 	rv = vntsd_que_rm(&groupp->no_cons_clientpq, clientp);
407 	assert(rv == VNTSD_SUCCESS);
408 
409 	rv = vntsd_que_append(&consp->clientpq, clientp);
410 	(void) mutex_unlock(&groupp->lock);
411 
412 	if (rv != VNTSD_SUCCESS) {
413 		if (consp->clientpq->handle == clientp) {
414 			/* writer */
415 			(void) close(consp->vcc_fd);
416 			consp->vcc_fd = -1;
417 		}
418 
419 		(void) mutex_unlock(&consp->lock);
420 		return (rv);
421 	}
422 
423 	(void) mutex_unlock(&consp->lock);
424 
425 	if (consp->clientpq->handle == clientp) {
426 		/* create a write thread */
427 		rv = create_write_thread(consp);
428 		if (rv != VNTSD_SUCCESS) {
429 			return (rv);
430 		}
431 	}
432 
433 	/* write connecting message */
434 	if ((rv = write_connect_msg(clientp, consp->group->group_name,
435 	    consp->domain_name)) != VNTSD_SUCCESS) {
436 			return (rv);
437 	}
438 
439 	/* process input from client */
440 	rv = vntsd_read(clientp);
441 
442 	/* client disconnected from the console */
443 	(void) mutex_lock(&groupp->lock);
444 
445 	/* remove client from console queue */
446 	(void) mutex_lock(&consp->lock);
447 	rv1 = vntsd_que_rm(&consp->clientpq, clientp);
448 	assert(rv1 == VNTSD_SUCCESS);
449 
450 	/* append client to group's no console selected  queue */
451 	rv1 = vntsd_que_append(&groupp->no_cons_clientpq, clientp);
452 	(void) mutex_unlock(&groupp->lock);
453 
454 	if (consp->clientpq == NULL) {
455 		/* clean up console since there is no client connected to it */
456 		assert(consp->vcc_fd != -1);
457 
458 		/* force write thread to exit */
459 		assert(consp->wr_tid != (thread_t)-1);
460 		(void) thr_kill(consp->wr_tid, SIGUSR1);
461 		(void) mutex_unlock(&consp->lock);
462 		(void) thr_join(consp->wr_tid, NULL, NULL);
463 		(void) mutex_lock(&consp->lock);
464 	}
465 
466 	if (consp->status & VNTSD_CONS_SIG_WAIT) {
467 		/* console is waiting for client to disconnect */
468 		(void) cond_signal(&consp->cvp);
469 	}
470 
471 	(void) mutex_unlock(&consp->lock);
472 
473 	return (rv1 == VNTSD_SUCCESS ? rv : rv1);
474 
475 }
476 
477 /* read command line input */
478 static int
479 read_cmd(vntsd_client_t *clientp, char *prompt, char *cmd)
480 {
481 	int		rv;
482 
483 	/* disable daemon special command */
484 	(void) mutex_lock(&clientp->lock);
485 	clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD;
486 	(void) mutex_unlock(&clientp->lock);
487 
488 	rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN);
489 	if (rv != VNTSD_SUCCESS) {
490 		return (rv);
491 	}
492 
493 	rv = vntsd_write_client(clientp, prompt, strlen(prompt));
494 	if (rv != VNTSD_SUCCESS) {
495 		return (rv);
496 	}
497 
498 	if ((rv = vntsd_read_data(clientp, cmd)) != VNTSD_SUCCESS) {
499 		return (rv);
500 	}
501 	if (*cmd == BS) {
502 		return (VNTSD_SUCCESS);
503 	}
504 
505 	rv = vntsd_write_client(clientp, cmd, 1);
506 
507 	*cmd = tolower(*cmd);
508 
509 	return (rv);
510 }
511 
512 /* reset client for selecting a console in the group */
513 static void
514 client_init(vntsd_client_t *clientp)
515 {
516 	(void) mutex_lock(&clientp->lock);
517 	clientp->cons = NULL;
518 	clientp->status = 0;
519 	(void) mutex_unlock(&clientp->lock);
520 }
521 /* is there any connection to a given console? */
522 static boolean_t
523 is_client_que_empty(vntsd_cons_t *consp)
524 {
525 	boolean_t  has_client = B_FALSE;
526 
527 	(void) mutex_lock(&consp->lock);
528 
529 	if (consp->clientpq != NULL)
530 		has_client = B_TRUE;
531 
532 	(void) mutex_unlock(&consp->lock);
533 
534 	return (has_client);
535 }
536 
537 /*
538  * close one opened console.
539  * This function is passed to vntsd_que_walk to close one console.
540  * The function returns B_FALSE so that vntsd_que_walk will
541  * continue to apply the function to all consoles in the group.
542  */
543 static boolean_t
544 close_one_vcc_fd(vntsd_cons_t *consp)
545 {
546 	(void) mutex_lock(&consp->lock);
547 
548 	if (consp->vcc_fd != -1) {
549 		(void) close(consp->vcc_fd);
550 		consp->vcc_fd = -1;
551 	}
552 
553 	(void) mutex_unlock(&consp->lock);
554 
555 	return (B_FALSE);
556 }
557 
558 
559 /* clean up client and exit the thread */
560 static void
561 client_fini(vntsd_group_t *groupp, vntsd_client_t *clientp)
562 {
563 
564 	assert(groupp);
565 	assert(clientp);
566 
567 	/* disconnct client from tcp port */
568 	assert(clientp->sockfd != -1);
569 	(void) close(clientp->sockfd);
570 
571 	(void) mutex_lock(&groupp->lock);
572 
573 	/*
574 	 * close all consoles in the group if the client is the
575 	 * last one connected to the group
576 	 */
577 	if (vntsd_que_walk(groupp->conspq, (el_func_t)is_client_que_empty) ==
578 	    VNTSD_SUCCESS) {
579 		(void) vntsd_que_walk(groupp->conspq,
580 		    (el_func_t)close_one_vcc_fd);
581 	}
582 
583 
584 	(void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp);
585 
586 	if ((groupp->no_cons_clientpq == NULL) &&
587 	    (groupp->status & VNTSD_GROUP_SIG_WAIT)) {
588 		/*
589 		 * group is waiting to be deleted. - signal the group's
590 		 * listen thread - the VNTSD_GROUP_SIG_WAIT state will
591 		 * be cleared when the listen thread exits.
592 		 */
593 		(void) cond_signal(&groupp->cvp);
594 	}
595 	(void) mutex_unlock(&groupp->lock);
596 
597 	(void) mutex_destroy(&clientp->lock);
598 	free(clientp);
599 
600 	thr_exit(0);
601 }
602 
603 /*  check client's status. exit if client quits or fatal errors */
604 static void
605 console_chk_status(vntsd_group_t *groupp, vntsd_client_t *clientp, int status)
606 {
607 	char    err_msg[VNTSD_LINE_LEN];
608 
609 	D1(stderr, "t@%d console_chk_status() status=%d "
610 	    "client status=%x num consoles=%d \n",
611 	    thr_self(), status, clientp->status, groupp->num_cons);
612 
613 	(void) snprintf(err_msg, VNTSD_LINE_LEN, "console_chk_status client%d"
614 	    " num_cos=%d", clientp->sockfd, groupp->num_cons);
615 
616 	/*
617 	 * obtain group lock to protect groupp->num_cons.
618 	 * When groupp->num_cons == 0, close client and exit the tread.
619 	 */
620 	(void) mutex_lock(&groupp->lock);
621 
622 	if (groupp->num_cons == 0) {
623 		/* no more console in the group */
624 		(void) mutex_unlock(&groupp->lock);
625 		client_fini(groupp, clientp);
626 		return;
627 	}
628 
629 	if (status == VNTSD_STATUS_INTR) {
630 		/* reason for signal? */
631 		status = vntsd_cons_chk_intr(clientp);
632 	}
633 
634 	switch (status) {
635 
636 	case VNTSD_STATUS_CLIENT_QUIT:
637 		(void) mutex_unlock(&groupp->lock);
638 		client_fini(groupp, clientp);
639 		return;
640 
641 	case VNTSD_STATUS_RESELECT_CONS:
642 
643 		if (clientp->cons == NULL) {
644 			/*
645 			 * domain was deleted before client connects to it
646 			 * connect to other console in the same group
647 			 */
648 			(void) mutex_unlock(&groupp->lock);
649 			client_init(clientp);
650 			return;
651 		}
652 
653 		if ((groupp->num_cons == 1) &&
654 		    ((clientp->status & VNTSD_CLIENT_CONS_DELETED) ||
655 		    (groupp->conspq->handle == clientp->cons))) {
656 			/* no other selection available */
657 			(void) mutex_unlock(&groupp->lock);
658 			client_fini(groupp, clientp);
659 		} else {
660 			(void) mutex_unlock(&groupp->lock);
661 			client_init(clientp);
662 		}
663 
664 		return;
665 
666 	case VNTSD_STATUS_VCC_IO_ERR:
667 		if ((clientp->status & VNTSD_CLIENT_CONS_DELETED) == 0) {
668 			/* check if console was deleted  */
669 			(void) mutex_unlock(&groupp->lock);
670 			status = vntsd_vcc_err(clientp->cons);
671 			(void) mutex_lock(&groupp->lock);
672 		}
673 
674 		if (status != VNTSD_STATUS_CONTINUE) {
675 			/* console was deleted */
676 			if (groupp->num_cons <= 1) {
677 				(void) mutex_unlock(&groupp->lock);
678 				client_fini(groupp, clientp);
679 				return;
680 			}
681 		}
682 
683 		(void) mutex_unlock(&groupp->lock);
684 		/* console is ok */
685 		client_init(clientp);
686 		return;
687 
688 	case VNTSD_STATUS_MOV_CONS_FORWARD:
689 	case VNTSD_STATUS_MOV_CONS_BACKWARD:
690 		if (groupp->num_cons == 1) {
691 			/* same console */
692 			(void) mutex_unlock(&groupp->lock);
693 			return;
694 		}
695 
696 		/* get selected console */
697 		clientp->cons = vntsd_que_pos(groupp->conspq,
698 		    clientp->cons,
699 		    (status == VNTSD_STATUS_MOV_CONS_FORWARD)?(1):(-1));
700 		(void) mutex_unlock(&groupp->lock);
701 		return;
702 
703 	case VNTSD_SUCCESS:
704 	case VNTSD_STATUS_CONTINUE:
705 		(void) mutex_unlock(&groupp->lock);
706 		client_init(clientp);
707 		return;
708 
709 
710 	case VNTSD_STATUS_NO_CONS:
711 		/*
712 		 * there are two cases when the status is VNTSD_SATATUS_NO_CONS.
713 		 * case 1. the console was removed but there is at least one
714 		 * another console in the group that client can connect to.
715 		 * case 2. there is no console in the group. Client needs to
716 		 * be disconnected from vntsd.
717 		 */
718 		if (groupp->num_cons == 0) {
719 			(void) mutex_unlock(&groupp->lock);
720 			client_fini(groupp, clientp);
721 		} else {
722 			(void) mutex_unlock(&groupp->lock);
723 			client_init(clientp);
724 		}
725 		return;
726 
727 
728 	case VNTSD_ERR_INVALID_INPUT:
729 		(void) mutex_unlock(&groupp->lock);
730 		return;
731 
732 	default:
733 		/* fatal error */
734 		(void) mutex_unlock(&groupp->lock);
735 		vntsd_log(status, err_msg);
736 		client_fini(groupp, clientp);
737 		return;
738 	}
739 }
740 
741 /* console thread */
742 void *
743 vntsd_console_thread(vntsd_thr_arg_t *argp)
744 {
745 	vntsd_group_t	    *groupp;
746 	vntsd_cons_t	    *consp;
747 	vntsd_client_t	    *clientp;
748 
749 	char		    buf[MAXHOSTNAMELEN];
750 	char		    prompt[72];
751 	char		    cmd;
752 	int		    rv = VNTSD_SUCCESS;
753 	int		    num_cons;
754 
755 
756 	groupp = (vntsd_group_t *)argp->handle;
757 	clientp = (vntsd_client_t *)argp->arg;
758 
759 	assert(groupp);
760 	assert(clientp);
761 
762 	/* free argp, which was allocated in listen thread */
763 	free(argp);
764 
765 	/* check if group is removed */
766 
767 	D1(stderr, "t@%d get_client_sel@%lld:client@%d\n", thr_self(),
768 	    groupp->tcp_port, clientp->sockfd);
769 
770 	bzero(buf, MAXHOSTNAMELEN);
771 
772 	/* host name */
773 	if (gethostname(buf, MAXHOSTNAMELEN)) {
774 		vntsd_log(VNTSD_STATUS_NO_HOST_NAME, "vntsd_console_thread()");
775 		(void) snprintf(buf, sizeof (buf), "unkown host");
776 	}
777 
778 	if (snprintf(prompt, sizeof (prompt),
779 	    "%s-vnts-%s: h, l, c{id}, n{name}, q:",
780 	    buf, groupp->group_name) >= sizeof (prompt)) {
781 		/* long prompt doesn't fit, use short one */
782 		(void) snprintf(prompt, sizeof (prompt),
783 		    "vnts: h, l, c{id}, n{name}, q:");
784 	}
785 
786 
787 	for (;;) {
788 		cmd = ' ';
789 		D1(stderr, "t@%d console_thread()@%lld:client@%d\n", thr_self(),
790 		    groupp->tcp_port, clientp->sockfd);
791 
792 		num_cons = vntsd_chk_group_total_cons(groupp);
793 
794 		if ((num_cons > 1) && (clientp->cons == NULL)) {
795 			/*  console to connect to */
796 			rv = read_cmd(clientp, prompt, &cmd);
797 			/* check error and may exit */
798 			console_chk_status(groupp, clientp, rv);
799 
800 			/* any console is removed from group? */
801 			num_cons = vntsd_chk_group_total_cons(groupp);
802 			if (num_cons <= 1) {
803 				cmd = ' ';
804 			}
805 		}
806 
807 		switch (cmd) {
808 
809 		case 'l':
810 
811 			/* list domain names */
812 			rv = list_all_domains(groupp, clientp);
813 			break;
814 
815 
816 		case 'q':
817 
818 			rv = VNTSD_STATUS_CLIENT_QUIT;
819 			break;
820 
821 		case ' ':
822 
823 			if (num_cons == 0) {
824 				/* no console in the group */
825 				rv = VNTSD_STATUS_NO_CONS;
826 				break;
827 			}
828 
829 			if (clientp->cons == NULL) {
830 				if (num_cons == 1) {
831 					/* by pass selecting console */
832 					consp = (vntsd_cons_t *)
833 					    (groupp->conspq->handle);
834 				} else {
835 					continue;
836 				}
837 
838 			} else {
839 				consp = clientp->cons;
840 			}
841 
842 			/* connect to console */
843 			rv = connect_cons(consp, clientp);
844 
845 			break;
846 
847 		case 'c':
848 		case 'n':
849 			/* select console */
850 			if (clientp->cons == NULL) {
851 				rv = select_cons(groupp, &consp, clientp, cmd);
852 				if (rv == VNTSD_ERR_INVALID_INPUT) {
853 					rv = display_help(clientp);
854 					break;
855 				}
856 
857 				/*
858 				 * all consoles in the group
859 				 * may be gone before this client
860 				 * could select one.
861 				 */
862 				if (rv != VNTSD_SUCCESS)
863 					break;
864 
865 			} else {
866 				consp = clientp->cons;
867 			}
868 			assert(consp);
869 
870 			/* connect to console */
871 			rv = connect_cons(consp, clientp);
872 			D1(stderr, "t@%d console_thread()"
873 			    "connect_cons returns %d\n",
874 			    thr_self(), rv);
875 			break;
876 
877 		case 'h':
878 		default:
879 			rv = display_help(clientp);
880 			break;
881 
882 		}
883 
884 		/* check error and may  exit */
885 		console_chk_status(groupp, clientp, rv);
886 	}
887 
888 	/*NOTREACHED*/
889 	return (NULL);
890 }
891