1 /*************************************************************************
3 * Copyright 2020 highstreet technologies GmbH and others
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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 ***************************************************************************/
20 #include "framework.h"
27 #include <sys/types.h>
29 #include <cjson/cJSON.h>
31 #include "utils/sys_utils.h"
32 #include "utils/log_utils.h"
33 #include "utils/rand_utils.h"
35 framework_arguments_t framework_arguments;
36 framework_config_t framework_config;
37 framework_environment_t framework_environment;
39 const char *argp_program_version = "ntsim-ng v0.0.1a";
40 const char *argp_program_bug_address = "<alexandru.stancu@highstreet-technologies.com> / <adrian.lita@highstreet-technologies.com>";
41 static char doc[] = "ntsim - new generation";
43 static struct argp_option options[] = {
44 // docker init functionality, independent from rest of the app
45 { "docker-init", 'i', 0, 0, "Runs initialization tasks for the Docker container that's being built. Do not run manually." },
47 // daemon modes (choose only one)
48 { "manager", 'm', 0, 0, "Run the daemon as manager." },
49 { "network-function", 'f', 0, 0, "Run the daemon as network function." },
51 // global settings, can be combined
52 { "operational-only", 'o', 0, 0, "When this is set, the RUNNING datastore is actually the OPERATIONAL one." },
53 { "fixed-rand", 'r', "SEED", 0, "Initialize RAND seed to a fixed value (for debugging purposes)." },
54 { "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" },
55 { "workspace", 'w', "PATH", 0, "Initialize workspace to a different one than the current working directory." },
57 // test modes (choose only one)
58 { "test-mode", 't', 0, 0, "Test mode to be deleted after." },
59 { "exhaustive-test", '0', 0, 0, "Do an automated test on the whole delpoy." },
61 // functions, can be combined
62 { "ls", '1', 0, 0, "Print all available root paths." },
63 { "schema", '2', "XPATH", 0, "Print schema for XPATH." },
64 { "populate", '3', 0, 0, "Populate everything." },
65 { "enable-features", '4', 0, 0, "Enables features. Usually works combined with populate." },
67 // function settings, can be combined with functions as well
68 { "nc-server-init", 'n', 0, 0, "Sets netconf server configuration." },
69 { "loop", 'l', 0, 0, "After doing the job, don't exit until CTRL+C is pressed." },
73 volatile sig_atomic_t framework_sigint;
74 static void framework_signal_handler(int signo);
76 static error_t parse_opt(int key, char *arg, struct argp_state *state);
78 void framework_init(int argc, char **argv) {
79 //initialize app arguments
80 framework_arguments.container_init = false;
81 framework_arguments.nc_server_init = false;
83 framework_arguments.manager = false;
84 framework_arguments.network_function = false;
86 framework_arguments.no_rand = false;
87 framework_arguments.fixed_seed = 0;
88 framework_arguments.operational_only = false;
89 framework_arguments.verbosity_level = 1;
90 framework_arguments.loop = false;
91 framework_arguments.test_mode = false;
93 framework_arguments.exhaustive_test = false;
94 framework_arguments.print_root_paths = false;
95 framework_arguments.print_structure_xpath = 0;
96 framework_arguments.populate_all = false;
97 framework_arguments.enable_features = false;
100 signal(SIGINT, framework_signal_handler);
101 signal(SIGTERM, framework_signal_handler);
102 signal(SIGKILL, framework_signal_handler);
103 signal(SIGQUIT, framework_signal_handler);
105 //parse provided command line arguments
106 struct argp argp = { options, parse_opt, 0, doc, 0, 0, 0 };
107 argp_parse(&argp, argc, argv, 0, 0, &framework_arguments);
109 //disable buffering for stdout
110 setbuf(stdout, NULL);
114 //test whether log and config folders are ok
115 if(!dir_exists("config")) {
117 mkdir("config", 0777);
120 if(!dir_exists("log")) {
125 //init logging subsystem
126 log_init("log/log.txt");
127 log_message(2, "app was called: ");
128 for(int i = 0; i < argc; i++) {
129 log_message(2, "%s ", argv[i]);
131 log_message(2, "\n");
134 getcwd(cwd, sizeof(cwd));
135 log_message(2, "current working dir is: %s\n", cwd);
138 log_message(2, "config folder wasn't found, and was created.\n");
142 log_message(2, "log folder wasn't found, and was created.\n");
145 if(!file_exists("config/config.json")) {
146 log_message(2, "config.json file missing. created.\n");
147 file_touch("config/config.json", "{}");
150 //init rand generator if needed
151 if(framework_arguments.no_rand == false) {
155 rand_init_fixed(framework_arguments.fixed_seed);
159 framework_config.docker_excluded_modules = 0;
160 framework_config.docker_excluded_modules_count = 0;
161 framework_config.docker_excluded_features = 0;
162 framework_config.docker_excluded_features_count = 0;
163 framework_config.debug_max_string_size = 0;
164 framework_config.populate_excluded_modules = 0;
165 framework_config.populate_excluded_modules_count = 0;
166 framework_config.default_list_instances = 1;
167 framework_config.custom_list_instances_count = 0;
168 framework_config.custom_list_instances = 0;
169 framework_config.restrict_schema_count = 0;
170 framework_config.restrict_schema = 0;
172 log_message(2, "starting parsing config.json\n");
173 char *config_contents = file_read_content("config/config.json");
174 cJSON *json = cJSON_Parse(config_contents);
175 free(config_contents);
177 log_error("config.json :%s", cJSON_GetErrorPtr());
183 main_node = cJSON_GetObjectItem(json, "docker-rules");
185 node = cJSON_GetObjectItem(main_node, "excluded-modules");
187 if(cJSON_IsArray(node)) {
189 cJSON_ArrayForEach(element, node) {
190 if(cJSON_IsString(element)) {
191 log_message(2, "adding docker-rules/exclude-modules: %s\n", element->valuestring);
192 framework_config.docker_excluded_modules = (char **)realloc(framework_config.docker_excluded_modules, sizeof(char*) * (framework_config.docker_excluded_modules_count + 1));
193 if(!framework_config.docker_excluded_modules) {
194 log_error("bad realloc");
196 framework_config.docker_excluded_modules[framework_config.docker_excluded_modules_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
197 if(!framework_config.docker_excluded_modules[framework_config.docker_excluded_modules_count]) {
198 log_error("bad malloc");
200 strcpy(framework_config.docker_excluded_modules[framework_config.docker_excluded_modules_count], element->valuestring);
201 framework_config.docker_excluded_modules_count++;
207 node = cJSON_GetObjectItem(main_node, "excluded-features");
209 if(cJSON_IsArray(node)) {
211 cJSON_ArrayForEach(element, node) {
212 if(cJSON_IsString(element)) {
213 log_message(2, "adding docker-rules/excluded-features: %s\n", element->valuestring);
214 framework_config.docker_excluded_features = (char **)realloc(framework_config.docker_excluded_features, sizeof(char*) * (framework_config.docker_excluded_features_count + 1));
215 if(!framework_config.docker_excluded_features) {
216 log_error("bad realloc");
218 framework_config.docker_excluded_features[framework_config.docker_excluded_features_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
219 if(!framework_config.docker_excluded_features[framework_config.docker_excluded_features_count]) {
220 log_error("bad malloc");
222 strcpy(framework_config.docker_excluded_features[framework_config.docker_excluded_features_count], element->valuestring);
223 framework_config.docker_excluded_features_count++;
230 main_node = cJSON_GetObjectItem(json, "debug-max-string-size");
232 framework_config.debug_max_string_size = main_node->valueint;
233 log_message(2, "setting debug-max-string-sizes: %d\n", framework_config.debug_max_string_size);
236 main_node = cJSON_GetObjectItem(json, "populate-rules");
238 node = cJSON_GetObjectItem(main_node, "excluded-modules");
240 if(cJSON_IsArray(node)) {
242 cJSON_ArrayForEach(element, node) {
243 if(cJSON_IsString(element)) {
244 log_message(2, "adding populate-rules/excluded-modules: %s\n", element->valuestring);
245 framework_config.populate_excluded_modules = (char **)realloc(framework_config.populate_excluded_modules, sizeof(char*) * (framework_config.populate_excluded_modules_count + 1));
246 if(!framework_config.populate_excluded_modules) {
247 log_error("bad realloc");
249 framework_config.populate_excluded_modules[framework_config.populate_excluded_modules_count] = (char *)malloc(sizeof(char) * (strlen(element->valuestring) + 1));
250 if(!framework_config.populate_excluded_modules[framework_config.populate_excluded_modules_count]) {
251 log_error("bad malloc");
253 strcpy(framework_config.populate_excluded_modules[framework_config.populate_excluded_modules_count], element->valuestring);
254 framework_config.populate_excluded_modules_count++;
260 node = cJSON_GetObjectItem(main_node, "default-list-instances");
262 if(cJSON_IsNumber(node)) {
263 framework_config.default_list_instances = node->valueint;
264 log_message(2, "found populate-rules/default-list-instances to be: %d\n", framework_config.default_list_instances);
268 node = cJSON_GetObjectItem(main_node, "custom-list-instances");
270 if(cJSON_IsArray(node)) {
272 cJSON_ArrayForEach(element, node) {
273 if(cJSON_IsObject(element)) {
275 cJSON_ArrayForEach(object, element) {
276 char *path = object->string;
277 int count = object->valueint;
278 log_message(2, "adding populate-rules/custom-list-instances %s - %d\n", path, count);
279 framework_config.custom_list_instances = (custom_list_instances_t *)realloc(framework_config.custom_list_instances, sizeof(custom_list_instances_t) * (framework_config.custom_list_instances_count + 1));
280 if(!framework_config.custom_list_instances) {
281 log_error("bad realloc");
284 framework_config.custom_list_instances[framework_config.custom_list_instances_count].path = (char *)malloc(sizeof(char) * (strlen(path) + 1));
285 if(!framework_config.custom_list_instances[framework_config.custom_list_instances_count].path) {
286 log_error("bad malloc");
288 strcpy(framework_config.custom_list_instances[framework_config.custom_list_instances_count].path, path);
289 framework_config.custom_list_instances[framework_config.custom_list_instances_count].count = count;
290 framework_config.custom_list_instances_count++;
297 node = cJSON_GetObjectItem(main_node, "restrict-schema");
299 if(cJSON_IsArray(node)) {
301 cJSON_ArrayForEach(element, node) {
302 if(cJSON_IsObject(element)) {
304 cJSON_ArrayForEach(object, element) {
305 char *path = object->string;
307 log_message(2, "adding populate-rules/restrict-schema: %s with values:", path);
308 framework_config.restrict_schema = (restrict_schema_t *)realloc(framework_config.restrict_schema, sizeof(restrict_schema_t) * (framework_config.restrict_schema_count + 1));
309 if(!framework_config.restrict_schema) {
310 log_error("bad realloc");
313 framework_config.restrict_schema[framework_config.restrict_schema_count].path = (char *)malloc(sizeof(char) * (strlen(path) + 1));
314 if(!framework_config.restrict_schema[framework_config.restrict_schema_count].path) {
315 log_error("bad malloc");
317 strcpy(framework_config.restrict_schema[framework_config.restrict_schema_count].path, path);
320 framework_config.restrict_schema[framework_config.restrict_schema_count].values_count = 0;
321 framework_config.restrict_schema[framework_config.restrict_schema_count].values = 0;
322 framework_config.restrict_schema[framework_config.restrict_schema_count].index = 0;
324 cJSON_ArrayForEach(value, object) {
325 if(cJSON_IsString(value)) {
326 framework_config.restrict_schema[framework_config.restrict_schema_count].values = (char **)realloc(framework_config.restrict_schema[framework_config.restrict_schema_count].values, sizeof(char*) * (framework_config.restrict_schema[framework_config.restrict_schema_count].values_count + 1));
327 if(!framework_config.restrict_schema[framework_config.restrict_schema_count].values) {
328 log_error("bad realloc");
330 framework_config.restrict_schema[framework_config.restrict_schema_count].values[framework_config.restrict_schema[framework_config.restrict_schema_count].values_count] = (char *)malloc(sizeof(char) * (strlen(value->valuestring) + 1));
331 if(!framework_config.restrict_schema[framework_config.restrict_schema_count].values[framework_config.restrict_schema[framework_config.restrict_schema_count].values_count]) {
332 log_error("bad malloc");
334 strcpy(framework_config.restrict_schema[framework_config.restrict_schema_count].values[framework_config.restrict_schema[framework_config.restrict_schema_count].values_count], value->valuestring);
335 framework_config.restrict_schema[framework_config.restrict_schema_count].values_count++;
337 log_message(2, " %s", value->valuestring);
340 log_message(2, "\n");
342 framework_config.restrict_schema_count++;
353 log_message(2, "finished parsing config.json\n");
356 bool ip_ok = get_local_ips("eth0", &framework_environment.ip_v4, &framework_environment.ip_v6);
358 log_error("could not get local IP addresses");
361 char *ipv6_env_var = getenv(ENV_VAR_IPV6ENABLED);
362 if(ipv6_env_var == 0) {
363 log_error("could not get the IPv6 Enabled env variable");
365 framework_environment.ip_v6_enabled = (strcmp(ipv6_env_var, "true") == 0) ? true : false;
368 framework_environment.docker_engine_version = getenv(ENV_VAR_DOCKER_ENGINE_VERSION) ? strdup(getenv(ENV_VAR_DOCKER_ENGINE_VERSION)) : strdup("1.40");
369 framework_environment.hostname = getenv(ENV_VAR_HOSTNAME) ? strdup(getenv(ENV_VAR_HOSTNAME)) : strdup("localhost");
370 framework_environment.host_ip = getenv(ENV_VAR_HOST_IP) ? strdup(getenv(ENV_VAR_HOST_IP)) : strdup("127.0.0.1");
371 framework_environment.host_base_port = get_int_from_string_with_default(getenv(ENV_VAR_HOST_BASE_PORT), 1000);
372 framework_environment.ssh_connections = get_int_from_string_with_default(getenv(ENV_VAR_SSH_CONNECTIONS), 1);
373 framework_environment.tls_connections = get_int_from_string_with_default(getenv(ENV_VAR_TLS_CONNECTIONS), 0);
374 framework_environment.ftp_connections = 1;
375 framework_environment.sftp_connections = 1;
377 framework_environment.sdn_controller_ip = getenv(ENV_VAR_SDN_CONTROLLER_IP) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_IP)) : strdup("127.0.0.1");
378 framework_environment.sdn_controller_port = get_int_from_string_with_default(getenv(ENV_VAR_SDN_CONTROLLER_PORT), 8181);
379 framework_environment.sdn_controller_callhome_port = get_int_from_string_with_default(getenv(ENV_VAR_SDN_CONTROLLER_CALLHOME_PORT), 6666);
380 framework_environment.sdn_controller_username = getenv(ENV_VAR_SDN_CONTROLLER_USERNAME) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_USERNAME)) : strdup("admin");
381 framework_environment.sdn_controller_password = getenv(ENV_VAR_SDN_CONTROLLER_PASSWORD) ? strdup(getenv(ENV_VAR_SDN_CONTROLLER_PASSWORD)) : strdup("admin");
383 framework_environment.ves_endpoint_protocol = getenv(ENV_VAR_VES_ENDPOINT_PROTOCOL) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_PROTOCOL)) : strdup("https");
384 framework_environment.ves_endpoint_ip = getenv(ENV_VAR_VES_ENDPOINT_IP) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_IP)) : strdup("127.0.0.1");
385 framework_environment.ves_endpoint_port = get_int_from_string_with_default(getenv(ENV_VAR_VES_ENDPOINT_PORT), 1234);
386 framework_environment.ves_endpoint_auth_method = getenv(ENV_VAR_VES_ENDPOINT_AUTH_METHOD) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_AUTH_METHOD)) : strdup("no-auth");
387 framework_environment.ves_endpoint_username = getenv(ENV_VAR_VES_ENDPOINT_USERNAME) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_USERNAME)) : strdup("admin");
388 framework_environment.ves_endpoint_password = getenv(ENV_VAR_VES_ENDPOINT_PASSWORD) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_PASSWORD)) : strdup("admin");
389 framework_environment.ves_endpoint_certificate = getenv(ENV_VAR_VES_ENDPOINT_CERTIFICATE) ? strdup(getenv(ENV_VAR_VES_ENDPOINT_CERTIFICATE)) : strdup("");
391 log_message(2, "[env] ip_v6_enabled = %s\n", framework_environment.ip_v6_enabled ? "true" : "false");
392 log_message(2, "[env] ip_v4 = %s\n", framework_environment.ip_v4);
393 log_message(2, "[env] ip_v6 = %s\n", framework_environment.ip_v6);
394 log_message(2, "[env] docker_engine_version = %s\n", framework_environment.docker_engine_version);
395 log_message(2, "[env] hostname = %s\n", framework_environment.hostname);
396 log_message(2, "[env] host_ip = %s\n", framework_environment.host_ip);
397 log_message(2, "[env] host_base_port = %d\n", framework_environment.host_base_port);
398 log_message(2, "[env] ssh_connections = %d\n", framework_environment.ssh_connections);
399 log_message(2, "[env] tls_connections = %d\n", framework_environment.tls_connections);
400 log_message(2, "[env] ftp_connections = %d\n", framework_environment.ftp_connections);
401 log_message(2, "[env] sftp_connections = %d\n", framework_environment.sftp_connections);
403 log_message(2, "[env] sdn_controller_ip = %s\n", framework_environment.sdn_controller_ip);
404 log_message(2, "[env] sdn_controller_port = %d\n", framework_environment.sdn_controller_port);
405 log_message(2, "[env] sdn_controller_callhome_port = %d\n", framework_environment.sdn_controller_callhome_port);
406 log_message(2, "[env] sdn_controller_username = %s\n", framework_environment.sdn_controller_username);
407 log_message(2, "[env] sdn_controller_password = %s\n", framework_environment.sdn_controller_password);
409 log_message(2, "[env] ves_endpoint_protocol = %s\n", framework_environment.ves_endpoint_protocol);
410 log_message(2, "[env] ves_endpoint_ip = %s\n", framework_environment.ves_endpoint_ip);
411 log_message(2, "[env] ves_endpoint_port = %d\n", framework_environment.ves_endpoint_port);
412 log_message(2, "[env] ves_endpoint_auth_method = %s\n", framework_environment.ves_endpoint_auth_method);
413 log_message(2, "[env] ves_endpoint_username = %s\n", framework_environment.ves_endpoint_username);
414 log_message(2, "[env] ves_endpoint_password = %s\n", framework_environment.ves_endpoint_password);
415 log_message(2, "[env] ves_endpoint_certificate = %s\n", framework_environment.ves_endpoint_certificate);
417 log_message(2, "finished environment vars\n");
420 void framework_free(void) {
421 log_message(2, "framework_free()... ");
423 free(framework_environment.ip_v4);
424 free(framework_environment.ip_v6);
425 free(framework_environment.docker_engine_version);
426 free(framework_environment.hostname);
427 free(framework_environment.host_ip);
428 free(framework_environment.sdn_controller_ip);
429 free(framework_environment.sdn_controller_username);
430 free(framework_environment.sdn_controller_password);
431 free(framework_environment.ves_endpoint_protocol);
432 free(framework_environment.ves_endpoint_ip);
433 free(framework_environment.ves_endpoint_auth_method);
434 free(framework_environment.ves_endpoint_username);
435 free(framework_environment.ves_endpoint_password);
436 free(framework_environment.ves_endpoint_certificate);
438 if(framework_arguments.print_structure_xpath) {
439 free(framework_arguments.print_structure_xpath);
440 framework_arguments.print_structure_xpath = 0;
443 if(framework_config.docker_excluded_modules_count) {
444 for(int i = 0; i < framework_config.docker_excluded_modules_count; i++) {
445 free(framework_config.docker_excluded_modules[i]);
447 free(framework_config.docker_excluded_modules);
450 if(framework_config.docker_excluded_features_count) {
451 for(int i = 0; i < framework_config.docker_excluded_features_count; i++) {
452 free(framework_config.docker_excluded_features[i]);
454 free(framework_config.docker_excluded_features);
457 if(framework_config.populate_excluded_modules_count) {
458 for(int i = 0; i < framework_config.populate_excluded_modules_count; i++) {
459 free(framework_config.populate_excluded_modules[i]);
461 free(framework_config.populate_excluded_modules);
464 for(int i = 0; i < framework_config.custom_list_instances_count; i++) {
465 free(framework_config.custom_list_instances[i].path);
468 free(framework_config.custom_list_instances);
470 for(int i = 0; i < framework_config.restrict_schema_count; i++) {
471 free(framework_config.restrict_schema[i].path);
472 for(int j = 0; j < framework_config.restrict_schema[i].values_count; j++) {
473 free(framework_config.restrict_schema[i].values[j]);
475 free(framework_config.restrict_schema[i].values);
477 free(framework_config.restrict_schema);
479 log_message(2, "done\n");
481 if(framework_arguments.container_init) {
482 rename("log/log.txt", "log/install_log.txt");
483 rename("log/error.txt", "log/install_error.txt");
487 bool framework_is_docker_excluded_module(const char *module) {
490 for(int i = 0; i < framework_config.docker_excluded_modules_count; i++) {
491 if(strstr(module, framework_config.docker_excluded_modules[i]) != 0) {
499 bool framework_is_docker_excluded_feature(const char *feature) {
502 for(int i = 0; i < framework_config.docker_excluded_features_count; i++) {
503 if(strstr(feature, framework_config.docker_excluded_features[i]) != 0) {
511 bool framework_is_populate_excluded_module(const char *module) {
514 for(int i = 0; i < framework_config.populate_excluded_modules_count; i++) {
515 if(strstr(module, framework_config.populate_excluded_modules[i]) != 0) {
523 int framework_populate_get_instance_count(const char *path) {
526 for(int i = 0; i < framework_config.custom_list_instances_count; i++) {
527 if(strcmp(path, framework_config.custom_list_instances[i].path) == 0) {
528 return framework_config.custom_list_instances[i].count;
531 return framework_config.default_list_instances;
534 char *framework_populate_get_restrict_schema(const char *path) {
538 for(int i = 0; i < framework_config.restrict_schema_count; i++) {
539 if(strcmp(path, framework_config.restrict_schema[i].path) == 0) {
540 ret = strdup(framework_config.restrict_schema[i].values[framework_config.restrict_schema[i].index]);
541 framework_config.restrict_schema[i].index++;
542 if(framework_config.restrict_schema[i].index >= framework_config.restrict_schema[i].values_count) {
543 framework_config.restrict_schema[i].index = 0;
552 static void framework_signal_handler(int signo) {
553 framework_sigint = 1;
556 static error_t parse_opt(int key, char *arg, struct argp_state *state) {
557 framework_arguments_t *iter_arguments = state->input;
560 iter_arguments->container_init = true;
564 iter_arguments->nc_server_init = true;
568 iter_arguments->manager = true;
572 iter_arguments->network_function = true;
577 iter_arguments->loop = true;
581 iter_arguments->no_rand = true;
582 framework_arguments.fixed_seed = 0;
585 framework_arguments.fixed_seed *= 10;
586 framework_arguments.fixed_seed += arg[i] - '0';
592 iter_arguments->operational_only = true;
596 iter_arguments->test_mode = true;
600 iter_arguments->verbosity_level = arg[0] - '0';
608 iter_arguments->exhaustive_test = true;
612 iter_arguments->print_root_paths = true;
616 iter_arguments->print_structure_xpath = (char *)malloc(sizeof(char) * (strlen(arg) + 1));
617 if(!iter_arguments->print_structure_xpath) {
618 log_error("very bad malloc failure here");
621 strcpy(iter_arguments->print_structure_xpath, arg);
622 if(arg[strlen(arg) - 1] == '/') {
623 iter_arguments->print_structure_xpath[strlen(arg) - 1] = 0;
628 iter_arguments->populate_all = true;
632 iter_arguments->enable_features = true;
636 if (state->arg_num >= 2) {
646 return ARGP_ERR_UNKNOWN;