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