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