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