/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Vntsd handles two types of special commands, one is telnet * commands and another is vntsd special commands. * telnet commands supported are: * WILL * WONT * DO * DONT * TEL_ECHO * SUPRESS * LINEMODE * BRK * AYT * HT * NOP * * Vntsd special commands are: * Send break (~#) * Send alternate break (~^B) * Exit (~.) * Force write access (~w) * Console next (~n) * Console previous (~p) * Help (~?) */ #include #include #include #include #include #include #include #include #include #include #include #include #include "vntsd.h" #include "chars.h" char vntsd_eol[] = { CR, LF, 0}; typedef int (*e_func_t)(vntsd_client_t *clientp); /* structure for daemon special cmd */ typedef struct { char e_char; /* char to match on */ char *e_help; /* help string */ e_func_t e_func; /* command */ } esctable_t; /* genbrk() - send a break to vcc driver */ static int genbrk(vntsd_client_t *clientp) { vntsd_cons_t *consp; assert(clientp); assert(clientp->cons); consp = clientp->cons; D1(stderr, "t@%d genbrk fd=%d sockfd %d\n", thr_self(), consp->vcc_fd, clientp->sockfd); assert(consp->clientpq != NULL); if (consp->clientpq->handle != clientp) { /* reader */ return (vntsd_write_line(clientp, gettext(VNTSD_NO_WRITE_ACCESS_MSG))); } /* writer */ if (ioctl(consp->vcc_fd, TCSBRK, NULL)) { return (VNTSD_ERR_VCC_IOCTL); } return (VNTSD_STATUS_CONTINUE); } /* genaltbrk() - handle the alternate break sequence */ static int genaltbrk(vntsd_client_t *clientp) { vntsd_cons_t *consp; char brkseq[2] = { '~', CNTRL('B')}; assert(clientp); assert(clientp->cons); consp = clientp->cons; D1(stderr, "t@%d genaltbrk fd=%d sockfd %d\n", thr_self(), consp->vcc_fd, clientp->sockfd); assert(consp->clientpq != NULL); if (consp->clientpq->handle != clientp) { /* reader */ return (vntsd_write_line(clientp, gettext(VNTSD_NO_WRITE_ACCESS_MSG))); } /* * Unlike the genbrk() function we will just forward the break sequence * on to vcc and subsequently the underlying console driver. This will * involve sending the characters '~' and CNTRL('B'). */ if ((vntsd_write_fd(clientp->cons->vcc_fd, brkseq, sizeof (brkseq))) == VNTSD_SUCCESS) return (VNTSD_STATUS_CONTINUE); else return (VNTSD_STATUS_VCC_IO_ERR); } /* * console_forward() - cycle client to the next console * in the group queue. */ static int console_forward(vntsd_client_t *clientp) { /* forward when there are mutiple consoles in the group */ if (clientp->cons->group->num_cons > 1) return (VNTSD_STATUS_MOV_CONS_FORWARD); return (VNTSD_STATUS_CONTINUE); } /* * console_backward() - cycle client to the previous * console in the group queue. */ static int console_backward(vntsd_client_t *clientp) { /* backward when there are mutiple consoles in the group */ if (clientp->cons->group->num_cons > 1) return (VNTSD_STATUS_MOV_CONS_BACKWARD); return (VNTSD_STATUS_CONTINUE); } /* acquire_write() - acquire write access to a console. */ static int acquire_write(vntsd_client_t *clientp) { int rv; int yes_no = 1; vntsd_cons_t *consp; assert(clientp); consp = clientp->cons; assert(consp); if (consp->clientpq->handle == clientp) { /* client is a writer */ if ((rv = vntsd_write_line(clientp, gettext("You have write permission"))) != VNTSD_SUCCESS) { return (rv); } return (VNTSD_STATUS_CONTINUE); } /* message to client */ if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { return (rv); } /* * TRANSLATION_NOTE * The following string should be formatted to fit on multiple lines * assuming a line width of at most 78 characters. There must be no * trailing newline. */ if ((rv = vntsd_write_lines(clientp, gettext("Warning: another user currently " "has write permission\nto this console and forcibly removing " "him/her will terminate\nany current write action and all work " "will be lost."))) != VNTSD_SUCCESS) { return (rv); } /* get client yes no */ if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { return (rv); } if ((rv = vntsd_get_yes_no(clientp, gettext("Would you like to continue?"), &yes_no)) != VNTSD_SUCCESS) { return (rv); } if (yes_no == B_FALSE) { /* client change mind no need to acquire write access */ return (VNTSD_STATUS_CONTINUE); } return (VNTSD_STATUS_ACQUIRE_WRITER); } /* client_exit() - disconnect client from the console. */ static int client_exit(vntsd_client_t *arg __unused) { return (VNTSD_STATUS_RESELECT_CONS); } static int daemon_cmd_help(vntsd_client_t *clientp); /* table for daemon commands */ static esctable_t etable[] = { /* send a break to vcc */ {'#', "Send break", genbrk}, /* alternate break sequence */ {CNTRL('B'), "Send alternate break", genaltbrk}, /* exit */ {'.', "Exit from this console", client_exit}, /* acquire write access */ {'w', "Force write access", acquire_write}, /* connect to next console in queue */ {'n', "Console next", (e_func_t)console_forward}, /* connect to previous console in queue */ {'p', "Console previous", (e_func_t)console_backward}, /* help must be next to last */ {'?', "Help", daemon_cmd_help}, /* table terminator */ {0, 0, 0} }; void vntsd_init_esctable_msgs(void) { esctable_t *p; for (p = etable; p->e_char != '\0'; p++) { p->e_help = gettext(p->e_help); } } /* daemon_cmd_help() - print help. */ static int daemon_cmd_help(vntsd_client_t *clientp) { esctable_t *p; int rv; char buf[VNTSD_LINE_LEN]; if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { return (rv); } /* * TRANSLATION_NOTE * VNTSD is the name of the VNTS daemon and should not be translated. */ if ((rv = vntsd_write_line(clientp, gettext("VNTSD commands"))) != VNTSD_SUCCESS) { return (rv); } for (p = etable; p->e_char; p++) { if (p->e_char == CNTRL('B')) { (void) snprintf(buf, sizeof (buf), "~^B --%s", p->e_help); } else { (void) snprintf(buf, sizeof (buf), "~%c --%s", p->e_char, p->e_help); } if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) { return (rv); } } return (VNTSD_STATUS_CONTINUE); } /* exit from daemon command */ static int exit_daemon_cmd(vntsd_client_t *clientp, int rv) { (void) mutex_lock(&clientp->lock); clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; (void) mutex_unlock(&clientp->lock); return (rv); } /* * vntsd_process_daemon_cmd() - special commands * "~" vntsd daemon commands * "~~" enter '~' character */ int vntsd_process_daemon_cmd(vntsd_client_t *clientp, char c) { esctable_t *p; int rv; char prev_char; prev_char = clientp->prev_char; if (c != VNTSD_DAEMON_CMD || (prev_char != 0 && prev_char != CR)) { /* not a daemon command */ return (VNTSD_SUCCESS); } if (clientp->status & VNTSD_CLIENT_DISABLE_DAEMON_CMD) { return (VNTSD_STATUS_CONTINUE); } /* no reentry to process_daemon_cmd */ (void) mutex_lock(&clientp->lock); clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; (void) mutex_unlock(&clientp->lock); D3(stderr, "t@%d process_daemon_cmd %d %d \n", thr_self(), clientp->cons->vcc_fd, clientp->sockfd); /* read in command */ if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { return (exit_daemon_cmd(clientp, rv)); } if (c == VNTSD_DAEMON_CMD) { /* * received another '~' * a user types '~~' to get '~' */ (void) mutex_lock(&clientp->lock); clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; (void) mutex_unlock(&clientp->lock); return (VNTSD_SUCCESS); } for (p = etable; p->e_char; p++) { if (p->e_char == c) { /* found match */ assert(p->e_func); rv = (*p->e_func)(clientp); return (exit_daemon_cmd(clientp, rv)); } } /* no match, print out the help */ p--; assert(p->e_char == '?'); rv = (*p->e_func)(clientp); return (exit_daemon_cmd(clientp, rv)); } /* vntsd_set_telnet_options() - change telnet client to character mode. */ int vntsd_set_telnet_options(int fd) { /* set client telnet options */ uint8_t buf[] = {IAC, DONT, LINEMODE, IAC, WILL, SUPRESS, IAC, WILL, TEL_ECHO, IAC, DONT, TERM_TYPE, IAC, DONT, TERM_SP, IAC, DONT, STATUS, IAC, DONT, FC, IAC, DONT, TM, IAC, DONT, ENV, IAC, DONT, WIN_SIZE}; return (vntsd_write_fd(fd, (char *)buf, 30)); } /* vntsd_telnet_cmd() process telnet commands */ int vntsd_telnet_cmd(vntsd_client_t *clientp, char c) { uint8_t buf[4]; char cmd; int rv = VNTSD_STATUS_CONTINUE; bzero(buf, 4); if ((uint8_t)c != IAC) { /* not telnet cmd */ return (VNTSD_SUCCESS); } if ((rv = vntsd_read_char(clientp, &cmd)) != VNTSD_SUCCESS) { return (rv); } if ((uint8_t)cmd == WILL || (uint8_t)cmd == WONT || (uint8_t)cmd == DO || (uint8_t)cmd == DONT) { if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { return (rv); } } switch ((uint8_t)cmd) { case WILL: switch ((uint8_t)c) { case TEL_ECHO: case SUPRESS: case LINEMODE: break; default: syslog(LOG_ERR, "not support telnet WILL %x\n", c); break; } break; case WONT: switch ((uint8_t)c) { case TEL_ECHO: case SUPRESS: case LINEMODE: default: syslog(LOG_ERR, "not support telnet WONT %x\n", c); break; } break; case DO: case DONT: buf[0] = IAC; buf[1] = WILL; buf[2] = c; rv = vntsd_write_client(clientp, (char *)buf, 3); break; case BRK: /* send break to vcc */ rv = genbrk(clientp); break; case IP: break; case AYT: { static char aytresp[] = "vntsd here"; rv = vntsd_write_client(clientp, aytresp, sizeof (aytresp) - 1); break; } case HT: case NOP: return (VNTSD_STATUS_CONTINUE); default: syslog(LOG_ERR, "not support telnet ctrl %2.2x\n", 0xff & cmd); break; } if (rv == VNTSD_SUCCESS) { return (VNTSD_STATUS_CONTINUE); } else { return (rv); } } /* * vntsd_ctrl_cmd() - control keys * read and write suspend are supported. */ int vntsd_ctrl_cmd(vntsd_client_t *clientp, char c) { int cmd; D3(stderr, "t@%d vntsd_ctrl_cmd%d %d\n", thr_self(), clientp->cons->vcc_fd, clientp->sockfd); if ((c != START) && (c != STOP)) { /* not a supported control command */ return (VNTSD_SUCCESS); } if (c == START) { D3(stderr, "t@%d client restart\n", thr_self()); /* send resume read */ cmd = 1; if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { return (VNTSD_STATUS_VCC_IO_ERR); } } if (c == STOP) { D3(stderr, "t@%d client suspend\n", thr_self()); /* send suspend read */ cmd = 0; if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { return (VNTSD_STATUS_VCC_IO_ERR); } } return (VNTSD_STATUS_CONTINUE); }