xref: /illumos-gate/usr/src/cmd/vntsd/cmd.c (revision 1f6352c601b7f3c744b4684ff673c813927da24c)
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