mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
The xenstore header xs.h is producing conflicts with other software[1]. xs is a too short identifier and does not matche the library. Renaming the headers to xenstore.h and xenstore_lib.h is the easiest way to make them easy recognizable and prevent furthe problems. [1]: http://bugs.debian.org/668550 [ Also update QEMU_TAG, to bring in corresponding change to qemu-xen-traditional. -iwj ] Signed-off-by: Bastian Blank <waldi@debian.org> Signed-off-by: Ian Campbell <ian.campbell@citrix.com> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> Committed-by: Ian Jackson <ian.jackson@eu.citrix.com> --HG-- rename : tools/xenstore/xs.h => tools/xenstore/xenstore.h rename : tools/xenstore/xs_lib.h => tools/xenstore/xenstore_lib.h
661 lines
16 KiB
C
661 lines
16 KiB
C
/*
|
|
* This file is subject to the terms and conditions of the GNU General
|
|
* Public License. See the file "COPYING" in the main directory of
|
|
* this archive for more details.
|
|
*
|
|
* Copyright (C) 2005 by Christian Limpach
|
|
* Copyright (C) 2005 XenSource Ltd.
|
|
*
|
|
*/
|
|
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <xenstore.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#define PATH_SEP '/'
|
|
#define MAX_PATH_LEN 256
|
|
|
|
#define MAX_PERMS 16
|
|
|
|
enum mode {
|
|
MODE_unknown,
|
|
MODE_chmod,
|
|
MODE_exists,
|
|
MODE_list,
|
|
MODE_ls,
|
|
MODE_read,
|
|
MODE_rm,
|
|
MODE_write,
|
|
MODE_watch,
|
|
};
|
|
|
|
static char *output_buf = NULL;
|
|
static int output_pos = 0;
|
|
|
|
static int output_size = 0;
|
|
|
|
static void
|
|
output(const char *fmt, ...) {
|
|
va_list ap;
|
|
int len;
|
|
char buf[1];
|
|
|
|
va_start(ap, fmt);
|
|
len = vsnprintf(buf, 1, fmt, ap);
|
|
if (len < 0)
|
|
err(1, "output");
|
|
va_end(ap);
|
|
if (len + 1 + output_pos > output_size) {
|
|
output_size += len + 1024;
|
|
output_buf = realloc(output_buf, output_size);
|
|
if (output_buf == NULL)
|
|
err(1, "malloc");
|
|
}
|
|
va_start(ap, fmt);
|
|
if (vsnprintf(&output_buf[output_pos], len + 1, fmt, ap) != len)
|
|
err(1, "output");
|
|
va_end(ap);
|
|
output_pos += len;
|
|
}
|
|
|
|
static void
|
|
usage(enum mode mode, int incl_mode, const char *progname)
|
|
{
|
|
const char *mstr = NULL;
|
|
|
|
switch (mode) {
|
|
case MODE_unknown:
|
|
errx(1, "Usage: %s <mode> [-h] [...]", progname);
|
|
case MODE_read:
|
|
mstr = incl_mode ? "read " : "";
|
|
errx(1, "Usage: %s %s[-h] [-p] [-s] key [...]", progname, mstr);
|
|
case MODE_write:
|
|
mstr = incl_mode ? "write " : "";
|
|
errx(1, "Usage: %s %s[-h] [-s] key value [...]", progname, mstr);
|
|
case MODE_rm:
|
|
mstr = incl_mode ? "rm " : "";
|
|
errx(1, "Usage: %s %s[-h] [-s] [-t] key [...]", progname, mstr);
|
|
case MODE_exists:
|
|
mstr = incl_mode ? "exists " : "";
|
|
case MODE_list:
|
|
mstr = mstr ? : incl_mode ? "list " : "";
|
|
errx(1, "Usage: %s %s[-h] [-p] [-s] key [...]", progname, mstr);
|
|
case MODE_ls:
|
|
mstr = mstr ? : incl_mode ? "ls " : "";
|
|
errx(1, "Usage: %s %s[-h] [-f] [-p] [-s] [path]", progname, mstr);
|
|
case MODE_chmod:
|
|
mstr = incl_mode ? "chmod " : "";
|
|
errx(1, "Usage: %s %s[-h] [-u] [-r] [-s] key <mode [modes...]>", progname, mstr);
|
|
case MODE_watch:
|
|
mstr = incl_mode ? "watch " : "";
|
|
errx(1, "Usage: %s %s[-h] [-n NR] key", progname, mstr);
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
do_rm(char *path, struct xs_handle *xsh, xs_transaction_t xth)
|
|
{
|
|
if (xs_rm(xsh, xth, path)) {
|
|
return 0;
|
|
}
|
|
else {
|
|
warnx("could not remove path %s", path);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
#define STRING_MAX XENSTORE_ABS_PATH_MAX+1024
|
|
static int max_width = 80;
|
|
static int desired_width = 60;
|
|
static int show_whole_path = 0;
|
|
|
|
#define TAG " = \"...\""
|
|
#define TAG_LEN strlen(TAG)
|
|
|
|
#define MIN(a, b) (((a) < (b))? (a) : (b))
|
|
|
|
static void do_ls(struct xs_handle *h, char *path, int cur_depth, int show_perms)
|
|
{
|
|
static struct expanding_buffer ebuf;
|
|
char **e;
|
|
char newpath[STRING_MAX], *val;
|
|
int newpath_len;
|
|
int i;
|
|
unsigned int num, len;
|
|
|
|
e = xs_directory(h, XBT_NULL, path, &num);
|
|
if (e == NULL)
|
|
err(1, "xs_directory (%s)", path);
|
|
|
|
for (i = 0; i<num; i++) {
|
|
char buf[MAX_STRLEN(unsigned int)+1];
|
|
struct xs_permissions *perms;
|
|
unsigned int nperms;
|
|
int linewid;
|
|
|
|
/* Compose fullpath */
|
|
newpath_len = snprintf(newpath, sizeof(newpath), "%s%s%s", path,
|
|
path[strlen(path)-1] == '/' ? "" : "/",
|
|
e[i]);
|
|
|
|
/* Print indent and path basename */
|
|
linewid = 0;
|
|
if (show_whole_path) {
|
|
fputs(newpath, stdout);
|
|
} else {
|
|
for (; linewid<cur_depth; linewid++) {
|
|
putchar(' ');
|
|
}
|
|
linewid += printf("%.*s",
|
|
(int) (max_width - TAG_LEN - linewid), e[i]);
|
|
}
|
|
|
|
/* Fetch value */
|
|
if ( newpath_len < sizeof(newpath) ) {
|
|
val = xs_read(h, XBT_NULL, newpath, &len);
|
|
}
|
|
else {
|
|
/* Path was truncated and thus invalid */
|
|
val = NULL;
|
|
len = 0;
|
|
}
|
|
|
|
/* Print value */
|
|
if (val == NULL) {
|
|
printf(":\n");
|
|
}
|
|
else {
|
|
if (max_width < (linewid + len + TAG_LEN)) {
|
|
printf(" = \"%.*s\\...\"",
|
|
(int)(max_width - TAG_LEN - linewid),
|
|
sanitise_value(&ebuf, val, len));
|
|
}
|
|
else {
|
|
linewid += printf(" = \"%s\"",
|
|
sanitise_value(&ebuf, val, len));
|
|
if (show_perms) {
|
|
putchar(' ');
|
|
for (linewid++;
|
|
linewid < MIN(desired_width, max_width);
|
|
linewid++)
|
|
putchar((linewid & 1)? '.' : ' ');
|
|
}
|
|
}
|
|
}
|
|
free(val);
|
|
|
|
if (show_perms) {
|
|
perms = xs_get_permissions(h, XBT_NULL, newpath, &nperms);
|
|
if (perms == NULL) {
|
|
warn("\ncould not access permissions for %s", e[i]);
|
|
}
|
|
else {
|
|
int i;
|
|
fputs(" (", stdout);
|
|
for (i = 0; i < nperms; i++) {
|
|
if (i)
|
|
putchar(',');
|
|
xs_perm_to_string(perms+i, buf, sizeof(buf));
|
|
fputs(buf, stdout);
|
|
}
|
|
putchar(')');
|
|
}
|
|
}
|
|
|
|
putchar('\n');
|
|
|
|
do_ls(h, newpath, cur_depth+1, show_perms);
|
|
}
|
|
free(e);
|
|
}
|
|
|
|
static void
|
|
do_chmod(char *path, struct xs_permissions *perms, int nperms, int upto,
|
|
int recurse, struct xs_handle *xsh, xs_transaction_t xth)
|
|
{
|
|
int ret;
|
|
|
|
if (!path[0])
|
|
return;
|
|
|
|
ret = xs_set_permissions(xsh, xth, path, perms, nperms);
|
|
if (!ret)
|
|
err(1, "Error occurred setting permissions on '%s'", path);
|
|
|
|
if (upto) {
|
|
/* apply same permissions to all parent entries: */
|
|
char *path_sep_ptr = strrchr(path, PATH_SEP);
|
|
if (!path_sep_ptr)
|
|
errx(1, "Unable to locate path separator '%c' in '%s'",
|
|
PATH_SEP, path);
|
|
|
|
*path_sep_ptr = '\0'; /* truncate path */
|
|
|
|
do_chmod(path, perms, nperms, 1, 0, xsh, xth);
|
|
|
|
*path_sep_ptr = PATH_SEP;
|
|
}
|
|
|
|
if (recurse) {
|
|
char buf[MAX_PATH_LEN];
|
|
|
|
/* apply same permissions to all child entries: */
|
|
unsigned int xsval_n;
|
|
char **xsval = xs_directory(xsh, xth, path, &xsval_n);
|
|
|
|
if (xsval) {
|
|
int i;
|
|
for (i = 0; i < xsval_n; i++) {
|
|
snprintf(buf, MAX_PATH_LEN, "%s/%s", path, xsval[i]);
|
|
|
|
do_chmod(buf, perms, nperms, 0, 1, xsh, xth);
|
|
}
|
|
|
|
free(xsval);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
do_watch(struct xs_handle *xsh, int max_events)
|
|
{
|
|
int count = 0;
|
|
char **vec = NULL;
|
|
|
|
for ( count = 0; max_events == -1 || count < max_events; count++ ) {
|
|
unsigned int num;
|
|
|
|
vec = xs_read_watch(xsh, &num);
|
|
if (vec == NULL)
|
|
continue;
|
|
|
|
printf("%s\n", vec[XS_WATCH_PATH]);
|
|
fflush(stdout);
|
|
free(vec);
|
|
}
|
|
}
|
|
|
|
static int
|
|
perform(enum mode mode, int optind, int argc, char **argv, struct xs_handle *xsh,
|
|
xs_transaction_t xth, int prefix, int tidy, int upto, int recurse, int nr_watches)
|
|
{
|
|
switch (mode) {
|
|
case MODE_ls:
|
|
if (optind == argc)
|
|
{
|
|
optind=0;
|
|
argc=1;
|
|
argv[0] = "/";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
while (optind < argc) {
|
|
switch (mode) {
|
|
case MODE_unknown:
|
|
/* CANNOT BE REACHED */
|
|
errx(1, "invalid mode %d", mode);
|
|
case MODE_read: {
|
|
static struct expanding_buffer ebuf;
|
|
unsigned len;
|
|
char *val = xs_read(xsh, xth, argv[optind], &len);
|
|
if (val == NULL) {
|
|
warnx("couldn't read path %s", argv[optind]);
|
|
return 1;
|
|
}
|
|
if (prefix)
|
|
output("%s: ", argv[optind]);
|
|
output("%s\n", sanitise_value(&ebuf, val, len));
|
|
free(val);
|
|
optind++;
|
|
break;
|
|
}
|
|
case MODE_write: {
|
|
static struct expanding_buffer ebuf;
|
|
char *val_spec = argv[optind + 1];
|
|
unsigned len;
|
|
expanding_buffer_ensure(&ebuf, strlen(val_spec)+1);
|
|
unsanitise_value(ebuf.buf, &len, val_spec);
|
|
if (!xs_write(xsh, xth, argv[optind], ebuf.buf, len)) {
|
|
warnx("could not write path %s", argv[optind]);
|
|
return 1;
|
|
}
|
|
optind += 2;
|
|
} break;
|
|
case MODE_rm: {
|
|
/* Remove the specified path. If the tidy flag is set, then also
|
|
remove any containing directories that are both empty and have no
|
|
value attached, and repeat, recursing all the way up to the root if
|
|
necessary.
|
|
*/
|
|
|
|
char *slash, *path = argv[optind];
|
|
|
|
if (tidy) {
|
|
/* Copy path, because we can't modify argv because we will need it
|
|
again if xs_transaction_end gives us EAGAIN. */
|
|
char *p = malloc(strlen(path) + 1);
|
|
strcpy(p, path);
|
|
path = p;
|
|
|
|
again:
|
|
if (do_rm(path, xsh, xth)) {
|
|
return 1;
|
|
}
|
|
|
|
slash = strrchr(p, '/');
|
|
if (slash) {
|
|
char *val;
|
|
unsigned len;
|
|
*slash = '\0';
|
|
val = xs_read(xsh, xth, p, &len);
|
|
if (val && len == 0) {
|
|
unsigned int num;
|
|
char ** list = xs_directory(xsh, xth, p, &num);
|
|
|
|
if (list) {
|
|
free(list);
|
|
if (num == 0)
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(path);
|
|
}
|
|
else {
|
|
if (do_rm(path, xsh, xth)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
optind++;
|
|
break;
|
|
}
|
|
case MODE_exists: {
|
|
char *val = xs_read(xsh, xth, argv[optind], NULL);
|
|
if (val == NULL) {
|
|
return 1;
|
|
}
|
|
free(val);
|
|
optind++;
|
|
break;
|
|
}
|
|
case MODE_list: {
|
|
unsigned int i, num;
|
|
char **list = xs_directory(xsh, xth, argv[optind], &num);
|
|
if (list == NULL) {
|
|
warnx("could not list path %s", argv[optind]);
|
|
return 1;
|
|
}
|
|
for (i = 0; i < num; i++) {
|
|
if (prefix)
|
|
output("%s/", argv[optind]);
|
|
output("%s\n", list[i]);
|
|
}
|
|
free(list);
|
|
optind++;
|
|
break;
|
|
}
|
|
case MODE_ls: {
|
|
do_ls(xsh, argv[optind], 0, prefix);
|
|
optind++;
|
|
break;
|
|
}
|
|
case MODE_chmod: {
|
|
struct xs_permissions perms[MAX_PERMS];
|
|
int nperms = 0;
|
|
/* save path pointer: */
|
|
char *path = argv[optind++];
|
|
for (; argv[optind]; optind++, nperms++)
|
|
{
|
|
if (MAX_PERMS <= nperms)
|
|
errx(1, "Too many permissions specified. "
|
|
"Maximum per invocation is %d.", MAX_PERMS);
|
|
|
|
perms[nperms].id = atoi(argv[optind]+1);
|
|
|
|
switch (argv[optind][0])
|
|
{
|
|
case 'n':
|
|
perms[nperms].perms = XS_PERM_NONE;
|
|
break;
|
|
case 'r':
|
|
perms[nperms].perms = XS_PERM_READ;
|
|
break;
|
|
case 'w':
|
|
perms[nperms].perms = XS_PERM_WRITE;
|
|
break;
|
|
case 'b':
|
|
perms[nperms].perms = XS_PERM_READ | XS_PERM_WRITE;
|
|
break;
|
|
default:
|
|
errx(1, "Invalid permission specification: '%c'",
|
|
argv[optind][0]);
|
|
}
|
|
}
|
|
|
|
do_chmod(path, perms, nperms, upto, recurse, xsh, xth);
|
|
break;
|
|
}
|
|
case MODE_watch: {
|
|
for (; argv[optind]; optind++) {
|
|
const char *w = argv[optind];
|
|
|
|
if (!xs_watch(xsh, w, w))
|
|
errx(1, "Unable to add watch on %s\n", w);
|
|
}
|
|
do_watch(xsh, nr_watches);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static enum mode lookup_mode(const char *m)
|
|
{
|
|
if (strcmp(m, "read") == 0)
|
|
return MODE_read;
|
|
else if (strcmp(m, "chmod") == 0)
|
|
return MODE_chmod;
|
|
else if (strcmp(m, "exists") == 0)
|
|
return MODE_exists;
|
|
else if (strcmp(m, "list") == 0)
|
|
return MODE_list;
|
|
else if (strcmp(m, "ls") == 0)
|
|
return MODE_ls;
|
|
else if (strcmp(m, "rm") == 0)
|
|
return MODE_rm;
|
|
else if (strcmp(m, "write") == 0)
|
|
return MODE_write;
|
|
else if (strcmp(m, "read") == 0)
|
|
return MODE_read;
|
|
else if (strcmp(m, "watch") == 0)
|
|
return MODE_watch;
|
|
|
|
errx(1, "unknown mode %s\n", m);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct xs_handle *xsh;
|
|
xs_transaction_t xth = XBT_NULL;
|
|
int ret = 0, socket = 0;
|
|
int prefix = 0;
|
|
int tidy = 0;
|
|
int upto = 0;
|
|
int recurse = 0;
|
|
int nr_watches = -1;
|
|
int transaction;
|
|
struct winsize ws;
|
|
enum mode mode;
|
|
|
|
const char *_command = strrchr(argv[0], '/');
|
|
const char *command = _command ? &_command[1] : argv[0];
|
|
int switch_argv = -1; /* which element of argv did we switch on */
|
|
|
|
if (strncmp(command, "xenstore-", strlen("xenstore-")) == 0)
|
|
{
|
|
switch_argv = 0;
|
|
command = command + strlen("xenstore-");
|
|
}
|
|
else if (argc < 2)
|
|
usage(MODE_unknown, 0, argv[0]);
|
|
else
|
|
{
|
|
command = argv[1];
|
|
switch_argv = 1;
|
|
}
|
|
|
|
mode = lookup_mode(command);
|
|
|
|
while (1) {
|
|
int c, index = 0;
|
|
static struct option long_options[] = {
|
|
{"help", 0, 0, 'h'},
|
|
{"flat", 0, 0, 'f'}, /* MODE_ls */
|
|
{"socket", 0, 0, 's'},
|
|
{"prefix", 0, 0, 'p'}, /* MODE_read || MODE_list || MODE_ls */
|
|
{"tidy", 0, 0, 't'}, /* MODE_rm */
|
|
{"upto", 0, 0, 'u'}, /* MODE_chmod */
|
|
{"recurse", 0, 0, 'r'}, /* MODE_chmod */
|
|
{"number", 1, 0, 'n'}, /* MODE_watch */
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
c = getopt_long(argc - switch_argv, argv + switch_argv, "hfspturn:",
|
|
long_options, &index);
|
|
if (c == -1)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 'h':
|
|
usage(mode, switch_argv, argv[0]);
|
|
/* NOTREACHED */
|
|
case 'f':
|
|
if ( mode == MODE_ls ) {
|
|
max_width = INT_MAX/2;
|
|
desired_width = 0;
|
|
show_whole_path = 1;
|
|
} else {
|
|
usage(mode, switch_argv, argv[0]);
|
|
}
|
|
break;
|
|
case 's':
|
|
socket = 1;
|
|
break;
|
|
case 'p':
|
|
if ( mode == MODE_read || mode == MODE_list || mode == MODE_ls )
|
|
prefix = 1;
|
|
else
|
|
usage(mode, switch_argv, argv[0]);
|
|
break;
|
|
case 't':
|
|
if ( mode == MODE_rm )
|
|
tidy = 1;
|
|
else
|
|
usage(mode, switch_argv, argv[0]);
|
|
break;
|
|
case 'u':
|
|
if ( mode == MODE_chmod )
|
|
upto = 1;
|
|
else
|
|
usage(mode, switch_argv, argv[0]);
|
|
break;
|
|
case 'r':
|
|
if ( mode == MODE_chmod )
|
|
recurse = 1;
|
|
else
|
|
usage(mode, switch_argv, argv[0]);
|
|
break;
|
|
case 'n':
|
|
if ( mode == MODE_watch )
|
|
nr_watches = atoi(optarg);
|
|
else
|
|
usage(mode, switch_argv, argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (mode) {
|
|
case MODE_ls:
|
|
break;
|
|
case MODE_write:
|
|
if ((argc - switch_argv - optind) % 2 == 1) {
|
|
usage(mode, switch_argv, argv[0]);
|
|
/* NOTREACHED */
|
|
}
|
|
/* DROP-THRU */
|
|
default:
|
|
if (optind == argc - switch_argv) {
|
|
usage(mode, switch_argv, argv[0]);
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
|
|
switch (mode) {
|
|
case MODE_read:
|
|
transaction = (argc - switch_argv - optind) > 1;
|
|
break;
|
|
case MODE_write:
|
|
transaction = (argc - switch_argv - optind) > 2;
|
|
break;
|
|
case MODE_ls:
|
|
case MODE_watch:
|
|
transaction = 0;
|
|
break;
|
|
default:
|
|
transaction = 1;
|
|
break;
|
|
}
|
|
|
|
if ( mode == MODE_ls )
|
|
{
|
|
memset(&ws, 0, sizeof(ws));
|
|
ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
|
|
if (!ret)
|
|
max_width = ws.ws_col - 2;
|
|
}
|
|
|
|
xsh = xs_open(socket ? XS_OPEN_SOCKETONLY : 0);
|
|
if (xsh == NULL) err(1, "xs_open");
|
|
|
|
again:
|
|
if (transaction) {
|
|
xth = xs_transaction_start(xsh);
|
|
if (xth == XBT_NULL)
|
|
errx(1, "couldn't start transaction");
|
|
}
|
|
|
|
ret = perform(mode, optind, argc - switch_argv, argv + switch_argv, xsh, xth, prefix, tidy, upto, recurse, nr_watches);
|
|
|
|
if (transaction && !xs_transaction_end(xsh, xth, ret)) {
|
|
if (ret == 0 && errno == EAGAIN) {
|
|
output_pos = 0;
|
|
goto again;
|
|
}
|
|
errx(1, "couldn't end transaction");
|
|
}
|
|
|
|
if (output_pos)
|
|
printf("%s", output_buf);
|
|
|
|
return ret;
|
|
}
|