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