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