parse.y revision 211886
1255332Scy%{
2255332Scy/*-
3255332Scy * Copyright (c) 2009-2010 The FreeBSD Foundation
4255332Scy * All rights reserved.
5255332Scy *
6255332Scy * This software was developed by Pawel Jakub Dawidek under sponsorship from
7255332Scy * the FreeBSD Foundation.
8255332Scy *
9255332Scy * Redistribution and use in source and binary forms, with or without
10255332Scy * modification, are permitted provided that the following conditions
11255332Scy * are met:
12255332Scy * 1. Redistributions of source code must retain the above copyright
13255332Scy *    notice, this list of conditions and the following disclaimer.
14255332Scy * 2. Redistributions in binary form must reproduce the above copyright
15255332Scy *    notice, this list of conditions and the following disclaimer in the
16255332Scy *    documentation and/or other materials provided with the distribution.
17255332Scy *
18255332Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19255332Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20255332Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21255332Scy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22255332Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23255332Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24255332Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25255332Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26255332Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27255332Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28255332Scy * SUCH DAMAGE.
29255332Scy *
30255332Scy * $FreeBSD: head/sbin/hastd/parse.y 211886 2010-08-27 15:16:52Z pjd $
31255332Scy */
32255332Scy
33255332Scy#include <sys/param.h>	/* MAXHOSTNAMELEN */
34255332Scy#include <sys/queue.h>
35255332Scy#include <sys/sysctl.h>
36255332Scy
37255332Scy#include <arpa/inet.h>
38255332Scy
39255332Scy#include <assert.h>
40255332Scy#include <err.h>
41255332Scy#include <stdio.h>
42255332Scy#include <string.h>
43255332Scy#include <sysexits.h>
44255332Scy#include <unistd.h>
45255332Scy
46255332Scy#include <pjdlog.h>
47255332Scy
48255332Scy#include "hast.h"
49255332Scy
50255332Scyextern int depth;
51255332Scyextern int lineno;
52255332Scy
53255332Scyextern FILE *yyin;
54255332Scyextern char *yytext;
55255332Scy
56255332Scystatic struct hastd_config *lconfig;
57255332Scystatic struct hast_resource *curres;
58255332Scystatic bool mynode;
59255332Scy
60255332Scystatic char depth0_control[HAST_ADDRSIZE];
61255332Scystatic char depth0_listen[HAST_ADDRSIZE];
62255332Scystatic int depth0_replication;
63255332Scystatic int depth0_timeout;
64255332Scystatic char depth0_exec[PATH_MAX];
65255332Scy
66255332Scystatic char depth1_provname[PATH_MAX];
67255332Scystatic char depth1_localpath[PATH_MAX];
68255332Scy
69255332Scyextern void yyrestart(FILE *);
70255332Scy
71255332Scystatic int
72255332Scyisitme(const char *name)
73255332Scy{
74255332Scy	char buf[MAXHOSTNAMELEN];
75255332Scy	char *pos;
76255332Scy	size_t bufsize;
77255332Scy
78255332Scy	/*
79255332Scy	 * First check if the give name matches our full hostname.
80255332Scy	 */
81255332Scy	if (gethostname(buf, sizeof(buf)) < 0) {
82255332Scy		pjdlog_errno(LOG_ERR, "gethostname() failed");
83255332Scy		return (-1);
84255332Scy	}
85255332Scy	if (strcmp(buf, name) == 0)
86255332Scy		return (1);
87255332Scy
88255332Scy	/*
89255332Scy	 * Now check if it matches first part of the host name.
90255332Scy	 */
91255332Scy	pos = strchr(buf, '.');
92255332Scy	if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0)
93255332Scy		return (1);
94255332Scy
95255332Scy	/*
96255332Scy	 * At the end check if name is equal to our host's UUID.
97255332Scy	 */
98255332Scy	bufsize = sizeof(buf);
99255332Scy	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
100255332Scy		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
101255332Scy		return (-1);
102255332Scy	}
103255332Scy	if (strcasecmp(buf, name) == 0)
104255332Scy		return (1);
105255332Scy
106255332Scy	/*
107255332Scy	 * Looks like this isn't about us.
108255332Scy	 */
109255332Scy	return (0);
110255332Scy}
111255332Scy
112255332Scyvoid
113255332Scyyyerror(const char *str)
114255332Scy{
115255332Scy
116255332Scy	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
117255332Scy	    lineno, yytext, str);
118255332Scy}
119255332Scy
120255332Scystruct hastd_config *
121255332Scyyy_config_parse(const char *config, bool exitonerror)
122255332Scy{
123255332Scy	int ret;
124255332Scy
125255332Scy	curres = NULL;
126255332Scy	mynode = false;
127255332Scy	depth = 0;
128255332Scy	lineno = 0;
129255332Scy
130255332Scy	depth0_timeout = HAST_TIMEOUT;
131255332Scy	depth0_replication = HAST_REPLICATION_MEMSYNC;
132255332Scy	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
133255332Scy	strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen));
134255332Scy	depth0_exec[0] = '\0';
135255332Scy
136255332Scy	lconfig = calloc(1, sizeof(*lconfig));
137255332Scy	if (lconfig == NULL) {
138255332Scy		pjdlog_error("Unable to allocate memory for configuration.");
139255332Scy		if (exitonerror)
140255332Scy			exit(EX_TEMPFAIL);
141255332Scy		return (NULL);
142255332Scy	}
143255332Scy
144255332Scy	TAILQ_INIT(&lconfig->hc_resources);
145255332Scy
146255332Scy	yyin = fopen(config, "r");
147255332Scy	if (yyin == NULL) {
148255332Scy		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
149255332Scy		    config);
150255332Scy		yy_config_free(lconfig);
151255332Scy		if (exitonerror)
152255332Scy			exit(EX_OSFILE);
153255332Scy		return (NULL);
154255332Scy	}
155255332Scy	yyrestart(yyin);
156255332Scy	ret = yyparse();
157255332Scy	fclose(yyin);
158255332Scy	if (ret != 0) {
159255332Scy		yy_config_free(lconfig);
160255332Scy		if (exitonerror)
161255332Scy			exit(EX_CONFIG);
162255332Scy		return (NULL);
163255332Scy	}
164255332Scy
165255332Scy	/*
166255332Scy	 * Let's see if everything is set up.
167255332Scy	 */
168255332Scy	if (lconfig->hc_controladdr[0] == '\0') {
169255332Scy		strlcpy(lconfig->hc_controladdr, depth0_control,
170255332Scy		    sizeof(lconfig->hc_controladdr));
171255332Scy	}
172255332Scy	if (lconfig->hc_listenaddr[0] == '\0') {
173255332Scy		strlcpy(lconfig->hc_listenaddr, depth0_listen,
174255332Scy		    sizeof(lconfig->hc_listenaddr));
175255332Scy	}
176255332Scy	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
177255332Scy		assert(curres->hr_provname[0] != '\0');
178255332Scy		assert(curres->hr_localpath[0] != '\0');
179255332Scy		assert(curres->hr_remoteaddr[0] != '\0');
180255332Scy
181255332Scy		if (curres->hr_replication == -1) {
182255332Scy			/*
183255332Scy			 * Replication is not set at resource-level.
184255332Scy			 * Use global or default setting.
185255332Scy			 */
186255332Scy			curres->hr_replication = depth0_replication;
187255332Scy		}
188255332Scy		if (curres->hr_timeout == -1) {
189255332Scy			/*
190255332Scy			 * Timeout is not set at resource-level.
191255332Scy			 * Use global or default setting.
192255332Scy			 */
193255332Scy			curres->hr_timeout = depth0_timeout;
194255332Scy		}
195255332Scy		if (curres->hr_exec[0] == '\0') {
196255332Scy			/*
197255332Scy			 * Exec is not set at resource-level.
198255332Scy			 * Use global or default setting.
199255332Scy			 */
200255332Scy			strlcpy(curres->hr_exec, depth0_exec,
201255332Scy			    sizeof(curres->hr_exec));
202255332Scy		}
203255332Scy	}
204255332Scy
205255332Scy	return (lconfig);
206255332Scy}
207255332Scy
208255332Scyvoid
209255332Scyyy_config_free(struct hastd_config *config)
210255332Scy{
211255332Scy	struct hast_resource *res;
212255332Scy
213255332Scy	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
214255332Scy		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
215255332Scy		free(res);
216255332Scy	}
217255332Scy	free(config);
218255332Scy}
219255332Scy%}
220255332Scy
221255332Scy%token CONTROL LISTEN PORT REPLICATION TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON
222255332Scy%token FULLSYNC MEMSYNC ASYNC
223255332Scy%token NUM STR OB CB
224255332Scy
225255332Scy%type <num> replication_type
226255332Scy
227255332Scy%union
228255332Scy{
229255332Scy	int num;
230255332Scy	char *str;
231255332Scy}
232255332Scy
233255332Scy%token <num> NUM
234255332Scy%token <str> STR
235255332Scy
236255332Scy%%
237255332Scy
238255332Scystatements:
239255332Scy	|
240255332Scy	statements statement
241255332Scy	;
242255332Scy
243255332Scystatement:
244255332Scy	control_statement
245255332Scy	|
246255332Scy	listen_statement
247255332Scy	|
248255332Scy	replication_statement
249255332Scy	|
250255332Scy	timeout_statement
251255332Scy	|
252255332Scy	exec_statement
253255332Scy	|
254255332Scy	node_statement
255255332Scy	|
256255332Scy	resource_statement
257255332Scy	;
258255332Scy
259255332Scycontrol_statement:	CONTROL STR
260255332Scy	{
261255332Scy		switch (depth) {
262255332Scy		case 0:
263255332Scy			if (strlcpy(depth0_control, $2,
264255332Scy			    sizeof(depth0_control)) >=
265255332Scy			    sizeof(depth0_control)) {
266255332Scy				pjdlog_error("control argument is too long.");
267255332Scy				return (1);
268255332Scy			}
269255332Scy			break;
270255332Scy		case 1:
271255332Scy			if (!mynode)
272255332Scy				break;
273255332Scy			if (strlcpy(lconfig->hc_controladdr, $2,
274255332Scy			    sizeof(lconfig->hc_controladdr)) >=
275255332Scy			    sizeof(lconfig->hc_controladdr)) {
276255332Scy				pjdlog_error("control argument is too long.");
277255332Scy				return (1);
278255332Scy			}
279255332Scy			break;
280255332Scy		default:
281255332Scy			assert(!"control at wrong depth level");
282255332Scy		}
283255332Scy	}
284255332Scy	;
285255332Scy
286255332Scylisten_statement:	LISTEN STR
287255332Scy	{
288255332Scy		switch (depth) {
289255332Scy		case 0:
290255332Scy			if (strlcpy(depth0_listen, $2,
291255332Scy			    sizeof(depth0_listen)) >=
292255332Scy			    sizeof(depth0_listen)) {
293255332Scy				pjdlog_error("listen argument is too long.");
294255332Scy				return (1);
295255332Scy			}
296255332Scy			break;
297255332Scy		case 1:
298255332Scy			if (!mynode)
299255332Scy				break;
300255332Scy			if (strlcpy(lconfig->hc_listenaddr, $2,
301255332Scy			    sizeof(lconfig->hc_listenaddr)) >=
302255332Scy			    sizeof(lconfig->hc_listenaddr)) {
303255332Scy				pjdlog_error("listen argument is too long.");
304255332Scy				return (1);
305255332Scy			}
306255332Scy			break;
307255332Scy		default:
308255332Scy			assert(!"listen at wrong depth level");
309255332Scy		}
310255332Scy	}
311255332Scy	;
312255332Scy
313255332Scyreplication_statement:	REPLICATION replication_type
314255332Scy	{
315255332Scy		switch (depth) {
316255332Scy		case 0:
317255332Scy			depth0_replication = $2;
318255332Scy			break;
319255332Scy		case 1:
320255332Scy			if (curres != NULL)
321255332Scy				curres->hr_replication = $2;
322255332Scy			break;
323255332Scy		default:
324255332Scy			assert(!"replication at wrong depth level");
325255332Scy		}
326255332Scy	}
327255332Scy	;
328255332Scy
329255332Scyreplication_type:
330255332Scy	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
331255332Scy	|
332255332Scy	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
333255332Scy	|
334255332Scy	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
335255332Scy	;
336255332Scy
337255332Scytimeout_statement:	TIMEOUT NUM
338255332Scy	{
339255332Scy		switch (depth) {
340255332Scy		case 0:
341255332Scy			depth0_timeout = $2;
342255332Scy			break;
343255332Scy		case 1:
344255332Scy			if (curres != NULL)
345255332Scy				curres->hr_timeout = $2;
346255332Scy			break;
347255332Scy		default:
348255332Scy			assert(!"timeout at wrong depth level");
349255332Scy		}
350255332Scy	}
351255332Scy	;
352255332Scy
353255332Scyexec_statement:		EXEC STR
354255332Scy	{
355255332Scy		switch (depth) {
356255332Scy		case 0:
357255332Scy			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
358255332Scy			    sizeof(depth0_exec)) {
359255332Scy				pjdlog_error("Exec path is too long.");
360255332Scy				return (1);
361255332Scy			}
362255332Scy			break;
363255332Scy		case 1:
364255332Scy			if (curres == NULL)
365255332Scy				break;
366255332Scy			if (strlcpy(curres->hr_exec, $2,
367255332Scy			    sizeof(curres->hr_exec)) >=
368255332Scy			    sizeof(curres->hr_exec)) {
369255332Scy				pjdlog_error("Exec path is too long.");
370255332Scy				return (1);
371255332Scy			}
372255332Scy			break;
373255332Scy		default:
374255332Scy			assert(!"exec at wrong depth level");
375255332Scy		}
376255332Scy	}
377255332Scy	;
378255332Scy
379255332Scynode_statement:		ON node_start OB node_entries CB
380255332Scy	{
381255332Scy		mynode = false;
382255332Scy	}
383255332Scy	;
384255332Scy
385255332Scynode_start:	STR
386255332Scy	{
387255332Scy		switch (isitme($1)) {
388255332Scy		case -1:
389255332Scy			return (1);
390255332Scy		case 0:
391255332Scy			break;
392255332Scy		case 1:
393255332Scy			mynode = true;
394255332Scy			break;
395255332Scy		default:
396255332Scy			assert(!"invalid isitme() return value");
397255332Scy		}
398255332Scy	}
399255332Scy	;
400255332Scy
401255332Scynode_entries:
402255332Scy	|
403255332Scy	node_entries node_entry
404255332Scy	;
405255332Scy
406255332Scynode_entry:
407255332Scy	control_statement
408255332Scy	|
409255332Scy	listen_statement
410255332Scy	;
411255332Scy
412255332Scyresource_statement:	RESOURCE resource_start OB resource_entries CB
413255332Scy	{
414255332Scy		if (curres != NULL) {
415255332Scy			/*
416255332Scy			 * Let's see there are some resource-level settings
417255332Scy			 * that we can use for node-level settings.
418255332Scy			 */
419255332Scy			if (curres->hr_provname[0] == '\0' &&
420255332Scy			    depth1_provname[0] != '\0') {
421255332Scy				/*
422255332Scy				 * Provider name is not set at node-level,
423255332Scy				 * but is set at resource-level, use it.
424255332Scy				 */
425255332Scy				strlcpy(curres->hr_provname, depth1_provname,
426255332Scy				    sizeof(curres->hr_provname));
427255332Scy			}
428255332Scy			if (curres->hr_localpath[0] == '\0' &&
429255332Scy			    depth1_localpath[0] != '\0') {
430255332Scy				/*
431255332Scy				 * Path to local provider is not set at
432255332Scy				 * node-level, but is set at resource-level,
433255332Scy				 * use it.
434255332Scy				 */
435255332Scy				strlcpy(curres->hr_localpath, depth1_localpath,
436255332Scy				    sizeof(curres->hr_localpath));
437255332Scy			}
438255332Scy
439255332Scy			/*
440255332Scy			 * If provider name is not given, use resource name
441255332Scy			 * as provider name.
442255332Scy			 */
443255332Scy			if (curres->hr_provname[0] == '\0') {
444255332Scy				strlcpy(curres->hr_provname, curres->hr_name,
445255332Scy				    sizeof(curres->hr_provname));
446255332Scy			}
447255332Scy
448255332Scy			/*
449255332Scy			 * Remote address has to be configured at this point.
450255332Scy			 */
451255332Scy			if (curres->hr_remoteaddr[0] == '\0') {
452255332Scy				pjdlog_error("Remote address not configured for resource %s.",
453255332Scy				    curres->hr_name);
454255332Scy				return (1);
455255332Scy			}
456255332Scy			/*
457255332Scy			 * Path to local provider has to be configured at this
458255332Scy			 * point.
459255332Scy			 */
460255332Scy			if (curres->hr_localpath[0] == '\0') {
461255332Scy				pjdlog_error("Path to local component not configured for resource %s.",
462255332Scy				    curres->hr_name);
463255332Scy				return (1);
464255332Scy			}
465255332Scy
466255332Scy			/* Put it onto resource list. */
467255332Scy			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
468255332Scy			curres = NULL;
469255332Scy		}
470255332Scy	}
471255332Scy	;
472255332Scy
473255332Scyresource_start:	STR
474255332Scy	{
475255332Scy		/*
476255332Scy		 * Clear those, so we can tell if they were set at
477255332Scy		 * resource-level or not.
478255332Scy		 */
479255332Scy		depth1_provname[0] = '\0';
480255332Scy		depth1_localpath[0] = '\0';
481255332Scy
482255332Scy		curres = calloc(1, sizeof(*curres));
483255332Scy		if (curres == NULL) {
484255332Scy			pjdlog_error("Unable to allocate memory for resource.");
485255332Scy			return (1);
486255332Scy		}
487255332Scy		if (strlcpy(curres->hr_name, $1,
488255332Scy		    sizeof(curres->hr_name)) >=
489255332Scy		    sizeof(curres->hr_name)) {
490255332Scy			pjdlog_error("Resource name is too long.");
491255332Scy			return (1);
492255332Scy		}
493255332Scy		curres->hr_role = HAST_ROLE_INIT;
494255332Scy		curres->hr_previous_role = HAST_ROLE_INIT;
495255332Scy		curres->hr_replication = -1;
496255332Scy		curres->hr_timeout = -1;
497255332Scy		curres->hr_exec[0] = '\0';
498255332Scy		curres->hr_provname[0] = '\0';
499255332Scy		curres->hr_localpath[0] = '\0';
500255332Scy		curres->hr_localfd = -1;
501255332Scy		curres->hr_remoteaddr[0] = '\0';
502255332Scy		curres->hr_ggateunit = -1;
503255332Scy	}
504255332Scy	;
505255332Scy
506255332Scyresource_entries:
507255332Scy	|
508255332Scy	resource_entries resource_entry
509255332Scy	;
510255332Scy
511255332Scyresource_entry:
512255332Scy	replication_statement
513255332Scy	|
514255332Scy	timeout_statement
515255332Scy	|
516255332Scy	exec_statement
517255332Scy	|
518255332Scy	name_statement
519255332Scy	|
520255332Scy	local_statement
521255332Scy	|
522255332Scy	resource_node_statement
523255332Scy	;
524255332Scy
525255332Scyname_statement:		NAME STR
526255332Scy	{
527255332Scy		switch (depth) {
528255332Scy		case 1:
529255332Scy			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