Home | History | Annotate | Download | only in vntsd
      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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     22 
     23 /*
     24  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     25  * Use is subject to license terms.
     26  */
     27 
     28 /*
     29  * read thread - Read from tcp client and write to vcc driver. There  are one
     30  * writer and multiple readers per console. The first client who connects to
     31  * a console get write access. An error message is returned to readers if they
     32  * attemp to input commands. Read thread accepts special daemon commands from
     33  * all clients.
     34  */
     35 
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <string.h>
     39 #include <unistd.h>
     40 #include <sys/types.h>
     41 #include <sys/socket.h>
     42 #include <netinet/in.h>
     43 #include <thread.h>
     44 #include <synch.h>
     45 #include <signal.h>
     46 #include <assert.h>
     47 #include <ctype.h>
     48 #include <syslog.h>
     49 #include <libintl.h>
     50 #include "vntsd.h"
     51 #include "chars.h"
     52 
     53 /* write_vcc()  - write to vcc virtual console */
     54 static int
     55 write_vcc(vntsd_client_t *clientp, char c)
     56 {
     57 	int	n;
     58 
     59 
     60 	assert(clientp);
     61 	assert(clientp->cons);
     62 
     63 	n = write(clientp->cons->vcc_fd, &c, 1);
     64 
     65 	if (n < 0) {
     66 		/* write error */
     67 		if (errno == EINTR) {
     68 			return (vntsd_cons_chk_intr(clientp));
     69 		}
     70 
     71 		return (VNTSD_STATUS_VCC_IO_ERR);
     72 	}
     73 
     74 	assert(n != 0);
     75 	return (VNTSD_SUCCESS);
     76 
     77 }
     78 
     79 /*
     80  * acquire_writer() the client is going to be writer.
     81  * insert the client to the head of the console client queue.
     82  */
     83 static int
     84 acquire_writer(vntsd_client_t *clientp)
     85 {
     86 	vntsd_cons_t	    *consp;
     87 	vntsd_client_t	    *writerp;
     88 	int		    rv;
     89 
     90 	D1(stderr, "t@%d:acuire_writer :client@%d\n", thr_self(),
     91 	    clientp->sockfd);
     92 
     93 	assert(clientp != NULL);
     94 	consp = clientp->cons;
     95 
     96 	assert(consp);
     97 
     98 	(void) mutex_lock(&consp->lock);
     99 
    100 	assert(consp->clientpq != NULL);
    101 	if (consp->clientpq->handle == clientp) {
    102 		/* clientp is a writer already */
    103 		(void) mutex_unlock(&consp->lock);
    104 		return (VNTSD_SUCCESS);
    105 	}
    106 
    107 	/* current writer */
    108 	writerp = (vntsd_client_t *)(consp->clientpq->handle);
    109 
    110 	(void) mutex_lock(&writerp->lock);
    111 
    112 	rv = vntsd_que_rm(&(consp->clientpq), clientp);
    113 	assert(rv == VNTSD_SUCCESS);
    114 
    115 	(void) mutex_lock(&clientp->lock);
    116 
    117 	/* move client to be first in the console queue */
    118 	consp->clientpq->handle = clientp;
    119 
    120 	/* move previous writer to be the second in the queue */
    121 	rv =  vntsd_que_insert_after(consp->clientpq, clientp, writerp);
    122 
    123 	(void) mutex_unlock(&consp->lock);
    124 	(void) mutex_unlock(&writerp->lock);
    125 	(void) mutex_unlock(&clientp->lock);
    126 
    127 	if (rv != VNTSD_SUCCESS) {
    128 		return (rv);
    129 	}
    130 
    131 	/* write warning message to the writer */
    132 
    133 	if ((rv = vntsd_write_line(writerp,
    134 	    gettext("Warning: Console connection forced into read-only mode")))
    135 	    != VNTSD_SUCCESS) {
    136 		return (rv);
    137 	}
    138 
    139 	return (VNTSD_SUCCESS);
    140 }
    141 
    142 /* interrupt handler */
    143 int
    144 vntsd_cons_chk_intr(vntsd_client_t *clientp)
    145 {
    146 
    147 	if (clientp->status & VNTSD_CLIENT_TIMEOUT) {
    148 		return (VNTSD_STATUS_CLIENT_QUIT);
    149 	}
    150 	if (clientp->status & VNTSD_CLIENT_CONS_DELETED) {
    151 		return (VNTSD_STATUS_RESELECT_CONS);
    152 	}
    153 
    154 	if (clientp->status & VNTSD_CLIENT_IO_ERR) {
    155 		return (VNTSD_STATUS_CLIENT_QUIT);
    156 	}
    157 	return (VNTSD_STATUS_CONTINUE);
    158 }
    159 
    160 /* read from client */
    161 static int
    162 read_char(vntsd_client_t *clientp, char *c)
    163 {
    164 	int	    rv;
    165 
    166 	for (; ; ) {
    167 
    168 		rv = vntsd_read_data(clientp, c);
    169 
    170 		switch (rv) {
    171 
    172 		case VNTSD_STATUS_ACQUIRE_WRITER:
    173 			clientp->prev_char = 0;
    174 			rv = acquire_writer(clientp);
    175 			if (rv != VNTSD_SUCCESS) {
    176 				return (rv);
    177 			}
    178 			break;
    179 
    180 		case VNTSD_SUCCESS:
    181 			/*
    182 			 * Based on telnet protocol, when an <eol> is entered,
    183 			 * vntsd receives <0x0d,0x00>. However, console expects
    184 			 * <0xd> only. We need to filter out <0x00>.
    185 			 */
    186 			if (clientp->prev_char == 0xd && *c == 0) {
    187 				clientp->prev_char = *c;
    188 				break;
    189 			}
    190 
    191 			clientp->prev_char = *c;
    192 			return (rv);
    193 
    194 		default:
    195 			assert(rv != VNTSD_STATUS_CONTINUE);
    196 			clientp->prev_char = 0;
    197 			return (rv);
    198 
    199 		}
    200 	}
    201 }
    202 
    203 /* vntsd_read worker */
    204 int
    205 vntsd_read(vntsd_client_t *clientp)
    206 {
    207 	char		c;
    208 	int		rv;
    209 
    210 
    211 	assert(clientp);
    212 	D3(stderr, "t@%d vntsd_read@%d\n", thr_self(), clientp->sockfd);
    213 
    214 	for (; ; ) {
    215 
    216 		/* client input */
    217 		rv = read_char(clientp, &c);
    218 
    219 		if (rv == VNTSD_STATUS_INTR) {
    220 			rv = vntsd_cons_chk_intr(clientp);
    221 		}
    222 
    223 		if (rv != VNTSD_SUCCESS) {
    224 			return (rv);
    225 		}
    226 
    227 		assert(clientp->cons);
    228 
    229 		/*
    230 		 * Only keyboard inputs from first connection to a
    231 		 * guest console should be accepted.  Check to see if
    232 		 * this client is the first connection in console
    233 		 * queue
    234 		 */
    235 		if (clientp->cons->clientpq->handle != clientp) {
    236 			/*
    237 			 * Since this console connection is not the first
    238 			 * connection in the console queue,
    239 			 * it is operating in 'reader'
    240 			 * mode, print warning and ignore the input.
    241 			 */
    242 			rv = vntsd_write_line(clientp,
    243 			    gettext(VNTSD_NO_WRITE_ACCESS_MSG));
    244 
    245 			/* check errors and interrupts */
    246 			if (rv == VNTSD_STATUS_INTR) {
    247 				rv = vntsd_cons_chk_intr(clientp);
    248 			}
    249 
    250 			if (rv != VNTSD_SUCCESS) {
    251 				return (rv);
    252 			}
    253 
    254 			continue;
    255 		}
    256 
    257 		rv = vntsd_ctrl_cmd(clientp, c);
    258 
    259 		switch (rv) {
    260 		case VNTSD_STATUS_CONTINUE:
    261 			continue;
    262 			break;
    263 		case VNTSD_STATUS_INTR:
    264 			rv = vntsd_cons_chk_intr(clientp);
    265 			if (rv != VNTSD_SUCCESS) {
    266 				return (rv);
    267 			}
    268 			break;
    269 		case VNTSD_SUCCESS:
    270 			break;
    271 		default:
    272 			return (rv);
    273 		}
    274 
    275 		/* write to vcc */
    276 		rv = write_vcc(clientp, c);
    277 		if (rv == VNTSD_STATUS_INTR) {
    278 			rv = vntsd_cons_chk_intr(clientp);
    279 		}
    280 		if (rv != VNTSD_SUCCESS) {
    281 			return (rv);
    282 		}
    283 
    284 	}
    285 
    286 	/*NOTREACHED*/
    287 	return (NULL);
    288 }
    289