Make CallHome address different that the SDN Controller RESTCONF address.
[sim/o1-interface.git] / ntsimulator / ntsim-ng / core / framework.c
1 /*************************************************************************
2 *
3 * Copyright 2020 highstreet technologies GmbH and others
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ***************************************************************************/
17
18 #define _GNU_SOURCE
19
20 #include "framework.h"
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <limits.h>
26 #include <assert.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <cjson/cJSON.h>
30
31 #include "utils/sys_utils.h"
32 #include "utils/log_utils.h"
33 #include "utils/rand_utils.h"
34
35 framework_arguments_t framework_arguments;
36 framework_environment_t framework_environment;
37 framework_config_t framework_config;
38
39 const char *argp_program_version = 0;    //is set later
40 const char *argp_program_bug_address = "<alexandru.stancu@highstreet-technologies.com> / <adrian.lita@highstreet-technologies.com>";
41 static char doc[] = "ntsim - new generation";
42
43 static struct argp_option options[] = {
44     { "container-init", 'i', 0, 0, "Runs initialization tasks for the Docker container that's being built. Do not run manually." },
45     { "supervisor", 's', 0, 0, "Run as supervisor; manager/network-function is chosen via config.json"},
46     { "manager", 'm', 0, 0, "Run the daemon as manager." },
47     { "network-function", 'f', 0, 0, "Run the daemon as network function." },
48     { "blank", 'b', 0, 0, "Run the deamon as a blank network function." },
49     { "generate", 'g', 0, 0, "Generate population data without commiting." },
50     { "test-mode", 't', 0, 0, "Test mode." },
51     
52     // tools
53     { "ls", '1', 0, 0, "Print all available root paths." },
54     { "schema", '2', "XPATH", 0, "Print schema for XPATH." },
55     
56     { "fixed-rand", 'r', "SEED", 0, "Initialize RAND seed to a fixed value (for debugging purposes)." },
57     { "verbose", 'v', "LEVEL", 0, "Verbosity level for printing to stdout (logs will still save everything). LEVEL is: 0=errors only, 1=requested info(default), 2=info" },
58     { "workspace", 'w', "PATH", 0, "Initialize workspace to a different one than the current working directory." },
59     { 0 } 
60 };
61
62 volatile sig_atomic_t framework_sigint;
63
64 static int framework_env_init(void);
65 static int framework_config_init(void);
66
67 static void framework_signal_handler(int signo);
68 static error_t parse_opt(int key, char *arg, struct argp_state *state);
69
70 int framework_init(int argc, char **argv) {
71     //environment vars
72     framework_environment.nts.version = (getenv(ENV_VAR_NTS_BUILD_VERSION) && strlen(getenv(ENV_VAR_NTS_BUILD_VERSION))) ? strdup(getenv(ENV_VAR_NTS_BUILD_VERSION)) : strdup(NTS_VERSION_FALLBACK"!");
73     if(getenv(ENV_VAR_NTS_BUILD_TIME) && strlen(getenv(ENV_VAR_NTS_BUILD_TIME))) {
74         framework_environment.nts.build_time = strdup(getenv(ENV_VAR_NTS_BUILD_TIME));
75     }
76     else {
77         if(__DATE__[0] == '?') {
78             framework_environment.nts.build_time = strdup("1970-01-01T00:00:00Z");
79         }
80         else {
81             //01234567890
82             //May  4 2021
83             int year = 0;
84             int month = 1;
85             int day = 0;
86             
87             year = (__DATE__[10] - '0') + (__DATE__[9] - '0')*10 + (__DATE__[8] - '0')*100 + (__DATE__[7] - '0')*1000;
88             day = (__DATE__[5] - '0');
89             if(__DATE__[4] != ' ') {
90                 day += (__DATE__[4] - '0')*10;
91             }
92             
93             switch(__DATE__[0]) {
94                 case 'J':
95                     switch(__DATE__[1]) {
96                         case 'a':
97                             month = 1;
98                             break;
99                         
100                         case 'u':
101                             if(__DATE__[2] == 'n') {
102                                 month = 6;
103                             }
104                             else {
105                                 month = 7;
106                             }
107                             break;
108                     }
109                     break;
110                     
111                 case 'F':
112                     month = 2;
113                     break;
114                     
115                 case 'M':
116                     switch(__DATE__[2]) {
117                         case 'r':
118                             month = 3;
119                             break;
120                             
121                         case 'y':
122                             month = 5;
123                             break;
124                     }
125                     break;
126                 
127                 case 'A':
128                     switch(__DATE__[1]) {
129                         case 'p':
130                             month = 4;
131                             break;
132                             
133                         case 'u':
134                             month = 8;
135                             break;
136                     }
137                     break;
138                     
139                 case 'S':
140                     month = 9;
141                     break;
142                     
143                 case 'O':
144                     month = 10;
145                     break;
146                     
147                 case 'N':
148                     month = 11;
149                     break;
150                     
151                 case 'D':
152                     month = 12;
153                     break;
154             }
155             
156             asprintf(&framework_environment.nts.build_time, "%04d-%02d-%02dT%sZ", year, month, day, __TIME__);
157         }
158     }
159
160     //set argp_version
161     char *version = 0;
162     asprintf(&version, "ntsim-ng v%s build %s", framework_environment.nts.version, framework_environment.nts.build_time);
163     argp_program_version = version;
164
165     //initialize app arguments
166     framework_arguments.nts_mode = NTS_MODE_DEFAULT;
167
168     framework_arguments.argc = argc;
169     framework_arguments.argv = argv;
170
171     framework_arguments.no_rand = false;
172     framework_arguments.fixed_seed = 0;
173     framework_arguments.verbosity_level = 1;
174
175     framework_arguments.print_root_paths = false;
176     framework_arguments.print_structure_xpath = 0;
177
178     //parse provided command line arguments
179     struct argp argp = { options, parse_opt, 0, doc, 0, 0, 0 };
180     argp_parse(&argp, argc, argv, 0, 0, &framework_arguments);
181
182     //manage signals
183     framework_sigint = 0;
184     signal(SIGINT, framework_signal_handler);
185     signal(SIGTERM, framework_signal_handler);
186     signal(SIGQUIT, framework_signal_handler);
187
188     //disable buffering for stdout
189     setbuf(stdout, NULL);
190
191     //init logging subsystem
192     char *log_file = 0;
193     char *stderr_file = 0;
194
195     if(!dir_exists("log")) {
196         mkdir("log", 0777);
197     }
198
199     switch(framework_arguments.nts_mode) {
200         case NTS_MODE_CONTAINER_INIT:
201             log_file = "log/log-install.txt";
202             stderr_file = "log/stderr-install.txt";
203             break;
204
205         case NTS_MODE_BLANK:
206             log_file = "log/log-blank.txt";
207             stderr_file = "log/stderr-blank.txt";
208             break;
209
210         case NTS_MODE_SUPERVISOR:
211             log_file = "log/log-supervisor.txt";
212             stderr_file = "log/stderr-supervisor.txt";
213             break;
214
215         case NTS_MODE_GENERATE_DATA:
216             log_file = "log/log-generate.txt";
217             stderr_file = "log/stderr-generate.txt";
218             break;
219
220         default:
221             log_file = "log/log.txt";
222             stderr_file = "log/stderr.txt";
223             break;
224     }
225
226     log_init(log_file);
227     log_redirect_stderr(stderr_file);
228
229     char cwd[PATH_MAX];
230     getcwd(cwd, sizeof(cwd));
231     log_add_verbose(2, "[framework] current working dir is: %s\n", cwd);
232
233     //init rand generator if needed
234     if(framework_arguments.no_rand == false) {
235         rand_init();
236     }
237     else {
238         rand_init_fixed(framework_arguments.fixed_seed);
239     }
240
241     log_add_verbose(2, "[framework] app was called: ");
242     for(int i = 0; i < argc; i++) {
243         log_add(2, "%s ", argv[i]);
244     }
245     log_add(2, "\n");
246
247     if(framework_env_init() != NTS_ERR_OK) {
248         log_error("[framework] framework_env_init() failed\n");
249         return NTS_ERR_FAILED;
250     }
251
252     if(framework_config_init() != NTS_ERR_OK) {
253         log_error("[framework] framework_config_init() failed\n");
254         return NTS_ERR_FAILED;
255     }
256
257     log_add_verbose(2, "[framework] init complete\n");
258     return NTS_ERR_OK;
259 }
260
261 static int framework_env_init(void) {
262     log_add_verbose(2, "[framework-env] started\n");
263
264     /*
265     The following env vars are taken care of by framework_init()
266         framework_environment.nts.version
267         framework_environment.nts.build_time
268     */
269
270     framework_environment.nts.manual = getenv(ENV_VAR_NTS_MANUAL) ? true : false;
271     framework_environment.nts.function_type = getenv(ENV_VAR_NTS_FUNCTION_TYPE) ? strdup(getenv(ENV_VAR_NTS_FUNCTION_TYPE)) : strdup("");
272     framework_environment.nts.nf_standalone_start_features = getenv(ENV_VAR_NTS_NF_STANDALONE_START_FEATURES) ? strdup(getenv(ENV_VAR_NTS_NF_STANDALONE_START_FEATURES)) : strdup("");
273     framework_environment.nts.nf_mount_point_addressing_method = getenv(ENV_VAR_NTS_NF_MOUNT_POINT_ADDRESSING_METHOD) ? strdup(getenv(ENV_VAR_NTS_NF_MOUNT_POINT_ADDRESSING_METHOD)) : strdup("docker-mapping");
274
275     framework_environment.settings.docker_repository = getenv(ENV_VAR_DOCKER_REPOSITORY) ? strdup(getenv(ENV_VAR_DOCKER_REPOSITORY)) : strdup("");
276     if(strlen(framework_environment.settings.docker_repository)) {
277         if(framework_environment.settings.docker_repository[strlen(framework_environment.settings.docker_repository) - 1] == '/') {
278             framework_environment.settings.docker_repository[strlen(framework_environment.settings.docker_repository) - 1] = 0;
279         }
280     }
281     framework_environment.settings.docker_engine_version = getenv(ENV_VAR_DOCKER_ENGINE_VERSION) ? strdup(getenv(ENV_VAR_DOCKER_ENGINE_VERSION)) : strdup("1.40");
282     framework_environment.settings.hostname = getenv(ENV_VAR_HOSTNAME) ? strdup(getenv(ENV_VAR_HOSTNAME)) : strdup("localhost");
283
284     bool ip_ok = get_local_ips("eth0", &framework_environment.settings.ip_v4, &framework_environment.settings.ip_v6);
285     if(!ip_ok) {
286         log_error("[framework-env] could not get local IP addresses\n");
287     }
288
289     char *ipv6_env_var = getenv(ENV_VAR_IPV6ENABLED);
290     if(ipv6_env_var == 0) {
291         log_error("[framework-env] could not get the IPv6 Enabled env variable\n");
292     }
293     framework_environment.settings.ip_v6_enabled = (strcmp(ipv6_env_var, "true") == 0) ? true : false;
294     framework_environment.settings.ssh_connections = get_int_from_string_with_default(getenv(ENV_VAR_SSH_CONNECTIONS), 1);
295     framework_environment.settings.tls_connections = get_int_from_string_with_default(getenv(ENV_VAR_TLS_CONNECTIONS), 0);
296     framework_environment.settings.ftp_connections = 1;
297     framework_environment.settings.sftp_connections = 1;
298
299     //build version and build time are set in the begining of the function
300     framework_environment.host.ip = getenv(ENV_VAR_HOST_IP) ? strdup(getenv(ENV_VAR_HOST_IP)) : strdup("127.0.0.1");
301     framework_environment.host.base_port = get_int_from_string_with_default(getenv(ENV_VAR_HOST_BASE_PORT), 1000);
302     framework_environment.host.ssh_base_port = get_int_from_string_with_default(getenv(ENV_VAR_HOST_NETCONF_SSH_BASE_PORT), 0);
303     framework_environment.host.tls_base_port = get_int_from_string_with_default(getenv(ENV_VAR_HOST_NETCONF_TLS_BASE_PORT), 0);
304     framework_environment.host.ftp_base_port = get_int_from_string_with_default(getenv(ENV_VAR_HOST_TRANSFER_FTP_BASE_PORT), 0);
305     framework_environment.host.sftp_base_port = get_int_from_string_with_default(getenv(ENV_VAR_HOST_TRANSFER_SFTP_BASE_PORT), 0);
306     
307     framework_environment.sdn_controller.protocol = getenv(ENV_VAR_SDN_CONTROLLER_IP) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_PROTOCOL)) : strdup("https");
308     framework_environment.sdn_controller.ip = getenv(ENV_VAR_SDN_CONTROLLER_IP) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_IP)) : strdup("127.0.0.1");
309     framework_environment.sdn_controller.port = get_int_from_string_with_default(getenv(ENV_VAR_SDN_CONTROLLER_PORT), 8181);
310     framework_environment.sdn_controller.callhome_ip = getenv(ENV_VAR_SDN_CONTROLLER_CALLHOME_IP) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_CALLHOME_IP)) : strdup("127.0.0.1");
311     framework_environment.sdn_controller.callhome_port = get_int_from_string_with_default(getenv(ENV_VAR_SDN_CONTROLLER_CALLHOME_PORT), 6666);
312     framework_environment.sdn_controller.username = getenv(ENV_VAR_SDN_CONTROLLER_USERNAME) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_USERNAME)) : strdup("admin");
313     framework_environment.sdn_controller.password = getenv(ENV_VAR_SDN_CONTROLLER_PASSWORD) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_PASSWORD)) : strdup("admin");
314     framework_environment.sdn_controller.port_absent = (getenv(ENV_VAR_SDN_CONTROLLER_PORT) == 0) ? true : false;
315
316     framework_environment.ves_endpoint.common_header_version = getenv(ENV_VAR_VES_COMMON_HEADER_VERSION) ? strdup(getenv(ENV_VAR_VES_COMMON_HEADER_VERSION)) : strdup("7.2");
317     framework_environment.ves_endpoint.protocol = getenv(ENV_VAR_VES_ENDPOINT_PROTOCOL) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_PROTOCOL)) : strdup("https");
318     framework_environment.ves_endpoint.ip = getenv(ENV_VAR_VES_ENDPOINT_IP) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_IP)) : strdup("127.0.0.1");
319     framework_environment.ves_endpoint.port = get_int_from_string_with_default(getenv(ENV_VAR_VES_ENDPOINT_PORT), 1234);
320     framework_environment.ves_endpoint.auth_method = getenv(ENV_VAR_VES_ENDPOINT_AUTH_METHOD) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_AUTH_METHOD)) : strdup("no-auth");
321     framework_environment.ves_endpoint.username = getenv(ENV_VAR_VES_ENDPOINT_USERNAME) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_USERNAME)) : strdup("admin");
322     framework_environment.ves_endpoint.password = getenv(ENV_VAR_VES_ENDPOINT_PASSWORD) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_PASSWORD)) : strdup("admin");
323     framework_environment.ves_endpoint.certificate = getenv(ENV_VAR_VES_ENDPOINT_CERTIFICATE) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_CERTIFICATE)) : strdup("");
324     framework_environment.ves_endpoint.port_absent = (getenv(ENV_VAR_VES_ENDPOINT_PORT) == 0) ? true : false;
325
326     log_add_verbose(2, "[framework-env] nts.manual = %d\n", framework_environment.nts.manual);
327     log_add_verbose(2, "[framework-env] nts.version = %s\n", framework_environment.nts.version);
328     log_add_verbose(2, "[framework-env] nts.build_time = %s\n", framework_environment.nts.build_time);
329     log_add_verbose(2, "[framework-env] nts.function_type = %s\n", framework_environment.nts.function_type);
330     log_add_verbose(2, "[framework-env] nts.nf_standalone_start_features = %s\n", framework_environment.nts.nf_standalone_start_features);
331     log_add_verbose(2, "[framework-env] nts.nf_mount_point_addressing_method = %s\n", framework_environment.nts.nf_mount_point_addressing_method);
332
333     log_add_verbose(2, "[framework-env] settings.docker_engine_version = %s\n", framework_environment.settings.docker_engine_version);
334     log_add_verbose(2, "[framework-env] settings.docker_repository = %s\n", framework_environment.settings.docker_repository);
335     log_add_verbose(2, "[framework-env] settings.hostname = %s\n", framework_environment.settings.hostname);
336     log_add_verbose(2, "[framework-env] settings.ip_v4 = %s\n", framework_environment.settings.ip_v4);
337     log_add_verbose(2, "[framework-env] settings.ip_v6 = %s\n", framework_environment.settings.ip_v6);
338     log_add_verbose(2, "[framework-env] settings.ip_v6_enabled = %s\n", framework_environment.settings.ip_v6_enabled ? "true" : "false");
339     log_add_verbose(2, "[framework-env] settings.ssh_connections = %d\n", framework_environment.settings.ssh_connections);
340     log_add_verbose(2, "[framework-env] settings.tls_connections = %d\n", framework_environment.settings.tls_connections);
341     log_add_verbose(2, "[framework-env] settings.ftp_connections = %d\n", framework_environment.settings.ftp_connections);
342     log_add_verbose(2, "[framework-env] settings.sftp_connections = %d\n", framework_environment.settings.sftp_connections);
343
344     //check ports
345     if(framework_environment.host.base_port < 1000) {
346         log_add_verbose(2, "[framework-env] host.base_port < 1000 -> disabling\n");
347         framework_environment.host.base_port = 0;
348     }
349
350     if(framework_environment.host.ssh_base_port < 1000) {
351         log_add_verbose(2, "[framework-env] host.ssh_base_port < 1000 -> using base_port\n");
352         framework_environment.host.ssh_base_port = framework_environment.host.base_port;
353     }
354
355     if(framework_environment.host.tls_base_port < 1000) {
356         log_add_verbose(2, "[framework-env] host.tls_base_port < 1000 -> using base_port\n");
357         framework_environment.host.tls_base_port = framework_environment.host.base_port;
358     }
359
360     if(framework_environment.host.ftp_base_port < 1000) {
361         log_add_verbose(2, "[framework-env] host.ftp_base_port < 1000 -> using base_port\n");
362         framework_environment.host.ftp_base_port = framework_environment.host.base_port;
363     }
364
365     if(framework_environment.host.sftp_base_port < 1000) {
366         log_add_verbose(2, "[framework-env] host.sftp_base_port < 1000 -> using base_port\n");
367         framework_environment.host.sftp_base_port = framework_environment.host.base_port;
368     }
369
370     if(framework_environment.host.base_port == 0) {
371         if(framework_environment.host.ssh_base_port == 0) {
372             log_error("[framework-env] host.ssh_base_port unknown\n");
373             return NTS_ERR_FAILED;
374         }
375
376         if(framework_environment.host.tls_base_port == 0) {
377             log_error("[framework-env] host.tls_base_port unknown\n");
378             return NTS_ERR_FAILED;
379         }
380
381         if(framework_environment.host.ftp_base_port == 0) {
382             log_error("[framework-env] host.ftp_base_port unknown\n");
383             return NTS_ERR_FAILED;
384         }
385
386         if(framework_environment.host.sftp_base_port == 0) {
387             log_error("[framework-env] host.sftp_base_port unknown\n");
388             return NTS_ERR_FAILED;
389         }
390     }
391     
392     log_add_verbose(2, "[framework-env] host.ip = %s\n", framework_environment.host.ip);
393     if(framework_environment.settings.ip_v6_enabled) {
394         if(strstr(framework_environment.host.ip, ".")) {
395             log_error("[framework-env] host.ip is an invalid IP v6\n");
396             return NTS_ERR_FAILED;
397         }
398     }
399     else {
400         if(strstr(framework_environment.host.ip, ":")) {
401             log_error("[framework-env] host.ip is an invalid IP v4\n");
402             return NTS_ERR_FAILED;
403         }
404     }
405
406     log_add_verbose(2, "[framework-env] host.base_port = %d\n", framework_environment.host.base_port);
407     log_add_verbose(2, "[framework-env] host.ssh_base_port = %d\n", framework_environment.host.ssh_base_port);
408     log_add_verbose(2, "[framework-env] host.tls_base_port = %d\n", framework_environment.host.tls_base_port);
409     log_add_verbose(2, "[framework-env] host.ftp_base_port = %d\n", framework_environment.host.ftp_base_port);
410     log_add_verbose(2, "[framework-env] host.sftp_base_port = %d\n", framework_environment.host.sftp_base_port);
411     
412     log_add_verbose(2, "[framework-env] sdn_controller.protocol = %s\n", framework_environment.sdn_controller.protocol);
413     log_add_verbose(2, "[framework-env] sdn_controller.ip = %s\n", framework_environment.sdn_controller.ip);
414     log_add_verbose(2, "[framework-env] sdn_controller.port = %d\n", framework_environment.sdn_controller.port);
415     log_add_verbose(2, "[framework-env] sdn_controller.callhome_ip = %s\n", framework_environment.sdn_controller.callhome_ip);
416     log_add_verbose(2, "[framework-env] sdn_controller.callhome_port = %d\n", framework_environment.sdn_controller.callhome_port);
417     log_add_verbose(2, "[framework-env] sdn_controller.username = %s\n", framework_environment.sdn_controller.username);
418     log_add_verbose(2, "[framework-env] sdn_controller.password = %s\n", framework_environment.sdn_controller.password);
419     log_add_verbose(2, "[framework-env] sdn_controller.port_absent = %d\n", framework_environment.sdn_controller.port_absent);
420
421     log_add_verbose(2, "[framework-env] ves_endpoint.common_header_version = %s\n", framework_environment.ves_endpoint.common_header_version);
422     log_add_verbose(2, "[framework-env] ves_endpoint.protocol = %s\n", framework_environment.ves_endpoint.protocol);
423     log_add_verbose(2, "[framework-env] ves_endpoint.ip = %s\n", framework_environment.ves_endpoint.ip);
424     log_add_verbose(2, "[framework-env] ves_endpoint.port = %d\n", framework_environment.ves_endpoint.port);
425     log_add_verbose(2, "[framework-env] ves_endpoint.auth_method = %s\n", framework_environment.ves_endpoint.auth_method);
426     log_add_verbose(2, "[framework-env] ves_endpoint.username = %s\n", framework_environment.ves_endpoint.username);
427     log_add_verbose(2, "[framework-env] ves_endpoint.password = %s\n", framework_environment.ves_endpoint.password);
428     log_add_verbose(2, "[framework-env] ves_endpoint.certificate = %s\n", framework_environment.ves_endpoint.certificate);
429     log_add_verbose(2, "[framework-env] ves_endpoint.port_absent = %d\n", framework_environment.ves_endpoint.port_absent);
430
431     log_add_verbose(2, "[framework-env] finished\n");
432     return NTS_ERR_OK;
433 }
434
435 static int framework_config_init(void) {
436     log_add_verbose(2, "[framework-config] started\n");
437
438     //init app config
439     framework_config.docker.excluded_modules = 0;
440     framework_config.docker.excluded_modules_count = 0;
441     framework_config.docker.excluded_features = 0;
442     framework_config.docker.excluded_features_count = 0;
443
444     framework_config.supervisor.rules_count = 0;
445     framework_config.supervisor.rules = 0;
446
447     framework_config.datastore_generate.debug_max_string_size = 0;
448     framework_config.datastore_generate.excluded_modules = 0;
449     framework_config.datastore_generate.excluded_modules_count = 0;
450     framework_config.datastore_generate.default_list_instances = 1;
451     framework_config.datastore_generate.custom_list_instances_count = 0;
452     framework_config.datastore_generate.custom_list_instances = 0;
453     framework_config.datastore_generate.restrict_schema_count = 0;
454     framework_config.datastore_generate.restrict_schema = 0;
455
456     framework_config.datastore_populate.random_generation_enabled = 1;
457     framework_config.datastore_populate.preg_operational_count = 0;
458     framework_config.datastore_populate.preg_operational = 0;
459     framework_config.datastore_populate.preg_running_count = 0;
460     framework_config.datastore_populate.preg_running = 0;
461
462     //config init
463     char *config_file = "config/config.json";
464     if(file_exists("/opt/dev/config/config.json")) {
465         config_file = "/opt/dev/config/config.json";
466         log_add_verbose(1, LOG_COLOR_BOLD_MAGENTA"config.json is loaded from external volume!\n"LOG_COLOR_RESET);
467     }
468     else {
469         if(!dir_exists("config")) {
470             log_add_verbose(2, "[framework-config] config/ folder wasn't found; created.\n");
471             mkdir("config", 0777);
472         }
473
474         if(!file_exists("config/config.json")) {
475             log_add_verbose(2, "[framework-config] config.json file missing; created.\n");
476             file_touch("config/config.json", "{}");
477         }
478     }
479
480     log_add_verbose(2, "[framework-config] parsing config.json from %s\n", config_file);
481     char *config_contents = file_read_content(config_file);
482     cJSON *json = cJSON_Parse(config_contents);
483     free(config_contents);
484     if(!json) {
485         log_error("[framework-config] config.json error: %s\n", cJSON_GetErrorPtr());
486     }
487     else {
488         cJSON *main_node;
489         cJSON *node;
490
491         if(framework_arguments.nts_mode == NTS_MODE_CONTAINER_INIT) {
492             main_node = cJSON_GetObjectItem(json, "container-rules");
493             if(main_node) {
494                 node = cJSON_GetObjectItem(main_node, "excluded-modules");
495                 if(node) {
496                     if(cJSON_IsArray(node)) {
497                         cJSON *element;
498                         cJSON_ArrayForEach(element, node) {
499                             if(cJSON_IsString(element)) {
500                                 log_add_verbose(2, "[framework-config] adding container-rules/exclude-modules: %s\n", element->valuestring);
501                                 framework_config.docker.excluded_modules = (char **)realloc(framework_config.docker.excluded_modules, sizeof(char*) * (framework_config.docker.excluded_modules_count + 1));
502                                 if(!framework_config.docker.excluded_modules) {
503                                     log_error("[framework-config] bad realloc\n");
504                                 }
505                                 framework_config.docker.excluded_modules[framework_config.docker.excluded_modules_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
506                                 if(!framework_config.docker.excluded_modules[framework_config.docker.excluded_modules_count]) {
507                                     log_error("[framework-config] bad malloc\n");
508                                 }
509                                 strcpy(framework_config.docker.excluded_modules[framework_config.docker.excluded_modules_count], element->valuestring);
510                                 framework_config.docker.excluded_modules_count++;
511                             }
512                         }
513                     }
514                 }
515
516                 node = cJSON_GetObjectItem(main_node, "excluded-features");
517                 if(node) {
518                     if(cJSON_IsArray(node)) {
519                         cJSON *element;
520                         cJSON_ArrayForEach(element, node) {
521                             if(cJSON_IsString(element)) {
522                                 log_add_verbose(2, "[framework-config] adding container-rules/excluded-features: %s\n", element->valuestring);
523                                 framework_config.docker.excluded_features = (char **)realloc(framework_config.docker.excluded_features, sizeof(char*) * (framework_config.docker.excluded_features_count + 1));
524                                 if(!framework_config.docker.excluded_features) {
525                                     log_error("[framework-config] bad realloc\n");
526                                 }
527                                 framework_config.docker.excluded_features[framework_config.docker.excluded_features_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
528                                 if(!framework_config.docker.excluded_features[framework_config.docker.excluded_features_count]) {
529                                     log_error("[framework-config] bad malloc\n");
530                                 }
531                                 strcpy(framework_config.docker.excluded_features[framework_config.docker.excluded_features_count], element->valuestring);
532                                 framework_config.docker.excluded_features_count++;
533                             }
534                         }
535                     }
536                 }
537             }
538         }
539         else if(framework_arguments.nts_mode == NTS_MODE_SUPERVISOR) {
540             main_node = cJSON_GetObjectItem(json, "supervisor-rules");
541             if(main_node) {
542                 cJSON *app;
543                 cJSON_ArrayForEach(app, main_node) {
544                     if(cJSON_IsObject(app)) {
545                         cJSON *object = cJSON_GetObjectItem(app, "path");
546                         if(object) {
547                             framework_config.supervisor.rules = (supervisor_rules_t *)realloc(framework_config.supervisor.rules, sizeof(supervisor_rules_t) * (framework_config.supervisor.rules_count + 1));
548                             if(!framework_config.supervisor.rules) {
549                                 log_error("[framework-config] bad realloc\n");
550                             }
551                             
552                             char *path = strdup(object->valuestring);
553                             bool autorestart = false;
554                             bool nomanual = false;
555                             char *stdout_path = 0;
556                             char *stderr_path = 0;
557
558                             int args_count = 0;
559                             char **args = 0;
560                             cJSON *args_json = cJSON_GetObjectItem(app, "args");
561                             if(args_json) {
562                                 args_count = cJSON_GetArraySize(args_json);
563                                 if(args_count) {
564                                     args = malloc(sizeof(char *) * args_count);
565                                     int i = 0;
566                                     cJSON *arg;
567                                     cJSON_ArrayForEach(arg, args_json) {
568                                         args[i] = strdup(arg->valuestring);
569                                         i++;
570                                     }
571                                 }
572                             }
573
574                             object = cJSON_GetObjectItem(app, "autorestart");
575                             if(object) {
576                                 autorestart = object->valueint;
577                             }
578
579                             object = cJSON_GetObjectItem(app, "nomanual");
580                             if(object) {
581                                 nomanual = object->valueint;
582                             }
583
584                             object = cJSON_GetObjectItem(app, "stdout");
585                             if(object) {
586                                 stdout_path = strdup(object->valuestring);
587                             }
588
589                             object = cJSON_GetObjectItem(app, "stderr");
590                             if(object) {
591                                 stderr_path = strdup(object->valuestring);
592                             }
593
594                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].name = strdup(app->string);
595                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].path = path;
596                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].args = args;
597                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].args_count = args_count;
598                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].autorestart = autorestart;
599                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].nomanual = nomanual;
600                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].stdout_path = stdout_path;
601                             framework_config.supervisor.rules[framework_config.supervisor.rules_count].stderr_path = stderr_path;
602
603                             log_add_verbose(2, "[framework-config] adding supervisor command: %s with autorestart: %d\n", path, autorestart);
604                             framework_config.supervisor.rules_count++;
605                         }
606                     }
607                 }
608             }
609         }
610         else {
611             main_node = cJSON_GetObjectItem(json, "datastore-random-generation-rules");
612             if(main_node) {
613                 node = cJSON_GetObjectItem(main_node, "excluded-modules");
614                 if(node) {
615                     if(cJSON_IsArray(node)) {
616                         cJSON *element;
617                         cJSON_ArrayForEach(element, node) {
618                             if(cJSON_IsString(element)) {
619                                 log_add_verbose(2, "[framework-config] adding datastore-random-generation-rules/excluded-modules: %s\n", element->valuestring);
620                                 framework_config.datastore_generate.excluded_modules = (char **)realloc(framework_config.datastore_generate.excluded_modules, sizeof(char*) * (framework_config.datastore_generate.excluded_modules_count + 1));
621                                 if(!framework_config.datastore_generate.excluded_modules) {
622                                     log_error("[framework-config] bad realloc\n");
623                                 }
624                                 framework_config.datastore_generate.excluded_modules[framework_config.datastore_generate.excluded_modules_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
625                                 if(!framework_config.datastore_generate.excluded_modules[framework_config.datastore_generate.excluded_modules_count]) {
626                                     log_error("[framework-config] bad malloc\n");
627                                 }
628                                 strcpy(framework_config.datastore_generate.excluded_modules[framework_config.datastore_generate.excluded_modules_count], element->valuestring);
629                                 framework_config.datastore_generate.excluded_modules_count++;
630                             }
631                         }
632                     }
633                 }
634
635                 node = cJSON_GetObjectItem(main_node, "debug-max-string-size");
636                 if(node) {
637                     framework_config.datastore_generate.debug_max_string_size = node->valueint;
638                     log_add_verbose(2, "[framework-config] setting datastore-random-generation-rules/debug-max-string-size: %d\n", framework_config.datastore_generate.debug_max_string_size);
639                 }
640
641                 node = cJSON_GetObjectItem(main_node, "default-list-instances");
642                 if(node) {
643                     if(cJSON_IsNumber(node)) {
644                         framework_config.datastore_generate.default_list_instances = node->valueint;
645                         log_add_verbose(2, "[framework-config] setting datastore-random-generation-rules/default-list-instances: %d\n", framework_config.datastore_generate.default_list_instances);
646                     }
647                 }
648
649                 node = cJSON_GetObjectItem(main_node, "custom-list-instances");
650                 if(node) {
651                     if(cJSON_IsArray(node)) {
652                         cJSON *element;
653                         cJSON_ArrayForEach(element, node) {
654                             if(cJSON_IsObject(element)) {
655                                 cJSON *object;
656                                 cJSON_ArrayForEach(object, element) {
657                                     char *path = object->string;
658                                     int count = object->valueint;
659                                     log_add_verbose(2, "[framework-config] adding datastore-random-generation-rules/custom-list-instances %s - %d\n", path, count);
660                                     framework_config.datastore_generate.custom_list_instances = (custom_list_instances_t *)realloc(framework_config.datastore_generate.custom_list_instances, sizeof(custom_list_instances_t) * (framework_config.datastore_generate.custom_list_instances_count + 1));
661                                     if(!framework_config.datastore_generate.custom_list_instances) {
662                                         log_error("[framework-config] bad realloc\n");
663                                     }
664                                     
665                                     framework_config.datastore_generate.custom_list_instances[framework_config.datastore_generate.custom_list_instances_count].path = (char *)malloc(sizeof(char) * (strlen(path) + 1));
666                                     if(!framework_config.datastore_generate.custom_list_instances[framework_config.datastore_generate.custom_list_instances_count].path) {
667                                         log_error("[framework-config] bad malloc\n");
668                                     }
669                                     strcpy(framework_config.datastore_generate.custom_list_instances[framework_config.datastore_generate.custom_list_instances_count].path, path);
670                                     framework_config.datastore_generate.custom_list_instances[framework_config.datastore_generate.custom_list_instances_count].count = count;
671                                     framework_config.datastore_generate.custom_list_instances_count++;
672                                 }
673                             }
674                         }
675                     }
676                 }
677
678                 node = cJSON_GetObjectItem(main_node, "restrict-schema");
679                 if(node) {
680                     if(cJSON_IsArray(node)) {
681                         cJSON *element;
682                         cJSON_ArrayForEach(element, node) {
683                             if(cJSON_IsObject(element)) {
684                                 cJSON *object;
685                                 cJSON_ArrayForEach(object, element) {
686                                     char *path = object->string;
687
688                                     log_add_verbose(2, "[framework-config] adding datastore-random-generation-rules/restrict-schema: %s with values:", path);
689                                     framework_config.datastore_generate.restrict_schema = (restrict_schema_t *)realloc(framework_config.datastore_generate.restrict_schema, sizeof(restrict_schema_t) * (framework_config.datastore_generate.restrict_schema_count + 1));
690                                     if(!framework_config.datastore_generate.restrict_schema) {
691                                         log_error("[framework-config] bad realloc\n");
692                                     }
693                                     
694                                     framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].path = (char *)malloc(sizeof(char) * (strlen(path) + 1));
695                                     if(!framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].path) {
696                                         log_error("[framework-config] bad malloc\n");
697                                     }
698                                     strcpy(framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].path, path);
699
700
701                                     framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values_count = 0;
702                                     framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values = 0;
703                                     framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].index = 0;
704                                     cJSON *value;
705                                     cJSON_ArrayForEach(value, object) {
706                                         if(cJSON_IsString(value)) {
707                                             framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values = (char **)realloc(framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values, sizeof(char*) * (framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values_count + 1));
708                                             if(!framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values) {
709                                                 log_error("[framework-config] bad realloc\n");
710                                             }
711                                             framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values[framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values_count] = (char *)malloc(sizeof(char) * (strlen(value->valuestring) + 1));
712                                             if(!framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values[framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values_count]) {
713                                                 log_error("[framework-config] bad malloc\n");
714                                             }
715                                             strcpy(framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values[framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values_count], value->valuestring);
716                                             framework_config.datastore_generate.restrict_schema[framework_config.datastore_generate.restrict_schema_count].values_count++;
717
718                                             log_add(2, " %s", value->valuestring);
719                                         }
720                                     }
721                                     log_add(2, "\n");
722
723                                     framework_config.datastore_generate.restrict_schema_count++;
724                                 }
725                             }
726                         }
727                     }
728                 }
729             }
730
731             main_node = cJSON_GetObjectItem(json, "datastore-populate-rules");
732             if(main_node) {
733                 node = cJSON_GetObjectItem(main_node, "random-generation-enabled");
734                 if(node) {
735                     framework_config.datastore_populate.random_generation_enabled = node->valueint;
736                     log_add_verbose(2, "[framework-config] setting datastore-populate-rules/random-generation-enabled: %d\n", framework_config.datastore_populate.random_generation_enabled);
737                 }
738                 else {
739                     log_add_verbose(2, "[framework-config] setting datastore-populate-rules/random-generation-enabled: %d [default value]\n", framework_config.datastore_populate.random_generation_enabled);
740                 }
741                 
742                 node = cJSON_GetObjectItem(main_node, "pre-generated-operational-data");
743                 if(node) {
744                     if(cJSON_IsArray(node)) {
745                         cJSON *element;
746                         cJSON_ArrayForEach(element, node) {
747                             if(cJSON_IsString(element)) {
748                                 log_add_verbose(2, "[framework-config] adding datastore-populate-rules/pre-generated-operational-data: %s\n", element->valuestring);
749                                 framework_config.datastore_populate.preg_operational = (char **)realloc(framework_config.datastore_populate.preg_operational, sizeof(char*) * (framework_config.datastore_populate.preg_operational_count + 1));
750                                 if(!framework_config.datastore_populate.preg_operational) {
751                                     log_error("[framework-config] bad realloc\n");
752                                 }
753                                 framework_config.datastore_populate.preg_operational[framework_config.datastore_populate.preg_operational_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
754                                 if(!framework_config.datastore_populate.preg_operational[framework_config.datastore_populate.preg_operational_count]) {
755                                     log_error("[framework-config] bad malloc\n");
756                                 }
757                                 strcpy(framework_config.datastore_populate.preg_operational[framework_config.datastore_populate.preg_operational_count], element->valuestring);
758                                 framework_config.datastore_populate.preg_operational_count++;
759                             }
760                         }
761                     }
762                 }
763
764                 node = cJSON_GetObjectItem(main_node, "pre-generated-running-data");
765                 if(node) {
766                     if(cJSON_IsArray(node)) {
767                         cJSON *element;
768                         cJSON_ArrayForEach(element, node) {
769                             if(cJSON_IsString(element)) {
770                                 log_add_verbose(2, "[framework-config] adding datastore-populate-rules/pre-generated-running-data: %s\n", element->valuestring);
771                                 framework_config.datastore_populate.preg_running = (char **)realloc(framework_config.datastore_populate.preg_running, sizeof(char*) * (framework_config.datastore_populate.preg_running_count + 1));
772                                 if(!framework_config.datastore_populate.preg_running) {
773                                     log_error("[framework-config] bad realloc\n");
774                                 }
775                                 framework_config.datastore_populate.preg_running[framework_config.datastore_populate.preg_running_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
776                                 if(!framework_config.datastore_populate.preg_running[framework_config.datastore_populate.preg_running_count]) {
777                                     log_error("[framework-config] bad malloc\n");
778                                 }
779                                 strcpy(framework_config.datastore_populate.preg_running[framework_config.datastore_populate.preg_running_count], element->valuestring);
780                                 framework_config.datastore_populate.preg_running_count++;
781                             }
782                         }
783                     }
784                 }
785
786             }
787         }
788
789         cJSON_Delete(json);
790         
791     }
792     log_add_verbose(2, "[framework-config] finished parsing config.json\n");
793
794     return NTS_ERR_OK;
795 }
796
797 void framework_free(void) {
798     log_add_verbose(2, "[framework-config] framework_free()... ");
799
800     signal(SIGINT, 0);
801     signal(SIGTERM, 0);
802     signal(SIGQUIT, 0);
803
804     free((char *)argp_program_version);
805     argp_program_version = 0;
806
807     free(framework_environment.nts.version);
808     free(framework_environment.nts.build_time);
809     free(framework_environment.nts.function_type);
810     free(framework_environment.nts.nf_standalone_start_features);
811     free(framework_environment.nts.nf_mount_point_addressing_method);
812     free(framework_environment.settings.ip_v4);
813     free(framework_environment.settings.ip_v6);
814     free(framework_environment.settings.docker_engine_version);
815     free(framework_environment.settings.docker_repository);
816     free(framework_environment.settings.hostname);
817     free(framework_environment.host.ip);
818     free(framework_environment.sdn_controller.protocol);
819     free(framework_environment.sdn_controller.ip);
820     free(framework_environment.sdn_controller.callhome_ip);
821     free(framework_environment.sdn_controller.username);
822     free(framework_environment.sdn_controller.password);
823     free(framework_environment.ves_endpoint.common_header_version);
824     free(framework_environment.ves_endpoint.protocol);
825     free(framework_environment.ves_endpoint.ip);
826     free(framework_environment.ves_endpoint.auth_method);
827     free(framework_environment.ves_endpoint.username);
828     free(framework_environment.ves_endpoint.password);
829     free(framework_environment.ves_endpoint.certificate);
830
831     free(framework_arguments.print_structure_xpath);
832     framework_arguments.print_structure_xpath = 0;
833
834     for(int i = 0; i < framework_config.supervisor.rules_count; i++) {
835         free(framework_config.supervisor.rules[i].name);
836         free(framework_config.supervisor.rules[i].path);
837         for(int j = 0; j < framework_config.supervisor.rules[i].args_count; j++) {
838             free(framework_config.supervisor.rules[i].args[j]);
839         }
840         free(framework_config.supervisor.rules[i].args);
841         free(framework_config.supervisor.rules[i].stdout_path);
842         free(framework_config.supervisor.rules[i].stderr_path);
843     }
844
845     free(framework_config.supervisor.rules);
846
847     for(int i = 0; i < framework_config.docker.excluded_modules_count; i++) {
848         free(framework_config.docker.excluded_modules[i]);
849     }
850     free(framework_config.docker.excluded_modules);
851
852     for(int i = 0; i < framework_config.docker.excluded_features_count; i++) {
853         free(framework_config.docker.excluded_features[i]);
854     }
855     free(framework_config.docker.excluded_features);
856     
857     for(int i = 0; i < framework_config.datastore_generate.excluded_modules_count; i++) {
858         free(framework_config.datastore_generate.excluded_modules[i]);
859     }
860     free(framework_config.datastore_generate.excluded_modules);
861
862
863     for(int i = 0; i < framework_config.datastore_generate.custom_list_instances_count; i++) {
864         free(framework_config.datastore_generate.custom_list_instances[i].path);
865         
866     }
867     free(framework_config.datastore_generate.custom_list_instances);
868
869     for(int i = 0; i < framework_config.datastore_generate.restrict_schema_count; i++) {
870         free(framework_config.datastore_generate.restrict_schema[i].path);
871         for(int j = 0; j < framework_config.datastore_generate.restrict_schema[i].values_count; j++) {
872             free(framework_config.datastore_generate.restrict_schema[i].values[j]);
873         }
874         free(framework_config.datastore_generate.restrict_schema[i].values);
875     }
876     free(framework_config.datastore_generate.restrict_schema);
877
878     for(int i = 0; i < framework_config.datastore_populate.preg_operational_count; i++) {
879         free(framework_config.datastore_populate.preg_operational[i]);
880     }
881     free(framework_config.datastore_populate.preg_operational);
882
883     for(int i = 0; i < framework_config.datastore_populate.preg_running_count; i++) {
884         free(framework_config.datastore_populate.preg_running[i]);
885     }
886     free(framework_config.datastore_populate.preg_running);
887
888
889     log_add(2, "done\n");
890     log_close();
891 }
892
893 static void framework_signal_handler(int signo) {
894     framework_sigint = 1;
895 }
896
897 static error_t parse_opt(int key, char *arg, struct argp_state *state) {
898     framework_arguments_t *iter_arguments = state->input;
899     switch (key) {
900         case 'i':
901             iter_arguments->nts_mode = NTS_MODE_CONTAINER_INIT;
902             break;
903
904         case 's':
905             iter_arguments->nts_mode = NTS_MODE_SUPERVISOR;
906             break;
907
908         case 'm':
909             iter_arguments->nts_mode = NTS_MODE_MANAGER;
910             break;
911
912         case 'f':
913             iter_arguments->nts_mode = NTS_MODE_NETWORK_FUNCTION;
914             break;
915
916         case 'b':
917             iter_arguments->nts_mode = NTS_MODE_BLANK;
918             break;
919
920         case 'g':
921             iter_arguments->nts_mode = NTS_MODE_GENERATE_DATA;
922             break;
923
924         case 't':
925             iter_arguments->nts_mode = NTS_MODE_TEST;
926             break;
927
928         case 'r':
929             iter_arguments->no_rand = true;
930             framework_arguments.fixed_seed = 0;
931             int i = 0;
932             while(arg[i]) {
933                 framework_arguments.fixed_seed *= 10;
934                 framework_arguments.fixed_seed += arg[i] - '0';
935                 i++;
936             }
937             break;
938
939         case 'v':
940             iter_arguments->verbosity_level = arg[0] - '0';
941             break;
942
943         case 'w':
944             chdir(arg);
945             break;
946
947         case '1':
948             iter_arguments->print_root_paths = true;
949             break;
950
951         case '2':
952             iter_arguments->print_structure_xpath = (char *)malloc(sizeof(char) * (strlen(arg) + 1));
953             if(!iter_arguments->print_structure_xpath) {
954                 log_error("[framework-arg] bad malloc\n");
955                 return 1;
956             }
957             strcpy(iter_arguments->print_structure_xpath, arg);
958             if(arg[strlen(arg) - 1] == '/') {
959                 iter_arguments->print_structure_xpath[strlen(arg) - 1] = 0;
960             }
961             break;
962
963         case ARGP_KEY_ARG:
964             if (state->arg_num >= 2) {
965                 argp_usage(state);
966             }
967             break;
968
969         case ARGP_KEY_END:
970
971             break;
972
973         default:
974             return ARGP_ERR_UNKNOWN;
975     }
976     
977     return 0;
978 }