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