/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "nss.h"
#include "secutil.h"
#include "pk11pub.h"
#include "cert.h"
typedef struct commandDescriptStr {
int required;
char *arg;
char *des;
} commandDescript;
enum optionNames {
opt_liborder = 0,
opt_mainDB,
opt_lib1DB,
opt_lib2DB,
opt_mainRO,
opt_lib1RO,
opt_lib2RO,
opt_mainCMD,
opt_lib1CMD,
opt_lib2CMD,
opt_mainTokNam,
opt_lib1TokNam,
opt_lib2TokNam,
opt_oldStyle,
opt_verbose,
opt_summary,
opt_help,
opt_last
};
static const secuCommandFlag options_init[] = {
{
/* opt_liborder */ 'o', PR_TRUE, "1M2zmi", PR_TRUE, "order" },
{
/* opt_mainDB */ 'd', PR_TRUE, 0, PR_FALSE, "main_db" },
{
/* opt_lib1DB */ '1', PR_TRUE, 0, PR_FALSE, "lib1_db" },
{
/* opt_lib2DB */ '2', PR_TRUE, 0, PR_FALSE, "lib2_db" },
{
/* opt_mainRO */ 'r', PR_FALSE, 0, PR_FALSE, "main_readonly" },
{
/* opt_lib1RO */ 0, PR_FALSE, 0, PR_FALSE, "lib1_readonly" },
{
/* opt_lib2RO */ 0, PR_FALSE, 0, PR_FALSE, "lib2_readonly" },
{
/* opt_mainCMD */ 'c', PR_TRUE, 0, PR_FALSE, "main_command" },
{
/* opt_lib1CMD */ 0, PR_TRUE, 0, PR_FALSE, "lib1_command" },
{
/* opt_lib2CMD */ 0, PR_TRUE, 0, PR_FALSE, "lib2_command" },
{
/* opt_mainTokNam */ 't', PR_TRUE, 0, PR_FALSE, "main_token_name" },
{
/* opt_lib1TokNam */ 0, PR_TRUE, 0, PR_FALSE, "lib1_token_name" },
{
/* opt_lib2TokNam */ 0, PR_TRUE, 0, PR_FALSE, "lib2_token_name" },
{
/* opt_oldStype */ 's', PR_FALSE, 0, PR_FALSE, "oldStype" },
{
/* opt_verbose */ 'v', PR_FALSE, 0, PR_FALSE, "verbose" },
{
/* opt_summary */ 'z', PR_FALSE, 0, PR_FALSE, "summary" },
{
/* opt_help */ 'h', PR_FALSE, 0, PR_FALSE, "help" }
};
static const commandDescript options_des[] = {
{
/* opt_liborder */ PR_FALSE, "initOrder",
" Specifies the order of NSS initialization and shutdown. Order is\n"
" given as a string where each character represents either an init or\n"
" a shutdown of the main program or one of the 2 test libraries\n"
" (library 1 and library 2). The valid characters are as follows:\n"
" M Init the main program\n 1 Init library 1\n"
" 2 Init library 2\n"
" m Shutdown the main program\n i Shutdown library 1\n"
" z Shutdown library 2\n" },
{
/* opt_mainDB */ PR_TRUE, "nss_db",
" Specified the directory to open the nss database for the main\n"
" program. Must be specified if \"M\
" is given in the order string\n" },
{
/* opt_lib1DB */ PR_FALSE, "nss_db",
" Specified the directory to open the nss database for library 1.\n"
" Must be specified if \"1\
" is given in the order string\n" },
{
/* opt_lib2DB */ PR_FALSE, "nss_db",
" Specified the directory to open the nss database for library 2.\n"
" Must be specified if \"2\
" is given in the order string\n" },
{
/* opt_mainRO */ PR_FALSE, NULL,
" Open the main program's database read only.\n" },
{
/* opt_lib1RO */ PR_FALSE, NULL,
" Open library 1's database read only.\n" },
{
/* opt_lib2RO */ PR_FALSE, NULL,
" Open library 2's database read only.\n" },
{
/* opt_mainCMD */ PR_FALSE, "nss_command",
" Specifies the NSS command to execute in the main program.\n"
" Valid commands are: \n"
" key_slot, list_slots, list_certs, add_cert, none.\n"
" Default is \"none\
".\n" },
{
/* opt_lib1CMD */ PR_FALSE, "nss_command",
" Specifies the NSS command to execute in library 1.\n" },
{
/* opt_lib2CMD */ PR_FALSE, "nss_command",
" Specifies the NSS command to execute in library 2.\n" },
{
/* opt_mainTokNam */ PR_FALSE, "token_name",
" Specifies the name of PKCS11 token for the main program's "
"database.\n" },
{
/* opt_lib1TokNam */ PR_FALSE, "token_name",
" Specifies the name of PKCS11 token for library 1's database.\n" },
{
/* opt_lib2TokNam */ PR_FALSE, "token_name",
" Specifies the name of PKCS11 token for library 2's database.\n" },
{
/* opt_oldStype */ PR_FALSE, NULL,
" Use NSS_Shutdown rather than NSS_ShutdownContext in the main\n"
" program.\n" },
{
/* opt_verbose */ PR_FALSE, NULL,
" Noisily output status to standard error\n" },
{
/* opt_summarize */ PR_FALSE, NULL,
"report a summary of the test results\n" },
{
/* opt_help */ PR_FALSE, NULL, " give this message\n" }
};
/*
* output our short help (table driven). (does not exit).
*/
static void
short_help(
const char *prog)
{
int count = opt_last;
int i, words_found;
/* make sure all the tables are up to date before we allow compiles to
* succeed */
PR_STATIC_ASSERT(
sizeof(options_init) /
sizeof(secuCommandFlag) == opt_last);
PR_STATIC_ASSERT(
sizeof(options_init) /
sizeof(secuCommandFlag) ==
sizeof(options_des) /
sizeof(commandDescript));
/* print the base usage */
fprintf(stderr,
"usage: %s ", prog);
for (i = 0, words_found = 0; i < count; i++) {
if (!options_des[i].required) {
fprintf(stderr,
"[");
}
if (options_init[i].longform) {
fprintf(stderr,
"--%s", options_init[i].longform);
words_found++;
}
else {
fprintf(stderr,
"-%c", options_init[i].flag);
}
if (options_init[i].needsArg) {
if (options_des[i].arg) {
fprintf(stderr,
" %s", options_des[i].arg);
}
else {
fprintf(stderr,
" arg");
}
words_found++;
}
if (!options_des[i].required) {
fprintf(stderr,
"]");
}
if (i < count - 1) {
if (words_found >= 5) {
fprintf(stderr,
"\n ");
words_found = 0;
}
else {
fprintf(stderr,
" ");
}
}
}
fprintf(stderr,
"\n");
}
/*
* print out long help. like short_help, this does not exit
*/
static void
long_help(
const char *prog)
{
int i;
int count = opt_last;
short_help(prog);
/* print the option descriptions */
fprintf(stderr,
"\n");
for (i = 0; i < count; i++) {
fprintf(stderr,
" ");
if (options_init[i].flag) {
fprintf(stderr,
"-%c", options_init[i].flag);
if (options_init[i].longform) {
fprintf(stderr,
",");
}
}
if (options_init[i].longform) {
fprintf(stderr,
"--%s", options_init[i].longform);
}
if (options_init[i].needsArg) {
if (options_des[i].arg) {
fprintf(stderr,
" %s", options_des[i].arg);
}
else {
fprintf(stderr,
" arg");
}
if (options_init[i].arg) {
fprintf(stderr,
" (default = \"%s\
")", options_init[i].arg);
}
}
fprintf(stderr,
"\n%s", options_des[i].des);
}
}
/*
* record summary data
*/
struct bufferData {
char *data;
/* lowest address of the buffer */
char *next;
/* pointer to the next element on the buffer */
int len;
/* length of the buffer */
};
/* our actual buffer. If data is NULL, then all append ops
* except are noops */
static struct bufferData buffer = { NULL, NULL, 0 };
#define CHUNK_SIZE 1000
/*
* get our initial data. and set the buffer variables up. on failure,
* just don't initialize the buffer.
*/
static void
initBuffer(
void)
{
buffer.data = PORT_Alloc(CHUNK_SIZE);
if (!buffer.data) {
return;
}
buffer.next = buffer.data;
buffer.len = CHUNK_SIZE;
}
/*
* grow the buffer. If we can't get more data, record a 'D' in the second
* to last record and allow the rest of the data to overwrite the last
* element.
*/
static void
growBuffer(
void)
{
char *
new = PORT_Realloc(buffer.data, buffer.len + CHUNK_SIZE);
if (!
new) {
buffer.data[buffer.len - 2] =
'D';
/* signal malloc failure in summary */
/* buffer must always point to good memory if it exists */
buffer.next = buffer.data + (buffer.len - 1);
return;
}
buffer.next =
new + (buffer.next - buffer.data);
buffer.data =
new;
buffer.len += CHUNK_SIZE;
}
/*
* append a label, doubles as appending a single character.
*/
static void
appendLabel(
char label)
{
if (!buffer.data) {
return;
}
*buffer.next++ = label;
if (buffer.data + buffer.len >= buffer.next) {
growBuffer();
}
}
/*
* append a string onto the buffer. The result will be <string>
*/
static void
appendString(
char *string)
{
if (!buffer.data) {
return;
}
appendLabel(
'<');
while (*string) {
appendLabel(*string++);
}
appendLabel(
'>');
}
/*
* append a bool, T= true, F=false
*/
static void
appendBool(PRBool
bool)
{
if (!buffer.data) {
return;
}
if (
bool) {
appendLabel(
't');
}
else {
appendLabel(
'f');
}
}
/*
* append a single hex nibble.
*/
static void
appendHex(
unsigned char nibble)
{
if (nibble <= 9) {
appendLabel(
'0' + nibble);
}
else {
appendLabel(
'a' + nibble - 10);
}
}
/*
* append a 32 bit integer (even on a 64 bit platform).
* for simplicity append it as a hex value, full extension with 0x prefix.
*/
static void
appendInt(
unsigned int value)
{
int i;
if (!buffer.data) {
return;
}
appendLabel(
'0');
appendLabel(
'x');
value = value & 0xffffffff;
/* only look at the buttom 8 bytes */
for (i = 0; i < 8; i++) {
appendHex(value >> 28);
value = value << 4;
}
}
/* append a trust flag */
static void
appendFlags(
unsigned int flag)
{
char trust[10];
char *cp = trust;
trust[0] = 0;
printflags(trust, flag);
while (*cp) {
appendLabel(*cp++);
}
}
/*
* dump our buffer out with a result= flag so we can find it easily.
* free the buffer as a side effect.
*/
static void
dumpBuffer(
void)
{
if (!buffer.data) {
return;
}
appendLabel(0);
/* terminate */
printf(
"\nresult=%s\n", buffer.data);
PORT_Free(buffer.data);
buffer.data = buffer.next = NULL;
buffer.len = 0;
}
/*
* usage, like traditional usage, automatically exit
*/
static void
usage(
const char *prog)
{
short_help(prog);
dumpBuffer();
exit(1);
}
/*
* like usage, except prints the long version of help
*/
static void
usage_long(
const char *prog)
{
long_help(prog);
dumpBuffer();
exit(1);
}
static const char *
bool2String(PRBool
bool)
{
return bool ?
"true" :
"false";
}
/*
* print out interesting info about the given slot
*/
void
print_slot(PK11SlotInfo *slot,
int log)
{
if (log) {
fprintf(stderr,
"* Name=%s Token_Name=%s present=%s, ro=%s *\n",
PK11_GetSlotName(slot), PK11_GetTokenName(slot),
bool2String(PK11_IsPresent(slot)),
bool2String(PK11_IsReadOnly(slot)));
}
appendLabel(
'S');
appendString(PK11_GetTokenName(slot));
appendBool(PK11_IsPresent(slot));
appendBool(PK11_IsReadOnly(slot));
}
/*
* list all our slots
*/
void
do_list_slots(
const char *progName,
int log)
{
PK11SlotList *list;
PK11SlotListElement *le;
list = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, NULL);
if (list == NULL) {
fprintf(stderr,
"ERROR: no tokens found %s\n",
SECU_Strerror(PORT_GetError()));
appendLabel(
'S');
appendString(
"none");
return;
}
for (le = PK11_GetFirstSafe(list); le;
le = PK11_GetNextSafe(list, le, PR_TRUE)) {
print_slot(le->slot, log);
}
PK11_FreeSlotList(list);
}
static PRBool
sort_CN(CERTCertificate *certa, CERTCertificate *certb,
void *arg)
{
char *commonNameA, *commonNameB;
int ret;
commonNameA = CERT_GetCommonName(&certa->subject);
commonNameB = CERT_GetCommonName(&certb->subject);
if (commonNameA == NULL) {
PORT_Free(commonNameB);
return PR_TRUE;
}
if (commonNameB == NULL) {
PORT_Free(commonNameA);
return PR_FALSE;
}
ret = PORT_Strcmp(commonNameA, commonNameB);
PORT_Free(commonNameA);
PORT_Free(commonNameB);
return (ret < 0) ? PR_TRUE : PR_FALSE;
}
/*
* list all the certs
*/
void
do_list_certs(
const char *progName,
int log)
{
CERTCertList *list;
CERTCertList *sorted;
CERTCertListNode *node;
CERTCertTrust trust;
unsigned int i;
list = PK11_ListCerts(PK11CertListUnique, NULL);
if (list == NULL) {
fprintf(stderr,
"ERROR: no certs found %s\n",
SECU_Strerror(PORT_GetError()));
appendLabel(
'C');
appendString(
"none");
return;
}
sorted = CERT_NewCertList();
if (sorted == NULL) {
fprintf(stderr,
"ERROR: no certs found %s\n",
SECU_Strerror(PORT_GetError()));
appendLabel(
'C');
appendLabel(
'E');
appendInt(PORT_GetError());
return;
}
/* sort the list */
for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
node = CERT_LIST_NEXT(node)) {
CERT_AddCertToListSorted(sorted, node->cert, sort_CN, NULL);
}
for (node = CERT_LIST_HEAD(sorted); !CERT_LIST_END(node, sorted);
node = CERT_LIST_NEXT(node)) {
CERTCertificate *cert = node->cert;
char *commonName;
SECU_PrintCertNickname(node, stderr);
if (log) {
fprintf(stderr,
"* Slot=%s*\n", cert->slot ? PK11_GetTokenName(cert->slot) :
"none");
fprintf(stderr,
"* Nickname=%s*\n", cert->nickname);
fprintf(stderr,
"* Subject=<%s>*\n", cert->subjectName);
fprintf(stderr,
"* Issuer=<%s>*\n", cert->issuerName);
fprintf(stderr,
"* SN=");
for (i = 0; i < cert->serialNumber.len; i++) {
if (i != 0)
fprintf(stderr,
":");
fprintf(stderr,
"%02x", cert->serialNumber.data[0]);
}
fprintf(stderr,
" *\n");
}
appendLabel(
'C');
commonName = CERT_GetCommonName(&cert->subject);
appendString(commonName ? commonName :
"*NoName*");
PORT_Free(commonName);
if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
appendFlags(trust.sslFlags);
appendFlags(trust.emailFlags);
appendFlags(trust.objectSigningFlags);
}
}
CERT_DestroyCertList(list);
}
/*
* need to implement yet... try to add a new certificate
*/
void
do_add_cert(
const char *progName,
int log)
{
PORT_Assert(
/* do_add_cert not implemented */ 0);
}
/*
* display the current key slot
*/
void
do_key_slot(
const char *progName,
int log)
{
PK11SlotInfo *slot = PK11_GetInternalKeySlot();
if (!slot) {
fprintf(stderr,
"ERROR: no internal key slot found %s\n",
SECU_Strerror(PORT_GetError()));
appendLabel(
'K');
appendLabel(
'S');
appendString(
"none");
}
print_slot(slot, log);
PK11_FreeSlot(slot);
}
/*
* execute some NSS command.
*/
void
do_command(
const char *label,
int initialized, secuCommandFlag *command,
const char *progName,
int log)
{
char *command_string;
if (!initialized) {
return;
}
if (command->activated) {
command_string = command->arg;
}
else {
command_string =
"none";
}
if (log) {
fprintf(stderr,
"*Executing nss command \"%s\
" for %s*\n",
command_string, label);
}
/* do something */
if (PORT_Strcasecmp(command_string,
"list_slots") == 0) {
do_list_slots(progName, log);
}
else if (PORT_Strcasecmp(command_string,
"list_certs") == 0) {
do_list_certs(progName, log);
}
else if (PORT_Strcasecmp(command_string,
"add_cert") == 0) {
do_add_cert(progName, log);
}
else if (PORT_Strcasecmp(command_string,
"key_slot") == 0) {
do_key_slot(progName, log);
}
else if (PORT_Strcasecmp(command_string,
"none") != 0) {
fprintf(stderr,
">> Unknown command (%s)\n", command_string);
appendLabel(
'E');
appendString(
"bc");
usage_long(progName);
}
}
/*
* functions do handle
* different library initializations.
*/
static int main_initialized;
static int lib1_initialized;
static int lib2_initialized;
void
main_Init(secuCommandFlag *db, secuCommandFlag *tokNam,
int readOnly,
const char *progName,
int log)
{
SECStatus rv;
if (log) {
fprintf(stderr,
"*NSS_Init for the main program*\n");
}
appendLabel(
'M');
if (!db->activated) {
fprintf(stderr,
">> No main_db has been specified\n");
usage(progName);
}
if (main_initialized) {
fprintf(stderr,
"Warning: Second initialization of Main\n");
appendLabel(
'E');
appendString(
"2M");
}
if (tokNam->activated) {
PK11_ConfigurePKCS11(NULL, NULL, NULL, tokNam->arg,
NULL, NULL, NULL, NULL, 0, 0);
}
rv = NSS_Initialize(db->arg,
"",
"",
"",
NSS_INIT_NOROOTINIT |
(readOnly ? NSS_INIT_READONLY : 0));
if (rv != SECSuccess) {
appendLabel(
'E');
appendInt(PORT_GetError());
fprintf(stderr,
">> %s\n", SECU_Strerror(PORT_GetError()));
dumpBuffer();
exit(1);
}
main_initialized = 1;
}
void
main_Do(secuCommandFlag *command,
const char *progName,
int log)
{
do_command(
"main", main_initialized, command, progName, log);
}
void
main_Shutdown(
int old_style,
const char *progName,
int log)
{
SECStatus rv;
appendLabel(
'N');
if (log) {
fprintf(stderr,
"*NSS_Shutdown for the main program*\n");
}
if (!main_initialized) {
fprintf(stderr,
"Warning: Main shutdown without corresponding init\n");
}
if (old_style) {
rv = NSS_Shutdown();
}
else {
rv = NSS_ShutdownContext(NULL);
}
fprintf(stderr,
"Shutdown main state = %d\n", rv);
if (rv != SECSuccess) {
appendLabel(
'E');
appendInt(PORT_GetError());
fprintf(stderr,
"ERROR: %s\n", SECU_Strerror(PORT_GetError()));
}
main_initialized = 0;
}
/* common library init */
NSSInitContext *
lib_Init(
const char *lableString,
char label,
int initialized,
secuCommandFlag *db, secuCommandFlag *tokNam,
int readonly,
const char *progName,
int log)
{
NSSInitContext *ctxt;
NSSInitParameters initStrings;
NSSInitParameters *initStringPtr = NULL;
appendLabel(label);
if (log) {
fprintf(stderr,
"*NSS_Init for %s*\n", lableString);
}
if (!db->activated) {
fprintf(stderr,
">> No %s_db has been specified\n", lableString);
usage(progName);
}
if (initialized) {
fprintf(stderr,
"Warning: Second initialization of %s\n", lableString);
}
if (tokNam->activated) {
PORT_Memset(&initStrings, 0,
sizeof(initStrings));
initStrings.length =
sizeof(initStrings);
initStrings.dbTokenDescription = tokNam->arg;
initStringPtr = &initStrings;
}
ctxt = NSS_InitContext(db->arg,
"",
"",
"", initStringPtr,
NSS_INIT_NOROOTINIT |
(readonly ? NSS_INIT_READONLY : 0));
if (ctxt == NULL) {
appendLabel(
'E');
appendInt(PORT_GetError());
fprintf(stderr,
">> %s\n", SECU_Strerror(PORT_GetError()));
dumpBuffer();
exit(1);
}
return ctxt;
}
/* common library shutdown */
void
lib_Shutdown(
const char *labelString,
char label, NSSInitContext *ctx,
int initialize,
const char *progName,
int log)
{
SECStatus rv;
appendLabel(label);
if (log) {
fprintf(stderr,
"*NSS_Shutdown for %s\n*", labelString);
}
if (!initialize) {
fprintf(stderr,
"Warning: %s shutdown without corresponding init\n",
labelString);
}
rv = NSS_ShutdownContext(ctx);
fprintf(stderr,
"Shutdown %s state = %d\n", labelString, rv);
if (rv != SECSuccess) {
appendLabel(
'E');
appendInt(PORT_GetError());
fprintf(stderr,
"ERROR: %s\n", SECU_Strerror(PORT_GetError()));
}
}
static NSSInitContext *lib1_context;
static NSSInitContext *lib2_context;
void
lib1_Init(secuCommandFlag *db, secuCommandFlag *tokNam,
int readOnly,
const char *progName,
int log)
{
lib1_context = lib_Init(
"lib1",
'1', lib1_initialized, db, tokNam,
readOnly, progName, log);
lib1_initialized = 1;
}
void
lib2_Init(secuCommandFlag *db, secuCommandFlag *tokNam,
int readOnly,
const char *progName,
int log)
{
lib2_context = lib_Init(
"lib2",
'2', lib2_initialized,
db, tokNam, readOnly, progName, log);
lib2_initialized = 1;
}
void
lib1_Do(secuCommandFlag *command,
const char *progName,
int log)
{
do_command(
"lib1", lib1_initialized, command, progName, log);
}
void
lib2_Do(secuCommandFlag *command,
const char *progName,
int log)
{
do_command(
"lib2", lib2_initialized, command, progName, log);
}
void
lib1_Shutdown(
const char *progName,
int log)
{
lib_Shutdown(
"lib1",
'I', lib1_context, lib1_initialized, progName, log);
lib1_initialized = 0;
/* don't clear lib1_Context, so we can test multiple attempts to close
* the same context produces correct errors*/
}
void
lib2_Shutdown(
const char *progName,
int log)
{
lib_Shutdown(
"lib2",
'Z', lib2_context, lib2_initialized, progName, log);
lib2_initialized = 0;
/* don't clear lib2_Context, so we can test multiple attempts to close
* the same context produces correct errors*/
}
int
main(
int argc,
char **argv)
{
SECStatus rv;
secuCommand libinit;
char *progName;
char *order;
secuCommandFlag *options;
int log = 0;
progName = strrchr(argv[0],
'/');
progName = progName ? progName + 1 : argv[0];
libinit.numCommands = 0;
libinit.commands = 0;
libinit.numOptions = opt_last;
options = (secuCommandFlag *)PORT_Alloc(
sizeof(options_init));
if (options == NULL) {
fprintf(stderr,
">> %s:Not enough free memory to run command\n",
progName);
exit(1);
}
PORT_Memcpy(options, options_init,
sizeof(options_init));
libinit.options = options;
rv = SECU_ParseCommandLine(argc, argv, progName, &libinit);
if (rv != SECSuccess) {
usage(progName);
}
if (libinit.options[opt_help].activated) {
long_help(progName);
exit(0);
}
log = libinit.options[opt_verbose].activated;
if (libinit.options[opt_summary].activated) {
initBuffer();
}
order = libinit.options[opt_liborder].arg;
if (!order) {
usage(progName);
}
if (log) {
fprintf(stderr,
"* initializing with order \"%s\
"*\n", order);
}
for (; *order; order++) {
switch (*order) {
case 'M':
main_Init(&libinit.options[opt_mainDB],
&libinit.options[opt_mainTokNam],
libinit.options[opt_mainRO].activated,
progName, log);
break;
case '1':
lib1_Init(&libinit.options[opt_lib1DB],
&libinit.options[opt_lib1TokNam],
libinit.options[opt_lib1RO].activated,
progName, log);
break;
case '2':
lib2_Init(&libinit.options[opt_lib2DB],
&libinit.options[opt_lib2TokNam],
libinit.options[opt_lib2RO].activated,
progName, log);
break;
case 'm':
main_Shutdown(libinit.options[opt_oldStyle].activated,
progName, log);
break;
case 'i':
lib1_Shutdown(progName, log);
break;
case 'z':
lib2_Shutdown(progName, log);
break;
default:
fprintf(stderr,
">> Unknown init/shutdown command \"%c\
"", *order);
usage_long(progName);
}
main_Do(&libinit.options[opt_mainCMD], progName, log);
lib1_Do(&libinit.options[opt_lib1CMD], progName, log);
lib2_Do(&libinit.options[opt_lib2CMD], progName, log);
}
if (NSS_IsInitialized()) {
appendLabel(
'X');
fprintf(stderr,
"Warning: NSS is initialized\n");
}
dumpBuffer();
exit(0);
}