Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: attr_clnt.c,v 1.3 2022/10/08 16:12:50 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	attr_clnt 3
      6 /* SUMMARY
      7 /*	attribute query-reply client
      8 /* SYNOPSIS
      9 /*	#include <attr_clnt.h>
     10 /*
     11 /*	typedef int (*ATTR_CLNT_PRINT_FN) (VSTREAM *, int, va_list);
     12 /*	typedef int (*ATTR_CLNT_SCAN_FN) (VSTREAM *, int, va_list);
     13 /*	typedef int (*ATTR_CLNT_HANDSHAKE_FN) (VSTREAM *);
     14 /*
     15 /*	ATTR_CLNT *attr_clnt_create(server, timeout, max_idle, max_ttl)
     16 /*	const char *server;
     17 /*	int	timeout;
     18 /*	int	max_idle;
     19 /*	int	max_ttl;
     20 /*
     21 /*	int	attr_clnt_request(client,
     22 /*			send_flags, send_type, send_name, ..., ATTR_TYPE_END,
     23 /*			recv_flags, recv_type, recv_name, ..., ATTR_TYPE_END)
     24 /*	ATTR_CLNT *client;
     25 /*	int	send_flags;
     26 /*	int	send_type;
     27 /*	const char *send_name;
     28 /*	int	recv_flags;
     29 /*	int	recv_type;
     30 /*	const char *recv_name;
     31 /*
     32 /*	void	attr_clnt_free(client)
     33 /*	ATTR_CLNT *client;
     34 /*
     35 /*	void	attr_clnt_control(client, name, value, ... ATTR_CLNT_CTL_END)
     36 /*	ATTR_CLNT *client;
     37 /*	int	name;
     38 /* DESCRIPTION
     39 /*	This module implements a client for a simple attribute-based
     40 /*	protocol. The default protocol is described in attr_scan_plain(3).
     41 /*
     42 /*	attr_clnt_create() creates a client handle. See auto_clnt(3) for
     43 /*	a description of the arguments.
     44 /*
     45 /*	attr_clnt_request() sends the specified request attributes and
     46 /*	receives a reply. The reply argument specifies a name-value table.
     47 /*	The other arguments are as described in attr_print_plain(3). The
     48 /*	result is the number of attributes received or -1 in case of trouble.
     49 /*
     50 /*	attr_clnt_free() destroys a client handle and closes its connection.
     51 /*
     52 /*	attr_clnt_control() allows the user to fine tune the behavior of
     53 /*	the specified client. The arguments are a list of (name, value)
     54 /*	terminated with ATTR_CLNT_CTL_END.
     55 /*	The following lists the names and the types of the corresponding
     56 /*	value arguments.
     57 /* .IP "ATTR_CLNT_CTL_PROTO(ATTR_CLNT_PRINT_FN, ATTR_CLNT_SCAN_FN)"
     58 /*	Specifies alternatives for the attr_plain_print() and
     59 /*	attr_plain_scan() functions.
     60 /* .IP "ATTR_CLNT_CTL_REQ_LIMIT(int)"
     61 /*	The maximal number of requests per connection (default: 0,
     62 /*	i.e. no limit).  To enable the limit, specify a value greater
     63 /*	than zero.
     64 /* .IP "ATTR_CLNT_CTL_TRY_LIMIT(int)"
     65 /*	The maximal number of attempts to send a request before
     66 /*	giving up (default: 2). To disable the limit, specify a
     67 /*	value equal to zero.
     68 /* .IP "ATTR_CLNT_CTL_TRY_DELAY(int)"
     69 /*	The time in seconds between attempts to send a request
     70 /*	(default: 1).  Specify a value greater than zero.
     71 /* .IP "ATTR_CLNT_CTL_HANDSHAKE(VSTREAM *)"
     72 /*      A pointer to function that will be called at the start of a
     73 /*      new connection, and that returns 0 in case of success.
     74 /* DIAGNOSTICS
     75 /*	Warnings: communication failure.
     76 /* SEE ALSO
     77 /*	auto_clnt(3), client endpoint management
     78 /*	attr_scan_plain(3), attribute protocol
     79 /*	attr_print_plain(3), attribute protocol
     80 /* LICENSE
     81 /* .ad
     82 /* .fi
     83 /*	The Secure Mailer license must be distributed with this software.
     84 /* AUTHOR(S)
     85 /*	Wietse Venema
     86 /*	IBM T.J. Watson Research
     87 /*	P.O. Box 704
     88 /*	Yorktown Heights, NY 10598, USA
     89 /*
     90 /*	Wietse Venema
     91 /*	Google, Inc.
     92 /*	111 8th Avenue
     93 /*	New York, NY 10011, USA
     94 /*--*/
     95 
     96 /* System library. */
     97 
     98 #include <sys_defs.h>
     99 #include <unistd.h>
    100 #include <errno.h>
    101 
    102 /* Utility library. */
    103 
    104 #include <msg.h>
    105 #include <mymalloc.h>
    106 #include <vstream.h>
    107 #include <htable.h>
    108 #include <attr.h>
    109 #include <iostuff.h>
    110 #include <compat_va_copy.h>
    111 #include <auto_clnt.h>
    112 #include <attr_clnt.h>
    113 
    114 /* Application-specific. */
    115 
    116 struct ATTR_CLNT {
    117     AUTO_CLNT *auto_clnt;
    118     /* Remaining properties are set with attr_clnt_control(). */
    119     ATTR_CLNT_PRINT_FN print;
    120     ATTR_CLNT_SCAN_FN scan;
    121     int     req_limit;
    122     int     req_count;
    123     int     try_limit;
    124     int     try_delay;
    125 };
    126 
    127 #define ATTR_CLNT_DEF_REQ_LIMIT	(0)	/* default per-session request limit */
    128 #define ATTR_CLNT_DEF_TRY_LIMIT	(2)	/* default request (re)try limit */
    129 #define ATTR_CLNT_DEF_TRY_DELAY	(1)	/* default request (re)try delay */
    130 
    131 /* attr_clnt_free - destroy attribute client */
    132 
    133 void    attr_clnt_free(ATTR_CLNT *client)
    134 {
    135     auto_clnt_free(client->auto_clnt);
    136     myfree((void *) client);
    137 }
    138 
    139 /* attr_clnt_create - create attribute client */
    140 
    141 ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
    142 			            int max_idle, int max_ttl)
    143 {
    144     ATTR_CLNT *client;
    145 
    146     client = (ATTR_CLNT *) mymalloc(sizeof(*client));
    147     client->auto_clnt = auto_clnt_create(service, timeout, max_idle, max_ttl);
    148     client->scan = attr_vscan_plain;
    149     client->print = attr_vprint_plain;
    150     client->req_limit = ATTR_CLNT_DEF_REQ_LIMIT;
    151     client->req_count = 0;
    152     client->try_limit = ATTR_CLNT_DEF_TRY_LIMIT;
    153     client->try_delay = ATTR_CLNT_DEF_TRY_DELAY;
    154     return (client);
    155 }
    156 
    157 /* attr_clnt_request - send query, receive reply */
    158 
    159 int     attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
    160 {
    161     const char *myname = "attr_clnt_request";
    162     VSTREAM *stream;
    163     int     count = 0;
    164     va_list saved_ap;
    165     va_list ap;
    166     int     type;
    167     int     recv_flags;
    168     int     err;
    169     int     ret;
    170 
    171     /*
    172      * XXX If the stream is readable before we send anything, then assume the
    173      * remote end disconnected.
    174      *
    175      * XXX For some reason we can't simply call the scan routine after the print
    176      * routine, that messes up the argument list.
    177      */
    178 #define SKIP_ARG(ap, type) { \
    179 	(void) va_arg(ap, char *); \
    180 	(void) va_arg(ap, type); \
    181     }
    182 #define SKIP_ARG2(ap, t1, t2) { \
    183 	SKIP_ARG(ap, t1); \
    184 	(void) va_arg(ap, t2); \
    185     }
    186 
    187     /* Finalize argument lists before returning. */
    188     va_start(saved_ap, send_flags);
    189     for (;;) {
    190 	errno = 0;
    191 	if ((stream = auto_clnt_access(client->auto_clnt)) != 0
    192 	    && readable(vstream_fileno(stream)) == 0) {
    193 	    errno = 0;
    194 	    VA_COPY(ap, saved_ap);
    195 	    err = (client->print(stream, send_flags, ap) != 0
    196 		   || vstream_fflush(stream) != 0);
    197 	    va_end(ap);
    198 	    if (err == 0) {
    199 		VA_COPY(ap, saved_ap);
    200 		while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
    201 		    switch (type) {
    202 		    case ATTR_TYPE_STR:
    203 			SKIP_ARG(ap, char *);
    204 			break;
    205 		    case ATTR_TYPE_DATA:
    206 			SKIP_ARG2(ap, ssize_t, char *);
    207 			break;
    208 		    case ATTR_TYPE_INT:
    209 			SKIP_ARG(ap, int);
    210 			break;
    211 		    case ATTR_TYPE_LONG:
    212 			SKIP_ARG(ap, long);
    213 			break;
    214 		    case ATTR_TYPE_HASH:
    215 			(void) va_arg(ap, HTABLE *);
    216 			break;
    217 		    default:
    218 			msg_panic("%s: unexpected attribute type %d",
    219 				  myname, type);
    220 		    }
    221 		}
    222 		recv_flags = va_arg(ap, int);
    223 		ret = client->scan(stream, recv_flags, ap);
    224 		va_end(ap);
    225 		/* Finalize argument lists before returning. */
    226 		if (ret > 0) {
    227 		    if (client->req_limit > 0
    228 			&& (client->req_count += 1) >= client->req_limit) {
    229 			auto_clnt_recover(client->auto_clnt);
    230 			client->req_count = 0;
    231 		    }
    232 		    break;
    233 		}
    234 	    }
    235 	}
    236 	if ((++count >= client->try_limit && client->try_limit > 0)
    237 	    || msg_verbose
    238 	    || (errno && errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
    239 	    msg_warn("problem talking to server %s: %m",
    240 		     auto_clnt_name(client->auto_clnt));
    241 	/* Finalize argument lists before returning. */
    242 	if (count >= client->try_limit && client->try_limit > 0) {
    243 	    ret = -1;
    244 	    break;
    245 	}
    246 	sleep(client->try_delay);
    247 	auto_clnt_recover(client->auto_clnt);
    248 	client->req_count = 0;
    249     }
    250     /* Finalize argument lists before returning. */
    251     va_end(saved_ap);
    252     return (ret);
    253 }
    254 
    255 /* attr_clnt_control - fine control */
    256 
    257 void    attr_clnt_control(ATTR_CLNT *client, int name,...)
    258 {
    259     const char *myname = "attr_clnt_control";
    260     va_list ap;
    261 
    262     for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
    263 	switch (name) {
    264 	case ATTR_CLNT_CTL_PROTO:
    265 	    client->print = va_arg(ap, ATTR_CLNT_PRINT_FN);
    266 	    client->scan = va_arg(ap, ATTR_CLNT_SCAN_FN);
    267 	    break;
    268 	case ATTR_CLNT_CTL_HANDSHAKE:
    269 	    auto_clnt_control(client->auto_clnt,
    270 			      AUTO_CLNT_CTL_HANDSHAKE,
    271 			      va_arg(ap, ATTR_CLNT_HANDSHAKE_FN),
    272 			      AUTO_CLNT_CTL_END);
    273 	    break;
    274 	case ATTR_CLNT_CTL_REQ_LIMIT:
    275 	    client->req_limit = va_arg(ap, int);
    276 	    if (client->req_limit < 0)
    277 		msg_panic("%s: bad request limit: %d",
    278 			  myname, client->req_limit);
    279 	    if (msg_verbose)
    280 		msg_info("%s: new request limit %d",
    281 			 myname, client->req_limit);
    282 	    break;
    283 	case ATTR_CLNT_CTL_TRY_LIMIT:
    284 	    client->try_limit = va_arg(ap, int);
    285 	    if (client->try_limit < 0)
    286 		msg_panic("%s: bad retry limit: %d", myname, client->try_limit);
    287 	    if (msg_verbose)
    288 		msg_info("%s: new retry limit %d", myname, client->try_limit);
    289 	    break;
    290 	case ATTR_CLNT_CTL_TRY_DELAY:
    291 	    client->try_delay = va_arg(ap, int);
    292 	    if (client->try_delay <= 0)
    293 		msg_panic("%s: bad retry delay: %d", myname, client->try_delay);
    294 	    if (msg_verbose)
    295 		msg_info("%s: new retry delay %d", myname, client->try_delay);
    296 	    break;
    297 	default:
    298 	    msg_panic("%s: bad name %d", myname, name);
    299 	}
    300     }
    301     va_end(ap);
    302 }
    303