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