]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sbin/hastd/parse.y
MFC r219351, r219354, r219369, r219370, r219371, r219372, r219373,
[FreeBSD/stable/8.git] / sbin / hastd / parse.y
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         ;