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