Home | History | Annotate | Download | only in vntsd
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * Vntsd handles two types of special commands, one is telnet
     28  * commands and another is vntsd special commands.
     29  * telnet commands supported are:
     30  * WILL
     31  * WONT
     32  * DO
     33  * DONT
     34  *  TEL_ECHO
     35  *  SUPRESS
     36  *  LINEMODE
     37  * BRK
     38  * AYT
     39  * HT
     40  * NOP
     41  *
     42  * Vntsd special commands are:
     43  *  Send break			(~#)
     44  *  Send alternate break	(~^B)
     45  *  Exit			(~.)
     46  *  Force write access		(~w)
     47  *  Console next		(~n)
     48  *  Console previous		(~p)
     49  *  Help			(~?)
     50  */
     51 
     52 #include <stdio.h>
     53 #include <stdlib.h>
     54 #include <string.h>
     55 #include <unistd.h>
     56 #include <sys/types.h>
     57 #include <sys/socket.h>
     58 #include <netinet/in.h>
     59 #include <thread.h>
     60 #include <ctype.h>
     61 #include <sys/termio.h>
     62 #include <libintl.h>
     63 #include <syslog.h>
     64 #include "vntsd.h"
     65 #include "chars.h"
     66 
     67 char vntsd_eol[] = { CR, LF, 0};
     68 
     69 typedef	int	    (*e_func_t)(vntsd_client_t *clientp);
     70 /* structure for daemon special cmd */
     71 typedef struct {
     72 	char e_char;				/* char to match on */
     73 	char *e_help;				/* help string */
     74 	e_func_t e_func;			/* command */
     75 } esctable_t;
     76 
     77 /* genbrk() -  send a break to vcc driver */
     78 static int
     79 genbrk(vntsd_client_t *clientp)
     80 {
     81 
     82 	vntsd_cons_t *consp;
     83 
     84 	assert(clientp);
     85 	assert(clientp->cons);
     86 
     87 	consp = clientp->cons;
     88 	D1(stderr, "t@%d genbrk fd=%d sockfd %d\n", thr_self(),
     89 	    consp->vcc_fd, clientp->sockfd);
     90 
     91 	assert(consp->clientpq != NULL);
     92 	if (consp->clientpq->handle != clientp) {
     93 		/* reader */
     94 		return (vntsd_write_line(clientp,
     95 		    gettext(VNTSD_NO_WRITE_ACCESS_MSG)));
     96 	}
     97 
     98 	/* writer */
     99 	if (ioctl(consp->vcc_fd, TCSBRK, NULL)) {
    100 		return (VNTSD_ERR_VCC_IOCTL);
    101 	}
    102 
    103 	return (VNTSD_STATUS_CONTINUE);
    104 }
    105 
    106 
    107 /* genaltbrk() - handle the alternate break sequence */
    108 static int
    109 genaltbrk(vntsd_client_t *clientp)
    110 {
    111 	vntsd_cons_t *consp;
    112 	char brkseq[2] = { '~', CNTRL('B')};
    113 
    114 	assert(clientp);
    115 	assert(clientp->cons);
    116 
    117 	consp = clientp->cons;
    118 	D1(stderr, "t@%d genaltbrk fd=%d sockfd %d\n", thr_self(),
    119 	    consp->vcc_fd, clientp->sockfd);
    120 
    121 	assert(consp->clientpq != NULL);
    122 	if (consp->clientpq->handle != clientp) {
    123 		/* reader */
    124 		return (vntsd_write_line(clientp,
    125 		    gettext(VNTSD_NO_WRITE_ACCESS_MSG)));
    126 	}
    127 
    128 	/*
    129 	 * Unlike the genbrk() function we will just forward the break sequence
    130 	 * on to vcc and subsequently the underlying console driver. This will
    131 	 * involve sending the characters '~' and CNTRL('B').
    132 	 */
    133 	if ((vntsd_write_fd(clientp->cons->vcc_fd, brkseq, sizeof (brkseq))) ==
    134 	    VNTSD_SUCCESS)
    135 		return (VNTSD_STATUS_CONTINUE);
    136 	else
    137 		return (VNTSD_STATUS_VCC_IO_ERR);
    138 }
    139 
    140 /*
    141  * console_forward()  - cycle client to the next console
    142  * in the group queue.
    143  */
    144 static int
    145 console_forward(vntsd_client_t *clientp)
    146 {
    147 	/* forward when there are mutiple consoles in the group */
    148 	if (clientp->cons->group->num_cons > 1)
    149 		return (VNTSD_STATUS_MOV_CONS_FORWARD);
    150 
    151 	return (VNTSD_STATUS_CONTINUE);
    152 
    153 }
    154 
    155 /*
    156  * console_backward()  - cycle client to the previous
    157  * console in the group queue.
    158  */
    159 static int
    160 console_backward(vntsd_client_t *clientp)
    161 {
    162 	/* backward when there are mutiple consoles in the group */
    163 	if (clientp->cons->group->num_cons > 1)
    164 		return (VNTSD_STATUS_MOV_CONS_BACKWARD);
    165 
    166 	return (VNTSD_STATUS_CONTINUE);
    167 
    168 }
    169 
    170 /* acquire_write() - acquire write access to a console. */
    171 static int
    172 acquire_write(vntsd_client_t *clientp)
    173 {
    174 	int	rv;
    175 	int	yes_no = 1;
    176 	vntsd_cons_t *consp;
    177 
    178 	assert(clientp);
    179 	consp = clientp->cons;
    180 	assert(consp);
    181 
    182 	if (consp->clientpq->handle == clientp) {
    183 		/* client is a  writer */
    184 		if ((rv = vntsd_write_line(clientp,
    185 		    gettext("You have write permission"))) !=
    186 		    VNTSD_SUCCESS) {
    187 			return (rv);
    188 
    189 		}
    190 		return (VNTSD_STATUS_CONTINUE);
    191 	}
    192 
    193 	/* message to client */
    194 	if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN))
    195 	    != VNTSD_SUCCESS) {
    196 		return (rv);
    197 	}
    198 
    199 	/*
    200 	 * TRANSLATION_NOTE
    201 	 * The following string should be formatted to fit on multiple lines
    202 	 * assuming a line width of at most 78 characters. There must be no
    203 	 * trailing newline.
    204 	 */
    205 	if ((rv = vntsd_write_lines(clientp,
    206 	    gettext("Warning: another user currently "
    207 	    "has write permission\nto this console and forcibly removing "
    208 	    "him/her will terminate\nany current write action and all work "
    209 	    "will be lost."))) != VNTSD_SUCCESS) {
    210 		return (rv);
    211 	}
    212 
    213 	/* get client yes no */
    214 	if ((rv = vntsd_write_client(clientp, vntsd_eol,
    215 	    VNTSD_EOL_LEN)) != VNTSD_SUCCESS) {
    216 		return (rv);
    217 	}
    218 
    219 	if ((rv = vntsd_get_yes_no(clientp,
    220 	    gettext("Would you like to continue?"),
    221 	    &yes_no)) != VNTSD_SUCCESS) {
    222 		return (rv);
    223 	}
    224 
    225 	if (yes_no == B_FALSE) {
    226 		/* client change mind no need to acquire  write access */
    227 		return (VNTSD_STATUS_CONTINUE);
    228 	}
    229 
    230 	return (VNTSD_STATUS_ACQUIRE_WRITER);
    231 }
    232 
    233 /* client_exit()  - disconnect client from the console. */
    234 static int
    235 client_exit(void)
    236 {
    237 	return (VNTSD_STATUS_RESELECT_CONS);
    238 }
    239 
    240 static int daemon_cmd_help(vntsd_client_t *clientp);
    241 
    242 /* table for daemon commands */
    243 
    244 static esctable_t  etable[] = {
    245 
    246 	/* send a break to vcc */
    247 	{'#', "Send break",  genbrk},
    248 
    249 	/* alternate break sequence */
    250 	{CNTRL('B'), "Send alternate break", genaltbrk},
    251 
    252 	/* exit */
    253 	{'.', "Exit from this console",  (e_func_t)client_exit},
    254 
    255 	/* acquire write access */
    256 	{'w', "Force write access", acquire_write},
    257 
    258 	/* connect to next console in queue */
    259 	{'n', "Console next", (e_func_t)console_forward},
    260 
    261 	/* connect to previous console in queue */
    262 	{'p', "Console previous", (e_func_t)console_backward},
    263 
    264 	/* help must be next to last */
    265 	{'?', "Help", daemon_cmd_help},
    266 
    267 	/* table terminator */
    268 	{0, 0, 0}
    269 };
    270 
    271 void
    272 vntsd_init_esctable_msgs(void)
    273 {
    274 	esctable_t  *p;
    275 
    276 	for (p = etable; p->e_char != '\0'; p++) {
    277 		p->e_help = gettext(p->e_help);
    278 	}
    279 }
    280 
    281 /* daemon_cmd_help() - print help. */
    282 static int
    283 daemon_cmd_help(vntsd_client_t *clientp)
    284 {
    285 	esctable_t  *p;
    286 	int	    rv;
    287 	char	    buf[VNTSD_LINE_LEN];
    288 
    289 	if ((rv = vntsd_write_client(clientp, vntsd_eol,
    290 	    VNTSD_EOL_LEN)) != VNTSD_SUCCESS) {
    291 		return (rv);
    292 	}
    293 
    294 	/*
    295 	 * TRANSLATION_NOTE
    296 	 * VNTSD is the name of the VNTS daemon and should not be translated.
    297 	 */
    298 	if ((rv = vntsd_write_line(clientp, gettext("VNTSD commands"))) !=
    299 	    VNTSD_SUCCESS) {
    300 		return (rv);
    301 	}
    302 
    303 	for (p = etable; p->e_char; p++) {
    304 
    305 		if (p->e_char == CNTRL('B')) {
    306 			(void) snprintf(buf, sizeof (buf), "~^B --%s",
    307 			    p->e_help);
    308 		} else {
    309 			(void) snprintf(buf, sizeof (buf),
    310 			    "~%c --%s", p->e_char, p->e_help);
    311 		}
    312 
    313 		if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) {
    314 			return (rv);
    315 		}
    316 	}
    317 
    318 	return (VNTSD_STATUS_CONTINUE);
    319 }
    320 
    321 /* exit from daemon command */
    322 static int
    323 exit_daemon_cmd(vntsd_client_t *clientp, int rv)
    324 {
    325 	(void) mutex_lock(&clientp->lock);
    326 	clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD;
    327 	(void) mutex_unlock(&clientp->lock);
    328 	return (rv);
    329 }
    330 
    331 /*
    332  * vntsd_process_daemon_cmd() - special commands
    333  * "<RET>~"  vntsd daemon commands
    334  * "<RET>~~" enter '~' character
    335  */
    336 int
    337 vntsd_process_daemon_cmd(vntsd_client_t *clientp, char c)
    338 {
    339 	esctable_t *p;
    340 	int	    rv;
    341 	char	    prev_char;
    342 
    343 	prev_char = clientp->prev_char;
    344 
    345 	if (c != VNTSD_DAEMON_CMD || (prev_char != 0 && prev_char != CR)) {
    346 		/* not a daemon command */
    347 		return (VNTSD_SUCCESS);
    348 	}
    349 
    350 	if (clientp->status & VNTSD_CLIENT_DISABLE_DAEMON_CMD) {
    351 		return (VNTSD_STATUS_CONTINUE);
    352 	}
    353 
    354 	/* no reentry to process_daemon_cmd */
    355 	(void) mutex_lock(&clientp->lock);
    356 	clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD;
    357 	(void) mutex_unlock(&clientp->lock);
    358 
    359 	D3(stderr, "t@%d process_daemon_cmd %d %d \n", thr_self(),
    360 	    clientp->cons->vcc_fd, clientp->sockfd);
    361 
    362 	/* read in command */
    363 	if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) {
    364 		return (exit_daemon_cmd(clientp, rv));
    365 	}
    366 
    367 	if (c == VNTSD_DAEMON_CMD) {
    368 		/*
    369 		 * received another '~'
    370 		 * a user types '~~' to get '~'
    371 		 */
    372 		(void) mutex_lock(&clientp->lock);
    373 		clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD;
    374 		(void) mutex_unlock(&clientp->lock);
    375 		return (VNTSD_SUCCESS);
    376 	}
    377 
    378 	for (p = etable; p->e_char; p++) {
    379 		if (p->e_char == c) {
    380 			/* found match */
    381 			assert(p->e_func);
    382 			rv = (*p->e_func)(clientp);
    383 			return (exit_daemon_cmd(clientp, rv));
    384 		}
    385 	}
    386 
    387 	/* no match, print out the help */
    388 	p--;
    389 	assert(p->e_char == '?');
    390 	rv = (*p->e_func)(clientp);
    391 
    392 	return (exit_daemon_cmd(clientp, rv));
    393 
    394 }
    395 
    396 /* vntsd_set_telnet_options() - change  telnet client to  character mode. */
    397 int
    398 vntsd_set_telnet_options(int fd)
    399 {
    400 	/* set client telnet options */
    401 	uint8_t buf[] = {IAC, DONT, LINEMODE, IAC, WILL, SUPRESS, IAC, WILL,
    402 		TEL_ECHO, IAC, DONT, TERM_TYPE, IAC, DONT, TERM_SP,
    403 		IAC, DONT, STATUS, IAC, DONT, FC, IAC, DONT, TM, IAC, DONT, ENV,
    404 		IAC, DONT, WIN_SIZE};
    405 
    406 	return (vntsd_write_fd(fd, (char *)buf, 30));
    407 }
    408 
    409 /*  vntsd_telnet_cmd() process telnet commands */
    410 int
    411 vntsd_telnet_cmd(vntsd_client_t *clientp, char c)
    412 {
    413 	uint8_t	buf[4];
    414 	char	cmd;
    415 	int	rv = VNTSD_STATUS_CONTINUE;
    416 
    417 	bzero(buf, 4);
    418 
    419 	if ((uint8_t)c != IAC) {
    420 		/* not telnet cmd */
    421 		return (VNTSD_SUCCESS);
    422 	}
    423 
    424 	if ((rv = vntsd_read_char(clientp, &cmd)) != VNTSD_SUCCESS) {
    425 		return (rv);
    426 	}
    427 
    428 	if ((uint8_t)cmd == WILL || (uint8_t)cmd == WONT ||
    429 	    (uint8_t)cmd == DO || (uint8_t)cmd == DONT) {
    430 		if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) {
    431 			return (rv);
    432 		}
    433 	}
    434 
    435 
    436 	switch ((uint8_t)cmd) {
    437 
    438 	case WILL:
    439 
    440 		switch ((uint8_t)c) {
    441 		case TEL_ECHO:
    442 		case SUPRESS:
    443 		case LINEMODE:
    444 			break;
    445 		default:
    446 			syslog(LOG_ERR, "not support telnet WILL %x\n", c);
    447 			break;
    448 		}
    449 		break;
    450 
    451 	case  WONT:
    452 
    453 		switch ((uint8_t)c) {
    454 		case TEL_ECHO:
    455 		case SUPRESS:
    456 		case LINEMODE:
    457 		default:
    458 			syslog(LOG_ERR, "not support telnet WONT %x\n", c);
    459 			break;
    460 		}
    461 		break;
    462 
    463 	case DO:
    464 	case DONT:
    465 
    466 		buf[0] = IAC;
    467 		buf[1] = WILL;
    468 		buf[2] = c;
    469 		rv = vntsd_write_client(clientp, (char *)buf, 3);
    470 
    471 		break;
    472 
    473 	case BRK:
    474 
    475 		/* send break to vcc */
    476 		rv = genbrk(clientp);
    477 		break;
    478 
    479 	case IP:
    480 
    481 		break;
    482 
    483 	case AYT: {
    484 			static char aytresp[] = "vntsd here";
    485 
    486 			rv = vntsd_write_client(clientp, aytresp,
    487 			    sizeof (aytresp) - 1);
    488 			break;
    489 		}
    490 
    491 	case HT:
    492 	case NOP:
    493 		return (VNTSD_STATUS_CONTINUE);
    494 
    495 	default:
    496 		syslog(LOG_ERR, "not support telnet ctrl %2.2x\n", 0xff & cmd);
    497 		break;
    498 	}
    499 
    500 	if (rv == VNTSD_SUCCESS) {
    501 		return (VNTSD_STATUS_CONTINUE);
    502 	} else {
    503 		return (rv);
    504 	}
    505 }
    506 
    507 
    508 /*
    509  * vntsd_ctrl_cmd()   - control keys
    510  * read and write suspend are supported.
    511  */
    512 int
    513 vntsd_ctrl_cmd(vntsd_client_t *clientp, char c)
    514 {
    515 	int	cmd;
    516 
    517 	D3(stderr, "t@%d vntsd_ctrl_cmd%d %d\n", thr_self(),
    518 	    clientp->cons->vcc_fd, clientp->sockfd);
    519 
    520 	if ((c != START) && (c != STOP)) {
    521 		/* not a supported control command */
    522 		return (VNTSD_SUCCESS);
    523 	}
    524 
    525 	if (c == START) {
    526 		D3(stderr, "t@%d client restart\n", thr_self());
    527 
    528 		/* send resume read */
    529 		cmd = 1;
    530 
    531 		if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) {
    532 			return (VNTSD_STATUS_VCC_IO_ERR);
    533 		}
    534 
    535 	}
    536 
    537 	if (c == STOP) {
    538 		D3(stderr, "t@%d client suspend\n", thr_self());
    539 
    540 		/* send suspend read */
    541 		cmd = 0;
    542 
    543 		if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) {
    544 			return (VNTSD_STATUS_VCC_IO_ERR);
    545 		}
    546 
    547 	}
    548 
    549 	return (VNTSD_STATUS_CONTINUE);
    550 }
    551