parse.y revision 204076
1%{
2/*-
3 * Copyright (c) 2009-2010 The FreeBSD Foundation
4 * All rights reserved.
5 *
6 * This software was developed by Pawel Jakub Dawidek under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * $FreeBSD: head/sbin/hastd/parse.y 204076 2010-02-18 23:16:19Z pjd $
31 */
32
33#include <sys/param.h>	/* MAXHOSTNAMELEN */
34#include <sys/queue.h>
35#include <sys/sysctl.h>
36
37#include <arpa/inet.h>
38
39#include <assert.h>
40#include <err.h>
41#include <stdio.h>
42#include <string.h>
43#include <sysexits.h>
44#include <unistd.h>
45
46#include "hast.h"
47
48extern int depth;
49extern int lineno;
50
51extern FILE *yyin;
52extern char *yytext;
53
54static struct hastd_config lconfig;
55static struct hast_resource *curres;
56static bool mynode;
57
58static char depth0_control[HAST_ADDRSIZE];
59static char depth0_listen[HAST_ADDRSIZE];
60static int depth0_replication;
61
62static char depth1_provname[PATH_MAX];
63static char depth1_localpath[PATH_MAX];
64
65static bool
66isitme(const char *name)
67{
68	char buf[MAXHOSTNAMELEN];
69	char *pos;
70	size_t bufsize;
71
72	/*
73	 * First check if the give name matches our full hostname.
74	 */
75	if (gethostname(buf, sizeof(buf)) < 0)
76		err(EX_OSERR, "gethostname() failed");
77	if (strcmp(buf, name) == 0)
78		return (true);
79
80	/*
81	 * Now check if it matches first part of the host name.
82	 */
83	pos = strchr(buf, '.');
84	if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0)
85		return (true);
86
87	/*
88	 * At the end check if name is equal to our host's UUID.
89	 */
90	bufsize = sizeof(buf);
91	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0)
92		err(EX_OSERR, "sysctlbyname(kern.hostuuid) failed");
93	if (strcasecmp(buf, name) == 0)
94		return (true);
95
96	/*
97	 * Looks like this isn't about us.
98	 */
99	return (false);
100}
101
102void
103yyerror(const char *str)
104{
105
106	fprintf(stderr, "error at line %d near '%s': %s\n",
107	    lineno, yytext, str);
108}
109
110struct hastd_config *
111yy_config_parse(const char *config)
112{
113	int ret;
114
115	curres = NULL;
116	mynode = false;
117
118	depth0_replication = HAST_REPLICATION_MEMSYNC;
119	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
120	strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen));
121
122	TAILQ_INIT(&lconfig.hc_resources);
123
124	yyin = fopen(config, "r");
125	if (yyin == NULL)
126		err(EX_OSFILE, "cannot open configuration file %s", config);
127	ret = yyparse();
128	fclose(yyin);
129	if (ret != 0) {
130		yy_config_free(&lconfig);
131		exit(EX_CONFIG);
132	}
133
134	/*
135	 * Let's see if everything is set up.
136	 */
137	if (lconfig.hc_controladdr[0] == '\0') {
138		strlcpy(lconfig.hc_controladdr, depth0_control,
139		    sizeof(lconfig.hc_controladdr));
140	}
141	if (lconfig.hc_listenaddr[0] == '\0') {
142		strlcpy(lconfig.hc_listenaddr, depth0_listen,
143		    sizeof(lconfig.hc_listenaddr));
144	}
145	TAILQ_FOREACH(curres, &lconfig.hc_resources, hr_next) {
146		assert(curres->hr_provname[0] != '\0');
147		assert(curres->hr_localpath[0] != '\0');
148		assert(curres->hr_remoteaddr[0] != '\0');
149
150		if (curres->hr_replication == -1) {
151			/*
152			 * Replication is not set at resource-level.
153			 * Use global or default setting.
154			 */
155			curres->hr_replication = depth0_replication;
156		}
157	}
158
159	return (&lconfig);
160}
161
162void
163yy_config_free(struct hastd_config *config)
164{
165	struct hast_resource *res;
166
167	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
168		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
169		free(res);
170	}
171}
172%}
173
174%token CONTROL LISTEN PORT REPLICATION EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON
175%token FULLSYNC MEMSYNC ASYNC
176%token NUM STR OB CB
177
178%type <num> replication_type
179
180%union
181{
182	int num;
183	char *str;
184}
185
186%token <num> NUM
187%token <str> STR
188
189%%
190
191statements:
192	|
193	statements statement
194	;
195
196statement:
197	control_statement
198	|
199	listen_statement
200	|
201	replication_statement
202	|
203	node_statement
204	|
205	resource_statement
206	;
207
208control_statement:	CONTROL STR
209	{
210		switch (depth) {
211		case 0:
212			if (strlcpy(depth0_control, $2,
213			    sizeof(depth0_control)) >=
214			    sizeof(depth0_control)) {
215				errx(EX_CONFIG, "control argument too long");
216			}
217			break;
218		case 1:
219			if (mynode) {
220				if (strlcpy(lconfig.hc_controladdr, $2,
221				    sizeof(lconfig.hc_controladdr)) >=
222				    sizeof(lconfig.hc_controladdr)) {
223					errx(EX_CONFIG,
224					    "control argument too long");
225				}
226			}
227			break;
228		default:
229			assert(!"control at wrong depth level");
230		}
231	}
232	;
233
234listen_statement:	LISTEN STR
235	{
236		switch (depth) {
237		case 0:
238			if (strlcpy(depth0_listen, $2,
239			    sizeof(depth0_listen)) >=
240			    sizeof(depth0_listen)) {
241				errx(EX_CONFIG, "listen argument too long");
242			}
243			break;
244		case 1:
245			if (mynode) {
246				if (strlcpy(lconfig.hc_listenaddr, $2,
247				    sizeof(lconfig.hc_listenaddr)) >=
248				    sizeof(lconfig.hc_listenaddr)) {
249					errx(EX_CONFIG,
250					    "listen argument too long");
251				}
252			}
253			break;
254		default:
255			assert(!"listen at wrong depth level");
256		}
257	}
258	;
259
260replication_statement:	REPLICATION replication_type
261	{
262		switch (depth) {
263		case 0:
264			depth0_replication = $2;
265			break;
266		case 1:
267			if (curres != NULL)
268				curres->hr_replication = $2;
269			break;
270		default:
271			assert(!"replication at wrong depth level");
272		}
273	}
274	;
275
276replication_type:
277	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
278	|
279	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
280	|
281	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
282	;
283
284node_statement:		ON node_start OB node_entries CB
285	{
286		mynode = false;
287	}
288	;
289
290node_start:	STR
291	{
292		if (isitme($1))
293			mynode = true;
294	}
295	;
296
297node_entries:
298	|
299	node_entries node_entry
300	;
301
302node_entry:
303	control_statement
304	|
305	listen_statement
306	;
307
308resource_statement:	RESOURCE resource_start OB resource_entries CB
309	{
310		if (curres != NULL) {
311			/*
312			 * Let's see there are some resource-level settings
313			 * that we can use for node-level settings.
314			 */
315			if (curres->hr_provname[0] == '\0' &&
316			    depth1_provname[0] != '\0') {
317				/*
318				 * Provider name is not set at node-level,
319				 * but is set at resource-level, use it.
320				 */
321				strlcpy(curres->hr_provname, depth1_provname,
322				    sizeof(curres->hr_provname));
323			}
324			if (curres->hr_localpath[0] == '\0' &&
325			    depth1_localpath[0] != '\0') {
326				/*
327				 * Path to local provider is not set at
328				 * node-level, but is set at resource-level,
329				 * use it.
330				 */
331				strlcpy(curres->hr_localpath, depth1_localpath,
332				    sizeof(curres->hr_localpath));
333			}
334
335			/*
336			 * If provider name is not given, use resource name
337			 * as provider name.
338			 */
339			if (curres->hr_provname[0] == '\0') {
340				strlcpy(curres->hr_provname, curres->hr_name,
341				    sizeof(curres->hr_provname));
342			}
343
344			/*
345			 * Remote address has to be configured at this point.
346			 */
347			if (curres->hr_remoteaddr[0] == '\0') {
348				errx(EX_CONFIG,
349				    "remote address not configured for resource %s",
350				    curres->hr_name);
351			}
352			/*
353			 * Path to local provider has to be configured at this
354			 * point.
355			 */
356			if (curres->hr_localpath[0] == '\0') {
357				errx(EX_CONFIG,
358				    "path local component not configured for resource %s",
359				    curres->hr_name);
360			}
361
362			/* Put it onto resource list. */
363			TAILQ_INSERT_TAIL(&lconfig.hc_resources, curres, hr_next);
364			curres = NULL;
365		}
366	}
367	;
368
369resource_start:	STR
370	{
371		/*
372		 * Clear those, so we can tell if they were set at
373		 * resource-level or not.
374		 */
375		depth1_provname[0] = '\0';
376		depth1_localpath[0] = '\0';
377
378		curres = calloc(1, sizeof(*curres));
379		if (curres == NULL) {
380			errx(EX_TEMPFAIL,
381			    "cannot allocate memory for resource");
382		}
383		if (strlcpy(curres->hr_name, $1,
384		    sizeof(curres->hr_name)) >=
385		    sizeof(curres->hr_name)) {
386			errx(EX_CONFIG,
387			    "resource name (%s) too long", $1);
388		}
389		curres->hr_role = HAST_ROLE_INIT;
390		curres->hr_previous_role = HAST_ROLE_INIT;
391		curres->hr_replication = -1;
392		curres->hr_provname[0] = '\0';
393		curres->hr_localpath[0] = '\0';
394		curres->hr_localfd = -1;
395		curres->hr_remoteaddr[0] = '\0';
396		curres->hr_ggateunit = -1;
397	}
398	;
399
400resource_entries:
401	|
402	resource_entries resource_entry
403	;
404
405resource_entry:
406	replication_statement
407	|
408	name_statement
409	|
410	local_statement
411	|
412	resource_node_statement
413	;
414
415name_statement:		NAME STR
416	{
417		switch (depth) {
418		case 1:
419			if (strlcpy(depth1_provname, $2,
420			    sizeof(depth1_provname)) >=
421			    sizeof(depth1_provname)) {
422				errx(EX_CONFIG, "name argument too long");
423			}
424			break;
425		case 2:
426			if (mynode) {
427				assert(curres != NULL);
428				if (strlcpy(curres->hr_provname, $2,
429				    sizeof(curres->hr_provname)) >=
430				    sizeof(curres->hr_provname)) {
431					errx(EX_CONFIG,
432					    "name argument too long");
433				}
434			}
435			break;
436		default:
437			assert(!"name at wrong depth level");
438		}
439	}
440	;
441
442local_statement:	LOCAL STR
443	{
444		switch (depth) {
445		case 1:
446			if (strlcpy(depth1_localpath, $2,
447			    sizeof(depth1_localpath)) >=
448			    sizeof(depth1_localpath)) {
449				errx(EX_CONFIG, "local argument too long");
450			}
451			break;
452		case 2:
453			if (mynode) {
454				assert(curres != NULL);
455				if (strlcpy(curres->hr_localpath, $2,
456				    sizeof(curres->hr_localpath)) >=
457				    sizeof(curres->hr_localpath)) {
458					errx(EX_CONFIG,
459					    "local argument too long");
460				}
461			}
462			break;
463		default:
464			assert(!"local at wrong depth level");
465		}
466	}
467	;
468
469resource_node_statement:ON resource_node_start OB resource_node_entries CB
470	{
471		mynode = false;
472	}
473	;
474
475resource_node_start:	STR
476	{
477		if (curres != NULL && isitme($1))
478			mynode = true;
479	}
480	;
481
482resource_node_entries:
483	|
484	resource_node_entries resource_node_entry
485	;
486
487resource_node_entry:
488	name_statement
489	|
490	local_statement
491	|
492	remote_statement
493	;
494
495remote_statement:	REMOTE STR
496	{
497		assert(depth == 2);
498		if (mynode) {
499			assert(curres != NULL);
500			if (strlcpy(curres->hr_remoteaddr, $2,
501			    sizeof(curres->hr_remoteaddr)) >=
502			    sizeof(curres->hr_remoteaddr)) {
503				errx(EX_CONFIG, "remote argument too long");
504			}
505		}
506	}
507	;
508