242 lines
5.1 KiB
C
242 lines
5.1 KiB
C
/*
|
|
* Copyright (C) 2012 Nominum, Inc.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
* documentation for any purpose with or without fee is hereby granted,
|
|
* provided that the above copyright notice and this permission notice
|
|
* appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <isc/file.h>
|
|
#include <isc/parseint.h>
|
|
#include <isc/result.h>
|
|
|
|
#include "log.h"
|
|
#include "opt.h"
|
|
#include "util.h"
|
|
|
|
#define MAX_OPTS 64
|
|
#define LINE_LENGTH 80
|
|
|
|
typedef struct {
|
|
char c;
|
|
perf_opttype_t type;
|
|
const char *desc;
|
|
const char *help;
|
|
const char *defval;
|
|
char defvalbuf[32];
|
|
union {
|
|
void *valp;
|
|
char **stringp;
|
|
isc_boolean_t *boolp;
|
|
unsigned int *uintp;
|
|
isc_uint64_t *uint64p;
|
|
double *doublep;
|
|
in_port_t *portp;
|
|
} u;
|
|
} opt_t;
|
|
|
|
static opt_t opts[MAX_OPTS];
|
|
static unsigned int nopts;
|
|
static char optstr[MAX_OPTS * 2 + 2];
|
|
static const char *progname;
|
|
|
|
void
|
|
perf_opt_add(char c, perf_opttype_t type, const char *desc, const char *help,
|
|
const char *defval, void *valp)
|
|
{
|
|
opt_t *opt;
|
|
char s[3];
|
|
|
|
if (nopts == MAX_OPTS)
|
|
perf_log_fatal("too many defined options");
|
|
opt = &opts[nopts++];
|
|
opt->c = c;
|
|
opt->type = type;
|
|
opt->desc = desc;
|
|
opt->help = help;
|
|
if (defval != NULL) {
|
|
strncpy(opt->defvalbuf, defval, sizeof(opt->defvalbuf));
|
|
opt->defval = opt->defvalbuf;
|
|
} else {
|
|
opt->defval = NULL;
|
|
}
|
|
opt->u.valp = valp;
|
|
|
|
sprintf(s, "%c%s", c, (type == perf_opt_boolean ? "" : ":"));
|
|
strcat(optstr, s);
|
|
}
|
|
|
|
void
|
|
perf_opt_usage(void)
|
|
{
|
|
unsigned int prefix_len, position, arg_len, i, j;
|
|
|
|
prefix_len = fprintf(stderr, "Usage: %s", progname);
|
|
position = prefix_len;
|
|
for (i = 0; i < nopts; i++) {
|
|
arg_len = 6;
|
|
if (opts[i].desc != NULL)
|
|
arg_len += strlen(opts[i].desc) + 1;
|
|
if (LINE_LENGTH - position - 1 < arg_len) {
|
|
fprintf(stderr, "\n");
|
|
for (j = 0; j < prefix_len; j++)
|
|
fprintf(stderr, " ");
|
|
position = prefix_len;
|
|
}
|
|
fprintf(stderr, " [-%c", opts[i].c);
|
|
if (opts[i].desc != NULL)
|
|
fprintf(stderr, " %s", opts[i].desc);
|
|
fprintf(stderr, "]");
|
|
position += arg_len;
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
for (i = 0; i < nopts; i++) {
|
|
fprintf(stderr, " -%c %s", opts[i].c, opts[i].help);
|
|
if (opts[i].defval)
|
|
fprintf(stderr, " (default: %s)", opts[i].defval);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
}
|
|
|
|
static isc_uint32_t
|
|
parse_uint(const char *desc, const char *str,
|
|
unsigned int min, unsigned int max)
|
|
{
|
|
isc_uint32_t val;
|
|
isc_result_t result;
|
|
|
|
val = 0;
|
|
result = isc_parse_uint32(&val, str, 10);
|
|
if (result != ISC_R_SUCCESS || val < min || val > max) {
|
|
fprintf(stderr, "invalid %s: %s\n", desc, str);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static isc_uint64_t
|
|
parse_timeval(const char *desc, const char *str)
|
|
{
|
|
const char *s;
|
|
char c;
|
|
isc_boolean_t seen_dot = ISC_FALSE;
|
|
|
|
s = str;
|
|
while (*s != 0) {
|
|
c = *s++;
|
|
if (c == '.') {
|
|
if (seen_dot)
|
|
goto fail;
|
|
seen_dot = ISC_TRUE;
|
|
} else if (c < '0' || c > '9') {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
return MILLION * atof(str);
|
|
fail:
|
|
fprintf(stderr, "invalid %s: %s\n", desc, str);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
static double
|
|
parse_double(const char *desc, const char *str) {
|
|
char c;
|
|
int index = 0;
|
|
isc_boolean_t seen_dot = ISC_FALSE;
|
|
|
|
while (str[index] != 0) {
|
|
c = str[index];
|
|
if (c == '.') {
|
|
if (seen_dot)
|
|
goto fail;
|
|
seen_dot = ISC_TRUE;
|
|
} else if (c < '0' || c > '9') {
|
|
goto fail;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
return atof(str);
|
|
fail:
|
|
fprintf(stderr, "invalid %s: %s\n", desc, str);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
perf_opt_parse(int argc, char **argv)
|
|
{
|
|
int c;
|
|
opt_t *opt;
|
|
isc_result_t result;
|
|
unsigned int i;
|
|
|
|
progname = isc_file_basename(argv[0]);
|
|
|
|
perf_opt_add('h', perf_opt_boolean, NULL, "print this help",
|
|
NULL, NULL);
|
|
|
|
while ((c = getopt(argc, argv, optstr)) != -1) {
|
|
for (i = 0; i < nopts; i++) {
|
|
if (opts[i].c == c)
|
|
break;
|
|
}
|
|
if (i == nopts) {
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
if (c == 'h') {
|
|
perf_opt_usage();
|
|
exit(0);
|
|
}
|
|
opt = &opts[i];
|
|
result = ISC_R_SUCCESS;
|
|
switch (opt->type) {
|
|
case perf_opt_string:
|
|
*opt->u.stringp = optarg;
|
|
break;
|
|
case perf_opt_boolean:
|
|
*opt->u.boolp = ISC_TRUE;
|
|
break;
|
|
case perf_opt_uint:
|
|
*opt->u.uintp = parse_uint(opt->desc, optarg,
|
|
1, 0xFFFFFFFF);
|
|
break;
|
|
case perf_opt_timeval:
|
|
*opt->u.uint64p = parse_timeval(opt->desc, optarg);
|
|
break;
|
|
case perf_opt_double:
|
|
*opt->u.doublep = parse_double(opt->desc, optarg);
|
|
break;
|
|
case perf_opt_port:
|
|
*opt->u.portp = parse_uint(opt->desc, optarg,
|
|
0, 0xFFFF);
|
|
break;
|
|
}
|
|
}
|
|
if (optind != argc) {
|
|
fprintf(stderr, "unexpected argument %s\n", argv[optind]);
|
|
perf_opt_usage();
|
|
exit(1);
|
|
}
|
|
}
|