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/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");
fprintf(out, " generate a new private key\n");
fprintf(out, " --type type of key, default: rsa\n");
fprintf(out, " --size keylength in bits, default: rsa 2048, ecdsa 384\n");
fprintf(out, " --outform encoding of generated private key\n");
int i;
memset(long_opts, 0, sizeof(long_opts));
if (cmd == CMD_HELP)
{
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");
fprintf(out, " extract the public key from a private key/certificate\n");
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");
}
FILE *out = stdout;
int i;
static void print_keyid(FILE *out)
{
fprintf(out, " pki --keyid [--in file] [--type rsa-priv|ecdsa-priv|pub|x509]\n");
fprintf(out, " calculate key identifiers of a key/certificate\n");
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)
{
if (error)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
}
fprintf(out, "strongSwan %s PKI tool\n", VERSION);
fprintf(out, "usage:\n");
fprintf(out, " pki%s --help\n", name);
fprintf(out, " show this usage information\n");
}
static int usage(char *error)
{
FILE *out = stdout;
if (error)
if (cmd == CMD_HELP)
{
out = stderr;
fprintf(out, "Error: %s\n", error);
for (i = 0; i < CMD_MAX; i++)
{
fprintf(out, " pki --%-6s %s\n", cmds[i].cmd, cmds[i].description);
}
}
print_version(out, "");
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)
else
{
out = stderr;
fprintf(out, "Error: %s\n", error);
for (i = 0; cmds[cmd].line[i]; i++)
{
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;
}
@ -275,6 +284,14 @@ static hash_algorithm_t get_digest(char *name)
return HASH_UNKNOWN;
}
/**
* Show usage information
*/
static int help(int argc, char *argv[])
{
return usage(CMD_HELP, NULL);
}
/**
* Generate a private key
*/
@ -286,19 +303,12 @@ static int gen(int argc, char *argv[])
private_key_t *key;
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)
{
switch (getopt_long(argc, argv, "", long_opts, NULL))
{
case 'h':
return usage_gen(NULL);
return usage(CMD_GEN, NULL);
case 't':
if (streq(optarg, "rsa"))
{
@ -310,26 +320,26 @@ static int gen(int argc, char *argv[])
}
else
{
return usage_gen("invalid key type");
return usage(CMD_GEN, "invalid key type");
}
continue;
case 'o':
if (!get_form(optarg, &form, FALSE))
{
return usage_gen("invalid key output format");
return usage(CMD_GEN, "invalid key output format");
}
continue;
case 's':
size = atoi(optarg);
if (!size)
{
return usage_gen("invalid key size");
return usage(CMD_GEN, "invalid key size");
}
continue;
case EOF:
break;
default:
return usage_gen("invalid --gen option");
return usage(CMD_GEN, "invalid --gen option");
}
break;
}
@ -387,19 +397,12 @@ static int pub(int argc, char *argv[])
char *file = NULL;
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)
{
switch (getopt_long(argc, argv, "", long_opts, NULL))
{
case 'h':
return usage_pub(NULL);
return usage(CMD_PUB, NULL);
case 't':
if (streq(optarg, "rsa"))
{
@ -418,13 +421,13 @@ static int pub(int argc, char *argv[])
}
else
{
return usage_pub("invalid input type");
return usage(CMD_PUB, "invalid input type");
}
continue;
case 'f':
if (!get_form(optarg, &form, TRUE))
{
return usage_pub("invalid output format");
return usage(CMD_PUB, "invalid output format");
}
continue;
case 'i':
@ -433,7 +436,7 @@ static int pub(int argc, char *argv[])
case EOF:
break;
default:
return usage_pub("invalid --pub option");
return usage(CMD_PUB, "invalid --pub option");
}
break;
}
@ -506,18 +509,12 @@ static int keyid(int argc, char *argv[])
void *cred;
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)
{
switch (getopt_long(argc, argv, "", long_opts, NULL))
{
case 'h':
return usage_keyid(NULL);
return usage(CMD_KEYID, NULL);
case 't':
if (streq(optarg, "rsa-priv"))
{
@ -541,7 +538,7 @@ static int keyid(int argc, char *argv[])
}
else
{
return usage_keyid("invalid input type");
return usage(CMD_KEYID, "invalid input type");
}
continue;
case 'i':
@ -550,7 +547,7 @@ static int keyid(int argc, char *argv[])
case EOF:
break;
default:
return usage_keyid("invalid --keyid option");
return usage(CMD_KEYID, "invalid --keyid option");
}
break;
}
@ -639,20 +636,6 @@ static int self(int argc, char *argv[])
x509_flag_t flags = 0;
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();
san = linked_list_create();
@ -817,7 +800,7 @@ end:
usage:
san->destroy_offset(san, offsetof(identification_t, destroy));
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;
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();
san = linked_list_create();
@ -1061,7 +1028,7 @@ end:
usage:
san->destroy_offset(san, offsetof(identification_t, destroy));
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))
{
case 'h':
return usage_verify(NULL);
return usage(CMD_VERIFY, NULL);
case 'i':
file = optarg;
continue;
@ -1095,7 +1062,7 @@ static int verify(int argc, char *argv[])
case EOF:
break;
default:
return usage_verify("invalid --verify option");
return usage(CMD_VERIFY, "invalid --verify option");
}
break;
}
@ -1174,16 +1141,7 @@ static int verify(int argc, char *argv[])
*/
int main(int argc, char *argv[])
{
struct option long_opts[] = {
{ "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 }
};
int op, i;
atexit(library_deinit);
if (!library_init(STRONGSWAN_CONF))
@ -1202,24 +1160,16 @@ int main(int argc, char *argv[])
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':
return usage(NULL);
case 'g':
return gen(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");
if (cmds[i].op == op)
{
build_opts(i);
return cmds[i].call(argc, argv);
}
}
return usage(CMD_HELP, "invalid operation");
}