use generic option parsing with usage information

This commit is contained in:
Martin Willi 2009-09-10 11:18:41 +02:00
parent 63ee88745a
commit e5e6c6f43c
1 changed files with 215 additions and 265 deletions

View File

@ -32,189 +32,198 @@
#include <credentials/certificates/certificate.h> #include <credentials/certificates/certificate.h>
#include <credentials/certificates/x509.h> #include <credentials/certificates/x509.h>
static void print_gen(FILE *out) static int help(int argc, char *argv[]);
static int gen(int argc, char *argv[]);
static int pub(int argc, char *argv[]);
static int keyid(int argc, char *argv[]);
static int self(int argc, char *argv[]);
static int issue(int argc, char *argv[]);
static int verify(int argc, char *argv[]);
typedef enum {
CMD_HELP = 0,
CMD_GEN,
CMD_PUB,
CMD_KEYID,
CMD_SELF,
CMD_ISSUE,
CMD_VERIFY,
CMD_MAX
} cmd_t;
#define MAX_OPTIONS 14
static struct {
int (*call)(int, char*[]);
char op;
char *cmd;
char *description;
char *line[8];
struct {
char *name;
char op;
int arg;
char *desc;
} options[MAX_OPTIONS];
} cmds[] = {
[CMD_HELP] = { help, 'h', "help",
"show usage information"
},
[CMD_GEN] = { gen, 'g', "gen", "generate a new private key",
{"[--type rsa|ecdsa] [--size bits] [--outform der|pem|pgp]"},
{
{"help", 'h', 0, "show usage information"},
{"type", 't', 1, "type of key, default: rsa"},
{"size", 's', 1, "keylength in bits, default: rsa 2048, ecdsa 384"},
{"outform", 'f', 1, "encoding of generated private key"},
}
},
[CMD_PUB] = { pub, 'p', "pub",
"extract the public key from a private key/certificate",
{"[--in file] [--type rsa|ecdsa|x509] [--outform der|pem|pgp]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "input file, default: stdin"},
{"type", 't', 1, "type of credential, default: rsa"},
{"outform", 'f', 1, "encoding of extracted public key"},
}
},
[CMD_KEYID] = { keyid, 'k', "keyid",
"calculate key identifiers of a key/certificate",
{"[--in file] [--type rsa-priv|ecdsa-priv|pub|x509]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "input file, default: stdin"},
{"type", 't', 1, "type of key, default: rsa-priv"},
}
},
[CMD_SELF] = { self, 's', "self",
"create a self signed certificate",
{"[--in file] [--type rsa|ecdsa]",
" --dn distinguished-name [--san subjectAltName]+",
"[--lifetime days] [--serial hex] [--ca]",
"[--digest md5|sha1|sha224|sha256|sha384|sha512]",
"[--options file]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "private key input file, default: stdin"},
{"type", 't', 1, "type of input key, default: rsa"},
{"dn", 'd', 1, "subject and issuer distinguished name"},
{"san", 'a', 1, "subjectAltName to include in certificate"},
{"lifetime",'l', 1, "days the certificate is valid, default: 1080"},
{"serial", 's', 1, "serial number in hex, default: random"},
{"ca", 'b', 0, "include CA basicConstraint, default: no"},
{"digest", 'g', 1, "digest for signature creation, default: sha1"},
{"options", '+', 1, "read command line options from file"},
}
},
[CMD_ISSUE] = { issue, 'i', "issue",
"issue a certificate using a CA certificate and key",
{"[--in file] [--type pub|pkcs10]",
" --cacert file --cakey file",
" --dn subject-dn [--san subjectAltName]+",
"[--lifetime days] [--serial hex] [--ca]",
"[--digest md5|sha1|sha224|sha256|sha384|sha512]",
"[--options file]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "public key/request file to issue, default: stdin"},
{"type", 't', 1, "type of input, default: pub"},
{"cacert", 'c', 1, "CA certificate file"},
{"cakey", 'k', 1, "CA private key file"},
{"dn", 'd', 1, "distinguished name to include as subject"},
{"san", 'a', 1, "subjectAltName to include in certificate"},
{"lifetime",'l', 1, "days the certificate is valid, default: 1080"},
{"serial", 's', 1, "serial number in hex, default: random"},
{"ca", 'b', 0, "include CA basicConstraint, default: no"},
{"digest", 'g', 1, "digest for signature creation, default: sha1"},
{"options", '+', 1, "read command line options from file"},
}
},
[CMD_VERIFY] = { verify, 'v', "verify",
"verify a certificate using the CA certificate",
{"[--in file] [--ca file]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "x509 certifcate to verify, default: stdin"},
{"cacert", 'c', 1, "CA certificate, default: verify self signed"},
}
},
};
/**
* Global options used by all subcommands
*/
static struct option long_opts[countof(cmds) > MAX_OPTIONS ?: MAX_OPTIONS];
/**
* Build long_opts for a specific command
*/
static void build_opts(cmd_t cmd)
{ {
fprintf(out, " pki --gen [--type rsa|ecdsa] [--size bits] [--outform der|pem|pgp]\n"); int i;
fprintf(out, " generate a new private key\n");
fprintf(out, " --type type of key, default: rsa\n"); memset(long_opts, 0, sizeof(long_opts));
fprintf(out, " --size keylength in bits, default: rsa 2048, ecdsa 384\n"); if (cmd == CMD_HELP)
fprintf(out, " --outform encoding of generated private key\n"); {
for (i = 0; i < CMD_MAX; i++)
{
long_opts[i].name = cmds[i].cmd;
long_opts[i].val = cmds[i].op;
}
}
else
{
for (i = 0; cmds[cmd].options[i].name; i++)
{
long_opts[i].name = cmds[cmd].options[i].name;
long_opts[i].has_arg = cmds[cmd].options[i].arg;
long_opts[i].val = cmds[cmd].options[i].op;
}
}
} }
static void print_pub(FILE *out) /**
* Print usage text, with an optional error
*/
static int usage(cmd_t cmd, char *error)
{ {
fprintf(out, " pki --pub [--in file] [--type rsa|ecdsa|x509] [--outform der|pem|pgp]\n"); FILE *out = stdout;
fprintf(out, " extract the public key from a private key/certificate\n"); int i;
fprintf(out, " --in input file, default: stdin\n");
fprintf(out, " --type type of credential, default: rsa\n");
fprintf(out, " --outform encoding of extracted public key\n");
}
static void print_keyid(FILE *out) if (error)
{ {
fprintf(out, " pki --keyid [--in file] [--type rsa-priv|ecdsa-priv|pub|x509]\n"); out = stderr;
fprintf(out, " calculate key identifiers of a key/certificate\n"); fprintf(out, "Error: %s\n", error);
fprintf(out, " --in input file, default: stdin\n"); }
fprintf(out, " --type type of key, default: rsa-priv\n");
}
static void print_self(FILE *out)
{
fprintf(out, " pki --self [--in file] [--type rsa|ecdsa]\n");
fprintf(out, " --dn distinguished-name [--san subjectAltName]+\n");
fprintf(out, " [--lifetime days] [--serial hex] [--ca]\n");
fprintf(out, " [--digest md5|sha1|sha224|sha256|sha384|sha512]\n");
fprintf(out, " [--options file]\n");
fprintf(out, " create a self signed certificate\n");
fprintf(out, " --in private key input file, default: stdin\n");
fprintf(out, " --type type of input key, default: rsa\n");
fprintf(out, " --dn subject and issuer distinguished name\n");
fprintf(out, " --san subjectAltName to include in certificate\n");
fprintf(out, " --lifetime days the certificate is valid, default: 1080\n");
fprintf(out, " --serial serial number in hex, default: random\n");
fprintf(out, " --ca include CA basicConstraint, default: no\n");
fprintf(out, " --digest digest for signature creation, default: sha1\n");
fprintf(out, " --options read command line options from file\n");
}
static void print_issue(FILE *out)
{
fprintf(out, " pki --issue [--in file] [--type pub|pkcs10]\n");
fprintf(out, " --cacert file --cakey file\n");
fprintf(out, " --dn subject-dn [--san subjectAltName]+\n");
fprintf(out, " [--lifetime days] [--serial hex] [--ca]\n");
fprintf(out, " [--digest md5|sha1|sha224|sha256|sha384|sha512]\n");
fprintf(out, " [--options file]\n");
fprintf(out, " issue a certificate using a CA certificate and key\n");
fprintf(out, " --in public key/request file to issue, default: stdin\n");
fprintf(out, " --type type of input, default: pub\n");
fprintf(out, " --cacert CA certificate file\n");
fprintf(out, " --cakey CA private key file\n");
fprintf(out, " --dn distinguished name to include as subject\n");
fprintf(out, " --san subjectAltName to include in certificate\n");
fprintf(out, " --lifetime days the certificate is valid, default: 1080\n");
fprintf(out, " --serial serial number in hex, default: random\n");
fprintf(out, " --ca include CA basicConstraint, default: no\n");
fprintf(out, " --digest digest for signature creation, default: sha1\n");
fprintf(out, " --options read command line options from file\n");
}
static void print_verify(FILE *out)
{
fprintf(out, " pki --verify [--in file] [--ca file]\n");
fprintf(out, " verify a certificate using the CA certificate\n");
fprintf(out, " --in x509 certifcate to verify, default: stdin\n");
fprintf(out, " --cacert CA certificate, default: verify self signed\n");
}
static void print_version(FILE *out, char *name)
{
fprintf(out, "strongSwan %s PKI tool\n", VERSION); fprintf(out, "strongSwan %s PKI tool\n", VERSION);
fprintf(out, "usage:\n"); fprintf(out, "usage:\n");
fprintf(out, " pki%s --help\n", name); if (cmd == CMD_HELP)
fprintf(out, " show this usage information\n");
}
static int usage(char *error)
{
FILE *out = stdout;
if (error)
{ {
out = stderr; for (i = 0; i < CMD_MAX; i++)
fprintf(out, "Error: %s\n", error); {
fprintf(out, " pki --%-6s %s\n", cmds[i].cmd, cmds[i].description);
}
} }
print_version(out, ""); else
print_gen(out);
print_pub(out);
print_keyid(out);
print_self(out);
print_issue(out);
print_verify(out);
return error != NULL;
}
static int usage_gen(char *error)
{
FILE *out = stdout;
if (error)
{ {
out = stderr; for (i = 0; cmds[cmd].line[i]; i++)
fprintf(out, "Error: %s\n", error); {
if (i == 0)
{
fprintf(out, " pki --%s %s\n", cmds[cmd].cmd, cmds[cmd].line[i]);
}
else
{
fprintf(out, " %s\n", cmds[cmd].line[i]);
}
}
for (i = 0; cmds[cmd].options[i].name; i++)
{
fprintf(out, " --%-8s %s\n",
cmds[cmd].options[i].name, cmds[cmd].options[i].desc);
}
} }
print_version(out, " --gen");
print_gen(out);
return error != NULL;
}
static int usage_pub(char *error)
{
FILE *out = stdout;
if (error)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
}
print_version(out, " --pub");
print_pub(out);
return error != NULL;
}
static int usage_keyid(char *error)
{
FILE *out = stdout;
if (error)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
}
print_version(out, " --keyid");
print_keyid(out);
return error != NULL;
}
static int usage_self(char *error)
{
FILE *out = stdout;
if (error)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
}
print_version(out, " --self");
print_self(out);
return error != NULL;
}
static int usage_issue(char *error)
{
FILE *out = stdout;
if (error)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
}
print_version(out, " --issue");
print_issue(out);
return error != NULL;
}
static int usage_verify(char *error)
{
FILE *out = stdout;
if (error)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
}
print_version(out, " --verify");
print_verify(out);
return error != NULL; return error != NULL;
} }
@ -275,6 +284,14 @@ static hash_algorithm_t get_digest(char *name)
return HASH_UNKNOWN; return HASH_UNKNOWN;
} }
/**
* Show usage information
*/
static int help(int argc, char *argv[])
{
return usage(CMD_HELP, NULL);
}
/** /**
* Generate a private key * Generate a private key
*/ */
@ -286,19 +303,12 @@ static int gen(int argc, char *argv[])
private_key_t *key; private_key_t *key;
chunk_t encoding; chunk_t encoding;
struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "type", required_argument, NULL, 't' },
{ "size", required_argument, NULL, 's' },
{ "outform", required_argument, NULL, 'o' },
{ 0,0,0,0 }
};
while (TRUE) while (TRUE)
{ {
switch (getopt_long(argc, argv, "", long_opts, NULL)) switch (getopt_long(argc, argv, "", long_opts, NULL))
{ {
case 'h': case 'h':
return usage_gen(NULL); return usage(CMD_GEN, NULL);
case 't': case 't':
if (streq(optarg, "rsa")) if (streq(optarg, "rsa"))
{ {
@ -310,26 +320,26 @@ static int gen(int argc, char *argv[])
} }
else else
{ {
return usage_gen("invalid key type"); return usage(CMD_GEN, "invalid key type");
} }
continue; continue;
case 'o': case 'o':
if (!get_form(optarg, &form, FALSE)) if (!get_form(optarg, &form, FALSE))
{ {
return usage_gen("invalid key output format"); return usage(CMD_GEN, "invalid key output format");
} }
continue; continue;
case 's': case 's':
size = atoi(optarg); size = atoi(optarg);
if (!size) if (!size)
{ {
return usage_gen("invalid key size"); return usage(CMD_GEN, "invalid key size");
} }
continue; continue;
case EOF: case EOF:
break; break;
default: default:
return usage_gen("invalid --gen option"); return usage(CMD_GEN, "invalid --gen option");
} }
break; break;
} }
@ -387,19 +397,12 @@ static int pub(int argc, char *argv[])
char *file = NULL; char *file = NULL;
void *cred; void *cred;
struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "type", required_argument, NULL, 't' },
{ "outform", required_argument, NULL, 'f' },
{ "in", required_argument, NULL, 'i' },
{ 0,0,0,0 }
};
while (TRUE) while (TRUE)
{ {
switch (getopt_long(argc, argv, "", long_opts, NULL)) switch (getopt_long(argc, argv, "", long_opts, NULL))
{ {
case 'h': case 'h':
return usage_pub(NULL); return usage(CMD_PUB, NULL);
case 't': case 't':
if (streq(optarg, "rsa")) if (streq(optarg, "rsa"))
{ {
@ -418,13 +421,13 @@ static int pub(int argc, char *argv[])
} }
else else
{ {
return usage_pub("invalid input type"); return usage(CMD_PUB, "invalid input type");
} }
continue; continue;
case 'f': case 'f':
if (!get_form(optarg, &form, TRUE)) if (!get_form(optarg, &form, TRUE))
{ {
return usage_pub("invalid output format"); return usage(CMD_PUB, "invalid output format");
} }
continue; continue;
case 'i': case 'i':
@ -433,7 +436,7 @@ static int pub(int argc, char *argv[])
case EOF: case EOF:
break; break;
default: default:
return usage_pub("invalid --pub option"); return usage(CMD_PUB, "invalid --pub option");
} }
break; break;
} }
@ -506,18 +509,12 @@ static int keyid(int argc, char *argv[])
void *cred; void *cred;
chunk_t id; chunk_t id;
struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "type", required_argument, NULL, 't' },
{ "in", required_argument, NULL, 'i' },
{ 0,0,0,0 }
};
while (TRUE) while (TRUE)
{ {
switch (getopt_long(argc, argv, "", long_opts, NULL)) switch (getopt_long(argc, argv, "", long_opts, NULL))
{ {
case 'h': case 'h':
return usage_keyid(NULL); return usage(CMD_KEYID, NULL);
case 't': case 't':
if (streq(optarg, "rsa-priv")) if (streq(optarg, "rsa-priv"))
{ {
@ -541,7 +538,7 @@ static int keyid(int argc, char *argv[])
} }
else else
{ {
return usage_keyid("invalid input type"); return usage(CMD_KEYID, "invalid input type");
} }
continue; continue;
case 'i': case 'i':
@ -550,7 +547,7 @@ static int keyid(int argc, char *argv[])
case EOF: case EOF:
break; break;
default: default:
return usage_keyid("invalid --keyid option"); return usage(CMD_KEYID, "invalid --keyid option");
} }
break; break;
} }
@ -639,20 +636,6 @@ static int self(int argc, char *argv[])
x509_flag_t flags = 0; x509_flag_t flags = 0;
options_t *options; options_t *options;
struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "options", required_argument, NULL, '+' },
{ "type", required_argument, NULL, 't' },
{ "in", required_argument, NULL, 'i' },
{ "dn", required_argument, NULL, 'd' },
{ "san", required_argument, NULL, 'a' },
{ "lifetime", required_argument, NULL, 'l' },
{ "serial", required_argument, NULL, 's' },
{ "digest", required_argument, NULL, 'g' },
{ "ca", no_argument, NULL, 'c' },
{ 0,0,0,0 }
};
options = options_create(); options = options_create();
san = linked_list_create(); san = linked_list_create();
@ -817,7 +800,7 @@ end:
usage: usage:
san->destroy_offset(san, offsetof(identification_t, destroy)); san->destroy_offset(san, offsetof(identification_t, destroy));
options->destroy(options); options->destroy(options);
return usage_self(error); return usage(CMD_SELF, error);
} }
/** /**
@ -841,22 +824,6 @@ static int issue(int argc, char *argv[])
x509_t *x509; x509_t *x509;
options_t *options; options_t *options;
struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "options", required_argument, NULL, '+' },
{ "type", required_argument, NULL, 't' },
{ "in", required_argument, NULL, 'i' },
{ "cacert", required_argument, NULL, 'c' },
{ "cakey", required_argument, NULL, 'k' },
{ "dn", required_argument, NULL, 'd' },
{ "san", required_argument, NULL, 'a' },
{ "lifetime", required_argument, NULL, 'l' },
{ "serial", required_argument, NULL, 's' },
{ "digest", required_argument, NULL, 'g' },
{ "ca", no_argument, NULL, 'b' },
{ 0,0,0,0 }
};
options = options_create(); options = options_create();
san = linked_list_create(); san = linked_list_create();
@ -1061,7 +1028,7 @@ end:
usage: usage:
san->destroy_offset(san, offsetof(identification_t, destroy)); san->destroy_offset(san, offsetof(identification_t, destroy));
options->destroy(options); options->destroy(options);
return usage_issue(error); return usage(CMD_ISSUE, error);
} }
/** /**
@ -1085,7 +1052,7 @@ static int verify(int argc, char *argv[])
switch (getopt_long(argc, argv, "", long_opts, NULL)) switch (getopt_long(argc, argv, "", long_opts, NULL))
{ {
case 'h': case 'h':
return usage_verify(NULL); return usage(CMD_VERIFY, NULL);
case 'i': case 'i':
file = optarg; file = optarg;
continue; continue;
@ -1095,7 +1062,7 @@ static int verify(int argc, char *argv[])
case EOF: case EOF:
break; break;
default: default:
return usage_verify("invalid --verify option"); return usage(CMD_VERIFY, "invalid --verify option");
} }
break; break;
} }
@ -1174,16 +1141,7 @@ static int verify(int argc, char *argv[])
*/ */
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct option long_opts[] = { int op, i;
{ "help", no_argument, NULL, 'h' },
{ "gen", no_argument, NULL, 'g' },
{ "pub", no_argument, NULL, 'p' },
{ "keyid", no_argument, NULL, 'k' },
{ "self", no_argument, NULL, 's' },
{ "issue", no_argument, NULL, 'i' },
{ "verify", no_argument, NULL, 'v' },
{ 0,0,0,0 }
};
atexit(library_deinit); atexit(library_deinit);
if (!library_init(STRONGSWAN_CONF)) if (!library_init(STRONGSWAN_CONF))
@ -1202,24 +1160,16 @@ int main(int argc, char *argv[])
exit(SS_RC_INITIALIZATION_FAILED); exit(SS_RC_INITIALIZATION_FAILED);
} }
switch (getopt_long(argc, argv, "", long_opts, NULL)) build_opts(CMD_HELP);
op = getopt_long(argc, argv, "", long_opts, NULL);
for (i = 0; i < CMD_MAX; i++)
{ {
case 'h': if (cmds[i].op == op)
return usage(NULL); {
case 'g': build_opts(i);
return gen(argc, argv); return cmds[i].call(argc, argv);
case 'p': }
return pub(argc, argv);
case 'k':
return keyid(argc, argv);
case 's':
return self(argc, argv);
case 'i':
return issue(argc, argv);
case 'v':
return verify(argc, argv);
default:
return usage("invalid operation");
} }
return usage(CMD_HELP, "invalid operation");
} }