Deleted Added
full compact
parse.y (230976) parse.y (235789)
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.

--- 14 unchanged lines hidden (view full) ---

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 *
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.

--- 14 unchanged lines hidden (view full) ---

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: head/sbin/hastd/parse.y 230976 2012-02-04 07:59:12Z pjd $
31 * $FreeBSD: head/sbin/hastd/parse.y 235789 2012-05-22 16:33:10Z bapt $
32 */
33
34#include <sys/param.h> /* MAXHOSTNAMELEN */
35#include <sys/queue.h>
36#include <sys/socket.h>
37#include <sys/sysctl.h>
38
39#include <arpa/inet.h>

--- 32 unchanged lines hidden (view full) ---

72static int depth0_metaflush;
73
74static char depth1_provname[PATH_MAX];
75static char depth1_localpath[PATH_MAX];
76static int depth1_metaflush;
77
78extern void yyrestart(FILE *);
79
32 */
33
34#include <sys/param.h> /* MAXHOSTNAMELEN */
35#include <sys/queue.h>
36#include <sys/socket.h>
37#include <sys/sysctl.h>
38
39#include <arpa/inet.h>

--- 32 unchanged lines hidden (view full) ---

72static int depth0_metaflush;
73
74static char depth1_provname[PATH_MAX];
75static char depth1_localpath[PATH_MAX];
76static int depth1_metaflush;
77
78extern void yyrestart(FILE *);
79
80static int
81isitme(const char *name)
82{
83 char buf[MAXHOSTNAMELEN];
84 char *pos;
85 size_t bufsize;
86
87 /*
88 * First check if the given name matches our full hostname.
89 */
90 if (gethostname(buf, sizeof(buf)) < 0) {
91 pjdlog_errno(LOG_ERR, "gethostname() failed");
92 return (-1);
93 }
94 if (strcmp(buf, name) == 0)
95 return (1);
96
97 /*
98 * Now check if it matches first part of the host name.
99 */
100 pos = strchr(buf, '.');
101 if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
102 strncmp(buf, name, pos - buf) == 0) {
103 return (1);
104 }
105
106 /*
107 * At the end check if name is equal to our host's UUID.
108 */
109 bufsize = sizeof(buf);
110 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
111 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
112 return (-1);
113 }
114 if (strcasecmp(buf, name) == 0)
115 return (1);
116
117 /*
118 * Looks like this isn't about us.
119 */
120 return (0);
121}
122
123static bool
124family_supported(int family)
125{
126 int sock;
127
128 sock = socket(family, SOCK_STREAM, 0);
129 if (sock == -1 && errno == EPROTONOSUPPORT)
130 return (false);
131 if (sock >= 0)
132 (void)close(sock);
133 return (true);
134}
135
136static int
137node_names(char **namesp)
138{
139 static char names[MAXHOSTNAMELEN * 3];
140 char buf[MAXHOSTNAMELEN];
141 char *pos;
142 size_t bufsize;
143
144 if (gethostname(buf, sizeof(buf)) < 0) {
145 pjdlog_errno(LOG_ERR, "gethostname() failed");
146 return (-1);
147 }
148
149 /* First component of the host name. */
150 pos = strchr(buf, '.');
151 if (pos != NULL && pos != buf) {
152 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
153 sizeof(names)));
154 (void)strlcat(names, ", ", sizeof(names));
155 }
156
157 /* Full host name. */
158 (void)strlcat(names, buf, sizeof(names));
159 (void)strlcat(names, ", ", sizeof(names));
160
161 /* Host UUID. */
162 bufsize = sizeof(buf);
163 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
164 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
165 return (-1);
166 }
167 (void)strlcat(names, buf, sizeof(names));
168
169 *namesp = names;
170
171 return (0);
172}
173
174void
175yyerror(const char *str)
176{
177
178 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
179 lineno, yytext, str);
180}
181
182struct hastd_config *
183yy_config_parse(const char *config, bool exitonerror)
184{
185 int ret;
186
187 curres = NULL;
188 mynode = false;
189 depth = 0;
190 lineno = 0;
191
192 depth0_timeout = HAST_TIMEOUT;
193 depth0_replication = HAST_REPLICATION_FULLSYNC;
194 depth0_checksum = HAST_CHECKSUM_NONE;
195 depth0_compression = HAST_COMPRESSION_HOLE;
196 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
197 strlcpy(depth0_pidfile, HASTD_PIDFILE, sizeof(depth0_pidfile));
198 TAILQ_INIT(&depth0_listen);
199 strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
200 sizeof(depth0_listen_tcp4));
201 strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
202 sizeof(depth0_listen_tcp6));
203 depth0_exec[0] = '\0';
204 depth0_metaflush = 1;
205
206 lconfig = calloc(1, sizeof(*lconfig));
207 if (lconfig == NULL) {
208 pjdlog_error("Unable to allocate memory for configuration.");
209 if (exitonerror)
210 exit(EX_TEMPFAIL);
211 return (NULL);
212 }
213
214 TAILQ_INIT(&lconfig->hc_listen);
215 TAILQ_INIT(&lconfig->hc_resources);
216
217 yyin = fopen(config, "r");
218 if (yyin == NULL) {
219 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
220 config);
221 yy_config_free(lconfig);
222 if (exitonerror)
223 exit(EX_OSFILE);
224 return (NULL);
225 }
226 yyrestart(yyin);
227 ret = yyparse();
228 fclose(yyin);
229 if (ret != 0) {
230 yy_config_free(lconfig);
231 if (exitonerror)
232 exit(EX_CONFIG);
233 return (NULL);
234 }
235
236 /*
237 * Let's see if everything is set up.
238 */
239 if (lconfig->hc_controladdr[0] == '\0') {
240 strlcpy(lconfig->hc_controladdr, depth0_control,
241 sizeof(lconfig->hc_controladdr));
242 }
243 if (lconfig->hc_pidfile[0] == '\0') {
244 strlcpy(lconfig->hc_pidfile, depth0_pidfile,
245 sizeof(lconfig->hc_pidfile));
246 }
247 if (!TAILQ_EMPTY(&depth0_listen))
248 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
249 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
250 struct hastd_listen *lst;
251
252 if (family_supported(AF_INET)) {
253 lst = calloc(1, sizeof(*lst));
254 if (lst == NULL) {
255 pjdlog_error("Unable to allocate memory for listen address.");
256 yy_config_free(lconfig);
257 if (exitonerror)
258 exit(EX_TEMPFAIL);
259 return (NULL);
260 }
261 (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
262 sizeof(lst->hl_addr));
263 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
264 } else {
265 pjdlog_debug(1,
266 "No IPv4 support in the kernel, not listening on IPv4 address.");
267 }
268 if (family_supported(AF_INET6)) {
269 lst = calloc(1, sizeof(*lst));
270 if (lst == NULL) {
271 pjdlog_error("Unable to allocate memory for listen address.");
272 yy_config_free(lconfig);
273 if (exitonerror)
274 exit(EX_TEMPFAIL);
275 return (NULL);
276 }
277 (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
278 sizeof(lst->hl_addr));
279 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
280 } else {
281 pjdlog_debug(1,
282 "No IPv6 support in the kernel, not listening on IPv6 address.");
283 }
284 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
285 pjdlog_error("No address to listen on.");
286 yy_config_free(lconfig);
287 if (exitonerror)
288 exit(EX_TEMPFAIL);
289 return (NULL);
290 }
291 }
292 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
293 PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
294 PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
295 PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
296
297 if (curres->hr_replication == -1) {
298 /*
299 * Replication is not set at resource-level.
300 * Use global or default setting.
301 */
302 curres->hr_replication = depth0_replication;
303 }
304 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC) {
305 pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
306 "memsync", "fullsync");
307 curres->hr_replication = HAST_REPLICATION_FULLSYNC;
308 }
309 if (curres->hr_checksum == -1) {
310 /*
311 * Checksum is not set at resource-level.
312 * Use global or default setting.
313 */
314 curres->hr_checksum = depth0_checksum;
315 }
316 if (curres->hr_compression == -1) {
317 /*
318 * Compression is not set at resource-level.
319 * Use global or default setting.
320 */
321 curres->hr_compression = depth0_compression;
322 }
323 if (curres->hr_timeout == -1) {
324 /*
325 * Timeout is not set at resource-level.
326 * Use global or default setting.
327 */
328 curres->hr_timeout = depth0_timeout;
329 }
330 if (curres->hr_exec[0] == '\0') {
331 /*
332 * Exec is not set at resource-level.
333 * Use global or default setting.
334 */
335 strlcpy(curres->hr_exec, depth0_exec,
336 sizeof(curres->hr_exec));
337 }
338 if (curres->hr_metaflush == -1) {
339 /*
340 * Metaflush is not set at resource-level.
341 * Use global or default setting.
342 */
343 curres->hr_metaflush = depth0_metaflush;
344 }
345 }
346
347 return (lconfig);
348}
349
350void
351yy_config_free(struct hastd_config *config)
352{
353 struct hastd_listen *lst;
354 struct hast_resource *res;
355
356 while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
357 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
358 free(lst);
359 }
360 while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
361 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
362 free(lst);
363 }
364 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
365 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
366 free(res);
367 }
368 free(config);
369}
80static int isitme(const char *name);
81static bool family_supported(int family);
82static int node_names(char **namesp);
370%}
371
372%token CONTROL PIDFILE LISTEN REPLICATION CHECKSUM COMPRESSION METAFLUSH
373%token TIMEOUT EXEC RESOURCE NAME LOCAL REMOTE SOURCE ON OFF
374%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
375%token NUM STR OB CB
376
377%type <str> remote_str

