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