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
write_vcc(vntsd_client_t * clientp,char c)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
acquire_writer(vntsd_client_t * clientp)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
vntsd_cons_chk_intr(vntsd_client_t * clientp)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
read_char(vntsd_client_t * clientp,char * c)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
vntsd_read(vntsd_client_t * clientp)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