]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sbin/hastd/parse.y
Copy stable/8 to releng/8.2 in preparation for FreeBSD-8.2 release.
[FreeBSD/releng/8.2.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;
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         ;