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