memcached-1.5.6/ 0000775 0001750 0001750 00000000000 13245330107 010432 5 0000000 0000000 memcached-1.5.6/slab_automove_extstore.h 0000664 0001750 0001750 00000000366 13240770206 015330 0000000 0000000 #ifndef SLAB_AUTOMOVE_EXTSTORE_H
#define SLAB_AUTOMOVE_EXTSTORE_H
void *slab_automove_extstore_init(struct settings *settings);
void slab_automove_extstore_free(void *arg);
void slab_automove_extstore_run(void *arg, int *src, int *dst);
#endif
memcached-1.5.6/config.guess 0000755 0001750 0001750 00000124753 12612517377 012721 0000000 0000000 #! /bin/sh
# Attempt to guess a canonical system name.
# Copyright 1992-2015 Free Software Foundation, Inc.
timestamp='2015-08-20'
# 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; maintained since 2000 by Ben Elliston.
#
# 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 to .
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-2015 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=`(uname -p 2>/dev/null || \
/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 ;;
earmv*)
arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
machine=${arch}${endian}-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*|earm*|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
# Determine ABI tags.
case "${UNAME_MACHINE_ARCH}" in
earm*)
expr='s/^earmv[0-9]/-eabi/;s/eb$//'
abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
;;
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/[-_].*//' | cut -d. -f1,2`
;;
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}${abi}"
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 ;;
*:Sortix:*:*)
echo ${UNAME_MACHINE}-unknown-sortix
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/lslpp ] ; then
IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
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 ;;
e2k:Linux:*:*)
echo ${UNAME_MACHINE}-unknown-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}-pc-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.5.6/assoc.h 0000664 0001750 0001750 00000000767 13242642567 011662 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);
void assoc_start_expand(uint64_t curr_items);
extern unsigned int hashpower;
extern unsigned int item_lock_hashpower;
memcached-1.5.6/jenkins_hash.c 0000664 0001750 0001750 00000034565 13115057711 013202 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.5.6/slabs.c 0000664 0001750 0001750 00000125160 13242642567 011644 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;
#ifdef EXTSTORE
static void *storage = NULL;
#endif
/**
* 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 grow_slab_list (const unsigned int id);
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);
#ifdef EXTSTORE
void slabs_set_storage(void *arg) {
storage = arg;
}
#endif
/*
* 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 || size > settings.item_size_max)
return 0;
while (size > slabclass[res].size)
if (res++ == power_largest) /* won't fit in the biggest slab */
return power_largest;
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, const uint32_t *slab_sizes) {
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) {
if (slab_sizes != NULL) {
if (slab_sizes[i-1] == 0)
break;
size = slab_sizes[i-1];
} else if (size >= settings.slab_chunk_size_max / factor) {
break;
}
/* 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.slab_page_size / slabclass[i].size;
if (slab_sizes == NULL)
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.slab_chunk_size_max;
slabclass[power_largest].perslab = settings.slab_page_size / settings.slab_chunk_size_max;
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);
}
}
void slabs_prefill_global(void) {
void *ptr;
slabclass_t *p = &slabclass[0];
int len = settings.slab_page_size;
while (mem_malloced < mem_limit
&& (ptr = memory_allocate(len)) != NULL) {
grow_slab_list(0);
p->slab_list[p->slabs++] = ptr;
}
mem_limit_reached = true;
}
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.slab_chunk_size_max != settings.slab_page_size)
? settings.slab_page_size
: 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, uint64_t *total_bytes,
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_bytes != NULL) {
*total_bytes = p->requested;
}
assert(size <= p->size);
/* 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_chunked(item *it, const size_t size) {
item_chunk *chunk = (item_chunk *) ITEM_data(it);
slabclass_t *p;
it->it_flags = ITEM_SLABBED;
it->slabs_clsid = 0;
it->prev = 0;
// header object's original classid is stored in chunk.
p = &slabclass[chunk->orig_clsid];
if (chunk->next) {
chunk = chunk->next;
chunk->prev = 0;
} else {
// header with no attached chunk
chunk = NULL;
}
// return the header object.
// TODO: This is in three places, here and in do_slabs_free().
it->prev = 0;
it->next = p->slots;
if (it->next) it->next->prev = it;
p->slots = it;
p->sl_curr++;
// TODO: macro
p->requested -= it->nkey + 1 + it->nsuffix + sizeof(item) + sizeof(item_chunk);
if (settings.use_cas) {
p->requested -= sizeof(uint64_t);
}
item_chunk *next_chunk;
while (chunk) {
assert(chunk->it_flags == ITEM_CHUNK);
chunk->it_flags = ITEM_SLABBED;
p = &slabclass[chunk->slabs_clsid];
chunk->slabs_clsid = 0;
next_chunk = chunk->next;
chunk->prev = 0;
chunk->next = p->slots;
if (chunk->next) chunk->next->prev = chunk;
p->slots = chunk;
p->sl_curr++;
p->requested -= chunk->size + sizeof(item_chunk);
chunk = next_chunk;
}
return;
}
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;
if ((it->it_flags & ITEM_CHUNKED) == 0) {
#ifdef EXTSTORE
bool is_hdr = it->it_flags & ITEM_HDR;
#endif
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++;
#ifdef EXTSTORE
if (!is_hdr) {
p->requested -= size;
} else {
p->requested -= (size - it->nbytes) + sizeof(item_hdr);
}
#else
p->requested -= size;
#endif
} else {
do_slabs_free_chunked(it, size);
}
return;
}
/* With refactoring of the various stats code the automover won't need a
* custom function here.
*/
void fill_slab_stats_automove(slab_stats_automove *am) {
int n;
pthread_mutex_lock(&slabs_lock);
for (n = 0; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
slabclass_t *p = &slabclass[n];
slab_stats_automove *cur = &am[n];
cur->chunks_per_page = p->perslab;
cur->free_chunks = p->sl_curr;
cur->total_pages = p->slabs;
cur->chunk_size = p->size;
}
pthread_mutex_unlock(&slabs_lock);
}
/* TODO: slabs_available_chunks should grow up to encompass this.
* mem_flag is redundant with the other function.
*/
unsigned int global_page_pool_size(bool *mem_flag) {
unsigned int ret = 0;
pthread_mutex_lock(&slabs_lock);
if (mem_flag != NULL)
*mem_flag = mem_malloced >= mem_limit ? true : false;
ret = slabclass[SLAB_GLOBAL_PAGE_POOL].slabs;
pthread_mutex_unlock(&slabs_lock);
return ret;
}
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_state.curr_bytes);
APPEND_STAT("curr_items", "%llu", (unsigned long long)stats_state.curr_items);
APPEND_STAT("total_items", "%llu", (unsigned long long)stats.total_items);
STATS_UNLOCK();
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 if (nz_strcmp(nkey, stat_type, "sizes_enable") == 0) {
item_stats_sizes_enable(add_stats, c);
} else if (nz_strcmp(nkey, stat_type, "sizes_disable") == 0) {
item_stats_sizes_disable(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;
}
/* Must only be used if all pages are item_size_max */
static void memory_release() {
void *p = NULL;
if (mem_base != NULL)
return;
if (!settings.slab_reassign)
return;
while (mem_malloced > mem_limit &&
(p = get_page_from_global_pool()) != NULL) {
free(p);
mem_malloced -= settings.slab_page_size;
}
}
void *slabs_alloc(size_t size, unsigned int id, uint64_t *total_bytes,
unsigned int flags) {
void *ret;
pthread_mutex_lock(&slabs_lock);
ret = do_slabs_alloc(size, id, total_bytes, 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);
}
static bool do_slabs_adjust_mem_limit(size_t new_mem_limit) {
/* Cannot adjust memory limit at runtime if prealloc'ed */
if (mem_base != NULL)
return false;
settings.maxbytes = new_mem_limit;
mem_limit = new_mem_limit;
mem_limit_reached = false; /* Will reset on next alloc */
memory_release(); /* free what might already be in the global pool */
return true;
}
bool slabs_adjust_mem_limit(size_t new_mem_limit) {
bool ret;
pthread_mutex_lock(&slabs_lock);
ret = do_slabs_adjust_mem_limit(new_mem_limit);
pthread_mutex_unlock(&slabs_lock);
return ret;
}
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,
uint64_t *total_bytes, 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_malloced >= mem_limit ? true : false;
if (total_bytes != NULL)
*total_bytes = p->requested;
if (chunks_perslab != NULL)
*chunks_perslab = p->perslab;
pthread_mutex_unlock(&slabs_lock);
return ret;
}
/* The slabber system could avoid needing to understand much, if anything,
* about items if callbacks were strategically used. Due to how the slab mover
* works, certain flag bits can only be adjusted while holding the slabs lock.
* Using these functions, isolate sections of code needing this and turn them
* into callbacks when an interface becomes more obvious.
*/
void slabs_mlock(void) {
pthread_mutex_lock(&slabs_lock);
}
void slabs_munlock(void) {
pthread_mutex_unlock(&slabs_lock);
}
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 < SLAB_GLOBAL_PAGE_POOL ||
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;
// Don't need to do chunk move work if page is in global pool.
if (slab_rebal.s_clsid == SLAB_GLOBAL_PAGE_POOL) {
slab_rebal.done = 1;
}
slab_rebalance_signal = 2;
if (settings.verbose > 1) {
fprintf(stderr, "Started a slab rebalance\n");
}
pthread_mutex_unlock(&slabs_lock);
STATS_LOCK();
stats_state.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;
#ifdef DEBUG_SLAB_MOVER
memcpy(ITEM_key(new_it), "deadbeef", 8);
#endif
new_it = NULL;
slab_rebal.inline_reclaim++;
} else {
break;
}
}
return new_it;
}
/* CALLED WITH slabs_lock HELD */
/* detaches item/chunk from freelist. */
static void slab_rebalance_cut_free(slabclass_t *s_cls, item *it) {
/* Ensure this was on the freelist and nothing else. */
assert(it->it_flags == ITEM_SLABBED);
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--;
}
enum move_status {
MOVE_PASS=0, MOVE_FROM_SLAB, MOVE_FROM_LRU, MOVE_BUSY, MOVE_LOCKED
};
#define SLAB_MOVE_MAX_LOOPS 1000
/* 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;
item_chunk *ch = NULL;
status = MOVE_PASS;
if (it->it_flags & ITEM_CHUNK) {
/* This chunk is a chained part of a larger item. */
ch = (item_chunk *) it;
/* Instead, we use the head chunk to find the item and effectively
* lock the entire structure. If a chunk has ITEM_CHUNK flag, its
* head cannot be slabbed, so the normal routine is safe. */
it = ch->head;
assert(it->it_flags & ITEM_CHUNKED);
}
/* 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) {
assert(ch == NULL);
slab_rebalance_cut_free(s_cls, it);
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 {
bool is_linked = (it->it_flags & ITEM_LINKED);
refcount = refcount_incr(it);
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 (is_linked) {
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 (refcount > 2 && is_linked) {
// TODO: Mark items for delete/rescue and process
// outside of the main loop.
if (slab_rebal.busy_loops > SLAB_MOVE_MAX_LOOPS) {
slab_rebal.busy_deletes++;
// Only safe to hold slabs lock because refcount
// can't drop to 0 until we release item lock.
STORAGE_delete(storage, it);
pthread_mutex_unlock(&slabs_lock);
do_item_unlink(it, hv);
pthread_mutex_lock(&slabs_lock);
}
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);
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);
#ifdef EXTSTORE
if (it->it_flags & ITEM_HDR) {
ntotal = (ntotal - it->nbytes) + sizeof(item_hdr);
}
#endif
/* REQUIRES slabs_lock: CHECK FOR cls->sl_curr > 0 */
if (ch == NULL && (it->it_flags & ITEM_CHUNKED)) {
/* Chunked should be identical to non-chunked, except we need
* to swap out ntotal for the head-chunk-total. */
ntotal = s_cls->size;
}
if ((it->exptime != 0 && it->exptime < current_time)
|| item_is_flushed(it)) {
/* Expired, don't save. */
save_item = 0;
} else if (ch == NULL &&
(new_it = slab_rebalance_alloc(ntotal, slab_rebal.s_clsid)) == NULL) {
/* Not a chunk of an item, and nomem. */
save_item = 0;
slab_rebal.evictions_nomem++;
} else if (ch != NULL &&
(new_it = slab_rebalance_alloc(s_cls->size, slab_rebal.s_clsid)) == NULL) {
/* Is a chunk of an item, and nomem. */
save_item = 0;
slab_rebal.evictions_nomem++;
} else {
/* Was whatever it was, and we have memory for it. */
save_item = 1;
}
pthread_mutex_unlock(&slabs_lock);
unsigned int requested_adjust = 0;
if (save_item) {
if (ch == NULL) {
assert((new_it->it_flags & ITEM_CHUNKED) == 0);
/* 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);
/* Need to walk the chunks and repoint head */
if (new_it->it_flags & ITEM_CHUNKED) {
item_chunk *fch = (item_chunk *) ITEM_data(new_it);
fch->next->prev = fch;
while (fch) {
fch->head = new_it;
fch = fch->next;
}
}
it->refcount = 0;
it->it_flags = ITEM_SLABBED|ITEM_FETCHED;
#ifdef DEBUG_SLAB_MOVER
memcpy(ITEM_key(it), "deadbeef", 8);
#endif
slab_rebal.rescues++;
requested_adjust = ntotal;
} else {
item_chunk *nch = (item_chunk *) new_it;
/* Chunks always have head chunk (the main it) */
ch->prev->next = nch;
if (ch->next)
ch->next->prev = nch;
memcpy(nch, ch, ch->used + sizeof(item_chunk));
ch->refcount = 0;
ch->it_flags = ITEM_SLABBED|ITEM_FETCHED;
slab_rebal.chunk_rescues++;
#ifdef DEBUG_SLAB_MOVER
memcpy(ITEM_key((item *)ch), "deadbeef", 8);
#endif
refcount_decr(it);
requested_adjust = s_cls->size;
}
} else {
/* restore ntotal in case we tried saving a head chunk. */
ntotal = ITEM_ntotal(it);
STORAGE_delete(storage, it);
do_item_unlink(it, hv);
slabs_free(it, ntotal, slab_rebal.s_clsid);
/* Swing around again later to remove it from the freelist. */
slab_rebal.busy_items++;
was_busy++;
}
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 -= requested_adjust;
break;
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;
slab_rebal.busy_loops++;
} 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;
uint32_t chunk_rescues;
uint32_t busy_deletes;
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.slab_page_size);
split_slab_page_into_freelist(slab_rebal.slab_start,
slab_rebal.d_clsid);
} else if (slab_rebal.d_clsid == SLAB_GLOBAL_PAGE_POOL) {
/* mem_malloc'ed might be higher than mem_limit. */
mem_limit_reached = false;
memory_release();
}
slab_rebal.busy_loops = 0;
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;
chunk_rescues = slab_rebal.chunk_rescues;
busy_deletes = slab_rebal.busy_deletes;
slab_rebal.evictions_nomem = 0;
slab_rebal.inline_reclaim = 0;
slab_rebal.rescues = 0;
slab_rebal.chunk_rescues = 0;
slab_rebal.busy_deletes = 0;
slab_rebalance_signal = 0;
pthread_mutex_unlock(&slabs_lock);
STATS_LOCK();
stats.slabs_moved++;
stats.slab_reassign_rescues += rescues;
stats.slab_reassign_evictions_nomem += evictions_nomem;
stats.slab_reassign_inline_reclaim += inline_reclaim;
stats.slab_reassign_chunk_rescues += chunk_rescues;
stats.slab_reassign_busy_deletes += busy_deletes;
stats_state.slab_reassign_running = false;
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 specificity 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(1000);
}
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) {
bool nospare = false;
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 < SLAB_GLOBAL_PAGE_POOL || src > power_largest ||
dst < SLAB_GLOBAL_PAGE_POOL || dst > power_largest)
return REASSIGN_BADCLASS;
pthread_mutex_lock(&slabs_lock);
if (slabclass[src].slabs < 2)
nospare = true;
pthread_mutex_unlock(&slabs_lock);
if (nospare)
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 initialize 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.5.6/m4/ 0000775 0001750 0001750 00000000000 13245330107 010752 5 0000000 0000000 memcached-1.5.6/m4/c99-backport.m4 0000644 0001750 0001750 00000012104 11446413300 013335 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.5.6/crawler.h 0000664 0001750 0001750 00000002355 13242642567 012204 0000000 0000000 #ifndef CRAWLER_H
#define CRAWLER_H
#define LRU_CRAWLER_CAP_REMAINING -1
typedef struct {
uint64_t histo[61];
uint64_t ttl_hourplus;
uint64_t noexp;
uint64_t reclaimed;
uint64_t seen;
rel_time_t start_time;
rel_time_t end_time;
bool run_complete;
} crawlerstats_t;
struct crawler_expired_data {
pthread_mutex_t lock;
crawlerstats_t crawlerstats[POWER_LARGEST];
/* redundant with crawlerstats_t so we can get overall start/stop/done */
rel_time_t start_time;
rel_time_t end_time;
bool crawl_complete;
bool is_external; /* whether this was an alloc local or remote to the module. */
};
enum crawler_result_type {
CRAWLER_OK=0, CRAWLER_RUNNING, CRAWLER_BADCLASS, CRAWLER_NOTSTARTED, CRAWLER_ERROR
};
int start_item_crawler_thread(void);
int stop_item_crawler_thread(void);
int init_lru_crawler(void *arg);
enum crawler_result_type lru_crawler_crawl(char *slabs, enum crawler_run_type,
void *c, const int sfd, unsigned int remaining);
int lru_crawler_start(uint8_t *ids, uint32_t remaining,
const enum crawler_run_type type, void *data,
void *c, const int sfd);
void lru_crawler_pause(void);
void lru_crawler_resume(void);
#endif
memcached-1.5.6/NEWS 0000664 0001750 0001750 00000000026 13025643161 011052 0000000 0000000 http://memcached.org/
memcached-1.5.6/storage.c 0000664 0001750 0001750 00000040517 13240770206 012174 0000000 0000000 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
#ifdef EXTSTORE
#include "storage.h"
#include
#include
#include
#define PAGE_BUCKET_DEFAULT 0
#define PAGE_BUCKET_COMPACT 1
#define PAGE_BUCKET_CHUNKED 2
#define PAGE_BUCKET_LOWTTL 3
int lru_maintainer_store(void *storage, const int clsid) {
//int i;
int did_moves = 0;
int item_age = settings.ext_item_age;
bool mem_limit_reached = false;
unsigned int chunks_free;
struct lru_pull_tail_return it_info;
// FIXME: need to directly ask the slabber how big a class is
if (slabs_clsid(settings.ext_item_size) > clsid)
return 0;
chunks_free = slabs_available_chunks(clsid, &mem_limit_reached,
NULL, NULL);
// if we are low on chunks and no spare, push out early.
if (chunks_free < settings.ext_free_memchunks[clsid] && mem_limit_reached)
item_age = 0;
it_info.it = NULL;
lru_pull_tail(clsid, COLD_LRU, 0, LRU_PULL_RETURN_ITEM, 0, &it_info);
/* Item is locked, and we have a reference to it. */
if (it_info.it == NULL) {
return did_moves;
}
obj_io io;
item *it = it_info.it;
/* First, storage for the header object */
size_t orig_ntotal = ITEM_ntotal(it);
uint32_t flags;
if ((it->it_flags & ITEM_HDR) == 0 &&
(item_age == 0 || current_time - it->time > item_age)) {
// FIXME: flag conversion again
if (settings.inline_ascii_response) {
flags = (uint32_t) strtoul(ITEM_suffix(it), (char **) NULL, 10);
} else if (it->nsuffix > 0) {
flags = *((uint32_t *)ITEM_suffix(it));
} else {
flags = 0;
}
item *hdr_it = do_item_alloc(ITEM_key(it), it->nkey, flags, it->exptime, sizeof(item_hdr));
/* Run the storage write understanding the start of the item is dirty.
* We will fill it (time/exptime/etc) from the header item on read.
*/
if (hdr_it != NULL) {
int bucket = (it->it_flags & ITEM_CHUNKED) ?
PAGE_BUCKET_CHUNKED : PAGE_BUCKET_DEFAULT;
// Compres soon to expire items into similar pages.
if (it->exptime - current_time < settings.ext_low_ttl) {
bucket = PAGE_BUCKET_LOWTTL;
}
hdr_it->it_flags |= ITEM_HDR;
io.len = orig_ntotal;
io.mode = OBJ_IO_WRITE;
// NOTE: when the item is read back in, the slab mover
// may see it. Important to have refcount>=2 or ~ITEM_LINKED
assert(it->refcount >= 2);
if (extstore_write_request(storage, bucket, &io) == 0) {
// cuddle the hash value into the time field so we don't have
// to recalculate it.
item *buf_it = (item *) io.buf;
buf_it->time = it_info.hv;
// copy from past the headers + time headers.
// TODO: should be in items.c
if (it->it_flags & ITEM_CHUNKED) {
// Need to loop through the item and copy
item_chunk *sch = (item_chunk *) ITEM_data(it);
int remain = orig_ntotal;
int copied = 0;
// copy original header
int hdrtotal = ITEM_ntotal(it) - it->nbytes;
memcpy((char *)io.buf+32, (char *)it+32, hdrtotal - 32);
copied = hdrtotal;
// copy data in like it were one large object.
while (sch && remain) {
assert(remain >= sch->used);
memcpy((char *)io.buf+copied, sch->data, sch->used);
// FIXME: use one variable?
remain -= sch->used;
copied += sch->used;
sch = sch->next;
}
} else {
memcpy((char *)io.buf+32, (char *)it+32, io.len-32);
}
// crc what we copied so we can do it sequentially.
buf_it->it_flags &= ~ITEM_LINKED;
buf_it->exptime = crc32c(0, (char*)io.buf+32, orig_ntotal-32);
extstore_write(storage, &io);
item_hdr *hdr = (item_hdr *) ITEM_data(hdr_it);
hdr->page_version = io.page_version;
hdr->page_id = io.page_id;
hdr->offset = io.offset;
// overload nbytes for the header it
hdr_it->nbytes = it->nbytes;
/* success! Now we need to fill relevant data into the new
* header and replace. Most of this requires the item lock
*/
/* CAS gets set while linking. Copy post-replace */
item_replace(it, hdr_it, it_info.hv);
ITEM_set_cas(hdr_it, ITEM_get_cas(it));
do_item_remove(hdr_it);
did_moves = 1;
LOGGER_LOG(NULL, LOG_EVICTIONS, LOGGER_EXTSTORE_WRITE, it, bucket);
} else {
/* Failed to write for some reason, can't continue. */
slabs_free(hdr_it, ITEM_ntotal(hdr_it), ITEM_clsid(hdr_it));
}
}
}
do_item_remove(it);
item_unlock(it_info.hv);
return did_moves;
}
/* Fetch stats from the external storage system and decide to compact.
* If we're more than half full, start skewing how aggressively to run
* compaction, up to a desired target when all pages are full.
*/
static int storage_compact_check(void *storage, logger *l,
uint32_t *page_id, uint64_t *page_version,
uint64_t *page_size, bool *drop_unread) {
struct extstore_stats st;
int x;
double rate;
uint64_t frag_limit;
uint64_t low_version = ULLONG_MAX;
uint64_t lowest_version = ULLONG_MAX;
unsigned int low_page = 0;
unsigned int lowest_page = 0;
extstore_get_stats(storage, &st);
if (st.pages_used == 0)
return 0;
// lets pick a target "wasted" value and slew.
if (st.pages_free > settings.ext_compact_under)
return 0;
*drop_unread = false;
// the number of free pages reduces the configured frag limit
// this allows us to defrag early if pages are very empty.
rate = 1.0 - ((double)st.pages_free / st.page_count);
rate *= settings.ext_max_frag;
frag_limit = st.page_size * rate;
LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_FRAGINFO,
NULL, rate, frag_limit);
st.page_data = calloc(st.page_count, sizeof(struct extstore_page_data));
extstore_get_page_data(storage, &st);
// find oldest page by version that violates the constraint
for (x = 0; x < st.page_count; x++) {
if (st.page_data[x].version == 0 ||
st.page_data[x].bucket == PAGE_BUCKET_LOWTTL)
continue;
if (st.page_data[x].version < lowest_version) {
lowest_page = x;
lowest_version = st.page_data[x].version;
}
if (st.page_data[x].bytes_used < frag_limit) {
if (st.page_data[x].version < low_version) {
low_page = x;
low_version = st.page_data[x].version;
}
}
}
*page_size = st.page_size;
free(st.page_data);
// we have a page + version to attempt to reclaim.
if (low_version != ULLONG_MAX) {
*page_id = low_page;
*page_version = low_version;
return 1;
} else if (lowest_version != ULLONG_MAX && settings.ext_drop_unread
&& st.pages_free <= settings.ext_drop_under) {
// nothing matched the frag rate barrier, so pick the absolute oldest
// version if we're configured to drop items.
*page_id = lowest_page;
*page_version = lowest_version;
*drop_unread = true;
return 1;
}
return 0;
}
static pthread_t storage_compact_tid;
static pthread_mutex_t storage_compact_plock;
#define MIN_STORAGE_COMPACT_SLEEP 10000
#define MAX_STORAGE_COMPACT_SLEEP 2000000
struct storage_compact_wrap {
obj_io io;
pthread_mutex_t lock; // gates the bools.
bool done;
bool submitted;
bool miss; // version flipped out from under us
};
static void storage_compact_readback(void *storage, logger *l,
bool drop_unread, char *readback_buf,
uint32_t page_id, uint64_t page_version, uint64_t read_size) {
uint64_t offset = 0;
unsigned int rescues = 0;
unsigned int lost = 0;
unsigned int skipped = 0;
while (offset < read_size) {
item *hdr_it = NULL;
item_hdr *hdr = NULL;
item *it = (item *)(readback_buf+offset);
unsigned int ntotal;
// probably zeroed out junk at the end of the wbuf
if (it->nkey == 0) {
break;
}
ntotal = ITEM_ntotal(it);
uint32_t hv = (uint32_t)it->time;
item_lock(hv);
// We don't have a conn and don't need to do most of do_item_get
hdr_it = assoc_find(ITEM_key(it), it->nkey, hv);
if (hdr_it != NULL) {
bool do_write = false;
refcount_incr(hdr_it);
// Check validity but don't bother removing it.
if ((hdr_it->it_flags & ITEM_HDR) && !item_is_flushed(hdr_it) &&
(hdr_it->exptime == 0 || hdr_it->exptime > current_time)) {
hdr = (item_hdr *)ITEM_data(hdr_it);
if (hdr->page_id == page_id && hdr->page_version == page_version) {
// Item header is still completely valid.
extstore_delete(storage, page_id, page_version, 1, ntotal);
// drop inactive items.
if (drop_unread && GET_LRU(hdr_it->slabs_clsid) == COLD_LRU) {
do_write = false;
skipped++;
} else {
do_write = true;
}
}
}
if (do_write) {
bool do_update = false;
int tries;
obj_io io;
io.len = ntotal;
io.mode = OBJ_IO_WRITE;
for (tries = 10; tries > 0; tries--) {
if (extstore_write_request(storage, PAGE_BUCKET_COMPACT, &io) == 0) {
memcpy(io.buf, it, io.len);
extstore_write(storage, &io);
do_update = true;
break;
} else {
usleep(1000);
}
}
if (do_update) {
if (it->refcount == 2) {
hdr->page_version = io.page_version;
hdr->page_id = io.page_id;
hdr->offset = io.offset;
rescues++;
} else {
lost++;
// TODO: re-alloc and replace header.
}
} else {
lost++;
}
}
do_item_remove(hdr_it);
}
item_unlock(hv);
offset += ntotal;
if (read_size - offset < sizeof(struct _stritem))
break;
}
STATS_LOCK();
stats.extstore_compact_lost += lost;
stats.extstore_compact_rescues += rescues;
stats.extstore_compact_skipped += skipped;
STATS_UNLOCK();
LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_READ_END,
NULL, page_id, offset, rescues, lost, skipped);
}
static void _storage_compact_cb(void *e, obj_io *io, int ret) {
struct storage_compact_wrap *wrap = (struct storage_compact_wrap *)io->data;
assert(wrap->submitted == true);
pthread_mutex_lock(&wrap->lock);
if (ret < 1) {
wrap->miss = true;
}
wrap->done = true;
pthread_mutex_unlock(&wrap->lock);
}
// TODO: hoist the storage bits from lru_maintainer_thread in here.
// would be nice if they could avoid hammering the same locks though?
// I guess it's only COLD. that's probably fine.
static void *storage_compact_thread(void *arg) {
void *storage = arg;
useconds_t to_sleep = MAX_STORAGE_COMPACT_SLEEP;
bool compacting = false;
uint64_t page_version = 0;
uint64_t page_size = 0;
uint64_t page_offset = 0;
uint32_t page_id = 0;
bool drop_unread = false;
char *readback_buf = NULL;
struct storage_compact_wrap wrap;
logger *l = logger_create();
if (l == NULL) {
fprintf(stderr, "Failed to allocate logger for storage compaction thread\n");
abort();
}
readback_buf = malloc(settings.ext_wbuf_size);
if (readback_buf == NULL) {
fprintf(stderr, "Failed to allocate readback buffer for storage compaction thread\n");
abort();
}
pthread_mutex_init(&wrap.lock, NULL);
wrap.done = false;
wrap.submitted = false;
wrap.io.data = &wrap;
wrap.io.buf = (void *)readback_buf;
wrap.io.len = settings.ext_wbuf_size;
wrap.io.mode = OBJ_IO_READ;
wrap.io.cb = _storage_compact_cb;
pthread_mutex_lock(&storage_compact_plock);
while (1) {
pthread_mutex_unlock(&storage_compact_plock);
if (to_sleep) {
extstore_run_maint(storage);
usleep(to_sleep);
}
pthread_mutex_lock(&storage_compact_plock);
if (!compacting && storage_compact_check(storage, l,
&page_id, &page_version, &page_size, &drop_unread)) {
page_offset = 0;
compacting = true;
LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_START,
NULL, page_id, page_version);
}
if (compacting) {
pthread_mutex_lock(&wrap.lock);
if (page_offset < page_size && !wrap.done && !wrap.submitted) {
wrap.io.page_version = page_version;
wrap.io.page_id = page_id;
wrap.io.offset = page_offset;
// FIXME: should be smarter about io->next (unlink at use?)
wrap.io.next = NULL;
wrap.submitted = true;
wrap.miss = false;
extstore_submit(storage, &wrap.io);
} else if (wrap.miss) {
LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_ABORT,
NULL, page_id);
wrap.done = false;
wrap.submitted = false;
compacting = false;
} else if (wrap.submitted && wrap.done) {
LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_READ_START,
NULL, page_id, page_offset);
storage_compact_readback(storage, l, drop_unread,
readback_buf, page_id, page_version, settings.ext_wbuf_size);
page_offset += settings.ext_wbuf_size;
wrap.done = false;
wrap.submitted = false;
} else if (page_offset >= page_size) {
compacting = false;
wrap.done = false;
wrap.submitted = false;
extstore_close_page(storage, page_id, page_version);
LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_END,
NULL, page_id);
}
pthread_mutex_unlock(&wrap.lock);
if (to_sleep > MIN_STORAGE_COMPACT_SLEEP)
to_sleep /= 2;
} else {
if (to_sleep < MAX_STORAGE_COMPACT_SLEEP)
to_sleep += MIN_STORAGE_COMPACT_SLEEP;
}
}
free(readback_buf);
return NULL;
}
// TODO
// logger needs logger_destroy() to exist/work before this is safe.
/*int stop_storage_compact_thread(void) {
int ret;
pthread_mutex_lock(&lru_maintainer_lock);
do_run_lru_maintainer_thread = 0;
pthread_mutex_unlock(&lru_maintainer_lock);
if ((ret = pthread_join(lru_maintainer_tid, NULL)) != 0) {
fprintf(stderr, "Failed to stop LRU maintainer thread: %s\n", strerror(ret));
return -1;
}
settings.lru_maintainer_thread = false;
return 0;
}*/
void storage_compact_pause(void) {
pthread_mutex_lock(&storage_compact_plock);
}
void storage_compact_resume(void) {
pthread_mutex_unlock(&storage_compact_plock);
}
int start_storage_compact_thread(void *arg) {
int ret;
pthread_mutex_init(&storage_compact_plock, NULL);
if ((ret = pthread_create(&storage_compact_tid, NULL,
storage_compact_thread, arg)) != 0) {
fprintf(stderr, "Can't create storage_compact thread: %s\n",
strerror(ret));
return -1;
}
return 0;
}
#endif
memcached-1.5.6/COPYING 0000644 0001750 0001750 00000002737 11246331452 011417 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.5.6/stats.h 0000644 0001750 0001750 00000000526 12606105233 011662 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.5.6/sasl_defs.h 0000644 0001750 0001750 00000001265 12606105233 012470 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.5.6/openbsd_priv.c 0000664 0001750 0001750 00000001525 13240770206 013216 0000000 0000000 #include
#include
#include
#include
#include
#include "memcached.h"
/*
* this section of code will drop all (OpenBSD) privileges including
* those normally granted to all userland process (basic privileges). The
* effect of this is that after running this code, the process will not able
* to fork(), exec(), etc. See pledge(2) for more information.
*/
void drop_privileges() {
extern char *__progname;
if (settings.socketpath != NULL) {
if (pledge("stdio unix", NULL) == -1) {
fprintf(stderr, "%s: pledge: %s\n", __progname, strerror(errno));
exit(EXIT_FAILURE);
}
} else {
if (pledge("stdio inet", NULL) == -1) {
fprintf(stderr, "%s: pledge: %s\n", __progname, strerror(errno));
exit(EXIT_FAILURE);
}
}
}
memcached-1.5.6/itoa_ljust.h 0000664 0001750 0001750 00000001466 13150607001 012702 0000000 0000000 #ifndef ITOA_LJUST_H
#define ITOA_LJUST_H
//=== itoa_ljust.h - Fast integer to ascii conversion
//
// Fast and simple integer to ASCII conversion:
//
// - 32 and 64-bit integers
// - signed and unsigned
// - user supplied buffer must be large enough for all decimal digits
// in value plus minus sign if negative
// - left-justified
// - NUL terminated
// - return value is pointer to NUL terminator
//
// Copyright (c) 2016 Arturo Martin-de-Nicolas
// arturomdn@gmail.com
// https://github.com/amdn/itoa_ljust/
//===----------------------------------------------------------------------===//
#include
char* itoa_u32(uint32_t u, char* buffer);
char* itoa_32( int32_t i, char* buffer);
char* itoa_u64(uint64_t u, char* buffer);
char* itoa_64( int64_t i, char* buffer);
#endif // ITOA_LJUST_H
memcached-1.5.6/AUTHORS 0000644 0001750 0001750 00000000105 12250557060 011420 0000000 0000000 Anatoly Vorobey
Brad Fitzpatrick
memcached-1.5.6/sasl_defs.c 0000664 0001750 0001750 00000011743 13025643161 012472 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.5.6/slab_automove_extstore.c 0000664 0001750 0001750 00000025014 13242642567 015332 0000000 0000000 /* Copyright 2017 Facebook.
*
* Use and distribution licensed under the BSD license. See
* the LICENSE file for full text.
*/
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
#include "slab_automove_extstore.h"
#include
#include
#define MIN_PAGES_FOR_SOURCE 2
#define MIN_PAGES_FOR_RECLAIM 2.5
#define MIN_PAGES_FREE 1.5
#define MEMCHECK_PERIOD 60
struct window_data {
uint64_t age;
uint64_t dirty;
uint64_t evicted;
unsigned int excess_free;
unsigned int relaxed;
};
struct window_global {
uint32_t pool_low;
uint32_t pool_high;
};
typedef struct {
struct window_data *window_data;
struct window_global *window_global;
struct settings *settings;
uint32_t window_size;
uint32_t window_cur;
uint32_t item_size;
rel_time_t last_memcheck_run;
double max_age_ratio;
double free_ratio;
bool pool_filled_once;
unsigned int free_mem[MAX_NUMBER_OF_SLAB_CLASSES];
item_stats_automove iam_before[MAX_NUMBER_OF_SLAB_CLASSES];
item_stats_automove iam_after[MAX_NUMBER_OF_SLAB_CLASSES];
slab_stats_automove sam_before[MAX_NUMBER_OF_SLAB_CLASSES];
slab_stats_automove sam_after[MAX_NUMBER_OF_SLAB_CLASSES];
} slab_automove;
void *slab_automove_extstore_init(struct settings *settings) {
uint32_t window_size = settings->slab_automove_window;
double max_age_ratio = settings->slab_automove_ratio;
slab_automove *a = calloc(1, sizeof(slab_automove));
if (a == NULL)
return NULL;
a->window_data = calloc(window_size * MAX_NUMBER_OF_SLAB_CLASSES, sizeof(struct window_data));
a->window_global = calloc(window_size, sizeof(struct window_global));
a->window_size = window_size;
a->max_age_ratio = max_age_ratio;
a->free_ratio = settings->slab_automove_freeratio;
a->item_size = settings->ext_item_size;
a->last_memcheck_run = 0;
a->settings = settings;
a->pool_filled_once = false;
if (a->window_data == NULL || a->window_global == NULL) {
if (a->window_data)
free(a->window_data);
if (a->window_global)
free(a->window_global);
free(a);
return NULL;
}
// do a dry run to fill the before structs
fill_item_stats_automove(a->iam_before);
fill_slab_stats_automove(a->sam_before);
return (void *)a;
}
void slab_automove_extstore_free(void *arg) {
slab_automove *a = (slab_automove *)arg;
free(a->window_data);
free(a);
}
static void window_sum(struct window_data *wd, struct window_data *w,
uint32_t size) {
for (int x = 0; x < size; x++) {
struct window_data *d = &wd[x];
w->age += d->age;
w->dirty += d->dirty;
w->evicted += d->evicted;
w->excess_free += d->excess_free;
w->relaxed += d->relaxed;
}
}
/* This could potentially merge with above */
static void window_global_sum(struct window_global *wg,
struct window_global *w, uint32_t size) {
for (int x = 0; x < size; x++) {
struct window_global *d = &wg[x];
w->pool_high += d->pool_high;
w->pool_low += d->pool_low;
}
}
static void global_pool_check(slab_automove *a) {
bool mem_limit_reached;
uint32_t free = a->free_mem[0];
struct window_global *wg = &a->window_global[a->window_cur % a->window_size];
unsigned int count = global_page_pool_size(&mem_limit_reached);
memset(wg, 0, sizeof(struct window_global));
if (!mem_limit_reached)
return;
if (count < free / 2) {
wg->pool_low = 1;
a->pool_filled_once = true;
} else if (count > free) {
wg->pool_high = 1;
} else {
a->pool_filled_once = true;
}
}
/* A percentage of memory is configured to be held "free" as buffers for the
* external storage system.
* % of global memory is desired in the global page pool
* each slab class has a % of free chunks desired based on how much memory is
* currently in the class. This allows time for extstore to flush data when
* spikes or waves of set data arrive.
* The global page pool reserve acts as a secondary buffer for any slab class,
* which helps absorb shifts in which class is active.
*/
static void memcheck(slab_automove *a) {
unsigned int total_pages = 0;
if (current_time < a->last_memcheck_run + MEMCHECK_PERIOD)
return;
a->last_memcheck_run = current_time;
for (int n = 1; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
slab_stats_automove *sam = &a->sam_after[n];
total_pages += sam->total_pages;
unsigned int hold_free = (sam->total_pages * sam->chunks_per_page)
* a->free_ratio;
if (sam->chunks_per_page * MIN_PAGES_FREE > hold_free)
hold_free = sam->chunks_per_page * MIN_PAGES_FREE;
a->free_mem[n] = hold_free;
if (a->settings->ext_free_memchunks[n] != hold_free && a->pool_filled_once) {
a->settings->ext_free_memchunks[n] = hold_free;
}
}
// remember to add what remains in global pool.
total_pages += a->sam_after[0].total_pages;
a->free_mem[0] = total_pages * a->free_ratio;
}
static struct window_data *get_window_data(slab_automove *a, int class) {
int w_offset = class * a->window_size;
return &a->window_data[w_offset + (a->window_cur % a->window_size)];
}
void slab_automove_extstore_run(void *arg, int *src, int *dst) {
slab_automove *a = (slab_automove *)arg;
int n;
struct window_data w_sum;
int oldest = -1;
uint64_t oldest_age = 0;
int youngest = -1;
uint64_t youngest_age = ~0;
bool too_free = false;
*src = -1;
*dst = -1;
global_pool_check(a);
struct window_global wg_sum;
memset(&wg_sum, 0, sizeof(struct window_global));
window_global_sum(a->window_global, &wg_sum, a->window_size);
// fill after structs
fill_item_stats_automove(a->iam_after);
fill_slab_stats_automove(a->sam_after);
a->window_cur++;
memcheck(a);
// iterate slabs
for (n = POWER_SMALLEST; n < MAX_NUMBER_OF_SLAB_CLASSES; n++) {
bool small_slab = a->sam_before[n].chunk_size < a->item_size
? true : false;
bool free_enough = false;
struct window_data *wd = get_window_data(a, n);
// summarize the window-up-to-now.
memset(&w_sum, 0, sizeof(struct window_data));
int w_offset = n * a->window_size;
window_sum(&a->window_data[w_offset], &w_sum, a->window_size);
memset(wd, 0, sizeof(struct window_data));
// if page delta, oom, or evicted delta, mark window dirty
// classes marked dirty cannot donate memory back to global pool.
if (a->iam_after[n].evicted - a->iam_before[n].evicted > 0 ||
a->iam_after[n].outofmemory - a->iam_before[n].outofmemory > 0) {
wd->evicted = 1;
wd->dirty = 1;
}
if (a->sam_after[n].total_pages - a->sam_before[n].total_pages > 0) {
wd->dirty = 1;
}
// Mark excess free if we're over the free mem limit for too long.
// "free_enough" means it is either wobbling, recently received a new
// page of memory, or the crawler is freeing memory.
if (a->sam_after[n].free_chunks > a->free_mem[n]) {
free_enough = true;
}
// double the free requirements means we may have memory we can
// reclaim to global, if it stays this way for the whole window.
if (a->sam_after[n].free_chunks > (a->free_mem[n] * 2) && a->free_mem[n] > 0) {
wd->excess_free = 1;
}
// set age into window
wd->age = a->iam_after[n].age;
// grab age as average of window total
uint64_t age = w_sum.age / a->window_size;
// if > N free chunks and not dirty, reclaim memory
// small slab classes aren't age balanced and rely more on global
// pool. reclaim them more aggressively.
if (a->sam_after[n].free_chunks > a->sam_after[n].chunks_per_page * MIN_PAGES_FOR_RECLAIM
&& w_sum.dirty == 0) {
if (small_slab) {
*src = n;
*dst = 0;
too_free = true;
} else if (!small_slab && w_sum.excess_free >= a->window_size) {
// If large slab and free chunks haven't decreased for a full
// window, reclaim pages.
*src = n;
*dst = 0;
too_free = true;
}
}
if (!small_slab) {
// if oldest and have enough pages, is oldest
if (age > oldest_age
&& a->sam_after[n].total_pages > MIN_PAGES_FOR_SOURCE) {
oldest = n;
oldest_age = age;
}
// don't count as youngest if it hasn't been using new chunks.
// (if it was relaxed recently, and is currently "free enough")
if (age < youngest_age && a->sam_after[n].total_pages != 0
&& w_sum.excess_free < a->window_size
&& !(w_sum.relaxed && free_enough)) {
youngest = n;
youngest_age = age;
}
}
}
memcpy(a->iam_before, a->iam_after,
sizeof(item_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES);
memcpy(a->sam_before, a->sam_after,
sizeof(slab_stats_automove) * MAX_NUMBER_OF_SLAB_CLASSES);
// only make decisions if window has filled once.
if (a->window_cur < a->window_size)
return;
if (wg_sum.pool_high >= a->window_size && !wg_sum.pool_low && youngest != -1) {
if (a->sam_after[youngest].free_chunks <= a->free_mem[youngest]) {
*src = 0;
*dst = youngest;
}
struct window_data *wd = get_window_data(a, youngest);
// "relaxing" here and below allows us to skip classes which will
// never grow or are growing slowly, more quickly finding other
// classes which violate the age ratio.
wd->relaxed = 1;
} else if (!too_free && wg_sum.pool_low && oldest != -1) {
*src = oldest;
*dst = 0;
} else if (!too_free && youngest != -1 && oldest != -1 && youngest != oldest) {
// if we have a youngest and oldest, and oldest is outside the ratio.
if (youngest_age < ((double)oldest_age * a->max_age_ratio)) {
struct window_data *wd = get_window_data(a, youngest);
wd->relaxed = 1;
// only actually assign more memory if it's absorbed what it has
if (a->sam_after[youngest].free_chunks <= a->free_mem[youngest]) {
*src = 0;
*dst = youngest;
}
}
}
return;
}
memcached-1.5.6/config.sub 0000755 0001750 0001750 00000106460 12612517377 012357 0000000 0000000 #! /bin/sh
# Configuration validation subroutine script.
# Copyright 1992-2015 Free Software Foundation, Inc.
timestamp='2015-08-20'
# 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 to .
#
# 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-2015 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* | netbsd*-eabi* | \
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 \
| ba \
| be32 | be64 \
| bfin \
| c4x | c8051 | clipper \
| d10v | d30v | dlx | dsp16xx \
| e2k | epiphany \
| fido | fr30 | frv | ft32 \
| 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[234]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 \
| visium \
| 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
;;
leon|leon[3-9])
basic_machine=sparc-$basic_machine
;;
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-* \
| ba-* \
| be32-* | be64-* \
| bfin-* | bs2000-* \
| c[123]* | c30-* | [cjt]90-* | c4x-* \
| c8051-* | clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
| e2k-* | 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-* \
| riscv32-* | riscv64-* \
| 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-* \
| visium-* \
| 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
;;
asmjs)
basic_machine=asmjs-unknown
;;
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
;;
leon-*|leon[3-9]-*)
basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
;;
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* | -cloudabi* | -sortix* \
| -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.5.6/LICENSE.bipbuffer 0000664 0001750 0001750 00000002700 13115057711 013324 0000000 0000000 Copyright (c) 2011, Willem-Hendrik Thiart
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.
* The names of its contributors may not 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 WILLEM-HENDRIK THIART 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.5.6/compile 0000755 0001750 0001750 00000016245 12676613141 011747 0000000 0000000 #! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
# Copyright (C) 1999-2014 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.5.6/jenkins_hash.h 0000664 0001750 0001750 00000000325 13025643161 013172 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.5.6/protocol_binary.h 0000664 0001750 0001750 00000040215 13115057711 013735 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.5.6/extstore.h 0000664 0001750 0001750 00000007773 13240770206 012421 0000000 0000000 #ifndef EXTSTORE_H
#define EXTSTORE_H
/* A safe-to-read dataset for determining compaction.
* id is the array index.
*/
struct extstore_page_data {
uint64_t version;
uint64_t bytes_used;
unsigned int bucket;
};
/* Pages can have objects deleted from them at any time. This creates holes
* that can't be reused until the page is either evicted or all objects are
* deleted.
* bytes_fragmented is the total bytes for all of these holes.
* It is the size of all used pages minus each page's bytes_used value.
*/
struct extstore_stats {
uint64_t page_allocs;
uint64_t page_count; /* total page count */
uint64_t page_evictions;
uint64_t page_reclaims;
uint64_t page_size; /* size in bytes per page (supplied by caller) */
uint64_t pages_free; /* currently unallocated/unused pages */
uint64_t pages_used;
uint64_t objects_evicted;
uint64_t objects_read;
uint64_t objects_written;
uint64_t objects_used; /* total number of objects stored */
uint64_t bytes_evicted;
uint64_t bytes_written;
uint64_t bytes_read; /* wbuf - read -> bytes read from storage */
uint64_t bytes_used; /* total number of bytes stored */
uint64_t bytes_fragmented; /* see above comment */
struct extstore_page_data *page_data;
};
// TODO: Temporary configuration structure. A "real" library should have an
// extstore_set(enum, void *ptr) which hides the implementation.
// this is plenty for quick development.
struct extstore_conf {
unsigned int page_size; // ideally 64-256M in size
unsigned int page_count;
unsigned int page_buckets; // number of different writeable pages
unsigned int wbuf_size; // must divide cleanly into page_size
unsigned int wbuf_count; // this might get locked to "2 per active page"
unsigned int io_threadcount;
unsigned int io_depth; // with normal I/O, hits locks less. req'd for AIO
};
enum obj_io_mode {
OBJ_IO_READ = 0,
OBJ_IO_WRITE,
};
typedef struct _obj_io obj_io;
typedef void (*obj_io_cb)(void *e, obj_io *io, int ret);
/* An object for both reads and writes to the storage engine.
* Once an IO is submitted, ->next may be changed by the IO thread. It is not
* safe to further modify the IO stack until the entire request is completed.
*/
struct _obj_io {
void *data; /* user supplied data pointer */
struct _obj_io *next;
char *buf; /* buffer of data to read or write to */
struct iovec *iov; /* alternatively, use this iovec */
unsigned int iovcnt; /* number of IOV's */
unsigned int page_version; /* page version for read mode */
unsigned int len; /* for both modes */
unsigned int offset; /* for read mode */
unsigned short page_id; /* for read mode */
enum obj_io_mode mode;
/* callback pointers? */
obj_io_cb cb;
};
enum extstore_res {
EXTSTORE_INIT_BAD_WBUF_SIZE = 1,
EXTSTORE_INIT_NEED_MORE_WBUF,
EXTSTORE_INIT_NEED_MORE_BUCKETS,
EXTSTORE_INIT_PAGE_WBUF_ALIGNMENT,
EXTSTORE_INIT_OOM,
EXTSTORE_INIT_OPEN_FAIL,
EXTSTORE_INIT_THREAD_FAIL
};
const char *extstore_err(enum extstore_res res);
void *extstore_init(char *fn, struct extstore_conf *cf, enum extstore_res *res);
int extstore_write_request(void *ptr, unsigned int bucket, obj_io *io);
void extstore_write(void *ptr, obj_io *io);
int extstore_submit(void *ptr, obj_io *io);
/* count are the number of objects being removed, bytes are the original
* length of those objects. Bytes is optional but you can't track
* fragmentation without it.
*/
int extstore_check(void *ptr, unsigned int page_id, uint64_t page_version);
int extstore_delete(void *ptr, unsigned int page_id, uint64_t page_version, unsigned int count, unsigned int bytes);
void extstore_get_stats(void *ptr, struct extstore_stats *st);
/* add page data array to a stats structure.
* caller must allocate its stats.page_data memory first.
*/
void extstore_get_page_data(void *ptr, struct extstore_stats *st);
void extstore_run_maint(void *ptr);
void extstore_close_page(void *ptr, unsigned int page_id, uint64_t page_version);
#endif
memcached-1.5.6/murmur3_hash.c 0000664 0001750 0001750 00000005412 13115057711 013140 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.5.6/aclocal.m4 0000664 0001750 0001750 00000124216 13245330104 012215 0000000 0000000 # generated automatically by aclocal 1.15 -*- Autoconf -*-
# Copyright (C) 1996-2014 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-2014 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.15'
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.15], [],
[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.15])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-2014 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],
[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
# Expand $ac_aux_dir to an absolute path.
am_aux_dir=`cd "$ac_aux_dir" && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997-2014 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-2014 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-2014 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-2014 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 (and possibly the TAP driver). 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 The trailing newline in this macro's definition is deliberate, for
dnl backward compatibility and to allow trailing 'dnl'-style comments
dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
])
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-2014 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+set}" != 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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-2014 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.5.6/crawler.c 0000664 0001750 0001750 00000054272 13245327530 012175 0000000 0000000 /* Copyright 2016 Netflix.
*
* Use and distribution licensed under the BSD license. See
* the LICENSE file for full text.
*/
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#include "memcached.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LARGEST_ID POWER_LARGEST
typedef struct {
void *c; /* original connection structure. still with source thread attached. */
int sfd; /* client fd. */
bipbuf_t *buf; /* output buffer */
char *cbuf; /* current buffer */
} crawler_client_t;
typedef struct _crawler_module_t crawler_module_t;
typedef void (*crawler_eval_func)(crawler_module_t *cm, item *it, uint32_t hv, int slab_cls);
typedef int (*crawler_init_func)(crawler_module_t *cm, void *data); // TODO: init args?
typedef void (*crawler_deinit_func)(crawler_module_t *cm); // TODO: extra args?
typedef void (*crawler_doneclass_func)(crawler_module_t *cm, int slab_cls);
typedef void (*crawler_finalize_func)(crawler_module_t *cm);
typedef struct {
crawler_init_func init; /* run before crawl starts */
crawler_eval_func eval; /* runs on an item. */
crawler_doneclass_func doneclass; /* runs once per sub-crawler completion. */
crawler_finalize_func finalize; /* runs once when all sub-crawlers are done. */
bool needs_lock; /* whether or not we need the LRU lock held when eval is called */
bool needs_client; /* whether or not to grab onto the remote client */
} crawler_module_reg_t;
struct _crawler_module_t {
void *data; /* opaque data pointer */
crawler_client_t c;
crawler_module_reg_t *mod;
};
static int crawler_expired_init(crawler_module_t *cm, void *data);
static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls);
static void crawler_expired_finalize(crawler_module_t *cm);
static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);
crawler_module_reg_t crawler_expired_mod = {
.init = crawler_expired_init,
.eval = crawler_expired_eval,
.doneclass = crawler_expired_doneclass,
.finalize = crawler_expired_finalize,
.needs_lock = true,
.needs_client = false
};
static void crawler_metadump_eval(crawler_module_t *cm, item *search, uint32_t hv, int i);
static void crawler_metadump_finalize(crawler_module_t *cm);
crawler_module_reg_t crawler_metadump_mod = {
.init = NULL,
.eval = crawler_metadump_eval,
.doneclass = NULL,
.finalize = crawler_metadump_finalize,
.needs_lock = false,
.needs_client = true
};
crawler_module_reg_t *crawler_mod_regs[3] = {
&crawler_expired_mod,
&crawler_expired_mod,
&crawler_metadump_mod
};
static int lru_crawler_client_getbuf(crawler_client_t *c);
crawler_module_t active_crawler_mod;
enum crawler_run_type active_crawler_type;
static crawler crawlers[LARGEST_ID];
static int crawler_count = 0;
static volatile int do_run_lru_crawler_thread = 0;
static int lru_crawler_initialized = 0;
static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t lru_crawler_cond = PTHREAD_COND_INITIALIZER;
#ifdef EXTSTORE
/* TODO: pass this around */
static void *storage;
#endif
/* Will crawl all slab classes a minimum of once per hour */
#define MAX_MAINTCRAWL_WAIT 60 * 60
/*** LRU CRAWLER THREAD ***/
#define LRU_CRAWLER_WRITEBUF 8192
static void lru_crawler_close_client(crawler_client_t *c) {
//fprintf(stderr, "CRAWLER: Closing client\n");
sidethread_conn_close(c->c);
c->c = NULL;
c->cbuf = NULL;
bipbuf_free(c->buf);
c->buf = NULL;
}
static void lru_crawler_release_client(crawler_client_t *c) {
//fprintf(stderr, "CRAWLER: Closing client\n");
redispatch_conn(c->c);
c->c = NULL;
c->cbuf = NULL;
bipbuf_free(c->buf);
c->buf = NULL;
}
static int crawler_expired_init(crawler_module_t *cm, void *data) {
struct crawler_expired_data *d;
if (data != NULL) {
d = data;
d->is_external = true;
cm->data = data;
} else {
// allocate data.
d = calloc(1, sizeof(struct crawler_expired_data));
if (d == NULL) {
return -1;
}
// init lock.
pthread_mutex_init(&d->lock, NULL);
d->is_external = false;
d->start_time = current_time;
cm->data = d;
}
pthread_mutex_lock(&d->lock);
memset(&d->crawlerstats, 0, sizeof(crawlerstats_t) * POWER_LARGEST);
for (int x = 0; x < POWER_LARGEST; x++) {
d->crawlerstats[x].start_time = current_time;
d->crawlerstats[x].run_complete = false;
}
pthread_mutex_unlock(&d->lock);
return 0;
}
static void crawler_expired_doneclass(crawler_module_t *cm, int slab_cls) {
struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;
pthread_mutex_lock(&d->lock);
d->crawlerstats[slab_cls].end_time = current_time;
d->crawlerstats[slab_cls].run_complete = true;
pthread_mutex_unlock(&d->lock);
}
static void crawler_expired_finalize(crawler_module_t *cm) {
struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;
pthread_mutex_lock(&d->lock);
d->end_time = current_time;
d->crawl_complete = true;
pthread_mutex_unlock(&d->lock);
if (!d->is_external) {
free(d);
}
}
/* I pulled this out to make the main thread clearer, but it reaches into the
* main thread's values too much. Should rethink again.
*/
static void crawler_expired_eval(crawler_module_t *cm, item *search, uint32_t hv, int i) {
struct crawler_expired_data *d = (struct crawler_expired_data *) cm->data;
pthread_mutex_lock(&d->lock);
crawlerstats_t *s = &d->crawlerstats[i];
int is_flushed = item_is_flushed(search);
#ifdef EXTSTORE
bool is_valid = true;
if (search->it_flags & ITEM_HDR) {
item_hdr *hdr = (item_hdr *)ITEM_data(search);
if (extstore_check(storage, hdr->page_id, hdr->page_version) != 0)
is_valid = false;
}
#endif
if ((search->exptime != 0 && search->exptime < current_time)
|| is_flushed
#ifdef EXTSTORE
|| !is_valid
#endif
) {
crawlers[i].reclaimed++;
s->reclaimed++;
if (settings.verbose > 1) {
int ii;
char *key = ITEM_key(search);
fprintf(stderr, "LRU crawler found an expired item (flags: %d, slab: %d): ",
search->it_flags, search->slabs_clsid);
for (ii = 0; ii < search->nkey; ++ii) {
fprintf(stderr, "%c", key[ii]);
}
fprintf(stderr, "\n");
}
if ((search->it_flags & ITEM_FETCHED) == 0 && !is_flushed) {
crawlers[i].unfetched++;
}
#ifdef EXTSTORE
STORAGE_delete(storage, search);
#endif
do_item_unlink_nolock(search, hv);
do_item_remove(search);
assert(search->slabs_clsid == 0);
} else {
s->seen++;
refcount_decr(search);
if (search->exptime == 0) {
s->noexp++;
} else if (search->exptime - current_time > 3599) {
s->ttl_hourplus++;
} else {
rel_time_t ttl_remain = search->exptime - current_time;
int bucket = ttl_remain / 60;
if (bucket <= 60) {
s->histo[bucket]++;
}
}
}
pthread_mutex_unlock(&d->lock);
}
static void crawler_metadump_eval(crawler_module_t *cm, item *it, uint32_t hv, int i) {
//int slab_id = CLEAR_LRU(i);
char keybuf[KEY_MAX_LENGTH * 3 + 1];
int is_flushed = item_is_flushed(it);
/* Ignore expired content. */
if ((it->exptime != 0 && it->exptime < current_time)
|| is_flushed) {
refcount_decr(it);
return;
}
// TODO: uriencode directly into the buffer.
uriencode(ITEM_key(it), keybuf, it->nkey, KEY_MAX_LENGTH * 3 + 1);
int total = snprintf(cm->c.cbuf, 4096,
"key=%s exp=%ld la=%llu cas=%llu fetch=%s cls=%u size=%lu\n",
keybuf,
(it->exptime == 0) ? -1 : (long)(it->exptime + process_started),
(unsigned long long)(it->time + process_started),
(unsigned long long)ITEM_get_cas(it),
(it->it_flags & ITEM_FETCHED) ? "yes" : "no",
ITEM_clsid(it),
(unsigned long) ITEM_ntotal(it));
refcount_decr(it);
// TODO: some way of tracking the errors. these are very unlikely though.
if (total >= LRU_CRAWLER_WRITEBUF - 1 || total <= 0) {
/* Failed to write, don't push it. */
return;
}
bipbuf_push(cm->c.buf, total);
}
static void crawler_metadump_finalize(crawler_module_t *cm) {
if (cm->c.c != NULL) {
// Ensure space for final message.
lru_crawler_client_getbuf(&cm->c);
memcpy(cm->c.cbuf, "END\r\n", 5);
bipbuf_push(cm->c.buf, 5);
}
}
static int lru_crawler_poll(crawler_client_t *c) {
unsigned char *data;
unsigned int data_size = 0;
struct pollfd to_poll[1];
to_poll[0].fd = c->sfd;
to_poll[0].events = POLLOUT;
int ret = poll(to_poll, 1, 1000);
if (ret < 0) {
// fatal.
return -1;
}
if (ret == 0) return 0;
if (to_poll[0].revents & POLLIN) {
char buf[1];
int res = read(c->sfd, buf, 1);
if (res == 0 || (res == -1 && (errno != EAGAIN && errno != EWOULDBLOCK))) {
lru_crawler_close_client(c);
return -1;
}
}
if ((data = bipbuf_peek_all(c->buf, &data_size)) != NULL) {
if (to_poll[0].revents & (POLLHUP|POLLERR)) {
lru_crawler_close_client(c);
return -1;
} else if (to_poll[0].revents & POLLOUT) {
int total = write(c->sfd, data, data_size);
if (total == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
lru_crawler_close_client(c);
return -1;
}
} else if (total == 0) {
lru_crawler_close_client(c);
return -1;
} else {
bipbuf_poll(c->buf, total);
}
}
}
return 0;
}
/* Grab some space to work with, if none exists, run the poll() loop and wait
* for it to clear up or close.
* Return NULL if closed.
*/
static int lru_crawler_client_getbuf(crawler_client_t *c) {
void *buf = NULL;
if (c->c == NULL) return -1;
/* not enough space. */
while ((buf = bipbuf_request(c->buf, LRU_CRAWLER_WRITEBUF)) == NULL) {
// TODO: max loops before closing.
int ret = lru_crawler_poll(c);
if (ret < 0) return ret;
}
c->cbuf = buf;
return 0;
}
static void lru_crawler_class_done(int i) {
crawlers[i].it_flags = 0;
crawler_count--;
do_item_unlinktail_q((item *)&crawlers[i]);
do_item_stats_add_crawl(i, crawlers[i].reclaimed,
crawlers[i].unfetched, crawlers[i].checked);
pthread_mutex_unlock(&lru_locks[i]);
if (active_crawler_mod.mod->doneclass != NULL)
active_crawler_mod.mod->doneclass(&active_crawler_mod, i);
}
static void *item_crawler_thread(void *arg) {
int i;
int crawls_persleep = settings.crawls_persleep;
pthread_mutex_lock(&lru_crawler_lock);
pthread_cond_signal(&lru_crawler_cond);
settings.lru_crawler = true;
if (settings.verbose > 2)
fprintf(stderr, "Starting LRU crawler background thread\n");
while (do_run_lru_crawler_thread) {
pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);
while (crawler_count) {
item *search = NULL;
void *hold_lock = NULL;
for (i = POWER_SMALLEST; i < LARGEST_ID; i++) {
if (crawlers[i].it_flags != 1) {
continue;
}
/* Get memory from bipbuf, if client has no space, flush. */
if (active_crawler_mod.c.c != NULL) {
int ret = lru_crawler_client_getbuf(&active_crawler_mod.c);
if (ret != 0) {
lru_crawler_class_done(i);
continue;
}
} else if (active_crawler_mod.mod->needs_client) {
lru_crawler_class_done(i);
continue;
}
pthread_mutex_lock(&lru_locks[i]);
search = do_item_crawl_q((item *)&crawlers[i]);
if (search == NULL ||
(crawlers[i].remaining && --crawlers[i].remaining < 1)) {
if (settings.verbose > 2)
fprintf(stderr, "Nothing left to crawl for %d\n", i);
lru_crawler_class_done(i);
continue;
}
uint32_t hv = hash(ITEM_key(search), search->nkey);
/* Attempt to hash item lock the "search" item. If locked, no
* other callers can incr the refcount
*/
if ((hold_lock = item_trylock(hv)) == NULL) {
pthread_mutex_unlock(&lru_locks[i]);
continue;
}
/* Now see if the item is refcount locked */
if (refcount_incr(search) != 2) {
refcount_decr(search);
if (hold_lock)
item_trylock_unlock(hold_lock);
pthread_mutex_unlock(&lru_locks[i]);
continue;
}
crawlers[i].checked++;
/* Frees the item or decrements the refcount. */
/* Interface for this could improve: do the free/decr here
* instead? */
if (!active_crawler_mod.mod->needs_lock) {
pthread_mutex_unlock(&lru_locks[i]);
}
active_crawler_mod.mod->eval(&active_crawler_mod, search, hv, i);
if (hold_lock)
item_trylock_unlock(hold_lock);
if (active_crawler_mod.mod->needs_lock) {
pthread_mutex_unlock(&lru_locks[i]);
}
if (crawls_persleep-- <= 0 && settings.lru_crawler_sleep) {
pthread_mutex_unlock(&lru_crawler_lock);
usleep(settings.lru_crawler_sleep);
pthread_mutex_lock(&lru_crawler_lock);
crawls_persleep = settings.crawls_persleep;
} else if (!settings.lru_crawler_sleep) {
// TODO: only cycle lock every N?
pthread_mutex_unlock(&lru_crawler_lock);
pthread_mutex_lock(&lru_crawler_lock);
}
}
}
if (active_crawler_mod.mod != NULL) {
if (active_crawler_mod.mod->finalize != NULL)
active_crawler_mod.mod->finalize(&active_crawler_mod);
while (active_crawler_mod.c.c != NULL && bipbuf_used(active_crawler_mod.c.buf)) {
lru_crawler_poll(&active_crawler_mod.c);
}
// Double checking in case the client closed during the poll
if (active_crawler_mod.c.c != NULL) {
lru_crawler_release_client(&active_crawler_mod.c);
}
active_crawler_mod.mod = NULL;
}
if (settings.verbose > 2)
fprintf(stderr, "LRU crawler thread sleeping\n");
STATS_LOCK();
stats_state.lru_crawler_running = false;
STATS_UNLOCK();
}
pthread_mutex_unlock(&lru_crawler_lock);
if (settings.verbose > 2)
fprintf(stderr, "LRU crawler thread stopping\n");
return NULL;
}
static pthread_t item_crawler_tid;
int stop_item_crawler_thread(void) {
int ret;
pthread_mutex_lock(&lru_crawler_lock);
do_run_lru_crawler_thread = 0;
pthread_cond_signal(&lru_crawler_cond);
pthread_mutex_unlock(&lru_crawler_lock);
if ((ret = pthread_join(item_crawler_tid, NULL)) != 0) {
fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret));
return -1;
}
settings.lru_crawler = false;
return 0;
}
/* Lock dance to "block" until thread is waiting on its condition:
* caller locks mtx. caller spawns thread.
* thread blocks on mutex.
* caller waits on condition, releases lock.
* thread gets lock, sends signal.
* caller can't wait, as thread has lock.
* thread waits on condition, releases lock
* caller wakes on condition, gets lock.
* caller immediately releases lock.
* thread is now safely waiting on condition before the caller returns.
*/
int start_item_crawler_thread(void) {
int ret;
if (settings.lru_crawler)
return -1;
pthread_mutex_lock(&lru_crawler_lock);
do_run_lru_crawler_thread = 1;
if ((ret = pthread_create(&item_crawler_tid, NULL,
item_crawler_thread, NULL)) != 0) {
fprintf(stderr, "Can't create LRU crawler thread: %s\n",
strerror(ret));
pthread_mutex_unlock(&lru_crawler_lock);
return -1;
}
/* Avoid returning until the crawler has actually started */
pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);
pthread_mutex_unlock(&lru_crawler_lock);
return 0;
}
/* 'remaining' is passed in so the LRU maintainer thread can scrub the whole
* LRU every time.
*/
static int do_lru_crawler_start(uint32_t id, uint32_t remaining) {
uint32_t sid = id;
int starts = 0;
pthread_mutex_lock(&lru_locks[sid]);
if (crawlers[sid].it_flags == 0) {
if (settings.verbose > 2)
fprintf(stderr, "Kicking LRU crawler off for LRU %u\n", sid);
crawlers[sid].nbytes = 0;
crawlers[sid].nkey = 0;
crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */
crawlers[sid].next = 0;
crawlers[sid].prev = 0;
crawlers[sid].time = 0;
if (remaining == LRU_CRAWLER_CAP_REMAINING) {
remaining = do_get_lru_size(sid);
}
crawlers[sid].remaining = remaining;
crawlers[sid].slabs_clsid = sid;
crawlers[sid].reclaimed = 0;
crawlers[sid].unfetched = 0;
crawlers[sid].checked = 0;
do_item_linktail_q((item *)&crawlers[sid]);
crawler_count++;
starts++;
}
pthread_mutex_unlock(&lru_locks[sid]);
if (starts) {
STATS_LOCK();
stats_state.lru_crawler_running = true;
stats.lru_crawler_starts++;
STATS_UNLOCK();
}
return starts;
}
static int lru_crawler_set_client(crawler_module_t *cm, void *c, const int sfd) {
crawler_client_t *crawlc = &cm->c;
if (crawlc->c != NULL) {
return -1;
}
crawlc->c = c;
crawlc->sfd = sfd;
crawlc->buf = bipbuf_new(1024 * 128);
if (crawlc->buf == NULL) {
return -2;
}
return 0;
}
int lru_crawler_start(uint8_t *ids, uint32_t remaining,
const enum crawler_run_type type, void *data,
void *c, const int sfd) {
int starts = 0;
bool is_running;
static rel_time_t block_ae_until = 0;
pthread_mutex_lock(&lru_crawler_lock);
STATS_LOCK();
is_running = stats_state.lru_crawler_running;
STATS_UNLOCK();
if (is_running &&
!(type == CRAWLER_AUTOEXPIRE && active_crawler_type == CRAWLER_AUTOEXPIRE)) {
pthread_mutex_unlock(&lru_crawler_lock);
block_ae_until = current_time + 60;
return -1;
}
if (type == CRAWLER_AUTOEXPIRE && block_ae_until > current_time) {
pthread_mutex_unlock(&lru_crawler_lock);
return -1;
}
/* Configure the module */
if (!is_running) {
assert(crawler_mod_regs[type] != NULL);
active_crawler_mod.mod = crawler_mod_regs[type];
active_crawler_type = type;
if (active_crawler_mod.mod->init != NULL) {
active_crawler_mod.mod->init(&active_crawler_mod, data);
}
if (active_crawler_mod.mod->needs_client) {
if (c == NULL || sfd == 0) {
pthread_mutex_unlock(&lru_crawler_lock);
return -2;
}
if (lru_crawler_set_client(&active_crawler_mod, c, sfd) != 0) {
pthread_mutex_unlock(&lru_crawler_lock);
return -2;
}
}
}
/* we allow the autocrawler to restart sub-LRU's before completion */
for (int sid = POWER_SMALLEST; sid < POWER_LARGEST; sid++) {
if (ids[sid])
starts += do_lru_crawler_start(sid, remaining);
}
if (starts) {
pthread_cond_signal(&lru_crawler_cond);
}
pthread_mutex_unlock(&lru_crawler_lock);
return starts;
}
/*
* Also only clear the crawlerstats once per sid.
*/
enum crawler_result_type lru_crawler_crawl(char *slabs, const enum crawler_run_type type,
void *c, const int sfd, unsigned int remaining) {
char *b = NULL;
uint32_t sid = 0;
int starts = 0;
uint8_t tocrawl[POWER_LARGEST];
/* FIXME: I added this while debugging. Don't think it's needed? */
memset(tocrawl, 0, sizeof(uint8_t) * POWER_LARGEST);
if (strcmp(slabs, "all") == 0) {
for (sid = 0; sid < POWER_LARGEST; sid++) {
tocrawl[sid] = 1;
}
} else {
for (char *p = strtok_r(slabs, ",", &b);
p != NULL;
p = strtok_r(NULL, ",", &b)) {
if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST
|| sid >= MAX_NUMBER_OF_SLAB_CLASSES) {
pthread_mutex_unlock(&lru_crawler_lock);
return CRAWLER_BADCLASS;
}
tocrawl[sid | TEMP_LRU] = 1;
tocrawl[sid | HOT_LRU] = 1;
tocrawl[sid | WARM_LRU] = 1;
tocrawl[sid | COLD_LRU] = 1;
}
}
starts = lru_crawler_start(tocrawl, remaining, type, NULL, c, sfd);
if (starts == -1) {
return CRAWLER_RUNNING;
} else if (starts == -2) {
return CRAWLER_ERROR; /* FIXME: not very helpful. */
} else if (starts) {
return CRAWLER_OK;
} else {
return CRAWLER_NOTSTARTED;
}
}
/* If we hold this lock, crawler can't wake up or move */
void lru_crawler_pause(void) {
pthread_mutex_lock(&lru_crawler_lock);
}
void lru_crawler_resume(void) {
pthread_mutex_unlock(&lru_crawler_lock);
}
int init_lru_crawler(void *arg) {
if (lru_crawler_initialized == 0) {
#ifdef EXTSTORE
storage = arg;
#endif
if (pthread_cond_init(&lru_crawler_cond, NULL) != 0) {
fprintf(stderr, "Can't initialize lru crawler condition\n");
return -1;
}
pthread_mutex_init(&lru_crawler_lock, NULL);
active_crawler_mod.c.c = NULL;
active_crawler_mod.mod = NULL;
active_crawler_mod.data = NULL;
lru_crawler_initialized = 1;
}
return 0;
}
memcached-1.5.6/murmur3_hash.h 0000664 0001750 0001750 00000001251 13025643161 013142 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.5.6/t/ 0000755 0001750 0001750 00000000000 13245327530 010701 5 0000000 0000000 memcached-1.5.6/t/flush-all.t 0000775 0001750 0001750 00000005772 13115057711 012712 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.5.6/t/issue_50.t 0000644 0001750 0001750 00000000605 12416643766 012456 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.5.6/t/extstore-buckets.t 0000664 0001750 0001750 00000003413 13240770206 014321 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use Data::Dumper qw/Dumper/;
my $ext_path;
if (!supports_extstore()) {
plan skip_all => 'extstore not enabled';
exit 0;
}
$ext_path = "/tmp/extstore.$$";
my $server = new_memcached("-m 256 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0,ext_path=$ext_path,ext_low_ttl=60,slab_automove=1");
my $sock = $server->sock;
my $value;
{
my @chars = ("C".."Z");
for (1 .. 20000) {
$value .= $chars[rand @chars];
}
}
# fill some larger objects
{
# interleave sets with 0 ttl vs long ttl's.
my $keycount = 1200;
for (1 .. $keycount) {
print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n";
print $sock "set lfoo$_ 0 5 20000 noreply\r\n$value\r\n";
}
# wait for a flush
sleep 10;
print $sock "lru_crawler crawl all\r\n";
<$sock>;
sleep 2;
# fetch
mem_get_is($sock, "nfoo1", $value);
# check extstore counters
my $stats = mem_stats($sock);
cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated');
cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written');
cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written');
cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched');
cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read');
cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read');
cmp_ok($stats->{extstore_page_reclaims}, '>', 1, 'at least two pages reclaimed');
}
done_testing();
END {
unlink $ext_path if $ext_path;
}
memcached-1.5.6/t/unixsocket.t 0000755 0001750 0001750 00000000767 12416643766 013232 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.5.6/t/bogus-commands.t 0000755 0001750 0001750 00000000417 12416643766 013744 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.5.6/t/slabs-reassign-chunked.t 0000664 0001750 0001750 00000007663 13150607001 015345 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 8;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-m 60 -o slab_reassign,slab_automove,lru_crawler,lru_maintainer,slab_chunk_max=4096');
my $sock = $server->sock;
sub dump_stats {
my $s = shift;
my $filter = shift || '';
for my $k (sort keys %$s) {
if ($filter) {
next unless $k =~ m/$filter/;
}
print STDERR "STAT: $k = ", $s->{$k}, "\n";
}
}
my $value;
{
my @chars = ("C".."Z");
for (1 .. 11000) {
$value .= $chars[rand @chars];
}
}
my $keycount = 5100;
my $res;
for (1 .. $keycount) {
# print STDERR "HI $_\n";
print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n";
# print $sock "set nfoo$_ 0 0 11000\r\n$value\r\n";
# my $res = scalar <$sock>;
# print STDERR "RES: $res\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";
# }
}
{
my $s = mem_stats($sock, 'slabs');
my $sid;
# Find the highest ID to source from.
for my $k (keys %$s) {
next unless $k =~ m/^(\d+):/;
$sid = $s->{$k} if $s->{$k} > $1;
}
for (my $x = 0; $x < 3; $x++) {
print $sock "slabs reassign 17 0\r\n";
my $res = scalar <$sock>;
chomp $res;
# print STDERR "SLABS REASSIGN RESULT: $res\n";
sleep 1;
}
}
# Make room in old class so rescues can happen when we switch slab classes.
#for (1 .. $todelete) {
# print $sock "delete nfoo$_ noreply\r\n";
#}
# Give LRU mover some time to reclaim slab chunks.
#sleep 1;
{
my $stats = mem_stats($sock);
cmp_ok($stats->{slab_global_page_pool}, '>', 0, 'global page pool > 0');
cmp_ok($stats->{slab_reassign_chunk_rescues}, '>', 0, 'some chunk rescues happened');
}
{
my $hits = 0;
for (1 .. $keycount) {
print $sock "get nfoo$_\r\n";
my $body = scalar(<$sock>);
my $expected = "VALUE nfoo$_ 0 11000\r\n$value\r\nEND\r\n";
if ($body =~ /^END/) {
next;
} else {
$body .= scalar(<$sock>) . scalar(<$sock>);
if ($body ne $expected) {
die "Something terrible has happened: $expected\nBODY:\n$body\nDONETEST\n";
}
$hits++;
}
}
cmp_ok($hits, '>', 0, "fetched back $hits values after reassignment");
}
$value = "A"x3000;
for (1 .. $keycount) {
print $sock "set ifoo$_ 0 0 3000 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 3000\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_chunk_rescues}, '>', 0, 'some reassign chunk rescues happened');
# Reassign rescues won't happen here because the headers are of a different
# size and we aren't moving pages out of that slab class
#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');
memcached-1.5.6/t/lru-maintainer.t 0000664 0001750 0001750 00000006402 13160272015 013732 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 226;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
# Regression test for underestimating the size of items after the large memory
# change.
my $server = new_memcached('-m 3 -o lru_maintainer,lru_crawler -l 127.0.0.1');
my $sock = $server->sock;
my $keystub = "X"x200;
for (1 .. 15000) {
print $sock "set $keystub$_ 0 0 2 noreply\r\nok\r\n";
}
# There's probably already an error on the wire, so we'll see that.
$keystub .= "20001";
print $sock "set $keystub 0 0 2\r\nok\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key without OOM");
# Basic tests
$server = new_memcached('-m 6 -o lru_maintainer,lru_crawler -l 127.0.0.1');
$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");
# Items need two fetches to become active
mem_get_is($sock, "canary", $value);
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($istats->{"items:31:number_warm"}, 0, "our canary moved to warm");
use Data::Dumper qw/Dumper/;
}
# Key should've been saved to the WARM_LRU, and still exists.
mem_get_is($sock, "canary", $value);
# Test TEMP_LRU
$server = new_memcached('-m 2 -o lru_maintainer,lru_crawler,temporary_ttl=61');
$sock = $server->sock;
{
my $stats = mem_stats($sock, "settings");
is($stats->{temp_lru}, "yes");
}
print $sock "set canary 0 30 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored temporary canary key");
{
my $stats = mem_stats($sock, "items");
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 higher 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->{"items:31:evictions"}, 0, "some evictions happened");
isnt($stats->{"items:31:number_hot"}, 0, "high exptime items went into hot LRU");
is($stats->{"items:31:number_temp"}, 1, "still one item in temporary LRU");
}
# Canary should still exist, even unfetched, because it's protected by
# temp LRU
mem_get_is($sock, "canary", $value);
memcached-1.5.6/t/maxconns.t 0000755 0001750 0001750 00000001112 12416643766 012645 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.5.6/t/issue_104.t 0000755 0001750 0001750 00000001145 12416643766 012541 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.5.6/t/issue_22.t 0000644 0001750 0001750 00000002166 12416643766 012461 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.5.6/t/expirations.t 0000775 0001750 0001750 00000003323 13160272015 013352 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 3 6\r\nfooval\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
mem_get_is($sock, "foo", "fooval");
sleep(4);
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.5.6/t/getandtouch.t 0000664 0001750 0001750 00000002513 13240770206 013313 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 15;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached();
my $sock = $server->sock;
# cache miss
print $sock "gat 10 foo1\r\n";
is(scalar <$sock>, "END\r\n", "cache miss");
# set foo1 and foo2 (and should get it)
print $sock "set foo1 0 2 7\r\nfooval1\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo");
print $sock "set foo2 0 2 7\r\nfooval2\r\n";
is(scalar <$sock>, "STORED\r\n", "stored foo2");
# get and touch it with cas
print $sock "gats 10 foo1 foo2\r\n";
ok(scalar <$sock> =~ /VALUE foo1 0 7 (\d+)\r\n/, "get and touch foo1 with cas regexp success");
is(scalar <$sock>, "fooval1\r\n","value");
ok(scalar <$sock> =~ /VALUE foo2 0 7 (\d+)\r\n/, "get and touch foo2 with cas regexp success");
is(scalar <$sock>, "fooval2\r\n","value");
is(scalar <$sock>, "END\r\n", "end");
# get and touch it without cas
print $sock "gat 10 foo1 foo2\r\n";
ok(scalar <$sock> =~ /VALUE foo1 0 7\r\n/, "get and touch foo1 without cas regexp success");
is(scalar <$sock>, "fooval1\r\n","value");
ok(scalar <$sock> =~ /VALUE foo2 0 7\r\n/, "get and touch foo2 without cas regexp success");
is(scalar <$sock>, "fooval2\r\n","value");
is(scalar <$sock>, "END\r\n", "end");
sleep 2;
mem_get_is($sock, "foo1", "fooval1");
mem_get_is($sock, "foo2", "fooval2");
memcached-1.5.6/t/issue_163.t 0000664 0001750 0001750 00000002054 13150607001 012517 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 amount");
memcached-1.5.6/t/slabs-reassign2.t 0000664 0001750 0001750 00000007474 13150607001 014010 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 12;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use Data::Dumper qw/Dumper/;
my $server = new_memcached('-m 60 -o slab_reassign,slab_automove,lru_crawler,lru_maintainer,slab_automove_window=3');
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};
# 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) {
next unless $_ % 2 == 0;
print $sock "delete nfoo$_ noreply\r\n";
}
{
my $tries;
for ($tries = 20; $tries > 0; $tries--) {
sleep 1;
my $stats = mem_stats($sock);
if ($stats->{slab_global_page_pool} > 24) {
last;
}
}
cmp_ok($tries, '>', 0, 'some pages moved back to global pool');
}
$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";
# }
}
# Force reassign evictions by moving too much memory manually.
{
my $s = mem_stats($sock, 'slabs');
my $max_pages = 0;
my $scls = 0;
for my $k (keys %$s) {
next unless $k =~ m/^(\d+)\:total_pages/;
if ($s->{$k} > $max_pages) {
$max_pages = $s->{$k};
$scls = $1;
}
}
my $tries;
for ($tries = 10; $tries > 0; $tries--) {
print $sock "slabs reassign $scls 1\r\n";
my $res = <$sock>;
sleep 1;
my $s = mem_stats($sock);
last if $s->{slab_reassign_evictions_nomem} > 0;
}
cmp_ok($tries, '>', 0, 'some reassign evictions happened');
}
cmp_ok($hits, '>', 2000, 'were able to fetch back some of the small keys');
my $stats_done = mem_stats($sock);
cmp_ok($stats_done->{slab_reassign_rescues}, '>', 0, 'some reassign rescues 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} > 50) {
last;
}
}
cmp_ok($tries, '>', 0, 'reclaimed at least 50 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.5.6/t/issue_61.t 0000644 0001750 0001750 00000001415 12416643766 012460 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.5.6/t/extstore.t 0000664 0001750 0001750 00000011520 13240770206 012661 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use Data::Dumper qw/Dumper/;
my $ext_path;
if (!supports_extstore()) {
plan skip_all => 'extstore not enabled';
exit 0;
}
$ext_path = "/tmp/extstore.$$";
my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path,slab_automove=0");
my $sock = $server->sock;
my $value;
{
my @chars = ("C".."Z");
for (1 .. 20000) {
$value .= $chars[rand @chars];
}
}
# fill a small object
print $sock "set foo 0 0 2\r\nhi\r\n";
is(scalar <$sock>, "STORED\r\n", "stored small value");
# fetch
mem_get_is($sock, "foo", "hi");
# check extstore counters
{
my $stats = mem_stats($sock);
is($stats->{extstore_objects_written}, 0);
}
# fill some larger objects
{
# set one canary value for later
print $sock "set canary 0 0 20000 noreply\r\n$value\r\n";
my $keycount = 1000;
for (1 .. $keycount) {
print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n";
}
# wait for a flush
sleep 4;
# fetch
# TODO: Fetch back all values
mem_get_is($sock, "nfoo1", $value);
# check extstore counters
my $stats = mem_stats($sock);
cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated');
cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written');
cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written');
cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched');
cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read');
cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read');
# Remove half of the keys for the next test.
for (1 .. $keycount) {
next unless $_ % 2 == 0;
print $sock "delete nfoo$_ noreply\r\n";
}
my $stats2 = mem_stats($sock);
cmp_ok($stats->{extstore_bytes_used}, '>', $stats2->{extstore_bytes_used},
'bytes used dropped after deletions');
cmp_ok($stats->{extstore_objects_used}, '>', $stats2->{extstore_objects_used},
'objects used dropped after deletions');
is($stats2->{badcrc_from_extstore}, 0, 'CRC checks successful');
is($stats2->{miss_from_extstore}, 0, 'no misses');
# delete the rest
for (1 .. $keycount) {
next unless $_ % 2 == 1;
print $sock "delete nfoo$_ noreply\r\n";
}
}
# fill to eviction
{
my $keycount = 3000;
for (1 .. $keycount) {
print $sock "set mfoo$_ 0 0 20000 noreply\r\n$value\r\n";
}
sleep 4;
my $stats = mem_stats($sock);
is($stats->{miss_from_extstore}, 0, 'no misses');
mem_get_is($sock, "canary", undef);
# check counters
$stats = mem_stats($sock);
cmp_ok($stats->{extstore_page_evictions}, '>', 0, 'at least one page evicted');
cmp_ok($stats->{extstore_objects_evicted}, '>', 0, 'at least one object evicted');
cmp_ok($stats->{extstore_bytes_evicted}, '>', 0, 'some bytes evicted');
is($stats->{extstore_pages_free}, 0, '0 pages are free');
is($stats->{miss_from_extstore}, 1, 'exactly one miss');
# refresh some keys so rescues happen while drop_unread == 1.
for (1 .. $keycount / 2) {
next unless $_ % 2 == 1;
# Need to be fetched twice in order to bump
print $sock "touch mfoo$_ 0 noreply\r\n";
print $sock "touch mfoo$_ 0 noreply\r\n";
}
print $sock "extstore drop_unread 1\r\n";
my $res = <$sock>;
print $sock "extstore max_frag 0\r\n";
$res = <$sock>;
for (1 .. $keycount) {
next unless $_ % 2 == 0;
print $sock "delete mfoo$_ noreply\r\n";
}
sleep 4;
$stats = mem_stats($sock);
cmp_ok($stats->{extstore_pages_free}, '>', 0, 'some pages now free');
cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened');
cmp_ok($stats->{extstore_compact_skipped}, '>', 0, 'some compaction skips happened');
print $sock "extstore drop_unread 0\r\n";
$res = <$sock>;
}
# attempt to incr/decr/append/prepend or chunk objects that were sent to disk.
{
my $keycount = 100;
for (1 .. $keycount) {
print $sock "set bfoo$_ 0 0 20000 noreply\r\n$value\r\n";
}
sleep 4;
# incr should be blocked.
print $sock "incr bfoo1 1\r\n";
is(scalar <$sock>, "CLIENT_ERROR cannot increment or decrement non-numeric value\r\n", 'incr fails');
# append/prepend *could* work, but it would require pulling the item back in.
print $sock "append bfoo1 0 0 2\r\nhi\r\n";
is(scalar <$sock>, "NOT_STORED\r\n", 'append falis');
print $sock "prepend bfoo1 0 0 2\r\nhi\r\n";
is(scalar <$sock>, "NOT_STORED\r\n", 'prepend fails');
}
done_testing();
END {
unlink $ext_path if $ext_path;
}
memcached-1.5.6/t/evictions.t 0000644 0001750 0001750 00000001551 12777363632 013027 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.5.6/t/whitespace.t 0000775 0001750 0001750 00000002541 13160272015 013142 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 compile_commands.json);
push(@exempted, glob("doc/*.xml"));
push(@exempted, glob("doc/*.full"));
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.5.6/t/incrdecr.t 0000755 0001750 0001750 00000004155 12416643766 012622 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.5.6/t/inline_asciihdr.t 0000664 0001750 0001750 00000001540 13150607001 014121 0000000 0000000 #!/usr/bin/perl
# Ensure get and gets can mirror flags + CAS properly when not inlining the
# ascii response header.
use strict;
use Test::More tests => 17;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-o no_inline_ascii_resp');
my $sock = $server->sock;
# 0 flags and size
print $sock "set foo 0 0 0\r\n\r\n";
is(scalar <$sock>, "STORED\r\n", "stored");
mem_get_is($sock, "foo", "");
for my $flags (0, 123, 2**16-1, 2**31, 2**32-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");
my @res = mem_gets($sock, "foo");
mem_gets_is({ sock => $sock,
flags => $flags }, $res[0], "foo", "fooval", "got flags $flags back");
}
memcached-1.5.6/t/binary-sasl.t 0000775 0001750 0001750 00000041726 13025643161 013246 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.5.6/t/stats-detail.t 0000644 0001750 0001750 00000004105 12416643766 013417 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.5.6/t/cas.t 0000664 0001750 0001750 00000011135 13115057711 011554 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 1");
is(scalar <$sock>, "END\r\n","gets bug15 END");
ok($bug15_cas != $next_bug15_cas, "CAS changed");
memcached-1.5.6/t/lru-crawler.t 0000664 0001750 0001750 00000004642 13160272015 013246 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 -o no_modern');
{
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 no_modern,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.5.6/t/issue_260.t 0000775 0001750 0001750 00000004501 13025643161 012527 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.5.6/t/flags.t 0000775 0001750 0001750 00000000717 13115057711 012111 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;
# set foo (and should get it)
for my $flags (0, 123, 2**16-1, 2**31) {
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.5.6/t/issue_29.t 0000644 0001750 0001750 00000001167 12416643766 012470 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.5.6/t/issue_152.t 0000644 0001750 0001750 00000000606 12416643766 012542 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.5.6/t/issue_3.t 0000644 0001750 0001750 00000002524 12416643766 012376 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.5.6/t/issue_192.t 0000664 0001750 0001750 00000001044 13025643161 012527 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.5.6/t/chunked-items.t 0000664 0001750 0001750 00000007466 13150607001 013552 0000000 0000000 #!/usr/bin/perl
# Networked logging tests.
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-m 48 -o slab_chunk_max=16384');
my $sock = $server->sock;
# We're testing to ensure item chaining doesn't corrupt or poorly overlap
# data, so create a non-repeating pattern.
my @parts = ();
for (1 .. 8000) {
push(@parts, $_);
}
my $pattern = join(':', @parts);
my $plen = length($pattern);
print $sock "set pattern 0 0 $plen\r\n$pattern\r\n";
is(scalar <$sock>, "STORED\r\n", "stored pattern successfully");
mem_get_is($sock, "pattern", $pattern);
for (1..5) {
my $size = 400 * 1024;
my $data = "x" x $size;
print $sock "set foo$_ 0 0 $size\r\n$data\r\n";
my $res = <$sock>;
is($res, "STORED\r\n", "stored some big items");
}
{
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) {
print $sock "set toast$_ 0 0 $biglen\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored big");
mem_get_is($sock, "toast$_", $big);
}
}
# Test a wide range of sets.
{
my $len = 1024 * 200;
while ($len < 1024 * 1024) {
my $val = "B" x $len;
print $sock "set foo_$len 0 0 $len\r\n$val\r\n";
is(scalar <$sock>, "STORED\r\n", "stored size $len");
$len += 2048;
}
}
# Test long appends and prepends.
# Note: memory bloats like crazy if we use one test per request.
{
my $str = 'seedstring';
my $len = length($str);
print $sock "set appender 0 0 $len\r\n$str\r\n";
is(scalar <$sock>, "STORED\r\n", "stored seed string for append");
my $unexpected = 0;
for my $part (@parts) {
# reduce required loops but still have a pattern.
my $todo = $part . "x" x 10;
$str .= $todo;
my $len = length($todo);
print $sock "append appender 0 0 $len\r\n$todo\r\n";
is(scalar <$sock>, "STORED\r\n", "append $todo size $len");
print $sock "get appender\r\n";
my $header = scalar <$sock>;
my $body = scalar <$sock>;
my $end = scalar <$sock>;
$unexpected++ unless $body eq "$str\r\n";
}
is($unexpected, 0, "No unexpected results during appends\n");
# Now test appending a chunked item to a chunked item.
$len = length($str);
print $sock "append appender 0 0 $len\r\n$str\r\n";
is(scalar <$sock>, "STORED\r\n", "append large string size $len");
mem_get_is($sock, "appender", $str . $str);
print $sock "delete appender\r\n";
is(scalar <$sock>, "DELETED\r\n", "removed appender key");
}
{
my $str = 'seedstring';
my $len = length($str);
print $sock "set prepender 0 0 $len\r\n$str\r\n";
is(scalar <$sock>, "STORED\r\n", "stored seed string for append");
my $unexpected = 0;
for my $part (@parts) {
# reduce required loops but still have a pattern.
$part .= "x" x 10;
$str = $part . $str;
my $len = length($part);
print $sock "prepend prepender 0 0 $len\r\n$part\r\n";
is(scalar <$sock>, "STORED\r\n", "prepend $part size $len");
print $sock "get prepender\r\n";
my $header = scalar <$sock>;
my $body = scalar <$sock>;
my $end = scalar <$sock>;
$unexpected++ unless $body eq "$str\r\n";
}
is($unexpected, 0, "No unexpected results during prepends\n");
# Now test prepending a chunked item to a chunked item.
$len = length($str);
print $sock "prepend prepender 0 0 $len\r\n$str\r\n";
is(scalar <$sock>, "STORED\r\n", "prepend large string size $len");
mem_get_is($sock, "prepender", $str . $str);
print $sock "delete prepender\r\n";
is(scalar <$sock>, "DELETED\r\n", "removed prepender key");
}
done_testing();
memcached-1.5.6/t/stats-conns.t 0000775 0001750 0001750 00000003644 13160272015 013267 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;
my $socket_path = $2;
# getsockname(2) doesn't return socket path on GNU/Hurd (and maybe others)
SKIP: {
skip "socket path checking on GNU kernel", 1 if ($^O eq 'gnu');
is($socket_path, $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("-l 0.0.0.0");
$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+)/;
print STDERR "PORT: ", $server->port, "\n";
is($1, $server->port, "tcp port number is correct");
memcached-1.5.6/t/issue_140.t 0000644 0001750 0001750 00000001716 12416643766 012542 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.5.6/t/binary.t 0000775 0001750 0001750 00000060143 13240770206 012300 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached("-o no_modern");
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 "Some chunked item tests";
my $s2 = new_memcached('-o no_modern,slab_chunk_max=4096');
ok($s2, "started the server");
my $m2 = MC::Client->new($s2);
# Specifically trying to cross the chunk boundary when internally
# appending CLRF.
for my $k (7900..8100) {
my $val = 'd' x $k;
$val .= '123';
$m2->set('t', $val, 0, 0);
# Ensure we get back the same value. Bugs can chop chars.
my (undef, $gval, undef) = $m2->get('t');
ok($gval eq $val, $gval . " = " . $val);
}
my $cval = ('d' x 8100) . '123';
my $m3 = $s2->new_sock;
mem_get_is($m3, 't', $cval, "large value set from bin fetched from ascii");
}
{
# 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'});
isnt('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");
}
done_testing();
# ######################################################################
# 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.5.6/t/00-startup.t 0000775 0001750 0001750 00000003773 13115057711 012741 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 20;
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.");
# Maximum connections must be greater than 0.
eval {
my $server = new_memcached("-c 0");
};
ok($@, "Died with invalid maximum connections 0.");
eval {
my $server = new_memcached("-c -1");
};
ok($@, "Died with invalid maximum connections -1.");
# Should not allow -t 0
eval {
my $server = new_memcached("-t 0");
};
ok($@, "Died with illegal 0 thread count");
memcached-1.5.6/t/lib/ 0000755 0001750 0001750 00000000000 13240770206 011444 5 0000000 0000000 memcached-1.5.6/t/lib/MemcachedTest.pm 0000664 0001750 0001750 00000020176 13240770206 014440 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;
my @unixsockets = ();
@EXPORT = qw(new_memcached sleep mem_get_is mem_gets mem_gets_is mem_stats
supports_sasl free_port supports_drop_priv supports_extstore);
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 supports_extstore {
my $output = `$builddir/memcached-debug -h`;
return 1 if $output =~ /ext_path/i;
return 0;
}
sub supports_drop_priv {
my $output = `$builddir/memcached-debug -h`;
return 1 if $output =~ /no_drop_privileges/i;
return 0;
}
sub new_memcached {
my ($args, $passed_port) = @_;
my $port = $passed_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;
}
if ($< == 0) {
$args .= " -u root";
}
$args .= " -o relaxed_privileges";
my $udpport;
if ($args =~ /-l (\S+)/) {
$port = free_port();
$udpport = free_port("udp");
$args .= " -p $port";
if (supports_udp()) {
$args .= " -U $udpport";
}
} elsif ($args !~ /-s (\S+)/) {
my $num = @unixsockets;
my $file = "/tmp/memcachetest.$$.$num";
$args .= " -s $file";
push(@unixsockets, $file);
}
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.");
}
END {
for (@unixsockets) {
unlink $_;
}
}
############################################################################
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.5.6/t/lru.t 0000775 0001750 0001750 00000003152 13160272015 011607 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('-o no_modern');
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.5.6/t/daemonize.t 0000755 0001750 0001750 00000001342 12416643766 012777 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.5.6/t/issue_42.t 0000664 0001750 0001750 00000001033 13160272015 012433 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("-o no_modern");
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" || $req == "770", "Check allocated size");
memcached-1.5.6/t/udp.t 0000775 0001750 0001750 00000022745 13160272015 011606 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("-l 127.0.0.1");
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.5.6/t/slabs_reassign.t 0000644 0001750 0001750 00000004657 12602341606 014015 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.5.6/t/issue_41.t 0000644 0001750 0001750 00000001724 12416643766 012461 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.5.6/t/stats.t 0000775 0001750 0001750 00000012156 13240770206 012153 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More tests => 108;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached("-o no_lru_crawler,no_lru_maintainer");
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.
## see doc/protocol.txt for others
# note that auth stats are tested in auth specific tests
my $stats = mem_stats($sock);
# Test number of keys
is(scalar(keys(%$stats)), 70, "expected count of stats values");
# Test initial state
foreach my $key (qw(curr_items total_items bytes cmd_get cmd_set get_hits evictions get_misses get_expired
bytes_written delete_hits delete_misses incr_hits incr_misses decr_hits get_flushed
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'});
isnt('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->{'get_expired'});
is(0, $stats->{'get_flushed'});
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'});
# item expired
print $sock "set should_expire 0 2678400 6\r\nfooval\r\n"; #2678400 = 31 days in seconds
is(scalar <$sock>, "STORED\r\n", "set item to expire");
print $sock "get should_expire\r\n";
is(scalar <$sock>, "END\r\n", "item not returned");
my $stats = mem_stats($sock);
is(1, $stats->{'get_expired'}, "get_expired counter is 1");
print $sock "set should_be_flushed 0 0 6\r\nbooval\r\n";
is(scalar <$sock>, "STORED\r\n", "set item to flush");
print $sock "flush_all\r\n";
is(scalar <$sock>, "OK\r\n", "flushed");
print $sock "get should_be_flushed\r\n";
is(scalar <$sock>, "END\r\n", "flushed item not returned");
my $stats = mem_stats($sock);
is($stats->{cmd_flush}, 1, "after one flush cmd_flush is 1");
is($stats->{get_flushed}, 1, "after flush and a get, get_flushed is 1");
memcached-1.5.6/t/stress-memcached.pl 0000755 0001750 0001750 00000004265 11246331452 014414 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.5.6/t/issue_183.t 0000664 0001750 0001750 00000001223 13160272015 012522 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("-o no_modern");
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.5.6/t/multiversioning.t 0000755 0001750 0001750 00000003016 12416643766 014262 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.5.6/t/touch.t 0000755 0001750 0001750 00000000731 12416643766 012147 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.5.6/t/64bit.t 0000755 0001750 0001750 00000002162 12416643766 011755 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.5.6/t/noreply.t 0000644 0001750 0001750 00000002300 12416643766 012504 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.5.6/t/idle-timeout.t 0000664 0001750 0001750 00000001715 13160272015 013406 0000000 0000000 #!/usr/bin/perl
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("-o idle_timeout=3 -l 127.0.0.1");
my $sock = $server->sock;
# Make sure we can talk to start with
my $stats = mem_stats($sock);
is($stats->{idle_kicks}, "0", "check stats initial");
isnt($sock->connected(), undef, "check connected");
# Make sure we don't timeout when active
for (my $i = 0; $i < 6; $i++) {
$stats = mem_stats($sock);
isnt($stats->{version}, undef, "check active $i");
}
$stats = mem_stats($sock);
is($stats->{idle_kicks}, "0", "check stats 2");
# Make sure we do timeout when not
sleep(5);
mem_stats($sock); # Network activity, so socket code will see dead socket
sleep(1);
is($sock->connected(), undef, "check disconnected");
$sock = $server->sock;
$stats = mem_stats($sock);
isnt($stats->{idle_kicks}, 0, "check stats timeout");
memcached-1.5.6/t/line-lengths.t 0000755 0001750 0001750 00000001004 12416643766 013410 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.5.6/t/binary-extstore.t 0000775 0001750 0001750 00000040132 13240770206 014147 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
use Data::Dumper qw/Dumper/;
my $ext_path;
if (!supports_extstore()) {
plan skip_all => 'extstore not enabled';
exit 0;
}
$ext_path = "/tmp/extstore.$$";
my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path,no_lru_crawler,slab_automove=0");
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;
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);
};
my $value;
my $bigvalue;
{
my @chars = ("C".."Z");
for (1 .. 20000) {
$value .= $chars[rand @chars];
}
for (1 .. 800000) {
$bigvalue .= $chars[rand @chars];
}
}
# diag "small object";
$set->('x', 10, 19, "somevalue");
# check extstore counters
{
my %stats = $mc->stats('');
is($stats{extstore_objects_written}, 0);
}
# diag "Delete";
#$delete->('x');
# diag "Flush";
#$empty->('y');
# fill some larger objects
{
my $keycount = 1000;
for (1 .. $keycount) {
$set->("nfoo$_", 0, 19, $value);
}
# wait for a flush
sleep 4;
# value returns for one flushed object.
$check->('nfoo1', 19, $value);
# check extstore counters
my %stats = $mc->stats('');
cmp_ok($stats{extstore_page_allocs}, '>', 0, 'at least one page allocated');
cmp_ok($stats{extstore_objects_written}, '>', $keycount / 2, 'some objects written');
cmp_ok($stats{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written');
cmp_ok($stats{get_extstore}, '>', 0, 'one object was fetched');
cmp_ok($stats{extstore_objects_read}, '>', 0, 'one object read');
cmp_ok($stats{extstore_bytes_read}, '>', length($value), 'some bytes read');
# Test multiget
my $rv = $mc->get_multi(qw(nfoo2 nfoo3 noexist));
is($rv->{nfoo2}->[1], $value, 'multiget nfoo2');
is($rv->{nfoo3}->[1], $value, 'multiget nfoo2');
# Remove half of the keys for the next test.
for (1 .. $keycount) {
next unless $_ % 2 == 0;
$delete->("nfoo$_");
}
my %stats2 = $mc->stats('');
cmp_ok($stats{extstore_bytes_used}, '>', $stats2{extstore_bytes_used},
'bytes used dropped after deletions');
cmp_ok($stats{extstore_objects_used}, '>', $stats2{extstore_objects_used},
'objects used dropped after deletions');
is($stats2{badcrc_from_extstore}, 0, 'CRC checks successful');
# delete the rest
for (1 .. $keycount) {
next unless $_ % 2 == 1;
$delete->("nfoo$_");
}
}
# check evictions and misses
{
my $keycount = 1000;
for (1 .. $keycount) {
$set->("mfoo$_", 0, 19, $value);
}
sleep 4;
for ($keycount .. ($keycount*3)) {
$set->("mfoo$_", 0, 19, $value);
}
sleep 4;
$empty->('mfoo1');
my %s = $mc->stats('');
cmp_ok($s{extstore_objects_evicted}, '>', 0);
cmp_ok($s{miss_from_extstore}, '>', 0);
}
# store and re-fetch a chunked value
{
my %stats = $mc->stats('');
$set->("bigvalue", 0, 0, $bigvalue);
sleep 4;
$check->("bigvalue", 0, $bigvalue);
my %stats2 = $mc->stats('');
cmp_ok($stats2{extstore_objects_written}, '>',
$stats{extstore_objects_written}, "a large value flushed");
}
# ensure ASCII can still fetch the chunked value.
{
my $ns = $server->new_sock;
my %s1 = $mc->stats('');
mem_get_is($ns, "bigvalue", $bigvalue);
print $ns "extstore recache_rate 1\r\n";
is(scalar <$ns>, "OK\r\n", "recache rate upped");
for (1..3) {
mem_get_is($ns, "bigvalue", $bigvalue);
$check->('bigvalue', 0, $bigvalue);
}
my %s2 = $mc->stats('');
cmp_ok($s2{recache_from_extstore}, '>', $s1{recache_from_extstore},
'a new recache happened');
}
done_testing();
END {
unlink $ext_path if $ext_path;
}
# ######################################################################
# 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.5.6/t/issue_67.t 0000664 0001750 0001750 00000004746 13245327530 012467 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);
# test build requires more privileges
$args .= " -o relaxed_privileges";
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, -1);
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', 11211, -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.5.6/t/dyn-maxbytes.t 0000664 0001750 0001750 00000005047 13150607001 013427 0000000 0000000 #!/usr/bin/perl
# Test the 'stats items' evictions counters.
use strict;
use Test::More tests => 309;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached("-m 3 -o modern,slab_automove_window=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");
}
my $stats = mem_stats($sock);
my $evicted = $stats->{evictions};
isnt($evicted, "0", "check evicted");
# We're past the memory limit. Try adjusting maxbytes upward.
$stats = mem_stats($sock, "settings");
my $pre_maxbytes = $stats->{"maxbytes"};
print $sock "cache_memlimit 8\r\n";
is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 3m to 8m");
# Confirm maxbytes updated.
$stats = mem_stats($sock, "settings");
isnt($stats->{"maxbytes"}, $pre_maxbytes, "stats settings maxbytes updated");
# Check for total_malloced increasing as new memory is added
$stats = mem_stats($sock, "slabs");
my $t_malloc = $stats->{"total_malloced"};
print $sock "set toast 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored toast");
$stats = mem_stats($sock, "slabs");
cmp_ok($stats->{"total_malloced"}, '>', $t_malloc, "stats slabs total_malloced increased");
$stats = mem_stats($sock);
my $new_evicted = $stats->{evictions};
cmp_ok($new_evicted, '==', $evicted, "no new evictions");
# Bump up to 16, fill a bit more, then delete everything.
print $sock "cache_memlimit 16\r\n";
is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 8m to 16m");
for (;$key < 150; $key++) {
print $sock "set key$key 0 0 66560\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
# Grab total_malloced after filling everything up.
$stats = mem_stats($sock, "slabs");
$t_malloc = $stats->{"total_malloced"};
print $sock "cache_memlimit 8\r\n";
is(scalar <$sock>, "OK\r\n", "bumped maxbytes from 16m to 8m");
# Remove all of the keys, allowing the slab rebalancer to push pages toward
# the global page pool.
for ($key = 0; $key < 150; $key++) {
print $sock "delete key$key\r\n";
like(scalar <$sock>, qr/(DELETED|NOT_FOUND)\r\n/, "deleted key$key");
}
# If memory limit is lower, it should free those pages back to the OS.
my $reduced = 0;
for (my $tries = 0; $tries < 6; $tries++) {
sleep 1;
$stats = mem_stats($sock, "slabs");
$reduced = $stats->{"total_malloced"} if ($t_malloc > $stats->{"total_malloced"});
last if $reduced;
}
isnt($reduced, 0, "total_malloced reduced to $reduced");
memcached-1.5.6/t/refhang.t 0000664 0001750 0001750 00000003466 13150607001 012420 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
plan skip_all => 'Test is flaky. Needs special hooks.';
plan tests => 127;
# 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.5.6/t/issue_70.t 0000664 0001750 0001750 00000001144 13204422154 012437 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.5.6/t/getset.t 0000775 0001750 0001750 00000006174 13150607001 012303 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");
# pipeline 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.5.6/t/misbehave.t 0000664 0001750 0001750 00000000572 13160272015 012750 0000000 0000000 #!/usr/bin/perl
use strict;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
if (supports_drop_priv()) {
plan tests => 1;
} else {
plan skip_all => 'Privilege drop not supported';
exit 0;
}
my $server = new_memcached();
my $sock = $server->sock;
print $sock "misbehave\r\n";
is(scalar <$sock>, "OK\r\n", "did not allow misbehaving");
memcached-1.5.6/t/item_size_max.t 0000664 0001750 0001750 00000002362 13115057711 013645 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 -o slab_chunk_max=1024');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 1024);
$server->stop();
# Reasonable but unreasonable.
$server = new_memcached('-I 2097152');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 2097152);
$server->stop();
# Suffix kilobytes.
$server = new_memcached('-I 512k -o slab_chunk_max=16384');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 524288);
$server->stop();
# Suffix megabytes.
$server = new_memcached('-m 256 -I 32m');
my $stats = mem_stats($server->sock, ' settings');
is($stats->{item_size_max}, 33554432);
$server->stop();
memcached-1.5.6/t/issue_14.t 0000644 0001750 0001750 00000001446 12416643766 012462 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.5.6/t/issue_108.t 0000644 0001750 0001750 00000001257 12416643766 012546 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.5.6/t/quit.t 0000664 0001750 0001750 00000001121 13160272015 011756 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 "quit\r\n";
# 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($sock, $data, 24),
};
is($bytesread, 0, "Read after quit.");
# Restore signal stuff.
alarm($oldalarmt);
}
memcached-1.5.6/t/slabhang.t 0000664 0001750 0001750 00000003371 13150607001 012560 0000000 0000000 #!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
plan skip_all => 'Test is flaky. Needs special hooks.';
plan tests => 74;
# start up a server with 10 maximum connections
my $server = new_memcached("-m 16 -o modern");
my $sock = $server->sock;
my $hangsock = $server->new_sock;
my $value = "B"x260144;
my $key = 0;
# disable the normal automover.
print $sock "slabs automove 0\r\n";
is(scalar <$sock>, "OK\r\n", "automover disabled");
# These aren't set to expire.
my $mget = '';
for ($key = 0; $key < 70; $key++) {
$mget .= "key$key ";
print $sock "set key$key 0 0 260144\r\n$value\r\n";
is(scalar <$sock>, "STORED\r\n", "stored key$key");
}
chop $mget;
# Don't intend to read the results, need to fill the socket.
print $hangsock "get $mget\r\n";
#sleep 8;
my $stats = mem_stats($sock, "slabs");
my $source = 0;
for my $key (keys %$stats) {
if ($key =~ m/^(\d+):total_pages/) {
my $sid = $1;
if ($stats->{$key} > 10) {
$source = $sid;
last;
}
}
}
isnt($source, 0, "found the source slab: $source");
my $busy;
my $tomove = 4;
my $reassign = "slabs reassign $source 1\r\n";
while ($tomove) {
$busy = 0;
print $sock $reassign;
my $res = scalar <$sock>;
while ($res =~ m/^BUSY/) {
if ($hangsock && $busy > 5) {
# unjam the pipeline
$hangsock->close;
}
last if ($busy > 10);
sleep 1;
$busy++;
print $sock $reassign;
$res = scalar <$sock>;
}
last if ($busy > 10);
$tomove--;
}
ok($busy <= 10, "didn't time out moving pages");
$stats = mem_stats($sock);
isnt($stats->{"slab_reassign_busy_deletes"}, "0", "deleted some busy items");
memcached-1.5.6/t/sasl/ 0000755 0001750 0001750 00000000000 13110467160 011636 5 0000000 0000000 memcached-1.5.6/t/sasl/memcached.conf 0000644 0001750 0001750 00000000117 11446413300 014327 0000000 0000000 mech_list: plain cram-md5
log_level: 5
sasldb_path: /tmp/test-memcached.sasldb
memcached-1.5.6/t/malicious-commands.t 0000664 0001750 0001750 00000001464 13160272015 014572 0000000 0000000 #!/usr/bin/perl
# These command strings are always expected to be malicious and as such we
# should just hang up on them.
use strict;
use Test::More tests => 3;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my @strs = (
"GET / HTTP/1.0",
"PUT /asdf/asd/fasdfasdf/sadf HTTP/1.1",
"DELETE HTTP/1.1"
);
for my $str (@strs) {
my $server = new_memcached();
my $sock = $server->sock;
print $sock "$str\r\n";
# 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($sock, $data, 24),
};
is($bytesread, 0, $str);
# Restore signal stuff.
alarm($oldalarmt);
}
memcached-1.5.6/t/chunked-extstore.t 0000664 0001750 0001750 00000011133 13240770206 014300 0000000 0000000 #!/usr/bin/perl
# Networked logging tests.
use strict;
use warnings;
use Test::More;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $ext_path;
if (!supports_extstore()) {
plan skip_all => 'extstore not enabled';
exit 0;
}
$ext_path = "/tmp/extstore.$$";
my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_page_count=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path,slab_chunk_max=16384,slab_automove=0");
my $sock = $server->sock;
# We're testing to ensure item chaining doesn't corrupt or poorly overlap
# data, so create a non-repeating pattern.
my @parts = ();
for (1 .. 8000) {
push(@parts, $_);
}
my $pattern = join(':', @parts);
my $plen = length($pattern);
print $sock "set pattern 0 0 $plen\r\n$pattern\r\n";
is(scalar <$sock>, "STORED\r\n", "stored pattern successfully");
# Stash two more for later test
print $sock "set pattern2 0 0 $plen noreply\r\n$pattern\r\n";
print $sock "set pattern3 0 0 $plen noreply\r\n$pattern\r\n";
sleep 4;
mem_get_is($sock, "pattern", $pattern);
for (1..5) {
my $size = 400 * 1024;
my $data = "x" x $size;
print $sock "set foo$_ 0 0 $size\r\n$data\r\n";
my $res = <$sock>;
is($res, "STORED\r\n", "stored some big items");
}
{
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..40) {
print $sock "set toast$_ 0 0 $biglen\r\n$big\r\n";
is(scalar <$sock>, "STORED\r\n", "stored big");
}
sleep 4;
for (1..40) {
mem_get_is($sock, "toast$_", $big);
}
my $stats = mem_stats($sock);
cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated');
cmp_ok($stats->{extstore_objects_written}, '>', 25, 'some objects written');
cmp_ok($stats->{extstore_bytes_written}, '>', $max * 2, 'some bytes written');
cmp_ok($stats->{get_extstore}, '>', 5, 'multiple objects fetched');
cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read');
cmp_ok($stats->{extstore_bytes_read}, '>', $max * 2, 'some bytes read');
is($stats->{badcrc_from_extstore}, 0, 'CRC checks successful');
}
# fill to eviction
{
my $keycount = 1000;
for (1 .. $keycount) {
print $sock "set mfoo$_ 0 0 $plen noreply\r\n$pattern\r\n";
}
sleep 6;
my $stats = mem_stats($sock);
cmp_ok($stats->{extstore_page_evictions}, '>', 0, 'at least one page evicted');
cmp_ok($stats->{extstore_objects_evicted}, '>', 0, 'at least one object evicted');
cmp_ok($stats->{extstore_bytes_evicted}, '>', 0, 'some bytes evicted');
is($stats->{extstore_pages_free}, 1, '1 page is free');
is($stats->{miss_from_extstore}, 0, 'no misses');
# original "pattern" key should be gone.
mem_get_is($sock, "pattern", undef, "original pattern key is gone");
$stats = mem_stats($sock);
is($stats->{miss_from_extstore}, 1, 'one extstore miss');
print $sock "get pattern2 pattern3\r\n";
is(scalar <$sock>, "END\r\n", "multiget double miss");
$stats = mem_stats($sock);
is($stats->{miss_from_extstore}, 3, 'three extstore misses');
}
# Let compaction run.
{
for (1..40) {
print $sock "delete toast$_ noreply\r\n" if $_ % 2 == 0;
}
for (1..1000) {
# Force a read so objects don't get skipped.
mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1;
}
for (1..1000) {
# Force a read so objects don't get skipped.
print $sock "delete mfoo$_ noreply\r\n" if $_ % 2 == 0;
}
sleep 4;
my $stats = mem_stats($sock);
cmp_ok($stats->{extstore_pages_free}, '>', 2, 'some pages now free');
cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened');
# Some of the early items got evicted
for (100..1000) {
# everything should validate properly.
mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1;
}
}
# test recache
{
print $sock "extstore recache_rate 1\r\n";
is(scalar <$sock>, "OK\r\n", "upped recache rate");
for (800..1000) {
mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1;
}
my $stats = mem_stats($sock);
cmp_ok($stats->{recache_from_extstore}, '>', 100, 'recaching happening');
for (800..1000) {
mem_get_is($sock, "mfoo$_", $pattern) if $_ % 2 == 1;
}
my $stats2 = mem_stats($sock);
is($stats->{recache_from_extstore}, $stats2->{recache_from_extstore},
'values already recached');
}
done_testing();
END {
unlink $ext_path if $ext_path;
}
memcached-1.5.6/t/watcher.t 0000664 0001750 0001750 00000004644 13115057711 012452 0000000 0000000 #!/usr/bin/perl
# Networked logging tests.
use strict;
use warnings;
use Socket qw/SO_RCVBUF/;
use Test::More tests => 8;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
my $server = new_memcached('-m 60 -o watcher_logbuf_size=8');
my $client = $server->sock;
my $watcher = $server->new_sock;
# This doesn't return anything.
print $watcher "watch\n";
my $res = <$watcher>;
is($res, "OK\r\n", "watcher enabled");
print $client "get foo\n";
$res = <$client>;
is($res, "END\r\n", "basic get works");
my $spacer = "X"x180;
# This is a flaky test... depends on buffer sizes. Could either have memc
# shrink the watcher buffer, or loop this and keep doubling until we get some
# skipped values.
for (1 .. 80000) {
print $client "get foo$_$spacer\n";
$res = <$client>;
}
# Let the logger thread catch up before we start reading.
sleep 1;
my $do_fetch = 0;
#print STDERR "RESULT: $res\n";
while (my $log = <$watcher>) {
# The "skipped" line won't actually print until some space frees up in the
# buffer, so we need to occasionally cause new lines to generate.
if (($do_fetch++ % 100) == 0) {
print $client "get foo\n";
$res = <$client>;
}
next unless $log =~ m/skipped/;
like($log, qr/skipped=/, "skipped some lines");
# This should unjam more of the text.
print $client "get foob\n";
$res = <$client>;
last;
}
$res = <$watcher>;
like($res, qr/ts=\d+\.\d+\ gid=\d+ type=item_get/, "saw a real log line after a skip");
# test combined logs
# fill to evictions, then enable watcher, set again, and look for both lines
{
my $value = "B"x11000;
my $keycount = 8000;
for (1 .. $keycount) {
print $client "set n,foo$_ 0 0 11000 noreply\r\n$value\r\n";
}
$watcher = $server->new_sock;
print $watcher "watch mutations evictions\n";
$res = <$watcher>;
is($res, "OK\r\n", "new watcher enabled");
my $watcher2 = $server->new_sock;
print $watcher2 "watch evictions\n";
$res = <$watcher2>;
is($res, "OK\r\n", "evictions watcher enabled");
print $client "set bfoo 0 0 11000 noreply\r\n$value\r\n";
my $found_log = 0;
my $found_ev = 0;
while (my $log = <$watcher>) {
$found_log = 1 if ($log =~ m/type=item_store/);
$found_ev = 1 if ($log =~ m/type=eviction/);
last if ($found_log && $found_ev);
}
is($found_log, 1, "found rawcmd log entry");
is($found_ev, 1, "found eviction log entry");
}
memcached-1.5.6/t/binary-get.t 0000755 0001750 0001750 00000000763 12416643766 013073 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.5.6/t/issue_68.t 0000644 0001750 0001750 00000000771 12416643766 012473 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.5.6/t/dash-M.t 0000664 0001750 0001750 00000001325 13240770206 012117 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 2');
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.5.6/memcached.h 0000664 0001750 0001750 00000066511 13241473232 012445 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
#include "itoa_ljust.h"
#include "protocol_binary.h"
#include "cache.h"
#include "logger.h"
#ifdef EXTSTORE
#include "extstore.h"
#include "crc32c.h"
#endif
#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)
/* Up to 3 numbers (2 32bit, 1 64bit), spaces, newlines, null 0 */
#define SUFFIX_SIZE 50
/** 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 100
/** 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
#define HASHPOWER_MAX 32
/*
* 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 ITEM_lruid(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_watch, /**< held by the logger thread as a watcher */
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_TCP(x) (x == tcp_transport)
#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, TOO_LARGE, NO_MEMORY
};
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. */
// TODO: Move to sub-header. needed in logger.h
//typedef unsigned int rel_time_t;
/** Use X macros to avoid iterating over the stats fields during reset and
* aggregation. No longer have to add new stats in 3+ places.
*/
#define SLAB_STATS_FIELDS \
X(set_cmds) \
X(get_hits) \
X(touch_hits) \
X(delete_hits) \
X(cas_hits) \
X(cas_badval) \
X(incr_hits) \
X(decr_hits)
/** Stats stored per slab (and per thread). */
struct slab_stats {
#define X(name) uint64_t name;
SLAB_STATS_FIELDS
#undef X
};
#define THREAD_STATS_FIELDS \
X(get_cmds) \
X(get_misses) \
X(get_expired) \
X(get_flushed) \
X(touch_cmds) \
X(touch_misses) \
X(delete_misses) \
X(incr_misses) \
X(decr_misses) \
X(cas_misses) \
X(bytes_read) \
X(bytes_written) \
X(flush_cmds) \
X(conn_yields) /* # of yields for connections (-R option)*/ \
X(auth_cmds) \
X(auth_errors) \
X(idle_kicks) /* idle connections killed */
#ifdef EXTSTORE
#define EXTSTORE_THREAD_STATS_FIELDS \
X(get_extstore) \
X(recache_from_extstore) \
X(miss_from_extstore) \
X(badcrc_from_extstore)
#endif
/**
* Stats stored per-thread.
*/
struct thread_stats {
pthread_mutex_t mutex;
#define X(name) uint64_t name;
THREAD_STATS_FIELDS
#ifdef EXTSTORE
EXTSTORE_THREAD_STATS_FIELDS
#endif
#undef X
struct slab_stats slab_stats[MAX_NUMBER_OF_SLAB_CLASSES];
uint64_t lru_hits[POWER_LARGEST];
};
/**
* Global stats. Only resettable stats should go into this structure.
*/
struct stats {
uint64_t total_items;
uint64_t total_conns;
uint64_t rejected_conns;
uint64_t malloc_fails;
uint64_t listen_disabled_num;
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_chunk_rescues; /* chunked-item chunks recovered */
uint64_t slab_reassign_busy_items; /* valid temporarily unmovable */
uint64_t slab_reassign_busy_deletes; /* refcounted items killed */
uint64_t lru_crawler_starts; /* Number of item crawlers kicked off */
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 */
uint64_t log_worker_dropped; /* logs dropped by worker threads */
uint64_t log_worker_written; /* logs written by worker threads */
uint64_t log_watcher_skipped; /* logs watchers missed */
uint64_t log_watcher_sent; /* logs sent to watcher buffers */
#ifdef EXTSTORE
uint64_t extstore_compact_lost; /* items lost because they were locked */
uint64_t extstore_compact_rescues; /* items re-written during compaction */
uint64_t extstore_compact_skipped; /* unhit items skipped during compaction */
#endif
struct timeval maxconns_entered; /* last time maxconns entered */
};
/**
* Global "state" stats. Reflects state that shouldn't be wiped ever.
* Ordered for some cache line locality for commonly updated counters.
*/
struct stats_state {
uint64_t curr_items;
uint64_t curr_bytes;
uint64_t curr_conns;
uint64_t hash_bytes; /* size used for hash tables */
unsigned int conn_structs;
unsigned int reserved_fds;
unsigned int hash_power_level; /* Better hope it's not over 9000 */
bool hash_is_expanding; /* If the hash table is being expanded */
bool accepting_conns; /* whether we are currently accepting */
bool slab_reassign_running; /* slab reassign in progress */
bool lru_crawler_running; /* crawl in progress */
};
#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 */
int slab_chunk_size_max; /* Upper end for chunks within slab pages. */
int slab_page_size; /* Slab's page units. */
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 lru_segmented; /* Use split or flat LRU's */
bool slab_reassign; /* Whether or not slab reassignment is allowed */
int slab_automove; /* Whether or not to automatically move slabs */
double slab_automove_ratio; /* youngest must be within pct of oldest */
unsigned int slab_automove_window; /* window mover for algorithm */
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 */
bool dump_enabled; /* whether cachedump/metadump commands work */
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 */
double hot_max_factor; /* HOT tail age relative to COLD tail */
double warm_max_factor; /* WARM tail age relative to COLD tail */
int crawls_persleep; /* Number of LRU crawls to run before sleeping */
bool inline_ascii_response; /* pre-format the VALUE line for ASCII responses */
bool temp_lru; /* TTL < temporary_ttl uses TEMP_LRU */
uint32_t temporary_ttl; /* temporary LRU threshold */
int idle_timeout; /* Number of seconds to let connections idle */
unsigned int logger_watcher_buf_size; /* size of logger's per-watcher buffer */
unsigned int logger_buf_size; /* size of per-thread logger buffer */
bool drop_privileges; /* Whether or not to drop unnecessary process privileges */
bool relaxed_privileges; /* Relax process restrictions when running testapp */
#ifdef EXTSTORE
unsigned int ext_item_size; /* minimum size of items to store externally */
unsigned int ext_item_age; /* max age of tail item before storing ext. */
unsigned int ext_low_ttl; /* remaining TTL below this uses own pages */
unsigned int ext_recache_rate; /* counter++ % recache_rate == 0 > recache */
unsigned int ext_wbuf_size; /* read only note for the engine */
unsigned int ext_compact_under; /* when fewer than this many pages, compact */
unsigned int ext_drop_under; /* when fewer than this many pages, drop COLD items */
double ext_max_frag; /* ideal maximum page fragmentation */
double slab_automove_freeratio; /* % of memory to hold free as buffer */
bool ext_drop_unread; /* skip unread items during compaction */
/* per-slab-class free chunk limit */
unsigned int ext_free_memchunks[MAX_NUMBER_OF_SLAB_CLASSES];
#endif
};
extern struct stats stats;
extern struct stats_state stats_state;
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
/* If an item's storage are chained chunks. */
#define ITEM_CHUNKED 32
#define ITEM_CHUNK 64
#ifdef EXTSTORE
/* ITEM_data bulk is external to item */
#define ITEM_HDR 128
#endif
/**
* 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;
// TODO: If we eventually want user loaded modules, we can't use an enum :(
enum crawler_run_type {
CRAWLER_AUTOEXPIRE=0, CRAWLER_EXPIRED, CRAWLER_METADUMP
};
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 */
uint64_t reclaimed; /* items reclaimed during this crawl. */
uint64_t unfetched; /* items reclaimed unfetched during this crawl. */
uint64_t checked; /* items examined during this crawl. */
} crawler;
/* Header when an item is actually a chunk of another item. */
typedef struct _strchunk {
struct _strchunk *next; /* points within its own chain. */
struct _strchunk *prev; /* can potentially point to the head. */
struct _stritem *head; /* always points to the owner chunk */
int size; /* available chunk space in bytes */
int used; /* chunk space used */
int nbytes; /* used. */
unsigned short refcount; /* used? */
uint8_t orig_clsid; /* For obj hdr chunks slabs_clsid is fake. */
uint8_t it_flags; /* ITEM_* above. */
uint8_t slabs_clsid; /* Same as above. */
char data[];
} item_chunk;
#ifdef EXTSTORE
typedef struct {
unsigned int page_version; /* from IO header */
unsigned int offset; /* from IO header */
unsigned short page_id; /* from IO header */
} item_hdr;
#endif
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 */
#ifdef EXTSTORE
cache_t *io_cache; /* IO objects */
void *storage; /* data object for storage system */
#endif
logger *l; /* logger buffer */
void *lru_bump_buf; /* async LRU bump buffer */
} LIBEVENT_THREAD;
typedef struct conn conn;
#ifdef EXTSTORE
typedef struct _io_wrap {
obj_io io;
struct _io_wrap *next;
conn *c;
item *hdr_it; /* original header item. */
unsigned int iovec_start; /* start of the iovecs for this IO */
unsigned int iovec_count; /* total number of iovecs */
unsigned int iovec_data; /* specific index of data iovec */
bool miss; /* signal a miss to unlink hdr_it */
bool badcrc; /* signal a crc failure */
bool active; // FIXME: canary for test. remove
} io_wrap;
#endif
/**
* The structure representing a connection into memcached.
*/
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;
#ifdef EXTSTORE
int io_wrapleft;
unsigned int recache_counter;
io_wrap *io_wraplist; /* linked list of io_wraps */
bool io_queued; /* FIXME: debugging flag */
#endif
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;
uint32_t chunk_rescues;
uint32_t busy_deletes;
uint32_t busy_loops;
uint8_t done;
};
extern struct slab_rebalance slab_rebal;
#ifdef EXTSTORE
extern void *ext_storage;
#endif
/*
* 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);
void conn_worker_readd(conn *c);
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 "crawler.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, void *arg);
void redispatch_conn(conn *c);
void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags, int read_buffer_size, enum network_transport transport);
void sidethread_conn_close(conn *c);
/* 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);
void conn_close_idle(conn *c);
item *item_alloc(char *key, size_t nkey, int flags, rel_time_t exptime, int nbytes);
#define DO_UPDATE true
#define DONT_UPDATE false
item *item_get(const char *key, const size_t nkey, conn *c, const bool do_update);
item *item_touch(const char *key, const size_t nkey, uint32_t exptime, conn *c);
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_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);
#define refcount_incr(it) ++(it->refcount)
#define refcount_decr(it) --(it->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 HAVE_DROP_WORKER_PRIVILEGES
extern void drop_worker_privileges(void);
#else
#define drop_worker_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.5.6/linux_priv.c 0000664 0001750 0001750 00000010617 13160272015 012721 0000000 0000000 #include "config.h"
#include
#include
#include
#include "memcached.h"
// In the future when the system is more tested this could be switched
// to SCMP_ACT_KILL instead.
#define DENY_ACTION SCMP_ACT_ERRNO(EACCES)
void drop_privileges(void) {
scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
if (ctx == NULL) {
return;
}
int rc = 0;
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(shmctl), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
#ifdef MEMCACHED_DEBUG
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
#endif
if (rc != 0) {
goto fail;
}
rc = seccomp_load(ctx);
if (rc < 0) {
goto fail;
}
fail:
seccomp_release(ctx);
}
void drop_worker_privileges(void) {
scmp_filter_ctx ctx = seccomp_init(DENY_ACTION);
if (ctx == NULL) {
return;
}
int rc = 0;
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sigreturn), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_wait), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(epoll_ctl), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpeername), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getrusage), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mremap), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvfrom), 0);
// for spawning the LRU crawler
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(madvise), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
// stat
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockname), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
if (settings.shutdown_command) {
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(tgkill), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
}
if (settings.relaxed_privileges) {
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
} else {
rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1, SCMP_A0(SCMP_CMP_EQ, 1));
}
if (rc != 0) {
goto fail;
}
rc = seccomp_load(ctx);
if (rc < 0) {
goto fail;
}
fail:
seccomp_release(ctx);
}
memcached-1.5.6/items.h 0000664 0001750 0001750 00000006334 13242642567 011667 0000000 0000000 #define HOT_LRU 0
#define WARM_LRU 64
#define COLD_LRU 128
#define TEMP_LRU 192
#define CLEAR_LRU(id) (id & ~(3<<6))
#define GET_LRU(id) (id & (3<<6))
/* See items.c */
uint64_t get_cas_id(void);
/*@null@*/
item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags, const rel_time_t exptime, const int nbytes);
item_chunk *do_item_alloc_chunk(item_chunk *ch, const size_t bytes_remain);
item *do_item_alloc_pull(const size_t ntotal, const unsigned int id);
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);
unsigned int do_get_lru_size(uint32_t id);
void do_item_linktail_q(item *it);
void do_item_unlinktail_q(item *it);
item *do_item_crawl_q(item *it);
void *item_lru_bump_buf_create(void);
#define LRU_PULL_EVICT 1
#define LRU_PULL_CRAWL_BLOCKS 2
#define LRU_PULL_RETURN_ITEM 4 /* fill info struct if available */
struct lru_pull_tail_return {
item *it;
uint32_t hv;
};
int lru_pull_tail(const int orig_id, const int cur_lru,
const uint64_t total_bytes, const uint8_t flags, const rel_time_t max_age,
struct lru_pull_tail_return *ret_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 do_item_stats_add_crawl(const int i, const uint64_t reclaimed,
const uint64_t unfetched, const uint64_t checked);
void item_stats_totals(ADD_STAT add_stats, void *c);
/*@null@*/
void item_stats_sizes(ADD_STAT add_stats, void *c);
void item_stats_sizes_init(void);
void item_stats_sizes_enable(ADD_STAT add_stats, void *c);
void item_stats_sizes_disable(ADD_STAT add_stats, void *c);
void item_stats_sizes_add(item *it);
void item_stats_sizes_remove(item *it);
bool item_stats_sizes_status(void);
/* stats getter for slab automover */
typedef struct {
int64_t evicted;
int64_t outofmemory;
uint32_t age;
} item_stats_automove;
void fill_item_stats_automove(item_stats_automove *am);
item *do_item_get(const char *key, const size_t nkey, const uint32_t hv, conn *c, const bool do_update);
item *do_item_touch(const char *key, const size_t nkey, uint32_t exptime, const uint32_t hv, conn *c);
void item_stats_reset(void);
extern pthread_mutex_t lru_locks[POWER_LARGEST];
int start_lru_maintainer_thread(void *arg);
int stop_lru_maintainer_thread(void);
int init_lru_maintainer(void);
void lru_maintainer_pause(void);
void lru_maintainer_resume(void);
void *lru_bump_buf_create(void);
#ifdef EXTSTORE
#define STORAGE_delete(e, it) \
do { \
if (it->it_flags & ITEM_HDR) { \
item_hdr *hdr = (item_hdr *)ITEM_data(it); \
extstore_delete(e, hdr->page_id, hdr->page_version, \
1, ITEM_ntotal(it)); \
} \
} while (0)
#else
#define STORAGE_delete(...)
#endif
memcached-1.5.6/timedrun.c 0000664 0001750 0001750 00000004565 13245327530 012365 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 status = 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, &status, 0);
if (p == pid) {
/* child exited. Let's get out of here */
rv = WIFEXITED(status) ?
WEXITSTATUS(status) :
(0x80 | WTERMSIG(status));
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.5.6/cache.c 0000664 0001750 0001750 00000007426 13150607001 011565 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;
pthread_mutex_lock(&cache->mutex);
ret = do_cache_alloc(cache);
pthread_mutex_unlock(&cache->mutex);
return ret;
}
void* do_cache_alloc(cache_t *cache) {
void *ret;
void *object;
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;
}
}
}
#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);
do_cache_free(cache, ptr);
pthread_mutex_unlock(&cache->mutex);
}
void do_cache_free(cache_t *cache, void *ptr) {
#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;
return;
}
uint64_t *pre = ptr;
--pre;
if (*pre != redzone_pattern) {
raise(SIGABRT);
cache_error = -1;
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);
}
}
}
memcached-1.5.6/daemon.c 0000644 0001750 0001750 00000006012 12606105207 011757 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.5.6/doc/ 0000755 0001750 0001750 00000000000 13245330106 011174 5 0000000 0000000 memcached-1.5.6/doc/CONTRIBUTORS 0000664 0001750 0001750 00000003036 13025643161 013004 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.5.6/doc/new_lru.txt 0000664 0001750 0001750 00000004645 13150607001 013337 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.
* Items hit at least twice are considered active.
* LRU updates only happen as items reach the bottom of an LRU. If active in
HOT, move to WARM, if active in WARM, stay in WARM. If active in COLD, move
to WARM.
The exception is that items active in COLD are immediately moved to WARM.
* HOT/WARM each capped at N% of memory available for that slab class. COLD
is uncapped (by default, as of this writing).
* HOT/WARM are also age capped by ratios of COLD's age. IE: HOT is capped at
N% of memory, or 10% of the age of COLD, whichever comes first.
* Items flow from HOT/WARM into COLD.
* A background thread exists which shuffles items between/within the LRU's as
limits are reached. This includes moves from COLD to WARM.
* 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
most item reads, largely 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 temporary_ttl=N` (when used with lru_maintainer) will make
items with a TTL less than or equal to this value use a fourth TEMP LRU. Items
stored in TEMP are never bumped within its LRU or moved to other LRU's. They
also cannot be evicted. This can help reduce holes and load on the LRU crawler.
Do not set temporary_ttl too high or memory could become exhausted.
memcached-1.5.6/doc/threads.txt 0000664 0001750 0001750 00000004656 13150607001 013320 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 accesses 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 issue:
- 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.5.6/doc/Makefile.am 0000664 0001750 0001750 00000001327 13115057711 013161 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: protocol-binary.full
@XML2RFC@ -c @abs_builddir@ protocol-binary.full $@
protocol-binary-range.txt: protocol-binary-range.full
@XML2RFC@ -c @abs_builddir@ protocol-binary-range.full $@
protocol-binary.full: protocol-binary.xml xml2rfc/rfc2629-noinc.xsl
@XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary.xml > $@
protocol-binary-range.full: protocol-binary-range.xml xml2rfc/rfc2629-noinc.xsl
@XSLTPROC@ --nonet xml2rfc/rfc2629-noinc.xsl protocol-binary-range.xml > $@
memcached-1.5.6/doc/protocol.txt 0000664 0001750 0001750 00000153251 13240770206 013533 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.
Note that a TTL of 1 will sometimes immediately expire. Time is internally
updated on second boundaries, which makes expiration time roughly +/- 1s.
This more proportionally affects very low TTL's.
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). If a negative value is given the item is immediately
expired.
- 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" operate 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.
Get And Touch
-----
The "gat" and "gats" commands are used to fetch items and update the
expiration time of an existing items.
gat *\r\n
gats *\r\n
- is expiration time.
- * 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.
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 Tuning
----------
Memcached supports multiple LRU algorithms, with a few tunables. Effort is
made to have sane defaults however you are able to tune while the daemon is
running.
The traditional model is "flat" mode, which is a single LRU chain per slab
class. The newer (with `-o modern` or `-o lru_maintainer`) is segmented into
HOT, WARM, COLD. There is also a TEMP LRU. See doc/new_lru.txt for details.
lru