simple console support through pts devices
This commit is contained in:
+42
-10
@@ -24,6 +24,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <utils/linked_list.h>
|
#include <utils/linked_list.h>
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
#define KERNEL_FILE "linux"
|
#define KERNEL_FILE "linux"
|
||||||
#define LOG_FILE "boot.log"
|
#define LOG_FILE "boot.log"
|
||||||
#define NOTIFY_FILE "notify"
|
#define NOTIFY_FILE "notify"
|
||||||
|
#define PTYS 0
|
||||||
|
|
||||||
typedef struct private_guest_t private_guest_t;
|
typedef struct private_guest_t private_guest_t;
|
||||||
|
|
||||||
@@ -67,6 +69,19 @@ struct private_guest_t {
|
|||||||
cowfs_t *cowfs;
|
cowfs_t *cowfs;
|
||||||
/** mconsole to control running UML */
|
/** mconsole to control running UML */
|
||||||
mconsole_t *mconsole;
|
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 */
|
/** list of interfaces attached to the guest */
|
||||||
linked_list_t *ifaces;
|
linked_list_t *ifaces;
|
||||||
};
|
};
|
||||||
@@ -167,6 +182,18 @@ static char* write_arg(char **pos, size_t *left, char *format, ...)
|
|||||||
return res;
|
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.
|
* 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, "umid=%s", this->name);
|
||||||
args[i++] = write_arg(&pos, &left, "mem=%dM", this->mem);
|
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, "mconsole=notify:%s", notify);
|
||||||
/*args[i++] = write_arg(&pos, &left, "con=pts");*/
|
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=none,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++] = NULL;
|
args[i++] = NULL;
|
||||||
|
|
||||||
this->pid = fork();
|
this->pid = fork();
|
||||||
@@ -247,6 +267,7 @@ static bool start(private_guest_t *this)
|
|||||||
stop(this);
|
stop(this);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->state = GUEST_RUNNING;
|
this->state = GUEST_RUNNING;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@@ -285,12 +306,22 @@ static bool set_scenario(private_guest_t *this, char *path)
|
|||||||
*/
|
*/
|
||||||
static void sigchild(private_guest_t *this)
|
static void sigchild(private_guest_t *this)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (this->state != GUEST_STOPPING)
|
if (this->state != GUEST_STOPPING)
|
||||||
{ /* collect zombie if uml crashed */
|
{ /* collect zombie if uml crashed */
|
||||||
waitpid(this->pid, NULL, WNOHANG);
|
waitpid(this->pid, NULL, WNOHANG);
|
||||||
}
|
}
|
||||||
DESTROY_IF(this->mconsole);
|
DESTROY_IF(this->mconsole);
|
||||||
this->mconsole = NULL;
|
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;
|
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.create_iface_iterator = (iterator_t*(*)(guest_t*))create_iface_iterator;
|
||||||
this->public.start = (void*)start;
|
this->public.start = (void*)start;
|
||||||
this->public.stop = (void*)stop;
|
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.set_scenario = (bool(*)(guest_t*, char *path))set_scenario;
|
||||||
this->public.sigchild = (void(*)(guest_t*))sigchild;
|
this->public.sigchild = (void(*)(guest_t*))sigchild;
|
||||||
this->public.destroy = (void*)destroy;
|
this->public.destroy = (void*)destroy;
|
||||||
|
|
||||||
if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
|
if (*parent == '/' || getcwd(cwd, sizeof(cwd)) == NULL)
|
||||||
{
|
{
|
||||||
asprintf(&this->dirname, "%s/%s", parent, name);
|
asprintf(&this->dirname, "%s/%s", parent, name);
|
||||||
|
|||||||
@@ -86,6 +86,17 @@ struct guest_t {
|
|||||||
*/
|
*/
|
||||||
bool (*stop) (guest_t *this);
|
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.
|
* @brief Create a new interface in the current scenario.
|
||||||
*
|
*
|
||||||
|
|||||||
+25
-3
@@ -57,7 +57,10 @@ static char* get_line(char *format, ...)
|
|||||||
line = readline(prompt);
|
line = readline(prompt);
|
||||||
if (line == NULL)
|
if (line == NULL)
|
||||||
{
|
{
|
||||||
continue;
|
printf("quit\n");
|
||||||
|
dumm->destroy(dumm);
|
||||||
|
clear_history();
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
if (*line == '\0')
|
if (*line == '\0')
|
||||||
{
|
{
|
||||||
@@ -190,6 +193,21 @@ static void guest_delif_menu(guest_t *guest)
|
|||||||
free(name);
|
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)
|
static void guest_menu(guest_t *guest)
|
||||||
{
|
{
|
||||||
while (TRUE)
|
while (TRUE)
|
||||||
@@ -205,7 +223,7 @@ static void guest_menu(guest_t *guest)
|
|||||||
{
|
{
|
||||||
if (guest->start(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
|
else
|
||||||
{
|
{
|
||||||
@@ -226,9 +244,13 @@ static void guest_menu(guest_t *guest)
|
|||||||
{
|
{
|
||||||
guest_delif_menu(guest);
|
guest_delif_menu(guest);
|
||||||
}
|
}
|
||||||
|
else if (streq(line, "console"))
|
||||||
|
{
|
||||||
|
guest_console(guest);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("back|start|stop|addif|delif\n");
|
printf("back|start|stop|addif|delif|console\n");
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-15
@@ -16,6 +16,8 @@
|
|||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -84,42 +86,45 @@ struct mconsole_notify {
|
|||||||
/**
|
/**
|
||||||
* send a request to UML using mconsole
|
* 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_request request;
|
||||||
mconsole_reply reply;
|
mconsole_reply reply;
|
||||||
bool first = TRUE, good = TRUE;
|
int len, total = 0;
|
||||||
int len;
|
|
||||||
|
|
||||||
memset(&request, 0, sizeof(request));
|
memset(&request, 0, sizeof(request));
|
||||||
request.magic = MCONSOLE_MAGIC;
|
request.magic = MCONSOLE_MAGIC;
|
||||||
request.version = MCONSOLE_VERSION;
|
request.version = MCONSOLE_VERSION;
|
||||||
request.len = min(strlen(command), sizeof(reply.data) - 1);
|
request.len = min(strlen(command), sizeof(reply.data) - 1);
|
||||||
strncpy(request.data, command, request.len);
|
strncpy(request.data, command, request.len);
|
||||||
|
*buf = '\0';
|
||||||
|
(*size)--;
|
||||||
|
|
||||||
if (sendto(this->console, &request, sizeof(request), 0,
|
if (sendto(this->console, &request, sizeof(request), 0,
|
||||||
(struct sockaddr*)&this->uml, sizeof(this->uml)) < 0)
|
(struct sockaddr*)&this->uml, sizeof(this->uml)) < 0)
|
||||||
{
|
{
|
||||||
DBG1("sending mconsole command to UML failed: %m");
|
snprintf(buf, *size, "sending mconsole command to UML failed: %m");
|
||||||
return FALSE;
|
return -1;
|
||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
len = recvfrom(this->console, &reply, sizeof(reply), 0, NULL, 0);
|
len = recv(this->console, &reply, sizeof(reply), 0);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
{
|
{
|
||||||
DBG1("receiving from mconsole failed: %m");
|
snprintf(buf, *size, "receiving from mconsole failed: %m");
|
||||||
return FALSE;
|
return -1;
|
||||||
}
|
}
|
||||||
if (first && reply.err)
|
if (len > 0)
|
||||||
{
|
{
|
||||||
good = FALSE;
|
strncat(buf, reply.data, min(reply.len, *size - total));
|
||||||
DBG1("received error from UML mconsole: %s", reply.data);
|
total += reply.len;
|
||||||
}
|
}
|
||||||
first = FALSE;
|
|
||||||
}
|
}
|
||||||
while (reply.more);
|
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 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 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.add_iface = (bool(*)(mconsole_t*, char *guest, char *host))add_iface;
|
||||||
this->public.del_iface = (bool(*)(mconsole_t*, char *guest))del_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;
|
this->public.destroy = (void*)destroy;
|
||||||
|
|
||||||
if (!wait_for_notify(this, notify))
|
if (!wait_for_notify(this, notify))
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ struct mconsole_t {
|
|||||||
*/
|
*/
|
||||||
bool (*del_iface)(mconsole_t *this, char *guest);
|
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
|
* @brief Destroy the mconsole instance
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user