]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sbin/hastd/parse.y
MFC r220520, r220521, r220522, r220523, r220573, r220744, r220865,
[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_FULLSYNC;
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_replication == HAST_REPLICATION_MEMSYNC ||
232                     curres->hr_replication == HAST_REPLICATION_ASYNC) {
233                         pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
234                             curres->hr_replication == HAST_REPLICATION_MEMSYNC ?
235                             "memsync" : "async", "fullsync");
236                         curres->hr_replication = HAST_REPLICATION_FULLSYNC;
237                 }
238                 if (curres->hr_checksum == -1) {
239                         /*
240                          * Checksum is not set at resource-level.
241                          * Use global or default setting.
242                          */
243                         curres->hr_checksum = depth0_checksum;
244                 }
245                 if (curres->hr_compression == -1) {
246                         /*
247                          * Compression is not set at resource-level.
248                          * Use global or default setting.
249                          */
250                         curres->hr_compression = depth0_compression;
251                 }
252                 if (curres->hr_timeout == -1) {
253                         /*
254                          * Timeout is not set at resource-level.
255                          * Use global or default setting.
256                          */
257                         curres->hr_timeout = depth0_timeout;
258                 }
259                 if (curres->hr_exec[0] == '\0') {
260                         /*
261                          * Exec is not set at resource-level.
262                          * Use global or default setting.
263                          */
264                         strlcpy(curres->hr_exec, depth0_exec,
265                             sizeof(curres->hr_exec));
266                 }
267         }
268
269         return (lconfig);
270 }
271
272 void
273 yy_config_free(struct hastd_config *config)
274 {
275         struct hast_resource *res;
276
277         while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
278                 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
279                 free(res);
280         }
281         free(config);
282 }
283 %}
284
285 %token CONTROL LISTEN PORT REPLICATION CHECKSUM COMPRESSION
286 %token TIMEOUT EXEC EXTENTSIZE RESOURCE NAME LOCAL REMOTE SOURCE ON
287 %token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
288 %token NUM STR OB CB
289
290 %type <num> replication_type
291 %type <num> checksum_type
292 %type <num> compression_type
293
294 %union
295 {
296         int num;
297         char *str;
298 }
299
300 %token <num> NUM
301 %token <str> STR
302
303 %%
304
305 statements:
306         |
307         statements statement
308         ;
309
310 statement:
311         control_statement
312         |
313         listen_statement
314         |
315         replication_statement
316         |
317         checksum_statement
318         |
319         compression_statement
320         |
321         timeout_statement
322         |
323         exec_statement
324         |
325         node_statement
326         |
327         resource_statement
328         ;
329
330 control_statement:      CONTROL STR
331         {
332                 switch (depth) {
333                 case 0:
334                         if (strlcpy(depth0_control, $2,
335                             sizeof(depth0_control)) >=
336                             sizeof(depth0_control)) {
337                                 pjdlog_error("control argument is too long.");
338                                 free($2);
339                                 return (1);
340                         }
341                         break;
342                 case 1:
343                         if (!mynode)
344                                 break;
345                         if (strlcpy(lconfig->hc_controladdr, $2,
346                             sizeof(lconfig->hc_controladdr)) >=
347                             sizeof(lconfig->hc_controladdr)) {
348                                 pjdlog_error("control argument is too long.");
349                                 free($2);
350                                 return (1);
351                         }
352                         break;
353                 default:
354                         assert(!"control at wrong depth level");
355                 }
356                 free($2);
357         }
358         ;
359
360 listen_statement:       LISTEN STR
361         {
362                 switch (depth) {
363                 case 0:
364                         if (strlcpy(depth0_listen, $2,
365                             sizeof(depth0_listen)) >=
366                             sizeof(depth0_listen)) {
367                                 pjdlog_error("listen argument is too long.");
368                                 free($2);
369                                 return (1);
370                         }
371                         break;
372                 case 1:
373                         if (!mynode)
374                                 break;
375                         if (strlcpy(lconfig->hc_listenaddr, $2,
376                             sizeof(lconfig->hc_listenaddr)) >=
377                             sizeof(lconfig->hc_listenaddr)) {
378                                 pjdlog_error("listen argument is too long.");
379                                 free($2);
380                                 return (1);
381                         }
382                         break;
383                 default:
384                         assert(!"listen at wrong depth level");
385                 }
386                 free($2);
387         }
388         ;
389
390 replication_statement:  REPLICATION replication_type
391         {
392                 switch (depth) {
393                 case 0:
394                         depth0_replication = $2;
395                         break;
396                 case 1:
397                         if (curres != NULL)
398                                 curres->hr_replication = $2;
399                         break;
400                 default:
401                         assert(!"replication at wrong depth level");
402                 }
403         }
404         ;
405
406 replication_type:
407         FULLSYNC        { $$ = HAST_REPLICATION_FULLSYNC; }
408         |
409         MEMSYNC         { $$ = HAST_REPLICATION_MEMSYNC; }
410         |
411         ASYNC           { $$ = HAST_REPLICATION_ASYNC; }
412         ;
413
414 checksum_statement:     CHECKSUM checksum_type
415         {
416                 switch (depth) {
417                 case 0:
418                         depth0_checksum = $2;
419                         break;
420                 case 1:
421                         if (curres != NULL)
422                                 curres->hr_checksum = $2;
423                         break;
424                 default:
425                         assert(!"checksum at wrong depth level");
426                 }
427         }
428         ;
429
430 checksum_type:
431         NONE            { $$ = HAST_CHECKSUM_NONE; }
432         |
433         CRC32           { $$ = HAST_CHECKSUM_CRC32; }
434         |
435         SHA256          { $$ = HAST_CHECKSUM_SHA256; }
436         ;
437
438 compression_statement:  COMPRESSION compression_type
439         {
440                 switch (depth) {
441                 case 0:
442                         depth0_compression = $2;
443                         break;
444                 case 1:
445                         if (curres != NULL)
446                                 curres->hr_compression = $2;
447                         break;
448                 default:
449                         assert(!"compression at wrong depth level");
450                 }
451         }
452         ;
453
454 compression_type:
455         NONE            { $$ = HAST_COMPRESSION_NONE; }
456         |
457         HOLE            { $$ = HAST_COMPRESSION_HOLE; }
458         |
459         LZF             { $$ = HAST_COMPRESSION_LZF; }
460         ;
461
462 timeout_statement:      TIMEOUT NUM
463         {
464                 if ($2 <= 0) {
465                         pjdlog_error("Negative or zero timeout.");
466                         return (1);
467                 }
468                 switch (depth) {
469                 case 0:
470                         depth0_timeout = $2;
471                         break;
472                 case 1:
473                         if (curres != NULL)
474                                 curres->hr_timeout = $2;
475                         break;
476                 default:
477                         assert(!"timeout at wrong depth level");
478                 }
479         }
480         ;
481
482 exec_statement:         EXEC STR
483         {
484                 switch (depth) {
485                 case 0:
486                         if (strlcpy(depth0_exec, $2, sizeof(depth0_exec)) >=
487                             sizeof(depth0_exec)) {
488                                 pjdlog_error("Exec path is too long.");
489                                 free($2);
490                                 return (1);
491                         }
492                         break;
493                 case 1:
494                         if (curres == NULL)
495                                 break;
496                         if (strlcpy(curres->hr_exec, $2,
497                             sizeof(curres->hr_exec)) >=
498                             sizeof(curres->hr_exec)) {
499                                 pjdlog_error("Exec path is too long.");
500                                 free($2);
501                                 return (1);
502                         }
503                         break;
504                 default:
505                         assert(!"exec at wrong depth level");
506                 }
507                 free($2);
508         }
509         ;
510
511 node_statement:         ON node_start OB node_entries CB
512         {
513                 mynode = false;
514         }
515         ;
516
517 node_start:     STR
518         {
519                 switch (isitme($1)) {
520                 case -1:
521                         free($1);
522                         return (1);
523                 case 0:
524                         break;
525                 case 1:
526                         mynode = true;
527                         break;
528                 default:
529                         assert(!"invalid isitme() return value");
530                 }
531                 free($1);
532         }
533         ;
534
535 node_entries:
536         |
537         node_entries node_entry
538         ;
539
540 node_entry:
541         control_statement
542         |
543         listen_statement
544         ;
545
546 resource_statement:     RESOURCE resource_start OB resource_entries CB
547         {
548                 if (curres != NULL) {
549                         /*
550                          * There must be section for this node, at least with
551                          * remote address configuration.
552                          */
553                         if (!hadmynode) {
554                                 char *names;
555
556                                 if (node_names(&names) != 0)
557                                         return (1);
558                                 pjdlog_error("No resource %s configuration for this node (acceptable node names: %s).",
559                                     curres->hr_name, names);
560                                 return (1);
561                         }
562
563                         /*
564                          * Let's see there are some resource-level settings
565                          * that we can use for node-level settings.
566                          */
567                         if (curres->hr_provname[0] == '\0' &&
568                             depth1_provname[0] != '\0') {
569                                 /*
570                                  * Provider name is not set at node-level,
571                                  * but is set at resource-level, use it.
572                                  */
573                                 strlcpy(curres->hr_provname, depth1_provname,
574                                     sizeof(curres->hr_provname));
575                         }
576                         if (curres->hr_localpath[0] == '\0' &&
577                             depth1_localpath[0] != '\0') {
578                                 /*
579                                  * Path to local provider is not set at
580                                  * node-level, but is set at resource-level,
581                                  * use it.
582                                  */
583                                 strlcpy(curres->hr_localpath, depth1_localpath,
584                                     sizeof(curres->hr_localpath));
585                         }
586
587                         /*
588                          * If provider name is not given, use resource name
589                          * as provider name.
590                          */
591                         if (curres->hr_provname[0] == '\0') {
592                                 strlcpy(curres->hr_provname, curres->hr_name,
593                                     sizeof(curres->hr_provname));
594                         }
595
596                         /*
597                          * Remote address has to be configured at this point.
598                          */
599                         if (curres->hr_remoteaddr[0] == '\0') {
600                                 pjdlog_error("Remote address not configured for resource %s.",
601                                     curres->hr_name);
602                                 return (1);
603                         }
604                         /*
605                          * Path to local provider has to be configured at this
606                          * point.
607                          */
608                         if (curres->hr_localpath[0] == '\0') {
609                                 pjdlog_error("Path to local component not configured for resource %s.",
610                                     curres->hr_name);
611                                 return (1);
612                         }
613
614                         /* Put it onto resource list. */
615                         TAILQ_INSERT_TAIL(&lconfig->hc_resources, curres, hr_next);
616                         curres = NULL;
617                 }
618         }
619         ;
620
621 resource_start: STR
622         {
623                 /* Check if there is no duplicate entry. */
624                 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
625                         if (strcmp(curres->hr_name, $1) == 0) {
626                                 pjdlog_error("Resource %s configured more than once.",
627                                     curres->hr_name);
628                                 free($1);
629                                 return (1);
630                         }
631                 }
632
633                 /*
634                  * Clear those, so we can tell if they were set at
635                  * resource-level or not.
636                  */
637                 depth1_provname[0] = '\0';
638                 depth1_localpath[0] = '\0';
639                 hadmynode = false;
640
641                 curres = calloc(1, sizeof(*curres));
642                 if (curres == NULL) {
643                         pjdlog_error("Unable to allocate memory for resource.");
644                         free($1);
645                         return (1);
646                 }
647                 if (strlcpy(curres->hr_name, $1,
648                     sizeof(curres->hr_name)) >=
649                     sizeof(curres->hr_name)) {
650                         pjdlog_error("Resource name is too long.");
651                         free($1);
652                         return (1);
653                 }
654                 free($1);
655                 curres->hr_role = HAST_ROLE_INIT;
656                 curres->hr_previous_role = HAST_ROLE_INIT;
657                 curres->hr_replication = -1;
658                 curres->hr_checksum = -1;
659                 curres->hr_compression = -1;
660                 curres->hr_timeout = -1;
661                 curres->hr_exec[0] = '\0';
662                 curres->hr_provname[0] = '\0';
663                 curres->hr_localpath[0] = '\0';
664                 curres->hr_localfd = -1;
665                 curres->hr_remoteaddr[0] = '\0';
666                 curres->hr_sourceaddr[0] = '\0';
667                 curres->hr_ggateunit = -1;
668         }
669         ;
670
671 resource_entries:
672         |
673         resource_entries resource_entry
674         ;
675
676 resource_entry:
677         replication_statement
678         |
679         checksum_statement
680         |
681         compression_statement
682         |
683         timeout_statement
684         |
685         exec_statement
686         |
687         name_statement
688         |
689         local_statement
690         |
691         resource_node_statement
692         ;
693
694 name_statement:         NAME STR
695         {
696                 switch (depth) {
697                 case 1:
698                         if (strlcpy(depth1_provname, $2,
699                             sizeof(depth1_provname)) >=
700                             sizeof(depth1_provname)) {
701                                 pjdlog_error("name argument is too long.");
702                                 free($2);
703                                 return (1);
704                         }
705                         break;
706                 case 2:
707                         if (!mynode)
708                                 break;
709                         assert(curres != NULL);
710                         if (strlcpy(curres->hr_provname, $2,
711                             sizeof(curres->hr_provname)) >=
712                             sizeof(curres->hr_provname)) {
713                                 pjdlog_error("name argument is too long.");
714                                 free($2);
715                                 return (1);
716                         }
717                         break;
718                 default:
719                         assert(!"name at wrong depth level");
720                 }
721                 free($2);
722         }
723         ;
724
725 local_statement:        LOCAL STR
726         {
727                 switch (depth) {
728                 case 1:
729                         if (strlcpy(depth1_localpath, $2,
730                             sizeof(depth1_localpath)) >=
731                             sizeof(depth1_localpath)) {
732                                 pjdlog_error("local argument is too long.");
733                                 free($2);
734                                 return (1);
735                         }
736                         break;
737                 case 2:
738                         if (!mynode)
739                                 break;
740                         assert(curres != NULL);
741                         if (strlcpy(curres->hr_localpath, $2,
742                             sizeof(curres->hr_localpath)) >=
743                             sizeof(curres->hr_localpath)) {
744                                 pjdlog_error("local argument is too long.");
745                                 free($2);
746                                 return (1);
747                         }
748                         break;
749                 default:
750                         assert(!"local at wrong depth level");
751                 }
752                 free($2);
753         }
754         ;
755
756 resource_node_statement:ON resource_node_start OB resource_node_entries CB
757         {
758                 mynode = false;
759         }
760         ;
761
762 resource_node_start:    STR
763         {
764                 if (curres != NULL) {
765                         switch (isitme($1)) {
766                         case -1:
767                                 free($1);
768                                 return (1);
769                         case 0:
770                                 break;
771                         case 1:
772                                 mynode = hadmynode = true;
773                                 break;
774                         default:
775                                 assert(!"invalid isitme() return value");
776                         }
777                 }
778                 free($1);
779         }
780         ;
781
782 resource_node_entries:
783         |
784         resource_node_entries resource_node_entry
785         ;
786
787 resource_node_entry:
788         name_statement
789         |
790         local_statement
791         |
792         remote_statement
793         |
794         source_statement
795         ;
796
797 remote_statement:       REMOTE STR
798         {
799                 assert(depth == 2);
800                 if (mynode) {
801                         assert(curres != NULL);
802                         if (strlcpy(curres->hr_remoteaddr, $2,
803                             sizeof(curres->hr_remoteaddr)) >=
804                             sizeof(curres->hr_remoteaddr)) {
805                                 pjdlog_error("remote argument is too long.");
806                                 free($2);
807                                 return (1);
808                         }
809                 }
810                 free($2);
811         }
812         ;
813
814 source_statement:       SOURCE STR
815         {
816                 assert(depth == 2);
817                 if (mynode) {
818                         assert(curres != NULL);
819                         if (strlcpy(curres->hr_sourceaddr, $2,
820                             sizeof(curres->hr_sourceaddr)) >=
821                             sizeof(curres->hr_sourceaddr)) {
822                                 pjdlog_error("source argument is too long.");
823                                 free($2);
824                                 return (1);
825                         }
826                 }
827                 free($2);
828         }
829         ;