Here's v6 of the patch. [x] Rebase on 20961ceaf0 [x] Don't call exit(1) after pg_fatal() [x] Use Tom Lane's suggestion for %lld in _() string [x] Allow full unsigned 16-bit range for ports (don't disallow ports 0-1023) [x] Explicitly cast parsed values to smaller integers
-- Joe Nelson https://begriffs.com
diff --git a/contrib/pg_standby/Makefile b/contrib/pg_standby/Makefile index 0bca2f8e9e..cb9292d0f4 100644 --- a/contrib/pg_standby/Makefile +++ b/contrib/pg_standby/Makefile @@ -6,6 +6,8 @@ PGAPPICON = win32 PROGRAM = pg_standby OBJS = pg_standby.o $(WIN32RES) +PG_LDFLAGS += -L$(top_builddir)/src/fe_utils -lpgfeutils + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c index 031b1b5cd5..c6405d8b94 100644 --- a/contrib/pg_standby/pg_standby.c +++ b/contrib/pg_standby/pg_standby.c @@ -33,6 +33,7 @@ #include "pg_getopt.h" #include "access/xlog_internal.h" +#include "fe_utils/option.h" const char *progname; @@ -678,6 +679,10 @@ main(int argc, char **argv) while ((c = getopt(argc, argv, "cdk:lr:s:t:w:")) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'c': /* Use copy */ @@ -687,12 +692,15 @@ main(int argc, char **argv) debug = true; break; case 'k': /* keepfiles */ - keepfiles = atoi(optarg); - if (keepfiles < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "%s: -k keepfiles must be >= 0\n", progname); + fprintf(stderr, "%s: invalid argument for -k keepfiles: %s\n", + progname, parse_error); exit(2); } + keepfiles = (int) parsed; break; case 'l': /* Use link */ @@ -706,31 +714,39 @@ main(int argc, char **argv) #endif break; case 'r': /* Retries */ - maxretries = atoi(optarg); - if (maxretries < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "%s: -r maxretries must be >= 0\n", progname); + fprintf(stderr, "%s: invalid argument for -r maxretries: %s\n", + progname, parse_error); exit(2); } + maxretries = (int) parsed; break; case 's': /* Sleep time */ - sleeptime = atoi(optarg); - if (sleeptime <= 0 || sleeptime > 60) + s = pg_strtoint64_range(optarg, &parsed, 1, 60, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "%s: -s sleeptime incorrectly set\n", progname); + fprintf(stderr, "%s: invalid argument for -s sleeptime: %s\n", + progname, parse_error); exit(2); } + sleeptime = (int) parsed; break; case 't': /* Trigger file */ triggerPath = pg_strdup(optarg); break; case 'w': /* Max wait time */ - maxwaittime = atoi(optarg); - if (maxwaittime < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "%s: -w maxwaittime incorrectly set\n", progname); + fprintf(stderr, "%s: invalid argument for -w maxwaittime: %s\n", + progname, parse_error); exit(2); } + maxwaittime = (int) parsed; break; default: fprintf(stderr, "Try \"%s --help\" for more information.\n", progname); diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 55ef13926d..2c0fbbc721 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -32,6 +32,7 @@ #include "common/logging.h" #include "common/string.h" #include "fe_utils/recovery_gen.h" +#include "fe_utils/option.h" #include "fe_utils/string_utils.h" #include "getopt_long.h" #include "libpq-fe.h" @@ -2073,6 +2074,10 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "CD:F:r:RS:T:X:l:nNzZ:d:c:h:p:U:s:wWkvP", long_options, &option_index)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'C': @@ -2157,12 +2162,13 @@ main(int argc, char **argv) #endif break; case 'Z': - compresslevel = atoi(optarg); - if (compresslevel < 0 || compresslevel > 9) + s = pg_strtoint64_range(optarg, &parsed, 0, 9, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid compression level \"%s\"", optarg); + pg_log_error("invalid compression level: %s", parse_error); exit(1); } + compresslevel = (int) parsed; break; case 'c': if (pg_strcasecmp(optarg, "fast") == 0) @@ -2195,12 +2201,14 @@ main(int argc, char **argv) dbgetpassword = 1; break; case 's': - standby_message_timeout = atoi(optarg) * 1000; - if (standby_message_timeout < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX / 1000, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid status interval \"%s\"", optarg); + pg_log_error("invalid status interval: %s", parse_error); exit(1); } + standby_message_timeout = (int) parsed * 1000; break; case 'v': verbose++; diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c index f39c1339d7..f967c11c44 100644 --- a/src/bin/pg_basebackup/pg_receivewal.c +++ b/src/bin/pg_basebackup/pg_receivewal.c @@ -21,6 +21,7 @@ #include "common/file_perm.h" #include "common/logging.h" +#include "fe_utils/option.h" #include "libpq-fe.h" #include "access/xlog_internal.h" #include "getopt_long.h" @@ -492,7 +493,6 @@ main(int argc, char **argv) {"no-sync", no_argument, NULL, 5}, {NULL, 0, NULL, 0} }; - int c; int option_index; char *db_name; @@ -521,6 +521,10 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "D:d:E:h:p:U:s:S:nwWvZ:", long_options, &option_index)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'D': @@ -533,11 +537,14 @@ main(int argc, char **argv) dbhost = pg_strdup(optarg); break; case 'p': - if (atoi(optarg) <= 0) + s = pg_strtoint64_range(optarg, &parsed, FE_UTILS_PORT_MIN, + FE_UTILS_PORT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid port number \"%s\"", optarg); + pg_log_error("invalid port number: %s", parse_error); exit(1); } + /* validated conversion above, but using the string */ dbport = pg_strdup(optarg); break; case 'U': @@ -550,12 +557,14 @@ main(int argc, char **argv) dbgetpassword = 1; break; case 's': - standby_message_timeout = atoi(optarg) * 1000; - if (standby_message_timeout < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX / 1000, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid status interval \"%s\"", optarg); + pg_log_error("invalid status interval: %s", parse_error); exit(1); } + standby_message_timeout = (int) parsed * 1000; break; case 'S': replication_slot = pg_strdup(optarg); @@ -575,12 +584,13 @@ main(int argc, char **argv) verbose++; break; case 'Z': - compresslevel = atoi(optarg); - if (compresslevel < 0 || compresslevel > 9) + s = pg_strtoint64_range(optarg, &parsed, 0, 9, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid compression level \"%s\"", optarg); + pg_log_error("invalid compression level: %s", parse_error); exit(1); } + compresslevel = (int) parsed; break; /* action */ case 1: diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c index af29dd7651..10103ebffc 100644 --- a/src/bin/pg_basebackup/pg_recvlogical.c +++ b/src/bin/pg_basebackup/pg_recvlogical.c @@ -26,6 +26,7 @@ #include "common/file_perm.h" #include "common/fe_memutils.h" #include "common/logging.h" +#include "fe_utils/option.h" #include "getopt_long.h" #include "libpq-fe.h" #include "libpq/pqsignal.h" @@ -705,6 +706,10 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "E:f:F:nvd:h:p:U:wWI:o:P:s:S:", long_options, &option_index)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { /* general options */ @@ -712,12 +717,14 @@ main(int argc, char **argv) outfile = pg_strdup(optarg); break; case 'F': - fsync_interval = atoi(optarg) * 1000; - if (fsync_interval < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX / 1000, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid fsync interval \"%s\"", optarg); + pg_log_error("invalid fsync interval: %s", parse_error); exit(1); } + fsync_interval = (int) parsed * 1000; break; case 'n': noloop = 1; @@ -733,11 +740,14 @@ main(int argc, char **argv) dbhost = pg_strdup(optarg); break; case 'p': - if (atoi(optarg) <= 0) + s = pg_strtoint64_range(optarg, &parsed, FE_UTILS_PORT_MIN, + FE_UTILS_PORT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid port number \"%s\"", optarg); + pg_log_error("invalid port number: %s", parse_error); exit(1); } + /* validated conversion above, but using the string */ dbport = pg_strdup(optarg); break; case 'U': @@ -790,12 +800,14 @@ main(int argc, char **argv) plugin = pg_strdup(optarg); break; case 's': - standby_message_timeout = atoi(optarg) * 1000; - if (standby_message_timeout < 0) + s = pg_strtoint64_range(optarg, &parsed, + 0, INT_MAX / 1000, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("invalid status interval \"%s\"", optarg); + pg_log_error("invalid status interval: %s", parse_error); exit(1); } + standby_message_timeout = (int) parsed * 1000; break; case 'S': replication_slot = pg_strdup(optarg); diff --git a/src/bin/pg_ctl/Makefile b/src/bin/pg_ctl/Makefile index 83cbf97ed8..f7d375f869 100644 --- a/src/bin/pg_ctl/Makefile +++ b/src/bin/pg_ctl/Makefile @@ -24,6 +24,7 @@ LDFLAGS_INTERNAL += $(libpq_pgport) SUBMAKE_LIBPQ := submake-libpq endif +LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils OBJS= pg_ctl.o $(WIN32RES) all: pg_ctl diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index eac4e771ab..b5ba9a86d2 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -28,6 +28,7 @@ #include "common/file_perm.h" #include "common/logging.h" #include "common/string.h" +#include "fe_utils/option.h" #include "getopt_long.h" #include "utils/pidfile.h" @@ -2298,6 +2299,10 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW", long_options, &option_index)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'D': @@ -2361,7 +2366,14 @@ main(int argc, char **argv) #endif break; case 't': - wait_seconds = atoi(optarg); + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) + { + write_stderr(_("invalid timeout: %s\n"), parse_error); + exit(1); + } + wait_seconds = (int) parsed; wait_seconds_arg = true; break; case 'U': diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index f01fea5b91..1bbf280aa8 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -62,6 +62,7 @@ #include "pg_backup_db.h" #include "pg_backup_utils.h" #include "pg_dump.h" +#include "fe_utils/option.h" #include "fe_utils/connect.h" #include "fe_utils/string_utils.h" @@ -430,6 +431,10 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "abBcCd:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:", long_options, &optindex)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'a': /* Dump data only */ @@ -473,7 +478,14 @@ main(int argc, char **argv) break; case 'j': /* number of dump jobs */ - numWorkers = atoi(optarg); + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) + { + pg_log_error("invalid job count: %s", parse_error); + exit_nicely(1); + } + numWorkers = (int) parsed; break; case 'n': /* include schema(s) */ @@ -536,12 +548,13 @@ main(int argc, char **argv) break; case 'Z': /* Compression Level */ - compressLevel = atoi(optarg); - if (compressLevel < 0 || compressLevel > 9) + s = pg_strtoint64_range(optarg, &parsed, 0, 9, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("compression level must be in range 0..9"); + pg_log_error("invalid compression level: %s", parse_error); exit_nicely(1); } + compressLevel = (int) parsed; break; case 0: @@ -574,12 +587,13 @@ main(int argc, char **argv) case 8: have_extra_float_digits = true; - extra_float_digits = atoi(optarg); - if (extra_float_digits < -15 || extra_float_digits > 3) + s = pg_strtoint64_range(optarg, &parsed, -15, 3, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("extra_float_digits must be in range -15..3"); + pg_log_error("invalid extra_float_digits: %s", parse_error); exit_nicely(1); } + extra_float_digits = (int) parsed; break; case 9: /* inserts */ diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index 40a6b3745c..db9ad10fe7 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -45,6 +45,7 @@ #include <termios.h> #endif +#include "fe_utils/option.h" #include "getopt_long.h" #include "dumputils.h" @@ -153,6 +154,10 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "acCd:ef:F:h:I:j:lL:n:N:Op:P:RsS:t:T:U:vwWx1", cmdopts, NULL)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'a': /* Dump data only */ @@ -183,7 +188,14 @@ main(int argc, char **argv) break; case 'j': /* number of restore jobs */ - numWorkers = atoi(optarg); + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) + { + pg_log_error("invalid job count: %s", parse_error); + exit_nicely(1); + } + numWorkers = (int) parsed; break; case 'l': /* Dump the TOC summary */ diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c index da9e1da78d..fd31a7c5e9 100644 --- a/src/bin/pg_upgrade/option.c +++ b/src/bin/pg_upgrade/option.c @@ -14,6 +14,7 @@ #include <io.h> #endif +#include "fe_utils/option.h" #include "getopt_long.h" #include "common/string.h" #include "utils/pidfile.h" @@ -106,6 +107,10 @@ parseCommandLine(int argc, char *argv[]) while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v", long_options, &optindex)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (option) { case 'b': @@ -129,7 +134,11 @@ parseCommandLine(int argc, char *argv[]) break; case 'j': - user_opts.jobs = atoi(optarg); + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) + pg_fatal("invalid job count: %s\n", parse_error); + user_opts.jobs = (int) parsed; break; case 'k': @@ -168,13 +177,19 @@ parseCommandLine(int argc, char *argv[]) * supported on all old/new versions (added in PG 9.2). */ case 'p': - if ((old_cluster.port = atoi(optarg)) <= 0) - pg_fatal("invalid old port number\n"); + s = pg_strtoint64_range(optarg, &parsed, FE_UTILS_PORT_MIN, + FE_UTILS_PORT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) + pg_fatal("invalid old port number: %s\n", parse_error); + old_cluster.port = (unsigned short) parsed; break; case 'P': - if ((new_cluster.port = atoi(optarg)) <= 0) - pg_fatal("invalid new port number\n"); + s = pg_strtoint64_range(optarg, &parsed, FE_UTILS_PORT_MIN, + FE_UTILS_PORT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) + pg_fatal("invalid new port number: %s\n", parse_error); + new_cluster.port = (unsigned short) parsed; break; case 'r': diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c index 25919885b1..a8fc36c25d 100644 --- a/src/bin/pgbench/pgbench.c +++ b/src/bin/pgbench/pgbench.c @@ -36,6 +36,7 @@ #include "common/logging.h" #include "common/string.h" #include "fe_utils/conditional.h" +#include "fe_utils/option.h" #include "getopt_long.h" #include "libpq-fe.h" #include "portability/instr_time.h" @@ -5327,6 +5328,9 @@ main(int argc, char **argv) while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) != -1) { char *script; + pg_strtoint_status s; + int64 parsed; + char *parse_error; switch (c) { @@ -5358,13 +5362,15 @@ main(int argc, char **argv) break; case 'c': benchmarking_option_set = true; - nclients = atoi(optarg); - if (nclients <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid number of clients: \"%s\"\n", - optarg); + fprintf(stderr, "invalid number of clients: %s\n", + parse_error); exit(1); } + nclients = (int) parsed; #ifdef HAVE_GETRLIMIT #ifdef RLIMIT_NOFILE /* most platforms use RLIMIT_NOFILE */ if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) @@ -5386,13 +5392,15 @@ main(int argc, char **argv) break; case 'j': /* jobs */ benchmarking_option_set = true; - nthreads = atoi(optarg); - if (nthreads <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid number of threads: \"%s\"\n", - optarg); + fprintf(stderr, "invalid number of threads: %s\n", + parse_error); exit(1); } + nthreads = (int) parsed; #ifndef ENABLE_THREAD_SAFETY if (nthreads != 1) { @@ -5411,31 +5419,37 @@ main(int argc, char **argv) break; case 's': scale_given = true; - scale = atoi(optarg); - if (scale <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid scaling factor: \"%s\"\n", optarg); + fprintf(stderr, "invalid scaling factor: %s\n", parse_error); exit(1); } + scale = (int) parsed; break; case 't': benchmarking_option_set = true; - nxacts = atoi(optarg); - if (nxacts <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid number of transactions: \"%s\"\n", - optarg); + fprintf(stderr, "invalid number of transactions: %s\n", + parse_error); exit(1); } + nxacts = (int) parsed; break; case 'T': benchmarking_option_set = true; - duration = atoi(optarg); - if (duration <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid duration: \"%s\"\n", optarg); + fprintf(stderr, "invalid duration: %s\n", parse_error); exit(1); } + duration = (int) parsed; break; case 'U': login = pg_strdup(optarg); @@ -5494,12 +5508,14 @@ main(int argc, char **argv) break; case 'F': initialization_option_set = true; - fillfactor = atoi(optarg); - if (fillfactor < 10 || fillfactor > 100) + s = pg_strtoint64_range(optarg, &parsed, + 10, 100, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid fillfactor: \"%s\"\n", optarg); + fprintf(stderr, "invalid fillfactor: %s\n", parse_error); exit(1); } + fillfactor = (int) parsed; break; case 'M': benchmarking_option_set = true; @@ -5515,13 +5531,15 @@ main(int argc, char **argv) break; case 'P': benchmarking_option_set = true; - progress = atoi(optarg); - if (progress <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid thread progress delay: \"%s\"\n", - optarg); + fprintf(stderr, "invalid thread progress delay: %s\n", + parse_error); exit(1); } + progress = (int) parsed; break; case 'R': { @@ -5576,13 +5594,15 @@ main(int argc, char **argv) break; case 5: /* aggregate-interval */ benchmarking_option_set = true; - agg_interval = atoi(optarg); - if (agg_interval <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - fprintf(stderr, "invalid number of seconds for aggregation: \"%s\"\n", - optarg); + fprintf(stderr, "invalid number of seconds for aggregation: %s\n", + parse_error); exit(1); } + agg_interval = (int) parsed; break; case 6: /* progress-timestamp */ progress_timestamp = true; diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c index f00aec15de..0dc5379bbf 100644 --- a/src/bin/scripts/reindexdb.c +++ b/src/bin/scripts/reindexdb.c @@ -15,6 +15,7 @@ #include "common.h" #include "common/logging.h" #include "fe_utils/connect.h" +#include "fe_utils/option.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" #include "scripts_parallel.h" @@ -105,6 +106,10 @@ main(int argc, char *argv[]) /* process command-line options */ while ((c = getopt_long(argc, argv, "h:p:U:wWeqS:d:ast:i:j:v", long_options, &optindex)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'h': @@ -147,12 +152,14 @@ main(int argc, char *argv[]) simple_string_list_append(&indexes, optarg); break; case 'j': - concurrentCons = atoi(optarg); - if (concurrentCons <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("number of parallel jobs must be at least 1"); + pg_log_error("invalid number of parallel jobs: %s", parse_error); exit(1); } + concurrentCons = (int) parsed; break; case 'v': verbose = true; diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c index 2c7219239f..ada5dd2f02 100644 --- a/src/bin/scripts/vacuumdb.c +++ b/src/bin/scripts/vacuumdb.c @@ -17,6 +17,7 @@ #include "common.h" #include "common/logging.h" #include "fe_utils/connect.h" +#include "fe_utils/option.h" #include "fe_utils/simple_list.h" #include "fe_utils/string_utils.h" #include "scripts_parallel.h" @@ -124,6 +125,10 @@ main(int argc, char *argv[]) while ((c = getopt_long(argc, argv, "h:p:U:wWeqd:zZFat:fvj:", long_options, &optindex)) != -1) { + pg_strtoint_status s; + int64 parsed; + char *parse_error; + switch (c) { case 'h': @@ -175,12 +180,14 @@ main(int argc, char *argv[]) vacopts.verbose = true; break; case 'j': - concurrentCons = atoi(optarg); - if (concurrentCons <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("number of parallel jobs must be at least 1"); + pg_log_error("invalid number of parallel jobs: %s", parse_error); exit(1); } + concurrentCons = (int) parsed; break; case 2: maintenance_db = pg_strdup(optarg); @@ -195,20 +202,24 @@ main(int argc, char *argv[]) vacopts.skip_locked = true; break; case 6: - vacopts.min_xid_age = atoi(optarg); - if (vacopts.min_xid_age <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("minimum transaction ID age must be at least 1"); + pg_log_error("invalid minimum transaction ID age: %s", parse_error); exit(1); } + vacopts.min_xid_age = (int) parsed; break; case 7: - vacopts.min_mxid_age = atoi(optarg); - if (vacopts.min_mxid_age <= 0) + s = pg_strtoint64_range(optarg, &parsed, + 1, INT_MAX, &parse_error); + if (s != PG_STRTOINT_OK) { - pg_log_error("minimum multixact ID age must be at least 1"); + pg_log_error("invalid minimum multixact ID age: %s", parse_error); exit(1); } + vacopts.min_mxid_age = (int) parsed; break; default: fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); diff --git a/src/fe_utils/Makefile b/src/fe_utils/Makefile index f2e516a2aa..83063abdcd 100644 --- a/src/fe_utils/Makefile +++ b/src/fe_utils/Makefile @@ -19,8 +19,8 @@ include $(top_builddir)/src/Makefile.global override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) -OBJS = conditional.o mbprint.o print.o psqlscan.o recovery_gen.o \ - simple_list.o string_utils.o +OBJS = conditional.o mbprint.o option.o print.o psqlscan.o \ + recovery_gen.o simple_list.o string_utils.o all: libpgfeutils.a diff --git a/src/fe_utils/option.c b/src/fe_utils/option.c new file mode 100644 index 0000000000..477081d044 --- /dev/null +++ b/src/fe_utils/option.c @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * option.c + * argument parsing helpers for frontend code + * + * Copyright (c) 2019, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/fe_utils/option.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include "fe_utils/option.h" + +pg_strtoint_status +pg_strtoint64_range(const char *str, int64 *result, + int64 min, int64 max, char **error) +{ + int64 temp; + pg_strtoint_status s = pg_strtoint64(str, &temp); + + if (s == PG_STRTOINT_OK && (temp < min || temp > max)) + s = PG_STRTOINT_RANGE_ERROR; + + switch (s) + { + case PG_STRTOINT_OK: + *result = temp; + break; + case PG_STRTOINT_SYNTAX_ERROR: + *error = psprintf(_("could not parse '%s' as integer"), str); + break; + case PG_STRTOINT_RANGE_ERROR: + *error = psprintf(_("%s is outside range %lld..%lld"), + str, (long long) min, (long long) max); + break; + default: + pg_unreachable(); + } + return s; +} diff --git a/src/include/fe_utils/option.h b/src/include/fe_utils/option.h new file mode 100644 index 0000000000..89511d30ef --- /dev/null +++ b/src/include/fe_utils/option.h @@ -0,0 +1,31 @@ +/* + * option.h + * argument parsing helpers for frontend code + * + * Copyright (c) 2019, PostgreSQL Global Development Group + * + * src/include/fe_utils/option.h + */ +#ifndef FE_OPTION_H +#define FE_OPTION_H + +#include <limits.h> + +#include "common/string.h" + +#define FE_UTILS_PORT_MIN 0 +#define FE_UTILS_PORT_MAX ((1 << 16) - 1) + +/* + * Parses string as int64 like pg_strtoint64, but fails + * with PG_STRTOINT_RANGE_ERROR if the result is outside + * the range min .. max inclusive. + * + * On failure, creates user-friendly error message with + * psprintf, and assigns it to the error output parameter. + */ +pg_strtoint_status +pg_strtoint64_range(const char *str, int64 *result, + int64 min, int64 max, char **error); + +#endif /* FE_OPTION_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 7a103e6140..a9a01d22b7 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -142,7 +142,7 @@ sub mkvcbuild our @pgcommonbkndfiles = @pgcommonallfiles; our @pgfeutilsfiles = qw( - conditional.c mbprint.c print.c psqlscan.l psqlscan.c + conditional.c mbprint.c option.c print.c psqlscan.l psqlscan.c simple_list.c string_utils.c recovery_gen.c); $libpgport = $solution->AddProject('libpgport', 'lib', 'misc');