src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
src/time.o src/fd.o src/pipe.o src/regex.o src/cfgparse.o src/server.o \
src/checks.o src/queue.o src/frontend.o src/proxy.o src/peers.o \
- src/stick_table.o src/proto_uxst.o \
+ src/arg.o src/stick_table.o src/proto_uxst.o \
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
src/stream_interface.o src/dumpstats.o src/proto_tcp.o \
src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
src/ev_poll.o src/ev_kqueue.o \
- src/acl.o src/memory.o src/freq_ctr.o \
+ src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
src/auth.o src/stick_table.o src/pattern.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
src/session.o src/hdr_idx.o src/ev_select.o src/signal.o \
src/lb_chash.o src/lb_fwlc.o src/lb_fwrr.o src/lb_map.o src/lb_fas.o \
src/ev_poll.o \
- src/acl.o src/memory.o src/freq_ctr.o \
+ src/arg.o src/acl.o src/memory.o src/freq_ctr.o \
src/auth.o src/stick_table.o src/pattern.o
EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o \
--- /dev/null
+/*
+ * include/proto/arg.h
+ * This file contains functions and macros declarations for generic argument parsing.
+ *
+ * Copyright 2012 Willy Tarreau <w@1wt.eu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _PROTO_ARG_H
+#define _PROTO_ARG_H
+
+#include <types/arg.h>
+
+/* Some macros used to build some arg list. We can declare various argument
+ * combinations from 0 to 7 args using a single 32-bit integer. The first
+ * argument of these macros is always the mandatory number of arguments, and
+ * remaining ones are optional args. Note: ARGM() may also be used to return
+ * the number of mandatory arguments in a mask.
+ */
+#define ARGM(m) \
+ (m & 15)
+
+#define ARG1(m, t1) \
+ (ARGM(m) + (ARGT_##t1 << 4))
+
+#define ARG2(m, t1, t2) \
+ (ARG1(m, t1) + (ARGT_##t2 << 8))
+
+#define ARG3(m, t1, t2, t3) \
+ (ARG2(m, t1, t2) + (ARGT_##t3 << 12))
+
+#define ARG4(m, t1, t2, t3, t4) \
+ (ARG3(m, t1, t2, t3) + (ARGT_##t4 << 16))
+
+#define ARG5(m, t1, t2, t3, t4, t5) \
+ (ARG4(m, t1, t2, t3, t4) + (ARGT_##t5 << 20))
+
+#define ARG6(m, t1, t2, t3, t4, t5, t6) \
+ (ARG5(m, t1, t2, t3, t4, t5) + (ARGT_##t6 << 24))
+
+#define ARG7(m, t1, t2, t3, t4, t5, t6, t7) \
+ (ARG6(m, t1, t2, t3, t4, t5, t6) + (ARGT_##t7 << 28))
+
+int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
+ char **err_msg, const char **err_ptr, int *err_arg);
+
+#endif /* _PROTO_ARG_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
--- /dev/null
+/*
+ * include/types/arg.h
+ * This file contains structure declarations for generaic argument parsing.
+ *
+ * Copyright 2012 Willy Tarreau <w@1wt.eu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _TYPES_ARG_H
+#define _TYPES_ARG_H
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <types/buffers.h>
+
+enum {
+ ARGT_STOP = 0, /* end of the arg list */
+ ARGT_UINT, /* unsigned integer, which is a positive integer without any sign */
+ ARGT_SINT, /* signed integer, the sign (+/-) was explicit. Falls back to UINT if no sign. */
+ ARGT_STR, /* string */
+ ARGT_IPV4, /* an IPv4 address */
+ ARGT_MSK4, /* an IPv4 address mask (integer or dotted), stored as ARGT_IPV4 */
+ ARGT_IPV6, /* an IPv6 address */
+ ARGT_MSK6, /* an IPv6 address mask (integer or dotted), stored as ARGT_IPV4 */
+ ARGT_TIME, /* a delay in ms by default, stored as ARGT_UINT */
+ ARGT_SIZE, /* a size in bytes by default, stored as ARGT_UINT */
+ ARGT_FE, /* a pointer to a frontend only */
+ ARGT_BE, /* a pointer to a backend only */
+ ARGT_TAB, /* a pointer to a stick table */
+ ARGT_SRV, /* a pointer to a server */
+ ARGT_USR, /* a pointer to a user list */
+ ARGT_UNASSIGNED15, /* will probably be used for variables later */
+ ARGT_NBTYPES /* no more values past 15 */
+};
+
+/* some types that are externally defined */
+struct proxy;
+struct server;
+struct userlist;
+
+union arg_data {
+ unsigned int uint; /* used for uint, time, size */
+ int sint;
+ struct chunk str;
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
+ struct proxy *prx; /* used for fe, be, tables */
+ struct server *srv;
+ struct userlist *usr;
+};
+
+struct arg {
+ int type; /* argument type */
+ union arg_data data; /* argument data */
+};
+
+
+#endif /* _TYPES_ARG_H */
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * End:
+ */
--- /dev/null
+/*
+ * Functions used to parse typed argument lists
+ *
+ * Copyright 2012 Willy Tarreau <w@1wt.eu>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <common/standard.h>
+#include <proto/arg.h>
+
+static const char *arg_type_names[ARGT_NBTYPES] = {
+ [ARGT_STOP] = "end of arguments",
+ [ARGT_UINT] = "unsigned integer",
+ [ARGT_SINT] = "signed integer",
+ [ARGT_STR] = "string",
+ [ARGT_IPV4] = "IPv4 address",
+ [ARGT_MSK4] = "IPv4 mask",
+ [ARGT_IPV6] = "IPv6 address",
+ [ARGT_MSK6] = "IPv6 mask",
+ [ARGT_TIME] = "delay",
+ [ARGT_SIZE] = "size",
+ [ARGT_FE] = "frontend",
+ [ARGT_BE] = "backend",
+ [ARGT_TAB] = "table",
+ [ARGT_SRV] = "server",
+ [ARGT_USR] = "user list",
+ /* Unassigned types must never happen. Better crash during parsing if they do. */
+};
+
+/* This function builds an argument list from a config line. It returns the
+ * number of arguments found, or <0 in case of any error. Everything needed
+ * it automatically allocated. A pointer to an error message might be returned
+ * in err_msg if not NULL, in which case it would be allocated and the caller
+ * will have to check it and free it. The output arg list is returned in argp
+ * which must be valid. The returned array is always terminated by an arg of
+ * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
+ * The mask is composed of a number of mandatory arguments in its lower 4 bits,
+ * and a concatenation of each argument type in each subsequent 4-bit block. If
+ * <err_msg> is not NULL, it must point to a freeable or NULL pointer.
+ */
+int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
+ char **err_msg, const char **err_ptr, int *err_arg)
+{
+ int nbarg;
+ int pos;
+ struct arg *arg, *arg_list = NULL;
+ const char *beg;
+ char *word = NULL;
+ const char *ptr_err = NULL;
+ int min_arg;
+
+ min_arg = mask & 15;
+ mask >>= 4;
+
+ pos = 0;
+ /* find between 0 and 8 the max number of args supported by the mask */
+ for (nbarg = 0; nbarg < 8 && ((mask >> (nbarg * 4)) & 0xF); nbarg++);
+
+ if (!nbarg)
+ goto end_parse;
+
+ /* Note: an empty input string contains an empty argument if this argument
+ * is marked mandatory. Otherwise we can ignore it.
+ */
+ if (!len && !min_arg)
+ goto end_parse;
+
+ arg = arg_list = calloc(nbarg + 1, sizeof(*arg));
+
+ /* Note: empty arguments after a comma always exist. */
+ while (pos < nbarg) {
+ beg = in;
+ while (len && *in != ',') {
+ in++;
+ len--;
+ }
+
+ /* we have a new argument between <beg> and <in> (not included).
+ * For ease of handling, we copy it into a zero-terminated word.
+ * By default, the output argument will be the same type of the
+ * expected one.
+ */
+ free(word);
+ word = my_strndup(beg, in - beg);
+
+ arg->type = (mask >> (pos * 4)) & 15;
+
+ switch (arg->type) {
+ case ARGT_SINT:
+ if (in == beg) // empty number
+ goto parse_err;
+ else if (*beg < '0' || *beg > '9') {
+ arg->data.sint = strl2uic(beg + 1, in - beg - 1);
+ if (*beg == '-')
+ arg->data.sint = -arg->data.sint;
+ else if (*beg != '+') // invalid first character
+ goto parse_err;
+ break;
+ }
+
+ arg->type = ARGT_UINT;
+ /* fall through ARGT_UINT if no sign is present */
+
+ case ARGT_UINT:
+ if (in == beg) // empty number
+ goto parse_err;
+
+ arg->data.uint = strl2uic(beg, in - beg);
+ break;
+
+ case ARGT_FE:
+ case ARGT_BE:
+ case ARGT_TAB:
+ case ARGT_SRV:
+ case ARGT_USR:
+ case ARGT_STR:
+ /* all types that must be resolved are stored as strings
+ * during the parsing. The caller must at one point resolve
+ * them and free the string.
+ */
+ arg->data.str.str = word;
+ arg->data.str.len = in - beg;
+ arg->data.str.size = arg->data.str.len + 1;
+ word = NULL;
+ break;
+
+ case ARGT_IPV4:
+ if (in == beg) // empty address
+ goto parse_err;
+
+ if (inet_pton(AF_INET, word, &arg->data.ipv4) <= 0)
+ goto parse_err;
+ break;
+
+ case ARGT_MSK4:
+ if (in == beg) // empty mask
+ goto parse_err;
+
+ if (!str2mask(word, &arg->data.ipv4))
+ goto parse_err;
+
+ arg->type = ARGT_IPV4;
+ break;
+
+ case ARGT_IPV6:
+ if (in == beg) // empty address
+ goto parse_err;
+
+ if (inet_pton(AF_INET6, word, &arg->data.ipv6) <= 0)
+ goto parse_err;
+ break;
+
+ case ARGT_MSK6: /* not yet implemented */
+ goto parse_err;
+
+ case ARGT_TIME:
+ if (in == beg) // empty time
+ goto parse_err;
+
+ ptr_err = parse_time_err(word, &arg->data.uint, TIME_UNIT_MS);
+ if (ptr_err)
+ goto parse_err;
+
+ arg->type = ARGT_UINT;
+ break;
+
+ case ARGT_SIZE:
+ if (in == beg) // empty size
+ goto parse_err;
+
+ ptr_err = parse_size_err(word, &arg->data.uint);
+ if (ptr_err)
+ goto parse_err;
+
+ arg->type = ARGT_UINT;
+ break;
+
+ /* FIXME: other types need to be implemented here */
+ default:
+ goto parse_err;
+ }
+
+ pos++;
+ arg++;
+
+ /* don't go back to parsing if we reached end */
+ if (!len || pos >= nbarg)
+ break;
+
+ /* skip comma */
+ in++; len--;
+ }
+
+ end_parse:
+ free(word); word = NULL;
+
+ if (pos < min_arg) {
+ /* not enough arguments */
+ if (err_msg)
+ memprintf(err_msg,
+ "Missing arguments (got %d/%d), type '%s' expected",
+ pos, min_arg, arg_type_names[(mask >> (pos * 4)) & 15]);
+ goto err;
+ }
+
+ if (len) {
+ /* too many arguments, starting at <in> */
+ if (err_msg) {
+ /* the caller is responsible for freeing this message */
+ word = my_strndup(in, len);
+ memprintf(err_msg, "End of arguments expected at '%s'", word);
+ free(word); word = NULL;
+ }
+ goto err;
+ }
+
+ /* note that pos might be < nbarg and this is not an error, it's up to the
+ * caller to decide what to do with optional args.
+ */
+ *argp = arg_list;
+
+ if (err_arg)
+ *err_arg = pos;
+ if (err_ptr)
+ *err_ptr = in;
+ return pos;
+
+ parse_err:
+ if (err_msg) {
+ memprintf(err_msg, "Failed to parse '%s' as type '%s'",
+ word, arg_type_names[(mask >> (pos * 4)) & 15]);
+ }
+
+ err:
+ free(word);
+ free(arg_list);
+ if (err_arg)
+ *err_arg = pos;
+ if (err_ptr)
+ *err_ptr = in;
+ return -1;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "proto/arg.h"
+
+int main(int argc, char **argv)
+{
+ int nbargs, err_arg, mask;
+ struct arg *argp;
+ char *err_msg = NULL;
+ const char *err_ptr = NULL;
+
+ if (argc < 2) {
+ printf("Usage: %s arg_list [arg_mask]\n"
+ " mask defaults to 0x86543290\n"
+ " eg: %s 10k,+20,Host,1.2.3.4,24,::5.6.7.8,120s\n", *argv, *argv);
+ return 1;
+ }
+
+ mask = ARG7(0,SIZE,SINT,STR,IPV4,MSK4,IPV6,TIME);
+ if (argc >= 3)
+ mask = atoll(argv[2]);
+
+ printf("Using mask=0x%08x\n", mask);
+ nbargs = make_arg_list(argv[1], strlen(argv[1]), mask,
+ &argp, &err_msg, &err_ptr, &err_arg);
+
+ printf("nbargs=%d\n", nbargs);
+ if (nbargs < 0) {
+ printf("err_msg=%s\n", err_msg); free(err_msg);
+ printf("err_ptr=%s (str+%d)\n", err_ptr, err_ptr - argv[1]);
+ printf("err_arg=%d\n", err_arg);
+ return 1;
+ }
+
+ if (nbargs > 0) {
+ int arg;
+
+ for (arg = 0; arg < nbargs; arg++)
+ printf("arg %d: type=%d, int=0x%08x\n",
+ arg, argp[arg].type, *(int*)&argp[arg].data.uint);
+ }
+ return 0;
+}