memcached-1.4.25/ 0000775 0001750 0001750 00000000000 12623542633 010522 5 0000000 0000000 memcached-1.4.25/config.guess 0000755 0001750 0001750 00000123550 12405615464 012767 0000000 0000000 #! /bin/sh
# Attempt to guess a canonical system name.
# Copyright 1992-2014 Free Software Foundation, Inc.
timestamp='2014-03-23'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see .
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
#
# Originally written by Per Bothner.
#
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
#
# Please send patches with a ChangeLog entry to config-patches@gnu.org.
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system \`$me' is run on.
Operation modes:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
Report bugs and patches to ."
version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
Copyright 1992-2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try \`$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
--version | -v )
echo "$version" ; exit ;;
--help | --h* | -h )
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
echo "$me: invalid option $1$help" >&2
exit 1 ;;
* )
break ;;
esac
done
if test $# != 0; then
echo "$me: too many arguments$help" >&2
exit 1
fi
trap 'exit 1' 1 2 15
# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
# compiler to aid in system detection is discouraged as it requires
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.
# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team.
set_cc_for_build='
trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
: ${TMPDIR=/tmp} ;
{ tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
dummy=$tmp/dummy ;
tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
case $CC_FOR_BUILD,$HOST_CC,$CC in
,,) echo "int x;" > $dummy.c ;
for c in cc gcc c89 c99 ; do
if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
CC_FOR_BUILD="$c"; break ;
fi ;
done ;
if test x"$CC_FOR_BUILD" = x ; then
CC_FOR_BUILD=no_compiler_found ;
fi
;;
,,*) CC_FOR_BUILD=$CC ;;
,*,*) CC_FOR_BUILD=$HOST_CC ;;
esac ; set_cc_for_build= ;'
# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
# (ghazi@noc.rutgers.edu 1994-08-24)
if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
PATH=$PATH:/.attbin ; export PATH
fi
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
case "${UNAME_SYSTEM}" in
Linux|GNU|GNU/*)
# If the system lacks a compiler, then just pick glibc.
# We could probably try harder.
LIBC=gnu
eval $set_cc_for_build
cat <<-EOF > $dummy.c
#include
#if defined(__UCLIBC__)
LIBC=uclibc
#elif defined(__dietlibc__)
LIBC=dietlibc
#else
LIBC=gnu
#endif
EOF
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
;;
esac
# Note: order is significant - the case branches are not exclusive.
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
# compatibility and a consistent mechanism for selecting the
# object file format.
#
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
sysctl="sysctl -n hw.machine_arch"
UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
/usr/sbin/$sysctl 2>/dev/null || echo unknown)`
case "${UNAME_MACHINE_ARCH}" in
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
esac
# The Operating System including object format, if it has switched
# to ELF recently, or will in the future.
case "${UNAME_MACHINE_ARCH}" in
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
eval $set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ELF__
then
# Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
# Return netbsd for either. FIX?
os=netbsd
else
os=netbsdelf
fi
;;
*)
os=netbsd
;;
esac
# The OS release
# Debian GNU/NetBSD machines have a different userland, and
# thus, need a distinct triplet. However, they do not need
# kernel version information, so it can be replaced with a
# suitable tag, in the style of linux-gnu.
case "${UNAME_VERSION}" in
Debian*)
release='-gnu'
;;
*)
release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
echo "${machine}-${os}${release}"
exit ;;
*:Bitrig:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
exit ;;
*:ekkoBSD:*:*)
echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
exit ;;
*:SolidBSD:*:*)
echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
exit ;;
macppc:MirBSD:*:*)
echo powerpc-unknown-mirbsd${UNAME_RELEASE}
exit ;;
*:MirBSD:*:*)
echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
exit ;;
alpha:OSF1:*:*)
case $UNAME_RELEASE in
*4.0)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
# OSF/1 and Tru64 systems produced since 1995. I hope that
# covers most systems running today. This code pipes the CPU
# types through head -n 1, so we only detect the type of CPU 0.
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
case "$ALPHA_CPU_TYPE" in
"EV4 (21064)")
UNAME_MACHINE="alpha" ;;
"EV4.5 (21064)")
UNAME_MACHINE="alpha" ;;
"LCA4 (21066/21068)")
UNAME_MACHINE="alpha" ;;
"EV5 (21164)")
UNAME_MACHINE="alphaev5" ;;
"EV5.6 (21164A)")
UNAME_MACHINE="alphaev56" ;;
"EV5.6 (21164PC)")
UNAME_MACHINE="alphapca56" ;;
"EV5.7 (21164PC)")
UNAME_MACHINE="alphapca57" ;;
"EV6 (21264)")
UNAME_MACHINE="alphaev6" ;;
"EV6.7 (21264A)")
UNAME_MACHINE="alphaev67" ;;
"EV6.8CB (21264C)")
UNAME_MACHINE="alphaev68" ;;
"EV6.8AL (21264B)")
UNAME_MACHINE="alphaev68" ;;
"EV6.8CX (21264D)")
UNAME_MACHINE="alphaev68" ;;
"EV6.9A (21264/EV69A)")
UNAME_MACHINE="alphaev69" ;;
"EV7 (21364)")
UNAME_MACHINE="alphaev7" ;;
"EV7.9 (21364A)")
UNAME_MACHINE="alphaev79" ;;
esac
# A Pn.n version is a patched version.
# A Vn.n version is a released version.
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
exitcode=$?
trap '' 0
exit $exitcode ;;
Alpha\ *:Windows_NT*:*)
# How do we know it's Interix rather than the generic POSIX subsystem?
# Should we change UNAME_MACHINE based on the output of uname instead
# of the specific Alpha model?
echo alpha-pc-interix
exit ;;
21064:Windows_NT:50:3)
echo alpha-dec-winnt3.5
exit ;;
Amiga*:UNIX_System_V:4.0:*)
echo m68k-unknown-sysv4
exit ;;
*:[Aa]miga[Oo][Ss]:*:*)
echo ${UNAME_MACHINE}-unknown-amigaos
exit ;;
*:[Mm]orph[Oo][Ss]:*:*)
echo ${UNAME_MACHINE}-unknown-morphos
exit ;;
*:OS/390:*:*)
echo i370-ibm-openedition
exit ;;
*:z/VM:*:*)
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
echo powerpc-ibm-os400
exit ;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
echo arm-acorn-riscix${UNAME_RELEASE}
exit ;;
arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
echo hppa1.1-hitachi-hiuxmpp
exit ;;
Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
if test "`(/bin/universe) 2>/dev/null`" = att ; then
echo pyramid-pyramid-sysv3
else
echo pyramid-pyramid-bsd
fi
exit ;;
NILE*:*:*:dcosx)
echo pyramid-pyramid-svr4
exit ;;
DRS?6000:unix:4.0:6*)
echo sparc-icl-nx6
exit ;;
DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
case `/usr/bin/uname -p` in
sparc) echo sparc-icl-nx7; exit ;;
esac ;;
s390x:SunOS:*:*)
echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4H:SunOS:5.*:*)
echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
echo i386-pc-auroraux${UNAME_RELEASE}
exit ;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
eval $set_cc_for_build
SUN_ARCH="i386"
# If there is a compiler, see if it is configured for 64-bit objects.
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
# This test works for both compilers.
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
SUN_ARCH="x86_64"
fi
fi
echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:6*:*)
# According to config.sub, this is the proper way to canonicalize
# SunOS6. Hard to guess exactly what SunOS6 will be like, but
# it's likely to be more like Solaris than SunOS4.
echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
sun4*:SunOS:*:*)
case "`/usr/bin/arch -k`" in
Series*|S4*)
UNAME_RELEASE=`uname -v`
;;
esac
# Japanese Language versions have a version number like `4.1.3-JL'.
echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
exit ;;
sun3*:SunOS:*:*)
echo m68k-sun-sunos${UNAME_RELEASE}
exit ;;
sun*:*:4.2BSD:*)
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
case "`/bin/arch`" in
sun3)
echo m68k-sun-sunos${UNAME_RELEASE}
;;
sun4)
echo sparc-sun-sunos${UNAME_RELEASE}
;;
esac
exit ;;
aushp:SunOS:*:*)
echo sparc-auspex-sunos${UNAME_RELEASE}
exit ;;
# The situation for MiNT is a little confusing. The machine name
# can be virtually everything (everything which is not
# "atarist" or "atariste" at least should have a processor
# > m68000). The system name ranges from "MiNT" over "FreeMiNT"
# to the lowercase version "mint" (or "freemint"). Finally
# the system name "TOS" denotes a system which is actually not
# MiNT. But MiNT is downward compatible to TOS, so this should
# be no problem.
atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
echo m68k-milan-mint${UNAME_RELEASE}
exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
echo m68k-hades-mint${UNAME_RELEASE}
exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
echo m68k-unknown-mint${UNAME_RELEASE}
exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
powerpc:machten:*:*)
echo powerpc-apple-machten${UNAME_RELEASE}
exit ;;
RISC*:Mach:*:*)
echo mips-dec-mach_bsd4.3
exit ;;
RISC*:ULTRIX:*:*)
echo mips-dec-ultrix${UNAME_RELEASE}
exit ;;
VAX*:ULTRIX*:*:*)
echo vax-dec-ultrix${UNAME_RELEASE}
exit ;;
2020:CLIX:*:* | 2430:CLIX:*:*)
echo clipper-intergraph-clix${UNAME_RELEASE}
exit ;;
mips:*:*:UMIPS | mips:*:*:RISCos)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#ifdef __cplusplus
#include /* for printf() prototype */
int main (int argc, char *argv[]) {
#else
int main (argc, argv) int argc; char *argv[]; {
#endif
#if defined (host_mips) && defined (MIPSEB)
#if defined (SYSTYPE_SYSV)
printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
#endif
#if defined (SYSTYPE_SVR4)
printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
#endif
#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
#endif
#endif
exit (-1);
}
EOF
$CC_FOR_BUILD -o $dummy $dummy.c &&
dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
SYSTEM_NAME=`$dummy $dummyarg` &&
{ echo "$SYSTEM_NAME"; exit; }
echo mips-mips-riscos${UNAME_RELEASE}
exit ;;
Motorola:PowerMAX_OS:*:*)
echo powerpc-motorola-powermax
exit ;;
Motorola:*:4.3:PL8-*)
echo powerpc-harris-powermax
exit ;;
Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
echo powerpc-harris-powermax
exit ;;
Night_Hawk:Power_UNIX:*:*)
echo powerpc-harris-powerunix
exit ;;
m88k:CX/UX:7*:*)
echo m88k-harris-cxux7
exit ;;
m88k:*:4*:R4*)
echo m88k-motorola-sysv4
exit ;;
m88k:*:3*:R3*)
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures
UNAME_PROCESSOR=`/usr/bin/uname -p`
if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
then
if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
[ ${TARGET_BINARY_INTERFACE}x = x ]
then
echo m88k-dg-dgux${UNAME_RELEASE}
else
echo m88k-dg-dguxbcs${UNAME_RELEASE}
fi
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
M88*:*:R3*:*)
# Delta 88k system running SVR3
echo m88k-motorola-sysv3
exit ;;
XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
echo m88k-tektronix-sysv3
exit ;;
Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
echo m68k-tektronix-bsd
exit ;;
*:IRIX*:*:*)
echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
exit ;;
????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
i*86:AIX:*:*)
echo i386-ibm-aix
exit ;;
ia64:AIX:*:*)
if [ -x /usr/bin/oslevel ] ; then
IBM_REV=`/usr/bin/oslevel`
else
IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
fi
echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
exit ;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#include
main()
{
if (!__power_pc())
exit(1);
puts("powerpc-ibm-aix3.2.5");
exit(0);
}
EOF
if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
then
echo "$SYSTEM_NAME"
else
echo rs6000-ibm-aix3.2.5
fi
elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
echo rs6000-ibm-aix3.2.4
else
echo rs6000-ibm-aix3.2
fi
exit ;;
*:AIX:*:[4567])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
IBM_ARCH=powerpc
fi
if [ -x /usr/bin/oslevel ] ; then
IBM_REV=`/usr/bin/oslevel`
else
IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
fi
echo ${IBM_ARCH}-ibm-aix${IBM_REV}
exit ;;
*:AIX:*:*)
echo rs6000-ibm-aix
exit ;;
ibmrt:4.4BSD:*|romp-ibm:BSD:*)
echo romp-ibm-bsd4.4
exit ;;
ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
exit ;; # report: romp-ibm BSD 4.3
*:BOSX:*:*)
echo rs6000-bull-bosx
exit ;;
DPX/2?00:B.O.S.:*:*)
echo m68k-bull-sysv3
exit ;;
9000/[34]??:4.3bsd:1.*:*)
echo m68k-hp-bsd
exit ;;
hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
echo m68k-hp-bsd4.4
exit ;;
9000/[34678]??:HP-UX:*:*)
HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
case "${UNAME_MACHINE}" in
9000/31? ) HP_ARCH=m68000 ;;
9000/[34]?? ) HP_ARCH=m68k ;;
9000/[678][0-9][0-9])
if [ -x /usr/bin/getconf ]; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
case "${sc_cpu_version}" in
523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
532) # CPU_PA_RISC2_0
case "${sc_kernel_bits}" in
32) HP_ARCH="hppa2.0n" ;;
64) HP_ARCH="hppa2.0w" ;;
'') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
esac ;;
esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#define _HPUX_SOURCE
#include
#include
int main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
#endif
long cpu = sysconf (_SC_CPU_VERSION);
switch (cpu)
{
case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
case CPU_PA_RISC2_0:
#if defined(_SC_KERNEL_BITS)
switch (bits)
{
case 64: puts ("hppa2.0w"); break;
case 32: puts ("hppa2.0n"); break;
default: puts ("hppa2.0"); break;
} break;
#else /* !defined(_SC_KERNEL_BITS) */
puts ("hppa2.0"); break;
#endif
default: puts ("hppa1.0"); break;
}
exit (0);
}
EOF
(CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
if [ ${HP_ARCH} = "hppa2.0w" ]
then
eval $set_cc_for_build
# hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
# 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
# generating 64-bit code. GNU and HP use different nomenclature:
#
# $ CC_FOR_BUILD=cc ./config.guess
# => hppa2.0w-hp-hpux11.23
# $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
# => hppa64-hp-hpux11.23
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
grep -q __LP64__
then
HP_ARCH="hppa2.0w"
else
HP_ARCH="hppa64"
fi
fi
echo ${HP_ARCH}-hp-hpux${HPUX_REV}
exit ;;
ia64:HP-UX:*:*)
HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
echo ia64-hp-hpux${HPUX_REV}
exit ;;
3050*:HI-UX:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#include
int
main ()
{
long cpu = sysconf (_SC_CPU_VERSION);
/* The order matters, because CPU_IS_HP_MC68K erroneously returns
true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
results, however. */
if (CPU_IS_PA_RISC (cpu))
{
switch (cpu)
{
case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
default: puts ("hppa-hitachi-hiuxwe2"); break;
}
}
else if (CPU_IS_HP_MC68K (cpu))
puts ("m68k-hitachi-hiuxwe2");
else puts ("unknown-hitachi-hiuxwe2");
exit (0);
}
EOF
$CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
{ echo "$SYSTEM_NAME"; exit; }
echo unknown-hitachi-hiuxwe2
exit ;;
9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
echo hppa1.1-hp-bsd
exit ;;
9000/8??:4.3bsd:*:*)
echo hppa1.0-hp-bsd
exit ;;
*9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
echo hppa1.0-hp-mpeix
exit ;;
hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
echo hppa1.1-hp-osf
exit ;;
hp8??:OSF1:*:*)
echo hppa1.0-hp-osf
exit ;;
i*86:OSF1:*:*)
if [ -x /usr/sbin/sysversion ] ; then
echo ${UNAME_MACHINE}-unknown-osf1mk
else
echo ${UNAME_MACHINE}-unknown-osf1
fi
exit ;;
parisc*:Lites*:*:*)
echo hppa1.1-hp-lites
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*[A-Z]90:*:*:*)
echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
-e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
-e 's/\.[^.]*$/.X/'
exit ;;
CRAY*TS:*:*:*)
echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*T3E:*:*:*)
echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*SV1:*:*:*)
echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
*:UNICOS/mp:*:*)
echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
5000:UNIX_System_V:4.*:*)
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
exit ;;
sparc*:BSD/OS:*:*)
echo sparc-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:BSD/OS:*:*)
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
UNAME_PROCESSOR=`/usr/bin/uname -p`
case ${UNAME_PROCESSOR} in
amd64)
echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
*)
echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
esac
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
*:MINGW64*:*)
echo ${UNAME_MACHINE}-pc-mingw64
exit ;;
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
*:MSYS*:*)
echo ${UNAME_MACHINE}-pc-msys
exit ;;
i*:windows32*:*)
# uname -m includes "-pc" on this system.
echo ${UNAME_MACHINE}-mingw32
exit ;;
i*:PW*:*)
echo ${UNAME_MACHINE}-pc-pw32
exit ;;
*:Interix*:*)
case ${UNAME_MACHINE} in
x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
authenticamd | genuineintel | EM64T)
echo x86_64-unknown-interix${UNAME_RELEASE}
exit ;;
IA64)
echo ia64-unknown-interix${UNAME_RELEASE}
exit ;;
esac ;;
[345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
echo i${UNAME_MACHINE}-pc-mks
exit ;;
8664:Windows_NT:*)
echo x86_64-pc-mks
exit ;;
i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
# How do we know it's Interix rather than the generic POSIX subsystem?
# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
# UNAME_MACHINE based on the output of uname instead of i386?
echo i586-pc-interix
exit ;;
i*:UWIN*:*)
echo ${UNAME_MACHINE}-pc-uwin
exit ;;
amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
echo x86_64-unknown-cygwin
exit ;;
p*:CYGWIN*:*)
echo powerpcle-unknown-cygwin
exit ;;
prep*:SunOS:5.*:*)
echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
exit ;;
*:GNU:*:*)
# the GNU system
echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
exit ;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
exit ;;
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
aarch64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
EV5) UNAME_MACHINE=alphaev5 ;;
EV56) UNAME_MACHINE=alphaev56 ;;
PCA56) UNAME_MACHINE=alphapca56 ;;
PCA57) UNAME_MACHINE=alphapca56 ;;
EV6) UNAME_MACHINE=alphaev6 ;;
EV67) UNAME_MACHINE=alphaev67 ;;
EV68*) UNAME_MACHINE=alphaev68 ;;
esac
objdump --private-headers /bin/sh | grep -q ld.so.1
if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
arc:Linux:*:* | arceb:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
arm*:Linux:*:*)
eval $set_cc_for_build
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_EABI__
then
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
else
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_PCS_VFP
then
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
else
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
fi
fi
exit ;;
avr32*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
cris:Linux:*:*)
echo ${UNAME_MACHINE}-axis-linux-${LIBC}
exit ;;
crisv32:Linux:*:*)
echo ${UNAME_MACHINE}-axis-linux-${LIBC}
exit ;;
frv:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
hexagon:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
i*86:Linux:*:*)
echo ${UNAME_MACHINE}-pc-linux-${LIBC}
exit ;;
ia64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
m32r*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
m68*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
sed 's/^ //' << EOF >$dummy.c
#undef CPU
#undef ${UNAME_MACHINE}
#undef ${UNAME_MACHINE}el
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
CPU=${UNAME_MACHINE}el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
CPU=${UNAME_MACHINE}
#else
CPU=
#endif
#endif
EOF
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
;;
openrisc*:Linux:*:*)
echo or1k-unknown-linux-${LIBC}
exit ;;
or32:Linux:*:* | or1k*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
padre:Linux:*:*)
echo sparc-unknown-linux-${LIBC}
exit ;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
echo hppa64-unknown-linux-${LIBC}
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
*) echo hppa-unknown-linux-${LIBC} ;;
esac
exit ;;
ppc64:Linux:*:*)
echo powerpc64-unknown-linux-${LIBC}
exit ;;
ppc:Linux:*:*)
echo powerpc-unknown-linux-${LIBC}
exit ;;
ppc64le:Linux:*:*)
echo powerpc64le-unknown-linux-${LIBC}
exit ;;
ppcle:Linux:*:*)
echo powerpcle-unknown-linux-${LIBC}
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
exit ;;
sh64*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
sh*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
sparc:Linux:*:* | sparc64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
tile*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
vax:Linux:*:*)
echo ${UNAME_MACHINE}-dec-linux-${LIBC}
exit ;;
x86_64:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
xtensa*:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
# earlier versions are messed up and put the nodename in both
# sysname and nodename.
echo i386-sequent-sysv4
exit ;;
i*86:UNIX_SV:4.2MP:2.*)
# Unixware is an offshoot of SVR4, but it has its own version
# number series starting with 2...
# I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
# Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
# If we were able to find `uname', then EMX Unix compatibility
# is probably installed.
echo ${UNAME_MACHINE}-pc-os2-emx
exit ;;
i*86:XTS-300:*:STOP)
echo ${UNAME_MACHINE}-unknown-stop
exit ;;
i*86:atheos:*:*)
echo ${UNAME_MACHINE}-unknown-atheos
exit ;;
i*86:syllable:*:*)
echo ${UNAME_MACHINE}-pc-syllable
exit ;;
i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
echo i386-unknown-lynxos${UNAME_RELEASE}
exit ;;
i*86:*DOS:*:*)
echo ${UNAME_MACHINE}-pc-msdosdjgpp
exit ;;
i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
else
echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
fi
exit ;;
i*86:*:5:[678]*)
# UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
*Pent*|*Celeron) UNAME_MACHINE=i686 ;;
esac
echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
exit ;;
i*86:*:3.2:*)
if test -f /usr/options/cb.name; then
UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then
UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
&& UNAME_MACHINE=i586
(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
&& UNAME_MACHINE=i686
(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
&& UNAME_MACHINE=i686
echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
else
echo ${UNAME_MACHINE}-pc-sysv32
fi
exit ;;
pc:*:*:*)
# Left here for compatibility:
# uname -m prints for DJGPP always 'pc', but it prints nothing about
# the processor, so we play safe by assuming i586.
# Note: whatever this is, it MUST be the same as what config.sub
# prints for the "djgpp" host, or else GDB configury will decide that
# this is a cross-build.
echo i586-pc-msdosdjgpp
exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
paragon:*:*:*)
echo i860-intel-osf1
exit ;;
i860:*:4.*:*) # i860-SVR4
if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
else # Add other i860-SVR4 vendors below as they are discovered.
echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4
fi
exit ;;
mini*:CTIX:SYS*5:*)
# "miniframe"
echo m68010-convergent-sysv
exit ;;
mc68k:UNIX:SYSTEM5:3.51m)
echo m68k-convergent-sysv
exit ;;
M680?0:D-NIX:5.3:*)
echo m68k-diab-dnix
exit ;;
M68*:*:R3V[5678]*:*)
test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
OS_REL=''
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3${OS_REL}; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4; exit; } ;;
NCR*:*:4.2:* | MPRAS*:*:4.2:*)
OS_REL='.3'
test -r /etc/.relid \
&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
&& { echo i486-ncr-sysv4.3${OS_REL}; exit; }
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; }
/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
echo m68k-unknown-lynxos${UNAME_RELEASE}
exit ;;
mc68030:UNIX_System_V:4.*:*)
echo m68k-atari-sysv4
exit ;;
TSUNAMI:LynxOS:2.*:*)
echo sparc-unknown-lynxos${UNAME_RELEASE}
exit ;;
rs6000:LynxOS:2.*:*)
echo rs6000-unknown-lynxos${UNAME_RELEASE}
exit ;;
PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
echo powerpc-unknown-lynxos${UNAME_RELEASE}
exit ;;
SM[BE]S:UNIX_SV:*:*)
echo mips-dde-sysv${UNAME_RELEASE}
exit ;;
RM*:ReliantUNIX-*:*:*)
echo mips-sni-sysv4
exit ;;
RM*:SINIX-*:*:*)
echo mips-sni-sysv4
exit ;;
*:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then
UNAME_MACHINE=`(uname -p) 2>/dev/null`
echo ${UNAME_MACHINE}-sni-sysv4
else
echo ns32k-sni-sysv
fi
exit ;;
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
# says
echo i586-unisys-sysv4
exit ;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes .
# How about differentiating between stratus architectures? -djm
echo hppa1.1-stratus-sysv4
exit ;;
*:*:*:FTX*)
# From seanf@swdc.stratus.com.
echo i860-stratus-sysv4
exit ;;
i*86:VOS:*:*)
# From Paul.Green@stratus.com.
echo ${UNAME_MACHINE}-stratus-vos
exit ;;
*:VOS:*:*)
# From Paul.Green@stratus.com.
echo hppa1.1-stratus-vos
exit ;;
mc68*:A/UX:*:*)
echo m68k-apple-aux${UNAME_RELEASE}
exit ;;
news*:NEWS-OS:6*:*)
echo mips-sony-newsos6
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
echo mips-nec-sysv${UNAME_RELEASE}
else
echo mips-unknown-sysv${UNAME_RELEASE}
fi
exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
echo powerpc-apple-beos
exit ;;
BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
echo i586-pc-beos
exit ;;
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
echo i586-pc-haiku
exit ;;
x86_64:Haiku:*:*)
echo x86_64-unknown-haiku
exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
SX-5:SUPER-UX:*:*)
echo sx5-nec-superux${UNAME_RELEASE}
exit ;;
SX-6:SUPER-UX:*:*)
echo sx6-nec-superux${UNAME_RELEASE}
exit ;;
SX-7:SUPER-UX:*:*)
echo sx7-nec-superux${UNAME_RELEASE}
exit ;;
SX-8:SUPER-UX:*:*)
echo sx8-nec-superux${UNAME_RELEASE}
exit ;;
SX-8R:SUPER-UX:*:*)
echo sx8r-nec-superux${UNAME_RELEASE}
exit ;;
Power*:Rhapsody:*:*)
echo powerpc-apple-rhapsody${UNAME_RELEASE}
exit ;;
*:Rhapsody:*:*)
echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
exit ;;
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
eval $set_cc_for_build
if test "$UNAME_PROCESSOR" = unknown ; then
UNAME_PROCESSOR=powerpc
fi
if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
case $UNAME_PROCESSOR in
i386) UNAME_PROCESSOR=x86_64 ;;
powerpc) UNAME_PROCESSOR=powerpc64 ;;
esac
fi
fi
elif test "$UNAME_PROCESSOR" = i386 ; then
# Avoid executing cc on OS X 10.9, as it ships with a stub
# that puts up a graphical alert prompting to install
# developer tools. Any system running Mac OS X 10.7 or
# later (Darwin 11 and later) is required to have a 64-bit
# processor. This is not true of the ARM version of Darwin
# that Apple uses in portable devices.
UNAME_PROCESSOR=x86_64
fi
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
exit ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
UNAME_PROCESSOR=`uname -p`
if test "$UNAME_PROCESSOR" = "x86"; then
UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
fi
echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
exit ;;
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
NEO-?:NONSTOP_KERNEL:*:*)
echo neo-tandem-nsk${UNAME_RELEASE}
exit ;;
NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
NSR-?:NONSTOP_KERNEL:*:*)
echo nsr-tandem-nsk${UNAME_RELEASE}
exit ;;
*:NonStop-UX:*:*)
echo mips-compaq-nonstopux
exit ;;
BS2000:POSIX*:*:*)
echo bs2000-siemens-sysv
exit ;;
DS/*:UNIX_System_V:*:*)
echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
exit ;;
*:Plan9:*:*)
# "uname -m" is not consistent, so use $cputype instead. 386
# is converted to i386 for consistency with other x86
# operating systems.
if test "$cputype" = "386"; then
UNAME_MACHINE=i386
else
UNAME_MACHINE="$cputype"
fi
echo ${UNAME_MACHINE}-unknown-plan9
exit ;;
*:TOPS-10:*:*)
echo pdp10-unknown-tops10
exit ;;
*:TENEX:*:*)
echo pdp10-unknown-tenex
exit ;;
KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
echo pdp10-dec-tops20
exit ;;
XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
echo pdp10-xkl-tops20
exit ;;
*:TOPS-20:*:*)
echo pdp10-unknown-tops20
exit ;;
*:ITS:*:*)
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
echo mips-sei-seiux${UNAME_RELEASE}
exit ;;
*:DragonFly:*:*)
echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
exit ;;
*:*VMS:*:*)
UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
V*) echo vax-dec-vms ; exit ;;
esac ;;
*:XENIX:*:SysV)
echo i386-pc-xenix
exit ;;
i*86:skyos:*:*)
echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
exit ;;
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
exit ;;
i*86:AROS:*:*)
echo ${UNAME_MACHINE}-pc-aros
exit ;;
x86_64:VMkernel:*:*)
echo ${UNAME_MACHINE}-unknown-esx
exit ;;
esac
cat >&2 < in order to provide the needed
information to handle your system.
config.guess timestamp = $timestamp
uname -m = `(uname -m) 2>/dev/null || echo unknown`
uname -r = `(uname -r) 2>/dev/null || echo unknown`
uname -s = `(uname -s) 2>/dev/null || echo unknown`
uname -v = `(uname -v) 2>/dev/null || echo unknown`
/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
hostinfo = `(hostinfo) 2>/dev/null`
/bin/universe = `(/bin/universe) 2>/dev/null`
/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
/bin/arch = `(/bin/arch) 2>/dev/null`
/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
UNAME_MACHINE = ${UNAME_MACHINE}
UNAME_RELEASE = ${UNAME_RELEASE}
UNAME_SYSTEM = ${UNAME_SYSTEM}
UNAME_VERSION = ${UNAME_VERSION}
EOF
exit 1
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
memcached-1.4.25/assoc.h 0000664 0001750 0001750 00000000711 12623270407 011717 0000000 0000000 /* associative array */
void assoc_init(const int hashpower_init);
item *assoc_find(const char *key, const size_t nkey, const uint32_t hv);
int assoc_insert(item *item, const uint32_t hv);
void assoc_delete(const char *key, const size_t nkey, const uint32_t hv);
void do_assoc_move_next_bucket(void);
int start_assoc_maintenance_thread(void);
void stop_assoc_maintenance_thread(void);
extern unsigned int hashpower;
extern unsigned int item_lock_hashpower;
memcached-1.4.25/jenkins_hash.c 0000664 0001750 0001750 00000034565 12623270407 013264 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Hash table
*
* The hash function used here is by Bob Jenkins, 1996:
*
* "By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net.
* You may use this code any way you wish, private, educational,
* or commercial. It's free."
*
*/
#include "memcached.h"
#include "jenkins_hash.h"
/*
* Since the hash function does bit manipulation, it needs to know
* whether it's big or little-endian. ENDIAN_LITTLE and ENDIAN_BIG
* are set in the configure script.
*/
#if ENDIAN_BIG == 1
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 1
#else
# if ENDIAN_LITTLE == 1
# define HASH_LITTLE_ENDIAN 1
# define HASH_BIG_ENDIAN 0
# else
# define HASH_LITTLE_ENDIAN 0
# define HASH_BIG_ENDIAN 0
# endif
#endif
#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k))))
/*
-------------------------------------------------------------------------------
mix -- mix 3 32-bit values reversibly.
This is reversible, so any information in (a,b,c) before mix() is
still in (a,b,c) after mix().
If four pairs of (a,b,c) inputs are run through mix(), or through
mix() in reverse, there are at least 32 bits of the output that
are sometimes the same for one pair and different for another pair.
This was tested for:
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
satisfy this are
4 6 8 16 19 4
9 15 3 18 27 15
14 9 3 7 17 3
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
for "differ" defined as + with a one-bit base and a two-bit delta. I
used http://burtleburtle.net/bob/hash/avalanche.html to choose
the operations, constants, and arrangements of the variables.
This does not achieve avalanche. There are input bits of (a,b,c)
that fail to affect some output bits of (a,b,c), especially of a. The
most thoroughly mixed value is c, but it doesn't really even achieve
avalanche in c.
This allows some parallelism. Read-after-writes are good at doubling
the number of bits affected, so the goal of mixing pulls in the opposite
direction as the goal of parallelism. I did what I could. Rotates
seem to cost as much as shifts on every machine I could lay my hands
on, and rotates are much kinder to the top and bottom bits, so I used
rotates.
-------------------------------------------------------------------------------
*/
#define mix(a,b,c) \
{ \
a -= c; a ^= rot(c, 4); c += b; \
b -= a; b ^= rot(a, 6); a += c; \
c -= b; c ^= rot(b, 8); b += a; \
a -= c; a ^= rot(c,16); c += b; \
b -= a; b ^= rot(a,19); a += c; \
c -= b; c ^= rot(b, 4); b += a; \
}
/*
-------------------------------------------------------------------------------
final -- final mixing of 3 32-bit values (a,b,c) into c
Pairs of (a,b,c) values differing in only a few bits will usually
produce values of c that look totally different. This was tested for
* pairs that differed by one bit, by two bits, in any combination
of top bits of (a,b,c), or in any combination of bottom bits of
(a,b,c).
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
is commonly produced by subtraction) look like a single 1-bit
difference.
* the base values were pseudorandom, all zero but one bit set, or
all zero plus a counter that starts at zero.
These constants passed:
14 11 25 16 4 14 24
12 14 25 16 4 14 24
and these came close:
4 8 15 26 3 22 24
10 8 15 26 3 22 24
11 8 15 26 3 22 24
-------------------------------------------------------------------------------
*/
#define final(a,b,c) \
{ \
c ^= b; c -= rot(b,14); \
a ^= c; a -= rot(c,11); \
b ^= a; b -= rot(a,25); \
c ^= b; c -= rot(b,16); \
a ^= c; a -= rot(c,4); \
b ^= a; b -= rot(a,14); \
c ^= b; c -= rot(b,24); \
}
#if HASH_LITTLE_ENDIAN == 1
uint32_t jenkins_hash(
const void *key, /* the key to hash */
size_t length) /* length of the key */
{
uint32_t a,b,c; /* internal state */
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + 0;
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = key; /* read 32-bit chunks */
#ifdef VALGRIND
const uint8_t *k8;
#endif /* ifdef VALGRIND */
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
length -= 12;
k += 3;
}
/*----------------------------- handle the last (probably partial) block */
/*
* "k[2]&0xffffff" actually reads beyond the end of the string, but
* then masks off the part it's not allowed to read. Because the
* string is aligned, the masked-off tail is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
#ifndef VALGRIND
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
case 5 : b+=k[1]&0xff; a+=k[0]; break;
case 4 : a+=k[0]; break;
case 3 : a+=k[0]&0xffffff; break;
case 2 : a+=k[0]&0xffff; break;
case 1 : a+=k[0]&0xff; break;
case 0 : return c; /* zero length strings require no mixing */
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
case 9 : c+=k8[8]; /* fall through */
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
case 5 : b+=k8[4]; /* fall through */
case 4 : a+=k[0]; break;
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
case 1 : a+=k8[0]; break;
case 0 : return c; /* zero length strings require no mixing */
}
#endif /* !valgrind */
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
const uint16_t *k = key; /* read 16-bit chunks */
const uint8_t *k8;
/*--------------- all but last block: aligned reads and different mixing */
while (length > 12)
{
a += k[0] + (((uint32_t)k[1])<<16);
b += k[2] + (((uint32_t)k[3])<<16);
c += k[4] + (((uint32_t)k[5])<<16);
mix(a,b,c);
length -= 12;
k += 6;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
switch(length)
{
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 11: c+=((uint32_t)k8[10])<<16; /* @fallthrough */
case 10: c+=k[4]; /* @fallthrough@ */
b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 9 : c+=k8[8]; /* @fallthrough */
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 7 : b+=((uint32_t)k8[6])<<16; /* @fallthrough */
case 6 : b+=k[2];
a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 5 : b+=k8[4]; /* @fallthrough */
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
break;
case 3 : a+=((uint32_t)k8[2])<<16; /* @fallthrough */
case 2 : a+=k[0];
break;
case 1 : a+=k8[0];
break;
case 0 : return c; /* zero length strings require no mixing */
}
} else { /* need to read the key one byte at a time */
const uint8_t *k = key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
a += ((uint32_t)k[1])<<8;
a += ((uint32_t)k[2])<<16;
a += ((uint32_t)k[3])<<24;
b += k[4];
b += ((uint32_t)k[5])<<8;
b += ((uint32_t)k[6])<<16;
b += ((uint32_t)k[7])<<24;
c += k[8];
c += ((uint32_t)k[9])<<8;
c += ((uint32_t)k[10])<<16;
c += ((uint32_t)k[11])<<24;
mix(a,b,c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=((uint32_t)k[11])<<24;
case 11: c+=((uint32_t)k[10])<<16;
case 10: c+=((uint32_t)k[9])<<8;
case 9 : c+=k[8];
case 8 : b+=((uint32_t)k[7])<<24;
case 7 : b+=((uint32_t)k[6])<<16;
case 6 : b+=((uint32_t)k[5])<<8;
case 5 : b+=k[4];
case 4 : a+=((uint32_t)k[3])<<24;
case 3 : a+=((uint32_t)k[2])<<16;
case 2 : a+=((uint32_t)k[1])<<8;
case 1 : a+=k[0];
break;
case 0 : return c; /* zero length strings require no mixing */
}
}
final(a,b,c);
return c; /* zero length strings require no mixing */
}
#elif HASH_BIG_ENDIAN == 1
/*
* hashbig():
* This is the same as hashword() on big-endian machines. It is different
* from hashlittle() on all machines. hashbig() takes advantage of
* big-endian byte ordering.
*/
uint32_t jenkins_hash( const void *key, size_t length)
{
uint32_t a,b,c;
union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + 0;
u.ptr = key;
if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
const uint32_t *k = key; /* read 32-bit chunks */
#ifdef VALGRIND
const uint8_t *k8;
#endif /* ifdef VALGRIND */
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
while (length > 12)
{
a += k[0];
b += k[1];
c += k[2];
mix(a,b,c);
length -= 12;
k += 3;
}
/*----------------------------- handle the last (probably partial) block */
/*
* "k[2]<<8" actually reads beyond the end of the string, but
* then shifts out the part it's not allowed to read. Because the
* string is aligned, the illegal read is in the same word as the
* rest of the string. Every machine with memory protection I've seen
* does it on word boundaries, so is OK with this. But VALGRIND will
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
#ifndef VALGRIND
switch(length)
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
case 4 : a+=k[0]; break;
case 3 : a+=k[0]&0xffffff00; break;
case 2 : a+=k[0]&0xffff0000; break;
case 1 : a+=k[0]&0xff000000; break;
case 0 : return c; /* zero length strings require no mixing */
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
switch(length) /* all the case statements fall through */
{
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
case 11: c+=((uint32_t)k8[10])<<8; /* fall through */
case 10: c+=((uint32_t)k8[9])<<16; /* fall through */
case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */
case 8 : b+=k[1]; a+=k[0]; break;
case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */
case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */
case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */
case 4 : a+=k[0]; break;
case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */
case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */
case 1 : a+=((uint32_t)k8[0])<<24; break;
case 0 : return c;
}
#endif /* !VALGRIND */
} else { /* need to read the key one byte at a time */
const uint8_t *k = key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
while (length > 12)
{
a += ((uint32_t)k[0])<<24;
a += ((uint32_t)k[1])<<16;
a += ((uint32_t)k[2])<<8;
a += ((uint32_t)k[3]);
b += ((uint32_t)k[4])<<24;
b += ((uint32_t)k[5])<<16;
b += ((uint32_t)k[6])<<8;
b += ((uint32_t)k[7]);
c += ((uint32_t)k[8])<<24;
c += ((uint32_t)k[9])<<16;
c += ((uint32_t)k[10])<<8;
c += ((uint32_t)k[11]);
mix(a,b,c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
switch(length) /* all the case statements fall through */
{
case 12: c+=k[11];
case 11: c+=((uint32_t)k[10])<<8;
case 10: c+=((uint32_t)k[9])<<16;
case 9 : c+=((uint32_t)k[8])<<24;
case 8 : b+=k[7];
case 7 : b+=((uint32_t)k[6])<<8;
case 6 : b+=((uint32_t)k[5])<<16;
case 5 : b+=((uint32_t)k[4])<<24;
case 4 : a+=k[3];
case 3 : a+=((uint32_t)k[2])<<8;
case 2 : a+=((uint32_t)k[1])<<16;
case 1 : a+=((uint32_t)k[0])<<24;
break;
case 0 : return c;
}
}
final(a,b,c);
return c;
}
#else /* HASH_XXX_ENDIAN == 1 */
#error Must define HASH_BIG_ENDIAN or HASH_LITTLE_ENDIAN
#endif /* HASH_XXX_ENDIAN == 1 */
memcached-1.4.25/slabs.c 0000664 0001750 0001750 00000100135 12623274034 011707 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Slabs memory allocation, based on powers-of-N. Slabs are up to 1MB in size
* and are divided into chunks. The chunk sizes start off at the size of the
* "item" structure plus space for a small key and value. They increase by
* a multiplier factor from there, up to half the maximum slab size. The last
* slab size is always 1MB, since that's the maximum item size allowed by the
* memcached protocol.
*/
#include "memcached.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define DEBUG_SLAB_MOVER
/* powers-of-N allocation structures */
typedef struct {
unsigned int size; /* sizes of items */
unsigned int perslab; /* how many items per slab */
void *slots; /* list of item ptrs */
unsigned int sl_curr; /* total free items in list */
unsigned int slabs; /* how many slabs were allocated for this class */
void **slab_list; /* array of slab pointers */
unsigned int list_size; /* size of prev array */
size_t requested; /* The number of requested bytes */
} slabclass_t;
static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];
static size_t mem_limit = 0;
static size_t mem_malloced = 0;
/* If the memory limit has been hit once. Used as a hint to decide when to
* early-wake the LRU maintenance thread */
static bool mem_limit_reached = false;
static int power_largest;
static void *mem_base = NULL;
static void *mem_current = NULL;
static size_t mem_avail = 0;
/**
* Access to the slab allocator is protected by this lock
*/
static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t slabs_rebalance_lock = PTHREAD_MUTEX_INITIALIZER;
/*
* Forward Declarations
*/
static int do_slabs_newslab(const unsigned int id);
static void *memory_allocate(size_t size);
static void do_slabs_free(void *ptr, const size_t size, unsigned int id);
/* Preallocate as many slab pages as possible (called from slabs_init)
on start-up, so users don't get confused out-of-memory errors when
they do have free (in-slab) space, but no space to make new slabs.
if maxslabs is 18 (POWER_LARGEST - POWER_SMALLEST + 1), then all
slab types can be made. if max memory is less than 18 MB, only the
smaller ones will be made. */
static void slabs_preallocate (const unsigned int maxslabs);
/*
* Figures out which slab class (chunk size) is required to store an item of
* a given size.
*
* Given object size, return id to use when allocating/freeing memory for object
* 0 means error: can't store such a large object
*/
unsigned int slabs_clsid(const size_t size) {
int res = POWER_SMALLEST;
if (size == 0)
return 0;
while (size > slabclass[res].size)
if (res++ == power_largest) /* won't fit in the biggest slab */
return 0;
return res;
}
/**
* Determines the chunk sizes and initializes the slab class descriptors
* accordingly.
*/
void slabs_init(const size_t limit, const double factor, const bool prealloc) {
int i = POWER_SMALLEST - 1;
unsigned int size = sizeof(item) + settings.chunk_size;
mem_limit = limit;
if (prealloc) {
/* Allocate everything in a big chunk with malloc */
mem_base = malloc(mem_limit);
if (mem_base != NULL) {
mem_current = mem_base;
mem_avail = mem_limit;
} else {
fprintf(stderr, "Warning: Failed to allocate requested memory in"
" one large chunk.\nWill allocate in smaller chunks\n");
}
}
memset(slabclass, 0, sizeof(slabclass));
while (++i < MAX_NUMBER_OF_SLAB_CLASSES-1 && size <= settings.item_size_max / factor) {
/* Make sure items are always n-byte aligned */
if (size % CHUNK_ALIGN_BYTES)
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
slabclass[i].size = size;
slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
size *= factor;
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
}
}
power_largest = i;
slabclass[power_largest].size = settings.item_size_max;
slabclass[power_largest].perslab = 1;
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
}
/* for the test suite: faking of how much we've already malloc'd */
{
char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
if (t_initial_malloc) {
mem_malloced = (size_t)atol(t_initial_malloc);
}
}
if (prealloc) {
slabs_preallocate(power_largest);
}
}
static void slabs_preallocate (const unsigned int maxslabs) {
int i;
unsigned int prealloc = 0;
/* pre-allocate a 1MB slab in every size class so people don't get
confused by non-intuitive "SERVER_ERROR out of memory"
messages. this is the most common question on the mailing
list. if you really don't want this, you can rebuild without
these three lines. */
for (i = POWER_SMALLEST; i < MAX_NUMBER_OF_SLAB_CLASSES; i++) {
if (++prealloc > maxslabs)
return;
if (do_slabs_newslab(i) == 0) {
fprintf(stderr, "Error while preallocating slab memory!\n"
"If using -L or other prealloc options, max memory must be "
"at least %d megabytes.\n", power_largest);
exit(1);
}
}
}
static int grow_slab_list (const unsigned int id) {
slabclass_t *p = &slabclass[id];
if (p->slabs == p->list_size) {
size_t new_size = (p->list_size != 0) ? p->list_size * 2 : 16;
void *new_list = realloc(p->slab_list, new_size * sizeof(void *));
if (new_list == 0) return 0;
p->list_size = new_size;
p->slab_list = new_list;
}
return 1;
}
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {
slabclass_t *p = &slabclass[id];
int x;
for (x = 0; x < p->perslab; x++) {
do_slabs_free(ptr, 0, id);
ptr += p->size;
}
}
/* Fast FIFO queue */
static void *get_page_from_global_pool(void) {
slabclass_t *p = &slabclass[SLAB_GLOBAL_PAGE_POOL];
if (p->slabs < 1) {
return NULL;
}
char *ret = p->slab_list[p->slabs - 1];
p->slabs--;
return ret;
}
static int do_slabs_newslab(const unsigned int id) {
slabclass_t *p = &slabclass[id];
slabclass_t *g = &slabclass[SLAB_GLOBAL_PAGE_POOL];
int len = settings.slab_reassign ? settings.item_size_max
: p->size * p->perslab;
char *ptr;
if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0
&& g->slabs == 0)) {
mem_limit_reached = true;
MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
return 0;
}
if ((grow_slab_list(id) == 0) ||
(((ptr = get_page_from_global_pool()) == NULL) &&
((ptr = memory_allocate((size_t)len)) == 0))) {
MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
return 0;
}
memset(ptr, 0, (size_t)len);
split_slab_page_into_freelist(ptr, id);
p->slab_list[p->slabs++] = ptr;
MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
return 1;
}
/*@null@*/
static void *do_slabs_alloc(const size_t size, unsigned int id, unsigned int *total_chunks,
unsigned int flags) {
slabclass_t *p;
void *ret = NULL;
item *it = NULL;
if (id < POWER_SMALLEST || id > power_largest) {
MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);
return NULL;
}
p = &slabclass[id];
assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0);
if (total_chunks != NULL) {
*total_chunks = p->slabs * p->perslab;
}
/* fail unless we have space at the end of a recently allocated page,
we have something on our freelist, or we could allocate a new page */
if (p->sl_curr == 0 && flags != SLABS_ALLOC_NO_NEWPAGE) {
do_slabs_newslab(id);
}
if (p->sl_curr != 0) {
/* return off our freelist */
it = (item *)p->slots;
p->slots = it->next;
if (it->next) it->next->prev = 0;
/* Kill flag and initialize refcount here for lock safety in slab
* mover's freeness detection. */
it->it_flags &= ~ITEM_SLABBED;
it->refcount = 1;
p->sl_curr--;
ret = (void *)it;
} else {
ret = NULL;
}
if (ret) {
p->requested += size;
MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);
} else {
MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);
}
return ret;
}
static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {
slabclass_t *p;
item *it;
assert(id >= POWER_SMALLEST && id <= power_largest);
if (id < POWER_SMALLEST || id > power_largest)
return;
MEMCACHED_SLABS_FREE(size, id, ptr);
p = &slabclass[id];
it = (item *)ptr;
it->it_flags = ITEM_SLABBED;
it->slabs_clsid = 0;
it->prev = 0;
it->next = p->slots;
if (it->next) it->next->prev = it;
p->slots = it;
p->sl_curr++;
p->requested -= size;
return;
}
static int nz_strcmp(int nzlength, const char *nz, const char *z) {
int zlength=strlen(z);
return (zlength == nzlength) && (strncmp(nz, z, zlength) == 0) ? 0 : -1;
}
bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c) {
bool ret = true;
if (add_stats != NULL) {
if (!stat_type) {
/* prepare general statistics for the engine */
STATS_LOCK();
APPEND_STAT("bytes", "%llu", (unsigned long long)stats.curr_bytes);
APPEND_STAT("curr_items", "%u", stats.curr_items);
APPEND_STAT("total_items", "%u", stats.total_items);
STATS_UNLOCK();
if (settings.slab_automove > 0) {
pthread_mutex_lock(&slabs_lock);
APPEND_STAT("slab_global_page_pool", "%u", slabclass[SLAB_GLOBAL_PAGE_POOL].slabs);
pthread_mutex_unlock(&slabs_lock);
}
item_stats_totals(add_stats, c);
} else if (nz_strcmp(nkey, stat_type, "items") == 0) {
item_stats(add_stats, c);
} else if (nz_strcmp(nkey, stat_type, "slabs") == 0) {
slabs_stats(add_stats, c);
} else if (nz_strcmp(nkey, stat_type, "sizes") == 0) {
item_stats_sizes(add_stats, c);
} else {
ret = false;
}
} else {
ret = false;
}
return ret;
}
/*@null@*/
static void do_slabs_stats(ADD_STAT add_stats, void *c) {
int i, total;
/* Get the per-thread stats which contain some interesting aggregates */
struct thread_stats thread_stats;
threadlocal_stats_aggregate(&thread_stats);
total = 0;
for(i = POWER_SMALLEST; i <= power_largest; i++) {
slabclass_t *p = &slabclass[i];
if (p->slabs != 0) {
uint32_t perslab, slabs;
slabs = p->slabs;
perslab = p->perslab;
char key_str[STAT_KEY_LEN];
char val_str[STAT_VAL_LEN];
int klen = 0, vlen = 0;
APPEND_NUM_STAT(i, "chunk_size", "%u", p->size);
APPEND_NUM_STAT(i, "chunks_per_page", "%u", perslab);
APPEND_NUM_STAT(i, "total_pages", "%u", slabs);
APPEND_NUM_STAT(i, "total_chunks", "%u", slabs * perslab);
APPEND_NUM_STAT(i, "used_chunks", "%u",
slabs*perslab - p->sl_curr);
APPEND_NUM_STAT(i, "free_chunks", "%u", p->sl_curr);
/* Stat is dead, but displaying zero instead of removing it. */
APPEND_NUM_STAT(i, "free_chunks_end", "%u", 0);
APPEND_NUM_STAT(i, "mem_requested", "%llu",
(unsigned long long)p->requested);
APPEND_NUM_STAT(i, "get_hits", "%llu",
(unsigned long long)thread_stats.slab_stats[i].get_hits);
APPEND_NUM_STAT(i, "cmd_set", "%llu",
(unsigned long long)thread_stats.slab_stats[i].set_cmds);
APPEND_NUM_STAT(i, "delete_hits", "%llu",
(unsigned long long)thread_stats.slab_stats[i].delete_hits);
APPEND_NUM_STAT(i, "incr_hits", "%llu",
(unsigned long long)thread_stats.slab_stats[i].incr_hits);
APPEND_NUM_STAT(i, "decr_hits", "%llu",
(unsigned long long)thread_stats.slab_stats[i].decr_hits);
APPEND_NUM_STAT(i, "cas_hits", "%llu",
(unsigned long long)thread_stats.slab_stats[i].cas_hits);
APPEND_NUM_STAT(i, "cas_badval", "%llu",
(unsigned long long)thread_stats.slab_stats[i].cas_badval);
APPEND_NUM_STAT(i, "touch_hits", "%llu",
(unsigned long long)thread_stats.slab_stats[i].touch_hits);
total++;
}
}
/* add overall slab stats and append terminator */
APPEND_STAT("active_slabs", "%d", total);
APPEND_STAT("total_malloced", "%llu", (unsigned long long)mem_malloced);
add_stats(NULL, 0, NULL, 0, c);
}
static void *memory_allocate(size_t size) {
void *ret;
if (mem_base == NULL) {
/* We are not using a preallocated large memory chunk */
ret = malloc(size);
} else {
ret = mem_current;
if (size > mem_avail) {
return NULL;
}
/* mem_current pointer _must_ be aligned!!! */
if (size % CHUNK_ALIGN_BYTES) {
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
}
mem_current = ((char*)mem_current) + size;
if (size < mem_avail) {
mem_avail -= size;
} else {
mem_avail = 0;
}
}
mem_malloced += size;
return ret;
}
void *slabs_alloc(size_t size, unsigned int id, unsigned int *total_chunks,
unsigned int flags) {
void *ret;
pthread_mutex_lock(&slabs_lock);
ret = do_slabs_alloc(size, id, total_chunks, flags);
pthread_mutex_unlock(&slabs_lock);
return ret;
}
void slabs_free(void *ptr, size_t size, unsigned int id) {
pthread_mutex_lock(&slabs_lock);
do_slabs_free(ptr, size, id);
pthread_mutex_unlock(&slabs_lock);
}
void slabs_stats(ADD_STAT add_stats, void *c) {
pthread_mutex_lock(&slabs_lock);
do_slabs_stats(add_stats, c);
pthread_mutex_unlock(&slabs_lock);
}
void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal)
{
pthread_mutex_lock(&slabs_lock);
slabclass_t *p;
if (id < POWER_SMALLEST || id > power_largest) {
fprintf(stderr, "Internal error! Invalid slab class\n");
abort();
}
p = &slabclass[id];
p->requested = p->requested - old + ntotal;
pthread_mutex_unlock(&slabs_lock);
}
unsigned int slabs_available_chunks(const unsigned int id, bool *mem_flag,
unsigned int *total_chunks, unsigned int *chunks_perslab) {
unsigned int ret;
slabclass_t *p;
pthread_mutex_lock(&slabs_lock);
p = &slabclass[id];
ret = p->sl_curr;
if (mem_flag != NULL)
*mem_flag = mem_limit_reached;
if (total_chunks != NULL)
*total_chunks = p->slabs * p->perslab;
if (chunks_perslab != NULL)
*chunks_perslab = p->perslab;
pthread_mutex_unlock(&slabs_lock);
return ret;
}
static pthread_cond_t slab_rebalance_cond = PTHREAD_COND_INITIALIZER;
static volatile int do_run_slab_thread = 1;
static volatile int do_run_slab_rebalance_thread = 1;
#define DEFAULT_SLAB_BULK_CHECK 1
int slab_bulk_check = DEFAULT_SLAB_BULK_CHECK;
static int slab_rebalance_start(void) {
slabclass_t *s_cls;
int no_go = 0;
pthread_mutex_lock(&slabs_lock);
if (slab_rebal.s_clsid < POWER_SMALLEST ||
slab_rebal.s_clsid > power_largest ||
slab_rebal.d_clsid < SLAB_GLOBAL_PAGE_POOL ||
slab_rebal.d_clsid > power_largest ||
slab_rebal.s_clsid == slab_rebal.d_clsid)
no_go = -2;
s_cls = &slabclass[slab_rebal.s_clsid];
if (!grow_slab_list(slab_rebal.d_clsid)) {
no_go = -1;
}
if (s_cls->slabs < 2)
no_go = -3;
if (no_go != 0) {
pthread_mutex_unlock(&slabs_lock);
return no_go; /* Should use a wrapper function... */
}
/* Always kill the first available slab page as it is most likely to
* contain the oldest items
*/
slab_rebal.slab_start = s_cls->slab_list[0];
slab_rebal.slab_end = (char *)slab_rebal.slab_start +
(s_cls->size * s_cls->perslab);
slab_rebal.slab_pos = slab_rebal.slab_start;
slab_rebal.done = 0;
/* Also tells do_item_get to search for items in this slab */
slab_rebalance_signal = 2;
if (settings.verbose > 1) {
fprintf(stderr, "Started a slab rebalance\n");
}
pthread_mutex_unlock(&slabs_lock);
STATS_LOCK();
stats.slab_reassign_running = true;
STATS_UNLOCK();
return 0;
}
/* CALLED WITH slabs_lock HELD */
static void *slab_rebalance_alloc(const size_t size, unsigned int id) {
slabclass_t *s_cls;
s_cls = &slabclass[slab_rebal.s_clsid];
int x;
item *new_it = NULL;
for (x = 0; x < s_cls->perslab; x++) {
new_it = do_slabs_alloc(size, id, NULL, SLABS_ALLOC_NO_NEWPAGE);
/* check that memory isn't within the range to clear */
if (new_it == NULL) {
break;
}
if ((void *)new_it >= slab_rebal.slab_start
&& (void *)new_it < slab_rebal.slab_end) {
/* Pulled something we intend to free. Mark it as freed since
* we've already done the work of unlinking it from the freelist.
*/
s_cls->requested -= size;
new_it->refcount = 0;
new_it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
new_it = NULL;
slab_rebal.inline_reclaim++;
} else {
break;
}
}
return new_it;
}
enum move_status {
MOVE_PASS=0, MOVE_FROM_SLAB, MOVE_FROM_LRU, MOVE_BUSY, MOVE_LOCKED
};
/* refcount == 0 is safe since nobody can incr while item_lock is held.
* refcount != 0 is impossible since flags/etc can be modified in other
* threads. instead, note we found a busy one and bail. logic in do_item_get
* will prevent busy items from continuing to be busy
* NOTE: This is checking it_flags outside of an item lock. I believe this
* works since it_flags is 8 bits, and we're only ever comparing a single bit
* regardless. ITEM_SLABBED bit will always be correct since we're holding the
* lock which modifies that bit. ITEM_LINKED won't exist if we're between an
* item having ITEM_SLABBED removed, and the key hasn't been added to the item
* yet. The memory barrier from the slabs lock should order the key write and the
* flags to the item?
* If ITEM_LINKED did exist and was just removed, but we still see it, that's
* still safe since it will have a valid key, which we then lock, and then
* recheck everything.
* This may not be safe on all platforms; If not, slabs_alloc() will need to
* seed the item key while holding slabs_lock.
*/
static int slab_rebalance_move(void) {
slabclass_t *s_cls;
int x;
int was_busy = 0;
int refcount = 0;
uint32_t hv;
void *hold_lock;
enum move_status status = MOVE_PASS;
pthread_mutex_lock(&slabs_lock);
s_cls = &slabclass[slab_rebal.s_clsid];
for (x = 0; x < slab_bulk_check; x++) {
hv = 0;
hold_lock = NULL;
item *it = slab_rebal.slab_pos;
status = MOVE_PASS;
/* ITEM_FETCHED when ITEM_SLABBED is overloaded to mean we've cleared
* the chunk for move. Only these two flags should exist.
*/
if (it->it_flags != (ITEM_SLABBED|ITEM_FETCHED)) {
/* ITEM_SLABBED can only be added/removed under the slabs_lock */
if (it->it_flags & ITEM_SLABBED) {
/* remove from slab freelist */
if (s_cls->slots == it) {
s_cls->slots = it->next;
}
if (it->next) it->next->prev = it->prev;
if (it->prev) it->prev->next = it->next;
s_cls->sl_curr--;
status = MOVE_FROM_SLAB;
} else if ((it->it_flags & ITEM_LINKED) != 0) {
/* If it doesn't have ITEM_SLABBED, the item could be in any
* state on its way to being freed or written to. If no
* ITEM_SLABBED, but it's had ITEM_LINKED, it must be active
* and have the key written to it already.
*/
hv = hash(ITEM_key(it), it->nkey);
if ((hold_lock = item_trylock(hv)) == NULL) {
status = MOVE_LOCKED;
} else {
refcount = refcount_incr(&it->refcount);
if (refcount == 2) { /* item is linked but not busy */
/* Double check ITEM_LINKED flag here, since we're
* past a memory barrier from the mutex. */
if ((it->it_flags & ITEM_LINKED) != 0) {
status = MOVE_FROM_LRU;
} else {
/* refcount == 1 + !ITEM_LINKED means the item is being
* uploaded to, or was just unlinked but hasn't been freed
* yet. Let it bleed off on its own and try again later */
status = MOVE_BUSY;
}
} else {
if (settings.verbose > 2) {
fprintf(stderr, "Slab reassign hit a busy item: refcount: %d (%d -> %d)\n",
it->refcount, slab_rebal.s_clsid, slab_rebal.d_clsid);
}
status = MOVE_BUSY;
}
/* Item lock must be held while modifying refcount */
if (status == MOVE_BUSY) {
refcount_decr(&it->refcount);
item_trylock_unlock(hold_lock);
}
}
} else {
/* See above comment. No ITEM_SLABBED or ITEM_LINKED. Mark
* busy and wait for item to complete its upload. */
status = MOVE_BUSY;
}
}
int save_item = 0;
item *new_it = NULL;
size_t ntotal = 0;
switch (status) {
case MOVE_FROM_LRU:
/* Lock order is LRU locks -> slabs_lock. unlink uses LRU lock.
* We only need to hold the slabs_lock while initially looking
* at an item, and at this point we have an exclusive refcount
* (2) + the item is locked. Drop slabs lock, drop item to
* refcount 1 (just our own, then fall through and wipe it
*/
/* Check if expired or flushed */
ntotal = ITEM_ntotal(it);
/* REQUIRES slabs_lock: CHECK FOR cls->sl_curr > 0 */
if ((it->exptime != 0 && it->exptime < current_time)
|| item_is_flushed(it)) {
/* TODO: maybe we only want to save if item is in HOT or
* WARM LRU?
*/
save_item = 0;
} else if ((new_it = slab_rebalance_alloc(ntotal, slab_rebal.s_clsid)) == NULL) {
save_item = 0;
slab_rebal.evictions_nomem++;
} else {
save_item = 1;
}
pthread_mutex_unlock(&slabs_lock);
if (save_item) {
/* if free memory, memcpy. clear prev/next/h_bucket */
memcpy(new_it, it, ntotal);
new_it->prev = 0;
new_it->next = 0;
new_it->h_next = 0;
/* These are definitely required. else fails assert */
new_it->it_flags &= ~ITEM_LINKED;
new_it->refcount = 0;
do_item_replace(it, new_it, hv);
slab_rebal.rescues++;
} else {
do_item_unlink(it, hv);
}
item_trylock_unlock(hold_lock);
pthread_mutex_lock(&slabs_lock);
/* Always remove the ntotal, as we added it in during
* do_slabs_alloc() when copying the item.
*/
s_cls->requested -= ntotal;
case MOVE_FROM_SLAB:
it->refcount = 0;
it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
#ifdef DEBUG_SLAB_MOVER
memcpy(ITEM_key(it), "deadbeef", 8);
#endif
break;
case MOVE_BUSY:
case MOVE_LOCKED:
slab_rebal.busy_items++;
was_busy++;
break;
case MOVE_PASS:
break;
}
slab_rebal.slab_pos = (char *)slab_rebal.slab_pos + s_cls->size;
if (slab_rebal.slab_pos >= slab_rebal.slab_end)
break;
}
if (slab_rebal.slab_pos >= slab_rebal.slab_end) {
/* Some items were busy, start again from the top */
if (slab_rebal.busy_items) {
slab_rebal.slab_pos = slab_rebal.slab_start;
STATS_LOCK();
stats.slab_reassign_busy_items += slab_rebal.busy_items;
STATS_UNLOCK();
slab_rebal.busy_items = 0;
} else {
slab_rebal.done++;
}
}
pthread_mutex_unlock(&slabs_lock);
return was_busy;
}
static void slab_rebalance_finish(void) {
slabclass_t *s_cls;
slabclass_t *d_cls;
int x;
uint32_t rescues;
uint32_t evictions_nomem;
uint32_t inline_reclaim;
pthread_mutex_lock(&slabs_lock);
s_cls = &slabclass[slab_rebal.s_clsid];
d_cls = &slabclass[slab_rebal.d_clsid];
#ifdef DEBUG_SLAB_MOVER
/* If the algorithm is broken, live items can sneak in. */
slab_rebal.slab_pos = slab_rebal.slab_start;
while (1) {
item *it = slab_rebal.slab_pos;
assert(it->it_flags == (ITEM_SLABBED|ITEM_FETCHED));
assert(memcmp(ITEM_key(it), "deadbeef", 8) == 0);
it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
slab_rebal.slab_pos = (char *)slab_rebal.slab_pos + s_cls->size;
if (slab_rebal.slab_pos >= slab_rebal.slab_end)
break;
}
#endif
/* At this point the stolen slab is completely clear.
* We always kill the "first"/"oldest" slab page in the slab_list, so
* shuffle the page list backwards and decrement.
*/
s_cls->slabs--;
for (x = 0; x < s_cls->slabs; x++) {
s_cls->slab_list[x] = s_cls->slab_list[x+1];
}
d_cls->slab_list[d_cls->slabs++] = slab_rebal.slab_start;
/* Don't need to split the page into chunks if we're just storing it */
if (slab_rebal.d_clsid > SLAB_GLOBAL_PAGE_POOL) {
memset(slab_rebal.slab_start, 0, (size_t)settings.item_size_max);
split_slab_page_into_freelist(slab_rebal.slab_start,
slab_rebal.d_clsid);
}
slab_rebal.done = 0;
slab_rebal.s_clsid = 0;
slab_rebal.d_clsid = 0;
slab_rebal.slab_start = NULL;
slab_rebal.slab_end = NULL;
slab_rebal.slab_pos = NULL;
evictions_nomem = slab_rebal.evictions_nomem;
inline_reclaim = slab_rebal.inline_reclaim;
rescues = slab_rebal.rescues;
slab_rebal.evictions_nomem = 0;
slab_rebal.inline_reclaim = 0;
slab_rebal.rescues = 0;
slab_rebalance_signal = 0;
pthread_mutex_unlock(&slabs_lock);
STATS_LOCK();
stats.slab_reassign_running = false;
stats.slabs_moved++;
stats.slab_reassign_rescues += rescues;
stats.slab_reassign_evictions_nomem += evictions_nomem;
stats.slab_reassign_inline_reclaim += inline_reclaim;
STATS_UNLOCK();
if (settings.verbose > 1) {
fprintf(stderr, "finished a slab move\n");
}
}
/* Slab mover thread.
* Sits waiting for a condition to jump off and shovel some memory about
*/
static void *slab_rebalance_thread(void *arg) {
int was_busy = 0;
/* So we first pass into cond_wait with the mutex held */
mutex_lock(&slabs_rebalance_lock);
while (do_run_slab_rebalance_thread) {
if (slab_rebalance_signal == 1) {
if (slab_rebalance_start() < 0) {
/* Handle errors with more specifity as required. */
slab_rebalance_signal = 0;
}
was_busy = 0;
} else if (slab_rebalance_signal && slab_rebal.slab_start != NULL) {
was_busy = slab_rebalance_move();
}
if (slab_rebal.done) {
slab_rebalance_finish();
} else if (was_busy) {
/* Stuck waiting for some items to unlock, so slow down a bit
* to give them a chance to free up */
usleep(50);
}
if (slab_rebalance_signal == 0) {
/* always hold this lock while we're running */
pthread_cond_wait(&slab_rebalance_cond, &slabs_rebalance_lock);
}
}
return NULL;
}
/* Iterate at most once through the slab classes and pick a "random" source.
* I like this better than calling rand() since rand() is slow enough that we
* can just check all of the classes once instead.
*/
static int slabs_reassign_pick_any(int dst) {
static int cur = POWER_SMALLEST - 1;
int tries = power_largest - POWER_SMALLEST + 1;
for (; tries > 0; tries--) {
cur++;
if (cur > power_largest)
cur = POWER_SMALLEST;
if (cur == dst)
continue;
if (slabclass[cur].slabs > 1) {
return cur;
}
}
return -1;
}
static enum reassign_result_type do_slabs_reassign(int src, int dst) {
if (slab_rebalance_signal != 0)
return REASSIGN_RUNNING;
if (src == dst)
return REASSIGN_SRC_DST_SAME;
/* Special indicator to choose ourselves. */
if (src == -1) {
src = slabs_reassign_pick_any(dst);
/* TODO: If we end up back at -1, return a new error type */
}
if (src < POWER_SMALLEST || src > power_largest ||
dst < SLAB_GLOBAL_PAGE_POOL || dst > power_largest)
return REASSIGN_BADCLASS;
if (slabclass[src].slabs < 2)
return REASSIGN_NOSPARE;
slab_rebal.s_clsid = src;
slab_rebal.d_clsid = dst;
slab_rebalance_signal = 1;
pthread_cond_signal(&slab_rebalance_cond);
return REASSIGN_OK;
}
enum reassign_result_type slabs_reassign(int src, int dst) {
enum reassign_result_type ret;
if (pthread_mutex_trylock(&slabs_rebalance_lock) != 0) {
return REASSIGN_RUNNING;
}
ret = do_slabs_reassign(src, dst);
pthread_mutex_unlock(&slabs_rebalance_lock);
return ret;
}
/* If we hold this lock, rebalancer can't wake up or move */
void slabs_rebalancer_pause(void) {
pthread_mutex_lock(&slabs_rebalance_lock);
}
void slabs_rebalancer_resume(void) {
pthread_mutex_unlock(&slabs_rebalance_lock);
}
static pthread_t rebalance_tid;
int start_slab_maintenance_thread(void) {
int ret;
slab_rebalance_signal = 0;
slab_rebal.slab_start = NULL;
char *env = getenv("MEMCACHED_SLAB_BULK_CHECK");
if (env != NULL) {
slab_bulk_check = atoi(env);
if (slab_bulk_check == 0) {
slab_bulk_check = DEFAULT_SLAB_BULK_CHECK;
}
}
if (pthread_cond_init(&slab_rebalance_cond, NULL) != 0) {
fprintf(stderr, "Can't intiialize rebalance condition\n");
return -1;
}
pthread_mutex_init(&slabs_rebalance_lock, NULL);
if ((ret = pthread_create(&rebalance_tid, NULL,
slab_rebalance_thread, NULL)) != 0) {
fprintf(stderr, "Can't create rebal thread: %s\n", strerror(ret));
return -1;
}
return 0;
}
/* The maintenance thread is on a sleep/loop cycle, so it should join after a
* short wait */
void stop_slab_maintenance_thread(void) {
mutex_lock(&slabs_rebalance_lock);
do_run_slab_thread = 0;
do_run_slab_rebalance_thread = 0;
pthread_cond_signal(&slab_rebalance_cond);
pthread_mutex_unlock(&slabs_rebalance_lock);
/* Wait for the maintenance thread to stop */
pthread_join(rebalance_tid, NULL);
}
memcached-1.4.25/m4/ 0000775 0001750 0001750 00000000000 12623542633 011042 5 0000000 0000000 memcached-1.4.25/m4/c99-backport.m4 0000644 0001750 0001750 00000012104 11446413300 013415 0000000 0000000 # AC_PROG_CC_C99 ([ACTION-IF-AVAILABLE], [ACTION-IF-UNAVAILABLE])
# ----------------------------------------------------------------
# If the C compiler is not in ISO C99 mode by default, try to add an
# option to output variable CC to make it so. This macro tries
# various options that select ISO C99 on some system or another. It
# considers the compiler to be in ISO C99 mode if it handles _Bool,
# // comments, flexible array members, inline, long long int, mixed
# code and declarations, named initialization of structs, restrict,
# va_copy, varargs macros, variable declarations in for loops and
# variable length arrays.
AC_DEFUN([AC_PROG_CC_C99],
[AC_C_STD_TRY([c99],
[[#include
#include
#include
#include
#include
// Check varargs macros. These examples are taken from C99 6.10.3.5.
#define debug(...) fprintf (stderr, __VA_ARGS__)
#define showlist(...) puts (#__VA_ARGS__)
#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
static void
test_varargs_macros (void)
{
int x = 1234;
int y = 5678;
debug ("Flag");
debug ("X = %d\n", x);
showlist (The first, second, and third items.);
report (x>y, "x is %d but y is %d", x, y);
}
// Check long long types.
#define BIG64 18446744073709551615ull
#define BIG32 4294967295ul
#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
#if !BIG_OK
your preprocessor is broken;
#endif
#if BIG_OK
#else
your preprocessor is broken;
#endif
static long long int bignum = -9223372036854775807LL;
static unsigned long long int ubignum = BIG64;
struct incomplete_array
{
int datasize;
double data[];
};
struct named_init {
int number;
const wchar_t *name;
double average;
};
typedef const char *ccp;
static inline int
test_restrict (ccp restrict text)
{
// See if C++-style comments work.
// Iterate through items via the restricted pointer.
// Also check for declarations in for loops.
for (unsigned int i = 0; *(text+i) != '\0'; ++i)
continue;
return 0;
}
// Check varargs and va_copy.
static void
test_varargs (const char *format, ...)
{
va_list args;
va_start (args, format);
va_list args_copy;
va_copy (args_copy, args);
const char *str;
int number;
float fnumber;
while (*format)
{
switch (*format++)
{
case 's': // string
str = va_arg (args_copy, const char *);
break;
case 'd': // int
number = va_arg (args_copy, int);
break;
case 'f': // float
fnumber = va_arg (args_copy, double);
break;
default:
break;
}
}
va_end (args_copy);
va_end (args);
}
]],
[[
// Check bool.
_Bool success = false;
// Check restrict.
if (test_restrict ("String literal") == 0)
success = true;
char *restrict newvar = "Another string";
// Check varargs.
test_varargs ("s, d' f .", "string", 65, 34.234);
test_varargs_macros ();
// Check flexible array members.
struct incomplete_array *ia =
malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
ia->datasize = 10;
for (int i = 0; i < ia->datasize; ++i)
ia->data[i] = i * 1.234;
// Check named initializers.
struct named_init ni = {
.number = 34,
.name = L"Test wide string",
.average = 543.34343,
};
ni.number = 58;
int dynamic_array[ni.number];
dynamic_array[ni.number - 1] = 543;
// work around unused variable warnings
return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
|| dynamic_array[ni.number - 1] != 543);
]],
dnl Try
dnl GCC -std=gnu99 (unused restrictive modes: -std=c99 -std=iso9899:1999)
dnl AIX -qlanglvl=extc99 (unused restrictive mode: -qlanglvl=stdc99)
dnl Intel ICC -c99
dnl IRIX -c99
dnl Solaris (unused because it causes the compiler to assume C99 semantics for
dnl library functions, and this is invalid before Solaris 10: -xc99)
dnl Tru64 -c99
dnl with extended modes being tried first.
[[-std=gnu99 -c99 -qlanglvl=extc99]], [$1], [$2])[]dnl
])# AC_PROG_CC_C99
# AC_C_STD_TRY(STANDARD, TEST-PROLOGUE, TEST-BODY, OPTION-LIST,
# ACTION-IF-AVAILABLE, ACTION-IF-UNAVAILABLE)
# --------------------------------------------------------------
# Check whether the C compiler accepts features of STANDARD (e.g `c89', `c99')
# by trying to compile a program of TEST-PROLOGUE and TEST-BODY. If this fails,
# try again with each compiler option in the space-separated OPTION-LIST; if one
# helps, append it to CC. If eventually successful, run ACTION-IF-AVAILABLE,
# else ACTION-IF-UNAVAILABLE.
AC_DEFUN([AC_C_STD_TRY],
[AC_MSG_CHECKING([for $CC option to accept ISO ]m4_translit($1, [c], [C]))
AC_CACHE_VAL(ac_cv_prog_cc_$1,
[ac_cv_prog_cc_$1=no
ac_save_CC=$CC
AC_LANG_CONFTEST([AC_LANG_PROGRAM([$2], [$3])])
for ac_arg in '' $4
do
CC="$ac_save_CC $ac_arg"
_AC_COMPILE_IFELSE([], [ac_cv_prog_cc_$1=$ac_arg])
test "x$ac_cv_prog_cc_$1" != "xno" && break
done
rm -f conftest.$ac_ext
CC=$ac_save_CC
])# AC_CACHE_VAL
case "x$ac_cv_prog_cc_$1" in
x)
AC_MSG_RESULT([none needed]) ;;
xno)
AC_MSG_RESULT([unsupported]) ;;
*)
CC="$CC $ac_cv_prog_cc_$1"
AC_MSG_RESULT([$ac_cv_prog_cc_$1]) ;;
esac
AS_IF([test "x$ac_cv_prog_cc_$1" != xno], [$5], [$6])
])# AC_C_STD_TRY
memcached-1.4.25/NEWS 0000664 0001750 0001750 00000000026 12623270407 011134 0000000 0000000 http://memcached.org/
memcached-1.4.25/COPYING 0000644 0001750 0001750 00000002737 11246331452 011477 0000000 0000000 Copyright (c) 2003, Danga Interactive, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of the Danga Interactive 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 COPYRIGHT HOLDERS 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 COPYRIGHT
OWNER 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.
memcached-1.4.25/stats.h 0000644 0001750 0001750 00000000526 12606105233 011742 0000000 0000000 /* stats */
void stats_prefix_init(void);
void stats_prefix_clear(void);
void stats_prefix_record_get(const char *key, const size_t nkey, const bool is_hit);
void stats_prefix_record_delete(const char *key, const size_t nkey);
void stats_prefix_record_set(const char *key, const size_t nkey);
/*@null@*/
char *stats_prefix_dump(int *length);
memcached-1.4.25/sasl_defs.h 0000644 0001750 0001750 00000001265 12606105233 012550 0000000 0000000 #ifndef SASL_DEFS_H
#define SASL_DEFS_H 1
// Longest one I could find was ``9798-U-RSA-SHA1-ENC''
#define MAX_SASL_MECH_LEN 32
#if defined(HAVE_SASL_SASL_H) && defined(ENABLE_SASL)
#include
void init_sasl(void);
extern char my_sasl_hostname[1025];
#else /* End of SASL support */
typedef void* sasl_conn_t;
#define init_sasl() {}
#define sasl_dispose(x) {}
#define sasl_server_new(a, b, c, d, e, f, g, h) 1
#define sasl_listmech(a, b, c, d, e, f, g, h) 1
#define sasl_server_start(a, b, c, d, e, f) 1
#define sasl_server_step(a, b, c, d, e) 1
#define sasl_getprop(a, b, c) {}
#define SASL_OK 0
#define SASL_CONTINUE -1
#endif /* sasl compat */
#endif /* SASL_DEFS_H */
memcached-1.4.25/AUTHORS 0000644 0001750 0001750 00000000105 12250557060 011500 0000000 0000000 Anatoly Vorobey
Brad Fitzpatrick
memcached-1.4.25/sasl_defs.c 0000644 0001750 0001750 00000011743 12606105207 012546 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
#include
#include
#include
#include
char my_sasl_hostname[1025];
#ifdef HAVE_SASL_CB_GETCONF
/* The locations we may search for a SASL config file if the user didn't
* specify one in the environment variable SASL_CONF_PATH
*/
const char * const locations[] = {
"/etc/sasl/memcached.conf",
"/etc/sasl2/memcached.conf",
NULL
};
#endif
#ifndef HAVE_SASL_CALLBACK_FT
typedef int (*sasl_callback_ft)(void);
#endif
#ifdef ENABLE_SASL_PWDB
#define MAX_ENTRY_LEN 256
static const char *memcached_sasl_pwdb;
static int sasl_server_userdb_checkpass(sasl_conn_t *conn,
void *context,
const char *user,
const char *pass,
unsigned passlen,
struct propctx *propctx)
{
size_t unmlen = strlen(user);
if ((passlen + unmlen) > (MAX_ENTRY_LEN - 4)) {
fprintf(stderr,
"WARNING: Failed to authenticate <%s> due to too long password (%d)\n",
user, passlen);
return SASL_NOAUTHZ;
}
FILE *pwfile = fopen(memcached_sasl_pwdb, "r");
if (pwfile == NULL) {
if (settings.verbose) {
vperror("WARNING: Failed to open sasl database <%s>",
memcached_sasl_pwdb);
}
return SASL_NOAUTHZ;
}
char buffer[MAX_ENTRY_LEN];
bool ok = false;
while ((fgets(buffer, sizeof(buffer), pwfile)) != NULL) {
if (memcmp(user, buffer, unmlen) == 0 && buffer[unmlen] == ':') {
/* This is the correct user */
++unmlen;
if (memcmp(pass, buffer + unmlen, passlen) == 0 &&
(buffer[unmlen + passlen] == ':' || /* Additional tokens */
buffer[unmlen + passlen] == '\n' || /* end of line */
buffer[unmlen + passlen] == '\r'|| /* dos format? */
buffer[unmlen + passlen] == '\0')) { /* line truncated */
ok = true;
}
break;
}
}
(void)fclose(pwfile);
if (ok) {
return SASL_OK;
}
if (settings.verbose) {
fprintf(stderr, "INFO: User <%s> failed to authenticate\n", user);
}
return SASL_NOAUTHZ;
}
#endif
#ifdef HAVE_SASL_CB_GETCONF
static int sasl_getconf(void *context, const char **path)
{
*path = getenv("SASL_CONF_PATH");
if (*path == NULL) {
for (int i = 0; locations[i] != NULL; ++i) {
if (access(locations[i], F_OK) == 0) {
*path = locations[i];
break;
}
}
}
if (settings.verbose) {
if (*path != NULL) {
fprintf(stderr, "Reading configuration from: <%s>\n", *path);
} else {
fprintf(stderr, "Failed to locate a config path\n");
}
}
return (*path != NULL) ? SASL_OK : SASL_FAIL;
}
#endif
static int sasl_log(void *context, int level, const char *message)
{
bool log = true;
switch (level) {
case SASL_LOG_NONE:
log = false;
break;
case SASL_LOG_PASS:
case SASL_LOG_TRACE:
case SASL_LOG_DEBUG:
case SASL_LOG_NOTE:
if (settings.verbose < 2) {
log = false;
}
break;
case SASL_LOG_WARN:
case SASL_LOG_FAIL:
if (settings.verbose < 1) {
log = false;
}
break;
default:
/* This is an error */
;
}
if (log) {
fprintf(stderr, "SASL (severity %d): %s\n", level, message);
}
return SASL_OK;
}
static sasl_callback_t sasl_callbacks[] = {
#ifdef ENABLE_SASL_PWDB
{ SASL_CB_SERVER_USERDB_CHECKPASS, (sasl_callback_ft)sasl_server_userdb_checkpass, NULL },
#endif
{ SASL_CB_LOG, (sasl_callback_ft)sasl_log, NULL },
#ifdef HAVE_SASL_CB_GETCONF
{ SASL_CB_GETCONF, sasl_getconf, NULL },
#endif
{ SASL_CB_LIST_END, NULL, NULL }
};
void init_sasl(void) {
#ifdef ENABLE_SASL_PWDB
memcached_sasl_pwdb = getenv("MEMCACHED_SASL_PWDB");
if (memcached_sasl_pwdb == NULL) {
if (settings.verbose) {
fprintf(stderr,
"INFO: MEMCACHED_SASL_PWDB not specified. "
"Internal passwd database disabled\n");
}
sasl_callbacks[0].id = SASL_CB_LIST_END;
sasl_callbacks[0].proc = NULL;
}
#endif
memset(my_sasl_hostname, 0, sizeof(my_sasl_hostname));
if (gethostname(my_sasl_hostname, sizeof(my_sasl_hostname)-1) == -1) {
if (settings.verbose) {
fprintf(stderr, "Error discovering hostname for SASL\n");
}
my_sasl_hostname[0] = '\0';
}
if (sasl_server_init(sasl_callbacks, "memcached") != SASL_OK) {
fprintf(stderr, "Error initializing sasl.\n");
exit(EXIT_FAILURE);
} else {
if (settings.verbose) {
fprintf(stderr, "Initialized SASL.\n");
}
}
}
memcached-1.4.25/config.sub 0000755 0001750 0001750 00000105775 12405615464 012443 0000000 0000000 #! /bin/sh
# Configuration validation subroutine script.
# Copyright 1992-2014 Free Software Foundation, Inc.
timestamp='2014-09-11'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see .
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that
# program. This Exception is an additional permission under section 7
# of the GNU General Public License, version 3 ("GPLv3").
# Please send patches with a ChangeLog entry to config-patches@gnu.org.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
# If it is invalid, we print an error message on stderr and exit with code 1.
# Otherwise, we print the canonical config type on stdout and succeed.
# You can get the latest version of this script from:
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
# that are meaningful with *any* GNU software.
# Each package is responsible for reporting which valid configurations
# it does not support. The user should be able to distinguish
# a failure to support a valid configuration from a meaningless
# configuration.
# The goal of this file is to map all the various variations of a given
# machine specification into a single specification in the form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
# or in some cases, the newer four-part form:
# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# It is wrong to echo any other type of specification.
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION] CPU-MFR-OPSYS
$0 [OPTION] ALIAS
Canonicalize a configuration name.
Operation modes:
-h, --help print this help, then exit
-t, --time-stamp print date of last modification, then exit
-v, --version print version number, then exit
Report bugs and patches to ."
version="\
GNU config.sub ($timestamp)
Copyright 1992-2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try \`$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
case $1 in
--time-stamp | --time* | -t )
echo "$timestamp" ; exit ;;
--version | -v )
echo "$version" ; exit ;;
--help | --h* | -h )
echo "$usage"; exit ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
echo "$me: invalid option $1$help"
exit 1 ;;
*local*)
# First pass through any local machine types.
echo $1
exit ;;
* )
break ;;
esac
done
case $# in
0) echo "$me: missing argument$help" >&2
exit 1;;
1) ;;
*) echo "$me: too many arguments$help" >&2
exit 1;;
esac
# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
knetbsd*-gnu* | netbsd*-gnu* | \
kopensolaris*-gnu* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
android-linux)
os=-linux-android
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
then os=`echo $1 | sed 's/.*-/-/'`
else os=; fi
;;
esac
### Let's recognize common machines as not being operating systems so
### that things like config.sub decstation-3100 work. We also
### recognize some manufacturers as not being operating systems, so we
### can provide default operating systems below.
case $os in
-sun*os*)
# Prevent following clause from handling this invalid input.
;;
-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
-bluegene*)
os=-cnk
;;
-sim | -cisco | -oki | -wec | -winbond)
os=
basic_machine=$1
;;
-scout)
;;
-wrs)
os=-vxworks
basic_machine=$1
;;
-chorusos*)
os=-chorusos
basic_machine=$1
;;
-chorusrdb)
os=-chorusrdb
basic_machine=$1
;;
-hiux*)
os=-hiuxwe2
;;
-sco6)
os=-sco5v6
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco5)
os=-sco3.2v5
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco4)
os=-sco3.2v4
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco3.2.[4-9]*)
os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco3.2v[4-9]*)
# Don't forget version if it is 3.2v4 or newer.
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco5v6*)
# Don't forget version if it is 3.2v4 or newer.
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-sco*)
os=-sco3.2v2
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-udk*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-isc)
os=-isc2.2
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-clix*)
basic_machine=clipper-intergraph
;;
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
-lynx*178)
os=-lynxos178
;;
-lynx*5)
os=-lynxos5
;;
-lynx*)
os=-lynxos
;;
-ptx*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
;;
-windowsnt*)
os=`echo $os | sed -e 's/windowsnt/winnt/'`
;;
-psos*)
os=-psos
;;
-mint | -mint[0-9]*)
basic_machine=m68k-atari
os=-mint
;;
esac
# Decode aliases for certain CPU-COMPANY combinations.
case $basic_machine in
# Recognize the basic CPU types without company name.
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
| aarch64 | aarch64_be \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| am33_2.0 \
| arc | arceb \
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
| avr | avr32 \
| be32 | be64 \
| bfin \
| c4x | c8051 | clipper \
| d10v | d30v | dlx | dsp16xx \
| epiphany \
| fido | fr30 | frv \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
| hexagon \
| i370 | i860 | i960 | ia64 \
| ip2k | iq2000 \
| k1om \
| le32 | le64 \
| lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
| mips64octeon | mips64octeonel \
| mips64orion | mips64orionel \
| mips64r5900 | mips64r5900el \
| mips64vr | mips64vrel \
| mips64vr4100 | mips64vr4100el \
| mips64vr4300 | mips64vr4300el \
| mips64vr5000 | mips64vr5000el \
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
| mipsisa32r6 | mipsisa32r6el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
| mipsisa64r6 | mipsisa64r6el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
| mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
| moxie \
| mt \
| msp430 \
| nds32 | nds32le | nds32be \
| nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
| open8 | or1k | or1knd | or32 \
| pdp10 | pdp11 | pj | pjl \
| powerpc | powerpc64 | powerpc64le | powerpcle \
| pyramid \
| riscv32 | riscv64 \
| rl78 | rx \
| score \
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
| spu \
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
| ubicom32 \
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
| we32k \
| x86 | xc16x | xstormy16 | xtensa \
| z8k | z80)
basic_machine=$basic_machine-unknown
;;
c54x)
basic_machine=tic54x-unknown
;;
c55x)
basic_machine=tic55x-unknown
;;
c6x)
basic_machine=tic6x-unknown
;;
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
;;
ms1)
basic_machine=mt-unknown
;;
strongarm | thumb | xscale)
basic_machine=arm-unknown
;;
xgate)
basic_machine=$basic_machine-unknown
os=-none
;;
xscaleeb)
basic_machine=armeb-unknown
;;
xscaleel)
basic_machine=armel-unknown
;;
# We use `pc' rather than `unknown'
# because (1) that's what they normally are, and
# (2) the word "unknown" tends to confuse beginning users.
i*86 | x86_64)
basic_machine=$basic_machine-pc
;;
# Object if more than one company name word.
*-*-*)
echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
exit 1
;;
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
| aarch64-* | aarch64_be-* \
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
| avr-* | avr32-* \
| be32-* | be64-* \
| bfin-* | bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* \
| c8051-* | clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| elxsi-* \
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
| hexagon-* \
| i*86-* | i860-* | i960-* | ia64-* \
| ip2k-* | iq2000-* \
| k1om-* \
| le32-* | le64-* \
| lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
| microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
| mips64octeon-* | mips64octeonel-* \
| mips64orion-* | mips64orionel-* \
| mips64r5900-* | mips64r5900el-* \
| mips64vr-* | mips64vrel-* \
| mips64vr4100-* | mips64vr4100el-* \
| mips64vr4300-* | mips64vr4300el-* \
| mips64vr5000-* | mips64vr5000el-* \
| mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
| mipsisa32r6-* | mipsisa32r6el-* \
| mipsisa64-* | mipsisa64el-* \
| mipsisa64r2-* | mipsisa64r2el-* \
| mipsisa64r6-* | mipsisa64r6el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
| mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
| nds32-* | nds32le-* | nds32be-* \
| nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
| open8-* \
| or1k*-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
| pyramid-* \
| rl78-* | romp-* | rs6000-* | rx-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
| sparclite-* \
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
| tahoe-* \
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
| tile*-* \
| tron-* \
| ubicom32-* \
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
| vax-* \
| we32k-* \
| x86-* | x86_64-* | xc16x-* | xps100-* \
| xstormy16-* | xtensa*-* \
| ymp-* \
| z8k-* | z80-*)
;;
# Recognize the basic CPU types without company name, with glob match.
xtensa*)
basic_machine=$basic_machine-unknown
;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
386bsd)
basic_machine=i386-unknown
os=-bsd
;;
3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
basic_machine=m68000-att
;;
3b*)
basic_machine=we32k-att
;;
a29khif)
basic_machine=a29k-amd
os=-udi
;;
abacus)
basic_machine=abacus-unknown
;;
adobe68k)
basic_machine=m68010-adobe
os=-scout
;;
alliant | fx80)
basic_machine=fx80-alliant
;;
altos | altos3068)
basic_machine=m68k-altos
;;
am29k)
basic_machine=a29k-none
os=-bsd
;;
amd64)
basic_machine=x86_64-pc
;;
amd64-*)
basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
amdahl)
basic_machine=580-amdahl
os=-sysv
;;
amiga | amiga-*)
basic_machine=m68k-unknown
;;
amigaos | amigados)
basic_machine=m68k-unknown
os=-amigaos
;;
amigaunix | amix)
basic_machine=m68k-unknown
os=-sysv4
;;
apollo68)
basic_machine=m68k-apollo
os=-sysv
;;
apollo68bsd)
basic_machine=m68k-apollo
os=-bsd
;;
aros)
basic_machine=i386-pc
os=-aros
;;
aux)
basic_machine=m68k-apple
os=-aux
;;
balance)
basic_machine=ns32k-sequent
os=-dynix
;;
blackfin)
basic_machine=bfin-unknown
os=-linux
;;
blackfin-*)
basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
bluegene*)
basic_machine=powerpc-ibm
os=-cnk
;;
c54x-*)
basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
c55x-*)
basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
c6x-*)
basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
c90)
basic_machine=c90-cray
os=-unicos
;;
cegcc)
basic_machine=arm-unknown
os=-cegcc
;;
convex-c1)
basic_machine=c1-convex
os=-bsd
;;
convex-c2)
basic_machine=c2-convex
os=-bsd
;;
convex-c32)
basic_machine=c32-convex
os=-bsd
;;
convex-c34)
basic_machine=c34-convex
os=-bsd
;;
convex-c38)
basic_machine=c38-convex
os=-bsd
;;
cray | j90)
basic_machine=j90-cray
os=-unicos
;;
craynv)
basic_machine=craynv-cray
os=-unicosmp
;;
cr16 | cr16-*)
basic_machine=cr16-unknown
os=-elf
;;
crds | unos)
basic_machine=m68k-crds
;;
crisv32 | crisv32-* | etraxfs*)
basic_machine=crisv32-axis
;;
cris | cris-* | etrax*)
basic_machine=cris-axis
;;
crx)
basic_machine=crx-unknown
os=-elf
;;
da30 | da30-*)
basic_machine=m68k-da30
;;
decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
basic_machine=mips-dec
;;
decsystem10* | dec10*)
basic_machine=pdp10-dec
os=-tops10
;;
decsystem20* | dec20*)
basic_machine=pdp10-dec
os=-tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
basic_machine=m68k-motorola
;;
delta88)
basic_machine=m88k-motorola
os=-sysv3
;;
dicos)
basic_machine=i686-pc
os=-dicos
;;
djgpp)
basic_machine=i586-pc
os=-msdosdjgpp
;;
dpx20 | dpx20-*)
basic_machine=rs6000-bull
os=-bosx
;;
dpx2* | dpx2*-bull)
basic_machine=m68k-bull
os=-sysv3
;;
ebmon29k)
basic_machine=a29k-amd
os=-ebmon
;;
elxsi)
basic_machine=elxsi-elxsi
os=-bsd
;;
encore | umax | mmax)
basic_machine=ns32k-encore
;;
es1800 | OSE68k | ose68k | ose | OSE)
basic_machine=m68k-ericsson
os=-ose
;;
fx2800)
basic_machine=i860-alliant
;;
genix)
basic_machine=ns32k-ns
;;
gmicro)
basic_machine=tron-gmicro
os=-sysv
;;
go32)
basic_machine=i386-pc
os=-go32
;;
h3050r* | hiux*)
basic_machine=hppa1.1-hitachi
os=-hiuxwe2
;;
h8300hms)
basic_machine=h8300-hitachi
os=-hms
;;
h8300xray)
basic_machine=h8300-hitachi
os=-xray
;;
h8500hms)
basic_machine=h8500-hitachi
os=-hms
;;
harris)
basic_machine=m88k-harris
os=-sysv3
;;
hp300-*)
basic_machine=m68k-hp
;;
hp300bsd)
basic_machine=m68k-hp
os=-bsd
;;
hp300hpux)
basic_machine=m68k-hp
os=-hpux
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
basic_machine=hppa1.0-hp
;;
hp9k2[0-9][0-9] | hp9k31[0-9])
basic_machine=m68000-hp
;;
hp9k3[2-9][0-9])
basic_machine=m68k-hp
;;
hp9k6[0-9][0-9] | hp6[0-9][0-9])
basic_machine=hppa1.0-hp
;;
hp9k7[0-79][0-9] | hp7[0-79][0-9])
basic_machine=hppa1.1-hp
;;
hp9k78[0-9] | hp78[0-9])
# FIXME: really hppa2.0-hp
basic_machine=hppa1.1-hp
;;
hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
# FIXME: really hppa2.0-hp
basic_machine=hppa1.1-hp
;;
hp9k8[0-9][13679] | hp8[0-9][13679])
basic_machine=hppa1.1-hp
;;
hp9k8[0-9][0-9] | hp8[0-9][0-9])
basic_machine=hppa1.0-hp
;;
hppa-next)
os=-nextstep3
;;
hppaosf)
basic_machine=hppa1.1-hp
os=-osf
;;
hppro)
basic_machine=hppa1.1-hp
os=-proelf
;;
i370-ibm* | ibm*)
basic_machine=i370-ibm
;;
i*86v32)
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv32
;;
i*86v4*)
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv4
;;
i*86v)
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv
;;
i*86sol2)
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-solaris2
;;
i386mach)
basic_machine=i386-mach
os=-mach
;;
i386-vsta | vsta)
basic_machine=i386-unknown
os=-vsta
;;
iris | iris4d)
basic_machine=mips-sgi
case $os in
-irix*)
;;
*)
os=-irix4
;;
esac
;;
isi68 | isi)
basic_machine=m68k-isi
os=-sysv
;;
m68knommu)
basic_machine=m68k-unknown
os=-linux
;;
m68knommu-*)
basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
m88k-omron*)
basic_machine=m88k-omron
;;
magnum | m3230)
basic_machine=mips-mips
os=-sysv
;;
merlin)
basic_machine=ns32k-utek
os=-sysv
;;
microblaze*)
basic_machine=microblaze-xilinx
;;
mingw64)
basic_machine=x86_64-pc
os=-mingw64
;;
mingw32)
basic_machine=i686-pc
os=-mingw32
;;
mingw32ce)
basic_machine=arm-unknown
os=-mingw32ce
;;
miniframe)
basic_machine=m68000-convergent
;;
*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
basic_machine=m68k-atari
os=-mint
;;
mips3*-*)
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
;;
mips3*)
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
;;
monitor)
basic_machine=m68k-rom68k
os=-coff
;;
morphos)
basic_machine=powerpc-unknown
os=-morphos
;;
moxiebox)
basic_machine=moxie-unknown
os=-moxiebox
;;
msdos)
basic_machine=i386-pc
os=-msdos
;;
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
msys)
basic_machine=i686-pc
os=-msys
;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
nacl)
basic_machine=le32-unknown
os=-nacl
;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
;;
netbsd386)
basic_machine=i386-unknown
os=-netbsd
;;
netwinder)
basic_machine=armv4l-rebel
os=-linux
;;
news | news700 | news800 | news900)
basic_machine=m68k-sony
os=-newsos
;;
news1000)
basic_machine=m68030-sony
os=-newsos
;;
news-3600 | risc-news)
basic_machine=mips-sony
os=-newsos
;;
necv70)
basic_machine=v70-nec
os=-sysv
;;
next | m*-next )
basic_machine=m68k-next
case $os in
-nextstep* )
;;
-ns2*)
os=-nextstep2
;;
*)
os=-nextstep3
;;
esac
;;
nh3000)
basic_machine=m68k-harris
os=-cxux
;;
nh[45]000)
basic_machine=m88k-harris
os=-cxux
;;
nindy960)
basic_machine=i960-intel
os=-nindy
;;
mon960)
basic_machine=i960-intel
os=-mon960
;;
nonstopux)
basic_machine=mips-compaq
os=-nonstopux
;;
np1)
basic_machine=np1-gould
;;
neo-tandem)
basic_machine=neo-tandem
;;
nse-tandem)
basic_machine=nse-tandem
;;
nsr-tandem)
basic_machine=nsr-tandem
;;
op50n-* | op60c-*)
basic_machine=hppa1.1-oki
os=-proelf
;;
openrisc | openrisc-*)
basic_machine=or32-unknown
;;
os400)
basic_machine=powerpc-ibm
os=-os400
;;
OSE68000 | ose68000)
basic_machine=m68000-ericsson
os=-ose
;;
os68k)
basic_machine=m68k-none
os=-os68k
;;
pa-hitachi)
basic_machine=hppa1.1-hitachi
os=-hiuxwe2
;;
paragon)
basic_machine=i860-intel
os=-osf
;;
parisc)
basic_machine=hppa-unknown
os=-linux
;;
parisc-*)
basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
os=-linux
;;
pbd)
basic_machine=sparc-tti
;;
pbb)
basic_machine=m68k-tti
;;
pc532 | pc532-*)
basic_machine=ns32k-pc532
;;
pc98)
basic_machine=i386-pc
;;
pc98-*)
basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentium | p5 | k5 | k6 | nexgen | viac3)
basic_machine=i586-pc
;;
pentiumpro | p6 | 6x86 | athlon | athlon_*)
basic_machine=i686-pc
;;
pentiumii | pentium2 | pentiumiii | pentium3)
basic_machine=i686-pc
;;
pentium4)
basic_machine=i786-pc
;;
pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentiumpro-* | p6-* | 6x86-* | athlon-*)
basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pentium4-*)
basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
pn)
basic_machine=pn-gould
;;
power) basic_machine=power-ibm
;;
ppc | ppcbe) basic_machine=powerpc-unknown
;;
ppc-* | ppcbe-*)
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppcle | powerpclittle | ppc-le | powerpc-little)
basic_machine=powerpcle-unknown
;;
ppcle-* | powerpclittle-*)
basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppc64) basic_machine=powerpc64-unknown
;;
ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ppc64le | powerpc64little | ppc64-le | powerpc64-little)
basic_machine=powerpc64le-unknown
;;
ppc64le-* | powerpc64little-*)
basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
ps2)
basic_machine=i386-ibm
;;
pw32)
basic_machine=i586-unknown
os=-pw32
;;
rdos | rdos64)
basic_machine=x86_64-pc
os=-rdos
;;
rdos32)
basic_machine=i386-pc
os=-rdos
;;
rom68k)
basic_machine=m68k-rom68k
os=-coff
;;
rm[46]00)
basic_machine=mips-siemens
;;
rtpc | rtpc-*)
basic_machine=romp-ibm
;;
s390 | s390-*)
basic_machine=s390-ibm
;;
s390x | s390x-*)
basic_machine=s390x-ibm
;;
sa29200)
basic_machine=a29k-amd
os=-udi
;;
sb1)
basic_machine=mipsisa64sb1-unknown
;;
sb1el)
basic_machine=mipsisa64sb1el-unknown
;;
sde)
basic_machine=mipsisa32-sde
os=-elf
;;
sei)
basic_machine=mips-sei
os=-seiux
;;
sequent)
basic_machine=i386-sequent
;;
sh)
basic_machine=sh-hitachi
os=-hms
;;
sh5el)
basic_machine=sh5le-unknown
;;
sh64)
basic_machine=sh64-unknown
;;
sparclite-wrs | simso-wrs)
basic_machine=sparclite-wrs
os=-vxworks
;;
sps7)
basic_machine=m68k-bull
os=-sysv2
;;
spur)
basic_machine=spur-unknown
;;
st2000)
basic_machine=m68k-tandem
;;
stratus)
basic_machine=i860-stratus
os=-sysv4
;;
strongarm-* | thumb-*)
basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
sun2)
basic_machine=m68000-sun
;;
sun2os3)
basic_machine=m68000-sun
os=-sunos3
;;
sun2os4)
basic_machine=m68000-sun
os=-sunos4
;;
sun3os3)
basic_machine=m68k-sun
os=-sunos3
;;
sun3os4)
basic_machine=m68k-sun
os=-sunos4
;;
sun4os3)
basic_machine=sparc-sun
os=-sunos3
;;
sun4os4)
basic_machine=sparc-sun
os=-sunos4
;;
sun4sol2)
basic_machine=sparc-sun
os=-solaris2
;;
sun3 | sun3-*)
basic_machine=m68k-sun
;;
sun4)
basic_machine=sparc-sun
;;
sun386 | sun386i | roadrunner)
basic_machine=i386-sun
;;
sv1)
basic_machine=sv1-cray
os=-unicos
;;
symmetry)
basic_machine=i386-sequent
os=-dynix
;;
t3e)
basic_machine=alphaev5-cray
os=-unicos
;;
t90)
basic_machine=t90-cray
os=-unicos
;;
tile*)
basic_machine=$basic_machine-unknown
os=-linux-gnu
;;
tx39)
basic_machine=mipstx39-unknown
;;
tx39el)
basic_machine=mipstx39el-unknown
;;
toad1)
basic_machine=pdp10-xkl
os=-tops20
;;
tower | tower-32)
basic_machine=m68k-ncr
;;
tpf)
basic_machine=s390x-ibm
os=-tpf
;;
udi29k)
basic_machine=a29k-amd
os=-udi
;;
ultra3)
basic_machine=a29k-nyu
os=-sym1
;;
v810 | necv810)
basic_machine=v810-nec
os=-none
;;
vaxv)
basic_machine=vax-dec
os=-sysv
;;
vms)
basic_machine=vax-dec
os=-vms
;;
vpp*|vx|vx-*)
basic_machine=f301-fujitsu
;;
vxworks960)
basic_machine=i960-wrs
os=-vxworks
;;
vxworks68)
basic_machine=m68k-wrs
os=-vxworks
;;
vxworks29k)
basic_machine=a29k-wrs
os=-vxworks
;;
w65*)
basic_machine=w65-wdc
os=-none
;;
w89k-*)
basic_machine=hppa1.1-winbond
os=-proelf
;;
xbox)
basic_machine=i686-pc
os=-mingw32
;;
xps | xps100)
basic_machine=xps100-honeywell
;;
xscale-* | xscalee[bl]-*)
basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
;;
ymp)
basic_machine=ymp-cray
os=-unicos
;;
z8k-*-coff)
basic_machine=z8k-unknown
os=-sim
;;
z80-*-coff)
basic_machine=z80-unknown
os=-sim
;;
none)
basic_machine=none-none
os=-none
;;
# Here we handle the default manufacturer of certain CPU types. It is in
# some cases the only manufacturer, in others, it is the most popular.
w89k)
basic_machine=hppa1.1-winbond
;;
op50n)
basic_machine=hppa1.1-oki
;;
op60c)
basic_machine=hppa1.1-oki
;;
romp)
basic_machine=romp-ibm
;;
mmix)
basic_machine=mmix-knuth
;;
rs6000)
basic_machine=rs6000-ibm
;;
vax)
basic_machine=vax-dec
;;
pdp10)
# there are many clones, so DEC is not a safe bet
basic_machine=pdp10-unknown
;;
pdp11)
basic_machine=pdp11-dec
;;
we32k)
basic_machine=we32k-att
;;
sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
basic_machine=sh-unknown
;;
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
basic_machine=sparc-sun
;;
cydra)
basic_machine=cydra-cydrome
;;
orion)
basic_machine=orion-highlevel
;;
orion105)
basic_machine=clipper-highlevel
;;
mac | mpw | mac-mpw)
basic_machine=m68k-apple
;;
pmac | pmac-mpw)
basic_machine=powerpc-apple
;;
*-unknown)
# Make sure to match an already-canonicalized machine name.
;;
*)
echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
exit 1
;;
esac
# Here we canonicalize certain aliases for manufacturers.
case $basic_machine in
*-digital*)
basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
;;
*-commodore*)
basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
;;
*)
;;
esac
# Decode manufacturer-specific aliases for certain operating systems.
if [ x"$os" != x"" ]
then
case $os in
# First match some system type aliases
# that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
-auroraux)
os=-auroraux
;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
;;
-solaris)
os=-solaris2
;;
-svr4*)
os=-sysv4
;;
-unixware*)
os=-sysv4.2uw
;;
-gnu/linux*)
os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
;;
# First accept the basic system types.
# The portable systems comes first.
# Each alternative MUST END IN A *, to match a version number.
# -sysv* is not here because it comes later, after sysvr4.
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
| -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
| -aos* | -aros* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
| -bitrig* | -openbsd* | -solidbsd* \
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
| -chorusos* | -chorusrdb* | -cegcc* \
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
case $basic_machine in
x86-* | i*86-*)
;;
*)
os=-nto$os
;;
esac
;;
-nto-qnx*)
;;
-nto*)
os=`echo $os | sed -e 's|nto|nto-qnx|'`
;;
-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
| -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
| -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
;;
-mac*)
os=`echo $os | sed -e 's|mac|macos|'`
;;
-linux-dietlibc)
os=-linux-dietlibc
;;
-linux*)
os=`echo $os | sed -e 's|linux|linux-gnu|'`
;;
-sunos5*)
os=`echo $os | sed -e 's|sunos5|solaris2|'`
;;
-sunos6*)
os=`echo $os | sed -e 's|sunos6|solaris3|'`
;;
-opened*)
os=-openedition
;;
-os400*)
os=-os400
;;
-wince*)
os=-wince
;;
-osfrose*)
os=-osfrose
;;
-osf*)
os=-osf
;;
-utek*)
os=-bsd
;;
-dynix*)
os=-bsd
;;
-acis*)
os=-aos
;;
-atheos*)
os=-atheos
;;
-syllable*)
os=-syllable
;;
-386bsd)
os=-bsd
;;
-ctix* | -uts*)
os=-sysv
;;
-nova*)
os=-rtmk-nova
;;
-ns2 )
os=-nextstep2
;;
-nsk*)
os=-nsk
;;
# Preserve the version number of sinix5.
-sinix5.*)
os=`echo $os | sed -e 's|sinix|sysv|'`
;;
-sinix*)
os=-sysv4
;;
-tpf*)
os=-tpf
;;
-triton*)
os=-sysv3
;;
-oss*)
os=-sysv3
;;
-svr4)
os=-sysv4
;;
-svr3)
os=-sysv3
;;
-sysvr4)
os=-sysv4
;;
# This must come after -sysvr4.
-sysv*)
;;
-ose*)
os=-ose
;;
-es1800*)
os=-ose
;;
-xenix)
os=-xenix
;;
-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
os=-mint
;;
-aros*)
os=-aros
;;
-zvmoe)
os=-zvmoe
;;
-dicos*)
os=-dicos
;;
-nacl*)
;;
-none)
;;
*)
# Get rid of the `-' at the beginning of $os.
os=`echo $os | sed 's/[^-]*-//'`
echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
exit 1
;;
esac
else
# Here we handle the default operating systems that come with various machines.
# The value should be what the vendor currently ships out the door with their
# machine or put another way, the most popular os provided with the machine.
# Note that if you're going to try to match "-MANUFACTURER" here (say,
# "-sun"), then you have to tell the case statement up towards the top
# that MANUFACTURER isn't an operating system. Otherwise, code above
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
case $basic_machine in
score-*)
os=-elf
;;
spu-*)
os=-elf
;;
*-acorn)
os=-riscix1.2
;;
arm*-rebel)
os=-linux
;;
arm*-semi)
os=-aout
;;
c4x-* | tic4x-*)
os=-coff
;;
c8051-*)
os=-elf
;;
hexagon-*)
os=-elf
;;
tic54x-*)
os=-coff
;;
tic55x-*)
os=-coff
;;
tic6x-*)
os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
os=-tops20
;;
pdp11-*)
os=-none
;;
*-dec | vax-*)
os=-ultrix4.2
;;
m68*-apollo)
os=-domain
;;
i386-sun)
os=-sunos4.0.2
;;
m68000-sun)
os=-sunos3
;;
m68*-cisco)
os=-aout
;;
mep-*)
os=-elf
;;
mips*-cisco)
os=-elf
;;
mips*-*)
os=-elf
;;
or32-*)
os=-coff
;;
*-tti) # must be before sparc entry or we get the wrong os.
os=-sysv3
;;
sparc-* | *-sun)
os=-sunos4.1.1
;;
*-be)
os=-beos
;;
*-haiku)
os=-haiku
;;
*-ibm)
os=-aix
;;
*-knuth)
os=-mmixware
;;
*-wec)
os=-proelf
;;
*-winbond)
os=-proelf
;;
*-oki)
os=-proelf
;;
*-hp)
os=-hpux
;;
*-hitachi)
os=-hiux
;;
i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
os=-sysv
;;
*-cbm)
os=-amigaos
;;
*-dg)
os=-dgux
;;
*-dolphin)
os=-sysv3
;;
m68k-ccur)
os=-rtu
;;
m88k-omron*)
os=-luna
;;
*-next )
os=-nextstep
;;
*-sequent)
os=-ptx
;;
*-crds)
os=-unos
;;
*-ns)
os=-genix
;;
i370-*)
os=-mvs
;;
*-next)
os=-nextstep3
;;
*-gould)
os=-sysv
;;
*-highlevel)
os=-bsd
;;
*-encore)
os=-bsd
;;
*-sgi)
os=-irix
;;
*-siemens)
os=-sysv4
;;
*-masscomp)
os=-rtu
;;
f30[01]-fujitsu | f700-fujitsu)
os=-uxpv
;;
*-rom68k)
os=-coff
;;
*-*bug)
os=-coff
;;
*-apple)
os=-macos
;;
*-atari*)
os=-mint
;;
*)
os=-none
;;
esac
fi
# Here we handle the case where we know the os, and the CPU type, but not the
# manufacturer. We pick the logical manufacturer.
vendor=unknown
case $basic_machine in
*-unknown)
case $os in
-riscix*)
vendor=acorn
;;
-sunos*)
vendor=sun
;;
-cnk*|-aix*)
vendor=ibm
;;
-beos*)
vendor=be
;;
-hpux*)
vendor=hp
;;
-mpeix*)
vendor=hp
;;
-hiux*)
vendor=hitachi
;;
-unos*)
vendor=crds
;;
-dgux*)
vendor=dg
;;
-luna*)
vendor=omron
;;
-genix*)
vendor=ns
;;
-mvs* | -opened*)
vendor=ibm
;;
-os400*)
vendor=ibm
;;
-ptx*)
vendor=sequent
;;
-tpf*)
vendor=ibm
;;
-vxsim* | -vxworks* | -windiss*)
vendor=wrs
;;
-aux*)
vendor=apple
;;
-hms*)
vendor=hitachi
;;
-mpw* | -macos*)
vendor=apple
;;
-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
vendor=atari
;;
-vos*)
vendor=stratus
;;
esac
basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
;;
esac
echo $basic_machine$os
exit
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
# End:
memcached-1.4.25/compile 0000755 0001750 0001750 00000016245 12367655477 012046 0000000 0000000 #! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
# Written by Tom Tromey .
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to or send patches to
# .
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to .
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:
memcached-1.4.25/jenkins_hash.h 0000664 0001750 0001750 00000000325 12623270407 013254 0000000 0000000 #ifndef JENKINS_HASH_H
#define JENKINS_HASH_H
#ifdef __cplusplus
extern "C" {
#endif
uint32_t jenkins_hash(const void *key, size_t length);
#ifdef __cplusplus
}
#endif
#endif /* JENKINS_HASH_H */
memcached-1.4.25/protocol_binary.h 0000664 0001750 0001750 00000040215 12623270407 014017 0000000 0000000 /*
* Copyright (c) <2008>, Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the 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 SUN MICROSYSTEMS, INC. ``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 SUN MICROSYSTEMS, INC. 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.
*/
/*
* Summary: Constants used by to implement the binary protocol.
*
* Copy: See Copyright for the status of this software.
*
* Author: Trond Norbye
*/
#ifndef PROTOCOL_BINARY_H
#define PROTOCOL_BINARY_H
/**
* This file contains definitions of the constants and packet formats
* defined in the binary specification. Please note that you _MUST_ remember
* to convert each multibyte field to / from network byte order to / from
* host order.
*/
#ifdef __cplusplus
extern "C"
{
#endif
/**
* Definition of the legal "magic" values used in a packet.
* See section 3.1 Magic byte
*/
typedef enum {
PROTOCOL_BINARY_REQ = 0x80,
PROTOCOL_BINARY_RES = 0x81
} protocol_binary_magic;
/**
* Definition of the valid response status numbers.
* See section 3.2 Response Status
*/
typedef enum {
PROTOCOL_BINARY_RESPONSE_SUCCESS = 0x00,
PROTOCOL_BINARY_RESPONSE_KEY_ENOENT = 0x01,
PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS = 0x02,
PROTOCOL_BINARY_RESPONSE_E2BIG = 0x03,
PROTOCOL_BINARY_RESPONSE_EINVAL = 0x04,
PROTOCOL_BINARY_RESPONSE_NOT_STORED = 0x05,
PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL = 0x06,
PROTOCOL_BINARY_RESPONSE_AUTH_ERROR = 0x20,
PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE = 0x21,
PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND = 0x81,
PROTOCOL_BINARY_RESPONSE_ENOMEM = 0x82
} protocol_binary_response_status;
/**
* Definition of the different command opcodes.
* See section 3.3 Command Opcodes
*/
typedef enum {
PROTOCOL_BINARY_CMD_GET = 0x00,
PROTOCOL_BINARY_CMD_SET = 0x01,
PROTOCOL_BINARY_CMD_ADD = 0x02,
PROTOCOL_BINARY_CMD_REPLACE = 0x03,
PROTOCOL_BINARY_CMD_DELETE = 0x04,
PROTOCOL_BINARY_CMD_INCREMENT = 0x05,
PROTOCOL_BINARY_CMD_DECREMENT = 0x06,
PROTOCOL_BINARY_CMD_QUIT = 0x07,
PROTOCOL_BINARY_CMD_FLUSH = 0x08,
PROTOCOL_BINARY_CMD_GETQ = 0x09,
PROTOCOL_BINARY_CMD_NOOP = 0x0a,
PROTOCOL_BINARY_CMD_VERSION = 0x0b,
PROTOCOL_BINARY_CMD_GETK = 0x0c,
PROTOCOL_BINARY_CMD_GETKQ = 0x0d,
PROTOCOL_BINARY_CMD_APPEND = 0x0e,
PROTOCOL_BINARY_CMD_PREPEND = 0x0f,
PROTOCOL_BINARY_CMD_STAT = 0x10,
PROTOCOL_BINARY_CMD_SETQ = 0x11,
PROTOCOL_BINARY_CMD_ADDQ = 0x12,
PROTOCOL_BINARY_CMD_REPLACEQ = 0x13,
PROTOCOL_BINARY_CMD_DELETEQ = 0x14,
PROTOCOL_BINARY_CMD_INCREMENTQ = 0x15,
PROTOCOL_BINARY_CMD_DECREMENTQ = 0x16,
PROTOCOL_BINARY_CMD_QUITQ = 0x17,
PROTOCOL_BINARY_CMD_FLUSHQ = 0x18,
PROTOCOL_BINARY_CMD_APPENDQ = 0x19,
PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a,
PROTOCOL_BINARY_CMD_TOUCH = 0x1c,
PROTOCOL_BINARY_CMD_GAT = 0x1d,
PROTOCOL_BINARY_CMD_GATQ = 0x1e,
PROTOCOL_BINARY_CMD_GATK = 0x23,
PROTOCOL_BINARY_CMD_GATKQ = 0x24,
PROTOCOL_BINARY_CMD_SASL_LIST_MECHS = 0x20,
PROTOCOL_BINARY_CMD_SASL_AUTH = 0x21,
PROTOCOL_BINARY_CMD_SASL_STEP = 0x22,
/* These commands are used for range operations and exist within
* this header for use in other projects. Range operations are
* not expected to be implemented in the memcached server itself.
*/
PROTOCOL_BINARY_CMD_RGET = 0x30,
PROTOCOL_BINARY_CMD_RSET = 0x31,
PROTOCOL_BINARY_CMD_RSETQ = 0x32,
PROTOCOL_BINARY_CMD_RAPPEND = 0x33,
PROTOCOL_BINARY_CMD_RAPPENDQ = 0x34,
PROTOCOL_BINARY_CMD_RPREPEND = 0x35,
PROTOCOL_BINARY_CMD_RPREPENDQ = 0x36,
PROTOCOL_BINARY_CMD_RDELETE = 0x37,
PROTOCOL_BINARY_CMD_RDELETEQ = 0x38,
PROTOCOL_BINARY_CMD_RINCR = 0x39,
PROTOCOL_BINARY_CMD_RINCRQ = 0x3a,
PROTOCOL_BINARY_CMD_RDECR = 0x3b,
PROTOCOL_BINARY_CMD_RDECRQ = 0x3c
/* End Range operations */
} protocol_binary_command;
/**
* Definition of the data types in the packet
* See section 3.4 Data Types
*/
typedef enum {
PROTOCOL_BINARY_RAW_BYTES = 0x00
} protocol_binary_datatypes;
/**
* Definition of the header structure for a request packet.
* See section 2
*/
typedef union {
struct {
uint8_t magic;
uint8_t opcode;
uint16_t keylen;
uint8_t extlen;
uint8_t datatype;
uint16_t reserved;
uint32_t bodylen;
uint32_t opaque;
uint64_t cas;
} request;
uint8_t bytes[24];
} protocol_binary_request_header;
/**
* Definition of the header structure for a response packet.
* See section 2
*/
typedef union {
struct {
uint8_t magic;
uint8_t opcode;
uint16_t keylen;
uint8_t extlen;
uint8_t datatype;
uint16_t status;
uint32_t bodylen;
uint32_t opaque;
uint64_t cas;
} response;
uint8_t bytes[24];
} protocol_binary_response_header;
/**
* Definition of a request-packet containing no extras
*/
typedef union {
struct {
protocol_binary_request_header header;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header)];
} protocol_binary_request_no_extras;
/**
* Definition of a response-packet containing no extras
*/
typedef union {
struct {
protocol_binary_response_header header;
} message;
uint8_t bytes[sizeof(protocol_binary_response_header)];
} protocol_binary_response_no_extras;
/**
* Definition of the packet used by the get, getq, getk and getkq command.
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_get;
typedef protocol_binary_request_no_extras protocol_binary_request_getq;
typedef protocol_binary_request_no_extras protocol_binary_request_getk;
typedef protocol_binary_request_no_extras protocol_binary_request_getkq;
/**
* Definition of the packet returned from a successful get, getq, getk and
* getkq.
* See section 4
*/
typedef union {
struct {
protocol_binary_response_header header;
struct {
uint32_t flags;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_response_header) + 4];
} protocol_binary_response_get;
typedef protocol_binary_response_get protocol_binary_response_getq;
typedef protocol_binary_response_get protocol_binary_response_getk;
typedef protocol_binary_response_get protocol_binary_response_getkq;
/**
* Definition of the packet used by the delete command
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_delete;
/**
* Definition of the packet returned by the delete command
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_delete;
/**
* Definition of the packet used by the flush command
* See section 4
* Please note that the expiration field is optional, so remember to see
* check the header.bodysize to see if it is present.
*/
typedef union {
struct {
protocol_binary_request_header header;
struct {
uint32_t expiration;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
} protocol_binary_request_flush;
/**
* Definition of the packet returned by the flush command
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_flush;
/**
* Definition of the packet used by set, add and replace
* See section 4
*/
typedef union {
struct {
protocol_binary_request_header header;
struct {
uint32_t flags;
uint32_t expiration;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header) + 8];
} protocol_binary_request_set;
typedef protocol_binary_request_set protocol_binary_request_add;
typedef protocol_binary_request_set protocol_binary_request_replace;
/**
* Definition of the packet returned by set, add and replace
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_set;
typedef protocol_binary_response_no_extras protocol_binary_response_add;
typedef protocol_binary_response_no_extras protocol_binary_response_replace;
/**
* Definition of the noop packet
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_noop;
/**
* Definition of the packet returned by the noop command
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_noop;
/**
* Definition of the structure used by the increment and decrement
* command.
* See section 4
*/
typedef union {
struct {
protocol_binary_request_header header;
struct {
uint64_t delta;
uint64_t initial;
uint32_t expiration;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header) + 20];
} protocol_binary_request_incr;
typedef protocol_binary_request_incr protocol_binary_request_decr;
/**
* Definition of the response from an incr or decr command
* command.
* See section 4
*/
typedef union {
struct {
protocol_binary_response_header header;
struct {
uint64_t value;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_response_header) + 8];
} protocol_binary_response_incr;
typedef protocol_binary_response_incr protocol_binary_response_decr;
/**
* Definition of the quit
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_quit;
/**
* Definition of the packet returned by the quit command
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_quit;
/**
* Definition of the packet used by append and prepend command
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_append;
typedef protocol_binary_request_no_extras protocol_binary_request_prepend;
/**
* Definition of the packet returned from a successful append or prepend
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_append;
typedef protocol_binary_response_no_extras protocol_binary_response_prepend;
/**
* Definition of the packet used by the version command
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_version;
/**
* Definition of the packet returned from a successful version command
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_version;
/**
* Definition of the packet used by the stats command.
* See section 4
*/
typedef protocol_binary_request_no_extras protocol_binary_request_stats;
/**
* Definition of the packet returned from a successful stats command
* See section 4
*/
typedef protocol_binary_response_no_extras protocol_binary_response_stats;
/**
* Definition of the packet used by the touch command.
*/
typedef union {
struct {
protocol_binary_request_header header;
struct {
uint32_t expiration;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
} protocol_binary_request_touch;
/**
* Definition of the packet returned from the touch command
*/
typedef protocol_binary_response_no_extras protocol_binary_response_touch;
/**
* Definition of the packet used by the GAT(Q) command.
*/
typedef union {
struct {
protocol_binary_request_header header;
struct {
uint32_t expiration;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
} protocol_binary_request_gat;
typedef protocol_binary_request_gat protocol_binary_request_gatq;
typedef protocol_binary_request_gat protocol_binary_request_gatk;
typedef protocol_binary_request_gat protocol_binary_request_gatkq;
/**
* Definition of the packet returned from the GAT(Q)
*/
typedef protocol_binary_response_get protocol_binary_response_gat;
typedef protocol_binary_response_get protocol_binary_response_gatq;
typedef protocol_binary_response_get protocol_binary_response_gatk;
typedef protocol_binary_response_get protocol_binary_response_gatkq;
/**
* Definition of a request for a range operation.
* See http://code.google.com/p/memcached/wiki/RangeOps
*
* These types are used for range operations and exist within
* this header for use in other projects. Range operations are
* not expected to be implemented in the memcached server itself.
*/
typedef union {
struct {
protocol_binary_response_header header;
struct {
uint16_t size;
uint8_t reserved;
uint8_t flags;
uint32_t max_results;
} body;
} message;
uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
} protocol_binary_request_rangeop;
typedef protocol_binary_request_rangeop protocol_binary_request_rget;
typedef protocol_binary_request_rangeop protocol_binary_request_rset;
typedef protocol_binary_request_rangeop protocol_binary_request_rsetq;
typedef protocol_binary_request_rangeop protocol_binary_request_rappend;
typedef protocol_binary_request_rangeop protocol_binary_request_rappendq;
typedef protocol_binary_request_rangeop protocol_binary_request_rprepend;
typedef protocol_binary_request_rangeop protocol_binary_request_rprependq;
typedef protocol_binary_request_rangeop protocol_binary_request_rdelete;
typedef protocol_binary_request_rangeop protocol_binary_request_rdeleteq;
typedef protocol_binary_request_rangeop protocol_binary_request_rincr;
typedef protocol_binary_request_rangeop protocol_binary_request_rincrq;
typedef protocol_binary_request_rangeop protocol_binary_request_rdecr;
typedef protocol_binary_request_rangeop protocol_binary_request_rdecrq;
#ifdef __cplusplus
}
#endif
#endif /* PROTOCOL_BINARY_H */
memcached-1.4.25/murmur3_hash.c 0000664 0001750 0001750 00000005412 12623270407 013222 0000000 0000000 //-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.
#include "murmur3_hash.h"
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
// Microsoft Visual Studio
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#include
#define ROTL32(x,y) _rotl(x,y)
#define BIG_CONSTANT(x) (x)
// Other compilers
#else // defined(_MSC_VER)
#define FORCE_INLINE inline __attribute__((always_inline))
static inline uint32_t rotl32 ( uint32_t x, int8_t r )
{
return (x << r) | (x >> (32 - r));
}
#define ROTL32(x,y) rotl32(x,y)
#define BIG_CONSTANT(x) (x##LLU)
#endif // !defined(_MSC_VER)
//-----------------------------------------------------------------------------
// Block read - if your platform needs to do endian-swapping or can only
// handle aligned reads, do the conversion here
static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i )
{
return p[i];
}
//-----------------------------------------------------------------------------
// Finalization mix - force all bits of a hash block to avalanche
static FORCE_INLINE uint32_t fmix32 ( uint32_t h )
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
//-----------------------------------------------------------------------------
/* Definition modified slightly from the public domain interface (no seed +
* return value */
uint32_t MurmurHash3_x86_32 ( const void * key, size_t length)
{
const uint8_t * data = (const uint8_t*)key;
const int nblocks = length / 4;
uint32_t h1 = 0;
uint32_t c1 = 0xcc9e2d51;
uint32_t c2 = 0x1b873593;
//----------
// body
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
for(int i = -nblocks; i; i++)
{
uint32_t k1 = getblock32(blocks,i);
k1 *= c1;
k1 = ROTL32(k1,15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1,13);
h1 = h1*5+0xe6546b64;
}
//----------
// tail
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
uint32_t k1 = 0;
switch(length & 3)
{
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= length;
h1 = fmix32(h1);
//*(uint32_t*)out = h1;
return h1;
}
memcached-1.4.25/aclocal.m4 0000664 0001750 0001750 00000123671 12623542630 012311 0000000 0000000 # generated automatically by aclocal 1.14.1 -*- Autoconf -*-
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
[m4_warning([this file was generated for autoconf 2.69.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
# Copyright (C) 2002-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.14'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.14.1], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.14.1])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is '.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[dnl Rely on autoconf to set up CDPATH properly.
AC_PREREQ([2.50])dnl
# expand $ac_aux_dir to an absolute path
am_aux_dir=`cd $ac_aux_dir && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ([2.52])dnl
m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
m4_define([_AM_COND_VALUE_$1], [$2])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
# will think it sees a *use*, and therefore will trigger all it's
# C support machinery. Also note that it means that autoscan, seeing
# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
# _AM_DEPENDENCIES(NAME)
# ----------------------
# See how the compiler implements dependency checking.
# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
# We try a few techniques and use that to set a single cache variable.
#
# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
# dependency, and given that the user is not expected to run this macro,
# just rely on AC_PROG_CC.
AC_DEFUN([_AM_DEPENDENCIES],
[AC_REQUIRE([AM_SET_DEPDIR])dnl
AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
AC_REQUIRE([AM_MAKE_INCLUDE])dnl
AC_REQUIRE([AM_DEP_TRACK])dnl
m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
[$1], [CXX], [depcc="$CXX" am_compiler_list=],
[$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
[$1], [UPC], [depcc="$UPC" am_compiler_list=],
[$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
AC_CACHE_CHECK([dependency style of $depcc],
[am_cv_$1_dependencies_compiler_type],
[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
# making a dummy file named 'D' -- because '-MD' means "put the output
# in D".
rm -rf conftest.dir
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
cp "$am_depcomp" conftest.dir
cd conftest.dir
# We will build objects and dependencies in a subdirectory because
# it helps to detect inapplicable dependency modes. For instance
# both Tru64's cc and ICC support -MD to output dependencies as a
# side effect of compilation, but ICC will put the dependencies in
# the current directory while Tru64 will put them in the object
# directory.
mkdir sub
am_cv_$1_dependencies_compiler_type=none
if test "$am_compiler_list" = ""; then
am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
fi
am__universal=false
m4_case([$1], [CC],
[case " $depcc " in #(
*\ -arch\ *\ -arch\ *) am__universal=true ;;
esac],
[CXX],
[case " $depcc " in #(
*\ -arch\ *\ -arch\ *) am__universal=true ;;
esac])
for depmode in $am_compiler_list; do
# Setup a source with many dependencies, because some compilers
# like to wrap large dependency lists on column 80 (with \), and
# we should not choose a depcomp mode which is confused by this.
#
# We need to recreate these files for each test, as the compiler may
# overwrite some of them when testing with obscure command lines.
# This happens at least with the AIX C compiler.
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
# Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
# Solaris 10 /bin/sh.
echo '/* dummy */' > sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
# We check with '-c' and '-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
# handle '-M -o', and we need to detect this. Also, some Intel
# versions had trouble with output in subdirs.
am__obj=sub/conftest.${OBJEXT-o}
am__minus_obj="-o $am__obj"
case $depmode in
gcc)
# This depmode causes a compiler race in universal mode.
test "$am__universal" = false || continue
;;
nosideeffect)
# After this tag, mechanisms are not by side-effect, so they'll
# only be used when explicitly requested.
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
msvc7 | msvc7msys | msvisualcpp | msvcmsys)
# This compiler won't grok '-c -o', but also, the minuso test has
# not run yet. These depmodes are late enough in the game, and
# so weak that their functioning should not be impacted.
am__obj=conftest.${OBJEXT-o}
am__minus_obj=
;;
none) break ;;
esac
if depmode=$depmode \
source=sub/conftest.c object=$am__obj \
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thusly:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_$1_dependencies_compiler_type=$depmode
break
fi
fi
done
cd ..
rm -rf conftest.dir
else
am_cv_$1_dependencies_compiler_type=none
fi
])
AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
AM_CONDITIONAL([am__fastdep$1], [
test "x$enable_dependency_tracking" != xno \
&& test "$am_cv_$1_dependencies_compiler_type" = gcc3])
])
# AM_SET_DEPDIR
# -------------
# Choose a directory name for dependency files.
# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
AC_DEFUN([AM_SET_DEPDIR],
[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
])
# AM_DEP_TRACK
# ------------
AC_DEFUN([AM_DEP_TRACK],
[AC_ARG_ENABLE([dependency-tracking], [dnl
AS_HELP_STRING(
[--enable-dependency-tracking],
[do not reject slow dependency extractors])
AS_HELP_STRING(
[--disable-dependency-tracking],
[speeds up one-time build])])
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
am__nodep='_no'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])dnl
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
AC_SUBST([am__nodep])dnl
_AM_SUBST_NOTMAKE([am__nodep])dnl
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
[{
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
case $CONFIG_FILES in
*\'*) eval set x "$CONFIG_FILES" ;;
*) set x $CONFIG_FILES ;;
esac
shift
for mf
do
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
# We used to match only the files named 'Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# Grep'ing the whole file is not good either: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
dirpart=`AS_DIRNAME("$mf")`
else
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
# from the Makefile without running 'make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
test -z "$am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`AS_DIRNAME(["$file"])`
AS_MKDIR_P([$dirpart/$fdir])
# echo "creating $dirpart/$file"
echo '# dummy' > "$dirpart/$file"
done
done
}
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
# AM_OUTPUT_DEPENDENCY_COMMANDS
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
# This code is only required when automatic dependency tracking
# is enabled. FIXME. This creates each '.P' file that we will
# need in order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
[AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])
[_AM_PROG_CC_C_O
])
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.65])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[AC_DIAGNOSE([obsolete],
[$0: two- and three-arguments forms are deprecated.])
m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(
m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
[ok:ok],,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
AM_MISSING_PROG([AUTOCONF], [autoconf])
AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
AM_MISSING_PROG([AUTOHEADER], [autoheader])
AM_MISSING_PROG([MAKEINFO], [makeinfo])
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
#
#
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
# We need awk for the "check" target. The system "awk" is bad on
# some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES([CC])],
[m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES([CXX])],
[m4_define([AC_PROG_CXX],
m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES([OBJC])],
[m4_define([AC_PROG_OBJC],
m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
[_AM_DEPENDENCIES([OBJCXX])],
[m4_define([AC_PROG_OBJCXX],
m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
])
AC_REQUIRE([AM_SILENT_RULES])dnl
dnl The testsuite driver may need to know about EXEEXT, so add the
dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
# POSIX will say in a future version that running "rm -f" with no argument
# is OK; and we want to be able to make that assumption in our Makefile
# recipes. So use an aggressive probe to check that the usage we want is
# actually supported "in the wild" to an acceptable degree.
# See automake bug#10828.
# To make any issue more visible, cause the running configure to be aborted
# by default if the 'rm' program in use doesn't match our expectations; the
# user can still override this though.
if rm -f && rm -fr && rm -rf; then : OK; else
cat >&2 <<'END'
Oops!
Your 'rm' program seems unable to run without file operands specified
on the command line, even when the '-f' option is present. This is contrary
to the behaviour of most rm programs out there, and not conforming with
the upcoming POSIX standard:
Please tell bug-automake@gnu.org about your system, including the value
of your $PATH and any error possibly output before this message. This
can help us improve future automake versions.
END
if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
echo 'Configuration will proceed anyway, since you have set the' >&2
echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
echo >&2
else
cat >&2 <<'END'
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
that behaves properly: .
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
to "yes", and re-run configure.
END
AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
fi
fi
])
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_arg=$1
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$_am_arg | $_am_arg:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
if test x"${install_sh}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
*)
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
AC_SUBST([install_sh])])
# Copyright (C) 2003-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MAKE_INCLUDE()
# -----------------
# Check to see how make treats includes.
AC_DEFUN([AM_MAKE_INCLUDE],
[am_make=${MAKE-make}
cat > confinc << 'END'
am__doit:
@echo this is the am__doit target
.PHONY: am__doit
END
# If we don't find an include directive, just comment out the code.
AC_MSG_CHECKING([for style of include used by $am_make])
am__include="#"
am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
# Ignore all kinds of additional output from 'make'.
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=include
am__quote=
_am_result=GNU
;;
esac
# Now try BSD make style include.
if test "$am__include" = "#"; then
echo '.include "confinc"' > confmf
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=.include
am__quote="\""
_am_result=BSD
;;
esac
fi
AC_SUBST([am__include])
AC_SUBST([am__quote])
AC_MSG_RESULT([$_am_result])
rm -f confinc confmf
])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it is modern enough.
# If it is, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
*)
MISSING="\${SHELL} $am_aux_dir/missing" ;;
esac
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
am_missing_run="$MISSING "
else
am_missing_run=
AC_MSG_WARN(['missing' script is too old or missing])
fi
])
# -*- Autoconf -*-
# Obsolete and "removed" macros, that must however still report explicit
# error messages when used, to smooth transition.
#
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
AC_DEFUN([AM_CONFIG_HEADER],
[AC_DIAGNOSE([obsolete],
['$0': this macro is obsolete.
You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl
AC_CONFIG_HEADERS($@)])
AC_DEFUN([AM_PROG_CC_STDC],
[AC_PROG_CC
am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
AC_DIAGNOSE([obsolete],
['$0': this macro is obsolete.
You should simply use the 'AC][_PROG_CC' macro instead.
Also, your code should no longer depend upon 'am_cv_prog_cc_stdc',
but upon 'ac_cv_prog_cc_stdc'.])])
AC_DEFUN([AM_C_PROTOTYPES],
[AC_FATAL([automatic de-ANSI-fication support has been removed])])
AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# --------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
# _AM_SET_OPTIONS(OPTIONS)
# ------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_CC_C_O
# ---------------
# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
# to automatically call this.
AC_DEFUN([_AM_PROG_CC_C_O],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([compile])dnl
AC_LANG_PUSH([C])dnl
AC_CACHE_CHECK(
[whether $CC understands -c and -o together],
[am_cv_prog_cc_c_o],
[AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
# Make sure it works both with $CC and with simple cc.
# Following AC_PROG_CC_C_O, we do the test twice because some
# compilers refuse to overwrite an existing .o file with -o,
# though they will create one.
am_cv_prog_cc_c_o=yes
for am_i in 1 2; do
if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
&& test -f conftest2.$ac_objext; then
: OK
else
am_cv_prog_cc_c_o=no
break
fi
done
rm -f core conftest*
unset am_i])
if test "$am_cv_prog_cc_c_o" != yes; then
# Losing compiler, so override with the script.
# FIXME: It is wrong to rewrite CC.
# But if we don't then we get into trouble of one sort or another.
# A longer-term fix would be to have automake use am__CC in this case,
# and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
CC="$am_aux_dir/compile $CC"
fi
AC_LANG_POP([C])])
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_RUN_LOG(COMMAND)
# -------------------
# Run COMMAND, save the exit status in ac_status, and log it.
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
AC_DEFUN([AM_RUN_LOG],
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
(exit $ac_status); }])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[[\\\"\#\$\&\'\`$am_lf]]*)
AC_MSG_ERROR([unsafe absolute working directory name]);;
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
esac
# Do 'set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
am_has_slept=no
for am_try in 1 2; do
echo "timestamp, slept: $am_has_slept" > conftest.file
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
if test "$[*]" = "X"; then
# -L didn't work.
set X `ls -t "$srcdir/configure" conftest.file`
fi
if test "$[*]" != "X $srcdir/configure conftest.file" \
&& test "$[*]" != "X conftest.file $srcdir/configure"; then
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
alias in your environment])
fi
if test "$[2]" = conftest.file || test $am_try -eq 2; then
break
fi
# Just in case.
sleep 1
am_has_slept=yes
done
test "$[2]" = conftest.file
)
then
# Ok.
:
else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
AC_MSG_RESULT([yes])
# If we didn't sleep, we still need to ensure time stamps of config.status and
# generated files are strictly newer.
am_sleep_pid=
if grep 'slept: no' conftest.file >/dev/null 2>&1; then
( sleep 1 ) &
am_sleep_pid=$!
fi
AC_CONFIG_COMMANDS_PRE(
[AC_MSG_CHECKING([that generated files are newer than configure])
if test -n "$am_sleep_pid"; then
# Hide warnings about reused PIDs.
wait $am_sleep_pid 2>/dev/null
fi
AC_MSG_RESULT([done])])
rm -f conftest.file
])
# Copyright (C) 2009-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_SILENT_RULES([DEFAULT])
# --------------------------
# Enable less verbose build rules; with the default set to DEFAULT
# ("yes" being less verbose, "no" or empty being verbose).
AC_DEFUN([AM_SILENT_RULES],
[AC_ARG_ENABLE([silent-rules], [dnl
AS_HELP_STRING(
[--enable-silent-rules],
[less verbose build output (undo: "make V=1")])
AS_HELP_STRING(
[--disable-silent-rules],
[verbose build output (undo: "make V=0")])dnl
])
case $enable_silent_rules in @%:@ (((
yes) AM_DEFAULT_VERBOSITY=0;;
no) AM_DEFAULT_VERBOSITY=1;;
*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
esac
dnl
dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
dnl do not support nested variable expansions.
dnl See automake bug#9928 and bug#10237.
am_make=${MAKE-make}
AC_CACHE_CHECK([whether $am_make supports nested variables],
[am_cv_make_support_nested_variables],
[if AS_ECHO([['TRUE=$(BAR$(V))
BAR0=false
BAR1=true
V=1
am__doit:
@$(TRUE)
.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
am_cv_make_support_nested_variables=yes
else
am_cv_make_support_nested_variables=no
fi])
if test $am_cv_make_support_nested_variables = yes; then
dnl Using '$V' instead of '$(V)' breaks IRIX make.
AM_V='$(V)'
AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
else
AM_V=$AM_DEFAULT_VERBOSITY
AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
fi
AC_SUBST([AM_V])dnl
AM_SUBST_NOTMAKE([AM_V])dnl
AC_SUBST([AM_DEFAULT_V])dnl
AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
AM_BACKSLASH='\'
AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor 'install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in "make install-strip", and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using 'strip' when the user
# run "make install-strip". However 'strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the 'STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004-2013 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of 'v7', 'ustar', or 'pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
#
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AC_SUBST([AMTAR], ['$${TAR-tar}'])
# We'll loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
m4_if([$1], [v7],
[am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
[m4_case([$1],
[ustar],
[# The POSIX 1988 'ustar' format is defined with fixed-size fields.
# There is notably a 21 bits limit for the UID and the GID. In fact,
# the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
# and bug#13588).
am_max_uid=2097151 # 2^21 - 1
am_max_gid=$am_max_uid
# The $UID and $GID variables are not portable, so we need to resort
# to the POSIX-mandated id(1) utility. Errors in the 'id' calls
# below are definitely unexpected, so allow the users to see them
# (that is, avoid stderr redirection).
am_uid=`id -u || echo unknown`
am_gid=`id -g || echo unknown`
AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
if test $am_uid -le $am_max_uid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi
AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
if test $am_gid -le $am_max_gid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi],
[pax],
[],
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Go ahead even if we have the value already cached. We do so because we
# need to set the values for the 'am__tar' and 'am__untar' variables.
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
for _am_tool in $_am_tools; do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar; do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works.
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar /dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
memcached-1.4.25/murmur3_hash.h 0000664 0001750 0001750 00000001251 12623270407 013224 0000000 0000000 //-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
#ifndef MURMURHASH3_H
#define MURMURHASH3_H
//-----------------------------------------------------------------------------
// Platform-specific functions and macros
#include
#include
//-----------------------------------------------------------------------------
uint32_t MurmurHash3_x86_32(const void *key, size_t length);
//-----------------------------------------------------------------------------
#endif // MURMURHASH3_H
memcached-1.4.25/t/ 0000755 0001750 0001750 00000000000 12623472400 010755 5 0000000 0000000 memcached-1.4.25/t/flush-all.t 0000775 0001750 0001750 00000005772 12623270407 012774 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 26;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $expire;
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
print $sock "flush_all\r\n";
is(scalar <$sock>, "OK\r\n", "did flush_all");
mem_get_is($sock, "foo", undef);
# Test flush_all with zero delay.
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
print $sock "flush_all 0\r\n";
is(scalar <$sock>, "OK\r\n", "did flush_all");
mem_get_is($sock, "foo", undef);
# check that flush_all doesn't blow away items that immediately get set
print $sock "set foo 0 0 3\r\nnew\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo = 'new'");
mem_get_is($sock, "foo", 'new');
# and the other form, specifying a flush_all time...
my $expire = time() + 2;
print $sock "flush_all $expire\r\n";
is(scalar <$sock>, "OK\r\n", "did flush_all in future");
print $sock "set foo 0 0 4\r\n1234\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo = '1234'");
mem_get_is($sock, "foo", '1234');
sleep(3);
mem_get_is($sock, "foo", undef);
print $sock "set foo 0 0 5\r\n12345\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo = '12345'");
mem_get_is($sock, "foo", '12345');
print $sock "flush_all 86400\r\n";
is(scalar <$sock>, "OK\r\n", "did flush_all for far future");
# Check foo still exists.
mem_get_is($sock, "foo", '12345');
print $sock "set foo2 0 0 5\r\n54321\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo2 = '54321'");
mem_get_is($sock, "foo", '12345');
mem_get_is($sock, "foo2", '54321');
# Test -F option which disables flush_all
$server = new_memcached('-F');
$sock = $server->sock;
print $sock "set foo 0 0 7\r\nfooval2\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval2");
print $sock "flush_all\r\n";
is(scalar <$sock>, "CLIENT_ERROR flush_all not allowed\r\n", "flush_all was not allowed");
mem_get_is($sock, "foo", "fooval2");
# Test that disabling CAS makes flush_all less accurate.
# Due to lock ordering issues we can no longer evict items newer than
# oldest_live, so we rely on the CAS counter for an exact cliff. So disabling
# CAS now means all items set in the same second will fail to set.
$server = new_memcached('-C');
$sock = $server->sock;
my $failed_nocas = 0;
# Running this 100,000 times failed the test a handful of times. 50 tries
# should be enough.
for (1..50) {
print $sock "flush_all 0\r\n";
my $foo = scalar <$sock>;
print $sock "set foo 0 0 3\r\nnew\r\n";
$foo = scalar <$sock>;
print $sock "get foo\r\n";
my $line = scalar <$sock>;
if ($line =~ /^VALUE/) {
$line = scalar <$sock>;
$line = scalar <$sock>;
print STDERR "Succeeded!!!\n";
next;
} elsif ($line =~ /^END/) {
$failed_nocas++;
last;
}
}
is($failed_nocas, 1, "failed to set value after flush with no CAS at least once");
memcached-1.4.25/t/issue_50.t 0000644 0001750 0001750 00000000605 12416643766 012536 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 1;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-B binary');
my $sock = $server->sock;
$SIG{ALRM} = sub { die "alarm\n" };
alarm(2);
print $sock "Here's a bunch of garbage that doesn't look like the bin prot.";
my $rv = <$sock>;
ok(1, "Either the above worked and quit, or hung forever.");
memcached-1.4.25/t/unixsocket.t 0000755 0001750 0001750 00000000767 12416643766 013312 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 3;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $filename = "/tmp/memcachetest$$";
my $server = new_memcached("-s $filename");
my $sock = $server->sock;
ok(-S $filename, "creating unix domain socket $filename");
# set foo (and should get it)
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
unlink($filename);
## Just some basic stuff for now...
memcached-1.4.25/t/bogus-commands.t 0000755 0001750 0001750 00000000417 12416643766 014024 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 1;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
print $sock "boguscommand slkdsldkfjsd\r\n";
is(scalar <$sock>, "ERROR\r\n", "got error back");
memcached-1.4.25/t/lru-maintainer.t 0000664 0001750 0001750 00000005264 12623270407 014025 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 224;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-m 6 -o lru_maintainer,lru_crawler');
my $sock = $server->sock;
for (1 .. 10) {
print $sock "set ifoo$_ 0 1 2\r\nok\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
sleep 3;
{
my $stats = mem_stats($sock);
is($stats->{reclaimed}, 10, "expired key automatically reclaimed");
}
my $value = "B"x66560;
print $sock "set canary 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored canary key");
# Now flush the slab class with junk.
for (my $key = 0; $key < 100; $key++) {
if ($key == 30) {
my $stats;
for (0..2) {
# Give the juggler some time to move. some platforms suffer at
# this more than others (solaris amd64?)
$stats = mem_stats($sock, "items");
if ($stats->{"items:31:moves_to_cold"} == 0) { sleep 1; next; }
last;
}
isnt($stats->{"items:31:moves_to_cold"}, 0, "moved some items to cold");
# Fetch the canary once, so it's now marked as active.
mem_get_is($sock, "canary", $value);
}
print $sock "set key$key 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
{
my $stats = mem_stats($sock);
isnt($stats->{evictions}, 0, "some evictions happened");
my $istats = mem_stats($sock, "items");
isnt($stats->{"items:31:number_warm"}, 0, "our canary moved to warm");
}
# Key should've been saved to the WARM_LRU, and still exists.
mem_get_is($sock, "canary", $value);
# Test NOEXP_LRU
$server = new_memcached('-m 2 -o lru_maintainer,lru_crawler,expirezero_does_not_evict');
$sock = $server->sock;
{
my $stats = mem_stats($sock, "settings");
is($stats->{expirezero_does_not_evict}, "yes");
}
print $sock "set canary 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored noexpire canary key");
{
my $stats = mem_stats($sock, "items");
is($stats->{"items:31:number_noexp"}, 1, "one item in noexpire LRU");
is($stats->{"items:31:number_hot"}, 0, "item did not go into hot LRU");
}
# *Not* fetching the key, and flushing the slab class with junk.
# Using keys with actual TTL's here.
for (my $key = 0; $key < 100; $key++) {
print $sock "set key$key 0 3600 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
{
my $stats = mem_stats($sock, "items");
isnt($stats->{evictions}, 0, "some evictions happened");
isnt($stats->{number_hot}, 0, "nonzero exptime items went into hot LRU");
}
# Canary should still exist, even unfetched, because it's protected by
# noexpire.
mem_get_is($sock, "canary", $value);
memcached-1.4.25/t/maxconns.t 0000755 0001750 0001750 00000001112 12416643766 012725 0000000 0000000 #!/usr/bin/perl
# NOTE: This test never worked. Memcached would ignore maxconns requests lower
# than the current ulimit. Test needs to be updated.
use strict;
use warnings;
use Test::More tests => 11;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
# start up a server with 10 maximum connections
my $server = new_memcached('-c 100');
my $sock = $server->sock;
my @sockets;
ok(defined($sock), 'Connection 0');
push (@sockets, $sock);
foreach my $conn (1..10) {
$sock = $server->new_sock;
ok(defined($sock), "Made connection $conn");
push(@sockets, $sock);
}
memcached-1.4.25/t/issue_104.t 0000755 0001750 0001750 00000001145 12416643766 012621 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 6;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# first get should miss
print $sock "get foo\r\n";
is(scalar <$sock>, "END\r\n", "get foo");
# Now set and get (should hit)
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
my $stats = mem_stats($sock);
is($stats->{cmd_get}, 2, "Should have 2 get requests");
is($stats->{get_hits}, 1, "Should have 1 hit");
is($stats->{get_misses}, 1, "Should have 1 miss");
memcached-1.4.25/t/issue_22.t 0000644 0001750 0001750 00000002166 12416643766 012541 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 84;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached("-m 3");
my $sock = $server->sock;
my $value = "B"x66560;
my $key = 0;
for ($key = 0; $key < 40; $key++) {
print $sock "set key$key 0 0 66560\r\n$value\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key$key");
}
my $first_stats = mem_stats($sock, "items");
my $first_evicted = $first_stats->{"items:31:evicted"};
# I get 1 eviction on a 32 bit binary, but 4 on a 64 binary..
# Just check that I have evictions...
isnt ($first_evicted, "0", "check evicted");
print $sock "stats reset\r\n";
is (scalar <$sock>, "RESET\r\n", "Stats reset");
my $second_stats = mem_stats($sock, "items");
my $second_evicted = $second_stats->{"items:31:evicted"};
is ($second_evicted, "0", "check evicted");
for ($key = 40; $key < 80; $key++) {
print $sock "set key$key 0 0 66560\r\n$value\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key$key");
}
my $last_stats = mem_stats($sock, "items");
my $last_evicted = $last_stats->{"items:31:evicted"};
is ($last_evicted, "40", "check evicted");
memcached-1.4.25/t/expirations.t 0000755 0001750 0001750 00000003325 12416643766 013454 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 15;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $expire;
sub wait_for_early_second {
my $have_hires = eval "use Time::HiRes (); 1";
if ($have_hires) {
my $tsh = Time::HiRes::time();
my $ts = int($tsh);
return if ($tsh - $ts) < 0.5;
}
my $ts = int(time());
while (1) {
my $t = int(time());
return if $t != $ts;
select undef, undef, undef, 0.10; # 1/10th of a second sleeps until time changes.
}
}
wait_for_early_second();
print $sock "set foo 0 1 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
sleep(1.5);
mem_get_is($sock, "foo", undef);
$expire = time() - 1;
print $sock "set foo 0 $expire 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", undef, "already expired");
$expire = time() + 1;
print $sock "set foo 0 $expire 6\r\nfoov+1\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "foov+1");
sleep(2.2);
mem_get_is($sock, "foo", undef, "now expired");
$expire = time() - 20;
print $sock "set boo 0 $expire 6\r\nbooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored boo");
mem_get_is($sock, "boo", undef, "now expired");
print $sock "add add 0 2 6\r\naddval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored add");
mem_get_is($sock, "add", "addval");
# second add fails
print $sock "add add 0 2 7\r\naddval2\r\n";
is(scalar <$sock>, "NOT_STORED\r\n", "add failure");
sleep(2.3);
print $sock "add add 0 2 7\r\naddval3\r\n";
is(scalar <$sock>, "STORED\r\n", "stored add again");
mem_get_is($sock, "add", "addval3");
memcached-1.4.25/t/issue_163.t 0000644 0001750 0001750 00000002053 12416643766 012622 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 7;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $value1 = "A"x66560;
my $value2 = "B"x66570;
print $sock "set key 0 1 66560\r\n$value1\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $stats = mem_stats($sock, "slabs");
my $requested = $stats->{"31:mem_requested"};
isnt ($requested, "0", "We should have requested some memory");
sleep(3);
print $sock "set key 0 0 66570\r\n$value2\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $stats = mem_stats($sock, "items");
my $reclaimed = $stats->{"items:31:reclaimed"};
is ($reclaimed, "1", "Objects should be reclaimed");
print $sock "delete key\r\n";
is (scalar <$sock>, "DELETED\r\n", "deleted key");
print $sock "set key 0 0 66560\r\n$value1\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $stats = mem_stats($sock, "slabs");
my $requested2 = $stats->{"31:mem_requested"};
is ($requested2, $requested, "we've not allocated and freed the same amont");
memcached-1.4.25/t/slabs-reassign2.t 0000664 0001750 0001750 00000005647 12623472400 014077 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 11;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-m 60 -o slab_reassign,slab_automove=2,lru_crawler,lru_maintainer');
my $sock = $server->sock;
my $value = "B"x11000;
my $keycount = 5000;
my $res;
for (1 .. $keycount) {
print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n";
}
my $todelete = 0;
{
my $stats = mem_stats($sock);
cmp_ok($stats->{curr_items}, '>', 4000, "stored at least 4000 11k items");
$todelete = $stats->{curr_items} / 2;
# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') {
# print STDERR "$_: ", $stats->{$_}, "\n";
# }
}
# Make room in old class so rescues can happen when we switch slab classes.
for (1 .. $todelete) {
print $sock "delete nfoo$_ noreply\r\n";
}
$value = "B"x7000;
for (1 .. $keycount) {
print $sock "set ifoo$_ 0 0 7000 noreply\r\n$value\r\n";
}
my $missing = 0;
my $hits = 0;
for (1 .. $keycount) {
print $sock "get ifoo$_\r\n";
my $body = scalar(<$sock>);
my $expected = "VALUE ifoo$_ 0 7000\r\n$value\r\nEND\r\n";
if ($body =~ /^END/) {
$missing++;
} else {
$body .= scalar(<$sock>) . scalar(<$sock>);
if ($body ne $expected) {
print STDERR "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n";
} else {
$hits++;
}
}
}
#print STDERR "HITS: $hits, MISSES: $missing\n";
{
my $stats = mem_stats($sock);
cmp_ok($stats->{evictions}, '<', 2000, 'evictions were less than 2000');
# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') {
# print STDERR "$_: ", $stats->{$_}, "\n";
# }
}
cmp_ok($hits, '>', 4000, 'were able to fetch back 2/3rds of 8k keys');
my $stats_done = mem_stats($sock);
cmp_ok($stats_done->{slab_reassign_rescues}, '>', 0, 'some reassign rescues happened');
cmp_ok($stats_done->{slab_reassign_evictions_nomem}, '>', 0, 'some reassign evictions happened');
print $sock "flush_all\r\n";
is(scalar <$sock>, "OK\r\n", "did flush_all");
my $tries;
for ($tries = 20; $tries > 0; $tries--) {
sleep 1;
my $stats = mem_stats($sock);
if ($stats->{slab_global_page_pool} == 56) {
last;
}
}
cmp_ok($tries, '>', 0, 'reclaimed 61 pages before timeout');
{
my $stats = mem_stats($sock, "slabs");
is($stats->{total_malloced}, 62914560, "total_malloced is what we expect");
}
# Set into an entirely new class. Overload a bit to try to cause problems.
$value = "B"x4096;
for (1 .. $keycount * 4) {
print $sock "set jfoo$_ 0 0 4096 noreply\r\n$value\r\n";
}
{
my $stats = mem_stats($sock);
cmp_ok($stats->{curr_items}, '>', 10000, "stored at least 10000 4k items");
is($stats->{slab_global_page_pool}, 0, "drained the global page pool");
}
{
my $stats = mem_stats($sock, "slabs");
is($stats->{total_malloced}, 62914560, "total_malloced is same after re-assignment");
}
memcached-1.4.25/t/issue_61.t 0000644 0001750 0001750 00000001415 12416643766 012540 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 7;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached("-R 1");
my $sock = $server->sock;
print $sock "set foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\nset foobar 0 0 5\r\nBubba\r\n";
is (scalar <$sock>, "STORED\r\n", "stored foobar");
is (scalar <$sock>, "STORED\r\n", "stored foobar");
is (scalar <$sock>, "STORED\r\n", "stored foobar");
is (scalar <$sock>, "STORED\r\n", "stored foobar");
is (scalar <$sock>, "STORED\r\n", "stored foobar");
is (scalar <$sock>, "STORED\r\n", "stored foobar");
my $stats = mem_stats($sock);
is ($stats->{"conn_yields"}, "5", "Got a decent number of yields");
memcached-1.4.25/t/evictions.t 0000644 0001750 0001750 00000001551 12416643766 013106 0000000 0000000 #!/usr/bin/perl
# Test the 'stats items' evictions counters.
use strict;
use Test::More tests => 92;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached("-m 3");
my $sock = $server->sock;
my $value = "B"x66560;
my $key = 0;
# These aren't set to expire.
for ($key = 0; $key < 40; $key++) {
print $sock "set key$key 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
# These ones would expire in 600 seconds.
for ($key = 0; $key < 50; $key++) {
print $sock "set key$key 0 600 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
my $stats = mem_stats($sock, "items");
my $evicted = $stats->{"items:31:evicted"};
isnt($evicted, "0", "check evicted");
my $evicted_nonzero = $stats->{"items:31:evicted_nonzero"};
isnt($evicted_nonzero, "0", "check evicted_nonzero");
memcached-1.4.25/t/whitespace.t 0000775 0001750 0001750 00000002442 12623270407 013230 0000000 0000000 #!/usr/bin/perl
use strict;
use FindBin qw($Bin);
our @files;
BEGIN {
chdir "$Bin/.." or die;
my @exempted = qw(Makefile.am ChangeLog doc/Makefile.am README README.md);
push(@exempted, glob("doc/*.xml"));
push(@exempted, glob("doc/xml2rfc/*.xsl"));
push(@exempted, glob("m4/*backport*m4"));
push(@exempted, glob("*.orig"));
push(@exempted, glob(".*.swp"));
my %exempted_hash = map { $_ => 1 } @exempted;
my @stuff = split /\0/, `git ls-files -z -c -m -o --exclude-standard`;
@files = grep { ! $exempted_hash{$_} } @stuff;
# We won't find any files if git isn't installed. If git isn't
# installed, they're probably not doing any useful development, or
# at the very least am will clean up whitespace when we receive
# their patch.
unless (@files) {
use Test::More;
plan skip_all => "Skipping tests probably because you don't have git.";
exit 0;
}
}
use Test::More tests => scalar(@files);
foreach my $f (@files) {
open(my $fh, $f) or die "Cannot open file $f: $!";
my $before = do { local $/; <$fh>; };
close ($fh);
my $after = $before;
$after =~ s/\t/ /g;
$after =~ s/ +$//mg;
$after .= "\n" unless $after =~ /\n$/;
ok ($after eq $before, "$f (see devtools/clean-whitespace.pl)");
}
memcached-1.4.25/t/incrdecr.t 0000755 0001750 0001750 00000004155 12416643766 012702 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 23;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# Bug 21
print $sock "set bug21 0 0 19\r\n9223372036854775807\r\n";
is(scalar <$sock>, "STORED\r\n", "stored text");
print $sock "incr bug21 1\r\n";
is(scalar <$sock>, "9223372036854775808\r\n", "bug21 incr 1");
print $sock "incr bug21 1\r\n";
is(scalar <$sock>, "9223372036854775809\r\n", "bug21 incr 2");
print $sock "decr bug21 1\r\n";
is(scalar <$sock>, "9223372036854775808\r\n", "bug21 decr");
print $sock "set num 0 0 1\r\n1\r\n";
is(scalar <$sock>, "STORED\r\n", "stored num");
mem_get_is($sock, "num", 1, "stored 1");
print $sock "incr num 1\r\n";
is(scalar <$sock>, "2\r\n", "+ 1 = 2");
mem_get_is($sock, "num", 2);
print $sock "incr num 8\r\n";
is(scalar <$sock>, "10\r\n", "+ 8 = 10");
mem_get_is($sock, "num", 10);
print $sock "decr num 1\r\n";
is(scalar <$sock>, "9\r\n", "- 1 = 9");
print $sock "decr num 9\r\n";
is(scalar <$sock>, "0\r\n", "- 9 = 0");
print $sock "decr num 5\r\n";
is(scalar <$sock>, "0\r\n", "- 5 = 0");
printf $sock "set num 0 0 10\r\n4294967296\r\n";
is(scalar <$sock>, "STORED\r\n", "stored 2**32");
print $sock "incr num 1\r\n";
is(scalar <$sock>, "4294967297\r\n", "4294967296 + 1 = 4294967297");
printf $sock "set num 0 0 %d\r\n18446744073709551615\r\n", length("18446744073709551615");
is(scalar <$sock>, "STORED\r\n", "stored 2**64-1");
print $sock "incr num 1\r\n";
is(scalar <$sock>, "0\r\n", "(2**64 - 1) + 1 = 0");
print $sock "decr bogus 5\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "can't decr bogus key");
print $sock "decr incr 5\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "can't incr bogus key");
print $sock "set bigincr 0 0 1\r\n0\r\n";
is(scalar <$sock>, "STORED\r\n", "stored bigincr");
print $sock "incr bigincr 18446744073709551610\r\n";
is(scalar <$sock>, "18446744073709551610\r\n");
print $sock "set text 0 0 2\r\nhi\r\n";
is(scalar <$sock>, "STORED\r\n", "stored hi");
print $sock "incr text 1\r\n";
is(scalar <$sock>,
"CLIENT_ERROR cannot increment or decrement non-numeric value\r\n",
"hi - 1 = 0");
memcached-1.4.25/t/binary-sasl.t 0000775 0001750 0001750 00000041726 12623270407 013330 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Cwd;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $supports_sasl = supports_sasl();
use Test::More;
if (supports_sasl()) {
if ($ENV{'RUN_SASL_TESTS'}) {
plan tests => 33;
} else {
plan skip_all => 'Skipping SASL tests';
exit 0;
}
} else {
plan tests => 1;
eval {
my $server = new_memcached("-S");
};
ok($@, "Died with illegal -S args when SASL is not supported.");
exit 0;
}
eval {
my $server = new_memcached("-S -B auto");
};
ok($@, "SASL shouldn't be used with protocol auto negotiate");
eval {
my $server = new_memcached("-S -B ascii");
};
ok($@, "SASL isn't implemented in the ascii protocol");
eval {
my $server = new_memcached("-S -B binary -B ascii");
};
ok($@, "SASL isn't implemented in the ascii protocol");
# Based almost 100% off testClient.py which is:
# Copyright (c) 2007 Dustin Sallings
# Command constants
use constant CMD_GET => 0x00;
use constant CMD_SET => 0x01;
use constant CMD_ADD => 0x02;
use constant CMD_REPLACE => 0x03;
use constant CMD_DELETE => 0x04;
use constant CMD_INCR => 0x05;
use constant CMD_DECR => 0x06;
use constant CMD_QUIT => 0x07;
use constant CMD_FLUSH => 0x08;
use constant CMD_GETQ => 0x09;
use constant CMD_NOOP => 0x0A;
use constant CMD_VERSION => 0x0B;
use constant CMD_GETK => 0x0C;
use constant CMD_GETKQ => 0x0D;
use constant CMD_APPEND => 0x0E;
use constant CMD_PREPEND => 0x0F;
use constant CMD_STAT => 0x10;
use constant CMD_SETQ => 0x11;
use constant CMD_ADDQ => 0x12;
use constant CMD_REPLACEQ => 0x13;
use constant CMD_DELETEQ => 0x14;
use constant CMD_INCREMENTQ => 0x15;
use constant CMD_DECREMENTQ => 0x16;
use constant CMD_QUITQ => 0x17;
use constant CMD_FLUSHQ => 0x18;
use constant CMD_APPENDQ => 0x19;
use constant CMD_PREPENDQ => 0x1A;
use constant CMD_SASL_LIST_MECHS => 0x20;
use constant CMD_SASL_AUTH => 0x21;
use constant CMD_SASL_STEP => 0x22;
use constant ERR_AUTH_ERROR => 0x20;
# REQ and RES formats are divided even though they currently share
# the same format, since they _could_ differ in the future.
use constant REQ_PKT_FMT => "CCnCCnNNNN";
use constant RES_PKT_FMT => "CCnCCnNNNN";
use constant INCRDECR_PKT_FMT => "NNNNN";
use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT));
use constant REQ_MAGIC => 0x80;
use constant RES_MAGIC => 0x81;
my $pwd=getcwd;
$ENV{'SASL_CONF_PATH'} = "$pwd/t/sasl";
my $server = new_memcached('-B binary -S ');
my $mc = MC::Client->new;
my $check = sub {
my ($key, $orig_val) = @_;
my ($status, $val, $cas) = $mc->get($key);
if ($val =~ /^\d+$/) {
cmp_ok($val,'==', $orig_val, "$val = $orig_val");
}
else {
cmp_ok($val, 'eq', $orig_val, "$val = $orig_val");
}
};
my $set = sub {
my ($key, $orig_value, $exp) = @_;
$exp = defined $exp ? $exp : 0;
my ($status, $rv)= $mc->set($key, $orig_value, $exp);
$check->($key, $orig_value);
};
my $empty = sub {
my $key = shift;
my ($status,$rv) =()= eval { $mc->get($key) };
#if ($status == ERR_AUTH_ERROR) {
# ok($@->auth_error, "Not authorized to connect");
#}
#else {
# ok($@->not_found, "We got a not found error when we expected one");
#}
if ($status) {
ok($@->not_found, "We got a not found error when we expected one");
}
};
my $delete = sub {
my ($key, $when) = @_;
$mc->delete($key, $when);
$empty->($key);
};
# BEGIN THE TEST
ok($server, "started the server");
my $v = $mc->version;
ok(defined $v && length($v), "Proper version: $v");
# list mechs
my $mechs= $mc->list_mechs();
Test::More::cmp_ok($mechs, 'eq', 'CRAM-MD5 PLAIN', "list_mechs $mechs");
# this should fail, not authenticated
{
my ($status, $val)= $mc->set('x', "somevalue");
ok($status, "this fails to authenticate");
cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches");
}
$empty->('x');
{
my $mc = MC::Client->new;
my ($status, $val) = $mc->delete('x');
ok($status, "this fails to authenticate");
cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches");
}
$empty->('x');
{
my $mc = MC::Client->new;
my ($status, $val)= $mc->set('x', "somevalue");
ok($status, "this fails to authenticate");
cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches");
}
$empty->('x');
{
my $mc = MC::Client->new;
my ($status, $val)= $mc->flush('x');
ok($status, "this fails to authenticate");
cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches");
}
$empty->('x');
# Build the auth DB for testing.
my $sasldb = '/tmp/test-memcached.sasldb';
unlink $sasldb;
my $saslpasswd_path;
for my $dir (split(/:/, $ENV{PATH}),
"/usr/bin",
"/usr/sbin",
"/usr/local/bin",
"/usr/local/sbin",
) {
my $exe = $dir . '/saslpasswd2';
if (-x $exe) {
$saslpasswd_path = $exe;
last;
}
}
system("echo testpass | $saslpasswd_path -a memcached -c -p testuser");
$mc = MC::Client->new;
# Attempt a bad auth mech.
is ($mc->authenticate('testuser', 'testpass', "X" x 40), 0x4, "bad mech");
# Attempt bad authentication.
is ($mc->authenticate('testuser', 'wrongpassword'), 0x20, "bad auth");
# Now try good authentication and make the tests work.
is ($mc->authenticate('testuser', 'testpass'), 0, "authenticated");
# these should work
{
my ($status, $val)= $mc->set('x', "somevalue");
ok(! $status);
}
$check->('x','somevalue');
{
my ($status, $val)= $mc->delete('x');
ok(! $status);
}
$empty->('x');
{
my ($status, $val)= $mc->set('x', "somevalue");
ok(! $status);
}
$check->('x','somevalue');
{
my ($status, $val)= $mc->flush('x');
ok(! $status);
}
$empty->('x');
{
my $mc = MC::Client->new;
# Attempt bad authentication.
is ($mc->authenticate('testuser', 'wrongpassword'), 0x20, "bad auth");
# This should fail because $mc is not authenticated
my ($status, $val)= $mc->set('x', "somevalue");
ok($status, "this fails to authenticate");
cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches");
}
$empty->('x', 'somevalue');
{
my $mc = MC::Client->new;
# Attempt bad authentication.
is ($mc->authenticate('testuser', 'wrongpassword'), 0x20, "bad auth");
# Mix an authenticated connection and an unauthenticated connection to
# confirm c->authenticated is not shared among connections
my $mc2 = MC::Client->new;
is ($mc2->authenticate('testuser', 'testpass'), 0, "authenticated");
my ($status, $val)= $mc2->set('x', "somevalue");
ok(! $status);
# This should fail because $mc is not authenticated
($status, $val)= $mc->set('x', "somevalue");
ok($status, "this fails to authenticate");
cmp_ok($status,'==',ERR_AUTH_ERROR, "error code matches");
}
# check the SASL stats, make sure they track things correctly
# note: the enabled or not is presence checked in stats.t
# while authenticated, get current counter
#
# My initial approach was going to be to get current counts, reauthenticate
# and fail, followed by a reauth successfully so I'd know what happened.
# Reauthentication is currently unsupported, so it doesn't work that way at the
# moment. Adding tests may break this.
{
my %stats = $mc->stats('');
is ($stats{'auth_cmds'}, 5, "auth commands counted");
is ($stats{'auth_errors'}, 3, "auth errors correct");
}
# Along with the assertion added to the code to verify we're staying
# within bounds when we do a stats detail dump (detail turned on at
# the top).
# my %stats = $mc->stats('detail dump');
# ######################################################################
# Test ends around here.
# ######################################################################
package MC::Client;
use strict;
use warnings;
use fields qw(socket);
use IO::Socket::INET;
use constant ERR_AUTH_ERROR => 0x20;
sub new {
my $self = shift;
my ($s) = @_;
$s = $server unless defined $s;
my $sock = $s->sock;
$self = fields::new($self);
$self->{socket} = $sock;
return $self;
}
sub authenticate {
my ($self, $user, $pass, $mech)= @_;
$mech ||= 'PLAIN';
my $buf = sprintf("%c%s%c%s", 0, $user, 0, $pass);
my ($status, $rv, undef) = $self->_do_command(::CMD_SASL_AUTH, $mech, $buf, '');
return $status;
}
sub list_mechs {
my ($self)= @_;
my ($status, $rv, undef) = $self->_do_command(::CMD_SASL_LIST_MECHS, '', '', '');
return join(" ", sort(split(/\s+/, $rv)));
}
sub build_command {
my $self = shift;
die "Not enough args to send_command" unless @_ >= 4;
my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
$extra_header = '' unless defined $extra_header;
my $keylen = length($key);
my $vallen = length($val);
my $extralen = length($extra_header);
my $datatype = 0; # field for future use
my $reserved = 0; # field for future use
my $totallen = $keylen + $vallen + $extralen;
my $ident_hi = 0;
my $ident_lo = 0;
if ($cas) {
$ident_hi = int($cas / 2 ** 32);
$ident_lo = int($cas % 2 ** 32);
}
my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen,
$datatype, $reserved, $totallen, $opaque, $ident_hi,
$ident_lo);
my $full_msg = $msg . $extra_header . $key . $val;
return $full_msg;
}
sub send_command {
my $self = shift;
die "Not enough args to send_command" unless @_ >= 4;
my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
my $full_msg = $self->build_command($cmd, $key, $val, $opaque, $extra_header, $cas);
my $sent = $self->{socket}->send($full_msg);
die("Send failed: $!") unless $sent;
if($sent != length($full_msg)) {
die("only sent $sent of " . length($full_msg) . " bytes");
}
}
sub flush_socket {
my $self = shift;
$self->{socket}->flush;
}
# Send a silent command and ensure it doesn't respond.
sub send_silent {
my $self = shift;
die "Not enough args to send_silent" unless @_ >= 4;
my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
$self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
$self->send_command(::CMD_NOOP, '', '', $opaque + 1);
my ($ropaque, $status, $data) = $self->_handle_single_response;
Test::More::is($ropaque, $opaque + 1);
}
sub silent_mutation {
my $self = shift;
my ($cmd, $key, $value) = @_;
$empty->($key);
my $extra = pack "NN", 82, 0;
$mc->send_silent($cmd, $key, $value, 7278552, $extra, 0);
$check->($key, $value);
}
sub _handle_single_response {
my $self = shift;
my $myopaque = shift;
$self->{socket}->recv(my $response, ::MIN_RECV_BYTES);
my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining,
$opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $response);
return ($opaque, '', '', '', 0) if not defined $remaining;
return ($opaque, '', '', '', 0) if ($remaining == 0);
# fetch the value
my $rv="";
while($remaining - length($rv) > 0) {
$self->{socket}->recv(my $buf, $remaining - length($rv));
$rv .= $buf;
}
if(length($rv) != $remaining) {
my $found = length($rv);
die("Expected $remaining bytes, got $found");
}
my $cas = ($ident_hi * 2 ** 32) + $ident_lo;
#if ($status) {
#die MC::Error->new($status, $rv);
#}
return ($opaque, $status, $rv, $cas, $keylen);
}
sub _do_command {
my $self = shift;
die unless @_ >= 3;
my ($cmd, $key, $val, $extra_header, $cas) = @_;
$extra_header = '' unless defined $extra_header;
my $opaque = int(rand(2**32));
$self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
my (undef, $status, $rv, $rcas) = $self->_handle_single_response($opaque);
return ($status, $rv, $rcas);
}
sub _incrdecr_header {
my $self = shift;
my ($amt, $init, $exp) = @_;
my $amt_hi = int($amt / 2 ** 32);
my $amt_lo = int($amt % 2 ** 32);
my $init_hi = int($init / 2 ** 32);
my $init_lo = int($init % 2 ** 32);
my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi,
$init_lo, $exp);
return $extra_header;
}
sub _incrdecr {
my $self = shift;
my ($cmd, $key, $amt, $init, $exp) = @_;
my ($status, $data, undef) = $self->_do_command($cmd, $key, '',
$self->_incrdecr_header($amt, $init, $exp));
my $header = substr $data, 0, 8, '';
my ($resp_hi, $resp_lo) = unpack "NN", $header;
my $resp = ($resp_hi * 2 ** 32) + $resp_lo;
return $resp;
}
sub silent_incrdecr {
my $self = shift;
my ($cmd, $key, $amt, $init, $exp) = @_;
my $opaque = 8275753;
$mc->send_silent($cmd, $key, '', $opaque,
$mc->_incrdecr_header($amt, $init, $exp));
}
sub stats {
my $self = shift;
my $key = shift;
my $cas = 0;
my $opaque = int(rand(2**32));
$self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas);
my %rv = ();
my $found_key = '';
my $found_val = '';
my $status= 0;
do {
my ($op, $status, $data, $cas, $keylen) = $self->_handle_single_response($opaque);
if ($keylen > 0) {
$found_key = substr($data, 0, $keylen);
$found_val = substr($data, $keylen);
$rv{$found_key} = $found_val;
} else {
$found_key = '';
}
} while($found_key ne '');
return %rv;
}
sub get {
my $self = shift;
my $key = shift;
my ($status, $rv, $cas) = $self->_do_command(::CMD_GET, $key, '', '');
my $header = substr $rv, 0, 4, '';
my $flags = unpack("N", $header);
return ($status, $rv);
}
sub get_multi {
my $self = shift;
my @keys = @_;
for (my $i = 0; $i < @keys; $i++) {
$self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0);
}
my $terminal = @keys + 10;
$self->send_command(::CMD_NOOP, '', '', $terminal);
my %return;
my $status = 0;
while (1) {
my ($opaque, $status, $data) = $self->_handle_single_response;
last if $opaque == $terminal;
my $header = substr $data, 0, 4, '';
my $flags = unpack("N", $header);
$return{$keys[$opaque]} = [$flags, $data];
}
return %return if wantarray;
return \%return;
}
sub version {
my $self = shift;
return $self->_do_command(::CMD_VERSION, '', '');
}
sub flush {
my $self = shift;
return $self->_do_command(::CMD_FLUSH, '', '');
}
sub add {
my $self = shift;
my ($key, $val, $flags, $expire) = @_;
my $extra_header = pack "NN", $flags, $expire;
my $cas = 0;
return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas);
}
sub set {
my $self = shift;
my $flags = 0;
my $cas = 0;
my ($key, $val, $expire) = @_;
$expire = defined $expire ? $expire : 0;
my $extra_header = pack "NN", $flags, $expire;
return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas);
}
sub _append_prepend {
my $self = shift;
my ($cmd, $key, $val, $cas) = @_;
return $self->_do_command($cmd, $key, $val, '', $cas);
}
sub replace {
my $self = shift;
my ($key, $val, $flags, $expire) = @_;
my $extra_header = pack "NN", $flags, $expire;
my $cas = 0;
return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas);
}
sub delete {
my $self = shift;
my ($key) = @_;
return $self->_do_command(::CMD_DELETE, $key, '');
}
sub incr {
my $self = shift;
my ($key, $amt, $init, $exp) = @_;
$amt = 1 unless defined $amt;
$init = 0 unless defined $init;
$exp = 0 unless defined $exp;
return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp);
}
sub decr {
my $self = shift;
my ($key, $amt, $init, $exp) = @_;
$amt = 1 unless defined $amt;
$init = 0 unless defined $init;
$exp = 0 unless defined $exp;
return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp);
}
sub noop {
my $self = shift;
return $self->_do_command(::CMD_NOOP, '', '');
}
package MC::Error;
use strict;
use warnings;
use constant ERR_UNKNOWN_CMD => 0x81;
use constant ERR_NOT_FOUND => 0x1;
use constant ERR_EXISTS => 0x2;
use constant ERR_TOO_BIG => 0x3;
use constant ERR_EINVAL => 0x4;
use constant ERR_NOT_STORED => 0x5;
use constant ERR_DELTA_BADVAL => 0x6;
use constant ERR_AUTH_ERROR => 0x20;
use overload '""' => sub {
my $self = shift;
return "Memcache Error ($self->[0]): $self->[1]";
};
sub new {
my $class = shift;
my $error = [@_];
my $self = bless $error, (ref $class || $class);
return $self;
}
sub not_found {
my $self = shift;
return $self->[0] == ERR_NOT_FOUND;
}
sub exists {
my $self = shift;
return $self->[0] == ERR_EXISTS;
}
sub too_big {
my $self = shift;
return $self->[0] == ERR_TOO_BIG;
}
sub delta_badval {
my $self = shift;
return $self->[0] == ERR_DELTA_BADVAL;
}
sub auth_error {
my $self = shift;
return $self->[0] == ERR_AUTH_ERROR;
}
unlink $sasldb;
# vim: filetype=perl
memcached-1.4.25/t/stats-detail.t 0000644 0001750 0001750 00000004105 12416643766 013477 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 24;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $expire;
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "END\r\n", "verified empty stats at start");
print $sock "stats detail on\r\n";
is(scalar <$sock>, "OK\r\n", "detail collection turned on");
print $sock "set foo:123 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "PREFIX foo get 0 hit 0 set 1 del 0\r\n", "details after set");
is(scalar <$sock>, "END\r\n", "end of details");
mem_get_is($sock, "foo:123", "fooval");
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 1 del 0\r\n", "details after get with hit");
is(scalar <$sock>, "END\r\n", "end of details");
mem_get_is($sock, "foo:124", undef);
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "PREFIX foo get 2 hit 1 set 1 del 0\r\n", "details after get without hit");
is(scalar <$sock>, "END\r\n", "end of details");
print $sock "delete foo:125\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "sent delete command");
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "PREFIX foo get 2 hit 1 set 1 del 1\r\n", "details after delete");
is(scalar <$sock>, "END\r\n", "end of details");
print $sock "stats reset\r\n";
is(scalar <$sock>, "RESET\r\n", "stats cleared");
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "END\r\n", "empty stats after clear");
mem_get_is($sock, "foo:123", "fooval");
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 0 del 0\r\n", "details after clear and get");
is(scalar <$sock>, "END\r\n", "end of details");
print $sock "stats detail off\r\n";
is(scalar <$sock>, "OK\r\n", "detail collection turned off");
mem_get_is($sock, "foo:124", undef);
mem_get_is($sock, "foo:123", "fooval");
print $sock "stats detail dump\r\n";
is(scalar <$sock>, "PREFIX foo get 1 hit 1 set 0 del 0\r\n", "details after stats turned off");
is(scalar <$sock>, "END\r\n", "end of details");
memcached-1.4.25/t/cas.t 0000644 0001750 0001750 00000011135 12416643766 011650 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 43;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $sock2 = $server->new_sock;
my @result;
my @result2;
ok($sock != $sock2, "have two different connections open");
sub check_args {
my ($line, $name) = @_;
my $svr = new_memcached();
my $s = $svr->sock;
print $s $line;
is(scalar <$s>, "CLIENT_ERROR bad command line format\r\n", $name);
undef $svr;
}
check_args "cas bad blah 0 0 0\r\n\r\n", "bad flags";
check_args "cas bad 0 blah 0 0\r\n\r\n", "bad exp";
check_args "cas bad 0 0 blah 0\r\n\r\n", "bad cas";
check_args "cas bad 0 0 0 blah\r\n\r\n", "bad size";
# gets foo (should not exist)
print $sock "gets foo\r\n";
is(scalar <$sock>, "END\r\n", "gets failed");
# set foo
print $sock "set foo 0 0 6\r\nbarval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored barval");
# gets foo and verify identifier exists
@result = mem_gets($sock, "foo");
mem_gets_is($sock,$result[0],"foo","barval");
# cas fail
print $sock "cas foo 0 0 6 123\r\nbarva2\r\n";
is(scalar <$sock>, "EXISTS\r\n", "cas failed for foo");
# gets foo - success
@result = mem_gets($sock, "foo");
mem_gets_is($sock,$result[0],"foo","barval");
# cas success
print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n";
is(scalar <$sock>, "STORED\r\n", "cas success, set foo");
# cas failure (reusing the same key)
print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n";
is(scalar <$sock>, "EXISTS\r\n", "reusing a CAS ID");
# delete foo
print $sock "delete foo\r\n";
is(scalar <$sock>, "DELETED\r\n", "deleted foo");
# cas missing
print $sock "cas foo 0 0 6 $result[0]\r\nbarva2\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "cas failed, foo does not exist");
# cas empty
print $sock "cas foo 0 0 6 \r\nbarva2\r\n";
is(scalar <$sock>, "ERROR\r\n", "cas empty, throw error");
# cant parse barval2\r\n
is(scalar <$sock>, "ERROR\r\n", "error out on barval2 parsing");
# set foo1
print $sock "set foo1 0 0 1\r\n1\r\n";
is(scalar <$sock>, "STORED\r\n", "set foo1");
# set foo2
print $sock "set foo2 0 0 1\r\n2\r\n";
is(scalar <$sock>, "STORED\r\n", "set foo2");
# gets foo1 check
print $sock "gets foo1\r\n";
ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "gets foo1 regexp success");
my $foo1_cas = $1;
is(scalar <$sock>, "1\r\n","gets foo1 data is 1");
is(scalar <$sock>, "END\r\n","gets foo1 END");
# gets foo2 check
print $sock "gets foo2\r\n";
ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/,"gets foo2 regexp success");
my $foo2_cas = $1;
is(scalar <$sock>, "2\r\n","gets foo2 data is 2");
is(scalar <$sock>, "END\r\n","gets foo2 END");
# validate foo1 != foo2
ok($foo1_cas != $foo2_cas,"foo1 != foo2 single-gets success");
# multi-gets
print $sock "gets foo1 foo2\r\n";
ok(scalar <$sock> =~ /VALUE foo1 0 1 (\d+)\r\n/, "validating first set of data is foo1");
$foo1_cas = $1;
is(scalar <$sock>, "1\r\n", "validating foo1 set of data is 1");
ok(scalar <$sock> =~ /VALUE foo2 0 1 (\d+)\r\n/, "validating second set of data is foo2");
$foo2_cas = $1;
is(scalar <$sock>, "2\r\n", "validating foo2 set of data is 2");
is(scalar <$sock>, "END\r\n","validating foo1,foo2 gets is over - END");
# validate foo1 != foo2
ok($foo1_cas != $foo2_cas, "foo1 != foo2 multi-gets success");
### simulate race condition with cas
# gets foo1 - success
@result = mem_gets($sock, "foo1");
ok($result[0] != "", "sock - gets foo1 is not empty");
# gets foo2 - success
@result2 = mem_gets($sock2, "foo1");
ok($result2[0] != "","sock2 - gets foo1 is not empty");
print $sock "cas foo1 0 0 6 $result[0]\r\nbarva2\r\n";
print $sock2 "cas foo1 0 0 5 $result2[0]\r\napple\r\n";
my $res1 = <$sock>;
my $res2 = <$sock2>;
ok( ( $res1 eq "STORED\r\n" && $res2 eq "EXISTS\r\n") ||
( $res1 eq "EXISTS\r\n" && $res2 eq "STORED\r\n"),
"cas on same item from two sockets");
### bug 15: http://code.google.com/p/memcached/issues/detail?id=15
# set foo
print $sock "set bug15 0 0 1\r\n0\r\n";
is(scalar <$sock>, "STORED\r\n", "stored 0");
# Check out the first gets.
print $sock "gets bug15\r\n";
ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success");
my $bug15_cas = $1;
is(scalar <$sock>, "0\r\n", "gets bug15 data is 0");
is(scalar <$sock>, "END\r\n","gets bug15 END");
# Increment
print $sock "incr bug15 1\r\n";
is(scalar <$sock>, "1\r\n", "incr worked");
# Validate a changed CAS
print $sock "gets bug15\r\n";
ok(scalar <$sock> =~ /VALUE bug15 0 1 (\d+)\r\n/, "gets bug15 regexp success");
my $next_bug15_cas = $1;
is(scalar <$sock>, "1\r\n", "gets bug15 data is 0");
is(scalar <$sock>, "END\r\n","gets bug15 END");
ok($bug15_cas != $next_bug15_cas, "CAS changed");
memcached-1.4.25/t/lru-crawler.t 0000664 0001750 0001750 00000004613 12623270407 013332 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 221;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-m 32');
{
my $stats = mem_stats($server->sock, ' settings');
is($stats->{lru_crawler}, "no");
}
my $sock = $server->sock;
# Fill a slab a bit.
# Some immortal items, some long expiring items, some short expiring items.
# Done so the immortals end up at the tail.
for (1 .. 30) {
print $sock "set ifoo$_ 0 0 2\r\nok\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
for (1 .. 30) {
print $sock "set lfoo$_ 0 3600 2\r\nok\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
for (1 .. 30) {
print $sock "set sfoo$_ 0 1 2\r\nok\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
{
my $slabs = mem_stats($sock, "slabs");
is($slabs->{"1:used_chunks"}, 90, "slab1 has 90 used chunks");
}
sleep 3;
print $sock "lru_crawler enable\r\n";
is(scalar <$sock>, "OK\r\n", "enabled lru crawler");
{
my $stats = mem_stats($server->sock, ' settings');
is($stats->{lru_crawler}, "yes");
}
print $sock "lru_crawler crawl 1\r\n";
is(scalar <$sock>, "OK\r\n", "kicked lru crawler");
while (1) {
my $stats = mem_stats($sock);
last unless $stats->{lru_crawler_running};
sleep 1;
}
{
my $slabs = mem_stats($sock, "slabs");
is($slabs->{"1:used_chunks"}, 60, "slab1 now has 60 used chunks");
my $items = mem_stats($sock, "items");
is($items->{"items:1:crawler_reclaimed"}, 30, "slab1 has 30 reclaims");
}
for (1 .. 30) {
mem_get_is($sock, "ifoo$_", "ok");
mem_get_is($sock, "lfoo$_", "ok");
mem_get_is($sock, "sfoo$_", undef);
}
print $sock "lru_crawler disable\r\n";
is(scalar <$sock>, "OK\r\n", "disabled lru crawler");
{
my $stats = mem_stats($server->sock, ' settings');
is($stats->{lru_crawler}, "no");
}
$server->stop;
# Test initializing crawler from starttime.
$server = new_memcached('-m 32 -o lru_crawler');
$sock = $server->sock;
for (1 .. 30) {
print $sock "set sfoo$_ 0 1 2\r\nok\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
sleep 3;
print $sock "lru_crawler crawl 1\r\n";
is(scalar <$sock>, "OK\r\n", "kicked lru crawler");
while (1) {
my $stats = mem_stats($sock);
last unless $stats->{lru_crawler_running};
sleep 1;
}
{
my $slabs = mem_stats($sock, "slabs");
is($slabs->{"1:used_chunks"}, 0, "slab1 now has 0 used chunks");
}
memcached-1.4.25/t/issue_260.t 0000775 0001750 0001750 00000004501 12623270407 012611 0000000 0000000 #!/usr/bin/perl
# Issue #260 is a terrible bug.
# In order to run this test:
# * checkout 1.4.15
# * change TAIL_REPAIR_TIME from (3 * 3600) to 3
# Now it should cause an assert. Patches can be tested to fix it.
use strict;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
plan skip_all => "Only possible to test #260 under artificial conditions";
exit 0;
plan tests => 11074;
# assuming max slab is 1M and default mem is 64M
my $server = new_memcached();
my $sock = $server->sock;
# create a big value for the largest slab
my $max = 1024 * 1024;
my $big = 'x' x (1024 * 1024 - 250);
ok(length($big) > 512 * 1024);
ok(length($big) < 1024 * 1024);
# set the big value
my $len = length($big);
print $sock "set big 0 0 $len\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored big");
mem_get_is($sock, "big", $big);
# no evictions yet
my $stats = mem_stats($sock);
is($stats->{"evictions"}, "0", "no evictions to start");
# set many big items, enough to get evictions
for (my $i = 0; $i < 100; $i++) {
print $sock "set item_$i 0 0 $len\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored item_$i");
}
# some evictions should have happened
my $stats = mem_stats($sock);
my $evictions = int($stats->{"evictions"});
ok($evictions == 37, "some evictions happened");
# the first big value should be gone
mem_get_is($sock, "big", undef);
# the earliest items should be gone too
for (my $i = 0; $i < $evictions - 1; $i++) {
mem_get_is($sock, "item_$i", undef);
}
# check that the non-evicted are the right ones
for (my $i = $evictions - 1; $i < $evictions + 4; $i++) {
mem_get_is($sock, "item_$i", $big);
}
# Now we fill a slab with incrementable items...
for (my $i = 0; $i < 10923; $i++) {
print $sock "set sitem_$i 0 0 1\r\n1\r\n";
is(scalar <$sock>, "STORED\r\n", "stored sitem_$i");
}
my $stats = mem_stats($sock);
my $evictions = int($stats->{"evictions"});
ok($evictions == 38, "one more eviction happened: $evictions");
# That evicted item was the first one we put in.
mem_get_is($sock, "sitem_0", undef);
sleep 15;
# Now we increment the item which should be on the tail.
# THIS asserts the memcached-debug binary.
print $sock "incr sitem_1 1\r\n";
is(scalar <$sock>, "2\r\n", "incremented to two");
#my $stats = mem_stats($sock, "slabs");
#is($stats->{"1:free_chunks"}, 0, "free chunks should still be 0");
memcached-1.4.25/t/flags.t 0000755 0001750 0001750 00000000710 12416643766 012176 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 6;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# set foo (and should get it)
for my $flags (0, 123, 2**16-1) {
print $sock "set foo $flags 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is({ sock => $sock,
flags => $flags }, "foo", "fooval", "got flags $flags back");
}
memcached-1.4.25/t/issue_29.t 0000644 0001750 0001750 00000001167 12416643766 012550 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 4;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
print $sock "set issue29 0 0 0\r\n\r\n";
is (scalar <$sock>, "STORED\r\n", "stored issue29");
my $first_stats = mem_stats($sock, "slabs");
my $first_used = $first_stats->{"1:used_chunks"};
is(1, $first_used, "Used one");
print $sock "set issue29_b 0 0 0\r\n\r\n";
is (scalar <$sock>, "STORED\r\n", "stored issue29_b");
my $second_stats = mem_stats($sock, "slabs");
my $second_used = $second_stats->{"1:used_chunks"};
is(2, $second_used, "Used two")
memcached-1.4.25/t/issue_152.t 0000644 0001750 0001750 00000000606 12416643766 012622 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 2;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $key = "a"x251;
print $sock "set a 1 0 1\r\na\r\n";
is (scalar <$sock>, "STORED\r\n", "Stored key");
print $sock "get a $key\r\n";
is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n", "illegal key");
memcached-1.4.25/t/issue_3.t 0000644 0001750 0001750 00000002524 12416643766 012456 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 8;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $key = "del_key";
print $sock "delete $key\r\n";
is (scalar <$sock>, "NOT_FOUND\r\n", "not found on delete");
print $sock "delete $key 10\r\n";
is (scalar <$sock>, "CLIENT_ERROR bad command line format."
. " Usage: delete [noreply]\r\n", "invalid delete");
print $sock "add $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Add before a broken delete.");
print $sock "delete $key 10 noreply\r\n";
# Does not reply
# is (scalar <$sock>, "ERROR\r\n", "Even more invalid delete");
print $sock "add $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "NOT_STORED\r\n", "Failed to add after failed silent delete.");
print $sock "delete $key noreply\r\n";
# Will not reply, so let's do a set and check that.
print $sock "set $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Stored a key");
print $sock "delete $key\r\n";
is (scalar <$sock>, "DELETED\r\n", "Properly deleted");
print $sock "set $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Stored a key");
print $sock "delete $key noreply\r\n";
# will not reply, but a subsequent add will succeed
print $sock "add $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Add succeeded after deletion.");
memcached-1.4.25/t/issue_192.t 0000664 0001750 0001750 00000001044 12623270407 012611 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 2;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
ok($server->new_sock, "opened new socket");
print $sock "\x80\x12\x00\x01\x08\x00\x00\x00\xff\xff\xff\xe8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
sleep 0.5;
ok($server->new_sock, "failed to open new socket");
memcached-1.4.25/t/stats-conns.t 0000775 0001750 0001750 00000003257 12623270407 013355 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 10;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
## First make sure we report UNIX-domain sockets correctly
my $filename = "/tmp/memcachetest$$";
my $server = new_memcached("-s $filename");
my $sock = $server->sock;
my $stats_sock = $server->new_sock;
ok(-S $filename, "creating unix domain socket $filename");
print $sock "set foo 0 0 6\r\n";
sleep(1); # so we can test secs_since_last_cmd is nonzero
print $stats_sock "stats conns\r\n";
my $stats;
while (<$stats_sock>) {
last if /^(\.|END)/;
$stats .= $_;
}
like($stats, qr/STAT \d+:addr /);
$stats =~ m/STAT (\d+):addr unix:(\/[^\r\n]*)/g;
my $listen_fd = $1;
is($2, $filename, "unix domain socket path reported correctly");
$stats =~ m/STAT (\d+):state conn_listening\r\n/g;
is($1, $listen_fd, "listen socket fd reported correctly");
like($stats, qr/STAT \d+:state conn_nread/,
"one client is sending data");
like($stats, qr/STAT \d+:state conn_parse_cmd/,
"one client is in command processing");
like($stats, qr/STAT \d+:secs_since_last_cmd [1-9]\r/,
"nonzero secs_since_last_cmd");
$server->stop;
unlink($filename);
## Now look at TCP
$server = new_memcached();
$sock = $server->sock;
$stats_sock = $server->new_sock;
print $sock "set foo 0 0 6\r\n";
print $stats_sock "stats conns\r\n";
$stats = '';
while (<$stats_sock>) {
last if /^(\.|END)/;
$stats .= $_;
}
like($stats, qr/STAT \d+:state conn_listen/, "there is a listen socket");
$stats =~ m/STAT \d+:addr udp:0.0.0.0:(\d+)/;
is($1, $server->udpport, "udp port number is correct");
$stats =~ m/STAT \d+:addr tcp:0.0.0.0:(\d+)/;
is($1, $server->port, "tcp port number is correct");
memcached-1.4.25/t/issue_140.t 0000644 0001750 0001750 00000001716 12416643766 012622 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
plan skip_all => 'Fix for Issue 140 was only an illusion';
plan tests => 7;
my $server = new_memcached();
my $sock = $server->sock;
print $sock "set a 0 0 1\r\na\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $stats = mem_stats($sock, "items");
my $age = $stats->{"items:1:age"};
isnt ($age, "0", "Age should not be zero");
print $sock "flush_all\r\n";
is (scalar <$sock>, "OK\r\n", "items flushed");
my $stats = mem_stats($sock, "items");
my $age = $stats->{"items:1:age"};
is ($age, undef, "all should be gone");
print $sock "set a 0 1 1\r\na\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $stats = mem_stats($sock, "items");
my $age = $stats->{"items:1:age"};
isnt ($age, "0", "Age should not be zero");
sleep(3);
my $stats = mem_stats($sock, "items");
my $age = $stats->{"items:1:age"};
is ($age, undef, "all should be gone");
memcached-1.4.25/t/binary.t 0000775 0001750 0001750 00000056622 12623274034 012371 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 3651;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
ok($server, "started the server");
# Based almost 100% off testClient.py which is:
# Copyright (c) 2007 Dustin Sallings
# Command constants
use constant CMD_GET => 0x00;
use constant CMD_SET => 0x01;
use constant CMD_ADD => 0x02;
use constant CMD_REPLACE => 0x03;
use constant CMD_DELETE => 0x04;
use constant CMD_INCR => 0x05;
use constant CMD_DECR => 0x06;
use constant CMD_QUIT => 0x07;
use constant CMD_FLUSH => 0x08;
use constant CMD_GETQ => 0x09;
use constant CMD_NOOP => 0x0A;
use constant CMD_VERSION => 0x0B;
use constant CMD_GETK => 0x0C;
use constant CMD_GETKQ => 0x0D;
use constant CMD_APPEND => 0x0E;
use constant CMD_PREPEND => 0x0F;
use constant CMD_STAT => 0x10;
use constant CMD_SETQ => 0x11;
use constant CMD_ADDQ => 0x12;
use constant CMD_REPLACEQ => 0x13;
use constant CMD_DELETEQ => 0x14;
use constant CMD_INCREMENTQ => 0x15;
use constant CMD_DECREMENTQ => 0x16;
use constant CMD_QUITQ => 0x17;
use constant CMD_FLUSHQ => 0x18;
use constant CMD_APPENDQ => 0x19;
use constant CMD_PREPENDQ => 0x1A;
use constant CMD_TOUCH => 0x1C;
use constant CMD_GAT => 0x1D;
use constant CMD_GATQ => 0x1E;
use constant CMD_GATK => 0x23;
use constant CMD_GATKQ => 0x24;
# REQ and RES formats are divided even though they currently share
# the same format, since they _could_ differ in the future.
use constant REQ_PKT_FMT => "CCnCCnNNNN";
use constant RES_PKT_FMT => "CCnCCnNNNN";
use constant INCRDECR_PKT_FMT => "NNNNN";
use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT));
use constant REQ_MAGIC => 0x80;
use constant RES_MAGIC => 0x81;
my $mc = MC::Client->new;
# Let's turn on detail stats for all this stuff
$mc->stats('detail on');
my $check = sub {
my ($key, $orig_flags, $orig_val) = @_;
my ($flags, $val, $cas) = $mc->get($key);
is($flags, $orig_flags, "Flags is set properly");
ok($val eq $orig_val || $val == $orig_val, $val . " = " . $orig_val);
};
my $set = sub {
my ($key, $exp, $orig_flags, $orig_value) = @_;
$mc->set($key, $orig_value, $orig_flags, $exp);
$check->($key, $orig_flags, $orig_value);
};
my $empty = sub {
my $key = shift;
my $rv =()= eval { $mc->get($key) };
is($rv, 0, "Didn't get a result from get");
ok($@->not_found, "We got a not found error when we expected one");
};
my $delete = sub {
my ($key, $when) = @_;
$mc->delete($key, $when);
$empty->($key);
};
# diag "Test Version";
my $v = $mc->version;
ok(defined $v && length($v), "Proper version: $v");
# Bug 71
{
my %stats1 = $mc->stats('');
$mc->flush;
my %stats2 = $mc->stats('');
is($stats2{'cmd_flush'}, $stats1{'cmd_flush'} + 1,
"Stats not updated on a binary flush");
}
# diag "Flushing...";
$mc->flush;
# diag "Noop";
$mc->noop;
# diag "Simple set/get";
$set->('x', 5, 19, "somevalue");
# diag "Delete";
$delete->('x');
# diag "Flush";
$set->('x', 5, 19, "somevaluex");
$set->('y', 5, 17, "somevaluey");
$mc->flush;
$empty->('x');
$empty->('y');
{
# diag "Add";
$empty->('i');
$mc->add('i', 'ex', 5, 10);
$check->('i', 5, "ex");
my $rv =()= eval { $mc->add('i', "ex2", 10, 5) };
is($rv, 0, "Add didn't return anything");
ok($@->exists, "Expected exists error received");
$check->('i', 5, "ex");
}
{
# diag "Too big.";
$empty->('toobig');
$mc->set('toobig', 'not too big', 10, 10);
eval {
my $bigval = ("x" x (1024*1024)) . "x";
$mc->set('toobig', $bigval, 10, 10);
};
ok($@->too_big, "Was too big");
$empty->('toobig');
}
{
# diag "Replace";
$empty->('j');
my $rv =()= eval { $mc->replace('j', "ex", 19, 5) };
is($rv, 0, "Replace didn't return anything");
ok($@->not_found, "Expected not_found error received");
$empty->('j');
$mc->add('j', "ex2", 14, 5);
$check->('j', 14, "ex2");
$mc->replace('j', "ex3", 24, 5);
$check->('j', 24, "ex3");
}
{
# diag "MultiGet";
$mc->add('xx', "ex", 1, 5);
$mc->add('wye', "why", 2, 5);
my $rv = $mc->get_multi(qw(xx wye zed));
# CAS is returned with all gets.
$rv->{xx}->[2] = 0;
$rv->{wye}->[2] = 0;
is_deeply($rv->{xx}, [1, 'ex', 0], "X is correct");
is_deeply($rv->{wye}, [2, 'why', 0], "Y is correct");
is(keys(%$rv), 2, "Got only two answers like we expect");
}
# diag "Test increment";
$mc->flush;
is($mc->incr("x"), 0, "First incr call is zero");
is($mc->incr("x"), 1, "Second incr call is one");
is($mc->incr("x", 211), 212, "Adding 211 gives you 212");
is($mc->incr("x", 2**33), 8589934804, "Blast the 32bit border");
# diag "Issue 48 - incrementing plain text.";
{
$mc->set("issue48", "text", 0, 0);
my $rv =()= eval { $mc->incr('issue48'); };
ok($@ && $@->delta_badval, "Expected invalid value when incrementing text.");
$check->('issue48', 0, "text");
$rv =()= eval { $mc->decr('issue48'); };
ok($@ && $@->delta_badval, "Expected invalid value when decrementing text.");
$check->('issue48', 0, "text");
}
# diag "Issue 320 - incr/decr wrong length for initial value";
{
$mc->flush;
is($mc->incr("issue320", 1, 1, 0), 1, "incr initial value is 1");
my (undef, $rv, undef) = $mc->get("issue320");
is(length($rv), 1, "initial value length is 1");
is($rv, "1", "initial value is 1");
}
# diag "Test decrement";
$mc->flush;
is($mc->incr("x", undef, 5), 5, "Initial value");
is($mc->decr("x"), 4, "Decrease by one");
is($mc->decr("x", 211), 0, "Floor is zero");
{
# diag "bug220
my ($rv, $cas) = $mc->set("bug220", "100", 0, 0);
my ($irv, $icas) = $mc->incr_cas("bug220", 999);
ok($icas != $cas);
is($irv, 1099, "Incr amount failed");
my ($flags, $val, $gcas) = $mc->get("bug220");
is($gcas, $icas, "CAS didn't match after incr/gets");
($irv, $icas) = $mc->incr_cas("bug220", 999);
ok($icas != $cas);
is($irv, 2098, "Incr amount failed");
($flags, $val, $gcas) = $mc->get("bug220");
is($gcas, $icas, "CAS didn't match after incr/gets");
}
{
# diag "bug21";
$mc->add("bug21", "9223372036854775807", 0, 0);
is($mc->incr("bug21"), 9223372036854775808, "First incr for bug21.");
is($mc->incr("bug21"), 9223372036854775809, "Second incr for bug21.");
is($mc->decr("bug21"), 9223372036854775808, "Decr for bug21.");
}
{
# diag "CAS";
$mc->flush;
{
my $rv =()= eval { $mc->set("x", "bad value", 19, 5, 0x7FFFFFF) };
is($rv, 0, "Empty return on expected failure");
ok($@->not_found, "Error was 'not found' as expected");
}
my ($r, $rcas) = $mc->add("x", "original value", 5, 19);
my ($flags, $val, $i) = $mc->get("x");
is($val, "original value", "->gets returned proper value");
is($rcas, $i, "Add CAS matched.");
{
my $rv =()= eval { $mc->set("x", "broken value", 19, 5, $i+1) };
is($rv, 0, "Empty return on expected failure (1)");
ok($@->exists, "Expected error state of 'exists' (1)");
}
($r, $rcas) = $mc->set("x", "new value", 19, 5, $i);
my ($newflags, $newval, $newi) = $mc->get("x");
is($newval, "new value", "CAS properly overwrote value");
is($rcas, $newi, "Get CAS matched.");
{
my $rv =()= eval { $mc->set("x", "replay value", 19, 5, $i) };
is($rv, 0, "Empty return on expected failure (2)");
ok($@->exists, "Expected error state of 'exists' (2)");
}
}
# diag "Touch commands";
{
$mc->flush;
$mc->set("totouch", "toast", 0, 1);
my $res = $mc->touch("totouch", 10);
sleep 2;
$check->("totouch", 0, "toast");
$mc->set("totouch", "toast2", 0, 1);
my ($flags, $val, $i) = $mc->gat("totouch", 10);
is($val, "toast2", "GAT returned correct value");
sleep 2;
$check->("totouch", 0, "toast2");
# Test miss as well
$mc->set("totouch", "toast3", 0, 1);
$res = $mc->touch("totouch", 1);
sleep 3;
$empty->("totouch");
}
# diag "Silent set.";
$mc->silent_mutation(::CMD_SETQ, 'silentset', 'silentsetval');
# diag "Silent add.";
$mc->silent_mutation(::CMD_ADDQ, 'silentadd', 'silentaddval');
# diag "Silent replace.";
{
my $key = "silentreplace";
my $extra = pack "NN", 829, 0;
$empty->($key);
# $mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0);
# $empty->($key);
$mc->add($key, "xval", 831, 0);
$check->($key, 831, 'xval');
$mc->send_silent(::CMD_REPLACEQ, $key, 'somevalue', 7278552, $extra, 0);
$check->($key, 829, 'somevalue');
}
# diag "Silent delete";
{
my $key = "silentdelete";
$empty->($key);
$mc->set($key, "some val", 19, 0);
$mc->send_silent(::CMD_DELETEQ, $key, '', 772);
$empty->($key);
}
# diag "Silent increment";
{
my $key = "silentincr";
my $opaque = 98428747;
$empty->($key);
$mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 0, 0, 0);
is($mc->incr($key, 0), 0, "First call is 0");
$mc->silent_incrdecr(::CMD_INCREMENTQ, $key, 8, 0, 0);
is($mc->incr($key, 0), 8);
}
# diag "Silent decrement";
{
my $key = "silentdecr";
my $opaque = 98428147;
$empty->($key);
$mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 0, 185, 0);
is($mc->incr($key, 0), 185);
$mc->silent_incrdecr(::CMD_DECREMENTQ, $key, 8, 0, 0);
is($mc->incr($key, 0), 177);
}
# diag "Silent flush";
{
my %stats1 = $mc->stats('');
$set->('x', 5, 19, "somevaluex");
$set->('y', 5, 17, "somevaluey");
$mc->send_silent(::CMD_FLUSHQ, '', '', 2775256);
$empty->('x');
$empty->('y');
my %stats2 = $mc->stats('');
is($stats2{'cmd_flush'}, $stats1{'cmd_flush'} + 1,
"Stats not updated on a binary quiet flush");
}
# diag "Append";
{
my $key = "appendkey";
my $value = "some value";
$set->($key, 8, 19, $value);
$mc->_append_prepend(::CMD_APPEND, $key, " more");
$check->($key, 19, $value . " more");
}
# diag "Prepend";
{
my $key = "prependkey";
my $value = "some value";
$set->($key, 8, 19, $value);
$mc->_append_prepend(::CMD_PREPEND, $key, "prefixed ");
$check->($key, 19, "prefixed " . $value);
}
# diag "Silent append";
{
my $key = "appendqkey";
my $value = "some value";
$set->($key, 8, 19, $value);
$mc->send_silent(::CMD_APPENDQ, $key, " more", 7284492);
$check->($key, 19, $value . " more");
}
# diag "Silent prepend";
{
my $key = "prependqkey";
my $value = "some value";
$set->($key, 8, 19, $value);
$mc->send_silent(::CMD_PREPENDQ, $key, "prefixed ", 7284492);
$check->($key, 19, "prefixed " . $value);
}
# diag "Leaky binary get test.";
# # http://code.google.com/p/memcached/issues/detail?id=16
{
# Get a new socket so we can speak text to it.
my $sock = $server->new_sock;
my $max = 1024 * 1024;
my $big = "a big value that's > .5M and < 1M. ";
while (length($big) * 2 < $max) {
$big = $big . $big;
}
my $biglen = length($big);
for(1..100) {
my $key = "some_key_$_";
# print STDERR "Key is $key\n";
# print $sock "set $key 0 0 $vallen\r\n$value\r\n";
print $sock "set $key 0 0 $biglen\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored big");
my ($f, $v, $c) = $mc->get($key);
}
}
# diag "Test stats settings."
{
my %stats = $mc->stats('settings');
is(1024, $stats{'maxconns'});
is('NULL', $stats{'domain_socket'});
is('on', $stats{'evictions'});
is('yes', $stats{'cas_enabled'});
is('yes', $stats{'flush_enabled'});
}
# diag "Test quit commands.";
{
my $s2 = new_memcached();
my $mc2 = MC::Client->new($s2);
$mc2->send_command(CMD_QUITQ, '', '', 0, '', 0);
# Five seconds ought to be enough to get hung up on.
my $oldalarmt = alarm(5);
# Verify we can't read anything.
my $bytesread = -1;
eval {
local $SIG{'ALRM'} = sub { die "timeout" };
my $data = "";
$bytesread = sysread($mc2->{socket}, $data, 24),
};
is($bytesread, 0, "Read after quit.");
# Restore signal stuff.
alarm($oldalarmt);
}
# diag "Test protocol boundary overruns";
{
use List::Util qw[min];
# Attempting some protocol overruns by toying around with the edge
# of the data buffer at a few different sizes. This assumes the
# boundary is at or around 2048 bytes.
for (my $i = 1900; $i < 2100; $i++) {
my $k = "test_key_$i";
my $v = 'x' x $i;
# diag "Trying $i $k";
my $extra = pack "NN", 82, 0;
my $data = $mc->build_command(::CMD_SETQ, $k, $v, 0, $extra, 0);
$data .= $mc->build_command(::CMD_SETQ, "alt_$k", "blah", 0, $extra, 0);
if (length($data) > 2024) {
for (my $j = 2024; $j < min(2096, length($data)); $j++) {
$mc->{socket}->send(substr($data, 0, $j));
$mc->flush_socket;
sleep(0.001);
$mc->{socket}->send(substr($data, $j));
$mc->flush_socket;
}
} else {
$mc->{socket}->send($data);
}
$mc->flush_socket;
$check->($k, 82, $v);
$check->("alt_$k", 82, "blah");
}
}
# Along with the assertion added to the code to verify we're staying
# within bounds when we do a stats detail dump (detail turned on at
# the top).
my %stats = $mc->stats('detail dump');
# This test causes a disconnection.
{
# diag "Key too large.";
my $key = "x" x 365;
eval {
$mc->get($key, 'should die', 10, 10);
};
ok($@->einval, "Invalid key length");
}
# ######################################################################
# Test ends around here.
# ######################################################################
package MC::Client;
use strict;
use warnings;
use fields qw(socket);
use IO::Socket::INET;
sub new {
my $self = shift;
my ($s) = @_;
$s = $server unless defined $s;
my $sock = $s->sock;
$self = fields::new($self);
$self->{socket} = $sock;
return $self;
}
sub build_command {
my $self = shift;
die "Not enough args to send_command" unless @_ >= 4;
my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
$extra_header = '' unless defined $extra_header;
my $keylen = length($key);
my $vallen = length($val);
my $extralen = length($extra_header);
my $datatype = 0; # field for future use
my $reserved = 0; # field for future use
my $totallen = $keylen + $vallen + $extralen;
my $ident_hi = 0;
my $ident_lo = 0;
if ($cas) {
$ident_hi = int($cas / 2 ** 32);
$ident_lo = int($cas % 2 ** 32);
}
my $msg = pack(::REQ_PKT_FMT, ::REQ_MAGIC, $cmd, $keylen, $extralen,
$datatype, $reserved, $totallen, $opaque, $ident_hi,
$ident_lo);
my $full_msg = $msg . $extra_header . $key . $val;
return $full_msg;
}
sub send_command {
my $self = shift;
die "Not enough args to send_command" unless @_ >= 4;
my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
my $full_msg = $self->build_command($cmd, $key, $val, $opaque, $extra_header, $cas);
my $sent = $self->{socket}->send($full_msg);
die("Send failed: $!") unless $sent;
if($sent != length($full_msg)) {
die("only sent $sent of " . length($full_msg) . " bytes");
}
}
sub flush_socket {
my $self = shift;
$self->{socket}->flush;
}
# Send a silent command and ensure it doesn't respond.
sub send_silent {
my $self = shift;
die "Not enough args to send_silent" unless @_ >= 4;
my ($cmd, $key, $val, $opaque, $extra_header, $cas) = @_;
$self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
$self->send_command(::CMD_NOOP, '', '', $opaque + 1);
my ($ropaque, $data) = $self->_handle_single_response;
Test::More::is($ropaque, $opaque + 1);
}
sub silent_mutation {
my $self = shift;
my ($cmd, $key, $value) = @_;
$empty->($key);
my $extra = pack "NN", 82, 0;
$mc->send_silent($cmd, $key, $value, 7278552, $extra, 0);
$check->($key, 82, $value);
}
sub _handle_single_response {
my $self = shift;
my $myopaque = shift;
my $hdr = "";
while(::MIN_RECV_BYTES - length($hdr) > 0) {
$self->{socket}->recv(my $response, ::MIN_RECV_BYTES - length($hdr));
$hdr .= $response;
}
Test::More::is(length($hdr), ::MIN_RECV_BYTES, "Expected read length");
my ($magic, $cmd, $keylen, $extralen, $datatype, $status, $remaining,
$opaque, $ident_hi, $ident_lo) = unpack(::RES_PKT_FMT, $hdr);
Test::More::is($magic, ::RES_MAGIC, "Got proper response magic");
my $cas = ($ident_hi * 2 ** 32) + $ident_lo;
return ($opaque, '', $cas, 0) if($remaining == 0);
# fetch the value
my $rv="";
while($remaining - length($rv) > 0) {
$self->{socket}->recv(my $buf, $remaining - length($rv));
$rv .= $buf;
}
if(length($rv) != $remaining) {
my $found = length($rv);
die("Expected $remaining bytes, got $found");
}
if (defined $myopaque) {
Test::More::is($opaque, $myopaque, "Expected opaque");
} else {
Test::More::pass("Implicit pass since myopaque is undefined");
}
if ($status) {
die MC::Error->new($status, $rv);
}
return ($opaque, $rv, $cas, $keylen);
}
sub _do_command {
my $self = shift;
die unless @_ >= 3;
my ($cmd, $key, $val, $extra_header, $cas) = @_;
$extra_header = '' unless defined $extra_header;
my $opaque = int(rand(2**32));
$self->send_command($cmd, $key, $val, $opaque, $extra_header, $cas);
my (undef, $rv, $rcas) = $self->_handle_single_response($opaque);
return ($rv, $rcas);
}
sub _incrdecr_header {
my $self = shift;
my ($amt, $init, $exp) = @_;
my $amt_hi = int($amt / 2 ** 32);
my $amt_lo = int($amt % 2 ** 32);
my $init_hi = int($init / 2 ** 32);
my $init_lo = int($init % 2 ** 32);
my $extra_header = pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi,
$init_lo, $exp);
return $extra_header;
}
sub _incrdecr_cas {
my $self = shift;
my ($cmd, $key, $amt, $init, $exp) = @_;
my ($data, $rcas) = $self->_do_command($cmd, $key, '',
$self->_incrdecr_header($amt, $init, $exp));
my $header = substr $data, 0, 8, '';
my ($resp_hi, $resp_lo) = unpack "NN", $header;
my $resp = ($resp_hi * 2 ** 32) + $resp_lo;
return $resp, $rcas;
}
sub _incrdecr {
my $self = shift;
my ($v, $c) = $self->_incrdecr_cas(@_);
return $v
}
sub silent_incrdecr {
my $self = shift;
my ($cmd, $key, $amt, $init, $exp) = @_;
my $opaque = 8275753;
$mc->send_silent($cmd, $key, '', $opaque,
$mc->_incrdecr_header($amt, $init, $exp));
}
sub stats {
my $self = shift;
my $key = shift;
my $cas = 0;
my $opaque = int(rand(2**32));
$self->send_command(::CMD_STAT, $key, '', $opaque, '', $cas);
my %rv = ();
my $found_key = '';
my $found_val = '';
do {
my ($op, $data, $cas, $keylen) = $self->_handle_single_response($opaque);
if($keylen > 0) {
$found_key = substr($data, 0, $keylen);
$found_val = substr($data, $keylen);
$rv{$found_key} = $found_val;
} else {
$found_key = '';
}
} while($found_key ne '');
return %rv;
}
sub get {
my $self = shift;
my $key = shift;
my ($rv, $cas) = $self->_do_command(::CMD_GET, $key, '', '');
my $header = substr $rv, 0, 4, '';
my $flags = unpack("N", $header);
return ($flags, $rv, $cas);
}
sub get_multi {
my $self = shift;
my @keys = @_;
for (my $i = 0; $i < @keys; $i++) {
$self->send_command(::CMD_GETQ, $keys[$i], '', $i, '', 0);
}
my $terminal = @keys + 10;
$self->send_command(::CMD_NOOP, '', '', $terminal);
my %return;
while (1) {
my ($opaque, $data) = $self->_handle_single_response;
last if $opaque == $terminal;
my $header = substr $data, 0, 4, '';
my $flags = unpack("N", $header);
$return{$keys[$opaque]} = [$flags, $data];
}
return %return if wantarray;
return \%return;
}
sub touch {
my $self = shift;
my ($key, $expire) = @_;
my $extra_header = pack "N", $expire;
my $cas = 0;
return $self->_do_command(::CMD_TOUCH, $key, '', $extra_header, $cas);
}
sub gat {
my $self = shift;
my $key = shift;
my $expire = shift;
my $extra_header = pack "N", $expire;
my ($rv, $cas) = $self->_do_command(::CMD_GAT, $key, '', $extra_header);
my $header = substr $rv, 0, 4, '';
my $flags = unpack("N", $header);
return ($flags, $rv, $cas);
}
sub version {
my $self = shift;
return $self->_do_command(::CMD_VERSION, '', '');
}
sub flush {
my $self = shift;
return $self->_do_command(::CMD_FLUSH, '', '');
}
sub add {
my $self = shift;
my ($key, $val, $flags, $expire) = @_;
my $extra_header = pack "NN", $flags, $expire;
my $cas = 0;
return $self->_do_command(::CMD_ADD, $key, $val, $extra_header, $cas);
}
sub set {
my $self = shift;
my ($key, $val, $flags, $expire, $cas) = @_;
my $extra_header = pack "NN", $flags, $expire;
return $self->_do_command(::CMD_SET, $key, $val, $extra_header, $cas);
}
sub _append_prepend {
my $self = shift;
my ($cmd, $key, $val, $cas) = @_;
return $self->_do_command($cmd, $key, $val, '', $cas);
}
sub replace {
my $self = shift;
my ($key, $val, $flags, $expire) = @_;
my $extra_header = pack "NN", $flags, $expire;
my $cas = 0;
return $self->_do_command(::CMD_REPLACE, $key, $val, $extra_header, $cas);
}
sub delete {
my $self = shift;
my ($key) = @_;
return $self->_do_command(::CMD_DELETE, $key, '');
}
sub incr {
my $self = shift;
my ($key, $amt, $init, $exp) = @_;
$amt = 1 unless defined $amt;
$init = 0 unless defined $init;
$exp = 0 unless defined $exp;
return $self->_incrdecr(::CMD_INCR, $key, $amt, $init, $exp);
}
sub incr_cas {
my $self = shift;
my ($key, $amt, $init, $exp) = @_;
$amt = 1 unless defined $amt;
$init = 0 unless defined $init;
$exp = 0 unless defined $exp;
return $self->_incrdecr_cas(::CMD_INCR, $key, $amt, $init, $exp);
}
sub decr {
my $self = shift;
my ($key, $amt, $init, $exp) = @_;
$amt = 1 unless defined $amt;
$init = 0 unless defined $init;
$exp = 0 unless defined $exp;
return $self->_incrdecr(::CMD_DECR, $key, $amt, $init, $exp);
}
sub noop {
my $self = shift;
return $self->_do_command(::CMD_NOOP, '', '');
}
package MC::Error;
use strict;
use warnings;
use constant ERR_UNKNOWN_CMD => 0x81;
use constant ERR_NOT_FOUND => 0x1;
use constant ERR_EXISTS => 0x2;
use constant ERR_TOO_BIG => 0x3;
use constant ERR_EINVAL => 0x4;
use constant ERR_NOT_STORED => 0x5;
use constant ERR_DELTA_BADVAL => 0x6;
use overload '""' => sub {
my $self = shift;
return "Memcache Error ($self->[0]): $self->[1]";
};
sub new {
my $class = shift;
my $error = [@_];
my $self = bless $error, (ref $class || $class);
return $self;
}
sub not_found {
my $self = shift;
return $self->[0] == ERR_NOT_FOUND;
}
sub exists {
my $self = shift;
return $self->[0] == ERR_EXISTS;
}
sub too_big {
my $self = shift;
return $self->[0] == ERR_TOO_BIG;
}
sub delta_badval {
my $self = shift;
return $self->[0] == ERR_DELTA_BADVAL;
}
sub einval {
my $self = shift;
return $self->[0] == ERR_EINVAL;
}
# vim: filetype=perl
memcached-1.4.25/t/00-startup.t 0000755 0001750 0001750 00000003376 12416643766 013034 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 18;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
eval {
my $server = new_memcached();
ok($server, "started the server");
};
is($@, '', 'Basic startup works');
eval {
my $server = new_memcached("-l fooble");
};
ok($@, "Died with illegal -l args");
eval {
my $server = new_memcached("-l 127.0.0.1");
};
is($@,'', "-l 127.0.0.1 works");
eval {
my $server = new_memcached('-C');
my $stats = mem_stats($server->sock, 'settings');
is('no', $stats->{'cas_enabled'});
};
is($@, '', "-C works");
eval {
my $server = new_memcached('-b 8675');
my $stats = mem_stats($server->sock, 'settings');
is('8675', $stats->{'tcp_backlog'});
};
is($@, '', "-b works");
foreach my $val ('auto', 'ascii') {
eval {
my $server = new_memcached("-B $val");
my $stats = mem_stats($server->sock, 'settings');
ok($stats->{'binding_protocol'} =~ /$val/, "$val works");
};
is($@, '', "$val works");
}
# For the binary test, we just verify it starts since we don't have an easy bin client.
eval {
my $server = new_memcached("-B binary");
};
is($@, '', "binary works");
eval {
my $server = new_memcached("-vv -B auto");
};
is($@, '', "auto works");
eval {
my $server = new_memcached("-vv -B ascii");
};
is($@, '', "ascii works");
# For the binary test, we just verify it starts since we don't have an easy bin client.
eval {
my $server = new_memcached("-vv -B binary");
};
is($@, '', "binary works");
# Should blow up with something invalid.
eval {
my $server = new_memcached("-B http");
};
ok($@, "Died with illegal -B arg.");
# Should not allow -t 0
eval {
my $server = new_memcached("-t 0");
};
ok($@, "Died with illegal 0 thread count");
memcached-1.4.25/t/lib/ 0000755 0001750 0001750 00000000000 12573233463 011533 5 0000000 0000000 memcached-1.4.25/t/lib/MemcachedTest.pm 0000644 0001750 0001750 00000016707 12250557060 014524 0000000 0000000 package MemcachedTest;
use strict;
use IO::Socket::INET;
use IO::Socket::UNIX;
use Exporter 'import';
use Carp qw(croak);
use vars qw(@EXPORT);
# Instead of doing the substitution with Autoconf, we assume that
# cwd == builddir.
use Cwd;
my $builddir = getcwd;
@EXPORT = qw(new_memcached sleep mem_get_is mem_gets mem_gets_is mem_stats
supports_sasl free_port);
sub sleep {
my $n = shift;
select undef, undef, undef, $n;
}
sub mem_stats {
my ($sock, $type) = @_;
$type = $type ? " $type" : "";
print $sock "stats$type\r\n";
my $stats = {};
while (<$sock>) {
last if /^(\.|END)/;
/^(STAT|ITEM) (\S+)\s+([^\r\n]+)/;
#print " slabs: $_";
$stats->{$2} = $3;
}
return $stats;
}
sub mem_get_is {
# works on single-line values only. no newlines in value.
my ($sock_opts, $key, $val, $msg) = @_;
my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {};
my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts;
my $expect_flags = $opts->{flags} || 0;
my $dval = defined $val ? "'$val'" : "";
$msg ||= "$key == $dval";
print $sock "get $key\r\n";
if (! defined $val) {
my $line = scalar <$sock>;
if ($line =~ /^VALUE/) {
$line .= scalar(<$sock>) . scalar(<$sock>);
}
Test::More::is($line, "END\r\n", $msg);
} else {
my $len = length($val);
my $body = scalar(<$sock>);
my $expected = "VALUE $key $expect_flags $len\r\n$val\r\nEND\r\n";
if (!$body || $body =~ /^END/) {
Test::More::is($body, $expected, $msg);
return;
}
$body .= scalar(<$sock>) . scalar(<$sock>);
Test::More::is($body, $expected, $msg);
}
}
sub mem_gets {
# works on single-line values only. no newlines in value.
my ($sock_opts, $key) = @_;
my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {};
my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts;
my $val;
my $expect_flags = $opts->{flags} || 0;
print $sock "gets $key\r\n";
my $response = <$sock>;
if ($response =~ /^END/) {
return "NOT_FOUND";
}
else
{
$response =~ /VALUE (.*) (\d+) (\d+) (\d+)/;
my $flags = $2;
my $len = $3;
my $identifier = $4;
read $sock, $val , $len;
# get the END
$_ = <$sock>;
$_ = <$sock>;
return ($identifier,$val);
}
}
sub mem_gets_is {
# works on single-line values only. no newlines in value.
my ($sock_opts, $identifier, $key, $val, $msg) = @_;
my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {};
my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts;
my $expect_flags = $opts->{flags} || 0;
my $dval = defined $val ? "'$val'" : "";
$msg ||= "$key == $dval";
print $sock "gets $key\r\n";
if (! defined $val) {
my $line = scalar <$sock>;
if ($line =~ /^VALUE/) {
$line .= scalar(<$sock>) . scalar(<$sock>);
}
Test::More::is($line, "END\r\n", $msg);
} else {
my $len = length($val);
my $body = scalar(<$sock>);
my $expected = "VALUE $key $expect_flags $len $identifier\r\n$val\r\nEND\r\n";
if (!$body || $body =~ /^END/) {
Test::More::is($body, $expected, $msg);
return;
}
$body .= scalar(<$sock>) . scalar(<$sock>);
Test::More::is($body, $expected, $msg);
}
}
sub free_port {
my $type = shift || "tcp";
my $sock;
my $port;
while (!$sock) {
$port = int(rand(20000)) + 30000;
$sock = IO::Socket::INET->new(LocalAddr => '127.0.0.1',
LocalPort => $port,
Proto => $type,
ReuseAddr => 1);
}
return $port;
}
sub supports_udp {
my $output = `$builddir/memcached-debug -h`;
return 0 if $output =~ /^memcached 1\.1\./;
return 1;
}
sub supports_sasl {
my $output = `$builddir/memcached-debug -h`;
return 1 if $output =~ /sasl/i;
return 0;
}
sub new_memcached {
my ($args, $passed_port) = @_;
my $port = $passed_port || free_port();
my $host = '127.0.0.1';
if ($ENV{T_MEMD_USE_DAEMON}) {
my ($host, $port) = ($ENV{T_MEMD_USE_DAEMON} =~ m/^([^:]+):(\d+)$/);
my $conn = IO::Socket::INET->new(PeerAddr => "$host:$port");
if ($conn) {
return Memcached::Handle->new(conn => $conn,
host => $host,
port => $port);
}
croak("Failed to connect to specified memcached server.") unless $conn;
}
my $udpport = free_port("udp");
$args .= " -p $port";
if (supports_udp()) {
$args .= " -U $udpport";
}
if ($< == 0) {
$args .= " -u root";
}
my $childpid = fork();
my $exe = "$builddir/memcached-debug";
croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe;
croak("memcached binary not executable\n") unless -x _;
unless ($childpid) {
exec "$builddir/timedrun 600 $exe $args";
exit; # never gets here.
}
# unix domain sockets
if ($args =~ /-s (\S+)/) {
sleep 1;
my $filename = $1;
my $conn = IO::Socket::UNIX->new(Peer => $filename) ||
croak("Failed to connect to unix domain socket: $! '$filename'");
return Memcached::Handle->new(pid => $childpid,
conn => $conn,
domainsocket => $filename,
host => $host,
port => $port);
}
# try to connect / find open port, only if we're not using unix domain
# sockets
for (1..20) {
my $conn = IO::Socket::INET->new(PeerAddr => "127.0.0.1:$port");
if ($conn) {
return Memcached::Handle->new(pid => $childpid,
conn => $conn,
udpport => $udpport,
host => $host,
port => $port);
}
select undef, undef, undef, 0.10;
}
croak("Failed to startup/connect to memcached server.");
}
############################################################################
package Memcached::Handle;
sub new {
my ($class, %params) = @_;
return bless \%params, $class;
}
sub DESTROY {
my $self = shift;
kill 2, $self->{pid};
}
sub stop {
my $self = shift;
kill 15, $self->{pid};
}
sub host { $_[0]{host} }
sub port { $_[0]{port} }
sub udpport { $_[0]{udpport} }
sub sock {
my $self = shift;
if ($self->{conn} && ($self->{domainsocket} || getpeername($self->{conn}))) {
return $self->{conn};
}
return $self->new_sock;
}
sub new_sock {
my $self = shift;
if ($self->{domainsocket}) {
return IO::Socket::UNIX->new(Peer => $self->{domainsocket});
} else {
return IO::Socket::INET->new(PeerAddr => "$self->{host}:$self->{port}");
}
}
sub new_udp_sock {
my $self = shift;
return IO::Socket::INET->new(PeerAddr => '127.0.0.1',
PeerPort => $self->{udpport},
Proto => 'udp',
LocalAddr => '127.0.0.1',
LocalPort => MemcachedTest::free_port('udp'),
);
}
1;
memcached-1.4.25/t/lru.t 0000755 0001750 0001750 00000003134 12416643766 011707 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 149;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
# assuming max slab is 1M and default mem is 64M
my $server = new_memcached();
my $sock = $server->sock;
# create a big value for the largest slab
my $max = 1024 * 1024;
my $big = 'x' x (1024 * 1024 - 250);
ok(length($big) > 512 * 1024);
ok(length($big) < 1024 * 1024);
# test that an even bigger value is rejected while we're here
my $too_big = $big . $big . $big;
my $len = length($too_big);
print $sock "set too_big 0 0 $len\r\n$too_big\r\n";
is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "too_big not stored");
# set the big value
my $len = length($big);
print $sock "set big 0 0 $len\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored big");
mem_get_is($sock, "big", $big);
# no evictions yet
my $stats = mem_stats($sock);
is($stats->{"evictions"}, "0", "no evictions to start");
# set many big items, enough to get evictions
for (my $i = 0; $i < 100; $i++) {
print $sock "set item_$i 0 0 $len\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored item_$i");
}
# some evictions should have happened
my $stats = mem_stats($sock);
my $evictions = int($stats->{"evictions"});
ok($evictions == 37, "some evictions happened");
# the first big value should be gone
mem_get_is($sock, "big", undef);
# the earliest items should be gone too
for (my $i = 0; $i < $evictions - 1; $i++) {
mem_get_is($sock, "item_$i", undef);
}
# check that the non-evicted are the right ones
for (my $i = $evictions - 1; $i < $evictions + 4; $i++) {
mem_get_is($sock, "item_$i", $big);
}
memcached-1.4.25/t/daemonize.t 0000755 0001750 0001750 00000001342 12416643766 013057 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 7;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use File::Temp qw(tempfile);
my (undef, $tmpfn) = tempfile();
my $server = new_memcached("-d -P $tmpfn");
my $sock = $server->sock;
sleep 0.5;
ok(-e $tmpfn, "pid file exists");
ok(-s $tmpfn, "pid file has length");
open (my $fh, $tmpfn) or die;
my $readpid = do { local $/; <$fh>; };
chomp $readpid;
close ($fh);
ok(kill(0, $readpid), "process is still running");
my $stats = mem_stats($sock);
is($stats->{pid}, $readpid, "memcached reports same pid as file");
ok($server->new_sock, "opened new socket");
ok(kill(9, $readpid), "sent KILL signal");
sleep 0.5;
ok(! $server->new_sock, "failed to open new socket");
memcached-1.4.25/t/issue_42.t 0000644 0001750 0001750 00000000774 12416643766 012546 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 11;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $value = "B"x10;
my $key = 0;
for ($key = 0; $key < 10; $key++) {
print $sock "set key$key 0 0 10\r\n$value\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key$key");
}
my $first_stats = mem_stats($sock, "slabs");
my $req = $first_stats->{"1:mem_requested"};
ok ($req == "640" || $req == "800", "Check allocated size");
memcached-1.4.25/t/udp.t 0000755 0001750 0001750 00000022727 12416643766 011706 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 48;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use constant IS_ASCII => 0;
use constant IS_BINARY => 1;
use constant ENTRY_EXISTS => 0;
use constant ENTRY_MISSING => 1;
use constant BIN_REQ_MAGIC => 0x80;
use constant BIN_RES_MAGIC => 0x81;
use constant CMD_GET => 0x00;
use constant CMD_SET => 0x01;
use constant CMD_ADD => 0x02;
use constant CMD_REPLACE => 0x03;
use constant CMD_DELETE => 0x04;
use constant CMD_INCR => 0x05;
use constant CMD_DECR => 0x06;
use constant CMD_APPEND => 0x0E;
use constant CMD_PREPEND => 0x0F;
use constant REQ_PKT_FMT => "CCnCCnNNNN";
use constant RES_PKT_FMT => "CCnCCnNNNN";
use constant INCRDECR_PKT_FMT => "NNNNN";
use constant MIN_RECV_BYTES => length(pack(RES_PKT_FMT));
my $server = new_memcached();
my $sock = $server->sock;
# set foo (and should get it)
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
my $usock = $server->new_udp_sock
or die "Can't bind : $@\n";
# testing sequence of request ids
for my $offt (1, 1, 2) {
my $req = 160 + $offt;
my $res = send_udp_request($usock, $req, "get foo\r\n");
ok($res, "got result");
is(keys %$res, 1, "one key (one packet)");
ok($res->{0}, "only got seq number 0");
is(substr($res->{0}, 8), "VALUE foo 0 6\r\nfooval\r\nEND\r\n");
is(hexify(substr($res->{0}, 0, 2)), hexify(pack("n", $req)), "udp request number in response ($req) is correct");
}
# op tests
for my $prot (::IS_ASCII,::IS_BINARY) {
udp_set_test($prot,45,"aval$prot","1",0,0);
udp_set_test($prot,45,"bval$prot","abcd" x 1024,0,0);
udp_get_test($prot,45,"aval$prot","1",::ENTRY_EXISTS);
udp_get_test($prot,45,"404$prot","1",::ENTRY_MISSING);
udp_incr_decr_test($prot,45,"aval$prot","1","incr",1);
udp_incr_decr_test($prot,45,"aval$prot","1","decr",2);
udp_delete_test($prot,45,"aval$prot");
}
sub udp_set_test {
my ($protocol, $req_id, $key, $value, $flags, $exp) = @_;
my $req = "";
my $val_len = length($value);
if ($protocol == ::IS_ASCII) {
$req = "set $key $flags $exp $val_len\r\n$value\r\n";
} elsif ($protocol == ::IS_BINARY) {
my $key_len = length($key);
my $extra = pack "NN",$flags,$exp;
my $extra_len = length($extra);
my $total_len = $val_len + $extra_len + $key_len;
$req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_SET, $key_len, $extra_len, 0, 0, $total_len, 0, 0, 0);
$req .= $extra . $key . $value;
}
my $datagrams = send_udp_request($usock, $req_id, $req);
my $resp = construct_udp_message($datagrams);
if ($protocol == ::IS_ASCII) {
is($resp,"STORED\r\n","Store key $key using ASCII protocol");
} elsif ($protocol == ::IS_BINARY) {
my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
$resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
is($resp_status,"0","Store key $key using binary protocol");
}
}
sub udp_get_test {
my ($protocol, $req_id, $key, $value, $exists) = @_;
my $key_len = length($key);
my $value_len = length($value);
my $req = "";
if ($protocol == ::IS_ASCII) {
$req = "get $key\r\n";
} elsif ($protocol == ::IS_BINARY) {
$req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_GET, $key_len, 0, 0, 0, $key_len, 0, 0, 0);
$req .= $key;
}
my $datagrams = send_udp_request($usock, $req_id, $req);
my $resp = construct_udp_message($datagrams);
if ($protocol == ::IS_ASCII) {
if ($exists == ::ENTRY_EXISTS) {
is($resp,"VALUE $key 0 $value_len\r\n$value\r\nEND\r\n","Retrieve entry with key $key using ASCII protocol");
} else {
is($resp,"END\r\n","Retrieve non existing entry with key $key using ASCII protocol");
}
} elsif ($protocol == ::IS_BINARY) {
my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
$resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
if ($exists == ::ENTRY_EXISTS) {
is($resp_status,"0","Retrieve entry with key $key using binary protocol");
is(substr($resp,::MIN_RECV_BYTES + $resp_extra_len + $resp_key_len, $value_len),$value,"Value for key $key retrieved with binary protocol matches");
} else {
is($resp_status,"1","Retrieve non existing entry with key $key using binary protocol");
}
}
}
sub udp_delete_test {
my ($protocol, $req_id, $key) = @_;
my $req = "";
my $key_len = length($key);
if ($protocol == ::IS_ASCII) {
$req = "delete $key\r\n";
} elsif ($protocol == ::IS_BINARY) {
$req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, ::CMD_DELETE, $key_len, 0, 0, 0, $key_len, 0, 0, 0);
$req .= $key;
}
my $datagrams = send_udp_request($usock, $req_id, $req);
my $resp = construct_udp_message($datagrams);
if ($protocol == ::IS_ASCII) {
is($resp,"DELETED\r\n","Delete key $key using ASCII protocol");
} elsif ($protocol == ::IS_BINARY) {
my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
$resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
is($resp_status,"0","Delete key $key using binary protocol");
}
}
sub udp_incr_decr_test {
my ($protocol, $req_id, $key, $val, $optype, $init_val) = @_;
my $req = "";
my $key_len = length($key);
my $expected_value = 0;
my $acmd = "incr";
my $bcmd = ::CMD_INCR;
if ($optype eq "incr") {
$expected_value = $init_val + $val;
} else {
$acmd = "decr";
$bcmd = ::CMD_DECR;
$expected_value = $init_val - $val;
}
if ($protocol == ::IS_ASCII) {
$req = "$acmd $key $val\r\n";
} elsif ($protocol == ::IS_BINARY) {
my $extra = pack(::INCRDECR_PKT_FMT, ($val / 2 ** 32),($val % 2 ** 32), 0, 0, 0);
my $extra_len = length($extra);
$req = pack(::REQ_PKT_FMT, ::BIN_REQ_MAGIC, $bcmd, $key_len, $extra_len, 0, 0, $key_len + $extra_len, 0, 0, 0);
$req .= $extra . $key;
}
my $datagrams = send_udp_request($usock, $req_id, $req);
my $resp = construct_udp_message($datagrams);
if ($protocol == ::IS_ASCII) {
is($resp,"$expected_value\r\n","perform $acmd math operation on key $key with ASCII protocol");
} elsif ($protocol == ::IS_BINARY) {
my ($resp_magic, $resp_op_code, $resp_key_len, $resp_extra_len, $resp_data_type, $resp_status, $resp_total_len,
$resp_opaque, $resp_ident_hi, $resp_ident_lo) = unpack(::RES_PKT_FMT, $resp);
is($resp_status,"0","perform $acmd math operation on key $key with binary protocol");
my ($resp_hi,$resp_lo) = unpack("NN",substr($resp,::MIN_RECV_BYTES + $resp_extra_len + $resp_key_len,
$resp_total_len - $resp_extra_len - $resp_key_len));
is(($resp_hi * 2 ** 32) + $resp_lo,$expected_value,"validate result of binary protocol math operation $acmd . Expected value $expected_value")
}
}
sub construct_udp_message {
my $datagrams = shift;
my $num_datagram = keys (%$datagrams);
my $msg = "";
my $cur_dg ="";
my $cur_udp_header ="";
for (my $cur_dg_index = 0; $cur_dg_index < $num_datagram; $cur_dg_index++) {
$cur_dg = $datagrams->{$cur_dg_index};
isnt($cur_dg,"","missing datagram for segment $cur_dg_index");
$cur_udp_header=substr($cur_dg, 0, 8);
$msg .= substr($cur_dg,8);
}
return $msg;
}
sub hexify {
my $val = shift;
$val =~ s/(.)/sprintf("%02x", ord($1))/egs;
return $val;
}
# returns undef on select timeout, or hashref of "seqnum" -> payload (including headers)
# verifies that resp_id is equal to id sent in request
# ensures consistency in num packets that make up response
sub send_udp_request {
my ($sock, $reqid, $req) = @_;
my $pkt = pack("nnnn", $reqid, 0, 1, 0); # request id (opaque), seq num, #packets, reserved (must be 0)
$pkt .= $req;
my $fail = sub {
my $msg = shift;
warn " FAILING send_udp because: $msg\n";
return undef;
};
return $fail->("send") unless send($sock, $pkt, 0);
my $ret = {};
my $got = 0; # packets got
my $numpkts = undef;
while (!defined($numpkts) || $got < $numpkts) {
my $rin = '';
vec($rin, fileno($sock), 1) = 1;
my $rout;
return $fail->("timeout after $got packets") unless
select($rout = $rin, undef, undef, 1.5);
my $res;
my $sender = $sock->recv($res, 1500, 0);
my ($resid, $seq, $this_numpkts, $resv) = unpack("nnnn", substr($res, 0, 8));
die "Response ID of $resid doesn't match request if of $reqid" unless $resid == $reqid;
die "Reserved area not zero" unless $resv == 0;
die "num packets changed midstream!" if defined $numpkts && $this_numpkts != $numpkts;
$numpkts = $this_numpkts;
$ret->{$seq} = $res;
$got++;
}
return $ret;
}
__END__
$sender = recv($usock, $ans, 1050, 0);
__END__
$usock->send
($hispaddr = recv(SOCKET, $rtime, 4, 0)) || die "recv: $!";
($port, $hisiaddr) = sockaddr_in($hispaddr);
$host = gethostbyaddr($hisiaddr, AF_INET);
$histime = unpack("N", $rtime) - $SECS_of_70_YEARS ;
memcached-1.4.25/t/slabs_reassign.t 0000644 0001750 0001750 00000004657 12602341606 014075 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 130;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
# Enable manual slab reassign, cap at 6 slabs
my $server = new_memcached('-o slab_reassign -m 4');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{slab_reassign}, "yes");
my $sock = $server->sock;
# Fill a largeish slab until it evicts (honors the -m 6)
my $bigdata = 'x' x 70000; # slab 31
for (1 .. 60) {
print $sock "set bfoo$_ 0 0 70000\r\n", $bigdata, "\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
# Fill a smaller slab until it evicts
my $smalldata = 'y' x 20000; # slab 25
for (1 .. 60) {
print $sock "set sfoo$_ 0 0 20000\r\n", $smalldata, "\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
}
my $items_before = mem_stats($sock, "items");
isnt($items_before->{"items:31:evicted"}, 0, "slab 31 evicted is nonzero");
isnt($items_before->{"items:25:evicted"}, 0, "slab 25 evicted is nonzero");
my $slabs_before = mem_stats($sock, "slabs");
# Move a large slab to the smaller slab
print $sock "slabs reassign 31 25\r\n";
is(scalar <$sock>, "OK\r\n", "slab rebalancer started");
# Still working out how/if to signal the thread. For now, just sleep.
sleep 2;
# Check that stats counters increased
my $slabs_after = mem_stats($sock, "slabs");
$stats = mem_stats($sock);
isnt($stats->{slabs_moved}, 0, "slabs moved is nonzero");
# Check that slab stats reflect the change
ok($slabs_before->{"31:total_pages"} != $slabs_after->{"31:total_pages"},
"slab 31 pagecount changed");
ok($slabs_before->{"25:total_pages"} != $slabs_after->{"25:total_pages"},
"slab 25 pagecount changed");
# Try to move another slab, see that you can move two in a row
print $sock "slabs reassign 31 25\r\n";
like(scalar <$sock>, qr/^OK/, "Cannot re-run against class with empty space");
# Try to move a page backwards. Should complain that source class isn't "safe"
# to move from.
# TODO: Wait until the above command completes, then try to move it back?
# Seems pointless...
#print $sock "slabs reassign 25 31\r\n";
#like(scalar <$sock>, qr/^UNSAFE/, "Cannot move an unsafe slab back");
# Try to insert items into both slabs
print $sock "set bfoo51 0 0 70000\r\n", $bigdata, "\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
print $sock "set sfoo51 0 0 20000\r\n", $smalldata, "\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key");
# Do need to come up with better automated tests for this.
memcached-1.4.25/t/issue_41.t 0000644 0001750 0001750 00000001724 12416643766 012541 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(ceil);
use Test::More tests => 691;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $factor = 2;
my $val = "x" x $factor;
my $key = '';
# SET items of diverse size to the daemon so it can attempt
# to return a large stats output for slabs
for (my $i=0; $i<69; $i++) {
for (my $j=0; $j<10; $j++) {
$key = "$i:$j";
print $sock "set key$key 0 0 $factor\r\n$val\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key$key");
}
$factor *= 1.2;
$factor = ceil($factor);
$val = "x" x $factor;
}
# This request will kill the daemon if it has not allocated
# enough memory internally.
my $stats = mem_stats($sock, "slabs");
# Verify whether the daemon is still running or not by asking
# it for statistics.
print $sock "version\r\n";
my $v = scalar <$sock>;
ok(defined $v && length($v), "memcached didn't respond");
memcached-1.4.25/t/stats.t 0000775 0001750 0001750 00000012713 12623274034 012234 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 98;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
## Output looks like this:
##
## STAT pid 22969
## STAT uptime 13
## STAT time 1259170891
## STAT version 1.4.3
## STAT libevent 1.4.13-stable.
## STAT pointer_size 32
## STAT rusage_user 0.001198
## STAT rusage_system 0.003523
## STAT curr_connections 10
## STAT total_connections 11
## STAT connection_structures 11
## STAT reserved_fds 20
## STAT cmd_get 0
## STAT cmd_set 0
## STAT cmd_flush 0
## STAT cmd_touch 0
## STAT get_hits 0
## STAT get_misses 0
## STAT delete_misses 0
## STAT delete_hits 0
## STAT incr_misses 0
## STAT incr_hits 0
## STAT decr_misses 0
## STAT decr_hits 0
## STAT cas_misses 0
## STAT cas_hits 0
## STAT cas_badval 0
## STAT touch_hits 0
## STAT touch_misses 0
## STAT auth_cmds 0
## STAT auth_unknowns 0
## STAT bytes_read 7
## STAT bytes_written 0
## STAT limit_maxbytes 67108864
## STAT accepting_conns 1
## STAT listen_disabled_num 0
## STAT time_in_listen_disabled_us 0
## STAT threads 4
## STAT conn_yields 0
## STAT hash_power_level 16
## STAT hash_bytes 524288
## STAT hash_is_expanding 0
## STAT malloc_fails 0
## STAT bytes 0
## STAT curr_items 0
## STAT total_items 0
## STAT expired_unfetched 0
## STAT evicted_unfetched 0
## STAT evictions 0
## STAT reclaimed 0
## STAT crawler_reclaimed 0
## STAT lrutail_reflocked 0
## see doc/protocol.txt for others
# note that auth stats are tested in auth specfic tests
my $stats = mem_stats($sock);
# Test number of keys
is(scalar(keys(%$stats)), 53, "53 stats values");
# Test initial state
foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses
bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits
decr_misses listen_disabled_num lrutail_reflocked time_in_listen_disabled_us)) {
is($stats->{$key}, 0, "initial $key is zero");
}
is($stats->{accepting_conns}, 1, "initial accepting_conns is one");
# Do some operations
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
my $stats = mem_stats($sock);
foreach my $key (qw(total_items curr_items cmd_get cmd_set get_hits)) {
is($stats->{$key}, 1, "after one set/one get $key is 1");
}
my $cache_dump = mem_stats($sock, " cachedump 1 100");
ok(defined $cache_dump->{'foo'}, "got foo from cachedump");
print $sock "delete foo\r\n";
is(scalar <$sock>, "DELETED\r\n", "deleted foo");
my $stats = mem_stats($sock);
is($stats->{delete_hits}, 1);
is($stats->{delete_misses}, 0);
print $sock "delete foo\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't delete foo again");
my $stats = mem_stats($sock);
is($stats->{delete_hits}, 1);
is($stats->{delete_misses}, 1);
# incr stats
sub check_incr_stats {
my ($ih, $im, $dh, $dm) = @_;
my $stats = mem_stats($sock);
is($stats->{incr_hits}, $ih);
is($stats->{incr_misses}, $im);
is($stats->{decr_hits}, $dh);
is($stats->{decr_misses}, $dm);
}
print $sock "incr i 1\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't incr a missing thing");
check_incr_stats(0, 1, 0, 0);
print $sock "decr d 1\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "shouldn't decr a missing thing");
check_incr_stats(0, 1, 0, 1);
print $sock "set n 0 0 1\r\n0\r\n";
is(scalar <$sock>, "STORED\r\n", "stored n");
print $sock "incr n 3\r\n";
is(scalar <$sock>, "3\r\n", "incr works");
check_incr_stats(1, 1, 0, 1);
print $sock "decr n 1\r\n";
is(scalar <$sock>, "2\r\n", "decr works");
check_incr_stats(1, 1, 1, 1);
# cas stats
sub check_cas_stats {
my ($ch, $cm, $cb) = @_;
my $stats = mem_stats($sock);
is($stats->{cas_hits}, $ch);
is($stats->{cas_misses}, $cm);
is($stats->{cas_badval}, $cb);
}
check_cas_stats(0, 0, 0);
print $sock "cas c 0 0 1 99999999\r\nz\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "missed cas");
check_cas_stats(0, 1, 0);
print $sock "set c 0 0 1\r\nx\r\n";
is(scalar <$sock>, "STORED\r\n", "stored c");
my ($id, $v) = mem_gets($sock, 'c');
is('x', $v, 'got the expected value');
print $sock "cas c 0 0 1 99999999\r\nz\r\n";
is(scalar <$sock>, "EXISTS\r\n", "missed cas");
check_cas_stats(0, 1, 1);
my ($newid, $v) = mem_gets($sock, 'c');
is('x', $v, 'got the expected value');
print $sock "cas c 0 0 1 $id\r\nz\r\n";
is(scalar <$sock>, "STORED\r\n", "good cas");
check_cas_stats(1, 1, 1);
my ($newid, $v) = mem_gets($sock, 'c');
is('z', $v, 'got the expected value');
my $settings = mem_stats($sock, ' settings');
is(1024, $settings->{'maxconns'});
is('NULL', $settings->{'domain_socket'});
is('on', $settings->{'evictions'});
is('yes', $settings->{'cas_enabled'});
is('no', $settings->{'auth_enabled_sasl'});
print $sock "stats reset\r\n";
is(scalar <$sock>, "RESET\r\n", "good stats reset");
my $stats = mem_stats($sock);
is(0, $stats->{'cmd_get'});
is(0, $stats->{'cmd_set'});
is(0, $stats->{'get_hits'});
is(0, $stats->{'get_misses'});
is(0, $stats->{'delete_misses'});
is(0, $stats->{'delete_hits'});
is(0, $stats->{'incr_misses'});
is(0, $stats->{'incr_hits'});
is(0, $stats->{'decr_misses'});
is(0, $stats->{'decr_hits'});
is(0, $stats->{'cas_misses'});
is(0, $stats->{'cas_hits'});
is(0, $stats->{'cas_badval'});
is(0, $stats->{'evictions'});
is(0, $stats->{'reclaimed'});
is(0, $stats->{'lrutail_reflocked'});
print $sock "flush_all\r\n";
is(scalar <$sock>, "OK\r\n", "flushed");
my $stats = mem_stats($sock);
is($stats->{cmd_flush}, 1, "after one flush cmd_flush is 1");
memcached-1.4.25/t/stress-memcached.pl 0000755 0001750 0001750 00000004265 11246331452 014474 0000000 0000000 #!/usr/bin/perl
#
use strict;
use lib '../../api/perl/lib';
use Cache::Memcached;
use Time::HiRes qw(time);
unless (@ARGV == 2) {
die "Usage: stress-memcached.pl ip:port threads\n";
}
my $host = shift;
my $threads = shift;
my $memc = new Cache::Memcached;
$memc->set_servers([$host]);
unless ($memc->set("foo", "bar") &&
$memc->get("foo") eq "bar") {
die "memcached not running at $host ?\n";
}
$memc->disconnect_all();
my $running = 0;
while (1) {
if ($running < $threads) {
my $cpid = fork();
if ($cpid) {
$running++;
#print "Launched $cpid. Running $running threads.\n";
} else {
stress();
exit 0;
}
} else {
wait();
$running--;
}
}
sub stress {
undef $memc;
$memc = new Cache::Memcached;
$memc->set_servers([$host]);
my ($t1, $t2);
my $start = sub { $t1 = time(); };
my $stop = sub {
my $op = shift;
$t2 = time();
my $td = sprintf("%0.3f", $t2 - $t1);
if ($td > 0.25) { print "Took $td seconds for: $op\n"; }
};
my $max = rand(50);
my $sets = 0;
for (my $i = 0; $i < $max; $i++) {
my $key = key($i);
my $set = $memc->set($key, $key);
$sets++ if $set;
}
for (1..int(rand(500))) {
my $rand = int(rand($max));
my $key = key($rand);
my $meth = int(rand(3));
my $exp = int(rand(3));
undef $exp unless $exp;
$start->();
if ($meth == 0) {
$memc->add($key, $key, $exp);
$stop->("add");
} elsif ($meth == 1) {
$memc->delete($key);
$stop->("delete");
} else {
$memc->set($key, $key, $exp);
$stop->("set");
}
$rand = int(rand($max));
$key = key($rand);
$start->();
my $v = $memc->get($key);
$stop->("get");
if ($v && $v ne $key) { die "Bogus: $v for key $rand\n"; }
}
$start->();
my $multi = $memc->get_multi(map { key(int(rand($max))) } (1..$max));
$stop->("get_multi");
}
sub key {
my $n = shift;
$_ = sprintf("%04d", $n);
if ($n % 2) { $_ .= "a"x20; }
$_;
}
memcached-1.4.25/t/issue_183.t 0000644 0001750 0001750 00000001205 12416643766 012622 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 5;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
print $sock "set key 0 0 1\r\n1\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $s1 = mem_stats($sock);
my $r1 = $s1->{"reclaimed"};
is ($r1, "0", "Objects should not be reclaimed");
sleep(2);
print $sock "flush_all\r\n";
is (scalar <$sock>, "OK\r\n", "Cache flushed");
print $sock "set key 0 0 1\r\n1\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key");
my $s2 = mem_stats($sock);
my $r2 = $s2->{"reclaimed"};
is ($r2, "1", "Objects should be reclaimed");
memcached-1.4.25/t/multiversioning.t 0000755 0001750 0001750 00000003016 12416643766 014342 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 13;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $sock2 = $server->new_sock;
ok($sock != $sock2, "have two different connections open");
# set large value
my $size = 256 * 1024; # 256 kB
my $bigval = "0123456789abcdef" x ($size / 16);
$bigval =~ s/^0/\[/; $bigval =~ s/f$/\]/;
my $bigval2 = uc($bigval);
print $sock "set big 0 0 $size\r\n$bigval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "big", $bigval, "big value got correctly");
print $sock "get big\r\n";
my $buf;
is(read($sock, $buf, $size / 2), $size / 2, "read half the answer back");
like($buf, qr/VALUE big/, "buf has big value header in it");
like($buf, qr/abcdef/, "buf has some data in it");
unlike($buf, qr/abcde\]/, "buf doesn't yet close");
# sock2 interrupts (maybe sock1 is slow) and deletes stuff:
print $sock2 "delete big\r\n";
is(scalar <$sock2>, "DELETED\r\n", "deleted big from sock2 while sock1's still reading it");
mem_get_is($sock2, "big", undef, "nothing from sock2 now. gone from namespace.");
print $sock2 "set big 0 0 $size\r\n$bigval2\r\n";
is(scalar <$sock2>, "STORED\r\n", "stored big w/ val2");
mem_get_is($sock2, "big", $bigval2, "big value2 got correctly");
# sock1 resumes reading...
$buf .= <$sock>;
$buf .= <$sock>;
like($buf, qr/abcde\]/, "buf now closes");
# and if sock1 reads again, it's the uppercase version:
mem_get_is($sock, "big", $bigval2, "big value2 got correctly from sock1");
memcached-1.4.25/t/touch.t 0000755 0001750 0001750 00000000731 12416643766 012227 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 4;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# set foo (and should get it)
print $sock "set foo 0 2 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
# touch it
print $sock "touch foo 10\r\n";
is(scalar <$sock>, "TOUCHED\r\n", "touched foo");
sleep 2;
mem_get_is($sock, "foo", "fooval");
memcached-1.4.25/t/64bit.t 0000755 0001750 0001750 00000002162 12416643766 012035 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
$ENV{T_MEMD_INITIAL_MALLOC} = "4294967328"; # 2**32 + 32 , just over 4GB
$ENV{T_MEMD_SLABS_ALLOC} = 0; # don't preallocate slabs
my $server = new_memcached("-m 4098 -M");
my $sock = $server->sock;
my ($stats, $slabs) = @_;
$stats = mem_stats($sock);
if ($stats->{'pointer_size'} eq "32") {
plan skip_all => 'Skipping 64-bit tests on 32-bit build';
exit 0;
} else {
plan tests => 6;
}
is($stats->{'pointer_size'}, 64, "is 64 bit");
is($stats->{'limit_maxbytes'}, "4297064448", "max bytes is 4098 MB");
$slabs = mem_stats($sock, 'slabs');
is($slabs->{'total_malloced'}, "4294967328", "expected (faked) value of total_malloced");
is($slabs->{'active_slabs'}, 0, "no active slabs");
my $hit_limit = 0;
for (1..5) {
my $size = 400 * 1024;
my $data = "a" x $size;
print $sock "set big$_ 0 0 $size\r\n$data\r\n";
my $res = <$sock>;
$hit_limit = 1 if $res ne "STORED\r\n";
}
ok($hit_limit, "hit size limit");
$slabs = mem_stats($sock, 'slabs');
is($slabs->{'active_slabs'}, 1, "1 active slab");
memcached-1.4.25/t/noreply.t 0000644 0001750 0001750 00000002300 12416643766 012564 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 9;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# Test that commands can take 'noreply' parameter.
print $sock "flush_all noreply\r\n";
print $sock "flush_all 0 noreply\r\n";
print $sock "verbosity 0 noreply\r\n";
print $sock "add noreply:foo 0 0 1 noreply\r\n1\r\n";
mem_get_is($sock, "noreply:foo", "1");
print $sock "set noreply:foo 0 0 1 noreply\r\n2\r\n";
mem_get_is($sock, "noreply:foo", "2");
print $sock "replace noreply:foo 0 0 1 noreply\r\n3\r\n";
mem_get_is($sock, "noreply:foo", "3");
print $sock "append noreply:foo 0 0 1 noreply\r\n4\r\n";
mem_get_is($sock, "noreply:foo", "34");
print $sock "prepend noreply:foo 0 0 1 noreply\r\n5\r\n";
my @result = mem_gets($sock, "noreply:foo");
ok($result[1] eq "534");
print $sock "cas noreply:foo 0 0 1 $result[0] noreply\r\n6\r\n";
mem_get_is($sock, "noreply:foo", "6");
print $sock "incr noreply:foo 3 noreply\r\n";
mem_get_is($sock, "noreply:foo", "9");
print $sock "decr noreply:foo 2 noreply\r\n";
mem_get_is($sock, "noreply:foo", "7");
print $sock "delete noreply:foo noreply\r\n";
mem_get_is($sock, "noreply:foo");
memcached-1.4.25/t/line-lengths.t 0000755 0001750 0001750 00000001004 12416643766 013470 0000000 0000000 #!/usr/bin/perl
use strict;
use FindBin qw($Bin);
our @files;
BEGIN {
chdir "$Bin/.." or die;
@files = ( "doc/protocol.txt" );
}
use Test::More tests => scalar(@files);
foreach my $f (@files) {
open(my $fh, $f) or die("Can't open $f");
my @long_lines = ();
my $line_number = 0;
while(<$fh>) {
$line_number++;
if(length($_) > 80) {
push(@long_lines, $line_number);
}
}
close($fh);
ok(@long_lines == 0, "$f has a long lines: @long_lines");
}
memcached-1.4.25/t/issue_67.t 0000644 0001750 0001750 00000004623 12416643766 012552 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 22;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use Carp qw(croak);
use Cwd;
my $builddir = getcwd;
$ENV{'MEMCACHED_PORT_FILENAME'} = "/tmp/ports.$$";
sub read_ports {
my %rv = ();
open(my $f, "/tmp/ports.$$") || die("Can't open ports file.");
while(<$f>) {
my ($type, $port) = split(/:\s+/);
$rv{$type} = $port + 0;
}
unlink "/tmp/ports.$$";
return %rv;
}
sub validate_port {
my ($name, $got, $expected) = @_;
# diag "Wanted $expected, got $got";
if ($expected == -1) {
ok(!defined($got), "$name expected no port, got $got");
} elsif ($expected == 0) {
ok($got != 11211, "$name expected random port (got $got)");
} else {
is($got, $expected, "$name");
}
}
sub run_server {
my ($args) = @_;
my $exe = "$builddir/memcached-debug";
croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe;
my $childpid = fork();
my $root = '';
$root = "-u root" if ($< == 0);
my $cmd = "$builddir/timedrun 10 $exe $root $args";
unless($childpid) {
exec $cmd;
exit; # NOTREACHED
}
for (1..20) {
if (-f "/tmp/ports.$$") {
return Memcached::Handle->new(pid => $childpid);
}
select undef, undef, undef, 0.10;
}
croak "Failed to start server.";
}
sub when {
my ($name, $params, $expected_tcp, $expected_udp) = @_;
my $server = run_server($params);
my %ports = read_ports();
validate_port($name, $ports{'TCP INET'}, $expected_tcp);
validate_port($name, $ports{'UDP INET'}, $expected_udp);
}
# Disabling the defaults since it conflicts with a running instance.
# when('no arguments', '', 11211, 11211);
when('specifying tcp port', '-p 11212', 11212, 11212);
when('specifying udp port', '-U 11222', 11222, 11222);
when('specifying tcp ephemeral port', '-p -1', 0, 0);
when('specifying udp ephemeral port', '-U -1', 0, 0);
when('tcp port disabled', '-p 0', -1, -1);
when('udp port disabled', '-U 0', -1, -1);
when('specifying tcp and udp ports', '-p 11232 -U 11233', 11232, 11233);
when('specifying tcp and disabling udp', '-p 11242 -U 0', 11242, -1);
when('specifying udp and disabling tcp', '-p -1 -U 11252', 0, 11252);
when('specifying tcp and ephemeral udp', '-p 11262 -U -1', 11262, 0);
when('specifying udp and ephemeral tcp', '-p -1 -U 11272', 0, 11272);
memcached-1.4.25/t/refhang.t 0000664 0001750 0001750 00000003366 12623270407 012511 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 127;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
# start up a server with 10 maximum connections
my $server = new_memcached("-m 6");
my $sock = $server->sock;
my $hangsock = $server->new_sock;
my $hangsock2 = $server->new_sock;
my $value = "B"x66560;
my $key = 0;
# These aren't set to expire.
my $mget = '';
for ($key = 0; $key < 120; $key++) {
$mget .= "key$key " if $key < 115;
print $sock "set key$key 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
chop $mget;
my $stats = mem_stats($sock, "items");
isnt($stats->{"items:31:evicted"}, "0", "check evicted");
my $lrutail_reflocked = $stats->{"items:31:lrutail_reflocked"};
is($lrutail_reflocked, "0", "check no slab lrutail_reflocked");
$stats = mem_stats($sock);
is($stats->{"lrutail_reflocked"}, "0", "check no total lrutail_reflocked");
# Don't intend to read the results, need to fill the socket.
# TODO: This test would be smarter if we cranked down the socket buffers
# first? Or perhaps used a unix domain socket.
print $hangsock "get $mget\r\n";
#sleep 8;
# Now we try a bunch of sets again, and see if they start coming back as OOM's
for ($key = 121; $key < 240; $key++) {
print $sock "set key$key 0 0 66560\r\n$value\r\n";
my $res = scalar <$sock>;
}
$stats = mem_stats($sock, "items");
# Some items will OOM since we only clear a handful per alloc attempt.
ok($stats->{"items:31:outofmemory"} > 0, "some ooms happened");
ok($stats->{"items:31:outofmemory"} < 20, "fewer than 20 ooms");
isnt($stats->{"items:31:lrutail_reflocked"}, "0", "nonzero lrutail_reflocked");
$stats = mem_stats($sock);
isnt($stats->{"lrutail_reflocked"}, "0", "nonzero total lrutail_reflocked");
memcached-1.4.25/t/issue_70.t 0000644 0001750 0001750 00000001144 12416643766 012537 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 4;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
print $sock "set issue70 0 0 0\r\n\r\n";
is (scalar <$sock>, "STORED\r\n", "stored issue70");
print $sock "set issue70 0 0 -1\r\n";
is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n");
print $sock "set issue70 0 0 4294967295\r\n";
is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n");
print $sock "set issue70 0 0 2147483647\r\nscoobyscoobydoo";
is (scalar <$sock>, "CLIENT_ERROR bad command line format\r\n");
memcached-1.4.25/t/getset.t 0000755 0001750 0001750 00000006174 12416643766 012407 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 539;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# set foo (and should get it)
print $sock "set foo 0 0 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
# add bar (and should get it)
print $sock "add bar 0 0 6\r\nbarval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored barval");
mem_get_is($sock, "bar", "barval");
# add foo (but shouldn't get new value)
print $sock "add foo 0 0 5\r\nfoov2\r\n";
is(scalar <$sock>, "NOT_STORED\r\n", "not stored");
mem_get_is($sock, "foo", "fooval");
# replace bar (should work)
print $sock "replace bar 0 0 6\r\nbarva2\r\n";
is(scalar <$sock>, "STORED\r\n", "replaced barval 2");
# replace notexist (shouldn't work)
print $sock "replace notexist 0 0 6\r\nbarva2\r\n";
is(scalar <$sock>, "NOT_STORED\r\n", "didn't replace notexist");
# delete foo.
print $sock "delete foo\r\n";
is(scalar <$sock>, "DELETED\r\n", "deleted foo");
# delete foo again. not found this time.
print $sock "delete foo\r\n";
is(scalar <$sock>, "NOT_FOUND\r\n", "deleted foo, but not found");
# add moo
#
print $sock "add moo 0 0 6\r\nmooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored barval");
mem_get_is($sock, "moo", "mooval");
# check-and-set (cas) failure case, try to set value with incorrect cas unique val
print $sock "cas moo 0 0 6 0\r\nMOOVAL\r\n";
is(scalar <$sock>, "EXISTS\r\n", "check and set with invalid id");
# test "gets", grab unique ID
print $sock "gets moo\r\n";
# VALUE moo 0 6 3084947704
#
my @retvals = split(/ /, scalar <$sock>);
my $data = scalar <$sock>; # grab data
my $dot = scalar <$sock>; # grab dot on line by itself
is($retvals[0], "VALUE", "get value using 'gets'");
my $unique_id = $retvals[4];
# clean off \r\n
$unique_id =~ s/\r\n$//;
ok($unique_id =~ /^\d+$/, "unique ID '$unique_id' is an integer");
# now test that we can store moo with the correct unique id
print $sock "cas moo 0 0 6 $unique_id\r\nMOOVAL\r\n";
is(scalar <$sock>, "STORED\r\n");
mem_get_is($sock, "moo", "MOOVAL");
# pipeling is okay
print $sock "set foo 0 0 6\r\nfooval\r\ndelete foo\r\nset foo 0 0 6\r\nfooval\r\ndelete foo\r\n";
is(scalar <$sock>, "STORED\r\n", "pipeline set");
is(scalar <$sock>, "DELETED\r\n", "pipeline delete");
is(scalar <$sock>, "STORED\r\n", "pipeline set");
is(scalar <$sock>, "DELETED\r\n", "pipeline delete");
# Test sets up to a large size around 1MB.
# Everything up to 1MB - 1k should succeed, everything 1MB +1k should fail.
my $len = 1024;
while ($len < 1024*1028) {
my $val = "B"x$len;
if ($len > (1024*1024)) {
# Ensure causing a memory overflow doesn't leave stale data.
print $sock "set foo_$len 0 0 3\r\nMOO\r\n";
is(scalar <$sock>, "STORED\r\n");
print $sock "set foo_$len 0 0 $len\r\n$val\r\n";
is(scalar <$sock>, "SERVER_ERROR object too large for cache\r\n", "failed to store size $len");
mem_get_is($sock, "foo_$len");
} else {
print $sock "set foo_$len 0 0 $len\r\n$val\r\n";
is(scalar <$sock>, "STORED\r\n", "stored size $len");
}
$len += 2048;
}
memcached-1.4.25/t/item_size_max.t 0000644 0001750 0001750 00000002274 12416643766 013743 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 7;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $stats = mem_stats($sock, ' settings');
# Ensure default still works.
is($stats->{item_size_max}, 1024 * 1024);
$server->stop();
# Should die.
eval {
$server = new_memcached('-I 1000');
};
ok($@ && $@ =~ m/^Failed/, "Shouldn't start with < 1k item max");
eval {
$server = new_memcached('-I 256m');
};
ok($@ && $@ =~ m/^Failed/, "Shouldn't start with > 128m item max");
# Minimum.
$server = new_memcached('-I 1024');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 1024);
$server->stop();
# Reasonable but unreasonable.
$server = new_memcached('-I 1049600');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 1049600);
$server->stop();
# Suffix kilobytes.
$server = new_memcached('-I 512k');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 524288);
$server->stop();
# Suffix megabytes.
$server = new_memcached('-I 32m');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 33554432);
$server->stop();
memcached-1.4.25/t/issue_14.t 0000644 0001750 0001750 00000001446 12416643766 012542 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 21;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $value = "B"x66560;
my $key = 0;
for ($key = 0; $key < 10; $key++) {
print $sock "set key$key 0 2 66560\r\n$value\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key$key");
}
#print $sock "stats slabs"
my $first_stats = mem_stats($sock, "slabs");
my $first_malloc = $first_stats->{total_malloced};
sleep(4);
for ($key = 10; $key < 20; $key++) {
print $sock "set key$key 0 2 66560\r\n$value\r\n";
is (scalar <$sock>, "STORED\r\n", "stored key$key");
}
my $second_stats = mem_stats($sock, "slabs");
my $second_malloc = $second_stats->{total_malloced};
is ($second_malloc, $first_malloc, "Memory grows..")
memcached-1.4.25/t/issue_108.t 0000644 0001750 0001750 00000001257 12416643766 012626 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 4;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $key = "del_key";
print $sock "add $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Added a key");
print $sock "delete $key 0\r\n";
is (scalar <$sock>, "DELETED\r\n", "Properly deleted with 0");
print $sock "add $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Added again a key");
print $sock "delete $key 0 noreply\r\n";
# will not reply, but a subsequent add will succeed
print $sock "add $key 0 0 1\r\nx\r\n";
is (scalar <$sock>, "STORED\r\n", "Add succeeded after quiet deletion.");
memcached-1.4.25/t/sasl/ 0000755 0001750 0001750 00000000000 11446413300 011713 5 0000000 0000000 memcached-1.4.25/t/sasl/memcached.conf 0000644 0001750 0001750 00000000117 11446413300 014407 0000000 0000000 mech_list: plain cram-md5
log_level: 5
sasldb_path: /tmp/test-memcached.sasldb
memcached-1.4.25/t/binary-get.t 0000755 0001750 0001750 00000000763 12416643766 013153 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 8;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
my $count = 1;
foreach my $blob ("mooo\0", "mumble\0\0\0\0\r\rblarg", "\0", "\r") {
my $key = "foo$count";
my $len = length($blob);
print "len is $len\n";
print $sock "set $key 0 0 $len\r\n$blob\r\n";
is(scalar <$sock>, "STORED\r\n", "stored $key");
mem_get_is($sock, $key, $blob);
$count++;
}
memcached-1.4.25/t/issue_68.t 0000644 0001750 0001750 00000000771 12416643766 012553 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 996;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
for (my $keyi = 1; $keyi < 250; $keyi++) {
my $key = "x" x $keyi;
print $sock "set $key 0 0 1\r\n9\r\n";
is (scalar <$sock>, "STORED\r\n", "stored $key");
mem_get_is($sock, $key, "9");
print $sock "incr $key 1\r\n";
is (scalar <$sock>, "10\r\n", "incr $key to 10");
mem_get_is($sock, $key, "10");
}
memcached-1.4.25/t/dash-M.t 0000644 0001750 0001750 00000001325 12416643766 012213 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-M -m 1');
my $sock = $server->sock;
my $value = "B" x 8192;
my $vallen = length($value);
my $resp = "STORED\r\n";
my $key = 0;
while($resp eq "STORED\r\n") {
print $sock "set dash$key 0 0 $vallen\r\n$value\r\n";
$key++;
$resp = scalar <$sock>;
}
my $max_stored = $key - 1;
plan tests => $max_stored + 1;
print $sock "set dash$key 0 0 $vallen\r\n$value\r\n";
is(scalar <$sock>, "SERVER_ERROR out of memory storing object\r\n",
"failed to add another one.");
for($key = 0; $key < $max_stored; $key++) {
mem_get_is $sock, "dash$key", $value, "Failed at dash$key";
}
memcached-1.4.25/memcached.h 0000664 0001750 0001750 00000053165 12623274034 012530 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/** \file
* The main memcached header holding commonly used data
* structures and function prototypes.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "protocol_binary.h"
#include "cache.h"
#include "sasl_defs.h"
/** Maximum length of a key. */
#define KEY_MAX_LENGTH 250
/** Size of an incr buf. */
#define INCR_MAX_STORAGE_LEN 24
#define DATA_BUFFER_SIZE 2048
#define UDP_READ_BUFFER_SIZE 65536
#define UDP_MAX_PAYLOAD_SIZE 1400
#define UDP_HEADER_SIZE 8
#define MAX_SENDBUF_SIZE (256 * 1024 * 1024)
/* I'm told the max length of a 64-bit num converted to string is 20 bytes.
* Plus a few for spaces, \r\n, \0 */
#define SUFFIX_SIZE 24
/** Initial size of list of items being returned by "get". */
#define ITEM_LIST_INITIAL 200
/** Initial size of list of CAS suffixes appended to "gets" lines. */
#define SUFFIX_LIST_INITIAL 20
/** Initial size of the sendmsg() scatter/gather array. */
#define IOV_LIST_INITIAL 400
/** Initial number of sendmsg() argument structures to allocate. */
#define MSG_LIST_INITIAL 10
/** High water marks for buffer shrinking */
#define READ_BUFFER_HIGHWAT 8192
#define ITEM_LIST_HIGHWAT 400
#define IOV_LIST_HIGHWAT 600
#define MSG_LIST_HIGHWAT 100
/* Binary protocol stuff */
#define MIN_BIN_PKT_LENGTH 16
#define BIN_PKT_HDR_WORDS (MIN_BIN_PKT_LENGTH/sizeof(uint32_t))
/* Initial power multiplier for the hash table */
#define HASHPOWER_DEFAULT 16
/*
* We only reposition items in the LRU queue if they haven't been repositioned
* in this many seconds. That saves us from churning on frequently-accessed
* items.
*/
#define ITEM_UPDATE_INTERVAL 60
/* unistd.h is here */
#if HAVE_UNISTD_H
# include
#endif
/* Slab sizing definitions. */
#define POWER_SMALLEST 1
#define POWER_LARGEST 256 /* actual cap is 255 */
#define SLAB_GLOBAL_PAGE_POOL 0 /* magic slab class for storing pages for reassignment */
#define CHUNK_ALIGN_BYTES 8
/* slab class max is a 6-bit number, -1. */
#define MAX_NUMBER_OF_SLAB_CLASSES (63 + 1)
/** How long an object can reasonably be assumed to be locked before
harvesting it on a low memory condition. Default: disabled. */
#define TAIL_REPAIR_TIME_DEFAULT 0
/* warning: don't use these macros with a function, as it evals its arg twice */
#define ITEM_get_cas(i) (((i)->it_flags & ITEM_CAS) ? \
(i)->data->cas : (uint64_t)0)
#define ITEM_set_cas(i,v) { \
if ((i)->it_flags & ITEM_CAS) { \
(i)->data->cas = v; \
} \
}
#define ITEM_key(item) (((char*)&((item)->data)) \
+ (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
#define ITEM_suffix(item) ((char*) &((item)->data) + (item)->nkey + 1 \
+ (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
#define ITEM_data(item) ((char*) &((item)->data) + (item)->nkey + 1 \
+ (item)->nsuffix \
+ (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
#define ITEM_ntotal(item) (sizeof(struct _stritem) + (item)->nkey + 1 \
+ (item)->nsuffix + (item)->nbytes \
+ (((item)->it_flags & ITEM_CAS) ? sizeof(uint64_t) : 0))
#define ITEM_clsid(item) ((item)->slabs_clsid & ~(3<<6))
#define STAT_KEY_LEN 128
#define STAT_VAL_LEN 128
/** Append a simple stat with a stat name, value format and value */
#define APPEND_STAT(name, fmt, val) \
append_stat(name, add_stats, c, fmt, val);
/** Append an indexed stat with a stat name (with format), value format
and value */
#define APPEND_NUM_FMT_STAT(name_fmt, num, name, fmt, val) \
klen = snprintf(key_str, STAT_KEY_LEN, name_fmt, num, name); \
vlen = snprintf(val_str, STAT_VAL_LEN, fmt, val); \
add_stats(key_str, klen, val_str, vlen, c);
/** Common APPEND_NUM_FMT_STAT format. */
#define APPEND_NUM_STAT(num, name, fmt, val) \
APPEND_NUM_FMT_STAT("%d:%s", num, name, fmt, val)
/**
* Callback for any function producing stats.
*
* @param key the stat's key
* @param klen length of the key
* @param val the stat's value in an ascii form (e.g. text form of a number)
* @param vlen length of the value
* @parm cookie magic callback cookie
*/
typedef void (*ADD_STAT)(const char *key, const uint16_t klen,
const char *val, const uint32_t vlen,
const void *cookie);
/*
* NOTE: If you modify this table you _MUST_ update the function state_text
*/
/**
* Possible states of a connection.
*/
enum conn_states {
conn_listening, /**< the socket which listens for connections */
conn_new_cmd, /**< Prepare connection for next command */
conn_waiting, /**< waiting for a readable socket */
conn_read, /**< reading in a command line */
conn_parse_cmd, /**< try to parse a command from the input buffer */
conn_write, /**< writing out a simple response */
conn_nread, /**< reading in a fixed number of bytes */
conn_swallow, /**< swallowing unnecessary bytes w/o storing */
conn_closing, /**< closing this connection */
conn_mwrite, /**< writing out many items sequentially */
conn_closed, /**< connection is closed */
conn_max_state /**< Max state value (used for assertion) */
};
enum bin_substates {
bin_no_state,
bin_reading_set_header,
bin_reading_cas_header,
bin_read_set_value,
bin_reading_get_key,
bin_reading_stat,
bin_reading_del_header,
bin_reading_incr_header,
bin_read_flush_exptime,
bin_reading_sasl_auth,
bin_reading_sasl_auth_data,
bin_reading_touch_key,
};
enum protocol {
ascii_prot = 3, /* arbitrary value. */
binary_prot,
negotiating_prot /* Discovering the protocol */
};
enum network_transport {
local_transport, /* Unix sockets*/
tcp_transport,
udp_transport
};
enum pause_thread_types {
PAUSE_WORKER_THREADS = 0,
PAUSE_ALL_THREADS,
RESUME_ALL_THREADS,
RESUME_WORKER_THREADS
};
#define IS_UDP(x) (x == udp_transport)
#define NREAD_ADD 1
#define NREAD_SET 2
#define NREAD_REPLACE 3
#define NREAD_APPEND 4
#define NREAD_PREPEND 5
#define NREAD_CAS 6
enum store_item_type {
NOT_STORED=0, STORED, EXISTS, NOT_FOUND
};
enum delta_result_type {
OK, NON_NUMERIC, EOM, DELTA_ITEM_NOT_FOUND, DELTA_ITEM_CAS_MISMATCH
};
/** Time relative to server start. Smaller than time_t on 64-bit systems. */
typedef unsigned int rel_time_t;
/** Stats stored per slab (and per thread). */
struct slab_stats {
uint64_t set_cmds;
uint64_t get_hits;
uint64_t touch_hits;
uint64_t delete_hits;
uint64_t cas_hits;
uint64_t cas_badval;
uint64_t incr_hits;
uint64_t decr_hits;
};
/**
* Stats stored per-thread.
*/
struct thread_stats {
pthread_mutex_t mutex;
uint64_t get_cmds;
uint64_t get_misses;
uint64_t touch_cmds;
uint64_t touch_misses;
uint64_t delete_misses;
uint64_t incr_misses;
uint64_t decr_misses;
uint64_t cas_misses;
uint64_t bytes_read;
uint64_t bytes_written;
uint64_t flush_cmds;
uint64_t conn_yields; /* # of yields for connections (-R option)*/
uint64_t auth_cmds;
uint64_t auth_errors;
struct slab_stats slab_stats[MAX_NUMBER_OF_SLAB_CLASSES];
};
/**
* Global stats.
*/
struct stats {
pthread_mutex_t mutex;
unsigned int curr_items;
unsigned int total_items;
uint64_t curr_bytes;
unsigned int curr_conns;
unsigned int total_conns;
uint64_t rejected_conns;
uint64_t malloc_fails;
unsigned int reserved_fds;
unsigned int conn_structs;
uint64_t get_cmds;
uint64_t set_cmds;
uint64_t touch_cmds;
uint64_t get_hits;
uint64_t get_misses;
uint64_t touch_hits;
uint64_t touch_misses;
uint64_t evictions;
uint64_t reclaimed;
time_t started; /* when the process was started */
bool accepting_conns; /* whether we are currently accepting */
uint64_t listen_disabled_num;
unsigned int hash_power_level; /* Better hope it's not over 9000 */
uint64_t hash_bytes; /* size used for hash tables */
bool hash_is_expanding; /* If the hash table is being expanded */
uint64_t expired_unfetched; /* items reclaimed but never touched */
uint64_t evicted_unfetched; /* items evicted but never touched */
bool slab_reassign_running; /* slab reassign in progress */
uint64_t slabs_moved; /* times slabs were moved around */
uint64_t slab_reassign_rescues; /* items rescued during slab move */
uint64_t slab_reassign_evictions_nomem; /* valid items lost during slab move */
uint64_t slab_reassign_inline_reclaim; /* valid items lost during slab move */
uint64_t slab_reassign_busy_items; /* valid temporarily unmovable */
uint64_t lru_crawler_starts; /* Number of item crawlers kicked off */
bool lru_crawler_running; /* crawl in progress */
uint64_t lru_maintainer_juggles; /* number of LRU bg pokes */
uint64_t time_in_listen_disabled_us; /* elapsed time in microseconds while server unable to process new connections */
struct timeval maxconns_entered; /* last time maxconns entered */
};
#define MAX_VERBOSITY_LEVEL 2
/* When adding a setting, be sure to update process_stat_settings */
/**
* Globally accessible settings as derived from the commandline.
*/
struct settings {
size_t maxbytes;
int maxconns;
int port;
int udpport;
char *inter;
int verbose;
rel_time_t oldest_live; /* ignore existing items older than this */
uint64_t oldest_cas; /* ignore existing items with CAS values lower than this */
int evict_to_free;
char *socketpath; /* path to unix socket if using local socket */
int access; /* access mask (a la chmod) for unix domain socket */
double factor; /* chunk size growth factor */
int chunk_size;
int num_threads; /* number of worker (without dispatcher) libevent threads to run */
int num_threads_per_udp; /* number of worker threads serving each udp socket */
char prefix_delimiter; /* character that marks a key prefix (for stats) */
int detail_enabled; /* nonzero if we're collecting detailed stats */
int reqs_per_event; /* Maximum number of io to process on each
io-event. */
bool use_cas;
enum protocol binding_protocol;
int backlog;
int item_size_max; /* Maximum item size, and upper end for slabs */
bool sasl; /* SASL on/off */
bool maxconns_fast; /* Whether or not to early close connections */
bool lru_crawler; /* Whether or not to enable the autocrawler thread */
bool lru_maintainer_thread; /* LRU maintainer background thread */
bool slab_reassign; /* Whether or not slab reassignment is allowed */
int slab_automove; /* Whether or not to automatically move slabs */
int hashpower_init; /* Starting hash power level */
bool shutdown_command; /* allow shutdown command */
int tail_repair_time; /* LRU tail refcount leak repair time */
bool flush_enabled; /* flush_all enabled */
char *hash_algorithm; /* Hash algorithm in use */
int lru_crawler_sleep; /* Microsecond sleep between items */
uint32_t lru_crawler_tocrawl; /* Number of items to crawl per run */
int hot_lru_pct; /* percentage of slab space for HOT_LRU */
int warm_lru_pct; /* percentage of slab space for WARM_LRU */
int crawls_persleep; /* Number of LRU crawls to run before sleeping */
bool expirezero_does_not_evict; /* exptime == 0 goes into NOEXP_LRU */
};
extern struct stats stats;
extern time_t process_started;
extern struct settings settings;
#define ITEM_LINKED 1
#define ITEM_CAS 2
/* temp */
#define ITEM_SLABBED 4
/* Item was fetched at least once in its lifetime */
#define ITEM_FETCHED 8
/* Appended on fetch, removed on LRU shuffling */
#define ITEM_ACTIVE 16
/**
* Structure for storing items within memcached.
*/
typedef struct _stritem {
/* Protected by LRU locks */
struct _stritem *next;
struct _stritem *prev;
/* Rest are protected by an item lock */
struct _stritem *h_next; /* hash chain next */
rel_time_t time; /* least recent access */
rel_time_t exptime; /* expire time */
int nbytes; /* size of data */
unsigned short refcount;
uint8_t nsuffix; /* length of flags-and-length string */
uint8_t it_flags; /* ITEM_* above */
uint8_t slabs_clsid;/* which slab class we're in */
uint8_t nkey; /* key length, w/terminating null and padding */
/* this odd type prevents type-punning issues when we do
* the little shuffle to save space when not using CAS. */
union {
uint64_t cas;
char end;
} data[];
/* if it_flags & ITEM_CAS we have 8 bytes CAS */
/* then null-terminated key */
/* then " flags length\r\n" (no terminating null) */
/* then data with terminating \r\n (no terminating null; it's binary!) */
} item;
typedef struct {
struct _stritem *next;
struct _stritem *prev;
struct _stritem *h_next; /* hash chain next */
rel_time_t time; /* least recent access */
rel_time_t exptime; /* expire time */
int nbytes; /* size of data */
unsigned short refcount;
uint8_t nsuffix; /* length of flags-and-length string */
uint8_t it_flags; /* ITEM_* above */
uint8_t slabs_clsid;/* which slab class we're in */
uint8_t nkey; /* key length, w/terminating null and padding */
uint32_t remaining; /* Max keys to crawl per slab per invocation */
} crawler;
typedef struct {
pthread_t thread_id; /* unique ID of this thread */
struct event_base *base; /* libevent handle this thread uses */
struct event notify_event; /* listen event for notify pipe */
int notify_receive_fd; /* receiving end of notify pipe */
int notify_send_fd; /* sending end of notify pipe */
struct thread_stats stats; /* Stats generated by this thread */
struct conn_queue *new_conn_queue; /* queue of new connections to handle */
cache_t *suffix_cache; /* suffix cache */
} LIBEVENT_THREAD;
typedef struct {
pthread_t thread_id; /* unique ID of this thread */
struct event_base *base; /* libevent handle this thread uses */
} LIBEVENT_DISPATCHER_THREAD;
/**
* The structure representing a connection into memcached.
*/
typedef struct conn conn;
struct conn {
int sfd;
sasl_conn_t *sasl_conn;
bool authenticated;
enum conn_states state;
enum bin_substates substate;
rel_time_t last_cmd_time;
struct event event;
short ev_flags;
short which; /** which events were just triggered */
char *rbuf; /** buffer to read commands into */
char *rcurr; /** but if we parsed some already, this is where we stopped */
int rsize; /** total allocated size of rbuf */
int rbytes; /** how much data, starting from rcur, do we have unparsed */
char *wbuf;
char *wcurr;
int wsize;
int wbytes;
/** which state to go into after finishing current write */
enum conn_states write_and_go;
void *write_and_free; /** free this memory after finishing writing */
char *ritem; /** when we read in an item's value, it goes here */
int rlbytes;
/* data for the nread state */
/**
* item is used to hold an item structure created after reading the command
* line of set/add/replace commands, but before we finished reading the actual
* data. The data is read into ITEM_data(item) to avoid extra copying.
*/
void *item; /* for commands set/add/replace */
/* data for the swallow state */
int sbytes; /* how many bytes to swallow */
/* data for the mwrite state */
struct iovec *iov;
int iovsize; /* number of elements allocated in iov[] */
int iovused; /* number of elements used in iov[] */
struct msghdr *msglist;
int msgsize; /* number of elements allocated in msglist[] */
int msgused; /* number of elements used in msglist[] */
int msgcurr; /* element in msglist[] being transmitted now */
int msgbytes; /* number of bytes in current msg */
item **ilist; /* list of items to write out */
int isize;
item **icurr;
int ileft;
char **suffixlist;
int suffixsize;
char **suffixcurr;
int suffixleft;
enum protocol protocol; /* which protocol this connection speaks */
enum network_transport transport; /* what transport is used by this connection */
/* data for UDP clients */
int request_id; /* Incoming UDP request ID, if this is a UDP "connection" */
struct sockaddr_in6 request_addr; /* udp: Who sent the most recent request */
socklen_t request_addr_size;
unsigned char *hdrbuf; /* udp packet headers */
int hdrsize; /* number of headers' worth of space is allocated */
bool noreply; /* True if the reply should not be sent. */
/* current stats command */
struct {
char *buffer;
size_t size;
size_t offset;
} stats;
/* Binary protocol stuff */
/* This is where the binary header goes */
protocol_binary_request_header binary_header;
uint64_t cas; /* the cas to return */
short cmd; /* current command being processed */
int opaque;
int keylen;
conn *next; /* Used for generating a list of conn structures */
LIBEVENT_THREAD *thread; /* Pointer to the thread object serving this connection */
};
/* array of conn structures, indexed by file descriptor */
extern conn **conns;
/* current time of day (updated periodically) */
extern volatile rel_time_t current_time;
/* TODO: Move to slabs.h? */
extern volatile int slab_rebalance_signal;
struct slab_rebalance {
void *slab_start;
void *slab_end;
void *slab_pos;
int s_clsid;
int d_clsid;
uint32_t busy_items;
uint32_t rescues;
uint32_t evictions_nomem;
uint32_t inline_reclaim;
uint8_t done;
};
extern struct slab_rebalance slab_rebal;
/*
* Functions
*/
void do_accept_new_conns(const bool do_accept);
enum delta_result_type do_add_delta(conn *c, const char *key,
const size_t nkey, const bool incr,
const int64_t delta, char *buf,
uint64_t *cas, const uint32_t hv);
enum store_item_type do_store_item(item *item, int comm, conn* c, const uint32_t hv);
conn *conn_new(const int sfd, const enum conn_states init_state, const int event_flags, const int read_buffer_size, enum network_transport transport, struct event_base *base);
extern int daemonize(int nochdir, int noclose);
#define mutex_lock(x) pthread_mutex_lock(x)
#define mutex_unlock(x) pthread_mutex_unlock(x)
#include "stats.h"
#include "slabs.h"
#include "assoc.h"
#include "items.h"
#include "trace.h"
#include "hash.h"
#include "util.h"
/*
* Functions such as the libevent-related calls that need to do cross-thread
* communication in multithreaded mode (rather than actually doing the work
* in the current thread) are called via "dispatch_" frontends, which are
* also #define-d to directly call the underlying code in singlethreaded mode.
*/
void memcached_thread_init(int nthreads, struct event_base *main_base);
int dispatch_event_add(int thread, conn *c);
void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags, int read_buffer_size, enum network_transport transport);
/* Lock wrappers for cache functions that are called from main loop. */
enum delta_result_type add_delta(conn *c, const char *key,
const size_t nkey, const int incr,
const int64_t delta, char *buf,
uint64_t *cas);
void accept_new_conns(const bool do_accept);
conn *conn_from_freelist(void);
bool conn_add_to_freelist(conn *c);
int is_listen_thread(void);
item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes);
item *item_get(const char *key, const size_t nkey);
item *item_touch(const char *key, const size_t nkey, uint32_t exptime);
int item_link(item *it);
void item_remove(item *it);
int item_replace(item *it, item *new_it, const uint32_t hv);
void item_unlink(item *it);
void item_update(item *it);
void item_lock(uint32_t hv);
void *item_trylock(uint32_t hv);
void item_trylock_unlock(void *arg);
void item_unlock(uint32_t hv);
void pause_threads(enum pause_thread_types type);
unsigned short refcount_incr(unsigned short *refcount);
unsigned short refcount_decr(unsigned short *refcount);
void STATS_LOCK(void);
void STATS_UNLOCK(void);
void threadlocal_stats_reset(void);
void threadlocal_stats_aggregate(struct thread_stats *stats);
void slab_stats_aggregate(struct thread_stats *stats, struct slab_stats *out);
/* Stat processing functions */
void append_stat(const char *name, ADD_STAT add_stats, conn *c,
const char *fmt, ...);
enum store_item_type store_item(item *item, int comm, conn *c);
#if HAVE_DROP_PRIVILEGES
extern void drop_privileges(void);
#else
#define drop_privileges()
#endif
/* If supported, give compiler hints for branch prediction. */
#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
#define __builtin_expect(x, expected_value) (x)
#endif
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
memcached-1.4.25/items.h 0000664 0001750 0001750 00000003337 12623274034 011737 0000000 0000000 /* See items.c */
uint64_t get_cas_id(void);
/*@null@*/
item *do_item_alloc(char *key, const size_t nkey, const int flags, const rel_time_t exptime, const int nbytes, const uint32_t cur_hv);
void item_free(item *it);
bool item_size_ok(const size_t nkey, const int flags, const int nbytes);
int do_item_link(item *it, const uint32_t hv); /** may fail if transgresses limits */
void do_item_unlink(item *it, const uint32_t hv);
void do_item_unlink_nolock(item *it, const uint32_t hv);
void do_item_remove(item *it);
void do_item_update(item *it); /** update LRU time to current and reposition */
void do_item_update_nolock(item *it);
int do_item_replace(item *it, item *new_it, const uint32_t hv);
int item_is_flushed(item *it);
/*@null@*/
char *item_cachedump(const unsigned int slabs_clsid, const unsigned int limit, unsigned int *bytes);
void item_stats(ADD_STAT add_stats, void *c);
void item_stats_totals(ADD_STAT add_stats, void *c);
/*@null@*/
void item_stats_sizes(ADD_STAT add_stats, void *c);
item *do_item_get(const char *key, const size_t nkey, const uint32_t hv);
item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv);
void item_stats_reset(void);
extern pthread_mutex_t lru_locks[POWER_LARGEST];
enum crawler_result_type {
CRAWLER_OK=0, CRAWLER_RUNNING, CRAWLER_BADCLASS, CRAWLER_NOTSTARTED
};
int start_lru_maintainer_thread(void);
int stop_lru_maintainer_thread(void);
int init_lru_maintainer(void);
void lru_maintainer_pause(void);
void lru_maintainer_resume(void);
int start_item_crawler_thread(void);
int stop_item_crawler_thread(void);
int init_lru_crawler(void);
enum crawler_result_type lru_crawler_crawl(char *slabs);
void lru_crawler_pause(void);
void lru_crawler_resume(void);
memcached-1.4.25/timedrun.c 0000644 0001750 0001750 00000004560 12606105207 012431 0000000 0000000 #include
#include
#include
#include
#include
#include
#include
static int caught = 0;
static void caught_signal(int which)
{
caught = which;
}
static int wait_for_process(pid_t pid)
{
int rv = EX_SOFTWARE;
int stats = 0;
int i = 0;
struct sigaction sig_handler;
sig_handler.sa_handler = caught_signal;
sig_handler.sa_flags = 0;
sigaction(SIGALRM, &sig_handler, NULL);
sigaction(SIGHUP, &sig_handler, NULL);
sigaction(SIGINT, &sig_handler, NULL);
sigaction(SIGTERM, &sig_handler, NULL);
sigaction(SIGPIPE, &sig_handler, NULL);
/* Loop forever waiting for the process to quit */
for (i = 0; ;i++) {
pid_t p = waitpid(pid, &stats, 0);
if (p == pid) {
/* child exited. Let's get out of here */
rv = WIFEXITED(stats) ?
WEXITSTATUS(stats) :
(0x80 | WTERMSIG(stats));
break;
} else {
int sig = 0;
switch (i) {
case 0:
/* On the first iteration, pass the signal through */
sig = caught > 0 ? caught : SIGTERM;
if (caught == SIGALRM) {
fprintf(stderr, "Timeout.. killing the process\n");
}
break;
case 1:
sig = SIGTERM;
break;
default:
sig = SIGKILL;
break;
}
if (kill(pid, sig) < 0) {
/* Kill failed. Must have lost the process. :/ */
perror("lost child when trying to kill");
}
/* Wait up to 5 seconds for the pid */
alarm(5);
}
}
return rv;
}
static int spawn_and_wait(char **argv)
{
int rv = EX_SOFTWARE;
pid_t pid = fork();
switch (pid) {
case -1:
perror("fork");
rv = EX_OSERR;
break; /* NOTREACHED */
case 0:
execvp(argv[0], argv);
perror("exec");
rv = EX_SOFTWARE;
break; /* NOTREACHED */
default:
rv = wait_for_process(pid);
}
return rv;
}
int main(int argc, char **argv)
{
int naptime = 0;
assert(argc > 2);
naptime = atoi(argv[1]);
assert(naptime > 0 && naptime < 1800);
alarm(naptime);
return spawn_and_wait(argv+2);
}
memcached-1.4.25/cache.c 0000644 0001750 0001750 00000007265 12606105207 011652 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include
#include
#include
#ifndef NDEBUG
#include
#endif
#include "cache.h"
#ifndef NDEBUG
const uint64_t redzone_pattern = 0xdeadbeefcafebabe;
int cache_error = 0;
#endif
const int initial_pool_size = 64;
cache_t* cache_create(const char *name, size_t bufsize, size_t align,
cache_constructor_t* constructor,
cache_destructor_t* destructor) {
cache_t* ret = calloc(1, sizeof(cache_t));
char* nm = strdup(name);
void** ptr = calloc(initial_pool_size, sizeof(void*));
if (ret == NULL || nm == NULL || ptr == NULL ||
pthread_mutex_init(&ret->mutex, NULL) == -1) {
free(ret);
free(nm);
free(ptr);
return NULL;
}
ret->name = nm;
ret->ptr = ptr;
ret->freetotal = initial_pool_size;
ret->constructor = constructor;
ret->destructor = destructor;
#ifndef NDEBUG
ret->bufsize = bufsize + 2 * sizeof(redzone_pattern);
#else
ret->bufsize = bufsize;
#endif
return ret;
}
static inline void* get_object(void *ptr) {
#ifndef NDEBUG
uint64_t *pre = ptr;
return pre + 1;
#else
return ptr;
#endif
}
void cache_destroy(cache_t *cache) {
while (cache->freecurr > 0) {
void *ptr = cache->ptr[--cache->freecurr];
if (cache->destructor) {
cache->destructor(get_object(ptr), NULL);
}
free(ptr);
}
free(cache->name);
free(cache->ptr);
pthread_mutex_destroy(&cache->mutex);
free(cache);
}
void* cache_alloc(cache_t *cache) {
void *ret;
void *object;
pthread_mutex_lock(&cache->mutex);
if (cache->freecurr > 0) {
ret = cache->ptr[--cache->freecurr];
object = get_object(ret);
} else {
object = ret = malloc(cache->bufsize);
if (ret != NULL) {
object = get_object(ret);
if (cache->constructor != NULL &&
cache->constructor(object, NULL, 0) != 0) {
free(ret);
object = NULL;
}
}
}
pthread_mutex_unlock(&cache->mutex);
#ifndef NDEBUG
if (object != NULL) {
/* add a simple form of buffer-check */
uint64_t *pre = ret;
*pre = redzone_pattern;
ret = pre+1;
memcpy(((char*)ret) + cache->bufsize - (2 * sizeof(redzone_pattern)),
&redzone_pattern, sizeof(redzone_pattern));
}
#endif
return object;
}
void cache_free(cache_t *cache, void *ptr) {
pthread_mutex_lock(&cache->mutex);
#ifndef NDEBUG
/* validate redzone... */
if (memcmp(((char*)ptr) + cache->bufsize - (2 * sizeof(redzone_pattern)),
&redzone_pattern, sizeof(redzone_pattern)) != 0) {
raise(SIGABRT);
cache_error = 1;
pthread_mutex_unlock(&cache->mutex);
return;
}
uint64_t *pre = ptr;
--pre;
if (*pre != redzone_pattern) {
raise(SIGABRT);
cache_error = -1;
pthread_mutex_unlock(&cache->mutex);
return;
}
ptr = pre;
#endif
if (cache->freecurr < cache->freetotal) {
cache->ptr[cache->freecurr++] = ptr;
} else {
/* try to enlarge free connections array */
size_t newtotal = cache->freetotal * 2;
void **new_free = realloc(cache->ptr, sizeof(char *) * newtotal);
if (new_free) {
cache->freetotal = newtotal;
cache->ptr = new_free;
cache->ptr[cache->freecurr++] = ptr;
} else {
if (cache->destructor) {
cache->destructor(ptr, NULL);
}
free(ptr);
}
}
pthread_mutex_unlock(&cache->mutex);
}
memcached-1.4.25/daemon.c 0000644 0001750 0001750 00000006012 12606105207 012037 0000000 0000000 /* $Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $ */
/* $NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $ */
/*-
* Copyright (c) 1990, 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. 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 __SUNPRO_C || defined __DECC || defined __HP_cc
# pragma ident "@(#)$Header: /cvsroot/wikipedia/willow/src/bin/willow/daemon.c,v 1.1 2005/05/02 19:15:21 kateturner Exp $"
# pragma ident "$NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $"
#endif
#include
#include
#include
#include
#include "memcached.h"
int daemonize(int nochdir, int noclose)
{
int fd;
switch (fork()) {
case -1:
return (-1);
case 0:
break;
default:
_exit(EXIT_SUCCESS);
}
if (setsid() == -1)
return (-1);
if (nochdir == 0) {
if(chdir("/") != 0) {
perror("chdir");
return (-1);
}
}
if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
if(dup2(fd, STDIN_FILENO) < 0) {
perror("dup2 stdin");
return (-1);
}
if(dup2(fd, STDOUT_FILENO) < 0) {
perror("dup2 stdout");
return (-1);
}
if(dup2(fd, STDERR_FILENO) < 0) {
perror("dup2 stderr");
return (-1);
}
if (fd > STDERR_FILENO) {
if(close(fd) < 0) {
perror("close");
return (-1);
}
}
}
return (0);
}
memcached-1.4.25/doc/ 0000755 0001750 0001750 00000000000 12623542632 011264 5 0000000 0000000 memcached-1.4.25/doc/CONTRIBUTORS 0000664 0001750 0001750 00000003036 12623270407 013066 0000000 0000000 MEMCACHED CONTRIBUTORS
This file contains a list of people who have contributed code and
effort to the memcached project. If you don't see your name mentioned
send email to the memcached mailing list so you can be immortalized.
Also see the ChangeLog for even more people who have helped over the
years by submitting fixes, patches and reporting bugs.
A list is generated from git here: http://memcached.org/about
Major authors:
--------------
Brad Fitzpatrick -- maintainer, original implementations
Anatoly Vorobey -- lots of the modern server code
Steven Grimm -- iov writing (less CPU), UDP mode,
non-2.0 slab mantissas, multithread, ...
Other Contributors
------------------
Evan Martin
Nathan Neulinger
Eric Hodel
Michael Johnson
Paul Querna
Jamie McCarthy
Philip Neustrom
Andrew O'Brien
Josh Rotenberg
Robin H. Johnson
Tim Yardley
Paolo Borelli
Eli Bingham
Jean-Francois Bustarret
Paul G
Paul Lindner
Dormando
Dustin Sallings
Chris Goffinet
Tomash Brechko
Brian Aker
Trond Norbye
memcached-1.4.25/doc/new_lru.txt 0000664 0001750 0001750 00000003664 12623270407 013431 0000000 0000000 In versions new enough to have the `-o lru_maintainer` option, a new LRU
mechanic is available.
Previously: Each slab class has an independent doubly-linked list comprising
its LRU. Items are pulled from the bottom and either reclaimed or evicted as
needed.
Now, enabling `-o lru_maintainer` changes all of the behavior below:
* LRU's are now split between HOT, WARM, and COLD LRU's. New items enter the
HOT LRU.
* LRU updates only happen as items reach the bottom of an LRU. If active in
HOT, stay in HOT, if active in WARM, stay in WARM. If active in COLD, move
to WARM.
* HOT/WARM each capped at 32% of memory available for that slab class. COLD
is uncapped (by default, as of this writing).
* Items flow from HOT/WARM into COLD.
* A background thread exists which shuffles items between/within the LRU's as
limits are reached.
* The background thread can also control the lru_crawler, if enabled.
The primary goal is to better protect active items from "scanning". Items
which are never hit again will flow from HOT, through COLD, and out the
bottom. Items occasionally active (reaching COLD, but being hit before
eviction), move to WARM. There they can stay relatively protected.
A secondary goal is to improve latency. The LRU locks are no longer used on
item reads, only during sets and from the background thread. Also the
background thread is likely to find expired items and release them back to the
slab class asynchronously, which speeds up new allocations.
It is recommended to use this feature with the lru crawler as well:
`memcached -o lru_maintainer,lru_crawler` - Then it will automatically scan
slab classes for items with expired TTL's. If your items are always set to
never expire, you can omit this option safely.
An extra option: `-o expirezero_does_not_evict` (when used with
lru_maintainer) will make items with an expiration time of 0 unevictable. Take
caution as this will crowd out memory available for other items.
memcached-1.4.25/doc/threads.txt 0000664 0001750 0001750 00000004660 12623270407 013405 0000000 0000000 Multithreading in memcached *was* originally simple:
- One listener thread
- N "event worker" threads
- Some misc background threads
Each worker thread is assigned connections, and runs its own epoll loop. The
central hash table, LRU lists, and some statistics counters are covered by
global locks. Protocol parsing, data transfer happens in threads. Data lookups
and modifications happen under central locks.
THIS HAS CHANGED!
- A secondary small hash table of locks is used to lock an item by its hash
value. This prevents multiple threads from acting on the same item at the
same time.
- This secondary hash table is mapped to the central hash tables buckets. This
allows multiple threads to access the hash table in parallel. Only one
thread may read or write against a particular hash table bucket.
- atomic refcounts per item are used to manage garbage collection and
mutability.
- When pulling an item off of the LRU tail for eviction or re-allocation, the
system must attempt to lock the item's bucket, which is done with a trylock
to avoid deadlocks. If a bucket is in use (and not by that thread) it will
walk up the LRU a little in an attempt to fetch a non-busy item.
- Each LRU (and sub-LRU's in newer modes) has an independent lock.
- Raw accessses to the slab class are protected by a global slabs_lock. This
is a short lock which covers pushing and popping free memory.
- item_lock must be held while modifying an item.
- slabs_lock must be held while modifying the ITEM_SLABBED flag bit within an item.
- ITEM_LINKED must not be set before an item has a key copied into it.
- items without ITEM_SLABBED set cannot have their memory zeroed out.
LOCK ORDERS:
(incomplete as of writing, sorry):
item_lock -> lru_lock -> slabs_lock
lru_lock -> item_trylock
Various stats_locks should never have other locks as dependencies.
Various locks exist for background threads. They can be used to pause the
thread execution or update settings while the threads are idle. They may call
item or lru locks.
A low priority isssue:
- If you remove the per-thread stats lock, CPU usage goes down by less than a
point of a percent, and it does not improve scalability.
- In my testing, the remaining global STATS_LOCK calls never seem to collide.
Yes, more stats can be moved to threads, and those locks can actually be
removed entirely on x86-64 systems. However my tests haven't shown that as
beneficial so far, so I've prioritized other work.
memcached-1.4.25/doc/Makefile.am 0000664 0001750 0001750 00000001247 12623270407 013244 0000000 0000000 man_MANS = memcached.1
EXTRA_DIST = *.txt
BUILT_SOURCES=
if BUILD_SPECIFICATIONS
BUILT_SOURCES += protocol-binary.txt protocol-binary-range.txt
MOSTLYCLEANFILES = protocol-binary.txt protocol-binary-range.txt
endif
protocol-binary.txt: $(.TARGET:R).full
@XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
protocol-binary-range.txt: $(.TARGET:R).full
@XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
protocol-binary.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
@XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
protocol-binary-range.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
@XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
memcached-1.4.25/doc/protocol.txt 0000664 0001750 0001750 00000127032 12623274034 013613 0000000 0000000 Protocol
--------
Clients of memcached communicate with server through TCP connections.
(A UDP interface is also available; details are below under "UDP
protocol.") A given running memcached server listens on some
(configurable) port; clients connect to that port, send commands to
the server, read responses, and eventually close the connection.
There is no need to send any command to end the session. A client may
just close the connection at any moment it no longer needs it. Note,
however, that clients are encouraged to cache their connections rather
than reopen them every time they need to store or retrieve data. This
is because memcached is especially designed to work very efficiently
with a very large number (many hundreds, more than a thousand if
necessary) of open connections. Caching connections will eliminate the
overhead associated with establishing a TCP connection (the overhead
of preparing for a new connection on the server side is insignificant
compared to this).
There are two kinds of data sent in the memcache protocol: text lines
and unstructured data. Text lines are used for commands from clients
and responses from servers. Unstructured data is sent when a client
wants to store or retrieve data. The server will transmit back
unstructured data in exactly the same way it received it, as a byte
stream. The server doesn't care about byte order issues in
unstructured data and isn't aware of them. There are no limitations on
characters that may appear in unstructured data; however, the reader
of such data (either a client or a server) will always know, from a
preceding text line, the exact length of the data block being
transmitted.
Text lines are always terminated by \r\n. Unstructured data is _also_
terminated by \r\n, even though \r, \n or any other 8-bit characters
may also appear inside the data. Therefore, when a client retrieves
data from a server, it must use the length of the data block (which it
will be provided with) to determine where the data block ends, and not
the fact that \r\n follows the end of the data block, even though it
does.
Keys
----
Data stored by memcached is identified with the help of a key. A key
is a text string which should uniquely identify the data for clients
that are interested in storing and retrieving it. Currently the
length limit of a key is set at 250 characters (of course, normally
clients wouldn't need to use such long keys); the key must not include
control characters or whitespace.
Commands
--------
There are three types of commands.
Storage commands (there are six: "set", "add", "replace", "append"
"prepend" and "cas") ask the server to store some data identified by a
key. The client sends a command line, and then a data block; after
that the client expects one line of response, which will indicate
success or failure.
Retrieval commands (there are two: "get" and "gets") ask the server to
retrieve data corresponding to a set of keys (one or more keys in one
request). The client sends a command line, which includes all the
requested keys; after that for each item the server finds it sends to
the client one response line with information about the item, and one
data block with the item's data; this continues until the server
finished with the "END" response line.
All other commands don't involve unstructured data. In all of them,
the client sends one command line, and expects (depending on the
command) either one line of response, or several lines of response
ending with "END" on the last line.
A command line always starts with the name of the command, followed by
parameters (if any) delimited by whitespace. Command names are
lower-case and are case-sensitive.
Expiration times
----------------
Some commands involve a client sending some kind of expiration time
(relative to an item or to an operation requested by the client) to
the server. In all such cases, the actual value sent may either be
Unix time (number of seconds since January 1, 1970, as a 32-bit
value), or a number of seconds starting from current time. In the
latter case, this number of seconds may not exceed 60*60*24*30 (number
of seconds in 30 days); if the number sent by a client is larger than
that, the server will consider it to be real Unix time value rather
than an offset from current time.
Error strings
-------------
Each command sent by a client may be answered with an error string
from the server. These error strings come in three types:
- "ERROR\r\n"
means the client sent a nonexistent command name.
- "CLIENT_ERROR \r\n"
means some sort of client error in the input line, i.e. the input
doesn't conform to the protocol in some way. is a
human-readable error string.
- "SERVER_ERROR \r\n"
means some sort of server error prevents the server from carrying
out the command. is a human-readable error string. In cases
of severe server errors, which make it impossible to continue
serving the client (this shouldn't normally happen), the server will
close the connection after sending the error line. This is the only
case in which the server closes a connection to a client.
In the descriptions of individual commands below, these error lines
are not again specifically mentioned, but clients must allow for their
possibility.
Storage commands
----------------
First, the client sends a command line which looks like this:
[noreply]\r\n
cas [noreply]\r\n
- is "set", "add", "replace", "append" or "prepend"
"set" means "store this data".
"add" means "store this data, but only if the server *doesn't* already
hold data for this key".
"replace" means "store this data, but only if the server *does*
already hold data for this key".
"append" means "add this data to an existing key after existing data".
"prepend" means "add this data to an existing key before existing data".
The append and prepend commands do not accept flags or exptime.
They update existing data portions, and ignore new flag and exptime
settings.
"cas" is a check and set operation which means "store this data but
only if no one else has updated since I last fetched it."
- is the key under which the client asks to store the data
- is an arbitrary 16-bit unsigned integer (written out in
decimal) that the server stores along with the data and sends back
when the item is retrieved. Clients may use this as a bit field to
store data-specific information; this field is opaque to the server.
Note that in memcached 1.2.1 and higher, flags may be 32-bits, instead
of 16, but you might want to restrict yourself to 16 bits for
compatibility with older versions.
- is expiration time. If it's 0, the item never expires
(although it may be deleted from the cache to make place for other
items). If it's non-zero (either Unix time or offset in seconds from
current time), it is guaranteed that clients will not be able to
retrieve this item after the expiration time arrives (measured by
server time).
- is the number of bytes in the data block to follow, *not*
including the delimiting \r\n. may be zero (in which case
it's followed by an empty data block).
- is a unique 64-bit value of an existing entry.
Clients should use the value returned from the "gets" command
when issuing "cas" updates.
- "noreply" optional parameter instructs the server to not send the
reply. NOTE: if the request line is malformed, the server can't
parse "noreply" option reliably. In this case it may send the error
to the client, and not reading it on the client side will break
things. Client should construct only valid requests.
After this line, the client sends the data block:
\r\n
- is a chunk of arbitrary 8-bit data of length
from the previous line.
After sending the command line and the data block the client awaits
the reply, which may be:
- "STORED\r\n", to indicate success.
- "NOT_STORED\r\n" to indicate the data was not stored, but not
because of an error. This normally means that the
condition for an "add" or a "replace" command wasn't met.
- "EXISTS\r\n" to indicate that the item you are trying to store with
a "cas" command has been modified since you last fetched it.
- "NOT_FOUND\r\n" to indicate that the item you are trying to store
with a "cas" command did not exist.
Retrieval command:
------------------
The retrieval commands "get" and "gets" operates like this:
get *\r\n
gets *\r\n
- * means one or more key strings separated by whitespace.
After this command, the client expects zero or more items, each of
which is received as a text line followed by a data block. After all
the items have been transmitted, the server sends the string
"END\r\n"
to indicate the end of response.
Each item sent by the server looks like this:
VALUE []\r\n
\r\n
- is the key for the item being sent
- is the flags value set by the storage command
- is the length of the data block to follow, *not* including
its delimiting \r\n
- is a unique 64-bit integer that uniquely identifies
this specific item.
- is the data for this item.
If some of the keys appearing in a retrieval request are not sent back
by the server in the item list this means that the server does not
hold items with such keys (because they were never stored, or stored
but deleted to make space for more items, or expired, or explicitly
deleted by a client).
Deletion
--------
The command "delete" allows for explicit deletion of items:
delete [noreply]\r\n
- is the key of the item the client wishes the server to delete
- "noreply" optional parameter instructs the server to not send the
reply. See the note in Storage commands regarding malformed
requests.
The response line to this command can be one of:
- "DELETED\r\n" to indicate success
- "NOT_FOUND\r\n" to indicate that the item with this key was not
found.
See the "flush_all" command below for immediate invalidation
of all existing items.
Increment/Decrement
-------------------
Commands "incr" and "decr" are used to change data for some item
in-place, incrementing or decrementing it. The data for the item is
treated as decimal representation of a 64-bit unsigned integer. If
the current data value does not conform to such a representation, the
incr/decr commands return an error (memcached <= 1.2.6 treated the
bogus value as if it were 0, leading to confusion). Also, the item
must already exist for incr/decr to work; these commands won't pretend
that a non-existent key exists with value 0; instead, they will fail.
The client sends the command line:
incr [noreply]\r\n
or
decr [noreply]\r\n
- is the key of the item the client wishes to change
- is the amount by which the client wants to increase/decrease
the item. It is a decimal representation of a 64-bit unsigned integer.
- "noreply" optional parameter instructs the server to not send the
reply. See the note in Storage commands regarding malformed
requests.
The response will be one of:
- "NOT_FOUND\r\n" to indicate the item with this value was not found
- \r\n , where is the new value of the item's data,
after the increment/decrement operation was carried out.
Note that underflow in the "decr" command is caught: if a client tries
to decrease the value below 0, the new value will be 0. Overflow in
the "incr" command will wrap around the 64 bit mark.
Note also that decrementing a number such that it loses length isn't
guaranteed to decrement its returned length. The number MAY be
space-padded at the end, but this is purely an implementation
optimization, so you also shouldn't rely on that.
Touch
-----
The "touch" command is used to update the expiration time of an existing item
without fetching it.
touch [noreply]\r\n
- is the key of the item the client wishes the server to touch
- is expiration time. Works the same as with the update commands
(set/add/etc). This replaces the existing expiration time. If an existing
item were to expire in 10 seconds, but then was touched with an
expiration time of "20", the item would then expire in 20 seconds.
- "noreply" optional parameter instructs the server to not send the
reply. See the note in Storage commands regarding malformed
requests.
The response line to this command can be one of:
- "TOUCHED\r\n" to indicate success
- "NOT_FOUND\r\n" to indicate that the item with this key was not
found.
Slabs Reassign
--------------
NOTE: This command is subject to change as of this writing.
The slabs reassign command is used to redistribute memory once a running
instance has hit its limit. It might be desirable to have memory laid out
differently than was automatically assigned after the server started.
slabs reassign \r\n
- is an id number for the slab class to steal a page from
A source class id of -1 means "pick from any valid class"
- is an id number for the slab class to move a page to
The response line could be one of:
- "OK" to indicate the page has been scheduled to move
- "BUSY [message]" to indicate a page is already being processed, try again
later.
- "BADCLASS [message]" a bad class id was specified
- "NOSPARE [message]" source class has no spare pages
- "NOTFULL [message]" dest class must be full to move new pages to it
- "UNSAFE [message]" source class cannot move a page right now
- "SAME [message]" must specify different source/dest ids.
Slabs Automove
--------------
NOTE: This command is subject to change as of this writing.
The slabs automove command enables a background thread which decides on its
own when to move memory between slab classes. Its implementation and options
will likely be in flux for several versions. See the wiki/mailing list for
more details.
The automover can be enabled or disabled at runtime with this command.
slabs automove <0|1>
- 0|1|2 is the indicator on whether to enable the slabs automover or not.
The response should always be "OK\r\n"
- <0> means to set the thread on standby
- <1> means to return pages to a global pool when there are more than 2 pages
worth of free chunks in a slab class. Pages are then re-assigned back into
other classes as-needed.
- <2> is a highly aggressive mode which causes pages to be moved every time
there is an eviction. It is not recommended to run for very long in this
mode unless your access patterns are very well understood.
LRU_Crawler
-----------
NOTE: This command (and related commands) are subject to change as of this
writing.
The LRU Crawler is an optional background thread which will walk from the tail
toward the head of requested slab classes, actively freeing memory for expired
items. This is useful if you have a mix of items with both long and short
TTL's, but aren't accessed very often. This system is not required for normal
usage, and can add small amounts of latency and increase CPU usage.
lru_crawler
- Enable or disable the LRU Crawler background thread.
The response line could be one of:
- "OK" to indicate the crawler has been started or stopped.
- "ERROR [message]" something went wrong while enabling or disabling.
lru_crawler sleep
- The number of microseconds to sleep in between each item checked for
expiration. Smaller numbers will obviously impact the system more.
A value of "0" disables the sleep, "1000000" (one second) is the max.
The response line could be one of:
- "OK"
- "CLIENT_ERROR [message]" indicating a format or bounds issue.
lru_crawler tocrawl <32u>
- The maximum number of items to inspect in a slab class per run request. This
allows you to avoid scanning all of very large slabs when it is unlikely to
find items to expire.
The response line could be one of:
- "OK"
- "CLIENT_ERROR [message]" indicating a format or bound issue.
lru_crawler crawl
- Takes a single, or a list of, numeric classids (ie: 1,3,10). This instructs
the crawler to start at the tail of each of these classids and run to the
head. The crawler cannot be stopped or restarted until it completes the
previous request.
The special keyword "all" instructs it to crawl all slabs with items in
them.
The response line could be one of:
- "OK" to indicate successful launch.
- "BUSY [message]" to indicate the crawler is already processing a request.
- "BADCLASS [message]" to indicate an invalid class was specified.
Statistics
----------
The command "stats" is used to query the server about statistics it
maintains and other internal data. It has two forms. Without
arguments:
stats\r\n
it causes the server to output general-purpose statistics and
settings, documented below. In the other form it has some arguments:
stats \r\n
Depending on , various internal data is sent by the server. The
kinds of arguments and the data sent are not documented in this version
of the protocol, and are subject to change for the convenience of
memcache developers.
General-purpose statistics
--------------------------
Upon receiving the "stats" command without arguments, the server sents
a number of lines which look like this:
STAT \r\n
The server terminates this list with the line
END\r\n
In each line of statistics, is the name of this statistic, and
is the data. The following is the list of all names sent in
response to the "stats" command, together with the type of the value
sent for this name, and the meaning of the value.
In the type column below, "32u" means a 32-bit unsigned integer, "64u"
means a 64-bit unsigned integer. '32u.32u' means two 32-bit unsigned
integers separated by a colon (treat this as a floating point number).
|-----------------------+---------+-------------------------------------------|
| Name | Type | Meaning |
|-----------------------+---------+-------------------------------------------|
| pid | 32u | Process id of this server process |
| uptime | 32u | Number of secs since the server started |
| time | 32u | current UNIX time according to the server |
| version | string | Version string of this server |
| pointer_size | 32 | Default size of pointers on the host OS |
| | | (generally 32 or 64) |
| rusage_user | 32u.32u | Accumulated user time for this process |
| | | (seconds:microseconds) |
| rusage_system | 32u.32u | Accumulated system time for this process |
| | | (seconds:microseconds) |
| curr_items | 32u | Current number of items stored |
| total_items | 32u | Total number of items stored since |
| | | the server started |
| bytes | 64u | Current number of bytes used |
| | | to store items |
| curr_connections | 32u | Number of open connections |
| total_connections | 32u | Total number of connections opened since |
| | | the server started running |
| rejected_connections | 64u | Conns rejected in maxconns_fast mode |
| connection_structures | 32u | Number of connection structures allocated |
| | | by the server |
| reserved_fds | 32u | Number of misc fds used internally |
| cmd_get | 64u | Cumulative number of retrieval reqs |
| cmd_set | 64u | Cumulative number of storage reqs |
| cmd_flush | 64u | Cumulative number of flush reqs |
| cmd_touch | 64u | Cumulative number of touch reqs |
| get_hits | 64u | Number of keys that have been requested |
| | | and found present |
| get_misses | 64u | Number of items that have been requested |
| | | and not found |
| delete_misses | 64u | Number of deletions reqs for missing keys |
| delete_hits | 64u | Number of deletion reqs resulting in |
| | | an item being removed. |
| incr_misses | 64u | Number of incr reqs against missing keys. |
| incr_hits | 64u | Number of successful incr reqs. |
| decr_misses | 64u | Number of decr reqs against missing keys. |
| decr_hits | 64u | Number of successful decr reqs. |
| cas_misses | 64u | Number of CAS reqs against missing keys. |
| cas_hits | 64u | Number of successful CAS reqs. |
| cas_badval | 64u | Number of CAS reqs for which a key was |
| | | found, but the CAS value did not match. |
| touch_hits | 64u | Numer of keys that have been touched with |
| | | a new expiration time |
| touch_misses | 64u | Numer of items that have been touched and |
| | | not found |
| auth_cmds | 64u | Number of authentication commands |
| | | handled, success or failure. |
| auth_errors | 64u | Number of failed authentications. |
| evictions | 64u | Number of valid items removed from cache |
| | | to free memory for new items |
| reclaimed | 64u | Number of times an entry was stored using |
| | | memory from an expired entry |
| bytes_read | 64u | Total number of bytes read by this server |
| | | from network |
| bytes_written | 64u | Total number of bytes sent by this server |
| | | to network |
| limit_maxbytes | 32u | Number of bytes this server is allowed to |
| | | use for storage. |
| accepting_conns | bool | Whether or not server is accepting conns |
| listen_disabled_num | 64u | Number of times server has stopped |
| | | accepting new connections (maxconns). |
| time_in_listen_disabled_us |
| | 64u | Number of microseconds in maxconns. |
| threads | 32u | Number of worker threads requested. |
| | | (see doc/threads.txt) |
| conn_yields | 64u | Number of times any connection yielded to |
| | | another due to hitting the -R limit. |
| hash_power_level | 32u | Current size multiplier for hash table |
| hash_bytes | 64u | Bytes currently used by hash tables |
| hash_is_expanding | bool | Indicates if the hash table is being |
| | | grown to a new size |
| expired_unfetched | 64u | Items pulled from LRU that were never |
| | | touched by get/incr/append/etc before |
| | | expiring |
| evicted_unfetched | 64u | Items evicted from LRU that were never |
| | | touched by get/incr/append/etc. |
| slab_reassign_running | bool | If a slab page is being moved |
| slabs_moved | 64u | Total slab pages moved |
| crawler_reclaimed | 64u | Total items freed by LRU Crawler |
| crawler_items-checked | 64u | Total items examined by LRU Crawler |
| lrutail_reflocked | 64u | Times LRU tail was found with active ref. |
| | | Items can be evicted to avoid OOM errors. |
| moves_to_cold | 64u | Items moved from HOT/WARM to COLD LRU's |
| moves_to_warm | 64u | Items moved from COLD to WARM LRU |
| moves_within_lru | 64u | Items reshuffled within HOT or WARM LRU's |
| direct_reclaims | 64u | Times worker threads had to directly |
| | | reclaim or evict items. |
| lru_crawler_starts | 64u | Times an LRU crawler was started |
| lru_maintainer_juggles |
| | 64u | Number of times the LRU bg thread woke up |
| slab_global_page_pool | 32u | Slab pages returned to global pool for |
| | | reassignment to other slab classes. |
| slab_reassign_rescues | 64u | Items rescued from eviction in page move |
| slab_reassign_evictions_nomem |
| | 64u | Valid items evicted during a page move |
| | | (due to no free memory in slab) |
| slab_reassign_inline_reclaim |
| | 64u | Internal stat counter for when the page |
| | | mover clears memory from the chunk |
| | | freelist when it wasn't expecting to. |
| slab_reassign_busy_items |
| | 64u | Items busy during page move, requiring a |
| | | retry before page can be moved. |
|-----------------------+---------+-------------------------------------------|
Settings statistics
-------------------
CAVEAT: This section describes statistics which are subject to change in the
future.
The "stats" command with the argument of "settings" returns details of
the settings of the running memcached. This is primarily made up of
the results of processing commandline options.
Note that these are not guaranteed to return in any specific order and
this list may not be exhaustive. Otherwise, this returns like any
other stats command.
|-------------------+----------+----------------------------------------------|
| Name | Type | Meaning |
|-------------------+----------+----------------------------------------------|
| maxbytes | size_t | Maximum number of bytes allows in this cache |
| maxconns | 32 | Maximum number of clients allowed. |
| tcpport | 32 | TCP listen port. |
| udpport | 32 | UDP listen port. |
| inter | string | Listen interface. |
| verbosity | 32 | 0 = none, 1 = some, 2 = lots |
| oldest | 32u | Age of the oldest honored object. |
| evictions | on/off | When off, LRU evictions are disabled. |
| domain_socket | string | Path to the domain socket (if any). |
| umask | 32 (oct) | umask for the creation of the domain socket. |
| growth_factor | float | Chunk size growth factor. |
| chunk_size | 32 | Minimum space allocated for key+value+flags. |
| num_threads | 32 | Number of threads (including dispatch). |
| stat_key_prefix | char | Stats prefix separator character. |
| detail_enabled | bool | If yes, stats detail is enabled. |
| reqs_per_event | 32 | Max num IO ops processed within an event. |
| cas_enabled | bool | When no, CAS is not enabled for this server. |
| tcp_backlog | 32 | TCP listen backlog. |
| auth_enabled_sasl | yes/no | SASL auth requested and enabled. |
| item_size_max | size_t | maximum item size |
| maxconns_fast | bool | If fast disconnects are enabled |
| hashpower_init | 32 | Starting size multiplier for hash table |
| slab_reassign | bool | Whether slab page reassignment is allowed |
| slab_automove | bool | Whether slab page automover is enabled |
| hash_algorithm | char | Hash table algorithm in use |
| lru_crawler | bool | Whether the LRU crawler is enabled |
| lru_crawler_sleep | 32 | Microseconds to sleep between LRU crawls |
| lru_crawler_tocrawl |
| | 32u | Max items to crawl per slab per run |
| lru_maintainer_thread |
| | bool | Split LRU mode and background threads |
| hot_lru_pct | 32 | Pct of slab memory reserved for HOT LRU |
| warm_lru_pct | 32 | Pct of slab memory reserved for WARM LRU |
| expirezero_does_not_evict |
| | bool | If yes, items with 0 exptime cannot evict |
|-------------------+----------+----------------------------------------------|
Item statistics
---------------
CAVEAT: This section describes statistics which are subject to change in the
future.
The "stats" command with the argument of "items" returns information about
item storage per slab class. The data is returned in the format:
STAT items::\r\n
The server terminates this list with the line
END\r\n
The slabclass aligns with class ids used by the "stats slabs" command. Where
"stats slabs" describes size and memory usage, "stats items" shows higher
level information.
The following item values are defined as of writing.
Name Meaning
------------------------------
number Number of items presently stored in this class. Expired
items are not automatically excluded.
number_hot Number of items presently stored in the HOT LRU.
number_warm Number of items presently stored in the WARM LRU.
number_cold Number of items presently stored in the COLD LRU.
number_noexp Number of items presently stored in the NOEXP class.
age Age of the oldest item in the LRU.
evicted Number of times an item had to be evicted from the LRU
before it expired.
evicted_nonzero Number of times an item which had an explicit expire
time set had to be evicted from the LRU before it
expired.
evicted_time Seconds since the last access for the most recent item
evicted from this class. Use this to judge how
recently active your evicted data is.
outofmemory Number of times the underlying slab class was unable to
store a new item. This means you are running with -M or
an eviction failed.
tailrepairs Number of times we self-healed a slab with a refcount
leak. If this counter is increasing a lot, please
report your situation to the developers.
reclaimed Number of times an entry was stored using memory from
an expired entry.
expired_unfetched Number of expired items reclaimed from the LRU which
were never touched after being set.
evicted_unfetched Number of valid items evicted from the LRU which were
never touched after being set.
crawler_reclaimed Number of items freed by the LRU Crawler.
lrutail_reflocked Number of items found to be refcount locked in the
LRU tail.
moves_to_cold Number of items moved from HOT or WARM into COLD.
moves_to_warm Number of items moved from COLD to WARM.
moves_within_lru Number of times active items were bumped within
HOT or WARM.
direct_reclaims Number of times worker threads had to directly pull LRU
tails to find memory for a new item.
Note this will only display information about slabs which exist, so an empty
cache will return an empty set.
Item size statistics
--------------------
CAVEAT: This section describes statistics which are subject to change in the
future.
The "stats" command with the argument of "sizes" returns information about the
general size and count of all items stored in the cache.
WARNING: This command WILL lock up your cache! It iterates over *every item*
and examines the size. While the operation is fast, if you have many items
you could prevent memcached from serving requests for several seconds.
The data is returned in the following format:
STAT \r\n
The server terminates this list with the line
END\r\n
'size' is an approximate size of the item, within 32 bytes.
'count' is the amount of items that exist within that 32-byte range.
This is essentially a display of all of your items if there was a slab class
for every 32 bytes. You can use this to determine if adjusting the slab growth
factor would save memory overhead. For example: generating more classes in the
lower range could allow items to fit more snugly into their slab classes, if
most of your items are less than 200 bytes in size.
Slab statistics
---------------
CAVEAT: This section describes statistics which are subject to change in the
future.
The "stats" command with the argument of "slabs" returns information about
each of the slabs created by memcached during runtime. This includes per-slab
information along with some totals. The data is returned in the format:
STAT :\r\n
STAT \r\n
The server terminates this list with the line
END\r\n
|-----------------+----------------------------------------------------------|
| Name | Meaning |
|-----------------+----------------------------------------------------------|
| chunk_size | The amount of space each chunk uses. One item will use |
| | one chunk of the appropriate size. |
| chunks_per_page | How many chunks exist within one page. A page by |
| | default is less than or equal to one megabyte in size. |
| | Slabs are allocated by page, then broken into chunks. |
| total_pages | Total number of pages allocated to the slab class. |
| total_chunks | Total number of chunks allocated to the slab class. |
| get_hits | Total number of get requests serviced by this class. |
| cmd_set | Total number of set requests storing data in this class. |
| delete_hits | Total number of successful deletes from this class. |
| incr_hits | Total number of incrs modifying this class. |
| decr_hits | Total number of decrs modifying this class. |
| cas_hits | Total number of CAS commands modifying this class. |
| cas_badval | Total number of CAS commands that failed to modify a |
| | value due to a bad CAS id. |
| touch_hits | Total number of touches serviced by this class. |
| used_chunks | How many chunks have been allocated to items. |
| free_chunks | Chunks not yet allocated to items, or freed via delete. |
| free_chunks_end | Number of free chunks at the end of the last allocated |
| | page. |
| mem_requested | Number of bytes requested to be stored in this slab[*]. |
| active_slabs | Total number of slab classes allocated. |
| total_malloced | Total amount of memory allocated to slab pages. |
|-----------------+----------------------------------------------------------|
* Items are stored in a slab that is the same size or larger than the
item. mem_requested shows the size of all items within a
slab. (total_chunks * chunk_size) - mem_requested shows memory
wasted in a slab class. If you see a lot of waste, consider tuning
the slab factor.
Connection statistics
---------------------
The "stats" command with the argument of "conns" returns information
about currently active connections and about sockets that are listening
for new connections. The data is returned in the format:
STAT :\r\n
The server terminates this list with the line
END\r\n
The following "stat" keywords may be present:
|---------------------+------------------------------------------------------|
| Name | Meaning |
|---------------------+------------------------------------------------------|
| addr | The address of the remote side. For listening |
| | sockets this is the listen address. Note that some |
| | socket types (such as UNIX-domain) don't have |
| | meaningful remote addresses. |
| state | The current state of the connection. See below. |
| secs_since_last_cmd | The number of seconds since the most recently |
| | issued command on the connection. This measures |
| | the time since the start of the command, so if |
| | "state" indicates a command is currently executing, |
| | this will be the number of seconds the current |
| | command has been running. |
|---------------------+------------------------------------------------------|
The value of the "state" stat may be one of the following:
|----------------+-----------------------------------------------------------|
| Name | Meaning |
|----------------+-----------------------------------------------------------|
| conn_closing | Shutting down the connection. |
| conn_listening | Listening for new connections or a new UDP request. |
| conn_mwrite | Writing a complex response, e.g., to a "get" command. |
| conn_new_cmd | Connection is being prepared to accept a new command. |
| conn_nread | Reading extended data, typically for a command such as |
| | "set" or "put". |
| conn_parse_cmd | The server has received a command and is in the middle |
| | of parsing it or executing it. |
| conn_read | Reading newly-arrived command data. |
| conn_swallow | Discarding excess input, e.g., after an error has |
| | occurred. |
| conn_waiting | A partial command has been received and the server is |
| | waiting for the rest of it to arrive (note the difference |
| | between this and conn_nread). |
| conn_write | Writing a simple response (anything that doesn't involve |
| | sending back multiple lines of response data). |
|----------------+-----------------------------------------------------------|
Other commands
--------------
"flush_all" is a command with an optional numeric argument. It always
succeeds, and the server sends "OK\r\n" in response (unless "noreply"
is given as the last parameter). Its effect is to invalidate all
existing items immediately (by default) or after the expiration
specified. After invalidation none of the items will be returned in
response to a retrieval command (unless it's stored again under the
same key *after* flush_all has invalidated the items). flush_all
doesn't actually free all the memory taken up by existing items; that
will happen gradually as new items are stored. The most precise
definition of what flush_all does is the following: it causes all
items whose update time is earlier than the time at which flush_all
was set to be executed to be ignored for retrieval purposes.
The intent of flush_all with a delay, was that in a setting where you
have a pool of memcached servers, and you need to flush all content,
you have the option of not resetting all memcached servers at the
same time (which could e.g. cause a spike in database load with all
clients suddenly needing to recreate content that would otherwise
have been found in the memcached daemon).
The delay option allows you to have them reset in e.g. 10 second
intervals (by passing 0 to the first, 10 to the second, 20 to the
third, etc. etc.).
"version" is a command with no arguments:
version\r\n
In response, the server sends
"VERSION \r\n", where is the version string for the
server.
"verbosity" is a command with a numeric argument. It always succeeds,
and the server sends "OK\r\n" in response (unless "noreply" is given
as the last parameter). Its effect is to set the verbosity level of
the logging output.
"quit" is a command with no arguments:
quit\r\n
Upon receiving this command, the server closes the
connection. However, the client may also simply close the connection
when it no longer needs it, without issuing this command.
UDP protocol
------------
For very large installations where the number of clients is high enough
that the number of TCP connections causes scaling difficulties, there is
also a UDP-based interface. The UDP interface does not provide guaranteed
delivery, so should only be used for operations that aren't required to
succeed; typically it is used for "get" requests where a missing or
incomplete response can simply be treated as a cache miss.
Each UDP datagram contains a simple frame header, followed by data in the
same format as the TCP protocol described above. In the current
implementation, requests must be contained in a single UDP datagram, but
responses may span several datagrams. (The only common requests that would
span multiple datagrams are huge multi-key "get" requests and "set"
requests, both of which are more suitable to TCP transport for reliability
reasons anyway.)
The frame header is 8 bytes long, as follows (all values are 16-bit integers
in network byte order, high byte first):
0-1 Request ID
2-3 Sequence number
4-5 Total number of datagrams in this message
6-7 Reserved for future use; must be 0
The request ID is supplied by the client. Typically it will be a
monotonically increasing value starting from a random seed, but the client
is free to use whatever request IDs it likes. The server's response will
contain the same ID as the incoming request. The client uses the request ID
to differentiate between responses to outstanding requests if there are
several pending from the same server; any datagrams with an unknown request
ID are probably delayed responses to an earlier request and should be
discarded.
The sequence number ranges from 0 to n-1, where n is the total number of
datagrams in the message. The client should concatenate the payloads of the
datagrams for a given response in sequence number order; the resulting byte
stream will contain a complete response in the same format as the TCP
protocol (including terminating \r\n sequences).
memcached-1.4.25/doc/Makefile 0000664 0001750 0001750 00000036552 12623542632 012661 0000000 0000000 # Makefile.in generated by automake 1.14.1 from Makefile.am.
# doc/Makefile. Generated from Makefile.in by configure.
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/memcached
pkgincludedir = $(includedir)/memcached
pkglibdir = $(libdir)/memcached
pkglibexecdir = $(libexecdir)/memcached
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = x86_64-unknown-linux-gnu
host_triplet = x86_64-unknown-linux-gnu
#am__append_1 = protocol-binary.txt protocol-binary-range.txt
subdir = doc
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/version.m4 \
$(top_srcdir)/m4/c99-backport.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_$(V))
am__v_P_ = $(am__v_P_$(AM_DEFAULT_VERBOSITY))
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_$(V))
am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_$(V))
am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
man1dir = $(mandir)/man1
am__installdirs = "$(DESTDIR)$(man1dir)"
NROFF = nroff
MANS = $(man_MANS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing aclocal-1.14
AMTAR = $${TAR-tar}
AM_DEFAULT_VERBOSITY = 1
AUTOCONF = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing autoconf
AUTOHEADER = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing autoheader
AUTOMAKE = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing automake-1.14
AWK = gawk
CC = gcc -std=gnu99
CCDEPMODE = depmode=gcc3
CFLAGS = -g -O2 -pthread -pthread -Wall -Werror -pedantic -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls
CPP = gcc -E
CPPFLAGS =
CYGPATH_W = echo
DEFS = -DHAVE_CONFIG_H
DEPDIR = .deps
DTRACE =
DTRACEFLAGS =
ECHO_C =
ECHO_N = -n
ECHO_T =
EGREP = /bin/grep -E
ENABLE_SASL =
EXEEXT =
GREP = /bin/grep
INSTALL = /usr/bin/install -c
INSTALL_DATA = ${INSTALL} -m 644
INSTALL_PROGRAM = ${INSTALL}
INSTALL_SCRIPT = ${INSTALL}
INSTALL_STRIP_PROGRAM = $(install_sh) -c -s
LDFLAGS =
LIBOBJS =
LIBS = -levent
LTLIBOBJS =
MAKEINFO = ${SHELL} /home/dormando/d/p/danga/git/memcached/missing makeinfo
MKDIR_P = /bin/mkdir -p
OBJEXT = o
PACKAGE = memcached
PACKAGE_BUGREPORT = memcached@googlegroups.com
PACKAGE_NAME = memcached
PACKAGE_STRING = memcached 1.4.25
PACKAGE_TARNAME = memcached
PACKAGE_URL =
PACKAGE_VERSION = 1.4.25
PATH_SEPARATOR = :
PROFILER = /usr/bin/gcov
PROFILER_FLAGS = -fprofile-arcs -ftest-coverage
PROFILER_LDFLAGS = -lgcov
SET_MAKE =
SHELL = /bin/bash
STRIP =
VERSION = 1.4.25
XML2RFC = no
XSLTPROC = no
abs_builddir = /home/dormando/d/p/danga/git/memcached/doc
abs_srcdir = /home/dormando/d/p/danga/git/memcached/doc
abs_top_builddir = /home/dormando/d/p/danga/git/memcached
abs_top_srcdir = /home/dormando/d/p/danga/git/memcached
ac_ct_CC = gcc
am__include = include
am__leading_dot = .
am__quote =
am__tar = $${TAR-tar} chof - "$$tardir"
am__untar = $${TAR-tar} xf -
bindir = ${exec_prefix}/bin
build = x86_64-unknown-linux-gnu
build_alias =
build_cpu = x86_64
build_os = linux-gnu
build_vendor = unknown
builddir = .
datadir = ${datarootdir}
datarootdir = ${prefix}/share
docdir = ${datarootdir}/doc/${PACKAGE_TARNAME}
dvidir = ${docdir}
exec_prefix = ${prefix}
host = x86_64-unknown-linux-gnu
host_alias =
host_cpu = x86_64
host_os = linux-gnu
host_vendor = unknown
htmldir = ${docdir}
includedir = ${prefix}/include
infodir = ${datarootdir}/info
install_sh = ${SHELL} /home/dormando/d/p/danga/git/memcached/install-sh
libdir = ${exec_prefix}/lib
libexecdir = ${exec_prefix}/libexec
localedir = ${datarootdir}/locale
localstatedir = ${prefix}/var
mandir = ${datarootdir}/man
mkdir_p = $(MKDIR_P)
oldincludedir = /usr/include
pdfdir = ${docdir}
prefix = /usr/local
program_transform_name = s,x,x,
psdir = ${docdir}
sbindir = ${exec_prefix}/sbin
sharedstatedir = ${prefix}/com
srcdir = .
sysconfdir = ${prefix}/etc
target_alias =
top_build_prefix = ../
top_builddir = ..
top_srcdir = ..
man_MANS = memcached.1
EXTRA_DIST = *.txt
BUILT_SOURCES = $(am__append_1)
#MOSTLYCLEANFILES = protocol-binary.txt protocol-binary-range.txt
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign doc/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-man1: $(man_MANS)
@$(NORMAL_INSTALL)
@list1=''; \
list2='$(man_MANS)'; \
test -n "$(man1dir)" \
&& test -n "`echo $$list1$$list2`" \
|| exit 0; \
echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
$(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
{ for i in $$list1; do echo "$$i"; done; \
if test -n "$$list2"; then \
for i in $$list2; do echo "$$i"; done \
| sed -n '/\.1[a-z]*$$/p'; \
fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
done | \
sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
sed 'N;N;s,\n, ,g' | { \
list=; while read file base inst; do \
if test "$$base" = "$$inst"; then list="$$list $$file"; else \
echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
$(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
fi; \
done; \
for i in $$list; do echo "$$i"; done | $(am__base_list) | \
while read files; do \
test -z "$$files" || { \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
done; }
uninstall-man1:
@$(NORMAL_UNINSTALL)
@list=''; test -n "$(man1dir)" || exit 0; \
files=`{ for i in $$list; do echo "$$i"; done; \
l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
sed -n '/\.1[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
tags TAGS:
ctags CTAGS:
cscope cscopelist:
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) check-am
all-am: Makefile $(MANS)
installdirs:
for dir in "$(DESTDIR)$(man1dir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
clean: clean-am
clean-am: clean-generic mostlyclean-am
distclean: distclean-am
-rm -f Makefile
distclean-am: clean-am distclean-generic
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-man
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man: install-man1
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-man
uninstall-man: uninstall-man1
.MAKE: all check install install-am install-strip
.PHONY: all all-am check check-am clean clean-generic cscopelist-am \
ctags-am distclean distclean-generic distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-man1 install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
pdf-am ps ps-am tags-am uninstall uninstall-am uninstall-man \
uninstall-man1
protocol-binary.txt: $(.TARGET:R).full
no -c /home/dormando/d/p/danga/git/memcached/doc $(.TARGET:R).full $@
protocol-binary-range.txt: $(.TARGET:R).full
no -c /home/dormando/d/p/danga/git/memcached/doc $(.TARGET:R).full $@
protocol-binary.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
no --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
protocol-binary-range.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
no --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
memcached-1.4.25/doc/Doxyfile 0000644 0001750 0001750 00000144517 11446413300 012715 0000000 0000000 # Doxyfile 1.5.2
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file that
# follow. The default is UTF-8 which is also the encoding used for all text before
# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
# possible encodings.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = memcached
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 0.8
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY = doxygen
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
# 4096 sub-directories (in 2 levels) under the output directory of each output
# format and will distribute the generated files over these directories.
# Enabling this option can be useful when feeding doxygen a huge amount of
# source files, where putting all generated files in the same directory would
# otherwise cause performance problems for the file system.
CREATE_SUBDIRS = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = NO
# This tag implements a quasi-intelligent brief description abbreviator
# that is used to form the text in various listings. Each string
# in this list, if found as the leading text of the brief description, will be
# stripped from the text and the result after processing the whole list, is
# used as the annotated text. Otherwise, the brief description is used as-is.
# If left blank, the following values are used ("$name" is automatically
# replaced with the name of the entity): "The $name class" "The $name widget"
# "The $name file" "is" "provides" "specifies" "contains"
# "represents" "a" "an" "the"
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = YES
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user-defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the
# path to strip.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
# the path mentioned in the documentation of a class, which tells
# the reader which header file to include in order to use a class.
# If left blank only the name of the header file containing the class
# definition is used. Otherwise one should specify the include paths that
# are normally passed to the compiler using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explicit @brief command for a brief description.
JAVADOC_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
# The new default is to treat a multi-line C++ comment block as a detailed
# description. Set this tag to YES if you prefer the old behaviour instead.
MULTILINE_CPP_IS_BRIEF = NO
# If the DETAILS_AT_TOP tag is set to YES then Doxygen
# will output the detailed description near the top, like JavaDoc.
# If set to NO, the detailed description appears after the member
# documentation.
DETAILS_AT_TOP = NO
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
# a new page for each member. If set to NO, the documentation of a member will
# be part of the file/class/namespace that contains it.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 8
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user-defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
# sources only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
# sources only. Doxygen will then generate output that is more tailored for Java.
# For instance, namespaces will be presented as packages, qualified scopes
# will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
# include (a tag file for) the STL sources as input, then you should
# set this tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
# func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
CPP_CLI_SUPPORT = NO
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
# NO to prevent subgrouping. Alternatively, this can be done per class using
# the \nosubgrouping command.
SUBGROUPING = YES
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# This flag is only useful for Objective-C code. When set to YES local
# methods, which are defined in the implementation section but not in
# the interface are included in the documentation.
# If set to NO (the default) only methods in the interface are included.
EXTRACT_LOCAL_METHODS = NO
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these classes will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
# friend (class|struct|union) declarations.
# If set to NO (the default) these declarations will be included in the
# documentation.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
# documentation blocks found inside the body of a function.
# If set to NO (the default) these blocks will be appended to the
# function's detailed documentation block.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = YES
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put a list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = NO
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
# brief documentation of file, namespace and class members alphabetically
# by member name. If set to NO (the default) the members will appear in
# declaration order.
SORT_BRIEF_DOCS = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
# sorted by fully-qualified names, including namespaces. If set to
# NO (the default), the class list will be sorted only by class name,
# not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the
# alphabetical list.
SORT_BY_SCOPE_NAME = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
# disable (NO) the deprecated list. This list is created by putting
# \deprecated commands in the documentation.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consists of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
# If the sources in your project are distributed over multiple directories
# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
# in the documentation. The default is NO.
SHOW_DIRECTORIES = NO
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from the
# version control system). Doxygen will invoke the program by executing (via
# popen()) the command , where is the value of
# the FILE_VERSION_FILTER tag, and is the name of an input file
# provided by doxygen. Whatever the program writes to standard output
# is used as the file version. See the manual for examples.
FILE_VERSION_FILTER =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some
# parameters in a documented function, or documenting parameters that
# don't exist or using markup commands wrongly.
WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be abled to get warnings for
# functions that are documented, but have no documentation for their parameters
# or return value. If set to NO (the default) doxygen will only warn about
# wrong or incomplete parameter documentation, but not about the absence of
# documentation.
WARN_NO_PARAMDOC = NO
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text. Optionally the format may contain
# $version, which will be replaced by the version of the file (if it could
# be obtained via FILE_VERSION_FILTER)
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = ..
# This tag can be used to specify the character encoding of the source files that
# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
# See http://www.gnu.org/software/libiconv for the list of possible encodings.
INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py
FILE_PATTERNS = *.h \
*.c
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE = config.h
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded
# from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories. Note that the wildcards are matched
# against the file with absolute path, so to exclude all test directories
# for example use the pattern */test/*
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the output.
# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command , where
# is the value of the INPUT_FILTER tag, and is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output. If FILTER_PATTERNS is specified, this tag will be
# ignored.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form:
# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
# is applied to all files.
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
# Note: To get rid of all source code in the generated output, make sure also
# VERBATIM_HEADERS is set to NO.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
# link to the source code. Otherwise they will link to the documentstion.
REFERENCES_LINK_SOURCE = YES
# If the USE_HTAGS tag is set to YES then the references to source code
# will point to the HTML generated by the htags(1) tool instead of doxygen
# built-in source browser. The htags tool is part of GNU's global source
# tagging system (see http://www.gnu.org/software/global/global.html). You
# will need version 4.8.6 or higher.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT = html
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
# doxygen will generate files with .html extension.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet. Note that doxygen will try to copy
# the style sheet file to the HTML output directory, so don't put your own
# stylesheet in the HTML output directory as well, or it will be erased!
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
# be used to specify the file name of the resulting .chm file. You
# can add a path in front of the file if the result should not be
# written to the html output directory.
CHM_FILE =
# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
# be used to specify the location (absolute path including file name) of
# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
# the HTML help compiler on the generated index.hhp.
HHC_LOCATION =
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the HTML help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
# probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = NO
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
# invoked. If left blank `latex' will be used as the default command name.
LATEX_CMD_NAME = latex
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
# generate index for LaTeX. If left blank `makeindex' will be used as the
# default command name.
MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
# If LATEX_HIDE_INDICES is set to YES then doxygen will not
# include the index chapters (such as File Index, Compound Index, etc.)
# in the output.
LATEX_HIDE_INDICES = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimized for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assignments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = YES
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT = man
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION = .3
# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
# then it will generate one additional man file for each entity
# documented in the real man page(s). These additional files
# only source the real man page, but without them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation.
GENERATE_XML = NO
# The XML_OUTPUT tag is used to specify where the XML pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `xml' will be used as the default path.
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that
# enabling this will significantly increase the size of the XML output.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES Doxygen will
# generate a Perl module file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
# the necessary Makefile rules, Perl scripts and LaTeX code to be able
# to generate PDF and DVI output from the Perl module output.
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
# nicely formatted so it can be parsed by a human reader. This is useful
# if you want to understand what is going on. On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
PERLMOD_PRETTY = YES
# The names of the make variables in the generated doxyrules.make file
# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
# This is useful so different doxyrules.make files included by the same
# Makefile don't overwrite each other's variables.
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_DEFINED tags.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed. To prevent a macro definition from being
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
# function macros are typically used for boiler-plate code, and will confuse
# the parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
# The TAGFILES option can be used to specify one or more tagfiles.
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
# does not have to be run to correct the links.
# Note that each tag file must have a unique name
# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
# in the modules index. If set to NO, only the current project's groups will
# be listed.
EXTERNAL_GROUPS = YES
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
# or super classes. Setting the tag to NO turns the diagrams off. Note that
# this option is superseded by the HAVE_DOT option below. This is only a
# fallback. It is recommended to install and use dot, since it yields more
# powerful graphs.
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
# be found in the default search path.
MSCGEN_PATH =
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, a graph visualization
# toolkit from AT&T and Lucent Bell Labs. The other options in this section
# have no effect if this option is set to NO (the default)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for groups, showing the direct groups dependencies
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
UML_LOOK = NO
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = NO
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
# generate a call dependency graph for every global function or class method.
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command.
CALL_GRAPH = NO
# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will
# generate a caller dependency graph for every global function or class method.
# Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command.
CALLER_GRAPH = NO
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
# then doxygen will show the dependencies a directory has on other directories
# in a graphical way. The dependency relations are determined by the #include
# relations between the files in the directories.
DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
# nodes that will be shown in the graph. If the number of nodes in a graph
# becomes larger than this value, doxygen will truncate the graph, which is
# visualized by representing a node as a red box. Note that doxygen will always
# show the root nodes and its direct children regardless of this setting.
DOT_GRAPH_MAX_NODES = 50
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, which results in a white background.
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10)
# support this, this feature is disabled by default.
DOT_MULTI_TARGETS = NO
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::additions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
memcached-1.4.25/doc/readme.txt 0000644 0001750 0001750 00000000112 12250557060 013171 0000000 0000000 To build the documentation you need xml2rfc ( http://xml.resource.org/ ).
memcached-1.4.25/doc/protocol-binary.xml 0000664 0001750 0001750 00000172404 12623270407 015061 0000000 0000000
Memcache Binary Protocol Six Apart, Ltd.548 4th StreetSan FranciscoCA94107USAaaron@serendipity.palo-alto.ca.usSun Microsystems, INCHaakon VII g. 7BTrondheimNO-7485 TrondheimNorwaytrond.norbye@sun.com
Applications
memcache memcached cache
This memo explains the memcache binary protocol for informational
purposes.
Memcache is a high performance key-value cache. It is intentionally a
dumb cache, optimized for speed only. Applications using memcache do
not rely on it for data -- a persistent database with guaranteed
reliability is strongly recommended -- but applications can run much
faster when cached data is available in memcache.
Memcache is a high performance key-value cache. It is intentionally a
dumb cache, optimized for speed only. Applications using memcache should
not rely on it for data -- a persistent database with guaranteed
reliability is strongly recommended -- but applications can run much
faster when cached data is available in memcache.
Memcache was originally written to make
LiveJournal faster. It now powers all of
the fastest web sites that you love.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in
.
General format of a packet:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0/ HEADER /
/ /
/ /
/ /
+---------------+---------------+---------------+---------------+
24/ COMMAND-SPECIFIC EXTRAS (as needed) /
+/ (note length in the extras length header field) /
+---------------+---------------+---------------+---------------+
m/ Key (as needed) /
+/ (note length in key length header field) /
+---------------+---------------+---------------+---------------+
n/ Value (as needed) /
+/ (note length is total body length header field, minus /
+/ sum of the extras and key length body fields) /
+---------------+---------------+---------------+---------------+
Total 24 bytes
Request header:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Magic | Opcode | Key length |
+---------------+---------------+---------------+---------------+
4| Extras length | Data type | Reserved |
+---------------+---------------+---------------+---------------+
8| Total body length |
+---------------+---------------+---------------+---------------+
12| Opaque |
+---------------+---------------+---------------+---------------+
16| CAS |
| |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Response header:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Magic | Opcode | Key Length |
+---------------+---------------+---------------+---------------+
4| Extras length | Data type | Status |
+---------------+---------------+---------------+---------------+
8| Total body length |
+---------------+---------------+---------------+---------------+
12| Opaque |
+---------------+---------------+---------------+---------------+
16| CAS |
| |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Header fields:
Magic number.Command code.Length in bytes of the text key that follows the command extras.Status of the response (non-zero on error).Length in bytes of the command extras.Reserved for future use (Sean is using this soon).Really reserved for future use (up for grabs).Length in bytes of extra + key + value.Will be copied back to you in the response.Data version check.Request packet for this protocol versionResponse packet for this protocol version
Magic byte / version. For each version of the protocol, we'll use a
different request/response value pair. This is useful for protocol
analyzers to distinguish the nature of the packet from the direction
which it is moving. Note, it is common to run a memcached instance on
a host that also runs an application server. Such a host will both
send and receive memcache packets.
The version should hopefully correspond only to different meanings of
the command byte. In an ideal world, we will not change the header
format. As reserved bytes are given defined meaning, the protocol
version / magic byte values should be incremented.
Traffic analysis tools are encouraged to identify memcache packets
and provide detailed interpretation if the magic bytes are recognized
and otherwise to provide a generic breakdown of the packet. Note, that
the key and value positions can always be identified even if the magic
byte or command opcode are not recognized.
Possible values of this two-byte field:
No errorKey not foundKey existsValue too largeInvalid argumentsItem not storedIncr/Decr on non-numeric value.Unknown commandOut of memory
Possible values of the one-byte field:
GetSetAddReplaceDeleteIncrementDecrementQuitFlushGetQNo-opVersionGetKGetKQAppendPrependStatSetQAddQReplaceQDeleteQIncrementQDecrementQQuitQFlushQAppendQPrependQ
As a convention all of the commands ending with "Q" for
Quiet. A quiet version of a command will omit responses
that are considered uninteresting. Whether a given response
is interesting is dependent upon the command. See the
descriptions of the
set commands
and set commands for
examples of commands that include quiet variants.
Possible values of the one-byte field:
Raw bytes
All communication is initiated by a request from the client,
and the server will respond to each request with zero or
multiple packets for each request. If the status code of a response
packet is non-nil, the body of the packet will contain a textual error
message. If the status code is nil, the command opcode will define the
layout of the body of the message.
The following figure illustrates the packet layout for
a packet with an error message.
Packet layout:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x01 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x09 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x4e ('N') | 0x6f ('o') | 0x74 ('t') | 0x20 (' ') |
+---------------+---------------+---------------+---------------+
28| 0x66 ('f') | 0x6f ('o') | 0x75 ('u') | 0x6e ('n') |
+---------------+---------------+---------------+---------------+
32| 0x64 ('d') |
+---------------+
Total 33 bytes (24 byte header, and 9 bytes value)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x00
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Status (6,7) : 0x0001
Total body (8-11) : 0x00000009
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key : None
Value (24-32): The textual string "Not found"
Request:
MUST NOT have extras.MUST have key.MUST NOT have value.
Response (if found):
MUST have extras.MAY have key.MAY have value.4 byte flagsExtra data for the get commands:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Flags |
+---------------+---------------+---------------+---------------+
Total 4 bytes
The get command gets a single key. The getq command is both mum
on cache miss and quiet, holding its response until a non-quiet
command is issued. Getk and getkq differs from get and getq by
adding the key into the response packet.
You're not guaranteed a response to a getq/getkq cache hit until
you send a non-getq/getkq command later, which uncorks the
server and bundles up IOs to send to the client in one go.
Clients should implement multi-get (still important for
reducing network roundtrips!) as n pipelined requests, the
first n-1 being getq/getkq, the last being a regular
get/getk. That way you're guaranteed to get a response, and
you know when the server's done. You can also do the naive
thing and send n pipelined get/getks, but then you could potentially
get back a lot of "NOT_FOUND" error code packets.
Alternatively, you can send 'n' getq/getkqs, followed by a
'noop' command.
To request the data associated with the key "Hello" the
following fields must be specified in the packet.
get request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
28| 0x6f ('o') |
+---------------+
Total 29 bytes (24 byte header, and 5 bytes key)
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x00
Key length (2,3) : 0x0005
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000005
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key (24-29): The textual string: "Hello"
Value : None
If the item exist on the server the following packet is returned,
otherwise a packet with status code != 0 will be returned (see
Introduction)
get/getq response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x04 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x09 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x01 |
+---------------+---------------+---------------+---------------+
24| 0xde | 0xad | 0xbe | 0xef |
+---------------+---------------+---------------+---------------+
28| 0x57 ('W') | 0x6f ('o') | 0x72 ('r') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
32| 0x64 ('d') |
+---------------+
Total 33 bytes (24 byte header, 4 byte extras and 5 byte value)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x00
Key length (2,3) : 0x0000
Extra length (4) : 0x04
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000009
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000001
Extras :
Flags (24-27): 0xdeadbeef
Key : None
Value (28-32): The textual string "World"
getk/getkq response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
4| 0x04 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x09 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x01 |
+---------------+---------------+---------------+---------------+
24| 0xde | 0xad | 0xbe | 0xef |
+---------------+---------------+---------------+---------------+
28| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
32| 0x6f ('o') | 0x57 ('W') | 0x6f ('o') | 0x72 ('r') |
+---------------+---------------+---------------+---------------+
36| 0x6c ('l') | 0x64 ('d') |
+---------------+---------------+
Total 38 bytes (24 byte header, 4 byte extras, 5 byte key
and 5 byte value)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x00
Key length (2,3) : 0x0005
Extra length (4) : 0x04
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000009
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000001
Extras :
Flags (24-27): 0xdeadbeef
Key (28-32): The textual string: "Hello"
Value (33-37): The textual string: "World"
MUST have extras.MUST have key.MUST have value.4 byte flags4 byte expiration timeExtra data for set/add/replace:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Flags |
+---------------+---------------+---------------+---------------+
4| Expiration |
+---------------+---------------+---------------+---------------+
Total 8 bytes
If the Data Version Check (CAS) is nonzero, the requested
operation MUST only succeed if the item exists and has a CAS value
identical to the provided value.
Add MUST fail if the item already exist.
Replace MUST fail if the item doesn't exist.
Set should store the data unconditionally if the item exists
or not.
Quiet mutations only return responses on failure. Success
is considered the general case and is suppressed when in
quiet mode, but errors should not be allowed to go
unnoticed.
The following figure shows an add-command for
Key: "Hello"Value: "World"Flags: 0xdeadbeefExpiry: in two hoursAdd request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x02 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
4| 0x08 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x12 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0xde | 0xad | 0xbe | 0xef |
+---------------+---------------+---------------+---------------+
28| 0x00 | 0x00 | 0x0e | 0x10 |
+---------------+---------------+---------------+---------------+
32| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
36| 0x6f ('o') | 0x57 ('W') | 0x6f ('o') | 0x72 ('r') |
+---------------+---------------+---------------+---------------+
40| 0x6c ('l') | 0x64 ('d') |
+---------------+---------------+
Total 42 bytes (24 byte header, 8 byte extras, 5 byte key and
5 byte value)
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x02
Key length (2,3) : 0x0005
Extra length (4) : 0x08
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000012
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras :
Flags (24-27): 0xdeadbeef
Expiry (28-31): 0x00000e10
Key (32-36): The textual string "Hello"
Value (37-41): The textual string "World"
The response-packet contains no extra data, and the result of the
operation is signaled through the status code. If the command
succeeds, the CAS value for the item is returned in the CAS-field
of the packet.
Successful add response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x02 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x01 |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x02
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000000
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000001
Extras : None
Key : None
Value : None
MUST NOT have extras.MUST have key.MUST NOT have value.
Delete the item with the specific key.
The following figure shows a delete message for the
item "Hello".Delete request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x04 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
28| 0x6f ('o') |
+---------------+
Total 29 bytes (24 byte header, 5 byte value)
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x04
Key length (2,3) : 0x0005
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000005
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key : The textual string "Hello"
Value : None
The response-packet contains no extra data, and the result of the
operation is signaled through the status code.
MUST have extras.MUST have key.MUST NOT have value.8 byte value to add / subtract8 byte initial value (unsigned)4 byte expiration timeExtra data for incr/decr:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Amount to add |
| |
+---------------+---------------+---------------+---------------+
8| Initial value |
| |
+---------------+---------------+---------------+---------------+
16| Expiration |
+---------------+---------------+---------------+---------------+
Total 20 bytes
These commands will either add or remove the specified
amount to the requested counter.
If the counter does not exist, one of two things may happen:
If the expiration value is all one-bits (0xffffffff), the
operation will fail with NOT_FOUND.For all other expiration values, the operation will succeed
by seeding the value for this key with the provided initial
value to expire with the provided expiration time. The flags
will be set to zero.incr/decr response body:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 64-bit unsigned response. |
| |
+---------------+---------------+---------------+---------------+
Total 8 bytes
The following figure shows an incr-command for
Key: "counter"Delta: 0x01Initial: 0x00Expiry: in two hoursIncrement request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x05 | 0x00 | 0x07 |
+---------------+---------------+---------------+---------------+
4| 0x14 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x1b |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
28| 0x00 | 0x00 | 0x00 | 0x01 |
+---------------+---------------+---------------+---------------+
32| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
36| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
40| 0x00 | 0x00 | 0x0e | 0x10 |
+---------------+---------------+---------------+---------------+
44| 0x63 ('c') | 0x6f ('o') | 0x75 ('u') | 0x6e ('n') |
+---------------+---------------+---------------+---------------+
48| 0x74 ('t') | 0x65 ('e') | 0x72 ('r') |
+---------------+---------------+---------------+
Total 51 bytes (24 byte header, 20 byte extras, 7 byte key)
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x05
Key length (2,3) : 0x0007
Extra length (4) : 0x14
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x0000001b
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras :
delta (24-31): 0x0000000000000001
initial (32-39): 0x0000000000000000
exipration (40-43): 0x00000e10
Key : Textual string "counter"
Value : None
If the key doesn't exist, the server will respond with the
initial value. If not the incremented value will be returned.
Let's assume that the key didn't exist, so the initial value
is returned.
Increment response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x05 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x08 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
24| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
28| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
Total 32 bytes (24 byte header, 8 byte value)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x05
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000008
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000005
Extras : None
Key : None
Value : 0x0000000000000000
MUST NOT have extras.MUST NOT have key.MUST NOT have value.
Close the connection to the server.
Quit request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x07 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x07
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000000
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key : None
Value : None
The response-packet contains no extra data, and the result of the
operation is signaled through the status code. The server will
then close the connection.
MAY have extras.MUST NOT have key.MUST NOT have value.4 byte expiration timeExtra data for flush:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Expiration |
+---------------+---------------+---------------+---------------+
Total 4 bytes
Flush the items in the cache now or some time in the future as
specified by the expiration field. See the documentation of the
textual protocol for the full description on how to specify the
expiration time.
To flush the cache (delete all items) in two hours, the set
the following values in the request
Flush request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x08 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x04 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x04 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x00 | 0x00 | 0x0e | 0x10 |
+---------------+---------------+---------------+---------------+
Total 28 bytes (24 byte header, 4 byte body)
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x08
Key length (2,3) : 0x0000
Extra length (4) : 0x04
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000004
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras :
Expiry (24-27): 0x000e10
Key : None
Value : None
The response-packet contains no extra data, and the result of the
operation is signaled through the status code.
MUST NOT have extras.MUST NOT have key.MUST NOT have value.
Used as a keep alive. Flushes outstanding getq/getkq's.
Noop request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x0a | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x0a
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000000
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key : None
Value : None
The response-packet contains no extra data, and the result of the
operation is signaled through the status code.
MUST NOT have extras.MUST NOT have key.MUST NOT have value.
Request the server version.
The server responds with a packet containing the version string
in the body with the following format: "x.y.z"
Version request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x0b | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x0b
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000000
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Version response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x0b | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x31 ('1') | 0x2e ('.') | 0x33 ('3') | 0x2e ('.') |
+---------------+---------------+---------------+---------------+
28| 0x31 ('1') |
+---------------+
Total 29 bytes (24 byte header, 5 byte body)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x0b
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000005
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key : None
Value : Textual string "1.3.1"
MUST NOT have extras.MUST have key.MUST have value.
These commands will either append or prepend the specified
value to the requested key.
The following example appends '!' to the 'Hello' key.Append request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x0e | 0x00 | 0x05 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x06 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x48 ('H') | 0x65 ('e') | 0x6c ('l') | 0x6c ('l') |
+---------------+---------------+---------------+---------------+
28| 0x6f ('o') | 0x21 ('!') |
+---------------+---------------+
Total 30 bytes (24 byte header, 5 byte key, 1 byte value)
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x0e
Key length (2,3) : 0x0005
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000006
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key (24-28): The textual string "Hello"
Value (29) : "!"
The response-packet contains no extra data, and the result of the
operation is signaled through the status code.
MUST NOT have extras.MAY have key.MUST NOT have value.
Request server statistics. Without a key specified the server will
respond with a "default" set of statistics information. Each piece
of statistical information is returned in its own packet (key
contains the name of the statistical item and the body contains the
value in ASCII format). The sequence of return packets is terminated
with a packet that contains no key and no value.
The following example requests all statistics from the serverStat request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x80 | 0x10 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
Total 24 bytes
Field (offset) (value)
Magic (0) : 0x80
Opcode (1) : 0x10
Key length (2,3) : 0x0000
Extra length (4) : 0x00
Data type (5) : 0x00
Reserved (6,7) : 0x0000
Total body (8-11) : 0x00000000
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Extras : None
Key : None
Value : None
The server will send each value in a separate packet with
an "empty" packet (no key / no value) to terminate the sequence.
Each of the response packets look like the following example:
Stat response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| 0x81 | 0x10 | 0x00 | 0x03 |
+---------------+---------------+---------------+---------------+
4| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
8| 0x00 | 0x00 | 0x00 | 0x07 |
+---------------+---------------+---------------+---------------+
12| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
16| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
20| 0x00 | 0x00 | 0x00 | 0x00 |
+---------------+---------------+---------------+---------------+
24| 0x70 ('p') | 0x69 ('i') | 0x64 ('d') | 0x33 ('3') |
+---------------+---------------+---------------+---------------+
28| 0x30 ('0') | 0x37 ('7') | 0x38 ('8') |
+---------------+---------------+---------------+
Total 31 bytes (24 byte header, 3 byte key, 4 byte body)
Field (offset) (value)
Magic (0) : 0x81
Opcode (1) : 0x10
Key length (2,3) : 0x0003
Extra length (4) : 0x00
Data type (5) : 0x00
Status (6,7) : 0x0000
Total body (8-11) : 0x00000007
Opaque (12-15): 0x00000000
CAS (16-23): 0x0000000000000000
Exstras : None
Key : The textual string "pid"
Value : The textual string "3078"
Memcache has no authentication or security layers whatsoever. It is
RECOMMENDED that memcache be deployed strictly on closed, protected,
back-end networks within a single data center, within a single cluster of
servers, or even on a single host, providing shared caching for multiple
applications. Memcache MUST NOT be made available on a public network.
LJ NEEDS MOAR SPEEDDanga Interactivehttp://www.livejournal.com/
Thanks to Brad Fitzpatrick, Anatoly Vorobey, Steven Grimm, and Dustin
Sallings, for their work on the memcached server.
Thanks to Sean Chittenden, Jonathan Steinert, Brian Aker, Evan Martin,
Nathan Neulinger, Eric Hodel, Michael Johnson, Paul Querna, Jamie
McCarthy, Philip Neustrom, Andrew O'Brien, Josh Rotenberg, Robin H.
Johnson, Tim Yardley, Paolo Borelli, Eli Bingham, Jean-Francois
Bustarret, Paul G, Paul Lindner, Alan Kasindorf, Chris Goffinet, Tomash
Brechko, and others for their work reporting bugs and maintaining
memcached client libraries and bindings in many languages.
memcached-1.4.25/doc/memcached.1 0000664 0001750 0001750 00000013576 12623274034 013210 0000000 0000000 .TH MEMCACHED 1 "April 11, 2005"
.SH NAME
memcached \- high-performance memory object caching system
.SH SYNOPSIS
.B memcached
.RI [ options ]
.br
.SH DESCRIPTION
This manual page documents briefly the
.B memcached
memory object caching daemon.
.PP
.B memcached
is a flexible memory object caching daemon designed to alleviate database load
in dynamic web applications by storing objects in memory. It's based on
libevent to scale to any size needed, and is specifically optimized to avoid
swapping and always use non-blocking I/O.
.br
.SH OPTIONS
These programs follow the usual GNU command line syntax. A summary of options
is included below.
.TP
.B \-s
Unix socket path to listen on (disables network support).
.TP
.B \-a
Permissions (in octal format) for Unix socket created with \-s option.
.TP
.B \-l
Listen on ; default to INADDR_ANY. may be specified as host:port.
If you don't specify a port number, the value you specified with -p or -U is
used. You may specify multiple addresses separated by comma or by using -l
multiple times. This is an important option to consider as there is no other
way to secure the installation. Binding to an internal or firewalled network
interface is suggested.
.TP
.B \-d
Run memcached as a daemon.
.TP
.B \-u
Assume the identity of (only when run as root).
.TP
.B \-m
Use MB memory max to use for object storage; the default is 64 megabytes.
.TP
.B \-c
Use max simultaneous connections; the default is 1024.
.TP
.B \-R
This option seeks to prevent client starvation by setting a limit to the
number of sequential requests the server will process from an individual
client connection. Once a connection has exceeded this value, the server will
attempt to process I/O on other connections before handling any further
request from this connection. The default value for this option is 20.
.TP
.B \-k
Lock down all paged memory. This is a somewhat dangerous option with large
caches, so consult the README and memcached homepage for configuration
suggestions.
.TP
.B \-p
Listen on TCP port , the default is port 11211.
.TP
.B \-U
Listen on UDP port , the default is port 11211, 0 is off.
.TP
.B \-M
Disable automatic removal of items from the cache when out of memory.
Additions will not be possible until adequate space is freed up.
.TP
.B \-r
Raise the core file size limit to the maximum allowable.
.TP
.B \-f
Use as the multiplier for computing the sizes of memory chunks that
items are stored in. A lower value may result in less wasted memory depending
on the total amount of memory available and the distribution of item sizes.
The default is 1.25.
.TP
.B \-n
Allocate a minimum of bytes for the item key, value, and flags. The
default is 48. If you have a lot of small keys and values, you can get a
significant memory efficiency gain with a lower value. If you use a high
chunk growth factor (\-f option), on the other hand, you may want to increase
the size to allow a bigger percentage of your items to fit in the most densely
packed (smallest) chunks.
.TP
.B \-C
Disable the use of CAS (and reduce the per-item size by 8 bytes).
.TP
.B \-h
Show the version of memcached and a summary of options.
.TP
.B \-v
Be verbose during the event loop; print out errors and warnings.
.TP
.B \-vv
Be even more verbose; same as \-v but also print client commands and
responses.
.TP
.B \-i
Print memcached and libevent licenses.
.TP
.B \-P
Print pidfile to , only used under \-d option.
.TP
.B \-t
Number of threads to use to process incoming requests. This option is only
meaningful if memcached was compiled with thread support enabled. It is
typically not useful to set this higher than the number of CPU cores on the
memcached server. The default is 4.
.TP
.B \-D
Use as the delimiter between key prefixes and IDs. This is used for
per-prefix stats reporting. The default is ":" (colon). If this option is
specified, stats collection is turned on automatically; if not, then it may
be turned on by sending the "stats detail on" command to the server.
.TP
.B \-L
Try to use large memory pages (if available). Increasing the memory page size
could reduce the number of TLB misses and improve the performance. In order to
get large pages from the OS, memcached will allocate the total item-cache in
one large chunk. Only available if supported on your OS.
.TP
.B \-b
Set the backlog queue limit to connections. The default is 1024.
.TP
.B \-B
Specify the binding protocol to use. By default, the server will
autonegotiate client connections. By using this option, you can
specify the protocol clients must speak. Possible options are "auto"
(the default, autonegotiation behavior), "ascii" and "binary".
.TP
.B \-I
Override the default size of each slab page. The default size is 1mb. Default
value for this parameter is 1m, minimum is 1k, max is 128m.
Adjusting this value changes the item size limit.
Beware that this also increases the number of slabs (use -v to view), and the
overal memory usage of memcached.
.TP
.B \-S
Turn on SASL authentication. This option is only meaningful if memcached was
compiled with SASL support enabled.
.TP
.B \-F
Disables the "flush_all" command. The cmd_flush counter will increment, but
clients will receive an error message and the flush will not occur.
.TP
.B \-o
Comma separated list of extended or experimental options. See -h or wiki for
up to date list.
.br
.SH LICENSE
The memcached daemon is copyright Danga Interactive and is distributed under
the BSD license. Note that daemon clients are licensed separately.
.br
.SH SEE ALSO
The README file that comes with memcached
.br
.B http://www.memcached.org
.SH AUTHOR
The memcached daemon was written by Anatoly Vorobey
.B
and Brad Fitzpatrick
.B
and the rest of the crew of Danga Interactive
.B http://www.danga.com
.br
memcached-1.4.25/doc/protocol-binary-range.xml 0000664 0001750 0001750 00000022111 12623270407 016140 0000000 0000000
Memcache Binary Protocol: Extensions for UDP Six Apart, Ltd.548 4th StreetSan FranciscoCA94107USAaaron@serendipity.palo-alto.ca.us
Applications
memcache memcached cache udp
This memo explains extensions to the memcache binary protocol for use in a UDP environment.
Memcache is a high performance key-value cache. It is intentionally a
dumb cache, optimized for speed only. Applications using memcache do
not rely on it for data -- a persistent database with guaranteed reliability
is strongly recommended -- but applications can run much faster when
cached data is available in memcache.
Memcache is a high performance key-value cache. It is intentionally a
dumb cache, optimized for speed only. Applications using memcache do
not rely on it for data -- a persistent database with guaranteed reliability
is strongly recommended -- but applications can run much faster when
cached data is available in memcache.
Sites may find that, due to their network architecture or application usage patterns,
the stateless protocol better suits their needs. This document
provides extensions and descriptions of use of the memcache protocol
in a UDP environment.
It is a goal of this document to provide sufficient information in each UDP packet
as to avoid any requirement for statefulness on the part of the server nor significant
caching of outstanding packets on the part of the client.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in .
The magic bytes remains the same as in .
Additional status values:
Value is larger than a single response packet
Additional opcode values:
Get RangeSet Range
There are no new data types in this extension.
This section extends the behavior of the Get and GetQ commands as described in
.
When a Get or GetQ request is made via UDP, and the value of the key for which
the request was made is larger than can be placed into a single UDP packet (noting
that the protocol header must also be counted), a Get Range response packet
MUST be sent instead of the Get response packet. In this instance:
The Status field of the response header MUST be 0x0004.The Offset field of the GetR response extras MUST be 0.The Length field of the GetR response extras, and the data contained in
the Value field of the packet, SHOULD be the maximum
allowed length of a UDP packet, less the space required by the header
and extras; however it MAY be any amount below this maximum.The Total value length field of the response extras MUST be the
actual length of the complete value.
The client, upon receipt of a Get Range response bearing Status 0x004
and a Message ID corresponding to its Get request, shall then know that
it has received only the first portion of the value. The client MAY choose
to request the remaining portion of the value by sending one or more Get Range requests.
The Get Range request is primarily intended for use over a UDP transport
to request byte ranges of the value for a key. In the event that the Data version
check fails to match that of the key, an error MUST be returned.
Extra data for get range request:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Flags |
+---------------+---------------+---------------+---------------+
4| Data version check |
| |
+---------------+---------------+---------------+---------------+
12| Offset |
+---------------+---------------+---------------+---------------+
16| Length |
+---------------+---------------+---------------+---------------+
Total 20 bytes
The Get Range request is primarily intended for use over a UDP transport
to indicate the location of the bytes of the value for a key contained in
a given packet. A client receives enough information in each Get Range
extras to construct an appropriately sized buffer in its own memory and
blindly insert the contents of the packet at the given byte offset.
Extra data for get range response:
Byte/ 0 | 1 | 2 | 3 |
/ | | | |
|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+---------------+---------------+---------------+---------------+
0| Flags |
+---------------+---------------+---------------+---------------+
4| Data version check |
| |
+---------------+---------------+---------------+---------------+
12| Offset |
+---------------+---------------+---------------+---------------+
16| Length |
+---------------+---------------+---------------+---------------+
20| Total value length |
+---------------+---------------+---------------+---------------+
Total 24 bytes
This document does not introduce any new security considerations
beyond those discussed in .
memcached-1.4.25/doc/xml2rfc/ 0000755 0001750 0001750 00000000000 12623270407 012637 5 0000000 0000000 memcached-1.4.25/doc/xml2rfc/rfc2629.dtd 0000644 0001750 0001750 00000020745 12250557060 014360 0000000 0000000
%rfc2629-xhtml;
%rfc2629-other;
memcached-1.4.25/doc/xml2rfc/rfc2629-refchk.xsl 0000644 0001750 0001750 00000005006 12250557060 015644 0000000 0000000
memcached-1.4.25/doc/xml2rfc/reference.RFC.0768.xml 0000644 0001750 0001750 00000001300 12250557060 016244 0000000 0000000
User Datagram ProtocolUniversity of Southern California (USC)/Information Sciences Institute4676 Admiralty WayMarina del ReyCA90291US+1 213 822 1511
memcached-1.4.25/doc/xml2rfc/reference.RFC.2119.xml 0000664 0001750 0001750 00000003240 12623270407 016244 0000000 0000000
Key words for use in RFCs to Indicate Requirement LevelsHarvard University1350 Mass. Ave.CambridgeMA 02138- +1 617 495 3864sob@harvard.edu
General
keyword
In many standards track documents several words are used to signify
the requirements in the specification. These words are often
capitalized. This document defines these words as they should be
interpreted in IETF documents. Authors who follow these guidelines
should incorporate this phrase near the beginning of their document:
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL
NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
RFC 2119.
Note that the force of these words is modified by the requirement
level of the document in which they are used.
memcached-1.4.25/doc/xml2rfc/rfc2629-other.ent 0000644 0001750 0001750 00000007254 12250557060 015512 0000000 0000000
memcached-1.4.25/doc/xml2rfc/rfc2629-xhtml.ent 0000644 0001750 0001750 00000036170 12250557060 015524 0000000 0000000
memcached-1.4.25/doc/xml2rfc/rfc2629-noinc.xsl 0000664 0001750 0001750 00000006012 12623270407 015511 0000000 0000000
.xml
memcached-1.4.25/doc/Makefile.in 0000664 0001750 0001750 00000035752 12623542630 013265 0000000 0000000 # Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
@BUILD_SPECIFICATIONS_TRUE@am__append_1 = protocol-binary.txt protocol-binary-range.txt
subdir = doc
DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/version.m4 \
$(top_srcdir)/m4/c99-backport.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
man1dir = $(mandir)/man1
am__installdirs = "$(DESTDIR)$(man1dir)"
NROFF = nroff
MANS = $(man_MANS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DTRACE = @DTRACE@
DTRACEFLAGS = @DTRACEFLAGS@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
ENABLE_SASL = @ENABLE_SASL@
EXEEXT = @EXEEXT@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LTLIBOBJS = @LTLIBOBJS@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PROFILER = @PROFILER@
PROFILER_FLAGS = @PROFILER_FLAGS@
PROFILER_LDFLAGS = @PROFILER_LDFLAGS@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
XML2RFC = @XML2RFC@
XSLTPROC = @XSLTPROC@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
man_MANS = memcached.1
EXTRA_DIST = *.txt
BUILT_SOURCES = $(am__append_1)
@BUILD_SPECIFICATIONS_TRUE@MOSTLYCLEANFILES = protocol-binary.txt protocol-binary-range.txt
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign doc/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign doc/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-man1: $(man_MANS)
@$(NORMAL_INSTALL)
@list1=''; \
list2='$(man_MANS)'; \
test -n "$(man1dir)" \
&& test -n "`echo $$list1$$list2`" \
|| exit 0; \
echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \
$(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \
{ for i in $$list1; do echo "$$i"; done; \
if test -n "$$list2"; then \
for i in $$list2; do echo "$$i"; done \
| sed -n '/\.1[a-z]*$$/p'; \
fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
done | \
sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
sed 'N;N;s,\n, ,g' | { \
list=; while read file base inst; do \
if test "$$base" = "$$inst"; then list="$$list $$file"; else \
echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
$(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
fi; \
done; \
for i in $$list; do echo "$$i"; done | $(am__base_list) | \
while read files; do \
test -z "$$files" || { \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
done; }
uninstall-man1:
@$(NORMAL_UNINSTALL)
@list=''; test -n "$(man1dir)" || exit 0; \
files=`{ for i in $$list; do echo "$$i"; done; \
l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
sed -n '/\.1[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir)
tags TAGS:
ctags CTAGS:
cscope cscopelist:
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) check-am
all-am: Makefile $(MANS)
installdirs:
for dir in "$(DESTDIR)$(man1dir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
-test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES)
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
clean: clean-am
clean-am: clean-generic mostlyclean-am
distclean: distclean-am
-rm -f Makefile
distclean-am: clean-am distclean-generic
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-man
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man: install-man1
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-generic
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-man
uninstall-man: uninstall-man1
.MAKE: all check install install-am install-strip
.PHONY: all all-am check check-am clean clean-generic cscopelist-am \
ctags-am distclean distclean-generic distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-man1 install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
pdf-am ps ps-am tags-am uninstall uninstall-am uninstall-man \
uninstall-man1
protocol-binary.txt: $(.TARGET:R).full
@XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
protocol-binary-range.txt: $(.TARGET:R).full
@XML2RFC@ -c @abs_builddir@ $(.TARGET:R).full $@
protocol-binary.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
@XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
protocol-binary-range.full: $(.TARGET:R).xml xml2rfc/rfc2629-noinc.xsl
@XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl $(.TARGET:R).xml > $@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
memcached-1.4.25/memcached.c 0000664 0001750 0001750 00000575130 12623274034 012524 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* memcached - memory caching daemon
*
* http://www.memcached.org/
*
* Copyright 2003 Danga Interactive, Inc. All rights reserved.
*
* Use and distribution licensed under the BSD license. See
* the LICENSE file for full text.
*
* Authors:
* Anatoly Vorobey
* Brad Fitzpatrick
*/
#include "memcached.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* some POSIX systems need the following definition
* to get mlockall flags out of sys/mman.h. */
#ifndef _P1003_1B_VISIBLE
#define _P1003_1B_VISIBLE
#endif
/* need this to get IOV_MAX on some platforms. */
#ifndef __need_IOV_MAX
#define __need_IOV_MAX
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* FreeBSD 4.x doesn't have IOV_MAX exposed. */
#ifndef IOV_MAX
#if defined(__FreeBSD__) || defined(__APPLE__)
# define IOV_MAX 1024
#endif
#endif
/*
* forward declarations
*/
static void drive_machine(conn *c);
static int new_socket(struct addrinfo *ai);
static int try_read_command(conn *c);
enum try_read_result {
READ_DATA_RECEIVED,
READ_NO_DATA_RECEIVED,
READ_ERROR, /** an error occurred (on the socket) (or client closed connection) */
READ_MEMORY_ERROR /** failed to allocate more memory */
};
static enum try_read_result try_read_network(conn *c);
static enum try_read_result try_read_udp(conn *c);
static void conn_set_state(conn *c, enum conn_states state);
/* stats */
static void stats_init(void);
static void server_stats(ADD_STAT add_stats, conn *c);
static void process_stat_settings(ADD_STAT add_stats, void *c);
static void conn_to_str(const conn *c, char *buf);
/* defaults */
static void settings_init(void);
/* event handling, network IO */
static void event_handler(const int fd, const short which, void *arg);
static void conn_close(conn *c);
static void conn_init(void);
static bool update_event(conn *c, const int new_flags);
static void complete_nread(conn *c);
static void process_command(conn *c, char *command);
static void write_and_free(conn *c, char *buf, int bytes);
static int ensure_iov_space(conn *c);
static int add_iov(conn *c, const void *buf, int len);
static int add_msghdr(conn *c);
static void write_bin_error(conn *c, protocol_binary_response_status err,
const char *errstr, int swallow);
static void conn_free(conn *c);
/** exported globals **/
struct stats stats;
struct settings settings;
time_t process_started; /* when the process was started */
conn **conns;
struct slab_rebalance slab_rebal;
volatile int slab_rebalance_signal;
/** file scope variables **/
static conn *listen_conn = NULL;
static int max_fds;
static struct event_base *main_base;
enum transmit_result {
TRANSMIT_COMPLETE, /** All done writing. */
TRANSMIT_INCOMPLETE, /** More data remaining to write. */
TRANSMIT_SOFT_ERROR, /** Can't write any more right now. */
TRANSMIT_HARD_ERROR /** Can't write (c->state is set to conn_closing) */
};
static enum transmit_result transmit(conn *c);
/* This reduces the latency without adding lots of extra wiring to be able to
* notify the listener thread of when to listen again.
* Also, the clock timer could be broken out into its own thread and we
* can block the listener via a condition.
*/
static volatile bool allow_new_conns = true;
static struct event maxconnsevent;
static void maxconns_handler(const int fd, const short which, void *arg) {
struct timeval t = {.tv_sec = 0, .tv_usec = 10000};
if (fd == -42 || allow_new_conns == false) {
/* reschedule in 10ms if we need to keep polling */
evtimer_set(&maxconnsevent, maxconns_handler, 0);
event_base_set(main_base, &maxconnsevent);
evtimer_add(&maxconnsevent, &t);
} else {
evtimer_del(&maxconnsevent);
accept_new_conns(true);
}
}
#define REALTIME_MAXDELTA 60*60*24*30
/*
* given time value that's either unix time or delta from current unix time, return
* unix time. Use the fact that delta can't exceed one month (and real time value can't
* be that low).
*/
static rel_time_t realtime(const time_t exptime) {
/* no. of seconds in 30 days - largest possible delta exptime */
if (exptime == 0) return 0; /* 0 means never expire */
if (exptime > REALTIME_MAXDELTA) {
/* if item expiration is at/before the server started, give it an
expiration time of 1 second after the server started.
(because 0 means don't expire). without this, we'd
underflow and wrap around to some large value way in the
future, effectively making items expiring in the past
really expiring never */
if (exptime <= process_started)
return (rel_time_t)1;
return (rel_time_t)(exptime - process_started);
} else {
return (rel_time_t)(exptime + current_time);
}
}
static void stats_init(void) {
stats.curr_items = stats.total_items = stats.curr_conns = stats.total_conns = stats.conn_structs = 0;
stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = stats.reclaimed = 0;
stats.touch_cmds = stats.touch_misses = stats.touch_hits = stats.rejected_conns = 0;
stats.malloc_fails = 0;
stats.curr_bytes = stats.listen_disabled_num = 0;
stats.hash_power_level = stats.hash_bytes = stats.hash_is_expanding = 0;
stats.expired_unfetched = stats.evicted_unfetched = 0;
stats.slabs_moved = 0;
stats.lru_maintainer_juggles = 0;
stats.accepting_conns = true; /* assuming we start in this state. */
stats.slab_reassign_running = false;
stats.lru_crawler_running = false;
stats.lru_crawler_starts = 0;
stats.time_in_listen_disabled_us = 0;
/* make the time we started always be 2 seconds before we really
did, so time(0) - time.started is never zero. if so, things
like 'settings.oldest_live' which act as booleans as well as
values are now false in boolean context... */
process_started = time(0) - ITEM_UPDATE_INTERVAL - 2;
stats_prefix_init();
}
static void stats_reset(void) {
STATS_LOCK();
stats.total_items = stats.total_conns = 0;
stats.rejected_conns = 0;
stats.malloc_fails = 0;
stats.evictions = 0;
stats.reclaimed = 0;
stats.listen_disabled_num = 0;
stats_prefix_clear();
STATS_UNLOCK();
threadlocal_stats_reset();
item_stats_reset();
}
static void settings_init(void) {
settings.use_cas = true;
settings.access = 0700;
settings.port = 11211;
settings.udpport = 11211;
/* By default this string should be NULL for getaddrinfo() */
settings.inter = NULL;
settings.maxbytes = 64 * 1024 * 1024; /* default is 64MB */
settings.maxconns = 1024; /* to limit connections-related memory to about 5MB */
settings.verbose = 0;
settings.oldest_live = 0;
settings.oldest_cas = 0; /* supplements accuracy of oldest_live */
settings.evict_to_free = 1; /* push old items out of cache when memory runs out */
settings.socketpath = NULL; /* by default, not using a unix socket */
settings.factor = 1.25;
settings.chunk_size = 48; /* space for a modest key and value */
settings.num_threads = 4; /* N workers */
settings.num_threads_per_udp = 0;
settings.prefix_delimiter = ':';
settings.detail_enabled = 0;
settings.reqs_per_event = 20;
settings.backlog = 1024;
settings.binding_protocol = negotiating_prot;
settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */
settings.maxconns_fast = false;
settings.lru_crawler = false;
settings.lru_crawler_sleep = 100;
settings.lru_crawler_tocrawl = 0;
settings.lru_maintainer_thread = false;
settings.hot_lru_pct = 32;
settings.warm_lru_pct = 32;
settings.expirezero_does_not_evict = false;
settings.hashpower_init = 0;
settings.slab_reassign = false;
settings.slab_automove = 0;
settings.shutdown_command = false;
settings.tail_repair_time = TAIL_REPAIR_TIME_DEFAULT;
settings.flush_enabled = true;
settings.crawls_persleep = 1000;
}
/*
* Adds a message header to a connection.
*
* Returns 0 on success, -1 on out-of-memory.
*/
static int add_msghdr(conn *c)
{
struct msghdr *msg;
assert(c != NULL);
if (c->msgsize == c->msgused) {
msg = realloc(c->msglist, c->msgsize * 2 * sizeof(struct msghdr));
if (! msg) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
return -1;
}
c->msglist = msg;
c->msgsize *= 2;
}
msg = c->msglist + c->msgused;
/* this wipes msg_iovlen, msg_control, msg_controllen, and
msg_flags, the last 3 of which aren't defined on solaris: */
memset(msg, 0, sizeof(struct msghdr));
msg->msg_iov = &c->iov[c->iovused];
if (IS_UDP(c->transport) && c->request_addr_size > 0) {
msg->msg_name = &c->request_addr;
msg->msg_namelen = c->request_addr_size;
}
c->msgbytes = 0;
c->msgused++;
if (IS_UDP(c->transport)) {
/* Leave room for the UDP header, which we'll fill in later. */
return add_iov(c, NULL, UDP_HEADER_SIZE);
}
return 0;
}
extern pthread_mutex_t conn_lock;
/*
* Initializes the connections array. We don't actually allocate connection
* structures until they're needed, so as to avoid wasting memory when the
* maximum connection count is much higher than the actual number of
* connections.
*
* This does end up wasting a few pointers' worth of memory for FDs that are
* used for things other than connections, but that's worth it in exchange for
* being able to directly index the conns array by FD.
*/
static void conn_init(void) {
/* We're unlikely to see an FD much higher than maxconns. */
int next_fd = dup(1);
int headroom = 10; /* account for extra unexpected open FDs */
struct rlimit rl;
max_fds = settings.maxconns + headroom + next_fd;
/* But if possible, get the actual highest FD we can possibly ever see. */
if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
max_fds = rl.rlim_max;
} else {
fprintf(stderr, "Failed to query maximum file descriptor; "
"falling back to maxconns\n");
}
close(next_fd);
if ((conns = calloc(max_fds, sizeof(conn *))) == NULL) {
fprintf(stderr, "Failed to allocate connection structures\n");
/* This is unrecoverable so bail out early. */
exit(1);
}
}
static const char *prot_text(enum protocol prot) {
char *rv = "unknown";
switch(prot) {
case ascii_prot:
rv = "ascii";
break;
case binary_prot:
rv = "binary";
break;
case negotiating_prot:
rv = "auto-negotiate";
break;
}
return rv;
}
conn *conn_new(const int sfd, enum conn_states init_state,
const int event_flags,
const int read_buffer_size, enum network_transport transport,
struct event_base *base) {
conn *c;
assert(sfd >= 0 && sfd < max_fds);
c = conns[sfd];
if (NULL == c) {
if (!(c = (conn *)calloc(1, sizeof(conn)))) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
fprintf(stderr, "Failed to allocate connection object\n");
return NULL;
}
MEMCACHED_CONN_CREATE(c);
c->rbuf = c->wbuf = 0;
c->ilist = 0;
c->suffixlist = 0;
c->iov = 0;
c->msglist = 0;
c->hdrbuf = 0;
c->rsize = read_buffer_size;
c->wsize = DATA_BUFFER_SIZE;
c->isize = ITEM_LIST_INITIAL;
c->suffixsize = SUFFIX_LIST_INITIAL;
c->iovsize = IOV_LIST_INITIAL;
c->msgsize = MSG_LIST_INITIAL;
c->hdrsize = 0;
c->rbuf = (char *)malloc((size_t)c->rsize);
c->wbuf = (char *)malloc((size_t)c->wsize);
c->ilist = (item **)malloc(sizeof(item *) * c->isize);
c->suffixlist = (char **)malloc(sizeof(char *) * c->suffixsize);
c->iov = (struct iovec *)malloc(sizeof(struct iovec) * c->iovsize);
c->msglist = (struct msghdr *)malloc(sizeof(struct msghdr) * c->msgsize);
if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0 || c->iov == 0 ||
c->msglist == 0 || c->suffixlist == 0) {
conn_free(c);
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
fprintf(stderr, "Failed to allocate buffers for connection\n");
return NULL;
}
STATS_LOCK();
stats.conn_structs++;
STATS_UNLOCK();
c->sfd = sfd;
conns[sfd] = c;
}
c->transport = transport;
c->protocol = settings.binding_protocol;
/* unix socket mode doesn't need this, so zeroed out. but why
* is this done for every command? presumably for UDP
* mode. */
if (!settings.socketpath) {
c->request_addr_size = sizeof(c->request_addr);
} else {
c->request_addr_size = 0;
}
if (transport == tcp_transport && init_state == conn_new_cmd) {
if (getpeername(sfd, (struct sockaddr *) &c->request_addr,
&c->request_addr_size)) {
perror("getpeername");
memset(&c->request_addr, 0, sizeof(c->request_addr));
}
}
if (settings.verbose > 1) {
if (init_state == conn_listening) {
fprintf(stderr, "<%d server listening (%s)\n", sfd,
prot_text(c->protocol));
} else if (IS_UDP(transport)) {
fprintf(stderr, "<%d server listening (udp)\n", sfd);
} else if (c->protocol == negotiating_prot) {
fprintf(stderr, "<%d new auto-negotiating client connection\n",
sfd);
} else if (c->protocol == ascii_prot) {
fprintf(stderr, "<%d new ascii client connection.\n", sfd);
} else if (c->protocol == binary_prot) {
fprintf(stderr, "<%d new binary client connection.\n", sfd);
} else {
fprintf(stderr, "<%d new unknown (%d) client connection\n",
sfd, c->protocol);
assert(false);
}
}
c->state = init_state;
c->rlbytes = 0;
c->cmd = -1;
c->rbytes = c->wbytes = 0;
c->wcurr = c->wbuf;
c->rcurr = c->rbuf;
c->ritem = 0;
c->icurr = c->ilist;
c->suffixcurr = c->suffixlist;
c->ileft = 0;
c->suffixleft = 0;
c->iovused = 0;
c->msgcurr = 0;
c->msgused = 0;
c->authenticated = false;
c->write_and_go = init_state;
c->write_and_free = 0;
c->item = 0;
c->noreply = false;
event_set(&c->event, sfd, event_flags, event_handler, (void *)c);
event_base_set(base, &c->event);
c->ev_flags = event_flags;
if (event_add(&c->event, 0) == -1) {
perror("event_add");
return NULL;
}
STATS_LOCK();
stats.curr_conns++;
stats.total_conns++;
STATS_UNLOCK();
MEMCACHED_CONN_ALLOCATE(c->sfd);
return c;
}
static void conn_release_items(conn *c) {
assert(c != NULL);
if (c->item) {
item_remove(c->item);
c->item = 0;
}
while (c->ileft > 0) {
item *it = *(c->icurr);
assert((it->it_flags & ITEM_SLABBED) == 0);
item_remove(it);
c->icurr++;
c->ileft--;
}
if (c->suffixleft != 0) {
for (; c->suffixleft > 0; c->suffixleft--, c->suffixcurr++) {
cache_free(c->thread->suffix_cache, *(c->suffixcurr));
}
}
c->icurr = c->ilist;
c->suffixcurr = c->suffixlist;
}
static void conn_cleanup(conn *c) {
assert(c != NULL);
conn_release_items(c);
if (c->write_and_free) {
free(c->write_and_free);
c->write_and_free = 0;
}
if (c->sasl_conn) {
assert(settings.sasl);
sasl_dispose(&c->sasl_conn);
c->sasl_conn = NULL;
}
if (IS_UDP(c->transport)) {
conn_set_state(c, conn_read);
}
}
/*
* Frees a connection.
*/
void conn_free(conn *c) {
if (c) {
assert(c != NULL);
assert(c->sfd >= 0 && c->sfd < max_fds);
MEMCACHED_CONN_DESTROY(c);
conns[c->sfd] = NULL;
if (c->hdrbuf)
free(c->hdrbuf);
if (c->msglist)
free(c->msglist);
if (c->rbuf)
free(c->rbuf);
if (c->wbuf)
free(c->wbuf);
if (c->ilist)
free(c->ilist);
if (c->suffixlist)
free(c->suffixlist);
if (c->iov)
free(c->iov);
free(c);
}
}
static void conn_close(conn *c) {
assert(c != NULL);
/* delete the event, the socket and the conn */
event_del(&c->event);
if (settings.verbose > 1)
fprintf(stderr, "<%d connection closed.\n", c->sfd);
conn_cleanup(c);
MEMCACHED_CONN_RELEASE(c->sfd);
conn_set_state(c, conn_closed);
close(c->sfd);
pthread_mutex_lock(&conn_lock);
allow_new_conns = true;
pthread_mutex_unlock(&conn_lock);
STATS_LOCK();
stats.curr_conns--;
STATS_UNLOCK();
return;
}
/*
* Shrinks a connection's buffers if they're too big. This prevents
* periodic large "get" requests from permanently chewing lots of server
* memory.
*
* This should only be called in between requests since it can wipe output
* buffers!
*/
static void conn_shrink(conn *c) {
assert(c != NULL);
if (IS_UDP(c->transport))
return;
if (c->rsize > READ_BUFFER_HIGHWAT && c->rbytes < DATA_BUFFER_SIZE) {
char *newbuf;
if (c->rcurr != c->rbuf)
memmove(c->rbuf, c->rcurr, (size_t)c->rbytes);
newbuf = (char *)realloc((void *)c->rbuf, DATA_BUFFER_SIZE);
if (newbuf) {
c->rbuf = newbuf;
c->rsize = DATA_BUFFER_SIZE;
}
/* TODO check other branch... */
c->rcurr = c->rbuf;
}
if (c->isize > ITEM_LIST_HIGHWAT) {
item **newbuf = (item**) realloc((void *)c->ilist, ITEM_LIST_INITIAL * sizeof(c->ilist[0]));
if (newbuf) {
c->ilist = newbuf;
c->isize = ITEM_LIST_INITIAL;
}
/* TODO check error condition? */
}
if (c->msgsize > MSG_LIST_HIGHWAT) {
struct msghdr *newbuf = (struct msghdr *) realloc((void *)c->msglist, MSG_LIST_INITIAL * sizeof(c->msglist[0]));
if (newbuf) {
c->msglist = newbuf;
c->msgsize = MSG_LIST_INITIAL;
}
/* TODO check error condition? */
}
if (c->iovsize > IOV_LIST_HIGHWAT) {
struct iovec *newbuf = (struct iovec *) realloc((void *)c->iov, IOV_LIST_INITIAL * sizeof(c->iov[0]));
if (newbuf) {
c->iov = newbuf;
c->iovsize = IOV_LIST_INITIAL;
}
/* TODO check return value */
}
}
/**
* Convert a state name to a human readable form.
*/
static const char *state_text(enum conn_states state) {
const char* const statenames[] = { "conn_listening",
"conn_new_cmd",
"conn_waiting",
"conn_read",
"conn_parse_cmd",
"conn_write",
"conn_nread",
"conn_swallow",
"conn_closing",
"conn_mwrite",
"conn_closed" };
return statenames[state];
}
/*
* Sets a connection's current state in the state machine. Any special
* processing that needs to happen on certain state transitions can
* happen here.
*/
static void conn_set_state(conn *c, enum conn_states state) {
assert(c != NULL);
assert(state >= conn_listening && state < conn_max_state);
if (state != c->state) {
if (settings.verbose > 2) {
fprintf(stderr, "%d: going from %s to %s\n",
c->sfd, state_text(c->state),
state_text(state));
}
if (state == conn_write || state == conn_mwrite) {
MEMCACHED_PROCESS_COMMAND_END(c->sfd, c->wbuf, c->wbytes);
}
c->state = state;
}
}
/*
* Ensures that there is room for another struct iovec in a connection's
* iov list.
*
* Returns 0 on success, -1 on out-of-memory.
*/
static int ensure_iov_space(conn *c) {
assert(c != NULL);
if (c->iovused >= c->iovsize) {
int i, iovnum;
struct iovec *new_iov = (struct iovec *)realloc(c->iov,
(c->iovsize * 2) * sizeof(struct iovec));
if (! new_iov) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
return -1;
}
c->iov = new_iov;
c->iovsize *= 2;
/* Point all the msghdr structures at the new list. */
for (i = 0, iovnum = 0; i < c->msgused; i++) {
c->msglist[i].msg_iov = &c->iov[iovnum];
iovnum += c->msglist[i].msg_iovlen;
}
}
return 0;
}
/*
* Adds data to the list of pending data that will be written out to a
* connection.
*
* Returns 0 on success, -1 on out-of-memory.
*/
static int add_iov(conn *c, const void *buf, int len) {
struct msghdr *m;
int leftover;
bool limit_to_mtu;
assert(c != NULL);
do {
m = &c->msglist[c->msgused - 1];
/*
* Limit UDP packets, and the first payloads of TCP replies, to
* UDP_MAX_PAYLOAD_SIZE bytes.
*/
limit_to_mtu = IS_UDP(c->transport) || (1 == c->msgused);
/* We may need to start a new msghdr if this one is full. */
if (m->msg_iovlen == IOV_MAX ||
(limit_to_mtu && c->msgbytes >= UDP_MAX_PAYLOAD_SIZE)) {
add_msghdr(c);
m = &c->msglist[c->msgused - 1];
}
if (ensure_iov_space(c) != 0)
return -1;
/* If the fragment is too big to fit in the datagram, split it up */
if (limit_to_mtu && len + c->msgbytes > UDP_MAX_PAYLOAD_SIZE) {
leftover = len + c->msgbytes - UDP_MAX_PAYLOAD_SIZE;
len -= leftover;
} else {
leftover = 0;
}
m = &c->msglist[c->msgused - 1];
m->msg_iov[m->msg_iovlen].iov_base = (void *)buf;
m->msg_iov[m->msg_iovlen].iov_len = len;
c->msgbytes += len;
c->iovused++;
m->msg_iovlen++;
buf = ((char *)buf) + len;
len = leftover;
} while (leftover > 0);
return 0;
}
/*
* Constructs a set of UDP headers and attaches them to the outgoing messages.
*/
static int build_udp_headers(conn *c) {
int i;
unsigned char *hdr;
assert(c != NULL);
if (c->msgused > c->hdrsize) {
void *new_hdrbuf;
if (c->hdrbuf) {
new_hdrbuf = realloc(c->hdrbuf, c->msgused * 2 * UDP_HEADER_SIZE);
} else {
new_hdrbuf = malloc(c->msgused * 2 * UDP_HEADER_SIZE);
}
if (! new_hdrbuf) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
return -1;
}
c->hdrbuf = (unsigned char *)new_hdrbuf;
c->hdrsize = c->msgused * 2;
}
hdr = c->hdrbuf;
for (i = 0; i < c->msgused; i++) {
c->msglist[i].msg_iov[0].iov_base = (void*)hdr;
c->msglist[i].msg_iov[0].iov_len = UDP_HEADER_SIZE;
*hdr++ = c->request_id / 256;
*hdr++ = c->request_id % 256;
*hdr++ = i / 256;
*hdr++ = i % 256;
*hdr++ = c->msgused / 256;
*hdr++ = c->msgused % 256;
*hdr++ = 0;
*hdr++ = 0;
assert((void *) hdr == (caddr_t)c->msglist[i].msg_iov[0].iov_base + UDP_HEADER_SIZE);
}
return 0;
}
static void out_string(conn *c, const char *str) {
size_t len;
assert(c != NULL);
if (c->noreply) {
if (settings.verbose > 1)
fprintf(stderr, ">%d NOREPLY %s\n", c->sfd, str);
c->noreply = false;
conn_set_state(c, conn_new_cmd);
return;
}
if (settings.verbose > 1)
fprintf(stderr, ">%d %s\n", c->sfd, str);
/* Nuke a partial output... */
c->msgcurr = 0;
c->msgused = 0;
c->iovused = 0;
add_msghdr(c);
len = strlen(str);
if ((len + 2) > c->wsize) {
/* ought to be always enough. just fail for simplicity */
str = "SERVER_ERROR output line too long";
len = strlen(str);
}
memcpy(c->wbuf, str, len);
memcpy(c->wbuf + len, "\r\n", 2);
c->wbytes = len + 2;
c->wcurr = c->wbuf;
conn_set_state(c, conn_write);
c->write_and_go = conn_new_cmd;
return;
}
/*
* Outputs a protocol-specific "out of memory" error. For ASCII clients,
* this is equivalent to out_string().
*/
static void out_of_memory(conn *c, char *ascii_error) {
const static char error_prefix[] = "SERVER_ERROR ";
const static int error_prefix_len = sizeof(error_prefix) - 1;
if (c->protocol == binary_prot) {
/* Strip off the generic error prefix; it's irrelevant in binary */
if (!strncmp(ascii_error, error_prefix, error_prefix_len)) {
ascii_error += error_prefix_len;
}
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, ascii_error, 0);
} else {
out_string(c, ascii_error);
}
}
/*
* we get here after reading the value in set/add/replace commands. The command
* has been stored in c->cmd, and the item is ready in c->item.
*/
static void complete_nread_ascii(conn *c) {
assert(c != NULL);
item *it = c->item;
int comm = c->cmd;
enum store_item_type ret;
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
if (strncmp(ITEM_data(it) + it->nbytes - 2, "\r\n", 2) != 0) {
out_string(c, "CLIENT_ERROR bad data chunk");
} else {
ret = store_item(it, comm, c);
#ifdef ENABLE_DTRACE
uint64_t cas = ITEM_get_cas(it);
switch (c->cmd) {
case NREAD_ADD:
MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
(ret == 1) ? it->nbytes : -1, cas);
break;
case NREAD_REPLACE:
MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
(ret == 1) ? it->nbytes : -1, cas);
break;
case NREAD_APPEND:
MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
(ret == 1) ? it->nbytes : -1, cas);
break;
case NREAD_PREPEND:
MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
(ret == 1) ? it->nbytes : -1, cas);
break;
case NREAD_SET:
MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
(ret == 1) ? it->nbytes : -1, cas);
break;
case NREAD_CAS:
MEMCACHED_COMMAND_CAS(c->sfd, ITEM_key(it), it->nkey, it->nbytes,
cas);
break;
}
#endif
switch (ret) {
case STORED:
out_string(c, "STORED");
break;
case EXISTS:
out_string(c, "EXISTS");
break;
case NOT_FOUND:
out_string(c, "NOT_FOUND");
break;
case NOT_STORED:
out_string(c, "NOT_STORED");
break;
default:
out_string(c, "SERVER_ERROR Unhandled storage type.");
}
}
item_remove(c->item); /* release the c->item reference */
c->item = 0;
}
/**
* get a pointer to the start of the request struct for the current command
*/
static void* binary_get_request(conn *c) {
char *ret = c->rcurr;
ret -= (sizeof(c->binary_header) + c->binary_header.request.keylen +
c->binary_header.request.extlen);
assert(ret >= c->rbuf);
return ret;
}
/**
* get a pointer to the key in this request
*/
static char* binary_get_key(conn *c) {
return c->rcurr - (c->binary_header.request.keylen);
}
static void add_bin_header(conn *c, uint16_t err, uint8_t hdr_len, uint16_t key_len, uint32_t body_len) {
protocol_binary_response_header* header;
assert(c);
c->msgcurr = 0;
c->msgused = 0;
c->iovused = 0;
if (add_msghdr(c) != 0) {
/* This should never run out of memory because iov and msg lists
* have minimum sizes big enough to hold an error response.
*/
out_of_memory(c, "SERVER_ERROR out of memory adding binary header");
return;
}
header = (protocol_binary_response_header *)c->wbuf;
header->response.magic = (uint8_t)PROTOCOL_BINARY_RES;
header->response.opcode = c->binary_header.request.opcode;
header->response.keylen = (uint16_t)htons(key_len);
header->response.extlen = (uint8_t)hdr_len;
header->response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES;
header->response.status = (uint16_t)htons(err);
header->response.bodylen = htonl(body_len);
header->response.opaque = c->opaque;
header->response.cas = htonll(c->cas);
if (settings.verbose > 1) {
int ii;
fprintf(stderr, ">%d Writing bin response:", c->sfd);
for (ii = 0; ii < sizeof(header->bytes); ++ii) {
if (ii % 4 == 0) {
fprintf(stderr, "\n>%d ", c->sfd);
}
fprintf(stderr, " 0x%02x", header->bytes[ii]);
}
fprintf(stderr, "\n");
}
add_iov(c, c->wbuf, sizeof(header->response));
}
/**
* Writes a binary error response. If errstr is supplied, it is used as the
* error text; otherwise a generic description of the error status code is
* included.
*/
static void write_bin_error(conn *c, protocol_binary_response_status err,
const char *errstr, int swallow) {
size_t len;
if (!errstr) {
switch (err) {
case PROTOCOL_BINARY_RESPONSE_ENOMEM:
errstr = "Out of memory";
break;
case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND:
errstr = "Unknown command";
break;
case PROTOCOL_BINARY_RESPONSE_KEY_ENOENT:
errstr = "Not found";
break;
case PROTOCOL_BINARY_RESPONSE_EINVAL:
errstr = "Invalid arguments";
break;
case PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS:
errstr = "Data exists for key.";
break;
case PROTOCOL_BINARY_RESPONSE_E2BIG:
errstr = "Too large.";
break;
case PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL:
errstr = "Non-numeric server-side value for incr or decr";
break;
case PROTOCOL_BINARY_RESPONSE_NOT_STORED:
errstr = "Not stored.";
break;
case PROTOCOL_BINARY_RESPONSE_AUTH_ERROR:
errstr = "Auth failure.";
break;
default:
assert(false);
errstr = "UNHANDLED ERROR";
fprintf(stderr, ">%d UNHANDLED ERROR: %d\n", c->sfd, err);
}
}
if (settings.verbose > 1) {
fprintf(stderr, ">%d Writing an error: %s\n", c->sfd, errstr);
}
len = strlen(errstr);
add_bin_header(c, err, 0, 0, len);
if (len > 0) {
add_iov(c, errstr, len);
}
conn_set_state(c, conn_mwrite);
if(swallow > 0) {
c->sbytes = swallow;
c->write_and_go = conn_swallow;
} else {
c->write_and_go = conn_new_cmd;
}
}
/* Form and send a response to a command over the binary protocol */
static void write_bin_response(conn *c, void *d, int hlen, int keylen, int dlen) {
if (!c->noreply || c->cmd == PROTOCOL_BINARY_CMD_GET ||
c->cmd == PROTOCOL_BINARY_CMD_GETK) {
add_bin_header(c, 0, hlen, keylen, dlen);
if(dlen > 0) {
add_iov(c, d, dlen);
}
conn_set_state(c, conn_mwrite);
c->write_and_go = conn_new_cmd;
} else {
conn_set_state(c, conn_new_cmd);
}
}
static void complete_incr_bin(conn *c) {
item *it;
char *key;
size_t nkey;
/* Weird magic in add_delta forces me to pad here */
char tmpbuf[INCR_MAX_STORAGE_LEN];
uint64_t cas = 0;
protocol_binary_response_incr* rsp = (protocol_binary_response_incr*)c->wbuf;
protocol_binary_request_incr* req = binary_get_request(c);
assert(c != NULL);
assert(c->wsize >= sizeof(*rsp));
/* fix byteorder in the request */
req->message.body.delta = ntohll(req->message.body.delta);
req->message.body.initial = ntohll(req->message.body.initial);
req->message.body.expiration = ntohl(req->message.body.expiration);
key = binary_get_key(c);
nkey = c->binary_header.request.keylen;
if (settings.verbose > 1) {
int i;
fprintf(stderr, "incr ");
for (i = 0; i < nkey; i++) {
fprintf(stderr, "%c", key[i]);
}
fprintf(stderr, " %lld, %llu, %d\n",
(long long)req->message.body.delta,
(long long)req->message.body.initial,
req->message.body.expiration);
}
if (c->binary_header.request.cas != 0) {
cas = c->binary_header.request.cas;
}
switch(add_delta(c, key, nkey, c->cmd == PROTOCOL_BINARY_CMD_INCREMENT,
req->message.body.delta, tmpbuf,
&cas)) {
case OK:
rsp->message.body.value = htonll(strtoull(tmpbuf, NULL, 10));
if (cas) {
c->cas = cas;
}
write_bin_response(c, &rsp->message.body, 0, 0,
sizeof(rsp->message.body.value));
break;
case NON_NUMERIC:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_DELTA_BADVAL, NULL, 0);
break;
case EOM:
out_of_memory(c, "SERVER_ERROR Out of memory incrementing value");
break;
case DELTA_ITEM_NOT_FOUND:
if (req->message.body.expiration != 0xffffffff) {
/* Save some room for the response */
rsp->message.body.value = htonll(req->message.body.initial);
snprintf(tmpbuf, INCR_MAX_STORAGE_LEN, "%llu",
(unsigned long long)req->message.body.initial);
int res = strlen(tmpbuf);
it = item_alloc(key, nkey, 0, realtime(req->message.body.expiration),
res + 2);
if (it != NULL) {
memcpy(ITEM_data(it), tmpbuf, res);
memcpy(ITEM_data(it) + res, "\r\n", 2);
if (store_item(it, NREAD_ADD, c)) {
c->cas = ITEM_get_cas(it);
write_bin_response(c, &rsp->message.body, 0, 0, sizeof(rsp->message.body.value));
} else {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_NOT_STORED,
NULL, 0);
}
item_remove(it); /* release our reference */
} else {
out_of_memory(c,
"SERVER_ERROR Out of memory allocating new item");
}
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
if (c->cmd == PROTOCOL_BINARY_CMD_INCREMENT) {
c->thread->stats.incr_misses++;
} else {
c->thread->stats.decr_misses++;
}
pthread_mutex_unlock(&c->thread->stats.mutex);
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
}
break;
case DELTA_ITEM_CAS_MISMATCH:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
break;
}
}
static void complete_update_bin(conn *c) {
protocol_binary_response_status eno = PROTOCOL_BINARY_RESPONSE_EINVAL;
enum store_item_type ret = NOT_STORED;
assert(c != NULL);
item *it = c->item;
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].set_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
/* We don't actually receive the trailing two characters in the bin
* protocol, so we're going to just set them here */
*(ITEM_data(it) + it->nbytes - 2) = '\r';
*(ITEM_data(it) + it->nbytes - 1) = '\n';
ret = store_item(it, c->cmd, c);
#ifdef ENABLE_DTRACE
uint64_t cas = ITEM_get_cas(it);
switch (c->cmd) {
case NREAD_ADD:
MEMCACHED_COMMAND_ADD(c->sfd, ITEM_key(it), it->nkey,
(ret == STORED) ? it->nbytes : -1, cas);
break;
case NREAD_REPLACE:
MEMCACHED_COMMAND_REPLACE(c->sfd, ITEM_key(it), it->nkey,
(ret == STORED) ? it->nbytes : -1, cas);
break;
case NREAD_APPEND:
MEMCACHED_COMMAND_APPEND(c->sfd, ITEM_key(it), it->nkey,
(ret == STORED) ? it->nbytes : -1, cas);
break;
case NREAD_PREPEND:
MEMCACHED_COMMAND_PREPEND(c->sfd, ITEM_key(it), it->nkey,
(ret == STORED) ? it->nbytes : -1, cas);
break;
case NREAD_SET:
MEMCACHED_COMMAND_SET(c->sfd, ITEM_key(it), it->nkey,
(ret == STORED) ? it->nbytes : -1, cas);
break;
}
#endif
switch (ret) {
case STORED:
/* Stored */
write_bin_response(c, NULL, 0, 0, 0);
break;
case EXISTS:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
break;
case NOT_FOUND:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
break;
case NOT_STORED:
if (c->cmd == NREAD_ADD) {
eno = PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS;
} else if(c->cmd == NREAD_REPLACE) {
eno = PROTOCOL_BINARY_RESPONSE_KEY_ENOENT;
} else {
eno = PROTOCOL_BINARY_RESPONSE_NOT_STORED;
}
write_bin_error(c, eno, NULL, 0);
}
item_remove(c->item); /* release the c->item reference */
c->item = 0;
}
static void process_bin_get_or_touch(conn *c) {
item *it;
protocol_binary_response_get* rsp = (protocol_binary_response_get*)c->wbuf;
char* key = binary_get_key(c);
size_t nkey = c->binary_header.request.keylen;
int should_touch = (c->cmd == PROTOCOL_BINARY_CMD_TOUCH ||
c->cmd == PROTOCOL_BINARY_CMD_GAT ||
c->cmd == PROTOCOL_BINARY_CMD_GATK);
int should_return_key = (c->cmd == PROTOCOL_BINARY_CMD_GETK ||
c->cmd == PROTOCOL_BINARY_CMD_GATK);
int should_return_value = (c->cmd != PROTOCOL_BINARY_CMD_TOUCH);
if (settings.verbose > 1) {
fprintf(stderr, "<%d %s ", c->sfd, should_touch ? "TOUCH" : "GET");
fwrite(key, 1, nkey, stderr);
fputc('\n', stderr);
}
if (should_touch) {
protocol_binary_request_touch *t = binary_get_request(c);
time_t exptime = ntohl(t->message.body.expiration);
it = item_touch(key, nkey, realtime(exptime));
} else {
it = item_get(key, nkey);
}
if (it) {
/* the length has two unnecessary bytes ("\r\n") */
uint16_t keylen = 0;
uint32_t bodylen = sizeof(rsp->message.body) + (it->nbytes - 2);
item_update(it);
pthread_mutex_lock(&c->thread->stats.mutex);
if (should_touch) {
c->thread->stats.touch_cmds++;
c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
} else {
c->thread->stats.get_cmds++;
c->thread->stats.slab_stats[ITEM_clsid(it)].get_hits++;
}
pthread_mutex_unlock(&c->thread->stats.mutex);
if (should_touch) {
MEMCACHED_COMMAND_TOUCH(c->sfd, ITEM_key(it), it->nkey,
it->nbytes, ITEM_get_cas(it));
} else {
MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
it->nbytes, ITEM_get_cas(it));
}
if (c->cmd == PROTOCOL_BINARY_CMD_TOUCH) {
bodylen -= it->nbytes - 2;
} else if (should_return_key) {
bodylen += nkey;
keylen = nkey;
}
add_bin_header(c, 0, sizeof(rsp->message.body), keylen, bodylen);
rsp->message.header.response.cas = htonll(ITEM_get_cas(it));
// add the flags
rsp->message.body.flags = htonl(strtoul(ITEM_suffix(it), NULL, 10));
add_iov(c, &rsp->message.body, sizeof(rsp->message.body));
if (should_return_key) {
add_iov(c, ITEM_key(it), nkey);
}
if (should_return_value) {
/* Add the data minus the CRLF */
add_iov(c, ITEM_data(it), it->nbytes - 2);
}
conn_set_state(c, conn_mwrite);
c->write_and_go = conn_new_cmd;
/* Remember this command so we can garbage collect it later */
c->item = it;
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
if (should_touch) {
c->thread->stats.touch_cmds++;
c->thread->stats.touch_misses++;
} else {
c->thread->stats.get_cmds++;
c->thread->stats.get_misses++;
}
pthread_mutex_unlock(&c->thread->stats.mutex);
if (should_touch) {
MEMCACHED_COMMAND_TOUCH(c->sfd, key, nkey, -1, 0);
} else {
MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
}
if (c->noreply) {
conn_set_state(c, conn_new_cmd);
} else {
if (should_return_key) {
char *ofs = c->wbuf + sizeof(protocol_binary_response_header);
add_bin_header(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
0, nkey, nkey);
memcpy(ofs, key, nkey);
add_iov(c, ofs, nkey);
conn_set_state(c, conn_mwrite);
c->write_and_go = conn_new_cmd;
} else {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT,
NULL, 0);
}
}
}
if (settings.detail_enabled) {
stats_prefix_record_get(key, nkey, NULL != it);
}
}
static void append_bin_stats(const char *key, const uint16_t klen,
const char *val, const uint32_t vlen,
conn *c) {
char *buf = c->stats.buffer + c->stats.offset;
uint32_t bodylen = klen + vlen;
protocol_binary_response_header header = {
.response.magic = (uint8_t)PROTOCOL_BINARY_RES,
.response.opcode = PROTOCOL_BINARY_CMD_STAT,
.response.keylen = (uint16_t)htons(klen),
.response.datatype = (uint8_t)PROTOCOL_BINARY_RAW_BYTES,
.response.bodylen = htonl(bodylen),
.response.opaque = c->opaque
};
memcpy(buf, header.bytes, sizeof(header.response));
buf += sizeof(header.response);
if (klen > 0) {
memcpy(buf, key, klen);
buf += klen;
if (vlen > 0) {
memcpy(buf, val, vlen);
}
}
c->stats.offset += sizeof(header.response) + bodylen;
}
static void append_ascii_stats(const char *key, const uint16_t klen,
const char *val, const uint32_t vlen,
conn *c) {
char *pos = c->stats.buffer + c->stats.offset;
uint32_t nbytes = 0;
int remaining = c->stats.size - c->stats.offset;
int room = remaining - 1;
if (klen == 0 && vlen == 0) {
nbytes = snprintf(pos, room, "END\r\n");
} else if (vlen == 0) {
nbytes = snprintf(pos, room, "STAT %s\r\n", key);
} else {
nbytes = snprintf(pos, room, "STAT %s %s\r\n", key, val);
}
c->stats.offset += nbytes;
}
static bool grow_stats_buf(conn *c, size_t needed) {
size_t nsize = c->stats.size;
size_t available = nsize - c->stats.offset;
bool rv = true;
/* Special case: No buffer -- need to allocate fresh */
if (c->stats.buffer == NULL) {
nsize = 1024;
available = c->stats.size = c->stats.offset = 0;
}
while (needed > available) {
assert(nsize > 0);
nsize = nsize << 1;
available = nsize - c->stats.offset;
}
if (nsize != c->stats.size) {
char *ptr = realloc(c->stats.buffer, nsize);
if (ptr) {
c->stats.buffer = ptr;
c->stats.size = nsize;
} else {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
rv = false;
}
}
return rv;
}
static void append_stats(const char *key, const uint16_t klen,
const char *val, const uint32_t vlen,
const void *cookie)
{
/* value without a key is invalid */
if (klen == 0 && vlen > 0) {
return ;
}
conn *c = (conn*)cookie;
if (c->protocol == binary_prot) {
size_t needed = vlen + klen + sizeof(protocol_binary_response_header);
if (!grow_stats_buf(c, needed)) {
return ;
}
append_bin_stats(key, klen, val, vlen, c);
} else {
size_t needed = vlen + klen + 10; // 10 == "STAT = \r\n"
if (!grow_stats_buf(c, needed)) {
return ;
}
append_ascii_stats(key, klen, val, vlen, c);
}
assert(c->stats.offset <= c->stats.size);
}
static void process_bin_stat(conn *c) {
char *subcommand = binary_get_key(c);
size_t nkey = c->binary_header.request.keylen;
if (settings.verbose > 1) {
int ii;
fprintf(stderr, "<%d STATS ", c->sfd);
for (ii = 0; ii < nkey; ++ii) {
fprintf(stderr, "%c", subcommand[ii]);
}
fprintf(stderr, "\n");
}
if (nkey == 0) {
/* request all statistics */
server_stats(&append_stats, c);
(void)get_stats(NULL, 0, &append_stats, c);
} else if (strncmp(subcommand, "reset", 5) == 0) {
stats_reset();
} else if (strncmp(subcommand, "settings", 8) == 0) {
process_stat_settings(&append_stats, c);
} else if (strncmp(subcommand, "detail", 6) == 0) {
char *subcmd_pos = subcommand + 6;
if (strncmp(subcmd_pos, " dump", 5) == 0) {
int len;
char *dump_buf = stats_prefix_dump(&len);
if (dump_buf == NULL || len <= 0) {
out_of_memory(c, "SERVER_ERROR Out of memory generating stats");
return ;
} else {
append_stats("detailed", strlen("detailed"), dump_buf, len, c);
free(dump_buf);
}
} else if (strncmp(subcmd_pos, " on", 3) == 0) {
settings.detail_enabled = 1;
} else if (strncmp(subcmd_pos, " off", 4) == 0) {
settings.detail_enabled = 0;
} else {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
return;
}
} else {
if (get_stats(subcommand, nkey, &append_stats, c)) {
if (c->stats.buffer == NULL) {
out_of_memory(c, "SERVER_ERROR Out of memory generating stats");
} else {
write_and_free(c, c->stats.buffer, c->stats.offset);
c->stats.buffer = NULL;
}
} else {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
}
return;
}
/* Append termination package and start the transfer */
append_stats(NULL, 0, NULL, 0, c);
if (c->stats.buffer == NULL) {
out_of_memory(c, "SERVER_ERROR Out of memory preparing to send stats");
} else {
write_and_free(c, c->stats.buffer, c->stats.offset);
c->stats.buffer = NULL;
}
}
static void bin_read_key(conn *c, enum bin_substates next_substate, int extra) {
assert(c);
c->substate = next_substate;
c->rlbytes = c->keylen + extra;
/* Ok... do we have room for the extras and the key in the input buffer? */
ptrdiff_t offset = c->rcurr + sizeof(protocol_binary_request_header) - c->rbuf;
if (c->rlbytes > c->rsize - offset) {
size_t nsize = c->rsize;
size_t size = c->rlbytes + sizeof(protocol_binary_request_header);
while (size > nsize) {
nsize *= 2;
}
if (nsize != c->rsize) {
if (settings.verbose > 1) {
fprintf(stderr, "%d: Need to grow buffer from %lu to %lu\n",
c->sfd, (unsigned long)c->rsize, (unsigned long)nsize);
}
char *newm = realloc(c->rbuf, nsize);
if (newm == NULL) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
if (settings.verbose) {
fprintf(stderr, "%d: Failed to grow buffer.. closing connection\n",
c->sfd);
}
conn_set_state(c, conn_closing);
return;
}
c->rbuf= newm;
/* rcurr should point to the same offset in the packet */
c->rcurr = c->rbuf + offset - sizeof(protocol_binary_request_header);
c->rsize = nsize;
}
if (c->rbuf != c->rcurr) {
memmove(c->rbuf, c->rcurr, c->rbytes);
c->rcurr = c->rbuf;
if (settings.verbose > 1) {
fprintf(stderr, "%d: Repack input buffer\n", c->sfd);
}
}
}
/* preserve the header in the buffer.. */
c->ritem = c->rcurr + sizeof(protocol_binary_request_header);
conn_set_state(c, conn_nread);
}
/* Just write an error message and disconnect the client */
static void handle_binary_protocol_error(conn *c) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, 0);
if (settings.verbose) {
fprintf(stderr, "Protocol error (opcode %02x), close connection %d\n",
c->binary_header.request.opcode, c->sfd);
}
c->write_and_go = conn_closing;
}
static void init_sasl_conn(conn *c) {
assert(c);
/* should something else be returned? */
if (!settings.sasl)
return;
c->authenticated = false;
if (!c->sasl_conn) {
int result=sasl_server_new("memcached",
NULL,
my_sasl_hostname[0] ? my_sasl_hostname : NULL,
NULL, NULL,
NULL, 0, &c->sasl_conn);
if (result != SASL_OK) {
if (settings.verbose) {
fprintf(stderr, "Failed to initialize SASL conn.\n");
}
c->sasl_conn = NULL;
}
}
}
static void bin_list_sasl_mechs(conn *c) {
// Guard against a disabled SASL.
if (!settings.sasl) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
c->binary_header.request.bodylen
- c->binary_header.request.keylen);
return;
}
init_sasl_conn(c);
const char *result_string = NULL;
unsigned int string_length = 0;
int result=sasl_listmech(c->sasl_conn, NULL,
"", /* What to prepend the string with */
" ", /* What to separate mechanisms with */
"", /* What to append to the string */
&result_string, &string_length,
NULL);
if (result != SASL_OK) {
/* Perhaps there's a better error for this... */
if (settings.verbose) {
fprintf(stderr, "Failed to list SASL mechanisms.\n");
}
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
return;
}
write_bin_response(c, (char*)result_string, 0, 0, string_length);
}
static void process_bin_sasl_auth(conn *c) {
// Guard for handling disabled SASL on the server.
if (!settings.sasl) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
c->binary_header.request.bodylen
- c->binary_header.request.keylen);
return;
}
assert(c->binary_header.request.extlen == 0);
int nkey = c->binary_header.request.keylen;
int vlen = c->binary_header.request.bodylen - nkey;
if (nkey > MAX_SASL_MECH_LEN) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_EINVAL, NULL, vlen);
c->write_and_go = conn_swallow;
return;
}
char *key = binary_get_key(c);
assert(key);
item *it = item_alloc(key, nkey, 0, 0, vlen);
if (it == 0) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_ENOMEM, NULL, vlen);
c->write_and_go = conn_swallow;
return;
}
c->item = it;
c->ritem = ITEM_data(it);
c->rlbytes = vlen;
conn_set_state(c, conn_nread);
c->substate = bin_reading_sasl_auth_data;
}
static void process_bin_complete_sasl_auth(conn *c) {
assert(settings.sasl);
const char *out = NULL;
unsigned int outlen = 0;
assert(c->item);
init_sasl_conn(c);
int nkey = c->binary_header.request.keylen;
int vlen = c->binary_header.request.bodylen - nkey;
char mech[nkey+1];
memcpy(mech, ITEM_key((item*)c->item), nkey);
mech[nkey] = 0x00;
if (settings.verbose)
fprintf(stderr, "mech: ``%s'' with %d bytes of data\n", mech, vlen);
const char *challenge = vlen == 0 ? NULL : ITEM_data((item*) c->item);
int result=-1;
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_SASL_AUTH:
result = sasl_server_start(c->sasl_conn, mech,
challenge, vlen,
&out, &outlen);
break;
case PROTOCOL_BINARY_CMD_SASL_STEP:
result = sasl_server_step(c->sasl_conn,
challenge, vlen,
&out, &outlen);
break;
default:
assert(false); /* CMD should be one of the above */
/* This code is pretty much impossible, but makes the compiler
happier */
if (settings.verbose) {
fprintf(stderr, "Unhandled command %d with challenge %s\n",
c->cmd, challenge);
}
break;
}
item_unlink(c->item);
if (settings.verbose) {
fprintf(stderr, "sasl result code: %d\n", result);
}
switch(result) {
case SASL_OK:
c->authenticated = true;
write_bin_response(c, "Authenticated", 0, 0, strlen("Authenticated"));
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.auth_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
break;
case SASL_CONTINUE:
add_bin_header(c, PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE, 0, 0, outlen);
if(outlen > 0) {
add_iov(c, out, outlen);
}
conn_set_state(c, conn_mwrite);
c->write_and_go = conn_new_cmd;
break;
default:
if (settings.verbose)
fprintf(stderr, "Unknown sasl response: %d\n", result);
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.auth_cmds++;
c->thread->stats.auth_errors++;
pthread_mutex_unlock(&c->thread->stats.mutex);
}
}
static bool authenticated(conn *c) {
assert(settings.sasl);
bool rv = false;
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_SASL_AUTH: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_SASL_STEP: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_VERSION: /* FALLTHROUGH */
rv = true;
break;
default:
rv = c->authenticated;
}
if (settings.verbose > 1) {
fprintf(stderr, "authenticated() in cmd 0x%02x is %s\n",
c->cmd, rv ? "true" : "false");
}
return rv;
}
static void dispatch_bin_command(conn *c) {
int protocol_error = 0;
int extlen = c->binary_header.request.extlen;
int keylen = c->binary_header.request.keylen;
uint32_t bodylen = c->binary_header.request.bodylen;
if (settings.sasl && !authenticated(c)) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
c->write_and_go = conn_closing;
return;
}
MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
c->noreply = true;
/* binprot supports 16bit keys, but internals are still 8bit */
if (keylen > KEY_MAX_LENGTH) {
handle_binary_protocol_error(c);
return;
}
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_SETQ:
c->cmd = PROTOCOL_BINARY_CMD_SET;
break;
case PROTOCOL_BINARY_CMD_ADDQ:
c->cmd = PROTOCOL_BINARY_CMD_ADD;
break;
case PROTOCOL_BINARY_CMD_REPLACEQ:
c->cmd = PROTOCOL_BINARY_CMD_REPLACE;
break;
case PROTOCOL_BINARY_CMD_DELETEQ:
c->cmd = PROTOCOL_BINARY_CMD_DELETE;
break;
case PROTOCOL_BINARY_CMD_INCREMENTQ:
c->cmd = PROTOCOL_BINARY_CMD_INCREMENT;
break;
case PROTOCOL_BINARY_CMD_DECREMENTQ:
c->cmd = PROTOCOL_BINARY_CMD_DECREMENT;
break;
case PROTOCOL_BINARY_CMD_QUITQ:
c->cmd = PROTOCOL_BINARY_CMD_QUIT;
break;
case PROTOCOL_BINARY_CMD_FLUSHQ:
c->cmd = PROTOCOL_BINARY_CMD_FLUSH;
break;
case PROTOCOL_BINARY_CMD_APPENDQ:
c->cmd = PROTOCOL_BINARY_CMD_APPEND;
break;
case PROTOCOL_BINARY_CMD_PREPENDQ:
c->cmd = PROTOCOL_BINARY_CMD_PREPEND;
break;
case PROTOCOL_BINARY_CMD_GETQ:
c->cmd = PROTOCOL_BINARY_CMD_GET;
break;
case PROTOCOL_BINARY_CMD_GETKQ:
c->cmd = PROTOCOL_BINARY_CMD_GETK;
break;
case PROTOCOL_BINARY_CMD_GATQ:
c->cmd = PROTOCOL_BINARY_CMD_GAT;
break;
case PROTOCOL_BINARY_CMD_GATKQ:
c->cmd = PROTOCOL_BINARY_CMD_GATK;
break;
default:
c->noreply = false;
}
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_VERSION:
if (extlen == 0 && keylen == 0 && bodylen == 0) {
write_bin_response(c, VERSION, 0, 0, strlen(VERSION));
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_FLUSH:
if (keylen == 0 && bodylen == extlen && (extlen == 0 || extlen == 4)) {
bin_read_key(c, bin_read_flush_exptime, extlen);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_NOOP:
if (extlen == 0 && keylen == 0 && bodylen == 0) {
write_bin_response(c, NULL, 0, 0, 0);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_SET: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_ADD: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_REPLACE:
if (extlen == 8 && keylen != 0 && bodylen >= (keylen + 8)) {
bin_read_key(c, bin_reading_set_header, 8);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_GETQ: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_GET: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_GETKQ: /* FALLTHROUGH */
case PROTOCOL_BINARY_CMD_GETK:
if (extlen == 0 && bodylen == keylen && keylen > 0) {
bin_read_key(c, bin_reading_get_key, 0);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_DELETE:
if (keylen > 0 && extlen == 0 && bodylen == keylen) {
bin_read_key(c, bin_reading_del_header, extlen);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_INCREMENT:
case PROTOCOL_BINARY_CMD_DECREMENT:
if (keylen > 0 && extlen == 20 && bodylen == (keylen + extlen)) {
bin_read_key(c, bin_reading_incr_header, 20);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_APPEND:
case PROTOCOL_BINARY_CMD_PREPEND:
if (keylen > 0 && extlen == 0) {
bin_read_key(c, bin_reading_set_header, 0);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_STAT:
if (extlen == 0) {
bin_read_key(c, bin_reading_stat, 0);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_QUIT:
if (keylen == 0 && extlen == 0 && bodylen == 0) {
write_bin_response(c, NULL, 0, 0, 0);
c->write_and_go = conn_closing;
if (c->noreply) {
conn_set_state(c, conn_closing);
}
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS:
if (extlen == 0 && keylen == 0 && bodylen == 0) {
bin_list_sasl_mechs(c);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_SASL_AUTH:
case PROTOCOL_BINARY_CMD_SASL_STEP:
if (extlen == 0 && keylen != 0) {
bin_read_key(c, bin_reading_sasl_auth, 0);
} else {
protocol_error = 1;
}
break;
case PROTOCOL_BINARY_CMD_TOUCH:
case PROTOCOL_BINARY_CMD_GAT:
case PROTOCOL_BINARY_CMD_GATQ:
case PROTOCOL_BINARY_CMD_GATK:
case PROTOCOL_BINARY_CMD_GATKQ:
if (extlen == 4 && keylen != 0) {
bin_read_key(c, bin_reading_touch_key, 4);
} else {
protocol_error = 1;
}
break;
default:
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND, NULL,
bodylen);
}
if (protocol_error)
handle_binary_protocol_error(c);
}
static void process_bin_update(conn *c) {
char *key;
int nkey;
int vlen;
item *it;
protocol_binary_request_set* req = binary_get_request(c);
assert(c != NULL);
key = binary_get_key(c);
nkey = c->binary_header.request.keylen;
/* fix byteorder in the request */
req->message.body.flags = ntohl(req->message.body.flags);
req->message.body.expiration = ntohl(req->message.body.expiration);
vlen = c->binary_header.request.bodylen - (nkey + c->binary_header.request.extlen);
if (settings.verbose > 1) {
int ii;
if (c->cmd == PROTOCOL_BINARY_CMD_ADD) {
fprintf(stderr, "<%d ADD ", c->sfd);
} else if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
fprintf(stderr, "<%d SET ", c->sfd);
} else {
fprintf(stderr, "<%d REPLACE ", c->sfd);
}
for (ii = 0; ii < nkey; ++ii) {
fprintf(stderr, "%c", key[ii]);
}
fprintf(stderr, " Value len is %d", vlen);
fprintf(stderr, "\n");
}
if (settings.detail_enabled) {
stats_prefix_record_set(key, nkey);
}
it = item_alloc(key, nkey, req->message.body.flags,
realtime(req->message.body.expiration), vlen+2);
if (it == 0) {
if (! item_size_ok(nkey, req->message.body.flags, vlen + 2)) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
} else {
out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
}
/* Avoid stale data persisting in cache because we failed alloc.
* Unacceptable for SET. Anywhere else too? */
if (c->cmd == PROTOCOL_BINARY_CMD_SET) {
it = item_get(key, nkey);
if (it) {
item_unlink(it);
item_remove(it);
}
}
/* swallow the data line */
c->write_and_go = conn_swallow;
return;
}
ITEM_set_cas(it, c->binary_header.request.cas);
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_ADD:
c->cmd = NREAD_ADD;
break;
case PROTOCOL_BINARY_CMD_SET:
c->cmd = NREAD_SET;
break;
case PROTOCOL_BINARY_CMD_REPLACE:
c->cmd = NREAD_REPLACE;
break;
default:
assert(0);
}
if (ITEM_get_cas(it) != 0) {
c->cmd = NREAD_CAS;
}
c->item = it;
c->ritem = ITEM_data(it);
c->rlbytes = vlen;
conn_set_state(c, conn_nread);
c->substate = bin_read_set_value;
}
static void process_bin_append_prepend(conn *c) {
char *key;
int nkey;
int vlen;
item *it;
assert(c != NULL);
key = binary_get_key(c);
nkey = c->binary_header.request.keylen;
vlen = c->binary_header.request.bodylen - nkey;
if (settings.verbose > 1) {
fprintf(stderr, "Value len is %d\n", vlen);
}
if (settings.detail_enabled) {
stats_prefix_record_set(key, nkey);
}
it = item_alloc(key, nkey, 0, 0, vlen+2);
if (it == 0) {
if (! item_size_ok(nkey, 0, vlen + 2)) {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_E2BIG, NULL, vlen);
} else {
out_of_memory(c, "SERVER_ERROR Out of memory allocating item");
}
/* swallow the data line */
c->write_and_go = conn_swallow;
return;
}
ITEM_set_cas(it, c->binary_header.request.cas);
switch (c->cmd) {
case PROTOCOL_BINARY_CMD_APPEND:
c->cmd = NREAD_APPEND;
break;
case PROTOCOL_BINARY_CMD_PREPEND:
c->cmd = NREAD_PREPEND;
break;
default:
assert(0);
}
c->item = it;
c->ritem = ITEM_data(it);
c->rlbytes = vlen;
conn_set_state(c, conn_nread);
c->substate = bin_read_set_value;
}
static void process_bin_flush(conn *c) {
time_t exptime = 0;
protocol_binary_request_flush* req = binary_get_request(c);
rel_time_t new_oldest = 0;
if (!settings.flush_enabled) {
// flush_all is not allowed but we log it on stats
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_AUTH_ERROR, NULL, 0);
return;
}
if (c->binary_header.request.extlen == sizeof(req->message.body)) {
exptime = ntohl(req->message.body.expiration);
}
if (exptime > 0) {
new_oldest = realtime(exptime);
} else {
new_oldest = current_time;
}
if (settings.use_cas) {
settings.oldest_live = new_oldest - 1;
if (settings.oldest_live <= current_time)
settings.oldest_cas = get_cas_id();
} else {
settings.oldest_live = new_oldest;
}
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.flush_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
write_bin_response(c, NULL, 0, 0, 0);
}
static void process_bin_delete(conn *c) {
item *it;
protocol_binary_request_delete* req = binary_get_request(c);
char* key = binary_get_key(c);
size_t nkey = c->binary_header.request.keylen;
assert(c != NULL);
if (settings.verbose > 1) {
int ii;
fprintf(stderr, "Deleting ");
for (ii = 0; ii < nkey; ++ii) {
fprintf(stderr, "%c", key[ii]);
}
fprintf(stderr, "\n");
}
if (settings.detail_enabled) {
stats_prefix_record_delete(key, nkey);
}
it = item_get(key, nkey);
if (it) {
uint64_t cas = ntohll(req->message.header.request.cas);
if (cas == 0 || cas == ITEM_get_cas(it)) {
MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
item_unlink(it);
write_bin_response(c, NULL, 0, 0, 0);
} else {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, NULL, 0);
}
item_remove(it); /* release our reference */
} else {
write_bin_error(c, PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, NULL, 0);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.delete_misses++;
pthread_mutex_unlock(&c->thread->stats.mutex);
}
}
static void complete_nread_binary(conn *c) {
assert(c != NULL);
assert(c->cmd >= 0);
switch(c->substate) {
case bin_reading_set_header:
if (c->cmd == PROTOCOL_BINARY_CMD_APPEND ||
c->cmd == PROTOCOL_BINARY_CMD_PREPEND) {
process_bin_append_prepend(c);
} else {
process_bin_update(c);
}
break;
case bin_read_set_value:
complete_update_bin(c);
break;
case bin_reading_get_key:
case bin_reading_touch_key:
process_bin_get_or_touch(c);
break;
case bin_reading_stat:
process_bin_stat(c);
break;
case bin_reading_del_header:
process_bin_delete(c);
break;
case bin_reading_incr_header:
complete_incr_bin(c);
break;
case bin_read_flush_exptime:
process_bin_flush(c);
break;
case bin_reading_sasl_auth:
process_bin_sasl_auth(c);
break;
case bin_reading_sasl_auth_data:
process_bin_complete_sasl_auth(c);
break;
default:
fprintf(stderr, "Not handling substate %d\n", c->substate);
assert(0);
}
}
static void reset_cmd_handler(conn *c) {
c->cmd = -1;
c->substate = bin_no_state;
if(c->item != NULL) {
item_remove(c->item);
c->item = NULL;
}
conn_shrink(c);
if (c->rbytes > 0) {
conn_set_state(c, conn_parse_cmd);
} else {
conn_set_state(c, conn_waiting);
}
}
static void complete_nread(conn *c) {
assert(c != NULL);
assert(c->protocol == ascii_prot
|| c->protocol == binary_prot);
if (c->protocol == ascii_prot) {
complete_nread_ascii(c);
} else if (c->protocol == binary_prot) {
complete_nread_binary(c);
}
}
/*
* Stores an item in the cache according to the semantics of one of the set
* commands. In threaded mode, this is protected by the cache lock.
*
* Returns the state of storage.
*/
enum store_item_type do_store_item(item *it, int comm, conn *c, const uint32_t hv) {
char *key = ITEM_key(it);
item *old_it = do_item_get(key, it->nkey, hv);
enum store_item_type stored = NOT_STORED;
item *new_it = NULL;
int flags;
if (old_it != NULL && comm == NREAD_ADD) {
/* add only adds a nonexistent item, but promote to head of LRU */
do_item_update(old_it);
} else if (!old_it && (comm == NREAD_REPLACE
|| comm == NREAD_APPEND || comm == NREAD_PREPEND))
{
/* replace only replaces an existing value; don't store */
} else if (comm == NREAD_CAS) {
/* validate cas operation */
if(old_it == NULL) {
// LRU expired
stored = NOT_FOUND;
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.cas_misses++;
pthread_mutex_unlock(&c->thread->stats.mutex);
}
else if (ITEM_get_cas(it) == ITEM_get_cas(old_it)) {
// cas validates
// it and old_it may belong to different classes.
// I'm updating the stats for the one that's getting pushed out
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(old_it)].cas_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
item_replace(old_it, it, hv);
stored = STORED;
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(old_it)].cas_badval++;
pthread_mutex_unlock(&c->thread->stats.mutex);
if(settings.verbose > 1) {
fprintf(stderr, "CAS: failure: expected %llu, got %llu\n",
(unsigned long long)ITEM_get_cas(old_it),
(unsigned long long)ITEM_get_cas(it));
}
stored = EXISTS;
}
} else {
/*
* Append - combine new and old record into single one. Here it's
* atomic and thread-safe.
*/
if (comm == NREAD_APPEND || comm == NREAD_PREPEND) {
/*
* Validate CAS
*/
if (ITEM_get_cas(it) != 0) {
// CAS much be equal
if (ITEM_get_cas(it) != ITEM_get_cas(old_it)) {
stored = EXISTS;
}
}
if (stored == NOT_STORED) {
/* we have it and old_it here - alloc memory to hold both */
/* flags was already lost - so recover them from ITEM_suffix(it) */
flags = (int) strtol(ITEM_suffix(old_it), (char **) NULL, 10);
new_it = do_item_alloc(key, it->nkey, flags, old_it->exptime, it->nbytes + old_it->nbytes - 2 /* CRLF */, hv);
if (new_it == NULL) {
/* SERVER_ERROR out of memory */
if (old_it != NULL)
do_item_remove(old_it);
return NOT_STORED;
}
/* copy data from it and old_it to new_it */
if (comm == NREAD_APPEND) {
memcpy(ITEM_data(new_it), ITEM_data(old_it), old_it->nbytes);
memcpy(ITEM_data(new_it) + old_it->nbytes - 2 /* CRLF */, ITEM_data(it), it->nbytes);
} else {
/* NREAD_PREPEND */
memcpy(ITEM_data(new_it), ITEM_data(it), it->nbytes);
memcpy(ITEM_data(new_it) + it->nbytes - 2 /* CRLF */, ITEM_data(old_it), old_it->nbytes);
}
it = new_it;
}
}
if (stored == NOT_STORED) {
if (old_it != NULL)
item_replace(old_it, it, hv);
else
do_item_link(it, hv);
c->cas = ITEM_get_cas(it);
stored = STORED;
}
}
if (old_it != NULL)
do_item_remove(old_it); /* release our reference */
if (new_it != NULL)
do_item_remove(new_it);
if (stored == STORED) {
c->cas = ITEM_get_cas(it);
}
return stored;
}
typedef struct token_s {
char *value;
size_t length;
} token_t;
#define COMMAND_TOKEN 0
#define SUBCOMMAND_TOKEN 1
#define KEY_TOKEN 1
#define MAX_TOKENS 8
/*
* Tokenize the command string by replacing whitespace with '\0' and update
* the token array tokens with pointer to start of each token and length.
* Returns total number of tokens. The last valid token is the terminal
* token (value points to the first unprocessed character of the string and
* length zero).
*
* Usage example:
*
* while(tokenize_command(command, ncommand, tokens, max_tokens) > 0) {
* for(int ix = 0; tokens[ix].length != 0; ix++) {
* ...
* }
* ncommand = tokens[ix].value - command;
* command = tokens[ix].value;
* }
*/
static size_t tokenize_command(char *command, token_t *tokens, const size_t max_tokens) {
char *s, *e;
size_t ntokens = 0;
size_t len = strlen(command);
unsigned int i = 0;
assert(command != NULL && tokens != NULL && max_tokens > 1);
s = e = command;
for (i = 0; i < len; i++) {
if (*e == ' ') {
if (s != e) {
tokens[ntokens].value = s;
tokens[ntokens].length = e - s;
ntokens++;
*e = '\0';
if (ntokens == max_tokens - 1) {
e++;
s = e; /* so we don't add an extra token */
break;
}
}
s = e + 1;
}
e++;
}
if (s != e) {
tokens[ntokens].value = s;
tokens[ntokens].length = e - s;
ntokens++;
}
/*
* If we scanned the whole string, the terminal value pointer is null,
* otherwise it is the first unprocessed character.
*/
tokens[ntokens].value = *e == '\0' ? NULL : e;
tokens[ntokens].length = 0;
ntokens++;
return ntokens;
}
/* set up a connection to write a buffer then free it, used for stats */
static void write_and_free(conn *c, char *buf, int bytes) {
if (buf) {
c->write_and_free = buf;
c->wcurr = buf;
c->wbytes = bytes;
conn_set_state(c, conn_write);
c->write_and_go = conn_new_cmd;
} else {
out_of_memory(c, "SERVER_ERROR out of memory writing stats");
}
}
static inline bool set_noreply_maybe(conn *c, token_t *tokens, size_t ntokens)
{
int noreply_index = ntokens - 2;
/*
NOTE: this function is not the first place where we are going to
send the reply. We could send it instead from process_command()
if the request line has wrong number of tokens. However parsing
malformed line for "noreply" option is not reliable anyway, so
it can't be helped.
*/
if (tokens[noreply_index].value
&& strcmp(tokens[noreply_index].value, "noreply") == 0) {
c->noreply = true;
}
return c->noreply;
}
void append_stat(const char *name, ADD_STAT add_stats, conn *c,
const char *fmt, ...) {
char val_str[STAT_VAL_LEN];
int vlen;
va_list ap;
assert(name);
assert(add_stats);
assert(c);
assert(fmt);
va_start(ap, fmt);
vlen = vsnprintf(val_str, sizeof(val_str) - 1, fmt, ap);
va_end(ap);
add_stats(name, strlen(name), val_str, vlen, c);
}
inline static void process_stats_detail(conn *c, const char *command) {
assert(c != NULL);
if (strcmp(command, "on") == 0) {
settings.detail_enabled = 1;
out_string(c, "OK");
}
else if (strcmp(command, "off") == 0) {
settings.detail_enabled = 0;
out_string(c, "OK");
}
else if (strcmp(command, "dump") == 0) {
int len;
char *stats = stats_prefix_dump(&len);
write_and_free(c, stats, len);
}
else {
out_string(c, "CLIENT_ERROR usage: stats detail on|off|dump");
}
}
/* return server specific stats only */
static void server_stats(ADD_STAT add_stats, conn *c) {
pid_t pid = getpid();
rel_time_t now = current_time;
struct thread_stats thread_stats;
threadlocal_stats_aggregate(&thread_stats);
struct slab_stats slab_stats;
slab_stats_aggregate(&thread_stats, &slab_stats);
#ifndef WIN32
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
#endif /* !WIN32 */
STATS_LOCK();
APPEND_STAT("pid", "%lu", (long)pid);
APPEND_STAT("uptime", "%u", now - ITEM_UPDATE_INTERVAL);
APPEND_STAT("time", "%ld", now + (long)process_started);
APPEND_STAT("version", "%s", VERSION);
APPEND_STAT("libevent", "%s", event_get_version());
APPEND_STAT("pointer_size", "%d", (int)(8 * sizeof(void *)));
#ifndef WIN32
append_stat("rusage_user", add_stats, c, "%ld.%06ld",
(long)usage.ru_utime.tv_sec,
(long)usage.ru_utime.tv_usec);
append_stat("rusage_system", add_stats, c, "%ld.%06ld",
(long)usage.ru_stime.tv_sec,
(long)usage.ru_stime.tv_usec);
#endif /* !WIN32 */
APPEND_STAT("curr_connections", "%u", stats.curr_conns - 1);
APPEND_STAT("total_connections", "%u", stats.total_conns);
if (settings.maxconns_fast) {
APPEND_STAT("rejected_connections", "%llu", (unsigned long long)stats.rejected_conns);
}
APPEND_STAT("connection_structures", "%u", stats.conn_structs);
APPEND_STAT("reserved_fds", "%u", stats.reserved_fds);
APPEND_STAT("cmd_get", "%llu", (unsigned long long)thread_stats.get_cmds);
APPEND_STAT("cmd_set", "%llu", (unsigned long long)slab_stats.set_cmds);
APPEND_STAT("cmd_flush", "%llu", (unsigned long long)thread_stats.flush_cmds);
APPEND_STAT("cmd_touch", "%llu", (unsigned long long)thread_stats.touch_cmds);
APPEND_STAT("get_hits", "%llu", (unsigned long long)slab_stats.get_hits);
APPEND_STAT("get_misses", "%llu", (unsigned long long)thread_stats.get_misses);
APPEND_STAT("delete_misses", "%llu", (unsigned long long)thread_stats.delete_misses);
APPEND_STAT("delete_hits", "%llu", (unsigned long long)slab_stats.delete_hits);
APPEND_STAT("incr_misses", "%llu", (unsigned long long)thread_stats.incr_misses);
APPEND_STAT("incr_hits", "%llu", (unsigned long long)slab_stats.incr_hits);
APPEND_STAT("decr_misses", "%llu", (unsigned long long)thread_stats.decr_misses);
APPEND_STAT("decr_hits", "%llu", (unsigned long long)slab_stats.decr_hits);
APPEND_STAT("cas_misses", "%llu", (unsigned long long)thread_stats.cas_misses);
APPEND_STAT("cas_hits", "%llu", (unsigned long long)slab_stats.cas_hits);
APPEND_STAT("cas_badval", "%llu", (unsigned long long)slab_stats.cas_badval);
APPEND_STAT("touch_hits", "%llu", (unsigned long long)slab_stats.touch_hits);
APPEND_STAT("touch_misses", "%llu", (unsigned long long)thread_stats.touch_misses);
APPEND_STAT("auth_cmds", "%llu", (unsigned long long)thread_stats.auth_cmds);
APPEND_STAT("auth_errors", "%llu", (unsigned long long)thread_stats.auth_errors);
APPEND_STAT("bytes_read", "%llu", (unsigned long long)thread_stats.bytes_read);
APPEND_STAT("bytes_written", "%llu", (unsigned long long)thread_stats.bytes_written);
APPEND_STAT("limit_maxbytes", "%llu", (unsigned long long)settings.maxbytes);
APPEND_STAT("accepting_conns", "%u", stats.accepting_conns);
APPEND_STAT("listen_disabled_num", "%llu", (unsigned long long)stats.listen_disabled_num);
APPEND_STAT("time_in_listen_disabled_us", "%llu", stats.time_in_listen_disabled_us);
APPEND_STAT("threads", "%d", settings.num_threads);
APPEND_STAT("conn_yields", "%llu", (unsigned long long)thread_stats.conn_yields);
APPEND_STAT("hash_power_level", "%u", stats.hash_power_level);
APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats.hash_bytes);
APPEND_STAT("hash_is_expanding", "%u", stats.hash_is_expanding);
if (settings.slab_reassign) {
APPEND_STAT("slab_reassign_rescues", "%llu", stats.slab_reassign_rescues);
APPEND_STAT("slab_reassign_evictions_nomem", "%llu", stats.slab_reassign_evictions_nomem);
APPEND_STAT("slab_reassign_inline_reclaim", "%llu", stats.slab_reassign_inline_reclaim);
APPEND_STAT("slab_reassign_busy_items", "%llu", stats.slab_reassign_busy_items);
APPEND_STAT("slab_reassign_running", "%u", stats.slab_reassign_running);
APPEND_STAT("slabs_moved", "%llu", stats.slabs_moved);
}
if (settings.lru_crawler) {
APPEND_STAT("lru_crawler_running", "%u", stats.lru_crawler_running);
APPEND_STAT("lru_crawler_starts", "%u", stats.lru_crawler_starts);
}
if (settings.lru_maintainer_thread) {
APPEND_STAT("lru_maintainer_juggles", "%llu", (unsigned long long)stats.lru_maintainer_juggles);
}
APPEND_STAT("malloc_fails", "%llu",
(unsigned long long)stats.malloc_fails);
STATS_UNLOCK();
}
static void process_stat_settings(ADD_STAT add_stats, void *c) {
assert(add_stats);
APPEND_STAT("maxbytes", "%llu", (unsigned long long)settings.maxbytes);
APPEND_STAT("maxconns", "%d", settings.maxconns);
APPEND_STAT("tcpport", "%d", settings.port);
APPEND_STAT("udpport", "%d", settings.udpport);
APPEND_STAT("inter", "%s", settings.inter ? settings.inter : "NULL");
APPEND_STAT("verbosity", "%d", settings.verbose);
APPEND_STAT("oldest", "%lu", (unsigned long)settings.oldest_live);
APPEND_STAT("evictions", "%s", settings.evict_to_free ? "on" : "off");
APPEND_STAT("domain_socket", "%s",
settings.socketpath ? settings.socketpath : "NULL");
APPEND_STAT("umask", "%o", settings.access);
APPEND_STAT("growth_factor", "%.2f", settings.factor);
APPEND_STAT("chunk_size", "%d", settings.chunk_size);
APPEND_STAT("num_threads", "%d", settings.num_threads);
APPEND_STAT("num_threads_per_udp", "%d", settings.num_threads_per_udp);
APPEND_STAT("stat_key_prefix", "%c", settings.prefix_delimiter);
APPEND_STAT("detail_enabled", "%s",
settings.detail_enabled ? "yes" : "no");
APPEND_STAT("reqs_per_event", "%d", settings.reqs_per_event);
APPEND_STAT("cas_enabled", "%s", settings.use_cas ? "yes" : "no");
APPEND_STAT("tcp_backlog", "%d", settings.backlog);
APPEND_STAT("binding_protocol", "%s",
prot_text(settings.binding_protocol));
APPEND_STAT("auth_enabled_sasl", "%s", settings.sasl ? "yes" : "no");
APPEND_STAT("item_size_max", "%d", settings.item_size_max);
APPEND_STAT("maxconns_fast", "%s", settings.maxconns_fast ? "yes" : "no");
APPEND_STAT("hashpower_init", "%d", settings.hashpower_init);
APPEND_STAT("slab_reassign", "%s", settings.slab_reassign ? "yes" : "no");
APPEND_STAT("slab_automove", "%d", settings.slab_automove);
APPEND_STAT("lru_crawler", "%s", settings.lru_crawler ? "yes" : "no");
APPEND_STAT("lru_crawler_sleep", "%d", settings.lru_crawler_sleep);
APPEND_STAT("lru_crawler_tocrawl", "%lu", (unsigned long)settings.lru_crawler_tocrawl);
APPEND_STAT("tail_repair_time", "%d", settings.tail_repair_time);
APPEND_STAT("flush_enabled", "%s", settings.flush_enabled ? "yes" : "no");
APPEND_STAT("hash_algorithm", "%s", settings.hash_algorithm);
APPEND_STAT("lru_maintainer_thread", "%s", settings.lru_maintainer_thread ? "yes" : "no");
APPEND_STAT("hot_lru_pct", "%d", settings.hot_lru_pct);
APPEND_STAT("warm_lru_pct", "%d", settings.warm_lru_pct);
APPEND_STAT("expirezero_does_not_evict", "%s", settings.expirezero_does_not_evict ? "yes" : "no");
}
static void conn_to_str(const conn *c, char *buf) {
char addr_text[MAXPATHLEN];
if (!c) {
strcpy(buf, "");
} else if (c->state == conn_closed) {
strcpy(buf, "");
} else {
const char *protoname = "?";
struct sockaddr_in6 local_addr;
struct sockaddr *addr = (void *)&c->request_addr;
int af;
unsigned short port = 0;
/* For listen ports and idle UDP ports, show listen address */
if (c->state == conn_listening ||
(IS_UDP(c->transport) &&
c->state == conn_read)) {
socklen_t local_addr_len = sizeof(local_addr);
if (getsockname(c->sfd,
(struct sockaddr *)&local_addr,
&local_addr_len) == 0) {
addr = (struct sockaddr *)&local_addr;
}
}
af = addr->sa_family;
addr_text[0] = '\0';
switch (af) {
case AF_INET:
(void) inet_ntop(af,
&((struct sockaddr_in *)addr)->sin_addr,
addr_text,
sizeof(addr_text) - 1);
port = ntohs(((struct sockaddr_in *)addr)->sin_port);
protoname = IS_UDP(c->transport) ? "udp" : "tcp";
break;
case AF_INET6:
addr_text[0] = '[';
addr_text[1] = '\0';
if (inet_ntop(af,
&((struct sockaddr_in6 *)addr)->sin6_addr,
addr_text + 1,
sizeof(addr_text) - 2)) {
strcat(addr_text, "]");
}
port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
protoname = IS_UDP(c->transport) ? "udp6" : "tcp6";
break;
case AF_UNIX:
strncpy(addr_text,
((struct sockaddr_un *)addr)->sun_path,
sizeof(addr_text) - 1);
addr_text[sizeof(addr_text)-1] = '\0';
protoname = "unix";
break;
}
if (strlen(addr_text) < 2) {
/* Most likely this is a connected UNIX-domain client which
* has no peer socket address, but there's no portable way
* to tell for sure.
*/
sprintf(addr_text, "", af);
}
if (port) {
sprintf(buf, "%s:%s:%u", protoname, addr_text, port);
} else {
sprintf(buf, "%s:%s", protoname, addr_text);
}
}
}
static void process_stats_conns(ADD_STAT add_stats, void *c) {
int i;
char key_str[STAT_KEY_LEN];
char val_str[STAT_VAL_LEN];
char conn_name[MAXPATHLEN + sizeof("unix:")];
int klen = 0, vlen = 0;
assert(add_stats);
for (i = 0; i < max_fds; i++) {
if (conns[i]) {
/* This is safe to do unlocked because conns are never freed; the
* worst that'll happen will be a minor inconsistency in the
* output -- not worth the complexity of the locking that'd be
* required to prevent it.
*/
if (conns[i]->state != conn_closed) {
conn_to_str(conns[i], conn_name);
APPEND_NUM_STAT(i, "addr", "%s", conn_name);
APPEND_NUM_STAT(i, "state", "%s",
state_text(conns[i]->state));
APPEND_NUM_STAT(i, "secs_since_last_cmd", "%d",
current_time - conns[i]->last_cmd_time);
}
}
}
}
static void process_stat(conn *c, token_t *tokens, const size_t ntokens) {
const char *subcommand = tokens[SUBCOMMAND_TOKEN].value;
assert(c != NULL);
if (ntokens < 2) {
out_string(c, "CLIENT_ERROR bad command line");
return;
}
if (ntokens == 2) {
server_stats(&append_stats, c);
(void)get_stats(NULL, 0, &append_stats, c);
} else if (strcmp(subcommand, "reset") == 0) {
stats_reset();
out_string(c, "RESET");
return ;
} else if (strcmp(subcommand, "detail") == 0) {
/* NOTE: how to tackle detail with binary? */
if (ntokens < 4)
process_stats_detail(c, ""); /* outputs the error message */
else
process_stats_detail(c, tokens[2].value);
/* Output already generated */
return ;
} else if (strcmp(subcommand, "settings") == 0) {
process_stat_settings(&append_stats, c);
} else if (strcmp(subcommand, "cachedump") == 0) {
char *buf;
unsigned int bytes, id, limit = 0;
if (ntokens < 5) {
out_string(c, "CLIENT_ERROR bad command line");
return;
}
if (!safe_strtoul(tokens[2].value, &id) ||
!safe_strtoul(tokens[3].value, &limit)) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
if (id >= MAX_NUMBER_OF_SLAB_CLASSES-1) {
out_string(c, "CLIENT_ERROR Illegal slab id");
return;
}
buf = item_cachedump(id, limit, &bytes);
write_and_free(c, buf, bytes);
return ;
} else if (strcmp(subcommand, "conns") == 0) {
process_stats_conns(&append_stats, c);
} else {
/* getting here means that the subcommand is either engine specific or
is invalid. query the engine and see. */
if (get_stats(subcommand, strlen(subcommand), &append_stats, c)) {
if (c->stats.buffer == NULL) {
out_of_memory(c, "SERVER_ERROR out of memory writing stats");
} else {
write_and_free(c, c->stats.buffer, c->stats.offset);
c->stats.buffer = NULL;
}
} else {
out_string(c, "ERROR");
}
return ;
}
/* append terminator and start the transfer */
append_stats(NULL, 0, NULL, 0, c);
if (c->stats.buffer == NULL) {
out_of_memory(c, "SERVER_ERROR out of memory writing stats");
} else {
write_and_free(c, c->stats.buffer, c->stats.offset);
c->stats.buffer = NULL;
}
}
/* ntokens is overwritten here... shrug.. */
static inline void process_get_command(conn *c, token_t *tokens, size_t ntokens, bool return_cas) {
char *key;
size_t nkey;
int i = 0;
item *it;
token_t *key_token = &tokens[KEY_TOKEN];
char *suffix;
assert(c != NULL);
do {
while(key_token->length != 0) {
key = key_token->value;
nkey = key_token->length;
if(nkey > KEY_MAX_LENGTH) {
out_string(c, "CLIENT_ERROR bad command line format");
while (i-- > 0) {
item_remove(*(c->ilist + i));
}
return;
}
it = item_get(key, nkey);
if (settings.detail_enabled) {
stats_prefix_record_get(key, nkey, NULL != it);
}
if (it) {
if (i >= c->isize) {
item **new_list = realloc(c->ilist, sizeof(item *) * c->isize * 2);
if (new_list) {
c->isize *= 2;
c->ilist = new_list;
} else {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
item_remove(it);
break;
}
}
/*
* Construct the response. Each hit adds three elements to the
* outgoing data list:
* "VALUE "
* key
* " " + flags + " " + data length + "\r\n" + data (with \r\n)
*/
if (return_cas)
{
MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
it->nbytes, ITEM_get_cas(it));
/* Goofy mid-flight realloc. */
if (i >= c->suffixsize) {
char **new_suffix_list = realloc(c->suffixlist,
sizeof(char *) * c->suffixsize * 2);
if (new_suffix_list) {
c->suffixsize *= 2;
c->suffixlist = new_suffix_list;
} else {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
item_remove(it);
break;
}
}
suffix = cache_alloc(c->thread->suffix_cache);
if (suffix == NULL) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
out_of_memory(c, "SERVER_ERROR out of memory making CAS suffix");
item_remove(it);
while (i-- > 0) {
item_remove(*(c->ilist + i));
}
return;
}
*(c->suffixlist + i) = suffix;
int suffix_len = snprintf(suffix, SUFFIX_SIZE,
" %llu\r\n",
(unsigned long long)ITEM_get_cas(it));
if (add_iov(c, "VALUE ", 6) != 0 ||
add_iov(c, ITEM_key(it), it->nkey) != 0 ||
add_iov(c, ITEM_suffix(it), it->nsuffix - 2) != 0 ||
add_iov(c, suffix, suffix_len) != 0 ||
add_iov(c, ITEM_data(it), it->nbytes) != 0)
{
item_remove(it);
break;
}
}
else
{
MEMCACHED_COMMAND_GET(c->sfd, ITEM_key(it), it->nkey,
it->nbytes, ITEM_get_cas(it));
if (add_iov(c, "VALUE ", 6) != 0 ||
add_iov(c, ITEM_key(it), it->nkey) != 0 ||
add_iov(c, ITEM_suffix(it), it->nsuffix + it->nbytes) != 0)
{
item_remove(it);
break;
}
}
if (settings.verbose > 1) {
int ii;
fprintf(stderr, ">%d sending key ", c->sfd);
for (ii = 0; ii < it->nkey; ++ii) {
fprintf(stderr, "%c", key[ii]);
}
fprintf(stderr, "\n");
}
/* item_get() has incremented it->refcount for us */
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].get_hits++;
c->thread->stats.get_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
item_update(it);
*(c->ilist + i) = it;
i++;
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.get_misses++;
c->thread->stats.get_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
MEMCACHED_COMMAND_GET(c->sfd, key, nkey, -1, 0);
}
key_token++;
}
/*
* If the command string hasn't been fully processed, get the next set
* of tokens.
*/
if(key_token->value != NULL) {
ntokens = tokenize_command(key_token->value, tokens, MAX_TOKENS);
key_token = tokens;
}
} while(key_token->value != NULL);
c->icurr = c->ilist;
c->ileft = i;
if (return_cas) {
c->suffixcurr = c->suffixlist;
c->suffixleft = i;
}
if (settings.verbose > 1)
fprintf(stderr, ">%d END\n", c->sfd);
/*
If the loop was terminated because of out-of-memory, it is not
reliable to add END\r\n to the buffer, because it might not end
in \r\n. So we send SERVER_ERROR instead.
*/
if (key_token->value != NULL || add_iov(c, "END\r\n", 5) != 0
|| (IS_UDP(c->transport) && build_udp_headers(c) != 0)) {
out_of_memory(c, "SERVER_ERROR out of memory writing get response");
}
else {
conn_set_state(c, conn_mwrite);
c->msgcurr = 0;
}
}
static void process_update_command(conn *c, token_t *tokens, const size_t ntokens, int comm, bool handle_cas) {
char *key;
size_t nkey;
unsigned int flags;
int32_t exptime_int = 0;
time_t exptime;
int vlen;
uint64_t req_cas_id=0;
item *it;
assert(c != NULL);
set_noreply_maybe(c, tokens, ntokens);
if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
key = tokens[KEY_TOKEN].value;
nkey = tokens[KEY_TOKEN].length;
if (! (safe_strtoul(tokens[2].value, (uint32_t *)&flags)
&& safe_strtol(tokens[3].value, &exptime_int)
&& safe_strtol(tokens[4].value, (int32_t *)&vlen))) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
/* Ubuntu 8.04 breaks when I pass exptime to safe_strtol */
exptime = exptime_int;
/* Negative exptimes can underflow and end up immortal. realtime() will
immediately expire values that are greater than REALTIME_MAXDELTA, but less
than process_started, so lets aim for that. */
if (exptime < 0)
exptime = REALTIME_MAXDELTA + 1;
// does cas value exist?
if (handle_cas) {
if (!safe_strtoull(tokens[5].value, &req_cas_id)) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
}
vlen += 2;
if (vlen < 0 || vlen - 2 < 0) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
if (settings.detail_enabled) {
stats_prefix_record_set(key, nkey);
}
it = item_alloc(key, nkey, flags, realtime(exptime), vlen);
if (it == 0) {
if (! item_size_ok(nkey, flags, vlen))
out_string(c, "SERVER_ERROR object too large for cache");
else
out_of_memory(c, "SERVER_ERROR out of memory storing object");
/* swallow the data line */
c->write_and_go = conn_swallow;
c->sbytes = vlen;
/* Avoid stale data persisting in cache because we failed alloc.
* Unacceptable for SET. Anywhere else too? */
if (comm == NREAD_SET) {
it = item_get(key, nkey);
if (it) {
item_unlink(it);
item_remove(it);
}
}
return;
}
ITEM_set_cas(it, req_cas_id);
c->item = it;
c->ritem = ITEM_data(it);
c->rlbytes = it->nbytes;
c->cmd = comm;
conn_set_state(c, conn_nread);
}
static void process_touch_command(conn *c, token_t *tokens, const size_t ntokens) {
char *key;
size_t nkey;
int32_t exptime_int = 0;
item *it;
assert(c != NULL);
set_noreply_maybe(c, tokens, ntokens);
if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
key = tokens[KEY_TOKEN].value;
nkey = tokens[KEY_TOKEN].length;
if (!safe_strtol(tokens[2].value, &exptime_int)) {
out_string(c, "CLIENT_ERROR invalid exptime argument");
return;
}
it = item_touch(key, nkey, realtime(exptime_int));
if (it) {
item_update(it);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.touch_cmds++;
c->thread->stats.slab_stats[ITEM_clsid(it)].touch_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
out_string(c, "TOUCHED");
item_remove(it);
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.touch_cmds++;
c->thread->stats.touch_misses++;
pthread_mutex_unlock(&c->thread->stats.mutex);
out_string(c, "NOT_FOUND");
}
}
static void process_arithmetic_command(conn *c, token_t *tokens, const size_t ntokens, const bool incr) {
char temp[INCR_MAX_STORAGE_LEN];
uint64_t delta;
char *key;
size_t nkey;
assert(c != NULL);
set_noreply_maybe(c, tokens, ntokens);
if (tokens[KEY_TOKEN].length > KEY_MAX_LENGTH) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
key = tokens[KEY_TOKEN].value;
nkey = tokens[KEY_TOKEN].length;
if (!safe_strtoull(tokens[2].value, &delta)) {
out_string(c, "CLIENT_ERROR invalid numeric delta argument");
return;
}
switch(add_delta(c, key, nkey, incr, delta, temp, NULL)) {
case OK:
out_string(c, temp);
break;
case NON_NUMERIC:
out_string(c, "CLIENT_ERROR cannot increment or decrement non-numeric value");
break;
case EOM:
out_of_memory(c, "SERVER_ERROR out of memory");
break;
case DELTA_ITEM_NOT_FOUND:
pthread_mutex_lock(&c->thread->stats.mutex);
if (incr) {
c->thread->stats.incr_misses++;
} else {
c->thread->stats.decr_misses++;
}
pthread_mutex_unlock(&c->thread->stats.mutex);
out_string(c, "NOT_FOUND");
break;
case DELTA_ITEM_CAS_MISMATCH:
break; /* Should never get here */
}
}
/*
* adds a delta value to a numeric item.
*
* c connection requesting the operation
* it item to adjust
* incr true to increment value, false to decrement
* delta amount to adjust value by
* buf buffer for response string
*
* returns a response string to send back to the client.
*/
enum delta_result_type do_add_delta(conn *c, const char *key, const size_t nkey,
const bool incr, const int64_t delta,
char *buf, uint64_t *cas,
const uint32_t hv) {
char *ptr;
uint64_t value;
int res;
item *it;
it = do_item_get(key, nkey, hv);
if (!it) {
return DELTA_ITEM_NOT_FOUND;
}
/* Can't delta zero byte values. 2-byte are the "\r\n" */
if (it->nbytes <= 2) {
return NON_NUMERIC;
}
if (cas != NULL && *cas != 0 && ITEM_get_cas(it) != *cas) {
do_item_remove(it);
return DELTA_ITEM_CAS_MISMATCH;
}
ptr = ITEM_data(it);
if (!safe_strtoull(ptr, &value)) {
do_item_remove(it);
return NON_NUMERIC;
}
if (incr) {
value += delta;
MEMCACHED_COMMAND_INCR(c->sfd, ITEM_key(it), it->nkey, value);
} else {
if(delta > value) {
value = 0;
} else {
value -= delta;
}
MEMCACHED_COMMAND_DECR(c->sfd, ITEM_key(it), it->nkey, value);
}
pthread_mutex_lock(&c->thread->stats.mutex);
if (incr) {
c->thread->stats.slab_stats[ITEM_clsid(it)].incr_hits++;
} else {
c->thread->stats.slab_stats[ITEM_clsid(it)].decr_hits++;
}
pthread_mutex_unlock(&c->thread->stats.mutex);
snprintf(buf, INCR_MAX_STORAGE_LEN, "%llu", (unsigned long long)value);
res = strlen(buf);
/* refcount == 2 means we are the only ones holding the item, and it is
* linked. We hold the item's lock in this function, so refcount cannot
* increase. */
if (res + 2 <= it->nbytes && it->refcount == 2) { /* replace in-place */
/* When changing the value without replacing the item, we
need to update the CAS on the existing item. */
ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
memcpy(ITEM_data(it), buf, res);
memset(ITEM_data(it) + res, ' ', it->nbytes - res - 2);
do_item_update(it);
} else if (it->refcount > 1) {
item *new_it;
new_it = do_item_alloc(ITEM_key(it), it->nkey, atoi(ITEM_suffix(it) + 1), it->exptime, res + 2, hv);
if (new_it == 0) {
do_item_remove(it);
return EOM;
}
memcpy(ITEM_data(new_it), buf, res);
memcpy(ITEM_data(new_it) + res, "\r\n", 2);
item_replace(it, new_it, hv);
// Overwrite the older item's CAS with our new CAS since we're
// returning the CAS of the old item below.
ITEM_set_cas(it, (settings.use_cas) ? ITEM_get_cas(new_it) : 0);
do_item_remove(new_it); /* release our reference */
} else {
/* Should never get here. This means we somehow fetched an unlinked
* item. TODO: Add a counter? */
if (settings.verbose) {
fprintf(stderr, "Tried to do incr/decr on invalid item\n");
}
if (it->refcount == 1)
do_item_remove(it);
return DELTA_ITEM_NOT_FOUND;
}
if (cas) {
*cas = ITEM_get_cas(it); /* swap the incoming CAS value */
}
do_item_remove(it); /* release our reference */
return OK;
}
static void process_delete_command(conn *c, token_t *tokens, const size_t ntokens) {
char *key;
size_t nkey;
item *it;
assert(c != NULL);
if (ntokens > 3) {
bool hold_is_zero = strcmp(tokens[KEY_TOKEN+1].value, "0") == 0;
bool sets_noreply = set_noreply_maybe(c, tokens, ntokens);
bool valid = (ntokens == 4 && (hold_is_zero || sets_noreply))
|| (ntokens == 5 && hold_is_zero && sets_noreply);
if (!valid) {
out_string(c, "CLIENT_ERROR bad command line format. "
"Usage: delete [noreply]");
return;
}
}
key = tokens[KEY_TOKEN].value;
nkey = tokens[KEY_TOKEN].length;
if(nkey > KEY_MAX_LENGTH) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
if (settings.detail_enabled) {
stats_prefix_record_delete(key, nkey);
}
it = item_get(key, nkey);
if (it) {
MEMCACHED_COMMAND_DELETE(c->sfd, ITEM_key(it), it->nkey);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.slab_stats[ITEM_clsid(it)].delete_hits++;
pthread_mutex_unlock(&c->thread->stats.mutex);
item_unlink(it);
item_remove(it); /* release our reference */
out_string(c, "DELETED");
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.delete_misses++;
pthread_mutex_unlock(&c->thread->stats.mutex);
out_string(c, "NOT_FOUND");
}
}
static void process_verbosity_command(conn *c, token_t *tokens, const size_t ntokens) {
unsigned int level;
assert(c != NULL);
set_noreply_maybe(c, tokens, ntokens);
level = strtoul(tokens[1].value, NULL, 10);
settings.verbose = level > MAX_VERBOSITY_LEVEL ? MAX_VERBOSITY_LEVEL : level;
out_string(c, "OK");
return;
}
static void process_slabs_automove_command(conn *c, token_t *tokens, const size_t ntokens) {
unsigned int level;
assert(c != NULL);
set_noreply_maybe(c, tokens, ntokens);
level = strtoul(tokens[2].value, NULL, 10);
if (level == 0) {
settings.slab_automove = 0;
} else if (level == 1 || level == 2) {
settings.slab_automove = level;
} else {
out_string(c, "ERROR");
return;
}
out_string(c, "OK");
return;
}
static void process_command(conn *c, char *command) {
token_t tokens[MAX_TOKENS];
size_t ntokens;
int comm;
assert(c != NULL);
MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
if (settings.verbose > 1)
fprintf(stderr, "<%d %s\n", c->sfd, command);
/*
* for commands set/add/replace, we build an item and read the data
* directly into it, then continue in nread_complete().
*/
c->msgcurr = 0;
c->msgused = 0;
c->iovused = 0;
if (add_msghdr(c) != 0) {
out_of_memory(c, "SERVER_ERROR out of memory preparing response");
return;
}
ntokens = tokenize_command(command, tokens, MAX_TOKENS);
if (ntokens >= 3 &&
((strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) ||
(strcmp(tokens[COMMAND_TOKEN].value, "bget") == 0))) {
process_get_command(c, tokens, ntokens, false);
} else if ((ntokens == 6 || ntokens == 7) &&
((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) ||
(strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) ||
(strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)) ||
(strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) ||
(strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) )) {
process_update_command(c, tokens, ntokens, comm, false);
} else if ((ntokens == 7 || ntokens == 8) && (strcmp(tokens[COMMAND_TOKEN].value, "cas") == 0 && (comm = NREAD_CAS))) {
process_update_command(c, tokens, ntokens, comm, true);
} else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) {
process_arithmetic_command(c, tokens, ntokens, 1);
} else if (ntokens >= 3 && (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0)) {
process_get_command(c, tokens, ntokens, true);
} else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "decr") == 0)) {
process_arithmetic_command(c, tokens, ntokens, 0);
} else if (ntokens >= 3 && ntokens <= 5 && (strcmp(tokens[COMMAND_TOKEN].value, "delete") == 0)) {
process_delete_command(c, tokens, ntokens);
} else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "touch") == 0)) {
process_touch_command(c, tokens, ntokens);
} else if (ntokens >= 2 && (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0)) {
process_stat(c, tokens, ntokens);
} else if (ntokens >= 2 && ntokens <= 4 && (strcmp(tokens[COMMAND_TOKEN].value, "flush_all") == 0)) {
time_t exptime = 0;
rel_time_t new_oldest = 0;
set_noreply_maybe(c, tokens, ntokens);
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.flush_cmds++;
pthread_mutex_unlock(&c->thread->stats.mutex);
if (!settings.flush_enabled) {
// flush_all is not allowed but we log it on stats
out_string(c, "CLIENT_ERROR flush_all not allowed");
return;
}
if (ntokens != (c->noreply ? 3 : 2)) {
exptime = strtol(tokens[1].value, NULL, 10);
if(errno == ERANGE) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
}
/*
If exptime is zero realtime() would return zero too, and
realtime(exptime) - 1 would overflow to the max unsigned
value. So we process exptime == 0 the same way we do when
no delay is given at all.
*/
if (exptime > 0) {
new_oldest = realtime(exptime);
} else { /* exptime == 0 */
new_oldest = current_time;
}
if (settings.use_cas) {
settings.oldest_live = new_oldest - 1;
if (settings.oldest_live <= current_time)
settings.oldest_cas = get_cas_id();
} else {
settings.oldest_live = new_oldest;
}
out_string(c, "OK");
return;
} else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "version") == 0)) {
out_string(c, "VERSION " VERSION);
} else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "quit") == 0)) {
conn_set_state(c, conn_closing);
} else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "shutdown") == 0)) {
if (settings.shutdown_command) {
conn_set_state(c, conn_closing);
raise(SIGINT);
} else {
out_string(c, "ERROR: shutdown not enabled");
}
} else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "slabs") == 0) {
if (ntokens == 5 && strcmp(tokens[COMMAND_TOKEN + 1].value, "reassign") == 0) {
int src, dst, rv;
if (settings.slab_reassign == false) {
out_string(c, "CLIENT_ERROR slab reassignment disabled");
return;
}
src = strtol(tokens[2].value, NULL, 10);
dst = strtol(tokens[3].value, NULL, 10);
if (errno == ERANGE) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
rv = slabs_reassign(src, dst);
switch (rv) {
case REASSIGN_OK:
out_string(c, "OK");
break;
case REASSIGN_RUNNING:
out_string(c, "BUSY currently processing reassign request");
break;
case REASSIGN_BADCLASS:
out_string(c, "BADCLASS invalid src or dst class id");
break;
case REASSIGN_NOSPARE:
out_string(c, "NOSPARE source class has no spare pages");
break;
case REASSIGN_SRC_DST_SAME:
out_string(c, "SAME src and dst class are identical");
break;
}
return;
} else if (ntokens == 4 &&
(strcmp(tokens[COMMAND_TOKEN + 1].value, "automove") == 0)) {
process_slabs_automove_command(c, tokens, ntokens);
} else {
out_string(c, "ERROR");
}
} else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "lru_crawler") == 0) {
if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "crawl") == 0) {
int rv;
if (settings.lru_crawler == false) {
out_string(c, "CLIENT_ERROR lru crawler disabled");
return;
}
rv = lru_crawler_crawl(tokens[2].value);
switch(rv) {
case CRAWLER_OK:
out_string(c, "OK");
break;
case CRAWLER_RUNNING:
out_string(c, "BUSY currently processing crawler request");
break;
case CRAWLER_BADCLASS:
out_string(c, "BADCLASS invalid class id");
break;
case CRAWLER_NOTSTARTED:
out_string(c, "NOTSTARTED no items to crawl");
break;
}
return;
} else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "tocrawl") == 0) {
uint32_t tocrawl;
if (!safe_strtoul(tokens[2].value, &tocrawl)) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
settings.lru_crawler_tocrawl = tocrawl;
out_string(c, "OK");
return;
} else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "sleep") == 0) {
uint32_t tosleep;
if (!safe_strtoul(tokens[2].value, &tosleep)) {
out_string(c, "CLIENT_ERROR bad command line format");
return;
}
if (tosleep > 1000000) {
out_string(c, "CLIENT_ERROR sleep must be one second or less");
return;
}
settings.lru_crawler_sleep = tosleep;
out_string(c, "OK");
return;
} else if (ntokens == 3) {
if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "enable") == 0)) {
if (start_item_crawler_thread() == 0) {
out_string(c, "OK");
} else {
out_string(c, "ERROR failed to start lru crawler thread");
}
} else if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "disable") == 0)) {
if (stop_item_crawler_thread() == 0) {
out_string(c, "OK");
} else {
out_string(c, "ERROR failed to stop lru crawler thread");
}
} else {
out_string(c, "ERROR");
}
return;
} else {
out_string(c, "ERROR");
}
} else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "verbosity") == 0)) {
process_verbosity_command(c, tokens, ntokens);
} else {
out_string(c, "ERROR");
}
return;
}
/*
* if we have a complete line in the buffer, process it.
*/
static int try_read_command(conn *c) {
assert(c != NULL);
assert(c->rcurr <= (c->rbuf + c->rsize));
assert(c->rbytes > 0);
if (c->protocol == negotiating_prot || c->transport == udp_transport) {
if ((unsigned char)c->rbuf[0] == (unsigned char)PROTOCOL_BINARY_REQ) {
c->protocol = binary_prot;
} else {
c->protocol = ascii_prot;
}
if (settings.verbose > 1) {
fprintf(stderr, "%d: Client using the %s protocol\n", c->sfd,
prot_text(c->protocol));
}
}
if (c->protocol == binary_prot) {
/* Do we have the complete packet header? */
if (c->rbytes < sizeof(c->binary_header)) {
/* need more data! */
return 0;
} else {
#ifdef NEED_ALIGN
if (((long)(c->rcurr)) % 8 != 0) {
/* must realign input buffer */
memmove(c->rbuf, c->rcurr, c->rbytes);
c->rcurr = c->rbuf;
if (settings.verbose > 1) {
fprintf(stderr, "%d: Realign input buffer\n", c->sfd);
}
}
#endif
protocol_binary_request_header* req;
req = (protocol_binary_request_header*)c->rcurr;
if (settings.verbose > 1) {
/* Dump the packet before we convert it to host order */
int ii;
fprintf(stderr, "<%d Read binary protocol data:", c->sfd);
for (ii = 0; ii < sizeof(req->bytes); ++ii) {
if (ii % 4 == 0) {
fprintf(stderr, "\n<%d ", c->sfd);
}
fprintf(stderr, " 0x%02x", req->bytes[ii]);
}
fprintf(stderr, "\n");
}
c->binary_header = *req;
c->binary_header.request.keylen = ntohs(req->request.keylen);
c->binary_header.request.bodylen = ntohl(req->request.bodylen);
c->binary_header.request.cas = ntohll(req->request.cas);
if (c->binary_header.request.magic != PROTOCOL_BINARY_REQ) {
if (settings.verbose) {
fprintf(stderr, "Invalid magic: %x\n",
c->binary_header.request.magic);
}
conn_set_state(c, conn_closing);
return -1;
}
c->msgcurr = 0;
c->msgused = 0;
c->iovused = 0;
if (add_msghdr(c) != 0) {
out_of_memory(c,
"SERVER_ERROR Out of memory allocating headers");
return 0;
}
c->cmd = c->binary_header.request.opcode;
c->keylen = c->binary_header.request.keylen;
c->opaque = c->binary_header.request.opaque;
/* clear the returned cas value */
c->cas = 0;
dispatch_bin_command(c);
c->rbytes -= sizeof(c->binary_header);
c->rcurr += sizeof(c->binary_header);
}
} else {
char *el, *cont;
if (c->rbytes == 0)
return 0;
el = memchr(c->rcurr, '\n', c->rbytes);
if (!el) {
if (c->rbytes > 1024) {
/*
* We didn't have a '\n' in the first k. This _has_ to be a
* large multiget, if not we should just nuke the connection.
*/
char *ptr = c->rcurr;
while (*ptr == ' ') { /* ignore leading whitespaces */
++ptr;
}
if (ptr - c->rcurr > 100 ||
(strncmp(ptr, "get ", 4) && strncmp(ptr, "gets ", 5))) {
conn_set_state(c, conn_closing);
return 1;
}
}
return 0;
}
cont = el + 1;
if ((el - c->rcurr) > 1 && *(el - 1) == '\r') {
el--;
}
*el = '\0';
assert(cont <= (c->rcurr + c->rbytes));
c->last_cmd_time = current_time;
process_command(c, c->rcurr);
c->rbytes -= (cont - c->rcurr);
c->rcurr = cont;
assert(c->rcurr <= (c->rbuf + c->rsize));
}
return 1;
}
/*
* read a UDP request.
*/
static enum try_read_result try_read_udp(conn *c) {
int res;
assert(c != NULL);
c->request_addr_size = sizeof(c->request_addr);
res = recvfrom(c->sfd, c->rbuf, c->rsize,
0, (struct sockaddr *)&c->request_addr,
&c->request_addr_size);
if (res > 8) {
unsigned char *buf = (unsigned char *)c->rbuf;
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.bytes_read += res;
pthread_mutex_unlock(&c->thread->stats.mutex);
/* Beginning of UDP packet is the request ID; save it. */
c->request_id = buf[0] * 256 + buf[1];
/* If this is a multi-packet request, drop it. */
if (buf[4] != 0 || buf[5] != 1) {
out_string(c, "SERVER_ERROR multi-packet request not supported");
return READ_NO_DATA_RECEIVED;
}
/* Don't care about any of the rest of the header. */
res -= 8;
memmove(c->rbuf, c->rbuf + 8, res);
c->rbytes = res;
c->rcurr = c->rbuf;
return READ_DATA_RECEIVED;
}
return READ_NO_DATA_RECEIVED;
}
/*
* read from network as much as we can, handle buffer overflow and connection
* close.
* before reading, move the remaining incomplete fragment of a command
* (if any) to the beginning of the buffer.
*
* To protect us from someone flooding a connection with bogus data causing
* the connection to eat up all available memory, break out and start looking
* at the data I've got after a number of reallocs...
*
* @return enum try_read_result
*/
static enum try_read_result try_read_network(conn *c) {
enum try_read_result gotdata = READ_NO_DATA_RECEIVED;
int res;
int num_allocs = 0;
assert(c != NULL);
if (c->rcurr != c->rbuf) {
if (c->rbytes != 0) /* otherwise there's nothing to copy */
memmove(c->rbuf, c->rcurr, c->rbytes);
c->rcurr = c->rbuf;
}
while (1) {
if (c->rbytes >= c->rsize) {
if (num_allocs == 4) {
return gotdata;
}
++num_allocs;
char *new_rbuf = realloc(c->rbuf, c->rsize * 2);
if (!new_rbuf) {
STATS_LOCK();
stats.malloc_fails++;
STATS_UNLOCK();
if (settings.verbose > 0) {
fprintf(stderr, "Couldn't realloc input buffer\n");
}
c->rbytes = 0; /* ignore what we read */
out_of_memory(c, "SERVER_ERROR out of memory reading request");
c->write_and_go = conn_closing;
return READ_MEMORY_ERROR;
}
c->rcurr = c->rbuf = new_rbuf;
c->rsize *= 2;
}
int avail = c->rsize - c->rbytes;
res = read(c->sfd, c->rbuf + c->rbytes, avail);
if (res > 0) {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.bytes_read += res;
pthread_mutex_unlock(&c->thread->stats.mutex);
gotdata = READ_DATA_RECEIVED;
c->rbytes += res;
if (res == avail) {
continue;
} else {
break;
}
}
if (res == 0) {
return READ_ERROR;
}
if (res == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
return READ_ERROR;
}
}
return gotdata;
}
static bool update_event(conn *c, const int new_flags) {
assert(c != NULL);
struct event_base *base = c->event.ev_base;
if (c->ev_flags == new_flags)
return true;
if (event_del(&c->event) == -1) return false;
event_set(&c->event, c->sfd, new_flags, event_handler, (void *)c);
event_base_set(base, &c->event);
c->ev_flags = new_flags;
if (event_add(&c->event, 0) == -1) return false;
return true;
}
/*
* Sets whether we are listening for new connections or not.
*/
void do_accept_new_conns(const bool do_accept) {
conn *next;
for (next = listen_conn; next; next = next->next) {
if (do_accept) {
update_event(next, EV_READ | EV_PERSIST);
if (listen(next->sfd, settings.backlog) != 0) {
perror("listen");
}
}
else {
update_event(next, 0);
if (listen(next->sfd, 0) != 0) {
perror("listen");
}
}
}
if (do_accept) {
struct timeval maxconns_exited;
uint64_t elapsed_us;
gettimeofday(&maxconns_exited,NULL);
STATS_LOCK();
elapsed_us =
(maxconns_exited.tv_sec - stats.maxconns_entered.tv_sec) * 1000000
+ (maxconns_exited.tv_usec - stats.maxconns_entered.tv_usec);
stats.time_in_listen_disabled_us += elapsed_us;
stats.accepting_conns = true;
STATS_UNLOCK();
} else {
STATS_LOCK();
stats.accepting_conns = false;
gettimeofday(&stats.maxconns_entered,NULL);
stats.listen_disabled_num++;
STATS_UNLOCK();
allow_new_conns = false;
maxconns_handler(-42, 0, 0);
}
}
/*
* Transmit the next chunk of data from our list of msgbuf structures.
*
* Returns:
* TRANSMIT_COMPLETE All done writing.
* TRANSMIT_INCOMPLETE More data remaining to write.
* TRANSMIT_SOFT_ERROR Can't write any more right now.
* TRANSMIT_HARD_ERROR Can't write (c->state is set to conn_closing)
*/
static enum transmit_result transmit(conn *c) {
assert(c != NULL);
if (c->msgcurr < c->msgused &&
c->msglist[c->msgcurr].msg_iovlen == 0) {
/* Finished writing the current msg; advance to the next. */
c->msgcurr++;
}
if (c->msgcurr < c->msgused) {
ssize_t res;
struct msghdr *m = &c->msglist[c->msgcurr];
res = sendmsg(c->sfd, m, 0);
if (res > 0) {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.bytes_written += res;
pthread_mutex_unlock(&c->thread->stats.mutex);
/* We've written some of the data. Remove the completed
iovec entries from the list of pending writes. */
while (m->msg_iovlen > 0 && res >= m->msg_iov->iov_len) {
res -= m->msg_iov->iov_len;
m->msg_iovlen--;
m->msg_iov++;
}
/* Might have written just part of the last iovec entry;
adjust it so the next write will do the rest. */
if (res > 0) {
m->msg_iov->iov_base = (caddr_t)m->msg_iov->iov_base + res;
m->msg_iov->iov_len -= res;
}
return TRANSMIT_INCOMPLETE;
}
if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
if (!update_event(c, EV_WRITE | EV_PERSIST)) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't update event\n");
conn_set_state(c, conn_closing);
return TRANSMIT_HARD_ERROR;
}
return TRANSMIT_SOFT_ERROR;
}
/* if res == 0 or res == -1 and error is not EAGAIN or EWOULDBLOCK,
we have a real error, on which we close the connection */
if (settings.verbose > 0)
perror("Failed to write, and not due to blocking");
if (IS_UDP(c->transport))
conn_set_state(c, conn_read);
else
conn_set_state(c, conn_closing);
return TRANSMIT_HARD_ERROR;
} else {
return TRANSMIT_COMPLETE;
}
}
static void drive_machine(conn *c) {
bool stop = false;
int sfd;
socklen_t addrlen;
struct sockaddr_storage addr;
int nreqs = settings.reqs_per_event;
int res;
const char *str;
#ifdef HAVE_ACCEPT4
static int use_accept4 = 1;
#else
static int use_accept4 = 0;
#endif
assert(c != NULL);
while (!stop) {
switch(c->state) {
case conn_listening:
addrlen = sizeof(addr);
#ifdef HAVE_ACCEPT4
if (use_accept4) {
sfd = accept4(c->sfd, (struct sockaddr *)&addr, &addrlen, SOCK_NONBLOCK);
} else {
sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen);
}
#else
sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen);
#endif
if (sfd == -1) {
if (use_accept4 && errno == ENOSYS) {
use_accept4 = 0;
continue;
}
perror(use_accept4 ? "accept4()" : "accept()");
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* these are transient, so don't log anything */
stop = true;
} else if (errno == EMFILE) {
if (settings.verbose > 0)
fprintf(stderr, "Too many open connections\n");
accept_new_conns(false);
stop = true;
} else {
perror("accept()");
stop = true;
}
break;
}
if (!use_accept4) {
if (fcntl(sfd, F_SETFL, fcntl(sfd, F_GETFL) | O_NONBLOCK) < 0) {
perror("setting O_NONBLOCK");
close(sfd);
break;
}
}
if (settings.maxconns_fast &&
stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {
str = "ERROR Too many open connections\r\n";
res = write(sfd, str, strlen(str));
close(sfd);
STATS_LOCK();
stats.rejected_conns++;
STATS_UNLOCK();
} else {
dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,
DATA_BUFFER_SIZE, tcp_transport);
}
stop = true;
break;
case conn_waiting:
if (!update_event(c, EV_READ | EV_PERSIST)) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't update event\n");
conn_set_state(c, conn_closing);
break;
}
conn_set_state(c, conn_read);
stop = true;
break;
case conn_read:
res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);
switch (res) {
case READ_NO_DATA_RECEIVED:
conn_set_state(c, conn_waiting);
break;
case READ_DATA_RECEIVED:
conn_set_state(c, conn_parse_cmd);
break;
case READ_ERROR:
conn_set_state(c, conn_closing);
break;
case READ_MEMORY_ERROR: /* Failed to allocate more memory */
/* State already set by try_read_network */
break;
}
break;
case conn_parse_cmd :
if (try_read_command(c) == 0) {
/* wee need more data! */
conn_set_state(c, conn_waiting);
}
break;
case conn_new_cmd:
/* Only process nreqs at a time to avoid starving other
connections */
--nreqs;
if (nreqs >= 0) {
reset_cmd_handler(c);
} else {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.conn_yields++;
pthread_mutex_unlock(&c->thread->stats.mutex);
if (c->rbytes > 0) {
/* We have already read in data into the input buffer,
so libevent will most likely not signal read events
on the socket (unless more data is available. As a
hack we should just put in a request to write data,
because that should be possible ;-)
*/
if (!update_event(c, EV_WRITE | EV_PERSIST)) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't update event\n");
conn_set_state(c, conn_closing);
break;
}
}
stop = true;
}
break;
case conn_nread:
if (c->rlbytes == 0) {
complete_nread(c);
break;
}
/* Check if rbytes < 0, to prevent crash */
if (c->rlbytes < 0) {
if (settings.verbose) {
fprintf(stderr, "Invalid rlbytes to read: len %d\n", c->rlbytes);
}
conn_set_state(c, conn_closing);
break;
}
/* first check if we have leftovers in the conn_read buffer */
if (c->rbytes > 0) {
int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;
if (c->ritem != c->rcurr) {
memmove(c->ritem, c->rcurr, tocopy);
}
c->ritem += tocopy;
c->rlbytes -= tocopy;
c->rcurr += tocopy;
c->rbytes -= tocopy;
if (c->rlbytes == 0) {
break;
}
}
/* now try reading from the socket */
res = read(c->sfd, c->ritem, c->rlbytes);
if (res > 0) {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.bytes_read += res;
pthread_mutex_unlock(&c->thread->stats.mutex);
if (c->rcurr == c->ritem) {
c->rcurr += res;
}
c->ritem += res;
c->rlbytes -= res;
break;
}
if (res == 0) { /* end of stream */
conn_set_state(c, conn_closing);
break;
}
if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
if (!update_event(c, EV_READ | EV_PERSIST)) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't update event\n");
conn_set_state(c, conn_closing);
break;
}
stop = true;
break;
}
/* otherwise we have a real error, on which we close the connection */
if (settings.verbose > 0) {
fprintf(stderr, "Failed to read, and not due to blocking:\n"
"errno: %d %s \n"
"rcurr=%lx ritem=%lx rbuf=%lx rlbytes=%d rsize=%d\n",
errno, strerror(errno),
(long)c->rcurr, (long)c->ritem, (long)c->rbuf,
(int)c->rlbytes, (int)c->rsize);
}
conn_set_state(c, conn_closing);
break;
case conn_swallow:
/* we are reading sbytes and throwing them away */
if (c->sbytes == 0) {
conn_set_state(c, conn_new_cmd);
break;
}
/* first check if we have leftovers in the conn_read buffer */
if (c->rbytes > 0) {
int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes;
c->sbytes -= tocopy;
c->rcurr += tocopy;
c->rbytes -= tocopy;
break;
}
/* now try reading from the socket */
res = read(c->sfd, c->rbuf, c->rsize > c->sbytes ? c->sbytes : c->rsize);
if (res > 0) {
pthread_mutex_lock(&c->thread->stats.mutex);
c->thread->stats.bytes_read += res;
pthread_mutex_unlock(&c->thread->stats.mutex);
c->sbytes -= res;
break;
}
if (res == 0) { /* end of stream */
conn_set_state(c, conn_closing);
break;
}
if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
if (!update_event(c, EV_READ | EV_PERSIST)) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't update event\n");
conn_set_state(c, conn_closing);
break;
}
stop = true;
break;
}
/* otherwise we have a real error, on which we close the connection */
if (settings.verbose > 0)
fprintf(stderr, "Failed to read, and not due to blocking\n");
conn_set_state(c, conn_closing);
break;
case conn_write:
/*
* We want to write out a simple response. If we haven't already,
* assemble it into a msgbuf list (this will be a single-entry
* list for TCP or a two-entry list for UDP).
*/
if (c->iovused == 0 || (IS_UDP(c->transport) && c->iovused == 1)) {
if (add_iov(c, c->wcurr, c->wbytes) != 0) {
if (settings.verbose > 0)
fprintf(stderr, "Couldn't build response\n");
conn_set_state(c, conn_closing);
break;
}
}
/* fall through... */
case conn_mwrite:
if (IS_UDP(c->transport) && c->msgcurr == 0 && build_udp_headers(c) != 0) {
if (settings.verbose > 0)
fprintf(stderr, "Failed to build UDP headers\n");
conn_set_state(c, conn_closing);
break;
}
switch (transmit(c)) {
case TRANSMIT_COMPLETE:
if (c->state == conn_mwrite) {
conn_release_items(c);
/* XXX: I don't know why this wasn't the general case */
if(c->protocol == binary_prot) {
conn_set_state(c, c->write_and_go);
} else {
conn_set_state(c, conn_new_cmd);
}
} else if (c->state == conn_write) {
if (c->write_and_free) {
free(c->write_and_free);
c->write_and_free = 0;
}
conn_set_state(c, c->write_and_go);
} else {
if (settings.verbose > 0)
fprintf(stderr, "Unexpected state %d\n", c->state);
conn_set_state(c, conn_closing);
}
break;
case TRANSMIT_INCOMPLETE:
case TRANSMIT_HARD_ERROR:
break; /* Continue in state machine. */
case TRANSMIT_SOFT_ERROR:
stop = true;
break;
}
break;
case conn_closing:
if (IS_UDP(c->transport))
conn_cleanup(c);
else
conn_close(c);
stop = true;
break;
case conn_closed:
/* This only happens if dormando is an idiot. */
abort();
break;
case conn_max_state:
assert(false);
break;
}
}
return;
}
void event_handler(const int fd, const short which, void *arg) {
conn *c;
c = (conn *)arg;
assert(c != NULL);
c->which = which;
/* sanity */
if (fd != c->sfd) {
if (settings.verbose > 0)
fprintf(stderr, "Catastrophic: event fd doesn't match conn fd!\n");
conn_close(c);
return;
}
drive_machine(c);
/* wait for next event */
return;
}
static int new_socket(struct addrinfo *ai) {
int sfd;
int flags;
if ((sfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
return -1;
}
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("setting O_NONBLOCK");
close(sfd);
return -1;
}
return sfd;
}
/*
* Sets a socket's send buffer size to the maximum allowed by the system.
*/
static void maximize_sndbuf(const int sfd) {
socklen_t intsize = sizeof(int);
int last_good = 0;
int min, max, avg;
int old_size;
/* Start with the default size. */
if (getsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &old_size, &intsize) != 0) {
if (settings.verbose > 0)
perror("getsockopt(SO_SNDBUF)");
return;
}
/* Binary-search for the real maximum. */
min = old_size;
max = MAX_SENDBUF_SIZE;
while (min <= max) {
avg = ((unsigned int)(min + max)) / 2;
if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void *)&avg, intsize) == 0) {
last_good = avg;
min = avg + 1;
} else {
max = avg - 1;
}
}
if (settings.verbose > 1)
fprintf(stderr, "<%d send buffer was %d, now %d\n", sfd, old_size, last_good);
}
/**
* Create a socket and bind it to a specific port number
* @param interface the interface to bind to
* @param port the port number to bind to
* @param transport the transport protocol (TCP / UDP)
* @param portnumber_file A filepointer to write the port numbers to
* when they are successfully added to the list of ports we
* listen on.
*/
static int server_socket(const char *interface,
int port,
enum network_transport transport,
FILE *portnumber_file) {
int sfd;
struct linger ling = {0, 0};
struct addrinfo *ai;
struct addrinfo *next;
struct addrinfo hints = { .ai_flags = AI_PASSIVE,
.ai_family = AF_UNSPEC };
char port_buf[NI_MAXSERV];
int error;
int success = 0;
int flags =1;
hints.ai_socktype = IS_UDP(transport) ? SOCK_DGRAM : SOCK_STREAM;
if (port == -1) {
port = 0;
}
snprintf(port_buf, sizeof(port_buf), "%d", port);
error= getaddrinfo(interface, port_buf, &hints, &ai);
if (error != 0) {
if (error != EAI_SYSTEM)
fprintf(stderr, "getaddrinfo(): %s\n", gai_strerror(error));
else
perror("getaddrinfo()");
return 1;
}
for (next= ai; next; next= next->ai_next) {
conn *listen_conn_add;
if ((sfd = new_socket(next)) == -1) {
/* getaddrinfo can return "junk" addresses,
* we make sure at least one works before erroring.
*/
if (errno == EMFILE) {
/* ...unless we're out of fds */
perror("server_socket");
exit(EX_OSERR);
}
continue;
}
#ifdef IPV6_V6ONLY
if (next->ai_family == AF_INET6) {
error = setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &flags, sizeof(flags));
if (error != 0) {
perror("setsockopt");
close(sfd);
continue;
}
}
#endif
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
if (IS_UDP(transport)) {
maximize_sndbuf(sfd);
} else {
error = setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
if (error != 0)
perror("setsockopt");
error = setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
if (error != 0)
perror("setsockopt");
error = setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags));
if (error != 0)
perror("setsockopt");
}
if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) {
if (errno != EADDRINUSE) {
perror("bind()");
close(sfd);
freeaddrinfo(ai);
return 1;
}
close(sfd);
continue;
} else {
success++;
if (!IS_UDP(transport) && listen(sfd, settings.backlog) == -1) {
perror("listen()");
close(sfd);
freeaddrinfo(ai);
return 1;
}
if (portnumber_file != NULL &&
(next->ai_addr->sa_family == AF_INET ||
next->ai_addr->sa_family == AF_INET6)) {
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} my_sockaddr;
socklen_t len = sizeof(my_sockaddr);
if (getsockname(sfd, (struct sockaddr*)&my_sockaddr, &len)==0) {
if (next->ai_addr->sa_family == AF_INET) {
fprintf(portnumber_file, "%s INET: %u\n",
IS_UDP(transport) ? "UDP" : "TCP",
ntohs(my_sockaddr.in.sin_port));
} else {
fprintf(portnumber_file, "%s INET6: %u\n",
IS_UDP(transport) ? "UDP" : "TCP",
ntohs(my_sockaddr.in6.sin6_port));
}
}
}
}
if (IS_UDP(transport)) {
int c;
for (c = 0; c < settings.num_threads_per_udp; c++) {
/* Allocate one UDP file descriptor per worker thread;
* this allows "stats conns" to separately list multiple
* parallel UDP requests in progress.
*
* The dispatch code round-robins new connection requests
* among threads, so this is guaranteed to assign one
* FD to each thread.
*/
int per_thread_fd = c ? dup(sfd) : sfd;
dispatch_conn_new(per_thread_fd, conn_read,
EV_READ | EV_PERSIST,
UDP_READ_BUFFER_SIZE, transport);
}
} else {
if (!(listen_conn_add = conn_new(sfd, conn_listening,
EV_READ | EV_PERSIST, 1,
transport, main_base))) {
fprintf(stderr, "failed to create listening connection\n");
exit(EXIT_FAILURE);
}
listen_conn_add->next = listen_conn;
listen_conn = listen_conn_add;
}
}
freeaddrinfo(ai);
/* Return zero iff we detected no errors in starting up connections */
return success == 0;
}
static int server_sockets(int port, enum network_transport transport,
FILE *portnumber_file) {
if (settings.inter == NULL) {
return server_socket(settings.inter, port, transport, portnumber_file);
} else {
// tokenize them and bind to each one of them..
char *b;
int ret = 0;
char *list = strdup(settings.inter);
if (list == NULL) {
fprintf(stderr, "Failed to allocate memory for parsing server interface string\n");
return 1;
}
for (char *p = strtok_r(list, ";,", &b);
p != NULL;
p = strtok_r(NULL, ";,", &b)) {
int the_port = port;
char *h = NULL;
if (*p == '[') {
// expecting it to be an IPv6 address enclosed in []
// i.e. RFC3986 style recommended by RFC5952
char *e = strchr(p, ']');
if (e == NULL) {
fprintf(stderr, "Invalid IPV6 address: \"%s\"", p);
return 1;
}
h = ++p; // skip the opening '['
*e = '\0';
p = ++e; // skip the closing ']'
}
char *s = strchr(p, ':');
if (s != NULL) {
// If no more semicolons - attempt to treat as port number.
// Otherwise the only valid option is an unenclosed IPv6 without port, until
// of course there was an RFC3986 IPv6 address previously specified -
// in such a case there is no good option, will just send it to fail as port number.
if (strchr(s + 1, ':') == NULL || h != NULL) {
*s = '\0';
++s;
if (!safe_strtol(s, &the_port)) {
fprintf(stderr, "Invalid port number: \"%s\"", s);
return 1;
}
}
}
if (h != NULL)
p = h;
if (strcmp(p, "*") == 0) {
p = NULL;
}
ret |= server_socket(p, the_port, transport, portnumber_file);
}
free(list);
return ret;
}
}
static int new_socket_unix(void) {
int sfd;
int flags;
if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket()");
return -1;
}
if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||
fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("setting O_NONBLOCK");
close(sfd);
return -1;
}
return sfd;
}
static int server_socket_unix(const char *path, int access_mask) {
int sfd;
struct linger ling = {0, 0};
struct sockaddr_un addr;
struct stat tstat;
int flags =1;
int old_umask;
if (!path) {
return 1;
}
if ((sfd = new_socket_unix()) == -1) {
return 1;
}
/*
* Clean up a previous socket file if we left it around
*/
if (lstat(path, &tstat) == 0) {
if (S_ISSOCK(tstat.st_mode))
unlink(path);
}
setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags));
setsockopt(sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags));
setsockopt(sfd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
/*
* the memset call clears nonstandard fields in some impementations
* that otherwise mess things up.
*/
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
assert(strcmp(addr.sun_path, path) == 0);
old_umask = umask( ~(access_mask&0777));
if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind()");
close(sfd);
umask(old_umask);
return 1;
}
umask(old_umask);
if (listen(sfd, settings.backlog) == -1) {
perror("listen()");
close(sfd);
return 1;
}
if (!(listen_conn = conn_new(sfd, conn_listening,
EV_READ | EV_PERSIST, 1,
local_transport, main_base))) {
fprintf(stderr, "failed to create listening connection\n");
exit(EXIT_FAILURE);
}
return 0;
}
/*
* We keep the current time of day in a global variable that's updated by a
* timer event. This saves us a bunch of time() system calls (we really only
* need to get the time once a second, whereas there can be tens of thousands
* of requests a second) and allows us to use server-start-relative timestamps
* rather than absolute UNIX timestamps, a space savings on systems where
* sizeof(time_t) > sizeof(unsigned int).
*/
volatile rel_time_t current_time;
static struct event clockevent;
/* libevent uses a monotonic clock when available for event scheduling. Aside
* from jitter, simply ticking our internal timer here is accurate enough.
* Note that users who are setting explicit dates for expiration times *must*
* ensure their clocks are correct before starting memcached. */
static void clock_handler(const int fd, const short which, void *arg) {
struct timeval t = {.tv_sec = 1, .tv_usec = 0};
static bool initialized = false;
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
static bool monotonic = false;
static time_t monotonic_start;
#endif
if (initialized) {
/* only delete the event if it's actually there. */
evtimer_del(&clockevent);
} else {
initialized = true;
/* process_started is initialized to time() - 2. We initialize to 1 so
* flush_all won't underflow during tests. */
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
monotonic = true;
monotonic_start = ts.tv_sec - ITEM_UPDATE_INTERVAL - 2;
}
#endif
}
evtimer_set(&clockevent, clock_handler, 0);
event_base_set(main_base, &clockevent);
evtimer_add(&clockevent, &t);
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
if (monotonic) {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
return;
current_time = (rel_time_t) (ts.tv_sec - monotonic_start);
return;
}
#endif
{
struct timeval tv;
gettimeofday(&tv, NULL);
current_time = (rel_time_t) (tv.tv_sec - process_started);
}
}
static void usage(void) {
printf(PACKAGE " " VERSION "\n");
printf("-p TCP port number to listen on (default: 11211)\n"
"-U UDP port number to listen on (default: 11211, 0 is off)\n"
"-s UNIX socket path to listen on (disables network support)\n"
"-A enable ascii \"shutdown\" command\n"
"-a access mask for UNIX socket, in octal (default: 0700)\n"
"-l