1 /*
2 * This file is subject to the terms and conditions of the GNU General
3 * Public License. See the file "COPYING" in the main directory of
4 * this archive for more details.
5 *
6 * Copyright (C) 2005 by Christian Limpach
7 * Copyright (C) 2005 XenSource Ltd.
8 *
9 */
10
11 #include <err.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <getopt.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <termios.h>
20 #include <unistd.h>
21 #include <xenstore.h>
22
23 #include <sys/ioctl.h>
24
25 #define PATH_SEP '/'
26 #define MAX_PATH_LEN 256
27
28
29 enum mode {
30 MODE_unknown,
31 MODE_chmod,
32 MODE_exists,
33 MODE_list,
34 MODE_ls,
35 MODE_read,
36 MODE_rm,
37 MODE_write,
38 MODE_watch,
39 };
40
41 static char *output_buf = NULL;
42 static int output_pos = 0;
43 static struct expanding_buffer ebuf;
44
45 static int output_size = 0;
46
47 static void
output(const char * fmt,...)48 output(const char *fmt, ...) {
49 va_list ap;
50 int len;
51 char buf[1];
52
53 va_start(ap, fmt);
54 len = vsnprintf(buf, 1, fmt, ap);
55 if (len < 0)
56 err(1, "output");
57 va_end(ap);
58 if (len + 1 + output_pos > output_size) {
59 output_size += len + 1024;
60 output_buf = realloc(output_buf, output_size);
61 if (output_buf == NULL)
62 err(1, "malloc");
63 }
64 va_start(ap, fmt);
65 if (vsnprintf(&output_buf[output_pos], len + 1, fmt, ap) != len)
66 err(1, "output");
67 va_end(ap);
68 output_pos += len;
69 }
70
71 static void
usage(enum mode mode,int incl_mode,const char * progname)72 usage(enum mode mode, int incl_mode, const char *progname)
73 {
74 const char *mstr = NULL;
75
76 switch (mode) {
77 case MODE_unknown:
78 errx(1, "Usage: %s <mode> [-h] [...]", progname);
79 case MODE_read:
80 mstr = incl_mode ? "read " : "";
81 errx(1, "Usage: %s %s[-h] [-p] [-s] key [...]", progname, mstr);
82 case MODE_write:
83 mstr = incl_mode ? "write " : "";
84 errx(1, "Usage: %s %s[-h] [-s] key value [...]", progname, mstr);
85 case MODE_rm:
86 mstr = incl_mode ? "rm " : "";
87 errx(1, "Usage: %s %s[-h] [-s] [-t] key [...]", progname, mstr);
88 case MODE_exists:
89 mstr = incl_mode ? "exists " : "";
90 /* fallthrough */
91 case MODE_list:
92 mstr = mstr ? : incl_mode ? "list " : "";
93 errx(1, "Usage: %s %s[-h] [-p] [-s] key [...]", progname, mstr);
94 case MODE_ls:
95 mstr = mstr ? : incl_mode ? "ls " : "";
96 errx(1, "Usage: %s %s[-h] [-f] [-p] [-s] [path]", progname, mstr);
97 case MODE_chmod:
98 mstr = incl_mode ? "chmod " : "";
99 errx(1, "Usage: %s %s[-h] [-u] [-r] [-s] key <mode [modes...]>", progname, mstr);
100 case MODE_watch:
101 mstr = incl_mode ? "watch " : "";
102 errx(1, "Usage: %s %s[-h] [-n NR] key", progname, mstr);
103 }
104 }
105
106
107 static int
do_rm(char * path,struct xs_handle * xsh,xs_transaction_t xth)108 do_rm(char *path, struct xs_handle *xsh, xs_transaction_t xth)
109 {
110 if (xs_rm(xsh, xth, path)) {
111 return 0;
112 }
113 else {
114 warnx("could not remove path %s", path);
115 return 1;
116 }
117 }
118
119 #define STRING_MAX XENSTORE_ABS_PATH_MAX+1024
120 static int max_width = 80;
121 static int desired_width = 60;
122 static int show_whole_path = 0;
123
124 #define TAG " = \"...\""
125 #define TAG_LEN strlen(TAG)
126
127 #define MIN(a, b) (((a) < (b))? (a) : (b))
128
do_ls(struct xs_handle * h,char * path,int cur_depth,int show_perms)129 static void do_ls(struct xs_handle *h, char *path, int cur_depth, int show_perms)
130 {
131 char **e;
132 char *newpath, *val;
133 int newpath_len;
134 int i;
135 unsigned int num, len;
136
137 newpath = malloc(STRING_MAX);
138 if (!newpath)
139 err(1, "malloc in do_ls");
140
141 e = xs_directory(h, XBT_NULL, path, &num);
142 if (e == NULL)
143 err(1, "xs_directory (%s)", path);
144
145 for (i = 0; i<num; i++) {
146 char buf[MAX_STRLEN(unsigned int)+1];
147 struct xs_permissions *perms;
148 unsigned int nperms;
149 int linewid;
150
151 /* Compose fullpath */
152 newpath_len = snprintf(newpath, STRING_MAX, "%s%s%s", path,
153 path[strlen(path)-1] == '/' ? "" : "/",
154 e[i]);
155
156 /* Print indent and path basename */
157 linewid = 0;
158 if (show_whole_path) {
159 fputs(newpath, stdout);
160 } else {
161 for (; linewid<cur_depth; linewid++) {
162 putchar(' ');
163 }
164 linewid += printf("%.*s",
165 (int) (max_width - TAG_LEN - linewid), e[i]);
166 }
167
168 /* Fetch value */
169 if ( newpath_len < STRING_MAX ) {
170 val = xs_read(h, XBT_NULL, newpath, &len);
171 }
172 else {
173 /* Path was truncated and thus invalid */
174 val = NULL;
175 len = 0;
176 }
177
178 /* Print value */
179 if (val == NULL) {
180 printf(":\n");
181 }
182 else {
183 if (max_width < (linewid + len + TAG_LEN)) {
184 printf(" = \"%.*s\\...\"",
185 (int)(max_width - TAG_LEN - linewid),
186 sanitise_value(&ebuf, val, len));
187 }
188 else {
189 linewid += printf(" = \"%s\"",
190 sanitise_value(&ebuf, val, len));
191 if (show_perms) {
192 putchar(' ');
193 for (linewid++;
194 linewid < MIN(desired_width, max_width);
195 linewid++)
196 putchar((linewid & 1)? '.' : ' ');
197 }
198 }
199 }
200 free(val);
201
202 if (show_perms) {
203 perms = xs_get_permissions(h, XBT_NULL, newpath, &nperms);
204 if (perms == NULL) {
205 warn("\ncould not access permissions for %s", e[i]);
206 }
207 else {
208 int i;
209 fputs(" (", stdout);
210 for (i = 0; i < nperms; i++) {
211 if (i)
212 putchar(',');
213 xs_perm_to_string(perms+i, buf, sizeof(buf));
214 fputs(buf, stdout);
215 }
216 putchar(')');
217 }
218 }
219
220 putchar('\n');
221
222 do_ls(h, newpath, cur_depth+1, show_perms);
223 }
224 free(e);
225 free(newpath);
226 }
227
228 static void
do_chmod(char * path,struct xs_permissions * perms,int nperms,int upto,int recurse,struct xs_handle * xsh,xs_transaction_t xth)229 do_chmod(char *path, struct xs_permissions *perms, int nperms, int upto,
230 int recurse, struct xs_handle *xsh, xs_transaction_t xth)
231 {
232 int ret;
233
234 if (!path[0])
235 return;
236
237 ret = xs_set_permissions(xsh, xth, path, perms, nperms);
238 if (!ret)
239 err(1, "Error occurred setting permissions on '%s'", path);
240
241 if (upto) {
242 /* apply same permissions to all parent entries: */
243 char *path_sep_ptr = strrchr(path, PATH_SEP);
244 if (!path_sep_ptr)
245 errx(1, "Unable to locate path separator '%c' in '%s'",
246 PATH_SEP, path);
247
248 *path_sep_ptr = '\0'; /* truncate path */
249
250 do_chmod(path, perms, nperms, 1, 0, xsh, xth);
251
252 *path_sep_ptr = PATH_SEP;
253 }
254
255 if (recurse) {
256 char buf[MAX_PATH_LEN];
257
258 /* apply same permissions to all child entries: */
259 unsigned int xsval_n;
260 char **xsval = xs_directory(xsh, xth, path, &xsval_n);
261
262 if (xsval) {
263 int i;
264 for (i = 0; i < xsval_n; i++) {
265 snprintf(buf, MAX_PATH_LEN, "%s/%s", path, xsval[i]);
266
267 do_chmod(buf, perms, nperms, 0, 1, xsh, xth);
268 }
269
270 free(xsval);
271 }
272 }
273 }
274
275 static void
do_watch(struct xs_handle * xsh,int max_events)276 do_watch(struct xs_handle *xsh, int max_events)
277 {
278 int count = 0;
279 char **vec = NULL;
280
281 for ( count = 0; max_events == -1 || count < max_events; count++ ) {
282 unsigned int num;
283
284 vec = xs_read_watch(xsh, &num);
285 if (vec == NULL)
286 continue;
287
288 printf("%s\n", vec[XS_WATCH_PATH]);
289 fflush(stdout);
290 free(vec);
291 }
292 }
293
294 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)295 perform(enum mode mode, int optind, int argc, char **argv, struct xs_handle *xsh,
296 xs_transaction_t xth, int prefix, int tidy, int upto, int recurse, int nr_watches)
297 {
298 switch (mode) {
299 case MODE_ls:
300 if (optind == argc)
301 {
302 optind=0;
303 argc=1;
304 argv[0] = "/";
305 }
306 break;
307 default:
308 break;
309 }
310
311 while (optind < argc) {
312 switch (mode) {
313 case MODE_unknown:
314 /* CANNOT BE REACHED */
315 errx(1, "invalid mode %d", mode);
316 case MODE_read: {
317 unsigned len;
318 char *val = xs_read(xsh, xth, argv[optind], &len);
319 if (val == NULL) {
320 warnx("couldn't read path %s", argv[optind]);
321 return 1;
322 }
323 if (prefix)
324 output("%s: ", argv[optind]);
325 output("%s\n", sanitise_value(&ebuf, val, len));
326 free(val);
327 optind++;
328 break;
329 }
330 case MODE_write: {
331 char *val_spec = argv[optind + 1];
332 unsigned len;
333 expanding_buffer_ensure(&ebuf, strlen(val_spec)+1);
334 unsanitise_value(ebuf.buf, &len, val_spec);
335 if (!xs_write(xsh, xth, argv[optind], ebuf.buf, len)) {
336 warnx("could not write path %s", argv[optind]);
337 return 1;
338 }
339 optind += 2;
340 } break;
341 case MODE_rm: {
342 /* Remove the specified path. If the tidy flag is set, then also
343 remove any containing directories that are both empty and have no
344 value attached, and repeat, recursing all the way up to the root if
345 necessary.
346 */
347
348 char *slash, *path = argv[optind];
349
350 if (tidy) {
351 /* Copy path, because we can't modify argv because we will need it
352 again if xs_transaction_end gives us EAGAIN. */
353 char *p = malloc(strlen(path) + 1);
354 strcpy(p, path);
355 path = p;
356
357 again:
358 if (do_rm(path, xsh, xth)) {
359 return 1;
360 }
361
362 slash = strrchr(p, '/');
363 if (slash) {
364 char *val;
365 unsigned len;
366 *slash = '\0';
367 val = xs_read(xsh, xth, p, &len);
368 if (val && len == 0) {
369 unsigned int num;
370 char ** list = xs_directory(xsh, xth, p, &num);
371
372 if (list) {
373 free(list);
374 if (num == 0){
375 free(val);
376 goto again;
377 }
378 }
379 }
380 free(val);
381 }
382
383 free(path);
384 }
385 else {
386 if (do_rm(path, xsh, xth)) {
387 return 1;
388 }
389 }
390
391 optind++;
392 break;
393 }
394 case MODE_exists: {
395 char *val = xs_read(xsh, xth, argv[optind], NULL);
396 if (val == NULL) {
397 return 1;
398 }
399 free(val);
400 optind++;
401 break;
402 }
403 case MODE_list: {
404 unsigned int i, num;
405 char **list = xs_directory(xsh, xth, argv[optind], &num);
406 if (list == NULL) {
407 warnx("could not list path %s", argv[optind]);
408 return 1;
409 }
410 for (i = 0; i < num; i++) {
411 if (prefix)
412 output("%s/", argv[optind]);
413 output("%s\n", list[i]);
414 }
415 free(list);
416 optind++;
417 break;
418 }
419 case MODE_ls: {
420 do_ls(xsh, argv[optind], 0, prefix);
421 optind++;
422 break;
423 }
424 case MODE_chmod: {
425 /* save path pointer: */
426 char *path = argv[optind++];
427 int nperms = argc - optind;
428 struct xs_permissions perms[nperms];
429 int i;
430 for (i = 0; argv[optind]; optind++, i++)
431 {
432 perms[i].id = atoi(argv[optind]+1);
433
434 switch (argv[optind][0])
435 {
436 case 'n':
437 perms[i].perms = XS_PERM_NONE;
438 break;
439 case 'r':
440 perms[i].perms = XS_PERM_READ;
441 break;
442 case 'w':
443 perms[i].perms = XS_PERM_WRITE;
444 break;
445 case 'b':
446 perms[i].perms = XS_PERM_READ | XS_PERM_WRITE;
447 break;
448 default:
449 errx(1, "Invalid permission specification: '%c'",
450 argv[optind][0]);
451 }
452 }
453
454 do_chmod(path, perms, nperms, upto, recurse, xsh, xth);
455 break;
456 }
457 case MODE_watch: {
458 for (; argv[optind]; optind++) {
459 const char *w = argv[optind];
460
461 if (!xs_watch(xsh, w, w))
462 errx(1, "Unable to add watch on %s\n", w);
463 }
464 do_watch(xsh, nr_watches);
465 }
466 }
467 }
468
469 return 0;
470 }
471
lookup_mode(const char * m)472 static enum mode lookup_mode(const char *m)
473 {
474 if (strcmp(m, "read") == 0)
475 return MODE_read;
476 else if (strcmp(m, "chmod") == 0)
477 return MODE_chmod;
478 else if (strcmp(m, "exists") == 0)
479 return MODE_exists;
480 else if (strcmp(m, "list") == 0)
481 return MODE_list;
482 else if (strcmp(m, "ls") == 0)
483 return MODE_ls;
484 else if (strcmp(m, "rm") == 0)
485 return MODE_rm;
486 else if (strcmp(m, "write") == 0)
487 return MODE_write;
488 else if (strcmp(m, "read") == 0)
489 return MODE_read;
490 else if (strcmp(m, "watch") == 0)
491 return MODE_watch;
492
493 errx(1, "unknown mode %s\n", m);
494 return 0;
495 }
496
497 int
main(int argc,char ** argv)498 main(int argc, char **argv)
499 {
500 struct xs_handle *xsh;
501 xs_transaction_t xth = XBT_NULL;
502 int ret = 0, socket = 0;
503 int prefix = 0;
504 int tidy = 0;
505 int upto = 0;
506 int recurse = 0;
507 int nr_watches = -1;
508 int transaction;
509 struct winsize ws;
510 enum mode mode;
511
512 const char *_command = strrchr(argv[0], '/');
513 const char *command = _command ? &_command[1] : argv[0];
514 int switch_argv = -1; /* which element of argv did we switch on */
515
516 if (strncmp(command, "xenstore-", strlen("xenstore-")) == 0)
517 {
518 switch_argv = 0;
519 command = command + strlen("xenstore-");
520 }
521 else if (argc < 2)
522 usage(MODE_unknown, 0, argv[0]);
523 else
524 {
525 command = argv[1];
526 switch_argv = 1;
527 }
528
529 mode = lookup_mode(command);
530
531 while (1) {
532 int c, index = 0;
533 static struct option long_options[] = {
534 {"help", 0, 0, 'h'},
535 {"flat", 0, 0, 'f'}, /* MODE_ls */
536 {"socket", 0, 0, 's'},
537 {"prefix", 0, 0, 'p'}, /* MODE_read || MODE_list || MODE_ls */
538 {"tidy", 0, 0, 't'}, /* MODE_rm */
539 {"upto", 0, 0, 'u'}, /* MODE_chmod */
540 {"recurse", 0, 0, 'r'}, /* MODE_chmod */
541 {"number", 1, 0, 'n'}, /* MODE_watch */
542 {0, 0, 0, 0}
543 };
544
545 c = getopt_long(argc - switch_argv, argv + switch_argv, "hfspturn:",
546 long_options, &index);
547 if (c == -1)
548 break;
549
550 switch (c) {
551 case 'h':
552 usage(mode, switch_argv, argv[0]);
553 /* NOTREACHED */
554 case 'f':
555 if ( mode == MODE_ls ) {
556 max_width = INT_MAX/2;
557 desired_width = 0;
558 show_whole_path = 1;
559 } else {
560 usage(mode, switch_argv, argv[0]);
561 }
562 break;
563 case 's':
564 socket = 1;
565 break;
566 case 'p':
567 if ( mode == MODE_read || mode == MODE_list || mode == MODE_ls )
568 prefix = 1;
569 else
570 usage(mode, switch_argv, argv[0]);
571 break;
572 case 't':
573 if ( mode == MODE_rm )
574 tidy = 1;
575 else
576 usage(mode, switch_argv, argv[0]);
577 break;
578 case 'u':
579 if ( mode == MODE_chmod )
580 upto = 1;
581 else
582 usage(mode, switch_argv, argv[0]);
583 break;
584 case 'r':
585 if ( mode == MODE_chmod )
586 recurse = 1;
587 else
588 usage(mode, switch_argv, argv[0]);
589 break;
590 case 'n':
591 if ( mode == MODE_watch )
592 nr_watches = atoi(optarg);
593 else
594 usage(mode, switch_argv, argv[0]);
595 break;
596 }
597 }
598
599 switch (mode) {
600 case MODE_ls:
601 break;
602 case MODE_write:
603 if ((argc - switch_argv - optind) % 2 == 1) {
604 usage(mode, switch_argv, argv[0]);
605 /* NOTREACHED */
606 }
607 /* DROP-THRU */
608 default:
609 if (optind == argc - switch_argv) {
610 usage(mode, switch_argv, argv[0]);
611 /* NOTREACHED */
612 }
613 }
614
615 switch (mode) {
616 case MODE_read:
617 transaction = (argc - switch_argv - optind) > 1;
618 break;
619 case MODE_write:
620 transaction = (argc - switch_argv - optind) > 2;
621 break;
622 case MODE_ls:
623 case MODE_watch:
624 transaction = 0;
625 break;
626 default:
627 transaction = 1;
628 break;
629 }
630
631 if ( mode == MODE_ls )
632 {
633 memset(&ws, 0, sizeof(ws));
634 ret = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
635 if (!ret)
636 max_width = ws.ws_col - 2;
637 }
638
639 xsh = xs_open(socket ? XS_OPEN_SOCKETONLY : 0);
640 if (xsh == NULL) err(1, "xs_open");
641
642 again:
643 if (transaction) {
644 xth = xs_transaction_start(xsh);
645 if (xth == XBT_NULL)
646 errx(1, "couldn't start transaction");
647 }
648
649 ret = perform(mode, optind, argc - switch_argv, argv + switch_argv, xsh, xth, prefix, tidy, upto, recurse, nr_watches);
650
651 if (transaction && !xs_transaction_end(xsh, xth, ret)) {
652 if (ret == 0 && errno == EAGAIN) {
653 output_pos = 0;
654 goto again;
655 }
656 errx(1, "couldn't end transaction");
657 }
658
659 if (output_pos)
660 printf("%s", output_buf);
661
662 free(output_buf);
663 free(ebuf.buf);
664
665 if (xsh)
666 xs_close(xsh);
667
668 return ret;
669 }
670