#include <sys_defs.h>
#include <errno.h>
#include <unistd.h>
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <vstring.h>
#include <vstream.h>
#include <attr.h>
#include <dict.h>
#include <mail_proto.h>
#include <mail_params.h>
#include <clnt_stream.h>
#include <dict_proxy.h>
typedef struct {
DICT dict;
int in_flags;
VSTRING *result;
} DICT_PROXY;
#define STR(x) vstring_str(x)
#define VSTREQ(v,s) (strcmp(STR(v),s) == 0)
static CLNT_STREAM *proxy_stream;
static const char *dict_proxy_lookup(DICT *dict, const char *key)
{
const char *myname = "dict_proxy_lookup";
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
VSTREAM *stream;
int status;
int count = 0;
int request_flags;
VSTRING_RESET(dict_proxy->result);
VSTRING_TERMINATE(dict_proxy->result);
request_flags = (dict_proxy->in_flags & DICT_FLAG_RQST_MASK)
| (dict->flags & DICT_FLAG_RQST_MASK);
for (;;) {
stream = clnt_stream_access(proxy_stream);
errno = 0;
count += 1;
if (attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_LOOKUP,
ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict->name,
ATTR_TYPE_INT, MAIL_ATTR_FLAGS, request_flags,
ATTR_TYPE_STR, MAIL_ATTR_KEY, key,
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
ATTR_TYPE_STR, MAIL_ATTR_VALUE, dict_proxy->result,
ATTR_TYPE_END) != 2) {
if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m", myname, VSTREAM_PATH(stream));
} else {
if (msg_verbose)
msg_info("%s: table=%s flags=%s key=%s -> status=%d result=%s",
myname, dict->name,
dict_flags_str(request_flags), key,
status, STR(dict_proxy->result));
switch (status) {
case PROXY_STAT_BAD:
msg_fatal("%s lookup failed for table \"%s\" key \"%s\": "
"invalid request",
MAIL_SERVICE_PROXYMAP, dict->name, key);
case PROXY_STAT_DENY:
msg_fatal("%s service is not configured for table \"%s\"",
MAIL_SERVICE_PROXYMAP, dict->name);
case PROXY_STAT_OK:
return (STR(dict_proxy->result));
case PROXY_STAT_NOKEY:
dict_errno = 0;
return (0);
case PROXY_STAT_RETRY:
dict_errno = DICT_ERR_RETRY;
return (0);
default:
msg_warn("%s lookup failed for table \"%s\" key \"%s\": "
"unexpected reply status %d",
MAIL_SERVICE_PROXYMAP, dict->name, key, status);
}
}
clnt_stream_recover(proxy_stream);
sleep(1);
}
}
static void dict_proxy_close(DICT *dict)
{
DICT_PROXY *dict_proxy = (DICT_PROXY *) dict;
vstring_free(dict_proxy->result);
dict_free(dict);
}
DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags)
{
const char *myname = "dict_proxy_open";
DICT_PROXY *dict_proxy;
VSTREAM *stream;
int server_flags;
int status;
char *kludge = 0;
char *prefix;
if (open_flags != O_RDONLY)
msg_fatal("%s: %s map open requires O_RDONLY access mode",
map, DICT_TYPE_PROXY);
if (dict_flags & DICT_FLAG_NO_PROXY)
return (dict_open(map, open_flags, dict_flags));
dict_proxy = (DICT_PROXY *)
dict_alloc(DICT_TYPE_PROXY, map, sizeof(*dict_proxy));
dict_proxy->dict.lookup = dict_proxy_lookup;
dict_proxy->dict.close = dict_proxy_close;
dict_proxy->in_flags = dict_flags;
dict_proxy->result = vstring_alloc(10);
if (proxy_stream == 0) {
if (access(MAIL_CLASS_PRIVATE "/" MAIL_SERVICE_PROXYMAP, F_OK) == 0)
prefix = MAIL_CLASS_PRIVATE;
else
prefix = kludge = concatenate(var_queue_dir, "/",
MAIL_CLASS_PRIVATE, (char *) 0);
proxy_stream = clnt_stream_create(prefix,
MAIL_SERVICE_PROXYMAP,
var_ipc_idle_limit,
var_ipc_ttl_limit);
if (kludge)
myfree(kludge);
}
for (;;) {
stream = clnt_stream_access(proxy_stream);
errno = 0;
if (attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_STR, MAIL_ATTR_REQ, PROXY_REQ_OPEN,
ATTR_TYPE_STR, MAIL_ATTR_TABLE, dict_proxy->dict.name,
ATTR_TYPE_INT, MAIL_ATTR_FLAGS, dict_proxy->in_flags,
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &server_flags,
ATTR_TYPE_END) != 2) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("%s: service %s: %m", VSTREAM_PATH(stream), myname);
} else {
if (msg_verbose)
msg_info("%s: connect to map=%s status=%d server_flags=%s",
myname, dict_proxy->dict.name, status,
dict_flags_str(server_flags));
switch (status) {
case PROXY_STAT_BAD:
msg_fatal("%s open failed for table \"%s\": invalid request",
MAIL_SERVICE_PROXYMAP, dict_proxy->dict.name);
case PROXY_STAT_DENY:
msg_fatal("%s service is not configured for table \"%s\"",
MAIL_SERVICE_PROXYMAP, dict_proxy->dict.name);
case PROXY_STAT_OK:
dict_proxy->dict.flags = dict_proxy->in_flags
| (server_flags & DICT_FLAG_IMPL_MASK);
return (DICT_DEBUG (&dict_proxy->dict));
default:
msg_warn("%s open failed for table \"%s\": unexpected status %d",
MAIL_SERVICE_PROXYMAP, dict_proxy->dict.name, status);
}
}
clnt_stream_recover(proxy_stream);
sleep(1);
}
}