diff options
Diffstat (limited to 'usr/src/cmd/vntsd/console.c')
-rw-r--r-- | usr/src/cmd/vntsd/console.c | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/usr/src/cmd/vntsd/console.c b/usr/src/cmd/vntsd/console.c new file mode 100644 index 0000000000..4b7c145e0e --- /dev/null +++ b/usr/src/cmd/vntsd/console.c @@ -0,0 +1,721 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Listen thread creates a console thread whenever there is a tcp client + * made a conection to its port. In the console thread, if there are + * multiple consoles in the group, client will be asked for a console selection. + * a write thread for a console is created when first client connects to a + * selected console and console thread becomes read thread for the client. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <thread.h> +#include <synch.h> +#include <signal.h> +#include <assert.h> +#include <ctype.h> +#include <syslog.h> +#include <libintl.h> +#include <netdb.h> +#include "vntsd.h" +#include "chars.h" + +/* display domain names in the group */ +static boolean_t +display_domain_name(vntsd_cons_t *consp, int *fd) +{ + char buf[VNTSD_LINE_LEN]; + char *status; + + + if (consp->clientpq != NULL) { + status = gettext("connected"); + } else if (consp->status & VNTSD_CONS_DELETED) { + status = gettext("removing..."); + } else { + status = gettext("online"); + } + + (void) snprintf(buf, sizeof (buf), "%-20d%-30s%-25s%s", + consp->cons_no, consp->domain_name, status, vntsd_eol); + + return (vntsd_write_fd(*fd, buf, strlen(buf)) != VNTSD_SUCCESS); +} + +/* output connected message to tcp client */ +static int +write_connect_msg(vntsd_client_t *clientp, char *group_name, + char *domain_name) +{ + + int rv = VNTSD_SUCCESS; + char buf[VNTSD_LINE_LEN]; + + if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) != + VNTSD_SUCCESS) { + return (rv); + } + + (void) snprintf(buf, sizeof (buf), + gettext("Connecting to console \"%s\" in group \"%s\" ...."), + domain_name, group_name); + + if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) { + return (rv); + } + + if ((rv = vntsd_write_line(clientp, + gettext("Press ~? for control options .."))) != + VNTSD_SUCCESS) { + return (rv); + } + + return (VNTSD_SUCCESS); +} + +static int +create_write_thread(vntsd_cons_t *consp) +{ + + assert(consp); + + /* create write thread for the console */ + (void) mutex_lock(&consp->lock); + if (thr_create(NULL, 0, (thr_func_t)vntsd_write_thread, + (void *)consp, NULL, &consp->wr_tid)) { + + DERR(stderr, "t@%d create_rd_wr_thread@%d: " + "create write thread failed\n", + thr_self(), consp->cons_no); + (void) close(consp->vcc_fd); + consp->vcc_fd = -1; + (void) mutex_unlock(&consp->lock); + + return (VNTSD_ERR_CREATE_WR_THR); + } + (void) mutex_unlock(&consp->lock); + return (VNTSD_SUCCESS); +} + +/* Display all domain consoles in a group. */ +static int +list_all_domains(vntsd_group_t *groupp, vntsd_client_t *clientp) +{ + char vntsd_line[VNTSD_LINE_LEN]; + int rv = VNTSD_SUCCESS; + + if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) + != VNTSD_SUCCESS) { + return (rv); + } + + /* + * TRANSLATION_NOTE + * The following three strings of the form "DOMAIN .." are table + * headers and should be all uppercase. + */ + (void) snprintf(vntsd_line, sizeof (vntsd_line), + "%-20s%-30s%-25s", + gettext("DOMAIN ID"), gettext("DOMAIN NAME"), + gettext("DOMAIN STATE")); + + if ((rv = vntsd_write_line(clientp, vntsd_line)) != VNTSD_SUCCESS) { + return (rv); + } + + (void) mutex_lock(&groupp->lock); + + if (vntsd_que_find(groupp->conspq, (compare_func_t)display_domain_name, + &(clientp->sockfd)) != NULL) { + rv = VNTSD_ERR_WRITE_CLIENT; + } + + (void) mutex_unlock(&groupp->lock); + + return (rv); +} + +/* display help */ +static int +display_help(vntsd_client_t *clientp) +{ + int rv = VNTSD_SUCCESS; + char *bufp; + + if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) + != VNTSD_SUCCESS) { + return (rv); + } + + /* + * TRANSLATION_NOTE + * The following three strings of the form ". -- ..." are help + * messages for single character commands. Do not translate the + * character before the --. + */ + bufp = gettext("h -- this help)"); + + if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { + return (rv); + } + + bufp = gettext("l -- list of consoles"); + + if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { + return (rv); + } + + bufp = gettext("q -- quit"); + + if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { + return (rv); + } + + /* + * TRANSLATION_NOTE + * In the following string, "id" is a short mnemonic for + * "identifier" and both occurrences should be translated. + */ + + bufp = gettext("[c[c ]]{id} -- connect to console of domain {id}"); + + if ((rv = vntsd_write_line(clientp, bufp)) != VNTSD_SUCCESS) { + return (rv); + } + + return (VNTSD_SUCCESS); +} + +/* select a console to connect */ +static int +select_cons(vntsd_group_t *groupp, int num_cons, vntsd_cons_t **consp, + vntsd_client_t *clientp, char c) +{ + int cons_no = -2; + int n; + int i; + char buf[VNTSD_LINE_LEN]; + int rv; + + + + (void) mutex_lock(&groupp->lock); + if (groupp->num_cons == 0) { + (void) mutex_unlock(&groupp->lock); + /* no console in this group */ + return (VNTSD_STATUS_NO_CONS); + } + (void) mutex_unlock(&groupp->lock); + + if (num_cons == 1) { + /* by pass selecting console */ + *consp = (vntsd_cons_t *)(groupp->conspq->handle); + return (VNTSD_SUCCESS); + } + + + if (isdigit(c)) { + /* {id} input */ + cons_no = c - '0'; + } else if (c == 'c') { + /* c{id} or c {id} input */ + cons_no = -1; + } else if (!isspace(c)) { + return (VNTSD_ERR_INVALID_INPUT); + } + + /* get client selections */ + n = VNTSD_LINE_LEN; + + if ((rv = vntsd_read_line(clientp, buf, &n)) != VNTSD_SUCCESS) { + return (rv); + } + + /* parse command */ + for (i = 0; i < n; i++) { + if (cons_no == -1) { + /* c{id} */ + cons_no = atoi(buf + i); + break; + } + + if (isspace(buf[i]) && cons_no == -2) { + /* skip space */ + continue; + } + + if (buf[i] == 'c') { + /* c{id} or c {id} */ + cons_no = -1; + } else if (buf[i] == CR) { + break; + } else { + return (VNTSD_ERR_INVALID_INPUT); + } + } + + if (cons_no < 0) { + return (VNTSD_ERR_INVALID_INPUT); + } + + /* get selected console */ + (void) mutex_lock(&groupp->lock); + + *consp = (vntsd_cons_t *)vntsd_que_find(groupp->conspq, + (compare_func_t)vntsd_cons_by_consno, &cons_no); + + if (*consp == NULL) { + /* during console selection, the console has been deleted */ + (void) mutex_unlock(&groupp->lock); + + return (VNTSD_ERR_INVALID_INPUT); + } + if ((*consp)->status & VNTSD_CONS_DELETED) { + return (VNTSD_ERR_INVALID_INPUT); + } + + (void) mutex_unlock(&groupp->lock); + + return (VNTSD_SUCCESS); +} + +/* compare if there is a match console in the gorup */ +static boolean_t +find_cons_in_group(vntsd_cons_t *consp_in_group, vntsd_cons_t *consp) +{ + if (consp_in_group == consp) { + return (B_TRUE); + } else { + return (B_FALSE); + } +} + +/* connect a client to a console */ +static int +connect_cons(vntsd_cons_t *consp, vntsd_client_t *clientp) +{ + int rv, rv1; + vntsd_group_t *groupp; + + assert(consp); + groupp = consp->group; + assert(groupp); + assert(clientp); + + (void) mutex_lock(&groupp->lock); + + /* check if console is valid */ + consp = vntsd_que_find(groupp->conspq, + (compare_func_t)find_cons_in_group, consp); + + if (consp == NULL) { + (void) mutex_unlock(&groupp->lock); + return (VNTSD_STATUS_NO_CONS); + } + if (consp->status & VNTSD_CONS_DELETED) { + (void) mutex_unlock(&groupp->lock); + return (VNTSD_STATUS_NO_CONS); + } + + (void) mutex_lock(&consp->lock); + (void) mutex_lock(&clientp->lock); + + + clientp->cons = consp; + + /* enable daemon cmd */ + clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; + + if (consp->clientpq == NULL) { + /* first connect to console - a writer */ + assert(consp->vcc_fd == -1); + /* open vcc */ + consp->vcc_fd = vntsd_open_vcc(consp->dev_name, consp->cons_no); + if (consp->vcc_fd < 0) { + (void) mutex_unlock(&clientp->lock); + (void) mutex_unlock(&consp->lock); + (void) mutex_unlock(&groupp->lock); + assert(consp->group); + return (vntsd_vcc_err(consp)); + } + } + + (void) mutex_unlock(&clientp->lock); + + /* + * move the client from group's no console selected queue + * to cons queue + */ + + rv = vntsd_que_rm(&groupp->no_cons_clientpq, clientp); + assert(rv == VNTSD_SUCCESS); + + rv = vntsd_que_append(&consp->clientpq, clientp); + (void) mutex_unlock(&groupp->lock); + + if (rv != VNTSD_SUCCESS) { + if (consp->clientpq->handle == clientp) { + /* writer */ + (void) close(consp->vcc_fd); + consp->vcc_fd = -1; + } + + (void) mutex_unlock(&consp->lock); + return (rv); + } + + (void) mutex_unlock(&consp->lock); + + if (consp->clientpq->handle == clientp) { + /* create a write thread */ + rv = create_write_thread(consp); + if (rv != VNTSD_SUCCESS) { + return (rv); + } + } + + /* write connecting message */ + if ((rv = write_connect_msg(clientp, consp->group->group_name, + consp->domain_name)) != VNTSD_SUCCESS) { + return (rv); + } + + /* process input from client */ + rv = vntsd_read(clientp); + + /* client disconnected from the console */ + (void) mutex_lock(&groupp->lock); + + /* remove client from console queue */ + (void) mutex_lock(&consp->lock); + rv1 = vntsd_que_rm(&consp->clientpq, clientp); + assert(rv1 == VNTSD_SUCCESS); + + /* append client to group's no console selected queue */ + rv1 = vntsd_que_append(&groupp->no_cons_clientpq, clientp); + (void) mutex_unlock(&groupp->lock); + + if (consp->clientpq == NULL) { + /* clean up console since there is no client connected to it */ + assert(consp->vcc_fd != -1); + + /* close vcc port */ + (void) close(consp->vcc_fd); + consp->vcc_fd = -1; + + /* force write thread to exit */ + assert(consp->wr_tid != (thread_t)-1); + (void) thr_kill(consp->wr_tid, SIGUSR1); + (void) mutex_unlock(&consp->lock); + (void) thr_join(consp->wr_tid, NULL, NULL); + (void) mutex_lock(&consp->lock); + } + + if (consp->status & VNTSD_CONS_SIG_WAIT) { + /* console is waiting for client to disconnect */ + (void) cond_signal(&consp->cvp); + } + + (void) mutex_unlock(&consp->lock); + + return (rv1 == VNTSD_SUCCESS ? rv : rv1); + +} + +/* read command line input */ +static int +read_cmd(vntsd_client_t *clientp, char *prompt, char *cmd) +{ + int rv; + + /* disable daemon special command */ + (void) mutex_lock(&clientp->lock); + clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; + (void) mutex_unlock(&clientp->lock); + + if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) + != VNTSD_SUCCESS) { + return (rv); + } + + if ((rv = vntsd_write_client(clientp, prompt, strlen(prompt))) + != VNTSD_SUCCESS) { + return (rv); + } + + if ((rv = vntsd_read_data(clientp, cmd)) != VNTSD_SUCCESS) { + return (rv); + } + if (*cmd == BS) { + return (VNTSD_SUCCESS); + } + + rv = vntsd_write_client(clientp, cmd, 1); + + *cmd = tolower(*cmd); + + return (rv); +} + +/* reset client for selecting a console in the group */ +static void +client_init(vntsd_client_t *clientp) +{ + (void) mutex_lock(&clientp->lock); + clientp->cons = NULL; + clientp->status = 0; + (void) mutex_unlock(&clientp->lock); +} + +/* clean up client and exit the thread */ +static void +client_fini(vntsd_group_t *groupp, vntsd_client_t *clientp) +{ + + assert(groupp); + assert(clientp); + + /* disconnct client from tcp port */ + assert(clientp->sockfd != -1); + (void) close(clientp->sockfd); + + (void) mutex_lock(&groupp->lock); + (void) vntsd_que_rm(&groupp->no_cons_clientpq, clientp); + + if ((groupp->no_cons_clientpq == NULL) && + (groupp->status & VNTSD_GROUP_SIG_WAIT)) { + /* group is waiting to be deleted */ + groupp->status &= ~VNTSD_GROUP_SIG_WAIT; + (void) cond_signal(&groupp->cvp); + } + (void) mutex_unlock(&groupp->lock); + + (void) mutex_destroy(&clientp->lock); + free(clientp); + + thr_exit(0); +} + +/* check client's status. exit if client quits or fatal errors */ +static void +console_chk_status(vntsd_group_t *groupp, vntsd_client_t *clientp, int status) +{ + char err_msg[VNTSD_LINE_LEN]; + + D1(stderr, "t@%d console_chk_status() status=%d " + "client status=%x num consoles=%d \n", + thr_self(), status, clientp->status, groupp->num_cons); + + (void) snprintf(err_msg, VNTSD_LINE_LEN, "console_chk_status client%d" + " num_cos=%d", clientp->sockfd, groupp->num_cons); + + if (groupp->num_cons == 0) { + /* no more console in the group */ + client_fini(groupp, clientp); + } + + if (status == VNTSD_STATUS_INTR) { + /* reason for signal? */ + status = vntsd_cons_chk_intr(clientp); + } + + switch (status) { + + case VNTSD_STATUS_CLIENT_QUIT: + client_fini(groupp, clientp); + return; + + case VNTSD_STATUS_RESELECT_CONS: + assert(clientp->cons); + if ((groupp->num_cons == 1) && + (groupp->conspq->handle == clientp->cons)) { + /* no other selection available */ + client_fini(groupp, clientp); + } else { + client_init(clientp); + } + return; + + case VNTSD_STATUS_VCC_IO_ERR: + if ((clientp->status & VNTSD_CLIENT_CONS_DELETED) == 0) { + /* check if console was deleted */ + status = vntsd_vcc_err(clientp->cons); + } + + if (status != VNTSD_STATUS_CONTINUE) { + /* console was deleted */ + if (groupp->num_cons == 1) { + client_fini(groupp, clientp); + } + } + + /* console is ok */ + client_init(clientp); + return; + + case VNTSD_STATUS_MOV_CONS_FORWARD: + case VNTSD_STATUS_MOV_CONS_BACKWARD: + if (groupp->num_cons == 1) { + /* same console */ + return; + } + + /* get selected console */ + (void) mutex_lock(&(clientp->cons->group->lock)); + clientp->cons = vntsd_que_pos(clientp->cons->group->conspq, + clientp->cons, + (status == VNTSD_STATUS_MOV_CONS_FORWARD)?(1):(-1)); + (void) mutex_unlock(&(clientp->cons->group->lock)); + return; + + case VNTSD_SUCCESS: + case VNTSD_STATUS_CONTINUE: + case VNTSD_STATUS_NO_CONS: + client_init(clientp); + return; + + case VNTSD_ERR_INVALID_INPUT: + return; + + default: + /* fatal error */ + vntsd_log(status, err_msg); + client_fini(groupp, clientp); + return; + } +} + +/* console thread */ +void * +vntsd_console_thread(vntsd_thr_arg_t *argp) +{ + vntsd_group_t *groupp; + vntsd_cons_t *consp; + vntsd_client_t *clientp; + + char buf[MAXHOSTNAMELEN]; + char prompt[72]; + char cmd; + int rv = VNTSD_SUCCESS; + int num_cons; + + + groupp = (vntsd_group_t *)argp->handle; + clientp = (vntsd_client_t *)argp->arg; + + assert(groupp); + assert(clientp); + + /* check if group is removed */ + + D1(stderr, "t@%d get_client_sel@%lld:client@%d\n", thr_self(), + groupp->tcp_port, clientp->sockfd); + + bzero(buf, MAXHOSTNAMELEN); + + /* host name */ + if (gethostname(buf, MAXHOSTNAMELEN)) { + vntsd_log(VNTSD_STATUS_NO_HOST_NAME, "vntsd_console_thread()"); + (void) snprintf(buf, sizeof (buf), "unkown host"); + } + + if (snprintf(prompt, sizeof (prompt), + "%s-vnts-%s: h,l,{id},c{id},c {id},q:", + buf, groupp->group_name) >= sizeof (prompt)) { + /* long prompt doesn't fit, use short one */ + (void) snprintf(prompt, sizeof (prompt), + "vnts: h,l,{id},c{id},c {id}, q:"); + } + + + for (;;) { + cmd = ' '; + D1(stderr, "t@%d console_thread()@%lld:client@%d\n", thr_self(), + groupp->tcp_port, clientp->sockfd); + + num_cons = vntsd_chk_group_total_cons(groupp); + + if ((num_cons > 1) && (clientp->cons == NULL)) { + /* console to connect to */ + rv = read_cmd(clientp, prompt, &cmd); + /* check error and may exit */ + console_chk_status(groupp, clientp, rv); + } + + switch (cmd) { + + case 'l': + + /* list domain names */ + rv = list_all_domains(groupp, clientp); + break; + + + case 'q': + + rv = VNTSD_STATUS_CLIENT_QUIT; + break; + + case 'h': + rv = display_help(clientp); + break; + + default: + /* select console */ + if (clientp->cons == NULL) { + rv = select_cons(groupp, num_cons, + &consp, clientp, cmd); + if (rv == VNTSD_ERR_INVALID_INPUT) { + rv = display_help(clientp); + break; + } + } else { + consp = clientp->cons; + } + assert(consp); + + /* connect to console */ + rv = connect_cons(consp, clientp); + D1(stderr, "t@%d console_thread()" + "connect_cons returns %d\n", + thr_self(), rv); + break; + + } + /* check error and may exit */ + console_chk_status(groupp, clientp, rv); + } + + /*NOTREACHED*/ + return (NULL); +} |