- 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
This commit is contained in:
parent
4b38e5bbca
commit
e14f1c3b3b
@ -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 \
|
||||
|
12
etc/cached.conf
Normal file
12
etc/cached.conf
Normal file
@ -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
|
@ -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)
|
||||
|
12
etc/nscd.conf
Normal file
12
etc/nscd.conf
Normal file
@ -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
|
@ -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}
|
||||
|
||||
|
30
etc/rc.d/cached
Normal file
30
etc/rc.d/cached
Normal file
@ -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"
|
30
etc/rc.d/nscd
Normal file
30
etc/rc.d/nscd
Normal file
@ -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"
|
@ -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
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
197
lib/libc/include/nscache.h
Normal file
197
lib/libc/include/nscache.h
Normal file
@ -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
|
107
lib/libc/include/nscachedcli.h
Normal file
107
lib/libc/include/nscachedcli.h
Normal file
@ -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
|
@ -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
|
||||
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
@ -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 }
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 *
|
||||
|
@ -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 (NS_UNAVAIL);
|
||||
|
||||
__setprotoent_p(f, ped);
|
||||
return (NS_UNAVAIL);
|
||||
}
|
||||
|
||||
static int
|
||||
files_endprotoent(void *retval, void *mdata, va_list ap)
|
||||
{
|
||||
struct protoent_data *ped;
|
||||
|
||||
if ((ped = __protoent_data_init()) == NULL)
|
||||
return;
|
||||
__setprotoent_p(f, ped);
|
||||
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)
|
||||
{
|
||||
struct protoent_data *ped;
|
||||
#ifdef NS_CACHING
|
||||
static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
|
||||
protocols, (void *)nss_lt_all,
|
||||
NULL, NULL);
|
||||
#endif
|
||||
|
||||
if ((ped = __protoent_data_init()) == NULL)
|
||||
return;
|
||||
__endprotoent_p(ped);
|
||||
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 *
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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,12 +649,21 @@ 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 },
|
||||
NS_NIS_CB(_nis_ghbyaddr, NULL)
|
||||
#ifdef ICMPNL
|
||||
{ NSSRC_ICMP, _icmp_ghbyaddr, NULL },
|
||||
#endif
|
||||
#ifdef NS_CACHING
|
||||
NS_CACHE_CB(&cache_info)
|
||||
#endif
|
||||
{ 0 }
|
||||
};
|
||||
|
@ -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);
|
||||
|
438
lib/libc/net/nscache.c
Normal file
438
lib/libc/net/nscache.c
Normal file
@ -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(¶ms, 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(¶ms);
|
||||
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(¶ms, 0, sizeof(struct cached_connection_params));
|
||||
params.socket_path = CACHED_SOCKET_PATH;
|
||||
|
||||
connection = __open_cached_connection(¶ms);
|
||||
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(¶ms, 0, sizeof(struct cached_connection_params));
|
||||
params.socket_path = CACHED_SOCKET_PATH;
|
||||
|
||||
connection = __open_cached_connection(¶ms);
|
||||
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(¶ms, 0, sizeof(struct cached_connection_params));
|
||||
params.socket_path = CACHED_SOCKET_PATH;
|
||||
|
||||
rs = __open_cached_mp_read_session(¶ms,
|
||||
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(¶ms, 0, sizeof(struct cached_connection_params));
|
||||
params.socket_path = CACHED_SOCKET_PATH;
|
||||
|
||||
ws = __open_cached_mp_write_session(¶ms,
|
||||
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);
|
||||
}
|
576
lib/libc/net/nscachedcli.c
Normal file
576
lib/libc/net/nscachedcli.c
Normal file
@ -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, ¬ification, 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, ¬ification, 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);
|
||||
}
|
@ -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:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 \
|
||||
|
@ -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
|
||||
|
19
usr.sbin/cached/Makefile
Normal file
19
usr.sbin/cached/Makefile
Normal file
@ -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>
|
127
usr.sbin/cached/agent.c
Normal file
127
usr.sbin/cached/agent.c
Normal file
@ -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);
|
||||
}
|
72
usr.sbin/cached/agent.h
Normal file
72
usr.sbin/cached/agent.h
Normal file
@ -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
|
3
usr.sbin/cached/agents/Makefile.inc
Normal file
3
usr.sbin/cached/agents/Makefile.inc
Normal file
@ -0,0 +1,3 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SRCS += passwd.c group.c services.c
|
262
usr.sbin/cached/agents/group.c
Normal file
262
usr.sbin/cached/agents/group.c
Normal file
@ -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);
|
||||
}
|
32
usr.sbin/cached/agents/group.h
Normal file
32
usr.sbin/cached/agents/group.h
Normal file
@ -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();
|
269
usr.sbin/cached/agents/passwd.c
Normal file
269
usr.sbin/cached/agents/passwd.c
Normal file
@ -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);
|
||||
}
|
32
usr.sbin/cached/agents/passwd.h
Normal file
32
usr.sbin/cached/agents/passwd.h
Normal file
@ -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();
|
286
usr.sbin/cached/agents/services.c
Normal file
286
usr.sbin/cached/agents/services.c
Normal file
@ -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);
|
||||
}
|
32
usr.sbin/cached/agents/services.h
Normal file
32
usr.sbin/cached/agents/services.h
Normal file
@ -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();
|
148
usr.sbin/cached/cached.8
Normal file
148
usr.sbin/cached/cached.8
Normal file
@ -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
|
884
usr.sbin/cached/cached.c
Normal file
884
usr.sbin/cached/cached.c
Normal file
@ -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(¶ms, 0, sizeof(struct cache_params));
|
||||
params.get_time_func = get_time_func;
|
||||
retval = init_cache(¶ms);
|
||||
|
||||
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);
|
||||
}
|
102
usr.sbin/cached/cached.conf.5
Normal file
102
usr.sbin/cached/cached.conf.5
Normal file
@ -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
|
284
usr.sbin/cached/cachedcli.c
Normal file
284
usr.sbin/cached/cachedcli.c
Normal file
@ -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);
|
||||
}
|
57
usr.sbin/cached/cachedcli.h
Normal file
57
usr.sbin/cached/cachedcli.h
Normal file
@ -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
|
1234
usr.sbin/cached/cachelib.c
Normal file
1234
usr.sbin/cached/cachelib.c
Normal file
File diff suppressed because it is too large
Load Diff
281
usr.sbin/cached/cachelib.h
Normal file
281
usr.sbin/cached/cachelib.h
Normal file
@ -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
|
590
usr.sbin/cached/cacheplcs.c
Normal file
590
usr.sbin/cached/cacheplcs.c
Normal file
@ -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);
|
||||
}
|
137
usr.sbin/cached/cacheplcs.h
Normal file
137
usr.sbin/cached/cacheplcs.h
Normal file
@ -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
|
588
usr.sbin/cached/config.c
Normal file
588
usr.sbin/cached/config.c
Normal file
@ -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);
|
||||
}
|
156
usr.sbin/cached/config.h
Normal file
156
usr.sbin/cached/config.h
Normal file
@ -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
|
149
usr.sbin/cached/debug.c
Normal file
149
usr.sbin/cached/debug.c
Normal file
@ -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;
|
||||
}
|
67
usr.sbin/cached/debug.h
Normal file
67
usr.sbin/cached/debug.h
Normal file
@ -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
|
218
usr.sbin/cached/hashtable.h
Normal file
218
usr.sbin/cached/hashtable.h
Normal file
@ -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
|
78
usr.sbin/cached/log.c
Normal file
78
usr.sbin/cached/log.c
Normal file
@ -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);
|
||||
}
|
43
usr.sbin/cached/log.h
Normal file
43
usr.sbin/cached/log.h
Normal file
@ -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
|
537
usr.sbin/cached/mp_rs_query.c
Normal file
537
usr.sbin/cached/mp_rs_query.c
Normal file
@ -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);
|
||||
}
|
34
usr.sbin/cached/mp_rs_query.h
Normal file
34
usr.sbin/cached/mp_rs_query.h
Normal file
@ -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
|
548
usr.sbin/cached/mp_ws_query.c
Normal file
548
usr.sbin/cached/mp_ws_query.c
Normal file
@ -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);
|
||||
}
|
36
usr.sbin/cached/mp_ws_query.h
Normal file
36
usr.sbin/cached/mp_ws_query.h
Normal file
@ -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
|
474
usr.sbin/cached/parser.c
Normal file
474
usr.sbin/cached/parser.c
Normal file
@ -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);
|
||||
}
|
35
usr.sbin/cached/parser.h
Normal file
35
usr.sbin/cached/parser.h
Normal file
@ -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
|
550
usr.sbin/cached/protocol.c
Normal file
550
usr.sbin/cached/protocol.c
Normal file
@ -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);
|
||||
}
|
265
usr.sbin/cached/protocol.h
Normal file
265
usr.sbin/cached/protocol.h
Normal file
@ -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
|
1278
usr.sbin/cached/query.c
Normal file
1278
usr.sbin/cached/query.c
Normal file
File diff suppressed because it is too large
Load Diff
110
usr.sbin/cached/query.h
Normal file
110
usr.sbin/cached/query.h
Normal file
@ -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
|
36
usr.sbin/cached/singletons.c
Normal file
36
usr.sbin/cached/singletons.c
Normal file
@ -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;
|
47
usr.sbin/cached/singletons.h
Normal file
47
usr.sbin/cached/singletons.h
Normal file
@ -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
|
19
usr.sbin/nscd/Makefile
Normal file
19
usr.sbin/nscd/Makefile
Normal file
@ -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>
|
127
usr.sbin/nscd/agent.c
Normal file
127
usr.sbin/nscd/agent.c
Normal file
@ -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);
|
||||
}
|
72
usr.sbin/nscd/agent.h
Normal file
72
usr.sbin/nscd/agent.h
Normal file
@ -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
|
3
usr.sbin/nscd/agents/Makefile.inc
Normal file
3
usr.sbin/nscd/agents/Makefile.inc
Normal file
@ -0,0 +1,3 @@
|
||||
# $FreeBSD$
|
||||
|
||||
SRCS += passwd.c group.c services.c
|
262
usr.sbin/nscd/agents/group.c
Normal file
262
usr.sbin/nscd/agents/group.c
Normal file
@ -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);
|
||||
}
|
32
usr.sbin/nscd/agents/group.h
Normal file
32
usr.sbin/nscd/agents/group.h
Normal file
@ -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();
|
269
usr.sbin/nscd/agents/passwd.c
Normal file
269
usr.sbin/nscd/agents/passwd.c
Normal file
@ -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);
|
||||
}
|
32
usr.sbin/nscd/agents/passwd.h
Normal file
32
usr.sbin/nscd/agents/passwd.h
Normal file
@ -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();
|
286
usr.sbin/nscd/agents/services.c
Normal file
286
usr.sbin/nscd/agents/services.c
Normal file
@ -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);
|
||||
}
|
32
usr.sbin/nscd/agents/services.h
Normal file
32
usr.sbin/nscd/agents/services.h
Normal file
@ -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();
|
1234
usr.sbin/nscd/cachelib.c
Normal file
1234
usr.sbin/nscd/cachelib.c
Normal file
File diff suppressed because it is too large
Load Diff
281
usr.sbin/nscd/cachelib.h
Normal file
281
usr.sbin/nscd/cachelib.h
Normal file
@ -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
|
590
usr.sbin/nscd/cacheplcs.c
Normal file
590
usr.sbin/nscd/cacheplcs.c
Normal file
@ -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);
|
||||
}
|
137
usr.sbin/nscd/cacheplcs.h
Normal file
137
usr.sbin/nscd/cacheplcs.h
Normal file
@ -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
|
588
usr.sbin/nscd/config.c
Normal file
588
usr.sbin/nscd/config.c
Normal file
@ -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);
|
||||
}
|
156
usr.sbin/nscd/config.h
Normal file
156
usr.sbin/nscd/config.h
Normal file
@ -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
|
149
usr.sbin/nscd/debug.c
Normal file
149
usr.sbin/nscd/debug.c
Normal file
@ -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;
|
||||
}
|
67
usr.sbin/nscd/debug.h
Normal file
67
usr.sbin/nscd/debug.h
Normal file
@ -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
|
218
usr.sbin/nscd/hashtable.h
Normal file
218
usr.sbin/nscd/hashtable.h
Normal file
@ -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
|
78
usr.sbin/nscd/log.c
Normal file
78
usr.sbin/nscd/log.c
Normal file
@ -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);
|
||||
}
|
43
usr.sbin/nscd/log.h
Normal file
43
usr.sbin/nscd/log.h
Normal file
@ -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
|
34
usr.sbin/nscd/mp_rs_query.
Normal file
34
usr.sbin/nscd/mp_rs_query.
Normal file
@ -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
|
537
usr.sbin/nscd/mp_rs_query.c
Normal file
537
usr.sbin/nscd/mp_rs_query.c
Normal file
@ -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);
|
||||
}
|
34
usr.sbin/nscd/mp_rs_query.h
Normal file
34
usr.sbin/nscd/mp_rs_query.h
Normal file
@ -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
|
548
usr.sbin/nscd/mp_ws_query.c
Normal file
548
usr.sbin/nscd/mp_ws_query.c
Normal file
@ -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);
|
||||
}
|
36
usr.sbin/nscd/mp_ws_query.h
Normal file
36
usr.sbin/nscd/mp_ws_query.h
Normal file
@ -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
|
148
usr.sbin/nscd/nscd.8
Normal file
148
usr.sbin/nscd/nscd.8
Normal file
@ -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
|
884
usr.sbin/nscd/nscd.c
Normal file
884
usr.sbin/nscd/nscd.c
Normal file
@ -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(¶ms, 0, sizeof(struct cache_params));
|
||||
params.get_time_func = get_time_func;
|
||||
retval = init_cache(¶ms);
|
||||
|
||||
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);
|
||||
}
|
102
usr.sbin/nscd/nscd.conf.5
Normal file
102
usr.sbin/nscd/nscd.conf.5
Normal file
@ -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
|
284
usr.sbin/nscd/nscdcli.c
Normal file
284
usr.sbin/nscd/nscdcli.c
Normal file
@ -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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user