parse.y revision 207371
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 207371 2010-04-29 15:36:32Z 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;
61static int depth0_timeout;
62
63static char depth1_provname[PATH_MAX];
64static char depth1_localpath[PATH_MAX];
65
66static bool
67isitme(const char *name)
68{
69	char buf[MAXHOSTNAMELEN];
70	char *pos;
71	size_t bufsize;
72
73	/*
74	 * First check if the give name matches our full hostname.
75	 */
76	if (gethostname(buf, sizeof(buf)) < 0)
77		err(EX_OSERR, "gethostname() failed");
78	if (strcmp(buf, name) == 0)
79		return (true);
80
81	/*
82	 * Now check if it matches first part of the host name.
83	 */
84	pos = strchr(buf, '.');
85	if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0)
86		return (true);
87
88	/*
89	 * At the end check if name is equal to our host's UUID.
90	 */
91	bufsize = sizeof(buf);
92	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0)
93		err(EX_OSERR, "sysctlbyname(kern.hostuuid) failed");
94	if (strcasecmp(buf, name) == 0)
95		return (true);
96
97	/*
98	 * Looks like this isn't about us.
99	 */
100	return (false);
101}
102
103void
104yyerror(const char *str)
105{
106
107	fprintf(stderr, "error at line %d near '%s': %s\n",
108	    lineno, yytext, str);
109}
110
111struct hastd_config *
112yy_config_parse(const char *config)
113{
114	int ret;
115
116	curres = NULL;
117	mynode = false;
118
119	depth0_timeout = HAST_TIMEOUT;
120	depth0_replication = HAST_REPLICATION_MEMSYNC;
121	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
122	strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen));
123
124	TAILQ_INIT(&lconfig.hc_resources);
125
126	yyin = fopen(config, "r");
127	if (yyin == NULL)
128		err(EX_OSFILE, "cannot open configuration file %s", config);
129	ret = yyparse();
130	fclose(yyin);
131	if (ret != 0) {
132		yy_config_free(&lconfig);
133		exit(EX_CONFIG);
134	}
135
136	/*
137	 * Let's see if everything is set up.
138	 */
139	if (lconfig.hc_controladdr[0] == '\0') {
140		strlcpy(lconfig.hc_controladdr, depth0_control,
141		    sizeof(lconfig.hc_controladdr));
142	}
143	if (lconfig.hc_listenaddr[0] == '\0') {
144		strlcpy(lconfig.hc_listenaddr, depth0_listen,
145		    sizeof(lconfig.hc_listenaddr));
146	}
147	TAILQ_FOREACH(curres, &lconfig.hc_resources, hr_next) {
148		assert(curres->hr_provname[0] != '\0');
149		assert(curres->hr_localpath[0] != '\0');
150		assert(curres->hr_remoteaddr[0] != '\0');
151
152		if (curres->hr_replication == -1) {
153			/*
154			 * Replication is not set at resource-level.
155			 * Use global or default setting.
156			 */
157			curres->hr_replication = depth0_replication;
158		}
159		if (curres->hr_timeout == -1) {
160			/*
161			 * Timeout is not set at resource-level.
162			 * Use global or default setting.
163			 */
164			curres->hr_timeout = depth0_timeout;
165		}
166	}
167
168	return (&lconfig);
169}
170
171void
172yy_config_free(struct hastd_config *config)
173{
174	struct hast_resource *res;
175
176	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
177		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
178		free(res);
179	}
180}
181%}
182
183%token CONTROL LISTEN PORT REPLICATION TIMEOUT EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON
184%token FULLSYNC MEMSYNC ASYNC
185%token NUM STR OB CB
186
187%type <num> replication_type
188
189%union
190{
191	int num;
192	char *str;
193}
194
195%token <num> NUM
196%token <str> STR
197
198%%
199
200statements:
201	|
202	statements statement
203	;
204
205statement:
206	control_statement
207	|
208	listen_statement
209	|
210	replication_statement
211	|
212	timeout_statement
213	|
214	node_statement
215	|
216	resource_statement
217	;
218
219control_statement:	CONTROL STR
220	{
221		switch (depth) {
222		case 0:
223			if (strlcpy(depth0_control, $2,
224			    sizeof(depth0_control)) >=
225			    sizeof(depth0_control)) {
226				errx(EX_CONFIG, "control argument too long");
227			}
228			break;
229		case 1:
230			if (mynode) {
231				if (strlcpy(lconfig.hc_controladdr, $2,
232				    sizeof(lconfig.hc_controladdr)) >=
233				    sizeof(lconfig.hc_controladdr)) {
234					errx(EX_CONFIG,
235					    "control argument too long");
236				}
237			}
238			break;
239		default:
240			assert(!"control at wrong depth level");
241		}
242	}
243	;
244
245listen_statement:	LISTEN STR
246	{
247		switch (depth) {
248		case 0:
249			if (strlcpy(depth0_listen, $2,
250			    sizeof(depth0_listen)) >=
251			    sizeof(depth0_listen)) {
252				errx(EX_CONFIG, "listen argument too long");
253			}
254			break;
255		case 1:
256			if (mynode) {
257				if (strlcpy(lconfig.hc_listenaddr, $2,
258				    sizeof(lconfig.hc_listenaddr)) >=
259				    sizeof(lconfig.hc_listenaddr)) {
260					errx(EX_CONFIG,
261					    "listen argument too long");
262				}
263			}
264			break;
265		default:
266			assert(!"listen at wrong depth level");
267		}
268	}
269	;
270
271replication_statement:	REPLICATION replication_type
272	{
273		switch (depth) {
274		case 0:
275			depth0_replication = $2;
276			break;
277		case 1:
278			if (curres != NULL)
279				curres->hr_replication = $2;
280			break;
281		default:
282			assert(!"replication at wrong depth level");
283		}
284	}
285	;
286
287replication_type:
288	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
289	|
290	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
291	|
292	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
293	;
294
295timeout_statement:	TIMEOUT NUM
296	{
297		switch (depth) {
298		case 0:
299			depth0_timeout = $2;
300			break;
301		case 1:
302			if (curres != NULL)
303				curres->hr_timeout = $2;
304			break;
305		default:
306			assert(!"timeout at wrong depth level");
307		}
308	}
309	;
310
311node_statement:		ON node_start OB node_entries CB
312	{
313		mynode = false;
314	}
315	;
316
317node_start:	STR
318	{
319		if (isitme($1))
320			mynode = true;
321	}
322	;
323
324node_entries:
325	|
326	node_entries node_entry
327	;
328
329node_entry:
330	control_statement
331	|
332	listen_statement
333	;
334
335resource_statement:	RESOURCE resource_start OB resource_entries CB
336	{
337		if (curres != NULL) {
338			/*
339			 * Let's see there are some resource-level settings
340			 * that we can use for node-level settings.
341			 */
342			if (curres->hr_provname[0] == '\0' &&
343			    depth1_provname[0] != '\0') {
344				/*
345				 * Provider name is not set at node-level,
346				 * but is set at resource-level, use it.
347				 */
348				strlcpy(curres->hr_provname, depth1_provname,
349				    sizeof(curres->hr_provname));
350			}
351			if (curres->hr_localpath[0] == '\0' &&
352			    depth1_localpath[0] != '\0') {
353				/*
354				 * Path to local provider is not set at
355				 * node-level, but is set at resource-level,
356				 * use it.
357				 */
358				strlcpy(curres->hr_localpath, depth1_localpath,
359				    sizeof(curres->hr_localpath));
360			}
361
362			/*
363			 * If provider name is not given, use resource name
364			 * as provider name.
365			 */
366			if (curres->hr_provname[0] == '\0') {
367				strlcpy(curres->hr_provname, curres->hr_name,
368				    sizeof(curres->hr_provname));
369			}
370
371			/*
372			 * Remote address has to be configured at this point.
373			 */
374			if (curres->hr_remoteaddr[0] == '\0') {
375				errx(EX_CONFIG,
376				    "remote address not configured for resource %s",
377				    curres->hr_name);
378			}
379			/*
380			 * Path to local provider has to be configured at this
381			 * point.
382			 */
383			if (curres->hr_localpath[0] == '\0') {
384				errx(EX_CONFIG,
385				    "path local component not configured for resource %s",
386				    curres->hr_name);
387			}
388
389			/* Put it onto resource list. */
390			TAILQ_INSERT_TAIL(&lconfig.hc_resources, curres, hr_next);
391			curres = NULL;
392		}
393	}
394	;
395
396resource_start:	STR
397	{
398		/*
399		 * Clear those, so we can tell if they were set at
400		 * resource-level or not.
401		 */
402		depth1_provname[0] = '\0';
403		depth1_localpath[0] = '\0';
404
405		curres = calloc(1, sizeof(*curres));
406		if (curres == NULL) {
407			errx(EX_TEMPFAIL,
408			    "cannot allocate memory for resource");
409		}
410		if (strlcpy(curres->hr_name, $1,
411		    sizeof(curres->hr_name)) >=
412		    sizeof(curres->hr_name)) {
413			errx(EX_CONFIG,
414			    "resource name (%s) too long", $1);
415		}
416		curres->hr_role = HAST_ROLE_INIT;
417		curres->hr_previous_role = HAST_ROLE_INIT;
418		curres->hr_replication = -1;
419		curres->hr_timeout = -1;
420		curres->hr_provname[0] = '\0';
421		curres->hr_localpath[0] = '\0';
422		curres->hr_localfd = -1;
423		curres->hr_remoteaddr[0] = '\0';
424		curres->hr_ggateunit = -1;
425	}
426	;
427
428resource_entries:
429	|
430	resource_entries resource_entry
431	;
432
433resource_entry:
434	replication_statement
435	|
436	timeout_statement
437	|
438	name_statement
439	|
440	local_statement
441	|
442	resource_node_statement
443	;
444
445name_statement:		NAME STR
446	{
447		switch (depth) {
448		case 1:
449			if (strlcpy(depth1_provname, $2,
450			    sizeof(depth1_provname)) >=
451			    sizeof(depth1_provname)) {
452				errx(EX_CONFIG, "name argument too long");
453			}
454			break;
455		case 2:
456			if (mynode) {
457				assert(curres != NULL);
458				if (strlcpy(curres->hr_provname, $2,
459				    sizeof(curres->hr_provname)) >=
460				    sizeof(curres->hr_provname)) {
461					errx(EX_CONFIG,
462					    "name argument too long");
463				}
464			}
465			break;
466		default:
467			assert(!"name at wrong depth level");
468		}
469	}
470	;
471
472local_statement:	LOCAL STR
473	{
474		switch (depth) {
475		case 1:
476			if (strlcpy(depth1_localpath, $2,
477			    sizeof(depth1_localpath)) >=
478			    sizeof(depth1_localpath)) {
479				errx(EX_CONFIG, "local argument too long");
480			}
481			break;
482		case 2:
483			if (mynode) {
484				assert(curres != NULL);
485				if (strlcpy(curres->hr_localpath, $2,
486				    sizeof(curres->hr_localpath)) >=
487				    sizeof(curres->hr_localpath)) {
488					errx(EX_CONFIG,
489					    "local argument too long");
490				}
491			}
492			break;
493		default:
494			assert(!"local at wrong depth level");
495		}
496	}
497	;
498
499resource_node_statement:ON resource_node_start OB resource_node_entries CB
500	{
501		mynode = false;
502	}
503	;
504
505resource_node_start:	STR
506	{
507		if (curres != NULL && isitme($1))
508			mynode = true;
509	}
510	;
511
512resource_node_entries:
513	|
514	resource_node_entries resource_node_entry
515	;
516
517resource_node_entry:
518	name_statement
519	|
520	local_statement
521	|
522	remote_statement
523	;
524
525remote_statement:	REMOTE STR
526	{
527		assert(depth == 2);
528		if (mynode) {
529			assert(curres != NULL);
530			if (strlcpy(curres->hr_remoteaddr, $2,
531			    sizeof(curres->hr_remoteaddr)) >=
532			    sizeof(curres->hr_remoteaddr)) {
533				errx(EX_CONFIG, "remote argument too long");
534			}
535		}
536	}
537	;
538