From 0907c31db34713f0e74b90f725fabb5c91c5f45f Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Wed, 23 May 2012 23:27:42 +0200 Subject: Allow saving to/restoring from a file without shell redirection Mathieu Bridon suggested that in some environments where there is no access to a full shell with input/output redirection, it'd be useful to read from/write to directly a file (bugzilla #788). The patch adds the new "-file" option to specify a filename to print into when listing/saving sets or read from when restoring sets. --- Make_global.am | 3 ++- include/libipset/session.h | 2 ++ lib/Makefile.am | 2 +- lib/libipset.map | 5 ++++ lib/session.c | 13 ++++++++++ src/ipset.8 | 27 +++++++++++++++----- src/ipset.c | 61 +++++++++++++++++++++++++++++++++++++++++++--- src/ui.c | 39 +++++++++++++++++++++++++++++ 8 files changed, 141 insertions(+), 11 deletions(-) diff --git a/Make_global.am b/Make_global.am index 24a1a34..4a8f61a 100644 --- a/Make_global.am +++ b/Make_global.am @@ -68,7 +68,8 @@ # as -version-info 2:0:0. This release has a new, but backwards incompatible # interface. -LIBVERSION = 2:1:0 +# curr:rev:age +LIBVERSION = 3:0:1 AM_CPPFLAGS = $(kinclude_CFLAGS) $(all_includes) -I$(top_srcdir)/include \ -I/usr/local/include diff --git a/include/libipset/session.h b/include/libipset/session.h index 467bb2f..988233e 100644 --- a/include/libipset/session.h +++ b/include/libipset/session.h @@ -97,6 +97,8 @@ extern int ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, typedef int (*ipset_outfn)(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); +extern int ipset_session_outfn(struct ipset_session *session, + ipset_outfn outfn); extern struct ipset_session *ipset_session_init(ipset_outfn outfn); extern int ipset_session_fini(struct ipset_session *session); diff --git a/lib/Makefile.am b/lib/Makefile.am index fd853dd..392b5f9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -19,7 +19,7 @@ lib_LTLIBRARIES = libipset.la include $(top_srcdir)/lib/Make_extra.am -libipset_la_LDFLAGS = -Wl,--version-script=$(top_srcdir)/lib/libipset.map -version-info $(LIBVERSION) +libipset_la_LDFLAGS = -Wl,--version-script=$(top_srcdir)/lib/libipset.map -version-number $(LIBVERSION) libipset_la_LIBADD = ${libmnl_LIBS} $(IPSET_SETTYPE_STATIC_OBJECTS) libipset_la_SOURCES = \ data.c \ diff --git a/lib/libipset.map b/lib/libipset.map index fd6b8c0..74370b2 100644 --- a/lib/libipset.map +++ b/lib/libipset.map @@ -117,3 +117,8 @@ global: ipset_parse_timeout; ipset_data_test_ignored; } LIBIPSET_1.0; + +LIBIPSET_3.0 { +global: + ipset_session_outfn; +} LIBIPSET_2.0; diff --git a/lib/session.c b/lib/session.c index 6700ea1..3803bfa 100644 --- a/lib/session.c +++ b/lib/session.c @@ -1940,6 +1940,19 @@ cleanup: return ret; } +/** + * ipset_session_outfn - set session output printing function + * + * Set the session printing function. + * + */ +int +ipset_session_outfn(struct ipset_session *session, ipset_outfn outfn) +{ + session->outfn = outfn ? outfn : printf; + return 0; +} + /** * ipset_session_init - initialize an ipset session * diff --git a/src/ipset.8 b/src/ipset.8 index a2ee39f..d140f3d 100644 --- a/src/ipset.8 +++ b/src/ipset.8 @@ -13,7 +13,7 @@ .\" You should have received a copy of the GNU General Public License .\" along with this program; if not, write to the Free Software .\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -.TH "IPSET" "8" "Oct 15, 2010" "Jozsef Kadlecsik" "" +.TH "IPSET" "8" "May 23, 2012" "Jozsef Kadlecsik" "" .SH "NAME" ipset \(em administration tool for IP sets .SH "SYNOPSIS" @@ -21,7 +21,7 @@ ipset \(em administration tool for IP sets .PP COMMANDS := { \fBcreate\fR | \fBadd\fR | \fBdel\fR | \fBtest\fR | \fBdestroy\fR | \fBlist\fR | \fBsave\fR | \fBrestore\fR | \fBflush\fR | \fBrename\fR | \fBswap\fR | \fBhelp\fR | \fBversion\fR | \fB\-\fR } .PP -\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR } +\fIOPTIONS\fR := { \fB\-exist\fR | \fB\-output\fR { \fBplain\fR | \fBsave\fR | \fBxml\fR } | \fB\-quiet\fR | \fB\-resolve\fR | \fB\-sorted\fR | \fB\-name\fR | \fB\-terse\fR | \fB\-file\fR \fIfilename\fR } .PP \fBipset\fR \fBcreate\fR \fISETNAME\fR \fITYPENAME\fR [ \fICREATE\-OPTIONS\fR ] .PP @@ -126,18 +126,25 @@ If the option \fB\-name\fR is specified, just the names of the existing sets are listed. If the option \fB\-terse\fR -is specified, just the set names and headers are listed. +is specified, just the set names and headers are listed. The output is printed +to stdout, the option +\fB\-file\fR +can be used to specify a filename instead of stdout. .TP \fBsave\fP [ \fISETNAME\fP ] Save the given set, or all sets if none is given to stdout in a format that \fBrestore\fP -can read. +can read. The option +\fB\-file\fR +can be used to specify a filename instead of stdout. .TP \fBrestore\fP Restore a saved session generated by \fBsave\fP. -The saved session can be fed from stdin. +The saved session can be fed from stdin or the option +\fB\-file\fR +can be used to specify a filename instead of stdin. .TP \fBflush\fP [ \fISETNAME\fP ] Flush all entries from the specified set or flush @@ -201,7 +208,15 @@ List just the names of the existing sets, i.e. suppress listing of set headers a .TP \fB\-t\fP, \fB\-terse\fP List the set names and headers, i.e. suppress listing of set members. - +.TP +\fB\-f\fP, \fB\-file\fP \fIfilename\fR +Specidy a filename to print into instead of stdout +(\fBlist\fR +or +\fBsave\fR +commands) or read from instead of stdin +(\fBrestore\fR +command). .SH "SET TYPES" A set type comprises of the storage method by which the data is stored and the data type(s) which are stored in the set. Therefore the diff --git a/src/ipset.c b/src/ipset.c index 4a20ca1..8e5411f 100644 --- a/src/ipset.c +++ b/src/ipset.c @@ -6,7 +6,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include /* assert */ #include /* isspace */ +#include /* errno */ #include /* va_* */ #include /* bool */ #include /* fprintf, fgets */ @@ -32,6 +34,8 @@ static bool interactive; static char cmdline[1024]; static char *newargv[255]; static int newargc; +static FILE *fd = NULL; +static const char *filename = NULL; enum exittype { NO_PROBLEM = 0, @@ -74,6 +78,8 @@ exit_error(int status, const char *msg, ...) ipset_session_fini(session); D("status: %u", status); + if (fd) + fclose(fd); exit(status > VERSION_PROBLEM ? OTHER_PROBLEM : status); /* Unreached */ return -1; @@ -92,6 +98,8 @@ handle_error(void) if (!interactive) { ipset_session_fini(session); + if (fd) + fclose(fd); exit(OTHER_PROBLEM); } @@ -120,6 +128,32 @@ help(void) } } +int +ipset_parse_file(struct ipset_session *session UNUSED, + int opt UNUSED, const char *str) +{ + if (filename != NULL) + return exit_error(PARAMETER_PROBLEM, + "-file option can be specified once"); + filename = str; + + return 0; +} + +int __attribute__ ((format (printf, 1, 2))) +ipset_print_file(const char *fmt, ...) +{ + int len; + va_list args; + + assert(fd != NULL); + va_start(args, fmt); + len = vfprintf(fd, fmt, args); + va_end(args); + + return len; +} + /* Build faked argv from parsed line */ static void build_argv(char *buffer) @@ -156,12 +190,22 @@ restore(char *argv0) { int ret = 0; char *c; + FILE *fread = stdin; /* Initialize newargv/newargc */ newargc = 0; newargv[newargc++] = argv0; + if (filename) { + fd = fopen(filename, "r"); + if (!fd) { + return exit_error(OTHER_PROBLEM, + "Cannot open %s for reading: %s", + filename, strerror(errno)); + } + fread = fd; + } - while (fgets(cmdline, sizeof(cmdline), stdin)) { + while (fgets(cmdline, sizeof(cmdline), fread)) { restore_line++; c = cmdline; while (isspace(c[0])) @@ -624,10 +668,19 @@ parse_commandline(int argc, char *argv[]) check_allowed(type, cmd); break; - case IPSET_CMD_DESTROY: - case IPSET_CMD_FLUSH: case IPSET_CMD_LIST: case IPSET_CMD_SAVE: + if (filename != NULL) { + fd = fopen(filename, "w"); + if (!fd) + return exit_error(OTHER_PROBLEM, + "Cannot open %s for writing: " + "%s", filename, + strerror(errno)); + ipset_session_outfn(session, ipset_print_file); + } + case IPSET_CMD_DESTROY: + case IPSET_CMD_FLUSH: /* Args: [setname] */ if (arg0) { ret = ipset_parse_setname(session, @@ -721,6 +774,8 @@ main(int argc, char *argv[]) ret = parse_commandline(argc, argv); ipset_session_fini(session); + if (fd) + fclose(fd); return ret; } diff --git a/src/ui.c b/src/ui.c index 7e40bde..0ebacbb 100644 --- a/src/ui.c +++ b/src/ui.c @@ -154,6 +154,37 @@ ipset_match_cmd(const char *arg, const char * const name[]) (name[1] != NULL && tolower(arg[0]) == name[1][0]); } +/* Used up so far + * + * -A add + * -D del + * -E rename + * -f -file + * -F flush + * -h help + * -H help + * -L list + * -n -name + * -N create + * -o -output + * -r -resolve + * -R restore + * -s -sorted + * -S save + * -t -terse + * -T test + * -q -quiet + * -X destroy + * -v version + * -V version + * -W swap + * -! -exist + */ + +int +ipset_parse_file(struct ipset_session *session, + int opt, const char *str); + const struct ipset_envopts ipset_envopts[] = { { .name = { "-o", "-output" }, .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, @@ -202,6 +233,14 @@ const struct ipset_envopts ipset_envopts[] = { " When listing, list setnames and set headers\n" " from kernel only.", }, + { .name = { "-f", "-file" }, + .parse = ipset_parse_file, + .has_arg = IPSET_MANDATORY_ARG, .flag = IPSET_OPT_MAX, + .help = "\n" + " Read from the given file instead of standard\n" + " input (restore) or write to given file instead\n" + " of standard output (list/save).", + }, { }, }; -- cgit v1.2.3