xref: /titanic_41/usr/src/cmd/vntsd/cmd.c (revision a62774df315360f02521d6470eab7d5080137dad)
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
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
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
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
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
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
235  client_exit(void)
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",  (e_func_t)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
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
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
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
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
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
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
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