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 */
54static boolean_t
55display_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 */
76static int
77write_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
106static int
107create_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. */
131static int
132list_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 */
169static int
170display_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 */
221static boolean_t
222cons_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 */
231static int
232name_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 */
247static int
248select_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 */
339static boolean_t
340find_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 */
350static int
351connect_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 */
480static int
481read_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 */
515static void
516client_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? */
524static boolean_t
525is_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 */
545static boolean_t
546close_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 */
562static void
563client_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 */
606static void
607console_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 */
744void *
745vntsd_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