parse.y revision 211886
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 211886 2010-08-27 15:16:52Z 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				return (1);
268			}
269			break;
270		case 1:
271			if (!mynode)
272				break;
273			if (strlcpy(lconfig->hc_controladdr, $2,
274			    sizeof(lconfig->hc_controladdr)) >=
275			    sizeof(lconfig->hc_controladdr)) {
276				pjdlog_error("control argument is too long.");
277				return (1);
278			}
279			break;
280		default:
281			assert(!"control at wrong depth level");
282		}
283	}
284	;
285
286listen_statement:	LISTEN STR
287	{
288		switch (depth) {
289		case 0:
290			if (strlcpy(depth0_listen, $2,
291			    sizeof(depth0_listen)) >=
292			    sizeof(depth0_listen)) {
293				pjdlog_error("listen argument is too long.");
294				return (1);
295			}
296			break;
297		case 1:
298			if (!mynode)
299				break;
300			if (strlcpy(lconfig->hc_listenaddr, $2,
301			    sizeof(lconfig->hc_listenaddr)) >=
302			    sizeof(lconfig->hc_listenaddr)) {
303				pjdlog_error("listen argument is too long.");
304				return (1);
305			}
306			break;
307		default:
308			assert(!"listen at wrong depth level");
309		}
310	}
311	;
312
313replication_statement:	REPLICATION replication_type
314	{
315		switch (depth) {
316		case 0:
317			depth0_replication = $2;
318			break;
319		case 1:
320			if (curres != NULL)
321				curres->hr_replication = $2;
322			break;
323		default:
324			assert(!"replication at wrong depth level");
325		}
326	}
327	;
328
329replication_type:
330	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
331	|
332	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
333	|
334	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
335	;
336
337timeout_statement:	TIMEOUT NUM
338	{
339		switch (depth) {
340		case 0:
341			depth0_timeout = $2;
342			break;
343		case 1:
344			if (curres != NULL)
345				curres->hr_timeout = $2;
346			break;
347		default:
348			assert(!"timeout at wrong depth level");
349		}
350	}
351	;
352
353exec_statement:		EXEC STR
354	{
355		switch (depth) {
356		case 0:
357			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
358			    sizeof(depth0_exec)) {
359				pjdlog_error("Exec path is too long.");
360				return (1);
361			}
362			break;
363		case 1:
364			if (curres == NULL)
365				break;
366			if (strlcpy(curres->hr_exec, $2,
367			    sizeof(curres->hr_exec)) >=
368			    sizeof(curres->hr_exec)) {
369				pjdlog_error("Exec path is too long.");
370				return (1);
371			}
372			break;
373		default:
374			assert(!"exec at wrong depth level");
375		}
376	}
377	;
378
379node_statement:		ON node_start OB node_entries CB
380	{
381		mynode = false;
382	}
383	;
384
385node_start:	STR
386	{
387		switch (isitme($1)) {
388		case -1:
389			return (1);
390		case 0:
391			break;
392		case 1:
393			mynode = true;
394			break;
395		default:
396			assert(!"invalid isitme() return value");
397		}
398	}
399	;
400
401node_entries:
402	|
403	node_entries node_entry
404	;
405
406node_entry:
407	control_statement
408	|
409	listen_statement
410	;
411
412resource_statement:	RESOURCE resource_start OB resource_entries CB
413	{
414		if (curres != NULL) {
415			/*
416			 * Let's see there are some resource-level settings
417			 * that we can use for node-level settings.
418			 */
419			if (curres->hr_provname[0] == '\0' &&
420			    depth1_provname[0] != '\0') {
421				/*
422				 * Provider name is not set at node-level,
423				 * but is set at resource-level, use it.
424				 */
425				strlcpy(curres->hr_provname, depth1_provname,
426				    sizeof(curres->hr_provname));
427			}
428			if (curres->hr_localpath[0] == '\0' &&
429			    depth1_localpath[0] != '\0') {
430				/*
431				 * Path to local provider is not set at
432				 * node-level, but is set at resource-level,
433				 * use it.
434				 */
435				strlcpy(curres->hr_localpath, depth1_localpath,
436				    sizeof(curres->hr_localpath));
437			}
438
439			/*
440			 * If provider name is not given, use resource name
441			 * as provider name.
442			 */
443			if (curres->hr_provname[0] == '\0') {
444				strlcpy(curres->hr_provname, curres->hr_name,
445				    sizeof(curres->hr_provname));
446			}
447
448			/*
449			 * Remote address has to be configured at this point.
450			 */
451			if (curres->hr_remoteaddr[0] == '\0') {
452				pjdlog_error("Remote address not configured for resource %s.",
453				    curres->hr_name);
454				return (1);
455			}
456			/*
457			 * Path to local provider has to be configured at this
458			 * point.
459			 */
460			if (curres->hr_localpath[0] == '\0') {
461				pjdlog_error("Path to local component not configured for resource %s.",
462				    curres->hr_name);
463				return (1);
464			}
465
466			/* Put it onto resource list. */
467			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
468			curres = NULL;
469		}
470	}
471	;
472
473resource_start:	STR
474	{
475		/*
476		 * Clear those, so we can tell if they were set at
477		 * resource-level or not.
478		 */
479		depth1_provname[0] = '\0';
480		depth1_localpath[0] = '\0';
481
482		curres = calloc(1, sizeof(*curres));
483		if (curres == NULL) {
484			pjdlog_error("Unable to allocate memory for resource.");
485			return (1);
486		}
487		if (strlcpy(curres->hr_name, $1,
488		    sizeof(curres->hr_name)) >=
489		    sizeof(curres->hr_name)) {
490			pjdlog_error("Resource name is too long.");
491			return (1);
492		}
493		curres->hr_role = HAST_ROLE_INIT;
494		curres->hr_previous_role = HAST_ROLE_INIT;
495		curres->hr_replication = -1;
496		curres->hr_timeout = -1;
497		curres->hr_exec[0] = '\0';
498		curres->hr_provname[0] = '\0';
499		curres->hr_localpath[0] = '\0';
500		curres->hr_localfd = -1;
501		curres->hr_remoteaddr[0] = '\0';
502		curres->hr_ggateunit = -1;
503	}
504	;
505
506resource_entries:
507	|
508	resource_entries resource_entry
509	;
510
511resource_entry:
512	replication_statement
513	|
514	timeout_statement
515	|
516	exec_statement
517	|
518	name_statement
519	|
520	local_statement
521	|
522	resource_node_statement
523	;
524
525name_statement:		NAME STR
526	{
527		switch (depth) {
528		case 1:
529			if (strlcpy(depth1_provname, $2,
530			    sizeof(depth1_provname)) >=
531			    sizeof(depth1_provname)) {
532				pjdlog_error("name argument is too long.");
533				return (1);
534			}
535			break;
536		case 2:
537			if (!mynode)
538				break;
539			assert(curres != NULL);
540			if (strlcpy(curres->hr_provname, $2,
541			    sizeof(curres->hr_provname)) >=
542			    sizeof(curres->hr_provname)) {
543				pjdlog_error("name argument is too long.");
544				return (1);
545			}
546			break;
547		default:
548			assert(!"name at wrong depth level");
549		}
550	}
551	;
552
553local_statement:	LOCAL STR
554	{
555		switch (depth) {
556		case 1:
557			if (strlcpy(depth1_localpath, $2,
558			    sizeof(depth1_localpath)) >=
559			    sizeof(depth1_localpath)) {
560				pjdlog_error("local argument is too long.");
561				return (1);
562			}
563			break;
564		case 2:
565			if (!mynode)
566				break;
567			assert(curres != NULL);
568			if (strlcpy(curres->hr_localpath, $2,
569			    sizeof(curres->hr_localpath)) >=
570			    sizeof(curres->hr_localpath)) {
571				pjdlog_error("local argument is too long.");
572				return (1);
573			}
574			break;
575		default:
576			assert(!"local at wrong depth level");
577		}
578	}
579	;
580
581resource_node_statement:ON resource_node_start OB resource_node_entries CB
582	{
583		mynode = false;
584	}
585	;
586
587resource_node_start:	STR
588	{
589		if (curres != NULL) {
590			switch (isitme($1)) {
591			case -1:
592				return (1);
593			case 0:
594				break;
595			case 1:
596				mynode = true;
597				break;
598			default:
599				assert(!"invalid isitme() return value");
600			}
601		}
602	}
603	;
604
605resource_node_entries:
606	|
607	resource_node_entries resource_node_entry
608	;
609
610resource_node_entry:
611	name_statement
612	|
613	local_statement
614	|
615	remote_statement
616	;
617
618remote_statement:	REMOTE STR
619	{
620		assert(depth == 2);
621		if (mynode) {
622			assert(curres != NULL);
623			if (strlcpy(curres->hr_remoteaddr, $2,
624			    sizeof(curres->hr_remoteaddr)) >=
625			    sizeof(curres->hr_remoteaddr)) {
626				pjdlog_error("remote argument is too long.");
627				return (1);
628			}
629		}
630	}
631	;
632