xref: /dragonfly/contrib/lvm2/dist/lib/config/config.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /*	$NetBSD: config.c,v 1.1.1.2 2009/12/02 00:26:28 haad Exp $	*/
2*86d7f5d3SJohn Marino 
3*86d7f5d3SJohn Marino /*
4*86d7f5d3SJohn Marino  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5*86d7f5d3SJohn Marino  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * This file is part of LVM2.
8*86d7f5d3SJohn Marino  *
9*86d7f5d3SJohn Marino  * This copyrighted material is made available to anyone wishing to use,
10*86d7f5d3SJohn Marino  * modify, copy, or redistribute it subject to the terms and conditions
11*86d7f5d3SJohn Marino  * of the GNU Lesser General Public License v.2.1.
12*86d7f5d3SJohn Marino  *
13*86d7f5d3SJohn Marino  * You should have received a copy of the GNU Lesser General Public License
14*86d7f5d3SJohn Marino  * along with this program; if not, write to the Free Software Foundation,
15*86d7f5d3SJohn Marino  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16*86d7f5d3SJohn Marino  */
17*86d7f5d3SJohn Marino 
18*86d7f5d3SJohn Marino #include "lib.h"
19*86d7f5d3SJohn Marino #include "config.h"
20*86d7f5d3SJohn Marino #include "crc.h"
21*86d7f5d3SJohn Marino #include "device.h"
22*86d7f5d3SJohn Marino #include "str_list.h"
23*86d7f5d3SJohn Marino #include "toolcontext.h"
24*86d7f5d3SJohn Marino #include "lvm-string.h"
25*86d7f5d3SJohn Marino #include "lvm-file.h"
26*86d7f5d3SJohn Marino 
27*86d7f5d3SJohn Marino #include <sys/stat.h>
28*86d7f5d3SJohn Marino #include <sys/mman.h>
29*86d7f5d3SJohn Marino #include <unistd.h>
30*86d7f5d3SJohn Marino #include <fcntl.h>
31*86d7f5d3SJohn Marino #include <ctype.h>
32*86d7f5d3SJohn Marino 
33*86d7f5d3SJohn Marino #define SECTION_B_CHAR '{'
34*86d7f5d3SJohn Marino #define SECTION_E_CHAR '}'
35*86d7f5d3SJohn Marino 
36*86d7f5d3SJohn Marino enum {
37*86d7f5d3SJohn Marino 	TOK_INT,
38*86d7f5d3SJohn Marino 	TOK_FLOAT,
39*86d7f5d3SJohn Marino 	TOK_STRING,		/* Single quotes */
40*86d7f5d3SJohn Marino 	TOK_STRING_ESCAPED,	/* Double quotes */
41*86d7f5d3SJohn Marino 	TOK_EQ,
42*86d7f5d3SJohn Marino 	TOK_SECTION_B,
43*86d7f5d3SJohn Marino 	TOK_SECTION_E,
44*86d7f5d3SJohn Marino 	TOK_ARRAY_B,
45*86d7f5d3SJohn Marino 	TOK_ARRAY_E,
46*86d7f5d3SJohn Marino 	TOK_IDENTIFIER,
47*86d7f5d3SJohn Marino 	TOK_COMMA,
48*86d7f5d3SJohn Marino 	TOK_EOF
49*86d7f5d3SJohn Marino };
50*86d7f5d3SJohn Marino 
51*86d7f5d3SJohn Marino struct parser {
52*86d7f5d3SJohn Marino 	const char *fb, *fe;		/* file limits */
53*86d7f5d3SJohn Marino 
54*86d7f5d3SJohn Marino 	int t;			/* token limits and type */
55*86d7f5d3SJohn Marino 	const char *tb, *te;
56*86d7f5d3SJohn Marino 
57*86d7f5d3SJohn Marino 	int fd;			/* descriptor for file being parsed */
58*86d7f5d3SJohn Marino 	int line;		/* line number we are on */
59*86d7f5d3SJohn Marino 
60*86d7f5d3SJohn Marino 	struct dm_pool *mem;
61*86d7f5d3SJohn Marino };
62*86d7f5d3SJohn Marino 
63*86d7f5d3SJohn Marino struct cs {
64*86d7f5d3SJohn Marino 	struct config_tree cft;
65*86d7f5d3SJohn Marino 	struct dm_pool *mem;
66*86d7f5d3SJohn Marino 	time_t timestamp;
67*86d7f5d3SJohn Marino 	char *filename;
68*86d7f5d3SJohn Marino 	int exists;
69*86d7f5d3SJohn Marino 	int keep_open;
70*86d7f5d3SJohn Marino 	struct device *dev;
71*86d7f5d3SJohn Marino };
72*86d7f5d3SJohn Marino 
73*86d7f5d3SJohn Marino struct output_line {
74*86d7f5d3SJohn Marino 	FILE *fp;
75*86d7f5d3SJohn Marino 	struct dm_pool *mem;
76*86d7f5d3SJohn Marino 	putline_fn putline;
77*86d7f5d3SJohn Marino 	void *putline_baton;
78*86d7f5d3SJohn Marino };
79*86d7f5d3SJohn Marino 
80*86d7f5d3SJohn Marino static void _get_token(struct parser *p, int tok_prev);
81*86d7f5d3SJohn Marino static void _eat_space(struct parser *p);
82*86d7f5d3SJohn Marino static struct config_node *_file(struct parser *p);
83*86d7f5d3SJohn Marino static struct config_node *_section(struct parser *p);
84*86d7f5d3SJohn Marino static struct config_value *_value(struct parser *p);
85*86d7f5d3SJohn Marino static struct config_value *_type(struct parser *p);
86*86d7f5d3SJohn Marino static int _match_aux(struct parser *p, int t);
87*86d7f5d3SJohn Marino static struct config_value *_create_value(struct dm_pool *mem);
88*86d7f5d3SJohn Marino static struct config_node *_create_node(struct dm_pool *mem);
89*86d7f5d3SJohn Marino static char *_dup_tok(struct parser *p);
90*86d7f5d3SJohn Marino 
91*86d7f5d3SJohn Marino static const int sep = '/';
92*86d7f5d3SJohn Marino 
93*86d7f5d3SJohn Marino #define MAX_INDENT 32
94*86d7f5d3SJohn Marino 
95*86d7f5d3SJohn Marino #define match(t) do {\
96*86d7f5d3SJohn Marino    if (!_match_aux(p, (t))) {\
97*86d7f5d3SJohn Marino 	log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
98*86d7f5d3SJohn Marino 		  p->tb - p->fb + 1, p->line); \
99*86d7f5d3SJohn Marino       return 0;\
100*86d7f5d3SJohn Marino    } \
101*86d7f5d3SJohn Marino } while(0);
102*86d7f5d3SJohn Marino 
_tok_match(const char * str,const char * b,const char * e)103*86d7f5d3SJohn Marino static int _tok_match(const char *str, const char *b, const char *e)
104*86d7f5d3SJohn Marino {
105*86d7f5d3SJohn Marino 	while (*str && (b != e)) {
106*86d7f5d3SJohn Marino 		if (*str++ != *b++)
107*86d7f5d3SJohn Marino 			return 0;
108*86d7f5d3SJohn Marino 	}
109*86d7f5d3SJohn Marino 
110*86d7f5d3SJohn Marino 	return !(*str || (b != e));
111*86d7f5d3SJohn Marino }
112*86d7f5d3SJohn Marino 
113*86d7f5d3SJohn Marino /*
114*86d7f5d3SJohn Marino  * public interface
115*86d7f5d3SJohn Marino  */
create_config_tree(const char * filename,int keep_open)116*86d7f5d3SJohn Marino struct config_tree *create_config_tree(const char *filename, int keep_open)
117*86d7f5d3SJohn Marino {
118*86d7f5d3SJohn Marino 	struct cs *c;
119*86d7f5d3SJohn Marino 	struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
120*86d7f5d3SJohn Marino 
121*86d7f5d3SJohn Marino 	if (!mem) {
122*86d7f5d3SJohn Marino 		log_error("Failed to allocate config pool.");
123*86d7f5d3SJohn Marino 		return 0;
124*86d7f5d3SJohn Marino 	}
125*86d7f5d3SJohn Marino 
126*86d7f5d3SJohn Marino 	if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
127*86d7f5d3SJohn Marino 		log_error("Failed to allocate config tree.");
128*86d7f5d3SJohn Marino 		dm_pool_destroy(mem);
129*86d7f5d3SJohn Marino 		return 0;
130*86d7f5d3SJohn Marino 	}
131*86d7f5d3SJohn Marino 
132*86d7f5d3SJohn Marino 	c->mem = mem;
133*86d7f5d3SJohn Marino 	c->cft.root = (struct config_node *) NULL;
134*86d7f5d3SJohn Marino 	c->timestamp = 0;
135*86d7f5d3SJohn Marino 	c->exists = 0;
136*86d7f5d3SJohn Marino 	c->keep_open = keep_open;
137*86d7f5d3SJohn Marino 	c->dev = 0;
138*86d7f5d3SJohn Marino 	if (filename)
139*86d7f5d3SJohn Marino 		c->filename = dm_pool_strdup(c->mem, filename);
140*86d7f5d3SJohn Marino 	return &c->cft;
141*86d7f5d3SJohn Marino }
142*86d7f5d3SJohn Marino 
destroy_config_tree(struct config_tree * cft)143*86d7f5d3SJohn Marino void destroy_config_tree(struct config_tree *cft)
144*86d7f5d3SJohn Marino {
145*86d7f5d3SJohn Marino 	struct cs *c = (struct cs *) cft;
146*86d7f5d3SJohn Marino 
147*86d7f5d3SJohn Marino 	if (c->dev)
148*86d7f5d3SJohn Marino 		dev_close(c->dev);
149*86d7f5d3SJohn Marino 
150*86d7f5d3SJohn Marino 	dm_pool_destroy(c->mem);
151*86d7f5d3SJohn Marino }
152*86d7f5d3SJohn Marino 
_parse_config_file(struct parser * p,struct config_tree * cft)153*86d7f5d3SJohn Marino static int _parse_config_file(struct parser *p, struct config_tree *cft)
154*86d7f5d3SJohn Marino {
155*86d7f5d3SJohn Marino 	p->tb = p->te = p->fb;
156*86d7f5d3SJohn Marino 	p->line = 1;
157*86d7f5d3SJohn Marino 	_get_token(p, TOK_SECTION_E);
158*86d7f5d3SJohn Marino 	if (!(cft->root = _file(p)))
159*86d7f5d3SJohn Marino 		return_0;
160*86d7f5d3SJohn Marino 
161*86d7f5d3SJohn Marino 	return 1;
162*86d7f5d3SJohn Marino }
163*86d7f5d3SJohn Marino 
create_config_tree_from_string(struct cmd_context * cmd __attribute ((unused)),const char * config_settings)164*86d7f5d3SJohn Marino struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute((unused)),
165*86d7f5d3SJohn Marino 						   const char *config_settings)
166*86d7f5d3SJohn Marino {
167*86d7f5d3SJohn Marino 	struct cs *c;
168*86d7f5d3SJohn Marino 	struct config_tree *cft;
169*86d7f5d3SJohn Marino 	struct parser *p;
170*86d7f5d3SJohn Marino 
171*86d7f5d3SJohn Marino 	if (!(cft = create_config_tree(NULL, 0)))
172*86d7f5d3SJohn Marino 		return_NULL;
173*86d7f5d3SJohn Marino 
174*86d7f5d3SJohn Marino 	c = (struct cs *) cft;
175*86d7f5d3SJohn Marino 	if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
176*86d7f5d3SJohn Marino 		log_error("Failed to allocate config tree parser.");
177*86d7f5d3SJohn Marino 		destroy_config_tree(cft);
178*86d7f5d3SJohn Marino 		return NULL;
179*86d7f5d3SJohn Marino 	}
180*86d7f5d3SJohn Marino 
181*86d7f5d3SJohn Marino 	p->mem = c->mem;
182*86d7f5d3SJohn Marino 	p->fb = config_settings;
183*86d7f5d3SJohn Marino 	p->fe = config_settings + strlen(config_settings);
184*86d7f5d3SJohn Marino 
185*86d7f5d3SJohn Marino 	if (!_parse_config_file(p, cft)) {
186*86d7f5d3SJohn Marino 		destroy_config_tree(cft);
187*86d7f5d3SJohn Marino 		return_NULL;
188*86d7f5d3SJohn Marino 	}
189*86d7f5d3SJohn Marino 
190*86d7f5d3SJohn Marino 	return cft;
191*86d7f5d3SJohn Marino }
192*86d7f5d3SJohn Marino 
override_config_tree_from_string(struct cmd_context * cmd,const char * config_settings)193*86d7f5d3SJohn Marino int override_config_tree_from_string(struct cmd_context *cmd,
194*86d7f5d3SJohn Marino 				     const char *config_settings)
195*86d7f5d3SJohn Marino {
196*86d7f5d3SJohn Marino 	if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) {
197*86d7f5d3SJohn Marino 		log_error("Failed to set overridden configuration entries.");
198*86d7f5d3SJohn Marino 		return 1;
199*86d7f5d3SJohn Marino 	}
200*86d7f5d3SJohn Marino 
201*86d7f5d3SJohn Marino 	return 0;
202*86d7f5d3SJohn Marino }
203*86d7f5d3SJohn Marino 
read_config_fd(struct config_tree * cft,struct device * dev,off_t offset,size_t size,off_t offset2,size_t size2,checksum_fn_t checksum_fn,uint32_t checksum)204*86d7f5d3SJohn Marino int read_config_fd(struct config_tree *cft, struct device *dev,
205*86d7f5d3SJohn Marino 		   off_t offset, size_t size, off_t offset2, size_t size2,
206*86d7f5d3SJohn Marino 		   checksum_fn_t checksum_fn, uint32_t checksum)
207*86d7f5d3SJohn Marino {
208*86d7f5d3SJohn Marino 	struct cs *c = (struct cs *) cft;
209*86d7f5d3SJohn Marino 	struct parser *p;
210*86d7f5d3SJohn Marino 	int r = 0;
211*86d7f5d3SJohn Marino 	int use_mmap = 1;
212*86d7f5d3SJohn Marino 	off_t mmap_offset = 0;
213*86d7f5d3SJohn Marino 	char *buf = NULL;
214*86d7f5d3SJohn Marino 
215*86d7f5d3SJohn Marino 	if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
216*86d7f5d3SJohn Marino 		return_0;
217*86d7f5d3SJohn Marino 	p->mem = c->mem;
218*86d7f5d3SJohn Marino 
219*86d7f5d3SJohn Marino 	/* Only use mmap with regular files */
220*86d7f5d3SJohn Marino 	if (!(dev->flags & DEV_REGULAR) || size2)
221*86d7f5d3SJohn Marino 		use_mmap = 0;
222*86d7f5d3SJohn Marino 
223*86d7f5d3SJohn Marino 	if (use_mmap) {
224*86d7f5d3SJohn Marino 		mmap_offset = offset % lvm_getpagesize();
225*86d7f5d3SJohn Marino 		/* memory map the file */
226*86d7f5d3SJohn Marino 		p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
227*86d7f5d3SJohn Marino 			     MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
228*86d7f5d3SJohn Marino 		if (p->fb == (caddr_t) (-1)) {
229*86d7f5d3SJohn Marino 			log_sys_error("mmap", dev_name(dev));
230*86d7f5d3SJohn Marino 			goto out;
231*86d7f5d3SJohn Marino 		}
232*86d7f5d3SJohn Marino 		p->fb = p->fb + mmap_offset;
233*86d7f5d3SJohn Marino 	} else {
234*86d7f5d3SJohn Marino 		if (!(buf = dm_malloc(size + size2)))
235*86d7f5d3SJohn Marino 			return_0;
236*86d7f5d3SJohn Marino 		if (!dev_read_circular(dev, (uint64_t) offset, size,
237*86d7f5d3SJohn Marino 				       (uint64_t) offset2, size2, buf)) {
238*86d7f5d3SJohn Marino 			goto out;
239*86d7f5d3SJohn Marino 		}
240*86d7f5d3SJohn Marino 		p->fb = buf;
241*86d7f5d3SJohn Marino 	}
242*86d7f5d3SJohn Marino 
243*86d7f5d3SJohn Marino 	if (checksum_fn && checksum !=
244*86d7f5d3SJohn Marino 	    (checksum_fn(checksum_fn(INITIAL_CRC, p->fb, size),
245*86d7f5d3SJohn Marino 			 p->fb + size, size2))) {
246*86d7f5d3SJohn Marino 		log_error("%s: Checksum error", dev_name(dev));
247*86d7f5d3SJohn Marino 		goto out;
248*86d7f5d3SJohn Marino 	}
249*86d7f5d3SJohn Marino 
250*86d7f5d3SJohn Marino 	p->fe = p->fb + size + size2;
251*86d7f5d3SJohn Marino 
252*86d7f5d3SJohn Marino 	if (!_parse_config_file(p, cft))
253*86d7f5d3SJohn Marino 		goto_out;
254*86d7f5d3SJohn Marino 
255*86d7f5d3SJohn Marino 	r = 1;
256*86d7f5d3SJohn Marino 
257*86d7f5d3SJohn Marino       out:
258*86d7f5d3SJohn Marino 	if (!use_mmap)
259*86d7f5d3SJohn Marino 		dm_free(buf);
260*86d7f5d3SJohn Marino 	else {
261*86d7f5d3SJohn Marino 		/* unmap the file */
262*86d7f5d3SJohn Marino 		if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
263*86d7f5d3SJohn Marino 			log_sys_error("munmap", dev_name(dev));
264*86d7f5d3SJohn Marino 			r = 0;
265*86d7f5d3SJohn Marino 		}
266*86d7f5d3SJohn Marino 	}
267*86d7f5d3SJohn Marino 
268*86d7f5d3SJohn Marino 	return r;
269*86d7f5d3SJohn Marino }
270*86d7f5d3SJohn Marino 
read_config_file(struct config_tree * cft)271*86d7f5d3SJohn Marino int read_config_file(struct config_tree *cft)
272*86d7f5d3SJohn Marino {
273*86d7f5d3SJohn Marino 	struct cs *c = (struct cs *) cft;
274*86d7f5d3SJohn Marino 	struct stat info;
275*86d7f5d3SJohn Marino 	int r = 1;
276*86d7f5d3SJohn Marino 
277*86d7f5d3SJohn Marino 	if (stat(c->filename, &info)) {
278*86d7f5d3SJohn Marino 		log_sys_error("stat", c->filename);
279*86d7f5d3SJohn Marino 		c->exists = 0;
280*86d7f5d3SJohn Marino 		return 0;
281*86d7f5d3SJohn Marino 	}
282*86d7f5d3SJohn Marino 
283*86d7f5d3SJohn Marino 	if (!S_ISREG(info.st_mode)) {
284*86d7f5d3SJohn Marino 		log_error("%s is not a regular file", c->filename);
285*86d7f5d3SJohn Marino 		c->exists = 0;
286*86d7f5d3SJohn Marino 		return 0;
287*86d7f5d3SJohn Marino 	}
288*86d7f5d3SJohn Marino 
289*86d7f5d3SJohn Marino 	c->exists = 1;
290*86d7f5d3SJohn Marino 
291*86d7f5d3SJohn Marino 	if (info.st_size == 0) {
292*86d7f5d3SJohn Marino 		log_verbose("%s is empty", c->filename);
293*86d7f5d3SJohn Marino 		return 1;
294*86d7f5d3SJohn Marino 	}
295*86d7f5d3SJohn Marino 
296*86d7f5d3SJohn Marino 	if (!c->dev) {
297*86d7f5d3SJohn Marino 		if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1)))
298*86d7f5d3SJohn Marino 			return_0;
299*86d7f5d3SJohn Marino 
300*86d7f5d3SJohn Marino 		if (!dev_open_flags(c->dev, O_RDONLY, 0, 0))
301*86d7f5d3SJohn Marino 			return_0;
302*86d7f5d3SJohn Marino 	}
303*86d7f5d3SJohn Marino 
304*86d7f5d3SJohn Marino 	r = read_config_fd(cft, c->dev, 0, (size_t) info.st_size, 0, 0,
305*86d7f5d3SJohn Marino 			   (checksum_fn_t) NULL, 0);
306*86d7f5d3SJohn Marino 
307*86d7f5d3SJohn Marino 	if (!c->keep_open) {
308*86d7f5d3SJohn Marino 		dev_close(c->dev);
309*86d7f5d3SJohn Marino 		c->dev = 0;
310*86d7f5d3SJohn Marino 	}
311*86d7f5d3SJohn Marino 
312*86d7f5d3SJohn Marino 	c->timestamp = info.st_ctime;
313*86d7f5d3SJohn Marino 
314*86d7f5d3SJohn Marino 	return r;
315*86d7f5d3SJohn Marino }
316*86d7f5d3SJohn Marino 
config_file_timestamp(struct config_tree * cft)317*86d7f5d3SJohn Marino time_t config_file_timestamp(struct config_tree *cft)
318*86d7f5d3SJohn Marino {
319*86d7f5d3SJohn Marino 	struct cs *c = (struct cs *) cft;
320*86d7f5d3SJohn Marino 
321*86d7f5d3SJohn Marino 	return c->timestamp;
322*86d7f5d3SJohn Marino }
323*86d7f5d3SJohn Marino 
324*86d7f5d3SJohn Marino /*
325*86d7f5d3SJohn Marino  * Return 1 if config files ought to be reloaded
326*86d7f5d3SJohn Marino  */
config_file_changed(struct config_tree * cft)327*86d7f5d3SJohn Marino int config_file_changed(struct config_tree *cft)
328*86d7f5d3SJohn Marino {
329*86d7f5d3SJohn Marino 	struct cs *c = (struct cs *) cft;
330*86d7f5d3SJohn Marino 	struct stat info;
331*86d7f5d3SJohn Marino 
332*86d7f5d3SJohn Marino 	if (!c->filename)
333*86d7f5d3SJohn Marino 		return 0;
334*86d7f5d3SJohn Marino 
335*86d7f5d3SJohn Marino 	if (stat(c->filename, &info) == -1) {
336*86d7f5d3SJohn Marino 		/* Ignore a deleted config file: still use original data */
337*86d7f5d3SJohn Marino 		if (errno == ENOENT) {
338*86d7f5d3SJohn Marino 			if (!c->exists)
339*86d7f5d3SJohn Marino 				return 0;
340*86d7f5d3SJohn Marino 			log_very_verbose("Config file %s has disappeared!",
341*86d7f5d3SJohn Marino 					 c->filename);
342*86d7f5d3SJohn Marino 			goto reload;
343*86d7f5d3SJohn Marino 		}
344*86d7f5d3SJohn Marino 		log_sys_error("stat", c->filename);
345*86d7f5d3SJohn Marino 		log_error("Failed to reload configuration files");
346*86d7f5d3SJohn Marino 		return 0;
347*86d7f5d3SJohn Marino 	}
348*86d7f5d3SJohn Marino 
349*86d7f5d3SJohn Marino 	if (!S_ISREG(info.st_mode)) {
350*86d7f5d3SJohn Marino 		log_error("Configuration file %s is not a regular file",
351*86d7f5d3SJohn Marino 			  c->filename);
352*86d7f5d3SJohn Marino 		goto reload;
353*86d7f5d3SJohn Marino 	}
354*86d7f5d3SJohn Marino 
355*86d7f5d3SJohn Marino 	/* Unchanged? */
356*86d7f5d3SJohn Marino 	if (c->timestamp == info.st_ctime)
357*86d7f5d3SJohn Marino 		return 0;
358*86d7f5d3SJohn Marino 
359*86d7f5d3SJohn Marino       reload:
360*86d7f5d3SJohn Marino 	log_verbose("Detected config file change to %s", c->filename);
361*86d7f5d3SJohn Marino 	return 1;
362*86d7f5d3SJohn Marino }
363*86d7f5d3SJohn Marino 
_line_start(struct output_line * outline)364*86d7f5d3SJohn Marino static int _line_start(struct output_line *outline)
365*86d7f5d3SJohn Marino {
366*86d7f5d3SJohn Marino 	if (!dm_pool_begin_object(outline->mem, 128)) {
367*86d7f5d3SJohn Marino 		log_error("dm_pool_begin_object failed for config line");
368*86d7f5d3SJohn Marino 		return 0;
369*86d7f5d3SJohn Marino 	}
370*86d7f5d3SJohn Marino 
371*86d7f5d3SJohn Marino 	return 1;
372*86d7f5d3SJohn Marino }
373*86d7f5d3SJohn Marino 
374*86d7f5d3SJohn Marino static int _line_append(struct output_line *outline, const char *fmt, ...)
375*86d7f5d3SJohn Marino   __attribute__ ((format(printf, 2, 3)));
_line_append(struct output_line * outline,const char * fmt,...)376*86d7f5d3SJohn Marino static int _line_append(struct output_line *outline, const char *fmt, ...)
377*86d7f5d3SJohn Marino {
378*86d7f5d3SJohn Marino 	char buf[4096];
379*86d7f5d3SJohn Marino 	va_list ap;
380*86d7f5d3SJohn Marino 	int n;
381*86d7f5d3SJohn Marino 
382*86d7f5d3SJohn Marino 	va_start(ap, fmt);
383*86d7f5d3SJohn Marino 	n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap);
384*86d7f5d3SJohn Marino 	if (n < 0 || n > (int) sizeof buf - 1) {
385*86d7f5d3SJohn Marino 		log_error("vsnprintf failed for config line");
386*86d7f5d3SJohn Marino 		return 0;
387*86d7f5d3SJohn Marino 	}
388*86d7f5d3SJohn Marino 	va_end(ap);
389*86d7f5d3SJohn Marino 
390*86d7f5d3SJohn Marino 	if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
391*86d7f5d3SJohn Marino 		log_error("dm_pool_grow_object failed for config line");
392*86d7f5d3SJohn Marino 		return 0;
393*86d7f5d3SJohn Marino 	}
394*86d7f5d3SJohn Marino 
395*86d7f5d3SJohn Marino 	return 1;
396*86d7f5d3SJohn Marino }
397*86d7f5d3SJohn Marino 
398*86d7f5d3SJohn Marino #define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
399*86d7f5d3SJohn Marino 
_line_end(struct output_line * outline)400*86d7f5d3SJohn Marino static int _line_end(struct output_line *outline)
401*86d7f5d3SJohn Marino {
402*86d7f5d3SJohn Marino 	const char *line;
403*86d7f5d3SJohn Marino 
404*86d7f5d3SJohn Marino 	if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
405*86d7f5d3SJohn Marino 		log_error("dm_pool_grow_object failed for config line");
406*86d7f5d3SJohn Marino 		return 0;
407*86d7f5d3SJohn Marino 	}
408*86d7f5d3SJohn Marino 
409*86d7f5d3SJohn Marino 	line = dm_pool_end_object(outline->mem);
410*86d7f5d3SJohn Marino 	if (outline->putline)
411*86d7f5d3SJohn Marino 		outline->putline(line, outline->putline_baton);
412*86d7f5d3SJohn Marino 	else {
413*86d7f5d3SJohn Marino 		if (!outline->fp)
414*86d7f5d3SJohn Marino 			log_print("%s", line);
415*86d7f5d3SJohn Marino 		else
416*86d7f5d3SJohn Marino 			fprintf(outline->fp, "%s\n", line);
417*86d7f5d3SJohn Marino 	}
418*86d7f5d3SJohn Marino 
419*86d7f5d3SJohn Marino 	return 1;
420*86d7f5d3SJohn Marino }
421*86d7f5d3SJohn Marino 
_write_value(struct output_line * outline,struct config_value * v)422*86d7f5d3SJohn Marino static int _write_value(struct output_line *outline, struct config_value *v)
423*86d7f5d3SJohn Marino {
424*86d7f5d3SJohn Marino 	char *buf;
425*86d7f5d3SJohn Marino 
426*86d7f5d3SJohn Marino 	switch (v->type) {
427*86d7f5d3SJohn Marino 	case CFG_STRING:
428*86d7f5d3SJohn Marino 		if (!(buf = alloca(escaped_len(v->v.str)))) {
429*86d7f5d3SJohn Marino 			log_error("temporary stack allocation for a config "
430*86d7f5d3SJohn Marino 				  "string failed");
431*86d7f5d3SJohn Marino 			return 0;
432*86d7f5d3SJohn Marino 		}
433*86d7f5d3SJohn Marino 		line_append("\"%s\"", escape_double_quotes(buf, v->v.str));
434*86d7f5d3SJohn Marino 		break;
435*86d7f5d3SJohn Marino 
436*86d7f5d3SJohn Marino 	case CFG_FLOAT:
437*86d7f5d3SJohn Marino 		line_append("%f", v->v.r);
438*86d7f5d3SJohn Marino 		break;
439*86d7f5d3SJohn Marino 
440*86d7f5d3SJohn Marino 	case CFG_INT:
441*86d7f5d3SJohn Marino 		line_append("%" PRId64, v->v.i);
442*86d7f5d3SJohn Marino 		break;
443*86d7f5d3SJohn Marino 
444*86d7f5d3SJohn Marino 	case CFG_EMPTY_ARRAY:
445*86d7f5d3SJohn Marino 		line_append("[]");
446*86d7f5d3SJohn Marino 		break;
447*86d7f5d3SJohn Marino 
448*86d7f5d3SJohn Marino 	default:
449*86d7f5d3SJohn Marino 		log_error("_write_value: Unknown value type: %d", v->type);
450*86d7f5d3SJohn Marino 
451*86d7f5d3SJohn Marino 	}
452*86d7f5d3SJohn Marino 
453*86d7f5d3SJohn Marino 	return 1;
454*86d7f5d3SJohn Marino }
455*86d7f5d3SJohn Marino 
_write_config(const struct config_node * n,int only_one,struct output_line * outline,int level)456*86d7f5d3SJohn Marino static int _write_config(const struct config_node *n, int only_one,
457*86d7f5d3SJohn Marino 			 struct output_line *outline, int level)
458*86d7f5d3SJohn Marino {
459*86d7f5d3SJohn Marino 	char space[MAX_INDENT + 1];
460*86d7f5d3SJohn Marino 	int l = (level < MAX_INDENT) ? level : MAX_INDENT;
461*86d7f5d3SJohn Marino 	int i;
462*86d7f5d3SJohn Marino 
463*86d7f5d3SJohn Marino 	if (!n)
464*86d7f5d3SJohn Marino 		return 1;
465*86d7f5d3SJohn Marino 
466*86d7f5d3SJohn Marino 	for (i = 0; i < l; i++)
467*86d7f5d3SJohn Marino 		space[i] = '\t';
468*86d7f5d3SJohn Marino 	space[i] = '\0';
469*86d7f5d3SJohn Marino 
470*86d7f5d3SJohn Marino 	do {
471*86d7f5d3SJohn Marino 		if (!_line_start(outline))
472*86d7f5d3SJohn Marino 			return_0;
473*86d7f5d3SJohn Marino 		line_append("%s%s", space, n->key);
474*86d7f5d3SJohn Marino 		if (!n->v) {
475*86d7f5d3SJohn Marino 			/* it's a sub section */
476*86d7f5d3SJohn Marino 			line_append(" {");
477*86d7f5d3SJohn Marino 			if (!_line_end(outline))
478*86d7f5d3SJohn Marino 				return_0;
479*86d7f5d3SJohn Marino 			_write_config(n->child, 0, outline, level + 1);
480*86d7f5d3SJohn Marino 			if (!_line_start(outline))
481*86d7f5d3SJohn Marino 				return_0;
482*86d7f5d3SJohn Marino 			line_append("%s}", space);
483*86d7f5d3SJohn Marino 		} else {
484*86d7f5d3SJohn Marino 			/* it's a value */
485*86d7f5d3SJohn Marino 			struct config_value *v = n->v;
486*86d7f5d3SJohn Marino 			line_append("=");
487*86d7f5d3SJohn Marino 			if (v->next) {
488*86d7f5d3SJohn Marino 				line_append("[");
489*86d7f5d3SJohn Marino 				while (v) {
490*86d7f5d3SJohn Marino 					if (!_write_value(outline, v))
491*86d7f5d3SJohn Marino 						return_0;
492*86d7f5d3SJohn Marino 					v = v->next;
493*86d7f5d3SJohn Marino 					if (v)
494*86d7f5d3SJohn Marino 						line_append(", ");
495*86d7f5d3SJohn Marino 				}
496*86d7f5d3SJohn Marino 				line_append("]");
497*86d7f5d3SJohn Marino 			} else
498*86d7f5d3SJohn Marino 				if (!_write_value(outline, v))
499*86d7f5d3SJohn Marino 					return_0;
500*86d7f5d3SJohn Marino 		}
501*86d7f5d3SJohn Marino 		if (!_line_end(outline))
502*86d7f5d3SJohn Marino 			return_0;
503*86d7f5d3SJohn Marino 		n = n->sib;
504*86d7f5d3SJohn Marino 	} while (n && !only_one);
505*86d7f5d3SJohn Marino 	/* FIXME: add error checking */
506*86d7f5d3SJohn Marino 	return 1;
507*86d7f5d3SJohn Marino }
508*86d7f5d3SJohn Marino 
write_config_node(const struct config_node * cn,putline_fn putline,void * baton)509*86d7f5d3SJohn Marino int write_config_node(const struct config_node *cn, putline_fn putline, void *baton)
510*86d7f5d3SJohn Marino {
511*86d7f5d3SJohn Marino 	struct output_line outline;
512*86d7f5d3SJohn Marino 	outline.fp = NULL;
513*86d7f5d3SJohn Marino 	outline.mem = dm_pool_create("config_line", 1024);
514*86d7f5d3SJohn Marino 	outline.putline = putline;
515*86d7f5d3SJohn Marino 	outline.putline_baton = baton;
516*86d7f5d3SJohn Marino 	if (!_write_config(cn, 0, &outline, 0)) {
517*86d7f5d3SJohn Marino 		dm_pool_destroy(outline.mem);
518*86d7f5d3SJohn Marino 		return_0;
519*86d7f5d3SJohn Marino 	}
520*86d7f5d3SJohn Marino 	dm_pool_destroy(outline.mem);
521*86d7f5d3SJohn Marino 	return 1;
522*86d7f5d3SJohn Marino }
523*86d7f5d3SJohn Marino 
write_config_file(struct config_tree * cft,const char * file,int argc,char ** argv)524*86d7f5d3SJohn Marino int write_config_file(struct config_tree *cft, const char *file,
525*86d7f5d3SJohn Marino 		      int argc, char **argv)
526*86d7f5d3SJohn Marino {
527*86d7f5d3SJohn Marino 	struct config_node *cn;
528*86d7f5d3SJohn Marino 	int r = 1;
529*86d7f5d3SJohn Marino 	struct output_line outline;
530*86d7f5d3SJohn Marino 	outline.fp = NULL;
531*86d7f5d3SJohn Marino 	outline.putline = NULL;
532*86d7f5d3SJohn Marino 
533*86d7f5d3SJohn Marino 	if (!file)
534*86d7f5d3SJohn Marino 		file = "stdout";
535*86d7f5d3SJohn Marino 	else if (!(outline.fp = fopen(file, "w"))) {
536*86d7f5d3SJohn Marino 		log_sys_error("open", file);
537*86d7f5d3SJohn Marino 		return 0;
538*86d7f5d3SJohn Marino 	}
539*86d7f5d3SJohn Marino 
540*86d7f5d3SJohn Marino 	outline.mem = dm_pool_create("config_line", 1024);
541*86d7f5d3SJohn Marino 
542*86d7f5d3SJohn Marino 	log_verbose("Dumping configuration to %s", file);
543*86d7f5d3SJohn Marino 	if (!argc) {
544*86d7f5d3SJohn Marino 		if (!_write_config(cft->root, 0, &outline, 0)) {
545*86d7f5d3SJohn Marino 			log_error("Failure while writing to %s", file);
546*86d7f5d3SJohn Marino 			r = 0;
547*86d7f5d3SJohn Marino 		}
548*86d7f5d3SJohn Marino 	} else while (argc--) {
549*86d7f5d3SJohn Marino 		if ((cn = find_config_node(cft->root, *argv))) {
550*86d7f5d3SJohn Marino 			if (!_write_config(cn, 1, &outline, 0)) {
551*86d7f5d3SJohn Marino 				log_error("Failure while writing to %s", file);
552*86d7f5d3SJohn Marino 				r = 0;
553*86d7f5d3SJohn Marino 			}
554*86d7f5d3SJohn Marino 		} else {
555*86d7f5d3SJohn Marino 			log_error("Configuration node %s not found", *argv);
556*86d7f5d3SJohn Marino 			r = 0;
557*86d7f5d3SJohn Marino 		}
558*86d7f5d3SJohn Marino 		argv++;
559*86d7f5d3SJohn Marino 	}
560*86d7f5d3SJohn Marino 
561*86d7f5d3SJohn Marino 	if (outline.fp && lvm_fclose(outline.fp, file)) {
562*86d7f5d3SJohn Marino 		stack;
563*86d7f5d3SJohn Marino 		r = 0;
564*86d7f5d3SJohn Marino 	}
565*86d7f5d3SJohn Marino 
566*86d7f5d3SJohn Marino 	dm_pool_destroy(outline.mem);
567*86d7f5d3SJohn Marino 	return r;
568*86d7f5d3SJohn Marino }
569*86d7f5d3SJohn Marino 
570*86d7f5d3SJohn Marino /*
571*86d7f5d3SJohn Marino  * parser
572*86d7f5d3SJohn Marino  */
_file(struct parser * p)573*86d7f5d3SJohn Marino static struct config_node *_file(struct parser *p)
574*86d7f5d3SJohn Marino {
575*86d7f5d3SJohn Marino 	struct config_node *root = NULL, *n, *l = NULL;
576*86d7f5d3SJohn Marino 	while (p->t != TOK_EOF) {
577*86d7f5d3SJohn Marino 		if (!(n = _section(p)))
578*86d7f5d3SJohn Marino 			return_0;
579*86d7f5d3SJohn Marino 
580*86d7f5d3SJohn Marino 		if (!root)
581*86d7f5d3SJohn Marino 			root = n;
582*86d7f5d3SJohn Marino 		else
583*86d7f5d3SJohn Marino 			l->sib = n;
584*86d7f5d3SJohn Marino 		n->parent = root;
585*86d7f5d3SJohn Marino 		l = n;
586*86d7f5d3SJohn Marino 	}
587*86d7f5d3SJohn Marino 	return root;
588*86d7f5d3SJohn Marino }
589*86d7f5d3SJohn Marino 
_section(struct parser * p)590*86d7f5d3SJohn Marino static struct config_node *_section(struct parser *p)
591*86d7f5d3SJohn Marino {
592*86d7f5d3SJohn Marino 	/* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
593*86d7f5d3SJohn Marino 	struct config_node *root, *n, *l = NULL;
594*86d7f5d3SJohn Marino 	if (!(root = _create_node(p->mem)))
595*86d7f5d3SJohn Marino 		return_0;
596*86d7f5d3SJohn Marino 
597*86d7f5d3SJohn Marino 	if (!(root->key = _dup_tok(p)))
598*86d7f5d3SJohn Marino 		return_0;
599*86d7f5d3SJohn Marino 
600*86d7f5d3SJohn Marino 	match(TOK_IDENTIFIER);
601*86d7f5d3SJohn Marino 
602*86d7f5d3SJohn Marino 	if (p->t == TOK_SECTION_B) {
603*86d7f5d3SJohn Marino 		match(TOK_SECTION_B);
604*86d7f5d3SJohn Marino 		while (p->t != TOK_SECTION_E) {
605*86d7f5d3SJohn Marino 			if (!(n = _section(p)))
606*86d7f5d3SJohn Marino 				return_0;
607*86d7f5d3SJohn Marino 
608*86d7f5d3SJohn Marino 			if (!root->child)
609*86d7f5d3SJohn Marino 				root->child = n;
610*86d7f5d3SJohn Marino 			else
611*86d7f5d3SJohn Marino 				l->sib = n;
612*86d7f5d3SJohn Marino 			n->parent = root;
613*86d7f5d3SJohn Marino 			l = n;
614*86d7f5d3SJohn Marino 		}
615*86d7f5d3SJohn Marino 		match(TOK_SECTION_E);
616*86d7f5d3SJohn Marino 	} else {
617*86d7f5d3SJohn Marino 		match(TOK_EQ);
618*86d7f5d3SJohn Marino 		if (!(root->v = _value(p)))
619*86d7f5d3SJohn Marino 			return_0;
620*86d7f5d3SJohn Marino 	}
621*86d7f5d3SJohn Marino 
622*86d7f5d3SJohn Marino 	return root;
623*86d7f5d3SJohn Marino }
624*86d7f5d3SJohn Marino 
_value(struct parser * p)625*86d7f5d3SJohn Marino static struct config_value *_value(struct parser *p)
626*86d7f5d3SJohn Marino {
627*86d7f5d3SJohn Marino 	/* '[' TYPE* ']' | TYPE */
628*86d7f5d3SJohn Marino 	struct config_value *h = NULL, *l, *ll = NULL;
629*86d7f5d3SJohn Marino 	if (p->t == TOK_ARRAY_B) {
630*86d7f5d3SJohn Marino 		match(TOK_ARRAY_B);
631*86d7f5d3SJohn Marino 		while (p->t != TOK_ARRAY_E) {
632*86d7f5d3SJohn Marino 			if (!(l = _type(p)))
633*86d7f5d3SJohn Marino 				return_0;
634*86d7f5d3SJohn Marino 
635*86d7f5d3SJohn Marino 			if (!h)
636*86d7f5d3SJohn Marino 				h = l;
637*86d7f5d3SJohn Marino 			else
638*86d7f5d3SJohn Marino 				ll->next = l;
639*86d7f5d3SJohn Marino 			ll = l;
640*86d7f5d3SJohn Marino 
641*86d7f5d3SJohn Marino 			if (p->t == TOK_COMMA)
642*86d7f5d3SJohn Marino 				match(TOK_COMMA);
643*86d7f5d3SJohn Marino 		}
644*86d7f5d3SJohn Marino 		match(TOK_ARRAY_E);
645*86d7f5d3SJohn Marino 		/*
646*86d7f5d3SJohn Marino 		 * Special case for an empty array.
647*86d7f5d3SJohn Marino 		 */
648*86d7f5d3SJohn Marino 		if (!h) {
649*86d7f5d3SJohn Marino 			if (!(h = _create_value(p->mem)))
650*86d7f5d3SJohn Marino 				return NULL;
651*86d7f5d3SJohn Marino 
652*86d7f5d3SJohn Marino 			h->type = CFG_EMPTY_ARRAY;
653*86d7f5d3SJohn Marino 		}
654*86d7f5d3SJohn Marino 
655*86d7f5d3SJohn Marino 	} else
656*86d7f5d3SJohn Marino 		h = _type(p);
657*86d7f5d3SJohn Marino 
658*86d7f5d3SJohn Marino 	return h;
659*86d7f5d3SJohn Marino }
660*86d7f5d3SJohn Marino 
_type(struct parser * p)661*86d7f5d3SJohn Marino static struct config_value *_type(struct parser *p)
662*86d7f5d3SJohn Marino {
663*86d7f5d3SJohn Marino 	/* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
664*86d7f5d3SJohn Marino 	struct config_value *v = _create_value(p->mem);
665*86d7f5d3SJohn Marino 
666*86d7f5d3SJohn Marino 	if (!v)
667*86d7f5d3SJohn Marino 		return NULL;
668*86d7f5d3SJohn Marino 
669*86d7f5d3SJohn Marino 	switch (p->t) {
670*86d7f5d3SJohn Marino 	case TOK_INT:
671*86d7f5d3SJohn Marino 		v->type = CFG_INT;
672*86d7f5d3SJohn Marino 		v->v.i = strtoll(p->tb, NULL, 0);	/* FIXME: check error */
673*86d7f5d3SJohn Marino 		match(TOK_INT);
674*86d7f5d3SJohn Marino 		break;
675*86d7f5d3SJohn Marino 
676*86d7f5d3SJohn Marino 	case TOK_FLOAT:
677*86d7f5d3SJohn Marino 		v->type = CFG_FLOAT;
678*86d7f5d3SJohn Marino 		v->v.r = strtod(p->tb, NULL);	/* FIXME: check error */
679*86d7f5d3SJohn Marino 		match(TOK_FLOAT);
680*86d7f5d3SJohn Marino 		break;
681*86d7f5d3SJohn Marino 
682*86d7f5d3SJohn Marino 	case TOK_STRING:
683*86d7f5d3SJohn Marino 		v->type = CFG_STRING;
684*86d7f5d3SJohn Marino 
685*86d7f5d3SJohn Marino 		p->tb++, p->te--;	/* strip "'s */
686*86d7f5d3SJohn Marino 		if (!(v->v.str = _dup_tok(p)))
687*86d7f5d3SJohn Marino 			return_0;
688*86d7f5d3SJohn Marino 		p->te++;
689*86d7f5d3SJohn Marino 		match(TOK_STRING);
690*86d7f5d3SJohn Marino 		break;
691*86d7f5d3SJohn Marino 
692*86d7f5d3SJohn Marino 	case TOK_STRING_ESCAPED:
693*86d7f5d3SJohn Marino 		v->type = CFG_STRING;
694*86d7f5d3SJohn Marino 
695*86d7f5d3SJohn Marino 		p->tb++, p->te--;	/* strip "'s */
696*86d7f5d3SJohn Marino 		if (!(v->v.str = _dup_tok(p)))
697*86d7f5d3SJohn Marino 			return_0;
698*86d7f5d3SJohn Marino 		unescape_double_quotes(v->v.str);
699*86d7f5d3SJohn Marino 		p->te++;
700*86d7f5d3SJohn Marino 		match(TOK_STRING_ESCAPED);
701*86d7f5d3SJohn Marino 		break;
702*86d7f5d3SJohn Marino 
703*86d7f5d3SJohn Marino 	default:
704*86d7f5d3SJohn Marino 		log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
705*86d7f5d3SJohn Marino 			  p->tb - p->fb + 1, p->line);
706*86d7f5d3SJohn Marino 		return 0;
707*86d7f5d3SJohn Marino 	}
708*86d7f5d3SJohn Marino 	return v;
709*86d7f5d3SJohn Marino }
710*86d7f5d3SJohn Marino 
_match_aux(struct parser * p,int t)711*86d7f5d3SJohn Marino static int _match_aux(struct parser *p, int t)
712*86d7f5d3SJohn Marino {
713*86d7f5d3SJohn Marino 	if (p->t != t)
714*86d7f5d3SJohn Marino 		return 0;
715*86d7f5d3SJohn Marino 
716*86d7f5d3SJohn Marino 	_get_token(p, t);
717*86d7f5d3SJohn Marino 	return 1;
718*86d7f5d3SJohn Marino }
719*86d7f5d3SJohn Marino 
720*86d7f5d3SJohn Marino /*
721*86d7f5d3SJohn Marino  * tokeniser
722*86d7f5d3SJohn Marino  */
_get_token(struct parser * p,int tok_prev)723*86d7f5d3SJohn Marino static void _get_token(struct parser *p, int tok_prev)
724*86d7f5d3SJohn Marino {
725*86d7f5d3SJohn Marino 	int values_allowed = 0;
726*86d7f5d3SJohn Marino 
727*86d7f5d3SJohn Marino 	p->tb = p->te;
728*86d7f5d3SJohn Marino 	_eat_space(p);
729*86d7f5d3SJohn Marino 	if (p->tb == p->fe || !*p->tb) {
730*86d7f5d3SJohn Marino 		p->t = TOK_EOF;
731*86d7f5d3SJohn Marino 		return;
732*86d7f5d3SJohn Marino 	}
733*86d7f5d3SJohn Marino 
734*86d7f5d3SJohn Marino 	/* Should next token be interpreted as value instead of identifier? */
735*86d7f5d3SJohn Marino 	if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
736*86d7f5d3SJohn Marino 	    tok_prev == TOK_COMMA)
737*86d7f5d3SJohn Marino 		values_allowed = 1;
738*86d7f5d3SJohn Marino 
739*86d7f5d3SJohn Marino 	p->t = TOK_INT;		/* fudge so the fall through for
740*86d7f5d3SJohn Marino 				   floats works */
741*86d7f5d3SJohn Marino 	switch (*p->te) {
742*86d7f5d3SJohn Marino 	case SECTION_B_CHAR:
743*86d7f5d3SJohn Marino 		p->t = TOK_SECTION_B;
744*86d7f5d3SJohn Marino 		p->te++;
745*86d7f5d3SJohn Marino 		break;
746*86d7f5d3SJohn Marino 
747*86d7f5d3SJohn Marino 	case SECTION_E_CHAR:
748*86d7f5d3SJohn Marino 		p->t = TOK_SECTION_E;
749*86d7f5d3SJohn Marino 		p->te++;
750*86d7f5d3SJohn Marino 		break;
751*86d7f5d3SJohn Marino 
752*86d7f5d3SJohn Marino 	case '[':
753*86d7f5d3SJohn Marino 		p->t = TOK_ARRAY_B;
754*86d7f5d3SJohn Marino 		p->te++;
755*86d7f5d3SJohn Marino 		break;
756*86d7f5d3SJohn Marino 
757*86d7f5d3SJohn Marino 	case ']':
758*86d7f5d3SJohn Marino 		p->t = TOK_ARRAY_E;
759*86d7f5d3SJohn Marino 		p->te++;
760*86d7f5d3SJohn Marino 		break;
761*86d7f5d3SJohn Marino 
762*86d7f5d3SJohn Marino 	case ',':
763*86d7f5d3SJohn Marino 		p->t = TOK_COMMA;
764*86d7f5d3SJohn Marino 		p->te++;
765*86d7f5d3SJohn Marino 		break;
766*86d7f5d3SJohn Marino 
767*86d7f5d3SJohn Marino 	case '=':
768*86d7f5d3SJohn Marino 		p->t = TOK_EQ;
769*86d7f5d3SJohn Marino 		p->te++;
770*86d7f5d3SJohn Marino 		break;
771*86d7f5d3SJohn Marino 
772*86d7f5d3SJohn Marino 	case '"':
773*86d7f5d3SJohn Marino 		p->t = TOK_STRING_ESCAPED;
774*86d7f5d3SJohn Marino 		p->te++;
775*86d7f5d3SJohn Marino 		while ((p->te != p->fe) && (*p->te) && (*p->te != '"')) {
776*86d7f5d3SJohn Marino 			if ((*p->te == '\\') && (p->te + 1 != p->fe) &&
777*86d7f5d3SJohn Marino 			    *(p->te + 1))
778*86d7f5d3SJohn Marino 				p->te++;
779*86d7f5d3SJohn Marino 			p->te++;
780*86d7f5d3SJohn Marino 		}
781*86d7f5d3SJohn Marino 
782*86d7f5d3SJohn Marino 		if ((p->te != p->fe) && (*p->te))
783*86d7f5d3SJohn Marino 			p->te++;
784*86d7f5d3SJohn Marino 		break;
785*86d7f5d3SJohn Marino 
786*86d7f5d3SJohn Marino 	case '\'':
787*86d7f5d3SJohn Marino 		p->t = TOK_STRING;
788*86d7f5d3SJohn Marino 		p->te++;
789*86d7f5d3SJohn Marino 		while ((p->te != p->fe) && (*p->te) && (*p->te != '\''))
790*86d7f5d3SJohn Marino 			p->te++;
791*86d7f5d3SJohn Marino 
792*86d7f5d3SJohn Marino 		if ((p->te != p->fe) && (*p->te))
793*86d7f5d3SJohn Marino 			p->te++;
794*86d7f5d3SJohn Marino 		break;
795*86d7f5d3SJohn Marino 
796*86d7f5d3SJohn Marino 	case '.':
797*86d7f5d3SJohn Marino 		p->t = TOK_FLOAT;
798*86d7f5d3SJohn Marino 	case '0':
799*86d7f5d3SJohn Marino 	case '1':
800*86d7f5d3SJohn Marino 	case '2':
801*86d7f5d3SJohn Marino 	case '3':
802*86d7f5d3SJohn Marino 	case '4':
803*86d7f5d3SJohn Marino 	case '5':
804*86d7f5d3SJohn Marino 	case '6':
805*86d7f5d3SJohn Marino 	case '7':
806*86d7f5d3SJohn Marino 	case '8':
807*86d7f5d3SJohn Marino 	case '9':
808*86d7f5d3SJohn Marino 	case '+':
809*86d7f5d3SJohn Marino 	case '-':
810*86d7f5d3SJohn Marino 		if (values_allowed) {
811*86d7f5d3SJohn Marino 			p->te++;
812*86d7f5d3SJohn Marino 			while ((p->te != p->fe) && (*p->te)) {
813*86d7f5d3SJohn Marino 				if (*p->te == '.') {
814*86d7f5d3SJohn Marino 					if (p->t == TOK_FLOAT)
815*86d7f5d3SJohn Marino 						break;
816*86d7f5d3SJohn Marino 					p->t = TOK_FLOAT;
817*86d7f5d3SJohn Marino 				} else if (!isdigit((int) *p->te))
818*86d7f5d3SJohn Marino 					break;
819*86d7f5d3SJohn Marino 				p->te++;
820*86d7f5d3SJohn Marino 			}
821*86d7f5d3SJohn Marino 			break;
822*86d7f5d3SJohn Marino 		}
823*86d7f5d3SJohn Marino 
824*86d7f5d3SJohn Marino 	default:
825*86d7f5d3SJohn Marino 		p->t = TOK_IDENTIFIER;
826*86d7f5d3SJohn Marino 		while ((p->te != p->fe) && (*p->te) && !isspace(*p->te) &&
827*86d7f5d3SJohn Marino 		       (*p->te != '#') && (*p->te != '=') &&
828*86d7f5d3SJohn Marino 		       (*p->te != SECTION_B_CHAR) &&
829*86d7f5d3SJohn Marino 		       (*p->te != SECTION_E_CHAR))
830*86d7f5d3SJohn Marino 			p->te++;
831*86d7f5d3SJohn Marino 		break;
832*86d7f5d3SJohn Marino 	}
833*86d7f5d3SJohn Marino }
834*86d7f5d3SJohn Marino 
_eat_space(struct parser * p)835*86d7f5d3SJohn Marino static void _eat_space(struct parser *p)
836*86d7f5d3SJohn Marino {
837*86d7f5d3SJohn Marino 	while ((p->tb != p->fe) && (*p->tb)) {
838*86d7f5d3SJohn Marino 		if (*p->te == '#')
839*86d7f5d3SJohn Marino 			while ((p->te != p->fe) && (*p->te) && (*p->te != '\n'))
840*86d7f5d3SJohn Marino 				p->te++;
841*86d7f5d3SJohn Marino 
842*86d7f5d3SJohn Marino 		else if (isspace(*p->te)) {
843*86d7f5d3SJohn Marino 			while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) {
844*86d7f5d3SJohn Marino 				if (*p->te == '\n')
845*86d7f5d3SJohn Marino 					p->line++;
846*86d7f5d3SJohn Marino 				p->te++;
847*86d7f5d3SJohn Marino 			}
848*86d7f5d3SJohn Marino 		}
849*86d7f5d3SJohn Marino 
850*86d7f5d3SJohn Marino 		else
851*86d7f5d3SJohn Marino 			return;
852*86d7f5d3SJohn Marino 
853*86d7f5d3SJohn Marino 		p->tb = p->te;
854*86d7f5d3SJohn Marino 	}
855*86d7f5d3SJohn Marino }
856*86d7f5d3SJohn Marino 
857*86d7f5d3SJohn Marino /*
858*86d7f5d3SJohn Marino  * memory management
859*86d7f5d3SJohn Marino  */
_create_value(struct dm_pool * mem)860*86d7f5d3SJohn Marino static struct config_value *_create_value(struct dm_pool *mem)
861*86d7f5d3SJohn Marino {
862*86d7f5d3SJohn Marino 	struct config_value *v = dm_pool_alloc(mem, sizeof(*v));
863*86d7f5d3SJohn Marino 
864*86d7f5d3SJohn Marino 	if (v)
865*86d7f5d3SJohn Marino 		memset(v, 0, sizeof(*v));
866*86d7f5d3SJohn Marino 
867*86d7f5d3SJohn Marino 	return v;
868*86d7f5d3SJohn Marino }
869*86d7f5d3SJohn Marino 
_create_node(struct dm_pool * mem)870*86d7f5d3SJohn Marino static struct config_node *_create_node(struct dm_pool *mem)
871*86d7f5d3SJohn Marino {
872*86d7f5d3SJohn Marino 	struct config_node *n = dm_pool_alloc(mem, sizeof(*n));
873*86d7f5d3SJohn Marino 
874*86d7f5d3SJohn Marino 	if (n)
875*86d7f5d3SJohn Marino 		memset(n, 0, sizeof(*n));
876*86d7f5d3SJohn Marino 
877*86d7f5d3SJohn Marino 	return n;
878*86d7f5d3SJohn Marino }
879*86d7f5d3SJohn Marino 
_dup_tok(struct parser * p)880*86d7f5d3SJohn Marino static char *_dup_tok(struct parser *p)
881*86d7f5d3SJohn Marino {
882*86d7f5d3SJohn Marino 	size_t len = p->te - p->tb;
883*86d7f5d3SJohn Marino 	char *str = dm_pool_alloc(p->mem, len + 1);
884*86d7f5d3SJohn Marino 	if (!str)
885*86d7f5d3SJohn Marino 		return_0;
886*86d7f5d3SJohn Marino 	strncpy(str, p->tb, len);
887*86d7f5d3SJohn Marino 	str[len] = '\0';
888*86d7f5d3SJohn Marino 	return str;
889*86d7f5d3SJohn Marino }
890*86d7f5d3SJohn Marino 
891*86d7f5d3SJohn Marino /*
892*86d7f5d3SJohn Marino  * utility functions
893*86d7f5d3SJohn Marino  */
_find_config_node(const struct config_node * cn,const char * path)894*86d7f5d3SJohn Marino static struct config_node *_find_config_node(const struct config_node *cn,
895*86d7f5d3SJohn Marino 					     const char *path)
896*86d7f5d3SJohn Marino {
897*86d7f5d3SJohn Marino 	const char *e;
898*86d7f5d3SJohn Marino 	const struct config_node *cn_found = NULL;
899*86d7f5d3SJohn Marino 
900*86d7f5d3SJohn Marino 	while (cn) {
901*86d7f5d3SJohn Marino 		/* trim any leading slashes */
902*86d7f5d3SJohn Marino 		while (*path && (*path == sep))
903*86d7f5d3SJohn Marino 			path++;
904*86d7f5d3SJohn Marino 
905*86d7f5d3SJohn Marino 		/* find the end of this segment */
906*86d7f5d3SJohn Marino 		for (e = path; *e && (*e != sep); e++) ;
907*86d7f5d3SJohn Marino 
908*86d7f5d3SJohn Marino 		/* hunt for the node */
909*86d7f5d3SJohn Marino 		cn_found = NULL;
910*86d7f5d3SJohn Marino 		while (cn) {
911*86d7f5d3SJohn Marino 			if (_tok_match(cn->key, path, e)) {
912*86d7f5d3SJohn Marino 				/* Inefficient */
913*86d7f5d3SJohn Marino 				if (!cn_found)
914*86d7f5d3SJohn Marino 					cn_found = cn;
915*86d7f5d3SJohn Marino 				else
916*86d7f5d3SJohn Marino 					log_error("WARNING: Ignoring duplicate"
917*86d7f5d3SJohn Marino 						  " config node: %s ("
918*86d7f5d3SJohn Marino 						  "seeking %s)", cn->key, path);
919*86d7f5d3SJohn Marino 			}
920*86d7f5d3SJohn Marino 
921*86d7f5d3SJohn Marino 			cn = cn->sib;
922*86d7f5d3SJohn Marino 		}
923*86d7f5d3SJohn Marino 
924*86d7f5d3SJohn Marino 		if (cn_found && *e)
925*86d7f5d3SJohn Marino 			cn = cn_found->child;
926*86d7f5d3SJohn Marino 		else
927*86d7f5d3SJohn Marino 			break;	/* don't move into the last node */
928*86d7f5d3SJohn Marino 
929*86d7f5d3SJohn Marino 		path = e;
930*86d7f5d3SJohn Marino 	}
931*86d7f5d3SJohn Marino 
932*86d7f5d3SJohn Marino 	return (struct config_node *) cn_found;
933*86d7f5d3SJohn Marino }
934*86d7f5d3SJohn Marino 
_find_first_config_node(const struct config_node * cn1,const struct config_node * cn2,const char * path)935*86d7f5d3SJohn Marino static struct config_node *_find_first_config_node(const struct config_node *cn1,
936*86d7f5d3SJohn Marino 						   const struct config_node *cn2,
937*86d7f5d3SJohn Marino 						   const char *path)
938*86d7f5d3SJohn Marino {
939*86d7f5d3SJohn Marino 	struct config_node *cn;
940*86d7f5d3SJohn Marino 
941*86d7f5d3SJohn Marino 	if (cn1 && (cn = _find_config_node(cn1, path)))
942*86d7f5d3SJohn Marino 		return cn;
943*86d7f5d3SJohn Marino 
944*86d7f5d3SJohn Marino 	if (cn2 && (cn = _find_config_node(cn2, path)))
945*86d7f5d3SJohn Marino 		return cn;
946*86d7f5d3SJohn Marino 
947*86d7f5d3SJohn Marino 	return NULL;
948*86d7f5d3SJohn Marino }
949*86d7f5d3SJohn Marino 
find_config_node(const struct config_node * cn,const char * path)950*86d7f5d3SJohn Marino struct config_node *find_config_node(const struct config_node *cn,
951*86d7f5d3SJohn Marino 				     const char *path)
952*86d7f5d3SJohn Marino {
953*86d7f5d3SJohn Marino 	return _find_config_node(cn, path);
954*86d7f5d3SJohn Marino }
955*86d7f5d3SJohn Marino 
_find_config_str(const struct config_node * cn1,const struct config_node * cn2,const char * path,const char * fail)956*86d7f5d3SJohn Marino static const char *_find_config_str(const struct config_node *cn1,
957*86d7f5d3SJohn Marino 				    const struct config_node *cn2,
958*86d7f5d3SJohn Marino 				    const char *path, const char *fail)
959*86d7f5d3SJohn Marino {
960*86d7f5d3SJohn Marino 	const struct config_node *n = _find_first_config_node(cn1, cn2, path);
961*86d7f5d3SJohn Marino 
962*86d7f5d3SJohn Marino 	/* Empty strings are ignored */
963*86d7f5d3SJohn Marino 	if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) {
964*86d7f5d3SJohn Marino 		log_very_verbose("Setting %s to %s", path, n->v->v.str);
965*86d7f5d3SJohn Marino 		return n->v->v.str;
966*86d7f5d3SJohn Marino 	}
967*86d7f5d3SJohn Marino 
968*86d7f5d3SJohn Marino 	if (fail)
969*86d7f5d3SJohn Marino 		log_very_verbose("%s not found in config: defaulting to %s",
970*86d7f5d3SJohn Marino 				 path, fail);
971*86d7f5d3SJohn Marino 	return fail;
972*86d7f5d3SJohn Marino }
973*86d7f5d3SJohn Marino 
find_config_str(const struct config_node * cn,const char * path,const char * fail)974*86d7f5d3SJohn Marino const char *find_config_str(const struct config_node *cn,
975*86d7f5d3SJohn Marino 			    const char *path, const char *fail)
976*86d7f5d3SJohn Marino {
977*86d7f5d3SJohn Marino 	return _find_config_str(cn, NULL, path, fail);
978*86d7f5d3SJohn Marino }
979*86d7f5d3SJohn Marino 
_find_config_int64(const struct config_node * cn1,const struct config_node * cn2,const char * path,int64_t fail)980*86d7f5d3SJohn Marino static int64_t _find_config_int64(const struct config_node *cn1,
981*86d7f5d3SJohn Marino 				  const struct config_node *cn2,
982*86d7f5d3SJohn Marino 				  const char *path, int64_t fail)
983*86d7f5d3SJohn Marino {
984*86d7f5d3SJohn Marino 	const struct config_node *n = _find_first_config_node(cn1, cn2, path);
985*86d7f5d3SJohn Marino 
986*86d7f5d3SJohn Marino 	if (n && n->v && n->v->type == CFG_INT) {
987*86d7f5d3SJohn Marino 		log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
988*86d7f5d3SJohn Marino 		return n->v->v.i;
989*86d7f5d3SJohn Marino 	}
990*86d7f5d3SJohn Marino 
991*86d7f5d3SJohn Marino 	log_very_verbose("%s not found in config: defaulting to %" PRId64,
992*86d7f5d3SJohn Marino 			 path, fail);
993*86d7f5d3SJohn Marino 	return fail;
994*86d7f5d3SJohn Marino }
995*86d7f5d3SJohn Marino 
find_config_int(const struct config_node * cn,const char * path,int fail)996*86d7f5d3SJohn Marino int find_config_int(const struct config_node *cn, const char *path, int fail)
997*86d7f5d3SJohn Marino {
998*86d7f5d3SJohn Marino 	/* FIXME Add log_error message on overflow */
999*86d7f5d3SJohn Marino 	return (int) _find_config_int64(cn, NULL, path, (int64_t) fail);
1000*86d7f5d3SJohn Marino }
1001*86d7f5d3SJohn Marino 
_find_config_float(const struct config_node * cn1,const struct config_node * cn2,const char * path,float fail)1002*86d7f5d3SJohn Marino static float _find_config_float(const struct config_node *cn1,
1003*86d7f5d3SJohn Marino 				const struct config_node *cn2,
1004*86d7f5d3SJohn Marino 				const char *path, float fail)
1005*86d7f5d3SJohn Marino {
1006*86d7f5d3SJohn Marino 	const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1007*86d7f5d3SJohn Marino 
1008*86d7f5d3SJohn Marino 	if (n && n->v && n->v->type == CFG_FLOAT) {
1009*86d7f5d3SJohn Marino 		log_very_verbose("Setting %s to %f", path, n->v->v.r);
1010*86d7f5d3SJohn Marino 		return n->v->v.r;
1011*86d7f5d3SJohn Marino 	}
1012*86d7f5d3SJohn Marino 
1013*86d7f5d3SJohn Marino 	log_very_verbose("%s not found in config: defaulting to %f",
1014*86d7f5d3SJohn Marino 			 path, fail);
1015*86d7f5d3SJohn Marino 
1016*86d7f5d3SJohn Marino 	return fail;
1017*86d7f5d3SJohn Marino 
1018*86d7f5d3SJohn Marino }
1019*86d7f5d3SJohn Marino 
find_config_float(const struct config_node * cn,const char * path,float fail)1020*86d7f5d3SJohn Marino float find_config_float(const struct config_node *cn, const char *path,
1021*86d7f5d3SJohn Marino 			float fail)
1022*86d7f5d3SJohn Marino {
1023*86d7f5d3SJohn Marino 	return _find_config_float(cn, NULL, path, fail);
1024*86d7f5d3SJohn Marino }
1025*86d7f5d3SJohn Marino 
find_config_tree_node(struct cmd_context * cmd,const char * path)1026*86d7f5d3SJohn Marino struct config_node *find_config_tree_node(struct cmd_context *cmd,
1027*86d7f5d3SJohn Marino 					  const char *path)
1028*86d7f5d3SJohn Marino {
1029*86d7f5d3SJohn Marino 	return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path);
1030*86d7f5d3SJohn Marino }
1031*86d7f5d3SJohn Marino 
find_config_tree_str(struct cmd_context * cmd,const char * path,const char * fail)1032*86d7f5d3SJohn Marino const char *find_config_tree_str(struct cmd_context *cmd,
1033*86d7f5d3SJohn Marino 				 const char *path, const char *fail)
1034*86d7f5d3SJohn Marino {
1035*86d7f5d3SJohn Marino 	return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1036*86d7f5d3SJohn Marino }
1037*86d7f5d3SJohn Marino 
find_config_tree_int(struct cmd_context * cmd,const char * path,int fail)1038*86d7f5d3SJohn Marino int find_config_tree_int(struct cmd_context *cmd, const char *path,
1039*86d7f5d3SJohn Marino 			 int fail)
1040*86d7f5d3SJohn Marino {
1041*86d7f5d3SJohn Marino 	/* FIXME Add log_error message on overflow */
1042*86d7f5d3SJohn Marino 	return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail);
1043*86d7f5d3SJohn Marino }
1044*86d7f5d3SJohn Marino 
find_config_tree_float(struct cmd_context * cmd,const char * path,float fail)1045*86d7f5d3SJohn Marino float find_config_tree_float(struct cmd_context *cmd, const char *path,
1046*86d7f5d3SJohn Marino 			     float fail)
1047*86d7f5d3SJohn Marino {
1048*86d7f5d3SJohn Marino 	return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1049*86d7f5d3SJohn Marino }
1050*86d7f5d3SJohn Marino 
_str_in_array(const char * str,const char * const values[])1051*86d7f5d3SJohn Marino static int _str_in_array(const char *str, const char * const values[])
1052*86d7f5d3SJohn Marino {
1053*86d7f5d3SJohn Marino 	int i;
1054*86d7f5d3SJohn Marino 
1055*86d7f5d3SJohn Marino 	for (i = 0; values[i]; i++)
1056*86d7f5d3SJohn Marino 		if (!strcasecmp(str, values[i]))
1057*86d7f5d3SJohn Marino 			return 1;
1058*86d7f5d3SJohn Marino 
1059*86d7f5d3SJohn Marino 	return 0;
1060*86d7f5d3SJohn Marino }
1061*86d7f5d3SJohn Marino 
_str_to_bool(const char * str,int fail)1062*86d7f5d3SJohn Marino static int _str_to_bool(const char *str, int fail)
1063*86d7f5d3SJohn Marino {
1064*86d7f5d3SJohn Marino 	const char * const _true_values[]  = { "y", "yes", "on", "true", NULL };
1065*86d7f5d3SJohn Marino 	const char * const _false_values[] = { "n", "no", "off", "false", NULL };
1066*86d7f5d3SJohn Marino 
1067*86d7f5d3SJohn Marino 	if (_str_in_array(str, _true_values))
1068*86d7f5d3SJohn Marino 		return 1;
1069*86d7f5d3SJohn Marino 
1070*86d7f5d3SJohn Marino 	if (_str_in_array(str, _false_values))
1071*86d7f5d3SJohn Marino 		return 0;
1072*86d7f5d3SJohn Marino 
1073*86d7f5d3SJohn Marino 	return fail;
1074*86d7f5d3SJohn Marino }
1075*86d7f5d3SJohn Marino 
_find_config_bool(const struct config_node * cn1,const struct config_node * cn2,const char * path,int fail)1076*86d7f5d3SJohn Marino static int _find_config_bool(const struct config_node *cn1,
1077*86d7f5d3SJohn Marino 			     const struct config_node *cn2,
1078*86d7f5d3SJohn Marino 			     const char *path, int fail)
1079*86d7f5d3SJohn Marino {
1080*86d7f5d3SJohn Marino 	const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1081*86d7f5d3SJohn Marino 	struct config_value *v;
1082*86d7f5d3SJohn Marino 
1083*86d7f5d3SJohn Marino 	if (!n)
1084*86d7f5d3SJohn Marino 		return fail;
1085*86d7f5d3SJohn Marino 
1086*86d7f5d3SJohn Marino 	v = n->v;
1087*86d7f5d3SJohn Marino 
1088*86d7f5d3SJohn Marino 	switch (v->type) {
1089*86d7f5d3SJohn Marino 	case CFG_INT:
1090*86d7f5d3SJohn Marino 		return v->v.i ? 1 : 0;
1091*86d7f5d3SJohn Marino 
1092*86d7f5d3SJohn Marino 	case CFG_STRING:
1093*86d7f5d3SJohn Marino 		return _str_to_bool(v->v.str, fail);
1094*86d7f5d3SJohn Marino 	}
1095*86d7f5d3SJohn Marino 
1096*86d7f5d3SJohn Marino 	return fail;
1097*86d7f5d3SJohn Marino }
1098*86d7f5d3SJohn Marino 
find_config_bool(const struct config_node * cn,const char * path,int fail)1099*86d7f5d3SJohn Marino int find_config_bool(const struct config_node *cn, const char *path, int fail)
1100*86d7f5d3SJohn Marino {
1101*86d7f5d3SJohn Marino 	return _find_config_bool(cn, NULL, path, fail);
1102*86d7f5d3SJohn Marino }
1103*86d7f5d3SJohn Marino 
find_config_tree_bool(struct cmd_context * cmd,const char * path,int fail)1104*86d7f5d3SJohn Marino int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail)
1105*86d7f5d3SJohn Marino {
1106*86d7f5d3SJohn Marino 	return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1107*86d7f5d3SJohn Marino }
1108*86d7f5d3SJohn Marino 
get_config_uint32(const struct config_node * cn,const char * path,uint32_t * result)1109*86d7f5d3SJohn Marino int get_config_uint32(const struct config_node *cn, const char *path,
1110*86d7f5d3SJohn Marino 		      uint32_t *result)
1111*86d7f5d3SJohn Marino {
1112*86d7f5d3SJohn Marino 	const struct config_node *n;
1113*86d7f5d3SJohn Marino 
1114*86d7f5d3SJohn Marino 	n = find_config_node(cn, path);
1115*86d7f5d3SJohn Marino 
1116*86d7f5d3SJohn Marino 	if (!n || !n->v || n->v->type != CFG_INT)
1117*86d7f5d3SJohn Marino 		return 0;
1118*86d7f5d3SJohn Marino 
1119*86d7f5d3SJohn Marino 	*result = n->v->v.i;
1120*86d7f5d3SJohn Marino 	return 1;
1121*86d7f5d3SJohn Marino }
1122*86d7f5d3SJohn Marino 
get_config_uint64(const struct config_node * cn,const char * path,uint64_t * result)1123*86d7f5d3SJohn Marino int get_config_uint64(const struct config_node *cn, const char *path,
1124*86d7f5d3SJohn Marino 		      uint64_t *result)
1125*86d7f5d3SJohn Marino {
1126*86d7f5d3SJohn Marino 	const struct config_node *n;
1127*86d7f5d3SJohn Marino 
1128*86d7f5d3SJohn Marino 	n = find_config_node(cn, path);
1129*86d7f5d3SJohn Marino 
1130*86d7f5d3SJohn Marino 	if (!n || !n->v || n->v->type != CFG_INT)
1131*86d7f5d3SJohn Marino 		return 0;
1132*86d7f5d3SJohn Marino 
1133*86d7f5d3SJohn Marino 	*result = (uint64_t) n->v->v.i;
1134*86d7f5d3SJohn Marino 	return 1;
1135*86d7f5d3SJohn Marino }
1136*86d7f5d3SJohn Marino 
get_config_str(const struct config_node * cn,const char * path,char ** result)1137*86d7f5d3SJohn Marino int get_config_str(const struct config_node *cn, const char *path,
1138*86d7f5d3SJohn Marino 		   char **result)
1139*86d7f5d3SJohn Marino {
1140*86d7f5d3SJohn Marino 	const struct config_node *n;
1141*86d7f5d3SJohn Marino 
1142*86d7f5d3SJohn Marino 	n = find_config_node(cn, path);
1143*86d7f5d3SJohn Marino 
1144*86d7f5d3SJohn Marino 	if (!n || !n->v || n->v->type != CFG_STRING)
1145*86d7f5d3SJohn Marino 		return 0;
1146*86d7f5d3SJohn Marino 
1147*86d7f5d3SJohn Marino 	*result = n->v->v.str;
1148*86d7f5d3SJohn Marino 	return 1;
1149*86d7f5d3SJohn Marino }
1150*86d7f5d3SJohn Marino 
1151*86d7f5d3SJohn Marino /* Insert cn2 after cn1 */
_insert_config_node(struct config_node ** cn1,struct config_node * cn2)1152*86d7f5d3SJohn Marino static void _insert_config_node(struct config_node **cn1,
1153*86d7f5d3SJohn Marino 				struct config_node *cn2)
1154*86d7f5d3SJohn Marino {
1155*86d7f5d3SJohn Marino 	if (!*cn1) {
1156*86d7f5d3SJohn Marino 		*cn1 = cn2;
1157*86d7f5d3SJohn Marino 		cn2->sib = NULL;
1158*86d7f5d3SJohn Marino 	} else {
1159*86d7f5d3SJohn Marino 		cn2->sib = (*cn1)->sib;
1160*86d7f5d3SJohn Marino 		(*cn1)->sib = cn2;
1161*86d7f5d3SJohn Marino 	}
1162*86d7f5d3SJohn Marino }
1163*86d7f5d3SJohn Marino 
1164*86d7f5d3SJohn Marino /*
1165*86d7f5d3SJohn Marino  * Merge section cn2 into section cn1 (which has the same name)
1166*86d7f5d3SJohn Marino  * overwriting any existing cn1 nodes with matching names.
1167*86d7f5d3SJohn Marino  */
_merge_section(struct config_node * cn1,struct config_node * cn2)1168*86d7f5d3SJohn Marino static void _merge_section(struct config_node *cn1, struct config_node *cn2)
1169*86d7f5d3SJohn Marino {
1170*86d7f5d3SJohn Marino 	struct config_node *cn, *nextn, *oldn;
1171*86d7f5d3SJohn Marino 	struct config_value *cv;
1172*86d7f5d3SJohn Marino 
1173*86d7f5d3SJohn Marino 	for (cn = cn2->child; cn; cn = nextn) {
1174*86d7f5d3SJohn Marino 		nextn = cn->sib;
1175*86d7f5d3SJohn Marino 
1176*86d7f5d3SJohn Marino 		/* Skip "tags" */
1177*86d7f5d3SJohn Marino 		if (!strcmp(cn->key, "tags"))
1178*86d7f5d3SJohn Marino 			continue;
1179*86d7f5d3SJohn Marino 
1180*86d7f5d3SJohn Marino 		/* Subsection? */
1181*86d7f5d3SJohn Marino 		if (!cn->v)
1182*86d7f5d3SJohn Marino 			/* Ignore - we don't have any of these yet */
1183*86d7f5d3SJohn Marino 			continue;
1184*86d7f5d3SJohn Marino 		/* Not already present? */
1185*86d7f5d3SJohn Marino 		if (!(oldn = find_config_node(cn1->child, cn->key))) {
1186*86d7f5d3SJohn Marino 			_insert_config_node(&cn1->child, cn);
1187*86d7f5d3SJohn Marino 			continue;
1188*86d7f5d3SJohn Marino 		}
1189*86d7f5d3SJohn Marino 		/* Merge certain value lists */
1190*86d7f5d3SJohn Marino 		if ((!strcmp(cn1->key, "activation") &&
1191*86d7f5d3SJohn Marino 		     !strcmp(cn->key, "volume_list")) ||
1192*86d7f5d3SJohn Marino 		    (!strcmp(cn1->key, "devices") &&
1193*86d7f5d3SJohn Marino 		     (!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
1194*86d7f5d3SJohn Marino 			cv = cn->v;
1195*86d7f5d3SJohn Marino 			while (cv->next)
1196*86d7f5d3SJohn Marino 				cv = cv->next;
1197*86d7f5d3SJohn Marino 			cv->next = oldn->v;
1198*86d7f5d3SJohn Marino 		}
1199*86d7f5d3SJohn Marino 
1200*86d7f5d3SJohn Marino 		/* Replace values */
1201*86d7f5d3SJohn Marino 		oldn->v = cn->v;
1202*86d7f5d3SJohn Marino 	}
1203*86d7f5d3SJohn Marino }
1204*86d7f5d3SJohn Marino 
_match_host_tags(struct dm_list * tags,struct config_node * tn)1205*86d7f5d3SJohn Marino static int _match_host_tags(struct dm_list *tags, struct config_node *tn)
1206*86d7f5d3SJohn Marino {
1207*86d7f5d3SJohn Marino 	struct config_value *tv;
1208*86d7f5d3SJohn Marino 	const char *str;
1209*86d7f5d3SJohn Marino 
1210*86d7f5d3SJohn Marino 	for (tv = tn->v; tv; tv = tv->next) {
1211*86d7f5d3SJohn Marino 		if (tv->type != CFG_STRING)
1212*86d7f5d3SJohn Marino 			continue;
1213*86d7f5d3SJohn Marino 		str = tv->v.str;
1214*86d7f5d3SJohn Marino 		if (*str == '@')
1215*86d7f5d3SJohn Marino 			str++;
1216*86d7f5d3SJohn Marino 		if (!*str)
1217*86d7f5d3SJohn Marino 			continue;
1218*86d7f5d3SJohn Marino 		if (str_list_match_item(tags, str))
1219*86d7f5d3SJohn Marino 			return 1;
1220*86d7f5d3SJohn Marino 	}
1221*86d7f5d3SJohn Marino 
1222*86d7f5d3SJohn Marino 	return 0;
1223*86d7f5d3SJohn Marino }
1224*86d7f5d3SJohn Marino 
1225*86d7f5d3SJohn Marino /* Destructively merge a new config tree into an existing one */
merge_config_tree(struct cmd_context * cmd,struct config_tree * cft,struct config_tree * newdata)1226*86d7f5d3SJohn Marino int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
1227*86d7f5d3SJohn Marino 		      struct config_tree *newdata)
1228*86d7f5d3SJohn Marino {
1229*86d7f5d3SJohn Marino 	struct config_node *root = cft->root;
1230*86d7f5d3SJohn Marino 	struct config_node *cn, *nextn, *oldn, *tn, *cn2;
1231*86d7f5d3SJohn Marino 
1232*86d7f5d3SJohn Marino 	for (cn = newdata->root; cn; cn = nextn) {
1233*86d7f5d3SJohn Marino 		nextn = cn->sib;
1234*86d7f5d3SJohn Marino 		/* Ignore tags section */
1235*86d7f5d3SJohn Marino 		if (!strcmp(cn->key, "tags"))
1236*86d7f5d3SJohn Marino 			continue;
1237*86d7f5d3SJohn Marino 		/* If there's a tags node, skip if host tags don't match */
1238*86d7f5d3SJohn Marino 		if ((tn = find_config_node(cn->child, "tags"))) {
1239*86d7f5d3SJohn Marino 			if (!_match_host_tags(&cmd->tags, tn))
1240*86d7f5d3SJohn Marino 				continue;
1241*86d7f5d3SJohn Marino 		}
1242*86d7f5d3SJohn Marino 		if (!(oldn = find_config_node(root, cn->key))) {
1243*86d7f5d3SJohn Marino 			_insert_config_node(&cft->root, cn);
1244*86d7f5d3SJohn Marino 			/* Remove any "tags" nodes */
1245*86d7f5d3SJohn Marino 			for (cn2 = cn->child; cn2; cn2 = cn2->sib) {
1246*86d7f5d3SJohn Marino 				if (!strcmp(cn2->key, "tags")) {
1247*86d7f5d3SJohn Marino 					cn->child = cn2->sib;
1248*86d7f5d3SJohn Marino 					continue;
1249*86d7f5d3SJohn Marino 				}
1250*86d7f5d3SJohn Marino 				if (cn2->sib && !strcmp(cn2->sib->key, "tags")) {
1251*86d7f5d3SJohn Marino 					cn2->sib = cn2->sib->sib;
1252*86d7f5d3SJohn Marino 					continue;
1253*86d7f5d3SJohn Marino 				}
1254*86d7f5d3SJohn Marino 			}
1255*86d7f5d3SJohn Marino 			continue;
1256*86d7f5d3SJohn Marino 		}
1257*86d7f5d3SJohn Marino 		_merge_section(oldn, cn);
1258*86d7f5d3SJohn Marino 	}
1259*86d7f5d3SJohn Marino 
1260*86d7f5d3SJohn Marino 	return 1;
1261*86d7f5d3SJohn Marino }
1262*86d7f5d3SJohn Marino 
1263*86d7f5d3SJohn Marino /*
1264*86d7f5d3SJohn Marino  * Convert a token type to the char it represents.
1265*86d7f5d3SJohn Marino  */
_token_type_to_char(int type)1266*86d7f5d3SJohn Marino static char _token_type_to_char(int type)
1267*86d7f5d3SJohn Marino {
1268*86d7f5d3SJohn Marino 	switch (type) {
1269*86d7f5d3SJohn Marino 		case TOK_SECTION_B:
1270*86d7f5d3SJohn Marino 			return SECTION_B_CHAR;
1271*86d7f5d3SJohn Marino 		case TOK_SECTION_E:
1272*86d7f5d3SJohn Marino 			return SECTION_E_CHAR;
1273*86d7f5d3SJohn Marino 		default:
1274*86d7f5d3SJohn Marino 			return 0;
1275*86d7f5d3SJohn Marino 	}
1276*86d7f5d3SJohn Marino }
1277*86d7f5d3SJohn Marino 
1278*86d7f5d3SJohn Marino /*
1279*86d7f5d3SJohn Marino  * Returns:
1280*86d7f5d3SJohn Marino  *  # of 'type' tokens in 'str'.
1281*86d7f5d3SJohn Marino  */
_count_tokens(const char * str,unsigned len,int type)1282*86d7f5d3SJohn Marino static unsigned _count_tokens(const char *str, unsigned len, int type)
1283*86d7f5d3SJohn Marino {
1284*86d7f5d3SJohn Marino 	char c;
1285*86d7f5d3SJohn Marino 
1286*86d7f5d3SJohn Marino 	c = _token_type_to_char(type);
1287*86d7f5d3SJohn Marino 
1288*86d7f5d3SJohn Marino 	return count_chars(str, len, c);
1289*86d7f5d3SJohn Marino }
1290*86d7f5d3SJohn Marino 
config_parent_name(const struct config_node * n)1291*86d7f5d3SJohn Marino const char *config_parent_name(const struct config_node *n)
1292*86d7f5d3SJohn Marino {
1293*86d7f5d3SJohn Marino 	return (n->parent ? n->parent->key : "(root)");
1294*86d7f5d3SJohn Marino }
1295*86d7f5d3SJohn Marino /*
1296*86d7f5d3SJohn Marino  * Heuristic function to make a quick guess as to whether a text
1297*86d7f5d3SJohn Marino  * region probably contains a valid config "section".  (Useful for
1298*86d7f5d3SJohn Marino  * scanning areas of the disk for old metadata.)
1299*86d7f5d3SJohn Marino  * Config sections contain various tokens, may contain other sections
1300*86d7f5d3SJohn Marino  * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
1301*86d7f5d3SJohn Marino  * end (type 'TOK_SECTION_E') tokens.  As a quick heuristic, we just
1302*86d7f5d3SJohn Marino  * count the number of begin and end tokens, and see if they are
1303*86d7f5d3SJohn Marino  * non-zero and the counts match.
1304*86d7f5d3SJohn Marino  * Full validation of the section should be done with another function
1305*86d7f5d3SJohn Marino  * (for example, read_config_fd).
1306*86d7f5d3SJohn Marino  *
1307*86d7f5d3SJohn Marino  * Returns:
1308*86d7f5d3SJohn Marino  *  0 - probably is not a valid config section
1309*86d7f5d3SJohn Marino  *  1 - probably _is_ a valid config section
1310*86d7f5d3SJohn Marino  */
maybe_config_section(const char * str,unsigned len)1311*86d7f5d3SJohn Marino unsigned maybe_config_section(const char *str, unsigned len)
1312*86d7f5d3SJohn Marino {
1313*86d7f5d3SJohn Marino 	int begin_count;
1314*86d7f5d3SJohn Marino 	int end_count;
1315*86d7f5d3SJohn Marino 
1316*86d7f5d3SJohn Marino 	begin_count = _count_tokens(str, len, TOK_SECTION_B);
1317*86d7f5d3SJohn Marino 	end_count = _count_tokens(str, len, TOK_SECTION_E);
1318*86d7f5d3SJohn Marino 
1319*86d7f5d3SJohn Marino 	if (begin_count && end_count && (begin_count == end_count))
1320*86d7f5d3SJohn Marino 		return 1;
1321*86d7f5d3SJohn Marino 	else
1322*86d7f5d3SJohn Marino 		return 0;
1323*86d7f5d3SJohn Marino }
1324*86d7f5d3SJohn Marino 
_clone_config_value(struct dm_pool * mem,const struct config_value * v)1325*86d7f5d3SJohn Marino static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
1326*86d7f5d3SJohn Marino {
1327*86d7f5d3SJohn Marino 	if (!v)
1328*86d7f5d3SJohn Marino 		return NULL;
1329*86d7f5d3SJohn Marino 	struct config_value *new = _create_value(mem);
1330*86d7f5d3SJohn Marino 	new->type = v->type;
1331*86d7f5d3SJohn Marino 	if (v->type == CFG_STRING)
1332*86d7f5d3SJohn Marino 		new->v.str = dm_pool_strdup(mem, v->v.str);
1333*86d7f5d3SJohn Marino 	else
1334*86d7f5d3SJohn Marino 		new->v = v->v;
1335*86d7f5d3SJohn Marino 	new->next = _clone_config_value(mem, v->next);
1336*86d7f5d3SJohn Marino 	return new;
1337*86d7f5d3SJohn Marino }
1338*86d7f5d3SJohn Marino 
clone_config_node(struct dm_pool * mem,const struct config_node * cn,int siblings)1339*86d7f5d3SJohn Marino struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
1340*86d7f5d3SJohn Marino 				      int siblings)
1341*86d7f5d3SJohn Marino {
1342*86d7f5d3SJohn Marino 	if (!cn)
1343*86d7f5d3SJohn Marino 		return NULL;
1344*86d7f5d3SJohn Marino 	struct config_node *new = _create_node(mem);
1345*86d7f5d3SJohn Marino 	new->key = dm_pool_strdup(mem, cn->key);
1346*86d7f5d3SJohn Marino 	new->child = clone_config_node(mem, cn->child, 1);
1347*86d7f5d3SJohn Marino 	new->v = _clone_config_value(mem, cn->v);
1348*86d7f5d3SJohn Marino 	if (siblings)
1349*86d7f5d3SJohn Marino 		new->sib = clone_config_node(mem, cn->sib, siblings);
1350*86d7f5d3SJohn Marino 	else
1351*86d7f5d3SJohn Marino 		new->sib = NULL;
1352*86d7f5d3SJohn Marino 	return new;
1353*86d7f5d3SJohn Marino }
1354