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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Vntsd handles two types of special commands, one is telnet 30 * commands and another is vntsd special commands. 31 * telnet commands supported are: 32 * WILL 33 * WONT 34 * DO 35 * DONT 36 * TEL_ECHO 37 * SUPRESS 38 * LINEMODE 39 * BRK 40 * AYT 41 * HT 42 * 43 * Vntsd special commands are: 44 * Send break (~#) 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 * console_forward() - cycle client to the next console 108 * in the group queue. 109 */ 110 static int 111 console_forward(vntsd_client_t *clientp) 112 { 113 /* forward when there are mutiple consoles in the group */ 114 if (clientp->cons->group->num_cons > 1) 115 return (VNTSD_STATUS_MOV_CONS_FORWARD); 116 117 return (VNTSD_STATUS_CONTINUE); 118 119 } 120 121 /* 122 * console_backward() - cycle client to the previous 123 * console in the group queue. 124 */ 125 static int 126 console_backward(vntsd_client_t *clientp) 127 { 128 /* backward when there are mutiple consoles in the group */ 129 if (clientp->cons->group->num_cons > 1) 130 return (VNTSD_STATUS_MOV_CONS_BACKWARD); 131 132 return (VNTSD_STATUS_CONTINUE); 133 134 } 135 136 /* acquire_write() - acquire write access to a console. */ 137 static int 138 acquire_write(vntsd_client_t *clientp) 139 { 140 int rv; 141 int yes_no = 1; 142 vntsd_cons_t *consp; 143 144 assert(clientp); 145 consp = clientp->cons; 146 assert(consp); 147 148 if (consp->clientpq->handle == clientp) { 149 /* client is a writer */ 150 if ((rv = vntsd_write_line(clientp, 151 gettext("You have write permission"))) != 152 VNTSD_SUCCESS) { 153 return (rv); 154 155 } 156 return (VNTSD_STATUS_CONTINUE); 157 } 158 159 /* message to client */ 160 if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN)) 161 != VNTSD_SUCCESS) { 162 return (rv); 163 } 164 165 /* 166 * TRANSLATION_NOTE 167 * The following string should be formatted to fit on multiple lines 168 * assuming a line width of at most 78 characters. There must be no 169 * trailing newline. 170 */ 171 if ((rv = vntsd_write_lines(clientp, 172 gettext("Warning: another user currently " 173 "has write permission\nto this console and forcibly removing " 174 "him/her will terminate\nany current write action and all work " 175 "will be lost."))) != VNTSD_SUCCESS) { 176 return (rv); 177 } 178 179 /* get client yes no */ 180 if ((rv = vntsd_write_client(clientp, vntsd_eol, 181 VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { 182 return (rv); 183 } 184 185 if ((rv = vntsd_get_yes_no(clientp, 186 gettext("Would you like to continue?"), 187 &yes_no)) != VNTSD_SUCCESS) { 188 return (rv); 189 } 190 191 if (yes_no == B_FALSE) { 192 /* client change mind no need to acquire write access */ 193 return (VNTSD_STATUS_CONTINUE); 194 } 195 196 return (VNTSD_STATUS_ACQUIRE_WRITER); 197 } 198 199 /* client_exit() - disconnect client from the console. */ 200 static int 201 client_exit(void) 202 { 203 return (VNTSD_STATUS_RESELECT_CONS); 204 } 205 206 static int daemon_cmd_help(vntsd_client_t *clientp); 207 208 /* table for daemon commands */ 209 210 static esctable_t etable[] = { 211 212 /* send a break to vcc */ 213 {'#', "Send break", genbrk}, 214 215 /* exit */ 216 {'.', "Exit from this console", (e_func_t)client_exit}, 217 218 /* acquire write access */ 219 {'w', "Force write access", acquire_write}, 220 221 /* connect to next console in queue */ 222 {'n', "Console next", (e_func_t)console_forward}, 223 224 /* connect to previous console in queue */ 225 {'p', "Console previous", (e_func_t)console_backward}, 226 227 /* help must be next to last */ 228 {'?', "Help", daemon_cmd_help}, 229 230 /* table terminator */ 231 {0, 0, 0} 232 }; 233 234 void 235 vntsd_init_esctable_msgs(void) 236 { 237 esctable_t *p; 238 239 for (p = etable; p->e_char != '\0'; p++) { 240 p->e_help = gettext(p->e_help); 241 } 242 } 243 244 /* daemon_cmd_help() - print help. */ 245 static int 246 daemon_cmd_help(vntsd_client_t *clientp) 247 { 248 esctable_t *p; 249 int rv; 250 char buf[VNTSD_LINE_LEN]; 251 252 if ((rv = vntsd_write_client(clientp, vntsd_eol, 253 VNTSD_EOL_LEN)) != VNTSD_SUCCESS) { 254 return (rv); 255 } 256 257 /* 258 * TRANSLATION_NOTE 259 * VNTSD is the name of the VNTS daemon and should not be translated. 260 */ 261 if ((rv = vntsd_write_line(clientp, gettext("VNTSD commands"))) != 262 VNTSD_SUCCESS) { 263 return (rv); 264 } 265 266 for (p = etable; p->e_char; p++) { 267 (void) snprintf(buf, sizeof (buf), 268 "~%c --%s", p->e_char, p->e_help); 269 270 if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) { 271 return (rv); 272 } 273 } 274 275 return (VNTSD_STATUS_CONTINUE); 276 } 277 278 /* exit from daemon command */ 279 static int 280 exit_daemon_cmd(vntsd_client_t *clientp, int rv) 281 { 282 (void) mutex_lock(&clientp->lock); 283 clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; 284 (void) mutex_unlock(&clientp->lock); 285 return (rv); 286 } 287 288 /* 289 * vntsd_process_daemon_cmd() - special commands 290 * "<RET>~" vntsd daemon commands 291 * "<RET>~~" enter '~' character 292 */ 293 int 294 vntsd_process_daemon_cmd(vntsd_client_t *clientp, char c) 295 { 296 esctable_t *p; 297 int rv; 298 char prev_char; 299 300 prev_char = clientp->prev_char; 301 clientp->prev_char = c; 302 303 if (c != VNTSD_DAEMON_CMD || (prev_char != 0 && prev_char != CR)) { 304 /* not a daemon command */ 305 return (VNTSD_SUCCESS); 306 } 307 308 if (clientp->status & VNTSD_CLIENT_DISABLE_DAEMON_CMD) { 309 return (VNTSD_STATUS_CONTINUE); 310 } 311 312 /* no reentry to process_daemon_cmd */ 313 (void) mutex_lock(&clientp->lock); 314 clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; 315 (void) mutex_unlock(&clientp->lock); 316 317 D3(stderr, "t@%d process_daemon_cmd %d %d \n", thr_self(), 318 clientp->cons->vcc_fd, clientp->sockfd); 319 320 /* read in command */ 321 if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { 322 return (exit_daemon_cmd(clientp, rv)); 323 } 324 325 clientp->prev_char = c; 326 if (c == VNTSD_DAEMON_CMD) { 327 /* 328 * received another '~' 329 * a user types '~~' to get '~' 330 */ 331 (void) mutex_lock(&clientp->lock); 332 clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD; 333 (void) mutex_unlock(&clientp->lock); 334 return (VNTSD_SUCCESS); 335 } 336 337 for (p = etable; p->e_char; p++) { 338 if (p->e_char == c) { 339 /* found match */ 340 assert(p->e_func); 341 rv = (*p->e_func)(clientp); 342 return (exit_daemon_cmd(clientp, rv)); 343 } 344 } 345 346 /* no match, print out the help */ 347 p--; 348 assert(p->e_char == '?'); 349 rv = (*p->e_func)(clientp); 350 351 return (exit_daemon_cmd(clientp, rv)); 352 353 } 354 355 /* vntsd_set_telnet_options() - change telnet client to character mode. */ 356 int 357 vntsd_set_telnet_options(int fd) 358 { 359 /* set client telnet options */ 360 uint8_t buf[] = {IAC, DONT, LINEMODE, IAC, WILL, SUPRESS, IAC, WILL, 361 TEL_ECHO, IAC, DONT, TERM_TYPE, IAC, DONT, TERM_SP, 362 IAC, DONT, STATUS, IAC, DONT, FC, IAC, DONT, TM, IAC, DONT, ENV, 363 IAC, DONT, WIN_SIZE}; 364 365 return (vntsd_write_fd(fd, (char *)buf, 30)); 366 } 367 368 /* vntsd_telnet_cmd() process telnet commands */ 369 int 370 vntsd_telnet_cmd(vntsd_client_t *clientp, char c) 371 { 372 uint8_t buf[4]; 373 char cmd; 374 int rv = VNTSD_STATUS_CONTINUE; 375 376 bzero(buf, 4); 377 378 if ((uint8_t)c != IAC) { 379 /* not telnet cmd */ 380 return (VNTSD_SUCCESS); 381 } 382 383 if ((rv = vntsd_read_char(clientp, &cmd)) != VNTSD_SUCCESS) { 384 return (rv); 385 } 386 387 if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { 388 return (rv); 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 441 rv = vntsd_write_client(clientp, &c, 1); 442 break; 443 444 case HT: 445 return (VNTSD_STATUS_CONTINUE); 446 447 default: 448 syslog(LOG_ERR, "not support telnet ctrl %x\n", c); 449 break; 450 } 451 452 if (rv == VNTSD_SUCCESS) { 453 return (VNTSD_STATUS_CONTINUE); 454 } else { 455 return (rv); 456 } 457 } 458 459 460 /* 461 * vntsd_ctrl_cmd() - control keys 462 * read and write suspend are supported. 463 */ 464 int 465 vntsd_ctrl_cmd(vntsd_client_t *clientp, char c) 466 { 467 int cmd; 468 469 D3(stderr, "t@%d vntsd_ctrl_cmd%d %d\n", thr_self(), 470 clientp->cons->vcc_fd, clientp->sockfd); 471 472 if ((c != START) && (c != STOP)) { 473 /* not a supported control command */ 474 return (VNTSD_SUCCESS); 475 } 476 477 if (c == START) { 478 D3(stderr, "t@%d client restart\n", thr_self()); 479 480 /* send resume read */ 481 cmd = 1; 482 483 if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 484 return (VNTSD_STATUS_VCC_IO_ERR); 485 } 486 487 } 488 489 if (c == STOP) { 490 D3(stderr, "t@%d client suspend\n", thr_self()); 491 492 /* send suspend read */ 493 cmd = 0; 494 495 if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 496 return (VNTSD_STATUS_VCC_IO_ERR); 497 } 498 499 } 500 501 return (VNTSD_STATUS_CONTINUE); 502 } 503