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