/*
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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "utils.h"
#include "talloc.h"
#include "core.h"
#include "control.h"
#include "domain.h"
#include "lu.h"
struct cmd_s {
char *cmd;
int (*func)(const void *, struct connection *, const 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(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num)
return EINVAL;
check_store();
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_log(const void *ctx, struct connection *conn,
const char **vec, int num)
{
int ret;
if (num == 0) {
char *resp = talloc_asprintf(ctx, "Log switch settings:\n");
unsigned int idx;
bool on;
if (!resp)
return ENOMEM;
for (idx = 0; trace_switches[idx]; idx++) {
on = trace_flags & (1u << idx);
resp = talloc_asprintf_append(resp, "%-8s: %s\n",
trace_switches[idx],
on ? "on" : "off");
if (!resp)
return ENOMEM;
}
send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
return 0;
}
if (num != 1)
return EINVAL;
if (!strcmp(vec[0], "on"))
reopen_log();
else if (!strcmp(vec[0], "off"))
close_log();
else {
ret = set_trace_switch(vec[0]);
if (ret)
return ret;
}
send_ack(conn, XS_CONTROL);
return 0;
}
static int quota_show_current(const void *ctx, struct connection *conn,
const struct quota *quotas)
{
char *resp;
unsigned int i;
resp = talloc_strdup(ctx, "Quota settings:\n");
if (!resp)
return ENOMEM;
for (i = 0; i < ACC_N; i++) {
if (!quotas[i].name)
continue;
resp = talloc_asprintf_append(resp, "%-17s: %8d %s\n",
quotas[i].name, quotas[i].val,
quotas[i].descr);
if (!resp)
return ENOMEM;
}
send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
return 0;
}
static int quota_set(const void *ctx, struct connection *conn,
const char **vec, int num, struct quota *quotas)
{
unsigned int i;
int val;
if (num != 2)
return EINVAL;
val = atoi(vec[1]);
if (val < 1)
return EINVAL;
for (i = 0; i < ACC_N; i++) {
if (quotas[i].name && !strcmp(vec[0], quotas[i].name)) {
quotas[i].val = val;
send_ack(conn, XS_CONTROL);
return 0;
}
}
return EINVAL;
}
static int quota_get(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num != 1)
return EINVAL;
return domain_get_quota(ctx, conn, atoi(vec[0]));
}
static int quota_max(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num > 1)
return EINVAL;
if (num == 1) {
if (!strcmp(vec[0], "-r"))
domain_reset_global_acc();
else
return EINVAL;
}
return domain_max_global_acc(ctx, conn);
}
static int do_control_quota(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num == 0)
return quota_show_current(ctx, conn, hard_quotas);
if (!strcmp(vec[0], "set"))
return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas);
if (!strcmp(vec[0], "max"))
return quota_max(ctx, conn, vec + 1, num - 1);
return quota_get(ctx, conn, vec, num);
}
static int do_control_quota_s(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num == 0)
return quota_show_current(ctx, conn, soft_quotas);
if (!strcmp(vec[0], "set"))
return quota_set(ctx, conn, vec + 1, num - 1, soft_quotas);
return EINVAL;
}
static int do_control_logfile(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num != 1)
return EINVAL;
close_log();
talloc_free(tracefile);
tracefile = absolute_filename(NULL, vec[0]);
reopen_log();
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_memreport(const void *ctx, struct connection *conn,
const char **vec, int num)
{
FILE *fp;
const char *filename;
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 {
filename = absolute_filename(ctx, vec[0]);
if (!filename)
return ENOMEM;
fp = fopen(filename, "a");
}
if (!fp)
return EBADF;
talloc_report_full(NULL, fp);
fclose(fp);
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_print(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num != 1)
return EINVAL;
xprintf("control: %s", vec[0]);
send_ack(conn, XS_CONTROL);
return 0;
}
static int do_control_help(const void *, struct connection *, const 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
* Mini-OS: -b
* -d (multiple of those)
* 2. New command-line (optional): -c
* 3. Start of update: -s [-F] [-t ] [-v ]
* 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 ] [-F] [-t ] [-v ] \n"
" Default timeout is 60 seconds, default version is 2.", 7 },
#endif
{ "logfile", do_control_logfile, "" },
{ "memreport", do_control_memreport, "[]" },
{ "print", do_control_print, "" },
{ "quota", do_control_quota,
"[set ||max [-r]]" },
{ "quota-soft", do_control_quota_s, "[set ]" },
{ "help", do_control_help, "" },
};
static int do_control_help(const void *ctx, struct connection *conn,
const char **vec, int num)
{
int cmd;
char *resp;
if (num)
return EINVAL;
resp = talloc_asprintf(ctx, "%s", "");
if (!resp)
return ENOMEM;
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
resp = talloc_asprintf_append(resp, "%-15s %s\n",
cmds[cmd].cmd, cmds[cmd].pars);
if (!resp)
return ENOMEM;
}
send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
return 0;
}
int do_control(const void *ctx, struct connection *conn,
struct buffered_data *in)
{
unsigned int cmd, num, off;
const char **vec = NULL;
if (domain_is_unprivileged(conn))
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 = xenstore_count_strings(in->buffer, in->used);
if (cmds[cmd].max_pars)
num = min(num, cmds[cmd].max_pars);
vec = (const char **)talloc_array(ctx, char *, num);
if (!vec)
return ENOMEM;
if (get_strings(in, vec, num) < num)
return EIO;
return cmds[cmd].func(ctx, conn, vec + 1, num - 1);
}