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 * cycle console down (~n) 48 * cycle console up (~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 down", (e_func_t)console_forward}, 213 214 /* connect to previous console in queue */ 215 {'p', "console up", (e_func_t)console_backward}, 216 217 /* help must be next to last */ 218 {'?', "_", 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 /* vntsd_process_daemon_cmd() - special commands */ 279 int 280 vntsd_process_daemon_cmd(vntsd_client_t *clientp, char c) 281 { 282 esctable_t *p; 283 int rv; 284 285 if (c != VNTSD_DAEMON_CMD) { 286 /* not a daemon command */ 287 return (VNTSD_SUCCESS); 288 } 289 290 if (clientp->status & VNTSD_CLIENT_DISABLE_DAEMON_CMD) { 291 return (VNTSD_STATUS_CONTINUE); 292 } 293 294 /* no reentry to process_daemon_cmd */ 295 (void) mutex_lock(&clientp->lock); 296 clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD; 297 (void) mutex_unlock(&clientp->lock); 298 299 D3(stderr, "t@%d process_daemon_cmd %d %d \n", thr_self(), 300 clientp->cons->vcc_fd, clientp->sockfd); 301 302 /* read in command */ 303 if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { 304 return (exit_daemon_cmd(clientp, rv)); 305 } 306 307 for (p = etable; p->e_char; p++) { 308 if (p->e_char == c) { 309 /* found match */ 310 assert(p->e_func); 311 rv = (*p->e_func)(clientp); 312 return (exit_daemon_cmd(clientp, rv)); 313 } 314 } 315 316 /* no match, print out the help */ 317 p--; 318 assert(p->e_char == '?'); 319 rv = (*p->e_func)(clientp); 320 321 return (exit_daemon_cmd(clientp, rv)); 322 323 } 324 325 /* vntsd_set_telnet_options() - change telnet client to character mode. */ 326 int 327 vntsd_set_telnet_options(int fd) 328 { 329 /* set client telnet options */ 330 uint8_t buf[] = {IAC, DONT, LINEMODE, IAC, WILL, SUPRESS, IAC, WILL, 331 TEL_ECHO, IAC, DONT, TERM_TYPE, IAC, DONT, TERM_SP, 332 IAC, DONT, STATUS, IAC, DONT, FC, IAC, DONT, TM, IAC, DONT, ENV, 333 IAC, DONT, WIN_SIZE}; 334 335 return (vntsd_write_fd(fd, (char *)buf, 30)); 336 } 337 338 /* vntsd_telnet_cmd() process telnet commands */ 339 int 340 vntsd_telnet_cmd(vntsd_client_t *clientp, char c) 341 { 342 uint8_t buf[4]; 343 char cmd; 344 int rv = VNTSD_STATUS_CONTINUE; 345 346 bzero(buf, 4); 347 348 if ((uint8_t)c != IAC) { 349 /* not telnet cmd */ 350 return (VNTSD_SUCCESS); 351 } 352 353 if ((rv = vntsd_read_char(clientp, &cmd)) != VNTSD_SUCCESS) { 354 return (rv); 355 } 356 357 if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) { 358 return (rv); 359 } 360 361 362 switch ((uint8_t)cmd) { 363 364 case WILL: 365 366 switch ((uint8_t)c) { 367 case TEL_ECHO: 368 case SUPRESS: 369 case LINEMODE: 370 break; 371 default: 372 syslog(LOG_ERR, "not support telnet WILL %x\n", c); 373 break; 374 } 375 break; 376 377 case WONT: 378 379 switch ((uint8_t)c) { 380 case TEL_ECHO: 381 case SUPRESS: 382 case LINEMODE: 383 default: 384 syslog(LOG_ERR, "not support telnet WONT %x\n", c); 385 break; 386 } 387 break; 388 389 case DO: 390 case DONT: 391 392 buf[0] = IAC; 393 buf[1] = WILL; 394 buf[2] = c; 395 rv = vntsd_write_client(clientp, (char *)buf, 3); 396 397 break; 398 399 case BRK: 400 401 /* send break to vcc */ 402 rv = genbrk(clientp); 403 break; 404 405 case IP: 406 407 break; 408 409 case AYT: 410 411 rv = vntsd_write_client(clientp, &c, 1); 412 break; 413 414 case HT: 415 return (VNTSD_STATUS_CONTINUE); 416 417 default: 418 syslog(LOG_ERR, "not support telnet ctrl %x\n", c); 419 break; 420 } 421 422 if (rv == VNTSD_SUCCESS) { 423 return (VNTSD_STATUS_CONTINUE); 424 } else { 425 return (rv); 426 } 427 } 428 429 430 /* 431 * vntsd_ctrl_cmd() - control keys 432 * read and write suspend are supported. 433 */ 434 int 435 vntsd_ctrl_cmd(vntsd_client_t *clientp, char c) 436 { 437 int cmd; 438 439 D3(stderr, "t@%d vntsd_ctrl_cmd%d %d\n", thr_self(), 440 clientp->cons->vcc_fd, clientp->sockfd); 441 442 if ((c != START) && (c != STOP)) { 443 /* not a supported control command */ 444 return (VNTSD_SUCCESS); 445 } 446 447 if (c == START) { 448 449 D3(stderr, "t@%d client restart\n", thr_self()); 450 451 /* send resume read */ 452 cmd = 1; 453 454 if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 455 return (VNTSD_STATUS_VCC_IO_ERR); 456 } 457 458 /* send resume write */ 459 cmd = 3; 460 461 if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 462 return (VNTSD_STATUS_VCC_IO_ERR); 463 } 464 } 465 466 if (c == STOP) { 467 D3(stderr, "t@%d client suspend\n", thr_self()); 468 469 /* send suspend read */ 470 cmd = 0; 471 472 if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 473 return (VNTSD_STATUS_VCC_IO_ERR); 474 } 475 476 /* send suspend write */ 477 cmd = 2; 478 479 if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) { 480 perror("ioctl TCXONC"); 481 return (VNTSD_STATUS_VCC_IO_ERR); 482 } 483 } 484 485 return (VNTSD_STATUS_CONTINUE); 486 } 487