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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 #pragma ident "%Z%%M% %I% %E% SMI"
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.
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
53 /* display domain names in the group */
55 display_domain_name(vntsd_cons_t
*consp
, int *fd
)
57 char buf
[VNTSD_LINE_LEN
];
61 if (consp
->clientpq
!= NULL
) {
62 status
= gettext("connected");
63 } else if (consp
->status
& VNTSD_CONS_DELETED
) {
64 status
= gettext("removing...");
66 status
= gettext("online");
69 (void) snprintf(buf
, sizeof (buf
), "%-20d%-30s%-25s%s",
70 consp
->cons_no
, consp
->domain_name
, status
, vntsd_eol
);
72 return (vntsd_write_fd(*fd
, buf
, strlen(buf
)) != VNTSD_SUCCESS
);
75 /* output connected message to tcp client */
77 write_connect_msg(vntsd_client_t
*clientp
, char *group_name
,
81 int rv
= VNTSD_SUCCESS
;
82 char buf
[VNTSD_LINE_LEN
];
84 if ((rv
= vntsd_write_client(clientp
, vntsd_eol
, VNTSD_EOL_LEN
)) !=
89 (void) snprintf(buf
, sizeof (buf
),
90 gettext("Connecting to console \"%s\" in group \"%s\" ...."),
91 domain_name
, group_name
);
93 if ((rv
= vntsd_write_line(clientp
, buf
)) != VNTSD_SUCCESS
) {
97 if ((rv
= vntsd_write_line(clientp
,
98 gettext("Press ~? for control options .."))) !=
103 return (VNTSD_SUCCESS
);
107 create_write_thread(vntsd_cons_t
*consp
)
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
)) {
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
);
122 (void) mutex_unlock(&consp
->lock
);
124 return (VNTSD_ERR_CREATE_WR_THR
);
126 (void) mutex_unlock(&consp
->lock
);
127 return (VNTSD_SUCCESS
);
130 /* Display all domain consoles in a group. */
132 list_all_domains(vntsd_group_t
*groupp
, vntsd_client_t
*clientp
)
134 char vntsd_line
[VNTSD_LINE_LEN
];
135 int rv
= VNTSD_SUCCESS
;
137 if ((rv
= vntsd_write_client(clientp
, vntsd_eol
, VNTSD_EOL_LEN
))
144 * The following three strings of the form "DOMAIN .." are table
145 * headers and should be all uppercase.
147 (void) snprintf(vntsd_line
, sizeof (vntsd_line
),
149 gettext("DOMAIN ID"), gettext("DOMAIN NAME"),
150 gettext("DOMAIN STATE"));
152 if ((rv
= vntsd_write_line(clientp
, vntsd_line
)) != VNTSD_SUCCESS
) {
156 (void) mutex_lock(&groupp
->lock
);
158 if (vntsd_que_find(groupp
->conspq
, (compare_func_t
)display_domain_name
,
159 &(clientp
->sockfd
)) != NULL
) {
160 rv
= VNTSD_ERR_WRITE_CLIENT
;
163 (void) mutex_unlock(&groupp
->lock
);
170 display_help(vntsd_client_t
*clientp
)
172 int rv
= VNTSD_SUCCESS
;
175 if ((rv
= vntsd_write_client(clientp
, vntsd_eol
, VNTSD_EOL_LEN
))
182 * The following three strings of the form ". -- ..." are help
183 * messages for single character commands. Do not translate the
184 * character before the --.
186 bufp
= gettext("h -- this help");
188 if ((rv
= vntsd_write_line(clientp
, bufp
)) != VNTSD_SUCCESS
) {
192 bufp
= gettext("l -- list of consoles");
194 if ((rv
= vntsd_write_line(clientp
, bufp
)) != VNTSD_SUCCESS
) {
198 bufp
= gettext("q -- quit");
200 if ((rv
= vntsd_write_line(clientp
, bufp
)) != VNTSD_SUCCESS
) {
206 * In the following string, "id" is a short mnemonic for
207 * "identifier" and both occurrences should be translated.
210 bufp
= gettext("c{id}, n{name} -- connect to a console of domain {id}"
211 " or domain {name}");
213 if ((rv
= vntsd_write_line(clientp
, bufp
)) != VNTSD_SUCCESS
) {
217 return (VNTSD_SUCCESS
);
220 /* cons_by_name() - find a console structure according to a ldom's name */
222 cons_by_name(vntsd_cons_t
*consp
, char *name
)
224 if (consp
->status
& VNTSD_CONS_DELETED
) {
227 return (strcmp(consp
->domain_name
, name
) == 0);
230 /* name_to_cons_no - convert a ldom's name to its consno */
232 name_to_cons_no(vntsd_group_t
*groupp
, char *name
)
236 consp
= (vntsd_cons_t
*)vntsd_que_find(groupp
->conspq
,
237 (compare_func_t
)cons_by_name
, name
);
243 return (consp
->cons_no
);
246 /* select a console to connect */
248 select_cons(vntsd_group_t
*groupp
, vntsd_cons_t
**consp
,
249 vntsd_client_t
*clientp
, char c
)
254 char buf
[VNTSD_LINE_LEN
];
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
);
265 (void) mutex_unlock(&groupp
->lock
);
268 /* c{id} or n{name} */
272 if ((rv
= vntsd_read_line(clientp
, buf
, &n
)) != VNTSD_SUCCESS
) {
277 for (i
= 0; i
< n
; i
++) {
281 /* c{id} or c {id} */
282 if (isspace(buf
[i
])) {
286 if (!isdigit(buf
[i
])) {
287 return (VNTSD_ERR_INVALID_INPUT
);
290 cons_no
= atoi(buf
+ i
);
294 /* n{name) or n {name} */
295 if (isspace(buf
[i
])) {
300 cons_no
= name_to_cons_no(groupp
, buf
+i
);
304 /* should never get here */
305 return (VNTSD_ERR_INVALID_INPUT
);
309 /* got user selection */
314 return (VNTSD_ERR_INVALID_INPUT
);
317 /* get selected console */
318 (void) mutex_lock(&groupp
->lock
);
320 *consp
= (vntsd_cons_t
*)vntsd_que_find(groupp
->conspq
,
321 (compare_func_t
)vntsd_cons_by_consno
, &cons_no
);
323 if (*consp
== NULL
) {
324 /* during console selection, the console has been deleted */
325 (void) mutex_unlock(&groupp
->lock
);
327 return (VNTSD_ERR_INVALID_INPUT
);
329 if ((*consp
)->status
& VNTSD_CONS_DELETED
) {
330 return (VNTSD_ERR_INVALID_INPUT
);
333 (void) mutex_unlock(&groupp
->lock
);
335 return (VNTSD_SUCCESS
);
338 /* compare if there is a match console in the gorup */
340 find_cons_in_group(vntsd_cons_t
*consp_in_group
, vntsd_cons_t
*consp
)
342 if (consp_in_group
== consp
) {
349 /* connect a client to a console */
351 connect_cons(vntsd_cons_t
*consp
, vntsd_client_t
*clientp
)
354 vntsd_group_t
*groupp
;
357 groupp
= consp
->group
;
361 (void) mutex_lock(&groupp
->lock
);
363 /* check if console is valid */
364 consp
= vntsd_que_find(groupp
->conspq
,
365 (compare_func_t
)find_cons_in_group
, consp
);
368 (void) mutex_unlock(&groupp
->lock
);
369 return (VNTSD_STATUS_NO_CONS
);
371 if (consp
->status
& VNTSD_CONS_DELETED
) {
372 (void) mutex_unlock(&groupp
->lock
);
373 return (VNTSD_STATUS_NO_CONS
);
376 (void) mutex_lock(&consp
->lock
);
377 (void) mutex_lock(&clientp
->lock
);
380 clientp
->cons
= consp
;
382 /* enable daemon cmd */
383 clientp
->status
&= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD
;
385 if (consp
->clientpq
== NULL
&& consp
->vcc_fd
== -1) {
388 * the first connection to a console - a writer
389 * and the console has not opened.
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
));
401 (void) mutex_unlock(&clientp
->lock
);
404 * move the client from group's no console selected queue
408 rv
= vntsd_que_rm(&groupp
->no_cons_clientpq
, clientp
);
409 assert(rv
== VNTSD_SUCCESS
);
411 rv
= vntsd_que_append(&consp
->clientpq
, clientp
);
412 (void) mutex_unlock(&groupp
->lock
);
414 if (rv
!= VNTSD_SUCCESS
) {
415 if (consp
->clientpq
->handle
== clientp
) {
417 (void) close(consp
->vcc_fd
);
421 (void) mutex_unlock(&consp
->lock
);
425 (void) mutex_unlock(&consp
->lock
);
427 if (consp
->clientpq
->handle
== clientp
) {
428 /* create a write thread */
429 rv
= create_write_thread(consp
);
430 if (rv
!= VNTSD_SUCCESS
) {
435 /* write connecting message */
436 if ((rv
= write_connect_msg(clientp
, consp
->group
->group_name
,
437 consp
->domain_name
)) != VNTSD_SUCCESS
) {
441 /* process input from client */
442 rv
= vntsd_read(clientp
);
444 /* client disconnected from the console */
445 (void) mutex_lock(&groupp
->lock
);
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
);
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
);
456 if (consp
->clientpq
== NULL
) {
457 /* clean up console since there is no client connected to it */
458 assert(consp
->vcc_fd
!= -1);
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
);
468 if (consp
->status
& VNTSD_CONS_SIG_WAIT
) {
469 /* console is waiting for client to disconnect */
470 (void) cond_signal(&consp
->cvp
);
473 (void) mutex_unlock(&consp
->lock
);
475 return (rv1
== VNTSD_SUCCESS
? rv
: rv1
);
479 /* read command line input */
481 read_cmd(vntsd_client_t
*clientp
, char *prompt
, char *cmd
)
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
);
490 if ((rv
= vntsd_write_client(clientp
, vntsd_eol
, VNTSD_EOL_LEN
))
495 if ((rv
= vntsd_write_client(clientp
, prompt
, strlen(prompt
)))
500 if ((rv
= vntsd_read_data(clientp
, cmd
)) != VNTSD_SUCCESS
) {
504 return (VNTSD_SUCCESS
);
507 rv
= vntsd_write_client(clientp
, cmd
, 1);
509 *cmd
= tolower(*cmd
);
514 /* reset client for selecting a console in the group */
516 client_init(vntsd_client_t
*clientp
)
518 (void) mutex_lock(&clientp
->lock
);
519 clientp
->cons
= NULL
;
521 (void) mutex_unlock(&clientp
->lock
);
523 /* is there any connection to a given console? */
525 is_client_que_empty(vntsd_cons_t
*consp
)
527 boolean_t has_client
= B_FALSE
;
529 (void) mutex_lock(&consp
->lock
);
531 if (consp
->clientpq
!= NULL
)
534 (void) mutex_unlock(&consp
->lock
);
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.
546 close_one_vcc_fd(vntsd_cons_t
*consp
)
548 (void) mutex_lock(&consp
->lock
);
550 if (consp
->vcc_fd
!= -1) {
551 (void) close(consp
->vcc_fd
);
555 (void) mutex_unlock(&consp
->lock
);
561 /* clean up client and exit the thread */
563 client_fini(vntsd_group_t
*groupp
, vntsd_client_t
*clientp
)
569 /* disconnct client from tcp port */
570 assert(clientp
->sockfd
!= -1);
571 (void) close(clientp
->sockfd
);
573 (void) mutex_lock(&groupp
->lock
);
576 * close all consoles in the group if the client is the
577 * last one connected to the group
579 if (vntsd_que_walk(groupp
->conspq
, (el_func_t
)is_client_que_empty
) ==
581 (void) vntsd_que_walk(groupp
->conspq
,
582 (el_func_t
)close_one_vcc_fd
);
586 (void) vntsd_que_rm(&groupp
->no_cons_clientpq
, clientp
);
588 if ((groupp
->no_cons_clientpq
== NULL
) &&
589 (groupp
->status
& VNTSD_GROUP_SIG_WAIT
)) {
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.
595 (void) cond_signal(&groupp
->cvp
);
597 (void) mutex_unlock(&groupp
->lock
);
599 (void) mutex_destroy(&clientp
->lock
);
605 /* check client's status. exit if client quits or fatal errors */
607 console_chk_status(vntsd_group_t
*groupp
, vntsd_client_t
*clientp
, int status
)
609 char err_msg
[VNTSD_LINE_LEN
];
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
);
615 (void) snprintf(err_msg
, VNTSD_LINE_LEN
, "console_chk_status client%d"
616 " num_cos=%d", clientp
->sockfd
, groupp
->num_cons
);
619 * obtain group lock to protect groupp->num_cons.
620 * When groupp->num_cons == 0, close client and exit the tread.
622 (void) mutex_lock(&groupp
->lock
);
624 if (groupp
->num_cons
== 0) {
625 /* no more console in the group */
626 (void) mutex_unlock(&groupp
->lock
);
627 client_fini(groupp
, clientp
);
631 if (status
== VNTSD_STATUS_INTR
) {
632 /* reason for signal? */
633 status
= vntsd_cons_chk_intr(clientp
);
638 case VNTSD_STATUS_CLIENT_QUIT
:
639 (void) mutex_unlock(&groupp
->lock
);
640 client_fini(groupp
, clientp
);
643 case VNTSD_STATUS_RESELECT_CONS
:
645 if (clientp
->cons
== NULL
) {
647 * domain was deleted before client connects to it
648 * connect to other console in the same group
650 (void) mutex_unlock(&groupp
->lock
);
651 client_init(clientp
);
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
);
662 (void) mutex_unlock(&groupp
->lock
);
663 client_init(clientp
);
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
);
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
);
685 (void) mutex_unlock(&groupp
->lock
);
687 client_init(clientp
);
690 case VNTSD_STATUS_MOV_CONS_FORWARD
:
691 case VNTSD_STATUS_MOV_CONS_BACKWARD
:
692 if (groupp
->num_cons
== 1) {
694 (void) mutex_unlock(&groupp
->lock
);
698 /* get selected console */
699 clientp
->cons
= vntsd_que_pos(groupp
->conspq
,
701 (status
== VNTSD_STATUS_MOV_CONS_FORWARD
)?(1):(-1));
702 (void) mutex_unlock(&groupp
->lock
);
706 case VNTSD_STATUS_CONTINUE
:
707 (void) mutex_unlock(&groupp
->lock
);
708 client_init(clientp
);
712 case VNTSD_STATUS_NO_CONS
:
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.
720 if (groupp
->num_cons
== 0) {
721 (void) mutex_unlock(&groupp
->lock
);
722 client_fini(groupp
, clientp
);
724 (void) mutex_unlock(&groupp
->lock
);
725 client_init(clientp
);
730 case VNTSD_ERR_INVALID_INPUT
:
731 (void) mutex_unlock(&groupp
->lock
);
736 (void) mutex_unlock(&groupp
->lock
);
737 vntsd_log(status
, err_msg
);
738 client_fini(groupp
, clientp
);
745 vntsd_console_thread(vntsd_thr_arg_t
*argp
)
747 vntsd_group_t
*groupp
;
749 vntsd_client_t
*clientp
;
751 char buf
[MAXHOSTNAMELEN
];
754 int rv
= VNTSD_SUCCESS
;
758 groupp
= (vntsd_group_t
*)argp
->handle
;
759 clientp
= (vntsd_client_t
*)argp
->arg
;
764 /* free argp, which was allocated in listen thread */
767 /* check if group is removed */
769 D1(stderr
, "t@%d get_client_sel@%lld:client@%d\n", thr_self(),
770 groupp
->tcp_port
, clientp
->sockfd
);
772 bzero(buf
, MAXHOSTNAMELEN
);
775 if (gethostname(buf
, MAXHOSTNAMELEN
)) {
776 vntsd_log(VNTSD_STATUS_NO_HOST_NAME
, "vntsd_console_thread()");
777 (void) snprintf(buf
, sizeof (buf
), "unkown host");
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:");
791 D1(stderr
, "t@%d console_thread()@%lld:client@%d\n", thr_self(),
792 groupp
->tcp_port
, clientp
->sockfd
);
794 num_cons
= vntsd_chk_group_total_cons(groupp
);
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
);
802 /* any console is removed from group? */
803 num_cons
= vntsd_chk_group_total_cons(groupp
);
813 /* list domain names */
814 rv
= list_all_domains(groupp
, clientp
);
820 rv
= VNTSD_STATUS_CLIENT_QUIT
;
826 /* no console in the group */
827 rv
= VNTSD_STATUS_NO_CONS
;
831 if (clientp
->cons
== NULL
) {
833 /* by pass selecting console */
834 consp
= (vntsd_cons_t
*)
835 (groupp
->conspq
->handle
);
841 consp
= clientp
->cons
;
844 /* connect to console */
845 rv
= connect_cons(consp
, clientp
);
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
);
860 * all consoles in the group
861 * may be gone before this client
864 if (rv
!= VNTSD_SUCCESS
)
868 consp
= clientp
->cons
;
872 /* connect to console */
873 rv
= connect_cons(consp
, clientp
);
874 D1(stderr
, "t@%d console_thread()"
875 "connect_cons returns %d\n",
881 rv
= display_help(clientp
);
886 /* check error and may exit */
887 console_chk_status(groupp
, clientp
, rv
);