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