--- 621 unchanged lines hidden (view full) ---

999 pjdlog_error("source argument is too long.");
1000 free($2);
1001 return (1);
1002 }
1003 }
1004 free($2);
1005 }
1006 ;
83%}
84
85%token CONTROL PIDFILE LISTEN REPLICATION CHECKSUM COMPRESSION METAFLUSH
86%token TIMEOUT EXEC RESOURCE NAME LOCAL REMOTE SOURCE ON OFF
87%token FULLSYNC MEMSYNC ASYNC NONE CRC32 SHA256 HOLE LZF
88%token NUM STR OB CB
89
90%type <str> remote_str

--- 621 unchanged lines hidden (view full) ---

712 pjdlog_error("source argument is too long.");
713 free($2);
714 return (1);
715 }
716 }
717 free($2);
718 }
719 ;
720
721%%
722
723static int
724isitme(const char *name)
725{
726 char buf[MAXHOSTNAMELEN];
727 char *pos;
728 size_t bufsize;
729
730 /*
731 * First check if the given name matches our full hostname.
732 */
733 if (gethostname(buf, sizeof(buf)) < 0) {
734 pjdlog_errno(LOG_ERR, "gethostname() failed");
735 return (-1);
736 }
737 if (strcmp(buf, name) == 0)
738 return (1);
739
740 /*
741 * Now check if it matches first part of the host name.
742 */
743 pos = strchr(buf, '.');
744 if (pos != NULL && (size_t)(pos - buf) == strlen(name) &&
745 strncmp(buf, name, pos - buf) == 0) {
746 return (1);
747 }
748
749 /*
750 * At the end check if name is equal to our host's UUID.
751 */
752 bufsize = sizeof(buf);
753 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
754 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
755 return (-1);
756 }
757 if (strcasecmp(buf, name) == 0)
758 return (1);
759
760 /*
761 * Looks like this isn't about us.
762 */
763 return (0);
764}
765
766static bool
767family_supported(int family)
768{
769 int sock;
770
771 sock = socket(family, SOCK_STREAM, 0);
772 if (sock == -1 && errno == EPROTONOSUPPORT)
773 return (false);
774 if (sock >= 0)
775 (void)close(sock);
776 return (true);
777}
778
779static int
780node_names(char **namesp)
781{
782 static char names[MAXHOSTNAMELEN * 3];
783 char buf[MAXHOSTNAMELEN];
784 char *pos;
785 size_t bufsize;
786
787 if (gethostname(buf, sizeof(buf)) < 0) {
788 pjdlog_errno(LOG_ERR, "gethostname() failed");
789 return (-1);
790 }
791
792 /* First component of the host name. */
793 pos = strchr(buf, '.');
794 if (pos != NULL && pos != buf) {
795 (void)strlcpy(names, buf, MIN((size_t)(pos - buf + 1),
796 sizeof(names)));
797 (void)strlcat(names, ", ", sizeof(names));
798 }
799
800 /* Full host name. */
801 (void)strlcat(names, buf, sizeof(names));
802 (void)strlcat(names, ", ", sizeof(names));
803
804 /* Host UUID. */
805 bufsize = sizeof(buf);
806 if (sysctlbyname("kern.hostuuid", buf, &bufsize, NULL, 0) < 0) {
807 pjdlog_errno(LOG_ERR, "sysctlbyname(kern.hostuuid) failed");
808 return (-1);
809 }
810 (void)strlcat(names, buf, sizeof(names));
811
812 *namesp = names;
813
814 return (0);
815}
816
817void
818yyerror(const char *str)
819{
820
821 pjdlog_error("Unable to parse configuration file at line %d near '%s': %s",
822 lineno, yytext, str);
823}
824
825struct hastd_config *
826yy_config_parse(const char *config, bool exitonerror)
827{
828 int ret;
829
830 curres = NULL;
831 mynode = false;
832 depth = 0;
833 lineno = 0;
834
835 depth0_timeout = HAST_TIMEOUT;
836 depth0_replication = HAST_REPLICATION_FULLSYNC;
837 depth0_checksum = HAST_CHECKSUM_NONE;
838 depth0_compression = HAST_COMPRESSION_HOLE;
839 strlcpy(depth0_control, HAST_CONTROL, sizeof(depth0_control));
840 strlcpy(depth0_pidfile, HASTD_PIDFILE, sizeof(depth0_pidfile));
841 TAILQ_INIT(&depth0_listen);
842 strlcpy(depth0_listen_tcp4, HASTD_LISTEN_TCP4,
843 sizeof(depth0_listen_tcp4));
844 strlcpy(depth0_listen_tcp6, HASTD_LISTEN_TCP6,
845 sizeof(depth0_listen_tcp6));
846 depth0_exec[0] = '\0';
847 depth0_metaflush = 1;
848
849 lconfig = calloc(1, sizeof(*lconfig));
850 if (lconfig == NULL) {
851 pjdlog_error("Unable to allocate memory for configuration.");
852 if (exitonerror)
853 exit(EX_TEMPFAIL);
854 return (NULL);
855 }
856
857 TAILQ_INIT(&lconfig->hc_listen);
858 TAILQ_INIT(&lconfig->hc_resources);
859
860 yyin = fopen(config, "r");
861 if (yyin == NULL) {
862 pjdlog_errno(LOG_ERR, "Unable to open configuration file %s",
863 config);
864 yy_config_free(lconfig);
865 if (exitonerror)
866 exit(EX_OSFILE);
867 return (NULL);
868 }
869 yyrestart(yyin);
870 ret = yyparse();
871 fclose(yyin);
872 if (ret != 0) {
873 yy_config_free(lconfig);
874 if (exitonerror)
875 exit(EX_CONFIG);
876 return (NULL);
877 }
878
879 /*
880 * Let's see if everything is set up.
881 */
882 if (lconfig->hc_controladdr[0] == '\0') {
883 strlcpy(lconfig->hc_controladdr, depth0_control,
884 sizeof(lconfig->hc_controladdr));
885 }
886 if (lconfig->hc_pidfile[0] == '\0') {
887 strlcpy(lconfig->hc_pidfile, depth0_pidfile,
888 sizeof(lconfig->hc_pidfile));
889 }
890 if (!TAILQ_EMPTY(&depth0_listen))
891 TAILQ_CONCAT(&lconfig->hc_listen, &depth0_listen, hl_next);
892 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
893 struct hastd_listen *lst;
894
895 if (family_supported(AF_INET)) {
896 lst = calloc(1, sizeof(*lst));
897 if (lst == NULL) {
898 pjdlog_error("Unable to allocate memory for listen address.");
899 yy_config_free(lconfig);
900 if (exitonerror)
901 exit(EX_TEMPFAIL);
902 return (NULL);
903 }
904 (void)strlcpy(lst->hl_addr, depth0_listen_tcp4,
905 sizeof(lst->hl_addr));
906 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
907 } else {
908 pjdlog_debug(1,
909 "No IPv4 support in the kernel, not listening on IPv4 address.");
910 }
911 if (family_supported(AF_INET6)) {
912 lst = calloc(1, sizeof(*lst));
913 if (lst == NULL) {
914 pjdlog_error("Unable to allocate memory for listen address.");
915 yy_config_free(lconfig);
916 if (exitonerror)
917 exit(EX_TEMPFAIL);
918 return (NULL);
919 }
920 (void)strlcpy(lst->hl_addr, depth0_listen_tcp6,
921 sizeof(lst->hl_addr));
922 TAILQ_INSERT_TAIL(&lconfig->hc_listen, lst, hl_next);
923 } else {
924 pjdlog_debug(1,
925 "No IPv6 support in the kernel, not listening on IPv6 address.");
926 }
927 if (TAILQ_EMPTY(&lconfig->hc_listen)) {
928 pjdlog_error("No address to listen on.");
929 yy_config_free(lconfig);
930 if (exitonerror)
931 exit(EX_TEMPFAIL);
932 return (NULL);
933 }
934 }
935 TAILQ_FOREACH(curres, &lconfig->hc_resources, hr_next) {
936 PJDLOG_ASSERT(curres->hr_provname[0] != '\0');
937 PJDLOG_ASSERT(curres->hr_localpath[0] != '\0');
938 PJDLOG_ASSERT(curres->hr_remoteaddr[0] != '\0');
939
940 if (curres->hr_replication == -1) {
941 /*
942 * Replication is not set at resource-level.
943 * Use global or default setting.
944 */
945 curres->hr_replication = depth0_replication;
946 }
947 if (curres->hr_replication == HAST_REPLICATION_MEMSYNC) {
948 pjdlog_warning("Replication mode \"%s\" is not implemented, falling back to \"%s\".",
949 "memsync", "fullsync");
950 curres->hr_replication = HAST_REPLICATION_FULLSYNC;
951 }
952 if (curres->hr_checksum == -1) {
953 /*
954 * Checksum is not set at resource-level.
955 * Use global or default setting.
956 */
957 curres->hr_checksum = depth0_checksum;
958 }
959 if (curres->hr_compression == -1) {
960 /*
961 * Compression is not set at resource-level.
962 * Use global or default setting.
963 */
964 curres->hr_compression = depth0_compression;
965 }
966 if (curres->hr_timeout == -1) {
967 /*
968 * Timeout is not set at resource-level.
969 * Use global or default setting.
970 */
971 curres->hr_timeout = depth0_timeout;
972 }
973 if (curres->hr_exec[0] == '\0') {
974 /*
975 * Exec is not set at resource-level.
976 * Use global or default setting.
977 */
978 strlcpy(curres->hr_exec, depth0_exec,
979 sizeof(curres->hr_exec));
980 }
981 if (curres->hr_metaflush == -1) {
982 /*
983 * Metaflush is not set at resource-level.
984 * Use global or default setting.
985 */
986 curres->hr_metaflush = depth0_metaflush;
987 }
988 }
989
990 return (lconfig);
991}
992
993void
994yy_config_free(struct hastd_config *config)
995{
996 struct hastd_listen *lst;
997 struct hast_resource *res;
998
999 while ((lst = TAILQ_FIRST(&depth0_listen)) != NULL) {
1000 TAILQ_REMOVE(&depth0_listen, lst, hl_next);
1001 free(lst);
1002 }
1003 while ((lst = TAILQ_FIRST(&config->hc_listen)) != NULL) {
1004 TAILQ_REMOVE(&config->hc_listen, lst, hl_next);
1005 free(lst);
1006 }
1007 while ((res = TAILQ_FIRST(&config->hc_resources)) != NULL) {
1008 TAILQ_REMOVE(&config->hc_resources, res, hr_next);
1009 free(res);
1010 }
1011 free(config);
1012}