1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <kapp/extern.h>
28 #include "main-priv.h"
29 #include <sysalloc.h>
30 #include <kapp/main.h>
31 #include <kfg/config.h>
32 #include <kproc/procmgr.h>
33 #include <klib/report.h>
34 #include <klib/writer.h>
35 #include <klib/log.h>
36 #include <klib/text.h>
37 #include <klib/status.h>
38 #include <klib/rc.h>
39 #include <kns/manager.h>
40 
41 #if ! NO_KRSRC
42 #include <kfc/except.h>
43 #include <kfc/rsrc.h>
44 #include <kfc/rsrc-global.h>
45 #include <kfc/ctx.h>
46 #endif
47 
48 #include <strtol.h>
49 
50 #include <stdlib.h>
51 #include <assert.h>
52 #include <string.h>
53 
54 #if 0
55 /* NextArg
56  *  extracts the next argument from a switch parameter
57  *
58  *  "arg" is an indirect pointer to somewhere within an
59  *  argument string, representing the last character parsed,
60  *  e.g. "-auv"
61  *         ^    : "arg" indicates the 'a'.
62  *  in the example given, NextArg will advance the pointer
63  *  to point to the 'u'.
64  *
65  *  had "arg" pointed instead to the 'v' above, i.e. there
66  *  are no characters remaining in "arg" to be parsed, then
67  *  an attempt to return the next element of "argv" will be
68  *  made by invoking NextArgh.
69  *
70  *  "arg" [ IN, OUT ] - current argument string as indicated above
71  *
72  *  "i" [ IN, OUT ] - loop counter. required to be < argc,
73  *  advanced if necessary to access next argument in argv.
74  *
75  *  "argc" [ IN ] - number of arguments in argv
76  *
77  *  "argv" [ IN ] - program arguments
78  *
79  *  "handle_null" [ IN, NULL OKAY ] and "data" [ IN, OPAQUE ] -
80  *  optional callback function to handle NULL arguments. default
81  *  behavior is to log error using "logerr" and invoke "exit".
82  */
83 const char * CC NextArg ( const char **argp, int *i, int argc, char *argv [],
84     const char * ( CC * handle_null ) ( void *data ), void *data )
85 {
86     if ( argp != NULL )
87     {
88         const char *arg = * argp;
89         if ( arg != NULL && arg [ 0 ] != 0 && arg [ 1 ] != 0 )
90         {
91             * argp = "-";
92             return arg + 1;
93         }
94         return NextArgh ( i, argc, argv, handle_null, data );
95     }
96 
97     /* report a fatal error if no arg given */
98     if ( handle_null == NULL )
99     {
100         rc_t rc = RC ( rcApp, rcArgv, rcAccessing, rcParam, rcNull );
101         LOGERR ( klogFatal, ( klogFatal, rc, "internal error" ));
102         exit ( 5 );
103     }
104 
105     return ( * handle_null ) ( data );
106 }
107 #endif
108 
KAppCheckEnvironment(bool require64Bits,uint64_t requireRamSize)109 rc_t CC KAppCheckEnvironment ( bool require64Bits, uint64_t requireRamSize )
110 {
111     rc_t rc;
112     uint64_t totalRam;
113 #if _ARCH_BITS != 64
114     if ( require64Bits )
115     {
116         rc = RC ( rcApp, rcNoTarg, rcInitializing, rcResources, rcUnsupported );
117         LOGERR ( klogFatal, rc, "can only be run as 64-bit application" );
118         return rc;
119     }
120 #endif
121 
122     rc = KAppGetTotalRam ( & totalRam );
123     if ( rc != 0 )
124     {
125         return rc;
126     }
127 
128     if ( requireRamSize && totalRam < requireRamSize )
129     {
130         rc = RC ( rcApp, rcNoTarg, rcInitializing, rcResources, rcUnsupported );
131         PLOGERR ( klogFatal, ( klogFatal, rc,  "there is not enough RAM in the system."
132                                            " required size: $(REQUIRED) B, present: $(PRESENT) B"
133                               , "REQUIRED=%lu,PRESENT=%lu"
134                               , requireRamSize
135                               , totalRam ) );
136         return rc;
137     }
138 
139     return 0;
140 }
141 
142 /* AsciiToXXX
143  *  replacement for atoi
144  *  converts NUL terminated string in "arg" to integer
145  *  invokes error handler if there is a format error in string
146  *
147  *  "arg" [ IN ] - NUL terminated textual representation of integer
148  *  obeys standard conversion rules:
149  *    starts with "0x" or "0X" - interpret as hex
150  *    starts with '0' - interpret as octal
151  *    otherwise - interpret as decimal
152  *
153  *  "handler_error" [ IN, NULL OKAY ] and "data" [ IN, OPAQUE ] -
154  *  optional callback function to handle case where "arg" could not
155  *  be processed in its entirety. default behavior is to log error
156  *  using "logerr" and invoke "exit".
157  */
158 static
HandleAsciiToIntError(const char * arg,void * ignore)159 void CC HandleAsciiToIntError ( const char *arg, void *ignore )
160 {
161     rc_t rc;
162 
163     if ( arg == NULL )
164         rc = RC ( rcApp, rcNumeral, rcConverting, rcString, rcNull );
165     else if ( arg [ 0 ] == 0 )
166         rc = RC ( rcApp, rcNumeral, rcConverting, rcString, rcEmpty );
167     else
168         rc = RC ( rcApp, rcNumeral, rcConverting, rcString, rcInvalid );
169 
170     LOGERR ( klogFatal, rc, "expected numeral" );
171     exit ( 10 );
172 }
173 
AsciiToI32(const char * arg,void (CC * handle_error)(const char * arg,void * data),void * data)174 int32_t CC AsciiToI32 ( const char *arg,
175     void ( CC * handle_error ) ( const char *arg, void *data ), void *data )
176 {
177     if ( handle_error == NULL )
178         handle_error = HandleAsciiToIntError;
179 
180     if ( arg != NULL && arg [ 0 ] != 0 )
181     {
182         char *end;
183         long int i = strtol ( arg, & end, 0 );
184         if ( end [ 0 ] == 0 )
185         {
186             if ( sizeof i == sizeof ( int32_t ) )
187                 return ( int32_t ) i;
188 
189             if ( ( ( i < 0 ) && ( ( ( uint64_t ) - i ) >> 32 ) == 0 ) ||
190                  ( ( i > 0 ) && ( ( ( uint64_t ) i ) >> 32 ) == 0 ) )
191                 return ( int32_t ) i;
192         }
193     }
194 
195     ( * handle_error ) ( arg, data );
196     return 0;
197 }
198 
AsciiToU32(const char * arg,void (CC * handle_error)(const char * arg,void * data),void * data)199 uint32_t CC AsciiToU32 ( const char *arg,
200     void ( CC * handle_error ) ( const char *arg, void *data ), void *data )
201 {
202     if ( handle_error == NULL )
203         handle_error = HandleAsciiToIntError;
204 
205     if ( arg != NULL && arg [ 0 ] != 0 )
206     {
207         char *end;
208         unsigned long int i = strtoul ( arg, & end, 0 );
209         if ( end [ 0 ] == 0 )
210         {
211             if ( sizeof i == sizeof ( uint32_t ) )
212                 return ( uint32_t ) i;
213 
214             if ( ( ( ( uint64_t ) i ) >> 32 ) == 0 )
215                 return ( uint32_t ) i;
216         }
217     }
218 
219     ( * handle_error ) ( arg, data );
220     return 0;
221 }
222 
AsciiToI64(const char * arg,void (CC * handle_error)(const char * arg,void * data),void * data)223 int64_t CC AsciiToI64 ( const char *arg,
224     void ( CC * handle_error ) ( const char *arg, void *data ), void *data )
225 {
226     if ( handle_error == NULL )
227         handle_error = HandleAsciiToIntError;
228 
229     if ( arg != NULL && arg [ 0 ] != 0 )
230     {
231         char *end;
232         int64_t i = strtoi64 ( arg, & end, 0 );
233 
234         if ( end [ 0 ] == 0 )
235             return i;
236     }
237 
238     ( * handle_error ) ( arg, data );
239     return 0;
240 }
241 
AsciiToU64(const char * arg,void (CC * handle_error)(const char * arg,void * data),void * data)242 uint64_t CC AsciiToU64 ( const char *arg,
243     void ( CC * handle_error ) ( const char *arg, void *data ), void *data )
244 {
245     if ( handle_error == NULL )
246         handle_error = HandleAsciiToIntError;
247 
248     if ( arg != NULL && arg [ 0 ] != 0 )
249     {
250         char *end;
251         uint64_t i = strtou64 ( arg, & end, 0 );
252 
253         if ( end [ 0 ] == 0 )
254             return i;
255     }
256 
257     ( * handle_error ) ( arg, data );
258     return 0;
259 }
260 
261 /* KLogLevelParamStrings
262  *  Used to compare against command line parameters
263  *  These must match KLogLEvel enum in log.h
264  */
265 static
logLevelFromString(const char * str,void * data)266 void CC logLevelFromString ( const char * str, void *data )
267 {
268     KLogLevel ix;
269     const char ** paramStrings;
270 
271     paramStrings = KLogGetParamStrings();
272 
273     for ( ix = klogLevelMin; ix <= klogLevelMax; ++ix )
274     {
275         if ( strcmp ( str, paramStrings [ ( int ) ix ] ) == 0 )
276         {
277             * ( int32_t* ) data = ix;
278             return;
279         }
280     }
281 
282     /* this RC should reflect an invalid string parameter to set the log level */
283     PLOGERR ( klogFatal, ( klogFatal, RC ( rcApp, rcArgv, rcParsing, rcRange, rcInvalid ),
284                            "log level '$(lvl)' is unrecognized", "lvl=%s", str ));
285     exit ( 10 );
286 }
287 
288 static
LogLevelAbsolute(const char * string)289 rc_t LogLevelAbsolute ( const char * string )
290 {
291     int32_t 	new_level;
292     int32_t	absolute_level = -1;	/* if this remains -1 then we didn't have a good symbolic string */
293 
294     /* parse as if integer value but fail over to symbolic strings */
295     new_level = AsciiToU32 ( string, logLevelFromString, & absolute_level );
296 
297     return KLogLevelSet( ( KLogLevel ) ( ( absolute_level == -1 ) ? new_level : absolute_level ) );
298 }
299 
300 static
LogLevelRelative(const char * string)301 rc_t LogLevelRelative ( const char * string )
302 {
303     int32_t adjust = 0;
304     int i;
305 
306     for ( i = 0; string [ i ] != 0; ++ i )
307     {
308         switch ( string [ i ] )
309         {
310         case '+':
311             ++ adjust;
312             break;
313 
314         case '-':
315             -- adjust;
316             break;
317 
318         default:
319             return RC ( rcApp, rcArgv, rcParsing, rcToken, rcUnrecognized );
320         }
321     }
322     KLogLevelAdjust(adjust);
323     return 0;
324 }
325 
NextLogLevelCommon(const char * level_parameter)326 rc_t CC NextLogLevelCommon ( const char * level_parameter )
327 {
328     if ( level_parameter == NULL )
329         return RC ( rcApp, rcArgv, rcParsing, rcString, rcNull );
330 
331     if ( ( level_parameter [ 0 ] == '+' ) || ( level_parameter [ 0 ] == '-' ) )
332         return LogLevelRelative ( level_parameter );
333 
334     return LogLevelAbsolute ( level_parameter );
335 }
336 
337 #if 0
338 static
339 void CC HandleLogLevelError ( rc_t rc )
340 {
341     LOGERR ( klogFatal, rc, "expected log level" );
342     exit ( 10 );
343 }
344 
345 /* NextLogLevel
346  *  handle a numeric or textual argument to --log-level <level>  The --log-level is not
347  *  specified here and could be any string of the programmers choice
348  */
349 void CC NextLogLevel ( const char ** argp,
350 		    int *ip,
351 		    int argc,
352 		    char *argv [],
353 		    const char* ( CC * handle_null ) ( void *data ), void *data )
354 {
355     rc_t rc = NextLogLevelCommon ( NextArg ( argp, ip, argc, argv, handle_null, data ) );
356     if ( rc != 0 )
357     {
358         if ( handle_null != NULL )
359             ( * handle_null ) ( data );
360         else
361             HandleLogLevelError ( rc );
362     }
363 }
364 
365 /* NextLogLevelh
366  *  handle a numeric or textual argument to --log-level <level>  The --log-level is not
367  *  specified here and could be any string of the programmers choice
368  */
369 void CC NextLogLevelh (int *ip,
370 		    int argc,
371 		    char *argv [],
372 		    const char* ( CC * handle_null ) ( void *data ), void *data )
373 {
374     rc_t rc = NextLogLevelCommon ( NextArgh ( ip, argc, argv, handle_null, data ) );
375     if ( rc != 0 )
376     {
377         if ( handle_null != NULL )
378             ( * handle_null ) ( data );
379         else
380             HandleLogLevelError ( rc );
381     }
382 }
383 #endif
384 
385 /* KMane
386  *  executable entrypoint "main" is implemented by
387  *  an OS-specific wrapper that takes care of establishing
388  *  signal handlers, logging, etc.
389  *
390  *  in turn, OS-specific "main" will invoke "KMain" as
391  *  platform independent main entrypoint.
392  *
393  *  "argc" [ IN ] - the number of textual parameters in "argv"
394  *  should never be < 0, but has been left as a signed int
395  *  for reasons of tradition.
396  *
397  *  "argv" [ IN ] - array of NUL terminated strings expected
398  *  to be in the shell-native character set: ASCII or UTF-8
399  *  element 0 is expected to be executable identity or path.
400  */
401 #if NO_KRSRC
402 static
atexit_task(void)403 void CC atexit_task ( void )
404 {
405     KProcMgrWhack ();
406 }
407 #endif
408 
KMane(int argc,char * argv[])409 rc_t KMane ( int argc, char *argv [] )
410 {
411     rc_t rc = 0;
412     KNSManager * kns = NULL;
413 #if NO_KRSRC
414     int status;
415 #else
416     KCtx local_ctx, * ctx = & local_ctx;
417     DECLARE_FUNC_LOC ( rcExe, rcProcess, rcExecuting );
418 #endif
419 
420     /* get application version */
421     ver_t vers = KAppVersion ();
422 
423     /* initialize error reporting */
424     ReportInit ( argc, argv, vers );
425 
426 #if NO_KRSRC
427     /* initialize cleanup tasks */
428     status = atexit ( atexit_task );
429     if ( status != 0 )
430         return SILENT_RC ( rcApp, rcNoTarg, rcInitializing, rcFunction, rcNotAvailable );
431 
432     /* initialize proc mgr */
433     rc = KProcMgrInit ();
434     if ( rc != 0 )
435         return rc;
436 
437     kns = NULL;
438 #else
439     ON_FAIL ( KRsrcGlobalInit ( & local_ctx, & s_func_loc, false ) )
440     {
441         assert ( ctx -> rc != 0 );
442         return ctx -> rc;
443     }
444 
445     kns = ctx -> rsrc -> kns;
446 #endif
447 
448     /* initialize the default User-Agent in the kns-manager to default value - using "vers" and argv[0] above strrchr '/' */
449     {
450         const char * tool = argv[ 0 ];
451         size_t tool_size = string_size ( tool );
452 
453         const char * sep = string_rchr ( tool, tool_size, '/' );
454         if ( sep ++ == NULL )
455             sep = tool;
456         else
457             tool_size -= sep - tool;
458 
459         sep = string_chr ( tool = sep, tool_size, '.' );
460         if ( sep != NULL )
461             tool_size = sep - tool;
462 
463         KNSManagerSetUserAgent ( kns, PKGNAMESTR " sra-toolkit %.*s.%V", ( uint32_t ) tool_size, tool, vers );
464     }
465 
466     KNSManagerSetQuitting ( kns, Quitting );
467 
468     /* initialize logging */
469     rc = KWrtInit(argv[0], vers);
470     if ( rc == 0 )
471         rc = KLogLibHandlerSetStdErr ();
472     if ( rc == 0 )
473         rc = KStsLibHandlerSetStdOut ();
474 
475     if ( rc == 0 )
476     {
477 #if KFG_COMMON_CREATION
478         KConfig *kfg;
479         rc = KConfigMake ( & kfg );
480         if ( rc == 0 )
481         {
482 #endif
483             rc = KMain ( argc, argv );
484             if ( rc != 0 )
485             {
486 
487 #if _DEBUGGING
488                 rc_t rc2;
489                 uint32_t lineno;
490                 const char *filename, *function;
491                 while ( GetUnreadRCInfo ( & rc2, & filename, & function, & lineno ) )
492                 {
493                     pLogErr ( klogWarn, rc2, "$(filename):$(lineno) within $(function)"
494                               , "filename=%s,lineno=%u,function=%s"
495                               , filename
496                               , lineno
497                               , function
498                         );
499                 }
500 #endif
501 
502             }
503             {
504                 rc_t r2 = KNSManagerRelease ( kns );
505                 if ( rc == 0 && r2 != 0 )
506                     rc = r2;
507                 kns = NULL;
508             }
509 #if KFG_COMMON_CREATION
510             KConfigRelease ( kfg );
511         }
512 #endif
513     }
514 
515     /* finalize error reporting */
516     ReportSilence ();
517     ReportFinalize ( rc );
518 
519 #if ! NO_KRSRC
520     KRsrcGlobalWhack ( ctx );
521 #endif
522 
523     return rc;
524 }
525