summaryrefslogtreecommitdiffstats
path: root/src/util/attr_clnt.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/attr_clnt.c')
-rw-r--r--src/util/attr_clnt.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/src/util/attr_clnt.c b/src/util/attr_clnt.c
new file mode 100644
index 0000000..0ec0944
--- /dev/null
+++ b/src/util/attr_clnt.c
@@ -0,0 +1,285 @@
+/*++
+/* NAME
+/* attr_clnt 3
+/* SUMMARY
+/* attribute query-reply client
+/* SYNOPSIS
+/* #include <attr_clnt.h>
+/*
+/* typedef int (*ATTR_CLNT_PRINT_FN) (VSTREAM *, int, va_list);
+/* typedef int (*ATTR_CLNT_SCAN_FN) (VSTREAM *, int, va_list);
+/*
+/* ATTR_CLNT *attr_clnt_create(server, timeout, max_idle, max_ttl)
+/* const char *server;
+/* int timeout;
+/* int max_idle;
+/* int max_ttl;
+/*
+/* int attr_clnt_request(client,
+/* send_flags, send_type, send_name, ..., ATTR_TYPE_END,
+/* recv_flags, recv_type, recv_name, ..., ATTR_TYPE_END)
+/* ATTR_CLNT *client;
+/* int send_flags;
+/* int send_type;
+/* const char *send_name;
+/* int recv_flags;
+/* int recv_type;
+/* const char *recv_name;
+/*
+/* void attr_clnt_free(client)
+/* ATTR_CLNT *client;
+/*
+/* void attr_clnt_control(client, name, value, ... ATTR_CLNT_CTL_END)
+/* ATTR_CLNT *client;
+/* int name;
+/* DESCRIPTION
+/* This module implements a client for a simple attribute-based
+/* protocol. The default protocol is described in attr_scan_plain(3).
+/*
+/* attr_clnt_create() creates a client handle. See auto_clnt(3) for
+/* a description of the arguments.
+/*
+/* attr_clnt_request() sends the specified request attributes and
+/* receives a reply. The reply argument specifies a name-value table.
+/* The other arguments are as described in attr_print_plain(3). The
+/* result is the number of attributes received or -1 in case of trouble.
+/*
+/* attr_clnt_free() destroys a client handle and closes its connection.
+/*
+/* attr_clnt_control() allows the user to fine tune the behavior of
+/* the specified client. The arguments are a list of (name, value)
+/* terminated with ATTR_CLNT_CTL_END.
+/* The following lists the names and the types of the corresponding
+/* value arguments.
+/* .IP "ATTR_CLNT_CTL_PROTO(ATTR_CLNT_PRINT_FN, ATTR_CLNT_SCAN_FN)"
+/* Specifies alternatives for the attr_plain_print() and
+/* attr_plain_scan() functions.
+/* .IP "ATTR_CLNT_CTL_REQ_LIMIT(int)"
+/* The maximal number of requests per connection (default: 0,
+/* i.e. no limit). To enable the limit, specify a value greater
+/* than zero.
+/* .IP "ATTR_CLNT_CTL_TRY_LIMIT(int)"
+/* The maximal number of attempts to send a request before
+/* giving up (default: 2). To disable the limit, specify a
+/* value equal to zero.
+/* .IP "ATTR_CLNT_CTL_TRY_DELAY(int)"
+/* The time in seconds between attempts to send a request
+/* (default: 1). Specify a value greater than zero.
+/* DIAGNOSTICS
+/* Warnings: communication failure.
+/* SEE ALSO
+/* auto_clnt(3), client endpoint management
+/* attr_scan_plain(3), attribute protocol
+/* attr_print_plain(3), attribute protocol
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <attr.h>
+#include <iostuff.h>
+#include <compat_va_copy.h>
+#include <auto_clnt.h>
+#include <attr_clnt.h>
+
+/* Application-specific. */
+
+struct ATTR_CLNT {
+ AUTO_CLNT *auto_clnt;
+ /* Remaining properties are set with attr_clnt_control(). */
+ ATTR_CLNT_PRINT_FN print;
+ ATTR_CLNT_SCAN_FN scan;
+ int req_limit;
+ int req_count;
+ int try_limit;
+ int try_delay;
+};
+
+#define ATTR_CLNT_DEF_REQ_LIMIT (0) /* default per-session request limit */
+#define ATTR_CLNT_DEF_TRY_LIMIT (2) /* default request (re)try limit */
+#define ATTR_CLNT_DEF_TRY_DELAY (1) /* default request (re)try delay */
+
+/* attr_clnt_free - destroy attribute client */
+
+void attr_clnt_free(ATTR_CLNT *client)
+{
+ auto_clnt_free(client->auto_clnt);
+ myfree((void *) client);
+}
+
+/* attr_clnt_create - create attribute client */
+
+ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
+ int max_idle, int max_ttl)
+{
+ ATTR_CLNT *client;
+
+ client = (ATTR_CLNT *) mymalloc(sizeof(*client));
+ client->auto_clnt = auto_clnt_create(service, timeout, max_idle, max_ttl);
+ client->scan = attr_vscan_plain;
+ client->print = attr_vprint_plain;
+ client->req_limit = ATTR_CLNT_DEF_REQ_LIMIT;
+ client->req_count = 0;
+ client->try_limit = ATTR_CLNT_DEF_TRY_LIMIT;
+ client->try_delay = ATTR_CLNT_DEF_TRY_DELAY;
+ return (client);
+}
+
+/* attr_clnt_request - send query, receive reply */
+
+int attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
+{
+ const char *myname = "attr_clnt_request";
+ VSTREAM *stream;
+ int count = 0;
+ va_list saved_ap;
+ va_list ap;
+ int type;
+ int recv_flags;
+ int err;
+ int ret;
+
+ /*
+ * XXX If the stream is readable before we send anything, then assume the
+ * remote end disconnected.
+ *
+ * XXX For some reason we can't simply call the scan routine after the print
+ * routine, that messes up the argument list.
+ */
+#define SKIP_ARG(ap, type) { \
+ (void) va_arg(ap, char *); \
+ (void) va_arg(ap, type); \
+ }
+#define SKIP_ARG2(ap, t1, t2) { \
+ SKIP_ARG(ap, t1); \
+ (void) va_arg(ap, t2); \
+ }
+
+ /* Finalize argument lists before returning. */
+ va_start(saved_ap, send_flags);
+ for (;;) {
+ errno = 0;
+ if ((stream = auto_clnt_access(client->auto_clnt)) != 0
+ && readable(vstream_fileno(stream)) == 0) {
+ errno = 0;
+ VA_COPY(ap, saved_ap);
+ err = (client->print(stream, send_flags, ap) != 0
+ || vstream_fflush(stream) != 0);
+ va_end(ap);
+ if (err == 0) {
+ VA_COPY(ap, saved_ap);
+ while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
+ switch (type) {
+ case ATTR_TYPE_STR:
+ SKIP_ARG(ap, char *);
+ break;
+ case ATTR_TYPE_DATA:
+ SKIP_ARG2(ap, ssize_t, char *);
+ break;
+ case ATTR_TYPE_INT:
+ SKIP_ARG(ap, int);
+ break;
+ case ATTR_TYPE_LONG:
+ SKIP_ARG(ap, long);
+ break;
+ case ATTR_TYPE_HASH:
+ (void) va_arg(ap, HTABLE *);
+ break;
+ default:
+ msg_panic("%s: unexpected attribute type %d",
+ myname, type);
+ }
+ }
+ recv_flags = va_arg(ap, int);
+ ret = client->scan(stream, recv_flags, ap);
+ va_end(ap);
+ /* Finalize argument lists before returning. */
+ if (ret > 0) {
+ if (client->req_limit > 0
+ && (client->req_count += 1) >= client->req_limit) {
+ auto_clnt_recover(client->auto_clnt);
+ client->req_count = 0;
+ }
+ break;
+ }
+ }
+ }
+ if ((++count >= client->try_limit && client->try_limit > 0)
+ || msg_verbose
+ || (errno && errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
+ msg_warn("problem talking to server %s: %m",
+ auto_clnt_name(client->auto_clnt));
+ /* Finalize argument lists before returning. */
+ if (count >= client->try_limit && client->try_limit > 0) {
+ ret = -1;
+ break;
+ }
+ sleep(client->try_delay);
+ auto_clnt_recover(client->auto_clnt);
+ client->req_count = 0;
+ }
+ /* Finalize argument lists before returning. */
+ va_end(saved_ap);
+ return (ret);
+}
+
+/* attr_clnt_control - fine control */
+
+void attr_clnt_control(ATTR_CLNT *client, int name,...)
+{
+ const char *myname = "attr_clnt_control";
+ va_list ap;
+
+ for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
+ switch (name) {
+ case ATTR_CLNT_CTL_PROTO:
+ client->print = va_arg(ap, ATTR_CLNT_PRINT_FN);
+ client->scan = va_arg(ap, ATTR_CLNT_SCAN_FN);
+ break;
+ case ATTR_CLNT_CTL_REQ_LIMIT:
+ client->req_limit = va_arg(ap, int);
+ if (client->req_limit < 0)
+ msg_panic("%s: bad request limit: %d",
+ myname, client->req_limit);
+ if (msg_verbose)
+ msg_info("%s: new request limit %d",
+ myname, client->req_limit);
+ break;
+ case ATTR_CLNT_CTL_TRY_LIMIT:
+ client->try_limit = va_arg(ap, int);
+ if (client->try_limit < 0)
+ msg_panic("%s: bad retry limit: %d", myname, client->try_limit);
+ if (msg_verbose)
+ msg_info("%s: new retry limit %d", myname, client->try_limit);
+ break;
+ case ATTR_CLNT_CTL_TRY_DELAY:
+ client->try_delay = va_arg(ap, int);
+ if (client->try_delay <= 0)
+ msg_panic("%s: bad retry delay: %d", myname, client->try_delay);
+ if (msg_verbose)
+ msg_info("%s: new retry delay %d", myname, client->try_delay);
+ break;
+ default:
+ msg_panic("%s: bad name %d", myname, name);
+ }
+ }
+ va_end(ap);
+}