comparison nss/lib/pk11wrap/pk11load.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e5118fa0cb1
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 /*
5 * The following handles the loading, unloading and management of
6 * various PCKS #11 modules
7 */
8 #define FORCE_PR_LOG 1
9 #include "seccomon.h"
10 #include "pkcs11.h"
11 #include "secmod.h"
12 #include "prlink.h"
13 #include "pk11func.h"
14 #include "secmodi.h"
15 #include "secmodti.h"
16 #include "nssilock.h"
17 #include "secerr.h"
18 #include "prenv.h"
19 #include "utilparst.h"
20
21 #define DEBUG_MODULE 1
22
23 #ifdef DEBUG_MODULE
24 static char *modToDBG = NULL;
25
26 #include "debug_module.c"
27 #endif
28
29 /* build the PKCS #11 2.01 lock files */
30 CK_RV PR_CALLBACK secmodCreateMutext(CK_VOID_PTR_PTR pmutex) {
31 *pmutex = (CK_VOID_PTR) PZ_NewLock(nssILockOther);
32 if ( *pmutex ) return CKR_OK;
33 return CKR_HOST_MEMORY;
34 }
35
36 CK_RV PR_CALLBACK secmodDestroyMutext(CK_VOID_PTR mutext) {
37 PZ_DestroyLock((PZLock *)mutext);
38 return CKR_OK;
39 }
40
41 CK_RV PR_CALLBACK secmodLockMutext(CK_VOID_PTR mutext) {
42 PZ_Lock((PZLock *)mutext);
43 return CKR_OK;
44 }
45
46 CK_RV PR_CALLBACK secmodUnlockMutext(CK_VOID_PTR mutext) {
47 PZ_Unlock((PZLock *)mutext);
48 return CKR_OK;
49 }
50
51 static SECMODModuleID nextModuleID = 1;
52 static const CK_C_INITIALIZE_ARGS secmodLockFunctions = {
53 secmodCreateMutext, secmodDestroyMutext, secmodLockMutext,
54 secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS|
55 CKF_OS_LOCKING_OK
56 ,NULL
57 };
58 static const CK_C_INITIALIZE_ARGS secmodNoLockArgs = {
59 NULL, NULL, NULL, NULL,
60 CKF_LIBRARY_CANT_CREATE_OS_THREADS
61 ,NULL
62 };
63
64 static PRBool loadSingleThreadedModules = PR_TRUE;
65 static PRBool enforceAlreadyInitializedError = PR_TRUE;
66 static PRBool finalizeModules = PR_TRUE;
67
68 /* set global options for NSS PKCS#11 module loader */
69 SECStatus pk11_setGlobalOptions(PRBool noSingleThreadedModules,
70 PRBool allowAlreadyInitializedModules,
71 PRBool dontFinalizeModules)
72 {
73 if (noSingleThreadedModules) {
74 loadSingleThreadedModules = PR_FALSE;
75 } else {
76 loadSingleThreadedModules = PR_TRUE;
77 }
78 if (allowAlreadyInitializedModules) {
79 enforceAlreadyInitializedError = PR_FALSE;
80 } else {
81 enforceAlreadyInitializedError = PR_TRUE;
82 }
83 if (dontFinalizeModules) {
84 finalizeModules = PR_FALSE;
85 } else {
86 finalizeModules = PR_TRUE;
87 }
88 return SECSuccess;
89 }
90
91 PRBool pk11_getFinalizeModulesOption(void)
92 {
93 return finalizeModules;
94 }
95
96 /*
97 * Allow specification loading the same module more than once at init time.
98 * This enables 2 things.
99 *
100 * 1) we can load additional databases by manipulating secmod.db/pkcs11.txt.
101 * 2) we can handle the case where some library has already initialized NSS
102 * before the main application.
103 *
104 * oldModule is the module we have already initialized.
105 * char *modulespec is the full module spec for the library we want to
106 * initialize.
107 */
108 static SECStatus
109 secmod_handleReload(SECMODModule *oldModule, SECMODModule *newModule)
110 {
111 PK11SlotInfo *slot;
112 char *modulespec;
113 char *newModuleSpec;
114 char **children;
115 CK_SLOT_ID *ids;
116 SECMODConfigList *conflist = NULL;
117 SECStatus rv = SECFailure;
118 int count = 0;
119
120 /* first look for tokens= key words from the module spec */
121 modulespec = newModule->libraryParams;
122 newModuleSpec = secmod_ParseModuleSpecForTokens(PR_TRUE,
123 newModule->isFIPS, modulespec, &children, &ids);
124 if (!newModuleSpec) {
125 return SECFailure;
126 }
127
128 /*
129 * We are now trying to open a new slot on an already loaded module.
130 * If that slot represents a cert/key database, we don't want to open
131 * multiple copies of that same database. Unfortunately we understand
132 * the softoken flags well enough to be able to do this, so we can only get
133 * the list of already loaded databases if we are trying to open another
134 * internal module.
135 */
136 if (oldModule->internal) {
137 conflist = secmod_GetConfigList(oldModule->isFIPS,
138 oldModule->libraryParams, &count);
139 }
140
141
142 /* don't open multiple of the same db */
143 if (conflist && secmod_MatchConfigList(newModuleSpec, conflist, count)) {
144 rv = SECSuccess;
145 goto loser;
146 }
147 slot = SECMOD_OpenNewSlot(oldModule, newModuleSpec);
148 if (slot) {
149 int newID;
150 char **thisChild;
151 CK_SLOT_ID *thisID;
152 char *oldModuleSpec;
153
154 if (secmod_IsInternalKeySlot(newModule)) {
155 pk11_SetInternalKeySlotIfFirst(slot);
156 }
157 newID = slot->slotID;
158 PK11_FreeSlot(slot);
159 for (thisChild=children, thisID=ids; thisChild && *thisChild;
160 thisChild++,thisID++) {
161 if (conflist &&
162 secmod_MatchConfigList(*thisChild, conflist, count)) {
163 *thisID = (CK_SLOT_ID) -1;
164 continue;
165 }
166 slot = SECMOD_OpenNewSlot(oldModule, *thisChild);
167 if (slot) {
168 *thisID = slot->slotID;
169 PK11_FreeSlot(slot);
170 } else {
171 *thisID = (CK_SLOT_ID) -1;
172 }
173 }
174
175 /* update the old module initialization string in case we need to
176 * shutdown and reinit the whole mess (this is rare, but can happen
177 * when trying to stop smart card insertion/removal threads)... */
178 oldModuleSpec = secmod_MkAppendTokensList(oldModule->arena,
179 oldModule->libraryParams, newModuleSpec, newID,
180 children, ids);
181 if (oldModuleSpec) {
182 oldModule->libraryParams = oldModuleSpec;
183 }
184
185 rv = SECSuccess;
186 }
187
188 loser:
189 secmod_FreeChildren(children, ids);
190 PORT_Free(newModuleSpec);
191 if (conflist) {
192 secmod_FreeConfigList(conflist, count);
193 }
194 return rv;
195 }
196
197 /*
198 * collect the steps we need to initialize a module in a single function
199 */
200 SECStatus
201 secmod_ModuleInit(SECMODModule *mod, SECMODModule **reload,
202 PRBool* alreadyLoaded)
203 {
204 CK_C_INITIALIZE_ARGS moduleArgs;
205 CK_VOID_PTR pInitArgs;
206 CK_RV crv;
207
208 if (reload) {
209 *reload = NULL;
210 }
211
212 if (!mod || !alreadyLoaded) {
213 PORT_SetError(SEC_ERROR_INVALID_ARGS);
214 return SECFailure;
215 }
216
217 if (mod->libraryParams == NULL) {
218 if (mod->isThreadSafe) {
219 pInitArgs = (void *) &secmodLockFunctions;
220 } else {
221 pInitArgs = NULL;
222 }
223 } else {
224 if (mod->isThreadSafe) {
225 moduleArgs = secmodLockFunctions;
226 } else {
227 moduleArgs = secmodNoLockArgs;
228 }
229 moduleArgs.LibraryParameters = (void *) mod->libraryParams;
230 pInitArgs = &moduleArgs;
231 }
232 crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
233 if (CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) {
234 SECMODModule *oldModule = NULL;
235
236 /* Library has already been loaded once, if caller expects it, and it
237 * has additional configuration, try reloading it as well. */
238 if (reload != NULL && mod->libraryParams) {
239 oldModule = secmod_FindModuleByFuncPtr(mod->functionList);
240 }
241 /* Library has been loaded by NSS. It means it may be capable of
242 * reloading */
243 if (oldModule) {
244 SECStatus rv;
245 rv = secmod_handleReload(oldModule, mod);
246 if (rv == SECSuccess) {
247 /* This module should go away soon, since we've
248 * simply expanded the slots on the old module.
249 * When it goes away, it should not Finalize since
250 * that will close our old module as well. Setting
251 * the function list to NULL will prevent that close */
252 mod->functionList = NULL;
253 *reload = oldModule;
254 return SECSuccess;
255 }
256 SECMOD_DestroyModule(oldModule);
257 }
258 /* reload not possible, fall back to old semantics */
259 if (!enforceAlreadyInitializedError) {
260 *alreadyLoaded = PR_TRUE;
261 return SECSuccess;
262 }
263 }
264 if (crv != CKR_OK) {
265 if (!mod->isThreadSafe ||
266 crv == CKR_NETSCAPE_CERTDB_FAILED ||
267 crv == CKR_NETSCAPE_KEYDB_FAILED) {
268 PORT_SetError(PK11_MapError(crv));
269 return SECFailure;
270 }
271 /* If we had attempted to init a single threaded module "with"
272 * parameters and it failed, should we retry "without" parameters?
273 * (currently we don't retry in this scenario) */
274
275 if (!loadSingleThreadedModules) {
276 PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
277 return SECFailure;
278 }
279 /* If we arrive here, the module failed a ThreadSafe init. */
280 mod->isThreadSafe = PR_FALSE;
281 if (!mod->libraryParams) {
282 pInitArgs = NULL;
283 } else {
284 moduleArgs = secmodNoLockArgs;
285 moduleArgs.LibraryParameters = (void *) mod->libraryParams;
286 pInitArgs = &moduleArgs;
287 }
288 crv = PK11_GETTAB(mod)->C_Initialize(pInitArgs);
289 if ((CKR_CRYPTOKI_ALREADY_INITIALIZED == crv) &&
290 (!enforceAlreadyInitializedError)) {
291 *alreadyLoaded = PR_TRUE;
292 return SECSuccess;
293 }
294 if (crv != CKR_OK) {
295 PORT_SetError(PK11_MapError(crv));
296 return SECFailure;
297 }
298 }
299 return SECSuccess;
300 }
301
302 /*
303 * set the hasRootCerts flags in the module so it can be stored back
304 * into the database.
305 */
306 void
307 SECMOD_SetRootCerts(PK11SlotInfo *slot, SECMODModule *mod) {
308 PK11PreSlotInfo *psi = NULL;
309 int i;
310
311 if (slot->hasRootCerts) {
312 for (i=0; i < mod->slotInfoCount; i++) {
313 if (slot->slotID == mod->slotInfo[i].slotID) {
314 psi = &mod->slotInfo[i];
315 break;
316 }
317 }
318 if (psi == NULL) {
319 /* allocate more slots */
320 PK11PreSlotInfo *psi_list = (PK11PreSlotInfo *)
321 PORT_ArenaAlloc(mod->arena,
322 (mod->slotInfoCount+1)* sizeof(PK11PreSlotInfo));
323 /* copy the old ones */
324 if (mod->slotInfoCount > 0) {
325 PORT_Memcpy(psi_list,mod->slotInfo,
326 (mod->slotInfoCount)*sizeof(PK11PreSlotInfo));
327 }
328 /* assign psi to the last new slot */
329 psi = &psi_list[mod->slotInfoCount];
330 psi->slotID = slot->slotID;
331 psi->askpw = 0;
332 psi->timeout = 0;
333 psi ->defaultFlags = 0;
334
335 /* increment module count & store new list */
336 mod->slotInfo = psi_list;
337 mod->slotInfoCount++;
338
339 }
340 psi->hasRootCerts = 1;
341 }
342 }
343
344 #ifdef NSS_STATIC
345 extern CK_RV NSC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList);
346 extern CK_RV FC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList);
347 extern char **NSC_ModuleDBFunc(unsigned long function,char *parameters, void *args);
348 extern CK_RV builtinsC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList);
349 #else
350 static const char* my_shlib_name =
351 SHLIB_PREFIX"nss"SHLIB_VERSION"."SHLIB_SUFFIX;
352 static const char* softoken_shlib_name =
353 SHLIB_PREFIX"softokn"SOFTOKEN_SHLIB_VERSION"."SHLIB_SUFFIX;
354 static const PRCallOnceType pristineCallOnce;
355 static PRCallOnceType loadSoftokenOnce;
356 static PRLibrary* softokenLib;
357 static PRInt32 softokenLoadCount;
358 #endif /* NSS_STATIC */
359
360 #include "prio.h"
361 #include "prprf.h"
362 #include <stdio.h>
363 #include "prsystem.h"
364
365 #ifndef NSS_STATIC
366 /* This function must be run only once. */
367 /* determine if hybrid platform, then actually load the DSO. */
368 static PRStatus
369 softoken_LoadDSO( void )
370 {
371 PRLibrary * handle;
372
373 handle = PORT_LoadLibraryFromOrigin(my_shlib_name,
374 (PRFuncPtr) &softoken_LoadDSO,
375 softoken_shlib_name);
376 if (handle) {
377 softokenLib = handle;
378 return PR_SUCCESS;
379 }
380 return PR_FAILURE;
381 }
382 #endif /* !NSS_STATIC */
383
384 /*
385 * load a new module into our address space and initialize it.
386 */
387 SECStatus
388 secmod_LoadPKCS11Module(SECMODModule *mod, SECMODModule **oldModule) {
389 PRLibrary *library = NULL;
390 CK_C_GetFunctionList entry = NULL;
391 CK_INFO info;
392 CK_ULONG slotCount = 0;
393 SECStatus rv;
394 PRBool alreadyLoaded = PR_FALSE;
395 char *disableUnload = NULL;
396
397 if (mod->loaded) return SECSuccess;
398
399 /* intenal modules get loaded from their internal list */
400 if (mod->internal && (mod->dllName == NULL)) {
401 #ifdef NSS_STATIC
402 if (mod->isFIPS) {
403 entry = FC_GetFunctionList;
404 } else {
405 entry = NSC_GetFunctionList;
406 }
407 if (mod->isModuleDB) {
408 mod->moduleDBFunc = NSC_ModuleDBFunc;
409 }
410 #else
411 /*
412 * Loads softoken as a dynamic library,
413 * even though the rest of NSS assumes this as the "internal" module.
414 */
415 if (!softokenLib &&
416 PR_SUCCESS != PR_CallOnce(&loadSoftokenOnce, &softoken_LoadDSO))
417 return SECFailure;
418
419 PR_ATOMIC_INCREMENT(&softokenLoadCount);
420
421 if (mod->isFIPS) {
422 entry = (CK_C_GetFunctionList)
423 PR_FindSymbol(softokenLib, "FC_GetFunctionList");
424 } else {
425 entry = (CK_C_GetFunctionList)
426 PR_FindSymbol(softokenLib, "NSC_GetFunctionList");
427 }
428
429 if (!entry)
430 return SECFailure;
431
432 if (mod->isModuleDB) {
433 mod->moduleDBFunc = (CK_C_GetFunctionList)
434 PR_FindSymbol(softokenLib, "NSC_ModuleDBFunc");
435 }
436 #endif
437
438 if (mod->moduleDBOnly) {
439 mod->loaded = PR_TRUE;
440 return SECSuccess;
441 }
442 } else {
443 /* Not internal, load the DLL and look up C_GetFunctionList */
444 if (mod->dllName == NULL) {
445 return SECFailure;
446 }
447 #if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS)
448 if (strstr(mod->dllName, "nssckbi") != NULL) {
449 mod->library = NULL;
450 PORT_Assert(!mod->moduleDBOnly);
451 entry = builtinsC_GetFunctionList;
452 PORT_Assert(!mod->isModuleDB);
453 goto library_loaded;
454 }
455 #endif
456
457 /* load the library. If this succeeds, then we have to remember to
458 * unload the library if anything goes wrong from here on out...
459 */
460 library = PR_LoadLibrary(mod->dllName);
461 mod->library = (void *)library;
462
463 if (library == NULL) {
464 return SECFailure;
465 }
466
467 /*
468 * now we need to get the entry point to find the function pointers
469 */
470 if (!mod->moduleDBOnly) {
471 entry = (CK_C_GetFunctionList)
472 PR_FindSymbol(library, "C_GetFunctionList");
473 }
474 if (mod->isModuleDB) {
475 mod->moduleDBFunc = (void *)
476 PR_FindSymbol(library, "NSS_ReturnModuleSpecData");
477 }
478 #if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS)
479 library_loaded:
480 #endif
481 if (mod->moduleDBFunc == NULL) mod->isModuleDB = PR_FALSE;
482 if (entry == NULL) {
483 if (mod->isModuleDB) {
484 mod->loaded = PR_TRUE;
485 mod->moduleDBOnly = PR_TRUE;
486 return SECSuccess;
487 }
488 PR_UnloadLibrary(library);
489 return SECFailure;
490 }
491 }
492
493 /*
494 * We need to get the function list
495 */
496 if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK)
497 goto fail;
498
499 #ifdef DEBUG_MODULE
500 if (PR_TRUE) {
501 modToDBG = PR_GetEnv("NSS_DEBUG_PKCS11_MODULE");
502 if (modToDBG && strcmp(mod->commonName, modToDBG) == 0) {
503 mod->functionList = (void *)nss_InsertDeviceLog(
504 (CK_FUNCTION_LIST_PTR)mod->functionList);
505 }
506 }
507 #endif
508
509 mod->isThreadSafe = PR_TRUE;
510
511 /* Now we initialize the module */
512 rv = secmod_ModuleInit(mod, oldModule, &alreadyLoaded);
513 if (rv != SECSuccess) {
514 goto fail;
515 }
516
517 /* module has been reloaded, this module itself is done,
518 * return to the caller */
519 if (mod->functionList == NULL) {
520 mod->loaded = PR_TRUE; /* technically the module is loaded.. */
521 return SECSuccess;
522 }
523
524 /* check the version number */
525 if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2;
526 if (info.cryptokiVersion.major != 2) goto fail2;
527 /* all 2.0 are a priori *not* thread safe */
528 if (info.cryptokiVersion.minor < 1) {
529 if (!loadSingleThreadedModules) {
530 PORT_SetError(SEC_ERROR_INCOMPATIBLE_PKCS11);
531 goto fail2;
532 } else {
533 mod->isThreadSafe = PR_FALSE;
534 }
535 }
536 mod->cryptokiVersion = info.cryptokiVersion;
537
538 /* If we don't have a common name, get it from the PKCS 11 module */
539 if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) {
540 mod->commonName = PK11_MakeString(mod->arena,NULL,
541 (char *)info.libraryDescription, sizeof(info.libraryDescription));
542 if (mod->commonName == NULL) goto fail2;
543 }
544
545
546 /* initialize the Slots */
547 if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) {
548 CK_SLOT_ID *slotIDs;
549 int i;
550 CK_RV crv;
551
552 mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena,
553 sizeof(PK11SlotInfo *) * slotCount);
554 if (mod->slots == NULL) goto fail2;
555
556 slotIDs = (CK_SLOT_ID *) PORT_Alloc(sizeof(CK_SLOT_ID)*slotCount);
557 if (slotIDs == NULL) {
558 goto fail2;
559 }
560 crv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount);
561 if (crv != CKR_OK) {
562 PORT_Free(slotIDs);
563 goto fail2;
564 }
565
566 /* Initialize each slot */
567 for (i=0; i < (int)slotCount; i++) {
568 mod->slots[i] = PK11_NewSlotInfo(mod);
569 PK11_InitSlot(mod,slotIDs[i],mod->slots[i]);
570 /* look down the slot info table */
571 PK11_LoadSlotList(mod->slots[i],mod->slotInfo,mod->slotInfoCount);
572 SECMOD_SetRootCerts(mod->slots[i],mod);
573 /* explicitly mark the internal slot as such if IsInternalKeySlot()
574 * is set */
575 if (secmod_IsInternalKeySlot(mod) && (i == (mod->isFIPS ? 0 : 1))) {
576 pk11_SetInternalKeySlotIfFirst(mod->slots[i]);
577 }
578 }
579 mod->slotCount = slotCount;
580 mod->slotInfoCount = 0;
581 PORT_Free(slotIDs);
582 }
583
584 mod->loaded = PR_TRUE;
585 mod->moduleID = nextModuleID++;
586 return SECSuccess;
587 fail2:
588 if (enforceAlreadyInitializedError || (!alreadyLoaded)) {
589 PK11_GETTAB(mod)->C_Finalize(NULL);
590 }
591 fail:
592 mod->functionList = NULL;
593 disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
594 if (library && !disableUnload) {
595 PR_UnloadLibrary(library);
596 }
597 return SECFailure;
598 }
599
600 SECStatus
601 SECMOD_UnloadModule(SECMODModule *mod) {
602 PRLibrary *library;
603 char *disableUnload = NULL;
604
605 if (!mod->loaded) {
606 return SECFailure;
607 }
608 if (finalizeModules) {
609 if (mod->functionList &&!mod->moduleDBOnly) {
610 PK11_GETTAB(mod)->C_Finalize(NULL);
611 }
612 }
613 mod->moduleID = 0;
614 mod->loaded = PR_FALSE;
615
616 /* do we want the semantics to allow unloading the internal library?
617 * if not, we should change this to SECFailure and move it above the
618 * mod->loaded = PR_FALSE; */
619 if (mod->internal && (mod->dllName == NULL)) {
620 #ifndef NSS_STATIC
621 if (0 == PR_ATOMIC_DECREMENT(&softokenLoadCount)) {
622 if (softokenLib) {
623 disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
624 if (!disableUnload) {
625 PRStatus status = PR_UnloadLibrary(softokenLib);
626 PORT_Assert(PR_SUCCESS == status);
627 }
628 softokenLib = NULL;
629 }
630 loadSoftokenOnce = pristineCallOnce;
631 }
632 #endif
633 return SECSuccess;
634 }
635
636 library = (PRLibrary *)mod->library;
637 /* paranoia */
638 if (library == NULL) {
639 #if defined(NSS_STATIC) && !defined(NSS_DISABLE_ROOT_CERTS)
640 if (strstr(mod->dllName, "nssckbi") != NULL) {
641 return SECSuccess;
642 }
643 #endif
644 return SECFailure;
645 }
646
647 disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
648 if (!disableUnload) {
649 PR_UnloadLibrary(library);
650 }
651 return SECSuccess;
652 }
653
654 void
655 nss_DumpModuleLog(void)
656 {
657 #ifdef DEBUG_MODULE
658 if (modToDBG) {
659 print_final_statistics();
660 }
661 #endif
662 }
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)