]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sbin/hastd/parse.y
MFC r216477,r216478,r216479,r216494,r216721,r216722:
[FreeBSD/stable/8.git] / sbin / hastd / parse.y
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, hadmynode;
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 static int
113 node_names(char **namesp)
114 {
115         static char names[MAXHOSTNAMELEN * 3];
116         char buf[MAXHOSTNAMELEN];
117         char *pos;
118         size_t bufsize;
119
120         if (gethostname(buf, sizeof(buf)) < 0) {
121                 pjdlog_errno(LOG_ERR, "gethostname() failed");
122                 return (-1);
123         }
124
125         /* First component of the host name. */
126         pos = strchr(buf, '.');
127         if (pos != NULL && pos != buf) {
128                 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
129                     sizeof(names)));
130                 (void)strlcat(names, ", ", sizeof(names));
131         }
132
133         /* Full host name. */
134         (void)strlcat(names, buf, sizeof(names));
135         (void)strlcat(names, ", ", sizeof(names));
136
137         /* Host UUID. */
138         bufsize = sizeof(buf);
139         if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
140                 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
141                 return (-1);
142         }
143         (void)strlcat(names, buf, sizeof(names));
144
145         *namesp = names;
146
147         return (0);
148 }
149
150 void
151 yyerror(const char *str)
152 {
153
154         pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
155             lineno, yytext, str);
156 }
157
158 struct hastd_config *
159 yy_config_parse(const char *config, bool exitonerror)
160 {
161         int ret;
162
163         curres = NULL;
164         mynode = false;
165         depth = 0;
166         lineno = 0;
167
168         depth0_timeout = HAST_TIMEOUT;
169         depth0_replication = HAST_REPLICATION_MEMSYNC;
170         strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
171         strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen));
172         depth0_exec[0] = '\0';
173
174         lconfig = calloc(1, sizeof(*lconfig));
175         if (lconfig == NULL) {
176                 pjdlog_error("Unable to allocate memory for configuration.");
177                 if (exitonerror)
178                         exit(EX_TEMPFAIL);
179                 return (NULL);
180         }
181
182         TAILQ_INIT(&lconfig->hc_resources);
183
184         yyin = fopen(config, "r");
185         if (yyin == NULL) {
186                 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
187                     config);
188                 yy_config_free(lconfig);
189                 if (exitonerror)
190                         exit(EX_OSFILE);
191                 return (NULL);
192         }
193         yyrestart(yyin);
194         ret = yyparse();
195         fclose(yyin);
196         if (ret != 0) {
197                 yy_config_free(lconfig);
198                 if (exitonerror)
199                         exit(EX_CONFIG);
200                 return (NULL);
201         }
202
203         /*
204          * Let's see if everything is set up.
205          */
206         if (lconfig->hc_controladdr[0] == '\0') {
207                 strlcpy(lconfig->hc_controladdr, depth0_control,
208                     sizeof(lconfig->hc_controladdr));
209         }
210         if (lconfig->hc_listenaddr[0] == '\0') {
211                 strlcpy(lconfig->hc_listenaddr, depth0_listen,
212                     sizeof(lconfig->hc_listenaddr));
213         }
214         TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
215                 assert(curres->hr_provname[0] != '\0');
216                 assert(curres->hr_localpath[0] != '\0');
217                 assert(curres->hr_remoteaddr[0] != '\0');
218
219                 if (curres->hr_replication == -1) {
220                         /*
221                          * Replication is not set at resource-level.
222                          * Use global or default setting.
223                          */
224                         curres->hr_replication = depth0_replication;
225                 }
226                 if (curres->hr_timeout == -1) {
227                         /*
228                          * Timeout is not set at resource-level.
229                          * Use global or default setting.
230                          */
231                         curres->hr_timeout = depth0_timeout;
232                 }
233                 if (curres->hr_exec[0] == '\0') {
234                         /*
235                          * Exec is not set at resource-level.
236                          * Use global or default setting.
237                          */
238                         strlcpy(curres->hr_exec, depth0_exec,
239                             sizeof(curres->hr_exec));
240                 }
241         }
242
243         return (lconfig);
244 }
245
246 void
247 yy_config_free(struct hastd_config *config)
248 {
249         struct hast_resource *res;
250
251         while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
252                 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
253                 free(res);
254         }
255         free(config);
256 }
257 %}
258
259 %token CONTROL LISTEN PORT REPLICATION TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON
260 %token FULLSYNC MEMSYNC ASYNC
261 %token NUM STR OB CB
262
263 %type <num> replication_type
264
265 %union
266 {
267         int num;
268         char *str;
269 }
270
271 %token <num> NUM
272 %token <str> STR
273
274 %%
275
276 statements:
277         |
278         statements statement
279         ;
280
281 statement:
282         control_statement
283         |
284         listen_statement
285         |
286         replication_statement
287         |
288         timeout_statement
289         |
290         exec_statement
291         |
292         node_statement
293         |
294         resource_statement
295         ;
296
297 control_statement:      CONTROL STR
298         {
299                 switch (depth) {
300                 case 0:
301                         if (strlcpy(depth0_control, $2,
302                             sizeof(depth0_control)) >=
303                             sizeof(depth0_control)) {
304                                 pjdlog_error("control argument is too long.");
305                                 free($2);
306                                 return (1);
307                         }
308                         break;
309                 case 1:
310                         if (!mynode)
311                                 break;
312                         if (strlcpy(lconfig->hc_controladdr, $2,
313                             sizeof(lconfig->hc_controladdr)) >=
314                             sizeof(lconfig->hc_controladdr)) {
315                                 pjdlog_error("control argument is too long.");
316                                 free($2);
317                                 return (1);
318                         }
319                         break;
320                 default:
321                         assert(!"control at wrong depth level");
322                 }
323                 free($2);
324         }
325         ;
326
327 listen_statement:       LISTEN STR
328         {
329                 switch (depth) {
330                 case 0:
331                         if (strlcpy(depth0_listen, $2,
332                             sizeof(depth0_listen)) >=
333                             sizeof(depth0_listen)) {
334                                 pjdlog_error("listen argument is too long.");
335                                 free($2);
336                                 return (1);
337                         }
338                         break;
339                 case 1:
340                         if (!mynode)
341                                 break;
342                         if (strlcpy(lconfig->hc_listenaddr, $2,
343                             sizeof(lconfig->hc_listenaddr)) >=
344                             sizeof(lconfig->hc_listenaddr)) {
345                                 pjdlog_error("listen argument is too long.");
346                                 free($2);
347                                 return (1);
348                         }
349                         break;
350                 default:
351                         assert(!"listen at wrong depth level");
352                 }
353                 free($2);
354         }
355         ;
356
357 replication_statement:  REPLICATION replication_type
358         {
359                 switch (depth) {
360                 case 0:
361                         depth0_replication = $2;
362                         break;
363                 case 1:
364                         if (curres != NULL)
365                                 curres->hr_replication = $2;
366                         break;
367                 default:
368                         assert(!"replication at wrong depth level");
369                 }
370         }
371         ;
372
373 replication_type:
374         FULLSYNC        { $$ = HAST_REPLICATION_FULLSYNC; }
375         |
376         MEMSYNC         { $$ = HAST_REPLICATION_MEMSYNC; }
377         |
378         ASYNC           { $$ = HAST_REPLICATION_ASYNC; }
379         ;
380
381 timeout_statement:      TIMEOUT NUM
382         {
383                 switch (depth) {
384                 case 0:
385                         depth0_timeout = $2;
386                         break;
387                 case 1:
388                         if (curres != NULL)
389                                 curres->hr_timeout = $2;
390                         break;
391                 default:
392                         assert(!"timeout at wrong depth level");
393                 }
394         }
395         ;
396
397 exec_statement:         EXEC STR
398         {
399                 switch (depth) {
400                 case 0:
401                         if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
402                             sizeof(depth0_exec)) {
403                                 pjdlog_error("Exec path is too long.");
404                                 free($2);
405                                 return (1);
406                         }
407                         break;
408                 case 1:
409                         if (curres == NULL)
410                                 break;
411                         if (strlcpy(curres->hr_exec, $2,
412                             sizeof(curres->hr_exec)) >=
413                             sizeof(curres->hr_exec)) {
414                                 pjdlog_error("Exec path is too long.");
415                                 free($2);
416                                 return (1);
417                         }
418                         break;
419                 default:
420                         assert(!"exec at wrong depth level");
421                 }
422                 free($2);
423         }
424         ;
425
426 node_statement:         ON node_start OB node_entries CB
427         {
428                 mynode = false;
429         }
430         ;
431
432 node_start:     STR
433         {
434                 switch (isitme($1)) {
435                 case -1:
436                         free($1);
437                         return (1);
438                 case 0:
439                         break;
440                 case 1:
441                         mynode = true;
442                         break;
443                 default:
444                         assert(!"invalid isitme() return value");
445                 }
446                 free($1);
447         }
448         ;
449
450 node_entries:
451         |
452         node_entries node_entry
453         ;
454
455 node_entry:
456         control_statement
457         |
458         listen_statement
459         ;
460
461 resource_statement:     RESOURCE resource_start OB resource_entries CB
462         {
463                 if (curres != NULL) {
464                         /*
465                          * There must be section for this node, at least with
466                          * remote address configuration.
467                          */
468                         if (!hadmynode) {
469                                 char *names;
470
471                                 if (node_names(&names) != 0)
472                                         return (1);
473                                 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
474                                     curres->hr_name, names);
475                                 return (1);
476                         }
477
478                         /*
479                          * Let's see there are some resource-level settings
480                          * that we can use for node-level settings.
481                          */
482                         if (curres->hr_provname[0] == '\0' &&
483                             depth1_provname[0] != '\0') {
484                                 /*
485                                  * Provider name is not set at node-level,
486                                  * but is set at resource-level, use it.
487                                  */
488                                 strlcpy(curres->hr_provname, depth1_provname,
489                                     sizeof(curres->hr_provname));
490                         }
491                         if (curres->hr_localpath[0] == '\0' &&
492                             depth1_localpath[0] != '\0') {
493                                 /*
494                                  * Path to local provider is not set at
495                                  * node-level, but is set at resource-level,
496                                  * use it.
497                                  */
498                                 strlcpy(curres->hr_localpath, depth1_localpath,
499                                     sizeof(curres->hr_localpath));
500                         }
501
502                         /*
503                          * If provider name is not given, use resource name
504                          * as provider name.
505                          */
506                         if (curres->hr_provname[0] == '\0') {
507                                 strlcpy(curres->hr_provname, curres->hr_name,
508                                     sizeof(curres->hr_provname));
509                         }
510
511                         /*
512                          * Remote address has to be configured at this point.
513                          */
514                         if (curres->hr_remoteaddr[0] == '\0') {
515                                 pjdlog_error("Remote address not configured for resource %s.",
516                                     curres->hr_name);
517                                 return (1);
518                         }
519                         /*
520                          * Path to local provider has to be configured at this
521                          * point.
522                          */
523                         if (curres->hr_localpath[0] == '\0') {
524                                 pjdlog_error("Path to local component not configured for resource %s.",
525                                     curres->hr_name);
526                                 return (1);
527                         }
528
529                         /* Put it onto resource list. */
530                         TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
531                         curres = NULL;
532                 }
533         }
534         ;
535
536 resource_start: STR
537         {
538                 /* Check if there is no duplicate entry. */
539                 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
540                         if (strcmp(curres->hr_name, $1) == 0) {
541                                 pjdlog_error("Resource %s configured more than once.",
542                                     curres->hr_name);
543                                 free($1);
544                                 return (1);
545                         }
546                 }
547
548                 /*
549                  * Clear those, so we can tell if they were set at
550                  * resource-level or not.
551                  */
552                 depth1_provname[0] = '\0';
553                 depth1_localpath[0] = '\0';
554                 hadmynode = false;
555
556                 curres = calloc(1, sizeof(*curres));
557                 if (curres == NULL) {
558                         pjdlog_error("Unable to allocate memory for resource.");
559                         free($1);
560                         return (1);
561                 }
562                 if (strlcpy(curres->hr_name, $1,
563                     sizeof(curres->hr_name)) >=
564                     sizeof(curres->hr_name)) {
565                         pjdlog_error("Resource name is too long.");
566                         free($1);
567                         return (1);
568                 }
569                 free($1);
570                 curres->hr_role = HAST_ROLE_INIT;
571                 curres->hr_previous_role = HAST_ROLE_INIT;
572                 curres->hr_replication = -1;
573                 curres->hr_timeout = -1;
574                 curres->hr_exec[0] = '\0';
575                 curres->hr_provname[0] = '\0';
576                 curres->hr_localpath[0] = '\0';
577                 curres->hr_localfd = -1;
578                 curres->hr_remoteaddr[0] = '\0';
579                 curres->hr_ggateunit = -1;
580         }
581         ;
582
583 resource_entries:
584         |
585         resource_entries resource_entry
586         ;
587
588 resource_entry:
589         replication_statement
590         |
591         timeout_statement
592         |
593         exec_statement
594         |
595         name_statement
596         |
597         local_statement
598         |
599         resource_node_statement
600         ;
601
602 name_statement:         NAME STR
603         {
604                 switch (depth) {
605                 case 1:
606                         if (strlcpy(depth1_provname, $2,
607                             sizeof(depth1_provname)) >=
608                             sizeof(depth1_provname)) {
609                                 pjdlog_error("name argument is too long.");
610                                 free($2);
611                                 return (1);
612                         }
613                         break;
614                 case 2:
615                         if (!mynode)
616                                 break;
617                         assert(curres != NULL);
618                         if (strlcpy(curres->hr_provname, $2,
619                             sizeof(curres->hr_provname)) >=
620                             sizeof(curres->hr_provname)) {
621                                 pjdlog_error("name argument is too long.");
622                                 free($2);
623                                 return (1);
624                         }
625                         break;
626                 default:
627                         assert(!"name at wrong depth level");
628                 }
629                 free($2);
630         }
631         ;
632
633 local_statement:        LOCAL STR
634         {
635                 switch (depth) {
636                 case 1:
637                         if (strlcpy(depth1_localpath, $2,
638                             sizeof(depth1_localpath)) >=
639                             sizeof(depth1_localpath)) {
640                                 pjdlog_error("local argument is too long.");
641                                 free($2);
642                                 return (1);
643                         }
644                         break;
645                 case 2:
646                         if (!mynode)
647                                 break;
648                         assert(curres != NULL);
649                         if (strlcpy(curres->hr_localpath, $2,
650                             sizeof(curres->hr_localpath)) >=
651                             sizeof(curres->hr_localpath)) {
652                                 pjdlog_error("local argument is too long.");
653                                 free($2);
654                                 return (1);
655                         }
656                         break;
657                 default:
658                         assert(!"local at wrong depth level");
659                 }
660                 free($2);
661         }
662         ;
663
664 resource_node_statement:ON resource_node_start OB resource_node_entries CB
665         {
666                 mynode = false;
667         }
668         ;
669
670 resource_node_start:    STR
671         {
672                 if (curres != NULL) {
673                         switch (isitme($1)) {
674                         case -1:
675                                 free($1);
676                                 return (1);
677                         case 0:
678                                 break;
679                         case 1:
680                                 mynode = hadmynode = true;
681                                 break;
682                         default:
683                                 assert(!"invalid isitme() return value");
684                         }
685                 }
686                 free($1);
687         }
688         ;
689
690 resource_node_entries:
691         |
692         resource_node_entries resource_node_entry
693         ;
694
695 resource_node_entry:
696         name_statement
697         |
698         local_statement
699         |
700         remote_statement
701         ;
702
703 remote_statement:       REMOTE STR
704         {
705                 assert(depth == 2);
706                 if (mynode) {
707                         assert(curres != NULL);
708                         if (strlcpy(curres->hr_remoteaddr, $2,
709                             sizeof(curres->hr_remoteaddr)) >=
710                             sizeof(curres->hr_remoteaddr)) {
711                                 pjdlog_error("remote argument is too long.");
712                                 free($2);
713                                 return (1);
714                         }
715                 }
716                 free($2);
717         }
718         ;