ia64/xen-unstable

view tools/console/daemon/io.c @ 6538:84ee014ebd41

Merge xen-vtx-unstable.hg
author adsharma@los-vmm.sc.intel.com
date Wed Aug 17 12:34:38 2005 -0800 (2005-08-17)
parents 23979fb12c49 0237746ecf92
children 99914b54f7bf
line source
1 /*\
2 * Copyright (C) International Business Machines Corp., 2005
3 * Author(s): Anthony Liguori <aliguori@us.ibm.com>
4 *
5 * Xen Console Daemon
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; under version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 \*/
21 #define _GNU_SOURCE
23 #include "utils.h"
24 #include "io.h"
26 #include "xc.h"
27 #include "xs.h"
28 #include "xen/io/domain_controller.h"
29 #include "xcs_proto.h"
31 #include <malloc.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <sys/select.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <termios.h>
40 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
41 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
43 struct buffer
44 {
45 char *data;
46 size_t size;
47 size_t capacity;
48 size_t max_capacity;
49 };
51 static void buffer_append(struct buffer *buffer, const void *data, size_t size)
52 {
53 if ((buffer->capacity - buffer->size) < size) {
54 buffer->capacity += (size + 1024);
55 buffer->data = realloc(buffer->data, buffer->capacity);
56 if (buffer->data == NULL) {
57 dolog(LOG_ERR, "Memory allocation failed");
58 exit(ENOMEM);
59 }
60 }
62 memcpy(buffer->data + buffer->size, data, size);
63 buffer->size += size;
65 if (buffer->max_capacity &&
66 buffer->size > buffer->max_capacity) {
67 memmove(buffer->data + (buffer->size - buffer->max_capacity),
68 buffer->data, buffer->max_capacity);
69 buffer->data = realloc(buffer->data, buffer->max_capacity);
70 buffer->capacity = buffer->max_capacity;
71 }
72 }
74 static bool buffer_empty(struct buffer *buffer)
75 {
76 return buffer->size == 0;
77 }
79 static void buffer_advance(struct buffer *buffer, size_t size)
80 {
81 size = MIN(size, buffer->size);
82 memmove(buffer->data, buffer + size, buffer->size - size);
83 buffer->size -= size;
84 }
86 struct domain
87 {
88 int domid;
89 int tty_fd;
90 bool is_dead;
91 struct buffer buffer;
92 struct domain *next;
93 };
95 static struct domain *dom_head;
97 static bool domain_is_valid(int domid)
98 {
99 bool ret;
100 xc_dominfo_t info;
102 ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
103 info.domid == domid);
105 return ret;
106 }
108 static int domain_create_tty(struct domain *dom)
109 {
110 char path[1024];
111 int master;
113 if ((master = getpt()) == -1 ||
114 grantpt(master) == -1 || unlockpt(master) == -1) {
115 dolog(LOG_ERR, "Failed to create tty for domain-%d",
116 dom->domid);
117 master = -1;
118 } else {
119 const char *slave = ptsname(master);
120 struct termios term;
121 char *data;
122 unsigned int len;
124 if (tcgetattr(master, &term) != -1) {
125 cfmakeraw(&term);
126 tcsetattr(master, TCSAFLUSH, &term);
127 }
129 xs_mkdir(xs, "/console");
130 snprintf(path, sizeof(path), "/console/%d", dom->domid);
131 xs_mkdir(xs, path);
132 strcat(path, "/tty");
134 xs_write(xs, path, slave, strlen(slave), O_CREAT);
136 snprintf(path, sizeof(path), "/console/%d/limit", dom->domid);
137 data = xs_read(xs, path, &len);
138 if (data) {
139 dom->buffer.max_capacity = strtoul(data, 0, 0);
140 free(data);
141 }
142 }
144 return master;
145 }
147 static struct domain *create_domain(int domid)
148 {
149 struct domain *dom;
151 dom = (struct domain *)malloc(sizeof(struct domain));
152 if (dom == NULL) {
153 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
154 __FILE__, __FUNCTION__, __LINE__);
155 exit(ENOMEM);
156 }
158 dom->domid = domid;
159 dom->tty_fd = domain_create_tty(dom);
160 dom->is_dead = false;
161 dom->buffer.data = 0;
162 dom->buffer.size = 0;
163 dom->buffer.capacity = 0;
164 dom->buffer.max_capacity = 0;
165 dom->next = 0;
167 dolog(LOG_DEBUG, "New domain %d", domid);
169 return dom;
170 }
172 static struct domain *lookup_domain(int domid)
173 {
174 struct domain **pp;
176 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
177 struct domain *dom = *pp;
179 if (dom->domid == domid) {
180 return dom;
181 } else if (dom->domid > domid) {
182 *pp = create_domain(domid);
183 (*pp)->next = dom;
184 return *pp;
185 }
186 }
188 *pp = create_domain(domid);
189 return *pp;
190 }
192 static void remove_domain(struct domain *dom)
193 {
194 struct domain **pp;
196 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
198 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
199 struct domain *d = *pp;
201 if (dom->domid == d->domid) {
202 *pp = d->next;
203 if (d->buffer.data) {
204 free(d->buffer.data);
205 }
206 free(d);
207 break;
208 }
209 }
210 }
212 static void remove_dead_domains(struct domain *dom)
213 {
214 if (dom == NULL) return;
215 remove_dead_domains(dom->next);
217 if (dom->is_dead) {
218 remove_domain(dom);
219 }
220 }
222 static void handle_tty_read(struct domain *dom)
223 {
224 ssize_t len;
225 xcs_msg_t msg;
227 msg.type = XCS_REQUEST;
228 msg.u.control.remote_dom = dom->domid;
229 msg.u.control.msg.type = CMSG_CONSOLE;
230 msg.u.control.msg.subtype = CMSG_CONSOLE_DATA;
231 msg.u.control.msg.id = 1;
233 len = read(dom->tty_fd, msg.u.control.msg.msg, 60);
234 if (len < 1) {
235 close(dom->tty_fd);
237 if (domain_is_valid(dom->domid)) {
238 dom->tty_fd = domain_create_tty(dom);
239 } else {
240 dom->is_dead = true;
241 }
242 } else if (domain_is_valid(dom->domid)) {
243 msg.u.control.msg.length = len;
245 if (!write_sync(xcs_data_fd, &msg, sizeof(msg))) {
246 dolog(LOG_ERR, "Write to xcs failed: %m");
247 exit(1);
248 }
249 } else {
250 close(dom->tty_fd);
251 dom->is_dead = true;
252 }
253 }
255 static void handle_tty_write(struct domain *dom)
256 {
257 ssize_t len;
259 len = write(dom->tty_fd, dom->buffer.data, dom->buffer.size);
260 if (len < 1) {
261 close(dom->tty_fd);
263 if (domain_is_valid(dom->domid)) {
264 dom->tty_fd = domain_create_tty(dom);
265 } else {
266 dom->is_dead = true;
267 }
268 } else {
269 buffer_advance(&dom->buffer, len);
270 }
271 }
273 static void handle_xcs_msg(int fd)
274 {
275 xcs_msg_t msg;
277 if (!read_sync(fd, &msg, sizeof(msg))) {
278 dolog(LOG_ERR, "read from xcs failed! %m");
279 exit(1);
280 } else if (msg.type == XCS_REQUEST) {
281 struct domain *dom;
283 dom = lookup_domain(msg.u.control.remote_dom);
284 buffer_append(&dom->buffer,
285 msg.u.control.msg.msg,
286 msg.u.control.msg.length);
287 }
288 }
290 static void enum_domains(void)
291 {
292 int domid = 0;
293 xc_dominfo_t dominfo;
295 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
296 lookup_domain(dominfo.domid);
297 domid = dominfo.domid + 1;
298 }
299 }
301 void handle_io(void)
302 {
303 fd_set readfds, writefds;
304 int ret;
305 int max_fd = -1;
306 int num_of_writes = 0;
308 do {
309 struct domain *d;
310 struct timeval tv = { 1, 0 };
312 FD_ZERO(&readfds);
313 FD_ZERO(&writefds);
315 FD_SET(xcs_data_fd, &readfds);
316 max_fd = MAX(xcs_data_fd, max_fd);
318 for (d = dom_head; d; d = d->next) {
319 if (d->tty_fd != -1) {
320 FD_SET(d->tty_fd, &readfds);
321 }
323 if (d->tty_fd != -1 && !buffer_empty(&d->buffer)) {
324 FD_SET(d->tty_fd, &writefds);
325 }
327 max_fd = MAX(d->tty_fd, max_fd);
328 }
330 ret = select(max_fd + 1, &readfds, &writefds, 0, &tv);
331 if (tv.tv_sec == 1 && (++num_of_writes % 100) == 0) {
332 #if 0
333 /* FIXME */
334 /* This is a nasty hack. xcs does not handle the
335 control channels filling up well at all. We'll
336 throttle ourselves here since we do proper
337 queueing to give the domains a shot at pulling out
338 the data. Fixing xcs is not worth it as it's
339 going away */
340 tv.tv_usec = 1000;
341 select(0, 0, 0, 0, &tv);
342 #endif
343 }
344 enum_domains();
346 if (FD_ISSET(xcs_data_fd, &readfds)) {
347 handle_xcs_msg(xcs_data_fd);
348 }
350 for (d = dom_head; d; d = d->next) {
351 if (!d->is_dead && FD_ISSET(d->tty_fd, &readfds)) {
352 handle_tty_read(d);
353 }
355 if (!d->is_dead && FD_ISSET(d->tty_fd, &writefds)) {
356 handle_tty_write(d);
357 }
358 }
360 remove_dead_domains(dom_head);
361 } while (ret > -1);
362 }