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