xref: /freebsd/sbin/hastd/parse.y (revision dadef94c7a762d05890e2891bc4a7d1dfe0cf758)
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 <pjdlog.h>
47 
48 #include "hast.h"
49 
50 extern int depth;
51 extern int lineno;
52 
53 extern FILE *yyin;
54 extern char *yytext;
55 
56 static struct hastd_config *lconfig;
57 static struct hast_resource *curres;
58 static bool mynode;
59 
60 static char depth0_control[HAST_ADDRSIZE];
61 static char depth0_listen[HAST_ADDRSIZE];
62 static int depth0_replication;
63 static int depth0_timeout;
64 static char depth0_exec[PATH_MAX];
65 
66 static char depth1_provname[PATH_MAX];
67 static char depth1_localpath[PATH_MAX];
68 
69 extern void yyrestart(FILE *);
70 
71 static int
72 isitme(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 
112 void
113 yyerror(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 
120 struct hastd_config *
121 yy_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 
208 void
209 yy_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 
238 statements:
239 	|
240 	statements statement
241 	;
242 
243 statement:
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 
259 control_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 
286 listen_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 
313 replication_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 
329 replication_type:
330 	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
331 	|
332 	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
333 	|
334 	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
335 	;
336 
337 timeout_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 
353 exec_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 
379 node_statement:		ON node_start OB node_entries CB
380 	{
381 		mynode = false;
382 	}
383 	;
384 
385 node_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 
401 node_entries:
402 	|
403 	node_entries node_entry
404 	;
405 
406 node_entry:
407 	control_statement
408 	|
409 	listen_statement
410 	;
411 
412 resource_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 
473 resource_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 
506 resource_entries:
507 	|
508 	resource_entries resource_entry
509 	;
510 
511 resource_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 
525 name_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 
553 local_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 
581 resource_node_statement:ON resource_node_start OB resource_node_entries CB
582 	{
583 		mynode = false;
584 	}
585 	;
586 
587 resource_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 
605 resource_node_entries:
606 	|
607 	resource_node_entries resource_node_entry
608 	;
609 
610 resource_node_entry:
611 	name_statement
612 	|
613 	local_statement
614 	|
615 	remote_statement
616 	;
617 
618 remote_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