1 /*------------------------------------------------------------------------------
2  *
3  * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4  * The YADIFA TM software product is provided under the BSD 3-clause license:
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *        * Redistributions of source code must retain the above copyright
11  *          notice, this list of conditions and the following disclaimer.
12  *        * Redistributions in binary form must reproduce the above copyright
13  *          notice, this list of conditions and the following disclaimer in the
14  *          documentation and/or other materials provided with the distribution.
15  *        * Neither the name of EURid nor the names of its contributors may be
16  *          used to endorse or promote products derived from this software
17  *          without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31  *------------------------------------------------------------------------------
32  *
33  */
34 
35 /** @defgroup yadifa
36  *  @ingroup ###
37  *  @brief
38  */
39 
40 #include <strings.h>
41 
42 #include "client-config.h"
43 
44 #include "module.h"
45 #include "common.h"
46 #include "ya-conf.h"
47 #include "main.h"
48 
49 #if HAS_CTRL
50 #include "module/ctrl.h"
51 #endif
52 
53 #if HAS_KEYGEN
54 #include "module/keygen.h"
55 #endif
56 
57 #if HAS_ZONESIGN
58 #include "module/zonesign.h"
59 #endif
60 
61 #if HAS_YADIG
62 #include "module/yadig.h"
63 #endif
64 
65 #if HAS_CRAFT
66 #pragma message("THIS MODULE CANNOT BE PUBLISHED, EVER")
67 #include "module/craft.h"
68 #endif
69 
70 #include <dnscore/logger_handle.h>
71 #include <dnscore/config-cmdline.h>
72 #include <dnscore/config_settings.h>
73 
74 logger_handle *g_yadifa_logger = LOGGER_HANDLE_SINK;
75 
76 extern logger_handle *g_generate_logger;
77 
78 static int verbosity_level = 0;
79 
module_verbosity_level()80 int module_verbosity_level()
81 {
82     return verbosity_level;
83 }
84 
85 
86 // each module has a structure --> virtual table
87 // only those that are compiled in the program are taken in
88 static const module_s *module_list[] =
89 {
90 #if HAS_CTRL
91     &ctrl_program,
92 #endif
93 
94 #if HAS_KEYGEN
95     &keygen_program,
96 #endif
97 
98 #if HAS_ZONESIGN
99     &zonesign_program,
100 #endif
101 
102 #if HAS_YADIG
103     &yadig_program,
104 #endif
105 
106 #if HAS_CRAFT
107     &craft_program,
108 #endif
109     NULL
110 };
111 
112 void
module_print_help(const module_s * module,const char * program_name,int is_executable)113 module_print_help(const module_s *module, const char* program_name, int is_executable)
114 {
115     // formatln("is_executable=%i", is_executable);
116 
117     if(is_executable == 0) // if it's not a a module, give the program help (covers 'unspecified')
118     {
119         osformatln(termout, "Usage:\n\n%s %s [command] [options []]\n", program_name, module->parametername);
120     }
121     else
122     {
123         osformatln(termout, "Usage:\n\n%s command [options []]\n", program_name);
124     }
125     module->help_print(module, termout);
126 }
127 
128 static void
module_program_print_help(const char * program_name,int help_count,int version_count)129 module_program_print_help(const char *program_name, int help_count, int version_count)
130 {
131     yadifa_show_version(version_count); // level 0 prints nothing
132 
133     if(help_count > 0 || version_count == 0)
134     {
135         // give help
136 
137         formatln("%s command [parameters]\n", program_name);
138         println("\tCommands:");
139 
140         for(int i = 0; module_list[i] != NULL; ++i) // VS false positive: the last item of the array is guaranteed to be NULL
141         {
142             formatln("\t\t%12s : %s", module_list[i]->parametername, module_list[i]->name);
143         }
144 
145         formatln("\nTry '%s help command' for more information about a command.\n", program_name);
146     }
147 }
148 
149 const module_s *
module_get_from_args(int * argcp,char ** argv,int * is_executable_ptr)150 module_get_from_args(int *argcp, char **argv, int *is_executable_ptr)
151 {
152     // const char *executable_name = filename_from_path(argv[0]); // note: not used
153 
154     *is_executable_ptr = -1;
155 
156     // no <commandname> found, check for <parametername>
157 
158     int argc = *argcp;
159 
160     // if <parametername> is used there must be at least 3 parameters.
161     // if not we print an help page
162 
163     if(argc >= 2) // if there is at least one parameter after the name
164     {
165         // compares the parameter name with each module "parametername"
166 
167         for(int i = 0; module_list[i] != NULL; ++i) // VS false positive: the last item of the array is guaranted to be NULL
168         {
169             if(strcmp(argv[1], module_list[i]->parametername) == 0)
170             {
171                 // module has been matched
172 
173                 *is_executable_ptr = 0;
174 
175                 // patch the command parameters (shifts out the first parameter)
176 
177                 for(int i = 2; i < argc; ++i)
178                 {
179                     argv[i - 1] = argv[i];
180                 }
181 
182                 --argc;
183                 *argcp = argc;
184 
185                 return module_list[i];
186             }
187         }
188 
189         // no module has been matched, look if help was requested one way or another
190 
191         if(strcasecmp(argv[1], "help") == 0)
192         {
193             // no match.
194             // is it "help" ?
195 
196             if(argc >= 3) // if there is a parameter after help ..;
197             {
198                 // putting in place the whole settings just to answer help or version would be pointless
199 
200                 // find the module whose help was requested and print it
201 
202                 for(int i = 0; module_list[i] != NULL; ++i)
203                 {
204                     if(strcmp(argv[2], module_list[i]->parametername) == 0)
205                     {
206                         *is_executable_ptr = 0;
207 
208                         module_print_help(module_list[i], argv[0], *is_executable_ptr);
209                         return NULL;
210                     }
211                 }
212 
213                 formatln("'%s' isn't a command.\n", argv[2]);
214 
215                 module_program_print_help(argv[0], 1, 0);
216 
217                 return NULL;
218             }
219             else
220             {
221                 println("help command requires a parameter.\n");
222 
223                 module_program_print_help(argv[0], 1, 0);
224                 return NULL;
225             }
226         }
227         else if((strcasecmp(argv[1], "--help") == 0) || (strcasecmp(argv[1], "-h") == 0))
228         {
229             // help was asked in a standard way: print the general help listing the modules
230             // used as flag to know, that even if its not a module, the exit can be clean, 'help' of the module
231             // has been asked
232             *is_executable_ptr = 0;
233         }
234         else
235         {
236             // whatever was asked to yadifa is unknown: complain about it then print the general help listing the modules
237         }
238     }
239     else
240     {
241         // nothing was asked to yadifa: print the general help listing the modules
242     }
243 
244     int help_count = 0;
245     int version_count = 0;
246     int verbose_level = 0;
247 
248     for(int i = 1; i < argc; ++i)
249     {
250         if((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0) )
251         {
252             ++help_count;
253         }
254         else if((strcmp(argv[1], "--version") == 0) || (strcmp(argv[1], "-V") == 0))
255         {
256             ++version_count;
257         }
258         else if((strcmp(argv[1], "--verbose") == 0) || (strcmp(argv[1], "-v") == 0))
259         {
260             ++verbose_level;
261         }
262         else
263         {
264             flushout();
265             osformatln(termerr, "%s: invalid option: %s", argv[0], argv[i]);
266             flusherr();
267         }
268     }
269 
270     verbosity_level = verbose_level;
271 
272     module_program_print_help(argv[0], help_count, version_count);
273 
274     return NULL;
275 }
276 
277 ya_result
module_run_from_args(int * argcp,char * argv[])278 module_run_from_args(int *argcp, char *argv[])
279 {
280     ya_result ret = ERROR;
281 
282     int argc = *argcp;
283     int is_executable = -2;
284 
285     const module_s *module = module_get_from_args(&argc, argv, &is_executable);
286 
287     module_arg_set(argv, argc);
288 
289     *argcp = argc;
290 
291     // at this point, the program to execute is known
292 
293     if(module != NULL)
294     {
295         if(FAIL(ret = module->init()))
296         {
297             formatln("module %s initialisation failed: %r", module->name, ret);
298             return ret;
299         }
300 
301         // if the logger is running (not a guarantee), register the system logger
302 
303         if(logger_is_running())
304         {
305             logger_handle_create("yadifa", &g_yadifa_logger);
306         }
307 
308         // @TODO 20180611 gve -- this must be uncomment
309         if(ISOK(ret = ya_conf_init()))
310         {
311             int priority = ret;
312 
313             if(ISOK(ret = module->config_register(priority)))
314             {
315                 ret = ya_conf_read(module->cmdline_table, argc, argv, module->filter, module->filter_arg, module->rcname);
316 
317                 ya_conf_finalize();
318 
319                 if(cmdline_help_get() + cmdline_version_get() > 0)
320                 {
321                     module_print_help(module, argv[0], is_executable);
322                 }
323                 else
324                 {
325                     if(ret  == 0)
326                     {
327                         if(ISOK(ret = module->setup()))
328                         {
329                             ret = module->run();
330                         }
331                         else // something is wrong with the setup
332                         {
333                             module_print_help(module, argv[0], is_executable);
334                         }
335 
336                         // THERE IS NO GUARANTEE TO REACH THIS LINE (TCL)
337                     }
338 
339                     if(FAIL(ret))
340                     {
341                         if((ret == CONFIG_PARSE_UNKNOWN_KEYWORD) || (ret == COMMAND_ARGUMENT_EXPECTED) || (ret == YADIFA_MODULE_HELP_REQUESTED))
342                         {
343                             module_print_help(module, argv[0], is_executable);
344                         }
345                         else
346                         {
347                             /// @todo 20220511 edf -- print the error if it's not YADIFA_MODULE_HELP_REQUESTED ?
348                             flushout();
349                             flusherr();
350                             osformatln(termerr, "error: %r", ret);
351                         }
352                     }
353                 }
354             }
355             else
356             {
357                 formatln("module %s configuration registration failed: %r", module->name, ret);
358             }
359         }
360         else
361         {
362             formatln("module %s configuration initialisation failed: %r", module->name, ret);
363         }
364     }
365     else
366     {
367         // no module matched but maybe 'help' was asked instead
368         if (is_executable == 0)
369         {
370             // 'help' was asked instead
371             ret = 0;
372         }
373     }
374 
375     *argcp = argc;
376 
377     return ret;
378 }
379 
380 /*----------------------------------------------------------------------------*/
381 #pragma mark MODULES DEFAULT FUNCTIONS
382 
383 // ********************************************************************************
384 // ***** module initializer
385 // ********************************************************************************
386 
387 ya_result
module_default_init(const struct module_s * m)388 module_default_init(const struct module_s* m)
389 {
390     (void)m;
391     return SUCCESS;
392 }
393 
394 // ********************************************************************************
395 // ***** module finalizer
396 // ********************************************************************************
397 
398 ya_result
module_default_finalize()399 module_default_finalize()
400 {
401     return SUCCESS;
402 }
403 
404 // ********************************************************************************
405 // ***** module register
406 // ********************************************************************************
407 
408 int
module_default_config_register(int argc,char ** argv)409 module_default_config_register(int argc, char **argv)
410 {
411     (void)argc;
412     (void)argv;
413     return 0;
414 }
415 
416 
417 
418 // ********************************************************************************
419 // ***** module setup
420 // ********************************************************************************
421 
422 int
module_default_setup()423 module_default_setup()
424 {
425     return SUCCESS; // returns anything else than 0 => program will exit
426 }
427 
428 // ********************************************************************************
429 // ***** module run
430 // ********************************************************************************
431 
432 ya_result
module_default_run()433 module_default_run()
434 {
435     return SUCCESS;
436 }
437 
438 ya_result
module_default_help_print(const struct module_s * m,output_stream * os)439 module_default_help_print(const struct module_s* m, output_stream *os)
440 {
441     if(m->help_text != NULL)
442     {
443         osformatln(os, m->help_text, m->name);
444     }
445     else
446     {
447         osformatln(os, "help callback for module '%s' is not set properly", m->name);
448     }
449     return SUCCESS;
450 }
451 
452 ya_result
module_default_cmdline_help_print(const struct module_s * m,output_stream * os)453 module_default_cmdline_help_print(const struct module_s* m, output_stream *os)
454 {
455     cmdline_print_help(m->cmdline_table, 16, 28, " :  ", 48, os);
456     return SUCCESS;
457 }
458