summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorume <ume@FreeBSD.org>2006-04-28 12:03:38 +0000
committerume <ume@FreeBSD.org>2006-04-28 12:03:38 +0000
commite14f1c3b3b31e7f6c28cef5acc87f4c7373d288c (patch)
treed796503361cc28eb3b9eaa593876abd826a2cf81
parent4b38e5bbca25f143cbf615b8c7fe1f7873ba1e6c (diff)
downloadFreeBSD-src-e14f1c3b3b31e7f6c28cef5acc87f4c7373d288c.zip
FreeBSD-src-e14f1c3b3b31e7f6c28cef5acc87f4c7373d288c.tar.gz
- Extend the nsswitch to support Services, Protocols and Rpc
databases. - Make nsswitch support caching. Submitted by: Michael Bushkov <bushman__at__rsu.ru> Sponsored by: Google Summer of Code 2005
-rw-r--r--etc/Makefile4
-rw-r--r--etc/cached.conf12
-rw-r--r--etc/defaults/rc.conf1
-rw-r--r--etc/nscd.conf12
-rwxr-xr-xetc/rc.d/Makefile4
-rw-r--r--etc/rc.d/cached30
-rw-r--r--etc/rc.d/nscd30
-rw-r--r--include/nsswitch.h10
-rw-r--r--include/rpc/rpcent.h7
-rw-r--r--lib/libc/Makefile3
-rw-r--r--lib/libc/gen/getgrent.c278
-rw-r--r--lib/libc/gen/getpwent.c277
-rw-r--r--lib/libc/include/nscache.h197
-rw-r--r--lib/libc/include/nscachedcli.h107
-rw-r--r--lib/libc/net/Makefile.inc7
-rw-r--r--lib/libc/net/getaddrinfo.c197
-rw-r--r--lib/libc/net/gethostnamadr.c296
-rw-r--r--lib/libc/net/getnetnamadr.c241
-rw-r--r--lib/libc/net/getproto.c90
-rw-r--r--lib/libc/net/getprotoent.c339
-rw-r--r--lib/libc/net/getprotoname.c88
-rw-r--r--lib/libc/net/getservbyname.c101
-rw-r--r--lib/libc/net/getservbyport.c95
-rw-r--r--lib/libc/net/getservent.c1308
-rw-r--r--lib/libc/net/name6.c259
-rw-r--r--lib/libc/net/netdb_private.h27
-rw-r--r--lib/libc/net/nscache.c438
-rw-r--r--lib/libc/net/nscachedcli.c576
-rw-r--r--lib/libc/net/nsdispatch.c95
-rw-r--r--lib/libc/rpc/getrpcent.c1156
-rw-r--r--share/mk/bsd.own.mk2
-rw-r--r--usr.sbin/Makefile5
-rw-r--r--usr.sbin/cached/Makefile19
-rw-r--r--usr.sbin/cached/agent.c127
-rw-r--r--usr.sbin/cached/agent.h72
-rw-r--r--usr.sbin/cached/agents/Makefile.inc3
-rw-r--r--usr.sbin/cached/agents/group.c262
-rw-r--r--usr.sbin/cached/agents/group.h32
-rw-r--r--usr.sbin/cached/agents/passwd.c269
-rw-r--r--usr.sbin/cached/agents/passwd.h32
-rw-r--r--usr.sbin/cached/agents/services.c286
-rw-r--r--usr.sbin/cached/agents/services.h32
-rw-r--r--usr.sbin/cached/cached.8148
-rw-r--r--usr.sbin/cached/cached.c884
-rw-r--r--usr.sbin/cached/cached.conf.5102
-rw-r--r--usr.sbin/cached/cachedcli.c284
-rw-r--r--usr.sbin/cached/cachedcli.h57
-rw-r--r--usr.sbin/cached/cachelib.c1234
-rw-r--r--usr.sbin/cached/cachelib.h281
-rw-r--r--usr.sbin/cached/cacheplcs.c590
-rw-r--r--usr.sbin/cached/cacheplcs.h137
-rw-r--r--usr.sbin/cached/config.c588
-rw-r--r--usr.sbin/cached/config.h156
-rw-r--r--usr.sbin/cached/debug.c149
-rw-r--r--usr.sbin/cached/debug.h67
-rw-r--r--usr.sbin/cached/hashtable.h218
-rw-r--r--usr.sbin/cached/log.c78
-rw-r--r--usr.sbin/cached/log.h43
-rw-r--r--usr.sbin/cached/mp_rs_query.c537
-rw-r--r--usr.sbin/cached/mp_rs_query.h34
-rw-r--r--usr.sbin/cached/mp_ws_query.c548
-rw-r--r--usr.sbin/cached/mp_ws_query.h36
-rw-r--r--usr.sbin/cached/parser.c474
-rw-r--r--usr.sbin/cached/parser.h35
-rw-r--r--usr.sbin/cached/protocol.c550
-rw-r--r--usr.sbin/cached/protocol.h265
-rw-r--r--usr.sbin/cached/query.c1278
-rw-r--r--usr.sbin/cached/query.h110
-rw-r--r--usr.sbin/cached/singletons.c36
-rw-r--r--usr.sbin/cached/singletons.h47
-rw-r--r--usr.sbin/nscd/Makefile19
-rw-r--r--usr.sbin/nscd/agent.c127
-rw-r--r--usr.sbin/nscd/agent.h72
-rw-r--r--usr.sbin/nscd/agents/Makefile.inc3
-rw-r--r--usr.sbin/nscd/agents/group.c262
-rw-r--r--usr.sbin/nscd/agents/group.h32
-rw-r--r--usr.sbin/nscd/agents/passwd.c269
-rw-r--r--usr.sbin/nscd/agents/passwd.h32
-rw-r--r--usr.sbin/nscd/agents/services.c286
-rw-r--r--usr.sbin/nscd/agents/services.h32
-rw-r--r--usr.sbin/nscd/cachelib.c1234
-rw-r--r--usr.sbin/nscd/cachelib.h281
-rw-r--r--usr.sbin/nscd/cacheplcs.c590
-rw-r--r--usr.sbin/nscd/cacheplcs.h137
-rw-r--r--usr.sbin/nscd/config.c588
-rw-r--r--usr.sbin/nscd/config.h156
-rw-r--r--usr.sbin/nscd/debug.c149
-rw-r--r--usr.sbin/nscd/debug.h67
-rw-r--r--usr.sbin/nscd/hashtable.h218
-rw-r--r--usr.sbin/nscd/log.c78
-rw-r--r--usr.sbin/nscd/log.h43
-rw-r--r--usr.sbin/nscd/mp_rs_query.34
-rw-r--r--usr.sbin/nscd/mp_rs_query.c537
-rw-r--r--usr.sbin/nscd/mp_rs_query.h34
-rw-r--r--usr.sbin/nscd/mp_ws_query.c548
-rw-r--r--usr.sbin/nscd/mp_ws_query.h36
-rw-r--r--usr.sbin/nscd/nscd.8148
-rw-r--r--usr.sbin/nscd/nscd.c884
-rw-r--r--usr.sbin/nscd/nscd.conf.5102
-rw-r--r--usr.sbin/nscd/nscdcli.c284
-rw-r--r--usr.sbin/nscd/nscdcli.h57
-rw-r--r--usr.sbin/nscd/parser.c474
-rw-r--r--usr.sbin/nscd/parser.h35
-rw-r--r--usr.sbin/nscd/protocol.c550
-rw-r--r--usr.sbin/nscd/protocol.h265
-rw-r--r--usr.sbin/nscd/query.c1278
-rw-r--r--usr.sbin/nscd/query.h110
-rw-r--r--usr.sbin/nscd/singletons.c36
-rw-r--r--usr.sbin/nscd/singletons.h47
109 files changed, 25779 insertions, 747 deletions
diff --git a/etc/Makefile b/etc/Makefile
index 9933975..e2fa4bb 100644
--- a/etc/Makefile
+++ b/etc/Makefile
@@ -35,6 +35,10 @@ BSM_ETC_DIR= ${DESTDIR}/etc/security
BIN1+= printcap
.endif
+.if ${MK_NS_CACHING} != "no"
+BIN1+= cached.conf
+.endif
+
.if ${MK_OPENSSH} != "no"
SSH= ${.CURDIR}/../crypto/openssh/ssh_config \
${.CURDIR}/../crypto/openssh/sshd_config \
diff --git a/etc/cached.conf b/etc/cached.conf
new file mode 100644
index 0000000..19a8b5b
--- /dev/null
+++ b/etc/cached.conf
@@ -0,0 +1,12 @@
+#
+# Default caching daemon configuration file
+# $FreeBSD$
+#
+
+enable-cache passwd yes
+enable-cache group yes
+enable-cache hosts yes
+enable-cache services yes
+enable-cache protocols yes
+enable-cache rpc yes
+enable-cache networks yes
diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf
index 316705e..6d92919 100644
--- a/etc/defaults/rc.conf
+++ b/etc/defaults/rc.conf
@@ -472,6 +472,7 @@ sendmail_msp_queue_flags="-L sm-msp-queue -Ac -q30m"
auditd_enable="NO" # Run the audit daemon.
auditd_flags="" # Which options to pass to the audit daemon.
+cached_enable="NO" # Run the nsswitch caching daemon.
cron_enable="YES" # Run the periodic job daemon.
cron_program="/usr/sbin/cron" # Which cron executable to run (if enabled).
cron_dst="YES" # Handle DST transitions intelligently (YES/NO)
diff --git a/etc/nscd.conf b/etc/nscd.conf
new file mode 100644
index 0000000..19a8b5b
--- /dev/null
+++ b/etc/nscd.conf
@@ -0,0 +1,12 @@
+#
+# Default caching daemon configuration file
+# $FreeBSD$
+#
+
+enable-cache passwd yes
+enable-cache group yes
+enable-cache hosts yes
+enable-cache services yes
+enable-cache protocols yes
+enable-cache rpc yes
+enable-cache networks yes
diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile
index a955b76..45ffa4a 100755
--- a/etc/rc.d/Makefile
+++ b/etc/rc.d/Makefile
@@ -46,6 +46,10 @@ FILES= DAEMON LOGIN NETWORKING SERVERS \
FILES+= sshd
.endif
+.if ${MK_NS_CACHING} != "no"
+FILES+= cached
+.endif
+
FILESDIR= /etc/rc.d
FILESMODE= ${BINMODE}
diff --git a/etc/rc.d/cached b/etc/rc.d/cached
new file mode 100644
index 0000000..0376b58
--- /dev/null
+++ b/etc/rc.d/cached
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: cached
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+
+#
+# Add the following lines to /etc/rc.conf to enable cached:
+#
+# cached_enable="YES"
+#
+# See cached(8) for flags
+#
+
+. /etc/rc.subr
+
+name=cached
+rcvar=`set_rcvar`
+
+command=/usr/sbin/cached
+
+cached_enable=${cached_enable:-"NO"}
+cached_pidfile=${cached_pidfile:-"/var/run/cached.pid"}
+cached_flags=${cached_flags:-""}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/etc/rc.d/nscd b/etc/rc.d/nscd
new file mode 100644
index 0000000..0376b58
--- /dev/null
+++ b/etc/rc.d/nscd
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: cached
+# REQUIRE: DAEMON
+# BEFORE: LOGIN
+
+#
+# Add the following lines to /etc/rc.conf to enable cached:
+#
+# cached_enable="YES"
+#
+# See cached(8) for flags
+#
+
+. /etc/rc.subr
+
+name=cached
+rcvar=`set_rcvar`
+
+command=/usr/sbin/cached
+
+cached_enable=${cached_enable:-"NO"}
+cached_pidfile=${cached_pidfile:-"/var/run/cached.pid"}
+cached_flags=${cached_flags:-""}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/include/nsswitch.h b/include/nsswitch.h
index 22e482c..5e7ee0c 100644
--- a/include/nsswitch.h
+++ b/include/nsswitch.h
@@ -68,6 +68,7 @@
#define NSSRC_DNS "dns" /* DNS; IN for hosts, HS for others */
#define NSSRC_NIS "nis" /* YP/NIS */
#define NSSRC_COMPAT "compat" /* passwd,group in YP compat mode */
+#define NSSRC_CACHE "cache" /* cache daemon */
/*
* currently implemented databases
@@ -80,6 +81,11 @@
#define NSDB_PASSWD "passwd"
#define NSDB_PASSWD_COMPAT "passwd_compat"
#define NSDB_SHELLS "shells"
+#define NSDB_SERVICES "services"
+#define NSDB_SERVICES_COMPAT "services_compat"
+#define NSDB_SSH_HOSTKEYS "ssh_hostkeys"
+#define NSDB_PROTOCOLS "protocols"
+#define NSDB_RPC "rpc"
/*
* suggested databases to implement
@@ -93,11 +99,8 @@
#define NSDB_NETMASKS "netmasks"
#define NSDB_PHONES "phones"
#define NSDB_PRINTCAP "printcap"
-#define NSDB_PROTOCOLS "protocols"
#define NSDB_REMOTE "remote"
-#define NSDB_RPC "rpc"
#define NSDB_SENDMAILVARS "sendmailvars"
-#define NSDB_SERVICES "services"
#define NSDB_TERMCAP "termcap"
#define NSDB_TTYS "ttys"
@@ -193,7 +196,6 @@ enum nss_lookup_type {
};
#ifdef _NS_PRIVATE
-
/*
* private data structures for back-end nsswitch implementation
*/
diff --git a/include/rpc/rpcent.h b/include/rpc/rpcent.h
index 4388d8b..64858b1 100644
--- a/include/rpc/rpcent.h
+++ b/include/rpc/rpcent.h
@@ -53,12 +53,7 @@ struct rpcent {
};
__BEGIN_DECLS
-extern struct rpcent *getrpcbyname_r(const char *, struct rpcent *,
- char *, int);
-extern struct rpcent *getrpcbynumber_r(int, struct rpcent *, char *, int);
-extern struct rpcent *getrpcent_r(struct rpcent *, char *, int);
-
-/* Old interfaces that return a pointer to a static area; MT-unsafe */
+/* These interfaces are currently implemented through nsswitch and MT-safe */
extern struct rpcent *getrpcbyname(char *);
extern struct rpcent *getrpcbynumber(int);
extern struct rpcent *getrpcent(void);
diff --git a/lib/libc/Makefile b/lib/libc/Makefile
index f3a7c03..496598a 100644
--- a/lib/libc/Makefile
+++ b/lib/libc/Makefile
@@ -72,6 +72,9 @@ CFLAGS+= -DHESIOD
.if ${MK_FP_LIBC} == "no"
CFLAGS+= -DNO_FLOATING_POINT
.endif
+.if ${MK_NS_CACHING} != "no"
+CFLAGS+= -DNS_CACHING
+.endif
.if defined(SYMVER_ENABLED)
VERSION_DEF=${.CURDIR}/Versions.def
diff --git a/lib/libc/gen/getgrent.c b/lib/libc/gen/getgrent.c
index c9b7474..d9ea5bd 100644
--- a/lib/libc/gen/getgrent.c
+++ b/lib/libc/gen/getgrent.c
@@ -57,7 +57,9 @@ __FBSDID("$FreeBSD$");
#include "un-namespace.h"
#include "libc_private.h"
#include "nss_tls.h"
-
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
enum constants {
GRP_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
@@ -141,11 +143,235 @@ NSS_TLS_HANDLING(compat);
static int compat_setgrent(void *, void *, va_list);
static int compat_group(void *, void *, va_list);
+#ifdef NS_CACHING
+static int grp_id_func(char *, size_t *, va_list, void *);
+static int grp_marshal_func(char *, size_t *, void *, va_list, void *);
+static int grp_unmarshal_func(char *, size_t, void *, va_list, void *);
+
+static int
+grp_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
+{
+ char *name;
+ gid_t gid;
+
+ size_t desired_size, size;
+ int res = NS_UNAVAIL;
+ enum nss_lookup_type lookup_type;
+
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ size = strlen(name);
+ desired_size = sizeof(enum nss_lookup_type) + size + 1;
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ gid = va_arg(ap, gid_t);
+ desired_size = sizeof(enum nss_lookup_type) + sizeof(gid_t);
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), &gid,
+ sizeof(gid_t));
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+static int
+grp_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ gid_t gid;
+ struct group *grp;
+ char *orig_buf;
+ size_t orig_buf_size;
+
+ struct group new_grp;
+ size_t desired_size, size, mem_size;
+ char *p, **mem;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ gid = va_arg(ap, gid_t);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ grp = va_arg(ap, struct group *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ desired_size = _ALIGNBYTES + sizeof(struct group) + sizeof(char *);
+
+ if (grp->gr_name != NULL)
+ desired_size += strlen(grp->gr_name) + 1;
+ if (grp->gr_passwd != NULL)
+ desired_size += strlen(grp->gr_passwd) + 1;
+
+ if (grp->gr_mem != NULL) {
+ mem_size = 0;
+ for (mem = grp->gr_mem; *mem; ++mem) {
+ desired_size += strlen(*mem) + 1;
+ ++mem_size;
+ }
+
+ desired_size += _ALIGNBYTES + (mem_size + 1) * sizeof(char *);
+ }
+
+ if (desired_size > *buffer_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_grp, grp, sizeof(struct group));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct group) + sizeof(char *);
+ memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_grp.gr_name != NULL) {
+ size = strlen(new_grp.gr_name);
+ memcpy(p, new_grp.gr_name, size);
+ new_grp.gr_name = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_passwd != NULL) {
+ size = strlen(new_grp.gr_passwd);
+ memcpy(p, new_grp.gr_passwd, size);
+ new_grp.gr_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_mem != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
+ new_grp.gr_mem = (char **)p;
+ p += sizeof(char *) * (mem_size + 1);
+
+ for (mem = new_grp.gr_mem; *mem; ++mem) {
+ size = strlen(*mem);
+ memcpy(p, *mem, size);
+ *mem = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_grp, sizeof(struct group));
+ return (NS_SUCCESS);
+}
+
+static int
+grp_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ gid_t gid;
+ struct group *grp;
+ char *orig_buf;
+ size_t orig_buf_size;
+ int *ret_errno;
+
+ char *p;
+ char **mem;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ gid = va_arg(ap, gid_t);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ grp = va_arg(ap, struct group *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int *);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct group) - sizeof(char *)) {
+ *ret_errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(grp, buffer, sizeof(struct group));
+ memcpy(&p, buffer + sizeof(struct group), sizeof(char *));
+
+ orig_buf = (char *)_ALIGN(orig_buf);
+ memcpy(orig_buf, buffer + sizeof(struct group) + sizeof(char *) +
+ _ALIGN(p) - (size_t)p,
+ buffer_size - sizeof(struct group) - sizeof(char *) -
+ _ALIGN(p) + (size_t)p);
+ p = (char *)_ALIGN(p);
+
+ NS_APPLY_OFFSET(grp->gr_name, orig_buf, p, char *);
+ NS_APPLY_OFFSET(grp->gr_passwd, orig_buf, p, char *);
+ if (grp->gr_mem != NULL) {
+ NS_APPLY_OFFSET(grp->gr_mem, orig_buf, p, char **);
+
+ for (mem = grp->gr_mem; *mem; ++mem)
+ NS_APPLY_OFFSET(*mem, orig_buf, p, char *);
+ }
+
+ if (retval != NULL)
+ *((struct group **)retval) = grp;
+
+ return (NS_SUCCESS);
+}
+
+NSS_MP_CACHE_HANDLING(group);
+#endif /* NS_CACHING */
+
/* XXX IEEE Std 1003.1, 2003 specifies `void setgrent(void)' */
int
setgrent(void)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ group, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
#ifdef HESIOD
@@ -155,6 +381,9 @@ setgrent(void)
{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc, 0);
@@ -165,6 +394,12 @@ setgrent(void)
int
setgroupent(int stayopen)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ group, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)SETGRENT },
#ifdef HESIOD
@@ -174,6 +409,9 @@ setgroupent(int stayopen)
{ NSSRC_NIS, nis_setgrent, (void *)SETGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)SETGRENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "setgrent", defaultsrc,
@@ -185,6 +423,12 @@ setgroupent(int stayopen)
void
endgrent(void)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ group, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setgrent, (void *)ENDGRENT },
#ifdef HESIOD
@@ -194,6 +438,9 @@ endgrent(void)
{ NSSRC_NIS, nis_setgrent, (void *)ENDGRENT },
#endif
{ NSSRC_COMPAT, compat_setgrent, (void *)ENDGRENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_GROUP, "endgrent", defaultsrc);
@@ -204,6 +451,12 @@ int
getgrent_r(struct group *grp, char *buffer, size_t bufsize,
struct group **result)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ group, (void *)nss_lt_all,
+ grp_marshal_func, grp_unmarshal_func);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_group, (void *)nss_lt_all },
#ifdef HESIOD
@@ -213,6 +466,9 @@ getgrent_r(struct group *grp, char *buffer, size_t bufsize,
{ NSSRC_NIS, nis_group, (void *)nss_lt_all },
#endif
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_all },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
@@ -232,6 +488,13 @@ int
getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
struct group **result)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ group, (void *)nss_lt_name,
+ grp_id_func, grp_marshal_func, grp_unmarshal_func);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_group, (void *)nss_lt_name },
#ifdef HESIOD
@@ -241,6 +504,9 @@ getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize,
{ NSSRC_NIS, nis_group, (void *)nss_lt_name },
#endif
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_name },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
@@ -260,6 +526,13 @@ int
getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
struct group **result)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ group, (void *)nss_lt_id,
+ grp_id_func, grp_marshal_func, grp_unmarshal_func);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_group, (void *)nss_lt_id },
#ifdef HESIOD
@@ -269,6 +542,9 @@ getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize,
{ NSSRC_NIS, nis_group, (void *)nss_lt_id },
#endif
{ NSSRC_COMPAT, compat_group, (void *)nss_lt_id },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
diff --git a/lib/libc/gen/getpwent.c b/lib/libc/gen/getpwent.c
index a0867dd..f729cdf 100644
--- a/lib/libc/gen/getpwent.c
+++ b/lib/libc/gen/getpwent.c
@@ -61,6 +61,9 @@ __FBSDID("$FreeBSD$");
#include "libc_private.h"
#include "pw_scan.h"
#include "nss_tls.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
#ifndef CTASSERT
#define CTASSERT(x) _CTASSERT(x, __LINE__)
@@ -195,9 +198,233 @@ static int compat_use_template(struct passwd *, struct passwd *, char *,
static int compat_redispatch(struct compat_state *, enum nss_lookup_type,
enum nss_lookup_type, const char *, const char *, uid_t,
struct passwd *, char *, size_t, int *);
+
+#ifdef NS_CACHING
+static int pwd_id_func(char *, size_t *, va_list ap, void *);
+static int pwd_marshal_func(char *, size_t *, void *, va_list, void *);
+static int pwd_unmarshal_func(char *, size_t, void *, va_list, void *);
+
+static int
+pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
+{
+ char *name;
+ uid_t uid;
+ size_t size, desired_size;
+ int res = NS_UNAVAIL;
+ enum nss_lookup_type lookup_type;
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ size = strlen(name);
+ desired_size = sizeof(enum nss_lookup_type) + size + 1;
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ uid = va_arg(ap, uid_t);
+ desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t);
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), &uid,
+ sizeof(uid_t));
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+static int
+pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ uid_t uid;
+ struct passwd *pwd;
+ char *orig_buf;
+ size_t orig_buf_size;
+
+ struct passwd new_pwd;
+ size_t desired_size, size;
+ char *p;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ uid = va_arg(ap, uid_t);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ pwd = va_arg(ap, struct passwd *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ desired_size = sizeof(struct passwd) + sizeof(char *) +
+ strlen(pwd->pw_name) + 1;
+ if (pwd->pw_passwd != NULL)
+ desired_size += strlen(pwd->pw_passwd) + 1;
+ if (pwd->pw_class != NULL)
+ desired_size += strlen(pwd->pw_class) + 1;
+ if (pwd->pw_gecos != NULL)
+ desired_size += strlen(pwd->pw_gecos) + 1;
+ if (pwd->pw_dir != NULL)
+ desired_size += strlen(pwd->pw_dir) + 1;
+ if (pwd->pw_shell != NULL)
+ desired_size += strlen(pwd->pw_shell) + 1;
+
+ if (*buffer_size < desired_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_pwd, pwd, sizeof(struct passwd));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct passwd) + sizeof(char *);
+ memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
+
+ if (new_pwd.pw_name != NULL) {
+ size = strlen(new_pwd.pw_name);
+ memcpy(p, new_pwd.pw_name, size);
+ new_pwd.pw_name = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_passwd != NULL) {
+ size = strlen(new_pwd.pw_passwd);
+ memcpy(p, new_pwd.pw_passwd, size);
+ new_pwd.pw_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_class != NULL) {
+ size = strlen(new_pwd.pw_class);
+ memcpy(p, new_pwd.pw_class, size);
+ new_pwd.pw_class = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_gecos != NULL) {
+ size = strlen(new_pwd.pw_gecos);
+ memcpy(p, new_pwd.pw_gecos, size);
+ new_pwd.pw_gecos = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_dir != NULL) {
+ size = strlen(new_pwd.pw_dir);
+ memcpy(p, new_pwd.pw_dir, size);
+ new_pwd.pw_dir = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_shell != NULL) {
+ size = strlen(new_pwd.pw_shell);
+ memcpy(p, new_pwd.pw_shell, size);
+ new_pwd.pw_shell = p;
+ p += size + 1;
+ }
+
+ memcpy(buffer, &new_pwd, sizeof(struct passwd));
+ return (NS_SUCCESS);
+}
+
+static int
+pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ uid_t uid;
+ struct passwd *pwd;
+ char *orig_buf;
+ size_t orig_buf_size;
+ int *ret_errno;
+
+ char *p;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ uid = va_arg(ap, uid_t);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ pwd = va_arg(ap, struct passwd *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int *);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct passwd) - sizeof(char *)) {
+ *ret_errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(pwd, buffer, sizeof(struct passwd));
+ memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *));
+ memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *),
+ buffer_size - sizeof(struct passwd) - sizeof(char *));
+
+ NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *);
+ NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *);
+ NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *);
+ NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *);
+ NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *);
+ NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *);
+
+ if (retval != NULL)
+ *((struct passwd **)retval) = pwd;
+
+ return (NS_SUCCESS);
+}
+
+NSS_MP_CACHE_HANDLING(passwd);
+#endif /* NS_CACHING */
+
void
setpwent(void)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ passwd, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setpwent, (void *)SETPWENT },
#ifdef HESIOD
@@ -207,6 +434,9 @@ setpwent(void)
{ NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
#endif
{ NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0);
@@ -216,6 +446,12 @@ setpwent(void)
int
setpassent(int stayopen)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ passwd, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setpwent, (void *)SETPWENT },
#ifdef HESIOD
@@ -225,6 +461,9 @@ setpassent(int stayopen)
{ NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
#endif
{ NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc,
@@ -236,6 +475,12 @@ setpassent(int stayopen)
void
endpwent(void)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ passwd, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_setpwent, (void *)ENDPWENT },
#ifdef HESIOD
@@ -245,6 +490,9 @@ endpwent(void)
{ NSSRC_NIS, nis_setpwent, (void *)ENDPWENT },
#endif
{ NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
(void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc);
@@ -255,6 +503,12 @@ int
getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
struct passwd **result)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ passwd, (void *)nss_lt_all,
+ pwd_marshal_func, pwd_unmarshal_func);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_passwd, (void *)nss_lt_all },
#ifdef HESIOD
@@ -264,6 +518,9 @@ getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
{ NSSRC_NIS, nis_passwd, (void *)nss_lt_all },
#endif
{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
@@ -284,6 +541,13 @@ int
getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize,
struct passwd **result)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ passwd, (void *)nss_lt_name,
+ pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_passwd, (void *)nss_lt_name },
#ifdef HESIOD
@@ -293,6 +557,9 @@ getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize,
{ NSSRC_NIS, nis_passwd, (void *)nss_lt_name },
#endif
{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
@@ -313,6 +580,13 @@ int
getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
struct passwd **result)
{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ passwd, (void *)nss_lt_id,
+ pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
+#endif
+
static const ns_dtab dtab[] = {
{ NSSRC_FILES, files_passwd, (void *)nss_lt_id },
#ifdef HESIOD
@@ -322,6 +596,9 @@ getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
{ NSSRC_NIS, nis_passwd, (void *)nss_lt_id },
#endif
{ NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ NULL, NULL, NULL }
};
int rv, ret_errno;
diff --git a/lib/libc/include/nscache.h b/lib/libc/include/nscache.h
new file mode 100644
index 0000000..015c4ec
--- /dev/null
+++ b/lib/libc/include/nscache.h
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __NS_CACHE_H__
+#define __NS_CACHE_H__
+
+#include "nscachedcli.h"
+
+typedef int (*nss_cache_id_func_t)(char *, size_t *, va_list, void *);
+typedef int (*nss_cache_marshal_func_t)(char *, size_t *, void *, va_list,
+ void *);
+typedef int (*nss_cache_unmarshal_func_t)(char *, size_t, void *, va_list,
+ void *);
+
+typedef void (*nss_set_mp_ws_func_t)(cached_mp_write_session);
+typedef cached_mp_write_session (*nss_get_mp_ws_func_t)(void);
+
+typedef void (*nss_set_mp_rs_func_t)(cached_mp_read_session);
+typedef cached_mp_read_session (*nss_get_mp_rs_func_t)(void);
+
+typedef struct _nss_cache_info {
+ char *entry_name;
+ void *mdata;
+
+ /*
+ * These 3 functions should be implemented specifically for each
+ * nsswitch database.
+ */
+ nss_cache_id_func_t id_func; /* marshals the request parameters */
+ nss_cache_marshal_func_t marshal_func; /* marshals response */
+ nss_cache_unmarshal_func_t unmarshal_func; /* unmarshals response */
+
+ /*
+ * These 4 functions should be generated with NSS_MP_CACHE_HANDLING
+ * macro.
+ */
+ nss_set_mp_ws_func_t set_mp_ws_func; /* sets current write session */
+ nss_get_mp_ws_func_t get_mp_ws_func; /* gets current write session */
+
+ nss_set_mp_rs_func_t set_mp_rs_func; /* sets current read session */
+ nss_get_mp_rs_func_t get_mp_rs_func; /* gets current read session */
+} nss_cache_info;
+
+/*
+ * NSS_MP_CACHE_HANDLING implements the set_mp_ws, get_mp_ws, set_mp_rs,
+ * get_mp_rs functions, that are used in _nss_cache_info. It uses
+ * NSS_TLS_HANDLING macro to organize thread local storage.
+ */
+#define NSS_MP_CACHE_HANDLING(name) \
+struct name##_mp_state { \
+ cached_mp_write_session mp_write_session; \
+ cached_mp_read_session mp_read_session; \
+}; \
+ \
+static void \
+name##_mp_endstate(void *s) { \
+ struct name##_mp_state *mp_state; \
+ \
+ mp_state = (struct name##_mp_state *)s; \
+ if (mp_state->mp_write_session != INVALID_CACHED_MP_WRITE_SESSION)\
+ __abandon_cached_mp_write_session(mp_state->mp_write_session);\
+ \
+ if (mp_state->mp_read_session != INVALID_CACHED_MP_READ_SESSION)\
+ __close_cached_mp_read_session(mp_state->mp_read_session);\
+} \
+NSS_TLS_HANDLING(name##_mp); \
+ \
+static void \
+name##_set_mp_ws(cached_mp_write_session ws) \
+{ \
+ struct name##_mp_state *mp_state; \
+ int res; \
+ \
+ res = name##_mp_getstate(&mp_state); \
+ if (res != 0) \
+ return; \
+ \
+ mp_state->mp_write_session = ws; \
+} \
+ \
+static cached_mp_write_session \
+name##_get_mp_ws(void) \
+{ \
+ struct name##_mp_state *mp_state; \
+ int res; \
+ \
+ res = name##_mp_getstate(&mp_state); \
+ if (res != 0) \
+ return (INVALID_CACHED_MP_WRITE_SESSION); \
+ \
+ return (mp_state->mp_write_session); \
+} \
+ \
+static void \
+name##_set_mp_rs(cached_mp_read_session rs) \
+{ \
+ struct name##_mp_state *mp_state; \
+ int res; \
+ \
+ res = name##_mp_getstate(&mp_state); \
+ if (res != 0) \
+ return; \
+ \
+ mp_state->mp_read_session = rs; \
+} \
+ \
+static cached_mp_read_session \
+name##_get_mp_rs(void) \
+{ \
+ struct name##_mp_state *mp_state; \
+ int res; \
+ \
+ res = name##_mp_getstate(&mp_state); \
+ if (res != 0) \
+ return (INVALID_CACHED_MP_READ_SESSION); \
+ \
+ return (mp_state->mp_read_session); \
+}
+
+/*
+ * These macros should be used to initialize _nss_cache_info structure. For
+ * multipart queries in setXXXent and getXXXent functions mf and uf
+ * (marshal function and unmarshal function) should be both NULL.
+ */
+#define NS_COMMON_CACHE_INFO_INITIALIZER(name, mdata, if, mf, uf) \
+ {#name, mdata, if, mf, uf, NULL, NULL, NULL, NULL}
+#define NS_MP_CACHE_INFO_INITIALIZER(name, mdata, mf, uf) \
+ {#name, mdata, NULL, mf, uf, name##_set_mp_ws, name##_get_mp_ws,\
+ name##_set_mp_rs, name##_get_mp_rs }
+
+/*
+ * Analog of other XXX_CB macros. Has the pointer to _nss_cache_info
+ * structure as the only argument.
+ */
+#define NS_CACHE_CB(cinfo) {NSSRC_CACHE, __nss_cache_handler, (void *)(cinfo) },
+
+/* args are: current pointer, current buffer, initial buffer, pointer type */
+#define NS_APPLY_OFFSET(cp, cb, ib, p_type) \
+ if ((cp) != NULL) \
+ (cp) = (p_type)((char *)(cb) + (size_t)(cp) - (size_t)(ib))
+/*
+ * Gets new pointer from the marshalled buffer by uisng initial address
+ * and initial buffer address
+ */
+#define NS_GET_NEWP(cp, cb, ib) \
+ ((char *)(cb) + (size_t)(cp) - (size_t)(ib))
+
+typedef struct _nss_cache_data {
+ char *key;
+ size_t key_size;
+
+ nss_cache_info const *info;
+} nss_cache_data;
+
+__BEGIN_DECLS
+/* dummy function, which is needed to make nss_method_lookup happy */
+extern int __nss_cache_handler(void *, void *, va_list);
+
+#ifdef _NS_PRIVATE
+extern int __nss_common_cache_read(void *, void *, va_list);
+extern int __nss_common_cache_write(void *, void *, va_list);
+extern int __nss_common_cache_write_negative(void *);
+
+extern int __nss_mp_cache_read(void *, void *, va_list);
+extern int __nss_mp_cache_write(void *, void *, va_list);
+extern int __nss_mp_cache_write_submit(void *, void *, va_list);
+extern int __nss_mp_cache_end(void *, void *, va_list);
+#endif /* _NS_PRIVATE */
+
+__END_DECLS
+
+#endif
diff --git a/lib/libc/include/nscachedcli.h b/lib/libc/include/nscachedcli.h
new file mode 100644
index 0000000..b0f79bd
--- /dev/null
+++ b/lib/libc/include/nscachedcli.h
@@ -0,0 +1,107 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __NS_CACHED_CLI_H__
+#define __NS_CACHED_CLI_H__
+
+/*
+ * This file contains API for working with caching daemon
+ */
+
+enum comm_element_t {
+ CET_UNDEFINED = 0,
+ CET_WRITE_REQUEST = 1,
+ CET_WRITE_RESPONSE = 2,
+ CET_READ_REQUEST = 3,
+ CET_READ_RESPONSE = 4,
+ CET_TRANSFORM_REQUEST = 5,
+ CET_TRANSFORM_RESPONSE = 6,
+ CET_MP_WRITE_SESSION_REQUEST = 7,
+ CET_MP_WRITE_SESSION_RESPONSE = 8,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST = 9,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10,
+ CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11,
+ CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12,
+ CET_MP_READ_SESSION_REQUEST = 13,
+ CET_MP_READ_SESSION_RESPONSE = 14,
+ CET_MP_READ_SESSION_READ_REQUEST = 15,
+ CET_MP_READ_SESSION_READ_RESPONSE = 16,
+ CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17
+};
+
+struct cached_connection_params {
+ char *socket_path;
+ struct timeval timeout;
+};
+
+struct cached_connection_ {
+ int sockfd;
+ int read_queue;
+ int write_queue;
+
+ int mp_flag; /* shows if the connection is used for
+ * multipart operations */
+};
+
+/* simple abstractions for not to write "struct" every time */
+typedef struct cached_connection_ *cached_connection;
+typedef struct cached_connection_ *cached_mp_write_session;
+typedef struct cached_connection_ *cached_mp_read_session;
+
+#define INVALID_CACHED_CONNECTION (NULL)
+#define INVALID_CACHED_MP_WRITE_SESSION (NULL)
+#define INVALID_CACHED_MP_READ_SESSION (NULL)
+
+__BEGIN_DECLS
+
+/* initialization/destruction routines */
+extern cached_connection __open_cached_connection(
+ struct cached_connection_params const *);
+extern void __close_cached_connection(cached_connection);
+
+/* simple read/write operations */
+extern int __cached_write(cached_connection, const char *, const char *,
+ size_t, const char *, size_t);
+extern int __cached_read(cached_connection, const char *, const char *,
+ size_t, char *, size_t *);
+
+/* multipart read/write operations */
+extern cached_mp_write_session __open_cached_mp_write_session(
+ struct cached_connection_params const *, const char *);
+extern int __cached_mp_write(cached_mp_write_session, const char *, size_t);
+extern int __abandon_cached_mp_write_session(cached_mp_write_session);
+extern int __close_cached_mp_write_session(cached_mp_write_session);
+
+extern cached_mp_read_session __open_cached_mp_read_session(
+ struct cached_connection_params const *, const char *);
+extern int __cached_mp_read(cached_mp_read_session, char *, size_t *);
+extern int __close_cached_mp_read_session(cached_mp_read_session);
+
+__END_DECLS
+
+#endif
diff --git a/lib/libc/net/Makefile.inc b/lib/libc/net/Makefile.inc
index 3c363cd..9921f8d 100644
--- a/lib/libc/net/Makefile.inc
+++ b/lib/libc/net/Makefile.inc
@@ -9,13 +9,16 @@ SRCS+= addr2ascii.c ascii2addr.c base64.c ether_addr.c eui64.c \
gethostbydns.c gethostbyht.c gethostbynis.c gethostnamadr.c \
getifaddrs.c getifmaddrs.c getnameinfo.c \
getnetbydns.c getnetbyht.c getnetbynis.c getnetnamadr.c \
- getproto.c getprotoent.c getprotoname.c getservbyname.c \
- getservbyport.c getservent.c \
+ getproto.c getprotoent.c getprotoname.c getservent.c \
if_indextoname.c if_nameindex.c if_nametoindex.c \
ip6opt.c linkaddr.c map_v4v6.c name6.c \
nsdispatch.c nslexer.c nsparser.c nss_compat.c \
rcmd.c rcmdsh.c recv.c rthdr.c send.c sockatmark.c vars.c
+.if ${MK_NS_CACHING} != "no"
+SRCS+= nscache.c nscachedcli.c
+.endif
+
# for binary backward compatibility against FreeBSD 6.X and earlier
SRCS+= res_mkupdate.c res_update.c
diff --git a/lib/libc/net/getaddrinfo.c b/lib/libc/net/getaddrinfo.c
index a2552a9..b422c4b 100644
--- a/lib/libc/net/getaddrinfo.c
+++ b/lib/libc/net/getaddrinfo.c
@@ -103,6 +103,9 @@ __FBSDID("$FreeBSD$");
#include <nsswitch.h>
#include "un-namespace.h"
#include "libc_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
#if defined(__KAME__) && defined(INET6)
# define FAITH
@@ -279,6 +282,11 @@ static int _files_getaddrinfo(void *, void *, va_list);
static struct addrinfo *_yphostent(char *, const struct addrinfo *);
static int _yp_getaddrinfo(void *, void *, va_list);
#endif
+#ifdef NS_CACHING
+static int addrinfo_id_func(char *, size_t *, va_list, void *);
+static int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *);
+static int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
static int res_queryN(const char *, struct res_target *, res_state);
static int res_searchN(const char *, struct res_target *, res_state);
@@ -1525,6 +1533,185 @@ ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid)
}
#endif
+
+#ifdef NS_CACHING
+static int
+addrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap,
+ void *cache_mdata)
+{
+ res_state statp;
+ u_long res_options;
+
+ const int op_id = 0; /* identifies the getaddrinfo for the cache */
+ char *hostname;
+ struct addrinfo *hints;
+
+ char *p;
+ int ai_flags, ai_family, ai_socktype, ai_protocol;
+ size_t desired_size, size;
+
+ statp = __res_state();
+ res_options = statp->options & (RES_RECURSE | RES_DEFNAMES |
+ RES_DNSRCH | RES_NOALIASES | RES_USE_INET6);
+
+ hostname = va_arg(ap, char *);
+ hints = va_arg(ap, struct addrinfo *);
+
+ desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4;
+ if (hostname != NULL) {
+ size = strlen(hostname);
+ desired_size += size + 1;
+ } else
+ size = 0;
+
+ if (desired_size > *buffer_size) {
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ if (hints == NULL)
+ ai_flags = ai_family = ai_socktype = ai_protocol = 0;
+ else {
+ ai_flags = hints->ai_flags;
+ ai_family = hints->ai_family;
+ ai_socktype = hints->ai_socktype;
+ ai_protocol = hints->ai_protocol;
+ }
+
+ p = buffer;
+ memcpy(p, &res_options, sizeof(res_options));
+ p += sizeof(res_options);
+
+ memcpy(p, &op_id, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &ai_flags, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &ai_family, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &ai_socktype, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &ai_protocol, sizeof(int));
+ p += sizeof(int);
+
+ if (hostname != NULL)
+ memcpy(p, hostname, size);
+
+ *buffer_size = desired_size;
+ return (NS_SUCCESS);
+}
+
+static int
+addrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval,
+ va_list ap, void *cache_mdata)
+{
+ struct addrinfo *ai, *cai;
+ char *p;
+ size_t desired_size, size, ai_size;
+
+ ai = *((struct addrinfo **)retval);
+
+ desired_size = sizeof(size_t);
+ ai_size = 0;
+ for (cai = ai; cai != NULL; cai = cai->ai_next) {
+ desired_size += sizeof(struct addrinfo) + cai->ai_addrlen;
+ if (cai->ai_canonname != NULL)
+ desired_size += sizeof(size_t) +
+ strlen(cai->ai_canonname);
+ ++ai_size;
+ }
+
+ if (desired_size > *buffer_size) {
+ /* this assignment is here for future use */
+ errno = ERANGE;
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memset(buffer, 0, desired_size);
+ p = buffer;
+
+ memcpy(p, &ai_size, sizeof(size_t));
+ p += sizeof(size_t);
+ for (cai = ai; cai != NULL; cai = cai->ai_next) {
+ memcpy(p, cai, sizeof(struct addrinfo));
+ p += sizeof(struct addrinfo);
+
+ memcpy(p, cai->ai_addr, cai->ai_addrlen);
+ p += cai->ai_addrlen;
+
+ if (cai->ai_canonname != NULL) {
+ size = strlen(cai->ai_canonname);
+ memcpy(p, &size, sizeof(size_t));
+ p += sizeof(size_t);
+
+ memcpy(p, cai->ai_canonname, size);
+ p += size;
+ }
+ }
+
+ return (NS_SUCCESS);
+}
+
+static int
+addrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval,
+ va_list ap, void *cache_mdata)
+{
+ struct addrinfo new_ai, *result, *sentinel, *lasts;
+
+ char *p;
+ size_t ai_size, ai_i, size;
+
+ p = buffer;
+ memcpy(&ai_size, p, sizeof(size_t));
+ p += sizeof(size_t);
+
+ result = NULL;
+ lasts = NULL;
+ for (ai_i = 0; ai_i < ai_size; ++ai_i) {
+ memcpy(&new_ai, p, sizeof(struct addrinfo));
+ p += sizeof(struct addrinfo);
+ size = new_ai.ai_addrlen + sizeof(struct addrinfo) +
+ _ALIGNBYTES;
+
+ sentinel = (struct addrinfo *)malloc(size);
+ memset(sentinel, 0, size);
+
+ memcpy(sentinel, &new_ai, sizeof(struct addrinfo));
+ sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel +
+ sizeof(struct addrinfo));
+
+ memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen);
+ p += new_ai.ai_addrlen;
+
+ if (new_ai.ai_canonname != NULL) {
+ memcpy(&size, p, sizeof(size_t));
+ p += sizeof(size_t);
+
+ sentinel->ai_canonname = (char *)malloc(size + 1);
+ memset(sentinel->ai_canonname, 0, size + 1);
+
+ memcpy(sentinel->ai_canonname, p, size);
+ p += size;
+ }
+
+ if (result == NULL) {
+ result = sentinel;
+ lasts = sentinel;
+ } else {
+ lasts->ai_next = sentinel;
+ lasts = sentinel;
+ }
+ }
+
+ *((struct addrinfo **)retval) = result;
+ return (NS_SUCCESS);
+}
+#endif /* NS_CACHING */
+
/*
* FQDN hostname, DNS lookup
*/
@@ -1535,10 +1722,20 @@ explore_fqdn(const struct addrinfo *pai, const char *hostname,
struct addrinfo *result;
struct addrinfo *cur;
int error = 0;
+
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ hosts, NULL, addrinfo_id_func, addrinfo_marshal_func,
+ addrinfo_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_getaddrinfo, NULL)
{ NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */
NS_NIS_CB(_yp_getaddrinfo, NULL)
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
diff --git a/lib/libc/net/gethostnamadr.c b/lib/libc/net/gethostnamadr.c
index c8867e5..965af66 100644
--- a/lib/libc/net/gethostnamadr.c
+++ b/lib/libc/net/gethostnamadr.c
@@ -44,6 +44,9 @@ __FBSDID("$FreeBSD$");
#include <resolv.h> /* XXX hack for _res */
#include "un-namespace.h"
#include "netdb_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
extern int _ht_gethostbyname(void *, void *, va_list);
extern int _dns_gethostbyname(void *, void *, va_list);
@@ -61,6 +64,11 @@ static const ns_src default_src[] = {
{ NSSRC_DNS, NS_SUCCESS },
{ 0 }
};
+#ifdef NS_CACHING
+static int host_id_func(char *, size_t *, va_list, void *);
+static int host_marshal_func(char *, size_t *, void *, va_list, void *);
+static int host_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
NETDB_THREAD_ALLOC(hostent)
NETDB_THREAD_ALLOC(hostent_data)
@@ -152,9 +160,277 @@ __copy_hostent(struct hostent *he, struct hostent *hptr, char *buf,
return (0);
}
+#ifdef NS_CACHING
+static int
+host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
+{
+ res_state statp;
+ u_long res_options;
+
+ const int op_id = 1;
+ char *str;
+ int len, type;
+
+ size_t desired_size, size;
+ enum nss_lookup_type lookup_type;
+ char *p;
+ int res = NS_UNAVAIL;
+
+ statp = __res_state();
+ res_options = statp->options & (RES_RECURSE | RES_DEFNAMES |
+ RES_DNSRCH | RES_NOALIASES | RES_USE_INET6);
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ str = va_arg(ap, char *);
+ type = va_arg(ap, int);
+
+ size = strlen(str);
+ desired_size = sizeof(res_options) + sizeof(int) +
+ sizeof(enum nss_lookup_type) + sizeof(int) + size + 1;
+
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ p = buffer;
+
+ memcpy(p, &res_options, sizeof(res_options));
+ p += sizeof(res_options);
+
+ memcpy(p, &op_id, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
+ p += sizeof(int);
+
+ memcpy(p, &type, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, str, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ str = va_arg(ap, char *);
+ len = va_arg(ap, int);
+ type = va_arg(ap, int);
+
+ desired_size = sizeof(res_options) + sizeof(int) +
+ sizeof(enum nss_lookup_type) + sizeof(int) * 2 + len;
+
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ p = buffer;
+ memcpy(p, &res_options, sizeof(res_options));
+ p += sizeof(res_options);
+
+ memcpy(p, &op_id, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
+ p += sizeof(int);
+
+ memcpy(p, &type, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &len, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, str, len);
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+static int
+host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *str;
+ int len, type;
+ struct hostent *ht;
+
+ struct hostent new_ht;
+ size_t desired_size, aliases_size, addr_size, size;
+ char *p, **iter;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ str = va_arg(ap, char *);
+ type = va_arg(ap, int);
+ break;
+ case nss_lt_id:
+ str = va_arg(ap, char *);
+ len = va_arg(ap, int);
+ type = va_arg(ap, int);
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+ ht = va_arg(ap, struct hostent *);
+
+ desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *);
+ if (ht->h_name != NULL)
+ desired_size += strlen(ht->h_name) + 1;
+
+ if (ht->h_aliases != NULL) {
+ aliases_size = 0;
+ for (iter = ht->h_aliases; *iter; ++iter) {
+ desired_size += strlen(*iter) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += _ALIGNBYTES +
+ (aliases_size + 1) * sizeof(char *);
+ }
+
+ if (ht->h_addr_list != NULL) {
+ addr_size = 0;
+ for (iter = ht->h_addr_list; *iter; ++iter)
+ ++addr_size;
+
+ desired_size += addr_size * _ALIGN(ht->h_length);
+ desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *);
+ }
+
+ if (desired_size > *buffer_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_ht, ht, sizeof(struct hostent));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct hostent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_ht.h_name != NULL) {
+ size = strlen(new_ht.h_name);
+ memcpy(p, new_ht.h_name, size);
+ new_ht.h_name = p;
+ p += size + 1;
+ }
+
+ if (new_ht.h_aliases != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size);
+ new_ht.h_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (iter = new_ht.h_aliases; *iter; ++iter) {
+ size = strlen(*iter);
+ memcpy(p, *iter, size);
+ *iter = p;
+ p += size + 1;
+ }
+ }
+
+ if (new_ht.h_addr_list != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size);
+ new_ht.h_addr_list = (char **)p;
+ p += sizeof(char *) * (addr_size + 1);
+
+ size = _ALIGN(new_ht.h_length);
+ for (iter = new_ht.h_addr_list; *iter; ++iter) {
+ memcpy(p, *iter, size);
+ *iter = p;
+ p += size + 1;
+ }
+ }
+ memcpy(buffer, &new_ht, sizeof(struct hostent));
+ return (NS_SUCCESS);
+}
+
+static int
+host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *str;
+ int len, type;
+ struct hostent *ht;
+
+ char *p;
+ char **iter;
+ char *orig_buf;
+ size_t orig_buf_size;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ str = va_arg(ap, char *);
+ type = va_arg(ap, int);
+ break;
+ case nss_lt_id:
+ str = va_arg(ap, char *);
+ len = va_arg(ap, int);
+ type = va_arg(ap, int);
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ ht = va_arg(ap, struct hostent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct hostent) - sizeof(char *)) {
+ errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(ht, buffer, sizeof(struct hostent));
+ memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *));
+
+ orig_buf = (char *)_ALIGN(orig_buf);
+ memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) +
+ _ALIGN(p) - (size_t)p,
+ buffer_size - sizeof(struct hostent) - sizeof(char *) -
+ _ALIGN(p) + (size_t)p);
+ p = (char *)_ALIGN(p);
+
+ NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *);
+ if (ht->h_aliases != NULL) {
+ NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **);
+
+ for (iter = ht->h_aliases; *iter; ++iter)
+ NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
+ }
+
+ if (ht->h_addr_list != NULL) {
+ NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **);
+
+ for (iter = ht->h_addr_list; *iter; ++iter)
+ NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
+ }
+
+ *((struct hostent **)retval) = ht;
+ return (NS_SUCCESS);
+}
+#endif /* NS_CACHING */
+
static int
fakeaddr(const char *name, int af, struct hostent *hp, char *buf,
-size_t buflen, res_state statp)
+ size_t buflen, res_state statp)
{
struct hostent_data *hed;
struct hostent he;
@@ -248,10 +524,19 @@ gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf,
int rval, ret_errno;
char abuf[MAXDNAME];
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ hosts, (void *)nss_lt_name,
+ host_id_func, host_marshal_func, host_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_ht_gethostbyname, NULL)
{ NSSRC_DNS, _dns_gethostbyname, NULL },
NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
@@ -297,10 +582,19 @@ gethostbyaddr_r(const char *addr, int len, int af, struct hostent *hp,
int rval, ret_errno;
res_state statp;
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ hosts, (void *)nss_lt_id,
+ host_id_func, host_marshal_func, host_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_ht_gethostbyaddr, NULL)
{ NSSRC_DNS, _dns_gethostbyaddr, NULL },
NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
diff --git a/lib/libc/net/getnetnamadr.c b/lib/libc/net/getnetnamadr.c
index 633cb30..ec7e94b 100644
--- a/lib/libc/net/getnetnamadr.c
+++ b/lib/libc/net/getnetnamadr.c
@@ -42,6 +42,9 @@ __FBSDID("$FreeBSD$");
#include <nsswitch.h>
#include "un-namespace.h"
#include "netdb_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
extern int _ht_getnetbyname(void *, void *, va_list);
extern int _dns_getnetbyname(void *, void *, va_list);
@@ -60,6 +63,220 @@ static const ns_src default_src[] = {
NETDB_THREAD_ALLOC(netent_data)
NETDB_THREAD_ALLOC(netdata)
+#ifdef NS_CACHING
+static int
+net_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
+{
+ char *name;
+ uint32_t net;
+ int type;
+
+ size_t desired_size, size;
+ enum nss_lookup_type lookup_type;
+ int res = NS_UNAVAIL;
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+
+ size = strlen(name);
+ desired_size = sizeof(enum nss_lookup_type) + size + 1;
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ net = va_arg(ap, uint32_t);
+ type = va_arg(ap, int);
+
+ desired_size = sizeof(enum nss_lookup_type) +
+ sizeof(uint32_t) + sizeof(int);
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), &net,
+ sizeof(uint32_t));
+ memcpy(buffer + sizeof(enum nss_lookup_type) + sizeof(uint32_t),
+ &type, sizeof(int));
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+
+static int
+net_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ uint32_t net;
+ int type;
+ struct netent *ne;
+ char *orig_buf;
+ size_t orig_buf_size;
+
+ struct netent new_ne;
+ size_t desired_size, size, aliases_size;
+ char *p;
+ char **alias;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ net = va_arg(ap, uint32_t);
+ type = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ ne = va_arg(ap, struct netent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ desired_size = _ALIGNBYTES + sizeof(struct netent) + sizeof(char *);
+ if (ne->n_name != NULL)
+ desired_size += strlen(ne->n_name) + 1;
+
+ if (ne->n_aliases != NULL) {
+ aliases_size = 0;
+ for (alias = ne->n_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += _ALIGNBYTES +
+ (aliases_size + 1) * sizeof(char *);
+ }
+
+ if (*buffer_size < desired_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_ne, ne, sizeof(struct netent));
+
+ *buffer_size = desired_size;
+ memset(buffer, 0, desired_size);
+ p = buffer + sizeof(struct netent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct netent), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_ne.n_name != NULL) {
+ size = strlen(new_ne.n_name);
+ memcpy(p, new_ne.n_name, size);
+ new_ne.n_name = p;
+ p += size + 1;
+ }
+
+ if (new_ne.n_aliases != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_ne.n_aliases, sizeof(char *) * aliases_size);
+ new_ne.n_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (alias = new_ne.n_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_ne, sizeof(struct netent));
+ return (NS_SUCCESS);
+}
+
+static int
+net_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ uint32_t net;
+ int type;
+ struct netent *ne;
+ char *orig_buf;
+ size_t orig_buf_size;
+ int *ret_errno;
+
+ char *p;
+ char **alias;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ net = va_arg(ap, uint32_t);
+ type = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ ne = va_arg(ap, struct netent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int *);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct netent) - sizeof(char *)) {
+ *ret_errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(ne, buffer, sizeof(struct netent));
+ memcpy(&p, buffer + sizeof(struct netent), sizeof(char *));
+
+ orig_buf = (char *)_ALIGN(orig_buf);
+ memcpy(orig_buf, buffer + sizeof(struct netent) + sizeof(char *) +
+ _ALIGN(p) - (size_t)p,
+ buffer_size - sizeof(struct netent) - sizeof(char *) -
+ _ALIGN(p) + (size_t)p);
+ p = (char *)_ALIGN(p);
+
+ NS_APPLY_OFFSET(ne->n_name, orig_buf, p, char *);
+ if (ne->n_aliases != NULL) {
+ NS_APPLY_OFFSET(ne->n_aliases, orig_buf, p, char **);
+
+ for (alias = ne->n_aliases; *alias; ++alias)
+ NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
+ }
+
+ if (retval != NULL)
+ *((struct netent **)retval) = ne;
+
+ return (NS_SUCCESS);
+}
+#endif /* NS_CACHING */
+
static void
netent_data_free(void *ptr)
{
@@ -128,14 +345,22 @@ int
getnetbyname_r(const char *name, struct netent *ne, char *buffer,
size_t buflen, struct netent **result, int *h_errorp)
{
- int rval, ret_errno;
-
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ networks, (void *)nss_lt_name,
+ net_id_func, net_marshal_func, net_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_ht_getnetbyname, NULL)
{ NSSRC_DNS, _dns_getnetbyname, NULL },
NS_NIS_CB(_nis_getnetbyname, NULL) /* force -DHESIOD */
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
+ int rval, ret_errno;
rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS,
"getnetbyname_r", default_src, name, ne, buffer, buflen,
@@ -148,14 +373,22 @@ int
getnetbyaddr_r(uint32_t addr, int af, struct netent *ne, char *buffer,
size_t buflen, struct netent **result, int *h_errorp)
{
- int rval, ret_errno;
-
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ networks, (void *)nss_lt_id,
+ net_id_func, net_marshal_func, net_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_ht_getnetbyaddr, NULL)
{ NSSRC_DNS, _dns_getnetbyaddr, NULL },
NS_NIS_CB(_nis_getnetbyaddr, NULL) /* force -DHESIOD */
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
+ int rval, ret_errno;
rval = _nsdispatch((void *)result, dtab, NSDB_NETWORKS,
"getnetbyaddr_r", default_src, addr, af, ne, buffer, buflen,
diff --git a/lib/libc/net/getproto.c b/lib/libc/net/getproto.c
index 9a32983..b2a3fe7 100644
--- a/lib/libc/net/getproto.c
+++ b/lib/libc/net/getproto.c
@@ -38,30 +38,96 @@ static char sccsid[] = "@(#)getproto.c 8.1 (Berkeley) 6/4/93";
__FBSDID("$FreeBSD$");
#include <netdb.h>
+#include <nsswitch.h>
#include "netdb_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
+#include "nss_tls.h"
-int
-getprotobynumber_r(int proto, struct protoent *pptr, char *buffer,
- size_t buflen, struct protoent **result)
+static const ns_src defaultsrc[] = {
+ { NSSRC_FILES, NS_SUCCESS },
+ { NULL, 0 }
+};
+
+#ifdef NS_CACHING
+extern int __proto_id_func(char *, size_t *, va_list, void *);
+extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *);
+extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
+
+static int
+files_getprotobynumber(void *retval, void *mdata, va_list ap)
{
struct protoent pe;
struct protoent_data *ped;
int error;
- if ((ped = __protoent_data_init()) == NULL)
- return (-1);
+ int number;
+ struct protoent *pptr;
+ char *buffer;
+ size_t buflen;
+ int *errnop;
+
+ number = va_arg(ap, int);
+ pptr = va_arg(ap, struct protoent *);
+ buffer = va_arg(ap, char *);
+ buflen = va_arg(ap, size_t);
+ errnop = va_arg(ap, int *);
+
+ if ((ped = __protoent_data_init()) == NULL) {
+ *errnop = -1;
+ return (NS_NOTFOUND);
+ }
+
__setprotoent_p(ped->stayopen, ped);
while ((error = __getprotoent_p(&pe, ped)) == 0)
- if (pe.p_proto == proto)
+ if (pe.p_proto == number)
break;
if (!ped->stayopen)
__endprotoent_p(ped);
- if (error != 0)
- return (-1);
- if (__copy_protoent(&pe, pptr, buffer, buflen) != 0)
- return (-1);
- *result = pptr;
- return (0);
+ if (error != 0) {
+ *errnop = -1;
+ return (NS_NOTFOUND);
+ }
+ if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) {
+ *errnop = -1;
+ return (NS_NOTFOUND);
+ }
+
+ *((struct protoent **)retval) = pptr;
+ return (NS_SUCCESS);
+}
+
+int
+getprotobynumber_r(int proto, struct protoent *pptr, char *buffer,
+ size_t buflen, struct protoent **result)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ protocols, (void *)nss_lt_id,
+ __proto_id_func, __proto_marshal_func, __proto_unmarshal_func);
+#endif
+
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_getprotobynumber, NULL },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobynumber_r",
+ defaultsrc, proto, pptr, buffer, buflen, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
}
struct protoent *
diff --git a/lib/libc/net/getprotoent.c b/lib/libc/net/getprotoent.c
index 28f055b..3d3a57e 100644
--- a/lib/libc/net/getprotoent.c
+++ b/lib/libc/net/getprotoent.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <limits.h>
#include <netdb.h>
+#include <nsswitch.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -50,6 +51,15 @@ __FBSDID("$FreeBSD$");
#include "reentrant.h"
#include "un-namespace.h"
#include "netdb_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
+#include "nss_tls.h"
+
+static const ns_src defaultsrc[] = {
+ { NSSRC_FILES, NS_SUCCESS },
+ { NULL, 0 }
+};
NETDB_THREAD_ALLOC(protoent_data)
NETDB_THREAD_ALLOC(protodata)
@@ -78,6 +88,214 @@ protodata_free(void *ptr)
free(ptr);
}
+#ifdef NS_CACHING
+int
+__proto_id_func(char *buffer, size_t *buffer_size, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ int proto;
+
+ size_t desired_size, size;
+ enum nss_lookup_type lookup_type;
+ int res = NS_UNAVAIL;
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+
+ size = strlen(name);
+ desired_size = sizeof(enum nss_lookup_type) + size + 1;
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ proto = va_arg(ap, int);
+
+ desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), &proto,
+ sizeof(int));
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+
+int
+__proto_marshal_func(char *buffer, size_t *buffer_size, void *retval,
+ va_list ap, void *cache_mdata)
+{
+ char *name;
+ int num;
+ struct protoent *proto;
+ char *orig_buf;
+ size_t orig_buf_size;
+
+ struct protoent new_proto;
+ size_t desired_size, size, aliases_size;
+ char *p;
+ char **alias;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ num = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ proto = va_arg(ap, struct protoent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ desired_size = _ALIGNBYTES + sizeof(struct protoent) + sizeof(char *);
+ if (proto->p_name != NULL)
+ desired_size += strlen(proto->p_name) + 1;
+
+ if (proto->p_aliases != NULL) {
+ aliases_size = 0;
+ for (alias = proto->p_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += _ALIGNBYTES + (aliases_size + 1) *
+ sizeof(char *);
+ }
+
+ if (*buffer_size < desired_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_proto, proto, sizeof(struct protoent));
+
+ *buffer_size = desired_size;
+ memset(buffer, 0, desired_size);
+ p = buffer + sizeof(struct protoent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct protoent), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_proto.p_name != NULL) {
+ size = strlen(new_proto.p_name);
+ memcpy(p, new_proto.p_name, size);
+ new_proto.p_name = p;
+ p += size + 1;
+ }
+
+ if (new_proto.p_aliases != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_proto.p_aliases, sizeof(char *) * aliases_size);
+ new_proto.p_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (alias = new_proto.p_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_proto, sizeof(struct protoent));
+ return (NS_SUCCESS);
+}
+
+int
+__proto_unmarshal_func(char *buffer, size_t buffer_size, void *retval,
+ va_list ap, void *cache_mdata)
+{
+ char *name;
+ int num;
+ struct protoent *proto;
+ char *orig_buf;
+ size_t orig_buf_size;
+ int *ret_errno;
+
+ char *p;
+ char **alias;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ num = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ proto = va_arg(ap, struct protoent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int *);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct protoent) - sizeof(char *)) {
+ *ret_errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(proto, buffer, sizeof(struct protoent));
+ memcpy(&p, buffer + sizeof(struct protoent), sizeof(char *));
+
+ orig_buf = (char *)_ALIGN(orig_buf);
+ memcpy(orig_buf, buffer + sizeof(struct protoent) + sizeof(char *) +
+ _ALIGN(p) - (size_t)p,
+ buffer_size - sizeof(struct protoent) - sizeof(char *) -
+ _ALIGN(p) + (size_t)p);
+ p = (char *)_ALIGN(p);
+
+ NS_APPLY_OFFSET(proto->p_name, orig_buf, p, char *);
+ if (proto->p_aliases != NULL) {
+ NS_APPLY_OFFSET(proto->p_aliases, orig_buf, p, char **);
+
+ for (alias = proto->p_aliases; *alias; ++alias)
+ NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
+ }
+
+ if (retval != NULL)
+ *((struct protoent **)retval) = proto;
+
+ return (NS_SUCCESS);
+}
+
+NSS_MP_CACHE_HANDLING(protocols);
+#endif /* NS_CACHING */
+
int
__copy_protoent(struct protoent *pe, struct protoent *pptr, char *buf,
size_t buflen)
@@ -194,42 +412,133 @@ again:
return (0);
}
-int
-getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen,
- struct protoent **result)
+static int
+files_getprotoent_r(void *retval, void *mdata, va_list ap)
{
struct protoent pe;
struct protoent_data *ped;
+ struct protoent *pptr;
+ char *buffer;
+ size_t buflen;
+ int *errnop;
+
+ pptr = va_arg(ap, struct protoent *);
+ buffer = va_arg(ap, char *);
+ buflen = va_arg(ap, size_t);
+ errnop = va_arg(ap, int *);
+
if ((ped = __protoent_data_init()) == NULL)
return (-1);
- if (__getprotoent_p(&pe, ped) != 0)
- return (-1);
- if (__copy_protoent(&pe, pptr, buffer, buflen) != 0)
- return (-1);
- *result = pptr;
- return (0);
+ if (__getprotoent_p(&pe, ped) != 0) {
+ *errnop = errno;
+ return (NS_NOTFOUND);
+ }
+
+ if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) {
+ *errnop = errno;
+ return (NS_NOTFOUND);
+ }
+
+ *((struct protoent **)retval) = pptr;
+ return (NS_SUCCESS);
}
-void
-setprotoent(int f)
+static int
+files_setprotoent(void *retval, void *mdata, va_list ap)
{
struct protoent_data *ped;
+ int f;
+ f = va_arg(ap, int);
if ((ped = __protoent_data_init()) == NULL)
- return;
+ return (NS_UNAVAIL);
+
__setprotoent_p(f, ped);
+ return (NS_UNAVAIL);
}
-void
-endprotoent(void)
+static int
+files_endprotoent(void *retval, void *mdata, va_list ap)
{
struct protoent_data *ped;
if ((ped = __protoent_data_init()) == NULL)
- return;
+ return (NS_UNAVAIL);
+
__endprotoent_p(ped);
+ return (NS_UNAVAIL);
+}
+
+int
+getprotoent_r(struct protoent *pptr, char *buffer, size_t buflen,
+ struct protoent **result)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ protocols, (void *)nss_lt_all,
+ __proto_marshal_func, __proto_unmarshal_func);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_getprotoent_r, (void *)nss_lt_all },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotoent_r",
+ defaultsrc, pptr, buffer, buflen, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
+}
+
+void
+setprotoent(int stayopen)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ protocols, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setprotoent, NULL },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+
+ (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "setprotoent", defaultsrc,
+ stayopen);
+}
+
+void
+endprotoent(void)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ protocols, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_endprotoent, NULL },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+
+ (void)nsdispatch(NULL, dtab, NSDB_PROTOCOLS, "endprotoent", defaultsrc);
}
struct protoent *
diff --git a/lib/libc/net/getprotoname.c b/lib/libc/net/getprotoname.c
index afe3f9d47..4ef50e3 100644
--- a/lib/libc/net/getprotoname.c
+++ b/lib/libc/net/getprotoname.c
@@ -38,20 +38,50 @@ static char sccsid[] = "@(#)getprotoname.c 8.1 (Berkeley) 6/4/93";
__FBSDID("$FreeBSD$");
#include <netdb.h>
+#include <nsswitch.h>
#include <string.h>
#include "netdb_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
+#include "nss_tls.h"
-int
-getprotobyname_r(const char *name, struct protoent *pptr, char *buffer,
- size_t buflen, struct protoent **result)
+static const ns_src defaultsrc[] = {
+ { NSSRC_FILES, NS_SUCCESS },
+ { NULL, 0 }
+};
+
+#ifdef NS_CACHING
+extern int __proto_id_func(char *, size_t *, va_list, void *);
+extern int __proto_marshal_func(char *, size_t *, void *, va_list, void *);
+extern int __proto_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
+
+static int
+files_getprotobyname(void *retval, void *mdata, va_list ap)
{
struct protoent pe;
struct protoent_data *ped;
char **cp;
int error;
- if ((ped = __protoent_data_init()) == NULL)
- return (-1);
+ char *name;
+ struct protoent *pptr;
+ char *buffer;
+ size_t buflen;
+ int *errnop;
+
+ name = va_arg(ap, char *);
+ pptr = va_arg(ap, struct protoent *);
+ buffer = va_arg(ap, char *);
+ buflen = va_arg(ap, size_t);
+ errnop = va_arg(ap, int *);
+
+
+ if ((ped = __protoent_data_init()) == NULL) {
+ *errnop = -1;
+ return (NS_NOTFOUND);
+ }
__setprotoent_p(ped->stayopen, ped);
while ((error = __getprotoent_p(&pe, ped)) == 0) {
@@ -64,12 +94,48 @@ getprotobyname_r(const char *name, struct protoent *pptr, char *buffer,
found:
if (!ped->stayopen)
__endprotoent_p(ped);
- if (error != 0)
- return (-1);
- if (__copy_protoent(&pe, pptr, buffer, buflen) != 0)
- return (-1);
- *result = pptr;
- return (0);
+ if (error != 0) {
+ *errnop = -1;
+ return (NS_NOTFOUND);
+ }
+ if (__copy_protoent(&pe, pptr, buffer, buflen) != 0) {
+ *errnop = -1;
+ return (NS_NOTFOUND);
+ }
+
+ *((struct protoent **)retval) = pptr;
+ return (NS_SUCCESS);
+}
+
+
+int
+getprotobyname_r(const char *name, struct protoent *pptr, char *buffer,
+ size_t buflen, struct protoent **result)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ protocols, (void *)nss_lt_name,
+ __proto_id_func, __proto_marshal_func, __proto_unmarshal_func);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_getprotobyname, NULL },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_PROTOCOLS, "getprotobyname_r",
+ defaultsrc, name, pptr, buffer, buflen, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
}
struct protoent *
diff --git a/lib/libc/net/getservbyname.c b/lib/libc/net/getservbyname.c
deleted file mode 100644
index 8870c6c..0000000
--- a/lib/libc/net/getservbyname.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getservbyname.c 8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <netdb.h>
-#include <string.h>
-#include "netdb_private.h"
-
-int
-getservbyname_r(const char *name, const char *proto, struct servent *sptr,
- char *buffer, size_t buflen, struct servent **result)
-{
- struct servent se;
- struct servent_data *sed;
- char **cp;
- int error;
-
- if ((sed = __servent_data_init()) == NULL)
- return (-1);
-
-#ifdef YP
- sed->yp_name = (char *)name;
- sed->yp_proto = (char *)proto;
-#endif
-
- __setservent_p(sed->stayopen, sed);
- while ((error = __getservent_p(&se, sed)) == 0) {
- if (strcmp(name, se.s_name) == 0)
- goto gotname;
- for (cp = se.s_aliases; *cp; cp++)
- if (strcmp(name, *cp) == 0)
- goto gotname;
- continue;
-gotname:
- if (proto == 0 || strcmp(se.s_proto, proto) == 0)
- break;
- }
- if (!sed->stayopen)
- __endservent_p(sed);
-
-#ifdef YP
- sed->yp_name = NULL;
- sed->yp_proto = NULL;
-#endif
-
- if (error != 0)
- return (-1);
- if (__copy_servent(&se, sptr, buffer, buflen) != 0)
- return (-1);
- *result = sptr;
- return (0);
-}
-
-struct servent *
-getservbyname(const char *name, const char *proto)
-{
- struct servdata *sd;
- struct servent *rval;
-
- if ((sd = __servdata_init()) == NULL)
- return (NULL);
- if (getservbyname_r(name, proto, &sd->serv, sd->data, sizeof(sd->data),
- &rval) != 0)
- return (NULL);
- return (rval);
-}
diff --git a/lib/libc/net/getservbyport.c b/lib/libc/net/getservbyport.c
deleted file mode 100644
index 827e5bf..0000000
--- a/lib/libc/net/getservbyport.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 1983, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)getservbyport.c 8.1 (Berkeley) 6/4/93";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <netdb.h>
-#include <string.h>
-#include "netdb_private.h"
-
-int
-getservbyport_r(int port, const char *proto, struct servent *sptr,
- char *buffer, size_t buflen, struct servent **result)
-{
- struct servent se;
- struct servent_data *sed;
- int error;
-
- if ((sed = __servent_data_init()) == NULL)
- return (-1);
-
-#ifdef YP
- sed->yp_port = port;
- sed->yp_proto = (char *)proto;
-#endif
-
- __setservent_p(sed->stayopen, sed);
- while ((error = __getservent_p(&se, sed)) == 0) {
- if (se.s_port != port)
- continue;
- if (proto == 0 || strcmp(se.s_proto, proto) == 0)
- break;
- }
- if (!sed->stayopen)
- __endservent_p(sed);
-
-#ifdef YP
- sed->yp_port = 0;
- sed->yp_proto = NULL;
-#endif
-
- if (error != 0)
- return (-1);
- if (__copy_servent(&se, sptr, buffer, buflen) != 0)
- return (-1);
- *result = sptr;
- return (0);
-}
-
-struct servent *
-getservbyport(int port, const char *proto)
-{
- struct servdata *sd;
- struct servent *rval;
-
- if ((sd = __servdata_init()) == NULL)
- return (NULL);
- if (getservbyport_r(port, proto, &sd->serv, sd->data,
- sizeof(sd->data), &rval) != 0)
- return (NULL);
- return (rval);
-}
diff --git a/lib/libc/net/getservent.c b/lib/libc/net/getservent.c
index ff4864a9..b616e25 100644
--- a/lib/libc/net/getservent.c
+++ b/lib/libc/net/getservent.c
@@ -44,9 +44,11 @@ __FBSDID("$FreeBSD$");
#include <errno.h>
#include <limits.h>
#include <netdb.h>
+#include <nsswitch.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
#ifdef YP
#include <rpc/rpc.h>
#include <rpcsvc/yp_prot.h>
@@ -56,350 +58,1156 @@ __FBSDID("$FreeBSD$");
#include "reentrant.h"
#include "un-namespace.h"
#include "netdb_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
+#include "nss_tls.h"
-NETDB_THREAD_ALLOC(servent_data)
-NETDB_THREAD_ALLOC(servdata)
+enum constants
+{
+ SETSERVENT = 1,
+ ENDSERVENT = 2,
+ SERVENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
+ SERVENT_STORAGE_MAX = 1 << 20, /* 1 MByte */
+};
-static void
-servent_data_clear(struct servent_data *sed)
+struct servent_mdata
{
- if (sed->fp) {
- fclose(sed->fp);
- sed->fp = NULL;
- }
+ enum nss_lookup_type how;
+ int compat_mode;
+};
+
+static const ns_src defaultsrc[] = {
+ { NSSRC_COMPAT, NS_SUCCESS },
+ { NULL, 0 }
+};
+
+static int servent_unpack(char *, struct servent *, char **, size_t, int *);
+
+/* files backend declarations */
+struct files_state
+{
+ FILE *fp;
+ int stayopen;
+
+ int compat_mode_active;
+};
+static void files_endstate(void *);
+NSS_TLS_HANDLING(files);
+
+static int files_servent(void *, void *, va_list);
+static int files_setservent(void *, void *, va_list);
+
#ifdef YP
- free(sed->yp_key);
- sed->yp_key = NULL;
+/* nis backend declarations */
+static int nis_servent(void *, void *, va_list);
+static int nis_setservent(void *, void *, va_list);
+
+struct nis_state
+{
+ int yp_stepping;
+ char yp_domain[MAXHOSTNAMELEN];
+ char *yp_key;
+ int yp_keylen;
+};
+static void nis_endstate(void *);
+NSS_TLS_HANDLING(nis);
+
+static int nis_servent(void *, void *, va_list);
+static int nis_setservent(void *, void *, va_list);
#endif
-}
-static void
-servent_data_free(void *ptr)
+/* compat backend declarations */
+static int compat_setservent(void *, void *, va_list);
+
+/* get** wrappers for get**_r functions declarations */
+struct servent_state {
+ struct servent serv;
+ char *buffer;
+ size_t bufsize;
+};
+static void servent_endstate(void *);
+NSS_TLS_HANDLING(servent);
+
+struct key {
+ const char *proto;
+ union {
+ const char *name;
+ int port;
+ };
+};
+
+static int wrap_getservbyname_r(struct key, struct servent *, char *, size_t,
+ struct servent **);
+static int wrap_getservbyport_r(struct key, struct servent *, char *, size_t,
+ struct servent **);
+static int wrap_getservent_r(struct key, struct servent *, char *, size_t,
+ struct servent **);
+static struct servent *getserv(int (*fn)(struct key, struct servent *, char *,
+ size_t, struct servent **), struct key);
+
+#ifdef NS_CACHING
+static int serv_id_func(char *, size_t *, va_list, void *);
+static int serv_marshal_func(char *, size_t *, void *, va_list, void *);
+static int serv_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
+
+static int
+servent_unpack(char *p, struct servent *serv, char **aliases,
+ size_t aliases_size, int *errnop)
{
- struct servent_data *sed = ptr;
+ char *cp, **q, *endp;
+ long l;
+
+ if (*p == '#')
+ return -1;
+
+ memset(serv, 0, sizeof(struct servent));
+
+ cp = strpbrk(p, "#\n");
+ if (cp != NULL)
+ *cp = '\0';
+ serv->s_name = p;
+
+ p = strpbrk(p, " \t");
+ if (p == NULL)
+ return -1;
+ *p++ = '\0';
+ while (*p == ' ' || *p == '\t')
+ p++;
+ cp = strpbrk(p, ",/");
+ if (cp == NULL)
+ return -1;
+
+ *cp++ = '\0';
+ l = strtol(p, &endp, 10);
+ if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
+ return -1;
+ serv->s_port = htons((in_port_t)l);
+ serv->s_proto = cp;
+
+ q = serv->s_aliases = aliases;
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &aliases[aliases_size - 1]) {
+ *q++ = cp;
+ } else {
+ *q = NULL;
+ *errnop = ERANGE;
+ return -1;
+ }
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
+ }
+ *q = NULL;
- servent_data_clear(sed);
- free(sed);
+ return 0;
}
-static void
-servdata_free(void *ptr)
+/* files backend implementation */
+static void
+files_endstate(void *p)
{
- free(ptr);
+ FILE * f;
+
+ if (p == NULL)
+ return;
+
+ f = ((struct files_state *)p)->fp;
+ if (f != NULL)
+ fclose(f);
+
+ free(p);
}
-int
-__copy_servent(struct servent *se, struct servent *sptr, char *buf,
- size_t buflen)
+/*
+ * compat structures. compat and files sources functionalities are almost
+ * equal, so they all are managed by files_servent function
+ */
+static int
+files_servent(void *retval, void *mdata, va_list ap)
{
- char *cp;
- int i, n;
- int numptr, len;
-
- /* Find out the amount of space required to store the answer. */
- numptr = 1; /* NULL ptr */
- len = (char *)ALIGN(buf) - buf;
- for (i = 0; se->s_aliases[i]; i++, numptr++) {
- len += strlen(se->s_aliases[i]) + 1;
+ static const ns_src compat_src[] = {
+#ifdef YP
+ { NSSRC_NIS, NS_SUCCESS },
+#endif
+ { NULL, 0 }
+ };
+ ns_dtab compat_dtab[] = {
+#ifdef YP
+ { NSSRC_NIS, nis_servent,
+ (void *)((struct servent_mdata *)mdata)->how },
+#endif
+ { NULL, NULL, NULL }
+ };
+
+ struct files_state *st;
+ int rv;
+ int stayopen;
+
+ struct servent_mdata *serv_mdata;
+ char *name;
+ char *proto;
+ int port;
+
+ struct servent *serv;
+ char *buffer;
+ size_t bufsize;
+ int *errnop;
+
+ char **aliases;
+ int aliases_size;
+ size_t linesize;
+ char *line;
+ char **cp;
+
+ name = NULL;
+ proto = NULL;
+ serv_mdata = (struct servent_mdata *)mdata;
+ switch (serv_mdata->how) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ port = va_arg(ap, int);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ return NS_NOTFOUND;
+ };
+
+ serv = va_arg(ap, struct servent *);
+ buffer = va_arg(ap, char *);
+ bufsize = va_arg(ap, size_t);
+ errnop = va_arg(ap,int *);
+
+ *errnop = files_getstate(&st);
+ if (*errnop != 0)
+ return (NS_UNAVAIL);
+
+ if (st->fp == NULL)
+ st->compat_mode_active = 0;
+
+ if (st->fp == NULL && (st->fp = fopen(_PATH_SERVICES, "r")) == NULL) {
+ *errnop = errno;
+ return (NS_UNAVAIL);
}
- len += strlen(se->s_name) + 1;
- len += strlen(se->s_proto) + 1;
- len += numptr * sizeof(char*);
- if (len > (int)buflen) {
- errno = ERANGE;
- return (-1);
+ if (serv_mdata->how == nss_lt_all)
+ stayopen = 1;
+ else {
+ rewind(st->fp);
+ stayopen = st->stayopen;
}
- /* copy port value */
- sptr->s_port = se->s_port;
+ rv = NS_NOTFOUND;
+ do {
+ if (!st->compat_mode_active) {
+ if ((line = fgetln(st->fp, &linesize)) == NULL) {
+ *errnop = errno;
+ rv = NS_RETURN;
+ break;
+ }
+
+ if (*line=='+') {
+ if (serv_mdata->compat_mode != 0)
+ st->compat_mode_active = 1;
+ } else {
+ if (bufsize <= linesize + _ALIGNBYTES +
+ sizeof(char *)) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+ aliases = (char **)_ALIGN(&buffer[linesize+1]);
+ aliases_size = (buffer + bufsize -
+ (char *)aliases) / sizeof(char *);
+ if (aliases_size < 1) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+
+ memcpy(buffer, line, linesize);
+ buffer[linesize] = '\0';
+ }
+ }
+
+ if (st->compat_mode_active != 0) {
+ switch (serv_mdata->how) {
+ case nss_lt_name:
+ rv = nsdispatch(retval, compat_dtab,
+ NSDB_SERVICES_COMPAT, "getservbyname_r",
+ compat_src, name, proto, serv, buffer,
+ bufsize, errnop);
+ break;
+ case nss_lt_id:
+ rv = nsdispatch(retval, compat_dtab,
+ NSDB_SERVICES_COMPAT, "getservbyport_r",
+ compat_src, port, proto, serv, buffer,
+ bufsize, errnop);
+ break;
+ case nss_lt_all:
+ rv = nsdispatch(retval, compat_dtab,
+ NSDB_SERVICES_COMPAT, "getservent_r",
+ compat_src, serv, buffer, bufsize, errnop);
+ break;
+ }
+
+ if (!(rv & NS_TERMINATE) ||
+ serv_mdata->how != nss_lt_all)
+ st->compat_mode_active = 0;
+
+ continue;
+ }
+
+ rv = servent_unpack(buffer, serv, aliases, aliases_size,
+ errnop);
+ if (rv !=0 ) {
+ if (*errnop == 0) {
+ rv = NS_NOTFOUND;
+ continue;
+ }
+ else {
+ rv = NS_RETURN;
+ break;
+ }
+ }
+
+ rv = NS_NOTFOUND;
+ switch (serv_mdata->how) {
+ case nss_lt_name:
+ if (strcmp(name, serv->s_name) == 0)
+ goto gotname;
+ for (cp = serv->s_aliases; *cp; cp++)
+ if (strcmp(name, *cp) == 0)
+ goto gotname;
- cp = (char *)ALIGN(buf) + numptr * sizeof(char *);
+ continue;
+ gotname:
+ if (proto == 0 || strcmp(serv->s_proto, proto) == 0)
+ rv = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ if (port != serv->s_port)
+ continue;
+
+ if (proto == 0 || strcmp(serv->s_proto, proto) == 0)
+ rv = NS_SUCCESS;
+ break;
+ case nss_lt_all:
+ rv = NS_SUCCESS;
+ break;
+ }
- /* copy official name */
- n = strlen(se->s_name) + 1;
- strcpy(cp, se->s_name);
- sptr->s_name = cp;
- cp += n;
+ } while (!(rv & NS_TERMINATE));
- /* copy aliases */
- sptr->s_aliases = (char **)ALIGN(buf);
- for (i = 0 ; se->s_aliases[i]; i++) {
- n = strlen(se->s_aliases[i]) + 1;
- strcpy(cp, se->s_aliases[i]);
- sptr->s_aliases[i] = cp;
- cp += n;
+ if (!stayopen && st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
}
- sptr->s_aliases[i] = NULL;
- /* copy proto */
- n = strlen(se->s_proto) + 1;
- strcpy(cp, se->s_proto);
- sptr->s_proto = cp;
- cp += n;
+ if ((rv == NS_SUCCESS) && (retval != NULL))
+ *(struct servent **)retval=serv;
- return (0);
+ return (rv);
}
+static int
+files_setservent(void *retval, void *mdata, va_list ap)
+{
+ struct files_state *st;
+ int rv;
+ int f;
+
+ rv = files_getstate(&st);
+ if (rv != 0)
+ return (NS_UNAVAIL);
+
+ switch ((enum constants)mdata) {
+ case SETSERVENT:
+ f = va_arg(ap,int);
+ if (st->fp == NULL)
+ st->fp = fopen(_PATH_SERVICES, "r");
+ else
+ rewind(st->fp);
+ st->stayopen |= f;
+ break;
+ case ENDSERVENT:
+ if (st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+ st->stayopen = 0;
+ break;
+ default:
+ break;
+ };
+
+ st->compat_mode_active = 0;
+ return (NS_UNAVAIL);
+}
+
+/* nis backend implementation */
#ifdef YP
+static void
+nis_endstate(void *p)
+{
+ if (p == NULL)
+ return;
+
+ free(((struct nis_state *)p)->yp_key);
+ free(p);
+}
+
static int
-_getservbyport_yp(struct servent_data *sed)
+nis_servent(void *retval, void *mdata, va_list ap)
{
- char *result;
- int resultlen;
+ char *resultbuf, *lastkey;
+ int resultbuflen;
char buf[YPMAXRECORD + 2];
+
+ struct nis_state *st;
int rv;
- snprintf(buf, sizeof(buf), "%d/%s", ntohs(sed->yp_port),
- sed->yp_proto);
+ enum nss_lookup_type how;
+ char *name;
+ char *proto;
+ int port;
- sed->yp_port = 0;
- sed->yp_proto = NULL;
+ struct servent *serv;
+ char *buffer;
+ size_t bufsize;
+ int *errnop;
- if (!sed->yp_domain) {
- if (yp_get_default_domain(&sed->yp_domain))
- return (0);
+ char **aliases;
+ int aliases_size;
+
+ name = NULL;
+ proto = NULL;
+ how = (enum nss_lookup_type)mdata;
+ switch (how) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ port = va_arg(ap, int);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ return NS_NOTFOUND;
+ };
+
+ serv = va_arg(ap, struct servent *);
+ buffer = va_arg(ap, char *);
+ bufsize = va_arg(ap, size_t);
+ errnop = va_arg(ap, int *);
+
+ *errnop = nis_getstate(&st);
+ if (*errnop != 0)
+ return (NS_UNAVAIL);
+
+ if (st->yp_domain[0] == '\0') {
+ if (getdomainname(st->yp_domain, sizeof st->yp_domain)) {
+ *errnop = errno;
+ return (NS_UNAVAIL);
+ }
}
- /*
- * We have to be a little flexible here. Ideally you're supposed
- * to have both a services.byname and a services.byport map, but
- * some systems have only services.byname. FreeBSD cheats a little
- * by putting the services.byport information in the same map as
- * services.byname so that either case will work. We allow for both
- * possibilities here: if there is no services.byport map, we try
- * services.byname instead.
- */
- if ((rv = yp_match(sed->yp_domain, "services.byport", buf, strlen(buf),
- &result, &resultlen))) {
- if (rv == YPERR_MAP) {
- if (yp_match(sed->yp_domain, "services.byname", buf,
- strlen(buf), &result, &resultlen))
- return(0);
+ do {
+ switch (how) {
+ case nss_lt_name:
+ snprintf(buf, sizeof(buf), "%s/%s", name, proto);
+ if (yp_match(st->yp_domain, "services.byname", buf,
+ strlen(buf), &resultbuf, &resultbuflen)) {
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ break;
+ case nss_lt_id:
+ snprintf(buf, sizeof(buf), "%d/%s", ntohs(port),
+ proto);
+
+ /*
+ * We have to be a little flexible
+ * here. Ideally you're supposed to have both
+ * a services.byname and a services.byport
+ * map, but some systems have only
+ * services.byname. FreeBSD cheats a little by
+ * putting the services.byport information in
+ * the same map as services.byname so that
+ * either case will work. We allow for both
+ * possibilities here: if there is no
+ * services.byport map, we try services.byname
+ * instead.
+ */
+ rv = yp_match(st->yp_domain, "services.byport", buf,
+ strlen(buf), &resultbuf, &resultbuflen);
+ if (rv) {
+ if (rv == YPERR_MAP) {
+ if (yp_match(st->yp_domain,
+ "services.byname", buf,
+ strlen(buf), &resultbuf,
+ &resultbuflen)) {
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ } else {
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ }
+
+ break;
+ case nss_lt_all:
+ if (!st->yp_stepping) {
+ free(st->yp_key);
+ rv = yp_first(st->yp_domain, "services.byname",
+ &st->yp_key, &st->yp_keylen, &resultbuf,
+ &resultbuflen);
+ if (rv) {
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ st->yp_stepping = 1;
+ } else {
+ lastkey = st->yp_key;
+ rv = yp_next(st->yp_domain, "services.byname",
+ st->yp_key, st->yp_keylen, &st->yp_key,
+ &st->yp_keylen, &resultbuf, &resultbuflen);
+ free(lastkey);
+ if (rv) {
+ st->yp_stepping = 0;
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ }
+ break;
+ };
+
+ /* we need a room for additional \n symbol */
+ if (bufsize <=
+ resultbuflen + 1 + _ALIGNBYTES + sizeof(char *)) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+
+ aliases = (char **)_ALIGN(&buffer[resultbuflen + 2]);
+ aliases_size =
+ (buffer + bufsize - (char *)aliases) / sizeof(char *);
+ if (aliases_size < 1) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+
+ /*
+ * servent_unpack expects lines terminated with \n --
+ * make it happy
+ */
+ memcpy(buffer, resultbuf, resultbuflen);
+ buffer[resultbuflen] = '\n';
+ buffer[resultbuflen + 1] = '\0';
+
+ if (servent_unpack(buffer, serv, aliases, aliases_size,
+ errnop) != 0) {
+ if (*errnop == 0)
+ rv = NS_NOTFOUND;
+ else
+ rv = NS_RETURN;
} else
- return(0);
- }
+ rv = NS_SUCCESS;
+ free(resultbuf);
+
+ } while (!(rv & NS_TERMINATE) && how == nss_lt_all);
- /* getservent() expects lines terminated with \n -- make it happy */
- snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
+fin:
+ if (rv == NS_SUCCESS && retval != NULL)
+ *(struct servent **)retval = serv;
- free(result);
- return(1);
+ return (rv);
}
static int
-_getservbyname_yp(struct servent_data *sed)
+nis_setservent(void *result, void *mdata, va_list ap)
{
- char *result;
- int resultlen;
- char buf[YPMAXRECORD + 2];
+ struct nis_state *st;
+ int rv;
- if(!sed->yp_domain) {
- if(yp_get_default_domain(&sed->yp_domain))
- return (0);
- }
+ rv = nis_getstate(&st);
+ if (rv != 0)
+ return (NS_UNAVAIL);
+
+ switch ((enum constants)mdata) {
+ case SETSERVENT:
+ case ENDSERVENT:
+ free(st->yp_key);
+ st->yp_key = NULL;
+ st->yp_stepping = 0;
+ break;
+ default:
+ break;
+ };
- snprintf(buf, sizeof(buf), "%s/%s", sed->yp_name, sed->yp_proto);
+ return (NS_UNAVAIL);
+}
+#endif
+
+/* compat backend implementation */
+static int
+compat_setservent(void *retval, void *mdata, va_list ap)
+{
+ static const ns_src compat_src[] = {
+#ifdef YP
+ { NSSRC_NIS, NS_SUCCESS },
+#endif
+ { NULL, 0 }
+ };
+ ns_dtab compat_dtab[] = {
+#ifdef YP
+ { NSSRC_NIS, nis_setservent, mdata },
+#endif
+ { NULL, NULL, NULL }
+ };
+ int f;
- sed->yp_name = 0;
- sed->yp_proto = NULL;
+ (void)files_setservent(retval, mdata, ap);
- if (yp_match(sed->yp_domain, "services.byname", buf, strlen(buf),
- &result, &resultlen)) {
- return(0);
+ switch ((enum constants)mdata) {
+ case SETSERVENT:
+ f = va_arg(ap,int);
+ (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
+ "setservent", compat_src, f);
+ break;
+ case ENDSERVENT:
+ (void)nsdispatch(retval, compat_dtab, NSDB_SERVICES_COMPAT,
+ "endservent", compat_src);
+ break;
+ default:
+ break;
}
- /* getservent() expects lines terminated with \n -- make it happy */
- snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
-
- free(result);
- return(1);
+ return (NS_UNAVAIL);
}
+#ifdef NS_CACHING
static int
-_getservent_yp(struct servent_data *sed)
+serv_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
{
- char *lastkey, *result;
- int resultlen;
- int rv;
+ char *name;
+ char *proto;
+ int port;
- if (!sed->yp_domain) {
- if (yp_get_default_domain(&sed->yp_domain))
- return (0);
- }
+ size_t desired_size, size, size2;
+ enum nss_lookup_type lookup_type;
+ int res = NS_UNAVAIL;
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ proto = va_arg(ap, char *);
+
+ size = strlen(name);
+ desired_size = sizeof(enum nss_lookup_type) + size + 1;
+ if (proto != NULL) {
+ size2 = strlen(proto);
+ desired_size += size2 + 1;
+ } else
+ size2 = 0;
- if (!sed->yp_stepping) {
- free(sed->yp_key);
- rv = yp_first(sed->yp_domain, "services.byname", &sed->yp_key,
- &sed->yp_keylen, &result, &resultlen);
- if (rv) {
- sed->yp_stepping = 0;
- return(0);
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
}
- sed->yp_stepping = 1;
- } else {
- lastkey = sed->yp_key;
- rv = yp_next(sed->yp_domain, "services.byname", sed->yp_key,
- sed->yp_keylen, &sed->yp_key, &sed->yp_keylen, &result,
- &resultlen);
- free(lastkey);
- if (rv) {
- sed->yp_stepping = 0;
- return (0);
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+ if (proto != NULL)
+ memcpy(buffer + sizeof(enum nss_lookup_type) + size + 1,
+ proto, size2 + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ port = va_arg(ap, int);
+ proto = va_arg(ap, char *);
+
+ desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
+ if (proto != NULL) {
+ size = strlen(proto);
+ desired_size += size + 1;
+ } else
+ size = 0;
+
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), &port,
+ sizeof(int));
+
+ if (proto != NULL)
+ memcpy(buffer + sizeof(enum nss_lookup_type) +
+ sizeof(int), proto, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+int
+serv_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
+{
+ char *name;
+ char *proto;
+ int port;
+ struct servent *serv;
+ char *orig_buf;
+ size_t orig_buf_size;
+
+ struct servent new_serv;
+ size_t desired_size;
+ char **alias;
+ char *p;
+ size_t size;
+ size_t aliases_size;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ port = va_arg(ap, int);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ serv = va_arg(ap, struct servent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ desired_size = _ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
+ if (serv->s_name != NULL)
+ desired_size += strlen(serv->s_name) + 1;
+ if (serv->s_proto != NULL)
+ desired_size += strlen(serv->s_proto) + 1;
+
+ aliases_size = 0;
+ if (serv->s_aliases != NULL) {
+ for (alias = serv->s_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
}
+
+ desired_size += _ALIGNBYTES +
+ sizeof(char *) * (aliases_size + 1);
+ }
+
+ if (*buffer_size < desired_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_serv, serv, sizeof(struct servent));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct servent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_serv.s_name != NULL) {
+ size = strlen(new_serv.s_name);
+ memcpy(p, new_serv.s_name, size);
+ new_serv.s_name = p;
+ p += size + 1;
}
- /* getservent() expects lines terminated with \n -- make it happy */
- snprintf(sed->line, sizeof sed->line, "%.*s\n", resultlen, result);
+ if (new_serv.s_proto != NULL) {
+ size = strlen(new_serv.s_proto);
+ memcpy(p, new_serv.s_proto, size);
+ new_serv.s_proto = p;
+ p += size + 1;
+ }
- free(result);
+ if (new_serv.s_aliases != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
+ new_serv.s_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
- return(1);
+ for (alias = new_serv.s_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_serv, sizeof(struct servent));
+ return (NS_SUCCESS);
}
-#endif
-void
-__setservent_p(int f, struct servent_data *sed)
+int
+serv_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
{
- if (sed->fp == NULL)
- sed->fp = fopen(_PATH_SERVICES, "r");
- else
- rewind(sed->fp);
- sed->stayopen |= f;
+ char *name;
+ char *proto;
+ int port;
+ struct servent *serv;
+ char *orig_buf;
+ char *p;
+ char **alias;
+ size_t orig_buf_size;
+ int *ret_errno;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ port = va_arg(ap, int);
+ proto = va_arg(ap, char *);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ serv = va_arg(ap, struct servent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int *);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct servent) - sizeof(char *)) {
+ *ret_errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(serv, buffer, sizeof(struct servent));
+ memcpy(&p, buffer + sizeof(struct servent), sizeof(char *));
+
+ orig_buf = (char *)_ALIGN(orig_buf);
+ memcpy(orig_buf, buffer + sizeof(struct servent) + sizeof(char *) +
+ (_ALIGN(p) - (size_t)p),
+ buffer_size - sizeof(struct servent) - sizeof(char *) -
+ (_ALIGN(p) - (size_t)p));
+ p = (char *)_ALIGN(p);
+
+ NS_APPLY_OFFSET(serv->s_name, orig_buf, p, char *);
+ NS_APPLY_OFFSET(serv->s_proto, orig_buf, p, char *);
+ if (serv->s_aliases != NULL) {
+ NS_APPLY_OFFSET(serv->s_aliases, orig_buf, p, char **);
+
+ for (alias = serv->s_aliases; *alias; ++alias)
+ NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
+ }
+
+ if (retval != NULL)
+ *((struct servent **)retval) = serv;
+ return (NS_SUCCESS);
}
-void
-__endservent_p(struct servent_data *sed)
+NSS_MP_CACHE_HANDLING(services);
+#endif /* NS_CACHING */
+
+/* get**_r functions implementation */
+int
+getservbyname_r(const char *name, const char *proto, struct servent *serv,
+ char *buffer, size_t bufsize, struct servent **result)
{
- servent_data_clear(sed);
- sed->stayopen = 0;
+ static const struct servent_mdata mdata = { nss_lt_name, 0 };
+ static const struct servent_mdata compat_mdata = { nss_lt_name, 1 };
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ services, (void *)nss_lt_name,
+ serv_id_func, serv_marshal_func, serv_unmarshal_func);
+#endif /* NS_CACHING */
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_servent, (void *)&mdata },
#ifdef YP
- sed->yp_stepping = 0;
- sed->yp_domain = NULL;
+ { NSSRC_NIS, nis_servent, (void *)nss_lt_name },
+#endif
+ { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyname_r",
+ defaultsrc, name, proto, serv, buffer, bufsize, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
}
int
-__getservent_p(struct servent *se, struct servent_data *sed)
+getservbyport_r(int port, const char *proto, struct servent *serv,
+ char *buffer, size_t bufsize, struct servent **result)
{
- char *p;
- char *cp, **q, *endp;
- long l;
-
-#ifdef YP
- if (sed->yp_stepping && _getservent_yp(sed)) {
- p = sed->line;
- goto unpack;
- }
-tryagain:
+ static const struct servent_mdata mdata = { nss_lt_id, 0 };
+ static const struct servent_mdata compat_mdata = { nss_lt_id, 1 };
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ services, (void *)nss_lt_id,
+ serv_id_func, serv_marshal_func, serv_unmarshal_func);
#endif
- if (sed->fp == NULL && (sed->fp = fopen(_PATH_SERVICES, "r")) == NULL)
- return (-1);
-again:
- if ((p = fgets(sed->line, sizeof sed->line, sed->fp)) == NULL)
- return (-1);
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_servent, (void *)&mdata },
#ifdef YP
- if (*p == '+' && _yp_check(NULL)) {
- if (sed->yp_name != NULL) {
- if (!_getservbyname_yp(sed))
- goto tryagain;
- }
- else if (sed->yp_port != 0) {
- if (!_getservbyport_yp(sed))
- goto tryagain;
- }
- else if (!_getservent_yp(sed))
- goto tryagain;
- }
-unpack:
+ { NSSRC_NIS, nis_servent, (void *)nss_lt_id },
#endif
- if (*p == '#')
- goto again;
- cp = strpbrk(p, "#\n");
- if (cp != NULL)
- *cp = '\0';
- se->s_name = p;
- p = strpbrk(p, " \t");
- if (p == NULL)
- goto again;
- *p++ = '\0';
- while (*p == ' ' || *p == '\t')
- p++;
- cp = strpbrk(p, ",/");
- if (cp == NULL)
- goto again;
- *cp++ = '\0';
- l = strtol(p, &endp, 10);
- if (endp == p || *endp != '\0' || l < 0 || l > USHRT_MAX)
- goto again;
- se->s_port = htons((in_port_t)l);
- se->s_proto = cp;
- q = se->s_aliases = sed->aliases;
- cp = strpbrk(cp, " \t");
- if (cp != NULL)
- *cp++ = '\0';
- while (cp && *cp) {
- if (*cp == ' ' || *cp == '\t') {
- cp++;
- continue;
- }
- if (q < &sed->aliases[_MAXALIASES - 1])
- *q++ = cp;
- cp = strpbrk(cp, " \t");
- if (cp != NULL)
- *cp++ = '\0';
- }
- *q = NULL;
- return (0);
+ { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservbyport_r",
+ defaultsrc, port, proto, serv, buffer, bufsize, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
}
int
-getservent_r(struct servent *sptr, char *buffer, size_t buflen,
+getservent_r(struct servent *serv, char *buffer, size_t bufsize,
struct servent **result)
{
- struct servent se;
- struct servent_data *sed;
-
- if ((sed = __servent_data_init()) == NULL)
- return (-1);
-
- if (__getservent_p(&se, sed) != 0)
- return (-1);
- if (__copy_servent(&se, sptr, buffer, buflen) != 0)
- return (-1);
- *result = sptr;
- return (0);
+ static const struct servent_mdata mdata = { nss_lt_all, 0 };
+ static const struct servent_mdata compat_mdata = { nss_lt_all, 1 };
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ services, (void *)nss_lt_all,
+ serv_marshal_func, serv_unmarshal_func);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_servent, (void *)&mdata },
+#ifdef YP
+ { NSSRC_NIS, nis_servent, (void *)nss_lt_all },
+#endif
+ { NSSRC_COMPAT, files_servent, (void *)&compat_mdata },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_SERVICES, "getservent_r",
+ defaultsrc, serv, buffer, bufsize, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
}
void
-setservent(int f)
+setservent(int stayopen)
{
- struct servent_data *sed;
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ services, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setservent, (void *)SETSERVENT },
+#ifdef YP
+ { NSSRC_NIS, nis_setservent, (void *)SETSERVENT },
+#endif
+ { NSSRC_COMPAT, compat_setservent, (void *)SETSERVENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
- if ((sed = __servent_data_init()) == NULL)
- return;
- __setservent_p(f, sed);
+ (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "setservent", defaultsrc,
+ stayopen);
}
void
-endservent(void)
+endservent()
{
- struct servent_data *sed;
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ services, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setservent, (void *)ENDSERVENT },
+#ifdef YP
+ { NSSRC_NIS, nis_setservent, (void *)ENDSERVENT },
+#endif
+ { NSSRC_COMPAT, compat_setservent, (void *)ENDSERVENT },
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
- if ((sed = __servent_data_init()) == NULL)
+ (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endservent", defaultsrc);
+}
+
+/* get** wrappers for get**_r functions implementation */
+static void
+servent_endstate(void *p)
+{
+ if (p == NULL)
return;
- __endservent_p(sed);
+
+ free(((struct servent_state *)p)->buffer);
+ free(p);
+}
+
+static int
+wrap_getservbyname_r(struct key key, struct servent *serv, char *buffer,
+ size_t bufsize, struct servent **res)
+{
+ return (getservbyname_r(key.name, key.proto, serv, buffer, bufsize,
+ res));
+}
+
+static int
+wrap_getservbyport_r(struct key key, struct servent *serv, char *buffer,
+ size_t bufsize, struct servent **res)
+{
+ return (getservbyport_r(key.port, key.proto, serv, buffer, bufsize,
+ res));
+}
+
+static int
+wrap_getservent_r(struct key key, struct servent *serv, char *buffer,
+ size_t bufsize, struct servent **res)
+{
+ return (getservent_r(serv, buffer, bufsize, res));
+}
+
+static struct servent *
+getserv(int (*fn)(struct key, struct servent *, char *, size_t,
+ struct servent **), struct key key)
+{
+ int rv;
+ struct servent *res;
+ struct servent_state * st;
+
+ rv = servent_getstate(&st);
+ if (rv != 0) {
+ errno = rv;
+ return NULL;
+ }
+
+ if (st->buffer == NULL) {
+ st->buffer = malloc(SERVENT_STORAGE_INITIAL);
+ if (st->buffer == NULL)
+ return (NULL);
+ st->bufsize = SERVENT_STORAGE_INITIAL;
+ }
+ do {
+ rv = fn(key, &st->serv, st->buffer, st->bufsize, &res);
+ if (res == NULL && rv == ERANGE) {
+ free(st->buffer);
+ if ((st->bufsize << 1) > SERVENT_STORAGE_MAX) {
+ st->buffer = NULL;
+ errno = ERANGE;
+ return (NULL);
+ }
+ st->bufsize <<= 1;
+ st->buffer = malloc(st->bufsize);
+ if (st->buffer == NULL)
+ return (NULL);
+ }
+ } while (res == NULL && rv == ERANGE);
+ if (rv != 0)
+ errno = rv;
+
+ return (res);
+}
+
+struct servent *
+getservbyname(const char *name, const char *proto)
+{
+ struct key key;
+
+ key.name = name;
+ key.proto = proto;
+
+ return (getserv(wrap_getservbyname_r, key));
}
struct servent *
-getservent(void)
+getservbyport(int port, const char *proto)
{
- struct servdata *sd;
- struct servent *rval;
-
- if ((sd = __servdata_init()) == NULL)
- return (NULL);
- if (getservent_r(&sd->serv, sd->data, sizeof(sd->data), &rval) != 0)
- return (NULL);
- return (rval);
+ struct key key;
+
+ key.port = port;
+ key.proto = proto;
+
+ return (getserv(wrap_getservbyport_r, key));
+}
+
+struct servent *
+getservent()
+{
+ struct key key;
+
+ key.proto = NULL;
+ key.port = 0;
+
+ return (getserv(wrap_getservent_r, key));
}
diff --git a/lib/libc/net/name6.c b/lib/libc/net/name6.c
index 18f2fa2..dc6521a 100644
--- a/lib/libc/net/name6.c
+++ b/lib/libc/net/name6.c
@@ -123,6 +123,9 @@ __FBSDID("$FreeBSD$");
#include "netdb_private.h"
#include "res_config.h"
#include "res_private.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
#ifndef _PATH_HOSTS
#define _PATH_HOSTS "/etc/hosts"
@@ -235,6 +238,11 @@ static void _dns_ehent(void) __unused;
#ifdef ICMPNL
static int _icmp_ghbyaddr(void *, void *, va_list);
#endif /* ICMPNL */
+#ifdef NS_CACHING
+static int ipnode_id_func(char *, size_t *, va_list, void *);
+static int ipnode_marshal_func(char *, size_t *, void *, va_list, void *);
+static int ipnode_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
#ifdef ICMPNL
static mutex_t _getipnodeby_thread_lock = MUTEX_INITIALIZER;
@@ -281,6 +289,239 @@ _mapped_addr_enabled(void)
return 0;
}
+#ifdef NS_CACHING
+static int
+ipnode_id_func(char *buffer, size_t *buffer_size, va_list ap,
+ void *cache_mdata)
+{
+ res_state statp;
+ u_long res_options;
+
+ const int op_id = 2;
+ char *name;
+ int af;
+ size_t len;
+ void *src;
+
+ char *p;
+ size_t desired_size, size;
+ enum nss_lookup_type lookup_type;
+ int res = NS_UNAVAIL;
+
+ statp = __res_state();
+ res_options = statp->options & (RES_RECURSE | RES_DEFNAMES |
+ RES_DNSRCH | RES_NOALIASES | RES_USE_INET6);
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ af = va_arg(ap, int);
+
+ size = strlen(name);
+ desired_size = sizeof(res_options) + sizeof(int) +
+ sizeof(enum nss_lookup_type) + sizeof(int) + size + 1;
+
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ p = buffer;
+ memcpy(p, &res_options, sizeof(res_options));
+ p += sizeof(res_options);
+
+ memcpy(p, &op_id, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
+ p += sizeof(enum nss_lookup_type);
+
+ memcpy(p, &af, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, name, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ src = va_arg(ap, void *);
+ len = va_arg(ap, size_t);
+ af = va_arg(ap, int);
+
+ desired_size = sizeof(res_options) + sizeof(int) +
+ sizeof(enum nss_lookup_type) + sizeof(int) +
+ sizeof(size_t) + len;
+
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ p = buffer;
+ memcpy(p, &res_options, sizeof(res_options));
+ p += sizeof(res_options);
+
+ memcpy(p, &op_id, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
+ p += sizeof(enum nss_lookup_type);
+
+ memcpy(p, &af, sizeof(int));
+ p += sizeof(int);
+
+ memcpy(p, &len, sizeof(size_t));
+ p += sizeof(size_t);
+
+ memcpy(p, src, len);
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+static int
+ipnode_marshal_func(char *buffer, size_t *buffer_size, void *retval,
+ va_list ap, void *cache_mdata)
+{
+ struct hostent *ht;
+
+ struct hostent new_ht;
+ size_t desired_size, aliases_size, addr_size, size;
+ char *p, **iter;
+
+ ht = *((struct hostent **)retval);
+
+ desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *);
+ if (ht->h_name != NULL)
+ desired_size += strlen(ht->h_name) + 1;
+
+ if (ht->h_aliases != NULL) {
+ aliases_size = 0;
+ for (iter = ht->h_aliases; *iter; ++iter) {
+ desired_size += strlen(*iter) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += _ALIGNBYTES +
+ (aliases_size + 1) * sizeof(char *);
+ }
+
+ if (ht->h_addr_list != NULL) {
+ addr_size = 0;
+ for (iter = ht->h_addr_list; *iter; ++iter)
+ ++addr_size;
+
+ desired_size += addr_size * _ALIGN(ht->h_length);
+ desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *);
+ }
+
+ if (desired_size > *buffer_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_ht, ht, sizeof(struct hostent));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct hostent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_ht.h_name != NULL) {
+ size = strlen(new_ht.h_name);
+ memcpy(p, new_ht.h_name, size);
+ new_ht.h_name = p;
+ p += size + 1;
+ }
+
+ if (new_ht.h_aliases != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size);
+ new_ht.h_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (iter = new_ht.h_aliases; *iter; ++iter) {
+ size = strlen(*iter);
+ memcpy(p, *iter, size);
+ *iter = p;
+ p += size + 1;
+ }
+ }
+
+ if (new_ht.h_addr_list != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size);
+ new_ht.h_addr_list = (char **)p;
+ p += sizeof(char *) * (addr_size + 1);
+
+ size = _ALIGN(new_ht.h_length);
+ for (iter = new_ht.h_addr_list; *iter; ++iter) {
+ memcpy(p, *iter, size);
+ *iter = p;
+ p += size + 1;
+ }
+ }
+ memcpy(buffer, &new_ht, sizeof(struct hostent));
+ return (NS_SUCCESS);
+}
+
+static int
+ipnode_unmarshal_func(char *buffer, size_t buffer_size, void *retval,
+ va_list ap, void *cache_mdata)
+{
+ struct hostent new_ht;
+ struct hostent *ht;
+
+ char *p;
+ char **iter;
+ char *orig_buf;
+ int err;
+
+ ht = &new_ht;
+
+ memcpy(ht, buffer, sizeof(struct hostent));
+ memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *));
+
+ orig_buf = buffer + sizeof(struct hostent) + sizeof(char *) +
+ _ALIGN(p) - (size_t)p;
+ p = (char *)_ALIGN(p);
+
+
+ NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *);
+ if (ht->h_aliases != NULL) {
+ NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **);
+
+ for (iter = ht->h_aliases; *iter; ++iter)
+ NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
+ }
+
+ if (ht->h_addr_list != NULL) {
+ NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **);
+
+ for (iter = ht->h_addr_list; *iter; ++iter)
+ NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
+ }
+
+ ht = _hpcopy(ht, &err);
+ if (ht == NULL)
+ return (NS_UNAVAIL);
+
+ *((struct hostent **)retval) = ht;
+ return (NS_SUCCESS);
+}
+#endif
+
/*
* Functions defined in RFC2553
* getipnodebyname, getipnodebyaddr, freehostent
@@ -292,10 +533,19 @@ _ghbyname(const char *name, int af, int flags, int *errp)
struct hostent *hp;
int rval;
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ hosts, (void *)nss_lt_name,
+ ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_ghbyname, NULL)
{ NSSRC_DNS, _dns_ghbyname, NULL },
NS_NIS_CB(_nis_ghbyname, NULL)
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
@@ -399,6 +649,12 @@ getipnodebyaddr(const void *src, size_t len, int af, int *errp)
struct in_addr addrbuf;
#endif
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ hosts, (void *)nss_lt_id,
+ ipnode_id_func, ipnode_marshal_func, ipnode_unmarshal_func);
+#endif
static const ns_dtab dtab[] = {
NS_FILES_CB(_files_ghbyaddr, NULL)
{ NSSRC_DNS, _dns_ghbyaddr, NULL },
@@ -406,6 +662,9 @@ getipnodebyaddr(const void *src, size_t len, int af, int *errp)
#ifdef ICMPNL
{ NSSRC_ICMP, _icmp_ghbyaddr, NULL },
#endif
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
{ 0 }
};
diff --git a/lib/libc/net/netdb_private.h b/lib/libc/net/netdb_private.h
index fa1de62..e5535f6 100644
--- a/lib/libc/net/netdb_private.h
+++ b/lib/libc/net/netdb_private.h
@@ -100,22 +100,6 @@ struct protoent_data {
char line[_MAXLINELEN + 1];
};
-struct servent_data {
- FILE *fp;
- char *aliases[_MAXALIASES];
- int stayopen;
- char line[_MAXLINELEN + 1];
-#ifdef YP
- int yp_stepping;
- char *yp_name;
- char *yp_proto;
- int yp_port;
- char *yp_domain;
- char *yp_key;
- int yp_keylen;
-#endif
-};
-
struct hostdata {
struct hostent host;
char data[sizeof(struct hostent_data)];
@@ -131,11 +115,6 @@ struct protodata {
char data[sizeof(struct protoent_data)];
};
-struct servdata {
- struct servent serv;
- char data[sizeof(struct servent_data)];
-};
-
struct hostdata *__hostdata_init(void);
struct hostent *__hostent_init(void);
struct hostent_data *__hostent_data_init(void);
@@ -143,19 +122,13 @@ struct netdata *__netdata_init(void);
struct netent_data *__netent_data_init(void);
struct protodata *__protodata_init(void);
struct protoent_data *__protoent_data_init(void);
-struct servdata *__servdata_init(void);
-struct servent_data *__servent_data_init(void);
int __copy_hostent(struct hostent *, struct hostent *, char *, size_t);
int __copy_netent(struct netent *, struct netent *, char *, size_t);
int __copy_protoent(struct protoent *, struct protoent *, char *, size_t);
-int __copy_servent(struct servent *, struct servent *, char *, size_t);
void __endprotoent_p(struct protoent_data *);
-void __endservent_p(struct servent_data *);
int __getprotoent_p(struct protoent *, struct protoent_data *);
-int __getservent_p(struct servent *, struct servent_data *);
void __setprotoent_p(int, struct protoent_data *);
-void __setservent_p(int, struct servent_data *);
void _endhostdnsent(void);
void _endhosthtent(struct hostent_data *);
void _endnetdnsent(void);
diff --git a/lib/libc/net/nscache.c b/lib/libc/net/nscache.c
new file mode 100644
index 0000000..98a4367
--- /dev/null
+++ b/lib/libc/net/nscache.c
@@ -0,0 +1,438 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <nsswitch.h>
+#include <stdlib.h>
+#include <string.h>
+#include "un-namespace.h"
+#include "nscachedcli.h"
+#include "nscache.h"
+
+#define NSS_CACHE_KEY_INITIAL_SIZE (256)
+#define NSS_CACHE_KEY_SIZE_LIMIT (NSS_CACHE_KEY_INITIAL_SIZE << 4)
+
+#define NSS_CACHE_BUFFER_INITIAL_SIZE (1024)
+#define NSS_CACHE_BUFFER_SIZE_LIMIT (NSS_CACHE_BUFFER_INITIAL_SIZE << 8)
+
+#define CACHED_SOCKET_PATH "/var/run/cached"
+
+int
+__nss_cache_handler(void *retval, void *mdata, va_list ap)
+{
+ return (NS_UNAVAIL);
+}
+
+int
+__nss_common_cache_read(void *retval, void *mdata, va_list ap)
+{
+ struct cached_connection_params params;
+ cached_connection connection;
+
+ char *buffer;
+ size_t buffer_size, size;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+ va_list ap_new;
+ int res;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ memset(&params, 0, sizeof(struct cached_connection_params));
+ params.socket_path = CACHED_SOCKET_PATH;
+
+ cache_data->key = (char *)malloc(NSS_CACHE_KEY_INITIAL_SIZE);
+ memset(cache_data->key, 0, NSS_CACHE_KEY_INITIAL_SIZE);
+ cache_data->key_size = NSS_CACHE_KEY_INITIAL_SIZE;
+ va_copy(ap_new, ap);
+
+ do {
+ size = cache_data->key_size;
+ res = cache_info->id_func(cache_data->key, &size, ap_new,
+ cache_info->mdata);
+ va_end(ap_new);
+ if (res == NS_RETURN) {
+ if (cache_data->key_size > NSS_CACHE_KEY_SIZE_LIMIT)
+ break;
+
+ cache_data->key_size <<= 1;
+ cache_data->key = realloc(cache_data->key,
+ cache_data->key_size);
+ memset(cache_data->key, 0, cache_data->key_size);
+ va_copy(ap_new, ap);
+ }
+ } while (res == NS_RETURN);
+
+ if (res != NS_SUCCESS) {
+ free(cache_data->key);
+ cache_data->key = NULL;
+ cache_data->key_size = 0;
+ return (res);
+ } else
+ cache_data->key_size = size;
+
+ buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
+ buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
+ memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
+
+ do {
+ connection = __open_cached_connection(&params);
+ if (connection == NULL) {
+ res = -1;
+ break;
+ }
+ res = __cached_read(connection, cache_info->entry_name,
+ cache_data->key, cache_data->key_size, buffer,
+ &buffer_size);
+ __close_cached_connection(connection);
+ if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
+ buffer = (char *)realloc(buffer, buffer_size);
+ memset(buffer, 0, buffer_size);
+ }
+ } while (res == -2);
+
+ if (res == 0) {
+ if (buffer_size == 0) {
+ free(buffer);
+ free(cache_data->key);
+ cache_data->key = NULL;
+ cache_data->key_size = 0;
+ return (NS_RETURN);
+ }
+
+ va_copy(ap_new, ap);
+ res = cache_info->unmarshal_func(buffer, buffer_size, retval,
+ ap_new, cache_info->mdata);
+ va_end(ap_new);
+
+ if (res != NS_SUCCESS) {
+ free(buffer);
+ free(cache_data->key);
+ cache_data->key = NULL;
+ cache_data->key_size = 0;
+ return (res);
+ } else
+ res = 0;
+ }
+
+ if (res == 0) {
+ free(cache_data->key);
+ cache_data->key = NULL;
+ cache_data->key_size = 0;
+ }
+
+ free(buffer);
+ return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
+}
+
+int
+__nss_common_cache_write(void *retval, void *mdata, va_list ap)
+{
+ struct cached_connection_params params;
+ cached_connection connection;
+
+ char *buffer;
+ size_t buffer_size;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+ va_list ap_new;
+ int res;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ if (cache_data->key == NULL)
+ return (NS_UNAVAIL);
+
+ memset(&params, 0, sizeof(struct cached_connection_params));
+ params.socket_path = CACHED_SOCKET_PATH;
+
+ connection = __open_cached_connection(&params);
+ if (connection == NULL) {
+ free(cache_data->key);
+ return (NS_UNAVAIL);
+ }
+
+ buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
+ buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
+ memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
+
+ do {
+ size_t size;
+
+ size = buffer_size;
+ va_copy(ap_new, ap);
+ res = cache_info->marshal_func(buffer, &size, retval, ap_new,
+ cache_info->mdata);
+ va_end(ap_new);
+
+ if (res == NS_RETURN) {
+ if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
+ break;
+
+ buffer_size <<= 1;
+ buffer = (char *)realloc(buffer, buffer_size);
+ memset(buffer, 0, buffer_size);
+ }
+ } while (res == NS_RETURN);
+
+ if (res != NS_SUCCESS) {
+ __close_cached_connection(connection);
+ free(cache_data->key);
+ free(buffer);
+ return (res);
+ }
+
+ res = __cached_write(connection, cache_info->entry_name,
+ cache_data->key, cache_data->key_size, buffer, buffer_size);
+ __close_cached_connection(connection);
+
+ free(cache_data->key);
+ free(buffer);
+
+ return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
+}
+
+int
+__nss_common_cache_write_negative(void *mdata)
+{
+ struct cached_connection_params params;
+ cached_connection connection;
+ int res;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ if (cache_data->key == NULL)
+ return (NS_UNAVAIL);
+
+ memset(&params, 0, sizeof(struct cached_connection_params));
+ params.socket_path = CACHED_SOCKET_PATH;
+
+ connection = __open_cached_connection(&params);
+ if (connection == NULL) {
+ free(cache_data->key);
+ return (NS_UNAVAIL);
+ }
+
+ res = __cached_write(connection, cache_info->entry_name,
+ cache_data->key, cache_data->key_size, NULL, 0);
+ __close_cached_connection(connection);
+
+ free(cache_data->key);
+ return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
+}
+
+int
+__nss_mp_cache_read(void *retval, void *mdata, va_list ap)
+{
+ struct cached_connection_params params;
+ cached_mp_read_session rs;
+
+ char *buffer;
+ size_t buffer_size;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+ va_list ap_new;
+ int res;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ if (cache_info->get_mp_ws_func() != INVALID_CACHED_MP_WRITE_SESSION)
+ return (NS_UNAVAIL);
+
+ rs = cache_info->get_mp_rs_func();
+ if (rs == INVALID_CACHED_MP_READ_SESSION) {
+ memset(&params, 0, sizeof(struct cached_connection_params));
+ params.socket_path = CACHED_SOCKET_PATH;
+
+ rs = __open_cached_mp_read_session(&params,
+ cache_info->entry_name);
+ if (rs == INVALID_CACHED_MP_READ_SESSION)
+ return (NS_UNAVAIL);
+
+ cache_info->set_mp_rs_func(rs);
+ }
+
+ buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
+ buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
+ memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
+
+ do {
+ res = __cached_mp_read(rs, buffer, &buffer_size);
+ if (res == -2 && buffer_size < NSS_CACHE_BUFFER_SIZE_LIMIT) {
+ buffer = (char *)realloc(buffer, buffer_size);
+ memset(buffer, 0, buffer_size);
+ }
+ } while (res == -2);
+
+ if (res == 0) {
+ va_copy(ap_new, ap);
+ res = cache_info->unmarshal_func(buffer, buffer_size, retval,
+ ap_new, cache_info->mdata);
+ va_end(ap_new);
+
+ if (res != NS_SUCCESS) {
+ free(buffer);
+ return (res);
+ } else
+ res = 0;
+ } else {
+ free(buffer);
+ __close_cached_mp_read_session(rs);
+ rs = INVALID_CACHED_MP_READ_SESSION;
+ cache_info->set_mp_rs_func(rs);
+ return (res == -1 ? NS_RETURN : NS_UNAVAIL);
+ }
+
+ free(buffer);
+ return (res == 0 ? NS_SUCCESS : NS_NOTFOUND);
+}
+
+int
+__nss_mp_cache_write(void *retval, void *mdata, va_list ap)
+{
+ struct cached_connection_params params;
+ cached_mp_write_session ws;
+
+ char *buffer;
+ size_t buffer_size;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+ va_list ap_new;
+ int res;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ ws = cache_info->get_mp_ws_func();
+ if (ws == INVALID_CACHED_MP_WRITE_SESSION) {
+ memset(&params, 0, sizeof(struct cached_connection_params));
+ params.socket_path = CACHED_SOCKET_PATH;
+
+ ws = __open_cached_mp_write_session(&params,
+ cache_info->entry_name);
+ if (ws == INVALID_CACHED_MP_WRITE_SESSION)
+ return (NS_UNAVAIL);
+
+ cache_info->set_mp_ws_func(ws);
+ }
+
+ buffer_size = NSS_CACHE_BUFFER_INITIAL_SIZE;
+ buffer = (char *)malloc(NSS_CACHE_BUFFER_INITIAL_SIZE);
+ memset(buffer, 0, NSS_CACHE_BUFFER_INITIAL_SIZE);
+
+ do {
+ size_t size;
+
+ size = buffer_size;
+ va_copy(ap_new, ap);
+ res = cache_info->marshal_func(buffer, &size, retval, ap_new,
+ cache_info->mdata);
+ va_end(ap_new);
+
+ if (res == NS_RETURN) {
+ if (buffer_size > NSS_CACHE_BUFFER_SIZE_LIMIT)
+ break;
+
+ buffer_size <<= 1;
+ buffer = (char *)realloc(buffer, buffer_size);
+ memset(buffer, 0, buffer_size);
+ }
+ } while (res == NS_RETURN);
+
+ if (res != NS_SUCCESS) {
+ free(buffer);
+ return (res);
+ }
+
+ res = __cached_mp_write(ws, buffer, buffer_size);
+
+ free(buffer);
+ return (res == 0 ? NS_SUCCESS : NS_UNAVAIL);
+}
+
+int
+__nss_mp_cache_write_submit(void *retval, void *mdata, va_list ap)
+{
+ cached_mp_write_session ws;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ ws = cache_info->get_mp_ws_func();
+ if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
+ __close_cached_mp_write_session(ws);
+ ws = INVALID_CACHED_MP_WRITE_SESSION;
+ cache_info->set_mp_ws_func(ws);
+ }
+ return (NS_UNAVAIL);
+}
+
+int
+__nss_mp_cache_end(void *retval, void *mdata, va_list ap)
+{
+ cached_mp_write_session ws;
+ cached_mp_read_session rs;
+
+ nss_cache_info const *cache_info;
+ nss_cache_data *cache_data;
+
+ cache_data = (nss_cache_data *)mdata;
+ cache_info = cache_data->info;
+
+ ws = cache_info->get_mp_ws_func();
+ if (ws != INVALID_CACHED_MP_WRITE_SESSION) {
+ __abandon_cached_mp_write_session(ws);
+ ws = INVALID_CACHED_MP_WRITE_SESSION;
+ cache_info->set_mp_ws_func(ws);
+ }
+
+ rs = cache_info->get_mp_rs_func();
+ if (rs != INVALID_CACHED_MP_READ_SESSION) {
+ __close_cached_mp_read_session(rs);
+ rs = INVALID_CACHED_MP_READ_SESSION;
+ cache_info->set_mp_rs_func(rs);
+ }
+
+ return (NS_UNAVAIL);
+}
diff --git a/lib/libc/net/nscachedcli.c b/lib/libc/net/nscachedcli.c
new file mode 100644
index 0000000..374f370
--- /dev/null
+++ b/lib/libc/net/nscachedcli.c
@@ -0,0 +1,576 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "namespace.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/event.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "un-namespace.h"
+#include "nscachedcli.h"
+
+#define NS_DEFAULT_CACHED_IO_TIMEOUT 4
+
+static int safe_write(struct cached_connection_ *, const void *, size_t);
+static int safe_read(struct cached_connection_ *, void *, size_t);
+static int send_credentials(struct cached_connection_ *, int);
+
+/*
+ * safe_write writes data to the specified connection and tries to do it in
+ * the very safe manner. We ensure, that we can write to the socket with
+ * kevent. If the data_size can't be sent in one piece, then it would be
+ * splitted.
+ */
+static int
+safe_write(struct cached_connection_ *connection, const void *data,
+ size_t data_size)
+{
+ struct kevent eventlist;
+ int nevents;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist,
+ 1, &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ s_result = _write(connection->sockfd, data + result,
+ eventlist.data < data_size - result ?
+ eventlist.data : data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+/*
+ * safe_read reads data from connection and tries to do it in the very safe
+ * and stable way. It uses kevent to ensure, that the data are availabe for
+ * reading. If the amount of data to be read is too large, then they would
+ * be splitted.
+ */
+static int
+safe_read(struct cached_connection_ *connection, void *data, size_t data_size)
+{
+ struct kevent eventlist;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+ int nevents;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = NS_DEFAULT_CACHED_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->read_queue, NULL, 0, &eventlist,
+ 1, &timeout);
+ if (nevents == 1 && eventlist.filter == EVFILT_READ) {
+ s_result = _read(connection->sockfd, data + result,
+ eventlist.data <= data_size - result ?
+ eventlist.data : data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+/*
+ * Sends the credentials information to the connection along with the
+ * communication element type.
+ */
+static int
+send_credentials(struct cached_connection_ *connection, int type)
+{
+ struct kevent eventlist;
+ int nevents;
+ ssize_t result;
+ int res;
+
+ struct msghdr cred_hdr;
+ struct iovec iov;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = sizeof(cmsg);
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ iov.iov_base = &type;
+ iov.iov_len = sizeof(int);
+
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ NOTE_LOWAT, sizeof(int), NULL);
+ res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1,
+ NULL);
+ if (nevents == 1 && eventlist.filter == EVFILT_WRITE) {
+ result = (_sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ?
+ -1 : 0;
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+ return (result);
+ } else
+ return (-1);
+}
+
+/*
+ * Opens the connection with the specified params. Initializes all kqueues.
+ */
+struct cached_connection_ *
+__open_cached_connection(struct cached_connection_params const *params)
+{
+ struct cached_connection_ *retval;
+ struct kevent eventlist;
+ struct sockaddr_un client_address;
+ int client_address_len, client_socket;
+ int res;
+
+ assert(params != NULL);
+
+ client_socket = _socket(PF_LOCAL, SOCK_STREAM, 0);
+ client_address.sun_family = PF_LOCAL;
+ strncpy(client_address.sun_path, params->socket_path,
+ sizeof(client_address.sun_path));
+ client_address_len = sizeof(client_address.sun_family) +
+ strlen(client_address.sun_path) + 1;
+
+ res = _connect(client_socket, (struct sockaddr *)&client_address,
+ client_address_len);
+ if (res == -1) {
+ _close(client_socket);
+ return (NULL);
+ }
+ _fcntl(client_socket, F_SETFL, O_NONBLOCK);
+
+ retval = malloc(sizeof(struct cached_connection_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cached_connection_));
+
+ retval->sockfd = client_socket;
+
+ retval->write_queue = kqueue();
+ assert(retval->write_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ retval->read_queue = kqueue();
+ assert(retval->read_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
+ res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL);
+
+ return (retval);
+}
+
+void
+__close_cached_connection(struct cached_connection_ *connection)
+{
+ assert(connection != NULL);
+
+ _close(connection->sockfd);
+ _close(connection->read_queue);
+ _close(connection->write_queue);
+ free(connection);
+}
+
+/*
+ * This function is very close to the cache_write function of the caching
+ * library, which is used in the caching daemon. It caches the data with the
+ * specified key in the cache entry with entry_name.
+ */
+int
+__cached_write(struct cached_connection_ *connection, const char *entry_name,
+ const char *key, size_t key_size, const char *data, size_t data_size)
+{
+ size_t name_size;
+ int error_code;
+ int result;
+
+ error_code = -1;
+ result = 0;
+ result = send_credentials(connection, CET_WRITE_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ name_size = strlen(entry_name);
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, &key_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, &data_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, key, key_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, data, data_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_read(connection, &error_code, sizeof(int));
+ if (result != 0)
+ error_code = -1;
+
+fin:
+ return (error_code);
+}
+
+/*
+ * This function is very close to the cache_read function of the caching
+ * library, which is used in the caching daemon. It reads cached data with the
+ * specified key from the cache entry with entry_name.
+ */
+int
+__cached_read(struct cached_connection_ *connection, const char *entry_name,
+ const char *key, size_t key_size, char *data, size_t *data_size)
+{
+ size_t name_size, result_size;
+ int error_code, rec_error_code;
+ int result;
+
+ assert(connection != NULL);
+ result = 0;
+ error_code = -1;
+
+ result = send_credentials(connection, CET_READ_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ name_size = strlen(entry_name);
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, &key_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, key, key_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_read(connection, &rec_error_code, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (rec_error_code != 0) {
+ error_code = rec_error_code;
+ goto fin;
+ }
+
+ result = safe_read(connection, &result_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ if (result_size > *data_size) {
+ *data_size = result_size;
+ error_code = -2;
+ goto fin;
+ }
+
+ result = safe_read(connection, data, result_size);
+ if (result != 0)
+ goto fin;
+
+ *data_size = result_size;
+ error_code = 0;
+
+fin:
+ return (error_code);
+}
+
+/*
+ * Initializes the mp_write_session. For such a session the new connection
+ * would be opened. The data should be written to the session with
+ * __cached_mp_write function. The __close_cached_mp_write_session function
+ * should be used to submit session and __abandon_cached_mp_write_session - to
+ * abandon it. When the session is submitted, the whole se
+ */
+struct cached_connection_ *
+__open_cached_mp_write_session(struct cached_connection_params const *params,
+ const char *entry_name)
+{
+ struct cached_connection_ *connection, *retval;
+ size_t name_size;
+ int error_code;
+ int result;
+
+ retval = NULL;
+ connection = __open_cached_connection(params);
+ if (connection == NULL)
+ return (NULL);
+ connection->mp_flag = 1;
+
+ result = send_credentials(connection, CET_MP_WRITE_SESSION_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ name_size = strlen(entry_name);
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_read(connection, &error_code, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (error_code != 0)
+ result = error_code;
+
+fin:
+ if (result != 0)
+ __close_cached_connection(connection);
+ else
+ retval = connection;
+ return (retval);
+}
+
+/*
+ * Adds new portion of data to the opened write session
+ */
+int
+__cached_mp_write(struct cached_connection_ *ws, const char *data,
+ size_t data_size)
+{
+ int request, result;
+ int error_code;
+
+ error_code = -1;
+
+ request = CET_MP_WRITE_SESSION_WRITE_REQUEST;
+ result = safe_write(ws, &request, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(ws, &data_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(ws, data, data_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_read(ws, &error_code, sizeof(int));
+ if (result != 0)
+ error_code = -1;
+
+fin:
+ return (error_code);
+}
+
+/*
+ * Abandons all operations with the write session. All data, that were written
+ * to the session before, are discarded.
+ */
+int
+__abandon_cached_mp_write_session(struct cached_connection_ *ws)
+{
+ int notification;
+ int result;
+
+ notification = CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION;
+ result = safe_write(ws, &notification, sizeof(int));
+ __close_cached_connection(ws);
+ return (result);
+}
+
+/*
+ * Gracefully closes the write session. The data, that were previously written
+ * to the session, are committed.
+ */
+int
+__close_cached_mp_write_session(struct cached_connection_ *ws)
+{
+ int notification;
+ int result;
+
+ notification = CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION;
+ result = safe_write(ws, &notification, sizeof(int));
+ __close_cached_connection(ws);
+ return (0);
+}
+
+struct cached_connection_ *
+__open_cached_mp_read_session(struct cached_connection_params const *params,
+ const char *entry_name)
+{
+ struct cached_connection_ *connection, *retval;
+ size_t name_size;
+ int error_code;
+ int result;
+
+ retval = NULL;
+ connection = __open_cached_connection(params);
+ if (connection == NULL)
+ return (NULL);
+ connection->mp_flag = 1;
+
+ result = send_credentials(connection, CET_MP_READ_SESSION_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ name_size = strlen(entry_name);
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+
+ result = safe_read(connection, &error_code, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (error_code != 0)
+ result = error_code;
+
+fin:
+ if (result != 0)
+ __close_cached_connection(connection);
+ else
+ retval = connection;
+ return (retval);
+}
+
+int
+__cached_mp_read(struct cached_connection_ *rs, char *data, size_t *data_size)
+{
+ size_t result_size;
+ int error_code, rec_error_code;
+ int request, result;
+
+ error_code = -1;
+ request = CET_MP_READ_SESSION_READ_REQUEST;
+ result = safe_write(rs, &request, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ result = safe_read(rs, &rec_error_code, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (rec_error_code != 0) {
+ error_code = rec_error_code;
+ goto fin;
+ }
+
+ result = safe_read(rs, &result_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ if (result_size > *data_size) {
+ *data_size = result_size;
+ error_code = -2;
+ goto fin;
+ }
+
+ result = safe_read(rs, data, result_size);
+ if (result != 0)
+ goto fin;
+
+ *data_size = result_size;
+ error_code = 0;
+
+fin:
+ return (error_code);
+}
+
+int
+__close_cached_mp_read_session(struct cached_connection_ *rs)
+{
+
+ __close_cached_connection(rs);
+ return (0);
+}
diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c
index c9be763..0e3c419 100644
--- a/lib/libc/net/nsdispatch.c
+++ b/lib/libc/net/nsdispatch.c
@@ -86,6 +86,9 @@ __FBSDID("$FreeBSD$");
#include <syslog.h>
#include <unistd.h>
#include "un-namespace.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
enum _nss_constants {
/* Number of elements allocated when we grow a vector */
@@ -124,6 +127,14 @@ static ns_mod *_nsmod;
static int __nss_builtin_handle;
static void *nss_builtin_handle = &__nss_builtin_handle;
+#ifdef NS_CACHING
+/*
+ * Cache lookup cycle prevention function - if !NULL then no cache lookups
+ * will be made
+ */
+static void *nss_cache_cycle_prevention_func = NULL;
+#endif
+
/*
* Attempt to spew relatively uniform messages to syslog.
*/
@@ -231,8 +242,6 @@ vector_free(void *vec, unsigned int *count, size_t esize,
*count = 0;
}
-
-
/*
* Comparison functions for vector_search.
*/
@@ -256,8 +265,6 @@ mtab_compare(const void *a, const void *b)
((const ns_mtab *)b)->database));
}
-
-
/*
* NSS nsmap management.
*/
@@ -318,6 +325,9 @@ nss_configure(void)
struct stat statbuf;
int result, isthreaded;
const char *path;
+#ifdef NS_CACHING
+ void *handle;
+#endif
result = 0;
isthreaded = __isthreaded;
@@ -356,6 +366,15 @@ nss_configure(void)
if (confmod == 0)
(void)atexit(nss_atexit);
confmod = statbuf.st_mtime;
+
+#ifdef NS_CACHING
+ handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
+ if (handle != NULL) {
+ nss_cache_cycle_prevention_func = dlsym(handle,
+ "_nss_cache_cycle_prevention_function");
+ dlclose(handle);
+ }
+#endif
fin:
if (isthreaded) {
(void)_pthread_rwlock_unlock(&nss_lock);
@@ -583,6 +602,12 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
void *mdata;
int isthreaded, serrno, i, result, srclistsize;
+#ifdef NS_CACHING
+ nss_cache_data cache_data;
+ nss_cache_data *cache_data_p;
+ int cache_flag;
+#endif
+
isthreaded = __isthreaded;
serrno = errno;
if (isthreaded) {
@@ -608,18 +633,80 @@ _nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database,
while (srclist[srclistsize].name != NULL)
srclistsize++;
}
+
+#ifdef NS_CACHING
+ cache_data_p = NULL;
+ cache_flag = 0;
+#endif
for (i = 0; i < srclistsize; i++) {
result = NS_NOTFOUND;
method = nss_method_lookup(srclist[i].name, database,
method_name, disp_tab, &mdata);
+
if (method != NULL) {
+#ifdef NS_CACHING
+ if (strcmp(srclist[i].name, NSSRC_CACHE) == 0 &&
+ nss_cache_cycle_prevention_func == NULL) {
+#ifdef NS_STRICT_LIBC_EID_CHECKING
+ if (issetugid() != 0)
+ continue;
+#endif
+ cache_flag = 1;
+
+ memset(&cache_data, 0, sizeof(nss_cache_data));
+ cache_data.info = (nss_cache_info const *)mdata;
+ cache_data_p = &cache_data;
+
+ va_start(ap, defaults);
+ if (cache_data.info->id_func != NULL)
+ result = __nss_common_cache_read(retval,
+ cache_data_p, ap);
+ else if (cache_data.info->marshal_func != NULL)
+ result = __nss_mp_cache_read(retval,
+ cache_data_p, ap);
+ else
+ result = __nss_mp_cache_end(retval,
+ cache_data_p, ap);
+ va_end(ap);
+ } else {
+ cache_flag = 0;
+ va_start(ap, defaults);
+ result = method(retval, mdata, ap);
+ va_end(ap);
+ }
+#else /* NS_CACHING */
va_start(ap, defaults);
result = method(retval, mdata, ap);
va_end(ap);
+#endif /* NS_CACHING */
+
if (result & (srclist[i].flags))
break;
}
}
+
+#ifdef NS_CACHING
+ if (cache_data_p != NULL &&
+ (result & (NS_NOTFOUND | NS_SUCCESS)) && cache_flag == 0) {
+ va_start(ap, defaults);
+ if (result == NS_SUCCESS) {
+ if (cache_data.info->id_func != NULL)
+ __nss_common_cache_write(retval, cache_data_p,
+ ap);
+ else if (cache_data.info->marshal_func != NULL)
+ __nss_mp_cache_write(retval, cache_data_p, ap);
+ } else if (result == NS_NOTFOUND) {
+ if (cache_data.info->id_func == NULL) {
+ if (cache_data.info->marshal_func != NULL)
+ __nss_mp_cache_write_submit(retval,
+ cache_data_p, ap);
+ } else
+ __nss_common_cache_write_negative(cache_data_p);
+ }
+ va_end(ap);
+ }
+#endif /* NS_CACHING */
+
if (isthreaded)
(void)_pthread_rwlock_unlock(&nss_lock);
fin:
diff --git a/lib/libc/rpc/getrpcent.c b/lib/libc/rpc/getrpcent.c
index b20d80d..2822c76 100644
--- a/lib/libc/rpc/getrpcent.c
+++ b/lib/libc/rpc/getrpcent.c
@@ -40,281 +40,1009 @@ __FBSDID("$FreeBSD$");
* Copyright (c) 1984 by Sun Microsystems, Inc.
*/
-#include "namespace.h"
+#include <sys/param.h>
#include <sys/types.h>
-
-#include <netinet/in.h>
+#include <sys/socket.h>
#include <arpa/inet.h>
-
#include <assert.h>
-#include <netdb.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <netinet/in.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-
+#include <stdarg.h>
+#include <stdlib.h>
#include <rpc/rpc.h>
#ifdef YP
#include <rpcsvc/yp_prot.h>
#include <rpcsvc/ypclnt.h>
#endif
+#include <unistd.h>
+#include "namespace.h"
+#include "reentrant.h"
#include "un-namespace.h"
#include "libc_private.h"
+#include "nss_tls.h"
+#ifdef NS_CACHING
+#include "nscache.h"
+#endif
-/*
- * Internet version.
- */
-static struct rpcdata {
- FILE *rpcf;
+#define RPCDB "/etc/rpc"
+
+/* nsswitch declarations */
+enum constants
+{
+ SETRPCENT = 1,
+ ENDRPCENT = 2,
+ RPCENT_STORAGE_INITIAL = 1 << 10, /* 1 KByte */
+ RPCENT_STORAGE_MAX = 1 << 20, /* 1 MByte */
+};
+
+static const ns_src defaultsrc[] = {
+ { NSSRC_FILES, NS_SUCCESS },
+#ifdef YP
+ { NSSRC_NIS, NS_SUCCESS },
+#endif
+ { NULL, 0 }
+};
+
+/* files backend declarations */
+struct files_state {
+ FILE *fp;
int stayopen;
-#define MAXALIASES 35
- char *rpc_aliases[MAXALIASES];
- struct rpcent rpc;
- char line[BUFSIZ+1];
-#ifdef YP
- char *domain;
+};
+
+static int files_rpcent(void *, void *, va_list);
+static int files_setrpcent(void *, void *, va_list);
+
+static void files_endstate(void *);
+NSS_TLS_HANDLING(files);
+
+/* nis backend declarations */
+#ifdef YP
+struct nis_state {
+ char domain[MAXHOSTNAMELEN];
char *current;
int currentlen;
+ int stepping;
+ int no_name_map;
+};
+
+static int nis_rpcent(void *, void *, va_list);
+static int nis_setrpcent(void *, void *, va_list);
+
+static void nis_endstate(void *);
+NSS_TLS_HANDLING(nis);
#endif
-} *rpcdata;
-static struct rpcent *interpret(char *val, size_t len);
+/* get** wrappers for get**_r functions declarations */
+struct rpcent_state {
+ struct rpcent rpc;
+ char *buffer;
+ size_t bufsize;
+};
+static void rpcent_endstate(void *);
+NSS_TLS_HANDLING(rpcent);
-#ifdef YP
-static int __yp_nomap = 0;
-#endif /* YP */
+union key {
+ const char *name;
+ int number;
+};
-#define RPCDB "/etc/rpc"
+static int wrap_getrpcbyname_r(union key, struct rpcent *, char *,
+ size_t, struct rpcent **);
+static int wrap_getrpcbynumber_r(union key, struct rpcent *, char *,
+ size_t, struct rpcent **);
+static int wrap_getrpcent_r(union key, struct rpcent *, char *,
+ size_t, struct rpcent **);
+static struct rpcent *getrpc(int (*fn)(union key, struct rpcent *, char *,
+ size_t, struct rpcent **), union key);
-static struct rpcdata *_rpcdata(void);
+#ifdef NS_CACHING
+static int rpc_id_func(char *, size_t *, va_list, void *);
+static int rpc_marshal_func(char *, size_t *, void *, va_list, void *);
+static int rpc_unmarshal_func(char *, size_t, void *, va_list, void *);
+#endif
-static struct rpcdata *
-_rpcdata()
+static int
+rpcent_unpack(char *p, struct rpcent *rpc, char **r_aliases,
+ size_t aliases_size, int *errnop)
{
- struct rpcdata *d = rpcdata;
+ char *cp, **q;
+
+ assert(p != NULL);
+
+ if (*p == '#')
+ return (-1);
+ cp = strpbrk(p, "#\n");
+ if (cp == NULL)
+ return (-1);
+ *cp = '\0';
+ cp = strpbrk(p, " \t");
+ if (cp == NULL)
+ return (-1);
+ *cp++ = '\0';
+ /* THIS STUFF IS INTERNET SPECIFIC */
+ rpc->r_name = p;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ rpc->r_number = atoi(cp);
+ q = rpc->r_aliases = r_aliases;
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
+ while (cp && *cp) {
+ if (*cp == ' ' || *cp == '\t') {
+ cp++;
+ continue;
+ }
+ if (q < &(r_aliases[aliases_size - 1]))
+ *q++ = cp;
+ else {
+ *errnop = ERANGE;
+ return -1;
+ }
- if (d == 0) {
- d = (struct rpcdata *)calloc(1, sizeof (struct rpcdata));
- rpcdata = d;
+ cp = strpbrk(cp, " \t");
+ if (cp != NULL)
+ *cp++ = '\0';
}
- return (d);
+ *q = NULL;
+ return 0;
}
-struct rpcent *
-getrpcbynumber(number)
+/* files backend implementation */
+static void
+files_endstate(void *p)
+{
+ FILE * f;
+
+ if (p == NULL)
+ return;
+
+ f = ((struct files_state *)p)->fp;
+ if (f != NULL)
+ fclose(f);
+
+ free(p);
+}
+
+static int
+files_rpcent(void *retval, void *mdata, va_list ap)
+{
+ char *name;
int number;
+ struct rpcent *rpc;
+ char *buffer;
+ size_t bufsize;
+ int *errnop;
+
+ char *line;
+ size_t linesize;
+ char **aliases;
+ int aliases_size;
+ char **rp;
+
+ struct files_state *st;
+ int rv;
+ int stayopen;
+ enum nss_lookup_type how;
+
+ how = (enum nss_lookup_type)mdata;
+ switch (how)
+ {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ number = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ return (NS_NOTFOUND);
+ }
+
+ rpc = va_arg(ap, struct rpcent *);
+ buffer = va_arg(ap, char *);
+ bufsize = va_arg(ap, size_t);
+ errnop = va_arg(ap, int *);
+
+ *errnop = files_getstate(&st);
+ if (*errnop != 0)
+ return (NS_UNAVAIL);
+
+ if (st->fp == NULL && (st->fp = fopen(RPCDB, "r")) == NULL) {
+ *errnop = errno;
+ return (NS_UNAVAIL);
+ }
+
+ if (how == nss_lt_all)
+ stayopen = 1;
+ else {
+ rewind(st->fp);
+ stayopen = st->stayopen;
+ }
+
+ do {
+ if ((line = fgetln(st->fp, &linesize)) == NULL) {
+ *errnop = errno;
+ rv = NS_RETURN;
+ break;
+ }
+
+ if (bufsize <= linesize + _ALIGNBYTES + sizeof(char *)) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+
+ aliases = (char **)_ALIGN(&buffer[linesize+1]);
+ aliases_size = (buffer + bufsize -
+ (char *)aliases)/sizeof(char *);
+ if (aliases_size < 1) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+
+ memcpy(buffer, line, linesize);
+ buffer[linesize] = '\0';
+
+ rv = rpcent_unpack(buffer, rpc, aliases, aliases_size, errnop);
+ if (rv != 0) {
+ if (*errnop == 0) {
+ rv = NS_NOTFOUND;
+ continue;
+ }
+ else {
+ rv = NS_RETURN;
+ break;
+ }
+ }
+
+ switch (how)
+ {
+ case nss_lt_name:
+ if (strcmp(rpc->r_name, name) == 0)
+ goto done;
+ for (rp = rpc->r_aliases; *rp != NULL; rp++) {
+ if (strcmp(*rp, name) == 0)
+ goto done;
+ }
+ rv = NS_NOTFOUND;
+ continue;
+done:
+ rv = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ rv = (rpc->r_number == number) ? NS_SUCCESS :
+ NS_NOTFOUND;
+ break;
+ case nss_lt_all:
+ rv = NS_SUCCESS;
+ break;
+ }
+
+ } while (!(rv & NS_TERMINATE));
+
+ if (!stayopen && st->fp!=NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+
+ if ((rv == NS_SUCCESS) && (retval != NULL))
+ *((struct rpcent **)retval) = rpc;
+
+ return (rv);
+}
+
+static int
+files_setrpcent(void *retval, void *mdata, va_list ap)
{
-#ifdef YP
- int reason;
- char adrstr[16];
-#endif
- struct rpcent *p;
- struct rpcdata *d = _rpcdata();
+ struct files_state *st;
+ int rv;
+ int f;
- if (d == 0)
- return (0);
-#ifdef YP
- if (!__yp_nomap && _yp_check(&d->domain)) {
- sprintf(adrstr, "%d", number);
- reason = yp_match(d->domain, "rpc.bynumber", adrstr, strlen(adrstr),
- &d->current, &d->currentlen);
- switch(reason) {
- case 0:
- break;
- case YPERR_MAP:
- __yp_nomap = 1;
- goto no_yp;
- break;
- default:
- return(0);
- break;
- }
- d->current[d->currentlen] = '\0';
- p = interpret(d->current, d->currentlen);
- (void) free(d->current);
- return p;
- }
-no_yp:
-#endif /* YP */
-
- setrpcent(0);
- while ((p = getrpcent()) != NULL) {
- if (p->r_number == number)
+ rv = files_getstate(&st);
+ if (rv != 0)
+ return (NS_UNAVAIL);
+
+ switch ((enum constants)mdata)
+ {
+ case SETRPCENT:
+ f = va_arg(ap,int);
+ if (st->fp == NULL)
+ st->fp = fopen(RPCDB, "r");
+ else
+ rewind(st->fp);
+ st->stayopen |= f;
+ break;
+ case ENDRPCENT:
+ if (st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+ st->stayopen = 0;
+ break;
+ default:
+ break;
+ }
+
+ return (NS_UNAVAIL);
+}
+
+/* nis backend implementation */
+#ifdef YP
+static void
+nis_endstate(void *p)
+{
+ if (p == NULL)
+ return;
+
+ free(((struct nis_state *)p)->current);
+ free(p);
+}
+
+static int
+nis_rpcent(void *retval, void *mdata, va_list ap)
+{
+ char *name;
+ int number;
+ struct rpcent *rpc;
+ char *buffer;
+ size_t bufsize;
+ int *errnop;
+
+ char **rp;
+ char **aliases;
+ int aliases_size;
+
+ char *lastkey;
+ char *resultbuf;
+ int resultbuflen;
+ char buf[YPMAXRECORD + 2];
+
+ struct nis_state *st;
+ int rv;
+ enum nss_lookup_type how;
+ int no_name_active;
+
+ how = (enum nss_lookup_type)mdata;
+ switch (how)
+ {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ number = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ return (NS_NOTFOUND);
+ }
+
+ rpc = va_arg(ap, struct rpcent *);
+ buffer = va_arg(ap, char *);
+ bufsize = va_arg(ap, size_t);
+ errnop = va_arg(ap, int *);
+
+ *errnop = nis_getstate(&st);
+ if (*errnop != 0)
+ return (NS_UNAVAIL);
+
+ if (st->domain[0] == '\0') {
+ if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
+ *errnop = errno;
+ return (NS_UNAVAIL);
+ }
+ }
+
+ no_name_active = 0;
+ do {
+ switch (how)
+ {
+ case nss_lt_name:
+ if (!st->no_name_map)
+ {
+ snprintf(buf, sizeof buf, "%s", name);
+ rv = yp_match(st->domain, "rpc.byname", buf,
+ strlen(buf), &resultbuf, &resultbuflen);
+
+ switch (rv) {
+ case 0:
+ break;
+ case YPERR_MAP:
+ st->stepping = 0;
+ no_name_active = 1;
+ how = nss_lt_all;
+
+ rv = NS_NOTFOUND;
+ continue;
+ default:
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ } else {
+ st->stepping = 0;
+ no_name_active = 1;
+ how = nss_lt_all;
+
+ rv = NS_NOTFOUND;
+ continue;
+ }
+ break;
+ case nss_lt_id:
+ snprintf(buf, sizeof buf, "%d", number);
+ if (yp_match(st->domain, "rpc.bynumber", buf,
+ strlen(buf), &resultbuf, &resultbuflen)) {
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ break;
+ case nss_lt_all:
+ if (!st->stepping) {
+ rv = yp_first(st->domain, "rpc.bynumber",
+ &st->current,
+ &st->currentlen, &resultbuf,
+ &resultbuflen);
+ if (rv) {
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ st->stepping = 1;
+ } else {
+ lastkey = st->current;
+ rv = yp_next(st->domain, "rpc.bynumber",
+ st->current,
+ st->currentlen, &st->current,
+ &st->currentlen,
+ &resultbuf, &resultbuflen);
+ free(lastkey);
+ if (rv) {
+ st->stepping = 0;
+ rv = NS_NOTFOUND;
+ goto fin;
+ }
+ }
+ break;
+ }
+
+ /* we need a room for additional \n symbol */
+ if (bufsize <= resultbuflen + 1 + _ALIGNBYTES +
+ sizeof(char *)) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
break;
+ }
+
+ aliases=(char **)_ALIGN(&buffer[resultbuflen+2]);
+ aliases_size = (buffer + bufsize - (char *)aliases) /
+ sizeof(char *);
+ if (aliases_size < 1) {
+ *errnop = ERANGE;
+ rv = NS_RETURN;
+ break;
+ }
+
+ /*
+ * rpcent_unpack expects lines terminated with \n -- make it happy
+ */
+ memcpy(buffer, resultbuf, resultbuflen);
+ buffer[resultbuflen] = '\n';
+ buffer[resultbuflen+1] = '\0';
+ free(resultbuf);
+
+ if (rpcent_unpack(buffer, rpc, aliases, aliases_size,
+ errnop) != 0) {
+ if (*errnop == 0)
+ rv = NS_NOTFOUND;
+ else
+ rv = NS_RETURN;
+ } else {
+ if ((how == nss_lt_all) && (no_name_active != 0)) {
+ if (strcmp(rpc->r_name, name) == 0)
+ goto done;
+ for (rp = rpc->r_aliases; *rp != NULL; rp++) {
+ if (strcmp(*rp, name) == 0)
+ goto done;
+ }
+ rv = NS_NOTFOUND;
+ continue;
+done:
+ rv = NS_SUCCESS;
+ } else
+ rv = NS_SUCCESS;
+ }
+
+ } while (!(rv & NS_TERMINATE) && (how == nss_lt_all));
+
+fin:
+ if ((rv == NS_SUCCESS) && (retval != NULL))
+ *((struct rpcent **)retval) = rpc;
+
+ return (rv);
+}
+
+static int
+nis_setrpcent(void *retval, void *mdata, va_list ap)
+{
+ struct nis_state *st;
+ int rv;
+
+ rv = nis_getstate(&st);
+ if (rv != 0)
+ return (NS_UNAVAIL);
+
+ switch ((enum constants)mdata)
+ {
+ case SETRPCENT:
+ case ENDRPCENT:
+ free(st->current);
+ st->current = NULL;
+ st->stepping = 0;
+ break;
+ default:
+ break;
}
- endrpcent();
- return (p);
+
+ return (NS_UNAVAIL);
}
+#endif
-struct rpcent *
-getrpcbyname(name)
+#ifdef NS_CACHING
+static int
+rpc_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
+{
char *name;
+ int rpc;
+
+ size_t desired_size, size;
+ enum nss_lookup_type lookup_type;
+ int res = NS_UNAVAIL;
+
+ lookup_type = (enum nss_lookup_type)cache_mdata;
+ switch (lookup_type) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+
+ size = strlen(name);
+ desired_size = sizeof(enum nss_lookup_type) + size + 1;
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
+
+ res = NS_SUCCESS;
+ break;
+ case nss_lt_id:
+ rpc = va_arg(ap, int);
+
+ desired_size = sizeof(enum nss_lookup_type) + sizeof(int);
+ if (desired_size > *buffer_size) {
+ res = NS_RETURN;
+ goto fin;
+ }
+
+ memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
+ memcpy(buffer + sizeof(enum nss_lookup_type), &rpc,
+ sizeof(int));
+
+ res = NS_SUCCESS;
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+fin:
+ *buffer_size = desired_size;
+ return (res);
+}
+
+static int
+rpc_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
{
- struct rpcent *rpc = NULL;
- char **rp;
+ char *name;
+ int num;
+ struct rpcent *rpc;
+ char *orig_buf;
+ size_t orig_buf_size;
- assert(name != NULL);
+ struct rpcent new_rpc;
+ size_t desired_size, size, aliases_size;
+ char *p;
+ char **alias;
- setrpcent(0);
- while ((rpc = getrpcent()) != NULL) {
- if (strcmp(rpc->r_name, name) == 0)
- goto done;
- for (rp = rpc->r_aliases; *rp != NULL; rp++) {
- if (strcmp(*rp, name) == 0)
- goto done;
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ num = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ rpc = va_arg(ap, struct rpcent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+
+ desired_size = _ALIGNBYTES + sizeof(struct rpcent) + sizeof(char *);
+ if (rpc->r_name != NULL)
+ desired_size += strlen(rpc->r_name) + 1;
+
+ if (rpc->r_aliases != NULL) {
+ aliases_size = 0;
+ for (alias = rpc->r_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
}
+
+ desired_size += _ALIGNBYTES + (aliases_size + 1) *
+ sizeof(char *);
}
-done:
- endrpcent();
- return (rpc);
+
+ if (*buffer_size < desired_size) {
+ /* this assignment is here for future use */
+ *buffer_size = desired_size;
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_rpc, rpc, sizeof(struct rpcent));
+
+ *buffer_size = desired_size;
+ memset(buffer, 0, desired_size);
+ p = buffer + sizeof(struct rpcent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct rpcent), &p, sizeof(char *));
+ p = (char *)_ALIGN(p);
+
+ if (new_rpc.r_name != NULL) {
+ size = strlen(new_rpc.r_name);
+ memcpy(p, new_rpc.r_name, size);
+ new_rpc.r_name = p;
+ p += size + 1;
+ }
+
+ if (new_rpc.r_aliases != NULL) {
+ p = (char *)_ALIGN(p);
+ memcpy(p, new_rpc.r_aliases, sizeof(char *) * aliases_size);
+ new_rpc.r_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (alias = new_rpc.r_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_rpc, sizeof(struct rpcent));
+ return (NS_SUCCESS);
}
-void
-setrpcent(f)
- int f;
+static int
+rpc_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
+ void *cache_mdata)
{
- struct rpcdata *d = _rpcdata();
+ char *name;
+ int num;
+ struct rpcent *rpc;
+ char *orig_buf;
+ size_t orig_buf_size;
+ int *ret_errno;
- if (d == 0)
- return;
-#ifdef YP
- if (!__yp_nomap && _yp_check(NULL)) {
- if (d->current)
- free(d->current);
- d->current = NULL;
- d->currentlen = 0;
- return;
- }
- __yp_nomap = 0;
-#endif /* YP */
- if (d->rpcf == NULL)
- d->rpcf = fopen(RPCDB, "r");
+ char *p;
+ char **alias;
+
+ switch ((enum nss_lookup_type)cache_mdata) {
+ case nss_lt_name:
+ name = va_arg(ap, char *);
+ break;
+ case nss_lt_id:
+ num = va_arg(ap, int);
+ break;
+ case nss_lt_all:
+ break;
+ default:
+ /* should be unreachable */
+ return (NS_UNAVAIL);
+ }
+
+ rpc = va_arg(ap, struct rpcent *);
+ orig_buf = va_arg(ap, char *);
+ orig_buf_size = va_arg(ap, size_t);
+ ret_errno = va_arg(ap, int *);
+
+ if (orig_buf_size <
+ buffer_size - sizeof(struct rpcent) - sizeof(char *)) {
+ *ret_errno = ERANGE;
+ return (NS_RETURN);
+ }
+
+ memcpy(rpc, buffer, sizeof(struct rpcent));
+ memcpy(&p, buffer + sizeof(struct rpcent), sizeof(char *));
+
+ orig_buf = (char *)_ALIGN(orig_buf);
+ memcpy(orig_buf, buffer + sizeof(struct rpcent) + sizeof(char *) +
+ _ALIGN(p) - (size_t)p,
+ buffer_size - sizeof(struct rpcent) - sizeof(char *) -
+ _ALIGN(p) + (size_t)p);
+ p = (char *)_ALIGN(p);
+
+ NS_APPLY_OFFSET(rpc->r_name, orig_buf, p, char *);
+ if (rpc->r_aliases != NULL) {
+ NS_APPLY_OFFSET(rpc->r_aliases, orig_buf, p, char **);
+
+ for (alias = rpc->r_aliases ; *alias; ++alias)
+ NS_APPLY_OFFSET(*alias, orig_buf, p, char *);
+ }
+
+ if (retval != NULL)
+ *((struct rpcent **)retval) = rpc;
+
+ return (NS_SUCCESS);
+}
+
+NSS_MP_CACHE_HANDLING(rpc);
+#endif /* NS_CACHING */
+
+
+/* get**_r functions implementation */
+static int
+getrpcbyname_r(const char *name, struct rpcent *rpc, char *buffer,
+ size_t bufsize, struct rpcent **result)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ rpc, (void *)nss_lt_name,
+ rpc_id_func, rpc_marshal_func, rpc_unmarshal_func);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_rpcent, (void *)nss_lt_name },
+#ifdef YP
+ { NSSRC_NIS, nis_rpcent, (void *)nss_lt_name },
+#endif
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcbyname_r", defaultsrc,
+ name, rpc, buffer, bufsize, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
else
- rewind(d->rpcf);
- d->stayopen |= f;
+ return (ret_errno);
}
-void
-endrpcent()
+static int
+getrpcbynumber_r(int number, struct rpcent *rpc, char *buffer,
+ size_t bufsize, struct rpcent **result)
{
- struct rpcdata *d = _rpcdata();
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info =
+ NS_COMMON_CACHE_INFO_INITIALIZER(
+ rpc, (void *)nss_lt_id,
+ rpc_id_func, rpc_marshal_func, rpc_unmarshal_func);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_rpcent, (void *)nss_lt_id },
+#ifdef YP
+ { NSSRC_NIS, nis_rpcent, (void *)nss_lt_id },
+#endif
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcbynumber_r", defaultsrc,
+ number, rpc, buffer, bufsize, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
+}
+
+static int
+getrpcent_r(struct rpcent *rpc, char *buffer, size_t bufsize,
+ struct rpcent **result)
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ rpc, (void *)nss_lt_all,
+ rpc_marshal_func, rpc_unmarshal_func);
+#endif
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_rpcent, (void *)nss_lt_all },
+#ifdef YP
+ { NSSRC_NIS, nis_rpcent, (void *)nss_lt_all },
+#endif
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+ int rv, ret_errno;
+
+ ret_errno = 0;
+ *result = NULL;
+ rv = nsdispatch(result, dtab, NSDB_RPC, "getrpcent_r", defaultsrc,
+ rpc, buffer, bufsize, &ret_errno);
+
+ if (rv == NS_SUCCESS)
+ return (0);
+ else
+ return (ret_errno);
+}
- if (d == 0)
+/* get** wrappers for get**_r functions implementation */
+static void
+rpcent_endstate(void *p)
+{
+ if (p == NULL)
return;
-#ifdef YP
- if (!__yp_nomap && _yp_check(NULL)) {
- if (d->current && !d->stayopen)
- free(d->current);
- d->current = NULL;
- d->currentlen = 0;
- return;
- }
- __yp_nomap = 0;
-#endif /* YP */
- if (d->rpcf && !d->stayopen) {
- fclose(d->rpcf);
- d->rpcf = NULL;
+
+ free(((struct rpcent_state *)p)->buffer);
+ free(p);
+}
+
+static int
+wrap_getrpcbyname_r(union key key, struct rpcent *rpc, char *buffer,
+ size_t bufsize, struct rpcent **res)
+{
+ return (getrpcbyname_r(key.name, rpc, buffer, bufsize, res));
+}
+
+static int
+wrap_getrpcbynumber_r(union key key, struct rpcent *rpc, char *buffer,
+ size_t bufsize, struct rpcent **res)
+{
+ return (getrpcbynumber_r(key.number, rpc, buffer, bufsize, res));
+}
+
+static int
+wrap_getrpcent_r(union key key __unused, struct rpcent *rpc, char *buffer,
+ size_t bufsize, struct rpcent **res)
+{
+ return (getrpcent_r(rpc, buffer, bufsize, res));
+}
+
+static struct rpcent *
+getrpc(int (*fn)(union key, struct rpcent *, char *, size_t, struct rpcent **),
+ union key key)
+{
+ int rv;
+ struct rpcent *res;
+ struct rpcent_state * st;
+
+ rv=rpcent_getstate(&st);
+ if (rv != 0) {
+ errno = rv;
+ return NULL;
+ }
+
+ if (st->buffer == NULL) {
+ st->buffer = malloc(RPCENT_STORAGE_INITIAL);
+ if (st->buffer == NULL)
+ return (NULL);
+ st->bufsize = RPCENT_STORAGE_INITIAL;
}
+ do {
+ rv = fn(key, &st->rpc, st->buffer, st->bufsize, &res);
+ if (res == NULL && rv == ERANGE) {
+ free(st->buffer);
+ if ((st->bufsize << 1) > RPCENT_STORAGE_MAX) {
+ st->buffer = NULL;
+ errno = ERANGE;
+ return (NULL);
+ }
+ st->bufsize <<= 1;
+ st->buffer = malloc(st->bufsize);
+ if (st->buffer == NULL)
+ return (NULL);
+ }
+ } while (res == NULL && rv == ERANGE);
+ if (rv != 0)
+ errno = rv;
+
+ return (res);
+}
+
+struct rpcent *
+getrpcbyname(char *name)
+{
+ union key key;
+
+ key.name = name;
+
+ return (getrpc(wrap_getrpcbyname_r, key));
+}
+
+struct rpcent *
+getrpcbynumber(int number)
+{
+ union key key;
+
+ key.number = number;
+
+ return (getrpc(wrap_getrpcbynumber_r, key));
}
struct rpcent *
getrpcent()
{
- struct rpcdata *d = _rpcdata();
-#ifdef YP
- struct rpcent *hp;
- int reason;
- char *val = NULL;
- int vallen;
-#endif
+ union key key;
- if (d == 0)
- return(NULL);
-#ifdef YP
- if (!__yp_nomap && _yp_check(&d->domain)) {
- if (d->current == NULL && d->currentlen == 0) {
- reason = yp_first(d->domain, "rpc.bynumber",
- &d->current, &d->currentlen,
- &val, &vallen);
- } else {
- reason = yp_next(d->domain, "rpc.bynumber",
- d->current, d->currentlen,
- &d->current, &d->currentlen,
- &val, &vallen);
- }
- switch(reason) {
- case 0:
- break;
- case YPERR_MAP:
- __yp_nomap = 1;
- goto no_yp;
- break;
- default:
- return(0);
- break;
- }
- val[vallen] = '\0';
- hp = interpret(val, vallen);
- (void) free(val);
- return hp;
- }
-no_yp:
-#endif /* YP */
- if (d->rpcf == NULL && (d->rpcf = fopen(RPCDB, "r")) == NULL)
- return (NULL);
- /* -1 so there is room to append a \n below */
- if (fgets(d->line, BUFSIZ - 1, d->rpcf) == NULL)
- return (NULL);
- return (interpret(d->line, strlen(d->line)));
+ key.number = 0; /* not used */
+
+ return (getrpc(wrap_getrpcent_r, key));
}
-static struct rpcent *
-interpret(val, len)
- char *val;
- size_t len;
+void
+setrpcent(int stayopen)
{
- struct rpcdata *d = _rpcdata();
- char *p;
- char *cp, **q;
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ rpc, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
- assert(val != NULL);
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setrpcent, (void *)SETRPCENT },
+#ifdef YP
+ { NSSRC_NIS, nis_setrpcent, (void *)SETRPCENT },
+#endif
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
- if (d == 0)
- return (0);
- (void) strncpy(d->line, val, BUFSIZ);
- d->line[BUFSIZ] = '\0';
- p = d->line;
- p[len] = '\n';
- if (*p == '#')
- return (getrpcent());
- cp = strpbrk(p, "#\n");
- if (cp == NULL)
- return (getrpcent());
- *cp = '\0';
- cp = strpbrk(p, " \t");
- if (cp == NULL)
- return (getrpcent());
- *cp++ = '\0';
- /* THIS STUFF IS INTERNET SPECIFIC */
- d->rpc.r_name = d->line;
- while (*cp == ' ' || *cp == '\t')
- cp++;
- d->rpc.r_number = atoi(cp);
- q = d->rpc.r_aliases = d->rpc_aliases;
- cp = strpbrk(cp, " \t");
- if (cp != NULL)
- *cp++ = '\0';
- while (cp && *cp) {
- if (*cp == ' ' || *cp == '\t') {
- cp++;
- continue;
- }
- if (q < &(d->rpc_aliases[MAXALIASES - 1]))
- *q++ = cp;
- cp = strpbrk(cp, " \t");
- if (cp != NULL)
- *cp++ = '\0';
- }
- *q = NULL;
- return (&d->rpc);
+ (void)nsdispatch(NULL, dtab, NSDB_RPC, "setrpcent", defaultsrc,
+ stayopen);
}
+void
+endrpcent()
+{
+#ifdef NS_CACHING
+ static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
+ rpc, (void *)nss_lt_all,
+ NULL, NULL);
+#endif
+
+ static const ns_dtab dtab[] = {
+ { NSSRC_FILES, files_setrpcent, (void *)ENDRPCENT },
+#ifdef YP
+ { NSSRC_NIS, nis_setrpcent, (void *)ENDRPCENT },
+#endif
+#ifdef NS_CACHING
+ NS_CACHE_CB(&cache_info)
+#endif
+ { NULL, NULL, NULL }
+ };
+
+ (void)nsdispatch(NULL, dtab, NSDB_SERVICES, "endrpcent", defaultsrc);
+}
diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk
index 9e052e1..49c8501 100644
--- a/share/mk/bsd.own.mk
+++ b/share/mk/bsd.own.mk
@@ -243,6 +243,7 @@ WITHOUT_${var}=
NIS \
NLS \
NLS_CATALOGS \
+ NS_CACHING \
OBJC \
OPENSSH \
OPENSSL \
@@ -331,6 +332,7 @@ WITH_IDEA=
NIS \
NLS \
NLS_CATALOGS \
+ NS_CACHING \
OBJC \
OPENSSH \
OPENSSL \
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 0b2d93e..524f9c2 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -27,6 +27,7 @@ SUBDIR= ac \
bsnmpd \
${_btxld} \
burncd \
+ ${_cached} \
cdcontrol \
chkgrp \
chown \
@@ -225,6 +226,10 @@ _rndc-confgen= rndc-confgen
_bluetooth= bluetooth
.endif
+.if ${MK_NS_CACHING} != "no"
+_cached= cached
+.endif
+
.if ${MK_OPENSSL} != "no"
_keyserv= keyserv
.endif
diff --git a/usr.sbin/cached/Makefile b/usr.sbin/cached/Makefile
new file mode 100644
index 0000000..5478341
--- /dev/null
+++ b/usr.sbin/cached/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+PROG=cached
+PROGNAME=cached
+MAN=cached.conf.5 cached.8
+
+WARNS?=2
+SRCS= agent.c cached.c cachedcli.c cachelib.c cacheplcs.c debug.c log.c \
+ config.c query.c mp_ws_query.c mp_rs_query.c singletons.c protocol.c \
+ parser.c
+CFLAGS+= -DCONFIG_PATH="\"${PREFIX}/etc/cached.conf\""
+DPADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL}
+LDADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL}
+LDFLAGS+= -Xlinker --export-dynamic
+
+.PATH: ${.CURDIR}/agents
+.include "agents/Makefile.inc"
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cached/agent.c b/usr.sbin/cached/agent.c
new file mode 100644
index 0000000..2d58ef1
--- /dev/null
+++ b/usr.sbin/cached/agent.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include "agent.h"
+#include "debug.h"
+
+static int
+agent_cmp_func(const void *a1, const void *a2)
+{
+ struct agent const *ap1 = *((struct agent const **)a1);
+ struct agent const *ap2 = *((struct agent const **)a2);
+ int res;
+
+ res = strcmp(ap1->name, ap2->name);
+ if (res == 0) {
+ if (ap1->type == ap2->type)
+ res = 0;
+ else if (ap1->type < ap2->type)
+ res = -1;
+ else
+ res = 1;
+ }
+
+ return (res);
+}
+
+struct agent_table *
+init_agent_table()
+{
+ struct agent_table *retval;
+
+ TRACE_IN(init_agent_table);
+ retval = (struct agent_table *)malloc(sizeof(struct agent_table));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct agent_table));
+
+ TRACE_OUT(init_agent_table);
+ return (retval);
+}
+
+void
+register_agent(struct agent_table *at, struct agent *a)
+{
+ struct agent **new_agents;
+ size_t new_agents_num;
+
+ TRACE_IN(register_agent);
+ assert(at != NULL);
+ assert(a != NULL);
+ new_agents_num = at->agents_num + 1;
+ new_agents = (struct agent **)malloc(sizeof(struct agent *) *
+ new_agents_num);
+ assert(new_agents != NULL);
+ memcpy(new_agents, at->agents, at->agents_num * sizeof(struct agent *));
+ new_agents[new_agents_num - 1] = a;
+ qsort(new_agents, new_agents_num, sizeof(struct agent *),
+ agent_cmp_func);
+
+ free(at->agents);
+ at->agents = new_agents;
+ at->agents_num = new_agents_num;
+ TRACE_OUT(register_agent);
+}
+
+struct agent *
+find_agent(struct agent_table *at, const char *name, enum agent_type type)
+{
+ struct agent **res;
+ struct agent model, *model_p;
+
+ TRACE_IN(find_agent);
+ model.name = (char *)name;
+ model.type = type;
+ model_p = &model;
+ res = bsearch(&model_p, at->agents, at->agents_num,
+ sizeof(struct agent *), agent_cmp_func);
+
+ TRACE_OUT(find_agent);
+ return ( res == NULL ? NULL : *res);
+}
+
+void
+destroy_agent_table(struct agent_table *at)
+{
+ size_t i;
+
+ TRACE_IN(destroy_agent_table);
+ assert(at != NULL);
+ for (i = 0; i < at->agents_num; ++i) {
+ free(at->agents[i]->name);
+ free(at->agents[i]);
+ }
+
+ free(at->agents);
+ free(at);
+ TRACE_OUT(destroy_agent_table);
+}
diff --git a/usr.sbin/cached/agent.h b/usr.sbin/cached/agent.h
new file mode 100644
index 0000000..5dc368d
--- /dev/null
+++ b/usr.sbin/cached/agent.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_AGENT_H__
+#define __CACHED_AGENT_H__
+
+/*
+ * Agents are used to perform the actual lookups from the caching daemon.
+ * There are two types of daemons: for common requests and for multipart
+ * requests.
+ * All agents are stored in the agents table, which is the singleton.
+ */
+
+enum agent_type {
+ COMMON_AGENT = 0,
+ MULTIPART_AGENT = 1
+};
+
+struct agent {
+ char *name;
+ enum agent_type type;
+};
+
+struct common_agent {
+ struct agent parent;
+ int (*lookup_func)(const char *, size_t, char **, size_t *);
+};
+
+struct multipart_agent {
+ struct agent parent;
+ void *(*mp_init_func)();
+ int (*mp_lookup_func)(char **, size_t *, void *);
+ void (*mp_destroy_func)(void *);
+};
+
+struct agent_table {
+ struct agent **agents;
+ size_t agents_num;
+};
+
+extern struct agent_table *init_agent_table();
+extern void register_agent(struct agent_table *, struct agent *);
+extern struct agent *find_agent(struct agent_table *, const char *,
+ enum agent_type);
+extern void destroy_agent_table(struct agent_table *);
+
+#endif
diff --git a/usr.sbin/cached/agents/Makefile.inc b/usr.sbin/cached/agents/Makefile.inc
new file mode 100644
index 0000000..1be32e1
--- /dev/null
+++ b/usr.sbin/cached/agents/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+SRCS += passwd.c group.c services.c
diff --git a/usr.sbin/cached/agents/group.c b/usr.sbin/cached/agents/group.c
new file mode 100644
index 0000000..b9190be
--- /dev/null
+++ b/usr.sbin/cached/agents/group.c
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <nsswitch.h>
+#include <grp.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "passwd.h"
+
+static int group_marshal_func(struct group *, char *, size_t *);
+static int group_lookup_func(const char *, size_t, char **, size_t *);
+static void *group_mp_init_func();
+static int group_mp_lookup_func(char **, size_t *, void *);
+static void group_mp_destroy_func(void *);
+
+static int
+group_marshal_func(struct group *grp, char *buffer, size_t *buffer_size)
+{
+ struct group new_grp;
+ size_t desired_size, size, mem_size;
+ char *p, **mem;
+
+ TRACE_IN(group_marshal_func);
+ desired_size = ALIGNBYTES + sizeof(struct group) + sizeof(char *);
+
+ if (grp->gr_name != NULL)
+ desired_size += strlen(grp->gr_name) + 1;
+ if (grp->gr_passwd != NULL)
+ desired_size += strlen(grp->gr_passwd) + 1;
+
+ if (grp->gr_mem != NULL) {
+ mem_size = 0;
+ for (mem = grp->gr_mem; *mem; ++mem) {
+ desired_size += strlen(*mem) + 1;
+ ++mem_size;
+ }
+
+ desired_size += ALIGNBYTES + (mem_size + 1) * sizeof(char *);
+ }
+
+ if ((desired_size > *buffer_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(group_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_grp, grp, sizeof(struct group));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct group) + sizeof(char *);
+ memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
+ p = (char *)ALIGN(p);
+
+ if (new_grp.gr_name != NULL) {
+ size = strlen(new_grp.gr_name);
+ memcpy(p, new_grp.gr_name, size);
+ new_grp.gr_name = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_passwd != NULL) {
+ size = strlen(new_grp.gr_passwd);
+ memcpy(p, new_grp.gr_passwd, size);
+ new_grp.gr_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_mem != NULL) {
+ p = (char *)ALIGN(p);
+ memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
+ new_grp.gr_mem = (char **)p;
+ p += sizeof(char *) * (mem_size + 1);
+
+ for (mem = new_grp.gr_mem; *mem; ++mem) {
+ size = strlen(*mem);
+ memcpy(p, *mem, size);
+ *mem = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_grp, sizeof(struct group));
+ TRACE_OUT(group_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+group_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *name;
+ size_t size;
+ gid_t gid;
+
+ struct group *result;
+
+ TRACE_IN(group_lookup_func);
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(group_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ name = (char *)malloc(size);
+ assert(name != NULL);
+ memset(name, 0, size);
+ memcpy(name, key + sizeof(enum nss_lookup_type), size - 1);
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(gid_t)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&gid, key + sizeof(enum nss_lookup_type), sizeof(gid_t));
+ break;
+ default:
+ TRACE_OUT(group_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ TRACE_STR(name);
+ result = getgrnam(name);
+ free(name);
+ break;
+ case nss_lt_id:
+ result = getgrgid(gid);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ group_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ group_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(group_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+group_mp_init_func()
+{
+ TRACE_IN(group_mp_init_func);
+ setgrent();
+ TRACE_OUT(group_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+group_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct group *result;
+
+ TRACE_IN(group_mp_lookup_func);
+ result = getgrent();
+ if (result != NULL) {
+ group_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ group_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(group_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+group_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(group_mp_destroy_func);
+ TRACE_OUT(group_mp_destroy_func);
+}
+
+struct agent *
+init_group_agent()
+{
+ struct common_agent *retval;
+
+ TRACE_IN(init_group_agent);
+ retval = (struct common_agent *)malloc(sizeof(struct common_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct common_agent));
+
+ retval->parent.name = strdup("group");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = group_lookup_func;
+
+ TRACE_OUT(init_group_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_group_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_group_mp_agent);
+ retval = (struct multipart_agent *)malloc(
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct multipart_agent));
+
+ retval->parent.name = strdup("group");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = group_mp_init_func;
+ retval->mp_lookup_func = group_mp_lookup_func;
+ retval->mp_destroy_func = group_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_group_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/cached/agents/group.h b/usr.sbin/cached/agents/group.h
new file mode 100644
index 0000000..e6c7397
--- /dev/null
+++ b/usr.sbin/cached/agents/group.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "../agent.h"
+
+extern struct agent *init_group_agent();
+extern struct agent *init_group_mp_agent();
diff --git a/usr.sbin/cached/agents/passwd.c b/usr.sbin/cached/agents/passwd.c
new file mode 100644
index 0000000..50c55ba
--- /dev/null
+++ b/usr.sbin/cached/agents/passwd.c
@@ -0,0 +1,269 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <nsswitch.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "passwd.h"
+
+static int passwd_marshal_func(struct passwd *, char *, size_t *);
+static int passwd_lookup_func(const char *, size_t, char **, size_t *);
+static void *passwd_mp_init_func();
+static int passwd_mp_lookup_func(char **, size_t *, void *);
+static void passwd_mp_destroy_func(void *mdata);
+
+static int
+passwd_marshal_func(struct passwd *pwd, char *buffer, size_t *buffer_size)
+{
+ char *p;
+ struct passwd new_pwd;
+ size_t desired_size, size;
+
+ TRACE_IN(passwd_marshal_func);
+ desired_size = sizeof(struct passwd) + sizeof(char *) +
+ strlen(pwd->pw_name) + 1;
+ if (pwd->pw_passwd != NULL)
+ desired_size += strlen(pwd->pw_passwd) + 1;
+ if (pwd->pw_class != NULL)
+ desired_size += strlen(pwd->pw_class) + 1;
+ if (pwd->pw_gecos != NULL)
+ desired_size += strlen(pwd->pw_gecos) + 1;
+ if (pwd->pw_dir != NULL)
+ desired_size += strlen(pwd->pw_dir) + 1;
+ if (pwd->pw_shell != NULL)
+ desired_size += strlen(pwd->pw_shell) + 1;
+
+ if ((*buffer_size < desired_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(passwd_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_pwd, pwd, sizeof(struct passwd));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct passwd) + sizeof(char *);
+ memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
+
+ if (new_pwd.pw_name != NULL) {
+ size = strlen(new_pwd.pw_name);
+ memcpy(p, new_pwd.pw_name, size);
+ new_pwd.pw_name = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_passwd != NULL) {
+ size = strlen(new_pwd.pw_passwd);
+ memcpy(p, new_pwd.pw_passwd, size);
+ new_pwd.pw_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_class != NULL) {
+ size = strlen(new_pwd.pw_class);
+ memcpy(p, new_pwd.pw_class, size);
+ new_pwd.pw_class = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_gecos != NULL) {
+ size = strlen(new_pwd.pw_gecos);
+ memcpy(p, new_pwd.pw_gecos, size);
+ new_pwd.pw_gecos = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_dir != NULL) {
+ size = strlen(new_pwd.pw_dir);
+ memcpy(p, new_pwd.pw_dir, size);
+ new_pwd.pw_dir = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_shell != NULL) {
+ size = strlen(new_pwd.pw_shell);
+ memcpy(p, new_pwd.pw_shell, size);
+ new_pwd.pw_shell = p;
+ p += size + 1;
+ }
+
+ memcpy(buffer, &new_pwd, sizeof(struct passwd));
+ TRACE_OUT(passwd_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+passwd_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *login;
+ size_t size;
+ uid_t uid;
+
+ struct passwd *result;
+
+ TRACE_IN(passwd_lookup_func);
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ login = (char *)malloc(size);
+ assert(login != NULL);
+ memset(login, 0, size);
+ memcpy(login, key + sizeof(enum nss_lookup_type), size - 1);
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(uid_t)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&uid, key + sizeof(enum nss_lookup_type), sizeof(uid_t));
+ break;
+ default:
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ result = getpwnam(login);
+ free(login);
+ break;
+ case nss_lt_id:
+ result = getpwuid(uid);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ passwd_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ passwd_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(passwd_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+passwd_mp_init_func()
+{
+ TRACE_IN(passwd_mp_init_func);
+ setpwent();
+ TRACE_OUT(passwd_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+passwd_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct passwd *result;
+
+ TRACE_IN(passwd_mp_lookup_func);
+ result = getpwent();
+ if (result != NULL) {
+ passwd_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ passwd_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(passwd_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+passwd_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(passwd_mp_destroy_func);
+ TRACE_OUT(passwd_mp_destroy_func);
+}
+
+struct agent *
+init_passwd_agent()
+{
+ struct common_agent *retval;
+
+ TRACE_IN(init_passwd_agent);
+ retval = (struct common_agent *)malloc(sizeof(struct common_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct common_agent));
+
+ retval->parent.name = strdup("passwd");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = passwd_lookup_func;
+
+ TRACE_OUT(init_passwd_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_passwd_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_passwd_mp_agent);
+ retval = (struct multipart_agent *)malloc(
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct multipart_agent));
+
+ retval->parent.name = strdup("passwd");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = passwd_mp_init_func;
+ retval->mp_lookup_func = passwd_mp_lookup_func;
+ retval->mp_destroy_func = passwd_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_passwd_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/cached/agents/passwd.h b/usr.sbin/cached/agents/passwd.h
new file mode 100644
index 0000000..956a50d
--- /dev/null
+++ b/usr.sbin/cached/agents/passwd.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "../agent.h"
+
+extern struct agent *init_passwd_agent();
+extern struct agent *init_passwd_mp_agent();
diff --git a/usr.sbin/cached/agents/services.c b/usr.sbin/cached/agents/services.c
new file mode 100644
index 0000000..3683396
--- /dev/null
+++ b/usr.sbin/cached/agents/services.c
@@ -0,0 +1,286 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <nsswitch.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "services.h"
+
+static int services_marshal_func(struct servent *, char *, size_t *);
+static int services_lookup_func(const char *, size_t, char **, size_t *);
+static void *services_mp_init_func();
+static int services_mp_lookup_func(char **, size_t *, void *);
+static void services_mp_destroy_func(void *);
+
+static int
+services_marshal_func(struct servent *serv, char *buffer, size_t *buffer_size)
+{
+ struct servent new_serv;
+ size_t desired_size;
+ char **alias;
+ char *p;
+ size_t size;
+ size_t aliases_size;
+
+ TRACE_IN(services_marshal_func);
+ desired_size = ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
+ if (serv->s_name != NULL)
+ desired_size += strlen(serv->s_name) + 1;
+ if (serv->s_proto != NULL)
+ desired_size += strlen(serv->s_proto) + 1;
+
+ aliases_size = 0;
+ if (serv->s_aliases != NULL) {
+ for (alias = serv->s_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += ALIGNBYTES + sizeof(char *) *
+ (aliases_size + 1);
+ }
+
+ if ((*buffer_size < desired_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(services_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_serv, serv, sizeof(struct servent));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct servent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
+ p = (char *)ALIGN(p);
+
+ if (new_serv.s_name != NULL) {
+ size = strlen(new_serv.s_name);
+ memcpy(p, new_serv.s_name, size);
+ new_serv.s_name = p;
+ p += size + 1;
+ }
+
+ if (new_serv.s_proto != NULL) {
+ size = strlen(new_serv.s_proto);
+ memcpy(p, new_serv.s_proto, size);
+ new_serv.s_proto = p;
+ p += size + 1;
+ }
+
+ if (new_serv.s_aliases != NULL) {
+ p = (char *)ALIGN(p);
+ memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
+ new_serv.s_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (alias = new_serv.s_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_serv, sizeof(struct servent));
+ TRACE_OUT(services_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+services_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *name = NULL;
+ char *proto = NULL;
+ size_t size, size2;
+ int port;
+
+ struct servent *result;
+
+ TRACE_IN(services_lookup_func);
+
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ name = (char *)malloc(size);
+ assert(name != NULL);
+ memset(name, 0, size);
+ memcpy(name, key + sizeof(enum nss_lookup_type), size - 1);
+
+ size2 = strlen(name) + 1;
+ if (size2 < size) {
+ proto = strchr(name, '\0');
+ if (strrchr(name, '\0') > proto)
+ ++proto ;
+ else
+ proto = NULL;
+ }
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(int)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&port, key + sizeof(enum nss_lookup_type),
+ sizeof(int));
+
+ size = key_size - sizeof(enum nss_lookup_type) + sizeof(int);
+ if (size > 0) {
+ proto = (char *)malloc(size + 1);
+ assert(proto != NULL);
+ memset(proto, size + 1, 0);
+ memcpy(proto, key + sizeof(enum nss_lookup_type) +
+ sizeof(int), size);
+ }
+ break;
+ default:
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ result = getservbyname(name, proto);
+ free(name);
+ break;
+ case nss_lt_id:
+ result = getservbyport(port, proto);
+ free(proto);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ services_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ services_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(services_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+services_mp_init_func()
+{
+ TRACE_IN(services_mp_init_func);
+ setservent(0);
+ TRACE_OUT(services_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+services_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct servent *result;
+
+ TRACE_IN(services_mp_lookup_func);
+ result = getservent();
+ if (result != NULL) {
+ services_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ services_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(services_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+services_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(services_mp_destroy_func);
+ TRACE_OUT(services_mp_destroy_func);
+}
+
+struct agent *
+init_services_agent()
+{
+ struct common_agent *retval;
+ TRACE_IN(init_services_agent);
+
+ retval = (struct common_agent *)malloc(sizeof(struct common_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct common_agent));
+
+ retval->parent.name = strdup("services");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = services_lookup_func;
+
+ TRACE_OUT(init_services_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_services_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_services_mp_agent);
+ retval = (struct multipart_agent *)malloc(
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct multipart_agent));
+
+ retval->parent.name = strdup("services");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = services_mp_init_func;
+ retval->mp_lookup_func = services_mp_lookup_func;
+ retval->mp_destroy_func = services_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_services_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/cached/agents/services.h b/usr.sbin/cached/agents/services.h
new file mode 100644
index 0000000..0b77c87
--- /dev/null
+++ b/usr.sbin/cached/agents/services.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "../agent.h"
+
+extern struct agent *init_services_agent();
+extern struct agent *init_services_mp_agent();
diff --git a/usr.sbin/cached/cached.8 b/usr.sbin/cached/cached.8
new file mode 100644
index 0000000..5a8693a
--- /dev/null
+++ b/usr.sbin/cached/cached.8
@@ -0,0 +1,148 @@
+.\" Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+
+.Dd October 20, 2005
+.Dt CACHED 8
+.Os
+.Sh NAME
+.Nm cached
+.Nd caching server daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl nst
+.Op Fl i Ar cachename
+.Op Fl I Ar cachename
+.Sh DESCRIPTION
+.Nm
+- is the system caching daemon.
+It can cache almost all types of data and is basically intended to be used
+with
+.Pa nsswitch
+subsystem.
+The cache is actually per-user. It means that each user can work only with the
+cached data, that were cached by him and can't poison the cache of other users.
+.Nm
+supports 2 types of caching:
+.Bl -tag -width Pair
+.It Sy Type
+.Sy Description
+.It Common caching
+Each cached element is the the key+value pair.
+This type of caching supports policies, which are applied, when maximum
+number of cached elements is exceeded.
+3 policies are available:
+.Pa FIFO
+(first in - first out),
+.Pa LRU
+(least recently used) and
+.Pa LFU
+(least frequently used).
+This type of caching is used with
+.Fn getXXXbyname
+- like functions.
+.It Multipart caching
+Each cached element is the part of the elements sequence.
+This type of caching is intended to be used with
+.Fn getXXXent
+- like functions.
+.El
+.Pp
+.Nm
+is able not only to cache elements, but to perform the actual nsswitch
+lookups by itself. To enable this feature use
+.Pa perform-actual-lookups
+parameter in the
+.Xr cached.conf 5
+.Pp
+.Nm
+recognizes the following runtime options:
+.Bl -tag -width indent
+.It Fl n
+Do not daemonize.
+.Nm
+doesn't fork and doesn't disconnect itself from the terminal.
+.It Fl s
+Single-threaded mode.
+Forces using only one thread for all processing purposes (it overrides
+the
+.Pa threads
+parameter in the
+.Xr cached.conf 5
+file).
+.It Fl t
+Trace mode.
+All trace messages would be written to the stdout.
+This mode is usually used with
+.Fl n
+and
+.Fl s
+flags for debugging purposes.
+.It Fl i Ar cachename
+Invalidates personal cache. When specified,
+.Nm
+acts as the administration tool. It asks the already
+running
+.Nm
+to invalidate the specified part of the cache of the
+calling user. For example, you may want sometimes
+to invalidate your
+.Pa hosts
+cache. You can specify
+.Pa all
+as the
+.Pa cachename
+to invalidate your personal cache as a whole. You
+can't use this option for the cachename, for which
+the
+.Pa perform-actual-lookups
+option is enabled.
+.It Fl I Ar cachename
+Invalidates the cache for every user. When specified,
+.Nm
+acts as the administration tool. It asks the already
+running
+.Nm
+to invalidate the specified part of the cache for
+every user. You can specify
+.Pa all
+as the
+.Pa cachename
+to invalidate the whole cache. Only root can use this
+option.
+.El
+.Sh FILES
+.Xr cached.conf 5
+.Sh SEE ALSO
+.Xr cached.conf 5
+.Xr nsswitch.conf 5
+.Xr nsdispatch 3
+.Sh "AUTHORS"
+Michael Bushkov
+.Aq bushman@rsu.ru
+.Sh "BUGS"
+To report bugs or suggestions please mail me
+.Aq bushman@rsu.ru
diff --git a/usr.sbin/cached/cached.c b/usr.sbin/cached/cached.c
new file mode 100644
index 0000000..3219d22
--- /dev/null
+++ b/usr.sbin/cached/cached.c
@@ -0,0 +1,884 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in thereg
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "agents/passwd.h"
+#include "agents/group.h"
+#include "agents/services.h"
+#include "cachedcli.h"
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "parser.h"
+#include "query.h"
+#include "singletons.h"
+
+#ifndef CONFIG_PATH
+#define CONFIG_PATH "/etc/cached.conf"
+#endif
+#define DEFAULT_CONFIG_PATH "cached.conf"
+
+#define MAX_SOCKET_IO_SIZE 4096
+
+struct processing_thread_args {
+ cache the_cache;
+ struct configuration *the_configuration;
+ struct runtime_env *the_runtime_env;
+};
+
+static void accept_connection(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void destroy_cache_(cache);
+static void destroy_runtime_env(struct runtime_env *);
+static cache init_cache_(struct configuration *);
+static struct runtime_env *init_runtime_env(struct configuration *);
+static void print_version_info(void);
+static void processing_loop(cache, struct runtime_env *,
+ struct configuration *);
+static void process_socket_event(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void process_timer_event(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void *processing_thread(void *);
+static void usage(void);
+
+void get_time_func(struct timeval *);
+
+static void
+print_version_info(void)
+{
+ TRACE_IN(print_version_info);
+ printf("cached v0.2 (20 Oct 2005)\nwas developed during SoC 2005\n");
+ TRACE_OUT(print_version_info);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,"usage: cached [-nstiId]\n");
+ exit(1);
+}
+
+static cache
+init_cache_(struct configuration *config)
+{
+ struct cache_params params;
+ cache retval;
+
+ struct configuration_entry *config_entry;
+ size_t size, i;
+ int res;
+
+ TRACE_IN(init_cache_);
+
+ memset(&params, 0, sizeof(struct cache_params));
+ params.get_time_func = get_time_func;
+ retval = init_cache(&params);
+
+ size = configuration_get_entries_size(config);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(config, i);
+ /*
+ * We should register common entries now - multipart entries
+ * would be registered automatically during the queries.
+ */
+ res = register_cache_entry(retval, (struct cache_entry_params *)
+ &config_entry->positive_cache_params);
+ config_entry->positive_cache_entry = find_cache_entry(retval,
+ config_entry->positive_cache_params.entry_name);
+ assert(config_entry->positive_cache_entry !=
+ INVALID_CACHE_ENTRY);
+
+ res = register_cache_entry(retval, (struct cache_entry_params *)
+ &config_entry->negative_cache_params);
+ config_entry->negative_cache_entry = find_cache_entry(retval,
+ config_entry->negative_cache_params.entry_name);
+ assert(config_entry->negative_cache_entry !=
+ INVALID_CACHE_ENTRY);
+ }
+
+ LOG_MSG_2("cache", "cache was successfully initialized");
+ TRACE_OUT(init_cache_);
+ return (retval);
+}
+
+static void
+destroy_cache_(cache the_cache)
+{
+ TRACE_IN(destroy_cache_);
+ destroy_cache(the_cache);
+ TRACE_OUT(destroy_cache_);
+}
+
+/*
+ * Socket and kqueues are prepared here. We have one global queue for both
+ * socket and timers events.
+ */
+static struct runtime_env *
+init_runtime_env(struct configuration *config)
+{
+ int serv_addr_len;
+ struct sockaddr_un serv_addr;
+
+ struct kevent eventlist;
+ struct timespec timeout;
+
+ struct runtime_env *retval;
+
+ TRACE_IN(init_runtime_env);
+ retval = (struct runtime_env *)malloc(sizeof(struct runtime_env));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct runtime_env));
+
+ retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0);
+
+ if (config->force_unlink == 1)
+ unlink(config->socket_path);
+
+ memset(&serv_addr, 0, sizeof(struct sockaddr_un));
+ serv_addr.sun_family = PF_LOCAL;
+ strncpy(serv_addr.sun_path, config->socket_path,
+ sizeof(serv_addr.sun_path));
+ serv_addr_len = sizeof(serv_addr.sun_family) +
+ strlen(serv_addr.sun_path) + 1;
+
+ if (bind(retval->sockfd, (struct sockaddr *)&serv_addr,
+ serv_addr_len) == -1) {
+ close(retval->sockfd);
+ free(retval);
+
+ LOG_ERR_2("runtime environment", "can't bind socket to path: "
+ "%s", config->socket_path);
+ TRACE_OUT(init_runtime_env);
+ return (NULL);
+ }
+ LOG_MSG_2("runtime environment", "using socket %s",
+ config->socket_path);
+
+ /*
+ * Here we're marking socket as non-blocking and setting its backlog
+ * to the maximum value
+ */
+ chmod(config->socket_path, config->socket_mode);
+ listen(retval->sockfd, -1);
+ fcntl(retval->sockfd, F_SETFL, O_NONBLOCK);
+
+ retval->queue = kqueue();
+ assert(retval->queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT,
+ 0, 0, 0);
+ memset(&timeout, 0, sizeof(struct timespec));
+ kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout);
+
+ LOG_MSG_2("runtime environment", "successfully initialized");
+ TRACE_OUT(init_runtime_env);
+ return (retval);
+}
+
+static void
+destroy_runtime_env(struct runtime_env *env)
+{
+ TRACE_IN(destroy_runtime_env);
+ close(env->queue);
+ close(env->sockfd);
+ free(env);
+ TRACE_OUT(destroy_runtime_env);
+}
+
+static void
+accept_connection(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct kevent eventlist[2];
+ struct timespec timeout;
+ struct query_state *qstate;
+
+ int fd;
+ int res;
+
+ uid_t euid;
+ gid_t egid;
+
+ TRACE_IN(accept_connection);
+ fd = accept(event_data->ident, NULL, NULL);
+ if (fd == -1) {
+ LOG_ERR_2("accept_connection", "error %d during accept()",
+ errno);
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ if (getpeereid(fd, &euid, &egid) != 0) {
+ LOG_ERR_2("accept_connection", "error %d during getpeereid()",
+ errno);
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ qstate = init_query_state(fd, sizeof(int), euid, egid);
+ if (qstate == NULL) {
+ LOG_ERR_2("accept_connection", "can't init query_state");
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ memset(&timeout, 0, sizeof(struct timespec));
+ EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ 0, qstate->timeout.tv_sec * 1000, qstate);
+ EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, qstate->kevent_watermark, qstate);
+ res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout);
+ if (res < 0)
+ LOG_ERR_2("accept_connection", "kevent error");
+
+ TRACE_OUT(accept_connection);
+}
+
+static void
+process_socket_event(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct kevent eventlist[2];
+ struct timeval query_timeout;
+ struct timespec kevent_timeout;
+ int nevents;
+ int eof_res, res;
+ ssize_t io_res;
+ struct query_state *qstate;
+
+ TRACE_IN(process_socket_event);
+ eof_res = event_data->flags & EV_EOF ? 1 : 0;
+ res = 0;
+
+ memset(&kevent_timeout, 0, sizeof(struct timespec));
+ EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE,
+ 0, 0, NULL);
+ nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout);
+ if (nevents == -1) {
+ if (errno == ENOENT) {
+ /* the timer is already handling this event */
+ TRACE_OUT(process_socket_event);
+ return;
+ } else {
+ /* some other error happened */
+ LOG_ERR_2("process_socket_event", "kevent error, errno"
+ " is %d", errno);
+ TRACE_OUT(process_socket_event);
+ return;
+ }
+ }
+ qstate = (struct query_state *)event_data->udata;
+
+ /*
+ * If the buffer that is to be send/received is too large,
+ * we send it implicitly, by using query_io_buffer_read and
+ * query_io_buffer_write functions in the query_state. These functions
+ * use the temporary buffer, which is later send/received in parts.
+ * The code below implements buffer splitting/mergind for send/receive
+ * operations. It also does the actual socket IO operations.
+ */
+ if (((qstate->use_alternate_io == 0) &&
+ (qstate->kevent_watermark <= event_data->data)) ||
+ ((qstate->use_alternate_io != 0) &&
+ (qstate->io_buffer_watermark <= event_data->data))) {
+ if (qstate->use_alternate_io != 0) {
+ switch (qstate->io_buffer_filter) {
+ case EVFILT_READ:
+ io_res = query_socket_read(qstate,
+ qstate->io_buffer_p,
+ qstate->io_buffer_watermark);
+ if (io_res < 0) {
+ qstate->use_alternate_io = 0;
+ qstate->process_func = NULL;
+ } else {
+ qstate->io_buffer_p += io_res;
+ if (qstate->io_buffer_p ==
+ qstate->io_buffer +
+ qstate->io_buffer_size) {
+ qstate->io_buffer_p =
+ qstate->io_buffer;
+ qstate->use_alternate_io = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (qstate->use_alternate_io == 0) {
+ do {
+ res = qstate->process_func(qstate);
+ } while ((qstate->kevent_watermark == 0) &&
+ (qstate->process_func != NULL) &&
+ (res == 0));
+
+ if (res != 0)
+ qstate->process_func = NULL;
+ }
+
+ if ((qstate->use_alternate_io != 0) &&
+ (qstate->io_buffer_filter == EVFILT_WRITE)) {
+ io_res = query_socket_write(qstate, qstate->io_buffer_p,
+ qstate->io_buffer_watermark);
+ if (io_res < 0) {
+ qstate->use_alternate_io = 0;
+ qstate->process_func = NULL;
+ } else
+ qstate->io_buffer_p += io_res;
+ }
+ } else {
+ /* assuming that socket was closed */
+ qstate->process_func = NULL;
+ qstate->use_alternate_io = 0;
+ }
+
+ if (((qstate->process_func == NULL) &&
+ (qstate->use_alternate_io == 0)) ||
+ (eof_res != 0) || (res != 0)) {
+ destroy_query_state(qstate);
+ close(event_data->ident);
+ TRACE_OUT(process_socket_event);
+ return;
+ }
+
+ /* updating the query_state lifetime variable */
+ get_time_func(&query_timeout);
+ query_timeout.tv_usec = 0;
+ query_timeout.tv_sec -= qstate->creation_time.tv_sec;
+ if (query_timeout.tv_sec > qstate->timeout.tv_sec)
+ query_timeout.tv_sec = 0;
+ else
+ query_timeout.tv_sec = qstate->timeout.tv_sec -
+ query_timeout.tv_sec;
+
+ if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p ==
+ qstate->io_buffer + qstate->io_buffer_size))
+ qstate->use_alternate_io = 0;
+
+ if (qstate->use_alternate_io == 0) {
+ /*
+ * If we must send/receive the large block of data,
+ * we should prepare the query_state's io_XXX fields.
+ * We should also substitute its write_func and read_func
+ * with the query_io_buffer_write and query_io_buffer_read,
+ * which will allow us to implicitly send/receive this large
+ * buffer later (in the subsequent calls to the
+ * process_socket_event).
+ */
+ if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) {
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->io_buffer = (char *)malloc(
+ qstate->kevent_watermark);
+ assert(qstate->io_buffer != NULL);
+ memset(qstate->io_buffer, 0, qstate->kevent_watermark);
+
+ qstate->io_buffer_p = qstate->io_buffer;
+ qstate->io_buffer_size = qstate->kevent_watermark;
+ qstate->io_buffer_filter = qstate->kevent_filter;
+
+ qstate->write_func = query_io_buffer_write;
+ qstate->read_func = query_io_buffer_read;
+
+ if (qstate->kevent_filter == EVFILT_READ)
+ qstate->use_alternate_io = 1;
+
+ qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->kevent_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate);
+ } else {
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->kevent_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, qstate->kevent_watermark, qstate);
+ }
+ } else {
+ if (qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p <
+ MAX_SOCKET_IO_SIZE) {
+ qstate->io_buffer_watermark = qstate->io_buffer +
+ qstate->io_buffer_size - qstate->io_buffer_p;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->io_buffer_filter,
+ EV_ADD | EV_ONESHOT, NOTE_LOWAT,
+ qstate->io_buffer_watermark,
+ qstate);
+ } else {
+ qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->io_buffer_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate);
+ }
+ }
+ EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER,
+ EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate);
+ kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout);
+
+ TRACE_OUT(process_socket_event);
+}
+
+/*
+ * This routine is called if timer event has been signaled in the kqueue. It
+ * just closes the socket and destroys the query_state.
+ */
+static void
+process_timer_event(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct query_state *qstate;
+
+ TRACE_IN(process_timer_event);
+ qstate = (struct query_state *)event_data->udata;
+ destroy_query_state(qstate);
+ close(event_data->ident);
+ TRACE_OUT(process_timer_event);
+}
+
+/*
+ * Processing loop is the basic processing routine, that forms a body of each
+ * procssing thread
+ */
+static void
+processing_loop(cache the_cache, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct timespec timeout;
+ const int eventlist_size = 1;
+ struct kevent eventlist[eventlist_size];
+ int nevents, i;
+
+ TRACE_MSG("=> processing_loop");
+ memset(&timeout, 0, sizeof(struct timespec));
+ memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size);
+
+ for (;;) {
+ nevents = kevent(env->queue, NULL, 0, eventlist,
+ eventlist_size, NULL);
+ /*
+ * we can only receive 1 event on success
+ */
+ if (nevents == 1) {
+ struct kevent *event_data;
+ event_data = &eventlist[0];
+
+ if (event_data->ident == env->sockfd) {
+ for (i = 0; i < event_data->data; ++i)
+ accept_connection(event_data, env, config);
+
+ EV_SET(eventlist, s_runtime_env->sockfd,
+ EVFILT_READ, EV_ADD | EV_ONESHOT,
+ 0, 0, 0);
+ memset(&timeout, 0,
+ sizeof(struct timespec));
+ kevent(s_runtime_env->queue, eventlist,
+ 1, NULL, 0, &timeout);
+
+ } else {
+ switch (event_data->filter) {
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+ process_socket_event(event_data,
+ env, config);
+ break;
+ case EVFILT_TIMER:
+ process_timer_event(event_data,
+ env, config);
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ /* this branch shouldn't be currently executed */
+ }
+ }
+
+ TRACE_MSG("<= processing_loop");
+}
+
+/*
+ * Wrapper above the processing loop function. It sets the thread signal mask
+ * to avoid SIGPIPE signals (which can happen if the client works incorrectly).
+ */
+static void *
+processing_thread(void *data)
+{
+ struct processing_thread_args *args;
+ sigset_t new;
+
+ TRACE_MSG("=> processing_thread");
+ args = (struct processing_thread_args *)data;
+
+ sigemptyset(&new);
+ sigaddset(&new, SIGPIPE);
+ if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0)
+ LOG_ERR_1("processing thread",
+ "thread can't block the SIGPIPE signal");
+
+ processing_loop(args->the_cache, args->the_runtime_env,
+ args->the_configuration);
+ free(args);
+ TRACE_MSG("<= processing_thread");
+
+ return (NULL);
+}
+
+void
+get_time_func(struct timeval *time)
+{
+ struct timespec res;
+ memset(&res, 0, sizeof(struct timespec));
+ clock_gettime(CLOCK_MONOTONIC, &res);
+
+ time->tv_sec = res.tv_sec;
+ time->tv_usec = 0;
+}
+
+/*
+ * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will
+ * search for this symbol in the executable. This symbol is the attribute of
+ * the caching daemon. So, if it exists, nsdispatch won't try to connect to
+ * the caching daemon and will just ignore the 'cache' source in the
+ * nsswitch.conf. This method helps to avoid cycles and organize
+ * self-performing requests.
+ */
+void
+_nss_cache_cycle_prevention_function(void)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct processing_thread_args *thread_args;
+ pthread_t *threads;
+
+ struct pidfh *pidfile;
+ pid_t pid;
+
+ char const *config_file;
+ char const *error_str;
+ int error_line;
+ int i, res;
+
+ int trace_mode_enabled;
+ int force_single_threaded;
+ int do_not_daemonize;
+ int clear_user_cache_entries, clear_all_cache_entries;
+ char *user_config_entry_name, *global_config_entry_name;
+ int show_statistics;
+ int daemon_mode, interactive_mode;
+
+
+ /* by default all debug messages are omitted */
+ TRACE_OFF();
+
+ /* startup output */
+ print_version_info();
+
+ /* parsing command line arguments */
+ trace_mode_enabled = 0;
+ force_single_threaded = 0;
+ do_not_daemonize = 0;
+ clear_user_cache_entries = 0;
+ clear_all_cache_entries = 0;
+ show_statistics = 0;
+ user_config_entry_name = NULL;
+ global_config_entry_name = NULL;
+ while ((res = getopt(argc, argv, "nstdi:I:")) != -1) {
+ switch (res) {
+ case 'n':
+ do_not_daemonize = 1;
+ break;
+ case 's':
+ force_single_threaded = 1;
+ break;
+ case 't':
+ trace_mode_enabled = 1;
+ break;
+ case 'i':
+ clear_user_cache_entries = 1;
+ if (optarg != NULL)
+ if (strcmp(optarg, "all") != 0)
+ user_config_entry_name = strdup(optarg);
+ break;
+ case 'I':
+ clear_all_cache_entries = 1;
+ if (optarg != NULL)
+ if (strcmp(optarg, "all") != 0)
+ global_config_entry_name =
+ strdup(optarg);
+ break;
+ case 'd':
+ show_statistics = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ daemon_mode = do_not_daemonize | force_single_threaded |
+ trace_mode_enabled;
+ interactive_mode = clear_user_cache_entries | clear_all_cache_entries |
+ show_statistics;
+
+ if ((daemon_mode != 0) && (interactive_mode != 0)) {
+ LOG_ERR_1("main", "daemon mode and interactive_mode arguments "
+ "can't be used together");
+ usage();
+ }
+
+ if (interactive_mode != 0) {
+ FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r");
+ char pidbuf[256];
+
+ struct cached_connection_params connection_params;
+ cached_connection connection;
+
+ int result;
+
+ if (pidfin == NULL)
+ errx(EXIT_FAILURE, "There is no daemon running.");
+
+ memset(pidbuf, 0, sizeof(pidbuf));
+ fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin);
+ fclose(pidfin);
+
+ if (ferror(pidfin) != 0)
+ errx(EXIT_FAILURE, "Can't read from pidfile.");
+
+ if (sscanf(pidbuf, "%d", &pid) != 1)
+ errx(EXIT_FAILURE, "Invalid pidfile.");
+ LOG_MSG_1("main", "daemon PID is %d", pid);
+
+
+ memset(&connection_params, 0,
+ sizeof(struct cached_connection_params));
+ connection_params.socket_path = DEFAULT_SOCKET_PATH;
+ connection = open_cached_connection__(&connection_params);
+ if (connection == INVALID_CACHED_CONNECTION)
+ errx(EXIT_FAILURE, "Can't connect to the daemon.");
+
+ if (clear_user_cache_entries != 0) {
+ result = cached_transform__(connection,
+ user_config_entry_name, TT_USER);
+ if (result != 0)
+ LOG_MSG_1("main",
+ "user cache transformation failed");
+ else
+ LOG_MSG_1("main",
+ "user cache_transformation "
+ "succeeded");
+ }
+
+ if (clear_all_cache_entries != 0) {
+ if (geteuid() != 0)
+ errx(EXIT_FAILURE, "Only root can initiate "
+ "global cache transformation.");
+
+ result = cached_transform__(connection,
+ global_config_entry_name, TT_ALL);
+ if (result != 0)
+ LOG_MSG_1("main",
+ "global cache transformation "
+ "failed");
+ else
+ LOG_MSG_1("main",
+ "global cache transformation "
+ "succeeded");
+ }
+
+ close_cached_connection__(connection);
+
+ free(user_config_entry_name);
+ free(global_config_entry_name);
+ return (EXIT_SUCCESS);
+ }
+
+ pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid);
+ if (pidfile == NULL) {
+ if (errno == EEXIST)
+ errx(EXIT_FAILURE, "Daemon already running, pid: %d.",
+ pid);
+ warn("Cannot open or create pidfile");
+ }
+
+ if (trace_mode_enabled == 1)
+ TRACE_ON();
+
+ /* blocking the main thread from receiving SIGPIPE signal */
+ sigblock(sigmask(SIGPIPE));
+
+ /* daemonization */
+ if (do_not_daemonize == 0) {
+ res = daemon(0, trace_mode_enabled == 0 ? 0 : 1);
+ if (res != 0) {
+ LOG_ERR_1("main", "can't daemonize myself: %s",
+ strerror(errno));
+ pidfile_remove(pidfile);
+ goto fin;
+ } else
+ LOG_MSG_1("main", "successfully daemonized");
+ }
+
+ pidfile_write(pidfile);
+
+ s_agent_table = init_agent_table();
+ register_agent(s_agent_table, init_passwd_agent());
+ register_agent(s_agent_table, init_passwd_mp_agent());
+ register_agent(s_agent_table, init_group_agent());
+ register_agent(s_agent_table, init_group_mp_agent());
+ register_agent(s_agent_table, init_services_agent());
+ register_agent(s_agent_table, init_services_mp_agent());
+ LOG_MSG_1("main", "request agents registered successfully");
+
+ /*
+ * Hosts agent can't work properly until we have access to the
+ * appropriate dtab structures, which are used in nsdispatch
+ * calls
+ *
+ register_agent(s_agent_table, init_hosts_agent());
+ */
+
+ /* configuration initialization */
+ s_configuration = init_configuration();
+ fill_configuration_defaults(s_configuration);
+
+ error_str = NULL;
+ error_line = 0;
+ config_file = CONFIG_PATH;
+
+ res = parse_config_file(s_configuration, config_file, &error_str,
+ &error_line);
+ if ((res != 0) && (error_str == NULL)) {
+ config_file = DEFAULT_CONFIG_PATH;
+ res = parse_config_file(s_configuration, config_file,
+ &error_str, &error_line);
+ }
+
+ if (res != 0) {
+ if (error_str != NULL) {
+ LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n",
+ config_file, error_line, error_str);
+ } else {
+ LOG_ERR_1("main", "no configuration file found "
+ "- was looking for %s and %s",
+ CONFIG_PATH, DEFAULT_CONFIG_PATH);
+ }
+ destroy_configuration(s_configuration);
+ return (-1);
+ }
+
+ if (force_single_threaded == 1)
+ s_configuration->threads_num = 1;
+
+ /* cache initialization */
+ s_cache = init_cache_(s_configuration);
+ if (s_cache == NULL) {
+ LOG_ERR_1("main", "can't initialize the cache");
+ destroy_configuration(s_configuration);
+ return (-1);
+ }
+
+ /* runtime environment initialization */
+ s_runtime_env = init_runtime_env(s_configuration);
+ if (s_runtime_env == NULL) {
+ LOG_ERR_1("main", "can't initialize the runtime environment");
+ destroy_configuration(s_configuration);
+ destroy_cache_(s_cache);
+ return (-1);
+ }
+
+ if (s_configuration->threads_num > 1) {
+ threads = (pthread_t *)malloc(sizeof(pthread_t) *
+ s_configuration->threads_num);
+ memset(threads, 0, sizeof(pthread_t) *
+ s_configuration->threads_num);
+ for (i = 0; i < s_configuration->threads_num; ++i) {
+ thread_args = (struct processing_thread_args *)malloc(
+ sizeof(struct processing_thread_args));
+ thread_args->the_cache = s_cache;
+ thread_args->the_runtime_env = s_runtime_env;
+ thread_args->the_configuration = s_configuration;
+
+ LOG_MSG_1("main", "thread #%d was successfully created",
+ i);
+ pthread_create(&threads[i], NULL, processing_thread,
+ thread_args);
+
+ thread_args = NULL;
+ }
+
+ for (i = 0; i < s_configuration->threads_num; ++i)
+ pthread_join(threads[i], NULL);
+ } else {
+ LOG_MSG_1("main", "working in single-threaded mode");
+ processing_loop(s_cache, s_runtime_env, s_configuration);
+ }
+
+fin:
+ /* runtime environment destruction */
+ destroy_runtime_env(s_runtime_env);
+
+ /* cache destruction */
+ destroy_cache_(s_cache);
+
+ /* configuration destruction */
+ destroy_configuration(s_configuration);
+
+ /* agents table destruction */
+ destroy_agent_table(s_agent_table);
+
+ pidfile_remove(pidfile);
+ return (EXIT_SUCCESS);
+}
diff --git a/usr.sbin/cached/cached.conf.5 b/usr.sbin/cached/cached.conf.5
new file mode 100644
index 0000000..de6d43a
--- /dev/null
+++ b/usr.sbin/cached/cached.conf.5
@@ -0,0 +1,102 @@
+.\" Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+
+.Dd August 29, 2005
+.Dt CACHED.CONF 5
+.Os
+.Sh NAME
+.Nm cached.conf
+.Nd "caching daemon configuration file manual page"
+.Sh "DESCRIPTION"
+.Nm
+is used by the
+.Xr cached 8
+daemon and is read on its startup. Its syntax is mostly similar to the
+nscd.conf syntax in Linux and Solaris. It has some differences, though -
+see them below.
+.Pp
+Each line specifies either an attribute and a value, or an attribute,
+a cachename and a value. Usual cachenames are passwd, groups, hosts,
+services, protocols and rpc. You can also use any other cachename
+(for example, if some third-party application uses nsswitch).
+.Pp
+.Bl -tag -width Pair
+.It Sy threads [value]
+Number of threads, which would listen for connections and process requests. The
+minimum is 1. The default value is 8.
+.It Sy enable-cache [cachename] [yes|no]
+Enables or disables the cache for specified cachename.
+.It Sy positive-time-to-live [cachename] [value]
+Sets the TTL (time-to-live) for the specified cache in seconds. Larger values
+can increase system's performance, but they also can affect the cache
+coherence. The default value is 3600.
+.It Sy positive-policy [cachename] [fifo|lru|lfu]
+The policy that is applied to erase some of the cache elements, when the
+size limit of the given cachename is exceeded. Possible policies are:
+fifo (first-in-first-out), lru (least-recently-used),
+lfu (least-frequently-used). The default policy is
+.It Sy negative-time-to-live [cachename] [value]
+The TTL of the negative cached elements in seconds. The larger values can
+significantly increase system performance in some environments
+(when dealing with files with UIDs, which are not in system databases,
+for example). This number should be kept low to avoid the cache
+coherence problems. The default value is 60.
+.It Sy negative-policy [cachename] [fifo|lru|lfu]
+The same as the positive-policy, but this one is applied to the negative
+elements of the given cachename. The default policy is
+.It Sy suggested-size [cachename] [value]
+This is the internal hash table size. The value should be a prime number
+for optimum performance. You should only change this value when the number
+of cached elements is significantly (in 5-10 times) greater then the default
+hash table size (255).
+.It Sy keep-hot-count [cachename] [value]
+The size limit of the cache with the given cachename. When it is exceeded,
+the policy will be applied. The default value is 2048.
+.It Sy perform-actual-lookups [cachename] [yes|no]
+If enabled, the
+.Xr cached 8
+doesn't simply receive and cache the NSS-requests results, but performs
+all the lookups by itself and only returns the responses. If this feature is
+enabled, then for the given cachename
+.Xr cached 8
+will act similarly to the NSCD.
+.Pp
+.Pa NOTE:
+this feature is currently experimental - it supports only passwd, groups and
+services cachenames.
+.Sh "NOTES"
+You can use
+.Sq #
+symbol at the beginning of the line for comments.
+.Sh "SEE ALSO"
+.Xr cached 8
+.Sh "AUTHORS"
+Michael Bushkov
+.Aq bushman@rsu.ru
+.Sh "BUGS"
+To report bugs or suggestions please mail me
+.Aq bushman@rsu.ru
diff --git a/usr.sbin/cached/cachedcli.c b/usr.sbin/cached/cachedcli.c
new file mode 100644
index 0000000..c78c875
--- /dev/null
+++ b/usr.sbin/cached/cachedcli.c
@@ -0,0 +1,284 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/event.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "cachedcli.h"
+#include "protocol.h"
+
+#define DEFAULT_CACHED_IO_TIMEOUT 4
+
+static int safe_write(struct cached_connection_ *, const void *, size_t);
+static int safe_read(struct cached_connection_ *, void *, size_t);
+static int send_credentials(struct cached_connection_ *, int);
+
+static int
+safe_write(struct cached_connection_ *connection, const void *data,
+ size_t data_size)
+{
+ struct kevent eventlist;
+ int nevents;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist,
+ 1, &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ s_result = write(connection->sockfd, data + result,
+ eventlist.data < data_size - result ?
+ eventlist.data : data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+static int
+safe_read(struct cached_connection_ *connection, void *data, size_t data_size)
+{
+ struct kevent eventlist;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+ int nevents;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1,
+ &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) {
+ s_result = read(connection->sockfd, data + result,
+ eventlist.data <= data_size - result ? eventlist.data :
+ data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+static int
+send_credentials(struct cached_connection_ *connection, int type)
+{
+ struct kevent eventlist;
+ int nevents;
+ ssize_t result;
+ int res;
+
+ struct msghdr cred_hdr;
+ struct iovec iov;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ TRACE_IN(send_credentials);
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = sizeof(cmsg);
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ iov.iov_base = &type;
+ iov.iov_len = sizeof(int);
+
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ NOTE_LOWAT, sizeof(int), NULL);
+ res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1
+ : 0;
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+ TRACE_OUT(send_credentials);
+ return (result);
+ } else {
+ TRACE_OUT(send_credentials);
+ return (-1);
+ }
+}
+
+struct cached_connection_ *
+open_cached_connection__(struct cached_connection_params const *params)
+{
+ struct cached_connection_ *retval;
+ struct kevent eventlist;
+ struct sockaddr_un client_address;
+ int client_address_len, client_socket;
+ int res;
+
+ TRACE_IN(open_cached_connection);
+ assert(params != NULL);
+
+ client_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+ client_address.sun_family = PF_LOCAL;
+ strncpy(client_address.sun_path, params->socket_path,
+ sizeof(client_address.sun_path));
+ client_address_len = sizeof(client_address.sun_family) +
+ strlen(client_address.sun_path) + 1;
+
+ res = connect(client_socket, (struct sockaddr *)&client_address,
+ client_address_len);
+ if (res == -1) {
+ close(client_socket);
+ TRACE_OUT(open_cached_connection);
+ return (NULL);
+ }
+ fcntl(client_socket, F_SETFL, O_NONBLOCK);
+
+ retval = malloc(sizeof(struct cached_connection_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cached_connection_));
+
+ retval->sockfd = client_socket;
+
+ retval->write_queue = kqueue();
+ assert(retval->write_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ retval->read_queue = kqueue();
+ assert(retval->read_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD,
+ 0, 0, NULL);
+ res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL);
+
+ TRACE_OUT(open_cached_connection);
+ return (retval);
+}
+
+void
+close_cached_connection__(struct cached_connection_ *connection)
+{
+
+ TRACE_IN(close_cached_connection);
+ assert(connection != NULL);
+
+ close(connection->sockfd);
+ close(connection->read_queue);
+ close(connection->write_queue);
+ free(connection);
+ TRACE_OUT(close_cached_connection);
+}
+
+int
+cached_transform__(struct cached_connection_ *connection,
+ const char *entry_name, int transformation_type)
+{
+ size_t name_size;
+ int error_code;
+ int result;
+
+ TRACE_IN(cached_transform);
+
+ error_code = -1;
+ result = 0;
+ result = send_credentials(connection, CET_TRANSFORM_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ if (entry_name != NULL)
+ name_size = strlen(entry_name);
+ else
+ name_size = 0;
+
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, &transformation_type, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (entry_name != NULL) {
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+ }
+
+ result = safe_read(connection, &error_code, sizeof(int));
+ if (result != 0)
+ error_code = -1;
+
+fin:
+ TRACE_OUT(cached_transform);
+ return (error_code);
+}
diff --git a/usr.sbin/cached/cachedcli.h b/usr.sbin/cached/cachedcli.h
new file mode 100644
index 0000000..58d9ccc
--- /dev/null
+++ b/usr.sbin/cached/cachedcli.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CACHEDCLI_H__
+#define __CACHED_CACHEDCLI_H__
+
+struct cached_connection_params {
+ char *socket_path;
+ struct timeval timeout;
+};
+
+struct cached_connection_ {
+ int sockfd;
+ int read_queue;
+ int write_queue;
+};
+
+/* simple abstractions for not to write "struct" every time */
+typedef struct cached_connection_ *cached_connection;
+typedef struct cached_connection_ *cached_mp_write_session;
+typedef struct cached_connection_ *cached_mp_read_session;
+
+#define INVALID_CACHED_CONNECTION (NULL)
+
+/* initialization/destruction routines */
+extern cached_connection open_cached_connection__(
+ struct cached_connection_params const *);
+extern void close_cached_connection__(cached_connection);
+
+extern int cached_transform__(cached_connection, const char *, int);
+
+#endif
diff --git a/usr.sbin/cached/cachelib.c b/usr.sbin/cached/cachelib.c
new file mode 100644
index 0000000..4f771cc
--- /dev/null
+++ b/usr.sbin/cached/cachelib.c
@@ -0,0 +1,1234 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/time.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cachelib.h"
+#include "debug.h"
+
+#define INITIAL_ENTRIES_CAPACITY 32
+#define ENTRIES_CAPACITY_STEP 32
+
+#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
+ for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
+ (var) = ((a)*(var) + *(in_var)) % (M)
+
+#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
+ for ((var) = 0; *(in_var) != 0; ++(in_var)) \
+ (var) = ((a)*(var) + *(in_var)) & (M - 1)
+
+static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
+ struct cache_policy_item_ *);
+static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
+ struct cache_policy_item_ *);
+static void clear_cache_entry(struct cache_entry_ *);
+static void destroy_cache_entry(struct cache_entry_ *);
+static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
+static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
+static int entries_bsearch_cmp_func(const void *, const void *);
+static int entries_qsort_cmp_func(const void *, const void *);
+static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
+ const char *);
+static void flush_cache_entry(struct cache_entry_ *);
+static void flush_cache_policy(struct cache_common_entry_ *,
+ struct cache_policy_ *, struct cache_policy_ *,
+ int (*)(struct cache_common_entry_ *,
+ struct cache_policy_item_ *));
+static int ht_items_cmp_func(const void *, const void *);
+static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
+static hashtable_index_t ht_item_hash_func(const void *, size_t);
+
+/*
+ * Hashing and comparing routines, that are used with the hash tables
+ */
+static int
+ht_items_cmp_func(const void *p1, const void *p2)
+{
+ struct cache_ht_item_data_ *hp1, *hp2;
+ size_t min_size;
+ int result;
+
+ hp1 = (struct cache_ht_item_data_ *)p1;
+ hp2 = (struct cache_ht_item_data_ *)p2;
+
+ assert(hp1->key != NULL);
+ assert(hp2->key != NULL);
+
+ if (hp1->key_size != hp2->key_size) {
+ min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
+ hp2->key_size;
+ result = memcmp(hp1->key, hp2->key, min_size);
+
+ if (result == 0)
+ return ((hp1->key_size < hp2->key_size) ? -1 : 1);
+ else
+ return (result);
+ } else
+ return (memcmp(hp1->key, hp2->key, hp1->key_size));
+}
+
+static int
+ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
+{
+ struct cache_ht_item_data_ *hp1, *hp2;
+ size_t min_size;
+ int result;
+
+ hp1 = (struct cache_ht_item_data_ *)p1;
+ hp2 = (struct cache_ht_item_data_ *)p2;
+
+ assert(hp1->key != NULL);
+ assert(hp2->key != NULL);
+
+ if (hp1->key_size != hp2->key_size) {
+ min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
+ hp2->key_size;
+ result = memcmp(hp1->key, hp2->key, min_size);
+
+ if (result == 0)
+ if (min_size == hp1->key_size)
+ return (0);
+ else
+ return ((hp1->key_size < hp2->key_size) ? -1 : 1);
+ else
+ return (result);
+ } else
+ return (memcmp(hp1->key, hp2->key, hp1->key_size));
+}
+
+static hashtable_index_t
+ht_item_hash_func(const void *p, size_t cache_entries_size)
+{
+ struct cache_ht_item_data_ *hp;
+ size_t i;
+
+ hashtable_index_t retval;
+
+ hp = (struct cache_ht_item_data_ *)p;
+ assert(hp->key != NULL);
+
+ retval = 0;
+ for (i = 0; i < hp->key_size; ++i)
+ retval = (127 * retval + (unsigned char)hp->key[i]) %
+ cache_entries_size;
+
+ return retval;
+}
+
+HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
+ ht_item_hash_func, ht_items_cmp_func);
+
+/*
+ * Routines to sort and search the entries by name
+ */
+static int
+entries_bsearch_cmp_func(const void *key, const void *ent)
+{
+
+ assert(key != NULL);
+ assert(ent != NULL);
+
+ return (strcmp((char const *)key,
+ (*(struct cache_entry_ const **)ent)->name));
+}
+
+static int
+entries_qsort_cmp_func(const void *e1, const void *e2)
+{
+
+ assert(e1 != NULL);
+ assert(e2 != NULL);
+
+ return (strcmp((*(struct cache_entry_ const **)e1)->name,
+ (*(struct cache_entry_ const **)e2)->name));
+}
+
+static struct cache_entry_ **
+find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
+{
+
+ return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
+ the_cache->entries_size, sizeof(struct cache_entry_ *),
+ entries_bsearch_cmp_func)));
+}
+
+static void
+destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ struct cache_mp_data_item_ *data_item;
+
+ TRACE_IN(destroy_cache_mp_write_session);
+ assert(ws != NULL);
+ while (!TAILQ_EMPTY(&ws->items)) {
+ data_item = TAILQ_FIRST(&ws->items);
+ TAILQ_REMOVE(&ws->items, data_item, entries);
+ free(data_item->value);
+ free(data_item);
+ }
+
+ free(ws);
+ TRACE_OUT(destroy_cache_mp_write_session);
+}
+
+static void
+destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
+{
+
+ TRACE_IN(destroy_cache_mp_read_session);
+ assert(rs != NULL);
+ free(rs);
+ TRACE_OUT(destroy_cache_mp_read_session);
+}
+
+static void
+destroy_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_read_session_ *rs;
+ struct cache_mp_write_session_ *ws;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data;
+
+ TRACE_IN(destroy_cache_entry);
+ assert(entry != NULL);
+
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
+ {
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ }
+ HASHTABLE_ENTRY_CLEAR(ht_item, data);
+ }
+
+ HASHTABLE_DESTROY(&(common_entry->items), data);
+
+ /* FIFO policy is always first */
+ destroy_cache_fifo_policy(common_entry->policies[0]);
+ switch (common_entry->common_params.policy) {
+ case CPT_LRU:
+ destroy_cache_lru_policy(common_entry->policies[1]);
+ break;
+ case CPT_LFU:
+ destroy_cache_lfu_policy(common_entry->policies[1]);
+ break;
+ default:
+ break;
+ }
+ free(common_entry->policies);
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
+ ws = TAILQ_FIRST(&mp_entry->ws_head);
+ TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
+ destroy_cache_mp_write_session(ws);
+ }
+
+ while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
+ rs = TAILQ_FIRST(&mp_entry->rs_head);
+ TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
+ destroy_cache_mp_read_session(rs);
+ }
+
+ if (mp_entry->completed_write_session != NULL)
+ destroy_cache_mp_write_session(
+ mp_entry->completed_write_session);
+
+ if (mp_entry->pending_write_session != NULL)
+ destroy_cache_mp_write_session(
+ mp_entry->pending_write_session);
+ }
+
+ free(entry->name);
+ free(entry);
+ TRACE_OUT(destroy_cache_entry);
+}
+
+static void
+clear_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data;
+ struct cache_policy_ *policy;
+ struct cache_policy_item_ *item, *next_item;
+ size_t entry_size;
+ int i;
+
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ entry_size = 0;
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
+ {
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ }
+ entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
+ HASHTABLE_ENTRY_CLEAR(ht_item, data);
+ }
+
+ common_entry->items_size -= entry_size;
+ for (i = 0; i < common_entry->policies_size; ++i) {
+ policy = common_entry->policies[i];
+
+ next_item = NULL;
+ item = policy->get_first_item_func(policy);
+ while (item != NULL) {
+ next_item = policy->get_next_item_func(policy,
+ item);
+ policy->remove_item_func(policy, item);
+ policy->destroy_item_func(item);
+ item = next_item;
+ }
+ }
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if (mp_entry->rs_size == 0) {
+ if (mp_entry->completed_write_session != NULL) {
+ destroy_cache_mp_write_session(
+ mp_entry->completed_write_session);
+ mp_entry->completed_write_session = NULL;
+ }
+
+ memset(&mp_entry->creation_time, 0,
+ sizeof(struct timeval));
+ memset(&mp_entry->last_request_time, 0,
+ sizeof(struct timeval));
+ }
+ }
+}
+
+/*
+ * When passed to the flush_cache_policy, ensures that all old elements are
+ * deleted.
+ */
+static int
+cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
+ struct cache_policy_item_ *item)
+{
+
+ return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
+ entry->common_params.max_lifetime.tv_sec) ? 1: 0);
+}
+
+/*
+ * When passed to the flush_cache_policy, ensures that all elements, that
+ * exceed the size limit, are deleted.
+ */
+static int
+cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
+ struct cache_policy_item_ *item)
+{
+
+ return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
+ : 0);
+}
+
+/*
+ * Removes the elements from the cache entry, while the continue_func returns 1.
+ */
+static void
+flush_cache_policy(struct cache_common_entry_ *entry,
+ struct cache_policy_ *policy,
+ struct cache_policy_ *connected_policy,
+ int (*continue_func)(struct cache_common_entry_ *,
+ struct cache_policy_item_ *))
+{
+ struct cache_policy_item_ *item, *next_item, *connected_item;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data, ht_key;
+ hashtable_index_t hash;
+
+ assert(policy != NULL);
+
+ next_item = NULL;
+ item = policy->get_first_item_func(policy);
+ while ((item != NULL) && (continue_func(entry, item) == 1)) {
+ next_item = policy->get_next_item_func(policy, item);
+
+ connected_item = item->connected_item;
+ policy->remove_item_func(policy, item);
+
+ memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
+ ht_key.key = item->key;
+ ht_key.key_size = item->key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
+ &ht_key);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
+
+ ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
+ ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
+ &ht_key);
+ assert(ht_item_data != NULL);
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
+ --entry->items_size;
+
+ policy->destroy_item_func(item);
+
+ if (connected_item != NULL) {
+ connected_policy->remove_item_func(connected_policy,
+ connected_item);
+ connected_policy->destroy_item_func(connected_item);
+ }
+
+ item = next_item;
+ }
+}
+
+static void
+flush_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_common_entry_ *common_entry;
+ struct cache_policy_ *policy, *connected_policy;
+
+ connected_policy = NULL;
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+ if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
+ (common_entry->common_params.max_lifetime.tv_usec != 0)) {
+
+ policy = common_entry->policies[0];
+ if (common_entry->policies_size > 1)
+ connected_policy = common_entry->policies[1];
+
+ flush_cache_policy(common_entry, policy,
+ connected_policy,
+ cache_lifetime_common_continue_func);
+ }
+
+
+ if ((common_entry->common_params.max_elemsize != 0) &&
+ common_entry->items_size >
+ common_entry->common_params.max_elemsize) {
+
+ if (common_entry->policies_size > 1) {
+ policy = common_entry->policies[1];
+ connected_policy = common_entry->policies[0];
+ } else {
+ policy = common_entry->policies[0];
+ connected_policy = NULL;
+ }
+
+ flush_cache_policy(common_entry, policy,
+ connected_policy,
+ cache_elemsize_common_continue_func);
+ }
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
+ || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
+
+ if (mp_entry->last_request_time.tv_sec -
+ mp_entry->last_request_time.tv_sec >
+ mp_entry->mp_params.max_lifetime.tv_sec)
+ clear_cache_entry(entry);
+ }
+ }
+}
+
+struct cache_ *
+init_cache(struct cache_params const *params)
+{
+ struct cache_ *retval;
+
+ TRACE_IN(init_cache);
+ assert(params != NULL);
+
+ retval = (struct cache_ *)malloc(sizeof(struct cache_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_));
+
+ assert(params != NULL);
+ memcpy(&retval->params, params, sizeof(struct cache_params));
+
+ retval->entries = (struct cache_entry_ **)malloc(
+ sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
+ assert(retval->entries != NULL);
+ memset(retval->entries, 0, sizeof(sizeof(struct cache_entry_ *)
+ * INITIAL_ENTRIES_CAPACITY));
+
+ retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
+ retval->entries_size = 0;
+
+ TRACE_OUT(init_cache);
+ return (retval);
+}
+
+void
+destroy_cache(struct cache_ *the_cache)
+{
+
+ TRACE_IN(destroy_cache);
+ assert(the_cache != NULL);
+
+ if (the_cache->entries != NULL) {
+ size_t i;
+ for (i = 0; i < the_cache->entries_size; ++i)
+ destroy_cache_entry(the_cache->entries[i]);
+
+ free(the_cache->entries);
+ }
+
+ free(the_cache);
+ TRACE_OUT(destroy_cache);
+}
+
+int
+register_cache_entry(struct cache_ *the_cache,
+ struct cache_entry_params const *params)
+{
+ int policies_size;
+ size_t entry_name_size;
+ struct cache_common_entry_ *new_common_entry;
+ struct cache_mp_entry_ *new_mp_entry;
+
+ TRACE_IN(register_cache_entry);
+ assert(the_cache != NULL);
+
+ if (find_cache_entry(the_cache, params->entry_name) != NULL) {
+ TRACE_OUT(register_cache_entry);
+ return (-1);
+ }
+
+ if (the_cache->entries_size == the_cache->entries_capacity) {
+ struct cache_entry_ **new_entries;
+ size_t new_capacity;
+
+ new_capacity = the_cache->entries_capacity +
+ ENTRIES_CAPACITY_STEP;
+ new_entries = (struct cache_entry_ **)malloc(
+ sizeof(struct cache_entry_ *) * new_capacity);
+ assert(new_entries != NULL);
+
+ memset(new_entries, 0, sizeof(struct cache_entry_ *) *
+ new_capacity);
+ memcpy(new_entries, the_cache->entries,
+ sizeof(struct cache_entry_ *)
+ * the_cache->entries_size);
+
+ free(the_cache->entries);
+ the_cache->entries = new_entries;
+ }
+
+ entry_name_size = strlen(params->entry_name);
+ switch (params->entry_type)
+ {
+ case CET_COMMON:
+ new_common_entry = (struct cache_common_entry_ *)malloc(
+ sizeof(struct cache_common_entry_));
+ assert(new_common_entry != NULL);
+ memset(new_common_entry, 0, sizeof(struct cache_common_entry_));
+
+ memcpy(&new_common_entry->common_params, params,
+ sizeof(struct common_cache_entry_params));
+ new_common_entry->params =
+ (struct cache_entry_params *)&new_common_entry->common_params;
+
+ new_common_entry->common_params.entry_name = (char *)malloc(
+ entry_name_size+1);
+ assert(new_common_entry->common_params.entry_name != NULL);
+ memset(new_common_entry->common_params.entry_name, 0,
+ entry_name_size + 1);
+ strncpy(new_common_entry->common_params.entry_name,
+ params->entry_name, entry_name_size);
+ new_common_entry->name =
+ new_common_entry->common_params.entry_name;
+
+ HASHTABLE_INIT(&(new_common_entry->items),
+ struct cache_ht_item_data_, data,
+ new_common_entry->common_params.cache_entries_size);
+
+ if (new_common_entry->common_params.policy == CPT_FIFO)
+ policies_size = 1;
+ else
+ policies_size = 2;
+
+ new_common_entry->policies = (struct cache_policy_ **)malloc(
+ sizeof(struct cache_policy_ *) * policies_size);
+ assert(new_common_entry->policies != NULL);
+ memset(new_common_entry->policies, 0,
+ sizeof(struct cache_policy_ *) * policies_size);
+
+ new_common_entry->policies_size = policies_size;
+ new_common_entry->policies[0] = init_cache_fifo_policy();
+
+ if (policies_size > 1) {
+ switch (new_common_entry->common_params.policy) {
+ case CPT_LRU:
+ new_common_entry->policies[1] =
+ init_cache_lru_policy();
+ break;
+ case CPT_LFU:
+ new_common_entry->policies[1] =
+ init_cache_lfu_policy();
+ break;
+ default:
+ break;
+ }
+ }
+
+ new_common_entry->get_time_func =
+ the_cache->params.get_time_func;
+ the_cache->entries[the_cache->entries_size++] =
+ (struct cache_entry_ *)new_common_entry;
+ break;
+ case CET_MULTIPART:
+ new_mp_entry = (struct cache_mp_entry_ *)malloc(
+ sizeof(struct cache_mp_entry_));
+ assert(new_mp_entry != NULL);
+ memset(new_mp_entry, 0, sizeof(struct cache_mp_entry_));
+
+ memcpy(&new_mp_entry->mp_params, params,
+ sizeof(struct mp_cache_entry_params));
+ new_mp_entry->params =
+ (struct cache_entry_params *)&new_mp_entry->mp_params;
+
+ new_mp_entry->mp_params.entry_name = (char *)malloc(
+ entry_name_size+1);
+ assert(new_mp_entry->mp_params.entry_name != NULL);
+ memset(new_mp_entry->mp_params.entry_name, 0,
+ entry_name_size + 1);
+ strncpy(new_mp_entry->mp_params.entry_name, params->entry_name,
+ entry_name_size);
+ new_mp_entry->name = new_mp_entry->mp_params.entry_name;
+
+ TAILQ_INIT(&new_mp_entry->ws_head);
+ TAILQ_INIT(&new_mp_entry->rs_head);
+
+ new_mp_entry->get_time_func = the_cache->params.get_time_func;
+ the_cache->entries[the_cache->entries_size++] =
+ (struct cache_entry_ *)new_mp_entry;
+ break;
+ }
+
+
+ qsort(the_cache->entries, the_cache->entries_size,
+ sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
+
+ TRACE_OUT(register_cache_entry);
+ return (0);
+}
+
+int
+unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
+{
+ struct cache_entry_ **del_ent;
+
+ TRACE_IN(unregister_cache_entry);
+ assert(the_cache != NULL);
+
+ del_ent = find_cache_entry_p(the_cache, entry_name);
+ if (del_ent != NULL) {
+ destroy_cache_entry(*del_ent);
+ --the_cache->entries_size;
+
+ memmove(del_ent, del_ent + 1,
+ (&(the_cache->entries[--the_cache->entries_size]) -
+ del_ent) * sizeof(struct cache_entry_ *));
+
+ TRACE_OUT(unregister_cache_entry);
+ return (0);
+ } else {
+ TRACE_OUT(unregister_cache_entry);
+ return (-1);
+ }
+}
+
+struct cache_entry_ *
+find_cache_entry(struct cache_ *the_cache, const char *entry_name)
+{
+ struct cache_entry_ **result;
+
+ TRACE_IN(find_cache_entry);
+ result = find_cache_entry_p(the_cache, entry_name);
+
+ if (result == NULL) {
+ TRACE_OUT(find_cache_entry);
+ return (NULL);
+ } else {
+ TRACE_OUT(find_cache_entry);
+ return (*result);
+ }
+}
+
+/*
+ * Tries to read the element with the specified key from the cache. If the
+ * value_size is too small, it will be filled with the proper number, and
+ * the user will need to call cache_read again with the value buffer, that
+ * is large enough.
+ * Function returns 0 on success, -1 on error, and -2 if the value_size is too
+ * small.
+ */
+int
+cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
+ char *value, size_t *value_size)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_data_ item_data, *find_res;
+ struct cache_ht_item_ *item;
+ hashtable_index_t hash;
+ struct cache_policy_item_ *connected_item;
+
+ TRACE_IN(cache_read);
+ assert(entry != NULL);
+ assert(key != NULL);
+ assert(value_size != NULL);
+ assert(entry->params->entry_type == CET_COMMON);
+
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
+ /* can't avoid the cast here */
+ item_data.key = (char *)key;
+ item_data.key_size = key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
+ &item_data);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
+
+ item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
+ find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
+ if (find_res == NULL) {
+ TRACE_OUT(cache_read);
+ return (-1);
+ }
+
+ if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
+ (common_entry->common_params.max_lifetime.tv_usec != 0)) {
+
+ if (find_res->fifo_policy_item->last_request_time.tv_sec -
+ find_res->fifo_policy_item->creation_time.tv_sec >
+ common_entry->common_params.max_lifetime.tv_sec) {
+
+ free(find_res->key);
+ free(find_res->value);
+
+ connected_item =
+ find_res->fifo_policy_item->connected_item;
+ if (connected_item != NULL) {
+ common_entry->policies[1]->remove_item_func(
+ common_entry->policies[1],
+ connected_item);
+ common_entry->policies[1]->destroy_item_func(
+ connected_item);
+ }
+
+ common_entry->policies[0]->remove_item_func(
+ common_entry->policies[0],
+ find_res->fifo_policy_item);
+ common_entry->policies[0]->destroy_item_func(
+ find_res->fifo_policy_item);
+
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
+ --common_entry->items_size;
+ }
+ }
+
+ if ((*value_size < find_res->value_size) || (value == NULL)) {
+ *value_size = find_res->value_size;
+ TRACE_OUT(cache_read);
+ return (-2);
+ }
+
+ *value_size = find_res->value_size;
+ memcpy(value, find_res->value, find_res->value_size);
+
+ ++find_res->fifo_policy_item->request_count;
+ common_entry->get_time_func(
+ &find_res->fifo_policy_item->last_request_time);
+ common_entry->policies[0]->update_item_func(common_entry->policies[0],
+ find_res->fifo_policy_item);
+
+ if (find_res->fifo_policy_item->connected_item != NULL) {
+ connected_item = find_res->fifo_policy_item->connected_item;
+ memcpy(&connected_item->last_request_time,
+ &find_res->fifo_policy_item->last_request_time,
+ sizeof(struct timeval));
+ connected_item->request_count =
+ find_res->fifo_policy_item->request_count;
+
+ common_entry->policies[1]->update_item_func(
+ common_entry->policies[1], connected_item);
+ }
+
+ TRACE_OUT(cache_read);
+ return (0);
+}
+
+/*
+ * Writes the value with the specified key into the cache entry.
+ * Functions returns 0 on success, and -1 on error.
+ */
+int
+cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
+ char const *value, size_t value_size)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_data_ item_data, *find_res;
+ struct cache_ht_item_ *item;
+ hashtable_index_t hash;
+
+ struct cache_policy_ *policy, *connected_policy;
+ struct cache_policy_item_ *policy_item;
+ struct cache_policy_item_ *connected_policy_item;
+
+ TRACE_IN(cache_write);
+ assert(entry != NULL);
+ assert(key != NULL);
+ assert(value != NULL);
+ assert(entry->params->entry_type == CET_COMMON);
+
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
+ /* can't avoid the cast here */
+ item_data.key = (char *)key;
+ item_data.key_size = key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
+ &item_data);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
+
+ item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
+ find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
+ if (find_res != NULL) {
+ TRACE_OUT(cache_write);
+ return (-1);
+ }
+
+ item_data.key = (char *)malloc(key_size);
+ memcpy(item_data.key, key, key_size);
+
+ item_data.value = (char *)malloc(value_size);
+ assert(item_data.value != NULL);
+
+ memcpy(item_data.value, value, value_size);
+ item_data.value_size = value_size;
+
+ policy_item = common_entry->policies[0]->create_item_func();
+ policy_item->key = item_data.key;
+ policy_item->key_size = item_data.key_size;
+ common_entry->get_time_func(&policy_item->creation_time);
+
+ if (common_entry->policies_size > 1) {
+ connected_policy_item =
+ common_entry->policies[1]->create_item_func();
+ memcpy(&connected_policy_item->creation_time,
+ &policy_item->creation_time,
+ sizeof(struct timeval));
+ connected_policy_item->key = policy_item->key;
+ connected_policy_item->key_size = policy_item->key_size;
+
+ connected_policy_item->connected_item = policy_item;
+ policy_item->connected_item = connected_policy_item;
+ }
+
+ item_data.fifo_policy_item = policy_item;
+
+ common_entry->policies[0]->add_item_func(common_entry->policies[0],
+ policy_item);
+ if (common_entry->policies_size > 1)
+ common_entry->policies[1]->add_item_func(
+ common_entry->policies[1], connected_policy_item);
+
+ HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
+ ++common_entry->items_size;
+
+ if ((common_entry->common_params.max_elemsize != 0) &&
+ (common_entry->items_size >
+ common_entry->common_params.max_elemsize)) {
+ if (common_entry->policies_size > 1) {
+ policy = common_entry->policies[1];
+ connected_policy = common_entry->policies[0];
+ } else {
+ policy = common_entry->policies[0];
+ connected_policy = NULL;
+ }
+
+ flush_cache_policy(common_entry, policy, connected_policy,
+ cache_elemsize_common_continue_func);
+ }
+
+ TRACE_OUT(cache_write);
+ return (0);
+}
+
+/*
+ * Initializes the write session for the specified multipart entry. This
+ * session then should be filled with data either committed or abandoned by
+ * using close_cache_mp_write_session or abandon_cache_mp_write_session
+ * respectively.
+ * Returns NULL on errors (when there are too many opened write sessions for
+ * the entry).
+ */
+struct cache_mp_write_session_ *
+open_cache_mp_write_session(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_write_session_ *retval;
+
+ TRACE_IN(open_cache_mp_write_session);
+ assert(entry != NULL);
+ assert(entry->params->entry_type == CET_MULTIPART);
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if ((mp_entry->mp_params.max_sessions > 0) &&
+ (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
+ TRACE_OUT(open_cache_mp_write_session);
+ return (NULL);
+ }
+
+ retval = (struct cache_mp_write_session_ *)malloc(
+ sizeof(struct cache_mp_write_session_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_mp_write_session_));
+
+ TAILQ_INIT(&retval->items);
+ retval->parent_entry = mp_entry;
+
+ TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
+ ++mp_entry->ws_size;
+
+ TRACE_OUT(open_cache_mp_write_session);
+ return (retval);
+}
+
+/*
+ * Writes data to the specified session. Return 0 on success and -1 on errors
+ * (when write session size limit is exceeded).
+ */
+int
+cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
+ size_t data_size)
+{
+ struct cache_mp_data_item_ *new_item;
+
+ TRACE_IN(cache_mp_write);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
+ (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
+ TRACE_OUT(cache_mp_write);
+ return (-1);
+ }
+
+ new_item = (struct cache_mp_data_item_ *)malloc(
+ sizeof(struct cache_mp_data_item_));
+ assert(new_item != NULL);
+ memset(new_item, 0, sizeof(struct cache_mp_data_item_));
+
+ new_item->value = (char *)malloc(data_size);
+ assert(new_item->value != NULL);
+ memcpy(new_item->value, data, data_size);
+ new_item->value_size = data_size;
+
+ TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
+ ++ws->items_size;
+
+ TRACE_OUT(cache_mp_write);
+ return (0);
+}
+
+/*
+ * Abandons the write session and frees all the connected resources.
+ */
+void
+abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ TRACE_IN(abandon_cache_mp_write_session);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
+ --ws->parent_entry->ws_size;
+
+ destroy_cache_mp_write_session(ws);
+ TRACE_OUT(abandon_cache_mp_write_session);
+}
+
+/*
+ * Commits the session to the entry, for which it was created.
+ */
+void
+close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ TRACE_IN(close_cache_mp_write_session);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
+ --ws->parent_entry->ws_size;
+
+ if (ws->parent_entry->completed_write_session == NULL) {
+ /*
+ * If there is no completed session yet, this will be the one
+ */
+ ws->parent_entry->get_time_func(
+ &ws->parent_entry->creation_time);
+ ws->parent_entry->completed_write_session = ws;
+ } else {
+ /*
+ * If there is a completed session, then we'll save our session
+ * as a pending session. If there is already a pending session,
+ * it would be destroyed.
+ */
+ if (ws->parent_entry->pending_write_session != NULL)
+ destroy_cache_mp_write_session(
+ ws->parent_entry->pending_write_session);
+
+ ws->parent_entry->pending_write_session = ws;
+ }
+ TRACE_OUT(close_cache_mp_write_session);
+}
+
+/*
+ * Opens read session for the specified entry. Returns NULL on errors (when
+ * there are no data in the entry, or the data are obsolete).
+ */
+struct cache_mp_read_session_ *
+open_cache_mp_read_session(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_read_session_ *retval;
+
+ TRACE_IN(open_cache_mp_read_session);
+ assert(entry != NULL);
+ assert(entry->params->entry_type == CET_MULTIPART);
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if (mp_entry->completed_write_session == NULL) {
+ TRACE_OUT(open_cache_mp_read_session);
+ return (NULL);
+ }
+
+ if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
+ || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
+ if (mp_entry->last_request_time.tv_sec -
+ mp_entry->last_request_time.tv_sec >
+ mp_entry->mp_params.max_lifetime.tv_sec) {
+ flush_cache_entry(entry);
+ TRACE_OUT(open_cache_mp_read_session);
+ return (NULL);
+ }
+ }
+
+ retval = (struct cache_mp_read_session_ *)malloc(
+ sizeof(struct cache_mp_read_session_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_mp_read_session_));
+
+ retval->parent_entry = mp_entry;
+ retval->current_item = TAILQ_FIRST(
+ &mp_entry->completed_write_session->items);
+
+ TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
+ ++mp_entry->rs_size;
+
+ mp_entry->get_time_func(&mp_entry->last_request_time);
+ TRACE_OUT(open_cache_mp_read_session);
+ return (retval);
+}
+
+/*
+ * Reads the data from the read session - step by step.
+ * Returns 0 on success, -1 on error (when there are no more data), and -2 if
+ * the data_size is too small. In the last case, data_size would be filled
+ * the proper value.
+ */
+int
+cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
+{
+
+ TRACE_IN(cache_mp_read);
+ assert(rs != NULL);
+
+ if (rs->current_item == NULL) {
+ TRACE_OUT(cache_mp_read);
+ return (-1);
+ }
+
+ if (rs->current_item->value_size > *data_size) {
+ *data_size = rs->current_item->value_size;
+ if (data == NULL) {
+ TRACE_OUT(cache_mp_read);
+ return (0);
+ }
+
+ TRACE_OUT(cache_mp_read);
+ return (-2);
+ }
+
+ *data_size = rs->current_item->value_size;
+ memcpy(data, rs->current_item->value, rs->current_item->value_size);
+ rs->current_item = TAILQ_NEXT(rs->current_item, entries);
+
+ TRACE_OUT(cache_mp_read);
+ return (0);
+}
+
+/*
+ * Closes the read session. If there are no more read sessions and there is
+ * a pending write session, it will be committed and old
+ * completed_write_session will be destroyed.
+ */
+void
+close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
+{
+
+ TRACE_IN(close_cache_mp_read_session);
+ assert(rs != NULL);
+ assert(rs->parent_entry != NULL);
+
+ TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
+ --rs->parent_entry->rs_size;
+
+ if ((rs->parent_entry->rs_size == 0) &&
+ (rs->parent_entry->pending_write_session != NULL)) {
+ destroy_cache_mp_write_session(
+ rs->parent_entry->completed_write_session);
+ rs->parent_entry->completed_write_session =
+ rs->parent_entry->pending_write_session;
+ rs->parent_entry->pending_write_session = NULL;
+ }
+
+ destroy_cache_mp_read_session(rs);
+ TRACE_OUT(close_cache_mp_read_session);
+}
+
+int
+transform_cache_entry(struct cache_entry_ *entry,
+ enum cache_transformation_t transformation)
+{
+
+ TRACE_IN(transform_cache_entry);
+ switch (transformation) {
+ case CTT_CLEAR:
+ clear_cache_entry(entry);
+ TRACE_OUT(transform_cache_entry);
+ return (0);
+ case CTT_FLUSH:
+ flush_cache_entry(entry);
+ TRACE_OUT(transform_cache_entry);
+ return (0);
+ default:
+ TRACE_OUT(transform_cache_entry);
+ return (-1);
+ }
+}
+
+int
+transform_cache_entry_part(struct cache_entry_ *entry,
+ enum cache_transformation_t transformation, const char *key_part,
+ size_t key_part_size, enum part_position_t part_position)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data, ht_key;
+
+ struct cache_policy_item_ *item, *connected_item;
+
+ TRACE_IN(transform_cache_entry_part);
+ if (entry->params->entry_type != CET_COMMON) {
+ TRACE_OUT(transform_cache_entry_part);
+ return (-1);
+ }
+
+ if (transformation != CTT_CLEAR) {
+ TRACE_OUT(transform_cache_entry_part);
+ return (-1);
+ }
+
+ memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
+ ht_key.key = (char *)key_part; /* can't avoid casting here */
+ ht_key.key_size = key_part_size;
+
+ common_entry = (struct cache_common_entry_ *)entry;
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ do {
+ ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
+ ht_item, &ht_key,
+ ht_items_fixed_size_left_cmp_func);
+
+ if (ht_item_data != NULL) {
+ item = ht_item_data->fifo_policy_item;
+ connected_item = item->connected_item;
+
+ common_entry->policies[0]->remove_item_func(
+ common_entry->policies[0],
+ item);
+
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
+ ht_item_data);
+ --common_entry->items_size;
+
+ common_entry->policies[0]->destroy_item_func(
+ item);
+ if (common_entry->policies_size == 2) {
+ common_entry->policies[1]->remove_item_func(
+ common_entry->policies[1],
+ connected_item);
+ common_entry->policies[1]->destroy_item_func(
+ connected_item);
+ }
+ }
+ } while (ht_item_data != NULL);
+ }
+
+ TRACE_OUT(transform_cache_entry_part);
+ return (0);
+}
diff --git a/usr.sbin/cached/cachelib.h b/usr.sbin/cached/cachelib.h
new file mode 100644
index 0000000..d67e830
--- /dev/null
+++ b/usr.sbin/cached/cachelib.h
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CACHELIB_H__
+#define __CACHED_CACHELIB_H__
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include "hashtable.h"
+#include "cacheplcs.h"
+
+enum cache_entry_t {
+ CET_COMMON = 0, /* cache item is atomic */
+ CET_MULTIPART /* cache item is formed part by part */
+};
+
+enum cache_transformation_t {
+ CTT_FLUSH = 0, /* flush the cache - delete all obsolete items */
+ CTT_CLEAR = 1 /* delete all items in the cache */
+};
+
+/* cache deletion policy type enum */
+enum cache_policy_t {
+ CPT_FIFO = 0, /* first-in first-out */
+ CPT_LRU = 1, /* least recently used */
+ CPT_LFU = 2 /* least frequently used */
+};
+
+/* multipart sessions can be used for reading and writing */
+enum cache_mp_session_t {
+ CMPT_READ_SESSION,
+ CMPT_WRITE_SESSION
+};
+
+/*
+ * When doing partial transformations of entries (which are applied for
+ * elements with keys, that contain specified buffer in its left or
+ * right part), this enum will show the needed position of the key part.
+ */
+enum part_position_t {
+ KPPT_LEFT,
+ KPPT_RIGHT
+};
+
+/* num_levels attribute is obsolete, i think - user can always emulate it
+ * by using one entry.
+ * get_time_func is needed to have the clocks-independent counter
+ */
+struct cache_params
+{
+ void (*get_time_func)(struct timeval *);
+};
+
+/*
+ * base structure - normal_cache_entry_params and multipart_cache_entry_params
+ * are "inherited" from it
+ */
+struct cache_entry_params
+{
+ enum cache_entry_t entry_type;
+ char *entry_name;
+};
+
+/* params, used for most entries */
+struct common_cache_entry_params
+{
+ /* inherited fields */
+ enum cache_entry_t entry_type;
+
+ /* unique fields */
+ char *entry_name;
+ size_t cache_entries_size;
+
+ size_t max_elemsize; /* if 0 then no check is made */
+ size_t satisf_elemsize; /* if entry size is exceeded,
+ * this number of elements will be left,
+ * others will be deleted */
+ struct timeval max_lifetime; /* if 0 then no check is made */
+ enum cache_policy_t policy; /* policy used for transformations */
+};
+
+/* params, used for multipart entries */
+struct mp_cache_entry_params
+{
+ /* inherited fields */
+ enum cache_entry_t entry_type;
+ char *entry_name;
+
+ /* unique fields */
+ size_t max_elemsize; /* if 0 then no check is made */
+ size_t max_sessions; /* maximum number of active sessions */
+
+ struct timeval max_lifetime; /* maximum elements lifetime */
+};
+
+struct cache_ht_item_data_
+{
+ /* key is the bytes sequence only - not the null-terminated string */
+ char *key;
+ size_t key_size;
+
+ char *value;
+ size_t value_size;
+
+ struct cache_policy_item_ *fifo_policy_item;
+};
+
+struct cache_ht_item_
+{
+ HASHTABLE_ENTRY_HEAD(ht_item_, struct cache_ht_item_data_) data;
+};
+
+struct cache_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+};
+
+struct cache_common_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+
+ struct common_cache_entry_params common_params;
+
+ HASHTABLE_HEAD(cache_ht_, cache_ht_item_) items;
+ size_t items_size;
+
+ /*
+ * Entry always has the FIFO policy, that is used to eliminate old
+ * elements (the ones, with lifetime more than max_lifetime). Besides,
+ * user can specify another policy to be applied, when there are too
+ * many elements in the entry. So policies_size can be 1 or 2.
+ */
+ struct cache_policy_ **policies;
+ size_t policies_size;
+
+ void (*get_time_func)(struct timeval *);
+};
+
+struct cache_mp_data_item_ {
+ char *value;
+ size_t value_size;
+
+ TAILQ_ENTRY(cache_mp_data_item_) entries;
+};
+
+struct cache_mp_write_session_
+{
+ struct cache_mp_entry_ *parent_entry;
+
+ /*
+ * All items are accumulated in this queue. When the session is
+ * committed, they all will be copied to the multipart entry.
+ */
+ TAILQ_HEAD(cache_mp_data_item_head, cache_mp_data_item_) items;
+ size_t items_size;
+
+ TAILQ_ENTRY(cache_mp_write_session_) entries;
+};
+
+struct cache_mp_read_session_
+{
+ struct cache_mp_entry_ *parent_entry;
+ struct cache_mp_data_item_ *current_item;
+
+ TAILQ_ENTRY(cache_mp_read_session_) entries;
+};
+
+struct cache_mp_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+
+ struct mp_cache_entry_params mp_params;
+
+ /* All opened write sessions */
+ TAILQ_HEAD(write_sessions_head, cache_mp_write_session_) ws_head;
+ size_t ws_size;
+
+ /* All opened read sessions */
+ TAILQ_HEAD(read_sessions_head, cache_mp_read_session_) rs_head;
+ size_t rs_size;
+
+ /*
+ * completed_write_session is the committed write sessions. All read
+ * sessions use data from it. If the completed_write_session is out of
+ * date, but still in use by some of the read sessions, the newly
+ * committed write session is stored in the pending_write_session.
+ * In such a case, completed_write_session will be substituted with
+ * pending_write_session as soon as it won't be used by any of
+ * the read sessions.
+ */
+ struct cache_mp_write_session_ *completed_write_session;
+ struct cache_mp_write_session_ *pending_write_session;
+ struct timeval creation_time;
+ struct timeval last_request_time;
+
+ void (*get_time_func)(struct timeval *);
+};
+
+struct cache_
+{
+ struct cache_params params;
+
+ struct cache_entry_ **entries;
+ size_t entries_capacity;
+ size_t entries_size;
+};
+
+/* simple abstractions - for not to write "struct" every time */
+typedef struct cache_ *cache;
+typedef struct cache_entry_ *cache_entry;
+typedef struct cache_mp_write_session_ *cache_mp_write_session;
+typedef struct cache_mp_read_session_ *cache_mp_read_session;
+
+#define INVALID_CACHE (NULL)
+#define INVALID_CACHE_ENTRY (NULL)
+#define INVALID_CACHE_MP_WRITE_SESSION (NULL)
+#define INVALID_CACHE_MP_READ_SESSION (NULL)
+
+/*
+ * NOTE: all cache operations are thread-unsafe. You must ensure thread-safety
+ * externally, by yourself.
+ */
+
+/* cache initialization/destruction routines */
+extern cache init_cache(struct cache_params const *);
+extern void destroy_cache(cache);
+
+/* cache entries manipulation routines */
+extern int register_cache_entry(cache, struct cache_entry_params const *);
+extern int unregister_cache_entry(cache, const char *);
+extern cache_entry find_cache_entry(cache, const char *);
+
+/* read/write operations used on common entries */
+extern int cache_read(cache_entry, const char *, size_t, char *, size_t *);
+extern int cache_write(cache_entry, const char *, size_t, char const *, size_t);
+
+/* read/write operations used on multipart entries */
+extern cache_mp_write_session open_cache_mp_write_session(cache_entry);
+extern int cache_mp_write(cache_mp_write_session, char *, size_t);
+extern void abandon_cache_mp_write_session(cache_mp_write_session);
+extern void close_cache_mp_write_session(cache_mp_write_session);
+
+extern cache_mp_read_session open_cache_mp_read_session(cache_entry);
+extern int cache_mp_read(cache_mp_read_session, char *, size_t *);
+extern void close_cache_mp_read_session(cache_mp_read_session);
+
+/* transformation routines */
+extern int transform_cache_entry(cache_entry, enum cache_transformation_t);
+extern int transform_cache_entry_part(cache_entry, enum cache_transformation_t,
+ const char *, size_t, enum part_position_t);
+
+#endif
diff --git a/usr.sbin/cached/cacheplcs.c b/usr.sbin/cached/cacheplcs.c
new file mode 100644
index 0000000..a7ee38d
--- /dev/null
+++ b/usr.sbin/cached/cacheplcs.c
@@ -0,0 +1,590 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <string.h>
+#include "cacheplcs.h"
+#include "debug.h"
+
+static void cache_fifo_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lfu_policy_add_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static struct cache_policy_item_ * cache_lfu_policy_create_item(void);
+static void cache_lfu_policy_destroy_item(struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_first_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_last_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_next_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_prev_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static void cache_lfu_policy_remove_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lfu_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lru_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_queue_policy_add_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static struct cache_policy_item_ * cache_queue_policy_create_item();
+static void cache_queue_policy_destroy_item(struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_first_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_last_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_next_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_prev_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static void cache_queue_policy_remove_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void destroy_cache_queue_policy(struct cache_queue_policy_ *);
+static struct cache_queue_policy_ *init_cache_queue_policy(void);
+
+/*
+ * All cache_queue_policy_XXX functions below will be used to fill
+ * the cache_queue_policy structure. They implement the most functionality of
+ * LRU and FIFO policies. LRU and FIFO policies are actually the
+ * cache_queue_policy_ with cache_update_item function changed.
+ */
+static struct cache_policy_item_ *
+cache_queue_policy_create_item()
+{
+ struct cache_queue_policy_item_ *retval;
+
+ TRACE_IN(cache_queue_policy_create_item);
+ retval = (struct cache_queue_policy_item_ *)malloc(
+ sizeof(struct cache_queue_policy_item_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_queue_policy_item_));
+
+ TRACE_OUT(cache_queue_policy_create_item);
+ return ((struct cache_policy_item_ *)retval);
+}
+
+static void
+cache_queue_policy_destroy_item(struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_queue_policy_destroy_item);
+ assert(item != NULL);
+ free(item);
+ TRACE_OUT(cache_queue_policy_destroy_item);
+}
+
+static void
+cache_queue_policy_add_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_add_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+ TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_queue_policy_add_item);
+}
+
+static void
+cache_queue_policy_remove_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_remove_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_queue_policy_remove_item);
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_first_item(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(cache_queue_policy_get_first_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ TRACE_OUT(cache_queue_policy_get_first_item);
+ return ((struct cache_policy_item_ *)TAILQ_FIRST(&queue_policy->head));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_last_item(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(cache_queue_policy_get_last_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ TRACE_OUT(cache_queue_policy_get_last_item);
+ return ((struct cache_policy_item_ *)TAILQ_LAST(&queue_policy->head,
+ cache_queue_policy_head_));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_next_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_get_next_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TRACE_OUT(cache_queue_policy_get_next_item);
+ return ((struct cache_policy_item_ *)TAILQ_NEXT(queue_item, entries));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_prev_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_get_prev_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TRACE_OUT(cache_queue_policy_get_prev_item);
+ return ((struct cache_policy_item_ *)TAILQ_PREV(queue_item,
+ cache_queue_policy_head_, entries));
+}
+
+/*
+ * Initializes cache_queue_policy_ by filling the structure with the functions
+ * pointers, defined above
+ */
+static struct cache_queue_policy_ *
+init_cache_queue_policy(void)
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_queue_policy);
+ retval = (struct cache_queue_policy_ *)malloc(
+ sizeof(struct cache_queue_policy_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_queue_policy_));
+
+ retval->parent_data.create_item_func = cache_queue_policy_create_item;
+ retval->parent_data.destroy_item_func = cache_queue_policy_destroy_item;
+
+ retval->parent_data.add_item_func = cache_queue_policy_add_item;
+ retval->parent_data.remove_item_func = cache_queue_policy_remove_item;
+
+ retval->parent_data.get_first_item_func =
+ cache_queue_policy_get_first_item;
+ retval->parent_data.get_last_item_func =
+ cache_queue_policy_get_last_item;
+ retval->parent_data.get_next_item_func =
+ cache_queue_policy_get_next_item;
+ retval->parent_data.get_prev_item_func =
+ cache_queue_policy_get_prev_item;
+
+ TAILQ_INIT(&retval->head);
+ TRACE_OUT(init_cache_queue_policy);
+ return (retval);
+}
+
+static void
+destroy_cache_queue_policy(struct cache_queue_policy_ *queue_policy)
+{
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(destroy_cache_queue_policy);
+ while (!TAILQ_EMPTY(&queue_policy->head)) {
+ queue_item = TAILQ_FIRST(&queue_policy->head);
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ cache_queue_policy_destroy_item(
+ (struct cache_policy_item_ *)queue_item);
+ }
+ free(queue_policy);
+ TRACE_OUT(destroy_cache_queue_policy);
+}
+
+/*
+ * Makes cache_queue_policy_ behave like FIFO policy - we don't do anything,
+ * when the cache element is updated. So it always stays in its initial
+ * position in the queue - that is exactly the FIFO functionality.
+ */
+static void
+cache_fifo_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_fifo_policy_update_item);
+ /* policy and item arguments are ignored */
+ TRACE_OUT(cache_fifo_policy_update_item);
+}
+
+struct cache_policy_ *
+init_cache_fifo_policy()
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_fifo_policy);
+ retval = init_cache_queue_policy();
+ retval->parent_data.update_item_func = cache_fifo_policy_update_item;
+
+ TRACE_OUT(init_cache_fifo_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_fifo_policy(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(destroy_cache_fifo_policy);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ destroy_cache_queue_policy(queue_policy);
+ TRACE_OUT(destroy_cache_fifo_policy);
+}
+
+/*
+ * Makes cache_queue_policy_ behave like LRU policy. On each update, cache
+ * element is moved to the end of the queue - so it would be deleted in last
+ * turn. That is exactly the LRU policy functionality.
+ */
+static void
+cache_lru_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_lru_policy_update_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_lru_policy_update_item);
+}
+
+struct cache_policy_ *
+init_cache_lru_policy()
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_lru_policy);
+ retval = init_cache_queue_policy();
+ retval->parent_data.update_item_func = cache_lru_policy_update_item;
+
+ TRACE_OUT(init_cache_lru_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_lru_policy(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(destroy_cache_lru_policy);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ destroy_cache_queue_policy(queue_policy);
+ TRACE_OUT(destroy_cache_lru_policy);
+}
+
+/*
+ * LFU (least frequently used) policy implementation differs much from the
+ * LRU and FIFO (both based on cache_queue_policy_). Almost all cache_policy_
+ * functions are implemented specifically for this policy. The idea of this
+ * policy is to represent frequency (real number) as the integer number and
+ * use it as the index in the array. Each array's element is
+ * the list of elements. For example, if we have the 100-elements
+ * array for this policy, the elements with frequency 0.1 (calls per-second)
+ * would be in 10th element of the array.
+ */
+static struct cache_policy_item_ *
+cache_lfu_policy_create_item(void)
+{
+ struct cache_lfu_policy_item_ *retval;
+
+ TRACE_IN(cache_lfu_policy_create_item);
+ retval = (struct cache_lfu_policy_item_ *)malloc(
+ sizeof(struct cache_lfu_policy_item_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_lfu_policy_item_));
+
+ TRACE_OUT(cache_lfu_policy_create_item);
+ return ((struct cache_policy_item_ *)retval);
+}
+
+static void
+cache_lfu_policy_destroy_item(struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_lfu_policy_destroy_item);
+ assert(item != NULL);
+ free(item);
+ TRACE_OUT(cache_lfu_policy_destroy_item);
+}
+
+/*
+ * When placed in the LFU policy queue for the first time, the maximum
+ * frequency is assigned to the element
+ */
+static void
+cache_lfu_policy_add_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(cache_lfu_policy_add_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ lfu_item->frequency = CACHELIB_MAX_FREQUENCY - 1;
+ TAILQ_INSERT_HEAD(&(lfu_policy->groups[CACHELIB_MAX_FREQUENCY - 1]),
+ lfu_item, entries);
+ TRACE_OUT(cache_lfu_policy_add_item);
+}
+
+/*
+ * On each update the frequency of the element is recalculated and, if it
+ * changed, the element would be moved to the another place in the array.
+ */
+static void
+cache_lfu_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int index;
+
+ TRACE_IN(cache_lfu_policy_update_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ /*
+ * We calculate the square of the request_count to avoid grouping of
+ * all elements at the start of the array (for example, if array size is
+ * 100 and most of its elements has frequency below the 0.01, they
+ * all would be grouped in the first array's position). Other
+ * techniques should be used here later to ensure, that elements are
+ * equally distributed in the array and not grouped in its beginning.
+ */
+ if (lfu_item->parent_data.last_request_time.tv_sec !=
+ lfu_item->parent_data.creation_time.tv_sec) {
+ index = ((double)lfu_item->parent_data.request_count *
+ (double)lfu_item->parent_data.request_count /
+ (lfu_item->parent_data.last_request_time.tv_sec -
+ lfu_item->parent_data.creation_time.tv_sec + 1)) *
+ CACHELIB_MAX_FREQUENCY;
+ if (index >= CACHELIB_MAX_FREQUENCY)
+ index = CACHELIB_MAX_FREQUENCY - 1;
+ } else
+ index = CACHELIB_MAX_FREQUENCY - 1;
+
+ TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
+ entries);
+ lfu_item->frequency = index;
+ TAILQ_INSERT_HEAD(&(lfu_policy->groups[index]), lfu_item, entries);
+
+ TRACE_OUT(cache_lfu_policy_update_item);
+}
+
+static void
+cache_lfu_policy_remove_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(cache_lfu_policy_remove_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
+ entries);
+ TRACE_OUT(cache_lfu_policy_remove_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_first_item(struct cache_policy_ *policy)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_first_item);
+ lfu_item = NULL;
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ break;
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_first_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_last_item(struct cache_policy_ *policy)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_last_item);
+ lfu_item = NULL;
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = CACHELIB_MAX_FREQUENCY - 1; i >= 0; --i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
+ cache_lfu_policy_group_);
+ break;
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_last_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_next_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_next_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = TAILQ_NEXT((struct cache_lfu_policy_item_ *)item, entries);
+ if (lfu_item == NULL)
+ {
+ for (i = ((struct cache_lfu_policy_item_ *)item)->frequency + 1;
+ i < CACHELIB_MAX_FREQUENCY; ++i) {
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ break;
+ }
+ }
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_next_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_prev_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_prev_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = TAILQ_PREV((struct cache_lfu_policy_item_ *)item,
+ cache_lfu_policy_group_, entries);
+ if (lfu_item == NULL)
+ {
+ for (i = ((struct cache_lfu_policy_item_ *)item)->frequency - 1;
+ i >= 0; --i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
+ cache_lfu_policy_group_);
+ break;
+ }
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_prev_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+/*
+ * Initializes the cache_policy_ structure by filling it with appropriate
+ * functions pointers
+ */
+struct cache_policy_ *
+init_cache_lfu_policy()
+{
+ int i;
+ struct cache_lfu_policy_ *retval;
+
+ TRACE_IN(init_cache_lfu_policy);
+ retval = (struct cache_lfu_policy_ *)malloc(
+ sizeof(struct cache_lfu_policy_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_lfu_policy_));
+
+ retval->parent_data.create_item_func = cache_lfu_policy_create_item;
+ retval->parent_data.destroy_item_func = cache_lfu_policy_destroy_item;
+
+ retval->parent_data.add_item_func = cache_lfu_policy_add_item;
+ retval->parent_data.update_item_func = cache_lfu_policy_update_item;
+ retval->parent_data.remove_item_func = cache_lfu_policy_remove_item;
+
+ retval->parent_data.get_first_item_func =
+ cache_lfu_policy_get_first_item;
+ retval->parent_data.get_last_item_func =
+ cache_lfu_policy_get_last_item;
+ retval->parent_data.get_next_item_func =
+ cache_lfu_policy_get_next_item;
+ retval->parent_data.get_prev_item_func =
+ cache_lfu_policy_get_prev_item;
+
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
+ TAILQ_INIT(&(retval->groups[i]));
+
+ TRACE_OUT(init_cache_lfu_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_lfu_policy(struct cache_policy_ *policy)
+{
+ int i;
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(destroy_cache_lfu_policy);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) {
+ while (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ TAILQ_REMOVE(&(lfu_policy->groups[i]), lfu_item,
+ entries);
+ cache_lfu_policy_destroy_item(
+ (struct cache_policy_item_ *)lfu_item);
+ }
+ }
+ free(policy);
+ TRACE_OUT(destroy_cache_lfu_policy);
+}
diff --git a/usr.sbin/cached/cacheplcs.h b/usr.sbin/cached/cacheplcs.h
new file mode 100644
index 0000000..36997f5
--- /dev/null
+++ b/usr.sbin/cached/cacheplcs.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CACHEPLCS_H__
+#define __CACHED_CACHEPLCS_H__
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+/* common policy definitions */
+#define CACHELIB_MAX_FREQUENCY 100
+
+/*
+ * cache_policy_item_ represents some abstract cache element in the policy
+ * queue. connected_item pointers to the corresponding cache_policy_item_ in
+ * another policy queue.
+ */
+struct cache_policy_item_
+{
+ char *key;
+ size_t key_size;
+
+ size_t request_count;
+ struct timeval last_request_time;
+ struct timeval creation_time;
+
+ struct cache_policy_item_ *connected_item;
+};
+
+/*
+ * cache_policy_ represents an abstract policy queue. It can be customized by
+ * setting appropriate function pointers
+ */
+struct cache_policy_
+{
+ struct cache_policy_item_* (*create_item_func)();
+ void (*destroy_item_func)(struct cache_policy_item_ *);
+
+ void (*add_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+ void (*remove_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+ void (*update_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+
+ struct cache_policy_item_ *(*get_first_item_func)(
+ struct cache_policy_ *);
+ struct cache_policy_item_ *(*get_last_item_func)(
+ struct cache_policy_ *);
+ struct cache_policy_item_ *(*get_next_item_func)(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+ struct cache_policy_item_ *(*get_prev_item_func)(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+};
+
+/*
+ * LFU cache policy item "inherited" from cache_policy_item_ structure
+ */
+struct cache_lfu_policy_item_
+{
+ struct cache_policy_item_ parent_data;
+ int frequency;
+
+ TAILQ_ENTRY(cache_lfu_policy_item_) entries;
+};
+
+TAILQ_HEAD(cache_lfu_policy_group_, cache_lfu_policy_item_);
+
+/*
+ * LFU policy queue "inherited" from cache_policy_.
+ */
+struct cache_lfu_policy_
+{
+ struct cache_policy_ parent_data;
+ struct cache_lfu_policy_group_ groups[CACHELIB_MAX_FREQUENCY];
+};
+
+/*
+ * LRU and FIFO policies item "inherited" from cache_policy_item_
+ */
+struct cache_queue_policy_item_
+{
+ struct cache_policy_item_ parent_data;
+ TAILQ_ENTRY(cache_queue_policy_item_) entries;
+};
+
+/*
+ * LRU and FIFO policies "inherited" from cache_policy_
+ */
+struct cache_queue_policy_
+{
+ struct cache_policy_ parent_data;
+ TAILQ_HEAD(cache_queue_policy_head_, cache_queue_policy_item_) head;
+};
+
+typedef struct cache_queue_policy_ cache_fifo_policy_;
+typedef struct cache_queue_policy_ cache_lru_policy_;
+
+/* fifo policy routines */
+extern struct cache_policy_ *init_cache_fifo_policy();
+extern void destroy_cache_fifo_policy(struct cache_policy_ *);
+
+/* lru policy routines */
+extern struct cache_policy_ *init_cache_lru_policy();
+extern void destroy_cache_lru_policy(struct cache_policy_ *);
+
+/* lfu policy routines */
+extern struct cache_policy_ *init_cache_lfu_policy();
+extern void destroy_cache_lfu_policy(struct cache_policy_ *);
+
+#endif
diff --git a/usr.sbin/cached/config.c b/usr.sbin/cached/config.c
new file mode 100644
index 0000000..bc3eb49
--- /dev/null
+++ b/usr.sbin/cached/config.c
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+
+/*
+ * Default entries, which always exist in the configuration
+ */
+const char *c_default_entries[6] = {
+ NSDB_PASSWD,
+ NSDB_GROUP,
+ NSDB_HOSTS,
+ NSDB_SERVICES,
+ NSDB_PROTOCOLS,
+ NSDB_RPC
+ };
+
+static int configuration_entry_cmp(const void *, const void *);
+static int configuration_entry_sort_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_sort_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_part_cmp(const void *, const void *);
+static struct configuration_entry *create_configuration_entry(const char *,
+ struct timeval const *, struct timeval const *,
+ struct common_cache_entry_params const *,
+ struct common_cache_entry_params const *,
+ struct mp_cache_entry_params const *);
+
+static int
+configuration_entry_sort_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((*((struct configuration_entry **)e1))->name,
+ (*((struct configuration_entry **)e2))->name
+ ));
+}
+
+static int
+configuration_entry_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((const char *)e1,
+ (*((struct configuration_entry **)e2))->name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((*((cache_entry *)e1))->params->entry_name,
+ (*((cache_entry *)e2))->params->entry_name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((const char *)e1,
+ (*((cache_entry *)e2))->params->entry_name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2)
+{
+ return (strncmp((const char *)e1,
+ (*((cache_entry *)e2))->params->entry_name,
+ strlen((const char *)e1)
+ ));
+}
+
+static struct configuration_entry *
+create_configuration_entry(const char *name,
+ struct timeval const *common_timeout,
+ struct timeval const *mp_timeout,
+ struct common_cache_entry_params const *positive_params,
+ struct common_cache_entry_params const *negative_params,
+ struct mp_cache_entry_params const *mp_params)
+{
+ struct configuration_entry *retval;
+ size_t size;
+ int res;
+
+ TRACE_IN(create_configuration_entry);
+ assert(name != NULL);
+ assert(positive_params != NULL);
+ assert(negative_params != NULL);
+ assert(mp_params != NULL);
+
+ retval = (struct configuration_entry *)malloc(
+ sizeof(struct configuration_entry));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct configuration_entry));
+
+ res = pthread_mutex_init(&retval->positive_cache_lock, NULL);
+ if (res != 0) {
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create positive cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ res = pthread_mutex_init(&retval->negative_cache_lock, NULL);
+ if (res != 0) {
+ pthread_mutex_destroy(&retval->positive_cache_lock);
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create negative cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ res = pthread_mutex_init(&retval->mp_cache_lock, NULL);
+ if (res != 0) {
+ pthread_mutex_destroy(&retval->positive_cache_lock);
+ pthread_mutex_destroy(&retval->negative_cache_lock);
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create negative cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ memcpy(&retval->positive_cache_params, positive_params,
+ sizeof(struct common_cache_entry_params));
+ memcpy(&retval->negative_cache_params, negative_params,
+ sizeof(struct common_cache_entry_params));
+ memcpy(&retval->mp_cache_params, mp_params,
+ sizeof(struct mp_cache_entry_params));
+
+ size = strlen(name);
+ retval->name = (char *)malloc(size + 1);
+ assert(retval->name != NULL);
+ memset(retval->name, 0, size + 1);
+ memcpy(retval->name, name, size);
+
+ memcpy(&retval->common_query_timeout, common_timeout,
+ sizeof(struct timeval));
+ memcpy(&retval->mp_query_timeout, mp_timeout,
+ sizeof(struct timeval));
+
+ asprintf(&retval->positive_cache_params.entry_name, "%s+", name);
+ assert(retval->positive_cache_params.entry_name != NULL);
+
+ asprintf(&retval->negative_cache_params.entry_name, "%s-", name);
+ assert(retval->negative_cache_params.entry_name != NULL);
+
+ asprintf(&retval->mp_cache_params.entry_name, "%s*", name);
+ assert(retval->mp_cache_params.entry_name != NULL);
+
+ TRACE_OUT(create_configuration_entry);
+ return (retval);
+}
+
+/*
+ * Creates configuration entry and fills it with default values
+ */
+struct configuration_entry *
+create_def_configuration_entry(const char *name)
+{
+ struct common_cache_entry_params positive_params, negative_params;
+ struct mp_cache_entry_params mp_params;
+ struct timeval default_common_timeout, default_mp_timeout;
+
+ struct configuration_entry *res = NULL;
+
+ TRACE_IN(create_def_configuration_entry);
+ memset(&positive_params, 0,
+ sizeof(struct common_cache_entry_params));
+ positive_params.entry_type = CET_COMMON;
+ positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE;
+ positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE;
+ positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2;
+ positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME;
+ positive_params.policy = CPT_LRU;
+
+ memcpy(&negative_params, &positive_params,
+ sizeof(struct common_cache_entry_params));
+ negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE;
+ negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2;
+ negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME;
+ negative_params.policy = CPT_FIFO;
+
+ memset(&default_common_timeout, 0, sizeof(struct timeval));
+ default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT;
+
+ memset(&default_mp_timeout, 0, sizeof(struct timeval));
+ default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT;
+
+ memset(&mp_params, 0,
+ sizeof(struct mp_cache_entry_params));
+ mp_params.entry_type = CET_MULTIPART;
+ mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE;
+ mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE;
+ mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME;
+
+ res = create_configuration_entry(name, &default_common_timeout,
+ &default_mp_timeout, &positive_params, &negative_params,
+ &mp_params);
+
+ TRACE_OUT(create_def_configuration_entry);
+ return (res);
+}
+
+void
+destroy_configuration_entry(struct configuration_entry *entry)
+{
+ TRACE_IN(destroy_configuration_entry);
+ assert(entry != NULL);
+ pthread_mutex_destroy(&entry->positive_cache_lock);
+ pthread_mutex_destroy(&entry->negative_cache_lock);
+ pthread_mutex_destroy(&entry->mp_cache_lock);
+ free(entry->name);
+ free(entry->positive_cache_params.entry_name);
+ free(entry->negative_cache_params.entry_name);
+ free(entry->mp_cache_params.entry_name);
+ free(entry->mp_cache_entries);
+ free(entry);
+ TRACE_OUT(destroy_configuration_entry);
+}
+
+int
+add_configuration_entry(struct configuration *config,
+ struct configuration_entry *entry)
+{
+ TRACE_IN(add_configuration_entry);
+ assert(entry != NULL);
+ assert(entry->name != NULL);
+ if (configuration_find_entry(config, entry->name) != NULL) {
+ TRACE_OUT(add_configuration_entry);
+ return (-1);
+ }
+
+ if (config->entries_size == config->entries_capacity) {
+ struct configuration_entry **new_entries;
+
+ config->entries_capacity *= 2;
+ new_entries = (struct configuration_entry **)malloc(
+ sizeof(struct configuration_entry *) *
+ config->entries_capacity);
+ assert(new_entries != NULL);
+ memset(new_entries, 0, sizeof(struct configuration_entry *) *
+ config->entries_capacity);
+ memcpy(new_entries, config->entries,
+ sizeof(struct configuration_entry *) *
+ config->entries_size);
+
+ free(config->entries);
+ config->entries = new_entries;
+ }
+
+ config->entries[config->entries_size++] = entry;
+ qsort(config->entries, config->entries_size,
+ sizeof(struct configuration_entry *),
+ configuration_entry_sort_cmp);
+
+ TRACE_OUT(add_configuration_entry);
+ return (0);
+}
+
+size_t
+configuration_get_entries_size(struct configuration *config)
+{
+ TRACE_IN(configuration_get_entries_size);
+ assert(config != NULL);
+ TRACE_OUT(configuration_get_entries_size);
+ return (config->entries_size);
+}
+
+struct configuration_entry *
+configuration_get_entry(struct configuration *config, size_t index)
+{
+ TRACE_IN(configuration_get_entry);
+ assert(config != NULL);
+ assert(index < config->entries_size);
+ TRACE_OUT(configuration_get_entry);
+ return (config->entries[index]);
+}
+
+struct configuration_entry *
+configuration_find_entry(struct configuration *config,
+ const char *name)
+{
+ struct configuration_entry **retval;
+
+ TRACE_IN(configuration_find_entry);
+
+ retval = bsearch(name, config->entries, config->entries_size,
+ sizeof(struct configuration_entry *), configuration_entry_cmp);
+ TRACE_OUT(configuration_find_entry);
+
+ return ((retval != NULL) ? *retval : NULL);
+}
+
+/*
+ * All multipart cache entries are stored in the configuration_entry in the
+ * sorted array (sorted by names). The 3 functions below manage this array.
+ */
+
+int
+configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry,
+ cache_entry c_entry)
+{
+ cache_entry *new_mp_entries, *old_mp_entries;
+
+ TRACE_IN(configuration_entry_add_mp_cache_entry);
+ ++config_entry->mp_cache_entries_size;
+ new_mp_entries = (cache_entry *)malloc(sizeof(cache_entry) *
+ config_entry->mp_cache_entries_size);
+ assert(new_mp_entries != NULL);
+ new_mp_entries[0] = c_entry;
+
+ if (config_entry->mp_cache_entries_size - 1 > 0) {
+ memcpy(new_mp_entries + 1,
+ config_entry->mp_cache_entries,
+ (config_entry->mp_cache_entries_size - 1) *
+ sizeof(cache_entry));
+ }
+
+ old_mp_entries = config_entry->mp_cache_entries;
+ config_entry->mp_cache_entries = new_mp_entries;
+ free(old_mp_entries);
+
+ qsort(config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry),
+ configuration_entry_cache_mp_sort_cmp);
+
+ TRACE_OUT(configuration_entry_add_mp_cache_entry);
+ return (0);
+}
+
+cache_entry
+configuration_entry_find_mp_cache_entry(
+ struct configuration_entry *config_entry, const char *mp_name)
+{
+ cache_entry *result;
+
+ TRACE_IN(configuration_entry_find_mp_cache_entry);
+ result = bsearch(mp_name, config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry), configuration_entry_cache_mp_cmp);
+
+ if (result == NULL) {
+ TRACE_OUT(configuration_entry_find_mp_cache_entry);
+ return (NULL);
+ } else {
+ TRACE_OUT(configuration_entry_find_mp_cache_entry);
+ return (*result);
+ }
+}
+
+/*
+ * Searches for all multipart entries with names starting with mp_name.
+ * Needed for cache flushing.
+ */
+int
+configuration_entry_find_mp_cache_entries(
+ struct configuration_entry *config_entry, const char *mp_name,
+ cache_entry **start, cache_entry **finish)
+{
+ cache_entry *result;
+
+ TRACE_IN(configuration_entry_find_mp_cache_entries);
+ result = bsearch(mp_name, config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry), configuration_entry_cache_mp_part_cmp);
+
+ if (result == NULL) {
+ TRACE_OUT(configuration_entry_find_mp_cache_entries);
+ return (-1);
+ }
+
+ *start = result;
+ *finish = result + 1;
+
+ while (*start != config_entry->mp_cache_entries) {
+ if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0)
+ *start = *start - 1;
+ else
+ break;
+ }
+
+ while (*finish != config_entry->mp_cache_entries +
+ config_entry->mp_cache_entries_size) {
+
+ if (configuration_entry_cache_mp_part_cmp(
+ mp_name, *finish) == 0)
+ *finish = *finish + 1;
+ else
+ break;
+ }
+
+ TRACE_OUT(configuration_entry_find_mp_cache_entries);
+ return (0);
+}
+
+/*
+ * Configuration entry uses rwlock to handle access to its fields.
+ */
+void
+configuration_lock_rdlock(struct configuration *config)
+{
+ TRACE_IN(configuration_lock_rdlock);
+ pthread_rwlock_rdlock(&config->rwlock);
+ TRACE_OUT(configuration_lock_rdlock);
+}
+
+void
+configuration_lock_wrlock(struct configuration *config)
+{
+ TRACE_IN(configuration_lock_wrlock);
+ pthread_rwlock_wrlock(&config->rwlock);
+ TRACE_OUT(configuration_lock_wrlock);
+}
+
+void
+configuration_unlock(struct configuration *config)
+{
+ TRACE_IN(configuration_unlock);
+ pthread_rwlock_unlock(&config->rwlock);
+ TRACE_OUT(configuration_unlock);
+}
+
+/*
+ * Configuration entry uses 3 mutexes to handle cache operations. They are
+ * acquired by configuration_lock_entry and configuration_unlock_entry
+ * functions.
+ */
+void
+configuration_lock_entry(struct configuration_entry *entry,
+ enum config_entry_lock_type lock_type)
+{
+ TRACE_IN(configuration_lock_entry);
+ assert(entry != NULL);
+
+ switch (lock_type) {
+ case CELT_POSITIVE:
+ pthread_mutex_lock(&entry->positive_cache_lock);
+ break;
+ case CELT_NEGATIVE:
+ pthread_mutex_lock(&entry->negative_cache_lock);
+ break;
+ case CELT_MULTIPART:
+ pthread_mutex_lock(&entry->mp_cache_lock);
+ break;
+ default:
+ /* should be unreachable */
+ break;
+ }
+ TRACE_OUT(configuration_lock_entry);
+}
+
+void
+configuration_unlock_entry(struct configuration_entry *entry,
+ enum config_entry_lock_type lock_type)
+{
+ TRACE_IN(configuration_unlock_entry);
+ assert(entry != NULL);
+
+ switch (lock_type) {
+ case CELT_POSITIVE:
+ pthread_mutex_unlock(&entry->positive_cache_lock);
+ break;
+ case CELT_NEGATIVE:
+ pthread_mutex_unlock(&entry->negative_cache_lock);
+ break;
+ case CELT_MULTIPART:
+ pthread_mutex_unlock(&entry->mp_cache_lock);
+ break;
+ default:
+ /* should be unreachable */
+ break;
+ }
+ TRACE_OUT(configuration_unlock_entry);
+}
+
+struct configuration *
+init_configuration(void)
+{
+ struct configuration *retval;
+
+ TRACE_IN(init_configuration);
+ retval = (struct configuration *)malloc(sizeof(struct configuration));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct configuration));
+
+ retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
+ retval->entries = (struct configuration_entry **)malloc(
+ sizeof(struct configuration_entry *) *
+ retval->entries_capacity);
+ assert(retval->entries != NULL);
+ memset(retval->entries, 0, sizeof(struct configuration_entry *) *
+ retval->entries_capacity);
+
+ pthread_rwlock_init(&retval->rwlock, NULL);
+
+ TRACE_OUT(init_configuration);
+ return (retval);
+}
+
+void
+fill_configuration_defaults(struct configuration *config)
+{
+ size_t len, i;
+
+ TRACE_IN(fill_configuration_defaults);
+ assert(config != NULL);
+
+ if (config->socket_path != NULL)
+ free(config->socket_path);
+
+ len = strlen(DEFAULT_SOCKET_PATH);
+ config->socket_path = (char *)malloc(len + 1);
+ assert(config->socket_path != NULL);
+ memset(config->socket_path, 0, len + 1);
+ memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len);
+
+ len = strlen(DEFAULT_PIDFILE_PATH);
+ config->pidfile_path = (char *)malloc(len + 1);
+ assert(config->pidfile_path != NULL);
+ memset(config->pidfile_path, 0, len + 1);
+ memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len);
+
+ config->socket_mode = S_IFSOCK | S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ config->force_unlink = 1;
+
+ config->query_timeout = DEFAULT_QUERY_TIMEOUT;
+ config->threads_num = DEFAULT_THREADS_NUM;
+
+ for (i = 0; i < config->entries_size; ++i)
+ destroy_configuration_entry(config->entries[i]);
+ config->entries_size = 0;
+
+ TRACE_OUT(fill_configuration_defaults);
+}
+
+void
+destroy_configuration(struct configuration *config)
+{
+ int i;
+ TRACE_IN(destroy_configuration);
+ assert(config != NULL);
+ free(config->pidfile_path);
+ free(config->socket_path);
+
+ for (i = 0; i < config->entries_size; ++i)
+ destroy_configuration_entry(config->entries[i]);
+ free(config->entries);
+
+ pthread_rwlock_destroy(&config->rwlock);
+ free(config);
+ TRACE_OUT(destroy_configuration);
+}
diff --git a/usr.sbin/cached/config.h b/usr.sbin/cached/config.h
new file mode 100644
index 0000000..b54dc9b
--- /dev/null
+++ b/usr.sbin/cached/config.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CONFIG_H__
+#define __CACHED_CONFIG_H__
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <nsswitch.h>
+#include <unistd.h>
+#include "cachelib.h"
+
+#define DEFAULT_QUERY_TIMEOUT 8
+#define DEFAULT_THREADS_NUM 8
+
+#define DEFAULT_COMMON_ENTRY_TIMEOUT 10
+#define DEFAULT_MP_ENTRY_TIMEOUT 60
+#define DEFAULT_CACHE_HT_SIZE 257
+
+#define INITIAL_ENTRIES_CAPACITY 8
+#define DEFAULT_SOCKET_PATH "/var/run/cached"
+#define DEFAULT_PIDFILE_PATH "/var/run/cached.pid"
+
+#define DEFAULT_POSITIVE_ELEMENTS_SIZE (2048)
+#define DEFAULT_POSITIVE_LIFETIME (3600)
+
+#define DEFAULT_NEGATIVE_ELEMENTS_SIZE (2048)
+#define DEFAULT_NEGATIVE_LIFETIME (60)
+
+#define DEFAULT_MULTIPART_ELEMENTS_SIZE (1024 * 8)
+#define DEFAULT_MULITPART_SESSIONS_SIZE (1024)
+#define DEFAULT_MULITPART_LIFETIME (3600)
+
+extern const char *c_default_entries[6];
+
+/*
+ * Configuration entry represents the details of each cache entry in the
+ * config file (i.e. passwd or group). Its purpose also is to acquire locks
+ * of three different types (for usual read/write caching, for multipart
+ * caching and for caching of the negative results) for that cache entry.
+ */
+struct configuration_entry {
+ struct common_cache_entry_params positive_cache_params;
+ struct common_cache_entry_params negative_cache_params;
+ struct mp_cache_entry_params mp_cache_params;
+
+ /*
+ * configuration_entry holds pointers for all actual cache_entries,
+ * which are used for it. There is one for positive caching, one for
+ * for negative caching, and several (one per each euid/egid) for
+ * multipart caching.
+ */
+ cache_entry positive_cache_entry;
+ cache_entry negative_cache_entry;
+
+ cache_entry *mp_cache_entries;
+ size_t mp_cache_entries_size;
+
+ struct timeval common_query_timeout;
+ struct timeval mp_query_timeout;
+
+ char *name;
+ pthread_mutex_t positive_cache_lock;
+ pthread_mutex_t negative_cache_lock;
+ pthread_mutex_t mp_cache_lock;
+
+ int perform_actual_lookups;
+ int enabled;
+};
+
+/*
+ * Contains global configuration options and array of all configuration entries
+ */
+struct configuration {
+ char *pidfile_path;
+ char *socket_path;
+
+ struct configuration_entry **entries;
+ size_t entries_capacity;
+ size_t entries_size;
+
+ pthread_rwlock_t rwlock;
+
+ mode_t socket_mode;
+ int force_unlink;
+ int query_timeout;
+
+ int threads_num;
+};
+
+enum config_entry_lock_type {
+ CELT_POSITIVE,
+ CELT_NEGATIVE,
+ CELT_MULTIPART
+};
+
+extern struct configuration *init_configuration(void);
+extern void destroy_configuration(struct configuration *);
+extern void fill_configuration_defaults(struct configuration *);
+
+extern int add_configuration_entry(struct configuration *,
+ struct configuration_entry *);
+extern struct configuration_entry *create_def_configuration_entry(
+ const char *);
+extern void destroy_configuration_entry(struct configuration_entry *);
+extern size_t configuration_get_entries_size(struct configuration *);
+extern struct configuration_entry *configuration_get_entry(
+ struct configuration *, size_t);
+extern struct configuration_entry *configuration_find_entry(
+ struct configuration *, const char *);
+
+extern int configuration_entry_add_mp_cache_entry(struct configuration_entry *,
+ cache_entry);
+extern cache_entry configuration_entry_find_mp_cache_entry(
+ struct configuration_entry *,
+ const char *);
+extern int configuration_entry_find_mp_cache_entries(
+ struct configuration_entry *, const char *, cache_entry **,
+ cache_entry **);
+
+extern void configuration_lock_rdlock(struct configuration *config);
+extern void configuration_lock_wrlock(struct configuration *config);
+extern void configuration_unlock(struct configuration *config);
+
+extern void configuration_lock_entry(struct configuration_entry *,
+ enum config_entry_lock_type);
+extern void configuration_unlock_entry(struct configuration_entry *,
+ enum config_entry_lock_type);
+
+#endif
diff --git a/usr.sbin/cached/debug.c b/usr.sbin/cached/debug.c
new file mode 100644
index 0000000..420c517
--- /dev/null
+++ b/usr.sbin/cached/debug.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include "debug.h"
+
+static int trace_level = 0;
+static int trace_level_bk = 0;
+
+void
+__trace_in(const char *s, const char *f, int l)
+{
+ int i;
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level; ++i)
+ printf("\t");
+
+ printf("=> %s\n", s);
+ }
+
+ ++trace_level;
+}
+
+void
+__trace_point(const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= %s: %d\n", f, l);
+ }
+}
+
+void
+__trace_msg(const char *msg, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= MSG %s, %s: %d\n", msg, f, l);
+ }
+}
+
+void
+__trace_ptr(const char *desc, const void *p, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= PTR %s: %p, %s: %d\n", desc, p, f, l);
+ }
+}
+
+void
+__trace_int(const char *desc, int i, const char *f, int l)
+{
+ int j;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (j = 0; j < trace_level - 1; ++j)
+ printf("\t");
+
+ printf("= INT %s: %i, %s: %d\n",desc, i, f, l);
+ }
+}
+
+void
+__trace_str(const char *desc, const char *s, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= STR %s: '%s', %s: %d\n", desc, s, f, l);
+ }
+}
+
+void
+__trace_out(const char *s, const char *f, int l)
+{
+ int i;
+
+ --trace_level;
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level; ++i)
+ printf("\t");
+
+ printf("<= %s\n", s);
+ }
+}
+
+void
+__trace_on()
+{
+ trace_level = trace_level_bk;
+ trace_level_bk = 0;
+}
+
+void
+__trace_off()
+{
+ trace_level_bk = trace_level;
+ trace_level = 1024;
+}
diff --git a/usr.sbin/cached/debug.h b/usr.sbin/cached/debug.h
new file mode 100644
index 0000000..320e10f
--- /dev/null
+++ b/usr.sbin/cached/debug.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_DEBUG_H__
+#define __CACHED_DEBUG_H__
+
+#define TRACE_WANTED 32
+
+/* #ifndef NDEBUG */
+#if 0
+#define TRACE_IN(x) __trace_in(#x, __FILE__, __LINE__)
+#define TRACE_POINT() __trace_point(__FILE__, __LINE__)
+#define TRACE_MSG(x) __trace_msg(x, __FILE__, __LINE__)
+#define TRACE_PTR(p) __trace_ptr(#p, p, __FILE__, __LINE__)
+#define TRACE_INT(i) __trace_int(#i, i, __FILE__, __LINE__)
+#define TRACE_STR(s) __trace_str(#s, s, __FILE__, __LINE__)
+#define TRACE_OUT(x) __trace_out(#x, __FILE__, __LINE__)
+#define TRACE_ON() __trace_on()
+#define TRACE_OFF() __trace_off()
+#else
+#define TRACE_IN(x)
+#define TRACE_POINT()
+#define TRACE_MSG(x)
+#define TRACE_PTR(p)
+#define TRACE_INT(i)
+#define TRACE_STR(s)
+#define TRACE_OUT(x)
+#define TRACE_ON()
+#define TRACE_OFF()
+#endif
+
+extern void __trace_in(const char *, const char *, int);
+extern void __trace_point(const char *, int);
+extern void __trace_msg(const char *, const char *, int);
+extern void __trace_ptr(const char *, const void *, const char *, int);
+extern void __trace_int(const char *, int, const char *, int);
+extern void __trace_str(const char *, const char *, const char *, int);
+extern void __trace_out(const char *, const char *, int);
+extern void __trace_on();
+extern void __trace_off();
+
+#endif
diff --git a/usr.sbin/cached/hashtable.h b/usr.sbin/cached/hashtable.h
new file mode 100644
index 0000000..86dad9f
--- /dev/null
+++ b/usr.sbin/cached/hashtable.h
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHELIB_HASHTABLE_H__
+#define __CACHELIB_HASHTABLE_H__
+
+#include <search.h>
+#include <string.h>
+
+#define HASHTABLE_INITIAL_ENTRIES_CAPACITY 8
+typedef int hashtable_index_t;
+
+/*
+ * This file contains queue.h-like macro definitions for hash tables.
+ * Hash table is organized as an array of the specified size of the user
+ * defined (with HASTABLE_ENTRY_HEAD) structures. Each hash table
+ * entry (user defined structure) stores its elements in the sorted array.
+ * You can place elements into the hash table, retrieve elements with
+ * specified key, traverse through all elements, and delete them.
+ * New elements are placed into the hash table by using the compare and
+ * hashing functions, provided by the user.
+ */
+
+/*
+ * Defines the hash table entry structure, that uses specified type of
+ * elements.
+ */
+#define HASHTABLE_ENTRY_HEAD(name, type) struct name { \
+ type *values; \
+ size_t capacity; \
+ size_t size; \
+}
+
+/*
+ * Defines the hash table structure, which uses the specified type of entries.
+ * The only restriction for entries is that is that they should have the field,
+ * defined with HASHTABLE_ENTRY_HEAD macro.
+ */
+#define HASHTABLE_HEAD(name, entry) struct name { \
+ struct entry *entries; \
+ size_t entries_size; \
+}
+
+#define HASHTABLE_ENTRIES_COUNT(table) ((table)->entries_size)
+
+/*
+ * Unlike most of queue.h data types, hash tables can not be initialized
+ * statically - so there is no HASHTABLE_HEAD_INITIALIZED macro.
+ */
+#define HASHTABLE_INIT(table, type, field, _entries_size) \
+ do { \
+ hashtable_index_t var; \
+ (table)->entries = (void *)malloc( \
+ sizeof(*(table)->entries) * (_entries_size)); \
+ memset((table)->entries, 0, \
+ sizeof(*(table)->entries) * (_entries_size)); \
+ (table)->entries_size = (_entries_size); \
+ for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
+ (table)->entries[var].field.capacity = \
+ HASHTABLE_INITIAL_ENTRIES_CAPACITY; \
+ (table)->entries[var].field.size = 0; \
+ (table)->entries[var].field.values = (type *)malloc(\
+ sizeof(type) * \
+ HASHTABLE_INITIAL_ENTRIES_CAPACITY); \
+ assert((table)->entries[var].field.values != NULL);\
+ } \
+ } while (0)
+
+/*
+ * All initialized hashtables should be destroyed with this macro.
+ */
+#define HASHTABLE_DESTROY(table, field) \
+ do { \
+ hashtable_index_t var; \
+ for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
+ free((table)->entries[var].field.values); \
+ } \
+ } while (0)
+
+#define HASHTABLE_GET_ENTRY(table, hash) (&((table)->entries[hash]))
+
+/*
+ * Traverses through all hash table entries
+ */
+#define HASHTABLE_FOREACH(table, var) \
+ for ((var) = &((table)->entries[0]); \
+ (var) < &((table)->entries[HASHTABLE_ENTRIES_COUNT(table)]);\
+ ++(var))
+
+/*
+ * Traverses through all elements of the specified hash table entry
+ */
+#define HASHTABLE_ENTRY_FOREACH(entry, field, var) \
+ for ((var) = &((entry)->field.values[0]); \
+ (var) < &((entry)->field.values[(entry)->field.size]); \
+ ++(var))
+
+#define HASHTABLE_ENTRY_CLEAR(entry, field) \
+ ((entry)->field.size = 0)
+
+#define HASHTABLE_ENTRY_SIZE(entry, field) \
+ ((entry)->field.size)
+
+#define HASHTABLE_ENTRY_CAPACITY(entry, field) \
+ ((entry)->field.capacity)
+
+#define HASHTABLE_ENTRY_CAPACITY_INCREASE(entry, field, type) \
+ (entry)->field.capacity *= 2; \
+ (entry)->field.values = (type *)realloc((entry)->field.values, \
+ (entry)->field.capacity * sizeof(type));
+
+#define HASHTABLE_ENTRY_CAPACITY_DECREASE(entry, field, type) \
+ (entry)->field.capacity /= 2; \
+ (entry)->field.values = (type *)realloc((entry)->field.values, \
+ (entry)->field.capacity * sizeof(type));
+
+/*
+ * Generates prototypes for the hash table functions
+ */
+#define HASHTABLE_PROTOTYPE(name, entry_, type) \
+hashtable_index_t name##_CALCULATE_HASH(struct name *, type *); \
+void name##_ENTRY_STORE(struct entry_*, type *); \
+type *name##_ENTRY_FIND(struct entry_*, type *); \
+type *name##_ENTRY_FIND_SPECIAL(struct entry_ *, type *, \
+ int (*) (const void *, const void *)); \
+void name##_ENTRY_REMOVE(struct entry_*, type *);
+
+/*
+ * Generates implementations of the hash table functions
+ */
+#define HASHTABLE_GENERATE(name, entry_, type, field, HASH, CMP) \
+hashtable_index_t name##_CALCULATE_HASH(struct name *table, type *data) \
+{ \
+ \
+ return HASH(data, table->entries_size); \
+} \
+ \
+void name##_ENTRY_STORE(struct entry_ *the_entry, type *data) \
+{ \
+ \
+ if (the_entry->field.size == the_entry->field.capacity) \
+ HASHTABLE_ENTRY_CAPACITY_INCREASE(the_entry, field, type);\
+ \
+ memcpy(&(the_entry->field.values[the_entry->field.size++]), \
+ data, \
+ sizeof(type)); \
+ qsort(the_entry->field.values, the_entry->field.size, \
+ sizeof(type), CMP); \
+} \
+ \
+type *name##_ENTRY_FIND(struct entry_ *the_entry, type *key) \
+{ \
+ \
+ return ((type *)bsearch(key, the_entry->field.values, \
+ the_entry->field.size, sizeof(type), CMP)); \
+} \
+ \
+type *name##_ENTRY_FIND_SPECIAL(struct entry_ *the_entry, type *key, \
+ int (*compar) (const void *, const void *)) \
+{ \
+ return ((type *)bsearch(key, the_entry->field.values, \
+ the_entry->field.size, sizeof(type), compar)); \
+} \
+ \
+void name##_ENTRY_REMOVE(struct entry_ *the_entry, type *del_elm) \
+{ \
+ \
+ memmove(del_elm, del_elm + 1, \
+ (&the_entry->field.values[--the_entry->field.size] - del_elm) *\
+ sizeof(type)); \
+}
+
+/*
+ * Macro definitions below wrap the functions, generaed with
+ * HASHTABLE_GENERATE macro. You should use them and avoid using generated
+ * functions directly.
+ */
+#define HASHTABLE_CALCULATE_HASH(name, table, data) \
+ (name##_CALCULATE_HASH((table), data))
+
+#define HASHTABLE_ENTRY_STORE(name, entry, data) \
+ name##_ENTRY_STORE((entry), data)
+
+#define HASHTABLE_ENTRY_FIND(name, entry, key) \
+ (name##_ENTRY_FIND((entry), (key)))
+
+#define HASHTABLE_ENTRY_FIND_SPECIAL(name, entry, key, cmp) \
+ (name##_ENTRY_FIND_SPECIAL((entry), (key), (cmp)))
+
+#define HASHTABLE_ENTRY_REMOVE(name, entry, del_elm) \
+ name##_ENTRY_REMOVE((entry), (del_elm))
+
+#endif
diff --git a/usr.sbin/cached/log.c b/usr.sbin/cached/log.c
new file mode 100644
index 0000000..053930a
--- /dev/null
+++ b/usr.sbin/cached/log.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "log.h"
+
+void
+__log_msg(int level, const char *sender, const char *message, ...)
+{
+ va_list ap;
+ char *fmessage;
+
+ fmessage = NULL;
+ va_start(ap, message);
+ vasprintf(&fmessage, message, ap);
+ va_end(ap);
+ assert(fmessage != NULL);
+
+ printf("M%d from %s: %s\n", level, sender, fmessage);
+#ifndef NO_SYSLOG
+ if (level == 0)
+ syslog(LOG_INFO, "cached message (from %s): %s", sender,
+ fmessage);
+#endif
+ free(fmessage);
+}
+
+void
+__log_err(int level, const char *sender, const char *error, ...)
+{
+ va_list ap;
+ char *ferror;
+
+ ferror = NULL;
+ va_start(ap, error);
+ vasprintf(&ferror, error, ap);
+ va_end(ap);
+ assert(ferror != NULL);
+
+ printf("E%d from %s: %s\n", level, sender, ferror);
+
+#ifndef NO_SYSLOG
+ if (level == 0)
+ syslog(LOG_ERR, "cached error (from %s): %s", sender, ferror);
+#endif
+ free(ferror);
+}
diff --git a/usr.sbin/cached/log.h b/usr.sbin/cached/log.h
new file mode 100644
index 0000000..8d665a4
--- /dev/null
+++ b/usr.sbin/cached/log.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_LOG_H__
+#define __CACHED_LOG_H__
+
+#define LOG_MSG_1(sender, msg, ...) __log_msg(1, sender, msg, ##__VA_ARGS__)
+#define LOG_MSG_2(sender, msg, ...) __log_msg(2, sender, msg, ##__VA_ARGS__)
+#define LOG_MSG_3(sender, msg, ...) __log_msg(3, sedner, msg, ##__VA_ARGS__)
+
+#define LOG_ERR_1(sender, err, ...) __log_err(1, sender, err, ##__VA_ARGS__)
+#define LOG_ERR_2(sender, err, ...) __log_err(2, sender, err, ##__VA_ARGS__)
+#define LOG_ERR_3(sender, err, ...) __log_err(3, sender, err, ##__VA_ARGS__)
+
+extern void __log_msg(int, const char *, const char *, ...);
+extern void __log_err(int, const char *, const char *, ...);
+
+#endif
diff --git a/usr.sbin/cached/mp_rs_query.c b/usr.sbin/cached/mp_rs_query.c
new file mode 100644
index 0000000..ae0f3bb
--- /dev/null
+++ b/usr.sbin/cached/mp_rs_query.c
@@ -0,0 +1,537 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_rs_query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_read_session_close_notification(struct query_state *);
+static void on_mp_read_session_destroy(struct query_state *);
+static int on_mp_read_session_mapper(struct query_state *);
+/* int on_mp_read_session_request_read1(struct query_state *); */
+static int on_mp_read_session_request_read2(struct query_state *);
+static int on_mp_read_session_request_process(struct query_state *);
+static int on_mp_read_session_response_write1(struct query_state *);
+static int on_mp_read_session_read_request_process(struct query_state *);
+static int on_mp_read_session_read_response_write1(struct query_state *);
+static int on_mp_read_session_read_response_write2(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_read_session_destroy(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_read_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_read_session(
+ (cache_mp_read_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_read_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart read session initiation
+ * requests.
+ * - on_mp_read_session_request_read1 and on_mp_read_session_request_read2 read
+ * the request itself
+ * - on_mp_read_session_request_process processes it
+ * - on_mp_read_session_response_write1 sends the response
+ */
+int
+on_mp_read_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_READ_SESSION_REQUEST);
+ c_mp_rs_request = get_cache_mp_read_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_rs_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_rs_request->entry_length)) {
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_rs_request->entry = (char *)malloc(
+ c_mp_rs_request->entry_length + 1);
+ assert(c_mp_rs_request->entry != NULL);
+ memset(c_mp_rs_request->entry, 0,
+ c_mp_rs_request->entry_length + 1);
+
+ qstate->kevent_watermark = c_mp_rs_request->entry_length;
+ qstate->process_func = on_mp_read_session_request_read2;
+ }
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_read_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_request_read2);
+ c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_rs_request->entry,
+ c_mp_rs_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_read_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_read_session_request_process;
+ TRACE_OUT(on_mp_read_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_read_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ struct cache_mp_read_session_response *c_mp_rs_response;
+ cache_mp_read_session rs;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ char *buffer;
+ size_t buffer_size;
+ cache_mp_write_session ws;
+ struct agent *lookup_agent;
+ struct multipart_agent *mp_agent;
+ void *mdata;
+ int res;
+
+ TRACE_IN(on_mp_read_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_READ_SESSION_RESPONSE);
+ c_mp_rs_response = get_cache_mp_read_session_response(
+ &qstate->response);
+ c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_rs_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_rs_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_session_request",
+ "can't find configuration entry '%s'."
+ " aborting request", c_mp_rs_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_rs_response->error_code = EACCES;
+
+ LOG_ERR_2("read_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_rs_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ dec_cache_entry_name = strdup(
+ qstate->config_entry->mp_cache_params.entry_name);
+ else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_rs_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.entry_name);
+ }
+
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache, dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if ((c_entry == INVALID_CACHE) &&
+ (qstate->config_entry->perform_actual_lookups != 0))
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ if (c_entry != INVALID_CACHE_ENTRY) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ rs = open_cache_mp_read_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ if ((rs == INVALID_CACHE_MP_READ_SESSION) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ lookup_agent = find_agent(s_agent_table,
+ c_mp_rs_request->entry, MULTIPART_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == MULTIPART_AGENT)) {
+ mp_agent = (struct multipart_agent *)
+ lookup_agent;
+ mdata = mp_agent->mp_init_func();
+
+ /*
+ * Multipart agents read the whole snapshot
+ * of the data at one time.
+ */
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ if (ws != NULL) {
+ do {
+ buffer = NULL;
+ res = mp_agent->mp_lookup_func(&buffer,
+ &buffer_size,
+ mdata);
+
+ if ((res & NS_TERMINATE) &&
+ (buffer != NULL)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+ if (cache_mp_write(ws, buffer,
+ buffer_size) != 0) {
+ abandon_cache_mp_write_session(ws);
+ ws = NULL;
+ }
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+
+ free(buffer);
+ buffer = NULL;
+ } else {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+ close_cache_mp_write_session(ws);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+
+ free(buffer);
+ buffer = NULL;
+ }
+ } while ((res & NS_TERMINATE) &&
+ (ws != NULL));
+ }
+
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ rs = open_cache_mp_read_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ }
+
+ if (rs == INVALID_CACHE_MP_READ_SESSION)
+ c_mp_rs_response->error_code = -1;
+ else {
+ qstate->mdata = rs;
+ qstate->destroy_func = on_mp_read_session_destroy;
+
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ } else
+ c_mp_rs_response->error_code = -1;
+
+fin:
+ qstate->process_func = on_mp_read_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_read_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_read_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_response *c_mp_rs_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_response_write1);
+ c_mp_rs_response = get_cache_mp_read_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_rs_response->error_code,
+ sizeof(int));
+
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_read_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_rs_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_read_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_read_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_read_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_read_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_READ_SESSION_READ_REQUEST:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_read_session_read_request_process;
+ break;
+ case CET_MP_READ_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_read_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_3("on_mp_read_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart read sessions read
+ * requests. User doesn't have to pass any kind of data, besides the
+ * request identificator itself. So we don't need any XXX_read functions and
+ * start with the XXX_process function.
+ * - on_mp_read_session_read_request_process processes it
+ * - on_mp_read_session_read_response_write1 and
+ * on_mp_read_session_read_response_write2 sends the response
+ */
+static int
+on_mp_read_session_read_request_process(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+
+ TRACE_IN(on_mp_read_session_response_process);
+ init_comm_element(&qstate->response, CET_MP_READ_SESSION_READ_RESPONSE);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ read_response->error_code = cache_mp_read(
+ (cache_mp_read_session)qstate->mdata, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == 0) {
+ read_response->data = (char *)malloc(read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_mp_read(
+ (cache_mp_read_session)qstate->mdata,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_read_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_read_session_response_process);
+ return (0);
+}
+
+static int
+on_mp_read_session_read_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_read_response_write1);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ LOG_ERR_3("on_mp_read_session_read_response_write1",
+ "write failed");
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_mp_read_session_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_read_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ return (0);
+}
+
+static int
+on_mp_read_session_read_response_write2(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_read_response_write2);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_read_response_write2",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_read_response_write2);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+
+ TRACE_OUT(on_mp_read_session_read_response_write2);
+ return (0);
+}
+
+/*
+ * Handles session close notification by calling close_cache_mp_read_session
+ * function.
+ */
+static int
+on_mp_read_session_close_notification(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_read_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_read_session((cache_mp_read_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = NULL;
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_read_session_close_notification);
+ return (0);
+}
diff --git a/usr.sbin/cached/mp_rs_query.h b/usr.sbin/cached/mp_rs_query.h
new file mode 100644
index 0000000..f468afa
--- /dev/null
+++ b/usr.sbin/cached/mp_rs_query.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_MP_RS_QUERY_H__
+#define __CACHED_MP_RS_QUERY_H__
+
+extern int on_mp_read_session_request_read1(struct query_state *);
+
+#endif
diff --git a/usr.sbin/cached/mp_ws_query.c b/usr.sbin/cached/mp_ws_query.c
new file mode 100644
index 0000000..d7aeb49
--- /dev/null
+++ b/usr.sbin/cached/mp_ws_query.c
@@ -0,0 +1,548 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_write_session_abandon_notification(struct query_state *);
+static int on_mp_write_session_close_notification(struct query_state *);
+static void on_mp_write_session_destroy(struct query_state *);
+static int on_mp_write_session_mapper(struct query_state *);
+/* int on_mp_write_session_request_read1(struct query_state *); */
+static int on_mp_write_session_request_read2(struct query_state *);
+static int on_mp_write_session_request_process(struct query_state *);
+static int on_mp_write_session_response_write1(struct query_state *);
+static int on_mp_write_session_write_request_read1(struct query_state *);
+static int on_mp_write_session_write_request_read2(struct query_state *);
+static int on_mp_write_session_write_request_process(struct query_state *);
+static int on_mp_write_session_write_response_write1(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_write_session_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_write_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session(
+ (cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_write_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart write session initiation
+ * requests.
+ * - on_mp_write_session_request_read1 and on_mp_write_session_request_read2
+ * read the request itself
+ * - on_mp_write_session_request_process processes it
+ * - on_mp_write_session_response_write1 sends the response
+ */
+int
+on_mp_write_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_REQUEST);
+ c_mp_ws_request = get_cache_mp_write_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_ws_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_ws_request->entry_length)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "invalid entry_length value");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_ws_request->entry = (char *)malloc(
+ c_mp_ws_request->entry_length + 1);
+ assert(c_mp_ws_request->entry != NULL);
+ memset(c_mp_ws_request->entry, 0,
+ c_mp_ws_request->entry_length + 1);
+
+ qstate->kevent_watermark = c_mp_ws_request->entry_length;
+ qstate->process_func = on_mp_write_session_request_read2;
+ }
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read2);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_ws_request->entry,
+ c_mp_ws_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_request_process;
+
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ cache_mp_write_session ws;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ TRACE_IN(on_mp_write_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_WRITE_SESSION_RESPONSE);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_ws_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_ws_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_session_request",
+ "can't find configuration entry '%s'. "
+ "aborting request", c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_ws_response->error_code = EACCES;
+
+ LOG_ERR_2("write_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ c_mp_ws_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_session_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", c_mp_ws_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_ws_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ /*
+ * All multipart entries are separated by their name decorations.
+ * For one configuration entry there will be a lot of multipart
+ * cache entries - each with its own decorated name.
+ */
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.entry_name);
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if (c_entry == INVALID_CACHE_ENTRY)
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ assert(c_entry != NULL);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ if (ws == INVALID_CACHE_MP_WRITE_SESSION)
+ c_mp_ws_response->error_code = -1;
+ else {
+ qstate->mdata = ws;
+ qstate->destroy_func = on_mp_write_session_destroy;
+
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+fin:
+ qstate->process_func = on_mp_write_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_response_write1);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_ws_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_ws_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_write_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_write_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func =
+ on_mp_write_session_write_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_abandon_notification;
+ break;
+ case CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_2("on_mp_write_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart write sessions write
+ * requests.
+ * - on_mp_write_session_write_request_read1 and
+ * on_mp_write_session_write_request_read2 read the request itself
+ * - on_mp_write_session_write_request_process processes it
+ * - on_mp_write_session_write_response_write1 sends the response
+ */
+static int
+on_mp_write_session_write_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read1);
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->data_size,
+ sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->data_size)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "invalid data_size value");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ write_request->data = (char *)malloc(write_request->data_size);
+ assert(write_request->data != NULL);
+ memset(write_request->data, 0, write_request->data_size);
+
+ qstate->kevent_watermark = write_request->data_size;
+ qstate->process_func = on_mp_write_session_write_request_read2;
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read2);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_write_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_write_request_process;
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ struct cache_mp_write_session_write_response *write_response;
+
+ TRACE_IN(on_mp_write_session_write_request_process);
+ init_comm_element(&qstate->response,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ write_response->error_code = cache_mp_write(
+ (cache_mp_write_session)qstate->mdata,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_write_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_write_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_response_write1);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_write_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (-1);
+ }
+
+ if (write_response->error_code == 0) {
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = 0;
+ }
+
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (0);
+}
+
+/*
+ * Handles abandon notifications. Destroys the session by calling the
+ * abandon_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_abandon_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_abandon_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_abandon_notification);
+ return (0);
+}
+
+/*
+ * Handles close notifications. Commits the session by calling
+ * the close_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_close_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_close_notification);
+ return (0);
+}
+
+cache_entry register_new_mp_cache_entry(struct query_state *qstate,
+ const char *dec_cache_entry_name)
+{
+ cache_entry c_entry;
+ char *en_bkp;
+
+ TRACE_IN(register_new_mp_cache_entry);
+ c_entry = INVALID_CACHE_ENTRY;
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ configuration_lock_wrlock(s_configuration);
+ en_bkp = qstate->config_entry->mp_cache_params.entry_name;
+ qstate->config_entry->mp_cache_params.entry_name =
+ (char *)dec_cache_entry_name;
+ register_cache_entry(s_cache, (struct cache_entry_params *)
+ &qstate->config_entry->mp_cache_params);
+ qstate->config_entry->mp_cache_params.entry_name = en_bkp;
+ configuration_unlock(s_configuration);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ configuration_entry_add_mp_cache_entry(qstate->config_entry,
+ c_entry);
+
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ TRACE_OUT(register_new_mp_cache_entry);
+ return (c_entry);
+}
diff --git a/usr.sbin/cached/mp_ws_query.h b/usr.sbin/cached/mp_ws_query.h
new file mode 100644
index 0000000..ba77665
--- /dev/null
+++ b/usr.sbin/cached/mp_ws_query.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_MP_WS_QUERY_H__
+#define __CACHED_MP_WS_QUERY_H__
+
+extern int on_mp_write_session_request_read1(struct query_state *);
+extern cache_entry register_new_mp_cache_entry(struct query_state *,
+ const char *);
+
+#endif
diff --git a/usr.sbin/cached/parser.c b/usr.sbin/cached/parser.c
new file mode 100644
index 0000000..b877efa
--- /dev/null
+++ b/usr.sbin/cached/parser.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "parser.h"
+
+static void enable_cache(struct configuration *,const char *, int);
+static struct configuration_entry *find_create_entry(struct configuration *,
+ const char *);
+static int get_number(const char *, int, int);
+static enum cache_policy_t get_policy(const char *);
+static int get_yesno(const char *);
+static int check_cachename(const char *);
+static void check_files(struct configuration *, const char *, int);
+static void set_keep_hot_count(struct configuration *, const char *, int);
+static void set_negative_policy(struct configuration *, const char *,
+ enum cache_policy_t);
+static void set_negative_time_to_live(struct configuration *,
+ const char *, int);
+static void set_positive_policy(struct configuration *, const char *,
+ enum cache_policy_t);
+static void set_perform_actual_lookups(struct configuration *, const char *,
+ int);
+static void set_positive_time_to_live(struct configuration *,
+ const char *, int);
+static void set_suggested_size(struct configuration *, const char *,
+ int size);
+static void set_threads_num(struct configuration *, int);
+static int strbreak(char *, char **, int);
+
+static int
+strbreak(char *str, char **fields, int fields_size)
+{
+ char *c = str;
+ int i, num;
+
+ TRACE_IN(strbreak);
+ num = 0;
+ for (i = 0;
+ ((*fields =
+ strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL);
+ ++i)
+ if ((*(*fields)) != '\0') {
+ ++fields;
+ ++num;
+ }
+
+ TRACE_OUT(strbreak);
+ return (num);
+}
+
+/*
+ * Tries to find the configuration entry with the specified name. If search
+ * fails, the new entry with the default parameters will be created.
+ */
+static struct configuration_entry *
+find_create_entry(struct configuration *config,
+ const char *entry_name)
+{
+ struct configuration_entry *entry = NULL;
+ int res;
+
+ TRACE_IN(find_create_entry);
+ entry = configuration_find_entry(config, entry_name);
+ if (entry == NULL) {
+ entry = create_def_configuration_entry(entry_name);
+ assert( entry != NULL);
+ res = add_configuration_entry(config, entry);
+ assert(res == 0);
+ }
+
+ TRACE_OUT(find_create_entry);
+ return (entry);
+}
+
+/*
+ * The vast majority of the functions below corresponds to the particular
+ * keywords in the configuration file.
+ */
+static void
+enable_cache(struct configuration *config, const char *entry_name, int flag)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(enable_cache);
+ entry = find_create_entry(config, entry_name);
+ entry->enabled = flag;
+ TRACE_OUT(enable_cache);
+}
+
+static void
+set_positive_time_to_live(struct configuration *config,
+ const char *entry_name, int ttl)
+{
+ struct configuration_entry *entry;
+ struct timeval lifetime;
+
+ TRACE_IN(set_positive_time_to_live);
+ assert(ttl >= 0);
+ assert(entry_name != NULL);
+ memset(&lifetime, 0, sizeof(struct timeval));
+ lifetime.tv_sec = ttl;
+
+ entry = find_create_entry(config, entry_name);
+ memcpy(&entry->positive_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+ memcpy(&entry->mp_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+
+ TRACE_OUT(set_positive_time_to_live);
+}
+
+static void
+set_negative_time_to_live(struct configuration *config,
+ const char *entry_name, int nttl)
+{
+ struct configuration_entry *entry;
+ struct timeval lifetime;
+
+ TRACE_IN(set_negative_time_to_live);
+ assert(nttl > 0);
+ assert(entry_name != NULL);
+ memset(&lifetime, 0, sizeof(struct timeval));
+ lifetime.tv_sec = nttl;
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ memcpy(&entry->negative_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+
+ TRACE_OUT(set_negative_time_to_live);
+}
+
+/*
+ * Hot count is actually the elements size limit.
+ */
+static void
+set_keep_hot_count(struct configuration *config,
+ const char *entry_name, int count)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_keep_hot_count);
+ assert(count >= 0);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.max_elemsize = count;
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->negative_cache_params.max_elemsize = count;
+
+ TRACE_OUT(set_keep_hot_count);
+}
+
+static void
+set_positive_policy(struct configuration *config,
+ const char *entry_name, enum cache_policy_t policy)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_positive_policy);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.policy = policy;
+
+ TRACE_OUT(set_positive_policy);
+}
+
+static void
+set_negative_policy(struct configuration *config,
+ const char *entry_name, enum cache_policy_t policy)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_negative_policy);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->negative_cache_params.policy = policy;
+
+ TRACE_OUT(set_negative_policy);
+}
+
+static void
+set_perform_actual_lookups(struct configuration *config,
+ const char *entry_name, int flag)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_perform_actual_lookups);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->perform_actual_lookups = flag;
+
+ TRACE_OUT(set_perform_actual_lookups);
+}
+
+static void
+set_suggested_size(struct configuration *config,
+ const char *entry_name, int size)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_suggested_size);
+ assert(config != NULL);
+ assert(entry_name != NULL);
+ assert(size > 0);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.cache_entries_size = size;
+ entry->negative_cache_params.cache_entries_size = size;
+
+ TRACE_OUT(set_suggested_size);
+}
+
+static void
+check_files(struct configuration *config, const char *entry_name, int flag)
+{
+
+ TRACE_IN(check_files);
+ assert(entry_name != NULL);
+ TRACE_OUT(check_files);
+}
+
+static int
+get_yesno(const char *str)
+{
+
+ if (strcmp(str, "yes") == 0)
+ return (1);
+ else if (strcmp(str, "no") == 0)
+ return (0);
+ else
+ return (-1);
+}
+
+static int
+get_number(const char *str, int low, int max)
+{
+
+ char *end = NULL;
+ int res = 0;
+
+ if (str[0] == '\0')
+ return (-1);
+
+ res = strtol(str, &end, 10);
+ if (*end != '\0')
+ return (-1);
+ else
+ if (((res >= low) || (low == -1)) &&
+ ((res <= max) || (max == -1)))
+ return (res);
+ else
+ return (-2);
+}
+
+static enum cache_policy_t
+get_policy(const char *str)
+{
+
+ if (strcmp(str, "fifo") == 0)
+ return (CPT_FIFO);
+ else if (strcmp(str, "lru") == 0)
+ return (CPT_LRU);
+ else if (strcmp(str, "lfu") == 0)
+ return (CPT_LFU);
+
+ return (-1);
+}
+
+static int
+check_cachename(const char *str)
+{
+
+ assert(str != NULL);
+ return ((strlen(str) > 0) ? 0 : -1);
+}
+
+static void
+set_threads_num(struct configuration *config, int value)
+{
+
+ assert(config != NULL);
+ config->threads_num = value;
+}
+
+/*
+ * The main configuration routine. Its implementation is hugely inspired by the
+ * the same routine implementation in Solaris NSCD.
+ */
+int
+parse_config_file(struct configuration *config,
+ const char *fname, char const **error_str, int *error_line)
+{
+ FILE *fin;
+ char buffer[255];
+ char *fields[128];
+ int field_count, line_num, value;
+ int res;
+
+ TRACE_IN(parse_config_file);
+ assert(config != NULL);
+ assert(fname != NULL);
+
+ fin = fopen(fname, "r");
+ if (fin == NULL) {
+ TRACE_OUT(parse_config_file);
+ return (-1);
+ }
+
+ res = 0;
+ line_num = 0;
+ memset(buffer, 0, sizeof(buffer));
+ while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
+ field_count = strbreak(buffer, fields, sizeof(fields));
+ ++line_num;
+
+ if (field_count == 0)
+ continue;
+
+ switch (fields[0][0]) {
+ case '#':
+ case '\0':
+ continue;
+ case 'e':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "enable-cache") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ enable_cache(config, fields[1], value);
+ continue;
+ }
+ break;
+ case 'd':
+ if ((field_count == 2) &&
+ (strcmp(fields[0], "debug-level") == 0) &&
+ ((value = get_number(fields[1], 0, 10)) != -1)) {
+ continue;
+ }
+ break;
+ case 'p':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "positive-time-to-live") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_positive_time_to_live(config,
+ fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "positive-policy") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_policy(fields[2])) != -1)) {
+ set_positive_policy(config, fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "perform-actual-lookups") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ set_perform_actual_lookups(config, fields[1],
+ value);
+ continue;
+ }
+ break;
+ case 'n':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "negative-time-to-live") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_negative_time_to_live(config,
+ fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "negative-policy") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_policy(fields[2])) != -1)) {
+ set_negative_policy(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ case 's':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "suggested-size") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 1, -1)) != -1)) {
+ set_suggested_size(config, fields[1], value);
+ continue;
+ }
+ break;
+ case 't':
+ if ((field_count == 2) &&
+ (strcmp(fields[0], "threads") == 0) &&
+ ((value = get_number(fields[1], 1, -1)) != -1)) {
+ set_threads_num(config, value);
+ continue;
+ }
+ break;
+ case 'k':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "keep-hot-count") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_keep_hot_count(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ case 'c':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "check-files") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ check_files(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ LOG_ERR_2("config file parser", "error in file "
+ "%s on line %d", fname, line_num);
+ *error_str = "syntax error";
+ *error_line = line_num;
+ res = -1;
+ }
+ fclose(fin);
+
+ TRACE_OUT(parse_config_file);
+ return (res);
+}
diff --git a/usr.sbin/cached/parser.h b/usr.sbin/cached/parser.h
new file mode 100644
index 0000000..54cc898
--- /dev/null
+++ b/usr.sbin/cached/parser.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_PARSER_H__
+#define __CACHED_PARSER_H__
+
+extern int parse_config_file(struct configuration *,
+ const char *, char const **, int *);
+
+#endif
diff --git a/usr.sbin/cached/protocol.c b/usr.sbin/cached/protocol.c
new file mode 100644
index 0000000..08cea92
--- /dev/null
+++ b/usr.sbin/cached/protocol.c
@@ -0,0 +1,550 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "debug.h"
+#include "log.h"
+#include "protocol.h"
+
+/*
+ * Initializes the comm_element with any given type of data
+ */
+void
+init_comm_element(struct comm_element *element, enum comm_element_t type)
+{
+
+ TRACE_IN(init_comm_element);
+ memset(element, 0, sizeof(struct comm_element));
+
+ switch (type) {
+ case CET_WRITE_REQUEST:
+ init_cache_write_request(&element->c_write_request);
+ break;
+ case CET_WRITE_RESPONSE:
+ init_cache_write_response(&element->c_write_response);
+ break;
+ case CET_READ_REQUEST:
+ init_cache_read_request(&element->c_read_request);
+ break;
+ case CET_READ_RESPONSE:
+ init_cache_read_response(&element->c_read_response);
+ break;
+ case CET_TRANSFORM_REQUEST:
+ init_cache_transform_request(&element->c_transform_request);
+ break;
+ case CET_TRANSFORM_RESPONSE:
+ init_cache_transform_response(&element->c_transform_response);
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ init_cache_mp_write_session_request(&element->c_mp_ws_request);
+ break;
+ case CET_MP_WRITE_SESSION_RESPONSE:
+ init_cache_mp_write_session_response(&element->c_mp_ws_response);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ init_cache_mp_write_session_write_request(
+ &element->c_mp_ws_write_request);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_RESPONSE:
+ init_cache_mp_write_session_write_response(
+ &element->c_mp_ws_write_response);
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ init_cache_mp_read_session_request(&element->c_mp_rs_request);
+ break;
+ case CET_MP_READ_SESSION_RESPONSE:
+ init_cache_mp_read_session_response(&element->c_mp_rs_response);
+ break;
+ case CET_MP_READ_SESSION_READ_RESPONSE:
+ init_cache_mp_read_session_read_response(
+ &element->c_mp_rs_read_response);
+ break;
+ case CET_UNDEFINED:
+ break;
+ default:
+ LOG_ERR_2("init_comm_element", "invalid communication element");
+ TRACE_OUT(init_comm_element);
+ return;
+ }
+
+ element->type = type;
+ TRACE_OUT(init_comm_element);
+}
+
+void
+finalize_comm_element(struct comm_element *element)
+{
+
+ TRACE_IN(finalize_comm_element);
+ switch (element->type) {
+ case CET_WRITE_REQUEST:
+ finalize_cache_write_request(&element->c_write_request);
+ break;
+ case CET_WRITE_RESPONSE:
+ finalize_cache_write_response(&element->c_write_response);
+ break;
+ case CET_READ_REQUEST:
+ finalize_cache_read_request(&element->c_read_request);
+ break;
+ case CET_READ_RESPONSE:
+ finalize_cache_read_response(&element->c_read_response);
+ break;
+ case CET_TRANSFORM_REQUEST:
+ finalize_cache_transform_request(&element->c_transform_request);
+ break;
+ case CET_TRANSFORM_RESPONSE:
+ finalize_cache_transform_response(
+ &element->c_transform_response);
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ finalize_cache_mp_write_session_request(
+ &element->c_mp_ws_request);
+ break;
+ case CET_MP_WRITE_SESSION_RESPONSE:
+ finalize_cache_mp_write_session_response(
+ &element->c_mp_ws_response);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ finalize_cache_mp_write_session_write_request(
+ &element->c_mp_ws_write_request);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_RESPONSE:
+ finalize_cache_mp_write_session_write_response(
+ &element->c_mp_ws_write_response);
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ finalize_cache_mp_read_session_request(
+ &element->c_mp_rs_request);
+ break;
+ case CET_MP_READ_SESSION_RESPONSE:
+ finalize_cache_mp_read_session_response(
+ &element->c_mp_rs_response);
+ break;
+ case CET_MP_READ_SESSION_READ_RESPONSE:
+ finalize_cache_mp_read_session_read_response(
+ &element->c_mp_rs_read_response);
+ break;
+ case CET_UNDEFINED:
+ break;
+ default:
+ break;
+ }
+
+ element->type = CET_UNDEFINED;
+ TRACE_OUT(finalize_comm_element);
+}
+
+void
+init_cache_write_request(struct cache_write_request *write_request)
+{
+
+ TRACE_IN(init_cache_write_request);
+ memset(write_request, 0, sizeof(struct cache_write_request));
+ TRACE_OUT(init_cache_write_request);
+}
+
+void
+finalize_cache_write_request(struct cache_write_request *write_request)
+{
+
+ TRACE_IN(finalize_cache_write_request);
+ free(write_request->entry);
+ free(write_request->cache_key);
+ free(write_request->data);
+ TRACE_OUT(finalize_cache_write_request);
+}
+
+struct cache_write_request *
+get_cache_write_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_write_request);
+ assert(element->type == CET_WRITE_REQUEST);
+ TRACE_OUT(get_cache_write_request);
+ return (&element->c_write_request);
+}
+
+void
+init_cache_write_response(struct cache_write_response *write_response)
+{
+
+ TRACE_IN(init_cache_write_response);
+ memset(write_response, 0, sizeof(struct cache_write_response));
+ TRACE_OUT(init_cache_write_response);
+}
+
+void
+finalize_cache_write_response(struct cache_write_response *write_response)
+{
+
+ TRACE_IN(finalize_cache_write_response);
+ TRACE_OUT(finalize_cache_write_response);
+}
+
+struct cache_write_response *
+get_cache_write_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_write_response);
+ assert(element->type == CET_WRITE_RESPONSE);
+ TRACE_OUT(get_cache_write_response);
+ return (&element->c_write_response);
+}
+
+void
+init_cache_read_request(struct cache_read_request *read_request)
+{
+
+ TRACE_IN(init_cache_read_request);
+ memset(read_request, 0, sizeof(struct cache_read_request));
+ TRACE_OUT(init_cache_read_request);
+}
+
+void
+finalize_cache_read_request(struct cache_read_request *read_request)
+{
+
+ TRACE_IN(finalize_cache_read_request);
+ free(read_request->entry);
+ free(read_request->cache_key);
+ TRACE_OUT(finalize_cache_read_request);
+}
+
+struct cache_read_request *
+get_cache_read_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_read_request);
+ assert(element->type == CET_READ_REQUEST);
+ TRACE_OUT(get_cache_read_request);
+ return (&element->c_read_request);
+}
+
+void
+init_cache_read_response(struct cache_read_response *read_response)
+{
+
+ TRACE_IN(init_cache_read_response);
+ memset(read_response, 0, sizeof(struct cache_read_response));
+ TRACE_OUT(init_cache_read_response);
+}
+
+void
+finalize_cache_read_response(struct cache_read_response *read_response)
+{
+
+ TRACE_IN(finalize_cache_read_response);
+ free(read_response->data);
+ TRACE_OUT(finalize_cache_read_response);
+}
+
+struct cache_read_response *
+get_cache_read_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_read_response);
+ assert(element->type == CET_READ_RESPONSE);
+ TRACE_OUT(get_cache_read_response);
+ return (&element->c_read_response);
+}
+
+void
+init_cache_transform_request(struct cache_transform_request *transform_request)
+{
+
+ TRACE_IN(init_cache_transform_request);
+ memset(transform_request, 0, sizeof(struct cache_transform_request));
+ TRACE_OUT(init_cache_transform_request);
+}
+
+void
+finalize_cache_transform_request(
+ struct cache_transform_request *transform_request)
+{
+
+ TRACE_IN(finalize_cache_transform_request);
+ free(transform_request->entry);
+ TRACE_OUT(finalize_cache_transform_request);
+}
+
+struct cache_transform_request *
+get_cache_transform_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_transform_request);
+ assert(element->type == CET_TRANSFORM_REQUEST);
+ TRACE_OUT(get_cache_transform_request);
+ return (&element->c_transform_request);
+}
+
+void
+init_cache_transform_response(
+ struct cache_transform_response *transform_response)
+{
+
+ TRACE_IN(init_cache_transform_request);
+ memset(transform_response, 0, sizeof(struct cache_transform_response));
+ TRACE_OUT(init_cache_transform_request);
+}
+
+void
+finalize_cache_transform_response(
+ struct cache_transform_response *transform_response)
+{
+
+ TRACE_IN(finalize_cache_transform_response);
+ TRACE_OUT(finalize_cache_transform_response);
+}
+
+struct cache_transform_response *
+get_cache_transform_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_transform_response);
+ assert(element->type == CET_TRANSFORM_RESPONSE);
+ TRACE_OUT(get_cache_transform_response);
+ return (&element->c_transform_response);
+}
+
+
+void
+init_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *mp_ws_request)
+{
+
+ TRACE_IN(init_cache_mp_write_session_request);
+ memset(mp_ws_request, 0,
+ sizeof(struct cache_mp_write_session_request));
+ TRACE_OUT(init_cache_mp_write_session_request);
+}
+
+void
+finalize_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *mp_ws_request)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_request);
+ free(mp_ws_request->entry);
+ TRACE_OUT(finalize_cache_mp_write_session_request);
+}
+
+struct cache_mp_write_session_request *
+get_cache_mp_write_session_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_request);
+ assert(element->type == CET_MP_WRITE_SESSION_REQUEST);
+ TRACE_OUT(get_cache_mp_write_session_request);
+ return (&element->c_mp_ws_request);
+}
+
+void
+init_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *mp_ws_response)
+{
+
+ TRACE_IN(init_cache_mp_write_session_response);
+ memset(mp_ws_response, 0,
+ sizeof(struct cache_mp_write_session_response));
+ TRACE_OUT(init_cache_mp_write_session_response);
+}
+
+void
+finalize_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *mp_ws_response)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_response);
+ TRACE_OUT(finalize_cache_mp_write_session_response);
+}
+
+struct cache_mp_write_session_response *
+get_cache_mp_write_session_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_response);
+ assert(element->type == CET_MP_WRITE_SESSION_RESPONSE);
+ TRACE_OUT(get_cache_mp_write_session_response);
+ return (&element->c_mp_ws_response);
+}
+
+void
+init_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *mp_ws_write_request)
+{
+
+ TRACE_IN(init_cache_mp_write_session_write_request);
+ memset(mp_ws_write_request, 0,
+ sizeof(struct cache_mp_write_session_write_request));
+ TRACE_OUT(init_cache_mp_write_session_write_response);
+}
+
+void
+finalize_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *mp_ws_write_request)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_write_request);
+ free(mp_ws_write_request->data);
+ TRACE_OUT(finalize_cache_mp_write_session_write_request);
+}
+
+struct cache_mp_write_session_write_request *
+get_cache_mp_write_session_write_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_write_request);
+ assert(element->type == CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ TRACE_OUT(get_cache_mp_write_session_write_request);
+ return (&element->c_mp_ws_write_request);
+}
+
+void
+init_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *mp_ws_write_response)
+{
+
+ TRACE_IN(init_cache_mp_write_session_write_response);
+ memset(mp_ws_write_response, 0,
+ sizeof(struct cache_mp_write_session_write_response));
+ TRACE_OUT(init_cache_mp_write_session_write_response);
+}
+
+void
+finalize_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *mp_ws_write_response)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_write_response);
+ TRACE_OUT(finalize_cache_mp_write_session_write_response);
+}
+
+struct cache_mp_write_session_write_response *
+get_cache_mp_write_session_write_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_write_response);
+ assert(element->type == CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ TRACE_OUT(get_cache_mp_write_session_write_response);
+ return (&element->c_mp_ws_write_response);
+}
+
+void
+init_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *mp_rs_request)
+{
+
+ TRACE_IN(init_cache_mp_read_session_request);
+ memset(mp_rs_request, 0, sizeof(struct cache_mp_read_session_request));
+ TRACE_OUT(init_cache_mp_read_session_request);
+}
+
+void
+finalize_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *mp_rs_request)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_request);
+ free(mp_rs_request->entry);
+ TRACE_OUT(finalize_cache_mp_read_session_request);
+}
+
+struct cache_mp_read_session_request *
+get_cache_mp_read_session_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_request);
+ assert(element->type == CET_MP_READ_SESSION_REQUEST);
+ TRACE_OUT(get_cache_mp_read_session_request);
+ return (&element->c_mp_rs_request);
+}
+
+void
+init_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *mp_rs_response)
+{
+
+ TRACE_IN(init_cache_mp_read_session_response);
+ memset(mp_rs_response, 0,
+ sizeof(struct cache_mp_read_session_response));
+ TRACE_OUT(init_cache_mp_read_session_response);
+}
+
+void
+finalize_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *mp_rs_response)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_response);
+ TRACE_OUT(finalize_cache_mp_read_session_response);
+}
+
+struct cache_mp_read_session_response *
+get_cache_mp_read_session_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_response);
+ assert(element->type == CET_MP_READ_SESSION_RESPONSE);
+ TRACE_OUT(get_cache_mp_read_session_response);
+ return (&element->c_mp_rs_response);
+}
+
+void
+init_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *mp_ws_read_response)
+{
+
+ TRACE_IN(init_cache_mp_read_session_read_response);
+ memset(mp_ws_read_response, 0,
+ sizeof(struct cache_mp_read_session_read_response));
+ TRACE_OUT(init_cache_mp_read_session_read_response);
+}
+
+void
+finalize_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *mp_rs_read_response)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_read_response);
+ free(mp_rs_read_response->data);
+ TRACE_OUT(finalize_cache_mp_read_session_read_response);
+}
+
+struct cache_mp_read_session_read_response *
+get_cache_mp_read_session_read_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_read_response);
+ assert(element->type == CET_MP_READ_SESSION_READ_RESPONSE);
+ TRACE_OUT(get_cache_mp_read_session_read_response);
+ return (&element->c_mp_rs_read_response);
+}
diff --git a/usr.sbin/cached/protocol.h b/usr.sbin/cached/protocol.h
new file mode 100644
index 0000000..7fadbfc
--- /dev/null
+++ b/usr.sbin/cached/protocol.h
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_PROTOCOL_H__
+#define __CACHED_PROTOCOL_H__
+
+#include <stdlib.h>
+
+/* maximum buffer size to receive - larger buffers are not allowed */
+#define MAX_BUFFER_SIZE (1 << 20)
+
+/* buffer size correctness checking routine */
+#define BUFSIZE_CORRECT(x) (((x) > 0) && ((x) < MAX_BUFFER_SIZE))
+#define BUFSIZE_INVALID(x) (!BUFSIZE_CORRECT(x))
+
+/* structures below represent the data that are sent/received by the daemon */
+struct cache_write_request
+{
+ char *entry;
+ char *cache_key;
+ char *data;
+
+ size_t entry_length;
+ size_t cache_key_size;
+ size_t data_size;
+};
+
+struct cache_write_response
+{
+ int error_code;
+};
+
+struct cache_read_request
+{
+ char *entry;
+ char *cache_key;
+
+ size_t entry_length;
+ size_t cache_key_size;
+};
+
+struct cache_read_response
+{
+ char *data; // ignored if error_code is not 0
+ size_t data_size; // ignored if error_code is not 0
+
+ int error_code;
+};
+
+enum transformation_type {
+ TT_USER = 0, // tranform only the entries of the caller
+ TT_ALL = 1 // transform all entries
+};
+
+struct cache_transform_request
+{
+ char *entry; // ignored if entry_length is 0
+ size_t entry_length;
+
+ int transformation_type;
+};
+
+struct cache_transform_response
+{
+ int error_code;
+};
+
+struct cache_mp_write_session_request {
+ char *entry;
+ size_t entry_length;
+};
+
+struct cache_mp_write_session_response {
+ int error_code;
+};
+
+struct cache_mp_write_session_write_request {
+ char *data;
+ size_t data_size;
+};
+
+struct cache_mp_write_session_write_response {
+ int error_code;
+};
+
+struct cache_mp_read_session_request {
+ char *entry;
+ size_t entry_length;
+};
+
+struct cache_mp_read_session_response {
+ int error_code;
+};
+
+struct cache_mp_read_session_read_response {
+ char *data;
+ size_t data_size;
+
+ int error_code;
+};
+
+
+enum comm_element_t {
+ CET_UNDEFINED = 0,
+ CET_WRITE_REQUEST = 1,
+ CET_WRITE_RESPONSE = 2,
+ CET_READ_REQUEST = 3,
+ CET_READ_RESPONSE = 4,
+ CET_TRANSFORM_REQUEST = 5,
+ CET_TRANSFORM_RESPONSE = 6,
+ CET_MP_WRITE_SESSION_REQUEST = 7,
+ CET_MP_WRITE_SESSION_RESPONSE = 8,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST = 9,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10,
+ CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11,
+ CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12,
+ CET_MP_READ_SESSION_REQUEST = 13,
+ CET_MP_READ_SESSION_RESPONSE = 14,
+ CET_MP_READ_SESSION_READ_REQUEST = 15,
+ CET_MP_READ_SESSION_READ_RESPONSE = 16,
+ CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17,
+ CET_MAX = 18
+};
+
+/*
+ * The comm_element is used as the holder of any known (defined above) data
+ * type that is to be sent/received.
+ */
+struct comm_element
+{
+ union {
+ struct cache_write_request c_write_request;
+ struct cache_write_response c_write_response;
+ struct cache_read_request c_read_request;
+ struct cache_read_response c_read_response;
+ struct cache_transform_request c_transform_request;
+ struct cache_transform_response c_transform_response;
+
+ struct cache_mp_write_session_request c_mp_ws_request;
+ struct cache_mp_write_session_response c_mp_ws_response;
+ struct cache_mp_write_session_write_request c_mp_ws_write_request;
+ struct cache_mp_write_session_write_response c_mp_ws_write_response;
+
+ struct cache_mp_read_session_request c_mp_rs_request;
+ struct cache_mp_read_session_response c_mp_rs_response;
+ struct cache_mp_read_session_read_response c_mp_rs_read_response;
+ };
+ enum comm_element_t type;
+};
+
+extern void init_comm_element(struct comm_element *, enum comm_element_t type);
+extern void finalize_comm_element(struct comm_element *);
+
+/*
+ * For each type of data, there is three functions (init/finalize/get), that
+ * used with comm_element structure
+ */
+extern void init_cache_write_request(struct cache_write_request *);
+extern void finalize_cache_write_request(struct cache_write_request *);
+extern struct cache_write_request *get_cache_write_request(
+ struct comm_element *);
+
+extern void init_cache_write_response(struct cache_write_response *);
+extern void finalize_cache_write_response(struct cache_write_response *);
+extern struct cache_write_response *get_cache_write_response(
+ struct comm_element *);
+
+extern void init_cache_read_request(struct cache_read_request *);
+extern void finalize_cache_read_request(struct cache_read_request *);
+extern struct cache_read_request *get_cache_read_request(
+ struct comm_element *);
+
+extern void init_cache_read_response(struct cache_read_response *);
+extern void finalize_cache_read_response(struct cache_read_response *);
+extern struct cache_read_response *get_cache_read_response(
+ struct comm_element *);
+
+extern void init_cache_transform_request(struct cache_transform_request *);
+extern void finalize_cache_transform_request(struct cache_transform_request *);
+extern struct cache_transform_request *get_cache_transform_request(
+ struct comm_element *);
+
+extern void init_cache_transform_response(struct cache_transform_response *);
+extern void finalize_cache_transform_response(
+ struct cache_transform_response *);
+extern struct cache_transform_response *get_cache_transform_response(
+ struct comm_element *);
+
+extern void init_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *);
+extern void finalize_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *);
+extern struct cache_mp_write_session_request *
+ get_cache_mp_write_session_request(
+ struct comm_element *);
+
+extern void init_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *);
+extern void finalize_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *);
+extern struct cache_mp_write_session_response *
+ get_cache_mp_write_session_response(struct comm_element *);
+
+extern void init_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *);
+extern void finalize_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *);
+extern struct cache_mp_write_session_write_request *
+ get_cache_mp_write_session_write_request(struct comm_element *);
+
+extern void init_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *);
+extern void finalize_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *);
+extern struct cache_mp_write_session_write_response *
+ get_cache_mp_write_session_write_response(struct comm_element *);
+
+extern void init_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *);
+extern void finalize_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *);
+extern struct cache_mp_read_session_request *get_cache_mp_read_session_request(
+ struct comm_element *);
+
+extern void init_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *);
+extern void finalize_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *);
+extern struct cache_mp_read_session_response *
+ get_cache_mp_read_session_response(
+ struct comm_element *);
+
+extern void init_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *);
+extern void finalize_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *);
+extern struct cache_mp_read_session_read_response *
+ get_cache_mp_read_session_read_response(struct comm_element *);
+
+#endif
diff --git a/usr.sbin/cached/query.c b/usr.sbin/cached/query.c
new file mode 100644
index 0000000..28882c3
--- /dev/null
+++ b/usr.sbin/cached/query.c
@@ -0,0 +1,1278 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "query.h"
+#include "log.h"
+#include "mp_ws_query.h"
+#include "mp_rs_query.h"
+#include "singletons.h"
+
+static const char negative_data[1] = { 0 };
+
+extern void get_time_func(struct timeval *);
+
+static void clear_config_entry(struct configuration_entry *);
+static void clear_config_entry_part(struct configuration_entry *,
+ const char *, size_t);
+
+static int on_query_startup(struct query_state *);
+static void on_query_destroy(struct query_state *);
+
+static int on_read_request_read1(struct query_state *);
+static int on_read_request_read2(struct query_state *);
+static int on_read_request_process(struct query_state *);
+static int on_read_response_write1(struct query_state *);
+static int on_read_response_write2(struct query_state *);
+
+static int on_rw_mapper(struct query_state *);
+
+static int on_transform_request_read1(struct query_state *);
+static int on_transform_request_read2(struct query_state *);
+static int on_transform_request_process(struct query_state *);
+static int on_transform_response_write1(struct query_state *);
+
+static int on_write_request_read1(struct query_state *);
+static int on_write_request_read2(struct query_state *);
+static int on_negative_write_request_process(struct query_state *);
+static int on_write_request_process(struct query_state *);
+static int on_write_response_write1(struct query_state *);
+
+/*
+ * Clears the specified configuration entry (clears the cache for positive and
+ * and negative entries) and also for all multipart entries.
+ */
+static void
+clear_config_entry(struct configuration_entry *config_entry)
+{
+ size_t i;
+
+ TRACE_IN(clear_config_entry);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ for (i = 0; i < config_entry->mp_cache_entries_size; ++i)
+ transform_cache_entry(
+ config_entry->mp_cache_entries[i],
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry);
+}
+
+/*
+ * Clears the specified configuration entry by deleting only the elements,
+ * that are owned by the user with specified eid_str.
+ */
+static void
+clear_config_entry_part(struct configuration_entry *config_entry,
+ const char *eid_str, size_t eid_str_length)
+{
+ cache_entry *start, *finish, *mp_entry;
+ TRACE_IN(clear_config_entry_part);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ if (configuration_entry_find_mp_cache_entries(config_entry,
+ eid_str, &start, &finish) == 0) {
+ for (mp_entry = start; mp_entry != finish; ++mp_entry)
+ transform_cache_entry(*mp_entry, CTT_CLEAR);
+ }
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry_part);
+}
+
+/*
+ * This function is assigned to the query_state structue on its creation.
+ * It's main purpose is to receive credentials from the client.
+ */
+static int
+on_query_startup(struct query_state *qstate)
+{
+ struct msghdr cred_hdr;
+ struct iovec iov;
+ int elem_type;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ TRACE_IN(on_query_startup);
+ assert(qstate != NULL);
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ memset(&iov, 0, sizeof(struct iovec));
+ iov.iov_base = &elem_type;
+ iov.iov_len = sizeof(int);
+
+ if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ if (cmsg.hdr.cmsg_len != sizeof cmsg
+ || cmsg.hdr.cmsg_level != SOL_SOCKET
+ || cmsg.hdr.cmsg_type != SCM_CREDS) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->uid = cmsg.creds.cmcred_uid;
+ qstate->gid = cmsg.creds.cmcred_gid;
+
+#if defined(NS_CACHED_EID_CHECKING) || defined(NS_STRICT_CACHED_EID_CHECKING)
+/*
+ * This check is probably a bit redundant - per-user cache is always separated
+ * by the euid/egid pair
+ */
+ if (check_query_eids(qstate) != 0) {
+#ifdef NS_STRICT_CACHED_EID_CHECKING
+ TRACE_OUT(on_query_startup);
+ return (-1);
+#else
+ if ((elem_type != CET_READ_REQUEST) &&
+ (elem_type != CET_MP_READ_SESSION_REQUEST) &&
+ (elem_type != CET_WRITE_REQUEST) &&
+ (elem_type != CET_MP_WRITE_SESSION_REQUEST)) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+#endif
+ }
+#endif
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->process_func = on_read_request_read1;
+ break;
+ case CET_TRANSFORM_REQUEST:
+ qstate->process_func = on_transform_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ qstate->process_func = on_mp_write_session_request_read1;
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ qstate->process_func = on_mp_read_session_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ TRACE_OUT(on_query_startup);
+ return (0);
+}
+
+/*
+ * on_rw_mapper is used to process multiple read/write requests during
+ * one connection session. It's never called in the beginning (on query_state
+ * creation) as it does not process the multipart requests and does not
+ * receive credentials
+ */
+static int
+on_rw_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_rw_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_read_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ break;
+ }
+ }
+ TRACE_OUT(on_rw_mapper);
+ return (0);
+}
+
+/*
+ * The default query_destroy function
+ */
+static void
+on_query_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_query_destroy);
+ finalize_comm_element(&qstate->response);
+ finalize_comm_element(&qstate->request);
+ TRACE_OUT(on_query_destroy);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_write_request_read1 and on_write_request_read2 read the request itself
+ * - on_write_request_process processes it (if the client requests to
+ * cache the negative result, the on_negative_write_request_process is used)
+ * - on_write_response_write1 sends the response
+ */
+static int
+on_write_request_read1(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 3;
+ else {
+ init_comm_element(&qstate->request, CET_WRITE_REQUEST);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->entry_length,
+ sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->cache_key_size, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->data_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 3) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->entry_length) ||
+ BUFSIZE_INVALID(write_request->cache_key_size) ||
+ (BUFSIZE_INVALID(write_request->data_size) &&
+ (write_request->data_size != 0))) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ write_request->entry = (char *)malloc(
+ write_request->entry_length + 1);
+ assert(write_request->entry != NULL);
+ memset(write_request->entry, 0,
+ write_request->entry_length + 1);
+
+ write_request->cache_key = (char *)malloc(
+ write_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(write_request->cache_key != NULL);
+ memcpy(write_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+ memset(write_request->cache_key + qstate->eid_str_length, 0,
+ write_request->cache_key_size);
+
+ if (write_request->data_size != 0) {
+ write_request->data = (char *)malloc(
+ write_request->data_size);
+ assert(write_request->data != NULL);
+ memset(write_request->data, 0,
+ write_request->data_size);
+ }
+
+ qstate->kevent_watermark = write_request->entry_length +
+ write_request->cache_key_size +
+ write_request->data_size;
+ qstate->process_func = on_write_request_read2;
+ }
+
+ TRACE_OUT(on_write_request_read1);
+ return (0);
+}
+
+static int
+on_write_request_read2(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read2);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, write_request->entry,
+ write_request->entry_length);
+ result += qstate->read_func(qstate, write_request->cache_key +
+ qstate->eid_str_length, write_request->cache_key_size);
+ if (write_request->data_size != 0)
+ result += qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_write_request_read2);
+ return (-1);
+ }
+ write_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ if (write_request->data_size != 0)
+ qstate->process_func = on_write_request_process;
+ else
+ qstate->process_func = on_negative_write_request_process;
+ TRACE_OUT(on_write_request_read2);
+ return (0);
+}
+
+static int
+on_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_request", "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_write_request_process);
+ return (0);
+}
+
+static int
+on_negative_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_negative_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry (
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("negative_write_request",
+ "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("negative_write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("negative_write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ write_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_negative_write_request_process);
+ return (0);
+}
+
+static int
+on_write_response_write1(struct query_state *qstate)
+{
+ struct cache_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_write_response_write1);
+ write_response = get_cache_write_response(&qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_write_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+
+ TRACE_OUT(on_write_response_write1);
+ return (0);
+}
+
+/*
+ * The functions below are used to process read requests.
+ * - on_read_request_read1 and on_read_request_read2 read the request itself
+ * - on_read_request_process processes it
+ * - on_read_response_write1 and on_read_response_write2 send the response
+ */
+static int
+on_read_request_read1(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 2;
+ else {
+ init_comm_element(&qstate->request, CET_READ_REQUEST);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &read_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &read_request->cache_key_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 2) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(read_request->entry_length) ||
+ BUFSIZE_INVALID(read_request->cache_key_size)) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ read_request->entry = (char *)malloc(
+ read_request->entry_length + 1);
+ assert(read_request->entry != NULL);
+ memset(read_request->entry, 0, read_request->entry_length + 1);
+
+ read_request->cache_key = (char *)malloc(
+ read_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(read_request->cache_key != NULL);
+ memcpy(read_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+ memset(read_request->cache_key + qstate->eid_str_length, 0,
+ read_request->cache_key_size);
+
+ qstate->kevent_watermark = read_request->entry_length +
+ read_request->cache_key_size;
+ qstate->process_func = on_read_request_read2;
+ }
+
+ TRACE_OUT(on_read_request_read1);
+ return (0);
+}
+
+static int
+on_read_request_read2(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read2);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate, read_request->entry,
+ read_request->entry_length);
+ result += qstate->read_func(qstate,
+ read_request->cache_key + qstate->eid_str_length,
+ read_request->cache_key_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_request_read2);
+ return (-1);
+ }
+ read_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_read_request_process;
+
+ TRACE_OUT(on_read_request_read2);
+ return (0);
+}
+
+static int
+on_read_request_process(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ struct cache_read_response *read_response;
+ cache_entry c_entry, neg_c_entry;
+
+ struct agent *lookup_agent;
+ struct common_agent *c_agent;
+ int res;
+
+ TRACE_IN(on_read_request_process);
+ init_comm_element(&qstate->response, CET_READ_RESPONSE);
+ read_response = get_cache_read_response(&qstate->response);
+ read_request = get_cache_read_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, read_request->entry);
+ if (qstate->config_entry == NULL) {
+ read_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_request",
+ "can't find configuration "
+ "entry '%s'. aborting request", read_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ read_response->error_code = EACCES;
+
+ LOG_ERR_2("read_request",
+ "configuration entry '%s' is disabled",
+ read_request->entry);
+ goto fin;
+ }
+
+ /*
+ * if we perform lookups by ourselves, then we don't need to separate
+ * cache entries by euid and egid
+ */
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ memset(read_request->cache_key, 0, qstate->eid_str_length);
+ else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ /* if the lookup is not self-performing, we check for clients euid/egid */
+ read_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ neg_c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if ((c_entry != NULL) && (neg_c_entry != NULL)) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->data = (char *)malloc(
+ read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = neg_c_entry;
+ if (read_response->error_code == -1) {
+ read_response->error_code = cache_read(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((read_response->error_code == -1) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ free(read_response->data);
+ read_response->data = NULL;
+ read_response->data_size = 0;
+
+ lookup_agent = find_agent(s_agent_table,
+ read_request->entry, COMMON_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == COMMON_AGENT)) {
+ c_agent = (struct common_agent *)lookup_agent;
+ res = c_agent->lookup_func(
+ read_request->cache_key +
+ qstate->eid_str_length,
+ read_request->cache_key_size -
+ qstate->eid_str_length,
+ &read_response->data,
+ &read_response->data_size);
+
+ if (res == NS_SUCCESS) {
+ read_response->error_code = 0;
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ cache_write(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ read_response->data_size);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ } else if ((res == NS_NOTFOUND) ||
+ (res == NS_RETURN)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+ cache_write(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ }
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ read_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_read_response_write1;
+
+ TRACE_OUT(on_read_request_process);
+ return (0);
+}
+
+static int
+on_read_response_write1(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write1);
+ read_response = get_cache_read_response(&qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_read_response_write1);
+ return (0);
+}
+
+static int
+on_read_response_write2(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write2);
+ read_response = get_cache_read_response(&qstate->response);
+ if (read_response->data_size > 0) {
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write2);
+ return (-1);
+ }
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+ TRACE_OUT(on_read_response_write2);
+ return (0);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_transform_request_read1 and on_transform_request_read2 read the
+ * request itself
+ * - on_transform_request_process processes it
+ * - on_transform_response_write1 sends the response
+ */
+static int
+on_transform_request_read1(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else {
+ init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
+ transform_request =
+ get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &transform_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &transform_request->transformation_type, sizeof(int));
+
+ if (result != sizeof(size_t) + sizeof(int)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if ((transform_request->transformation_type != TT_USER) &&
+ (transform_request->transformation_type != TT_ALL)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if (transform_request->entry_length != 0) {
+ if (BUFSIZE_INVALID(transform_request->entry_length)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ transform_request->entry = (char *)malloc(
+ transform_request->entry_length + 1);
+ assert(transform_request->entry != NULL);
+ memset(transform_request->entry, 0,
+ transform_request->entry_length + 1);
+
+ qstate->process_func = on_transform_request_read2;
+ } else
+ qstate->process_func = on_transform_request_process;
+
+ qstate->kevent_watermark = transform_request->entry_length;
+ }
+
+ TRACE_OUT(on_transform_request_read1);
+ return (0);
+}
+
+static int
+on_transform_request_read2(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read2);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate, transform_request->entry,
+ transform_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_transform_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_request_process;
+
+ TRACE_OUT(on_transform_request_read2);
+ return (0);
+}
+
+static int
+on_transform_request_process(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ struct cache_transform_response *transform_response;
+ struct configuration_entry *config_entry;
+ size_t i, size;
+
+ TRACE_IN(on_transform_request_process);
+ init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
+ transform_response = get_cache_transform_response(&qstate->response);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ switch (transform_request->transformation_type) {
+ case TT_USER:
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(s_configuration);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(
+ s_configuration, i);
+
+ if (config_entry->perform_actual_lookups == 0)
+ clear_config_entry_part(config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ LOG_ERR_2("transform_request",
+ "can't transform the cache entry %s"
+ ", because it ised for actual lookups",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry_part(qstate->config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ break;
+ case TT_ALL:
+ if (qstate->euid != 0)
+ transform_response->error_code = -1;
+ else {
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(
+ s_configuration);
+ for (i = 0; i < size; ++i) {
+ clear_config_entry(
+ configuration_get_entry(
+ s_configuration, i));
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration,
+ transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry(qstate->config_entry);
+ }
+ }
+ break;
+ default:
+ transform_response->error_code = -1;
+ }
+
+fin:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_response_write1;
+ TRACE_OUT(on_transform_request_process);
+ return (0);
+}
+
+static int
+on_transform_response_write1(struct query_state *qstate)
+{
+ struct cache_transform_response *transform_response;
+ ssize_t result;
+
+ TRACE_IN(on_transform_response_write1);
+ transform_response = get_cache_transform_response(&qstate->response);
+ result = qstate->write_func(qstate, &transform_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_transform_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_transform_response_write1);
+ return (0);
+}
+
+/*
+ * Checks if the client's euid and egid do not differ from its uid and gid.
+ * Returns 0 on success.
+ */
+int
+check_query_eids(struct query_state *qstate)
+{
+
+ return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" read - when the buffer is
+ * too large to be received during one socket read operation
+ */
+ssize_t
+query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_read);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(buf, qstate->io_buffer_p, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ free(qstate->io_buffer);
+ qstate->io_buffer = NULL;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_read);
+ return (result);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" write - when the buffer is
+ * too large to be sent during one socket write operation
+ */
+ssize_t
+query_io_buffer_write(struct query_state *qstate, const void *buf,
+ size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_write);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(qstate->io_buffer_p, buf, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ qstate->use_alternate_io = 1;
+ qstate->io_buffer_p = qstate->io_buffer;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_write);
+ return (result);
+}
+
+/*
+ * The default "read" function, which reads data directly from socket
+ */
+ssize_t
+query_socket_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_read);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_read);
+ return (-1);
+ }
+
+ result = read(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_read);
+ return (result);
+}
+
+/*
+ * The default "write" function, which writes data directly to socket
+ */
+ssize_t
+query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_write);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_write);
+ return (-1);
+ }
+
+ result = write(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_write);
+ return (result);
+}
+
+/*
+ * Initializes the query_state structure by filling it with the default values.
+ */
+struct query_state *
+init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid)
+{
+ struct query_state *retval;
+
+ TRACE_IN(init_query_state);
+ retval = (struct query_state *)malloc(sizeof(struct query_state));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct query_state));
+
+ retval->sockfd = sockfd;
+ retval->kevent_filter = EVFILT_READ;
+ retval->kevent_watermark = kevent_watermark;
+
+ retval->euid = euid;
+ retval->egid = egid;
+ retval->uid = retval->gid = -1;
+
+ if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
+ retval->egid) == -1) {
+ free(retval);
+ return (NULL);
+ }
+ retval->eid_str_length = strlen(retval->eid_str);
+
+ init_comm_element(&retval->request, CET_UNDEFINED);
+ init_comm_element(&retval->response, CET_UNDEFINED);
+ retval->process_func = on_query_startup;
+ retval->destroy_func = on_query_destroy;
+
+ retval->write_func = query_socket_write;
+ retval->read_func = query_socket_read;
+
+ get_time_func(&retval->creation_time);
+ memcpy(&retval->timeout, &s_configuration->query_timeout,
+ sizeof(struct timeval));
+
+ TRACE_OUT(init_query_state);
+ return (retval);
+}
+
+void
+destroy_query_state(struct query_state *qstate)
+{
+
+ TRACE_IN(destroy_query_state);
+ if (qstate->eid_str != NULL)
+ free(qstate->eid_str);
+
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->destroy_func(qstate);
+ free(qstate);
+ TRACE_OUT(destroy_query_state);
+}
diff --git a/usr.sbin/cached/query.h b/usr.sbin/cached/query.h
new file mode 100644
index 0000000..5c4e918
--- /dev/null
+++ b/usr.sbin/cached/query.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_QUERY_H__
+#define __CACHED_QUERY_H__
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "cachelib.h"
+#include "config.h"
+#include "protocol.h"
+
+struct query_state;
+struct configuration;
+struct configuration_entry;
+
+typedef int (*query_process_func)(struct query_state *);
+typedef void (*query_destroy_func)(struct query_state *);
+typedef ssize_t (*query_read_func)(struct query_state *, void *, size_t);
+typedef ssize_t (*query_write_func)(struct query_state *, const void *,
+ size_t);
+
+/*
+ * The query state structure contains the information to process all types of
+ * requests and to send all types of responses.
+ */
+struct query_state {
+ struct timeval creation_time;
+ struct timeval timeout;
+
+ struct comm_element request;
+ struct comm_element response;
+ struct configuration_entry *config_entry;
+ void *mdata;
+
+ query_process_func process_func; /* called on each event */
+ query_destroy_func destroy_func; /* called on destroy */
+
+ /*
+ * By substituting these functions we can opaquely send and received
+ * very large buffers
+ */
+ query_write_func write_func; /* data write function */
+ query_read_func read_func; /* data read function */
+
+ char *eid_str; /* the user-identifying string (euid_egid_) */
+ size_t eid_str_length;
+
+ uid_t euid; /* euid of the caller, received via getpeereid */
+ uid_t uid; /* uid of the caller, received via credentials */
+ gid_t egid; /* egid of the caller, received via getpeereid */
+ gid_t gid; /* gid of the caller received via credentials */
+
+ size_t io_buffer_size;
+ size_t io_buffer_watermark;
+ size_t kevent_watermark; /* bytes to be sent/received */
+ int sockfd; /* the unix socket to read/write */
+ int kevent_filter; /* EVFILT_READ or EVFILT_WRITE */
+ int socket_failed; /* set to 1 if the socket doesn't work correctly */
+
+ /*
+ * These fields are used to opaquely proceed sending/receiving of
+ * the large buffers
+ */
+ char *io_buffer;
+ char *io_buffer_p;
+ int io_buffer_filter;
+ int use_alternate_io;
+};
+
+extern int check_query_eids(struct query_state *);
+
+extern ssize_t query_io_buffer_read(struct query_state *, void *, size_t);
+extern ssize_t query_io_buffer_write(struct query_state *, const void *,
+ size_t);
+
+extern ssize_t query_socket_read(struct query_state *, void *, size_t);
+extern ssize_t query_socket_write(struct query_state *, const void *,
+ size_t);
+
+extern struct query_state *init_query_state(int, size_t, uid_t, gid_t);
+extern void destroy_query_state(struct query_state *);
+
+#endif
diff --git a/usr.sbin/cached/singletons.c b/usr.sbin/cached/singletons.c
new file mode 100644
index 0000000..669d12b
--- /dev/null
+++ b/usr.sbin/cached/singletons.c
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "singletons.h"
+
+struct configuration *s_configuration = NULL;
+cache s_cache = INVALID_CACHE;
+struct runtime_env *s_runtime_env = NULL;
+struct agent_table *s_agent_table = NULL;
diff --git a/usr.sbin/cached/singletons.h b/usr.sbin/cached/singletons.h
new file mode 100644
index 0000000..918519c
--- /dev/null
+++ b/usr.sbin/cached/singletons.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_SINGLETONS_H__
+#define __CACHED_SINGLETONS_H__
+
+#include "cachelib.h"
+#include "config.h"
+#include "agent.h"
+
+struct runtime_env {
+ int queue;
+ int sockfd;
+ int finished; /* for future use */
+};
+
+extern struct configuration *s_configuration;
+extern cache s_cache;
+extern struct runtime_env *s_runtime_env;
+extern struct agent_table *s_agent_table;
+
+#endif
diff --git a/usr.sbin/nscd/Makefile b/usr.sbin/nscd/Makefile
new file mode 100644
index 0000000..5478341
--- /dev/null
+++ b/usr.sbin/nscd/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+PROG=cached
+PROGNAME=cached
+MAN=cached.conf.5 cached.8
+
+WARNS?=2
+SRCS= agent.c cached.c cachedcli.c cachelib.c cacheplcs.c debug.c log.c \
+ config.c query.c mp_ws_query.c mp_rs_query.c singletons.c protocol.c \
+ parser.c
+CFLAGS+= -DCONFIG_PATH="\"${PREFIX}/etc/cached.conf\""
+DPADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL}
+LDADD+=${LIBM} ${LIBPTHREAD} ${LIBUTIL}
+LDFLAGS+= -Xlinker --export-dynamic
+
+.PATH: ${.CURDIR}/agents
+.include "agents/Makefile.inc"
+.include "../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nscd/agent.c b/usr.sbin/nscd/agent.c
new file mode 100644
index 0000000..2d58ef1
--- /dev/null
+++ b/usr.sbin/nscd/agent.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include "agent.h"
+#include "debug.h"
+
+static int
+agent_cmp_func(const void *a1, const void *a2)
+{
+ struct agent const *ap1 = *((struct agent const **)a1);
+ struct agent const *ap2 = *((struct agent const **)a2);
+ int res;
+
+ res = strcmp(ap1->name, ap2->name);
+ if (res == 0) {
+ if (ap1->type == ap2->type)
+ res = 0;
+ else if (ap1->type < ap2->type)
+ res = -1;
+ else
+ res = 1;
+ }
+
+ return (res);
+}
+
+struct agent_table *
+init_agent_table()
+{
+ struct agent_table *retval;
+
+ TRACE_IN(init_agent_table);
+ retval = (struct agent_table *)malloc(sizeof(struct agent_table));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct agent_table));
+
+ TRACE_OUT(init_agent_table);
+ return (retval);
+}
+
+void
+register_agent(struct agent_table *at, struct agent *a)
+{
+ struct agent **new_agents;
+ size_t new_agents_num;
+
+ TRACE_IN(register_agent);
+ assert(at != NULL);
+ assert(a != NULL);
+ new_agents_num = at->agents_num + 1;
+ new_agents = (struct agent **)malloc(sizeof(struct agent *) *
+ new_agents_num);
+ assert(new_agents != NULL);
+ memcpy(new_agents, at->agents, at->agents_num * sizeof(struct agent *));
+ new_agents[new_agents_num - 1] = a;
+ qsort(new_agents, new_agents_num, sizeof(struct agent *),
+ agent_cmp_func);
+
+ free(at->agents);
+ at->agents = new_agents;
+ at->agents_num = new_agents_num;
+ TRACE_OUT(register_agent);
+}
+
+struct agent *
+find_agent(struct agent_table *at, const char *name, enum agent_type type)
+{
+ struct agent **res;
+ struct agent model, *model_p;
+
+ TRACE_IN(find_agent);
+ model.name = (char *)name;
+ model.type = type;
+ model_p = &model;
+ res = bsearch(&model_p, at->agents, at->agents_num,
+ sizeof(struct agent *), agent_cmp_func);
+
+ TRACE_OUT(find_agent);
+ return ( res == NULL ? NULL : *res);
+}
+
+void
+destroy_agent_table(struct agent_table *at)
+{
+ size_t i;
+
+ TRACE_IN(destroy_agent_table);
+ assert(at != NULL);
+ for (i = 0; i < at->agents_num; ++i) {
+ free(at->agents[i]->name);
+ free(at->agents[i]);
+ }
+
+ free(at->agents);
+ free(at);
+ TRACE_OUT(destroy_agent_table);
+}
diff --git a/usr.sbin/nscd/agent.h b/usr.sbin/nscd/agent.h
new file mode 100644
index 0000000..5dc368d
--- /dev/null
+++ b/usr.sbin/nscd/agent.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_AGENT_H__
+#define __CACHED_AGENT_H__
+
+/*
+ * Agents are used to perform the actual lookups from the caching daemon.
+ * There are two types of daemons: for common requests and for multipart
+ * requests.
+ * All agents are stored in the agents table, which is the singleton.
+ */
+
+enum agent_type {
+ COMMON_AGENT = 0,
+ MULTIPART_AGENT = 1
+};
+
+struct agent {
+ char *name;
+ enum agent_type type;
+};
+
+struct common_agent {
+ struct agent parent;
+ int (*lookup_func)(const char *, size_t, char **, size_t *);
+};
+
+struct multipart_agent {
+ struct agent parent;
+ void *(*mp_init_func)();
+ int (*mp_lookup_func)(char **, size_t *, void *);
+ void (*mp_destroy_func)(void *);
+};
+
+struct agent_table {
+ struct agent **agents;
+ size_t agents_num;
+};
+
+extern struct agent_table *init_agent_table();
+extern void register_agent(struct agent_table *, struct agent *);
+extern struct agent *find_agent(struct agent_table *, const char *,
+ enum agent_type);
+extern void destroy_agent_table(struct agent_table *);
+
+#endif
diff --git a/usr.sbin/nscd/agents/Makefile.inc b/usr.sbin/nscd/agents/Makefile.inc
new file mode 100644
index 0000000..1be32e1
--- /dev/null
+++ b/usr.sbin/nscd/agents/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+SRCS += passwd.c group.c services.c
diff --git a/usr.sbin/nscd/agents/group.c b/usr.sbin/nscd/agents/group.c
new file mode 100644
index 0000000..b9190be
--- /dev/null
+++ b/usr.sbin/nscd/agents/group.c
@@ -0,0 +1,262 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <nsswitch.h>
+#include <grp.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "passwd.h"
+
+static int group_marshal_func(struct group *, char *, size_t *);
+static int group_lookup_func(const char *, size_t, char **, size_t *);
+static void *group_mp_init_func();
+static int group_mp_lookup_func(char **, size_t *, void *);
+static void group_mp_destroy_func(void *);
+
+static int
+group_marshal_func(struct group *grp, char *buffer, size_t *buffer_size)
+{
+ struct group new_grp;
+ size_t desired_size, size, mem_size;
+ char *p, **mem;
+
+ TRACE_IN(group_marshal_func);
+ desired_size = ALIGNBYTES + sizeof(struct group) + sizeof(char *);
+
+ if (grp->gr_name != NULL)
+ desired_size += strlen(grp->gr_name) + 1;
+ if (grp->gr_passwd != NULL)
+ desired_size += strlen(grp->gr_passwd) + 1;
+
+ if (grp->gr_mem != NULL) {
+ mem_size = 0;
+ for (mem = grp->gr_mem; *mem; ++mem) {
+ desired_size += strlen(*mem) + 1;
+ ++mem_size;
+ }
+
+ desired_size += ALIGNBYTES + (mem_size + 1) * sizeof(char *);
+ }
+
+ if ((desired_size > *buffer_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(group_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_grp, grp, sizeof(struct group));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct group) + sizeof(char *);
+ memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
+ p = (char *)ALIGN(p);
+
+ if (new_grp.gr_name != NULL) {
+ size = strlen(new_grp.gr_name);
+ memcpy(p, new_grp.gr_name, size);
+ new_grp.gr_name = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_passwd != NULL) {
+ size = strlen(new_grp.gr_passwd);
+ memcpy(p, new_grp.gr_passwd, size);
+ new_grp.gr_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_mem != NULL) {
+ p = (char *)ALIGN(p);
+ memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
+ new_grp.gr_mem = (char **)p;
+ p += sizeof(char *) * (mem_size + 1);
+
+ for (mem = new_grp.gr_mem; *mem; ++mem) {
+ size = strlen(*mem);
+ memcpy(p, *mem, size);
+ *mem = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_grp, sizeof(struct group));
+ TRACE_OUT(group_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+group_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *name;
+ size_t size;
+ gid_t gid;
+
+ struct group *result;
+
+ TRACE_IN(group_lookup_func);
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(group_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ name = (char *)malloc(size);
+ assert(name != NULL);
+ memset(name, 0, size);
+ memcpy(name, key + sizeof(enum nss_lookup_type), size - 1);
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(gid_t)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&gid, key + sizeof(enum nss_lookup_type), sizeof(gid_t));
+ break;
+ default:
+ TRACE_OUT(group_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ TRACE_STR(name);
+ result = getgrnam(name);
+ free(name);
+ break;
+ case nss_lt_id:
+ result = getgrgid(gid);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ group_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ group_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(group_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+group_mp_init_func()
+{
+ TRACE_IN(group_mp_init_func);
+ setgrent();
+ TRACE_OUT(group_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+group_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct group *result;
+
+ TRACE_IN(group_mp_lookup_func);
+ result = getgrent();
+ if (result != NULL) {
+ group_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ group_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(group_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+group_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(group_mp_destroy_func);
+ TRACE_OUT(group_mp_destroy_func);
+}
+
+struct agent *
+init_group_agent()
+{
+ struct common_agent *retval;
+
+ TRACE_IN(init_group_agent);
+ retval = (struct common_agent *)malloc(sizeof(struct common_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct common_agent));
+
+ retval->parent.name = strdup("group");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = group_lookup_func;
+
+ TRACE_OUT(init_group_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_group_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_group_mp_agent);
+ retval = (struct multipart_agent *)malloc(
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct multipart_agent));
+
+ retval->parent.name = strdup("group");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = group_mp_init_func;
+ retval->mp_lookup_func = group_mp_lookup_func;
+ retval->mp_destroy_func = group_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_group_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/nscd/agents/group.h b/usr.sbin/nscd/agents/group.h
new file mode 100644
index 0000000..e6c7397
--- /dev/null
+++ b/usr.sbin/nscd/agents/group.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "../agent.h"
+
+extern struct agent *init_group_agent();
+extern struct agent *init_group_mp_agent();
diff --git a/usr.sbin/nscd/agents/passwd.c b/usr.sbin/nscd/agents/passwd.c
new file mode 100644
index 0000000..50c55ba
--- /dev/null
+++ b/usr.sbin/nscd/agents/passwd.c
@@ -0,0 +1,269 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <nsswitch.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "passwd.h"
+
+static int passwd_marshal_func(struct passwd *, char *, size_t *);
+static int passwd_lookup_func(const char *, size_t, char **, size_t *);
+static void *passwd_mp_init_func();
+static int passwd_mp_lookup_func(char **, size_t *, void *);
+static void passwd_mp_destroy_func(void *mdata);
+
+static int
+passwd_marshal_func(struct passwd *pwd, char *buffer, size_t *buffer_size)
+{
+ char *p;
+ struct passwd new_pwd;
+ size_t desired_size, size;
+
+ TRACE_IN(passwd_marshal_func);
+ desired_size = sizeof(struct passwd) + sizeof(char *) +
+ strlen(pwd->pw_name) + 1;
+ if (pwd->pw_passwd != NULL)
+ desired_size += strlen(pwd->pw_passwd) + 1;
+ if (pwd->pw_class != NULL)
+ desired_size += strlen(pwd->pw_class) + 1;
+ if (pwd->pw_gecos != NULL)
+ desired_size += strlen(pwd->pw_gecos) + 1;
+ if (pwd->pw_dir != NULL)
+ desired_size += strlen(pwd->pw_dir) + 1;
+ if (pwd->pw_shell != NULL)
+ desired_size += strlen(pwd->pw_shell) + 1;
+
+ if ((*buffer_size < desired_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(passwd_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_pwd, pwd, sizeof(struct passwd));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct passwd) + sizeof(char *);
+ memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
+
+ if (new_pwd.pw_name != NULL) {
+ size = strlen(new_pwd.pw_name);
+ memcpy(p, new_pwd.pw_name, size);
+ new_pwd.pw_name = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_passwd != NULL) {
+ size = strlen(new_pwd.pw_passwd);
+ memcpy(p, new_pwd.pw_passwd, size);
+ new_pwd.pw_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_class != NULL) {
+ size = strlen(new_pwd.pw_class);
+ memcpy(p, new_pwd.pw_class, size);
+ new_pwd.pw_class = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_gecos != NULL) {
+ size = strlen(new_pwd.pw_gecos);
+ memcpy(p, new_pwd.pw_gecos, size);
+ new_pwd.pw_gecos = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_dir != NULL) {
+ size = strlen(new_pwd.pw_dir);
+ memcpy(p, new_pwd.pw_dir, size);
+ new_pwd.pw_dir = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_shell != NULL) {
+ size = strlen(new_pwd.pw_shell);
+ memcpy(p, new_pwd.pw_shell, size);
+ new_pwd.pw_shell = p;
+ p += size + 1;
+ }
+
+ memcpy(buffer, &new_pwd, sizeof(struct passwd));
+ TRACE_OUT(passwd_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+passwd_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *login;
+ size_t size;
+ uid_t uid;
+
+ struct passwd *result;
+
+ TRACE_IN(passwd_lookup_func);
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ login = (char *)malloc(size);
+ assert(login != NULL);
+ memset(login, 0, size);
+ memcpy(login, key + sizeof(enum nss_lookup_type), size - 1);
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(uid_t)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&uid, key + sizeof(enum nss_lookup_type), sizeof(uid_t));
+ break;
+ default:
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ result = getpwnam(login);
+ free(login);
+ break;
+ case nss_lt_id:
+ result = getpwuid(uid);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ passwd_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ passwd_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(passwd_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+passwd_mp_init_func()
+{
+ TRACE_IN(passwd_mp_init_func);
+ setpwent();
+ TRACE_OUT(passwd_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+passwd_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct passwd *result;
+
+ TRACE_IN(passwd_mp_lookup_func);
+ result = getpwent();
+ if (result != NULL) {
+ passwd_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ passwd_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(passwd_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+passwd_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(passwd_mp_destroy_func);
+ TRACE_OUT(passwd_mp_destroy_func);
+}
+
+struct agent *
+init_passwd_agent()
+{
+ struct common_agent *retval;
+
+ TRACE_IN(init_passwd_agent);
+ retval = (struct common_agent *)malloc(sizeof(struct common_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct common_agent));
+
+ retval->parent.name = strdup("passwd");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = passwd_lookup_func;
+
+ TRACE_OUT(init_passwd_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_passwd_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_passwd_mp_agent);
+ retval = (struct multipart_agent *)malloc(
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct multipart_agent));
+
+ retval->parent.name = strdup("passwd");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = passwd_mp_init_func;
+ retval->mp_lookup_func = passwd_mp_lookup_func;
+ retval->mp_destroy_func = passwd_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_passwd_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/nscd/agents/passwd.h b/usr.sbin/nscd/agents/passwd.h
new file mode 100644
index 0000000..956a50d
--- /dev/null
+++ b/usr.sbin/nscd/agents/passwd.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "../agent.h"
+
+extern struct agent *init_passwd_agent();
+extern struct agent *init_passwd_mp_agent();
diff --git a/usr.sbin/nscd/agents/services.c b/usr.sbin/nscd/agents/services.c
new file mode 100644
index 0000000..3683396
--- /dev/null
+++ b/usr.sbin/nscd/agents/services.c
@@ -0,0 +1,286 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <nsswitch.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "services.h"
+
+static int services_marshal_func(struct servent *, char *, size_t *);
+static int services_lookup_func(const char *, size_t, char **, size_t *);
+static void *services_mp_init_func();
+static int services_mp_lookup_func(char **, size_t *, void *);
+static void services_mp_destroy_func(void *);
+
+static int
+services_marshal_func(struct servent *serv, char *buffer, size_t *buffer_size)
+{
+ struct servent new_serv;
+ size_t desired_size;
+ char **alias;
+ char *p;
+ size_t size;
+ size_t aliases_size;
+
+ TRACE_IN(services_marshal_func);
+ desired_size = ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
+ if (serv->s_name != NULL)
+ desired_size += strlen(serv->s_name) + 1;
+ if (serv->s_proto != NULL)
+ desired_size += strlen(serv->s_proto) + 1;
+
+ aliases_size = 0;
+ if (serv->s_aliases != NULL) {
+ for (alias = serv->s_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += ALIGNBYTES + sizeof(char *) *
+ (aliases_size + 1);
+ }
+
+ if ((*buffer_size < desired_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(services_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_serv, serv, sizeof(struct servent));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct servent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
+ p = (char *)ALIGN(p);
+
+ if (new_serv.s_name != NULL) {
+ size = strlen(new_serv.s_name);
+ memcpy(p, new_serv.s_name, size);
+ new_serv.s_name = p;
+ p += size + 1;
+ }
+
+ if (new_serv.s_proto != NULL) {
+ size = strlen(new_serv.s_proto);
+ memcpy(p, new_serv.s_proto, size);
+ new_serv.s_proto = p;
+ p += size + 1;
+ }
+
+ if (new_serv.s_aliases != NULL) {
+ p = (char *)ALIGN(p);
+ memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
+ new_serv.s_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (alias = new_serv.s_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_serv, sizeof(struct servent));
+ TRACE_OUT(services_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+services_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *name = NULL;
+ char *proto = NULL;
+ size_t size, size2;
+ int port;
+
+ struct servent *result;
+
+ TRACE_IN(services_lookup_func);
+
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ name = (char *)malloc(size);
+ assert(name != NULL);
+ memset(name, 0, size);
+ memcpy(name, key + sizeof(enum nss_lookup_type), size - 1);
+
+ size2 = strlen(name) + 1;
+ if (size2 < size) {
+ proto = strchr(name, '\0');
+ if (strrchr(name, '\0') > proto)
+ ++proto ;
+ else
+ proto = NULL;
+ }
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(int)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&port, key + sizeof(enum nss_lookup_type),
+ sizeof(int));
+
+ size = key_size - sizeof(enum nss_lookup_type) + sizeof(int);
+ if (size > 0) {
+ proto = (char *)malloc(size + 1);
+ assert(proto != NULL);
+ memset(proto, size + 1, 0);
+ memcpy(proto, key + sizeof(enum nss_lookup_type) +
+ sizeof(int), size);
+ }
+ break;
+ default:
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ result = getservbyname(name, proto);
+ free(name);
+ break;
+ case nss_lt_id:
+ result = getservbyport(port, proto);
+ free(proto);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ services_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ services_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(services_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+services_mp_init_func()
+{
+ TRACE_IN(services_mp_init_func);
+ setservent(0);
+ TRACE_OUT(services_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+services_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct servent *result;
+
+ TRACE_IN(services_mp_lookup_func);
+ result = getservent();
+ if (result != NULL) {
+ services_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ services_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(services_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+services_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(services_mp_destroy_func);
+ TRACE_OUT(services_mp_destroy_func);
+}
+
+struct agent *
+init_services_agent()
+{
+ struct common_agent *retval;
+ TRACE_IN(init_services_agent);
+
+ retval = (struct common_agent *)malloc(sizeof(struct common_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct common_agent));
+
+ retval->parent.name = strdup("services");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = services_lookup_func;
+
+ TRACE_OUT(init_services_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_services_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_services_mp_agent);
+ retval = (struct multipart_agent *)malloc(
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct multipart_agent));
+
+ retval->parent.name = strdup("services");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = services_mp_init_func;
+ retval->mp_lookup_func = services_mp_lookup_func;
+ retval->mp_destroy_func = services_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_services_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/nscd/agents/services.h b/usr.sbin/nscd/agents/services.h
new file mode 100644
index 0000000..0b77c87
--- /dev/null
+++ b/usr.sbin/nscd/agents/services.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include "../agent.h"
+
+extern struct agent *init_services_agent();
+extern struct agent *init_services_mp_agent();
diff --git a/usr.sbin/nscd/cachelib.c b/usr.sbin/nscd/cachelib.c
new file mode 100644
index 0000000..4f771cc
--- /dev/null
+++ b/usr.sbin/nscd/cachelib.c
@@ -0,0 +1,1234 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/time.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cachelib.h"
+#include "debug.h"
+
+#define INITIAL_ENTRIES_CAPACITY 32
+#define ENTRIES_CAPACITY_STEP 32
+
+#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
+ for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
+ (var) = ((a)*(var) + *(in_var)) % (M)
+
+#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
+ for ((var) = 0; *(in_var) != 0; ++(in_var)) \
+ (var) = ((a)*(var) + *(in_var)) & (M - 1)
+
+static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
+ struct cache_policy_item_ *);
+static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
+ struct cache_policy_item_ *);
+static void clear_cache_entry(struct cache_entry_ *);
+static void destroy_cache_entry(struct cache_entry_ *);
+static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
+static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
+static int entries_bsearch_cmp_func(const void *, const void *);
+static int entries_qsort_cmp_func(const void *, const void *);
+static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
+ const char *);
+static void flush_cache_entry(struct cache_entry_ *);
+static void flush_cache_policy(struct cache_common_entry_ *,
+ struct cache_policy_ *, struct cache_policy_ *,
+ int (*)(struct cache_common_entry_ *,
+ struct cache_policy_item_ *));
+static int ht_items_cmp_func(const void *, const void *);
+static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
+static hashtable_index_t ht_item_hash_func(const void *, size_t);
+
+/*
+ * Hashing and comparing routines, that are used with the hash tables
+ */
+static int
+ht_items_cmp_func(const void *p1, const void *p2)
+{
+ struct cache_ht_item_data_ *hp1, *hp2;
+ size_t min_size;
+ int result;
+
+ hp1 = (struct cache_ht_item_data_ *)p1;
+ hp2 = (struct cache_ht_item_data_ *)p2;
+
+ assert(hp1->key != NULL);
+ assert(hp2->key != NULL);
+
+ if (hp1->key_size != hp2->key_size) {
+ min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
+ hp2->key_size;
+ result = memcmp(hp1->key, hp2->key, min_size);
+
+ if (result == 0)
+ return ((hp1->key_size < hp2->key_size) ? -1 : 1);
+ else
+ return (result);
+ } else
+ return (memcmp(hp1->key, hp2->key, hp1->key_size));
+}
+
+static int
+ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
+{
+ struct cache_ht_item_data_ *hp1, *hp2;
+ size_t min_size;
+ int result;
+
+ hp1 = (struct cache_ht_item_data_ *)p1;
+ hp2 = (struct cache_ht_item_data_ *)p2;
+
+ assert(hp1->key != NULL);
+ assert(hp2->key != NULL);
+
+ if (hp1->key_size != hp2->key_size) {
+ min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
+ hp2->key_size;
+ result = memcmp(hp1->key, hp2->key, min_size);
+
+ if (result == 0)
+ if (min_size == hp1->key_size)
+ return (0);
+ else
+ return ((hp1->key_size < hp2->key_size) ? -1 : 1);
+ else
+ return (result);
+ } else
+ return (memcmp(hp1->key, hp2->key, hp1->key_size));
+}
+
+static hashtable_index_t
+ht_item_hash_func(const void *p, size_t cache_entries_size)
+{
+ struct cache_ht_item_data_ *hp;
+ size_t i;
+
+ hashtable_index_t retval;
+
+ hp = (struct cache_ht_item_data_ *)p;
+ assert(hp->key != NULL);
+
+ retval = 0;
+ for (i = 0; i < hp->key_size; ++i)
+ retval = (127 * retval + (unsigned char)hp->key[i]) %
+ cache_entries_size;
+
+ return retval;
+}
+
+HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
+ ht_item_hash_func, ht_items_cmp_func);
+
+/*
+ * Routines to sort and search the entries by name
+ */
+static int
+entries_bsearch_cmp_func(const void *key, const void *ent)
+{
+
+ assert(key != NULL);
+ assert(ent != NULL);
+
+ return (strcmp((char const *)key,
+ (*(struct cache_entry_ const **)ent)->name));
+}
+
+static int
+entries_qsort_cmp_func(const void *e1, const void *e2)
+{
+
+ assert(e1 != NULL);
+ assert(e2 != NULL);
+
+ return (strcmp((*(struct cache_entry_ const **)e1)->name,
+ (*(struct cache_entry_ const **)e2)->name));
+}
+
+static struct cache_entry_ **
+find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
+{
+
+ return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
+ the_cache->entries_size, sizeof(struct cache_entry_ *),
+ entries_bsearch_cmp_func)));
+}
+
+static void
+destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ struct cache_mp_data_item_ *data_item;
+
+ TRACE_IN(destroy_cache_mp_write_session);
+ assert(ws != NULL);
+ while (!TAILQ_EMPTY(&ws->items)) {
+ data_item = TAILQ_FIRST(&ws->items);
+ TAILQ_REMOVE(&ws->items, data_item, entries);
+ free(data_item->value);
+ free(data_item);
+ }
+
+ free(ws);
+ TRACE_OUT(destroy_cache_mp_write_session);
+}
+
+static void
+destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
+{
+
+ TRACE_IN(destroy_cache_mp_read_session);
+ assert(rs != NULL);
+ free(rs);
+ TRACE_OUT(destroy_cache_mp_read_session);
+}
+
+static void
+destroy_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_read_session_ *rs;
+ struct cache_mp_write_session_ *ws;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data;
+
+ TRACE_IN(destroy_cache_entry);
+ assert(entry != NULL);
+
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
+ {
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ }
+ HASHTABLE_ENTRY_CLEAR(ht_item, data);
+ }
+
+ HASHTABLE_DESTROY(&(common_entry->items), data);
+
+ /* FIFO policy is always first */
+ destroy_cache_fifo_policy(common_entry->policies[0]);
+ switch (common_entry->common_params.policy) {
+ case CPT_LRU:
+ destroy_cache_lru_policy(common_entry->policies[1]);
+ break;
+ case CPT_LFU:
+ destroy_cache_lfu_policy(common_entry->policies[1]);
+ break;
+ default:
+ break;
+ }
+ free(common_entry->policies);
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
+ ws = TAILQ_FIRST(&mp_entry->ws_head);
+ TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
+ destroy_cache_mp_write_session(ws);
+ }
+
+ while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
+ rs = TAILQ_FIRST(&mp_entry->rs_head);
+ TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
+ destroy_cache_mp_read_session(rs);
+ }
+
+ if (mp_entry->completed_write_session != NULL)
+ destroy_cache_mp_write_session(
+ mp_entry->completed_write_session);
+
+ if (mp_entry->pending_write_session != NULL)
+ destroy_cache_mp_write_session(
+ mp_entry->pending_write_session);
+ }
+
+ free(entry->name);
+ free(entry);
+ TRACE_OUT(destroy_cache_entry);
+}
+
+static void
+clear_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data;
+ struct cache_policy_ *policy;
+ struct cache_policy_item_ *item, *next_item;
+ size_t entry_size;
+ int i;
+
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ entry_size = 0;
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
+ {
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ }
+ entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
+ HASHTABLE_ENTRY_CLEAR(ht_item, data);
+ }
+
+ common_entry->items_size -= entry_size;
+ for (i = 0; i < common_entry->policies_size; ++i) {
+ policy = common_entry->policies[i];
+
+ next_item = NULL;
+ item = policy->get_first_item_func(policy);
+ while (item != NULL) {
+ next_item = policy->get_next_item_func(policy,
+ item);
+ policy->remove_item_func(policy, item);
+ policy->destroy_item_func(item);
+ item = next_item;
+ }
+ }
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if (mp_entry->rs_size == 0) {
+ if (mp_entry->completed_write_session != NULL) {
+ destroy_cache_mp_write_session(
+ mp_entry->completed_write_session);
+ mp_entry->completed_write_session = NULL;
+ }
+
+ memset(&mp_entry->creation_time, 0,
+ sizeof(struct timeval));
+ memset(&mp_entry->last_request_time, 0,
+ sizeof(struct timeval));
+ }
+ }
+}
+
+/*
+ * When passed to the flush_cache_policy, ensures that all old elements are
+ * deleted.
+ */
+static int
+cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
+ struct cache_policy_item_ *item)
+{
+
+ return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
+ entry->common_params.max_lifetime.tv_sec) ? 1: 0);
+}
+
+/*
+ * When passed to the flush_cache_policy, ensures that all elements, that
+ * exceed the size limit, are deleted.
+ */
+static int
+cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
+ struct cache_policy_item_ *item)
+{
+
+ return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
+ : 0);
+}
+
+/*
+ * Removes the elements from the cache entry, while the continue_func returns 1.
+ */
+static void
+flush_cache_policy(struct cache_common_entry_ *entry,
+ struct cache_policy_ *policy,
+ struct cache_policy_ *connected_policy,
+ int (*continue_func)(struct cache_common_entry_ *,
+ struct cache_policy_item_ *))
+{
+ struct cache_policy_item_ *item, *next_item, *connected_item;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data, ht_key;
+ hashtable_index_t hash;
+
+ assert(policy != NULL);
+
+ next_item = NULL;
+ item = policy->get_first_item_func(policy);
+ while ((item != NULL) && (continue_func(entry, item) == 1)) {
+ next_item = policy->get_next_item_func(policy, item);
+
+ connected_item = item->connected_item;
+ policy->remove_item_func(policy, item);
+
+ memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
+ ht_key.key = item->key;
+ ht_key.key_size = item->key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
+ &ht_key);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
+
+ ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
+ ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
+ &ht_key);
+ assert(ht_item_data != NULL);
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
+ --entry->items_size;
+
+ policy->destroy_item_func(item);
+
+ if (connected_item != NULL) {
+ connected_policy->remove_item_func(connected_policy,
+ connected_item);
+ connected_policy->destroy_item_func(connected_item);
+ }
+
+ item = next_item;
+ }
+}
+
+static void
+flush_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_common_entry_ *common_entry;
+ struct cache_policy_ *policy, *connected_policy;
+
+ connected_policy = NULL;
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+ if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
+ (common_entry->common_params.max_lifetime.tv_usec != 0)) {
+
+ policy = common_entry->policies[0];
+ if (common_entry->policies_size > 1)
+ connected_policy = common_entry->policies[1];
+
+ flush_cache_policy(common_entry, policy,
+ connected_policy,
+ cache_lifetime_common_continue_func);
+ }
+
+
+ if ((common_entry->common_params.max_elemsize != 0) &&
+ common_entry->items_size >
+ common_entry->common_params.max_elemsize) {
+
+ if (common_entry->policies_size > 1) {
+ policy = common_entry->policies[1];
+ connected_policy = common_entry->policies[0];
+ } else {
+ policy = common_entry->policies[0];
+ connected_policy = NULL;
+ }
+
+ flush_cache_policy(common_entry, policy,
+ connected_policy,
+ cache_elemsize_common_continue_func);
+ }
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
+ || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
+
+ if (mp_entry->last_request_time.tv_sec -
+ mp_entry->last_request_time.tv_sec >
+ mp_entry->mp_params.max_lifetime.tv_sec)
+ clear_cache_entry(entry);
+ }
+ }
+}
+
+struct cache_ *
+init_cache(struct cache_params const *params)
+{
+ struct cache_ *retval;
+
+ TRACE_IN(init_cache);
+ assert(params != NULL);
+
+ retval = (struct cache_ *)malloc(sizeof(struct cache_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_));
+
+ assert(params != NULL);
+ memcpy(&retval->params, params, sizeof(struct cache_params));
+
+ retval->entries = (struct cache_entry_ **)malloc(
+ sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
+ assert(retval->entries != NULL);
+ memset(retval->entries, 0, sizeof(sizeof(struct cache_entry_ *)
+ * INITIAL_ENTRIES_CAPACITY));
+
+ retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
+ retval->entries_size = 0;
+
+ TRACE_OUT(init_cache);
+ return (retval);
+}
+
+void
+destroy_cache(struct cache_ *the_cache)
+{
+
+ TRACE_IN(destroy_cache);
+ assert(the_cache != NULL);
+
+ if (the_cache->entries != NULL) {
+ size_t i;
+ for (i = 0; i < the_cache->entries_size; ++i)
+ destroy_cache_entry(the_cache->entries[i]);
+
+ free(the_cache->entries);
+ }
+
+ free(the_cache);
+ TRACE_OUT(destroy_cache);
+}
+
+int
+register_cache_entry(struct cache_ *the_cache,
+ struct cache_entry_params const *params)
+{
+ int policies_size;
+ size_t entry_name_size;
+ struct cache_common_entry_ *new_common_entry;
+ struct cache_mp_entry_ *new_mp_entry;
+
+ TRACE_IN(register_cache_entry);
+ assert(the_cache != NULL);
+
+ if (find_cache_entry(the_cache, params->entry_name) != NULL) {
+ TRACE_OUT(register_cache_entry);
+ return (-1);
+ }
+
+ if (the_cache->entries_size == the_cache->entries_capacity) {
+ struct cache_entry_ **new_entries;
+ size_t new_capacity;
+
+ new_capacity = the_cache->entries_capacity +
+ ENTRIES_CAPACITY_STEP;
+ new_entries = (struct cache_entry_ **)malloc(
+ sizeof(struct cache_entry_ *) * new_capacity);
+ assert(new_entries != NULL);
+
+ memset(new_entries, 0, sizeof(struct cache_entry_ *) *
+ new_capacity);
+ memcpy(new_entries, the_cache->entries,
+ sizeof(struct cache_entry_ *)
+ * the_cache->entries_size);
+
+ free(the_cache->entries);
+ the_cache->entries = new_entries;
+ }
+
+ entry_name_size = strlen(params->entry_name);
+ switch (params->entry_type)
+ {
+ case CET_COMMON:
+ new_common_entry = (struct cache_common_entry_ *)malloc(
+ sizeof(struct cache_common_entry_));
+ assert(new_common_entry != NULL);
+ memset(new_common_entry, 0, sizeof(struct cache_common_entry_));
+
+ memcpy(&new_common_entry->common_params, params,
+ sizeof(struct common_cache_entry_params));
+ new_common_entry->params =
+ (struct cache_entry_params *)&new_common_entry->common_params;
+
+ new_common_entry->common_params.entry_name = (char *)malloc(
+ entry_name_size+1);
+ assert(new_common_entry->common_params.entry_name != NULL);
+ memset(new_common_entry->common_params.entry_name, 0,
+ entry_name_size + 1);
+ strncpy(new_common_entry->common_params.entry_name,
+ params->entry_name, entry_name_size);
+ new_common_entry->name =
+ new_common_entry->common_params.entry_name;
+
+ HASHTABLE_INIT(&(new_common_entry->items),
+ struct cache_ht_item_data_, data,
+ new_common_entry->common_params.cache_entries_size);
+
+ if (new_common_entry->common_params.policy == CPT_FIFO)
+ policies_size = 1;
+ else
+ policies_size = 2;
+
+ new_common_entry->policies = (struct cache_policy_ **)malloc(
+ sizeof(struct cache_policy_ *) * policies_size);
+ assert(new_common_entry->policies != NULL);
+ memset(new_common_entry->policies, 0,
+ sizeof(struct cache_policy_ *) * policies_size);
+
+ new_common_entry->policies_size = policies_size;
+ new_common_entry->policies[0] = init_cache_fifo_policy();
+
+ if (policies_size > 1) {
+ switch (new_common_entry->common_params.policy) {
+ case CPT_LRU:
+ new_common_entry->policies[1] =
+ init_cache_lru_policy();
+ break;
+ case CPT_LFU:
+ new_common_entry->policies[1] =
+ init_cache_lfu_policy();
+ break;
+ default:
+ break;
+ }
+ }
+
+ new_common_entry->get_time_func =
+ the_cache->params.get_time_func;
+ the_cache->entries[the_cache->entries_size++] =
+ (struct cache_entry_ *)new_common_entry;
+ break;
+ case CET_MULTIPART:
+ new_mp_entry = (struct cache_mp_entry_ *)malloc(
+ sizeof(struct cache_mp_entry_));
+ assert(new_mp_entry != NULL);
+ memset(new_mp_entry, 0, sizeof(struct cache_mp_entry_));
+
+ memcpy(&new_mp_entry->mp_params, params,
+ sizeof(struct mp_cache_entry_params));
+ new_mp_entry->params =
+ (struct cache_entry_params *)&new_mp_entry->mp_params;
+
+ new_mp_entry->mp_params.entry_name = (char *)malloc(
+ entry_name_size+1);
+ assert(new_mp_entry->mp_params.entry_name != NULL);
+ memset(new_mp_entry->mp_params.entry_name, 0,
+ entry_name_size + 1);
+ strncpy(new_mp_entry->mp_params.entry_name, params->entry_name,
+ entry_name_size);
+ new_mp_entry->name = new_mp_entry->mp_params.entry_name;
+
+ TAILQ_INIT(&new_mp_entry->ws_head);
+ TAILQ_INIT(&new_mp_entry->rs_head);
+
+ new_mp_entry->get_time_func = the_cache->params.get_time_func;
+ the_cache->entries[the_cache->entries_size++] =
+ (struct cache_entry_ *)new_mp_entry;
+ break;
+ }
+
+
+ qsort(the_cache->entries, the_cache->entries_size,
+ sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
+
+ TRACE_OUT(register_cache_entry);
+ return (0);
+}
+
+int
+unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
+{
+ struct cache_entry_ **del_ent;
+
+ TRACE_IN(unregister_cache_entry);
+ assert(the_cache != NULL);
+
+ del_ent = find_cache_entry_p(the_cache, entry_name);
+ if (del_ent != NULL) {
+ destroy_cache_entry(*del_ent);
+ --the_cache->entries_size;
+
+ memmove(del_ent, del_ent + 1,
+ (&(the_cache->entries[--the_cache->entries_size]) -
+ del_ent) * sizeof(struct cache_entry_ *));
+
+ TRACE_OUT(unregister_cache_entry);
+ return (0);
+ } else {
+ TRACE_OUT(unregister_cache_entry);
+ return (-1);
+ }
+}
+
+struct cache_entry_ *
+find_cache_entry(struct cache_ *the_cache, const char *entry_name)
+{
+ struct cache_entry_ **result;
+
+ TRACE_IN(find_cache_entry);
+ result = find_cache_entry_p(the_cache, entry_name);
+
+ if (result == NULL) {
+ TRACE_OUT(find_cache_entry);
+ return (NULL);
+ } else {
+ TRACE_OUT(find_cache_entry);
+ return (*result);
+ }
+}
+
+/*
+ * Tries to read the element with the specified key from the cache. If the
+ * value_size is too small, it will be filled with the proper number, and
+ * the user will need to call cache_read again with the value buffer, that
+ * is large enough.
+ * Function returns 0 on success, -1 on error, and -2 if the value_size is too
+ * small.
+ */
+int
+cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
+ char *value, size_t *value_size)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_data_ item_data, *find_res;
+ struct cache_ht_item_ *item;
+ hashtable_index_t hash;
+ struct cache_policy_item_ *connected_item;
+
+ TRACE_IN(cache_read);
+ assert(entry != NULL);
+ assert(key != NULL);
+ assert(value_size != NULL);
+ assert(entry->params->entry_type == CET_COMMON);
+
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
+ /* can't avoid the cast here */
+ item_data.key = (char *)key;
+ item_data.key_size = key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
+ &item_data);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
+
+ item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
+ find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
+ if (find_res == NULL) {
+ TRACE_OUT(cache_read);
+ return (-1);
+ }
+
+ if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
+ (common_entry->common_params.max_lifetime.tv_usec != 0)) {
+
+ if (find_res->fifo_policy_item->last_request_time.tv_sec -
+ find_res->fifo_policy_item->creation_time.tv_sec >
+ common_entry->common_params.max_lifetime.tv_sec) {
+
+ free(find_res->key);
+ free(find_res->value);
+
+ connected_item =
+ find_res->fifo_policy_item->connected_item;
+ if (connected_item != NULL) {
+ common_entry->policies[1]->remove_item_func(
+ common_entry->policies[1],
+ connected_item);
+ common_entry->policies[1]->destroy_item_func(
+ connected_item);
+ }
+
+ common_entry->policies[0]->remove_item_func(
+ common_entry->policies[0],
+ find_res->fifo_policy_item);
+ common_entry->policies[0]->destroy_item_func(
+ find_res->fifo_policy_item);
+
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
+ --common_entry->items_size;
+ }
+ }
+
+ if ((*value_size < find_res->value_size) || (value == NULL)) {
+ *value_size = find_res->value_size;
+ TRACE_OUT(cache_read);
+ return (-2);
+ }
+
+ *value_size = find_res->value_size;
+ memcpy(value, find_res->value, find_res->value_size);
+
+ ++find_res->fifo_policy_item->request_count;
+ common_entry->get_time_func(
+ &find_res->fifo_policy_item->last_request_time);
+ common_entry->policies[0]->update_item_func(common_entry->policies[0],
+ find_res->fifo_policy_item);
+
+ if (find_res->fifo_policy_item->connected_item != NULL) {
+ connected_item = find_res->fifo_policy_item->connected_item;
+ memcpy(&connected_item->last_request_time,
+ &find_res->fifo_policy_item->last_request_time,
+ sizeof(struct timeval));
+ connected_item->request_count =
+ find_res->fifo_policy_item->request_count;
+
+ common_entry->policies[1]->update_item_func(
+ common_entry->policies[1], connected_item);
+ }
+
+ TRACE_OUT(cache_read);
+ return (0);
+}
+
+/*
+ * Writes the value with the specified key into the cache entry.
+ * Functions returns 0 on success, and -1 on error.
+ */
+int
+cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
+ char const *value, size_t value_size)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_data_ item_data, *find_res;
+ struct cache_ht_item_ *item;
+ hashtable_index_t hash;
+
+ struct cache_policy_ *policy, *connected_policy;
+ struct cache_policy_item_ *policy_item;
+ struct cache_policy_item_ *connected_policy_item;
+
+ TRACE_IN(cache_write);
+ assert(entry != NULL);
+ assert(key != NULL);
+ assert(value != NULL);
+ assert(entry->params->entry_type == CET_COMMON);
+
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
+ /* can't avoid the cast here */
+ item_data.key = (char *)key;
+ item_data.key_size = key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
+ &item_data);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
+
+ item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
+ find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
+ if (find_res != NULL) {
+ TRACE_OUT(cache_write);
+ return (-1);
+ }
+
+ item_data.key = (char *)malloc(key_size);
+ memcpy(item_data.key, key, key_size);
+
+ item_data.value = (char *)malloc(value_size);
+ assert(item_data.value != NULL);
+
+ memcpy(item_data.value, value, value_size);
+ item_data.value_size = value_size;
+
+ policy_item = common_entry->policies[0]->create_item_func();
+ policy_item->key = item_data.key;
+ policy_item->key_size = item_data.key_size;
+ common_entry->get_time_func(&policy_item->creation_time);
+
+ if (common_entry->policies_size > 1) {
+ connected_policy_item =
+ common_entry->policies[1]->create_item_func();
+ memcpy(&connected_policy_item->creation_time,
+ &policy_item->creation_time,
+ sizeof(struct timeval));
+ connected_policy_item->key = policy_item->key;
+ connected_policy_item->key_size = policy_item->key_size;
+
+ connected_policy_item->connected_item = policy_item;
+ policy_item->connected_item = connected_policy_item;
+ }
+
+ item_data.fifo_policy_item = policy_item;
+
+ common_entry->policies[0]->add_item_func(common_entry->policies[0],
+ policy_item);
+ if (common_entry->policies_size > 1)
+ common_entry->policies[1]->add_item_func(
+ common_entry->policies[1], connected_policy_item);
+
+ HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
+ ++common_entry->items_size;
+
+ if ((common_entry->common_params.max_elemsize != 0) &&
+ (common_entry->items_size >
+ common_entry->common_params.max_elemsize)) {
+ if (common_entry->policies_size > 1) {
+ policy = common_entry->policies[1];
+ connected_policy = common_entry->policies[0];
+ } else {
+ policy = common_entry->policies[0];
+ connected_policy = NULL;
+ }
+
+ flush_cache_policy(common_entry, policy, connected_policy,
+ cache_elemsize_common_continue_func);
+ }
+
+ TRACE_OUT(cache_write);
+ return (0);
+}
+
+/*
+ * Initializes the write session for the specified multipart entry. This
+ * session then should be filled with data either committed or abandoned by
+ * using close_cache_mp_write_session or abandon_cache_mp_write_session
+ * respectively.
+ * Returns NULL on errors (when there are too many opened write sessions for
+ * the entry).
+ */
+struct cache_mp_write_session_ *
+open_cache_mp_write_session(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_write_session_ *retval;
+
+ TRACE_IN(open_cache_mp_write_session);
+ assert(entry != NULL);
+ assert(entry->params->entry_type == CET_MULTIPART);
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if ((mp_entry->mp_params.max_sessions > 0) &&
+ (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
+ TRACE_OUT(open_cache_mp_write_session);
+ return (NULL);
+ }
+
+ retval = (struct cache_mp_write_session_ *)malloc(
+ sizeof(struct cache_mp_write_session_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_mp_write_session_));
+
+ TAILQ_INIT(&retval->items);
+ retval->parent_entry = mp_entry;
+
+ TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
+ ++mp_entry->ws_size;
+
+ TRACE_OUT(open_cache_mp_write_session);
+ return (retval);
+}
+
+/*
+ * Writes data to the specified session. Return 0 on success and -1 on errors
+ * (when write session size limit is exceeded).
+ */
+int
+cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
+ size_t data_size)
+{
+ struct cache_mp_data_item_ *new_item;
+
+ TRACE_IN(cache_mp_write);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
+ (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
+ TRACE_OUT(cache_mp_write);
+ return (-1);
+ }
+
+ new_item = (struct cache_mp_data_item_ *)malloc(
+ sizeof(struct cache_mp_data_item_));
+ assert(new_item != NULL);
+ memset(new_item, 0, sizeof(struct cache_mp_data_item_));
+
+ new_item->value = (char *)malloc(data_size);
+ assert(new_item->value != NULL);
+ memcpy(new_item->value, data, data_size);
+ new_item->value_size = data_size;
+
+ TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
+ ++ws->items_size;
+
+ TRACE_OUT(cache_mp_write);
+ return (0);
+}
+
+/*
+ * Abandons the write session and frees all the connected resources.
+ */
+void
+abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ TRACE_IN(abandon_cache_mp_write_session);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
+ --ws->parent_entry->ws_size;
+
+ destroy_cache_mp_write_session(ws);
+ TRACE_OUT(abandon_cache_mp_write_session);
+}
+
+/*
+ * Commits the session to the entry, for which it was created.
+ */
+void
+close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ TRACE_IN(close_cache_mp_write_session);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
+ --ws->parent_entry->ws_size;
+
+ if (ws->parent_entry->completed_write_session == NULL) {
+ /*
+ * If there is no completed session yet, this will be the one
+ */
+ ws->parent_entry->get_time_func(
+ &ws->parent_entry->creation_time);
+ ws->parent_entry->completed_write_session = ws;
+ } else {
+ /*
+ * If there is a completed session, then we'll save our session
+ * as a pending session. If there is already a pending session,
+ * it would be destroyed.
+ */
+ if (ws->parent_entry->pending_write_session != NULL)
+ destroy_cache_mp_write_session(
+ ws->parent_entry->pending_write_session);
+
+ ws->parent_entry->pending_write_session = ws;
+ }
+ TRACE_OUT(close_cache_mp_write_session);
+}
+
+/*
+ * Opens read session for the specified entry. Returns NULL on errors (when
+ * there are no data in the entry, or the data are obsolete).
+ */
+struct cache_mp_read_session_ *
+open_cache_mp_read_session(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_read_session_ *retval;
+
+ TRACE_IN(open_cache_mp_read_session);
+ assert(entry != NULL);
+ assert(entry->params->entry_type == CET_MULTIPART);
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if (mp_entry->completed_write_session == NULL) {
+ TRACE_OUT(open_cache_mp_read_session);
+ return (NULL);
+ }
+
+ if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
+ || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
+ if (mp_entry->last_request_time.tv_sec -
+ mp_entry->last_request_time.tv_sec >
+ mp_entry->mp_params.max_lifetime.tv_sec) {
+ flush_cache_entry(entry);
+ TRACE_OUT(open_cache_mp_read_session);
+ return (NULL);
+ }
+ }
+
+ retval = (struct cache_mp_read_session_ *)malloc(
+ sizeof(struct cache_mp_read_session_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_mp_read_session_));
+
+ retval->parent_entry = mp_entry;
+ retval->current_item = TAILQ_FIRST(
+ &mp_entry->completed_write_session->items);
+
+ TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
+ ++mp_entry->rs_size;
+
+ mp_entry->get_time_func(&mp_entry->last_request_time);
+ TRACE_OUT(open_cache_mp_read_session);
+ return (retval);
+}
+
+/*
+ * Reads the data from the read session - step by step.
+ * Returns 0 on success, -1 on error (when there are no more data), and -2 if
+ * the data_size is too small. In the last case, data_size would be filled
+ * the proper value.
+ */
+int
+cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
+{
+
+ TRACE_IN(cache_mp_read);
+ assert(rs != NULL);
+
+ if (rs->current_item == NULL) {
+ TRACE_OUT(cache_mp_read);
+ return (-1);
+ }
+
+ if (rs->current_item->value_size > *data_size) {
+ *data_size = rs->current_item->value_size;
+ if (data == NULL) {
+ TRACE_OUT(cache_mp_read);
+ return (0);
+ }
+
+ TRACE_OUT(cache_mp_read);
+ return (-2);
+ }
+
+ *data_size = rs->current_item->value_size;
+ memcpy(data, rs->current_item->value, rs->current_item->value_size);
+ rs->current_item = TAILQ_NEXT(rs->current_item, entries);
+
+ TRACE_OUT(cache_mp_read);
+ return (0);
+}
+
+/*
+ * Closes the read session. If there are no more read sessions and there is
+ * a pending write session, it will be committed and old
+ * completed_write_session will be destroyed.
+ */
+void
+close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
+{
+
+ TRACE_IN(close_cache_mp_read_session);
+ assert(rs != NULL);
+ assert(rs->parent_entry != NULL);
+
+ TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
+ --rs->parent_entry->rs_size;
+
+ if ((rs->parent_entry->rs_size == 0) &&
+ (rs->parent_entry->pending_write_session != NULL)) {
+ destroy_cache_mp_write_session(
+ rs->parent_entry->completed_write_session);
+ rs->parent_entry->completed_write_session =
+ rs->parent_entry->pending_write_session;
+ rs->parent_entry->pending_write_session = NULL;
+ }
+
+ destroy_cache_mp_read_session(rs);
+ TRACE_OUT(close_cache_mp_read_session);
+}
+
+int
+transform_cache_entry(struct cache_entry_ *entry,
+ enum cache_transformation_t transformation)
+{
+
+ TRACE_IN(transform_cache_entry);
+ switch (transformation) {
+ case CTT_CLEAR:
+ clear_cache_entry(entry);
+ TRACE_OUT(transform_cache_entry);
+ return (0);
+ case CTT_FLUSH:
+ flush_cache_entry(entry);
+ TRACE_OUT(transform_cache_entry);
+ return (0);
+ default:
+ TRACE_OUT(transform_cache_entry);
+ return (-1);
+ }
+}
+
+int
+transform_cache_entry_part(struct cache_entry_ *entry,
+ enum cache_transformation_t transformation, const char *key_part,
+ size_t key_part_size, enum part_position_t part_position)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data, ht_key;
+
+ struct cache_policy_item_ *item, *connected_item;
+
+ TRACE_IN(transform_cache_entry_part);
+ if (entry->params->entry_type != CET_COMMON) {
+ TRACE_OUT(transform_cache_entry_part);
+ return (-1);
+ }
+
+ if (transformation != CTT_CLEAR) {
+ TRACE_OUT(transform_cache_entry_part);
+ return (-1);
+ }
+
+ memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
+ ht_key.key = (char *)key_part; /* can't avoid casting here */
+ ht_key.key_size = key_part_size;
+
+ common_entry = (struct cache_common_entry_ *)entry;
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ do {
+ ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
+ ht_item, &ht_key,
+ ht_items_fixed_size_left_cmp_func);
+
+ if (ht_item_data != NULL) {
+ item = ht_item_data->fifo_policy_item;
+ connected_item = item->connected_item;
+
+ common_entry->policies[0]->remove_item_func(
+ common_entry->policies[0],
+ item);
+
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
+ ht_item_data);
+ --common_entry->items_size;
+
+ common_entry->policies[0]->destroy_item_func(
+ item);
+ if (common_entry->policies_size == 2) {
+ common_entry->policies[1]->remove_item_func(
+ common_entry->policies[1],
+ connected_item);
+ common_entry->policies[1]->destroy_item_func(
+ connected_item);
+ }
+ }
+ } while (ht_item_data != NULL);
+ }
+
+ TRACE_OUT(transform_cache_entry_part);
+ return (0);
+}
diff --git a/usr.sbin/nscd/cachelib.h b/usr.sbin/nscd/cachelib.h
new file mode 100644
index 0000000..d67e830
--- /dev/null
+++ b/usr.sbin/nscd/cachelib.h
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CACHELIB_H__
+#define __CACHED_CACHELIB_H__
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include "hashtable.h"
+#include "cacheplcs.h"
+
+enum cache_entry_t {
+ CET_COMMON = 0, /* cache item is atomic */
+ CET_MULTIPART /* cache item is formed part by part */
+};
+
+enum cache_transformation_t {
+ CTT_FLUSH = 0, /* flush the cache - delete all obsolete items */
+ CTT_CLEAR = 1 /* delete all items in the cache */
+};
+
+/* cache deletion policy type enum */
+enum cache_policy_t {
+ CPT_FIFO = 0, /* first-in first-out */
+ CPT_LRU = 1, /* least recently used */
+ CPT_LFU = 2 /* least frequently used */
+};
+
+/* multipart sessions can be used for reading and writing */
+enum cache_mp_session_t {
+ CMPT_READ_SESSION,
+ CMPT_WRITE_SESSION
+};
+
+/*
+ * When doing partial transformations of entries (which are applied for
+ * elements with keys, that contain specified buffer in its left or
+ * right part), this enum will show the needed position of the key part.
+ */
+enum part_position_t {
+ KPPT_LEFT,
+ KPPT_RIGHT
+};
+
+/* num_levels attribute is obsolete, i think - user can always emulate it
+ * by using one entry.
+ * get_time_func is needed to have the clocks-independent counter
+ */
+struct cache_params
+{
+ void (*get_time_func)(struct timeval *);
+};
+
+/*
+ * base structure - normal_cache_entry_params and multipart_cache_entry_params
+ * are "inherited" from it
+ */
+struct cache_entry_params
+{
+ enum cache_entry_t entry_type;
+ char *entry_name;
+};
+
+/* params, used for most entries */
+struct common_cache_entry_params
+{
+ /* inherited fields */
+ enum cache_entry_t entry_type;
+
+ /* unique fields */
+ char *entry_name;
+ size_t cache_entries_size;
+
+ size_t max_elemsize; /* if 0 then no check is made */
+ size_t satisf_elemsize; /* if entry size is exceeded,
+ * this number of elements will be left,
+ * others will be deleted */
+ struct timeval max_lifetime; /* if 0 then no check is made */
+ enum cache_policy_t policy; /* policy used for transformations */
+};
+
+/* params, used for multipart entries */
+struct mp_cache_entry_params
+{
+ /* inherited fields */
+ enum cache_entry_t entry_type;
+ char *entry_name;
+
+ /* unique fields */
+ size_t max_elemsize; /* if 0 then no check is made */
+ size_t max_sessions; /* maximum number of active sessions */
+
+ struct timeval max_lifetime; /* maximum elements lifetime */
+};
+
+struct cache_ht_item_data_
+{
+ /* key is the bytes sequence only - not the null-terminated string */
+ char *key;
+ size_t key_size;
+
+ char *value;
+ size_t value_size;
+
+ struct cache_policy_item_ *fifo_policy_item;
+};
+
+struct cache_ht_item_
+{
+ HASHTABLE_ENTRY_HEAD(ht_item_, struct cache_ht_item_data_) data;
+};
+
+struct cache_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+};
+
+struct cache_common_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+
+ struct common_cache_entry_params common_params;
+
+ HASHTABLE_HEAD(cache_ht_, cache_ht_item_) items;
+ size_t items_size;
+
+ /*
+ * Entry always has the FIFO policy, that is used to eliminate old
+ * elements (the ones, with lifetime more than max_lifetime). Besides,
+ * user can specify another policy to be applied, when there are too
+ * many elements in the entry. So policies_size can be 1 or 2.
+ */
+ struct cache_policy_ **policies;
+ size_t policies_size;
+
+ void (*get_time_func)(struct timeval *);
+};
+
+struct cache_mp_data_item_ {
+ char *value;
+ size_t value_size;
+
+ TAILQ_ENTRY(cache_mp_data_item_) entries;
+};
+
+struct cache_mp_write_session_
+{
+ struct cache_mp_entry_ *parent_entry;
+
+ /*
+ * All items are accumulated in this queue. When the session is
+ * committed, they all will be copied to the multipart entry.
+ */
+ TAILQ_HEAD(cache_mp_data_item_head, cache_mp_data_item_) items;
+ size_t items_size;
+
+ TAILQ_ENTRY(cache_mp_write_session_) entries;
+};
+
+struct cache_mp_read_session_
+{
+ struct cache_mp_entry_ *parent_entry;
+ struct cache_mp_data_item_ *current_item;
+
+ TAILQ_ENTRY(cache_mp_read_session_) entries;
+};
+
+struct cache_mp_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+
+ struct mp_cache_entry_params mp_params;
+
+ /* All opened write sessions */
+ TAILQ_HEAD(write_sessions_head, cache_mp_write_session_) ws_head;
+ size_t ws_size;
+
+ /* All opened read sessions */
+ TAILQ_HEAD(read_sessions_head, cache_mp_read_session_) rs_head;
+ size_t rs_size;
+
+ /*
+ * completed_write_session is the committed write sessions. All read
+ * sessions use data from it. If the completed_write_session is out of
+ * date, but still in use by some of the read sessions, the newly
+ * committed write session is stored in the pending_write_session.
+ * In such a case, completed_write_session will be substituted with
+ * pending_write_session as soon as it won't be used by any of
+ * the read sessions.
+ */
+ struct cache_mp_write_session_ *completed_write_session;
+ struct cache_mp_write_session_ *pending_write_session;
+ struct timeval creation_time;
+ struct timeval last_request_time;
+
+ void (*get_time_func)(struct timeval *);
+};
+
+struct cache_
+{
+ struct cache_params params;
+
+ struct cache_entry_ **entries;
+ size_t entries_capacity;
+ size_t entries_size;
+};
+
+/* simple abstractions - for not to write "struct" every time */
+typedef struct cache_ *cache;
+typedef struct cache_entry_ *cache_entry;
+typedef struct cache_mp_write_session_ *cache_mp_write_session;
+typedef struct cache_mp_read_session_ *cache_mp_read_session;
+
+#define INVALID_CACHE (NULL)
+#define INVALID_CACHE_ENTRY (NULL)
+#define INVALID_CACHE_MP_WRITE_SESSION (NULL)
+#define INVALID_CACHE_MP_READ_SESSION (NULL)
+
+/*
+ * NOTE: all cache operations are thread-unsafe. You must ensure thread-safety
+ * externally, by yourself.
+ */
+
+/* cache initialization/destruction routines */
+extern cache init_cache(struct cache_params const *);
+extern void destroy_cache(cache);
+
+/* cache entries manipulation routines */
+extern int register_cache_entry(cache, struct cache_entry_params const *);
+extern int unregister_cache_entry(cache, const char *);
+extern cache_entry find_cache_entry(cache, const char *);
+
+/* read/write operations used on common entries */
+extern int cache_read(cache_entry, const char *, size_t, char *, size_t *);
+extern int cache_write(cache_entry, const char *, size_t, char const *, size_t);
+
+/* read/write operations used on multipart entries */
+extern cache_mp_write_session open_cache_mp_write_session(cache_entry);
+extern int cache_mp_write(cache_mp_write_session, char *, size_t);
+extern void abandon_cache_mp_write_session(cache_mp_write_session);
+extern void close_cache_mp_write_session(cache_mp_write_session);
+
+extern cache_mp_read_session open_cache_mp_read_session(cache_entry);
+extern int cache_mp_read(cache_mp_read_session, char *, size_t *);
+extern void close_cache_mp_read_session(cache_mp_read_session);
+
+/* transformation routines */
+extern int transform_cache_entry(cache_entry, enum cache_transformation_t);
+extern int transform_cache_entry_part(cache_entry, enum cache_transformation_t,
+ const char *, size_t, enum part_position_t);
+
+#endif
diff --git a/usr.sbin/nscd/cacheplcs.c b/usr.sbin/nscd/cacheplcs.c
new file mode 100644
index 0000000..a7ee38d
--- /dev/null
+++ b/usr.sbin/nscd/cacheplcs.c
@@ -0,0 +1,590 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <string.h>
+#include "cacheplcs.h"
+#include "debug.h"
+
+static void cache_fifo_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lfu_policy_add_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static struct cache_policy_item_ * cache_lfu_policy_create_item(void);
+static void cache_lfu_policy_destroy_item(struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_first_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_last_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_next_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_prev_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static void cache_lfu_policy_remove_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lfu_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lru_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_queue_policy_add_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static struct cache_policy_item_ * cache_queue_policy_create_item();
+static void cache_queue_policy_destroy_item(struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_first_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_last_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_next_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_prev_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static void cache_queue_policy_remove_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void destroy_cache_queue_policy(struct cache_queue_policy_ *);
+static struct cache_queue_policy_ *init_cache_queue_policy(void);
+
+/*
+ * All cache_queue_policy_XXX functions below will be used to fill
+ * the cache_queue_policy structure. They implement the most functionality of
+ * LRU and FIFO policies. LRU and FIFO policies are actually the
+ * cache_queue_policy_ with cache_update_item function changed.
+ */
+static struct cache_policy_item_ *
+cache_queue_policy_create_item()
+{
+ struct cache_queue_policy_item_ *retval;
+
+ TRACE_IN(cache_queue_policy_create_item);
+ retval = (struct cache_queue_policy_item_ *)malloc(
+ sizeof(struct cache_queue_policy_item_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_queue_policy_item_));
+
+ TRACE_OUT(cache_queue_policy_create_item);
+ return ((struct cache_policy_item_ *)retval);
+}
+
+static void
+cache_queue_policy_destroy_item(struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_queue_policy_destroy_item);
+ assert(item != NULL);
+ free(item);
+ TRACE_OUT(cache_queue_policy_destroy_item);
+}
+
+static void
+cache_queue_policy_add_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_add_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+ TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_queue_policy_add_item);
+}
+
+static void
+cache_queue_policy_remove_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_remove_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_queue_policy_remove_item);
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_first_item(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(cache_queue_policy_get_first_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ TRACE_OUT(cache_queue_policy_get_first_item);
+ return ((struct cache_policy_item_ *)TAILQ_FIRST(&queue_policy->head));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_last_item(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(cache_queue_policy_get_last_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ TRACE_OUT(cache_queue_policy_get_last_item);
+ return ((struct cache_policy_item_ *)TAILQ_LAST(&queue_policy->head,
+ cache_queue_policy_head_));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_next_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_get_next_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TRACE_OUT(cache_queue_policy_get_next_item);
+ return ((struct cache_policy_item_ *)TAILQ_NEXT(queue_item, entries));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_prev_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_get_prev_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TRACE_OUT(cache_queue_policy_get_prev_item);
+ return ((struct cache_policy_item_ *)TAILQ_PREV(queue_item,
+ cache_queue_policy_head_, entries));
+}
+
+/*
+ * Initializes cache_queue_policy_ by filling the structure with the functions
+ * pointers, defined above
+ */
+static struct cache_queue_policy_ *
+init_cache_queue_policy(void)
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_queue_policy);
+ retval = (struct cache_queue_policy_ *)malloc(
+ sizeof(struct cache_queue_policy_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_queue_policy_));
+
+ retval->parent_data.create_item_func = cache_queue_policy_create_item;
+ retval->parent_data.destroy_item_func = cache_queue_policy_destroy_item;
+
+ retval->parent_data.add_item_func = cache_queue_policy_add_item;
+ retval->parent_data.remove_item_func = cache_queue_policy_remove_item;
+
+ retval->parent_data.get_first_item_func =
+ cache_queue_policy_get_first_item;
+ retval->parent_data.get_last_item_func =
+ cache_queue_policy_get_last_item;
+ retval->parent_data.get_next_item_func =
+ cache_queue_policy_get_next_item;
+ retval->parent_data.get_prev_item_func =
+ cache_queue_policy_get_prev_item;
+
+ TAILQ_INIT(&retval->head);
+ TRACE_OUT(init_cache_queue_policy);
+ return (retval);
+}
+
+static void
+destroy_cache_queue_policy(struct cache_queue_policy_ *queue_policy)
+{
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(destroy_cache_queue_policy);
+ while (!TAILQ_EMPTY(&queue_policy->head)) {
+ queue_item = TAILQ_FIRST(&queue_policy->head);
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ cache_queue_policy_destroy_item(
+ (struct cache_policy_item_ *)queue_item);
+ }
+ free(queue_policy);
+ TRACE_OUT(destroy_cache_queue_policy);
+}
+
+/*
+ * Makes cache_queue_policy_ behave like FIFO policy - we don't do anything,
+ * when the cache element is updated. So it always stays in its initial
+ * position in the queue - that is exactly the FIFO functionality.
+ */
+static void
+cache_fifo_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_fifo_policy_update_item);
+ /* policy and item arguments are ignored */
+ TRACE_OUT(cache_fifo_policy_update_item);
+}
+
+struct cache_policy_ *
+init_cache_fifo_policy()
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_fifo_policy);
+ retval = init_cache_queue_policy();
+ retval->parent_data.update_item_func = cache_fifo_policy_update_item;
+
+ TRACE_OUT(init_cache_fifo_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_fifo_policy(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(destroy_cache_fifo_policy);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ destroy_cache_queue_policy(queue_policy);
+ TRACE_OUT(destroy_cache_fifo_policy);
+}
+
+/*
+ * Makes cache_queue_policy_ behave like LRU policy. On each update, cache
+ * element is moved to the end of the queue - so it would be deleted in last
+ * turn. That is exactly the LRU policy functionality.
+ */
+static void
+cache_lru_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_lru_policy_update_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_lru_policy_update_item);
+}
+
+struct cache_policy_ *
+init_cache_lru_policy()
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_lru_policy);
+ retval = init_cache_queue_policy();
+ retval->parent_data.update_item_func = cache_lru_policy_update_item;
+
+ TRACE_OUT(init_cache_lru_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_lru_policy(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(destroy_cache_lru_policy);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ destroy_cache_queue_policy(queue_policy);
+ TRACE_OUT(destroy_cache_lru_policy);
+}
+
+/*
+ * LFU (least frequently used) policy implementation differs much from the
+ * LRU and FIFO (both based on cache_queue_policy_). Almost all cache_policy_
+ * functions are implemented specifically for this policy. The idea of this
+ * policy is to represent frequency (real number) as the integer number and
+ * use it as the index in the array. Each array's element is
+ * the list of elements. For example, if we have the 100-elements
+ * array for this policy, the elements with frequency 0.1 (calls per-second)
+ * would be in 10th element of the array.
+ */
+static struct cache_policy_item_ *
+cache_lfu_policy_create_item(void)
+{
+ struct cache_lfu_policy_item_ *retval;
+
+ TRACE_IN(cache_lfu_policy_create_item);
+ retval = (struct cache_lfu_policy_item_ *)malloc(
+ sizeof(struct cache_lfu_policy_item_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_lfu_policy_item_));
+
+ TRACE_OUT(cache_lfu_policy_create_item);
+ return ((struct cache_policy_item_ *)retval);
+}
+
+static void
+cache_lfu_policy_destroy_item(struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_lfu_policy_destroy_item);
+ assert(item != NULL);
+ free(item);
+ TRACE_OUT(cache_lfu_policy_destroy_item);
+}
+
+/*
+ * When placed in the LFU policy queue for the first time, the maximum
+ * frequency is assigned to the element
+ */
+static void
+cache_lfu_policy_add_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(cache_lfu_policy_add_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ lfu_item->frequency = CACHELIB_MAX_FREQUENCY - 1;
+ TAILQ_INSERT_HEAD(&(lfu_policy->groups[CACHELIB_MAX_FREQUENCY - 1]),
+ lfu_item, entries);
+ TRACE_OUT(cache_lfu_policy_add_item);
+}
+
+/*
+ * On each update the frequency of the element is recalculated and, if it
+ * changed, the element would be moved to the another place in the array.
+ */
+static void
+cache_lfu_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int index;
+
+ TRACE_IN(cache_lfu_policy_update_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ /*
+ * We calculate the square of the request_count to avoid grouping of
+ * all elements at the start of the array (for example, if array size is
+ * 100 and most of its elements has frequency below the 0.01, they
+ * all would be grouped in the first array's position). Other
+ * techniques should be used here later to ensure, that elements are
+ * equally distributed in the array and not grouped in its beginning.
+ */
+ if (lfu_item->parent_data.last_request_time.tv_sec !=
+ lfu_item->parent_data.creation_time.tv_sec) {
+ index = ((double)lfu_item->parent_data.request_count *
+ (double)lfu_item->parent_data.request_count /
+ (lfu_item->parent_data.last_request_time.tv_sec -
+ lfu_item->parent_data.creation_time.tv_sec + 1)) *
+ CACHELIB_MAX_FREQUENCY;
+ if (index >= CACHELIB_MAX_FREQUENCY)
+ index = CACHELIB_MAX_FREQUENCY - 1;
+ } else
+ index = CACHELIB_MAX_FREQUENCY - 1;
+
+ TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
+ entries);
+ lfu_item->frequency = index;
+ TAILQ_INSERT_HEAD(&(lfu_policy->groups[index]), lfu_item, entries);
+
+ TRACE_OUT(cache_lfu_policy_update_item);
+}
+
+static void
+cache_lfu_policy_remove_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(cache_lfu_policy_remove_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
+ entries);
+ TRACE_OUT(cache_lfu_policy_remove_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_first_item(struct cache_policy_ *policy)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_first_item);
+ lfu_item = NULL;
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ break;
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_first_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_last_item(struct cache_policy_ *policy)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_last_item);
+ lfu_item = NULL;
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = CACHELIB_MAX_FREQUENCY - 1; i >= 0; --i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
+ cache_lfu_policy_group_);
+ break;
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_last_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_next_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_next_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = TAILQ_NEXT((struct cache_lfu_policy_item_ *)item, entries);
+ if (lfu_item == NULL)
+ {
+ for (i = ((struct cache_lfu_policy_item_ *)item)->frequency + 1;
+ i < CACHELIB_MAX_FREQUENCY; ++i) {
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ break;
+ }
+ }
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_next_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_prev_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_prev_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = TAILQ_PREV((struct cache_lfu_policy_item_ *)item,
+ cache_lfu_policy_group_, entries);
+ if (lfu_item == NULL)
+ {
+ for (i = ((struct cache_lfu_policy_item_ *)item)->frequency - 1;
+ i >= 0; --i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
+ cache_lfu_policy_group_);
+ break;
+ }
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_prev_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+/*
+ * Initializes the cache_policy_ structure by filling it with appropriate
+ * functions pointers
+ */
+struct cache_policy_ *
+init_cache_lfu_policy()
+{
+ int i;
+ struct cache_lfu_policy_ *retval;
+
+ TRACE_IN(init_cache_lfu_policy);
+ retval = (struct cache_lfu_policy_ *)malloc(
+ sizeof(struct cache_lfu_policy_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cache_lfu_policy_));
+
+ retval->parent_data.create_item_func = cache_lfu_policy_create_item;
+ retval->parent_data.destroy_item_func = cache_lfu_policy_destroy_item;
+
+ retval->parent_data.add_item_func = cache_lfu_policy_add_item;
+ retval->parent_data.update_item_func = cache_lfu_policy_update_item;
+ retval->parent_data.remove_item_func = cache_lfu_policy_remove_item;
+
+ retval->parent_data.get_first_item_func =
+ cache_lfu_policy_get_first_item;
+ retval->parent_data.get_last_item_func =
+ cache_lfu_policy_get_last_item;
+ retval->parent_data.get_next_item_func =
+ cache_lfu_policy_get_next_item;
+ retval->parent_data.get_prev_item_func =
+ cache_lfu_policy_get_prev_item;
+
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
+ TAILQ_INIT(&(retval->groups[i]));
+
+ TRACE_OUT(init_cache_lfu_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_lfu_policy(struct cache_policy_ *policy)
+{
+ int i;
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(destroy_cache_lfu_policy);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) {
+ while (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ TAILQ_REMOVE(&(lfu_policy->groups[i]), lfu_item,
+ entries);
+ cache_lfu_policy_destroy_item(
+ (struct cache_policy_item_ *)lfu_item);
+ }
+ }
+ free(policy);
+ TRACE_OUT(destroy_cache_lfu_policy);
+}
diff --git a/usr.sbin/nscd/cacheplcs.h b/usr.sbin/nscd/cacheplcs.h
new file mode 100644
index 0000000..36997f5
--- /dev/null
+++ b/usr.sbin/nscd/cacheplcs.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CACHEPLCS_H__
+#define __CACHED_CACHEPLCS_H__
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+/* common policy definitions */
+#define CACHELIB_MAX_FREQUENCY 100
+
+/*
+ * cache_policy_item_ represents some abstract cache element in the policy
+ * queue. connected_item pointers to the corresponding cache_policy_item_ in
+ * another policy queue.
+ */
+struct cache_policy_item_
+{
+ char *key;
+ size_t key_size;
+
+ size_t request_count;
+ struct timeval last_request_time;
+ struct timeval creation_time;
+
+ struct cache_policy_item_ *connected_item;
+};
+
+/*
+ * cache_policy_ represents an abstract policy queue. It can be customized by
+ * setting appropriate function pointers
+ */
+struct cache_policy_
+{
+ struct cache_policy_item_* (*create_item_func)();
+ void (*destroy_item_func)(struct cache_policy_item_ *);
+
+ void (*add_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+ void (*remove_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+ void (*update_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+
+ struct cache_policy_item_ *(*get_first_item_func)(
+ struct cache_policy_ *);
+ struct cache_policy_item_ *(*get_last_item_func)(
+ struct cache_policy_ *);
+ struct cache_policy_item_ *(*get_next_item_func)(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+ struct cache_policy_item_ *(*get_prev_item_func)(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+};
+
+/*
+ * LFU cache policy item "inherited" from cache_policy_item_ structure
+ */
+struct cache_lfu_policy_item_
+{
+ struct cache_policy_item_ parent_data;
+ int frequency;
+
+ TAILQ_ENTRY(cache_lfu_policy_item_) entries;
+};
+
+TAILQ_HEAD(cache_lfu_policy_group_, cache_lfu_policy_item_);
+
+/*
+ * LFU policy queue "inherited" from cache_policy_.
+ */
+struct cache_lfu_policy_
+{
+ struct cache_policy_ parent_data;
+ struct cache_lfu_policy_group_ groups[CACHELIB_MAX_FREQUENCY];
+};
+
+/*
+ * LRU and FIFO policies item "inherited" from cache_policy_item_
+ */
+struct cache_queue_policy_item_
+{
+ struct cache_policy_item_ parent_data;
+ TAILQ_ENTRY(cache_queue_policy_item_) entries;
+};
+
+/*
+ * LRU and FIFO policies "inherited" from cache_policy_
+ */
+struct cache_queue_policy_
+{
+ struct cache_policy_ parent_data;
+ TAILQ_HEAD(cache_queue_policy_head_, cache_queue_policy_item_) head;
+};
+
+typedef struct cache_queue_policy_ cache_fifo_policy_;
+typedef struct cache_queue_policy_ cache_lru_policy_;
+
+/* fifo policy routines */
+extern struct cache_policy_ *init_cache_fifo_policy();
+extern void destroy_cache_fifo_policy(struct cache_policy_ *);
+
+/* lru policy routines */
+extern struct cache_policy_ *init_cache_lru_policy();
+extern void destroy_cache_lru_policy(struct cache_policy_ *);
+
+/* lfu policy routines */
+extern struct cache_policy_ *init_cache_lfu_policy();
+extern void destroy_cache_lfu_policy(struct cache_policy_ *);
+
+#endif
diff --git a/usr.sbin/nscd/config.c b/usr.sbin/nscd/config.c
new file mode 100644
index 0000000..bc3eb49
--- /dev/null
+++ b/usr.sbin/nscd/config.c
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+
+/*
+ * Default entries, which always exist in the configuration
+ */
+const char *c_default_entries[6] = {
+ NSDB_PASSWD,
+ NSDB_GROUP,
+ NSDB_HOSTS,
+ NSDB_SERVICES,
+ NSDB_PROTOCOLS,
+ NSDB_RPC
+ };
+
+static int configuration_entry_cmp(const void *, const void *);
+static int configuration_entry_sort_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_sort_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_part_cmp(const void *, const void *);
+static struct configuration_entry *create_configuration_entry(const char *,
+ struct timeval const *, struct timeval const *,
+ struct common_cache_entry_params const *,
+ struct common_cache_entry_params const *,
+ struct mp_cache_entry_params const *);
+
+static int
+configuration_entry_sort_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((*((struct configuration_entry **)e1))->name,
+ (*((struct configuration_entry **)e2))->name
+ ));
+}
+
+static int
+configuration_entry_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((const char *)e1,
+ (*((struct configuration_entry **)e2))->name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((*((cache_entry *)e1))->params->entry_name,
+ (*((cache_entry *)e2))->params->entry_name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((const char *)e1,
+ (*((cache_entry *)e2))->params->entry_name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2)
+{
+ return (strncmp((const char *)e1,
+ (*((cache_entry *)e2))->params->entry_name,
+ strlen((const char *)e1)
+ ));
+}
+
+static struct configuration_entry *
+create_configuration_entry(const char *name,
+ struct timeval const *common_timeout,
+ struct timeval const *mp_timeout,
+ struct common_cache_entry_params const *positive_params,
+ struct common_cache_entry_params const *negative_params,
+ struct mp_cache_entry_params const *mp_params)
+{
+ struct configuration_entry *retval;
+ size_t size;
+ int res;
+
+ TRACE_IN(create_configuration_entry);
+ assert(name != NULL);
+ assert(positive_params != NULL);
+ assert(negative_params != NULL);
+ assert(mp_params != NULL);
+
+ retval = (struct configuration_entry *)malloc(
+ sizeof(struct configuration_entry));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct configuration_entry));
+
+ res = pthread_mutex_init(&retval->positive_cache_lock, NULL);
+ if (res != 0) {
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create positive cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ res = pthread_mutex_init(&retval->negative_cache_lock, NULL);
+ if (res != 0) {
+ pthread_mutex_destroy(&retval->positive_cache_lock);
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create negative cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ res = pthread_mutex_init(&retval->mp_cache_lock, NULL);
+ if (res != 0) {
+ pthread_mutex_destroy(&retval->positive_cache_lock);
+ pthread_mutex_destroy(&retval->negative_cache_lock);
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create negative cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ memcpy(&retval->positive_cache_params, positive_params,
+ sizeof(struct common_cache_entry_params));
+ memcpy(&retval->negative_cache_params, negative_params,
+ sizeof(struct common_cache_entry_params));
+ memcpy(&retval->mp_cache_params, mp_params,
+ sizeof(struct mp_cache_entry_params));
+
+ size = strlen(name);
+ retval->name = (char *)malloc(size + 1);
+ assert(retval->name != NULL);
+ memset(retval->name, 0, size + 1);
+ memcpy(retval->name, name, size);
+
+ memcpy(&retval->common_query_timeout, common_timeout,
+ sizeof(struct timeval));
+ memcpy(&retval->mp_query_timeout, mp_timeout,
+ sizeof(struct timeval));
+
+ asprintf(&retval->positive_cache_params.entry_name, "%s+", name);
+ assert(retval->positive_cache_params.entry_name != NULL);
+
+ asprintf(&retval->negative_cache_params.entry_name, "%s-", name);
+ assert(retval->negative_cache_params.entry_name != NULL);
+
+ asprintf(&retval->mp_cache_params.entry_name, "%s*", name);
+ assert(retval->mp_cache_params.entry_name != NULL);
+
+ TRACE_OUT(create_configuration_entry);
+ return (retval);
+}
+
+/*
+ * Creates configuration entry and fills it with default values
+ */
+struct configuration_entry *
+create_def_configuration_entry(const char *name)
+{
+ struct common_cache_entry_params positive_params, negative_params;
+ struct mp_cache_entry_params mp_params;
+ struct timeval default_common_timeout, default_mp_timeout;
+
+ struct configuration_entry *res = NULL;
+
+ TRACE_IN(create_def_configuration_entry);
+ memset(&positive_params, 0,
+ sizeof(struct common_cache_entry_params));
+ positive_params.entry_type = CET_COMMON;
+ positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE;
+ positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE;
+ positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2;
+ positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME;
+ positive_params.policy = CPT_LRU;
+
+ memcpy(&negative_params, &positive_params,
+ sizeof(struct common_cache_entry_params));
+ negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE;
+ negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2;
+ negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME;
+ negative_params.policy = CPT_FIFO;
+
+ memset(&default_common_timeout, 0, sizeof(struct timeval));
+ default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT;
+
+ memset(&default_mp_timeout, 0, sizeof(struct timeval));
+ default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT;
+
+ memset(&mp_params, 0,
+ sizeof(struct mp_cache_entry_params));
+ mp_params.entry_type = CET_MULTIPART;
+ mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE;
+ mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE;
+ mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME;
+
+ res = create_configuration_entry(name, &default_common_timeout,
+ &default_mp_timeout, &positive_params, &negative_params,
+ &mp_params);
+
+ TRACE_OUT(create_def_configuration_entry);
+ return (res);
+}
+
+void
+destroy_configuration_entry(struct configuration_entry *entry)
+{
+ TRACE_IN(destroy_configuration_entry);
+ assert(entry != NULL);
+ pthread_mutex_destroy(&entry->positive_cache_lock);
+ pthread_mutex_destroy(&entry->negative_cache_lock);
+ pthread_mutex_destroy(&entry->mp_cache_lock);
+ free(entry->name);
+ free(entry->positive_cache_params.entry_name);
+ free(entry->negative_cache_params.entry_name);
+ free(entry->mp_cache_params.entry_name);
+ free(entry->mp_cache_entries);
+ free(entry);
+ TRACE_OUT(destroy_configuration_entry);
+}
+
+int
+add_configuration_entry(struct configuration *config,
+ struct configuration_entry *entry)
+{
+ TRACE_IN(add_configuration_entry);
+ assert(entry != NULL);
+ assert(entry->name != NULL);
+ if (configuration_find_entry(config, entry->name) != NULL) {
+ TRACE_OUT(add_configuration_entry);
+ return (-1);
+ }
+
+ if (config->entries_size == config->entries_capacity) {
+ struct configuration_entry **new_entries;
+
+ config->entries_capacity *= 2;
+ new_entries = (struct configuration_entry **)malloc(
+ sizeof(struct configuration_entry *) *
+ config->entries_capacity);
+ assert(new_entries != NULL);
+ memset(new_entries, 0, sizeof(struct configuration_entry *) *
+ config->entries_capacity);
+ memcpy(new_entries, config->entries,
+ sizeof(struct configuration_entry *) *
+ config->entries_size);
+
+ free(config->entries);
+ config->entries = new_entries;
+ }
+
+ config->entries[config->entries_size++] = entry;
+ qsort(config->entries, config->entries_size,
+ sizeof(struct configuration_entry *),
+ configuration_entry_sort_cmp);
+
+ TRACE_OUT(add_configuration_entry);
+ return (0);
+}
+
+size_t
+configuration_get_entries_size(struct configuration *config)
+{
+ TRACE_IN(configuration_get_entries_size);
+ assert(config != NULL);
+ TRACE_OUT(configuration_get_entries_size);
+ return (config->entries_size);
+}
+
+struct configuration_entry *
+configuration_get_entry(struct configuration *config, size_t index)
+{
+ TRACE_IN(configuration_get_entry);
+ assert(config != NULL);
+ assert(index < config->entries_size);
+ TRACE_OUT(configuration_get_entry);
+ return (config->entries[index]);
+}
+
+struct configuration_entry *
+configuration_find_entry(struct configuration *config,
+ const char *name)
+{
+ struct configuration_entry **retval;
+
+ TRACE_IN(configuration_find_entry);
+
+ retval = bsearch(name, config->entries, config->entries_size,
+ sizeof(struct configuration_entry *), configuration_entry_cmp);
+ TRACE_OUT(configuration_find_entry);
+
+ return ((retval != NULL) ? *retval : NULL);
+}
+
+/*
+ * All multipart cache entries are stored in the configuration_entry in the
+ * sorted array (sorted by names). The 3 functions below manage this array.
+ */
+
+int
+configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry,
+ cache_entry c_entry)
+{
+ cache_entry *new_mp_entries, *old_mp_entries;
+
+ TRACE_IN(configuration_entry_add_mp_cache_entry);
+ ++config_entry->mp_cache_entries_size;
+ new_mp_entries = (cache_entry *)malloc(sizeof(cache_entry) *
+ config_entry->mp_cache_entries_size);
+ assert(new_mp_entries != NULL);
+ new_mp_entries[0] = c_entry;
+
+ if (config_entry->mp_cache_entries_size - 1 > 0) {
+ memcpy(new_mp_entries + 1,
+ config_entry->mp_cache_entries,
+ (config_entry->mp_cache_entries_size - 1) *
+ sizeof(cache_entry));
+ }
+
+ old_mp_entries = config_entry->mp_cache_entries;
+ config_entry->mp_cache_entries = new_mp_entries;
+ free(old_mp_entries);
+
+ qsort(config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry),
+ configuration_entry_cache_mp_sort_cmp);
+
+ TRACE_OUT(configuration_entry_add_mp_cache_entry);
+ return (0);
+}
+
+cache_entry
+configuration_entry_find_mp_cache_entry(
+ struct configuration_entry *config_entry, const char *mp_name)
+{
+ cache_entry *result;
+
+ TRACE_IN(configuration_entry_find_mp_cache_entry);
+ result = bsearch(mp_name, config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry), configuration_entry_cache_mp_cmp);
+
+ if (result == NULL) {
+ TRACE_OUT(configuration_entry_find_mp_cache_entry);
+ return (NULL);
+ } else {
+ TRACE_OUT(configuration_entry_find_mp_cache_entry);
+ return (*result);
+ }
+}
+
+/*
+ * Searches for all multipart entries with names starting with mp_name.
+ * Needed for cache flushing.
+ */
+int
+configuration_entry_find_mp_cache_entries(
+ struct configuration_entry *config_entry, const char *mp_name,
+ cache_entry **start, cache_entry **finish)
+{
+ cache_entry *result;
+
+ TRACE_IN(configuration_entry_find_mp_cache_entries);
+ result = bsearch(mp_name, config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry), configuration_entry_cache_mp_part_cmp);
+
+ if (result == NULL) {
+ TRACE_OUT(configuration_entry_find_mp_cache_entries);
+ return (-1);
+ }
+
+ *start = result;
+ *finish = result + 1;
+
+ while (*start != config_entry->mp_cache_entries) {
+ if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0)
+ *start = *start - 1;
+ else
+ break;
+ }
+
+ while (*finish != config_entry->mp_cache_entries +
+ config_entry->mp_cache_entries_size) {
+
+ if (configuration_entry_cache_mp_part_cmp(
+ mp_name, *finish) == 0)
+ *finish = *finish + 1;
+ else
+ break;
+ }
+
+ TRACE_OUT(configuration_entry_find_mp_cache_entries);
+ return (0);
+}
+
+/*
+ * Configuration entry uses rwlock to handle access to its fields.
+ */
+void
+configuration_lock_rdlock(struct configuration *config)
+{
+ TRACE_IN(configuration_lock_rdlock);
+ pthread_rwlock_rdlock(&config->rwlock);
+ TRACE_OUT(configuration_lock_rdlock);
+}
+
+void
+configuration_lock_wrlock(struct configuration *config)
+{
+ TRACE_IN(configuration_lock_wrlock);
+ pthread_rwlock_wrlock(&config->rwlock);
+ TRACE_OUT(configuration_lock_wrlock);
+}
+
+void
+configuration_unlock(struct configuration *config)
+{
+ TRACE_IN(configuration_unlock);
+ pthread_rwlock_unlock(&config->rwlock);
+ TRACE_OUT(configuration_unlock);
+}
+
+/*
+ * Configuration entry uses 3 mutexes to handle cache operations. They are
+ * acquired by configuration_lock_entry and configuration_unlock_entry
+ * functions.
+ */
+void
+configuration_lock_entry(struct configuration_entry *entry,
+ enum config_entry_lock_type lock_type)
+{
+ TRACE_IN(configuration_lock_entry);
+ assert(entry != NULL);
+
+ switch (lock_type) {
+ case CELT_POSITIVE:
+ pthread_mutex_lock(&entry->positive_cache_lock);
+ break;
+ case CELT_NEGATIVE:
+ pthread_mutex_lock(&entry->negative_cache_lock);
+ break;
+ case CELT_MULTIPART:
+ pthread_mutex_lock(&entry->mp_cache_lock);
+ break;
+ default:
+ /* should be unreachable */
+ break;
+ }
+ TRACE_OUT(configuration_lock_entry);
+}
+
+void
+configuration_unlock_entry(struct configuration_entry *entry,
+ enum config_entry_lock_type lock_type)
+{
+ TRACE_IN(configuration_unlock_entry);
+ assert(entry != NULL);
+
+ switch (lock_type) {
+ case CELT_POSITIVE:
+ pthread_mutex_unlock(&entry->positive_cache_lock);
+ break;
+ case CELT_NEGATIVE:
+ pthread_mutex_unlock(&entry->negative_cache_lock);
+ break;
+ case CELT_MULTIPART:
+ pthread_mutex_unlock(&entry->mp_cache_lock);
+ break;
+ default:
+ /* should be unreachable */
+ break;
+ }
+ TRACE_OUT(configuration_unlock_entry);
+}
+
+struct configuration *
+init_configuration(void)
+{
+ struct configuration *retval;
+
+ TRACE_IN(init_configuration);
+ retval = (struct configuration *)malloc(sizeof(struct configuration));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct configuration));
+
+ retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
+ retval->entries = (struct configuration_entry **)malloc(
+ sizeof(struct configuration_entry *) *
+ retval->entries_capacity);
+ assert(retval->entries != NULL);
+ memset(retval->entries, 0, sizeof(struct configuration_entry *) *
+ retval->entries_capacity);
+
+ pthread_rwlock_init(&retval->rwlock, NULL);
+
+ TRACE_OUT(init_configuration);
+ return (retval);
+}
+
+void
+fill_configuration_defaults(struct configuration *config)
+{
+ size_t len, i;
+
+ TRACE_IN(fill_configuration_defaults);
+ assert(config != NULL);
+
+ if (config->socket_path != NULL)
+ free(config->socket_path);
+
+ len = strlen(DEFAULT_SOCKET_PATH);
+ config->socket_path = (char *)malloc(len + 1);
+ assert(config->socket_path != NULL);
+ memset(config->socket_path, 0, len + 1);
+ memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len);
+
+ len = strlen(DEFAULT_PIDFILE_PATH);
+ config->pidfile_path = (char *)malloc(len + 1);
+ assert(config->pidfile_path != NULL);
+ memset(config->pidfile_path, 0, len + 1);
+ memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len);
+
+ config->socket_mode = S_IFSOCK | S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ config->force_unlink = 1;
+
+ config->query_timeout = DEFAULT_QUERY_TIMEOUT;
+ config->threads_num = DEFAULT_THREADS_NUM;
+
+ for (i = 0; i < config->entries_size; ++i)
+ destroy_configuration_entry(config->entries[i]);
+ config->entries_size = 0;
+
+ TRACE_OUT(fill_configuration_defaults);
+}
+
+void
+destroy_configuration(struct configuration *config)
+{
+ int i;
+ TRACE_IN(destroy_configuration);
+ assert(config != NULL);
+ free(config->pidfile_path);
+ free(config->socket_path);
+
+ for (i = 0; i < config->entries_size; ++i)
+ destroy_configuration_entry(config->entries[i]);
+ free(config->entries);
+
+ pthread_rwlock_destroy(&config->rwlock);
+ free(config);
+ TRACE_OUT(destroy_configuration);
+}
diff --git a/usr.sbin/nscd/config.h b/usr.sbin/nscd/config.h
new file mode 100644
index 0000000..b54dc9b
--- /dev/null
+++ b/usr.sbin/nscd/config.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CONFIG_H__
+#define __CACHED_CONFIG_H__
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <nsswitch.h>
+#include <unistd.h>
+#include "cachelib.h"
+
+#define DEFAULT_QUERY_TIMEOUT 8
+#define DEFAULT_THREADS_NUM 8
+
+#define DEFAULT_COMMON_ENTRY_TIMEOUT 10
+#define DEFAULT_MP_ENTRY_TIMEOUT 60
+#define DEFAULT_CACHE_HT_SIZE 257
+
+#define INITIAL_ENTRIES_CAPACITY 8
+#define DEFAULT_SOCKET_PATH "/var/run/cached"
+#define DEFAULT_PIDFILE_PATH "/var/run/cached.pid"
+
+#define DEFAULT_POSITIVE_ELEMENTS_SIZE (2048)
+#define DEFAULT_POSITIVE_LIFETIME (3600)
+
+#define DEFAULT_NEGATIVE_ELEMENTS_SIZE (2048)
+#define DEFAULT_NEGATIVE_LIFETIME (60)
+
+#define DEFAULT_MULTIPART_ELEMENTS_SIZE (1024 * 8)
+#define DEFAULT_MULITPART_SESSIONS_SIZE (1024)
+#define DEFAULT_MULITPART_LIFETIME (3600)
+
+extern const char *c_default_entries[6];
+
+/*
+ * Configuration entry represents the details of each cache entry in the
+ * config file (i.e. passwd or group). Its purpose also is to acquire locks
+ * of three different types (for usual read/write caching, for multipart
+ * caching and for caching of the negative results) for that cache entry.
+ */
+struct configuration_entry {
+ struct common_cache_entry_params positive_cache_params;
+ struct common_cache_entry_params negative_cache_params;
+ struct mp_cache_entry_params mp_cache_params;
+
+ /*
+ * configuration_entry holds pointers for all actual cache_entries,
+ * which are used for it. There is one for positive caching, one for
+ * for negative caching, and several (one per each euid/egid) for
+ * multipart caching.
+ */
+ cache_entry positive_cache_entry;
+ cache_entry negative_cache_entry;
+
+ cache_entry *mp_cache_entries;
+ size_t mp_cache_entries_size;
+
+ struct timeval common_query_timeout;
+ struct timeval mp_query_timeout;
+
+ char *name;
+ pthread_mutex_t positive_cache_lock;
+ pthread_mutex_t negative_cache_lock;
+ pthread_mutex_t mp_cache_lock;
+
+ int perform_actual_lookups;
+ int enabled;
+};
+
+/*
+ * Contains global configuration options and array of all configuration entries
+ */
+struct configuration {
+ char *pidfile_path;
+ char *socket_path;
+
+ struct configuration_entry **entries;
+ size_t entries_capacity;
+ size_t entries_size;
+
+ pthread_rwlock_t rwlock;
+
+ mode_t socket_mode;
+ int force_unlink;
+ int query_timeout;
+
+ int threads_num;
+};
+
+enum config_entry_lock_type {
+ CELT_POSITIVE,
+ CELT_NEGATIVE,
+ CELT_MULTIPART
+};
+
+extern struct configuration *init_configuration(void);
+extern void destroy_configuration(struct configuration *);
+extern void fill_configuration_defaults(struct configuration *);
+
+extern int add_configuration_entry(struct configuration *,
+ struct configuration_entry *);
+extern struct configuration_entry *create_def_configuration_entry(
+ const char *);
+extern void destroy_configuration_entry(struct configuration_entry *);
+extern size_t configuration_get_entries_size(struct configuration *);
+extern struct configuration_entry *configuration_get_entry(
+ struct configuration *, size_t);
+extern struct configuration_entry *configuration_find_entry(
+ struct configuration *, const char *);
+
+extern int configuration_entry_add_mp_cache_entry(struct configuration_entry *,
+ cache_entry);
+extern cache_entry configuration_entry_find_mp_cache_entry(
+ struct configuration_entry *,
+ const char *);
+extern int configuration_entry_find_mp_cache_entries(
+ struct configuration_entry *, const char *, cache_entry **,
+ cache_entry **);
+
+extern void configuration_lock_rdlock(struct configuration *config);
+extern void configuration_lock_wrlock(struct configuration *config);
+extern void configuration_unlock(struct configuration *config);
+
+extern void configuration_lock_entry(struct configuration_entry *,
+ enum config_entry_lock_type);
+extern void configuration_unlock_entry(struct configuration_entry *,
+ enum config_entry_lock_type);
+
+#endif
diff --git a/usr.sbin/nscd/debug.c b/usr.sbin/nscd/debug.c
new file mode 100644
index 0000000..420c517
--- /dev/null
+++ b/usr.sbin/nscd/debug.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include "debug.h"
+
+static int trace_level = 0;
+static int trace_level_bk = 0;
+
+void
+__trace_in(const char *s, const char *f, int l)
+{
+ int i;
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level; ++i)
+ printf("\t");
+
+ printf("=> %s\n", s);
+ }
+
+ ++trace_level;
+}
+
+void
+__trace_point(const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= %s: %d\n", f, l);
+ }
+}
+
+void
+__trace_msg(const char *msg, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= MSG %s, %s: %d\n", msg, f, l);
+ }
+}
+
+void
+__trace_ptr(const char *desc, const void *p, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= PTR %s: %p, %s: %d\n", desc, p, f, l);
+ }
+}
+
+void
+__trace_int(const char *desc, int i, const char *f, int l)
+{
+ int j;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (j = 0; j < trace_level - 1; ++j)
+ printf("\t");
+
+ printf("= INT %s: %i, %s: %d\n",desc, i, f, l);
+ }
+}
+
+void
+__trace_str(const char *desc, const char *s, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= STR %s: '%s', %s: %d\n", desc, s, f, l);
+ }
+}
+
+void
+__trace_out(const char *s, const char *f, int l)
+{
+ int i;
+
+ --trace_level;
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level; ++i)
+ printf("\t");
+
+ printf("<= %s\n", s);
+ }
+}
+
+void
+__trace_on()
+{
+ trace_level = trace_level_bk;
+ trace_level_bk = 0;
+}
+
+void
+__trace_off()
+{
+ trace_level_bk = trace_level;
+ trace_level = 1024;
+}
diff --git a/usr.sbin/nscd/debug.h b/usr.sbin/nscd/debug.h
new file mode 100644
index 0000000..320e10f
--- /dev/null
+++ b/usr.sbin/nscd/debug.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_DEBUG_H__
+#define __CACHED_DEBUG_H__
+
+#define TRACE_WANTED 32
+
+/* #ifndef NDEBUG */
+#if 0
+#define TRACE_IN(x) __trace_in(#x, __FILE__, __LINE__)
+#define TRACE_POINT() __trace_point(__FILE__, __LINE__)
+#define TRACE_MSG(x) __trace_msg(x, __FILE__, __LINE__)
+#define TRACE_PTR(p) __trace_ptr(#p, p, __FILE__, __LINE__)
+#define TRACE_INT(i) __trace_int(#i, i, __FILE__, __LINE__)
+#define TRACE_STR(s) __trace_str(#s, s, __FILE__, __LINE__)
+#define TRACE_OUT(x) __trace_out(#x, __FILE__, __LINE__)
+#define TRACE_ON() __trace_on()
+#define TRACE_OFF() __trace_off()
+#else
+#define TRACE_IN(x)
+#define TRACE_POINT()
+#define TRACE_MSG(x)
+#define TRACE_PTR(p)
+#define TRACE_INT(i)
+#define TRACE_STR(s)
+#define TRACE_OUT(x)
+#define TRACE_ON()
+#define TRACE_OFF()
+#endif
+
+extern void __trace_in(const char *, const char *, int);
+extern void __trace_point(const char *, int);
+extern void __trace_msg(const char *, const char *, int);
+extern void __trace_ptr(const char *, const void *, const char *, int);
+extern void __trace_int(const char *, int, const char *, int);
+extern void __trace_str(const char *, const char *, const char *, int);
+extern void __trace_out(const char *, const char *, int);
+extern void __trace_on();
+extern void __trace_off();
+
+#endif
diff --git a/usr.sbin/nscd/hashtable.h b/usr.sbin/nscd/hashtable.h
new file mode 100644
index 0000000..86dad9f
--- /dev/null
+++ b/usr.sbin/nscd/hashtable.h
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHELIB_HASHTABLE_H__
+#define __CACHELIB_HASHTABLE_H__
+
+#include <search.h>
+#include <string.h>
+
+#define HASHTABLE_INITIAL_ENTRIES_CAPACITY 8
+typedef int hashtable_index_t;
+
+/*
+ * This file contains queue.h-like macro definitions for hash tables.
+ * Hash table is organized as an array of the specified size of the user
+ * defined (with HASTABLE_ENTRY_HEAD) structures. Each hash table
+ * entry (user defined structure) stores its elements in the sorted array.
+ * You can place elements into the hash table, retrieve elements with
+ * specified key, traverse through all elements, and delete them.
+ * New elements are placed into the hash table by using the compare and
+ * hashing functions, provided by the user.
+ */
+
+/*
+ * Defines the hash table entry structure, that uses specified type of
+ * elements.
+ */
+#define HASHTABLE_ENTRY_HEAD(name, type) struct name { \
+ type *values; \
+ size_t capacity; \
+ size_t size; \
+}
+
+/*
+ * Defines the hash table structure, which uses the specified type of entries.
+ * The only restriction for entries is that is that they should have the field,
+ * defined with HASHTABLE_ENTRY_HEAD macro.
+ */
+#define HASHTABLE_HEAD(name, entry) struct name { \
+ struct entry *entries; \
+ size_t entries_size; \
+}
+
+#define HASHTABLE_ENTRIES_COUNT(table) ((table)->entries_size)
+
+/*
+ * Unlike most of queue.h data types, hash tables can not be initialized
+ * statically - so there is no HASHTABLE_HEAD_INITIALIZED macro.
+ */
+#define HASHTABLE_INIT(table, type, field, _entries_size) \
+ do { \
+ hashtable_index_t var; \
+ (table)->entries = (void *)malloc( \
+ sizeof(*(table)->entries) * (_entries_size)); \
+ memset((table)->entries, 0, \
+ sizeof(*(table)->entries) * (_entries_size)); \
+ (table)->entries_size = (_entries_size); \
+ for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
+ (table)->entries[var].field.capacity = \
+ HASHTABLE_INITIAL_ENTRIES_CAPACITY; \
+ (table)->entries[var].field.size = 0; \
+ (table)->entries[var].field.values = (type *)malloc(\
+ sizeof(type) * \
+ HASHTABLE_INITIAL_ENTRIES_CAPACITY); \
+ assert((table)->entries[var].field.values != NULL);\
+ } \
+ } while (0)
+
+/*
+ * All initialized hashtables should be destroyed with this macro.
+ */
+#define HASHTABLE_DESTROY(table, field) \
+ do { \
+ hashtable_index_t var; \
+ for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
+ free((table)->entries[var].field.values); \
+ } \
+ } while (0)
+
+#define HASHTABLE_GET_ENTRY(table, hash) (&((table)->entries[hash]))
+
+/*
+ * Traverses through all hash table entries
+ */
+#define HASHTABLE_FOREACH(table, var) \
+ for ((var) = &((table)->entries[0]); \
+ (var) < &((table)->entries[HASHTABLE_ENTRIES_COUNT(table)]);\
+ ++(var))
+
+/*
+ * Traverses through all elements of the specified hash table entry
+ */
+#define HASHTABLE_ENTRY_FOREACH(entry, field, var) \
+ for ((var) = &((entry)->field.values[0]); \
+ (var) < &((entry)->field.values[(entry)->field.size]); \
+ ++(var))
+
+#define HASHTABLE_ENTRY_CLEAR(entry, field) \
+ ((entry)->field.size = 0)
+
+#define HASHTABLE_ENTRY_SIZE(entry, field) \
+ ((entry)->field.size)
+
+#define HASHTABLE_ENTRY_CAPACITY(entry, field) \
+ ((entry)->field.capacity)
+
+#define HASHTABLE_ENTRY_CAPACITY_INCREASE(entry, field, type) \
+ (entry)->field.capacity *= 2; \
+ (entry)->field.values = (type *)realloc((entry)->field.values, \
+ (entry)->field.capacity * sizeof(type));
+
+#define HASHTABLE_ENTRY_CAPACITY_DECREASE(entry, field, type) \
+ (entry)->field.capacity /= 2; \
+ (entry)->field.values = (type *)realloc((entry)->field.values, \
+ (entry)->field.capacity * sizeof(type));
+
+/*
+ * Generates prototypes for the hash table functions
+ */
+#define HASHTABLE_PROTOTYPE(name, entry_, type) \
+hashtable_index_t name##_CALCULATE_HASH(struct name *, type *); \
+void name##_ENTRY_STORE(struct entry_*, type *); \
+type *name##_ENTRY_FIND(struct entry_*, type *); \
+type *name##_ENTRY_FIND_SPECIAL(struct entry_ *, type *, \
+ int (*) (const void *, const void *)); \
+void name##_ENTRY_REMOVE(struct entry_*, type *);
+
+/*
+ * Generates implementations of the hash table functions
+ */
+#define HASHTABLE_GENERATE(name, entry_, type, field, HASH, CMP) \
+hashtable_index_t name##_CALCULATE_HASH(struct name *table, type *data) \
+{ \
+ \
+ return HASH(data, table->entries_size); \
+} \
+ \
+void name##_ENTRY_STORE(struct entry_ *the_entry, type *data) \
+{ \
+ \
+ if (the_entry->field.size == the_entry->field.capacity) \
+ HASHTABLE_ENTRY_CAPACITY_INCREASE(the_entry, field, type);\
+ \
+ memcpy(&(the_entry->field.values[the_entry->field.size++]), \
+ data, \
+ sizeof(type)); \
+ qsort(the_entry->field.values, the_entry->field.size, \
+ sizeof(type), CMP); \
+} \
+ \
+type *name##_ENTRY_FIND(struct entry_ *the_entry, type *key) \
+{ \
+ \
+ return ((type *)bsearch(key, the_entry->field.values, \
+ the_entry->field.size, sizeof(type), CMP)); \
+} \
+ \
+type *name##_ENTRY_FIND_SPECIAL(struct entry_ *the_entry, type *key, \
+ int (*compar) (const void *, const void *)) \
+{ \
+ return ((type *)bsearch(key, the_entry->field.values, \
+ the_entry->field.size, sizeof(type), compar)); \
+} \
+ \
+void name##_ENTRY_REMOVE(struct entry_ *the_entry, type *del_elm) \
+{ \
+ \
+ memmove(del_elm, del_elm + 1, \
+ (&the_entry->field.values[--the_entry->field.size] - del_elm) *\
+ sizeof(type)); \
+}
+
+/*
+ * Macro definitions below wrap the functions, generaed with
+ * HASHTABLE_GENERATE macro. You should use them and avoid using generated
+ * functions directly.
+ */
+#define HASHTABLE_CALCULATE_HASH(name, table, data) \
+ (name##_CALCULATE_HASH((table), data))
+
+#define HASHTABLE_ENTRY_STORE(name, entry, data) \
+ name##_ENTRY_STORE((entry), data)
+
+#define HASHTABLE_ENTRY_FIND(name, entry, key) \
+ (name##_ENTRY_FIND((entry), (key)))
+
+#define HASHTABLE_ENTRY_FIND_SPECIAL(name, entry, key, cmp) \
+ (name##_ENTRY_FIND_SPECIAL((entry), (key), (cmp)))
+
+#define HASHTABLE_ENTRY_REMOVE(name, entry, del_elm) \
+ name##_ENTRY_REMOVE((entry), (del_elm))
+
+#endif
diff --git a/usr.sbin/nscd/log.c b/usr.sbin/nscd/log.c
new file mode 100644
index 0000000..053930a
--- /dev/null
+++ b/usr.sbin/nscd/log.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "log.h"
+
+void
+__log_msg(int level, const char *sender, const char *message, ...)
+{
+ va_list ap;
+ char *fmessage;
+
+ fmessage = NULL;
+ va_start(ap, message);
+ vasprintf(&fmessage, message, ap);
+ va_end(ap);
+ assert(fmessage != NULL);
+
+ printf("M%d from %s: %s\n", level, sender, fmessage);
+#ifndef NO_SYSLOG
+ if (level == 0)
+ syslog(LOG_INFO, "cached message (from %s): %s", sender,
+ fmessage);
+#endif
+ free(fmessage);
+}
+
+void
+__log_err(int level, const char *sender, const char *error, ...)
+{
+ va_list ap;
+ char *ferror;
+
+ ferror = NULL;
+ va_start(ap, error);
+ vasprintf(&ferror, error, ap);
+ va_end(ap);
+ assert(ferror != NULL);
+
+ printf("E%d from %s: %s\n", level, sender, ferror);
+
+#ifndef NO_SYSLOG
+ if (level == 0)
+ syslog(LOG_ERR, "cached error (from %s): %s", sender, ferror);
+#endif
+ free(ferror);
+}
diff --git a/usr.sbin/nscd/log.h b/usr.sbin/nscd/log.h
new file mode 100644
index 0000000..8d665a4
--- /dev/null
+++ b/usr.sbin/nscd/log.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_LOG_H__
+#define __CACHED_LOG_H__
+
+#define LOG_MSG_1(sender, msg, ...) __log_msg(1, sender, msg, ##__VA_ARGS__)
+#define LOG_MSG_2(sender, msg, ...) __log_msg(2, sender, msg, ##__VA_ARGS__)
+#define LOG_MSG_3(sender, msg, ...) __log_msg(3, sedner, msg, ##__VA_ARGS__)
+
+#define LOG_ERR_1(sender, err, ...) __log_err(1, sender, err, ##__VA_ARGS__)
+#define LOG_ERR_2(sender, err, ...) __log_err(2, sender, err, ##__VA_ARGS__)
+#define LOG_ERR_3(sender, err, ...) __log_err(3, sender, err, ##__VA_ARGS__)
+
+extern void __log_msg(int, const char *, const char *, ...);
+extern void __log_err(int, const char *, const char *, ...);
+
+#endif
diff --git a/usr.sbin/nscd/mp_rs_query. b/usr.sbin/nscd/mp_rs_query.
new file mode 100644
index 0000000..f468afa
--- /dev/null
+++ b/usr.sbin/nscd/mp_rs_query.
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_MP_RS_QUERY_H__
+#define __CACHED_MP_RS_QUERY_H__
+
+extern int on_mp_read_session_request_read1(struct query_state *);
+
+#endif
diff --git a/usr.sbin/nscd/mp_rs_query.c b/usr.sbin/nscd/mp_rs_query.c
new file mode 100644
index 0000000..ae0f3bb
--- /dev/null
+++ b/usr.sbin/nscd/mp_rs_query.c
@@ -0,0 +1,537 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_rs_query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_read_session_close_notification(struct query_state *);
+static void on_mp_read_session_destroy(struct query_state *);
+static int on_mp_read_session_mapper(struct query_state *);
+/* int on_mp_read_session_request_read1(struct query_state *); */
+static int on_mp_read_session_request_read2(struct query_state *);
+static int on_mp_read_session_request_process(struct query_state *);
+static int on_mp_read_session_response_write1(struct query_state *);
+static int on_mp_read_session_read_request_process(struct query_state *);
+static int on_mp_read_session_read_response_write1(struct query_state *);
+static int on_mp_read_session_read_response_write2(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_read_session_destroy(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_read_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_read_session(
+ (cache_mp_read_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_read_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart read session initiation
+ * requests.
+ * - on_mp_read_session_request_read1 and on_mp_read_session_request_read2 read
+ * the request itself
+ * - on_mp_read_session_request_process processes it
+ * - on_mp_read_session_response_write1 sends the response
+ */
+int
+on_mp_read_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_READ_SESSION_REQUEST);
+ c_mp_rs_request = get_cache_mp_read_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_rs_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_rs_request->entry_length)) {
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_rs_request->entry = (char *)malloc(
+ c_mp_rs_request->entry_length + 1);
+ assert(c_mp_rs_request->entry != NULL);
+ memset(c_mp_rs_request->entry, 0,
+ c_mp_rs_request->entry_length + 1);
+
+ qstate->kevent_watermark = c_mp_rs_request->entry_length;
+ qstate->process_func = on_mp_read_session_request_read2;
+ }
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_read_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_request_read2);
+ c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_rs_request->entry,
+ c_mp_rs_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_read_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_read_session_request_process;
+ TRACE_OUT(on_mp_read_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_read_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ struct cache_mp_read_session_response *c_mp_rs_response;
+ cache_mp_read_session rs;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ char *buffer;
+ size_t buffer_size;
+ cache_mp_write_session ws;
+ struct agent *lookup_agent;
+ struct multipart_agent *mp_agent;
+ void *mdata;
+ int res;
+
+ TRACE_IN(on_mp_read_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_READ_SESSION_RESPONSE);
+ c_mp_rs_response = get_cache_mp_read_session_response(
+ &qstate->response);
+ c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_rs_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_rs_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_session_request",
+ "can't find configuration entry '%s'."
+ " aborting request", c_mp_rs_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_rs_response->error_code = EACCES;
+
+ LOG_ERR_2("read_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_rs_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ dec_cache_entry_name = strdup(
+ qstate->config_entry->mp_cache_params.entry_name);
+ else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_rs_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.entry_name);
+ }
+
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache, dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if ((c_entry == INVALID_CACHE) &&
+ (qstate->config_entry->perform_actual_lookups != 0))
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ if (c_entry != INVALID_CACHE_ENTRY) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ rs = open_cache_mp_read_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ if ((rs == INVALID_CACHE_MP_READ_SESSION) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ lookup_agent = find_agent(s_agent_table,
+ c_mp_rs_request->entry, MULTIPART_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == MULTIPART_AGENT)) {
+ mp_agent = (struct multipart_agent *)
+ lookup_agent;
+ mdata = mp_agent->mp_init_func();
+
+ /*
+ * Multipart agents read the whole snapshot
+ * of the data at one time.
+ */
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ if (ws != NULL) {
+ do {
+ buffer = NULL;
+ res = mp_agent->mp_lookup_func(&buffer,
+ &buffer_size,
+ mdata);
+
+ if ((res & NS_TERMINATE) &&
+ (buffer != NULL)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+ if (cache_mp_write(ws, buffer,
+ buffer_size) != 0) {
+ abandon_cache_mp_write_session(ws);
+ ws = NULL;
+ }
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+
+ free(buffer);
+ buffer = NULL;
+ } else {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+ close_cache_mp_write_session(ws);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+
+ free(buffer);
+ buffer = NULL;
+ }
+ } while ((res & NS_TERMINATE) &&
+ (ws != NULL));
+ }
+
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ rs = open_cache_mp_read_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ }
+
+ if (rs == INVALID_CACHE_MP_READ_SESSION)
+ c_mp_rs_response->error_code = -1;
+ else {
+ qstate->mdata = rs;
+ qstate->destroy_func = on_mp_read_session_destroy;
+
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ } else
+ c_mp_rs_response->error_code = -1;
+
+fin:
+ qstate->process_func = on_mp_read_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_read_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_read_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_response *c_mp_rs_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_response_write1);
+ c_mp_rs_response = get_cache_mp_read_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_rs_response->error_code,
+ sizeof(int));
+
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_read_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_rs_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_read_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_read_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_read_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_read_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_READ_SESSION_READ_REQUEST:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_read_session_read_request_process;
+ break;
+ case CET_MP_READ_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_read_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_3("on_mp_read_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart read sessions read
+ * requests. User doesn't have to pass any kind of data, besides the
+ * request identificator itself. So we don't need any XXX_read functions and
+ * start with the XXX_process function.
+ * - on_mp_read_session_read_request_process processes it
+ * - on_mp_read_session_read_response_write1 and
+ * on_mp_read_session_read_response_write2 sends the response
+ */
+static int
+on_mp_read_session_read_request_process(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+
+ TRACE_IN(on_mp_read_session_response_process);
+ init_comm_element(&qstate->response, CET_MP_READ_SESSION_READ_RESPONSE);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ read_response->error_code = cache_mp_read(
+ (cache_mp_read_session)qstate->mdata, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == 0) {
+ read_response->data = (char *)malloc(read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_mp_read(
+ (cache_mp_read_session)qstate->mdata,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_read_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_read_session_response_process);
+ return (0);
+}
+
+static int
+on_mp_read_session_read_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_read_response_write1);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ LOG_ERR_3("on_mp_read_session_read_response_write1",
+ "write failed");
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_mp_read_session_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_read_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ return (0);
+}
+
+static int
+on_mp_read_session_read_response_write2(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_read_response_write2);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_read_response_write2",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_read_response_write2);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+
+ TRACE_OUT(on_mp_read_session_read_response_write2);
+ return (0);
+}
+
+/*
+ * Handles session close notification by calling close_cache_mp_read_session
+ * function.
+ */
+static int
+on_mp_read_session_close_notification(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_read_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_read_session((cache_mp_read_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = NULL;
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_read_session_close_notification);
+ return (0);
+}
diff --git a/usr.sbin/nscd/mp_rs_query.h b/usr.sbin/nscd/mp_rs_query.h
new file mode 100644
index 0000000..f468afa
--- /dev/null
+++ b/usr.sbin/nscd/mp_rs_query.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_MP_RS_QUERY_H__
+#define __CACHED_MP_RS_QUERY_H__
+
+extern int on_mp_read_session_request_read1(struct query_state *);
+
+#endif
diff --git a/usr.sbin/nscd/mp_ws_query.c b/usr.sbin/nscd/mp_ws_query.c
new file mode 100644
index 0000000..d7aeb49
--- /dev/null
+++ b/usr.sbin/nscd/mp_ws_query.c
@@ -0,0 +1,548 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_write_session_abandon_notification(struct query_state *);
+static int on_mp_write_session_close_notification(struct query_state *);
+static void on_mp_write_session_destroy(struct query_state *);
+static int on_mp_write_session_mapper(struct query_state *);
+/* int on_mp_write_session_request_read1(struct query_state *); */
+static int on_mp_write_session_request_read2(struct query_state *);
+static int on_mp_write_session_request_process(struct query_state *);
+static int on_mp_write_session_response_write1(struct query_state *);
+static int on_mp_write_session_write_request_read1(struct query_state *);
+static int on_mp_write_session_write_request_read2(struct query_state *);
+static int on_mp_write_session_write_request_process(struct query_state *);
+static int on_mp_write_session_write_response_write1(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_write_session_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_write_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session(
+ (cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_write_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart write session initiation
+ * requests.
+ * - on_mp_write_session_request_read1 and on_mp_write_session_request_read2
+ * read the request itself
+ * - on_mp_write_session_request_process processes it
+ * - on_mp_write_session_response_write1 sends the response
+ */
+int
+on_mp_write_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_REQUEST);
+ c_mp_ws_request = get_cache_mp_write_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_ws_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_ws_request->entry_length)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "invalid entry_length value");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_ws_request->entry = (char *)malloc(
+ c_mp_ws_request->entry_length + 1);
+ assert(c_mp_ws_request->entry != NULL);
+ memset(c_mp_ws_request->entry, 0,
+ c_mp_ws_request->entry_length + 1);
+
+ qstate->kevent_watermark = c_mp_ws_request->entry_length;
+ qstate->process_func = on_mp_write_session_request_read2;
+ }
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read2);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_ws_request->entry,
+ c_mp_ws_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_request_process;
+
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ cache_mp_write_session ws;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ TRACE_IN(on_mp_write_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_WRITE_SESSION_RESPONSE);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_ws_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_ws_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_session_request",
+ "can't find configuration entry '%s'. "
+ "aborting request", c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_ws_response->error_code = EACCES;
+
+ LOG_ERR_2("write_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ c_mp_ws_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_session_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", c_mp_ws_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_ws_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ /*
+ * All multipart entries are separated by their name decorations.
+ * For one configuration entry there will be a lot of multipart
+ * cache entries - each with its own decorated name.
+ */
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.entry_name);
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if (c_entry == INVALID_CACHE_ENTRY)
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ assert(c_entry != NULL);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ if (ws == INVALID_CACHE_MP_WRITE_SESSION)
+ c_mp_ws_response->error_code = -1;
+ else {
+ qstate->mdata = ws;
+ qstate->destroy_func = on_mp_write_session_destroy;
+
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+fin:
+ qstate->process_func = on_mp_write_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_response_write1);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_ws_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_ws_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_write_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_write_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func =
+ on_mp_write_session_write_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_abandon_notification;
+ break;
+ case CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_2("on_mp_write_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart write sessions write
+ * requests.
+ * - on_mp_write_session_write_request_read1 and
+ * on_mp_write_session_write_request_read2 read the request itself
+ * - on_mp_write_session_write_request_process processes it
+ * - on_mp_write_session_write_response_write1 sends the response
+ */
+static int
+on_mp_write_session_write_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read1);
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->data_size,
+ sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->data_size)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "invalid data_size value");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ write_request->data = (char *)malloc(write_request->data_size);
+ assert(write_request->data != NULL);
+ memset(write_request->data, 0, write_request->data_size);
+
+ qstate->kevent_watermark = write_request->data_size;
+ qstate->process_func = on_mp_write_session_write_request_read2;
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read2);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_write_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_write_request_process;
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ struct cache_mp_write_session_write_response *write_response;
+
+ TRACE_IN(on_mp_write_session_write_request_process);
+ init_comm_element(&qstate->response,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ write_response->error_code = cache_mp_write(
+ (cache_mp_write_session)qstate->mdata,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_write_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_write_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_response_write1);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_write_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (-1);
+ }
+
+ if (write_response->error_code == 0) {
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = 0;
+ }
+
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (0);
+}
+
+/*
+ * Handles abandon notifications. Destroys the session by calling the
+ * abandon_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_abandon_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_abandon_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_abandon_notification);
+ return (0);
+}
+
+/*
+ * Handles close notifications. Commits the session by calling
+ * the close_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_close_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_close_notification);
+ return (0);
+}
+
+cache_entry register_new_mp_cache_entry(struct query_state *qstate,
+ const char *dec_cache_entry_name)
+{
+ cache_entry c_entry;
+ char *en_bkp;
+
+ TRACE_IN(register_new_mp_cache_entry);
+ c_entry = INVALID_CACHE_ENTRY;
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ configuration_lock_wrlock(s_configuration);
+ en_bkp = qstate->config_entry->mp_cache_params.entry_name;
+ qstate->config_entry->mp_cache_params.entry_name =
+ (char *)dec_cache_entry_name;
+ register_cache_entry(s_cache, (struct cache_entry_params *)
+ &qstate->config_entry->mp_cache_params);
+ qstate->config_entry->mp_cache_params.entry_name = en_bkp;
+ configuration_unlock(s_configuration);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ configuration_entry_add_mp_cache_entry(qstate->config_entry,
+ c_entry);
+
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ TRACE_OUT(register_new_mp_cache_entry);
+ return (c_entry);
+}
diff --git a/usr.sbin/nscd/mp_ws_query.h b/usr.sbin/nscd/mp_ws_query.h
new file mode 100644
index 0000000..ba77665
--- /dev/null
+++ b/usr.sbin/nscd/mp_ws_query.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_MP_WS_QUERY_H__
+#define __CACHED_MP_WS_QUERY_H__
+
+extern int on_mp_write_session_request_read1(struct query_state *);
+extern cache_entry register_new_mp_cache_entry(struct query_state *,
+ const char *);
+
+#endif
diff --git a/usr.sbin/nscd/nscd.8 b/usr.sbin/nscd/nscd.8
new file mode 100644
index 0000000..5a8693a
--- /dev/null
+++ b/usr.sbin/nscd/nscd.8
@@ -0,0 +1,148 @@
+.\" Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+
+.Dd October 20, 2005
+.Dt CACHED 8
+.Os
+.Sh NAME
+.Nm cached
+.Nd caching server daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl nst
+.Op Fl i Ar cachename
+.Op Fl I Ar cachename
+.Sh DESCRIPTION
+.Nm
+- is the system caching daemon.
+It can cache almost all types of data and is basically intended to be used
+with
+.Pa nsswitch
+subsystem.
+The cache is actually per-user. It means that each user can work only with the
+cached data, that were cached by him and can't poison the cache of other users.
+.Nm
+supports 2 types of caching:
+.Bl -tag -width Pair
+.It Sy Type
+.Sy Description
+.It Common caching
+Each cached element is the the key+value pair.
+This type of caching supports policies, which are applied, when maximum
+number of cached elements is exceeded.
+3 policies are available:
+.Pa FIFO
+(first in - first out),
+.Pa LRU
+(least recently used) and
+.Pa LFU
+(least frequently used).
+This type of caching is used with
+.Fn getXXXbyname
+- like functions.
+.It Multipart caching
+Each cached element is the part of the elements sequence.
+This type of caching is intended to be used with
+.Fn getXXXent
+- like functions.
+.El
+.Pp
+.Nm
+is able not only to cache elements, but to perform the actual nsswitch
+lookups by itself. To enable this feature use
+.Pa perform-actual-lookups
+parameter in the
+.Xr cached.conf 5
+.Pp
+.Nm
+recognizes the following runtime options:
+.Bl -tag -width indent
+.It Fl n
+Do not daemonize.
+.Nm
+doesn't fork and doesn't disconnect itself from the terminal.
+.It Fl s
+Single-threaded mode.
+Forces using only one thread for all processing purposes (it overrides
+the
+.Pa threads
+parameter in the
+.Xr cached.conf 5
+file).
+.It Fl t
+Trace mode.
+All trace messages would be written to the stdout.
+This mode is usually used with
+.Fl n
+and
+.Fl s
+flags for debugging purposes.
+.It Fl i Ar cachename
+Invalidates personal cache. When specified,
+.Nm
+acts as the administration tool. It asks the already
+running
+.Nm
+to invalidate the specified part of the cache of the
+calling user. For example, you may want sometimes
+to invalidate your
+.Pa hosts
+cache. You can specify
+.Pa all
+as the
+.Pa cachename
+to invalidate your personal cache as a whole. You
+can't use this option for the cachename, for which
+the
+.Pa perform-actual-lookups
+option is enabled.
+.It Fl I Ar cachename
+Invalidates the cache for every user. When specified,
+.Nm
+acts as the administration tool. It asks the already
+running
+.Nm
+to invalidate the specified part of the cache for
+every user. You can specify
+.Pa all
+as the
+.Pa cachename
+to invalidate the whole cache. Only root can use this
+option.
+.El
+.Sh FILES
+.Xr cached.conf 5
+.Sh SEE ALSO
+.Xr cached.conf 5
+.Xr nsswitch.conf 5
+.Xr nsdispatch 3
+.Sh "AUTHORS"
+Michael Bushkov
+.Aq bushman@rsu.ru
+.Sh "BUGS"
+To report bugs or suggestions please mail me
+.Aq bushman@rsu.ru
diff --git a/usr.sbin/nscd/nscd.c b/usr.sbin/nscd/nscd.c
new file mode 100644
index 0000000..3219d22
--- /dev/null
+++ b/usr.sbin/nscd/nscd.c
@@ -0,0 +1,884 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in thereg
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "agents/passwd.h"
+#include "agents/group.h"
+#include "agents/services.h"
+#include "cachedcli.h"
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "parser.h"
+#include "query.h"
+#include "singletons.h"
+
+#ifndef CONFIG_PATH
+#define CONFIG_PATH "/etc/cached.conf"
+#endif
+#define DEFAULT_CONFIG_PATH "cached.conf"
+
+#define MAX_SOCKET_IO_SIZE 4096
+
+struct processing_thread_args {
+ cache the_cache;
+ struct configuration *the_configuration;
+ struct runtime_env *the_runtime_env;
+};
+
+static void accept_connection(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void destroy_cache_(cache);
+static void destroy_runtime_env(struct runtime_env *);
+static cache init_cache_(struct configuration *);
+static struct runtime_env *init_runtime_env(struct configuration *);
+static void print_version_info(void);
+static void processing_loop(cache, struct runtime_env *,
+ struct configuration *);
+static void process_socket_event(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void process_timer_event(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void *processing_thread(void *);
+static void usage(void);
+
+void get_time_func(struct timeval *);
+
+static void
+print_version_info(void)
+{
+ TRACE_IN(print_version_info);
+ printf("cached v0.2 (20 Oct 2005)\nwas developed during SoC 2005\n");
+ TRACE_OUT(print_version_info);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,"usage: cached [-nstiId]\n");
+ exit(1);
+}
+
+static cache
+init_cache_(struct configuration *config)
+{
+ struct cache_params params;
+ cache retval;
+
+ struct configuration_entry *config_entry;
+ size_t size, i;
+ int res;
+
+ TRACE_IN(init_cache_);
+
+ memset(&params, 0, sizeof(struct cache_params));
+ params.get_time_func = get_time_func;
+ retval = init_cache(&params);
+
+ size = configuration_get_entries_size(config);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(config, i);
+ /*
+ * We should register common entries now - multipart entries
+ * would be registered automatically during the queries.
+ */
+ res = register_cache_entry(retval, (struct cache_entry_params *)
+ &config_entry->positive_cache_params);
+ config_entry->positive_cache_entry = find_cache_entry(retval,
+ config_entry->positive_cache_params.entry_name);
+ assert(config_entry->positive_cache_entry !=
+ INVALID_CACHE_ENTRY);
+
+ res = register_cache_entry(retval, (struct cache_entry_params *)
+ &config_entry->negative_cache_params);
+ config_entry->negative_cache_entry = find_cache_entry(retval,
+ config_entry->negative_cache_params.entry_name);
+ assert(config_entry->negative_cache_entry !=
+ INVALID_CACHE_ENTRY);
+ }
+
+ LOG_MSG_2("cache", "cache was successfully initialized");
+ TRACE_OUT(init_cache_);
+ return (retval);
+}
+
+static void
+destroy_cache_(cache the_cache)
+{
+ TRACE_IN(destroy_cache_);
+ destroy_cache(the_cache);
+ TRACE_OUT(destroy_cache_);
+}
+
+/*
+ * Socket and kqueues are prepared here. We have one global queue for both
+ * socket and timers events.
+ */
+static struct runtime_env *
+init_runtime_env(struct configuration *config)
+{
+ int serv_addr_len;
+ struct sockaddr_un serv_addr;
+
+ struct kevent eventlist;
+ struct timespec timeout;
+
+ struct runtime_env *retval;
+
+ TRACE_IN(init_runtime_env);
+ retval = (struct runtime_env *)malloc(sizeof(struct runtime_env));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct runtime_env));
+
+ retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0);
+
+ if (config->force_unlink == 1)
+ unlink(config->socket_path);
+
+ memset(&serv_addr, 0, sizeof(struct sockaddr_un));
+ serv_addr.sun_family = PF_LOCAL;
+ strncpy(serv_addr.sun_path, config->socket_path,
+ sizeof(serv_addr.sun_path));
+ serv_addr_len = sizeof(serv_addr.sun_family) +
+ strlen(serv_addr.sun_path) + 1;
+
+ if (bind(retval->sockfd, (struct sockaddr *)&serv_addr,
+ serv_addr_len) == -1) {
+ close(retval->sockfd);
+ free(retval);
+
+ LOG_ERR_2("runtime environment", "can't bind socket to path: "
+ "%s", config->socket_path);
+ TRACE_OUT(init_runtime_env);
+ return (NULL);
+ }
+ LOG_MSG_2("runtime environment", "using socket %s",
+ config->socket_path);
+
+ /*
+ * Here we're marking socket as non-blocking and setting its backlog
+ * to the maximum value
+ */
+ chmod(config->socket_path, config->socket_mode);
+ listen(retval->sockfd, -1);
+ fcntl(retval->sockfd, F_SETFL, O_NONBLOCK);
+
+ retval->queue = kqueue();
+ assert(retval->queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT,
+ 0, 0, 0);
+ memset(&timeout, 0, sizeof(struct timespec));
+ kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout);
+
+ LOG_MSG_2("runtime environment", "successfully initialized");
+ TRACE_OUT(init_runtime_env);
+ return (retval);
+}
+
+static void
+destroy_runtime_env(struct runtime_env *env)
+{
+ TRACE_IN(destroy_runtime_env);
+ close(env->queue);
+ close(env->sockfd);
+ free(env);
+ TRACE_OUT(destroy_runtime_env);
+}
+
+static void
+accept_connection(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct kevent eventlist[2];
+ struct timespec timeout;
+ struct query_state *qstate;
+
+ int fd;
+ int res;
+
+ uid_t euid;
+ gid_t egid;
+
+ TRACE_IN(accept_connection);
+ fd = accept(event_data->ident, NULL, NULL);
+ if (fd == -1) {
+ LOG_ERR_2("accept_connection", "error %d during accept()",
+ errno);
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ if (getpeereid(fd, &euid, &egid) != 0) {
+ LOG_ERR_2("accept_connection", "error %d during getpeereid()",
+ errno);
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ qstate = init_query_state(fd, sizeof(int), euid, egid);
+ if (qstate == NULL) {
+ LOG_ERR_2("accept_connection", "can't init query_state");
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ memset(&timeout, 0, sizeof(struct timespec));
+ EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ 0, qstate->timeout.tv_sec * 1000, qstate);
+ EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, qstate->kevent_watermark, qstate);
+ res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout);
+ if (res < 0)
+ LOG_ERR_2("accept_connection", "kevent error");
+
+ TRACE_OUT(accept_connection);
+}
+
+static void
+process_socket_event(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct kevent eventlist[2];
+ struct timeval query_timeout;
+ struct timespec kevent_timeout;
+ int nevents;
+ int eof_res, res;
+ ssize_t io_res;
+ struct query_state *qstate;
+
+ TRACE_IN(process_socket_event);
+ eof_res = event_data->flags & EV_EOF ? 1 : 0;
+ res = 0;
+
+ memset(&kevent_timeout, 0, sizeof(struct timespec));
+ EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE,
+ 0, 0, NULL);
+ nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout);
+ if (nevents == -1) {
+ if (errno == ENOENT) {
+ /* the timer is already handling this event */
+ TRACE_OUT(process_socket_event);
+ return;
+ } else {
+ /* some other error happened */
+ LOG_ERR_2("process_socket_event", "kevent error, errno"
+ " is %d", errno);
+ TRACE_OUT(process_socket_event);
+ return;
+ }
+ }
+ qstate = (struct query_state *)event_data->udata;
+
+ /*
+ * If the buffer that is to be send/received is too large,
+ * we send it implicitly, by using query_io_buffer_read and
+ * query_io_buffer_write functions in the query_state. These functions
+ * use the temporary buffer, which is later send/received in parts.
+ * The code below implements buffer splitting/mergind for send/receive
+ * operations. It also does the actual socket IO operations.
+ */
+ if (((qstate->use_alternate_io == 0) &&
+ (qstate->kevent_watermark <= event_data->data)) ||
+ ((qstate->use_alternate_io != 0) &&
+ (qstate->io_buffer_watermark <= event_data->data))) {
+ if (qstate->use_alternate_io != 0) {
+ switch (qstate->io_buffer_filter) {
+ case EVFILT_READ:
+ io_res = query_socket_read(qstate,
+ qstate->io_buffer_p,
+ qstate->io_buffer_watermark);
+ if (io_res < 0) {
+ qstate->use_alternate_io = 0;
+ qstate->process_func = NULL;
+ } else {
+ qstate->io_buffer_p += io_res;
+ if (qstate->io_buffer_p ==
+ qstate->io_buffer +
+ qstate->io_buffer_size) {
+ qstate->io_buffer_p =
+ qstate->io_buffer;
+ qstate->use_alternate_io = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (qstate->use_alternate_io == 0) {
+ do {
+ res = qstate->process_func(qstate);
+ } while ((qstate->kevent_watermark == 0) &&
+ (qstate->process_func != NULL) &&
+ (res == 0));
+
+ if (res != 0)
+ qstate->process_func = NULL;
+ }
+
+ if ((qstate->use_alternate_io != 0) &&
+ (qstate->io_buffer_filter == EVFILT_WRITE)) {
+ io_res = query_socket_write(qstate, qstate->io_buffer_p,
+ qstate->io_buffer_watermark);
+ if (io_res < 0) {
+ qstate->use_alternate_io = 0;
+ qstate->process_func = NULL;
+ } else
+ qstate->io_buffer_p += io_res;
+ }
+ } else {
+ /* assuming that socket was closed */
+ qstate->process_func = NULL;
+ qstate->use_alternate_io = 0;
+ }
+
+ if (((qstate->process_func == NULL) &&
+ (qstate->use_alternate_io == 0)) ||
+ (eof_res != 0) || (res != 0)) {
+ destroy_query_state(qstate);
+ close(event_data->ident);
+ TRACE_OUT(process_socket_event);
+ return;
+ }
+
+ /* updating the query_state lifetime variable */
+ get_time_func(&query_timeout);
+ query_timeout.tv_usec = 0;
+ query_timeout.tv_sec -= qstate->creation_time.tv_sec;
+ if (query_timeout.tv_sec > qstate->timeout.tv_sec)
+ query_timeout.tv_sec = 0;
+ else
+ query_timeout.tv_sec = qstate->timeout.tv_sec -
+ query_timeout.tv_sec;
+
+ if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p ==
+ qstate->io_buffer + qstate->io_buffer_size))
+ qstate->use_alternate_io = 0;
+
+ if (qstate->use_alternate_io == 0) {
+ /*
+ * If we must send/receive the large block of data,
+ * we should prepare the query_state's io_XXX fields.
+ * We should also substitute its write_func and read_func
+ * with the query_io_buffer_write and query_io_buffer_read,
+ * which will allow us to implicitly send/receive this large
+ * buffer later (in the subsequent calls to the
+ * process_socket_event).
+ */
+ if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) {
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->io_buffer = (char *)malloc(
+ qstate->kevent_watermark);
+ assert(qstate->io_buffer != NULL);
+ memset(qstate->io_buffer, 0, qstate->kevent_watermark);
+
+ qstate->io_buffer_p = qstate->io_buffer;
+ qstate->io_buffer_size = qstate->kevent_watermark;
+ qstate->io_buffer_filter = qstate->kevent_filter;
+
+ qstate->write_func = query_io_buffer_write;
+ qstate->read_func = query_io_buffer_read;
+
+ if (qstate->kevent_filter == EVFILT_READ)
+ qstate->use_alternate_io = 1;
+
+ qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->kevent_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate);
+ } else {
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->kevent_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, qstate->kevent_watermark, qstate);
+ }
+ } else {
+ if (qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p <
+ MAX_SOCKET_IO_SIZE) {
+ qstate->io_buffer_watermark = qstate->io_buffer +
+ qstate->io_buffer_size - qstate->io_buffer_p;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->io_buffer_filter,
+ EV_ADD | EV_ONESHOT, NOTE_LOWAT,
+ qstate->io_buffer_watermark,
+ qstate);
+ } else {
+ qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->io_buffer_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate);
+ }
+ }
+ EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER,
+ EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate);
+ kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout);
+
+ TRACE_OUT(process_socket_event);
+}
+
+/*
+ * This routine is called if timer event has been signaled in the kqueue. It
+ * just closes the socket and destroys the query_state.
+ */
+static void
+process_timer_event(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct query_state *qstate;
+
+ TRACE_IN(process_timer_event);
+ qstate = (struct query_state *)event_data->udata;
+ destroy_query_state(qstate);
+ close(event_data->ident);
+ TRACE_OUT(process_timer_event);
+}
+
+/*
+ * Processing loop is the basic processing routine, that forms a body of each
+ * procssing thread
+ */
+static void
+processing_loop(cache the_cache, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct timespec timeout;
+ const int eventlist_size = 1;
+ struct kevent eventlist[eventlist_size];
+ int nevents, i;
+
+ TRACE_MSG("=> processing_loop");
+ memset(&timeout, 0, sizeof(struct timespec));
+ memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size);
+
+ for (;;) {
+ nevents = kevent(env->queue, NULL, 0, eventlist,
+ eventlist_size, NULL);
+ /*
+ * we can only receive 1 event on success
+ */
+ if (nevents == 1) {
+ struct kevent *event_data;
+ event_data = &eventlist[0];
+
+ if (event_data->ident == env->sockfd) {
+ for (i = 0; i < event_data->data; ++i)
+ accept_connection(event_data, env, config);
+
+ EV_SET(eventlist, s_runtime_env->sockfd,
+ EVFILT_READ, EV_ADD | EV_ONESHOT,
+ 0, 0, 0);
+ memset(&timeout, 0,
+ sizeof(struct timespec));
+ kevent(s_runtime_env->queue, eventlist,
+ 1, NULL, 0, &timeout);
+
+ } else {
+ switch (event_data->filter) {
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+ process_socket_event(event_data,
+ env, config);
+ break;
+ case EVFILT_TIMER:
+ process_timer_event(event_data,
+ env, config);
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ /* this branch shouldn't be currently executed */
+ }
+ }
+
+ TRACE_MSG("<= processing_loop");
+}
+
+/*
+ * Wrapper above the processing loop function. It sets the thread signal mask
+ * to avoid SIGPIPE signals (which can happen if the client works incorrectly).
+ */
+static void *
+processing_thread(void *data)
+{
+ struct processing_thread_args *args;
+ sigset_t new;
+
+ TRACE_MSG("=> processing_thread");
+ args = (struct processing_thread_args *)data;
+
+ sigemptyset(&new);
+ sigaddset(&new, SIGPIPE);
+ if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0)
+ LOG_ERR_1("processing thread",
+ "thread can't block the SIGPIPE signal");
+
+ processing_loop(args->the_cache, args->the_runtime_env,
+ args->the_configuration);
+ free(args);
+ TRACE_MSG("<= processing_thread");
+
+ return (NULL);
+}
+
+void
+get_time_func(struct timeval *time)
+{
+ struct timespec res;
+ memset(&res, 0, sizeof(struct timespec));
+ clock_gettime(CLOCK_MONOTONIC, &res);
+
+ time->tv_sec = res.tv_sec;
+ time->tv_usec = 0;
+}
+
+/*
+ * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will
+ * search for this symbol in the executable. This symbol is the attribute of
+ * the caching daemon. So, if it exists, nsdispatch won't try to connect to
+ * the caching daemon and will just ignore the 'cache' source in the
+ * nsswitch.conf. This method helps to avoid cycles and organize
+ * self-performing requests.
+ */
+void
+_nss_cache_cycle_prevention_function(void)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct processing_thread_args *thread_args;
+ pthread_t *threads;
+
+ struct pidfh *pidfile;
+ pid_t pid;
+
+ char const *config_file;
+ char const *error_str;
+ int error_line;
+ int i, res;
+
+ int trace_mode_enabled;
+ int force_single_threaded;
+ int do_not_daemonize;
+ int clear_user_cache_entries, clear_all_cache_entries;
+ char *user_config_entry_name, *global_config_entry_name;
+ int show_statistics;
+ int daemon_mode, interactive_mode;
+
+
+ /* by default all debug messages are omitted */
+ TRACE_OFF();
+
+ /* startup output */
+ print_version_info();
+
+ /* parsing command line arguments */
+ trace_mode_enabled = 0;
+ force_single_threaded = 0;
+ do_not_daemonize = 0;
+ clear_user_cache_entries = 0;
+ clear_all_cache_entries = 0;
+ show_statistics = 0;
+ user_config_entry_name = NULL;
+ global_config_entry_name = NULL;
+ while ((res = getopt(argc, argv, "nstdi:I:")) != -1) {
+ switch (res) {
+ case 'n':
+ do_not_daemonize = 1;
+ break;
+ case 's':
+ force_single_threaded = 1;
+ break;
+ case 't':
+ trace_mode_enabled = 1;
+ break;
+ case 'i':
+ clear_user_cache_entries = 1;
+ if (optarg != NULL)
+ if (strcmp(optarg, "all") != 0)
+ user_config_entry_name = strdup(optarg);
+ break;
+ case 'I':
+ clear_all_cache_entries = 1;
+ if (optarg != NULL)
+ if (strcmp(optarg, "all") != 0)
+ global_config_entry_name =
+ strdup(optarg);
+ break;
+ case 'd':
+ show_statistics = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ daemon_mode = do_not_daemonize | force_single_threaded |
+ trace_mode_enabled;
+ interactive_mode = clear_user_cache_entries | clear_all_cache_entries |
+ show_statistics;
+
+ if ((daemon_mode != 0) && (interactive_mode != 0)) {
+ LOG_ERR_1("main", "daemon mode and interactive_mode arguments "
+ "can't be used together");
+ usage();
+ }
+
+ if (interactive_mode != 0) {
+ FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r");
+ char pidbuf[256];
+
+ struct cached_connection_params connection_params;
+ cached_connection connection;
+
+ int result;
+
+ if (pidfin == NULL)
+ errx(EXIT_FAILURE, "There is no daemon running.");
+
+ memset(pidbuf, 0, sizeof(pidbuf));
+ fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin);
+ fclose(pidfin);
+
+ if (ferror(pidfin) != 0)
+ errx(EXIT_FAILURE, "Can't read from pidfile.");
+
+ if (sscanf(pidbuf, "%d", &pid) != 1)
+ errx(EXIT_FAILURE, "Invalid pidfile.");
+ LOG_MSG_1("main", "daemon PID is %d", pid);
+
+
+ memset(&connection_params, 0,
+ sizeof(struct cached_connection_params));
+ connection_params.socket_path = DEFAULT_SOCKET_PATH;
+ connection = open_cached_connection__(&connection_params);
+ if (connection == INVALID_CACHED_CONNECTION)
+ errx(EXIT_FAILURE, "Can't connect to the daemon.");
+
+ if (clear_user_cache_entries != 0) {
+ result = cached_transform__(connection,
+ user_config_entry_name, TT_USER);
+ if (result != 0)
+ LOG_MSG_1("main",
+ "user cache transformation failed");
+ else
+ LOG_MSG_1("main",
+ "user cache_transformation "
+ "succeeded");
+ }
+
+ if (clear_all_cache_entries != 0) {
+ if (geteuid() != 0)
+ errx(EXIT_FAILURE, "Only root can initiate "
+ "global cache transformation.");
+
+ result = cached_transform__(connection,
+ global_config_entry_name, TT_ALL);
+ if (result != 0)
+ LOG_MSG_1("main",
+ "global cache transformation "
+ "failed");
+ else
+ LOG_MSG_1("main",
+ "global cache transformation "
+ "succeeded");
+ }
+
+ close_cached_connection__(connection);
+
+ free(user_config_entry_name);
+ free(global_config_entry_name);
+ return (EXIT_SUCCESS);
+ }
+
+ pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid);
+ if (pidfile == NULL) {
+ if (errno == EEXIST)
+ errx(EXIT_FAILURE, "Daemon already running, pid: %d.",
+ pid);
+ warn("Cannot open or create pidfile");
+ }
+
+ if (trace_mode_enabled == 1)
+ TRACE_ON();
+
+ /* blocking the main thread from receiving SIGPIPE signal */
+ sigblock(sigmask(SIGPIPE));
+
+ /* daemonization */
+ if (do_not_daemonize == 0) {
+ res = daemon(0, trace_mode_enabled == 0 ? 0 : 1);
+ if (res != 0) {
+ LOG_ERR_1("main", "can't daemonize myself: %s",
+ strerror(errno));
+ pidfile_remove(pidfile);
+ goto fin;
+ } else
+ LOG_MSG_1("main", "successfully daemonized");
+ }
+
+ pidfile_write(pidfile);
+
+ s_agent_table = init_agent_table();
+ register_agent(s_agent_table, init_passwd_agent());
+ register_agent(s_agent_table, init_passwd_mp_agent());
+ register_agent(s_agent_table, init_group_agent());
+ register_agent(s_agent_table, init_group_mp_agent());
+ register_agent(s_agent_table, init_services_agent());
+ register_agent(s_agent_table, init_services_mp_agent());
+ LOG_MSG_1("main", "request agents registered successfully");
+
+ /*
+ * Hosts agent can't work properly until we have access to the
+ * appropriate dtab structures, which are used in nsdispatch
+ * calls
+ *
+ register_agent(s_agent_table, init_hosts_agent());
+ */
+
+ /* configuration initialization */
+ s_configuration = init_configuration();
+ fill_configuration_defaults(s_configuration);
+
+ error_str = NULL;
+ error_line = 0;
+ config_file = CONFIG_PATH;
+
+ res = parse_config_file(s_configuration, config_file, &error_str,
+ &error_line);
+ if ((res != 0) && (error_str == NULL)) {
+ config_file = DEFAULT_CONFIG_PATH;
+ res = parse_config_file(s_configuration, config_file,
+ &error_str, &error_line);
+ }
+
+ if (res != 0) {
+ if (error_str != NULL) {
+ LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n",
+ config_file, error_line, error_str);
+ } else {
+ LOG_ERR_1("main", "no configuration file found "
+ "- was looking for %s and %s",
+ CONFIG_PATH, DEFAULT_CONFIG_PATH);
+ }
+ destroy_configuration(s_configuration);
+ return (-1);
+ }
+
+ if (force_single_threaded == 1)
+ s_configuration->threads_num = 1;
+
+ /* cache initialization */
+ s_cache = init_cache_(s_configuration);
+ if (s_cache == NULL) {
+ LOG_ERR_1("main", "can't initialize the cache");
+ destroy_configuration(s_configuration);
+ return (-1);
+ }
+
+ /* runtime environment initialization */
+ s_runtime_env = init_runtime_env(s_configuration);
+ if (s_runtime_env == NULL) {
+ LOG_ERR_1("main", "can't initialize the runtime environment");
+ destroy_configuration(s_configuration);
+ destroy_cache_(s_cache);
+ return (-1);
+ }
+
+ if (s_configuration->threads_num > 1) {
+ threads = (pthread_t *)malloc(sizeof(pthread_t) *
+ s_configuration->threads_num);
+ memset(threads, 0, sizeof(pthread_t) *
+ s_configuration->threads_num);
+ for (i = 0; i < s_configuration->threads_num; ++i) {
+ thread_args = (struct processing_thread_args *)malloc(
+ sizeof(struct processing_thread_args));
+ thread_args->the_cache = s_cache;
+ thread_args->the_runtime_env = s_runtime_env;
+ thread_args->the_configuration = s_configuration;
+
+ LOG_MSG_1("main", "thread #%d was successfully created",
+ i);
+ pthread_create(&threads[i], NULL, processing_thread,
+ thread_args);
+
+ thread_args = NULL;
+ }
+
+ for (i = 0; i < s_configuration->threads_num; ++i)
+ pthread_join(threads[i], NULL);
+ } else {
+ LOG_MSG_1("main", "working in single-threaded mode");
+ processing_loop(s_cache, s_runtime_env, s_configuration);
+ }
+
+fin:
+ /* runtime environment destruction */
+ destroy_runtime_env(s_runtime_env);
+
+ /* cache destruction */
+ destroy_cache_(s_cache);
+
+ /* configuration destruction */
+ destroy_configuration(s_configuration);
+
+ /* agents table destruction */
+ destroy_agent_table(s_agent_table);
+
+ pidfile_remove(pidfile);
+ return (EXIT_SUCCESS);
+}
diff --git a/usr.sbin/nscd/nscd.conf.5 b/usr.sbin/nscd/nscd.conf.5
new file mode 100644
index 0000000..de6d43a
--- /dev/null
+++ b/usr.sbin/nscd/nscd.conf.5
@@ -0,0 +1,102 @@
+.\" Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+
+.Dd August 29, 2005
+.Dt CACHED.CONF 5
+.Os
+.Sh NAME
+.Nm cached.conf
+.Nd "caching daemon configuration file manual page"
+.Sh "DESCRIPTION"
+.Nm
+is used by the
+.Xr cached 8
+daemon and is read on its startup. Its syntax is mostly similar to the
+nscd.conf syntax in Linux and Solaris. It has some differences, though -
+see them below.
+.Pp
+Each line specifies either an attribute and a value, or an attribute,
+a cachename and a value. Usual cachenames are passwd, groups, hosts,
+services, protocols and rpc. You can also use any other cachename
+(for example, if some third-party application uses nsswitch).
+.Pp
+.Bl -tag -width Pair
+.It Sy threads [value]
+Number of threads, which would listen for connections and process requests. The
+minimum is 1. The default value is 8.
+.It Sy enable-cache [cachename] [yes|no]
+Enables or disables the cache for specified cachename.
+.It Sy positive-time-to-live [cachename] [value]
+Sets the TTL (time-to-live) for the specified cache in seconds. Larger values
+can increase system's performance, but they also can affect the cache
+coherence. The default value is 3600.
+.It Sy positive-policy [cachename] [fifo|lru|lfu]
+The policy that is applied to erase some of the cache elements, when the
+size limit of the given cachename is exceeded. Possible policies are:
+fifo (first-in-first-out), lru (least-recently-used),
+lfu (least-frequently-used). The default policy is
+.It Sy negative-time-to-live [cachename] [value]
+The TTL of the negative cached elements in seconds. The larger values can
+significantly increase system performance in some environments
+(when dealing with files with UIDs, which are not in system databases,
+for example). This number should be kept low to avoid the cache
+coherence problems. The default value is 60.
+.It Sy negative-policy [cachename] [fifo|lru|lfu]
+The same as the positive-policy, but this one is applied to the negative
+elements of the given cachename. The default policy is
+.It Sy suggested-size [cachename] [value]
+This is the internal hash table size. The value should be a prime number
+for optimum performance. You should only change this value when the number
+of cached elements is significantly (in 5-10 times) greater then the default
+hash table size (255).
+.It Sy keep-hot-count [cachename] [value]
+The size limit of the cache with the given cachename. When it is exceeded,
+the policy will be applied. The default value is 2048.
+.It Sy perform-actual-lookups [cachename] [yes|no]
+If enabled, the
+.Xr cached 8
+doesn't simply receive and cache the NSS-requests results, but performs
+all the lookups by itself and only returns the responses. If this feature is
+enabled, then for the given cachename
+.Xr cached 8
+will act similarly to the NSCD.
+.Pp
+.Pa NOTE:
+this feature is currently experimental - it supports only passwd, groups and
+services cachenames.
+.Sh "NOTES"
+You can use
+.Sq #
+symbol at the beginning of the line for comments.
+.Sh "SEE ALSO"
+.Xr cached 8
+.Sh "AUTHORS"
+Michael Bushkov
+.Aq bushman@rsu.ru
+.Sh "BUGS"
+To report bugs or suggestions please mail me
+.Aq bushman@rsu.ru
diff --git a/usr.sbin/nscd/nscdcli.c b/usr.sbin/nscd/nscdcli.c
new file mode 100644
index 0000000..c78c875
--- /dev/null
+++ b/usr.sbin/nscd/nscdcli.c
@@ -0,0 +1,284 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/event.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "cachedcli.h"
+#include "protocol.h"
+
+#define DEFAULT_CACHED_IO_TIMEOUT 4
+
+static int safe_write(struct cached_connection_ *, const void *, size_t);
+static int safe_read(struct cached_connection_ *, void *, size_t);
+static int send_credentials(struct cached_connection_ *, int);
+
+static int
+safe_write(struct cached_connection_ *connection, const void *data,
+ size_t data_size)
+{
+ struct kevent eventlist;
+ int nevents;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist,
+ 1, &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ s_result = write(connection->sockfd, data + result,
+ eventlist.data < data_size - result ?
+ eventlist.data : data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+static int
+safe_read(struct cached_connection_ *connection, void *data, size_t data_size)
+{
+ struct kevent eventlist;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+ int nevents;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = DEFAULT_CACHED_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1,
+ &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) {
+ s_result = read(connection->sockfd, data + result,
+ eventlist.data <= data_size - result ? eventlist.data :
+ data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+static int
+send_credentials(struct cached_connection_ *connection, int type)
+{
+ struct kevent eventlist;
+ int nevents;
+ ssize_t result;
+ int res;
+
+ struct msghdr cred_hdr;
+ struct iovec iov;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ TRACE_IN(send_credentials);
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = sizeof(cmsg);
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ iov.iov_base = &type;
+ iov.iov_len = sizeof(int);
+
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ NOTE_LOWAT, sizeof(int), NULL);
+ res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1
+ : 0;
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+ TRACE_OUT(send_credentials);
+ return (result);
+ } else {
+ TRACE_OUT(send_credentials);
+ return (-1);
+ }
+}
+
+struct cached_connection_ *
+open_cached_connection__(struct cached_connection_params const *params)
+{
+ struct cached_connection_ *retval;
+ struct kevent eventlist;
+ struct sockaddr_un client_address;
+ int client_address_len, client_socket;
+ int res;
+
+ TRACE_IN(open_cached_connection);
+ assert(params != NULL);
+
+ client_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+ client_address.sun_family = PF_LOCAL;
+ strncpy(client_address.sun_path, params->socket_path,
+ sizeof(client_address.sun_path));
+ client_address_len = sizeof(client_address.sun_family) +
+ strlen(client_address.sun_path) + 1;
+
+ res = connect(client_socket, (struct sockaddr *)&client_address,
+ client_address_len);
+ if (res == -1) {
+ close(client_socket);
+ TRACE_OUT(open_cached_connection);
+ return (NULL);
+ }
+ fcntl(client_socket, F_SETFL, O_NONBLOCK);
+
+ retval = malloc(sizeof(struct cached_connection_));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct cached_connection_));
+
+ retval->sockfd = client_socket;
+
+ retval->write_queue = kqueue();
+ assert(retval->write_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ retval->read_queue = kqueue();
+ assert(retval->read_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD,
+ 0, 0, NULL);
+ res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL);
+
+ TRACE_OUT(open_cached_connection);
+ return (retval);
+}
+
+void
+close_cached_connection__(struct cached_connection_ *connection)
+{
+
+ TRACE_IN(close_cached_connection);
+ assert(connection != NULL);
+
+ close(connection->sockfd);
+ close(connection->read_queue);
+ close(connection->write_queue);
+ free(connection);
+ TRACE_OUT(close_cached_connection);
+}
+
+int
+cached_transform__(struct cached_connection_ *connection,
+ const char *entry_name, int transformation_type)
+{
+ size_t name_size;
+ int error_code;
+ int result;
+
+ TRACE_IN(cached_transform);
+
+ error_code = -1;
+ result = 0;
+ result = send_credentials(connection, CET_TRANSFORM_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ if (entry_name != NULL)
+ name_size = strlen(entry_name);
+ else
+ name_size = 0;
+
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, &transformation_type, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (entry_name != NULL) {
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+ }
+
+ result = safe_read(connection, &error_code, sizeof(int));
+ if (result != 0)
+ error_code = -1;
+
+fin:
+ TRACE_OUT(cached_transform);
+ return (error_code);
+}
diff --git a/usr.sbin/nscd/nscdcli.h b/usr.sbin/nscd/nscdcli.h
new file mode 100644
index 0000000..58d9ccc
--- /dev/null
+++ b/usr.sbin/nscd/nscdcli.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_CACHEDCLI_H__
+#define __CACHED_CACHEDCLI_H__
+
+struct cached_connection_params {
+ char *socket_path;
+ struct timeval timeout;
+};
+
+struct cached_connection_ {
+ int sockfd;
+ int read_queue;
+ int write_queue;
+};
+
+/* simple abstractions for not to write "struct" every time */
+typedef struct cached_connection_ *cached_connection;
+typedef struct cached_connection_ *cached_mp_write_session;
+typedef struct cached_connection_ *cached_mp_read_session;
+
+#define INVALID_CACHED_CONNECTION (NULL)
+
+/* initialization/destruction routines */
+extern cached_connection open_cached_connection__(
+ struct cached_connection_params const *);
+extern void close_cached_connection__(cached_connection);
+
+extern int cached_transform__(cached_connection, const char *, int);
+
+#endif
diff --git a/usr.sbin/nscd/parser.c b/usr.sbin/nscd/parser.c
new file mode 100644
index 0000000..b877efa
--- /dev/null
+++ b/usr.sbin/nscd/parser.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "parser.h"
+
+static void enable_cache(struct configuration *,const char *, int);
+static struct configuration_entry *find_create_entry(struct configuration *,
+ const char *);
+static int get_number(const char *, int, int);
+static enum cache_policy_t get_policy(const char *);
+static int get_yesno(const char *);
+static int check_cachename(const char *);
+static void check_files(struct configuration *, const char *, int);
+static void set_keep_hot_count(struct configuration *, const char *, int);
+static void set_negative_policy(struct configuration *, const char *,
+ enum cache_policy_t);
+static void set_negative_time_to_live(struct configuration *,
+ const char *, int);
+static void set_positive_policy(struct configuration *, const char *,
+ enum cache_policy_t);
+static void set_perform_actual_lookups(struct configuration *, const char *,
+ int);
+static void set_positive_time_to_live(struct configuration *,
+ const char *, int);
+static void set_suggested_size(struct configuration *, const char *,
+ int size);
+static void set_threads_num(struct configuration *, int);
+static int strbreak(char *, char **, int);
+
+static int
+strbreak(char *str, char **fields, int fields_size)
+{
+ char *c = str;
+ int i, num;
+
+ TRACE_IN(strbreak);
+ num = 0;
+ for (i = 0;
+ ((*fields =
+ strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL);
+ ++i)
+ if ((*(*fields)) != '\0') {
+ ++fields;
+ ++num;
+ }
+
+ TRACE_OUT(strbreak);
+ return (num);
+}
+
+/*
+ * Tries to find the configuration entry with the specified name. If search
+ * fails, the new entry with the default parameters will be created.
+ */
+static struct configuration_entry *
+find_create_entry(struct configuration *config,
+ const char *entry_name)
+{
+ struct configuration_entry *entry = NULL;
+ int res;
+
+ TRACE_IN(find_create_entry);
+ entry = configuration_find_entry(config, entry_name);
+ if (entry == NULL) {
+ entry = create_def_configuration_entry(entry_name);
+ assert( entry != NULL);
+ res = add_configuration_entry(config, entry);
+ assert(res == 0);
+ }
+
+ TRACE_OUT(find_create_entry);
+ return (entry);
+}
+
+/*
+ * The vast majority of the functions below corresponds to the particular
+ * keywords in the configuration file.
+ */
+static void
+enable_cache(struct configuration *config, const char *entry_name, int flag)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(enable_cache);
+ entry = find_create_entry(config, entry_name);
+ entry->enabled = flag;
+ TRACE_OUT(enable_cache);
+}
+
+static void
+set_positive_time_to_live(struct configuration *config,
+ const char *entry_name, int ttl)
+{
+ struct configuration_entry *entry;
+ struct timeval lifetime;
+
+ TRACE_IN(set_positive_time_to_live);
+ assert(ttl >= 0);
+ assert(entry_name != NULL);
+ memset(&lifetime, 0, sizeof(struct timeval));
+ lifetime.tv_sec = ttl;
+
+ entry = find_create_entry(config, entry_name);
+ memcpy(&entry->positive_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+ memcpy(&entry->mp_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+
+ TRACE_OUT(set_positive_time_to_live);
+}
+
+static void
+set_negative_time_to_live(struct configuration *config,
+ const char *entry_name, int nttl)
+{
+ struct configuration_entry *entry;
+ struct timeval lifetime;
+
+ TRACE_IN(set_negative_time_to_live);
+ assert(nttl > 0);
+ assert(entry_name != NULL);
+ memset(&lifetime, 0, sizeof(struct timeval));
+ lifetime.tv_sec = nttl;
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ memcpy(&entry->negative_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+
+ TRACE_OUT(set_negative_time_to_live);
+}
+
+/*
+ * Hot count is actually the elements size limit.
+ */
+static void
+set_keep_hot_count(struct configuration *config,
+ const char *entry_name, int count)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_keep_hot_count);
+ assert(count >= 0);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.max_elemsize = count;
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->negative_cache_params.max_elemsize = count;
+
+ TRACE_OUT(set_keep_hot_count);
+}
+
+static void
+set_positive_policy(struct configuration *config,
+ const char *entry_name, enum cache_policy_t policy)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_positive_policy);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.policy = policy;
+
+ TRACE_OUT(set_positive_policy);
+}
+
+static void
+set_negative_policy(struct configuration *config,
+ const char *entry_name, enum cache_policy_t policy)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_negative_policy);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->negative_cache_params.policy = policy;
+
+ TRACE_OUT(set_negative_policy);
+}
+
+static void
+set_perform_actual_lookups(struct configuration *config,
+ const char *entry_name, int flag)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_perform_actual_lookups);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->perform_actual_lookups = flag;
+
+ TRACE_OUT(set_perform_actual_lookups);
+}
+
+static void
+set_suggested_size(struct configuration *config,
+ const char *entry_name, int size)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_suggested_size);
+ assert(config != NULL);
+ assert(entry_name != NULL);
+ assert(size > 0);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.cache_entries_size = size;
+ entry->negative_cache_params.cache_entries_size = size;
+
+ TRACE_OUT(set_suggested_size);
+}
+
+static void
+check_files(struct configuration *config, const char *entry_name, int flag)
+{
+
+ TRACE_IN(check_files);
+ assert(entry_name != NULL);
+ TRACE_OUT(check_files);
+}
+
+static int
+get_yesno(const char *str)
+{
+
+ if (strcmp(str, "yes") == 0)
+ return (1);
+ else if (strcmp(str, "no") == 0)
+ return (0);
+ else
+ return (-1);
+}
+
+static int
+get_number(const char *str, int low, int max)
+{
+
+ char *end = NULL;
+ int res = 0;
+
+ if (str[0] == '\0')
+ return (-1);
+
+ res = strtol(str, &end, 10);
+ if (*end != '\0')
+ return (-1);
+ else
+ if (((res >= low) || (low == -1)) &&
+ ((res <= max) || (max == -1)))
+ return (res);
+ else
+ return (-2);
+}
+
+static enum cache_policy_t
+get_policy(const char *str)
+{
+
+ if (strcmp(str, "fifo") == 0)
+ return (CPT_FIFO);
+ else if (strcmp(str, "lru") == 0)
+ return (CPT_LRU);
+ else if (strcmp(str, "lfu") == 0)
+ return (CPT_LFU);
+
+ return (-1);
+}
+
+static int
+check_cachename(const char *str)
+{
+
+ assert(str != NULL);
+ return ((strlen(str) > 0) ? 0 : -1);
+}
+
+static void
+set_threads_num(struct configuration *config, int value)
+{
+
+ assert(config != NULL);
+ config->threads_num = value;
+}
+
+/*
+ * The main configuration routine. Its implementation is hugely inspired by the
+ * the same routine implementation in Solaris NSCD.
+ */
+int
+parse_config_file(struct configuration *config,
+ const char *fname, char const **error_str, int *error_line)
+{
+ FILE *fin;
+ char buffer[255];
+ char *fields[128];
+ int field_count, line_num, value;
+ int res;
+
+ TRACE_IN(parse_config_file);
+ assert(config != NULL);
+ assert(fname != NULL);
+
+ fin = fopen(fname, "r");
+ if (fin == NULL) {
+ TRACE_OUT(parse_config_file);
+ return (-1);
+ }
+
+ res = 0;
+ line_num = 0;
+ memset(buffer, 0, sizeof(buffer));
+ while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
+ field_count = strbreak(buffer, fields, sizeof(fields));
+ ++line_num;
+
+ if (field_count == 0)
+ continue;
+
+ switch (fields[0][0]) {
+ case '#':
+ case '\0':
+ continue;
+ case 'e':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "enable-cache") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ enable_cache(config, fields[1], value);
+ continue;
+ }
+ break;
+ case 'd':
+ if ((field_count == 2) &&
+ (strcmp(fields[0], "debug-level") == 0) &&
+ ((value = get_number(fields[1], 0, 10)) != -1)) {
+ continue;
+ }
+ break;
+ case 'p':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "positive-time-to-live") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_positive_time_to_live(config,
+ fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "positive-policy") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_policy(fields[2])) != -1)) {
+ set_positive_policy(config, fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "perform-actual-lookups") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ set_perform_actual_lookups(config, fields[1],
+ value);
+ continue;
+ }
+ break;
+ case 'n':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "negative-time-to-live") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_negative_time_to_live(config,
+ fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "negative-policy") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_policy(fields[2])) != -1)) {
+ set_negative_policy(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ case 's':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "suggested-size") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 1, -1)) != -1)) {
+ set_suggested_size(config, fields[1], value);
+ continue;
+ }
+ break;
+ case 't':
+ if ((field_count == 2) &&
+ (strcmp(fields[0], "threads") == 0) &&
+ ((value = get_number(fields[1], 1, -1)) != -1)) {
+ set_threads_num(config, value);
+ continue;
+ }
+ break;
+ case 'k':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "keep-hot-count") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_keep_hot_count(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ case 'c':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "check-files") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ check_files(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ LOG_ERR_2("config file parser", "error in file "
+ "%s on line %d", fname, line_num);
+ *error_str = "syntax error";
+ *error_line = line_num;
+ res = -1;
+ }
+ fclose(fin);
+
+ TRACE_OUT(parse_config_file);
+ return (res);
+}
diff --git a/usr.sbin/nscd/parser.h b/usr.sbin/nscd/parser.h
new file mode 100644
index 0000000..54cc898
--- /dev/null
+++ b/usr.sbin/nscd/parser.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_PARSER_H__
+#define __CACHED_PARSER_H__
+
+extern int parse_config_file(struct configuration *,
+ const char *, char const **, int *);
+
+#endif
diff --git a/usr.sbin/nscd/protocol.c b/usr.sbin/nscd/protocol.c
new file mode 100644
index 0000000..08cea92
--- /dev/null
+++ b/usr.sbin/nscd/protocol.c
@@ -0,0 +1,550 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "debug.h"
+#include "log.h"
+#include "protocol.h"
+
+/*
+ * Initializes the comm_element with any given type of data
+ */
+void
+init_comm_element(struct comm_element *element, enum comm_element_t type)
+{
+
+ TRACE_IN(init_comm_element);
+ memset(element, 0, sizeof(struct comm_element));
+
+ switch (type) {
+ case CET_WRITE_REQUEST:
+ init_cache_write_request(&element->c_write_request);
+ break;
+ case CET_WRITE_RESPONSE:
+ init_cache_write_response(&element->c_write_response);
+ break;
+ case CET_READ_REQUEST:
+ init_cache_read_request(&element->c_read_request);
+ break;
+ case CET_READ_RESPONSE:
+ init_cache_read_response(&element->c_read_response);
+ break;
+ case CET_TRANSFORM_REQUEST:
+ init_cache_transform_request(&element->c_transform_request);
+ break;
+ case CET_TRANSFORM_RESPONSE:
+ init_cache_transform_response(&element->c_transform_response);
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ init_cache_mp_write_session_request(&element->c_mp_ws_request);
+ break;
+ case CET_MP_WRITE_SESSION_RESPONSE:
+ init_cache_mp_write_session_response(&element->c_mp_ws_response);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ init_cache_mp_write_session_write_request(
+ &element->c_mp_ws_write_request);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_RESPONSE:
+ init_cache_mp_write_session_write_response(
+ &element->c_mp_ws_write_response);
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ init_cache_mp_read_session_request(&element->c_mp_rs_request);
+ break;
+ case CET_MP_READ_SESSION_RESPONSE:
+ init_cache_mp_read_session_response(&element->c_mp_rs_response);
+ break;
+ case CET_MP_READ_SESSION_READ_RESPONSE:
+ init_cache_mp_read_session_read_response(
+ &element->c_mp_rs_read_response);
+ break;
+ case CET_UNDEFINED:
+ break;
+ default:
+ LOG_ERR_2("init_comm_element", "invalid communication element");
+ TRACE_OUT(init_comm_element);
+ return;
+ }
+
+ element->type = type;
+ TRACE_OUT(init_comm_element);
+}
+
+void
+finalize_comm_element(struct comm_element *element)
+{
+
+ TRACE_IN(finalize_comm_element);
+ switch (element->type) {
+ case CET_WRITE_REQUEST:
+ finalize_cache_write_request(&element->c_write_request);
+ break;
+ case CET_WRITE_RESPONSE:
+ finalize_cache_write_response(&element->c_write_response);
+ break;
+ case CET_READ_REQUEST:
+ finalize_cache_read_request(&element->c_read_request);
+ break;
+ case CET_READ_RESPONSE:
+ finalize_cache_read_response(&element->c_read_response);
+ break;
+ case CET_TRANSFORM_REQUEST:
+ finalize_cache_transform_request(&element->c_transform_request);
+ break;
+ case CET_TRANSFORM_RESPONSE:
+ finalize_cache_transform_response(
+ &element->c_transform_response);
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ finalize_cache_mp_write_session_request(
+ &element->c_mp_ws_request);
+ break;
+ case CET_MP_WRITE_SESSION_RESPONSE:
+ finalize_cache_mp_write_session_response(
+ &element->c_mp_ws_response);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ finalize_cache_mp_write_session_write_request(
+ &element->c_mp_ws_write_request);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_RESPONSE:
+ finalize_cache_mp_write_session_write_response(
+ &element->c_mp_ws_write_response);
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ finalize_cache_mp_read_session_request(
+ &element->c_mp_rs_request);
+ break;
+ case CET_MP_READ_SESSION_RESPONSE:
+ finalize_cache_mp_read_session_response(
+ &element->c_mp_rs_response);
+ break;
+ case CET_MP_READ_SESSION_READ_RESPONSE:
+ finalize_cache_mp_read_session_read_response(
+ &element->c_mp_rs_read_response);
+ break;
+ case CET_UNDEFINED:
+ break;
+ default:
+ break;
+ }
+
+ element->type = CET_UNDEFINED;
+ TRACE_OUT(finalize_comm_element);
+}
+
+void
+init_cache_write_request(struct cache_write_request *write_request)
+{
+
+ TRACE_IN(init_cache_write_request);
+ memset(write_request, 0, sizeof(struct cache_write_request));
+ TRACE_OUT(init_cache_write_request);
+}
+
+void
+finalize_cache_write_request(struct cache_write_request *write_request)
+{
+
+ TRACE_IN(finalize_cache_write_request);
+ free(write_request->entry);
+ free(write_request->cache_key);
+ free(write_request->data);
+ TRACE_OUT(finalize_cache_write_request);
+}
+
+struct cache_write_request *
+get_cache_write_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_write_request);
+ assert(element->type == CET_WRITE_REQUEST);
+ TRACE_OUT(get_cache_write_request);
+ return (&element->c_write_request);
+}
+
+void
+init_cache_write_response(struct cache_write_response *write_response)
+{
+
+ TRACE_IN(init_cache_write_response);
+ memset(write_response, 0, sizeof(struct cache_write_response));
+ TRACE_OUT(init_cache_write_response);
+}
+
+void
+finalize_cache_write_response(struct cache_write_response *write_response)
+{
+
+ TRACE_IN(finalize_cache_write_response);
+ TRACE_OUT(finalize_cache_write_response);
+}
+
+struct cache_write_response *
+get_cache_write_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_write_response);
+ assert(element->type == CET_WRITE_RESPONSE);
+ TRACE_OUT(get_cache_write_response);
+ return (&element->c_write_response);
+}
+
+void
+init_cache_read_request(struct cache_read_request *read_request)
+{
+
+ TRACE_IN(init_cache_read_request);
+ memset(read_request, 0, sizeof(struct cache_read_request));
+ TRACE_OUT(init_cache_read_request);
+}
+
+void
+finalize_cache_read_request(struct cache_read_request *read_request)
+{
+
+ TRACE_IN(finalize_cache_read_request);
+ free(read_request->entry);
+ free(read_request->cache_key);
+ TRACE_OUT(finalize_cache_read_request);
+}
+
+struct cache_read_request *
+get_cache_read_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_read_request);
+ assert(element->type == CET_READ_REQUEST);
+ TRACE_OUT(get_cache_read_request);
+ return (&element->c_read_request);
+}
+
+void
+init_cache_read_response(struct cache_read_response *read_response)
+{
+
+ TRACE_IN(init_cache_read_response);
+ memset(read_response, 0, sizeof(struct cache_read_response));
+ TRACE_OUT(init_cache_read_response);
+}
+
+void
+finalize_cache_read_response(struct cache_read_response *read_response)
+{
+
+ TRACE_IN(finalize_cache_read_response);
+ free(read_response->data);
+ TRACE_OUT(finalize_cache_read_response);
+}
+
+struct cache_read_response *
+get_cache_read_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_read_response);
+ assert(element->type == CET_READ_RESPONSE);
+ TRACE_OUT(get_cache_read_response);
+ return (&element->c_read_response);
+}
+
+void
+init_cache_transform_request(struct cache_transform_request *transform_request)
+{
+
+ TRACE_IN(init_cache_transform_request);
+ memset(transform_request, 0, sizeof(struct cache_transform_request));
+ TRACE_OUT(init_cache_transform_request);
+}
+
+void
+finalize_cache_transform_request(
+ struct cache_transform_request *transform_request)
+{
+
+ TRACE_IN(finalize_cache_transform_request);
+ free(transform_request->entry);
+ TRACE_OUT(finalize_cache_transform_request);
+}
+
+struct cache_transform_request *
+get_cache_transform_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_transform_request);
+ assert(element->type == CET_TRANSFORM_REQUEST);
+ TRACE_OUT(get_cache_transform_request);
+ return (&element->c_transform_request);
+}
+
+void
+init_cache_transform_response(
+ struct cache_transform_response *transform_response)
+{
+
+ TRACE_IN(init_cache_transform_request);
+ memset(transform_response, 0, sizeof(struct cache_transform_response));
+ TRACE_OUT(init_cache_transform_request);
+}
+
+void
+finalize_cache_transform_response(
+ struct cache_transform_response *transform_response)
+{
+
+ TRACE_IN(finalize_cache_transform_response);
+ TRACE_OUT(finalize_cache_transform_response);
+}
+
+struct cache_transform_response *
+get_cache_transform_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_transform_response);
+ assert(element->type == CET_TRANSFORM_RESPONSE);
+ TRACE_OUT(get_cache_transform_response);
+ return (&element->c_transform_response);
+}
+
+
+void
+init_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *mp_ws_request)
+{
+
+ TRACE_IN(init_cache_mp_write_session_request);
+ memset(mp_ws_request, 0,
+ sizeof(struct cache_mp_write_session_request));
+ TRACE_OUT(init_cache_mp_write_session_request);
+}
+
+void
+finalize_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *mp_ws_request)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_request);
+ free(mp_ws_request->entry);
+ TRACE_OUT(finalize_cache_mp_write_session_request);
+}
+
+struct cache_mp_write_session_request *
+get_cache_mp_write_session_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_request);
+ assert(element->type == CET_MP_WRITE_SESSION_REQUEST);
+ TRACE_OUT(get_cache_mp_write_session_request);
+ return (&element->c_mp_ws_request);
+}
+
+void
+init_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *mp_ws_response)
+{
+
+ TRACE_IN(init_cache_mp_write_session_response);
+ memset(mp_ws_response, 0,
+ sizeof(struct cache_mp_write_session_response));
+ TRACE_OUT(init_cache_mp_write_session_response);
+}
+
+void
+finalize_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *mp_ws_response)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_response);
+ TRACE_OUT(finalize_cache_mp_write_session_response);
+}
+
+struct cache_mp_write_session_response *
+get_cache_mp_write_session_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_response);
+ assert(element->type == CET_MP_WRITE_SESSION_RESPONSE);
+ TRACE_OUT(get_cache_mp_write_session_response);
+ return (&element->c_mp_ws_response);
+}
+
+void
+init_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *mp_ws_write_request)
+{
+
+ TRACE_IN(init_cache_mp_write_session_write_request);
+ memset(mp_ws_write_request, 0,
+ sizeof(struct cache_mp_write_session_write_request));
+ TRACE_OUT(init_cache_mp_write_session_write_response);
+}
+
+void
+finalize_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *mp_ws_write_request)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_write_request);
+ free(mp_ws_write_request->data);
+ TRACE_OUT(finalize_cache_mp_write_session_write_request);
+}
+
+struct cache_mp_write_session_write_request *
+get_cache_mp_write_session_write_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_write_request);
+ assert(element->type == CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ TRACE_OUT(get_cache_mp_write_session_write_request);
+ return (&element->c_mp_ws_write_request);
+}
+
+void
+init_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *mp_ws_write_response)
+{
+
+ TRACE_IN(init_cache_mp_write_session_write_response);
+ memset(mp_ws_write_response, 0,
+ sizeof(struct cache_mp_write_session_write_response));
+ TRACE_OUT(init_cache_mp_write_session_write_response);
+}
+
+void
+finalize_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *mp_ws_write_response)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_write_response);
+ TRACE_OUT(finalize_cache_mp_write_session_write_response);
+}
+
+struct cache_mp_write_session_write_response *
+get_cache_mp_write_session_write_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_write_response);
+ assert(element->type == CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ TRACE_OUT(get_cache_mp_write_session_write_response);
+ return (&element->c_mp_ws_write_response);
+}
+
+void
+init_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *mp_rs_request)
+{
+
+ TRACE_IN(init_cache_mp_read_session_request);
+ memset(mp_rs_request, 0, sizeof(struct cache_mp_read_session_request));
+ TRACE_OUT(init_cache_mp_read_session_request);
+}
+
+void
+finalize_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *mp_rs_request)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_request);
+ free(mp_rs_request->entry);
+ TRACE_OUT(finalize_cache_mp_read_session_request);
+}
+
+struct cache_mp_read_session_request *
+get_cache_mp_read_session_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_request);
+ assert(element->type == CET_MP_READ_SESSION_REQUEST);
+ TRACE_OUT(get_cache_mp_read_session_request);
+ return (&element->c_mp_rs_request);
+}
+
+void
+init_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *mp_rs_response)
+{
+
+ TRACE_IN(init_cache_mp_read_session_response);
+ memset(mp_rs_response, 0,
+ sizeof(struct cache_mp_read_session_response));
+ TRACE_OUT(init_cache_mp_read_session_response);
+}
+
+void
+finalize_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *mp_rs_response)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_response);
+ TRACE_OUT(finalize_cache_mp_read_session_response);
+}
+
+struct cache_mp_read_session_response *
+get_cache_mp_read_session_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_response);
+ assert(element->type == CET_MP_READ_SESSION_RESPONSE);
+ TRACE_OUT(get_cache_mp_read_session_response);
+ return (&element->c_mp_rs_response);
+}
+
+void
+init_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *mp_ws_read_response)
+{
+
+ TRACE_IN(init_cache_mp_read_session_read_response);
+ memset(mp_ws_read_response, 0,
+ sizeof(struct cache_mp_read_session_read_response));
+ TRACE_OUT(init_cache_mp_read_session_read_response);
+}
+
+void
+finalize_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *mp_rs_read_response)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_read_response);
+ free(mp_rs_read_response->data);
+ TRACE_OUT(finalize_cache_mp_read_session_read_response);
+}
+
+struct cache_mp_read_session_read_response *
+get_cache_mp_read_session_read_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_read_response);
+ assert(element->type == CET_MP_READ_SESSION_READ_RESPONSE);
+ TRACE_OUT(get_cache_mp_read_session_read_response);
+ return (&element->c_mp_rs_read_response);
+}
diff --git a/usr.sbin/nscd/protocol.h b/usr.sbin/nscd/protocol.h
new file mode 100644
index 0000000..7fadbfc
--- /dev/null
+++ b/usr.sbin/nscd/protocol.h
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_PROTOCOL_H__
+#define __CACHED_PROTOCOL_H__
+
+#include <stdlib.h>
+
+/* maximum buffer size to receive - larger buffers are not allowed */
+#define MAX_BUFFER_SIZE (1 << 20)
+
+/* buffer size correctness checking routine */
+#define BUFSIZE_CORRECT(x) (((x) > 0) && ((x) < MAX_BUFFER_SIZE))
+#define BUFSIZE_INVALID(x) (!BUFSIZE_CORRECT(x))
+
+/* structures below represent the data that are sent/received by the daemon */
+struct cache_write_request
+{
+ char *entry;
+ char *cache_key;
+ char *data;
+
+ size_t entry_length;
+ size_t cache_key_size;
+ size_t data_size;
+};
+
+struct cache_write_response
+{
+ int error_code;
+};
+
+struct cache_read_request
+{
+ char *entry;
+ char *cache_key;
+
+ size_t entry_length;
+ size_t cache_key_size;
+};
+
+struct cache_read_response
+{
+ char *data; // ignored if error_code is not 0
+ size_t data_size; // ignored if error_code is not 0
+
+ int error_code;
+};
+
+enum transformation_type {
+ TT_USER = 0, // tranform only the entries of the caller
+ TT_ALL = 1 // transform all entries
+};
+
+struct cache_transform_request
+{
+ char *entry; // ignored if entry_length is 0
+ size_t entry_length;
+
+ int transformation_type;
+};
+
+struct cache_transform_response
+{
+ int error_code;
+};
+
+struct cache_mp_write_session_request {
+ char *entry;
+ size_t entry_length;
+};
+
+struct cache_mp_write_session_response {
+ int error_code;
+};
+
+struct cache_mp_write_session_write_request {
+ char *data;
+ size_t data_size;
+};
+
+struct cache_mp_write_session_write_response {
+ int error_code;
+};
+
+struct cache_mp_read_session_request {
+ char *entry;
+ size_t entry_length;
+};
+
+struct cache_mp_read_session_response {
+ int error_code;
+};
+
+struct cache_mp_read_session_read_response {
+ char *data;
+ size_t data_size;
+
+ int error_code;
+};
+
+
+enum comm_element_t {
+ CET_UNDEFINED = 0,
+ CET_WRITE_REQUEST = 1,
+ CET_WRITE_RESPONSE = 2,
+ CET_READ_REQUEST = 3,
+ CET_READ_RESPONSE = 4,
+ CET_TRANSFORM_REQUEST = 5,
+ CET_TRANSFORM_RESPONSE = 6,
+ CET_MP_WRITE_SESSION_REQUEST = 7,
+ CET_MP_WRITE_SESSION_RESPONSE = 8,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST = 9,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10,
+ CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11,
+ CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12,
+ CET_MP_READ_SESSION_REQUEST = 13,
+ CET_MP_READ_SESSION_RESPONSE = 14,
+ CET_MP_READ_SESSION_READ_REQUEST = 15,
+ CET_MP_READ_SESSION_READ_RESPONSE = 16,
+ CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17,
+ CET_MAX = 18
+};
+
+/*
+ * The comm_element is used as the holder of any known (defined above) data
+ * type that is to be sent/received.
+ */
+struct comm_element
+{
+ union {
+ struct cache_write_request c_write_request;
+ struct cache_write_response c_write_response;
+ struct cache_read_request c_read_request;
+ struct cache_read_response c_read_response;
+ struct cache_transform_request c_transform_request;
+ struct cache_transform_response c_transform_response;
+
+ struct cache_mp_write_session_request c_mp_ws_request;
+ struct cache_mp_write_session_response c_mp_ws_response;
+ struct cache_mp_write_session_write_request c_mp_ws_write_request;
+ struct cache_mp_write_session_write_response c_mp_ws_write_response;
+
+ struct cache_mp_read_session_request c_mp_rs_request;
+ struct cache_mp_read_session_response c_mp_rs_response;
+ struct cache_mp_read_session_read_response c_mp_rs_read_response;
+ };
+ enum comm_element_t type;
+};
+
+extern void init_comm_element(struct comm_element *, enum comm_element_t type);
+extern void finalize_comm_element(struct comm_element *);
+
+/*
+ * For each type of data, there is three functions (init/finalize/get), that
+ * used with comm_element structure
+ */
+extern void init_cache_write_request(struct cache_write_request *);
+extern void finalize_cache_write_request(struct cache_write_request *);
+extern struct cache_write_request *get_cache_write_request(
+ struct comm_element *);
+
+extern void init_cache_write_response(struct cache_write_response *);
+extern void finalize_cache_write_response(struct cache_write_response *);
+extern struct cache_write_response *get_cache_write_response(
+ struct comm_element *);
+
+extern void init_cache_read_request(struct cache_read_request *);
+extern void finalize_cache_read_request(struct cache_read_request *);
+extern struct cache_read_request *get_cache_read_request(
+ struct comm_element *);
+
+extern void init_cache_read_response(struct cache_read_response *);
+extern void finalize_cache_read_response(struct cache_read_response *);
+extern struct cache_read_response *get_cache_read_response(
+ struct comm_element *);
+
+extern void init_cache_transform_request(struct cache_transform_request *);
+extern void finalize_cache_transform_request(struct cache_transform_request *);
+extern struct cache_transform_request *get_cache_transform_request(
+ struct comm_element *);
+
+extern void init_cache_transform_response(struct cache_transform_response *);
+extern void finalize_cache_transform_response(
+ struct cache_transform_response *);
+extern struct cache_transform_response *get_cache_transform_response(
+ struct comm_element *);
+
+extern void init_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *);
+extern void finalize_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *);
+extern struct cache_mp_write_session_request *
+ get_cache_mp_write_session_request(
+ struct comm_element *);
+
+extern void init_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *);
+extern void finalize_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *);
+extern struct cache_mp_write_session_response *
+ get_cache_mp_write_session_response(struct comm_element *);
+
+extern void init_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *);
+extern void finalize_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *);
+extern struct cache_mp_write_session_write_request *
+ get_cache_mp_write_session_write_request(struct comm_element *);
+
+extern void init_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *);
+extern void finalize_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *);
+extern struct cache_mp_write_session_write_response *
+ get_cache_mp_write_session_write_response(struct comm_element *);
+
+extern void init_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *);
+extern void finalize_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *);
+extern struct cache_mp_read_session_request *get_cache_mp_read_session_request(
+ struct comm_element *);
+
+extern void init_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *);
+extern void finalize_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *);
+extern struct cache_mp_read_session_response *
+ get_cache_mp_read_session_response(
+ struct comm_element *);
+
+extern void init_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *);
+extern void finalize_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *);
+extern struct cache_mp_read_session_read_response *
+ get_cache_mp_read_session_read_response(struct comm_element *);
+
+#endif
diff --git a/usr.sbin/nscd/query.c b/usr.sbin/nscd/query.c
new file mode 100644
index 0000000..28882c3
--- /dev/null
+++ b/usr.sbin/nscd/query.c
@@ -0,0 +1,1278 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "query.h"
+#include "log.h"
+#include "mp_ws_query.h"
+#include "mp_rs_query.h"
+#include "singletons.h"
+
+static const char negative_data[1] = { 0 };
+
+extern void get_time_func(struct timeval *);
+
+static void clear_config_entry(struct configuration_entry *);
+static void clear_config_entry_part(struct configuration_entry *,
+ const char *, size_t);
+
+static int on_query_startup(struct query_state *);
+static void on_query_destroy(struct query_state *);
+
+static int on_read_request_read1(struct query_state *);
+static int on_read_request_read2(struct query_state *);
+static int on_read_request_process(struct query_state *);
+static int on_read_response_write1(struct query_state *);
+static int on_read_response_write2(struct query_state *);
+
+static int on_rw_mapper(struct query_state *);
+
+static int on_transform_request_read1(struct query_state *);
+static int on_transform_request_read2(struct query_state *);
+static int on_transform_request_process(struct query_state *);
+static int on_transform_response_write1(struct query_state *);
+
+static int on_write_request_read1(struct query_state *);
+static int on_write_request_read2(struct query_state *);
+static int on_negative_write_request_process(struct query_state *);
+static int on_write_request_process(struct query_state *);
+static int on_write_response_write1(struct query_state *);
+
+/*
+ * Clears the specified configuration entry (clears the cache for positive and
+ * and negative entries) and also for all multipart entries.
+ */
+static void
+clear_config_entry(struct configuration_entry *config_entry)
+{
+ size_t i;
+
+ TRACE_IN(clear_config_entry);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ for (i = 0; i < config_entry->mp_cache_entries_size; ++i)
+ transform_cache_entry(
+ config_entry->mp_cache_entries[i],
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry);
+}
+
+/*
+ * Clears the specified configuration entry by deleting only the elements,
+ * that are owned by the user with specified eid_str.
+ */
+static void
+clear_config_entry_part(struct configuration_entry *config_entry,
+ const char *eid_str, size_t eid_str_length)
+{
+ cache_entry *start, *finish, *mp_entry;
+ TRACE_IN(clear_config_entry_part);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ if (configuration_entry_find_mp_cache_entries(config_entry,
+ eid_str, &start, &finish) == 0) {
+ for (mp_entry = start; mp_entry != finish; ++mp_entry)
+ transform_cache_entry(*mp_entry, CTT_CLEAR);
+ }
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry_part);
+}
+
+/*
+ * This function is assigned to the query_state structue on its creation.
+ * It's main purpose is to receive credentials from the client.
+ */
+static int
+on_query_startup(struct query_state *qstate)
+{
+ struct msghdr cred_hdr;
+ struct iovec iov;
+ int elem_type;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ TRACE_IN(on_query_startup);
+ assert(qstate != NULL);
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ memset(&iov, 0, sizeof(struct iovec));
+ iov.iov_base = &elem_type;
+ iov.iov_len = sizeof(int);
+
+ if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ if (cmsg.hdr.cmsg_len != sizeof cmsg
+ || cmsg.hdr.cmsg_level != SOL_SOCKET
+ || cmsg.hdr.cmsg_type != SCM_CREDS) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->uid = cmsg.creds.cmcred_uid;
+ qstate->gid = cmsg.creds.cmcred_gid;
+
+#if defined(NS_CACHED_EID_CHECKING) || defined(NS_STRICT_CACHED_EID_CHECKING)
+/*
+ * This check is probably a bit redundant - per-user cache is always separated
+ * by the euid/egid pair
+ */
+ if (check_query_eids(qstate) != 0) {
+#ifdef NS_STRICT_CACHED_EID_CHECKING
+ TRACE_OUT(on_query_startup);
+ return (-1);
+#else
+ if ((elem_type != CET_READ_REQUEST) &&
+ (elem_type != CET_MP_READ_SESSION_REQUEST) &&
+ (elem_type != CET_WRITE_REQUEST) &&
+ (elem_type != CET_MP_WRITE_SESSION_REQUEST)) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+#endif
+ }
+#endif
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->process_func = on_read_request_read1;
+ break;
+ case CET_TRANSFORM_REQUEST:
+ qstate->process_func = on_transform_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ qstate->process_func = on_mp_write_session_request_read1;
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ qstate->process_func = on_mp_read_session_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ TRACE_OUT(on_query_startup);
+ return (0);
+}
+
+/*
+ * on_rw_mapper is used to process multiple read/write requests during
+ * one connection session. It's never called in the beginning (on query_state
+ * creation) as it does not process the multipart requests and does not
+ * receive credentials
+ */
+static int
+on_rw_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_rw_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_read_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ break;
+ }
+ }
+ TRACE_OUT(on_rw_mapper);
+ return (0);
+}
+
+/*
+ * The default query_destroy function
+ */
+static void
+on_query_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_query_destroy);
+ finalize_comm_element(&qstate->response);
+ finalize_comm_element(&qstate->request);
+ TRACE_OUT(on_query_destroy);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_write_request_read1 and on_write_request_read2 read the request itself
+ * - on_write_request_process processes it (if the client requests to
+ * cache the negative result, the on_negative_write_request_process is used)
+ * - on_write_response_write1 sends the response
+ */
+static int
+on_write_request_read1(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 3;
+ else {
+ init_comm_element(&qstate->request, CET_WRITE_REQUEST);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->entry_length,
+ sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->cache_key_size, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->data_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 3) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->entry_length) ||
+ BUFSIZE_INVALID(write_request->cache_key_size) ||
+ (BUFSIZE_INVALID(write_request->data_size) &&
+ (write_request->data_size != 0))) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ write_request->entry = (char *)malloc(
+ write_request->entry_length + 1);
+ assert(write_request->entry != NULL);
+ memset(write_request->entry, 0,
+ write_request->entry_length + 1);
+
+ write_request->cache_key = (char *)malloc(
+ write_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(write_request->cache_key != NULL);
+ memcpy(write_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+ memset(write_request->cache_key + qstate->eid_str_length, 0,
+ write_request->cache_key_size);
+
+ if (write_request->data_size != 0) {
+ write_request->data = (char *)malloc(
+ write_request->data_size);
+ assert(write_request->data != NULL);
+ memset(write_request->data, 0,
+ write_request->data_size);
+ }
+
+ qstate->kevent_watermark = write_request->entry_length +
+ write_request->cache_key_size +
+ write_request->data_size;
+ qstate->process_func = on_write_request_read2;
+ }
+
+ TRACE_OUT(on_write_request_read1);
+ return (0);
+}
+
+static int
+on_write_request_read2(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read2);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, write_request->entry,
+ write_request->entry_length);
+ result += qstate->read_func(qstate, write_request->cache_key +
+ qstate->eid_str_length, write_request->cache_key_size);
+ if (write_request->data_size != 0)
+ result += qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_write_request_read2);
+ return (-1);
+ }
+ write_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ if (write_request->data_size != 0)
+ qstate->process_func = on_write_request_process;
+ else
+ qstate->process_func = on_negative_write_request_process;
+ TRACE_OUT(on_write_request_read2);
+ return (0);
+}
+
+static int
+on_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_request", "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_write_request_process);
+ return (0);
+}
+
+static int
+on_negative_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_negative_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry (
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("negative_write_request",
+ "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("negative_write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("negative_write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ write_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_negative_write_request_process);
+ return (0);
+}
+
+static int
+on_write_response_write1(struct query_state *qstate)
+{
+ struct cache_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_write_response_write1);
+ write_response = get_cache_write_response(&qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_write_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+
+ TRACE_OUT(on_write_response_write1);
+ return (0);
+}
+
+/*
+ * The functions below are used to process read requests.
+ * - on_read_request_read1 and on_read_request_read2 read the request itself
+ * - on_read_request_process processes it
+ * - on_read_response_write1 and on_read_response_write2 send the response
+ */
+static int
+on_read_request_read1(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 2;
+ else {
+ init_comm_element(&qstate->request, CET_READ_REQUEST);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &read_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &read_request->cache_key_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 2) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(read_request->entry_length) ||
+ BUFSIZE_INVALID(read_request->cache_key_size)) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ read_request->entry = (char *)malloc(
+ read_request->entry_length + 1);
+ assert(read_request->entry != NULL);
+ memset(read_request->entry, 0, read_request->entry_length + 1);
+
+ read_request->cache_key = (char *)malloc(
+ read_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(read_request->cache_key != NULL);
+ memcpy(read_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+ memset(read_request->cache_key + qstate->eid_str_length, 0,
+ read_request->cache_key_size);
+
+ qstate->kevent_watermark = read_request->entry_length +
+ read_request->cache_key_size;
+ qstate->process_func = on_read_request_read2;
+ }
+
+ TRACE_OUT(on_read_request_read1);
+ return (0);
+}
+
+static int
+on_read_request_read2(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read2);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate, read_request->entry,
+ read_request->entry_length);
+ result += qstate->read_func(qstate,
+ read_request->cache_key + qstate->eid_str_length,
+ read_request->cache_key_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_request_read2);
+ return (-1);
+ }
+ read_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_read_request_process;
+
+ TRACE_OUT(on_read_request_read2);
+ return (0);
+}
+
+static int
+on_read_request_process(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ struct cache_read_response *read_response;
+ cache_entry c_entry, neg_c_entry;
+
+ struct agent *lookup_agent;
+ struct common_agent *c_agent;
+ int res;
+
+ TRACE_IN(on_read_request_process);
+ init_comm_element(&qstate->response, CET_READ_RESPONSE);
+ read_response = get_cache_read_response(&qstate->response);
+ read_request = get_cache_read_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, read_request->entry);
+ if (qstate->config_entry == NULL) {
+ read_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_request",
+ "can't find configuration "
+ "entry '%s'. aborting request", read_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ read_response->error_code = EACCES;
+
+ LOG_ERR_2("read_request",
+ "configuration entry '%s' is disabled",
+ read_request->entry);
+ goto fin;
+ }
+
+ /*
+ * if we perform lookups by ourselves, then we don't need to separate
+ * cache entries by euid and egid
+ */
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ memset(read_request->cache_key, 0, qstate->eid_str_length);
+ else {
+#ifdef NS_CACHED_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ /* if the lookup is not self-performing, we check for clients euid/egid */
+ read_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ neg_c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if ((c_entry != NULL) && (neg_c_entry != NULL)) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->data = (char *)malloc(
+ read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = neg_c_entry;
+ if (read_response->error_code == -1) {
+ read_response->error_code = cache_read(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((read_response->error_code == -1) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ free(read_response->data);
+ read_response->data = NULL;
+ read_response->data_size = 0;
+
+ lookup_agent = find_agent(s_agent_table,
+ read_request->entry, COMMON_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == COMMON_AGENT)) {
+ c_agent = (struct common_agent *)lookup_agent;
+ res = c_agent->lookup_func(
+ read_request->cache_key +
+ qstate->eid_str_length,
+ read_request->cache_key_size -
+ qstate->eid_str_length,
+ &read_response->data,
+ &read_response->data_size);
+
+ if (res == NS_SUCCESS) {
+ read_response->error_code = 0;
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ cache_write(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ read_response->data_size);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ } else if ((res == NS_NOTFOUND) ||
+ (res == NS_RETURN)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+ cache_write(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ }
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ read_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_read_response_write1;
+
+ TRACE_OUT(on_read_request_process);
+ return (0);
+}
+
+static int
+on_read_response_write1(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write1);
+ read_response = get_cache_read_response(&qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_read_response_write1);
+ return (0);
+}
+
+static int
+on_read_response_write2(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write2);
+ read_response = get_cache_read_response(&qstate->response);
+ if (read_response->data_size > 0) {
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write2);
+ return (-1);
+ }
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+ TRACE_OUT(on_read_response_write2);
+ return (0);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_transform_request_read1 and on_transform_request_read2 read the
+ * request itself
+ * - on_transform_request_process processes it
+ * - on_transform_response_write1 sends the response
+ */
+static int
+on_transform_request_read1(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else {
+ init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
+ transform_request =
+ get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &transform_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &transform_request->transformation_type, sizeof(int));
+
+ if (result != sizeof(size_t) + sizeof(int)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if ((transform_request->transformation_type != TT_USER) &&
+ (transform_request->transformation_type != TT_ALL)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if (transform_request->entry_length != 0) {
+ if (BUFSIZE_INVALID(transform_request->entry_length)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ transform_request->entry = (char *)malloc(
+ transform_request->entry_length + 1);
+ assert(transform_request->entry != NULL);
+ memset(transform_request->entry, 0,
+ transform_request->entry_length + 1);
+
+ qstate->process_func = on_transform_request_read2;
+ } else
+ qstate->process_func = on_transform_request_process;
+
+ qstate->kevent_watermark = transform_request->entry_length;
+ }
+
+ TRACE_OUT(on_transform_request_read1);
+ return (0);
+}
+
+static int
+on_transform_request_read2(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read2);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate, transform_request->entry,
+ transform_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_transform_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_request_process;
+
+ TRACE_OUT(on_transform_request_read2);
+ return (0);
+}
+
+static int
+on_transform_request_process(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ struct cache_transform_response *transform_response;
+ struct configuration_entry *config_entry;
+ size_t i, size;
+
+ TRACE_IN(on_transform_request_process);
+ init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
+ transform_response = get_cache_transform_response(&qstate->response);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ switch (transform_request->transformation_type) {
+ case TT_USER:
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(s_configuration);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(
+ s_configuration, i);
+
+ if (config_entry->perform_actual_lookups == 0)
+ clear_config_entry_part(config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ LOG_ERR_2("transform_request",
+ "can't transform the cache entry %s"
+ ", because it ised for actual lookups",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry_part(qstate->config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ break;
+ case TT_ALL:
+ if (qstate->euid != 0)
+ transform_response->error_code = -1;
+ else {
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(
+ s_configuration);
+ for (i = 0; i < size; ++i) {
+ clear_config_entry(
+ configuration_get_entry(
+ s_configuration, i));
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration,
+ transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry(qstate->config_entry);
+ }
+ }
+ break;
+ default:
+ transform_response->error_code = -1;
+ }
+
+fin:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_response_write1;
+ TRACE_OUT(on_transform_request_process);
+ return (0);
+}
+
+static int
+on_transform_response_write1(struct query_state *qstate)
+{
+ struct cache_transform_response *transform_response;
+ ssize_t result;
+
+ TRACE_IN(on_transform_response_write1);
+ transform_response = get_cache_transform_response(&qstate->response);
+ result = qstate->write_func(qstate, &transform_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_transform_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_transform_response_write1);
+ return (0);
+}
+
+/*
+ * Checks if the client's euid and egid do not differ from its uid and gid.
+ * Returns 0 on success.
+ */
+int
+check_query_eids(struct query_state *qstate)
+{
+
+ return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" read - when the buffer is
+ * too large to be received during one socket read operation
+ */
+ssize_t
+query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_read);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(buf, qstate->io_buffer_p, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ free(qstate->io_buffer);
+ qstate->io_buffer = NULL;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_read);
+ return (result);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" write - when the buffer is
+ * too large to be sent during one socket write operation
+ */
+ssize_t
+query_io_buffer_write(struct query_state *qstate, const void *buf,
+ size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_write);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(qstate->io_buffer_p, buf, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ qstate->use_alternate_io = 1;
+ qstate->io_buffer_p = qstate->io_buffer;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_write);
+ return (result);
+}
+
+/*
+ * The default "read" function, which reads data directly from socket
+ */
+ssize_t
+query_socket_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_read);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_read);
+ return (-1);
+ }
+
+ result = read(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_read);
+ return (result);
+}
+
+/*
+ * The default "write" function, which writes data directly to socket
+ */
+ssize_t
+query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_write);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_write);
+ return (-1);
+ }
+
+ result = write(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_write);
+ return (result);
+}
+
+/*
+ * Initializes the query_state structure by filling it with the default values.
+ */
+struct query_state *
+init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid)
+{
+ struct query_state *retval;
+
+ TRACE_IN(init_query_state);
+ retval = (struct query_state *)malloc(sizeof(struct query_state));
+ assert(retval != NULL);
+ memset(retval, 0, sizeof(struct query_state));
+
+ retval->sockfd = sockfd;
+ retval->kevent_filter = EVFILT_READ;
+ retval->kevent_watermark = kevent_watermark;
+
+ retval->euid = euid;
+ retval->egid = egid;
+ retval->uid = retval->gid = -1;
+
+ if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
+ retval->egid) == -1) {
+ free(retval);
+ return (NULL);
+ }
+ retval->eid_str_length = strlen(retval->eid_str);
+
+ init_comm_element(&retval->request, CET_UNDEFINED);
+ init_comm_element(&retval->response, CET_UNDEFINED);
+ retval->process_func = on_query_startup;
+ retval->destroy_func = on_query_destroy;
+
+ retval->write_func = query_socket_write;
+ retval->read_func = query_socket_read;
+
+ get_time_func(&retval->creation_time);
+ memcpy(&retval->timeout, &s_configuration->query_timeout,
+ sizeof(struct timeval));
+
+ TRACE_OUT(init_query_state);
+ return (retval);
+}
+
+void
+destroy_query_state(struct query_state *qstate)
+{
+
+ TRACE_IN(destroy_query_state);
+ if (qstate->eid_str != NULL)
+ free(qstate->eid_str);
+
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->destroy_func(qstate);
+ free(qstate);
+ TRACE_OUT(destroy_query_state);
+}
diff --git a/usr.sbin/nscd/query.h b/usr.sbin/nscd/query.h
new file mode 100644
index 0000000..5c4e918
--- /dev/null
+++ b/usr.sbin/nscd/query.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_QUERY_H__
+#define __CACHED_QUERY_H__
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "cachelib.h"
+#include "config.h"
+#include "protocol.h"
+
+struct query_state;
+struct configuration;
+struct configuration_entry;
+
+typedef int (*query_process_func)(struct query_state *);
+typedef void (*query_destroy_func)(struct query_state *);
+typedef ssize_t (*query_read_func)(struct query_state *, void *, size_t);
+typedef ssize_t (*query_write_func)(struct query_state *, const void *,
+ size_t);
+
+/*
+ * The query state structure contains the information to process all types of
+ * requests and to send all types of responses.
+ */
+struct query_state {
+ struct timeval creation_time;
+ struct timeval timeout;
+
+ struct comm_element request;
+ struct comm_element response;
+ struct configuration_entry *config_entry;
+ void *mdata;
+
+ query_process_func process_func; /* called on each event */
+ query_destroy_func destroy_func; /* called on destroy */
+
+ /*
+ * By substituting these functions we can opaquely send and received
+ * very large buffers
+ */
+ query_write_func write_func; /* data write function */
+ query_read_func read_func; /* data read function */
+
+ char *eid_str; /* the user-identifying string (euid_egid_) */
+ size_t eid_str_length;
+
+ uid_t euid; /* euid of the caller, received via getpeereid */
+ uid_t uid; /* uid of the caller, received via credentials */
+ gid_t egid; /* egid of the caller, received via getpeereid */
+ gid_t gid; /* gid of the caller received via credentials */
+
+ size_t io_buffer_size;
+ size_t io_buffer_watermark;
+ size_t kevent_watermark; /* bytes to be sent/received */
+ int sockfd; /* the unix socket to read/write */
+ int kevent_filter; /* EVFILT_READ or EVFILT_WRITE */
+ int socket_failed; /* set to 1 if the socket doesn't work correctly */
+
+ /*
+ * These fields are used to opaquely proceed sending/receiving of
+ * the large buffers
+ */
+ char *io_buffer;
+ char *io_buffer_p;
+ int io_buffer_filter;
+ int use_alternate_io;
+};
+
+extern int check_query_eids(struct query_state *);
+
+extern ssize_t query_io_buffer_read(struct query_state *, void *, size_t);
+extern ssize_t query_io_buffer_write(struct query_state *, const void *,
+ size_t);
+
+extern ssize_t query_socket_read(struct query_state *, void *, size_t);
+extern ssize_t query_socket_write(struct query_state *, const void *,
+ size_t);
+
+extern struct query_state *init_query_state(int, size_t, uid_t, gid_t);
+extern void destroy_query_state(struct query_state *);
+
+#endif
diff --git a/usr.sbin/nscd/singletons.c b/usr.sbin/nscd/singletons.c
new file mode 100644
index 0000000..669d12b
--- /dev/null
+++ b/usr.sbin/nscd/singletons.c
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "singletons.h"
+
+struct configuration *s_configuration = NULL;
+cache s_cache = INVALID_CACHE;
+struct runtime_env *s_runtime_env = NULL;
+struct agent_table *s_agent_table = NULL;
diff --git a/usr.sbin/nscd/singletons.h b/usr.sbin/nscd/singletons.h
new file mode 100644
index 0000000..918519c
--- /dev/null
+++ b/usr.sbin/nscd/singletons.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CACHED_SINGLETONS_H__
+#define __CACHED_SINGLETONS_H__
+
+#include "cachelib.h"
+#include "config.h"
+#include "agent.h"
+
+struct runtime_env {
+ int queue;
+ int sockfd;
+ int finished; /* for future use */
+};
+
+extern struct configuration *s_configuration;
+extern cache s_cache;
+extern struct runtime_env *s_runtime_env;
+extern struct agent_table *s_agent_table;
+
+#endif
OpenPOWER on IntegriCloud