xref: /freebsd/sbin/hastd/parse.y (revision aa0a1e58)
1 %{
2 /*-
3  * Copyright (c) 2009-2010 The FreeBSD Foundation
4  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5  * All rights reserved.
6  *
7  * This software was developed by Pawel Jakub Dawidek under sponsorship from
8  * the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 
34 #include <sys/param.h>	/* MAXHOSTNAMELEN */
35 #include <sys/queue.h>
36 #include <sys/sysctl.h>
37 
38 #include <arpa/inet.h>
39 
40 #include <assert.h>
41 #include <err.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <sysexits.h>
45 #include <unistd.h>
46 
47 #include <pjdlog.h>
48 
49 #include "hast.h"
50 
51 extern int depth;
52 extern int lineno;
53 
54 extern FILE *yyin;
55 extern char *yytext;
56 
57 static struct hastd_config *lconfig;
58 static struct hast_resource *curres;
59 static bool mynode, hadmynode;
60 
61 static char depth0_control[HAST_ADDRSIZE];
62 static char depth0_listen[HAST_ADDRSIZE];
63 static int depth0_replication;
64 static int depth0_checksum;
65 static int depth0_compression;
66 static int depth0_timeout;
67 static char depth0_exec[PATH_MAX];
68 
69 static char depth1_provname[PATH_MAX];
70 static char depth1_localpath[PATH_MAX];
71 
72 extern void yyrestart(FILE *);
73 
74 static int
75 isitme(const char *name)
76 {
77 	char buf[MAXHOSTNAMELEN];
78 	char *pos;
79 	size_t bufsize;
80 
81 	/*
82 	 * First check if the give name matches our full hostname.
83 	 */
84 	if (gethostname(buf, sizeof(buf)) < 0) {
85 		pjdlog_errno(LOG_ERR, "gethostname() failed");
86 		return (-1);
87 	}
88 	if (strcmp(buf, name) == 0)
89 		return (1);
90 
91 	/*
92 	 * Now check if it matches first part of the host name.
93 	 */
94 	pos = strchr(buf, '.');
95 	if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0)
96 		return (1);
97 
98 	/*
99 	 * At the end check if name is equal to our host's UUID.
100 	 */
101 	bufsize = sizeof(buf);
102 	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
103 		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
104 		return (-1);
105 	}
106 	if (strcasecmp(buf, name) == 0)
107 		return (1);
108 
109 	/*
110 	 * Looks like this isn't about us.
111 	 */
112 	return (0);
113 }
114 
115 static int
116 node_names(char **namesp)
117 {
118 	static char names[MAXHOSTNAMELEN * 3];
119 	char buf[MAXHOSTNAMELEN];
120 	char *pos;
121 	size_t bufsize;
122 
123 	if (gethostname(buf, sizeof(buf)) < 0) {
124 		pjdlog_errno(LOG_ERR, "gethostname() failed");
125 		return (-1);
126 	}
127 
128 	/* First component of the host name. */
129 	pos = strchr(buf, '.');
130 	if (pos != NULL && pos != buf) {
131 		(void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
132 		    sizeof(names)));
133 		(void)strlcat(names, ", ", sizeof(names));
134 	}
135 
136 	/* Full host name. */
137 	(void)strlcat(names, buf, sizeof(names));
138 	(void)strlcat(names, ", ", sizeof(names));
139 
140 	/* Host UUID. */
141 	bufsize = sizeof(buf);
142 	if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
143 		pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
144 		return (-1);
145 	}
146 	(void)strlcat(names, buf, sizeof(names));
147 
148 	*namesp = names;
149 
150 	return (0);
151 }
152 
153 void
154 yyerror(const char *str)
155 {
156 
157 	pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
158 	    lineno, yytext, str);
159 }
160 
161 struct hastd_config *
162 yy_config_parse(const char *config, bool exitonerror)
163 {
164 	int ret;
165 
166 	curres = NULL;
167 	mynode = false;
168 	depth = 0;
169 	lineno = 0;
170 
171 	depth0_timeout = HAST_TIMEOUT;
172 	depth0_replication = HAST_REPLICATION_MEMSYNC;
173 	depth0_checksum = HAST_CHECKSUM_NONE;
174 	depth0_compression = HAST_COMPRESSION_HOLE;
175 	strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
176 	strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen));
177 	depth0_exec[0] = '\0';
178 
179 	lconfig = calloc(1, sizeof(*lconfig));
180 	if (lconfig == NULL) {
181 		pjdlog_error("Unable to allocate memory for configuration.");
182 		if (exitonerror)
183 			exit(EX_TEMPFAIL);
184 		return (NULL);
185 	}
186 
187 	TAILQ_INIT(&lconfig->hc_resources);
188 
189 	yyin = fopen(config, "r");
190 	if (yyin == NULL) {
191 		pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
192 		    config);
193 		yy_config_free(lconfig);
194 		if (exitonerror)
195 			exit(EX_OSFILE);
196 		return (NULL);
197 	}
198 	yyrestart(yyin);
199 	ret = yyparse();
200 	fclose(yyin);
201 	if (ret != 0) {
202 		yy_config_free(lconfig);
203 		if (exitonerror)
204 			exit(EX_CONFIG);
205 		return (NULL);
206 	}
207 
208 	/*
209 	 * Let's see if everything is set up.
210 	 */
211 	if (lconfig->hc_controladdr[0] == '\0') {
212 		strlcpy(lconfig->hc_controladdr, depth0_control,
213 		    sizeof(lconfig->hc_controladdr));
214 	}
215 	if (lconfig->hc_listenaddr[0] == '\0') {
216 		strlcpy(lconfig->hc_listenaddr, depth0_listen,
217 		    sizeof(lconfig->hc_listenaddr));
218 	}
219 	TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
220 		assert(curres->hr_provname[0] != '\0');
221 		assert(curres->hr_localpath[0] != '\0');
222 		assert(curres->hr_remoteaddr[0] != '\0');
223 
224 		if (curres->hr_replication == -1) {
225 			/*
226 			 * Replication is not set at resource-level.
227 			 * Use global or default setting.
228 			 */
229 			curres->hr_replication = depth0_replication;
230 		}
231 		if (curres->hr_checksum == -1) {
232 			/*
233 			 * Checksum is not set at resource-level.
234 			 * Use global or default setting.
235 			 */
236 			curres->hr_checksum = depth0_checksum;
237 		}
238 		if (curres->hr_compression == -1) {
239 			/*
240 			 * Compression is not set at resource-level.
241 			 * Use global or default setting.
242 			 */
243 			curres->hr_compression = depth0_compression;
244 		}
245 		if (curres->hr_timeout == -1) {
246 			/*
247 			 * Timeout is not set at resource-level.
248 			 * Use global or default setting.
249 			 */
250 			curres->hr_timeout = depth0_timeout;
251 		}
252 		if (curres->hr_exec[0] == '\0') {
253 			/*
254 			 * Exec is not set at resource-level.
255 			 * Use global or default setting.
256 			 */
257 			strlcpy(curres->hr_exec, depth0_exec,
258 			    sizeof(curres->hr_exec));
259 		}
260 	}
261 
262 	return (lconfig);
263 }
264 
265 void
266 yy_config_free(struct hastd_config *config)
267 {
268 	struct hast_resource *res;
269 
270 	while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
271 		TAILQ_REMOVE(&config->hc_resources, res, hr_next);
272 		free(res);
273 	}
274 	free(config);
275 }
276 %}
277 
278 %token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
279 %token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
280 %token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
281 %token NUM STR OB CB
282 
283 %type <num> replication_type
284 %type <num> checksum_type
285 %type <num> compression_type
286 
287 %union
288 {
289 	int num;
290 	char *str;
291 }
292 
293 %token <num> NUM
294 %token <str> STR
295 
296 %%
297 
298 statements:
299 	|
300 	statements statement
301 	;
302 
303 statement:
304 	control_statement
305 	|
306 	listen_statement
307 	|
308 	replication_statement
309 	|
310 	checksum_statement
311 	|
312 	compression_statement
313 	|
314 	timeout_statement
315 	|
316 	exec_statement
317 	|
318 	node_statement
319 	|
320 	resource_statement
321 	;
322 
323 control_statement:	CONTROL STR
324 	{
325 		switch (depth) {
326 		case 0:
327 			if (strlcpy(depth0_control, $2,
328 			    sizeof(depth0_control)) >=
329 			    sizeof(depth0_control)) {
330 				pjdlog_error("control argument is too long.");
331 				free($2);
332 				return (1);
333 			}
334 			break;
335 		case 1:
336 			if (!mynode)
337 				break;
338 			if (strlcpy(lconfig->hc_controladdr, $2,
339 			    sizeof(lconfig->hc_controladdr)) >=
340 			    sizeof(lconfig->hc_controladdr)) {
341 				pjdlog_error("control argument is too long.");
342 				free($2);
343 				return (1);
344 			}
345 			break;
346 		default:
347 			assert(!"control at wrong depth level");
348 		}
349 		free($2);
350 	}
351 	;
352 
353 listen_statement:	LISTEN STR
354 	{
355 		switch (depth) {
356 		case 0:
357 			if (strlcpy(depth0_listen, $2,
358 			    sizeof(depth0_listen)) >=
359 			    sizeof(depth0_listen)) {
360 				pjdlog_error("listen argument is too long.");
361 				free($2);
362 				return (1);
363 			}
364 			break;
365 		case 1:
366 			if (!mynode)
367 				break;
368 			if (strlcpy(lconfig->hc_listenaddr, $2,
369 			    sizeof(lconfig->hc_listenaddr)) >=
370 			    sizeof(lconfig->hc_listenaddr)) {
371 				pjdlog_error("listen argument is too long.");
372 				free($2);
373 				return (1);
374 			}
375 			break;
376 		default:
377 			assert(!"listen at wrong depth level");
378 		}
379 		free($2);
380 	}
381 	;
382 
383 replication_statement:	REPLICATION replication_type
384 	{
385 		switch (depth) {
386 		case 0:
387 			depth0_replication = $2;
388 			break;
389 		case 1:
390 			if (curres != NULL)
391 				curres->hr_replication = $2;
392 			break;
393 		default:
394 			assert(!"replication at wrong depth level");
395 		}
396 	}
397 	;
398 
399 replication_type:
400 	FULLSYNC	{ $$ = HAST_REPLICATION_FULLSYNC; }
401 	|
402 	MEMSYNC		{ $$ = HAST_REPLICATION_MEMSYNC; }
403 	|
404 	ASYNC		{ $$ = HAST_REPLICATION_ASYNC; }
405 	;
406 
407 checksum_statement:	CHECKSUM checksum_type
408 	{
409 		switch (depth) {
410 		case 0:
411 			depth0_checksum = $2;
412 			break;
413 		case 1:
414 			if (curres != NULL)
415 				curres->hr_checksum = $2;
416 			break;
417 		default:
418 			assert(!"checksum at wrong depth level");
419 		}
420 	}
421 	;
422 
423 checksum_type:
424 	NONE		{ $$ = HAST_CHECKSUM_NONE; }
425 	|
426 	CRC32		{ $$ = HAST_CHECKSUM_CRC32; }
427 	|
428 	SHA256		{ $$ = HAST_CHECKSUM_SHA256; }
429 	;
430 
431 compression_statement:	COMPRESSION compression_type
432 	{
433 		switch (depth) {
434 		case 0:
435 			depth0_compression = $2;
436 			break;
437 		case 1:
438 			if (curres != NULL)
439 				curres->hr_compression = $2;
440 			break;
441 		default:
442 			assert(!"compression at wrong depth level");
443 		}
444 	}
445 	;
446 
447 compression_type:
448 	NONE		{ $$ = HAST_COMPRESSION_NONE; }
449 	|
450 	HOLE		{ $$ = HAST_COMPRESSION_HOLE; }
451 	|
452 	LZF		{ $$ = HAST_COMPRESSION_LZF; }
453 	;
454 
455 timeout_statement:	TIMEOUT NUM
456 	{
457 		switch (depth) {
458 		case 0:
459 			depth0_timeout = $2;
460 			break;
461 		case 1:
462 			if (curres != NULL)
463 				curres->hr_timeout = $2;
464 			break;
465 		default:
466 			assert(!"timeout at wrong depth level");
467 		}
468 	}
469 	;
470 
471 exec_statement:		EXEC STR
472 	{
473 		switch (depth) {
474 		case 0:
475 			if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
476 			    sizeof(depth0_exec)) {
477 				pjdlog_error("Exec path is too long.");
478 				free($2);
479 				return (1);
480 			}
481 			break;
482 		case 1:
483 			if (curres == NULL)
484 				break;
485 			if (strlcpy(curres->hr_exec, $2,
486 			    sizeof(curres->hr_exec)) >=
487 			    sizeof(curres->hr_exec)) {
488 				pjdlog_error("Exec path is too long.");
489 				free($2);
490 				return (1);
491 			}
492 			break;
493 		default:
494 			assert(!"exec at wrong depth level");
495 		}
496 		free($2);
497 	}
498 	;
499 
500 node_statement:		ON node_start OB node_entries CB
501 	{
502 		mynode = false;
503 	}
504 	;
505 
506 node_start:	STR
507 	{
508 		switch (isitme($1)) {
509 		case -1:
510 			free($1);
511 			return (1);
512 		case 0:
513 			break;
514 		case 1:
515 			mynode = true;
516 			break;
517 		default:
518 			assert(!"invalid isitme() return value");
519 		}
520 		free($1);
521 	}
522 	;
523 
524 node_entries:
525 	|
526 	node_entries node_entry
527 	;
528 
529 node_entry:
530 	control_statement
531 	|
532 	listen_statement
533 	;
534 
535 resource_statement:	RESOURCE resource_start OB resource_entries CB
536 	{
537 		if (curres != NULL) {
538 			/*
539 			 * There must be section for this node, at least with
540 			 * remote address configuration.
541 			 */
542 			if (!hadmynode) {
543 				char *names;
544 
545 				if (node_names(&names) != 0)
546 					return (1);
547 				pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
548 				    curres->hr_name, names);
549 				return (1);
550 			}
551 
552 			/*
553 			 * Let's see there are some resource-level settings
554 			 * that we can use for node-level settings.
555 			 */
556 			if (curres->hr_provname[0] == '\0' &&
557 			    depth1_provname[0] != '\0') {
558 				/*
559 				 * Provider name is not set at node-level,
560 				 * but is set at resource-level, use it.
561 				 */
562 				strlcpy(curres->hr_provname, depth1_provname,
563 				    sizeof(curres->hr_provname));
564 			}
565 			if (curres->hr_localpath[0] == '\0' &&
566 			    depth1_localpath[0] != '\0') {
567 				/*
568 				 * Path to local provider is not set at
569 				 * node-level, but is set at resource-level,
570 				 * use it.
571 				 */
572 				strlcpy(curres->hr_localpath, depth1_localpath,
573 				    sizeof(curres->hr_localpath));
574 			}
575 
576 			/*
577 			 * If provider name is not given, use resource name
578 			 * as provider name.
579 			 */
580 			if (curres->hr_provname[0] == '\0') {
581 				strlcpy(curres->hr_provname, curres->hr_name,
582 				    sizeof(curres->hr_provname));
583 			}
584 
585 			/*
586 			 * Remote address has to be configured at this point.
587 			 */
588 			if (curres->hr_remoteaddr[0] == '\0') {
589 				pjdlog_error("Remote address not configured for resource %s.",
590 				    curres->hr_name);
591 				return (1);
592 			}
593 			/*
594 			 * Path to local provider has to be configured at this
595 			 * point.
596 			 */
597 			if (curres->hr_localpath[0] == '\0') {
598 				pjdlog_error("Path to local component not configured for resource %s.",
599 				    curres->hr_name);
600 				return (1);
601 			}
602 
603 			/* Put it onto resource list. */
604 			TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
605 			curres = NULL;
606 		}
607 	}
608 	;
609 
610 resource_start:	STR
611 	{
612 		/* Check if there is no duplicate entry. */
613 		TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
614 			if (strcmp(curres->hr_name, $1) == 0) {
615 				pjdlog_error("Resource %s configured more than once.",
616 				    curres->hr_name);
617 				free($1);
618 				return (1);
619 			}
620 		}
621 
622 		/*
623 		 * Clear those, so we can tell if they were set at
624 		 * resource-level or not.
625 		 */
626 		depth1_provname[0] = '\0';
627 		depth1_localpath[0] = '\0';
628 		hadmynode = false;
629 
630 		curres = calloc(1, sizeof(*curres));
631 		if (curres == NULL) {
632 			pjdlog_error("Unable to allocate memory for resource.");
633 			free($1);
634 			return (1);
635 		}
636 		if (strlcpy(curres->hr_name, $1,
637 		    sizeof(curres->hr_name)) >=
638 		    sizeof(curres->hr_name)) {
639 			pjdlog_error("Resource name is too long.");
640 			free($1);
641 			return (1);
642 		}
643 		free($1);
644 		curres->hr_role = HAST_ROLE_INIT;
645 		curres->hr_previous_role = HAST_ROLE_INIT;
646 		curres->hr_replication = -1;
647 		curres->hr_checksum = -1;
648 		curres->hr_compression = -1;
649 		curres->hr_timeout = -1;
650 		curres->hr_exec[0] = '\0';
651 		curres->hr_provname[0] = '\0';
652 		curres->hr_localpath[0] = '\0';
653 		curres->hr_localfd = -1;
654 		curres->hr_remoteaddr[0] = '\0';
655 		curres->hr_sourceaddr[0] = '\0';
656 		curres->hr_ggateunit = -1;
657 	}
658 	;
659 
660 resource_entries:
661 	|
662 	resource_entries resource_entry
663 	;
664 
665 resource_entry:
666 	replication_statement
667 	|
668 	checksum_statement
669 	|
670 	compression_statement
671 	|
672 	timeout_statement
673 	|
674 	exec_statement
675 	|
676 	name_statement
677 	|
678 	local_statement
679 	|
680 	resource_node_statement
681 	;
682 
683 name_statement:		NAME STR
684 	{
685 		switch (depth) {
686 		case 1:
687 			if (strlcpy(depth1_provname, $2,
688 			    sizeof(depth1_provname)) >=
689 			    sizeof(depth1_provname)) {
690 				pjdlog_error("name argument is too long.");
691 				free($2);
692 				return (1);
693 			}
694 			break;
695 		case 2:
696 			if (!mynode)
697 				break;
698 			assert(curres != NULL);
699 			if (strlcpy(curres->hr_provname, $2,
700 			    sizeof(curres->hr_provname)) >=
701 			    sizeof(curres->hr_provname)) {
702 				pjdlog_error("name argument is too long.");
703 				free($2);
704 				return (1);
705 			}
706 			break;
707 		default:
708 			assert(!"name at wrong depth level");
709 		}
710 		free($2);
711 	}
712 	;
713 
714 local_statement:	LOCAL STR
715 	{
716 		switch (depth) {
717 		case 1:
718 			if (strlcpy(depth1_localpath, $2,
719 			    sizeof(depth1_localpath)) >=
720 			    sizeof(depth1_localpath)) {
721 				pjdlog_error("local argument is too long.");
722 				free($2);
723 				return (1);
724 			}
725 			break;
726 		case 2:
727 			if (!mynode)
728 				break;
729 			assert(curres != NULL);
730 			if (strlcpy(curres->hr_localpath, $2,
731 			    sizeof(curres->hr_localpath)) >=
732 			    sizeof(curres->hr_localpath)) {
733 				pjdlog_error("local argument is too long.");
734 				free($2);
735 				return (1);
736 			}
737 			break;
738 		default:
739 			assert(!"local at wrong depth level");
740 		}
741 		free($2);
742 	}
743 	;
744 
745 resource_node_statement:ON resource_node_start OB resource_node_entries CB
746 	{
747 		mynode = false;
748 	}
749 	;
750 
751 resource_node_start:	STR
752 	{
753 		if (curres != NULL) {
754 			switch (isitme($1)) {
755 			case -1:
756 				free($1);
757 				return (1);
758 			case 0:
759 				break;
760 			case 1:
761 				mynode = hadmynode = true;
762 				break;
763 			default:
764 				assert(!"invalid isitme() return value");
765 			}
766 		}
767 		free($1);
768 	}
769 	;
770 
771 resource_node_entries:
772 	|
773 	resource_node_entries resource_node_entry
774 	;
775 
776 resource_node_entry:
777 	name_statement
778 	|
779 	local_statement
780 	|
781 	remote_statement
782 	|
783 	source_statement
784 	;
785 
786 remote_statement:	REMOTE STR
787 	{
788 		assert(depth == 2);
789 		if (mynode) {
790 			assert(curres != NULL);
791 			if (strlcpy(curres->hr_remoteaddr, $2,
792 			    sizeof(curres->hr_remoteaddr)) >=
793 			    sizeof(curres->hr_remoteaddr)) {
794 				pjdlog_error("remote argument is too long.");
795 				free($2);
796 				return (1);
797 			}
798 		}
799 		free($2);
800 	}
801 	;
802 
803 source_statement:	SOURCE STR
804 	{
805 		assert(depth == 2);
806 		if (mynode) {
807 			assert(curres != NULL);
808 			if (strlcpy(curres->hr_sourceaddr, $2,
809 			    sizeof(curres->hr_sourceaddr)) >=
810 			    sizeof(curres->hr_sourceaddr)) {
811 				pjdlog_error("source argument is too long.");
812 				free($2);
813 				return (1);
814 			}
815 		}
816 		free($2);
817 	}
818 	;
819