Refactor commandline arg helpers to not exit on failure

Have the clopts_common helpers return success/fail (true/false) and have the desired return value passed in as an argument.  This allows failures to be handled by the calling applications and have a "cleaner" exit on failure.
This commit is contained in:
Michael Mann 2025-04-18 13:10:42 -04:00
parent e2ac9079a9
commit 7b048f64a9
10 changed files with 235 additions and 124 deletions

View File

@ -5094,7 +5094,10 @@ set_80211_channel(const char *iface, const char *opt)
}
if (options[0])
freq = get_nonzero_uint32(options[0], "802.11 channel frequency");
if (!get_nonzero_uint32(options[0], "802.11 channel frequency", &freq)) {
ret = EINVAL;
goto out;
}
if (args >= 1 && options[1]) {
type = ws80211_str_to_chan_type(options[1]);
@ -5106,10 +5109,16 @@ set_80211_channel(const char *iface, const char *opt)
}
if (args >= 2 && options[2])
center_freq1 = get_nonzero_uint32(options[2], "VHT center frequency");
if (!get_nonzero_uint32(options[2], "VHT center frequency", &center_freq1)) {
ret = EINVAL;
goto out;
}
if (args >= 3 && options[3])
center_freq2 = get_nonzero_uint32(options[3], "VHT center frequency 2");
if (!get_nonzero_uint32(options[3], "VHT center frequency 2", &center_freq2)) {
ret = EINVAL;
goto out;
}
ret = ws80211_set_freq(iface, freq, type, center_freq1, center_freq2);
@ -5667,10 +5676,12 @@ main(int argc, char *argv[])
machine_readable = true;
break;
case 'C':
pcap_queue_byte_limit = get_positive_int(ws_optarg, "byte_limit");
if (!get_positive_int64(ws_optarg, "byte_limit", &pcap_queue_byte_limit))
arg_error = true;
break;
case 'N':
pcap_queue_packet_limit = get_positive_int(ws_optarg, "packet_limit");
if (!get_positive_int64(ws_optarg, "packet_limit", &pcap_queue_packet_limit))
arg_error = true;
break;
default:
/* wslog arguments are okay */

View File

