]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sbin/hastd/parse.y
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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 "hast.h"
47
48 extern int depth;
49 extern int lineno;
50
51 extern FILE *yyin;
52 extern char *yytext;
53
54 static struct hastd_config lconfig;
55 static struct hast_resource *curres;
56 static bool mynode;
57
58 static char depth0_control[HAST_ADDRSIZE];
59 static char depth0_listen[HAST_ADDRSIZE];
60 static int depth0_replication;
61 static int depth0_timeout;
62
63 static char depth1_provname[PATH_MAX];
64 static char depth1_localpath[PATH_MAX];
65
66 static bool
67 isitme(const char *name)
68 {
69         char buf[MAXHOSTNAMELEN];
70         char *pos;
71         size_t bufsize;
72
73         /*
74          * First check if the give name matches our full hostname.
75          */
76         if (gethostname(buf, sizeof(buf)) < 0)
77                 err(EX_OSERR, "gethostname() failed");
78         if (strcmp(buf, name) == 0)
79                 return (true);
80
81         /*
82          * Now check if it matches first part of the host name.
83          */
84         pos = strchr(buf, '.');
85         if (pos != NULL && pos != buf && strncmp(buf, name, pos - buf) == 0)
86                 return (true);
87
88         /*
89          * At the end check if name is equal to our host's UUID.
90          */
91         bufsize = sizeof(buf);
92         if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0)
93                 err(EX_OSERR, "sysctlbyname(kern.hostuuid) failed");
94         if (strcasecmp(buf, name) == 0)
95                 return (true);
96
97         /*
98          * Looks like this isn't about us.
99          */
100         return (false);
101 }
102
103 void
104 yyerror(const char *str)
105 {
106
107         fprintf(stderr, "error at line %d near '%s': %s\n",
108             lineno, yytext, str);
109 }
110
111 struct hastd_config *
112 yy_config_parse(const char *config)
113 {
114         int ret;
115
116         curres = NULL;
117         mynode = false;
118
119         depth0_timeout = HAST_TIMEOUT;
120         depth0_replication = HAST_REPLICATION_MEMSYNC;
121         strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
122         strlcpy(depth0_listen, HASTD_LISTEN, sizeof(depth0_listen));
123
124         TAILQ_INIT(&lconfig.hc_resources);
125
126         yyin = fopen(config, "r");
127         if (yyin == NULL)
128                 err(EX_OSFILE, "cannot open configuration file %s", config);
129         ret = yyparse();
130         fclose(yyin);
131         if (ret != 0) {
132                 yy_config_free(&lconfig);
133                 exit(EX_CONFIG);
134         }
135
136         /*
137          * Let's see if everything is set up.
138          */
139         if (lconfig.hc_controladdr[0] == '\0') {
140                 strlcpy(lconfig.hc_controladdr, depth0_control,
141                     sizeof(lconfig.hc_controladdr));
142         }
143         if (lconfig.hc_listenaddr[0] == '\0') {
144                 strlcpy(lconfig.hc_listenaddr, depth0_listen,
145                     sizeof(lconfig.hc_listenaddr));
146         }
147         TAILQ_FOREACH(curres, &lconfig.hc_resources, hr_next) {
148                 assert(curres->hr_provname[0] != '\0');
149                 assert(curres->hr_localpath[0] != '\0');
150                 assert(curres->hr_remoteaddr[0] != '\0');
151
152                 if (curres->hr_replication == -1) {
153                         /*
154                          * Replication is not set at resource-level.
155                          * Use global or default setting.
156                          */
157                         curres->hr_replication = depth0_replication;
158                 }
159                 if (curres->hr_timeout == -1) {
160                         /*
161                          * Timeout is not set at resource-level.
162                          * Use global or default setting.
163                          */
164                         curres->hr_timeout = depth0_timeout;
165                 }
166         }
167
168         return (&lconfig);
169 }
170
171 void
172 yy_config_free(struct hastd_config *config)
173 {
174         struct hast_resource *res;
175
176         while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
177                 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
178                 free(res);
179         }
180 }
181 %}
182
183 %token CONTROL LISTEN PORT REPLICATION TIMEOUT EXTENTSIZE RESOURCE NAME LOCAL REMOTE ON
184 %token FULLSYNC MEMSYNC ASYNC
185 %token NUM STR OB CB
186
187 %type <num> replication_type
188
189 %union
190 {
191         int num;
192         char *str;
193 }
194
195 %token <num> NUM
196 %token <str> STR
197
198 %%
199
200 statements:
201         |
202         statements statement
203         ;
204
205 statement:
206         control_statement
207         |
208         listen_statement
209         |
210         replication_statement
211         |
212         timeout_statement
213         |
214         node_statement
215         |
216         resource_statement
217         ;
218
219 control_statement:      CONTROL STR
220         {
221                 switch (depth) {
222                 case 0:
223                         if (strlcpy(depth0_control, $2,
224                             sizeof(depth0_control)) >=
225                             sizeof(depth0_control)) {
226                                 errx(EX_CONFIG, "control argument too long");
227                         }
228                         break;
229                 case 1:
230                         if (mynode) {
231                                 if (strlcpy(lconfig.hc_controladdr, $2,
232                                     sizeof(lconfig.hc_controladdr)) >=
233                                     sizeof(lconfig.hc_controladdr)) {
234                                         errx(EX_CONFIG,
235                                             "control argument too long");
236                                 }
237                         }
238                         break;
239                 default:
240                         assert(!"control at wrong depth level");
241                 }
242         }
243         ;
244
245 listen_statement:       LISTEN STR
246         {
247                 switch (depth) {
248                 case 0:
249                         if (strlcpy(depth0_listen, $2,
250                             sizeof(depth0_listen)) >=
251                             sizeof(depth0_listen)) {
252                                 errx(EX_CONFIG, "listen argument too long");
253                         }
254                         break;
255                 case 1:
256                         if (mynode) {
257                                 if (strlcpy(lconfig.hc_listenaddr, $2,
258                                     sizeof(lconfig.hc_listenaddr)) >=
259                                     sizeof(lconfig.hc_listenaddr)) {
260                                         errx(EX_CONFIG,
261                                             "listen argument too long");
262                                 }
263                         }
264                         break;
265                 default:
266                         assert(!"listen at wrong depth level");
267                 }
268         }
269         ;
270
271 replication_statement:  REPLICATION replication_type
272         {
273                 switch (depth) {
274                 case 0:
275                         depth0_replication = $2;
276                         break;
277                 case 1:
278                         if (curres != NULL)
279                                 curres->hr_replication = $2;
280                         break;
281                 default:
282                         assert(!"replication at wrong depth level");
283                 }
284         }
285         ;
286
287 replication_type:
288         FULLSYNC        { $$ = HAST_REPLICATION_FULLSYNC; }
289         |
290         MEMSYNC         { $$ = HAST_REPLICATION_MEMSYNC; }
291         |
292         ASYNC           { $$ = HAST_REPLICATION_ASYNC; }
293         ;
294
295 timeout_statement:      TIMEOUT NUM
296         {
297                 switch (depth) {
298                 case 0:
299                         depth0_timeout = $2;
300                         break;
301                 case 1:
302                         if (curres != NULL)
303                                 curres->hr_timeout = $2;
304                         break;
305                 default:
306                         assert(!"timeout at wrong depth level");
307                 }
308         }
309         ;
310
311 node_statement:         ON node_start OB node_entries CB
312         {
313                 mynode = false;
314         }
315         ;
316
317 node_start:     STR
318         {
319                 if (isitme($1))
320                         mynode = true;
321         }
322         ;
323
324 node_entries:
325         |
326         node_entries node_entry
327         ;
328
329 node_entry:
330         control_statement
331         |
332         listen_statement
333         ;
334
335 resource_statement:     RESOURCE resource_start OB resource_entries CB
336         {
337                 if (curres != NULL) {
338                         /*
339                          * Let's see there are some resource-level settings
340                          * that we can use for node-level settings.
341                          */
342                         if (curres->hr_provname[0] == '\0' &&
343                             depth1_provname[0] != '\0') {
344                                 /*
345                                  * Provider name is not set at node-level,
346                                  * but is set at resource-level, use it.
347                                  */
348                                 strlcpy(curres->hr_provname, depth1_provname,
349                                     sizeof(curres->hr_provname));
350                         }
351                         if (curres->hr_localpath[0] == '\0' &&
352                             depth1_localpath[0] != '\0') {
353                                 /*
354                                  * Path to local provider is not set at
355                                  * node-level, but is set at resource-level,
356                                  * use it.
357                                  */
358                                 strlcpy(curres->hr_localpath, depth1_localpath,
359                                     sizeof(curres->hr_localpath));
360                         }
361
362                         /*
363                          * If provider name is not given, use resource name
364                          * as provider name.
365                          */
366                         if (curres->hr_provname[0] == '\0') {
367                                 strlcpy(curres->hr_provname, curres->hr_name,
368                                     sizeof(curres->hr_provname));
369                         }
370
371                         /*
372                          * Remote address has to be configured at this point.
373                          */
374                         if (curres->hr_remoteaddr[0] == '\0') {
375                                 errx(EX_CONFIG,
376                                     "remote address not configured for resource %s",
377                                     curres->hr_name);
378                         }
379                         /*
380                          * Path to local provider has to be configured at this
381                          * point.
382                          */
383                         if (curres->hr_localpath[0] == '\0') {
384                                 errx(EX_CONFIG,
385                                     "path local component not configured for resource %s",
386                                     curres->hr_name);
387                         }
388
389                         /* Put it onto resource list. */
390                         TAILQ_INSERT_TAIL(&lconfig.hc_resources, curres, hr_next);
391                         curres = NULL;
392                 }
393         }
394         ;
395
396 resource_start: STR
397         {
398                 /*
399                  * Clear those, so we can tell if they were set at
400                  * resource-level or not.
401                  */
402                 depth1_provname[0] = '\0';
403                 depth1_localpath[0] = '\0';
404
405                 curres = calloc(1, sizeof(*curres));
406                 if (curres == NULL) {
407                         errx(EX_TEMPFAIL,
408                             "cannot allocate memory for resource");
409                 }
410                 if (strlcpy(curres->hr_name, $1,
411                     sizeof(curres->hr_name)) >=
412                     sizeof(curres->hr_name)) {
413                         errx(EX_CONFIG,
414                             "resource name (%s) too long", $1);
415                 }
416                 curres->hr_role = HAST_ROLE_INIT;
417                 curres->hr_previous_role = HAST_ROLE_INIT;
418                 curres->hr_replication = -1;
419                 curres->hr_timeout = -1;
420                 curres->hr_provname[0] = '\0';
421                 curres->hr_localpath[0] = '\0';
422                 curres->hr_localfd = -1;
423                 curres->hr_remoteaddr[0] = '\0';
424                 curres->hr_ggateunit = -1;
425         }
426         ;
427
428 resource_entries:
429         |
430         resource_entries resource_entry
431         ;
432
433 resource_entry:
434         replication_statement
435         |
436         timeout_statement
437         |
438         name_statement
439         |
440         local_statement
441         |
442         resource_node_statement
443         ;
444
445 name_statement:         NAME STR
446         {
447                 switch (depth) {
448                 case 1:
449                         if (strlcpy(depth1_provname, $2,
450                             sizeof(depth1_provname)) >=
451                             sizeof(depth1_provname)) {
452                                 errx(EX_CONFIG, "name argument too long");
453                         }
454                         break;
455                 case 2:
456                         if (mynode) {
457                                 assert(curres != NULL);
458                                 if (strlcpy(curres->hr_provname, $2,
459                                     sizeof(curres->hr_provname)) >=
460                                     sizeof(curres->hr_provname)) {
461                                         errx(EX_CONFIG,
462                                             "name argument too long");
463                                 }
464                         }
465                         break;
466                 default:
467                         assert(!"name at wrong depth level");
468                 }
469         }
470         ;
471
472 local_statement:        LOCAL STR
473         {
474                 switch (depth) {
475                 case 1:
476                         if (strlcpy(depth1_localpath, $2,
477                             sizeof(depth1_localpath)) >=
478                             sizeof(depth1_localpath)) {
479                                 errx(EX_CONFIG, "local argument too long");
480                         }
481                         break;
482                 case 2:
483                         if (mynode) {
484                                 assert(curres != NULL);
485                                 if (strlcpy(curres->hr_localpath, $2,
486                                     sizeof(curres->hr_localpath)) >=
487                                     sizeof(curres->hr_localpath)) {
488                                         errx(EX_CONFIG,
489                                             "local argument too long");
490                                 }
491                         }
492                         break;
493                 default:
494                         assert(!"local at wrong depth level");
495                 }
496         }
497         ;
498
499 resource_node_statement:ON resource_node_start OB resource_node_entries CB
500         {
501                 mynode = false;
502         }
503         ;
504
505 resource_node_start:    STR
506         {
507                 if (curres != NULL && isitme($1))
508                         mynode = true;
509         }
510         ;
511
512 resource_node_entries:
513         |
514         resource_node_entries resource_node_entry
515         ;
516
517 resource_node_entry:
518         name_statement
519         |
520         local_statement
521         |
522         remote_statement
523         ;
524
525 remote_statement:       REMOTE STR
526         {
527                 assert(depth == 2);
528                 if (mynode) {
529                         assert(curres != NULL);
530                         if (strlcpy(curres->hr_remoteaddr, $2,
531                             sizeof(curres->hr_remoteaddr)) >=
532                             sizeof(curres->hr_remoteaddr)) {
533                                 errx(EX_CONFIG, "remote argument too long");
534                         }
535                 }
536         }
537         ;