11991Sheppo /* 21991Sheppo * CDDL HEADER START 31991Sheppo * 41991Sheppo * The contents of this file are subject to the terms of the 51991Sheppo * Common Development and Distribution License (the "License"). 61991Sheppo * You may not use this file except in compliance with the License. 71991Sheppo * 81991Sheppo * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91991Sheppo * or http://www.opensolaris.org/os/licensing. 101991Sheppo * See the License for the specific language governing permissions 111991Sheppo * and limitations under the License. 121991Sheppo * 131991Sheppo * When distributing Covered Code, include this CDDL HEADER in each 141991Sheppo * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151991Sheppo * If applicable, add the following below this CDDL HEADER, with the 161991Sheppo * fields enclosed by brackets "[]" replaced with your own identifying 171991Sheppo * information: Portions Copyright [yyyy] [name of copyright owner] 181991Sheppo * 191991Sheppo * CDDL HEADER END 201991Sheppo */ 211991Sheppo /* 22*8498SChris.Gerhard@sun.com * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 231991Sheppo * Use is subject to license terms. 241991Sheppo */ 251991Sheppo 261991Sheppo /* 271991Sheppo * Vntsd handles two types of special commands, one is telnet 281991Sheppo * commands and another is vntsd special commands. 291991Sheppo * telnet commands supported are: 301991Sheppo * WILL 311991Sheppo * WONT 321991Sheppo * DO 331991Sheppo * DONT 341991Sheppo * TEL_ECHO 351991Sheppo * SUPRESS 361991Sheppo * LINEMODE 371991Sheppo * BRK 381991Sheppo * AYT 391991Sheppo * HT 40*8498SChris.Gerhard@sun.com * NOP 411991Sheppo * 421991Sheppo * Vntsd special commands are: 432109Slm66018 * Send break (~#) 442109Slm66018 * Exit (~.) 452109Slm66018 * Force write access (~w) 462109Slm66018 * Console next (~n) 472109Slm66018 * Console previous (~p) 482109Slm66018 * Help (~?) 491991Sheppo */ 501991Sheppo 511991Sheppo #include <stdio.h> 521991Sheppo #include <stdlib.h> 531991Sheppo #include <string.h> 541991Sheppo #include <unistd.h> 551991Sheppo #include <sys/types.h> 561991Sheppo #include <sys/socket.h> 571991Sheppo #include <netinet/in.h> 581991Sheppo #include <thread.h> 591991Sheppo #include <ctype.h> 601991Sheppo #include <sys/termio.h> 611991Sheppo #include <libintl.h> 621991Sheppo #include <syslog.h> 631991Sheppo #include "vntsd.h" 641991Sheppo #include "chars.h" 651991Sheppo 661991Sheppo char vntsd_eol[] = { CR, LF, 0}; 671991Sheppo 681991Sheppo typedef int (*e_func_t)(vntsd_client_t *clientp); 691991Sheppo /* structure for daemon special cmd */ 701991Sheppo typedef struct { 711991Sheppo char e_char; /* char to match on */ 721991Sheppo char *e_help; /* help string */ 731991Sheppo e_func_t e_func; /* command */ 741991Sheppo } esctable_t; 751991Sheppo 761991Sheppo /* genbrk() - send a break to vcc driver */ 771991Sheppo static int 781991Sheppo genbrk(vntsd_client_t *clientp) 791991Sheppo { 801991Sheppo 811991Sheppo vntsd_cons_t *consp; 821991Sheppo 831991Sheppo assert(clientp); 841991Sheppo assert(clientp->cons); 851991Sheppo 861991Sheppo consp = clientp->cons; 871991Sheppo D1(stderr, "t@%d genbrk fd=%d sockfd %d\n", thr_self(), 881991Sheppo consp->vcc_fd, clientp->sockfd); 891991Sheppo 901991Sheppo assert(consp->clientpq != NULL); 911991Sheppo if (consp->clientpq->handle != clientp) { 921991Sheppo /* reader */ 931991Sheppo return (vntsd_write_line(clientp, 94*8498SChris.Gerhard@sun.com gettext(VNTSD_NO_WRITE_ACCESS_MSG))); 951991Sheppo } 961991Sheppo 971991Sheppo /* writer */ 981991Sheppo if (ioctl(consp->vcc_fd, TCSBRK, NULL)) { 991991Sheppo return (VNTSD_ERR_VCC_IOCTL); 1001991Sheppo } 1011991Sheppo 1021991Sheppo return (VNTSD_STATUS_CONTINUE); 1031991Sheppo } 1041991Sheppo 1051991Sheppo /* 1061991Sheppo * console_forward() - cycle client to the next console 1071991Sheppo * in the group queue. 1081991Sheppo */ 1091991Sheppo static int 1102956Sdtse console_forward(vntsd_client_t *clientp) 1111991Sheppo { 1122956Sdtse /* forward when there are mutiple consoles in the group */ 1132956Sdtse if (clientp->cons->group->num_cons > 1) 1142956Sdtse return (VNTSD_STATUS_MOV_CONS_FORWARD); 1152956Sdtse 1162956Sdtse return (VNTSD_STATUS_CONTINUE); 1172956Sdtse 1181991Sheppo } 1191991Sheppo 1201991Sheppo /* 1211991Sheppo * console_backward() - cycle client to the previous 1221991Sheppo * console in the group queue. 1231991Sheppo */ 1241991Sheppo static int 1252956Sdtse console_backward(vntsd_client_t *clientp) 1261991Sheppo { 1272956Sdtse /* backward when there are mutiple consoles in the group */ 1282956Sdtse if (clientp->cons->group->num_cons > 1) 1292956Sdtse return (VNTSD_STATUS_MOV_CONS_BACKWARD); 1302956Sdtse 1312956Sdtse return (VNTSD_STATUS_CONTINUE); 1322956Sdtse 1331991Sheppo } 1341991Sheppo 1351991Sheppo /* acquire_write() - acquire write access to a console. */ 1361991Sheppo static int 1371991Sheppo acquire_write(vntsd_client_t *clientp) 1381991Sheppo { 1391991Sheppo int rv; 1401991Sheppo int yes_no = 1; 1411991Sheppo vntsd_cons_t *consp; 1421991Sheppo 1431991Sheppo assert(clientp); 1441991Sheppo consp = clientp->cons; 1451991Sheppo assert(consp); 1461991Sheppo 1471991Sheppo if (consp->clientpq->handle == clientp) { 1481991Sheppo /* client is a writer */ 1491991Sheppo if ((rv = vntsd_write_line(clientp, 150*8498SChris.Gerhard@sun.com gettext("You have write permission"))) != 1511991Sheppo VNTSD_SUCCESS) { 1521991Sheppo return (rv); 1531991Sheppo 1541991Sheppo } 1551991Sheppo return (VNTSD_STATUS_CONTINUE); 1561991Sheppo } 1571991Sheppo 1581991Sheppo /* message to client */ 1591991Sheppo if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 1601991Sheppo != VNTSD_SUCCESS) { 1611991Sheppo return (rv); 1621991Sheppo } 1631991Sheppo 1641991Sheppo /* 1651991Sheppo * TRANSLATION_NOTE 1661991Sheppo * The following string should be formatted to fit on multiple lines 1671991Sheppo * assuming a line width of at most 78 characters. There must be no 1681991Sheppo * trailing newline. 1691991Sheppo */ 1701991Sheppo if ((rv = vntsd_write_lines(clientp, 171*8498SChris.Gerhard@sun.com gettext("Warning: another user currently " 1721991Sheppo "has write permission\nto this console and forcibly removing " 1731991Sheppo "him/her will terminate\nany current write action and all work " 1741991Sheppo "will be lost."))) != VNTSD_SUCCESS) { 1751991Sheppo return (rv); 1761991Sheppo } 1771991Sheppo 1781991Sheppo /* get client yes no */ 1791991Sheppo if ((rv = vntsd_write_client(clientp, vntsd_eol, 180*8498SChris.Gerhard@sun.com VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { 1811991Sheppo return (rv); 1821991Sheppo } 1831991Sheppo 1841991Sheppo if ((rv = vntsd_get_yes_no(clientp, 185*8498SChris.Gerhard@sun.com gettext("Would you like to continue?"), 186*8498SChris.Gerhard@sun.com &yes_no)) != VNTSD_SUCCESS) { 1871991Sheppo return (rv); 1881991Sheppo } 1891991Sheppo 1901991Sheppo if (yes_no == B_FALSE) { 1911991Sheppo /* client change mind no need to acquire write access */ 1921991Sheppo return (VNTSD_STATUS_CONTINUE); 1931991Sheppo } 1941991Sheppo 1951991Sheppo return (VNTSD_STATUS_ACQUIRE_WRITER); 1961991Sheppo } 1971991Sheppo 1981991Sheppo /* client_exit() - disconnect client from the console. */ 1991991Sheppo static int 2001991Sheppo client_exit(void) 2011991Sheppo { 2021991Sheppo return (VNTSD_STATUS_RESELECT_CONS); 2031991Sheppo } 2041991Sheppo 2051991Sheppo static int daemon_cmd_help(vntsd_client_t *clientp); 2061991Sheppo 2071991Sheppo /* table for daemon commands */ 2081991Sheppo 2091991Sheppo static esctable_t etable[] = { 2101991Sheppo 2111991Sheppo /* send a break to vcc */ 2122109Slm66018 {'#', "Send break", genbrk}, 2131991Sheppo 2141991Sheppo /* exit */ 2152109Slm66018 {'.', "Exit from this console", (e_func_t)client_exit}, 2161991Sheppo 2171991Sheppo /* acquire write access */ 2182109Slm66018 {'w', "Force write access", acquire_write}, 2191991Sheppo 2201991Sheppo /* connect to next console in queue */ 2212109Slm66018 {'n', "Console next", (e_func_t)console_forward}, 2221991Sheppo 2231991Sheppo /* connect to previous console in queue */ 2242109Slm66018 {'p', "Console previous", (e_func_t)console_backward}, 2251991Sheppo 2261991Sheppo /* help must be next to last */ 2272109Slm66018 {'?', "Help", daemon_cmd_help}, 2281991Sheppo 2291991Sheppo /* table terminator */ 2301991Sheppo {0, 0, 0} 2311991Sheppo }; 2321991Sheppo 2331991Sheppo void 2341991Sheppo vntsd_init_esctable_msgs(void) 2351991Sheppo { 2361991Sheppo esctable_t *p; 2371991Sheppo 2381991Sheppo for (p = etable; p->e_char != '\0'; p++) { 2391991Sheppo p->e_help = gettext(p->e_help); 2401991Sheppo } 2411991Sheppo } 2421991Sheppo 2431991Sheppo /* daemon_cmd_help() - print help. */ 2441991Sheppo static int 2451991Sheppo daemon_cmd_help(vntsd_client_t *clientp) 2461991Sheppo { 2471991Sheppo esctable_t *p; 2481991Sheppo int rv; 2491991Sheppo char buf[VNTSD_LINE_LEN]; 2501991Sheppo 2511991Sheppo if ((rv = vntsd_write_client(clientp, vntsd_eol, 252*8498SChris.Gerhard@sun.com VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { 253*8498SChris.Gerhard@sun.com return (rv); 2541991Sheppo } 2551991Sheppo 2561991Sheppo /* 2571991Sheppo * TRANSLATION_NOTE 2581991Sheppo * VNTSD is the name of the VNTS daemon and should not be translated. 2591991Sheppo */ 2601991Sheppo if ((rv = vntsd_write_line(clientp, gettext("VNTSD commands"))) != 2611991Sheppo VNTSD_SUCCESS) { 2621991Sheppo return (rv); 2631991Sheppo } 2641991Sheppo 2651991Sheppo for (p = etable; p->e_char; p++) { 2661991Sheppo (void) snprintf(buf, sizeof (buf), 267*8498SChris.Gerhard@sun.com "~%c --%s", p->e_char, p->e_help); 2681991Sheppo 2691991Sheppo if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) { 2701991Sheppo return (rv); 2711991Sheppo } 2721991Sheppo } 2731991Sheppo 2741991Sheppo return (VNTSD_STATUS_CONTINUE); 2751991Sheppo } 2761991Sheppo 2771991Sheppo /* exit from daemon command */ 2781991Sheppo static int 2791991Sheppo exit_daemon_cmd(vntsd_client_t *clientp, int rv) 2801991Sheppo { 2811991Sheppo (void) mutex_lock(&clientp->lock); 2821991Sheppo clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; 2831991Sheppo (void) mutex_unlock(&clientp->lock); 2841991Sheppo return (rv); 2851991Sheppo } 2861991Sheppo 2872336Snarayan /* 2882336Snarayan * vntsd_process_daemon_cmd() - special commands 2892336Snarayan * "<RET>~" vntsd daemon commands 2902336Snarayan * "<RET>~~" enter '~' character 2912336Snarayan */ 2921991Sheppo int 2931991Sheppo vntsd_process_daemon_cmd(vntsd_client_t *clientp, char c) 2941991Sheppo { 2951991Sheppo esctable_t *p; 2961991Sheppo int rv; 2972336Snarayan char prev_char; 2981991Sheppo 2992336Snarayan prev_char = clientp->prev_char; 3002336Snarayan 3012336Snarayan if (c != VNTSD_DAEMON_CMD || (prev_char != 0 && prev_char != CR)) { 3021991Sheppo /* not a daemon command */ 3031991Sheppo return (VNTSD_SUCCESS); 3041991Sheppo } 3051991Sheppo 3061991Sheppo if (clientp->status & VNTSD_CLIENT_DISABLE_DAEMON_CMD) { 3071991Sheppo return (VNTSD_STATUS_CONTINUE); 3081991Sheppo } 3091991Sheppo 3101991Sheppo /* no reentry to process_daemon_cmd */ 3111991Sheppo (void) mutex_lock(&clientp->lock); 3121991Sheppo clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; 3131991Sheppo (void) mutex_unlock(&clientp->lock); 3141991Sheppo 3151991Sheppo D3(stderr, "t@%d process_daemon_cmd %d %d \n", thr_self(), 3161991Sheppo clientp->cons->vcc_fd, clientp->sockfd); 3171991Sheppo 3181991Sheppo /* read in command */ 3191991Sheppo if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { 3201991Sheppo return (exit_daemon_cmd(clientp, rv)); 3211991Sheppo } 3221991Sheppo 3232336Snarayan if (c == VNTSD_DAEMON_CMD) { 3242336Snarayan /* 3252336Snarayan * received another '~' 3262336Snarayan * a user types '~~' to get '~' 3272336Snarayan */ 3282336Snarayan (void) mutex_lock(&clientp->lock); 3292336Snarayan clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; 3302336Snarayan (void) mutex_unlock(&clientp->lock); 3312336Snarayan return (VNTSD_SUCCESS); 3322336Snarayan } 3332336Snarayan 3341991Sheppo for (p = etable; p->e_char; p++) { 3351991Sheppo if (p->e_char == c) { 3361991Sheppo /* found match */ 3371991Sheppo assert(p->e_func); 3381991Sheppo rv = (*p->e_func)(clientp); 3391991Sheppo return (exit_daemon_cmd(clientp, rv)); 3401991Sheppo } 3411991Sheppo } 3421991Sheppo 3431991Sheppo /* no match, print out the help */ 3441991Sheppo p--; 3451991Sheppo assert(p->e_char == '?'); 3461991Sheppo rv = (*p->e_func)(clientp); 3471991Sheppo 3481991Sheppo return (exit_daemon_cmd(clientp, rv)); 3491991Sheppo 3501991Sheppo } 3511991Sheppo 3521991Sheppo /* vntsd_set_telnet_options() - change telnet client to character mode. */ 3531991Sheppo int 3541991Sheppo vntsd_set_telnet_options(int fd) 3551991Sheppo { 3561991Sheppo /* set client telnet options */ 3571991Sheppo uint8_t buf[] = {IAC, DONT, LINEMODE, IAC, WILL, SUPRESS, IAC, WILL, 3581991Sheppo TEL_ECHO, IAC, DONT, TERM_TYPE, IAC, DONT, TERM_SP, 3591991Sheppo IAC, DONT, STATUS, IAC, DONT, FC, IAC, DONT, TM, IAC, DONT, ENV, 3601991Sheppo IAC, DONT, WIN_SIZE}; 3611991Sheppo 3621991Sheppo return (vntsd_write_fd(fd, (char *)buf, 30)); 3631991Sheppo } 3641991Sheppo 3651991Sheppo /* vntsd_telnet_cmd() process telnet commands */ 3661991Sheppo int 3671991Sheppo vntsd_telnet_cmd(vntsd_client_t *clientp, char c) 3681991Sheppo { 3691991Sheppo uint8_t buf[4]; 3701991Sheppo char cmd; 3711991Sheppo int rv = VNTSD_STATUS_CONTINUE; 3721991Sheppo 3731991Sheppo bzero(buf, 4); 3741991Sheppo 3751991Sheppo if ((uint8_t)c != IAC) { 3761991Sheppo /* not telnet cmd */ 3771991Sheppo return (VNTSD_SUCCESS); 3781991Sheppo } 3791991Sheppo 3801991Sheppo if ((rv = vntsd_read_char(clientp, &cmd)) != VNTSD_SUCCESS) { 3811991Sheppo return (rv); 3821991Sheppo } 3831991Sheppo 384*8498SChris.Gerhard@sun.com if ((uint8_t)cmd == WILL || (uint8_t)cmd == WONT || 385*8498SChris.Gerhard@sun.com (uint8_t)cmd == DO || (uint8_t)cmd == DONT) { 3865757Sdtse if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { 3875757Sdtse return (rv); 3885757Sdtse } 3891991Sheppo } 3901991Sheppo 3911991Sheppo 3921991Sheppo switch ((uint8_t)cmd) { 3931991Sheppo 3941991Sheppo case WILL: 3951991Sheppo 3961991Sheppo switch ((uint8_t)c) { 3971991Sheppo case TEL_ECHO: 3981991Sheppo case SUPRESS: 3991991Sheppo case LINEMODE: 4001991Sheppo break; 4011991Sheppo default: 4021991Sheppo syslog(LOG_ERR, "not support telnet WILL %x\n", c); 4031991Sheppo break; 4041991Sheppo } 4051991Sheppo break; 4061991Sheppo 4071991Sheppo case WONT: 4081991Sheppo 4091991Sheppo switch ((uint8_t)c) { 4101991Sheppo case TEL_ECHO: 4111991Sheppo case SUPRESS: 4121991Sheppo case LINEMODE: 4131991Sheppo default: 4141991Sheppo syslog(LOG_ERR, "not support telnet WONT %x\n", c); 4151991Sheppo break; 4161991Sheppo } 4171991Sheppo break; 4181991Sheppo 4191991Sheppo case DO: 4201991Sheppo case DONT: 4211991Sheppo 4221991Sheppo buf[0] = IAC; 4231991Sheppo buf[1] = WILL; 4241991Sheppo buf[2] = c; 4251991Sheppo rv = vntsd_write_client(clientp, (char *)buf, 3); 4261991Sheppo 4271991Sheppo break; 4281991Sheppo 4291991Sheppo case BRK: 4301991Sheppo 4311991Sheppo /* send break to vcc */ 4321991Sheppo rv = genbrk(clientp); 4331991Sheppo break; 4341991Sheppo 4351991Sheppo case IP: 4361991Sheppo 4371991Sheppo break; 4381991Sheppo 439*8498SChris.Gerhard@sun.com case AYT: { 440*8498SChris.Gerhard@sun.com static char aytresp[] = "vntsd here"; 4411991Sheppo 442*8498SChris.Gerhard@sun.com rv = vntsd_write_client(clientp, aytresp, 443*8498SChris.Gerhard@sun.com sizeof (aytresp) - 1); 444*8498SChris.Gerhard@sun.com break; 445*8498SChris.Gerhard@sun.com } 4461991Sheppo 4471991Sheppo case HT: 448*8498SChris.Gerhard@sun.com case NOP: 4491991Sheppo return (VNTSD_STATUS_CONTINUE); 4501991Sheppo 4511991Sheppo default: 452*8498SChris.Gerhard@sun.com syslog(LOG_ERR, "not support telnet ctrl %2.2x\n", 0xff & cmd); 4531991Sheppo break; 4541991Sheppo } 4551991Sheppo 4561991Sheppo if (rv == VNTSD_SUCCESS) { 4571991Sheppo return (VNTSD_STATUS_CONTINUE); 4581991Sheppo } else { 4591991Sheppo return (rv); 4601991Sheppo } 4611991Sheppo } 4621991Sheppo 4631991Sheppo 4641991Sheppo /* 4651991Sheppo * vntsd_ctrl_cmd() - control keys 4661991Sheppo * read and write suspend are supported. 4671991Sheppo */ 4681991Sheppo int 4691991Sheppo vntsd_ctrl_cmd(vntsd_client_t *clientp, char c) 4701991Sheppo { 4711991Sheppo int cmd; 4721991Sheppo 4731991Sheppo D3(stderr, "t@%d vntsd_ctrl_cmd%d %d\n", thr_self(), 4741991Sheppo clientp->cons->vcc_fd, clientp->sockfd); 4751991Sheppo 4761991Sheppo if ((c != START) && (c != STOP)) { 4771991Sheppo /* not a supported control command */ 4781991Sheppo return (VNTSD_SUCCESS); 4791991Sheppo } 4801991Sheppo 4811991Sheppo if (c == START) { 4821991Sheppo D3(stderr, "t@%d client restart\n", thr_self()); 4831991Sheppo 4841991Sheppo /* send resume read */ 4851991Sheppo cmd = 1; 4861991Sheppo 4871991Sheppo if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 4881991Sheppo return (VNTSD_STATUS_VCC_IO_ERR); 4891991Sheppo } 4901991Sheppo 4911991Sheppo } 4921991Sheppo 4931991Sheppo if (c == STOP) { 4941991Sheppo D3(stderr, "t@%d client suspend\n", thr_self()); 4951991Sheppo 4961991Sheppo /* send suspend read */ 4971991Sheppo cmd = 0; 4981991Sheppo 4991991Sheppo if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 5001991Sheppo return (VNTSD_STATUS_VCC_IO_ERR); 5011991Sheppo } 5021991Sheppo 5031991Sheppo } 5041991Sheppo 5051991Sheppo return (VNTSD_STATUS_CONTINUE); 5061991Sheppo } 507