mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
At the moment, do_control_lu() will set errno to 0 before calling lu_arch() and then check errno. The expectation is nothing in lu_arch() will change the value unless there is an error. However, per errno(3), a function that succeeds is allowed to change errno. In fact, syslog() will overwrite errno if the logs are rotated at the time it is called. To prevent any further issue, errno is now always set before returning NULL. Additionally, errno is only checked when returning NULL so the client can see the error message if there is any. Reported-by: Michael Kurth <mku@amazon.com> Signed-off-by: Julien Grall <jgrall@amazon.com> Reviewed-by: Juergen Gross <jgross@suse.com>
917 lines
19 KiB
C
917 lines
19 KiB
C
/*
|
|
Interactive commands for Xen Store Daemon.
|
|
Copyright (C) 2017 Juergen Gross, SUSE Linux GmbH
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <xenctrl.h>
|
|
|
|
#include "utils.h"
|
|
#include "talloc.h"
|
|
#include "xs_lib.h"
|
|
#include "xenstored_core.h"
|
|
#include "xenstored_control.h"
|
|
#include "xenstored_domain.h"
|
|
#include "xenstored_watch.h"
|
|
|
|
/* Mini-OS only knows about MAP_ANON. */
|
|
#ifndef MAP_ANONYMOUS
|
|
#define MAP_ANONYMOUS MAP_ANON
|
|
#endif
|
|
|
|
#ifndef NO_LIVE_UPDATE
|
|
struct live_update {
|
|
/* For verification the correct connection is acting. */
|
|
struct connection *conn;
|
|
|
|
/* Pointer to the command used to request LU */
|
|
struct buffered_data *in;
|
|
|
|
#ifdef __MINIOS__
|
|
void *kernel;
|
|
unsigned int kernel_size;
|
|
unsigned int kernel_off;
|
|
|
|
void *dump_state;
|
|
unsigned long dump_size;
|
|
#else
|
|
char *filename;
|
|
#endif
|
|
|
|
char *cmdline;
|
|
|
|
/* Start parameters. */
|
|
bool force;
|
|
unsigned int timeout;
|
|
time_t started_at;
|
|
};
|
|
|
|
static struct live_update *lu_status;
|
|
|
|
struct lu_dump_state {
|
|
void *buf;
|
|
unsigned int size;
|
|
#ifndef __MINIOS__
|
|
int fd;
|
|
char *filename;
|
|
#endif
|
|
};
|
|
|
|
static int lu_destroy(void *data)
|
|
{
|
|
#ifdef __MINIOS__
|
|
if (lu_status->dump_state)
|
|
munmap(lu_status->dump_state, lu_status->dump_size);
|
|
#endif
|
|
lu_status = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const char *lu_begin(struct connection *conn)
|
|
{
|
|
if (lu_status)
|
|
return "live-update session already active.";
|
|
|
|
lu_status = talloc_zero(conn, struct live_update);
|
|
if (!lu_status)
|
|
return "Allocation failure.";
|
|
lu_status->conn = conn;
|
|
talloc_set_destructor(lu_status, lu_destroy);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct connection *lu_get_connection(void)
|
|
{
|
|
return lu_status ? lu_status->conn : NULL;
|
|
}
|
|
|
|
unsigned int lu_write_response(FILE *fp)
|
|
{
|
|
struct xsd_sockmsg msg;
|
|
|
|
assert(lu_status);
|
|
|
|
msg = lu_status->in->hdr.msg;
|
|
|
|
msg.len = sizeof("OK");
|
|
if (fp && fwrite(&msg, sizeof(msg), 1, fp) != 1)
|
|
return 0;
|
|
if (fp && fwrite("OK", msg.len, 1, fp) != 1)
|
|
return 0;
|
|
|
|
return sizeof(msg) + msg.len;
|
|
}
|
|
|
|
bool lu_is_pending(void)
|
|
{
|
|
return lu_status != NULL;
|
|
}
|
|
|
|
#else
|
|
struct connection *lu_get_connection(void)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
unsigned int lu_write_response(FILE *fp)
|
|
{
|
|
/* Unsupported */
|
|
return 0;
|
|
}
|
|
|
|
bool lu_is_pending(void)
|
|
{
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
struct cmd_s {
|
|
char *cmd;
|
|
int (*func)(void *, struct connection *, char **, int);
|
|
char *pars;
|
|
/*
|
|
* max_pars can be used to limit the size of the parameter vector,
|
|
* e.g. in case of large binary parts in the parameters.
|
|
* The command is included in the count, so 1 means just the command
|
|
* without any parameter.
|
|
* 0 == no limit (the default)
|
|
*/
|
|
unsigned int max_pars;
|
|
};
|
|
|
|
static int do_control_check(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num)
|
|
return EINVAL;
|
|
|
|
check_store();
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_log(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num != 1)
|
|
return EINVAL;
|
|
|
|
if (!strcmp(vec[0], "on"))
|
|
reopen_log();
|
|
else if (!strcmp(vec[0], "off"))
|
|
close_log();
|
|
else
|
|
return EINVAL;
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef __MINIOS__
|
|
static int do_control_memreport(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num)
|
|
return EINVAL;
|
|
|
|
talloc_report_full(NULL, stdout);
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
#else
|
|
static int do_control_logfile(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num != 1)
|
|
return EINVAL;
|
|
|
|
close_log();
|
|
talloc_free(tracefile);
|
|
tracefile = talloc_strdup(NULL, vec[0]);
|
|
reopen_log();
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_memreport(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
FILE *fp;
|
|
int fd;
|
|
|
|
if (num > 1)
|
|
return EINVAL;
|
|
|
|
if (num == 0) {
|
|
if (tracefd < 0) {
|
|
if (!tracefile)
|
|
return EBADF;
|
|
fp = fopen(tracefile, "a");
|
|
} else {
|
|
/*
|
|
* Use dup() in order to avoid closing the file later
|
|
* with fclose() which will release stream resources.
|
|
*/
|
|
fd = dup(tracefd);
|
|
if (fd < 0)
|
|
return EBADF;
|
|
fp = fdopen(fd, "a");
|
|
if (!fp)
|
|
close(fd);
|
|
}
|
|
} else
|
|
fp = fopen(vec[0], "a");
|
|
|
|
if (!fp)
|
|
return EBADF;
|
|
|
|
talloc_report_full(NULL, fp);
|
|
fclose(fp);
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int do_control_print(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num != 1)
|
|
return EINVAL;
|
|
|
|
xprintf("control: %s", vec[0]);
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef NO_LIVE_UPDATE
|
|
static const char *lu_abort(const void *ctx, struct connection *conn)
|
|
{
|
|
syslog(LOG_INFO, "live-update: abort\n");
|
|
|
|
if (!lu_status)
|
|
return "No live-update session active.";
|
|
|
|
/* Destructor will do the real abort handling. */
|
|
talloc_free(lu_status);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const char *lu_cmdline(const void *ctx, struct connection *conn,
|
|
const char *cmdline)
|
|
{
|
|
syslog(LOG_INFO, "live-update: cmdline %s\n", cmdline);
|
|
|
|
if (!lu_status || lu_status->conn != conn)
|
|
return "Not in live-update session.";
|
|
|
|
lu_status->cmdline = talloc_strdup(lu_status, cmdline);
|
|
if (!lu_status->cmdline)
|
|
return "Allocation failure.";
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef __MINIOS__
|
|
static const char *lu_binary_alloc(const void *ctx, struct connection *conn,
|
|
unsigned long size)
|
|
{
|
|
const char *ret;
|
|
|
|
syslog(LOG_INFO, "live-update: binary size %lu\n", size);
|
|
|
|
ret = lu_begin(conn);
|
|
if (ret)
|
|
return ret;
|
|
|
|
lu_status->kernel = talloc_size(lu_status, size);
|
|
if (!lu_status->kernel)
|
|
return "Allocation failure.";
|
|
|
|
lu_status->kernel_size = size;
|
|
lu_status->kernel_off = 0;
|
|
|
|
errno = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *lu_binary_save(const void *ctx, struct connection *conn,
|
|
unsigned int size, const char *data)
|
|
{
|
|
if (!lu_status || lu_status->conn != conn)
|
|
return "Not in live-update session.";
|
|
|
|
if (lu_status->kernel_off + size > lu_status->kernel_size)
|
|
return "Too much kernel data.";
|
|
|
|
memcpy(lu_status->kernel + lu_status->kernel_off, data, size);
|
|
lu_status->kernel_off += size;
|
|
|
|
errno = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *lu_arch(const void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num == 2 && !strcmp(vec[0], "-b"))
|
|
return lu_binary_alloc(ctx, conn, atol(vec[1]));
|
|
if (num > 2 && !strcmp(vec[0], "-d"))
|
|
return lu_binary_save(ctx, conn, atoi(vec[1]), vec[2]);
|
|
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
static FILE *lu_dump_open(const void *ctx)
|
|
{
|
|
lu_status->dump_size = ROUNDUP(talloc_total_size(NULL) * 2,
|
|
XC_PAGE_SHIFT);
|
|
lu_status->dump_state = mmap(NULL, lu_status->dump_size,
|
|
PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
if (lu_status->dump_state == MAP_FAILED)
|
|
return NULL;
|
|
|
|
return fmemopen(lu_status->dump_state, lu_status->dump_size, "w");
|
|
}
|
|
|
|
static void lu_dump_close(FILE *fp)
|
|
{
|
|
size_t size;
|
|
|
|
size = ftell(fp);
|
|
size = ROUNDUP(size, XC_PAGE_SHIFT);
|
|
munmap(lu_status->dump_state + size, lu_status->dump_size - size);
|
|
lu_status->dump_size = size;
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
static void lu_get_dump_state(struct lu_dump_state *state)
|
|
{
|
|
}
|
|
|
|
static void lu_close_dump_state(struct lu_dump_state *state)
|
|
{
|
|
}
|
|
|
|
static char *lu_exec(const void *ctx, int argc, char **argv)
|
|
{
|
|
return "NYI";
|
|
}
|
|
#else
|
|
static const char *lu_binary(const void *ctx, struct connection *conn,
|
|
const char *filename)
|
|
{
|
|
const char *ret;
|
|
struct stat statbuf;
|
|
|
|
syslog(LOG_INFO, "live-update: binary %s\n", filename);
|
|
|
|
if (stat(filename, &statbuf))
|
|
return "File not accessible.";
|
|
if (!(statbuf.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)))
|
|
return "File not executable.";
|
|
|
|
ret = lu_begin(conn);
|
|
if (ret)
|
|
return ret;
|
|
|
|
lu_status->filename = talloc_strdup(lu_status, filename);
|
|
if (!lu_status->filename)
|
|
return "Allocation failure.";
|
|
|
|
errno = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static const char *lu_arch(const void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num == 2 && !strcmp(vec[0], "-f"))
|
|
return lu_binary(ctx, conn, vec[1]);
|
|
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
static FILE *lu_dump_open(const void *ctx)
|
|
{
|
|
char *filename;
|
|
int fd;
|
|
|
|
filename = talloc_asprintf(ctx, "%s/state_dump", xs_daemon_rootdir());
|
|
if (!filename)
|
|
return NULL;
|
|
|
|
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
return fdopen(fd, "w");
|
|
}
|
|
|
|
static void lu_dump_close(FILE *fp)
|
|
{
|
|
fclose(fp);
|
|
}
|
|
|
|
static void lu_get_dump_state(struct lu_dump_state *state)
|
|
{
|
|
struct stat statbuf;
|
|
|
|
state->size = 0;
|
|
|
|
state->filename = talloc_asprintf(NULL, "%s/state_dump",
|
|
xs_daemon_rootdir());
|
|
if (!state->filename)
|
|
barf("Allocation failure");
|
|
|
|
state->fd = open(state->filename, O_RDONLY);
|
|
if (state->fd < 0)
|
|
return;
|
|
if (fstat(state->fd, &statbuf) != 0)
|
|
goto out_close;
|
|
state->size = statbuf.st_size;
|
|
|
|
state->buf = mmap(NULL, state->size, PROT_READ, MAP_PRIVATE,
|
|
state->fd, 0);
|
|
if (state->buf == MAP_FAILED) {
|
|
state->size = 0;
|
|
goto out_close;
|
|
}
|
|
|
|
return;
|
|
|
|
out_close:
|
|
close(state->fd);
|
|
}
|
|
|
|
static void lu_close_dump_state(struct lu_dump_state *state)
|
|
{
|
|
assert(state->filename != NULL);
|
|
|
|
munmap(state->buf, state->size);
|
|
close(state->fd);
|
|
|
|
unlink(state->filename);
|
|
talloc_free(state->filename);
|
|
}
|
|
|
|
static char *lu_exec(const void *ctx, int argc, char **argv)
|
|
{
|
|
argv[0] = lu_status->filename;
|
|
execvp(argv[0], argv);
|
|
|
|
return "Error activating new binary.";
|
|
}
|
|
#endif
|
|
|
|
static bool lu_check_lu_allowed(void)
|
|
{
|
|
struct connection *conn;
|
|
time_t now = time(NULL);
|
|
unsigned int ta_total = 0, ta_long = 0;
|
|
|
|
list_for_each_entry(conn, &connections, list) {
|
|
if (conn->ta_start_time) {
|
|
ta_total++;
|
|
if (now - conn->ta_start_time >= lu_status->timeout)
|
|
ta_long++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Allow LiveUpdate if one of the following conditions is met:
|
|
* - There is no active transactions
|
|
* - All transactions are long running (e.g. they have been
|
|
* active for more than lu_status->timeout sec) and the admin as
|
|
* requested to force the operation.
|
|
*/
|
|
return ta_total ? (lu_status->force && ta_long == ta_total) : true;
|
|
}
|
|
|
|
static const char *lu_reject_reason(const void *ctx)
|
|
{
|
|
char *ret = NULL;
|
|
struct connection *conn;
|
|
time_t now = time(NULL);
|
|
|
|
list_for_each_entry(conn, &connections, list) {
|
|
if (conn->ta_start_time &&
|
|
(now - conn->ta_start_time >= lu_status->timeout)) {
|
|
ret = talloc_asprintf(ctx, "%s\nDomain %u: %ld s",
|
|
ret ? : "Domains with long running transactions:",
|
|
conn->id,
|
|
now - conn->ta_start_time);
|
|
}
|
|
}
|
|
|
|
return ret ? (const char *)ret : "Overlapping transactions";
|
|
}
|
|
|
|
static const char *lu_dump_state(const void *ctx, struct connection *conn)
|
|
{
|
|
FILE *fp;
|
|
const char *ret;
|
|
struct xs_state_record_header end;
|
|
struct xs_state_preamble pre;
|
|
|
|
fp = lu_dump_open(ctx);
|
|
if (!fp)
|
|
return "Dump state open error";
|
|
|
|
memcpy(pre.ident, XS_STATE_IDENT, sizeof(pre.ident));
|
|
pre.version = htobe32(XS_STATE_VERSION);
|
|
pre.flags = XS_STATE_FLAGS;
|
|
if (fwrite(&pre, sizeof(pre), 1, fp) != 1) {
|
|
ret = "Dump write error";
|
|
goto out;
|
|
}
|
|
|
|
ret = dump_state_global(fp);
|
|
if (ret)
|
|
goto out;
|
|
ret = dump_state_connections(fp);
|
|
if (ret)
|
|
goto out;
|
|
ret = dump_state_special_nodes(fp);
|
|
if (ret)
|
|
goto out;
|
|
ret = dump_state_nodes(fp, ctx);
|
|
if (ret)
|
|
goto out;
|
|
|
|
end.type = XS_STATE_TYPE_END;
|
|
end.length = 0;
|
|
if (fwrite(&end, sizeof(end), 1, fp) != 1)
|
|
ret = "Dump write error";
|
|
|
|
out:
|
|
lu_dump_close(fp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void lu_read_state(void)
|
|
{
|
|
struct lu_dump_state state = {};
|
|
struct xs_state_record_header *head;
|
|
void *ctx = talloc_new(NULL); /* Work context for subfunctions. */
|
|
struct xs_state_preamble *pre;
|
|
|
|
syslog(LOG_INFO, "live-update: read state\n");
|
|
lu_get_dump_state(&state);
|
|
if (state.size == 0)
|
|
barf_perror("No state found after live-update");
|
|
|
|
pre = state.buf;
|
|
if (memcmp(pre->ident, XS_STATE_IDENT, sizeof(pre->ident)) ||
|
|
pre->version != htobe32(XS_STATE_VERSION) ||
|
|
pre->flags != XS_STATE_FLAGS)
|
|
barf("Unknown record identifier");
|
|
for (head = state.buf + sizeof(*pre);
|
|
head->type != XS_STATE_TYPE_END &&
|
|
(void *)head - state.buf < state.size;
|
|
head = (void *)head + sizeof(*head) + head->length) {
|
|
switch (head->type) {
|
|
case XS_STATE_TYPE_GLOBAL:
|
|
read_state_global(ctx, head + 1);
|
|
break;
|
|
case XS_STATE_TYPE_CONN:
|
|
read_state_connection(ctx, head + 1);
|
|
break;
|
|
case XS_STATE_TYPE_WATCH:
|
|
read_state_watch(ctx, head + 1);
|
|
break;
|
|
case XS_STATE_TYPE_TA:
|
|
xprintf("live-update: ignore transaction record\n");
|
|
break;
|
|
case XS_STATE_TYPE_NODE:
|
|
read_state_node(ctx, head + 1);
|
|
break;
|
|
default:
|
|
xprintf("live-update: unknown state record %08x\n",
|
|
head->type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lu_close_dump_state(&state);
|
|
|
|
talloc_free(ctx);
|
|
|
|
/*
|
|
* We may have missed the VIRQ_DOM_EXC notification and a domain may
|
|
* have died while we were live-updating. So check all the domains are
|
|
* still alive.
|
|
*/
|
|
check_domains();
|
|
}
|
|
|
|
static const char *lu_activate_binary(const void *ctx)
|
|
{
|
|
int argc;
|
|
char **argv;
|
|
unsigned int i;
|
|
|
|
if (lu_status->cmdline) {
|
|
argc = 4; /* At least one arg + progname + "-U" + NULL. */
|
|
for (i = 0; lu_status->cmdline[i]; i++)
|
|
if (isspace(lu_status->cmdline[i]))
|
|
argc++;
|
|
argv = talloc_array(ctx, char *, argc);
|
|
if (!argv)
|
|
return "Allocation failure.";
|
|
|
|
i = 0;
|
|
argc = 1;
|
|
argv[1] = strtok(lu_status->cmdline, " \t");
|
|
while (argv[argc]) {
|
|
if (!strcmp(argv[argc], "-U"))
|
|
i = 1;
|
|
argc++;
|
|
argv[argc] = strtok(NULL, " \t");
|
|
}
|
|
|
|
if (!i) {
|
|
argv[argc++] = "-U";
|
|
argv[argc] = NULL;
|
|
}
|
|
} else {
|
|
for (i = 0; i < orig_argc; i++)
|
|
if (!strcmp(orig_argv[i], "-U"))
|
|
break;
|
|
|
|
argc = orig_argc;
|
|
argv = talloc_array(ctx, char *, orig_argc + 2);
|
|
if (!argv)
|
|
return "Allocation failure.";
|
|
|
|
memcpy(argv, orig_argv, orig_argc * sizeof(*argv));
|
|
if (i == orig_argc)
|
|
argv[argc++] = "-U";
|
|
argv[argc] = NULL;
|
|
}
|
|
|
|
domain_deinit();
|
|
|
|
return lu_exec(ctx, argc, argv);
|
|
}
|
|
|
|
static bool do_lu_start(struct delayed_request *req)
|
|
{
|
|
time_t now = time(NULL);
|
|
const char *ret;
|
|
struct buffered_data *saved_in;
|
|
struct connection *conn = req->data;
|
|
|
|
/*
|
|
* Cancellation may have been requested asynchronously. In this
|
|
* case, lu_status will be NULL.
|
|
*/
|
|
if (!lu_status) {
|
|
ret = "Cancellation was requested";
|
|
goto out;
|
|
}
|
|
|
|
assert(lu_status->conn == conn);
|
|
|
|
if (!lu_check_lu_allowed()) {
|
|
if (now < lu_status->started_at + lu_status->timeout)
|
|
return false;
|
|
if (!lu_status->force) {
|
|
ret = lu_reject_reason(req);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
assert(req->in == lu_status->in);
|
|
/* Dump out internal state, including "OK" for live update. */
|
|
ret = lu_dump_state(req->in, conn);
|
|
if (!ret) {
|
|
/* Perform the activation of new binary. */
|
|
ret = lu_activate_binary(req->in);
|
|
}
|
|
|
|
/* We will reach this point only in case of failure. */
|
|
out:
|
|
/*
|
|
* send_reply() will send the response for conn->in. Save the current
|
|
* conn->in and restore it afterwards.
|
|
*/
|
|
saved_in = conn->in;
|
|
conn->in = req->in;
|
|
send_reply(conn, XS_CONTROL, ret, strlen(ret) + 1);
|
|
conn->in = saved_in;
|
|
talloc_free(lu_status);
|
|
|
|
return true;
|
|
}
|
|
|
|
static const char *lu_start(const void *ctx, struct connection *conn,
|
|
bool force, unsigned int to)
|
|
{
|
|
syslog(LOG_INFO, "live-update: start, force=%d, to=%u\n", force, to);
|
|
|
|
if (!lu_status || lu_status->conn != conn)
|
|
return "Not in live-update session.";
|
|
|
|
#ifdef __MINIOS__
|
|
if (lu_status->kernel_size != lu_status->kernel_off)
|
|
return "Kernel not complete.";
|
|
#endif
|
|
|
|
lu_status->force = force;
|
|
lu_status->timeout = to;
|
|
lu_status->started_at = time(NULL);
|
|
lu_status->in = conn->in;
|
|
|
|
errno = delay_request(conn, conn->in, do_lu_start, conn, false);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int do_control_lu(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
const char *ret = NULL;
|
|
unsigned int i;
|
|
bool force = false;
|
|
unsigned int to = 0;
|
|
|
|
if (num < 1)
|
|
return EINVAL;
|
|
|
|
if (!strcmp(vec[0], "-a")) {
|
|
if (num == 1)
|
|
ret = lu_abort(ctx, conn);
|
|
else
|
|
return EINVAL;
|
|
} else if (!strcmp(vec[0], "-c")) {
|
|
if (num == 2)
|
|
ret = lu_cmdline(ctx, conn, vec[1]);
|
|
else
|
|
return EINVAL;
|
|
} else if (!strcmp(vec[0], "-s")) {
|
|
for (i = 1; i < num; i++) {
|
|
if (!strcmp(vec[i], "-F"))
|
|
force = true;
|
|
else if (!strcmp(vec[i], "-t") && i < num - 1) {
|
|
i++;
|
|
to = atoi(vec[i]);
|
|
} else
|
|
return EINVAL;
|
|
}
|
|
ret = lu_start(ctx, conn, force, to);
|
|
if (!ret)
|
|
return errno;
|
|
} else {
|
|
ret = lu_arch(ctx, conn, vec, num);
|
|
if (!ret && errno)
|
|
return errno;
|
|
}
|
|
|
|
if (!ret)
|
|
ret = "OK";
|
|
send_reply(conn, XS_CONTROL, ret, strlen(ret) + 1);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int do_control_help(void *, struct connection *, char **, int);
|
|
|
|
static struct cmd_s cmds[] = {
|
|
{ "check", do_control_check, "" },
|
|
{ "log", do_control_log, "on|off" },
|
|
|
|
#ifndef NO_LIVE_UPDATE
|
|
/*
|
|
* The parameters are those of the xenstore-control utility!
|
|
* Depending on environment (Mini-OS or daemon) the live-update
|
|
* sequence is split into several sub-operations:
|
|
* 1. Specification of new binary
|
|
* daemon: -f <filename>
|
|
* Mini-OS: -b <binary-size>
|
|
* -d <size> <data-bytes> (multiple of those)
|
|
* 2. New command-line (optional): -c <cmdline>
|
|
* 3. Start of update: -s [-F] [-t <timeout>]
|
|
* Any sub-operation needs to respond with the string "OK" in case
|
|
* of success, any other response indicates failure.
|
|
* A started live-update sequence can be aborted via "-a" (not
|
|
* needed in case of failure for the first or last live-update
|
|
* sub-operation).
|
|
*/
|
|
{ "live-update", do_control_lu,
|
|
"[-c <cmdline>] [-F] [-t <timeout>] <file>\n"
|
|
" Default timeout is 60 seconds.", 5 },
|
|
#endif
|
|
#ifdef __MINIOS__
|
|
{ "memreport", do_control_memreport, "" },
|
|
#else
|
|
{ "logfile", do_control_logfile, "<file>" },
|
|
{ "memreport", do_control_memreport, "[<file>]" },
|
|
#endif
|
|
{ "print", do_control_print, "<string>" },
|
|
{ "help", do_control_help, "" },
|
|
};
|
|
|
|
static int do_control_help(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
int cmd, len = 0;
|
|
char *resp;
|
|
|
|
if (num)
|
|
return EINVAL;
|
|
|
|
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
|
|
len += strlen(cmds[cmd].cmd) + 1;
|
|
len += strlen(cmds[cmd].pars) + 1;
|
|
}
|
|
len++;
|
|
|
|
resp = talloc_array(ctx, char, len);
|
|
if (!resp)
|
|
return ENOMEM;
|
|
|
|
len = 0;
|
|
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
|
|
strcpy(resp + len, cmds[cmd].cmd);
|
|
len += strlen(cmds[cmd].cmd);
|
|
resp[len] = '\t';
|
|
len++;
|
|
strcpy(resp + len, cmds[cmd].pars);
|
|
len += strlen(cmds[cmd].pars);
|
|
resp[len] = '\n';
|
|
len++;
|
|
}
|
|
resp[len] = 0;
|
|
|
|
send_reply(conn, XS_CONTROL, resp, len);
|
|
return 0;
|
|
}
|
|
|
|
int do_control(struct connection *conn, struct buffered_data *in)
|
|
{
|
|
unsigned int cmd, num, off;
|
|
char **vec = NULL;
|
|
|
|
if (conn->id != 0)
|
|
return EACCES;
|
|
|
|
off = get_string(in, 0);
|
|
if (!off)
|
|
return EINVAL;
|
|
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++)
|
|
if (streq(in->buffer, cmds[cmd].cmd))
|
|
break;
|
|
if (cmd == ARRAY_SIZE(cmds))
|
|
return EINVAL;
|
|
|
|
num = xs_count_strings(in->buffer, in->used);
|
|
if (cmds[cmd].max_pars)
|
|
num = min(num, cmds[cmd].max_pars);
|
|
vec = talloc_array(in, char *, num);
|
|
if (!vec)
|
|
return ENOMEM;
|
|
if (get_strings(in, vec, num) < num)
|
|
return EIO;
|
|
|
|
return cmds[cmd].func(in, conn, vec + 1, num - 1);
|
|
}
|