From 69e56d9decf3ea9f85668d44934d85b1a39e70a4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 28 Aug 2007 07:53:46 +0000 Subject: [PATCH] simple console support through pts devices --- src/dumm/guest.c | 52 +++++++++++++++++++++++++------ src/dumm/guest.h | 11 +++++++ src/dumm/main.c | 28 +++++++++++++++-- src/dumm/mconsole.c | 76 ++++++++++++++++++++++++++++++++++++--------- src/dumm/mconsole.h | 8 +++++ 5 files changed, 147 insertions(+), 28 deletions(-) diff --git a/src/dumm/guest.c b/src/dumm/guest.c index abed80695..b74449cb3 100644 --- a/src/dumm/guest.c +++ b/src/dumm/guest.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #define KERNEL_FILE "linux" #define LOG_FILE "boot.log" #define NOTIFY_FILE "notify" +#define PTYS 0 typedef struct private_guest_t private_guest_t; @@ -67,6 +69,19 @@ struct private_guest_t { cowfs_t *cowfs; /** mconsole to control running UML */ mconsole_t *mconsole; + /** pty consoles */ + struct { + /** pty master fd */ + int master; + /** pty slave fd */ + int slave; + /** name of the pty */ + char name[16]; + /** currently in use */ + bool occupied; + /** is valid */ + bool valid; + } pty[PTYS]; /** list of interfaces attached to the guest */ linked_list_t *ifaces; }; @@ -167,6 +182,18 @@ static char* write_arg(char **pos, size_t *left, char *format, ...) return res; } +/** + * Implementation of get_t.close_console. + */ +static char* get_console(private_guest_t *this, int console) +{ + if (this->state == GUEST_RUNNING) + { + return this->mconsole->get_console_pts(this->mconsole, console); + } + return NULL; +} + /** * Implementation of guest_t.stop. */ @@ -212,15 +239,8 @@ static bool start(private_guest_t *this) args[i++] = write_arg(&pos, &left, "umid=%s", this->name); args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem); args[i++] = write_arg(&pos, &left, "mconsole=notify:%s", notify); - /*args[i++] = write_arg(&pos, &left, "con=pts");*/ - args[i++] = write_arg(&pos, &left, "con0=null,fd:%d", this->bootlog); - //args[i++] = write_arg(&pos, &left, "con0=fd:0,fd:1"); - //args[i++] = write_arg(&pos, &left, "con1=null,null"); - args[i++] = write_arg(&pos, &left, "con2=null,null"); - args[i++] = write_arg(&pos, &left, "con3=null,null"); - args[i++] = write_arg(&pos, &left, "con4=null,null"); - args[i++] = write_arg(&pos, &left, "con5=null,null"); - args[i++] = write_arg(&pos, &left, "con6=null,null"); + args[i++] = write_arg(&pos, &left, "con=pts"); + args[i++] = write_arg(&pos, &left, "con0=none,fd:%d", this->bootlog); args[i++] = NULL; this->pid = fork(); @@ -247,6 +267,7 @@ static bool start(private_guest_t *this) stop(this); return FALSE; } + this->state = GUEST_RUNNING; return TRUE; } @@ -285,12 +306,22 @@ static bool set_scenario(private_guest_t *this, char *path) */ static void sigchild(private_guest_t *this) { + int i; + if (this->state != GUEST_STOPPING) { /* collect zombie if uml crashed */ waitpid(this->pid, NULL, WNOHANG); } DESTROY_IF(this->mconsole); this->mconsole = NULL; + for (i = 0; i < PTYS; i++) + { + if (this->pty[i].valid) + { + close(this->pty[i].master); + close(this->pty[i].slave); + } + } this->state = GUEST_STOPPED; } @@ -425,10 +456,11 @@ static private_guest_t *guest_create_generic(char *parent, char *name, this->public.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator; this->public.start = (void*)start; this->public.stop = (void*)stop; + this->public.get_console = (char*(*)(guest_t*,int))get_console; this->public.set_scenario = (bool(*)(guest_t*, char *path))set_scenario; this->public.sigchild = (void(*)(guest_t*))sigchild; this->public.destroy = (void*)destroy; - + if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL) { asprintf(&this->dirname, "%s/%s", parent, name); diff --git a/src/dumm/guest.h b/src/dumm/guest.h index eb85ccb3c..10d42cb92 100644 --- a/src/dumm/guest.h +++ b/src/dumm/guest.h @@ -86,6 +86,17 @@ struct guest_t { */ bool (*stop) (guest_t *this); + /** + * @brief Get a console pts device. + * + * Every guest has 6 consoles, numbered from 1 to 6. These are associated + * to a unique pts device on the host. + * + * @param console console number to get (1-6) + * @return pts device file name, NULL if failed + */ + char* (*get_console) (guest_t *this, int console); + /** * @brief Create a new interface in the current scenario. * diff --git a/src/dumm/main.c b/src/dumm/main.c index a4389bc0d..b4c435256 100644 --- a/src/dumm/main.c +++ b/src/dumm/main.c @@ -57,7 +57,10 @@ static char* get_line(char *format, ...) line = readline(prompt); if (line == NULL) { - continue; + printf("quit\n"); + dumm->destroy(dumm); + clear_history(); + exit(0); } if (*line == '\0') { @@ -190,6 +193,21 @@ static void guest_delif_menu(guest_t *guest) free(name); } +static void guest_console(guest_t *guest) +{ + int con; + + for (con = 1; con <= 6; con++) + { + char *pts = guest->get_console(guest, con); + if (pts) + { + printf("%d: %s\n", con, pts); + free(pts); + } + } +} + static void guest_menu(guest_t *guest) { while (TRUE) @@ -205,7 +223,7 @@ static void guest_menu(guest_t *guest) { if (guest->start(guest)) { - printf("guest '%s' is booting\n", guest->get_name(guest)); + printf("guest '%s' is running\n", guest->get_name(guest)); } else { @@ -226,9 +244,13 @@ static void guest_menu(guest_t *guest) { guest_delif_menu(guest); } + else if (streq(line, "console")) + { + guest_console(guest); + } else { - printf("back|start|stop|addif|delif\n"); + printf("back|start|stop|addif|delif|console\n"); } free(line); } diff --git a/src/dumm/mconsole.c b/src/dumm/mconsole.c index 01547e1f1..77578238b 100644 --- a/src/dumm/mconsole.c +++ b/src/dumm/mconsole.c @@ -16,6 +16,8 @@ * for more details. */ +#define _GNU_SOURCE + #include #include #include @@ -84,42 +86,45 @@ struct mconsole_notify { /** * send a request to UML using mconsole */ -static bool request(private_mconsole_t *this, char *command) +static int request(private_mconsole_t *this, char *command, + char buf[], size_t *size) { mconsole_request request; mconsole_reply reply; - bool first = TRUE, good = TRUE; - int len; + int len, total = 0; memset(&request, 0, sizeof(request)); request.magic = MCONSOLE_MAGIC; request.version = MCONSOLE_VERSION; request.len = min(strlen(command), sizeof(reply.data) - 1); strncpy(request.data, command, request.len); + *buf = '\0'; + (*size)--; if (sendto(this->console, &request, sizeof(request), 0, (struct sockaddr*)&this->uml, sizeof(this->uml)) < 0) { - DBG1("sending mconsole command to UML failed: %m"); - return FALSE; + snprintf(buf, *size, "sending mconsole command to UML failed: %m"); + return -1; } do { - len = recvfrom(this->console, &reply, sizeof(reply), 0, NULL, 0); + len = recv(this->console, &reply, sizeof(reply), 0); if (len < 0) { - DBG1("receiving from mconsole failed: %m"); - return FALSE; + snprintf(buf, *size, "receiving from mconsole failed: %m"); + return -1; } - if (first && reply.err) + if (len > 0) { - good = FALSE; - DBG1("received error from UML mconsole: %s", reply.data); + strncat(buf, reply.data, min(reply.len, *size - total)); + total += reply.len; } - first = FALSE; } while (reply.more); - return good; + + *size = total; + return reply.err; } /** @@ -135,7 +140,13 @@ static bool add_iface(private_mconsole_t *this, char *guest, char *host) { return FALSE; } - return request(this, buf); + len = sizeof(buf); + if (request(this, buf, buf, &len) != 0) + { + DBG1("adding interface failed: %.*s", len, buf); + return FALSE; + } + return TRUE; } /** @@ -151,7 +162,41 @@ static bool del_iface(private_mconsole_t *this, char *guest) { return FALSE; } - return request(this, buf); + if (request(this, buf, buf, &len) != 0) + { + DBG1("removing interface failed: %.*s", len, buf); + return FALSE; + } + return TRUE; +} + +/** + * Implementation of mconsole_t.get_console_pts. + */ +static char* get_console_pts(private_mconsole_t *this, int con) +{ + char buf[128]; + char *pos; + int len; + + len = snprintf(buf, sizeof(buf), "config con%d", con); + if (len < 0 || len >= sizeof(buf)) + { + return NULL; + } + len = sizeof(buf); + if (request(this, buf, buf, &len) != 0) + { + DBG1("getting console pts failed: %.*s", len, buf); + return NULL; + } + pos = memchr(buf, ':', len); + if (pos == NULL) + { + return NULL; + } + pos++; + return strndup(pos, len - (pos - buf)); } /** @@ -250,6 +295,7 @@ mconsole_t *mconsole_create(char *notify) this->public.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface; this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_iface; + this->public.get_console_pts = (char*(*)(mconsole_t*, int con))get_console_pts; this->public.destroy = (void*)destroy; if (!wait_for_notify(this, notify)) diff --git a/src/dumm/mconsole.h b/src/dumm/mconsole.h index cf4f69957..53aaa1b8b 100644 --- a/src/dumm/mconsole.h +++ b/src/dumm/mconsole.h @@ -42,6 +42,14 @@ struct mconsole_t { */ bool (*del_iface)(mconsole_t *this, char *guest); + /** + * @brief Get the pts device file assigned to a console. + * + * @param con console number in guest + * @return allocated device string + */ + char* (*get_console_pts)(mconsole_t *this, int con); + /** * @brief Destroy the mconsole instance */