1 /*	$NetBSD: attr_clnt.c,v 1.1.1.1 2009/06/23 10:08:58 tron 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 /* DIAGNOSTICS
60 /*	Warnings: communication failure.
61 /* SEE ALSO
62 /*	auto_clnt(3), client endpoint management
63 /*	attr_scan_plain(3), attribute protocol
64 /*	attr_print_plain(3), attribute protocol
65 /* LICENSE
66 /* .ad
67 /* .fi
68 /*	The Secure Mailer license must be distributed with this software.
69 /* AUTHOR(S)
70 /*	Wietse Venema
71 /*	IBM T.J. Watson Research
72 /*	P.O. Box 704
73 /*	Yorktown Heights, NY 10598, USA
74 /*--*/
75 
76 /* System library. */
77 
78 #include <sys_defs.h>
79 #include <unistd.h>
80 #include <errno.h>
81 
82 /* Utility library. */
83 
84 #include <msg.h>
85 #include <mymalloc.h>
86 #include <vstream.h>
87 #include <htable.h>
88 #include <attr.h>
89 #include <iostuff.h>
90 #include <auto_clnt.h>
91 #include <attr_clnt.h>
92 
93 /* Application-specific. */
94 
95 struct ATTR_CLNT {
96     AUTO_CLNT *auto_clnt;
97     ATTR_CLNT_PRINT_FN print;
98     ATTR_CLNT_SCAN_FN scan;
99 };
100 
101 /* attr_clnt_free - destroy attribute client */
102 
103 void    attr_clnt_free(ATTR_CLNT *client)
104 {
105     auto_clnt_free(client->auto_clnt);
106     myfree((char *) client);
107 }
108 
109 /* attr_clnt_create - create attribute client */
110 
111 ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
112 			            int max_idle, int max_ttl)
113 {
114     ATTR_CLNT *client;
115 
116     client = (ATTR_CLNT *) mymalloc(sizeof(*client));
117     client->auto_clnt = auto_clnt_create(service, timeout, max_idle, max_ttl);
118     client->scan = attr_vscan_plain;
119     client->print = attr_vprint_plain;
120     return (client);
121 }
122 
123 /* attr_clnt_request - send query, receive reply */
124 
125 int     attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
126 {
127     const char *myname = "attr_clnt_request";
128     VSTREAM *stream;
129     int     count = 0;
130     va_list ap;
131     int     type;
132     int     recv_flags;
133     int     err;
134     int     ret;
135 
136     /*
137      * XXX If the stream is readable before we send anything, then assume the
138      * remote end disconnected.
139      *
140      * XXX For some reason we can't simply call the scan routine after the print
141      * routine, that messes up the argument list.
142      */
143 #define SKIP_ARG(ap, type) { \
144 	(void) va_arg(ap, char *); \
145 	(void) va_arg(ap, type); \
146     }
147 #define SKIP_ARG2(ap, t1, t2) { \
148 	SKIP_ARG(ap, t1); \
149 	(void) va_arg(ap, t2); \
150     }
151 
152     for (;;) {
153 	errno = 0;
154 	if ((stream = auto_clnt_access(client->auto_clnt)) != 0
155 	    && readable(vstream_fileno(stream)) == 0) {
156 	    errno = 0;
157 	    va_start(ap, send_flags);
158 	    err = (client->print(stream, send_flags, ap) != 0
159 		   || vstream_fflush(stream) != 0);
160 	    va_end(ap);
161 	    if (err == 0) {
162 		va_start(ap, send_flags);
163 		while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
164 		    switch (type) {
165 		    case ATTR_TYPE_STR:
166 			SKIP_ARG(ap, char *);
167 			break;
168 		    case ATTR_TYPE_DATA:
169 			SKIP_ARG2(ap, ssize_t, char *);
170 			break;
171 		    case ATTR_TYPE_INT:
172 			SKIP_ARG(ap, int);
173 			break;
174 		    case ATTR_TYPE_LONG:
175 			SKIP_ARG(ap, long);
176 			break;
177 		    case ATTR_TYPE_HASH:
178 			(void) va_arg(ap, HTABLE *);
179 			break;
180 		    default:
181 			msg_panic("%s: unexpected attribute type %d",
182 				  myname, type);
183 		    }
184 		}
185 		recv_flags = va_arg(ap, int);
186 		ret = client->scan(stream, recv_flags, ap);
187 		va_end(ap);
188 		if (ret > 0)
189 		    return (ret);
190 	    }
191 	}
192 	if (++count >= 2
193 	    || msg_verbose
194 	    || (errno && errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
195 	    msg_warn("problem talking to server %s: %m",
196 		     auto_clnt_name(client->auto_clnt));
197 	if (count >= 2)
198 	    return (-1);
199 	sleep(1);				/* XXX make configurable */
200 	auto_clnt_recover(client->auto_clnt);
201     }
202 }
203 
204 /* attr_clnt_control - fine control */
205 
206 void    attr_clnt_control(ATTR_CLNT *client, int name,...)
207 {
208     const char *myname = "attr_clnt_control";
209     va_list ap;
210 
211     for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
212 	switch (name) {
213 	case ATTR_CLNT_CTL_PROTO:
214 	    client->print = va_arg(ap, ATTR_CLNT_PRINT_FN);
215 	    client->scan = va_arg(ap, ATTR_CLNT_SCAN_FN);
216 	    break;
217 	default:
218 	    msg_panic("%s: bad name %d", myname, name);
219 	}
220     }
221     va_end(ap);
222 }
223