ia64/xen-unstable

view tools/console/daemon/io.c @ 8260:6b6e09240435

Fix printf of ssize_t that broke 64-bit build.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Wed Dec 07 10:54:57 2005 +0000 (2005-12-07)
parents 2b8efe11096b
children eae64901e428
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"
25 #include <xenctrl.h>
26 #include <xs.h>
27 #include <xen/linux/evtchn.h>
28 #include <xen/io/console.h>
30 #include <malloc.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/select.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <termios.h>
38 #include <stdarg.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
42 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
43 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
45 /* Each 10 bits takes ~ 3 digits, plus one, plus one for nul terminator. */
46 #define MAX_STRLEN(x) ((sizeof(x) * CHAR_BIT + CHAR_BIT-1) / 10 * 3 + 2)
48 struct buffer
49 {
50 char *data;
51 size_t consumed;
52 size_t size;
53 size_t capacity;
54 size_t max_capacity;
55 };
57 struct domain
58 {
59 int domid;
60 int tty_fd;
61 bool is_dead;
62 struct buffer buffer;
63 struct domain *next;
64 char *conspath;
65 int ring_ref;
66 evtchn_port_t local_port;
67 int evtchn_fd;
68 struct xencons_interface *interface;
69 };
71 static struct domain *dom_head;
73 static void evtchn_notify(struct domain *dom)
74 {
75 struct ioctl_evtchn_notify notify;
76 notify.port = dom->local_port;
77 (void)ioctl(dom->evtchn_fd, IOCTL_EVTCHN_NOTIFY, &notify);
78 }
80 static void buffer_append(struct domain *dom)
81 {
82 struct buffer *buffer = &dom->buffer;
83 XENCONS_RING_IDX cons, prod, size;
84 struct xencons_interface *intf = dom->interface;
86 cons = intf->out_cons;
87 prod = intf->out_prod;
88 mb();
90 size = prod - cons;
91 if ((size == 0) || (size > sizeof(intf->out)))
92 return;
94 if ((buffer->capacity - buffer->size) < size) {
95 buffer->capacity += (size + 1024);
96 buffer->data = realloc(buffer->data, buffer->capacity);
97 if (buffer->data == NULL) {
98 dolog(LOG_ERR, "Memory allocation failed");
99 exit(ENOMEM);
100 }
101 }
103 while (cons != prod)
104 buffer->data[buffer->size++] = intf->out[
105 MASK_XENCONS_IDX(cons++, intf->out)];
107 mb();
108 intf->out_cons = cons;
109 evtchn_notify(dom);
111 if (buffer->max_capacity &&
112 buffer->size > buffer->max_capacity) {
113 /* Discard the middle of the data. */
115 size_t over = buffer->size - buffer->max_capacity;
116 char *maxpos = buffer->data + buffer->max_capacity;
118 memmove(maxpos - over, maxpos, over);
119 buffer->data = realloc(buffer->data, buffer->max_capacity);
120 buffer->size = buffer->capacity = buffer->max_capacity;
122 if (buffer->consumed > buffer->max_capacity - over)
123 buffer->consumed = buffer->max_capacity - over;
124 }
125 }
127 static bool buffer_empty(struct buffer *buffer)
128 {
129 return buffer->size == 0;
130 }
132 static void buffer_advance(struct buffer *buffer, size_t len)
133 {
134 buffer->consumed += len;
135 if (buffer->consumed == buffer->size) {
136 buffer->consumed = 0;
137 buffer->size = 0;
138 }
139 }
141 static bool domain_is_valid(int domid)
142 {
143 bool ret;
144 xc_dominfo_t info;
146 ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
147 info.domid == domid);
149 return ret;
150 }
152 static int domain_create_tty(struct domain *dom)
153 {
154 char *path;
155 int master;
156 bool success;
158 if ((master = getpt()) == -1 ||
159 grantpt(master) == -1 || unlockpt(master) == -1) {
160 dolog(LOG_ERR, "Failed to create tty for domain-%d",
161 dom->domid);
162 master = -1;
163 } else {
164 const char *slave = ptsname(master);
165 struct termios term;
166 char *data;
167 unsigned int len;
169 if (tcgetattr(master, &term) != -1) {
170 cfmakeraw(&term);
171 tcsetattr(master, TCSAFLUSH, &term);
172 }
174 success = asprintf(&path, "%s/limit", dom->conspath) != -1;
175 if (!success)
176 goto out;
177 data = xs_read(xs, NULL, path, &len);
178 if (data) {
179 dom->buffer.max_capacity = strtoul(data, 0, 0);
180 free(data);
181 }
182 free(path);
184 success = asprintf(&path, "%s/tty", dom->conspath) != -1;
185 if (!success)
186 goto out;
187 success = xs_write(xs, NULL, path, slave, strlen(slave));
188 free(path);
189 if (!success)
190 goto out;
192 if (fcntl(master, F_SETFL, O_NONBLOCK) == -1)
193 goto out;
194 }
196 return master;
197 out:
198 close(master);
199 return -1;
200 }
202 /* Takes tuples of names, scanf-style args, and void **, NULL terminated. */
203 int xs_gather(struct xs_handle *xs, const char *dir, ...)
204 {
205 va_list ap;
206 const char *name;
207 char *path;
208 int ret = 0;
210 va_start(ap, dir);
211 while (ret == 0 && (name = va_arg(ap, char *)) != NULL) {
212 const char *fmt = va_arg(ap, char *);
213 void *result = va_arg(ap, void *);
214 char *p;
216 asprintf(&path, "%s/%s", dir, name);
217 p = xs_read(xs, NULL, path, NULL);
218 free(path);
219 if (p == NULL) {
220 ret = ENOENT;
221 break;
222 }
223 if (fmt) {
224 if (sscanf(p, fmt, result) == 0)
225 ret = EINVAL;
226 free(p);
227 } else
228 *(char **)result = p;
229 }
230 va_end(ap);
231 return ret;
232 }
234 static int domain_create_ring(struct domain *dom)
235 {
236 int err, remote_port, ring_ref, rc;
237 struct ioctl_evtchn_bind_interdomain bind;
239 err = xs_gather(xs, dom->conspath,
240 "ring-ref", "%u", &ring_ref,
241 "port", "%i", &remote_port,
242 NULL);
243 if (err)
244 goto out;
246 if (ring_ref != dom->ring_ref) {
247 if (dom->interface != NULL)
248 munmap(dom->interface, getpagesize());
249 dom->interface = xc_map_foreign_range(
250 xc, dom->domid, getpagesize(),
251 PROT_READ|PROT_WRITE,
252 (unsigned long)ring_ref);
253 if (dom->interface == NULL) {
254 err = EINVAL;
255 goto out;
256 }
257 dom->ring_ref = ring_ref;
258 }
260 dom->local_port = -1;
261 if (dom->evtchn_fd != -1)
262 close(dom->evtchn_fd);
264 /* Opening evtchn independently for each console is a bit
265 * wasteful, but that's how the code is structured... */
266 dom->evtchn_fd = open("/dev/xen/evtchn", O_RDWR);
267 if (dom->evtchn_fd == -1) {
268 err = errno;
269 goto out;
270 }
272 bind.remote_domain = dom->domid;
273 bind.remote_port = remote_port;
274 rc = ioctl(dom->evtchn_fd, IOCTL_EVTCHN_BIND_INTERDOMAIN, &bind);
275 if (rc == -1) {
276 err = errno;
277 close(dom->evtchn_fd);
278 dom->evtchn_fd = -1;
279 goto out;
280 }
281 dom->local_port = rc;
283 if (dom->tty_fd == -1) {
284 dom->tty_fd = domain_create_tty(dom);
286 if (dom->tty_fd == -1) {
287 err = errno;
288 close(dom->evtchn_fd);
289 dom->evtchn_fd = -1;
290 dom->local_port = -1;
291 goto out;
292 }
293 }
295 out:
296 return err;
297 }
299 static bool watch_domain(struct domain *dom, bool watch)
300 {
301 char domid_str[3 + MAX_STRLEN(dom->domid)];
302 bool success;
304 sprintf(domid_str, "dom%u", dom->domid);
305 if (watch)
306 success = xs_watch(xs, dom->conspath, domid_str);
307 else
308 success = xs_unwatch(xs, dom->conspath, domid_str);
309 if (success)
310 domain_create_ring(dom);
311 return success;
312 }
314 static struct domain *create_domain(int domid)
315 {
316 struct domain *dom;
317 char *s;
319 dom = (struct domain *)malloc(sizeof(struct domain));
320 if (dom == NULL) {
321 dolog(LOG_ERR, "Out of memory %s:%s():L%d",
322 __FILE__, __FUNCTION__, __LINE__);
323 exit(ENOMEM);
324 }
326 dom->domid = domid;
327 dom->conspath = xs_get_domain_path(xs, dom->domid);
328 s = realloc(dom->conspath, strlen(dom->conspath) +
329 strlen("/console") + 1);
330 if (s == NULL)
331 goto out;
332 dom->conspath = s;
333 strcat(dom->conspath, "/console");
335 dom->tty_fd = -1;
336 dom->is_dead = false;
337 dom->buffer.data = 0;
338 dom->buffer.consumed = 0;
339 dom->buffer.size = 0;
340 dom->buffer.capacity = 0;
341 dom->buffer.max_capacity = 0;
342 dom->next = NULL;
344 dom->ring_ref = -1;
345 dom->local_port = -1;
346 dom->interface = NULL;
347 dom->evtchn_fd = -1;
349 if (!watch_domain(dom, true))
350 goto out;
352 dom->next = dom_head;
353 dom_head = dom;
355 dolog(LOG_DEBUG, "New domain %d", domid);
357 return dom;
358 out:
359 free(dom->conspath);
360 free(dom);
361 return NULL;
362 }
364 static struct domain *lookup_domain(int domid)
365 {
366 struct domain *dom;
368 for (dom = dom_head; dom; dom = dom->next)
369 if (dom->domid == domid)
370 return dom;
371 return NULL;
372 }
374 static void remove_domain(struct domain *dom)
375 {
376 struct domain **pp;
378 dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
380 for (pp = &dom_head; *pp; pp = &(*pp)->next) {
381 if (dom == *pp) {
382 *pp = dom->next;
383 free(dom);
384 break;
385 }
386 }
387 }
389 static void cleanup_domain(struct domain *d)
390 {
391 if (d->tty_fd != -1) {
392 close(d->tty_fd);
393 d->tty_fd = -1;
394 }
396 free(d->buffer.data);
397 d->buffer.data = NULL;
399 free(d->conspath);
400 d->conspath = NULL;
402 remove_domain(d);
403 }
405 static void shutdown_domain(struct domain *d)
406 {
407 d->is_dead = true;
408 watch_domain(d, false);
409 if (d->interface != NULL)
410 munmap(d->interface, getpagesize());
411 d->interface = NULL;
412 if (d->evtchn_fd != -1)
413 close(d->evtchn_fd);
414 d->evtchn_fd = -1;
415 cleanup_domain(d);
416 }
418 void enum_domains(void)
419 {
420 int domid = 1;
421 xc_dominfo_t dominfo;
422 struct domain *dom;
424 while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
425 dom = lookup_domain(dominfo.domid);
426 if (dominfo.dying) {
427 if (dom)
428 shutdown_domain(dom);
429 } else {
430 if (dom == NULL)
431 create_domain(dominfo.domid);
432 }
433 domid = dominfo.domid + 1;
434 }
435 }
437 static void handle_tty_read(struct domain *dom)
438 {
439 ssize_t len = 0;
440 char msg[80];
441 int i;
442 struct xencons_interface *intf = dom->interface;
443 XENCONS_RING_IDX cons, prod;
445 cons = intf->in_cons;
446 prod = intf->in_prod;
447 mb();
449 if (sizeof(intf->in) > (prod - cons))
450 len = sizeof(intf->in) - (prod - cons);
451 if (len > sizeof(msg))
452 len = sizeof(msg);
454 if (len == 0)
455 return;
457 len = read(dom->tty_fd, msg, len);
458 if (len < 1) {
459 close(dom->tty_fd);
460 dom->tty_fd = -1;
462 if (domain_is_valid(dom->domid)) {
463 dom->tty_fd = domain_create_tty(dom);
464 } else {
465 shutdown_domain(dom);
466 }
467 } else if (domain_is_valid(dom->domid)) {
468 for (i = 0; i < len; i++) {
469 intf->in[MASK_XENCONS_IDX(prod++, intf->in)] =
470 msg[i];
471 }
472 wmb();
473 intf->in_prod = prod;
474 evtchn_notify(dom);
475 } else {
476 close(dom->tty_fd);
477 dom->tty_fd = -1;
478 shutdown_domain(dom);
479 }
480 }
482 static void handle_tty_write(struct domain *dom)
483 {
484 ssize_t len;
486 len = write(dom->tty_fd, dom->buffer.data + dom->buffer.consumed,
487 dom->buffer.size - dom->buffer.consumed);
488 if (len < 1) {
489 dolog(LOG_DEBUG, "Write failed on domain %d: %zd, %d\n",
490 dom->domid, len, errno);
492 close(dom->tty_fd);
493 dom->tty_fd = -1;
495 if (domain_is_valid(dom->domid)) {
496 dom->tty_fd = domain_create_tty(dom);
497 } else {
498 shutdown_domain(dom);
499 }
500 } else {
501 buffer_advance(&dom->buffer, len);
502 }
503 }
505 static void handle_ring_read(struct domain *dom)
506 {
507 evtchn_port_t v;
509 if (!read_sync(dom->evtchn_fd, &v, sizeof(v)))
510 return;
512 buffer_append(dom);
514 (void)write_sync(dom->evtchn_fd, &v, sizeof(v));
515 }
517 static void handle_xs(int fd)
518 {
519 char **vec;
520 int domid;
521 struct domain *dom;
522 unsigned int num;
524 vec = xs_read_watch(xs, &num);
525 if (!vec)
526 return;
528 if (!strcmp(vec[XS_WATCH_TOKEN], "domlist"))
529 enum_domains();
530 else if (sscanf(vec[XS_WATCH_TOKEN], "dom%u", &domid) == 1) {
531 dom = lookup_domain(domid);
532 /* We may get watches firing for domains that have recently
533 been removed, so dom may be NULL here. */
534 if (dom && dom->is_dead == false)
535 domain_create_ring(dom);
536 }
538 free(vec);
539 }
541 void handle_io(void)
542 {
543 fd_set readfds, writefds;
544 int ret;
546 do {
547 struct domain *d, *n;
548 struct timeval tv = { 100, 0 };
549 int max_fd = -1;
551 FD_ZERO(&readfds);
552 FD_ZERO(&writefds);
554 FD_SET(xs_fileno(xs), &readfds);
555 max_fd = MAX(xs_fileno(xs), max_fd);
557 for (d = dom_head; d; d = d->next) {
558 if (d->evtchn_fd != -1) {
559 FD_SET(d->evtchn_fd, &readfds);
560 max_fd = MAX(d->evtchn_fd, max_fd);
561 }
563 if (d->tty_fd != -1) {
564 if (!d->is_dead)
565 FD_SET(d->tty_fd, &readfds);
567 if (!buffer_empty(&d->buffer))
568 FD_SET(d->tty_fd, &writefds);
569 max_fd = MAX(d->tty_fd, max_fd);
570 }
571 }
573 ret = select(max_fd + 1, &readfds, &writefds, 0, &tv);
575 if (FD_ISSET(xs_fileno(xs), &readfds))
576 handle_xs(xs_fileno(xs));
578 for (d = dom_head; d; d = n) {
579 n = d->next;
580 if (d->evtchn_fd != -1 &&
581 FD_ISSET(d->evtchn_fd, &readfds))
582 handle_ring_read(d);
584 if (d->tty_fd != -1) {
585 if (FD_ISSET(d->tty_fd, &readfds))
586 handle_tty_read(d);
588 if (FD_ISSET(d->tty_fd, &writefds))
589 handle_tty_write(d);
591 if (d->is_dead)
592 cleanup_domain(d);
593 }
594 }
595 } while (ret > -1);
596 }
598 /*
599 * Local variables:
600 * c-file-style: "linux"
601 * indent-tabs-mode: t
602 * c-indent-level: 8
603 * c-basic-offset: 8
604 * tab-width: 8
605 * End:
606 */