/* * 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 #include #include #include #include #include #include #include #include static char *output_buf = NULL; static int output_pos = 0; #if defined(CLIENT_read) || defined(CLIENT_list) 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; } #endif static void usage(const char *progname) { #if defined(CLIENT_read) errx(1, "Usage: %s [-h] [-p] [-s] key [...]", progname); #elif defined(CLIENT_write) errx(1, "Usage: %s [-h] [-s] key value [...]", progname); #elif defined(CLIENT_rm) errx(1, "Usage: %s [-h] [-s] [-t] key [...]", progname); #elif defined(CLIENT_exists) || defined(CLIENT_list) errx(1, "Usage: %s [-h] [-s] key [...]", progname); #elif defined(CLIENT_chmod) errx(1, "Usage: %s [-h] [-s] key ", progname); #endif } #if defined(CLIENT_rm) 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; } } #endif #if defined(CLIENT_chmod) #define PATH_SEP '/' #define MAX_PATH_LEN 256 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); } } } #endif static int perform(int optind, int argc, char **argv, struct xs_handle *xsh, xs_transaction_t xth, int prefix, int tidy, int upto, int recurse) { while (optind < argc) { #if defined(CLIENT_read) char *val = xs_read(xsh, xth, argv[optind], NULL); if (val == NULL) { warnx("couldn't read path %s", argv[optind]); return 1; } if (prefix) output("%s: ", argv[optind]); output("%s\n", val); free(val); optind++; #elif defined(CLIENT_write) if (!xs_write(xsh, xth, argv[optind], argv[optind + 1], strlen(argv[optind + 1]))) { warnx("could not write path %s", argv[optind]); return 1; } optind += 2; #elif defined(CLIENT_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; *slash = '\0'; val = xs_read(xsh, xth, p, NULL); if (val && strlen(val) == 0) { unsigned int num; char ** list = xs_directory(xsh, xth, p, &num); if (list && num == 0) { goto again; } } } free(path); } else { if (do_rm(path, xsh, xth)) { return 1; } } optind++; #elif defined(CLIENT_exists) char *val = xs_read(xsh, xth, argv[optind], NULL); if (val == NULL) { return 1; } free(val); optind++; #elif defined(CLIENT_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++; #elif defined(CLIENT_chmod) #define MAX_PERMS 16 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); #endif } 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 transaction; while (1) { int c, index = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"socket", 0, 0, 's'}, #if defined(CLIENT_read) || defined(CLIENT_list) {"prefix", 0, 0, 'p'}, #elif defined(CLIENT_rm) {"tidy", 0, 0, 't'}, #elif defined(CLIENT_chmod) {"upto", 0, 0, 'u'}, {"recurse", 0, 0, 'r'}, #endif {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hs" #if defined(CLIENT_read) || defined(CLIENT_list) "p" #elif defined(CLIENT_rm) "t" #elif defined(CLIENT_chmod) "ur" #endif , long_options, &index); if (c == -1) break; switch (c) { case 'h': usage(argv[0]); /* NOTREACHED */ case 's': socket = 1; break; #if defined(CLIENT_read) || defined(CLIENT_list) case 'p': prefix = 1; break; #elif defined(CLIENT_rm) case 't': tidy = 1; break; #elif defined(CLIENT_chmod) case 'u': upto = 1; break; case 'r': recurse = 1; break; #endif } } if (optind == argc) { usage(argv[0]); /* NOTREACHED */ } #if defined(CLIENT_write) if ((argc - optind) % 2 == 1) { usage(argv[0]); /* NOTREACHED */ } #endif #if defined(CLIENT_read) transaction = (argc - optind) > 1; #elif defined(CLIENT_write) transaction = (argc - optind) > 2; #else transaction = 1; #endif xsh = socket ? xs_daemon_open() : xs_domain_open(); if (xsh == NULL) err(1, socket ? "xs_daemon_open" : "xs_domain_open"); again: if (transaction) { xth = xs_transaction_start(xsh); if (xth == XBT_NULL) errx(1, "couldn't start transaction"); } ret = perform(optind, argc, argv, xsh, xth, prefix, tidy, upto, recurse); 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; }