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