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(vntsd_client_t *arg __unused) 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", 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