ia64/xen-unstable

view tools/console/daemon/io.c @ 6204:2c2015c11b49

Under the right circumstances, xenconsoled will corrupt its internal
list of domains causing a SEGV. This is usually characterized by a
rapid number of creations/destructions. The attached patch fixes this.

1) Fix uninitialized next pointer. This could sometimes cause xenconsoled to
SEGV on an invalid domain pointer
2) Fix race condition in iterating domain list where removing a domain in a
callback could lead to the iterators becoming invalid.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Aug 16 07:06:10 2005 +0000 (2005-08-16)
parents d74e320900fd
children 0237746ecf92
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 /* FIXME */
333 /* This is a nasty hack. xcs does not handle the
334 control channels filling up well at all. We'll
335 throttle ourselves here since we do proper
336 queueing to give the domains a shot at pulling out
337 the data. Fixing xcs is not worth it as it's
338 going away */
339 tv.tv_usec = 1000;
340 select(0, 0, 0, 0, &tv);
341 }
342 enum_domains();
344 if (FD_ISSET(xcs_data_fd, &readfds)) {
345 handle_xcs_msg(xcs_data_fd);
346 }
348 for (d = dom_head; d; d = d->next) {
349 if (!d->is_dead && FD_ISSET(d->tty_fd, &readfds)) {
350 handle_tty_read(d);
351 }
353 if (!d->is_dead && FD_ISSET(d->tty_fd, &writefds)) {
354 handle_tty_write(d);
355 }
356 }
358 remove_dead_domains(dom_head);
359 } while (ret > -1);
360 }