@ -302,7 +302,8 @@ add_selection(char *sel, uint64_t* max_selection)
fprintf(stderr, "Not inclusive ...");
selectfrm[max_selected].inclusive = false;
selectfrm[max_selected].first = get_uint64(sel, "packet number");
if (!get_uint64(sel, "packet number", &selectfrm[max_selected].first))
return false;
if (selectfrm[max_selected].first > *max_selection)
*max_selection = selectfrm[max_selected].first;
@ -315,8 +316,10 @@ add_selection(char *sel, uint64_t* max_selection)
*locn = '\0'; /* split the range */
next = locn + 1;
selectfrm[max_selected].inclusive = true;
selectfrm[max_selected].first = get_uint64(sel, "beginning of packet range");
selectfrm[max_selected].second = get_uint64(next, "end of packet range");
if (!get_uint64(sel, "beginning of packet range", &selectfrm[max_selected].first))
return false;
if (!get_uint64(next, "end of packet range", &selectfrm[max_selected].second))
return false;
if (selectfrm[max_selected].second == 0)
{
@ -1695,7 +1698,10 @@ main(int argc, char *argv[])
}
case 'c':
split_packet_count = get_nonzero_uint64(ws_optarg, "packet count");
if (!get_nonzero_uint64(ws_optarg, "packet count", &split_packet_count)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
break;
case 'C':
@ -1743,7 +1749,10 @@ main(int argc, char *argv[])
case 'D':
dup_detect = true;
dup_detect_by_time = false;
dup_window = get_uint32(ws_optarg, "duplicate window");
if (!get_uint32(ws_optarg, "duplicate window", &dup_window)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
if (dup_window > MAX_DUP_DEPTH) {
cmdarg_err("\"%d\" duplicate window value must be between 0 and %d inclusive.",
dup_window, MAX_DUP_DEPTH);
@ -1779,7 +1788,11 @@ main(int argc, char *argv[])
case 'i': /* break capture file based on time interval */
{
double spb = get_positive_double(ws_optarg, "time interval");
double spb;
if (!get_positive_double(ws_optarg, "time interval", &spb)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
if (spb == 0.0) {
cmdarg_err("The specified interval is zero");
ret = WS_EXIT_INVALID_OPTION;
@ -1794,7 +1807,10 @@ main(int argc, char *argv[])
break;
case 'I': /* ignored_bytes at the beginning of the frame for duplications removal */
ignored_bytes = get_uint32(ws_optarg, "number of bytes to ignore");
if (!get_uint32(ws_optarg, "number of bytes to ignore", &ignored_bytes)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
break;
case 'L':
@ -1802,7 +1818,10 @@ main(int argc, char *argv[])
break;
case 'o':
change_offset = get_uint32(ws_optarg, "change offset");
if (!get_uint32(ws_optarg, "change offset", &change_offset)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
break;
case 'r':
@ -1853,7 +1872,10 @@ main(int argc, char *argv[])
}
case 's':
snaplen = get_nonzero_uint32(ws_optarg, "snapshot length");
if (!get_nonzero_uint32(ws_optarg, "snapshot length", &snaplen)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
break;
case 'S':

View File

@ -281,7 +281,10 @@ main(int argc, char *argv[])
break;
case 's':
snaplen = get_nonzero_uint32(ws_optarg, "snapshot length");
if (!get_nonzero_uint32(ws_optarg, "snapshot length", &snaplen)) {
status = false;
goto clean_exit;
}
break;
case 'V':

View File

@ -165,7 +165,10 @@ main(int argc, char *argv[])
while ((opt = ws_getopt_long(argc, argv, optstring, long_options, NULL)) != -1) {
switch (opt) {
case 'b': /* max bytes */
produce_max_bytes = get_positive_int(ws_optarg, "max bytes");
if (!get_positive_int(ws_optarg, "max bytes", &produce_max_bytes)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
if (produce_max_bytes > 65536) {
cmdarg_err("max bytes is > 65536");
ret = WS_EXIT_INVALID_OPTION;
@ -174,7 +177,10 @@ main(int argc, char *argv[])
break;
case 'c': /* count */
produce_count = get_positive_int(ws_optarg, "count");
if (!get_positive_int(ws_optarg, "count", &produce_count)) {
ret = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
break;
case 'F':

View File

@ -572,10 +572,9 @@ main(int argc, char *argv[])
break;
#if !defined(_WIN32) && defined(RLIMIT_AS)
case 'm':
limit.rlim_cur = get_positive_int(ws_optarg, "memory limit");
limit.rlim_max = get_positive_int(ws_optarg, "memory limit");
if(setrlimit(RLIMIT_AS, &limit) != 0) {
if (!get_uint32(ws_optarg, "memory limit", (uint32_t*)(&limit.rlim_cur)) ||
!get_uint32(ws_optarg, "memory limit", (uint32_t*)(&limit.rlim_max)) ||
(setrlimit(RLIMIT_AS, &limit) != 0)) {
cmdarg_err("setrlimit(RLIMIT_AS) failed: %s",
g_strerror(errno));
ret = WS_EXIT_INVALID_OPTION;

View File

@ -1079,7 +1079,7 @@ main(int argc, char *argv[])
char *err_str, *err_str_secondary;
#else
bool capture_option_specified = false;
volatile int max_packet_count = 0;
int max_packet_count = 0;
#endif
volatile int out_file_type = WTAP_FILE_TYPE_SUBTYPE_UNKNOWN;
volatile bool out_file_name_res = false;
@ -1428,7 +1428,9 @@ main(int argc, char *argv[])
cmdarg_err("-M does not support two-pass analysis.");
arg_error=true;
}
epan_auto_reset_count = get_positive_int(ws_optarg, "epan reset count");
if (!get_positive_int(ws_optarg, "epan reset count", &epan_auto_reset_count))
arg_error = true;
epan_auto_reset = true;
break;
case 'a': /* autostop criteria */
@ -1466,7 +1468,10 @@ main(int argc, char *argv[])
goto clean_exit;
}
#else
max_packet_count = get_positive_int(ws_optarg, "packet count");
if (!get_positive_int(ws_optarg, "packet count", &max_packet_count)) {
exit_status = WS_EXIT_INVALID_OPTION;
goto clean_exit;
}
#endif
break;
case 'w': /* Write to file x */

View File

@ -331,20 +331,25 @@ set_autostop_criterion(capture_options *capture_opts, const char *autostoparg)
}
if (strcmp(autostoparg,"duration") == 0) {
capture_opts->has_autostop_duration = true;
capture_opts->autostop_duration = get_positive_double(p,"autostop duration");
if (!get_positive_double(p,"autostop duration",&capture_opts->autostop_duration))
return false;
} else if (strcmp(autostoparg,"filesize") == 0) {
capture_opts->has_autostop_filesize = true;
capture_opts->autostop_filesize = get_nonzero_uint32(p,"autostop filesize");
if (!get_nonzero_uint32(p,"autostop filesize",&capture_opts->autostop_filesize))
return false;
} else if (strcmp(autostoparg,"files") == 0) {
capture_opts->multi_files_on = true;
capture_opts->has_autostop_files = true;
capture_opts->autostop_files = get_positive_int(p,"autostop files");
if (!get_positive_int(p,"autostop files",&capture_opts->autostop_files))
return false;
} else if (strcmp(autostoparg,"packets") == 0) {
capture_opts->has_autostop_written_packets = true;
capture_opts->autostop_written_packets = get_positive_int(p,"packet write count");
if (!get_positive_int(p,"packet write count",&capture_opts->autostop_written_packets))
return false;
} else if (strcmp(autostoparg,"events") == 0) {
capture_opts->has_autostop_written_packets = true;
capture_opts->autostop_written_packets = get_positive_int(p,"event write count");
if (!get_positive_int(p,"event write count",&capture_opts->autostop_written_packets))
return false;
} else {
return false;
}
@ -468,25 +473,33 @@ get_ring_arguments(capture_options *capture_opts, const char *arg)
if (strcmp(arg,"files") == 0) {
capture_opts->has_ring_num_files = true;
capture_opts->ring_num_files = get_nonzero_uint32(p, "number of ring buffer files");
if (!get_nonzero_uint32(p, "number of ring buffer files",&capture_opts->ring_num_files))
return false;
} else if (strcmp(arg,"filesize") == 0) {
capture_opts->has_autostop_filesize = true;
capture_opts->autostop_filesize = get_nonzero_uint32(p, "ring buffer filesize");
if (!get_nonzero_uint32(p, "ring buffer filesize",&capture_opts->autostop_filesize))
return false;
} else if (strcmp(arg,"duration") == 0) {
capture_opts->has_file_duration = true;
capture_opts->file_duration = get_positive_double(p, "ring buffer duration");
if (!get_positive_double(p, "ring buffer duration",&capture_opts->file_duration))
return false;
} else if (strcmp(arg,"interval") == 0) {
capture_opts->has_file_interval = true;
capture_opts->file_interval = get_positive_int(p, "ring buffer interval");
if (!get_positive_int(p, "ring buffer interval",&capture_opts->file_interval))
return false;
} else if (strcmp(arg,"nametimenum") == 0) {
int val = get_positive_int(p, "file name: time before num");
int val;
if (!get_positive_int(p, "file name: time before num", &val))
return false;
capture_opts->has_nametimenum = (val > 1);
} else if (strcmp(arg,"packets") == 0) {
capture_opts->has_file_packets = true;
capture_opts->file_packets = get_positive_int(p, "ring buffer packet count");
if (!get_positive_int(p, "ring buffer packet count",&capture_opts->file_packets))
return false;
} else if (strcmp(arg,"events") == 0) {
capture_opts->has_file_packets = true;
capture_opts->file_packets = get_positive_int(p, "ring buffer event count");
if (!get_positive_int(p, "ring buffer event count",&capture_opts->file_packets))
return false;
} else if (strcmp(arg,"printname") == 0) {
capture_opts->print_file_names = true;
capture_opts->print_name_to = g_strdup(p);
@ -530,10 +543,12 @@ get_sampling_arguments(capture_options *capture_opts, const char *arg)
interface_opts = &g_array_index(capture_opts->ifaces, interface_options, capture_opts->ifaces->len - 1);
interface_opts->sampling_method = CAPTURE_SAMP_BY_COUNT;
interface_opts->sampling_param = get_positive_int(p, "sampling count");
if (!get_positive_int(p, "sampling count", &interface_opts->sampling_param))
return false;
} else {
capture_opts->default_options.sampling_method = CAPTURE_SAMP_BY_COUNT;
capture_opts->default_options.sampling_param = get_positive_int(p, "sampling count");
if (!get_positive_int(p, "sampling count", &capture_opts->default_options.sampling_param))
return false;
}
} else if (strcmp(arg, "timer") == 0) {
if (capture_opts->ifaces->len > 0) {
@ -541,10 +556,12 @@ get_sampling_arguments(capture_options *capture_opts, const char *arg)
interface_opts = &g_array_index(capture_opts->ifaces, interface_options, capture_opts->ifaces->len - 1);
interface_opts->sampling_method = CAPTURE_SAMP_BY_TIMER;
interface_opts->sampling_param = get_positive_int(p, "sampling timer");
if (!get_positive_int(p, "sampling timer", &interface_opts->sampling_param))
return false;
} else {
capture_opts->default_options.sampling_method = CAPTURE_SAMP_BY_TIMER;
capture_opts->default_options.sampling_param = get_positive_int(p, "sampling timer");
if (!get_positive_int(p, "sampling timer", &capture_opts->default_options.sampling_param))
return false;
}
}
*colonp = ':';
@ -987,15 +1004,18 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_
interface_options *interface_opts;
interface_opts = &g_array_index(capture_opts->ifaces, interface_options, capture_opts->ifaces->len - 1);
interface_opts->buffer_size = get_positive_int(optarg_str_p, "buffer size");
if (!get_positive_int(optarg_str_p, "buffer size", &interface_opts->buffer_size))
return 1;
} else {
capture_opts->default_options.buffer_size = get_positive_int(optarg_str_p, "buffer size");
if (!get_positive_int(optarg_str_p, "buffer size", &capture_opts->default_options.buffer_size))
return 1;
}
break;
case 'c': /* Capture n packets */
/* XXX Use set_autostop_criterion instead? */
capture_opts->has_autostop_packets = true;
capture_opts->autostop_packets = get_positive_int(optarg_str_p, "packet count");
if (!get_positive_int(optarg_str_p, "packet count", &capture_opts->autostop_packets))
return 1;
break;
case 'f': /* capture filter */
get_filter_arguments(capture_opts, optarg_str_p);
@ -1089,7 +1109,8 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_
#endif
case 's': /* Set the snapshot (capture) length */
// XXX Should we error out if our flavor is Stratoshark?
snaplen = get_natural_int(optarg_str_p, "snapshot length");
if (!get_natural_int(optarg_str_p, "snapshot length", &snaplen))
return 1;
/*
* Make a snapshot length of 0 equivalent to the maximum packet
* length, mirroring what tcpdump does.
@ -1195,7 +1216,8 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_
capture_opts->temp_dir = g_strdup(optarg_str_p);
break;
case LONGOPT_UPDATE_INTERVAL: /* capture update interval */
capture_opts->update_interval = get_natural_int(optarg_str_p, "update interval");
if (!get_natural_int(optarg_str_p, "update interval", &capture_opts->update_interval))
return false;
break;
default:
/* the caller is responsible to send us only the right opt's */

View File

@ -607,7 +607,8 @@ void commandline_other_options(int argc, char *argv[], bool opt_reset)
global_commandline_info.jump_backwards = SD_BACKWARD;
break;
case 'g': /* Go to item with the given item number */
global_commandline_info.go_to_packet = get_nonzero_uint32(ws_optarg, "go to packet");
if (!get_nonzero_uint32(ws_optarg, "go to packet", &global_commandline_info.go_to_packet))
exit_application(WS_EXIT_INVALID_OPTION);
break;
case 'J': /* Jump to the first item which matches the filter criteria */
global_commandline_info.jfilter = ws_optarg;

View File

@ -14,127 +14,155 @@
#include <stdlib.h>
#include <errno.h>
#include <ws_exit_codes.h>
#include <wsutil/strtoi.h>
#include <wsutil/cmdarg_err.h>
int
get_natural_int(const char *string, const char *name)
bool
get_natural_int(const char *string, const char *name, int32_t* number)
{
int32_t number;
if (!ws_strtoi32(string, NULL, &number)) {
if (!ws_strtoi32(string, NULL, number)) {
if (errno == EINVAL) {
cmdarg_err("The specified %s \"%s\" isn't a decimal number", name, string);
exit(1);
return false;
}
if (number < 0) {
if (*number < 0) {
cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
exit(1);
return false;
}
cmdarg_err("The specified %s \"%s\" is too large (greater than %d)",
name, string, number);
exit(1);
name, string, *number);
return false;
}
if (number < 0) {
if (*number < 0) {
cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
exit(1);
return false;
}
return (int)number;
return true;
}
int
get_positive_int(const char *string, const char *name)
bool
get_positive_int(const char *string, const char *name, int32_t* number)
{
int number;
if (!get_natural_int(string, name, number))
return false;
number = get_natural_int(string, name);
if (number == 0) {
if (*number == 0) {
cmdarg_err("The specified %s is zero", name);
exit(1);
return false;
}
return number;
return true;
}
uint32_t
get_uint32(const char *string, const char *name)
bool
get_natural_int64(const char* string, const char* name, int64_t* number)
{
uint32_t number;
if (!ws_strtoi64(string, NULL, number)) {
if (errno == EINVAL) {
cmdarg_err("The specified %s \"%s\" isn't a decimal number", name, string);
return false;
}
if (*number < 0) {
cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
return false;
}
cmdarg_err("The specified %s \"%s\" is too large (greater than %" PRId64 ")",
name, string, *number);
return false;
}
if (*number < 0) {
cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
return false;
}
return true;
}
if (!ws_strtou32(string, NULL, &number)) {
bool
get_positive_int64(const char* string, const char* name, int64_t* number)
{
if (!get_natural_int64(string, name, number))
return false;
if (*number == 0) {
cmdarg_err("The specified %s is zero", name);
return false;
}
return true;
}
bool
get_uint32(const char *string, const char *name, uint32_t* number)
{
if (!ws_strtou32(string, NULL, number)) {
if (errno == EINVAL) {
cmdarg_err("The specified %s \"%s\" isn't a decimal number", name, string);
exit(1);
return false;
}
cmdarg_err("The specified %s \"%s\" is too large (greater than %d)",
name, string, number);
exit(1);
name, string, *number);
return false;
}
return number;
return true;
}
uint32_t
get_nonzero_uint32(const char *string, const char *name)
bool
get_nonzero_uint32(const char *string, const char *name, uint32_t* number)
{
uint32_t number;
if (!get_uint32(string, name, number))
return false;
number = get_uint32(string, name);
if (number == 0) {
if (*number == 0) {
cmdarg_err("The specified %s is zero", name);
exit(1);
return false;
}
return number;
return true;
}
uint64_t
get_uint64(const char *string, const char *name)
bool
get_uint64(const char *string, const char *name, uint64_t* number)
{
uint64_t number;
if (!ws_strtou64(string, NULL, &number)) {
if (!ws_strtou64(string, NULL, number)) {
if (errno == EINVAL) {
cmdarg_err("The specified %s \"%s\" isn't a decimal number", name, string);
exit(1);
return false;
}
cmdarg_err("The specified %s \"%s\" is too large (greater than %" PRIu64 ")",
name, string, number);
exit(1);
name, string, *number);
return false;
}
return number;
return true;
}
uint64_t
get_nonzero_uint64(const char *string, const char *name)
bool
get_nonzero_uint64(const char *string, const char *name, uint64_t* number)
{
uint64_t number;
if (!get_uint64(string, name, number))
return false;
number = get_uint64(string, name);
if (number == 0) {
if (*number == 0) {
cmdarg_err("The specified %s is zero", name);
exit(1);
return false;
}
return number;
return true;
}
double
get_positive_double(const char *string, const char *name)
bool
get_positive_double(const char *string, const char *name, double* number)
{
double number = g_ascii_strtod(string, NULL);
*number = g_ascii_strtod(string, NULL);
if (errno == EINVAL) {
cmdarg_err("The specified %s \"%s\" isn't a floating point number", name, string);
exit(1);
return false;
}
if (number < 0.0) {
if (*number < 0.0) {
cmdarg_err("The specified %s \"%s\" is a negative number", name, string);
exit(1);
return false;
}
return number;
return true;
}

View File

@ -40,34 +40,48 @@ extern "C" {
#define OPTSTRING_READ_CAPTURE_COMMON \
"r:"
WS_DLL_PUBLIC int
get_natural_int(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_natural_int(const char *string, const char *name, int32_t* number);
WS_DLL_PUBLIC int
get_positive_int(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_positive_int(const char *string, const char *name, int32_t* number);
WS_DLL_PUBLIC uint32_t
get_uint32(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_natural_int64(const char* string, const char* name, int64_t* number);
WS_DLL_PUBLIC bool
get_positive_int64(const char* string, const char* name, int64_t* number);
WS_DLL_PUBLIC bool
get_uint32(const char *string, const char *name, uint32_t* number);
WS_DEPRECATED_X("Use get_uint32 instead")
static inline uint32_t
get_guint32(const char *string, const char *name) { return get_uint32(string, name); }
get_guint32(const char *string, const char *name) {
uint32_t number = 0;
get_uint32(string, name, &number);
return number;
}
WS_DLL_PUBLIC uint32_t
get_nonzero_uint32(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_nonzero_uint32(const char *string, const char *name, uint32_t* number);
WS_DEPRECATED_X("Use get_nonzero_uint32 instead")
static inline uint32_t
get_nonzero_guint32(const char *string, const char *name) { return get_nonzero_uint32(string, name); }
get_nonzero_guint32(const char *string, const char *name) {
uint32_t number = 0;
get_nonzero_uint32(string, name, &number);
return number;
}
WS_DLL_PUBLIC uint64_t
get_uint64(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_uint64(const char *string, const char *name, uint64_t* number);
WS_DLL_PUBLIC uint64_t
get_nonzero_uint64(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_nonzero_uint64(const char *string, const char *name, uint64_t* number);
WS_DLL_PUBLIC double
get_positive_double(const char *string, const char *name);
WS_DLL_PUBLIC bool
get_positive_double(const char *string, const char *name, double* number);
#ifdef __cplusplus
}