2 * augrp.c: utility for printing and reading files as parsed by Augeas
4 * Copyright (C) 2007-2016 David Lutterkort
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Author: David Lutterkort <dlutter@redhat.com>
34 #define EXIT_TROUBLE 2
36 #define cleanup(_x) __attribute__((__cleanup__(_x)))
39 bool print_all = false;
40 bool print_only_values = false;
41 bool print_exact = false;
43 static void freep(void *p) {
47 static void aug_closep(struct augeas **p) {
51 __attribute__((noreturn))
52 static void usage(void) {
53 fprintf(stderr, "Usage: %s [OPTIONS] FILE\n", progname);
55 "Print the contents of a file as parsed by augeas.\n\n"
57 " -l, --lens LENS use LENS to transform the file\n"
58 " -L, --print-lens print the lens that will be used for a file an exit\n"
59 " -a, --all print all nodes, even ones without a value\n"
60 " -m, --match EXPR start printing where nodes match EXPR\n"
61 " -e, --exact print only exact matches instead of the entire tree\n"
62 " starting at a match\n"
63 " -o, --only-value print only the values of tree nodes, but no path\n"
64 " -q, --quiet do not print anything. Exit with zero status if a\n"
66 " -r, --root ROOT use ROOT as the root of the filesystem\n"
67 " -I, --include DIR search DIR for modules; can be given mutiple times\n"
68 " -S, --nostdinc do not search the builtin default directories\n"
71 " Print how augeas sees /etc/exports:\n"
72 " augmatch /etc/exports\n\n"
73 " Show only the entry for a specific mount:\n"
74 " augmatch -m 'dir[\"/home\"]' /etc/exports\n\n"
75 " Show all the clients to which we are exporting /home:\n"
76 " augmatch -eom 'dir[\"/home\"]/client' /etc/exports\n\n");
80 /* Exit with a failure if COND is true. Use FORMAT and the remaining
81 * arguments like printf to print an error message on stderr before
83 static void die(bool cond, const char *format, ...) {
85 fputs("error: ", stderr);
87 va_start(args, format);
88 vfprintf(stderr, format, args);
94 static void oom_when(bool cond) {
95 die(cond, "out of memory.\n");
98 /* Format a string with vasprintf and return it. The caller is responsible
99 * for freeing the result. */
100 static char *format(const char *format, ...) {
105 va_start(args, format);
106 r = vasprintf(&result, format, args);
112 /* Check for an Augeas error. If there is one, print it and all its detail
113 * and exit with failure. If there is no error, do nothing */
114 static void check_error(struct augeas *aug) {
115 die(aug == NULL, "could not initialize augeas\n");
116 oom_when(aug_error(aug) == AUG_ENOMEM);
117 if (aug_error(aug) != AUG_NOERROR) {
118 fprintf(stderr, "error: %s\n", aug_error_message(aug));
119 const char *msg = aug_error_minor_message(aug);
121 fprintf(stderr, "%s\n", msg);
123 msg = aug_error_details(aug);
125 fprintf(stderr, "%s\n", msg);
131 /* Check for an error trying to load FILE (e.g., a parse error) If there
132 * was one, print details and exit with failure. If there was none, do
134 static void check_load_error(struct augeas *aug, const char *file) {
135 char *info = format("/augeas/files%s", file);
136 const char *msg, *line, *col;
138 aug_defvar(aug, "info", info);
140 die(aug_ns_count(aug, "info") == 0, "file %s does not exist\n", file);
142 aug_defvar(aug, "error", "$info/error");
143 if (aug_ns_count(aug, "error") == 0)
146 aug_get(aug, "$error", &msg);
147 aug_get(aug, "$error/line", &line);
148 aug_get(aug, "$error/char", &col);
150 if (streqv(msg, "parse_failed")) {
151 msg = "parsing failed";
152 } else if (streqv(msg, "read_failed")) {
153 aug_get(aug, "$error/message", &msg);
156 if ((line != NULL) && (col != NULL)) {
157 fprintf(stderr, "error reading %s: %s on line %s, column %s\n",
158 file, msg, line, col);
160 fprintf(stderr, "error reading %s: %s\n", file, msg);
165 /* We keep track of where we are in the tree when we are printing it by
166 * assigning to augeas variables and using one struct node for each level
167 * in the tree. To keep things simple, we just preallocate a lot of them
168 * (up to max_nodes many). If we ever have a tree deeper than this, we are
169 * in trouble and will simply abort the program. */
170 static const size_t max_nodes = 256;
173 char *var; /* The variable where we store the nodes for this level */
174 const char *label; /* The label, index, and value of the current node */
175 int index; /* at the level that this struct node is for */
179 /* Print information about NODES[LEVEL] by printing the path to it going
180 * from NODES[0] to NODES[LEVEL]. PREFIX is the path from the start of the
181 * file to the current match (if the user specified --match) or the empty
182 * string (if we are printing the entire file. */
183 static void print_one(int level, const char *prefix, struct node *nodes) {
184 if (nodes[level].value == NULL && ! print_all)
187 if (print_only_values && nodes[level].value != NULL) {
188 printf("%s\n", nodes[level].value);
194 printf("%s/", prefix);
196 printf("%s", prefix);
199 for (int i=1; i <= level; i++) {
200 if (nodes[i].index > 0) {
201 printf("%s[%d]", nodes[i].label, nodes[i].index);
203 printf("%s", nodes[i].label);
210 if (nodes[level].value) {
211 printf(" = %s\n", nodes[level].value);
217 /* Recursively print the tree starting at NODES[LEVEL] */
218 static void print_tree(struct augeas *aug, int level,
219 const char *prefix, struct node *nodes) {
220 die(level + 1 >= max_nodes,
221 "tree has more than %d levels, which is more than we can handle\n",
224 struct node *cur = nodes + level;
225 struct node *next = cur + 1;
227 int count = aug_ns_count(aug, cur->var);
228 for (int i=0; i < count; i++) {
229 cleanup(freep) char *pattern = NULL;
231 aug_ns_label(aug, cur->var, i, &(cur->label), &(cur->index));
232 aug_ns_value(aug, cur->var, i, &(cur->value));
233 print_one(level, prefix, nodes);
236 pattern = format("$%s[%d]/*", cur->var, i+1);
237 aug_defvar(aug, next->var, pattern);
239 print_tree(aug, level+1, prefix, nodes);
244 /* Print the tree for file PATH (which must already start with /files), but
245 * only the nodes matching MATCH.
247 * Return EXIT_SUCCESS if there was at least one match, and EXIT_FAILURE
250 static int print(struct augeas *aug, const char *path, const char *match) {
251 static const char *const match_var = "match";
253 struct node *nodes = NULL;
255 nodes = calloc(max_nodes, sizeof(struct node));
256 oom_when(nodes == NULL);
258 for (int i=0; i < max_nodes; i++) {
259 nodes[i].var = format("var%d", i);
262 /* Set $match to the nodes matching the user's match expression */
263 aug_defvar(aug, match_var, match);
266 /* Go through the matches in MATCH_VAR one by one. We need to do it
267 * this way, since the prefix we need to print for each entry in
268 * MATCH_VAR is different for each entry. */
269 int count = aug_ns_count(aug, match_var);
270 for (int i=0; i < count; i++) {
271 cleanup(freep) char *prefix = NULL;
272 aug_ns_path(aug, match_var, i, &prefix);
273 aug_defvar(aug, nodes[0].var, prefix);
274 print_tree(aug, 0, prefix + strlen(path) + 1, nodes);
276 for (int i=0; i < max_nodes; i++) {
281 return (count == 0) ? EXIT_FAILURE : EXIT_SUCCESS;
284 /* Look at the filename and try to guess based on the extension. The
285 * builtin filters for lenses do not do that, as that would force augtool
286 * to scan everything on start
288 static char *guess_lens_name(const char *file) {
289 const char *ext = strrchr(file, '.');
294 if (streqv(ext, ".json")) {
295 return strdup("Json.lns");
296 } else if (streqv(ext, ".xml")) {
297 return strdup("Xml.lns");
303 int main(int argc, char **argv) {
305 cleanup(aug_closep) struct augeas *aug;
306 cleanup(freep) char *loadpath = NULL;
307 size_t loadpath_len = 0;
308 cleanup(freep) char *root = NULL;
309 cleanup(freep) char *lens = NULL;
310 cleanup(freep) char *matches = NULL;
311 size_t matches_len = 0;
312 const char *match = "*";
313 bool print_lens = false;
315 int result = EXIT_SUCCESS;
317 struct option options[] = {
318 { "help", 0, 0, 'h' },
319 { "include", 1, 0, 'I' },
320 { "lens", 1, 0, 'l' },
321 { "all", 0, 0, 'a' },
322 { "index", 0, 0, 'i' },
323 { "match", 1, 0, 'm' },
324 { "only-value", 0, 0, 'o' },
325 { "nostdinc", 0, 0, 'S' },
326 { "root", 1, 0, 'r' },
327 { "print-lens", 0, 0, 'L' },
328 { "exact", 0, 0, 'e' },
329 { "quiet", 0, 0, 'q' },
332 unsigned int flags = AUG_NO_LOAD|AUG_NO_ERR_CLOSE;
333 progname = basename(argv[0]);
335 setlocale(LC_ALL, "");
336 while ((opt = getopt_long(argc, argv, "ahI:l:m:oSr:eLq", options, NULL)) != -1) {
339 argz_add(&loadpath, &loadpath_len, optarg);
342 lens = strdup(optarg);
354 // If optarg is a numeric string like '1', it is not a legal
355 // part of a path by itself, and so we need to prefix it with
357 die(optarg[0] == '/',
358 "matches can only be relative paths, not %s\n", optarg);
359 argz_add(&matches, &matches_len, format("child::%s", optarg));
362 print_only_values = true;
365 root = strdup(optarg);
368 flags |= AUG_NO_STDINC;
377 fprintf(stderr, "Try '%s --help' for more information.\n",
384 if (optind >= argc) {
385 fprintf(stderr, "Expected an input file\n");
386 fprintf(stderr, "Try '%s --help' for more information.\n",
391 const char *file = argv[optind];
393 argz_stringify(loadpath, loadpath_len, ':');
396 lens = guess_lens_name(file);
400 /* We know which lens we want, we do not need to load all of them */
401 flags |= AUG_NO_MODL_AUTOLOAD;
404 aug = aug_init(root, loadpath, flags|AUG_NO_ERR_CLOSE);
408 aug_load_file(aug, file);
410 aug_transform(aug, lens, file, false);
415 /* The user just wants the lens name */
417 char *info = format("/augeas/files%s", file);
418 const char *lens_name;
419 aug_defvar(aug, "info", info);
420 die(aug_ns_count(aug, "info") == 0,
421 "file %s does not exist\n", file);
422 aug_get(aug, "$info/lens", &lens_name);
423 /* We are being extra careful here - the check_error above would
424 have already aborted the program if we could not determine a
425 lens; dieing here indicates some sort of bug */
426 die(lens_name == NULL, "could not find lens for %s\n",
428 if (lens_name[0] == '@')
430 printf("%s\n", lens_name);
434 check_load_error(aug, file);
436 char *path = format("/files%s", file);
437 aug_set(aug, "/augeas/context", path);
440 if (matches_len > 0) {
441 argz_stringify(matches, matches_len, '|');
446 int n = aug_match(aug, match, NULL);
448 result = (n == 0) ? EXIT_FAILURE : EXIT_SUCCESS;
450 result = print(aug, path, match);
459 * indent-tabs-mode: nil