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
genbrk(vntsd_client_t * clientp)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
genaltbrk(vntsd_client_t * clientp)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
console_forward(vntsd_client_t * clientp)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
console_backward(vntsd_client_t * clientp)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
acquire_write(vntsd_client_t * clientp)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
client_exit(vntsd_client_t * arg __unused)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
vntsd_init_esctable_msgs(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
daemon_cmd_help(vntsd_client_t * clientp)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
exit_daemon_cmd(vntsd_client_t * clientp,int rv)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
vntsd_process_daemon_cmd(vntsd_client_t * clientp,char c)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
vntsd_set_telnet_options(int fd)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
vntsd_telnet_cmd(vntsd_client_t * clientp,char c)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
vntsd_ctrl_cmd(vntsd_client_t * clientp,char c)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