1/*++
2/* NAME
3/*	attr_clnt 3
4/* SUMMARY
5/*	attribute query-reply client
6/* SYNOPSIS
7/*	#include <attr_clnt.h>
8/*
9/*	typedef int (*ATTR_CLNT_PRINT_FN) (VSTREAM *, int, va_list);
10/*	typedef int (*ATTR_CLNT_SCAN_FN) (VSTREAM *, int, va_list);
11/*
12/*	ATTR_CLNT *attr_clnt_create(server, timeout, max_idle, max_ttl)
13/*	const char *server;
14/*	int	timeout;
15/*	int	max_idle;
16/*	int	max_ttl;
17/*
18/*	int	attr_clnt_request(client,
19/*			send_flags, send_type, send_name, ..., ATTR_TYPE_END,
20/*			recv_flags, recv_type, recv_name, ..., ATTR_TYPE_END)
21/*	ATTR_CLNT *client;
22/*	int	send_flags;
23/*	int	send_type;
24/*	const char *send_name;
25/*	int	recv_flags;
26/*	int	recv_type;
27/*	const char *recv_name;
28/*
29/*	void	attr_clnt_free(client)
30/*	ATTR_CLNT *client;
31/*
32/*	void	attr_clnt_control(client, name, value, ... ATTR_CLNT_CTL_END)
33/*	ATTR_CLNT *client;
34/*	int	name;
35/* DESCRIPTION
36/*	This module implements a client for a simple attribute-based
37/*	protocol. The default protocol is described in attr_scan_plain(3).
38/*
39/*	attr_clnt_create() creates a client handle. See auto_clnt(3) for
40/*	a description of the arguments.
41/*
42/*	attr_clnt_request() sends the specified request attributes and
43/*	receives a reply. The reply argument specifies a name-value table.
44/*	The other arguments are as described in attr_print_plain(3). The
45/*	result is the number of attributes received or -1 in case of trouble.
46/*
47/*	attr_clnt_free() destroys a client handle and closes its connection.
48/*
49/*	attr_clnt_control() allows the user to fine tune the behavior of
50/*	the specified client. The arguments are a list of (name, value)
51/*	terminated with ATTR_CLNT_CTL_END.
52/*	The following lists the names and the types of the corresponding
53/*	value arguments.
54/* .IP "ATTR_CLNT_CTL_PROTO(ATTR_CLNT_PRINT_FN, ATTR_CLNT_SCAN_FN)"
55/*	Specifies alternatives for the attr_plain_print() and
56/*	attr_plain_scan() functions.
57/* DIAGNOSTICS
58/*	Warnings: communication failure.
59/* SEE ALSO
60/*	auto_clnt(3), client endpoint management
61/*	attr_scan_plain(3), attribute protocol
62/*	attr_print_plain(3), attribute protocol
63/* LICENSE
64/* .ad
65/* .fi
66/*	The Secure Mailer license must be distributed with this software.
67/* AUTHOR(S)
68/*	Wietse Venema
69/*	IBM T.J. Watson Research
70/*	P.O. Box 704
71/*	Yorktown Heights, NY 10598, USA
72/*--*/
73
74/* System library. */
75
76#include <sys_defs.h>
77#include <unistd.h>
78#include <errno.h>
79
80/* Utility library. */
81
82#include <msg.h>
83#include <mymalloc.h>
84#include <vstream.h>
85#include <htable.h>
86#include <attr.h>
87#include <iostuff.h>
88#include <compat_va_copy.h>
89#include <auto_clnt.h>
90#include <attr_clnt.h>
91
92/* Application-specific. */
93
94struct ATTR_CLNT {
95    AUTO_CLNT *auto_clnt;
96    ATTR_CLNT_PRINT_FN print;
97    ATTR_CLNT_SCAN_FN scan;
98};
99
100/* attr_clnt_free - destroy attribute client */
101
102void    attr_clnt_free(ATTR_CLNT *client)
103{
104    auto_clnt_free(client->auto_clnt);
105    myfree((char *) client);
106}
107
108/* attr_clnt_create - create attribute client */
109
110ATTR_CLNT *attr_clnt_create(const char *service, int timeout,
111			            int max_idle, int max_ttl)
112{
113    ATTR_CLNT *client;
114
115    client = (ATTR_CLNT *) mymalloc(sizeof(*client));
116    client->auto_clnt = auto_clnt_create(service, timeout, max_idle, max_ttl);
117    client->scan = attr_vscan_plain;
118    client->print = attr_vprint_plain;
119    return (client);
120}
121
122/* attr_clnt_request - send query, receive reply */
123
124int     attr_clnt_request(ATTR_CLNT *client, int send_flags,...)
125{
126    const char *myname = "attr_clnt_request";
127    VSTREAM *stream;
128    int     count = 0;
129    va_list saved_ap;
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    /* Finalize argument lists before returning. */
153    va_start(saved_ap, send_flags);
154    for (;;) {
155	errno = 0;
156	if ((stream = auto_clnt_access(client->auto_clnt)) != 0
157	    && readable(vstream_fileno(stream)) == 0) {
158	    errno = 0;
159	    VA_COPY(ap, saved_ap);
160	    err = (client->print(stream, send_flags, ap) != 0
161		   || vstream_fflush(stream) != 0);
162	    va_end(ap);
163	    if (err == 0) {
164		VA_COPY(ap, saved_ap);
165		while ((type = va_arg(ap, int)) != ATTR_TYPE_END) {
166		    switch (type) {
167		    case ATTR_TYPE_STR:
168			SKIP_ARG(ap, char *);
169			break;
170		    case ATTR_TYPE_DATA:
171			SKIP_ARG2(ap, ssize_t, char *);
172			break;
173		    case ATTR_TYPE_INT:
174			SKIP_ARG(ap, int);
175			break;
176		    case ATTR_TYPE_LONG:
177			SKIP_ARG(ap, long);
178			break;
179		    case ATTR_TYPE_HASH:
180			(void) va_arg(ap, HTABLE *);
181			break;
182		    default:
183			msg_panic("%s: unexpected attribute type %d",
184				  myname, type);
185		    }
186		}
187		recv_flags = va_arg(ap, int);
188		ret = client->scan(stream, recv_flags, ap);
189		va_end(ap);
190		/* Finalize argument lists before returning. */
191		if (ret > 0)
192		    break;
193	    }
194	}
195	if (++count >= 2
196	    || msg_verbose
197	    || (errno && errno != EPIPE && errno != ENOENT && errno != ECONNRESET))
198	    msg_warn("problem talking to server %s: %m",
199		     auto_clnt_name(client->auto_clnt));
200	/* Finalize argument lists before returning. */
201	if (count >= 2) {
202	    ret = -1;
203	    break;
204	}
205	sleep(1);				/* XXX make configurable */
206	auto_clnt_recover(client->auto_clnt);
207    }
208    /* Finalize argument lists before returning. */
209    va_end(saved_ap);
210    return (ret);
211}
212
213/* attr_clnt_control - fine control */
214
215void    attr_clnt_control(ATTR_CLNT *client, int name,...)
216{
217    const char *myname = "attr_clnt_control";
218    va_list ap;
219
220    for (va_start(ap, name); name != ATTR_CLNT_CTL_END; name = va_arg(ap, int)) {
221	switch (name) {
222	case ATTR_CLNT_CTL_PROTO:
223	    client->print = va_arg(ap, ATTR_CLNT_PRINT_FN);
224	    client->scan = va_arg(ap, ATTR_CLNT_SCAN_FN);
225	    break;
226	default:
227	    msg_panic("%s: bad name %d", myname, name);
228	}
229    }
230    va_end(ap);
231}
232