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