/*++ /* NAME /* attr_clnt 3 /* SUMMARY /* attribute query-reply client /* SYNOPSIS /* #include /* /* typedef int (*ATTR_CLNT_PRINT_FN) (VSTREAM *, int, va_list); /* typedef int (*ATTR_CLNT_SCAN_FN) (VSTREAM *, int, va_list); /* typedef int (*ATTR_CLNT_HANDSHAKE_FN) (VSTREAM *); /* /* 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. /* .IP "ATTR_CLNT_CTL_HANDSHAKE(VSTREAM *)" /* A pointer to function that will be called at the start of a /* new connection, and that returns 0 in case of success. /* 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 /* /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* System library. */ #include #include #include /* Utility library. */ #include #include #include #include #include #include #include #include #include /* 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_HANDSHAKE: auto_clnt_control(client->auto_clnt, AUTO_CLNT_CTL_HANDSHAKE, va_arg(ap, ATTR_CLNT_HANDSHAKE_FN), AUTO_CLNT_CTL_END); 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); }