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