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