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